hookehuyr

fix(plan): 修正 coverage 与 annual_premium 字段映射错误

修复 coverage 字段错误映射到 annual_premium API 字段的问题,
新增独立的 annual_premium 字段定义,同步更新 API 文档。

- coverage 现正确映射到 coverage API 字段(保额)
- annual_premium 独立映射到 annual_premium API 字段(年缴保费)
- 更新 API 文档补充 coverage 参数和完整请求示例

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
...@@ -569,7 +569,7 @@ GET /api/plan/config/:form_sn ...@@ -569,7 +569,7 @@ GET /api/plan/config/:form_sn
569 }, 569 },
570 "submit_mapping": { 570 "submit_mapping": {
571 "customer_name": { "api_field": "customer_name" }, 571 "customer_name": { "api_field": "customer_name" },
572 - "coverage": { "api_field": "annual_premium", "transform": "fen_to_yuan" } 572 + "annual_premium": { "api_field": "annual_premium", "transform": "fen_to_yuan" }
573 // ... 更多映射 573 // ... 更多映射
574 } 574 }
575 } 575 }
...@@ -676,7 +676,7 @@ CREATE TABLE product_configs ( ...@@ -676,7 +676,7 @@ CREATE TABLE product_configs (
676 }, 676 },
677 "submit_mapping": { 677 "submit_mapping": {
678 "customer_name": { "api_field": "customer_name" }, 678 "customer_name": { "api_field": "customer_name" },
679 - "coverage": { "api_field": "annual_premium", "transform": "fen_to_yuan" } 679 + "annual_premium": { "api_field": "annual_premium", "transform": "fen_to_yuan" }
680 } 680 }
681 } 681 }
682 ``` 682 ```
......
...@@ -616,7 +616,7 @@ ...@@ -616,7 +616,7 @@
616 "submit_mapping": { 616 "submit_mapping": {
617 "customer_name": { "api_field": "customer_name" }, 617 "customer_name": { "api_field": "customer_name" },
618 "gender": { "api_field": "customer_gender" }, 618 "gender": { "api_field": "customer_gender" },
619 - "coverage": { "api_field": "annual_premium", "transform": "fen_to_yuan" }, 619 + "annual_premium": { "api_field": "annual_premium", "transform": "fen_to_yuan" },
620 "payment_period": { "api_field": "payment_years" }, 620 "payment_period": { "api_field": "payment_years" },
621 "withdrawal_enabled": { "api_field": "allow_reduce_amount" }, 621 "withdrawal_enabled": { "api_field": "allow_reduce_amount" },
622 "withdrawal_mode": { "api_field": "withdrawal_option" }, 622 "withdrawal_mode": { "api_field": "withdrawal_option" },
......
...@@ -79,7 +79,7 @@ const reset_map = { ...@@ -79,7 +79,7 @@ const reset_map = {
79 ```javascript 79 ```javascript
80 // submit_mapping 示例(金额字段统一从分转元) 80 // submit_mapping 示例(金额字段统一从分转元)
81 const submit_mapping = { 81 const submit_mapping = {
82 - coverage: { api_field: 'annual_premium', transform: 'fen_to_yuan' }, 82 + annual_premium: { api_field: 'annual_premium', transform: 'fen_to_yuan' },
83 annual_withdrawal_amount: { api_field: 'annual_withdrawal_amount', transform: 'fen_to_yuan' }, 83 annual_withdrawal_amount: { api_field: 'annual_withdrawal_amount', transform: 'fen_to_yuan' },
84 withdrawal_mode: { api_field: 'withdrawal_option' } 84 withdrawal_mode: { api_field: 'withdrawal_option' }
85 } 85 }
...@@ -101,7 +101,7 @@ const submit_mapping = { ...@@ -101,7 +101,7 @@ const submit_mapping = {
101 withdrawal_modes: ['年龄指定金额', '最高固定金额'], 101 withdrawal_modes: ['年龄指定金额', '最高固定金额'],
102 withdrawal_periods: ['1年', '3年', '5年', '10年'], 102 withdrawal_periods: ['1年', '3年', '5年', '10年'],
103 form_schema: { base_fields: [], withdrawal_fields: [], reset_map: {} }, 103 form_schema: { base_fields: [], withdrawal_fields: [], reset_map: {} },
104 - submit_mapping: { coverage: { api_field: 'annual_premium', transform: 'fen_to_yuan' } }, 104 + submit_mapping: { annual_premium: { api_field: 'annual_premium', transform: 'fen_to_yuan' } },
105 source_file: '产品说明书.pdf', 105 source_file: '产品说明书.pdf',
106 warnings: [] 106 warnings: []
107 } 107 }
......
...@@ -52,15 +52,12 @@ paths: ...@@ -52,15 +52,12 @@ paths:
52 customer_age: 52 customer_age:
53 type: integer 53 type: integer
54 title: 年龄 54 title: 年龄
55 - description: 与 customer_birthday 二选一,至少提供一个
56 customer_birthday: 55 customer_birthday:
57 type: string 56 type: string
58 title: 出生年月日 57 title: 出生年月日
59 - format: date
60 - description: 与 customer_age 二选一,至少提供一个。格式:YYYY-MM-DD
61 annual_premium: 58 annual_premium:
62 type: integer 59 type: integer
63 - title: 保额 60 + title: 年缴保费
64 payment_years: 61 payment_years:
65 type: string 62 type: string
66 title: 繳費年期 63 title: 繳費年期
...@@ -94,12 +91,14 @@ paths: ...@@ -94,12 +91,14 @@ paths:
94 annual_increase_percentage: 91 annual_increase_percentage:
95 type: integer 92 type: integer
96 title: 每年递增提取百分比 93 title: 每年递增提取百分比
94 + coverage:
95 + type: string
96 + title: 保额
97 required: 97 required:
98 - customer_name 98 - customer_name
99 - customer_gender 99 - customer_gender
100 - # 年龄与出生年月日二选一(至少提供一个) 100 + - customer_age
101 - # - customer_age 101 + - customer_birthday
102 - # - customer_birthday
103 - annual_premium 102 - annual_premium
104 - payment_years 103 - payment_years
105 - currency_type 104 - currency_type
...@@ -112,12 +111,14 @@ paths: ...@@ -112,12 +111,14 @@ paths:
112 - product_id 111 - product_id
113 - annual_increase_percentage 112 - annual_increase_percentage
114 - annual_withdrawal_amount 113 - annual_withdrawal_amount
114 + - coverage
115 x-apifox-orders: 115 x-apifox-orders:
116 - customer_name 116 - customer_name
117 - customer_gender 117 - customer_gender
118 - customer_age 118 - customer_age
119 - annual_premium 119 - annual_premium
120 - payment_years 120 - payment_years
121 + - coverage
121 - currency_type 122 - currency_type
122 - product_id 123 - product_id
123 - customer_birthday 124 - customer_birthday
...@@ -129,7 +130,23 @@ paths: ...@@ -129,7 +130,23 @@ paths:
129 - total_premium 130 - total_premium
130 - annual_withdrawal_amount 131 - annual_withdrawal_amount
131 - annual_increase_percentage 132 - annual_increase_percentage
132 - example: "{\r\n customer_name: '张三',\r\n customer_gender: 'male',\r\n customer_age: 30,\r\n annual_premium: 5000,\r\n payment_years: '10年',\r\n currency_type: 'CNY',\r\n product_id: 1\r\n }" 133 + example:
134 + customer_name: 张三
135 + customer_gender:
136 + customer_age: 30
137 + annual_premium: 5000
138 + payment_years: 10年
139 + currency_type: CNY
140 + product_id: 3151974
141 + customer_birthday: '2025-03-21'
142 + allow_reduce_amount:
143 + withdrawal_option: 指定提取金额
144 + withdrawal_start_age: 60
145 + withdrawal_period: 10
146 + smoking_status:
147 + total_premium: 0
148 + annual_withdrawal_amount: 6
149 + annual_increase_percentage: 33
133 responses: 150 responses:
134 '200': 151 '200':
135 description: '' 152 description: ''
...@@ -169,15 +186,13 @@ paths: ...@@ -169,15 +186,13 @@ paths:
169 x-apifox-ordering: 0 186 x-apifox-ordering: 0
170 security: [] 187 security: []
171 x-apifox-folder: 计划书 188 x-apifox-folder: 计划书
172 - x-apifox-status: developing 189 + x-apifox-status: released
173 x-run-in-apifox: https://app.apifox.com/web/project/7792797/apis/api-417310981-run 190 x-run-in-apifox: https://app.apifox.com/web/project/7792797/apis/api-417310981-run
174 components: 191 components:
175 schemas: {} 192 schemas: {}
176 responses: {} 193 responses: {}
177 securitySchemes: {} 194 securitySchemes: {}
178 -servers: 195 +servers: []
179 - - url: https://manulife.onwall.cn
180 - description: 正式环境
181 security: [] 196 security: []
182 197
183 -```\ 198 +```
......
...@@ -13,9 +13,9 @@ const Api = { ...@@ -13,9 +13,9 @@ const Api = {
13 * @param {Object} params 请求参数 13 * @param {Object} params 请求参数
14 * @param {string} params.customer_name 申请人 14 * @param {string} params.customer_name 申请人
15 * @param {string} params.customer_gender 性别 15 * @param {string} params.customer_gender 性别
16 - * @param {integer} params.customer_age (可选) 与 customer_birthday 二选一,至少提供一个 16 + * @param {integer} params.customer_age 年龄
17 - * @param {string} params.customer_birthday (可选) 与 customer_age 二选一,至少提供一个。格式:YYYY-MM-DD 17 + * @param {string} params.customer_birthday 出生年月日
18 - * @param {integer} params.annual_premium 保额 18 + * @param {integer} params.annual_premium 年缴保费
19 * @param {string} params.payment_years 繳費年期 19 * @param {string} params.payment_years 繳費年期
20 * @param {string} params.currency_type 币种 20 * @param {string} params.currency_type 币种
21 * @param {string} params.allow_reduce_amount 是否容许减少名义金额 21 * @param {string} params.allow_reduce_amount 是否容许减少名义金额
...@@ -27,6 +27,7 @@ const Api = { ...@@ -27,6 +27,7 @@ const Api = {
27 * @param {integer} params.product_id 产品id 27 * @param {integer} params.product_id 产品id
28 * @param {integer} params.annual_withdrawal_amount 每年提取金额 28 * @param {integer} params.annual_withdrawal_amount 每年提取金额
29 * @param {integer} params.annual_increase_percentage 每年递增提取百分比 29 * @param {integer} params.annual_increase_percentage 每年递增提取百分比
30 + * @param {string} params.coverage 保额
30 * @returns {Promise<{ 31 * @returns {Promise<{
31 * code: number; // 状态码 32 * code: number; // 状态码
32 * msg: string; // 消息 33 * msg: string; // 消息
......
...@@ -277,7 +277,8 @@ const submit = async () => { ...@@ -277,7 +277,8 @@ const submit = async () => {
277 gender: { api_field: 'customer_gender' }, 277 gender: { api_field: 'customer_gender' },
278 birthday: { api_field: 'customer_birthday' }, 278 birthday: { api_field: 'customer_birthday' },
279 smoker: { api_field: 'smoking_status' }, 279 smoker: { api_field: 'smoking_status' },
280 - coverage: { api_field: 'annual_premium', transform: 'fen_to_yuan' }, 280 + coverage: { api_field: 'coverage', transform: 'fen_to_yuan' },
281 + annual_premium: { api_field: 'annual_premium', transform: 'fen_to_yuan' },
281 payment_period: { api_field: 'payment_years' }, 282 payment_period: { api_field: 'payment_years' },
282 withdrawal_enabled: { api_field: 'allow_reduce_amount' }, 283 withdrawal_enabled: { api_field: 'allow_reduce_amount' },
283 withdrawal_mode: { api_field: 'withdrawal_option' }, 284 withdrawal_mode: { api_field: 'withdrawal_option' },
......
1 +import { describe, expect, it } from 'vitest'
2 +import { PLAN_TEMPLATES } from '../plan-templates'
3 +import { PLAN_FIELD_DEFINITIONS } from '../plan-fields'
4 +
5 +describe('plan template amount field mapping', () => {
6 + it('should map protection coverage to coverage api field', () => {
7 + expect(PLAN_TEMPLATES['life-insurance-wiop3e'].config.submit_mapping.coverage).toEqual({
8 + api_field: 'coverage',
9 + transform: 'fen_to_yuan'
10 + })
11 + })
12 +
13 + it('should use annual_premium field for savings schema and submit mapping', () => {
14 + const savingsConfig = PLAN_TEMPLATES['savings-gs'].config
15 + const annualPremiumField = savingsConfig.form_schema.base_fields.find(field => field.key === 'annual_premium')
16 +
17 + expect(annualPremiumField).toMatchObject({
18 + key: 'annual_premium',
19 + label: '年缴保费',
20 + type: 'amount'
21 + })
22 +
23 + expect(savingsConfig.submit_mapping.annual_premium).toEqual({
24 + api_field: 'annual_premium',
25 + transform: 'fen_to_yuan'
26 + })
27 +
28 + expect(savingsConfig.submit_mapping.coverage).toBeUndefined()
29 + })
30 +})
31 +
32 +describe('plan field definitions amount semantics', () => {
33 + it('should expose separate coverage and annual_premium definitions', () => {
34 + expect(PLAN_FIELD_DEFINITIONS.coverage).toMatchObject({
35 + label: '保额',
36 + api_field: 'coverage',
37 + transform: 'fen_to_yuan'
38 + })
39 +
40 + expect(PLAN_FIELD_DEFINITIONS.annual_premium).toMatchObject({
41 + label: '年缴保费',
42 + api_field: 'annual_premium',
43 + transform: 'fen_to_yuan'
44 + })
45 + })
46 +})
...@@ -51,6 +51,7 @@ export const TRANSFORM_TYPES = { ...@@ -51,6 +51,7 @@ export const TRANSFORM_TYPES = {
51 * @property {Object} birthday - 出生日期 51 * @property {Object} birthday - 出生日期
52 * @property {Object} smoker - 是否吸烟 52 * @property {Object} smoker - 是否吸烟
53 * @property {Object} coverage - 保额 53 * @property {Object} coverage - 保额
54 + * @property {Object} annual_premium - 年缴保费
54 * @property {Object} payment_period - 缴费年期 55 * @property {Object} payment_period - 缴费年期
55 * @property {Object} withdrawal_enabled - 是否启用提取 56 * @property {Object} withdrawal_enabled - 是否启用提取
56 * @property {Object} withdrawal_mode - 提取模式 57 * @property {Object} withdrawal_mode - 提取模式
...@@ -126,13 +127,13 @@ export const PLAN_FIELD_DEFINITIONS = { ...@@ -126,13 +127,13 @@ export const PLAN_FIELD_DEFINITIONS = {
126 }, 127 },
127 128
128 /** 129 /**
129 - * 保额(年缴) 130 + * 保额
130 */ 131 */
131 coverage: { 132 coverage: {
132 label: '保额', 133 label: '保额',
133 type: FIELD_TYPES.AMOUNT, 134 type: FIELD_TYPES.AMOUNT,
134 required: true, 135 required: true,
135 - api_field: 'annual_premium', 136 + api_field: 'coverage',
136 transform: TRANSFORM_TYPES.FEN_TO_YUAN, 137 transform: TRANSFORM_TYPES.FEN_TO_YUAN,
137 component: 'PlanFieldAmount', 138 component: 'PlanFieldAmount',
138 group: FIELD_GROUPS.COVERAGE, 139 group: FIELD_GROUPS.COVERAGE,
...@@ -145,6 +146,25 @@ export const PLAN_FIELD_DEFINITIONS = { ...@@ -145,6 +146,25 @@ export const PLAN_FIELD_DEFINITIONS = {
145 }, 146 },
146 147
147 /** 148 /**
149 + * 年缴保费
150 + */
151 + annual_premium: {
152 + label: '年缴保费',
153 + type: FIELD_TYPES.AMOUNT,
154 + required: true,
155 + api_field: 'annual_premium',
156 + transform: TRANSFORM_TYPES.FEN_TO_YUAN,
157 + component: 'PlanFieldAmount',
158 + group: FIELD_GROUPS.COVERAGE,
159 + placeholder: '请输入年缴保费',
160 + validation: {
161 + required: (value) => value > 0,
162 + min: (value, config) => value >= (config?.min_coverage || 1000),
163 + max: (value, config) => value <= (config?.max_coverage || 10000000)
164 + }
165 + },
166 +
167 + /**
148 * 缴费年期 168 * 缴费年期
149 */ 169 */
150 payment_period: { 170 payment_period: {
......
...@@ -43,7 +43,7 @@ const baseSubmitMapping = { ...@@ -43,7 +43,7 @@ const baseSubmitMapping = {
43 age: { api_field: 'customer_age' }, 43 age: { api_field: 'customer_age' },
44 birthday: { api_field: 'customer_birthday' }, 44 birthday: { api_field: 'customer_birthday' },
45 smoker: { api_field: 'smoking_status' }, 45 smoker: { api_field: 'smoking_status' },
46 - coverage: { api_field: 'annual_premium', transform: 'fen_to_yuan' }, 46 + coverage: { api_field: 'coverage', transform: 'fen_to_yuan' },
47 payment_period: { api_field: 'payment_years' }, 47 payment_period: { api_field: 'payment_years' },
48 total_amount: { api_field: 'total_premium', transform: 'fen_to_yuan' } 48 total_amount: { api_field: 'total_premium', transform: 'fen_to_yuan' }
49 } 49 }
...@@ -70,7 +70,7 @@ const savingsSubmitMapping = { ...@@ -70,7 +70,7 @@ const savingsSubmitMapping = {
70 age: { api_field: 'customer_age' }, 70 age: { api_field: 'customer_age' },
71 birthday: { api_field: 'customer_birthday' }, 71 birthday: { api_field: 'customer_birthday' },
72 // smoker: { api_field: 'smoking_status' }, // 储蓄类产品不需要吸烟字段 72 // smoker: { api_field: 'smoking_status' }, // 储蓄类产品不需要吸烟字段
73 - coverage: { api_field: 'annual_premium', transform: 'fen_to_yuan' }, 73 + annual_premium: { api_field: 'annual_premium', transform: 'fen_to_yuan' },
74 payment_period: { api_field: 'payment_years' }, 74 payment_period: { api_field: 'payment_years' },
75 total_amount: { api_field: 'total_premium', transform: 'fen_to_yuan' }, 75 total_amount: { api_field: 'total_premium', transform: 'fen_to_yuan' },
76 withdrawal_enabled: { api_field: 'allow_reduce_amount' }, 76 withdrawal_enabled: { api_field: 'allow_reduce_amount' },
...@@ -97,7 +97,7 @@ const savingsFormSchema = { ...@@ -97,7 +97,7 @@ const savingsFormSchema = {
97 // 年龄与出生年月日二选一填写 97 // 年龄与出生年月日二选一填写
98 { id: 'age', key: 'age', type: 'age', label: '年龄', placeholder: '请输入年龄', input_label: '岁', required: false }, 98 { id: 'age', key: 'age', type: 'age', label: '年龄', placeholder: '请输入年龄', input_label: '岁', required: false },
99 { id: 'birthday', key: 'birthday', type: 'date', label: '出生年月日', placeholder: '请选择年月日', required: false }, 99 { id: 'birthday', key: 'birthday', type: 'date', label: '出生年月日', placeholder: '请选择年月日', required: false },
100 - { id: 'coverage', key: 'coverage', type: 'amount', label: '年缴保费', placeholder: '请输入年缴保费', input_label: '请输入年缴保费金额', required: true, currency_from: 'currency' }, 100 + { id: 'annual_premium', key: 'annual_premium', type: 'amount', label: '年缴保费', placeholder: '请输入年缴保费', input_label: '请输入年缴保费金额', required: true, currency_from: 'currency' },
101 { id: 'payment_period', key: 'payment_period', type: 'payment_period', label: '缴费年期', required: true, options_from: 'payment_periods' } 101 { id: 'payment_period', key: 'payment_period', type: 'payment_period', label: '缴费年期', required: true, options_from: 'payment_periods' }
102 ], 102 ],
103 // 提取计划字段:由 withdrawal_plan 开关控制 103 // 提取计划字段:由 withdrawal_plan 开关控制
......