diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysTeacherController.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysTeacherController.java new file mode 100644 index 00000000..87bbb162 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/SysTeacherController.java @@ -0,0 +1,80 @@ +package org.dromara.system.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.system.domain.bo.SysTeacherBo; +import org.dromara.system.domain.vo.SysTeacherVo; +import org.dromara.system.domain.vo.SysDeptTreeVo; +import org.dromara.system.service.ISysTeacherService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 教师信息管理 + * + * @author Lion Li + * @date 2025-01-21 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/system/teacher") +public class SysTeacherController extends BaseController { + + private final ISysTeacherService sysTeacherService; + + /** + * 查询教师信息列表 + */ + @SaCheckPermission("system:teacher:list") + @GetMapping("/list") + public TableDataInfo list(SysTeacherBo bo, PageQuery pageQuery) { + return sysTeacherService.queryPageList(bo, pageQuery); + } + + /** + * 根据姓名搜索教师 + */ + @SaCheckPermission("system:teacher:list") + @GetMapping("/search") + public R> searchByName(@RequestParam String name, + @RequestParam(defaultValue = "10") Integer limit) { + List list = sysTeacherService.searchByName(name, limit); + return R.ok(list); + } + + /** + * 获取部门树形结构 + */ + @SaCheckPermission("system:teacher:list") + @GetMapping("/deptTree") + public R> getDeptTreeData() { + List tree = sysTeacherService.getDeptTreeData(); + return R.ok(tree); + } + + /** + * 根据部门ID获取教师列表 + */ + @SaCheckPermission("system:teacher:list") + @GetMapping("/dept/{deptId}") + public R> getTeachersByDeptId(@PathVariable Long deptId) { + List list = sysTeacherService.getTeachersByDeptId(deptId); + return R.ok(list); + } + + /** + * 获取教师详细信息 + */ + @SaCheckPermission("system:teacher:query") + @GetMapping("/{userId}") + public R getInfo(@PathVariable Long userId) { + return R.ok(sysTeacherService.queryById(userId)); + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTeacherBo.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTeacherBo.java new file mode 100644 index 00000000..260509b2 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysTeacherBo.java @@ -0,0 +1,78 @@ +package org.dromara.system.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 教师信息业务对象 + * + * @author Lion Li + * @date 2025-01-21 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SysTeacherBo extends BaseEntity { + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户账号 + */ + @Size(min = 0, max = 30, message = "用户账号长度不能超过{max}个字符") + private String userName; + + /** + * 用户昵称(教师姓名) + */ + @NotBlank(message = "教师姓名不能为空") + @Size(min = 0, max = 30, message = "用户昵称长度不能超过{max}个字符") + private String nickName; + + /** + * 手机号码 + */ + @Size(min = 0, max = 11, message = "手机号码长度不能超过{max}个字符") + private String phonenumber; + + /** + * 用户邮箱 + */ + @Size(min = 0, max = 50, message = "用户邮箱长度不能超过{max}个字符") + private String email; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户性别 + */ + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 查询关键词(支持姓名、账号、手机号搜索) + */ + private String searchKeyword; + + /** + * 查询限制数量 + */ + private Integer limit; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptTreeVo.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptTreeVo.java new file mode 100644 index 00000000..a5111b2b --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysDeptTreeVo.java @@ -0,0 +1,46 @@ +package org.dromara.system.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 部门树形结构视图对象 + * + * @author Lion Li + * @date 2025-01-21 + */ +@Data +public class SysDeptTreeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 部门ID + */ + private Long id; + + /** + * 父部门ID + */ + private Long parentId; + + /** + * 部门名称 + */ + private String label; + + /** + * 显示顺序 + */ + private Integer orderNum; + + /** + * 子部门 + */ + private List children; + +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTeacherVo.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTeacherVo.java new file mode 100644 index 00000000..b1249923 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysTeacherVo.java @@ -0,0 +1,72 @@ +package org.dromara.system.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 教师信息视图对象 + * + * @author Lion Li + * @date 2025-01-21 + */ +@Data +public class SysTeacherVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称(教师姓名) + */ + private String nickName; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 部门名称 + */ + private String deptName; + + /** + * 用户性别 + */ + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTeacherMapper.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTeacherMapper.java new file mode 100644 index 00000000..f3b80968 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysTeacherMapper.java @@ -0,0 +1,184 @@ +package org.dromara.system.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.vo.SysTeacherVo; +import org.dromara.system.domain.vo.SysDeptTreeVo; + +import java.util.List; + +/** + * 教师信息Mapper接口 + * + * @author Lion Li + * @date 2025-01-21 + */ +public interface SysTeacherMapper extends BaseMapperPlus { + + /** + * 分页查询教师信息列表 + * + * @param page 分页对象 + * @param wrapper 查询条件 + * @return 教师信息分页列表 + */ + @Select(""" + SELECT + u.user_id, + u.user_name, + u.nick_name, + u.phonenumber, + u.email, + u.dept_id, + d.dept_name, + u.sex, + u.status, + u.create_time + FROM sys_user u + LEFT JOIN sys_dept d ON u.dept_id = d.dept_id + WHERE u.del_flag = '0' + AND u.account_type = '1' + ${ew.customSqlSegment} + ORDER BY u.create_time DESC + """) + Page selectPageTeacherList(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper wrapper); + + /** + * 查询教师信息列表 + * + * @param wrapper 查询条件 + * @return 教师信息列表 + */ + @Select(""" + SELECT + u.user_id, + u.user_name, + u.nick_name, + u.phonenumber, + u.email, + u.dept_id, + d.dept_name, + u.sex, + u.status, + u.create_time + FROM sys_user u + LEFT JOIN sys_dept d ON u.dept_id = d.dept_id + WHERE u.del_flag = '0' + AND u.account_type = '1' + ${ew.customSqlSegment} + ORDER BY u.create_time DESC + """) + List selectTeacherList(@Param(Constants.WRAPPER) Wrapper wrapper); + + /** + * 根据姓名搜索教师(模糊查询) + * + * @param name 教师姓名关键字 + * @param limit 查询限制数量 + * @return 教师信息列表 + */ + @Select(""" + SELECT + u.user_id, + u.user_name, + u.nick_name, + u.phonenumber, + u.email, + u.dept_id, + d.dept_name, + u.sex, + u.status, + u.create_time + FROM sys_user u + LEFT JOIN sys_dept d ON u.dept_id = d.dept_id + WHERE u.del_flag = '0' + AND u.account_type = '1' + AND (u.nick_name LIKE CONCAT('%', #{name}, '%') + OR u.user_name LIKE CONCAT('%', #{name}, '%') + OR u.phonenumber LIKE CONCAT('%', #{name}, '%')) + AND u.status = '0' + ORDER BY u.create_time DESC + LIMIT #{limit} + """) + List searchTeachersByName(@Param("name") String name, @Param("limit") Integer limit); + + /** + * 根据部门ID获取教师列表 + * + * @param deptId 部门ID + * @return 教师信息列表 + */ + @Select(""" + SELECT + u.user_id, + u.user_name, + u.nick_name, + u.phonenumber, + u.email, + u.dept_id, + d.dept_name, + u.sex, + u.status, + u.create_time + FROM sys_user u + LEFT JOIN sys_dept d ON u.dept_id = d.dept_id + WHERE u.del_flag = '0' + AND u.account_type = '1' + AND u.dept_id = #{deptId} + AND u.status = '0' + ORDER BY u.create_time DESC + """) + List selectTeachersByDeptId(@Param("deptId") Long deptId); + + /** + * 获取部门树形结构(只包含有教师的部门) + * + * @return 部门树信息集合 + */ + @Select(""" + SELECT DISTINCT + d.dept_id as id, + d.parent_id, + d.dept_name as label, + d.order_num + FROM sys_dept d + INNER JOIN sys_user u ON d.dept_id = u.dept_id + WHERE d.del_flag = '0' + AND d.status = '0' + AND u.del_flag = '0' + AND u.account_type = '1' + AND u.status = '0' + ORDER BY d.order_num + """) + List selectDeptTreeWithTeachers(); + + /** + * 根据部门ID列表获取所有父级部门 + * + * @param deptIds 部门ID列表 + * @return 父级部门信息集合 + */ + @Select(""" + + """) + List selectDeptsByIds(@Param("deptIds") List deptIds); +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTeacherService.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTeacherService.java new file mode 100644 index 00000000..36d6cdc0 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysTeacherService.java @@ -0,0 +1,67 @@ +package org.dromara.system.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.bo.SysTeacherBo; +import org.dromara.system.domain.vo.SysTeacherVo; +import org.dromara.system.domain.vo.SysDeptTreeVo; + +import java.util.List; + +/** + * 教师信息管理 服务层 + * + * @author Lion Li + * @date 2025-01-21 + */ +public interface ISysTeacherService { + + /** + * 分页查询教师信息列表 + * + * @param bo 教师信息 + * @param pageQuery 分页对象 + * @return 教师信息集合 + */ + TableDataInfo queryPageList(SysTeacherBo bo, PageQuery pageQuery); + + /** + * 根据姓名搜索教师 + * + * @param name 教师姓名关键字 + * @param limit 查询限制数量 + * @return 教师信息列表 + */ + List searchByName(String name, Integer limit); + + /** + * 获取部门树形结构 + * + * @return 部门树信息集合 + */ + List getDeptTreeData(); + + /** + * 根据部门ID获取教师列表 + * + * @param deptId 部门ID + * @return 教师信息列表 + */ + List getTeachersByDeptId(Long deptId); + + /** + * 根据ID查询教师详细信息 + * + * @param userId 用户ID + * @return 教师信息 + */ + SysTeacherVo queryById(Long userId); + + /** + * 查询教师信息列表 + * + * @param bo 教师信息 + * @return 教师信息集合 + */ + List queryList(SysTeacherBo bo); +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTeacherServiceImpl.java b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTeacherServiceImpl.java new file mode 100644 index 00000000..7d34abdd --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysTeacherServiceImpl.java @@ -0,0 +1,246 @@ +package org.dromara.system.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.constant.SystemConstants; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.system.domain.SysUser; +import org.dromara.system.domain.bo.SysTeacherBo; +import org.dromara.system.domain.vo.SysTeacherVo; +import org.dromara.system.domain.vo.SysDeptTreeVo; +import org.dromara.system.mapper.SysTeacherMapper; +import org.dromara.system.service.ISysTeacherService; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.HashSet; +import java.util.Set; + +/** + * 教师信息管理 服务实现 + * + * @author Lion Li + * @date 2025-01-21 + */ +@RequiredArgsConstructor +@Service +public class SysTeacherServiceImpl implements ISysTeacherService { + + private final SysTeacherMapper baseMapper; + + /** + * 分页查询教师信息列表 + * + * @param bo 教师信息 + * @param pageQuery 分页对象 + * @return 教师信息集合 + */ + @Override + public TableDataInfo queryPageList(SysTeacherBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page page = baseMapper.selectPageTeacherList(pageQuery.build(), lqw); + return TableDataInfo.build(page); + } + + /** + * 查询教师信息列表 + * + * @param bo 教师信息 + * @return 教师信息集合 + */ + @Override + public List queryList(SysTeacherBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectTeacherList(lqw); + } + + /** + * 根据姓名搜索教师 + * + * @param name 教师姓名关键字 + * @param limit 查询限制数量 + * @return 教师信息列表 + */ + @Override + public List searchByName(String name, Integer limit) { + if (StrUtil.isBlank(name)) { + return CollUtil.newArrayList(); + } + if (limit == null || limit <= 0) { + limit = 10; + } + return baseMapper.searchTeachersByName(name, limit); + } + + /** + * 获取部门树形结构 + * + * @return 部门树信息集合 + */ + @Override + public List getDeptTreeData() { + // 获取有教师的部门 + List teacherDepts = baseMapper.selectDeptTreeWithTeachers(); + + // 补全父级部门 + List completeDepts = completeParentDepts(teacherDepts); + + // 构建树形结构 + return buildDeptTree(completeDepts, 0L); + } + + /** + * 补全父级部门 + * + * @param teacherDepts 有教师的部门列表 + * @return 补全父级部门后的完整列表 + */ + private List completeParentDepts(List teacherDepts) { + Set allDeptIds = new HashSet<>(); + Set parentIds = new HashSet<>(); + + // 收集所有部门ID和父级ID + for (SysDeptTreeVo dept : teacherDepts) { + allDeptIds.add(dept.getId()); + if (dept.getParentId() != null && dept.getParentId() != 0L) { + collectParentIds(dept.getParentId(), parentIds); + } + } + + // 找出缺失的父级部门ID + Set missingParentIds = new HashSet<>(); + for (Long parentId : parentIds) { + if (!allDeptIds.contains(parentId)) { + missingParentIds.add(parentId); + } + } + + // 查询缺失的父级部门 + List completeDepts = new ArrayList<>(teacherDepts); + if (!missingParentIds.isEmpty()) { + List parentDepts = baseMapper.selectDeptsByIds(new ArrayList<>(missingParentIds)); + completeDepts.addAll(parentDepts); + + // 递归补全更上级的父级部门 + List moreParents = completeParentDepts(parentDepts); + for (SysDeptTreeVo parent : moreParents) { + if (!allDeptIds.contains(parent.getId()) && !missingParentIds.contains(parent.getId())) { + completeDepts.add(parent); + } + } + } + + return completeDepts; + } + + /** + * 递归收集所有父级部门ID + * + * @param parentId 父级部门ID + * @param parentIds 父级ID集合 + */ + private void collectParentIds(Long parentId, Set parentIds) { + if (parentId != null && parentId != 0L) { + parentIds.add(parentId); + } + } + + /** + * 构建部门树形结构 + * + * @param flatList 扁平部门列表 + * @param parentId 父部门ID + * @return 树形结构列表 + */ + private List buildDeptTree(List flatList, Long parentId) { + List tree = new ArrayList<>(); + + for (SysDeptTreeVo dept : flatList) { + if (ObjectUtil.equal(dept.getParentId(), parentId)) { + List children = buildDeptTree(flatList, dept.getId()); + dept.setChildren(children); + tree.add(dept); + } + } + + return tree; + } + + /** + * 根据部门ID获取教师列表 + * + * @param deptId 部门ID + * @return 教师信息列表 + */ + @Override + public List getTeachersByDeptId(Long deptId) { + if (ObjectUtil.isNull(deptId)) { + return CollUtil.newArrayList(); + } + return baseMapper.selectTeachersByDeptId(deptId); + } + + /** + * 根据ID查询教师详细信息 + * + * @param userId 用户ID + * @return 教师信息 + */ + @Override + public SysTeacherVo queryById(Long userId) { + if (ObjectUtil.isNull(userId)) { + return null; + } + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(SysUser::getUserId, userId); + lqw.eq(SysUser::getDelFlag, SystemConstants.NORMAL); + lqw.eq(SysUser::getAccountType, "1"); // 教师账号类型 + List list = baseMapper.selectTeacherList(lqw); + return CollUtil.isNotEmpty(list) ? list.get(0) : null; + } + + /** + * 构建查询条件 + * + * @param bo 教师信息业务对象 + * @return 查询条件 + */ + private LambdaQueryWrapper buildQueryWrapper(SysTeacherBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + + // 基础条件:未删除的教师用户 + lqw.eq(SysUser::getDelFlag, SystemConstants.NORMAL); + lqw.eq(SysUser::getAccountType, "1"); // 教师账号类型 + + // 动态查询条件 + lqw.eq(ObjectUtil.isNotNull(bo.getUserId()), SysUser::getUserId, bo.getUserId()); + lqw.like(StrUtil.isNotBlank(bo.getUserName()), SysUser::getUserName, bo.getUserName()); + lqw.like(StrUtil.isNotBlank(bo.getNickName()), SysUser::getNickName, bo.getNickName()); + lqw.eq(StrUtil.isNotBlank(bo.getPhonenumber()), SysUser::getPhonenumber, bo.getPhonenumber()); + lqw.like(StrUtil.isNotBlank(bo.getEmail()), SysUser::getEmail, bo.getEmail()); + lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysUser::getDeptId, bo.getDeptId()); + lqw.eq(StrUtil.isNotBlank(bo.getSex()), SysUser::getSex, bo.getSex()); + lqw.eq(StrUtil.isNotBlank(bo.getStatus()), SysUser::getStatus, bo.getStatus()); + + // 搜索关键词(支持姓名、账号、手机号) + if (StrUtil.isNotBlank(bo.getSearchKeyword())) { + String keyword = bo.getSearchKeyword(); + lqw.and(wrapper -> wrapper + .like(SysUser::getNickName, keyword) + .or() + .like(SysUser::getUserName, keyword) + .or() + .like(SysUser::getPhonenumber, keyword) + ); + } + + return lqw; + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/config/XgCacheConfig.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/config/XgCacheConfig.java new file mode 100644 index 00000000..32a61c20 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/config/XgCacheConfig.java @@ -0,0 +1,55 @@ +package org.dromara.xg.config; + +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +/** + * 学工模块缓存配置类 + * 配置Redis缓存,提升学生数据查询性能 + * + * @author AI Assistant + * @date 2025-01-21 + */ +@Configuration +@EnableCaching +public class XgCacheConfig { + + /** + * 配置学工模块Redis缓存管理器 + */ + @Bean("xgCacheManager") + public CacheManager xgCacheManager(RedisConnectionFactory factory) { + // 默认缓存配置 + RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(30)) // 默认30分钟过期 + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())) + .disableCachingNullValues(); // 不缓存null值 + + // 特定缓存配置 + Map cacheConfigurations = new HashMap<>(); + + // 学生树形数据缓存:缓存2小时,因为组织架构变化不频繁 + cacheConfigurations.put("student_tree", defaultConfig.entryTtl(Duration.ofHours(2))); + + // 学生列表缓存:缓存15分钟,因为学生数据可能变化较频繁 + cacheConfigurations.put("student_list", defaultConfig.entryTtl(Duration.ofMinutes(15))); + + return RedisCacheManager.builder(factory) + .cacheDefaults(defaultConfig) + .withInitialCacheConfigurations(cacheConfigurations) + .build(); + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/VolunteerMemberController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/VolunteerMemberController.java new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/VolunteerMemberController.java @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentController.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentController.java new file mode 100644 index 00000000..77e0c964 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/controller/XgStudentController.java @@ -0,0 +1,134 @@ +package org.dromara.xg.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.xg.domain.bo.XgStudentBo; +import org.dromara.xg.domain.vo.XgStudentVo; +import org.dromara.xg.domain.vo.XgStudentTreeVo; +import org.dromara.xg.service.IXgStudentService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 学生信息管理 + * + * @author Lion Li + * @date 2025-01-21 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/xg/student") +public class XgStudentController extends BaseController { + + private final IXgStudentService xgStudentService; + + /** + * 查询学生信息管理列表 + */ + @SaCheckPermission("xg:student:list") + @GetMapping("/list") + public TableDataInfo list(XgStudentBo bo, PageQuery pageQuery) { + return xgStudentService.queryPageList(bo, pageQuery); + } + + /** + * 根据姓名搜索学生 + */ + @SaCheckPermission("xg:student:list") + @GetMapping("/search") + public R> searchByName(@RequestParam String name, + @RequestParam(defaultValue = "10") Integer limit) { + List list = xgStudentService.searchByName(name, limit); + return R.ok(list); + } + + /** + * 获取学院专业班级树形结构 + */ + @SaCheckPermission("xg:student:list") + @GetMapping("/tree") + public R> getTreeData() { + List tree = xgStudentService.getTreeData(); + return R.ok(tree); + } + + /** + * 根据班级ID获取学生列表 + */ + @SaCheckPermission("xg:student:list") + @GetMapping("/class/{classId}") + public R> getStudentsByClassId(@PathVariable Long classId) { + List list = xgStudentService.getStudentsByClassId(classId); + return R.ok(list); + } + + /** + * 导出学生信息管理列表 + */ + @SaCheckPermission("xg:student:export") + @Log(title = "学生信息管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(XgStudentBo bo, HttpServletResponse response) { + List list = xgStudentService.queryList(bo); + ExcelUtil.exportExcel(list, "学生信息管理", XgStudentVo.class, response); + } + + /** + * 获取学生信息管理详细信息 + */ + @SaCheckPermission("xg:student:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(xgStudentService.queryById(id)); + } + + /** + * 新增学生信息管理 + */ + @SaCheckPermission("xg:student:add") + @Log(title = "学生信息管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody XgStudentBo bo) { + return toAjax(xgStudentService.insertByBo(bo)); + } + + /** + * 修改学生信息管理 + */ + @SaCheckPermission("xg:student:edit") + @Log(title = "学生信息管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody XgStudentBo bo) { + return toAjax(xgStudentService.updateByBo(bo)); + } + + /** + * 删除学生信息管理 + */ + @SaCheckPermission("xg:student:remove") + @Log(title = "学生信息管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(xgStudentService.deleteWithValidByIds(List.of(ids), true)); + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentBo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentBo.java new file mode 100644 index 00000000..94b3df1d --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/bo/XgStudentBo.java @@ -0,0 +1,103 @@ +package org.dromara.xg.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.xg.domain.XgStudentMsg; + +import java.util.Date; + +/** + * 学生信息业务对象 xg_student_msg + * + * @author Lion Li + * @date 2025-01-21 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = XgStudentMsg.class, reverseConvertGenerate = false) +public class XgStudentBo extends BaseEntity { + + /** + * 主键ID(雪花ID) + */ + @NotNull(message = "主键ID不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 学号 + */ + @NotBlank(message = "学号不能为空", groups = { AddGroup.class, EditGroup.class }) + private String studentNo; + + /** + * 姓名 + */ + @NotBlank(message = "姓名不能为空", groups = { AddGroup.class, EditGroup.class }) + private String name; + + /** + * 性别:0-男,1-女 + */ + private String gender; + + /** + * 手机号码 + */ + private String phone; + + /** + * 学院ID + */ + private String collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业ID + */ + private String majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 班级ID + */ + private String classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 年级 + */ + private String grade; + + /** + * 学籍状态:1-在籍,2-休学,3-复学,4-退学,5-毕业 + */ + private String studentStatus; + + /** + * 入学年份 + */ + private Integer enrollmentYear; + + /** + * 入学日期 + */ + private Date enrollmentDate; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentTreeVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentTreeVo.java new file mode 100644 index 00000000..e72d5c79 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentTreeVo.java @@ -0,0 +1,45 @@ +package org.dromara.xg.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 学生树形结构视图对象 + * + * @author Lion Li + * @date 2025-01-21 + */ +@Data +public class XgStudentTreeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 节点ID + */ + private String id; + + /** + * 节点名称 + */ + private String label; + + /** + * 节点类型: college-学院, major-专业, class-班级 + */ + private String type; + + /** + * 子节点 + */ + private List children; + + /** + * 学生数量(仅班级节点有) + */ + private Integer studentCount; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentVo.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentVo.java new file mode 100644 index 00000000..3ee77814 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/domain/vo/XgStudentVo.java @@ -0,0 +1,105 @@ +package org.dromara.xg.domain.vo; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.xg.domain.XgStudentMsg; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 学生信息视图对象 xg_student_msg + * + * @author Lion Li + * @date 2025-01-21 + */ +@Data +@AutoMapper(target = XgStudentMsg.class) +public class XgStudentVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键ID(雪花ID) + */ + private Long id; + + /** + * 学号 + */ + private String studentNo; + + /** + * 姓名 + */ + private String name; + + /** + * 性别:0-男,1-女 + */ + private String gender; + + /** + * 手机号码 + */ + private String phone; + + /** + * 学院ID + */ + private String collegeId; + + /** + * 学院名称 + */ + private String collegeName; + + /** + * 专业ID + */ + private String majorId; + + /** + * 专业名称 + */ + private String majorName; + + /** + * 班级ID + */ + private String classId; + + /** + * 班级名称 + */ + private String className; + + /** + * 年级 + */ + private String grade; + + /** + * 学籍状态:1-在籍,2-休学,3-复学,4-退学,5-毕业 + */ + private String studentStatus; + + /** + * 入学年份 + */ + private Integer enrollmentYear; + + /** + * 入学日期 + */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date enrollmentDate; + + /** + * 创建时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMapper.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMapper.java new file mode 100644 index 00000000..966c286a --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/mapper/XgStudentMapper.java @@ -0,0 +1,36 @@ +package org.dromara.xg.mapper; + +import org.apache.ibatis.annotations.Select; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.xg.domain.XgStudentMsg; +import org.dromara.xg.domain.vo.XgStudentVo; + +import java.util.List; +import java.util.Map; + +/** + * 学生信息管理Mapper接口 + * + * @author Lion Li + * @date 2025-01-21 + */ +public interface XgStudentMapper extends BaseMapperPlus { + + /** + * 查询树形结构所需的最少数据 + * 只查询构建树形结构必需的字段,避免全表查询,提升性能 + */ + @Select("SELECT DISTINCT " + + "college_id, college_name, " + + "major_id, major_name, " + + "class_id, class_name " + + "FROM xg_student_msg " + + "WHERE deleted = 0 " + + "AND student_status = '1' " + + "AND college_id IS NOT NULL " + + "AND major_id IS NOT NULL " + + "AND class_id IS NOT NULL " + + "ORDER BY college_name, major_name, class_name") + List> selectTreeNodes(); + +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentService.java new file mode 100644 index 00000000..dfcfb419 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/IXgStudentService.java @@ -0,0 +1,64 @@ +package org.dromara.xg.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.xg.domain.bo.XgStudentBo; +import org.dromara.xg.domain.vo.XgStudentVo; +import org.dromara.xg.domain.vo.XgStudentTreeVo; + +import java.util.Collection; +import java.util.List; + +/** + * 学生信息管理Service接口 + * + * @author Lion Li + * @date 2025-01-21 + */ +public interface IXgStudentService { + + /** + * 查询学生信息管理 + */ + XgStudentVo queryById(Long id); + + /** + * 查询学生信息管理列表 + */ + TableDataInfo queryPageList(XgStudentBo bo, PageQuery pageQuery); + + /** + * 查询学生信息管理列表 + */ + List queryList(XgStudentBo bo); + + /** + * 根据姓名搜索学生 + */ + List searchByName(String name, Integer limit); + + /** + * 获取学院专业班级树形结构 + */ + List getTreeData(); + + /** + * 根据班级ID获取学生列表 + */ + List getStudentsByClassId(Long classId); + + /** + * 新增学生信息管理 + */ + Boolean insertByBo(XgStudentBo bo); + + /** + * 修改学生信息管理 + */ + Boolean updateByBo(XgStudentBo bo); + + /** + * 校验并批量删除学生信息管理信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentCacheService.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentCacheService.java new file mode 100644 index 00000000..2a779ed1 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentCacheService.java @@ -0,0 +1,73 @@ +package org.dromara.xg.service.impl; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.CacheManager; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * 学生数据缓存清除服务 + * 用于在学生数据更新时清除相关缓存,保证数据一致性 + * + * @author AI Assistant + * @date 2025-01-21 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class XgStudentCacheService { + + private final CacheManager cacheManager; + + /** + * 清除学生树形数据缓存 + * 当学院、专业、班级信息发生变化时调用 + */ + public void evictTreeCache() { + try { + Objects.requireNonNull(cacheManager.getCache("student_tree")).clear(); + log.info("清除学生树形数据缓存成功"); + } catch (Exception e) { + log.warn("清除学生树形数据缓存失败", e); + } + } + + /** + * 清除指定班级的学生列表缓存 + * 当该班级的学生信息发生变化时调用 + */ + public void evictStudentListCache(Long classId) { + try { + String cacheKey = "class_" + classId; + Objects.requireNonNull(cacheManager.getCache("student_list")).evict(cacheKey); + log.info("清除班级[{}]学生列表缓存成功", classId); + } catch (Exception e) { + log.warn("清除班级[{}]学生列表缓存失败", classId, e); + } + } + + /** + * 清除所有学生列表缓存 + * 当有大批量学生数据更新时调用 + */ + public void evictAllStudentListCache() { + try { + Objects.requireNonNull(cacheManager.getCache("student_list")).clear(); + log.info("清除所有学生列表缓存成功"); + } catch (Exception e) { + log.warn("清除所有学生列表缓存失败", e); + } + } + + /** + * 清除所有相关缓存 + * 当进行大规模数据更新或系统维护时调用 + */ + public void evictAllCache() { + evictTreeCache(); + evictAllStudentListCache(); + log.info("清除所有学生相关缓存完成"); + } +} \ No newline at end of file diff --git a/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentServiceImpl.java b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentServiceImpl.java new file mode 100644 index 00000000..2df9c8b8 --- /dev/null +++ b/backend/ruoyi-modules/ruoyi-xg/src/main/java/org/dromara/xg/service/impl/XgStudentServiceImpl.java @@ -0,0 +1,214 @@ +package org.dromara.xg.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.xg.domain.XgStudentMsg; +import org.dromara.xg.domain.bo.XgStudentBo; +import org.dromara.xg.domain.vo.XgStudentTreeVo; +import org.dromara.xg.domain.vo.XgStudentVo; +import org.dromara.xg.mapper.XgStudentMapper; +import org.dromara.xg.service.IXgStudentService; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 学生信息管理Service业务层处理 + * + * @author Lion Li + * @date 2025-01-21 + */ +@RequiredArgsConstructor +@Service +public class XgStudentServiceImpl implements IXgStudentService { + + private final XgStudentMapper baseMapper; + + /** + * 查询学生信息管理 + */ + @Override + public XgStudentVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 查询学生信息管理列表 + */ + @Override + public TableDataInfo queryPageList(XgStudentBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询学生信息管理列表 + */ + @Override + public List queryList(XgStudentBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 根据姓名搜索学生 + */ + @Override + public List searchByName(String name, Integer limit) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(name), XgStudentMsg::getName, name); + lqw.eq(XgStudentMsg::getStudentStatus, "1"); // 只查询在籍学生 + lqw.last("LIMIT " + (limit != null ? limit : 10)); + return baseMapper.selectVoList(lqw); + } + + /** + * 获取学院专业班级树形结构 + * 优化版本:使用专门的SQL查询,避免全表查询,提升性能 + */ + @Override + @Cacheable(value = "student_tree", key = "'tree_data'", unless = "#result == null || #result.isEmpty()") + public List getTreeData() { + // 使用专门的SQL查询获取树形结构所需的最少数据 + List> treeNodes = baseMapper.selectTreeNodes(); + + // 构建树形结构 + Map collegeMap = new LinkedHashMap<>(); + Map majorMap = new LinkedHashMap<>(); + Map classMap = new LinkedHashMap<>(); + + for (Map node : treeNodes) { + String collegeId = (String) node.get("college_id"); + String collegeName = (String) node.get("college_name"); + String majorId = (String) node.get("major_id"); + String majorName = (String) node.get("major_name"); + String classId = (String) node.get("class_id"); + String className = (String) node.get("class_name"); + + // 学院节点 + if (StringUtils.isNotBlank(collegeId) && !collegeMap.containsKey(collegeId)) { + XgStudentTreeVo college = new XgStudentTreeVo(); + college.setId(collegeId); + college.setLabel(collegeName); + college.setType("college"); + college.setChildren(new ArrayList<>()); + collegeMap.put(collegeId, college); + } + + // 专业节点 + String majorKey = collegeId + "_" + majorId; + if (StringUtils.isNotBlank(majorId) && !majorMap.containsKey(majorKey)) { + XgStudentTreeVo major = new XgStudentTreeVo(); + major.setId(majorId); + major.setLabel(majorName); + major.setType("major"); + major.setChildren(new ArrayList<>()); + majorMap.put(majorKey, major); + + // 添加到学院下 + if (collegeMap.containsKey(collegeId)) { + collegeMap.get(collegeId).getChildren().add(major); + } + } + + // 班级节点 + String classKey = majorKey + "_" + classId; + if (StringUtils.isNotBlank(classId) && !classMap.containsKey(classKey)) { + XgStudentTreeVo clazz = new XgStudentTreeVo(); + clazz.setId(classId); + clazz.setLabel(className); + clazz.setType("class"); + clazz.setChildren(new ArrayList<>()); + classMap.put(classKey, clazz); + + // 添加到专业下 + if (majorMap.containsKey(majorKey)) { + majorMap.get(majorKey).getChildren().add(clazz); + } + } + } + + return new ArrayList<>(collegeMap.values()); + } + + /** + * 根据班级ID获取学生列表 + */ + @Override + @Cacheable(value = "student_list", key = "'class_' + #classId", unless = "#result == null || #result.isEmpty()") + public List getStudentsByClassId(Long classId) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(XgStudentMsg::getClassId, classId); + lqw.eq(XgStudentMsg::getStudentStatus, "1"); // 只查询在籍学生 + lqw.orderByAsc(XgStudentMsg::getStudentNo); // 按学号排序 + return baseMapper.selectVoList(lqw); + } + + /** + * 新增学生信息管理 + */ + @Override + public Boolean insertByBo(XgStudentBo bo) { + XgStudentMsg add = MapstructUtils.convert(bo, XgStudentMsg.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改学生信息管理 + */ + @Override + public Boolean updateByBo(XgStudentBo bo) { + XgStudentMsg update = MapstructUtils.convert(bo, XgStudentMsg.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(XgStudentMsg entity) { + // TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除学生信息管理 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + // TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + private LambdaQueryWrapper buildQueryWrapper(XgStudentBo bo) { + Map params = bo.getParams(); + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getStudentNo()), XgStudentMsg::getStudentNo, bo.getStudentNo()); + lqw.like(StringUtils.isNotBlank(bo.getName()), XgStudentMsg::getName, bo.getName()); + lqw.eq(StringUtils.isNotBlank(bo.getGender()), XgStudentMsg::getGender, bo.getGender()); + lqw.like(StringUtils.isNotBlank(bo.getPhone()), XgStudentMsg::getPhone, bo.getPhone()); + lqw.eq(StringUtils.isNotBlank(bo.getCollegeId()), XgStudentMsg::getCollegeId, bo.getCollegeId()); + lqw.eq(StringUtils.isNotBlank(bo.getMajorId()), XgStudentMsg::getMajorId, bo.getMajorId()); + lqw.eq(StringUtils.isNotBlank(bo.getClassId()), XgStudentMsg::getClassId, bo.getClassId()); + lqw.eq(StringUtils.isNotBlank(bo.getGrade()), XgStudentMsg::getGrade, bo.getGrade()); + lqw.eq(StringUtils.isNotBlank(bo.getStudentStatus()), XgStudentMsg::getStudentStatus, bo.getStudentStatus()); + lqw.eq(bo.getEnrollmentYear() != null, XgStudentMsg::getEnrollmentYear, bo.getEnrollmentYear()); + lqw.eq(bo.getEnrollmentDate() != null, XgStudentMsg::getEnrollmentDate, bo.getEnrollmentDate()); + return lqw; + } +} \ No newline at end of file diff --git a/web/src/api/system/teacher/index.ts b/web/src/api/system/teacher/index.ts new file mode 100644 index 00000000..843c330d --- /dev/null +++ b/web/src/api/system/teacher/index.ts @@ -0,0 +1,56 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { TeacherVO, TeacherQuery, DeptTreeNode } from './types'; +import { TableDataInfo } from '@/api/types'; + +/** + * 查询教师信息列表 + */ +export function listTeacher(query: TeacherQuery): AxiosPromise> { + return request({ + url: '/system/teacher/list', + method: 'get', + params: query + }); +} + +/** + * 获取教师详细信息 + */ +export function getTeacher(userId: string | number): AxiosPromise { + return request({ + url: '/system/teacher/' + userId, + method: 'get' + }); +} + +/** + * 根据姓名搜索教师 + */ +export function searchTeacherByName(name: string, limit = 10): AxiosPromise { + return request({ + url: '/system/teacher/search', + method: 'get', + params: { name, limit } + }); +} + +/** + * 获取部门树形结构 + */ +export function getDeptTreeData(): AxiosPromise { + return request({ + url: '/system/teacher/deptTree', + method: 'get' + }); +} + +/** + * 根据部门ID获取教师列表 + */ +export function getTeachersByDeptId(deptId: string | number): AxiosPromise { + return request({ + url: '/system/teacher/dept/' + deptId, + method: 'get' + }); +} diff --git a/web/src/api/system/teacher/types.ts b/web/src/api/system/teacher/types.ts new file mode 100644 index 00000000..fd4e09cf --- /dev/null +++ b/web/src/api/system/teacher/types.ts @@ -0,0 +1,111 @@ +import { PageQuery } from '@/api/types'; + +/** + * 教师信息视图对象 + */ +export interface TeacherVO { + /** + * 用户ID + */ + userId: string | number; + + /** + * 用户账号 + */ + userName: string; + + /** + * 用户昵称(教师姓名) + */ + nickName: string; + + /** + * 手机号码 + */ + phonenumber: string; + + /** + * 用户邮箱 + */ + email: string; + + /** + * 部门ID + */ + deptId: string | number; + + /** + * 部门名称 + */ + deptName: string; + + /** + * 用户性别 + */ + sex: string; + + /** + * 帐号状态 + */ + status: string; + + /** + * 创建时间 + */ + createTime: string; +} + +/** + * 教师查询对象 + */ +export interface TeacherQuery extends PageQuery { + /** + * 用户昵称(教师姓名) + */ + nickName?: string; + + /** + * 用户账号 + */ + userName?: string; + + /** + * 手机号码 + */ + phonenumber?: string; + + /** + * 部门ID + */ + deptId?: string | number; + + /** + * 帐号状态 + */ + status?: string; +} + +/** + * 部门树形节点 + */ +export interface DeptTreeNode { + /** + * 部门ID + */ + id: string | number; + + /** + * 部门名称 + */ + label: string; + + /** + * 子部门 + */ + children?: DeptTreeNode[]; + + /** + * 教师数量 + */ + teacherCount?: number; +} diff --git a/web/src/api/xg/student/index.ts b/web/src/api/xg/student/index.ts new file mode 100644 index 00000000..46026302 --- /dev/null +++ b/web/src/api/xg/student/index.ts @@ -0,0 +1,56 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { StudentVO, StudentQuery, StudentTreeNode } from './types'; +import { TableDataInfo } from '@/api/types'; + +/** + * 查询学生信息列表 + */ +export function listStudent(query: StudentQuery): AxiosPromise> { + return request({ + url: '/xg/student/list', + method: 'get', + params: query + }); +} + +/** + * 获取学生详细信息 + */ +export function getStudent(id: string | number): AxiosPromise { + return request({ + url: '/xg/student/' + id, + method: 'get' + }); +} + +/** + * 根据姓名搜索学生 + */ +export function searchStudentByName(name: string, limit = 10): AxiosPromise { + return request({ + url: '/xg/student/search', + method: 'get', + params: { name, limit } + }); +} + +/** + * 获取学院专业班级树形结构 + */ +export function getStudentTreeData(): AxiosPromise { + return request({ + url: '/xg/student/tree', + method: 'get' + }); +} + +/** + * 根据班级ID获取学生列表 + */ +export function getStudentsByClassId(classId: string | number): AxiosPromise { + return request({ + url: '/xg/student/class/' + classId, + method: 'get' + }); +} diff --git a/web/src/api/xg/student/types.ts b/web/src/api/xg/student/types.ts new file mode 100644 index 00000000..b1a2f3a2 --- /dev/null +++ b/web/src/api/xg/student/types.ts @@ -0,0 +1,146 @@ +import { PageQuery } from '@/api/types'; + +/** + * 学生信息视图对象 + */ +export interface StudentVO { + /** + * 主键ID + */ + id: string | number; + + /** + * 学号 + */ + studentNo: string; + + /** + * 姓名 + */ + name: string; + + /** + * 性别 + */ + gender: string; + + /** + * 手机号码 + */ + phone: string; + + /** + * 院系ID + */ + collegeId: string; + + /** + * 院系名称 + */ + collegeName: string; + + /** + * 专业ID + */ + majorId: string; + + /** + * 专业名称 + */ + majorName: string; + + /** + * 班级ID + */ + classId: string; + + /** + * 班级名称 + */ + className: string; + + /** + * 年级 + */ + grade: string; + + /** + * 学籍状态 + */ + studentStatus: string; + + /** + * 入学年份 + */ + enrollmentYear: number; +} + +/** + * 学生查询对象 + */ +export interface StudentQuery extends PageQuery { + /** + * 姓名 + */ + name?: string; + + /** + * 学号 + */ + studentNo?: string; + + /** + * 院系ID + */ + collegeId?: string; + + /** + * 专业ID + */ + majorId?: string; + + /** + * 班级ID + */ + classId?: string; + + /** + * 年级 + */ + grade?: string; + + /** + * 学籍状态 + */ + studentStatus?: string; +} + +/** + * 学院专业班级树形节点 + */ +export interface StudentTreeNode { + /** + * 节点ID + */ + id: string | number; + + /** + * 节点名称 + */ + label: string; + + /** + * 节点类型: college-学院, major-专业, class-班级 + */ + type: 'college' | 'major' | 'class'; + + /** + * 子节点 + */ + children?: StudentTreeNode[]; + + /** + * 学生数量(仅班级节点有) + */ + studentCount?: number; +} diff --git a/web/src/components/ActivitySelector/index.vue b/web/src/components/ActivitySelector/index.vue new file mode 100644 index 00000000..33fe418e --- /dev/null +++ b/web/src/components/ActivitySelector/index.vue @@ -0,0 +1,238 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/AdvisorManager/index.vue b/web/src/components/AdvisorManager/index.vue new file mode 100644 index 00000000..281a0402 --- /dev/null +++ b/web/src/components/AdvisorManager/index.vue @@ -0,0 +1,260 @@ + + + + + diff --git a/web/src/components/ClubSelector/index.vue b/web/src/components/ClubSelector/index.vue new file mode 100644 index 00000000..8e037796 --- /dev/null +++ b/web/src/components/ClubSelector/index.vue @@ -0,0 +1,226 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/CoOrganizerManager/index.vue b/web/src/components/CoOrganizerManager/index.vue new file mode 100644 index 00000000..c3c5fd64 --- /dev/null +++ b/web/src/components/CoOrganizerManager/index.vue @@ -0,0 +1,331 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/InstructorManager/index.vue b/web/src/components/InstructorManager/index.vue new file mode 100644 index 00000000..5b6ee1da --- /dev/null +++ b/web/src/components/InstructorManager/index.vue @@ -0,0 +1,353 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/MaterialTypeSelector/index.vue b/web/src/components/MaterialTypeSelector/index.vue new file mode 100644 index 00000000..86203afb --- /dev/null +++ b/web/src/components/MaterialTypeSelector/index.vue @@ -0,0 +1,92 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/StudentSelector/index.vue b/web/src/components/StudentSelector/index.vue new file mode 100644 index 00000000..a658fc9c --- /dev/null +++ b/web/src/components/StudentSelector/index.vue @@ -0,0 +1,468 @@ + + + + + diff --git a/web/src/components/TeacherSelector/index.vue b/web/src/components/TeacherSelector/index.vue new file mode 100644 index 00000000..21583990 --- /dev/null +++ b/web/src/components/TeacherSelector/index.vue @@ -0,0 +1,456 @@ + + + + + diff --git a/web/src/components/TeamMemberManager/index.vue b/web/src/components/TeamMemberManager/index.vue new file mode 100644 index 00000000..3fa4bc90 --- /dev/null +++ b/web/src/components/TeamMemberManager/index.vue @@ -0,0 +1,210 @@ + + + + + \ No newline at end of file diff --git a/web/src/components/VicePresidentManager/index.vue b/web/src/components/VicePresidentManager/index.vue new file mode 100644 index 00000000..4577deb8 --- /dev/null +++ b/web/src/components/VicePresidentManager/index.vue @@ -0,0 +1,253 @@ + + + + +