baodan/docs/PPT与海报功能完整解决方案.md

876 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 建议书PPT+ 海报功能 完整解决方案(终版)
> 更新时间2026-07-23
> 状态:需求已确认,可进入实施阶段
## Context
当前系统已有完整的 PPT 生成管线PDF 上传 → AI 解析 → 归一化 → 渲染 PPTX但保司/产品/模板数据仍以 JSON 文件 + 数据库种子形式存在,管理后台缺少对这些数据的 CRUD 界面。同时系统完全没有海报功能。
本次需求涉及两大模块:
1. **PPT 功能增强**:前端适配、历史记录、保司/产品/模板的后台可配置化
2. **海报功能(全新)**:保司小册子管理、计划书上传解析、文案生成(模板/AI 双模式)、海报模板、导出
**核心约束**:不修改当前可正常运行的功能,所有改动以增量方式添加。
---
## 一、已确认决策清单
| # | 项目 | 决策 | 备注 |
|---|------|------|------|
| 1 | 移动端适配 | 响应式适配,同一套代码 | 复用现有 responsive.css + useMobile() |
| 2 | 海报渲染引擎 | **纯 AI 生图**GPT image 模型) | 已有 OpenAI API Key |
| 3 | 文案生成 LLM | 复用现有 DeepSeek/MiniMax/Gemini | 多供应商 + 自动降级 |
| 4 | 历史记录保存 | 管理员可删除,保存期限可配置 | 后台提供配置项 |
| 5 | AI 文案审批 | 用户自行确认即可 | 无需额外审批流程 |
| 6 | 风格自由度 | 支持自定义参考图 + 配色 | 管理员上传参考图 + 选择配色方案 |
| 7 | product_type | 保持 savings/ci/iul 三种 | 预留 VARCHAR 扩展 |
| 8 | 产品字段 | 先用现有字段 + 按需扩展 | 不做大规模字段新增 |
| 9 | 场景化维度 | 自由标签 | 管理员自定义标签,不限固定维度 |
| 10 | 模板产品关联 | 模板独立于产品 | AI 根据产品数据自动适配 |
| 11 | 导出尺寸 | 支持自定义,预设 4 种 | 1080x1920 / 900x500 / 1080x1080 / 800x1200 |
---
## 二、现状分析
### 2.1 PPT 功能现状
| 子功能 | 当前状态 | 需求目标 | 差距 |
|--------|---------|---------|------|
| 前端适配 | PptPage.vue 4 步向导已存在Element Plus + responsive.css 有基础响应式 | 移动端全流程无障碍、骨架屏、手势支持 | 需补充骨架屏、触屏优化、进度反馈 |
| 历史记录 | 无PptSession 仅存当前会话 | 按用户留存完整历史 | 需新建表 + API + 前端页面 |
| 保司配置 | PptCompany 模型已存在,数据由 migration_014 从 JSON 种子导入 | 后台可增删改查 | 需新增 admin CRUD 接口 + 前端管理页 |
| 产品配置 | PptProduct 模型已存在,同上 | 后台可增删改查 + 海报所需扩展字段 | 需扩展字段 + admin CRUD + 前端管理页 |
| 模板配置 | PptTemplate 模型已存在,同上 | 场景化模板管理 | 需扩展字段(场景标签、预览图、适用范围) |
### 2.2 海报功能现状
完全不存在,需从零建设。可复用:
- PPT 模块的 LLM 客户端(`ppt/llm_client.py`)用于 AI 文案生成
- PPT 模块的 PDF 解析能力(`ppt/extraction.py`)用于计划书解析
- 现有认证/权限/审计中间件
- 现有文件上传模式
---
## 三、数据库设计(新增 5 张表 + 扩展 3 张表)
### 3.1 扩展现有表:`insurance_ppt_companies`
新增字段(不改已有字段):
| 字段 | 类型 | 说明 |
|------|------|------|
| logo_url | VARCHAR(500) | 公司 Logo 图片地址 |
| status | SMALLINT DEFAULT 1 | 1=启用, 0=停用 |
| sort_order | INTEGER DEFAULT 0 | 排序权重 |
### 3.2 扩展现有表:`insurance_ppt_products`
新增字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| product_code | VARCHAR(50) | 产品编码(供外部引用) |
| product_type | VARCHAR(20) | 产品类型savings/ci/iulVARCHAR 预留扩展) |
| coverage_period | VARCHAR(50) | 保障期限 |
| payment_period | VARCHAR(50) | 缴费期限 |
| insured_age_range | VARCHAR(50) | 投保年龄范围 |
| waiting_period | VARCHAR(50) | 等待期 |
| highlights | TEXT | JSON产品亮点/卖点列表 |
| extra_fields | TEXT | JSON扩展字段灵活承载非结构化字段 |
| status | SMALLINT DEFAULT 1 | 启用/停用 |
| sort_order | INTEGER DEFAULT 0 | 排序 |
| updated_at | TIMESTAMP | 更新时间 |
| **海报相关** | | |
| manual_file_url | VARCHAR(500) | 产品小册子 PDF 地址 |
| manual_parse_status | VARCHAR(20) DEFAULT 'none' | none/parsing/parsed/reviewed |
| manual_parsed_rules | TEXT | JSON解析出的产品规则库 |
| manual_reviewed_by | VARCHAR(50) | 核对人 |
| manual_reviewed_at | TIMESTAMP | 核对时间 |
`manual_parsed_rules` 建议内部结构:
```json
{
"product_name": "xxx保障计划",
"features": [
{"code": "income_stream", "title": "无忧选", "summary": "缴清保费后可定期领取非保证入息"},
{"code": "bonus_lock", "title": "终期红利锁定", "summary": "第5个保单周年日起可锁定终期红利"}
],
"currency_options": ["USD", "HKD", "RMB"]
}
```
### 3.3 扩展现有表:`insurance_ppt_templates`
新增字段:
| 字段 | 类型 | 说明 |
|------|------|------|
| name | VARCHAR(100) | 模板名称(当前只有 id缺人类可读名 |
| scenario_tag | VARCHAR(50) | 场景标签(自由标签,字典可配置) |
| preview_image | VARCHAR(500) | 预览图地址 |
| applicable_company_ids | TEXT | JSON 数组,适用保司 ID 列表(空=全部) |
| applicable_product_ids | TEXT | JSON 数组,适用产品 ID 列表(空=全部) |
| status | SMALLINT DEFAULT 1 | 启用/停用 |
### 3.4 新建表:`insurance_ppt_history`
```sql
CREATE TABLE insurance_ppt_history (
id BIGSERIAL PRIMARY KEY,
user_id VARCHAR(50) NOT NULL,
session_id VARCHAR(50), -- 关联 PptSession
action_type VARCHAR(20) NOT NULL, -- create/edit/preview/export/download
company_id VARCHAR(50),
product_id VARCHAR(50),
template_id VARCHAR(50),
content_snapshot TEXT, -- JSON 快照(历史详情用,避免关联数据变更后失真)
file_url VARCHAR(500),
ip VARCHAR(50),
user_agent VARCHAR(500),
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_ppt_history_user ON insurance_ppt_history(user_id, created_at DESC);
```
### 3.5 新建表:`poster_case_uploads`
```sql
CREATE TABLE poster_case_uploads (
id BIGSERIAL PRIMARY KEY,
user_id VARCHAR(50) NOT NULL,
product_id VARCHAR(50) NOT NULL,
source_file_url VARCHAR(500) NOT NULL,
parse_status VARCHAR(20) DEFAULT 'pending', -- pending/parsed/failed
parsed_data TEXT, -- JSON系统自动解析结果
confirmed_data TEXT, -- JSON人工核对后最终结果
confirmed_by VARCHAR(50),
confirmed_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
```
`parsed_data` / `confirmed_data` 建议字段:
```json
{
"age": 35,
"gender": "男",
"smoker": false,
"currency": "USD",
"sum_assured": 500000,
"premium_term": 5,
"annual_premium": 100000,
"first_year_premium_after_discount": 92000,
"levy_rate": 12.95,
"cash_value_table": [
{"year": 10, "guaranteed": 0, "non_guaranteed": 0}
],
"death_benefit_table": [
{"year": 10, "amount": 0}
]
}
```
### 3.6 新建表:`poster_templates`AI 生图方案)
> 注意:由于采用纯 AI 生图方案,模板不再存储 HTML 结构,改为存储 AI 生图所需的风格描述、参考图和配色方案。
```sql
CREATE TABLE poster_templates (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
scenario_tag VARCHAR(50), -- 自由标签
style_description TEXT NOT NULL, -- AI 生图的风格描述 prompt 片段
color_scheme TEXT, -- JSON配色方案 {"primary": "#1a1a2e", "accent": "#667eea", ...}
reference_image VARCHAR(500), -- 参考图地址AI 参考风格生成)
preview_image VARCHAR(500), -- 预览图(供管理员/用户选择时展示)
status SMALLINT DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
```
### 3.7 新建表:`poster_copy_templates`
```sql
CREATE TABLE poster_copy_templates (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
scenario_tag VARCHAR(50),
content TEXT NOT NULL, -- 文案模板,含 {{变量}} 占位符
variables TEXT, -- JSON变量列表及说明
status SMALLINT DEFAULT 1,
created_at TIMESTAMP DEFAULT NOW()
);
```
### 3.8 新建表:`poster_records`
```sql
CREATE TABLE poster_records (
id BIGSERIAL PRIMARY KEY,
user_id VARCHAR(50) NOT NULL,
product_id VARCHAR(50),
case_upload_id BIGINT,
template_id BIGINT,
copy_mode VARCHAR(20), -- template/ai
copy_content TEXT, -- JSON最终文案含用户编辑后版本
ai_raw_content TEXT, -- AI 原始文案(合规留痕,仅 AI 模式)
export_url VARCHAR(500),
export_format VARCHAR(10), -- png/jpg
export_size VARCHAR(20), -- e.g. "1080x1920"
reference_image_used VARCHAR(500), -- 使用的参考图(合规留痕)
prompt_used TEXT, -- 完整 prompt合规留痕
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_poster_records_user ON poster_records(user_id, created_at DESC);
```
### 3.9 新建配置表:`system_settings`(历史记录保存期限配置)
```sql
CREATE TABLE system_settings (
id BIGSERIAL PRIMARY KEY,
key VARCHAR(100) UNIQUE NOT NULL,
value TEXT NOT NULL,
description VARCHAR(500),
updated_by VARCHAR(50),
updated_at TIMESTAMP DEFAULT NOW()
);
-- 种子数据
INSERT INTO system_settings (key, value, description) VALUES
('ppt_history_retention_days', '365', 'PPT 历史记录保留天数0=永久保留)'),
('poster_history_retention_days', '365', '海报历史记录保留天数0=永久保留)');
```
---
## 四、后端 API 设计
### 4.1 路由注册(纯增量,不改已有路由)
`api/insurance/routes.py``register_insurance_routes()` 函数中追加:
```python
# 管理后台 PPT/海报配置管理
from insurance.admin.ppt_admin_routes import ppt_admin_bp
app.register_blueprint(ppt_admin_bp, url_prefix="/insurance/admin/ppt")
# 海报功能
from insurance.poster.routes import poster_bp
app.register_blueprint(poster_bp, url_prefix="/insurance/poster")
```
### 4.2 管理后台 API`/insurance/admin/ppt/`
所有接口需 `permission_required("config_manage")`
#### 保司管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /companies | 列表(支持 status 筛选、keyword 搜索、分页) |
| POST | /companies | 新增保司 |
| PUT | /companies/{id} | 编辑保司 |
| PUT | /companies/{id}/status | 启用/停用(软删除) |
#### 产品管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /products | 列表(支持 company_id、status、plan_type 筛选) |
| POST | /products | 新增产品 |
| PUT | /products/{id} | 编辑产品 |
| PUT | /products/{id}/status | 启用/停用 |
| POST | /products/{id}/manual/upload | 上传产品小册子 PDF |
| POST | /products/{id}/manual/parse | 触发小册子 AI 解析 |
| PUT | /products/{id}/manual/review | 人工核对并确认规则 → reviewed |
#### PPT 模板管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /templates | 列表 |
| POST | /templates | 新增 |
| PUT | /templates/{id} | 编辑 |
| PUT | /templates/{id}/status | 启用/停用 |
#### 海报模板管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /poster-templates | 列表 |
| POST | /poster-templates | 新增(含参考图上传) |
| PUT | /poster-templates/{id} | 编辑 |
| PUT | /poster-templates/{id}/status | 启用/停用 |
#### 文案模板管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /copy-templates | 列表 |
| POST | /copy-templates | 新增 |
| PUT | /copy-templates/{id} | 编辑 |
| DELETE | /copy-templates/{id} | 删除 |
#### 历史记录管理
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /history | 管理员查看全部 PPT+海报历史(支持 user_id/company_id/type 筛选) |
| GET | /history/export | 导出 CSV |
| DELETE | /history/{id} | 软删除(仅管理员) |
#### 系统配置
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /settings | 获取系统配置(含历史保留天数) |
| PUT | /settings | 更新系统配置 |
### 4.3 PPT 历史记录 API追加到现有 `/insurance/ppt/`
在现有 `ppt/routes.py` 文件末尾追加路由函数,不改动已有代码:
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /ppt/history | 当前用户的历史记录列表(分页、筛选) |
| GET | /ppt/history/{id} | 查看单条历史详情(含快照) |
| POST | /ppt/history/{id}/re-download | 重新下载 |
### 4.4 海报功能 API`/insurance/poster/`
所有接口需 `@jwt_required`
#### 流程 API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /products | 获取已 reviewed 的产品列表(供选择) |
| POST | /case-upload | 上传计划书 PDF → 触发解析 |
| GET | /case-upload/{id} | 获取解析结果 |
| PUT | /case-upload/{id}/confirm | 人工核对/修正解析数据 |
| GET | /templates | 获取可用海报模板列表 |
| GET | /copy-templates | 获取可用文案模板列表 |
| POST | /generate-copy | 生成文案(支持 template/ai 模式) |
| POST | /generate | **生成海报图片**(调用 GPT image 模型) |
| GET | /download/{record_id} | 下载导出文件 |
#### 记录 API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /records | 当前用户的海报生成记录 |
| GET | /records/{id} | 记录详情 |
---
## 五、海报生成技术方案(纯 AI 生图)
### 5.1 整体流程
```
选择产品 → 上传计划书 → AI 解析 + 人工核对 → 选模板/参考图 → 选文案模式 → 组装 Prompt → 调用 GPT Image API → 返回海报图片 → 下载
```
### 5.2 Prompt 组装策略
生成海报时,系统组装以下信息为一个完整 prompt
```
【系统指令】
你是一位专业的保险营销海报设计师。请根据以下信息生成一张 {width}x{height} 的保险营销海报。
【风格要求】
{poster_template.style_description}
配色方案:{poster_template.color_scheme}
参考风格:(附上 reference_image如有
【产品信息】
产品名称:{product.display_name}
所属公司:{company.display_name}
产品亮点:{product.manual_parsed_rules.features}
【客户数据】
年龄:{case_upload.confirmed_data.age}
保费:{case_upload.confirmed_data.annual_premium}
保障期限:{case_upload.confirmed_data.coverage_period}
...
【营销文案】
{generated_copy}
【输出要求】
- 尺寸:{width}x{height} 像素
- 包含公司 Logo 位置(如可获取)
- 文字清晰可读,中文为主
- 符合保险行业专业风格
```
### 5.3 GPT Image API 调用
```python
# poster/image_generator.py
import openai
import base64
import os
class PosterImageGenerator:
"""海报图片生成器,调用 GPT image 模型。"""
def __init__(self):
self.client = openai.OpenAI(
api_key=os.getenv("OPENAI_API_KEY"),
)
def generate(self, prompt: str, size: str = "1024x1792") -> bytes:
"""
调用 GPT image 模型生成海报图片。
参数:
prompt: 组装后的完整 prompt
size: 图片尺寸,支持 "1024x1024" / "1024x1792" / "1792x1024"
返回:
PNG 图片的 bytes
"""
response = self.client.images.generate(
model="gpt-image-1",
prompt=prompt,
n=1,
size=size,
)
# 解码 base64 图片
image_base64 = response.data[0].b64_json
return base64.b64decode(image_base64)
```
### 5.4 尺寸映射
用户选择的尺寸 → GPT image API 支持的尺寸:
| 用户选择 | API 尺寸 | 说明 |
|---------|---------|------|
| 1080x1920竖版海报 | 1024x1792 | 最接近的 API 支持尺寸 |
| 900x500横版海报 | 1792x1024 | 最接近的 API 支持尺寸 |
| 1080x1080正方形 | 1024x1024 | 精确匹配 |
| 800x1200通用竖版 | 1024x1792 | 最接近的 API 支持尺寸 |
| 自定义 | 按比例选择最近尺寸 | AI 图片生成后前端可裁剪/缩放 |
### 5.5 参考图处理
管理员上传的参考图(`poster_templates.reference_image`)在生成时:
1. 将参考图作为 `image` 参数传入 GPT image API 的 edit 端点
2. 或在 prompt 中描述参考图的风格特征(如果 API 不支持多图输入)
### 5.6 降级方案
如果 OpenAI image API 调用失败(网络/额度/限流):
- 降级为 **模板模式文案 + 基础排版图**(使用 Pillow 生成简单的文字+色块海报)
- 记录错误日志,提示用户重试
---
## 六、文案生成方案(双模式)
### 6.1 模板模式
1.`poster_copy_templates` 选择文案模板
2.`case_upload.confirmed_data` + `product.manual_parsed_rules` 填充 `{{变量}}` 占位符
3. 不调用外部 API纯本地字符串替换
4. 支持的变量示例:`{{product_name}}`、`{{annual_premium}}`、`{{feature_1_title}}`、`{{feature_1_summary}}`
### 6.2 AI 模式
复用现有 `ppt/llm_client.py` 的多供应商 LLM 客户端:
```python
# poster/copy_generator.py
from insurance.ppt.llm_client import llm_client
async def generate_ai_copy(product_rules: dict, customer_data: dict, style: str) -> dict:
"""
调用 LLM 生成营销文案。
返回:
{"headline": "...", "body": "...", "call_to_action": "..."}
"""
system_prompt = """你是一位专业的保险营销文案撰写人。
根据以下产品信息和客户数据,生成一张保险营销海报的文案。
要求:
1. 标题headline简短有力8字以内
2. 正文body突出产品亮点与客户需求的匹配50-100字
3. 行动号召call_to_action引导客户咨询15字以内
4. 风格:{style}
5. 必须基于真实数据,不得虚构收益数字
以 JSON 格式返回。"""
user_prompt = f"""
产品信息:{json.dumps(product_rules, ensure_ascii=False)}
客户数据:{json.dumps(customer_data, ensure_ascii=False)}"""
result = await llm_client.structured_output(
system_prompt, user_prompt,
schema={
"type": "object",
"properties": {
"headline": {"type": "string"},
"body": {"type": "string"},
"call_to_action": {"type": "string"},
},
"required": ["headline", "body", "call_to_action"],
}
)
return result
```
---
## 七、PDF 解析复用策略
### 7.1 计划书解析
复用 `ppt/extraction.py``ExtractionOrchestrator`,新增精简 prompt
```python
# 在 extraction.py 中新增方法
async def extract_for_poster(self, filepath: str) -> dict:
"""提取海报所需的关键字段(精简 prompt降低 LLM 成本)。"""
# 使用更短的 prompt只提取
# age, gender, currency, sum_assured, premium_term,
# annual_premium, coverage_period, key_benefits
...
```
### 7.2 保司小册子解析
新增专门的解析 prompt
```python
# poster/manual_parser.py
MANUAL_PARSE_PROMPT = """请从以下保险产品手册中提取产品规则和卖点信息。
输出 JSON 格式:
{
"product_name": "产品名称",
"features": [
{"code": "唯一编码", "title": "卖点标题", "summary": "一句话描述"}
],
"currency_options": ["USD", "HKD"],
"coverage_highlights": ["保障亮点1", "保障亮点2"]
}
"""
```
---
## 八、权限设计
复用现有 5 级角色体系,不新增角色:
| 操作 | super_admin | admin | manager | sales | client |
|------|:-----------:|:-----:|:-------:|:-----:|:------:|
| PPT 生成 | ✅ | ✅ | ✅ | ✅ | ✅ |
| PPT 历史(自己) | ✅ | ✅ | ✅ | ✅ | ✅ |
| 海报生成 | ✅ | ✅ | ✅ | ✅ | ❌ |
| 海报历史(自己) | ✅ | ✅ | ✅ | ✅ | ❌ |
| 保司/产品/模板管理 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 全部历史查看 | ✅ | ✅ | 本部门 | 仅自己 | ❌ |
| 历史删除 | ✅ | ✅ | ❌ | ❌ | ❌ |
| 系统配置(保留天数) | ✅ | ✅ | ❌ | ❌ | ❌ |
管理接口统一使用 `permission_required("config_manage")`,与现有 LLM 配置、通知渠道等管理接口权限一致。
---
## 九、前端设计
### 9.1 路由新增
```typescript
// 用户端(需 auth
{ path: '/ppt/history', component: PptHistoryPage } // PPT 历史记录
{ path: '/poster', component: PosterPage } // 海报生成向导
{ path: '/poster/history', component: PosterHistoryPage } // 海报历史记录
// 管理端(需 auth + admin + config_manage 权限)
{ path: '/admin/ppt/companies', component: PptCompaniesAdmin }
{ path: '/admin/ppt/products', component: PptProductsAdmin }
{ path: '/admin/ppt/templates', component: PptTemplatesAdmin }
{ path: '/admin/ppt/poster-templates', component: PosterTemplatesAdmin }
{ path: '/admin/ppt/copy-templates', component: CopyTemplatesAdmin }
{ path: '/admin/ppt/history', component: PptHistoryAdmin }
{ path: '/admin/ppt/settings', component: PptSettingsAdmin }
```
### 9.2 侧边栏更新App.vue
```
功能菜单:
- 智能问答(已有,不变)
- 建议书生成(已有,不变)
- 建议书历史(新增)
- 海报生成(新增)
- 海报历史(新增)
系统管理:
- ...(已有项全部不变)
- PPT/海报配置(新增折叠子菜单):
- 保司管理
- 产品管理
- PPT 模板管理
- 海报模板管理
- 文案模板管理
- 生成历史
- 系统设置
```
### 9.3 PPT 前端优化(需求 1.1,增量修改现有组件)
**骨架屏**:在 PptUpload/PptParsing/PptGenerate 组件中添加 `<el-skeleton>` 包裹,数据加载完成前显示骨架屏。
**移动端触屏优化**
- PptUpload拖拽区域增加 `min-height: 120px`,点击区域扩大
- PptGenerate样式卡片确保 `min-width: 140px`
- 所有按钮增加 `min-height: 44px`iOS 推荐触屏最小尺寸)
- 表单输入框增加 `font-size: 16px`(防止 iOS 自动缩放)
**导出进度反馈**:在 PptResult.vue 的下载按钮处增加 `<el-progress>` 条。
**断点**:继续使用现有 `useMobile()` composable768px 断点)。
### 9.4 PPT 历史记录页面
新建 `PptHistoryPage.vue`
- 表格列:时间、保司、产品、操作类型、文件、操作(查看/重新下载)
- 筛选栏:保司下拉、操作类型下拉、日期范围
- 分页:复用 `el-pagination`
- 点击"查看"弹出 `el-dialog` 展示 content_snapshot 的格式化视图
### 9.5 海报生成页面
新建 `PosterPage.vue`,采用与 PptPage 相同的 `el-steps` 向导模式:
**Step 1 - 选择产品**:展示已 reviewed 的产品卡片列表(按保司分组),未 reviewed 的产品灰显不可选。
**Step 2 - 上传计划书**
- 拖拽上传 PDF
- 解析进度(复用 PptParsing 的进度 UI 模式)
- 解析结果表单:逐字段展示,置信度低的字段高亮,用户可编辑修正
- 确认按钮
**Step 3 - 选择模板 + 文案**
- 海报模板选择:卡片网格,含预览图和风格描述
- 配色方案展示(从模板继承,可手动调整)
- 文案模式切换:
- 模板模式:选择文案模板 → 实时预览填充结果
- AI 模式:点击"生成文案" → 展示 AI 生成的文案headline/body/call_to_action→ 可编辑
- 参考图上传(可选,用户可上传自己的参考图覆盖模板默认图)
**Step 4 - 预览与导出**
- 点击"生成海报" → 显示加载状态AI 生图需要 10-30 秒)
- 海报图片预览
- 尺寸选择下拉
- 下载按钮
### 9.6 管理后台页面
每个管理页面遵循现有 admin 页面模式(表格 + 弹窗编辑):
- **PptCompaniesAdmin**:表格 + 搜索/状态筛选 + 新增/编辑弹窗(含 Logo 上传)
- **PptProductsAdmin**:表格 + 保司/类型/状态筛选 + 编辑弹窗(含小册子上传区域、规则核对表单)
- **PptTemplatesAdmin**:表格 + 编辑弹窗(名称、场景标签、预览图上传、适用范围多选)
- **PosterTemplatesAdmin**:表格 + 编辑弹窗(风格描述、配色方案编辑器、参考图上传、预览图)
- **CopyTemplatesAdmin**:表格 + 编辑弹窗(文案内容编辑、变量说明、场景标签)
- **PptHistoryAdmin**:只读表格 + 筛选 + 导出 CSV + 删除按钮
- **PptSettingsAdmin**:简单表单,配置历史记录保留天数
---
## 十、文件结构
### 后端新增文件
```
api/insurance/
├── db/
│ ├── migrate_015.py # 新表创建 + 现有表扩展
│ └── migrate_016.py # 种子数据(系统配置默认值)
├── models/
│ ├── ppt_history.py # PptHistory 模型
│ ├── poster_case_upload.py # PosterCaseUpload 模型
│ ├── poster_template_model.py # PosterTemplate 模型
│ ├── poster_copy_template.py # PosterCopyTemplate 模型
│ ├── poster_record.py # PosterRecord 模型
│ └── system_setting.py # SystemSetting 模型
├── admin/
│ ├── ppt_admin_routes.py # PPT/海报管理后台路由(新建)
│ └── ppt_admin_service.py # PPT/海报管理后台服务(新建)
├── poster/
│ ├── __init__.py # 包初始化
│ ├── routes.py # 海报功能路由
│ ├── service.py # 海报业务逻辑
│ ├── copy_generator.py # 文案生成(模板 + AI 双模式)
│ ├── image_generator.py # GPT image API 调用
│ └── manual_parser.py # 保司小册子解析 prompt
└── ppt/
└── routes.py # 末尾追加历史记录路由(不改已有代码)
```
### 前端新增文件
```
frontend/src/
├── pages/
│ ├── PptHistoryPage.vue # PPT 历史记录
│ ├── PosterPage.vue # 海报生成向导
│ ├── PosterHistoryPage.vue # 海报历史记录
│ └── admin/
│ ├── PptCompaniesAdmin.vue # 保司管理
│ ├── PptProductsAdmin.vue # 产品管理
│ ├── PptTemplatesAdmin.vue # PPT 模板管理
│ ├── PosterTemplatesAdmin.vue # 海报模板管理
│ ├── CopyTemplatesAdmin.vue # 文案模板管理
│ ├── PosterHistoryAdmin.vue # 生成历史管理
│ └── PptSettingsAdmin.vue # 系统设置
├── components/poster/
│ ├── PosterStepProduct.vue # Step1: 选择产品
│ ├── PosterStepUpload.vue # Step2: 上传计划书
│ ├── PosterStepTemplate.vue # Step3: 选模板+文案
│ └── PosterStepPreview.vue # Step4: 预览导出
├── utils/
│ ├── poster-api.ts # 海报 API 封装
│ └── ppt-admin-api.ts # PPT 管理 API 封装
```
---
## 十一、迁移脚本migrate_015
```python
"""迁移 015: PPT/海报功能扩展。"""
def migrate():
from insurance.db.compat import db
from sqlalchemy import text
# 1. 扩展 insurance_ppt_companies
for col in ["logo_url VARCHAR(500)", "status SMALLINT DEFAULT 1", "sort_order INTEGER DEFAULT 0"]:
try:
db.session.execute(text(f"ALTER TABLE insurance_ppt_companies ADD COLUMN {col}"))
except Exception:
pass # 已存在则跳过
# 现有数据默认启用
db.session.execute(text("UPDATE insurance_ppt_companies SET status = 1 WHERE status IS NULL"))
# 2. 扩展 insurance_ppt_products15+ 字段)
...
# 3. 扩展 insurance_ppt_templates6 字段)
...
# 4. 创建 insurance_ppt_history
...
# 5. 创建 poster_case_uploads
...
# 6. 创建 poster_templatesAI 生图方案)
...
# 7. 创建 poster_copy_templates
...
# 8. 创建 poster_records
...
# 9. 创建 system_settings
...
db.session.commit()
```
所有 ALTER TABLE 使用 `try/except` 包裹,保证幂等。
---
## 十二、实施计划(分 4 个阶段)
### 阶段 1数据库 + 后台管理(优先级最高)
**后端**
- migrate_015创建新表 + 扩展旧表
- 5 个新 SQLAlchemy 模型 + 1 个 SystemSetting 模型
- admin CRUD 接口ppt_admin_routes.py + ppt_admin_service.py
**前端**
- 7 个管理页面(保司/产品/PPT模板/海报模板/文案模板/历史/设置)
- 侧边栏更新
- API 封装ppt-admin-api.ts
**验证**:后台能正常增删改查保司、产品、模板数据;现有 PPT 生成功能不受影响。
### 阶段 2PPT 前端优化 + 历史记录
**后端**
- PptHistory 模型
- 在 ppt/routes.py 末尾追加历史记录路由
- 在 PPT 生成/下载流程中自动记录历史
**前端**
- PptPage 各子组件骨架屏、触屏优化、导出进度
- PptHistoryPage.vue
**验证**PPT 生成流程正常,历史记录自动写入并可查询。
### 阶段 3海报核心功能
**后端**
- PosterCaseUpload 模型 + 计划书上传/解析/确认 API
- copy_generator.py文案生成模板模式 + AI 模式)
- image_generator.pyGPT image API 调用)
- manual_parser.py小册子解析 prompt
- 海报生成完整流程 API
**前端**
- PosterPage.vue 4 步向导
- 4 个 Step 子组件
**验证**:完整海报生成流程可用(选产品 → 上传计划书 → 核对 → 选模板 → 生成文案 → 生成海报 → 下载)。
### 阶段 4海报历史 + 管理 + 合规
**后端**
- PosterRecord 模型 + 记录 API
- 历史删除 + 保留天数配置
- AI 文案留痕ai_raw_content + copy_content 双版本)
**前端**
- PosterHistoryPage.vue
- PosterHistoryAdmin.vue
- PptSettingsAdmin.vue
**验证**:全链路测试通过,合规留痕完整。
---
## 十三、风险与缓解
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| GPT image API 限流/费用高 | 海报生成不可用或成本超预期 | 降级为 Pillow 基础排版图;记录调用量监控 |
| AI 生图文字渲染不清晰 | 海报中文/数字模糊 | Prompt 中强调"文字清晰可读";可后期叠加文字层 |
| PDF 解析精度不够 | 计划书数据错误 | 人工核对表单兜底,标记低置信度字段 |
| 现有 PPT 功能被破坏 | 用户无法生成 PPT | 所有改动纯增量,不修改已有路由/模型字段定义 |
| 海报模板/参考图不足 | 初期无可用模板 | 先做 2-3 个基础模板,后续迭代 |
| AI 文案合规风险 | 营销内容违规 | 双版本留痕ai_raw_content + 最终版)+ 人工编辑确认 |
| OpenAI API Key 过期/额度不足 | 海报生成中断 | 前端显示明确错误提示 + 重试按钮;后台显示额度监控 |
---
## 十四、后续可扩展方向
- 增加 medical/annuity/life 产品类型(只需新增 extraction prompt + normalizer
- 海报批量生成(多客户一次导出)
- 海报分享链接(生成带水印的预览图 + 专属链接)
- 模板市场(管理员上传模板后其他用户可选用)
- PPT 历史记录的"一键重新生成"功能