调整征信维护为征信对象直接入库

This commit is contained in:
wkc
2026-03-24 15:05:02 +08:00
parent 6b0c83024e
commit fa9673c8c4
13 changed files with 238 additions and 115 deletions

View File

@@ -15,7 +15,5 @@ public class CcdiCreditInfoQueryDTO implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private String name; private String name;
private String staffId;
private String idCard; private String idCard;
private String maintained;
} }

View File

@@ -16,10 +16,8 @@ public class CreditInfoListVO implements Serializable {
@Serial @Serial
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private Long staffId;
private String name; private String name;
private String idCard; private String idCard;
private String deptName;
private Date queryDate; private Date queryDate;
private Long debtCount; private Long debtCount;
private BigDecimal debtTotalAmount; private BigDecimal debtTotalAmount;

View File

@@ -1,9 +1,7 @@
package com.ruoyi.info.collection.service.impl; package com.ruoyi.info.collection.service.impl;
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.common.utils.SecurityUtils; import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.info.collection.domain.CcdiBaseStaff;
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo; import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
import com.ruoyi.info.collection.domain.CcdiDebtsInfo; import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO; import com.ruoyi.info.collection.domain.dto.CcdiCreditInfoQueryDTO;
@@ -12,7 +10,6 @@ import com.ruoyi.info.collection.domain.vo.CreditInfoListVO;
import com.ruoyi.info.collection.domain.vo.CreditInfoNegativeVO; import com.ruoyi.info.collection.domain.vo.CreditInfoNegativeVO;
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadFailureVO; import com.ruoyi.info.collection.domain.vo.CreditInfoUploadFailureVO;
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO; import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO;
import com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper;
import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper; import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper;
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper; import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper; import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
@@ -45,9 +42,6 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
@Resource @Resource
private CreditInfoPayloadAssembler assembler; private CreditInfoPayloadAssembler assembler;
@Resource
private CcdiBaseStaffMapper baseStaffMapper;
@Resource @Resource
private CcdiDebtsInfoMapper debtsInfoMapper; private CcdiDebtsInfoMapper debtsInfoMapper;
@@ -156,7 +150,7 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
String personId = stringValue(header.get("query_cert_no")); String personId = stringValue(header.get("query_cert_no"));
String personName = stringValue(header.get("query_cust_name")); String personName = stringValue(header.get("query_cust_name"));
LocalDate queryDate = parseQueryDate(stringValue(header.get("report_time"))); LocalDate queryDate = parseQueryDate(stringValue(header.get("report_time")));
ensureStaffExists(personId); ensurePersonIdPresent(personId);
ensureLatestQueryDate(personId, queryDate); ensureLatestQueryDate(personId, queryDate);
List<CcdiDebtsInfo> debts = assembler.buildDebts(personId, personName, queryDate, payload); List<CcdiDebtsInfo> debts = assembler.buildDebts(personId, personName, queryDate, payload);
@@ -209,15 +203,9 @@ public class CcdiCreditInfoServiceImpl implements ICcdiCreditInfoService {
return header; return header;
} }
private void ensureStaffExists(String personId) { private void ensurePersonIdPresent(String personId) {
if (isBlank(personId)) { if (isBlank(personId)) {
throw new RuntimeException("征信解析结果缺少员工身份证号"); throw new RuntimeException("征信解析结果缺少身份证号");
}
CcdiBaseStaff staff = baseStaffMapper.selectOne(new LambdaQueryWrapper<CcdiBaseStaff>()
.eq(CcdiBaseStaff::getIdCard, personId)
.last("LIMIT 1"));
if (staff == null) {
throw new RuntimeException("未找到对应员工信息");
} }
} }

View File

@@ -6,68 +6,76 @@
<select id="selectCreditInfoPage" resultType="com.ruoyi.info.collection.domain.vo.CreditInfoListVO"> <select id="selectCreditInfoPage" resultType="com.ruoyi.info.collection.domain.vo.CreditInfoListVO">
SELECT SELECT
s.staff_id, COALESCE(debt_agg.person_name, neg.person_name) AS name,
s.name, credit_keys.person_id AS id_card,
s.id_card, COALESCE(debt_agg.query_date, neg.query_date) AS query_date,
d.dept_name,
debt_agg.query_date,
IFNULL(debt_agg.debt_count, 0) AS debt_count, IFNULL(debt_agg.debt_count, 0) AS debt_count,
IFNULL(debt_agg.debt_total_amount, 0) AS debt_total_amount, IFNULL(debt_agg.debt_total_amount, 0) AS debt_total_amount,
IFNULL(neg.civil_cnt, 0) AS civil_cnt, IFNULL(neg.civil_cnt, 0) AS civil_cnt,
IFNULL(neg.enforce_cnt, 0) AS enforce_cnt, IFNULL(neg.enforce_cnt, 0) AS enforce_cnt,
IFNULL(neg.adm_cnt, 0) AS adm_cnt IFNULL(neg.adm_cnt, 0) AS adm_cnt
FROM ccdi_base_staff s FROM (
LEFT JOIN sys_dept d ON s.dept_id = d.dept_id SELECT person_id
FROM ccdi_debts_info
GROUP BY person_id
UNION
SELECT person_id
FROM ccdi_credit_negative_info
GROUP BY person_id
) credit_keys
LEFT JOIN ( LEFT JOIN (
SELECT SELECT
person_id, person_id,
MAX(person_name) AS person_name,
MAX(query_date) AS query_date, MAX(query_date) AS query_date,
COUNT(*) AS debt_count, COUNT(*) AS debt_count,
SUM(debt_total_amount) AS debt_total_amount SUM(debt_total_amount) AS debt_total_amount
FROM ccdi_debts_info FROM ccdi_debts_info
GROUP BY person_id GROUP BY person_id
) debt_agg ON debt_agg.person_id = s.id_card ) debt_agg ON debt_agg.person_id = credit_keys.person_id
LEFT JOIN ccdi_credit_negative_info neg ON neg.person_id = s.id_card LEFT JOIN ccdi_credit_negative_info neg ON neg.person_id = credit_keys.person_id
<where> <where>
AND (debt_agg.person_id IS NOT NULL OR neg.person_id IS NOT NULL)
<if test="query != null and query.name != null and query.name != ''"> <if test="query != null and query.name != null and query.name != ''">
AND s.name LIKE CONCAT('%', #{query.name}, '%') AND COALESCE(debt_agg.person_name, neg.person_name) LIKE CONCAT('%', #{query.name}, '%')
</if>
<if test="query != null and query.staffId != null and query.staffId != ''">
AND CAST(s.staff_id AS CHAR) = #{query.staffId}
</if> </if>
<if test="query != null and query.idCard != null and query.idCard != ''"> <if test="query != null and query.idCard != null and query.idCard != ''">
AND s.id_card LIKE CONCAT('%', #{query.idCard}, '%') AND credit_keys.person_id LIKE CONCAT('%', #{query.idCard}, '%')
</if> </if>
</where> </where>
ORDER BY debt_agg.query_date DESC, s.staff_id DESC ORDER BY COALESCE(debt_agg.query_date, neg.query_date) DESC, credit_keys.person_id DESC
</select> </select>
<select id="selectCreditInfoSummaryByPersonId" resultType="com.ruoyi.info.collection.domain.vo.CreditInfoListVO"> <select id="selectCreditInfoSummaryByPersonId" resultType="com.ruoyi.info.collection.domain.vo.CreditInfoListVO">
SELECT SELECT
s.staff_id, COALESCE(debt_agg.person_name, neg.person_name) AS name,
s.name, credit_keys.person_id AS id_card,
s.id_card, COALESCE(debt_agg.query_date, neg.query_date) AS query_date,
d.dept_name,
debt_agg.query_date,
IFNULL(debt_agg.debt_count, 0) AS debt_count, IFNULL(debt_agg.debt_count, 0) AS debt_count,
IFNULL(debt_agg.debt_total_amount, 0) AS debt_total_amount, IFNULL(debt_agg.debt_total_amount, 0) AS debt_total_amount,
IFNULL(neg.civil_cnt, 0) AS civil_cnt, IFNULL(neg.civil_cnt, 0) AS civil_cnt,
IFNULL(neg.enforce_cnt, 0) AS enforce_cnt, IFNULL(neg.enforce_cnt, 0) AS enforce_cnt,
IFNULL(neg.adm_cnt, 0) AS adm_cnt IFNULL(neg.adm_cnt, 0) AS adm_cnt
FROM ccdi_base_staff s FROM (
LEFT JOIN sys_dept d ON s.dept_id = d.dept_id SELECT person_id
FROM ccdi_debts_info
GROUP BY person_id
UNION
SELECT person_id
FROM ccdi_credit_negative_info
GROUP BY person_id
) credit_keys
LEFT JOIN ( LEFT JOIN (
SELECT SELECT
person_id, person_id,
MAX(person_name) AS person_name,
MAX(query_date) AS query_date, MAX(query_date) AS query_date,
COUNT(*) AS debt_count, COUNT(*) AS debt_count,
SUM(debt_total_amount) AS debt_total_amount SUM(debt_total_amount) AS debt_total_amount
FROM ccdi_debts_info FROM ccdi_debts_info
GROUP BY person_id GROUP BY person_id
) debt_agg ON debt_agg.person_id = s.id_card ) debt_agg ON debt_agg.person_id = credit_keys.person_id
LEFT JOIN ccdi_credit_negative_info neg ON neg.person_id = s.id_card LEFT JOIN ccdi_credit_negative_info neg ON neg.person_id = credit_keys.person_id
WHERE s.id_card = #{personId} WHERE credit_keys.person_id = #{personId}
LIMIT 1 LIMIT 1
</select> </select>

View File

@@ -34,12 +34,15 @@ class CcdiCreditInfoControllerTest {
request.addParameter("pageNum", "1"); request.addParameter("pageNum", "1");
request.addParameter("pageSize", "10"); request.addParameter("pageSize", "10");
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request)); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
when(service.selectCreditInfoPage(any(), any())).thenReturn(new Page<CreditInfoListVO>(1, 10, 0)); try {
when(service.selectCreditInfoPage(any(), any())).thenReturn(new Page<CreditInfoListVO>(1, 10, 0));
TableDataInfo result = controller.list(new CcdiCreditInfoQueryDTO()); TableDataInfo result = controller.list(new CcdiCreditInfoQueryDTO());
assertEquals(0L, result.getTotal()); assertEquals(0L, result.getTotal());
RequestContextHolder.resetRequestAttributes(); } finally {
RequestContextHolder.resetRequestAttributes();
}
} }
@Test @Test

View File

@@ -12,13 +12,19 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class CcdiCreditInfoQueryMapperXmlTest { class CcdiCreditInfoQueryMapperXmlTest {
@Test @Test
void selectCreditInfoPage_shouldOnlyQueryMaintainedCreditInfo() throws Exception { void selectCreditInfoPage_shouldAggregateByCreditObject() throws Exception {
Path xmlPath = Path.of("src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml"); Path xmlPath = Path.of("src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml");
String source = Files.readString(xmlPath, StandardCharsets.UTF_8); String source = Files.readString(xmlPath, StandardCharsets.UTF_8);
assertTrue(source.contains("AND (debt_agg.person_id IS NOT NULL OR neg.person_id IS NOT NULL)"), assertFalse(source.contains("FROM ccdi_base_staff"),
"征信维护列表必须默认只返回已维护征信的员工"); "征信维护列表不应再从员工表驱动查询");
assertFalse(source.contains("query.maintained == '0'"), assertFalse(source.contains("query.staffId"),
"征信维护列表不应再支持未维护员工列表"); "征信维护列表不应再接收柜员号筛选");
assertFalse(source.contains("query.maintained"),
"征信维护列表不应再接收是否已维护筛选");
assertFalse(source.contains(") keys"),
"征信维护列表不应使用 MySQL 保留字作为派生表别名");
assertTrue(source.contains("person_id"),
"征信维护列表必须按征信对象证件号聚合");
} }
} }

View File

@@ -1,10 +1,8 @@
package com.ruoyi.info.collection.service; package com.ruoyi.info.collection.service;
import com.ruoyi.info.collection.domain.CcdiBaseStaff;
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo; import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
import com.ruoyi.info.collection.domain.CcdiDebtsInfo; import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO; import com.ruoyi.info.collection.domain.vo.CreditInfoUploadResultVO;
import com.ruoyi.info.collection.mapper.CcdiBaseStaffMapper;
import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper; import com.ruoyi.info.collection.mapper.CcdiCreditInfoQueryMapper;
import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper; import com.ruoyi.info.collection.mapper.CcdiCreditNegativeInfoMapper;
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper; import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
@@ -24,7 +22,6 @@ import java.io.File;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -47,9 +44,6 @@ class CcdiCreditInfoServiceImplTest {
@Mock @Mock
private CreditInfoPayloadAssembler assembler; private CreditInfoPayloadAssembler assembler;
@Mock
private CcdiBaseStaffMapper baseStaffMapper;
@Mock @Mock
private CcdiDebtsInfoMapper debtsInfoMapper; private CcdiDebtsInfoMapper debtsInfoMapper;
@@ -60,28 +54,23 @@ class CcdiCreditInfoServiceImplTest {
private CcdiCreditInfoQueryMapper queryMapper; private CcdiCreditInfoQueryMapper queryMapper;
@Test @Test
void uploadHtmlFiles_shouldIsolateFailuresPerEmployee() { void uploadHtmlFiles_shouldStoreCreditObjectWithoutStaffBinding() {
MockMultipartFile successFile = new MockMultipartFile("files", "a.html", "text/html", "<html>a</html>".getBytes(StandardCharsets.UTF_8)); MockMultipartFile file = new MockMultipartFile(
MockMultipartFile failFile = new MockMultipartFile("files", "b.html", "text/html", "<html>b</html>".getBytes(StandardCharsets.UTF_8)); "files", "family.html", "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));
when(creditParseClient.parse(anyString(), anyString(), any(File.class))) when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03")) .thenReturn(successResponse("330101199202020022", "李四", "2026-03-24"));
.thenThrow(new RuntimeException("征信解析失败"));
when(baseStaffMapper.selectOne(any())).thenReturn(baseStaff("330101199001010011", "张三"));
when(assembler.buildDebts(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class))) when(assembler.buildDebts(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
.thenReturn(List.of(buildDebt())); .thenReturn(List.of(buildDebt("330101199202020022")));
when(assembler.buildNegative(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class))) when(assembler.buildNegative(anyString(), anyString(), any(LocalDate.class), any(CreditParsePayload.class)))
.thenReturn(buildNegative()); .thenReturn(buildNegative("330101199202020022"));
CreditInfoUploadResultVO result = service.upload(Arrays.asList(successFile, failFile)); CreditInfoUploadResultVO result = service.upload(List.of(file));
assertEquals(1, result.getSuccessCount()); assertEquals(1, result.getSuccessCount());
assertEquals(1, result.getFailureCount()); assertEquals(0, result.getFailureCount());
assertEquals("征信解析失败", result.getFailures().get(0).getReason()); verify(debtsInfoMapper).deleteByPersonId("330101199202020022");
verify(debtsInfoMapper).deleteByPersonId("330101199001010011"); verify(negativeInfoMapper).deleteByPersonId("330101199202020022");
verify(negativeInfoMapper).deleteByPersonId("330101199001010011");
verify(debtsInfoMapper).insertBatch(any());
verify(negativeInfoMapper).insert(any(CcdiCreditNegativeInfo.class));
} }
@Test @Test
@@ -90,7 +79,6 @@ class CcdiCreditInfoServiceImplTest {
when(creditParseClient.parse(anyString(), anyString(), any(File.class))) when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03")); .thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
when(baseStaffMapper.selectOne(any())).thenReturn(baseStaff("330101199001010011", "张三"));
when(queryMapper.selectLatestQueryDate("330101199001010011")) when(queryMapper.selectLatestQueryDate("330101199001010011"))
.thenReturn(LocalDate.parse("2026-03-05")); .thenReturn(LocalDate.parse("2026-03-05"));
@@ -116,23 +104,16 @@ class CcdiCreditInfoServiceImplTest {
return response; return response;
} }
private CcdiBaseStaff baseStaff(String idCard, String name) { private CcdiDebtsInfo buildDebt(String personId) {
CcdiBaseStaff staff = new CcdiBaseStaff();
staff.setIdCard(idCard);
staff.setName(name);
return staff;
}
private CcdiDebtsInfo buildDebt() {
CcdiDebtsInfo debt = new CcdiDebtsInfo(); CcdiDebtsInfo debt = new CcdiDebtsInfo();
debt.setPersonId("330101199001010011"); debt.setPersonId(personId);
debt.setDebtTotalAmount(new BigDecimal("100")); debt.setDebtTotalAmount(new BigDecimal("100"));
return debt; return debt;
} }
private CcdiCreditNegativeInfo buildNegative() { private CcdiCreditNegativeInfo buildNegative(String personId) {
CcdiCreditNegativeInfo info = new CcdiCreditNegativeInfo(); CcdiCreditNegativeInfo info = new CcdiCreditNegativeInfo();
info.setPersonId("330101199001010011"); info.setPersonId(personId);
return info; return info;
} }
} }

View File

@@ -0,0 +1,73 @@
# 征信维护后端直接入库实施记录
## 1. 实施范围
- 实施目标:将征信维护后端切换为征信对象维度,上传时不再校验员工归属,解析成功后按 `person_id` 直接覆盖入库,列表查询改为征信表聚合
- 实施目录:`ccdi-info-collection`
- 实施日期2026-03-24
## 2. 实际修改文件
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/dto/CcdiCreditInfoQueryDTO.java`
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/vo/CreditInfoListVO.java`
- `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/impl/CcdiCreditInfoServiceImpl.java`
- `ccdi-info-collection/src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml`
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/service/CcdiCreditInfoServiceImplTest.java`
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/mapper/CcdiCreditInfoQueryMapperXmlTest.java`
- `ccdi-info-collection/src/test/java/com/ruoyi/info/collection/controller/CcdiCreditInfoControllerTest.java`
## 3. 功能落地说明
### 3.1 直接入库口径
- 上传解析后直接读取 `query_cert_no``query_cust_name``report_time`
- 删除员工主数据依赖,不再校验征信对象是否存在于 `ccdi_base_staff`
- 保留“同一证件号仅允许最新征信覆盖写入”的日期校验
- 继续沿用 `replaceEmployeeCredit` 方法完成按 `person_id` 的先删后插覆盖写入
### 3.2 DTO / VO 收敛
- `CcdiCreditInfoQueryDTO` 仅保留 `name``idCard`
- `CreditInfoListVO` 删除 `staffId``deptName`,仅保留征信对象摘要字段
### 3.3 SQL 聚合调整
- 列表查询改为以征信业务表为主集合,覆盖“只有负债”“只有负面信息”“两者都有”三类数据
- 列表与摘要查询均通过 `person_id` 聚合,不再关联 `ccdi_base_staff``sys_dept`
- 查询条件仅保留姓名、身份证号两项筛选
## 4. 验证记录
### 4.1 失败验证
执行命令:
```bash
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoServiceImplTest,CcdiCreditInfoQueryMapperXmlTest,CcdiCreditInfoControllerTest test
```
执行结果:
- 首轮失败点与计划一致集中暴露为员工校验未删除、XML 仍依赖员工表
### 4.2 分段验证
执行命令:
```bash
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoControllerTest test
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoServiceImplTest test
mvn -pl ccdi-info-collection -Dtest=CcdiCreditInfoQueryMapperXmlTest test
```
执行结果:
- 三组测试均通过
- 控制层分页委派契约保持不变
- 非员工征信对象可成功上传,旧日期上传仍被拦截
- XML 契约确认已去掉 `ccdi_base_staff``query.staffId``query.maintained`
## 5. 进程与环境说明
- 本次仅执行 Maven 单元测试,未启动本地后端服务
- 无残留测试进程需要清理

View File

@@ -0,0 +1,84 @@
# 征信维护前端直接入库实施记录
## 1. 实施范围
- 实施目标:将征信维护页面切换为征信对象维度展示,移除“柜员号”“是否已维护”旧口径字段,并保持上传、列表、详情、删除交互可用
- 实施目录:`ruoyi-ui`
- 实施日期2026-03-24
## 2. 实际修改文件
- `ruoyi-ui/src/views/ccdiCreditInfo/index.vue`
- `ruoyi-ui/tests/unit/credit-info-page-layout.test.js`
- `ruoyi-ui/tests/unit/credit-info-maintained-filter.test.js`
- `ruoyi-ui/tests/unit/credit-info-detail-ui.test.js`
## 3. 功能落地说明
### 3.1 查询区与列表区收敛
- 查询区仅保留“姓名”“身份证号”
- `queryParams` 与重置逻辑删除 `staffId``maintained`
- 列表区移除“柜员号”列,展示顺序调整为征信对象摘要字段
### 3.2 删除提示文案调整
- 删除确认文案从“该员工当前已维护的征信信息”改为“该征信对象当前已维护的征信信息”
### 3.3 数据流校验
- `@/api/ccdiCreditInfo` 中上传、列表、详情、删除接口路径保持不变
- 上传弹窗与详情弹窗交互保持可用,未引入额外页面或路由改动
## 4. 验证记录
### 4.1 失败验证
执行命令:
```bash
node ruoyi-ui/tests/unit/credit-info-page-layout.test.js
node ruoyi-ui/tests/unit/credit-info-maintained-filter.test.js
node ruoyi-ui/tests/unit/credit-info-detail-ui.test.js
```
执行结果:
- 首轮失败点与计划一致,页面仍包含 `staffId``maintained` 和旧删除文案
### 4.2 页面与契约验证
执行命令:
```bash
node ruoyi-ui/tests/unit/credit-info-page-layout.test.js
node ruoyi-ui/tests/unit/credit-info-maintained-filter.test.js
node ruoyi-ui/tests/unit/credit-info-detail-ui.test.js
node ruoyi-ui/tests/unit/credit-info-api-contract.test.js
node ruoyi-ui/tests/unit/credit-info-upload-ui.test.js
```
执行结果:
- 五个前端静态/契约测试全部通过
- 页面已彻底移除员工口径筛选项与列表列
- API 路径和上传交互契约未回退
### 4.3 前端构建
执行命令:
```bash
cd ruoyi-ui
npm run build:prod
```
执行结果:
- 构建通过,`dist/` 产物正常生成
- 仅出现项目既有的包体积告警,未出现新增编译错误
## 5. 进程与环境说明
- 本次未启动本地前端 dev server
- 无残留测试进程需要清理

View File

@@ -12,15 +12,6 @@
@keyup.enter.native="handleQuery" @keyup.enter.native="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="柜员号">
<el-input
v-model="queryParams.staffId"
placeholder="请输入柜员号"
clearable
style="width: 240px"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="身份证号"> <el-form-item label="身份证号">
<el-input <el-input
v-model="queryParams.idCard" v-model="queryParams.idCard"
@@ -30,11 +21,6 @@
@keyup.enter.native="handleQuery" @keyup.enter.native="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="是否已维护">
<el-select v-model="queryParams.maintained" placeholder="仅展示已维护数据" style="width: 240px">
<el-option label="已维护" value="1" />
</el-select>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -50,7 +36,6 @@
<el-table :data="creditInfoList" v-loading="loading"> <el-table :data="creditInfoList" v-loading="loading">
<el-table-column label="姓名" prop="name" align="center" /> <el-table-column label="姓名" prop="name" align="center" />
<el-table-column label="柜员号" prop="staffId" align="center" />
<el-table-column label="身份证号" prop="idCard" align="center" /> <el-table-column label="身份证号" prop="idCard" align="center" />
<el-table-column label="最近征信查询日期" align="center" width="160"> <el-table-column label="最近征信查询日期" align="center" width="160">
<template slot-scope="scope"> <template slot-scope="scope">
@@ -208,9 +193,7 @@ export default {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
name: undefined, name: undefined,
staffId: undefined,
idCard: undefined, idCard: undefined,
maintained: "1",
}, },
}; };
}, },
@@ -240,9 +223,7 @@ export default {
pageNum: 1, pageNum: 1,
pageSize: 10, pageSize: 10,
name: undefined, name: undefined,
staffId: undefined,
idCard: undefined, idCard: undefined,
maintained: "1",
}; };
this.resetForm("queryForm"); this.resetForm("queryForm");
this.handleQuery(); this.handleQuery();
@@ -321,7 +302,7 @@ export default {
handleDelete(row) { handleDelete(row) {
const personId = row.idCard; const personId = row.idCard;
this.$modal this.$modal
.confirm("确认删除该员工当前已维护的征信信息吗?") .confirm("确认删除该征信对象当前已维护的征信信息吗?")
.then(() => { .then(() => {
return deleteCreditInfo(personId); return deleteCreditInfo(personId);
}) })

View File

@@ -19,7 +19,7 @@ const source = fs.readFileSync(componentPath, "utf8");
"handleDetail", "handleDetail",
"handleDelete", "handleDelete",
"deleteCreditInfo", "deleteCreditInfo",
"确认删除该员工当前已维护的征信信息吗?", "确认删除该征信对象当前已维护的征信信息吗?",
].forEach((token) => { ].forEach((token) => {
assert(source.includes(token), `详情或删除交互缺少关键结构: ${token}`); assert(source.includes(token), `详情或删除交互缺少关键结构: ${token}`);
}); });

View File

@@ -8,14 +8,13 @@ const componentPath = path.resolve(
); );
const source = fs.readFileSync(componentPath, "utf8"); const source = fs.readFileSync(componentPath, "utf8");
assert( assert(!source.includes("maintained:"), "征信维护页面不应再维护 maintained 查询参数");
source.includes('maintained: "1"'),
"征信维护页面应默认只查询已维护数据"
);
assert( assert(
!source.includes('label="维护"'), !source.includes('label="是否已维护"'),
"征信维护页面不应再提供未维护筛选项" "征信维护页面不应再展示是否已维护筛选项"
); );
assert(!source.includes('value="1"'), "征信维护页面不应再内置已维护默认筛选");
console.log("credit-info-maintained-filter test passed"); console.log("credit-info-maintained-filter test passed");

View File

@@ -13,6 +13,8 @@ const source = fs.readFileSync(componentPath, "utf8");
[ [
"征信维护", "征信维护",
"姓名",
"身份证号",
"批量上传征信HTML", "批量上传征信HTML",
"最近征信查询日期", "最近征信查询日期",
"负债笔数", "负债笔数",
@@ -22,9 +24,11 @@ const source = fs.readFileSync(componentPath, "utf8");
"行政处罚笔数", "行政处罚笔数",
"详情", "详情",
"删除", "删除",
"@/api/ccdiCreditInfo",
].forEach((token) => { ].forEach((token) => {
assert(source.includes(token), `征信维护页面缺少关键结构: ${token}`); assert(source.includes(token), `征信维护页面缺少关键结构: ${token}`);
}); });
assert(!source.includes('label="柜员号"'), "征信维护页面不应再展示柜员号查询项");
assert(!source.includes('prop="staffId"'), "征信维护列表不应再展示柜员号列");
console.log("credit-info-page-layout test passed"); console.log("credit-info-page-layout test passed");