@ -0,0 +1,10 @@
|
||||
package com.flyingpig.kclassrollcall.common;
|
||||
|
||||
public class RedisConstant {
|
||||
|
||||
public static final String STUDENT_LIST_KEY = "student:list:";
|
||||
|
||||
public static final String STUDENT_SCORE_KEY = "student:score:";
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.flyingpig.kclassrollcall.config;
|
||||
|
||||
import org.redisson.Redisson;
|
||||
import org.redisson.api.RedissonClient;
|
||||
import org.redisson.config.Config;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class RedissonConfig {
|
||||
@Bean
|
||||
public RedissonClient redissonClient(){
|
||||
// 配置
|
||||
Config config = new Config();
|
||||
config.useSingleServer().setAddress("redis://localhost:6379");
|
||||
// 创建RedissonClient对象
|
||||
return Redisson.create(config);
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.flyingpig.kclassrollcall.dto.resp;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class StudentInfoInCache {
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private Integer no;
|
||||
|
||||
private String major;
|
||||
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
package com.flyingpig.kclassrollcall.init;
|
||||
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
|
||||
@Slf4j(topic = "Initialize DispatcherServlet")
|
||||
@RestController
|
||||
public final class InitializeDispatcherServletController {
|
||||
|
||||
@GetMapping("/initialize/dispatcher-servlet")
|
||||
public void initializeDispatcherServlet() {
|
||||
log.info("Initialized the dispatcherServlet to improve the first response time of the interface...");
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package com.flyingpig.kclassrollcall.init;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public final class InitializeDispatcherServletHandler implements CommandLineRunner {
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
private final ConfigurableEnvironment configurableEnvironment;
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
String url = String.format("http://127.0.0.1:%s%s",
|
||||
configurableEnvironment.getProperty("server.port", "8080") + configurableEnvironment.getProperty("server.servlet.context-path", ""),
|
||||
"/initialize/dispatcher-servlet");
|
||||
try {
|
||||
restTemplate.execute(url, HttpMethod.GET, null, null);
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package com.flyingpig.kclassrollcall.util.cache;
|
||||
|
||||
/**
|
||||
* 缓存加载器
|
||||
* 用于将数据库信息加载到缓存中
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface CacheLoader<T> {
|
||||
|
||||
/**
|
||||
* 加载缓存
|
||||
*/
|
||||
T load();
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package com.flyingpig.kclassrollcall;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.flyingpig.kclassrollcall.common.RedisConstant;
|
||||
import com.flyingpig.kclassrollcall.common.Result;
|
||||
import com.flyingpig.kclassrollcall.common.RollCallMode;
|
||||
import com.flyingpig.kclassrollcall.dto.resp.StudentInfoInCache;
|
||||
import com.flyingpig.kclassrollcall.entity.Student;
|
||||
import com.flyingpig.kclassrollcall.filter.UserContext;
|
||||
import com.flyingpig.kclassrollcall.mapper.StudentMapper;
|
||||
import com.flyingpig.kclassrollcall.service.impl.StudentServiceImpl;
|
||||
import com.flyingpig.kclassrollcall.util.cache.ListCacheUtil;
|
||||
import com.flyingpig.kclassrollcall.util.cache.StringCacheUtil;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class StudentServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private StudentMapper studentMapper;
|
||||
|
||||
@Mock
|
||||
private ListCacheUtil listCacheUtil;
|
||||
|
||||
@Mock
|
||||
private StringCacheUtil stringCacheUtil;
|
||||
|
||||
|
||||
|
||||
@InjectMocks
|
||||
private StudentServiceImpl studentService;
|
||||
|
||||
@Test
|
||||
public <T>
|
||||
|
||||
void testRollCall() {
|
||||
// 模拟依赖方法
|
||||
// 调用依赖方法方法
|
||||
Mockito.doReturn(Result.success()).when(studentService).rollCall(RollCallMode.EQUAL);
|
||||
List<Student> mockStudents = Arrays.asList(
|
||||
new Student(1L, "Alice", 1, "Math", 50.0, 100L),
|
||||
new Student(2L, "Bob", 2, "Science", 0.0, 100L)
|
||||
);
|
||||
when(studentMapper.selectList(new LambdaQueryWrapper<Student>().eq(Student::getTeacherId, any()))).thenReturn(mockStudents);
|
||||
// 模拟 rollCall 调用
|
||||
Result result = studentService.rollCall(RollCallMode.EQUAL);
|
||||
|
||||
// 验证返回结果
|
||||
assertNotNull(result);
|
||||
assertTrue(mockStudents.contains(result.getData())); // 随机选择一个学生
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package com.flyingpig.kclassrollcall;
|
||||
|
||||
import com.flyingpig.kclassrollcall.common.Result;
|
||||
import com.flyingpig.kclassrollcall.dto.req.LoginReq;
|
||||
import com.flyingpig.kclassrollcall.dto.resp.LoginResp;
|
||||
import com.flyingpig.kclassrollcall.entity.Teacher;
|
||||
import com.flyingpig.kclassrollcall.mapper.TeacherMapper;
|
||||
import com.flyingpig.kclassrollcall.service.ITeacherService;
|
||||
import com.flyingpig.kclassrollcall.service.impl.TeacherServiceImpl;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@SpringBootTest
|
||||
public class TeacherServiceImplTest {
|
||||
|
||||
@InjectMocks
|
||||
private TeacherServiceImpl teacherService; // 替换为你的实际服务类
|
||||
|
||||
@Mock
|
||||
private TeacherMapper teacherMapper; // 替换为你的 TeacherMapper 类
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoginSuccess() {
|
||||
// Arrange
|
||||
LoginReq loginReq = new LoginReq("flyingpig", "flyingpig");
|
||||
Teacher teacher = new Teacher();
|
||||
teacher.setId(1L);
|
||||
teacher.setName("John Doe");
|
||||
teacher.setUsername("flyingpig");
|
||||
teacher.setPassword("flyingpig");
|
||||
|
||||
when(teacherMapper.selectOne(any())).thenReturn(teacher); // 模拟数据库返回教师对象
|
||||
|
||||
// Act
|
||||
Result result = teacherService.login(loginReq);
|
||||
|
||||
// Assert
|
||||
assertEquals(200, result.getCode()); // 检查返回代码
|
||||
assertEquals("John Doe", ((LoginResp) result.getData()).getName()); // 检查返回的教师名称
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoginFailure() {
|
||||
// Arrange
|
||||
LoginReq loginReq = new LoginReq("username", "wrongpassword");
|
||||
|
||||
when(teacherMapper.selectOne(any())).thenReturn(null);
|
||||
|
||||
// Act
|
||||
Result result = teacherService.login(loginReq);
|
||||
|
||||
// Assert
|
||||
assertEquals(500, result.getCode()); // 检查返回代码
|
||||
assertEquals("账号或密码错误", result.getMsg()); // 检查返回的错误信息
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,135 @@
|
||||
function clickEffect() {
|
||||
let balls = [];
|
||||
let longPressed = false;
|
||||
let longPress;
|
||||
let multiplier = 0;
|
||||
let width, height;
|
||||
let origin;
|
||||
let normal;
|
||||
let ctx;
|
||||
const colours = ["#F73859", "#14FFEC", "#00E0FF", "#FF99FE", "#FAF15D"];
|
||||
const canvas = document.createElement("canvas");
|
||||
document.body.appendChild(canvas);
|
||||
canvas.setAttribute("style", "width: 100%; height: 100%; top: 0; left: 0; z-index: 99999; position: fixed; pointer-events: none;");
|
||||
const pointer = document.createElement("span");
|
||||
pointer.classList.add("pointer");
|
||||
document.body.appendChild(pointer);
|
||||
|
||||
if (canvas.getContext && window.addEventListener) {
|
||||
ctx = canvas.getContext("2d");
|
||||
updateSize();
|
||||
window.addEventListener('resize', updateSize, false);
|
||||
loop();
|
||||
window.addEventListener("mousedown", function(e) {
|
||||
pushBalls(randBetween(10, 20), e.clientX, e.clientY);
|
||||
document.body.classList.add("is-pressed");
|
||||
longPress = setTimeout(function() {
|
||||
document.body.classList.add("is-longpress");
|
||||
longPressed = true;
|
||||
}, 500);
|
||||
}, false);
|
||||
window.addEventListener("mouseup", function(e) {
|
||||
clearInterval(longPress);
|
||||
if (longPressed == true) {
|
||||
document.body.classList.remove("is-longpress");
|
||||
pushBalls(randBetween(50 + Math.ceil(multiplier), 100 + Math.ceil(multiplier)), e.clientX, e.clientY);
|
||||
longPressed = false;
|
||||
}
|
||||
document.body.classList.remove("is-pressed");
|
||||
}, false);
|
||||
window.addEventListener("mousemove", function(e) {
|
||||
let x = e.clientX;
|
||||
let y = e.clientY;
|
||||
pointer.style.top = y + "px";
|
||||
pointer.style.left = x + "px";
|
||||
}, false);
|
||||
} else {
|
||||
console.log("canvas or addEventListener is unsupported!");
|
||||
}
|
||||
|
||||
|
||||
function updateSize() {
|
||||
canvas.width = window.innerWidth * 2;
|
||||
canvas.height = window.innerHeight * 2;
|
||||
canvas.style.width = window.innerWidth + 'px';
|
||||
canvas.style.height = window.innerHeight + 'px';
|
||||
ctx.scale(2, 2);
|
||||
width = (canvas.width = window.innerWidth);
|
||||
height = (canvas.height = window.innerHeight);
|
||||
origin = {
|
||||
x: width / 2,
|
||||
y: height / 2
|
||||
};
|
||||
normal = {
|
||||
x: width / 2,
|
||||
y: height / 2
|
||||
};
|
||||
}
|
||||
class Ball {
|
||||
constructor(x = origin.x, y = origin.y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.angle = Math.PI * 2 * Math.random();
|
||||
if (longPressed == true) {
|
||||
this.multiplier = randBetween(14 + multiplier, 15 + multiplier);
|
||||
} else {
|
||||
this.multiplier = randBetween(6, 12);
|
||||
}
|
||||
this.vx = (this.multiplier + Math.random() * 0.5) * Math.cos(this.angle);
|
||||
this.vy = (this.multiplier + Math.random() * 0.5) * Math.sin(this.angle);
|
||||
this.r = randBetween(8, 12) + 3 * Math.random();
|
||||
this.color = colours[Math.floor(Math.random() * colours.length)];
|
||||
}
|
||||
update() {
|
||||
this.x += this.vx - normal.x;
|
||||
this.y += this.vy - normal.y;
|
||||
normal.x = -2 / window.innerWidth * Math.sin(this.angle);
|
||||
normal.y = -2 / window.innerHeight * Math.cos(this.angle);
|
||||
this.r -= 0.3;
|
||||
this.vx *= 0.9;
|
||||
this.vy *= 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
function pushBalls(count = 1, x = origin.x, y = origin.y) {
|
||||
for (let i = 0; i < count; i++) {
|
||||
balls.push(new Ball(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
function randBetween(min, max) {
|
||||
return Math.floor(Math.random() * max) + min;
|
||||
}
|
||||
|
||||
function loop() {
|
||||
ctx.fillStyle = "rgba(255, 255, 255, 0)";
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
for (let i = 0; i < balls.length; i++) {
|
||||
let b = balls[i];
|
||||
if (b.r < 0) continue;
|
||||
ctx.fillStyle = b.color;
|
||||
ctx.beginPath();
|
||||
ctx.arc(b.x, b.y, b.r, 0, Math.PI * 2, false);
|
||||
ctx.fill();
|
||||
b.update();
|
||||
}
|
||||
if (longPressed == true) {
|
||||
multiplier += 0.2;
|
||||
} else if (!longPressed && multiplier >= 0) {
|
||||
multiplier -= 0.4;
|
||||
}
|
||||
removeBall();
|
||||
requestAnimationFrame(loop);
|
||||
}
|
||||
|
||||
function removeBall() {
|
||||
for (let i = 0; i < balls.length; i++) {
|
||||
let b = balls[i];
|
||||
if (b.x + b.r < 0 || b.x - b.r > width || b.y + b.r < 0 || b.y - b.r > height || b.r < 0) {
|
||||
balls.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
clickEffect();//调用
|
||||
|
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 191 KiB |
After Width: | Height: | Size: 596 KiB |
Before Width: | Height: | Size: 110 KiB |
Before Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 112 KiB |
Before Width: | Height: | Size: 1.0 KiB |
Before Width: | Height: | Size: 6.7 KiB After Width: | Height: | Size: 212 KiB |