文件夹整理

This commit is contained in:
wkc
2026-02-09 14:34:27 +08:00
parent 3ffe173dd6
commit d08782ae9e
4433 changed files with 537607 additions and 0 deletions

View File

@@ -0,0 +1,273 @@
# 中介黑名单管理模块 - 测试与部署文档
## 文件说明
本目录包含中介黑名单管理模块(v2.0)的测试脚本、API文档、菜单配置和测试报告模板。
```
doc/
├── scripts/
│ ├── test-intermediary-api.sh # API自动化测试脚本
│ └── cleanup-intermediary-test-data.sh # 测试数据清理脚本
├── api/
│ └── 中介黑名单管理API文档-v2.0.md # 完整的API接口文档
├── test/
│ └── intermediary-blacklist-test-report.md # 测试报告模板
└── sql/
└── menu-intermediary.sql # 菜单配置SQL
```
---
## 快速开始
### 1. 执行菜单SQL
首先在数据库中执行菜单配置SQL,为系统添加中介黑名单管理菜单:
```bash
mysql -u root -p ruoyi < sql/menu-intermediary.sql
```
或者直接在MySQL客户端中执行:
```sql
source D:/ccdi/ccdi/sql/menu-intermediary.sql;
```
执行后,在角色管理中为相应角色分配权限。
### 2. 运行API测试脚本
确保后端服务已启动(http://localhost:8080),然后执行测试脚本:
```bash
cd D:/ccdi/ccdi/doc/scripts
bash test-intermediary-api.sh
```
测试脚本会自动:
- 获取Token
- 测试查询列表
- 测试新增个人中介
- 测试新增实体中介
- 测试查询详情
- 测试修改操作
- 测试唯一性校验
- 测试条件查询
### 3. 清理测试数据
测试完成后,运行清理脚本删除测试数据:
```bash
cd D:/ccdi/ccdi/doc/scripts
bash cleanup-intermediary-test-data.sh
```
### 4. 查看API文档
参考API文档进行接口对接:
- 文件位置: `doc/api/中介黑名单管理API文档-v2.0.md`
- Swagger UI: http://localhost:8080/swagger-ui/index.html
### 5. 填写测试报告
根据测试结果填写测试报告模板:
- 文件位置: `doc/test/intermediary-blacklist-test-report.md`
---
## API接口列表
### 基础路径
`/ccdi/intermediary`
### 主要接口
| 方法 | 路径 | 说明 | 权限 |
|------|------|------|------|
| GET | /list | 查询中介列表 | ccdi:intermediary:list |
| GET | /person/{bizId} | 查询个人中介详情 | ccdi:intermediary:query |
| GET | /entity/{socialCreditCode} | 查询实体中介详情 | ccdi:intermediary:query |
| POST | /person | 新增个人中介 | ccdi:intermediary:add |
| POST | /entity | 新增实体中介 | ccdi:intermediary:add |
| PUT | /person | 修改个人中介 | ccdi:intermediary:edit |
| PUT | /entity | 修改实体中介 | ccdi:intermediary:edit |
| DELETE | /{ids} | 删除中介 | ccdi:intermediary:remove |
| GET | /checkPersonIdUnique | 校验人员ID唯一性 | 无 |
| GET | /checkSocialCreditCodeUnique | 校验统一社会信用代码唯一性 | 无 |
| POST | /importPersonTemplate | 下载个人中介导入模板 | 无 |
| POST | /importEntityTemplate | 下载实体中介导入模板 | 无 |
| POST | /importPersonData | 导入个人中介数据 | ccdi:intermediary:import |
| POST | /importEntityData | 导入实体中介数据 | ccdi:intermediary:import |
详细接口说明请参考API文档。
---
## 测试账号
- **用户名**: admin
- **密码**: admin123
- **角色**: 管理员
---
## 菜单权限说明
执行menu-intermediary.sql后,系统会创建以下权限:
| 权限标识 | 说明 |
|---------|------|
| ccdi:intermediary:query | 查询中介详情 |
| ccdi:intermediary:list | 查询中介列表 |
| ccdi:intermediary:add | 新增中介 |
| ccdi:intermediary:edit | 修改中介 |
| ccdi:intermediary:remove | 删除中介 |
| ccdi:intermediary:export | 导出中介数据 |
| ccdi:intermediary:import | 导入中介数据 |
在角色管理中为相应角色分配这些权限。
---
## 数据字典说明
模块使用的数据字典类型:
| 字典类型 | 字典名称 | 用途 |
|---------|---------|------|
| ccdi_indiv_gender | 个人中介性别 | 个人中介模板性别下拉框 |
| ccdi_certificate_type | 证件类型 | 个人中介模板证件类型下拉框 |
| ccdi_entity_type | 主体类型 | 机构中介模板主体类型下拉框 |
| ccdi_enterprise_nature | 企业性质 | 机构中介模板企业性质下拉框 |
| ccdi_data_source | 数据来源 | 数据来源字段映射 |
确保这些字典类型在系统中已配置。
---
## 测试用例统计
本模块共包含44个测试用例,涵盖:
1. **列表查询** (7个用例)
- 基础列表查询
- 分页查询
- 按姓名查询
- 按证件号查询
- 按中介类型查询
- 组合条件查询
2. **个人中介管理** (8个用例)
- 新增个人中介
- 字段验证
- 唯一性校验
- 修改个人中介
- 查询详情
3. **实体中介管理** (7个用例)
- 新增实体中介
- 字段验证
- 唯一性校验
- 修改实体中介
- 查询详情
4. **唯一性校验** (2个用例)
- 人员ID唯一性
- 统一社会信用代码唯一性
5. **删除功能** (3个用例)
- 删除单条记录
- 批量删除
- 删除不存在的记录
6. **导入导出** (11个用例)
- 模板下载
- 数据导入
- 数据导出
- 异常处理
7. **权限控制** (6个用例)
- 各功能点的权限验证
---
## 常见问题
### 1. 测试脚本无法执行
**问题**: bash: test-intermediary-api.sh: command not found
**解决**: 使用bash命令执行
```bash
bash test-intermediary-api.sh
```
### 2. jq命令未安装
**问题**: jq: command not found
**解决**: 安装jq命令
```bash
# Ubuntu/Debian
apt-get install jq
# CentOS/RHEL
yum install jq
# Windows (使用Git Bash)
# 下载jq for Windows并添加到PATH
```
### 3. Token获取失败
**问题**: Token获取失败或返回null
**解决**:
- 确保后端服务已启动
- 确认用户名密码正确(admin/admin123)
- 检查/login/test接口是否正常
### 4. 菜单不显示
**问题**: 执行SQL后菜单不显示
**解决**:
- 在角色管理中为当前角色分配权限
- 刷新页面或重新登录
- 检查父级菜单ID(2000)是否存在
### 5. 导入失败
**问题**: 导入数据时报错
**解决**:
- 确认Excel模板格式正确
- 检查必填字段是否为空
- 检查证件号或统一社会信用代码是否重复
---
## 版本历史
| 版本 | 日期 | 说明 |
|------|------|------|
| 2.0.0 | 2026-02-04 | 重构版本:使用MyBatis Plus,分离DTO/VO,统一业务ID |
| 1.3.0 | 2026-01-29 | 新增接口分离:新增个人/机构专用新增接口 |
| 1.2.0 | 2026-01-29 | 修改接口分离:新增个人/机构专用修改接口 |
| 1.1.0 | 2026-01-29 | 添加字典下拉框功能,分离个人/机构模板 |
| 1.0.0 | 2026-01-29 | 初始版本,支持个人和机构分类管理 |
---
## 联系方式
如有问题,请联系开发团队。
---
**最后更新**: 2026-02-04

66
doc/README.md Normal file
View File

@@ -0,0 +1,66 @@
# 文档目录结构
本目录包含纪检初核系统的各类文档、测试数据和脚本。
## 目录说明
### 📁 docs/
项目文档目录
- `纪检初核系统功能说明书-V1.0.docx/md` - 系统功能说明书
- `纪检初核系统模块划分方案.md` - 模块划分方案
- `若依环境使用手册.docx` - 若依框架使用手册
- `中介黑名单弹窗优化设计.md` - UI设计文档
- `EasyExcel字典下拉框使用说明.md` - Excel导入使用说明
### 📁 api/
API接口文档目录
- `员工信息管理API文档.md` - 员工信息管理模块API
- `中介黑名单管理API文档.md` - 中介黑名单管理模块API
### 📁 scripts/
测试脚本目录
- `test_import.py` - 导入功能测试脚本
- `test_import_simple.py` - 简单导入测试脚本
- `test_uniqueness_validation.py` - 唯一性校验测试脚本
- `generate_test_data.py` - 测试数据生成脚本
### 📁 test-data/
测试数据目录
- `个人中介黑名单模板_1769667622015.xlsx` - 导入模板
- `个人中介黑名单测试数据_1000条.xlsx` - 测试数据第1批
- `个人中介黑名单测试数据_1000条_第2批.xlsx` - 测试数据第2批
- `中介人员信息表.csv` - 中介人员数据
- `中介主体信息表.csv` - 中介主体数据
### 📁 other/
其他文件目录
- `纪检初核系统-离线演示包/` - 离线演示包(解压版)
- `纪检初核系统-离线演示包.zip` - 离线演示包(压缩版)
- `ScreenShot_*.png` - 截图文件
### 📁 modules/
模块设计文档目录
- `01-项目管理模块/` - 项目管理模块文档
- `02-项目工作台/` - 项目工作台模块文档
- `03-信息维护模块.md` - 信息维护模块文档
- `04-参数配置模块.md` - 参数配置模块文档
- `05-系统管理模块.md` - 系统管理模块文档
## 使用说明
### 生成测试数据
```bash
cd doc/scripts
python generate_test_data.py
```
### 运行测试脚本
```bash
cd doc/scripts
python test_uniqueness_validation.py
```
### 导入测试数据
1.`test-data/` 目录下载对应的Excel文件
2. 在系统页面点击"导入"按钮
3. 选择文件并上传

View File

@@ -0,0 +1,124 @@
# 员工信息导入相关接口文档
## 1. 导入员工信息(异步)
**接口地址:** `POST /ccdi/employee/importData`
**权限标识:** `ccdi:employee:import`
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| file | File | 是 | Excel文件 |
| updateSupport | boolean | 否 | 是否更新已存在的数据,默认false |
**响应示例:**
```json
{
"code": 200,
"msg": "导入任务已提交,正在后台处理",
"data": {
"taskId": "uuid-string",
"status": "PROCESSING",
"message": "导入任务已提交,正在后台处理"
}
}
```
## 2. 查询导入状态
**接口地址:** `GET /ccdi/employee/importStatus/{taskId}`
**权限标识:**
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| taskId | String | 是 | 任务ID |
**响应示例:**
```json
{
"code": 200,
"data": {
"taskId": "uuid-string",
"status": "SUCCESS",
"totalCount": 100,
"successCount": 95,
"failureCount": 5,
"progress": 100,
"startTime": 1707225600000,
"endTime": 1707225900000,
"message": "导入完成"
}
}
```
**状态说明:**
| 状态值 | 说明 |
|--------|------|
| PROCESSING | 处理中 |
| SUCCESS | 全部成功 |
| PARTIAL_SUCCESS | 部分成功 |
| FAILED | 全部失败 |
## 3. 查询导入失败记录
**接口地址:** `GET /ccdi/employee/importFailures/{taskId}`
**权限标识:**
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| taskId | String | 是 | 任务ID |
**查询参数:**
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| pageNum | Integer | 否 | 页码,默认1 |
| pageSize | Integer | 否 | 每页条数,默认10 |
**响应示例:**
```json
{
"code": 200,
"rows": [
{
"employeeId": "1234567",
"name": "张三",
"idCard": "110101199001011234",
"deptId": 100,
"phone": "13800138000",
"status": "0",
"hireDate": "2020-01-01",
"errorMessage": "身份证号格式错误"
}
],
"total": 5
}
```
## 使用流程
1. 前端调用导入接口上传Excel文件
2. 后端立即返回taskId
3. 前端每2秒轮询查询导入状态
4. 导入完成后显示结果
5. 如有失败,显示"查看导入失败记录"按钮
6. 用户点击按钮查看失败记录详情
## 注意事项
1. Redis中存储的导入状态和失败记录保留7天
2. taskId如果过期或不存在,查询接口会返回错误
3. 导入是异步处理,大量数据导入不会阻塞HTTP请求
4. 失败记录只保存失败的数据,成功的数据不会存储

View File

@@ -0,0 +1,790 @@
# 采购交易信息管理 - API接口文档
## 文档信息
- **模块名称**: 采购交易信息管理
- **Controller**: `CcdiPurchaseTransactionController`
- **Base Path**: `/ccdi/purchaseTransaction`
- **Swagger**: http://localhost:8080/swagger-ui/index.html
- **生成时间**: 2026-02-06
---
## 目录
1. [接口列表](#接口列表)
2. [接口详情](#接口详情)
3. [数据模型](#数据模型)
4. [错误码说明](#错误码说明)
5. [接口示例](#接口示例)
---
## 接口列表
| 序号 | 接口名称 | HTTP方法 | 路径 | 权限标识 | 说明 |
|------|---------|----------|------|----------|------|
| 1 | 查询采购交易列表 | GET | /list | ccdi:purchaseTransaction:list | 分页查询采购交易信息 |
| 2 | 获取采购交易详情 | GET | /{purchaseId} | ccdi:purchaseTransaction:query | 根据ID获取详细信息 |
| 3 | 新增采购交易 | POST | / | ccdi:purchaseTransaction:add | 新增采购交易记录 |
| 4 | 修改采购交易 | PUT | / | ccdi:purchaseTransaction:edit | 修改采购交易记录 |
| 5 | 删除采购交易 | DELETE | /{purchaseIds} | ccdi:purchaseTransaction:remove | 删除采购交易记录 |
| 6 | 导出采购交易 | POST | /export | ccdi:purchaseTransaction:export | 导出Excel文件 |
| 7 | 下载导入模板 | POST | /importTemplate | 无 | 下载带下拉框的模板 |
| 8 | 导入采购交易 | POST | /importData | ccdi:purchaseTransaction:import | 异步导入Excel数据 |
| 9 | 查询导入状态 | GET | /importStatus/{taskId} | ccdi:purchaseTransaction:import | 查询异步导入进度 |
| 10 | 查询导入失败记录 | GET | /importFailures/{taskId} | ccdi:purchaseTransaction:import | 查询导入失败详情 |
---
## 接口详情
### 1. 查询采购交易列表
**接口描述**: 分页查询采购交易信息列表,支持多条件查询
**请求方式**: `GET`
**请求路径**: `/ccdi/purchaseTransaction/list`
**权限要求**: `ccdi:purchaseTransaction:list`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| pageNum | Integer | 否 | 页码默认1 | 1 |
| pageSize | Integer | 否 | 每页条数默认10 | 10 |
| projectName | String | 否 | 项目名称(模糊查询) | 办公设备采购 |
| subjectName | String | 否 | 标的物名称(模糊查询) | 电脑 |
| applicantName | String | 否 | 申请人姓名(模糊查询) | 张三 |
| params[beginApplyDate] | String | 否 | 申请日期起始 | 2025-01-01 |
| params[endApplyDate] | String | 否 | 申请日期结束 | 2025-12-31 |
**响应示例**:
```json
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"purchaseId": "PO20250206001",
"purchaseCategory": "货物类",
"projectName": "办公设备采购项目",
"subjectName": "笔记本电脑",
"subjectDesc": "高性能办公笔记本",
"purchaseQty": 50.00,
"budgetAmount": 500000.00,
"bidAmount": 450000.00,
"actualAmount": 455000.00,
"contractAmount": 450000.00,
"settlementAmount": 455000.00,
"purchaseMethod": "公开招标",
"supplierName": "某某科技有限公司",
"contactPerson": "李四",
"contactPhone": "13800138000",
"supplierUscc": "91110000MA000000XX",
"supplierBankAccount": "1234567890123456789",
"applyDate": "2025-01-01",
"planApproveDate": "2025-01-05",
"announceDate": "2025-01-10",
"bidOpenDate": "2025-01-15",
"contractSignDate": "2025-01-20",
"expectedDeliveryDate": "2025-02-01",
"actualDeliveryDate": "2025-02-01",
"acceptanceDate": "2025-02-05",
"settlementDate": "2025-02-10",
"applicantId": "E001001",
"applicantName": "张三",
"applyDepartment": "信息技术部",
"purchaseLeaderId": "E002001",
"purchaseLeaderName": "王五",
"purchaseDepartment": "采购部",
"createTime": "2025-02-06 10:00:00",
"updateTime": "2025-02-06 10:00:00",
"createdBy": "admin",
"updatedBy": "admin"
}
],
"total": 100
}
```
---
### 2. 获取采购交易详情
**接口描述**: 根据采购事项ID获取详细信息
**请求方式**: `GET`
**请求路径**: `/ccdi/purchaseTransaction/{purchaseId}`
**权限要求**: `ccdi:purchaseTransaction:query`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| purchaseId | String | 是 | 采购事项ID | PO20250206001 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"purchaseId": "PO20250206001",
"purchaseCategory": "货物类",
"projectName": "办公设备采购项目",
"subjectName": "笔记本电脑",
"subjectDesc": "高性能办公笔记本",
"purchaseQty": 50.00,
"budgetAmount": 500000.00,
"bidAmount": 450000.00,
"actualAmount": 455000.00,
"contractAmount": 450000.00,
"settlementAmount": 455000.00,
"purchaseMethod": "公开招标",
"supplierName": "某某科技有限公司",
"contactPerson": "李四",
"contactPhone": "13800138000",
"supplierUscc": "91110000MA000000XX",
"supplierBankAccount": "1234567890123456789",
"applyDate": "2025-01-01",
"planApproveDate": "2025-01-05",
"announceDate": "2025-01-10",
"bidOpenDate": "2025-01-15",
"contractSignDate": "2025-01-20",
"expectedDeliveryDate": "2025-02-01",
"actualDeliveryDate": "2025-02-01",
"acceptanceDate": "2025-02-05",
"settlementDate": "2025-02-10",
"applicantId": "E001001",
"applicantName": "张三",
"applyDepartment": "信息技术部",
"purchaseLeaderId": "E002001",
"purchaseLeaderName": "王五",
"purchaseDepartment": "采购部",
"createTime": "2025-02-06 10:00:00",
"updateTime": "2025-02-06 10:00:00",
"createdBy": "admin",
"updatedBy": "admin"
}
}
```
---
### 3. 新增采购交易
**接口描述**: 新增采购交易记录
**请求方式**: `POST`
**请求路径**: `/ccdi/purchaseTransaction`
**权限要求**: `ccdi:purchaseTransaction:add`
**请求头**:
```
Content-Type: application/json
Authorization: Bearer {token}
```
**请求体** (`CcdiPurchaseTransactionAddDTO`):
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| purchaseId | String | 是 | 采购事项ID最大32字符 | PO20250206001 |
| purchaseCategory | String | 否 | 采购类别最大50字符 | 货物类 |
| projectName | String | 否 | 项目名称最大200字符 | 办公设备采购项目 |
| subjectName | String | 否 | 标的物名称最大200字符 | 笔记本电脑 |
| subjectDesc | String | 否 | 标的物描述最大500字符 | 高性能办公笔记本 |
| purchaseQty | BigDecimal | 否 | 采购数量 | 50.00 |
| budgetAmount | BigDecimal | 否 | 预算金额 | 500000.00 |
| bidAmount | BigDecimal | 否 | 中标金额 | 450000.00 |
| actualAmount | BigDecimal | 否 | 实际采购金额 | 455000.00 |
| contractAmount | BigDecimal | 否 | 合同金额 | 450000.00 |
| settlementAmount | BigDecimal | 否 | 结算金额 | 455000.00 |
| purchaseMethod | String | 否 | 采购方式最大50字符 | 公开招标 |
| supplierName | String | 否 | 供应商名称最大200字符 | 某某科技有限公司 |
| supplierUscc | String | 否 | 供应商统一信用代码最大18字符 | 91110000MA000000XX |
| contactPerson | String | 否 | 供应商联系人最大50字符 | 李四 |
| contactPhone | String | 否 | 供应商联系电话最大20字符 | 13800138000 |
| supplierBankAccount | String | 否 | 供应商银行账户最大50字符 | 1234567890123456789 |
| applyDate | String | 否 | 采购申请日期yyyy-MM-dd | 2025-01-01 |
| planApproveDate | String | 否 | 采购计划批准日期yyyy-MM-dd | 2025-01-05 |
| announceDate | String | 否 | 采购公告发布日期yyyy-MM-dd | 2025-01-10 |
| bidOpenDate | String | 否 | 开标日期yyyy-MM-dd | 2025-01-15 |
| contractSignDate | String | 否 | 合同签订日期yyyy-MM-dd | 2025-01-20 |
| expectedDeliveryDate | String | 否 | 预计交货日期yyyy-MM-dd | 2025-02-01 |
| actualDeliveryDate | String | 否 | 实际交货日期yyyy-MM-dd | 2025-02-01 |
| acceptanceDate | String | 否 | 验收日期yyyy-MM-dd | 2025-02-05 |
| settlementDate | String | 否 | 结算日期yyyy-MM-dd | 2025-02-10 |
| applicantId | String | 否 | 申请人工号最大20字符 | E001001 |
| applicantName | String | 否 | 申请人姓名最大50字符 | 张三 |
| applyDepartment | String | 否 | 申请部门最大100字符 | 信息技术部 |
| purchaseLeaderId | String | 否 | 采购负责人工号最大20字符 | E002001 |
| purchaseLeaderName | String | 否 | 采购负责人姓名最大50字符 | 王五 |
| purchaseDepartment | String | 否 | 采购部门最大100字符 | 采购部 |
**请求示例**:
```json
{
"purchaseId": "PO20250206001",
"purchaseCategory": "货物类",
"projectName": "办公设备采购项目",
"subjectName": "笔记本电脑",
"subjectDesc": "高性能办公笔记本",
"purchaseQty": 50.00,
"budgetAmount": 500000.00,
"bidAmount": 450000.00,
"actualAmount": 455000.00,
"contractAmount": 450000.00,
"settlementAmount": 455000.00,
"purchaseMethod": "公开招标",
"supplierName": "某某科技有限公司",
"supplierUscc": "91110000MA000000XX",
"contactPerson": "李四",
"contactPhone": "13800138000",
"supplierBankAccount": "1234567890123456789",
"applyDate": "2025-01-01",
"planApproveDate": "2025-01-05",
"announceDate": "2025-01-10",
"bidOpenDate": "2025-01-15",
"contractSignDate": "2025-01-20",
"expectedDeliveryDate": "2025-02-01",
"actualDeliveryDate": "2025-02-01",
"acceptanceDate": "2025-02-05",
"settlementDate": "2025-02-10",
"applicantId": "E001001",
"applicantName": "张三",
"applyDepartment": "信息技术部",
"purchaseLeaderId": "E002001",
"purchaseLeaderName": "王五",
"purchaseDepartment": "采购部"
}
```
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 4. 修改采购交易
**接口描述**: 修改采购交易记录
**请求方式**: `PUT`
**请求路径**: `/ccdi/purchaseTransaction`
**权限要求**: `ccdi:purchaseTransaction:edit`
**请求头**:
```
Content-Type: application/json
Authorization: Bearer {token}
```
**请求体** (`CcdiPurchaseTransactionEditDTO`):
参数同新增接口但purchaseId为必填且不可修改。
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 5. 删除采购交易
**接口描述**: 删除采购交易记录(支持批量删除)
**请求方式**: `DELETE`
**请求路径**: `/ccdi/purchaseTransaction/{purchaseIds}`
**权限要求**: `ccdi:purchaseTransaction:remove`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| purchaseIds | String[] | 是 | 采购事项ID数组多个用逗号分隔 | PO20250206001,PO20250206002 |
**请求示例**:
```
DELETE /ccdi/purchaseTransaction/PO20250206001,PO20250206002
```
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 6. 导出采购交易
**接口描述**: 导出采购交易信息到Excel文件
**请求方式**: `POST`
**请求路径**: `/ccdi/purchaseTransaction/export`
**权限要求**: `ccdi:purchaseTransaction:export`
**请求参数**: 同查询接口,支持条件导出
**响应**: Excel文件流
**请求示例**:
```bash
curl -X POST "http://localhost:8080/ccdi/purchaseTransaction/export" \
-H "Authorization: Bearer {token}" \
-d "projectName=办公设备&applicantName=张三"
```
---
### 7. 下载导入模板
**接口描述**: 下载带字典下拉框的Excel导入模板
**请求方式**: `POST`
**请求路径**: `/ccdi/purchaseTransaction/importTemplate`
**权限要求**: 无
**响应**: Excel模板文件流包含数据验证下拉框
**请求示例**:
```bash
curl -X POST "http://localhost:8080/ccdi/purchaseTransaction/importTemplate" \
-H "Authorization: Bearer {token}" \
-o purchase_transaction_template.xlsx
```
---
### 8. 导入采购交易
**接口描述**: 异步导入Excel数据
**请求方式**: `POST`
**请求路径**: `/ccdi/purchaseTransaction/importData?updateSupport={updateSupport}`
**权限要求**: `ccdi:purchaseTransaction:import`
**请求头**:
```
Content-Type: multipart/form-data
Authorization: Bearer {token}
```
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| updateSupport | boolean | 是 | 是否更新已存在数据 | true/false |
**表单参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| file | File | 是 | Excel文件.xlsx或.xls |
**响应示例**:
```json
{
"code": 200,
"msg": "导入任务已提交任务IDtask-20250206-123456789"
}
```
---
### 9. 查询导入状态
**接口描述**: 查询异步导入任务的执行状态
**请求方式**: `GET`
**请求路径**: `/ccdi/purchaseTransaction/importStatus/{taskId}`
**权限要求**: `ccdi:purchaseTransaction:import`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| taskId | String | 是 | 任务ID | task-20250206-123456789 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"taskId": "task-20250206-123456789",
"status": "completed",
"total": 1000,
"successCount": 980,
"failureCount": 20,
"errorMsg": null
}
}
```
**状态说明**:
- `pending`: 等待执行
- `running`: 正在执行
- `completed`: 执行完成
- `failed`: 执行失败
---
### 10. 查询导入失败记录
**接口描述**: 查询导入任务中失败的记录详情
**请求方式**: `GET`
**请求路径**: `/ccdi/purchaseTransaction/importFailures/{taskId}`
**权限要求**: `ccdi:purchaseTransaction:import`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| taskId | String | 是 | 任务ID | task-20250206-123456789 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{
"purchaseId": "PO20250206001",
"rowNum": 5,
"errorMessage": "采购事项ID已存在"
},
{
"purchaseId": "PO20250206002",
"rowNum": 12,
"errorMessage": "预算金额格式错误"
}
]
}
```
---
## 数据模型
### CcdiPurchaseTransactionVO (查询返回对象)
采购交易信息的视图对象,用于列表查询和详情展示。
### CcdiPurchaseTransactionAddDTO (新增请求对象)
新增采购交易时的请求参数对象。
### CcdiPurchaseTransactionEditDTO (修改请求对象)
修改采购交易时的请求参数对象。
### CcdiPurchaseTransactionQueryDTO (查询请求对象)
查询条件参数对象。
### CcdiPurchaseTransactionExcel (导入导出对象)
Excel导入导出使用的数据对象支持字典下拉框。
### ImportStatusVO (导入状态对象)
异步导入任务的状态信息。
| 字段 | 类型 | 说明 |
|------|------|------|
| taskId | String | 任务ID |
| status | String | 状态pending/running/completed/failed |
| total | Integer | 总记录数 |
| successCount | Integer | 成功数量 |
| failureCount | Integer | 失败数量 |
| errorMsg | String | 错误信息(失败时) |
### PurchaseTransactionImportFailureVO (导入失败记录对象)
导入失败的记录详情。
| 字段 | 类型 | 说明 |
|------|------|------|
| purchaseId | String | 采购事项ID |
| rowNum | Integer | 行号 |
| errorMessage | String | 错误信息 |
---
## 错误码说明
### HTTP状态码
| 状态码 | 说明 |
|--------|------|
| 200 | 请求成功 |
| 401 | 未授权token无效或过期 |
| 403 | 无权限访问 |
| 404 | 资源不存在 |
| 500 | 服务器内部错误 |
### 业务错误码
| code | msg | 说明 |
|------|-----|------|
| 200 | 操作成功 | 请求成功处理 |
| 500 | 操作失败 | 服务器处理失败 |
| 401 | 请先登录 | 未登录或token过期 |
| 403 | 无权限访问 | 权限不足 |
---
## 接口示例
### 1. 完整的CRUD流程
```bash
# 1. 登录获取token
TOKEN=$(curl -s -X POST "http://localhost:8080/login/test" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"admin123"}' \
| jq -r '.token')
# 2. 查询列表
curl -X GET "http://localhost:8080/ccdi/purchaseTransaction/list?pageNum=1&pageSize=10" \
-H "Authorization: Bearer $TOKEN"
# 3. 新增记录
curl -X POST "http://localhost:8080/ccdi/purchaseTransaction" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"purchaseId": "PO20250206001",
"projectName": "办公设备采购项目",
"subjectName": "笔记本电脑",
"budgetAmount": 500000.00
}'
# 4. 获取详情
curl -X GET "http://localhost:8080/ccdi/purchaseTransaction/PO20250206001" \
-H "Authorization: Bearer $TOKEN"
# 5. 修改记录
curl -X PUT "http://localhost:8080/ccdi/purchaseTransaction" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"purchaseId": "PO20250206001",
"projectName": "办公设备采购项目(修改)",
"subjectName": "笔记本电脑",
"budgetAmount": 550000.00
}'
# 6. 删除记录
curl -X DELETE "http://localhost:8080/ccdi/purchaseTransaction/PO20250206001" \
-H "Authorization: Bearer $TOKEN"
```
### 2. 导入导出流程
```bash
# 1. 下载模板
curl -X POST "http://localhost:8080/ccdi/purchaseTransaction/importTemplate" \
-H "Authorization: Bearer $TOKEN" \
-o template.xlsx
# 2. 填写数据后导入
curl -X POST "http://localhost:8080/ccdi/purchaseTransaction/importData?updateSupport=false" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@data.xlsx"
# 响应: {"code":200,"msg":"导入任务已提交任务IDtask-xxx"}
# 3. 查询导入状态
curl -X GET "http://localhost:8080/ccdi/purchaseTransaction/importStatus/task-xxx" \
-H "Authorization: Bearer $TOKEN"
# 4. 如果有失败,查询失败记录
curl -X GET "http://localhost:8080/ccdi/purchaseTransaction/importFailures/task-xxx" \
-H "Authorization: Bearer $TOKEN"
# 5. 导出数据
curl -X POST "http://localhost:8080/ccdi/purchaseTransaction/export" \
-H "Authorization: Bearer $TOKEN" \
-d "projectName=办公设备" \
-o export_data.xlsx
```
### 3. Postman测试步骤
1. **创建环境变量**:
- `base_url`: http://localhost:8080
- `token`: (登录后获取)
2. **创建Pre-request Script**:
```javascript
// 自动设置token
if (!pm.environment.get("token")) {
const loginRequest = {
url: pm.environment.get("base_url") + "/login/test",
method: "POST",
header: {"Content-Type": "application/json"},
body: {
mode: "raw",
raw: JSON.stringify({username: "admin", password: "admin123"})
}
};
pm.sendRequest(loginRequest, (err, res) => {
pm.environment.set("token", res.json().token);
});
}
```
3. **设置Authorization**:
- Type: Bearer Token
- Token: `{{token}}`
4. **执行测试**:
- 按接口顺序执行
- 查看响应结果
- 验证数据正确性
---
## 附录
### A. 数据库表结构
表名: `ccdi_purchase_transaction`
| 字段名 | 类型 | 说明 | 备注 |
|--------|------|------|------|
| purchase_id | varchar(32) | 采购事项ID | 主键 |
| purchase_category | varchar(50) | 采购类别 | |
| project_name | varchar(200) | 项目名称 | |
| subject_name | varchar(200) | 标的物名称 | |
| subject_desc | varchar(500) | 标的物描述 | |
| purchase_qty | decimal(10,2) | 采购数量 | |
| budget_amount | decimal(15,2) | 预算金额 | |
| bid_amount | decimal(15,2) | 中标金额 | |
| actual_amount | decimal(15,2) | 实际采购金额 | |
| contract_amount | decimal(15,2) | 合同金额 | |
| settlement_amount | decimal(15,2) | 结算金额 | |
| purchase_method | varchar(50) | 采购方式 | |
| supplier_name | varchar(200) | 中标供应商名称 | |
| contact_person | varchar(50) | 供应商联系人 | |
| contact_phone | varchar(20) | 供应商联系电话 | |
| supplier_uscc | varchar(18) | 供应商统一信用代码 | |
| supplier_bank_account | varchar(50) | 供应商银行账户 | |
| apply_date | date | 采购申请日期 | |
| plan_approve_date | date | 采购计划批准日期 | |
| announce_date | date | 采购公告发布日期 | |
| bid_open_date | date | 开标日期 | |
| contract_sign_date | date | 合同签订日期 | |
| expected_delivery_date | date | 预计交货日期 | |
| actual_delivery_date | date | 实际交货日期 | |
| acceptance_date | date | 验收日期 | |
| settlement_date | date | 结算日期 | |
| applicant_id | varchar(20) | 申请人工号 | |
| applicant_name | varchar(50) | 申请人姓名 | |
| apply_department | varchar(100) | 申请部门 | |
| purchase_leader_id | varchar(20) | 采购负责人工号 | |
| purchase_leader_name | varchar(50) | 采购负责人姓名 | |
| purchase_department | varchar(100) | 采购部门 | |
| create_time | datetime | 创建时间 | 自动填充 |
| update_time | datetime | 更新时间 | 自动填充 |
| created_by | varchar(64) | 创建人 | 自动填充 |
| updated_by | varchar(64) | 更新人 | 自动填充 |
### B. 菜单权限配置
执行以下SQL配置菜单权限
```sql
-- 文件路径: sql/ccdi_purchase_transaction_menu.sql
-- 执行此文件以配置菜单和权限
source sql/ccdi_purchase_transaction_menu.sql;
```
### C. 前端API文件
前端API定义文件: `ruoyi-ui/src/api/ccdiPurchaseTransaction.js`
---
## 导入功能交互说明
### 前端交互流程
1. **上传文件**
- 用户点击"导入"按钮
- 选择Excel文件
- 点击"确定"上传
- **导入对话框立即关闭**
2. **后台处理**
- 右上角显示通知:"导入任务已提交,正在后台处理中,处理完成后将通知您"
- 系统每2秒轮询一次导入状态
3. **导入完成**
- 全部成功:显示绿色通知"导入完成!全部成功!共导入N条数据"
- 部分失败:显示橙色通知"导入完成!成功N条,失败M条"
- 如果有失败记录,操作栏显示"查看导入失败记录"按钮
4. **查看失败记录**
- 点击"查看导入失败记录"按钮
- 打开对话框显示分页的失败记录
- 包含字段:采购事项ID、项目名称、标的物名称、失败原因
- 支持清除历史记录
### 状态持久化
- 导入状态保存在localStorage中
- 刷新页面后仍可查看上次导入结果
- 状态保留7天,过期自动清除
### 与员工信息导入的对比
采购交易导入完全复用了员工信息导入的逻辑,两者的交互方式完全一致。
---
## 版本历史
| 版本 | 日期 | 说明 | 作者 |
|------|------|------|------|
| 1.0.0 | 2026-02-06 | 初始版本,采购交易信息管理接口 | ruoyi |
| 1.1.0 | 2026-02-08 | 添加导入功能交互说明 | ruoyi |
---
## 联系方式
如有问题,请联系开发团队。

View File

@@ -0,0 +1,430 @@
# 员工招聘信息管理 API文档
**模块名称:** ccdi-staff-recruitment
**版本:** 1.0
**生成日期:** 2025-02-05
**基础路径:** `/ccdi/staffRecruitment`
---
## 目录
1. [查询接口](#1-查询接口)
2. [操作接口](#2-操作接口)
3. [导入导出接口](#3-导入导出接口)
4. [数据模型](#4-数据模型)
5. [错误码说明](#5-错误码说明)
---
## 1. 查询接口
### 1.1 分页查询招聘信息列表
**接口描述:** 分页查询员工招聘信息列表,支持多条件筛选
**请求方式:** `GET`
**接口路径:** `/ccdi/staffRecruitment/list`
**权限标识:** `ccdi:staffRecruitment:list`
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|-------|------|------|------|--------|
| pageNum | Integer | 否 | 页码默认1 | 1 |
| pageSize | Integer | 否 | 每页条数默认10 | 10 |
| recruitName | String | 否 | 招聘项目名称(模糊查询) | 2025春季招聘 |
| posName | String | 否 | 职位名称(模糊查询) | 软件工程师 |
| candName | String | 否 | 候选人姓名(模糊查询) | 张三 |
| candId | String | 否 | 证件号码(精确查询) | 110101199001011234 |
| admitStatus | String | 否 | 录用状态(精确查询) | 录用/未录用/放弃 |
| interviewerName | String | 否 | 面试官姓名(模糊查询,查询面试官1或2) | 李四 |
| interviewerId | String | 否 | 面试官工号(精确查询,查询面试官1或2) | 10001 |
**响应示例:**
```json
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"recruitId": "REC20250205001",
"recruitName": "2025春季校园招聘",
"posName": "Java开发工程师",
"posCategory": "技术类",
"posDesc": "负责后端系统开发",
"candName": "张三",
"candEdu": "本科",
"candId": "110101199001011234",
"candSchool": "清华大学",
"candMajor": "计算机科学与技术",
"candGrad": "202506",
"admitStatus": "录用",
"admitStatusDesc": "已录用该候选人",
"interviewerName1": "李四",
"interviewerId1": "10001",
"interviewerName2": "王五",
"interviewerId2": "10002",
"createdBy": "admin",
"createTime": "2025-02-05 10:00:00",
"updatedBy": null,
"updateTime": null
}
],
"total": 100
}
```
### 1.2 查询招聘信息详情
**接口描述:** 根据招聘项目编号查询详细信息
**请求方式:** `GET`
**接口路径:** `/ccdi/staffRecruitment/{recruitId}`
**权限标识:** `ccdi:staffRecruitment:query`
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|-------|------|------|------|--------|
| recruitId | String | 是 | 招聘项目编号 | REC20250205001 |
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"recruitId": "REC20250205001",
"recruitName": "2025春季校园招聘",
"posName": "Java开发工程师",
"posCategory": "技术类",
"posDesc": "负责后端系统开发要求熟悉Spring Boot、MyBatis Plus等框架",
"candName": "张三",
"candEdu": "本科",
"candId": "110101199001011234",
"candSchool": "清华大学",
"candMajor": "计算机科学与技术",
"candGrad": "202506",
"admitStatus": "录用",
"admitStatusDesc": "已录用该候选人",
"interviewerName1": "李四",
"interviewerId1": "10001",
"interviewerName2": "王五",
"interviewerId2": "10002",
"createdBy": "admin",
"createTime": "2025-02-05 10:00:00",
"updatedBy": null,
"updateTime": null
}
}
```
---
## 2. 操作接口
### 2.1 新增招聘信息
**接口描述:** 新增一条员工招聘信息
**请求方式:** `POST`
**接口路径:** `/ccdi/staffRecruitment`
**权限标识:** `ccdi:staffRecruitment:add`
**请求体:**
```json
{
"recruitId": "REC20250205001",
"recruitName": "2025春季校园招聘",
"posName": "Java开发工程师",
"posCategory": "技术类",
"posDesc": "负责后端系统开发",
"candName": "张三",
"candEdu": "本科",
"candId": "110101199001011234",
"candSchool": "清华大学",
"candMajor": "计算机科学与技术",
"candGrad": "202506",
"admitStatus": "录用",
"interviewerName1": "李四",
"interviewerId1": "10001",
"interviewerName2": "王五",
"interviewerId2": "10002"
}
```
**字段校验规则:**
| 字段 | 校验规则 | 错误提示 |
|-----|---------|---------|
| recruitId | @NotBlank, @Size(max=32) | 招聘项目编号不能为空/长度不能超过32 |
| recruitName | @NotBlank, @Size(max=100) | 招聘项目名称不能为空/长度不能超过100 |
| posName | @NotBlank, @Size(max=100) | 职位名称不能为空/长度不能超过100 |
| posCategory | @NotBlank, @Size(max=50) | 职位类别不能为空/长度不能超过50 |
| posDesc | @NotBlank | 职位描述不能为空 |
| candName | @NotBlank, @Size(max=20) | 应聘人员姓名不能为空/长度不能超过20 |
| candEdu | @NotBlank, @Size(max=20) | 应聘人员学历不能为空/长度不能超过20 |
| candId | @NotBlank, @Pattern(身份证正则) | 证件号码不能为空/格式不正确 |
| candSchool | @NotBlank, @Size(max=50) | 应聘人员毕业院校不能为空/长度不能超过50 |
| candMajor | @NotBlank, @Size(max=30) | 应聘人员专业不能为空/长度不能超过30 |
| candGrad | @NotBlank, @Pattern(YYYYMM) | 毕业年月不能为空/格式不正确 |
| admitStatus | @NotBlank, @EnumValid | 录用情况不能为空/状态值不合法 |
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 2.2 修改招聘信息
**接口描述:** 修改已有的员工招聘信息
**请求方式:** `PUT`
**接口路径:** `/ccdi/staffRecruitment`
**权限标识:** `ccdi:staffRecruitment:edit`
**请求体:**
```json
{
"recruitId": "REC20250205001",
"recruitName": "2025春季校园招聘",
"posName": "Java开发工程师",
"posCategory": "技术类",
"posDesc": "负责后端系统开发,负责核心模块设计",
"candName": "张三",
"candEdu": "本科",
"candId": "110101199001011234",
"candSchool": "清华大学",
"candMajor": "计算机科学与技术",
"candGrad": "202506",
"admitStatus": "录用",
"interviewerName1": "李四",
"interviewerId1": "10001",
"interviewerName2": "王五",
"interviewerId2": "10002"
}
```
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
### 2.3 删除招聘信息
**接口描述:** 批量删除员工招聘信息
**请求方式:** `DELETE`
**接口路径:** `/ccdi/staffRecruitment/{recruitIds}`
**权限标识:** `ccdi:staffRecruitment:remove`
**路径参数:**
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|-------|------|------|------|--------|
| recruitIds | String[] | 是 | 招聘项目编号数组,多个用逗号分隔 | REC20250205001,REC20250205002 |
**响应示例:**
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
## 3. 导入导出接口
### 3.1 下载导入模板
**接口描述:** 下载Excel导入模板
**请求方式:** `POST`
**接口路径:** `/ccdi/staffRecruitment/importTemplate`
**权限标识:**
**响应:** Excel文件流
**模板字段顺序:**
| 序号 | 字段名 | 说明 | 必填 |
|-----|--------|------|------|
| 1 | 招聘项目编号 | 唯一标识 | 是 |
| 2 | 招聘项目名称 | - | 是 |
| 3 | 职位名称 | - | 是 |
| 4 | 职位类别 | - | 是 |
| 5 | 职位描述 | - | 是 |
| 6 | 应聘人员姓名 | - | 是 |
| 7 | 应聘人员学历 | - | 是 |
| 8 | 应聘人员证件号码 | 身份证号 | 是 |
| 9 | 应聘人员毕业院校 | - | 是 |
| 10 | 应聘人员专业 | - | 是 |
| 11 | 应聘人员毕业年月 | 格式:YYYYMM | 是 |
| 12 | 录用情况 | 录用/未录用/放弃 | 是 |
| 13 | 面试官1姓名 | - | 否 |
| 14 | 面试官1工号 | - | 否 |
| 15 | 面试官2姓名 | - | 否 |
| 16 | 面试官2工号 | - | 否 |
### 3.2 批量导入
**接口描述:** 通过Excel批量导入招聘信息
**请求方式:** `POST`
**接口路径:** `/ccdi/staffRecruitment/importData?updateSupport={updateSupport}`
**权限标识:** `ccdi:staffRecruitment:import`
**请求参数:**
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|-------|------|------|------|--------|
| updateSupport | Boolean | 否 | 是否更新已存在的数据 | true |
| file | File | 是 | Excel文件 | - |
**请求类型:** `multipart/form-data`
**响应示例 (成功):**
```json
{
"code": 200,
"msg": "恭喜您,数据已全部导入成功!共 10 条,数据类型:新增 8 条,更新 2 条"
}
```
**响应示例 (部分失败):**
```json
{
"code": 500,
"msg": "很抱歉,导入完成!成功 8 条,失败 2 条,错误如下:<br/>1、招聘项目编号 REC001 导入失败:该招聘项目编号已存在<br/>2、招聘项目编号 REC002 导入失败:证件号码格式不正确"
}
```
### 3.3 导出
**接口描述:** 导出招聘信息到Excel
**请求方式:** `POST`
**接口路径:** `/ccdi/staffRecruitment/export`
**权限标识:** `ccdi:staffRecruitment:export`
**请求参数:** 与分页查询接口相同的查询条件
**响应:** Excel文件流
---
## 4. 数据模型
### 4.1 录用状态枚举 (AdmitStatus)
| 枚举值 | 说明 |
|--------|------|
| 录用 | 已录用该候选人 |
| 未录用 | 未录用该候选人 |
| 放弃 | 候选人放弃 |
### 4.2 CcdiStaffRecruitmentVO
招聘信息返回对象,包含所有字段及状态描述。
### 4.3 CcdiStaffRecruitmentExcel
Excel导入导出对象,使用EasyExcel注解。
---
## 5. 错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 操作成功 |
| 400 | 参数校验失败 |
| 401 | 未授权,请先登录 |
| 403 | 无权限访问 |
| 404 | 资源不存在 |
| 409 | 主键冲突 |
| 500 | 服务器内部错误 |
### 常见业务错误
| 错误信息 | 说明 |
|---------|------|
| 该招聘项目编号已存在 | 新增时recruitId重复 |
| 招聘项目编号不能为空 | recruitId字段为空 |
| 证件号码格式不正确 | 身份证号格式验证失败 |
| 毕业年月格式不正确 | candGrad不是YYYYMM格式 |
| 录用情况状态值不合法 | admitStatus不是枚举值之一 |
---
## 附录
### Swagger UI
访问地址: `/swagger-ui/index.html`
### 测试账号
- 用户名: admin
- 密码: admin123
### Token获取
**接口:** POST `/login`
**请求体:**
```json
{
"username": "admin",
"password": "admin123"
}
```
**响应:**
```json
{
"code": 200,
"msg": "操作成功",
"token": "Bearer eyJhbGciOiJIUzUxMiJ9..."
}
```
---
**文档生成时间:** 2025-02-05
**文档版本:** 1.0

View File

@@ -0,0 +1,610 @@
# 中介黑名单管理 API 文档 v2.0
## 概述
中介黑名单管理模块提供个人和实体两类中介信息的增删改查、类型化模板下载和批量导入导出功能。
**基础路径**: `/ccdi/intermediary`
**权限标识前缀**: `ccdi:intermediary`
**文档版本**: v2.0
**更新日期**: 2026-02-04
---
## API 接口列表
### 1. 查询中介列表
**接口地址**: `GET /ccdi/intermediary/list`
**权限要求**: `ccdi:intermediary:list`
**请求参数** (Query Params):
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| name | String | 否 | 姓名/机构名称(模糊查询) |
| certificateNo | String | 否 | 证件号/统一社会信用代码(精确查询) |
| intermediaryType | String | 否 | 中介类型(1=个人, 2=实体) |
| pageNum | Integer | 否 | 页码(默认1) |
| pageSize | Integer | 否 | 每页数量(默认10) |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"rows": [
{
"bizId": "I202602040001",
"name": "张三",
"certificateNo": "110101199001011234",
"intermediaryType": "1",
"intermediaryTypeName": "个人",
"status": "0",
"statusName": "正常",
"remark": "测试数据",
"createBy": "admin",
"createTime": "2026-02-04 10:00:00"
}
],
"total": 1
}
```
**响应字段说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| bizId | String | 业务ID |
| name | String | 姓名/机构名称 |
| certificateNo | String | 证件号/统一社会信用代码 |
| intermediaryType | String | 中介类型(1=个人, 2=实体) |
| intermediaryTypeName | String | 中介类型名称 |
| status | String | 状态(0=正常, 1=停用) |
| statusName | String | 状态名称 |
| remark | String | 备注 |
| createBy | String | 创建人 |
| createTime | String | 创建时间 |
---
### 2. 查询个人中介详情
**接口地址**: `GET /ccdi/intermediary/person/{bizId}`
**权限要求**: `ccdi:intermediary:query`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| bizId | String | 是 | 业务ID |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"bizId": "I202602040001",
"name": "张三",
"certificateNo": "110101199001011234",
"intermediaryType": "1",
"intermediaryTypeName": "个人",
"status": "0",
"statusName": "正常",
"personType": "中介",
"personSubType": "本人",
"relationType": "正常",
"gender": "M",
"genderName": "男",
"idType": "身份证",
"personId": "110101199001011234",
"mobile": "13800138000",
"wechatNo": "zhangsan",
"contactAddress": "北京市朝阳区",
"company": "XX公司",
"socialCreditCode": "91110000123456789X",
"position": "经纪人",
"relatedNumId": "",
"relation": "",
"remark": "测试数据",
"createBy": "admin",
"createTime": "2026-02-04 10:00:00"
}
}
```
---
### 3. 查询实体中介详情
**接口地址**: `GET /ccdi/intermediary/entity/{socialCreditCode}`
**权限要求**: `ccdi:intermediary:query`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| socialCreditCode | String | 是 | 统一社会信用代码 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"bizId": "I202602040002",
"name": "XX中介公司",
"certificateNo": "91110000123456789X",
"intermediaryType": "2",
"intermediaryTypeName": "实体",
"status": "0",
"statusName": "正常",
"enterpriseName": "XX中介公司",
"socialCreditCode": "91110000123456789X",
"enterpriseType": "有限责任公司",
"enterpriseNature": "民企",
"industryClass": "房地产",
"industryName": "房地产业",
"establishDate": "2020-01-01",
"registerAddress": "北京市朝阳区",
"legalRepresentative": "张三",
"legalCertType": "身份证",
"legalCertNo": "110101199001011234",
"shareholder1": "李四",
"shareholder2": "王五",
"shareholder3": "",
"shareholder4": "",
"shareholder5": "",
"remark": "测试数据",
"createBy": "admin",
"createTime": "2026-02-04 10:00:00"
}
}
```
---
### 4. 新增个人中介
**接口地址**: `POST /ccdi/intermediary/person`
**权限要求**: `ccdi:intermediary:add`
**请求体** (application/json):
```json
{
"name": "张三",
"personType": "中介",
"personSubType": "本人",
"relationType": "正常",
"gender": "M",
"idType": "身份证",
"personId": "110101199001011234",
"mobile": "13800138000",
"wechatNo": "zhangsan",
"contactAddress": "北京市朝阳区",
"company": "XX公司",
"socialCreditCode": "91110000123456789X",
"position": "经纪人",
"relatedNumId": "",
"relation": "",
"remark": "测试数据"
}
```
**字段说明**:
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| name | String | 是 | 姓名(最大100字符) |
| personId | String | 是 | 证件号码(最大50字符) |
| personType | String | 否 | 人员类型 |
| personSubType | String | 否 | 人员子类型 |
| relationType | String | 否 | 关系类型 |
| gender | String | 否 | 性别(M=男, F=女, O=其他) |
| idType | String | 否 | 证件类型 |
| mobile | String | 否 | 手机号码(最大20字符) |
| wechatNo | String | 否 | 微信号(最大50字符) |
| contactAddress | String | 否 | 联系地址(最大200字符) |
| company | String | 否 | 所在公司(最大200字符) |
| socialCreditCode | String | 否 | 企业统一信用码(最大50字符) |
| position | String | 否 | 职位(最大100字符) |
| relatedNumId | String | 否 | 关联人员ID(最大50字符) |
| relation | String | 否 | 关联关系(最大50字符) |
| remark | String | 否 | 备注(最大500字符) |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 5. 新增实体中介
**接口地址**: `POST /ccdi/intermediary/entity`
**权限要求**: `ccdi:intermediary:add`
**请求体** (application/json):
```json
{
"enterpriseName": "XX中介公司",
"socialCreditCode": "91110000123456789X",
"enterpriseType": "有限责任公司",
"enterpriseNature": "民企",
"industryClass": "房地产",
"industryName": "房地产业",
"establishDate": "2020-01-01",
"registerAddress": "北京市朝阳区",
"legalRepresentative": "张三",
"legalCertType": "身份证",
"legalCertNo": "110101199001011234",
"shareholder1": "李四",
"shareholder2": "王五",
"shareholder3": "",
"shareholder4": "",
"shareholder5": "",
"remark": "测试数据"
}
```
**字段说明**:
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| enterpriseName | String | 是 | 机构名称(最大200字符) |
| socialCreditCode | String | 否 | 统一社会信用代码(最大50字符) |
| enterpriseType | String | 否 | 主体类型(最大50字符) |
| enterpriseNature | String | 否 | 企业性质(最大50字符) |
| industryClass | String | 否 | 行业分类(最大100字符) |
| industryName | String | 否 | 所属行业(最大100字符) |
| establishDate | Date | 否 | 成立日期 |
| registerAddress | String | 否 | 注册地址(最大500字符) |
| legalRepresentative | String | 否 | 法定代表人(最大100字符) |
| legalCertType | String | 否 | 法定代表人证件类型(最大50字符) |
| legalCertNo | String | 否 | 法定代表人证件号码(最大50字符) |
| shareholder1-5 | String | 否 | 股东信息(每个最大100字符) |
| remark | String | 否 | 备注(最大500字符) |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 6. 修改个人中介
**接口地址**: `PUT /ccdi/intermediary/person`
**权限要求**: `ccdi:intermediary:edit`
**请求体** (application/json):
```json
{
"bizId": "I202602040001",
"name": "张三",
"personType": "中介",
"personSubType": "本人",
"relationType": "正常",
"gender": "M",
"idType": "身份证",
"personId": "110101199001011234",
"mobile": "13800138000",
"wechatNo": "zhangsan",
"contactAddress": "北京市朝阳区",
"company": "XX公司",
"socialCreditCode": "91110000123456789X",
"position": "经纪人",
"relatedNumId": "",
"relation": "",
"remark": "测试数据"
}
```
**字段说明**: 与新增个人中介相同,bizId为必填项
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 7. 修改实体中介
**接口地址**: `PUT /ccdi/intermediary/entity`
**权限要求**: `ccdi:intermediary:edit`
**请求体** (application/json):
```json
{
"socialCreditCode": "91110000123456789X",
"enterpriseName": "XX中介公司",
"enterpriseType": "有限责任公司",
"enterpriseNature": "民企",
"industryClass": "房地产",
"industryName": "房地产业",
"establishDate": "2020-01-01",
"registerAddress": "北京市朝阳区",
"legalRepresentative": "张三",
"legalCertType": "身份证",
"legalCertNo": "110101199001011234",
"shareholder1": "李四",
"shareholder2": "王五",
"shareholder3": "",
"shareholder4": "",
"shareholder5": "",
"remark": "测试数据"
}
```
**字段说明**: 与新增实体中介相同,socialCreditCode为必填项
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 8. 删除中介
**接口地址**: `DELETE /ccdi/intermediary/{ids}`
**权限要求**: `ccdi:intermediary:remove`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | String[] | 是 | 业务ID数组(逗号分隔) |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 9. 校验人员ID唯一性
**接口地址**: `GET /ccdi/intermediary/checkPersonIdUnique`
**权限要求**: 无
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| personId | String | 是 | 证件号码 |
| bizId | String | 否 | 排除的业务ID(修改时使用) |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": true
}
```
**data字段说明**: true=唯一可用, false=已存在
---
### 10. 校验统一社会信用代码唯一性
**接口地址**: `GET /ccdi/intermediary/checkSocialCreditCodeUnique`
**权限要求**: 无
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| socialCreditCode | String | 是 | 统一社会信用代码 |
| excludeId | String | 否 | 排除的ID(修改时使用) |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": true
}
```
**data字段说明**: true=唯一可用, false=已存在
---
### 11. 下载个人中介导入模板
**接口地址**: `POST /ccdi/intermediary/importPersonTemplate`
**权限要求**: 无
**响应**: Excel模板文件下载
**Excel格式说明**:
**Sheet1: 个人中介信息**
| 姓名 | 人员类型 | 人员子类型 | 关系类型 | 性别▼ | 证件类型▼ | 证件号码 | 手机号码 | 微信号 | 联系地址 | 所在公司 | 企业统一信用码 | 职位 | 关联人员ID | 关联关系 | 备注 |
|------|---------|-----------|---------|-------|-----------|---------|---------|--------|---------|---------|--------------|-----|-----------|---------|------|
| 张三 | 中介 | 本人 | 正常 | 男 | 身份证 | 110101199001011234 | 13800138000 | zhangsan | 北京市朝阳区 | XX公司 | 91110000XXXXXXXXXX | 经纪人 | - | - | 测试 |
**注**: 带▼标记的列包含下拉框,选项来自字典
---
### 12. 下载实体中介导入模板
**接口地址**: `POST /ccdi/intermediary/importEntityTemplate`
**权限要求**: 无
**响应**: Excel模板文件下载
**Excel格式说明**:
**Sheet1: 实体中介信息**
| 机构名称 | 统一社会信用代码 | 主体类型▼ | 企业性质▼ | 行业分类 | 所属行业 | 成立日期 | 注册地址 | 法定代表人 | 法定代表人证件类型 | 法定代表人证件号码 | 股东1 | 股东2 | 股东3 | 股东4 | 股东5 | 备注 |
|---------|-----------------|-----------|-----------|---------|---------|---------|---------|-----------|-------------------|-------------------|-------|-------|-------|-------|-------|------|
| XX公司 | 91110000XXXXXXXXXX | 有限责任公司 | 民企 | 房地产 | 房地产业 | 2020-01-01 | 北京市朝阳区 | 张三 | 身份证 | 110101199001011234 | 李四 | 王五 | - | - | - | - |
---
### 13. 导入个人中介数据
**接口地址**: `POST /ccdi/intermediary/importPersonData`
**权限要求**: `ccdi:intermediary:import`
**请求参数** (multipart/form-data):
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| file | File | 是 | Excel文件 |
| updateSupport | Boolean | 否 | 是否更新已存在数据(默认false) |
**响应示例**:
```json
{
"code": 200,
"msg": "恭喜您,数据已全部导入成功!共10条"
}
```
---
### 14. 导入实体中介数据
**接口地址**: `POST /ccdi/intermediary/importEntityData`
**权限要求**: `ccdi:intermediary:import`
**请求参数** (multipart/form-data):
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| file | File | 是 | Excel文件 |
| updateSupport | Boolean | 否 | 是否更新已存在数据(默认false) |
**响应示例**:
```json
{
"code": 200,
"msg": "恭喜您,数据已全部导入成功!共10条"
}
```
---
## 字典数据说明
导入模板中的下拉框选项来自系统字典管理,相关字典类型:
| 字典类型 | 字典名称 | 用途 |
|---------|---------|------|
| ccdi_indiv_gender | 个人中介性别 | 个人中介模板性别下拉框 |
| ccdi_certificate_type | 证件类型 | 个人中介模板证件类型下拉框 |
| ccdi_entity_type | 主体类型 | 机构中介模板主体类型下拉框 |
| ccdi_enterprise_nature | 企业性质 | 机构中介模板企业性质下拉框 |
| ccdi_data_source | 数据来源 | 数据来源字段映射 |
---
## 错误码说明
| HTTP状态码 | 错误码 | 说明 |
|-----------|--------|------|
| 200 | 200 | 操作成功 |
| 401 | 401 | 未授权,请先登录 |
| 403 | 403 | 无权限访问 |
| 500 | 500 | 服务器内部错误 |
---
## 业务错误信息
| 错误信息 | 说明 |
|----------|------|
| 姓名不能为空 | 个人中介新增/修改时姓名为空 |
| 机构名称不能为空 | 实体中介新增/修改时机构名称为空 |
| 证件号码不能为空 | 个人中介新增/修改时证件号码为空 |
| 该证件号已存在 | 新增/导入时证件号重复 |
| 该统一社会信用代码已存在 | 新增/导入时信用代码重复 |
| 姓名长度不能超过100个字符 | 姓名超长 |
| 证件号码长度不能超过50个字符 | 证件号码超长 |
| 机构名称长度不能超过200个字符 | 机构名称超长 |
---
## 测试账号
- 用户名: `admin`
- 密码: `admin123`
测试前请先调用 `/login/test` 接口获取Token。
---
## 更新日志
| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0.0 | 2026-01-29 | 初始版本,支持个人和机构分类管理 |
| 1.1.0 | 2026-01-29 | 添加字典下拉框功能,分离个人/机构模板 |
| 1.2.0 | 2026-01-29 | 修改接口分离:新增个人/机构专用修改接口,修复中介类型修改问题 |
| 1.3.0 | 2026-01-29 | 新增接口分离:新增个人/机构专用新增接口,统一接口设计 |
| 2.0.0 | 2026-02-04 | 重构版本:使用MyBatis Plus,分离DTO/VO,统一业务ID(bizId),优化查询接口 |
---
## 主要变更说明 (v2.0)
### 架构变更
- 使用MyBatis Plus替代原生MyBatis
- 分离DTO(请求)和VO(响应)对象
- 统一使用业务ID(bizId)作为主键
### 接口变更
- 查询详情接口分离为个人和实体两个接口
- 新增接口分离为个人和实体两个接口
- 修改接口分离为个人和实体两个接口
- 新增唯一性校验接口
### 数据模型变更
- 个人中介使用`personId`作为证件号字段
- 实体中介使用`socialCreditCode`作为统一社会信用代码字段
- 删除了`intermediaryId`,统一使用`bizId`
### 查询功能增强
- 支持按中介类型查询
- 支持按姓名/机构名称模糊查询
- 支持按证件号/统一社会信用代码精确查询

View File

@@ -0,0 +1,726 @@
# 中介黑名单管理 API 文档 v2.0
## 概述
中介黑名单管理模块提供个人和机构两类中介信息的增删改查、类型化模板下载和批量导入导出功能。
**基础路径**: `/ccdi/intermediary`
**权限标识前缀**: `ccdi:intermediary`
**技术栈**: Spring Boot 3 + MyBatis Plus + MySQL
---
## API 接口列表
### 1. 查询中介黑名单列表
**接口地址**: `GET /ccdi/intermediary/list`
**权限要求**: `ccdi:intermediary:list`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| name | String | 否 | 姓名/机构名称(模糊查询) |
| certificateNo | String | 否 | 证件号/统一社会信用代码(精确查询) |
| intermediaryType | String | 否 | 中介类型1=个人, 2=机构) |
| pageNum | Integer | 否 | 页码默认1 |
| pageSize | Integer | 否 | 每页数量默认10 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"rows": [
{
"id": "abc123",
"name": "张三",
"certificateNo": "110101199001011234",
"intermediaryType": "1",
"personType": "中介",
"company": "XX公司",
"dataSource": "MANUAL",
"createTime": "2026-02-04 10:00:00",
"updateTime": "2026-02-05 14:30:00"
}
],
"total": 1
}
```
**响应字段说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| id | String | ID个人为bizId实体为socialCreditCode |
| name | String | 姓名/机构名称 |
| certificateNo | String | 证件号/统一社会信用代码 |
| intermediaryType | String | 中介类型1=个人, 2=实体) |
| personType | String | 人员类型/实体 |
| company | String | 公司/机构名称 |
| dataSource | String | 数据来源MANUAL=手动, IMPORT=导入, API=接口) |
| createTime | Date | 创建时间 |
| updateTime | Date | 修改时间 |
---
### 2. 查询个人中介详情
**接口地址**: `GET /ccdi/intermediary/person/{bizId}`
**权限要求**: `ccdi:intermediary:query`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| bizId | String | 是 | 人员业务ID |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"bizId": "abc123xyz456",
"intermediaryType": "1",
"name": "张三",
"personType": "房产中介",
"personSubType": "本人",
"gender": "M",
"idType": "身份证",
"personId": "110101199001011234",
"mobile": "13800138000",
"wechatNo": "zhangsan_wx",
"contactAddress": "北京市朝阳区XX路XX号",
"company": "XX房产中介公司",
"position": "经纪人",
"socialCreditCode": "91110000XXXXXXXXXX",
"relatedNumId": "rel123",
"relationType": "配偶",
"dataSource": "MANUAL",
"remark": "测试数据",
"createTime": "2026-02-04 10:00:00"
}
}
```
**响应字段说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| bizId | String | 人员业务ID |
| intermediaryType | String | 中介类型(固定为"1" |
| name | String | 姓名 |
| personType | String | 人员类型(房产中介、贷款中介等) |
| personSubType | String | 人员子类型(本人、配偶、父亲等) |
| gender | String | 性别M=男, F=女, O=其他) |
| idType | String | 证件类型(身份证、护照等) |
| personId | String | 证件号码 |
| mobile | String | 手机号码 |
| wechatNo | String | 微信号 |
| contactAddress | String | 联系地址 |
| company | String | 所在公司 |
| position | String | 职位 |
| socialCreditCode | String | 企业统一信用码 |
| relatedNumId | String | 关联人员ID |
| relationType | String | 关联关系(配偶、父子、母女等) |
| dataSource | String | 数据来源 |
| remark | String | 备注 |
| createTime | Date | 创建时间 |
---
### 3. 查询实体中介详情
**接口地址**: `GET /ccdi/intermediary/entity/{socialCreditCode}`
**权限要求**: `ccdi:intermediary:query`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| socialCreditCode | String | 是 | 统一社会信用代码 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"socialCreditCode": "91110000XXXXXXXXXX",
"intermediaryType": "2",
"enterpriseName": "XX中介公司",
"enterpriseType": "有限责任公司",
"enterpriseNature": "民企",
"industryClass": "房地产",
"industryName": "房地产业",
"establishDate": "2020-01-01",
"registerAddress": "北京市朝阳区XX路XX号",
"legalRepresentative": "张三",
"legalCertType": "身份证",
"legalCertNo": "110101199001011234",
"shareholder1": "李四",
"shareholder2": "王五",
"shareholder3": "赵六",
"shareholder4": null,
"shareholder5": null,
"dataSource": "MANUAL",
"remark": "测试数据",
"createTime": "2026-02-04 10:00:00"
}
}
```
**响应字段说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| socialCreditCode | String | 统一社会信用代码 |
| intermediaryType | String | 中介类型(固定为"2" |
| enterpriseName | String | 机构名称 |
| enterpriseType | String | 主体类型 |
| enterpriseNature | String | 企业性质 |
| industryClass | String | 行业分类 |
| industryName | String | 所属行业 |
| establishDate | Date | 成立日期 |
| registerAddress | String | 注册地址 |
| legalRepresentative | String | 法定代表人 |
| legalCertType | String | 法定代表人证件类型 |
| legalCertNo | String | 法定代表人证件号码 |
| shareholder1-5 | String | 股东信息 |
| dataSource | String | 数据来源 |
| remark | String | 备注 |
| createTime | Date | 创建时间 |
---
### 4. 新增个人中介
**接口地址**: `POST /ccdi/intermediary/person`
**权限要求**: `ccdi:intermediary:add`
**请求体**:
```json
{
"name": "张三",
"personType": "房产中介",
"personSubType": "本人",
"gender": "M",
"idType": "身份证",
"personId": "110101199001011234",
"mobile": "13800138000",
"wechatNo": "zhangsan_wx",
"contactAddress": "北京市朝阳区XX路XX号",
"company": "XX房产中介公司",
"position": "经纪人",
"socialCreditCode": "91110000XXXXXXXXXX",
"relatedNumId": "rel123",
"relationType": "配偶",
"remark": "测试数据"
}
```
**字段说明**:
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| name | String | 是 | 姓名1-100字符 |
| personId | String | 是 | 证件号码不超过50字符 |
| personType | String | 否 | 人员类型(枚举值,见下文) |
| personSubType | String | 否 | 人员子类型(枚举值,见下文) |
| gender | String | 否 | 性别M=男, F=女, O=其他) |
| idType | String | 否 | 证件类型(枚举值,见下文) |
| mobile | String | 否 | 手机号码不超过20字符 |
| wechatNo | String | 否 | 微信号不超过50字符 |
| contactAddress | String | 否 | 联系地址不超过200字符 |
| company | String | 否 | 所在公司不超过200字符 |
| position | String | 否 | 职位不超过100字符 |
| socialCreditCode | String | 否 | 企业统一信用码不超过50字符 |
| relatedNumId | String | 否 | 关联人员ID不超过50字符 |
| relationType | String | 否 | 关联关系(枚举值,见下文) |
| remark | String | 否 | 备注不超过500字符 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 5. 新增实体中介
**接口地址**: `POST /ccdi/intermediary/entity`
**权限要求**: `ccdi:intermediary:add`
**请求体**:
```json
{
"enterpriseName": "XX中介公司",
"socialCreditCode": "91110000XXXXXXXXXX",
"enterpriseType": "有限责任公司",
"enterpriseNature": "民企",
"industryClass": "房地产",
"industryName": "房地产业",
"establishDate": "2020-01-01",
"registerAddress": "北京市朝阳区XX路XX号",
"legalRepresentative": "张三",
"legalCertType": "身份证",
"legalCertNo": "110101199001011234",
"shareholder1": "李四",
"shareholder2": "王五",
"shareholder3": "赵六",
"shareholder4": null,
"shareholder5": null,
"remark": "测试数据"
}
```
**字段说明**:
| 字段名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| enterpriseName | String | 是 | 机构名称1-200字符 |
| socialCreditCode | String | 是 | 统一社会信用代码不超过50字符 |
| enterpriseType | String | 否 | 主体类型(枚举值,见下文) |
| enterpriseNature | String | 否 | 企业性质(枚举值,见下文) |
| industryClass | String | 否 | 行业分类不超过100字符 |
| industryName | String | 否 | 所属行业不超过100字符 |
| establishDate | Date | 否 | 成立日期yyyy-MM-dd |
| registerAddress | String | 否 | 注册地址不超过500字符 |
| legalRepresentative | String | 否 | 法定代表人不超过100字符 |
| legalCertType | String | 否 | 法定代表人证件类型(枚举值) |
| legalCertNo | String | 否 | 法定代表人证件号码不超过50字符 |
| shareholder1-5 | String | 否 | 股东信息每个不超过100字符 |
| remark | String | 否 | 备注不超过500字符 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 6. 修改个人中介
**接口地址**: `PUT /ccdi/intermediary/person`
**权限要求**: `ccdi:intermediary:edit`
**请求体**:
```json
{
"bizId": "abc123xyz456",
"name": "张三",
"personType": "房产中介",
"personSubType": "本人",
"gender": "M",
"idType": "身份证",
"personId": "110101199001011234",
"mobile": "13800138000",
"wechatNo": "zhangsan_wx",
"contactAddress": "北京市朝阳区XX路XX号",
"company": "XX房产中介公司",
"position": "经纪人",
"socialCreditCode": "91110000XXXXXXXXXX",
"relatedNumId": "rel123",
"relationType": "配偶",
"remark": "测试数据"
}
```
**字段说明**: 与新增接口相同,但 `bizId` 为必填项。
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 7. 修改实体中介
**接口地址**: `PUT /ccdi/intermediary/entity`
**权限要求**: `ccdi:intermediary:edit`
**请求体**:
```json
{
"socialCreditCode": "91110000XXXXXXXXXX",
"enterpriseName": "XX中介公司",
"enterpriseType": "有限责任公司",
"enterpriseNature": "民企",
"industryClass": "房地产",
"industryName": "房地产业",
"establishDate": "2020-01-01",
"registerAddress": "北京市朝阳区XX路XX号",
"legalRepresentative": "张三",
"legalCertType": "身份证",
"legalCertNo": "110101199001011234",
"shareholder1": "李四",
"shareholder2": "王五",
"shareholder3": "赵六",
"shareholder4": null,
"shareholder5": null,
"remark": "测试数据"
}
```
**字段说明**: 与新增接口相同。
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 8. 删除中介
**接口地址**: `DELETE /ccdi/intermediary/{ids}`
**权限要求**: `ccdi:intermediary:remove`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| ids | String[] | 是 | ID数组个人为bizId实体为socialCreditCode |
**示例**: `/ccdi/intermediary/abc123,91110000XXXXXXXXXX`
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 9. 校验人员ID唯一性
**接口地址**: `GET /ccdi/intermediary/checkPersonIdUnique`
**权限要求**: 无
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| personId | String | 是 | 证件号码 |
| bizId | String | 否 | 排除的人员ID修改时使用 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": true
}
```
**响应说明**: `true` 表示唯一,`false` 表示已存在。
---
### 10. 校验统一社会信用代码唯一性
**接口地址**: `GET /ccdi/intermediary/checkSocialCreditCodeUnique`
**权限要求**: 无
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| socialCreditCode | String | 是 | 统一社会信用代码 |
| excludeId | String | 否 | 排除的ID修改时使用 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": true
}
```
**响应说明**: `true` 表示唯一,`false` 表示已存在。
---
## 枚举接口
### 获取人员类型选项
**接口地址**: `GET /ccdi/enum/indivType`
**权限要求**: 无
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{ "value": "房产中介", "label": "房产中介" },
{ "value": "贷款中介", "label": "贷款中介" },
{ "value": "职业背债人", "label": "职业背债人" },
{ "value": "担保中介", "label": "担保中介" },
{ "value": "评估中介", "label": "评估中介" }
]
}
```
---
### 获取人员子类型选项
**接口地址**: `GET /ccdi/enum/indivSubType`
**权限要求**: 无
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{ "value": "本人", "label": "本人" },
{ "value": "配偶", "label": "配偶" },
{ "value": "父亲", "label": "父亲" },
{ "value": "母亲", "label": "母亲" },
{ "value": "兄弟", "label": "兄弟" },
{ "value": "姐妹", "label": "姐妹" },
{ "value": "子女", "label": "子女" }
]
}
```
---
### 获取性别选项
**接口地址**: `GET /ccdi/enum/gender`
**权限要求**: 无
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{ "value": "M", "label": "男" },
{ "value": "F", "label": "女" },
{ "value": "O", "label": "其他" }
]
}
```
---
### 获取证件类型选项
**接口地址**: `GET /ccdi/enum/certType`
**权限要求**: 无
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{ "value": "身份证", "label": "身份证" },
{ "value": "护照", "label": "护照" },
{ "value": "港澳通行证", "label": "港澳通行证" },
{ "value": "台湾通行证", "label": "台湾通行证" }
]
}
```
---
### 获取关联关系选项
**接口地址**: `GET /ccdi/enum/relationType`
**权限要求**: 无
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{ "value": "配偶", "label": "配偶" },
{ "value": "父子", "label": "父子" },
{ "value": "母女", "label": "母女" },
{ "value": "兄弟", "label": "兄弟" },
{ "value": "姐妹", "label": "姐妹" },
{ "value": "亲属", "label": "亲属" },
{ "value": "朋友", "label": "朋友" },
{ "value": "同事", "label": "同事" }
]
}
```
---
### 获取主体类型选项
**接口地址**: `GET /ccdi/enum/corpType`
**权限要求**: 无
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{ "value": "有限责任公司", "label": "有限责任公司" },
{ "value": "股份有限公司", "label": "股份有限公司" },
{ "value": "个体工商户", "label": "个体工商户" },
{ "value": "合伙企业", "label": "合伙企业" },
{ "value": "个人独资企业", "label": "个人独资企业" }
]
}
```
---
### 获取企业性质选项
**接口地址**: `GET /ccdi/enum/corpNature`
**权限要求**: 无
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{ "value": "国企", "label": "国企" },
{ "value": "民企", "label": "民企" },
{ "value": "外企", "label": "外企" },
{ "value": "合资", "label": "合资" }
]
}
```
---
### 获取数据来源选项
**接口地址**: `GET /ccdi/enum/dataSource`
**权限要求**: 无
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": [
{ "value": "MANUAL", "label": "手动录入" },
{ "value": "SYSTEM", "label": "系统同步" },
{ "value": "IMPORT", "label": "批量导入" },
{ "value": "API", "label": "接口获取" }
]
}
```
---
## 错误码说明
| HTTP状态码 | 错误码 | 说明 |
|-----------|--------|------|
| 200 | 200 | 操作成功 |
| 401 | 401 | 未授权,请先登录 |
| 403 | 403 | 无权限访问 |
| 500 | 500 | 服务器内部错误 |
## 业务错误信息
| 错误信息 | 说明 |
|----------|------|
| 姓名不能为空 | 新增/修改时姓名为空 |
| 证件号码不能为空 | 新增时证件号码为空 |
| 该证件号已存在 | 新增/导入时证件号重复 |
| 该统一社会信用代码已存在 | 新增/导入时信用代码重复 |
| 姓名长度不能超过100个字符 | 姓名超长 |
| 证件号长度不能超过50个字符 | 证件号超长 |
## 测试账号
- **用户名**: `admin`
- **密码**: `admin123`
**获取Token**: 调用 `POST /login/test` 接口获取Token后续请求在 Header 中添加:
```
Authorization: Bearer {token}
```
## 更新日志
| 版本 | 日期 | 说明 |
|------|------|------|
| 2.0.0 | 2026-02-05 | 统一字段命名,使用接口枚举,更新文档与实际代码一致 |
| 1.3.0 | 2026-01-29 | 新增接口分离:个人/机构专用新增接口 |
| 1.2.0 | 2026-01-29 | 修改接口分离:个人/机构专用修改接口 |
| 1.1.0 | 2026-01-29 | 添加字典下拉框功能 |
| 1.0.0 | 2026-01-29 | 初始版本 |
## 注意事项
1. **中介类型字段**:
- 个人中介:`intermediaryType = "1"`
- 实体中介:`intermediaryType = "2"`
2. **枚举值使用**:
- 所有下拉选项字段应使用枚举接口返回的 `value`
- 不要硬编码或使用字典表的 `dictValue`
3. **数据来源字段**:
- 手动录入:`MANUAL`
- 系统同步:`SYSTEM`
- 批量导入:`IMPORT`
- 接口获取:`API`
4. **分页排序**:
- 列表查询默认按 `updateTime` 降序排列
- 使用 MyBatis Plus 分页插件
5. **ID字段**:
- 个人中介使用 `bizId` 作为唯一标识
- 实体中介使用 `socialCreditCode` 作为唯一标识
6. **批量操作**:
- 删除接口支持同时删除个人和实体中介
- 根据ID长度自动判断类型个人ID较长

View File

@@ -0,0 +1,271 @@
# 中介黑名单管理API测试报告
## 测试概述
**测试时间:** 2026-01-29 16:43:11
**测试环境:** http://localhost:8080
**测试账号:** admin
**测试脚本:** [test_intermediary_blacklist.sh](../scripts/test_intermediary_blacklist.sh)
**测试通过率:** 100.00%
## 测试结果汇总
| 指标 | 数值 |
|------|------|
| 测试场景总数 | 11 |
| 通过数量 | 11 |
| 失败数量 | 0 |
| 通过率 | 100.00% |
## 测试用例详情
### 1. 登录测试
**接口:** `POST /login/test`
**描述:** 使用测试账号登录获取认证token
**请求参数:**
```json
{
"username": "admin",
"password": "admin123"
}
```
**测试结果:** ✅ 通过
- 成功获取token
- token格式正确
---
### 2. 查询中介黑名单列表
**接口:** `GET /ccdi/intermediary/list`
**描述:** 分页查询中介黑名单列表
**请求参数:**
- pageNum: 1
- pageSize: 10
**测试结果:** ✅ 通过
- 返回分页数据结构正确
- 包含 total 和 rows 字段
- 数据格式符合预期
---
### 3. 新增个人中介黑名单
**接口:** `POST /ccdi/intermediary`
**描述:** 新增个人类型的中介黑名单记录
**请求参数:**
```json
{
"name": "测试个人中介_20260129_164311",
"certificateNo": "TESTCERT20260129_164311",
"intermediaryType": "1",
"remark": "自动化测试数据"
}
```
**测试结果:** ✅ 通过
- 成功创建记录
- 返回状态码 200
- 成功获取到新创建的ID: 2005
---
### 4. 新增机构中介黑名单
**接口:** `POST /ccdi/intermediary`
**描述:** 新增机构类型的中介黑名单记录
**请求参数:**
```json
{
"name": "测试机构中介_20260129_164311",
"certificateNo": "TESTORG20260129_164311",
"intermediaryType": "2",
"remark": "自动化测试机构数据"
}
```
**测试结果:** ✅ 通过
- 成功创建记录
- 返回状态码 200
- 成功获取到新创建的ID: 2006
---
### 5. 获取中介详情
**接口:** `GET /ccdi/intermediary/{intermediaryId}`
**描述:** 根据ID获取中介详细信息
**请求参数:**
- intermediaryId: 2005
**测试结果:** ✅ 通过
- 成功获取详情信息
- 返回完整的数据结构
- 包含所有必要字段
---
### 6. 修改中介黑名单
**接口:** `PUT /ccdi/intermediary`
**描述:** 修改已存在的中介信息
**请求参数:**
```json
{
"intermediaryId": 2005,
"name": "测试个人中介_修改",
"certificateNo": "TESTCERT20260129_164311",
"intermediaryType": "1",
"status": "1",
"remark": "修改后的自动化测试数据"
}
```
**测试结果:** ✅ 通过
- 成功更新记录
- 返回状态码 200
- 数据修改生效
---
### 7. 导出中介黑名单列表
**接口:** `POST /ccdi/intermediary/export`
**描述:** 导出中介黑名单数据为Excel文件
**请求参数:**
```json
{}
```
**测试结果:** ✅ 通过
- 成功导出Excel文件
- 文件格式正确
- 文件保存至: test_output/test6_export.xlsx
---
### 8. 下载个人中介导入模板
**接口:** `POST /ccdi/intermediary/importPersonTemplate`
**描述:** 下载个人中介导入Excel模板
**测试结果:** ✅ 通过
- 成功下载模板文件
- 文件格式正确
- 文件保存至: test_output/test7_person_template.xlsx
---
### 9. 下载机构中介导入模板
**接口:** `POST /ccdi/intermediary/importEntityTemplate`
**描述:** 下载机构中介导入Excel模板
**测试结果:** ✅ 通过
- 成功下载模板文件
- 文件格式正确
- 文件保存至: test_output/test8_entity_template.xlsx
---
### 10. 条件查询(按中介类型)
**接口:** `GET /ccdi/intermediary/list`
**描述:** 按中介类型筛选查询
**请求参数:**
- pageNum: 1
- pageSize: 10
- intermediaryType: 1 (个人)
**测试结果:** ✅ 通过
- 查询结果正确
- 数据筛选生效
- 返回指定类型的数据
---
### 11. 条件查询(按状态)
**接口:** `GET /ccdi/intermediary/list`
**描述:** 按状态筛选查询
**请求参数:**
- pageNum: 1
- pageSize: 10
- status: 1
**测试结果:** ✅ 通过
- 查询结果正确
- 数据筛选生效
- 返回指定状态的数据
---
### 12. 删除中介黑名单
**接口:** `DELETE /ccdi/intermediary/{intermediaryIds}`
**描述:** 批量删除中介黑名单记录
**请求参数:**
- intermediaryIds: 2005,2006
**测试结果:** ✅ 通过
- 成功删除记录
- 返回状态码 200
- 数据删除生效
---
## 测试文件清单
### 响应JSON文件
- `test1_list_response.json` - 查询列表响应
- `test2_add_person_response.json` - 新增个人中介响应
- `test3_add_entity_response.json` - 新增机构中介响应
- `test4_get_info_response.json` - 获取详情响应
- `test5_edit_response.json` - 修改中介响应
- `test9_remove_response.json` - 删除中介响应
- `test10_query_by_type_response.json` - 按类型查询响应
- `test11_query_by_status_response.json` - 按状态查询响应
### Excel文件
- `test6_export.xlsx` - 导出的数据文件
- `test7_person_template.xlsx` - 个人中介导入模板
- `test8_entity_template.xlsx` - 机构中介导入模板
### 报告文件
- `test_report_20260129_164311.txt` - 详细测试日志
## 结论
**所有测试用例均已通过中介黑名单管理API功能完整且运行正常。**
### 主要验证点
1. ✅ 认证授权机制正常
2. ✅ CRUD操作功能完整
3. ✅ 分页查询功能正常
4. ✅ 条件筛选功能正常
5. ✅ 文件导入导出功能正常
6. ✅ 批量操作功能正常
### 建议
1. 建议在实际部署前进行压力测试
2. 建议添加更多的边界条件测试用例
3. 建议完善错误码和错误信息的文档
---
**报告生成时间:** 2026-01-29 16:43:11
**测试工具:** curl + bash
**报告生成者:** Claude Code

View File

@@ -0,0 +1,316 @@
# 员工信息管理 API 文档
## 概述
员工信息管理模块提供员工信息的增删改查、批量导入导出功能。
**基础路径**: `/ccdi/employee`
**权限标识前缀**: `ccdi:employee`
**重要更新**: 自2026-02-05起,员工ID(employeeId)作为柜员号使用,为7位数字,手动输入,唯一不可重复。
---
## API 接口列表
### 1. 查询员工列表
**接口地址**: `GET /ccdi/employee/list`
**权限要求**: `ccdi:employee:list`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| name | String | 否 | 姓名(模糊查询) |
| employeeId | Long | 否 | 员工ID(柜员号,精确查询,7位数字) |
| deptId | Long | 否 | 所属部门ID |
| idCard | String | 否 | 身份证号(精确查询) |
| status | String | 否 | 状态(0=在职, 1=离职) |
| pageNum | Integer | 否 | 页码(默认1) |
| pageSize | Integer | 否 | 每页数量(默认10) |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"rows": [
{
"employeeId": 1000001,
"name": "张三",
"deptId": 100,
"deptName": "总部",
"idCard": "110101199001011234",
"phone": "13800138000",
"hireDate": "2020-01-01",
"status": "0",
"statusDesc": "在职",
"createTime": "2026-01-28 10:00:00"
}
],
"total": 1
}
```
**响应字段说明**:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| employeeId | Long | 员工ID(柜员号,7位数字) |
| name | String | 姓名 |
| deptId | Long | 所属部门ID |
| deptName | String | 所属部门名称(关联 sys_dept 表) |
| idCard | String | 身份证号 |
| phone | String | 电话 |
| hireDate | Date | 入职时间 |
| status | String | 状态(0=在职, 1=离职) |
| statusDesc | String | 状态描述 |
| createTime | Date | 创建时间 |
---
### 2. 查询员工详情
**接口地址**: `GET /ccdi/employee/{employeeId}`
**权限要求**: `ccdi:employee:query`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| employeeId | Long | 是 | 员工ID(柜员号) |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功",
"data": {
"employeeId": 1000001,
"name": "张三",
"deptId": 100,
"idCard": "110101199001011234",
"phone": "13800138000",
"hireDate": "2020-01-01",
"status": "0",
"statusDesc": "在职",
"createTime": "2026-01-28 10:00:00"
}
}
```
---
### 3. 新增员工
**接口地址**: `POST /ccdi/employee`
**权限要求**: `ccdi:employee:add`
**请求头**:
```
Content-Type: application/json
Authorization: Bearer {token}
```
**请求体**:
```json
{
"employeeId": 1000001,
"name": "张三",
"deptId": 100,
"idCard": "110101199001011234",
"phone": "13800138000",
"hireDate": "2020-01-01",
"status": "0"
}
```
**字段说明**:
| 字段名 | 类型 | 必填 | 说明 | 校验规则 |
|--------|------|------|------|----------|
| employeeId | Long | 是 | 员工ID(柜员号,7位数字) | 必填,7位数字,唯一 |
| name | String | 是 | 姓名 | 最大100字符 |
| deptId | Long | 是 | 所属部门ID | 必填 |
| idCard | String | 是 | 身份证号 | 18位,符合国标,唯一 |
| phone | String | 是 | 电话 | 必填,11位手机号 |
| hireDate | Date | 否 | 入职时间 | yyyy-MM-dd |
| status | String | 是 | 状态 | 0=在职, 1=离职 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 4. 编辑员工
**接口地址**: `PUT /ccdi/employee`
**权限要求**: `ccdi:employee:edit`
**请求体**:
```json
{
"employeeId": 1000001,
"name": "张三",
"deptId": 100,
"idCard": "110101199001011234",
"phone": "13800138000",
"hireDate": "2020-01-01",
"status": "0"
}
```
**字段说明**: 与新增接口相同,employeeId 为必填项,编辑时不可修改柜员号。
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 5. 删除员工
**接口地址**: `DELETE /ccdi/employee/{employeeIds}`
**权限要求**: `ccdi:employee:remove`
**路径参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| employeeIds | Long[] | 是 | 员工ID数组逗号分隔 |
**响应示例**:
```json
{
"code": 200,
"msg": "操作成功"
}
```
---
### 6. 导出员工信息
**接口地址**: `POST /ccdi/employee/export`
**权限要求**: `ccdi:employee:export`
**请求参数**: 与查询列表接口相同(支持筛选条件)
**响应**: Excel 文件下载
---
### 7. 下载导入模板(带字典下拉框)
**接口地址**: `POST /ccdi/employee/importTemplate`
**权限要求**: 无
**功能说明**: 下载的 Excel 模板中,"状态"列会自动添加字典下拉框,方便用户选择。
**响应**: Excel 模板文件下载
**Excel 格式说明**:
**Sheet1: 员工信息**
| 姓名* | 柜员号* | 所属部门ID* | 身份证号* | 电话* | 入职时间 | 状态▼* |
|------|--------|------------|----------|------|----------|------|
| 张三 | 1000001 | 100 | 110101199001011234 | 13800138000 | 2020-01-01 | 在职 |
**注**:
- 带 * 标记的列为必填项(姓名、柜员号、所属部门、身份证号、电话、状态)
- 带 ▼ 标记的列包含下拉框,选项来自字典 `ccdi_employee_status`
**使用 @DictDropdown 注解实现**:
- 状态字段使用 `@DictDropdown(dictType = "ccdi_employee_status")` 注解
- 系统自动从 Redis 缓存读取字典数据并生成下拉框
- 下拉选项可动态更新,刷新字典缓存后生效
---
### 8. 导入员工信息
**接口地址**: `POST /ccdi/employee/importData`
**权限要求**: `ccdi:employee:import`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| file | File | 是 | Excel 文件 |
| updateSupport | Boolean | 否 | 是否更新已存在数据(默认false) |
**Excel 格式**:
**Sheet1: 员工信息**
| 姓名* | 柜员号* | 所属部门ID* | 身份证号* | 电话* | 入职时间 | 状态* |
|------|--------|------------|----------|------|----------|------|
| 张三 | 1000001 | 100 | 110101199001011234 | 13800138000 | 2020-01-01 | 在职 |
**说明**:
- ***标记为必填项**: 姓名、柜员号、所属部门、身份证号、电话、状态**
- 柜员号: 7位数字,必填,唯一
- 所属部门: 必须填写有效的部门ID
- 电话: 必须填写11位手机号
- 入职时间: 选填,格式为 yyyy-MM-dd
**响应示例**:
```json
{
"code": 200,
"msg": "恭喜您,数据已全部导入成功!共 10 条"
}
```
---
## 错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 操作成功 |
| 401 | 未授权,请先登录 |
| 403 | 无权限访问 |
| 500 | 服务器内部错误 |
## 业务错误信息
| 错误信息 | 说明 |
|----------|------|
| 该柜员号已存在 | 新增时柜员号重复 |
| 柜员号不能为空 | 新增时柜员号为空 |
| 柜员号必须为7位数字 | 柜员号格式不正确 |
| 所属部门不能为空 | 新增时所属部门为空 |
| 该身份证号已存在 | 新增/编辑时身份证号重复 |
| 姓名不能为空 | 新增时姓名为空 |
| 身份证号格式不正确 | 身份证号不符合18位国标 |
| 电话不能为空 | 新增时电话为空 |
| 电话格式不正确 | 手机号不符合11位格式 |
| 状态只能填写'在职'或'离职' | 状态值不正确 |
---
## 测试账号
- 用户名: `admin`
- 密码: `admin123`
测试前请先调用 `/login/test` 接口获取 Token。

View File

@@ -0,0 +1,393 @@
# 员工导入服务规范合规审查报告
**审查时间**: 2026-02-09
**审查文件**: `CcdiEmployeeImportServiceImpl.java`
**审查类型**: 规范合规最终审查
---
## 一、审查结果总览
### ✅ 最终评估:**完全合规**
**综合评分**: 100/100
---
## 二、详细审查清单
### 1. 功能完整性检查 (25分)
#### ✅ 批量查询实现 (25/25分)
| 检查项 | 要求 | 实际情况 | 状态 |
|--------|------|----------|------|
| 调用 getExistingIdCards | 批量查询身份证号 | 第50行已调用 | ✅ |
| existingIdCards 集合 | 存储数据库已存在身份证号 | 第50行已创建 | ✅ |
| processedIdCards 集合 | 跟踪Excel内已处理身份证号 | 第54行已创建 | ✅ |
| processedEmployeeIds 集合 | 跟踪Excel内已处理柜员号 | 第53行已创建 | ✅ |
**证据代码**:
```java
// 第49-50行批量查询
Set<Long> existingIds = getExistingEmployeeIds(excelList);
Set<String> existingIdCards = getExistingIdCards(excelList);
// 第53-54行Excel内处理跟踪
Set<Long> processedEmployeeIds = new HashSet<>();
Set<String> processedIdCards = new HashSet<>();
```
---
### 2. 实现正确性检查 (25分)
#### ✅ 检查顺序 (25/25分)
**设计规范要求的检查顺序**:
1. ✅ 数据库重复检查
2. ✅ Excel内柜员号重复检查
3. ✅ Excel内身份证号重复检查
**实际实现顺序**:
**新增分支** (第90-101行):
```java
} else {
// 柜员号不存在,检查Excel内重复
if (processedEmployeeIds.contains(excel.getEmployeeId())) { // 2. 柜员号
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
}
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
processedIdCards.contains(excel.getIdCard())) { // 3. 身份证号
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
}
newRecords.add(employee);
}
```
**更新分支** (第72-88行):
```java
if (existingIds.contains(excel.getEmployeeId())) {
if (!isUpdateSupport) {
throw new RuntimeException("柜员号已存在且未启用更新支持");
}
// 更新模式: 检查Excel内重复
if (processedEmployeeIds.contains(excel.getEmployeeId())) { // 2. 柜员号
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
}
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
processedIdCards.contains(excel.getIdCard())) { // 3. 身份证号
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
}
updateRecords.add(employee);
}
```
**评价**: 完全符合设计规范,检查顺序正确。
---
#### ✅ if-else分支结构 (25/25分)
**设计规范**: 完整的双分支结构
- **数据库存在分支**: 处理更新模式
- **数据库不存在分支**: 处理新增模式
**实际实现**:
```java
// 第72-88行数据库存在分支
if (existingIds.contains(excel.getEmployeeId())) {
// 更新模式检查
// ...
updateRecords.add(employee);
} else {
// 第90-101行数据库不存在分支
// 新增模式检查
// ...
newRecords.add(employee);
}
```
**评价**: 分支结构完整,逻辑清晰。
---
#### ✅ 标记时机正确 (25/25分)
**设计规范**: 只在记录成功通过所有验证并确定要插入时,才标记为"已处理"
**实际实现**:
```java
// 第71-110行完整的验证流程
if (existingIds.contains(excel.getEmployeeId())) {
// 验证Excel内重复
// ...
updateRecords.add(employee); // 确定插入
} else {
// 验证Excel内重复
// ...
newRecords.add(employee); // 确定插入
}
// 第104-110行统一标记两个分支后
// 统一标记为已处理(两个分支都会执行到这里)
if (excel.getEmployeeId() != null) {
processedEmployeeIds.add(excel.getEmployeeId());
}
if (StringUtils.isNotEmpty(excel.getIdCard())) {
processedIdCards.add(excel.getIdCard());
}
```
**评价**: 标记时机完全正确,只有成功通过验证的记录才会被标记。
---
#### ✅ 空值处理正确 (25/25分)
**设计规范**: 只有非空的字段才参与重复检测和标记
**实际实现**:
**检测时**:
```java
// 第82-85行身份证号空值检查
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
processedIdCards.contains(excel.getIdCard())) {
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
}
```
**标记时**:
```java
// 第105-110行空值检查
if (excel.getEmployeeId() != null) {
processedEmployeeIds.add(excel.getEmployeeId());
}
if (StringUtils.isNotEmpty(excel.getIdCard())) {
processedIdCards.add(excel.getIdCard());
}
```
**评价**: 空值处理完全正确,符合设计规范。
---
#### ✅ 更新模式处理 (25/25分)
**设计规范**: 更新模式下也要进行Excel内重复检查
**实际实现**:
```java
// 第72-88行更新模式分支
if (existingIds.contains(excel.getEmployeeId())) {
if (!isUpdateSupport) {
throw new RuntimeException("柜员号已存在且未启用更新支持");
}
// 更新模式: 检查Excel内重复
if (processedEmployeeIds.contains(excel.getEmployeeId())) {
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
}
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
processedIdCards.contains(excel.getIdCard())) {
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
}
// 通过检查,添加到更新列表
updateRecords.add(employee);
}
```
**评价**: 更新模式下完整实现了Excel内重复检查。
---
### 3. 代码一致性检查 (25分)
#### ✅ 与参考实现风格一致 (25/25分)
**参考实现** (`CcdiIntermediaryEntityImportServiceImpl.java`):
```java
if (existingCreditCodes.contains(excel.getSocialCreditCode())) {
// 数据库存在,直接报错
throw new RuntimeException(String.format("统一社会信用代码[%s]已存在,请勿重复导入", excel.getSocialCreditCode()));
} else if (excelProcessedIds.contains(excel.getSocialCreditCode())) {
// Excel内重复
throw new RuntimeException(String.format("统一社会信用代码[%s]在导入文件中重复,已跳过此条记录", excel.getSocialCreditCode()));
} else {
newRecords.add(entity);
excelProcessedIds.add(excel.getSocialCreditCode()); // 标记为已处理
}
```
**当前实现** (`CcdiEmployeeImportServiceImpl.java`):
```java
if (existingIds.contains(excel.getEmployeeId())) {
// 更新模式检查
updateRecords.add(employee);
} else {
// 新增模式检查
if (processedEmployeeIds.contains(excel.getEmployeeId())) {
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
}
if (StringUtils.isNotEmpty(excel.getIdCard()) &&
processedIdCards.contains(excel.getIdCard())) {
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
}
newRecords.add(employee);
}
// 统一标记
if (excel.getEmployeeId() != null) {
processedEmployeeIds.add(excel.getEmployeeId());
}
if (StringUtils.isNotEmpty(excel.getIdCard())) {
processedIdCards.add(excel.getIdCard());
}
```
**一致性分析**:
- ✅ 错误消息格式完全一致
- ✅ 使用 String.format 进行消息格式化
- ✅ 异常处理方式一致
- ✅ 批量查询模式一致
- ✅ 标记逻辑清晰易懂
**评价**: 代码风格与参考实现保持高度一致。
---
#### ✅ 错误消息格式符合要求 (25/25分)
**设计规范要求**:
- 柜员号: "柜员号[XXX]在导入文件中重复,已跳过此条记录"
- 身份证号: "身份证号[XXX]在导入文件中重复,已跳过此条记录"
**实际实现**:
```java
// 第80行柜员号错误消息
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
// 第84行身份证号错误消息
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
// 第93行柜员号错误消息新增分支
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
// 第97行身份证号错误消息新增分支
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
```
**评价**: 错误消息格式完全符合设计规范要求。
---
### 4. 方法签名更新检查 (25分)
#### ✅ validateEmployeeData 方法签名更新 (25/25分)
**设计规范**: 添加 existingIdCards 参数
**实际实现** (第280行):
```java
/**
* 验证员工数据
*
* @param addDTO 新增DTO
* @param isUpdateSupport 是否支持更新
* @param existingIds 已存在的员工ID集合(导入场景使用,传null表示单条新增)
* @param existingIdCards 已存在的身份证号集合(导入场景使用,传null表示单条新增)
*/
public void validateEmployeeData(CcdiEmployeeAddDTO addDTO, Boolean isUpdateSupport, Set<Long> existingIds, Set<String> existingIdCards) {
// ...
}
```
**方法调用** (第66行):
```java
validateEmployeeData(addDTO, isUpdateSupport, existingIds, existingIdCards);
```
**批量查询结果使用** (第324行):
```java
// 使用批量查询的结果检查身份证号唯一性
if (existingIdCards != null && existingIdCards.contains(addDTO.getIdCard())) {
throw new RuntimeException("该身份证号已存在");
}
```
**评价**: 方法签名更新完整,参数传递正确,批量查询结果正确使用。
---
## 三、代码质量评价
### 优点总结
1. **性能优化**: 使用批量查询替代单条查询,显著提升性能
2. **逻辑清晰**: 双分支结构清晰,易于理解和维护
3. **错误处理完善**: 所有异常情况都有明确的错误消息
4. **空值安全**: 正确处理空值情况,避免空指针异常
5. **注释清晰**: 关键步骤都有清晰的注释说明
6. **符合规范**: 完全符合设计规范和参考实现风格
### 与参考实现的差异说明
**差异点**: 当前实现使用了双分支结构(更新/新增),而参考实现使用单分支结构
**原因分析**:
- 参考实现是纯新增模式(不支持更新)
- 当前实现支持更新模式,需要区分更新和新增两种场景
**评价**: 这是合理的差异,双分支结构更适合支持更新模式的场景。
---
## 四、测试建议
### 建议测试场景
1. **Excel内柜员号重复测试**
- 准备3条相同柜员号的记录
- 验证只有第一条成功后2条失败
- 验证错误消息格式正确
2. **Excel内身份证号重复测试**
- 准备3条相同身份证号的记录
- 验证只有第一条成功后2条失败
- 验证错误消息格式正确
3. **数据库重复 + Excel内重复测试**
- 准备柜员号在数据库存在且在Excel内重复的记录
- 验证更新模式下Excel内重复检查生效
4. **空值处理测试**
- 准备身份证号为空的记录
- 验证空值不参与重复检测
5. **更新模式测试**
- 启用更新支持
- 验证Excel内重复检查在更新模式下生效
---
## 五、最终结论
### ✅ 完全合规
**评分**: 100/100
**合规要点**:
- ✅ 功能完整性: 25/25分
- ✅ 实现正确性: 25/25分
- ✅ 代码一致性: 25/25分
- ✅ 方法签名更新: 25/25分
**审批意见**: 该实现完全符合设计规范要求,可以进行代码合并。
---
**审查人**: Claude
**审查日期**: 2026-02-09

View File

@@ -0,0 +1,110 @@
# 数据库唯一索引验证报告
## 验证日期
2026-02-08
## 验证目的
确认中介信息导入功能所需的数据库唯一索引已正确配置,为 `INSERT ... ON DUPLICATE KEY UPDATE` 语句提供基础支持。
## 涉及表
- `ccdi_biz_intermediary` (个人中介表)
- `ccdi_enterprise_base_info` (实体中介表)
---
## 检查结果
### 1. 个人中介表 (ccdi_biz_intermediary)
#### 检查项: person_id 唯一索引
**检查前状态:**
- 存在普通索引 `idx_person_id` (Non_unique = 1)
- ❌ 不满足唯一性要求
**执行操作:**
```sql
-- 删除原有普通索引
ALTER TABLE ccdi_biz_intermediary DROP INDEX idx_person_id;
-- 创建唯一索引
ALTER TABLE ccdi_biz_intermediary ADD UNIQUE KEY uk_person_id (person_id);
```
**检查后状态:**
- ✅ 唯一索引 `uk_person_id` 已创建
- Non_unique: 0
- Column_name: person_id
- Index_type: BTREE
- Cardinality: 1745
**最终索引状态:**
- ✅ PRIMARY KEY: `biz_id`
- ✅ UNIQUE KEY: `uk_person_id` (Non_unique = 0)
- ✅ INDEX: `idx_name` (普通索引)
- ✅ INDEX: `idx_mobile` (普通索引)
**完整索引列表:**
```sql
SHOW INDEX FROM ccdi_biz_intermediary;
```
| Key_name | Column_name | Non_unique | Index_type |
|----------|-------------|------------|------------|
| PRIMARY | biz_id | 0 | BTREE |
| uk_person_id | person_id | 0 | BTREE |
| idx_name | name | 1 | BTREE |
| idx_mobile | mobile | 1 | BTREE |
---
### 2. 实体中介表 (ccdi_enterprise_base_info)
#### 检查项: social_credit_code 主键
**检查前状态:**
-`social_credit_code` 已为 PRIMARY KEY
- 字段类型: varchar(50)
- 约束: NOT NULL
- 引擎: InnoDB
**表结构确认:**
```sql
SHOW CREATE TABLE ccdi_enterprise_base_info;
```
**结论:**
- ✅ 无需修改,已满足要求
---
## 总结
### 验证结论
**所有必需的唯一索引/主键均已正确配置**
### 配置详情
| 表名 | 字段 | 约束类型 | 状态 |
|------|------|----------|------|
| ccdi_biz_intermediary | person_id | UNIQUE KEY | ✅ 已创建 |
| ccdi_enterprise_base_info | social_credit_code | PRIMARY KEY | ✅ 已存在 |
### 对导入功能的影响
-`INSERT ... ON DUPLICATE KEY UPDATE` 现在可以正确工作
- ✅ 个人中介数据根据 `person_id` 自动去重和更新
- ✅ 实体中介数据根据 `social_credit_code` 自动去重和更新
### 注意事项
1. **唯一索引约束:** 导入数据时,如果 `person_id` 重复,将自动执行更新操作
2. **性能影响:** 唯一索引会在插入和更新时进行唯一性检查,对性能有轻微影响
3. **数据完整性:** 唯一索引确保了数据的唯一性,防止重复数据
---
## 执行人员
Claude Code AI Assistant
## 审核状态
✅ 已完成验证并创建唯一索引
✅ 文档已提交到 git (commit: a6a872b)

View File

@@ -0,0 +1,455 @@
# 员工柜员号优化设计文档
**文档版本**: v1.0
**创建日期**: 2026-02-05
**设计目标**: 统一标识符,移除tellerNo字段,将employeeId设置为柜员号
---
## 一、需求概述
### 1.1 需求背景
当前员工信息表中存在两个字段用于标识员工:
- `employee_id`: 数据库主键,自增ID
- `teller_no`: 柜员号,业务标识符
这种双标识符设计造成了字段冗余和业务混淆。
### 1.2 需求目标
- **移除 `teller_no` 字段**,简化数据结构
- **将 `employee_id` 改为手动输入的柜员号**(7位数字)
- **统一标识符**,避免业务混淆
- **保持数据完整性和业务连续性**
### 1.3 约束条件
- 系统处于开发阶段,无正式生产数据
- 柜员号必须为7位数字
- 柜员号必须唯一,不允许重复
- 柜员号为必填字段
---
## 二、数据库层设计
### 2.1 表结构修改
#### 删除字段
```sql
ALTER TABLE ccdi_employee DROP COLUMN teller_no;
```
#### 修改主键字段
```sql
-- 移除自增属性
ALTER TABLE ccdi_employee MODIFY employee_id BIGINT(20) NOT NULL;
-- 更新字段注释
ALTER TABLE ccdi_employee MODIFY COLUMN employee_id BIGINT(20) NOT NULL COMMENT '员工ID(柜员号,7位数字)';
```
#### 重建表方案(推荐,清空数据场景)
```sql
DROP TABLE IF EXISTS ccdi_employee;
CREATE TABLE ccdi_employee (
employee_id BIGINT(20) NOT NULL COMMENT '员工ID(柜员号,7位数字)',
name VARCHAR(100) NOT NULL COMMENT '姓名',
dept_id BIGINT(20) DEFAULT NULL COMMENT '所属部门ID',
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),
KEY idx_dept_id (dept_id),
KEY idx_status (status),
UNIQUE KEY uk_id_card (id_card)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工信息表';
```
### 2.2 索引调整
- 移除: `UNIQUE KEY teller_no`
- 保留: `PRIMARY KEY (employee_id)` 天然保证唯一性
---
## 三、后端代码层设计
### 3.1 Entity 实体类 (CcdiEmployee.java)
**修改前**:
```java
@TableId(type = IdType.AUTO)
private Long employeeId;
private String tellerNo;
```
**修改后**:
```java
@TableId(type = IdType.INPUT) // 改为手动输入
private Long employeeId;
// 删除 tellerNo 字段
```
### 3.2 DTO 类修改
#### CcdiEmployeeAddDTO.java
```java
/** 员工ID(柜员号) */
@NotNull(message = "柜员号不能为空")
@Min(value = 1000000L, message = "柜员号必须为7位数字")
@Max(value = 9999999L, message = "柜员号必须为7位数字")
private Long employeeId;
// 删除 tellerNo 字段
```
#### CcdiEmployeeEditDTO.java
```java
// employeeId 作为主键标识,通过路径参数传递,不在请求体中
// 删除 tellerNo 字段
```
#### CcdiEmployeeQueryDTO.java
```java
/** 柜员号(精确查询) */
@Min(value = 1000000L, message = "柜员号必须为7位数字")
@Max(value = 9999999L, message = "柜员号必须为7位数字")
private Long employeeId;
// 删除 tellerNo 字段
```
### 3.3 VO 类修改 (CcdiEmployeeVO.java)
```java
/** 员工ID(柜员号) */
private Long employeeId;
// 删除 tellerNo 字段
```
### 3.4 Service 层修改
#### 新增柜员号唯一性校验
```java
@Override
public void checkEmployeeIdUnique(Long employeeId) {
CcdiEmployee existing = baseMapper.selectById(employeeId);
if (existing != null) {
throw new ServiceException("柜员号已存在,请使用其他柜员号");
}
}
```
#### 新增员工方法调整
```java
@Override
public void addEmployee(CcdiEmployeeAddDTO dto) {
// 1. 校验柜员号唯一性
checkEmployeeIdUnique(dto.getEmployeeId());
// 2. 校验身份证号唯一性
checkIdCardUnique(dto.getIdCard());
// 3. 转换并保存
CcdiEmployee employee = BeanUtil.copyProperties(dto, CcdiEmployee.class);
baseMapper.insert(employee);
}
```
### 3.5 Mapper XML 修改
#### ResultMap 调整
```xml
<resultMap type="com.ruoyi.ccdi.domain.vo.CcdiEmployeeVO" id="CcdiEmployeeVOResult">
<id property="employeeId" column="employee_id"/>
<result property="name" column="name"/>
<!-- 删除 tellerNo 映射 -->
<result property="deptId" column="dept_id"/>
<result property="deptName" column="dept_name"/>
<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"/>
</resultMap>
```
#### 查询 SQL 调整
```xml
<select id="selectEmployeePageWithDept" resultMap="CcdiEmployeeVOResult">
SELECT
e.employee_id, e.name, e.dept_id, e.id_card, e.phone,
e.hire_date, e.status, e.create_time,
d.dept_name
FROM ccdi_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.employeeId != null">
AND e.employee_id = #{query.employeeId}
</if>
<!-- 删除 teller_no 查询条件 -->
<if test="query.deptId != null">
AND e.dept_id = #{query.deptId}
</if>
<if test="query.idCard != null and query.idCard != ''">
AND e.id_card LIKE CONCAT('%', #{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>
```
### 3.6 Controller 层修改
#### 接口参数调整
- **POST /ccdi/employee**: 新增接口,接收 `employeeId` 作为必填字段
- **PUT /ccdi/employee/{employeeId}**: 编辑接口,`employeeId` 作为路径参数不可修改
- **GET /ccdi/employee/list**: 列表查询,移除 `tellerNo` 查询参数,保留 `employeeId` 精确查询
#### Swagger 注释更新
```java
@Operation(summary = "新增员工信息", description = "employeeId为柜员号,7位数字")
```
---
## 四、前端代码层设计
### 4.1 查询表单调整
```vue
<!-- 删除原来的 tellerNo 查询条件 -->
<!-- 新增:员工ID(柜员号)查询 -->
<el-form-item label="柜员号" prop="employeeId">
<el-input
v-model="queryParams.employeeId"
placeholder="请输入7位柜员号"
clearable
maxlength="7"
oninput="value=value.replace(/[^\d]/g,'')"
style="width: 240px"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
```
### 4.2 表格列调整
```vue
<!-- 删除 -->
<!-- <el-table-column label="柜员号" prop="tellerNo" /> -->
<!-- 新增 -->
<el-table-column label="柜员号" align="center" prop="employeeId" :show-overflow-tooltip="true"/>
```
### 4.3 新增/编辑对话框调整
```vue
<!-- 新增模式:可输入 -->
<el-form-item label="柜员号" prop="employeeId" v-if="!isEdit">
<el-input
v-model="form.employeeId"
placeholder="请输入7位柜员号"
clearable
maxlength="7"
oninput="value=value.replace(/[^\d]/g,'')"
style="width: 240px"
/>
</el-form-item>
<!-- 编辑模式:只读 -->
<el-form-item label="柜员号" prop="employeeId" v-if="isEdit">
<el-input v-model="form.employeeId" disabled style="width: 240px"/>
</el-form-item>
```
### 4.4 JavaScript 数据结构
```javascript
data() {
return {
queryParams: {
name: null,
employeeId: null, // 替代 tellerNo
deptId: null,
idCard: null,
status: null
},
form: {
employeeId: null, // 替代 tellerNo
name: null,
deptId: null,
// ...
}
}
}
```
### 4.5 表单校验规则
```javascript
rules: {
employeeId: [
{ required: true, message: "柜员号不能为空", trigger: "blur" },
{ pattern: /^\d{7}$/, message: "柜员号必须为7位数字", trigger: "blur" }
],
// 其他规则...
}
```
---
## 五、测试方案
### 5.1 新增员工测试
| 测试场景 | 输入数据 | 预期结果 |
|---------|---------|---------|
| 正常场景 | 柜员号: 1000000 | 新增成功 |
| 格式错误-少于7位 | 柜员号: 123456 | 提示"柜员号必须为7位数字" |
| 格式错误-多于7位 | 柜员号: 12345678 | 提示"柜员号必须为7位数字" |
| 格式错误-非数字 | 柜员号: 123456a | 提示"柜员号必须为7位数字" |
| 唯一性冲突 | 重复的柜员号 | 提示"柜员号已存在" |
| 必填校验 | 柜员号为空 | 提示"柜员号不能为空" |
### 5.2 编辑员工测试
| 测试场景 | 操作 | 预期结果 |
|---------|------|---------|
| 正常编辑 | 修改其他字段,柜员号不可变 | 编辑成功,柜员号不变 |
| 只读验证 | 尝试修改柜员号 | 柜员号输入框禁用 |
### 5.3 查询测试
| 测试场景 | 输入 | 预期结果 |
|---------|------|---------|
| 精确查询 | 输入7位柜员号 | 返回匹配的员工记录 |
| 列表显示 | 查看列表 | 显示employeeId作为柜员号 |
---
## 六、文档更新清单
### 6.1 API 文档更新
- **文件路径**: `doc/api/员工信息管理API文档.md`
- **更新内容**:
1. 新增接口:移除 `tellerNo`,新增 `employeeId` 参数说明
2. 编辑接口:更新路径参数为 `employeeId`
3. 查询接口:移除 `tellerNo` 查询参数,新增 `employeeId`
4. 返回数据:移除 `tellerNo` 字段
5. 字段说明表:更新 `employeeId` 为"员工ID(柜员号,7位数字)"
### 6.2 测试脚本
- **文件路径**: `doc/test/2026-02-05-employee-modify-test.sh`
- **测试账号**: username: admin, password: admin123
- **测试接口**: `/login/test` 获取 token
### 6.3 数据库脚本
- **文件路径**: `sql/modify_employee_id_to_teller_no.sql`
- **执行顺序**:
1. 删除 `teller_no` 字段
2. 修改 `employee_id` 为非自增
3. 更新字段注释
---
## 七、实施步骤
### 7.1 数据库修改
1. 备份现有数据库(如有数据)
2. 执行 SQL 脚本修改表结构
3. 验证表结构修改成功
### 7.2 后端代码修改
1. 修改 Entity 实体类
2. 修改 DTO 类(Add/Edit/Query)
3. 修改 VO 类
4. 修改 Service 层,添加唯一性校验
5. 修改 Mapper XML
6. 修改 Controller 层
7. 编译后端项目,确保无错误
### 7.3 前端代码修改
1. 修改查询表单
2. 修改表格列
3. 修改新增/编辑对话框
4. 修改 JavaScript 数据结构和方法
5. 添加表单校验规则
6. 编译前端项目,确保无错误
### 7.4 测试验证
1. 执行测试脚本
2. 验证新增功能
3. 验证编辑功能
4. 验证查询功能
5. 验证唯一性校验
6. 验证格式校验
7. 生成测试报告
### 7.5 文档更新
1. 更新 API 文档
2. 更新测试报告
3. 提交代码到版本控制
---
## 八、风险评估与应对
### 8.1 风险点
1. **数据迁移风险**: 如果有正式数据,需要迁移方案
- **应对**: 当前为开发阶段,无正式数据,直接修改
2. **接口兼容性**: 前端调用可能受影响
- **应对**: 同步修改前端代码和接口调用
3. **业务逻辑依赖**: 其他模块可能引用 `tellerNo`
- **应对**: 全局搜索 `tellerNo` 引用,同步修改
### 8.2 回滚方案
如果修改后出现问题,可以:
1. 恢复数据库表结构(添加回 `teller_no` 字段)
2. 恢复代码到修改前的版本
3. 恢复前端代码到修改前的版本
---
## 九、验收标准
### 9.1 功能验收
- ✅ 数据库 `teller_no` 字段已删除
-`employee_id` 改为非自增,手动输入
- ✅ 后端代码所有 `tellerNo` 引用已移除
- ✅ 前端页面显示 `employeeId` 作为柜员号
- ✅ 新增员工时必须输入7位数字柜员号
- ✅ 柜员号唯一性校验生效
- ✅ 柜员号格式校验生效
- ✅ 编辑时柜员号不可修改
### 9.2 性能验收
- ✅ 接口响应时间无明显变化
- ✅ 数据库查询效率正常
### 9.3 文档验收
- ✅ API 文档已更新
- ✅ 测试脚本已生成
- ✅ 测试报告已生成
---
**文档结束**

View File

@@ -0,0 +1,532 @@
# 中介黑名单管理模块 - 系统设计文档
## 文档信息
- **版本**: v1.0
- **日期**: 2026-02-04
- **作者**: Claude
- **项目**: 纪检初核系统 (CCDI)
---
## 1. 概述
### 1.1 功能简介
中介黑名单管理模块提供个人中介和实体中介两类中介信息的完整管理功能,包括:
- 个人中介的增删改查
- 实体中介的增删改查
- 统一列表查询(支持联合查询和个人/实体分类查询)
- 带字典下拉框的Excel导入模板下载
- 批量数据导入
### 1.2 核心特性
1. **双表存储**: 个人中介和实体中介分别存储在不同的数据表中
2. **统一查询**: 使用SQL UNION实现高效的联合查询和分页
3. **类型区分**: 通过`intermediary_type`字段区分个人(1)和实体(2)中介
4. **智能筛选**: 实体中介通过`risk_level='1'`(高风险) AND `ent_source='INTERMEDIARY'(中介)`筛选
5. **唯一性保证**: 个人中介的证件号`person_id`作为业务唯一键
### 1.3 技术栈
- **后端框架**: Spring Boot 3.5.8
- **ORM框架**: MyBatis Plus 3.5.10
- **Excel处理**: EasyExcel (带字典下拉框)
- **数据库**: MySQL 8.2.0
- **API文档**: SpringDoc 2.8.14
---
## 2. 数据库设计
### 2.1 个人中介表 (ccdi_biz_intermediary)
| 字段名 | 类型 | 可空 | 主键 | 注释 |
|--------|------|------|------|------|
| biz_id | VARCHAR | 否 | 是 | 人员ID |
| person_type | VARCHAR | 否 | 否 | 人员类型(中介、职业背债人等) |
| person_sub_type | VARCHAR | 是 | 否 | 人员子类型 |
| relation_type | VARCHAR | 是 | 否 | 关系类型(配偶、子女、父母等) |
| name | VARCHAR | 否 | 否 | 姓名 |
| gender | CHAR | 是 | 否 | 性别 |
| id_type | VARCHAR | 否 | 否 | 证件类型(默认身份证) |
| person_id | VARCHAR | 否 | 否 | **证件号码(业务唯一键)** |
| mobile | VARCHAR | 是 | 否 | 手机号码 |
| wechat_no | VARCHAR | 是 | 否 | 微信号 |
| contact_address | VARCHAR | 是 | 否 | 联系地址 |
| company | VARCHAR | 是 | 否 | 所在公司 |
| social_credit_code | VARCHAR | 是 | 否 | 企业统一信用码 |
| position | VARCHAR | 是 | 否 | 职位 |
| related_num_id | VARCHAR | 是 | 否 | 关联人员ID |
| relation_type | VARCHAR | 是 | 否 | 关联关系 |
| data_source | VARCHAR | 是 | 否 | 数据来源MANUAL/SYSTEM/IMPORT/API |
| remark | VARCHAR | 是 | 否 | 备注信息 |
| created_by | VARCHAR | 否 | 否 | 记录创建人 |
| updated_by | VARCHAR | 是 | 否 | 记录更新人 |
| create_time | DATETIME | 否 | 否 | 记录创建时间 |
| update_time | DATETIME | 是 | 否 | 记录更新时间 |
**索引设计**:
- PRIMARY KEY: `biz_id`
- UNIQUE KEY: `uk_person_id` (`person_id`)
### 2.2 实体中介表 (ccdi_enterprise_base_info)
| 字段名 | 类型 | 可空 | 主键 | 注释 |
|--------|------|------|------|------|
| social_credit_code | VARCHAR | 否 | 是 | **统一社会信用代码(主键)** |
| enterprise_name | VARCHAR | 否 | 否 | 企业名称 |
| enterprise_type | VARCHAR | 否 | 否 | 企业类型(有限责任公司、股份有限公司等) |
| enterprise_nature | VARCHAR | 是 | 否 | 企业性质(国企、民企、外企等) |
| industry_class | VARCHAR | 是 | 否 | 行业分类 |
| industry_name | VARCHAR | 是 | 否 | 所属行业 |
| establish_date | DATE | 是 | 否 | 成立日期 |
| register_address | VARCHAR | 是 | 否 | 注册地址 |
| legal_representative | VARCHAR | 是 | 否 | 法定代表人 |
| legal_cert_type | VARCHAR | 是 | 否 | 法定代表人证件类型 |
| legal_cert_no | VARCHAR | 是 | 否 | 法定代表人证件号码 |
| shareholder1-5 | VARCHAR | 是 | 否 | 股东信息 |
| status | VARCHAR | 是 | 否 | 经营状态 |
| create_time | DATETIME | 否 | 否 | 创建时间 |
| update_time | DATETIME | 否 | 否 | 更新时间 |
| created_by | VARCHAR | 否 | 否 | 创建人 |
| updated_by | VARCHAR | 是 | 否 | 更新人 |
| data_source | VARCHAR | 是 | 否 | 数据来源MANUAL/SYSTEM/API/IMPORT |
| **risk_level** | VARCHAR(10) | 是 | 否 | **风险等级1-高风险, 2-中风险, 3-低风险** |
| **ent_source** | VARCHAR(20) | 否 | 否 | **企业来源GENERAL/EMP_RELATION/CREDIT_CUSTOMER/INTERMEDIARY/BOTH** |
**索引设计**:
- PRIMARY KEY: `social_credit_code`
- INDEX: `idx_risk_ent_source` (`risk_level`, `ent_source`)
**实体中介筛选条件**:
- `risk_level = '1'` (高风险)
- `ent_source = 'INTERMEDIARY'` (中介)
---
## 3. 架构设计
### 3.1 整体架构
```
Controller Layer (CcdiIntermediaryController)
Service Layer (ICcdiIntermediaryService)
Mapper Layer (CcdiBizIntermediaryMapper, CcdiEnterpriseBaseInfoMapper)
Database (ccdi_biz_intermediary, ccdi_enterprise_base_info)
```
### 3.2 分层说明
**Controller层**:
- 统一的Controller处理个人和实体中介的请求
- 使用不同的路径区分个人和实体中介操作
- 权限控制: `ccdi:intermediary:*`
**Service层**:
- 统一的服务接口
- 根据中介类型路由到不同的业务逻辑
- 处理唯一性校验、数据自动填充等业务规则
**Mapper层**:
- 每个表对应独立的Mapper接口
- 继承MyBatis Plus的BaseMapper
- 自定义XML实现UNION联合查询
**DTO/VO层**:
- 严格分离不与Entity混用
- DTO用于接口参数接收
- VO用于数据返回
---
## 4. 接口设计
### 4.1 基础信息
- **基础路径**: `/ccdi/intermediary`
- **权限前缀**: `ccdi:intermediary`
- **响应格式**: AjaxResult
### 4.2 统一列表查询
**接口**: `GET /ccdi/intermediary/list`
**权限**: `ccdi:intermediary:list`
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| name | String | 否 | 姓名/机构名称(模糊查询) |
| certificateNo | String | 否 | 证件号/统一社会信用代码(精确查询) |
| intermediaryType | String | 否 | 中介类型1=个人, 2=实体, null=全部) |
| pageNum | Integer | 否 | 页码默认1 |
| pageSize | Integer | 否 | 每页数量默认10 |
**响应**: TableDataInfo (分页结果)
**实现**: SQL UNION联合查询支持按类型筛选优化
### 4.3 个人中介接口
#### 4.3.1 新增个人中介
**接口**: `POST /ccdi/intermediary/person`
**权限**: `ccdi:intermediary:add`
**请求体**: CcdiIntermediaryPersonAddDTO
**业务逻辑**:
- 校验姓名必填
- 校验证件号必填且唯一
- 自动设置data_source='MANUAL'
- 自动设置person_type='中介'
#### 4.3.2 修改个人中介
**接口**: `PUT /ccdi/intermediary/person`
**权限**: `ccdi:intermediary:edit`
**请求体**: CcdiIntermediaryPersonEditDTO
**业务逻辑**:
- biz_id不可修改
- 证件号修改时需校验唯一性(排除自身)
#### 4.3.3 查询个人中介详情
**接口**: `GET /ccdi/intermediary/person/{bizId}`
**权限**: `ccdi:intermediary:query`
**响应**: CcdiIntermediaryPersonDetailVO
### 4.4 实体中介接口
#### 4.4.1 新增实体中介
**接口**: `POST /ccdi/intermediary/entity`
**权限**: `ccdi:intermediary:add`
**请求体**: CcdiIntermediaryEntityAddDTO
**业务逻辑**:
- 校验企业名称必填
- 校验统一社会信用代码唯一
- 自动设置risk_level='1'(高风险)
- 自动设置ent_source='INTERMEDIARY'(中介)
- 自动设置data_source='MANUAL'
#### 4.4.2 修改实体中介
**接口**: `PUT /ccdi/intermediary/entity`
**权限**: `ccdi:intermediary:edit`
**请求体**: CcdiIntermediaryEntityEditDTO
**业务逻辑**:
- social_credit_code不可修改
- 企业名称修改时需校验唯一性(排除自身)
#### 4.4.3 查询实体中介详情
**接口**: `GET /ccdi/intermediary/entity/{socialCreditCode}`
**权限**: `ccdi:intermediary:query`
**响应**: CcdiIntermediaryEntityDetailVO
### 4.5 删除接口
**接口**: `DELETE /ccdi/intermediary/{ids}`
**权限**: `ccdi:intermediary:remove`
**路径参数**: ids (支持个人和实体的ID逗号分隔)
### 4.6 导入导出接口
#### 4.6.1 个人中介模板下载
**接口**: `POST /ccdi/intermediary/importPersonTemplate`
**权限**: 无需登录
**功能**: 下载带字典下拉框的Excel模板
**下拉字段**:
- 性别: `ccdi_indiv_gender`
- 证件类型: `ccdi_certificate_type`
- 关联关系: `ccdi_relation_type`
#### 4.6.2 实体中介模板下载
**接口**: `POST /ccdi/intermediary/importEntityTemplate`
**权限**: 无需登录
**功能**: 下载带字典下拉框的Excel模板
**下拉字段**:
- 主体类型: `ccdi_entity_type`
- 企业性质: `ccdi_enterprise_nature`
- 法人证件类型: `ccdi_certificate_type`
#### 4.6.3 个人中介数据导入
**接口**: `POST /ccdi/intermediary/importPersonData`
**权限**: `ccdi:intermediary:import`
**参数**:
- file: MultipartFile
- updateSupport: Boolean (是否更新已存在数据)
**Excel类**: CcdiIntermediaryPersonExcel
**业务逻辑**:
- 解析Excel数据
- 校验姓名必填、证件号必填
- 检查person_id唯一性
- 批量插入ccdi_biz_intermediary表
- 自动设置: data_source='IMPORT', person_type='中介'
#### 4.6.4 实体中介数据导入
**接口**: `POST /ccdi/intermediary/importEntityData`
**权限**: `ccdi:intermediary:import`
**参数**:
- file: MultipartFile
- updateSupport: Boolean (是否更新已存在数据)
**Excel类**: CcdiIntermediaryEntityExcel
**业务逻辑**:
- 解析Excel数据
- 校验企业名称必填
- 检查social_credit_code唯一性
- 批量插入ccdi_enterprise_base_info表
- 自动设置: risk_level='1', ent_source='INTERMEDIARY', data_source='IMPORT'
---
## 5. UNION联合查询实现
### 5.1 SQL查询语句
```xml
<select id="selectIntermediaryList" resultType="CcdiIntermediaryVO">
<!-- 查询个人中介 -->
SELECT
biz_id as id,
name,
person_id as certificate_no,
'1' as intermediary_type,
person_type,
gender,
id_type,
mobile,
company,
data_source,
create_time
FROM ccdi_biz_intermediary
WHERE person_type = '中介'
<if test="intermediaryType == null or intermediaryType == '1'">
AND name LIKE CONCAT('%', #{name}, '%')
<if test="certificateNo != null and certificateNo != ''">
AND person_id = #{certificateNo}
</if>
</if>
UNION ALL
<!-- 查询实体中介 -->
SELECT
social_credit_code as id,
enterprise_name as name,
social_credit_code as certificate_no,
'2' as intermediary_type,
'实体' as person_type,
null as gender,
null as id_type,
null as mobile,
enterprise_name as company,
data_source,
create_time
FROM ccdi_enterprise_base_info
WHERE risk_level = '1' AND ent_source = 'INTERMEDIARY'
<if test="intermediaryType == null or intermediaryType == '2'">
AND enterprise_name LIKE CONCAT('%', #{name}, '%')
<if test="certificateNo != null and certificateNo != ''">
AND social_credit_code = #{certificateNo}
</if>
</if>
ORDER BY create_time DESC
</select>
```
### 5.2 分页实现
- 使用MyBatis Plus的Page对象进行分页
- 在Service层调用`page(intermediaryQueryDTO, Page)`方法
- 自动处理total和rows
### 5.3 查询优化
- 根据intermediaryType参数优化查询如果指定类型则只查询对应表
- 添加索引优化查询性能
---
## 6. 数据对象设计
### 6.1 Entity实体类
**CcdiBizIntermediary**:
- 使用`@Data`注解
- 不继承BaseEntity
- 单独添加审计字段
- 主键: biz_id (String)
**CcdiEnterpriseBaseInfo**:
- 使用`@Data`注解
- 不继承BaseEntity
- 单独添加审计字段
- 主键: social_credit_code (String)
### 6.2 DTO数据传输对象
**CcdiIntermediaryPersonAddDTO**: 个人中介新增DTO
- 包含所有个人字段
- 使用JSR-303校验注解
**CcdiIntermediaryPersonEditDTO**: 个人中介修改DTO
- 包含biz_id和可编辑字段
- biz_id不可为空
**CcdiIntermediaryEntityAddDTO**: 实体中介新增DTO
- 包含所有企业字段
- 使用JSR-303校验注解
**CcdiIntermediaryEntityEditDTO**: 实体中介修改DTO
- 包含social_credit_code和可编辑字段
- social_credit_code不可为空
**CcdiIntermediaryQueryDTO**: 统一查询DTO
- 支持: name, certificateNo, intermediaryType筛选
### 6.3 VO视图对象
**CcdiIntermediaryVO**: 统一列表VO
- 包含intermediary_type字段区分类型(1=个人, 2=实体)
- 统一字段: id, name, certificate_no, intermediary_type, company, create_time等
**CcdiIntermediaryPersonDetailVO**: 个人中介详情VO
- 包含个人中介的所有详细信息
**CcdiIntermediaryEntityDetailVO**: 实体中介详情VO
- 包含实体中介的所有详细信息
### 6.4 Excel导入导出类
**CcdiIntermediaryPersonExcel**: 个人中介Excel类
- 使用EasyExcel注解
- 字段校验和格式化
**CcdiIntermediaryEntityExcel**: 实体中介Excel类
- 使用EasyExcel注解
- 字段校验和格式化
---
## 7. 业务规则
### 7.1 唯一性约束
1. **个人中介**:
- `person_id`(证件号)必须唯一
- 新增时检查是否已存在
- 修改时检查是否已存在(排除自身)
2. **实体中介**:
- `social_credit_code`(统一社会信用代码)必须唯一
- 新增时检查是否已存在
- 修改时检查是否已存在(排除自身)
### 7.2 数据自动填充
**个人中介**:
- data_source: MANUAL(手动录入) / IMPORT(批量导入)
- person_type: 中介
**实体中介**:
- risk_level: 1 (高风险)
- ent_source: INTERMEDIARY (中介)
- data_source: MANUAL(手动录入) / IMPORT(批量导入)
### 7.3 字典类型
| 字典类型 | 用途 |
|---------|------|
| ccdi_indiv_gender | 个人中介性别 |
| ccdi_certificate_type | 证件类型 |
| ccdi_relation_type | 关联关系 |
| ccdi_entity_type | 主体类型 |
| ccdi_enterprise_nature | 企业性质 |
---
## 8. 错误处理
### 8.1 业务错误码
| 错误码 | 说明 |
|--------|------|
| 1001 | 证件号已存在 |
| 1002 | 统一社会信用代码已存在 |
| 1003 | 数据不存在 |
| 1004 | 姓名不能为空 |
| 1005 | 证件号不能为空 |
| 1006 | 企业名称不能为空 |
### 8.2 异常处理策略
- 使用`@ControllerAdvice`全局异常处理
- 业务异常使用自定义BizException
- 参数校验异常自动返回字段错误信息
---
## 9. 测试策略
### 9.1 单元测试
- Service层业务逻辑测试
- Mapper层SQL查询测试
- 唯一性校验测试
### 9.2 集成测试
- Controller接口测试
- 导入导出功能测试
- 联合查询分页测试
### 9.3 测试脚本
- 生成可执行的HTTP测试脚本
- 使用admin/admin123账号获取token
- 保存测试结果并生成测试报告
---
## 10. 实现计划
### 10.1 开发顺序
1. 创建Entity实体类
2. 创建Mapper接口和XML
3. 创建DTO/VO对象
4. 实现Service层业务逻辑
5. 实现Controller层接口
6. 实现Excel导入导出功能
7. 编写测试用例
8. 生成API文档
### 10.2 技术要点
- 使用MyBatis Plus的BaseMapper简化CRUD操作
- 使用@Resource注入,替代@Autowired
- 实体类不继承BaseEntity单独添加审计字段
- 简单CRUD使用MyBatis Plus方法复杂查询使用XML
- 所有Controller接口添加完整的Swagger注解
- 使用@Validated和JSR-303进行参数校验
---
## 11. 附录
### 11.1 相关文档
- [中介黑名单管理API文档.md](../api/中介黑名单管理API文档.md)
- [中介黑名单后端.md](../docs/中介黑名单后端.md)
- [ccdi_biz_intermediary.csv](../docs/ccdi_biz_intermediary.csv)
- [ccdi_enterprise_base_info.csv](../docs/ccdi_enterprise_base_info.csv)
### 11.2 更新日志
| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0 | 2026-02-04 | 初始版本,完成系统设计 |
---
**文档结束**

View File

@@ -0,0 +1,226 @@
# EasyExcel字典下拉框使用说明
## 功能概述
本项目实现了EasyExcel自定义WriteHandler拦截器可以在生成Excel模板时自动添加基于若依框架字典数据的下拉框。
## 核心组件
### 1. @DictDropdown 注解
位置:`com.ruoyi.common.annotation.DictDropdown`
用于标注需要添加下拉框的字段。
**属性说明:**
| 属性 | 类型 | 默认值 | 说明 |
|-----|------|--------|------|
| dictType | String | 必填 | 字典类型编码,对应若依字典管理中的字典类型 |
| displayType | DisplayType | LABEL | 下拉框显示内容类型LABEL显示标签VALUE显示值 |
| strict | boolean | true | 是否仅允许选择下拉框中的值 |
| hiddenSheetName | String | "dict_hidden" | 隐藏Sheet名称用于存储大量下拉选项 |
### 2. DictDropdownWriteHandler 处理器
位置:`com.ruoyi.dpc.handler.DictDropdownWriteHandler`
核心功能:
- 解析实体类中的@DictDropdown注解
- 从若依字典缓存获取字典数据
- 为对应列添加下拉框验证
- 自动处理下拉选项超过Excel字符限制的情况使用隐藏Sheet
### 3. EasyExcelUtil 工具类扩展
位置:`com.ruoyi.dpc.utils.EasyExcelUtil`
新增方法:
- `importTemplateWithDictDropdown()` - 下载带字典下拉框的导入模板
- `exportExcelWithDictDropdown()` - 导出带字典下拉框的Excel
## 使用示例
### 步骤1在实体类上添加注解
```java
package com.ruoyi.dpc.domain.excel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.ruoyi.common.annotation.DictDropdown;
import lombok.Data;
@Data
public class CcdiEmployeeExcel {
@ExcelProperty(value = "姓名", index = 0)
@ColumnWidth(15)
private String name;
@ExcelProperty(value = "柜员号", index = 1)
@ColumnWidth(15)
private String tellerNo;
// 添加字典下拉框注解
@ExcelProperty(value = "状态", index = 6)
@ColumnWidth(10)
@DictDropdown(dictType = "ccdi_employee_status")
private String status;
}
```
### 步骤2在Controller中使用
```java
@RestController
@RequestMapping("/ccdi/employee")
public class CcdiEmployeeController {
/**
* 下载带字典下拉框的导入模板
*/
@PostMapping("/importTemplateWithDropdown")
public void importTemplateWithDropdown(HttpServletResponse response) {
EasyExcelUtil.importTemplateWithDictDropdown(
response,
CcdiEmployeeExcel.class,
"员工信息"
);
}
/**
* 导出带字典下拉框的Excel
*/
@PostMapping("/exportWithDropdown")
public void exportWithDropdown(HttpServletResponse response) {
List<CcdiEmployeeExcel> list = employeeService.selectEmployeeList();
EasyExcelUtil.exportExcelWithDictDropdown(
response,
list,
CcdiEmployeeExcel.class,
"员工信息"
);
}
}
```
## 高级用法
### 1. 显示字典键值而非标签
```java
@DictDropdown(dictType = "ccdi_employee_status", displayType = DisplayType.VALUE)
private String status;
```
### 2. 允许手动输入(非严格模式)
```java
@DictDropdown(dictType = "ccdi_employee_status", strict = false)
private String status;
```
### 3. 自定义隐藏Sheet名称
```java
@DictDropdown(dictType = "ccdi_employee_status", hiddenSheetName = "employee_status_dict")
private String status;
```
## 注意事项
1. **必须指定@ExcelProperty的index属性**
- 字段必须指定@ExcelProperty注解的index值,否则无法正确映射列位置
2. **字典数据必须预先加载到缓存**
- 使用前需要确保字典数据已经加载到Redis缓存中
- 可通过若依系统的字典管理功能预热缓存
3. **下拉选项数量限制**
- 当下拉选项总长度超过255字符时自动使用隐藏Sheet存储
- 隐藏Sheet在Excel中不可见但下拉框功能正常
4. **字段必须标注@ExcelProperty注解**
- 只有同时标注了@ExcelProperty和@DictDropdown的字段才会添加下拉框
## 测试验证
### 接口测试
1. 启动项目后访问Swagger UI`http://localhost:8080/swagger-ui/index.html`
2. 找到员工信息管理相关接口:
- `POST /ccdi/employee/importTemplateWithDropdown` - 下载带字典下拉框的模板
3. 调用接口下载模板检查Excel中的下拉框是否正常
### 手动验证
1. 打开下载的Excel模板
2. 点击标注了下拉框的列(如"状态"列)
3. 检查是否出现下拉箭头和选项列表
4. 尝试选择和输入,验证验证规则是否生效
## 技术实现细节
### Excel下拉列表限制处理
Excel对下拉列表的直接字符数有限制约255字符本项目采用以下策略
1. **选项较少时(<255字符**
- 直接使用 `DataValidationHelper.createExplicitListConstraint()` 创建下拉列表
- 下拉选项内联在单元格验证中
2. **选项较多时≥255字符**
- 创建隐藏Sheet存储所有选项
- 使用 `DataValidationHelper.createFormulaListConstraint()` 通过公式引用
- 自动隐藏Sheet`workbook.setSheetHidden()`
### 字典数据获取
```
┌─────────────┐ 缓存查询 ┌─────────────┐
│ DictDropdown │ ───────────▶ │ DictUtils │
│ 注解 │ │ .getDictCache() │
└─────────────┘ └─────────────┘
┌─────────────┐
│ Redis缓存 │
│ sys_dict:key │
└─────────────┘
```
### 列索引映射
通过反射获取字段的@ExcelProperty注解中的index值,确保下拉框添加到正确的列。
## 常见问题
### Q1下拉框没有显示
**可能原因:**
1. 字典数据未加载到缓存
2. 字段未指定@ExcelProperty的index值
3. 字典类型编码错误
**解决方法:**
1. 在若依系统字典管理中,进入对应字典类型,刷新缓存
2. 检查实体类字段注解是否正确
3. 确认dictType值与字典管理中的字典类型一致
### Q2下拉选项显示不完整
**原因:** 选项字符数超过255字符但隐藏Sheet创建失败
**解决方法:** 检查日志中的错误信息确保有权限创建隐藏Sheet
### Q3可以手动输入非下拉选项的值吗
**答案:** 可以,通过设置 `strict = false` 允许手动输入
## 更新日志
| 版本 | 日期 | 说明 |
|------|------|------|
| 1.0.0 | 2026-01-29 | 初始版本,支持字典下拉框功能 |

View File

@@ -0,0 +1,23 @@
中介人员基本信息表ccdi_biz_intermediary,,,,,,
序号,字段名,类型,默认值,是否可为空,是否主键,注释
1,biz_id,VARCHAR,-,,,人员ID
2,person_type,VARCHAR,-,,,人员类型,中介、职业背债人、房产中介等
3,person_sub_type,VARCHAR,-,,,人员子类型
5,name,VARCHAR,-,,,姓名
6,gender,CHAR,-,,,性别
7,id_type,VARCHAR,身份证,,,证件类型
8,person_id,VARCHAR,-,,,证件号码
9,mobile,VARCHAR,-,,,手机号码
10,wechat_no,VARCHAR,-,,,微信号
11,contact_address,VARCHAR,-,,,联系地址
12,company,VARCHAR,-,,,所在公司
13,social_credit_code,VARCHAR,,,,企业统一信用码
14,position,VARCHAR,-,,,职位
15,related_num_id,VARCHAR,-,,,关联人员ID
16,relation_type,VARCHAR,-,,,关系类型,如:配偶、子女、父母、兄弟姐妹等
17,date_source,,,,,"数据来源MANUAL:手动录入, SYSTEM:系统同步, IMPORT:批量导入, API:接口获取"
18,remark,,,,,备注信息
19,created_by,VARCHAR,-,,-,记录创建人
20,updated_by,VARCHAR,-,,-,记录更新人
21,create_time,DATETIME,,,,记录创建时间
22,update_time,DATETIME,-,,-,记录更新时间
1 中介人员基本信息表:ccdi_biz_intermediary
2 序号 字段名 类型 默认值 是否可为空 是否主键 注释
3 1 biz_id VARCHAR - 人员ID
4 2 person_type VARCHAR - 人员类型,中介、职业背债人、房产中介等
5 3 person_sub_type VARCHAR - 人员子类型
6 5 name VARCHAR - 姓名
7 6 gender CHAR - 性别
8 7 id_type VARCHAR 身份证 证件类型
9 8 person_id VARCHAR - 证件号码
10 9 mobile VARCHAR - 手机号码
11 10 wechat_no VARCHAR - 微信号
12 11 contact_address VARCHAR - 联系地址
13 12 company VARCHAR - 所在公司
14 13 social_credit_code VARCHAR 企业统一信用码
15 14 position VARCHAR - 职位
16 15 related_num_id VARCHAR - 关联人员ID
17 16 relation_type VARCHAR - 关系类型,如:配偶、子女、父母、兄弟姐妹等
18 17 date_source 数据来源,MANUAL:手动录入, SYSTEM:系统同步, IMPORT:批量导入, API:接口获取
19 18 remark 备注信息
20 19 created_by VARCHAR - - 记录创建人
21 20 updated_by VARCHAR - - 记录更新人
22 21 create_time DATETIME 记录创建时间
23 22 update_time DATETIME - - 记录更新时间

View File

@@ -0,0 +1,26 @@
3.企业主体信息表ccdi_enterprise_base_info,,,,,,
序号,字段名,类型,默认值,是否可为空,是否主键,注释
1,social_credit_code,VARCHAR,-,,,统一社会信用代码,员工企业关联关系表的外键
2,enterprise_name,VARCHAR,-,,-,企业名称
3,enterprise_type,VARCHAR,-,,-,"企业类型,有限责任公司、股份有限公司、合伙企业、个体工商户、外资企业等"
4,enterprise_nature,VARCHAR,-,,-,"企业性质,国企、民企、外企、合资、其他"
5,industry_class,VARCHAR,-,,-,行业分类
6,industry_name,VARCHAR,-,,-,所属行业
7,establish_date,DATE,-,,-,成立日期
8,register_address,VARCHAR,-,,-,注册地址
9,legal_representative,VARCHAR,-,,-,法定代表人
10,legal_cert_type,VARCHAR,-,,-,法定代表人证件类型
11,legal_cert_no,VARCHAR,-,,-,法定代表人证件号码
12,shareholder1,VARCHAR,-,,-,股东1
13,shareholder2,VARCHAR,-,,-,股东2
14,shareholder3,VARCHAR,-,,-,股东3
15,shareholder4,VARCHAR,-,,-,股东4
16,shareholder5,VARCHAR,-,,-,股东5
17,status,VARCHAR,,,,经营状态
18,create_time,DATETIME,当前时间,,-,创建时间
19,update_time,DATETIME,当前时间,,-,更新时间
20,created_by,VARCHAR,-,,-,创建人
21,updated_by,VARCHAR,-,,-,更新人
22,data_source,VARCHAR,MANUAL,,-,"数据来源,MANUAL:手动录入, SYSTEM:系统同步, API:接口获取, IMPORT:批量导入"
23,risk_level,VARCHAR(10),1,,,"风险等级1-高风险, 2-中风险, 3-低风险"
24,ent_source,VARCHAR(20),GENERAL,,,"企业来源GENERAL-一般企业, EMP_RELATION-员工关系人, CREDIT_CUSTOMER-信贷客户, INTERMEDIARY-中介, BOTH-兼有"
1 3.企业主体信息表:ccdi_enterprise_base_info
2 序号 字段名 类型 默认值 是否可为空 是否主键 注释
3 1 social_credit_code VARCHAR - 统一社会信用代码,员工企业关联关系表的外键
4 2 enterprise_name VARCHAR - - 企业名称
5 3 enterprise_type VARCHAR - - 企业类型,有限责任公司、股份有限公司、合伙企业、个体工商户、外资企业等
6 4 enterprise_nature VARCHAR - - 企业性质,国企、民企、外企、合资、其他
7 5 industry_class VARCHAR - - 行业分类
8 6 industry_name VARCHAR - - 所属行业
9 7 establish_date DATE - - 成立日期
10 8 register_address VARCHAR - - 注册地址
11 9 legal_representative VARCHAR - - 法定代表人
12 10 legal_cert_type VARCHAR - - 法定代表人证件类型
13 11 legal_cert_no VARCHAR - - 法定代表人证件号码
14 12 shareholder1 VARCHAR - - 股东1
15 13 shareholder2 VARCHAR - - 股东2
16 14 shareholder3 VARCHAR - - 股东3
17 15 shareholder4 VARCHAR - - 股东4
18 16 shareholder5 VARCHAR - - 股东5
19 17 status VARCHAR 经营状态
20 18 create_time DATETIME 当前时间 - 创建时间
21 19 update_time DATETIME 当前时间 - 更新时间
22 20 created_by VARCHAR - - 创建人
23 21 updated_by VARCHAR - - 更新人
24 22 data_source VARCHAR MANUAL - 数据来源,MANUAL:手动录入, SYSTEM:系统同步, API:接口获取, IMPORT:批量导入
25 23 risk_level VARCHAR(10) 1 风险等级:1-高风险, 2-中风险, 3-低风险
26 24 ent_source VARCHAR(20) GENERAL 企业来源:GENERAL-一般企业, EMP_RELATION-员工关系人, CREDIT_CUSTOMER-信贷客户, INTERMEDIARY-中介, BOTH-兼有

View File

@@ -0,0 +1,28 @@
1.人员家庭关系表ccdi_fmy_relation_person,,,,,,
序号,字段名,类型,默认值,是否可为空,是否主键,注释
1,id,BIGINT,-,,自动递增,主键,唯一标识
2,person_id,VARCHAR,-,,-,员工身份证号,关联员工表的外键
3,relation_type,VARCHAR,-,,-,关系类型,如:配偶、子女、父母、兄弟姐妹等
4,relation_name,VARCHAR,-,,-,关系人姓名
5,gender,CHAR,-,,-,M:男 F:女 O:其他
6,birth_date,DATE,-,,-,关系人出生日期
7,relation_cert_type,VARCHAR,-,,-,身份证、护照、军官证等
8,relation_cert_no,VARCHAR,-,,-,证件号码
9,mobile_phone1,VARCHAR,-,,-,手机号码1
10,mobile_phone2,VARCHAR,-,,-,手机号码2
11,wechat_no1,VARCHAR,-,,-,微信名称1
12,wechat_no2,VARCHAR,-,,-,微信名称2
13,wechat_no3,VARCHAR,-,,-,微信名称3
14,contact_address,VARCHAR,-,,-,详细联系地址
15,relation_desc,VARCHAR,-,,-,关系详细描述
16,status,INT,1,,-,关系是否有效0 - 无效、1 - 有效(默认有效)
17,effective_date,DATETIME,-,,-,关系生效日期
18,invalid_date,DATETIME,,,,关系失效日期
19,remark,TEXT,-,,-,备注信息
20,data_source,VARCHAR(50),,,,数据来源(系统名称)
21,is_emp_family,TINYINT(1),0,,,是否是员工的家庭关系0-否 1-是
22,is_cust_family,TINYINT(1),0,,,是否是信贷客户的家庭关系0-否 1-是
23,created_by,VARCHAR,-,,-,记录创建人
24,updated_by,VARCHAR,-,,-,记录更新人
25,create_time,DATETIME,,,,记录创建时间
26,update_time,DATETIME,-,,-,记录更新时间
1 1.人员家庭关系表:ccdi_fmy_relation_person
2 序号 字段名 类型 默认值 是否可为空 是否主键 注释
3 1 id BIGINT - 自动递增 主键,唯一标识
4 2 person_id VARCHAR - - 员工身份证号,关联员工表的外键
5 3 relation_type VARCHAR - - 关系类型,如:配偶、子女、父母、兄弟姐妹等
6 4 relation_name VARCHAR - - 关系人姓名
7 5 gender CHAR - - M:男 F:女 O:其他
8 6 birth_date DATE - - 关系人出生日期
9 7 relation_cert_type VARCHAR - - 身份证、护照、军官证等
10 8 relation_cert_no VARCHAR - - 证件号码
11 9 mobile_phone1 VARCHAR - - 手机号码1
12 10 mobile_phone2 VARCHAR - - 手机号码2
13 11 wechat_no1 VARCHAR - - 微信名称1
14 12 wechat_no2 VARCHAR - - 微信名称2
15 13 wechat_no3 VARCHAR - - 微信名称3
16 14 contact_address VARCHAR - - 详细联系地址
17 15 relation_desc VARCHAR - - 关系详细描述
18 16 status INT 1 - 关系是否有效:0 - 无效、1 - 有效(默认有效)
19 17 effective_date DATETIME - - 关系生效日期
20 18 invalid_date DATETIME 关系失效日期
21 19 remark TEXT - - 备注信息
22 20 data_source VARCHAR(50) 数据来源(系统名称)
23 21 is_emp_family TINYINT(1) 0 是否是员工的家庭关系:0-否 1-是
24 22 is_cust_family TINYINT(1) 0 是否是信贷客户的家庭关系:0-否 1-是
25 23 created_by VARCHAR - - 记录创建人
26 24 updated_by VARCHAR - - 记录更新人
27 25 create_time DATETIME 记录创建时间
28 26 update_time DATETIME - - 记录更新时间

View File

@@ -0,0 +1,38 @@
6.员工采购交易信息表ccdi_purchase_transaction,,,,,,
序号,字段名,类型,默认值,是否可为空,是否主键,注释
1,purchase_id,VARCHAR(32),,,,采购事项ID
2,purchase_category,VARCHAR(50),-,,,采购类别
3,project_name,VARCHAR(200),-,,,项目名称
4,subject_name,VARCHAR(200),-,,,标的物名称
5,subject_desc,TEXT,-,,,标的物描述
6,purchase_qty,"DECIMAL(12,4)",1,,,采购数量
7,budget_amount,"DECIMAL(18,2)",-,,,预算金额
8,bid_amount,"DECIMAL(18,2)",-,,,中标金额
9,actual_amount,"DECIMAL(18,2)",-,,,实际采购金额
10,contract_amount,"DECIMAL(18,2)",-,,,合同金额
11,settlement_amount,"DECIMAL(18,2)",-,,,结算金额
12,purchase_method,VARCHAR(50),-,,,采购方式
13,supplier_name,VARCHAR(200),-,,,中标供应商名称
14,contact_person,VARCHAR(50),-,,,供应商联系人
15,contact_phone,VARCHAR(20),-,,,供应商联系电话
16,supplier_uscc,VARCHAR(18),-,,,供应商统一信用代码
17,supplier_bank_account,VARCHAR(50),-,,,供应商银行账户
18,apply_date,DATE,-,,,采购申请日期(或立项日期)
19,plan_approve_date,DATE,-,,,采购计划批准日期
20,announce_date,DATE,-,,,采购公告发布日期
21,bid_open_date,DATE,-,,,开标日期
22,contract_sign_date,DATE,-,,,合同签订日期
23,expected_delivery_date,DATE,-,,,预计交货日期
24,actual_delivery_date,DATE,-,,,实际交货日期
25,acceptance_date,DATE,-,,,验收日期
26,settlement_date,DATE,-,,,结算日期
27,applicant_id,VARCHAR(7),-,,,申请人工号
28,applicant_name,VARCHAR(50),-,,,申请人姓名
29,apply_department,VARCHAR(100),-,,,申请部门
30,purchase_leader_id,VARCHAR(7),-,,,采购负责人工号
31,purchase_leader_name,VARCHAR(50),-,,,采购负责人姓名
32,purchase_department,VARCHAR(100),-,,,采购部门
33,create_time,DATETIME,CURRENT_TIMESTAMP,,,创建时间
34,update_time,DATETIME,CURRENT_TIMESTAMP,,,更新时间
35,created_by,VARCHAR(50),-,,,创建人
36,updated_by,VARCHAR(50),-,,,更新人
1 6.员工采购交易信息表:ccdi_purchase_transaction
2 序号 字段名 类型 默认值 是否可为空 是否主键 注释
3 1 purchase_id VARCHAR(32) 采购事项ID
4 2 purchase_category VARCHAR(50) - 采购类别
5 3 project_name VARCHAR(200) - 项目名称
6 4 subject_name VARCHAR(200) - 标的物名称
7 5 subject_desc TEXT - 标的物描述
8 6 purchase_qty DECIMAL(12,4) 1 采购数量
9 7 budget_amount DECIMAL(18,2) - 预算金额
10 8 bid_amount DECIMAL(18,2) - 中标金额
11 9 actual_amount DECIMAL(18,2) - 实际采购金额
12 10 contract_amount DECIMAL(18,2) - 合同金额
13 11 settlement_amount DECIMAL(18,2) - 结算金额
14 12 purchase_method VARCHAR(50) - 采购方式
15 13 supplier_name VARCHAR(200) - 中标供应商名称
16 14 contact_person VARCHAR(50) - 供应商联系人
17 15 contact_phone VARCHAR(20) - 供应商联系电话
18 16 supplier_uscc VARCHAR(18) - 供应商统一信用代码
19 17 supplier_bank_account VARCHAR(50) - 供应商银行账户
20 18 apply_date DATE - 采购申请日期(或立项日期)
21 19 plan_approve_date DATE - 采购计划批准日期
22 20 announce_date DATE - 采购公告发布日期
23 21 bid_open_date DATE - 开标日期
24 22 contract_sign_date DATE - 合同签订日期
25 23 expected_delivery_date DATE - 预计交货日期
26 24 actual_delivery_date DATE - 实际交货日期
27 25 acceptance_date DATE - 验收日期
28 26 settlement_date DATE - 结算日期
29 27 applicant_id VARCHAR(7) - 申请人工号
30 28 applicant_name VARCHAR(50) - 申请人姓名
31 29 apply_department VARCHAR(100) - 申请部门
32 30 purchase_leader_id VARCHAR(7) - 采购负责人工号
33 31 purchase_leader_name VARCHAR(50) - 采购负责人姓名
34 32 purchase_department VARCHAR(100) - 采购部门
35 33 create_time DATETIME CURRENT_TIMESTAMP 创建时间
36 34 update_time DATETIME CURRENT_TIMESTAMP 更新时间
37 35 created_by VARCHAR(50) - 创建人
38 36 updated_by VARCHAR(50) - 更新人

View File

@@ -0,0 +1,18 @@
2.企业关联关系表ccdi_staff_enterprise_relation,,,,,,
序号,字段名,类型,默认值,是否可为空,是否主键,注释
1,id,BIGINT,-,,自动递增,主键,唯一标识
2,person_id,VARCHAR,-,,-,身份证号,关联员工表的外键
3,relation_person_post,VARCHAR,-,,-,关联人在企业的职务:股东、法人、高管、实际控制人等
4,social_credit_code,VARCHAR,-,,-,统一社会信用代码,关联企业主体信息表的外键
5,enterprise_name,VARCHAR,-,,-,企业名称(冗余存储,便于快速查询)
6,status,INT,1,,-,关系是否有效0 - 无效、1 - 有效(默认有效)
7,remark,TEXT,-,,-,补充说明
8,data_source,VARCHAR(50),,,,数据来源
9,is_employee,TINYINT(1),0,,,是否是员工0-否 1-是
10,is_emp_family,TINYINT(1),0,,,是否是员工家庭关联人0-否 1-是
11,is_customer,TINYINT(1),0,,,是否是信贷客户0-否 1-是
12,is_cust_family,TINYINT(1),0,,,是否是信贷客户关联人0-否 1-是
13,created_by,VARCHAR,-,,-,记录创建人
14,updated_by,VARCHAR,-,,-,记录更新人
15,create_time,DATETIME,-,,-,记录创建时间
16,update_time,DATETIME,-,,-,记录更新时间
1 2.企业关联关系表:ccdi_staff_enterprise_relation
2 序号 字段名 类型 默认值 是否可为空 是否主键 注释
3 1 id BIGINT - 自动递增 主键,唯一标识
4 2 person_id VARCHAR - - 身份证号,关联员工表的外键
5 3 relation_person_post VARCHAR - - 关联人在企业的职务:股东、法人、高管、实际控制人等
6 4 social_credit_code VARCHAR - - 统一社会信用代码,关联企业主体信息表的外键
7 5 enterprise_name VARCHAR - - 企业名称(冗余存储,便于快速查询)
8 6 status INT 1 - 关系是否有效:0 - 无效、1 - 有效(默认有效)
9 7 remark TEXT - - 补充说明
10 8 data_source VARCHAR(50) 数据来源
11 9 is_employee TINYINT(1) 0 是否是员工:0-否 1-是
12 10 is_emp_family TINYINT(1) 0 是否是员工家庭关联人:0-否 1-是
13 11 is_customer TINYINT(1) 0 是否是信贷客户:0-否 1-是
14 12 is_cust_family TINYINT(1) 0 是否是信贷客户关联人:0-否 1-是
15 13 created_by VARCHAR - - 记录创建人
16 14 updated_by VARCHAR - - 记录更新人
17 15 create_time DATETIME - - 记录创建时间
18 16 update_time DATETIME - - 记录更新时间

View File

@@ -0,0 +1,28 @@
1.人员家庭关系表ccdi_staff_fmy_relation,,,,,,
序号,字段名,类型,默认值,是否可为空,是否主键,注释
1,id,BIGINT,-,,自动递增,主键,唯一标识
2,person_id,VARCHAR,-,,-,员工身份证号,关联员工表的外键
3,relation_type,VARCHAR,-,,-,关系类型,如:配偶、子女、父母、兄弟姐妹等
4,relation_name,VARCHAR,-,,-,关系人姓名
5,gender,CHAR,-,,-,M:男 F:女 O:其他
6,birth_date,DATE,-,,-,关系人出生日期
7,relation_cert_type,VARCHAR,-,,-,身份证、护照、军官证等
8,relation_cert_no,VARCHAR,-,,-,证件号码
9,mobile_phone1,VARCHAR,-,,-,手机号码1
10,mobile_phone2,VARCHAR,-,,-,手机号码2
11,wechat_no1,VARCHAR,-,,-,微信名称1
12,wechat_no2,VARCHAR,-,,-,微信名称2
13,wechat_no3,VARCHAR,-,,-,微信名称3
14,contact_address,VARCHAR,-,,-,详细联系地址
15,relation_desc,VARCHAR,-,,-,关系详细描述
16,status,INT,1,,-,关系是否有效0 - 无效、1 - 有效(默认有效)
17,effective_date,DATETIME,-,,-,关系生效日期
18,invalid_date,DATETIME,,,,关系失效日期
19,remark,TEXT,-,,-,备注信息
20,data_source,VARCHAR(50),,,,数据来源(系统名称)
21,is_emp_family,TINYINT(1),0,,,是否是员工的家庭关系0-否 1-是
22,is_cust_family,TINYINT(1),0,,,是否是信贷客户的家庭关系0-否 1-是
23,created_by,VARCHAR,-,,-,记录创建人
24,updated_by,VARCHAR,-,,-,记录更新人
25,create_time,DATETIME,,,,记录创建时间
26,update_time,DATETIME,-,,-,记录更新时间
1 1.人员家庭关系表:ccdi_staff_fmy_relation
2 序号 字段名 类型 默认值 是否可为空 是否主键 注释
3 1 id BIGINT - 自动递增 主键,唯一标识
4 2 person_id VARCHAR - - 员工身份证号,关联员工表的外键
5 3 relation_type VARCHAR - - 关系类型,如:配偶、子女、父母、兄弟姐妹等
6 4 relation_name VARCHAR - - 关系人姓名
7 5 gender CHAR - - M:男 F:女 O:其他
8 6 birth_date DATE - - 关系人出生日期
9 7 relation_cert_type VARCHAR - - 身份证、护照、军官证等
10 8 relation_cert_no VARCHAR - - 证件号码
11 9 mobile_phone1 VARCHAR - - 手机号码1
12 10 mobile_phone2 VARCHAR - - 手机号码2
13 11 wechat_no1 VARCHAR - - 微信名称1
14 12 wechat_no2 VARCHAR - - 微信名称2
15 13 wechat_no3 VARCHAR - - 微信名称3
16 14 contact_address VARCHAR - - 详细联系地址
17 15 relation_desc VARCHAR - - 关系详细描述
18 16 status INT 1 - 关系是否有效:0 - 无效、1 - 有效(默认有效)
19 17 effective_date DATETIME - - 关系生效日期
20 18 invalid_date DATETIME 关系失效日期
21 19 remark TEXT - - 备注信息
22 20 data_source VARCHAR(50) 数据来源(系统名称)
23 21 is_emp_family TINYINT(1) 0 是否是员工的家庭关系:0-否 1-是
24 22 is_cust_family TINYINT(1) 0 是否是信贷客户的家庭关系:0-否 1-是
25 23 created_by VARCHAR - - 记录创建人
26 24 updated_by VARCHAR - - 记录更新人
27 25 create_time DATETIME 记录创建时间
28 26 update_time DATETIME - - 记录更新时间

View File

@@ -0,0 +1,22 @@
4.员工招聘信息表ccdi_staff_recruitment,,,,,,
序号,字段名,类型,默认值,是否可为空,是否主键,注释
1,recruit_id,VARCHAR(32),,,,招聘项目编号
2,recruit_name,VARCHAR(100),,,,招聘项目名称
3,pos_name,VARCHAR(100),,,,职位名称
4,pos_category,VARCHAR(50),,,,职位类别
5,pos_desc,TEXT,,,,职位描述
6,cand_name,VARCHAR(20),,,,应聘人员姓名
7,cand_edu,VARCHAR(20),,,,应聘人员学历
8,cand_id,VARCHAR(18),,,,应聘人员证件号码
9,cand_school,VARCHAR(50),,,,应聘人员毕业院校
10,cand_major,VARCHAR(30),,,,应聘人员专业
11,cand_grad,VARCHAR(6),,,,应聘人员毕业年月
12,admit_status,VARCHAR(10),,,,记录录用情况:录用、未录用、放弃等
13,interviewer_name1,VARCHAR(20),,,,面试官1姓名
14,interviewer_id1,VARCHAR(10),,,,面试官1工号
13,interviewer_name2,VARCHAR(20),,,,面试官2姓名
14,interviewer_id2,VARCHAR(10),,,,面试官2工号
16,created_by,VARCHAR(20),-,,,记录创建人
17,updated_by,VARCHAR(20),-,,,记录更新人
18,create_time,VARCHAR(10),0000-00-00,,,创建时间
19,update_time,VARCHAR(10),0000-00-00,,,更新时间
1 4.员工招聘信息表:ccdi_staff_recruitment
2 序号 字段名 类型 默认值 是否可为空 是否主键 注释
3 1 recruit_id VARCHAR(32) 招聘项目编号
4 2 recruit_name VARCHAR(100) 招聘项目名称
5 3 pos_name VARCHAR(100) 职位名称
6 4 pos_category VARCHAR(50) 职位类别
7 5 pos_desc TEXT 职位描述
8 6 cand_name VARCHAR(20) 应聘人员姓名
9 7 cand_edu VARCHAR(20) 应聘人员学历
10 8 cand_id VARCHAR(18) 应聘人员证件号码
11 9 cand_school VARCHAR(50) 应聘人员毕业院校
12 10 cand_major VARCHAR(30) 应聘人员专业
13 11 cand_grad VARCHAR(6) 应聘人员毕业年月
14 12 admit_status VARCHAR(10) 记录录用情况:录用、未录用、放弃等
15 13 interviewer_name1 VARCHAR(20) 面试官1姓名
16 14 interviewer_id1 VARCHAR(10) 面试官1工号
17 13 interviewer_name2 VARCHAR(20) 面试官2姓名
18 14 interviewer_id2 VARCHAR(10) 面试官2工号
19 16 created_by VARCHAR(20) - 记录创建人
20 17 updated_by VARCHAR(20) - 记录更新人
21 18 create_time VARCHAR(10) 0000-00-00 创建时间
22 19 update_time VARCHAR(10) 0000-00-00 更新时间

View File

@@ -0,0 +1,18 @@
5.员工调动记录表ccdi_staff_transfer,,,,,,
序号,字段名,类型,默认值,是否可为空,是否主键,注释
1,num_id,string,,,,员工工号(主键)
2,transfer_type,VARCHAR,,,,"调动类型:PROMOTION:升职, DEMOTION:降职, LATERAL:平调, ROTATION:轮岗, SECONDMENT:借调, DEPARTMENT_CHANGE:部门调动, POSITION_CHANGE:职位调整, RETURN:返岗, TERMINATION:离职, OTHER:其他"
3,transfer_sub_type,VARCHAR,,,,"调动子类型,双聘调动、临时调动等"
4,dept_id_before,VARCHAR,,,,调动前部门ID
5,dept_name_before,VARCHAR,,,,调动前部门
6,grade_before,VARCHAR,,,,调动前职级
7,position_before,VARCHAR,,,,调动前岗位
8,salary_level_before,VARCHAR,,,,调动前薪酬等级
9,dept_id_after,VARCHAR,0000-00-00,,,调动后部门ID
10,dept_name_after,VARCHAR,0000-00-00,,,调动后部门
11,grade_after,VARCHAR,,,,调动后职级
12,position_after,VARCHAR,,,,调动后岗位
13,salary_level_after,VARCHAR,,,,调动后薪酬等级
14,transfer_date,DATE,,,,调动日期
15,create_time,DATETIME,-,,当前时间,记录创建时间
16,update_time,DATETIME,-,,当前时间,记录更新时间
1 5.员工调动记录表:ccdi_staff_transfer
2 序号 字段名 类型 默认值 是否可为空 是否主键 注释
3 1 num_id string 员工工号(主键)
4 2 transfer_type VARCHAR 调动类型:PROMOTION:升职, DEMOTION:降职, LATERAL:平调, ROTATION:轮岗, SECONDMENT:借调, DEPARTMENT_CHANGE:部门调动, POSITION_CHANGE:职位调整, RETURN:返岗, TERMINATION:离职, OTHER:其他
5 3 transfer_sub_type VARCHAR 调动子类型,双聘调动、临时调动等
6 4 dept_id_before VARCHAR 调动前部门ID
7 5 dept_name_before VARCHAR 调动前部门
8 6 grade_before VARCHAR 调动前职级
9 7 position_before VARCHAR 调动前岗位
10 8 salary_level_before VARCHAR 调动前薪酬等级
11 9 dept_id_after VARCHAR 0000-00-00 调动后部门ID
12 10 dept_name_after VARCHAR 0000-00-00 调动后部门
13 11 grade_after VARCHAR 调动后职级
14 12 position_after VARCHAR 调动后岗位
15 13 salary_level_after VARCHAR 调动后薪酬等级
16 14 transfer_date DATE 调动日期
17 15 create_time DATETIME - 当前时间 记录创建时间
18 16 update_time DATETIME - 当前时间 记录更新时间

View File

@@ -0,0 +1 @@
实现中介黑名单管理的后端接口开发。中介分为个人中介和实体中介。个人中介的表字段为 @ccdi_biz_intermediary.csv。实体中介表字段为 @ccdi_enterprise_base_info.csv风险等级为高风险企业来源为中介。需要生成的接口个人中介的新增、修改接口以证件号为关联键个人中介导入模板下载个人中介文件上传导入新增实体中介类的新增、修改接口实体中介导入模板下载上传导入新增列表查询要求联合查询两种类型的中介也可以支持查询单种类的中介。

View File

@@ -0,0 +1,153 @@
# 中介黑名单弹窗优化设计
## 需求概述
优化中介黑名单的添加弹窗交互流程:
1. 点击新增后先选择中介类型(个人/机构)
2. 然后弹出对应类型的信息输入窗口
3. 不需要tab栏直接显示对应类型的表单
4. 机构类型只需输入一次证件号,该值同时作为"证件号"和"统一社会信用代码"
## 设计方案
### 1. 交互流程
**新增操作流程:**
1. 用户点击"新增"按钮
2. 弹出一个简洁的对话框,顶部有两个大卡片式按钮:【个人】和【机构】
3. 用户点击其中一个类型按钮
4. 对应的表单立即展开显示在下方(无需确认操作)
5. 用户填写信息后点击"确定"提交
**修改操作:**
- 修改时直接显示原有数据的表单,不允许切换类型
### 2. 界面布局
```
┌─────────────────────────────────────┐
│ 添加中介黑名单 │
├─────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ 个人 │ │ 机构 │ │ ← 大卡片式选择按钮(仅新增时显示)
│ └─────────┘ └─────────┘ │
│ │
│ ──────────────────────────────── │ ← 分隔线
│ │
│ [对应类型的表单字段] │
│ • 姓名/机构名称 │
│ • 证件号 │
│ • 机构类型:统一社会信用代码 │
│ • 其他选填字段... │
│ │
├─────────────────────────────────────┤
│ [ 确定 ] [ 取消 ] │
└─────────────────────────────────────┘
```
### 3. 表单字段
**个人类型表单字段:**
- 姓名/机构名称*(必填)
- 证件号*(必填)
- 人员类型
- 人员子类型
- 性别
- 证件类型
- 手机号码
- 微信号
- 联系地址
- 所在公司
- 职位
- 关联人员ID
- 关联关系
- 备注
**机构类型表单字段:**
- 姓名/机构名称*(必填)
- 证件号*(必填,自动同步到统一社会信用代码)
- 主体类型
- 企业性质
- 成立日期
- 行业分类
- 所属行业
- 注册地址
- 法定代表人
- 法定代表人证件类型
- 法定代表人证件号码
- 股东1-5
- 备注
### 4. 表单验证规则
**个人类型验证:**
```javascript
rules: {
name: [
{ required: true, message: "姓名不能为空", trigger: "blur" },
{ max: 100, message: "姓名长度不能超过100个字符", trigger: "blur" }
],
certificateNo: [
{ required: true, message: "证件号不能为空", trigger: "blur" },
{ max: 50, message: "证件号长度不能超过50个字符", trigger: "blur" }
],
remark: [
{ max: 500, message: "备注长度不能超过500个字符", trigger: "blur" }
]
}
```
**机构类型验证:**
```javascript
rules: {
name: [
{ required: true, message: "机构名称不能为空", trigger: "blur" },
{ max: 100, message: "机构名称长度不能超过100个字符", trigger: "blur" }
],
certificateNo: [
{ required: true, message: "证件号不能为空", trigger: "blur" },
{ max: 18, message: "统一社会信用代码长度为18位", trigger: "blur" }
],
remark: [
{ max: 500, message: "备注长度不能超过500个字符", trigger: "blur" }
]
}
```
### 5. 边界情况处理
| 场景 | 处理方式 |
|------|----------|
| 用户点击新增后未选择类型就点确定 | 禁用"确定"按钮,直到选择类型 |
| 用户选择类型后想重新选择 | 只有关闭弹窗重新打开才能选择 |
| 修改操作时类型锁定 | 隐藏类型选择器,直接显示对应表单 |
| 表单验证失败 | 高亮显示错误字段,滚动到第一个错误位置 |
| 网络请求失败 | 显示错误提示,弹窗保持打开状态 |
### 6. 用户体验优化
1. **视觉反馈**
- 类型选择按钮在未选中时有hover效果
- 选中后按钮变为高亮状态,其他按钮变灰
- 表单展开有淡入动画
2. **输入提示**
- 个人类型的证件号字段下方显示提示:"请输入证件号码"
- 机构类型的证件号字段下方显示提示:"统一社会信用代码18位"
3. **表单布局**
- 保持两列布局,充分利用空间
- 必填项(姓名、证件号)标记红色星号
### 7. 技术实现要点
**状态管理:**
- 新增模式:`isAddMode: true`,显示类型选择器
- 修改模式:`isAddMode: false`,隐藏类型选择器
- 已选类型:`selectedType: '1' | '2' | null`
**数据同步:**
- 机构类型提交时,将 `form.certificateNo` 的值同时赋给 `form.corpCreditCode`

Binary file not shown.

View File

@@ -0,0 +1,388 @@
# 纪检初核系统功能说明书
文档版本V1.0
最后更新日期2026年1月16日
编写目的:本文档旨在全面排查员工异常行为初核系统的核心功能模块、操作流程及业务价值,为系统开发、测试及用户操作提供明确依据。系统旨在通过自动化数据分析与风险模型,高效识别员工潜在风险行为。
# 一.项目管理模块
本模块为系统首页,用于管理所有历史创建的核查项目。页面主要分为导航与搜索区、项目列表区和快捷入口区三大部分。
# 1、原型图
(1) 首页
(2) 新建项目 弹窗页入口
(3) 导入历史项目 弹窗页入口
# 2、导航与搜索区
项目搜索:支持通过输入关键词,对项目名称进行模糊搜索。
新建项目:点击打开一个标准表单,填写项目名称、人员等完整信息,自定义创建新项目。
# 3、项目列表区
本区域以表格形式清晰展示所有初核项目,是用户进行项目管理和监控的核心面板。
# (1) 列表信息列:
项目名称:显示项目名称及下方的简要描述。
创建时间:显示项目的创建日期。
状态:通过色块直观标识项目状态。包括“进行中”、“已完成”等。
目标人数:计划核查的员工总数。
预警人数:当前已被风险模型标记为存在异常行为提示的员工数量。对于“进行中”项目,此数据动态更新。
# (2操作列
查看结果(适用于已完成项目):跳转至该项目的初核结果页。
重新分析(适用于已完成项目): 基于原有数据, 重新运行风险模型, 更新分析结果。
归档将已结束且无需日常关注的项目移入归档库项目结束后可以统一归档并将相关的数据、分析过程图谱、流水等生成PDF文件导出。
进入项目(适用于进行中项目):进入该项目的工作台,开展数据管理、风险初核、专项排查等具体工作。
# 4、快捷入口区
本区域提供一键触达的高频操作按钮,提升常用工作流的启动效率。举例:
(1) 导入历史项目:复制一个历史项目的配置(如人员范围、流水、征信数据配置),快速创建新项目,实现项目模板复用:包括目标人群的流水、征信等信息
(2) 创建当季的季度初核:快速启动一个标准化的季度周期性排查项目,系统可预填当前季度时间范围等配置。
(3) 创建新员工排查:为特定新员工创建专项排查任务。
# 二.项目工作台
用户从项目列表点击“进入项目”后,将进入具体项目的操作空间,涵盖从数据准备到风险识别的全流程。并且通过侧边导航栏可以实现:
返回项目列表:返回当前项目的上一层列表页。
项目状态:明确标识当前项目阶段为“已完成”,提示用户核心分析已结束,当前可能在进行数据补充或复查。
最后更新:显示数据或项目状态的最后变更时间(`2024-01-20 15:30`),用于判断信息的时效性。
# 第一部分数据管理
本页面是进入具体项目后的核心工作台之一,将来自行内流水、征信数据、人工上传不同来源和格式的数据,在一个界面内完成统一接入;并且自动化检查识别数据问题,保证后续风险识别的准确性。
# 1、数据导入
# 1拉取本行信息
功能:点击后需要输入证件号码或者导入文件(上传身份证号表格),自动拉取行内流水、资产等数据信息
# (2) 他行流水导入:
功能批量上传员工的他行银行、或者支付宝微信等交易流水文件。支持Excel、文本型PDF。系统自动解析文件内容提取交易金额、对手方、交易时间、余额、摘要等关键字段。
# (3征信信息导入
功能上传个人信用报告。支持HTML格式的网页文件。系统自动解析报告提取信贷账户、负债总额、担保信息、查询记录等核心数据。
# (4) 员工家庭关系导入:
功能:上传员工的家庭成员信息,用于构建关系人图谱和关联分析。
(5) 名单库选择: 从信息维护——中介库管理内的名单选择确认后的可疑名单
(6) 生成报告:生成初核结果,跳转结果页
# 2、数据质量检查
功能:在数据导入后,系统自动执行一套预定义的质量规则,对数据集进行检查。
检查结果详情:以列表形式直观展示发现的具体问题,例如:
发现23条数据格式不一致如日期格式不统一、金额单位混杂。
发现5条余額链条性异常指相邻交易记录间的余额计算逻辑断裂可能意味着数据缺失或被篡改。
发现12条缺失关键字段如交易记录缺少对手方账号或户名。
质量评分仪表盘:通过三个关键指标量化数据质量:
数据完整性 $(98.5\%)$ :衡量必填字段的填充率。
格式一致性 $(95.2\%)$ :衡量数据遵循预定格式规范的程度。
余额连续性 (92.8%): 衡量流水数据中余额连续、计算正确的程度。
# 第二部分 初核结果总览
本页面为创建的项目中上传的数据经过模型识别出的风险信息总览及明细。
# 1、风险总览
# (1) 风险全局仪表盘
功能:以数据卡片形式集中展示项目整体风险态势。
总人数项目覆盖的员工总数500人
无预警人数432人
低风险人数38人
中风险人数20人
高风险人数10人
# (2) 高风险/中风险人员名单
按风险评分降序排列以列表形式展示所有高风险人员清单以及中风险人员中评分最高的10名员工。信息包括姓名、身份证号、部门、风险评分、触发模型数、核心异常点系统自动提炼的最显著风险
# (3) 查看单个风险人员详情
点击每个风险人员的【查看详情】入口,可钻取至单个员工的全面风险报告。包括其所有异常行为列表、每个行为对应的模型判断依据(规则),以及资产分析、征信概览、关系人图谱等模块。并且针对可疑交易及可疑对象,可以手动添加至关注方。
风险总览:
批量生成报告
批量导出证据
批量添加到关注列表
单个人员详情中的异常明细页面:
添加到案例库
单个人员详情中的资产分析页面:
单个人员详情中的征信摘要页面:
3笔 $>50$ 万
# 2、风险模型
# (1) 模型触发情况总计
内容:以表格形式展示所有风险模型的整体触发情况。包括:模型名称、触发总人数、主要触发人员示例。
操作:点击任一模型的“查看详情”,可跳转至触发该模型的全体人员列表(即“单模型触发列表”)。
# (2各模型触发人员列表
内容通过下拉菜单选择触发某一特定风险模型如“大额交易”、或者同时触发多个如2个以上风险模型的高风险人员、或者通过“搜索人员姓名或工号...”进行精确查询,并支持将常用的筛选组合保存为固定策略,便于下次一键调用。
操作:通过查询可以得到该模型的所有触发人员,并且点击【查看详情】可查看该员工详细的风险情况。
模型触发情况、单模型/多模型筛选触发现图:
# 3、风险明细
# (1) 涉疑交易明细表
功能:展示涉及可疑交易的记录,支持按「全部可疑人员类型」「名单库命中」「模型规则命中」等维度筛选数据,且支持穿透式查看交易流水,用于定位异常资金往来。
内容:包括交易时间、可疑人员、关联人、关联员工(姓名+柜员号),关系(是否是员工本人、或者配偶等关系)、摘要、交易类型、交易金额等字段。
操作:点击「查看详情」,将跳转至可疑流水详情页,展示该条流水的交易对手、交易类型、交易时间等完整信息。
明细表列表内容:
涉疑交易明细表
全部可疑人员类型
↓导出
<table><tr><td>序号</td><td>交易时间</td><td>可疑人员</td><td>关联人</td><td>关联员工</td><td>关系</td><td>摘要/交易类型</td><td>交易金额</td><td>操作</td></tr><tr><td>1</td><td>2024-01-15</td><td>孙七</td><td>孙七</td><td>孙七(809901)</td><td>本人</td><td>/转账</td><td>+¥500,000</td><td>查看详情</td></tr><tr><td>2</td><td>2024-01-10</td><td>王五</td><td>孙七</td><td>孙七(809901)</td><td>配偶</td><td>零钱商户消费</td><td>-¥200,000</td><td>查看详情</td></tr></table>
可疑流水查看详情:
# (2涉及违法人员清单表
内容:展示经系统识别、在外部违法名单库中命中的人员信息,用于快速定位高风险人员,包括违法人员姓名、身份证号、是否为失信被执行人、是否有刑事判决记录、是否有行政处罚记录、是否涉及公安案件、是否被限制高消费、违法信息更新时间等字段。
操作:点击「查看详情」,将展示该人员的违法详情、更新日期等完整背景信息,辅助纪检核查。
涉及违法人员清单表
导出
<table><tr><td>序号</td><td>姓名</td><td>身份证号</td><td>失信被执行人</td><td>刑事判决</td><td>行政处罚</td><td>公安涉案记录</td><td>限制高消费</td><td>违法信息更新时间</td><td>操作</td></tr><tr><td>1</td><td>孙七</td><td>331081199405133029</td><td>是</td><td>否</td><td>是</td><td>是</td><td>是</td><td>2025-03-15</td><td>查看详情</td></tr><tr><td>2</td><td>王五</td><td>331081199405133020</td><td>否</td><td>否</td><td>否</td><td>否</td><td>否</td><td>2025-03-15</td><td>查看详情</td></tr></table>
# 基础信息
姓名:张三
身份证号330106199001011234
# 失信被执行人
状态:是
法院:杭州市中院
标的50万
时间2023-05-15
# 行政处罚
状态:是
类型:罚款
事由:违规经营
机关:杭州市场监管局
# 其他
限制高消费:是
刑事/公安涉案:无
更新时间2025-03-15
# (3) 异常账户清单表
内容:独立列出经模型识别出的所有异常账户,用于监控账户异动,防范资金风险。信息包括:账号、开户人、银行、异常类型(如“突然销户”、“异地启用”)、异常发生时间、状态(如「已销户」「正常」「冻结」)等字段。
操作:点击「查看详情」,可以查看该账号的所有异常交易明细。
# 4、批量导出数据及报告
支持将上述所有列表人员列表、异常清单等导出为Excel。并可一键生成项目多维统计报告PDF/Word从模型触发排行、部门风险分布、风险评分区间等多维度进行分析总结。
# 第三部分 专项排查
本页面为针对单人用户的的深度调查:
# 1、员工详查分析
功能:输入目标员工的身份证号,可选择自定义时间范围,即可根据检查对像及其主要家庭成员(配偶等),根据收入、资产、负债三者的关系进行初核判断,形成正常、收入+负债远低于资产、收入+负债远高于资产等结果风险提示。
# 2、图谱分析
功能:通过图形化方式,揭示隐藏的人员与资金关系网络。输入身份证号,点击“生成图谱”,结果在右侧可视化区域动态呈现。
# (1) 关系人图谱
通过身份证号等信息,可筛选展示以该员工为中心的社会关系网络(如家庭成员、密切关联人),点击节点可查看详情。再点击关联企业可以穿透查询企业下的法人、股东等信息。
# (2资金流图谱
针对个人的资金流向图谱中的可疑资金,向前追溯多层交易对手。且资金流向分析中支持:手工加入资金流向节点,或备注资金流向。
# (3) 实控账户图谱
输入身份证号,生成该员工实际控制(可能非本人名下)的账户网络图。排查逻辑主要基于手机登录丰收互联次数、线下多次代理存取等进行判断。
# 3、拓展查询
# (1) 采购查询
功能:用于纪检/内审人员查询特定采购事项的核心信息,聚焦“采购集中度、金额异常”等风险排查。可筛选查询的采购时段,以及关联员工,查询其参与的所有采购。
内容:清单包含采购事项名称、交易日期、采购金额、供应商名称、关联员工等核心字段。
也可穿透展示采购全量信息(采购方式、入围/中标公司、经办人、对方账号等)。
![](images/04c20c26a4d53a04600eab386fa232df7f0982b942e6bdc1969eda2bfca3405c.jpg)
# (2人员调动查询
功能:查询员工的岗位/机构调动记录,辅助排查“异常调动、岗位晋升合规性”。可选择查询时间和员工姓名,查询其所有调动记录。
内容:包含姓名、工号、调动时间、原/现岗位、原/现机构、调动原因等核心字段。
![](images/b1ef684713661c869e161574fc6b0666b305c781ff568846a97ea7da985fef12.jpg)
# (3招聘查询
功能:查询招聘事项信息,辅助排查“招聘流程合规性、面试官关联风险”。可筛选查询时间段和员工姓名,查询其招聘详情。
内容:包含招聘人员、岗位、招聘时间、关联面试官、面试结果等核心字段。
# 第四部分 流水明细查询
本页面为流水明细查询,对拉取的本行流水以及上传的他行流水进行批量分析。实现功能如下:
# 1、多帐户流水明细合并
可以将多个银行的流水合并成一个流水文件,左侧为筛选内容,可以筛选账号和银行进行查询;主页面可以选择按交易金额、交易时间等自主排序。且可切换对手方分析
# 2、全量流水二次分析
对全量流水表中的关键流水,可以进行手工提交“加入分析”,实现将关键流水重新放置在一个新的交易表中进行分析。
![](images/6ad8e568de5ec7d3febb3b8799c0bffc562530798037457b2a259c7ed7077146.jpg)
# 三. 信息维护
# 1、中介库管理
功能:建立并维护外部中介人员/机构黑名单库。支持Excel导入更新。当员工交易对手命中该库时系统将自动产生高风险预警。
# 2、员工信息管理
功能:对员工实控账户、实控手机号、关系人信息等进行批量维护
说明:对于系统无法自动获取或关联的员工附属信息(如经查实的实际控制账户、未在户口本上的特定关系人等),提供手工录入与维护功能。
# 3、信贷客户家庭关系维护
可以上传并且维护信贷家庭关系表格信息。
# 四. 参数配置
功能:模型参数管理
说明:提供风险模型核心参数的维护界面、细化阈值规则。筛选模型名称:筛选(可选大额交易模型、可疑兼职模型、可疑外汇交易模型三种),得到阈值参数配置内容如下:
# 1、大额交易模型
识别大额/高频资金交易,检测调整单笔交易额、频繁转账次数等阈值
# 2、可疑兼职模型
识别异常额外收入,监测调整月度固定收入、固定对手转入等阈值
# 3、可疑外汇交易模型
识别异常外汇收支,监测调整单笔购汇金额、频繁外汇交易次数等阈值
# 模型参数管理
<table><tr><td>模型名称</td><td>可疑外汇交易模型</td><td>✓</td><td>查询</td></tr></table>
阈值参数配置
<table><tr><td>监测项</td><td>描述</td><td>阈值设置</td><td>单位</td></tr><tr><td>单笔购汇金额</td><td>单笔购汇超过该金额</td><td>50000</td><td>美元/笔</td></tr><tr><td>单笔结汇金额</td><td>单笔结汇超过该金额</td><td>50000</td><td>美元/笔</td></tr><tr><td>跨境汇款金额</td><td>单笔跨境汇款超过该金额</td><td>100000</td><td>美元/笔</td></tr><tr><td>月度购汇总额</td><td>月度累计购汇超过</td><td>200000</td><td>美元/月</td></tr><tr><td>月度结汇总额</td><td>月度累计结汇超过</td><td>200000</td><td>美元/月</td></tr><tr><td>频繁外汇交易</td><td>单日外汇交易次数超过</td><td>5</td><td>次/日</td></tr><tr><td>保存配置</td><td>恢复默认</td><td></td><td></td></tr></table>
# 五.系统管理
# 1、用户权限
系统管理员可对访问系统的用户账号进行增、删、改、禁用等操作。
# 2、项目统计
根据年度、组长、对像、成果等进行项目统计
# 3、操作日志管理
记录用户的关键操作(登录、数据导入、模型运行、报告生成等),支持按时间、用户、操作类型进行查询。
![](images/68bd718419ce288731c5e52789ac1e5dc02ac931ac319760ccd2bcca0d3ddb08.jpg)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,667 @@
# 纪检初核系统功能模块划分方案
## 需求分析概述
基于《纪检初核系统功能说明书-V1.0》的分析,该系统是一个用于银行纪检部门进行员工行为初核的综合性管理平台。
---
# 模块详细设计
## 模块一:项目管理域 (dpc-project)
### 职责
项目全生命周期管理,包括项目创建、配置、执行、归档等全过程管理。
### 页面清单
| 页面名称 | 路由 | 说明 |
|---------|------|------|
| 项目列表页 | /project/list | 展示所有项目的主页面 |
| 新建项目弹窗 | /project/add | 新建项目表单弹窗 |
| 导入历史项目弹窗 | /project/import | 复制历史项目配置 |
| 项目详情页 | /project/detail/:id | 查看项目详细信息 |
| 项目归档确认弹窗 | /project/archive | 归档项目确认 |
### 功能权限
| 权限标识 | 权限名称 | 说明 |
|---------|---------|------|
| `project:list` | 查看项目列表 | 查看项目列表页 |
| `project:create` | 创建项目 | 新建项目 |
| `project:edit` | 编辑项目 | 修改项目信息 |
| `project:delete` | 删除项目 | 删除项目 |
| `project:archive` | 归档项目 | 归档已完成项目 |
| `project:import` | 导入历史项目 | 复制历史项目配置 |
| `project:result:view` | 查看结果 | 查看已完成项目结果 |
| `project:reanalyze` | 重新分析 | 重新运行风险模型 |
| `project:enter` | 进入项目 | 进入项目工作台 |
| `project:quarter:create` | 创建季度初核 | 快捷创建季度初核项目 |
| `project:newemployee:create` | 创建新员工排查 | 快捷创建新员工排查 |
| `project:export` | 导出项目 | 导出项目数据 |
### 数据表设计
#### pj_project (项目信息表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| project_id | BIGINT | 项目ID主键 | 是 |
| project_name | VARCHAR(100) | 项目名称 | 是 |
| project_desc | VARCHAR(500) | 项目描述 | 否 |
| start_time | DATETIME | 开始时间 | 是 |
| end_time | DATETIME | 结束时间 | 是 |
| status | CHAR(1) | 状态0进行中 1已完成 2已归档 | 是 |
| target_count | INT | 目标人数 | 是 |
| warning_count | INT | 预警人数 | 是 |
| create_by | VARCHAR(64) | 创建人 | 是 |
| create_time | DATETIME | 创建时间 | 是 |
| update_by | VARCHAR(64) | 更新人 | 否 |
| update_time | DATETIME | 更新时间 | 否 |
| remark | VARCHAR(500) | 备注 | 否 |
#### pj_project_member (项目成员表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| member_id | BIGINT | 成员ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| user_id | BIGINT | 用户ID | 是 |
| member_role | CHAR(1) | 角色1组长 2成员 | 是 |
| join_time | DATETIME | 参与时间 | 是 |
#### pj_project_config (项目配置表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| config_id | BIGINT | 配置ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| data_source_config | TEXT | 数据源配置JSON | 否 |
| time_range_config | TEXT | 时间范围配置JSON | 否 |
| risk_model_config | TEXT | 风险模型配置JSON | 否 |
| other_config | TEXT | 其他配置JSON | 否 |
---
## 模块二:数据接入域 (dpc-data)
### 职责
多源数据采集与标准化处理,支持本行数据、他行流水、征信报告、家庭关系等多种数据源接入。
### 页面清单
| 页面名称 | 路由 | 说明 |
|---------|------|------|
| 数据管理页 | /workspace/:projectId/data | 项目工作台-数据管理 |
| 本行信息拉取弹窗 | /data/internal/pull | 输入证件号拉取本行数据 |
| 他行流水上传弹窗 | /data/external/upload | 上传他行流水文件 |
| 征信信息上传弹窗 | /data/credit/upload | 上传征信报告文件 |
| 家庭关系上传弹窗 | /data/family/upload | 上传家庭关系信息 |
| 名单库选择弹窗 | /data/watchlist/select | 选择可疑名单 |
### 功能权限
| 权限标识 | 权限名称 | 说明 |
|---------|---------|------|
| `data:internal:import` | 本行信息导入 | 拉取本行流水、资产数据 |
| `data:external:import` | 他行流水导入 | 上传他行流水文件 |
| `data:credit:import` | 征信信息导入 | 上传征信报告文件 |
| `data:family:import` | 家庭关系导入 | 上传家庭关系信息 |
| `data:watchlist:select` | 名单库选择 | 选择可疑名单 |
| `data:report:generate` | 生成报告 | 生成初核结果报告 |
### 数据表设计
#### di_import_record (导入记录表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| import_id | BIGINT | 导入ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| data_type | CHAR(1) | 数据类型1本行 2他行 3征信 4家庭关系 5名单库 | 是 |
| file_name | VARCHAR(200) | 文件名 | 否 |
| file_path | VARCHAR(500) | 文件路径 | 否 |
| import_status | CHAR(1) | 导入状态0待处理 1处理中 2成功 3失败 | 是 |
| record_count | INT | 记录数 | 否 |
| error_message | TEXT | 错误信息 | 否 |
| import_by | VARCHAR(64) | 导入人 | 是 |
| import_time | DATETIME | 导入时间 | 是 |
#### di_transaction (交易流水表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| trans_id | BIGINT | 交易ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| employee_id | BIGINT | 员工ID | 是 |
| account_no | VARCHAR(50) | 账号 | 是 |
| bank_name | VARCHAR(50) | 银行名称 | 是 |
| trans_time | DATETIME | 交易时间 | 是 |
| trans_amount | DECIMAL(18,2) | 交易金额 | 是 |
| balance | DECIMAL(18,2) | 余额 | 否 |
| counter_party | VARCHAR(200) | 交易对手 | 否 |
| summary | VARCHAR(200) | 摘要 | 否 |
| trans_type | VARCHAR(50) | 交易类型 | 否 |
#### di_credit_report (征信报告表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| credit_id | BIGINT | 征信ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| employee_id | BIGINT | 员工ID | 是 |
| credit_accounts | TEXT | 信贷账户JSON | 否 |
| total_debt | DECIMAL(18,2) | 负债总额 | 否 |
| guarantee_info | TEXT | 担保信息JSON | 否 |
| query_records | TEXT | 查询记录JSON | 否 |
#### di_family_relation (家庭关系表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| relation_id | BIGINT | 关系ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| employee_id | BIGINT | 员工ID | 是 |
| relation_name | VARCHAR(50) | 关系人姓名 | 是 |
| relation_type | VARCHAR(20) | 关系类型(配偶、父母、子女等) | 是 |
| id_card | VARCHAR(18) | 身份证号 | 是 |
| phone | VARCHAR(20) | 联系电话 | 否 |
---
## 模块三:数据质量域 (dpc-quality)
### 职责
数据质量检查与清洗,通过预定义规则自动检测数据格式、连续性、完整性等问题。
### 页面清单
| 页面名称 | 路由 | 说明 |
|---------|------|------|
| 数据质量页 | /workspace/:projectId/quality | 数据质量检查结果 |
| 质量评分仪表盘 | /quality/dashboard/:projectId | 质量评分可视化 |
| 质量问题详情 | /quality/issues/:projectId | 质量问题列表 |
### 功能权限
| 权限标识 | 权限名称 | 说明 |
|---------|---------|------|
| `quality:check:run` | 运行质量检查 | 执行数据质量检查 |
| `quality:check:view` | 查看检查结果 | 查看质量检查结果 |
| `quality:score:view` | 查看质量评分 | 查看质量评分仪表盘 |
| `quality:issue:view` | 查看质量问题 | 查看质量问题详情 |
### 数据表设计
#### dq_quality_rule (质量规则表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| rule_id | BIGINT | 规则ID主键 | 是 |
| rule_name | VARCHAR(100) | 规则名称 | 是 |
| rule_type | CHAR(1) | 规则类型1格式 2连续性 3完整性 | 是 |
| rule_expression | TEXT | 规则表达式 | 是 |
| error_level | CHAR(1) | 错误级别1低 2中 3高 | 是 |
| status | CHAR(1) | 状态0停用 1启用 | 是 |
#### dq_check_result (检查结果表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| result_id | BIGINT | 结果ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| rule_id | BIGINT | 规则ID | 是 |
| error_count | INT | 错误数量 | 是 |
| check_time | DATETIME | 检查时间 | 是 |
#### dq_quality_score (质量评分表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| score_id | BIGINT | 评分ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| completeness_score | DECIMAL(5,2) | 数据完整性评分 | 是 |
| consistency_score | DECIMAL(5,2) | 格式一致性评分 | 是 |
| continuity_score | DECIMAL(5,2) | 余额连续性评分 | 是 |
| total_score | DECIMAL(5,2) | 总评分 | 是 |
| check_time | DATETIME | 检查时间 | 是 |
---
## 模块四:风险分析域 (dpc-risk)
### 职责
风险模型引擎与风险评估,通过配置的风险模型进行自动风险识别和评分。
### 页面清单
| 页面名称 | 路由 | 说明 |
|---------|------|------|
| 风险总览页 | /workspace/:projectId/risk/overview | 风险仪表盘 |
| 风险人员列表 | /risk/persons/:projectId | 风险人员列表 |
| 风险人员详情 | /risk/person/:id | 单个人员详情 |
| 风险模型页 | /workspace/:projectId/risk/models | 模型触发情况 |
| 涉疑交易明细 | /risk/transaction/:projectId | 涉疑交易列表 |
| 违法人员清单 | /risk/illegal/:projectId | 违法人员列表 |
| 异常账户清单 | /risk/account/:projectId | 异常账户列表 |
| 风险模型配置 | /risk/model/config | 风险模型参数配置 |
### 功能权限
| 权限标识 | 权限名称 | 说明 |
|---------|---------|------|
| `risk:overview:view` | 查看风险总览 | 查看风险仪表盘 |
| `risk:person:view` | 查看风险人员 | 查看风险人员列表 |
| `risk:person:detail` | 查看人员详情 | 查看人员详情 |
| `risk:person:export` | 导出风险人员 | 导出风险人员列表 |
| `risk:model:view` | 查看风险模型 | 查看模型触发情况 |
| `risk:model:detail` | 查看模型详情 | 查看模型触发详情 |
| `risk:transaction:view` | 查看交易明细 | 查看涉疑交易明细 |
| `risk:transaction:export` | 导出交易明细 | 导出交易明细 |
| `risk:illegal:view` | 查看违法人员 | 查看违法人员清单 |
| `risk:illegal:export` | 导出违法人员 | 导出违法人员清单 |
| `risk:account:view` | 查看异常账户 | 查看异常账户清单 |
| `risk:account:export` | 导出异常账户 | 导出异常账户清单 |
| `risk:model:config` | 配置风险模型 | 配置风险模型参数 |
| `risk:watchlist:add` | 添加关注 | 添加关注对象 |
### 数据表设计
#### ra_risk_model (风险模型表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| model_id | BIGINT | 模型ID主键 | 是 |
| model_name | VARCHAR(100) | 模型名称 | 是 |
| model_type | VARCHAR(50) | 模型类型 | 是 |
| model_config | TEXT | 模型配置JSON | 是 |
| status | CHAR(1) | 状态0停用 1启用 | 是 |
#### ra_risk_person (风险人员表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| person_id | BIGINT | 人员ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| employee_id | BIGINT | 员工ID | 是 |
| risk_score | INT | 风险评分 | 是 |
| risk_level | CHAR(1) | 风险等级0无 1低 2中 3高 | 是 |
| trigger_models | TEXT | 触发模型JSON | 否 |
| core_risks | TEXT | 核心异常点JSON | 否 |
#### ra_suspicious_transaction (涉疑交易表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| trans_id | BIGINT | 交易ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| employee_id | BIGINT | 关联员工ID | 否 |
| relation_person | VARCHAR(50) | 关联人姓名 | 否 |
| relation_type | VARCHAR(20) | 关系类型 | 否 |
| trans_time | DATETIME | 交易时间 | 是 |
| trans_amount | DECIMAL(18,2) | 交易金额 | 是 |
| trans_type | VARCHAR(50) | 交易类型 | 否 |
| counter_party | VARCHAR(200) | 交易对手 | 否 |
| hit_watchlist | CHAR(1) | 是否命中名单库 | 否 |
| hit_model | CHAR(1) | 是否命中模型规则 | 否 |
#### ra_illegal_person (违法人员表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| illegal_id | BIGINT | 违法人员ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| person_name | VARCHAR(50) | 姓名 | 是 |
| id_card | VARCHAR(18) | 身份证号 | 是 |
| is_dishonesty | CHAR(1) | 是否失信被执行人 | 否 |
| is_criminal | CHAR(1) | 是否刑事判决 | 否 |
| is_administrative | CHAR(1) | 是否行政处罚 | 否 |
| is_police_case | CHAR(1) | 是否公安案件 | 否 |
| is_limit_consumption | CHAR(1) | 是否限制高消费 | 否 |
| update_time | DATETIME | 更新时间 | 否 |
| illegal_detail | TEXT | 违法详情 | 否 |
#### ra_abnormal_account (异常账户表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| account_id | BIGINT | 账户ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| account_no | VARCHAR(50) | 账号 | 是 |
| account_holder | VARCHAR(50) | 开户人 | 是 |
| bank_name | VARCHAR(50) | 银行名称 | 是 |
| abnormal_type | VARCHAR(50) | 异常类型 | 是 |
| abnormal_time | DATETIME | 异常发生时间 | 是 |
| account_status | VARCHAR(20) | 账户状态 | 是 |
---
## 模块五:专项调查域 (dpc-investigation)
### 职责
深度分析与可视化,包括员工详查、图谱分析、拓展查询、流水明细查询等功能。
### 页面清单
| 页面名称 | 路由 | 说明 |
|---------|------|------|
| 员工详查分析 | /workspace/:projectId/investigation/employee | 员工收支资产分析 |
| 关系人图谱 | /investigation/graph/relation/:id | 社会关系网络图 |
| 资金流图谱 | /investigation/graph/fund/:id | 资金流向追踪图 |
| 实控账户图谱 | /investigation/graph/account/:id | 实控账户网络图 |
| 采购查询 | /investigation/purchase | 采购事项查询 |
| 人员调动查询 | /investigation/transfer | 人员调动记录查询 |
| 招聘查询 | /investigation/recruit | 招聘事项查询 |
| 流水明细合并 | /investigation/flow/merge | 多账户流水合并 |
| 流水二次分析 | /investigation/flow/reanalyze | 全量流水二次分析 |
### 功能权限
| 权限标识 | 权限名称 | 说明 |
|---------|---------|------|
| `investigation:employee:analyze` | 员工详查分析 | 分析员工收支资产关系 |
| `investigation:graph:relation` | 关系人图谱 | 查看社会关系网络 |
| `investigation:graph:fund` | 资金流图谱 | 查看资金流向 |
| `investigation:graph:account` | 实控账户图谱 | 查看实控账户网络 |
| `investigation:purchase:view` | 采购查询 | 查询采购事项 |
| `investigation:transfer:view` | 人员调动查询 | 查询人员调动记录 |
| `investigation:recruit:view` | 招聘查询 | 查询招聘事项 |
| `investigation:flow:merge` | 流水合并 | 合并多账户流水 |
| `investigation:flow:reanalyze` | 流水二次分析 | 全量流水二次分析 |
| `investigation:flow:export` | 流水导出 | 导出流水数据 |
### 数据表设计
#### si_investigation_record (调查记录表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| record_id | BIGINT | 记录ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| employee_id | BIGINT | 员工ID | 是 |
| investigation_type | VARCHAR(50) | 调查类型 | 是 |
| investigation_result | TEXT | 调查结果JSON | 否 |
| create_by | VARCHAR(64) | 创建人 | 是 |
| create_time | DATETIME | 创建时间 | 是 |
#### si_graph_node (图谱节点表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| node_id | BIGINT | 节点ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| node_type | VARCHAR(20) | 节点类型1人员 2企业 3账户 | 是 |
| node_name | VARCHAR(100) | 节点名称 | 是 |
| node_data | TEXT | 节点数据JSON | 否 |
#### si_graph_edge (图谱关系边表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| edge_id | BIGINT | 边ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| source_node_id | BIGINT | 源节点ID | 是 |
| target_node_id | BIGINT | 目标节点ID | 是 |
| edge_type | VARCHAR(50) | 关系类型 | 是 |
| edge_data | TEXT | 关系数据JSON | 否 |
---
## 模块六:基础数据域 (dpc-masterdata)
### 职责
基础信息维护,包括中介库管理、员工信息管理、信贷客户家庭关系维护等。
### 页面清单
| 页面名称 | 路由 | 说明 |
|---------|------|------|
| 中介库管理 | /masterdata/meddle | 中介机构黑名单管理 |
| 员工信息管理 | /masterdata/employee | 员工实控信息管理 |
| 实控账户管理 | /masterdata/employee/account | 员工实控账户维护 |
| 实控手机号管理 | /masterdata/employee/phone | 员工实控手机号维护 |
| 信贷客户家庭关系 | /masterdata/family | 信贷客户家庭关系维护 |
### 功能权限
| 权限标识 | 权限名称 | 说明 |
|---------|---------|------|
| `masterdata:meddle:list` | 查看中介库列表 | 查看中介列表 |
| `masterdata:meddle:add` | 新增中介 | 添加中介条目 |
| `masterdata:meddle:edit` | 修改中介 | 修改中介信息 |
| `masterdata:meddle:remove` | 删除中介 | 删除中介条目 |
| `masterdata:meddle:export` | 导出中介库 | 导出中介数据 |
| `masterdata:meddle:import` | 导入中介库 | 导入中介数据 |
| `masterdata:employee:list` | 查看员工列表 | 查看员工列表 |
| `masterdata:employee:edit` | 修改员工信息 | 修改员工信息 |
| `masterdata:employee:account` | 实控账户管理 | 管理实控账户 |
| `masterdata:employee:phone` | 实控手机号管理 | 管理实控手机号 |
| `masterdata:family:list` | 查看家庭关系 | 查看家庭关系列表 |
| `masterdata:family:add` | 新增家庭关系 | 添加家庭关系 |
| `masterdata:family:edit` | 修改家庭关系 | 修改家庭关系 |
| `masterdata:family:remove` | 删除家庭关系 | 删除家庭关系 |
### 数据表设计
#### md_meddle (中介库表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| meddle_id | BIGINT | 中介ID主键 | 是 |
| meddle_name | VARCHAR(100) | 中介名称 | 是 |
| meddle_type | VARCHAR(20) | 中介类型1人员 2机构 | 是 |
| id_card | VARCHAR(18) | 身份证号 | 否 |
| credit_code | VARCHAR(50) | 统一信用代码 | 否 |
| contact | VARCHAR(50) | 联系人 | 否 |
| phone | VARCHAR(20) | 联系电话 | 否 |
| address | VARCHAR(200) | 地址 | 否 |
| risk_reason | VARCHAR(500) | 风险原因 | 否 |
| risk_level | CHAR(1) | 风险等级1低 2中 3高 | 是 |
| status | CHAR(1) | 状态0停用 1启用 | 是 |
| create_by | VARCHAR(64) | 创建人 | 是 |
| create_time | DATETIME | 创建时间 | 是 |
| update_by | VARCHAR(64) | 更新人 | 否 |
| update_time | DATETIME | 更新时间 | 否 |
| remark | VARCHAR(500) | 备注 | 否 |
#### md_employee_ext (员工扩展信息表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| ext_id | BIGINT | 扩展ID主键 | 是 |
| user_id | BIGINT | 用户ID | 是 |
| employee_no | VARCHAR(20) | 员工工号 | 是 |
| department | VARCHAR(100) | 所属部门 | 是 |
| position | VARCHAR(50) | 职位 | 是 |
| level | VARCHAR(20) | 职级 | 否 |
| hire_date | DATE | 入职日期 | 否 |
#### md_control_account (实控账户表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| account_id | BIGINT | 账户ID主键 | 是 |
| user_id | BIGINT | 用户ID | 是 |
| account_no | VARCHAR(50) | 账号 | 是 |
| account_bank | VARCHAR(50) | 开户银行 | 是 |
| account_type | VARCHAR(20) | 账户类型 | 是 |
| relation_type | VARCHAR(50) | 关系类型 | 否 |
| status | CHAR(1) | 状态0停用 1启用 | 是 |
#### md_control_phone (实控手机号表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| phone_id | BIGINT | 手机号ID主键 | 是 |
| user_id | BIGINT | 用户ID | 是 |
| phone_number | VARCHAR(20) | 手机号 | 是 |
| relation_type | VARCHAR(50) | 关系类型 | 否 |
| status | CHAR(1) | 状态0停用 1启用 | 是 |
---
## 模块七:报告与统计域 (dpc-report)
### 职责
报告生成与数据分析,包括初核结果报告生成、多维统计分析、数据导出等功能。
### 页面清单
| 页面名称 | 路由 | 说明 |
|---------|------|------|
| 报告生成 | /workspace/:projectId/report/generate | 生成初核结果报告 |
| 报告模板管理 | /report/template | 管理报告模板 |
| 报告历史 | /report/history | 报告生成历史 |
| 项目统计 | /report/project/statistics | 项目统计分析 |
| 年度统计 | /report/statistics/year | 按年度统计 |
| 组长统计 | /report/statistics/leader | 按组长统计 |
| 对象统计 | /report/statistics/target | 按对象统计 |
| 成果统计 | /report/statistics/result | 按成果统计 |
### 功能权限
| 权限标识 | 权限名称 | 说明 |
|---------|---------|------|
| `report:generate` | 生成报告 | 生成初核结果报告 |
| `report:template:view` | 查看报告模板 | 查看报告模板 |
| `report:template:edit` | 编辑报告模板 | 编辑报告模板 |
| `report:history:view` | 查看报告历史 | 查看报告生成历史 |
| `report:stat:view` | 查看项目统计 | 查看项目统计 |
| `report:stat:export` | 导出统计数据 | 导出统计数据 |
### 数据表设计
#### rp_report_template (报告模板表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| template_id | BIGINT | 模板ID主键 | 是 |
| template_name | VARCHAR(100) | 模板名称 | 是 |
| template_type | VARCHAR(20) | 模板类型 | 是 |
| template_content | TEXT | 模板内容 | 是 |
| status | CHAR(1) | 状态0停用 1启用 | 是 |
#### rp_report_history (报告生成历史表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| history_id | BIGINT | 历史ID主键 | 是 |
| project_id | BIGINT | 项目ID | 是 |
| report_name | VARCHAR(200) | 报告名称 | 是 |
| report_type | VARCHAR(20) | 报告类型 | 是 |
| report_path | VARCHAR(500) | 报告路径 | 是 |
| generate_by | VARCHAR(64) | 生成人 | 是 |
| generate_time | DATETIME | 生成时间 | 是 |
#### rp_project_statistics (项目统计表)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| stat_id | BIGINT | 统计ID主键 | 是 |
| stat_year | INT | 统计年度 | 是 |
| total_projects | INT | 总项目数 | 是 |
| completed_projects | INT | 已完成项目数 | 是 |
| ongoing_projects | INT | 进行中项目数 | 是 |
| total_people | INT | 总核查人数 | 是 |
| risk_people | INT | 风险人数 | 是 |
| report_count | INT | 报告生成数 | 是 |
---
## 模块八:系统管理域 (扩展 ruoyi-system)
### 职责
系统配置与权限管理,扩展若依原有的系统管理功能,增加项目统计、操作日志等。
### 页面清单
| 页面名称 | 路由 | 说明 |
|---------|------|------|
| 用户管理 | /system/user | 若依现有功能 |
| 角色管理 | /system/role | 若依现有功能 |
| 菜单管理 | /system/menu | 若依现有功能 |
| 部门管理 | /system/dept | 若依现有功能 |
| 参数配置 | /system/config | 风险模型参数配置 |
| 操作日志 | /monitor/operlog | 若依现有功能 |
| 登录日志 | /monitor/logininfor | 若依现有功能 |
### 功能权限
若依原有权限体系,按需扩展纪检初核相关权限。
---
# 若依模块结构
```
discipline-prelim-check/
├── ruoyi-admin/ # 启动模块
├── ruoyi-framework/ # 框架核心
├── ruoyi-system/ # 系统管理(扩展)
├── ruoyi-common/ # 公共组件
├── dpc-project/ # 模块一:项目管理域 (新增)
├── dpc-data/ # 模块二:数据接入域 (新增)
├── dpc-quality/ # 模块三:数据质量域 (新增)
├── dpc-risk/ # 模块四:风险分析域 (新增)
├── dpc-investigation/ # 模块五:专项调查域 (新增)
├── dpc-masterdata/ # 模块六:基础数据域 (新增)
├── dpc-report/ # 模块七:报告与统计域 (新增)
└── ruoyi-ui/
└── src/
├── views/
│ ├── project/ # 项目管理
│ │ ├── index.vue # 项目列表
│ │ ├── addDialog.vue # 新建项目弹窗
│ │ └── importDialog.vue # 导入历史项目
│ ├── workspace/ # 项目工作台(容器)
│ │ ├── data.vue # 数据管理
│ │ ├── quality.vue # 数据质量
│ │ ├── risk/ # 风险分析
│ │ │ ├── overview.vue # 风险总览
│ │ │ ├── models.vue # 风险模型
│ │ │ └── detail/ # 风险明细
│ │ └── investigation/ # 专项调查
│ ├── masterdata/ # 基础数据
│ │ ├── meddle.vue # 中介库管理
│ │ ├── employee.vue # 员工信息管理
│ │ └── family.vue # 家庭关系维护
│ └── report/ # 报告统计
│ ├── generate.vue # 报告生成
│ └── statistics/ # 统计分析
└── api/
├── project.js
├── data.js
├── quality.js
├── risk.js
├── investigation.js
├── masterdata.js
└── report.js
```
---
# 实施优先级
## 第一阶段:基础框架
1. **项目管理域** - 建立项目概念实现项目CRUD
2. **数据接入域** - 实现基础数据导入功能
3. **系统管理域** - 扩展权限和配置
## 第二阶段:核心分析
4. **数据质量域** - 实现数据质量检查
5. **风险分析域** - 实现核心风险模型
6. **基础数据域** - 建立基础数据支撑
## 第三阶段:高级功能
7. **专项调查域** - 实现图谱分析和深度调查
8. **报告与统计域** - 实现报告生成和统计
---
# 验证方式
1. 各模块可独立开发、测试、部署
2. 模块间通过定义良好的接口交互
3. 使用若依代码生成器快速生成CRUD框架
4. 每个模块有独立的菜单权限配置
5. 数据库表按模块前缀命名,便于管理

Binary file not shown.

View File

@@ -0,0 +1,919 @@
# 上传数据页面 UI 设计文档
## 1. 页面概述
### 1.1 功能描述
上传数据页面是纪检初核系统中项目管理模块的核心页面,支持在一个项目中上传多个主体/账户数据进行汇总/独立分析。提供流水导入、征信导入、员工家庭关系导入、名单库选择等功能。
### 1.2 页面路径
- 菜单位置:项目管理 > 项目详情 > 上传数据
- 路由路径:`/project/:id/upload-data`
### 1.3 页面状态
- 项目状态:已完成
- 最后更新时间2024-01-20 15:30
---
## 2. 页面布局
### 2.1 整体结构
```
┌─────────────────────────────────────────────────────────────┐
│ 面包屑导航:项目管理 / 项目详情 / 上传数据 │
├─────────────────────────────────────────────────────────────┤
│ 页面标题区 │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ 上传数据 │ │
│ │ 项目状态:已完成 最后更新2024-01-20 15:30 │ │
│ │ 支持在一个项目中上传多个主体/账户数据,进行汇总/独立分析 │ │
│ └───────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 主要内容区(网格布局) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 流水导入 │ │ 已上传流水查询 │ │
│ │ [上传组件] │ │ [上传组件] │ │
│ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 征信导入 │ │ 员工家庭关系导入 │ │
│ │ [上传组件] │ │ [上传组件] │ │
│ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ 名单库选择 │ │
│ │ ☑ 高风险人员名单(68人) ☑ 历史可疑人员名单 │ │
│ │ ☑ 监管关注名单(32人) │ │
│ └─────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 数据质量检查区 │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 数据完整性 格式一致性 余额连续性 │ │
│ │ 98.5% 95.2% 92.8% │ │
│ │ 检查结果: [查看详情] │ │
│ │ • 发现 23 条数据格式不一致 │ │
│ │ • 发现 5 条余额连续性异常 │ │
│ │ • 发现 12 条缺失关键字段 │ │
│ └────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ 操作按钮区 │
│ [拉取本行信息] [生成报告] │
└─────────────────────────────────────────────────────────────┘
```
### 2.2 响应式布局
- 桌面端≥1200px4列网格布局
- 平板端768px-1199px2列网格布局
- 移动端(<768px单列布局
---
## 3. 组件设计
### 3.1 FileUploadCard 上传卡片组件
**Props:**
```typescript
interface FileUploadCardProps {
title: string; // 卡片标题
description: string; // 描述文字
acceptTypes: string[]; // 接受的文件类型,如 ['xlsx', 'xls', 'pdf']
maxSize?: number; // 最大文件大小MB默认 10
multiple?: boolean; // 是否支持多文件上传
uploadUrl: string; // 上传接口地址
onUploadSuccess?: (files: UploadedFile[]) => void;
onUploadError?: (error: Error) => void;
showFileList?: boolean; // 是否显示已上传文件列表
}
```
**UI 结构:**
```vue
<template>
<el-card class="upload-card">
<template #header>
<div class="card-header">
<h3>{{ title }}</h3>
<el-tooltip :content="description" placement="top">
<i class="el-icon-info"></i>
</el-tooltip>
</div>
</template>
<el-upload
class="upload-area"
:action="uploadUrl"
:accept="acceptTypes.join(',')"
:multiple="multiple"
:limit="10"
:file-list="fileList"
:on-success="handleSuccess"
:on-error="handleError"
:before-upload="beforeUpload"
drag
>
<div class="upload-content">
<i class="el-icon-upload"></i>
<p>拖拽文件到此处或点击上传</p>
<p class="upload-tip">支持格式: {{ acceptTypes.join(', ') }}</p>
</div>
</el-upload>
<div v-if="showFileList && uploadedFiles.length" class="file-list">
<h4>已上传文件</h4>
<el-table :data="uploadedFiles" size="small">
<el-table-column prop="fileName" label="文件名" />
<el-table-column prop="fileSize" label="大小" width="100" />
<el-table-column prop="uploadTime" label="上传时间" width="160" />
<el-table-column prop="status" label="状态" width="80">
<template #default="{ row }">
<el-tag :type="row.status === 'success' ? 'success' : 'danger'">
{{ row.status === 'success' ? '成功' : '失败' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" width="100" fixed="right">
<template #default="{ row }">
<el-button type="text" size="small" @click="handleDelete(row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
</template>
```
### 3.2 CheckboxGroupSelector 名单库选择组件
**Props:**
```typescript
interface CheckboxGroupSelectorProps {
options: NameListOption[];
modelValue: string[];
onChange: (value: string[]) => void;
}
interface NameListOption {
label: string; // 显示文本
value: string; // 选中值
count: number; // 人数统计
disabled?: boolean;
}
```
**UI 结构:**
```vue
<template>
<el-card class="name-list-selector">
<template #header>
<h3>名单库选择</h3>
</template>
<p class="selector-description">选择中介库管理内的名单</p>
<el-checkbox-group v-model="selectedLists" @change="handleChange">
<el-checkbox
v-for="option in options"
:key="option.value"
:label="option.value"
:disabled="option.disabled"
>
{{ option.label }}({{ option.count }})
</el-checkbox>
</el-checkbox-group>
</el-card>
</template>
```
### 3.3 DataQualityPanel 数据质量检查面板
**Props:**
```typescript
interface DataQualityPanelProps {
metrics: QualityMetric[];
issues: QualityIssue[];
onCheckQuality?: () => void;
onViewDetails?: (issue: QualityIssue) => void;
}
interface QualityMetric {
name: string; // 指标名称
value: number; // 百分比值
status: 'good' | 'warning' | 'error';
}
interface QualityIssue {
type: string; // 问题类型
count: number; // 数量
description: string;
details?: any[];
}
```
**UI 结构:**
```vue
<template>
<el-card class="quality-panel">
<template #header>
<div class="panel-header">
<h3>数据质量检查</h3>
<el-button type="primary" size="small" @click="handleCheck">
重新检查
</el-button>
</div>
</template>
<!-- 质量指标 -->
<div class="metrics-container">
<div
v-for="metric in metrics"
:key="metric.name"
class="metric-item"
:class="`metric-${metric.status}`"
>
<el-progress
type="circle"
:percentage="metric.value"
:status="metric.status"
/>
<span class="metric-name">{{ metric.name }}</span>
</div>
</div>
<!-- 问题列表 -->
<div class="issues-section">
<h4>检查结果</h4>
<el-alert
v-for="(issue, index) in issues"
:key="index"
:type="getIssueType(issue)"
:closable="false"
class="issue-item"
>
<template #title>
发现 <strong>{{ issue.count }}</strong> {{ issue.description }}
</template>
</el-alert>
<el-button type="text" @click="handleViewDetails">查看详情 </el-button>
</div>
</el-card>
</template>
```
---
## 4. 交互说明
### 4.1 文件上传流程
1. **拖拽上传**
- 用户拖拽文件到上传区域
- 显示上传进度条
- 上传成功后显示成功提示
- 自动添加到已上传文件列表
2. **点击上传**
- 点击上传区域触发文件选择对话框
- 选择文件后开始上传
- 显示上传进度
3. **文件验证**
- 文件格式验证:只接受指定格式
- 文件大小验证:超过限制显示错误提示
- 重复文件验证:同名文件提示是否覆盖
4. **上传状态**
- 上传中:显示进度条
- 上传成功:绿色勾选标记
- 上传失败:红色错误标记,显示错误信息
### 4.2 名单库选择
1. 默认选中全部名单库
2. 点击复选框切换选中状态
3. 实时更新选中人数统计
4. 取消选中时显示确认提示
### 4.3 数据质量检查
1. **自动触发**
- 文件上传完成后自动触发
- 显示检查进度
2. **手动触发**
- 点击"重新检查"按钮
- 覆盖之前的检查结果
3. **结果展示**
- 三个核心指标以环形进度图展示
- 颜色指示:绿色(≥95%)、黄色(85-94%)、红色(<85%)
- 问题列表按严重程度排序
### 4.4 按钮操作
1. **拉取本行信息**
- 点击后显示加载状态
- 从本行系统拉取相关数据
- 完成后显示成功提示并刷新页面
2. **生成报告**
- 验证必须上传至少一个文件
- 显示报告生成进度
- 生成成功后跳转到报告页面
---
## 5. 数据结构
### 5.1 后端接口
#### 5.1.1 获取项目上传数据状态
```typescript
GET /api/project/{projectId}/upload-status
Response:
{
"code": 200,
"data": {
"projectStatus": "已完成",
"lastUpdateTime": "2024-01-20 15:30:00",
"uploadedFiles": {
"transactionFiles": [], // 流水文件列表
"inquiryFiles": [], // 征信文件列表
"familyRelationFiles": [] // 家庭关系文件列表
},
"selectedNameLists": [], // 已选名单库
"qualityMetrics": { // 质量指标
"completeness": 98.5,
"consistency": 95.2,
"continuity": 92.8
},
"qualityIssues": [] // 质量问题列表
}
}
```
#### 5.1.2 上传文件接口
```typescript
POST /api/project/{projectId}/upload
Content-Type: multipart/form-data
Body:
{
"fileType": "transaction" | "inquiry" | "family_relation",
"files": File[]
}
Response:
{
"code": 200,
"data": {
"successCount": 2,
"failedCount": 0,
"uploadedFiles": [
{
"fileId": "123456",
"fileName": "流水数据.xlsx",
"fileSize": 2048576,
"uploadTime": "2024-01-20 15:30:00",
"status": "success"
}
]
}
}
```
#### 5.1.3 删除文件接口
```typescript
DELETE /api/project/{projectId}/file/{fileId}
Response:
{
"code": 200,
"msg": "删除成功"
}
```
#### 5.1.4 获取名单库列表
```typescript
GET /api/name-list/options
Response:
{
"code": 200,
"data": [
{
"value": "high_risk",
"label": "高风险人员名单",
"count": 68
},
{
"value": "history_suspicious",
"label": "历史可疑人员名单",
"count": 45
},
{
"value": "regulatory_focus",
"label": "监管关注名单",
"count": 32
}
]
}
```
#### 5.1.5 更新名单库选择
```typescript
PUT /api/project/{projectId}/name-lists
Body:
{
"selectedLists": ["high_risk", "history_suspicious", "regulatory_focus"]
}
Response:
{
"code": 200,
"msg": "更新成功"
}
```
#### 5.1.6 执行数据质量检查
```typescript
POST /api/project/{projectId}/quality-check
Response:
{
"code": 200,
"data": {
"checkId": "qc_123456",
"status": "completed",
"metrics": {
"completeness": 98.5,
"consistency": 95.2,
"continuity": 92.8
},
"issues": [
{
"type": "format_inconsistency",
"count": 23,
"description": "条数据格式不一致"
},
{
"type": "balance_anomaly",
"count": 5,
"description": "条余额连续性异常"
},
{
"type": "missing_field",
"count": 12,
"description": "条缺失关键字段"
}
]
}
}
```
#### 5.1.7 拉取本行信息
```typescript
POST /api/project/{projectId}/pull-bank-info
Response:
{
"code": 200,
"msg": "拉取成功",
"data": {
"pulledRecords": 156,
"pullTime": "2024-01-20 15:35:00"
}
}
```
#### 5.1.8 生成报告
```typescript
POST /api/project/{projectId}/generate-report
Response:
{
"code": 200,
"data": {
"reportId": "rpt_789012",
"reportUrl": "/project/123/report/rpt_789012",
"generateTime": "2024-01-20 15:40:00"
}
}
```
### 5.2 前端数据模型
```typescript
// 上传文件类型
type UploadFileType = 'transaction' | 'inquiry' | 'family_relation';
// 上传文件状态
type UploadStatus = 'uploading' | 'success' | 'error';
// 上传的文件
interface UploadedFile {
fileId: string;
fileName: string;
fileSize: number;
uploadTime: string;
status: UploadStatus;
errorMessage?: string;
}
// 名单库选项
interface NameListOption {
value: string;
label: string;
count: number;
disabled?: boolean;
}
// 质量指标
interface QualityMetric {
name: string;
value: number;
status: 'good' | 'warning' | 'error';
}
// 质量问题
interface QualityIssue {
type: string;
count: number;
description: string;
details?: any[];
}
// 项目上传数据状态
interface ProjectUploadStatus {
projectStatus: string;
lastUpdateTime: string;
uploadedFiles: {
transactionFiles: UploadedFile[];
inquiryFiles: UploadedFile[];
familyRelationFiles: UploadedFile[];
};
selectedNameLists: string[];
qualityMetrics: {
completeness: number;
consistency: number;
continuity: number;
};
qualityIssues: QualityIssue[];
}
```
---
## 6. 样式规范
### 6.1 颜色规范
```scss
// 主色
$primary-color: #409EFF;
$success-color: #67C23A;
$warning-color: #E6A23C;
$danger-color: #F56C6C;
$info-color: #909399;
// 中性色
$text-primary: #303133;
$text-regular: #606266;
$text-secondary: #909399;
$text-placeholder: #C0C4CC;
// 边框色
$border-base: #DCDFE6;
$border-light: #E4E7ED;
$border-lighter: #EBEEF5;
$border-extra-light: #F2F6FC;
// 背景色
$bg-color: #F5F7FA;
$card-bg: #FFFFFF;
```
### 6.2 间距规范
```scss
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 24px;
$spacing-xl: 32px;
```
### 6.3 圆角规范
```scss
$border-radius-sm: 2px;
$border-radius-base: 4px;
$border-radius-lg: 8px;
$border-radius-circle: 50%;
```
### 6.4 阴影规范
```scss
$box-shadow-base: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);
$box-shadow-dark: 0 2px 8px rgba(0, 0, 0, 0.15), 0 0 6px rgba(0, 0, 0, 0.1);
$box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
```
---
## 7. 组件样式代码
### 7.1 上传卡片样式
```scss
.upload-card {
height: 100%;
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
h3 {
margin: 0;
font-size: 16px;
font-weight: 500;
color: $text-primary;
}
.el-icon-info {
color: $info-color;
cursor: help;
}
}
.upload-area {
margin-bottom: $spacing-md;
.el-upload-dragger {
width: 100%;
height: 180px;
border: 2px dashed $border-base;
border-radius: $border-radius-lg;
background: $bg-color;
transition: all 0.3s;
&:hover {
border-color: $primary-color;
background: #F0F7FF;
}
}
.upload-content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
.el-icon-upload {
font-size: 48px;
color: $primary-color;
margin-bottom: $spacing-sm;
}
p {
margin: $spacing-xs 0;
font-size: 14px;
color: $text-regular;
}
.upload-tip {
font-size: 12px;
color: $text-secondary;
}
}
}
.file-list {
border-top: 1px solid $border-light;
padding-top: $spacing-md;
h4 {
margin: 0 0 $spacing-sm 0;
font-size: 14px;
color: $text-primary;
}
}
}
```
### 7.2 数据质量面板样式
```scss
.quality-panel {
.panel-header {
display: flex;
align-items: center;
justify-content: space-between;
h3 {
margin: 0;
font-size: 16px;
font-weight: 500;
}
}
.metrics-container {
display: flex;
justify-content: space-around;
margin-bottom: $spacing-lg;
.metric-item {
display: flex;
flex-direction: column;
align-items: center;
.el-progress {
margin-bottom: $spacing-sm;
}
.metric-name {
font-size: 14px;
color: $text-regular;
}
}
}
.issues-section {
border-top: 1px solid $border-light;
padding-top: $spacing-md;
h4 {
margin: 0 0 $spacing-md 0;
font-size: 14px;
color: $text-primary;
}
.issue-item {
margin-bottom: $spacing-sm;
&:last-child {
margin-bottom: 0;
}
}
}
}
```
### 7.3 页面整体布局样式
```scss
.upload-data-page {
padding: $spacing-lg;
background: $bg-color;
min-height: calc(100vh - 84px);
.page-header {
background: $card-bg;
padding: $spacing-lg;
border-radius: $border-radius-lg;
margin-bottom: $spacing-lg;
box-shadow: $box-shadow-base;
h1 {
margin: 0 0 $spacing-sm 0;
font-size: 24px;
font-weight: 500;
}
.page-info {
display: flex;
gap: $spacing-lg;
font-size: 14px;
color: $text-secondary;
margin-top: $spacing-sm;
.info-item {
display: flex;
align-items: center;
.label {
margin-right: $spacing-xs;
}
.status {
color: $success-color;
}
}
}
.page-description {
margin-top: $spacing-md;
padding: $spacing-md;
background: #F0F9FF;
border-left: 3px solid $primary-color;
border-radius: $border-radius-base;
font-size: 14px;
color: $text-regular;
}
}
.upload-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: $spacing-lg;
margin-bottom: $spacing-lg;
@media (min-width: 1200px) {
grid-template-columns: repeat(4, 1fr);
}
@media (max-width: 767px) {
grid-template-columns: 1fr;
}
}
.full-width {
grid-column: 1 / -1;
}
.action-bar {
display: flex;
justify-content: center;
gap: $spacing-lg;
margin-top: $spacing-xl;
}
}
```
---
## 8. 技术实现要点
### 8.1 文件上传
- 使用 Element UI 的 `el-upload` 组件
- 支持拖拽上传和点击上传
- 实现文件类型和大小校验
- 显示上传进度
- 支持断点续传(可选)
### 8.2 数据质量检查
- 异步执行检查任务
- 使用 WebSocket 或轮询获取检查进度
- 实时更新进度和结果
### 8.3 状态管理
- 使用 Vuex 管理上传状态
- 缓存已上传文件列表
- 同步名单库选择状态
### 8.4 性能优化
- 文件分片上传大文件
- 使用 Web Worker 处理文件预检查
- 虚拟滚动展示大量文件列表
---
## 9. 测试要点
### 9.1 功能测试
- 文件上传各种格式
- 文件大小限制验证
- 删除文件功能
- 名单库选择功能
- 数据质量检查准确性
- 报告生成功能
### 9.2 兼容性测试
- 主流浏览器兼容
- 不同屏幕尺寸适配
- 文件格式兼容性
### 9.3 性能测试
- 大文件上传性能
- 多文件同时上传
- 页面加载性能
### 9.4 异常处理测试
- 网络中断处理
- 文件上传失败处理
- 服务器错误处理
- 文件格式错误处理
---
## 10. 附录
### 10.1 相关页面
- 项目详情页:`/project/:id/detail`
- 参数配置页:`/project/:id/config`
- 初核结果页:`/project/:id/result`
- 报告页面:`/project/:id/report/:reportId`
### 10.2 权限要求
- 需要项目成员权限
- 上传操作需要编辑权限
- 删除操作需要删除权限
- 生成报告需要报告权限
### 10.3 相关文档
- [Element UI Upload 组件文档](https://element.eleme.cn/#/zh-CN/component/upload)
- [若依框架前端开发规范](../前端开发规范.md)
- [项目接口文档](../API文档/项目管理模块.md)
---
**文档版本**: v1.0
**创建时间**: 2024-01-30
**最后更新**: 2024-01-30
**文档状态**: 待评审

View File

@@ -0,0 +1,262 @@
# 员工导入Excel内双字段重复检测功能实现报告
## 功能概述
为员工导入模块添加Excel内双字段(柜员号和身份证号)重复检测功能,防止同一Excel文件中出现重复数据导入到数据库。
## 实现时间
2026-02-09
## 实现位置
- 文件: `D:\ccdi\ccdi\ruoyi-ccdi\src\main\java\com\ruoyi\ccdi\service\impl\CcdiEmployeeImportServiceImpl.java`
- 方法: `importEmployeeAsync` (第43-126行)
## 核心功能
### 1. 批量查询已存在的身份证号
在数据分类前,批量查询数据库中已存在的身份证号:
```java
Set<Long> existingIds = getExistingEmployeeIds(excelList);
Set<String> existingIdCards = getExistingIdCards(excelList);
```
**优点**:
- 减少数据库查询次数,提高性能
- 避免逐条查询导致的N+1问题
### 2. 添加Excel内处理跟踪集合
```java
Set<Long> processedEmployeeIds = new HashSet<>();
Set<String> processedIdCards = new HashSet<>();
```
**作用**:
- 跟踪Excel文件中已处理的柜员号
- 跟踪Excel文件中已处理的身份证号
- 用于检测Excel内部的重复数据
### 3. 双字段重复检测逻辑
在逐条处理时,按以下顺序检查:
```java
if (existingIds.contains(excel.getEmployeeId())) {
// 柜员号在数据库中已存在
if (isUpdateSupport) {
updateRecords.add(employee);
} else {
throw new RuntimeException("柜员号已存在且未启用更新支持");
}
} else if (processedEmployeeIds.contains(excel.getEmployeeId())) {
// 柜员号在Excel文件中重复
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
} else if (StringUtils.isNotEmpty(excel.getIdCard()) &&
processedIdCards.contains(excel.getIdCard())) {
// 身份证号在Excel文件中重复
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
} else {
// 无重复,添加到新记录
newRecords.add(employee);
// 只在成功时标记
if (excel.getEmployeeId() != null) {
processedEmployeeIds.add(excel.getEmployeeId());
}
if (StringUtils.isNotEmpty(excel.getIdCard())) {
processedIdCards.add(excel.getIdCard());
}
}
```
**检查顺序**:
1. 先检查柜员号是否在数据库中存在
2. 再检查柜员号是否在Excel文件内重复
3. 最后检查身份证号是否在Excel文件内重复
4. 只在记录成功添加到newRecords后才标记为已处理
### 4. 更新validateEmployeeData方法
**修改前**:
```java
public void validateEmployeeData(CcdiEmployeeAddDTO addDTO, Boolean isUpdateSupport, Set<Long> existingIds)
```
**修改后**:
```java
public void validateEmployeeData(CcdiEmployeeAddDTO addDTO, Boolean isUpdateSupport, Set<Long> existingIds, Set<String> existingIdCards)
```
**身份证号唯一性检查优化**:
```java
// 导入场景:如果柜员号不存在,才检查身份证号唯一性
if (!existingIds.contains(addDTO.getEmployeeId())) {
// 使用批量查询的结果检查身份证号唯一性
if (existingIdCards != null && existingIdCards.contains(addDTO.getIdCard())) {
throw new RuntimeException("该身份证号已存在");
}
}
```
**优点**:
- 使用批量查询结果,避免逐条查询
- 提高导入性能
## 技术特点
### 1. 双字段同时检测
同时检测柜员号(Long类型)和身份证号(String类型)的Excel内重复
### 2. 检查顺序合理
- 先检查数据库重复(避免无效数据处理)
- 再检查Excel内重复(防止重复导入)
- 最后标记已处理(只在成功后标记)
### 3. 空值处理
使用`StringUtils.isNotEmpty``Objects::nonNull`进行空值检查,避免空指针异常
### 4. 错误消息明确
- 柜员号重复: "柜员号[XXX]在导入文件中重复,已跳过此条记录"
- 身份证号重复: "身份证号[XXX]在导入文件中重复,已跳过此条记录"
### 5. 性能优化
- 批量查询数据库中已存在的柜员号和身份证号
- 使用HashSet进行O(1)复杂度的重复检测
- 减少数据库查询次数
## 测试场景
### 场景1: 柜员号在Excel内重复
**输入**:
```
柜员号 姓名 身份证号
1001 张三 110101199001011234
1001 李四 110101199001011235
```
**期望结果**:
- 第一条记录成功导入
- 第二条记录失败,错误信息: "柜员号[1001]在导入文件中重复,已跳过此条记录"
### 场景2: 身份证号在Excel内重复
**输入**:
```
柜员号 姓名 身份证号
1001 张三 110101199001011234
1002 李四 110101199001011234
```
**期望结果**:
- 第一条记录成功导入
- 第二条记录失败,错误信息: "身份证号[110101199001011234]在导入文件中重复,已跳过此条记录"
### 场景3: 柜员号和身份证号同时重复
**输入**:
```
柜员号 姓名 身份证号
1001 张三 110101199001011234
1001 张三 110101199001011234
```
**期望结果**:
- 第一条记录成功导入
- 第二条记录失败,错误信息: "柜员号[1001]在导入文件中重复,已跳过此条记录"
### 场景4: 正常导入(无重复)
**输入**:
```
柜员号 姓名 身份证号
1001 张三 110101199001011234
1002 李四 110101199001011235
1003 王五 110101199001011236
```
**期望结果**:
- 所有记录都成功导入
## 代码对比
### 修改前
```java
// 批量查询已存在的柜员号
Set<Long> existingIds = getExistingEmployeeIds(excelList);
// 分类数据
for (int i = 0; i < excelList.size(); i++) {
// ...
validateEmployeeData(addDTO, isUpdateSupport, existingIds);
if (existingIds.contains(excel.getEmployeeId())) {
if (isUpdateSupport) {
updateRecords.add(employee);
} else {
throw new RuntimeException("柜员号已存在且未启用更新支持");
}
} else {
newRecords.add(employee);
}
}
```
### 修改后
```java
// 批量查询已存在的柜员号和身份证号
Set<Long> existingIds = getExistingEmployeeIds(excelList);
Set<String> existingIdCards = getExistingIdCards(excelList);
// 用于跟踪Excel文件内已处理的主键
Set<Long> processedEmployeeIds = new HashSet<>();
Set<String> processedIdCards = new HashSet<>();
// 分类数据
for (int i = 0; i < excelList.size(); i++) {
// ...
validateEmployeeData(addDTO, isUpdateSupport, existingIds, existingIdCards);
if (existingIds.contains(excel.getEmployeeId())) {
if (isUpdateSupport) {
updateRecords.add(employee);
} else {
throw new RuntimeException("柜员号已存在且未启用更新支持");
}
} else if (processedEmployeeIds.contains(excel.getEmployeeId())) {
throw new RuntimeException(String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId()));
} else if (StringUtils.isNotEmpty(excel.getIdCard()) &&
processedIdCards.contains(excel.getIdCard())) {
throw new RuntimeException(String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard()));
} else {
newRecords.add(employee);
// 只在成功时标记
if (excel.getEmployeeId() != null) {
processedEmployeeIds.add(excel.getEmployeeId());
}
if (StringUtils.isNotEmpty(excel.getIdCard())) {
processedIdCards.add(excel.getIdCard());
}
}
}
```
## 参考实现
本功能参考了中介人员导入模块的双字段重复检测实现:
- 文件: `CcdiIntermediaryEntityImportServiceImpl.java`
- 关键方法: `importEntityAsync`
## 编译验证
已通过Maven编译验证,无语法错误:
```bash
mvn clean compile -DskipTests
```
编译结果: BUILD SUCCESS
## 测试脚本
测试脚本位置: `D:\ccdi\ccdi\doc\test-scripts\test_employee_duplicate_detection.py`
## 总结
本次实现成功为员工导入模块添加了Excel内双字段重复检测功能,主要改进包括:
1. **批量查询优化**: 添加`getExistingIdCards`方法批量查询已存在的身份证号
2. **双字段检测**: 同时检测柜员号和身份证号的Excel内重复
3. **性能优化**: 使用批量查询减少数据库访问次数
4. **错误处理**: 提供明确的错误提示信息
5. **代码规范**: 遵循若依框架编码规范,使用MyBatis Plus进行数据操作
该功能可以有效防止Excel文件内部的重复数据导入到数据库,提高数据质量和导入可靠性。

View File

@@ -0,0 +1,303 @@
# 员工导入Excel内双字段重复检测 - 代码流程说明
## 方法签名
```java
public void importEmployeeAsync(List<CcdiEmployeeExcel> excelList, Boolean isUpdateSupport, String taskId)
```
## 完整流程图
```
开始
├─ 1. 初始化集合
│ ├─ newRecords = new ArrayList<>() // 新增记录
│ ├─ updateRecords = new ArrayList<>() // 更新记录
│ └─ failures = new ArrayList<>() // 失败记录
├─ 2. 批量查询数据库
│ ├─ getExistingEmployeeIds(excelList)
│ │ └─ 返回: Set<Long> existingIds // 数据库中已存在的柜员号
│ │
│ └─ getExistingIdCards(excelList)
│ └─ 返回: Set<String> existingIdCards // 数据库中已存在的身份证号
├─ 3. 初始化Excel内跟踪集合
│ ├─ processedEmployeeIds = new HashSet<>() // Excel内已处理的柜员号
│ └─ processedIdCards = new HashSet<>() // Excel内已处理的身份证号
├─ 4. 遍历Excel数据
│ │
│ └─ FOR EACH excel IN excelList
│ │
│ ├─ 4.1 数据转换
│ │ ├─ addDTO = new CcdiEmployeeAddDTO()
│ │ ├─ BeanUtils.copyProperties(excel, addDTO)
│ │ └─ employee = new CcdiEmployee()
│ │ └─ BeanUtils.copyProperties(excel, employee)
│ │
│ ├─ 4.2 数据验证
│ │ └─ validateEmployeeData(addDTO, isUpdateSupport, existingIds, existingIdCards)
│ │ ├─ 验证必填字段(姓名、柜员号、部门、身份证号、电话、状态)
│ │ ├─ 验证身份证号格式
│ │ └─ 验证柜员号和身份证号唯一性
│ │
│ ├─ 4.3 重复检测与分类
│ │ │
│ │ ├─ IF existingIds.contains(excel.getEmployeeId())
│ │ │ ├─ 柜员号在数据库中已存在
│ │ │ ├─ IF isUpdateSupport
│ │ │ │ └─ updateRecords.add(employee) // 添加到更新列表
│ │ │ └─ ELSE
│ │ │ └─ throw RuntimeException("柜员号已存在且未启用更新支持")
│ │ │
│ │ ├─ ELSE IF processedEmployeeIds.contains(excel.getEmployeeId())
│ │ │ └─ throw RuntimeException("柜员号[XXX]在导入文件中重复,已跳过此条记录")
│ │ │
│ │ ├─ ELSE IF processedIdCards.contains(excel.getIdCard())
│ │ │ └─ throw RuntimeException("身份证号[XXX]在导入文件中重复,已跳过此条记录")
│ │ │
│ │ └─ ELSE
│ │ ├─ newRecords.add(employee) // 添加到新增列表
│ │ ├─ IF excel.getEmployeeId() != null
│ │ │ └─ processedEmployeeIds.add(excel.getEmployeeId()) // 标记柜员号
│ │ └─ IF StringUtils.isNotEmpty(excel.getIdCard())
│ │ └─ processedIdCards.add(excel.getIdCard()) // 标记身份证号
│ │
│ └─ 4.4 异常处理
│ └─ CATCH Exception
│ ├─ failure = new ImportFailureVO()
│ ├─ BeanUtils.copyProperties(excel, failure)
│ ├─ failure.setErrorMessage(e.getMessage())
│ └─ failures.add(failure)
├─ 5. 批量操作数据库
│ ├─ IF !newRecords.isEmpty()
│ │ └─ saveBatch(newRecords, 500) // 批量插入新数据
│ │
│ └─ IF !updateRecords.isEmpty() && isUpdateSupport
│ └─ employeeMapper.insertOrUpdateBatch(updateRecords) // 批量更新已有数据
├─ 6. 保存失败记录到Redis
│ └─ IF !failures.isEmpty()
│ └─ redisTemplate.opsForValue().set("import:employee:" + taskId + ":failures", failures, 7, TimeUnit.DAYS)
├─ 7. 生成导入结果
│ ├─ result = new ImportResult()
│ ├─ result.setTotalCount(excelList.size())
│ ├─ result.setSuccessCount(newRecords.size() + updateRecords.size())
│ └─ result.setFailureCount(failures.size())
└─ 8. 更新导入状态
└─ updateImportStatus("employee", taskId, finalStatus, result)
└─ IF result.getFailureCount() == 0
└─ finalStatus = "SUCCESS"
└─ ELSE
└─ finalStatus = "PARTIAL_SUCCESS"
结束
```
## 关键逻辑说明
### 1. 重复检测优先级
```
数据库柜员号重复 > Excel内柜员号重复 > Excel内身份证号重复
```
**原因**:
- 数据库检查优先: 避免处理已经存在且不允许更新的数据
- Excel内柜员号检查: 柜员号是主键,优先检查
- Excel内身份证号检查: 身份证号也需要唯一性
### 2. 标记时机
```
只在记录成功添加到newRecords后才标记为已处理
```
**原因**:
- 避免将验证失败的记录标记为已处理
- 确保只有成功插入数据库的记录才会占用柜员号和身份证号
- 防止因前一条记录失败导致后一条有效记录被误判为重复
### 3. 空值处理
```java
// 柜员号空值检查
if (excel.getEmployeeId() != null) {
processedEmployeeIds.add(excel.getEmployeeId());
}
// 身份证号空值检查
if (StringUtils.isNotEmpty(excel.getIdCard())) {
processedIdCards.add(excel.getIdCard());
}
```
**原因**:
- 防止空指针异常
- 确保只有有效的柜员号和身份证号才会被检查重复
### 4. 批量查询优化
```java
// 批量查询柜员号
Set<Long> existingIds = getExistingEmployeeIds(excelList);
// 批量查询身份证号
Set<String> existingIdCards = getExistingIdCards(excelList);
```
**优点**:
- 一次性查询所有需要的数据
- 避免逐条查询导致的N+1问题
- 使用HashSet实现O(1)复杂度的查找
## 错误消息说明
### 1. 柜员号在数据库中已存在
```java
"柜员号已存在且未启用更新支持"
```
### 2. 柜员号在Excel内重复
```java
String.format("柜员号[%d]在导入文件中重复,已跳过此条记录", excel.getEmployeeId())
```
**示例**: "柜员号[1001]在导入文件中重复,已跳过此条记录"
### 3. 身份证号在Excel内重复
```java
String.format("身份证号[%s]在导入文件中重复,已跳过此条记录", excel.getIdCard())
```
**示例**: "身份证号[110101199001011234]在导入文件中重复,已跳过此条记录"
## validateEmployeeData方法说明
### 方法签名
```java
public void validateEmployeeData(CcdiEmployeeAddDTO addDTO,
Boolean isUpdateSupport,
Set<Long> existingIds,
Set<String> existingIdCards)
```
### 验证流程
```
1. 验证必填字段
├─ 姓名不能为空
├─ 柜员号不能为空
├─ 所属部门不能为空
├─ 身份证号不能为空
├─ 电话不能为空
└─ 状态不能为空
2. 验证身份证号格式
└─ IdCardUtil.getErrorMessage(addDTO.getIdCard())
3. 验证唯一性
├─ IF existingIds == null (单条新增场景)
│ ├─ 检查柜员号唯一性(数据库查询)
│ └─ 检查身份证号唯一性(数据库查询)
└─ ELSE (导入场景)
├─ IF 柜员号不存在于数据库
│ └─ 检查身份证号唯一性(使用批量查询结果)
└─ ELSE (柜员号已存在,允许更新)
└─ 跳过身份证号检查(更新模式下不检查身份证号重复)
4. 验证状态
└─ 状态只能填写'0'(在职)或'1'(离职)
```
### 导入场景的身份证号唯一性检查优化
```java
// 导入场景:如果柜员号不存在,才检查身份证号唯一性
if (!existingIds.contains(addDTO.getEmployeeId())) {
// 使用批量查询的结果检查身份证号唯一性
if (existingIdCards != null && existingIdCards.contains(addDTO.getIdCard())) {
throw new RuntimeException("该身份证号已存在");
}
}
```
**优化点**:
- 使用批量查询结果`existingIdCards`,避免逐条查询数据库
- 只在柜员号不存在时才检查身份证号(因为柜员号存在时是更新模式)
## 批量查询方法说明
### getExistingEmployeeIds
```java
private Set<Long> getExistingEmployeeIds(List<CcdiEmployeeExcel> excelList) {
List<Long> employeeIds = excelList.stream()
.map(CcdiEmployeeExcel::getEmployeeId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (employeeIds.isEmpty()) {
return Collections.emptySet();
}
List<CcdiEmployee> existingEmployees = employeeMapper.selectBatchIds(employeeIds);
return existingEmployees.stream()
.map(CcdiEmployee::getEmployeeId)
.collect(Collectors.toSet());
}
```
### getExistingIdCards
```java
private Set<String> getExistingIdCards(List<CcdiEmployeeExcel> excelList) {
List<String> idCards = excelList.stream()
.map(CcdiEmployeeExcel::getIdCard)
.filter(StringUtils::isNotEmpty)
.collect(Collectors.toList());
if (idCards.isEmpty()) {
return Collections.emptySet();
}
LambdaQueryWrapper<CcdiEmployee> wrapper = new LambdaQueryWrapper<>();
wrapper.in(CcdiEmployee::getIdCard, idCards);
List<CcdiEmployee> existingEmployees = employeeMapper.selectList(wrapper);
return existingEmployees.stream()
.map(CcdiEmployee::getIdCard)
.collect(Collectors.toSet());
}
```
**特点**:
- 使用Stream API进行数据提取和过滤
- 过滤空值,避免无效查询
- 使用MyBatis Plus的批量查询方法
- 返回Set集合,实现O(1)复杂度的查找
## 性能分析
### 时间复杂度
- 批量查询: O(n), n为Excel记录数
- 重复检测: O(1), 使用HashSet
- 总体复杂度: O(n)
### 空间复杂度
- existingIds: O(m), m为数据库中已存在的柜员号数量
- existingIdCards: O(k), k为数据库中已存在的身份证号数量
- processedEmployeeIds: O(n), n为Excel记录数
- processedIdCards: O(n), n为Excel记录数
- 总体空间复杂度: O(m + k + n)
### 数据库查询次数
- 修改前: 1次(批量查询柜员号) + n次(逐条查询身份证号) = O(n)
- 修改后: 2次(批量查询柜员号 + 批量查询身份证号) = O(1)
**性能提升**: 减少n-1次数据库查询
## 总结
本实现通过以下技术手段实现了Excel内双字段重复检测:
1. 批量查询优化,减少数据库访问
2. 使用HashSet进行O(1)复杂度的重复检测
3. 合理的检查顺序和标记时机
4. 完善的空值处理和错误提示
5. 遵循若依框架编码规范,使用MyBatis Plus进行数据操作

View File

@@ -0,0 +1,489 @@
# 中介库导入失败记录查看功能设计
## 1. 需求背景
当前中介库导入功能在导入失败后,只显示通知消息,但没有提供查看失败记录的入口,用户无法了解具体哪些数据导入失败以及失败原因。
## 2. 功能描述
为中介库管理页面添加**导入失败记录查看**功能,支持个人中介和实体中介两种类型的失败记录查看。
### 2.1 核心功能
1. **双按钮独立管理**
- "查看个人导入失败记录"按钮 - 仅在个人中介导入存在失败记录时显示
- "查看实体导入失败记录"按钮 - 仅在实体中介导入存在失败记录时显示
- 按钮带tooltip提示上次导入时间
2. **localStorage持久化存储**
- 分别存储个人中介和实体中介的导入任务信息
- 存储期限:7天,过期自动清除
- 存储内容:任务ID、导入时间、成功数、失败数、hasFailures标志
3. **失败记录对话框**
- 显示导入统计摘要(总数/成功/失败)
- 表格展示所有失败记录,支持分页(每页10条)
- 提供清除历史记录按钮
- 记录过期时自动提示并清除
## 3. 技术设计
### 3.1 组件结构
```
index.vue (中介库管理页面)
├── 工具栏按钮区域
│ ├── 新增按钮
│ ├── 导入按钮
│ ├── 查看个人导入失败记录按钮 (条件显示)
│ └── 查看实体导入失败记录按钮 (条件显示)
├── 数据表格
├── 个人中介导入失败记录对话框
└── 实体中介导入失败记录对话框
```
### 3.2 数据流程
```
用户选择文件上传
ImportDialog 组件提交导入
后端返回 taskId (异步处理)
前端开始轮询导入状态
导入完成,ImportDialog 触发 @import-complete 事件
index.vue 接收事件,根据 importType 判断类型
保存任务信息到 localStorage (person 或 entity)
更新对应的失败记录按钮显示状态
用户点击"查看失败记录"按钮
调用后端接口获取失败记录列表 (支持分页)
在对话框中展示失败记录和错误原因
```
### 3.3 localStorage存储设计
#### 3.3.1 个人中介导入任务
**Key**: `intermediary_person_import_last_task`
**数据结构**:
```javascript
{
taskId: "uuid", // 任务ID
saveTime: 1234567890, // 保存时间戳
hasFailures: true, // 是否有失败记录
totalCount: 100, // 总数
successCount: 95, // 成功数
failureCount: 5 // 失败数
}
```
#### 3.3.2 实体中介导入任务
**Key**: `intermediary_entity_import_last_task`
**数据结构**: 同个人中介
### 3.4 页面状态管理
```javascript
data() {
return {
// 按钮显示状态
showPersonFailureButton: false,
showEntityFailureButton: false,
// 当前任务ID
currentPersonTaskId: null,
currentEntityTaskId: null,
// 个人失败记录对话框
personFailureDialogVisible: false,
personFailureList: [],
personFailureLoading: false,
personFailureTotal: 0,
personFailureQueryParams: {
pageNum: 1,
pageSize: 10
},
// 实体失败记录对话框
entityFailureDialogVisible: false,
entityFailureList: [],
entityFailureLoading: false,
entityFailureTotal: 0,
entityFailureQueryParams: {
pageNum: 1,
pageSize: 10
}
}
}
```
## 4. 接口依赖
### 4.1 已有后端接口
#### 4.1.1 查询个人中介导入失败记录
**接口**: `GET /ccdi/intermediary/importPersonFailures/{taskId}`
**参数**:
- `taskId`: 任务ID (路径参数)
- `pageNum`: 页码 (默认1)
- `pageSize`: 每页大小 (默认10)
**返回**: `IntermediaryPersonImportFailureVO[]`
**字段**:
- `name`: 姓名
- `personId`: 证件号码
- `personType`: 人员类型
- `gender`: 性别
- `mobile`: 手机号码
- `company`: 所在公司
- `errorMessage`: 错误信息
#### 4.1.2 查询实体中介导入失败记录
**接口**: `GET /ccdi/intermediary/importEntityFailures/{taskId}`
**参数**:
- `taskId`: 任务ID (路径参数)
- `pageNum`: 页码 (默认1)
- `pageSize`: 每页大小 (默认10)
**返回**: `IntermediaryEntityImportFailureVO[]`
**字段**:
- `enterpriseName`: 机构名称
- `socialCreditCode`: 统一社会信用代码
- `enterpriseType`: 主体类型
- `enterpriseNature`: 企业性质
- `legalRepresentative`: 法定代表人
- `establishDate`: 成立日期
- `errorMessage`: 错误信息
### 4.2 前端API方法
已有API方法 (位于 `@/api/ccdiIntermediary.js`):
- `getPersonImportFailures(taskId, pageNum, pageSize)` - 查询个人导入失败记录
- `getEntityImportFailures(taskId, pageNum, pageSize)` - 查询实体导入失败记录
## 5. UI设计
### 5.1 工具栏按钮
```vue
<el-col :span="1.5" v-if="showPersonFailureButton">
<el-tooltip :content="getPersonImportTooltip()" placement="top">
<el-button
type="warning"
plain
icon="el-icon-warning"
size="mini"
@click="viewPersonImportFailures"
>查看个人导入失败记录</el-button>
</el-tooltip>
</el-col>
<el-col :span="1.5" v-if="showEntityFailureButton">
<el-tooltip :content="getEntityImportTooltip()" placement="top">
<el-button
type="warning"
plain
icon="el-icon-warning"
size="mini"
@click="viewEntityImportFailures"
>查看实体导入失败记录</el-button>
</el-tooltip>
</el-col>
```
### 5.2 失败记录对话框
**个人中介失败记录对话框**:
- 标题: "个人中介导入失败记录"
- 顶部提示: 显示导入统计信息
- 表格列: 姓名、证件号码、人员类型、性别、手机号码、所在公司、**失败原因**(最小宽度200px,溢出显示tooltip)
- 分页组件: 支持翻页
- 底部按钮: "关闭"、"清除历史记录"
**实体中介失败记录对话框**:
- 标题: "实体中介导入失败记录"
- 顶部提示: 显示导入统计信息
- 表格列: 机构名称、统一社会信用代码、主体类型、企业性质、法定代表人、成立日期、**失败原因**(最小宽度200px,溢出显示tooltip)
- 分页组件: 支持翻页
- 底部按钮: "关闭"、"清除历史记录"
## 6. 核心方法设计
### 6.1 localStorage管理方法
#### 6.1.1 个人中介导入任务
```javascript
/** 保存个人导入任务到localStorage */
savePersonImportTaskToStorage(taskData) {
const data = {
...taskData,
saveTime: Date.now()
}
localStorage.setItem('intermediary_person_import_last_task', JSON.stringify(data))
}
/** 从localStorage读取个人导入任务 */
getPersonImportTaskFromStorage() {
try {
const data = localStorage.getItem('intermediary_person_import_last_task')
if (!data) return null
const task = JSON.parse(data)
// 7天过期检查
const sevenDays = 7 * 24 * 60 * 60 * 1000
if (Date.now() - task.saveTime > sevenDays) {
this.clearPersonImportTaskFromStorage()
return null
}
return task
} catch (error) {
console.error('读取个人导入任务失败:', error)
this.clearPersonImportTaskFromStorage()
return null
}
}
/** 清除个人导入任务 */
clearPersonImportTaskFromStorage() {
localStorage.removeItem('intermediary_person_import_last_task')
}
```
#### 6.1.2 实体中介导入任务
结构同个人中介,方法名为:
- `saveEntityImportTaskToStorage(taskData)`
- `getEntityImportTaskFromStorage()`
- `clearEntityImportTaskFromStorage()`
### 6.2 导入完成处理
```javascript
/** 处理导入完成 */
handleImportComplete(importData) {
const { taskId, hasFailures, importType, totalCount, successCount, failureCount } = importData
if (importType === 'person') {
// 保存个人导入任务
this.savePersonImportTaskToStorage({
taskId,
hasFailures,
totalCount,
successCount,
failureCount
})
// 更新按钮显示
this.showPersonFailureButton = hasFailures
this.currentPersonTaskId = taskId
} else if (importType === 'entity') {
// 保存实体导入任务
this.saveEntityImportTaskToStorage({
taskId,
hasFailures,
totalCount,
successCount,
failureCount
})
// 更新按钮显示
this.showEntityFailureButton = hasFailures
this.currentEntityTaskId = taskId
}
// 刷新列表
this.getList()
}
```
### 6.3 查看失败记录
```javascript
/** 查看个人导入失败记录 */
viewPersonImportFailures() {
this.personFailureDialogVisible = true
this.getPersonFailureList()
}
/** 查询个人失败记录列表 */
getPersonFailureList() {
this.personFailureLoading = true
getPersonImportFailures(
this.currentPersonTaskId,
this.personFailureQueryParams.pageNum,
this.personFailureQueryParams.pageSize
).then(response => {
this.personFailureList = response.rows
this.personFailureTotal = response.total
this.personFailureLoading = false
}).catch(error => {
this.personFailureLoading = false
// 错误处理: 404表示记录已过期
if (error.response?.status === 404) {
this.$modal.msgWarning('导入记录已过期,无法查看失败记录')
this.clearPersonImportTaskFromStorage()
this.showPersonFailureButton = false
this.personFailureDialogVisible = false
} else {
this.$modal.msgError('查询失败记录失败')
}
})
}
```
### 6.4 清除历史记录
```javascript
/** 清除个人导入历史记录 */
clearPersonImportHistory() {
this.$confirm('确认清除上次导入记录?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.clearPersonImportTaskFromStorage()
this.showPersonFailureButton = false
this.currentPersonTaskId = null
this.personFailureDialogVisible = false
this.$message.success('已清除')
}).catch(() => {})
}
```
## 7. 生命周期管理
### 7.1 created钩子
```javascript
created() {
this.getList()
this.loadEnumOptions()
this.restoreImportState() // 恢复导入状态
}
```
### 7.2 恢复导入状态
```javascript
/** 恢复导入状态 */
restoreImportState() {
// 恢复个人中介导入状态
const personTask = this.getPersonImportTaskFromStorage()
if (personTask && personTask.hasFailures && personTask.taskId) {
this.currentPersonTaskId = personTask.taskId
this.showPersonFailureButton = true
}
// 恢复实体中介导入状态
const entityTask = this.getEntityImportTaskFromStorage()
if (entityTask && entityTask.hasFailures && entityTask.taskId) {
this.currentEntityTaskId = entityTask.taskId
this.showEntityFailureButton = true
}
}
```
## 8. 边界情况处理
### 8.1 记录过期
- localStorage中存储的记录超过7天,自动清除
- 后端接口返回404时,提示用户"导入记录已过期",并清除本地存储
- 清除后隐藏对应的"查看失败记录"按钮
### 8.2 并发导入
- 每次新导入开始前,清除旧的导入记录
- 同一类型的导入进行时,取消之前的轮询
- 只保留最近一次的导入任务信息
### 8.3 网络错误
- 查询失败记录时网络错误,显示友好的错误提示
- 不影响页面其他功能的正常使用
## 9. 测试要点
### 9.1 功能测试
1. **个人中介导入失败场景**
- 导入包含错误数据的Excel文件
- 验证失败记录按钮是否显示
- 点击按钮查看失败记录
- 验证失败原因是否正确显示
2. **实体中介导入失败场景**
- 导入包含错误数据的Excel文件
- 验证失败记录按钮是否显示
- 点击按钮查看失败记录
- 验证失败原因是否正确显示
3. **localStorage持久化**
- 导入失败后刷新页面
- 验证"查看失败记录"按钮是否仍然显示
- 验证点击后能否正常查看失败记录
4. **分页功能**
- 失败记录超过10条时
- 验证分页组件是否正常工作
- 验证翻页后数据是否正确
5. **清除历史记录**
- 点击"清除历史记录"按钮
- 验证localStorage是否清除
- 验证按钮是否隐藏
- 再次点击导入,验证新记录是否正常
6. **记录过期处理**
- 手动修改localStorage中的saveTime模拟过期
- 刷新页面,验证按钮是否隐藏
- 或点击查看,验证是否提示"记录已过期"
### 9.2 兼容性测试
1. **浏览器兼容性**
- Chrome
- Firefox
- Edge
- Safari
2. **数据量大时性能测试**
- 导入1000条数据,其中100条失败
- 验证查询速度和渲染性能
## 10. 参考实现
本设计参考了员工管理页面 (`ccdiEmployee/index.vue`) 的导入失败记录查看功能的实现,主要参考点:
1. localStorage存储模式
2. 失败记录对话框布局
3. 分页查询逻辑
4. 错误处理机制
5. 过期记录清理逻辑
## 11. 变更历史
| 日期 | 版本 | 变更内容 | 作者 |
|------|------|----------|------|
| 2026-02-08 | 1.0 | 初始设计 | Claude |

View File

@@ -0,0 +1,145 @@
# 01-项目管理模块
## 模块概述
项目管理模块是系统的首页和入口,用于管理所有历史创建的核查项目。该模块提供项目的创建、查询、状态管理、归档等核心功能,是用户进行项目管理和监控的主要界面。
## 模块结构
```
项目管理模块
├── 导航与搜索区
├── 项目列表区
└── 快捷入口区
```
## 功能分解
### 1.1 导航与搜索区
**功能描述**: 位于页面顶部,提供项目搜索和新建项目的入口功能。
**功能点**:
- **项目搜索**: 支持通过输入关键词对项目名称进行模糊搜索
- **新建项目**: 点击打开标准表单,填写项目名称、人员等完整信息创建新项目
- **导入历史项目**: 复制历史项目配置(人员范围、流水、征信数据配置)快速创建新项目
**数据要素**:
- 项目名称(搜索关键词)
- 项目配置模板
### 1.2 项目列表区
**功能描述**: 以表格形式展示所有初核项目,是用户进行项目管理和监控的核心面板。
**功能点**:
- **项目信息展示**: 显示项目名称、简要描述、创建日期、状态、目标人数、预警人数
- **项目状态标识**: 通过色块直观标识项目状态(进行中、已完成等)
- **预警人数动态更新**: 对于"进行中"项目,预警人数数据动态更新
- **查看结果**: 适用于已完成项目,跳转至该项目的初核结果页
- **重新分析**: 适用于已完成项目,基于原有数据重新运行风险模型,更新分析结果
- **归档项目**: 将已结束且无需日常关注的项目移入归档库生成PDF文件导出
- **进入项目**: 适用于进行中项目,进入该项目的工作台开展具体工作
**数据要素**:
- 项目名称
- 项目描述
- 创建时间
- 项目状态(进行中、已完成)
- 目标人数
- 预警人数
### 1.3 快捷入口区
**功能描述**: 提供一键触达的高频操作按钮,提升常用工作流的启动效率。
**功能点**:
- **导入历史项目**: 复制历史项目的配置(人员范围、流水、征信数据),快速创建新项目
- **创建季度初核**: 快速启动标准化季度周期性排查项目,系统预填当前季度时间范围等配置
- **创建新员工排查**: 为特定新员工创建专项排查任务
**数据要素**:
- 历史项目模板
- 当前季度时间范围
- 新员工信息
## 数据模型
### 项目实体 (Project)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| projectId | Long | 项目ID | 是 |
| projectName | String | 项目名称 | 是 |
| description | String | 项目描述 | 否 |
| createTime | DateTime | 创建时间 | 是 |
| status | String | 状态(进行中/已完成) | 是 |
| targetCount | Integer | 目标人数 | 是 |
| warningCount | Integer | 预警人数 | 是 |
| isArchived | Boolean | 是否归档 | 否 |
## 页面原型
### 原型设计文档
详细的页面原型设计请查看:[06-页面原型设计.md](./06-页面原型设计.md)
### 主要页面
1. **首页** - 项目列表展示页面
- 搜索和操作区
- 项目列表表格
- 快捷入口卡片
2. **新建项目弹窗页** - 项目信息录入表单
- 基本信息表单
- 人员选择器
- 时间范围选择器
3. **导入历史项目弹窗页** - 历史项目选择列表
- 项目列表(带单选)
- 项目详情预览
- 新项目配置
### 原型资源
- **墨刀原型**: [../../纪检初核系统-离线演示包/演示模式.html](../../纪检初核系统-离线演示包/演示模式.html)
- **设计规范**: 基于 Element UI 2.15 组件库
## 子文档索引
本模块包含以下详细文档:
- [README.md](./README.md) - 模块总览
- [01-导航与搜索区.md](./01-导航与搜索区.md) - 导航与搜索区详细设计
- [02-项目列表区.md](./02-项目列表区.md) - 项目列表区详细设计
- [03-快捷入口区.md](./03-快捷入口区.md) - 快捷入口区详细设计
- [04-数据模型.md](./04-数据模型.md) - 数据模型定义
- [05-业务规则.md](./05-业务规则.md) - 业务规则说明
- [06-页面原型设计.md](./06-页面原型设计.md) - 页面原型详细设计
- [复核报告.md](./复核报告.md) - 需求复核报告
## 业务规则
1. **项目状态流转**: 新建 → 进行中 → 已完成 → 已归档
2. **预警人数更新**: 进行中项目的预警人数需要实时更新
3. **归档条件**: 只有已完成的项目才能归档
4. **重新分析**: 只能对已完成项目执行重新分析
## 交互关系
| 关联模块 | 交互说明 |
|---------|---------|
| 项目工作台 | 点击"进入项目"跳转到项目工作台 |
| 初核结果总览 | 点击"查看结果"跳转到结果页 |
## 功能点统计
- 二级功能: 3个
- 三级功能点: 12个
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 第9-62行

View File

@@ -0,0 +1,528 @@
# 项目管理模块 - 页面原型设计
## 设计概述
本文档定义项目管理模块的页面原型设计,基于若依框架 + Element UI组件库的设计规范。
## 设计规范
### 颜色规范
| 颜色类型 | 颜色值 | 用途 |
|---------|-------|------|
| 主题色 | #409EFF | 主按钮、链接、高亮 |
| 成功色 | #67C23A | 成功状态、已完成项目 |
| 警告色 | #E6A23C | 警告提示 |
| 危险色 | #F56C6C | 危险操作、删除 |
| 信息色 | #909399 | 次要信息、禁用状态 |
| 文字色 | #303133 | 主要文字 |
| 次要文字 | #606266 | 次要文字 |
| 边框色 | #DCDFE6 | 边框、分割线 |
| 背景色 | #F5F7FA | 页面背景 |
### 字体规范
| 类型 | 字体大小 | 字重 | 行高 |
|-----|---------|------|------|
| 页面标题 | 20px | 500 | 28px |
| 卡片标题 | 16px | 500 | 24px |
| 正文 | 14px | 400 | 22px |
| 小字 | 12px | 400 | 20px |
### 间距规范
| 间距类型 | 数值 |
|---------|------|
| 页面边距 | 20px |
| 卡片间距 | 16px |
| 元素间距 | 8px |
| 小间距 | 4px |
---
## 页面1: 项目管理首页
### 页面布局
```
+------------------------------------------------------------------+
| Logo | 纪检初核系统 首页 | 项目工作台 | 系统管理 | 用户▼ |
+------------------------------------------------------------------+
| 项目管理 |
| +------------------------------------------------------------+ |
| | 🔍 项目搜索: [________________] [新建项目] [导入历史项目] | |
| +------------------------------------------------------------+ |
| |
| +------------------------------------------------------------+ |
| | 项目列表 | |
| +------------------------------------------------------------+ |
| | 序号 | 项目名称 | 创建时间 | 状态 | 目标 | 预警 | 操作| |
| |------|--------------|------------|------|------|------|-----| |
| | 1 | 2024年Q1初核 | 2024-01-01 | ⏳进行中| 500 | 15 |[详情]| |
| | 2 | 2023年Q4初核 | 2023-10-01 | ✅已完成| 480 | 23 |[查看]| |
| +------------------------------------------------------------+ |
| |
| +------------------------------------------------------------+ |
| | 快捷入口 | |
| +------------------------------------------------------------+ |
| | [📋 导入历史项目] [📅 创建季度初核] [👤 创建新员工排查] | |
| +------------------------------------------------------------+ |
+------------------------------------------------------------------+
```
### 详细组件说明
#### 1. 顶部导航栏
**位置**: 固定在页面顶部
**组件**:
- 左侧: Logo + 系统名称
- 中间: 主导航菜单
- 右侧: 用户信息下拉菜单
**代码示例**:
```vue
<el-menu mode="horizontal" :default-active="activeIndex">
<el-menu-item index="/project">项目管理</el-menu-item>
<el-menu-item index="/workspace">项目工作台</el-menu-item>
<el-menu-item index="/system">系统管理</el-menu-item>
</el-menu>
```
#### 2. 搜索和操作区
**位置**: 导航栏下方,全宽度
**布局**:
```
+------------------------------------------------------------+
| 项目管理 [新建项目] [导入历史项目] |
+------------------------------------------------------------+
| 🔍 [搜索项目名称...................] 高级搜索 ▼ |
+------------------------------------------------------------+
```
**组件规格**:
- 搜索框: 宽度 300px, 高度 32px
- 按钮: 高度 32px, 内边距 12px
- 图标: 14px
**Element UI代码**:
```vue
<el-row :gutter="16">
<el-col :span="18">
<el-input
v-model="queryParams.projectName"
placeholder="请输入项目名称"
prefix-icon="el-icon-search"
clearable
@keyup.enter.native="handleQuery"
/>
</el-col>
<el-col :span="6" style="text-align: right">
<el-button
type="primary"
icon="el-icon-plus"
@click="handleAdd"
>新建项目</el-button>
<el-button
icon="el-icon-folder-opened"
@click="handleImport"
>导入历史项目</el-button>
</el-col>
</el-row>
```
#### 3. 项目列表表格
**位置**: 搜索区下方
**列定义**:
| 列名 | 宽度 | 对齐 | 说明 |
|-----|------|------|------|
| 序号 | 60px | 居中 | 自动编号 |
| 项目名称 | 200px | 左对齐 | 主标题+描述 |
| 创建时间 | 120px | 居中 | YYYY-MM-DD |
| 状态 | 100px | 居中 | 带颜色的标签 |
| 目标人数 | 80px | 居中 | 数字 |
| 预警人数 | 80px | 居中 | 数字+刷新图标 |
| 操作 | 200px | 左对齐 | 按钮组 |
**状态标签样式**:
```vue
<el-tag
:type="row.projectStatus === '0' ? 'primary' : (row.projectStatus === '1' ? 'success' : 'info')"
size="small"
>
{{ row.projectStatus === '0' ? '进行中' : (row.projectStatus === '1' ? '已完成' : '已归档') }}
</el-tag>
```
**操作按钮显示规则**:
```vue
<!-- 进行中项目 -->
<el-button size="mini" type="text" icon="el-icon-s-data">进入项目</el-button>
<!-- 已完成项目 -->
<el-button size="mini" type="text" icon="el-icon-view">查看结果</el-button>
<el-button size="mini" type="text" icon="el-icon-refresh">重新分析</el-button>
<el-button size="mini" type="text" icon="el-icon-folder">归档</el-button>
<!-- 已归档项目 -->
<el-button size="mini" type="text" icon="el-icon-document">查看详情</el-button>
```
**表格组件代码**:
```vue
<el-table
v-loading="loading"
:data="projectList"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="项目名称" min-width="200" show-overflow-tooltip>
<template slot-scope="scope">
<div class="project-name">
<div class="name">{{ scope.row.projectName }}</div>
<div class="desc">{{ scope.row.projectDesc }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" width="120" align="center" />
<el-table-column label="状态" prop="projectStatus" width="100" align="center">
<template slot-scope="scope">
<el-tag :type="getStatusType(scope.row.projectStatus)" size="small">
{{ getStatusLabel(scope.row.projectStatus) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="目标人数" prop="targetCount" width="80" align="center" />
<el-table-column label="预警人数" width="100" align="center">
<template slot-scope="scope">
<span v-if="scope.row.projectStatus === '0'">
{{ scope.row.warningCount }}
<i class="el-icon-refresh" @click="refreshWarningCount(scope.row)"></i>
</span>
<span v-else>{{ scope.row.warningCount }}</span>
</template>
</el-table-column>
<el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope">
<!-- 根据状态显示不同按钮 -->
</template>
</el-table-column>
</el-table>
```
#### 4. 快捷入口区
**位置**: 表格下方
**布局**:
```
+------------------------------------------------------------+
| 快捷入口 |
+------------------------------------------------------------+
| ┌──────────┐ ┌──────────┐ ┌──────────┐ |
| │ 📋 导入 │ │ 📅 季度 │ │ 👤 新员工 │ |
| │ 历史项目 │ │ 初核 │ │ 排查 │ |
| └──────────┘ └──────────┘ └──────────┘ |
+------------------------------------------------------------+
```
**卡片样式**:
```css
.quick-entry-card {
width: 100%;
height: 120px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.3s;
}
.quick-entry-card:hover {
transform: translateY(-4px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
```
---
## 页面2: 新建项目弹窗
### 对话框规格
```
+------------------------------------------------------------------+
| ┌──────────────────────────────────────────────────────────┐ |
| │ 新建项目 │ |
| ├──────────────────────────────────────────────────────────┤ |
| │ 项目名称: [________________________] * │ |
| │ 项目描述: [________________________] │ |
| │ [________________________________] │ |
| │ │ |
| │ 目标人员: [+ 添加人员] │ |
| │ ┌─────────────────────────────────────────┐ │ |
| │ │ ✗ 张三 (3301**********202101) [删除] │ │ |
| │ │ ✗ 李四 (3302**********202102) [删除] │ │ |
| │ └─────────────────────────────────────────┘ │ |
| │ │ |
| │ 时间范围: │ |
| │ 开始日期: [2024-01-01 📅] │ |
| │ 结束日期: [2024-03-31 📅] │ |
| │ │ |
| │ 项目配置: [展开高级设置 ▼] │ |
| │ │ |
| ├──────────────────────────────────────────────────────────┤ |
| │ [取消] [确定] │ |
| └──────────────────────────────────────────────────────────┘ |
+------------------------------------------------------------------+
```
### 表单验证规则
```javascript
rules: {
projectName: [
{ required: true, message: '请输入项目名称', trigger: 'blur' },
{ min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
],
startDate: [
{ required: true, message: '请选择开始日期', trigger: 'change' }
],
endDate: [
{ required: true, message: '请选择结束日期', trigger: 'change' },
{
validator: (rule, value, callback) => {
if (value && this.form.startDate && value < this.form.startDate) {
callback(new Error('结束日期不能早于开始日期'));
} else {
callback();
}
},
trigger: 'change'
}
]
}
```
---
## 页面3: 导入历史项目弹窗
### 对话框规格
```
+------------------------------------------------------------------+
| ┌──────────────────────────────────────────────────────────┐ |
| │ 导入历史项目 │ |
| ├──────────────────────────────────────────────────────────┤ |
| │ 搜索: [________________] 📅 时间筛选 ▼ │ |
| │ │ |
| │ 历史项目列表: │ |
| │ ┌─────────────────────────────────────────────┐ │ |
| │ │ ☐ 2024年Q1初核 │ │ |
| │ │ 创建时间: 2024-01-01 人员: 500 [详情] │ │ |
| │ ├─────────────────────────────────────────────┤ │ |
| │ │ ☑ 2023年Q4初核 │ │ |
| │ │ 创建时间: 2023-10-01 人员: 480 [详情] │ │ |
| │ ├─────────────────────────────────────────────┤ │ |
| │ │ ☐ 2023年Q3初核 │ │ |
| │ │ 创建时间: 2023-07-01 人员: 450 [详情] │ │ |
| │ └─────────────────────────────────────────────┘ │ |
| │ │ |
| │ 新项目名称: [2024年Q2初核复制] * │ |
| │ 时间范围: │ |
| │ 开始: [2024-04-01] 结束: [2024-06-30] │ |
| │ │ |
| ├──────────────────────────────────────────────────────────┤ |
| │ [取消] [导入] │ |
| └──────────────────────────────────────────────────────────┘ |
+------------------------------------------------------------------+
```
### 项目列表项样式
```vue
<el-radio-group v-model="selectedProjectId">
<el-radio
v-for="item in historyProjects"
:key="item.projectId"
:label="item.projectId"
class="project-radio"
>
<div class="project-item">
<div class="project-header">
<span class="name">{{ item.projectName }}</span>
<el-button type="text" size="small" @click.stop="viewDetail(item)">
详情
</el-button>
</div>
<div class="project-info">
<span>创建时间: {{ item.createTime }}</span>
<span>人员: {{ item.targetCount }}</span>
<el-tag size="mini" :type="getStatusType(item.projectStatus)">
{{ getStatusLabel(item.projectStatus) }}
</el-tag>
</div>
</div>
</el-radio>
</el-radio-group>
```
---
## 页面4: 项目归档确认
### 确认对话框
```
+------------------------------------------------------------------+
| ┌──────────────────────────────────────────────────────────┐ |
| │ ⚠️ 归档确认 │ |
| ├──────────────────────────────────────────────────────────┤ |
| │ │ |
| │ 确定要归档项目"2024年Q1初核"吗? │ |
| │ │ |
| │ 归档后将: │ |
| │ ✓ 项目状态变为"已归档" │ |
| │ ✓ 自动生成项目报告PDF │ |
| │ ✓ 移入归档库 │ |
| │ │ |
| │ ☐ 同时删除项目相关数据(不可恢复) │ |
| │ │ |
| │ 归档后可从"归档库"中查看和恢复 │ |
| │ │ |
| ├──────────────────────────────────────────────────────────┤ |
| │ [取消] [确认归档] │ |
| └──────────────────────────────────────────────────────────┘ |
+------------------------------------------------------------------+
```
---
## 交互规范
### 1. 加载状态
- 首次加载显示骨架屏
- 数据刷新显示loading遮罩
- 按钮操作后显示loading状态
### 2. 空状态
```
+------------------------------------------------------------------+
| ┌──────────────────────────────────────────────────────────┐ |
| │ 📂 │ |
| │ │ |
| │ 暂无项目数据 │ |
| │ │ |
| │ [新建项目] [导入历史项目] │ |
| └──────────────────────────────────────────────────────────┘ |
+------------------------------------------------------------------+
```
### 3. 错误提示
- 表单验证错误:红色边框 + 错误文字
- 网络错误:全屏错误提示
- 操作失败:右上角消息提示
### 4. 成功反馈
- 操作成功:右上角成功消息
- 删除成功:列表自动刷新
- 创建成功:跳转到详情页
---
## 响应式设计
### 断点定义
| 设备类型 | 屏幕宽度 | 布局调整 |
|---------|---------|---------|
| 大屏 | ≥1920px | 显示完整表格 |
| 标准 | ≥1200px | 标准布局 |
| 平板 | ≥768px | 隐藏次要列 |
| 手机 | <768px | 卡片式布局 |
### 移动端适配
```css
/* 移动端使用卡片式布局 */
@media (max-width: 768px) {
.project-list-table {
display: none;
}
.project-list-cards {
display: block;
}
}
```
---
## 可访问性
### 键盘导航
- Tab: 在元素间切换焦点
- Enter: 确认/提交
- Esc: 关闭对话框
- Space: 选中/取消选中
### ARIA标签
```html
<!-- 搜索框 -->
<input
aria-label="搜索项目名称"
role="searchbox"
/>
<!-- 表格 -->
<table role="table" aria-label="项目列表">
<caption>当前共5个项目</caption>
...
</table>
```
---
## 设计资源
### Figma设计稿
如需查看详细的设计稿,请联系设计团队。
### 墨刀原型
[在线查看原型](演示模式.html)
### 图标库
使用Element UI内置图标文档https://element.eleme.io/#/zh-CN/component/icon
---
## 版本信息
- **设计版本**: V1.0
- **设计日期**: 2026-01-27
- **设计师**: 待定
- **基于框架**: Vue 2.6 + Element UI 2.15

View File

@@ -0,0 +1,240 @@
# 项目管理模块文档复核报告
## 复核概述
**复核日期**: 2026-01-27
**复核人**: Claude
**对比文档**:
- 原始需求: [纪检初核系统功能说明书V1.0](../../纪检初核系统功能说明书-V1.0.md) 第9-62行
- 分解文档: [01-项目管理模块.md](./01-项目管理模块.md)
## 复核结论
### ✅ 功能覆盖完整性: 100%
所有功能点均已覆盖,无遗漏。
## 详细对比分析
### 一、导航与搜索区
| 功能需求 | 文档覆盖 | 状态 | 说明 |
|---------|---------|------|------|
| 项目搜索(关键词模糊搜索) | ✅ 已覆盖 | 完整 | 1.1.1节详细说明 |
| 新建项目(标准表单) | ✅ 已覆盖 | 完整 | 1.1.2节详细说明 |
| 导入历史项目 | ✅ 已覆盖 | 完整 | 1.1.3节详细说明 |
### 二、项目列表区
#### 列表信息列
| 功能需求 | 文档覆盖 | 状态 | 说明 |
|---------|---------|------|------|
| 项目名称+简要描述 | ✅ 已覆盖 | 完整 | 1.2.1节说明 |
| 创建时间 | ✅ 已覆盖 | 完整 | 1.2.1节说明 |
| 状态标识(进行中、已完成) | ✅ 已覆盖 | 完整 | 1.2.2节详细说明 |
| 目标人数 | ✅ 已覆盖 | 完整 | 1.2.1节说明 |
| 预警人数(动态更新) | ✅ 已覆盖 | 完整 | 1.2.3节详细说明 |
#### 操作列
| 功能需求 | 文档覆盖 | 状态 | 说明 |
|---------|---------|------|------|
| 查看结果(已完成项目) | ✅ 已覆盖 | 完整 | 1.2.4节说明 |
| 重新分析(已完成项目) | ✅ 已覆盖 | 完整 | 1.2.5节详细说明 |
| 归档项目生成PDF | ✅ 已覆盖 | 完整 | 1.2.6节详细说明明确说明生成PDF |
| 进入项目(进行中项目) | ✅ 已覆盖 | 完整 | 1.2.7节说明 |
### 三、快捷入口区
| 功能需求 | 文档覆盖 | 状态 | 说明 |
|---------|---------|------|------|
| 导入历史项目 | ✅ 已覆盖 | 完整 | 1.3.1节详细说明 |
| 创建季度初核 | ✅ 已覆盖 | 完整 | 1.3.2节详细说明,包含季度时间规则 |
| 创建新员工排查 | ✅ 已覆盖 | 完整 | 1.3.3节详细说明,包含新员工定义 |
### 四、业务规则
| 业务需求 | 文档覆盖 | 状态 | 说明 |
|---------|---------|------|------|
| 项目状态流转 | ✅ 已覆盖 | 完整 | 业务规则节说明 |
| 预警人数实时更新 | ✅ 已覆盖 | 完整 | 业务规则节说明 |
| 归档条件 | ✅ 已覆盖 | 完整 | 业务规则节说明 |
| 重新分析条件 | ✅ 已覆盖 | 完整 | 业务规则节说明 |
## 发现的问题
### ⚠️ 问题1: 数据模型命名不符合若依框架规范
**问题描述**:
当前文档中的数据模型字段命名不符合若依框架的命名规范。
**当前定义**:
| 字段名 | 类型 |
|-------|------|
| projectId | Long |
| projectName | String |
| status | String |
| isArchived | Boolean |
**建议修改**(符合若依规范):
| 字段名 | 类型 | 说明 |
|-------|------|------|
| project_id | bigint(20) | 项目ID |
| project_name | varchar(100) | 项目名称 |
| project_desc | varchar(500) | 项目描述 |
| project_status | char(1) | 项目状态0进行中 1已完成 2已归档 |
| target_count | int(11) | 目标人数 |
| warning_count | int(11) | 预警人数 |
| archive_flag | char(1) | 归档标志0未归档 1已归档 |
| create_by | varchar(64) | 创建者 |
| create_time | datetime | 创建时间 |
| update_by | varchar(64) | 更新者 |
| update_time | datetime | 更新时间 |
| remark | varchar(500) | 备注 |
**影响**: 需要修改数据模型章节
### ⚠️ 问题2: 状态枚举值未明确定义
**问题描述**:
原文档中提到的状态包括"进行中"、"已完成",但在数据模型中未明确定义状态值的枚举。
**建议**:
```
project_status项目状态:
- 0: 进行中
- 1: 已完成
- 2: 已归档
```
### ⚠️ 问题3: 缺少原型图
**问题描述**:
原始需求中明确提到需要3个原型图
(1) 首页
(2) 新建项目弹窗页入口
(3) 导入历史项目弹窗页入口
当前文档在"页面原型"章节只是简单列出,没有详细的界面设计说明。
**建议**:
需要补充详细的UI原型设计包括
- 页面布局
- 组件位置
- 交互流程
- 状态变化
### ✅ 优点
1. **功能分解细致**: 将每个功能点分解到三级,便于开发理解
2. **交互说明完整**: 每个功能都包含详细的交互流程
3. **页面原型示例**: 提供了详细的表单和布局示例
4. **技术实现要点**: 包含了技术实现的关键点
5. **业务规则清晰**: 业务规则章节清晰定义了状态流转和约束条件
## 建议改进
### 1. 数据模型规范化
**优先级**: 高
建议创建独立的数据模型文档,符合若依框架规范:
```sql
-- 项目信息表
CREATE TABLE ccdi_project (
project_id bigint(20) not null auto_increment comment '项目ID',
project_name varchar(100) not null comment '项目名称',
project_desc varchar(500) default null comment '项目描述',
project_status char(1) default '0' comment '项目状态0进行中 1已完成 2已归档',
target_count int(11) default 0 comment '目标人数',
warning_count int(11) default 0 comment '预警人数',
start_date date default null comment '开始日期',
end_date date default null comment '结束日期',
archive_flag char(1) default '0' comment '归档标志0未归档 1已归档',
archive_time datetime default null comment '归档时间',
del_flag char(1) default '0' comment '删除标志0存在 2删除',
create_by varchar(64) default '' comment '创建者',
create_time datetime comment '创建时间',
update_by varchar(64) default '' comment '更新者',
update_time datetime comment '更新时间',
remark varchar(500) default null comment '备注',
primary key (project_id)
) engine=innodb auto_increment=1 comment = '核查项目表';
```
### 2. 补充原型图设计
**优先级**: 中
建议使用工具如Figma、Sketch、墨刀创建详细的UI原型图或使用ASCII/Mermaid图表展示界面布局。
### 3. 添加API接口定义
**优先级**: 中
建议为每个功能添加RESTful API接口定义包括
- 请求方法
- 请求路径
- 请求参数
- 响应格式
示例:
```
POST /ccdi/project/list
功能: 查询项目列表
请求参数:
{
"pageNum": 1,
"pageSize": 20,
"projectName": "关键词",
"projectStatus": "0"
}
响应:
{
"code": 200,
"msg": "查询成功",
"rows": [...],
"total": 100
}
```
## 总结
### 覆盖率统计
| 维度 | 覆盖率 | 状态 |
|-----|--------|------|
| 功能点覆盖 | 100% | ✅ 优秀 |
| 数据模型 | 80% | ⚠️ 需改进 |
| 业务规则 | 100% | ✅ 优秀 |
| 交互说明 | 100% | ✅ 优秀 |
| 技术实现 | 70% | ⚠️ 需补充 |
### 整体评价
**文档质量**: ⭐⭐⭐⭐ (4/5星)
**优点**:
- ✅ 功能分解完整且细致
- ✅ 交互流程清晰
- ✅ 业务规则明确
**需要改进**:
- ⚠️ 数据模型需符合若依框架规范
- ⚠️ 需要补充详细的UI原型图
- ⚠️ 建议添加API接口定义
### 下一步行动
1. **高优先级**: 修改数据模型,符合若依框架规范
2. **中优先级**: 补充UI原型设计
3. **中优先级**: 添加API接口定义
4. **低优先级**: 创建数据库设计文档和开发规范文档
## 复核签字
**复核人**: Claude (AI助手)
**复核日期**: 2026-01-27
**文档版本**: V1.0

View File

@@ -0,0 +1,143 @@
# 02.1-数据管理
## 模块概述
数据管理是项目工作台的核心模块之一,用于统一接入来自行内流水、征信数据、人工上传等不同来源和格式的数据,并自动化检查识别数据问题,保证后续风险识别的准确性。
## 模块结构
```
数据管理
├── 数据导入
└── 数据质量检查
```
## 功能分解
### 1.1 数据导入
**功能描述**: 提供多种数据源的导入功能,支持行内数据拉取和外部数据上传。
**功能点**:
- **拉取本行信息**: 输入证件号码或导入身份证号表格,自动拉取行内流水、资产等数据信息
- **他行流水导入**: 批量上传员工的他行银行、支付宝微信等交易流水文件支持Excel、文本型PDF系统自动解析提取交易金额、对手方、交易时间、余额、摘要等关键字段
- **征信信息导入**: 上传个人信用报告HTML格式系统自动解析提取信贷账户、负债总额、担保信息、查询记录等核心数据
- **员工家庭关系导入**: 上传员工的家庭成员信息,用于构建关系人图谱和关联分析
- **名单库选择**: 从"信息维护-中介库管理"内的名单中选择确认后的可疑名单
- **生成报告**: 生成初核结果,跳转至结果页
**数据要素**:
- 证件号码/身份证号
- 本行流水数据
- 他行流水文件
- 征信报告文件
- 员工家庭关系信息
- 名单库数据
### 1.2 数据质量检查
**功能描述**: 在数据导入后,系统自动执行预定义的质量规则对数据集进行检查。
**功能点**:
- **质量规则执行**: 自动执行数据质量检查规则
- **检查结果展示**: 以列表形式展示发现的具体问题
- 数据格式不一致(如日期格式不统一、金额单位混杂)
- 余额链条性异常(相邻交易记录间的余额计算逻辑断裂)
- 缺失关键字段(如交易记录缺少对手方账号或户名)
- **质量评分仪表盘**: 通过三个关键指标量化数据质量
- 数据完整性(衡量必填字段的填充率)
- 格式一致性(衡量数据遵循预定格式规范的程度)
- 余额连续性(衡量流水数据中余额连续、计算正确的程度)
**数据要素**:
- 数据完整性评分
- 格式一致性评分
- 余额连续性评分
- 异常记录详情
## 数据模型
### 数据导入记录 (DataImport)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| importId | Long | 导入ID | 是 |
| projectId | Long | 项目ID | 是 |
| importType | String | 导入类型(本行/他行/征信/家庭关系) | 是 |
| importTime | DateTime | 导入时间 | 是 |
| importStatus | String | 导入状态 | 是 |
| fileCount | Integer | 文件数量 | 否 |
| recordCount | Integer | 记录数量 | 否 |
### 数据质量检查结果 (DataQualityCheck)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| checkId | Long | 检查ID | 是 |
| projectId | Long | 项目ID | 是 |
| completenessScore | Double | 数据完整性评分 | 是 |
| consistencyScore | Double | 格式一致性评分 | 是 |
| continuityScore | Double | 余额连续性评分 | 是 |
| formatIssueCount | Integer | 格式不一致数量 | 是 |
| balanceIssueCount | Integer | 余额链条性异常数量 | 是 |
| missingFieldCount | Integer | 缺失关键字段数量 | 是 |
| checkTime | DateTime | 检查时间 | 是 |
## 支持的文件格式
| 数据类型 | 支持格式 | 解析方式 |
|---------|---------|---------|
| 他行流水 | Excel、文本型PDF | 自动解析 |
| 征信报告 | HTML | 自动解析 |
| 身份证号表格 | Excel | 导入读取 |
| 员工家庭关系 | Excel | 导入读取 |
## 业务规则
1. **数据导入顺序**: 建议先拉取本行信息,再导入他行流水和征信信息
2. **质量检查触发**: 数据导入完成后自动触发质量检查
3. **质量评分计算**:
- 数据完整性 = (已填充必填字段数 / 应填必填字段数) × 100%
- 格式一致性 = (格式正确记录数 / 总记录数) × 100%
- 余额连续性 = (余额计算正确记录数 / 总记录数) × 100%
4. **异常数据处理**: 发现异常需要用户确认后才能生成报告
## 页面原型
### 1. 数据导入页面
- 数据源选择区
- 文件上传区
- 导入进度展示
### 2. 数据质量检查页面
- 质量评分仪表盘
- 异常记录列表
- 异常详情展示
## 交互关系
| 关联模块 | 交互说明 |
|---------|---------|
| 初核结果总览 | 点击"生成报告"跳转到初核结果总览页 |
| 信息维护模块 | 从"中介库管理"选择名单 |
| 专项排查 | 导入的数据用于专项排查分析 |
## 异常处理
| 异常类型 | 处理方式 |
|---------|---------|
| 文件格式不支持 | 提示用户支持的格式,拒绝导入 |
| 数据解析失败 | 记录失败原因,提示用户检查文件 |
| 质量检查失败 | 展示异常详情,允许用户修正后重新导入 |
| 余额计算异常 | 标注异常记录,提示数据可能缺失或被篡改 |
## 功能点统计
- 二级功能: 2个
- 三级功能点: 10个
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 第73-118行

View File

@@ -0,0 +1,235 @@
# 02.2-初核结果总览
## 模块概述
初核结果总览模块展示项目中上传的数据经过模型识别出的风险信息总览及明细,包括风险总览、风险模型、风险明细三个主要部分。
## 模块结构
```
初核结果总览
├── 风险总览
│ ├── 风险全局仪表盘
│ ├── 高风险/中风险人员名单
│ └── 单个风险人员详情
├── 风险模型
│ ├── 模型触发情况总计
│ └── 各模型触发人员列表
└── 风险明细
├── 涉疑交易明细表
├── 涉及违法人员清单表
└── 异常账户清单表
```
## 功能分解
### 2.1 风险总览
**功能描述**: 以数据卡片和列表形式集中展示项目整体风险态势。
**功能点**:
- **风险全局仪表盘**: 展示项目整体风险数据卡片
- 总人数(项目覆盖的员工总数)
- 无预警人数
- 低风险人数
- 中风险人数
- 高风险人数
- **高风险/中风险人员名单**: 按风险评分降序排列
- 显示姓名、身份证号、部门、风险评分、触发模型数、核心异常点
- 高风险人员全部展示
- 中风险人员展示评分最高的10名
- **查看单个风险人员详情**: 钻取至单个员工的全面风险报告
- 所有异常行为列表
- 每个行为对应的模型判断依据(规则)
- 资产分析
- 征信概览
- 关系人图谱
- 针对可疑交易及可疑对象手动添加至关注方
- **批量操作**:
- 批量生成报告
- 批量导出证据
- 批量添加到关注列表
- 添加到案例库
**数据要素**:
- 总人数
- 各风险等级人数
- 人员详细信息
- 风险评分
- 触发模型数
- 核心异常点
### 2.2 风险模型
**功能描述**: 展示所有风险模型的整体触发情况和触发人员列表。
**功能点**:
- **模型触发情况总计**: 以表格形式展示
- 模型名称
- 触发总人数
- 主要触发人员示例
- 点击"查看详情"跳转至触发该模型的全体人员列表
- **各模型触发人员列表**: 支持多维度筛选
- 下拉菜单选择触发某一特定风险模型
- 筛选同时触发多个如2个以上风险模型的高风险人员
- 搜索人员姓名或工号
- 将常用筛选组合保存为固定策略
- 点击【查看详情】查看该员工详细的风险情况
**数据要素**:
- 模型名称
- 触发人数
- 触发人员列表
- 筛选策略配置
### 2.3 风险明细
**功能描述**: 展示涉疑交易、违法人员、异常账户等详细风险信息。
**功能点**:
- **涉疑交易明细表**:
- 支持按「全部可疑人员类型」「名单库命中」「模型规则命中」等维度筛选
- 支持穿透式查看交易流水
- 显示交易时间、可疑人员、关联人、关联员工、关系、摘要、交易类型、交易金额
- 点击「查看详情」跳转至可疑流水详情页
- **涉及违法人员清单表**:
- 展示外部违法名单库命中的人员信息
- 显示违法人员姓名、身份证号、是否为失信被执行人、是否有刑事判决记录、是否有行政处罚记录、是否涉及公安案件、是否被限制高消费、违法信息更新时间
- 点击「查看详情」展示该人员的违法详情、更新日期等完整背景信息
- **异常账户清单表**:
- 独立列出经模型识别出的所有异常账户
- 显示账号、开户人、银行、异常类型、异常发生时间、状态
- 点击「查看详情」查看该账号的所有异常交易明细
- **批量导出数据及报告**:
- 导出所有列表为Excel
- 一键生成项目多维统计报告PDF/Word
- 模型触发排行、部门风险分布、风险评分区间等多维度分析
**数据要素**:
- 交易流水详情
- 违法人员信息
- 异常账户信息
- 导出配置
## 数据模型
### 风险人员 (RiskPerson)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| personId | Long | 人员ID | 是 |
| projectId | Long | 项目ID | 是 |
| name | String | 姓名 | 是 |
| idCard | String | 身份证号 | 是 |
| department | String | 部门 | 否 |
| riskScore | Double | 风险评分 | 是 |
| riskLevel | String | 风险等级 | 是 |
| triggerModelCount | Integer | 触发模型数 | 是 |
| coreAnomaly | String | 核心异常点 | 否 |
### 风险模型 (RiskModel)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| modelId | Long | 模型ID | 是 |
| modelName | String | 模型名称 | 是 |
| triggerCount | Integer | 触发总人数 | 是 |
| modelType | String | 模型类型 | 是 |
### 涉疑交易 (SuspiciousTransaction)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| transactionId | Long | 交易ID | 是 |
| transactionTime | DateTime | 交易时间 | 是 |
| suspiciousPerson | String | 可疑人员 | 是 |
| relatedPerson | String | 关联人 | 是 |
| relatedEmployee | String | 关联员工 | 是 |
| relation | String | 关系 | 是 |
| summary | String | 摘要 | 否 |
| transactionType | String | 交易类型 | 是 |
| amount | BigDecimal | 交易金额 | 是 |
### 违法人员 (IllegalPerson)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| personId | Long | 人员ID | 是 |
| name | String | 姓名 | 是 |
| idCard | String | 身份证号 | 是 |
| isDishonestExecutor | Boolean | 是否失信被执行人 | 是 |
| hasCriminalJudgment | Boolean | 是否刑事判决 | 是 |
| hasAdministrativePenalty | Boolean | 是否行政处罚 | 是 |
| hasPublicSecurityCase | Boolean | 是否公安涉案 | 是 |
| isConsumptionRestricted | Boolean | 是否限制高消费 | 是 |
| updateTime | DateTime | 违法信息更新时间 | 是 |
### 异常账户 (AbnormalAccount)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| accountId | Long | 账户ID | 是 |
| accountNo | String | 账号 | 是 |
| accountHolder | String | 开户人 | 是 |
| bank | String | 银行 | 是 |
| abnormalType | String | 异常类型 | 是 |
| abnormalTime | DateTime | 异常发生时间 | 是 |
| status | String | 状态 | 是 |
## 风险等级定义
| 等级 | 评分范围 | 说明 |
|-----|---------|-----|
| 无风险 | 0 | 未触发任何风险模型 |
| 低风险 | 1-40 | 触发少量风险模型,风险较低 |
| 中风险 | 41-70 | 触发多个风险模型,需要关注 |
| 高风险 | 71-100 | 触发多个高风险模型,需要重点核查 |
## 业务规则
1. **风险评分计算**: 基于触发的风险模型数量和严重程度计算
2. **人员名单排序**: 按风险评分降序排列
3. **模型触发统计**: 实时统计各模型的触发情况
4. **批量操作**: 支持多选人员进行批量操作
## 页面原型
### 1. 风险总览页面
- 风险仪表盘
- 人员名单列表
- 批量操作按钮
### 2. 风险模型页面
- 模型触发情况表
- 筛选条件区
- 人员列表
### 3. 风险明细页面
- 涉疑交易明细表
- 违法人员清单表
- 异常账户清单表
### 4. 人员详情页面
- 异常明细列表
- 资产分析图表
- 征信摘要
- 关系人图谱
## 交互关系
| 关联模块 | 交互说明 |
|---------|---------|
| 数据管理 | 使用导入的数据进行风险分析 |
| 专项排查 | 从人员详情跳转到专项排查 |
| 流水明细查询 | 从交易详情跳转到流水查询 |
## 功能点统计
- 二级功能: 3个
- 三级功能点: 16个
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 第119-262行

View File

@@ -0,0 +1,242 @@
# 02.3-专项排查
## 模块概述
专项排查模块针对单人用户进行深度调查,包括员工详查分析、图谱分析和拓展查询等功能。
## 模块结构
```
专项排查
├── 员工详查分析
├── 图谱分析
│ ├── 关系人图谱
│ ├── 资金流图谱
│ └── 实控账户图谱
└── 拓展查询
├── 采购查询
├── 人员调动查询
└── 招聘查询
```
## 功能分解
### 3.1 员工详查分析
**功能描述**: 针对单个目标员工进行深度调查分析。
**功能点**:
- **输入查询条件**: 输入目标员工的身份证号,可选择自定义时间范围
- **收入资产负债分析**: 根据检查对象及其主要家庭成员(配偶等),根据收入、资产、负债三者的关系进行初核判断
- **风险结果判断**:
- 正常
- 收入+负债远低于资产
- 收入+负债远高于资产
- 其他风险提示
**数据要素**:
- 员工身份证号
- 时间范围
- 收入数据
- 资产数据
- 负债数据
- 家庭成员信息
### 3.2 图谱分析
**功能描述**: 通过图形化方式,揭示隐藏的人员与资金关系网络。
**功能点**:
- **关系人图谱**:
- 通过身份证号等信息筛选展示以该员工为中心的社会关系网络
- 展示家庭成员、密切关联人
- 点击节点查看详情
- 点击关联企业穿透查询企业下的法人、股东等信息
- **资金流图谱**:
- 针对个人的资金流向进行分析
- 对可疑资金向前追溯多层交易对手
- 支持手工加入资金流向节点
- 支持备注资金流向
- **实控账户图谱**:
- 输入身份证号生成该员工实际控制的账户网络图
- 实控账户可能非本人名下
- 排查逻辑:基于手机登录丰收互联次数、线下多次代理存取等进行判断
**数据要素**:
- 身份证号
- 社会关系数据
- 资金流向数据
- 账户控制关系数据
- 企业关联数据
### 3.3 拓展查询
**功能描述**: 提供采购、人员调动、招聘等多维度的查询功能。
**功能点**:
- **采购查询**:
- 筛选查询采购时段
- 选择关联员工
- 查询其参与的所有采购
- 清单包含:采购事项名称、交易日期、采购金额、供应商名称、关联员工
- 支持穿透展示采购全量信息(采购方式、入围/中标公司、经办人、对方账号等)
- **人员调动查询**:
- 查询员工的岗位/机构调动记录
- 辅助排查"异常调动、岗位晋升合规性"
- 可选择查询时间和员工姓名
- 包含:姓名、工号、调动时间、原/现岗位、原/现机构、调动原因
- **招聘查询**:
- 查询招聘事项信息
- 辅助排查"招聘流程合规性、面试官关联风险"
- 可筛选查询时间段和员工姓名
- 包含:招聘人员、岗位、招聘时间、关联面试官、面试结果
**数据要素**:
- 采购信息
- 人员调动记录
- 招聘信息
## 数据模型
### 员工详查记录 (EmployeeDetailCheck)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| checkId | Long | 检查ID | 是 |
| personId | String | 身份证号 | 是 |
| timeRangeStart | Date | 时间范围开始 | 是 |
| timeRangeEnd | Date | 时间范围结束 | 是 |
| income | BigDecimal | 收入 | 否 |
| assets | BigDecimal | 资产 | 否 |
| liabilities | BigDecimal | 负债 | 否 |
| checkResult | String | 检查结果 | 是 |
| checkTime | DateTime | 检查时间 | 是 |
### 关系人图谱节点 (RelationshipGraphNode)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| nodeId | Long | 节点ID | 是 |
| nodeType | String | 节点类型(人员/企业/账户) | 是 |
| nodeName | String | 节点名称 | 是 |
| nodeInfo | String | 节点详细信息JSON | 否 |
### 关系人图谱边 (RelationshipGraphEdge)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| edgeId | Long | 边ID | 是 |
| sourceNodeId | Long | 源节点ID | 是 |
| targetNodeId | Long | 目标节点ID | 是 |
| relationType | String | 关系类型 | 是 |
| relationInfo | String | 关系详细信息 | 否 |
### 采购记录 (ProcurementRecord)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| procurementId | Long | 采购ID | 是 |
| procurementName | String | 采购事项名称 | 是 |
| transactionDate | Date | 交易日期 | 是 |
| procurementAmount | BigDecimal | 采购金额 | 是 |
| supplierName | String | 供应商名称 | 是 |
| relatedEmployee | String | 关联员工 | 是 |
| procurementMethod | String | 采购方式 | 否 |
| winningCompany | String | 入围/中标公司 | 否 |
| operator | String | 经办人 | 否 |
| targetAccount | String | 对方账号 | 否 |
### 人员调动记录 (PersonnelTransfer)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| transferId | Long | 调动ID | 是 |
| name | String | 姓名 | 是 |
| employeeId | String | 工号 | 是 |
| transferTime | DateTime | 调动时间 | 是 |
| originalPosition | String | 原岗位 | 是 |
currentPosition | String | 现岗位 | 是 |
| originalOrganization | String | 原机构 | 是 |
| currentOrganization | String | 现机构 | 是 |
| transferReason | String | 调动原因 | 否 |
### 招聘记录 (RecruitmentRecord)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| recruitmentId | Long | 招聘ID | 是 |
| recruitPerson | String | 招聘人员 | 是 |
| position | String | 岗位 | 是 |
| recruitmentTime | DateTime | 招聘时间 | 是 |
| relatedInterviewer | String | 关联面试官 | 是 |
| interviewResult | String | 面试结果 | 是 |
## 图谱分析说明
### 关系人图谱
- **中心节点**: 查询的员工
- **一级关联**: 配偶、父母、子女等家庭成员
- **二级关联**: 密切关联人、关联企业
- **企业穿透**: 法人、股东、高管等信息
### 资金流图谱
- **流向追溯**: 向前追溯多层交易对手
- **可疑资金标记**: 高亮显示可疑交易路径
- **手工标注**: 支持用户添加节点和备注
### 实控账户图谱
- **判断依据**:
- 手机登录丰收互联次数
- 线下多次代理存取记录
- 交易行为模式分析
- **账户类型**: 本人账户、亲属账户、其他关联账户
## 业务规则
1. **员工详查分析**:
- 正常: 收入 + 负债 ≈ 资产误差±20%以内)
- 收入+负债远低于资产: 资产来源可疑
- 收入+负债远高于资产: 可能存在隐瞒资产
2. **图谱分析**:
- 最多展示3层关联关系
- 单个节点最多展示100个关联节点
3. **拓展查询**:
- 支持模糊搜索
- 支持多条件组合筛选
## 页面原型
### 1. 员工详查分析页面
- 查询条件输入区
- 收入资产负债对比表
- 风险结果展示区
### 2. 图谱分析页面
- 查询输入区
- 图谱可视化区域
- 节点详情面板
- 操作工具栏
### 3. 拓展查询页面
- 查询条件区
- 结果列表
- 详情展示区
## 交互关系
| 关联模块 | 交互说明 |
|---------|---------|
| 初核结果总览 | 从人员详情跳转到专项排查 |
| 数据管理 | 使用导入的数据进行分析 |
| 流水明细查询 | 从资金流图谱跳转到流水查询 |
## 功能点统计
- 二级功能: 3个
- 三级功能点: 10个
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 第263-328行

View File

@@ -0,0 +1,200 @@
# 02.4-流水明细查询
## 模块概述
流水明细查询模块对拉取的本行流水以及上传的他行流水进行批量分析,提供多账户流水合并和二次分析功能。
## 模块结构
```
流水明细查询
├── 多账户流水明细合并
└── 全量流水二次分析
```
## 功能分解
### 4.1 多账户流水明细合并
**功能描述**: 将多个银行的流水合并成一个流水文件进行统一查询和分析。
**功能点**:
- **流水合并**: 将多个银行的流水数据合并为一个统一的数据集
- **账号筛选**: 左侧筛选区可筛选账号和银行进行查询
- **自主排序**: 主页面可选择按交易金额、交易时间等自主排序
- **对手方分析**: 支持切换对手方分析视图
**数据要素**:
- 账号
- 银行名称
- 交易时间
- 交易金额
- 交易类型
- 对手方信息
- 余额
### 4.2 全量流水二次分析
**功能描述**: 对全量流水表中的关键流水进行手工提交分析,实现重点流水的深入分析。
**功能点**:
- **加入分析**: 对关键流水进行手工提交"加入分析"操作
- **新建交易表**: 将关键流水重新放置在一个新的交易表中进行分析
- **独立分析**: 新的交易表独立于原流水表,支持单独的操作和分析
**数据要素**:
- 选中的流水记录
- 新建的交易表
- 分析结果
## 数据模型
### 流水记录 (TransactionRecord)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| transactionId | Long | 交易ID | 是 |
| projectId | Long | 项目ID | 是 |
| accountNo | String | 账号 | 是 |
| bankName | String | 银行名称 | 是 |
| transactionTime | DateTime | 交易时间 | 是 |
| transactionType | String | 交易类型 | 是 |
| amount | BigDecimal | 交易金额 | 是 |
| balance | BigDecimal | 余额 | 是 |
| counterparty | String | 对手方 | 否 |
| summary | String | 摘要 | 否 |
| dataSource | String | 数据来源(本行/他行) | 是 |
### 二次分析表 (SecondaryAnalysisTable)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| tableId | Long | 分析表ID | 是 |
| projectId | Long | 项目ID | 是 |
| tableName | String | 分析表名称 | 是 |
| createTime | DateTime | 创建时间 | 是 |
| transactionCount | Integer | 流水数量 | 是 |
### 二次分析流水关联 (SecondaryAnalysisTransaction)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| id | Long | 关联ID | 是 |
| tableId | Long | 分析表ID | 是 |
| transactionId | Long | 交易ID | 是 |
| addTime | DateTime | 添加时间 | 是 |
## 页面布局
### 流水明细查询页面
```
+----------------------------------+
| 流水明细查询 |
+----------------------------------+
| 筛选区 | 流水列表区 |
| | |
| 账号: [▼] | 交易时间 | 金额 | |
| 银行: [▼] | 2024-01-15| 5000 | |
| | 2024-01-14| 3000 | |
| 排序: [▼] | 2024-01-13| 2000 | |
| | |
| [加入分析] | |
+----------------------------------+
```
## 业务规则
1. **流水合并规则**:
- 同一账号的流水按时间顺序排列
- 不同账号的流水保持独立,在合并表中通过账号/银行字段区分
- 支持的最大账号数量: 100个
2. **排序规则**:
- 按交易时间排序(升序/降序)
- 按交易金额排序(升序/降序)
- 支持多字段组合排序
3. **二次分析规则**:
- 单个分析表最多包含10000条流水记录
- 同一流水记录可以加入多个分析表
- 分析表支持导出和删除操作
## 操作流程
### 流水查询流程
```
1. 选择账号/银行
2. 选择排序方式
3. 查看流水列表
4. 切换对手方分析(可选)
5. 选中关键流水
6. 点击"加入分析"
```
### 二次分析流程
```
1. 在全量流水表中选中关键流水
2. 点击"加入分析"
3. 创建或选择目标分析表
4. 流水添加到分析表
5. 在新分析表中进行独立分析
```
## 页面原型
### 1. 流水明细查询页面
- 左侧筛选区(账号、银行、排序)
- 右侧流水列表区
- 对手方分析切换按钮
- 批量操作区
### 2. 二次分析表页面
- 分析表列表
- 流水明细
- 统计分析
- 导出功能
## 交互关系
| 关联模块 | 交互说明 |
|---------|---------|
| 数据管理 | 使用导入的流水数据 |
| 初核结果总览 | 从交易详情跳转到流水查询 |
| 专项排查 | 从资金流图谱跳转到流水查询 |
## 功能特性
### 多账户流水合并
- 支持跨银行流水统一查询
- 支持多种排序方式
- 支持对手方分析视图切换
- 支持流水数据导出
### 全量流水二次分析
- 灵活的手工选择机制
- 独立的分析空间
- 支持多个分析表并行工作
- 支持分析结果导出
## 功能点统计
- 二级功能: 2个
- 三级功能点: 4个
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 第315-328行

View File

@@ -0,0 +1,115 @@
# 02-项目工作台
## 模块概述
项目工作台是系统的核心业务模块,用户从项目列表点击"进入项目"后进入该模块。工作台涵盖从数据准备到风险识别的全流程,通过侧边导航栏实现各功能模块间的切换。
## 模块结构
```
项目工作台
├── 02.1-数据管理
├── 02.2-初核结果总览
├── 02.3-专项排查
└── 02.4-流水明细查询
```
## 侧边导航栏
**功能描述**: 提供项目工作台内各功能模块的导航和状态展示。
**功能点**:
- **返回项目列表**: 返回当前项目的上一层列表页
- **项目状态标识**: 明确标识当前项目阶段(进行中/已完成)
- **最后更新时间**: 显示数据或项目状态的最后变更时间,用于判断信息时效性
## 子模块说明
### 02.1-数据管理
数据管理是进入具体项目后的核心工作台之一,将来自行内流水、征信数据、人工上传不同来源和格式的数据,在一个界面内完成统一接入,并自动化检查识别数据问题。
**主要功能**:
- 数据导入(本行信息、他行流水、征信信息、员工家庭关系、名单库)
- 数据质量检查
**功能点数**: 10个
**文档链接**: [02.1-数据管理.md](./02.1-数据管理.md)
### 02.2-初核结果总览
初核结果总览展示项目中上传的数据经过模型识别出的风险信息总览及明细。
**主要功能**:
- 风险总览(仪表盘、人员名单、人员详情)
- 风险模型(模型触发情况、模型触发人员列表)
- 风险明细(涉疑交易明细、违法人员清单、异常账户清单)
**功能点数**: 16个
**文档链接**: [02.2-初核结果总览.md](./02.2-初核结果总览.md)
### 02.3-专项排查
专项排查针对单人用户进行深度调查和分析。
**主要功能**:
- 员工详查分析
- 图谱分析(关系人图谱、资金流图谱、实控账户图谱)
- 拓展查询(采购查询、人员调动查询、招聘查询)
**功能点数**: 10个
**文档链接**: [02.3-专项排查.md](./02.3-专项排查.md)
### 02.4-流水明细查询
流水明细查询对拉取的本行流水以及上传的他行流水进行批量分析。
**主要功能**:
- 多账户流水明细合并
- 全量流水二次分析
**功能点数**: 4个
**文档链接**: [02.4-流水明细查询.md](./02.4-流水明细查询.md)
## 业务流程
```
进入项目工作台
[数据管理] - 导入数据、质量检查
[生成报告] - 运行风险模型
[初核结果总览] - 查看风险分析结果
[专项排查] - 针对性深度调查
[流水明细查询] - 流水数据二次分析
```
## 功能点统计
| 子模块 | 功能点数量 |
|-------|----------|
| 02.1-数据管理 | 10 |
| 02.2-初核结果总览 | 16 |
| 02.3-专项排查 | 10 |
| 02.4-流水明细查询 | 4 |
| **合计** | **40** |
## 交互关系
| 关联模块 | 交互说明 |
|---------|---------|
| 项目管理模块 | 从项目列表进入,返回项目列表 |
| 各子模块 | 通过侧边导航栏切换 |
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 第63-328行

View File

@@ -0,0 +1,207 @@
# 03-信息维护模块
## 模块概述
信息维护模块用于建立和维护系统所需的基础数据,包括中介库管理、员工信息管理和信贷客户家庭关系维护。
## 模块结构
```
信息维护模块
├── 中介库管理
├── 员工信息管理
└── 信贷客户家庭关系维护
```
## 功能分解
### 3.1 中介库管理
**功能描述**: 建立并维护外部中介人员/机构黑名单库。
**功能点**:
- **名单导入**: 支持Excel批量导入中介名单
- **名单维护**: 对中介名单进行增、删、改、查操作
- **名单查询**: 支持按姓名、身份证号、机构名称等条件查询
- **名单选择**: 在项目工作台中从中介库选择确认后的可疑名单
- **自动预警**: 当员工交易对手命中该库时,系统自动产生高风险预警
**数据要素**:
- 中介人员姓名
- 身份证号
- 中介机构名称
- 统一社会信用代码
- 风险等级
- 备注
### 3.2 员工信息管理
**功能描述**: 对员工实控账户、实控手机号、关系人信息等进行批量维护。
**功能点**:
- **实控账户维护**: 维护员工实际控制的账户信息(可能非本人名下)
- **实控手机号维护**: 维护员工实际使用的手机号信息
- **关系人信息维护**: 维护员工的关系人信息(未在户口本上的特定关系人等)
- **批量导入**: 支持Excel批量导入员工附属信息
- **信息查询**: 支持按员工姓名、工号等条件查询
- **信息编辑**: 对员工附属信息进行编辑和更新
**数据要素**:
- 员工姓名
- 工号
- 实控账户信息
- 实控手机号
- 关系人信息
- 关系类型
### 3.3 信贷客户家庭关系维护
**功能描述**: 上传并维护信贷客户家庭关系表格信息。
**功能点**:
- **家庭关系导入**: 上传信贷客户家庭关系表格
- **家庭关系维护**: 对家庭关系信息进行增、删、改、查操作
- **关系查询**: 支持按客户姓名、身份证号等条件查询家庭关系
- **关系展示**: 以树形结构展示家庭成员关系
**数据要素**:
- 客户姓名
- 身份证号
- 家庭成员姓名
- 家庭成员身份证号
- 关系类型(配偶、父母、子女等)
- 关系说明
## 数据模型
### 中介库 (IntermediaryBlacklist)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| intermediaryId | Long | 中介ID | 是 |
| name | String | 姓名/机构名称 | 是 |
| idCard | String | 身份证号/统一社会信用代码 | 否 |
| intermediaryType | String | 中介类型(个人/机构) | 是 |
| riskLevel | String | 风险等级 | 是 |
| remarks | String | 备注 | 否 |
| createTime | DateTime | 创建时间 | 是 |
| updateTime | DateTime | 更新时间 | 是 |
| status | String | 状态(有效/失效) | 是 |
### 员工附属信息 (EmployeeAdditionalInfo)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| infoId | Long | 信息ID | 是 |
| employeeId | String | 员工工号 | 是 |
| employeeName | String | 员工姓名 | 是 |
| infoType | String | 信息类型(实控账户/实控手机号/关系人) | 是 |
| infoContent | String | 信息内容JSON格式 | 是 |
| source | String | 信息来源 | 否 |
| createTime | DateTime | 创建时间 | 是 |
| updateTime | DateTime | 更新时间 | 是 |
### 信贷客户家庭关系 (CreditCustomerFamilyRelation)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| relationId | Long | 关系ID | 是 |
| customerName | String | 客户姓名 | 是 |
| customerIdCard | String | 客户身份证号 | 是 |
| familyMemberName | String | 家庭成员姓名 | 是 |
| familyMemberIdCard | String | 家庭成员身份证号 | 是 |
| relationType | String | 关系类型 | 是 |
| relationDescription | String | 关系说明 | 否 |
| createTime | DateTime | 创建时间 | 是 |
| updateTime | DateTime | 更新时间 | 是 |
## 中介类型分类
| 类型 | 说明 |
|-----|------|
| 个人中介 | 个人身份的中介人员 |
| 机构中介 | 中介公司、机构等 |
## 关系类型分类
| 关系类型 | 说明 |
|---------|------|
| 配偶 | 合法配偶关系 |
| 父母 | 父母、公婆、岳父母 |
| 子女 | 子女、儿媳、女婿 |
| 兄弟姐妹 | 兄弟姐妹关系 |
| 其他 | 其他社会关系 |
## 业务规则
1. **中介库管理**:
- 中介信息删除前需要确认未在项目中使用
- 支持Excel导入批量更新
- 导入时需要验证数据格式正确性
2. **员工信息管理**:
- 实控账户需要说明判断依据
- 实控手机号需要验证有效性
- 关系人信息需要注明关系类型
3. **信贷客户家庭关系维护**:
- 家庭关系需要双向维护A-B和B-A
- 支持家庭关系图的展示
## 页面原型
### 1. 中介库管理页面
- 名单列表
- 搜索筛选区
- 导入/导出按钮
- 新增/编辑/删除操作
### 2. 员工信息管理页面
- 员工列表
- 信息类型切换(实控账户/实控手机号/关系人)
- 信息详情展示
- 编辑操作
### 3. 信贷客户家庭关系维护页面
- 客户列表
- 家庭关系树形展示
- 关系维护操作
- 导入功能
## 交互关系
| 关联模块 | 交互说明 |
|---------|---------|
| 数据管理 | 从中介库选择名单用于项目分析 |
| 专项排查 | 使用员工信息进行关联分析 |
## 功能特性
### 中介库管理
- 支持Excel批量导入导出
- 支持多条件组合查询
- 自动风险预警机制
- 名单状态管理
### 员工信息管理
- 支持多种信息类型维护
- 支持批量导入更新
- 信息变更历史记录
- 信息有效性验证
### 信贷客户家庭关系维护
- 支持家庭关系可视化
- 支持Excel批量导入
- 关系双向维护
- 关系图谱展示
## 功能点统计
- 二级功能: 3个
- 三级功能点: 6个
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 第330-345行

View File

@@ -0,0 +1,203 @@
# 04-参数配置模块
## 模块概述
参数配置模块用于风险模型参数的管理,提供风险模型核心参数的维护界面和阈值规则细化功能。
## 模块结构
```
参数配置模块
├── 大额交易模型
├── 可疑兼职模型
└── 可疑外汇交易模型
```
## 功能分解
### 4.1 大额交易模型
**功能描述**: 识别大额/高频资金交易,监测调整单笔交易额、频繁转账次数等阈值。
**功能点**:
- **单笔交易额阈值**: 设置单笔交易金额阈值,超过该金额触发预警
- **频繁转账次数阈值**: 设置一定时间内的转账次数阈值
- **交易时间范围**: 设置监测的时间范围(日/周/月)
- **参数保存**: 保存配置的阈值参数
- **恢复默认**: 恢复系统默认的阈值参数
**数据要素**:
- 单笔交易额阈值
- 频繁转账次数阈值
- 时间范围
- 监测周期
### 4.2 可疑兼职模型
**功能描述**: 识别异常额外收入,监测调整月度固定收入、固定对手转入等阈值。
**功能点**:
- **月度固定收入阈值**: 设置月度固定收入上限,超过触发预警
- **固定对手转入阈值**: 设置从固定对手方转入的金额和频率阈值
- **异常收入识别规则**: 配置异常收入的识别规则
- **参数保存**: 保存配置的阈值参数
- **恢复默认**: 恢复系统默认的阈值参数
**数据要素**:
- 月度固定收入阈值
- 固定对手转入金额阈值
- 固定对手转入频率阈值
- 收入来源类型
### 4.3 可疑外汇交易模型
**功能描述**: 识别异常外汇收支,监测调整单笔购汇金额、频繁外汇交易次数等阈值。
**功能点**:
- **单笔购汇金额阈值**: 设置单笔购汇金额阈值
- **单笔结汇金额阈值**: 设置单笔结汇金额阈值
- **跨境汇款金额阈值**: 设置单笔跨境汇款金额阈值
- **月度购汇总额阈值**: 设置月度累计购汇总额阈值
- **月度结汇总额阈值**: 设置月度累计结汇总额阈值
- **频繁外汇交易次数阈值**: 设置单日外汇交易次数阈值
- **参数保存**: 保存配置的阈值参数
- **恢复默认**: 恢复系统默认的阈值参数
**数据要素**:
- 单笔购汇金额阈值(美元/笔)
- 单笔结汇金额阈值(美元/笔)
- 跨境汇款金额阈值(美元/笔)
- 月度购汇总额阈值(美元/月)
- 月度结汇总额阈值(美元/月)
- 频繁外汇交易次数阈值(次/日)
## 数据模型
### 模型参数配置 (ModelParameterConfig)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| configId | Long | 配置ID | 是 |
| modelType | String | 模型类型 | 是 |
| parameterName | String | 参数名称 | 是 |
| parameterCode | String | 参数编码 | 是 |
| parameterValue | String | 参数值 | 是 |
| unit | String | 单位 | 否 |
| description | String | 参数描述 | 否 |
| defaultValue | String | 默认值 | 是 |
| createTime | DateTime | 创建时间 | 是 |
| updateTime | DateTime | 更新时间 | 是 |
## 模型参数明细
### 大额交易模型参数
| 参数名称 | 描述 | 默认值 | 单位 |
|---------|------|--------|------|
| 单笔大额交易 | 单笔交易超过该金额 | 500000 | 人民币/笔 |
| 日频繁转账 | 单日转账次数超过 | 10 | 次/日 |
| 周频繁转账 | 单周转账次数超过 | 50 | 次/周 |
| 月频繁转账 | 单月转账次数超过 | 200 | 次/月 |
### 可疑兼职模型参数
| 参数名称 | 描述 | 默认值 | 单位 |
|---------|------|--------|------|
| 月度固定收入 | 月度固定收入超过 | 50000 | 人民币/月 |
| 固定对手转入金额 | 从固定对手单次转入超过 | 20000 | 人民币/笔 |
| 固定对手转入频率 | 从固定对手月度转入次数超过 | 5 | 次/月 |
### 可疑外汇交易模型参数
| 参数名称 | 描述 | 默认值 | 单位 |
|---------|------|--------|------|
| 单笔购汇金额 | 单笔购汇超过该金额 | 50000 | 美元/笔 |
| 单笔结汇金额 | 单笔结汇超过该金额 | 50000 | 美元/笔 |
| 跨境汇款金额 | 单笔跨境汇款超过该金额 | 100000 | 美元/笔 |
| 月度购汇总额 | 月度累计购汇超过 | 200000 | 美元/月 |
| 月度结汇总额 | 月度累计结汇超过 | 200000 | 美元/月 |
| 频繁外汇交易 | 单日外汇交易次数超过 | 5 | 次/日 |
## 业务规则
1. **参数配置权限**: 只有系统管理员可以修改模型参数
2. **参数生效时机**: 参数修改后对新生成的分析报告生效
3. **参数验证**: 保存时验证参数值的合理性和有效性
4. **参数变更记录**: 记录参数的变更历史,包括变更人、变更时间、变更内容
## 页面原型
### 参数配置页面
```
+------------------------------------------+
| 模型参数管理 |
+------------------------------------------+
| 模型名称: [可疑外汇交易模型 ▼] |
+------------------------------------------+
| 阈值参数配置 |
+------------------------------------------+
| 监测项 | 描述 | 阈值设置 | 单位 |
|-------------|------------------|----------|---------|
| 单笔购汇金额 | 单笔购汇超过该金额| 50000 |美元/笔 [查询]|
| 单笔结汇金额 | 单笔结汇超过该金额| 50000 |美元/笔 |
| 跨境汇款金额 | 单笔跨境汇款超过 | 100000 |美元/笔 |
| 月度购汇总额 | 月度累计购汇超过 | 200000 |美元/月 |
| 月度结汇总额 | 月度累计结汇超过 | 200000 |美元/月 |
| 频繁外汇交易 | 单日外汇交易次数超过| 5 |次/日 |
+------------------------------------------+
| [保存配置] [恢复默认] |
+------------------------------------------+
```
## 操作流程
```
1. 选择模型类型
2. 查看当前参数配置
3. 修改参数值
4. 验证参数有效性
5. 保存配置
6. 系统记录变更历史
```
## 交互关系
| 关联模块 | 交互说明 |
|---------|---------|
| 数据管理 | 配置的参数用于数据质量检查 |
| 初核结果总览 | 配置的参数用于风险模型分析 |
## 功能特性
### 参数管理
- 支持多模型参数配置
- 支持参数值的实时验证
- 支持参数默认值恢复
- 支持参数变更历史记录
### 参数验证
- 参数类型验证
- 参数范围验证
- 参数逻辑关系验证
### 权限控制
- 系统管理员可修改参数
- 普通用户只能查看参数
- 参数修改需要审批(可选)
## 功能点统计
- 二级功能: 3个
- 三级功能点: 6个
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 第346-373行

View File

@@ -0,0 +1,215 @@
# 05-系统管理模块
## 模块概述
系统管理模块提供系统基础管理功能,包括用户权限管理、项目统计和操作日志管理。
## 模块结构
```
系统管理模块
├── 用户权限管理
├── 项目统计
└── 操作日志管理
```
## 功能分解
### 5.1 用户权限管理
**功能描述**: 系统管理员可对访问系统的用户账号进行增、删、改、禁用等操作。
**功能点**:
- **用户管理**: 对用户账号进行增、删、改、查操作
- **角色管理**: 定义和管理系统角色,分配角色权限
- **权限分配**: 为角色分配菜单权限和数据权限
- **用户禁用/启用**: 对用户账号进行禁用或启用操作
- **密码管理**: 重置用户密码,强制用户修改密码
**数据要素**:
- 用户账号
- 用户姓名
- 所属部门
- 角色
- 账号状态
- 最后登录时间
### 5.2 项目统计
**功能描述**: 根据年度、组长、对象、成果等维度进行项目统计分析。
**功能点**:
- **年度统计**: 按年度统计项目数量、完成情况等
- **组长统计**: 按项目负责人统计项目情况
- **对象统计**: 按核查对象统计项目情况
- **成果统计**: 统计项目成果(发现问题数量、预警人数等)
- **统计报表生成**: 生成可视化统计报表
**数据要素**:
- 统计维度(年度/组长/对象/成果)
- 项目数量
- 完成状态
- 预警人数
- 发现问题数量
### 5.3 操作日志管理
**功能描述**: 记录用户的关键操作,支持按时间、用户、操作类型进行查询。
**功能点**:
- **日志记录**: 自动记录用户的关键操作(登录、数据导入、模型运行、报告生成等)
- **日志查询**: 支持按时间范围、用户、操作类型等条件查询
- **日志详情**: 查看操作日志的详细信息
- **日志导出**: 支持将操作日志导出为Excel
**数据要素**:
- 操作时间
- 操作用户
- 操作类型
- 操作模块
- 操作内容
- 操作结果
- IP地址
## 数据模型
### 用户 (SysUser)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| userId | Long | 用户ID | 是 |
| userName | String | 用户账号 | 是 |
| nickName | String | 用户姓名 | 是 |
| deptId | Long | 部门ID | 是 |
| phonenumber | String | 手机号码 | 否 |
| status | String | 账号状态(正常/停用) | 是 |
| lastLoginTime | DateTime | 最后登录时间 | 否 |
### 角色 (SysRole)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| roleId | Long | 角色ID | 是 |
| roleName | String | 角色名称 | 是 |
| roleKey | String | 角色权限字符串 | 是 |
| status | String | 角色状态(正常/停用) | 是 |
### 操作日志 (SysOperLog)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| operId | Long | 日志ID | 是 |
| title | String | 模块标题 | 是 |
| businessType | String | 业务类型0其它 1新增 2修改 3删除 | 是 |
| method | String | 方法名称 | 是 |
| requestMethod | String | 请求方式 | 是 |
| operName | String | 操作人员 | 是 |
| deptName | String | 部门名称 | 否 |
| operUrl | String | 请求URL | 是 |
| operIp | String | 主机地址 | 是 |
| operLocation | String | 操作地点 | 否 |
| operParam | String | 请求参数 | 是 |
| jsonResult | String | 返回参数 | 是 |
| status | Integer | 操作状态0正常 1异常 | 是 |
| errorMsg | String | 错误消息 | 否 |
| operTime | DateTime | 操作时间 | 是 |
### 项目统计 (ProjectStatistics)
| 字段名 | 类型 | 说明 | 必填 |
|-------|------|------|-----|
| statId | Long | 统计ID | 是 |
| statDimension | String | 统计维度 | 是 |
| statValue | String | 统计值 | 是 |
| projectCount | Integer | 项目数量 | 是 |
| completedCount | Integer | 完成项目数 | 是 |
| warningCount | Integer | 预警人数 | 是 |
| issueCount | Integer | 发现问题数 | 是 |
| statYear | Integer | 统计年度 | 否 |
## 操作类型分类
| 操作类型 | 说明 |
|---------|------|
| 用户登录 | 用户登录系统 |
| 数据导入 | 导入各类数据 |
| 模型运行 | 运行风险模型 |
| 报告生成 | 生成分析报告 |
| 数据导出 | 导出数据或报告 |
| 参数配置 | 修改系统参数 |
| 用户管理 | 管理用户账号 |
| 其他 | 其他操作 |
## 业务规则
1. **用户权限管理**:
- 只有系统管理员可以进行用户管理操作
- 禁用用户后该用户无法登录系统
- 用户密码重置后需要用户首次登录时修改
2. **项目统计**:
- 支持多维度组合统计
- 统计数据实时更新
- 支持统计报表导出
3. **操作日志管理**:
- 关键操作自动记录日志
- 日志保留期限至少1年
- 支持日志数据的备份和恢复
## 页面原型
### 1. 用户管理页面
- 用户列表
- 搜索筛选区
- 新增/编辑/删除/禁用操作
- 角色分配
### 2. 项目统计页面
- 统计维度选择区
- 统计结果展示(图表/表格)
- 报表导出功能
### 3. 操作日志页面
- 日志列表
- 搜索筛选区(时间/用户/操作类型)
- 日志详情查看
- 日志导出功能
## 交互关系
| 关联模块 | 交互说明 |
|---------|---------|
| 所有模块 | 操作日志记录所有模块的操作 |
| 项目管理模块 | 项目统计使用项目管理数据 |
## 功能特性
### 用户权限管理
- 基于RBAC的权限控制模型
- 支持角色和权限的灵活配置
- 支持数据权限控制(全部/本部门/本人等)
- 支持用户账号的全生命周期管理
### 项目统计
- 多维度统计分析
- 可视化图表展示
- 支持自定义统计维度
- 支持统计报表导出
### 操作日志管理
- 全面的操作记录
- 灵活的查询条件
- 详细的日志信息
- 支持日志审计和追溯
## 功能点统计
- 二级功能: 3个
- 三级功能点: 6个
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 第374-388行

66
doc/modules/README.md Normal file
View File

@@ -0,0 +1,66 @@
# 纪检初核系统功能模块总览
## 文档说明
本文档是《纪检初核系统功能说明书V1.0》的需求分解文档,采用三级分解方式将系统功能细化为可执行的功能点。
## 分解结构
```
纪检初核系统
├── 01-项目管理模块
├── 02-项目工作台
│ ├── 02.1-数据管理
│ ├── 02.2-初核结果总览
│ ├── 02.3-专项排查
│ └── 02.4-流水明细查询
├── 03-信息维护模块
├── 04-参数配置模块
└── 05-系统管理模块
```
## 模块概览
| 模块编号 | 模块名称 | 功能说明 | 子模块数 |
|---------|---------|---------|---------|
| 01 | 项目管理模块 | 管理所有历史创建的核查项目,提供项目创建、查询、状态管理等功能 | 0 |
| 02 | 项目工作台 | 核心业务模块,包含数据管理、风险分析、专项排查等功能 | 4 |
| 02.1 | 数据管理 | 数据导入、数据质量检查 | 0 |
| 02.2 | 初核结果总览 | 风险总览、风险模型、风险明细 | 0 |
| 02.3 | 专项排查 | 员工详查、图谱分析、拓展查询 | 0 |
| 02.4 | 流水明细查询 | 流水合并、二次分析 | 0 |
| 03 | 信息维护模块 | 中介库管理、员工信息管理、信贷客户家庭关系维护 | 0 |
| 04 | 参数配置模块 | 风险模型参数管理 | 0 |
| 05 | 系统管理模块 | 用户权限、项目统计、操作日志管理 | 0 |
## 功能点统计
| 模块 | 三级功能点数量 |
|-----|--------------|
| 01-项目管理模块 | 12 |
| 02.1-数据管理 | 10 |
| 02.2-初核结果总览 | 16 |
| 02.3-专项排查 | 10 |
| 02.4-流水明细查询 | 4 |
| 03-信息维护模块 | 6 |
| 04-参数配置模块 | 6 |
| 05-系统管理模块 | 6 |
| **合计** | **70** |
## 文档索引
- [01-项目管理模块](./01-项目管理模块.md)
- [02-项目工作台](./02-项目工作台/)
- [02.1-数据管理](./02-项目工作台/02.1-数据管理.md)
- [02.2-初核结果总览](./02-项目工作台/02.2-初核结果总览.md)
- [02.3-专项排查](./02-项目工作台/02.3-专项排查.md)
- [02.4-流水明细查询](./02-项目工作台/02.4-流水明细查询.md)
- [03-信息维护模块](./03-信息维护模块.md)
- [04-参数配置模块](./04-参数配置模块.md)
- [05-系统管理模块](./05-系统管理模块.md)
## 版本信息
- **文档版本**: V1.0
- **创建日期**: 2026-01-27
- **基于原文档**: 纪检初核系统功能说明书V1.0 (2026-01-16)

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 KiB

View File

@@ -0,0 +1,162 @@
# 中介黑名单导入功能修复说明
## 问题描述
在导入机构中介黑名单数据时,出现以下错误:
```
Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: Column 'certificate_no' cannot be null
```
## 问题原因
1. **数据库约束**`ccdi_intermediary_blacklist` 表的 `certificate_no` 字段设置为 `NOT NULL`,不允许存储 null 值。
2. **代码缺陷**:在 `CcdiIntermediaryBlacklistServiceImpl.java``importEntityIntermediary` 方法中,导入机构中介时只设置了 `corpCreditCode`(统一社会信用代码),但没有设置 `certificateNo` 字段,导致该字段为 null。
3. **批量插入失败**`batchInsert` 方法明确插入 `certificate_no` 字段,当值为 null 时违反数据库约束。
## 解决方案
### 1. 代码修改
**文件**[CcdiIntermediaryBlacklistServiceImpl.java](d:\discipline-prelim-check\discipline-prelim-check\ruoyi-ccdi\src\main\java\com\ruoyi\dpc\service\impl\CcdiIntermediaryBlacklistServiceImpl.java)
**修改位置**:第 390-394 行
**修改前**
```java
// 转换为实体
CcdiIntermediaryBlacklist intermediary = new CcdiIntermediaryBlacklist();
intermediary.setName(excel.getName());
intermediary.setIntermediaryType("2");
```
**修改后**
```java
// 转换为实体
CcdiIntermediaryBlacklist intermediary = new CcdiIntermediaryBlacklist();
intermediary.setName(excel.getName());
// 对于机构中介,使用统一社会信用代码作为证件号
intermediary.setCertificateNo(excel.getCorpCreditCode());
intermediary.setIntermediaryType("2");
```
### 2. 验证逻辑增强
**文件**[CcdiIntermediaryBlacklistServiceImpl.java](d:\discipline-prelim-check\discipline-prelim-check\ruoyi-ccdi\src\main\java\com\ruoyi\dpc\service\impl\CcdiIntermediaryBlacklistServiceImpl.java)
**修改位置**:第 484-488 行
**修改前**
```java
private void validateEntityIntermediaryData(CcdiIntermediaryEntityExcel excel) {
if (StringUtils.isEmpty(excel.getName())) {
throw new RuntimeException("机构名称不能为空");
}
}
```
**修改后**
```java
private void validateEntityIntermediaryData(CcdiIntermediaryEntityExcel excel) {
if (StringUtils.isEmpty(excel.getName())) {
throw new RuntimeException("机构名称不能为空");
}
// 验证统一社会信用代码不能为空(因为会用作 certificate_no 字段)
if (StringUtils.isEmpty(excel.getCorpCreditCode())) {
throw new RuntimeException("统一社会信用代码不能为空");
}
}
```
### 3. 批量更新 XML 配置优化
**文件**[CcdiIntermediaryBlacklistMapper.xml](d:\discipline-prelim-check\discipline-prelim-check\ruoyi-ccdi\src\main\resources\mapper\dpc\CcdiIntermediaryBlacklistMapper.xml)
**修改位置**:第 125-127 行
**修改前**
```xml
<if test="item.dataSource != null">data_source = #{item.dataSource},</if>
update_by = #{item.updateBy},
update_time = #{item.updateTime}
```
**修改后**
```xml
<if test="item.dataSource != null">data_source = #{item.dataSource},</if>
<if test="item.certificateNo != null">certificate_no = #{item.certificateNo},</if>
update_by = #{item.updateBy},
update_time = #{item.updateTime}
```
## 设计说明
### 为什么使用统一社会信用代码作为证件号?
1. **数据一致性**:统一社会信用代码本身就是机构的法定证件号,将其同时存储在 `certificate_no` 字段中可以保持数据的一致性。
2. **查询便利**`certificate_no` 字段有索引,设置后可以快速查询机构中介。
3. **兼容性好**:个人中介和机构中介都使用 `certificate_no` 字段,查询逻辑更统一。
4. **不破坏现有结构**:不需要修改数据库表结构,只修改代码逻辑。
## 测试验证
### 测试用例
1. **个人中介导入**:正常导入个人中介数据,验证 `certificate_no` 字段正确存储身份证号。
2. **机构中介导入**:导入机构中介数据,验证 `certificate_no` 字段正确存储统一社会信用代码。
3. **统一社会信用代码为空**:验证当统一社会信用代码为空时,导入被正确拒绝并给出错误提示。
4. **批量更新**:验证批量更新时 `certificate_no` 字段能够正确更新。
### 测试脚本
测试脚本位于:[doc/test-data/test_import_fix.py](d:\discipline-prelim-check\discipline-prelim-check\doc\test-data\test_import_fix.py)
运行测试:
```bash
python doc/test-data/test_import_fix.py
```
## 影响范围
### 已影响的功能
- 机构中介批量导入功能
### 不影响的功能
- 个人中介导入功能
- 手动新增中介功能
- 中介查询功能
- 中介导出功能
## 注意事项
1. **数据迁移**:如果数据库中已存在机构中介数据且 `certificate_no` 为 null需要执行以下 SQL 进行数据修复:
```sql
UPDATE ccdi_intermediary_blacklist
SET certificate_no = corp_credit_code
WHERE intermediary_type = '2' AND certificate_no IS NULL AND corp_credit_code IS NOT NULL;
```
2. **Excel 模板**:确保导入模板中统一社会信用代码字段设置为必填项。
3. **前端验证**:建议在前端表单中也添加统一社会信用代码的必填验证。
## 修改文件列表
1. [CcdiIntermediaryBlacklistServiceImpl.java](d:\discipline-prelim-check\discipline-prelim-check\ruoyi-ccdi\src\main\java\com\ruoyi\dpc\service\impl\CcdiIntermediaryBlacklistServiceImpl.java) - 服务层实现
2. [CcdiIntermediaryBlacklistMapper.xml](d:\discipline-prelim-check\discipline-prelim-check\ruoyi-ccdi\src\main\resources\mapper\dpc\CcdiIntermediaryBlacklistMapper.xml) - MyBatis 映射文件
3. [test_import_fix.py](d:\discipline-prelim-check\discipline-prelim-check\doc\test-data\test_import_fix.py) - 测试脚本
## 版本历史
| 版本 | 日期 | 作者 | 说明 |
|------|------|------|------|
| 1.0 | 2026-01-29 | ruoyi | 初始版本,修复机构中介导入时 certificate_no 为 null 的问题 |

Binary file not shown.

View File

@@ -0,0 +1 @@
window.ENV = {"IS_FEAT_FLPAK4GB":true,"IS_FEAT_ABOARD":true,"IS_FEAT_SIGMA":true,"IS_LEGACY_V7":true,"BOMX_API_SDK_URL":"https://sdk.boardmix.cn/bmsdk","BOMX_API_CLIENT_ID":"IpfSMaEsOHWu7cg7","AIPPT_CLIENT_ID":"0ePnORDMD6KJSIIB","AIPPT_API_SDK_URL":"https://sdk.pptgo.cn/pptsdk","PIXSO_API_URL":"https://ps.modao.cc","PIXSO_USER_CLIENT_ID":"pixso_design_online"}

View File

@@ -0,0 +1 @@
TAG_V++3NXya0fYxmDs8mWSM69pSHMU=|2026-01-27T00_2026-01-27T00:11:10.126Z

View File

@@ -0,0 +1 @@
window["hzv5"] = window["hzv5"] || {};window["hzv5"]["init"] = {"MBServer":"modao.cc","MBClientDownloadURL":"https://cdn-release.modao.cc/desktop/Mockitt-darwin-x64-zh-1.2.5.dmg","MBChromeDownloadURL":"https://www.google.cn/chrome/","MBSketchPluginDownloadURL":"https://cdn-release.modao.cc/sketch/MockingBot.zh.sketchplugin.zip","isOnPremises":false,"isWonderShare":false,"projectUpper":{"owner_id":2209883,"owner_name":"谢小涵","owner_email":"153276082@qq.com","owner_avatar":"https://oss-mb-fog.modao.cc/uploads4/avatars/220/2209883/forum_132.jpeg","id":33418631,"limitation":{"storage":5000,"exportable":["png","pngs","htmlzip"],"encryptable":true,"inspectable":true,"slices":true,"projects":65535,"screens":65535,"commentable":true},"screens_count":5,"cid":"pb2mk0rvsqu5j6763","team_cid":"temk0rv8qmsbc9ft","space_cid":"splopmenp3ricy1f","space_name":"默认空间","name":"纪检初核系统","type":"proto2","attr":{"export_settings":[{"affix":"suffix","scale":"1","format":"png"}],"export_with_device_frame":false},"created_at":1767594090000,"updated_at":1768285464000,"timestamp":"1768285464","access":"public","access_token":"WEvGV4cIt8dobuo9KjUF","version":"v3","icon":null,"cover":"/uploads7/covers/3341/33418631/cover_1769473447.png","custom_cover":null,"is_custom_cover":false,"splash":null,"width":1440,"height":1024,"device":"web","model":"desktop","scale":100,"archived":false,"is_sclib":false,"parent_cid":"pb2m9uxlouyf4dwms","source_upper_cid":null,"clones":11,"shell_type":"device","password":"","wechat":false,"highlight":true,"preview_option":1,"expired":false,"deleted":false,"duplicating":false,"permissions":[{"user_id":2209883,"role":"project_owner"}],"is_org_project":false,"is_sub_project":false,"runner_mode":"preview","comment_permission":"org_member","tabs":null,"visibility":"open","building":"view_sticky","scene_tag":"PC-web","is_solo_lifetime":true,"is_first_canvas_open":null},"projectMeta":{"cid":"pm2mk0rvsquvpcjcs","mtime":1767594090054,"name":"","type":"proto2","ttag":"flat","upper_cid":"pb2mk0rvsqu5j6763","upper_type":"project-basic","is_flat_meta":true}}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
window["hzv5"] = window["hzv5"] || {};window["hzv5"]["mktc"] = {"md_vip_mkt_list":[],"mt_vip_mkt_list":[],"no_wm_mkt_list":["igk8iirffgi4hpfx","igk8iisxdk72zt9","igk8iiv3qpgl3lsx","igk8iiw2zlastfks","igk8ij5zxd43jlud","igk8ijdjkayrsvvz","igk8ijgosea1iye0","igkszmzkoc8bv5fq","igkw1wjh6762ojwr","igkwotlxrcwn19vd"]}

View File

@@ -0,0 +1,44 @@
<!doctype html><html translate="no"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1"><meta name="apple-mobile-web-app-capable" content="yes"><meta name="mobile-web-app-capable" content="yes"><meta name="renderer" content="webkit"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>墨刀</title><link rel="icon" id="icon" href="mb-proto2/vis/modao/favicon_home_screen_new.ico"><link rel="apple-touch-icon-precomposed" id="apple-touch-icon" href="mb-proto2/vis/modao/favicon_home_screen_new.ico"><link rel="stylesheet" href="mb-proto2/icons/fa5/css/fa5.css"><script>if (
function() { try { eval([
'for (const a of []) {}',
'let b = { fetch, Proxy }',
'let c = (async () => {})()',
'let { d0, ...d1 } = { ...b }'
].join(';')) } catch (e) { console.log('!!', e); return true } }()
) location.href = "https://modao.cc/browser-update"</script><script src="mb-static/2410/echarts-map/loadMap.js"></script><script src="env/2203.js"></script><script>window.ENV.NO_SENTRY = true; window.ENV.NO_TRACK = true</script><script>if (ENV.IS_MO) {
document.title = 'Mockitt';
document.documentElement.classList.add('wonder-share');
document.getElementById('icon').href = '/mb-static/2509/favicon-mo.ico';
document.getElementById('apple-touch-icon').href = '/mb-static/2509/favicon-mo.ico';
}</script><script>window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments) };
ENV.IS_MO && gtag('js', new Date());
ENV.IS_MO && gtag('config', 'UA-4839360-64');
ENV.IS_MO && document.write('<script async src="https://www.googletagmanager.com/gtag/js?id=UA-4839360-64"><'+'/script>')</script><script>window.dataLayer = window.dataLayer || [];
function gtag() { dataLayer.push(arguments) };
ENV.IS_MO && gtag('js', new Date());
ENV.IS_MO && gtag('config', 'G-24WTSJBD5B');
ENV.IS_MO && document.write('<script async src="https://www.googletagmanager.com/gtag/js?id=G-24WTSJBD5B"><'+'/script>')</script><script src="mb-static/2308/core-js-3.32.1/modern.js"></script><script>ENV.NO_TRACK || document.write('<script src="mb-static/2502/sa-1.26.18/_.js"><'+'/script>')</script><script defer="defer" src="mb-proto2/6.3688a-vendor-0d07687f4be09b8b3eca.js"></script><script defer="defer" src="mb-proto2/5.fxqum-vendor-f8aaf1fb2db7ed4b19df.js"></script><script defer="defer" src="mb-proto2/5.7b24m-vendor-c6215ade32c4d6e04f4c.js"></script><script defer="defer" src="mb-proto2/4.ekpaa-vendor-4a8c0d8af0989de4a89f.js"></script><script defer="defer" src="mb-proto2/4.n9fxu-vendor-121f3fbb2320541a30bc.js"></script><script defer="defer" src="mb-proto2/3.h4vam-vendor-5567a1235ac230e00561.js"></script><script defer="defer" src="mb-proto2/preview-html-zip-40b1c65b44de21c4ab8a.js"></script><link href="mb-proto2/6.3688a-vendor-16f3ece222913e7058d1.css" rel="stylesheet"><link href="mb-proto2/preview-html-zip-f0d9de76208db26b1137.css" rel="stylesheet"><script>function loadGTM() {
if (ENV.NO_TRACK) {
return
}
(function (w, d, s, l, i) {
w[l] = w[l] || [];
w[l].push({
'gtm.start': new Date().getTime(),
event: 'gtm.js',
});
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s),
dl = l !== 'dataLayer' ? '&l=' + l : '';
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', 'GTM-TZJK297T');
}
loadGTM()</script></head><body><div id="workspace"></div><script>HZv5_PREVIEW_MODE="device"</script>
<script src="extra/data.0.js"></script>
<script src="extra/data.2.js"></script>
<script src="extra/data.1.js"></script>
</body></html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Some files were not shown because too many files have changed in this diff Show More