config-generator.js
12.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
/**
* 计划书配置生成器
*
* @description 将提取的数据转换为 plan-templates.js 的配置格式
* @file utils/parsers/config-generator
* @author Claude Code
* @created 2026-02-13
*/
import { generateFormSn, validateConfig } from './ai-extractor'
/**
* 生成完整的配置代码
*
* @description 将提取的配置数据转换为可写入 plan-templates.js 的代码
* @param {Object} extractedConfig - 从文档提取的配置
* @param {Object} options - 生成选项
* @param {string} options.formSn - 自定义 form_sn(可选)
* @param {boolean} options.includeComment - 是否包含注释(默认 true)
* @returns {Object} { formSn: string, configCode: string, insertPosition: string }
*
* @example
* const result = generateConfigCode({
* product_name: '宏挚传承保障计划',
* product_type: 'savings',
* ...
* })
* // 返回: { formSn: 'savings-xxx', configCode: '...', insertPosition: '...' }
*/
export function generateConfigCode(extractedConfig, options = {}) {
const { formSn: customFormSn, includeComment = true } = options
// 验证配置
const validation = validateConfig(extractedConfig)
if (!validation.valid) {
throw new Error(`配置验证失败:\n${validation.errors.join('\n')}`)
}
// 生成 form_sn
const formSn = customFormSn || generateFormSn(
extractedConfig.product_name,
extractedConfig.product_type
)
// 根据产品类型生成配置
let configCode = ''
if (extractedConfig.is_savings) {
configCode = generateSavingsConfig(extractedConfig, formSn, includeComment)
} else if (extractedConfig.product_type === 'life-insurance') {
configCode = generateLifeInsuranceConfig(extractedConfig, formSn, includeComment)
} else if (extractedConfig.product_type === 'critical-illness') {
configCode = generateCriticalIllnessConfig(extractedConfig, formSn, includeComment)
} else {
throw new Error(`不支持的产品类型: ${extractedConfig.product_type}`)
}
// 生成插入位置提示
const insertPosition = generateInsertPositionHint(extractedConfig)
return {
formSn,
configCode,
insertPosition,
validation
}
}
/**
* 生成储蓄型产品配置
*
* @private
*/
function generateSavingsConfig(config, formSn, includeComment) {
const comment = includeComment ? `
// ${config.product_name}
// form_sn: ${formSn}
// 提取方式: ${config.withdrawal_modes.join(', ')}
// 提取周期: ${config.withdrawal_periods.join(', ')}` : ''
const paymentPeriodsArray = JSON.stringify(config.payment_periods, null, 2)
const withdrawalModesArray = JSON.stringify(config.withdrawal_modes, null, 2)
const withdrawalPeriodsArray = JSON.stringify(config.withdrawal_periods, null, 2)
const { form_schema_ref, submit_mapping_ref } = resolveSchemaRefs(config)
const form_schema_code = buildSchemaCode(config.form_schema, form_schema_ref)
const submit_mapping_code = buildSchemaCode(config.submit_mapping, submit_mapping_ref)
return ` '${formSn}': {${comment}
name: '${config.product_name}',
component: 'SavingsTemplate',
category: 'savings',
config: {
currency: '${config.currency}',
payment_periods: ${paymentPeriodsArray},
age_range: { min: ${config.age_range.min}, max: ${config.age_range.max} },
insurance_period: '${config.insurance_period}',
withdrawal_plan: {
enabled: true,
currencies: ['HKD', 'USD', 'CNY'],
default_currency: '${config.currency}',
withdrawal_modes: ${withdrawalModesArray},
withdrawal_periods: ${withdrawalPeriodsArray}
},
form_schema: ${form_schema_code},
submit_mapping: ${submit_mapping_code}
}
}`
}
/**
* 生成人寿保险产品配置
*
* @private
*/
function generateLifeInsuranceConfig(config, formSn, includeComment) {
const comment = includeComment ? `
// ${config.product_name}
// form_sn: ${formSn}` : ''
const paymentPeriodsArray = JSON.stringify(config.payment_periods, null, 2)
const { form_schema_ref, submit_mapping_ref } = resolveSchemaRefs(config)
const form_schema_code = buildSchemaCode(config.form_schema, form_schema_ref)
const submit_mapping_code = buildSchemaCode(config.submit_mapping, submit_mapping_ref)
return ` '${formSn}': {${comment}
name: '${config.product_name}',
component: 'LifeInsuranceTemplate',
config: {
currency: '${config.currency}',
payment_periods: ${paymentPeriodsArray},
age_range: { min: ${config.age_range.min}, max: ${config.age_range.max} },
insurance_period: '${config.insurance_period}',
form_schema: ${form_schema_code},
submit_mapping: ${submit_mapping_code}
}
}`
}
/**
* 生成重疾保险产品配置
*
* @private
*/
function generateCriticalIllnessConfig(config, formSn, includeComment) {
const comment = includeComment ? `
// ${config.product_name}
// form_sn: ${formSn}` : ''
const paymentPeriodsArray = JSON.stringify(config.payment_periods, null, 2)
const { form_schema_ref, submit_mapping_ref } = resolveSchemaRefs(config)
const form_schema_code = buildSchemaCode(config.form_schema, form_schema_ref)
const submit_mapping_code = buildSchemaCode(config.submit_mapping, submit_mapping_ref)
return ` '${formSn}': {${comment}
name: '${config.product_name}',
component: 'CriticalIllnessTemplate',
config: {
currency: '${config.currency}',
payment_periods: ${paymentPeriodsArray},
age_range: { min: ${config.age_range.min}, max: ${config.age_range.max} },
insurance_period: '${config.insurance_period}',
form_schema: ${form_schema_code},
submit_mapping: ${submit_mapping_code}
}
}`
}
/**
* 生成插入位置提示
*
* @private
* @param {Object} config - 配置对象
* @returns {string} 插入位置说明
*/
function generateInsertPositionHint(config) {
if (config.is_savings) {
return '// 插入到 PLAN_TEMPLATES 对象中的储蓄型产品部分(搜索 "// ====== 储蓄型产品")'
} else if (config.product_type === 'life-insurance') {
return '// 插入到 PLAN_TEMPLATES 对象中的人寿保险部分(搜索 "// 人寿保险产品")'
} else if (config.product_type === 'critical-illness') {
return '// 插入到 PLAN_TEMPLATES 对象中的重疾保险部分(搜索 "// 重疾保险产品")'
}
return '// 插入到 PLAN_TEMPLATES 对象中'
}
/**
* 生成完整的导出代码(包含导入和导出)
*
* @param {Object} config - 提取的配置
* @param {Object} options - 选项
* @returns {string} 完整的模块代码
*/
export function generateFullModuleCode(config, options = {}) {
const { configCode, formSn } = generateConfigCode(config, options)
return `/**
* 新增产品配置
* @description ${config.product_name}
* @created ${new Date().toISOString()}
*/
// 在 PLAN_TEMPLATES 中添加以下配置:
${configCode}
/**
* 如果需要导出,在文件底部的 export 中添加:
*/
export const ${toCamelCase(formSn)} = PLAN_TEMPLATES['${formSn}']
`
}
/**
* 转换为驼峰命名
*
* @private
*/
function toCamelCase(str) {
return str
.replace(/[-_\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '')
.replace(/^(.)/, (c) => c.toLowerCase())
}
function resolveSchemaRefs(config) {
const isSavings = config?.is_savings || config?.product_type === 'savings'
if (isSavings) {
return {
form_schema_ref: 'savingsFormSchema',
submit_mapping_ref: 'savingsSubmitMapping'
}
}
return {
form_schema_ref: 'protectionFormSchema',
submit_mapping_ref: 'baseSubmitMapping'
}
}
/**
* 构建表单 Schema 代码
*
* @description 将 schema 对象转换为代码字符串
* @param {Object|string} value - schema 值
* @param {string} fallbackRef - 回退引用名
* @returns {string} 代码字符串
*
* @updated 2026-02-15 - 支持新的条件格式 show_when: { field, op, value }
*/
function buildSchemaCode(value, fallbackRef) {
if (!value || isEmptyObject(value)) {
return fallbackRef
}
if (value && typeof value === 'object' && !Array.isArray(value)) {
const baseFields = value.base_fields
const withdrawalFields = value.withdrawal_fields
const baseFieldsEmpty = Array.isArray(baseFields) && baseFields.length === 0
const withdrawalFieldsEmpty = !Array.isArray(withdrawalFields) || withdrawalFields.length === 0
if (baseFieldsEmpty && withdrawalFieldsEmpty) {
return fallbackRef
}
// 处理字段中的 show_when 格式转换
const processedValue = processSchemaFields(value)
return JSON.stringify(processedValue, null, 2).replace(/\n/g, '\n ')
}
if (typeof value === 'string') {
return value
}
return JSON.stringify(value, null, 2).replace(/\n/g, '\n ')
}
/**
* 处理 Schema 字段,转换条件格式
*
* @private
*/
function processSchemaFields(schema) {
const result = {}
for (const [key, value] of Object.entries(schema)) {
if (key === 'base_fields' || key === 'withdrawal_fields') {
result[key] = value.map(field => processFieldCondition(field))
} else if (key !== 'reset_map') {
// 忽略 reset_map,不再生成
result[key] = value
}
}
return result
}
/**
* 处理字段条件,转换为新格式
*
* @private
*/
function processFieldCondition(field) {
const result = { ...field }
// 转换 show_when: { field, equals } -> { field, op: 'eq', value }
if (result.show_when && result.show_when.equals !== undefined) {
result.show_when = {
field: result.show_when.field,
op: 'eq',
value: result.show_when.equals
}
}
return result
}
function isEmptyObject(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) {
return false
}
return Object.keys(value).length === 0
}
/**
* 批量生成配置代码
*
* @param {Array} configs - 配置数组
* @returns {string} 批量配置代码
*/
export function generateBatchConfigCode(configs) {
const result = configs.map((config, index) => {
const { configCode } = generateConfigCode(config, {
formSn: `batch-product-${Date.now()}-${index}`,
includeComment: true
})
return configCode
})
return result.join(',\n\n')
}
/**
* 预览配置数据(用于确认)
*
* @param {Object} config - 配置对象
* @returns {Object} 格式化的预览数据
*/
export function previewConfig(config) {
return {
'产品名称': config.product_name,
'产品类型': getProductTypeLabel(config.product_type),
'币种': config.currency,
'缴费年期': config.payment_periods?.join('、') || '-',
'年龄范围': `${config.age_range?.min || 0} - ${config.age_range?.max || 75} 岁`,
'保险期间': config.insurance_period,
'是否储蓄型': config.is_savings ? '是' : '否',
'提取方式': config.withdrawal_modes?.join('、') || '-',
'提取周期': config.withdrawal_periods?.join('、') || '-'
}
}
/**
* 获取产品类型标签
*
* @private
*/
function getProductTypeLabel(type) {
const labels = {
'life-insurance': '人寿保险',
'critical-illness': '重疾保险',
'savings': '储蓄型'
}
return labels[type] || type
}
/**
* 生成差异对比(新配置 vs 现有配置)
*
* @param {Object} newConfig - 新配置
* @param {Object} existingConfig - 现有配置
* @returns {Object} 差异对象
*/
export function compareConfigs(newConfig, existingConfig) {
const differences = {
added: [],
removed: [],
changed: []
}
// 比较缴费年期
if (newConfig.payment_periods && existingConfig.payment_periods) {
const newPeriods = newConfig.payment_periods.filter(p => !existingConfig.payment_periods.includes(p))
const removedPeriods = existingConfig.payment_periods.filter(p => !newConfig.payment_periods.includes(p))
if (newPeriods.length > 0) differences.changed.push(`新增缴费年期: ${newPeriods.join(', ')}`)
if (removedPeriods.length > 0) differences.changed.push(`移除缴费年期: ${removedPeriods.join(', ')}`)
}
// 比较年龄范围
if (newConfig.age_range && existingConfig.age_range) {
if (newConfig.age_range.min !== existingConfig.age_range.min ||
newConfig.age_range.max !== existingConfig.age_range.max) {
differences.changed.push(`年龄范围: ${existingConfig.age_range.min}-${existingConfig.age_range.max} → ${newConfig.age_range.min}-${newConfig.age_range.max}`)
}
}
return differences
}