diff --git a/doc/2026-03-30-loan-pricing-sensitive-data-encryption-design.md b/doc/2026-03-30-loan-pricing-sensitive-data-encryption-design.md index 51c1946..3afc08c 100644 --- a/doc/2026-03-30-loan-pricing-sensitive-data-encryption-design.md +++ b/doc/2026-03-30-loan-pricing-sensitive-data-encryption-design.md @@ -19,7 +19,8 @@ ## 2. 已确认约束 - 仅修改贷款定价流程相关前后端与数据库脚本,不扩散到系统其他模块 -- 不覆盖模型输出表的敏感字段治理 +- 不覆盖模型输出表的存储加密治理 +- 详情页模型输出“基本信息”中的 `custName`、`idNum` 必须纳入展示脱敏范围 - 不新增前端字段级加密协议 - 不引入外部密钥管理系统 - 不新增兼容性字段、补丁式逻辑或降级分支 @@ -172,6 +173,14 @@ 4. 将解密后的明文复制到 `ModelInvokeDTO` 5. 模型调用完成后,模型输出链路保持现状,不纳入本次改造 +### 7.5 模型输出展示链路 + +1. 详情接口查询到 `ModelRetailOutputFields` 或 `ModelCorpOutputFields` +2. 模型输出实体中的 `custName`、`idNum` 保持当前存储方式,不做表级加密改造 +3. 在详情接口返回前,对模型输出“基本信息”中的 `custName`、`idNum` 做统一脱敏 +4. 前端 `ModelOutputDisplay` 组件直接展示后端返回的脱敏值 +5. 不提供模型输出基本信息的明文查看入口 + ## 8. 后端改造设计 ### 8.1 配置项 @@ -202,6 +211,9 @@ 5. `LoanPricingModelService#invokeModelAsync` 模型调用前解密 `custName`、`idNum`,确保模型收到明文业务数据 +6. `LoanPricingWorkflowServiceImpl#selectLoanPricingBySerialNum` + 在组装 `ModelRetailOutputFields`、`ModelCorpOutputFields` 返回值时,对其 `custName`、`idNum` 进行脱敏替换 + ### 8.3 对象边界 本次不新增明文返回字段,也不保留“密文字段 + 展示字段”双轨结构,避免对象语义膨胀。 @@ -225,6 +237,7 @@ - 保持字段布局不变 - `custName`、`idNum` 直接展示后端返回的脱敏值 - 不新增“查看明文”“复制原值”等交互入口 +- 模型输出组件 `ModelOutputDisplay.vue` 的“基本信息”页签继续直接消费后端字段,但要求后端返回值已完成脱敏 ### 9.3 建单页 @@ -266,8 +279,9 @@ 3. 列表查询仅支持通过 `custIsn` 命中 4. 列表返回中的 `custName` 为脱敏值 5. 详情返回中的 `custName`、`idNum` 为脱敏值 -6. 模型调用链路成功,证明服务内部解密逻辑成立 -7. 配置缺失时创建流程失败,确认不会明文入库 +6. 模型输出“基本信息”中的 `custName`、`idNum` 返回为脱敏值 +7. 模型调用链路成功,证明服务内部解密逻辑成立 +8. 配置缺失时创建流程失败,确认不会明文入库 ### 12.2 前端验收 @@ -275,7 +289,9 @@ 2. 列表页客户名称展示为脱敏值 3. 个人详情页客户名称、证件号码展示为脱敏值 4. 企业详情页客户名称、证件号码展示为脱敏值 -5. 创建流程、查看详情、设定执行利率等既有功能不受影响 +5. 个人模型输出“基本信息”页签中的客户名称、证件号码展示为脱敏值 +6. 企业模型输出“基本信息”页签中的客户名称、证件号码展示为脱敏值 +7. 创建流程、查看详情、设定执行利率等既有功能不受影响 ## 13. 风险与控制 @@ -306,12 +322,13 @@ - 贷款定价流程建单入库加密 - 贷款定价流程列表和详情展示脱敏 +- 贷款定价流程详情页中的模型输出“基本信息”展示脱敏 - 贷款定价流程查询条件收口为客户内码 - 贷款定价流程存量数据清空处理 本次不包含: -- 模型输出表加密改造 +- 模型输出表存储加密改造 - 系统其他模块的敏感字段改造 - 前后端传输层字段加密 - 密钥托管平台接入 diff --git a/doc/implementation-report-2026-03-30-loan-pricing-sensitive-data-encryption-design.md b/doc/implementation-report-2026-03-30-loan-pricing-sensitive-data-encryption-design.md index cf80445..ad240de 100644 --- a/doc/implementation-report-2026-03-30-loan-pricing-sensitive-data-encryption-design.md +++ b/doc/implementation-report-2026-03-30-loan-pricing-sensitive-data-encryption-design.md @@ -12,6 +12,7 @@ - 明确采用应用层 AES 加解密与返回前统一脱敏方案 - 明确列表查询改为仅支持客户内码 `custIsn` - 明确存量数据处理方式为直接清空,不做迁移 +- 补充模型输出“基本信息”中的 `custName`、`idNum` 也需纳入展示脱敏范围 ## 文档路径 @@ -22,6 +23,7 @@ - `loan_pricing_workflow.cust_name`、`loan_pricing_workflow.id_num` 改为密文存储 - 贷款定价流程列表页、详情页仅展示脱敏值 +- 贷款定价流程详情页中的模型输出“基本信息”也仅展示脱敏值 - 前端不承担加解密职责 - 模型调用前由后端服务内部解密敏感字段 diff --git a/doc/implementation-report-2026-03-30-loan-pricing-sensitive-data-encryption-plans.md b/doc/implementation-report-2026-03-30-loan-pricing-sensitive-data-encryption-plans.md index f1691da..e9fe3b8 100644 --- a/doc/implementation-report-2026-03-30-loan-pricing-sensitive-data-encryption-plans.md +++ b/doc/implementation-report-2026-03-30-loan-pricing-sensitive-data-encryption-plans.md @@ -9,6 +9,7 @@ - 明确后端采用统一加解密服务 + 统一展示脱敏服务的实施路径 - 明确前端仅做查询项收口和脱敏值消费,不承担加解密 - 明确测试命令、数据库清理脚本、实施记录与提交节点 +- 补充模型输出“基本信息”页签中的 `custName`、`idNum` 也纳入脱敏验证范围 ## 文档路径 - `docs/superpowers/plans/2026-03-30-loan-pricing-sensitive-data-encryption-backend-plan.md` @@ -18,7 +19,8 @@ ## 计划结论 - 计划已按仓库要求拆分为后端执行文档和前端执行文档 - 后端计划覆盖密钥配置、敏感字段加解密、列表/详情脱敏、模型调用前解密和历史数据清理 -- 前端计划覆盖查询项切换为客户内码、脱敏值展示消费、构建验证和联调验证 +- 后端计划补充模型输出“基本信息”返回值脱敏 +- 前端计划覆盖查询项切换为客户内码、脱敏值展示消费、构建验证和联调验证,并显式检查模型输出“基本信息”页签 - 两份计划都采用最短路径实现,不引入明密文兼容分支 ## 说明 diff --git a/docs/superpowers/plans/2026-03-30-loan-pricing-sensitive-data-encryption-backend-plan.md b/docs/superpowers/plans/2026-03-30-loan-pricing-sensitive-data-encryption-backend-plan.md index bbe0175..06b9341 100644 --- a/docs/superpowers/plans/2026-03-30-loan-pricing-sensitive-data-encryption-backend-plan.md +++ b/docs/superpowers/plans/2026-03-30-loan-pricing-sensitive-data-encryption-backend-plan.md @@ -2,9 +2,9 @@ > **For agentic workers:** REQUIRED: Use superpowers:executing-plans to implement this plan in this repository. Do not use subagents. Steps use checkbox (`- [ ]`) syntax for tracking. -**Goal:** 让贷款定价流程在后端对 `custName`、`idNum` 实现密文存储,并保证列表、详情、模型调用链路在各自边界内完成脱敏或解密。 +**Goal:** 让贷款定价流程在后端对 `custName`、`idNum` 实现密文存储,并保证列表、详情、模型输出基本信息、模型调用链路在各自边界内完成脱敏或解密。 -**Architecture:** 后端在 `ruoyi-loan-pricing` 模块内新增贷款定价专用敏感字段加解密服务和展示脱敏服务,固定密钥从配置读取。`LoanPricingWorkflowServiceImpl` 在创建、列表、详情链路显式接入这些服务,`LoanPricingModelService` 在调模型前显式解密,避免把密文错误透传给模型。 +**Architecture:** 后端在 `ruoyi-loan-pricing` 模块内新增贷款定价专用敏感字段加解密服务和展示脱敏服务,固定密钥从配置读取。`LoanPricingWorkflowServiceImpl` 在创建、列表、详情和模型输出展示链路显式接入这些服务,`LoanPricingModelService` 在调模型前显式解密,避免把密文错误透传给模型。 **Tech Stack:** Spring Boot、MyBatis Plus、JUnit 5、Mockito、Maven、JDK `javax.crypto` @@ -193,7 +193,7 @@ git add ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/impl/Loan git commit -m "接入流程敏感字段加密与列表脱敏" ``` -### Task 3: 接入详情返回与模型调用链路 +### Task 3: 接入详情返回、模型输出展示与模型调用链路 **Files:** - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImpl.java` @@ -202,7 +202,7 @@ git commit -m "接入流程敏感字段加密与列表脱敏" - Create: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServiceTest.java` - Modify: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImplTest.java` -- [ ] **Step 1: 写详情与模型调用失败测试** +- [ ] **Step 1: 写详情、模型输出展示与模型调用失败测试** 新增或补充以下测试场景: @@ -211,6 +211,16 @@ git commit -m "接入流程敏感字段加密与列表脱敏" void shouldMaskCustNameAndIdNumWhenReturningWorkflowDetail() { ... } ``` +```java +@Test +void shouldMaskCustNameAndIdNumInRetailModelOutputBasicInfo() { ... } +``` + +```java +@Test +void shouldMaskCustNameAndIdNumInCorporateModelOutputBasicInfo() { ... } +``` + ```java @Test void shouldDecryptCustNameAndIdNumBeforeInvokeModel() { ... } @@ -221,6 +231,8 @@ void shouldDecryptCustNameAndIdNumBeforeInvokeModel() { ... } ```java assertEquals("张*", result.getLoanPricingWorkflow().getCustName()); assertEquals("1101********1234", result.getLoanPricingWorkflow().getIdNum()); +assertEquals("张*", result.getModelRetailOutputFields().getCustName()); +assertEquals("1101********1234", result.getModelRetailOutputFields().getIdNum()); verify(modelService).invokeModel(argThat(dto -> Objects.equals("张三", dto.getCustName()) && Objects.equals("110101199001011234", dto.getIdNum()))); @@ -229,7 +241,7 @@ verify(modelService).invokeModel(argThat(dto -> - [ ] **Step 2: 运行详情与模型测试确认失败** Run: `mvn -pl ruoyi-loan-pricing -am -Dtest=LoanPricingWorkflowServiceImplTest,LoanPricingModelServiceTest -Dsurefire.failIfNoSpecifiedTests=false test` -Expected: FAIL,当前详情返回未脱敏,模型调用前也未解密。 +Expected: FAIL,当前详情返回未完整脱敏,模型输出“基本信息”仍会返回明文,模型调用前也未解密。 - [ ] **Step 3: 在详情返回前显式解密再脱敏** @@ -245,6 +257,7 @@ loanPricingWorkflow.setIdNum(loanPricingSensitiveDisplayService.maskIdNum(plainI 要求: - 对外返回对象中不保留明文 - 保持既有测算利率与执行利率逻辑不变 +- 若 `modelRetailOutputFields` 或 `modelCorpOutputFields` 非空,同样对其 `custName`、`idNum` 做脱敏替换 - [ ] **Step 4: 在模型调用前显式解密** @@ -316,6 +329,7 @@ Run: 按项目现有方式启动后端,创建一条个人流程和一条企业 Expected: - 数据库中的 `cust_name`、`id_num` 不等于前端提交明文 - 列表和详情返回的 `custName`、`idNum` 为脱敏值 +- 模型输出“基本信息”页签中的 `custName`、`idNum` 也为脱敏值 - [ ] **Step 4: 如果为验证启动了后端进程,结束对应进程** diff --git a/docs/superpowers/plans/2026-03-30-loan-pricing-sensitive-data-encryption-frontend-plan.md b/docs/superpowers/plans/2026-03-30-loan-pricing-sensitive-data-encryption-frontend-plan.md index 07e3436..7285585 100644 --- a/docs/superpowers/plans/2026-03-30-loan-pricing-sensitive-data-encryption-frontend-plan.md +++ b/docs/superpowers/plans/2026-03-30-loan-pricing-sensitive-data-encryption-frontend-plan.md @@ -2,9 +2,9 @@ > **For agentic workers:** REQUIRED: Use superpowers:executing-plans to implement this plan in this repository. Do not use subagents. Steps use checkbox (`- [ ]`) syntax for tracking. -**Goal:** 让贷款定价流程前端只按客户内码查询,并在列表页、详情页稳定展示后端返回的脱敏 `custName`、`idNum`。 +**Goal:** 让贷款定价流程前端只按客户内码查询,并在列表页、详情页、模型输出“基本信息”页签稳定展示后端返回的脱敏 `custName`、`idNum`。 -**Architecture:** 前端不承担任何加解密逻辑,只做查询项收口与脱敏值展示消费。列表页从按 `custName` 查询切换为按 `custIsn` 查询,详情页保持现有结构,继续直接渲染后端返回字段。 +**Architecture:** 前端不承担任何加解密逻辑,只做查询项收口与脱敏值展示消费。列表页从按 `custName` 查询切换为按 `custIsn` 查询,详情页与 `ModelOutputDisplay.vue` 保持现有结构,继续直接渲染后端返回字段,但要联调确认模型输出“基本信息”页签不再出现敏感明文。 **Tech Stack:** Vue 2、Element UI、RuoYi 前端工程、npm @@ -64,18 +64,19 @@ git add ruoyi-ui/src/views/loanPricing/workflow/index.vue ruoyi-ui/src/api/loanP git commit -m "调整流程列表按客户内码查询" ``` -### Task 2: 固化列表页与详情页的脱敏展示消费 +### Task 2: 固化列表页、详情页与模型输出基本信息的脱敏展示消费 **Files:** - Modify: `ruoyi-ui/src/views/loanPricing/workflow/index.vue` - Modify: `ruoyi-ui/src/views/loanPricing/workflow/detail.vue` - Modify: `ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue` - Modify: `ruoyi-ui/src/views/loanPricing/workflow/components/CorporateWorkflowDetail.vue` +- Inspect: `ruoyi-ui/src/views/loanPricing/workflow/components/ModelOutputDisplay.vue` - [ ] **Step 1: 核对当前页面直接消费后端字段的位置** -Run: `rg -n 'custName|idNum' ruoyi-ui/src/views/loanPricing/workflow/index.vue ruoyi-ui/src/views/loanPricing/workflow/detail.vue ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue ruoyi-ui/src/views/loanPricing/workflow/components/CorporateWorkflowDetail.vue` -Expected: 能定位列表和详情页中所有 `custName`、`idNum` 的展示位置。 +Run: `rg -n 'custName|idNum' ruoyi-ui/src/views/loanPricing/workflow/index.vue ruoyi-ui/src/views/loanPricing/workflow/detail.vue ruoyi-ui/src/views/loanPricing/workflow/components/PersonalWorkflowDetail.vue ruoyi-ui/src/views/loanPricing/workflow/components/CorporateWorkflowDetail.vue ruoyi-ui/src/views/loanPricing/workflow/components/ModelOutputDisplay.vue` +Expected: 能定位列表、详情以及模型输出“基本信息”页签中所有 `custName`、`idNum` 的展示位置。 - [ ] **Step 2: 去掉任何可能的前端二次处理设想,只保留直接展示** @@ -90,6 +91,7 @@ Expected: 能定位列表和详情页中所有 `custName`、`idNum` 的展示位 - 不新增“查看明文”按钮 - 不新增复制原值功能 - 不在前端自行做脱敏算法 +- `ModelOutputDisplay.vue` 继续直接消费后端字段,不新增本地脱敏逻辑 - [ ] **Step 3: 执行前端构建验证** @@ -103,6 +105,8 @@ Expected: - 列表页客户名称显示为脱敏值 - 个人详情页客户名称、证件号码显示为脱敏值 - 企业详情页客户名称、证件号码显示为脱敏值 +- 个人模型输出“基本信息”页签中的客户名称、证件号码显示为脱敏值 +- 企业模型输出“基本信息”页签中的客户名称、证件号码显示为脱敏值 - [ ] **Step 5: 如果为验证启动了前端进程,结束对应进程** @@ -129,6 +133,7 @@ git commit -m "接入流程敏感字段前端脱敏展示" - 流程列表页查询项已从客户名称切换为客户内码 - 前端不承担 `custName`、`idNum` 加解密逻辑 - 列表页与详情页均直接展示后端返回的脱敏值 +- 模型输出“基本信息”页签也直接展示后端返回的脱敏值 - 已完成前端构建验证与页面联调 ```