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

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 String name;
private String staffId;
private String idCard;
private String maintained;
}

View File

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

View File

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

View File

@@ -6,68 +6,76 @@
<select id="selectCreditInfoPage" resultType="com.ruoyi.info.collection.domain.vo.CreditInfoListVO">
SELECT
s.staff_id,
s.name,
s.id_card,
d.dept_name,
debt_agg.query_date,
COALESCE(debt_agg.person_name, neg.person_name) AS name,
credit_keys.person_id AS id_card,
COALESCE(debt_agg.query_date, neg.query_date) AS query_date,
IFNULL(debt_agg.debt_count, 0) AS debt_count,
IFNULL(debt_agg.debt_total_amount, 0) AS debt_total_amount,
IFNULL(neg.civil_cnt, 0) AS civil_cnt,
IFNULL(neg.enforce_cnt, 0) AS enforce_cnt,
IFNULL(neg.adm_cnt, 0) AS adm_cnt
FROM ccdi_base_staff s
LEFT JOIN sys_dept d ON s.dept_id = d.dept_id
FROM (
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 (
SELECT
person_id,
MAX(person_name) AS person_name,
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
) debt_agg ON debt_agg.person_id = s.id_card
LEFT JOIN ccdi_credit_negative_info neg ON neg.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 = credit_keys.person_id
<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 != ''">
AND s.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}
AND COALESCE(debt_agg.person_name, neg.person_name) LIKE CONCAT('%', #{query.name}, '%')
</if>
<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>
</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 id="selectCreditInfoSummaryByPersonId" resultType="com.ruoyi.info.collection.domain.vo.CreditInfoListVO">
SELECT
s.staff_id,
s.name,
s.id_card,
d.dept_name,
debt_agg.query_date,
COALESCE(debt_agg.person_name, neg.person_name) AS name,
credit_keys.person_id AS id_card,
COALESCE(debt_agg.query_date, neg.query_date) AS query_date,
IFNULL(debt_agg.debt_count, 0) AS debt_count,
IFNULL(debt_agg.debt_total_amount, 0) AS debt_total_amount,
IFNULL(neg.civil_cnt, 0) AS civil_cnt,
IFNULL(neg.enforce_cnt, 0) AS enforce_cnt,
IFNULL(neg.adm_cnt, 0) AS adm_cnt
FROM ccdi_base_staff s
LEFT JOIN sys_dept d ON s.dept_id = d.dept_id
FROM (
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 (
SELECT
person_id,
MAX(person_name) AS person_name,
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
) debt_agg ON debt_agg.person_id = s.id_card
LEFT JOIN ccdi_credit_negative_info neg ON neg.person_id = s.id_card
WHERE s.id_card = #{personId}
) debt_agg ON debt_agg.person_id = credit_keys.person_id
LEFT JOIN ccdi_credit_negative_info neg ON neg.person_id = credit_keys.person_id
WHERE credit_keys.person_id = #{personId}
LIMIT 1
</select>

View File

@@ -34,12 +34,15 @@ class CcdiCreditInfoControllerTest {
request.addParameter("pageNum", "1");
request.addParameter("pageSize", "10");
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());
RequestContextHolder.resetRequestAttributes();
assertEquals(0L, result.getTotal());
} finally {
RequestContextHolder.resetRequestAttributes();
}
}
@Test

View File

@@ -12,13 +12,19 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
class CcdiCreditInfoQueryMapperXmlTest {
@Test
void selectCreditInfoPage_shouldOnlyQueryMaintainedCreditInfo() throws Exception {
void selectCreditInfoPage_shouldAggregateByCreditObject() throws Exception {
Path xmlPath = Path.of("src/main/resources/mapper/info/collection/CcdiCreditInfoQueryMapper.xml");
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("query.maintained == '0'"),
"征信维护列表不应再支持未维护员工列表");
assertFalse(source.contains("FROM ccdi_base_staff"),
"征信维护列表不应再从员工表驱动查询");
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;
import com.ruoyi.info.collection.domain.CcdiBaseStaff;
import com.ruoyi.info.collection.domain.CcdiCreditNegativeInfo;
import com.ruoyi.info.collection.domain.CcdiDebtsInfo;
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.CcdiCreditNegativeInfoMapper;
import com.ruoyi.info.collection.mapper.CcdiDebtsInfoMapper;
@@ -24,7 +22,6 @@ import java.io.File;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -47,9 +44,6 @@ class CcdiCreditInfoServiceImplTest {
@Mock
private CreditInfoPayloadAssembler assembler;
@Mock
private CcdiBaseStaffMapper baseStaffMapper;
@Mock
private CcdiDebtsInfoMapper debtsInfoMapper;
@@ -60,28 +54,23 @@ class CcdiCreditInfoServiceImplTest {
private CcdiCreditInfoQueryMapper queryMapper;
@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));
void uploadHtmlFiles_shouldStoreCreditObjectWithoutStaffBinding() {
MockMultipartFile file = new MockMultipartFile(
"files", "family.html", "text/html", "<html>ok</html>".getBytes(StandardCharsets.UTF_8));
when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"))
.thenThrow(new RuntimeException("征信解析失败"));
when(baseStaffMapper.selectOne(any())).thenReturn(baseStaff("330101199001010011", "张三"));
.thenReturn(successResponse("330101199202020022", "李四", "2026-03-24"));
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)))
.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.getFailureCount());
assertEquals("征信解析失败", result.getFailures().get(0).getReason());
verify(debtsInfoMapper).deleteByPersonId("330101199001010011");
verify(negativeInfoMapper).deleteByPersonId("330101199001010011");
verify(debtsInfoMapper).insertBatch(any());
verify(negativeInfoMapper).insert(any(CcdiCreditNegativeInfo.class));
assertEquals(0, result.getFailureCount());
verify(debtsInfoMapper).deleteByPersonId("330101199202020022");
verify(negativeInfoMapper).deleteByPersonId("330101199202020022");
}
@Test
@@ -90,7 +79,6 @@ class CcdiCreditInfoServiceImplTest {
when(creditParseClient.parse(anyString(), anyString(), any(File.class)))
.thenReturn(successResponse("330101199001010011", "张三", "2026-03-03"));
when(baseStaffMapper.selectOne(any())).thenReturn(baseStaff("330101199001010011", "张三"));
when(queryMapper.selectLatestQueryDate("330101199001010011"))
.thenReturn(LocalDate.parse("2026-03-05"));
@@ -116,23 +104,16 @@ class CcdiCreditInfoServiceImplTest {
return response;
}
private CcdiBaseStaff baseStaff(String idCard, String name) {
CcdiBaseStaff staff = new CcdiBaseStaff();
staff.setIdCard(idCard);
staff.setName(name);
return staff;
}
private CcdiDebtsInfo buildDebt() {
private CcdiDebtsInfo buildDebt(String personId) {
CcdiDebtsInfo debt = new CcdiDebtsInfo();
debt.setPersonId("330101199001010011");
debt.setPersonId(personId);
debt.setDebtTotalAmount(new BigDecimal("100"));
return debt;
}
private CcdiCreditNegativeInfo buildNegative() {
private CcdiCreditNegativeInfo buildNegative(String personId) {
CcdiCreditNegativeInfo info = new CcdiCreditNegativeInfo();
info.setPersonId("330101199001010011");
info.setPersonId(personId);
return info;
}
}