feat: 员工信息管理功能完善
- 将员工表org_no字段迁移至dept_id,关联系统部门表 - 更新员工信息相关DTO、VO和Controller,使用deptId替代orgNo - 添加员工信息管理OpenSpec规范文档(proposal/design/spec/tasks) - 更新API文档,反映部门关联变更 - 添加数据库迁移脚本employee_org_no_to_dept_id.sql - 新增员工信息分页接口测试脚本(PowerShell/Python) - 更新CLAUDE.md,添加MCP数据库工具使用说明 Co-Authored-By: Claude (glm-4.7) <noreply@anthropic.com>
This commit is contained in:
@@ -13,7 +13,13 @@
|
|||||||
"Skill(document-skills:mcp-builder)",
|
"Skill(document-skills:mcp-builder)",
|
||||||
"Bash(ping:*)",
|
"Bash(ping:*)",
|
||||||
"Bash(git commit:*)",
|
"Bash(git commit:*)",
|
||||||
"Bash(taskkill:*)"
|
"Bash(taskkill:*)",
|
||||||
|
"Bash(cd:*)",
|
||||||
|
"mcp__database-server__read_query",
|
||||||
|
"mcp__database-server__list_tables",
|
||||||
|
"mcp__database-server__describe_table",
|
||||||
|
"mcp__database-server__list_insights",
|
||||||
|
"mcp__database-server__alter_table"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"enabledMcpjsonServers": [
|
"enabledMcpjsonServers": [
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
|
|
||||||
|
|
||||||
## 运行
|
## 运行
|
||||||
|
- 使用mcp工具进行数据库相关操作
|
||||||
- 使用根目录中的ry.bat控制后端的启动,不要自行在命令行中启动后端
|
- 使用根目录中的ry.bat控制后端的启动,不要自行在命令行中启动后端
|
||||||
- 测试方式为生成可执行的测试脚本
|
- 测试方式为生成可执行的测试脚本
|
||||||
- 测试脚本在运行完成后需要保存所有接口输出并生成测试用例报告
|
- 测试脚本在运行完成后需要保存所有接口输出并生成测试用例报告
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
|--------|------|------|------|
|
|--------|------|------|------|
|
||||||
| name | String | 否 | 姓名(模糊查询) |
|
| name | String | 否 | 姓名(模糊查询) |
|
||||||
| tellerNo | String | 否 | 柜员号(精确查询) |
|
| tellerNo | String | 否 | 柜员号(精确查询) |
|
||||||
| orgNo | String | 否 | 所属机构号 |
|
| deptId | Long | 否 | 所属部门ID |
|
||||||
| idCard | String | 否 | 身份证号(精确查询) |
|
| idCard | String | 否 | 身份证号(精确查询) |
|
||||||
| status | String | 否 | 状态(0=在职, 1=离职) |
|
| status | String | 否 | 状态(0=在职, 1=离职) |
|
||||||
| pageNum | Integer | 否 | 页码(默认1) |
|
| pageNum | Integer | 否 | 页码(默认1) |
|
||||||
@@ -40,7 +40,8 @@
|
|||||||
"employeeId": 1,
|
"employeeId": 1,
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
"tellerNo": "001",
|
"tellerNo": "001",
|
||||||
"orgNo": "1001",
|
"deptId": 100,
|
||||||
|
"deptName": "总部",
|
||||||
"idCard": "110101199001011234",
|
"idCard": "110101199001011234",
|
||||||
"phone": "13800138000",
|
"phone": "13800138000",
|
||||||
"hireDate": "2020-01-01",
|
"hireDate": "2020-01-01",
|
||||||
@@ -53,6 +54,22 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**响应字段说明**:
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| employeeId | Long | 员工ID |
|
||||||
|
| name | String | 姓名 |
|
||||||
|
| tellerNo | String | 柜员号 |
|
||||||
|
| deptId | Long | 所属部门ID |
|
||||||
|
| deptName | String | 所属部门名称(关联 sys_dept 表) |
|
||||||
|
| idCard | String | 身份证号 |
|
||||||
|
| phone | String | 电话 |
|
||||||
|
| hireDate | Date | 入职时间 |
|
||||||
|
| status | String | 状态(0=在职, 1=离职) |
|
||||||
|
| statusDesc | String | 状态描述 |
|
||||||
|
| createTime | Date | 创建时间 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### 2. 查询员工详情
|
### 2. 查询员工详情
|
||||||
@@ -76,7 +93,7 @@
|
|||||||
"employeeId": 1,
|
"employeeId": 1,
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
"tellerNo": "001",
|
"tellerNo": "001",
|
||||||
"orgNo": "1001",
|
"deptId": 100,
|
||||||
"idCard": "110101199001011234",
|
"idCard": "110101199001011234",
|
||||||
"phone": "13800138000",
|
"phone": "13800138000",
|
||||||
"hireDate": "2020-01-01",
|
"hireDate": "2020-01-01",
|
||||||
@@ -116,7 +133,7 @@ Authorization: Bearer {token}
|
|||||||
{
|
{
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
"tellerNo": "001",
|
"tellerNo": "001",
|
||||||
"orgNo": "1001",
|
"deptId": 100,
|
||||||
"idCard": "110101199001011234",
|
"idCard": "110101199001011234",
|
||||||
"phone": "13800138000",
|
"phone": "13800138000",
|
||||||
"hireDate": "2020-01-01",
|
"hireDate": "2020-01-01",
|
||||||
@@ -138,7 +155,7 @@ Authorization: Bearer {token}
|
|||||||
|--------|------|------|------|----------|
|
|--------|------|------|------|----------|
|
||||||
| name | String | 是 | 姓名 | 最大100字符 |
|
| name | String | 是 | 姓名 | 最大100字符 |
|
||||||
| tellerNo | String | 是 | 柜员号 | 最大50字符,唯一 |
|
| tellerNo | String | 是 | 柜员号 | 最大50字符,唯一 |
|
||||||
| orgNo | String | 否 | 所属机构号 | 最大50字符 |
|
| deptId | Long | 否 | 所属部门ID | |
|
||||||
| idCard | String | 是 | 身份证号 | 18位,符合国标,唯一 |
|
| idCard | String | 是 | 身份证号 | 18位,符合国标,唯一 |
|
||||||
| phone | String | 否 | 电话 | 11位手机号 |
|
| phone | String | 否 | 电话 | 11位手机号 |
|
||||||
| hireDate | Date | 否 | 入职时间 | yyyy-MM-dd |
|
| hireDate | Date | 否 | 入职时间 | yyyy-MM-dd |
|
||||||
@@ -176,7 +193,7 @@ Authorization: Bearer {token}
|
|||||||
"employeeId": 1,
|
"employeeId": 1,
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
"tellerNo": "001",
|
"tellerNo": "001",
|
||||||
"orgNo": "1001",
|
"deptId": 100,
|
||||||
"idCard": "110101199001011234",
|
"idCard": "110101199001011234",
|
||||||
"phone": "13800138000",
|
"phone": "13800138000",
|
||||||
"hireDate": "2020-01-01",
|
"hireDate": "2020-01-01",
|
||||||
@@ -266,9 +283,9 @@ Authorization: Bearer {token}
|
|||||||
**Excel 格式**:
|
**Excel 格式**:
|
||||||
|
|
||||||
**Sheet1: 员工信息**
|
**Sheet1: 员工信息**
|
||||||
| 姓名 | 柜员号 | 所属机构号 | 身份证号 | 电话 | 入职时间 | 状态 |
|
| 姓名 | 柜员号 | 所属部门ID | 身份证号 | 电话 | 入职时间 | 状态 |
|
||||||
|------|--------|------------|----------|------|----------|------|
|
|------|--------|------------|----------|------|----------|------|
|
||||||
| 张三 | 001 | 1001 | 110101199001011234 | 13800138000 | 2020-01-01 | 在职 |
|
| 张三 | 001 | 100 | 110101199001011234 | 13800138000 | 2020-01-01 | 在职 |
|
||||||
|
|
||||||
**Sheet2: 亲属信息(可选)**
|
**Sheet2: 亲属信息(可选)**
|
||||||
| 员工身份证号 | 亲属姓名 | 亲属身份证号 | 亲属手机号 | 与员工关系 |
|
| 员工身份证号 | 亲属姓名 | 亲属身份证号 | 亲属手机号 | 与员工关系 |
|
||||||
|
|||||||
223
openspec/changes/add-employee-info-ui/design.md
Normal file
223
openspec/changes/add-employee-info-ui/design.md
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
# Design: 员工信息管理前端设计
|
||||||
|
|
||||||
|
## 页面布局
|
||||||
|
|
||||||
|
### 主页面布局
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────────────┐
|
||||||
|
│ 信息维护 > 员工信息管理 │
|
||||||
|
├─────────────────────────────────────────────────────────────┤
|
||||||
|
│ ┌───────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ 搜索区: [姓名] [柜员号] [所属机构号] [身份证号] [状态▼] │ │
|
||||||
|
│ │ [搜索] [重置] │ │
|
||||||
|
│ └───────────────────────────────────────────────────────┘ │
|
||||||
|
│ ┌───────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ [新增] [导入] [搜索框展开] │ │
|
||||||
|
│ └───────────────────────────────────────────────────────┘ │
|
||||||
|
│ ┌───────────────────────────────────────────────────────┐ │
|
||||||
|
│ │ ┌───┬─────┬───────┬───────┬─────────┬─────┬─────────┐ │ │
|
||||||
|
│ │ │□ │姓名 │柜员号 │身份证号│所属机构 │状态 │ 操作 │ │ │
|
||||||
|
│ │ ├───┼─────┼───────┼───────┼─────────┼─────┼─────────┤ │ │
|
||||||
|
│ │ │□ │张三 │001 │110... │1001 │在职 │详情|编辑│ │ │
|
||||||
|
│ │ │ │ │ │ │ │ │删除 │ │ │
|
||||||
|
│ │ └───┴─────┴───────┴───────┴─────────┴─────┴─────────┘ │ │
|
||||||
|
│ │ < 1 2 3 4 5 ... 10 > │ │
|
||||||
|
│ └───────────────────────────────────────────────────────┘ │
|
||||||
|
└─────────────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 新增/编辑弹窗布局
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ 新增员工 / 编辑员工 [X]│
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ ┌───────────────────────────────────────┐ │
|
||||||
|
│ │ 姓名: [_____________] │ │
|
||||||
|
│ │ 柜员号: [_____________] │ │
|
||||||
|
│ │ 所属部门: [请选择部门 ▼] │ │
|
||||||
|
│ │ 身份证号: [__________________] │ │
|
||||||
|
│ │ 电话: [_____________] │ │
|
||||||
|
│ │ 入职时间: [_______________] │ │
|
||||||
|
│ │ 状态: ( ) 在职 ( ) 离职 │ │
|
||||||
|
│ └───────────────────────────────────────┘ │
|
||||||
|
│ ┌───────────────────────────────────────┐ │
|
||||||
|
│ │ 亲属信息 [+ 添加亲属] │ │
|
||||||
|
│ │ ┌───────────────────────────────────┐ │ │
|
||||||
|
│ │ │ 亲属姓名 | 身份证号 | 电话 | 关系 │ │ │
|
||||||
|
│ │ │ 李四 | 110... | 138..| 配偶 │ │ │
|
||||||
|
│ │ │ | [删除] | | │ │ │
|
||||||
|
│ │ └───────────────────────────────────┘ │ │
|
||||||
|
│ └───────────────────────────────────────┘ │
|
||||||
|
│ [确定] [取消] │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### 详情弹窗布局
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────┐
|
||||||
|
│ 员工详情 [X]│
|
||||||
|
├─────────────────────────────────────────────┤
|
||||||
|
│ ┌───────────────────────────────────────┐ │
|
||||||
|
│ │ 姓名: 张三 │ │
|
||||||
|
│ │ 柜员号: 001 │ │
|
||||||
|
│ │ 所属部门: 研发部门 │ │
|
||||||
|
│ │ 身份证号: 110101199001011234 │ │
|
||||||
|
│ │ 电话: 13800138000 │ │
|
||||||
|
│ │ 入职时间: 2020-01-01 │ │
|
||||||
|
│ │ 状态: 在职 │ │
|
||||||
|
│ │ 创建时间: 2026-01-28 10:00:00 │ │
|
||||||
|
│ └───────────────────────────────────────┘ │
|
||||||
|
│ ┌───────────────────────────────────────┐ │
|
||||||
|
│ │ 亲属信息 │ │
|
||||||
|
│ │ ┌───────────────────────────────────┐ │ │
|
||||||
|
│ │ │ 亲属姓名 │ 身份证号 │ 电话 │关系│ │ │
|
||||||
|
│ │ │ 李四 │ 110101... │ 138..│配偶│ │ │
|
||||||
|
│ │ │ │ │ │ │ │ │
|
||||||
|
│ │ └───────────────────────────────────┘ │ │
|
||||||
|
│ └───────────────────────────────────────┘ │
|
||||||
|
│ [关闭] │
|
||||||
|
└─────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 组件结构
|
||||||
|
|
||||||
|
### 文件结构
|
||||||
|
```
|
||||||
|
ruoyi-ui/src/
|
||||||
|
├── api/
|
||||||
|
│ └── dpcEmployee.js # API 接口定义
|
||||||
|
└── views/
|
||||||
|
└── dpcEmployee/
|
||||||
|
└── index.vue # 主页面组件
|
||||||
|
```
|
||||||
|
|
||||||
|
### 数据模型
|
||||||
|
|
||||||
|
#### 员工列表项
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
employeeId: Number,
|
||||||
|
name: String,
|
||||||
|
tellerNo: String,
|
||||||
|
orgNo: String, // 部门 ID (dept_id)
|
||||||
|
orgName: String, // 部门名称 (用于显示)
|
||||||
|
idCard: String,
|
||||||
|
phone: String,
|
||||||
|
hireDate: String,
|
||||||
|
status: String,
|
||||||
|
statusDesc: String,
|
||||||
|
createTime: String
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 员工表单
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
employeeId: Number | null,
|
||||||
|
name: String,
|
||||||
|
tellerNo: String,
|
||||||
|
orgNo: String, // 部门 ID (dept_id)
|
||||||
|
idCard: String,
|
||||||
|
phone: String,
|
||||||
|
hireDate: String,
|
||||||
|
status: String,
|
||||||
|
relatives: Array<{
|
||||||
|
relativeId: Number | null,
|
||||||
|
relativeName: String,
|
||||||
|
relativeIdCard: String,
|
||||||
|
relativePhone: String,
|
||||||
|
relationship: String
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### API 接口
|
||||||
|
|
||||||
|
| 接口名 | 方法 | 路径 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| listEmployee | GET | /dpc/employee/list | 查询员工列表 |
|
||||||
|
| getEmployee | GET | /dpc/employee/{id} | 获取员工详情 |
|
||||||
|
| addEmployee | POST | /dpc/employee | 新增员工 |
|
||||||
|
| updateEmployee | PUT | /dpc/employee | 编辑员工 |
|
||||||
|
| delEmployee | DELETE | /dpc/employee/{ids} | 删除员工 |
|
||||||
|
| importTemplate | POST | /dpc/employee/importTemplate | 下载导入模板 |
|
||||||
|
| importData | POST | /dpc/employee/importData | 导入员工信息 |
|
||||||
|
|
||||||
|
## 交互设计
|
||||||
|
|
||||||
|
### 1. 列表页面
|
||||||
|
- **默认状态**: 显示所有员工列表,默认分页大小 10
|
||||||
|
- **搜索**: 支持按姓名(模糊)、柜员号(精确)、所属部门、身份证号(精确)、状态筛选
|
||||||
|
- **所属部门筛选**: 使用部门树选择器
|
||||||
|
- **新增**: 点击"新增"按钮打开新增弹窗
|
||||||
|
- **编辑**: 点击"编辑"按钮打开编辑弹窗
|
||||||
|
- **详情**: 点击"详情"按钮打开详情弹窗
|
||||||
|
- **删除**: 点击"删除"按钮,弹出确认对话框后删除
|
||||||
|
- **导入**: 点击"导入"按钮,打开导入弹窗
|
||||||
|
|
||||||
|
### 2. 新增/编辑弹窗
|
||||||
|
- **所属部门**: 使用部门树选择器(`el-tree-select`),存储 `dept_id`
|
||||||
|
- **亲属管理**:
|
||||||
|
- 点击"+ 添加亲属"添加空行
|
||||||
|
- 点击"删除"移除对应亲属行
|
||||||
|
- 亲属姓名、关系为必填
|
||||||
|
- **表单验证**:
|
||||||
|
- 姓名: 必填,最大100字符
|
||||||
|
- 柜员号: 必填,最大50字符
|
||||||
|
- 所属部门: 选填
|
||||||
|
- 身份证号: 必填,18位格式校验
|
||||||
|
- 电话: 选填,11位手机号格式
|
||||||
|
- 亲属姓名: 必填
|
||||||
|
- 亲属关系: 必填
|
||||||
|
|
||||||
|
### 3. 详情弹窗
|
||||||
|
- 只读展示员工信息和亲属列表
|
||||||
|
- 无亲属时显示"暂无亲属信息"
|
||||||
|
|
||||||
|
### 4. 导入弹窗
|
||||||
|
- 支持拖拽上传
|
||||||
|
- 支持"更新已存在数据"选项
|
||||||
|
- 显示导入结果消息
|
||||||
|
|
||||||
|
## 表单验证规则
|
||||||
|
|
||||||
|
### 员工基本信息
|
||||||
|
| 字段 | 验证规则 |
|
||||||
|
|------|----------|
|
||||||
|
| 姓名 | 必填,最大100字符 |
|
||||||
|
| 柜员号 | 必填,最大50字符 |
|
||||||
|
| 身份证号 | 必填,18位,符合国标 |
|
||||||
|
| 电话 | 选填,11位手机号 |
|
||||||
|
| 入职时间 | 选填,日期格式 |
|
||||||
|
| 状态 | 必填,0或1 |
|
||||||
|
|
||||||
|
### 亲属信息
|
||||||
|
| 字段 | 验证规则 |
|
||||||
|
|------|----------|
|
||||||
|
| 亲属姓名 | 必填 |
|
||||||
|
| 亲属身份证号 | 选填 |
|
||||||
|
| 亲属电话 | 选填,11位手机号 |
|
||||||
|
| 与员工关系 | 必填 |
|
||||||
|
|
||||||
|
## 权限控制
|
||||||
|
|
||||||
|
| 按钮 | 权限标识 |
|
||||||
|
|------|----------|
|
||||||
|
| 新增 | dpc:employee:add |
|
||||||
|
| 编辑 | dpc:employee:edit |
|
||||||
|
| 删除 | dpc:employee:remove |
|
||||||
|
| 详情 | dpc:employee:query |
|
||||||
|
| 导入 | dpc:employee:import |
|
||||||
|
|
||||||
|
## 技术实现要点
|
||||||
|
|
||||||
|
1. **亲属子表单**: 使用 `v-for` 动态渲染亲属列表,支持增删操作
|
||||||
|
2. **部门选择器**: 使用 Element UI 的 `el-tree-select` 组件,关联 `sys_dept` 表
|
||||||
|
3. **日期选择器**: 使用 Element UI 的 `el-date-picker`
|
||||||
|
4. **状态下拉**: 使用 `el-select` 或 `el-radio-group`
|
||||||
|
5. **文件上传**: 使用 Element UI 的 `el-upload`
|
||||||
|
6. **分页**: 使用若依框架的 `pagination` 组件
|
||||||
|
|
||||||
|
## 参考
|
||||||
|
- 中介库管理页面: `ruoyi-ui/src/views/dpcIntermediary/index.vue`
|
||||||
|
- 若依用户管理页面: `ruoyi-ui/src/views/system/user/index.vue`
|
||||||
119
openspec/changes/add-employee-info-ui/proposal.md
Normal file
119
openspec/changes/add-employee-info-ui/proposal.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# Proposal: 添加员工信息管理前端页面
|
||||||
|
|
||||||
|
## Change ID
|
||||||
|
`add-employee-info-ui`
|
||||||
|
|
||||||
|
## Summary
|
||||||
|
为员工信息管理模块开发前端页面,实现员工及其亲属信息的可视化管理。包括员工列表查询、新增/编辑员工(支持亲属管理)、详情查看、Excel 导入等功能。页面将放置在"信息维护"菜单下。
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
员工信息管理的后端 API 已在 `add-employee-info` 变更中完成开发。现在需要开发前端页面以提供用户交互界面。
|
||||||
|
|
||||||
|
用户需要:
|
||||||
|
1. 可视化查看和管理员工信息列表
|
||||||
|
2. 创建员工时同时维护亲属信息
|
||||||
|
3. 查看员工详情时展示所有亲属
|
||||||
|
4. 支持批量导入员工数据
|
||||||
|
5. 在"信息维护"菜单下访问此功能
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
本提案实现以下前端功能:
|
||||||
|
|
||||||
|
### 包含的功能
|
||||||
|
- **员工列表页面**
|
||||||
|
- 搜索筛选区(姓名、柜员号、所属部门、身份证号、状态)
|
||||||
|
- 员工列表表格(分页显示)
|
||||||
|
- 操作按钮(新增、导入)
|
||||||
|
- 部门树选择器
|
||||||
|
|
||||||
|
- **新增员工弹窗**
|
||||||
|
- 员工基本信息表单
|
||||||
|
- 所属部门选择(关联 sys_dept 表)
|
||||||
|
- 亲属列表子表单(支持动态增删)
|
||||||
|
- 表单验证
|
||||||
|
|
||||||
|
- **编辑员工弹窗**
|
||||||
|
- 员工基本信息表单
|
||||||
|
- 所属部门选择
|
||||||
|
- 亲属列表子表单(支持动态增删)
|
||||||
|
- 表单验证
|
||||||
|
|
||||||
|
- **员工详情弹窗**
|
||||||
|
- 员工基本信息展示(只读)
|
||||||
|
- 显示部门名称
|
||||||
|
- 亲属列表展示(只读)
|
||||||
|
|
||||||
|
- **导入功能**
|
||||||
|
- 导入弹窗
|
||||||
|
- Excel 文件上传
|
||||||
|
- 导入结果反馈
|
||||||
|
- 模板下载
|
||||||
|
|
||||||
|
### 明确排除
|
||||||
|
- 菜单数据的数据库插入(需要前端开发完成后手动配置)
|
||||||
|
- 员工信息与其他模块的联动(属于后续功能)
|
||||||
|
|
||||||
|
## Proposed Design
|
||||||
|
详见 [design.md](./design.md)
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
|
||||||
|
### 选项1:参考中介库管理页面实现
|
||||||
|
**优点**:
|
||||||
|
- 保持项目内 UI/UX 一致性
|
||||||
|
- 复用已有的代码模式
|
||||||
|
- 开发效率高
|
||||||
|
|
||||||
|
**缺点**:
|
||||||
|
- 需要额外实现亲属子表单功能
|
||||||
|
|
||||||
|
**决定**:采用。参考 `dpcIntermediary` 页面的实现模式,在此基础上增加亲属子表单功能。
|
||||||
|
|
||||||
|
### 选项2:使用若依代码生成器
|
||||||
|
**优点**:
|
||||||
|
- 快速生成基础 CRUD 页面
|
||||||
|
|
||||||
|
**缺点**:
|
||||||
|
- 生成代码需要大量调整以支持亲属子表单
|
||||||
|
- 与现有前端风格可能不一致
|
||||||
|
|
||||||
|
**决定**:不采用。手动开发可以更好地实现亲属子表单功能,保持与中介库页面一致的风格。
|
||||||
|
|
||||||
|
## Impact
|
||||||
|
|
||||||
|
### 前端影响
|
||||||
|
- 新增视图:`ruoyi-ui/src/views/dpcEmployee/`
|
||||||
|
- `index.vue` - 员工列表页面
|
||||||
|
- 新增 API:`ruoyi-ui/src/api/dpcEmployee.js`
|
||||||
|
|
||||||
|
### 后端影响
|
||||||
|
无,后端 API 已在 `add-employee-info` 中完成。
|
||||||
|
|
||||||
|
### 数据库影响
|
||||||
|
- 需要在 `sys_menu` 表中插入菜单数据(开发完成后手动配置)
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- 依赖 `add-employee-info` 变更完成后端 API
|
||||||
|
- 依赖若依框架的前端组件(Element UI)
|
||||||
|
|
||||||
|
## Related Changes
|
||||||
|
- `add-employee-info` - 提供后端 API 接口
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
无
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
- [ ] 用户可以在"信息维护"菜单下访问员工信息管理页面
|
||||||
|
- [ ] 用户可以查看员工列表并支持分页
|
||||||
|
- [ ] 用户可以按多种条件搜索员工
|
||||||
|
- [ ] 用户可以新增员工并添加亲属
|
||||||
|
- [ ] 用户可以编辑员工信息和亲属
|
||||||
|
- [ ] 用户可以查看员工详情和亲属列表
|
||||||
|
- [ ] 用户可以删除员工
|
||||||
|
- [ ] 用户可以下载 Excel 导入模板
|
||||||
|
- [ ] 用户可以导入员工数据
|
||||||
|
|
||||||
|
## References
|
||||||
|
- [员工信息管理 API 文档](../../../doc/员工信息管理API文档.md)
|
||||||
|
- [模块设计文档 - 信息维护模块](../../../doc/modules/03-信息维护模块.md)
|
||||||
|
- [add-employee-info 变更](../add-employee-info/)
|
||||||
@@ -0,0 +1,475 @@
|
|||||||
|
# Spec: 员工信息管理前端用户界面
|
||||||
|
|
||||||
|
## ADDED Requirements
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL提供员工信息列表页面
|
||||||
|
|
||||||
|
前端MUST提供员工信息列表页面,允许用户查看、搜索和管理员工信息。
|
||||||
|
|
||||||
|
#### Scenario: 页面加载时显示员工列表
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**When** 用户访问员工信息管理页面
|
||||||
|
**Then** 页面应显示员工列表
|
||||||
|
**And** 列表应默认显示第1页,每页10条记录
|
||||||
|
**And** 页面应显示搜索筛选区和操作按钮
|
||||||
|
|
||||||
|
#### Scenario: 列表显示正确的员工信息
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**When** 系统返回员工列表数据
|
||||||
|
**Then** 列表应显示以下列:姓名、柜员号、身份证号、所属部门、电话、状态、操作
|
||||||
|
**And** 状态列应显示"在职"或"离职"的文字描述
|
||||||
|
**And** 操作列应显示"详情"、"编辑"、"删除"按钮
|
||||||
|
|
||||||
|
#### Scenario: 列表支持分页
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在25条员工记录
|
||||||
|
**When** 用户访问员工信息管理页面
|
||||||
|
**Then** 页面应显示分页控件
|
||||||
|
**And** 默认显示第1页的10条记录
|
||||||
|
**When** 用户点击第2页
|
||||||
|
**Then** 页面应显示第11-20条记录
|
||||||
|
|
||||||
|
#### Scenario: 搜索筛选区默认展开
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**When** 用户访问员工信息管理页面
|
||||||
|
**Then** 搜索筛选区应默认显示
|
||||||
|
**And** 搜索筛选区应包含:姓名输入框、柜员号输入框、所属部门选择器、身份证号输入框、状态下拉框
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL支持多条件搜索员工
|
||||||
|
|
||||||
|
前端MUST支持用户通过多种条件组合搜索员工信息。
|
||||||
|
|
||||||
|
#### Scenario: 按姓名模糊搜索
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在姓名为"张三"的员工
|
||||||
|
**When** 用户在姓名输入框输入"张"
|
||||||
|
**And** 点击"搜索"按钮
|
||||||
|
**Then** 列表应仅显示姓名中包含"张"的员工记录
|
||||||
|
**And** 分页控件应显示匹配的记录总数
|
||||||
|
|
||||||
|
#### Scenario: 按柜员号精确搜索
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在柜员号为"001"的员工
|
||||||
|
**When** 用户在柜员号输入框输入"001"
|
||||||
|
**And** 点击"搜索"按钮
|
||||||
|
**Then** 列表应仅显示柜员号为"001"的员工记录
|
||||||
|
|
||||||
|
#### Scenario: 按状态筛选
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在在职和离职两种状态的员工
|
||||||
|
**When** 用户在状态下拉框选择"在职"
|
||||||
|
**And** 点击"搜索"按钮
|
||||||
|
**Then** 列表应仅显示状态为"在职"的员工记录
|
||||||
|
|
||||||
|
#### Scenario: 按所属部门筛选
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**And** 系统中存在多个部门的员工
|
||||||
|
**When** 用户在所属部门选择器中选择"研发部门"
|
||||||
|
**And** 点击"搜索"按钮
|
||||||
|
**Then** 列表应仅显示该部门的员工记录
|
||||||
|
|
||||||
|
#### Scenario: 组合条件搜索
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:list` 权限
|
||||||
|
**When** 用户同时输入姓名"张"、柜员号"001"
|
||||||
|
**And** 点击"搜索"按钮
|
||||||
|
**Then** 列表应仅显示同时满足所有条件的记录
|
||||||
|
|
||||||
|
#### Scenario: 重置搜索条件
|
||||||
|
|
||||||
|
**Given** 用户已输入多个搜索条件并执行了搜索
|
||||||
|
**When** 用户点击"重置"按钮
|
||||||
|
**Then** 所有搜索条件输入框应清空
|
||||||
|
**And** 列表应显示所有员工记录(恢复默认)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL提供新增员工弹窗
|
||||||
|
|
||||||
|
前端MUST提供新增员工弹窗,允许用户输入员工基本信息并添加亲属信息。
|
||||||
|
|
||||||
|
#### Scenario: 点击新增按钮打开弹窗
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:add` 权限
|
||||||
|
**When** 用户点击"新增"按钮
|
||||||
|
**Then** 应打开新增员工弹窗
|
||||||
|
**And** 弹窗标题为"添加员工"
|
||||||
|
**And** 所有表单字段应为空
|
||||||
|
**And** 亲属列表应为空
|
||||||
|
|
||||||
|
#### Scenario: 填写员工基本信息
|
||||||
|
|
||||||
|
**Given** 新增员工弹窗已打开
|
||||||
|
**When** 用户填写姓名为"张三"
|
||||||
|
**And** 填写柜员号为"001"
|
||||||
|
**And** 在所属部门选择器中选择"研发部门"
|
||||||
|
**And** 填写身份证号为"110101199001011234"
|
||||||
|
**And** 填写电话为"13800138000"
|
||||||
|
**And** 选择入职时间为"2020-01-01"
|
||||||
|
**And** 选择状态为"在职"
|
||||||
|
**Then** 表单应正确显示填写的信息
|
||||||
|
|
||||||
|
#### Scenario: 添加单个亲属
|
||||||
|
|
||||||
|
**Given** 新增员工弹窗已打开
|
||||||
|
**And** 用户已填写员工基本信息
|
||||||
|
**When** 用户点击"+ 添加亲属"按钮
|
||||||
|
**Then** 亲属列表应新增一个空行
|
||||||
|
**When** 用户填写亲属姓名为"李四"
|
||||||
|
**And** 填写亲属身份证号为"110101199001011235"
|
||||||
|
**And** 填写亲属电话为"13800138001"
|
||||||
|
**And** 选择关系为"配偶"
|
||||||
|
**Then** 亲属列表应显示该亲属信息
|
||||||
|
|
||||||
|
#### Scenario: 添加多个亲属
|
||||||
|
|
||||||
|
**Given** 新增员工弹窗已打开
|
||||||
|
**And** 用户已填写员工基本信息
|
||||||
|
**When** 用户点击3次"+ 添加亲属"按钮
|
||||||
|
**And** 填写3个亲属的信息
|
||||||
|
**Then** 亲属列表应显示3个亲属
|
||||||
|
**And** 每个亲属应有独立的删除按钮
|
||||||
|
|
||||||
|
#### Scenario: 删除亲属
|
||||||
|
|
||||||
|
**Given** 新增员工弹窗已打开
|
||||||
|
**And** 亲属列表中存在2个亲属
|
||||||
|
**When** 用户点击第一个亲属的"删除"按钮
|
||||||
|
**Then** 亲属列表应仅显示1个亲属
|
||||||
|
**And** 被删除的亲属应从列表中移除
|
||||||
|
|
||||||
|
#### Scenario: 提交成功
|
||||||
|
|
||||||
|
**Given** 新增员工弹窗已打开
|
||||||
|
**And** 用户已填写所有必填信息
|
||||||
|
**When** 用户点击"确定"按钮
|
||||||
|
**Then** 系统应发送请求到后端
|
||||||
|
**And** 弹窗应关闭
|
||||||
|
**And** 应显示"新增成功"的提示消息
|
||||||
|
**And** 列表应刷新并显示新增的记录
|
||||||
|
|
||||||
|
#### Scenario: 表单验证-姓名为空
|
||||||
|
|
||||||
|
**Given** 新增员工弹窗已打开
|
||||||
|
**When** 用户不填写姓名
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应显示"姓名不能为空"的错误提示
|
||||||
|
**And** 弹窗应保持打开状态
|
||||||
|
**And** 不发送请求到后端
|
||||||
|
|
||||||
|
#### Scenario: 表单验证-身份证号格式错误
|
||||||
|
|
||||||
|
**Given** 新增员工弹窗已打开
|
||||||
|
**When** 用户填写身份证号为"123"
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应显示"身份证号格式不正确"的错误提示
|
||||||
|
**And** 弹窗应保持打开状态
|
||||||
|
|
||||||
|
#### Scenario: 亲属姓名为空验证
|
||||||
|
|
||||||
|
**Given** 新增员工弹窗已打开
|
||||||
|
**And** 用户已添加一个亲属但未填写亲属姓名
|
||||||
|
**When** 用户点击"确定"按钮
|
||||||
|
**Then** 系统应显示"亲属姓名不能为空"的错误提示
|
||||||
|
|
||||||
|
#### Scenario: 取消新增
|
||||||
|
|
||||||
|
**Given** 新增员工弹窗已打开
|
||||||
|
**And** 用户已填写部分信息
|
||||||
|
**When** 用户点击"取消"按钮
|
||||||
|
**Then** 弹窗应关闭
|
||||||
|
**And** 不保存任何数据
|
||||||
|
**And** 列表应保持原状态
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL提供编辑员工弹窗
|
||||||
|
|
||||||
|
前端MUST提供编辑员工弹窗,允许用户修改员工信息和亲属信息。
|
||||||
|
|
||||||
|
#### Scenario: 点击编辑按钮打开弹窗
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:edit` 权限
|
||||||
|
**And** 列表中存在一条员工记录
|
||||||
|
**When** 用户点击该记录的"编辑"按钮
|
||||||
|
**Then** 应打开编辑员工弹窗
|
||||||
|
**And** 弹窗标题为"修改员工"
|
||||||
|
**And** 表单应显示该员工的现有信息
|
||||||
|
**And** 亲属列表应显示该员工的所有亲属
|
||||||
|
|
||||||
|
#### Scenario: 修改员工基本信息
|
||||||
|
|
||||||
|
**Given** 编辑员工弹窗已打开
|
||||||
|
**When** 用户修改姓名为"李四"
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应发送更新请求到后端
|
||||||
|
**And** 弹窗应关闭
|
||||||
|
**And** 应显示"修改成功"的提示消息
|
||||||
|
**And** 列表应刷新并显示更新后的姓名
|
||||||
|
|
||||||
|
#### Scenario: 编辑时新增亲属
|
||||||
|
|
||||||
|
**Given** 编辑员工弹窗已打开
|
||||||
|
**And** 该员工当前有1个亲属
|
||||||
|
**When** 用户点击"+ 添加亲属"按钮
|
||||||
|
**And** 填写新亲属信息
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应发送更新请求到后端
|
||||||
|
**And** 再次打开编辑弹窗时应显示2个亲属
|
||||||
|
|
||||||
|
#### Scenario: 编辑时删除亲属
|
||||||
|
|
||||||
|
**Given** 编辑员工弹窗已打开
|
||||||
|
**And** 该员工当前有2个亲属
|
||||||
|
**When** 用户点击其中一个亲属的"删除"按钮
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应发送更新请求到后端
|
||||||
|
**And** 再次打开编辑弹窗时应仅显示1个亲属
|
||||||
|
|
||||||
|
#### Scenario: 编辑时修改亲属信息
|
||||||
|
|
||||||
|
**Given** 编辑员工弹窗已打开
|
||||||
|
**And** 该员工有亲属"李四"
|
||||||
|
**When** 用户修改亲属姓名为"王五"
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应发送更新请求到后端
|
||||||
|
**And** 再次打开编辑弹窗时应显示亲属姓名为"王五"
|
||||||
|
|
||||||
|
#### Scenario: 编辑时清除所有亲属
|
||||||
|
|
||||||
|
**Given** 编辑员工弹窗已打开
|
||||||
|
**And** 该员工有亲属信息
|
||||||
|
**When** 用户逐个删除所有亲属
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应发送更新请求到后端
|
||||||
|
**And** 再次打开编辑弹窗时亲属列表应为空
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL提供员工详情弹窗
|
||||||
|
|
||||||
|
前端MUST提供员工详情弹窗,以只读方式展示员工完整信息和亲属列表。
|
||||||
|
|
||||||
|
#### Scenario: 点击详情按钮打开弹窗
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:query` 权限
|
||||||
|
**And** 列表中存在一条员工记录
|
||||||
|
**When** 用户点击该记录的"详情"按钮
|
||||||
|
**Then** 应打开员工详情弹窗
|
||||||
|
**And** 弹窗标题为"员工详情"
|
||||||
|
**And** 所有字段应为只读状态
|
||||||
|
|
||||||
|
#### Scenario: 显示员工基本信息
|
||||||
|
|
||||||
|
**Given** 员工详情弹窗已打开
|
||||||
|
**Then** 应显示以下信息:姓名、柜员号、所属部门、身份证号、电话、入职时间、状态、创建时间
|
||||||
|
**And** 状态应显示为"在职"或"离职"的文字描述
|
||||||
|
**And** 所有字段应不可编辑
|
||||||
|
|
||||||
|
#### Scenario: 显示亲属列表
|
||||||
|
|
||||||
|
**Given** 员工详情弹窗已打开
|
||||||
|
**And** 该员工有2个亲属
|
||||||
|
**Then** 亲属列表应显示2个亲属
|
||||||
|
**And** 每个亲属应显示:亲属姓名、亲属身份证号、亲属电话、与员工关系
|
||||||
|
**And** 亲属列表应不可编辑
|
||||||
|
|
||||||
|
#### Scenario: 无亲属时显示提示
|
||||||
|
|
||||||
|
**Given** 员工详情弹窗已打开
|
||||||
|
**And** 该员工无亲属信息
|
||||||
|
**Then** 亲属列表区域应显示"暂无亲属信息"的提示
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL支持删除员工
|
||||||
|
|
||||||
|
前端MUST支持删除员工操作,包括单个删除和批量删除。
|
||||||
|
|
||||||
|
#### Scenario: 删除单条记录
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:remove` 权限
|
||||||
|
**And** 列表中存在一条员工记录
|
||||||
|
**When** 用户点击该记录的"删除"按钮
|
||||||
|
**Then** 应弹出确认对话框,提示是否确认删除
|
||||||
|
**When** 用户确认删除
|
||||||
|
**Then** 系统应发送删除请求到后端
|
||||||
|
**And** 应显示"删除成功"的提示消息
|
||||||
|
**And** 列表应刷新,该记录应从列表中移除
|
||||||
|
|
||||||
|
#### Scenario: 取消删除
|
||||||
|
|
||||||
|
**Given** 用户已点击"删除"按钮
|
||||||
|
**And** 确认对话框已显示
|
||||||
|
**When** 用户点击"取消"按钮
|
||||||
|
**Then** 对话框应关闭
|
||||||
|
**And** 不发送删除请求
|
||||||
|
**And** 记录应保留在列表中
|
||||||
|
|
||||||
|
#### Scenario: 批量删除
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:remove` 权限
|
||||||
|
**And** 列表中存在多条员工记录
|
||||||
|
**When** 用户勾选3条记录
|
||||||
|
**And** 点击"删除"按钮
|
||||||
|
**And** 确认删除
|
||||||
|
**Then** 系统应发送批量删除请求到后端
|
||||||
|
**And** 应显示"删除成功"的提示消息
|
||||||
|
**And** 列表应刷新,被删除的记录应从列表中移除
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL支持导入员工数据
|
||||||
|
|
||||||
|
前端MUST支持通过 Excel 文件批量导入员工数据。
|
||||||
|
|
||||||
|
#### Scenario: 点击导入按钮打开导入弹窗
|
||||||
|
|
||||||
|
**Given** 用户已登录系统且具有 `dpc:employee:import` 权限
|
||||||
|
**When** 用户点击"导入"按钮
|
||||||
|
**Then** 应打开导入数据弹窗
|
||||||
|
**And** 弹窗标题为"员工数据导入"
|
||||||
|
**And** 应显示文件上传区域
|
||||||
|
|
||||||
|
#### Scenario: 上传Excel文件
|
||||||
|
|
||||||
|
**Given** 导入弹窗已打开
|
||||||
|
**When** 用户选择一个 Excel 文件
|
||||||
|
**And** 点击"确定"按钮
|
||||||
|
**Then** 系统应上传文件到后端
|
||||||
|
**And** 应显示上传进度
|
||||||
|
**And** 上传完成后应显示导入结果消息
|
||||||
|
|
||||||
|
#### Scenario: 导入成功提示
|
||||||
|
|
||||||
|
**Given** 用户上传了一个包含有效数据的 Excel 文件
|
||||||
|
**When** 文件上传完成
|
||||||
|
**Then** 应显示导入成功消息,包含成功导入的记录数
|
||||||
|
**And** 弹窗应自动关闭
|
||||||
|
**And** 列表应刷新,显示新导入的记录
|
||||||
|
|
||||||
|
#### Scenario: 导入部分失败提示
|
||||||
|
|
||||||
|
**Given** 用户上传了一个包含10条数据的 Excel 文件
|
||||||
|
**And** 其中2条数据格式错误
|
||||||
|
**When** 文件上传完成
|
||||||
|
**Then** 应显示导入结果消息,说明成功8条,失败2条
|
||||||
|
**And** 消息中应包含失败行的错误详情
|
||||||
|
|
||||||
|
#### Scenario: 更新已存在数据选项
|
||||||
|
|
||||||
|
**Given** 导入弹窗已打开
|
||||||
|
**When** 用户勾选"是否更新已经存在的员工数据"选项
|
||||||
|
**And** 上传文件
|
||||||
|
**Then** 请求应携带 `updateSupport=true` 参数
|
||||||
|
|
||||||
|
#### Scenario: 下载导入模板
|
||||||
|
|
||||||
|
**Given** 导入弹窗已打开
|
||||||
|
**When** 用户点击"下载模板"链接
|
||||||
|
**Then** 浏览器应下载 Excel 模板文件
|
||||||
|
**And** 导入弹窗应保持打开
|
||||||
|
|
||||||
|
#### Scenario: 支持拖拽上传
|
||||||
|
|
||||||
|
**Given** 导入弹窗已打开
|
||||||
|
**When** 用户将 Excel 文件拖拽到上传区域
|
||||||
|
**Then** 文件应自动添加到上传队列
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL根据权限控制按钮显示
|
||||||
|
|
||||||
|
前端MUST根据用户权限显示或隐藏相应的操作按钮。
|
||||||
|
|
||||||
|
#### 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:import` 权限
|
||||||
|
**When** 用户访问员工信息管理页面
|
||||||
|
**Then** "导入"按钮应隐藏或禁用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL提供友好的用户反馈
|
||||||
|
|
||||||
|
前端MUST在各种操作中提供适当的用户反馈。
|
||||||
|
|
||||||
|
#### Scenario: 加载状态显示
|
||||||
|
|
||||||
|
**Given** 用户执行任何需要请求后端的操作
|
||||||
|
**When** 请求正在处理中
|
||||||
|
**Then** 列表或弹窗应显示加载状态(如 loading 遮罩)
|
||||||
|
|
||||||
|
#### Scenario: 操作成功提示
|
||||||
|
|
||||||
|
**Given** 用户执行新增、修改或删除操作
|
||||||
|
**When** 操作成功
|
||||||
|
**Then** 应显示成功提示消息
|
||||||
|
**And** 提示消息应在几秒后自动消失
|
||||||
|
|
||||||
|
#### Scenario: 操作失败提示
|
||||||
|
|
||||||
|
**Given** 用户执行新增、修改或删除操作
|
||||||
|
**When** 操作失败
|
||||||
|
**Then** 应显示错误提示消息
|
||||||
|
**And** 错误消息应说明失败原因
|
||||||
|
|
||||||
|
#### Scenario: 网络错误处理
|
||||||
|
|
||||||
|
**Given** 用户执行任何需要请求后端的操作
|
||||||
|
**When** 网络请求失败
|
||||||
|
**Then** 应显示网络错误提示消息
|
||||||
|
**And** 弹窗应保持打开状态(如果是新增/编辑操作)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Requirement: 前端SHALL支持响应式布局
|
||||||
|
|
||||||
|
前端MUST确保页面在不同屏幕尺寸下正常显示。
|
||||||
|
|
||||||
|
#### Scenario: 小屏幕适配
|
||||||
|
|
||||||
|
**Given** 用户使用小屏幕设备访问页面
|
||||||
|
**When** 页面加载完成
|
||||||
|
**Then** 搜索筛选区应合理布局
|
||||||
|
**And** 表格应支持横向滚动
|
||||||
|
**And** 弹窗应适配屏幕尺寸
|
||||||
|
|
||||||
|
#### Scenario: 大屏幕优化
|
||||||
|
|
||||||
|
**Given** 用户使用大屏幕设备访问页面
|
||||||
|
**When** 页面加载完成
|
||||||
|
**Then** 搜索筛选区应在一行内显示
|
||||||
|
**And** 表格应充分利用屏幕宽度
|
||||||
291
openspec/changes/add-employee-info-ui/tasks.md
Normal file
291
openspec/changes/add-employee-info-ui/tasks.md
Normal file
@@ -0,0 +1,291 @@
|
|||||||
|
# Tasks: 员工信息管理前端页面
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
本文档将员工信息管理前端页面的开发工作分解为可验证的任务项。任务按照依赖关系排序,确保开发过程顺畅。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. API 接口层开发
|
||||||
|
|
||||||
|
### 1.1 创建 API 接口文件
|
||||||
|
- [ ] 创建 `ruoyi-ui/src/api/dpcEmployee.js` 文件
|
||||||
|
- [ ] 实现 `listEmployee(query)` - 查询员工列表
|
||||||
|
- [ ] 实现 `getEmployee(employeeId)` - 获取员工详情
|
||||||
|
- [ ] 实现 `addEmployee(data)` - 新增员工
|
||||||
|
- [ ] 实现 `updateEmployee(data)` - 编辑员工
|
||||||
|
- [ ] 实现 `delEmployee(employeeIds)` - 删除员工
|
||||||
|
- [ ] 实现 `importTemplate()` - 下载导入模板
|
||||||
|
- [ ] 实现 `importData(data, updateSupport)` - 导入员工信息
|
||||||
|
|
||||||
|
**验证**: API 文件包含所有7个接口方法,路径与后端 API 文档一致。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 主页面组件开发
|
||||||
|
|
||||||
|
### 2.1 创建主页面文件
|
||||||
|
- [ ] 创建 `ruoyi-ui/src/views/dpcEmployee/` 目录
|
||||||
|
- [ ] 创建 `ruoyi-ui/src/views/dpcEmployee/index.vue` 文件
|
||||||
|
|
||||||
|
**验证**: 文件创建成功,可正常访问。
|
||||||
|
|
||||||
|
### 2.2 实现页面基础结构
|
||||||
|
- [ ] 实现 `<template>` 基础布局
|
||||||
|
- [ ] 实现搜索筛选区的表单(含部门树选择器)
|
||||||
|
- [ ] 实现操作按钮区(新增、导入)
|
||||||
|
- [ ] 实现员工列表表格
|
||||||
|
- [ ] 实现分页组件
|
||||||
|
|
||||||
|
**验证**: 页面可正常渲染,布局符合设计文档。
|
||||||
|
|
||||||
|
### 2.3 实现数据管理逻辑
|
||||||
|
- [ ] 定义 `data()` 数据结构
|
||||||
|
- `loading` - 加载状态
|
||||||
|
- `employeeList` - 员工列表数据
|
||||||
|
- `total` - 总记录数
|
||||||
|
- `queryParams` - 查询参数
|
||||||
|
- `showSearch` - 搜索区显示状态
|
||||||
|
- [ ] 实现 `getList()` 方法 - 查询员工列表
|
||||||
|
- [ ] 实现 `handleQuery()` 方法 - 搜索
|
||||||
|
- [ ] 实现 `resetQuery()` 方法 - 重置搜索条件
|
||||||
|
|
||||||
|
**验证**: 页面加载时正确显示员工列表,搜索和重置功能正常。
|
||||||
|
|
||||||
|
### 2.4 实现列表表格
|
||||||
|
- [ ] 添加表格列:复选框、姓名、柜员号、身份证号、所属部门、电话、状态、操作
|
||||||
|
- [ ] 实现状态标签显示(在职/离职)
|
||||||
|
- [ ] 实现操作列按钮:详情、编辑、删除
|
||||||
|
- [ ] 实现 `handleSelectionChange()` 方法 - 多选处理
|
||||||
|
|
||||||
|
**验证**: 列表正确显示所有字段,状态标签正确着色。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 新增/编辑弹窗开发
|
||||||
|
|
||||||
|
### 3.1 实现弹窗结构
|
||||||
|
- [ ] 添加新增/编辑弹窗的 `<el-dialog>`
|
||||||
|
- [ ] 实现弹窗标题动态显示
|
||||||
|
- [ ] 实现员工基本信息表单
|
||||||
|
- 姓名输入框(必填,最大100字符)
|
||||||
|
- 柜员号输入框(必填,最大50字符)
|
||||||
|
- 所属部门选择器(el-tree-select,关联 sys_dept)
|
||||||
|
- 身份证号输入框(必填,18位校验)
|
||||||
|
- 电话输入框(选填,11位手机号校验)
|
||||||
|
- 入职时间日期选择器
|
||||||
|
- 状态单选框组(在职/离职)
|
||||||
|
|
||||||
|
**验证**: 弹窗可正常打开和关闭,表单字段正确显示。
|
||||||
|
|
||||||
|
### 3.2 实现亲属子表单
|
||||||
|
- [ ] 添加亲属信息区域
|
||||||
|
- [ ] 实现"+ 添加亲属"按钮
|
||||||
|
- [ ] 实现亲属列表表格(亲属姓名、身份证号、电话、关系、操作)
|
||||||
|
- [ ] 实现亲属行的删除按钮
|
||||||
|
- [ ] 添加 `relatives` 数据字段
|
||||||
|
|
||||||
|
**验证**: 可以动态添加和删除亲属行。
|
||||||
|
|
||||||
|
### 3.3 实现表单验证
|
||||||
|
- [ ] 定义 `rules` 验证规则
|
||||||
|
- 姓名:必填,最大100字符
|
||||||
|
- 柜员号:必填,最大50字符
|
||||||
|
- 身份证号:必填,格式校验(18位)
|
||||||
|
- 电话:选填,格式校验(11位手机号)
|
||||||
|
- 亲属姓名:必填
|
||||||
|
- [ ] 实现 `reset()` 方法 - 重置表单
|
||||||
|
- [ ] 实现 `cancel()` 方法 - 取消操作
|
||||||
|
|
||||||
|
**验证**: 表单验证规则正确触发,错误提示显示正确。
|
||||||
|
|
||||||
|
### 3.4 实现新增逻辑
|
||||||
|
- [ ] 实现 `handleAdd()` 方法 - 打开新增弹窗
|
||||||
|
- [ ] 实现 `submitForm()` 方法 - 提交新增
|
||||||
|
- [ ] 集成 `addEmployee` API
|
||||||
|
- [ ] 处理成功/失败响应
|
||||||
|
|
||||||
|
**验证**: 可以成功新增员工及其亲属信息。
|
||||||
|
|
||||||
|
### 3.5 实现编辑逻辑
|
||||||
|
- [ ] 实现 `handleUpdate(row)` 方法 - 打开编辑弹窗
|
||||||
|
- [ ] 集成 `getEmployee` API 获取详情
|
||||||
|
- [ ] 回显员工信息和亲属列表
|
||||||
|
- [ ] 修改 `submitForm()` 方法处理编辑提交
|
||||||
|
- [ ] 集成 `updateEmployee` API
|
||||||
|
|
||||||
|
**验证**: 可以成功编辑员工信息和亲属信息,数据正确回显。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 详情弹窗开发
|
||||||
|
|
||||||
|
### 4.1 实现详情弹窗结构
|
||||||
|
- [ ] 添加详情弹窗的 `<el-dialog>`
|
||||||
|
- [ ] 实现员工基本信息展示区(只读)
|
||||||
|
- [ ] 实现亲属列表展示区(只读)
|
||||||
|
- [ ] 实现"暂无亲属信息"提示
|
||||||
|
|
||||||
|
**验证**: 详情弹窗正确显示员工信息和亲属列表。
|
||||||
|
|
||||||
|
### 4.2 实现详情查看逻辑
|
||||||
|
- [ ] 添加 `detailOpen` 数据字段
|
||||||
|
- [ ] 添加 `employeeDetail` 数据字段
|
||||||
|
- [ ] 实现 `handleDetail(row)` 方法
|
||||||
|
- [ ] 集成 `getEmployee` API
|
||||||
|
|
||||||
|
**验证**: 点击详情按钮可正确查看员工完整信息。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 删除功能开发
|
||||||
|
|
||||||
|
### 5.1 实现删除逻辑
|
||||||
|
- [ ] 实现 `handleDelete(row)` 方法
|
||||||
|
- [ ] 添加删除确认对话框
|
||||||
|
- [ ] 集成 `delEmployee` API
|
||||||
|
- [ ] 处理成功/失败响应
|
||||||
|
- [ ] 删除后刷新列表
|
||||||
|
|
||||||
|
**验证**: 可以成功删除员工记录,删除确认对话框正常显示。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 导入功能开发
|
||||||
|
|
||||||
|
### 6.1 实现导入弹窗
|
||||||
|
- [ ] 添加导入弹窗的 `<el-dialog>`
|
||||||
|
- [ ] 实现 `<el-upload>` 组件
|
||||||
|
- [ ] 配置上传参数(headers, action, 限制文件类型)
|
||||||
|
- [ ] 实现"更新已存在数据"复选框
|
||||||
|
- [ ] 实现"下载模板"链接
|
||||||
|
|
||||||
|
**验证**: 导入弹窗正常显示,可以正确选择文件。
|
||||||
|
|
||||||
|
### 6.2 实现导入逻辑
|
||||||
|
- [ ] 添加 `upload` 数据对象
|
||||||
|
- [ ] 实现 `handleImport()` 方法 - 打开导入弹窗
|
||||||
|
- [ ] 实现 `importTemplate()` 方法 - 下载模板
|
||||||
|
- [ ] 实现 `handleFileUploadProgress()` - 上传进度
|
||||||
|
- [ ] 实现 `handleFileSuccess()` - 上传成功处理
|
||||||
|
- [ ] 实现 `submitFileForm()` - 提交文件
|
||||||
|
|
||||||
|
**验证**: 可以成功下载模板,上传文件后正确显示导入结果。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. 权限控制
|
||||||
|
|
||||||
|
### 7.1 实现按钮权限控制
|
||||||
|
- [ ] 新增按钮添加 `v-hasPermi="['dpc:employee:add']"`
|
||||||
|
- [ ] 编辑按钮添加 `v-hasPermi="['dpc:employee:edit']"`
|
||||||
|
- [ ] 删除按钮添加 `v-hasPermi="['dpc:employee:remove']"`
|
||||||
|
- [ ] 详情按钮添加 `v-hasPermi="['dpc:employee:query']"`
|
||||||
|
- [ ] 导入按钮添加 `v-hasPermi="['dpc:employee:import']"`
|
||||||
|
|
||||||
|
**验证**: 按钮根据用户权限正确显示或隐藏。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 样式和交互优化
|
||||||
|
|
||||||
|
### 9.1 实现加载状态
|
||||||
|
- [ ] 列表加载时显示 loading 遮罩
|
||||||
|
- [ ] 提交表单时禁用提交按钮
|
||||||
|
- [ ] 文件上传时显示上传进度
|
||||||
|
|
||||||
|
**验证**: 各种加载状态正确显示。
|
||||||
|
|
||||||
|
### 9.2 实现用户反馈
|
||||||
|
- [ ] 操作成功显示成功提示
|
||||||
|
- [ ] 操作失败显示错误提示
|
||||||
|
- [ ] 导入结果显示详细信息
|
||||||
|
|
||||||
|
**验证**: 用户反馈消息清晰友好。
|
||||||
|
|
||||||
|
### 9.3 响应式布局
|
||||||
|
- [ ] 搜索筛选区响应式布局
|
||||||
|
- [ ] 表格支持横向滚动
|
||||||
|
- [ ] 弹窗适配小屏幕
|
||||||
|
|
||||||
|
**验证**: 页面在不同屏幕尺寸下正常显示。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 测试和修复
|
||||||
|
|
||||||
|
### 9.1 功能测试
|
||||||
|
- [ ] 测试列表查询和分页
|
||||||
|
- [ ] 测试搜索和筛选功能
|
||||||
|
- [ ] 测试新增员工(无亲属)
|
||||||
|
- [ ] 测试新增员工(单个亲属)
|
||||||
|
- [ ] 测试新增员工(多个亲属)
|
||||||
|
- [ ] 测试编辑员工基本信息
|
||||||
|
- [ ] 测试编辑员工亲属(新增、修改、删除)
|
||||||
|
- [ ] 测试查看详情
|
||||||
|
- [ ] 测试删除员工(单个)
|
||||||
|
- [ ] 测试批量删除
|
||||||
|
- [ ] 测试下载导入模板
|
||||||
|
- [ ] 测试导入数据(成功场景)
|
||||||
|
- [ ] 测试导入数据(部分失败场景)
|
||||||
|
- [ ] 测试表单验证(各种错误场景)
|
||||||
|
|
||||||
|
### 9.2 浏览器兼容性测试
|
||||||
|
- [ ] Chrome 测试
|
||||||
|
- [ ] Firefox 测试
|
||||||
|
- [ ] Edge 测试
|
||||||
|
|
||||||
|
### 9.3 Bug 修复
|
||||||
|
- [ ] 修复测试中发现的问题
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 菜单配置
|
||||||
|
|
||||||
|
### 11.1 数据库菜单配置
|
||||||
|
- [ ] 在 `sys_menu` 表中插入"信息维护"父菜单(如果不存在)
|
||||||
|
- [ ] 在 `sys_menu` 表中插入"员工信息管理"子菜单
|
||||||
|
- [ ] 配置菜单路由为 `dpcEmployee`
|
||||||
|
- [ ] 配置菜单组件为 `dpcEmployee/index`
|
||||||
|
- [ ] 配置菜单权限标识
|
||||||
|
- [ ] 配置菜单图标
|
||||||
|
|
||||||
|
**验证**: 菜单正确显示,点击可跳转到员工信息管理页面。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 依赖关系
|
||||||
|
|
||||||
|
```
|
||||||
|
1. API 接口层 (1.1)
|
||||||
|
↓
|
||||||
|
2. 主页面组件基础 (2.1 - 2.4)
|
||||||
|
↓
|
||||||
|
3. 新增/编辑弹窗 (3.1 - 3.5) ← 并行
|
||||||
|
4. 详情弹窗 (4.1 - 4.2) ← 并行
|
||||||
|
5. 删除功能 (5.1) ← 并行
|
||||||
|
6. 导入功能 (6.1 - 6.2) ← 并行
|
||||||
|
↓
|
||||||
|
7. 权限控制 (7.1)
|
||||||
|
↓
|
||||||
|
8. 样式优化 (8.1 - 8.3)
|
||||||
|
↓
|
||||||
|
9. 测试和修复 (9.1 - 9.3)
|
||||||
|
↓
|
||||||
|
10. 菜单配置 (10.1)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 预估工作量
|
||||||
|
|
||||||
|
| 任务组 | 预估时间 |
|
||||||
|
|--------|----------|
|
||||||
|
| API 接口层 | 0.5 小时 |
|
||||||
|
| 主页面组件 | 1.5 小时 |
|
||||||
|
| 新增/编辑弹窗 | 3 小时 |
|
||||||
|
| 详情弹窗 | 1 小时 |
|
||||||
|
| 删除功能 | 0.5 小时 |
|
||||||
|
| 导入功能 | 1.5 小时 |
|
||||||
|
| 权限控制 | 0.5 小时 |
|
||||||
|
| 样式优化 | 1 小时 |
|
||||||
|
| 测试和修复 | 1 小时 |
|
||||||
|
| 菜单配置 | 0.5 小时 |
|
||||||
|
| **总计** | **11 小时** |
|
||||||
@@ -143,7 +143,7 @@ public class DpcEmployeeController extends BaseController {
|
|||||||
DpcEmployeeAddDTO dto = new DpcEmployeeAddDTO();
|
DpcEmployeeAddDTO dto = new DpcEmployeeAddDTO();
|
||||||
dto.setName(entity.getName());
|
dto.setName(entity.getName());
|
||||||
dto.setTellerNo(entity.getTellerNo());
|
dto.setTellerNo(entity.getTellerNo());
|
||||||
dto.setOrgNo(entity.getOrgNo());
|
dto.setDeptId(entity.getDeptId());
|
||||||
dto.setIdCard(entity.getIdCard());
|
dto.setIdCard(entity.getIdCard());
|
||||||
dto.setPhone(entity.getPhone());
|
dto.setPhone(entity.getPhone());
|
||||||
dto.setHireDate(entity.getHireDate());
|
dto.setHireDate(entity.getHireDate());
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ public class DpcEmployee implements Serializable {
|
|||||||
@Excel(name = "柜员号")
|
@Excel(name = "柜员号")
|
||||||
private String tellerNo;
|
private String tellerNo;
|
||||||
|
|
||||||
/** 所属机构号 */
|
/** 所属部门ID */
|
||||||
@Excel(name = "所属机构号")
|
@Excel(name = "所属部门ID")
|
||||||
private String orgNo;
|
private Long deptId;
|
||||||
|
|
||||||
/** 身份证号 */
|
/** 身份证号 */
|
||||||
@Excel(name = "身份证号")
|
@Excel(name = "身份证号")
|
||||||
|
|||||||
@@ -35,10 +35,9 @@ public class DpcEmployeeAddDTO implements Serializable {
|
|||||||
@Size(max = 50, message = "柜员号长度不能超过50个字符")
|
@Size(max = 50, message = "柜员号长度不能超过50个字符")
|
||||||
private String tellerNo;
|
private String tellerNo;
|
||||||
|
|
||||||
/** 所属机构号 */
|
/** 所属部门ID */
|
||||||
@Excel(name = "所属机构号")
|
@Excel(name = "所属部门ID")
|
||||||
@Size(max = 50, message = "所属机构号长度不能超过50个字符")
|
private Long deptId;
|
||||||
private String orgNo;
|
|
||||||
|
|
||||||
/** 身份证号 */
|
/** 身份证号 */
|
||||||
@Excel(name = "身份证号")
|
@Excel(name = "身份证号")
|
||||||
|
|||||||
@@ -37,10 +37,9 @@ public class DpcEmployeeEditDTO implements Serializable {
|
|||||||
@Size(max = 50, message = "柜员号长度不能超过50个字符")
|
@Size(max = 50, message = "柜员号长度不能超过50个字符")
|
||||||
private String tellerNo;
|
private String tellerNo;
|
||||||
|
|
||||||
/** 所属机构号 */
|
/** 所属部门ID */
|
||||||
@Excel(name = "所属机构号")
|
@Excel(name = "所属部门ID")
|
||||||
@Size(max = 50, message = "所属机构号长度不能超过50个字符")
|
private Long deptId;
|
||||||
private String orgNo;
|
|
||||||
|
|
||||||
/** 身份证号 */
|
/** 身份证号 */
|
||||||
@Excel(name = "身份证号")
|
@Excel(name = "身份证号")
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ public class DpcEmployeeQueryDTO implements Serializable {
|
|||||||
/** 柜员号(精确查询) */
|
/** 柜员号(精确查询) */
|
||||||
private String tellerNo;
|
private String tellerNo;
|
||||||
|
|
||||||
/** 所属机构号 */
|
/** 所属部门ID */
|
||||||
private String orgNo;
|
private Long deptId;
|
||||||
|
|
||||||
/** 身份证号(精确查询) */
|
/** 身份证号(精确查询) */
|
||||||
private String idCard;
|
private String idCard;
|
||||||
|
|||||||
@@ -28,8 +28,11 @@ public class DpcEmployeeVO implements Serializable {
|
|||||||
/** 柜员号 */
|
/** 柜员号 */
|
||||||
private String tellerNo;
|
private String tellerNo;
|
||||||
|
|
||||||
/** 所属机构号 */
|
/** 所属部门ID */
|
||||||
private String orgNo;
|
private Long deptId;
|
||||||
|
|
||||||
|
/** 所属部门名称 */
|
||||||
|
private String deptName;
|
||||||
|
|
||||||
/** 身份证号 */
|
/** 身份证号 */
|
||||||
private String idCard;
|
private String idCard;
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
package com.ruoyi.dpc.mapper;
|
package com.ruoyi.dpc.mapper;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.ruoyi.dpc.domain.DpcEmployee;
|
import com.ruoyi.dpc.domain.DpcEmployee;
|
||||||
|
import com.ruoyi.dpc.domain.dto.DpcEmployeeQueryDTO;
|
||||||
import com.ruoyi.dpc.domain.vo.DpcEmployeeVO;
|
import com.ruoyi.dpc.domain.vo.DpcEmployeeVO;
|
||||||
import org.apache.ibatis.annotations.Param;
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
@@ -13,6 +15,16 @@ import org.apache.ibatis.annotations.Param;
|
|||||||
*/
|
*/
|
||||||
public interface DpcEmployeeMapper extends BaseMapper<DpcEmployee> {
|
public interface DpcEmployeeMapper extends BaseMapper<DpcEmployee> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询员工列表(包含部门名称)
|
||||||
|
*
|
||||||
|
* @param page 分页对象
|
||||||
|
* @param queryDTO 查询条件
|
||||||
|
* @return 员工VO分页结果
|
||||||
|
*/
|
||||||
|
Page<DpcEmployeeVO> selectEmployeePageWithDept(@Param("page") Page<DpcEmployeeVO> page,
|
||||||
|
@Param("query") DpcEmployeeQueryDTO queryDTO);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询员工详情(包含亲属列表)
|
* 查询员工详情(包含亲属列表)
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -63,18 +63,20 @@ public class DpcEmployeeServiceImpl implements IDpcEmployeeService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Page<DpcEmployeeVO> selectEmployeePage(Page<DpcEmployee> page, DpcEmployeeQueryDTO queryDTO) {
|
public Page<DpcEmployeeVO> selectEmployeePage(Page<DpcEmployee> page, DpcEmployeeQueryDTO queryDTO) {
|
||||||
LambdaQueryWrapper<DpcEmployee> wrapper = buildQueryWrapper(queryDTO);
|
// 使用关联查询获取部门名称
|
||||||
Page<DpcEmployee> resultPage = employeeMapper.selectPage(page, wrapper);
|
Page<DpcEmployeeVO> voPage = new Page<>(page.getCurrent(), page.getSize());
|
||||||
|
Page<DpcEmployeeVO> resultPage = employeeMapper.selectEmployeePageWithDept(voPage, queryDTO);
|
||||||
|
|
||||||
// 直接复用resultPage,只转换records
|
// 设置状态描述
|
||||||
Page<DpcEmployeeVO> voPage = new Page<>(resultPage.getCurrent(), resultPage.getSize(), resultPage.getTotal());
|
resultPage.getRecords().forEach(vo -> {
|
||||||
voPage.setRecords(resultPage.getRecords().stream()
|
if ("0".equals(vo.getStatus())) {
|
||||||
.map(this::convertToVO)
|
vo.setStatusDesc("在职");
|
||||||
.collect(Collectors.toList()));
|
} else if ("1".equals(vo.getStatus())) {
|
||||||
// 复制其他分页信息
|
vo.setStatusDesc("离职");
|
||||||
voPage.setPages(resultPage.getPages());
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return voPage;
|
return resultPage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -262,7 +264,7 @@ public class DpcEmployeeServiceImpl implements IDpcEmployeeService {
|
|||||||
LambdaQueryWrapper<DpcEmployee> wrapper = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<DpcEmployee> wrapper = new LambdaQueryWrapper<>();
|
||||||
wrapper.like(StringUtils.isNotEmpty(queryDTO.getName()), DpcEmployee::getName, queryDTO.getName())
|
wrapper.like(StringUtils.isNotEmpty(queryDTO.getName()), DpcEmployee::getName, queryDTO.getName())
|
||||||
.eq(StringUtils.isNotEmpty(queryDTO.getTellerNo()), DpcEmployee::getTellerNo, queryDTO.getTellerNo())
|
.eq(StringUtils.isNotEmpty(queryDTO.getTellerNo()), DpcEmployee::getTellerNo, queryDTO.getTellerNo())
|
||||||
.eq(StringUtils.isNotEmpty(queryDTO.getOrgNo()), DpcEmployee::getOrgNo, queryDTO.getOrgNo())
|
.eq(queryDTO.getDeptId() != null, DpcEmployee::getDeptId, queryDTO.getDeptId())
|
||||||
.eq(StringUtils.isNotEmpty(queryDTO.getIdCard()), DpcEmployee::getIdCard, queryDTO.getIdCard())
|
.eq(StringUtils.isNotEmpty(queryDTO.getIdCard()), DpcEmployee::getIdCard, queryDTO.getIdCard())
|
||||||
.eq(StringUtils.isNotEmpty(queryDTO.getStatus()), DpcEmployee::getStatus, queryDTO.getStatus())
|
.eq(StringUtils.isNotEmpty(queryDTO.getStatus()), DpcEmployee::getStatus, queryDTO.getStatus())
|
||||||
.orderByDesc(DpcEmployee::getCreateTime);
|
.orderByDesc(DpcEmployee::getCreateTime);
|
||||||
|
|||||||
@@ -8,7 +8,8 @@
|
|||||||
<id property="employeeId" column="employee_id"/>
|
<id property="employeeId" column="employee_id"/>
|
||||||
<result property="name" column="name"/>
|
<result property="name" column="name"/>
|
||||||
<result property="tellerNo" column="teller_no"/>
|
<result property="tellerNo" column="teller_no"/>
|
||||||
<result property="orgNo" column="org_no"/>
|
<result property="deptId" column="dept_id"/>
|
||||||
|
<result property="deptName" column="dept_name"/>
|
||||||
<result property="idCard" column="id_card"/>
|
<result property="idCard" column="id_card"/>
|
||||||
<result property="phone" column="phone"/>
|
<result property="phone" column="phone"/>
|
||||||
<result property="hireDate" column="hire_date"/>
|
<result property="hireDate" column="hire_date"/>
|
||||||
@@ -24,9 +25,35 @@
|
|||||||
</collection>
|
</collection>
|
||||||
</resultMap>
|
</resultMap>
|
||||||
|
|
||||||
|
<select id="selectEmployeePageWithDept" resultMap="DpcEmployeeVOResult">
|
||||||
|
SELECT
|
||||||
|
e.employee_id, e.name, e.teller_no, e.dept_id, e.id_card, e.phone, e.hire_date, e.status, e.create_time,
|
||||||
|
d.dept_name
|
||||||
|
FROM dpc_employee e
|
||||||
|
LEFT JOIN sys_dept d ON e.dept_id = d.dept_id
|
||||||
|
<where>
|
||||||
|
<if test="query.name != null and query.name != ''">
|
||||||
|
AND e.name LIKE CONCAT('%', #{query.name}, '%')
|
||||||
|
</if>
|
||||||
|
<if test="query.tellerNo != null and query.tellerNo != ''">
|
||||||
|
AND e.teller_no = #{query.tellerNo}
|
||||||
|
</if>
|
||||||
|
<if test="query.deptId != null">
|
||||||
|
AND e.dept_id = #{query.deptId}
|
||||||
|
</if>
|
||||||
|
<if test="query.idCard != null and query.idCard != ''">
|
||||||
|
AND e.id_card = #{query.idCard}
|
||||||
|
</if>
|
||||||
|
<if test="query.status != null and query.status != ''">
|
||||||
|
AND e.status = #{query.status}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
ORDER BY e.create_time DESC
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="selectEmployeeWithRelatives" parameterType="Long" resultMap="DpcEmployeeVOResult">
|
<select id="selectEmployeeWithRelatives" parameterType="Long" resultMap="DpcEmployeeVOResult">
|
||||||
SELECT
|
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,
|
e.employee_id, e.name, e.teller_no, e.dept_id, 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
|
r.relative_id, r.employee_id, r.relative_name, r.relative_id_card, r.relative_phone, r.relationship
|
||||||
FROM dpc_employee e
|
FROM dpc_employee e
|
||||||
LEFT JOIN dpc_employee_relative r ON e.employee_id = r.employee_id
|
LEFT JOIN dpc_employee_relative r ON e.employee_id = r.employee_id
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ CREATE TABLE `dpc_employee` (
|
|||||||
`employee_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '员工ID',
|
`employee_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '员工ID',
|
||||||
`name` VARCHAR(100) NOT NULL COMMENT '姓名',
|
`name` VARCHAR(100) NOT NULL COMMENT '姓名',
|
||||||
`teller_no` VARCHAR(50) NOT NULL COMMENT '柜员号',
|
`teller_no` VARCHAR(50) NOT NULL COMMENT '柜员号',
|
||||||
`org_no` VARCHAR(50) DEFAULT NULL COMMENT '所属机构号',
|
`dept_id` BIGINT DEFAULT NULL COMMENT '所属部门ID',
|
||||||
`id_card` VARCHAR(18) NOT NULL COMMENT '身份证号',
|
`id_card` VARCHAR(18) NOT NULL COMMENT '身份证号',
|
||||||
`phone` VARCHAR(11) DEFAULT NULL COMMENT '电话',
|
`phone` VARCHAR(11) DEFAULT NULL COMMENT '电话',
|
||||||
`hire_date` DATE DEFAULT NULL COMMENT '入职时间',
|
`hire_date` DATE DEFAULT NULL COMMENT '入职时间',
|
||||||
@@ -27,7 +27,7 @@ CREATE TABLE `dpc_employee` (
|
|||||||
PRIMARY KEY (`employee_id`),
|
PRIMARY KEY (`employee_id`),
|
||||||
UNIQUE KEY `uk_teller_no` (`teller_no`),
|
UNIQUE KEY `uk_teller_no` (`teller_no`),
|
||||||
UNIQUE KEY `uk_id_card` (`id_card`),
|
UNIQUE KEY `uk_id_card` (`id_card`),
|
||||||
KEY `idx_org_no` (`org_no`),
|
KEY `idx_dept_id` (`dept_id`),
|
||||||
KEY `idx_status` (`status`)
|
KEY `idx_status` (`status`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工信息表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='员工信息表';
|
||||||
|
|
||||||
|
|||||||
68
sql/migration/employee_org_no_to_dept_id.sql
Normal file
68
sql/migration/employee_org_no_to_dept_id.sql
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
-- ================================
|
||||||
|
-- 纪检初核系统 - 员工表字段迁移脚本
|
||||||
|
-- 功能: 将 org_no 字段改为 dept_id
|
||||||
|
-- 创建日期: 2026-01-28
|
||||||
|
-- ================================
|
||||||
|
|
||||||
|
-- 备份说明:执行前请先备份数据库
|
||||||
|
|
||||||
|
USE `discipline-prelim-check`;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 步骤1: 删除旧索引(如果存在)
|
||||||
|
-- ----------------------------
|
||||||
|
-- 使用存储过程检查并删除索引
|
||||||
|
DROP PROCEDURE IF EXISTS drop_index_if_exists;
|
||||||
|
DELIMITER //
|
||||||
|
CREATE PROCEDURE drop_index_if_exists()
|
||||||
|
BEGIN
|
||||||
|
DECLARE index_count INT;
|
||||||
|
SELECT COUNT(*) INTO index_count
|
||||||
|
FROM INFORMATION_SCHEMA.STATISTICS
|
||||||
|
WHERE TABLE_SCHEMA = 'discipline-prelim-check'
|
||||||
|
AND TABLE_NAME = 'dpc_employee'
|
||||||
|
AND INDEX_NAME = 'idx_org_no';
|
||||||
|
|
||||||
|
IF index_count > 0 THEN
|
||||||
|
ALTER TABLE `dpc_employee` DROP INDEX `idx_org_no`;
|
||||||
|
END IF;
|
||||||
|
END //
|
||||||
|
DELIMITER ;
|
||||||
|
|
||||||
|
CALL drop_index_if_exists();
|
||||||
|
DROP PROCEDURE IF EXISTS drop_index_if_exists;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 步骤2: 修改字段名和类型
|
||||||
|
-- ----------------------------
|
||||||
|
ALTER TABLE `dpc_employee`
|
||||||
|
CHANGE COLUMN `org_no` `dept_id` BIGINT DEFAULT NULL COMMENT '所属部门ID';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 步骤3: 创建新索引
|
||||||
|
-- ----------------------------
|
||||||
|
CREATE INDEX `idx_dept_id` ON `dpc_employee` (`dept_id`);
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 验证脚本
|
||||||
|
-- ----------------------------
|
||||||
|
-- 检查字段是否修改成功
|
||||||
|
SELECT
|
||||||
|
COLUMN_NAME,
|
||||||
|
DATA_TYPE,
|
||||||
|
COLUMN_TYPE,
|
||||||
|
IS_NULLABLE,
|
||||||
|
COLUMN_COMMENT
|
||||||
|
FROM INFORMATION_SCHEMA.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = 'discipline-prelim-check'
|
||||||
|
AND TABLE_NAME = 'dpc_employee'
|
||||||
|
AND COLUMN_NAME = 'dept_id';
|
||||||
|
|
||||||
|
-- 检查索引是否创建成功
|
||||||
|
SELECT
|
||||||
|
INDEX_NAME,
|
||||||
|
COLUMN_NAME
|
||||||
|
FROM INFORMATION_SCHEMA.STATISTICS
|
||||||
|
WHERE TABLE_SCHEMA = 'discipline-prelim-check'
|
||||||
|
AND TABLE_NAME = 'dpc_employee'
|
||||||
|
AND INDEX_NAME = 'idx_dept_id';
|
||||||
63
test/pagination_test_report_20260128_152606.txt
Normal file
63
test/pagination_test_report_20260128_152606.txt
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
============================================================
|
||||||
|
分页接口总数测试报告
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
测试时间: 2026-01-28 15:26:06
|
||||||
|
|
||||||
|
测试统计:
|
||||||
|
总测试数: 8
|
||||||
|
通过: 0
|
||||||
|
失败: 8
|
||||||
|
错误: 0
|
||||||
|
|
||||||
|
测试接口:
|
||||||
|
1. /dpc/employee/list - 员工列表(MyBatis Plus分页)
|
||||||
|
2. /dpc/intermediary/list - 中介黑名单列表(若依startPage分页)
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
详细结果:
|
||||||
|
------------------------------------------------------------
|
||||||
|
|
||||||
|
测试: 员工列表 - 第1页(10条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: FAIL
|
||||||
|
错误: 响应缺少data字段
|
||||||
|
|
||||||
|
测试: 员工列表 - 第2页(10条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: FAIL
|
||||||
|
错误: 响应缺少data字段
|
||||||
|
|
||||||
|
测试: 员工列表 - 第1页(5条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: FAIL
|
||||||
|
错误: 响应缺少data字段
|
||||||
|
|
||||||
|
测试: 员工列表 - 第1页(20条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: FAIL
|
||||||
|
错误: 响应缺少data字段
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第1页(10条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: FAIL
|
||||||
|
错误: 响应缺少data字段
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第2页(10条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: FAIL
|
||||||
|
错误: 响应缺少data字段
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第1页(5条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: FAIL
|
||||||
|
错误: 响应缺少data字段
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第1页(20条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: FAIL
|
||||||
|
错误: 响应缺少data字段
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
测试结论:
|
||||||
|
✗ 存在分页接口总数返回异常
|
||||||
84
test/pagination_test_report_20260128_152638.txt
Normal file
84
test/pagination_test_report_20260128_152638.txt
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
============================================================
|
||||||
|
分页接口总数测试报告
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
测试时间: 2026-01-28 15:26:38
|
||||||
|
|
||||||
|
测试统计:
|
||||||
|
总测试数: 8
|
||||||
|
通过: 7
|
||||||
|
失败: 1
|
||||||
|
错误: 0
|
||||||
|
|
||||||
|
测试接口:
|
||||||
|
1. /dpc/employee/list - 员工列表(MyBatis Plus分页)
|
||||||
|
2. /dpc/intermediary/list - 中介黑名单列表(若依startPage分页)
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
详细结果:
|
||||||
|
------------------------------------------------------------
|
||||||
|
|
||||||
|
测试: 员工列表 - 第1页(10条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/10
|
||||||
|
返回行数: 10
|
||||||
|
总数: 199
|
||||||
|
预期行数: 10
|
||||||
|
|
||||||
|
测试: 员工列表 - 第2页(10条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: PASS
|
||||||
|
页码: 2/10
|
||||||
|
返回行数: 10
|
||||||
|
总数: 199
|
||||||
|
预期行数: 10
|
||||||
|
|
||||||
|
测试: 员工列表 - 第1页(5条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/5
|
||||||
|
返回行数: 5
|
||||||
|
总数: 199
|
||||||
|
预期行数: 5
|
||||||
|
|
||||||
|
测试: 员工列表 - 第1页(20条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/20
|
||||||
|
返回行数: 20
|
||||||
|
总数: 199
|
||||||
|
预期行数: 20
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第1页(10条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/10
|
||||||
|
返回行数: 1
|
||||||
|
总数: 1
|
||||||
|
预期行数: 1
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第2页(10条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: FAIL
|
||||||
|
错误: 行数不匹配
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第1页(5条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/5
|
||||||
|
返回行数: 1
|
||||||
|
总数: 1
|
||||||
|
预期行数: 1
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第1页(20条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/20
|
||||||
|
返回行数: 1
|
||||||
|
总数: 1
|
||||||
|
预期行数: 1
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
测试结论:
|
||||||
|
✗ 存在分页接口总数返回异常
|
||||||
84
test/pagination_test_report_20260128_153235.txt
Normal file
84
test/pagination_test_report_20260128_153235.txt
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
============================================================
|
||||||
|
分页接口总数测试报告
|
||||||
|
============================================================
|
||||||
|
|
||||||
|
测试时间: 2026-01-28 15:32:35
|
||||||
|
|
||||||
|
测试统计:
|
||||||
|
总测试数: 8
|
||||||
|
通过: 7
|
||||||
|
失败: 1
|
||||||
|
错误: 0
|
||||||
|
|
||||||
|
测试接口:
|
||||||
|
1. /dpc/employee/list - 员工列表(MyBatis Plus分页)
|
||||||
|
2. /dpc/intermediary/list - 中介黑名单列表(若依startPage分页)
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
详细结果:
|
||||||
|
------------------------------------------------------------
|
||||||
|
|
||||||
|
测试: 员工列表 - 第1页(10条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/10
|
||||||
|
返回行数: 10
|
||||||
|
总数: 199
|
||||||
|
预期行数: 10
|
||||||
|
|
||||||
|
测试: 员工列表 - 第2页(10条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: PASS
|
||||||
|
页码: 2/10
|
||||||
|
返回行数: 10
|
||||||
|
总数: 199
|
||||||
|
预期行数: 10
|
||||||
|
|
||||||
|
测试: 员工列表 - 第1页(5条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/5
|
||||||
|
返回行数: 5
|
||||||
|
总数: 199
|
||||||
|
预期行数: 5
|
||||||
|
|
||||||
|
测试: 员工列表 - 第1页(20条/页)
|
||||||
|
API类型: MyBatis Plus
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/20
|
||||||
|
返回行数: 20
|
||||||
|
总数: 199
|
||||||
|
预期行数: 20
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第1页(10条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/10
|
||||||
|
返回行数: 1
|
||||||
|
总数: 1
|
||||||
|
预期行数: 1
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第2页(10条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: FAIL
|
||||||
|
错误: 行数不匹配
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第1页(5条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/5
|
||||||
|
返回行数: 1
|
||||||
|
总数: 1
|
||||||
|
预期行数: 1
|
||||||
|
|
||||||
|
测试: 中介黑名单 - 第1页(20条/页)
|
||||||
|
API类型: 若依startPage
|
||||||
|
状态: PASS
|
||||||
|
页码: 1/20
|
||||||
|
返回行数: 1
|
||||||
|
总数: 1
|
||||||
|
预期行数: 1
|
||||||
|
|
||||||
|
------------------------------------------------------------
|
||||||
|
测试结论:
|
||||||
|
✗ 存在分页接口总数返回异常
|
||||||
140
test/test_pagination.ps1
Normal file
140
test/test_pagination.ps1
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
# Pagination API Test Script
|
||||||
|
# Test employee list pagination and total count
|
||||||
|
|
||||||
|
$BaseUrl = "http://localhost:8080"
|
||||||
|
$LoginUrl = "$BaseUrl/login/test"
|
||||||
|
$ListUrl = "$BaseUrl/dpc/employee/list"
|
||||||
|
|
||||||
|
# Login to get token
|
||||||
|
$loginBody = @{
|
||||||
|
username = "admin"
|
||||||
|
password = "admin123"
|
||||||
|
} | ConvertTo-Json
|
||||||
|
|
||||||
|
Write-Host "Logging in..." -ForegroundColor Cyan
|
||||||
|
$loginResponse = Invoke-RestMethod -Uri $LoginUrl -Method Post -Body $loginBody -ContentType "application/json"
|
||||||
|
$token = $loginResponse.token
|
||||||
|
|
||||||
|
Write-Host "Login success!" -ForegroundColor Green
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
$headers = @{
|
||||||
|
Authorization = "Bearer $token"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Test function
|
||||||
|
function Test-Page {
|
||||||
|
param(
|
||||||
|
[int]$PageNum,
|
||||||
|
[int]$PageSize
|
||||||
|
)
|
||||||
|
|
||||||
|
Write-Host "========== Testing: pageNum=$PageNum, pageSize=$PageSize ==========" -ForegroundColor Yellow
|
||||||
|
|
||||||
|
$queryParams = @{
|
||||||
|
pageNum = $PageNum
|
||||||
|
pageSize = $PageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
$queryString = ($queryParams.GetEnumerator() | ForEach-Object { "$($_.Key)=$($_.Value)" }) -join '&'
|
||||||
|
$url = "$ListUrl?$queryString"
|
||||||
|
|
||||||
|
try {
|
||||||
|
$response = Invoke-RestMethod -Uri $url -Method Get -Headers $headers
|
||||||
|
|
||||||
|
$rows = $response.rows.Count
|
||||||
|
$total = $response.total
|
||||||
|
$code = $response.code
|
||||||
|
|
||||||
|
Write-Host " Response Code: $code" -ForegroundColor Cyan
|
||||||
|
Write-Host " Returned Rows: $rows" -ForegroundColor Cyan
|
||||||
|
Write-Host " Total Records: $total" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Calculate expected values
|
||||||
|
$expectedTotalPages = [Math]::Ceiling($total / $pageSize)
|
||||||
|
$expectedRows = if ($PageNum -lt $expectedTotalPages) { $pageSize } elseif ($PageNum -eq $expectedTotalPages) { $total - ($pageSize * ($PageNum - 1)) } else { 0 }
|
||||||
|
|
||||||
|
Write-Host " Expected Rows: $expectedRows" -ForegroundColor Cyan
|
||||||
|
Write-Host " Expected Total Pages: $expectedTotalPages" -ForegroundColor Cyan
|
||||||
|
|
||||||
|
# Verify
|
||||||
|
$isCorrect = $rows -eq $expectedRows
|
||||||
|
if ($isCorrect) {
|
||||||
|
Write-Host " Result: CORRECT" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host " Result: WRONG! Got $rows rows, expected $expectedRows" -ForegroundColor Red
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show returned employee IDs
|
||||||
|
if ($response.rows -and $response.rows.Count -gt 0) {
|
||||||
|
$ids = ($response.rows | ForEach-Object { $_.employeeId }) -join ', '
|
||||||
|
Write-Host " Employee IDs: $ids" -ForegroundColor Gray
|
||||||
|
}
|
||||||
|
|
||||||
|
return @{
|
||||||
|
Success = ($code -eq 200)
|
||||||
|
Rows = $rows
|
||||||
|
Total = $total
|
||||||
|
ExpectedRows = $expectedRows
|
||||||
|
IsCorrect = $isCorrect
|
||||||
|
Response = $response
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Host " Error: $_" -ForegroundColor Red
|
||||||
|
return @{
|
||||||
|
Success = $false
|
||||||
|
Error = $_.Exception.Message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================ Starting Pagination Tests ================" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Test cases
|
||||||
|
$testCases = @(
|
||||||
|
@{ PageNum = 1; PageSize = 10; Description = "Page 1, Size 10" }
|
||||||
|
@{ PageNum = 2; PageSize = 10; Description = "Page 2, Size 10" }
|
||||||
|
@{ PageNum = 1; PageSize = 5; Description = "Page 1, Size 5" }
|
||||||
|
@{ PageNum = 3; PageSize = 5; Description = "Page 3, Size 5" }
|
||||||
|
@{ PageNum = 1; PageSize = 20; Description = "Page 1, Size 20" }
|
||||||
|
@{ PageNum = 1; PageSize = 3; Description = "Page 1, Size 3" }
|
||||||
|
)
|
||||||
|
|
||||||
|
$results = @()
|
||||||
|
$allCorrect = $true
|
||||||
|
|
||||||
|
foreach ($testCase in $testCases) {
|
||||||
|
Write-Host ""
|
||||||
|
$result = Test-Page -PageNum $testCase.PageNum -PageSize $testCase.PageSize
|
||||||
|
$results += [PSCustomObject]@{
|
||||||
|
Test = $testCase.Description
|
||||||
|
PageNum = $testCase.PageNum
|
||||||
|
PageSize = $testCase.PageSize
|
||||||
|
ReturnedRows = $result.Rows
|
||||||
|
TotalRecords = $result.Total
|
||||||
|
ExpectedRows = $result.ExpectedRows
|
||||||
|
Correct = if ($result.IsCorrect) { "PASS" } else { "FAIL" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $result.IsCorrect) {
|
||||||
|
$allCorrect = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
Start-Sleep -Milliseconds 200
|
||||||
|
}
|
||||||
|
|
||||||
|
# Show summary
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "================ Test Results Summary ================" -ForegroundColor Yellow
|
||||||
|
Write-Host ""
|
||||||
|
$results | Format-Table -AutoSize
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
if ($allCorrect) {
|
||||||
|
Write-Host "PASS - All tests passed! Pagination is working correctly." -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host "FAIL - Some tests failed! Please check pagination logic." -ForegroundColor Red
|
||||||
|
}
|
||||||
437
test/test_pagination.py
Normal file
437
test/test_pagination.py
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""分页接口总数测试脚本
|
||||||
|
测试接口:
|
||||||
|
1. /dpc/employee/list - 员工列表(MyBatis Plus分页)
|
||||||
|
2. /dpc/intermediary/list - 中介黑名单列表(若依startPage分页)
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import io
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# 设置stdout编码为UTF-8
|
||||||
|
if sys.platform == 'win32':
|
||||||
|
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||||
|
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8')
|
||||||
|
|
||||||
|
BASE_URL = "http://localhost:8080"
|
||||||
|
LOGIN_URL = f"{BASE_URL}/login/test"
|
||||||
|
EMPLOYEE_LIST_URL = f"{BASE_URL}/dpc/employee/list"
|
||||||
|
INTERMEDIARY_LIST_URL = f"{BASE_URL}/dpc/intermediary/list"
|
||||||
|
|
||||||
|
# 测试结果存储
|
||||||
|
test_results = []
|
||||||
|
|
||||||
|
|
||||||
|
def login():
|
||||||
|
"""登录获取token"""
|
||||||
|
print("=" * 60)
|
||||||
|
print("步骤1: 获取认证Token")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
login_body = {
|
||||||
|
"username": "admin",
|
||||||
|
"password": "admin123"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 使用json参数发送JSON格式请求体
|
||||||
|
response = requests.post(LOGIN_URL, json=login_body)
|
||||||
|
print(f"请求URL: {LOGIN_URL}")
|
||||||
|
print(f"请求参数: {login_body}")
|
||||||
|
print(f"响应状态码: {response.status_code}")
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
if data.get("code") == 200:
|
||||||
|
token = data.get("token")
|
||||||
|
print(f"✓ Token获取成功: {token[:20]}...")
|
||||||
|
return token
|
||||||
|
else:
|
||||||
|
print(f"✗ Token获取失败: {data.get('msg')}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ 异常: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def test_page(url, token, page_num, page_size, test_name, api_type):
|
||||||
|
"""测试指定分页参数的接口"""
|
||||||
|
print(f"\n========== 测试: {test_name} ==========")
|
||||||
|
print(f"API类型: {api_type}")
|
||||||
|
print(f"URL: {url}")
|
||||||
|
print(f"参数: pageNum={page_num}, pageSize={page_size}")
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"pageNum": page_num,
|
||||||
|
"pageSize": page_size
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Authorization": f"Bearer {token}"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, params=params, headers=headers)
|
||||||
|
print(f"响应状态码: {response.status_code}")
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
print(f"✗ HTTP错误: {response.text}")
|
||||||
|
test_results.append({
|
||||||
|
"test_name": test_name,
|
||||||
|
"api_type": api_type,
|
||||||
|
"status": "FAIL",
|
||||||
|
"error": f"HTTP {response.status_code}"
|
||||||
|
})
|
||||||
|
return None
|
||||||
|
|
||||||
|
data = response.json()
|
||||||
|
print(f"响应内容:\n{json.dumps(data, indent=2, ensure_ascii=False)}")
|
||||||
|
|
||||||
|
code = data.get("code", 0)
|
||||||
|
|
||||||
|
if code != 200:
|
||||||
|
print(f"✗ 业务错误: {data.get('msg')}")
|
||||||
|
test_results.append({
|
||||||
|
"test_name": test_name,
|
||||||
|
"api_type": api_type,
|
||||||
|
"status": "FAIL",
|
||||||
|
"error": data.get("msg", "Unknown error")
|
||||||
|
})
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 检查响应结构 - 支持两种格式
|
||||||
|
# 格式1: {data: {total, rows}, code, msg}
|
||||||
|
# 格式2: {total, rows, code, msg}
|
||||||
|
if "data" in data:
|
||||||
|
response_data = data["data"]
|
||||||
|
rows = response_data.get("rows", [])
|
||||||
|
total = response_data.get("total")
|
||||||
|
else:
|
||||||
|
# 扁平结构格式
|
||||||
|
rows = data.get("rows", [])
|
||||||
|
total = data.get("total")
|
||||||
|
|
||||||
|
rows_count = len(rows)
|
||||||
|
print(f"\n--- 分页数据分析 ---")
|
||||||
|
print(f"返回行数(rows): {rows_count}")
|
||||||
|
print(f"总数(total): {total}")
|
||||||
|
|
||||||
|
# 验证total字段
|
||||||
|
if total is None:
|
||||||
|
print(f"✗ 响应缺少total字段")
|
||||||
|
test_results.append({
|
||||||
|
"test_name": test_name,
|
||||||
|
"api_type": api_type,
|
||||||
|
"status": "FAIL",
|
||||||
|
"error": "响应缺少total字段"
|
||||||
|
})
|
||||||
|
return None
|
||||||
|
|
||||||
|
if not isinstance(total, int):
|
||||||
|
print(f"✗ total类型错误: {type(total)}")
|
||||||
|
test_results.append({
|
||||||
|
"test_name": test_name,
|
||||||
|
"api_type": api_type,
|
||||||
|
"status": "FAIL",
|
||||||
|
"error": f"total类型错误: {type(total)}"
|
||||||
|
})
|
||||||
|
return None
|
||||||
|
|
||||||
|
if total < 0:
|
||||||
|
print(f"✗ total值无效: {total}")
|
||||||
|
test_results.append({
|
||||||
|
"test_name": test_name,
|
||||||
|
"api_type": api_type,
|
||||||
|
"status": "FAIL",
|
||||||
|
"error": f"total值无效: {total}"
|
||||||
|
})
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 计算预期值
|
||||||
|
expected_total_pages = (total + page_size - 1) // page_size
|
||||||
|
if page_num < expected_total_pages:
|
||||||
|
expected_rows = page_size
|
||||||
|
elif page_num == expected_total_pages:
|
||||||
|
expected_rows = total - (page_size * (page_num - 1))
|
||||||
|
else:
|
||||||
|
expected_rows = 0
|
||||||
|
|
||||||
|
print(f"预期行数: {expected_rows}")
|
||||||
|
print(f"预期总页数: {expected_total_pages}")
|
||||||
|
|
||||||
|
# 验证行数是否正确
|
||||||
|
is_correct = rows_count == expected_rows
|
||||||
|
|
||||||
|
if is_correct:
|
||||||
|
print(f"✓ 测试通过 - 分页总数返回正常")
|
||||||
|
test_results.append({
|
||||||
|
"test_name": test_name,
|
||||||
|
"api_type": api_type,
|
||||||
|
"status": "PASS",
|
||||||
|
"page_num": page_num,
|
||||||
|
"page_size": page_size,
|
||||||
|
"rows_count": rows_count,
|
||||||
|
"total": total,
|
||||||
|
"expected_rows": expected_rows
|
||||||
|
})
|
||||||
|
else:
|
||||||
|
print(f"✗ 测试失败 - 预期{expected_rows}行,实际{rows_count}行")
|
||||||
|
test_results.append({
|
||||||
|
"test_name": test_name,
|
||||||
|
"api_type": api_type,
|
||||||
|
"status": "FAIL",
|
||||||
|
"page_num": page_num,
|
||||||
|
"page_size": page_size,
|
||||||
|
"rows_count": rows_count,
|
||||||
|
"total": total,
|
||||||
|
"expected_rows": expected_rows,
|
||||||
|
"error": f"行数不匹配"
|
||||||
|
})
|
||||||
|
|
||||||
|
# 显示返回的ID
|
||||||
|
if rows:
|
||||||
|
if "employeeId" in rows[0]:
|
||||||
|
ids = ', '.join([str(r.get("employeeId")) for r in rows])
|
||||||
|
print(f"员工ID: {ids}")
|
||||||
|
elif "intermediaryId" in rows[0]:
|
||||||
|
ids = ', '.join([str(r.get("intermediaryId")) for r in rows])
|
||||||
|
print(f"中介ID: {ids}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"rows": rows_count,
|
||||||
|
"total": total,
|
||||||
|
"expected_rows": expected_rows,
|
||||||
|
"is_correct": is_correct
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ 异常: {e}")
|
||||||
|
test_results.append({
|
||||||
|
"test_name": test_name,
|
||||||
|
"api_type": api_type,
|
||||||
|
"status": "ERROR",
|
||||||
|
"error": str(e)
|
||||||
|
})
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def test_consistency(url, token, test_name, api_type):
|
||||||
|
"""测试不同pageSize下total是否一致"""
|
||||||
|
print(f"\n========== 测试总数一致性: {test_name} ==========")
|
||||||
|
|
||||||
|
page_sizes = [10, 20, 50]
|
||||||
|
totals = []
|
||||||
|
|
||||||
|
for size in page_sizes:
|
||||||
|
params = {"pageNum": 1, "pageSize": size}
|
||||||
|
headers = {"Authorization": f"Bearer {token}"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, params=params, headers=headers)
|
||||||
|
if response.status_code == 200:
|
||||||
|
data = response.json()
|
||||||
|
if data.get("code") == 200:
|
||||||
|
# 支持两种响应格式
|
||||||
|
if "data" in data:
|
||||||
|
total = data.get("data", {}).get("total")
|
||||||
|
else:
|
||||||
|
total = data.get("total")
|
||||||
|
totals.append(total)
|
||||||
|
print(f"pageSize={size}: total={total}")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"✗ 异常: {e}")
|
||||||
|
|
||||||
|
if len(set(totals)) == 1 and totals[0] is not None:
|
||||||
|
print(f"✓ 不同pageSize下总数一致: {totals[0]}")
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
print(f"✗ 不同pageSize下总数不一致: {totals}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def generate_report():
|
||||||
|
"""生成测试报告"""
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("测试报告")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
# 按API类型分组
|
||||||
|
employee_results = [r for r in test_results if "员工" in r.get("test_name", "")]
|
||||||
|
intermediary_results = [r for r in test_results if "中介" in r.get("test_name", "")]
|
||||||
|
|
||||||
|
pass_count = sum(1 for r in test_results if r["status"] == "PASS")
|
||||||
|
fail_count = sum(1 for r in test_results if r["status"] == "FAIL")
|
||||||
|
error_count = sum(1 for r in test_results if r["status"] == "ERROR")
|
||||||
|
|
||||||
|
print(f"\n总测试数: {len(test_results)}")
|
||||||
|
print(f"通过: {pass_count}")
|
||||||
|
print(f"失败: {fail_count}")
|
||||||
|
print(f"错误: {error_count}")
|
||||||
|
|
||||||
|
# 员工接口结果
|
||||||
|
print(f"\n--- 员工列表接口 (MyBatis Plus) ---")
|
||||||
|
print(f"测试数: {len(employee_results)}")
|
||||||
|
for r in employee_results:
|
||||||
|
status_icon = "✓" if r["status"] == "PASS" else "✗"
|
||||||
|
print(f"{status_icon} {r['test_name']}: {r['status']}")
|
||||||
|
if r["status"] == "PASS":
|
||||||
|
print(f" 页码: {r.get('page_num')}/{r.get('page_size')}, "
|
||||||
|
f"返回行数: {r.get('rows_count')}, 总数: {r.get('total')}")
|
||||||
|
else:
|
||||||
|
print(f" 错误: {r.get('error', 'Unknown')}")
|
||||||
|
|
||||||
|
# 中介黑名单接口结果
|
||||||
|
print(f"\n--- 中介黑名单接口 (若依startPage) ---")
|
||||||
|
print(f"测试数: {len(intermediary_results)}")
|
||||||
|
for r in intermediary_results:
|
||||||
|
status_icon = "✓" if r["status"] == "PASS" else "✗"
|
||||||
|
print(f"{status_icon} {r['test_name']}: {r['status']}")
|
||||||
|
if r["status"] == "PASS":
|
||||||
|
print(f" 页码: {r.get('page_num')}/{r.get('page_size')}, "
|
||||||
|
f"返回行数: {r.get('rows_count')}, 总数: {r.get('total')}")
|
||||||
|
else:
|
||||||
|
print(f" 错误: {r.get('error', 'Unknown')}")
|
||||||
|
|
||||||
|
# 总体结论
|
||||||
|
print(f"\n--- 测试结论 ---")
|
||||||
|
if fail_count == 0 and error_count == 0:
|
||||||
|
print("✓ 所有分页接口总数返回正常")
|
||||||
|
else:
|
||||||
|
print("✗ 存在分页接口总数返回异常")
|
||||||
|
|
||||||
|
# 保存报告到文件
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
report_file = f"test/pagination_test_report_{timestamp}.txt"
|
||||||
|
|
||||||
|
with open(report_file, "w", encoding="utf-8") as f:
|
||||||
|
f.write("=" * 60 + "\n")
|
||||||
|
f.write("分页接口总数测试报告\n")
|
||||||
|
f.write("=" * 60 + "\n\n")
|
||||||
|
f.write(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
||||||
|
|
||||||
|
f.write("测试统计:\n")
|
||||||
|
f.write(f" 总测试数: {len(test_results)}\n")
|
||||||
|
f.write(f" 通过: {pass_count}\n")
|
||||||
|
f.write(f" 失败: {fail_count}\n")
|
||||||
|
f.write(f" 错误: {error_count}\n\n")
|
||||||
|
|
||||||
|
f.write("测试接口:\n")
|
||||||
|
f.write(" 1. /dpc/employee/list - 员工列表(MyBatis Plus分页)\n")
|
||||||
|
f.write(" 2. /dpc/intermediary/list - 中介黑名单列表(若依startPage分页)\n\n")
|
||||||
|
|
||||||
|
f.write("-" * 60 + "\n")
|
||||||
|
f.write("详细结果:\n")
|
||||||
|
f.write("-" * 60 + "\n\n")
|
||||||
|
|
||||||
|
for r in test_results:
|
||||||
|
f.write(f"测试: {r['test_name']}\n")
|
||||||
|
f.write(f"API类型: {r['api_type']}\n")
|
||||||
|
f.write(f"状态: {r['status']}\n")
|
||||||
|
if r['status'] == 'PASS':
|
||||||
|
f.write(f" 页码: {r.get('page_num')}/{r.get('page_size')}\n")
|
||||||
|
f.write(f" 返回行数: {r.get('rows_count')}\n")
|
||||||
|
f.write(f" 总数: {r.get('total')}\n")
|
||||||
|
f.write(f" 预期行数: {r.get('expected_rows')}\n")
|
||||||
|
else:
|
||||||
|
f.write(f" 错误: {r.get('error', 'Unknown')}\n")
|
||||||
|
f.write("\n")
|
||||||
|
|
||||||
|
f.write("-" * 60 + "\n")
|
||||||
|
f.write("测试结论:\n")
|
||||||
|
if fail_count == 0 and error_count == 0:
|
||||||
|
f.write("✓ 所有分页接口总数返回正常\n")
|
||||||
|
else:
|
||||||
|
f.write("✗ 存在分页接口总数返回异常\n")
|
||||||
|
|
||||||
|
print(f"\n报告已保存至: {report_file}")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("分页接口总数测试")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||||
|
|
||||||
|
# 获取token
|
||||||
|
token = login()
|
||||||
|
if not token:
|
||||||
|
print("\n✗ 无法获取token,测试终止")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\n✓ 登录成功,开始测试")
|
||||||
|
|
||||||
|
# 员工列表接口测试用例
|
||||||
|
employee_test_cases = [
|
||||||
|
{"page_num": 1, "page_size": 10, "desc": "员工列表 - 第1页(10条/页)"},
|
||||||
|
{"page_num": 2, "page_size": 10, "desc": "员工列表 - 第2页(10条/页)"},
|
||||||
|
{"page_num": 1, "page_size": 5, "desc": "员工列表 - 第1页(5条/页)"},
|
||||||
|
{"page_num": 1, "page_size": 20, "desc": "员工列表 - 第1页(20条/页)"},
|
||||||
|
]
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("测试员工列表接口(MyBatis Plus分页)")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
for test_case in employee_test_cases:
|
||||||
|
test_page(
|
||||||
|
EMPLOYEE_LIST_URL,
|
||||||
|
token,
|
||||||
|
test_case["page_num"],
|
||||||
|
test_case["page_size"],
|
||||||
|
test_case["desc"],
|
||||||
|
"MyBatis Plus"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 测试总数一致性
|
||||||
|
test_consistency(
|
||||||
|
EMPLOYEE_LIST_URL,
|
||||||
|
token,
|
||||||
|
"员工列表-总数一致性",
|
||||||
|
"MyBatis Plus"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 中介黑名单接口测试用例
|
||||||
|
intermediary_test_cases = [
|
||||||
|
{"page_num": 1, "page_size": 10, "desc": "中介黑名单 - 第1页(10条/页)"},
|
||||||
|
{"page_num": 2, "page_size": 10, "desc": "中介黑名单 - 第2页(10条/页)"},
|
||||||
|
{"page_num": 1, "page_size": 5, "desc": "中介黑名单 - 第1页(5条/页)"},
|
||||||
|
{"page_num": 1, "page_size": 20, "desc": "中介黑名单 - 第1页(20条/页)"},
|
||||||
|
]
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("测试中介黑名单接口(若依startPage分页)")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
for test_case in intermediary_test_cases:
|
||||||
|
test_page(
|
||||||
|
INTERMEDIARY_LIST_URL,
|
||||||
|
token,
|
||||||
|
test_case["page_num"],
|
||||||
|
test_case["page_size"],
|
||||||
|
test_case["desc"],
|
||||||
|
"若依startPage"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 测试总数一致性
|
||||||
|
test_consistency(
|
||||||
|
INTERMEDIARY_LIST_URL,
|
||||||
|
token,
|
||||||
|
"中介黑名单-总数一致性",
|
||||||
|
"若依startPage"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 生成报告
|
||||||
|
generate_report()
|
||||||
|
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("测试完成")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user