26 KiB
Credit Info Maintenance Backend Implementation Plan
For agentic workers: REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 在 ccdi-info-collection 模块中新增征信维护后端能力,支持批量上传征信 HTML、调用现有 ccdi-lsfx 征信解析客户端、按员工维度覆盖写入 ccdi_debts_info 与 ccdi_credit_negative_info,并提供列表、详情、删除接口。
Architecture: 业务入口放在 ccdi-info-collection,由独立 CcdiCreditInfoController 暴露上传、列表、详情、删除接口。上传链路直接复用 ccdi-lsfx 的 CreditParseClient,通过 query_cert_no 归户到员工,并由独立 payload 装配器把 lx_debt 转成负债明细、把 lx_publictype 转成负面信息;最终按员工身份证号在单事务内先删后插,保证“只保留最新征信”。
Tech Stack: Java 21, Spring Boot 3, MyBatis Plus, MyBatis XML, Jackson, JUnit 5, Mockito, Maven, MySQL
文件结构与职责
新增文件
sql/migration/2026-03-23-create-credit-info-tables.sql创建ccdi_debts_info与ccdi_credit_negative_info两张业务表。sql/ccdi_credit_info_menu.sql在“信息维护”下新增“征信维护”菜单与按钮权限。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiDebtsInfo.java负债明细实体。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiCreditNegativeInfo.java负面信息实体。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiCreditInfoQueryDTO.java列表查询条件 DTO。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoListVO.java员工维度摘要列表 VO。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoDetailVO.java征信详情聚合 VO。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoNegativeVO.java负面信息展示 VO。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadResultVO.java批量上传结果 VO。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadFailureVO.java单文件失败结果 VO。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiDebtsInfoMapper.java负债表增删查 Mapper。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditNegativeInfoMapper.java负面信息表增删查 Mapper。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditInfoQueryMapper.java员工维度摘要列表与详情聚合查询 Mapper。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiCreditInfoService.java征信维护业务服务接口。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java征信维护业务服务实现。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssembler.java负责把CreditParseResponse装配成负债明细和负面信息对象。ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiCreditInfoController.java征信维护上传、列表、详情、删除接口。ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml聚合查询 SQL。docs/reports/implementation/2026-03-23-credit-info-maintenance-backend-implementation.md后端实施记录。
修改文件
ccdi-info-collection/pom.xml显式引入ccdi-lsfx依赖,允许业务服务直接复用CreditParseClient。
测试文件
ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssemblerTest.java锁定lx_debt与lx_publictype的映射规则。ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java锁定批量上传、最新征信覆盖、单员工失败隔离与删除行为。ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiCreditInfoControllerTest.java锁定接口入参、分页、删除与返回结构。
参考文件
docs/design/2026-03-23-credit-info-maintenance-design.mdccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/CreditParseClient.javaccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/CreditParseResponse.javaccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiBaseStaffController.javaccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiAssetInfoImportServiceImpl.javaccdi-info-collection/src/main/resources/mapper/info/collection/CcdiBaseStaffMapper.xml
Task 1: 建表脚本与菜单脚本
Files:
-
Create:
sql/migration/2026-03-23-create-credit-info-tables.sql -
Create:
sql/ccdi_credit_info_menu.sql -
Review:
assets/征信解析/ccdi_debts_info.xlsx -
Review:
docs/design/2026-03-23-credit-info-maintenance-design.md -
Step 1: 先写脚本契约检查命令
Run:
test -f sql/migration/2026-03-23-create-credit-info-tables.sql
Expected:
-
FAIL,因为建表脚本尚不存在。
-
Step 2: 编写两张业务表脚本
在 sql/migration/2026-03-23-create-credit-info-tables.sql 中创建:
CREATE TABLE `ccdi_debts_info` (
`debt_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`person_id` VARCHAR(18) NOT NULL COMMENT '员工身份证号',
`person_name` VARCHAR(100) DEFAULT NULL COMMENT '员工姓名',
`query_date` DATE DEFAULT NULL COMMENT '征信查询日期',
`debt_main_type` VARCHAR(50) DEFAULT NULL COMMENT '负债大类',
`debt_sub_type` VARCHAR(50) DEFAULT NULL COMMENT '负债小类',
`creditor_type` VARCHAR(50) DEFAULT NULL COMMENT '债权人类型',
`debt_name` VARCHAR(100) DEFAULT NULL COMMENT '负债名称',
`principal_balance` DECIMAL(18,2) DEFAULT NULL COMMENT '负债本金余额',
`debt_total_amount` DECIMAL(18,2) DEFAULT NULL COMMENT '负债总额',
`debt_status` VARCHAR(20) DEFAULT NULL COMMENT '负债状态',
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`debt_id`),
KEY `idx_person_id` (`person_id`),
KEY `idx_query_date` (`query_date`),
KEY `idx_person_query_date` (`person_id`, `query_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工征信负债明细';
CREATE TABLE `ccdi_credit_negative_info` (
`negative_id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
`person_id` VARCHAR(18) NOT NULL COMMENT '员工身份证号',
`person_name` VARCHAR(100) DEFAULT NULL COMMENT '员工姓名',
`query_date` DATE DEFAULT NULL COMMENT '征信查询日期',
`civil_cnt` INT DEFAULT 0 COMMENT '民事案件笔数',
`enforce_cnt` INT DEFAULT 0 COMMENT '强制执行笔数',
`adm_cnt` INT DEFAULT 0 COMMENT '行政处罚笔数',
`civil_lmt` DECIMAL(18,2) DEFAULT 0 COMMENT '民事案件金额',
`enforce_lmt` DECIMAL(18,2) DEFAULT 0 COMMENT '强制执行金额',
`adm_lmt` DECIMAL(18,2) DEFAULT 0 COMMENT '行政处罚金额',
`create_by` VARCHAR(64) DEFAULT '' COMMENT '创建者',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`update_by` VARCHAR(64) DEFAULT '' COMMENT '更新者',
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
PRIMARY KEY (`negative_id`),
UNIQUE KEY `uk_person_id` (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='员工征信负面信息';
- Step 3: 编写菜单与权限脚本
在 sql/ccdi_credit_info_menu.sql 中新增:
SET @parent_menu_id = (SELECT menu_id FROM sys_menu WHERE menu_name='信息维护' AND parent_id=0 LIMIT 1);
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark)
VALUES ('征信维护', @parent_menu_id, 4, 'creditInfo', 'ccdiCreditInfo/index', 1, 0, 'C', '0', '0', 'ccdi:creditInfo:list', 'document', 'admin', NOW(), '', NULL, '员工征信维护菜单');
SET @menu_id = LAST_INSERT_ID();
INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, remark) VALUES
('征信查询', @menu_id, 1, '', '', 1, 0, 'F', '0', '0', 'ccdi:creditInfo:query', '#', 'admin', NOW(), ''),
('征信上传', @menu_id, 2, '', '', 1, 0, 'F', '0', '0', 'ccdi:creditInfo:upload', '#', 'admin', NOW(), ''),
('征信删除', @menu_id, 3, '', '', 1, 0, 'F', '0', '0', 'ccdi:creditInfo:remove', '#', 'admin', NOW(), '');
- Step 4: 检查脚本关键字
Run:
rg -n "ccdi_debts_info|ccdi_credit_negative_info|征信维护|ccdi:creditInfo" sql/migration/2026-03-23-create-credit-info-tables.sql sql/ccdi_credit_info_menu.sql
Expected:
-
PASS,能看到两张表名、菜单名和权限前缀。
-
Step 5: 提交 SQL 脚本
git add sql/migration/2026-03-23-create-credit-info-tables.sql sql/ccdi_credit_info_menu.sql
git commit -m "新增征信维护建表与菜单脚本"
Task 2: 接入 ccdi-lsfx 依赖并建立对象骨架
Files:
-
Modify:
ccdi-info-collection/pom.xml -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiDebtsInfo.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/CcdiCreditNegativeInfo.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiCreditInfoQueryDTO.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoListVO.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoDetailVO.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoNegativeVO.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadResultVO.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoUploadFailureVO.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiDebtsInfoMapper.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditNegativeInfoMapper.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/CcdiCreditInfoQueryMapper.java -
Step 1: 先写编译失败用例
在 ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java 先写最小测试骨架,显式引用待创建的对象:
class CcdiCreditInfoServiceImplTest {
@Test
void shouldCompileCreditInfoContracts() {
CcdiCreditInfoQueryDTO queryDTO = new CcdiCreditInfoQueryDTO();
CreditInfoUploadResultVO resultVO = new CreditInfoUploadResultVO();
assertNotNull(queryDTO);
assertNotNull(resultVO);
}
}
- Step 2: 运行测试确认失败
Run:
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoServiceImplTest test
Expected:
-
FAIL,提示相关 DTO / VO 不存在,且
ccdi-info-collection还不能解析CreditParseClient依赖。 -
Step 3: 增加模块依赖
在 ccdi-info-collection/pom.xml 中追加:
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ccdi-lsfx</artifactId>
<version>3.9.1</version>
</dependency>
- Step 4: 建立实体与 VO 骨架
实体最小结构示例:
@Data
@TableName("ccdi_debts_info")
public class CcdiDebtsInfo {
@TableId(type = IdType.AUTO)
private Long debtId;
private String personId;
private String personName;
private Date queryDate;
private String debtMainType;
private String debtSubType;
private String creditorType;
private String debtName;
private BigDecimal principalBalance;
private BigDecimal debtTotalAmount;
private String debtStatus;
private String createBy;
private Date createTime;
private String updateBy;
private Date updateTime;
}
查询 DTO 最小结构:
@Data
public class CcdiCreditInfoQueryDTO {
private String name;
private String staffId;
private String idCard;
private String maintained;
}
- Step 5: 建立 Mapper 接口骨架
至少补齐如下方法:
List<CcdiDebtsInfo> selectByPersonId(String personId);
int deleteByPersonId(String personId);
int insertBatch(@Param("list") List<CcdiDebtsInfo> list);
CreditInfoListVO selectCreditInfoSummaryByPersonId(String personId);
- Step 6: 运行测试确认通过
Run:
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoServiceImplTest test
Expected:
-
PASS,合同对象已补齐,模块可成功编译测试。
-
Step 7: 提交对象骨架
git add ccdi-info-collection/pom.xml ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java
git commit -m "新增征信维护对象与依赖骨架"
Task 3: 实现 payload 装配器
Files:
-
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssembler.java -
Create:
ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssemblerTest.java -
Step 1: 先写负债映射失败用例
在 CreditInfoPayloadAssemblerTest.java 中先锁定 lx_debt 的 7 组映射:
@Test
void shouldConvertDebtPayloadToSevenTypedRows() {
CreditParsePayload payload = new CreditParsePayload();
Map<String, Object> debt = new HashMap<>();
debt.put("uncle_bank_house_bal", "50000");
debt.put("uncle_bank_house_lmt", "100000");
debt.put("uncle_bank_house_state", "正常");
debt.put("uncle_not_bank_bal", "2000");
debt.put("uncle_not_bank_lmt", "3000");
debt.put("uncle_not_bank_state", "逾期");
payload.setLxDebt(debt);
List<CcdiDebtsInfo> rows = assembler.buildDebts("330101199001010011", "张三", LocalDate.parse("2026-03-01"), payload);
assertEquals(2, rows.size());
assertEquals("住房贷款", rows.get(0).getDebtSubType());
assertEquals("非银", rows.get(1).getCreditorType());
}
- Step 2: 再写负面信息失败用例
@Test
void shouldBuildNegativeInfoFromPublicTypePayload() {
CreditParsePayload payload = new CreditParsePayload();
Map<String, Object> publictype = new HashMap<>();
publictype.put("civil_cnt", 2);
publictype.put("civil_lmt", "9800");
payload.setLxPublictype(publictype);
CcdiCreditNegativeInfo info = assembler.buildNegative("330101199001010011", "张三", LocalDate.parse("2026-03-01"), payload);
assertEquals(2, info.getCivilCnt());
assertEquals(new BigDecimal("9800"), info.getCivilLmt());
}
- Step 3: 运行测试确认失败
Run:
mvn -pl ccdi-info-collection -Dtest=CreditInfoPayloadAssemblerTest test
Expected:
-
FAIL,提示
CreditInfoPayloadAssembler不存在。 -
Step 4: 编写最小装配实现
示例结构:
@Component
public class CreditInfoPayloadAssembler {
public List<CcdiDebtsInfo> buildDebts(String personId, String personName, LocalDate queryDate, CreditParsePayload payload) {
List<CcdiDebtsInfo> rows = new ArrayList<>();
rows.add(buildDebtRow("uncle_bank_house", "银行", "住房贷款", "银行", "未结清银行住房贷款", personId, personName, queryDate, payload.getLxDebt()));
// 其余 6 组同理
return rows.stream().filter(Objects::nonNull).toList();
}
public CcdiCreditNegativeInfo buildNegative(String personId, String personName, LocalDate queryDate, CreditParsePayload payload) {
Map<String, Object> source = payload.getLxPublictype();
CcdiCreditNegativeInfo info = new CcdiCreditNegativeInfo();
info.setPersonId(personId);
info.setPersonName(personName);
info.setQueryDate(java.sql.Date.valueOf(queryDate));
info.setCivilCnt(toInteger(source.get("civil_cnt")));
return info;
}
}
- Step 5: 补上 0 值过滤断言
再追加测试:
@Test
void shouldSkipDebtRowWhenAllMetricsAreEmpty() {
CreditParsePayload payload = new CreditParsePayload();
payload.setLxDebt(new HashMap<>());
assertTrue(assembler.buildDebts("3301", "张三", LocalDate.parse("2026-03-01"), payload).isEmpty());
}
- Step 6: 运行测试确认通过
Run:
mvn -pl ccdi-info-collection -Dtest=CreditInfoPayloadAssemblerTest test
Expected:
-
PASS
-
Step 7: 提交装配器
git add ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssembler.java ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/support/CreditInfoPayloadAssemblerTest.java
git commit -m "新增征信解析结果装配器"
Task 4: 实现批量上传与最新征信覆盖服务
Files:
-
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiCreditInfoService.java -
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java -
Modify:
ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java -
Step 1: 先写批量上传失败用例
在 CcdiCreditInfoServiceImplTest.java 中覆盖“一个员工失败不影响其他员工”:
@Test
void uploadHtmlFiles_shouldIsolateFailuresPerEmployee() {
MockMultipartFile successFile = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8));
MockMultipartFile failFile = new MockMultipartFile("files", "b.html", "text/html", "<html>b</html>".getBytes(StandardCharsets.UTF_8));
when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"))
.thenThrow(new RuntimeException("征信解析失败"));
CreditInfoUploadResultVO result = service.upload(Arrays.asList(successFile, failFile));
assertEquals(1, result.getSuccessCount());
assertEquals(1, result.getFailureCount());
assertEquals("征信解析失败", result.getFailures().get(0).getReason());
}
- Step 2: 再写“旧日期拒绝覆盖”失败用例
@Test
void uploadHtmlFiles_shouldRejectOlderReportDate() {
when(queryMapper.selectLatestQueryDate("330101199001010011"))
.thenReturn(LocalDate.parse("2026-03-05"));
CreditInfoUploadResultVO result = service.upload(List.of(file));
assertEquals(0, result.getSuccessCount());
assertEquals("上传征信日期早于当前已维护最新记录", result.getFailures().get(0).getReason());
}
- Step 3: 运行测试确认失败
Run:
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoServiceImplTest test
Expected:
-
FAIL,提示
ICcdiCreditInfoService/CcdiCreditInfoServiceImpl不存在或上传方法未实现。 -
Step 4: 实现最小上传服务
服务核心流程:
@Transactional(rollbackFor = Exception.class)
public void replaceEmployeeCredit(String personId, List<CcdiDebtsInfo> debts, CcdiCreditNegativeInfo negative, String userName) {
debtsInfoMapper.deleteByPersonId(personId);
negativeInfoMapper.deleteByPersonId(personId);
if (!debts.isEmpty()) {
debts.forEach(item -> {
item.setCreateBy(userName);
item.setUpdateBy(userName);
});
debtsInfoMapper.insertBatch(debts);
}
negative.setCreateBy(userName);
negative.setUpdateBy(userName);
negativeInfoMapper.insert(negative);
}
批量上传方法要求:
-
遍历
MultipartFile[] -
非
.html/.htm文件直接记失败 -
通过
CreditParseClient.parse("LXCUSTALL", "PERSON", tempFile)解析 -
用
query_cert_no查询员工是否存在 -
用
report_time判断是否为最新 -
同一员工事务内执行覆盖写入
-
聚合成功数、失败数、失败清单
-
Step 5: 补充“删除旧数据再插新数据”的验证
追加断言:
verify(debtsInfoMapper).deleteByPersonId("330101199001010011");
verify(negativeInfoMapper).deleteByPersonId("330101199001010011");
verify(debtsInfoMapper).insertBatch(anyList());
verify(negativeInfoMapper).insert(any(CcdiCreditNegativeInfo.class));
- Step 6: 运行测试确认通过
Run:
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoServiceImplTest test
Expected:
-
PASS
-
Step 7: 提交上传服务
git add ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/ICcdiCreditInfoService.java ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java
git commit -m "新增征信维护上传与覆盖服务"
Task 5: 实现列表聚合、详情与删除接口
Files:
-
Create:
ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiCreditInfoController.java -
Create:
ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml -
Create:
ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiCreditInfoControllerTest.java -
Step 1: 先写控制器失败用例
@Test
void list_shouldDelegateWithPageRequest() {
when(service.selectCreditInfoPage(any(), any())).thenReturn(new Page<>(1, 10, 0));
TableDataInfo result = controller.list(new CcdiCreditInfoQueryDTO());
assertEquals(0L, result.getTotal());
}
@Test
void remove_shouldCallDeleteByPersonId() {
when(service.deleteByPersonId("330101199001010011")).thenReturn(1);
AjaxResult result = controller.remove("330101199001010011");
assertEquals(200, result.get("code"));
}
- Step 2: 运行测试确认失败
Run:
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoControllerTest test
Expected:
-
FAIL,提示
CcdiCreditInfoController不存在。 -
Step 3: 编写最小聚合 SQL
在 CcdiCreditInfoQueryMapper.xml 中实现员工维度列表聚合:
<select id="selectCreditInfoPage" resultType="com.ruoyi.info.collection.domain.vo.CreditInfoListVO">
SELECT
s.staff_id,
s.name,
s.id_card,
s.dept_name,
debtAgg.query_date,
IFNULL(debtAgg.debt_count, 0) AS debtCount,
IFNULL(debtAgg.debt_total_amount, 0) AS debtTotalAmount,
IFNULL(neg.civil_cnt, 0) AS civilCnt,
IFNULL(neg.enforce_cnt, 0) AS enforceCnt,
IFNULL(neg.adm_cnt, 0) AS admCnt
FROM ccdi_base_staff s
LEFT JOIN (
SELECT person_id, MAX(query_date) AS query_date, COUNT(*) AS debt_count, SUM(debt_total_amount) AS debt_total_amount
FROM ccdi_debts_info
GROUP BY person_id
) debtAgg ON debtAgg.person_id = s.id_card
LEFT JOIN ccdi_credit_negative_info neg ON neg.person_id = s.id_card
</select>
- Step 4: 编写控制器
接口定义:
@PostMapping("/upload")
public AjaxResult upload(@RequestParam("files") MultipartFile[] files) { ... }
@GetMapping("/list")
public TableDataInfo list(CcdiCreditInfoQueryDTO queryDTO) { ... }
@GetMapping("/{personId}")
public AjaxResult detail(@PathVariable String personId) { ... }
@DeleteMapping("/{personId}")
public AjaxResult remove(@PathVariable String personId) { ... }
权限要求:
-
ccdi:creditInfo:list -
ccdi:creditInfo:query -
ccdi:creditInfo:upload -
ccdi:creditInfo:remove -
Step 5: 运行测试确认通过
Run:
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoControllerTest test
Expected:
-
PASS
-
Step 6: 做一次模块回归
Run:
mvn -pl ccdi-info-collection -am test -Dtest=CreditInfoPayloadAssemblerTest,CcdiCreditInfoServiceImplTest,CcdiCreditInfoControllerTest
mvn -pl ccdi-info-collection -am compile
Expected:
-
PASS,测试与编译均通过。
-
Step 7: 提交接口与聚合查询
git add ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/CcdiCreditInfoController.java ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiCreditInfoControllerTest.java
git commit -m "新增征信维护查询与接口"
Task 6: 联调验证与实施记录
Files:
-
Create:
docs/reports/implementation/2026-03-23-credit-info-maintenance-backend-implementation.md -
Step 1: 启动 Mock 服务
Run:
cd lsfx-mock-server
python3 main.py
Expected:
-
本地监听
http://localhost:8000 -
Step 2: 启动后端并执行上传联调
Run:
mvn -pl ruoyi-admin -am spring-boot:run
curl -F 'files=@assets/征信解析员工样本/0001_徐伟_2040.html;type=text/html' http://127.0.0.1:62318/ccdi/creditInfo/upload
Expected:
-
返回
AjaxResult.code = 200 -
data.successCount = 1 -
data.failureCount = 0 -
Step 3: 验证列表、详情、删除接口
Run:
curl 'http://127.0.0.1:62318/ccdi/creditInfo/list?pageNum=1&pageSize=10'
curl 'http://127.0.0.1:62318/ccdi/creditInfo/320101199001010030'
curl -X DELETE 'http://127.0.0.1:62318/ccdi/creditInfo/320101199001010030'
Expected:
-
列表能返回员工维度摘要
-
详情能返回负债列表和负面信息
-
删除后再次查询详情应为空或提示未维护征信
-
Step 4: 记录实施结果
在实施记录中至少写明:
-
实际修改文件
-
关键测试命令与结果
-
使用
bin/mysql_utf8_exec.sh执行 SQL 的命令 -
启停的后端与 mock 进程及已停止说明
-
Step 5: 提交实施记录
git add docs/reports/implementation/2026-03-23-credit-info-maintenance-backend-implementation.md
git commit -m "补充征信维护后端实施记录"
Review Notes
- 仓库协作约定禁止开启 subagent,因此本计划执行时使用
superpowers:executing-plans,不走子代理审阅。 - SQL 执行必须使用
bin/mysql_utf8_exec.sh <sql-file>,不要直接手写mysql -e。 - 联调完成后要主动关闭
ruoyi-admin与lsfx-mock-server进程,避免残留端口。