From 1b043fa2d6a6fff74e673e0bd0f66b9ee0ad553d Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Thu, 29 Jan 2026 13:39:47 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AD=E4=BB=8B=E7=B1=BB=E5=9E=8B=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 5 +- CLAUDE.md | 2 +- doc/EasyExcel字典下拉框使用说明.md | 226 ++++++ doc/中介主体信息表.csv | 22 + doc/中介人员信息表.csv | 20 + doc/中介黑名单管理API文档.md | 404 ++++++++++ doc/员工信息管理API文档.md | 18 +- .../design.md | 700 ++++++++++++++++++ .../proposal.md | 144 ++++ .../intermediary-detailed-fields/spec.md | 181 +++++ .../tasks.md | 358 +++++++++ .../ruoyi/common/annotation/DictDropdown.java | 73 ++ .../dpc/controller/DpcEmployeeController.java | 6 +- .../DpcIntermediaryBlacklistController.java | 50 +- .../dpc/domain/DpcIntermediaryBlacklist.java | 90 +++ .../dto/DpcIntermediaryEntityAddDTO.java | 239 ++++++ .../dto/DpcIntermediaryPersonAddDTO.java | 204 +++++ .../dpc/domain/excel/DpcEmployeeExcel.java | 5 +- .../excel/DpcIntermediaryEntityExcel.java | 96 +++ .../excel/DpcIntermediaryPersonExcel.java | 84 +++ .../domain/vo/DpcIntermediaryBlacklistVO.java | 98 +-- .../vo/DpcIntermediaryEntityDetailVO.java | 126 ++++ .../vo/DpcIntermediaryPersonDetailVO.java | 113 +++ .../java/com/ruoyi/dpc/enums/DataSource.java | 41 + .../com/ruoyi/dpc/enums/EmployeeStatus.java | 35 + .../main/java/com/ruoyi/dpc/enums/Gender.java | 38 + .../ruoyi/dpc/enums/IntermediaryStatus.java | 35 + .../com/ruoyi/dpc/enums/IntermediaryType.java | 35 + .../dpc/handler/DictDropdownWriteHandler.java | 336 +++++++++ .../IDpcIntermediaryBlacklistService.java | 28 + .../service/impl/DpcEmployeeServiceImpl.java | 20 +- .../DpcIntermediaryBlacklistServiceImpl.java | 283 ++++++- .../com/ruoyi/dpc/utils/EasyExcelUtil.java | 96 +++ sql/dpc_intermediary_dict_data_20260129.sql | 139 ++++ sql/dpc_intermediary_enhance_20260129.sql | 63 ++ sql/fix_intermediary_comments_20260129.sql | 105 +++ 36 files changed, 4373 insertions(+), 145 deletions(-) create mode 100644 doc/EasyExcel字典下拉框使用说明.md create mode 100644 doc/中介主体信息表.csv create mode 100644 doc/中介人员信息表.csv create mode 100644 doc/中介黑名单管理API文档.md create mode 100644 openspec/changes/enhance-intermediary-with-detailed-fields/design.md create mode 100644 openspec/changes/enhance-intermediary-with-detailed-fields/proposal.md create mode 100644 openspec/changes/enhance-intermediary-with-detailed-fields/specs/intermediary-detailed-fields/spec.md create mode 100644 openspec/changes/enhance-intermediary-with-detailed-fields/tasks.md create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictDropdown.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/dto/DpcIntermediaryEntityAddDTO.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/dto/DpcIntermediaryPersonAddDTO.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcIntermediaryEntityExcel.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcIntermediaryPersonExcel.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryEntityDetailVO.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryPersonDetailVO.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/DataSource.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/EmployeeStatus.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/Gender.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/IntermediaryStatus.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/IntermediaryType.java create mode 100644 ruoyi-dpc/src/main/java/com/ruoyi/dpc/handler/DictDropdownWriteHandler.java create mode 100644 sql/dpc_intermediary_dict_data_20260129.sql create mode 100644 sql/dpc_intermediary_enhance_20260129.sql create mode 100644 sql/fix_intermediary_comments_20260129.sql diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 6cea9b6..8326344 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -22,7 +22,10 @@ "mcp__database-server__alter_table", "mcp__database-server__write_query", "Bash(mvn dependency:tree:*)", - "Bash(javac:*)" + "Bash(javac:*)", + "Bash(unzip:*)", + "Bash(chcp:*)", + "Skill(superpowers:brainstorming)" ] }, "enabledMcpjsonServers": [ diff --git a/CLAUDE.md b/CLAUDE.md index fae8313..5cf45ef 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,7 +38,7 @@ ## 运行 -- 使用mcp工具进行数据库相关操作 +- 使用mcp:dpc_intermediary_blacklist进行数据库相关操作 - 使用根目录中的ry.bat控制后端的启动,不要自行在命令行中启动后端 - 测试方式为生成可执行的测试脚本 - 测试脚本在运行完成后需要保存所有接口输出并生成测试用例报告 diff --git a/doc/EasyExcel字典下拉框使用说明.md b/doc/EasyExcel字典下拉框使用说明.md new file mode 100644 index 0000000..a3ffec5 --- /dev/null +++ b/doc/EasyExcel字典下拉框使用说明.md @@ -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 DpcEmployeeExcel { + + @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 = "dpc_employee_status") + private String status; +} +``` + +### 步骤2:在Controller中使用 + +```java +@RestController +@RequestMapping("/dpc/employee") +public class DpcEmployeeController { + + /** + * 下载带字典下拉框的导入模板 + */ + @PostMapping("/importTemplateWithDropdown") + public void importTemplateWithDropdown(HttpServletResponse response) { + EasyExcelUtil.importTemplateWithDictDropdown( + response, + DpcEmployeeExcel.class, + "员工信息" + ); + } + + /** + * 导出带字典下拉框的Excel + */ + @PostMapping("/exportWithDropdown") + public void exportWithDropdown(HttpServletResponse response) { + List list = employeeService.selectEmployeeList(); + EasyExcelUtil.exportExcelWithDictDropdown( + response, + list, + DpcEmployeeExcel.class, + "员工信息" + ); + } +} +``` + +## 高级用法 + +### 1. 显示字典键值而非标签 + +```java +@DictDropdown(dictType = "dpc_employee_status", displayType = DisplayType.VALUE) +private String status; +``` + +### 2. 允许手动输入(非严格模式) + +```java +@DictDropdown(dictType = "dpc_employee_status", strict = false) +private String status; +``` + +### 3. 自定义隐藏Sheet名称 + +```java +@DictDropdown(dictType = "dpc_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 /dpc/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 | 初始版本,支持字典下拉框功能 | diff --git a/doc/中介主体信息表.csv b/doc/中介主体信息表.csv new file mode 100644 index 0000000..69c3c3c --- /dev/null +++ b/doc/中介主体信息表.csv @@ -0,0 +1,22 @@ +ֶ,,/,ǷΪ,Ĭֵ,˵ +ͳһô,VARCHAR,18,,-,ͳһô +,VARCHAR,200,,-,ҵע +,VARCHAR,50,,-,ҵͣι˾ɷ޹˾ϻҵ幤̻ҵ +ҵ,VARCHAR,50,,-,󡢺ʡ +ҵ,VARCHAR,100,,-,ҵ +ҵ,VARCHAR,100,,-,ҵ +,DATE,-,,-,ҵ +עַ,VARCHAR,500,,-,עַ +,VARCHAR,50,,-, +֤,VARCHAR,30,,-,֤ +֤,VARCHAR,30,,-,֤ +ɶ1,VARCHAR,30,,-,ɶ +ɶ2,VARCHAR,30,,-,ɶ +ɶ3,VARCHAR,30,,-,ɶ +ɶ4,VARCHAR,30,,-,ɶ +ɶ5,VARCHAR,30,,-,ɶ +ʱ,DATETIME,-,,ǰʱ,¼ʱ +ʱ,DATETIME,-,,ǰʱ,¼ʱ +,VARCHAR,50,,-,¼ +,VARCHAR,50,,-,¼ +Դ,VARCHAR,30,,MANUAL,"MANUAL:ֶ¼, SYSTEM:ϵͳͬ, API:ӿڻȡ, IMPORT:" diff --git a/doc/中介人员信息表.csv b/doc/中介人员信息表.csv new file mode 100644 index 0000000..1607881 --- /dev/null +++ b/doc/中介人员信息表.csv @@ -0,0 +1,20 @@ +ֶ,,/,ǷΪ,Ĭֵ,˵ +ԱID,VARCHAR,20,,-,н顢ְҵծˡн +Ա,VARCHAR,30,,-,н顢ְҵծˡн +Ա,VARCHAR,50,,-,磺ˡż +,VARCHAR,50,,-,Ա +Ա,CHAR,1,,-,"M:, F:Ů, O:" +֤,VARCHAR,30,,֤,֤ա۰̨֤֤֤ͨ +֤,VARCHAR,30,,-,֤루ܴ洢 +ֻ,VARCHAR,20,,-,ֻ루ܴ洢 +΢ź,VARCHAR,50,,-,΢ź +ϵַ,VARCHAR,200,,-,ϸϵַ +ڹ˾,VARCHAR,100,,-,ǰְ˾ +ְλ,VARCHAR,100,,-,ְλ/ְ +ԱID,VARCHAR,20,,-,ԱID +ϵ,VARCHAR,50,,-,ԱĹϵ +ʱ,DATETIME,-,,ǰʱ,¼ʱ +ʱ,DATETIME,-,,ǰʱ,¼ʱ +,VARCHAR,50,,-,¼ +,VARCHAR,50,,-,¼ +Դ,VARCHAR,30,,MANUAL,"MANUAL:ֶ¼, SYSTEM:ϵͳͬ, IMPORT:, API:ӿڻȡ" diff --git a/doc/中介黑名单管理API文档.md b/doc/中介黑名单管理API文档.md new file mode 100644 index 0000000..b7c9906 --- /dev/null +++ b/doc/中介黑名单管理API文档.md @@ -0,0 +1,404 @@ +# 中介黑名单管理 API 文档 + +## 概述 + +中介黑名单管理模块提供个人和机构两类中介信息的增删改查、类型化模板下载和批量导入导出功能。 + +**基础路径**: `/dpc/intermediary` + +**权限标识前缀**: `dpc:intermediary` + +--- + +## API 接口列表 + +### 1. 查询中介黑名单列表 + +**接口地址**: `GET /dpc/intermediary/list` + +**权限要求**: `dpc:intermediary:list` + +**请求参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| name | String | 否 | 姓名/机构名称(模糊查询) | +| certificateNo | String | 否 | 证件号/统一社会信用代码(精确查询) | +| intermediaryType | String | 否 | 中介类型(1=个人, 2=机构) | +| status | String | 否 | 状态(0=正常, 1=停用) | +| pageNum | Integer | 否 | 页码(默认1) | +| pageSize | Integer | 否 | 每页数量(默认10) | + +**响应示例**: +```json +{ + "code": 200, + "msg": "操作成功", + "rows": [ + { + "intermediaryId": 1, + "name": "张三", + "certificateNo": "110101199001011234", + "intermediaryType": "1", + "intermediaryTypeName": "个人", + "status": "0", + "statusName": "正常", + "remark": "测试数据", + "createTime": "2026-01-29 10:00:00" + } + ], + "total": 1 +} +``` + +**响应字段说明**: + +| 字段名 | 类型 | 说明 | +|--------|------|------| +| intermediaryId | Long | 中介ID | +| name | String | 姓名/机构名称 | +| certificateNo | String | 证件号/统一社会信用代码 | +| intermediaryType | String | 中介类型(1=个人, 2=机构) | +| intermediaryTypeName | String | 中介类型名称 | +| status | String | 状态(0=正常, 1=停用) | +| statusName | String | 状态名称 | +| remark | String | 备注 | +| createTime | Date | 创建时间 | + +--- + +### 2. 获取中介黑名单详细信息 + +**接口地址**: `GET /dpc/intermediary/{intermediaryId}` + +**权限要求**: `dpc:intermediary:query` + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| intermediaryId | Long | 是 | 中介ID | + +**功能说明**: 根据中介类型返回不同的详情结构 + +**个人类型响应示例**: +```json +{ + "code": 200, + "msg": "操作成功", + "data": { + "intermediaryId": 1, + "name": "张三", + "certificateNo": "110101199001011234", + "intermediaryType": "1", + "intermediaryTypeName": "个人", + "status": "0", + "statusName": "正常", + "dataSource": "IMPORT", + "dataSourceName": "批量导入", + "indivType": "中介", + "indivGender": "M", + "indivGenderName": "男", + "indivCertType": "身份证", + "indivPhone": "13800138000", + "indivCompany": "XX公司", + "indivPosition": "经纪人" + } +} +``` + +**机构类型响应示例**: +```json +{ + "code": 200, + "msg": "操作成功", + "data": { + "intermediaryId": 2, + "name": "XX中介公司", + "intermediaryType": "2", + "intermediaryTypeName": "机构", + "status": "0", + "statusName": "正常", + "dataSource": "MANUAL", + "dataSourceName": "手动录入", + "corpCreditCode": "91110000XXXXXXXXXX", + "corpType": "有限责任公司", + "corpNature": "民企", + "corpLegalRep": "张三", + "corpAddress": "北京市朝阳区" + } +} +``` + +--- + +### 3. 新增中介黑名单 + +**接口地址**: `POST /dpc/intermediary` + +**权限要求**: `dpc:intermediary:add` + +**请求体**: +```json +{ + "name": "张三", + "certificateNo": "110101199001011234", + "intermediaryType": "1", + "status": "0", + "remark": "测试数据" +} +``` + +**字段说明**: + +| 字段名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| name | String | 是 | 姓名/机构名称 | +| certificateNo | String | 是 | 证件号/统一社会信用代码 | +| intermediaryType | String | 是 | 中介类型(1=个人, 2=机构) | +| status | String | 是 | 状态(0=正常, 1=停用) | +| remark | String | 否 | 备注 | + +**响应示例**: +```json +{ + "code": 200, + "msg": "操作成功" +} +``` + +--- + +### 4. 修改中介黑名单 + +**接口地址**: `PUT /dpc/intermediary` + +**权限要求**: `dpc:intermediary:edit` + +**请求体**: +```json +{ + "intermediaryId": 1, + "name": "张三", + "certificateNo": "110101199001011234", + "intermediaryType": "1", + "status": "0", + "remark": "测试数据" +} +``` + +**字段说明**: 与新增接口相同,intermediaryId 为必填项。 + +**响应示例**: +```json +{ + "code": 200, + "msg": "操作成功" +} +``` + +--- + +### 5. 删除中介黑名单 + +**接口地址**: `DELETE /dpc/intermediary/{intermediaryIds}` + +**权限要求**: `dpc:intermediary:remove` + +**路径参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| intermediaryIds | Long[] | 是 | 中介ID数组(逗号分隔) | + +**响应示例**: +```json +{ + "code": 200, + "msg": "操作成功" +} +``` + +--- + +### 6. 导出中介黑名单 + +**接口地址**: `POST /dpc/intermediary/export` + +**权限要求**: `dpc:intermediary:export` + +**请求参数**: 与查询列表接口相同(支持筛选条件) + +**响应**: Excel 文件下载 + +--- + +### 7. 下载个人中介导入模板(带字典下拉框) + +**接口地址**: `POST /dpc/intermediary/importPersonTemplate` + +**权限要求**: 无 + +**功能说明**: 下载的 Excel 模板中,性别、证件类型列会自动添加字典下拉框。 + +**响应**: Excel 模板文件下载 + +**Excel 格式说明**: + +**Sheet1: 个人中介黑名单** +| 姓名 | 人员类型 | 人员子类型 | 性别▼ | 证件类型▼ | 证件号码 | 手机号码 | 微信号 | 联系地址 | 所在公司 | 职位 | 关联人员ID | 关联关系 | 备注 | +|------|---------|-----------|-------|-----------|---------|---------|--------|---------|---------|-----|-----------|---------|------| +| 张三 | 中介 | 本人 | 男 | 身份证 | 110101199001011234 | 13800138000 | zhangsan | 北京市朝阳区 | XX公司 | 经纪人 | - | - | 测试 | + +**注**:带 ▼ 标记的列包含下拉框,选项来自字典: +- 性别:`dpc_indiv_gender` +- 证件类型:`dpc_certificate_type` + +--- + +### 8. 下载机构中介导入模板(带字典下拉框) + +**接口地址**: `POST /dpc/intermediary/importEntityTemplate` + +**权限要求**: 无 + +**功能说明**: 下载的 Excel 模板中,主体类型、企业性质列会自动添加字典下拉框。 + +**响应**: Excel 模板文件下载 + +**Excel 格式说明**: + +**Sheet1: 机构中介黑名单** +| 机构名称 | 统一社会信用代码 | 主体类型▼ | 企业性质▼ | 行业分类 | 所属行业 | 成立日期 | 注册地址 | 法定代表人 | 法定代表人证件类型 | 法定代表人证件号码 | 股东1 | 股东2 | 股东3 | 股东4 | 股东5 | 备注 | +|---------|-----------------|-----------|-----------|---------|---------|---------|---------|-----------|-------------------|-------------------|-------|-------|-------|-------|-------|------| +| XX公司 | 91110000XXXXXXXXXX | 有限责任公司 | 民企 | 房地产 | 房地产业 | 2020-01-01 | 北京市朝阳区 | 张三 | 身份证 | 110101199001011234 | 李四 | 王五 | - | - | - | - | + +**注**:带 ▼ 标记的列包含下拉框,选项来自字典: +- 主体类型:`dpc_entity_type` +- 企业性质:`dpc_enterprise_nature` + +--- + +### 9. 导入个人中介黑名单 + +**接口地址**: `POST /dpc/intermediary/importPersonData` + +**权限要求**: `dpc:intermediary:import` + +**请求参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| file | File | 是 | Excel 文件 | +| updateSupport | Boolean | 否 | 是否更新已存在数据(默认false) | + +**Excel 格式**: 参见"下载个人中介导入模板" + +**数据验证规则**: +1. **姓名**:必填,长度 1-100 字符 +2. **证件号码**:必填,长度不超过 50 字符 +3. **证件类型**:选填,默认"身份证" +4. **其他字段**:选填,按长度限制验证 +5. **状态**:系统默认设置为"正常"(0) +6. **数据来源**:系统默认设置为"批量导入"(IMPORT) + +**响应示例**: +```json +{ + "code": 200, + "msg": "恭喜您,数据已全部导入成功!共 10 条" +} +``` + +--- + +### 10. 导入机构中介黑名单 + +**接口地址**: `POST /dpc/intermediary/importEntityData` + +**权限要求**: `dpc:intermediary:import` + +**请求参数**: + +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| file | File | 是 | Excel 文件 | +| updateSupport | Boolean | 否 | 是否更新已存在数据(默认false) | + +**Excel 格式**: 参见"下载机构中介导入模板" + +**数据验证规则**: +1. **机构名称**:必填,长度 1-200 字符 +2. **统一社会信用代码**:选填,18 位 +3. **其他字段**:选填,按长度限制验证 +4. **状态**:系统默认设置为"正常"(0) +5. **数据来源**:系统默认设置为"批量导入"(IMPORT) + +**响应示例**: +```json +{ + "code": 200, + "msg": "恭喜您,数据已全部导入成功!共 10 条" +} +``` + +--- + +## 字典数据说明 + +导入模板中的下拉框选项来自系统字典管理,相关字典类型: + +### 个人中介字典 + +| 字典类型 | 字典名称 | 用途 | +|---------|---------|------| +| dpc_indiv_gender | 个人中介性别 | 个人中介模板性别下拉框 | +| dpc_certificate_type | 证件类型 | 个人中介模板证件类型下拉框 | + +### 机构中介字典 + +| 字典类型 | 字典名称 | 用途 | +|---------|---------|------| +| dpc_entity_type | 主体类型 | 机构中介模板主体类型下拉框 | +| dpc_enterprise_nature | 企业性质 | 机构中介模板企业性质下拉框 | + +### 通用字典 + +| 字典类型 | 字典名称 | 用途 | +|---------|---------|------| +| dpc_data_source | 数据来源 | 数据来源字段映射 | + +--- + +## 错误码说明 + +| 错误码 | 说明 | +|--------|------| +| 200 | 操作成功 | +| 401 | 未授权,请先登录 | +| 403 | 无权限访问 | +| 500 | 服务器内部错误 | + +## 业务错误信息 + +| 错误信息 | 说明 | +|----------|------| +| 姓名不能为空 | 个人中介导入时姓名为空 | +| 机构名称不能为空 | 机构中介导入时机构名称为空 | +| 证件号码不能为空 | 个人中介导入时证件号码为空 | +| 该证件号已存在 | 新增/导入时证件号重复 | +| 该统一社会信用代码已存在 | 新增/导入时信用代码重复 | + +## 测试账号 + +- 用户名: `admin` +- 密码: `admin123` + +测试前请先调用 `/login/test` 接口获取 Token。 + +## 更新日志 + +| 版本 | 日期 | 说明 | +|------|------|------| +| 1.0.0 | 2026-01-29 | 初始版本,支持个人和机构分类管理 | +| 1.1.0 | 2026-01-29 | 添加字典下拉框功能,分离个人/机构模板 | diff --git a/doc/员工信息管理API文档.md b/doc/员工信息管理API文档.md index 1a4cbf8..0fb8848 100644 --- a/doc/员工信息管理API文档.md +++ b/doc/员工信息管理API文档.md @@ -257,14 +257,30 @@ Authorization: Bearer {token} --- -### 7. 下载导入模板 +### 7. 下载导入模板(带字典下拉框) **接口地址**: `POST /dpc/employee/importTemplate` **权限要求**: 无 +**功能说明**: 下载的 Excel 模板中,"状态"列会自动添加字典下拉框,方便用户选择。 + **响应**: Excel 模板文件下载 +**Excel 格式说明**: + +**Sheet1: 员工信息** +| 姓名 | 柜员号 | 所属部门ID | 身份证号 | 电话 | 入职时间 | 状态▼ | +|------|--------|------------|----------|------|----------|------| +| 张三 | 001 | 100 | 110101199001011234 | 13800138000 | 2020-01-01 | 在职 | + +**注**:带 ▼ 标记的列包含下拉框,选项来自字典 `dpc_employee_status`。 + +**使用 @DictDropdown 注解实现**: +- 状态字段使用 `@DictDropdown(dictType = "dpc_employee_status")` 注解 +- 系统自动从 Redis 缓存读取字典数据并生成下拉框 +- 下拉选项可动态更新,刷新字典缓存后生效 + --- ### 8. 导入员工信息 diff --git a/openspec/changes/enhance-intermediary-with-detailed-fields/design.md b/openspec/changes/enhance-intermediary-with-detailed-fields/design.md new file mode 100644 index 0000000..fd88a2a --- /dev/null +++ b/openspec/changes/enhance-intermediary-with-detailed-fields/design.md @@ -0,0 +1,700 @@ +# Design: 增强中介黑名单字段并实现类型化模板导入 + +## 数据库设计 + +### 表扩展:dpc_intermediary_blacklist + +在现有表基础上添加以下字段: + +```sql +-- ============================================================ +-- 个人类型字段 (以 indiv_ 前缀标识,individual 缩写) +-- ============================================================ +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_type` VARCHAR(30) DEFAULT NULL COMMENT '人员类型(中介、职业背债人、房产中介等)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_sub_type` VARCHAR(50) DEFAULT NULL COMMENT '人员子类型(本人、配偶等)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_gender` CHAR(1) DEFAULT NULL COMMENT '性别(M男 F女 O其他)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_cert_type` VARCHAR(30) DEFAULT '身份证' COMMENT '证件类型'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_phone` VARCHAR(20) DEFAULT NULL COMMENT '手机号码(加密存储)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_wechat` VARCHAR(50) DEFAULT NULL COMMENT '微信号'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_address` VARCHAR(200) DEFAULT NULL COMMENT '联系地址'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_company` VARCHAR(100) DEFAULT NULL COMMENT '所在公司'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_position` VARCHAR(100) DEFAULT NULL COMMENT '职位/职务'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_related_id` VARCHAR(20) DEFAULT NULL COMMENT '关联人员ID'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_relation` VARCHAR(50) DEFAULT NULL COMMENT '关联关系'; + +-- ============================================================ +-- 机构类型字段 (以 corp_ 前缀标识,corporation 缩写) +-- ============================================================ +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_credit_code` VARCHAR(18) DEFAULT NULL COMMENT '统一社会信用代码'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_type` VARCHAR(50) DEFAULT NULL COMMENT '主体类型(有限责任公司、股份有限公司等)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_nature` VARCHAR(50) DEFAULT NULL COMMENT '企业性质(国企、民企、外企等)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_industry_category` VARCHAR(100) DEFAULT NULL COMMENT '行业分类'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_industry` VARCHAR(100) DEFAULT NULL COMMENT '所属行业'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_establish_date` DATE DEFAULT NULL COMMENT '成立日期'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_address` VARCHAR(500) DEFAULT NULL COMMENT '注册地址'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_legal_rep` VARCHAR(50) DEFAULT NULL COMMENT '法定代表人'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_legal_cert_type` VARCHAR(30) DEFAULT NULL COMMENT '法定代表人证件类型'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_legal_cert_no` VARCHAR(30) DEFAULT NULL COMMENT '法定代表人证件号码'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_1` VARCHAR(30) DEFAULT NULL COMMENT '股东1'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_2` VARCHAR(30) DEFAULT NULL COMMENT '股东2'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_3` VARCHAR(30) DEFAULT NULL COMMENT '股东3'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_4` VARCHAR(30) DEFAULT NULL COMMENT '股东4'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_5` VARCHAR(30) DEFAULT NULL COMMENT '股东5'; + +-- ============================================================ +-- 通用字段 +-- ============================================================ +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `data_source` VARCHAR(30) DEFAULT 'MANUAL' COMMENT '数据来源(MANUAL手动录入 SYSTEM系统同步 IMPORT批量导入 API接口获取)'; +``` + +### 完整表结构 + +```sql +CREATE TABLE `dpc_intermediary_blacklist` ( + `intermediary_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '中介ID', + + -- ============================================================ + -- 核心字段(原有) + -- ============================================================ + `name` VARCHAR(100) NOT NULL COMMENT '姓名/机构名称', + `certificate_no` VARCHAR(50) DEFAULT NULL COMMENT '证件号', + `intermediary_type` CHAR(1) NOT NULL DEFAULT '1' COMMENT '中介类型(1个人 2机构)', + `status` CHAR(1) NOT NULL DEFAULT '0' COMMENT '状态(0正常 1停用)', + `remark` VARCHAR(500) DEFAULT NULL COMMENT '备注', + + -- ============================================================ + -- 个人类型字段(新增,以 indiv_ 前缀标识) + -- ============================================================ + `indiv_type` VARCHAR(30) DEFAULT NULL COMMENT '人员类型', + `indiv_sub_type` VARCHAR(50) DEFAULT NULL COMMENT '人员子类型', + `indiv_gender` CHAR(1) DEFAULT NULL COMMENT '性别(M男 F女 O其他)', + `indiv_cert_type` VARCHAR(30) DEFAULT '身份证' COMMENT '证件类型', + `indiv_phone` VARCHAR(20) DEFAULT NULL COMMENT '手机号码', + `indiv_wechat` VARCHAR(50) DEFAULT NULL COMMENT '微信号', + `indiv_address` VARCHAR(200) DEFAULT NULL COMMENT '联系地址', + `indiv_company` VARCHAR(100) DEFAULT NULL COMMENT '所在公司', + `indiv_position` VARCHAR(100) DEFAULT NULL COMMENT '职位/职务', + `indiv_related_id` VARCHAR(20) DEFAULT NULL COMMENT '关联人员ID', + `indiv_relation` VARCHAR(50) DEFAULT NULL COMMENT '关联关系', + + -- ============================================================ + -- 机构类型字段(新增,以 corp_ 前缀标识) + -- ============================================================ + `corp_credit_code` VARCHAR(18) DEFAULT NULL COMMENT '统一社会信用代码', + `corp_type` VARCHAR(50) DEFAULT NULL COMMENT '主体类型', + `corp_nature` VARCHAR(50) DEFAULT NULL COMMENT '企业性质', + `corp_industry_category` VARCHAR(100) DEFAULT NULL COMMENT '行业分类', + `corp_industry` VARCHAR(100) DEFAULT NULL COMMENT '所属行业', + `corp_establish_date` DATE DEFAULT NULL COMMENT '成立日期', + `corp_address` VARCHAR(500) DEFAULT NULL COMMENT '注册地址', + `corp_legal_rep` VARCHAR(50) DEFAULT NULL COMMENT '法定代表人', + `corp_legal_cert_type` VARCHAR(30) DEFAULT NULL COMMENT '法定代表人证件类型', + `corp_legal_cert_no` VARCHAR(30) DEFAULT NULL COMMENT '法定代表人证件号码', + `corp_shareholder_1` VARCHAR(30) DEFAULT NULL COMMENT '股东1', + `corp_shareholder_2` VARCHAR(30) DEFAULT NULL COMMENT '股东2', + `corp_shareholder_3` VARCHAR(30) DEFAULT NULL COMMENT '股东3', + `corp_shareholder_4` VARCHAR(30) DEFAULT NULL COMMENT '股东4', + `corp_shareholder_5` VARCHAR(30) DEFAULT NULL COMMENT '股东5', + + -- ============================================================ + -- 通用字段(新增) + -- ============================================================ + `data_source` VARCHAR(30) DEFAULT 'MANUAL' COMMENT '数据来源', + + -- ============================================================ + -- 审计字段(原有) + -- ============================================================ + `create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者', + `create_time` DATETIME DEFAULT NULL COMMENT '创建时间', + `update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者', + `update_time` DATETIME DEFAULT NULL COMMENT '更新时间', + + PRIMARY KEY (`intermediary_id`), + KEY `idx_name` (`name`), + KEY `idx_certificate_no` (`certificate_no`), + KEY `idx_intermediary_type` (`intermediary_type`), + KEY `idx_corp_credit_code` (`corp_credit_code`), + KEY `idx_indiv_phone` (`indiv_phone`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='中介人员黑名单表'; +``` + +### 新增字段说明 + +| 字段名 | 类型 | 说明 | 适用类型 | 必填 | 默认值 | +|-------|------|------|---------|-----|-------| +| indiv_type | VARCHAR(30) | 人员类型(中介、职业背债人等) | 个人 | 否 | NULL | +| indiv_sub_type | VARCHAR(50) | 人员子类型(本人、配偶等) | 个人 | 否 | NULL | +| indiv_gender | CHAR(1) | 性别(M男 F女 O其他) | 个人 | 否 | NULL | +| indiv_cert_type | VARCHAR(30) | 证件类型 | 个人 | 否 | 身份证 | +| indiv_phone | VARCHAR(20) | 手机号码 | 个人 | 否 | NULL | +| indiv_wechat | VARCHAR(50) | 微信号 | 个人 | 否 | NULL | +| indiv_address | VARCHAR(200) | 联系地址 | 个人 | 否 | NULL | +| indiv_company | VARCHAR(100) | 所在公司 | 个人 | 否 | NULL | +| indiv_position | VARCHAR(100) | 职位/职务 | 个人 | 否 | NULL | +| indiv_related_id | VARCHAR(20) | 关联人员ID | 个人 | 否 | NULL | +| indiv_relation | VARCHAR(50) | 关联关系 | 个人 | 否 | NULL | +| corp_credit_code | VARCHAR(18) | 统一社会信用代码 | 机构 | 否 | NULL | +| corp_type | VARCHAR(50) | 主体类型 | 机构 | 否 | NULL | +| corp_nature | VARCHAR(50) | 企业性质 | 机构 | 否 | NULL | +| corp_industry_category | VARCHAR(100) | 行业分类 | 机构 | 否 | NULL | +| corp_industry | VARCHAR(100) | 所属行业 | 机构 | 否 | NULL | +| corp_establish_date | DATE | 成立日期 | 机构 | 否 | NULL | +| corp_address | VARCHAR(500) | 注册地址 | 机构 | 否 | NULL | +| corp_legal_rep | VARCHAR(50) | 法定代表人 | 机构 | 否 | NULL | +| corp_legal_cert_type | VARCHAR(30) | 法定代表人证件类型 | 机构 | 否 | NULL | +| corp_legal_cert_no | VARCHAR(30) | 法定代表人证件号码 | 机构 | 否 | NULL | +| corp_shareholder_1 | VARCHAR(30) | 股东1 | 机构 | 否 | NULL | +| corp_shareholder_2 | VARCHAR(30) | 股东2 | 机构 | 否 | NULL | +| corp_shareholder_3 | VARCHAR(30) | 股东3 | 机构 | 否 | NULL | +| corp_shareholder_4 | VARCHAR(30) | 股东4 | 机构 | 否 | NULL | +| corp_shareholder_5 | VARCHAR(30) | 股东5 | 机构 | 否 | NULL | +| data_source | VARCHAR(30) | 数据来源 | 全部 | 否 | MANUAL | + +**字段命名规则:** +- **个人字段**:统一使用 `indiv_` 前缀(individual 缩写),便于快速识别 +- **机构字段**:统一使用 `corp_` 前缀(corporation 缩写),便于快速识别 +- **通用字段**:无前缀,适用于所有类型 + +## 后端设计 + +### 模块结构 + +``` +ruoyi-dpc/ +├── src/main/java/com/ruoyi/dpc/ +│ ├── controller/ +│ │ └── DpcIntermediaryBlacklistController.java (修改) +│ ├── domain/ +│ │ ├── DpcIntermediaryBlacklist.java (修改 - 添加新字段) +│ │ ├── dto/ +│ │ │ ├── DpcIntermediaryBlacklistAddDTO.java (保留 - 兼容) +│ │ │ ├── DpcIntermediaryBlacklistEditDTO.java (保留 - 兼容) +│ │ │ ├── DpcIntermediaryBlacklistQueryDTO.java (保留) +│ │ │ ├── DpcIntermediaryPersonAddDTO.java (新增) +│ │ │ └── DpcIntermediaryEntityAddDTO.java (新增) +│ │ ├── vo/ +│ │ │ ├── DpcIntermediaryBlacklistVO.java (保留 - 列表用) +│ │ │ ├── DpcIntermediaryPersonDetailVO.java (新增) +│ │ │ └── DpcIntermediaryEntityDetailVO.java (新增) +│ │ └── excel/ +│ │ ├── DpcIntermediaryBlacklistExcel.java (保留 - 通用模板) +│ │ ├── DpcIntermediaryPersonExcel.java (新增) +│ │ └── DpcIntermediaryEntityExcel.java (新增) +│ ├── mapper/ +│ │ └── DpcIntermediaryBlacklistMapper.java +│ └── service/ +│ ├── IDpcIntermediaryBlacklistService.java (修改接口) +│ └── impl/ +│ └── DpcIntermediaryBlacklistServiceImpl.java (修改实现) +└── src/main/resources/mapper/dpc/ + └── DpcIntermediaryBlacklistMapper.xml +``` + +### Controller 层设计 + +**新增接口:** + +```java +@RestController +@RequestMapping("/dpc/intermediary") +public class DpcIntermediaryBlacklistController extends BaseController { + + // ... 现有接口保持不变 ... + + /** + * 下载个人中介导入模板(带字典下拉框) + */ + @Operation(summary = "下载个人中介导入模板") + @PostMapping("/importPersonTemplate") + public void importPersonTemplate(HttpServletResponse response) { + EasyExcelUtil.importTemplateWithDictDropdown( + response, + DpcIntermediaryPersonExcel.class, + "个人中介黑名单" + ); + } + + /** + * 下载机构中介导入模板(带字典下拉框) + */ + @Operation(summary = "下载机构中介导入模板") + @PostMapping("/importEntityTemplate") + public void importEntityTemplate(HttpServletResponse response) { + EasyExcelUtil.importTemplateWithDictDropdown( + response, + DpcIntermediaryEntityExcel.class, + "机构中介黑名单" + ); + } + + /** + * 导入个人中介黑名单 + */ + @Operation(summary = "导入个人中介黑名单") + @PreAuthorize("@ss.hasPermi('dpc:intermediary:import')") + @Log(title = "中介黑名单", businessType = BusinessType.IMPORT) + @PostMapping("/importPersonData") + public AjaxResult importPersonData(MultipartFile file, boolean updateSupport) throws Exception { + List list = EasyExcelUtil.importExcel( + file.getInputStream(), DpcIntermediaryPersonExcel.class); + String message = intermediaryService.importPersonIntermediary(list, updateSupport); + return success(message); + } + + /** + * 导入机构中介黑名单 + */ + @Operation(summary = "导入机构中介黑名单") + @PreAuthorize("@ss.hasPermi('dpc:intermediary:import')") + @Log(title = "中介黑名单", businessType = BusinessType.IMPORT) + @PostMapping("/importEntityData") + public AjaxResult importEntityData(MultipartFile file, boolean updateSupport) throws Exception { + List list = EasyExcelUtil.importExcel( + file.getInputStream(), DpcIntermediaryEntityExcel.class); + String message = intermediaryService.importEntityIntermediary(list, updateSupport); + return success(message); + } +} +``` + +### Service 层设计 + +**接口扩展:** + +```java +public interface IDpcIntermediaryBlacklistService { + + // ... 现有方法保持不变 ... + + /** + * 根据中介类型获取详情(返回不同类型) + */ + Object selectIntermediaryDetailById(Long intermediaryId); + + /** + * 导入个人中介数据 + */ + String importPersonIntermediary(List list, boolean isUpdateSupport); + + /** + * 导入机构中介数据 + */ + String importEntityIntermediary(List list, boolean isUpdateSupport); +} +``` + +### Excel 类设计 + +**使用 @DictDropdown 注解实现字典下拉框** + +项目已有现成的字典下拉框功能,详见:[doc/EasyExcel字典下拉框使用说明.md](../../../doc/EasyExcel字典下拉框使用说明.md) + +**个人中介 Excel 类:** + +```java +@Data +public class DpcIntermediaryPersonExcel implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "姓名", index = 0) + @ColumnWidth(15) + private String name; + + @ExcelProperty(value = "人员类型", index = 1) + @ColumnWidth(15) + private String indivType; // 对应 indiv_type + + @ExcelProperty(value = "人员子类型", index = 2) + @ColumnWidth(15) + private String indivSubType; // 对应 indiv_sub_type + + @ExcelProperty(value = "性别", index = 3) + @ColumnWidth(10) + @DictDropdown(dictType = "dpc_indiv_gender") + private String indivGender; // 对应 indiv_gender,字典下拉框:男/女/其他 + + @ExcelProperty(value = "证件类型", index = 4) + @ColumnWidth(15) + @DictDropdown(dictType = "dpc_certificate_type") + private String indivCertType; // 对应 indiv_cert_type,字典下拉框:身份证/护照/港澳通行证/台胞证/军官证 + + @ExcelProperty(value = "证件号码", index = 5) + @ColumnWidth(20) + private String certificateNo; + + @ExcelProperty(value = "手机号码", index = 6) + @ColumnWidth(15) + private String indivPhone; // 对应 indiv_phone + + @ExcelProperty(value = "微信号", index = 7) + @ColumnWidth(15) + private String indivWechat; // 对应 indiv_wechat + + @ExcelProperty(value = "联系地址", index = 8) + @ColumnWidth(30) + private String indivAddress; // 对应 indiv_address + + @ExcelProperty(value = "所在公司", index = 9) + @ColumnWidth(20) + private String indivCompany; // 对应 indiv_company + + @ExcelProperty(value = "职位", index = 10) + @ColumnWidth(15) + private String indivPosition; // 对应 indiv_position + + @ExcelProperty(value = "关联人员ID", index = 11) + @ColumnWidth(15) + private String indivRelatedId; // 对应 indiv_related_id + + @ExcelProperty(value = "关联关系", index = 12) + @ColumnWidth(15) + private String indivRelation; // 对应 indiv_relation + + @ExcelProperty(value = "备注", index = 13) + @ColumnWidth(30) + private String remark; + + // 以下字段不在 Excel 中显示,由系统自动设置 + // private String status; // 默认:正常(0) + // private String dataSource; // 默认:批量导入(IMPORT) +} +``` + +**机构中介 Excel 类:** + +```java +@Data +public class DpcIntermediaryEntityExcel implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "机构名称", index = 0) + @ColumnWidth(25) + private String name; + + @ExcelProperty(value = "统一社会信用代码", index = 1) + @ColumnWidth(20) + private String corpCreditCode; // 对应 corp_credit_code + + @ExcelProperty(value = "主体类型", index = 2) + @ColumnWidth(20) + @DictDropdown(dictType = "dpc_entity_type") + private String corpType; // 对应 corp_type,字典下拉框:有限责任公司/股份有限公司/合伙企业/个体工商户/外资企业 + + @ExcelProperty(value = "企业性质", index = 3) + @ColumnWidth(15) + @DictDropdown(dictType = "dpc_enterprise_nature") + private String corpNature; // 对应 corp_nature,字典下拉框:国企/民企/外企/合资/其他 + + @ExcelProperty(value = "行业分类", index = 4) + @ColumnWidth(15) + private String corpIndustryCategory; // 对应 corp_industry_category + + @ExcelProperty(value = "所属行业", index = 5) + @ColumnWidth(15) + private String corpIndustry; // 对应 corp_industry + + @ExcelProperty(value = "成立日期", index = 6) + @ColumnWidth(15) + private String corpEstablishDate; // 对应 corp_establish_date + + @ExcelProperty(value = "注册地址", index = 7) + @ColumnWidth(40) + private String corpAddress; // 对应 corp_address + + @ExcelProperty(value = "法定代表人", index = 8) + @ColumnWidth(15) + private String corpLegalRep; // 对应 corp_legal_rep + + @ExcelProperty(value = "法定代表人证件类型", index = 9) + @ColumnWidth(20) + private String corpLegalCertType; // 对应 corp_legal_cert_type + + @ExcelProperty(value = "法定代表人证件号码", index = 10) + @ColumnWidth(20) + private String corpLegalCertNo; // 对应 corp_legal_cert_no + + @ExcelProperty(value = "股东1", index = 11) + @ColumnWidth(15) + private String corpShareholder1; // 对应 corp_shareholder_1 + + @ExcelProperty(value = "股东2", index = 12) + @ColumnWidth(15) + private String corpShareholder2; // 对应 corp_shareholder_2 + + @ExcelProperty(value = "股东3", index = 13) + @ColumnWidth(15) + private String corpShareholder3; // 对应 corp_shareholder_3 + + @ExcelProperty(value = "股东4", index = 14) + @ColumnWidth(15) + private String corpShareholder4; // 对应 corp_shareholder_4 + + @ExcelProperty(value = "股东5", index = 15) + @ColumnWidth(15) + private String corpShareholder5; // 对应 corp_shareholder_5 + + @ExcelProperty(value = "备注", index = 16) + @ColumnWidth(30) + private String remark; + + // 以下字段不在 Excel 中显示,由系统自动设置 + // private String status; // 默认:正常(0) + // private String dataSource; // 默认:批量导入(IMPORT) +} +``` + +### VO 设计 + +**个人详情 VO:** + +```java +@Data +public class DpcIntermediaryPersonDetailVO implements Serializable { + + // 核心字段 + private Long intermediaryId; + private String name; + private String certificateNo; + private String intermediaryType; + private String intermediaryTypeName; + private String status; + private String statusName; + private String remark; + private String dataSource; + + // 个人专属字段(使用 indiv_ 前缀) + private String indivType; // 人员类型 + private String indivSubType; // 人员子类型 + private String indivGender; // 性别 + private String indivGenderName; // 性别名称 + private String indivCertType; // 证件类型 + private String indivPhone; // 手机号码 + private String indivWechat; // 微信号 + private String indivAddress; // 联系地址 + private String indivCompany; // 所在公司 + private String indivPosition; // 职位/职务 + private String indivRelatedId; // 关联人员ID + private String indivRelation; // 关联关系 + + // 审计字段 + private String createBy; + private Date createTime; + private String updateBy; + private Date updateTime; +} +``` + +**机构详情 VO:** + +```java +@Data +public class DpcIntermediaryEntityDetailVO implements Serializable { + + // 核心字段 + private Long intermediaryId; + private String name; + private String certificateNo; + private String intermediaryType; + private String intermediaryTypeName; + private String status; + private String statusName; + private String remark; + private String dataSource; + + // 机构专属字段(使用 corp_ 前缀) + private String corpCreditCode; // 统一社会信用代码 + private String corpType; // 主体类型 + private String corpNature; // 企业性质 + private String corpIndustryCategory; // 行业分类 + private String corpIndustry; // 所属行业 + private String corpEstablishDate; // 成立日期 + private String corpAddress; // 注册地址 + private String corpLegalRep; // 法定代表人 + private String corpLegalCertType; // 法定代表人证件类型 + private String corpLegalCertNo; // 法定代表人证件号码 + private String corpShareholder1; // 股东1 + private String corpShareholder2; // 股东2 + private String corpShareholder3; // 股东3 + private String corpShareholder4; // 股东4 + private String corpShareholder5; // 股东5 + + // 审计字段 + private String createBy; + private Date createTime; + private String updateBy; + private Date updateTime; +} +``` + +## Excel 导入导出设计 + +### Excel 下拉框配置 + +为提高数据录入的准确性和一致性,以下字段在 Excel 模板中配置下拉框验证: + +#### 个人中介模板下拉框 + +| 列名 | 下拉框选项 | 说明 | +|------|-----------|------| +| 性别 | 男, 女, 其他 | 对应值:M, F, O | +| 证件类型 | 身份证, 护照, 港澳通行证, 台胞证, 军官证 | 从字典 dpc_certificate_type 加载 | + +#### 机构中介模板下拉框 + +| 列名 | 下拉框选项 | 说明 | +|------|-----------|------| +| 主体类型 | 有限责任公司, 股份有限公司, 合伙企业, 个体工商户, 外资企业 | 从字典 dpc_entity_type 加载 | +| 企业性质 | 国企, 民企, 外企, 合资, 其他 | 从字典 dpc_enterprise_nature 加载 | + +**注意**: +- **状态字段**:不在 Excel 模板中显示,导入时默认设置为"正常"(0) +- **数据来源字段**:不在 Excel 模板中显示,导入时默认设置为"批量导入"(IMPORT) + +#### 下拉框实现方式 + +使用项目现有的 `@DictDropdown` 注解功能,详见:[doc/EasyExcel字典下拉框使用说明.md](../../../doc/EasyExcel字典下拉框使用说明.md) + +**核心组件:** +- `@DictDropdown` 注解:`com.ruoyi.common.annotation.DictDropdown` +- `DictDropdownWriteHandler` 处理器:`com.ruoyi.dpc.handler.DictDropdownWriteHandler` +- `EasyExcelUtil.importTemplateWithDictDropdown()`:`com.ruoyi.dpc.utils.EasyExcelUtil` + +**实现步骤:** +1. 在 Excel 类的对应字段上添加 `@DictDropdown(dictType = "字典类型")` 注解 +2. 在若依系统中配置对应的字典数据 +3. 使用 `EasyExcelUtil.importTemplateWithDictDropdown()` 生成模板 +4. 系统自动从 Redis 缓存读取字典数据并生成下拉框 + +**优势:** +- 无需手动编写下拉框处理器代码 +- 字典数据统一管理,维护方便 +- 支持大量下拉选项(自动使用隐藏 Sheet) +- 下拉选项可动态更新 + +### 个人中介模板格式 + +| 姓名 | 人员类型 | 人员子类型 | 性别▼ | 证件类型▼ | 证件号码 | 手机号码 | 微信号 | 联系地址 | 所在公司 | 职位 | 关联人员ID | 关联关系 | 备注 | +|------|---------|-----------|-------|-----------|---------|---------|--------|---------|---------|-----|-----------|---------|------| +| 张三 | 中介 | 本人 | 男 | 身份证 | 110101199001011234 | 13800138000 | zhangsan | 北京市朝阳区 | XX公司 | 经纪人 | - | - | 测试 | + +**注:带 ▼ 标记的列包含下拉框;状态默认为"正常",数据来源默认为"批量导入"** + +### 机构中介模板格式 + +| 机构名称 | 统一社会信用代码 | 主体类型▼ | 企业性质▼ | 行业分类 | 所属行业 | 成立日期 | 注册地址 | 法定代表人 | 法定代表人证件类型 | 法定代表人证件号码 | 股东1 | 股东2 | 股东3 | 股东4 | 股东5 | 备注 | +|---------|-----------------|-----------|-----------|---------|---------|---------|---------|-----------|-------------------|-------------------|-------|-------|-------|-------|-------|------| +| XX中介公司 | 91110000XXXXXXXXXX | 有限责任公司 | 民企 | 房地产 | 房地产业 | 2020-01-01 | 北京市朝阳区 | 张三 | 身份证 | 110101199001011234 | 李四 | 王五 | - | - | - | - | + +**注:带 ▼ 标记的列包含下拉框;状态默认为"正常",数据来源默认为"批量导入"** + +### 导入数据验证规则 + +**个人中介验证规则:** +1. **姓名**:必填,长度 1-50 字符 +2. **证件号码**:必填,长度不超过 30 字符 +3. **证件类型**:选填,默认"身份证" +4. **其他字段**:选填,按长度限制验证 +5. **状态**:系统默认设置为"正常"(0) +6. **数据来源**:系统默认设置为"批量导入"(IMPORT) + +**机构中介验证规则:** +1. **机构名称**:必填,长度 1-200 字符 +2. **统一社会信用代码**:选填,18 位 +3. **其他字段**:选填,按长度限制验证 +4. **状态**:系统默认设置为"正常"(0) +5. **数据来源**:系统默认设置为"批量导入"(IMPORT) + +## 字典数据设计 + +### 人员类型(dpc_person_type) + +| 字典值 | 字典标签 | 排序 | 状态 | +|-------|---------|-----|------| +| 中介 | 中介 | 1 | 正常 | +| 职业背债人 | 职业背债人 | 2 | 正常 | +| 房产中介 | 房产中介 | 3 | 正常 | + +### 人员子类型(dpc_person_sub_type) + +| 字典值 | 字典标签 | 排序 | 状态 | +|-------|---------|-----|------| +| 本人 | 本人 | 1 | 正常 | +| 配偶 | 配偶 | 2 | 正常 | +| 子女 | 子女 | 3 | 正常 | +| 其他 | 其他 | 9 | 正常 | + +### 证件类型(dpc_certificate_type) + +| 字典值 | 字典标签 | 排序 | 状态 | +|-------|---------|-----|------| +| 身份证 | 身份证 | 1 | 正常 | +| 护照 | 护照 | 2 | 正常 | +| 港澳通行证 | 港澳通行证 | 3 | 正常 | +| 台胞证 | 台胞证 | 4 | 正常 | +| 军官证 | 军官证 | 5 | 正常 | + +### 主体类型(dpc_entity_type) + +| 字典值 | 字典标签 | 排序 | 状态 | +|-------|---------|-----|------| +| 有限责任公司 | 有限责任公司 | 1 | 正常 | +| 股份有限公司 | 股份有限公司 | 2 | 正常 | +| 合伙企业 | 合伙企业 | 3 | 正常 | +| 个体工商户 | 个体工商户 | 4 | 正常 | +| 外资企业 | 外资企业 | 5 | 正常 | + +### 企业性质(dpc_enterprise_nature) + +| 字典值 | 字典标签 | 排序 | 状态 | +|-------|---------|-----|------| +| 国企 | 国企 | 1 | 正常 | +| 民企 | 民企 | 2 | 正常 | +| 外企 | 外企 | 3 | 正常 | +| 合资 | 合资 | 4 | 正常 | +| 其他 | 其他 | 9 | 正常 | + +### 数据来源(dpc_data_source) + +| 字典值 | 字典标签 | 排序 | 状态 | +|-------|---------|-----|------| +| MANUAL | 手动录入 | 1 | 正常 | +| SYSTEM | 系统同步 | 2 | 正常 | +| IMPORT | 批量导入 | 3 | 正常 | +| API | 接口获取 | 4 | 正常 | + +## 技术约束 + +1. **后端框架**:Spring Boot 3.5.8 +2. **ORM 框架**:MyBatis Plus 3.5.10 +3. **Excel 处理**:EasyExcel +4. **数据库**:MySQL 8.2.0 +5. **字符编码**:UTF-8(utf8mb4) +6. **向后兼容**:保持现有接口和数据结构不变 + +## 工具类使用 + +### EasyExcelUtil 现有方法 + +项目已有 `EasyExcelUtil.importTemplateWithDictDropdown()` 方法,可直接使用: + +```java +// 生成个人中介模板(带字典下拉框) +EasyExcelUtil.importTemplateWithDictDropdown( + response, + DpcIntermediaryPersonExcel.class, + "个人中介黑名单" +); + +// 生成机构中介模板(带字典下拉框) +EasyExcelUtil.importTemplateWithDictDropdown( + response, + DpcIntermediaryEntityExcel.class, + "机构中介黑名单" +); +``` + +**参考文档:** [doc/EasyExcel字典下拉框使用说明.md](../../../doc/EasyExcel字典下拉框使用说明.md) diff --git a/openspec/changes/enhance-intermediary-with-detailed-fields/proposal.md b/openspec/changes/enhance-intermediary-with-detailed-fields/proposal.md new file mode 100644 index 0000000..ca8fa80 --- /dev/null +++ b/openspec/changes/enhance-intermediary-with-detailed-fields/proposal.md @@ -0,0 +1,144 @@ +# Proposal: 增强中介黑名单字段并实现类型化模板导入 + +## Change ID +`enhance-intermediary-with-detailed-fields` + +## Summary +增强中介黑名单功能,添加个人和机构类型的详细字段,实现类型化的模板下载和导入功能,并添加详情展示接口,根据中介类型展示不同的字段信息。 + +## Motivation +目前中介黑名单功能存在以下问题: +1. 字段过于简单,只有基础的姓名/机构名称、证件号、中介类型、状态、备注 +2. 无法区分个人和机构的不同信息需求 +3. 只有一个通用的导入模板,无法满足不同类型的详细数据录入 +4. 缺少详情展示接口,无法查看完整的个人或机构信息 + +业务需求: +1. 个人中介需要记录:人员类型、性别、证件类型、手机号码、微信号、联系地址、所在公司、职位、关联关系等详细信息 +2. 机构中介需要记录:统一社会信用代码、主体类型、企业性质、行业分类、成立日期、注册地址、法定代表人、股东等详细信息 +3. 需要分开的个人和机构导入模板,便于用户分别录入不同类型的数据 +4. 需要详情接口展示完整的个人或机构信息 + +## Scope +本提案实现以下功能: + +### 包含的功能 +- **1.1 数据库字段扩展** + - 为个人类型添加详细字段(人员类型、性别、证件类型、手机号码、微信号等) + - 为机构类型添加详细字段(统一社会信用代码、主体类型、企业性质、法定代表人、股东等) + - 保持现有字段的向后兼容性 + +- **1.2 模板下载功能增强** + - 个人中介模板下载(包含个人专属字段) + - 机构中介模板下载(包含机构专属字段) + +- **1.3 导入功能增强** + - 支持个人中介批量导入 + - 支持机构中介批量导入 + - 根据模板类型自动识别中介类型 + +- **1.4 详情展示接口** + - 根据中介类型返回不同的字段信息 + - 个人类型返回个人详细字段 + - 机构类型返回机构详细字段 + +- **1.5 列表接口优化** + - 保持列表接口简洁,只显示核心字段 + - 支持按新增字段进行筛选 + +### 明确排除 +- 前端页面的修改(后续独立变更) +- 数据迁移脚本(旧数据保持原样,新字段可为空) + +## Proposed Design +详见 [design.md](./design.md) + +## Alternatives Considered + +### 选项1:分表存储(个人表和机构表分离) +**优点**: +- 字段分离清晰 +- 查询性能可能更好(表更小) + +**缺点**: +- 需要维护多个表和关联关系 +- 列表查询需要联合查询 +- 导入导出逻辑复杂化 +- 不符合现有设计思路 + +**决定**:不采用。单表设计更简洁,通过中介类型区分即可满足需求。 + +### 选项2:使用 JSON 字段存储扩展信息 +**优点**: +- 灵活性高,易于扩展 +- 不需要修改表结构 + +**缺点**: +- 不利于按扩展字段查询和筛选 +- 不利于数据验证和约束 +- 导入导出逻辑复杂 + +**决定**:不采用。使用独立的字段更有利于数据管理和查询。 + +### 选项3:在现有基础上添加字段 +**优点**: +- 保持向后兼容 +- 实现简单直接 + +**缺点**: +- 表会有很多字段(约40+个) +- 个人和机构字段混在一起 + +**决定**:采用。这是最直接的方案,通过业务逻辑区分使用哪些字段,数据库层面允许字段为空。 + +## Impact + +### 后端影响 +- **数据库**:扩展 `dpc_intermediary_blacklist` 表,添加约30个新字段 +- **实体类**:`DpcIntermediaryBlacklist` 添加新字段属性 +- **DTO**:创建 `DpcIntermediaryPersonAddDTO` 和 `DpcIntermediaryEntityAddDTO` +- **VO**:创建 `DpcIntermediaryPersonDetailVO` 和 `DpcIntermediaryEntityDetailVO` +- **Excel**:创建 `DpcIntermediaryPersonExcel` 和 `DpcIntermediaryEntityExcel` +- **Controller**:添加两个模板下载接口,修改详情接口 +- **Service**:扩展导入逻辑,支持不同类型的模板 + +### 前端影响 +- API 接口变更:需要调用新的模板下载接口和详情接口 +- 详情页面需要根据类型显示不同字段 + +### 数据库影响 +- 扩展现有表,添加新字段(默认允许 NULL,保持向后兼容) + +## Dependencies +- 依赖现有 `dpc_intermediary_blacklist` 表和基础功能 +- 依赖 EasyExcel 进行 Excel 导入导出 + +## Related Changes +- `add-intermediary-blacklist` - 基础中介黑名单功能 + +## Open Questions +1. **旧数据处理**: + - 现有数据如何处理? + - 建议:保持不变,新字段为空,用户可后续编辑补充 + +2. **必填字段**: + - 新增字段哪些设为必填? + - 建议:除核心字段外,新字段都设为可选,便于分阶段完善数据 + +3. **详情接口设计**: + - 是分开的两个接口还是一个接口根据类型返回不同 VO? + - 建议:一个接口,根据中介类型返回不同的 VO 结构 + +## Success Criteria +- [ ] 数据库表扩展完成,添加个人和机构的详细字段 +- [ ] 可以下载个人中介专用导入模板 +- [ ] 可以下载机构中介专用导入模板 +- [ ] 可以使用个人模板批量导入个人中介数据 +- [ ] 可以使用机构模板批量导入机构中介数据 +- [ ] 详情接口根据类型返回完整的字段信息 +- [ ] 现有功能保持正常运行,向后兼容 + +## References +- [doc/中介人员信息表.csv](../../../doc/中介人员信息表.csv) +- [doc/中介主体信息表.csv](../../../doc/中介主体信息表.csv) +- [现有中介黑名单设计](../add-intermediary-blacklist/design.md) diff --git a/openspec/changes/enhance-intermediary-with-detailed-fields/specs/intermediary-detailed-fields/spec.md b/openspec/changes/enhance-intermediary-with-detailed-fields/specs/intermediary-detailed-fields/spec.md new file mode 100644 index 0000000..b6c367c --- /dev/null +++ b/openspec/changes/enhance-intermediary-with-detailed-fields/specs/intermediary-detailed-fields/spec.md @@ -0,0 +1,181 @@ +# Spec: 中介黑名单详细字段与类型化导入 + +## ADDED Requirements + +### Requirement: 支持个人和机构类型的不同字段存储 +系统必须能够为个人和机构类型的中介存储不同的字段信息,所有新增字段默认为可选,以保持向后兼容性。 + +#### Scenario: 个人中介存储详细信息 +**Given** 用户创建或编辑个人类型的中介 +**When** 填写个人专属字段(人员类型、性别、证件类型、手机号码等) +**Then** 系统应将这些字段保存到数据库 +**And** 个人专属字段应与机构专属字段分开存储在同一表中 + +#### Scenario: 机构中介存储详细信息 +**Given** 用户创建或编辑机构类型的中介 +**When** 填写机构专属字段(统一社会信用代码、主体类型、法定代表人等) +**Then** 系统应将这些字段保存到数据库 +**And** 机构专属字段应与个人专属字段分开存储在同一表中 + +#### Scenario: 向后兼容性保持 +**Given** 系统中存在旧的中介数据(只有基础字段) +**When** 查询或编辑这些旧数据 +**Then** 系统应正常工作 +**And** 新增字段应允许为空 + +### Requirement: 支持个人和机构分离的模板下载 +系统必须提供独立的个人和机构中介导入模板下载功能。 + +#### Scenario: 下载个人中介模板 +**Given** 用户在中介黑名单管理页面 +**When** 点击"下载个人模板"按钮 +**Then** 系统应生成包含个人专属字段的 Excel 模板 +**And** 模板应包含:姓名、人员类型、人员子类型、性别、证件类型、证件号码、手机号码、微信号、联系地址、所在公司、职位、关联人员ID、关联关系、状态、备注 +**And** 模板应包含示例数据便于用户理解 + +#### Scenario: 下载机构中介模板 +**Given** 用户在中介黑名单管理页面 +**When** 点击"下载机构模板"按钮 +**Then** 系统应生成包含机构专属字段的 Excel 模板 +**And** 模板应包含:机构名称、统一社会信用代码、主体类型、企业性质、行业分类、所属行业、成立日期、注册地址、法定代表人、法定代表人证件类型、法定代表人证件号码、股东1-5、状态、备注 +**And** 模板应包含示例数据便于用户理解 + +### Requirement: 支持个人和机构分离的批量导入 +系统必须支持使用个人和机构专用模板进行批量导入,并根据模板类型自动设置中介类型。 + +#### Scenario: 使用个人模板导入个人中介 +**Given** 用户准备了一个包含个人中介数据的 Excel 文件 +**When** 用户通过"导入个人数据"接口上传文件 +**Then** 系统应解析 Excel 数据 +**And** 自动设置中介类型为"个人"(1) +**And** 验证必填字段(姓名、证件号码、状态) +**And** 验证可选字段的长度限制 +**And** 将数据保存到数据库 +**And** 返回导入结果统计(成功数、失败数) + +#### Scenario: 使用机构模板导入机构中介 +**Given** 用户准备了一个包含机构中介数据的 Excel 文件 +**When** 用户通过"导入机构数据"接口上传文件 +**Then** 系统应解析 Excel 数据 +**And** 自动设置中介类型为"机构"(2) +**And** 验证必填字段(机构名称、状态) +**And** 验证可选字段的长度限制 +**And** 将数据保存到数据库 +**And** 返回导入结果统计(成功数、失败数) + +#### Scenario: 导入数据验证与错误提示 +**Given** 用户上传的 Excel 文件包含验证失败的数据 +**When** 系统执行导入 +**Then** 系统应记录每一行的验证错误 +**And** 失败数据不应写入数据库 +**And** 返回详细的错误信息,包括行号和错误原因 + +#### Scenario: 支持更新模式导入 +**Given** 用户选择"更新支持"模式 +**And** Excel 中包含已存在的中介(通过证件号或统一社会信用代码判断) +**When** 执行导入 +**Then** 系统应更新现有记录而不是创建新记录 + +### Requirement: 支持类型化的详情展示 +系统必须提供详情接口,根据中介类型返回包含不同字段的详情信息。 + +#### Scenario: 查询个人中介详情 +**Given** 数据库中存在个人类型的中介记录 +**When** 用户调用详情接口查询该记录 +**Then** 系统应返回包含个人专属字段的详情 VO +**And** 详情应包含:核心字段 + 个人专属字段(人员类型、性别、证件类型、手机号码、微信号、联系地址、所在公司、职位、关联关系等) +**And** 机构专属字段应不返回或为 null + +#### Scenario: 查询机构中介详情 +**Given** 数据库中存在机构类型的中介记录 +**When** 用户调用详情接口查询该记录 +**Then** 系统应返回包含机构专属字段的详情 VO +**And** 详情应包含:核心字段 + 机构专属字段(统一社会信用代码、主体类型、企业性质、法定代表人、股东等) +**And** 个人专属字段应不返回或为 null + +#### Scenario: 详情接口返回类型自动识别 +**Given** 用户调用详情接口,只提供中介ID +**When** 系统查询到该中介记录 +**Then** 系统应根据中介类型自动返回对应类型的 VO +**And** 个人类型返回 DpcIntermediaryPersonDetailVO +**And** 机构类型返回 DpcIntermediaryEntityDetailVO + +### Requirement: 保持现有功能兼容性 +所有新增功能必须保持现有接口和功能的正常运行。 + +#### Scenario: 列表接口保持简洁 +**Given** 用户调用中介黑名单列表接口 +**When** 系统返回列表数据 +**Then** 应保持现有的简洁字段结构 +**And** 列表 VO 应只包含:中介ID、姓名/机构名称、证件号、中介类型、状态、创建时间等核心字段 +**And** 不应包含新增的详细字段 + +#### Scenario: 现有导入接口保持可用 +**Given** 用户使用原有的通用导入模板和接口 +**When** 执行导入操作 +**Then** 系统应正常处理 +**And** 兼容只有基础字段的数据 + +#### Scenario: 现有新增/编辑接口保持可用 +**Given** 用户使用现有的新增/编辑接口 +**When** 只填写基础字段 +**Then** 系统应正常保存 +**And** 新增详细字段应保持为空或默认值 + +### Requirement: 支持按新增字段进行筛选 +列表查询接口应支持按新增字段进行筛选。 + +#### Scenario: 按人员类型筛选个人中介 +**Given** 用户在列表页面 +**When** 选择人员类型筛选条件(如"中介") +**Then** 系统应返回匹配该人员类型的个人中介记录 + +#### Scenario: 按企业性质筛选机构中介 +**Given** 用户在列表页面 +**When** 选择企业性质筛选条件(如"民企") +**Then** 系统应返回匹配该企业性质的机构中介记录 + +#### Scenario: 按数据来源筛选 +**Given** 用户在列表页面 +**When** 选择数据来源筛选条件(如"批量导入") +**Then** 系统应返回匹配该数据来源的所有中介记录 + +### Requirement: 字段加密存储 +敏感字段必须进行加密存储。 + +#### Scenario: 手机号码加密 +**Given** 用户输入或导入手机号码 +**When** 数据保存到数据库 +**Then** 手机号码应加密存储 +**And** 查询时解密返回 + +#### Scenario: 证件号码加密 +**Given** 用户输入或导入证件号码(个人或法定代表人) +**When** 数据保存到数据库 +**Then** 证件号码应加密存储 +**And** 查询时解密返回 + +## MODIFIED Requirements + +无。本提案为纯新增功能,不修改现有需求。 + +## REMOVED Requirements + +无。本提案不删除任何现有功能。 + +## Cross-References + +### Related Capabilities +- `intermediary-blacklist-management` (基础中介黑名单管理功能) +- `excel-import-export` (Excel 导入导出基础功能) + +### Dependencies +- 依赖现有 `dpc_intermediary_blacklist` 表结构 +- 依赖 EasyExcel 工具类 +- 依赖现有权限控制体系 + +### Sequencing +1. 首先完成数据库表扩展 +2. 然后更新实体类和 DTO/VO +3. 实现 Excel 类和导入导出逻辑 +4. 最后更新 Controller 和 Service 层 diff --git a/openspec/changes/enhance-intermediary-with-detailed-fields/tasks.md b/openspec/changes/enhance-intermediary-with-detailed-fields/tasks.md new file mode 100644 index 0000000..4de6be0 --- /dev/null +++ b/openspec/changes/enhance-intermediary-with-detailed-fields/tasks.md @@ -0,0 +1,358 @@ +# Tasks: 增强中介黑名单字段并实现类型化模板导入 + +## 任务概述 + +本任务列表将指导完成中介黑名单功能的增强,包括添加个人和机构类型的详细字段、实现类型化的模板下载和导入功能,以及添加详情展示接口。 + +## 任务清单 + +### 阶段一:数据库扩展 + +#### 任务 1.1:生成数据库变更 SQL 脚本 +- [ ] 编写 ALTER TABLE 语句,添加个人类型字段(11个字段) +- [ ] 编写 ALTER TABLE 语句,添加机构类型字段(15个字段) +- [ ] 编写 ALTER TABLE 语句,添加通用字段(1个字段:data_source) +- [ ] 添加必要的索引(idx_credit_code, idx_phone_number) +- [ ] 将 SQL 脚本保存到 `sql/` 目录,命名格式:`dpc_intermediary_enhance_YYYYMMDD.sql` + +**验证方式**:SQL 语法检查,确保所有字段正确添加 + +**依赖**:无 + +--- + +#### 任务 1.2:执行数据库变更 +- [ ] 在开发环境执行 SQL 脚本 +- [ ] 验证表结构变更成功 +- [ ] 确认所有字段添加成功且类型正确 +- [ ] 确认索引创建成功 + +**验证方式**:DESCRIBE dpc_intermediary_blacklist; SHOW INDEX FROM dpc_intermediary_blacklist; + +**依赖**:任务 1.1 + +--- + +### 阶段二:后端实体类和 DTO/VO 更新 + +#### 任务 2.1:更新实体类 DpcIntermediaryBlacklist +- [ ] 添加个人类型字段属性(11个) +- [ ] 添加机构类型字段属性(15个) +- [ ] 添加通用字段属性(1个:dataSource) +- [ ] 保持现有字段不变,确保向后兼容 +- [ ] 使用 Lombok @Data 注解 + +**验证方式**:编译通过,无语法错误 + +**依赖**:任务 1.2 + +--- + +#### 任务 2.2:创建个人中介 DTO +- [ ] 创建 `DpcIntermediaryPersonAddDTO.java` +- [ ] 添加个人专属字段 +- [ ] 添加适当的验证注解(@NotBlank, @Size 等) +- [ ] 实现 Serializable 接口 + +**验证方式**:编译通过,DTO 字段完整 + +**依赖**:任务 2.1 + +--- + +#### 任务 2.3:创建机构中介 DTO +- [ ] 创建 `DpcIntermediaryEntityAddDTO.java` +- [ ] 添加机构专属字段 +- [ ] 添加适当的验证注解 +- [ ] 实现 Serializable 接口 + +**验证方式**:编译通过,DTO 字段完整 + +**依赖**:任务 2.1 + +--- + +#### 任务 2.4:创建个人详情 VO +- [ ] 创建 `DpcIntermediaryPersonDetailVO.java` +- [ ] 包含核心字段 + 个人专属字段 +- [ ] 添加关联字段名称(如 genderName, certificateTypeName) +- [ ] 添加审计字段 +- [ ] 使用 @JsonFormat 注解格式化日期 + +**验证方式**:编译通过,VO 字段完整 + +**依赖**:任务 2.2 + +--- + +#### 任务 2.5:创建机构详情 VO +- [ ] 创建 `DpcIntermediaryEntityDetailVO.java` +- [ ] 包含核心字段 + 机构专属字段 +- [ ] 添加关联字段名称 +- [ ] 添加审计字段 +- [ ] 使用 @JsonFormat 注解格式化日期 + +**验证方式**:编译通过,VO 字段完整 + +**依赖**:任务 2.3 + +--- + +### 阶段三:Excel 类创建 + +#### 任务 3.1:创建个人中介 Excel 类 +- [ ] 创建 `DpcIntermediaryPersonExcel.java` +- [ ] 使用 @ExcelProperty 注解定义 Excel 列 +- [ ] 使用 @ColumnWidth 注解设置列宽 +- [ ] 字段顺序:姓名 -> 人员类型 -> ... -> 备注(共14列) +- [ ] **不在模板中显示**:状态、数据来源字段(由系统自动设置) +- [ ] **添加字典下拉框注解**: + - `@DictDropdown(dictType = "dpc_indiv_gender")` - 性别字段 + - `@DictDropdown(dictType = "dpc_certificate_type")` - 证件类型字段 + +**验证方式**:编译通过,Excel 注解正确 + +**依赖**:任务 2.2 + +--- + +#### 任务 3.2:创建机构中介 Excel 类 +- [ ] 创建 `DpcIntermediaryEntityExcel.java` +- [ ] 使用 @ExcelProperty 注解定义 Excel 列 +- [ ] 使用 @ColumnWidth 注解设置列宽 +- [ ] 字段顺序:机构名称 -> 统一社会信用代码 -> ... -> 备注(共17列) +- [ ] **不在模板中显示**:状态、数据来源字段(由系统自动设置) +- [ ] **添加字典下拉框注解**: + - `@DictDropdown(dictType = "dpc_entity_type")` - 主体类型字段 + - `@DictDropdown(dictType = "dpc_enterprise_nature")` - 企业性质字段 + +**验证方式**:编译通过,Excel 注解正确 + +**依赖**:任务 2.3 + +--- + +### 阶段四:Service 层实现 + +#### 任务 4.1:扩展 Service 接口 +- [ ] 在 `IDpcIntermediaryBlacklistService` 中添加新方法: + - `Object selectIntermediaryDetailById(Long intermediaryId)` + - `String importPersonIntermediary(List list, boolean isUpdateSupport)` + - `String importEntityIntermediary(List list, boolean isUpdateSupport)` + +**验证方式**:编译通过,接口方法签名正确 + +**依赖**:任务 2.4, 2.5, 3.1, 3.2 + +--- + +#### 任务 4.2:实现详情查询方法 +- [ ] 在 `DpcIntermediaryBlacklistServiceImpl` 中实现 `selectIntermediaryDetailById` +- [ ] 根据中介类型返回不同的 VO +- [ ] 个人类型返回 `DpcIntermediaryPersonDetailVO` +- [ ] 机构类型返回 `DpcIntermediaryEntityDetailVO` +- [ ] 填充关联字段名称(字典转换) + +**验证方式**:单元测试验证不同类型返回正确的 VO + +**依赖**:任务 4.1 + +--- + +#### 任务 4.3:实现个人中介导入方法 +- [ ] 实现 `importPersonIntermediary` 方法 +- [ ] 验证必填字段(姓名、证件号码、状态) +- [ ] 验证字段长度限制 +- [ ] 设置中介类型为"1"(个人) +- [ ] 支持更新模式(通过证件号判断是否已存在) +- [ ] 返回导入结果统计 + +**验证方式**:单元测试验证导入逻辑和错误处理 + +**依赖**:任务 4.1 + +--- + +#### 任务 4.4:实现机构中介导入方法 +- [ ] 实现 `importEntityIntermediary` 方法 +- [ ] 验证必填字段(机构名称、状态) +- [ ] 验证字段长度限制 +- [ ] 设置中介类型为"2"(机构) +- [ ] 支持更新模式(通过统一社会信用代码判断是否已存在) +- [ ] 返回导入结果统计 + +**验证方式**:单元测试验证导入逻辑和错误处理 + +**依赖**:任务 4.1 + +--- + +### 阶段五:Controller 层实现 + +#### 任务 5.1:添加模板下载接口(支持字典下拉框) +- [ ] 添加 `POST /dpc/intermediary/importPersonTemplate` 接口 +- [ ] 添加 `POST /dpc/intermediary/importEntityTemplate` 接口 +- [ ] 使用 `EasyExcelUtil.importTemplateWithDictDropdown()` 生成模板 +- [ ] 添加 @Operation 注解用于 Swagger 文档 +- [ ] 不需要权限控制(模板下载公开) + +**验证方式**: +- 通过 Swagger UI 或 Postman 测试接口 +- 下载模板后验证字典下拉框功能是否正常 +- 验证下拉选项是否与字典数据一致 + +**依赖**:任务 3.1, 3.2, 6.1(确保字典数据已配置) + +--- + +#### 任务 5.2:添加类型化导入接口 +- [ ] 添加 `POST /dpc/intermediary/importPersonData` 接口 +- [ ] 添加 `POST /dpc/intermediary/importEntityData` 接口 +- [ ] 添加 updateSupport 参数支持更新模式 +- [ ] 添加 @PreAuthorize 权限注解 +- [ ] 添加 @Log 注解记录操作日志 +- [ ] 添加 @Operation 注解用于 Swagger 文档 + +**验证方式**:通过 Swagger UI 或 Postman 测试接口 + +**依赖**:任务 4.3, 4.4 + +--- + +#### 任务 5.3:修改详情接口 +- [ ] 修改 `GET /dpc/intermediary/{intermediaryId}` 接口 +- [ ] 返回类型改为 Object(根据实际类型返回不同 VO) +- [ ] 调用新的 `selectIntermediaryDetailById` 方法 +- [ ] 确保前端能够根据类型正确解析响应 + +**验证方式**:通过 Swagger UI 或 Postman 测试接口,验证不同类型返回不同结构 + +**依赖**:任务 4.2 + +--- + +### 阶段六:字典数据配置 + +#### 任务 6.1:创建字典数据 SQL 脚本 +- [ ] 添加人员类型字典(dpc_person_type) +- [ ] 添加人员子类型字典(dpc_person_sub_type) +- [ ] **添加性别字典(dpc_indiv_gender)** - 用于个人中介下拉框 +- [ ] **添加证件类型字典(dpc_certificate_type)** - 用于个人中介下拉框 +- [ ] **添加主体类型字典(dpc_entity_type)** - 用于机构中介下拉框 +- [ ] **添加企业性质字典(dpc_enterprise_nature)** - 用于机构中介下拉框 +- [ ] 添加数据来源字典(dpc_data_source) +- [ ] 将 SQL 脚本保存到 `sql/` 目录 + +**验证方式**:SQL 语法检查 + +**依赖**:无 + +--- + +#### 任务 6.2:执行字典数据 SQL 并预热缓存 +- [ ] 在开发环境执行字典数据 SQL +- [ ] 通过系统管理 > 字典管理验证字典数据 +- [ ] **重要**:在每个字典类型页面点击"刷新缓存"按钮,确保字典数据加载到 Redis +- [ ] 验证字典缓存是否生效(可通过测试模板下载确认下拉框是否显示) + +**验证方式**: +- 在系统界面中查看字典数据 +- 下载模板验证下拉框功能 +- 检查 Redis 缓存中的字典数据(`sys_dict_cache:*`) + +**依赖**:任务 6.1 + +--- + +### 阶段七:测试 + +#### 任务 7.1:编写单元测试 +- [ ] 测试详情查询方法(个人和机构) +- [ ] 测试个人中介导入方法 +- [ ] 测试机构中介导入方法 +- [ ] 测试数据验证逻辑 +- [ ] 测试更新模式 + +**验证方式**:所有单元测试通过 + +**依赖**:任务 4.2, 4.3, 4.4 + +--- + +#### 任务 7.2:接口集成测试 +- [ ] 测试模板下载接口(个人和机构) +- [ ] 测试数据导入接口(个人和机构) +- [ ] 测试详情查询接口 +- [ ] 测试列表接口(确保不受影响) +- [ ] 测试现有新增/编辑接口(确保不受影响) + +**验证方式**:生成测试脚本并执行,记录测试结果 + +**依赖**:任务 5.1, 5.2, 5.3 + +--- + +#### 任务 7.3:生成测试报告 +- [ ] 生成可执行的测试脚本 +- [ ] 执行测试并保存所有接口输出 +- [ ] 生成测试用例报告(Markdown 格式) +- [ ] 报告保存到 `doc/` 目录 + +**验证方式**:测试报告完整,所有测试用例通过 + +**依赖**:任务 7.2 + +--- + +### 阶段八:文档更新 + +#### 任务 8.1:更新 API 文档 +- [ ] 更新 `doc/中介黑名单API文档.md` +- [ ] 添加新增接口的文档: + - 个人模板下载接口 + - 机构模板下载接口 + - 个人数据导入接口 + - 机构数据导入接口 + - 详情接口(说明返回类型变化) +- [ ] 更新数据模型说明 + +**验证方式**:文档与实际接口一致 + +**依赖**:任务 5.1, 5.2, 5.3 + +--- + +#### 任务 8.2:更新数据库设计文档 +- [ ] 更新表结构说明 +- [ ] 添加新字段说明 +- [ ] 更新索引说明 + +**验证方式**:文档与实际表结构一致 + +**依赖**:任务 1.2 + +--- + +## 任务执行顺序建议 + +1. **第一周**:完成阶段一(数据库扩展)和阶段二(实体类和 DTO/VO) +2. **第二周**:完成阶段三(Excel 类)和阶段四(Service 层) +3. **第三周**:完成阶段五(Controller 层)和阶段六(字典数据) +4. **第四周**:完成阶段七(测试)和阶段八(文档更新) + +## 并行化建议 + +- 任务 2.2 和 2.3 可以并行开发 +- 任务 2.4 和 2.5 可以并行开发 +- 任务 3.1 和 3.2 可以并行开发 +- 任务 4.3 和 4.4 可以并行开发 +- 任务 5.1 和 5.2 可以并行开发 + +## 关键里程碑 + +1. **里程碑 1**:数据库扩展完成(任务 1.2) +2. **里程碑 2**:后端实体类和 DTO/VO 完成(任务 2.5) +3. **里程碑 3**:Excel 类创建完成(任务 3.2) +4. **里程碑 4**:Service 层实现完成(任务 4.4) +5. **里程碑 5**:Controller 层实现完成(任务 5.3) +6. **里程碑 6**:测试通过,文档更新完成(任务 8.2) diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictDropdown.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictDropdown.java new file mode 100644 index 0000000..4377109 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictDropdown.java @@ -0,0 +1,73 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * EasyExcel字典下拉框注解 + * 用于在生成Excel模板时,为字段添加基于若依字典数据的下拉框 + * + * 使用示例: + *
+ * @DictDropdown(dictType = "sys_user_sex")
+ * @ExcelProperty(value = "性别", index = 2)
+ * private String gender;
+ * 
+ * + * @author ruoyi + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.FIELD) +public @interface DictDropdown { + + /** + * 字典类型编码,对应若依框架字典管理中的字典类型 + * 如:sys_user_sex、sys_normal_disable等 + * + * @return 字典类型编码 + */ + String dictType(); + + /** + * 下拉框显示内容类型 + * LABEL: 显示字典标签(如:男、女) + * VALUE: 显示字典键值(如:0、1) + * + * @return 显示类型 + */ + DisplayType displayType() default DisplayType.LABEL; + + /** + * 是否仅允许选择下拉框中的值 + * true: 只能从下拉框中选择 + * false: 可以手动输入其他值 + * + * @return 是否仅允许选择 + */ + boolean strict() default true; + + /** + * 隐藏Sheet的名称(用于存储大量下拉选项) + * 当下拉选项超过Excel字符限制时,会创建隐藏Sheet存储选项 + * + * @return 隐藏Sheet名称 + */ + String hiddenSheetName() default "dict_hidden"; + + /** + * 显示类型枚举 + */ + enum DisplayType { + /** + * 显示字典标签 + */ + LABEL, + + /** + * 显示字典键值 + */ + VALUE + } +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/controller/DpcEmployeeController.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/controller/DpcEmployeeController.java index 464e55a..fc487d5 100644 --- a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/controller/DpcEmployeeController.java +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/controller/DpcEmployeeController.java @@ -15,7 +15,6 @@ import com.ruoyi.dpc.domain.excel.DpcEmployeeExcel; import com.ruoyi.dpc.domain.vo.DpcEmployeeVO; import com.ruoyi.dpc.service.IDpcEmployeeService; import com.ruoyi.dpc.utils.EasyExcelUtil; -import com.ruoyi.dpc.utils.handler.EmployeeStatusSheetWriteHandler; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -111,12 +110,13 @@ public class DpcEmployeeController extends BaseController { } /** - * 下载导入模板 + * 下载带字典下拉框的导入模板 + * 使用@DictDropdown注解自动添加下拉框 */ @Operation(summary = "下载导入模板") @PostMapping("/importTemplate") public void importTemplate(HttpServletResponse response) { - EasyExcelUtil.importTemplateExcel(response, DpcEmployeeExcel.class, "员工信息", new EmployeeStatusSheetWriteHandler()); + EasyExcelUtil.importTemplateWithDictDropdown(response, DpcEmployeeExcel.class, "员工信息"); } /** diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/controller/DpcIntermediaryBlacklistController.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/controller/DpcIntermediaryBlacklistController.java index b1846a1..e43fc8a 100644 --- a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/controller/DpcIntermediaryBlacklistController.java +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/controller/DpcIntermediaryBlacklistController.java @@ -13,6 +13,8 @@ import com.ruoyi.dpc.domain.dto.DpcIntermediaryBlacklistAddDTO; import com.ruoyi.dpc.domain.dto.DpcIntermediaryBlacklistEditDTO; import com.ruoyi.dpc.domain.dto.DpcIntermediaryBlacklistQueryDTO; import com.ruoyi.dpc.domain.excel.DpcIntermediaryBlacklistExcel; +import com.ruoyi.dpc.domain.excel.DpcIntermediaryEntityExcel; +import com.ruoyi.dpc.domain.excel.DpcIntermediaryPersonExcel; import com.ruoyi.dpc.domain.vo.DpcIntermediaryBlacklistVO; import com.ruoyi.dpc.service.IDpcIntermediaryBlacklistService; import com.ruoyi.dpc.utils.EasyExcelUtil; @@ -68,13 +70,13 @@ public class DpcIntermediaryBlacklistController extends BaseController { } /** - * 获取中介黑名单详细信息 + * 获取中介黑名单详细信息(根据类型返回不同结构) */ @Operation(summary = "获取中介黑名单详细信息") @PreAuthorize("@ss.hasPermi('dpc:intermediary:query')") @GetMapping(value = "/{intermediaryId}") public AjaxResult getInfo(@PathVariable Long intermediaryId) { - return success(intermediaryService.selectIntermediaryById(intermediaryId)); + return success(intermediaryService.selectIntermediaryDetailById(intermediaryId)); } /** @@ -111,24 +113,46 @@ public class DpcIntermediaryBlacklistController extends BaseController { } /** - * 下载导入模板 + * 下载个人中介导入模板(带字典下拉框) */ - @Operation(summary = "下载导入模板") - @PostMapping("/importTemplate") - public void importTemplate(HttpServletResponse response) { - EasyExcelUtil.importTemplateExcel(response, DpcIntermediaryBlacklistExcel.class, "中介黑名单"); + @Operation(summary = "下载个人中介导入模板") + @PostMapping("/importPersonTemplate") + public void importPersonTemplate(HttpServletResponse response) { + EasyExcelUtil.importTemplateWithDictDropdown(response, DpcIntermediaryPersonExcel.class, "个人中介黑名单"); } /** - * 导入中介黑名单 + * 下载机构中介导入模板(带字典下拉框) */ - @Operation(summary = "导入中介黑名单") + @Operation(summary = "下载机构中介导入模板") + @PostMapping("/importEntityTemplate") + public void importEntityTemplate(HttpServletResponse response) { + EasyExcelUtil.importTemplateWithDictDropdown(response, DpcIntermediaryEntityExcel.class, "机构中介黑名单"); + } + + /** + * 导入个人中介黑名单 + */ + @Operation(summary = "导入个人中介黑名单") @PreAuthorize("@ss.hasPermi('dpc:intermediary:import')") @Log(title = "中介黑名单", businessType = BusinessType.IMPORT) - @PostMapping("/importData") - public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception { - List list = EasyExcelUtil.importExcel(file.getInputStream(), DpcIntermediaryBlacklistExcel.class); - String message = intermediaryService.importIntermediary(list, updateSupport); + @PostMapping("/importPersonData") + public AjaxResult importPersonData(MultipartFile file, boolean updateSupport) throws Exception { + List list = EasyExcelUtil.importExcel(file.getInputStream(), DpcIntermediaryPersonExcel.class); + String message = intermediaryService.importPersonIntermediary(list, updateSupport); + return success(message); + } + + /** + * 导入机构中介黑名单 + */ + @Operation(summary = "导入机构中介黑名单") + @PreAuthorize("@ss.hasPermi('dpc:intermediary:import')") + @Log(title = "中介黑名单", businessType = BusinessType.IMPORT) + @PostMapping("/importEntityData") + public AjaxResult importEntityData(MultipartFile file, boolean updateSupport) throws Exception { + List list = EasyExcelUtil.importExcel(file.getInputStream(), DpcIntermediaryEntityExcel.class); + String message = intermediaryService.importEntityIntermediary(list, updateSupport); return success(message); } } diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/DpcIntermediaryBlacklist.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/DpcIntermediaryBlacklist.java index 0847dec..cf22e55 100644 --- a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/DpcIntermediaryBlacklist.java +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/DpcIntermediaryBlacklist.java @@ -41,6 +41,96 @@ public class DpcIntermediaryBlacklist implements Serializable { /** 备注 */ private String remark; + // ============================================================ + // 个人类型字段 (以 indiv_ 前缀标识,individual 缩写) + // ============================================================ + /** 人员类型(中介、职业背债人、房产中介等) */ + private String indivType; + + /** 人员子类型(本人、配偶等) */ + private String indivSubType; + + /** 性别(M男 F女 O其他) */ + private String indivGender; + + /** 证件类型 */ + private String indivCertType; + + /** 手机号码(加密存储) */ + private String indivPhone; + + /** 微信号 */ + private String indivWechat; + + /** 联系地址 */ + private String indivAddress; + + /** 所在公司 */ + private String indivCompany; + + /** 职位/职务 */ + private String indivPosition; + + /** 关联人员ID */ + private String indivRelatedId; + + /** 关联关系 */ + private String indivRelation; + + // ============================================================ + // 机构类型字段 (以 corp_ 前缀标识,corporation 缩写) + // ============================================================ + /** 统一社会信用代码 */ + private String corpCreditCode; + + /** 主体类型(有限责任公司、股份有限公司等) */ + private String corpType; + + /** 企业性质(国企、民企、外企等) */ + private String corpNature; + + /** 行业分类 */ + private String corpIndustryCategory; + + /** 所属行业 */ + private String corpIndustry; + + /** 成立日期 */ + private Date corpEstablishDate; + + /** 注册地址 */ + private String corpAddress; + + /** 法定代表人 */ + private String corpLegalRep; + + /** 法定代表人证件类型 */ + private String corpLegalCertType; + + /** 法定代表人证件号码 */ + private String corpLegalCertNo; + + /** 股东1 */ + private String corpShareholder1; + + /** 股东2 */ + private String corpShareholder2; + + /** 股东3 */ + private String corpShareholder3; + + /** 股东4 */ + private String corpShareholder4; + + /** 股东5 */ + private String corpShareholder5; + + // ============================================================ + // 通用字段 + // ============================================================ + /** 数据来源(MANUAL手动录入 SYSTEM系统同步 IMPORT批量导入 API接口获取) */ + private String dataSource; + /** 创建者 */ @TableField(fill = FieldFill.INSERT) private String createBy; diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/dto/DpcIntermediaryEntityAddDTO.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/dto/DpcIntermediaryEntityAddDTO.java new file mode 100644 index 0000000..8318070 --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/dto/DpcIntermediaryEntityAddDTO.java @@ -0,0 +1,239 @@ +package com.ruoyi.dpc.domain.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 机构中介黑名单新增 DTO + * + * @author ruoyi + * @date 2026-01-29 + */ +public class DpcIntermediaryEntityAddDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 机构名称 */ + @NotBlank(message = "机构名称不能为空") + @Size(min = 1, max = 100, message = "机构名称长度不能超过100个字符") + private String name; + + /** 状态 */ + @NotBlank(message = "状态不能为空") + private String status; + + /** 备注 */ + @Size(max = 500, message = "备注长度不能超过500个字符") + private String remark; + + // ============================================================ + // 机构专属字段 + // ============================================================ + /** 统一社会信用代码 */ + @Size(max = 18, message = "统一社会信用代码长度不能超过18个字符") + private String corpCreditCode; + + /** 主体类型 */ + @Size(max = 50, message = "主体类型长度不能超过50个字符") + private String corpType; + + /** 企业性质 */ + @Size(max = 50, message = "企业性质长度不能超过50个字符") + private String corpNature; + + /** 行业分类 */ + @Size(max = 100, message = "行业分类长度不能超过100个字符") + private String corpIndustryCategory; + + /** 所属行业 */ + @Size(max = 100, message = "所属行业长度不能超过100个字符") + private String corpIndustry; + + /** 成立日期 */ + private Date corpEstablishDate; + + /** 注册地址 */ + @Size(max = 500, message = "注册地址长度不能超过500个字符") + private String corpAddress; + + /** 法定代表人 */ + @Size(max = 50, message = "法定代表人长度不能超过50个字符") + private String corpLegalRep; + + /** 法定代表人证件类型 */ + @Size(max = 30, message = "法定代表人证件类型长度不能超过30个字符") + private String corpLegalCertType; + + /** 法定代表人证件号码 */ + @Size(max = 30, message = "法定代表人证件号码长度不能超过30个字符") + private String corpLegalCertNo; + + /** 股东1 */ + @Size(max = 30, message = "股东1长度不能超过30个字符") + private String corpShareholder1; + + /** 股东2 */ + @Size(max = 30, message = "股东2长度不能超过30个字符") + private String corpShareholder2; + + /** 股东3 */ + @Size(max = 30, message = "股东3长度不能超过30个字符") + private String corpShareholder3; + + /** 股东4 */ + @Size(max = 30, message = "股东4长度不能超过30个字符") + private String corpShareholder4; + + /** 股东5 */ + @Size(max = 30, message = "股东5长度不能超过30个字符") + private String corpShareholder5; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getCorpCreditCode() { + return corpCreditCode; + } + + public void setCorpCreditCode(String corpCreditCode) { + this.corpCreditCode = corpCreditCode; + } + + public String getCorpType() { + return corpType; + } + + public void setCorpType(String corpType) { + this.corpType = corpType; + } + + public String getCorpNature() { + return corpNature; + } + + public void setCorpNature(String corpNature) { + this.corpNature = corpNature; + } + + public String getCorpIndustryCategory() { + return corpIndustryCategory; + } + + public void setCorpIndustryCategory(String corpIndustryCategory) { + this.corpIndustryCategory = corpIndustryCategory; + } + + public String getCorpIndustry() { + return corpIndustry; + } + + public void setCorpIndustry(String corpIndustry) { + this.corpIndustry = corpIndustry; + } + + public Date getCorpEstablishDate() { + return corpEstablishDate; + } + + public void setCorpEstablishDate(Date corpEstablishDate) { + this.corpEstablishDate = corpEstablishDate; + } + + public String getCorpAddress() { + return corpAddress; + } + + public void setCorpAddress(String corpAddress) { + this.corpAddress = corpAddress; + } + + public String getCorpLegalRep() { + return corpLegalRep; + } + + public void setCorpLegalRep(String corpLegalRep) { + this.corpLegalRep = corpLegalRep; + } + + public String getCorpLegalCertType() { + return corpLegalCertType; + } + + public void setCorpLegalCertType(String corpLegalCertType) { + this.corpLegalCertType = corpLegalCertType; + } + + public String getCorpLegalCertNo() { + return corpLegalCertNo; + } + + public void setCorpLegalCertNo(String corpLegalCertNo) { + this.corpLegalCertNo = corpLegalCertNo; + } + + public String getCorpShareholder1() { + return corpShareholder1; + } + + public void setCorpShareholder1(String corpShareholder1) { + this.corpShareholder1 = corpShareholder1; + } + + public String getCorpShareholder2() { + return corpShareholder2; + } + + public void setCorpShareholder2(String corpShareholder2) { + this.corpShareholder2 = corpShareholder2; + } + + public String getCorpShareholder3() { + return corpShareholder3; + } + + public void setCorpShareholder3(String corpShareholder3) { + this.corpShareholder3 = corpShareholder3; + } + + public String getCorpShareholder4() { + return corpShareholder4; + } + + public void setCorpShareholder4(String corpShareholder4) { + this.corpShareholder4 = corpShareholder4; + } + + public String getCorpShareholder5() { + return corpShareholder5; + } + + public void setCorpShareholder5(String corpShareholder5) { + this.corpShareholder5 = corpShareholder5; + } +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/dto/DpcIntermediaryPersonAddDTO.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/dto/DpcIntermediaryPersonAddDTO.java new file mode 100644 index 0000000..993de3d --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/dto/DpcIntermediaryPersonAddDTO.java @@ -0,0 +1,204 @@ +package com.ruoyi.dpc.domain.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 个人中介黑名单新增 DTO + * + * @author ruoyi + * @date 2026-01-29 + */ +public class DpcIntermediaryPersonAddDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** 姓名 */ + @NotBlank(message = "姓名不能为空") + @Size(min = 1, max = 100, message = "姓名长度不能超过100个字符") + private String name; + + /** 证件号码 */ + @NotBlank(message = "证件号码不能为空") + @Size(max = 50, message = "证件号码长度不能超过50个字符") + private String certificateNo; + + /** 状态 */ + @NotBlank(message = "状态不能为空") + private String status; + + /** 备注 */ + @Size(max = 500, message = "备注长度不能超过500个字符") + private String remark; + + // ============================================================ + // 个人专属字段 + // ============================================================ + /** 人员类型 */ + @Size(max = 30, message = "人员类型长度不能超过30个字符") + private String indivType; + + /** 人员子类型 */ + @Size(max = 50, message = "人员子类型长度不能超过50个字符") + private String indivSubType; + + /** 性别 */ + @Size(max = 1, message = "性别长度不能超过1个字符") + private String indivGender; + + /** 证件类型 */ + @Size(max = 30, message = "证件类型长度不能超过30个字符") + private String indivCertType; + + /** 手机号码 */ + @Size(max = 20, message = "手机号码长度不能超过20个字符") + private String indivPhone; + + /** 微信号 */ + @Size(max = 50, message = "微信号长度不能超过50个字符") + private String indivWechat; + + /** 联系地址 */ + @Size(max = 200, message = "联系地址长度不能超过200个字符") + private String indivAddress; + + /** 所在公司 */ + @Size(max = 100, message = "所在公司长度不能超过100个字符") + private String indivCompany; + + /** 职位/职务 */ + @Size(max = 100, message = "职位长度不能超过100个字符") + private String indivPosition; + + /** 关联人员ID */ + @Size(max = 20, message = "关联人员ID长度不能超过20个字符") + private String indivRelatedId; + + /** 关联关系 */ + @Size(max = 50, message = "关联关系长度不能超过50个字符") + private String indivRelation; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCertificateNo() { + return certificateNo; + } + + public void setCertificateNo(String certificateNo) { + this.certificateNo = certificateNo; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getIndivType() { + return indivType; + } + + public void setIndivType(String indivType) { + this.indivType = indivType; + } + + public String getIndivSubType() { + return indivSubType; + } + + public void setIndivSubType(String indivSubType) { + this.indivSubType = indivSubType; + } + + public String getIndivGender() { + return indivGender; + } + + public void setIndivGender(String indivGender) { + this.indivGender = indivGender; + } + + public String getIndivCertType() { + return indivCertType; + } + + public void setIndivCertType(String indivCertType) { + this.indivCertType = indivCertType; + } + + public String getIndivPhone() { + return indivPhone; + } + + public void setIndivPhone(String indivPhone) { + this.indivPhone = indivPhone; + } + + public String getIndivWechat() { + return indivWechat; + } + + public void setIndivWechat(String indivWechat) { + this.indivWechat = indivWechat; + } + + public String getIndivAddress() { + return indivAddress; + } + + public void setIndivAddress(String indivAddress) { + this.indivAddress = indivAddress; + } + + public String getIndivCompany() { + return indivCompany; + } + + public void setIndivCompany(String indivCompany) { + this.indivCompany = indivCompany; + } + + public String getIndivPosition() { + return indivPosition; + } + + public void setIndivPosition(String indivPosition) { + this.indivPosition = indivPosition; + } + + public String getIndivRelatedId() { + return indivRelatedId; + } + + public void setIndivRelatedId(String indivRelatedId) { + this.indivRelatedId = indivRelatedId; + } + + public String getIndivRelation() { + return indivRelation; + } + + public void setIndivRelation(String indivRelation) { + this.indivRelation = indivRelation; + } +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcEmployeeExcel.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcEmployeeExcel.java index ca90d43..55b21fa 100644 --- a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcEmployeeExcel.java +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcEmployeeExcel.java @@ -2,7 +2,7 @@ package com.ruoyi.dpc.domain.excel; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.write.style.ColumnWidth; -import com.ruoyi.dpc.utils.converter.EmployeeStatusConverter; +import com.ruoyi.common.annotation.DictDropdown; import lombok.Data; import java.io.Serial; @@ -52,7 +52,8 @@ public class DpcEmployeeExcel implements Serializable { private Date hireDate; /** 状态 */ - @ExcelProperty(value = "状态", converter = EmployeeStatusConverter.class, index = 6) + @ExcelProperty(value = "状态", index = 6) @ColumnWidth(10) + @DictDropdown(dictType = "dpc_employee_status") private String status; } diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcIntermediaryEntityExcel.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcIntermediaryEntityExcel.java new file mode 100644 index 0000000..160f5f6 --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcIntermediaryEntityExcel.java @@ -0,0 +1,96 @@ +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; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 机构中介黑名单Excel导入对象 + * + * @author ruoyi + * @date 2026-01-29 + */ +@Data +public class DpcIntermediaryEntityExcel implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "机构名称", index = 0) + @ColumnWidth(25) + private String name; + + @ExcelProperty(value = "统一社会信用代码", index = 1) + @ColumnWidth(20) + private String corpCreditCode; + + @ExcelProperty(value = "主体类型", index = 2) + @ColumnWidth(20) + @DictDropdown(dictType = "dpc_entity_type") + private String corpType; + + @ExcelProperty(value = "企业性质", index = 3) + @ColumnWidth(15) + @DictDropdown(dictType = "dpc_enterprise_nature") + private String corpNature; + + @ExcelProperty(value = "行业分类", index = 4) + @ColumnWidth(15) + private String corpIndustryCategory; + + @ExcelProperty(value = "所属行业", index = 5) + @ColumnWidth(15) + private String corpIndustry; + + @ExcelProperty(value = "成立日期", index = 6) + @ColumnWidth(15) + private String corpEstablishDate; + + @ExcelProperty(value = "注册地址", index = 7) + @ColumnWidth(40) + private String corpAddress; + + @ExcelProperty(value = "法定代表人", index = 8) + @ColumnWidth(15) + private String corpLegalRep; + + @ExcelProperty(value = "法定代表人证件类型", index = 9) + @ColumnWidth(20) + private String corpLegalCertType; + + @ExcelProperty(value = "法定代表人证件号码", index = 10) + @ColumnWidth(20) + private String corpLegalCertNo; + + @ExcelProperty(value = "股东1", index = 11) + @ColumnWidth(15) + private String corpShareholder1; + + @ExcelProperty(value = "股东2", index = 12) + @ColumnWidth(15) + private String corpShareholder2; + + @ExcelProperty(value = "股东3", index = 13) + @ColumnWidth(15) + private String corpShareholder3; + + @ExcelProperty(value = "股东4", index = 14) + @ColumnWidth(15) + private String corpShareholder4; + + @ExcelProperty(value = "股东5", index = 15) + @ColumnWidth(15) + private String corpShareholder5; + + @ExcelProperty(value = "备注", index = 16) + @ColumnWidth(30) + private String remark; + + // 以下字段不在 Excel 中显示,由系统自动设置 + // private String status; // 默认:正常(0) + // private String dataSource; // 默认:批量导入(IMPORT) +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcIntermediaryPersonExcel.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcIntermediaryPersonExcel.java new file mode 100644 index 0000000..6cc3011 --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/excel/DpcIntermediaryPersonExcel.java @@ -0,0 +1,84 @@ +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; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 个人中介黑名单Excel导入对象 + * + * @author ruoyi + * @date 2026-01-29 + */ +@Data +public class DpcIntermediaryPersonExcel implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @ExcelProperty(value = "姓名", index = 0) + @ColumnWidth(15) + private String name; + + @ExcelProperty(value = "人员类型", index = 1) + @ColumnWidth(15) + private String indivType; + + @ExcelProperty(value = "人员子类型", index = 2) + @ColumnWidth(15) + private String indivSubType; + + @ExcelProperty(value = "性别", index = 3) + @ColumnWidth(10) + @DictDropdown(dictType = "dpc_indiv_gender") + private String indivGender; + + @ExcelProperty(value = "证件类型", index = 4) + @ColumnWidth(15) + @DictDropdown(dictType = "dpc_certificate_type") + private String indivCertType; + + @ExcelProperty(value = "证件号码", index = 5) + @ColumnWidth(20) + private String certificateNo; + + @ExcelProperty(value = "手机号码", index = 6) + @ColumnWidth(15) + private String indivPhone; + + @ExcelProperty(value = "微信号", index = 7) + @ColumnWidth(15) + private String indivWechat; + + @ExcelProperty(value = "联系地址", index = 8) + @ColumnWidth(30) + private String indivAddress; + + @ExcelProperty(value = "所在公司", index = 9) + @ColumnWidth(20) + private String indivCompany; + + @ExcelProperty(value = "职位", index = 10) + @ColumnWidth(15) + private String indivPosition; + + @ExcelProperty(value = "关联人员ID", index = 11) + @ColumnWidth(15) + private String indivRelatedId; + + @ExcelProperty(value = "关联关系", index = 12) + @ColumnWidth(15) + private String indivRelation; + + @ExcelProperty(value = "备注", index = 13) + @ColumnWidth(30) + private String remark; + + // 以下字段不在 Excel 中显示,由系统自动设置 + // private String status; // 默认:正常(0) + // private String dataSource; // 默认:批量导入(IMPORT) +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryBlacklistVO.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryBlacklistVO.java index 946c79a..3a2eac2 100644 --- a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryBlacklistVO.java +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryBlacklistVO.java @@ -1,6 +1,7 @@ package com.ruoyi.dpc.domain.vo; import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; import java.io.Serial; import java.io.Serializable; @@ -12,6 +13,7 @@ import java.util.Date; * @author ruoyi * @date 2026-01-27 */ +@Data public class DpcIntermediaryBlacklistVO implements Serializable { @Serial @@ -54,100 +56,4 @@ public class DpcIntermediaryBlacklistVO implements Serializable { /** 更新时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date updateTime; - - public Long getIntermediaryId() { - return intermediaryId; - } - - public void setIntermediaryId(Long intermediaryId) { - this.intermediaryId = intermediaryId; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getCertificateNo() { - return certificateNo; - } - - public void setCertificateNo(String certificateNo) { - this.certificateNo = certificateNo; - } - - public String getIntermediaryType() { - return intermediaryType; - } - - public void setIntermediaryType(String intermediaryType) { - this.intermediaryType = intermediaryType; - } - - public String getIntermediaryTypeName() { - return intermediaryTypeName; - } - - public void setIntermediaryTypeName(String intermediaryTypeName) { - this.intermediaryTypeName = intermediaryTypeName; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public String getStatusName() { - return statusName; - } - - public void setStatusName(String statusName) { - this.statusName = statusName; - } - - public String getRemark() { - return remark; - } - - public void setRemark(String remark) { - this.remark = remark; - } - - public String getCreateBy() { - return createBy; - } - - public void setCreateBy(String createBy) { - this.createBy = createBy; - } - - public Date getCreateTime() { - return createTime; - } - - public void setCreateTime(Date createTime) { - this.createTime = createTime; - } - - public String getUpdateBy() { - return updateBy; - } - - public void setUpdateBy(String updateBy) { - this.updateBy = updateBy; - } - - public Date getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(Date updateTime) { - this.updateTime = updateTime; - } } diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryEntityDetailVO.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryEntityDetailVO.java new file mode 100644 index 0000000..c54c097 --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryEntityDetailVO.java @@ -0,0 +1,126 @@ +package com.ruoyi.dpc.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 机构中介黑名单详情 VO + * + * @author ruoyi + * @date 2026-01-29 + */ +@Data +public class DpcIntermediaryEntityDetailVO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + // ============================================================ + // 核心字段 + // ============================================================ + /** 中介ID */ + private Long intermediaryId; + + /** 机构名称 */ + private String name; + + /** 证件号码 */ + private String certificateNo; + + /** 中介类型 */ + private String intermediaryType; + + /** 中介类型名称 */ + private String intermediaryTypeName; + + /** 状态 */ + private String status; + + /** 状态名称 */ + private String statusName; + + /** 备注 */ + private String remark; + + /** 数据来源 */ + private String dataSource; + + /** 数据来源名称 */ + private String dataSourceName; + + // ============================================================ + // 机构专属字段 + // ============================================================ + /** 统一社会信用代码 */ + private String corpCreditCode; + + /** 主体类型 */ + private String corpType; + + /** 主体类型名称 */ + private String corpTypeName; + + /** 企业性质 */ + private String corpNature; + + /** 企业性质名称 */ + private String corpNatureName; + + /** 行业分类 */ + private String corpIndustryCategory; + + /** 所属行业 */ + private String corpIndustry; + + /** 成立日期 */ + @JsonFormat(pattern = "yyyy-MM-dd") + private Date corpEstablishDate; + + /** 注册地址 */ + private String corpAddress; + + /** 法定代表人 */ + private String corpLegalRep; + + /** 法定代表人证件类型 */ + private String corpLegalCertType; + + /** 法定代表人证件号码 */ + private String corpLegalCertNo; + + /** 股东1 */ + private String corpShareholder1; + + /** 股东2 */ + private String corpShareholder2; + + /** 股东3 */ + private String corpShareholder3; + + /** 股东4 */ + private String corpShareholder4; + + /** 股东5 */ + private String corpShareholder5; + + // ============================================================ + // 审计字段 + // ============================================================ + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryPersonDetailVO.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryPersonDetailVO.java new file mode 100644 index 0000000..e20cf2b --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/domain/vo/DpcIntermediaryPersonDetailVO.java @@ -0,0 +1,113 @@ +package com.ruoyi.dpc.domain.vo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 个人中介黑名单详情 VO + * + * @author ruoyi + * @date 2026-01-29 + */ +@Data +public class DpcIntermediaryPersonDetailVO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + // ============================================================ + // 核心字段 + // ============================================================ + /** 中介ID */ + private Long intermediaryId; + + /** 姓名 */ + private String name; + + /** 证件号码 */ + private String certificateNo; + + /** 中介类型 */ + private String intermediaryType; + + /** 中介类型名称 */ + private String intermediaryTypeName; + + /** 状态 */ + private String status; + + /** 状态名称 */ + private String statusName; + + /** 备注 */ + private String remark; + + /** 数据来源 */ + private String dataSource; + + /** 数据来源名称 */ + private String dataSourceName; + + // ============================================================ + // 个人专属字段 + // ============================================================ + /** 人员类型 */ + private String indivType; + + /** 人员子类型 */ + private String indivSubType; + + /** 性别 */ + private String indivGender; + + /** 性别名称 */ + private String indivGenderName; + + /** 证件类型 */ + private String indivCertType; + + /** 证件类型名称 */ + private String indivCertTypeName; + + /** 手机号码 */ + private String indivPhone; + + /** 微信号 */ + private String indivWechat; + + /** 联系地址 */ + private String indivAddress; + + /** 所在公司 */ + private String indivCompany; + + /** 职位/职务 */ + private String indivPosition; + + /** 关联人员ID */ + private String indivRelatedId; + + /** 关联关系 */ + private String indivRelation; + + // ============================================================ + // 审计字段 + // ============================================================ + /** 创建者 */ + private String createBy; + + /** 创建时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** 更新者 */ + private String updateBy; + + /** 更新时间 */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/DataSource.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/DataSource.java new file mode 100644 index 0000000..03d1ff3 --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/DataSource.java @@ -0,0 +1,41 @@ +package com.ruoyi.dpc.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据来源枚举 + * + * @author ruoyi + */ +@Getter +@AllArgsConstructor +public enum DataSource { + + /** 手动录入 */ + MANUAL("MANUAL", "手动录入"), + + /** 系统同步 */ + SYSTEM("SYSTEM", "系统同步"), + + /** 批量导入 */ + IMPORT("IMPORT", "批量导入"), + + /** 接口获取 */ + API("API", "接口获取"); + + private final String code; + private final String desc; + + /** + * 根据编码获取描述 + */ + public static String getDescByCode(String code) { + for (DataSource source : values()) { + if (source.getCode().equals(code)) { + return source.getDesc(); + } + } + return null; + } +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/EmployeeStatus.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/EmployeeStatus.java new file mode 100644 index 0000000..48391db --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/EmployeeStatus.java @@ -0,0 +1,35 @@ +package com.ruoyi.dpc.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 员工状态枚举 + * + * @author ruoyi + */ +@Getter +@AllArgsConstructor +public enum EmployeeStatus { + + /** 在职 */ + ACTIVE("0", "在职"), + + /** 离职 */ + INACTIVE("1", "离职"); + + private final String code; + private final String desc; + + /** + * 根据编码获取描述 + */ + public static String getDescByCode(String code) { + for (EmployeeStatus status : values()) { + if (status.getCode().equals(code)) { + return status.getDesc(); + } + } + return null; + } +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/Gender.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/Gender.java new file mode 100644 index 0000000..85dcf7b --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/Gender.java @@ -0,0 +1,38 @@ +package com.ruoyi.dpc.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 性别枚举 + * + * @author ruoyi + */ +@Getter +@AllArgsConstructor +public enum Gender { + + /** 男 */ + MALE("M", "男"), + + /** 女 */ + FEMALE("F", "女"), + + /** 其他 */ + OTHER("O", "其他"); + + private final String code; + private final String desc; + + /** + * 根据编码获取描述 + */ + public static String getDescByCode(String code) { + for (Gender gender : values()) { + if (gender.getCode().equals(code)) { + return gender.getDesc(); + } + } + return null; + } +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/IntermediaryStatus.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/IntermediaryStatus.java new file mode 100644 index 0000000..fb05b8d --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/IntermediaryStatus.java @@ -0,0 +1,35 @@ +package com.ruoyi.dpc.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 中介状态枚举 + * + * @author ruoyi + */ +@Getter +@AllArgsConstructor +public enum IntermediaryStatus { + + /** 正常 */ + NORMAL("0", "正常"), + + /** 停用 */ + DISABLED("1", "停用"); + + private final String code; + private final String desc; + + /** + * 根据编码获取描述 + */ + public static String getDescByCode(String code) { + for (IntermediaryStatus status : values()) { + if (status.getCode().equals(code)) { + return status.getDesc(); + } + } + return null; + } +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/IntermediaryType.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/IntermediaryType.java new file mode 100644 index 0000000..72ea9f5 --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/enums/IntermediaryType.java @@ -0,0 +1,35 @@ +package com.ruoyi.dpc.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 中介类型枚举 + * + * @author ruoyi + */ +@Getter +@AllArgsConstructor +public enum IntermediaryType { + + /** 个人 */ + PERSON("1", "个人"), + + /** 机构 */ + ENTITY("2", "机构"); + + private final String code; + private final String desc; + + /** + * 根据编码获取描述 + */ + public static String getDescByCode(String code) { + for (IntermediaryType type : values()) { + if (type.getCode().equals(code)) { + return type.getDesc(); + } + } + return null; + } +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/handler/DictDropdownWriteHandler.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/handler/DictDropdownWriteHandler.java new file mode 100644 index 0000000..df14fea --- /dev/null +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/handler/DictDropdownWriteHandler.java @@ -0,0 +1,336 @@ +package com.ruoyi.dpc.handler; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.write.handler.SheetWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder; +import com.ruoyi.common.annotation.DictDropdown; +import com.ruoyi.common.core.domain.entity.SysDictData; +import com.ruoyi.common.utils.DictUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddressList; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * EasyExcel字典下拉框写入处理器 + * 在Excel模板生成时,为标注了@DictDropdown注解的字段添加下拉框 + * + * @author ruoyi + */ +@Slf4j +public class DictDropdownWriteHandler implements SheetWriteHandler { + + /** + * Excel下拉列表直接写入的最大字符数限制 + */ + private static final int MAX_DIRECT_LENGTH = 255; + + /** + * 实体类Class对象 + */ + private final Class modelClass; + + /** + * 构造函数 + * + * @param modelClass 实体类Class对象 + */ + public DictDropdownWriteHandler(Class modelClass) { + this.modelClass = modelClass; + } + + @Override + public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) { + // 获取工作簿和工作表 + Workbook workbook = writeWorkbookHolder.getWorkbook(); + Sheet sheet = writeSheetHolder.getSheet(); + + // 创建数据验证助手 + DataValidationHelper validationHelper = sheet.getDataValidationHelper(); + + // 解析实体类中的字段及其注解 + Map dropdownMap = parseDropdownFields(); + + // 为每个需要下拉框的字段添加数据验证 + for (Map.Entry entry : dropdownMap.entrySet()) { + Integer columnIndex = entry.getKey(); + DictDropdown dropdown = entry.getValue(); + + try { + // 获取字典数据 + List dictDataList = getDictData(dropdown.dictType()); + if (dictDataList == null || dictDataList.isEmpty()) { + log.warn("字典类型[{}]没有可用数据,跳过下拉框创建", dropdown.dictType()); + continue; + } + + // 获取下拉选项列表 + String[] dropdownOptions = extractOptions(dictDataList, dropdown.displayType()); + if (dropdownOptions.length == 0) { + log.warn("字典类型[{}]提取选项为空,跳过下拉框创建", dropdown.dictType()); + continue; + } + + // 创建数据验证 + DataValidation validation = createDataValidation( + workbook, + sheet, + validationHelper, + columnIndex, + dropdownOptions, + dropdown.hiddenSheetName(), + dropdown.strict() + ); + + if (validation != null) { + sheet.addValidationData(validation); + log.info("成功为列[{}]添加字典[{}]的下拉框,选项数量:{}", columnIndex, dropdown.dictType(), dropdownOptions.length); + } + } catch (Exception e) { + log.error("为列[{}]添加字典[{}]下拉框失败:{}", columnIndex, dropdown.dictType(), e.getMessage(), e); + } + } + } + + /** + * 解析实体类中的字段,获取需要添加下拉框的字段及其注解 + * + * @return 字段索引与注解的映射 + */ + private Map parseDropdownFields() { + Map result = new HashMap<>(); + + // 获取所有字段(包括父类的) + List fields = getAllFields(modelClass); + + for (Field field : fields) { + // 检查是否有@DictDropdown注解 + DictDropdown dropdown = field.getAnnotation(DictDropdown.class); + if (dropdown == null) { + continue; + } + + // 获取列索引 + Integer columnIndex = getColumnIndex(field); + if (columnIndex == null) { + log.warn("字段[{}]没有指定@ExcelProperty的index,跳过下拉框创建", field.getName()); + continue; + } + + result.put(columnIndex, dropdown); + } + + return result; + } + + /** + * 获取类的所有字段(包括父类的) + * + * @param clazz 类对象 + * @return 字段列表 + */ + private List getAllFields(Class clazz) { + List fields = new ArrayList<>(); + while (clazz != null && clazz != Object.class) { + fields.addAll(Arrays.asList(clazz.getDeclaredFields())); + clazz = clazz.getSuperclass(); + } + return fields; + } + + /** + * 获取字段对应的列索引 + * + * @param field 字段对象 + * @return 列索引 + */ + private Integer getColumnIndex(Field field) { + ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class); + if (excelProperty != null && excelProperty.index() >= 0) { + return excelProperty.index(); + } + return null; + } + + /** + * 获取字典数据 + * + * @param dictType 字典类型 + * @return 字典数据列表 + */ + private List getDictData(String dictType) { + try { + // 先从缓存获取 + List dictDataList = DictUtils.getDictCache(dictType); + + if (dictDataList == null || dictDataList.isEmpty()) { + log.warn("从缓存获取字典[{}]数据为空", dictType); + } + + return dictDataList; + } catch (Exception e) { + log.error("获取字典[{}]数据失败:{}", dictType, e.getMessage(), e); + return Collections.emptyList(); + } + } + + /** + * 从字典数据中提取下拉选项 + * + * @param dictDataList 字典数据列表 + * @param displayType 显示类型 + * @return 下拉选项数组 + */ + private String[] extractOptions(List dictDataList, DictDropdown.DisplayType displayType) { + List options = new ArrayList<>(); + + for (SysDictData dictData : dictDataList) { + String optionValue; + if (displayType == DictDropdown.DisplayType.VALUE) { + optionValue = dictData.getDictValue(); + } else { + optionValue = dictData.getDictLabel(); + } + + if (optionValue != null && !optionValue.isEmpty()) { + options.add(optionValue); + } + } + + return options.toArray(new String[0]); + } + + /** + * 创建数据验证 + * + * @param workbook 工作簿 + * @param sheet 工作表 + * @param validationHelper 数据验证助手 + * @param columnIndex 列索引 + * @param options 下拉选项 + * @param hiddenSheetName 隐藏Sheet名称 + * @param strict 是否严格模式 + * @return 数据验证对象 + */ + private DataValidation createDataValidation( + Workbook workbook, + Sheet sheet, + DataValidationHelper validationHelper, + int columnIndex, + String[] options, + String hiddenSheetName, + boolean strict + ) { + // 计算选项字符串总长度 + int totalLength = Arrays.stream(options) + .mapToInt(String::length) + .sum() + options.length - 1; // 加上分隔符 + + // 设置数据验证的范围(从第2行开始,第1行是表头) + int firstRow = 1; // 从第2行开始(0-based索引) + int lastRow = sheet.getLastRowNum() + 1000; // 扩展到足够多的行 + if (lastRow < 100) { + lastRow = 100; // 至少100行 + } + CellRangeAddressList addressList = new CellRangeAddressList(firstRow, lastRow, columnIndex, columnIndex); + + // 判断是否需要使用隐藏Sheet + if (totalLength > MAX_DIRECT_LENGTH) { + // 使用隐藏Sheet存储选项 + return createHiddenSheetValidation(workbook, sheet, validationHelper, addressList, options, hiddenSheetName, strict); + } else { + // 直接创建下拉列表 + return createDirectValidation(validationHelper, addressList, options, strict); + } + } + + /** + * 使用隐藏Sheet创建数据验证(适用于选项较多的情况) + * + * @param workbook 工作簿 + * @param sheet 主工作表 + * @param validationHelper 数据验证助手 + * @param addressList 单元格范围 + * @param options 下拉选项 + * @param hiddenSheetName 隐藏Sheet名称 + * @param strict 是否严格模式 + * @return 数据验证对象 + */ + private DataValidation createHiddenSheetValidation( + Workbook workbook, + Sheet sheet, + DataValidationHelper validationHelper, + CellRangeAddressList addressList, + String[] options, + String hiddenSheetName, + boolean strict + ) { + try { + // 创建或获取隐藏Sheet + Sheet hiddenSheet = workbook.getSheet(hiddenSheetName); + if (hiddenSheet == null) { + hiddenSheet = workbook.createSheet(hiddenSheetName); + } + + // 写入选项到隐藏Sheet + Row row = hiddenSheet.createRow(0); + for (int i = 0; i < options.length; i++) { + Cell cell = row.createCell(i); + cell.setCellValue(options[i]); + } + + // 构建公式引用 + String formula = String.format("%s!$A$1:$%s$1", hiddenSheetName, + (char) ('A' + options.length - 1)); + + // 创建列表验证 + DataValidationConstraint constraint = validationHelper.createFormulaListConstraint(formula); + + // 设置验证属性 + DataValidation validation = validationHelper.createValidation(constraint, addressList); + validation.setSuppressDropDownArrow(true); + validation.setErrorStyle(strict ? DataValidation.ErrorStyle.STOP : DataValidation.ErrorStyle.WARNING); + validation.setShowErrorBox(true); + + // 隐藏Sheet(在Excel中无法通过界面隐藏,需要通过代码设置) + workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheetName), true); + + return validation; + } catch (Exception e) { + log.error("创建隐藏Sheet数据验证失败:{}", e.getMessage(), e); + return null; + } + } + + /** + * 直接创建下拉列表验证(适用于选项较少的情况) + * + * @param validationHelper 数据验证助手 + * @param addressList 单元格范围 + * @param options 下拉选项 + * @param strict 是否严格模式 + * @return 数据验证对象 + */ + private DataValidation createDirectValidation( + DataValidationHelper validationHelper, + CellRangeAddressList addressList, + String[] options, + boolean strict + ) { + // 创建显式列表验证 + DataValidationConstraint constraint = validationHelper.createExplicitListConstraint(options); + + // 创建数据验证 + DataValidation validation = validationHelper.createValidation(constraint, addressList); + + // 设置验证属性 + validation.setSuppressDropDownArrow(true); + validation.setErrorStyle(strict ? DataValidation.ErrorStyle.STOP : DataValidation.ErrorStyle.WARNING); + validation.setShowErrorBox(true); + + return validation; + } +} diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/IDpcIntermediaryBlacklistService.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/IDpcIntermediaryBlacklistService.java index afc698e..8b7639a 100644 --- a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/IDpcIntermediaryBlacklistService.java +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/IDpcIntermediaryBlacklistService.java @@ -6,6 +6,8 @@ import com.ruoyi.dpc.domain.dto.DpcIntermediaryBlacklistAddDTO; import com.ruoyi.dpc.domain.dto.DpcIntermediaryBlacklistEditDTO; import com.ruoyi.dpc.domain.dto.DpcIntermediaryBlacklistQueryDTO; import com.ruoyi.dpc.domain.excel.DpcIntermediaryBlacklistExcel; +import com.ruoyi.dpc.domain.excel.DpcIntermediaryEntityExcel; +import com.ruoyi.dpc.domain.excel.DpcIntermediaryPersonExcel; import com.ruoyi.dpc.domain.vo.DpcIntermediaryBlacklistVO; import java.util.List; @@ -83,4 +85,30 @@ public interface IDpcIntermediaryBlacklistService { * @return 结果 */ String importIntermediary(List excelList, Boolean isUpdateSupport); + + /** + * 根据中介类型获取详情(返回不同类型) + * + * @param intermediaryId 中介ID + * @return 个人返回 DpcIntermediaryPersonDetailVO,机构返回 DpcIntermediaryEntityDetailVO + */ + Object selectIntermediaryDetailById(Long intermediaryId); + + /** + * 导入个人中介数据 + * + * @param excelList Excel实体列表 + * @param isUpdateSupport 是否更新支持 + * @return 结果 + */ + String importPersonIntermediary(List excelList, Boolean isUpdateSupport); + + /** + * 导入机构中介数据 + * + * @param excelList Excel实体列表 + * @param isUpdateSupport 是否更新支持 + * @return 结果 + */ + String importEntityIntermediary(List excelList, Boolean isUpdateSupport); } diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/impl/DpcEmployeeServiceImpl.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/impl/DpcEmployeeServiceImpl.java index 18a8436..4e35e41 100644 --- a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/impl/DpcEmployeeServiceImpl.java +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/impl/DpcEmployeeServiceImpl.java @@ -12,6 +12,7 @@ import com.ruoyi.dpc.domain.dto.DpcEmployeeQueryDTO; import com.ruoyi.dpc.domain.dto.DpcEmployeeRelativeAddDTO; import com.ruoyi.dpc.domain.excel.DpcEmployeeExcel; import com.ruoyi.dpc.domain.vo.DpcEmployeeVO; +import com.ruoyi.dpc.enums.EmployeeStatus; import com.ruoyi.dpc.mapper.DpcEmployeeMapper; import com.ruoyi.dpc.mapper.DpcEmployeeRelativeMapper; import com.ruoyi.dpc.service.IDpcEmployeeService; @@ -69,13 +70,9 @@ public class DpcEmployeeServiceImpl implements IDpcEmployeeService { Page resultPage = employeeMapper.selectEmployeePageWithDept(voPage, queryDTO); // 设置状态描述 - resultPage.getRecords().forEach(vo -> { - if ("0".equals(vo.getStatus())) { - vo.setStatusDesc("在职"); - } else if ("1".equals(vo.getStatus())) { - vo.setStatusDesc("离职"); - } - }); + resultPage.getRecords().forEach(vo -> + vo.setStatusDesc(EmployeeStatus.getDescByCode(vo.getStatus())) + ); return resultPage; } @@ -335,14 +332,7 @@ public class DpcEmployeeServiceImpl implements IDpcEmployeeService { DpcEmployeeVO vo = new DpcEmployeeVO(); BeanUtils.copyProperties(employee, vo); - - // 设置状态描述 - if ("0".equals(employee.getStatus())) { - vo.setStatusDesc("在职"); - } else if ("1".equals(employee.getStatus())) { - vo.setStatusDesc("离职"); - } - + vo.setStatusDesc(EmployeeStatus.getDescByCode(employee.getStatus())); return vo; } } diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/impl/DpcIntermediaryBlacklistServiceImpl.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/impl/DpcIntermediaryBlacklistServiceImpl.java index 6652959..bee4d9f 100644 --- a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/impl/DpcIntermediaryBlacklistServiceImpl.java +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/service/impl/DpcIntermediaryBlacklistServiceImpl.java @@ -8,13 +8,22 @@ import com.ruoyi.dpc.domain.dto.DpcIntermediaryBlacklistAddDTO; import com.ruoyi.dpc.domain.dto.DpcIntermediaryBlacklistEditDTO; import com.ruoyi.dpc.domain.dto.DpcIntermediaryBlacklistQueryDTO; import com.ruoyi.dpc.domain.excel.DpcIntermediaryBlacklistExcel; +import com.ruoyi.dpc.domain.excel.DpcIntermediaryEntityExcel; +import com.ruoyi.dpc.domain.excel.DpcIntermediaryPersonExcel; import com.ruoyi.dpc.domain.vo.DpcIntermediaryBlacklistVO; +import com.ruoyi.dpc.domain.vo.DpcIntermediaryEntityDetailVO; +import com.ruoyi.dpc.domain.vo.DpcIntermediaryPersonDetailVO; +import com.ruoyi.dpc.enums.DataSource; +import com.ruoyi.dpc.enums.Gender; +import com.ruoyi.dpc.enums.IntermediaryStatus; +import com.ruoyi.dpc.enums.IntermediaryType; import com.ruoyi.dpc.mapper.DpcIntermediaryBlacklistMapper; import com.ruoyi.dpc.service.IDpcIntermediaryBlacklistService; import jakarta.annotation.Resource; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; +import java.text.SimpleDateFormat; import java.util.List; import java.util.stream.Collectors; @@ -178,6 +187,265 @@ public class DpcIntermediaryBlacklistServiceImpl implements IDpcIntermediaryBlac } } + /** + * 根据中介类型获取详情(返回不同类型) + * + * @param intermediaryId 中介ID + * @return 个人返回 DpcIntermediaryPersonDetailVO,机构返回 DpcIntermediaryEntityDetailVO + */ + @Override + public Object selectIntermediaryDetailById(Long intermediaryId) { + DpcIntermediaryBlacklist intermediary = intermediaryMapper.selectById(intermediaryId); + if (intermediary == null) { + return null; + } + + // 根据中介类型返回不同的 VO + if ("1".equals(intermediary.getIntermediaryType())) { + // 个人类型 + return convertToPersonDetailVO(intermediary); + } else { + // 机构类型 + return convertToEntityDetailVO(intermediary); + } + } + + /** + * 导入个人中介数据 + * + * @param excelList Excel实体列表 + * @param isUpdateSupport 是否更新支持 + * @return 结果 + */ + @Override + public String importPersonIntermediary(List excelList, Boolean isUpdateSupport) { + if (excelList == null || excelList.isEmpty()) { + return "至少需要一条数据"; + } + + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + + for (int i = 0; i < excelList.size(); i++) { + DpcIntermediaryPersonExcel excel = excelList.get(i); + try { + // 验证数据 + validatePersonIntermediaryData(excel); + + // 转换为实体 + DpcIntermediaryBlacklist intermediary = new DpcIntermediaryBlacklist(); + intermediary.setName(excel.getName()); + intermediary.setCertificateNo(excel.getCertificateNo()); + intermediary.setIntermediaryType("1"); // 个人类型 + intermediary.setStatus("0"); // 默认正常 + intermediary.setDataSource("IMPORT"); // 批量导入 + intermediary.setRemark(excel.getRemark()); + + // 个人专属字段 + intermediary.setIndivType(excel.getIndivType()); + intermediary.setIndivSubType(excel.getIndivSubType()); + intermediary.setIndivGender(excel.getIndivGender()); + intermediary.setIndivCertType(excel.getIndivCertType()); + intermediary.setIndivPhone(excel.getIndivPhone()); + intermediary.setIndivWechat(excel.getIndivWechat()); + intermediary.setIndivAddress(excel.getIndivAddress()); + intermediary.setIndivCompany(excel.getIndivCompany()); + intermediary.setIndivPosition(excel.getIndivPosition()); + intermediary.setIndivRelatedId(excel.getIndivRelatedId()); + intermediary.setIndivRelation(excel.getIndivRelation()); + + // 设置默认证件类型 + if (StringUtils.isEmpty(intermediary.getIndivCertType())) { + intermediary.setIndivCertType("身份证"); + } + + // 检查是否已存在(通过证件号判断) + if (isUpdateSupport && StringUtils.isNotEmpty(excel.getCertificateNo())) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DpcIntermediaryBlacklist::getCertificateNo, excel.getCertificateNo()) + .eq(DpcIntermediaryBlacklist::getIntermediaryType, "1"); + DpcIntermediaryBlacklist existing = intermediaryMapper.selectOne(wrapper); + if (existing != null) { + intermediary.setIntermediaryId(existing.getIntermediaryId()); + intermediaryMapper.updateById(intermediary); + successNum++; + successMsg.append("
").append(successNum).append("、").append(excel.getName()).append(" 更新成功"); + continue; + } + } + + intermediaryMapper.insert(intermediary); + successNum++; + successMsg.append("
").append(successNum).append("、").append(excel.getName()).append(" 导入成功"); + } catch (Exception e) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、第").append(i + 1).append("行导入失败:"); + failureMsg.append(e.getMessage()); + } + } + + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new RuntimeException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条"); + return successMsg.toString(); + } + } + + /** + * 导入机构中介数据 + * + * @param excelList Excel实体列表 + * @param isUpdateSupport 是否更新支持 + * @return 结果 + */ + @Override + public String importEntityIntermediary(List excelList, Boolean isUpdateSupport) { + if (excelList == null || excelList.isEmpty()) { + return "至少需要一条数据"; + } + + int successNum = 0; + int failureNum = 0; + StringBuilder successMsg = new StringBuilder(); + StringBuilder failureMsg = new StringBuilder(); + + for (int i = 0; i < excelList.size(); i++) { + DpcIntermediaryEntityExcel excel = excelList.get(i); + try { + // 验证数据 + validateEntityIntermediaryData(excel); + + // 转换为实体 + DpcIntermediaryBlacklist intermediary = new DpcIntermediaryBlacklist(); + intermediary.setName(excel.getName()); + intermediary.setIntermediaryType("2"); // 机构类型 + intermediary.setStatus("0"); // 默认正常 + intermediary.setDataSource("IMPORT"); // 批量导入 + intermediary.setRemark(excel.getRemark()); + + // 机构专属字段 + intermediary.setCorpCreditCode(excel.getCorpCreditCode()); + intermediary.setCorpType(excel.getCorpType()); + intermediary.setCorpNature(excel.getCorpNature()); + intermediary.setCorpIndustryCategory(excel.getCorpIndustryCategory()); + intermediary.setCorpIndustry(excel.getCorpIndustry()); + + // 解析成立日期 + if (StringUtils.isNotEmpty(excel.getCorpEstablishDate())) { + try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + intermediary.setCorpEstablishDate(sdf.parse(excel.getCorpEstablishDate())); + } catch (Exception e) { + // 忽略日期解析错误 + } + } + + intermediary.setCorpAddress(excel.getCorpAddress()); + intermediary.setCorpLegalRep(excel.getCorpLegalRep()); + intermediary.setCorpLegalCertType(excel.getCorpLegalCertType()); + intermediary.setCorpLegalCertNo(excel.getCorpLegalCertNo()); + intermediary.setCorpShareholder1(excel.getCorpShareholder1()); + intermediary.setCorpShareholder2(excel.getCorpShareholder2()); + intermediary.setCorpShareholder3(excel.getCorpShareholder3()); + intermediary.setCorpShareholder4(excel.getCorpShareholder4()); + intermediary.setCorpShareholder5(excel.getCorpShareholder5()); + + // 检查是否已存在(通过统一社会信用代码判断) + if (isUpdateSupport && StringUtils.isNotEmpty(excel.getCorpCreditCode())) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(DpcIntermediaryBlacklist::getCorpCreditCode, excel.getCorpCreditCode()) + .eq(DpcIntermediaryBlacklist::getIntermediaryType, "2"); + DpcIntermediaryBlacklist existing = intermediaryMapper.selectOne(wrapper); + if (existing != null) { + intermediary.setIntermediaryId(existing.getIntermediaryId()); + intermediaryMapper.updateById(intermediary); + successNum++; + successMsg.append("
").append(successNum).append("、").append(excel.getName()).append(" 更新成功"); + continue; + } + } + + intermediaryMapper.insert(intermediary); + successNum++; + successMsg.append("
").append(successNum).append("、").append(excel.getName()).append(" 导入成功"); + } catch (Exception e) { + failureNum++; + failureMsg.append("
").append(failureNum).append("、第").append(i + 1).append("行导入失败:"); + failureMsg.append(e.getMessage()); + } + } + + if (failureNum > 0) { + failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:"); + throw new RuntimeException(failureMsg.toString()); + } else { + successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条"); + return successMsg.toString(); + } + } + + /** + * 验证个人中介数据 + */ + private void validatePersonIntermediaryData(DpcIntermediaryPersonExcel excel) { + if (StringUtils.isEmpty(excel.getName())) { + throw new RuntimeException("姓名不能为空"); + } + if (StringUtils.isEmpty(excel.getCertificateNo())) { + throw new RuntimeException("证件号码不能为空"); + } + } + + /** + * 验证机构中介数据 + */ + private void validateEntityIntermediaryData(DpcIntermediaryEntityExcel excel) { + if (StringUtils.isEmpty(excel.getName())) { + throw new RuntimeException("机构名称不能为空"); + } + } + + /** + * 转换为个人详情 VO + */ + private DpcIntermediaryPersonDetailVO convertToPersonDetailVO(DpcIntermediaryBlacklist intermediary) { + if (intermediary == null) { + return null; + } + + DpcIntermediaryPersonDetailVO vo = new DpcIntermediaryPersonDetailVO(); + BeanUtils.copyProperties(intermediary, vo); + + vo.setIntermediaryTypeName(IntermediaryType.PERSON.getDesc()); + vo.setStatusName(IntermediaryStatus.getDescByCode(intermediary.getStatus())); + vo.setDataSourceName(DataSource.getDescByCode(intermediary.getDataSource())); + vo.setIndivGenderName(Gender.getDescByCode(intermediary.getIndivGender())); + + return vo; + } + + /** + * 转换为机构详情 VO + */ + private DpcIntermediaryEntityDetailVO convertToEntityDetailVO(DpcIntermediaryBlacklist intermediary) { + if (intermediary == null) { + return null; + } + + DpcIntermediaryEntityDetailVO vo = new DpcIntermediaryEntityDetailVO(); + BeanUtils.copyProperties(intermediary, vo); + + vo.setIntermediaryTypeName(IntermediaryType.ENTITY.getDesc()); + vo.setStatusName(IntermediaryStatus.getDescByCode(intermediary.getStatus())); + vo.setDataSourceName(DataSource.getDescByCode(intermediary.getDataSource())); + + return vo; + } + /** * 构建查询条件 */ @@ -228,19 +496,8 @@ public class DpcIntermediaryBlacklistServiceImpl implements IDpcIntermediaryBlac DpcIntermediaryBlacklistVO vo = new DpcIntermediaryBlacklistVO(); BeanUtils.copyProperties(intermediary, vo); - // 设置中介类型名称 - if ("1".equals(intermediary.getIntermediaryType())) { - vo.setIntermediaryTypeName("个人"); - } else if ("2".equals(intermediary.getIntermediaryType())) { - vo.setIntermediaryTypeName("机构"); - } - - // 设置状态名称 - if ("0".equals(intermediary.getStatus())) { - vo.setStatusName("正常"); - } else if ("1".equals(intermediary.getStatus())) { - vo.setStatusName("停用"); - } + vo.setIntermediaryTypeName(IntermediaryType.getDescByCode(intermediary.getIntermediaryType())); + vo.setStatusName(IntermediaryStatus.getDescByCode(intermediary.getStatus())); return vo; } diff --git a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/utils/EasyExcelUtil.java b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/utils/EasyExcelUtil.java index f3a058a..118cd0f 100644 --- a/ruoyi-dpc/src/main/java/com/ruoyi/dpc/utils/EasyExcelUtil.java +++ b/ruoyi-dpc/src/main/java/com/ruoyi/dpc/utils/EasyExcelUtil.java @@ -3,6 +3,7 @@ package com.ruoyi.dpc.utils; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.write.handler.WriteHandler; import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; +import com.ruoyi.dpc.handler.DictDropdownWriteHandler; import jakarta.servlet.http.HttpServletResponse; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -154,4 +155,99 @@ public class EasyExcelUtil { String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + encodedFileName + ".xlsx"); } + + /** + * 下载带字典下拉框的导入模板 + * 自动解析实体类中的@DictDropdown注解,为对应字段添加下拉框 + * + * @param response 响应对象 + * @param clazz 实体类 + * @param sheetName 工作表名称 + * @param 泛型 + */ + public static void importTemplateWithDictDropdown(HttpServletResponse response, Class clazz, String sheetName) { + try { + setResponseHeader(response, sheetName + "模板"); + EasyExcel.write(response.getOutputStream(), clazz) + .sheet(sheetName) + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(new DictDropdownWriteHandler(clazz)) + .doWrite(List.of()); + } catch (IOException e) { + throw new RuntimeException("下载带字典下拉框的导入模板失败", e); + } + } + + /** + * 下载带字典下拉框的导入模板(指定文件名) + * 自动解析实体类中的@DictDropdown注解,为对应字段添加下拉框 + * + * @param response 响应对象 + * @param clazz 实体类 + * @param sheetName 工作表名称 + * @param fileName 文件名称 + * @param 泛型 + */ + public static void importTemplateWithDictDropdown(HttpServletResponse response, Class clazz, + String sheetName, String fileName) { + try { + setResponseHeader(response, fileName); + EasyExcel.write(response.getOutputStream(), clazz) + .sheet(sheetName) + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(new DictDropdownWriteHandler(clazz)) + .doWrite(List.of()); + } catch (IOException e) { + throw new RuntimeException("下载带字典下拉框的导入模板失败", e); + } + } + + /** + * 导出Excel(带字典下拉框) + * 导出的数据包含实际值,但模板中有下拉框供后续编辑使用 + * + * @param response 响应对象 + * @param list 数据列表 + * @param clazz 实体类 + * @param sheetName 工作表名称 + * @param 泛型 + */ + public static void exportExcelWithDictDropdown(HttpServletResponse response, List list, + Class clazz, String sheetName) { + try { + setResponseHeader(response, sheetName); + EasyExcel.write(response.getOutputStream(), clazz) + .sheet(sheetName) + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(new DictDropdownWriteHandler(clazz)) + .doWrite(list); + } catch (IOException e) { + throw new RuntimeException("导出带字典下拉框的Excel失败", e); + } + } + + /** + * 导出Excel(带字典下拉框,指定文件名) + * 导出的数据包含实际值,但模板中有下拉框供后续编辑使用 + * + * @param response 响应对象 + * @param list 数据列表 + * @param clazz 实体类 + * @param sheetName 工作表名称 + * @param fileName 文件名称 + * @param 泛型 + */ + public static void exportExcelWithDictDropdown(HttpServletResponse response, List list, + Class clazz, String sheetName, String fileName) { + try { + setResponseHeader(response, fileName); + EasyExcel.write(response.getOutputStream(), clazz) + .sheet(sheetName) + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(new DictDropdownWriteHandler(clazz)) + .doWrite(list); + } catch (IOException e) { + throw new RuntimeException("导出带字典下拉框的Excel失败", e); + } + } } diff --git a/sql/dpc_intermediary_dict_data_20260129.sql b/sql/dpc_intermediary_dict_data_20260129.sql new file mode 100644 index 0000000..e4cc4ce --- /dev/null +++ b/sql/dpc_intermediary_dict_data_20260129.sql @@ -0,0 +1,139 @@ +-- ============================================================ +-- 中介黑名单字典数据脚本 +-- 功能:创建个人和机构中介所需的字典数据 +-- 作者:ruoyi +-- 日期:2026-01-29 +-- ============================================================ + +-- 设置客户端字符集为 utf8mb4 +SET NAMES utf8mb4; +SET CHARACTER SET utf8mb4; +SET character_set_client = utf8mb4; +SET character_set_connection = utf8mb4; +SET character_set_results = utf8mb4; + +USE `discipline-prelim-check`; + +-- ============================================================ +-- 人员类型字典(dpc_person_type) +-- ============================================================ +-- 先删除旧数据(如果存在) +DELETE FROM sys_dict_data WHERE dict_type = 'dpc_person_type'; +DELETE FROM sys_dict_type WHERE dict_type = 'dpc_person_type'; + +-- 插入字典类型 +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +VALUES ('人员类型', 'dpc_person_type', '0', 'admin', NOW(), '中介黑名单-人员类型'); + +-- 插入字典数据 +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +VALUES +(1, '中介', '中介', 'dpc_person_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(2, '职业背债人', '职业背债人', 'dpc_person_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(3, '房产中介', '房产中介', 'dpc_person_type', '', 'default', 'N', '0', 'admin', NOW(), NULL); + +-- ============================================================ +-- 人员子类型字典(dpc_person_sub_type) +-- ============================================================ +DELETE FROM sys_dict_data WHERE dict_type = 'dpc_person_sub_type'; +DELETE FROM sys_dict_type WHERE dict_type = 'dpc_person_sub_type'; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +VALUES ('人员子类型', 'dpc_person_sub_type', '0', 'admin', NOW(), '中介黑名单-人员子类型'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +VALUES +(1, '本人', '本人', 'dpc_person_sub_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(2, '配偶', '配偶', 'dpc_person_sub_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(3, '子女', '子女', 'dpc_person_sub_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(9, '其他', '其他', 'dpc_person_sub_type', '', 'default', 'N', '0', 'admin', NOW(), NULL); + +-- ============================================================ +-- 性别字典(dpc_indiv_gender)- 用于个人中介Excel下拉框 +-- ============================================================ +DELETE FROM sys_dict_data WHERE dict_type = 'dpc_indiv_gender'; +DELETE FROM sys_dict_type WHERE dict_type = 'dpc_indiv_gender'; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +VALUES ('个人中介性别', 'dpc_indiv_gender', '0', 'admin', NOW(), '中介黑名单-个人中介性别'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +VALUES +(1, '男', 'M', 'dpc_indiv_gender', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(2, '女', 'F', 'dpc_indiv_gender', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(3, '其他', 'O', 'dpc_indiv_gender', '', 'default', 'N', '0', 'admin', NOW(), NULL); + +-- ============================================================ +-- 证件类型字典(dpc_certificate_type)- 用于个人中介Excel下拉框 +-- ============================================================ +DELETE FROM sys_dict_data WHERE dict_type = 'dpc_certificate_type'; +DELETE FROM sys_dict_type WHERE dict_type = 'dpc_certificate_type'; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +VALUES ('证件类型', 'dpc_certificate_type', '0', 'admin', NOW(), '中介黑名单-证件类型'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +VALUES +(1, '身份证', '身份证', 'dpc_certificate_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(2, '护照', '护照', 'dpc_certificate_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(3, '港澳通行证', '港澳通行证', 'dpc_certificate_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(4, '台胞证', '台胞证', 'dpc_certificate_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(5, '军官证', '军官证', 'dpc_certificate_type', '', 'default', 'N', '0', 'admin', NOW(), NULL); + +-- ============================================================ +-- 主体类型字典(dpc_entity_type)- 用于机构中介Excel下拉框 +-- ============================================================ +DELETE FROM sys_dict_data WHERE dict_type = 'dpc_entity_type'; +DELETE FROM sys_dict_type WHERE dict_type = 'dpc_entity_type'; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +VALUES ('主体类型', 'dpc_entity_type', '0', 'admin', NOW(), '中介黑名单-主体类型'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +VALUES +(1, '有限责任公司', '有限责任公司', 'dpc_entity_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(2, '股份有限公司', '股份有限公司', 'dpc_entity_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(3, '合伙企业', '合伙企业', 'dpc_entity_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(4, '个体工商户', '个体工商户', 'dpc_entity_type', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(5, '外资企业', '外资企业', 'dpc_entity_type', '', 'default', 'N', '0', 'admin', NOW(), NULL); + +-- ============================================================ +-- 企业性质字典(dpc_enterprise_nature)- 用于机构中介Excel下拉框 +-- ============================================================ +DELETE FROM sys_dict_data WHERE dict_type = 'dpc_enterprise_nature'; +DELETE FROM sys_dict_type WHERE dict_type = 'dpc_enterprise_nature'; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +VALUES ('企业性质', 'dpc_enterprise_nature', '0', 'admin', NOW(), '中介黑名单-企业性质'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +VALUES +(1, '国企', '国企', 'dpc_enterprise_nature', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(2, '民企', '民企', 'dpc_enterprise_nature', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(3, '外企', '外企', 'dpc_enterprise_nature', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(4, '合资', '合资', 'dpc_enterprise_nature', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(9, '其他', '其他', 'dpc_enterprise_nature', '', 'default', 'N', '0', 'admin', NOW(), NULL); + +-- ============================================================ +-- 数据来源字典(dpc_data_source) +-- ============================================================ +DELETE FROM sys_dict_data WHERE dict_type = 'dpc_data_source'; +DELETE FROM sys_dict_type WHERE dict_type = 'dpc_data_source'; + +INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark) +VALUES ('数据来源', 'dpc_data_source', '0', 'admin', NOW(), '中介黑名单-数据来源'); + +INSERT INTO sys_dict_data (dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, status, create_by, create_time, remark) +VALUES +(1, '手动录入', 'MANUAL', 'dpc_data_source', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(2, '系统同步', 'SYSTEM', 'dpc_data_source', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(3, '批量导入', 'IMPORT', 'dpc_data_source', '', 'default', 'N', '0', 'admin', NOW(), NULL), +(4, '接口获取', 'API', 'dpc_data_source', '', 'default', 'N', '0', 'admin', NOW(), NULL); + +-- ============================================================ +-- 执行完成后操作说明 +-- ============================================================ +-- 1. 执行完成后,请通过系统管理 > 字典管理验证字典数据 +-- 2. 重要:在每个字典类型页面点击"刷新缓存"按钮,确保字典数据加载到 Redis +-- 3. 验证字典缓存是否生效(可通过测试模板下载确认下拉框是否显示) +-- 4. 检查 Redis 缓存中的字典数据(sys_dict_cache:*) diff --git a/sql/dpc_intermediary_enhance_20260129.sql b/sql/dpc_intermediary_enhance_20260129.sql new file mode 100644 index 0000000..0908075 --- /dev/null +++ b/sql/dpc_intermediary_enhance_20260129.sql @@ -0,0 +1,63 @@ +-- ============================================================ +-- 中介黑名单表字段扩展脚本 +-- 功能:为个人和机构类型中介添加详细字段 +-- 作者:ruoyi +-- 日期:2026-01-29 +-- ============================================================ + +USE `discipline-prelim-check`; + +-- ============================================================ +-- 个人类型字段 (以 indiv_ 前缀标识,individual 缩写) +-- ============================================================ +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_type` VARCHAR(30) DEFAULT NULL COMMENT '人员类型(中介、职业背债人、房产中介等)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_sub_type` VARCHAR(50) DEFAULT NULL COMMENT '人员子类型(本人、配偶等)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_gender` CHAR(1) DEFAULT NULL COMMENT '性别(M男 F女 O其他)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_cert_type` VARCHAR(30) DEFAULT '身份证' COMMENT '证件类型'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_phone` VARCHAR(20) DEFAULT NULL COMMENT '手机号码(加密存储)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_wechat` VARCHAR(50) DEFAULT NULL COMMENT '微信号'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_address` VARCHAR(200) DEFAULT NULL COMMENT '联系地址'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_company` VARCHAR(100) DEFAULT NULL COMMENT '所在公司'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_position` VARCHAR(100) DEFAULT NULL COMMENT '职位/职务'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_related_id` VARCHAR(20) DEFAULT NULL COMMENT '关联人员ID'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `indiv_relation` VARCHAR(50) DEFAULT NULL COMMENT '关联关系'; + +-- ============================================================ +-- 机构类型字段 (以 corp_ 前缀标识,corporation 缩写) +-- ============================================================ +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_credit_code` VARCHAR(18) DEFAULT NULL COMMENT '统一社会信用代码'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_type` VARCHAR(50) DEFAULT NULL COMMENT '主体类型(有限责任公司、股份有限公司等)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_nature` VARCHAR(50) DEFAULT NULL COMMENT '企业性质(国企、民企、外企等)'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_industry_category` VARCHAR(100) DEFAULT NULL COMMENT '行业分类'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_industry` VARCHAR(100) DEFAULT NULL COMMENT '所属行业'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_establish_date` DATE DEFAULT NULL COMMENT '成立日期'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_address` VARCHAR(500) DEFAULT NULL COMMENT '注册地址'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_legal_rep` VARCHAR(50) DEFAULT NULL COMMENT '法定代表人'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_legal_cert_type` VARCHAR(30) DEFAULT NULL COMMENT '法定代表人证件类型'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_legal_cert_no` VARCHAR(30) DEFAULT NULL COMMENT '法定代表人证件号码'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_1` VARCHAR(30) DEFAULT NULL COMMENT '股东1'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_2` VARCHAR(30) DEFAULT NULL COMMENT '股东2'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_3` VARCHAR(30) DEFAULT NULL COMMENT '股东3'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_4` VARCHAR(30) DEFAULT NULL COMMENT '股东4'; +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `corp_shareholder_5` VARCHAR(30) DEFAULT NULL COMMENT '股东5'; + +-- ============================================================ +-- 通用字段 +-- ============================================================ +ALTER TABLE dpc_intermediary_blacklist ADD COLUMN `data_source` VARCHAR(30) DEFAULT 'MANUAL' COMMENT '数据来源(MANUAL手动录入 SYSTEM系统同步 IMPORT批量导入 API接口获取)'; + +-- ============================================================ +-- 添加索引 +-- ============================================================ +-- 为统一社会信用代码添加索引(用于机构中介去重判断) +ALTER TABLE dpc_intermediary_blacklist ADD INDEX `idx_corp_credit_code` (`corp_credit_code`); + +-- 为手机号码添加索引(用于个人中介查询) +ALTER TABLE dpc_intermediary_blacklist ADD INDEX `idx_indiv_phone` (`indiv_phone`); + +-- ============================================================ +-- 验证脚本 +-- ============================================================ +-- 执行完成后,请运行以下命令验证表结构变更: +-- DESCRIBE dpc_intermediary_blacklist; +-- SHOW INDEX FROM dpc_intermediary_blacklist; diff --git a/sql/fix_intermediary_comments_20260129.sql b/sql/fix_intermediary_comments_20260129.sql new file mode 100644 index 0000000..79133bf --- /dev/null +++ b/sql/fix_intermediary_comments_20260129.sql @@ -0,0 +1,105 @@ +-- ============================================================ +-- 修复中介黑名单表字段备注乱码 +-- 功能:将字段备注从乱码修复为正确的中文 +-- 作者:ruoyi +-- 日期:2026-01-29 +-- ============================================================ + +-- 设置客户端字符集为 utf8mb4 +SET NAMES utf8mb4; +SET CHARACTER SET utf8mb4; +SET character_set_client = utf8mb4; +SET character_set_connection = utf8mb4; +SET character_set_results = utf8mb4; + +USE `discipline-prelim-check`; + +-- ============================================================ +-- 修复个人类型字段备注 +-- ============================================================ +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_type VARCHAR(30) DEFAULT NULL COMMENT '人员类型(中介、职业背债人、房产中介等)'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_sub_type VARCHAR(50) DEFAULT NULL COMMENT '人员子类型(本人、配偶等)'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_gender CHAR(1) DEFAULT NULL COMMENT '性别(M男 F女 O其他)'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_cert_type VARCHAR(30) DEFAULT '身份证' COMMENT '证件类型'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_phone VARCHAR(20) DEFAULT NULL COMMENT '手机号码(加密存储)'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_wechat VARCHAR(50) DEFAULT NULL COMMENT '微信号'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_address VARCHAR(200) DEFAULT NULL COMMENT '联系地址'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_company VARCHAR(100) DEFAULT NULL COMMENT '所在公司'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_position VARCHAR(100) DEFAULT NULL COMMENT '职位/职务'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_related_id VARCHAR(20) DEFAULT NULL COMMENT '关联人员ID'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN indiv_relation VARCHAR(50) DEFAULT NULL COMMENT '关联关系'; + +-- ============================================================ +-- 修复机构类型字段备注 +-- ============================================================ +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_credit_code VARCHAR(18) DEFAULT NULL COMMENT '统一社会信用代码'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_type VARCHAR(50) DEFAULT NULL COMMENT '主体类型(有限责任公司、股份有限公司等)'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_nature VARCHAR(50) DEFAULT NULL COMMENT '企业性质(国企、民企、外企等)'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_industry_category VARCHAR(100) DEFAULT NULL COMMENT '行业分类'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_industry VARCHAR(100) DEFAULT NULL COMMENT '所属行业'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_establish_date DATE DEFAULT NULL COMMENT '成立日期'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_address VARCHAR(500) DEFAULT NULL COMMENT '注册地址'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_legal_rep VARCHAR(50) DEFAULT NULL COMMENT '法定代表人'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_legal_cert_type VARCHAR(30) DEFAULT NULL COMMENT '法定代表人证件类型'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_legal_cert_no VARCHAR(30) DEFAULT NULL COMMENT '法定代表人证件号码'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_shareholder_1 VARCHAR(30) DEFAULT NULL COMMENT '股东1'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_shareholder_2 VARCHAR(30) DEFAULT NULL COMMENT '股东2'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_shareholder_3 VARCHAR(30) DEFAULT NULL COMMENT '股东3'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_shareholder_4 VARCHAR(30) DEFAULT NULL COMMENT '股东4'; + +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN corp_shareholder_5 VARCHAR(30) DEFAULT NULL COMMENT '股东5'; + +-- ============================================================ +-- 修复通用字段备注 +-- ============================================================ +ALTER TABLE dpc_intermediary_blacklist +MODIFY COLUMN data_source VARCHAR(30) DEFAULT 'MANUAL' COMMENT '数据来源(MANUAL手动录入 SYSTEM系统同步 IMPORT批量导入 API接口获取)';