plan-backend-migration-guide.md
34.7 KB
计划书配置架构与后端迁移指南
文档目的:详细说明计划书配置生成逻辑,为后端迁移提供完整的技术参考
作者:Claude Code 创建日期:2026-02-25 版本:1.0.0
目录
架构概览
整体架构图
┌─────────────────────────────────────────────────────────────────────────┐
│ 前端(当前架构) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 配置层 (Config) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ plan-templates.js - 产品模板配置 │ │ │
│ │ │ ├─ 产品映射: form_sn → TemplateConfig │ │ │
│ │ │ ├─ 基础 Schema: protectionFormSchema, savingsFormSchema │ │ │
│ │ │ └─ 字段映射: baseSubmitMapping, savingsSubmitMapping │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ plan-fields.js - 字段定义库 │ │ │
│ │ │ ├─ 字段类型: TEXT, AMOUNT, DATE, RADIO, SELECT... │ │ │
│ │ │ ├─ 字段分组: BASIC, COVERAGE, WITHDRAWAL │ │ │
│ │ │ └─ 验证规则: required, min, max, range │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 容器层 (Container) │ │
│ │ │ │
│ │ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ │ PlanFormContainer.vue │ │ │
│ │ │ ├─ 接收产品对象 (product.form_sn) │ │ │
│ │ │ ├─ 匹配模板配置 (getTemplateConfig) │ │ │
│ │ │ ├─ 合并后端配置 (product.plan_config) │ │ │
│ │ │ └─ 动态加载模板组件 │ │ │
│ │ └──────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 模板层 (Template) │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │ │
│ │ │ LifeInsurance │ │ CriticalIllness │ │ Savings │ │ │
│ │ │ Template.vue │ │ Template.vue │ │ Template.vue │ │ │
│ │ │ │ │ │ │ │ │ │
│ │ │ - 基础字段 │ │ - 基础字段 │ │ - 基础字段 │ │ │
│ │ │ - 保障配置 │ │ - 保障配置 │ │ - 提取计划 │ │ │
│ │ └──────────────────┘ └──────────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 字段组件层 (Fields) │ │
│ │ │ │
│ │ NameInput │ AmountKeyboard │ DatePicker │ Radio │ AgePicker │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
│
│ 提交订单
▼
┌─────────────────────────────────────────────────────────────────────────┐
│ 后端 API │
│ /srv/?a=proposal&t=add │
│ - customer_name │
│ - customer_gender │
│ - annual_premium (需要 fen→yuan 转换) │
│ - payment_years │
│ - withdrawal_option │
│ ... │
└─────────────────────────────────────────────────────────────────────────┘
数据流图
产品列表 API
│
│ { id, product_name, form_sn, plan_config? }
▼
┌─────────────────────────────────────────────────────────────┐
│ PlanFormContainer │
│ │
│ 1. 根据 form_sn 从 plan-templates.js 获取配置 │
│ 2. 合并 product.plan_config (后端覆盖) │
│ 3. 动态加载对应 Template 组件 │
└─────────────────────────────────────────────────────────────┘
│
│ { config: { currency, payment_periods, form_schema, ... } }
▼
┌─────────────────────────────────────────────────────────────┐
│ Template Component (LifeInsuranceTemplate 等) │
│ │
│ 1. 解析 form_schema.base_fields 和 withdrawal_fields │
│ 2. 根据 field.type 渲染对应的字段组件 │
│ 3. 使用 useFieldDependencies 管理字段联动 │
└─────────────────────────────────────────────────────────────┘
│
│ 用户填写表单
│ v-model 双向绑定
▼
┌─────────────────────────────────────────────────────────────┐
│ 表单数据 formData │
│ { customer_name, gender, birthday, coverage, ... } │
└─────────────────────────────────────────────────────────────┘
│
│ 点击"生成计划书"
▼
┌─────────────────────────────────────────────────────────────┐
│ submit() 方法 │
│ │
│ 1. 调用 template.validate() 校验表单 │
│ 2. 根据 submit_mapping 映射字段 │
│ 3. 执行值转换 (fen_to_yuan) │
│ 4. 调用 addAPI(formData) │
└─────────────────────────────────────────────────────────────┘
核心数据结构
1. TemplateConfig(模板配置)
// 位置: src/config/plan-templates.js
const PLAN_TEMPLATES = {
'savings-gs': { // form_sn: 产品唯一标识
name: '宏挚传承保障计划', // 产品名称
component: 'SavingsTemplate', // 模板组件名
category: 'savings', // 产品分类
config: { // 模板配置对象
// === 基础配置 ===
currency: 'USD', // 默认币种
payment_periods: [ // 缴费年期选项
'整付', '3 年', '5 年', '10 年', '15 年'
],
age_range: { min: 0, max: 100 }, // 投保年龄范围
insurance_period: '终身', // 保险期间
// === 提取计划配置(储蓄型产品) ===
withdrawal_plan: {
enabled: true, // 是否启用提取计划
currencies: ['HKD', 'USD', 'CNY'], // 支持的币种
default_currency: 'USD', // 默认币种
withdrawal_modes: [ // 提取模式
'指定提取金额',
'最高固定提取金额'
],
withdrawal_periods: [ // 提取年期
'1年', '2年', '3年', '5年',
'10年', '15年', '20年', '终身'
]
},
// === 表单 Schema ===
form_schema: savingsFormSchema, // 见下方详细说明
// === 提交字段映射 ===
submit_mapping: savingsSubmitMapping // 见下方详细说明
}
}
}
2. FormSchema(表单结构定义)
// 位置: src/config/plan-templates.js
const savingsFormSchema = {
// 基础字段:所有产品都有的字段
base_fields: [
{
id: 'customer_name', // 唯一标识
key: 'customer_name', // formData 中的键名
type: 'name', // 字段类型
label: '申请人', // 显示标签
placeholder: '请输入申请人', // 占位符
required: true // 是否必填
},
{
id: 'gender',
key: 'gender',
type: 'radio', // 单选框
label: '性别',
options: ['男', '女'], // 选项列表
required: true
},
{
id: 'birthday',
key: 'birthday',
type: 'date', // 日期选择器
label: '出生年月日',
placeholder: '请选择年月日',
required: true
},
{
id: 'coverage',
key: 'coverage',
type: 'amount', // 金额输入(键盘)
label: '年缴保费',
placeholder: '请输入年缴保费',
input_label: '请输入年缴保费金额',
required: true,
currency_from: 'currency' // 币种来源: config.currency
},
{
id: 'payment_period',
key: 'payment_period',
type: 'payment_period', // 缴费年期(专用组件)
label: '缴费年期',
required: true,
options_from: 'payment_periods' // 选项来源: config.payment_periods
}
],
// 提取计划字段:储蓄型产品特有
withdrawal_fields: [
{
id: 'withdrawal_enabled',
key: 'withdrawal_enabled',
type: 'radio',
label: '是否希望生成一份允许减少名义金额的提取说明?',
options: ['是', '否'],
required: true,
default: '否'
},
{
id: 'withdrawal_mode',
key: 'withdrawal_mode',
type: 'radio',
label: '提取选项',
options: ['指定提取金额', '最高固定提取金额'],
required: true,
default: '指定提取金额',
section_title: '款项提取(允许减少名义金额)',
clear_when_hidden: true, // 隐藏时清空值
// === 条件显示规则 ===
show_when: {
field: 'withdrawal_enabled', // 依赖字段
op: 'eq', // 操作符: eq/ne/gt/lt/in
value: '是' // 比较值
}
},
{
id: 'withdrawal_method',
key: 'withdrawal_method',
type: 'radio',
label: '提取方式',
options: ['按年岁'],
required: true,
default: '按年岁',
show_when: {
field: 'withdrawal_mode',
op: 'eq',
value: '指定提取金额'
},
clear_when_hidden: true
},
{
id: 'annual_withdrawal_amount',
key: 'annual_withdrawal_amount',
type: 'amount',
label: '每年提取金额',
placeholder: '请输入每年提取金额',
input_label: '请输入每年提取金额',
required: true,
currency_from: 'withdrawal_plan.default_currency',
show_when: {
field: 'withdrawal_mode',
op: 'eq',
value: '指定提取金额'
},
clear_when_hidden: true
},
// ... 更多字段
]
}
3. SubmitMapping(提交字段映射)
// 位置: src/config/plan-templates.js
const savingsSubmitMapping = {
// 基础字段映射
customer_name: {
api_field: 'customer_name' // API 字段名
},
gender: {
api_field: 'customer_gender'
},
birthday: {
api_field: 'customer_birthday'
},
smoker: {
api_field: 'smoking_status'
},
coverage: {
api_field: 'annual_premium', // API 字段名
transform: 'fen_to_yuan' // 值转换: 分→元
},
payment_period: {
api_field: 'payment_years'
},
// 提取计划字段映射
withdrawal_enabled: {
api_field: 'allow_reduce_amount'
},
withdrawal_mode: {
api_field: 'withdrawal_option'
},
annual_withdrawal_amount: {
api_field: 'annual_withdrawal_amount',
transform: 'fen_to_yuan'
},
// 指定提取金额模式的特殊字段
withdrawal_start_age_specified: {
api_field: 'withdrawal_start_age' // 根据模式动态选择
},
withdrawal_period_specified: {
api_field: 'withdrawal_period'
},
// 最高固定提取金额模式的特殊字段
withdrawal_start_age_fixed: {
api_field: 'withdrawal_start_age'
},
withdrawal_period_fixed: {
api_field: 'withdrawal_period'
}
}
4. FieldDefinitions(字段定义)
// 位置: src/config/plan-fields.js
export const PLAN_FIELD_DEFINITIONS = {
customer_name: {
label: '申请人',
type: FIELD_TYPES.TEXT,
required: true,
api_field: 'customer_name',
component: 'PlanFieldName',
group: FIELD_GROUPS.BASIC,
validation: {
required: (value) => value?.trim()?.length >= 2
}
},
coverage: {
label: '保额',
type: FIELD_TYPES.AMOUNT,
required: true,
api_field: 'annual_premium',
transform: TRANSFORM_TYPES.FEN_TO_YUAN,
component: 'PlanFieldAmount',
group: FIELD_GROUPS.COVERAGE,
placeholder: '请输入保额',
validation: {
required: (value) => value > 0,
min: (value, config) => value >= (config?.min_coverage || 1000),
max: (value, config) => value <= (config?.max_coverage || 10000000)
}
},
// ... 更多字段定义
}
配置生成流程
1. 文档解析生成配置(当前工具)
┌─────────────────────────────────────────────────────────────────────────┐
│ 文档解析流程 │
│ │
│ 产品文档 (PDF/Word) │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ AI 文档解析器 (/admin/document-parser/index) │ │
│ │ ├─ 豆包 AI / OpenAI API │ │
│ │ ├─ 智能字段提取 │ │
│ │ └─ 生成结构化配置 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ 提取的配置数据 │ │
│ │ { │ │
│ │ product_name: "宏挚传承保障计划", │ │
│ │ product_type: "savings", │ │
│ │ currency: "USD", │ │
│ │ payment_periods: ["整付", "3 年", ...], │ │
│ │ form_schema: { base_fields: [...], withdrawal_fields: [...] }│ │
│ │ } │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ config-generator.js - 配置代码生成器 │ │
│ │ ├─ generateConfigCode() - 生成配置代码 │ │
│ │ ├─ generateSavingsConfig() - 生成储蓄型配置 │ │
│ │ └─ buildSchemaCode() - 构建 Schema 代码 │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 配置代码(复制到 plan-templates.js) │
└─────────────────────────────────────────────────────────────────────────┘
2. 运行时配置加载流程
// PlanFormContainer.vue 中的关键代码
// 1. 根据 form_sn 获取模板配置
const templateConfig = computed(() => {
const config = PLAN_TEMPLATES[props.product.form_sn]
// 2. 合并后端返回的 plan_config(覆盖默认配置)
return {
...config.config, // 默认配置
...config, // 顶层属性(name, component 等)
...(props.product.plan_config || {}) // 后端动态配置
}
})
// 3. 动态加载模板组件
const currentTemplateComponent = computed(() => {
const componentMap = {
'LifeInsuranceTemplate': LifeInsuranceTemplate,
'CriticalIllnessTemplate': CriticalIllnessTemplate,
'SavingsTemplate': SavingsTemplate
}
return componentMap[templateConfig.value.component]
})
后端迁移方案
方案对比
| 方案 | 优势 | 劣势 | 推荐度 |
|---|---|---|---|
| 方案 A:完全后端化 | 前端代码最少,配置集中管理 | 后端复杂度高,需要维护表单渲染引擎 | ⭐⭐⭐ |
| 方案 B:配置 API 化 | 前端保留灵活性,后端只管理配置 | 前端仍需维护模板组件 | ⭐⭐⭐⭐⭐ |
| 方案 C:混合模式 | 平衡前后端职责,渐进式迁移 | 架构稍复杂 | ⭐⭐⭐⭐ |
推荐方案:方案 B(配置 API 化)
┌─────────────────────────────────────────────────────────────────────────┐
│ 迁移后架构(方案 B) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 后端配置管理 │ │
│ │ │ │
│ │ 产品配置表 (product_configs) │ │
│ │ ├─ form_sn │ │
│ │ ├─ form_schema (JSON) │ │
│ │ ├─ submit_mapping (JSON) │ │
│ │ └─ metadata (currency, age_range, ...) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ GET /api/plan/config/:form_sn │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 前端配置缓存 │ │
│ │ │ │
│ │ Pinia Store (planConfigStore) │ │
│ │ ├─ configs: Map<form_sn, TemplateConfig> │ │
│ │ ├─ fetchConfig(formSn) - 拉取配置 │ │
│ │ └─ prefetchAll() - 预加载所有配置 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ PlanFormContainer │ │
│ │ (逻辑不变,只是配置来源变了) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Template Components │ │
│ │ (保持不变) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
API 设计建议
1. 获取计划书配置
GET /api/plan/config/:form_sn
Response:
{
"code": 1,
"data": {
"form_sn": "savings-gs",
"name": "宏挚传承保障计划",
"component": "SavingsTemplate",
"config": {
"currency": "USD",
"payment_periods": ["整付", "3 年", "5 年"],
"age_range": { "min": 0, "max": 100 },
"insurance_period": "终身",
"withdrawal_plan": {
"enabled": true,
"currencies": ["HKD", "USD", "CNY"],
"default_currency": "USD",
"withdrawal_modes": ["指定提取金额", "最高固定提取金额"],
"withdrawal_periods": ["1年", "2年", "3年"]
},
"form_schema": {
"base_fields": [
{
"id": "customer_name",
"key": "customer_name",
"type": "name",
"label": "申请人",
"placeholder": "请输入申请人",
"required": true
}
// ... 更多字段
],
"withdrawal_fields": [
// ... 提取计划字段
]
},
"submit_mapping": {
"customer_name": { "api_field": "customer_name" },
"annual_premium": { "api_field": "annual_premium", "transform": "fen_to_yuan" }
// ... 更多映射
}
}
}
}
2. 批量获取配置(预加载)
GET /api/plan/config/batch?form_sn[]=savings-gs&form_sn[]=life-insurance-wiop3e
Response:
{
"code": 1,
"data": {
"savings-gs": { /* 配置对象 */ },
"life-insurance-wiop3e": { /* 配置对象 */ }
}
}
3. 创建计划书(保持不变)
POST /srv/?a=proposal&t=add
Request:
{
"product_id": 123,
"customer_name": "张三",
"customer_gender": "male",
"annual_premium": 100000, // 单位:元
"payment_years": "5 年",
"withdrawal_option": "指定提取金额",
"allow_reduce_amount": true
// ... 其他字段
}
数据模型设计
product_configs 表
CREATE TABLE product_configs (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
form_sn VARCHAR(100) UNIQUE NOT NULL COMMENT '产品表单标识',
name VARCHAR(200) NOT NULL COMMENT '产品名称',
component VARCHAR(50) NOT NULL COMMENT '模板组件名',
category VARCHAR(50) COMMENT '产品分类',
-- 基础配置 (JSON)
config JSON NOT NULL COMMENT '模板配置',
-- 元数据 (冗余字段,方便查询)
currency VARCHAR(10) COMMENT '默认币种',
age_min INT COMMENT '最小投保年龄',
age_max INT COMMENT '最大投保年龄',
insurance_period VARCHAR(50) COMMENT '保险期间',
-- 时间戳
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_form_sn (form_sn),
INDEX idx_category (category)
) COMMENT='计划书产品配置表';
config JSON 结构
{
"currency": "USD",
"payment_periods": ["整付", "3 年", "5 年"],
"age_range": { "min": 0, "max": 100 },
"insurance_period": "终身",
"withdrawal_plan": {
"enabled": true,
"currencies": ["HKD", "USD", "CNY"],
"default_currency": "USD",
"withdrawal_modes": ["指定提取金额", "最高固定提取金额"],
"withdrawal_periods": ["1年", "2年", "3年"]
},
"form_schema": {
"base_fields": [
{
"id": "customer_name",
"key": "customer_name",
"type": "name",
"label": "申请人",
"placeholder": "请输入申请人",
"required": true
}
],
"withdrawal_fields": []
},
"submit_mapping": {
"customer_name": { "api_field": "customer_name" },
"annual_premium": { "api_field": "annual_premium", "transform": "fen_to_yuan" }
}
}
迁移步骤建议
阶段 1:后端配置存储(1-2 周)
-
创建
product_configs表 - 迁移现有配置到数据库
- 实现配置 CRUD API
阶段 2:前端配置 API 集成(1 周)
-
创建
planConfigStore -
修改
PlanFormContainer使用 API 配置 - 添加配置缓存和预加载逻辑
阶段 3:配置管理后台(1-2 周)
- 开发配置管理界面
- 支持可视化配置表单字段
- 支持配置预览和测试
阶段 4:文档解析集成(1 周)
- 将文档解析器连接到配置 API
- 支持自动创建和更新配置
附录
A. 字段类型与组件映射表
| field.type | 组件 | 说明 |
|---|---|---|
name |
PlanFieldName | 姓名输入(特殊验证) |
text |
nut-input | 普通文本输入 |
amount |
PlanFieldAmount | 金额键盘输入 |
percentage |
nut-input | 百分比输入(0-100) |
date |
PlanFieldDatePicker | 日期选择器 |
age |
PlanFieldAgePicker | 年龄选择器 |
radio |
PlanFieldRadio | 单选按钮组 |
select |
PlanFieldSelect | 下拉选择器 |
payment_period |
PaymentPeriodRadio | 缴费年期(专用) |
B. 条件操作符
| 操作符 | 说明 | 示例 |
|---|---|---|
eq |
等于 | { field: 'gender', op: 'eq', value: 'male' } |
ne |
不等于 | { field: 'gender', op: 'ne', value: 'male' } |
gt |
大于 | { field: 'age', op: 'gt', value: 18 } |
lt |
小于 | { field: 'age', op: 'lt', value: 65 } |
in |
包含于 | { field: 'type', op: 'in', value: ['A', 'B'] } |
C. 值转换类型
| transform | 说明 | 方向 |
|---|---|---|
fen_to_yuan |
分 → 元 | 提交时 |
yuan_to_fen |
元 → 分 | 回显时 |
none |
不转换 | - |