Compare commits
3 Commits
948144e7b2
...
260c2c79f1
| Author | SHA1 | Date | |
|---|---|---|---|
| 260c2c79f1 | |||
| 4135b72648 | |||
| 5404f295e4 |
@@ -33,6 +33,7 @@ public class SaTokenConfigure implements WebMvcConfigurer {
|
|||||||
.notMatch("/student/detail")
|
.notMatch("/student/detail")
|
||||||
.notMatch("/studentLessonPlans/list")
|
.notMatch("/studentLessonPlans/list")
|
||||||
.notMatch("/studentLessonPlans/history")
|
.notMatch("/studentLessonPlans/history")
|
||||||
|
.notMatch("/student/analyze")
|
||||||
.notMatch("/unit/list")
|
.notMatch("/unit/list")
|
||||||
.notMatch("/vocabulary/list")
|
.notMatch("/vocabulary/list")
|
||||||
.notMatch("/plan/download")
|
.notMatch("/plan/download")
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.yinlihupo.enlish.service.constant;
|
||||||
|
|
||||||
|
public class StudentConstant {
|
||||||
|
|
||||||
|
public static final String ANALYZE_STUDENT_STUDY = "analyzeStudentStudy";
|
||||||
|
|
||||||
|
public static String buildAnalyzeStudentStudyKey(Integer studentId) {
|
||||||
|
return ANALYZE_STUDENT_STUDY + ":" + studentId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package com.yinlihupo.enlish.service.controller;
|
||||||
|
|
||||||
|
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
|
||||||
|
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
|
||||||
|
import com.yinlihupo.enlish.service.model.vo.user.CreateUserReqVO;
|
||||||
|
import com.yinlihupo.enlish.service.model.vo.user.FindUserListRepVO;
|
||||||
|
import com.yinlihupo.enlish.service.model.vo.user.FindUserListRspVO;
|
||||||
|
import com.yinlihupo.enlish.service.service.RoleService;
|
||||||
|
import com.yinlihupo.enlish.service.service.UserService;
|
||||||
|
import com.yinlihupo.framework.biz.operationlog.aspect.ApiOperationLog;
|
||||||
|
import com.yinlihupo.framework.common.response.PageResponse;
|
||||||
|
import com.yinlihupo.framework.common.response.Response;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RequestMapping("/admin/")
|
||||||
|
@RestController
|
||||||
|
public class AdminController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private UserService userService;
|
||||||
|
@Resource
|
||||||
|
private PasswordEncoder passwordEncoder;
|
||||||
|
@Resource
|
||||||
|
private RoleService roleService;
|
||||||
|
|
||||||
|
@PostMapping("user/list")
|
||||||
|
@ApiOperationLog(description = "查询用户列表")
|
||||||
|
public PageResponse<FindUserListRspVO> listUser(@RequestBody FindUserListRepVO findUserListRepVO) {
|
||||||
|
Integer userTotal = userService.findUserTotal();
|
||||||
|
if (userTotal == null || userTotal == 0) {
|
||||||
|
throw new RuntimeException("暂无用户");
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer page = findUserListRepVO.getPage();
|
||||||
|
Integer pageSize = findUserListRepVO.getPageSize();
|
||||||
|
String name = findUserListRepVO.getName();
|
||||||
|
List<UserDO> usersList = userService.findUsersList(page, pageSize, name.isEmpty() ? null : name);
|
||||||
|
Map<Long, RoleDO> userId2RoleMap = roleService.findUserId2RoleMap(usersList.stream().map(UserDO::getId).toList());
|
||||||
|
return PageResponse.success(usersList.stream().map(userDO -> FindUserListRspVO.builder()
|
||||||
|
.id(userDO.getId())
|
||||||
|
.name(userDO.getName())
|
||||||
|
.phone(userDO.getPhone())
|
||||||
|
.roleName(userId2RoleMap.get(userDO.getId()).getRoleName())
|
||||||
|
.build()).toList(), page, userTotal, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("user/create")
|
||||||
|
public Response<Void> createUser(@RequestBody CreateUserReqVO createUserReqVO) {
|
||||||
|
String name = createUserReqVO.getName();
|
||||||
|
String phone = createUserReqVO.getPhone();
|
||||||
|
String password = createUserReqVO.getPassword();
|
||||||
|
|
||||||
|
userService.createUser(UserDO.builder()
|
||||||
|
.name(name)
|
||||||
|
.phone(phone)
|
||||||
|
.password(passwordEncoder.encode(password))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
return Response.success();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,18 @@ public class LoginController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("logout")
|
||||||
|
@ApiOperationLog(description = "登出")
|
||||||
|
public Response<Void> logout() {
|
||||||
|
try {
|
||||||
|
StpUtil.logout();
|
||||||
|
return Response.success();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("登出失败 {}", e.getMessage());
|
||||||
|
return Response.fail("登出失败 " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("sendVerificationCode")
|
@PostMapping("sendVerificationCode")
|
||||||
@ApiOperationLog(description = "发送验证码")
|
@ApiOperationLog(description = "发送验证码")
|
||||||
public Response<Void> sendVerificationCode(@RequestBody VerificationCodeReqVO verificationCodeReqVO) {
|
public Response<Void> sendVerificationCode(@RequestBody VerificationCodeReqVO verificationCodeReqVO) {
|
||||||
|
|||||||
@@ -85,4 +85,11 @@ public class StudentController {
|
|||||||
studentService.deleteStudent(deleteStudentReqVO.getStudentId());
|
studentService.deleteStudent(deleteStudentReqVO.getStudentId());
|
||||||
return Response.success();
|
return Response.success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("analyze")
|
||||||
|
@ApiOperationLog(description = "学生学习分析")
|
||||||
|
public Response<String> analyzeStudentStudy(@RequestBody AnalyzeStudentStudyReqVO analyzeStudentStudyReqVO) {
|
||||||
|
String analyzeStudentStudy = studentService.analyzeStudentStudy(analyzeStudentStudyReqVO.getStudentId());
|
||||||
|
return Response.success(analyzeStudentStudy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,8 +22,4 @@ public class UserDO {
|
|||||||
|
|
||||||
private String email;
|
private String email;
|
||||||
|
|
||||||
private Integer status;
|
|
||||||
|
|
||||||
private Integer isDeleted;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,16 @@
|
|||||||
package com.yinlihupo.enlish.service.domain.dataobject;
|
package com.yinlihupo.enlish.service.domain.dataobject;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
public class UserRoleRelDO {
|
public class UserRoleRelDO {
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@@ -11,45 +20,4 @@ public class UserRoleRelDO {
|
|||||||
|
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
|
|
||||||
private Boolean isDeleted;
|
|
||||||
|
|
||||||
public Long getId() {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(Long id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getUserId() {
|
|
||||||
return userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setUserId(Long userId) {
|
|
||||||
this.userId = userId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Long getRoleId() {
|
|
||||||
return roleId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRoleId(Long roleId) {
|
|
||||||
this.roleId = roleId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Date getCreateTime() {
|
|
||||||
return createTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCreateTime(Date createTime) {
|
|
||||||
this.createTime = createTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean getIsDeleted() {
|
|
||||||
return isDeleted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIsDeleted(Boolean isDeleted) {
|
|
||||||
this.isDeleted = isDeleted;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -22,4 +22,6 @@ public interface ExamWordsJudgeResultDOMapper {
|
|||||||
ExamWordsJudgeResultDO selectDetailById(@Param("id") Integer id);
|
ExamWordsJudgeResultDO selectDetailById(@Param("id") Integer id);
|
||||||
|
|
||||||
List<ExamWordsJudgeResultDO> selectByStudentId(@Param("studentId") Integer studentId);
|
List<ExamWordsJudgeResultDO> selectByStudentId(@Param("studentId") Integer studentId);
|
||||||
|
|
||||||
|
List<ExamWordsJudgeResultDO> selectByStudentIdAndLimitTime(@Param("studentId") Integer studentId);
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.yinlihupo.enlish.service.domain.mapper;
|
package com.yinlihupo.enlish.service.domain.mapper;
|
||||||
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface RoleDOMapper {
|
public interface RoleDOMapper {
|
||||||
|
|
||||||
List<RoleDO> selectAll();
|
List<RoleDO> selectAll();
|
||||||
|
|
||||||
|
List<RoleDO> selectByIds(@Param("ids") List<Long> ids);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
package com.yinlihupo.enlish.service.domain.mapper;
|
package com.yinlihupo.enlish.service.domain.mapper;
|
||||||
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface UserDOMapper {
|
public interface UserDOMapper {
|
||||||
|
|
||||||
@@ -9,4 +12,10 @@ public interface UserDOMapper {
|
|||||||
void insert(UserDO userDO);
|
void insert(UserDO userDO);
|
||||||
|
|
||||||
UserDO selectById(Long id);
|
UserDO selectById(Long id);
|
||||||
|
|
||||||
|
List<UserDO> selectUserDOList(@Param("name") String name, @Param("startIndex") Integer startIndex, @Param("pageSize") Integer pageSize);
|
||||||
|
|
||||||
|
int selectUserTotal();
|
||||||
|
|
||||||
|
void createUser(UserDO userDO);
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
package com.yinlihupo.enlish.service.domain.mapper;
|
package com.yinlihupo.enlish.service.domain.mapper;
|
||||||
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.UserRoleRelDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.UserRoleRelDO;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface UserRoleRelDOMapper {
|
public interface UserRoleRelDOMapper {
|
||||||
|
|
||||||
List<UserRoleRelDO> selectAll();
|
List<UserRoleRelDO> selectAll();
|
||||||
|
|
||||||
|
List<UserRoleRelDO> selectByUserIds(@Param("userIds") List<Long> userIds);
|
||||||
}
|
}
|
||||||
@@ -16,4 +16,6 @@ public interface WordMasteryLogDOMapper {
|
|||||||
int batchUpdateStudentMastery(@Param("wordMasteryLogDOs") List<WordMasteryLogDO> wordMasteryLogDOs);
|
int batchUpdateStudentMastery(@Param("wordMasteryLogDOs") List<WordMasteryLogDO> wordMasteryLogDOs);
|
||||||
|
|
||||||
int selectStudentStrengthCount(@Param("studentId") Integer studentId);
|
int selectStudentStrengthCount(@Param("studentId") Integer studentId);
|
||||||
|
|
||||||
|
List<WordMasteryLogDO> selectByStudentIdAndLimitTime(@Param("studentId") Integer studentId);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.yinlihupo.enlish.service.model.bo.exam;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class ExamWordsJudgeResultDetail {
|
||||||
|
|
||||||
|
private Integer correctWordCount;
|
||||||
|
|
||||||
|
private Integer wrongWordCount;
|
||||||
|
|
||||||
|
private LocalDateTime startDate;
|
||||||
|
|
||||||
|
private List<String> correctWords;
|
||||||
|
|
||||||
|
private List<String> wrongWords;
|
||||||
|
|
||||||
|
private String msg;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.yinlihupo.enlish.service.model.bo.exam;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class WordMasteryDetail {
|
||||||
|
|
||||||
|
private String word;
|
||||||
|
|
||||||
|
private Integer reviewCount;
|
||||||
|
|
||||||
|
private Double memoryStrength;
|
||||||
|
|
||||||
|
private LocalDateTime update_time;
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package com.yinlihupo.enlish.service.model.vo.student;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class AnalyzeStudentStudyReqVO {
|
||||||
|
|
||||||
|
private Integer studentId;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.yinlihupo.enlish.service.model.vo.user;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class CreateUserReqVO {
|
||||||
|
|
||||||
|
private String name;
|
||||||
|
private String phone;
|
||||||
|
private String password;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.yinlihupo.enlish.service.model.vo.user;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class FindUserListRepVO {
|
||||||
|
|
||||||
|
private Integer page;
|
||||||
|
private Integer pageSize;
|
||||||
|
private String name;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.yinlihupo.enlish.service.model.vo.user;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class FindUserListRspVO {
|
||||||
|
private Long id;
|
||||||
|
private String name;
|
||||||
|
private String phone;
|
||||||
|
private String roleName;
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.yinlihupo.enlish.service.service;
|
||||||
|
|
||||||
|
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public interface RoleService {
|
||||||
|
|
||||||
|
void pushRolePermission2Redis();
|
||||||
|
|
||||||
|
Map<Long, RoleDO> findUserId2RoleMap(List<Long> userIds);
|
||||||
|
}
|
||||||
@@ -20,4 +20,6 @@ public interface StudentService {
|
|||||||
void addStudent(AddStudentReqVO addStudentReqVO);
|
void addStudent(AddStudentReqVO addStudentReqVO);
|
||||||
|
|
||||||
void deleteStudent(Integer studentId);
|
void deleteStudent(Integer studentId);
|
||||||
|
|
||||||
|
String analyzeStudentStudy(Integer studentId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,15 @@ package com.yinlihupo.enlish.service.service;
|
|||||||
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface UserService {
|
public interface UserService {
|
||||||
|
|
||||||
void pushRolePermission2Redis();
|
|
||||||
|
|
||||||
UserDO findUser();
|
UserDO findUser();
|
||||||
|
|
||||||
|
List<UserDO> findUsersList(int page, int limit, String name);
|
||||||
|
|
||||||
|
Integer findUserTotal();
|
||||||
|
|
||||||
|
void createUser(UserDO userDO);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,8 +59,7 @@ public class LoginServiceImpl implements LoginService {
|
|||||||
|
|
||||||
if (reqPassword != null && passwordEncoder.matches(reqPassword, userDO.getPassword())) {
|
if (reqPassword != null && passwordEncoder.matches(reqPassword, userDO.getPassword())) {
|
||||||
StpUtil.login(userDO.getId());
|
StpUtil.login(userDO.getId());
|
||||||
throw new RuntimeException("密码错误");
|
return;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new RuntimeException("登录错误");
|
throw new RuntimeException("登录错误");
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.yinlihupo.enlish.service.service.role;
|
||||||
|
|
||||||
|
import com.yinlihupo.enlish.service.constant.RoleConstants;
|
||||||
|
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
|
||||||
|
import com.yinlihupo.enlish.service.domain.dataobject.UserRoleRelDO;
|
||||||
|
import com.yinlihupo.enlish.service.domain.mapper.RoleDOMapper;
|
||||||
|
import com.yinlihupo.enlish.service.domain.mapper.UserDOMapper;
|
||||||
|
import com.yinlihupo.enlish.service.domain.mapper.UserRoleRelDOMapper;
|
||||||
|
import com.yinlihupo.enlish.service.service.RoleService;
|
||||||
|
import com.yinlihupo.framework.common.util.JsonUtils;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class RoleServiceImpl implements RoleService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private StringRedisTemplate stringRedisTemplate;
|
||||||
|
@Resource
|
||||||
|
private RoleDOMapper roleDOMapper;
|
||||||
|
@Resource
|
||||||
|
private UserRoleRelDOMapper userRoleRelDOMapper;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pushRolePermission2Redis() {
|
||||||
|
List<RoleDO> roleDOS = roleDOMapper.selectAll();
|
||||||
|
List<String> roleKeys = roleDOS.stream().map(RoleDO::getRoleKey).toList();
|
||||||
|
log.info("将角色同步到 redis 中, {}", roleKeys);
|
||||||
|
stringRedisTemplate.opsForValue().set(RoleConstants.ROLE, JsonUtils.toJsonString(roleKeys), 60 * 60 * 24);
|
||||||
|
|
||||||
|
Map<Long, RoleDO> roleId2RoleDO = roleDOS.stream().collect(Collectors.toMap(RoleDO::getId, roleDO -> roleDO));
|
||||||
|
|
||||||
|
List<UserRoleRelDO> userRoleRelDOS = userRoleRelDOMapper.selectAll();
|
||||||
|
Map<Long, List<UserRoleRelDO>> userId2UserRoleRelDOs = userRoleRelDOS.stream().collect(Collectors.groupingBy(UserRoleRelDO::getUserId));
|
||||||
|
|
||||||
|
userId2UserRoleRelDOs.forEach((userId, userRoleRelDOs) -> {
|
||||||
|
List<Long> roleIds = userRoleRelDOs.stream().map(UserRoleRelDO::getRoleId).toList();
|
||||||
|
List<RoleDO> roleDOs = roleIds.stream().map(roleId2RoleDO::get).toList();
|
||||||
|
List<String> user2RoleKeys = roleDOs.stream().map(RoleDO::getRoleKey).toList();
|
||||||
|
log.info("将用户 {} 的角色同步到 redis 中, {}", userId, roleKeys);
|
||||||
|
stringRedisTemplate.opsForValue().set(RoleConstants.buildUserRoleKey(userId), JsonUtils.toJsonString(user2RoleKeys));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<Long, RoleDO> findUserId2RoleMap(List<Long> userIds) {
|
||||||
|
List<UserRoleRelDO> userRoleRelDOS = userRoleRelDOMapper.selectByUserIds(userIds);
|
||||||
|
Map<Long, UserRoleRelDO> userId2UserRoleRelDOs = userRoleRelDOS.stream().collect(Collectors.toMap(UserRoleRelDO::getUserId, userRoleRelDO -> userRoleRelDO));
|
||||||
|
|
||||||
|
List<RoleDO> roleDOS = roleDOMapper.selectByIds(userRoleRelDOS.stream().map(UserRoleRelDO::getRoleId).toList());
|
||||||
|
Map<Long, RoleDO> roleId2RoleDO = roleDOS.stream().collect(Collectors.toMap(RoleDO::getId, roleDO -> roleDO));
|
||||||
|
|
||||||
|
return userIds.stream().collect(Collectors.toMap(userId -> userId, userId -> roleId2RoleDO.get(userId2UserRoleRelDOs.get(userId).getRoleId())));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,24 @@
|
|||||||
package com.yinlihupo.enlish.service.service.student;
|
package com.yinlihupo.enlish.service.service.student;
|
||||||
|
|
||||||
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.ClassDO;
|
import com.yinlihupo.enlish.service.constant.StudentConstant;
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.GradeDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.*;
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.StudentDO;
|
|
||||||
import com.yinlihupo.enlish.service.domain.mapper.*;
|
import com.yinlihupo.enlish.service.domain.mapper.*;
|
||||||
import com.yinlihupo.enlish.service.model.bo.StudentDetail;
|
import com.yinlihupo.enlish.service.model.bo.StudentDetail;
|
||||||
|
import com.yinlihupo.enlish.service.model.bo.exam.ExamWordsJudgeResultDetail;
|
||||||
|
import com.yinlihupo.enlish.service.model.bo.exam.WordMasteryDetail;
|
||||||
import com.yinlihupo.enlish.service.model.vo.student.AddStudentReqVO;
|
import com.yinlihupo.enlish.service.model.vo.student.AddStudentReqVO;
|
||||||
import com.yinlihupo.enlish.service.service.StudentService;
|
import com.yinlihupo.enlish.service.service.StudentService;
|
||||||
|
import com.yinlihupo.enlish.service.utils.DifyArticleClient;
|
||||||
|
import com.yinlihupo.framework.common.util.JsonUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.*;
|
||||||
import java.util.Map;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -29,6 +34,12 @@ public class StudentServiceImpl implements StudentService {
|
|||||||
private VocabularyBankDOMapper vocabularyBankMapper;
|
private VocabularyBankDOMapper vocabularyBankMapper;
|
||||||
@Resource
|
@Resource
|
||||||
private WordMasteryLogDOMapper wordMasteryLogDOMapper;
|
private WordMasteryLogDOMapper wordMasteryLogDOMapper;
|
||||||
|
@Resource
|
||||||
|
private ExamWordsJudgeResultDOMapper examWordsJudgeResultDOMapper;
|
||||||
|
@Resource
|
||||||
|
private DifyArticleClient difyArticleClient;
|
||||||
|
@Resource
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<StudentDO> getStudentsByClassIdAndGradeId(Integer classId, Integer gradeId, String name, Integer pageNo, Integer pageSize) {
|
public List<StudentDO> getStudentsByClassIdAndGradeId(Integer classId, Integer gradeId, String name, Integer pageNo, Integer pageSize) {
|
||||||
@@ -94,4 +105,66 @@ public class StudentServiceImpl implements StudentService {
|
|||||||
public void deleteStudent(Integer studentId) {
|
public void deleteStudent(Integer studentId) {
|
||||||
studentDOMapper.deleteById(studentId);
|
studentDOMapper.deleteById(studentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String analyzeStudentStudy(Integer studentId) {
|
||||||
|
|
||||||
|
String key = StudentConstant.buildAnalyzeStudentStudyKey(studentId);
|
||||||
|
if (redisTemplate.hasKey(key)) {
|
||||||
|
Object ans = redisTemplate.opsForValue().get(key);
|
||||||
|
return JsonUtils.toJsonString(ans);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ExamWordsJudgeResultDO> examWordsJudgeResultDOS = examWordsJudgeResultDOMapper.selectByStudentIdAndLimitTime(studentId);
|
||||||
|
|
||||||
|
List<Integer> wordIds = new java.util.ArrayList<>(examWordsJudgeResultDOS.stream().map(ExamWordsJudgeResultDO::getCorrectWordIds).flatMap(List::stream).toList());
|
||||||
|
wordIds.addAll(examWordsJudgeResultDOS.stream().map(ExamWordsJudgeResultDO::getWrongWordIds).flatMap(List::stream).toList());
|
||||||
|
List<VocabularyBankDO> vocabularyBankDOS = vocabularyBankMapper.selectVocabularyBankDOListByIds(wordIds);
|
||||||
|
Map<Integer, VocabularyBankDO> id2Word = vocabularyBankDOS.stream().collect(Collectors.toMap(VocabularyBankDO::getId, vocabularyBankDO -> vocabularyBankDO));
|
||||||
|
|
||||||
|
List<ExamWordsJudgeResultDetail> examWordsJudgeResultDetails = new ArrayList<>();
|
||||||
|
for (ExamWordsJudgeResultDO examWordsJudgeResultDO : examWordsJudgeResultDOS) {
|
||||||
|
List<Integer> correctWordIds = examWordsJudgeResultDO.getCorrectWordIds();
|
||||||
|
List<String> correctWords = correctWordIds.stream().map(id2Word::get).map(VocabularyBankDO::getWord).toList();
|
||||||
|
List<Integer> wrongWordIds = examWordsJudgeResultDO.getWrongWordIds();
|
||||||
|
List<String> wrongWords = wrongWordIds.stream().map(id2Word::get).map(VocabularyBankDO::getWord).toList();
|
||||||
|
|
||||||
|
examWordsJudgeResultDetails.add(ExamWordsJudgeResultDetail.builder()
|
||||||
|
.correctWordCount(examWordsJudgeResultDO.getCorrectWordCount())
|
||||||
|
.wrongWordCount(examWordsJudgeResultDO.getWrongWordCount())
|
||||||
|
.startDate(examWordsJudgeResultDO.getStartDate())
|
||||||
|
.correctWords(correctWords)
|
||||||
|
.wrongWords(wrongWords)
|
||||||
|
.msg(examWordsJudgeResultDO.getMsg()).build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> studentStudyInfo = new HashMap<>();
|
||||||
|
studentStudyInfo.put("考试记录", examWordsJudgeResultDetails);
|
||||||
|
|
||||||
|
List<WordMasteryLogDO> wordMasteryLogDOS = wordMasteryLogDOMapper.selectByStudentIdAndLimitTime(studentId);
|
||||||
|
List<VocabularyBankDO> masteredWords = vocabularyBankMapper.selectVocabularyBankDOListByIds(wordMasteryLogDOS.stream().map(WordMasteryLogDO::getWordId).toList());
|
||||||
|
Map<Integer, VocabularyBankDO> id2MasteryWord = masteredWords.stream().collect(Collectors.toMap(VocabularyBankDO::getId, vocabularyBankDO -> vocabularyBankDO));
|
||||||
|
List<WordMasteryDetail> wordMasteryDetails = new ArrayList<>();
|
||||||
|
for (WordMasteryLogDO wordMasteryLogDO : wordMasteryLogDOS) {
|
||||||
|
wordMasteryDetails.add(WordMasteryDetail.builder()
|
||||||
|
.word(id2MasteryWord.get(wordMasteryLogDO.getWordId()).getWord())
|
||||||
|
.reviewCount(wordMasteryLogDO.getReviewCount())
|
||||||
|
.memoryStrength(wordMasteryLogDO.getMemoryStrength())
|
||||||
|
.update_time(wordMasteryLogDO.getUpdate_time())
|
||||||
|
.build());
|
||||||
|
}
|
||||||
|
studentStudyInfo.put("单词掌握情况", wordMasteryDetails);
|
||||||
|
|
||||||
|
try {
|
||||||
|
String analyze = difyArticleClient.sendStudentAnalyze(JsonUtils.toJsonString(studentStudyInfo)).getAnswer();
|
||||||
|
// 设置过期时间 3 天
|
||||||
|
redisTemplate.opsForValue().set(key, analyze);
|
||||||
|
redisTemplate.expire(key, 3, TimeUnit.DAYS);
|
||||||
|
return analyze;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,57 +1,24 @@
|
|||||||
package com.yinlihupo.enlish.service.service.user;
|
package com.yinlihupo.enlish.service.service.user;
|
||||||
|
|
||||||
import cn.dev33.satoken.stp.StpUtil;
|
import cn.dev33.satoken.stp.StpUtil;
|
||||||
import com.yinlihupo.enlish.service.constant.RoleConstants;
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.RoleDO;
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.UserDO;
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.UserRoleRelDO;
|
|
||||||
import com.yinlihupo.enlish.service.domain.mapper.RoleDOMapper;
|
|
||||||
import com.yinlihupo.enlish.service.domain.mapper.UserDOMapper;
|
import com.yinlihupo.enlish.service.domain.mapper.UserDOMapper;
|
||||||
import com.yinlihupo.enlish.service.domain.mapper.UserRoleRelDOMapper;
|
|
||||||
import com.yinlihupo.enlish.service.service.UserService;
|
import com.yinlihupo.enlish.service.service.UserService;
|
||||||
import com.yinlihupo.framework.common.util.JsonUtils;
|
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class UserServiceImpl implements UserService {
|
public class UserServiceImpl implements UserService {
|
||||||
|
|
||||||
@Resource
|
|
||||||
private StringRedisTemplate stringRedisTemplate;
|
|
||||||
@Resource
|
|
||||||
private RoleDOMapper roleDOMapper;
|
|
||||||
@Resource
|
|
||||||
private UserRoleRelDOMapper userRoleRelDOMapper;
|
|
||||||
@Resource
|
@Resource
|
||||||
private UserDOMapper userDOMapper;
|
private UserDOMapper userDOMapper;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushRolePermission2Redis() {
|
|
||||||
List<RoleDO> roleDOS = roleDOMapper.selectAll();
|
|
||||||
List<String> roleKeys = roleDOS.stream().map(RoleDO::getRoleKey).toList();
|
|
||||||
log.info("将角色同步到 redis 中, {}", roleKeys);
|
|
||||||
stringRedisTemplate.opsForValue().set(RoleConstants.ROLE, JsonUtils.toJsonString(roleKeys), 60 * 60 * 24);
|
|
||||||
|
|
||||||
Map<Long, RoleDO> roleId2RoleDO = roleDOS.stream().collect(Collectors.toMap(RoleDO::getId, roleDO -> roleDO));
|
|
||||||
|
|
||||||
List<UserRoleRelDO> userRoleRelDOS = userRoleRelDOMapper.selectAll();
|
|
||||||
Map<Long, List<UserRoleRelDO>> userId2UserRoleRelDOs = userRoleRelDOS.stream().collect(Collectors.groupingBy(UserRoleRelDO::getUserId));
|
|
||||||
|
|
||||||
userId2UserRoleRelDOs.forEach((userId, userRoleRelDOs) -> {
|
|
||||||
List<Long> roleIds = userRoleRelDOs.stream().map(UserRoleRelDO::getRoleId).toList();
|
|
||||||
List<RoleDO> roleDOs = roleIds.stream().map(roleId2RoleDO::get).toList();
|
|
||||||
List<String> user2RoleKeys = roleDOs.stream().map(RoleDO::getRoleKey).toList();
|
|
||||||
log.info("将用户 {} 的角色同步到 redis 中, {}", userId, roleKeys);
|
|
||||||
stringRedisTemplate.opsForValue().set(RoleConstants.buildUserRoleKey(userId), JsonUtils.toJsonString(user2RoleKeys), 60 * 60 * 24);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserDO findUser() {
|
public UserDO findUser() {
|
||||||
@@ -60,4 +27,20 @@ public class UserServiceImpl implements UserService {
|
|||||||
return userDOMapper.selectById(loginId);
|
return userDOMapper.selectById(loginId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<UserDO> findUsersList(int page, int limit, String name) {
|
||||||
|
|
||||||
|
return userDOMapper.selectUserDOList(name, (page - 1) * limit, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer findUserTotal() {
|
||||||
|
return userDOMapper.selectUserTotal();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createUser(UserDO userDO) {
|
||||||
|
userDOMapper.insert(userDO);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.yinlihupo.enlish.service.task;
|
package com.yinlihupo.enlish.service.task;
|
||||||
|
|
||||||
|
|
||||||
import com.yinlihupo.enlish.service.service.UserService;
|
import com.yinlihupo.enlish.service.service.RoleService;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
import org.springframework.scheduling.annotation.Scheduled;
|
||||||
@@ -13,11 +13,11 @@ import org.springframework.stereotype.Component;
|
|||||||
public class UserRoleTask {
|
public class UserRoleTask {
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private UserService userService;
|
private RoleService roleService;
|
||||||
|
|
||||||
@Scheduled(cron = "0 0 1 * * ?")
|
@Scheduled(cron = "0 0 1 * * ?")
|
||||||
public void PushRolePermissions2Redis() {
|
public void PushRolePermissions2Redis() {
|
||||||
log.info("定时任务,将系统权限推送到 redis 中");
|
log.info("定时任务,将系统权限推送到 redis 中");
|
||||||
userService.pushRolePermission2Redis();
|
roleService.pushRolePermission2Redis();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public class DifyArticleClient {
|
|||||||
|
|
||||||
@Value("${ai.key}")
|
@Value("${ai.key}")
|
||||||
private String apiKey;
|
private String apiKey;
|
||||||
|
private String anaKey = "app-hrUFcopdcpnflsvpHWRuBfCp";
|
||||||
@Value("${ai.url}")
|
@Value("${ai.url}")
|
||||||
private String baseUrl;
|
private String baseUrl;
|
||||||
private final HttpClient httpClient;
|
private final HttpClient httpClient;
|
||||||
@@ -35,6 +36,40 @@ public class DifyArticleClient {
|
|||||||
this.objectMapper = new ObjectMapper();
|
this.objectMapper = new ObjectMapper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DifyResponse sendStudentAnalyze(String query) throws Exception {
|
||||||
|
String endpoint = this.baseUrl;
|
||||||
|
|
||||||
|
// 1. 构建请求体对象
|
||||||
|
ChatRequest payload = new ChatRequest();
|
||||||
|
payload.setQuery(query);
|
||||||
|
payload.setUser(String.valueOf(1));
|
||||||
|
payload.setResponseMode("blocking"); // 使用阻塞模式,一次性返回
|
||||||
|
payload.setInputs(new HashMap<>());
|
||||||
|
|
||||||
|
// 2. 序列化为 JSON 字符串
|
||||||
|
String jsonBody = objectMapper.writeValueAsString(payload);
|
||||||
|
|
||||||
|
// 3. 构建 HTTP 请求
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(endpoint))
|
||||||
|
.header("Authorization", "Bearer " + anaKey)
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||||
|
.timeout(Duration.ofSeconds(30)) // 读取超时
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 4. 发送请求
|
||||||
|
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||||
|
|
||||||
|
// 5. 检查状态码
|
||||||
|
if (response.statusCode() != 200) {
|
||||||
|
throw new RuntimeException("Dify 请求失败: HTTP " + response.statusCode() + " | Body: " + response.body());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 反序列化响应体
|
||||||
|
return objectMapper.readValue(response.body(), DifyResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送对话请求 (阻塞模式)
|
* 发送对话请求 (阻塞模式)
|
||||||
*
|
*
|
||||||
@@ -64,7 +99,7 @@ public class DifyArticleClient {
|
|||||||
// 3. 构建 HTTP 请求
|
// 3. 构建 HTTP 请求
|
||||||
HttpRequest request = HttpRequest.newBuilder()
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
.uri(URI.create(endpoint))
|
.uri(URI.create(endpoint))
|
||||||
.header("Authorization", "Bearer " + this.apiKey)
|
.header("Authorization", "Bearer " + apiKey)
|
||||||
.header("Content-Type", "application/json")
|
.header("Content-Type", "application/json")
|
||||||
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
|
||||||
.timeout(Duration.ofSeconds(30)) // 读取超时
|
.timeout(Duration.ofSeconds(30)) // 读取超时
|
||||||
|
|||||||
@@ -74,5 +74,11 @@
|
|||||||
order by start_date desc
|
order by start_date desc
|
||||||
limit 500;
|
limit 500;
|
||||||
</select>
|
</select>
|
||||||
|
<select id="selectByStudentIdAndLimitTime" resultMap="ResultMapWithBLOBs">
|
||||||
|
select *
|
||||||
|
from exam_words_judge_result
|
||||||
|
where student_id = #{studentId}
|
||||||
|
and start_date between date_sub(now(), interval 7 day) and now()
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -14,4 +14,11 @@
|
|||||||
select * from role
|
select * from role
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectByIds" resultMap="BaseResultMap">
|
||||||
|
select * from role where id in
|
||||||
|
<foreach item="item" collection="ids" separator="," open="(" close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -8,8 +8,6 @@
|
|||||||
<result column="openid" jdbcType="VARCHAR" property="openid" />
|
<result column="openid" jdbcType="VARCHAR" property="openid" />
|
||||||
<result column="phone" jdbcType="VARCHAR" property="phone" />
|
<result column="phone" jdbcType="VARCHAR" property="phone" />
|
||||||
<result column="email" jdbcType="VARCHAR" property="email" />
|
<result column="email" jdbcType="VARCHAR" property="email" />
|
||||||
<result column="status" jdbcType="INTEGER" property="status" />
|
|
||||||
<result column="is_deleted" jdbcType="INTEGER" property="isDeleted" />
|
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
|
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
|
||||||
@@ -17,18 +15,36 @@
|
|||||||
values (#{password}, #{phone}, #{name})
|
values (#{password}, #{phone}, #{name})
|
||||||
</insert>
|
</insert>
|
||||||
|
|
||||||
|
<insert id="createUser">
|
||||||
|
insert into user (phone, name, password)
|
||||||
|
values (#{phone}, #{name}, #{password})
|
||||||
|
</insert>
|
||||||
|
|
||||||
<select id="selectByPhone" resultMap="BaseResultMap">
|
<select id="selectByPhone" resultMap="BaseResultMap">
|
||||||
select *
|
select *
|
||||||
from user
|
from user
|
||||||
where phone = #{phone}
|
where phone = #{phone}
|
||||||
and is_deleted = 0
|
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select id="selectById">
|
<select id="selectById">
|
||||||
select *
|
select *
|
||||||
from user
|
from user
|
||||||
where id = #{id}
|
where id = #{id}
|
||||||
and is_deleted = 0
|
</select>
|
||||||
|
|
||||||
|
<select id="selectUserDOList" resultMap="BaseResultMap">
|
||||||
|
select *
|
||||||
|
from user
|
||||||
|
where 1 = 1
|
||||||
|
<if test="name != null">
|
||||||
|
and name = #{name}
|
||||||
|
</if>
|
||||||
|
limit #{startIndex}, #{pageSize}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="selectUserTotal" resultType="java.lang.Integer">
|
||||||
|
select count(*)
|
||||||
|
from user
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
<result column="user_id" jdbcType="BIGINT" property="userId" />
|
<result column="user_id" jdbcType="BIGINT" property="userId" />
|
||||||
<result column="role_id" jdbcType="BIGINT" property="roleId" />
|
<result column="role_id" jdbcType="BIGINT" property="roleId" />
|
||||||
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
|
<result column="create_time" jdbcType="TIMESTAMP" property="createTime" />
|
||||||
<result column="is_deleted" jdbcType="BIT" property="isDeleted" />
|
|
||||||
</resultMap>
|
</resultMap>
|
||||||
<select id="selectAll" resultMap="BaseResultMap">
|
<select id="selectAll" resultMap="BaseResultMap">
|
||||||
select *
|
select *
|
||||||
@@ -14,4 +14,13 @@
|
|||||||
where is_deleted = 0
|
where is_deleted = 0
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="selectByUserIds" resultMap="BaseResultMap">
|
||||||
|
select *
|
||||||
|
from user_role_rel
|
||||||
|
where user_id in
|
||||||
|
<foreach item="item" index="index" collection="userIds" open="(" separator="," close=")">
|
||||||
|
#{item}
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -52,4 +52,10 @@
|
|||||||
</foreach>
|
</foreach>
|
||||||
</update>
|
</update>
|
||||||
|
|
||||||
|
<select id="selectByStudentIdAndLimitTime" resultMap="BaseResultMap">
|
||||||
|
select *
|
||||||
|
from word_mastery_log
|
||||||
|
where student_id = #{studentId}
|
||||||
|
and update_time between date_sub(now(), interval 7 day) and now()
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
@@ -2,6 +2,8 @@ package com.yinlihupo.enlish.service.service.exam;
|
|||||||
|
|
||||||
import com.yinlihupo.enlish.service.domain.dataobject.ExamWordsJudgeResultDO;
|
import com.yinlihupo.enlish.service.domain.dataobject.ExamWordsJudgeResultDO;
|
||||||
import com.yinlihupo.enlish.service.service.ExamWordsJudgeService;
|
import com.yinlihupo.enlish.service.service.ExamWordsJudgeService;
|
||||||
|
import com.yinlihupo.enlish.service.service.StudentService;
|
||||||
|
import com.yinlihupo.enlish.service.utils.DifyArticleClient;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@@ -16,7 +18,10 @@ public class ExamWordsJudgeServiceTest {
|
|||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private ExamWordsJudgeService examWordsJudgeService;
|
private ExamWordsJudgeService examWordsJudgeService;
|
||||||
|
@Resource
|
||||||
|
private StudentService studentService;
|
||||||
|
@Resource
|
||||||
|
private DifyArticleClient difyArticleClient;
|
||||||
@Test
|
@Test
|
||||||
public void judgeExamWords() {
|
public void judgeExamWords() {
|
||||||
examWordsJudgeService.judgeExamWords(1);
|
examWordsJudgeService.judgeExamWords(1);
|
||||||
@@ -27,4 +32,17 @@ public class ExamWordsJudgeServiceTest {
|
|||||||
List<ExamWordsJudgeResultDO> examWordsJudgeResult = examWordsJudgeService.getExamWordsJudgeResult(1, 10);
|
List<ExamWordsJudgeResultDO> examWordsJudgeResult = examWordsJudgeService.getExamWordsJudgeResult(1, 10);
|
||||||
log.info("examWordsJudgeResult:{}", examWordsJudgeResult);
|
log.info("examWordsJudgeResult:{}", examWordsJudgeResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void selectExamWordsJudgeResult2() {
|
||||||
|
String s = studentService.analyzeStudentStudy(1);
|
||||||
|
try {
|
||||||
|
DifyArticleClient.DifyResponse difyResponse = difyArticleClient.sendStudentAnalyze(s);
|
||||||
|
String answer = difyResponse.getAnswer();
|
||||||
|
log.info("answer:{}", answer);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
log.info("s:{}", s);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
54
enlish-vue/package-lock.json
generated
54
enlish-vue/package-lock.json
generated
@@ -13,6 +13,7 @@
|
|||||||
"echarts": "^6.0.0",
|
"echarts": "^6.0.0",
|
||||||
"element-plus": "^2.12.0",
|
"element-plus": "^2.12.0",
|
||||||
"flowbite": "^1.8.1",
|
"flowbite": "^1.8.1",
|
||||||
|
"markdown-it": "^14.1.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
"universal-cookie": "^8.0.1",
|
"universal-cookie": "^8.0.1",
|
||||||
@@ -1413,6 +1414,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/argparse": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
|
||||||
|
"license": "Python-2.0"
|
||||||
|
},
|
||||||
"node_modules/async-validator": {
|
"node_modules/async-validator": {
|
||||||
"version": "4.2.5",
|
"version": "4.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
|
"resolved": "https://registry.npmjs.org/async-validator/-/async-validator-4.2.5.tgz",
|
||||||
@@ -2326,6 +2333,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/linkify-it": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"uc.micro": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/local-pkg": {
|
"node_modules/local-pkg": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz",
|
||||||
@@ -2378,6 +2394,23 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.5.5"
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/markdown-it": {
|
||||||
|
"version": "14.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
|
||||||
|
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"argparse": "^2.0.1",
|
||||||
|
"entities": "^4.4.0",
|
||||||
|
"linkify-it": "^5.0.0",
|
||||||
|
"mdurl": "^2.0.0",
|
||||||
|
"punycode.js": "^2.3.1",
|
||||||
|
"uc.micro": "^2.1.0"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"markdown-it": "bin/markdown-it.mjs"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/math-intrinsics": {
|
"node_modules/math-intrinsics": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||||
@@ -2387,6 +2420,12 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mdurl": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/memoize-one": {
|
"node_modules/memoize-one": {
|
||||||
"version": "6.0.0",
|
"version": "6.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||||
@@ -2865,6 +2904,15 @@
|
|||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/punycode.js": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/quansync": {
|
"node_modules/quansync": {
|
||||||
"version": "0.2.11",
|
"version": "0.2.11",
|
||||||
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
|
"resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
|
||||||
@@ -3233,6 +3281,12 @@
|
|||||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
|
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
|
||||||
"license": "0BSD"
|
"license": "0BSD"
|
||||||
},
|
},
|
||||||
|
"node_modules/uc.micro": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/ufo": {
|
"node_modules/ufo": {
|
||||||
"version": "1.6.1",
|
"version": "1.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
"echarts": "^6.0.0",
|
"echarts": "^6.0.0",
|
||||||
"element-plus": "^2.12.0",
|
"element-plus": "^2.12.0",
|
||||||
"flowbite": "^1.8.1",
|
"flowbite": "^1.8.1",
|
||||||
|
"markdown-it": "^14.1.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
"universal-cookie": "^8.0.1",
|
"universal-cookie": "^8.0.1",
|
||||||
|
|||||||
10
enlish-vue/src/api/admin.js
Normal file
10
enlish-vue/src/api/admin.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import axios from "@/axios";
|
||||||
|
|
||||||
|
export function getUserList(data) {
|
||||||
|
return axios.post('/admin/user/list', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createUser(data) {
|
||||||
|
return axios.post('/admin/user/create', data)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -23,4 +23,11 @@ export function deleteStudent(id) {
|
|||||||
return axios.post('/student/delete', {
|
return axios.post('/student/delete', {
|
||||||
studentId: id
|
studentId: id
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加一个最大响应时间 20 秒
|
||||||
|
export function getStudentStudyAnalyze(data) {
|
||||||
|
return axios.post('/student/analyze', data, {
|
||||||
|
timeout: 20000
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ export function login(data) {
|
|||||||
return axios.post("/login/login", data)
|
return axios.post("/login/login", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function logout() {
|
||||||
|
return axios.post("/login/logout")
|
||||||
|
}
|
||||||
|
|
||||||
export function getVerificationCode(data) {
|
export function getVerificationCode(data) {
|
||||||
return axios.post("/login/sendVerificationCode", data)
|
return axios.post("/login/sendVerificationCode", data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,10 +8,31 @@
|
|||||||
</a>
|
</a>
|
||||||
<div class="flex items-center lg:order-2">
|
<div class="flex items-center lg:order-2">
|
||||||
<template v-if="userName">
|
<template v-if="userName">
|
||||||
<span
|
<div class="relative" ref="menuRef">
|
||||||
class="text-gray-800 dark:text-white font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2">
|
<button
|
||||||
{{ userName }}
|
@click="menuOpen = !menuOpen"
|
||||||
</span>
|
class="text-gray-800 dark:text-white font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 flex items-center">
|
||||||
|
<span class="mr-2">{{ userName }}</span>
|
||||||
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"/>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
v-if="menuOpen"
|
||||||
|
class="absolute right-0 mt-2 w-40 bg-white dark:bg-gray-700 border border-gray-200 dark:border-gray-600 rounded shadow z-50">
|
||||||
|
<router-link
|
||||||
|
to="/admid"
|
||||||
|
@click="menuOpen = false"
|
||||||
|
class="block px-4 py-2 text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600">
|
||||||
|
后台
|
||||||
|
</router-link>
|
||||||
|
<button
|
||||||
|
@click="handleLogout"
|
||||||
|
class="w-full text-left block px-4 py-2 text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-600">
|
||||||
|
登出
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a href="#" @click.prevent="showLogin = true"
|
<a href="#" @click.prevent="showLogin = true"
|
||||||
@@ -19,10 +40,6 @@
|
|||||||
Login
|
Login
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
<a href="#"
|
|
||||||
class="text-white bg-primary-700 hover:bg-primary-800 focus:ring-4 focus:ring-primary-300 font-medium rounded-lg text-sm px-4 lg:px-5 py-2 lg:py-2.5 mr-2 dark:bg-primary-600 dark:hover:bg-primary-700 focus:outline-none dark:focus:ring-primary-800">
|
|
||||||
Get started
|
|
||||||
</a>
|
|
||||||
<button data-collapse-toggle="mobile-menu-2" type="button"
|
<button data-collapse-toggle="mobile-menu-2" type="button"
|
||||||
class="inline-flex items-center p-2 ml-1 text-sm text-gray-500 rounded-lg lg:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
|
class="inline-flex items-center p-2 ml-1 text-sm text-gray-500 rounded-lg lg:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
|
||||||
aria-controls="mobile-menu-2" aria-expanded="false">
|
aria-controls="mobile-menu-2" aria-expanded="false">
|
||||||
@@ -74,11 +91,17 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
||||||
import LoginDialog from '@/layouts/components/LoginDialog.vue'
|
import LoginDialog from '@/layouts/components/LoginDialog.vue'
|
||||||
import { getUserInfo } from '@/api/user'
|
import { getUserInfo, logout } from '@/api/user'
|
||||||
|
import { removeToken } from '@/composables/auth'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { showMessage } from '@/composables/util.js'
|
||||||
const showLogin = ref(false)
|
const showLogin = ref(false)
|
||||||
const userName = ref('')
|
const userName = ref('')
|
||||||
|
const menuOpen = ref(false)
|
||||||
|
const menuRef = ref(null)
|
||||||
|
const router = useRouter()
|
||||||
async function refreshUser() {
|
async function refreshUser() {
|
||||||
try {
|
try {
|
||||||
const r = await getUserInfo()
|
const r = await getUserInfo()
|
||||||
@@ -88,7 +111,29 @@ async function refreshUser() {
|
|||||||
userName.value = ''
|
userName.value = ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async function handleLogout() {
|
||||||
|
try {
|
||||||
|
await logout()
|
||||||
|
} finally {
|
||||||
|
removeToken()
|
||||||
|
userName.value = ''
|
||||||
|
menuOpen.value = false
|
||||||
|
showMessage('已退出登录', 'success')
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function onDocClick(e) {
|
||||||
|
if (!menuOpen.value) return
|
||||||
|
const el = menuRef.value
|
||||||
|
if (el && !el.contains(e.target)) {
|
||||||
|
menuOpen.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
refreshUser()
|
refreshUser()
|
||||||
|
document.addEventListener('click', onDocClick)
|
||||||
|
})
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
document.removeEventListener('click', onDocClick)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
147
enlish-vue/src/pages/admid/admid.vue
Normal file
147
enlish-vue/src/pages/admid/admid.vue
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
<template>
|
||||||
|
<div class="common-layout">
|
||||||
|
<el-container>
|
||||||
|
<el-header>
|
||||||
|
<Header></Header>
|
||||||
|
</el-header>
|
||||||
|
|
||||||
|
<el-main class="p-4">
|
||||||
|
<el-card>
|
||||||
|
<div class="flex items-center mb-4">
|
||||||
|
<el-input v-model="query.name" placeholder="姓名" clearable style="max-width:220px" />
|
||||||
|
<el-button type="primary" class="ml-2" @click="fetchList">查询</el-button>
|
||||||
|
<el-button class="ml-2" @click="resetSearch">重置</el-button>
|
||||||
|
<el-button type="success" class="ml-2" @click="openCreate">新增用户</el-button>
|
||||||
|
</div>
|
||||||
|
<el-table :data="list" v-loading="loading" border stripe>
|
||||||
|
<el-table-column prop="name" label="姓名" />
|
||||||
|
<el-table-column prop="phone" label="手机号" />
|
||||||
|
<el-table-column prop="roleName" label="角色" />
|
||||||
|
</el-table>
|
||||||
|
<div class="mt-4 flex justify-end">
|
||||||
|
<el-pagination
|
||||||
|
background
|
||||||
|
:current-page="page"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:total="totalCount"
|
||||||
|
layout="prev, pager, next, sizes, total"
|
||||||
|
@current-change="onPageChange"
|
||||||
|
@size-change="onSizeChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog v-model="createVisible" title="新增用户" width="420px">
|
||||||
|
<el-form :model="createForm" :rules="rules" ref="createFormRef" label-width="80px">
|
||||||
|
<el-form-item label="姓名" prop="name">
|
||||||
|
<el-input v-model="createForm.name" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="手机号" prop="phone">
|
||||||
|
<el-input v-model="createForm.phone" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="密码" prop="password">
|
||||||
|
<el-input v-model="createForm.password" type="password" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="createVisible=false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="createLoading" @click="submitCreate">提交</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</el-main>
|
||||||
|
|
||||||
|
</el-container>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import Header from '@/layouts/components/Header.vue'
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { getUserList, createUser } from '@/api/admin'
|
||||||
|
import { showMessage } from '@/composables/util.js'
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const list = ref([])
|
||||||
|
const page = ref(1)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
const totalCount = ref(0)
|
||||||
|
const query = reactive({ name: '' })
|
||||||
|
|
||||||
|
async function fetchList() {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
const r = await getUserList({ page: page.value, pageSize: pageSize.value, name: query.name })
|
||||||
|
const d = r?.data
|
||||||
|
if (d?.success) {
|
||||||
|
list.value = d?.data || []
|
||||||
|
page.value = d?.pageNo || page.value
|
||||||
|
totalCount.value = d?.totalCount || 0
|
||||||
|
pageSize.value = d?.pageSize || pageSize.value
|
||||||
|
} else {
|
||||||
|
list.value = []
|
||||||
|
totalCount.value = 0
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetSearch() {
|
||||||
|
query.name = ''
|
||||||
|
page.value = 1
|
||||||
|
fetchList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onPageChange(p) {
|
||||||
|
page.value = p
|
||||||
|
fetchList()
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSizeChange(s) {
|
||||||
|
pageSize.value = s
|
||||||
|
page.value = 1
|
||||||
|
fetchList()
|
||||||
|
}
|
||||||
|
|
||||||
|
const createVisible = ref(false)
|
||||||
|
const createForm = reactive({ name: '', phone: '', password: '' })
|
||||||
|
const createFormRef = ref()
|
||||||
|
const createLoading = ref(false)
|
||||||
|
const rules = {
|
||||||
|
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||||
|
phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
|
||||||
|
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
|
||||||
|
}
|
||||||
|
|
||||||
|
function openCreate() {
|
||||||
|
createVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitCreate() {
|
||||||
|
createFormRef.value?.validate(async (valid) => {
|
||||||
|
if (!valid) return
|
||||||
|
createLoading.value = true
|
||||||
|
try {
|
||||||
|
const r = await createUser({ name: createForm.name, phone: createForm.phone, password: createForm.password })
|
||||||
|
const d = r?.data
|
||||||
|
if (d?.success) {
|
||||||
|
showMessage('新增成功', 'success')
|
||||||
|
createVisible.value = false
|
||||||
|
createForm.name = ''
|
||||||
|
createForm.phone = ''
|
||||||
|
createForm.password = ''
|
||||||
|
fetchList()
|
||||||
|
} else {
|
||||||
|
showMessage(d?.message || '新增失败', 'error')
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
createLoading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchList()
|
||||||
|
})
|
||||||
|
|
||||||
|
</script>
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
<template>
|
|
||||||
<div class="common-layout">
|
|
||||||
<el-container>
|
|
||||||
<el-header>
|
|
||||||
<Header></Header>
|
|
||||||
</el-header>
|
|
||||||
|
|
||||||
<el-container>
|
|
||||||
<el-aside width="200px">
|
|
||||||
Aside
|
|
||||||
</el-aside>
|
|
||||||
|
|
||||||
<el-main>
|
|
||||||
Main
|
|
||||||
</el-main>
|
|
||||||
</el-container>
|
|
||||||
|
|
||||||
</el-container>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import Header from '@/layouts/components/Header.vue'
|
|
||||||
|
|
||||||
</script>
|
|
||||||
@@ -32,6 +32,20 @@
|
|||||||
<div class="text-md font-semibold mb-3">学生学案记录</div>
|
<div class="text-md font-semibold mb-3">学生学案记录</div>
|
||||||
<PlanHistoryChart :student-id="route.params.id" />
|
<PlanHistoryChart :student-id="route.params.id" />
|
||||||
</div>
|
</div>
|
||||||
|
<div class="bg-white dark:bg-gray-800 rounded-lg shadow p-6">
|
||||||
|
<div class="flex items-center justify-between mb-3">
|
||||||
|
<div class="text-md font-semibold">学习分析</div>
|
||||||
|
<el-button type="primary" size="small" :loading="analyzeLoading" @click="fetchStudyAnalyze">
|
||||||
|
生成学习分析
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<template v-if="analysisHtml">
|
||||||
|
<div class="leading-7 text-gray-700 dark:text-gray-200" v-html="analysisHtml"></div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-empty description="点击右上按钮生成学习分析" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-main>
|
</el-main>
|
||||||
|
|
||||||
@@ -41,17 +55,28 @@
|
|||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Header from '@/layouts/components/Header.vue'
|
import Header from '@/layouts/components/Header.vue'
|
||||||
import { ref, onMounted } from 'vue'
|
import { ref, onMounted, computed } from 'vue'
|
||||||
import { useRoute } from 'vue-router'
|
import { useRoute } from 'vue-router'
|
||||||
import { getStudentDetail } from '@/api/student'
|
import { getStudentDetail, getStudentStudyAnalyze } from '@/api/student'
|
||||||
import { getStudentExamHistory } from '@/api/exam'
|
import { getStudentExamHistory } from '@/api/exam'
|
||||||
import ExamHistoryChart from '@/layouts/components/student/ExamHistoryChart.vue'
|
import ExamHistoryChart from '@/layouts/components/student/ExamHistoryChart.vue'
|
||||||
import PlanHistoryChart from '@/layouts/components/student/PlanHistoryChart.vue'
|
import PlanHistoryChart from '@/layouts/components/student/PlanHistoryChart.vue'
|
||||||
|
import MarkdownIt from 'markdown-it'
|
||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const detail = ref(null)
|
const detail = ref(null)
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const history = ref([])
|
const history = ref([])
|
||||||
|
const analyzeLoading = ref(false)
|
||||||
|
const analysisText = ref('')
|
||||||
|
const md = new MarkdownIt({
|
||||||
|
html: false,
|
||||||
|
linkify: true,
|
||||||
|
breaks: true
|
||||||
|
})
|
||||||
|
const analysisHtml = computed(() => {
|
||||||
|
return analysisText.value ? md.render(analysisText.value) : ''
|
||||||
|
})
|
||||||
|
|
||||||
async function fetchDetail() {
|
async function fetchDetail() {
|
||||||
const id = route.params.id
|
const id = route.params.id
|
||||||
@@ -76,6 +101,22 @@ async function fetchExamHistory() {
|
|||||||
}) : []
|
}) : []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchStudyAnalyze() {
|
||||||
|
const id = route.params.id
|
||||||
|
if (!id) return
|
||||||
|
analyzeLoading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getStudentStudyAnalyze({
|
||||||
|
studentId: Number(id)
|
||||||
|
})
|
||||||
|
const d = res.data
|
||||||
|
const raw = typeof d?.data === 'string' ? d.data : ''
|
||||||
|
analysisText.value = raw.replace(/\\n/g, '\n')
|
||||||
|
} finally {
|
||||||
|
analyzeLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchDetail()
|
fetchDetail()
|
||||||
fetchExamHistory()
|
fetchExamHistory()
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import Index from '@/pages/index.vue'
|
|
||||||
import Uploadpng from '@/pages/uploadpng.vue'
|
import Uploadpng from '@/pages/uploadpng.vue'
|
||||||
import LearningPlan from '@/pages/LearningPlan.vue'
|
import LearningPlan from '@/pages/LearningPlan.vue'
|
||||||
import Class from '@/pages/class.vue'
|
import Class from '@/pages/class.vue'
|
||||||
import { createRouter, createWebHashHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
|
import Admid from '@/pages/admid/admid.vue'
|
||||||
import Student from '@/pages/student.vue'
|
import Student from '@/pages/student.vue'
|
||||||
|
|
||||||
// 统一在这里声明所有路由
|
// 统一在这里声明所有路由
|
||||||
@@ -34,6 +34,13 @@ const routes = [
|
|||||||
meta: { // meta 信息
|
meta: { // meta 信息
|
||||||
title: '学生详情' // 页面标题
|
title: '学生详情' // 页面标题
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/admid', // 路由地址
|
||||||
|
component: Admid, // 对应组件
|
||||||
|
meta: { // meta 信息
|
||||||
|
title: '管理员页面' // 页面标题
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user