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

876 lines
31 KiB
Markdown
Raw Normal View History

2026-07-23 15:04:16 +08:00
# 建议书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 历史记录的"一键重新生成"功能