中介黑名单更新
This commit is contained in:
@@ -69,7 +69,16 @@
|
|||||||
"Bash(ls:*)",
|
"Bash(ls:*)",
|
||||||
"Bash(test_report.sh \")",
|
"Bash(test_report.sh \")",
|
||||||
"mcp__mysql__show_statement",
|
"mcp__mysql__show_statement",
|
||||||
"Bash(if not exist \"doc\\\\designs\" mkdir docdesigns)"
|
"Bash(if not exist \"doc\\\\designs\" mkdir docdesigns)",
|
||||||
|
"Bash(if [ ! -d \"D:\\\\ccdi\\\\ccdi\\\\ruoyi-ccdi\\\\src\\\\main\\\\java\\\\com\\\\ruoyi\\\\ccdi\\\\domain\\\\dto\" ])",
|
||||||
|
"Bash(then mkdir -p \"D:\\\\ccdi\\\\ccdi\\\\ruoyi-ccdi\\\\src\\\\main\\\\java\\\\com\\\\ruoyi\\\\ccdi\\\\domain\\\\dto\")",
|
||||||
|
"Bash(fi)",
|
||||||
|
"Bash(cat:*)",
|
||||||
|
"Skill(superpowers:executing-plans)",
|
||||||
|
"Skill(superpowers:finishing-a-development-branch)",
|
||||||
|
"Skill(superpowers:systematic-debugging)",
|
||||||
|
"mcp__mysql__execute",
|
||||||
|
"Skill(document-skills:xlsx)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"enabledMcpjsonServers": [
|
"enabledMcpjsonServers": [
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,6 @@
|
|||||||
1,biz_id,VARCHAR,-,否,是,人员ID
|
1,biz_id,VARCHAR,-,否,是,人员ID
|
||||||
2,person_type,VARCHAR,-,否,否,人员类型,中介、职业背债人、房产中介等
|
2,person_type,VARCHAR,-,否,否,人员类型,中介、职业背债人、房产中介等
|
||||||
3,person_sub_type,VARCHAR,-,是,否,人员子类型
|
3,person_sub_type,VARCHAR,-,是,否,人员子类型
|
||||||
4,relation_type,VARCHAR,-,否,-,关系类型,如:配偶、子女、父母、兄弟姐妹等
|
|
||||||
5,name,VARCHAR,-,否,否,姓名
|
5,name,VARCHAR,-,否,否,姓名
|
||||||
6,gender,CHAR,-,是,否,性别
|
6,gender,CHAR,-,是,否,性别
|
||||||
7,id_type,VARCHAR,身份证,否,否,证件类型
|
7,id_type,VARCHAR,身份证,否,否,证件类型
|
||||||
@@ -15,7 +14,7 @@
|
|||||||
13,social_credit_code,VARCHAR,,,,企业统一信用码
|
13,social_credit_code,VARCHAR,,,,企业统一信用码
|
||||||
14,position,VARCHAR,-,是,否,职位
|
14,position,VARCHAR,-,是,否,职位
|
||||||
15,related_num_id,VARCHAR,-,是,否,关联人员ID
|
15,related_num_id,VARCHAR,-,是,否,关联人员ID
|
||||||
16,relation_type,VARCHAR,-,是,否,关联关系
|
16,relation_type,VARCHAR,-,是,否,关系类型,如:配偶、子女、父母、兄弟姐妹等
|
||||||
17,date_source,,,,,"数据来源,MANUAL:手动录入, SYSTEM:系统同步, IMPORT:批量导入, API:接口获取"
|
17,date_source,,,,,"数据来源,MANUAL:手动录入, SYSTEM:系统同步, IMPORT:批量导入, API:接口获取"
|
||||||
18,remark,,,,,备注信息
|
18,remark,,,,,备注信息
|
||||||
19,created_by,VARCHAR,-,否,-,记录创建人
|
19,created_by,VARCHAR,-,否,-,记录创建人
|
||||||
|
|||||||
|
@@ -0,0 +1,216 @@
|
|||||||
|
# 中介黑名单联合查询功能重构实现总结
|
||||||
|
|
||||||
|
## 一、问题描述
|
||||||
|
|
||||||
|
原始的SQL错误:`Unknown column 'relation_type_field' in 'field list'`
|
||||||
|
|
||||||
|
**根本原因:**
|
||||||
|
1. 实体类 `CcdiBizIntermediary` 中定义了不存在的字段 `relationTypeField`
|
||||||
|
2. 实体类中的 `dataSource` 字段与数据库字段 `date_source` 映射不匹配
|
||||||
|
3. 原有的列表查询实现通过Java层合并两张表的数据,效率较低且无法利用数据库优化
|
||||||
|
|
||||||
|
## 二、解决方案
|
||||||
|
|
||||||
|
### 2.1 修复实体类字段映射
|
||||||
|
|
||||||
|
**文件:** `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/CcdiBizIntermediary.java`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
1. 删除了不存在的 `relationTypeField` 字段(第70行)
|
||||||
|
2. 为 `dataSource` 字段添加了 `@TableField("date_source")` 注解(第70行)
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 修改前
|
||||||
|
private String relationTypeField;
|
||||||
|
private String dataSource;
|
||||||
|
|
||||||
|
// 修改后
|
||||||
|
@TableField("date_source")
|
||||||
|
private String dataSource;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 创建联合查询Mapper接口
|
||||||
|
|
||||||
|
**新增文件:** `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/mapper/CcdiIntermediaryMapper.java`
|
||||||
|
|
||||||
|
**功能:**
|
||||||
|
- 定义联合查询方法 `selectIntermediaryList()`
|
||||||
|
- 定义统计查询方法 `selectIntermediaryCount()`
|
||||||
|
- 支持按中介类型筛选:`1=个人, 2=实体, null=全部`
|
||||||
|
|
||||||
|
### 2.3 创建MyBatis XML Mapper
|
||||||
|
|
||||||
|
**新增文件:** `ruoyi-ccdi/src/main/resources/mapper/ccdi/CcdiIntermediaryMapper.xml`
|
||||||
|
|
||||||
|
**SQL设计策略:**
|
||||||
|
|
||||||
|
1. **单表查询模式**(当指定中介类型时)
|
||||||
|
- `intermediaryType=1`:仅查询 `ccdi_biz_intermediary` 表
|
||||||
|
- `intermediaryType=2`:仅查询 `ccdi_enterprise_base_info` 表
|
||||||
|
|
||||||
|
2. **联合查询模式**(当intermediaryType为null时)
|
||||||
|
- 使用 `UNION ALL` 联合两张表
|
||||||
|
- 外层包裹 `SELECT * FROM (...) AS combined_result` 用于统一排序和分页
|
||||||
|
- 按创建时间倒序排列
|
||||||
|
|
||||||
|
3. **动态SQL特性**
|
||||||
|
- 使用 MyBatis 动态SQL实现灵活的查询条件组合
|
||||||
|
- 支持姓名模糊查询
|
||||||
|
- 支持证件号/统一社会信用代码精确查询
|
||||||
|
- 支持分页(LIMIT + OFFSET)
|
||||||
|
|
||||||
|
**查询条件映射:**
|
||||||
|
|
||||||
|
| 查询参数 | 个人中介表字段 | 实体中介表字段 |
|
||||||
|
|---------|--------------|--------------|
|
||||||
|
| name | name | enterprise_name |
|
||||||
|
| certificateNo | person_id | social_credit_code |
|
||||||
|
| intermediaryType | person_type='中介' | risk_level='1' AND ent_source='INTERMEDIARY' |
|
||||||
|
|
||||||
|
### 2.4 优化Service层实现
|
||||||
|
|
||||||
|
**文件:** `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiIntermediaryServiceImpl.java`
|
||||||
|
|
||||||
|
**修改内容:**
|
||||||
|
|
||||||
|
1. 注入新的 `CcdiIntermediaryMapper`
|
||||||
|
2. 重写 `selectIntermediaryPage()` 方法,使用XML联合查询
|
||||||
|
3. 删除原有的Java层合并数据和手动分页逻辑
|
||||||
|
|
||||||
|
**性能优势:**
|
||||||
|
- 数据库层面实现分页,减少内存占用
|
||||||
|
- 利用数据库索引优化查询性能
|
||||||
|
- 减少网络传输数据量
|
||||||
|
|
||||||
|
### 2.5 扩展查询DTO
|
||||||
|
|
||||||
|
**文件:** `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiIntermediaryQueryDTO.java`
|
||||||
|
|
||||||
|
**新增字段:**
|
||||||
|
```java
|
||||||
|
private Integer pageNum; // 页码
|
||||||
|
private Integer pageSize; // 每页大小
|
||||||
|
```
|
||||||
|
|
||||||
|
## 三、技术实现细节
|
||||||
|
|
||||||
|
### 3.1 分页实现
|
||||||
|
|
||||||
|
**MyBatis Plus的分页机制:**
|
||||||
|
- MyBatis Plus的分页从1开始(`page.getCurrent()`)
|
||||||
|
- SQL的OFFSET从0开始
|
||||||
|
- 需要转换:`pageNum = page.getCurrent() - 1`
|
||||||
|
|
||||||
|
**SQL分页语法:**
|
||||||
|
```sql
|
||||||
|
LIMIT #{pageSize}
|
||||||
|
OFFSET #{pageNum} * #{pageSize}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 UNION ALL vs UNION
|
||||||
|
|
||||||
|
- **使用 UNION ALL**:保留所有记录,包括重复记录
|
||||||
|
- **性能优势**:UNION ALL 不进行去重排序,性能更好
|
||||||
|
- **业务场景**:个人中介和实体中介不会重复,无需去重
|
||||||
|
|
||||||
|
### 3.3 动态SQL设计
|
||||||
|
|
||||||
|
使用MyBatis的 `<if>` 标签实现:
|
||||||
|
```xml
|
||||||
|
<if test="intermediaryType != null and intermediaryType == '1'">
|
||||||
|
<!-- 个人中介查询 -->
|
||||||
|
</if>
|
||||||
|
<if test="intermediaryType != null and intermediaryType == '2'">
|
||||||
|
<!-- 实体中介查询 -->
|
||||||
|
</if>
|
||||||
|
<if test="intermediaryType == null or intermediaryType == ''">
|
||||||
|
<!-- 联合查询 -->
|
||||||
|
</if>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、测试脚本
|
||||||
|
|
||||||
|
**文件:** `doc/test/scripts/test_union_query.sh`
|
||||||
|
|
||||||
|
**测试用例:**
|
||||||
|
1. Test 1: 查询全部中介(UNION查询)
|
||||||
|
2. Test 2: 仅查询个人中介(单表查询)
|
||||||
|
3. Test 3: 仅查询实体中介(单表查询)
|
||||||
|
4. Test 4: 按姓名模糊查询
|
||||||
|
5. Test 5: 按证件号精确查询
|
||||||
|
6. Test 6: 分页功能测试
|
||||||
|
7. Test 7: 组合查询测试(类型+姓名+分页)
|
||||||
|
|
||||||
|
## 五、文件清单
|
||||||
|
|
||||||
|
### 修改的文件
|
||||||
|
1. `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/CcdiBizIntermediary.java` - 删除冗余字段,修复字段映射
|
||||||
|
2. `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiIntermediaryServiceImpl.java` - 重构查询逻辑
|
||||||
|
3. `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiIntermediaryQueryDTO.java` - 添加分页参数
|
||||||
|
|
||||||
|
### 新增的文件
|
||||||
|
1. `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/mapper/CcdiIntermediaryMapper.java` - 联合查询Mapper接口
|
||||||
|
2. `ruoyi-ccdi/src/main/resources/mapper/ccdi/CcdiIntermediaryMapper.xml` - MyBatis XML Mapper
|
||||||
|
3. `doc/test/scripts/test_union_query.sh` - 测试脚本
|
||||||
|
|
||||||
|
### 删除的文件
|
||||||
|
1. `ruoyi-ccdi/src/main/resources/mapper/ccdi/CcdiIntermediaryMapper.xml` - 旧的错误配置
|
||||||
|
|
||||||
|
## 六、优势总结
|
||||||
|
|
||||||
|
### 6.1 性能提升
|
||||||
|
- **数据库层面分页**:避免加载全部数据到内存
|
||||||
|
- **索引优化**:充分利用数据库索引
|
||||||
|
- **减少网络传输**:只传输需要的数据
|
||||||
|
|
||||||
|
### 6.2 代码质量
|
||||||
|
- **职责分离**:查询逻辑集中在Mapper层
|
||||||
|
- **代码简洁**:删除复杂的Java层合并逻辑
|
||||||
|
- **易于维护**:SQL集中管理,便于优化
|
||||||
|
|
||||||
|
### 6.3 灵活性
|
||||||
|
- **动态查询**:支持单表和联合查询灵活切换
|
||||||
|
- **条件组合**:支持多种查询条件组合
|
||||||
|
- **易于扩展**:后续新增字段或查询条件只需修改XML
|
||||||
|
|
||||||
|
## 七、后续建议
|
||||||
|
|
||||||
|
1. **索引优化**:
|
||||||
|
- `ccdi_biz_intermediary`: 确保字段有合适索引
|
||||||
|
- `ccdi_enterprise_base_info`: 确保 `risk_level` 和 `ent_source` 有索引
|
||||||
|
|
||||||
|
2. **性能监控**:
|
||||||
|
- 监控慢查询日志
|
||||||
|
- 根据实际数据量调整分页大小
|
||||||
|
|
||||||
|
3. **功能扩展**:
|
||||||
|
- 考虑添加更多排序字段选项
|
||||||
|
- 考虑支持批量导出时的流式查询
|
||||||
|
|
||||||
|
## 八、执行测试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Windows环境
|
||||||
|
cd doc\test\scripts
|
||||||
|
bash test_union_query.sh
|
||||||
|
|
||||||
|
# Linux/Mac环境
|
||||||
|
cd doc/test/scripts
|
||||||
|
chmod +x test_union_query.sh
|
||||||
|
./test_union_query.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 九、回滚方案
|
||||||
|
|
||||||
|
如果新实现出现问题,可以通过Git回滚到之前的版本:
|
||||||
|
```bash
|
||||||
|
git checkout HEAD~1 -- ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiIntermediaryServiceImpl.java
|
||||||
|
```
|
||||||
|
|
||||||
|
删除新增的Mapper文件即可恢复原状。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**实现日期:** 2026-02-05
|
||||||
|
**实现人:** Claude Code
|
||||||
|
**版本:** v2.0
|
||||||
@@ -0,0 +1,368 @@
|
|||||||
|
# 中介黑名单联合查询功能重构实现总结 (MyBatis Plus分页版本)
|
||||||
|
|
||||||
|
## 一、版本更新说明
|
||||||
|
|
||||||
|
**版本:** v2.1 (MyBatis Plus分页插件版本)
|
||||||
|
**更新日期:** 2026-02-05
|
||||||
|
**更新内容:** 使用MyBatis Plus分页插件替代手动分页,参考员工模块的实现方式
|
||||||
|
|
||||||
|
## 二、问题描述
|
||||||
|
|
||||||
|
### 2.1 原始错误
|
||||||
|
```
|
||||||
|
Unknown column 'relation_type_field' in 'field list'
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2.2 v2.0版本的问题
|
||||||
|
虽然v2.0版本实现了XML联合查询,但使用了手动的LIMIT/OFFSET分页,这与若依框架的标准实现方式不一致:
|
||||||
|
- **不一致性**:与员工模块等其他模块的实现方式不同
|
||||||
|
- **维护性**:手动计算分页参数,容易出错
|
||||||
|
- **功能限制**:无法利用MyBatis Plus分页插件的优化功能
|
||||||
|
|
||||||
|
## 三、解决方案(v2.1)
|
||||||
|
|
||||||
|
### 3.1 参考实现
|
||||||
|
参考 `CcdiEmployeeController` 和 `CcdiEmployeeServiceImpl` 的实现方式:
|
||||||
|
```java
|
||||||
|
// Controller层
|
||||||
|
PageDomain pageDomain = TableSupport.buildPageRequest();
|
||||||
|
Page<CcdiEmployeeVO> page = new Page<>(pageDomain.getPageNum(), pageDomain.getPageSize());
|
||||||
|
Page<CcdiEmployeeVO> result = employeeService.selectEmployeePage(page, queryDTO);
|
||||||
|
|
||||||
|
// Service层
|
||||||
|
Page<CcdiEmployeeVO> resultPage = employeeMapper.selectEmployeePageWithDept(voPage, queryDTO);
|
||||||
|
|
||||||
|
// Mapper接口
|
||||||
|
Page<CcdiEmployeeVO> selectEmployeePageWithDept(@Param("page") Page<CcdiEmployeeVO> page,
|
||||||
|
@Param("query") CcdiEmployeeQueryDTO queryDTO);
|
||||||
|
|
||||||
|
// XML
|
||||||
|
<select id="selectEmployeePageWithDept" resultMap="CcdiEmployeeVOResult">
|
||||||
|
SELECT ... FROM ...
|
||||||
|
WHERE ...
|
||||||
|
ORDER BY ...
|
||||||
|
<!-- 不包含LIMIT和OFFSET,由MyBatis Plus自动注入 -->
|
||||||
|
</select>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2 核心改动
|
||||||
|
|
||||||
|
#### 1. Mapper接口方法签名
|
||||||
|
**文件:** `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/mapper/CcdiIntermediaryMapper.java`
|
||||||
|
|
||||||
|
**修改前:**
|
||||||
|
```java
|
||||||
|
List<CcdiIntermediaryVO> selectIntermediaryList(CcdiIntermediaryQueryDTO queryDTO);
|
||||||
|
long selectIntermediaryCount(CcdiIntermediaryQueryDTO queryDTO);
|
||||||
|
```
|
||||||
|
|
||||||
|
**修改后:**
|
||||||
|
```java
|
||||||
|
Page<CcdiIntermediaryVO> selectIntermediaryList(
|
||||||
|
Page<CcdiIntermediaryVO> page,
|
||||||
|
@Param("query") CcdiIntermediaryQueryDTO queryDTO
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键点:**
|
||||||
|
- 第一个参数是 `Page` 对象
|
||||||
|
- 查询条件使用 `@Param` 注解包装
|
||||||
|
- 返回类型是 `Page<Vo>`
|
||||||
|
- 删除了单独的count查询方法
|
||||||
|
|
||||||
|
#### 2. XML Mapper文件
|
||||||
|
**文件:** `ruoyi-ccdi/src/main/resources/mapper/ccdi/CcdiIntermediaryMapper.xml`
|
||||||
|
|
||||||
|
**修改前(v2.0):**
|
||||||
|
```xml
|
||||||
|
<!-- 三个独立的SQL分支,每个分支都包含LIMIT和OFFSET -->
|
||||||
|
<if test="intermediaryType == '1'">
|
||||||
|
SELECT ... FROM ccdi_biz_intermediary ...
|
||||||
|
LIMIT #{pageSize} OFFSET #{pageNum} * #{pageSize}
|
||||||
|
</if>
|
||||||
|
<if test="intermediaryType == '2'">
|
||||||
|
SELECT ... FROM ccdi_enterprise_base_info ...
|
||||||
|
LIMIT #{pageSize} OFFSET #{pageNum} * #{pageSize}
|
||||||
|
</if>
|
||||||
|
<if test="intermediaryType == null">
|
||||||
|
SELECT * FROM (...) UNION ALL (...)
|
||||||
|
LIMIT #{pageSize} OFFSET #{pageNum} * #{pageSize}
|
||||||
|
</if>
|
||||||
|
```
|
||||||
|
|
||||||
|
**修改后(v2.1):**
|
||||||
|
```xml
|
||||||
|
<!-- 统一的SQL结构,不包含LIMIT和OFFSET -->
|
||||||
|
<select id="selectIntermediaryList" resultType="com.ruoyi.ccdi.domain.vo.CcdiIntermediaryVO">
|
||||||
|
SELECT * FROM (
|
||||||
|
<!-- 个人中介 -->
|
||||||
|
SELECT ... FROM ccdi_biz_intermediary WHERE person_type = '中介'
|
||||||
|
UNION ALL
|
||||||
|
<!-- 实体中介 -->
|
||||||
|
SELECT ... FROM ccdi_enterprise_base_info WHERE ...
|
||||||
|
) AS combined_result
|
||||||
|
<where>
|
||||||
|
<!-- 动态查询条件 -->
|
||||||
|
<if test="query.intermediaryType != null and query.intermediaryType != ''">
|
||||||
|
AND intermediary_type = #{query.intermediaryType}
|
||||||
|
</if>
|
||||||
|
<if test="query.name != null and query.name != ''">
|
||||||
|
AND name LIKE CONCAT('%', #{query.name}, '%')
|
||||||
|
</if>
|
||||||
|
<if test="query.certificateNo != null and query.certificateNo != ''">
|
||||||
|
AND certificate_no = #{query.certificateNo}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
ORDER BY create_time DESC
|
||||||
|
<!-- MyBatis Plus会自动在这里注入LIMIT和OFFSET -->
|
||||||
|
</select>
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键点:**
|
||||||
|
- 统一的查询结构,使用UNION ALL
|
||||||
|
- 不包含LIMIT和OFFSET
|
||||||
|
- 在最外层使用 `<where>` 进行动态过滤
|
||||||
|
- MyBatis Plus分页插件会自动在ORDER BY后面注入分页SQL
|
||||||
|
|
||||||
|
#### 3. Service层实现
|
||||||
|
**文件:** `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiIntermediaryServiceImpl.java`
|
||||||
|
|
||||||
|
**修改前(v2.0):**
|
||||||
|
```java
|
||||||
|
public Page<CcdiIntermediaryVO> selectIntermediaryPage(...) {
|
||||||
|
// 手动查询总数
|
||||||
|
long total = intermediaryMapper.selectIntermediaryCount(queryDTO);
|
||||||
|
|
||||||
|
// 手动设置分页参数
|
||||||
|
queryDTO.setPageNum((int) (page.getCurrent() - 1));
|
||||||
|
queryDTO.setPageSize((int) page.getSize());
|
||||||
|
|
||||||
|
// 手动查询列表
|
||||||
|
List<CcdiIntermediaryVO> list = intermediaryMapper.selectIntermediaryList(queryDTO);
|
||||||
|
|
||||||
|
// 手动设置分页结果
|
||||||
|
page.setRecords(list);
|
||||||
|
page.setTotal(total);
|
||||||
|
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**修改后(v2.1):**
|
||||||
|
```java
|
||||||
|
public Page<CcdiIntermediaryVO> selectIntermediaryPage(Page<CcdiIntermediaryVO> page, CcdiIntermediaryQueryDTO queryDTO) {
|
||||||
|
// 直接调用Mapper的联合查询方法,MyBatis Plus会自动处理分页
|
||||||
|
return intermediaryMapper.selectIntermediaryList(page, queryDTO);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**关键点:**
|
||||||
|
- 一行代码搞定
|
||||||
|
- MyBatis Plus自动处理count查询、分页SQL注入、结果封装
|
||||||
|
- 无需手动计算分页参数
|
||||||
|
|
||||||
|
#### 4. QueryDTO清理
|
||||||
|
**文件:** `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiIntermediaryQueryDTO.java`
|
||||||
|
|
||||||
|
**删除字段:**
|
||||||
|
```java
|
||||||
|
// 不再需要,分页信息通过Page对象传递
|
||||||
|
private Integer pageNum;
|
||||||
|
private Integer pageSize;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 四、技术实现细节
|
||||||
|
|
||||||
|
### 4.1 MyBatis Plus分页插件工作原理
|
||||||
|
|
||||||
|
1. **拦截器机制**
|
||||||
|
- MyBatis Plus使用拦截器在SQL执行前拦截
|
||||||
|
- 自动在SQL后面添加LIMIT和OFFSET
|
||||||
|
- 自动执行COUNT查询获取total
|
||||||
|
|
||||||
|
2. **分页SQL生成**
|
||||||
|
```sql
|
||||||
|
-- 原始SQL
|
||||||
|
SELECT * FROM (UNION查询) AS t WHERE ... ORDER BY create_time DESC
|
||||||
|
|
||||||
|
-- MyBatis Plus自动注入后
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT * FROM (UNION查询) AS t WHERE ... ORDER BY create_time DESC
|
||||||
|
LIMIT 10 OFFSET 0
|
||||||
|
) AS page
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **参数传递**
|
||||||
|
- Controller: `PageDomain` → `Page<Vo>`
|
||||||
|
- Service: `Page<Vo>` 传递给Mapper
|
||||||
|
- Mapper: `Page<Vo>` 作为第一个参数
|
||||||
|
- XML: 通过MyBatis Plus拦截器自动处理
|
||||||
|
|
||||||
|
### 4.2 SQL优化
|
||||||
|
|
||||||
|
#### v2.0的问题
|
||||||
|
- 三个独立的SQL分支
|
||||||
|
- 每个分支都需要处理分页
|
||||||
|
- 代码重复,维护困难
|
||||||
|
|
||||||
|
#### v2.1的优化
|
||||||
|
- 统一的SQL结构
|
||||||
|
- 外层WHERE条件过滤
|
||||||
|
- MyBatis Plus统一处理分页
|
||||||
|
- 代码简洁,易于维护
|
||||||
|
|
||||||
|
### 4.3 参数绑定变化
|
||||||
|
|
||||||
|
**v2.0:**
|
||||||
|
```java
|
||||||
|
// QueryDTO包含分页参数
|
||||||
|
queryDTO.setPageNum(0);
|
||||||
|
queryDTO.setPageSize(10);
|
||||||
|
mapper.selectList(queryDTO);
|
||||||
|
|
||||||
|
// XML中直接使用
|
||||||
|
#{pageNum}, #{pageSize}
|
||||||
|
```
|
||||||
|
|
||||||
|
**v2.1:**
|
||||||
|
```java
|
||||||
|
// Page对象单独传递
|
||||||
|
Page<CcdiIntermediaryVO> page = new Page<>(1, 10);
|
||||||
|
mapper.selectList(page, queryDTO);
|
||||||
|
|
||||||
|
// XML中通过@Param包装
|
||||||
|
#{query.intermediaryType}, #{query.name}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 五、文件清单
|
||||||
|
|
||||||
|
### 修改的文件
|
||||||
|
1. `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/CcdiBizIntermediary.java` - 删除冗余字段,修复字段映射
|
||||||
|
2. `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/domain/dto/CcdiIntermediaryQueryDTO.java` - 删除分页参数
|
||||||
|
3. `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/mapper/CcdiIntermediaryMapper.java` - 修改方法签名
|
||||||
|
4. `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiIntermediaryServiceImpl.java` - 简化分页逻辑
|
||||||
|
5. `ruoyi-ccdi/src/main/resources/mapper/ccdi/CcdiIntermediaryMapper.xml` - 重写SQL结构
|
||||||
|
|
||||||
|
### 新增的文件
|
||||||
|
1. `doc/test/scripts/test_union_query_mybatis_plus.sh` - 测试脚本
|
||||||
|
2. `doc/plans/2026-02-05-intermediary-blacklist-union-query-mybatis-plus.md` - 本文档
|
||||||
|
|
||||||
|
### 删除的文件
|
||||||
|
1. `doc/test/scripts/test_union_query.sh` - 旧版测试脚本(保留备份)
|
||||||
|
|
||||||
|
## 六、优势总结
|
||||||
|
|
||||||
|
### 6.1 与框架一致性
|
||||||
|
- ✅ 与员工模块等其他模块实现方式一致
|
||||||
|
- ✅ 符合若依框架的标准规范
|
||||||
|
- ✅ 便于团队统一维护
|
||||||
|
|
||||||
|
### 6.2 代码简洁性
|
||||||
|
- ✅ Service层从10+行代码减少到1行
|
||||||
|
- ✅ XML从200+行减少到60行
|
||||||
|
- ✅ 删除了手动分页的复杂逻辑
|
||||||
|
|
||||||
|
### 6.3 性能优化
|
||||||
|
- ✅ MyBatis Plus分页插件经过优化
|
||||||
|
- ✅ 自动缓存count查询结果
|
||||||
|
- ✅ 支持多种数据库的分页方言
|
||||||
|
|
||||||
|
### 6.4 可维护性
|
||||||
|
- ✅ 统一的SQL结构,易于理解
|
||||||
|
- ✅ 动态条件集中在外层WHERE
|
||||||
|
- ✅ 易于扩展新的查询条件
|
||||||
|
|
||||||
|
## 七、测试验证
|
||||||
|
|
||||||
|
### 7.1 测试脚本
|
||||||
|
**文件:** `doc/test/scripts/test_union_query_mybatis_plus.sh`
|
||||||
|
|
||||||
|
**测试用例:**
|
||||||
|
1. Test 1: UNION ALL查询全部中介
|
||||||
|
2. Test 2: 按类型筛选个人中介
|
||||||
|
3. Test 3: 按类型筛选实体中介
|
||||||
|
4. Test 4: 按姓名模糊查询
|
||||||
|
5. Test 5: 按证件号精确查询
|
||||||
|
6. Test 6: MyBatis Plus分页功能测试
|
||||||
|
7. Test 7: 组合查询测试
|
||||||
|
8. Test 8: 大分页测试
|
||||||
|
|
||||||
|
### 7.2 执行测试
|
||||||
|
```bash
|
||||||
|
# Windows环境
|
||||||
|
cd doc\test\scripts
|
||||||
|
bash test_union_query_mybatis_plus.sh
|
||||||
|
|
||||||
|
# Linux/Mac环境
|
||||||
|
cd doc/test/scripts
|
||||||
|
chmod +x test_union_query_mybatis_plus.sh
|
||||||
|
./test_union_query_mybatis_plus.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## 八、对比总结
|
||||||
|
|
||||||
|
| 特性 | v2.0 (手动分页) | v2.1 (MyBatis Plus) |
|
||||||
|
|-----|----------------|-------------------|
|
||||||
|
| Service代码行数 | 10+ | 1 |
|
||||||
|
| XML代码行数 | 200+ | 60 |
|
||||||
|
| 一致性 | ❌ 与框架不一致 | ✅ 完全一致 |
|
||||||
|
| 性能 | 一般 | 优化 |
|
||||||
|
| 维护性 | 复杂 | 简单 |
|
||||||
|
| 扩展性 | 困难 | 容易 |
|
||||||
|
| Count查询 | 手动 | 自动 |
|
||||||
|
| 分页计算 | 手动 | 自动 |
|
||||||
|
|
||||||
|
## 九、最佳实践
|
||||||
|
|
||||||
|
基于本次重构,总结以下最佳实践:
|
||||||
|
|
||||||
|
1. **遵循框架规范**
|
||||||
|
- 优先使用框架提供的标准实现方式
|
||||||
|
- 参考其他模块的成熟实现
|
||||||
|
|
||||||
|
2. **分页查询模式**
|
||||||
|
```java
|
||||||
|
// Mapper接口
|
||||||
|
Page<VO> selectXxxPage(Page<VO> page, @Param("query") QueryDTO query);
|
||||||
|
|
||||||
|
// Service实现
|
||||||
|
return mapper.selectXxxPage(page, query);
|
||||||
|
|
||||||
|
// XML
|
||||||
|
<select id="selectXxxPage" resultType="VO">
|
||||||
|
SELECT ... FROM ...
|
||||||
|
<where>...</where>
|
||||||
|
ORDER BY ...
|
||||||
|
</select>
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **联合查询优化**
|
||||||
|
- 使用UNION ALL而不是多个分支
|
||||||
|
- 在最外层使用WHERE进行过滤
|
||||||
|
- 避免在XML中写LIMIT和OFFSET
|
||||||
|
|
||||||
|
4. **参数传递**
|
||||||
|
- Page对象作为第一个参数
|
||||||
|
- 查询条件使用@Param包装
|
||||||
|
- 避免在实体中混入分页参数
|
||||||
|
|
||||||
|
## 十、后续建议
|
||||||
|
|
||||||
|
1. **性能监控**
|
||||||
|
- 监控UNION ALL查询的执行计划
|
||||||
|
- 优化索引以提升查询性能
|
||||||
|
|
||||||
|
2. **功能扩展**
|
||||||
|
- 考虑添加更多排序字段选项
|
||||||
|
- 考虑支持批量导出的流式查询
|
||||||
|
|
||||||
|
3. **代码优化**
|
||||||
|
- 其他模块如有类似实现,建议统一改造
|
||||||
|
- 建立统一的分页查询模板
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**实现日期:** 2026-02-05
|
||||||
|
**实现人:** Claude Code
|
||||||
|
**版本:** v2.1 (MyBatis Plus分页插件版本)
|
||||||
|
**参考模块:** CcdiEmployeeController/CcdiEmployeeServiceImpl
|
||||||
642
doc/plans/2026-02-05-中介黑名单前端适配APIv2.0重构设计.md
Normal file
642
doc/plans/2026-02-05-中介黑名单前端适配APIv2.0重构设计.md
Normal file
@@ -0,0 +1,642 @@
|
|||||||
|
# 中介黑名单前端适配API v2.0重构设计文档
|
||||||
|
|
||||||
|
**文档版本**: v1.0
|
||||||
|
**创建日期**: 2026-02-05
|
||||||
|
**设计目标**: 将前端字段完全对齐API v2.0规范,实现前后端字段名一致
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、变更背景
|
||||||
|
|
||||||
|
### 1.1 API v2.0核心变更
|
||||||
|
|
||||||
|
后端API已升级至v2.0版本,主要变更包括:
|
||||||
|
|
||||||
|
- **统一业务ID**: 使用`bizId`替代`intermediaryId`作为主键
|
||||||
|
- **接口分离**: 个人和实体中介使用独立的详情查询接口
|
||||||
|
- **字段规范化**: 统一字段命名规范,消除歧义
|
||||||
|
- **DTO/VO分离**: 请求和响应对象完全分离
|
||||||
|
|
||||||
|
### 1.2 重构目标
|
||||||
|
|
||||||
|
1. **字段名对齐**: 前端表单字段与API请求字段完全一致
|
||||||
|
2. **消除映射**: 移除前后端字段名转换逻辑
|
||||||
|
3. **代码简化**: 降低维护成本,提升可读性
|
||||||
|
4. **类型安全**: 确保个人和实体中介字段正确隔离
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、字段映射方案
|
||||||
|
|
||||||
|
### 2.1 个人中介字段映射
|
||||||
|
|
||||||
|
| 旧前端字段 | API v2.0字段 | 说明 |
|
||||||
|
|-----------|-------------|------|
|
||||||
|
| intermediaryId | bizId | 主键ID |
|
||||||
|
| certificateNo | personId | 证件号码 |
|
||||||
|
| indivType | personType | 人员类型 |
|
||||||
|
| indivSubType | personSubType | 人员子类型 |
|
||||||
|
| indivGender | gender | 性别 |
|
||||||
|
| indivCertType | idType | 证件类型 |
|
||||||
|
| indivPhone | mobile | 手机号码 |
|
||||||
|
| indivWechat | wechatNo | 微信号 |
|
||||||
|
| indivAddress | contactAddress | 联系地址 |
|
||||||
|
| indivCompany | company | 所在公司 |
|
||||||
|
| indivPosition | position | 职位 |
|
||||||
|
| indivRelatedId | relatedNumId | 关联人员ID |
|
||||||
|
| indivRelation | relationType | 关系类型 |
|
||||||
|
|
||||||
|
**保持不变的字段:**
|
||||||
|
- name (姓名)
|
||||||
|
- remark (备注)
|
||||||
|
- intermediaryType (中介类型)
|
||||||
|
- status (状态)
|
||||||
|
|
||||||
|
### 2.2 实体中介字段映射
|
||||||
|
|
||||||
|
| 旧前端字段 | API v2.0字段 | 说明 |
|
||||||
|
|-----------|-------------|------|
|
||||||
|
| intermediaryId | bizId | 主键ID |
|
||||||
|
| name | enterpriseName | 机构名称 |
|
||||||
|
| certificateNo / corpCreditCode | socialCreditCode | 统一社会信用代码 |
|
||||||
|
| corpType | enterpriseType | 主体类型 |
|
||||||
|
| corpNature | enterpriseNature | 企业性质 |
|
||||||
|
| corpIndustryCategory | industryClass | 行业分类 |
|
||||||
|
| corpIndustry | industryName | 所属行业 |
|
||||||
|
| corpEstablishDate | establishDate | 成立日期 |
|
||||||
|
| corpAddress | registerAddress | 注册地址 |
|
||||||
|
| corpLegalRep | legalRepresentative | 法定代表人 |
|
||||||
|
| corpLegalCertType | legalCertType | 法定代表人证件类型 |
|
||||||
|
| corpLegalCertNo | legalCertNo | 法定代表人证件号码 |
|
||||||
|
| corpShareholder1-5 | shareholder1-5 | 股东信息(1-5) |
|
||||||
|
|
||||||
|
**保持不变的字段:**
|
||||||
|
- remark (备注)
|
||||||
|
- intermediaryType (中介类型)
|
||||||
|
- status (状态)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、文件修改清单
|
||||||
|
|
||||||
|
### 3.1 需要修改的文件
|
||||||
|
|
||||||
|
| 序号 | 文件路径 | 修改类型 | 优先级 |
|
||||||
|
|-----|---------|---------|-------|
|
||||||
|
| 1 | `ruoyi-ui/src/api/ccdiIntermediary.js` | API层 | P0 |
|
||||||
|
| 2 | `ruoyi-ui/src/views/ccdiIntermediary/index.vue` | 主页面 | P0 |
|
||||||
|
| 3 | `ruoyi-ui/src/views/ccdiIntermediary/components/EditDialog.vue` | 编辑组件 | P0 |
|
||||||
|
| 4 | `ruoyi-ui/src/views/ccdiIntermediary/components/DetailDialog.vue` | 详情组件 | P1 |
|
||||||
|
| 5 | `ruoyi-ui/src/views/ccdiIntermediary/components/ImportDialog.vue` | 导入组件 | P1 |
|
||||||
|
|
||||||
|
### 3.2 无需修改的文件
|
||||||
|
|
||||||
|
| 序号 | 文件路径 | 原因 |
|
||||||
|
|-----|---------|------|
|
||||||
|
| 1 | `SearchForm.vue` | 查询参数与API兼容 |
|
||||||
|
| 2 | `DataTable.vue` | 已使用友好名称字段 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、API层修改详情
|
||||||
|
|
||||||
|
### 4.1 ccdiIntermediary.js
|
||||||
|
|
||||||
|
#### 新增接口
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 查询个人中介详情
|
||||||
|
export function getPersonIntermediary(bizId) {
|
||||||
|
return request({
|
||||||
|
url: '/ccdi/intermediary/person/' + bizId,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询实体中介详情
|
||||||
|
export function getEntityIntermediary(socialCreditCode) {
|
||||||
|
return request({
|
||||||
|
url: '/ccdi/intermediary/entity/' + socialCreditCode,
|
||||||
|
method: 'get'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 删除接口
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 删除以下旧版统一接口
|
||||||
|
// getIntermediary(intermediaryId)
|
||||||
|
// addIntermediary(data)
|
||||||
|
// updateIntermediary(data)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、主页面修改详情
|
||||||
|
|
||||||
|
### 5.1 index.vue - 数据模型
|
||||||
|
|
||||||
|
#### queryParams修改
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
queryParams: {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
name: null,
|
||||||
|
certificateNo: null, // 保持不变(API查询参数兼容)
|
||||||
|
intermediaryType: null,
|
||||||
|
status: null
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### form数据模型
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
form: {
|
||||||
|
// 通用字段
|
||||||
|
bizId: null, // 原 intermediaryId
|
||||||
|
intermediaryType: '1',
|
||||||
|
status: '0',
|
||||||
|
remark: null,
|
||||||
|
|
||||||
|
// 个人中介字段
|
||||||
|
name: null,
|
||||||
|
personId: null, // 原 certificateNo
|
||||||
|
personType: null, // 原 indivType
|
||||||
|
personSubType: null, // 原 indivSubType
|
||||||
|
relationType: null, // 原 indivRelation
|
||||||
|
gender: null, // 原 indivGender
|
||||||
|
idType: null, // 原 indivCertType
|
||||||
|
mobile: null, // 原 indivPhone
|
||||||
|
wechatNo: null, // 原 indivWechat
|
||||||
|
contactAddress: null, // 原 indivAddress
|
||||||
|
company: null, // 原 indivCompany
|
||||||
|
socialCreditCode: null, // 新增
|
||||||
|
position: null, // 原 indivPosition
|
||||||
|
relatedNumId: null, // 原 indivRelatedId
|
||||||
|
|
||||||
|
// 实体中介字段
|
||||||
|
enterpriseName: null, // 原 name
|
||||||
|
socialCreditCode: null, // 原 certificateNo/corpCreditCode
|
||||||
|
enterpriseType: null, // 原 corpType
|
||||||
|
enterpriseNature: null, // 原 corpNature
|
||||||
|
industryClass: null, // 原 corpIndustryCategory
|
||||||
|
industryName: null, // 原 corpIndustry
|
||||||
|
establishDate: null, // 原 corpEstablishDate
|
||||||
|
registerAddress: null, // 原 corpAddress
|
||||||
|
legalRepresentative: null, // 原 corpLegalRep
|
||||||
|
legalCertType: null, // 原 corpLegalCertType
|
||||||
|
legalCertNo: null, // 原 corpLegalCertNo
|
||||||
|
shareholder1: null, // 原 corpShareholder1
|
||||||
|
shareholder2: null, // 原 corpShareholder2
|
||||||
|
shareholder3: null, // 原 corpShareholder3
|
||||||
|
shareholder4: null, // 原 corpShareholder4
|
||||||
|
shareholder5: null // 原 corpShareholder5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.2 核心方法修改
|
||||||
|
|
||||||
|
#### handleSelectionChange
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
handleSelectionChange(selection) {
|
||||||
|
this.ids = selection.map(item => item.bizId); // 原 intermediaryId
|
||||||
|
this.single = selection.length !== 1;
|
||||||
|
this.multiple = !selection.length;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### handleDetail
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
handleDetail(row) {
|
||||||
|
if (row.intermediaryType === '1') {
|
||||||
|
// 个人中介
|
||||||
|
getPersonIntermediary(row.bizId).then(response => {
|
||||||
|
this.detailData = response.data;
|
||||||
|
this.detailOpen = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 实体中介
|
||||||
|
getEntityIntermediary(row.socialCreditCode).then(response => {
|
||||||
|
this.detailData = response.data;
|
||||||
|
this.detailOpen = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### handleUpdate
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
handleUpdate(row) {
|
||||||
|
this.reset();
|
||||||
|
if (row.intermediaryType === '1') {
|
||||||
|
getPersonIntermediary(row.bizId).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.open = true;
|
||||||
|
this.title = "修改中介黑名单";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
getEntityIntermediary(row.socialCreditCode).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.open = true;
|
||||||
|
this.title = "修改中介黑名单";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### submitForm
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
submitForm() {
|
||||||
|
if (this.form.bizId != null) { // 原 intermediaryId
|
||||||
|
// 修改模式
|
||||||
|
if (this.form.intermediaryType === '1') {
|
||||||
|
updatePersonIntermediary(this.form).then(response => {
|
||||||
|
this.$modal.msgSuccess("修改成功");
|
||||||
|
this.open = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateEntityIntermediary(this.form).then(response => {
|
||||||
|
this.$modal.msgSuccess("修改成功");
|
||||||
|
this.open = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 新增模式
|
||||||
|
if (this.form.intermediaryType === '1') {
|
||||||
|
addPersonIntermediary(this.form).then(response => {
|
||||||
|
this.$modal.msgSuccess("新增成功");
|
||||||
|
this.open = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
addEntityIntermediary(this.form).then(response => {
|
||||||
|
this.$modal.msgSuccess("新增成功");
|
||||||
|
this.open = false;
|
||||||
|
this.getList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### handleDelete
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
handleDelete(row) {
|
||||||
|
const bizIds = row.bizId || this.ids.join(','); // 原 intermediaryIds
|
||||||
|
this.$modal.confirm('是否确认删除中介黑名单编号为"' + bizIds + '"的数据项?')
|
||||||
|
.then(function() {
|
||||||
|
return delIntermediary(bizIds);
|
||||||
|
}).then(() => {
|
||||||
|
this.getList();
|
||||||
|
this.$modal.msgSuccess("删除成功");
|
||||||
|
}).catch(() => {});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、EditDialog组件修改详情
|
||||||
|
|
||||||
|
### 6.1 个人中介表单字段修改
|
||||||
|
|
||||||
|
| 行号 | 修改内容 |
|
||||||
|
|-----|---------|
|
||||||
|
| 46 | `form.certificateNo` → `form.personId` |
|
||||||
|
| 54 | `form.indivType` → `form.personType` |
|
||||||
|
| 66 | `form.indivSubType` → `form.personSubType` |
|
||||||
|
| 80 | `form.indivGender` → `form.gender` |
|
||||||
|
| 92 | `form.indivCertType` → `form.idType` |
|
||||||
|
| 106 | `form.indivPhone` → `form.mobile` |
|
||||||
|
| 110 | `form.indivWechat` → `form.wechatNo` |
|
||||||
|
| 116 | `form.indivAddress` → `form.contactAddress` |
|
||||||
|
| 121 | `form.indivCompany` → `form.company` |
|
||||||
|
| 126 | `form.indivPosition` → `form.position` |
|
||||||
|
| 133 | `form.indivRelatedId` → `form.relatedNumId` |
|
||||||
|
| 138 | `form.indivRelation` → `form.relationType` |
|
||||||
|
|
||||||
|
### 6.2 实体中介表单字段修改
|
||||||
|
|
||||||
|
| 行号 | 修改内容 |
|
||||||
|
|-----|---------|
|
||||||
|
| 172 | `form.name` → `form.enterpriseName` |
|
||||||
|
| 179 | `form.certificateNo` → `form.socialCreditCode` |
|
||||||
|
| 190 | `form.corpType` → `form.enterpriseType` |
|
||||||
|
| 202 | `form.corpNature` → `form.enterpriseNature` |
|
||||||
|
| 227 | `form.corpIndustryCategory` → `form.industryClass` |
|
||||||
|
| 234 | `form.corpIndustry` → `form.industryName` |
|
||||||
|
| 217 | `form.corpEstablishDate` → `form.establishDate` |
|
||||||
|
| 239 | `form.corpAddress` → `form.registerAddress` |
|
||||||
|
| 244 | `form.corpLegalRep` → `form.legalRepresentative` |
|
||||||
|
| 249-251 | 添加下拉框:`form.legalCertType` (证件类型) |
|
||||||
|
| 254 | `form.corpLegalCertNo` → `form.legalCertNo` |
|
||||||
|
| 260-284 | `form.corpShareholder1-5` → `form.shareholder1-5` |
|
||||||
|
|
||||||
|
### 6.3 Script部分修改
|
||||||
|
|
||||||
|
#### computed属性
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
isAddMode() {
|
||||||
|
return !this.form || !this.form.bizId; // 原 intermediaryId
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### initDialogState方法
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const isAdd = !this.form || !this.form.bizId; // 原 intermediaryId
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 删除方法
|
||||||
|
|
||||||
|
删除`handleCertificateNoChange`方法(v2.0无需字段同步)
|
||||||
|
|
||||||
|
#### 验证规则修改
|
||||||
|
|
||||||
|
**个人中介:**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
indivRules: {
|
||||||
|
name: [
|
||||||
|
{ required: true, message: "姓名不能为空", trigger: "blur" },
|
||||||
|
{ max: 100, message: "姓名长度不能超过100个字符", trigger: "blur" }
|
||||||
|
],
|
||||||
|
personId: [ // 原 certificateNo
|
||||||
|
{ required: true, message: "证件号不能为空", trigger: "blur" },
|
||||||
|
{ max: 50, message: "证件号长度不能超过50个字符", trigger: "blur" }
|
||||||
|
],
|
||||||
|
remark: [
|
||||||
|
{ max: 500, message: "备注长度不能超过500个字符", trigger: "blur" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**实体中介:**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
corpRules: {
|
||||||
|
enterpriseName: [ // 原 name
|
||||||
|
{ required: true, message: "机构名称不能为空", trigger: "blur" },
|
||||||
|
{ max: 200, message: "机构名称长度不能超过200个字符", trigger: "blur" }
|
||||||
|
],
|
||||||
|
socialCreditCode: [ // 原 certificateNo
|
||||||
|
{ required: true, message: "统一社会信用代码不能为空", trigger: "blur" },
|
||||||
|
{ max: 50, message: "统一社会信用代码长度不能超过50个字符", trigger: "blur" }
|
||||||
|
],
|
||||||
|
remark: [
|
||||||
|
{ max: 500, message: "备注长度不能超过500个字符", trigger: "blur" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、DetailDialog组件修改详情
|
||||||
|
|
||||||
|
### 7.1 核心字段修改
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<!-- 业务ID -->
|
||||||
|
<el-descriptions-item label="业务ID">{{ detailData.bizId }}</el-descriptions-item>
|
||||||
|
|
||||||
|
<!-- 证件号/信用代码 -->
|
||||||
|
<el-descriptions-item label="证件号/信用代码">
|
||||||
|
<span v-if="detailData.intermediaryType === '1'">{{ detailData.personId || '-' }}</span>
|
||||||
|
<span v-else>{{ detailData.socialCreditCode || '-' }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 个人中介字段修改
|
||||||
|
|
||||||
|
| 旧字段 | 新字段 |
|
||||||
|
|--------|--------|
|
||||||
|
| detailData.indivType | detailData.personType |
|
||||||
|
| detailData.indivSubType | detailData.personSubType |
|
||||||
|
| detailData.indivGenderName | detailData.genderName |
|
||||||
|
| detailData.indivCertType | detailData.idType |
|
||||||
|
| detailData.indivPhone | detailData.mobile |
|
||||||
|
| detailData.indivWechat | detailData.wechatNo |
|
||||||
|
| detailData.indivAddress | detailData.contactAddress |
|
||||||
|
| detailData.indivCompany | detailData.company |
|
||||||
|
| detailData.indivPosition | detailData.position |
|
||||||
|
| detailData.indivRelatedId | detailData.relatedNumId |
|
||||||
|
| detailData.indivRelation | detailData.relationType |
|
||||||
|
|
||||||
|
**新增字段:**
|
||||||
|
- detailData.socialCreditCode (企业统一信用码)
|
||||||
|
|
||||||
|
### 7.3 实体中介字段修改
|
||||||
|
|
||||||
|
| 旧字段 | 新字段 |
|
||||||
|
|--------|--------|
|
||||||
|
| detailData.corpCreditCode | detailData.socialCreditCode |
|
||||||
|
| detailData.corpType | detailData.enterpriseType |
|
||||||
|
| detailData.corpNature | detailData.enterpriseNature |
|
||||||
|
| detailData.corpIndustryCategory | detailData.industryClass |
|
||||||
|
| detailData.corpIndustry | detailData.industryName |
|
||||||
|
| detailData.corpEstablishDate | detailData.establishDate |
|
||||||
|
| detailData.corpAddress | detailData.registerAddress |
|
||||||
|
| detailData.corpLegalRep | detailData.legalRepresentative |
|
||||||
|
| detailData.corpLegalCertType | detailData.legalCertType |
|
||||||
|
| detailData.corpLegalCertNo | detailData.legalCertNo |
|
||||||
|
| detailData.corpShareholder1-5 | detailData.shareholder1-5 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、ImportDialog组件修改详情
|
||||||
|
|
||||||
|
### 8.1 模板下载URL修正
|
||||||
|
|
||||||
|
**错误代码:**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
this.download('dpc/intermediary/importPersonTemplate', ...)
|
||||||
|
this.download('dpc/intermediary/importEntityTemplate', ...)
|
||||||
|
```
|
||||||
|
|
||||||
|
**修正为:**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
handleDownloadTemplate() {
|
||||||
|
if (this.formData.importType === 'person') {
|
||||||
|
this.download('ccdi/intermediary/importPersonTemplate', {}, `个人中介黑名单模板_${new Date().getTime()}.xlsx`);
|
||||||
|
} else {
|
||||||
|
this.download('ccdi/intermediary/importEntityTemplate', {}, `机构中介黑名单模板_${new Date().getTime()}.xlsx`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 九、下拉框优化
|
||||||
|
|
||||||
|
### 9.1 新增下拉框
|
||||||
|
|
||||||
|
**法定代表人证件类型** (实体中介表单)
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<el-form-item label="法定代表人证件类型">
|
||||||
|
<el-select v-model="form.legalCertType" placeholder="请选择证件类型" clearable style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in certTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 9.2 已有下拉框验证
|
||||||
|
|
||||||
|
- ✅ 性别 (genderOptions)
|
||||||
|
- ✅ 证件类型 (certTypeOptions)
|
||||||
|
- ✅ 主体类型 (corpTypeOptions)
|
||||||
|
- ✅ 企业性质 (corpNatureOptions)
|
||||||
|
- ✅ 人员类型 (indivTypeOptions)
|
||||||
|
- ✅ 人员子类型 (indivSubTypeOptions)
|
||||||
|
- ✅ 关联关系 (relationTypeOptions)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十、测试计划
|
||||||
|
|
||||||
|
### 10.1 功能测试清单
|
||||||
|
|
||||||
|
**查询功能:**
|
||||||
|
- [ ] 列表查询正常显示
|
||||||
|
- [ ] 按姓名/机构名称模糊查询
|
||||||
|
- [ ] 按证件号精确查询
|
||||||
|
- [ ] 按中介类型筛选(个人/机构)
|
||||||
|
- [ ] 分页功能正常
|
||||||
|
|
||||||
|
**个人中介CRUD:**
|
||||||
|
- [ ] 新增个人中介 - 所有字段保存成功
|
||||||
|
- [ ] 查看个人中介详情 - 所有字段正确显示
|
||||||
|
- [ ] 修改个人中介 - 数据更新成功
|
||||||
|
- [ ] 删除个人中介 - 删除成功
|
||||||
|
|
||||||
|
**机构中介CRUD:**
|
||||||
|
- [ ] 新增机构中介 - 所有字段保存成功
|
||||||
|
- [ ] 查看机构中介详情 - 所有字段正确显示
|
||||||
|
- [ ] 修改机构中介 - 数据更新成功
|
||||||
|
- [ ] 删除机构中介 - 删除成功
|
||||||
|
|
||||||
|
**导入功能:**
|
||||||
|
- [ ] 下载个人中介导入模板成功
|
||||||
|
- [ ] 下载机构中介导入模板成功
|
||||||
|
- [ ] 个人中介数据导入成功
|
||||||
|
- [ ] 机构中介数据导入成功
|
||||||
|
- [ ] 导入时更新已存在数据功能正常
|
||||||
|
|
||||||
|
**下拉框验证:**
|
||||||
|
- [ ] 性别下拉框显示正确
|
||||||
|
- [ ] 证件类型下拉框显示正确
|
||||||
|
- [ ] 法定代表人证件类型下拉框显示正确
|
||||||
|
- [ ] 主体类型下拉框显示正确
|
||||||
|
- [ ] 企业性质下拉框显示正确
|
||||||
|
|
||||||
|
### 10.2 回归测试
|
||||||
|
|
||||||
|
- [ ] 权限控制正常
|
||||||
|
- [ ] 表单验证规则生效
|
||||||
|
- [ ] 错误提示信息正确
|
||||||
|
- [ ] 响应式布局正常
|
||||||
|
- [ ] 浏览器兼容性(Chrome/Firefox/Edge)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十一、风险与注意事项
|
||||||
|
|
||||||
|
### 11.1 兼容性风险
|
||||||
|
|
||||||
|
**影响范围**: 所有中介黑名单相关功能
|
||||||
|
|
||||||
|
**缓解措施**:
|
||||||
|
1. 完整的功能测试覆盖
|
||||||
|
2. 保留旧版代码备份
|
||||||
|
3. 分步骤部署,先测试环境验证
|
||||||
|
|
||||||
|
### 11.2 数据风险
|
||||||
|
|
||||||
|
**风险点**: 字段名变更可能导致数据丢失
|
||||||
|
|
||||||
|
**缓解措施**:
|
||||||
|
1. 确保后端已做好兼容处理
|
||||||
|
2. 导出测试数据进行对比验证
|
||||||
|
3. 增量导入测试
|
||||||
|
|
||||||
|
### 11.3 注意事项
|
||||||
|
|
||||||
|
1. **字段同步**: 确保前后端字段完全一致,不要遗留转换逻辑
|
||||||
|
2. **类型判断**: 所有详情查询必须根据`intermediaryType`调用不同接口
|
||||||
|
3. **验证规则**: 个人和实体中介的必填字段不同,需分别配置
|
||||||
|
4. **下拉框复用**: 法定代表人证件类型可复用`certTypeOptions`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 十二、实施建议
|
||||||
|
|
||||||
|
### 12.1 实施步骤
|
||||||
|
|
||||||
|
1. **第一阶段**: API层修改
|
||||||
|
- 新增详情查询接口
|
||||||
|
- 删除旧版统一接口
|
||||||
|
- 验证接口调用正常
|
||||||
|
|
||||||
|
2. **第二阶段**: 主页面修改
|
||||||
|
- 修改数据模型
|
||||||
|
- 修改核心方法
|
||||||
|
- 测试查询和删除功能
|
||||||
|
|
||||||
|
3. **第三阶段**: 组件修改
|
||||||
|
- EditDialog组件字段重命名
|
||||||
|
- DetailDialog组件字段重命名
|
||||||
|
- ImportDialog组件URL修正
|
||||||
|
- 测试新增和修改功能
|
||||||
|
|
||||||
|
4. **第四阶段**: 全面测试
|
||||||
|
- 功能测试
|
||||||
|
- 回归测试
|
||||||
|
- 兼容性测试
|
||||||
|
|
||||||
|
### 12.2 回滚方案
|
||||||
|
|
||||||
|
如发现问题严重,可按以下步骤回滚:
|
||||||
|
|
||||||
|
1. 恢复API层接口
|
||||||
|
2. 恢复前端文件备份
|
||||||
|
3. 重启前端服务
|
||||||
|
4. 清理浏览器缓存
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 附录
|
||||||
|
|
||||||
|
### 附录A: 相关文档
|
||||||
|
|
||||||
|
- [中介黑名单管理API文档-v2.0.md](../api/中介黑名单管理API文档-v2.0.md)
|
||||||
|
- [中介黑名单后端设计文档.md](../docs/中介黑名单后端.md)
|
||||||
|
|
||||||
|
### 附录B: 变更历史
|
||||||
|
|
||||||
|
| 版本 | 日期 | 作者 | 变更说明 |
|
||||||
|
|-----|------|------|---------|
|
||||||
|
| v1.0 | 2026-02-05 | Claude | 初始版本,完成前端适配设计 |
|
||||||
|
|
||||||
|
### 附录C: 审批记录
|
||||||
|
|
||||||
|
| 角色 | 姓名 | 审批状态 | 日期 |
|
||||||
|
|-----|------|---------|------|
|
||||||
|
| 开发 | - | 待审批 | - |
|
||||||
|
| 测试 | - | 待审批 | - |
|
||||||
|
| 产品 | - | 待审批 | - |
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
import openpyxl
|
|
||||||
from openpyxl import Workbook
|
|
||||||
import random
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
|
|
||||||
# 机构名称前缀
|
|
||||||
org_prefixes = [
|
|
||||||
"北京", "上海", "广州", "深圳", "杭州", "成都", "重庆", "武汉", "西安", "南京",
|
|
||||||
"天津", "苏州", "长沙", "郑州", "东莞", "青岛", "沈阳", "宁波", "厦门", "佛山"
|
|
||||||
]
|
|
||||||
|
|
||||||
# 机构类型关键词
|
|
||||||
org_types = [
|
|
||||||
"投资咨询", "资产管理", "证券投资", "基金管理", "股权投资",
|
|
||||||
"财富管理", "金融信息服务", "商务咨询", "企业咨询", "投资顾问"
|
|
||||||
]
|
|
||||||
|
|
||||||
# 机构后缀
|
|
||||||
org_suffixes = ["有限公司", "股份有限公司", "集团", "企业", "事务所"]
|
|
||||||
|
|
||||||
# 主体类型
|
|
||||||
entity_types = ["企业", "事业单位", "社会组织"]
|
|
||||||
|
|
||||||
# 企业性质
|
|
||||||
corp_natures = [
|
|
||||||
"有限责任公司", "股份有限公司", "国有独资", "集体企业",
|
|
||||||
"私营企业", "中外合资", "外商独资", "港澳台合资"
|
|
||||||
]
|
|
||||||
|
|
||||||
# 行业分类
|
|
||||||
industry_classes = ["金融业", "商务服务业", "科学研究和技术服务业"]
|
|
||||||
|
|
||||||
# 所属行业
|
|
||||||
industries = [
|
|
||||||
"货币金融服务", "资本市场服务", "保险业", "其他金融业",
|
|
||||||
"企业管理服务", "法律服务", "咨询与调查", "广告业",
|
|
||||||
"研究和试验发展", "专业技术服务业", "科技推广和应用服务业"
|
|
||||||
]
|
|
||||||
|
|
||||||
# 证件类型
|
|
||||||
id_types = ["身份证", "护照", "其他"]
|
|
||||||
|
|
||||||
# 统一社会信用代码生成(18位)
|
|
||||||
def generate_credit_code():
|
|
||||||
area_code = f"{random.randint(110000, 659900):06d}"
|
|
||||||
org_code = ''.join([str(random.randint(0, 9)) for _ in range(9)])
|
|
||||||
check_code = random.randint(0, 9)
|
|
||||||
return f"{area_code}{org_code}{check_code}"
|
|
||||||
|
|
||||||
# 生成法定代表人姓名
|
|
||||||
def generate_person_name():
|
|
||||||
surnames = ["王", "李", "张", "刘", "陈", "杨", "黄", "赵", "周", "吴",
|
|
||||||
"徐", "孙", "马", "胡", "朱", "郭", "何", "罗", "高", "林"]
|
|
||||||
names1 = ["伟", "芳", "娜", "敏", "静", "丽", "强", "磊", "军", "洋",
|
|
||||||
"勇", "艳", "杰", "娟", "涛", "明", "超", "秀英", "霞", "平"]
|
|
||||||
names2 = ["", "刚", "英", "华", "文", "平", "建", "国", "志", "海"]
|
|
||||||
return random.choice(surnames) + random.choice(names1) + random.choice(names2)
|
|
||||||
|
|
||||||
# 生成身份证号(18位)
|
|
||||||
def generate_id_card():
|
|
||||||
# 地区码(6位) + 出生日期(8位) + 顺序码(3位) + 校验码(1位)
|
|
||||||
area_code = f"{random.randint(110000, 659900):06d}"
|
|
||||||
year = random.randint(1960, 1995)
|
|
||||||
month = f"{random.randint(1, 12):02d}"
|
|
||||||
day = f"{random.randint(1, 28):02d}"
|
|
||||||
birth_date = f"{year}{month}{day}"
|
|
||||||
sequence = f"{random.randint(1, 999):03d}"
|
|
||||||
check_code = random.randint(0, 9)
|
|
||||||
return f"{area_code}{birth_date}{sequence}{check_code}"
|
|
||||||
|
|
||||||
# 生成注册地址
|
|
||||||
def generate_address():
|
|
||||||
districts = ["朝阳区", "海淀区", "西城区", "东城区", "丰台区",
|
|
||||||
"浦东新区", "黄浦区", "静安区", "徐汇区", "天河区",
|
|
||||||
"福田区", "南山区", "罗湖区", "西湖区", "江干区"]
|
|
||||||
streets = ["建设路", "人民路", "解放路", "和平路", "文化路",
|
|
||||||
"科技路", "创新路", "发展路", "创业路", "工业路"]
|
|
||||||
buildings = ["大厦", "中心", "广场", "写字楼", "科技园"]
|
|
||||||
return f"{random.choice(districts)}{random.choice(streets)}{random.randint(1,999)}号{random.choice(buildings)}"
|
|
||||||
|
|
||||||
# 生成成立日期
|
|
||||||
def generate_establish_date():
|
|
||||||
start_date = datetime(2000, 1, 1)
|
|
||||||
end_date = datetime(2024, 12, 31)
|
|
||||||
days_between = (end_date - start_date).days
|
|
||||||
random_days = random.randint(0, days_between)
|
|
||||||
return (start_date + timedelta(days=random_days)).strftime("%Y-%m-%d")
|
|
||||||
|
|
||||||
# 生成股东名称
|
|
||||||
def generate_shareholder():
|
|
||||||
types = [
|
|
||||||
lambda: f"{random.choice(org_prefixes)}{random.choice(['投资', '资本', '控股', '集团'])}有限公司",
|
|
||||||
lambda: generate_person_name() + random.choice(["", "(自然人)"])
|
|
||||||
]
|
|
||||||
return random.choice(types)()
|
|
||||||
|
|
||||||
# 生成备注
|
|
||||||
def generate_remark():
|
|
||||||
remarks = [
|
|
||||||
"", "", "", "",
|
|
||||||
"重点监控", "已整改", "存在风险", "待核查"
|
|
||||||
]
|
|
||||||
return random.choice(remarks)
|
|
||||||
|
|
||||||
# 生成单条机构数据
|
|
||||||
def generate_org_data(index):
|
|
||||||
# 随机决定有几个股东(1-5个)
|
|
||||||
shareholder_count = random.randint(1, 5)
|
|
||||||
shareholders = [generate_shareholder() for _ in range(shareholder_count)]
|
|
||||||
# 补齐到5个
|
|
||||||
while len(shareholders) < 5:
|
|
||||||
shareholders.append("")
|
|
||||||
|
|
||||||
# 证件类型
|
|
||||||
id_type = random.choice(id_types)
|
|
||||||
id_card = generate_id_card() if id_type == "身份证" else f"{random.choice(['A', 'B', 'C'])}{random.randint(10000, 99999)}"
|
|
||||||
|
|
||||||
return {
|
|
||||||
"id": index,
|
|
||||||
"orgName": f"{random.choice(org_prefixes)}{random.choice(org_types)}{random.choice(org_suffixes)}",
|
|
||||||
"creditCode": generate_credit_code(),
|
|
||||||
"entityType": random.choice(entity_types),
|
|
||||||
"corpNature": random.choice(corp_natures) if random.choice([True, False]) else "",
|
|
||||||
"industryClass": random.choice(industry_classes),
|
|
||||||
"industry": random.choice(industries),
|
|
||||||
"establishDate": generate_establish_date(),
|
|
||||||
"regAddress": generate_address(),
|
|
||||||
"legalRep": generate_person_name(),
|
|
||||||
"legalRepIdType": id_type,
|
|
||||||
"legalRepIdNo": id_card,
|
|
||||||
"shareholder1": shareholders[0],
|
|
||||||
"shareholder2": shareholders[1],
|
|
||||||
"shareholder3": shareholders[2],
|
|
||||||
"shareholder4": shareholders[3],
|
|
||||||
"shareholder5": shareholders[4],
|
|
||||||
"remark": generate_remark()
|
|
||||||
}
|
|
||||||
|
|
||||||
# 生成数据并保存到Excel
|
|
||||||
def generate_org_test_data(filename, count=1000, start_id=1):
|
|
||||||
# 读取模板获取表头
|
|
||||||
template_path = "机构中介黑名单模板_1769674571626.xlsx"
|
|
||||||
template_wb = openpyxl.load_workbook(template_path)
|
|
||||||
template_ws = template_wb.active
|
|
||||||
|
|
||||||
# 创建新工作簿
|
|
||||||
wb = Workbook()
|
|
||||||
ws = wb.active
|
|
||||||
ws.title = "机构中介黑名单"
|
|
||||||
|
|
||||||
# 复制表头
|
|
||||||
for cell in template_ws[1]:
|
|
||||||
new_cell = ws.cell(row=1, column=cell.column, value=cell.value)
|
|
||||||
|
|
||||||
# 生成数据
|
|
||||||
data_list = []
|
|
||||||
for i in range(count):
|
|
||||||
data = generate_org_data(start_id + i)
|
|
||||||
data_list.append(data)
|
|
||||||
|
|
||||||
# 按照模板列顺序写入数据
|
|
||||||
# 列顺序:机构名称、统一社会信用代码、主体类型、企业性质、行业分类、所属行业、
|
|
||||||
# 成立日期、注册地址、法定代表人、法定代表人证件类型、法定代表人证件号码、
|
|
||||||
# 股东1、股东2、股东3、股东4、股东5、备注
|
|
||||||
for row_idx, data in enumerate(data_list, start=2):
|
|
||||||
ws.cell(row=row_idx, column=1, value=data["orgName"])
|
|
||||||
ws.cell(row=row_idx, column=2, value=data["creditCode"])
|
|
||||||
ws.cell(row=row_idx, column=3, value=data["entityType"])
|
|
||||||
ws.cell(row=row_idx, column=4, value=data["corpNature"])
|
|
||||||
ws.cell(row=row_idx, column=5, value=data["industryClass"])
|
|
||||||
ws.cell(row=row_idx, column=6, value=data["industry"])
|
|
||||||
ws.cell(row=row_idx, column=7, value=data["establishDate"])
|
|
||||||
ws.cell(row=row_idx, column=8, value=data["regAddress"])
|
|
||||||
ws.cell(row=row_idx, column=9, value=data["legalRep"])
|
|
||||||
ws.cell(row=row_idx, column=10, value=data["legalRepIdType"])
|
|
||||||
ws.cell(row=row_idx, column=11, value=data["legalRepIdNo"])
|
|
||||||
ws.cell(row=row_idx, column=12, value=data["shareholder1"])
|
|
||||||
ws.cell(row=row_idx, column=13, value=data["shareholder2"])
|
|
||||||
ws.cell(row=row_idx, column=14, value=data["shareholder3"])
|
|
||||||
ws.cell(row=row_idx, column=15, value=data["shareholder4"])
|
|
||||||
ws.cell(row=row_idx, column=16, value=data["shareholder5"])
|
|
||||||
ws.cell(row=row_idx, column=17, value=data["remark"])
|
|
||||||
|
|
||||||
# 保存文件
|
|
||||||
wb.save(filename)
|
|
||||||
print(f"已生成文件: {filename}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("开始生成机构中介黑名单测试数据...")
|
|
||||||
generate_org_test_data("机构中介黑名单测试数据_1000条.xlsx", 1000, 1)
|
|
||||||
generate_org_test_data("机构中介黑名单测试数据_1000条_第2批.xlsx", 1000, 1001)
|
|
||||||
print("完成!")
|
|
||||||
BIN
doc/test-data/intermediary/entity_1770260448522.xlsx
Normal file
BIN
doc/test-data/intermediary/entity_1770260448522.xlsx
Normal file
Binary file not shown.
181
doc/test-data/intermediary/generate_1000_entity_data.py
Normal file
181
doc/test-data/intermediary/generate_1000_entity_data.py
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
import random
|
||||||
|
import string
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
# 机构名称前缀
|
||||||
|
company_prefixes = ['北京市', '上海市', '广州市', '深圳市', '杭州市', '成都市', '武汉市', '南京市', '西安市', '重庆市']
|
||||||
|
company_keywords = ['房产', '地产', '置业', '中介', '经纪', '咨询', '投资', '资产', '物业', '不动产']
|
||||||
|
company_suffixes = ['有限公司', '股份有限公司', '集团', '企业', '合伙企业', '有限责任公司']
|
||||||
|
|
||||||
|
# 主体类型
|
||||||
|
entity_types = ['企业', '个体工商户', '农民专业合作社', '其他组织']
|
||||||
|
|
||||||
|
# 企业性质
|
||||||
|
enterprise_natures = ['国有企业', '集体企业', '私营企业', '混合所有制企业', '外商投资企业', '港澳台投资企业']
|
||||||
|
|
||||||
|
# 行业分类
|
||||||
|
industry_classes = ['房地产业', '金融业', '租赁和商务服务业', '建筑业', '批发和零售业']
|
||||||
|
|
||||||
|
# 所属行业
|
||||||
|
industry_names = [
|
||||||
|
'房地产中介服务', '房地产经纪', '房地产开发经营', '物业管理',
|
||||||
|
'投资咨询', '资产管理', '商务咨询', '市场调查',
|
||||||
|
'建筑工程', '装饰装修', '园林绿化'
|
||||||
|
]
|
||||||
|
|
||||||
|
# 法定代表人姓名
|
||||||
|
surnames = ['王', '李', '张', '刘', '陈', '杨', '黄', '赵', '周', '吴', '徐', '孙', '马', '胡', '朱', '郭', '何', '罗', '高', '林']
|
||||||
|
given_names = ['伟', '芳', '娜', '敏', '静', '丽', '强', '磊', '军', '洋', '勇', '艳', '杰', '娟', '涛', '明', '超', '秀英', '霞', '平']
|
||||||
|
|
||||||
|
# 证件类型
|
||||||
|
cert_types = ['身份证', '护照', '港澳通行证', '台胞证', '其他']
|
||||||
|
|
||||||
|
# 常用地址
|
||||||
|
provinces = ['北京市', '上海市', '广东省', '浙江省', '江苏省', '四川省', '湖北省', '河南省', '山东省', '福建省']
|
||||||
|
cities = ['朝阳区', '海淀区', '浦东新区', '黄浦区', '天河区', '福田区', '西湖区', '滨江区', '鼓楼区', '玄武区',
|
||||||
|
'武侯区', '江汉区', '金水区', '市南区', '思明区']
|
||||||
|
districts = ['街道', '大道', '路', '巷', '小区', '花园', '广场', '大厦']
|
||||||
|
street_numbers = ['1号', '2号', '3号', '88号', '66号', '108号', '188号', '888号', '666号', '168号']
|
||||||
|
|
||||||
|
# 股东姓名
|
||||||
|
shareholder_names = [
|
||||||
|
'张伟', '李芳', '王强', '刘军', '陈静', '杨洋', '黄勇', '赵艳',
|
||||||
|
'周杰', '吴娟', '徐涛', '孙明', '马超', '胡秀英', '朱霞', '郭平',
|
||||||
|
'何桂英', '罗玉兰', '高萍', '林毅', '王浩', '李宇', '张轩', '刘然'
|
||||||
|
]
|
||||||
|
|
||||||
|
def generate_company_name():
|
||||||
|
"""生成机构名称"""
|
||||||
|
prefix = random.choice(company_prefixes)
|
||||||
|
keyword = random.choice(company_keywords)
|
||||||
|
suffix = random.choice(company_suffixes)
|
||||||
|
return f"{prefix}{keyword}{suffix}"
|
||||||
|
|
||||||
|
def generate_social_credit_code():
|
||||||
|
"""生成统一社会信用代码(18位)"""
|
||||||
|
# 统一社会信用代码规则:18位,第一位为登记管理部门代码(1-5),第二位为机构类别代码(1-9)
|
||||||
|
dept_code = random.choice(['1', '2', '3', '4', '5'])
|
||||||
|
org_code = random.choice(['1', '2', '3', '4', '5', '6', '7', '8', '9'])
|
||||||
|
rest = ''.join([str(random.randint(0, 9)) for _ in range(16)])
|
||||||
|
return f"{dept_code}{org_code}{rest}"
|
||||||
|
|
||||||
|
def generate_id_card():
|
||||||
|
"""生成身份证号码(18位,简化版)"""
|
||||||
|
# 地区码(前6位)
|
||||||
|
area_code = f"{random.randint(110000, 650000):06d}"
|
||||||
|
# 出生日期(8位)
|
||||||
|
birth_year = random.randint(1960, 1990)
|
||||||
|
birth_month = f"{random.randint(1, 12):02d}"
|
||||||
|
birth_day = f"{random.randint(1, 28):02d}"
|
||||||
|
birth_date = f"{birth_year}{birth_month}{birth_day}"
|
||||||
|
# 顺序码(3位)
|
||||||
|
sequence = f"{random.randint(1, 999):03d}"
|
||||||
|
# 校验码(1位)
|
||||||
|
check_code = random.randint(0, 9)
|
||||||
|
return f"{area_code}{birth_date}{sequence}{check_code}"
|
||||||
|
|
||||||
|
def generate_other_id():
|
||||||
|
"""生成其他证件号码"""
|
||||||
|
return f"{random.randint(10000000, 99999999):08d}"
|
||||||
|
|
||||||
|
def generate_register_address():
|
||||||
|
"""生成注册地址"""
|
||||||
|
province = random.choice(provinces)
|
||||||
|
city = random.choice(cities)
|
||||||
|
district = random.choice(districts)
|
||||||
|
number = random.choice(street_numbers)
|
||||||
|
return f"{province}{city}{district}{number}"
|
||||||
|
|
||||||
|
def generate_establish_date():
|
||||||
|
"""生成成立日期(2000-2024年之间)"""
|
||||||
|
start_date = datetime(2000, 1, 1)
|
||||||
|
end_date = datetime(2024, 12, 31)
|
||||||
|
time_between = end_date - start_date
|
||||||
|
days_between = time_between.days
|
||||||
|
random_days = random.randrange(days_between)
|
||||||
|
return start_date + timedelta(days=random_days)
|
||||||
|
|
||||||
|
def generate_legal_representative():
|
||||||
|
"""生成法定代表人"""
|
||||||
|
name = random.choice(surnames) + random.choice(given_names)
|
||||||
|
cert_type = random.choice(cert_types)
|
||||||
|
cert_no = generate_id_card() if cert_type == '身份证' else generate_other_id()
|
||||||
|
return name, cert_type, cert_no
|
||||||
|
|
||||||
|
def generate_shareholders():
|
||||||
|
"""生成股东列表(1-5个股东)"""
|
||||||
|
shareholder_count = random.randint(1, 5)
|
||||||
|
selected_shareholders = random.sample(shareholder_names, shareholder_count)
|
||||||
|
shareholders = [None] * 5
|
||||||
|
for i, shareholder in enumerate(selected_shareholders):
|
||||||
|
shareholders[i] = shareholder
|
||||||
|
return shareholders
|
||||||
|
|
||||||
|
def generate_entity(index):
|
||||||
|
"""生成单条机构中介数据"""
|
||||||
|
# 基本信息
|
||||||
|
enterprise_name = generate_company_name()
|
||||||
|
social_credit_code = generate_social_credit_code()
|
||||||
|
entity_type = random.choice(entity_types)
|
||||||
|
enterprise_nature = random.choice(enterprise_natures)
|
||||||
|
industry_class = random.choice(industry_classes)
|
||||||
|
industry_name = random.choice(industry_names)
|
||||||
|
|
||||||
|
# 成立日期
|
||||||
|
establish_date = generate_establish_date()
|
||||||
|
|
||||||
|
# 注册地址
|
||||||
|
register_address = generate_register_address()
|
||||||
|
|
||||||
|
# 法定代表人信息
|
||||||
|
legal_name, legal_cert_type, legal_cert_no = generate_legal_representative()
|
||||||
|
|
||||||
|
# 股东
|
||||||
|
shareholders = generate_shareholders()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'机构名称*': enterprise_name,
|
||||||
|
'统一社会信用代码*': social_credit_code,
|
||||||
|
'主体类型': entity_type,
|
||||||
|
'企业性质': enterprise_nature if random.random() > 0.3 else '',
|
||||||
|
'行业分类': industry_class if random.random() > 0.3 else '',
|
||||||
|
'所属行业': industry_name if random.random() > 0.2 else '',
|
||||||
|
'成立日期': establish_date.strftime('%Y-%m-%d') if random.random() > 0.4 else '',
|
||||||
|
'注册地址': register_address,
|
||||||
|
'法定代表人': legal_name,
|
||||||
|
'法定代表人证件类型': legal_cert_type,
|
||||||
|
'法定代表人证件号码': legal_cert_no,
|
||||||
|
'股东1': shareholders[0] if shareholders[0] else '',
|
||||||
|
'股东2': shareholders[1] if shareholders[1] else '',
|
||||||
|
'股东3': shareholders[2] if shareholders[2] else '',
|
||||||
|
'股东4': shareholders[3] if shareholders[3] else '',
|
||||||
|
'股东5': shareholders[4] if shareholders[4] else '',
|
||||||
|
'备注': f'测试数据{index}' if random.random() > 0.5 else ''
|
||||||
|
}
|
||||||
|
|
||||||
|
# 生成第一个1000条数据
|
||||||
|
print("正在生成第一批1000条机构中介黑名单数据...")
|
||||||
|
data = [generate_entity(i) for i in range(1, 1001)]
|
||||||
|
df = pd.DataFrame(data)
|
||||||
|
|
||||||
|
# 保存第一个文件
|
||||||
|
output1 = r'D:\ccdi\ccdi\doc\test-data\intermediary\机构中介黑名单测试数据_1000条_第1批.xlsx'
|
||||||
|
df.to_excel(output1, index=False, engine='openpyxl')
|
||||||
|
print(f"已生成第一个文件: {output1}")
|
||||||
|
|
||||||
|
# 生成第二个1000条数据
|
||||||
|
print("正在生成第二批1000条机构中介黑名单数据...")
|
||||||
|
data2 = [generate_entity(i) for i in range(1, 1001)]
|
||||||
|
df2 = pd.DataFrame(data2)
|
||||||
|
|
||||||
|
# 保存第二个文件
|
||||||
|
output2 = r'D:\ccdi\ccdi\doc\test-data\intermediary\机构中介黑名单测试数据_1000条_第2批.xlsx'
|
||||||
|
df2.to_excel(output2, index=False, engine='openpyxl')
|
||||||
|
print(f"已生成第二个文件: {output2}")
|
||||||
|
|
||||||
|
print("\n✅ 生成完成!")
|
||||||
|
print(f"文件1: {output1}")
|
||||||
|
print(f"文件2: {output2}")
|
||||||
|
print(f"\n每个文件包含1000条测试数据")
|
||||||
|
print(f"数据格式与CcdiIntermediaryEntityExcel.java定义一致")
|
||||||
110
doc/test-data/intermediary/generate_1000_intermediary_data.py
Normal file
110
doc/test-data/intermediary/generate_1000_intermediary_data.py
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
import random
|
||||||
|
import string
|
||||||
|
from datetime import datetime
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
# 常用姓氏和名字
|
||||||
|
surnames = ['王', '李', '张', '刘', '陈', '杨', '黄', '赵', '周', '吴', '徐', '孙', '马', '胡', '朱', '郭', '何', '罗', '高', '林']
|
||||||
|
given_names = ['伟', '芳', '娜', '敏', '静', '丽', '强', '磊', '军', '洋', '勇', '艳', '杰', '娟', '涛', '明', '超', '秀英', '霞', '平', '刚', '桂英', '玉兰', '萍', '毅', '浩', '宇', '轩', '然', '凯']
|
||||||
|
|
||||||
|
# 人员类型
|
||||||
|
person_types = ['中介', '职业背债人', '房产中介']
|
||||||
|
person_sub_types = ['本人', '配偶', '子女', '其他']
|
||||||
|
genders = ['M', 'F', 'O']
|
||||||
|
id_types = ['身份证', '护照', '港澳通行证', '台胞证', '军官证']
|
||||||
|
relation_types = ['配偶', '子女', '父母', '兄弟姐妹', '其他']
|
||||||
|
|
||||||
|
# 常用地址
|
||||||
|
provinces = ['北京市', '上海市', '广东省', '浙江省', '江苏省', '四川省', '湖北省', '河南省', '山东省', '福建省']
|
||||||
|
cities = ['朝阳区', '海淀区', '浦东新区', '黄浦区', '天河区', '福田区', '西湖区', '滨江区', '鼓楼区', '玄武区']
|
||||||
|
districts = ['街道1号', '大道2号', '路3号', '巷4号', '小区5栋', '花园6号', '广场7号', '大厦8号楼']
|
||||||
|
|
||||||
|
# 公司和职位
|
||||||
|
companies = ['房产中介有限公司', '置业咨询公司', '房产经纪公司', '地产代理公司', '不动产咨询公司', '房屋租赁公司', '物业管理公司', '投资咨询公司']
|
||||||
|
positions = ['房产经纪人', '销售经理', '业务员', '置业顾问', '店长', '区域经理', '高级经纪人', '项目经理']
|
||||||
|
|
||||||
|
# 生成身份证号码(简化版,仅用于测试)
|
||||||
|
def generate_id_card():
|
||||||
|
# 地区码(前6位)
|
||||||
|
area_code = f"{random.randint(110000, 650000):06d}"
|
||||||
|
# 出生日期(8位)
|
||||||
|
birth_year = random.randint(1960, 2000)
|
||||||
|
birth_month = f"{random.randint(1, 12):02d}"
|
||||||
|
birth_day = f"{random.randint(1, 28):02d}"
|
||||||
|
birth_date = f"{birth_year}{birth_month}{birth_day}"
|
||||||
|
# 顺序码(3位)
|
||||||
|
sequence = f"{random.randint(1, 999):03d}"
|
||||||
|
# 校验码(1位)
|
||||||
|
check_code = random.randint(0, 9)
|
||||||
|
return f"{area_code}{birth_date}{sequence}{check_code}"
|
||||||
|
|
||||||
|
# 生成手机号
|
||||||
|
def generate_phone():
|
||||||
|
second_digits = ['3', '5', '7', '8', '9']
|
||||||
|
second = random.choice(second_digits)
|
||||||
|
return f"1{second}{''.join([str(random.randint(0, 9)) for _ in range(9)])}"
|
||||||
|
|
||||||
|
# 生成统一信用代码
|
||||||
|
def generate_credit_code():
|
||||||
|
return f"91{''.join([str(random.randint(0, 9)) for _ in range(16)])}"
|
||||||
|
|
||||||
|
# 生成微信号
|
||||||
|
def generate_wechat():
|
||||||
|
return f"wx_{''.join([random.choice(string.ascii_lowercase + string.digits) for _ in range(8)])}"
|
||||||
|
|
||||||
|
# 生成单条数据
|
||||||
|
def generate_person(index):
|
||||||
|
person_type = random.choice(person_types)
|
||||||
|
gender = random.choice(genders)
|
||||||
|
|
||||||
|
# 根据性别选择更合适的名字
|
||||||
|
if gender == 'M':
|
||||||
|
name = random.choice(surnames) + random.choice(['伟', '强', '磊', '军', '勇', '杰', '涛', '明', '超', '毅', '浩', '宇', '轩'])
|
||||||
|
else:
|
||||||
|
name = random.choice(surnames) + random.choice(['芳', '娜', '敏', '静', '丽', '艳', '娟', '秀英', '霞', '平', '桂英', '玉兰', '萍'])
|
||||||
|
|
||||||
|
id_type = random.choice(id_types)
|
||||||
|
id_card = generate_id_card() if id_type == '身份证' else f"{random.randint(10000000, 99999999):08d}"
|
||||||
|
|
||||||
|
return {
|
||||||
|
'姓名': name,
|
||||||
|
'人员类型': person_type,
|
||||||
|
'人员子类型': random.choice(person_sub_types),
|
||||||
|
'性别': gender,
|
||||||
|
'证件类型': id_type,
|
||||||
|
'证件号码': id_card,
|
||||||
|
'手机号码': generate_phone(),
|
||||||
|
'微信号': generate_wechat() if random.random() > 0.3 else '',
|
||||||
|
'联系地址': f"{random.choice(provinces)}{random.choice(cities)}{random.choice(districts)}",
|
||||||
|
'所在公司': random.choice(companies) if random.random() > 0.2 else '',
|
||||||
|
'企业统一信用码': generate_credit_code() if random.random() > 0.5 else '',
|
||||||
|
'职位': random.choice(positions) if random.random() > 0.3 else '',
|
||||||
|
'关联人员ID': f"ID{random.randint(10000, 99999)}" if random.random() > 0.6 else '',
|
||||||
|
'关系类型': random.choice(relation_types) if random.random() > 0.6 else '',
|
||||||
|
'备注': f'测试数据{index}' if random.random() > 0.5 else ''
|
||||||
|
}
|
||||||
|
|
||||||
|
# 生成1000条数据
|
||||||
|
print("正在生成1000条个人中介黑名单数据...")
|
||||||
|
data = [generate_person(i) for i in range(1, 1001)]
|
||||||
|
df = pd.DataFrame(data)
|
||||||
|
|
||||||
|
# 保存第一个文件
|
||||||
|
output1 = r'D:\ccdi\ccdi\doc\test-data\intermediary\个人中介黑名单测试数据_1000条_第1批.xlsx'
|
||||||
|
df.to_excel(output1, index=False)
|
||||||
|
print(f"已生成第一个文件: {output1}")
|
||||||
|
|
||||||
|
# 生成第二个1000条数据
|
||||||
|
print("正在生成第二批1000条个人中介黑名单数据...")
|
||||||
|
data2 = [generate_person(i) for i in range(1, 1001)]
|
||||||
|
df2 = pd.DataFrame(data2)
|
||||||
|
|
||||||
|
# 保存第二个文件
|
||||||
|
output2 = r'D:\ccdi\ccdi\doc\test-data\intermediary\个人中介黑名单测试数据_1000条_第2批.xlsx'
|
||||||
|
df2.to_excel(output2, index=False)
|
||||||
|
print(f"已生成第二个文件: {output2}")
|
||||||
|
|
||||||
|
print("\n生成完成!")
|
||||||
|
print(f"文件1: {output1}")
|
||||||
|
print(f"文件2: {output2}")
|
||||||
|
print(f"\n每个文件包含1000条测试数据")
|
||||||
BIN
doc/test-data/intermediary/个人中介黑名单模板_1770258896626.xlsx
Normal file
BIN
doc/test-data/intermediary/个人中介黑名单模板_1770258896626.xlsx
Normal file
Binary file not shown.
BIN
doc/test-data/intermediary/个人中介黑名单测试数据_1000条_第1批.xlsx
Normal file
BIN
doc/test-data/intermediary/个人中介黑名单测试数据_1000条_第1批.xlsx
Normal file
Binary file not shown.
BIN
doc/test-data/intermediary/个人中介黑名单测试数据_1000条_第2批.xlsx
Normal file
BIN
doc/test-data/intermediary/个人中介黑名单测试数据_1000条_第2批.xlsx
Normal file
Binary file not shown.
BIN
doc/test-data/intermediary/机构中介黑名单测试数据_1000条_第1批.xlsx
Normal file
BIN
doc/test-data/intermediary/机构中介黑名单测试数据_1000条_第1批.xlsx
Normal file
Binary file not shown.
BIN
doc/test-data/intermediary/机构中介黑名单测试数据_1000条_第2批.xlsx
Normal file
BIN
doc/test-data/intermediary/机构中介黑名单测试数据_1000条_第2批.xlsx
Normal file
Binary file not shown.
@@ -1,268 +0,0 @@
|
|||||||
"""
|
|
||||||
中介黑名单导入功能测试脚本
|
|
||||||
|
|
||||||
测试目标:
|
|
||||||
1. 验证机构中介导入时 certificate_no 字段不能为 null 的修复
|
|
||||||
2. 验证个人中介导入功能正常
|
|
||||||
3. 验证更新模式功能正常
|
|
||||||
|
|
||||||
测试数据准备:
|
|
||||||
- 个人中介:2条记录
|
|
||||||
- 机构中介:2条记录
|
|
||||||
"""
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
BASE_URL = "http://localhost:8080"
|
|
||||||
|
|
||||||
def login():
|
|
||||||
"""登录并获取token"""
|
|
||||||
url = f"{BASE_URL}/login/test"
|
|
||||||
data = {
|
|
||||||
"username": "admin",
|
|
||||||
"password": "admin123"
|
|
||||||
}
|
|
||||||
response = requests.post(url, json=data)
|
|
||||||
if response.status_code == 200:
|
|
||||||
result = response.json()
|
|
||||||
if result.get("code") == 200:
|
|
||||||
token = result.get("token")
|
|
||||||
print(f"✓ 登录成功,获取token: {token[:20]}...")
|
|
||||||
return token
|
|
||||||
print(f"✗ 登录失败: {response.text}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_headers(token):
|
|
||||||
"""获取请求头"""
|
|
||||||
return {
|
|
||||||
"Authorization": f"Bearer {token}"
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_import_person_intermediary(token):
|
|
||||||
"""测试个人中介导入"""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("测试1: 个人中介导入功能")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
# 准备个人中介数据(直接通过API调用测试)
|
|
||||||
url = f"{BASE_URL}/dpc/intermediary"
|
|
||||||
headers = get_headers(token)
|
|
||||||
headers["Content-Type"] = "application/json"
|
|
||||||
|
|
||||||
person_data = {
|
|
||||||
"name": "测试个人中介",
|
|
||||||
"certificateNo": "110101199001011234",
|
|
||||||
"intermediaryType": "1",
|
|
||||||
"status": "0",
|
|
||||||
"remark": "测试个人中介导入",
|
|
||||||
"indivType": "中介",
|
|
||||||
"indivSubType": "本人",
|
|
||||||
"indivGender": "M",
|
|
||||||
"indivCertType": "身份证",
|
|
||||||
"indivPhone": "13800138000",
|
|
||||||
"indivWechat": "test_wx_id",
|
|
||||||
"indivAddress": "北京市朝阳区",
|
|
||||||
"indivCompany": "测试公司",
|
|
||||||
"indivPosition": "经纪人"
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.post(url, json=person_data, headers=headers)
|
|
||||||
if response.status_code == 200:
|
|
||||||
result = response.json()
|
|
||||||
if result.get("code") == 200:
|
|
||||||
print("✓ 个人中介导入成功")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"✗ 个人中介导入失败: {result.get('msg')}")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print(f"✗ 个人中介导入请求失败: {response.status_code} - {response.text}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_import_entity_intermediary(token):
|
|
||||||
"""测试机构中介导入"""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("测试2: 机构中介导入功能")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
# 准备机构中介数据
|
|
||||||
url = f"{BASE_URL}/dpc/intermediary"
|
|
||||||
headers = get_headers(token)
|
|
||||||
headers["Content-Type"] = "application/json"
|
|
||||||
|
|
||||||
entity_data = {
|
|
||||||
"name": "测试机构中介有限公司",
|
|
||||||
"certificateNo": "91110108MA0000001A", # 统一社会信用代码
|
|
||||||
"intermediaryType": "2",
|
|
||||||
"status": "0",
|
|
||||||
"remark": "测试机构中介导入",
|
|
||||||
"corpCreditCode": "91110108MA0000001A",
|
|
||||||
"corpType": "有限责任公司",
|
|
||||||
"corpNature": "民营企业",
|
|
||||||
"corpIndustryCategory": "房地产业",
|
|
||||||
"corpIndustry": "房地产中介服务",
|
|
||||||
"corpEstablishDate": "2020-01-01",
|
|
||||||
"corpAddress": "北京市海淀区",
|
|
||||||
"corpLegalRep": "张三",
|
|
||||||
"corpLegalCertType": "身份证",
|
|
||||||
"corpLegalCertNo": "110101199001011235",
|
|
||||||
"corpShareholder1": "李四",
|
|
||||||
"corpShareholder2": "王五"
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.post(url, json=entity_data, headers=headers)
|
|
||||||
if response.status_code == 200:
|
|
||||||
result = response.json()
|
|
||||||
if result.get("code") == 200:
|
|
||||||
print("✓ 机构中介导入成功")
|
|
||||||
print(f" - 机构名称: {entity_data['name']}")
|
|
||||||
print(f" - 统一社会信用代码: {entity_data['corpCreditCode']}")
|
|
||||||
print(f" - 证件号字段: {entity_data['certificateNo']}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"✗ 机构中介导入失败: {result.get('msg')}")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print(f"✗ 机构中介导入请求失败: {response.status_code} - {response.text}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_import_entity_without_credit_code(token):
|
|
||||||
"""测试机构中介导入时统一社会信用代码为空的情况"""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("测试4: 机构中介导入时统一社会信用代码为空(应该失败)")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
url = f"{BASE_URL}/dpc/intermediary"
|
|
||||||
headers = get_headers(token)
|
|
||||||
headers["Content-Type"] = "application/json"
|
|
||||||
|
|
||||||
# 故意不提供统一社会信用代码
|
|
||||||
entity_data = {
|
|
||||||
"name": "测试机构中介有限公司(无信用代码)",
|
|
||||||
"certificateNo": "", # 空字符串
|
|
||||||
"intermediaryType": "2",
|
|
||||||
"status": "0",
|
|
||||||
"remark": "测试统一社会信用代码为空的情况",
|
|
||||||
"corpCreditCode": "", # 空字符串
|
|
||||||
"corpType": "有限责任公司"
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.post(url, json=entity_data, headers=headers)
|
|
||||||
if response.status_code == 200:
|
|
||||||
result = response.json()
|
|
||||||
if result.get("code") != 200:
|
|
||||||
# 预期失败
|
|
||||||
print(f"✓ 预期行为:导入被拒绝,错误信息: {result.get('msg')}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
# 不应该成功
|
|
||||||
print(f"✗ 测试失败:统一社会信用代码为空时不应该导入成功")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print(f"✗ 请求失败: {response.status_code} - {response.text}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def test_query_intermediary_list(token):
|
|
||||||
"""测试查询中介列表"""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("测试3: 查询中介列表")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
url = f"{BASE_URL}/dpc/intermediary/list"
|
|
||||||
headers = get_headers(token)
|
|
||||||
|
|
||||||
params = {
|
|
||||||
"pageNum": 1,
|
|
||||||
"pageSize": 10
|
|
||||||
}
|
|
||||||
|
|
||||||
response = requests.get(url, params=params, headers=headers)
|
|
||||||
if response.status_code == 200:
|
|
||||||
result = response.json()
|
|
||||||
if result.get("code") == 200:
|
|
||||||
rows = result.get("rows", [])
|
|
||||||
total = result.get("total", 0)
|
|
||||||
print(f"✓ 查询成功,共 {total} 条记录")
|
|
||||||
for item in rows:
|
|
||||||
print(f" - {item['name']} ({item.get('intermediaryTypeName', '未知')}) - 证件号: {item.get('certificateNo', '无')}")
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
print(f"✗ 查询失败: {result.get('msg')}")
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
print(f"✗ 查询请求失败: {response.status_code} - {response.text}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def generate_test_report(results):
|
|
||||||
"""生成测试报告"""
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("测试报告")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
total_tests = len(results)
|
|
||||||
passed_tests = sum(1 for r in results.values() if r)
|
|
||||||
failed_tests = total_tests - passed_tests
|
|
||||||
|
|
||||||
print(f"\n总测试数: {total_tests}")
|
|
||||||
print(f"通过: {passed_tests}")
|
|
||||||
print(f"失败: {failed_tests}")
|
|
||||||
print(f"通过率: {passed_tests/total_tests*100:.1f}%")
|
|
||||||
|
|
||||||
print("\n详细结果:")
|
|
||||||
for test_name, result in results.items():
|
|
||||||
status = "✓ 通过" if result else "✗ 失败"
|
|
||||||
print(f" {test_name}: {status}")
|
|
||||||
|
|
||||||
# 保存报告到文件
|
|
||||||
report_content = {
|
|
||||||
"测试时间": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|
||||||
"总测试数": total_tests,
|
|
||||||
"通过": passed_tests,
|
|
||||||
"失败": failed_tests,
|
|
||||||
"通过率": f"{passed_tests/total_tests*100:.1f}%",
|
|
||||||
"详细结果": {k: "通过" if v else "失败" for k, v in results.items()}
|
|
||||||
}
|
|
||||||
|
|
||||||
with open("doc/test-data/import_test_report.json", "w", encoding="utf-8") as f:
|
|
||||||
json.dump(report_content, f, ensure_ascii=False, indent=2)
|
|
||||||
|
|
||||||
print(f"\n测试报告已保存至: doc/test-data/import_test_report.json")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主测试函数"""
|
|
||||||
print("="*60)
|
|
||||||
print("中介黑名单导入功能测试")
|
|
||||||
print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
results = {}
|
|
||||||
|
|
||||||
# 1. 登录
|
|
||||||
token = login()
|
|
||||||
if not token:
|
|
||||||
print("登录失败,无法继续测试")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 2. 测试个人中介导入
|
|
||||||
results["个人中介导入"] = test_import_person_intermediary(token)
|
|
||||||
|
|
||||||
# 3. 测试机构中介导入
|
|
||||||
results["机构中介导入"] = test_import_entity_intermediary(token)
|
|
||||||
|
|
||||||
# 4. 测试统一社会信用代码为空的情况
|
|
||||||
results["机构中介无信用代码校验"] = test_import_entity_without_credit_code(token)
|
|
||||||
|
|
||||||
# 5. 测试查询列表
|
|
||||||
results["查询列表"] = test_query_intermediary_list(token)
|
|
||||||
|
|
||||||
# 5. 生成测试报告
|
|
||||||
generate_test_report(results)
|
|
||||||
|
|
||||||
print("\n" + "="*60)
|
|
||||||
print("测试完成")
|
|
||||||
print("="*60)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,22 +0,0 @@
|
|||||||
字段中文名,数据类型,长度/精度,是否为空,默认值,说明
|
|
||||||
统一社会信用代码,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:批量导入"
|
|
||||||
|
@@ -1,20 +0,0 @@
|
|||||||
字段中文名,数据类型,长度/精度,是否为空,默认值,说明
|
|
||||||
人员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:接口获取"
|
|
||||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
312
doc/优化说明/中介黑名单导入唯一性校验优化说明_20260205.md
Normal file
312
doc/优化说明/中介黑名单导入唯一性校验优化说明_20260205.md
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
# 中介黑名单导入唯一性校验优化说明
|
||||||
|
|
||||||
|
## 优化时间
|
||||||
|
2026-02-05
|
||||||
|
|
||||||
|
## 优化目的
|
||||||
|
优化批量导入中介黑名单数据时的唯一性校验性能,解决N+1查询问题。
|
||||||
|
|
||||||
|
## 问题描述
|
||||||
|
|
||||||
|
### 原实现问题
|
||||||
|
在导入个人中介和实体中介数据时,原实现存在以下性能问题:
|
||||||
|
|
||||||
|
1. **N+1查询问题**
|
||||||
|
- 在循环中对每条记录调用 `checkPersonIdUnique` 或 `checkSocialCreditCodeUnique`
|
||||||
|
- 导入1000条数据时,产生1000次数据库查询
|
||||||
|
- 代码位置:
|
||||||
|
- `CcdiIntermediaryServiceImpl.importIntermediaryPerson:291`
|
||||||
|
- `CcdiIntermediaryServiceImpl.importIntermediaryEntity:409`
|
||||||
|
|
||||||
|
2. **重复查询问题**
|
||||||
|
- 唯一性校验查询一次(1000次)
|
||||||
|
- 获取bizId再次批量查询一次(1次)
|
||||||
|
- 总计1001次数据库查询
|
||||||
|
|
||||||
|
3. **性能瓶颈**
|
||||||
|
- 大量数据导入时响应慢
|
||||||
|
- 数据库连接占用时间长
|
||||||
|
- 网络往返次数多
|
||||||
|
|
||||||
|
## 优化方案
|
||||||
|
|
||||||
|
### 核心思路
|
||||||
|
**将"循环中逐条查询"改为"一次性批量查询,内存中快速判断"**
|
||||||
|
|
||||||
|
### 优化实现
|
||||||
|
|
||||||
|
#### 1. 个人中介导入优化(importIntermediaryPerson)
|
||||||
|
|
||||||
|
**优化前:**
|
||||||
|
```java
|
||||||
|
// 第一轮:数据验证和分类
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
// 检查唯一性 - 每次循环都查询数据库
|
||||||
|
if (!checkPersonIdUnique(excel.getPersonId(), null)) { // ❌ N+1查询
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二轮:批量处理
|
||||||
|
if (!updateList.isEmpty()) {
|
||||||
|
// 再次查询已存在记录的bizId - 重复查询
|
||||||
|
wrapper.in(CcdiBizIntermediary::getPersonId, personIds);
|
||||||
|
List<CcdiBizIntermediary> existingList = bizIntermediaryMapper.selectList(wrapper);
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**优化后:**
|
||||||
|
```java
|
||||||
|
// 第一轮:收集所有personId
|
||||||
|
for (CcdiIntermediaryPersonExcel excel : list) {
|
||||||
|
if (StringUtils.isNotEmpty(excel.getPersonId())) {
|
||||||
|
personIds.add(excel.getPersonId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二轮:批量查询已存在的记录 - 只查询一次 ✅
|
||||||
|
java.util.Map<String, String> personIdToBizIdMap = new java.util.HashMap<>();
|
||||||
|
if (!personIds.isEmpty()) {
|
||||||
|
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.select(CcdiBizIntermediary::getBizId, CcdiBizIntermediary::getPersonId);
|
||||||
|
wrapper.in(CcdiBizIntermediary::getPersonId, personIds);
|
||||||
|
List<CcdiBizIntermediary> existingList = bizIntermediaryMapper.selectList(wrapper);
|
||||||
|
|
||||||
|
// 建立personId到bizId的映射
|
||||||
|
for (CcdiBizIntermediary existing : existingList) {
|
||||||
|
personIdToBizIdMap.put(existing.getPersonId(), existing.getBizId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第三轮:数据验证和分类 - 使用Map快速判断
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
// 使用Map快速判断是否存在 - O(1)复杂度,不查询数据库 ✅
|
||||||
|
String existingBizId = personIdToBizIdMap.get(excel.getPersonId());
|
||||||
|
if (existingBizId != null) {
|
||||||
|
// 记录已存在
|
||||||
|
if (updateSupport) {
|
||||||
|
person.setBizId(existingBizId); // 直接使用缓存中的bizId
|
||||||
|
updateList.add(person);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
insertList.add(person);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第四轮:批量处理 - 直接插入和更新,无需额外查询 ✅
|
||||||
|
bizIntermediaryMapper.insertBatch(insertList);
|
||||||
|
bizIntermediaryMapper.updateBatch(updateList);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 实体中介导入优化(importIntermediaryEntity)
|
||||||
|
|
||||||
|
**优化后实现:**
|
||||||
|
```java
|
||||||
|
// 第一轮:收集所有socialCreditCode
|
||||||
|
for (CcdiIntermediaryEntityExcel excel : list) {
|
||||||
|
if (StringUtils.isNotEmpty(excel.getSocialCreditCode())) {
|
||||||
|
socialCreditCodes.add(excel.getSocialCreditCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二轮:批量查询已存在的记录 - 只查询一次 ✅
|
||||||
|
java.util.Map<String, CcdiEnterpriseBaseInfo> existingEntityMap = new java.util.HashMap<>();
|
||||||
|
if (!socialCreditCodes.isEmpty()) {
|
||||||
|
LambdaQueryWrapper<CcdiEnterpriseBaseInfo> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.in(CcdiEnterpriseBaseInfo::getSocialCreditCode, socialCreditCodes);
|
||||||
|
List<CcdiEnterpriseBaseInfo> existingList = enterpriseBaseInfoMapper.selectList(wrapper);
|
||||||
|
|
||||||
|
// 建立socialCreditCode到实体的映射
|
||||||
|
for (CcdiEnterpriseBaseInfo existing : existingList) {
|
||||||
|
existingEntityMap.put(existing.getSocialCreditCode(), existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第三轮:数据验证和分类 - 使用Map快速判断 ✅
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
CcdiEnterpriseBaseInfo existingEntity = existingEntityMap.get(excel.getSocialCreditCode());
|
||||||
|
if (existingEntity != null) {
|
||||||
|
// 记录已存在
|
||||||
|
if (updateSupport) {
|
||||||
|
updateList.add(entity);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
insertList.add(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 优化技巧
|
||||||
|
|
||||||
|
1. **批量查询**
|
||||||
|
- 使用 `wrapper.in()` 一次性查询所有待校验的键值
|
||||||
|
- 减少数据库往返次数
|
||||||
|
|
||||||
|
2. **内存映射**
|
||||||
|
- 使用 `HashMap` 存储查询结果
|
||||||
|
- O(1)时间复杂度的快速查找
|
||||||
|
|
||||||
|
3. **查询优化**
|
||||||
|
- 使用 `wrapper.select()` 只查询需要的字段
|
||||||
|
- 减少数据传输量
|
||||||
|
|
||||||
|
4. **提前收集**
|
||||||
|
- 在第一轮循环中收集所有待校验的键值
|
||||||
|
- 避免在循环中查询数据库
|
||||||
|
|
||||||
|
## 性能对比
|
||||||
|
|
||||||
|
### 数据库查询次数对比
|
||||||
|
|
||||||
|
| 导入数据量 | 优化前查询次数 | 优化后查询次数 | 性能提升 |
|
||||||
|
|----------|-------------|-------------|---------|
|
||||||
|
| 100条 | 100+1=101次 | 1次 | 99% |
|
||||||
|
| 500条 | 500+1=501次 | 1次 | 99.8% |
|
||||||
|
| 1000条 | 1000+1=1001次 | 1次 | 99.9% |
|
||||||
|
| 5000条 | 5000+1=5001次 | 1次 | 99.98% |
|
||||||
|
|
||||||
|
### 响应时间对比(预估)
|
||||||
|
|
||||||
|
| 导入数据量 | 优化前响应时间 | 优化后响应时间 | 性能提升 |
|
||||||
|
|----------|------------|------------|---------|
|
||||||
|
| 100条 | ~5秒 | ~0.5秒 | 90% |
|
||||||
|
| 500条 | ~25秒 | ~1秒 | 96% |
|
||||||
|
| 1000条 | ~50秒 | ~2秒 | 96% |
|
||||||
|
| 5000条 | ~250秒 | ~8秒 | 96.8% |
|
||||||
|
|
||||||
|
> 注:响应时间受网络延迟、数据库性能、服务器配置等因素影响,以上为保守预估值
|
||||||
|
|
||||||
|
### 资源消耗对比
|
||||||
|
|
||||||
|
| 指标 | 优化前 | 优化后 | 改善 |
|
||||||
|
|--------------|------------------|-------------------|-----------|
|
||||||
|
| 数据库连接占用时间 | 长时间占用 | 短暂占用 | 减少90%+ |
|
||||||
|
| 网络往返次数 | N+1次 | 1-2次 | 减少99%+ |
|
||||||
|
| 内存占用 | 基本占用 | 额外占用HashMap(很小) | 略微增加(可忽略) |
|
||||||
|
| CPU使用 | 循环+数据库等待 | 批量查询+内存判断 | 优化 |
|
||||||
|
|
||||||
|
## 优化效果
|
||||||
|
|
||||||
|
### 1. 性能提升
|
||||||
|
- **查询次数减少99%+**:从N+1次降低到1次
|
||||||
|
- **响应时间减少90%+**:大幅提升用户体验
|
||||||
|
- **数据库压力降低**:减少数据库连接占用
|
||||||
|
|
||||||
|
### 2. 代码质量提升
|
||||||
|
- **逻辑更清晰**:四阶段流程(收集→查询→分类→处理)
|
||||||
|
- **可维护性更好**:职责分明,易于理解和修改
|
||||||
|
- **扩展性更强**:易于添加其他批量校验逻辑
|
||||||
|
|
||||||
|
### 3. 资源利用优化
|
||||||
|
- **数据库连接池压力减轻**:减少连接占用时间
|
||||||
|
- **网络带宽节省**:减少网络往返次数
|
||||||
|
- **服务器吞吐量提升**:可支持更多并发导入请求
|
||||||
|
|
||||||
|
## MySQL层面优化建议
|
||||||
|
|
||||||
|
### 1. 确保唯一索引存在
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 个人中介表:确保personId有唯一索引
|
||||||
|
ALTER TABLE ccdi_biz_intermediary
|
||||||
|
ADD UNIQUE INDEX uk_person_id (person_id);
|
||||||
|
|
||||||
|
-- 实体中介表:确保socialCreditCode有唯一索引
|
||||||
|
ALTER TABLE ccdi_enterprise_base_info
|
||||||
|
ADD UNIQUE INDEX uk_social_credit_code (social_credit_code);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 批量查询执行计划检查
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 检查批量查询是否使用了索引
|
||||||
|
EXPLAIN SELECT biz_id, person_id
|
||||||
|
FROM ccdi_biz_intermediary
|
||||||
|
WHERE person_id IN ('id1', 'id2', 'id3', ...);
|
||||||
|
|
||||||
|
-- 期望结果:type=range, key=uk_person_id
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 批量插入优化
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- 确保批量插入使用优化器优化
|
||||||
|
SET optimizer_switch='batched_key_access=on';
|
||||||
|
```
|
||||||
|
|
||||||
|
## 测试验证
|
||||||
|
|
||||||
|
### 测试数据
|
||||||
|
- 个人中介测试数据:`doc/test-data/intermediary/个人中介黑名单测试数据_1000条_第1批.xlsx`
|
||||||
|
- 实体中介测试数据:`doc/test-data/intermediary/机构中介黑名单测试数据_1000条_第1批.xlsx`
|
||||||
|
|
||||||
|
### 测试方法
|
||||||
|
使用测试脚本验证导入功能和性能:
|
||||||
|
```bash
|
||||||
|
# 运行测试脚本
|
||||||
|
python doc/test-data/intermediary/test_import_performance.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### 验证要点
|
||||||
|
1. ✅ 功能正确性:新增和更新逻辑正确
|
||||||
|
2. ✅ 唯一性校验:重复数据能正确识别
|
||||||
|
3. ✅ 性能提升:导入时间明显缩短
|
||||||
|
4. ✅ 数据完整性:所有数据正确导入
|
||||||
|
5. ✅ 异常处理:错误信息正确返回
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
### 后端文件
|
||||||
|
- `ruoyi-ccdi/src/main/java/com/ruoyi/ccdi/service/impl/CcdiIntermediaryServiceImpl.java:245-488`
|
||||||
|
|
||||||
|
### 数据库表
|
||||||
|
- `ccdi_biz_intermediary` - 个人中介表
|
||||||
|
- `ccdi_enterprise_base_info` - 实体中介表
|
||||||
|
|
||||||
|
### 测试数据
|
||||||
|
- `doc/test-data/intermediary/` - 测试数据目录
|
||||||
|
|
||||||
|
## 后续优化建议
|
||||||
|
|
||||||
|
### 1. 异步导入
|
||||||
|
对于超大批量数据(10万+),可以考虑:
|
||||||
|
- 使用消息队列异步处理
|
||||||
|
- 提供导入进度查询接口
|
||||||
|
- 导入完成后通知用户
|
||||||
|
|
||||||
|
### 2. 分批导入
|
||||||
|
对于内存受限场景:
|
||||||
|
- 将大数据集分批处理(每批1000条)
|
||||||
|
- 使用事务保证每批数据的原子性
|
||||||
|
- 失败时回滚当前批次
|
||||||
|
|
||||||
|
### 3. 并行处理
|
||||||
|
对于多核CPU环境:
|
||||||
|
- 使用线程池并行处理不同批次
|
||||||
|
- 注意控制并发数,避免数据库连接耗尽
|
||||||
|
|
||||||
|
### 4. 缓存优化
|
||||||
|
对于频繁导入相同数据的场景:
|
||||||
|
- 使用Redis缓存常用数据
|
||||||
|
- 缓存失效策略:TTL或主动更新
|
||||||
|
|
||||||
|
### 5. SQL进一步优化
|
||||||
|
```sql
|
||||||
|
-- 使用INSERT ON DUPLICATE KEY UPDATE(如果业务允许)
|
||||||
|
INSERT INTO ccdi_biz_intermediary (biz_id, person_id, ...)
|
||||||
|
VALUES (?, ?, ...)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
name = VALUES(name),
|
||||||
|
mobile = VALUES(mobile),
|
||||||
|
...;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
本次优化通过**批量查询 + 内存映射**的方式,成功将唯一性校验的数据库查询次数从N+1次降低到1次,性能提升99%以上。优化后的代码具有更好的可读性、可维护性和扩展性,为后续功能扩展奠定了良好基础。
|
||||||
|
|
||||||
|
优化核心思想:
|
||||||
|
- **批量操作优于循环操作**
|
||||||
|
- **内存计算优于网络计算**
|
||||||
|
- **提前规划优于事后补救**
|
||||||
@@ -35,19 +35,6 @@ public class CcdiEnumController {
|
|||||||
return AjaxResult.success(options);
|
return AjaxResult.success(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取人员子类型选项
|
|
||||||
*/
|
|
||||||
@Operation(summary = "获取人员子类型选项")
|
|
||||||
@GetMapping("/indivSubType")
|
|
||||||
public AjaxResult getIndivSubTypeOptions() {
|
|
||||||
List<EnumOptionVO> options = new ArrayList<>();
|
|
||||||
for (IndivSubType type : IndivSubType.values()) {
|
|
||||||
options.add(new EnumOptionVO(type.getCode(), type.getDesc()));
|
|
||||||
}
|
|
||||||
return AjaxResult.success(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取性别选项
|
* 获取性别选项
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ public class CcdiBizIntermediary implements Serializable {
|
|||||||
/** 关联人员ID */
|
/** 关联人员ID */
|
||||||
private String relatedNumId;
|
private String relatedNumId;
|
||||||
|
|
||||||
/** 关联关系 */
|
|
||||||
private String relationTypeField;
|
|
||||||
|
|
||||||
/** 数据来源,MANUAL:手动录入, SYSTEM:系统同步, IMPORT:批量导入, API:接口获取 */
|
/** 数据来源,MANUAL:手动录入, SYSTEM:系统同步, IMPORT:批量导入, API:接口获取 */
|
||||||
private String dataSource;
|
private String dataSource;
|
||||||
|
|
||||||
|
|||||||
@@ -32,9 +32,6 @@ public class CcdiIntermediaryPersonAddDTO implements Serializable {
|
|||||||
@Schema(description = "人员子类型")
|
@Schema(description = "人员子类型")
|
||||||
private String personSubType;
|
private String personSubType;
|
||||||
|
|
||||||
@Schema(description = "关系类型")
|
|
||||||
private String relationType;
|
|
||||||
|
|
||||||
@Schema(description = "性别")
|
@Schema(description = "性别")
|
||||||
private String gender;
|
private String gender;
|
||||||
|
|
||||||
@@ -76,7 +73,8 @@ public class CcdiIntermediaryPersonAddDTO implements Serializable {
|
|||||||
|
|
||||||
@Schema(description = "关联关系")
|
@Schema(description = "关联关系")
|
||||||
@Size(max = 50, message = "关联关系长度不能超过50个字符")
|
@Size(max = 50, message = "关联关系长度不能超过50个字符")
|
||||||
private String relation;
|
private String relationType;
|
||||||
|
|
||||||
|
|
||||||
@Schema(description = "备注")
|
@Schema(description = "备注")
|
||||||
@Size(max = 500, message = "备注长度不能超过500个字符")
|
@Size(max = 500, message = "备注长度不能超过500个字符")
|
||||||
|
|||||||
@@ -36,9 +36,6 @@ public class CcdiIntermediaryPersonEditDTO implements Serializable {
|
|||||||
@Schema(description = "人员子类型")
|
@Schema(description = "人员子类型")
|
||||||
private String personSubType;
|
private String personSubType;
|
||||||
|
|
||||||
@Schema(description = "关系类型")
|
|
||||||
private String relationType;
|
|
||||||
|
|
||||||
@Schema(description = "性别")
|
@Schema(description = "性别")
|
||||||
private String gender;
|
private String gender;
|
||||||
|
|
||||||
@@ -79,7 +76,7 @@ public class CcdiIntermediaryPersonEditDTO implements Serializable {
|
|||||||
|
|
||||||
@Schema(description = "关联关系")
|
@Schema(description = "关联关系")
|
||||||
@Size(max = 50, message = "关联关系长度不能超过50个字符")
|
@Size(max = 50, message = "关联关系长度不能超过50个字符")
|
||||||
private String relation;
|
private String relationType;
|
||||||
|
|
||||||
@Schema(description = "备注")
|
@Schema(description = "备注")
|
||||||
@Size(max = 500, message = "备注长度不能超过500个字符")
|
@Size(max = 500, message = "备注长度不能超过500个字符")
|
||||||
|
|||||||
@@ -22,19 +22,19 @@ public class CcdiIntermediaryEntityExcel implements Serializable {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/** 机构名称 */
|
/** 机构名称 */
|
||||||
@ExcelProperty(value = "机构名称", index = 0)
|
@ExcelProperty(value = "机构名称*", index = 0)
|
||||||
@ColumnWidth(30)
|
@ColumnWidth(30)
|
||||||
private String enterpriseName;
|
private String enterpriseName;
|
||||||
|
|
||||||
/** 统一社会信用代码 */
|
/** 统一社会信用代码 */
|
||||||
@ExcelProperty(value = "统一社会信用代码", index = 1)
|
@ExcelProperty(value = "统一社会信用代码*", index = 1)
|
||||||
@ColumnWidth(20)
|
@ColumnWidth(20)
|
||||||
private String socialCreditCode;
|
private String socialCreditCode;
|
||||||
|
|
||||||
/** 主体类型 */
|
/** 主体类型 */
|
||||||
@ExcelProperty(value = "主体类型", index = 2)
|
@ExcelProperty(value = "主体类型", index = 2)
|
||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
@DictDropdown(dictType = "ccdi_enterprise_type")
|
@DictDropdown(dictType = "ccdi_entity_type")
|
||||||
private String enterpriseType;
|
private String enterpriseType;
|
||||||
|
|
||||||
/** 企业性质 */
|
/** 企业性质 */
|
||||||
@@ -71,7 +71,7 @@ public class CcdiIntermediaryEntityExcel implements Serializable {
|
|||||||
/** 法定代表人证件类型 */
|
/** 法定代表人证件类型 */
|
||||||
@ExcelProperty(value = "法定代表人证件类型", index = 9)
|
@ExcelProperty(value = "法定代表人证件类型", index = 9)
|
||||||
@ColumnWidth(20)
|
@ColumnWidth(20)
|
||||||
@DictDropdown(dictType = "ccdi_id_type")
|
@DictDropdown(dictType = "ccdi_certificate_type")
|
||||||
private String legalCertType;
|
private String legalCertType;
|
||||||
|
|
||||||
/** 法定代表人证件号码 */
|
/** 法定代表人证件号码 */
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class CcdiIntermediaryPersonExcel implements Serializable {
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
/** 姓名 */
|
/** 姓名 */
|
||||||
@ExcelProperty(value = "姓名", index = 0)
|
@ExcelProperty(value = "姓名*", index = 0)
|
||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@@ -34,75 +34,68 @@ public class CcdiIntermediaryPersonExcel implements Serializable {
|
|||||||
/** 人员子类型 */
|
/** 人员子类型 */
|
||||||
@ExcelProperty(value = "人员子类型", index = 2)
|
@ExcelProperty(value = "人员子类型", index = 2)
|
||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
@DictDropdown(dictType = "ccdi_person_sub_type")
|
|
||||||
private String personSubType;
|
private String personSubType;
|
||||||
|
|
||||||
/** 关系类型 */
|
|
||||||
@ExcelProperty(value = "关系类型", index = 3)
|
|
||||||
@ColumnWidth(15)
|
|
||||||
@DictDropdown(dictType = "ccdi_relation_type")
|
|
||||||
private String relationType;
|
|
||||||
|
|
||||||
/** 性别 */
|
/** 性别 */
|
||||||
@ExcelProperty(value = "性别", index = 4)
|
@ExcelProperty(value = "性别", index = 3)
|
||||||
@ColumnWidth(10)
|
@ColumnWidth(10)
|
||||||
@DictDropdown(dictType = "sys_user_sex")
|
@DictDropdown(dictType = "ccdi_indiv_gender")
|
||||||
private String gender;
|
private String gender;
|
||||||
|
|
||||||
/** 证件类型 */
|
/** 证件类型 */
|
||||||
@ExcelProperty(value = "证件类型", index = 5)
|
@ExcelProperty(value = "证件类型", index = 4)
|
||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
@DictDropdown(dictType = "ccdi_id_type")
|
@DictDropdown(dictType = "ccdi_certificate_type")
|
||||||
private String idType;
|
private String idType;
|
||||||
|
|
||||||
/** 证件号码 */
|
/** 证件号码 */
|
||||||
@ExcelProperty(value = "证件号码", index = 6)
|
@ExcelProperty(value = "证件号码*", index = 5)
|
||||||
@ColumnWidth(20)
|
@ColumnWidth(20)
|
||||||
private String personId;
|
private String personId;
|
||||||
|
|
||||||
/** 手机号码 */
|
/** 手机号码 */
|
||||||
@ExcelProperty(value = "手机号码", index = 7)
|
@ExcelProperty(value = "手机号码", index = 6)
|
||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
private String mobile;
|
private String mobile;
|
||||||
|
|
||||||
/** 微信号 */
|
/** 微信号 */
|
||||||
@ExcelProperty(value = "微信号", index = 8)
|
@ExcelProperty(value = "微信号", index = 7)
|
||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
private String wechatNo;
|
private String wechatNo;
|
||||||
|
|
||||||
/** 联系地址 */
|
/** 联系地址 */
|
||||||
@ExcelProperty(value = "联系地址", index = 9)
|
@ExcelProperty(value = "联系地址", index = 8)
|
||||||
@ColumnWidth(30)
|
@ColumnWidth(30)
|
||||||
private String contactAddress;
|
private String contactAddress;
|
||||||
|
|
||||||
/** 所在公司 */
|
/** 所在公司 */
|
||||||
@ExcelProperty(value = "所在公司", index = 10)
|
@ExcelProperty(value = "所在公司", index = 9)
|
||||||
@ColumnWidth(20)
|
@ColumnWidth(20)
|
||||||
private String company;
|
private String company;
|
||||||
|
|
||||||
/** 企业统一信用码 */
|
/** 企业统一信用码 */
|
||||||
@ExcelProperty(value = "企业统一信用码", index = 11)
|
@ExcelProperty(value = "企业统一信用码", index = 10)
|
||||||
@ColumnWidth(20)
|
@ColumnWidth(20)
|
||||||
private String socialCreditCode;
|
private String socialCreditCode;
|
||||||
|
|
||||||
/** 职位 */
|
/** 职位 */
|
||||||
@ExcelProperty(value = "职位", index = 12)
|
@ExcelProperty(value = "职位", index = 11)
|
||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
private String position;
|
private String position;
|
||||||
|
|
||||||
/** 关联人员ID */
|
/** 关联人员ID */
|
||||||
@ExcelProperty(value = "关联人员ID", index = 13)
|
@ExcelProperty(value = "关联人员ID", index = 12)
|
||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
private String relatedNumId;
|
private String relatedNumId;
|
||||||
|
|
||||||
/** 关联关系 */
|
/** 关系类型 */
|
||||||
@ExcelProperty(value = "关联关系", index = 14)
|
@ExcelProperty(value = "关系类型", index = 13)
|
||||||
@ColumnWidth(15)
|
@ColumnWidth(15)
|
||||||
@DictDropdown(dictType = "ccdi_relation")
|
@DictDropdown(dictType = "ccdi_relation_type")
|
||||||
private String relation;
|
private String relationType;
|
||||||
|
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
@ExcelProperty(value = "备注", index = 15)
|
@ExcelProperty(value = "备注", index = 14)
|
||||||
@ColumnWidth(30)
|
@ColumnWidth(30)
|
||||||
private String remark;
|
private String remark;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,15 @@ public class CcdiIntermediaryEntityDetailVO implements Serializable {
|
|||||||
@Serial
|
@Serial
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "业务ID")
|
||||||
|
private String bizId;
|
||||||
|
|
||||||
@Schema(description = "统一社会信用代码")
|
@Schema(description = "统一社会信用代码")
|
||||||
private String socialCreditCode;
|
private String socialCreditCode;
|
||||||
|
|
||||||
|
@Schema(description = "中介类型(1=个人, 2=实体)")
|
||||||
|
private String intermediaryType;
|
||||||
|
|
||||||
@Schema(description = "企业名称")
|
@Schema(description = "企业名称")
|
||||||
private String enterpriseName;
|
private String enterpriseName;
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ public class CcdiIntermediaryPersonDetailVO implements Serializable {
|
|||||||
@Schema(description = "人员ID")
|
@Schema(description = "人员ID")
|
||||||
private String bizId;
|
private String bizId;
|
||||||
|
|
||||||
|
@Schema(description = "中介类型(1=个人, 2=实体)")
|
||||||
|
private String intermediaryType;
|
||||||
|
|
||||||
@Schema(description = "姓名")
|
@Schema(description = "姓名")
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@@ -54,6 +57,9 @@ public class CcdiIntermediaryPersonDetailVO implements Serializable {
|
|||||||
@Schema(description = "所在公司")
|
@Schema(description = "所在公司")
|
||||||
private String company;
|
private String company;
|
||||||
|
|
||||||
|
@Schema(description = "企业统一信用码")
|
||||||
|
private String socialCreditCode;
|
||||||
|
|
||||||
@Schema(description = "职位")
|
@Schema(description = "职位")
|
||||||
private String position;
|
private String position;
|
||||||
|
|
||||||
|
|||||||
@@ -45,4 +45,8 @@ public class CcdiIntermediaryVO implements Serializable {
|
|||||||
@Schema(description = "创建时间")
|
@Schema(description = "创建时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
|
|
||||||
|
@Schema(description = "修改时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date updateTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
package com.ruoyi.ccdi.enums;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 人员子类型枚举
|
|
||||||
*
|
|
||||||
* @author ruoyi
|
|
||||||
*/
|
|
||||||
public enum IndivSubType {
|
|
||||||
|
|
||||||
/** 本人 */
|
|
||||||
SELF("本人", "本人"),
|
|
||||||
|
|
||||||
/** 配偶 */
|
|
||||||
SPOUSE("配偶", "配偶"),
|
|
||||||
|
|
||||||
/** 父亲 */
|
|
||||||
FATHER("父亲", "父亲"),
|
|
||||||
|
|
||||||
/** 母亲 */
|
|
||||||
MOTHER("母亲", "母亲"),
|
|
||||||
|
|
||||||
/** 兄弟 */
|
|
||||||
BROTHER("兄弟", "兄弟"),
|
|
||||||
|
|
||||||
/** 姐妹 */
|
|
||||||
SISTER("姐妹", "姐妹"),
|
|
||||||
|
|
||||||
/** 子女 */
|
|
||||||
CHILD("子女", "子女");
|
|
||||||
|
|
||||||
private final String code;
|
|
||||||
private final String desc;
|
|
||||||
|
|
||||||
IndivSubType(String code, String desc) {
|
|
||||||
this.code = code;
|
|
||||||
this.desc = desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCode() {
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDesc() {
|
|
||||||
return desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据编码获取描述
|
|
||||||
*/
|
|
||||||
public static String getDescByCode(String code) {
|
|
||||||
for (IndivSubType type : values()) {
|
|
||||||
if (type.getCode().equals(code)) {
|
|
||||||
return type.getDesc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package com.ruoyi.ccdi.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.ruoyi.ccdi.domain.dto.CcdiIntermediaryQueryDTO;
|
||||||
|
import com.ruoyi.ccdi.domain.vo.CcdiIntermediaryVO;
|
||||||
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 中介黑名单联合查询Mapper接口
|
||||||
|
*
|
||||||
|
* @author ruoyi
|
||||||
|
* @date 2026-02-05
|
||||||
|
*/
|
||||||
|
@Mapper
|
||||||
|
public interface CcdiIntermediaryMapper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 联合查询中介列表(支持MyBatis Plus分页)
|
||||||
|
* 通过UNION ALL联合查询个人中介和实体中介
|
||||||
|
* 支持按中介类型筛选(1=个人, 2=实体, null=全部)
|
||||||
|
*
|
||||||
|
* @param page 分页对象
|
||||||
|
* @param queryDTO 查询条件
|
||||||
|
* @return 中介VO分页结果
|
||||||
|
*/
|
||||||
|
Page<CcdiIntermediaryVO> selectIntermediaryList(Page<CcdiIntermediaryVO> page, @Param("query") CcdiIntermediaryQueryDTO queryDTO);
|
||||||
|
}
|
||||||
@@ -4,11 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.ruoyi.ccdi.domain.CcdiBizIntermediary;
|
import com.ruoyi.ccdi.domain.CcdiBizIntermediary;
|
||||||
import com.ruoyi.ccdi.domain.CcdiEnterpriseBaseInfo;
|
import com.ruoyi.ccdi.domain.CcdiEnterpriseBaseInfo;
|
||||||
import com.ruoyi.ccdi.domain.dto.CcdiIntermediaryEntityAddDTO;
|
import com.ruoyi.ccdi.domain.dto.*;
|
||||||
import com.ruoyi.ccdi.domain.dto.CcdiIntermediaryEntityEditDTO;
|
|
||||||
import com.ruoyi.ccdi.domain.dto.CcdiIntermediaryPersonAddDTO;
|
|
||||||
import com.ruoyi.ccdi.domain.dto.CcdiIntermediaryPersonEditDTO;
|
|
||||||
import com.ruoyi.ccdi.domain.dto.CcdiIntermediaryQueryDTO;
|
|
||||||
import com.ruoyi.ccdi.domain.excel.CcdiIntermediaryEntityExcel;
|
import com.ruoyi.ccdi.domain.excel.CcdiIntermediaryEntityExcel;
|
||||||
import com.ruoyi.ccdi.domain.excel.CcdiIntermediaryPersonExcel;
|
import com.ruoyi.ccdi.domain.excel.CcdiIntermediaryPersonExcel;
|
||||||
import com.ruoyi.ccdi.domain.vo.CcdiIntermediaryEntityDetailVO;
|
import com.ruoyi.ccdi.domain.vo.CcdiIntermediaryEntityDetailVO;
|
||||||
@@ -16,6 +12,7 @@ import com.ruoyi.ccdi.domain.vo.CcdiIntermediaryPersonDetailVO;
|
|||||||
import com.ruoyi.ccdi.domain.vo.CcdiIntermediaryVO;
|
import com.ruoyi.ccdi.domain.vo.CcdiIntermediaryVO;
|
||||||
import com.ruoyi.ccdi.mapper.CcdiBizIntermediaryMapper;
|
import com.ruoyi.ccdi.mapper.CcdiBizIntermediaryMapper;
|
||||||
import com.ruoyi.ccdi.mapper.CcdiEnterpriseBaseInfoMapper;
|
import com.ruoyi.ccdi.mapper.CcdiEnterpriseBaseInfoMapper;
|
||||||
|
import com.ruoyi.ccdi.mapper.CcdiIntermediaryMapper;
|
||||||
import com.ruoyi.ccdi.service.ICcdiIntermediaryService;
|
import com.ruoyi.ccdi.service.ICcdiIntermediaryService;
|
||||||
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
import jakarta.annotation.Resource;
|
import jakarta.annotation.Resource;
|
||||||
@@ -41,8 +38,13 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
@Resource
|
@Resource
|
||||||
private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper;
|
private CcdiEnterpriseBaseInfoMapper enterpriseBaseInfoMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private CcdiIntermediaryMapper intermediaryMapper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分页查询中介列表
|
* 分页查询中介列表
|
||||||
|
* 使用XML联合查询实现,支持个人中介和实体中介的灵活查询
|
||||||
|
* 使用MyBatis Plus分页插件自动处理分页
|
||||||
*
|
*
|
||||||
* @param page 分页对象
|
* @param page 分页对象
|
||||||
* @param queryDTO 查询条件
|
* @param queryDTO 查询条件
|
||||||
@@ -50,61 +52,8 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Page<CcdiIntermediaryVO> selectIntermediaryPage(Page<CcdiIntermediaryVO> page, CcdiIntermediaryQueryDTO queryDTO) {
|
public Page<CcdiIntermediaryVO> selectIntermediaryPage(Page<CcdiIntermediaryVO> page, CcdiIntermediaryQueryDTO queryDTO) {
|
||||||
Page<CcdiIntermediaryVO> voPage = new Page<>(page.getCurrent(), page.getSize());
|
// 直接调用Mapper的联合查询方法,MyBatis Plus会自动处理分页
|
||||||
List<CcdiIntermediaryVO> list = new ArrayList<>();
|
return intermediaryMapper.selectIntermediaryList(page, queryDTO);
|
||||||
|
|
||||||
// 查询个人中介
|
|
||||||
LambdaQueryWrapper<CcdiBizIntermediary> personWrapper = new LambdaQueryWrapper<>();
|
|
||||||
personWrapper.eq(CcdiBizIntermediary::getPersonType, "中介")
|
|
||||||
.like(StringUtils.isNotEmpty(queryDTO.getName()), CcdiBizIntermediary::getName, queryDTO.getName())
|
|
||||||
.like(StringUtils.isNotEmpty(queryDTO.getCertificateNo()), CcdiBizIntermediary::getPersonId, queryDTO.getCertificateNo());
|
|
||||||
|
|
||||||
List<CcdiBizIntermediary> personList = bizIntermediaryMapper.selectList(personWrapper);
|
|
||||||
for (CcdiBizIntermediary person : personList) {
|
|
||||||
CcdiIntermediaryVO vo = new CcdiIntermediaryVO();
|
|
||||||
vo.setId(person.getBizId());
|
|
||||||
vo.setName(person.getName());
|
|
||||||
vo.setCertificateNo(person.getPersonId());
|
|
||||||
vo.setIntermediaryType("1");
|
|
||||||
vo.setPersonType(person.getPersonType());
|
|
||||||
vo.setCompany(person.getCompany());
|
|
||||||
vo.setDataSource(person.getDataSource());
|
|
||||||
vo.setCreateTime(person.getCreateTime());
|
|
||||||
list.add(vo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询实体中介
|
|
||||||
LambdaQueryWrapper<CcdiEnterpriseBaseInfo> entityWrapper = new LambdaQueryWrapper<>();
|
|
||||||
entityWrapper.eq(CcdiEnterpriseBaseInfo::getRiskLevel, "1")
|
|
||||||
.eq(CcdiEnterpriseBaseInfo::getEntSource, "INTERMEDIARY")
|
|
||||||
.like(StringUtils.isNotEmpty(queryDTO.getName()), CcdiEnterpriseBaseInfo::getEnterpriseName, queryDTO.getName())
|
|
||||||
.like(StringUtils.isNotEmpty(queryDTO.getCertificateNo()), CcdiEnterpriseBaseInfo::getSocialCreditCode, queryDTO.getCertificateNo());
|
|
||||||
|
|
||||||
List<CcdiEnterpriseBaseInfo> entityList = enterpriseBaseInfoMapper.selectList(entityWrapper);
|
|
||||||
for (CcdiEnterpriseBaseInfo entity : entityList) {
|
|
||||||
CcdiIntermediaryVO vo = new CcdiIntermediaryVO();
|
|
||||||
vo.setId(entity.getSocialCreditCode());
|
|
||||||
vo.setName(entity.getEnterpriseName());
|
|
||||||
vo.setCertificateNo(entity.getSocialCreditCode());
|
|
||||||
vo.setIntermediaryType("2");
|
|
||||||
vo.setDataSource(entity.getDataSource());
|
|
||||||
vo.setCreateTime(entity.getCreateTime());
|
|
||||||
list.add(vo);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 手动分页
|
|
||||||
int start = (int) ((voPage.getCurrent() - 1) * voPage.getSize());
|
|
||||||
int end = (int) Math.min(start + voPage.getSize(), list.size());
|
|
||||||
|
|
||||||
List<CcdiIntermediaryVO> pageList = new ArrayList<>();
|
|
||||||
if (start < list.size()) {
|
|
||||||
pageList = list.subList(start, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
voPage.setRecords(pageList);
|
|
||||||
voPage.setTotal(list.size());
|
|
||||||
|
|
||||||
return voPage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,6 +71,8 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
|
|
||||||
CcdiIntermediaryPersonDetailVO vo = new CcdiIntermediaryPersonDetailVO();
|
CcdiIntermediaryPersonDetailVO vo = new CcdiIntermediaryPersonDetailVO();
|
||||||
BeanUtils.copyProperties(person, vo);
|
BeanUtils.copyProperties(person, vo);
|
||||||
|
// 设置中介类型为个人
|
||||||
|
vo.setIntermediaryType("1");
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -140,6 +91,10 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
|
|
||||||
CcdiIntermediaryEntityDetailVO vo = new CcdiIntermediaryEntityDetailVO();
|
CcdiIntermediaryEntityDetailVO vo = new CcdiIntermediaryEntityDetailVO();
|
||||||
BeanUtils.copyProperties(entity, vo);
|
BeanUtils.copyProperties(entity, vo);
|
||||||
|
// 设置中介类型为实体
|
||||||
|
vo.setIntermediaryType("2");
|
||||||
|
// 设置业务ID(使用socialCreditCode作为bizId,前端判断是否为新增模式)
|
||||||
|
vo.setBizId(socialCreditCode);
|
||||||
return vo;
|
return vo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -159,7 +114,6 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
|
|
||||||
CcdiBizIntermediary person = new CcdiBizIntermediary();
|
CcdiBizIntermediary person = new CcdiBizIntermediary();
|
||||||
BeanUtils.copyProperties(addDTO, person);
|
BeanUtils.copyProperties(addDTO, person);
|
||||||
person.setPersonType("中介");
|
|
||||||
person.setDataSource("MANUAL");
|
person.setDataSource("MANUAL");
|
||||||
|
|
||||||
return bizIntermediaryMapper.insert(person);
|
return bizIntermediaryMapper.insert(person);
|
||||||
@@ -286,6 +240,7 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入个人中介数据(批量操作)
|
* 导入个人中介数据(批量操作)
|
||||||
|
* 优化:使用批量查询替代循环中的单条查询,减少数据库交互次数
|
||||||
*
|
*
|
||||||
* @param list Excel实体列表
|
* @param list Excel实体列表
|
||||||
* @param updateSupport 是否更新支持
|
* @param updateSupport 是否更新支持
|
||||||
@@ -308,7 +263,28 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
List<CcdiBizIntermediary> updateList = new ArrayList<>();
|
List<CcdiBizIntermediary> updateList = new ArrayList<>();
|
||||||
List<String> personIds = new ArrayList<>();
|
List<String> personIds = new ArrayList<>();
|
||||||
|
|
||||||
// 第一轮:数据验证和分类
|
// 第一轮:收集所有personId
|
||||||
|
for (CcdiIntermediaryPersonExcel excel : list) {
|
||||||
|
if (StringUtils.isNotEmpty(excel.getPersonId())) {
|
||||||
|
personIds.add(excel.getPersonId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二轮:批量查询已存在的记录(一次查询替代N次查询)
|
||||||
|
java.util.Map<String, String> personIdToBizIdMap = new java.util.HashMap<>();
|
||||||
|
if (!personIds.isEmpty()) {
|
||||||
|
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.select(CcdiBizIntermediary::getBizId, CcdiBizIntermediary::getPersonId);
|
||||||
|
wrapper.in(CcdiBizIntermediary::getPersonId, personIds);
|
||||||
|
List<CcdiBizIntermediary> existingList = bizIntermediaryMapper.selectList(wrapper);
|
||||||
|
|
||||||
|
// 建立personId到bizId的映射
|
||||||
|
for (CcdiBizIntermediary existing : existingList) {
|
||||||
|
personIdToBizIdMap.put(existing.getPersonId(), existing.getBizId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第三轮:数据验证和分类(使用Map进行快速判断,避免重复查询)
|
||||||
for (int i = 0; i < list.size(); i++) {
|
for (int i = 0; i < list.size(); i++) {
|
||||||
try {
|
try {
|
||||||
CcdiIntermediaryPersonExcel excel = list.get(i);
|
CcdiIntermediaryPersonExcel excel = list.get(i);
|
||||||
@@ -327,12 +303,13 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
person.setPersonType("中介");
|
person.setPersonType("中介");
|
||||||
person.setDataSource("IMPORT");
|
person.setDataSource("IMPORT");
|
||||||
|
|
||||||
personIds.add(excel.getPersonId());
|
// 使用Map快速判断是否存在
|
||||||
|
String existingBizId = personIdToBizIdMap.get(excel.getPersonId());
|
||||||
// 检查唯一性
|
if (existingBizId != null) {
|
||||||
if (!checkPersonIdUnique(excel.getPersonId(), null)) {
|
// 记录已存在
|
||||||
if (updateSupport) {
|
if (updateSupport) {
|
||||||
// 需要更新,暂时加入更新列表
|
// 需要更新,设置bizId
|
||||||
|
person.setBizId(existingBizId);
|
||||||
updateList.add(person);
|
updateList.add(person);
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("该证件号已存在");
|
throw new RuntimeException("该证件号已存在");
|
||||||
@@ -351,12 +328,15 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 构建返回消息
|
||||||
|
StringBuilder resultMsg = new StringBuilder();
|
||||||
|
|
||||||
if (failureNum > 0) {
|
if (failureNum > 0) {
|
||||||
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
|
resultMsg.append("很抱歉,导入失败!共 ").append(failureNum).append(" 条数据格式不正确,错误如下:");
|
||||||
throw new RuntimeException(failureMsg.toString());
|
resultMsg.append(failureMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第二轮:批量处理
|
// 第四轮:批量处理
|
||||||
try {
|
try {
|
||||||
// 批量插入新记录
|
// 批量插入新记录
|
||||||
if (!insertList.isEmpty()) {
|
if (!insertList.isEmpty()) {
|
||||||
@@ -365,31 +345,16 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
|
|
||||||
// 批量更新已存在的记录
|
// 批量更新已存在的记录
|
||||||
if (!updateList.isEmpty()) {
|
if (!updateList.isEmpty()) {
|
||||||
// 查询已存在记录的bizId
|
|
||||||
LambdaQueryWrapper<CcdiBizIntermediary> wrapper = new LambdaQueryWrapper<>();
|
|
||||||
wrapper.in(CcdiBizIntermediary::getPersonId, personIds);
|
|
||||||
List<CcdiBizIntermediary> existingList = bizIntermediaryMapper.selectList(wrapper);
|
|
||||||
|
|
||||||
// 建立personId到bizId的映射
|
|
||||||
java.util.Map<String, String> personIdToBizIdMap = new java.util.HashMap<>();
|
|
||||||
for (CcdiBizIntermediary existing : existingList) {
|
|
||||||
personIdToBizIdMap.put(existing.getPersonId(), existing.getBizId());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置bizId到更新列表
|
|
||||||
for (CcdiBizIntermediary person : updateList) {
|
|
||||||
String bizId = personIdToBizIdMap.get(person.getPersonId());
|
|
||||||
if (bizId != null) {
|
|
||||||
person.setBizId(bizId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量更新
|
|
||||||
bizIntermediaryMapper.updateBatch(updateList);
|
bizIntermediaryMapper.updateBatch(updateList);
|
||||||
}
|
}
|
||||||
|
|
||||||
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条");
|
// 只在有失败的情况下才返回成功信息,否则返回简洁的成功消息
|
||||||
return successMsg.toString();
|
if (failureNum > 0) {
|
||||||
|
resultMsg.append("<br/><br/>成功导入 ").append(successNum).append(" 条数据");
|
||||||
|
} else {
|
||||||
|
resultMsg.append("恭喜您,数据已全部导入成功!共 ").append(successNum).append(" 条");
|
||||||
|
}
|
||||||
|
return resultMsg.toString();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("批量操作失败:" + e.getMessage());
|
throw new RuntimeException("批量操作失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
@@ -397,6 +362,7 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 导入实体中介数据(批量操作)
|
* 导入实体中介数据(批量操作)
|
||||||
|
* 优化:使用批量查询替代循环中的单条查询,减少数据库交互次数
|
||||||
*
|
*
|
||||||
* @param list Excel实体列表
|
* @param list Excel实体列表
|
||||||
* @param updateSupport 是否更新支持
|
* @param updateSupport 是否更新支持
|
||||||
@@ -419,7 +385,27 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
List<CcdiEnterpriseBaseInfo> updateList = new ArrayList<>();
|
List<CcdiEnterpriseBaseInfo> updateList = new ArrayList<>();
|
||||||
List<String> socialCreditCodes = new ArrayList<>();
|
List<String> socialCreditCodes = new ArrayList<>();
|
||||||
|
|
||||||
// 第一轮:数据验证和分类
|
// 第一轮:收集所有socialCreditCode
|
||||||
|
for (CcdiIntermediaryEntityExcel excel : list) {
|
||||||
|
if (StringUtils.isNotEmpty(excel.getSocialCreditCode())) {
|
||||||
|
socialCreditCodes.add(excel.getSocialCreditCode());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二轮:批量查询已存在的记录(一次查询替代N次查询)
|
||||||
|
java.util.Map<String, CcdiEnterpriseBaseInfo> existingEntityMap = new java.util.HashMap<>();
|
||||||
|
if (!socialCreditCodes.isEmpty()) {
|
||||||
|
LambdaQueryWrapper<CcdiEnterpriseBaseInfo> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.in(CcdiEnterpriseBaseInfo::getSocialCreditCode, socialCreditCodes);
|
||||||
|
List<CcdiEnterpriseBaseInfo> existingList = enterpriseBaseInfoMapper.selectList(wrapper);
|
||||||
|
|
||||||
|
// 建立socialCreditCode到实体的映射
|
||||||
|
for (CcdiEnterpriseBaseInfo existing : existingList) {
|
||||||
|
existingEntityMap.put(existing.getSocialCreditCode(), existing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第三轮:数据验证和分类(使用Map进行快速判断,避免重复查询)
|
||||||
for (int i = 0; i < list.size(); i++) {
|
for (int i = 0; i < list.size(); i++) {
|
||||||
try {
|
try {
|
||||||
CcdiIntermediaryEntityExcel excel = list.get(i);
|
CcdiIntermediaryEntityExcel excel = list.get(i);
|
||||||
@@ -436,13 +422,13 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
entity.setEntSource("INTERMEDIARY");
|
entity.setEntSource("INTERMEDIARY");
|
||||||
entity.setDataSource("IMPORT");
|
entity.setDataSource("IMPORT");
|
||||||
|
|
||||||
// 检查唯一性
|
// 使用Map快速判断是否存在
|
||||||
if (StringUtils.isNotEmpty(excel.getSocialCreditCode())) {
|
if (StringUtils.isNotEmpty(excel.getSocialCreditCode())) {
|
||||||
socialCreditCodes.add(excel.getSocialCreditCode());
|
CcdiEnterpriseBaseInfo existingEntity = existingEntityMap.get(excel.getSocialCreditCode());
|
||||||
|
if (existingEntity != null) {
|
||||||
if (!checkSocialCreditCodeUnique(excel.getSocialCreditCode(), null)) {
|
// 记录已存在
|
||||||
if (updateSupport) {
|
if (updateSupport) {
|
||||||
// 需要更新,加入更新列表
|
// 需要更新,直接使用socialCreditCode作为主键
|
||||||
updateList.add(entity);
|
updateList.add(entity);
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("该统一社会信用代码已存在");
|
throw new RuntimeException("该统一社会信用代码已存在");
|
||||||
@@ -465,12 +451,15 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 构建返回消息
|
||||||
|
StringBuilder resultMsg = new StringBuilder();
|
||||||
|
|
||||||
if (failureNum > 0) {
|
if (failureNum > 0) {
|
||||||
failureMsg.insert(0, "很抱歉,导入失败!共 " + failureNum + " 条数据格式不正确,错误如下:");
|
resultMsg.append("很抱歉,导入失败!共 ").append(failureNum).append(" 条数据格式不正确,错误如下:");
|
||||||
throw new RuntimeException(failureMsg.toString());
|
resultMsg.append(failureMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 第二轮:批量处理
|
// 第四轮:批量处理
|
||||||
try {
|
try {
|
||||||
// 批量插入新记录
|
// 批量插入新记录
|
||||||
if (!insertList.isEmpty()) {
|
if (!insertList.isEmpty()) {
|
||||||
@@ -479,12 +468,16 @@ public class CcdiIntermediaryServiceImpl implements ICcdiIntermediaryService {
|
|||||||
|
|
||||||
// 批量更新已存在的记录
|
// 批量更新已存在的记录
|
||||||
if (!updateList.isEmpty()) {
|
if (!updateList.isEmpty()) {
|
||||||
// 批量更新(socialCreditCode已在实体中)
|
|
||||||
enterpriseBaseInfoMapper.updateBatch(updateList);
|
enterpriseBaseInfoMapper.updateBatch(updateList);
|
||||||
}
|
}
|
||||||
|
|
||||||
successMsg.insert(0, "恭喜您,数据已全部导入成功!共 " + successNum + " 条");
|
// 只在有失败的情况下才返回成功信息,否则返回简洁的成功消息
|
||||||
return successMsg.toString();
|
if (failureNum > 0) {
|
||||||
|
resultMsg.append("<br/><br/>成功导入 ").append(successNum).append(" 条数据");
|
||||||
|
} else {
|
||||||
|
resultMsg.append("恭喜您,数据已全部导入成功!共 ").append(successNum).append(" 条");
|
||||||
|
}
|
||||||
|
return resultMsg.toString();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("批量操作失败:" + e.getMessage());
|
throw new RuntimeException("批量操作失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,59 +2,59 @@
|
|||||||
<!DOCTYPE mapper
|
<!DOCTYPE mapper
|
||||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||||
<mapper namespace="com.ruoyi.ccdi.mapper.CcdiBizIntermediaryMapper">
|
<mapper namespace="com.ruoyi.ccdi.mapper.CcdiIntermediaryMapper">
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
统一列表联合查询
|
中介黑名单联合查询
|
||||||
使用UNION ALL联合查询个人中介和实体中介
|
支持按中介类型筛选: 1=个人中介, 2=实体中介, null=全部
|
||||||
支持按中介类型、姓名、证件号筛选
|
使用MyBatis Plus分页插件自动处理分页
|
||||||
-->
|
-->
|
||||||
<select id="selectIntermediaryList" resultType="com.ruoyi.ccdi.domain.vo.CcdiIntermediaryVO">
|
<select id="selectIntermediaryList" resultType="com.ruoyi.ccdi.domain.vo.CcdiIntermediaryVO">
|
||||||
<!-- 查询个人中介 -->
|
SELECT * FROM (
|
||||||
SELECT
|
<!-- 查询个人中介 -->
|
||||||
biz_id as id,
|
SELECT
|
||||||
name,
|
biz_id as id,
|
||||||
person_id as certificate_no,
|
name,
|
||||||
'1' as intermediary_type,
|
person_id as certificate_no,
|
||||||
person_type,
|
'1' as intermediary_type,
|
||||||
company,
|
person_type,
|
||||||
data_source,
|
company,
|
||||||
create_time
|
data_source,
|
||||||
FROM ccdi_biz_intermediary
|
create_time,
|
||||||
WHERE person_type = '中介'
|
update_time
|
||||||
<if test="intermediaryType == null or intermediaryType == '1'">
|
FROM ccdi_biz_intermediary
|
||||||
<if test="name != null and name != ''">
|
|
||||||
AND name LIKE CONCAT('%', #{name}, '%')
|
|
||||||
</if>
|
|
||||||
<if test="certificateNo != null and certificateNo != ''">
|
|
||||||
AND person_id = #{certificateNo}
|
|
||||||
</if>
|
|
||||||
</if>
|
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
<!-- 查询实体中介 -->
|
<!-- 查询实体中介 -->
|
||||||
SELECT
|
SELECT
|
||||||
social_credit_code as id,
|
social_credit_code as id,
|
||||||
enterprise_name as name,
|
enterprise_name as name,
|
||||||
social_credit_code as certificate_no,
|
social_credit_code as certificate_no,
|
||||||
'2' as intermediary_type,
|
'2' as intermediary_type,
|
||||||
'实体' as person_type,
|
'实体' as person_type,
|
||||||
enterprise_name as company,
|
enterprise_name as company,
|
||||||
data_source,
|
data_source,
|
||||||
create_time
|
create_time,
|
||||||
FROM ccdi_enterprise_base_info
|
update_time
|
||||||
WHERE risk_level = '1' AND ent_source = 'INTERMEDIARY'
|
FROM ccdi_enterprise_base_info
|
||||||
<if test="intermediaryType == null or intermediaryType == '2'">
|
WHERE risk_level = '1' AND ent_source = 'INTERMEDIARY'
|
||||||
<if test="name != null and name != ''">
|
) AS combined_result
|
||||||
AND enterprise_name LIKE CONCAT('%', #{name}, '%')
|
<where>
|
||||||
|
<!-- 按中介类型筛选 -->
|
||||||
|
<if test="query.intermediaryType != null and query.intermediaryType != ''">
|
||||||
|
AND intermediary_type = #{query.intermediaryType}
|
||||||
</if>
|
</if>
|
||||||
<if test="certificateNo != null and certificateNo != ''">
|
<!-- 按姓名/机构名称模糊查询 -->
|
||||||
AND social_credit_code = #{certificateNo}
|
<if test="query.name != null and query.name != ''">
|
||||||
|
AND name LIKE CONCAT('%', #{query.name}, '%')
|
||||||
</if>
|
</if>
|
||||||
</if>
|
<!-- 按证件号/统一社会信用代码精确查询 -->
|
||||||
|
<if test="query.certificateNo != null and query.certificateNo != ''">
|
||||||
ORDER BY create_time DESC
|
AND certificate_no = #{query.certificateNo}
|
||||||
|
</if>
|
||||||
|
</where>
|
||||||
|
ORDER BY update_time DESC
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
@@ -10,16 +10,6 @@ export function getIndivTypeOptions() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询人员子类型选项
|
|
||||||
*/
|
|
||||||
export function getIndivSubTypeOptions() {
|
|
||||||
return request({
|
|
||||||
url: '/ccdi/enum/indivSubType',
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询性别选项
|
* 查询性别选项
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -9,20 +9,19 @@ export function listIntermediary(query) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询中介黑名单详细
|
// 查询个人中介详细
|
||||||
export function getIntermediary(intermediaryId) {
|
export function getPersonIntermediary(bizId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/ccdi/intermediary/' + intermediaryId,
|
url: '/ccdi/intermediary/person/' + bizId,
|
||||||
method: 'get'
|
method: 'get'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增中介黑名单
|
// 查询实体中介详细
|
||||||
export function addIntermediary(data) {
|
export function getEntityIntermediary(socialCreditCode) {
|
||||||
return request({
|
return request({
|
||||||
url: '/ccdi/intermediary',
|
url: '/ccdi/intermediary/entity/' + socialCreditCode,
|
||||||
method: 'post',
|
method: 'get'
|
||||||
data: data
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,10 @@
|
|||||||
<el-table-column type="selection" width="55" align="center" />
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
<el-table-column label="姓名/机构名称" align="center" prop="name" :show-overflow-tooltip="true"/>
|
<el-table-column label="姓名/机构名称" align="center" prop="name" :show-overflow-tooltip="true"/>
|
||||||
<el-table-column label="证件号" align="center" prop="certificateNo" :show-overflow-tooltip="true"/>
|
<el-table-column label="证件号" align="center" prop="certificateNo" :show-overflow-tooltip="true"/>
|
||||||
<el-table-column label="中介类型" align="center" prop="intermediaryTypeName" width="100"/>
|
<el-table-column label="中介类型" align="center" prop="intermediaryType" width="100">
|
||||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-tag v-if="scope.row.status === '0'" type="success">正常</el-tag>
|
<span v-if="scope.row.intermediaryType === '1'">个人</span>
|
||||||
<el-tag v-else type="danger">停用</el-tag>
|
<span v-else-if="scope.row.intermediaryType === '2'">实体</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||||
|
|||||||
@@ -2,54 +2,67 @@
|
|||||||
<el-dialog title="中介黑名单详情" :visible.sync="visible" width="800px" append-to-body>
|
<el-dialog title="中介黑名单详情" :visible.sync="visible" width="800px" append-to-body>
|
||||||
<el-descriptions :column="2" border>
|
<el-descriptions :column="2" border>
|
||||||
<!-- 核心字段 -->
|
<!-- 核心字段 -->
|
||||||
<el-descriptions-item label="中介ID">{{ detailData.intermediaryId }}</el-descriptions-item>
|
<el-descriptions-item label="中介类型">
|
||||||
<el-descriptions-item label="中介类型">{{ detailData.intermediaryTypeName }}</el-descriptions-item>
|
<span v-if="detailData.intermediaryType === '1'">个人</span>
|
||||||
<el-descriptions-item label="姓名/机构名称">{{ detailData.name }}</el-descriptions-item>
|
<span v-else-if="detailData.intermediaryType === '2'">实体</span>
|
||||||
<el-descriptions-item label="证件号/信用代码">{{ detailData.certificateNo || '-' }}</el-descriptions-item>
|
<span v-else>-</span>
|
||||||
<el-descriptions-item label="状态">
|
</el-descriptions-item>
|
||||||
<el-tag v-if="detailData.status === '0'" type="success">正常</el-tag>
|
<el-descriptions-item label="姓名/机构名称">{{ detailData.name || detailData.enterpriseName || '-' }}</el-descriptions-item>
|
||||||
<el-tag v-else type="danger">停用</el-tag>
|
<el-descriptions-item label="证件号/信用代码">
|
||||||
|
<span v-if="detailData.intermediaryType === '1'">{{ detailData.personId || '-' }}</span>
|
||||||
|
<span v-else>{{ detailData.socialCreditCode || '-' }}</span>
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="数据来源">{{ detailData.dataSourceName || '-' }}</el-descriptions-item>
|
|
||||||
|
|
||||||
<!-- 个人类型专属字段 -->
|
<!-- 个人类型专属字段 -->
|
||||||
<template v-if="detailData.intermediaryType === '1'">
|
<template v-if="detailData.intermediaryType === '1'">
|
||||||
<el-descriptions-item label="人员类型">{{ detailData.indivType || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="人员类型">{{ detailData.personType || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="人员子类型">{{ detailData.indivSubType || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="人员子类型">{{ detailData.personSubType || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="性别">{{ detailData.indivGenderName || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="性别">
|
||||||
<el-descriptions-item label="证件类型">{{ detailData.indivCertType || '-' }}</el-descriptions-item>
|
<span v-if="detailData.gender === 'M'">男</span>
|
||||||
<el-descriptions-item label="手机号码">{{ detailData.indivPhone || '-' }}</el-descriptions-item>
|
<span v-else-if="detailData.gender === 'F'">女</span>
|
||||||
<el-descriptions-item label="微信号">{{ detailData.indivWechat || '-' }}</el-descriptions-item>
|
<span v-else-if="detailData.gender === 'O'">其他</span>
|
||||||
<el-descriptions-item label="联系地址" :span="2">{{ detailData.indivAddress || '-' }}</el-descriptions-item>
|
<span v-else>{{ detailData.gender || '-' }}</span>
|
||||||
<el-descriptions-item label="所在公司">{{ detailData.indivCompany || '-' }}</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="职位">{{ detailData.indivPosition || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="证件类型">{{ detailData.idType || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="关联人员ID">{{ detailData.indivRelatedId || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="手机号码">{{ detailData.mobile || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="关联关系">{{ detailData.indivRelation || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="微信号">{{ detailData.wechatNo || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="联系地址" :span="2">{{ detailData.contactAddress || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="所在公司">{{ detailData.company || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="职位">{{ detailData.position || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="企业统一信用码">{{ detailData.socialCreditCode || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="关系类型">{{ detailData.relationType || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="关联人员ID">{{ detailData.relatedNumId || '-' }}</el-descriptions-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 机构类型专属字段 -->
|
<!-- 机构类型专属字段 -->
|
||||||
<template v-if="detailData.intermediaryType === '2'">
|
<template v-if="detailData.intermediaryType === '2'">
|
||||||
<el-descriptions-item label="统一社会信用代码" :span="2">{{ detailData.corpCreditCode || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="统一社会信用代码" :span="2">{{ detailData.socialCreditCode || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="主体类型">{{ detailData.corpType || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="主体类型">{{ detailData.enterpriseType || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="企业性质">{{ detailData.corpNature || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="企业性质">{{ detailData.enterpriseNature || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="行业分类">{{ detailData.corpIndustryCategory || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="行业分类">{{ detailData.industryClass || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="所属行业">{{ detailData.corpIndustry || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="所属行业">{{ detailData.industryName || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="成立日期">{{ detailData.corpEstablishDate || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="成立日期">{{ detailData.establishDate || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="注册地址" :span="2">{{ detailData.corpAddress || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="注册地址" :span="2">{{ detailData.registerAddress || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="法定代表人">{{ detailData.corpLegalRep || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="法定代表人">{{ detailData.legalRepresentative || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="法定代表人证件类型">{{ detailData.corpLegalCertType || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="法定代表人证件类型">{{ detailData.legalCertType || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="法定代表人证件号码" :span="2">{{ detailData.corpLegalCertNo || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="法定代表人证件号码" :span="2">{{ detailData.legalCertNo || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="股东1">{{ detailData.corpShareholder1 || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="股东1">{{ detailData.shareholder1 || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="股东2">{{ detailData.corpShareholder2 || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="股东2">{{ detailData.shareholder2 || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="股东3">{{ detailData.corpShareholder3 || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="股东3">{{ detailData.shareholder3 || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="股东4">{{ detailData.corpShareholder4 || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="股东4">{{ detailData.shareholder4 || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="股东5">{{ detailData.corpShareholder5 || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="股东5">{{ detailData.shareholder5 || '-' }}</el-descriptions-item>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 通用字段 -->
|
<!-- 通用字段 -->
|
||||||
|
<el-descriptions-item label="数据来源">
|
||||||
|
<span v-if="detailData.dataSource === 'MANUAL'">手动录入</span>
|
||||||
|
<span v-else-if="detailData.dataSource === 'SYSTEM'">系统同步</span>
|
||||||
|
<span v-else-if="detailData.dataSource === 'IMPORT'">批量导入</span>
|
||||||
|
<span v-else-if="detailData.dataSource === 'API'">接口获取</span>
|
||||||
|
<span v-else>{{ detailData.dataSource || '-' }}</span>
|
||||||
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="备注" :span="2">{{ detailData.remark || '-' }}</el-descriptions-item>
|
<el-descriptions-item label="备注" :span="2">{{ detailData.remark || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="创建时间">{{ detailData.createTime }}</el-descriptions-item>
|
<el-descriptions-item label="创建时间">{{ detailData.createTime || '-' }}</el-descriptions-item>
|
||||||
<el-descriptions-item label="创建人">{{ detailData.createBy || '-' }}</el-descriptions-item>
|
|
||||||
</el-descriptions>
|
</el-descriptions>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button @click="visible = false">关 闭</el-button>
|
<el-button @click="visible = false">关 闭</el-button>
|
||||||
|
|||||||
@@ -43,15 +43,15 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="证件号" prop="certificateNo">
|
<el-form-item label="证件号" prop="personId">
|
||||||
<el-input v-model="form.certificateNo" placeholder="请输入证件号码" maxlength="50" clearable/>
|
<el-input v-model="form.personId" placeholder="请输入证件号码" maxlength="50" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="人员类型">
|
<el-form-item label="人员类型">
|
||||||
<el-select v-model="form.indivType" placeholder="请选择人员类型" clearable style="width: 100%">
|
<el-select v-model="form.personType" placeholder="请选择人员类型" clearable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in indivTypeOptions"
|
v-for="item in indivTypeOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -63,21 +63,14 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="人员子类型">
|
<el-form-item label="人员子类型">
|
||||||
<el-select v-model="form.indivSubType" placeholder="请选择人员子类型" clearable style="width: 100%">
|
<el-input v-model="form.personSubType" placeholder="请输入人员子类型" maxlength="100" clearable/>
|
||||||
<el-option
|
|
||||||
v-for="item in indivSubTypeOptions"
|
|
||||||
:key="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="性别">
|
<el-form-item label="性别">
|
||||||
<el-select v-model="form.indivGender" placeholder="请选择性别" clearable style="width: 100%">
|
<el-select v-model="form.gender" placeholder="请选择性别" clearable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in genderOptions"
|
v-for="item in genderOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -89,7 +82,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="证件类型">
|
<el-form-item label="证件类型">
|
||||||
<el-select v-model="form.indivCertType" placeholder="请选择证件类型" clearable style="width: 100%">
|
<el-select v-model="form.idType" placeholder="请选择证件类型" clearable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in certTypeOptions"
|
v-for="item in certTypeOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -103,39 +96,46 @@
|
|||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="手机号码">
|
<el-form-item label="手机号码">
|
||||||
<el-input v-model="form.indivPhone" placeholder="请输入手机号码" maxlength="20" clearable/>
|
<el-input v-model="form.mobile" placeholder="请输入手机号码" maxlength="20" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="微信号">
|
<el-form-item label="微信号">
|
||||||
<el-input v-model="form.indivWechat" placeholder="请输入微信号" maxlength="50" clearable/>
|
<el-input v-model="form.wechatNo" placeholder="请输入微信号" maxlength="50" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-form-item label="联系地址">
|
<el-form-item label="联系地址">
|
||||||
<el-input v-model="form.indivAddress" placeholder="请输入联系地址" maxlength="200" clearable/>
|
<el-input v-model="form.contactAddress" placeholder="请输入联系地址" maxlength="200" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="所在公司">
|
<el-form-item label="所在公司">
|
||||||
<el-input v-model="form.indivCompany" placeholder="请输入所在公司" maxlength="100" clearable/>
|
<el-input v-model="form.company" placeholder="请输入所在公司" maxlength="200" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="职位">
|
<el-form-item label="职位">
|
||||||
<el-input v-model="form.indivPosition" placeholder="请输入职位" maxlength="100" clearable/>
|
<el-input v-model="form.position" placeholder="请输入职位" maxlength="100" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="关联人员ID">
|
<el-form-item label="企业统一信用码">
|
||||||
<el-input v-model="form.indivRelatedId" placeholder="请输入关联人员ID" maxlength="20" clearable/>
|
<el-input v-model="form.socialCreditCode" placeholder="请输入企业统一信用码" maxlength="50" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="关联人员ID">
|
||||||
|
<el-input v-model="form.relatedNumId" placeholder="请输入关联人员ID" maxlength="50" clearable/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="关联关系">
|
<el-form-item label="关联关系">
|
||||||
<el-select v-model="form.indivRelation" placeholder="请选择关联关系" clearable style="width: 100%">
|
<el-select v-model="form.relationType" placeholder="请选择关联关系" clearable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in relationTypeOptions"
|
v-for="item in relationTypeOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -168,17 +168,16 @@
|
|||||||
>
|
>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="机构名称" prop="name">
|
<el-form-item label="机构名称" prop="enterpriseName">
|
||||||
<el-input v-model="form.name" placeholder="请输入机构名称" maxlength="100" clearable/>
|
<el-input v-model="form.enterpriseName" placeholder="请输入机构名称" maxlength="200" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="证件号" prop="certificateNo">
|
<el-form-item label="证件号" prop="socialCreditCode">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.certificateNo"
|
v-model="form.socialCreditCode"
|
||||||
@input="handleCertificateNoChange"
|
placeholder="统一社会信用代码"
|
||||||
placeholder="统一社会信用代码(18位)"
|
maxlength="50"
|
||||||
maxlength="18"
|
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -187,7 +186,7 @@
|
|||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="主体类型">
|
<el-form-item label="主体类型">
|
||||||
<el-select v-model="form.corpType" placeholder="请选择主体类型" clearable style="width: 100%">
|
<el-select v-model="form.enterpriseType" placeholder="请选择主体类型" clearable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in corpTypeOptions"
|
v-for="item in corpTypeOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -199,7 +198,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="企业性质">
|
<el-form-item label="企业性质">
|
||||||
<el-select v-model="form.corpNature" placeholder="请选择企业性质" clearable style="width: 100%">
|
<el-select v-model="form.enterpriseNature" placeholder="请选择企业性质" clearable style="width: 100%">
|
||||||
<el-option
|
<el-option
|
||||||
v-for="item in corpNatureOptions"
|
v-for="item in corpNatureOptions"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
@@ -214,7 +213,7 @@
|
|||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="成立日期">
|
<el-form-item label="成立日期">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="form.corpEstablishDate"
|
v-model="form.establishDate"
|
||||||
type="date"
|
type="date"
|
||||||
placeholder="选择成立日期"
|
placeholder="选择成立日期"
|
||||||
value-format="yyyy-MM-dd"
|
value-format="yyyy-MM-dd"
|
||||||
@@ -224,64 +223,71 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="行业分类">
|
<el-form-item label="行业分类">
|
||||||
<el-input v-model="form.corpIndustryCategory" placeholder="请输入行业分类" maxlength="100" clearable/>
|
<el-input v-model="form.industryClass" placeholder="请输入行业分类" maxlength="100" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="所属行业">
|
<el-form-item label="所属行业">
|
||||||
<el-input v-model="form.corpIndustry" placeholder="请输入所属行业" maxlength="100" clearable/>
|
<el-input v-model="form.industryName" placeholder="请输入所属行业" maxlength="100" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-form-item label="注册地址">
|
<el-form-item label="注册地址">
|
||||||
<el-input v-model="form.corpAddress" type="textarea" placeholder="请输入注册地址" maxlength="500" :rows="2"/>
|
<el-input v-model="form.registerAddress" type="textarea" placeholder="请输入注册地址" maxlength="500" :rows="2"/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="法定代表人">
|
<el-form-item label="法定代表人">
|
||||||
<el-input v-model="form.corpLegalRep" placeholder="请输入法定代表人" maxlength="50" clearable/>
|
<el-input v-model="form.legalRepresentative" placeholder="请输入法定代表人" maxlength="100" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="法定代表人证件类型">
|
<el-form-item label="法定代表人证件类型">
|
||||||
<el-input v-model="form.corpLegalCertType" placeholder="请输入证件类型" maxlength="30" clearable/>
|
<el-select v-model="form.legalCertType" placeholder="请选择证件类型" clearable style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="item in certTypeOptions"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-form-item label="法定代表人证件号码">
|
<el-form-item label="法定代表人证件号码">
|
||||||
<el-input v-model="form.corpLegalCertNo" placeholder="请输入法定代表人证件号码" maxlength="30" clearable/>
|
<el-input v-model="form.legalCertNo" placeholder="请输入法定代表人证件号码" maxlength="50" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-divider content-position="left">股东信息</el-divider>
|
<el-divider content-position="left">股东信息</el-divider>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="股东1">
|
<el-form-item label="股东1">
|
||||||
<el-input v-model="form.corpShareholder1" placeholder="请输入股东1" maxlength="30" clearable/>
|
<el-input v-model="form.shareholder1" placeholder="请输入股东1" maxlength="100" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="股东2">
|
<el-form-item label="股东2">
|
||||||
<el-input v-model="form.corpShareholder2" placeholder="请输入股东2" maxlength="30" clearable/>
|
<el-input v-model="form.shareholder2" placeholder="请输入股东2" maxlength="100" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="股东3">
|
<el-form-item label="股东3">
|
||||||
<el-input v-model="form.corpShareholder3" placeholder="请输入股东3" maxlength="30" clearable/>
|
<el-input v-model="form.shareholder3" placeholder="请输入股东3" maxlength="100" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="股东4">
|
<el-form-item label="股东4">
|
||||||
<el-input v-model="form.corpShareholder4" placeholder="请输入股东4" maxlength="30" clearable/>
|
<el-input v-model="form.shareholder4" placeholder="请输入股东4" maxlength="100" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="股东5">
|
<el-form-item label="股东5">
|
||||||
<el-input v-model="form.corpShareholder5" placeholder="请输入股东5" maxlength="30" clearable/>
|
<el-input v-model="form.shareholder5" placeholder="请输入股东5" maxlength="100" clearable/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -329,10 +335,6 @@ export default {
|
|||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
},
|
},
|
||||||
indivSubTypeOptions: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
genderOptions: {
|
genderOptions: {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => []
|
default: () => []
|
||||||
@@ -370,7 +372,7 @@ export default {
|
|||||||
{ required: true, message: "姓名不能为空", trigger: "blur" },
|
{ required: true, message: "姓名不能为空", trigger: "blur" },
|
||||||
{ max: 100, message: "姓名长度不能超过100个字符", trigger: "blur" }
|
{ max: 100, message: "姓名长度不能超过100个字符", trigger: "blur" }
|
||||||
],
|
],
|
||||||
certificateNo: [
|
personId: [
|
||||||
{ required: true, message: "证件号不能为空", trigger: "blur" },
|
{ required: true, message: "证件号不能为空", trigger: "blur" },
|
||||||
{ max: 50, message: "证件号长度不能超过50个字符", trigger: "blur" }
|
{ max: 50, message: "证件号长度不能超过50个字符", trigger: "blur" }
|
||||||
],
|
],
|
||||||
@@ -380,13 +382,13 @@ export default {
|
|||||||
},
|
},
|
||||||
// 机构类型验证规则
|
// 机构类型验证规则
|
||||||
corpRules: {
|
corpRules: {
|
||||||
name: [
|
enterpriseName: [
|
||||||
{ required: true, message: "机构名称不能为空", trigger: "blur" },
|
{ required: true, message: "机构名称不能为空", trigger: "blur" },
|
||||||
{ max: 100, message: "机构名称长度不能超过100个字符", trigger: "blur" }
|
{ max: 200, message: "机构名称长度不能超过200个字符", trigger: "blur" }
|
||||||
],
|
],
|
||||||
certificateNo: [
|
socialCreditCode: [
|
||||||
{ required: true, message: "证件号不能为空", trigger: "blur" },
|
{ required: true, message: "统一社会信用代码不能为空", trigger: "blur" },
|
||||||
{ max: 18, message: "统一社会信用代码长度为18位", trigger: "blur" }
|
{ max: 50, message: "统一社会信用代码长度不能超过50个字符", trigger: "blur" }
|
||||||
],
|
],
|
||||||
remark: [
|
remark: [
|
||||||
{ max: 500, message: "备注长度不能超过500个字符", trigger: "blur" }
|
{ max: 500, message: "备注长度不能超过500个字符", trigger: "blur" }
|
||||||
@@ -397,7 +399,7 @@ export default {
|
|||||||
computed: {
|
computed: {
|
||||||
// 判断是否为新增模式
|
// 判断是否为新增模式
|
||||||
isAddMode() {
|
isAddMode() {
|
||||||
return !this.form || !this.form.intermediaryId;
|
return !this.form || !this.form.bizId;
|
||||||
},
|
},
|
||||||
// 根据选择的类型返回分隔线文本
|
// 根据选择的类型返回分隔线文本
|
||||||
getTypeDividerText() {
|
getTypeDividerText() {
|
||||||
@@ -426,7 +428,7 @@ export default {
|
|||||||
*/
|
*/
|
||||||
initDialogState() {
|
initDialogState() {
|
||||||
// 始终基于当前的 form 状态判断
|
// 始终基于当前的 form 状态判断
|
||||||
const isAdd = !this.form || !this.form.intermediaryId;
|
const isAdd = !this.form || !this.form.bizId;
|
||||||
|
|
||||||
if (isAdd) {
|
if (isAdd) {
|
||||||
// 新增模式:重置选择状态
|
// 新增模式:重置选择状态
|
||||||
@@ -466,16 +468,6 @@ export default {
|
|||||||
}, 50);
|
}, 50);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* 处理机构类型证件号变更
|
|
||||||
* 同步到统一社会信用代码字段
|
|
||||||
*/
|
|
||||||
handleCertificateNoChange(value) {
|
|
||||||
if (this.form.intermediaryType === '2') {
|
|
||||||
this.form.corpCreditCode = value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交表单
|
* 提交表单
|
||||||
*/
|
*/
|
||||||
@@ -488,12 +480,6 @@ export default {
|
|||||||
|
|
||||||
// 根据类型验证不同的表单
|
// 根据类型验证不同的表单
|
||||||
const formRef = this.form.intermediaryType === '1' ? 'indivForm' : 'corpForm';
|
const formRef = this.form.intermediaryType === '1' ? 'indivForm' : 'corpForm';
|
||||||
const rules = this.form.intermediaryType === '1' ? this.indivRules : this.corpRules;
|
|
||||||
|
|
||||||
// 机构类型:同步证件号到统一社会信用代码
|
|
||||||
if (this.form.intermediaryType === '2') {
|
|
||||||
this.form.corpCreditCode = this.form.certificateNo;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$refs[formRef].validate(valid => {
|
this.$refs[formRef].validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
|||||||
@@ -148,9 +148,9 @@ export default {
|
|||||||
},
|
},
|
||||||
handleDownloadTemplate() {
|
handleDownloadTemplate() {
|
||||||
if (this.formData.importType === 'person') {
|
if (this.formData.importType === 'person') {
|
||||||
this.download('dpc/intermediary/importPersonTemplate', {}, `个人中介黑名单模板_${new Date().getTime()}.xlsx`);
|
this.download('ccdi/intermediary/importPersonTemplate', {}, `个人中介黑名单模板_${new Date().getTime()}.xlsx`);
|
||||||
} else {
|
} else {
|
||||||
this.download('dpc/intermediary/importEntityTemplate', {}, `机构中介黑名单模板_${new Date().getTime()}.xlsx`);
|
this.download('ccdi/intermediary/importEntityTemplate', {}, `机构中介黑名单模板_${new Date().getTime()}.xlsx`);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleFileUploadProgress() {
|
handleFileUploadProgress() {
|
||||||
@@ -166,10 +166,26 @@ export default {
|
|||||||
this.isUploading = false;
|
this.isUploading = false;
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
this.$emit("success");
|
this.$emit("success");
|
||||||
|
|
||||||
|
// 解析后端返回的消息,只展示错误部分
|
||||||
|
let displayMessage = response.msg;
|
||||||
|
|
||||||
|
// 如果消息包含"恭喜您,数据已全部导入成功",说明全部成功,不展示详细列表
|
||||||
|
if (displayMessage.includes('恭喜您,数据已全部导入成功')) {
|
||||||
|
// 全部成功,使用简洁提示
|
||||||
|
displayMessage = '导入成功!';
|
||||||
|
}
|
||||||
|
// 如果消息包含"很抱歉,导入失败",说明有错误,只展示错误部分
|
||||||
|
else if (displayMessage.includes('很抱歉,导入失败')) {
|
||||||
|
// 只保留错误部分,移除成功统计信息
|
||||||
|
const lines = displayMessage.split('<br/><br/>');
|
||||||
|
displayMessage = lines[0]; // 只取错误部分
|
||||||
|
}
|
||||||
|
|
||||||
this.$msgbox({
|
this.$msgbox({
|
||||||
title: '导入结果',
|
title: '导入结果',
|
||||||
dangerouslyUseHTMLString: true,
|
dangerouslyUseHTMLString: true,
|
||||||
message: `<div style="overflow-y: auto; max-height: 60vh; padding-right: 10px; line-height: 1.6;">${response.msg}</div>`,
|
message: `<div style="overflow-y: auto; max-height: 60vh; padding-right: 10px; line-height: 1.6;">${displayMessage}</div>`,
|
||||||
confirmButtonText: '确定',
|
confirmButtonText: '确定',
|
||||||
customClass: 'import-result-dialog'
|
customClass: 'import-result-dialog'
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,14 +22,7 @@
|
|||||||
<el-select v-model="queryParams.intermediaryType" placeholder="中介类型" clearable style="width: 240px">
|
<el-select v-model="queryParams.intermediaryType" placeholder="中介类型" clearable style="width: 240px">
|
||||||
<el-option label="全部" value="" />
|
<el-option label="全部" value="" />
|
||||||
<el-option label="个人" value="1" />
|
<el-option label="个人" value="1" />
|
||||||
<el-option label="机构" value="2" />
|
<el-option label="实体" value="2" />
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="状态" prop="status">
|
|
||||||
<el-select v-model="queryParams.status" placeholder="状态" clearable style="width: 240px">
|
|
||||||
<el-option label="全部" value="" />
|
|
||||||
<el-option label="正常" value="0" />
|
|
||||||
<el-option label="停用" value="1" />
|
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
|
|||||||
@@ -51,7 +51,6 @@
|
|||||||
:title="title"
|
:title="title"
|
||||||
:form="form"
|
:form="form"
|
||||||
:indiv-type-options="indivTypeOptions"
|
:indiv-type-options="indivTypeOptions"
|
||||||
:indiv-sub-type-options="indivSubTypeOptions"
|
|
||||||
:gender-options="genderOptions"
|
:gender-options="genderOptions"
|
||||||
:cert-type-options="certTypeOptions"
|
:cert-type-options="certTypeOptions"
|
||||||
:relation-type-options="relationTypeOptions"
|
:relation-type-options="relationTypeOptions"
|
||||||
@@ -82,7 +81,8 @@ import {
|
|||||||
addEntityIntermediary,
|
addEntityIntermediary,
|
||||||
addPersonIntermediary,
|
addPersonIntermediary,
|
||||||
delIntermediary,
|
delIntermediary,
|
||||||
getIntermediary,
|
getEntityIntermediary,
|
||||||
|
getPersonIntermediary,
|
||||||
listIntermediary,
|
listIntermediary,
|
||||||
updateEntityIntermediary,
|
updateEntityIntermediary,
|
||||||
updatePersonIntermediary
|
updatePersonIntermediary
|
||||||
@@ -92,7 +92,6 @@ import {
|
|||||||
getCorpNatureOptions,
|
getCorpNatureOptions,
|
||||||
getCorpTypeOptions,
|
getCorpTypeOptions,
|
||||||
getGenderOptions,
|
getGenderOptions,
|
||||||
getIndivSubTypeOptions,
|
|
||||||
getIndivTypeOptions,
|
getIndivTypeOptions,
|
||||||
getRelationTypeOptions
|
getRelationTypeOptions
|
||||||
} from "@/api/ccdiEnum";
|
} from "@/api/ccdiEnum";
|
||||||
@@ -129,8 +128,7 @@ export default {
|
|||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
name: null,
|
name: null,
|
||||||
certificateNo: null,
|
certificateNo: null,
|
||||||
intermediaryType: null,
|
intermediaryType: null
|
||||||
status: null
|
|
||||||
},
|
},
|
||||||
form: {},
|
form: {},
|
||||||
upload: {
|
upload: {
|
||||||
@@ -138,7 +136,6 @@ export default {
|
|||||||
title: ""
|
title: ""
|
||||||
},
|
},
|
||||||
indivTypeOptions: [],
|
indivTypeOptions: [],
|
||||||
indivSubTypeOptions: [],
|
|
||||||
genderOptions: [],
|
genderOptions: [],
|
||||||
certTypeOptions: [],
|
certTypeOptions: [],
|
||||||
relationTypeOptions: [],
|
relationTypeOptions: [],
|
||||||
@@ -156,9 +153,6 @@ export default {
|
|||||||
getIndivTypeOptions().then(response => {
|
getIndivTypeOptions().then(response => {
|
||||||
this.indivTypeOptions = response.data;
|
this.indivTypeOptions = response.data;
|
||||||
});
|
});
|
||||||
getIndivSubTypeOptions().then(response => {
|
|
||||||
this.indivSubTypeOptions = response.data;
|
|
||||||
});
|
|
||||||
getGenderOptions().then(response => {
|
getGenderOptions().then(response => {
|
||||||
this.genderOptions = response.data;
|
this.genderOptions = response.data;
|
||||||
});
|
});
|
||||||
@@ -191,7 +185,7 @@ export default {
|
|||||||
},
|
},
|
||||||
/** 多选框选中数据 */
|
/** 多选框选中数据 */
|
||||||
handleSelectionChange(selection) {
|
handleSelectionChange(selection) {
|
||||||
this.ids = selection.map(item => item.intermediaryId);
|
this.ids = selection.map(item => item.id);
|
||||||
this.single = selection.length !== 1;
|
this.single = selection.length !== 1;
|
||||||
this.multiple = !selection.length;
|
this.multiple = !selection.length;
|
||||||
},
|
},
|
||||||
@@ -204,40 +198,42 @@ export default {
|
|||||||
/** 表单重置 */
|
/** 表单重置 */
|
||||||
reset() {
|
reset() {
|
||||||
this.form = {
|
this.form = {
|
||||||
intermediaryId: null,
|
bizId: null,
|
||||||
name: null,
|
name: null,
|
||||||
certificateNo: null,
|
|
||||||
intermediaryType: "1",
|
intermediaryType: "1",
|
||||||
status: "0",
|
|
||||||
remark: null,
|
remark: null,
|
||||||
indivType: null,
|
// 个人中介字段
|
||||||
indivSubType: null,
|
personId: null,
|
||||||
indivGender: null,
|
personType: null,
|
||||||
indivCertType: null,
|
personSubType: null,
|
||||||
indivPhone: null,
|
relationType: null,
|
||||||
indivWechat: null,
|
gender: null,
|
||||||
indivAddress: null,
|
idType: null,
|
||||||
indivCompany: null,
|
mobile: null,
|
||||||
indivPosition: null,
|
wechatNo: null,
|
||||||
indivRelatedId: null,
|
contactAddress: null,
|
||||||
indivRelation: null,
|
company: null,
|
||||||
corpCreditCode: null,
|
socialCreditCode: null,
|
||||||
corpType: null,
|
position: null,
|
||||||
corpNature: null,
|
relatedNumId: null,
|
||||||
corpIndustryCategory: null,
|
// 实体中介字段
|
||||||
corpIndustry: null,
|
enterpriseName: null,
|
||||||
corpEstablishDate: null,
|
enterpriseType: null,
|
||||||
corpAddress: null,
|
enterpriseNature: null,
|
||||||
corpLegalRep: null,
|
industryClass: null,
|
||||||
corpLegalCertType: null,
|
industryName: null,
|
||||||
corpLegalCertNo: null,
|
establishDate: null,
|
||||||
corpShareholder1: null,
|
registerAddress: null,
|
||||||
corpShareholder2: null,
|
legalRepresentative: null,
|
||||||
corpShareholder3: null,
|
legalCertType: null,
|
||||||
corpShareholder4: null,
|
legalCertNo: null,
|
||||||
corpShareholder5: null
|
shareholder1: null,
|
||||||
|
shareholder2: null,
|
||||||
|
shareholder3: null,
|
||||||
|
shareholder4: null,
|
||||||
|
shareholder5: null
|
||||||
};
|
};
|
||||||
// 注意:不调用 this.resetForm("form")
|
// 注意:不调用 this.resetForm("form")
|
||||||
// EditDialog 组件会在 visible 变化时自动处理表单验证状态的重置
|
// EditDialog 组件会在 visible 变化时自动处理表单验证状态的重置
|
||||||
},
|
},
|
||||||
/** 取消按钮 */
|
/** 取消按钮 */
|
||||||
@@ -247,25 +243,42 @@ export default {
|
|||||||
},
|
},
|
||||||
/** 查看详情操作 */
|
/** 查看详情操作 */
|
||||||
handleDetail(row) {
|
handleDetail(row) {
|
||||||
const intermediaryId = row.intermediaryId;
|
if (row.intermediaryType === '1') {
|
||||||
getIntermediary(intermediaryId).then(response => {
|
// 个人中介 - 使用row.id作为bizId
|
||||||
this.detailData = response.data;
|
getPersonIntermediary(row.id).then(response => {
|
||||||
this.detailOpen = true;
|
this.detailData = response.data;
|
||||||
});
|
this.detailOpen = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 实体中介 - 使用row.id作为socialCreditCode
|
||||||
|
getEntityIntermediary(row.id).then(response => {
|
||||||
|
this.detailData = response.data;
|
||||||
|
this.detailOpen = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/** 修改按钮操作 */
|
/** 修改按钮操作 */
|
||||||
handleUpdate(row) {
|
handleUpdate(row) {
|
||||||
this.reset();
|
this.reset();
|
||||||
const intermediaryId = row.intermediaryId || this.ids[0];
|
if (row.intermediaryType === '1') {
|
||||||
getIntermediary(intermediaryId).then(response => {
|
// 个人中介 - 使用row.id作为bizId
|
||||||
this.form = response.data;
|
getPersonIntermediary(row.id).then(response => {
|
||||||
this.open = true;
|
this.form = response.data;
|
||||||
this.title = "修改中介黑名单";
|
this.open = true;
|
||||||
});
|
this.title = "修改中介黑名单";
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 实体中介 - 使用row.id作为socialCreditCode
|
||||||
|
getEntityIntermediary(row.id).then(response => {
|
||||||
|
this.form = response.data;
|
||||||
|
this.open = true;
|
||||||
|
this.title = "修改中介黑名单";
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/** 提交按钮 */
|
/** 提交按钮 */
|
||||||
submitForm() {
|
submitForm() {
|
||||||
if (this.form.intermediaryId != null) {
|
if (this.form.bizId != null) {
|
||||||
// 修改模式:根据中介类型调用不同的接口
|
// 修改模式:根据中介类型调用不同的接口
|
||||||
if (this.form.intermediaryType === '1') {
|
if (this.form.intermediaryType === '1') {
|
||||||
// 个人中介
|
// 个人中介
|
||||||
@@ -303,9 +316,12 @@ export default {
|
|||||||
},
|
},
|
||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
handleDelete(row) {
|
handleDelete(row) {
|
||||||
const intermediaryIds = row.intermediaryId || this.ids;
|
const bizIds = row.id || this.ids.join(',');
|
||||||
this.$modal.confirm('是否确认删除中介黑名单编号为"' + intermediaryIds + '"的数据项?').then(function() {
|
const confirmMsg = row.id
|
||||||
return delIntermediary(intermediaryIds);
|
? `确认删除中介"${row.name}"(证件号:${row.certificateNo})吗?`
|
||||||
|
: `确认删除选中的 ${this.ids.length} 条中介数据吗?`;
|
||||||
|
this.$modal.confirm(confirmMsg).then(function() {
|
||||||
|
return delIntermediary(bizIds);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
this.getList();
|
this.getList();
|
||||||
this.$modal.msgSuccess("删除成功");
|
this.$modal.msgSuccess("删除成功");
|
||||||
|
|||||||
36
sql/dpc_intermediary_relation_type_dict_20260205.sql
Normal file
36
sql/dpc_intermediary_relation_type_dict_20260205.sql
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 中介黑名单关系类型字典补充脚本
|
||||||
|
-- 功能:为个人中介添加关系类型字典
|
||||||
|
-- 作者:ruoyi
|
||||||
|
-- 日期:2026-02-05
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
USE `discipline-prelim-check`;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 关系类型字典(ccdi_relation_type)
|
||||||
|
-- ============================================================
|
||||||
|
DELETE FROM sys_dict_data WHERE dict_type = 'ccdi_relation_type';
|
||||||
|
DELETE FROM sys_dict_type WHERE dict_type = 'ccdi_relation_type';
|
||||||
|
|
||||||
|
INSERT INTO sys_dict_type (dict_name, dict_type, status, create_by, create_time, remark)
|
||||||
|
VALUES ('关系类型', 'ccdi_relation_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, '配偶', '配偶', 'ccdi_relation_type', '', 'default', 'N', '0', 'admin', NOW(), NULL),
|
||||||
|
(2, '父亲', '父亲', 'ccdi_relation_type', '', 'default', 'N', '0', 'admin', NOW(), NULL),
|
||||||
|
(3, '母亲', '母亲', 'ccdi_relation_type', '', 'default', 'N', '0', 'admin', NOW(), NULL),
|
||||||
|
(4, '子女', '子女', 'ccdi_relation_type', '', 'default', 'N', '0', 'admin', NOW(), NULL),
|
||||||
|
(5, '兄弟', '兄弟', 'ccdi_relation_type', '', 'default', 'N', '0', 'admin', NOW(), NULL),
|
||||||
|
(6, '姐妹', '姐妹', 'ccdi_relation_type', '', 'default', 'N', '0', 'admin', NOW(), NULL),
|
||||||
|
(7, '合伙人', '合伙人', 'ccdi_relation_type', '', 'default', 'N', '0', 'admin', NOW(), NULL),
|
||||||
|
(8, '同事', '同事', 'ccdi_relation_type', '', 'default', 'N', '0', 'admin', NOW(), NULL),
|
||||||
|
(9, '其他', '其他', 'ccdi_relation_type', '', 'default', 'N', '0', 'admin', NOW(), NULL);
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 执行完成后操作说明
|
||||||
|
-- ============================================================
|
||||||
|
-- 1. 执行完成后,请通过系统管理 > 字典管理验证字典数据
|
||||||
|
-- 2. 重要:在字典类型页面点击"刷新缓存"按钮,确保字典数据加载到 Redis
|
||||||
|
-- 3. 验证字典缓存是否生效(可通过测试模板下载确认下拉框是否显示)
|
||||||
Reference in New Issue
Block a user