From 0ea504f6b3dea791a27dce5563af69396620ecf8 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Tue, 26 May 2026 17:18:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E5=8F=8C=E5=91=98=E5=B7=A5?= =?UTF-8?q?=E5=A4=AB=E5=A6=BB=E5=AE=B6=E5=BA=AD=E4=B8=93=E9=A1=B9=E6=A0=B8?= =?UTF-8?q?=E6=9F=A5=E5=8F=A3=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/CcdiProjectSpecialCheckMapper.xml | 485 ++++++++++-------- ...rojectSpecialCheckMapperDetailSqlTest.java | 12 + ...iProjectSpecialCheckMapperListSqlTest.java | 14 + ...ouble-staff-spouse-family-special-check.md | 57 ++ 4 files changed, 364 insertions(+), 204 deletions(-) create mode 100644 docs/reports/implementation/2026-05-26-double-staff-spouse-family-special-check.md diff --git a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml index 9b286cb1..22377b6b 100644 --- a/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml +++ b/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml @@ -82,7 +82,7 @@ @@ -93,7 +93,7 @@ @@ -144,15 +144,39 @@ select - person_id, - max(relation_name) as spouse_name, - min(relation_cert_no) as spouse_id_card, - max(annual_income) as spouse_income - from ccdi_staff_fmy_relation - where status = 1 - and is_emp_family = 1 - and relation_type = '配偶' - group by person_id + relation_pair.person_id, + max(relation_pair.spouse_name) as spouse_name, + min(relation_pair.spouse_id_card) as spouse_id_card, + max(coalesce(spouse_staff.annual_income, relation_pair.spouse_relation_income, 0)) as spouse_income, + max(case when spouse_staff.id_card is not null then 1 else 0 end) as spouse_is_staff + from ( + select + relation.person_id, + relation.relation_name as spouse_name, + relation.relation_cert_no as spouse_id_card, + relation.annual_income as spouse_relation_income + from ccdi_staff_fmy_relation relation + where relation.status = 1 + and relation.is_emp_family = 1 + and relation.relation_type = '配偶' + union all + select + relation.relation_cert_no as person_id, + base_staff.name as spouse_name, + relation.person_id as spouse_id_card, + null as spouse_relation_income + from ccdi_staff_fmy_relation relation + inner join ccdi_base_staff current_staff + on current_staff.id_card = relation.relation_cert_no + left join ccdi_base_staff base_staff + on base_staff.id_card = relation.person_id + where relation.status = 1 + and relation.is_emp_family = 1 + and relation.relation_type = '配偶' + ) relation_pair + left join ccdi_base_staff spouse_staff + on spouse_staff.id_card = relation_pair.spouse_id_card + group by relation_pair.person_id @@ -306,6 +314,7 @@ aggregated.project_id, aggregated.staff_id_card, aggregated.spouse_id_card, + aggregated.spouse_is_staff, aggregated.staff_code, aggregated.staff_name, aggregated.dept_name, @@ -344,89 +353,136 @@ end as summary_risk_level_name from ( select - #{projectId} as project_id, - scope.staff_id_card, - scope.staff_code, - scope.staff_name, - scope.dept_name, - spouse.spouse_id_card, - coalesce(base_staff.annual_income, 0) as self_income, - coalesce(spouse.spouse_income, 0) as spouse_income, - coalesce(base_staff.annual_income, 0) + coalesce(spouse.spouse_income, 0) as total_income, + source.*, case - when coalesce(( + when source.self_asset_record_count = 0 + or source.spouse_staff_asset_record_count = 0 then 1 + else 0 + end as missing_self_asset_info, + case + when source.self_debt_record_count = 0 + or source.spouse_staff_debt_record_count = 0 then 1 + else 0 + end as missing_self_debt_info + from ( + select + #{projectId} as project_id, + scope.staff_id_card, + scope.staff_code, + scope.staff_name, + scope.dept_name, + spouse.spouse_id_card, + spouse.spouse_is_staff, + coalesce(base_staff.annual_income, 0) as self_income, + coalesce(spouse.spouse_income, 0) as spouse_income, + coalesce(base_staff.annual_income, 0) + coalesce(spouse.spouse_income, 0) as total_income, + coalesce(( select count(1) from ccdi_asset_info asset where asset.family_id = scope.staff_id_card and asset.person_id = scope.staff_id_card - ), 0) = 0 then 1 - else 0 - end as missing_self_asset_info, - coalesce(( - select sum(coalesce(asset.current_value, 0)) - from ccdi_asset_info asset - where asset.family_id = scope.staff_id_card - and asset.person_id = scope.staff_id_card - ), 0) as self_total_asset, - coalesce(( - select sum(coalesce(asset.current_value, 0)) - from ccdi_asset_info asset - where asset.family_id = scope.staff_id_card - and spouse.spouse_id_card is not null - and asset.person_id = spouse.spouse_id_card - ), 0) as spouse_total_asset, - coalesce(( - select sum(coalesce(asset.current_value, 0)) - from ccdi_asset_info asset - where asset.family_id = scope.staff_id_card - and ( - asset.person_id = scope.staff_id_card - or (spouse.spouse_id_card is not null and asset.person_id = spouse.spouse_id_card) - ) - ), 0) as total_asset, - case - when coalesce(( + ), 0) as self_asset_record_count, + case + when spouse.spouse_is_staff = 1 then coalesce(( + select count(1) + from ccdi_asset_info asset + where asset.family_id = spouse.spouse_id_card + and asset.person_id = spouse.spouse_id_card + ), 0) + else 1 + end as spouse_staff_asset_record_count, + coalesce(( + select sum(coalesce(asset.current_value, 0)) + from ccdi_asset_info asset + where asset.family_id = scope.staff_id_card + and asset.person_id = scope.staff_id_card + ), 0) as self_total_asset, + coalesce(( + select sum(coalesce(asset.current_value, 0)) + from ccdi_asset_info asset + where spouse.spouse_id_card is not null + and ( + ( + spouse.spouse_is_staff = 1 + and asset.family_id = spouse.spouse_id_card + and asset.person_id = spouse.spouse_id_card + ) + or ( + (spouse.spouse_is_staff is null or spouse.spouse_is_staff != 1) + and asset.family_id = scope.staff_id_card + and asset.person_id = spouse.spouse_id_card + ) + ) + ), 0) as spouse_total_asset, + coalesce(( + select sum(coalesce(asset.current_value, 0)) + from ccdi_asset_info asset + where (asset.family_id = scope.staff_id_card and asset.person_id = scope.staff_id_card) + or ( + spouse.spouse_id_card is not null + and ( + ( + spouse.spouse_is_staff = 1 + and asset.family_id = spouse.spouse_id_card + and asset.person_id = spouse.spouse_id_card + ) + or ( + (spouse.spouse_is_staff is null or spouse.spouse_is_staff != 1) + and asset.family_id = scope.staff_id_card + and asset.person_id = spouse.spouse_id_card + ) + ) + ) + ), 0) as total_asset, + coalesce(( select count(1) from ccdi_debts_info debt where debt.person_id = scope.staff_id_card - ), 0) = 0 then 1 - else 0 - end as missing_self_debt_info, - coalesce(( - select sum(coalesce(debt.principal_balance, 0)) - from ccdi_debts_info debt - where debt.person_id = scope.staff_id_card - ), 0) as self_total_debt, - coalesce(( - select sum(coalesce(debt.principal_balance, 0)) - from ccdi_debts_info debt - where spouse.spouse_id_card is not null - and debt.person_id = spouse.spouse_id_card - ), 0) as spouse_total_debt, - coalesce(( - select sum(coalesce(debt.principal_balance, 0)) - from ccdi_debts_info debt - where debt.person_id = scope.staff_id_card - or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card) - ), 0) as total_debt, - coalesce(base_staff.annual_income, 0) - + coalesce(spouse.spouse_income, 0) - + coalesce(( + ), 0) as self_debt_record_count, + case + when spouse.spouse_is_staff = 1 then coalesce(( + select count(1) + from ccdi_debts_info debt + where debt.person_id = spouse.spouse_id_card + ), 0) + else 1 + end as spouse_staff_debt_record_count, + coalesce(( + select sum(coalesce(debt.principal_balance, 0)) + from ccdi_debts_info debt + where debt.person_id = scope.staff_id_card + ), 0) as self_total_debt, + coalesce(( + select sum(coalesce(debt.principal_balance, 0)) + from ccdi_debts_info debt + where spouse.spouse_id_card is not null + and debt.person_id = spouse.spouse_id_card + ), 0) as spouse_total_debt, + coalesce(( select sum(coalesce(debt.principal_balance, 0)) from ccdi_debts_info debt where debt.person_id = scope.staff_id_card or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card) - ), 0) as comparison_amount - from ( - - ) scope - left join ccdi_base_staff base_staff - on base_staff.id_card = scope.staff_id_card - left join ( - - ) spouse - on spouse.person_id = scope.staff_id_card - where scope.staff_id_card = #{staffIdCard} + ), 0) as total_debt, + coalesce(base_staff.annual_income, 0) + + coalesce(spouse.spouse_income, 0) + + coalesce(( + select sum(coalesce(debt.principal_balance, 0)) + from ccdi_debts_info debt + where debt.person_id = scope.staff_id_card + or (spouse.spouse_id_card is not null and debt.person_id = spouse.spouse_id_card) + ), 0) as comparison_amount + from ( + + ) scope + left join ccdi_base_staff base_staff + on base_staff.id_card = scope.staff_id_card + left join ( + + ) spouse + on spouse.person_id = scope.staff_id_card + where scope.staff_id_card = #{staffIdCard} + ) source ) aggregated @@ -437,7 +493,15 @@ asset.asset_sub_type, case when asset.person_id = #{staffIdCard} then base_staff.name - else spouse.relation_name + else coalesce(holder_staff.name, ( + select max(relation.relation_name) + from ccdi_staff_fmy_relation relation + where relation.person_id = #{staffIdCard} + and relation.relation_cert_no = asset.person_id + and relation.status = 1 + and relation.is_emp_family = 1 + and relation.relation_type = '配偶' + )) end as holder_name, asset.person_id as holder_id_card, asset.current_value, @@ -445,16 +509,24 @@ from ccdi_asset_info asset left join ccdi_base_staff base_staff on base_staff.id_card = #{staffIdCard} - left join ccdi_staff_fmy_relation spouse - on spouse.person_id = #{staffIdCard} - and spouse.status = 1 - and spouse.relation_type = '配偶' - and spouse.relation_cert_no = asset.person_id - where asset.family_id = #{staffIdCard} - and ( - asset.person_id = #{staffIdCard} - or (#{spouseIdCard} is not null and asset.person_id = #{spouseIdCard}) - ) + left join ccdi_base_staff holder_staff + on holder_staff.id_card = asset.person_id + where (asset.family_id = #{staffIdCard} and asset.person_id = #{staffIdCard}) + or ( + #{spouseIdCard} is not null + and ( + ( + #{spouseIsStaff} = 1 + and asset.family_id = #{spouseIdCard} + and asset.person_id = #{spouseIdCard} + ) + or ( + (#{spouseIsStaff} is null or #{spouseIsStaff} != 1) + and asset.family_id = #{staffIdCard} + and asset.person_id = #{spouseIdCard} + ) + ) + ) order by case when asset.person_id = #{staffIdCard} then 1 else 2 end, asset.valuation_date desc, @@ -469,7 +541,15 @@ debt.creditor_type, case when debt.person_id = #{staffIdCard} then base_staff.name - else spouse.relation_name + else coalesce(owner_staff.name, ( + select max(relation.relation_name) + from ccdi_staff_fmy_relation relation + where relation.person_id = #{staffIdCard} + and relation.relation_cert_no = debt.person_id + and relation.status = 1 + and relation.is_emp_family = 1 + and relation.relation_type = '配偶' + )) end as owner_name, debt.person_id as owner_id_card, debt.principal_balance, @@ -477,11 +557,8 @@ from ccdi_debts_info debt left join ccdi_base_staff base_staff on base_staff.id_card = #{staffIdCard} - left join ccdi_staff_fmy_relation spouse - on spouse.person_id = #{staffIdCard} - and spouse.status = 1 - and spouse.relation_type = '配偶' - and spouse.relation_cert_no = debt.person_id + left join ccdi_base_staff owner_staff + on owner_staff.id_card = debt.person_id where debt.person_id = #{staffIdCard} or (#{spouseIdCard} is not null and debt.person_id = #{spouseIdCard}) order by diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperDetailSqlTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperDetailSqlTest.java index c892e87a..923b66dc 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperDetailSqlTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperDetailSqlTest.java @@ -17,6 +17,13 @@ class CcdiProjectSpecialCheckMapperDetailSqlTest { assertTrue(xml.contains("select id=\"selectFamilyAssetItemsByScope\"")); assertTrue(xml.contains("select id=\"selectFamilyDebtItemsByScope\"")); assertTrue(xml.contains("scope.staff_id_card = #{staffIdCard}")); + assertTrue(xml.contains("spouseIsStaff=spouse_is_staff")); + assertTrue(xml.contains("relation.relation_cert_no as person_id")); + assertTrue(xml.contains("spouse_staff.annual_income")); + assertTrue(xml.contains("spouse.spouse_is_staff = 1")); + assertTrue(xml.contains("asset.family_id = spouse.spouse_id_card")); + assertTrue(xml.contains("source.spouse_staff_asset_record_count = 0")); + assertTrue(xml.contains("source.spouse_staff_debt_record_count = 0")); assertTrue(xml.contains("incomeDetail")); assertTrue(xml.contains("assetDetail")); assertTrue(xml.contains("debtDetail")); @@ -31,6 +38,9 @@ class CcdiProjectSpecialCheckMapperDetailSqlTest { assertTrue(xml.contains("asset_main_type")); assertTrue(xml.contains("asset_sub_type")); assertTrue(xml.contains("holder_name")); + assertTrue(xml.contains("holder_staff.name")); + assertTrue(xml.contains("select max(relation.relation_name)")); + assertTrue(xml.contains("relation.relation_cert_no = asset.person_id")); assertTrue(xml.contains("current_value")); assertTrue(xml.contains("valuation_date")); assertTrue(xml.contains("debt_name")); @@ -38,6 +48,8 @@ class CcdiProjectSpecialCheckMapperDetailSqlTest { assertTrue(xml.contains("debt_sub_type")); assertTrue(xml.contains("creditor_type")); assertTrue(xml.contains("owner_name")); + assertTrue(xml.contains("owner_staff.name")); + assertTrue(xml.contains("relation.relation_cert_no = debt.person_id")); assertTrue(xml.contains("principal_balance")); assertTrue(xml.contains("query_date")); assertFalse(xml.contains("ccdi_project_overview_employee_result")); diff --git a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperListSqlTest.java b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperListSqlTest.java index b885b18d..48b85373 100644 --- a/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperListSqlTest.java +++ b/ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperListSqlTest.java @@ -19,11 +19,25 @@ class CcdiProjectSpecialCheckMapperListSqlTest { assertTrue(xml.contains("ccdi_base_staff")); assertTrue(xml.contains("ccdi_staff_fmy_relation")); assertTrue(xml.contains("relation_type = '配偶'")); + assertTrue(xml.contains("union all")); + assertTrue(xml.contains("relation.relation_cert_no as person_id")); + assertTrue(xml.contains("inner join ccdi_base_staff current_staff")); + assertTrue(xml.contains("spouse_staff.annual_income")); + assertTrue(xml.contains("spouse_is_staff")); assertTrue(xml.contains("annual_income")); assertTrue(xml.contains("current_value")); assertTrue(xml.contains("principal_balance")); assertTrue(listSql.contains("self_asset_record_count")); + assertTrue(listSql.contains("spouse_staff_asset_record_count")); assertTrue(listSql.contains("self_debt_record_count")); + assertTrue(listSql.contains("spouse_staff_debt_record_count")); + assertTrue(listSql.contains("source.self_asset_record_count = 0")); + assertTrue(listSql.contains("source.spouse_staff_asset_record_count = 0")); + assertTrue(listSql.contains("source.self_debt_record_count = 0")); + assertTrue(listSql.contains("source.spouse_staff_debt_record_count = 0")); + assertTrue(listSql.contains("asset.family_id = spouse.spouse_id_card")); + assertTrue(listSql.contains("asset.person_id = spouse.spouse_id_card")); + assertTrue(listSql.contains("debt.person_id = spouse.spouse_id_card")); assertTrue(listSql.contains("then 'MISSING_INFO'")); assertTrue(listSql.contains("then '缺少信息'")); assertTrue(listSql.contains("comparison_amount")); diff --git a/docs/reports/implementation/2026-05-26-double-staff-spouse-family-special-check.md b/docs/reports/implementation/2026-05-26-double-staff-spouse-family-special-check.md new file mode 100644 index 00000000..bbc4456b --- /dev/null +++ b/docs/reports/implementation/2026-05-26-double-staff-spouse-family-special-check.md @@ -0,0 +1,57 @@ +# 双员工夫妻家庭专项核查实施记录 + +- 保存路径:`docs/reports/implementation/2026-05-26-double-staff-spouse-family-special-check.md` +- 实施日期:2026-05-26 +- 变更范围:后端专项核查家庭资产负债聚合 SQL 与对应 Mapper SQL 结构测试 + +## 修改内容 + +1. 调整 `CcdiProjectSpecialCheckMapper.xml` 的配偶识别口径: + - 支持 `本人 -> 配偶` 的直接关系。 + - 支持 `配偶员工 -> 本人` 的反向推导,覆盖只维护单向配偶关系但双方都是员工的情况。 + - 增加 `spouse_is_staff` 标识,配偶为员工时收入优先取 `ccdi_base_staff.annual_income`。 + +2. 调整家庭资产、负债与缺少信息口径: + - 双员工夫妻家庭中,本人资产取 `family_id = 本人身份证号 AND person_id = 本人身份证号`。 + - 配偶为员工时,配偶资产取 `family_id = 配偶身份证号 AND person_id = 配偶身份证号`。 + - 配偶不是员工时,配偶资产仍取 `family_id = 本人身份证号 AND person_id = 配偶身份证号`。 + - 负债继续按本人和配偶证件号从 `ccdi_debts_info` 汇总。 + - 配偶为员工时,任一公司员工成员缺少本人资产或本人负债记录,风险结果按“缺少信息”处理。 + +3. 调整详情明细展示查询: + - 资产明细按 `spouse_is_staff` 选择员工本人资产或亲属资产,避免把员工配偶作为亲属资产重复计入。 + - 资产持有人和负债归属人名称优先取员工主档,非员工配偶再从亲属关系中取名,避免双向配偶关系导致明细行重复。 + +4. 补充 Mapper SQL 结构测试断言: + - 校验双向配偶识别、员工配偶收入来源、员工配偶资产归属、员工配偶负债缺失判断和明细查询参数传递。 + +## 影响范围 + +- 接口路径和返回结构不变: + - `GET /ccdi/project/special-check/family-asset-liability/list` + - `GET /ccdi/project/special-check/family-asset-liability/detail` +- 前端页面无需调整,仍按员工展示列表。 +- 不新增数据库表和字段。 + +## 验证结果 + +1. 主代码编译: + - 命令:`mvn -DskipTests compile` + - 目录:`ccdi-project` + - 结果:通过。 + +2. Mapper XML 格式校验: + - 命令:`xmllint --noout ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml` + - 结果:通过。 + +3. 本次变更空白检查: + - 命令:`git diff --check -- ccdi-project/src/main/resources/mapper/ccdi/project/CcdiProjectSpecialCheckMapper.xml ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperListSqlTest.java ccdi-project/src/test/java/com/ruoyi/ccdi/project/mapper/CcdiProjectSpecialCheckMapperDetailSqlTest.java` + - 结果:通过。 + +4. 双员工夫妻 Mapper 关键口径文本校验: + - 命令:Ruby 读取 Mapper XML 并检查反向配偶、员工配偶收入、员工配偶资产、员工配偶负债和明细参数关键片段。 + - 结果:通过,输出 `double-staff spouse mapper checks passed`。 + +5. 专项 JUnit: + - 命令:`mvn test -Dtest=CcdiProjectSpecialCheckMapperListSqlTest,CcdiProjectSpecialCheckMapperDetailSqlTest` + - 结果:未完成执行。当前模块测试编译阶段被既有无关测试错误阻断,错误集中在 `CcdiBankStatementTest` 与 `CcdiFileUploadServiceImplTest` 中旧接口签名不匹配,本次未修改这些文件。