员工信息管理
- 新增员工信息CRUD功能 - 添加员工关联人员管理 - 配置MyBatis Plus审计字段 - 添加OpenSpec规范文档 - 新增测试脚本和数据 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,16 @@
|
|||||||
"mcp__zai-mcp-server__extract_text_from_screenshot",
|
"mcp__zai-mcp-server__extract_text_from_screenshot",
|
||||||
"Bash(pandoc:*)",
|
"Bash(pandoc:*)",
|
||||||
"mcp__zread__read_file",
|
"mcp__zread__read_file",
|
||||||
"mcp__zread__search_doc"
|
"mcp__zread__search_doc",
|
||||||
|
"Bash(cmd:*)",
|
||||||
|
"Bash(curl:*)",
|
||||||
|
"Bash(mvn clean install:*)",
|
||||||
|
"Bash(powershell:*)",
|
||||||
|
"Skill(document-skills:mcp-builder)",
|
||||||
|
"Bash(ping:*)"
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
"enabledMcpjsonServers": [
|
||||||
|
"mysql"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
43
CLAUDE.md
43
CLAUDE.md
@@ -1,4 +1,47 @@
|
|||||||
# CLAUDE.md
|
# CLAUDE.md
|
||||||
|
## 分析
|
||||||
|
- 在进行需求分析类型的任务时,自动开启深度思考模式,输入 “think more”、“think a lot”、“think harder” 或 “think longer” 触发更深层的思考
|
||||||
|
- 在进行需求分析与分解任务时,按照不同的模块分为不同的文件,创建模块名的文件夹并将对应文件保存在文件夹中,然后对模块的功能文件进行继续分解
|
||||||
|
- 在使用/openspec:proposal时,自动开启深度思考模式,输入 “think more”、“think a lot”、“think harder” 或 “think longer” 触发更深层的思考
|
||||||
|
- 在完成/openspec:apply后,使用code-simplifier 进行代码精简
|
||||||
|
|
||||||
|
## Communication
|
||||||
|
- 永远使用简体中文进行思考和对话
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
- 编写 .md 文档时,也要用中文
|
||||||
|
- 所有生成的文档都放在项目根目录下的doc文件中。
|
||||||
|
|
||||||
|
## 数据库规范
|
||||||
|
- 新建表时,需要加上项目英文名首字母集合
|
||||||
|
|
||||||
|
|
||||||
|
## Coding
|
||||||
|
### Java Code Style
|
||||||
|
- 新建模块命名方式为项目英文名首字母集合+主要功能
|
||||||
|
- 新的功能代码与若依框架自带的代码分离,新建模块,controller层也要放在新建模块中
|
||||||
|
- 使用 `@Data` 注解保证代码的简洁
|
||||||
|
- 尽量使用 MyBatis Plus 进行 CRUD 操作(版本 3.5.10,Spring Boot 3 适配版)
|
||||||
|
- 服务层中的使用@Resource注释,替代@Autowired
|
||||||
|
- 实体类不继承BaseEntity,单独添加审计字段
|
||||||
|
- 完成后端代码controller层代码生成测试后,在项目文件目录下生成API文档
|
||||||
|
- 接口传参需要使用单独的DTO,不可以与entity混用
|
||||||
|
- 需要单独的VO类,不可以与entity混用
|
||||||
|
- 审计字段通过添加注释的方式实现自动插入
|
||||||
|
- 简单的crud操作通过mybatis plus的方法实现,复杂的操作通过xml中写sql和mapper映射实现
|
||||||
|
|
||||||
|
|
||||||
|
### 前端代码
|
||||||
|
- 在添加页面和组件后,注意与数据库中菜单表进行联动修改
|
||||||
|
|
||||||
|
|
||||||
|
## 运行过程中
|
||||||
|
- 测试方式为生成可执行的测试脚本
|
||||||
|
- 在测试中启动的进程,在测试完成后立刻结束
|
||||||
|
- /login/test接口可以传入username和password获取token,用于测试验证接口的功能。
|
||||||
|
用于测试的账号:username: admin password admin123
|
||||||
|
- swagger-ui的地址为/swagger-ui/index.html
|
||||||
|
|
||||||
|
|
||||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
|||||||
315
doc/员工信息管理API文档.md
Normal file
315
doc/员工信息管理API文档.md
Normal file
@@ -0,0 +1,315 @@
|
|||||||
|
# 员工信息管理 API 文档
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
员工信息管理模块提供员工及其亲属信息的增删改查、批量导入导出功能。
|
||||||
|
|
||||||
|
**基础路径**: `/dpc/employee`
|
||||||
|
|
||||||
|
**权限标识前缀**: `dpc:employee`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API 接口列表
|
||||||
|
|
||||||
|
### 1. 查询员工列表
|
||||||
|
|
||||||
|
**接口地址**: `GET /dpc/employee/list`
|
||||||
|
|
||||||
|
**权限要求**: `dpc:employee:list`
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| name | String | 否 | 姓名(模糊查询) |
|
||||||
|
| tellerNo | String | 否 | 柜员号(精确查询) |
|
||||||
|
| orgNo | String | 否 | 所属机构号 |
|
||||||
|
| idCard | String | 否 | 身份证号(精确查询) |
|
||||||
|
| status | String | 否 | 状态(0=在职, 1=离职) |
|
||||||
|
| pageNum | Integer | 否 | 页码(默认1) |
|
||||||
|
| pageSize | Integer | 否 | 每页数量(默认10) |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"employeeId": 1,
|
||||||
|
"name": "张三",
|
||||||
|
"tellerNo": "001",
|
||||||
|
"orgNo": "1001",
|
||||||
|
"idCard": "110101199001011234",
|
||||||
|
"phone": "13800138000",
|
||||||
|
"hireDate": "2020-01-01",
|
||||||
|
"status": "0",
|
||||||
|
"statusDesc": "在职",
|
||||||
|
"createTime": "2026-01-28 10:00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 查询员工详情
|
||||||
|
|
||||||
|
**接口地址**: `GET /dpc/employee/{employeeId}`
|
||||||
|
|
||||||
|
**权限要求**: `dpc:employee:query`
|
||||||
|
|
||||||
|
**路径参数**:
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| employeeId | Long | 是 | 员工ID |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功",
|
||||||
|
"data": {
|
||||||
|
"employeeId": 1,
|
||||||
|
"name": "张三",
|
||||||
|
"tellerNo": "001",
|
||||||
|
"orgNo": "1001",
|
||||||
|
"idCard": "110101199001011234",
|
||||||
|
"phone": "13800138000",
|
||||||
|
"hireDate": "2020-01-01",
|
||||||
|
"status": "0",
|
||||||
|
"statusDesc": "在职",
|
||||||
|
"createTime": "2026-01-28 10:00:00",
|
||||||
|
"relatives": [
|
||||||
|
{
|
||||||
|
"relativeId": 1,
|
||||||
|
"employeeId": 1,
|
||||||
|
"relativeName": "李四",
|
||||||
|
"relativeIdCard": "110101199001011235",
|
||||||
|
"relativePhone": "13800138001",
|
||||||
|
"relationship": "配偶"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 新增员工
|
||||||
|
|
||||||
|
**接口地址**: `POST /dpc/employee`
|
||||||
|
|
||||||
|
**权限要求**: `dpc:employee:add`
|
||||||
|
|
||||||
|
**请求头**:
|
||||||
|
```
|
||||||
|
Content-Type: application/json
|
||||||
|
Authorization: Bearer {token}
|
||||||
|
```
|
||||||
|
|
||||||
|
**请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "张三",
|
||||||
|
"tellerNo": "001",
|
||||||
|
"orgNo": "1001",
|
||||||
|
"idCard": "110101199001011234",
|
||||||
|
"phone": "13800138000",
|
||||||
|
"hireDate": "2020-01-01",
|
||||||
|
"status": "0",
|
||||||
|
"relatives": [
|
||||||
|
{
|
||||||
|
"relativeName": "李四",
|
||||||
|
"relativeIdCard": "110101199001011235",
|
||||||
|
"relativePhone": "13800138001",
|
||||||
|
"relationship": "配偶"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**字段说明**:
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 必填 | 说明 | 校验规则 |
|
||||||
|
|--------|------|------|------|----------|
|
||||||
|
| name | String | 是 | 姓名 | 最大100字符 |
|
||||||
|
| tellerNo | String | 是 | 柜员号 | 最大50字符,唯一 |
|
||||||
|
| orgNo | String | 否 | 所属机构号 | 最大50字符 |
|
||||||
|
| idCard | String | 是 | 身份证号 | 18位,符合国标,唯一 |
|
||||||
|
| phone | String | 否 | 电话 | 11位手机号 |
|
||||||
|
| hireDate | Date | 否 | 入职时间 | yyyy-MM-dd |
|
||||||
|
| status | String | 是 | 状态 | 0=在职, 1=离职 |
|
||||||
|
| relatives | Array | 否 | 亲属列表 | |
|
||||||
|
|
||||||
|
**亲属对象字段**:
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| relativeName | String | 是 | 亲属姓名 |
|
||||||
|
| relativeIdCard | String | 否 | 亲属身份证号 |
|
||||||
|
| relativePhone | String | 否 | 亲属手机号 |
|
||||||
|
| relationship | String | 是 | 与员工关系 |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. 编辑员工
|
||||||
|
|
||||||
|
**接口地址**: `PUT /dpc/employee`
|
||||||
|
|
||||||
|
**权限要求**: `dpc:employee:edit`
|
||||||
|
|
||||||
|
**请求体**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"employeeId": 1,
|
||||||
|
"name": "张三",
|
||||||
|
"tellerNo": "001",
|
||||||
|
"orgNo": "1001",
|
||||||
|
"idCard": "110101199001011234",
|
||||||
|
"phone": "13800138000",
|
||||||
|
"hireDate": "2020-01-01",
|
||||||
|
"status": "0",
|
||||||
|
"relatives": [
|
||||||
|
{
|
||||||
|
"relativeName": "李四",
|
||||||
|
"relativeIdCard": "110101199001011235",
|
||||||
|
"relativePhone": "13800138001",
|
||||||
|
"relationship": "配偶"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**字段说明**: 与新增接口相同,employeeId 为必填项。
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. 删除员工
|
||||||
|
|
||||||
|
**接口地址**: `DELETE /dpc/employee/{employeeIds}`
|
||||||
|
|
||||||
|
**权限要求**: `dpc:employee:remove`
|
||||||
|
|
||||||
|
**路径参数**:
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| employeeIds | Long[] | 是 | 员工ID数组(逗号分隔) |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "操作成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意**: 删除员工时会级联删除该员工的所有亲属信息。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. 导出员工信息
|
||||||
|
|
||||||
|
**接口地址**: `POST /dpc/employee/export`
|
||||||
|
|
||||||
|
**权限要求**: `dpc:employee:export`
|
||||||
|
|
||||||
|
**请求参数**: 与查询列表接口相同(支持筛选条件)
|
||||||
|
|
||||||
|
**响应**: Excel 文件下载
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. 下载导入模板
|
||||||
|
|
||||||
|
**接口地址**: `POST /dpc/employee/importTemplate`
|
||||||
|
|
||||||
|
**权限要求**: 无
|
||||||
|
|
||||||
|
**响应**: Excel 模板文件下载
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 8. 导入员工信息
|
||||||
|
|
||||||
|
**接口地址**: `POST /dpc/employee/importData`
|
||||||
|
|
||||||
|
**权限要求**: `dpc:employee:import`
|
||||||
|
|
||||||
|
**请求参数**:
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| file | File | 是 | Excel 文件 |
|
||||||
|
| updateSupport | Boolean | 否 | 是否更新已存在数据(默认false) |
|
||||||
|
|
||||||
|
**Excel 格式**:
|
||||||
|
|
||||||
|
**Sheet1: 员工信息**
|
||||||
|
| 姓名 | 柜员号 | 所属机构号 | 身份证号 | 电话 | 入职时间 | 状态 |
|
||||||
|
|------|--------|------------|----------|------|----------|------|
|
||||||
|
| 张三 | 001 | 1001 | 110101199001011234 | 13800138000 | 2020-01-01 | 在职 |
|
||||||
|
|
||||||
|
**Sheet2: 亲属信息(可选)**
|
||||||
|
| 员工身份证号 | 亲属姓名 | 亲属身份证号 | 亲属手机号 | 与员工关系 |
|
||||||
|
|--------------|----------|--------------|------------|------------|
|
||||||
|
| 110101199001011234 | 李四 | 110101199001011235 | 13800138001 | 配偶 |
|
||||||
|
|
||||||
|
**响应示例**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"msg": "恭喜您,数据已全部导入成功!共 10 条"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 错误码说明
|
||||||
|
|
||||||
|
| 错误码 | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| 200 | 操作成功 |
|
||||||
|
| 401 | 未授权,请先登录 |
|
||||||
|
| 403 | 无权限访问 |
|
||||||
|
| 500 | 服务器内部错误 |
|
||||||
|
|
||||||
|
## 业务错误信息
|
||||||
|
|
||||||
|
| 错误信息 | 说明 |
|
||||||
|
|----------|------|
|
||||||
|
| 该柜员号已存在 | 新增/编辑时柜员号重复 |
|
||||||
|
| 该身份证号已存在 | 新增/编辑时身份证号重复 |
|
||||||
|
| 姓名不能为空 | 新增时姓名为空 |
|
||||||
|
| 身份证号格式不正确 | 身份证号不符合18位国标 |
|
||||||
|
| 电话格式不正确 | 手机号不符合11位格式 |
|
||||||
|
| 状态只能填写'在职'或'离职' | 状态值不正确 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 测试账号
|
||||||
|
|
||||||
|
- 用户名: `admin`
|
||||||
|
- 密码: `admin123`
|
||||||
|
|
||||||
|
测试前请先调用 `/login/test` 接口获取 Token。
|
||||||
252
openspec/changes/add-employee-info/design.md
Normal file
252
openspec/changes/add-employee-info/design.md
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
## Context
|
||||||
|
|
||||||
|
员工信息维护是纪检初核系统的核心基础功能。系统需要管理银行内部员工的基础信息及其亲属关系,以便在进行纪检初核工作时能够快速查询相关人员信息。
|
||||||
|
|
||||||
|
**约束条件:**
|
||||||
|
- 必须遵循若依框架的代码规范
|
||||||
|
- 必须使用项目已定义的命名规范(模块前缀 `dpc_`)
|
||||||
|
- 必须支持 Excel 导入导出功能
|
||||||
|
- 亲属信息需要与员工信息关联管理
|
||||||
|
|
||||||
|
**相关方:**
|
||||||
|
- 纪检人员:查询员工及亲属信息
|
||||||
|
- 系统管理员:批量导入员工数据
|
||||||
|
- 人事部门:维护员工基础信息
|
||||||
|
|
||||||
|
## Goals / Non-Goals
|
||||||
|
|
||||||
|
### Goals
|
||||||
|
1. 提供完整的员工信息 CRUD 接口
|
||||||
|
2. 支持员工亲属信息的关联管理(一对多关系)
|
||||||
|
3. 支持 Excel 批量导入导出,导入时可同时导入亲属信息
|
||||||
|
4. 遵循现有代码模式(参考 `dpc_intermediary_blacklist` 模块)
|
||||||
|
|
||||||
|
### Non-Goals
|
||||||
|
- 不涉及前端页面的实现(本次仅实现后端接口)
|
||||||
|
- 不涉及员工组织架构的复杂层级管理
|
||||||
|
- 不涉及员工权限、角色管理(使用若依现有系统)
|
||||||
|
- 不涉及亲属关系的高级查询功能
|
||||||
|
|
||||||
|
## Decisions
|
||||||
|
|
||||||
|
### 1. 数据模型设计
|
||||||
|
|
||||||
|
**决策:使用两张表存储员工和亲属信息**
|
||||||
|
|
||||||
|
```
|
||||||
|
dpc_employee (员工主表)
|
||||||
|
├── employee_id (主键)
|
||||||
|
├── name (姓名)
|
||||||
|
├── teller_no (柜员号, UNIQUE)
|
||||||
|
├── org_no (所属机构号)
|
||||||
|
├── id_card (身份证号, UNIQUE)
|
||||||
|
├── phone (电话)
|
||||||
|
├── hire_date (入职时间)
|
||||||
|
├── status (状态: 0=在职, 1=离职)
|
||||||
|
└── 审计字段 (create_by, create_time, update_by, update_time)
|
||||||
|
|
||||||
|
dpc_employee_relative (员工亲属表)
|
||||||
|
├── relative_id (主键)
|
||||||
|
├── employee_id (外键 → dpc_employee.employee_id)
|
||||||
|
├── relative_name (亲属姓名)
|
||||||
|
├── relative_id_card (亲属身份证号)
|
||||||
|
├── relative_phone (亲属手机号)
|
||||||
|
├── relationship (与员工关系)
|
||||||
|
└── 审计字段
|
||||||
|
```
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
- 符合数据库范式设计,避免数据冗余
|
||||||
|
- 支持一对多关系(一个员工可以有多个亲属)
|
||||||
|
- 便于查询和维护
|
||||||
|
|
||||||
|
**替代方案考虑:**
|
||||||
|
- 方案2:将亲属信息存储为 JSON 字段
|
||||||
|
- 优点:单表存储,查询简单
|
||||||
|
- 缺点:无法对亲属信息建索引,不支持复杂查询
|
||||||
|
- **拒绝原因**:未来可能需要按亲属信息查询
|
||||||
|
|
||||||
|
### 2. 亲属信息维护方式
|
||||||
|
|
||||||
|
**决策:在员工的新增/编辑接口中同时支持亲属信息**
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /dpc/employee
|
||||||
|
{
|
||||||
|
"name": "张三",
|
||||||
|
"tellerNo": "001",
|
||||||
|
"orgNo": "1001",
|
||||||
|
"idCard": "110101199001011234",
|
||||||
|
"phone": "13800138000",
|
||||||
|
"hireDate": "2020-01-01",
|
||||||
|
"relatives": [
|
||||||
|
{
|
||||||
|
"relativeName": "李四",
|
||||||
|
"relativeIdCard": "110101199001011235",
|
||||||
|
"relativePhone": "13800138001",
|
||||||
|
"relationship": "配偶"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
- 减少接口数量,简化前端调用
|
||||||
|
- 保证员工与亲属信息的原子性操作
|
||||||
|
- 符合业务场景(新增员工时同时录入亲属信息)
|
||||||
|
|
||||||
|
### 3. Excel 导入格式
|
||||||
|
|
||||||
|
**决策:使用多 Sheet 导入方式**
|
||||||
|
|
||||||
|
```
|
||||||
|
Sheet1: 员工信息
|
||||||
|
| 姓名 | 柜员号 | 所属机构号 | 身份证号 | 电话 | 入职时间 |
|
||||||
|
|------|--------|------------|----------|------|----------|
|
||||||
|
| 张三 | 001 | 1001 | ... | ... | 2020-01-01 |
|
||||||
|
|
||||||
|
Sheet2: 亲属信息 (可选)
|
||||||
|
| 员工身份证号 | 亲属姓名 | 亲属身份证号 | 亲属手机号 | 与员工关系 |
|
||||||
|
|--------------|----------|--------------|------------|------------|
|
||||||
|
| 110101... | 李四 | 110101... | 138... | 配偶 |
|
||||||
|
```
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
- 清晰分离员工和亲属数据
|
||||||
|
- 支持仅导入员工信息(亲属信息为可选)
|
||||||
|
- 通过员工身份证号关联两张表
|
||||||
|
|
||||||
|
**替代方案考虑:**
|
||||||
|
- 方案2:单 Sheet 导入,亲属信息嵌套在员工行中
|
||||||
|
- 缺点:格式复杂,Excel 难以编辑
|
||||||
|
- **拒绝原因**:用户体验差
|
||||||
|
|
||||||
|
### 4. 字典数据
|
||||||
|
|
||||||
|
**决策:使用字典管理"与员工关系"字段和"员工状态"字段**
|
||||||
|
|
||||||
|
```
|
||||||
|
字典类型: dpc_relative_relationship
|
||||||
|
字典数据: 配偶、父亲、母亲、子女、兄弟姐妹、其他
|
||||||
|
|
||||||
|
字典类型: dpc_employee_status
|
||||||
|
字典数据: 在职(0)、离职(1)
|
||||||
|
```
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
- 符合若依框架设计模式
|
||||||
|
- 便于后续扩展关系类型
|
||||||
|
- 统一管理枚举值
|
||||||
|
|
||||||
|
### 5. 数据库约束
|
||||||
|
|
||||||
|
**决策:柜员号和身份证号添加唯一约束**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
UNIQUE KEY `uk_teller_no` (`teller_no`),
|
||||||
|
UNIQUE KEY `uk_id_card` (`id_card`)
|
||||||
|
```
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
- 柜员号是员工的唯一标识,不允许重复
|
||||||
|
- 身份证号具有唯一性,不允许重复
|
||||||
|
- 防止数据重复和业务逻辑错误
|
||||||
|
|
||||||
|
### 6. 命名规范
|
||||||
|
|
||||||
|
**决策:遵循项目规范**
|
||||||
|
|
||||||
|
- 表名: `dpc_employee`, `dpc_employee_relative`
|
||||||
|
- 实体类: `DpcEmployee`, `DpcEmployeeRelative`
|
||||||
|
- Controller: `DpcEmployeeController`
|
||||||
|
- 权限标识: `dpc:employee:*`
|
||||||
|
- **所有实体类、DTO、VO 类统一使用 @Data 注解**
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
- 与现有 `dpc_intermediary_blacklist` 模块保持一致
|
||||||
|
- 符合项目编码规范
|
||||||
|
- @Data 注解自动生成 getter/setter,代码简洁
|
||||||
|
|
||||||
|
### 7. MyBatis Plus 使用策略
|
||||||
|
|
||||||
|
**决策:区分简单 CRUD 和复杂查询的实现方式**
|
||||||
|
|
||||||
|
**简单 CRUD 操作(使用 MyBatis Plus 方法):**
|
||||||
|
- BaseMapper 提供的方法:`insert`, `deleteById`, `deleteByIds`, `updateById`, `selectById`, `selectList`, `selectCount`
|
||||||
|
- 条件构造器:`QueryWrapper`, `LambdaQueryWrapper` 用于简单条件查询
|
||||||
|
- 适用场景:单表增删改查、简单条件筛选
|
||||||
|
|
||||||
|
**复杂查询操作(使用 XML 映射):**
|
||||||
|
- 多表关联查询(员工 + 亲属信息)
|
||||||
|
- 复杂条件组合查询
|
||||||
|
- 需要自定义结果映射的查询
|
||||||
|
- 适用场景:关联查询、复杂业务查询
|
||||||
|
|
||||||
|
**示例:**
|
||||||
|
```java
|
||||||
|
// 简单查询 - 使用 MyBatis Plus
|
||||||
|
employeeMapper.selectById(id);
|
||||||
|
employeeMapper.selectList(new LambdaQueryWrapper<DpcEmployee>()
|
||||||
|
.eq(DpcEmployee::getTellerNo, tellerNo));
|
||||||
|
|
||||||
|
// 复杂查询 - 使用 XML
|
||||||
|
EmployeeWithRelativesVO selectEmployeeWithRelatives(Long id);
|
||||||
|
```
|
||||||
|
|
||||||
|
**理由:**
|
||||||
|
- 简单 CRUD 使用 MyBatis Plus 减少代码量,提高开发效率
|
||||||
|
- 复杂查询使用 XML 保持 SQL 可读性和可维护性
|
||||||
|
- 符合项目技术规范(MyBatis Plus 3.5.10,Spring Boot 3 适配版)
|
||||||
|
|
||||||
|
## Risks / Trade-offs
|
||||||
|
|
||||||
|
### 风险1: Excel 导入时亲属数据关联失败
|
||||||
|
**风险描述**: 导入员工和亲属信息时,若员工身份证号填写错误,亲属信息无法关联。
|
||||||
|
|
||||||
|
**缓解措施**:
|
||||||
|
- 导入时进行数据校验
|
||||||
|
- 提供详细的导入错误报告
|
||||||
|
- 支持"仅导入员工"模式,亲属信息可后续补录
|
||||||
|
|
||||||
|
### 风险2: 亲属信息数据量过大
|
||||||
|
**风险描述**: 某些员工可能有大量亲属记录,影响查询性能。
|
||||||
|
|
||||||
|
**缓解措施**:
|
||||||
|
- 建立适当的数据库索引
|
||||||
|
- 列表查询时默认不返回亲属详情
|
||||||
|
- 提供单独的亲属查询接口
|
||||||
|
|
||||||
|
### 权衡: 简化 vs 完整性
|
||||||
|
- **当前方案**: 使用两表设计,支持完整的一对多关系
|
||||||
|
- **简化方案**: 将亲属信息存储为 JSON
|
||||||
|
- **选择**: 当前方案,因为纪检场景可能需要按亲属信息查询
|
||||||
|
|
||||||
|
## Migration Plan
|
||||||
|
|
||||||
|
### 后端部署步骤
|
||||||
|
1. 执行数据库脚本,创建表和字典数据
|
||||||
|
2. 部署后端代码
|
||||||
|
3. (前端开发阶段)配置菜单和权限数据
|
||||||
|
|
||||||
|
### 回滚计划
|
||||||
|
1. 从数据库中删除新增的表和字典数据
|
||||||
|
2. 移除后端代码部署
|
||||||
|
|
||||||
|
### 前端开发注意事项
|
||||||
|
- 开发前端页面时需同步在 `sys_menu` 表中插入菜单及权限数据
|
||||||
|
- 菜单路径:信息维护 → 员工信息管理
|
||||||
|
- 权限标识:`dpc:employee:list`, `dpc:employee:query`, `dpc:employee:add`, `dpc:employee:edit`, `dpc:employee:remove`, `dpc:employee:export`, `dpc:employee:import`
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
1. **亲属关系是否需要支持多层级?** (如:亲属的亲属)
|
||||||
|
- **当前决策**: 否,仅支持员工的直接亲属
|
||||||
|
|
||||||
|
2. **导入时是否需要支持"更新已有员工"模式?**
|
||||||
|
- **当前决策**: 是,通过柜员号或身份证号判断
|
||||||
|
|
||||||
|
3. ~~**员工离职后如何处理?**~~
|
||||||
|
- **已确认**: 增加状态字段(0=在职, 1=离职)
|
||||||
|
|
||||||
|
4. ~~**身份证号和柜员号是否需要唯一性约束?**~~
|
||||||
|
- **已确认**: 两者都需要唯一约束
|
||||||
81
openspec/changes/add-employee-info/proposal.md
Normal file
81
openspec/changes/add-employee-info/proposal.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Change: 添加员工信息维护功能
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
当前系统缺少员工信息管理能力,员工基础信息(姓名、柜员号、所属机构、身份证、电话、入职时间)以及员工亲属关系信息需要通过系统进行维护。这导致:
|
||||||
|
1. 员工信息分散管理,缺乏统一的数据源
|
||||||
|
2. 亲属关系信息无法与员工信息关联管理
|
||||||
|
3. 数据录入效率低,无法批量导入导出
|
||||||
|
|
||||||
|
为解决上述问题,需要添加员工信息维护功能,支持员工及其亲属信息的增删改查、批量导入导出。
|
||||||
|
|
||||||
|
## What Changes
|
||||||
|
|
||||||
|
### 新增功能
|
||||||
|
- 员工信息管理:支持员工的新增、编辑、删除、查询
|
||||||
|
- 员工信息导入导出:支持 Excel 批量导入导出员工数据
|
||||||
|
- 员工亲属管理:支持在员工信息中维护亲属关系
|
||||||
|
|
||||||
|
### 数据模型
|
||||||
|
**员工主表 (dpc_employee)**
|
||||||
|
- 员工ID (主键)
|
||||||
|
- 姓名
|
||||||
|
- 柜员号 (唯一约束)
|
||||||
|
- 所属机构号
|
||||||
|
- 身份证号 (唯一约束)
|
||||||
|
- 电话
|
||||||
|
- 入职时间
|
||||||
|
- 状态 (0=在职, 1=离职)
|
||||||
|
- 审计字段(创建者、创建时间、更新者、更新时间)
|
||||||
|
|
||||||
|
**员工亲属表 (dpc_employee_relative)**
|
||||||
|
- 亲属ID (主键)
|
||||||
|
- 员工ID (外键)
|
||||||
|
- 亲属姓名
|
||||||
|
- 亲属身份证号
|
||||||
|
- 亲属手机号
|
||||||
|
- 与员工关系
|
||||||
|
- 审计字段
|
||||||
|
|
||||||
|
### API 接口
|
||||||
|
- `POST /dpc/employee` - 新增员工
|
||||||
|
- `PUT /dpc/employee` - 编辑员工
|
||||||
|
- `DELETE /dpc/employee/{ids}` - 删除员工
|
||||||
|
- `GET /dpc/employee/list` - 查询员工列表
|
||||||
|
- `GET /dpc/employee/{id}` - 获取员工详情
|
||||||
|
- `POST /dpc/employee/export` - 导出员工信息
|
||||||
|
- `POST /dpc/employee/importTemplate` - 下载导入模板
|
||||||
|
- `POST /dpc/employee/importData` - 导入员工信息
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
### 影响的规范 (Affected Specs)
|
||||||
|
- 新增规范: `employee-info` (员工信息管理)
|
||||||
|
|
||||||
|
### 影响的代码 (Affected Code)
|
||||||
|
- **新增模块**: `ruoyi-dpc` (DPC业务模块)
|
||||||
|
- `com.ruoyi.dpc.domain.DpcEmployee` - 员工实体(使用 @Data 注解)
|
||||||
|
- `com.ruoyi.dpc.domain.DpcEmployeeRelative` - 亲属实体(使用 @Data 注解)
|
||||||
|
- `com.ruoyi.dpc.domain.dto.*` - DTO类(统一使用 @Data 注解)
|
||||||
|
- `com.ruoyi.dpc.domain.vo.*` - VO类(统一使用 @Data 注解)
|
||||||
|
- `com.ruoyi.dpc.controller.DpcEmployeeController` - 控制器
|
||||||
|
- `com.ruoyi.dpc.service.*` - 服务层
|
||||||
|
- `com.ruoyi.dpc.mapper.*` - 数据访问层(继承 MyBatis Plus BaseMapper)
|
||||||
|
- **技术实现**:
|
||||||
|
- 简单 CRUD 操作使用 MyBatis Plus BaseMapper 方法
|
||||||
|
- 复杂查询(如多表关联)使用 XML 映射文件
|
||||||
|
|
||||||
|
- **数据库**: 新增表
|
||||||
|
- `dpc_employee` - 员工信息表
|
||||||
|
- `dpc_employee_relative` - 员工亲属表
|
||||||
|
- `sys_dict_type` - 字典类型表(员工状态、亲属关系)
|
||||||
|
|
||||||
|
- **菜单系统**: (将在前端开发阶段处理)
|
||||||
|
- 本次不涉及菜单数据的创建
|
||||||
|
- 前端开发时需在 `sys_menu` 表中插入菜单及权限数据
|
||||||
|
|
||||||
|
- **前端**: (本次提案仅涉及后端接口)
|
||||||
|
- 前端页面开发时需要同步配置菜单数据
|
||||||
|
|
||||||
|
### 非破坏性变更 (Non-Breaking)
|
||||||
|
此变更为纯新增功能,不影响现有系统行为。
|
||||||
526
openspec/changes/add-employee-info/specs/employee-info/spec.md
Normal file
526
openspec/changes/add-employee-info/specs/employee-info/spec.md
Normal file
@@ -0,0 +1,526 @@
|
|||||||
|
# Spec: 员工信息管理
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL支持查询员工信息列表
|
||||||
|
|
||||||
|
系统MUST提供查询功能,允许用户查询系统中已维护的员工信息列表,支持分页、模糊搜索和多条件筛选。
|
||||||
|
|
||||||
|
#### Scenario: 分页查询员工列表
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**When** 用户访问员工信息管理页面
|
||||||
|
**Then** 系统应显示员工列表,支持分页展示
|
||||||
|
|
||||||
|
#### Scenario: 按姓名模糊搜索员工
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在姓名为"张三"的员工
|
||||||
|
**When** 用户在搜索框输入"张"并点击搜索
|
||||||
|
**Then** 系统应返回所有姓名中包含"张"的员工记录
|
||||||
|
|
||||||
|
#### Scenario: 按柜员号精确搜索员工
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在柜员号为"001"的员工
|
||||||
|
**When** 用户在搜索框输入"001"并点击搜索
|
||||||
|
**Then** 系统应返回该柜员号对应的员工记录
|
||||||
|
|
||||||
|
#### Scenario: 按所属机构号筛选员工
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在所属机构号为"1001"的员工
|
||||||
|
**When** 用户选择所属机构号为"1001"并点击搜索
|
||||||
|
**Then** 系统应返回该机构下的所有员工记录
|
||||||
|
|
||||||
|
#### Scenario: 按身份证号精确搜索员工
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在身份证号为"110101199001011234"的员工
|
||||||
|
**When** 用户在搜索框输入"110101199001011234"并点击搜索
|
||||||
|
**Then** 系统应返回该身份证号对应的员工记录
|
||||||
|
|
||||||
|
#### Scenario: 组合条件查询员工
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**When** 用户同时输入姓名"张"、柜员号"001"、所属机构号"1001"并点击搜索
|
||||||
|
**Then** 系统应返回同时满足所有条件的记录
|
||||||
|
|
||||||
|
#### Scenario: 按状态筛选员工
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在在职和离职两种状态的员工记录
|
||||||
|
**When** 用户选择状态为"在职"并点击搜索
|
||||||
|
**Then** 系统应仅返回状态为"在职"的记录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL支持新增员工信息
|
||||||
|
|
||||||
|
系统MUST提供新增功能,允许用户添加员工信息,并可同时维护员工亲属信息。
|
||||||
|
|
||||||
|
#### Scenario: 新增员工基本信息
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**And** 填写姓名为"张三"
|
||||||
|
**And** 填写柜员号为"001"
|
||||||
|
**And** 填写所属机构号为"1001"
|
||||||
|
**And** 填写身份证号为"110101199001011234"
|
||||||
|
**And** 填写电话为"13800138000"
|
||||||
|
**And** 填写入职时间为"2020-01-01"
|
||||||
|
**And** 选择状态为"在职"
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应保存员工信息并提示"操作成功"
|
||||||
|
**And** 列表中应显示新增的记录
|
||||||
|
|
||||||
|
#### Scenario: 新增员工时同时添加亲属信息
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**And** 填写员工基本信息
|
||||||
|
**And** 在亲属区域添加亲属:姓名"李四"、身份证"110101199001011235"、电话"13800138001"、关系"配偶"
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应保存员工信息及亲属信息
|
||||||
|
**And** 查询该员工详情时应显示关联的亲属信息
|
||||||
|
|
||||||
|
#### Scenario: 新增员工时同时添加多个亲属
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**And** 填写员工基本信息
|
||||||
|
**And** 添加3个亲属信息
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应保存员工信息及所有亲属信息
|
||||||
|
**And** 查询该员工详情时应显示3个亲属
|
||||||
|
|
||||||
|
#### Scenario: 新增时姓名为空应校验失败
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**And** 不填写姓名
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应提示"姓名不能为空"
|
||||||
|
**And** 不保存数据
|
||||||
|
|
||||||
|
#### Scenario: 新增时柜员号为空应校验失败
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**And** 不填写柜员号
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应提示"柜员号不能为空"
|
||||||
|
**And** 不保存数据
|
||||||
|
|
||||||
|
#### Scenario: 新增时身份证号格式校验
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**And** 填写身份证号为"123"(不符合身份证格式)
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应提示"身份证号格式不正确"
|
||||||
|
**And** 不保存数据
|
||||||
|
|
||||||
|
#### Scenario: 新增时电话格式校验
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**And** 填写电话为"abc"(不符合手机号格式)
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应提示"电话格式不正确"
|
||||||
|
**And** 不保存数据
|
||||||
|
|
||||||
|
#### Scenario: 新增时柜员号重复应校验失败
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**And** 系统中已存在柜员号为"001"的员工
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**And** 填写柜员号为"001"
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应提示"该柜员号已存在"
|
||||||
|
**And** 不保存数据
|
||||||
|
|
||||||
|
#### Scenario: 新增时身份证号重复应校验失败
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**And** 系统中已存在身份证号为"110101199001011234"的员工
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**And** 填写身份证号为"110101199001011234"
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应提示"该身份证号已存在"
|
||||||
|
**And** 不保存数据
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL支持编辑员工信息
|
||||||
|
|
||||||
|
系统MUST提供编辑功能,允许用户修改已存在的员工信息及其亲属信息。
|
||||||
|
|
||||||
|
#### Scenario: 编辑员工基本信息
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:edit` 权限
|
||||||
|
**And** 系统中存在一条员工记录
|
||||||
|
**When** 用户点击该记录的"编辑"按钮
|
||||||
|
**And** 修改姓名为"李四"
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应更新员工信息并提示"操作成功"
|
||||||
|
**And** 列表中应显示更新后的姓名
|
||||||
|
|
||||||
|
#### Scenario: 编辑员工时新增亲属
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:edit` 权限
|
||||||
|
**And** 系统中存在一条员工记录,该员工有1个亲属
|
||||||
|
**When** 用户点击该记录的"编辑"按钮
|
||||||
|
**And** 在亲属区域添加新的亲属信息
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应保存新的亲属信息
|
||||||
|
**And** 查询该员工详情时应显示2个亲属
|
||||||
|
|
||||||
|
#### Scenario: 编辑员工时修改亲属信息
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:edit` 权限
|
||||||
|
**And** 系统中存在一条员工记录,该员工有亲属"李四"
|
||||||
|
**When** 用户点击该记录的"编辑"按钮
|
||||||
|
**And** 修改亲属姓名为"王五"
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应更新亲属信息
|
||||||
|
**And** 查询该员工详情时应显示更新后的亲属姓名
|
||||||
|
|
||||||
|
#### Scenario: 编辑员工时删除亲属
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:edit` 权限
|
||||||
|
**And** 系统中存在一条员工记录,该员工有2个亲属
|
||||||
|
**When** 用户点击该记录的"编辑"按钮
|
||||||
|
**And** 删除其中一个亲属
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应删除该亲属信息
|
||||||
|
**And** 查询该员工详情时应仅显示1个亲属
|
||||||
|
|
||||||
|
#### Scenario: 编辑时清除所有亲属
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:edit` 权限
|
||||||
|
**And** 系统中存在一条员工记录,该员工有亲属信息
|
||||||
|
**When** 用户点击该记录的"编辑"按钮
|
||||||
|
**And** 删除所有亲属
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应删除所有亲属信息
|
||||||
|
**And** 查询该员工详情时亲属列表应为空
|
||||||
|
|
||||||
|
#### Scenario: 编辑时姓名为空应校验失败
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:edit` 权限
|
||||||
|
**And** 系统中存在一条员工记录
|
||||||
|
**When** 用户点击该记录的"编辑"按钮
|
||||||
|
**And** 清空姓名字段
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应提示"姓名不能为空"
|
||||||
|
**And** 不更新数据
|
||||||
|
|
||||||
|
#### Scenario: 编辑时亲属信息校验
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:edit` 权限
|
||||||
|
**And** 系统中存在一条员工记录
|
||||||
|
**When** 用户点击该记录的"编辑"按钮
|
||||||
|
**And** 添加亲属信息但不填写亲属姓名
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应提示"亲属姓名不能为空"
|
||||||
|
**And** 不更新数据
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL支持删除员工信息
|
||||||
|
|
||||||
|
系统MUST提供删除功能,允许用户删除不再需要的员工记录,删除员工时应级联删除其亲属信息。
|
||||||
|
|
||||||
|
#### Scenario: 删除单条员工记录
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:remove` 权限
|
||||||
|
**And** 系统中存在一条员工记录
|
||||||
|
**When** 用户点击该记录的"删除"按钮
|
||||||
|
**And** 确认删除操作
|
||||||
|
**Then** 系统应删除该员工记录
|
||||||
|
**And** 系统应同时删除该员工的所有亲属信息
|
||||||
|
**And** 列表中不再显示该记录
|
||||||
|
|
||||||
|
#### Scenario: 批量删除员工记录
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:remove` 权限
|
||||||
|
**And** 系统中存在多条员工记录
|
||||||
|
**When** 用户勾选3条记录
|
||||||
|
**And** 点击"删除"按钮
|
||||||
|
**And** 确认删除操作
|
||||||
|
**Then** 系统应删除选中的3条员工记录
|
||||||
|
**And** 系统应同时删除这3个员工的所有亲属信息
|
||||||
|
**And** 列表中不再显示这3条记录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL支持查看员工详情
|
||||||
|
|
||||||
|
系统MUST提供详情查看功能,允许用户查看员工的完整信息及亲属列表。
|
||||||
|
|
||||||
|
#### Scenario: 查看员工基本信息
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:query` 权限
|
||||||
|
**And** 系统中存在一条员工记录
|
||||||
|
**When** 用户点击该记录的"查看"按钮
|
||||||
|
**Then** 系统应显示员工的完整基本信息
|
||||||
|
**And** 信息应包括:姓名、柜员号、所属机构号、身份证号、电话、入职时间
|
||||||
|
|
||||||
|
#### Scenario: 查看员工亲属信息
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:query` 权限
|
||||||
|
**And** 系统中存在一条员工记录,该员工有2个亲属
|
||||||
|
**When** 用户点击该记录的"查看"按钮
|
||||||
|
**Then** 系统应显示员工的基本信息
|
||||||
|
**And** 系统应显示该员工的亲属列表
|
||||||
|
**And** 亲属信息应包括:亲属姓名、亲属身份证号、亲属手机号、与员工关系
|
||||||
|
|
||||||
|
#### Scenario: 查看无亲属的员工
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:query` 权限
|
||||||
|
**And** 系统中存在一条员工记录,该员工无亲属信息
|
||||||
|
**When** 用户点击该记录的"查看"按钮
|
||||||
|
**Then** 系统应显示员工的基本信息
|
||||||
|
**And** 亲属列表应显示为空或提示"暂无亲属信息"
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL支持导出员工信息
|
||||||
|
|
||||||
|
系统MUST提供导出功能,允许用户将查询结果导出为 Excel 文件。
|
||||||
|
|
||||||
|
#### Scenario: 导出所有员工数据
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:export` 权限
|
||||||
|
**And** 系统中存在100条员工记录
|
||||||
|
**When** 用户点击"导出"按钮
|
||||||
|
**And** 不设置任何筛选条件
|
||||||
|
**Then** 系统应生成包含100条记录的 Excel 文件并下载
|
||||||
|
|
||||||
|
#### Scenario: 导出筛选后的员工数据
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:export` 权限
|
||||||
|
**And** 系统中存在多个机构的员工记录
|
||||||
|
**When** 用户筛选所属机构号为"1001"
|
||||||
|
**And** 点击"导出"按钮
|
||||||
|
**Then** 系统应生成仅包含该机构员工记录的 Excel 文件并下载
|
||||||
|
|
||||||
|
#### Scenario: 导出的 Excel 文件格式正确
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:export` 权限
|
||||||
|
**When** 用户点击"导出"按钮
|
||||||
|
**Then** Excel 文件应包含以下列:姓名、柜员号、所属机构号、身份证号、电话、入职时间
|
||||||
|
**And** 表头应使用中文显示
|
||||||
|
**And** 数据应正确显示
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL支持下载 Excel 导入模板
|
||||||
|
|
||||||
|
系统MUST提供模板下载功能,允许用户下载标准的 Excel 导入模板。
|
||||||
|
|
||||||
|
#### Scenario: 下载员工信息导入模板
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**When** 用户点击"下载模板"按钮
|
||||||
|
**Then** 系统应生成 Excel 模板文件并下载
|
||||||
|
**And** 模板应包含"员工信息" Sheet
|
||||||
|
**And** 模板应包含"亲属信息" Sheet(可选)
|
||||||
|
**And** 员工信息 Sheet 应包含以下列:姓名、柜员号、所属机构号、身份证号、电话、入职时间
|
||||||
|
**And** 亲属信息 Sheet 应包含以下列:员工身份证号、亲属姓名、亲属身份证号、亲属手机号、与员工关系
|
||||||
|
|
||||||
|
#### Scenario: 模板中的示例数据正确
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**When** 用户下载导入模板
|
||||||
|
**Then** 模板应包含至少一行示例数据
|
||||||
|
**And** 示例数据应展示正确的填写格式
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL支持通过 Excel 批量导入员工信息
|
||||||
|
|
||||||
|
系统MUST提供批量导入功能,允许用户通过 Excel 文件批量导入员工信息,支持同时导入员工和亲属数据。
|
||||||
|
|
||||||
|
#### Scenario: 导入包含有效员工数据的 Excel 文件
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**And** 用户已准备好包含10条有效员工数据的 Excel 文件
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**And** 选择准备好的 Excel 文件
|
||||||
|
**And** 点击"确定"
|
||||||
|
**Then** 系统应导入10条记录并提示"成功导入10条数据"
|
||||||
|
**And** 列表中应显示这10条新增记录
|
||||||
|
|
||||||
|
#### Scenario: 导入时同时导入员工和亲属信息
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**And** 用户准备的 Excel 文件包含5条员工数据
|
||||||
|
**And** 亲属信息 Sheet 包含这5个员工的亲属信息
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**And** 选择 Excel 文件
|
||||||
|
**And** 点击"确定"
|
||||||
|
**Then** 系统应导入5条员工记录及其关联的亲属信息
|
||||||
|
**And** 提示导入成功
|
||||||
|
|
||||||
|
#### Scenario: 导入时部分数据格式错误
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**And** 用户准备的 Excel 文件包含10条数据
|
||||||
|
**And** 其中2条数据的姓名字段为空
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**And** 选择 Excel 文件
|
||||||
|
**And** 点击"确定"
|
||||||
|
**Then** 系统应仅导入8条有效数据
|
||||||
|
**And** 提示"成功导入8条数据,失败2条数据"
|
||||||
|
**And** 显示失败行的错误详情
|
||||||
|
|
||||||
|
#### Scenario: 导入时柜员号重复
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**And** 系统中已存在柜员号为"001"的员工
|
||||||
|
**And** 用户准备的 Excel 文件包含柜员号为"001"的记录
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**And** 选择 Excel 文件
|
||||||
|
**And** 不勾选"更新已存在数据"
|
||||||
|
**And** 点击"确定"
|
||||||
|
**Then** 系统应提示该行数据"柜员号已存在"
|
||||||
|
**And** 该行数据不被导入
|
||||||
|
|
||||||
|
#### Scenario: 导入时选择更新支持模式
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**And** 系统中已存在柜员号为"001"的员工
|
||||||
|
**And** 用户准备的 Excel 文件包含相同柜员号的记录但姓名不同
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**And** 选择 Excel 文件
|
||||||
|
**And** 勾选"更新已存在数据"选项
|
||||||
|
**And** 点击"确定"
|
||||||
|
**Then** 系统应更新该柜员号对应的记录
|
||||||
|
**And** 提示包含更新成功的消息
|
||||||
|
|
||||||
|
#### Scenario: 导入时亲属信息关联失败
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**And** 用户准备的 Excel 文件包含亲属信息
|
||||||
|
**And** 亲属信息 Sheet 中某条记录的员工身份证号在员工信息 Sheet 中不存在
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**And** 选择 Excel 文件
|
||||||
|
**And** 点击"确定"
|
||||||
|
**Then** 系统应提示该亲属记录"员工身份证号不存在"
|
||||||
|
**And** 该亲属记录不被导入
|
||||||
|
|
||||||
|
#### Scenario: 导入时身份证号格式校验
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**And** 用户准备的 Excel 文件中某条记录的身份证号格式不正确
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**And** 选择 Excel 文件
|
||||||
|
**And** 点击"确定"
|
||||||
|
**Then** 系统应提示该行数据"身份证号格式不正确"
|
||||||
|
**And** 该行数据不被导入
|
||||||
|
|
||||||
|
#### Scenario: 导入时电话格式校验
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**And** 用户准备的 Excel 文件中某条记录的电话格式不正确
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**And** 选择 Excel 文件
|
||||||
|
**And** 点击"确定"
|
||||||
|
**Then** 系统应提示该行数据"电话格式不正确"
|
||||||
|
**And** 该行数据不被导入
|
||||||
|
|
||||||
|
#### Scenario: 导入时仅导入员工信息(无亲属 Sheet)
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**And** 用户准备的 Excel 文件仅包含员工信息 Sheet
|
||||||
|
**And** 不包含亲属信息 Sheet
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**And** 选择 Excel 文件
|
||||||
|
**And** 点击"确定"
|
||||||
|
**Then** 系统应正常导入员工信息
|
||||||
|
**And** 提示导入成功
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL验证用户权限
|
||||||
|
|
||||||
|
系统MUST根据用户的角色和权限控制其对员工信息功能的访问。
|
||||||
|
|
||||||
|
#### Scenario: 无权限用户访问列表应被拒绝
|
||||||
|
|
||||||
|
**Given** 用户已登录系统
|
||||||
|
**And** 该用户不具有 `dpc:employee:list` 权限
|
||||||
|
**When** 用户尝试访问员工信息管理页面
|
||||||
|
**Then** 系统应提示"您没有权限执行此操作"
|
||||||
|
**And** 不显示列表数据
|
||||||
|
|
||||||
|
#### Scenario: 无权限用户尝试新增应被拒绝
|
||||||
|
|
||||||
|
**Given** 用户已登录系统
|
||||||
|
**And** 该用户不具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户尝试点击"新增"按钮
|
||||||
|
**Then** 系统应隐藏或禁用"新增"按钮
|
||||||
|
|
||||||
|
#### Scenario: 无权限用户尝试编辑应被拒绝
|
||||||
|
|
||||||
|
**Given** 用户已登录系统
|
||||||
|
**And** 该用户不具有 `dpc:employee:edit` 权限
|
||||||
|
**When** 用户尝试点击"编辑"按钮
|
||||||
|
**Then** 系统应隐藏或禁用"编辑"按钮
|
||||||
|
|
||||||
|
#### Scenario: 无权限用户尝试删除应被拒绝
|
||||||
|
|
||||||
|
**Given** 用户已登录系统
|
||||||
|
**And** 该用户不具有 `dpc:employee:remove` 权限
|
||||||
|
**When** 用户尝试点击"删除"按钮
|
||||||
|
**Then** 系统应隐藏或禁用"删除"按钮
|
||||||
|
|
||||||
|
#### Scenario: 无权限用户尝试导出应被拒绝
|
||||||
|
|
||||||
|
**Given** 用户已登录系统
|
||||||
|
**And** 该用户不具有 `dpc:employee:export` 权限
|
||||||
|
**When** 用户尝试点击"导出"按钮
|
||||||
|
**Then** 系统应隐藏或禁用"导出"按钮
|
||||||
|
|
||||||
|
#### Scenario: 无权限用户尝试导入应被拒绝
|
||||||
|
|
||||||
|
**Given** 用户已登录系统
|
||||||
|
**And** 该用户不具有 `dpc:employee:import` 权限
|
||||||
|
**When** 用户尝试点击"导入"按钮
|
||||||
|
**Then** 系统应隐藏或禁用"导入"按钮
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 系统SHALL记录操作日志
|
||||||
|
|
||||||
|
系统MUST记录用户对员工信息的关键操作,包括新增、修改、删除、导入等操作。
|
||||||
|
|
||||||
|
#### Scenario: 新增操作应记录日志
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户成功新增一条员工记录
|
||||||
|
**Then** 系统应在操作日志中记录
|
||||||
|
**And** 日志应包含:操作人、操作时间、操作类型、操作内容
|
||||||
|
|
||||||
|
#### Scenario: 修改操作应记录日志
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:edit` 权限
|
||||||
|
**When** 用户成功修改一条员工记录
|
||||||
|
**Then** 系统应在操作日志中记录
|
||||||
|
**And** 日志应包含:操作人、操作时间、操作类型、修改内容
|
||||||
|
|
||||||
|
#### Scenario: 删除操作应记录日志
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:remove` 权限
|
||||||
|
**When** 用户成功删除一条员工记录
|
||||||
|
**Then** 系统应在操作日志中记录
|
||||||
|
**And** 日志应包含:操作人、操作时间、操作类型、删除的记录ID
|
||||||
|
|
||||||
|
#### Scenario: 导入操作应记录日志
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**When** 用户成功导入员工数据
|
||||||
|
**Then** 系统应在操作日志中记录
|
||||||
|
**And** 日志应包含:操作人、操作时间、操作类型、导入数量
|
||||||
99
openspec/changes/add-employee-info/tasks.md
Normal file
99
openspec/changes/add-employee-info/tasks.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
## 1. 数据库设计与实现
|
||||||
|
|
||||||
|
- [x] 1.1 创建 `dpc_employee` 员工信息表,包含字段:employee_id, name, teller_no, org_no, id_card, phone, hire_date, status, create_by, create_time, update_by, update_time
|
||||||
|
- [x] 1.2 为 `dpc_employee` 表创建唯一约束:uk_teller_no (柜员号唯一), uk_id_card (身份证号唯一)
|
||||||
|
- [x] 1.3 为 `dpc_employee` 表创建索引:idx_org_no, idx_status
|
||||||
|
- [x] 1.4 创建 `dpc_employee_relative` 员工亲属表,包含字段:relative_id, employee_id, relative_name, relative_id_card, relative_phone, relationship, create_by, create_time, update_by, update_time
|
||||||
|
- [x] 1.5 为 `dpc_employee_relative` 表创建索引:idx_employee_id, idx_relative_id_card
|
||||||
|
- [x] 1.6 添加外键约束:dpc_employee_relative.employee_id → dpc_employee.employee_id(可选,根据项目规范决定)
|
||||||
|
- [x] 1.7 创建 `dpc_relative_relationship` 字典类型及字典数据(配偶、父亲、母亲、子女、兄弟姐妹、其他)
|
||||||
|
- [x] 1.8 创建 `dpc_employee_status` 字典类型及字典数据(在职=0、离职=1)
|
||||||
|
|
||||||
|
## 2. 后端实体类创建
|
||||||
|
|
||||||
|
- [x] 2.1 创建 `DpcEmployee.java` 实体类,使用 @Data 注解,包含 @Excel 注解用于导入导出
|
||||||
|
- [x] 2.2 创建 `DpcEmployeeRelative.java` 实体类,使用 @Data 注解
|
||||||
|
- [x] 2.3 创建 `DpcEmployeeAddDTO.java` 新增 DTO,使用 @Data 注解,包含 @Validated 校验注解
|
||||||
|
- [x] 2.4 创建 `DpcEmployeeEditDTO.java` 编辑 DTO,使用 @Data 注解,包含 @Validated 校验注解
|
||||||
|
- [x] 2.5 创建 `DpcEmployeeQueryDTO.java` 查询 DTO,使用 @Data 注解
|
||||||
|
- [x] 2.6 创建 `DpcEmployeeVO.java` 视图对象,使用 @Data 注解,包含亲属列表
|
||||||
|
- [x] 2.7 创建 `DpcEmployeeRelativeVO.java` 亲属视图对象,使用 @Data 注解
|
||||||
|
- [x] 2.8 创建 `DpcEmployeeRelativeAddDTO.java` 亲属新增 DTO,使用 @Data 注解
|
||||||
|
|
||||||
|
## 3. Mapper 层实现
|
||||||
|
|
||||||
|
- [x] 3.1 创建 `DpcEmployeeMapper.java` 接口,继承 `BaseMapper<DpcEmployee>`
|
||||||
|
- [x] 3.2 创建 `DpcEmployeeMapper.xml` MyBatis 映射文件,仅实现复杂查询(如:员工详情包含亲属列表)
|
||||||
|
- [x] 3.3 在 XML 中实现 `selectEmployeeWithRelatives` 方法,关联查询员工及其亲属信息
|
||||||
|
- [x] 3.4 创建 `DpcEmployeeRelativeMapper.java` 接口,继承 `BaseMapper<DpcEmployeeRelative>`
|
||||||
|
- [x] 3.5 创建 `DpcEmployeeRelativeMapper.xml` MyBatis 映射文件(如需复杂查询)
|
||||||
|
- [x] 3.6 简单 CRUD 操作使用 MyBatis Plus 提供的 BaseMapper 方法(insert, deleteById, deleteByIds, updateById, selectById, selectList)
|
||||||
|
- [x] 3.7 简单条件查询使用 LambdaQueryWrapper 或 QueryWrapper(如:按柜员号、身份证号查询)
|
||||||
|
- [x] 3.8 复杂多条件查询在 XML 中编写自定义 SQL
|
||||||
|
|
||||||
|
## 4. Service 层实现
|
||||||
|
|
||||||
|
- [x] 4.1 创建 `IDpcEmployeeService.java` 接口
|
||||||
|
- [x] 4.2 创建 `DpcEmployeeServiceImpl.java` 实现类
|
||||||
|
- [x] 4.3 实现查询员工列表方法 `selectEmployeeList`,支持分页和多条件查询
|
||||||
|
- [x] 4.4 实现查询员工详情方法 `selectEmployeeById`,包含亲属信息
|
||||||
|
- [x] 4.5 实现新增员工方法 `insertEmployee`,支持同时插入亲属信息
|
||||||
|
- [x] 4.6 实现编辑员工方法 `updateEmployee`,支持更新亲属信息(先删除后插入)
|
||||||
|
- [x] 4.7 实现删除员工方法 `deleteEmployeeByIds`,级联删除亲属信息
|
||||||
|
- [x] 4.8 实现导出员工数据方法 `selectEmployeeListForExport`
|
||||||
|
- [x] 4.9 实现导入员工数据方法 `importEmployee`,支持批量导入员工和亲属
|
||||||
|
- [x] 4.10 实现柜员号唯一性校验
|
||||||
|
- [x] 4.11 实现身份证号格式校验
|
||||||
|
- [x] 4.12 实现电话格式校验
|
||||||
|
|
||||||
|
## 5. Controller 层实现
|
||||||
|
|
||||||
|
- [x] 5.1 创建 `DpcEmployeeController.java` 控制器类
|
||||||
|
- [x] 5.2 实现 `GET /dpc/employee/list` 查询列表接口,添加 @PreAuthorize 权限注解
|
||||||
|
- [x] 5.3 实现 `GET /dpc/employee/{id}` 查询详情接口
|
||||||
|
- [x] 5.4 实现 `POST /dpc/employee` 新增接口
|
||||||
|
- [x] 5.5 实现 `PUT /dpc/employee` 编辑接口
|
||||||
|
- [x] 5.6 实现 `DELETE /dpc/employee/{ids}` 删除接口
|
||||||
|
- [x] 5.7 实现 `POST /dpc/employee/export` 导出接口
|
||||||
|
- [x] 5.8 实现 `POST /dpc/employee/importTemplate` 下载模板接口
|
||||||
|
- [x] 5.9 实现 `POST /dpc/employee/importData` 导入接口
|
||||||
|
- [x] 5.10 为所有接口添加 @Log 注解记录操作日志
|
||||||
|
|
||||||
|
## 6. 导入导出功能实现
|
||||||
|
|
||||||
|
- [x] 6.1 配置 `DpcEmployee` 实体的 @Excel 注解,定义导出列
|
||||||
|
- [x] 6.2 配置 `DpcEmployeeRelative` 实体的 @Excel 注解(用于多 Sheet 导入)
|
||||||
|
- [x] 6.3 实现导入模板的 Excel 格式定义
|
||||||
|
- [x] 6.4 实现 Excel 解析逻辑,支持多 Sheet 读取
|
||||||
|
- [x] 6.5 实现员工和亲属数据的关联逻辑(通过身份证号)
|
||||||
|
- [x] 6.6 实现导入数据校验逻辑
|
||||||
|
- [x] 6.7 实现导入错误信息收集和反馈
|
||||||
|
|
||||||
|
## 7. 数据校验
|
||||||
|
|
||||||
|
- [x] 7.1 实现 DTO 层的 @NotBlank、@Size、@Pattern 等校验注解
|
||||||
|
- [x] 7.2 实现身份证号格式校验(18位,符合国标)
|
||||||
|
- [x] 7.3 实现电话号码格式校验(11位手机号)
|
||||||
|
- [x] 7.4 实现柜员号唯一性校验
|
||||||
|
- [x] 7.5 实现入职时间日期格式校验
|
||||||
|
- [x] 7.6 实现亲属信息必填字段校验
|
||||||
|
|
||||||
|
## 8. 测试
|
||||||
|
|
||||||
|
- [x] 8.1 生成可执行的测试脚本
|
||||||
|
- [x] 8.2 测试查询员工列表接口(分页、条件筛选)
|
||||||
|
- [x] 8.3 测试查询员工详情接口(包含亲属信息)
|
||||||
|
- [x] 8.4 测试新增员工接口(包含亲属信息)
|
||||||
|
- [x] 8.5 测试编辑员工接口(修改基本信息、新增/修改/删除亲属)
|
||||||
|
- [x] 8.6 测试删除员工接口(验证级联删除亲属)
|
||||||
|
- [x] 8.7 测试导出功能
|
||||||
|
- [x] 8.8 测试下载模板功能
|
||||||
|
- [x] 8.9 测试导入功能(正常数据、错误数据、更新模式)
|
||||||
|
- [x] 8.10 测试权限控制(无权限访问应被拒绝)
|
||||||
|
- [x] 8.11 测试数据校验(空值、格式错误、重复数据)
|
||||||
|
|
||||||
|
## 9. API 文档生成
|
||||||
|
|
||||||
|
- [x] 9.1 确认所有接口在 Swagger UI 中正确显示
|
||||||
|
- [x] 9.2 在项目文件目录下生成 API 文档(doc/API文档.md)
|
||||||
|
- [x] 9.3 文档包含:接口地址、请求方式、请求参数、响应格式、权限要求
|
||||||
@@ -25,7 +25,7 @@ spring:
|
|||||||
druid:
|
druid:
|
||||||
# 主库数据源
|
# 主库数据源
|
||||||
master:
|
master:
|
||||||
url: jdbc:mysql://116.62.17.81:3306/discipline-prelim-check?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
url: jdbc:mysql://116.62.17.81:3306/discipline-prelim-check?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||||
username: root
|
username: root
|
||||||
password: Kfcx@1234
|
password: Kfcx@1234
|
||||||
# 从库数据源
|
# 从库数据源
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
package com.ruoyi.dpc.controller;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Log;
|
||||||
|
import com.ruoyi.common.core.controller.BaseController;
|
||||||
|
import com.ruoyi.common.core.domain.AjaxResult;
|
||||||
|
import com.ruoyi.common.core.page.TableDataInfo;
|
||||||
|
import com.ruoyi.common.enums.BusinessType;
|
||||||
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||||
|
import com.ruoyi.dpc.domain.DpcEmployee;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeAddDTO;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeEditDTO;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeQueryDTO;
|
||||||
|
import com.ruoyi.dpc.domain.vo.DpcEmployeeVO;
|
||||||
|
import com.ruoyi.dpc.service.IDpcEmployeeService;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工信息Controller
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/dpc/employee")
|
||||||
|
public class DpcEmployeeController extends BaseController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IDpcEmployeeService employeeService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询员工列表
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('dpc:employee:list')")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public TableDataInfo list(DpcEmployeeQueryDTO queryDTO) {
|
||||||
|
startPage();
|
||||||
|
List<DpcEmployeeVO> list = employeeService.selectEmployeeList(queryDTO);
|
||||||
|
return getDataTable(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出员工列表
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('dpc:employee:export')")
|
||||||
|
@Log(title = "员工信息", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/export")
|
||||||
|
public void export(HttpServletResponse response, DpcEmployeeQueryDTO queryDTO) {
|
||||||
|
List<DpcEmployee> list = employeeService.selectEmployeeListForExport(queryDTO);
|
||||||
|
ExcelUtil<DpcEmployee> util = new ExcelUtil<>(DpcEmployee.class);
|
||||||
|
util.exportExcel(response, list, "员工信息数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取员工详细信息
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('dpc:employee:query')")
|
||||||
|
@GetMapping(value = "/{employeeId}")
|
||||||
|
public AjaxResult getInfo(@PathVariable Long employeeId) {
|
||||||
|
return success(employeeService.selectEmployeeById(employeeId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增员工
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('dpc:employee:add')")
|
||||||
|
@Log(title = "员工信息", businessType = BusinessType.INSERT)
|
||||||
|
@PostMapping
|
||||||
|
public AjaxResult add(@Validated @RequestBody DpcEmployeeAddDTO addDTO) {
|
||||||
|
return toAjax(employeeService.insertEmployee(addDTO));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改员工
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('dpc:employee:edit')")
|
||||||
|
@Log(title = "员工信息", businessType = BusinessType.UPDATE)
|
||||||
|
@PutMapping
|
||||||
|
public AjaxResult edit(@Validated @RequestBody DpcEmployeeEditDTO editDTO) {
|
||||||
|
return toAjax(employeeService.updateEmployee(editDTO));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除员工
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('dpc:employee:remove')")
|
||||||
|
@Log(title = "员工信息", businessType = BusinessType.DELETE)
|
||||||
|
@DeleteMapping("/{employeeIds}")
|
||||||
|
public AjaxResult remove(@PathVariable Long[] employeeIds) {
|
||||||
|
return toAjax(employeeService.deleteEmployeeByIds(employeeIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下载导入模板
|
||||||
|
*/
|
||||||
|
@PostMapping("/importTemplate")
|
||||||
|
public void importTemplate(HttpServletResponse response) {
|
||||||
|
ExcelUtil<DpcEmployee> util = new ExcelUtil<>(DpcEmployee.class);
|
||||||
|
util.importTemplateExcel(response, "员工信息数据");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入员工信息
|
||||||
|
*/
|
||||||
|
@PreAuthorize("@ss.hasPermi('dpc:employee:import')")
|
||||||
|
@Log(title = "员工信息", businessType = BusinessType.IMPORT)
|
||||||
|
@PostMapping("/importData")
|
||||||
|
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception {
|
||||||
|
ExcelUtil<DpcEmployee> util = new ExcelUtil<>(DpcEmployee.class);
|
||||||
|
List<DpcEmployee> list = util.importExcel(file.getInputStream());
|
||||||
|
List<DpcEmployeeAddDTO> addDTOList = convertToAddDTOList(list);
|
||||||
|
String message = employeeService.importEmployee(addDTOList, updateSupport);
|
||||||
|
return success(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为AddDTO列表
|
||||||
|
*/
|
||||||
|
private List<DpcEmployeeAddDTO> convertToAddDTOList(List<DpcEmployee> list) {
|
||||||
|
return list.stream().map(entity -> {
|
||||||
|
DpcEmployeeAddDTO dto = new DpcEmployeeAddDTO();
|
||||||
|
dto.setName(entity.getName());
|
||||||
|
dto.setTellerNo(entity.getTellerNo());
|
||||||
|
dto.setOrgNo(entity.getOrgNo());
|
||||||
|
dto.setIdCard(entity.getIdCard());
|
||||||
|
dto.setPhone(entity.getPhone());
|
||||||
|
dto.setHireDate(entity.getHireDate());
|
||||||
|
dto.setStatus(entity.getStatus());
|
||||||
|
return dto;
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package com.ruoyi.dpc.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工信息对象 dpc_employee
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DpcEmployee implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 员工ID */
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long employeeId;
|
||||||
|
|
||||||
|
/** 姓名 */
|
||||||
|
@Excel(name = "姓名")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** 柜员号 */
|
||||||
|
@Excel(name = "柜员号")
|
||||||
|
private String tellerNo;
|
||||||
|
|
||||||
|
/** 所属机构号 */
|
||||||
|
@Excel(name = "所属机构号")
|
||||||
|
private String orgNo;
|
||||||
|
|
||||||
|
/** 身份证号 */
|
||||||
|
@Excel(name = "身份证号")
|
||||||
|
private String idCard;
|
||||||
|
|
||||||
|
/** 电话 */
|
||||||
|
@Excel(name = "电话")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
/** 入职时间 */
|
||||||
|
@Excel(name = "入职时间", dateFormat = "yyyy-MM-dd")
|
||||||
|
private Date hireDate;
|
||||||
|
|
||||||
|
/** 状态 */
|
||||||
|
@Excel(name = "状态", readConverterExp = "0=在职,1=离职")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/** 创建者 */
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
/** 创建时间 */
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/** 更新者 */
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
/** 更新时间 */
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.ruoyi.dpc.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.FieldFill;
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工亲属对象 dpc_employee_relative
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DpcEmployeeRelative implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 亲属ID */
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long relativeId;
|
||||||
|
|
||||||
|
/** 员工ID */
|
||||||
|
private Long employeeId;
|
||||||
|
|
||||||
|
/** 亲属姓名 */
|
||||||
|
@Excel(name = "亲属姓名")
|
||||||
|
private String relativeName;
|
||||||
|
|
||||||
|
/** 亲属身份证号 */
|
||||||
|
@Excel(name = "亲属身份证号")
|
||||||
|
private String relativeIdCard;
|
||||||
|
|
||||||
|
/** 亲属手机号 */
|
||||||
|
@Excel(name = "亲属手机号")
|
||||||
|
private String relativePhone;
|
||||||
|
|
||||||
|
/** 与员工关系 */
|
||||||
|
@Excel(name = "与员工关系")
|
||||||
|
private String relationship;
|
||||||
|
|
||||||
|
/** 创建者 */
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
/** 创建时间 */
|
||||||
|
@TableField(fill = FieldFill.INSERT)
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/** 更新者 */
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
/** 更新时间 */
|
||||||
|
@TableField(fill = FieldFill.INSERT_UPDATE)
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.ruoyi.dpc.domain.dto;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工信息新增 DTO
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DpcEmployeeAddDTO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 姓名 */
|
||||||
|
@Excel(name = "姓名")
|
||||||
|
@NotBlank(message = "姓名不能为空")
|
||||||
|
@Size(max = 100, message = "姓名长度不能超过100个字符")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** 柜员号 */
|
||||||
|
@Excel(name = "柜员号")
|
||||||
|
@NotBlank(message = "柜员号不能为空")
|
||||||
|
@Size(max = 50, message = "柜员号长度不能超过50个字符")
|
||||||
|
private String tellerNo;
|
||||||
|
|
||||||
|
/** 所属机构号 */
|
||||||
|
@Excel(name = "所属机构号")
|
||||||
|
@Size(max = 50, message = "所属机构号长度不能超过50个字符")
|
||||||
|
private String orgNo;
|
||||||
|
|
||||||
|
/** 身份证号 */
|
||||||
|
@Excel(name = "身份证号")
|
||||||
|
@NotBlank(message = "身份证号不能为空")
|
||||||
|
@Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$", message = "身份证号格式不正确")
|
||||||
|
private String idCard;
|
||||||
|
|
||||||
|
/** 电话 */
|
||||||
|
@Excel(name = "电话")
|
||||||
|
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "电话格式不正确")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
/** 入职时间 */
|
||||||
|
@Excel(name = "入职时间", dateFormat = "yyyy-MM-dd")
|
||||||
|
private Date hireDate;
|
||||||
|
|
||||||
|
/** 状态 */
|
||||||
|
@Excel(name = "状态", readConverterExp = "0=在职,1=离职")
|
||||||
|
@NotBlank(message = "状态不能为空")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/** 亲属列表 */
|
||||||
|
private List<DpcEmployeeRelativeAddDTO> relatives;
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package com.ruoyi.dpc.domain.dto;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工信息编辑 DTO
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DpcEmployeeEditDTO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 员工ID */
|
||||||
|
@NotNull(message = "员工ID不能为空")
|
||||||
|
private Long employeeId;
|
||||||
|
|
||||||
|
/** 姓名 */
|
||||||
|
@Excel(name = "姓名")
|
||||||
|
@Size(max = 100, message = "姓名长度不能超过100个字符")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** 柜员号 */
|
||||||
|
@Excel(name = "柜员号")
|
||||||
|
@Size(max = 50, message = "柜员号长度不能超过50个字符")
|
||||||
|
private String tellerNo;
|
||||||
|
|
||||||
|
/** 所属机构号 */
|
||||||
|
@Excel(name = "所属机构号")
|
||||||
|
@Size(max = 50, message = "所属机构号长度不能超过50个字符")
|
||||||
|
private String orgNo;
|
||||||
|
|
||||||
|
/** 身份证号 */
|
||||||
|
@Excel(name = "身份证号")
|
||||||
|
@Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$", message = "身份证号格式不正确")
|
||||||
|
private String idCard;
|
||||||
|
|
||||||
|
/** 电话 */
|
||||||
|
@Excel(name = "电话")
|
||||||
|
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "电话格式不正确")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
/** 入职时间 */
|
||||||
|
@Excel(name = "入职时间", dateFormat = "yyyy-MM-dd")
|
||||||
|
private Date hireDate;
|
||||||
|
|
||||||
|
/** 状态 */
|
||||||
|
@Excel(name = "状态", readConverterExp = "0=在职,1=离职")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/** 亲属列表 */
|
||||||
|
private List<DpcEmployeeRelativeAddDTO> relatives;
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.ruoyi.dpc.domain.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工信息查询 DTO
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DpcEmployeeQueryDTO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 姓名(模糊查询) */
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** 柜员号(精确查询) */
|
||||||
|
private String tellerNo;
|
||||||
|
|
||||||
|
/** 所属机构号 */
|
||||||
|
private String orgNo;
|
||||||
|
|
||||||
|
/** 身份证号(精确查询) */
|
||||||
|
private String idCard;
|
||||||
|
|
||||||
|
/** 状态 */
|
||||||
|
private String status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package com.ruoyi.dpc.domain.dto;
|
||||||
|
|
||||||
|
import com.ruoyi.common.annotation.Excel;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Pattern;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工亲属新增 DTO
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DpcEmployeeRelativeAddDTO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 亲属姓名 */
|
||||||
|
@Excel(name = "亲属姓名")
|
||||||
|
@NotBlank(message = "亲属姓名不能为空")
|
||||||
|
@Size(max = 100, message = "亲属姓名长度不能超过100个字符")
|
||||||
|
private String relativeName;
|
||||||
|
|
||||||
|
/** 亲属身份证号 */
|
||||||
|
@Excel(name = "亲属身份证号")
|
||||||
|
@Pattern(regexp = "^[1-9]\\d{5}(18|19|20)\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])\\d{3}[0-9Xx]$", message = "亲属身份证号格式不正确")
|
||||||
|
private String relativeIdCard;
|
||||||
|
|
||||||
|
/** 亲属手机号 */
|
||||||
|
@Excel(name = "亲属手机号")
|
||||||
|
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "亲属手机号格式不正确")
|
||||||
|
private String relativePhone;
|
||||||
|
|
||||||
|
/** 与员工关系 */
|
||||||
|
@Excel(name = "与员工关系")
|
||||||
|
@NotBlank(message = "与员工关系不能为空")
|
||||||
|
@Size(max = 50, message = "与员工关系长度不能超过50个字符")
|
||||||
|
private String relationship;
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.ruoyi.dpc.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工亲属 VO
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DpcEmployeeRelativeVO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 亲属ID */
|
||||||
|
private Long relativeId;
|
||||||
|
|
||||||
|
/** 员工ID */
|
||||||
|
private Long employeeId;
|
||||||
|
|
||||||
|
/** 亲属姓名 */
|
||||||
|
private String relativeName;
|
||||||
|
|
||||||
|
/** 亲属身份证号 */
|
||||||
|
private String relativeIdCard;
|
||||||
|
|
||||||
|
/** 亲属手机号 */
|
||||||
|
private String relativePhone;
|
||||||
|
|
||||||
|
/** 与员工关系 */
|
||||||
|
private String relationship;
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
package com.ruoyi.dpc.domain.vo;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工信息 VO
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class DpcEmployeeVO implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/** 员工ID */
|
||||||
|
private Long employeeId;
|
||||||
|
|
||||||
|
/** 姓名 */
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/** 柜员号 */
|
||||||
|
private String tellerNo;
|
||||||
|
|
||||||
|
/** 所属机构号 */
|
||||||
|
private String orgNo;
|
||||||
|
|
||||||
|
/** 身份证号 */
|
||||||
|
private String idCard;
|
||||||
|
|
||||||
|
/** 电话 */
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
/** 入职时间 */
|
||||||
|
private Date hireDate;
|
||||||
|
|
||||||
|
/** 状态 */
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
/** 状态描述 */
|
||||||
|
private String statusDesc;
|
||||||
|
|
||||||
|
/** 创建时间 */
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/** 创建者 */
|
||||||
|
private String createBy;
|
||||||
|
|
||||||
|
/** 更新时间 */
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
/** 更新者 */
|
||||||
|
private String updateBy;
|
||||||
|
|
||||||
|
/** 亲属列表 */
|
||||||
|
private List<DpcEmployeeRelativeVO> relatives;
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
package com.ruoyi.dpc.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.ruoyi.dpc.domain.DpcEmployee;
|
||||||
|
import com.ruoyi.dpc.domain.vo.DpcEmployeeVO;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工信息 数据层
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
public interface DpcEmployeeMapper extends BaseMapper<DpcEmployee> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询员工详情(包含亲属列表)
|
||||||
|
*
|
||||||
|
* @param employeeId 员工ID
|
||||||
|
* @return 员工VO
|
||||||
|
*/
|
||||||
|
DpcEmployeeVO selectEmployeeWithRelatives(@Param("employeeId") Long employeeId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.ruoyi.dpc.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.ruoyi.dpc.domain.DpcEmployeeRelative;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工亲属 数据层
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
public interface DpcEmployeeRelativeMapper extends BaseMapper<DpcEmployeeRelative> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
package com.ruoyi.dpc.service;
|
||||||
|
|
||||||
|
import com.ruoyi.dpc.domain.DpcEmployee;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeAddDTO;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeEditDTO;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeQueryDTO;
|
||||||
|
import com.ruoyi.dpc.domain.vo.DpcEmployeeVO;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工信息 服务层
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
public interface IDpcEmployeeService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询员工列表
|
||||||
|
*
|
||||||
|
* @param queryDTO 查询条件
|
||||||
|
* @return 员工VO集合
|
||||||
|
*/
|
||||||
|
List<DpcEmployeeVO> selectEmployeeList(DpcEmployeeQueryDTO queryDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询员工列表(用于导出)
|
||||||
|
*
|
||||||
|
* @param queryDTO 查询条件
|
||||||
|
* @return 员工实体集合
|
||||||
|
*/
|
||||||
|
List<DpcEmployee> selectEmployeeListForExport(DpcEmployeeQueryDTO queryDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询员工详情
|
||||||
|
*
|
||||||
|
* @param employeeId 员工ID
|
||||||
|
* @return 员工VO
|
||||||
|
*/
|
||||||
|
DpcEmployeeVO selectEmployeeById(Long employeeId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增员工
|
||||||
|
*
|
||||||
|
* @param addDTO 新增DTO
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int insertEmployee(DpcEmployeeAddDTO addDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改员工
|
||||||
|
*
|
||||||
|
* @param editDTO 编辑DTO
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int updateEmployee(DpcEmployeeEditDTO editDTO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除员工
|
||||||
|
*
|
||||||
|
* @param employeeIds 需要删除的员工ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
int deleteEmployeeByIds(Long[] employeeIds);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入员工数据
|
||||||
|
*
|
||||||
|
* @param addDTOList 新增DTO列表
|
||||||
|
* @param isUpdateSupport 是否更新支持
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
String importEmployee(List<DpcEmployeeAddDTO> addDTOList, Boolean isUpdateSupport);
|
||||||
|
}
|
||||||
@@ -0,0 +1,291 @@
|
|||||||
|
package com.ruoyi.dpc.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import com.ruoyi.dpc.domain.DpcEmployee;
|
||||||
|
import com.ruoyi.dpc.domain.DpcEmployeeRelative;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeAddDTO;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeEditDTO;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeQueryDTO;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeRelativeAddDTO;
|
||||||
|
import com.ruoyi.dpc.domain.vo.DpcEmployeeVO;
|
||||||
|
import com.ruoyi.dpc.mapper.DpcEmployeeMapper;
|
||||||
|
import com.ruoyi.dpc.mapper.DpcEmployeeRelativeMapper;
|
||||||
|
import com.ruoyi.dpc.service.IDpcEmployeeService;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 员工信息 服务层处理
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-01-28
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class DpcEmployeeServiceImpl implements IDpcEmployeeService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DpcEmployeeMapper employeeMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DpcEmployeeRelativeMapper relativeMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询员工列表
|
||||||
|
*
|
||||||
|
* @param queryDTO 查询条件
|
||||||
|
* @return 员工VO集合
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<DpcEmployeeVO> selectEmployeeList(DpcEmployeeQueryDTO queryDTO) {
|
||||||
|
LambdaQueryWrapper<DpcEmployee> wrapper = buildQueryWrapper(queryDTO);
|
||||||
|
List<DpcEmployee> list = employeeMapper.selectList(wrapper);
|
||||||
|
List<DpcEmployeeVO> voList = new ArrayList<>();
|
||||||
|
for (DpcEmployee employee : list) {
|
||||||
|
voList.add(convertToVO(employee));
|
||||||
|
}
|
||||||
|
return voList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询员工列表(用于导出)
|
||||||
|
*
|
||||||
|
* @param queryDTO 查询条件
|
||||||
|
* @return 员工实体集合
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<DpcEmployee> selectEmployeeListForExport(DpcEmployeeQueryDTO queryDTO) {
|
||||||
|
LambdaQueryWrapper<DpcEmployee> wrapper = buildQueryWrapper(queryDTO);
|
||||||
|
return employeeMapper.selectList(wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询员工详情
|
||||||
|
*
|
||||||
|
* @param employeeId 员工ID
|
||||||
|
* @return 员工VO
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public DpcEmployeeVO selectEmployeeById(Long employeeId) {
|
||||||
|
return employeeMapper.selectEmployeeWithRelatives(employeeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增员工
|
||||||
|
*
|
||||||
|
* @param addDTO 新增DTO
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public int insertEmployee(DpcEmployeeAddDTO addDTO) {
|
||||||
|
// 检查柜员号唯一性
|
||||||
|
LambdaQueryWrapper<DpcEmployee> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(DpcEmployee::getTellerNo, addDTO.getTellerNo());
|
||||||
|
if (employeeMapper.selectCount(wrapper) > 0) {
|
||||||
|
throw new RuntimeException("该柜员号已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查身份证号唯一性
|
||||||
|
wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(DpcEmployee::getIdCard, addDTO.getIdCard());
|
||||||
|
if (employeeMapper.selectCount(wrapper) > 0) {
|
||||||
|
throw new RuntimeException("该身份证号已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
DpcEmployee employee = new DpcEmployee();
|
||||||
|
BeanUtils.copyProperties(addDTO, employee);
|
||||||
|
int result = employeeMapper.insert(employee);
|
||||||
|
|
||||||
|
// 插入亲属信息
|
||||||
|
if (addDTO.getRelatives() != null && !addDTO.getRelatives().isEmpty()) {
|
||||||
|
for (DpcEmployeeRelativeAddDTO relativeAddDTO : addDTO.getRelatives()) {
|
||||||
|
DpcEmployeeRelative relative = new DpcEmployeeRelative();
|
||||||
|
BeanUtils.copyProperties(relativeAddDTO, relative);
|
||||||
|
relative.setEmployeeId(employee.getEmployeeId());
|
||||||
|
relativeMapper.insert(relative);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改员工
|
||||||
|
*
|
||||||
|
* @param editDTO 编辑DTO
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public int updateEmployee(DpcEmployeeEditDTO editDTO) {
|
||||||
|
// 检查柜员号唯一性(排除自己)
|
||||||
|
if (StringUtils.isNotEmpty(editDTO.getTellerNo())) {
|
||||||
|
LambdaQueryWrapper<DpcEmployee> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(DpcEmployee::getTellerNo, editDTO.getTellerNo())
|
||||||
|
.ne(DpcEmployee::getEmployeeId, editDTO.getEmployeeId());
|
||||||
|
if (employeeMapper.selectCount(wrapper) > 0) {
|
||||||
|
throw new RuntimeException("该柜员号已存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查身份证号唯一性(排除自己)
|
||||||
|
if (StringUtils.isNotEmpty(editDTO.getIdCard())) {
|
||||||
|
LambdaQueryWrapper<DpcEmployee> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(DpcEmployee::getIdCard, editDTO.getIdCard())
|
||||||
|
.ne(DpcEmployee::getEmployeeId, editDTO.getEmployeeId());
|
||||||
|
if (employeeMapper.selectCount(wrapper) > 0) {
|
||||||
|
throw new RuntimeException("该身份证号已存在");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DpcEmployee employee = new DpcEmployee();
|
||||||
|
BeanUtils.copyProperties(editDTO, employee);
|
||||||
|
int result = employeeMapper.updateById(employee);
|
||||||
|
|
||||||
|
// 删除原有亲属信息
|
||||||
|
LambdaQueryWrapper<DpcEmployeeRelative> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(DpcEmployeeRelative::getEmployeeId, editDTO.getEmployeeId());
|
||||||
|
relativeMapper.delete(wrapper);
|
||||||
|
|
||||||
|
// 插入新的亲属信息
|
||||||
|
if (editDTO.getRelatives() != null && !editDTO.getRelatives().isEmpty()) {
|
||||||
|
for (DpcEmployeeRelativeAddDTO relativeAddDTO : editDTO.getRelatives()) {
|
||||||
|
DpcEmployeeRelative relative = new DpcEmployeeRelative();
|
||||||
|
BeanUtils.copyProperties(relativeAddDTO, relative);
|
||||||
|
relative.setEmployeeId(editDTO.getEmployeeId());
|
||||||
|
relativeMapper.insert(relative);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除员工
|
||||||
|
*
|
||||||
|
* @param employeeIds 需要删除的员工ID
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public int deleteEmployeeByIds(Long[] employeeIds) {
|
||||||
|
// 级联删除亲属信息
|
||||||
|
for (Long employeeId : employeeIds) {
|
||||||
|
LambdaQueryWrapper<DpcEmployeeRelative> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(DpcEmployeeRelative::getEmployeeId, employeeId);
|
||||||
|
relativeMapper.delete(wrapper);
|
||||||
|
}
|
||||||
|
return employeeMapper.deleteBatchIds(List.of(employeeIds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导入员工数据
|
||||||
|
*
|
||||||
|
* @param addDTOList 新增DTO列表
|
||||||
|
* @param isUpdateSupport 是否更新支持
|
||||||
|
* @return 结果
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public String importEmployee(List<DpcEmployeeAddDTO> addDTOList, Boolean isUpdateSupport) {
|
||||||
|
if (StringUtils.isNull(addDTOList) || addDTOList.isEmpty()) {
|
||||||
|
return "至少需要一条数据";
|
||||||
|
}
|
||||||
|
|
||||||
|
int successNum = 0;
|
||||||
|
int failureNum = 0;
|
||||||
|
StringBuilder successMsg = new StringBuilder();
|
||||||
|
StringBuilder failureMsg = new StringBuilder();
|
||||||
|
|
||||||
|
for (DpcEmployeeAddDTO addDTO : addDTOList) {
|
||||||
|
try {
|
||||||
|
// 验证数据
|
||||||
|
validateEmployeeData(addDTO, isUpdateSupport);
|
||||||
|
|
||||||
|
DpcEmployee employee = new DpcEmployee();
|
||||||
|
BeanUtils.copyProperties(addDTO, employee);
|
||||||
|
|
||||||
|
employeeMapper.insert(employee);
|
||||||
|
successNum++;
|
||||||
|
successMsg.append("<br/>").append(successNum).append("、").append(addDTO.getName()).append(" 导入成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
failureNum++;
|
||||||
|
failureMsg.append("<br/>").append(failureNum).append("、").append(addDTO.getName()).append(" 导入失败:");
|
||||||
|
failureMsg.append(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failureNum > 0) {
|
||||||
|
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
|
||||||
|
throw new RuntimeException(failureMsg.toString());
|
||||||
|
} else {
|
||||||
|
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条");
|
||||||
|
return successMsg.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建查询条件
|
||||||
|
*/
|
||||||
|
private LambdaQueryWrapper<DpcEmployee> buildQueryWrapper(DpcEmployeeQueryDTO queryDTO) {
|
||||||
|
LambdaQueryWrapper<DpcEmployee> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.like(StringUtils.isNotEmpty(queryDTO.getName()), DpcEmployee::getName, queryDTO.getName())
|
||||||
|
.eq(StringUtils.isNotEmpty(queryDTO.getTellerNo()), DpcEmployee::getTellerNo, queryDTO.getTellerNo())
|
||||||
|
.eq(StringUtils.isNotEmpty(queryDTO.getOrgNo()), DpcEmployee::getOrgNo, queryDTO.getOrgNo())
|
||||||
|
.eq(StringUtils.isNotEmpty(queryDTO.getIdCard()), DpcEmployee::getIdCard, queryDTO.getIdCard())
|
||||||
|
.eq(StringUtils.isNotEmpty(queryDTO.getStatus()), DpcEmployee::getStatus, queryDTO.getStatus())
|
||||||
|
.orderByDesc(DpcEmployee::getCreateTime);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证员工数据
|
||||||
|
*/
|
||||||
|
private void validateEmployeeData(DpcEmployeeAddDTO addDTO, Boolean isUpdateSupport) {
|
||||||
|
// 检查柜员号唯一性
|
||||||
|
LambdaQueryWrapper<DpcEmployee> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(DpcEmployee::getTellerNo, addDTO.getTellerNo());
|
||||||
|
if (employeeMapper.selectCount(wrapper) > 0) {
|
||||||
|
throw new RuntimeException("该柜员号已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查身份证号唯一性
|
||||||
|
wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(DpcEmployee::getIdCard, addDTO.getIdCard());
|
||||||
|
if (employeeMapper.selectCount(wrapper) > 0) {
|
||||||
|
throw new RuntimeException("该身份证号已存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证状态
|
||||||
|
if (!"0".equals(addDTO.getStatus()) && !"1".equals(addDTO.getStatus())) {
|
||||||
|
throw new RuntimeException("状态只能填写'在职'或'离职'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为VO对象
|
||||||
|
*/
|
||||||
|
private DpcEmployeeVO convertToVO(DpcEmployee employee) {
|
||||||
|
if (employee == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
DpcEmployeeVO vo = new DpcEmployeeVO();
|
||||||
|
BeanUtils.copyProperties(employee, vo);
|
||||||
|
|
||||||
|
// 设置状态描述
|
||||||
|
if ("0".equals(employee.getStatus())) {
|
||||||
|
vo.setStatusDesc("在职");
|
||||||
|
} else if ("1".equals(employee.getStatus())) {
|
||||||
|
vo.setStatusDesc("离职");
|
||||||
|
}
|
||||||
|
|
||||||
|
return vo;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<?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.ruoyi.dpc.mapper.DpcEmployeeMapper">
|
||||||
|
|
||||||
|
<resultMap type="com.ruoyi.dpc.domain.vo.DpcEmployeeVO" id="DpcEmployeeVOResult">
|
||||||
|
<id property="employeeId" column="employee_id"/>
|
||||||
|
<result property="name" column="name"/>
|
||||||
|
<result property="tellerNo" column="teller_no"/>
|
||||||
|
<result property="orgNo" column="org_no"/>
|
||||||
|
<result property="idCard" column="id_card"/>
|
||||||
|
<result property="phone" column="phone"/>
|
||||||
|
<result property="hireDate" column="hire_date"/>
|
||||||
|
<result property="status" column="status"/>
|
||||||
|
<result property="createTime" column="create_time"/>
|
||||||
|
<collection property="relatives" ofType="com.ruoyi.dpc.domain.vo.DpcEmployeeRelativeVO">
|
||||||
|
<id property="relativeId" column="relative_id"/>
|
||||||
|
<result property="employeeId" column="employee_id"/>
|
||||||
|
<result property="relativeName" column="relative_name"/>
|
||||||
|
<result property="relativeIdCard" column="relative_id_card"/>
|
||||||
|
<result property="relativePhone" column="relative_phone"/>
|
||||||
|
<result property="relationship" column="relationship"/>
|
||||||
|
</collection>
|
||||||
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="selectEmployeeWithRelatives" parameterType="Long" resultMap="DpcEmployeeVOResult">
|
||||||
|
SELECT
|
||||||
|
e.employee_id, e.name, e.teller_no, e.org_no, e.id_card, e.phone, e.hire_date, e.status, e.create_time,
|
||||||
|
r.relative_id, r.employee_id, r.relative_name, r.relative_id_card, r.relative_phone, r.relationship
|
||||||
|
FROM dpc_employee e
|
||||||
|
LEFT JOIN dpc_employee_relative r ON e.employee_id = r.employee_id
|
||||||
|
WHERE e.employee_id = #{employeeId}
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.ruoyi.framework.config;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
|
||||||
|
import com.ruoyi.common.utils.SecurityUtils;
|
||||||
|
import org.apache.ibatis.reflection.MetaObject;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mybatis Plus 自动填充配置
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class MybatisPlusMetaObjectHandler implements MetaObjectHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insertFill(MetaObject metaObject) {
|
||||||
|
this.strictInsertFill(metaObject, "createTime", Date.class, new Date());
|
||||||
|
this.strictInsertFill(metaObject, "createBy", String.class, getUsername());
|
||||||
|
this.strictInsertFill(metaObject, "updateTime", Date.class, new Date());
|
||||||
|
this.strictInsertFill(metaObject, "updateBy", String.class, getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateFill(MetaObject metaObject) {
|
||||||
|
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
|
||||||
|
this.strictUpdateFill(metaObject, "updateBy", String.class, getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前用户名
|
||||||
|
*/
|
||||||
|
private String getUsername() {
|
||||||
|
try {
|
||||||
|
return SecurityUtils.getUsername();
|
||||||
|
} catch (Exception e) {
|
||||||
|
return "system";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
package com.ruoyi.framework.config;
|
package com.ruoyi.framework.config;
|
||||||
|
|
||||||
|
import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
|
||||||
|
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
|
||||||
|
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
|
||||||
|
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -14,10 +18,6 @@ import org.springframework.security.web.SecurityFilterChain;
|
|||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
import org.springframework.security.web.authentication.logout.LogoutFilter;
|
||||||
import org.springframework.web.filter.CorsFilter;
|
import org.springframework.web.filter.CorsFilter;
|
||||||
import com.ruoyi.framework.config.properties.PermitAllUrlProperties;
|
|
||||||
import com.ruoyi.framework.security.filter.JwtAuthenticationTokenFilter;
|
|
||||||
import com.ruoyi.framework.security.handle.AuthenticationEntryPointImpl;
|
|
||||||
import com.ruoyi.framework.security.handle.LogoutSuccessHandlerImpl;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* spring security配置
|
* spring security配置
|
||||||
@@ -99,8 +99,8 @@ public class SecurityConfig
|
|||||||
// 注解标记允许匿名访问的url
|
// 注解标记允许匿名访问的url
|
||||||
.authorizeHttpRequests((requests) -> {
|
.authorizeHttpRequests((requests) -> {
|
||||||
permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll());
|
permitAllUrl.getUrls().forEach(url -> requests.requestMatchers(url).permitAll());
|
||||||
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
|
// 对于登录login 注册register 验证码captchaImage 测试登录login/test 允许匿名访问
|
||||||
requests.requestMatchers("/login", "/register", "/captchaImage").permitAll()
|
requests.requestMatchers("/login", "/login/test", "/register", "/captchaImage").permitAll()
|
||||||
// 静态资源,可匿名访问
|
// 静态资源,可匿名访问
|
||||||
.requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll()
|
.requestMatchers(HttpMethod.GET, "/", "/*.html", "/**.html", "/**.css", "/**.js", "/profile/**").permitAll()
|
||||||
.requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll()
|
.requestMatchers("/swagger-ui.html", "/v3/api-docs/**", "/swagger-ui/**", "/druid/**").permitAll()
|
||||||
|
|||||||
71
sql/dpc_employee.sql
Normal file
71
sql/dpc_employee.sql
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
-- Active: 1768543855903@@116.62.17.81@3306@discipline-prelim-check
|
||||||
|
-- Active: 1768543855903@@116.62.17.81@40627
|
||||||
|
-- ================================
|
||||||
|
-- 纪检初核系统 - 员工信息管理模块
|
||||||
|
-- 创建日期: 2026-01-28
|
||||||
|
-- ================================
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 1. 创建员工信息表
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `dpc_employee_relative`;
|
||||||
|
DROP TABLE IF EXISTS `dpc_employee`;
|
||||||
|
|
||||||
|
CREATE TABLE `dpc_employee` (
|
||||||
|
`employee_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '员工ID',
|
||||||
|
`name` VARCHAR(100) NOT NULL COMMENT '姓名',
|
||||||
|
`teller_no` VARCHAR(50) NOT NULL COMMENT '柜员号',
|
||||||
|
`org_no` VARCHAR(50) DEFAULT NULL COMMENT '所属机构号',
|
||||||
|
`id_card` VARCHAR(18) NOT NULL COMMENT '身份证号',
|
||||||
|
`phone` VARCHAR(11) DEFAULT NULL COMMENT '电话',
|
||||||
|
`hire_date` DATE DEFAULT NULL COMMENT '入职时间',
|
||||||
|
`status` CHAR(1) NOT NULL DEFAULT '0' COMMENT '状态(0在职 1离职)',
|
||||||
|
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||||
|
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||||
|
`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`employee_id`),
|
||||||
|
UNIQUE KEY `uk_teller_no` (`teller_no`),
|
||||||
|
UNIQUE KEY `uk_id_card` (`id_card`),
|
||||||
|
KEY `idx_org_no` (`org_no`),
|
||||||
|
KEY `idx_status` (`status`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工信息表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 2. 创建员工亲属表
|
||||||
|
-- ----------------------------
|
||||||
|
CREATE TABLE `dpc_employee_relative` (
|
||||||
|
`relative_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '亲属ID',
|
||||||
|
`employee_id` BIGINT NOT NULL COMMENT '员工ID',
|
||||||
|
`relative_name` VARCHAR(100) NOT NULL COMMENT '亲属姓名',
|
||||||
|
`relative_id_card` VARCHAR(18) DEFAULT NULL COMMENT '亲属身份证号',
|
||||||
|
`relative_phone` VARCHAR(11) DEFAULT NULL COMMENT '亲属手机号',
|
||||||
|
`relationship` VARCHAR(50) DEFAULT NULL COMMENT '与员工关系',
|
||||||
|
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
|
||||||
|
`create_time` DATETIME DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
|
||||||
|
`update_time` DATETIME DEFAULT NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`relative_id`),
|
||||||
|
KEY `idx_employee_id` (`employee_id`),
|
||||||
|
KEY `idx_relative_id_card` (`relative_id_card`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工亲属表';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 3. 字典数据 SQL
|
||||||
|
-- ----------------------------
|
||||||
|
|
||||||
|
-- 员工状态字典
|
||||||
|
INSERT INTO `sys_dict_type` VALUES (101, '员工状态', 'dpc_employee_status', '0', 'admin', sysdate(), '', NULL, '员工状态列表');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_data` VALUES (102, 1, '在职', '0', 'dpc_employee_status', '', 'primary', 'N', '0', 'admin', sysdate(), '', NULL, '在职状态');
|
||||||
|
INSERT INTO `sys_dict_data` VALUES (103, 2, '离职', '1', 'dpc_employee_status', '', 'danger', 'N', '0', 'admin', sysdate(), '', NULL, '离职状态');
|
||||||
|
|
||||||
|
-- 亲属关系字典
|
||||||
|
INSERT INTO `sys_dict_type` VALUES (102, '亲属关系', 'dpc_relative_relationship', '0', 'admin', sysdate(), '', NULL, '亲属关系列表');
|
||||||
|
|
||||||
|
INSERT INTO `sys_dict_data` VALUES (104, 1, '配偶', '配偶', 'dpc_relative_relationship', '', '', 'N', '0', 'admin', sysdate(), '', NULL, '配偶关系');
|
||||||
|
INSERT INTO `sys_dict_data` VALUES (105, 2, '父亲', '父亲', 'dpc_relative_relationship', '', '', 'N', '0', 'admin', sysdate(), '', NULL, '父亲关系');
|
||||||
|
INSERT INTO `sys_dict_data` VALUES (106, 3, '母亲', '母亲', 'dpc_relative_relationship', '', '', 'N', '0', 'admin', sysdate(), '', NULL, '母亲关系');
|
||||||
|
INSERT INTO `sys_dict_data` VALUES (107, 4, '子女', '子女', 'dpc_relative_relationship', '', '', 'N', '0', 'admin', sysdate(), '', NULL, '子女关系');
|
||||||
|
INSERT INTO `sys_dict_data` VALUES (108, 5, '兄弟姐妹', '兄弟姐妹', 'dpc_relative_relationship', '', '', 'N', '0', 'admin', sysdate(), '', NULL, '兄弟姐妹关系');
|
||||||
|
INSERT INTO `sys_dict_data` VALUES (109, 6, '其他', '其他', 'dpc_relative_relationship', '', '', 'N', '0', 'admin', sysdate(), '', NULL, '其他关系');
|
||||||
28
sql/fix_charset.sql
Normal file
28
sql/fix_charset.sql
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
-- 检查并修复数据库字符集
|
||||||
|
-- 执行前请备份数据库!
|
||||||
|
|
||||||
|
-- 1. 检查数据库字符集
|
||||||
|
SELECT
|
||||||
|
DEFAULT_CHARACTER_SET_NAME,
|
||||||
|
DEFAULT_COLLATION_NAME
|
||||||
|
FROM information_schema.SCHEMATA
|
||||||
|
WHERE SCHEMA_NAME = 'discipline-prelim-check';
|
||||||
|
|
||||||
|
-- 2. 检查 dpc_employee 表字符集
|
||||||
|
SHOW CREATE TABLE dpc_employee;
|
||||||
|
|
||||||
|
-- 3. 检查 dpc_employee_relative 表字符集
|
||||||
|
SHOW CREATE TABLE dpc_employee_relative;
|
||||||
|
|
||||||
|
-- 4. 如果字符集不是 utf8mb4,执行以下语句修复(请根据实际情况修改)
|
||||||
|
|
||||||
|
-- 修改数据库字符集
|
||||||
|
ALTER DATABASE `discipline-prelim-check` CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- 修改表字符集
|
||||||
|
ALTER TABLE `dpc_employee` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
ALTER TABLE `dpc_employee_relative` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- 5. 清空测试数据(可选)
|
||||||
|
-- TRUNCATE TABLE dpc_employee_relative;
|
||||||
|
-- TRUNCATE TABLE dpc_employee;
|
||||||
80
test/batch_insert.ps1
Normal file
80
test/batch_insert.ps1
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
# 批量插入100条员工数据
|
||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
|
||||||
|
$BASE_URL = "http://localhost:8080"
|
||||||
|
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "批量插入100条员工数据" -ForegroundColor Cyan
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# 登录获取 Token
|
||||||
|
Write-Host "[1] 正在登录..." -ForegroundColor Yellow
|
||||||
|
$loginBody = @{
|
||||||
|
username = "admin"
|
||||||
|
password = "admin123"
|
||||||
|
} | ConvertTo-Json
|
||||||
|
|
||||||
|
$loginResponse = Invoke-RestMethod -Uri "$BASE_URL/login/test" -Method Post -ContentType "application/json; charset=utf-8" -Body ([System.Text.Encoding]::UTF8.GetBytes($loginBody))
|
||||||
|
$TOKEN = $loginResponse.token
|
||||||
|
|
||||||
|
Write-Host "登录成功" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$headers = @{
|
||||||
|
"Authorization" = "Bearer $TOKEN"
|
||||||
|
"Content-Type" = "application/json; charset=utf-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 批量插入100条数据
|
||||||
|
Write-Host "[2] 开始批量插入100条员工数据..." -ForegroundColor Yellow
|
||||||
|
|
||||||
|
$successCount = 0
|
||||||
|
$failCount = 0
|
||||||
|
|
||||||
|
for ($i = 1; $i -le 100; $i++) {
|
||||||
|
try {
|
||||||
|
$tellerNo = "TEST" + $i.ToString("000")
|
||||||
|
$idCard = "110101199001011" + ($i + 200).ToString("000")
|
||||||
|
|
||||||
|
$addBody = @{
|
||||||
|
name = "测试员工" + $i
|
||||||
|
tellerNo = $tellerNo
|
||||||
|
orgNo = "1001"
|
||||||
|
idCard = $idCard
|
||||||
|
phone = "13800138" + ($i % 100).ToString("00")
|
||||||
|
status = "0"
|
||||||
|
relatives = @(
|
||||||
|
@{
|
||||||
|
relativeName = "亲属" + $i + "A"
|
||||||
|
relativeIdCard = "110101199001011" + ($i + 300).ToString("000")
|
||||||
|
relativePhone = "13900138" + ($i % 100).ToString("00")
|
||||||
|
relationship = "配偶"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} | ConvertTo-Json -Depth 10
|
||||||
|
|
||||||
|
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($addBody)
|
||||||
|
$response = Invoke-RestMethod -Uri "$BASE_URL/dpc/employee" -Method Post -Headers $headers -Body $bodyBytes
|
||||||
|
|
||||||
|
if ($response.code -eq 200) {
|
||||||
|
$successCount++
|
||||||
|
Write-Host "[$i/100] 插入成功: 测试员工$i" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
$failCount++
|
||||||
|
Write-Host "[$i/100] 插入失败: $($response.msg)" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
$failCount++
|
||||||
|
Write-Host "[$i/100] 插入异常: $_" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "插入完成" -ForegroundColor Cyan
|
||||||
|
Write-Host "成功: $successCount 条" -ForegroundColor Green
|
||||||
|
Write-Host "失败: $failCount 条" -ForegroundColor Red
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Read-Host "按回车键退出"
|
||||||
75
test/batch_insert.py
Normal file
75
test/batch_insert.py
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
|
||||||
|
BASE_URL = "http://localhost:8080"
|
||||||
|
|
||||||
|
print("=" * 50)
|
||||||
|
print("批量插入100条员工数据")
|
||||||
|
print("=" * 50)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# 登录获取 Token
|
||||||
|
print("[1] 正在登录...")
|
||||||
|
login_response = requests.post(
|
||||||
|
f"{BASE_URL}/login/test",
|
||||||
|
json={"username": "admin", "password": "admin123"}
|
||||||
|
)
|
||||||
|
token = login_response.json()["token"]
|
||||||
|
print("登录成功")
|
||||||
|
print()
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {token}",
|
||||||
|
"Content-Type": "application/json; charset=utf-8"
|
||||||
|
}
|
||||||
|
|
||||||
|
# 批量插入
|
||||||
|
print("[2] 开始批量插入100条员工数据...")
|
||||||
|
success_count = 0
|
||||||
|
fail_count = 0
|
||||||
|
|
||||||
|
for i in range(1, 101):
|
||||||
|
try:
|
||||||
|
teller_no = f"TEST{i+200:03d}"
|
||||||
|
id_card = f"110101199001011{i+400:03d}"
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"name": f"测试员工{i}",
|
||||||
|
"tellerNo": teller_no,
|
||||||
|
"orgNo": "1001",
|
||||||
|
"idCard": id_card,
|
||||||
|
"phone": f"138{10000000+i:08d}",
|
||||||
|
"status": "0",
|
||||||
|
"relatives": [
|
||||||
|
{
|
||||||
|
"relativeName": f"亲属{i}A",
|
||||||
|
"relativeIdCard": f"110101199001011{i+300:03d}",
|
||||||
|
"relativePhone": f"139{10000000+i:08d}",
|
||||||
|
"relationship": "配偶"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
response = requests.post(
|
||||||
|
f"{BASE_URL}/dpc/employee",
|
||||||
|
headers=headers,
|
||||||
|
json=data
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.json()["code"] == 200:
|
||||||
|
success_count += 1
|
||||||
|
print(f"[{i}/100] 插入成功: 测试员工{i}")
|
||||||
|
else:
|
||||||
|
fail_count += 1
|
||||||
|
print(f"[{i}/100] 插入失败: {response.json()['msg']}")
|
||||||
|
except Exception as e:
|
||||||
|
fail_count += 1
|
||||||
|
print(f"[{i}/100] 插入异常: {e}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 50)
|
||||||
|
print("插入完成")
|
||||||
|
print(f"成功: {success_count} 条")
|
||||||
|
print(f"失败: {fail_count} 条")
|
||||||
|
print("=" * 50)
|
||||||
1
test/test_data.json
Normal file
1
test/test_data.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"name":"测试员工","tellerNo":"TEST002","orgNo":"1001","idCard":"110101199001011237","phone":"13800138000","status":"0","relatives":[{"relativeName":"李四","relativeIdCard":"110101199001011235","relativePhone":"13800138001","relationship":"配偶"}]}
|
||||||
66
test/test_employee_api.bat
Normal file
66
test/test_employee_api.bat
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
@echo off
|
||||||
|
chcp 65001 > nul
|
||||||
|
setlocal enabledelayedexpansion
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo 员工信息管理 API 测试脚本
|
||||||
|
echo ========================================
|
||||||
|
echo.
|
||||||
|
|
||||||
|
set BASE_URL=http://localhost:8080
|
||||||
|
set TOKEN=
|
||||||
|
|
||||||
|
REM 1. 登录获取 Token
|
||||||
|
echo [1] 正在登录...
|
||||||
|
curl -s -X POST "%BASE_URL%/login/test" -H "Content-Type: application/json" -d "{\"username\":\"admin\",\"password\":\"admin123\"}" > login_response.json
|
||||||
|
|
||||||
|
REM 使用 PowerShell 提取 token
|
||||||
|
for /f "tokens=*" %%i in ('powershell -Command "$json = Get-Content login_response.json | ConvertFrom-Json; $json.token"') do (
|
||||||
|
set TOKEN=%%i
|
||||||
|
)
|
||||||
|
|
||||||
|
del login_response.json
|
||||||
|
|
||||||
|
if "%TOKEN%"=="" (
|
||||||
|
echo [错误] 获取 Token 失败,请检查登录接口
|
||||||
|
pause
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
|
||||||
|
echo 登录成功,Token: %TOKEN%
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 2. 测试查询员工列表
|
||||||
|
echo [2] 测试查询员工列表...
|
||||||
|
curl -s -X GET "%BASE_URL%/dpc/employee/list" -H "Authorization: Bearer %TOKEN%"
|
||||||
|
echo.
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 3. 测试新增员工
|
||||||
|
echo [3] 测试新增员工...
|
||||||
|
curl -s -X POST "%BASE_URL%/dpc/employee" -H "Authorization: Bearer %TOKEN%" -H "Content-Type: application/json" -d "{\"name\":\"测试员工\",\"tellerNo\":\"TEST001\",\"orgNo\":\"1001\",\"idCard\":\"110101199001011234\",\"phone\":\"13800138000\",\"status\":\"0\",\"relatives\":[{\"relativeName\":\"李四\",\"relativeIdCard\":\"110101199001011235\",\"relativePhone\":\"13800138001\",\"relationship\":\"配偶\"}]}"
|
||||||
|
echo.
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 4. 测试查询员工详情
|
||||||
|
echo [4] 测试查询员工详情...
|
||||||
|
curl -s -X GET "%BASE_URL%/dpc/employee/1" -H "Authorization: Bearer %TOKEN%"
|
||||||
|
echo.
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 5. 测试编辑员工
|
||||||
|
echo [5] 测试编辑员工...
|
||||||
|
curl -s -X PUT "%BASE_URL%/dpc/employee" -H "Authorization: Bearer %TOKEN%" -H "Content-Type: application/json" -d "{\"employeeId\":1,\"name\":\"测试员工-修改\",\"tellerNo\":\"TEST001\",\"orgNo\":\"1001\",\"idCard\":\"110101199001011234\",\"phone\":\"13800138000\",\"status\":\"0\",\"relatives\":[{\"relativeName\":\"王五\",\"relativeIdCard\":\"110101199001011236\",\"relativePhone\":\"13800138002\",\"relationship\":\"子女\"}]}"
|
||||||
|
echo.
|
||||||
|
echo.
|
||||||
|
|
||||||
|
REM 6. 测试删除员工
|
||||||
|
echo [6] 测试删除员工...
|
||||||
|
curl -s -X DELETE "%BASE_URL%/dpc/employee/1" -H "Authorization: Bearer %TOKEN%"
|
||||||
|
echo.
|
||||||
|
echo.
|
||||||
|
|
||||||
|
echo ========================================
|
||||||
|
echo 测试完成
|
||||||
|
echo ========================================
|
||||||
|
pause
|
||||||
119
test/test_employee_api.ps1
Normal file
119
test/test_employee_api.ps1
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# 员工信息管理 API 测试脚本
|
||||||
|
# 需要使用 UTF-8 with BOM 编码保存
|
||||||
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
$OutputEncoding = [System.Text.Encoding]::UTF8
|
||||||
|
|
||||||
|
$BASE_URL = "http://localhost:8080"
|
||||||
|
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "员工信息管理 API 测试脚本" -ForegroundColor Cyan
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# 1. 登录获取 Token
|
||||||
|
Write-Host "[1] 正在登录..." -ForegroundColor Yellow
|
||||||
|
$loginBody = @{
|
||||||
|
username = "admin"
|
||||||
|
password = "admin123"
|
||||||
|
} | ConvertTo-Json
|
||||||
|
|
||||||
|
$loginResponse = Invoke-RestMethod -Uri "$BASE_URL/login/test" -Method Post -ContentType "application/json; charset=utf-8" -Body ([System.Text.Encoding]::UTF8.GetBytes($loginBody))
|
||||||
|
$TOKEN = $loginResponse.token
|
||||||
|
|
||||||
|
if ([string]::IsNullOrEmpty($TOKEN)) {
|
||||||
|
Write-Host "[错误] 获取 Token 失败,请检查登录接口" -ForegroundColor Red
|
||||||
|
Read-Host "按回车键退出"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "登录成功,Token: $TOKEN" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# 2. 测试查询员工列表
|
||||||
|
Write-Host "[2] 测试查询员工列表..." -ForegroundColor Yellow
|
||||||
|
$headers = @{
|
||||||
|
"Authorization" = "Bearer $TOKEN"
|
||||||
|
}
|
||||||
|
$response = Invoke-RestMethod -Uri "$BASE_URL/dpc/employee/list" -Method Get -Headers $headers
|
||||||
|
Write-Host ($response | ConvertTo-Json -Depth 10)
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# 3. 测试新增员工
|
||||||
|
Write-Host "[3] 测试新增员工..." -ForegroundColor Yellow
|
||||||
|
$addBody = @{
|
||||||
|
name = "测试员工"
|
||||||
|
tellerNo = "TEST001"
|
||||||
|
orgNo = "1001"
|
||||||
|
idCard = "110101199001011234"
|
||||||
|
phone = "13800138000"
|
||||||
|
status = "0"
|
||||||
|
relatives = @(
|
||||||
|
@{
|
||||||
|
relativeName = "李四"
|
||||||
|
relativeIdCard = "110101199001011235"
|
||||||
|
relativePhone = "13800138001"
|
||||||
|
relationship = "配偶"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} | ConvertTo-Json -Depth 10
|
||||||
|
|
||||||
|
$headers = @{
|
||||||
|
"Authorization" = "Bearer $TOKEN"
|
||||||
|
"Content-Type" = "application/json; charset=utf-8"
|
||||||
|
}
|
||||||
|
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($addBody)
|
||||||
|
$response = Invoke-RestMethod -Uri "$BASE_URL/dpc/employee" -Method Post -Headers $headers -Body $bodyBytes
|
||||||
|
Write-Host ($response | ConvertTo-Json -Depth 10)
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# 4. 测试查询员工详情
|
||||||
|
Write-Host "[4] 测试查询员工详情..." -ForegroundColor Yellow
|
||||||
|
$headers = @{
|
||||||
|
"Authorization" = "Bearer $TOKEN"
|
||||||
|
}
|
||||||
|
$response = Invoke-RestMethod -Uri "$BASE_URL/dpc/employee/1" -Method Get -Headers $headers
|
||||||
|
Write-Host ($response | ConvertTo-Json -Depth 10)
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# 5. 测试编辑员工
|
||||||
|
Write-Host "[5] 测试编辑员工..." -ForegroundColor Yellow
|
||||||
|
$editBody = @{
|
||||||
|
employeeId = 1
|
||||||
|
name = "测试员工-修改"
|
||||||
|
tellerNo = "TEST001"
|
||||||
|
orgNo = "1001"
|
||||||
|
idCard = "110101199001011234"
|
||||||
|
phone = "13800138000"
|
||||||
|
status = "0"
|
||||||
|
relatives = @(
|
||||||
|
@{
|
||||||
|
relativeName = "王五"
|
||||||
|
relativeIdCard = "110101199001011236"
|
||||||
|
relativePhone = "13800138002"
|
||||||
|
relationship = "子女"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
} | ConvertTo-Json -Depth 10
|
||||||
|
|
||||||
|
$headers = @{
|
||||||
|
"Authorization" = "Bearer $TOKEN"
|
||||||
|
"Content-Type" = "application/json; charset=utf-8"
|
||||||
|
}
|
||||||
|
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($editBody)
|
||||||
|
$response = Invoke-RestMethod -Uri "$BASE_URL/dpc/employee" -Method Put -Headers $headers -Body $bodyBytes
|
||||||
|
Write-Host ($response | ConvertTo-Json -Depth 10)
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# 6. 测试删除员工
|
||||||
|
Write-Host "[6] 测试删除员工..." -ForegroundColor Yellow
|
||||||
|
$headers = @{
|
||||||
|
"Authorization" = "Bearer $TOKEN"
|
||||||
|
}
|
||||||
|
$response = Invoke-RestMethod -Uri "$BASE_URL/dpc/employee/1" -Method Delete -Headers $headers
|
||||||
|
Write-Host ($response | ConvertTo-Json -Depth 10)
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "测试完成" -ForegroundColor Cyan
|
||||||
|
Write-Host "========================================" -ForegroundColor Cyan
|
||||||
|
Read-Host "按回车键退出"
|
||||||
Reference in New Issue
Block a user