# Shangyu Pricing Field Adjustment Backend Implementation Plan > **For agentic workers:** Follow this repository's `AGENTS.md`: do not invoke `using-superpowers` or subagents during implementation unless the user explicitly requests them. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Update the backend create-flow contract so `businessType`, `couponRate`, personal removed fields, and customer-type-specific collateral options match the approved Shangyu pricing spec. **Architecture:** Keep the existing workflow creation API and entity flow. Add `couponRate` to the DTO/entity/model path, tighten service-layer validation in `LoanPricingWorkflowServiceImpl`, remove personal create dependencies on `loanPurpose` and `bizProof`, and update SQL schema files in place. **Tech Stack:** Spring Boot, RuoYi, MyBatis Plus, Lombok, Jakarta Validation, JUnit 5, Mockito, MySQL SQL files. --- ## File Structure - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java` - Change `businessType` values to `新增/存量新增/存量转贷`. - Remove personal create DTO dependency on `loanPurpose` and `bizProof`. - Add `couponRate`. - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/CorporateLoanPricingCreateDTO.java` - Change `businessType` values to `新增/存量新增/存量转贷`. - Expand `collType` values for corporate mortgage and pledge paths. - Add `couponRate`. - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/entity/LoanPricingWorkflow.java` - Update `businessType` comment and add persisted `couponRate`. - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java` - Add `couponRate` only. Do not add `businessType`; the user excluded that model-input branch from this scope. - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java` - Map `couponRate`. - Stop mapping removed personal fields from the personal create DTO. - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImpl.java` - Update business type validation. - Add collateral-option validation by `custType + guarType`. - Add required `couponRate` validation for `质押 + 存单质押`. - Modify Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImplTest.java` - Add validation coverage and update existing create tests to set valid `businessType`. - Modify Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java` - Remove old personal `loanPurpose` DTO expectations so Maven test compilation remains valid after DTO removal. - Keep `loanTerm/loanLoop` model assertions. - Create Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/util/LoanPricingConverterTest.java` - Confirm personal removed fields no longer drive conversion and `couponRate` maps. - Modify Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServiceTest.java` - Confirm `couponRate` is copied into `ModelInvokeDTO` via the model invocation path. - Create: `sql/add_coupon_rate_20260511.sql` - Migration for existing databases. - Modify: `sql/loan_pricing_workflow.sql` - Add `coupon_rate`. - Modify: `sql/loan_pricing_schema_20260328.sql` - Add `coupon_rate` to `loan_pricing_workflow`. - Modify: `sql/loan_pricing_prod_init_20260331.sql` - Add `coupon_rate` to production init schema. ## Task 1: Add Failing Backend Contract Tests **Files:** - Modify Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImplTest.java` - Modify Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java` - Create Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/util/LoanPricingConverterTest.java` - Modify Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServiceTest.java` - [ ] **Step 1: Update existing create tests with valid business type** In `LoanPricingWorkflowServiceImplTest`, every test that calls `createLoanPricing` must set: ```java workflow.setBusinessType("新增"); ``` For tests using mortgage or pledge, also set an allowed `collType` for the matching `custType`. - [ ] **Step 2: Update old personal parameter tests** In `LoanPricingModelServicePersonalParamsTest`, replace the old DTO field test: Add the static import if it is not present: ```java import static org.junit.jupiter.api.Assertions.assertNull; ``` ```java @Test void shouldRemoveLoanPurposeAndKeepLoanTermInPersonalCreateDto() throws NoSuchFieldException { assertThrows(NoSuchFieldException.class, () -> PersonalLoanPricingCreateDTO.class.getDeclaredField("loanPurpose")); assertNotNull(PersonalLoanPricingCreateDTO.class.getDeclaredField("loanTerm")); } ``` Replace the old converter test that called `dto.setLoanPurpose(...)`: ```java @Test void shouldMapLoanTermWithoutLoanPurposeFromPersonalDto() { PersonalLoanPricingCreateDTO dto = new PersonalLoanPricingCreateDTO(); dto.setCustIsn("CUST001"); dto.setCustName("张三"); dto.setGuarType("信用"); dto.setApplyAmt("100000"); dto.setBusinessType("新增"); dto.setLoanTerm("3"); LoanPricingWorkflow workflow = LoanPricingConverter.toEntity(dto); assertNull(workflow.getLoanPurpose()); assertNull(workflow.getBizProof()); assertEquals("3", workflow.getLoanTerm()); } ``` In `shouldInvokePersonalModelWithExpectedParams`, remove: ```java workflow.setLoanPurpose("business"); workflow.setBizProof("true"); ``` and remove these argument matcher clauses: ```java && Objects.equals("business", dto.getLoanPurpose()) && Objects.equals("1", dto.getBizProof()) ``` Keep the existing `loanTerm`, `loanLoop`, `collThirdParty`, and `collType` assertions. - [ ] **Step 3: Add failing business type and collateral validation tests** Add tests like: ```java @Test void shouldRejectOldBusinessType() { LoanPricingWorkflow workflow = validPersonalWorkflow(); workflow.setBusinessType("新客"); ServiceException ex = assertThrows(ServiceException.class, () -> loanPricingWorkflowService.createLoanPricing(workflow)); assertEquals("业务种类必须是:新增、存量新增、存量转贷之一", ex.getMessage()); } @Test void shouldRequireHistoryRateForStockTransfer() { LoanPricingWorkflow workflow = validCorporateWorkflow(); workflow.setBusinessType("存量转贷"); workflow.setLoanRateHistory(null); ServiceException ex = assertThrows(ServiceException.class, () -> loanPricingWorkflowService.createLoanPricing(workflow)); assertEquals("请选择历史贷款合同", ex.getMessage()); } @Test void shouldRequireCouponRateForCertificatePledge() { LoanPricingWorkflow workflow = validPersonalWorkflow(); workflow.setGuarType("质押"); workflow.setCollType("存单质押"); workflow.setCouponRate(null); ServiceException ex = assertThrows(ServiceException.class, () -> loanPricingWorkflowService.createLoanPricing(workflow)); assertEquals("存单票面利率不能为空", ex.getMessage()); } ``` Add helper methods: ```java private LoanPricingWorkflow validPersonalWorkflow() { LoanPricingWorkflow workflow = new LoanPricingWorkflow(); workflow.setCustType("个人"); workflow.setCustIsn("P001"); workflow.setCustName("张三"); workflow.setIdNum("330102199001011234"); workflow.setGuarType("信用"); workflow.setApplyAmt("100000"); workflow.setBusinessType("新增"); return workflow; } private LoanPricingWorkflow validCorporateWorkflow() { LoanPricingWorkflow workflow = new LoanPricingWorkflow(); workflow.setCustType("企业"); workflow.setCustIsn("C001"); workflow.setCustName("测试企业"); workflow.setIdNum("91330100MA0000000X"); workflow.setGuarType("信用"); workflow.setApplyAmt("1000000"); workflow.setBusinessType("新增"); return workflow; } ``` - [ ] **Step 4: Add failing allowed-option tests** Add focused tests: ```java @Test void shouldAllowPersonalMortgageLineType() { LoanPricingWorkflow workflow = validPersonalWorkflow(); workflow.setGuarType("抵押"); workflow.setCollType("一线"); when(sensitiveFieldCryptoService.encrypt(any())).thenAnswer(invocation -> invocation.getArgument(0)); loanPricingWorkflowService.createLoanPricing(workflow); verify(loanPricingWorkflowMapper).insert(any()); } @Test void shouldRejectCorporateMortgageTypeForPersonalMortgage() { LoanPricingWorkflow workflow = validPersonalWorkflow(); workflow.setGuarType("抵押"); workflow.setCollType("排污权抵押"); ServiceException ex = assertThrows(ServiceException.class, () -> loanPricingWorkflowService.createLoanPricing(workflow)); assertEquals("个人抵押抵质押类型必须是:一线、一类、二类、三类之一", ex.getMessage()); } @Test void shouldAllowCorporatePledgeEquityType() { LoanPricingWorkflow workflow = validCorporateWorkflow(); workflow.setGuarType("质押"); workflow.setCollType("股权质押"); when(sensitiveFieldCryptoService.encrypt(any())).thenAnswer(invocation -> invocation.getArgument(0)); loanPricingWorkflowService.createLoanPricing(workflow); verify(loanPricingWorkflowMapper).insert(any()); } ``` - [ ] **Step 5: Add failing converter test** Create `LoanPricingConverterTest`: ```java package com.ruoyi.loanpricing.util; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import com.ruoyi.loanpricing.domain.dto.CorporateLoanPricingCreateDTO; import com.ruoyi.loanpricing.domain.dto.PersonalLoanPricingCreateDTO; import com.ruoyi.loanpricing.domain.entity.LoanPricingWorkflow; import org.junit.jupiter.api.Test; class LoanPricingConverterTest { @Test void shouldMapCouponRateForPersonalWorkflow() { PersonalLoanPricingCreateDTO dto = new PersonalLoanPricingCreateDTO(); dto.setCustIsn("P001"); dto.setGuarType("质押"); dto.setApplyAmt("100000"); dto.setBusinessType("新增"); dto.setCouponRate("2.15"); LoanPricingWorkflow workflow = LoanPricingConverter.toEntity(dto); assertEquals("2.15", workflow.getCouponRate()); assertNull(workflow.getLoanPurpose()); assertNull(workflow.getBizProof()); } @Test void shouldMapCouponRateForCorporateWorkflow() { CorporateLoanPricingCreateDTO dto = new CorporateLoanPricingCreateDTO(); dto.setCustIsn("C001"); dto.setGuarType("质押"); dto.setApplyAmt("1000000"); dto.setBusinessType("新增"); dto.setCouponRate("2.35"); LoanPricingWorkflow workflow = LoanPricingConverter.toEntity(dto); assertEquals("2.35", workflow.getCouponRate()); } } ``` - [ ] **Step 6: Add failing model DTO test** In `LoanPricingModelServiceTest`, add a test or extend the existing argument-captor test to assert: ```java assertEquals("2.15", capturedModelInvokeDto.getCouponRate()); ``` The workflow used by that test must set: ```java loanPricingWorkflow.setCouponRate("2.15"); loanPricingWorkflow.setBusinessType("新增"); ``` - [ ] **Step 7: Run backend tests and confirm failure** Run: ```bash mvn -pl ruoyi-loan-pricing -am -Dtest=LoanPricingWorkflowServiceImplTest,LoanPricingModelServicePersonalParamsTest,LoanPricingConverterTest,LoanPricingModelServiceTest -Dsurefire.failIfNoSpecifiedTests=false test ``` Expected: FAIL because `couponRate` and new validation are not implemented yet. ## Task 2: Implement DTO, Entity, Converter, and Model DTO Fields **Files:** - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java` - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/CorporateLoanPricingCreateDTO.java` - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/entity/LoanPricingWorkflow.java` - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java` - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java` - Modify Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java` - [ ] **Step 1: Update personal create DTO** In `PersonalLoanPricingCreateDTO`: - Remove the `loanPurpose` field and its validation annotations. - Remove the `bizProof` field. - Change `businessType` annotations to: ```java @Schema(description = "业务种类", requiredMode = Schema.RequiredMode.REQUIRED, example = "新增", allowableValues = {"新增", "存量新增", "存量转贷"}) @NotBlank(message = "业务种类不能为空") @Pattern(regexp = "^(新增|存量新增|存量转贷)$", message = "业务种类必须是:新增、存量新增、存量转贷之一") private String businessType; ``` - Add: ```java @Schema(description = "存单票面利率", example = "2.15") private String couponRate; ``` - [ ] **Step 2: Update corporate create DTO** In `CorporateLoanPricingCreateDTO`: ```java @Schema(description = "业务种类", requiredMode = Schema.RequiredMode.REQUIRED, example = "新增", allowableValues = {"新增", "存量新增", "存量转贷"}) @NotBlank(message = "业务种类不能为空") @Pattern(regexp = "^(新增|存量新增|存量转贷)$", message = "业务种类必须是:新增、存量新增、存量转贷之一") private String businessType; ``` Change `collType` annotation to include the combined corporate option set: ```java @Schema(description = "抵质押类型", example = "一类", allowableValues = {"一类", "二类", "三类", "四类", "排污权抵押", "设备等其他不动产抵押", "存单质押", "股权质押", "其他质押"}) @Pattern(regexp = "^(一类|二类|三类|四类|排污权抵押|设备等其他不动产抵押|存单质押|股权质押|其他质押)$", message = "抵质押类型不符合当前客户类型和担保方式") private String collType; ``` Add: ```java @Schema(description = "存单票面利率", example = "2.35") private String couponRate; ``` - [ ] **Step 3: Update workflow entity** In `LoanPricingWorkflow`: ```java /** 业务种类: 新增/存量新增/存量转贷 */ private String businessType; /** 存单票面利率 */ private String couponRate; ``` Keep `loanPurpose` and `bizProof` on the entity for historical rows and existing schema compatibility; they are no longer populated by personal create DTO conversion. - [ ] **Step 4: Update model DTO** In `ModelInvokeDTO`, add: ```java /** * 存单票面利率 */ private String couponRate; ``` Do not add `businessType` in this task. - [ ] **Step 5: Update converter** In personal conversion, remove: ```java entity.setLoanPurpose(dto.getLoanPurpose()); entity.setBizProof(dto.getBizProof()); ``` Add for both personal and corporate conversion: ```java entity.setCouponRate(dto.getCouponRate()); ``` - [ ] **Step 6: Run targeted converter and model tests** Run: ```bash mvn -pl ruoyi-loan-pricing -am -Dtest=LoanPricingModelServicePersonalParamsTest,LoanPricingConverterTest,LoanPricingModelServiceTest -Dsurefire.failIfNoSpecifiedTests=false test ``` Expected: personal parameter, converter, and model field tests PASS; service validation tests may still fail until Task 3. - [ ] **Step 7: Commit field and converter changes** ```bash git add ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/PersonalLoanPricingCreateDTO.java \ ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/CorporateLoanPricingCreateDTO.java \ ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/entity/LoanPricingWorkflow.java \ ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/domain/dto/ModelInvokeDTO.java \ ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/util/LoanPricingConverter.java \ ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServicePersonalParamsTest.java \ ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/util/LoanPricingConverterTest.java \ ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/LoanPricingModelServiceTest.java git commit -m "调整上虞利率定价后端字段" ``` ## Task 3: Implement Service-Layer Validation **Files:** - Modify: `ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImpl.java` - Modify Test: `ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImplTest.java` - [ ] **Step 1: Replace business type validation** Update `validateBusinessTypeAndHistoryRate`: ```java private void validateBusinessTypeAndHistoryRate(LoanPricingWorkflow workflow) { if (!StringUtils.hasText(workflow.getBusinessType())) { throw new ServiceException("业务种类不能为空"); } if (!isOneOf(workflow.getBusinessType(), "新增", "存量新增", "存量转贷")) { throw new ServiceException("业务种类必须是:新增、存量新增、存量转贷之一"); } if ("存量转贷".equals(workflow.getBusinessType()) && !StringUtils.hasText(workflow.getLoanRateHistory())) { throw new ServiceException("请选择历史贷款合同"); } } ``` - [ ] **Step 2: Add collateral and coupon validators** Add calls before insert: ```java validateBusinessTypeAndHistoryRate(loanPricingWorkflow); validateCollateralType(loanPricingWorkflow); validateCouponRate(loanPricingWorkflow); ``` Add helper methods: ```java private void validateCouponRate(LoanPricingWorkflow workflow) { if ("质押".equals(workflow.getGuarType()) && "存单质押".equals(workflow.getCollType()) && !StringUtils.hasText(workflow.getCouponRate())) { throw new ServiceException("存单票面利率不能为空"); } } private void validateCollateralType(LoanPricingWorkflow workflow) { if (!"抵押".equals(workflow.getGuarType()) && !"质押".equals(workflow.getGuarType())) { return; } if (!StringUtils.hasText(workflow.getCollType())) { throw new ServiceException("请选择抵质押类型"); } if ("个人".equals(workflow.getCustType()) && "抵押".equals(workflow.getGuarType()) && !isOneOf(workflow.getCollType(), "一线", "一类", "二类", "三类")) { throw new ServiceException("个人抵押抵质押类型必须是:一线、一类、二类、三类之一"); } if ("个人".equals(workflow.getCustType()) && "质押".equals(workflow.getGuarType()) && !isOneOf(workflow.getCollType(), "存单质押", "其他质押")) { throw new ServiceException("个人质押抵质押类型必须是:存单质押、其他质押之一"); } if ("企业".equals(workflow.getCustType()) && "抵押".equals(workflow.getGuarType()) && !isOneOf(workflow.getCollType(), "一类", "二类", "三类", "四类", "排污权抵押", "设备等其他不动产抵押")) { throw new ServiceException("企业抵押抵质押类型必须是:一类、二类、三类、四类、排污权抵押、设备等其他不动产抵押之一"); } if ("企业".equals(workflow.getCustType()) && "质押".equals(workflow.getGuarType()) && !isOneOf(workflow.getCollType(), "存单质押", "股权质押", "其他质押")) { throw new ServiceException("企业质押抵质押类型必须是:存单质押、股权质押、其他质押之一"); } } private boolean isOneOf(String value, String... allowedValues) { for (String allowedValue : allowedValues) { if (allowedValue.equals(value)) { return true; } } return false; } ``` - [ ] **Step 3: Run service tests** Run: ```bash mvn -pl ruoyi-loan-pricing -am -Dtest=LoanPricingWorkflowServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test ``` Expected: PASS. - [ ] **Step 4: Run backend targeted suite** Run: ```bash mvn -pl ruoyi-loan-pricing -am -Dtest=LoanPricingWorkflowServiceImplTest,LoanPricingModelServicePersonalParamsTest,LoanPricingConverterTest,LoanPricingModelServiceTest -Dsurefire.failIfNoSpecifiedTests=false test ``` Expected: PASS. - [ ] **Step 5: Commit validation changes** ```bash git add ruoyi-loan-pricing/src/main/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImpl.java \ ruoyi-loan-pricing/src/test/java/com/ruoyi/loanpricing/service/impl/LoanPricingWorkflowServiceImplTest.java git commit -m "增加上虞利率定价创建校验" ``` ## Task 4: Update SQL Schema Files **Files:** - Create: `sql/add_coupon_rate_20260511.sql` - Modify: `sql/loan_pricing_workflow.sql` - Modify: `sql/loan_pricing_schema_20260328.sql` - Modify: `sql/loan_pricing_prod_init_20260331.sql` - [ ] **Step 1: Create migration SQL** Create `sql/add_coupon_rate_20260511.sql`: ```sql -- 上虞利率定价存单票面利率字段 ALTER TABLE `loan_pricing_workflow` ADD COLUMN `coupon_rate` varchar(100) DEFAULT NULL COMMENT '存单票面利率' AFTER `loan_rate_history`; ``` - [ ] **Step 2: Update standalone workflow schema** In `sql/loan_pricing_workflow.sql`, add after `loan_rate_history`: ```sql `coupon_rate` varchar(100) DEFAULT NULL COMMENT '存单票面利率', ``` - [ ] **Step 3: Update bundled schema files** Apply the same column to the `loan_pricing_workflow` table definitions in: - `sql/loan_pricing_schema_20260328.sql` - `sql/loan_pricing_prod_init_20260331.sql` - [ ] **Step 4: Verify SQL references** Run: ```bash rg -n "coupon_rate|存单票面利率" sql ``` Expected: `coupon_rate` appears in the migration and all three schema/init files. - [ ] **Step 5: Commit SQL changes** ```bash git add sql/add_coupon_rate_20260511.sql \ sql/loan_pricing_workflow.sql \ sql/loan_pricing_schema_20260328.sql \ sql/loan_pricing_prod_init_20260331.sql git commit -m "新增存单票面利率数据库字段" ``` ## Task 5: Backend Verification and Record **Files:** - Create or Modify: `doc/implementation-report-2026-05-11-shangyu-pricing-field-adjustment.md` - [ ] **Step 1: Run backend verification** Run: ```bash mvn -pl ruoyi-loan-pricing -am -Dtest=LoanPricingWorkflowServiceImplTest,LoanPricingModelServicePersonalParamsTest,LoanPricingConverterTest,LoanPricingModelServiceTest -Dsurefire.failIfNoSpecifiedTests=false test ``` Expected: PASS. - [ ] **Step 2: Check staged-independent status** Run: ```bash git status --short ``` Expected: only intended backend and SQL files are modified or staged for this implementation slice. Existing unrelated files such as `AGENTS.md`, `.DS_Store`, and operation-manual docs must stay out of these commits. - [ ] **Step 3: Update implementation record** Add backend notes to `doc/implementation-report-2026-05-11-shangyu-pricing-field-adjustment.md`: ```markdown ## 后端实现 - 调整个人/企业创建 DTO 的业务种类口径为 `新增/存量新增/存量转贷`。 - 新增 `couponRate` 存单票面利率字段,并贯通流程保存和模型入参。 - 取消对私创建链路对 `loanPurpose`、`bizProof` 的依赖。 - 增加服务层校验,覆盖业务种类、历史贷款利率、抵质押类型和存单票面利率。 - 新增 `coupon_rate` 数据库字段及 schema/init SQL。 ## 后端验证 - `mvn -pl ruoyi-loan-pricing -am -Dtest=LoanPricingWorkflowServiceImplTest,LoanPricingModelServicePersonalParamsTest,LoanPricingConverterTest,LoanPricingModelServiceTest -Dsurefire.failIfNoSpecifiedTests=false test` ``` - [ ] **Step 4: Commit backend record** ```bash git add doc/implementation-report-2026-05-11-shangyu-pricing-field-adjustment.md git commit -m "记录上虞字段调整后端实现" ```