# 盘古用户平台 - 基础数据模块后端开发文档 --- | 文档信息 | 内容 | | -------- | --------------------------- | | **文档版本** | V1.0 | | **项目名称** | 盘古用户平台(Pangu User Platform) | | **模块名称** | 基础数据管理-后端开发 | | **编写团队** | pangu | | **创建日期** | 2026-01-31 | --- ## 1. 开发环境要求 | 环境 | 版本要求 | 说明 | | ------------- | ------- | ------------ | | JDK | 17+ | Java运行环境 | | Maven | 3.8+ | 项目构建工具 | | Spring Boot | 3.3.x | 应用框架 | | MyBatis Plus | 3.5.x | ORM框架 | | MySQL | 8.0+ | 数据库 | | Redis | 7.x | 缓存 | --- ## 2. 目录结构 ``` pangu-admin/src/main/java/com/pangu/ ├── base/ # 基础数据模块 │ ├── controller/ # 控制器层 │ │ ├── GradeController.java # 年级管理 │ │ ├── PgClassController.java # 班级管理 │ │ ├── SubjectController.java # 学科管理 │ │ └── RegionController.java # 区域管理 │ ├── service/ # 服务层 │ │ ├── IGradeService.java │ │ ├── IPgClassService.java │ │ ├── ISubjectService.java │ │ ├── IRegionService.java │ │ └── impl/ │ │ ├── GradeServiceImpl.java │ │ ├── PgClassServiceImpl.java │ │ ├── SubjectServiceImpl.java │ │ └── RegionServiceImpl.java │ ├── mapper/ # 数据访问层 │ │ ├── GradeMapper.java │ │ ├── PgClassMapper.java │ │ ├── SubjectMapper.java │ │ └── RegionMapper.java │ └── domain/ # 实体类 │ ├── Grade.java │ ├── PgClass.java │ ├── Subject.java │ └── Region.java │ └── resources/mapper/base/ # MyBatis XML ├── GradeMapper.xml ├── PgClassMapper.xml ├── SubjectMapper.xml └── RegionMapper.xml ``` --- ## 3. 实体类设计 ### 3.1 年级实体(Grade.java) ```java package com.pangu.base.domain; import com.baomidou.mybatisplus.annotation.*; import com.pangu.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; /** * 年级实体类 * @author pangu */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pg_grade") public class Grade extends BaseEntity { private static final long serialVersionUID = 1L; /** 年级ID */ @TableId(type = IdType.AUTO) private Long gradeId; /** 年级编码 */ private String gradeCode; /** 年级名称 */ @NotBlank(message = "年级名称不能为空") @Size(max = 50, message = "年级名称长度不能超过50个字符") private String gradeName; /** 显示顺序 */ private Integer orderNum; /** 状态(0正常 1停用)*/ private String status; /** 删除标志(0存在 1删除)*/ @TableLogic private String delFlag; } ``` ### 3.2 班级实体(PgClass.java) ```java package com.pangu.base.domain; import com.baomidou.mybatisplus.annotation.*; import com.pangu.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; /** * 班级实体类 * 注意:类名使用PgClass避免与java.lang.Class冲突 * @author pangu */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pg_class") public class PgClass extends BaseEntity { private static final long serialVersionUID = 1L; /** 班级ID */ @TableId(type = IdType.AUTO) private Long classId; /** 班级编码 */ private String classCode; /** 班级名称 */ @NotBlank(message = "班级名称不能为空") @Size(max = 50, message = "班级名称长度不能超过50个字符") private String className; /** 显示顺序 */ private Integer orderNum; /** 状态(0正常 1停用)*/ private String status; /** 删除标志(0存在 1删除)*/ @TableLogic private String delFlag; } ``` ### 3.3 学科实体(Subject.java) ```java package com.pangu.base.domain; import com.baomidou.mybatisplus.annotation.*; import com.pangu.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; /** * 学科实体类 * @author pangu */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pg_subject") public class Subject extends BaseEntity { private static final long serialVersionUID = 1L; /** 学科ID */ @TableId(type = IdType.AUTO) private Long subjectId; /** 学科编码 */ private String subjectCode; /** 学科名称 */ @NotBlank(message = "学科名称不能为空") @Size(max = 50, message = "学科名称长度不能超过50个字符") private String subjectName; /** 显示顺序 */ private Integer orderNum; /** 状态(0正常 1停用)*/ private String status; /** 删除标志(0存在 1删除)*/ @TableLogic private String delFlag; } ``` ### 3.4 区域实体(Region.java) ```java package com.pangu.base.domain; import com.baomidou.mybatisplus.annotation.*; import com.pangu.common.core.domain.BaseEntity; import lombok.Data; import lombok.EqualsAndHashCode; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; import java.util.List; /** * 区域实体类 * @author pangu */ @Data @EqualsAndHashCode(callSuper = true) @TableName("pg_region") public class Region extends BaseEntity { private static final long serialVersionUID = 1L; /** 区域ID */ @TableId(type = IdType.AUTO) private Long regionId; /** 父区域ID */ private Long parentId; /** 区域名称 */ @NotBlank(message = "区域名称不能为空") @Size(max = 100, message = "区域名称长度不能超过100个字符") private String regionName; /** 区域编码 */ private String regionCode; /** 层级(1省 2市 3区)*/ private Integer level; /** 祖级列表 */ private String ancestors; /** 显示顺序 */ private Integer orderNum; /** 状态(0正常 1停用)*/ private String status; /** 删除标志(0存在 1删除)*/ @TableLogic private String delFlag; /** 子区域(非数据库字段)*/ @TableField(exist = false) private List children; } ``` --- ## 4. Mapper层设计 ### 4.1 年级Mapper接口 ```java package com.pangu.base.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.pangu.base.domain.Grade; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 年级Mapper接口 * @author pangu */ @Mapper public interface GradeMapper extends BaseMapper { /** * 查询年级列表 * @param grade 查询条件 * @return 年级列表 */ List selectGradeList(Grade grade); /** * 根据ID查询年级 * @param gradeId 年级ID * @return 年级信息 */ Grade selectGradeById(Long gradeId); /** * 新增年级 * @param grade 年级信息 * @return 影响行数 */ int insertGrade(Grade grade); /** * 修改年级 * @param grade 年级信息 * @return 影响行数 */ int updateGrade(Grade grade); /** * 删除年级(软删除) * @param gradeId 年级ID * @return 影响行数 */ int deleteGradeById(Long gradeId); /** * 校验年级名称唯一 * @param gradeName 年级名称 * @return 年级信息 */ Grade checkGradeNameUnique(@Param("gradeName") String gradeName); /** * 查询最大编码 * @return 最大编码 */ String selectMaxGradeCode(); } ``` ### 4.2 年级Mapper XML ```xml select grade_id, grade_code, grade_name, order_num, status, del_flag, create_by, create_time, update_by, update_time, remark from pg_grade where del_flag = '0' insert into pg_grade ( grade_code, grade_name, order_num, status, del_flag, create_by, create_time, remark ) values ( #{gradeCode}, #{gradeName}, #{orderNum}, #{status}, '0', #{createBy}, #{createTime}, #{remark} ) update pg_grade grade_name = #{gradeName}, order_num = #{orderNum}, status = #{status}, remark = #{remark}, update_by = #{updateBy}, update_time = #{updateTime} where grade_id = #{gradeId} update pg_grade set del_flag = '1' where grade_id = #{gradeId} ``` ### 4.3 区域Mapper接口 ```java package com.pangu.base.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.pangu.base.domain.Region; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 区域Mapper接口 * @author pangu */ @Mapper public interface RegionMapper extends BaseMapper { /** * 查询区域列表 * @param region 查询条件 * @return 区域列表 */ List selectRegionList(Region region); /** * 根据ID查询区域 * @param regionId 区域ID * @return 区域信息 */ Region selectRegionById(Long regionId); /** * 新增区域 * @param region 区域信息 * @return 影响行数 */ int insertRegion(Region region); /** * 修改区域 * @param region 区域信息 * @return 影响行数 */ int updateRegion(Region region); /** * 删除区域(软删除) * @param regionId 区域ID * @return 影响行数 */ int deleteRegionById(Long regionId); /** * 查询子区域数量 * @param parentId 父区域ID * @return 子区域数量 */ int countChildByParentId(@Param("parentId") Long parentId); /** * 查询指定层级的最大编码 * @param level 层级 * @return 最大编码 */ String selectMaxCodeByLevel(@Param("level") Integer level); /** * 查询指定父级下的最大编码 * @param parentId 父区域ID * @return 最大编码 */ String selectMaxCodeByParent(@Param("parentId") Long parentId); } ``` ### 4.4 区域Mapper XML ```xml select region_id, parent_id, region_name, region_code, level, ancestors, order_num, status, del_flag, create_by, create_time, update_by, update_time from pg_region where del_flag = '0' insert into pg_region ( parent_id, region_name, region_code, level, ancestors, order_num, status, del_flag, create_by, create_time ) values ( #{parentId}, #{regionName}, #{regionCode}, #{level}, #{ancestors}, #{orderNum}, #{status}, '0', #{createBy}, #{createTime} ) update pg_region region_name = #{regionName}, order_num = #{orderNum}, status = #{status}, update_by = #{updateBy}, update_time = #{updateTime} where region_id = #{regionId} update pg_region set del_flag = '1' where region_id = #{regionId} ``` --- ## 5. Service层设计 ### 5.1 年级Service接口 ```java package com.pangu.base.service; import com.pangu.base.domain.Grade; import java.util.List; /** * 年级管理Service接口 * @author pangu */ public interface IGradeService { /** * 查询年级分页列表 * @param grade 查询条件 * @return 年级列表 */ List selectGradeList(Grade grade); /** * 查询年级选项列表(下拉用) * @return 启用状态的年级列表 */ List selectGradeOptions(); /** * 根据ID查询年级 * @param gradeId 年级ID * @return 年级信息 */ Grade selectGradeById(Long gradeId); /** * 新增年级 * @param grade 年级信息 * @return 影响行数 */ int insertGrade(Grade grade); /** * 修改年级 * @param grade 年级信息 * @return 影响行数 */ int updateGrade(Grade grade); /** * 删除年级 * @param gradeId 年级ID * @return 影响行数 */ int deleteGradeById(Long gradeId); /** * 校验年级名称是否唯一 * @param grade 年级信息 * @return true-唯一 false-不唯一 */ boolean checkGradeNameUnique(Grade grade); /** * 检查年级是否被学校使用 * @param gradeId 年级ID * @return true-被使用 false-未使用 */ boolean checkGradeExistSchool(Long gradeId); } ``` ### 5.2 年级Service实现 ```java package com.pangu.base.service.impl; import com.pangu.base.domain.Grade; import com.pangu.base.mapper.GradeMapper; import com.pangu.base.mapper.SchoolGradeMapper; import com.pangu.base.service.IGradeService; import com.pangu.common.utils.DateUtils; import com.pangu.common.utils.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * 年级管理Service实现 * @author pangu */ @Service public class GradeServiceImpl implements IGradeService { @Autowired private GradeMapper gradeMapper; @Autowired private SchoolGradeMapper schoolGradeMapper; @Override public List selectGradeList(Grade grade) { return gradeMapper.selectGradeList(grade); } @Override public List selectGradeOptions() { Grade grade = new Grade(); grade.setStatus("0"); // 只查启用的 return gradeMapper.selectGradeList(grade); } @Override public Grade selectGradeById(Long gradeId) { return gradeMapper.selectGradeById(gradeId); } @Override public int insertGrade(Grade grade) { // 生成年级编码 grade.setGradeCode(generateGradeCode()); grade.setCreateTime(DateUtils.getNowDate()); return gradeMapper.insertGrade(grade); } /** * 生成年级编码 * 规则:GRD + 3位序号(如GRD001) */ private String generateGradeCode() { String maxCode = gradeMapper.selectMaxGradeCode(); if (StringUtils.isEmpty(maxCode)) { return "GRD001"; } // 提取序号并+1 int num = Integer.parseInt(maxCode.substring(3)) + 1; return String.format("GRD%03d", num); } @Override public int updateGrade(Grade grade) { grade.setUpdateTime(DateUtils.getNowDate()); return gradeMapper.updateGrade(grade); } @Override public int deleteGradeById(Long gradeId) { return gradeMapper.deleteGradeById(gradeId); } @Override public boolean checkGradeNameUnique(Grade grade) { Long gradeId = grade.getGradeId() == null ? -1L : grade.getGradeId(); Grade info = gradeMapper.checkGradeNameUnique(grade.getGradeName()); // 名称不存在,或者是当前记录自己,则唯一 return info == null || info.getGradeId().equals(gradeId); } @Override public boolean checkGradeExistSchool(Long gradeId) { int count = schoolGradeMapper.countByGradeId(gradeId); return count > 0; } } ``` ### 5.3 区域Service接口 ```java package com.pangu.base.service; import com.pangu.base.domain.Region; import java.util.List; /** * 区域管理Service接口 * @author pangu */ public interface IRegionService { /** * 查询区域树 * @return 树形结构的区域列表 */ List selectRegionTree(); /** * 根据ID查询区域 * @param regionId 区域ID * @return 区域信息 */ Region selectRegionById(Long regionId); /** * 新增区域 * @param region 区域信息 * @return 影响行数 */ int insertRegion(Region region); /** * 修改区域 * @param region 区域信息 * @return 影响行数 */ int updateRegion(Region region); /** * 删除区域 * @param regionId 区域ID * @return 影响行数 */ int deleteRegionById(Long regionId); /** * 是否存在子区域 * @param regionId 区域ID * @return true-存在 false-不存在 */ boolean hasChildRegion(Long regionId); /** * 检查区域是否被学校使用 * @param regionId 区域ID * @return true-被使用 false-未使用 */ boolean checkRegionExistSchool(Long regionId); /** * 刷新区域缓存 */ void refreshRegionCache(); } ``` ### 5.4 区域Service实现 ```java package com.pangu.base.service.impl; import com.pangu.base.domain.Region; import com.pangu.base.mapper.MemberMapper; import com.pangu.base.mapper.RegionMapper; import com.pangu.base.mapper.SchoolMapper; import com.pangu.base.service.IRegionService; import com.pangu.common.core.redis.RedisCache; import com.pangu.common.utils.DateUtils; import com.pangu.common.utils.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; /** * 区域管理Service实现 * @author pangu */ @Service public class RegionServiceImpl implements IRegionService { /** 区域树缓存Key */ private static final String REGION_TREE_KEY = "base:region:tree"; /** 缓存过期时间(小时)*/ private static final int CACHE_EXPIRE_HOURS = 24; @Autowired private RegionMapper regionMapper; @Autowired private SchoolMapper schoolMapper; @Autowired private RedisCache redisCache; @Override public List selectRegionTree() { // 优先从缓存获取 List cacheList = redisCache.getCacheObject(REGION_TREE_KEY); if (cacheList != null && !cacheList.isEmpty()) { return cacheList; } // 查询所有区域(未删除的) List regionList = regionMapper.selectRegionList(new Region()); // 构建树形结构 List tree = buildRegionTree(regionList); // 放入缓存 redisCache.setCacheObject(REGION_TREE_KEY, tree, CACHE_EXPIRE_HOURS, TimeUnit.HOURS); return tree; } /** * 构建区域树形结构 * @param regionList 区域列表 * @return 树形结构 */ private List buildRegionTree(List regionList) { if (regionList == null || regionList.isEmpty()) { return new ArrayList<>(); } // 构建ID到节点的映射 Map regionMap = regionList.stream() .collect(Collectors.toMap(Region::getRegionId, r -> r)); List rootList = new ArrayList<>(); for (Region region : regionList) { if (region.getParentId() == 0) { // 顶级节点 rootList.add(region); } else { // 子节点,挂载到父节点下 Region parent = regionMap.get(region.getParentId()); if (parent != null) { if (parent.getChildren() == null) { parent.setChildren(new ArrayList<>()); } parent.getChildren().add(region); } } } // 对每一层按orderNum排序 sortRegionTree(rootList); return rootList; } /** * 递归排序区域树 */ private void sortRegionTree(List regions) { if (regions == null || regions.isEmpty()) { return; } regions.sort(Comparator.comparingInt(r -> r.getOrderNum() == null ? 0 : r.getOrderNum())); for (Region region : regions) { if (region.getChildren() != null) { sortRegionTree(region.getChildren()); } } } @Override public Region selectRegionById(Long regionId) { return regionMapper.selectRegionById(regionId); } @Override public int insertRegion(Region region) { // 设置层级和祖级列表 if (region.getParentId() == null || region.getParentId() == 0) { region.setParentId(0L); region.setLevel(1); region.setAncestors("0"); } else { Region parent = regionMapper.selectRegionById(region.getParentId()); if (parent == null) { throw new RuntimeException("父区域不存在"); } region.setLevel(parent.getLevel() + 1); region.setAncestors(parent.getAncestors() + "," + parent.getRegionId()); } // 生成区域编码 region.setRegionCode(generateRegionCode(region)); region.setCreateTime(DateUtils.getNowDate()); // 默认状态为启用 if (region.getStatus() == null) { region.setStatus("0"); } int rows = regionMapper.insertRegion(region); // 清除缓存 if (rows > 0) { refreshRegionCache(); } return rows; } /** * 生成区域编码 * 规则: * - 省级:REG + 2位序号(如REG01) * - 市级:父编码 + 2位序号(如REG0101) * - 区级:父编码 + 2位序号(如REG010101) */ private String generateRegionCode(Region region) { if (region.getParentId() == 0) { // 省级编码 String maxCode = regionMapper.selectMaxCodeByLevel(1); if (StringUtils.isEmpty(maxCode)) { return "REG01"; } int num = Integer.parseInt(maxCode.substring(3)) + 1; return String.format("REG%02d", num); } else { // 市/区级编码 Region parent = regionMapper.selectRegionById(region.getParentId()); String maxCode = regionMapper.selectMaxCodeByParent(region.getParentId()); if (StringUtils.isEmpty(maxCode)) { return parent.getRegionCode() + "01"; } String suffix = maxCode.substring(parent.getRegionCode().length()); int num = Integer.parseInt(suffix) + 1; return parent.getRegionCode() + String.format("%02d", num); } } @Override public int updateRegion(Region region) { region.setUpdateTime(DateUtils.getNowDate()); int rows = regionMapper.updateRegion(region); // 清除缓存 if (rows > 0) { refreshRegionCache(); } return rows; } @Override public int deleteRegionById(Long regionId) { int rows = regionMapper.deleteRegionById(regionId); // 清除缓存 if (rows > 0) { refreshRegionCache(); } return rows; } @Override public boolean hasChildRegion(Long regionId) { int count = regionMapper.countChildByParentId(regionId); return count > 0; } @Override public boolean checkRegionExistSchool(Long regionId) { int count = schoolMapper.countByRegionId(regionId); return count > 0; } @Override public void refreshRegionCache() { redisCache.deleteObject(REGION_TREE_KEY); } } ``` --- ## 6. Controller层设计 ### 6.1 年级Controller ```java package com.pangu.base.controller; import com.pangu.base.domain.Grade; import com.pangu.base.service.IGradeService; import com.pangu.common.annotation.Log; import com.pangu.common.core.controller.BaseController; import com.pangu.common.core.domain.AjaxResult; import com.pangu.common.core.page.TableDataInfo; import com.pangu.common.enums.BusinessType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; /** * 年级管理Controller * @author pangu */ @RestController @RequestMapping("/api/grade") public class GradeController extends BaseController { @Autowired private IGradeService gradeService; /** * 获取年级分页列表 */ @PreAuthorize("@ss.hasPermi('base:grade:list')") @GetMapping("/list") public TableDataInfo list(Grade grade) { startPage(); List list = gradeService.selectGradeList(grade); return getDataTable(list); } /** * 获取年级选项列表(下拉用) */ @GetMapping("/options") public AjaxResult options() { List list = gradeService.selectGradeOptions(); return success(list); } /** * 获取年级详情 */ @PreAuthorize("@ss.hasPermi('base:grade:query')") @GetMapping("/{gradeId}") public AjaxResult getInfo(@PathVariable Long gradeId) { return success(gradeService.selectGradeById(gradeId)); } /** * 新增年级 */ @PreAuthorize("@ss.hasPermi('base:grade:add')") @Log(title = "年级管理", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@Validated @RequestBody Grade grade) { // 校验名称唯一 if (!gradeService.checkGradeNameUnique(grade)) { return error("新增年级'" + grade.getGradeName() + "'失败,年级名称已存在"); } grade.setCreateBy(getUsername()); return toAjax(gradeService.insertGrade(grade)); } /** * 修改年级 */ @PreAuthorize("@ss.hasPermi('base:grade:edit')") @Log(title = "年级管理", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@Validated @RequestBody Grade grade) { // 校验名称唯一 if (!gradeService.checkGradeNameUnique(grade)) { return error("修改年级'" + grade.getGradeName() + "'失败,年级名称已存在"); } grade.setUpdateBy(getUsername()); return toAjax(gradeService.updateGrade(grade)); } /** * 删除年级 */ @PreAuthorize("@ss.hasPermi('base:grade:remove')") @Log(title = "年级管理", businessType = BusinessType.DELETE) @DeleteMapping("/{gradeId}") public AjaxResult remove(@PathVariable Long gradeId) { // 检查是否被学校引用 if (gradeService.checkGradeExistSchool(gradeId)) { return error("该年级已被学校使用,不能删除"); } return toAjax(gradeService.deleteGradeById(gradeId)); } } ``` ### 6.2 区域Controller ```java package com.pangu.base.controller; import com.pangu.base.domain.Region; import com.pangu.base.service.IRegionService; import com.pangu.common.annotation.Log; import com.pangu.common.core.controller.BaseController; import com.pangu.common.core.domain.AjaxResult; import com.pangu.common.enums.BusinessType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import java.util.List; /** * 区域管理Controller * @author pangu */ @RestController @RequestMapping("/api/region") public class RegionController extends BaseController { @Autowired private IRegionService regionService; /** * 获取区域树 */ @GetMapping("/tree") public AjaxResult tree() { List tree = regionService.selectRegionTree(); return success(tree); } /** * 获取区域详情 */ @PreAuthorize("@ss.hasPermi('base:region:query')") @GetMapping("/{regionId}") public AjaxResult getInfo(@PathVariable Long regionId) { return success(regionService.selectRegionById(regionId)); } /** * 新增区域 */ @PreAuthorize("@ss.hasPermi('base:region:add')") @Log(title = "区域管理", businessType = BusinessType.INSERT) @PostMapping public AjaxResult add(@Validated @RequestBody Region region) { region.setCreateBy(getUsername()); return toAjax(regionService.insertRegion(region)); } /** * 修改区域 */ @PreAuthorize("@ss.hasPermi('base:region:edit')") @Log(title = "区域管理", businessType = BusinessType.UPDATE) @PutMapping public AjaxResult edit(@Validated @RequestBody Region region) { // 父级不能选择自己 if (region.getRegionId().equals(region.getParentId())) { return error("修改区域'" + region.getRegionName() + "'失败,上级区域不能选择自己"); } region.setUpdateBy(getUsername()); return toAjax(regionService.updateRegion(region)); } /** * 删除区域 */ @PreAuthorize("@ss.hasPermi('base:region:remove')") @Log(title = "区域管理", businessType = BusinessType.DELETE) @DeleteMapping("/{regionId}") public AjaxResult remove(@PathVariable Long regionId) { // 检查是否有子区域 if (regionService.hasChildRegion(regionId)) { return error("存在下级区域,不能删除"); } // 检查是否被学校引用 if (regionService.checkRegionExistSchool(regionId)) { return error("该区域已被学校使用,不能删除"); } return toAjax(regionService.deleteRegionById(regionId)); } /** * 刷新区域缓存 */ @PreAuthorize("@ss.hasPermi('base:region:remove')") @Log(title = "区域管理", businessType = BusinessType.CLEAN) @DeleteMapping("/refreshCache") public AjaxResult refreshCache() { regionService.refreshRegionCache(); return success(); } } ``` --- ## 7. 权限配置 ### 7.1 菜单权限 在系统菜单中添加以下菜单: | 菜单名称 | 菜单路径 | 权限标识 | 菜单类型 | | ---- | ------------- | ---------------- | ---- | | 基础数据 | /base | - | 目录 | | 年级管理 | /base/grade | base:grade:list | 菜单 | | 班级管理 | /base/class | base:class:list | 菜单 | | 学科管理 | /base/subject | base:subject:list | 菜单 | | 区域管理 | /base/region | base:region:list | 菜单 | ### 7.2 按钮权限 | 功能 | 权限标识 | | ---- | ----------------- | | 年级查询 | base:grade:query | | 年级新增 | base:grade:add | | 年级修改 | base:grade:edit | | 年级删除 | base:grade:remove | | 班级查询 | base:class:query | | 班级新增 | base:class:add | | 班级修改 | base:class:edit | | 班级删除 | base:class:remove | | 学科查询 | base:subject:query | | 学科新增 | base:subject:add | | 学科修改 | base:subject:edit | | 学科删除 | base:subject:remove | | 区域查询 | base:region:query | | 区域新增 | base:region:add | | 区域修改 | base:region:edit | | 区域删除 | base:region:remove | --- ## 8. 单元测试 ### 8.1 年级Service测试 ```java package com.pangu.base.service; import com.pangu.base.domain.Grade; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; import static org.junit.jupiter.api.Assertions.*; /** * 年级Service单元测试 * @author pangu */ @SpringBootTest public class GradeServiceTest { @Autowired private IGradeService gradeService; @Test public void testSelectGradeList() { Grade grade = new Grade(); List list = gradeService.selectGradeList(grade); assertNotNull(list); assertTrue(list.size() > 0); } @Test public void testSelectGradeOptions() { List list = gradeService.selectGradeOptions(); assertNotNull(list); // 选项列表应该只包含启用的 for (Grade grade : list) { assertEquals("0", grade.getStatus()); } } @Test public void testInsertGrade() { Grade grade = new Grade(); grade.setGradeName("测试年级"); grade.setOrderNum(99); grade.setStatus("0"); grade.setCreateBy("test"); int result = gradeService.insertGrade(grade); assertEquals(1, result); assertNotNull(grade.getGradeId()); assertNotNull(grade.getGradeCode()); assertTrue(grade.getGradeCode().startsWith("GRD")); // 清理测试数据 gradeService.deleteGradeById(grade.getGradeId()); } @Test public void testCheckGradeNameUnique() { // 新增时检查 Grade grade = new Grade(); grade.setGradeName("一年级"); // 假设已存在 boolean unique = gradeService.checkGradeNameUnique(grade); assertFalse(unique); // 不存在的名称 grade.setGradeName("不存在的年级"); unique = gradeService.checkGradeNameUnique(grade); assertTrue(unique); } } ``` ### 8.2 区域Service测试 ```java package com.pangu.base.service; import com.pangu.base.domain.Region; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import java.util.List; import static org.junit.jupiter.api.Assertions.*; /** * 区域Service单元测试 * @author pangu */ @SpringBootTest public class RegionServiceTest { @Autowired private IRegionService regionService; @Test public void testSelectRegionTree() { List tree = regionService.selectRegionTree(); assertNotNull(tree); assertTrue(tree.size() > 0); // 验证树形结构 Region root = tree.get(0); assertEquals(Long.valueOf(0), root.getParentId()); assertEquals(Integer.valueOf(1), root.getLevel()); } @Test public void testInsertRegion() { // 新增省级区域 Region region = new Region(); region.setParentId(0L); region.setRegionName("测试省"); region.setOrderNum(99); region.setCreateBy("test"); int result = regionService.insertRegion(region); assertEquals(1, result); assertNotNull(region.getRegionId()); assertNotNull(region.getRegionCode()); assertEquals(Integer.valueOf(1), region.getLevel()); assertEquals("0", region.getAncestors()); // 清理测试数据 regionService.deleteRegionById(region.getRegionId()); } @Test public void testHasChildRegion() { // 湖北省应该有子区域 boolean hasChild = regionService.hasChildRegion(1L); assertTrue(hasChild); } @Test public void testRefreshRegionCache() { // 第一次查询,建立缓存 List tree1 = regionService.selectRegionTree(); // 刷新缓存 regionService.refreshRegionCache(); // 第二次查询,重新从数据库加载 List tree2 = regionService.selectRegionTree(); // 数据应该一致 assertEquals(tree1.size(), tree2.size()); } } ``` --- ## 9. 开发检查清单 ### 9.1 年级管理 - [ ] Grade 实体类 - [ ] GradeMapper 接口 - [ ] GradeMapper.xml - [ ] IGradeService 接口 - [ ] GradeServiceImpl 实现 - [ ] GradeController - [ ] 单元测试 - [ ] 权限配置 ### 9.2 班级管理 - [ ] PgClass 实体类 - [ ] PgClassMapper 接口 - [ ] PgClassMapper.xml - [ ] IPgClassService 接口 - [ ] PgClassServiceImpl 实现 - [ ] PgClassController - [ ] 单元测试 - [ ] 权限配置 ### 9.3 学科管理 - [ ] Subject 实体类 - [ ] SubjectMapper 接口 - [ ] SubjectMapper.xml - [ ] ISubjectService 接口 - [ ] SubjectServiceImpl 实现 - [ ] SubjectController - [ ] 单元测试 - [ ] 权限配置 ### 9.4 区域管理 - [ ] Region 实体类 - [ ] RegionMapper 接口 - [ ] RegionMapper.xml - [ ] IRegionService 接口 - [ ] RegionServiceImpl 实现(含缓存) - [ ] RegionController - [ ] 单元测试 - [ ] 权限配置 --- *文档结束*