plan-backend-migration-guide.md 34.7 KB

计划书配置架构与后端迁移指南

文档目的:详细说明计划书配置生成逻辑,为后端迁移提供完整的技术参考

作者:Claude Code 创建日期:2026-02-25 版本:1.0.0


目录

  1. 架构概览
  2. 核心数据结构
  3. 配置生成流程
  4. 后端迁移方案
  5. API 设计建议
  6. 数据模型设计

架构概览

整体架构图

┌─────────────────────────────────────────────────────────────────────────┐
│                              前端(当前架构)                              │
├─────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                        配置层 (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" },
        "coverage": { "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" },
    "coverage": { "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 不转换 -

相关文档