1579 lines
42 KiB
Markdown
1579 lines
42 KiB
Markdown
# 盘古用户平台 - 基础数据模块后端开发文档
|
||
|
||
---
|
||
|
||
| 文档信息 | 内容 |
|
||
| -------- | --------------------------- |
|
||
| **文档版本** | 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<Region> 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<Grade> {
|
||
|
||
/**
|
||
* 查询年级列表
|
||
* @param grade 查询条件
|
||
* @return 年级列表
|
||
*/
|
||
List<Grade> 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
|
||
<?xml version="1.0" encoding="UTF-8" ?>
|
||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||
<mapper namespace="com.pangu.base.mapper.GradeMapper">
|
||
|
||
<resultMap id="GradeResult" type="Grade">
|
||
<id property="gradeId" column="grade_id"/>
|
||
<result property="gradeCode" column="grade_code"/>
|
||
<result property="gradeName" column="grade_name"/>
|
||
<result property="orderNum" column="order_num"/>
|
||
<result property="status" column="status"/>
|
||
<result property="delFlag" column="del_flag"/>
|
||
<result property="createBy" column="create_by"/>
|
||
<result property="createTime" column="create_time"/>
|
||
<result property="updateBy" column="update_by"/>
|
||
<result property="updateTime" column="update_time"/>
|
||
<result property="remark" column="remark"/>
|
||
</resultMap>
|
||
|
||
<sql id="selectGradeVo">
|
||
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'
|
||
</sql>
|
||
|
||
<!-- 查询年级列表 -->
|
||
<select id="selectGradeList" parameterType="Grade" resultMap="GradeResult">
|
||
<include refid="selectGradeVo"/>
|
||
<if test="gradeName != null and gradeName != ''">
|
||
AND grade_name like concat('%', #{gradeName}, '%')
|
||
</if>
|
||
<if test="gradeCode != null and gradeCode != ''">
|
||
AND grade_code = #{gradeCode}
|
||
</if>
|
||
<if test="status != null and status != ''">
|
||
AND status = #{status}
|
||
</if>
|
||
order by order_num
|
||
</select>
|
||
|
||
<!-- 根据ID查询年级 -->
|
||
<select id="selectGradeById" parameterType="Long" resultMap="GradeResult">
|
||
<include refid="selectGradeVo"/>
|
||
AND grade_id = #{gradeId}
|
||
</select>
|
||
|
||
<!-- 新增年级 -->
|
||
<insert id="insertGrade" parameterType="Grade" useGeneratedKeys="true" keyProperty="gradeId">
|
||
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}
|
||
)
|
||
</insert>
|
||
|
||
<!-- 修改年级 -->
|
||
<update id="updateGrade" parameterType="Grade">
|
||
update pg_grade
|
||
<set>
|
||
<if test="gradeName != null and gradeName != ''">grade_name = #{gradeName},</if>
|
||
<if test="orderNum != null">order_num = #{orderNum},</if>
|
||
<if test="status != null">status = #{status},</if>
|
||
<if test="remark != null">remark = #{remark},</if>
|
||
update_by = #{updateBy},
|
||
update_time = #{updateTime}
|
||
</set>
|
||
where grade_id = #{gradeId}
|
||
</update>
|
||
|
||
<!-- 删除年级(软删除)-->
|
||
<update id="deleteGradeById" parameterType="Long">
|
||
update pg_grade set del_flag = '1' where grade_id = #{gradeId}
|
||
</update>
|
||
|
||
<!-- 校验年级名称唯一 -->
|
||
<select id="checkGradeNameUnique" parameterType="String" resultMap="GradeResult">
|
||
<include refid="selectGradeVo"/>
|
||
AND grade_name = #{gradeName}
|
||
limit 1
|
||
</select>
|
||
|
||
<!-- 查询最大编码 -->
|
||
<select id="selectMaxGradeCode" resultType="String">
|
||
select max(grade_code) from pg_grade where del_flag = '0'
|
||
</select>
|
||
|
||
</mapper>
|
||
```
|
||
|
||
### 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<Region> {
|
||
|
||
/**
|
||
* 查询区域列表
|
||
* @param region 查询条件
|
||
* @return 区域列表
|
||
*/
|
||
List<Region> 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
|
||
<?xml version="1.0" encoding="UTF-8" ?>
|
||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||
<mapper namespace="com.pangu.base.mapper.RegionMapper">
|
||
|
||
<resultMap id="RegionResult" type="Region">
|
||
<id property="regionId" column="region_id"/>
|
||
<result property="parentId" column="parent_id"/>
|
||
<result property="regionName" column="region_name"/>
|
||
<result property="regionCode" column="region_code"/>
|
||
<result property="level" column="level"/>
|
||
<result property="ancestors" column="ancestors"/>
|
||
<result property="orderNum" column="order_num"/>
|
||
<result property="status" column="status"/>
|
||
<result property="delFlag" column="del_flag"/>
|
||
<result property="createBy" column="create_by"/>
|
||
<result property="createTime" column="create_time"/>
|
||
<result property="updateBy" column="update_by"/>
|
||
<result property="updateTime" column="update_time"/>
|
||
</resultMap>
|
||
|
||
<sql id="selectRegionVo">
|
||
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'
|
||
</sql>
|
||
|
||
<!-- 查询区域列表 -->
|
||
<select id="selectRegionList" parameterType="Region" resultMap="RegionResult">
|
||
<include refid="selectRegionVo"/>
|
||
<if test="regionName != null and regionName != ''">
|
||
AND region_name like concat('%', #{regionName}, '%')
|
||
</if>
|
||
<if test="status != null and status != ''">
|
||
AND status = #{status}
|
||
</if>
|
||
<if test="parentId != null">
|
||
AND parent_id = #{parentId}
|
||
</if>
|
||
order by order_num
|
||
</select>
|
||
|
||
<!-- 根据ID查询区域 -->
|
||
<select id="selectRegionById" parameterType="Long" resultMap="RegionResult">
|
||
<include refid="selectRegionVo"/>
|
||
AND region_id = #{regionId}
|
||
</select>
|
||
|
||
<!-- 新增区域 -->
|
||
<insert id="insertRegion" parameterType="Region" useGeneratedKeys="true" keyProperty="regionId">
|
||
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}
|
||
)
|
||
</insert>
|
||
|
||
<!-- 修改区域 -->
|
||
<update id="updateRegion" parameterType="Region">
|
||
update pg_region
|
||
<set>
|
||
<if test="regionName != null and regionName != ''">region_name = #{regionName},</if>
|
||
<if test="orderNum != null">order_num = #{orderNum},</if>
|
||
<if test="status != null">status = #{status},</if>
|
||
update_by = #{updateBy},
|
||
update_time = #{updateTime}
|
||
</set>
|
||
where region_id = #{regionId}
|
||
</update>
|
||
|
||
<!-- 删除区域(软删除)-->
|
||
<update id="deleteRegionById" parameterType="Long">
|
||
update pg_region set del_flag = '1' where region_id = #{regionId}
|
||
</update>
|
||
|
||
<!-- 查询子区域数量 -->
|
||
<select id="countChildByParentId" parameterType="Long" resultType="int">
|
||
select count(*) from pg_region where parent_id = #{parentId} and del_flag = '0'
|
||
</select>
|
||
|
||
<!-- 查询指定层级的最大编码 -->
|
||
<select id="selectMaxCodeByLevel" parameterType="int" resultType="String">
|
||
select max(region_code) from pg_region where level = #{level} and del_flag = '0'
|
||
</select>
|
||
|
||
<!-- 查询指定父级下的最大编码 -->
|
||
<select id="selectMaxCodeByParent" parameterType="Long" resultType="String">
|
||
select max(region_code) from pg_region where parent_id = #{parentId} and del_flag = '0'
|
||
</select>
|
||
|
||
</mapper>
|
||
```
|
||
|
||
---
|
||
|
||
## 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<Grade> selectGradeList(Grade grade);
|
||
|
||
/**
|
||
* 查询年级选项列表(下拉用)
|
||
* @return 启用状态的年级列表
|
||
*/
|
||
List<Grade> 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<Grade> selectGradeList(Grade grade) {
|
||
return gradeMapper.selectGradeList(grade);
|
||
}
|
||
|
||
@Override
|
||
public List<Grade> 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<Region> 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<Region> selectRegionTree() {
|
||
// 优先从缓存获取
|
||
List<Region> cacheList = redisCache.getCacheObject(REGION_TREE_KEY);
|
||
if (cacheList != null && !cacheList.isEmpty()) {
|
||
return cacheList;
|
||
}
|
||
|
||
// 查询所有区域(未删除的)
|
||
List<Region> regionList = regionMapper.selectRegionList(new Region());
|
||
|
||
// 构建树形结构
|
||
List<Region> tree = buildRegionTree(regionList);
|
||
|
||
// 放入缓存
|
||
redisCache.setCacheObject(REGION_TREE_KEY, tree, CACHE_EXPIRE_HOURS, TimeUnit.HOURS);
|
||
|
||
return tree;
|
||
}
|
||
|
||
/**
|
||
* 构建区域树形结构
|
||
* @param regionList 区域列表
|
||
* @return 树形结构
|
||
*/
|
||
private List<Region> buildRegionTree(List<Region> regionList) {
|
||
if (regionList == null || regionList.isEmpty()) {
|
||
return new ArrayList<>();
|
||
}
|
||
|
||
// 构建ID到节点的映射
|
||
Map<Long, Region> regionMap = regionList.stream()
|
||
.collect(Collectors.toMap(Region::getRegionId, r -> r));
|
||
|
||
List<Region> 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<Region> 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<Grade> list = gradeService.selectGradeList(grade);
|
||
return getDataTable(list);
|
||
}
|
||
|
||
/**
|
||
* 获取年级选项列表(下拉用)
|
||
*/
|
||
@GetMapping("/options")
|
||
public AjaxResult options() {
|
||
List<Grade> 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<Region> 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<Grade> list = gradeService.selectGradeList(grade);
|
||
assertNotNull(list);
|
||
assertTrue(list.size() > 0);
|
||
}
|
||
|
||
@Test
|
||
public void testSelectGradeOptions() {
|
||
List<Grade> 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<Region> 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<Region> tree1 = regionService.selectRegionTree();
|
||
|
||
// 刷新缓存
|
||
regionService.refreshRegionCache();
|
||
|
||
// 第二次查询,重新从数据库加载
|
||
List<Region> 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
|
||
- [ ] 单元测试
|
||
- [ ] 权限配置
|
||
|
||
---
|
||
|
||
*文档结束*
|