baodan/docs/开发计划.md

32 KiB
Raw Blame History

保险智能客服系统 — 完整开发计划

基于:需求文档 V1.0 + API 文档 V1.1 + 前端开发计划 + BaoDan 源码分析 当前状态后端空壳49 行注释)+ 前端骨架743 行)+ SQL 建表112 行) 预计工期10 个工作日(一人全栈) 核心原则BaoDan 已有的绝不重复造轮子


零、已确认不需要自研的功能(用 BaoDan 原生)

功能 BaoDan 提供的 对应需求编号
LLM 模型配置 CRUD ModelProviderCredentialApi 后台可视化配置 6.1 / A6.1.1-A6.1.2
Prompt 编辑/版本/测试 App 配置页 + Annotation 系统 6.2 / A6.1.3-A6.1.4
对话日志查看/筛选 BaoDan 后台 /logs 页面 4.1.1-4.1.2
负反馈管理 MessageFeedbackExportApi + Annotation 4.1.4
点赞/点踩 BaoDan WebApp 内置 1.4.1
推荐追问 MessageSuggestedQuestionApi 1.2.4 / A2.2.2
来源引用标注 BaoDan RAG 自动返回 sources 1.2.3
会话标题自动命名 BaoDan 自动命名 1.1.5
知识库测试入口hit testing HitTestingApi 3.4.1
FAQ 手动条目 BaoDan Annotation Reply = FAQ 3.4.3
文档上传/列表/删除/重试 BaoDan /datasets 后台 3.1.1-3.1.2 / 3.1.5 / 3.2.x
Markdown 渲染 BaoDan WebApp 内置 1.2.1
多轮对话上下文 BaoDan conversation 机制 1.1.3
流式输出 BaoDan WebApp iframe 1.1.2

以上功能不需要写后端接口,不需要写前端页面。


一、后端自研接口清单25 个,对齐 API 文档 V1.1

1.1 认证鉴权4 个)

# 方法 URL 说明
1 POST /api/auth/wework-login 企微 OAuth 登录 → JWT
2 POST /api/auth/password-login 账密登录 → JWT
3 POST /api/auth/refresh-token 刷新 Token
4 POST /api/auth/logout 退出Token 黑名单)

1.2 智能问答5 个)

# 方法 URL 说明
5 POST /api/chat/message 发送消息SSE 流式,调 BaoDan Chat API
6 GET /api/chat/sessions 会话列表(封装 BaoDan API
7 POST /api/chat/sessions 创建会话
8 DELETE /api/chat/sessions/{id} 删除会话
9 GET /api/chat/sessions/{id}/messages 消息记录(含来源引用)

1.3 反馈 + 检索2 个)

# 方法 URL 说明
10 POST /api/chat/messages/{id}/feedback 提交反馈(封装 BaoDan API
11 POST /api/retrieval/search 向量检索(封装 BaoDan API

1.4 产品推荐4 个)

# 方法 URL 说明
12 POST /api/recommend/generate 生成方案(调 BaoDan Workflow
13 GET /api/recommend/generate/{task_id} 查询任务状态
14 POST /api/recommend/{id}/export 导出 PDF/PPT/Word
15 POST /api/recommend/{id}/share 生成分享链接

1.5 知识库增强6 个)

# 方法 URL 说明
16 POST /api/kb/documents/upload 上传文档(附标签,调 BaoDan API
17 GET /api/kb/documents 文档列表(带自定义标签)
18 GET /api/kb/documents/{id}/status 文档处理状态
19 PATCH /api/kb/documents/{id} 更新元数据(编号/标签)
20 DELETE /api/kb/documents/{id} 删除文档
21-24 * /api/kb/datasources/* 保司数据源 CRUD + 同步 + 日志4 个)

1.6 用户权限3 个)

# 方法 URL 说明
25 CRUD /api/admin/users 用户管理(含停用吊销 Token
26 POST /api/admin/users/batch-import 批量导入
27 CRUD /api/admin/roles 角色管理

1.7 系统日志 + 统计4 个)

# 方法 URL 说明
28 GET /api/admin/logs/system 系统操作日志
29 GET /api/stats/overview 使用概览
30 GET /api/stats/trend 趋势数据
31 GET /api/stats/kb-health 知识库健康度

1.8 企微回调4 个)

# 方法 URL 说明
32 GET /api/wecom/callback 企微 URL 验证
33 POST /api/wecom/callback 企微消息接收
34 GET /api/wecom/oauth OAuth 入口
35 GET /api/wecom/oauth/callback OAuth 回调

二、前端自研页面清单

2.1 用户端5 个页面 + 5 个组件)

页面 路径 说明 对应需求
LoginPage.vue /login 企微 OAuth + 账密登录 无(全新)
ChatPage.vue /chat iframe 嵌入 BaoDan WebApp M1
ChatFilters.vue 险种/保司筛选器组件 1.3.1-1.3.3
ChatCopyButton.vue 复制回答按钮组件 1.2.5
RecommendPage.vue /recommend 推荐方案表单 + 结果 M2.1-M2.2
RecommendForm.vue 客户信息表单组件 2.1.1-2.1.6
RecommendResult.vue 方案展示组件 2.2-2.3
RecommendHistory.vue /recommend/history 历史方案列表 2.5.1-2.5.4
RecommendDetail.vue /recommend/detail/:id 方案详情 + 导出 2.3-2.4

2.2 管理端6 个页面)

页面 路径 说明 对应需求
DashboardPage.vue /admin 管理后台首页(跳转入口)
UsersPage.vue /admin/users 用户 CRUD + 批量导入 5.1-5.2
PermissionsPage.vue /admin/roles 角色权限配置 5.2
StatsPage.vue /admin/stats 数据统计仪表盘 M7
KBManagePage.vue /admin/kb 文档管理 + 标签 + 编号 3.1.3-3.1.4
DataSourcePage.vue /admin/datasource 保司数据源配置 3.3

知识库主界面、对话日志、Prompt 管理、模型配置 → 跳转 BaoDan 后台,不自研。


三、数据库表3 张自研表,已建好)

表名 用途 当前状态
wecom_user_mapping 企微用户 ↔ BaoDan 用户映射 已建
recommendation_records 推荐方案记录 已建
system_operation_logs 系统操作审计日志 已建

可能需要追加的表:

表名 用途 何时建
document_metadata 文档编号/标签/险种/保司 Phase 7 KB 增强时
datasource_configs 保司 API 数据源配置 Phase 7 KB 增强时
datasource_sync_logs 同步日志 Phase 7 KB 增强时
share_tokens 方案分享链接 Token Phase 3 产品推荐时
token_blacklist JWT 黑名单(或用 Redis Phase 1 认证时

四、分阶段开发计划


Phase 0项目脚手架0.5 天)

目标BaoDan + 自研代码能跑起来,路由能通。

后端任务

0.1 创建 insurance/ 模块骨架

api/insurance/
├── __init__.py              # 空
├── config.py                # 配置常量JWT Secret、企微配置等
├── utils/
│   ├── __init__.py
│   ├── response.py          # 统一响应封装success_response() / error_response()
│   ├── auth.py              # JWT 签发/校验/装饰器
│   └── db.py                # 复用 BaoDan 的 dbfrom extensions.ext_database import db
├── wecom/
│   ├── __init__.py
│   ├── routes.py            # Blueprint: wecom_bp
│   ├── wecom_bot.py         # 消息回调处理
│   ├── wecom_oauth.py       # OAuth 登录
│   └── wecom_api.py         # 企微 API 封装(发消息/获取 Token
├── recommend/
│   ├── __init__.py
│   ├── routes.py            # Blueprint: recommend_bp
│   └── workflow_helper.py   # BaoDan Workflow 调用封装
├── permissions/
│   ├── __init__.py
│   ├── routes.py            # Blueprint: permissions_bp
│   └── middleware.py        # 权限校验中间件
├── stats/
│   ├── __init__.py
│   └── routes.py            # Blueprint: stats_bp
└── db/
    ├── __init__.py
    ├── models.py            # SQLAlchemy 模型3 张自研表)
    └── init.sql             # 建表脚本(已有)

每个 routes.py 先放一个 health check 占位接口。

0.2 注册路由到 BaoDan

修改 baodan-main/api/app_factory.py(或 main.py),在 app 创建后加:

try:
    from insurance.utils.response import InsuranceResponse
    from insurance.wecom.routes import wecom_bp
    from insurance.recommend.routes import recommend_bp
    from insurance.permissions.routes import permissions_bp
    from insurance.stats.routes import stats_bp
    app.register_blueprint(wecom_bp, url_prefix='/api/wecom')
    app.register_blueprint(recommend_bp, url_prefix='/api/recommend')
    app.register_blueprint(permissions_bp, url_prefix='/api/admin')
    app.register_blueprint(stats_bp, url_prefix='/api/stats')
except ImportError:
    pass

0.3 修改 BaoDan 前端 iframe 配置

web/.env.local 添加:

NEXT_PUBLIC_ALLOW_EMBED=true

前端任务

0.4 确认前端项目能启动

已有 package.jsonrouterApp.vue。安装依赖后 pnpm dev 验证。

0.5 统一响应拦截器

确保 src/utils/api.ts 的 axios 拦截器处理 code !== 0 的情况。

验证标准

# 后端
curl http://localhost:5001/api/health              # 返回 {"status":"ok"}
curl http://localhost:5001/api/auth/password-login  # 返回参数错误(不是 404

# 前端
pnpm dev → 浏览器打开 localhost:3000 → 显示登录页

Phase 1认证系统1 天)

目标能登录、能鉴权、Token 能刷新。

后端0.5 天)

1.1 config.py — 配置项

JWT_SECRET = os.getenv('JWT_SECRET', 'your-secret-key')
JWT_EXPIRE_HOURS = 2
REDIS_TOKEN_PREFIX = 'token:blacklist:'
WECOM_CORP_ID = os.getenv('WECOM_CORP_ID', '')
WECOM_AGENT_SECRET = os.getenv('WECOM_AGENT_SECRET', '')
WECOM_TOKEN = os.getenv('WECOM_TOKEN', '')
WECOM_AES_KEY = os.getenv('WECOM_AES_KEY', '')

1.2 utils/auth.py — JWT 工具

  • generate_token(user_id, username, role, department) → 签发 JWT
  • decode_token(token) → 解码 + 校验有效期
  • login_required 装饰器 → 从 Header 提取 JWT → 解码 → 注入 current_user
  • admin_required 装饰器 → 额外校验 role in [super_admin, admin]

1.3 utils/response.py — 统一响应

def success(data=None, message="success"):
    return {"code": 0, "message": message, "data": data}

def error(code, message):
    return {"code": code, "message": message, "data": None}

1.4 POST /api/auth/password-login

输入:{ username, password }
处理:
  1. 参数校验3-64字符8-128字符
  2. 查 wecom_user_mapping 表找用户
  3. bcrypt 校验密码(需在用户表加 password_hash 字段)
  4. 签发 JWT
  5. 记录 system_operation_logsaction=login
输出:{ token, expires_in, user }

注意wecom_user_mapping 表需要加 password_hash 字段ALTER TABLE

1.5 POST /api/auth/refresh-token

处理:
  1. 校验旧 Token 有效性(未过期、未在黑名单)
  2. 签发新 Token
  3. 旧 Token 加入 Redis 黑名单EX 7200
输出:{ token, expires_in }

1.6 POST /api/auth/logout

处理:
  1. 当前 Token 加入 Redis 黑名单
  2. 记录操作日志
输出:{ code: 0 }

1.7 POST /api/auth/wework-login

输入:{ code, state }
处理:
  1. 用 code 调企微 gettoken API → 拿 access_token
  2. 用 access_token + code 调企微 auth/get_user_info → 拿 userid/name/department
  3. 查/建 wecom_user_mapping 记录
  4. 签发 JWT
输出:{ token, expires_in, user }

前端0.5 天)

1.8 LoginPage.vue

  • 企微登录按钮 → window.location.href = '/api/wecom/oauth'
  • 账密登录表单 → POST /api/auth/password-login → 存 token → 跳 /chat
  • 表单校验:用户名必填,密码必填

1.9 路由守卫

  • router.beforeEach 检查 localStorage.token
  • 无 token → 跳 /login

1.10 Token 自动刷新

  • api.ts 拦截器:响应 401 → 尝试 refresh-token → 成功则重试原请求 → 失败跳登录页

验证标准

# 后端
curl -X POST http://localhost:5001/api/auth/password-login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"test1234"}'
# → 返回 JWT token

curl -H "Authorization: Bearer <token>" http://localhost:5001/api/auth/refresh-token
# → 返回新 token

curl -H "Authorization: Bearer <old_token>" http://localhost:5001/api/auth/refresh-token
# → 旧 token 已失效

Phase 2对话页面1 天)

目标:用户能在 iframe 中跟 BaoDan 对话,筛选器可用。

后端0.5 天)

2.1 POST /api/chat/messageSSE 流式)

这是核心接口,封装 BaoDan Chat API

输入:{ session_id, message, filters }
处理:
  1. JWT 鉴权
  2. 参数校验
  3. 根据 filters.险种 选择对应的 BaoDan App不同险种不同知识库
     - 无险种 → 全库 App
     - 重疾险 → 重疾险专用 App
     - 以此类推
  4. 调 BaoDan Chat APIblocking 模式):
     POST http://localhost:5001/v1/chat-messages
     Headers: Authorization: Bearer app-xxxxxxxx
     Body: { query, user, conversation_id, response_mode: "blocking" }
  5. 解析 BaoDan 响应 → 提取 answer + retriever_resources
  6. 返回 SSE 流式数据
输出SSE
  data: {"type":"source","data":{...}}
  data: {"type":"delta","data":"根据"}
  data: {"type":"done","data":{"message_id":"...","conversation_id":"..."}}

关键点BaoDan 本身已有 SSE我们可以直接用 blocking 模式拿到完整回答后转 SSE或用 streaming 模式透传。先做 blocking 模式(简单),后续优化为 streaming。

2.2 GET /api/chat/sessions

处理:
  1. 调 BaoDan API: GET /v1/conversations?user={user_id}&page=1&limit=20
  2. 转换格式为 API 文档定义的结构
输出:{ total, items: [{ id, title, created_at, updated_at, message_count }] }

2.3 POST /api/chat/sessions

处理:调 BaoDan API 创建空会话(或前端直接新建)

2.4 DELETE /api/chat/sessions/{id}

处理:调 BaoDan API 删除会话

2.5 GET /api/chat/sessions/{id}/messages

处理:调 BaoDan API 获取消息列表 → 转换格式

前端0.5 天)

2.6 ChatPage.vue

结构:
  ┌─────────────────────────────┐
  │ ChatFilters筛选器          │
  ├─────────────────────────────┤
  │                             │
  │ iframeBaoDan WebApp         │
  │ src="/baodan/chat/{app_id}"   │
  │                             │
  ├─────────────────────────────┤
  │ ChatCopyButton + Suggestions │
  └─────────────────────────────┘
  • ChatEmbed 组件iframe 加载 BaoDan WebApp
  • ChatFilters险种下拉7 种)+ 保司下拉(动态加载)
  • 选择险种后切换 iframe src 到对应 BaoDan App

2.7 App.vue 侧边栏

左侧导航:
  智能问答 → /chat
  产品推荐 → /recommend
  历史方案 → /recommend/history
  ────────(管理员以下才显示)
  管理后台 → /admin
  ────────
  • 根据用户 role 控制显示哪些菜单
  • 顶部栏:页面标题 + 用户名 + 退出

验证标准

# 浏览器
1. 登录 → 自动跳 /chat
2. iframe 加载出 BaoDan 对话界面
3. 筛选器下拉选择"重疾险"
4. 在 iframe 中提问 → BaoDan 回答
5. 侧边栏点击"产品推荐" → 跳转正常

Phase 3产品推荐2 天)— 最大自研模块

目标:填写客户信息 → 生成方案 → 查看结果 → 导出。

后端1 天)

3.1 数据模型扩展 — recommendation_records

确认表结构满足需求,补充 share_tokenshare_expire_at 字段。

3.2 POST /api/recommend/generate

输入:{ customer, insurance_types, coverage_amount, coverage_period, existing_policies }
处理:
  1. JWT 鉴权
  2. 参数校验(年龄 1-150、性别 male/female、险种至少 1 项等,见需求文档 13.3 节)
  3. INSERT recommendation_recordsstatus=pending
  4. 异步调 BaoDan Workflow API
     POST http://localhost:5001/v1/workflows/run
     Headers: Authorization: Bearer app-yyyyyyyyyyyy
     Body: { inputs: { age, gender, insurance_types, ... }, response_mode: "blocking", user }
  5. 解析 Workflow 输出 → JSON 格式化为 3 套方案(基础/均衡/全面)
  6. UPDATE recommendation_recordsstatus=done, generated_plan=...
  7. 记录操作日志
输出:{ task_id, status }

关键Workflow 返回的是 Markdown 格式的方案文本,需要在 workflow_helper.py 中解析为结构化 JSON。

3.3 workflow_helper.py — BaoDan Workflow 调用封装

def call_recommendation_workflow(customer_info, insurance_types, coverage_amount, coverage_period):
    """
    调 BaoDan Workflow API 生成推荐方案
    返回: { plans: [{ name, total_premium, items: [...], summary }] }
    """
    # 1. 构造 inputs
    # 2. POST /v1/workflows/run
    # 3. 解析 Markdown 输出 → 结构化 JSON
    # 4. 失败处理(超时 120s、Workflow 错误)

3.4 GET /api/recommend/generate/{task_id}

处理:查 recommendation_records 表,返回 status + proposal

3.5 POST /api/recommend/{id}/export

输入:{ format: "pdf" | "pptx" | "docx" }
处理:
  1. 查推荐记录
  2. 根据 format 调对应生成器:
     - docx: python-docx先做最简单
     - pdf: reportlab 或 weasyprint
     - pptx: python-pptx
  3. 生成文件 → 保存到 /tmp/exports/ 或 OSS
  4. 返回下载链接(带临时 token30 分钟有效)
输出:{ download_url, expire_at }

3.6 POST /api/recommend/{id}/share

处理:
  1. 生成随机 token
  2. 存 share_tokens 表(含过期时间)
输出:{ share_url, expire_at }

前端1 天)

3.7 RecommendForm.vue — 表单组件

结构
  基础信息姓名 / 年龄 / 性别radio/ 健康状况select/ 职业
  收入预算年收入/ 月预算
  关注险种checkbox group寿险/重疾险/医疗险/意外险/年金险/储蓄险
  保障需求保额slider + input/ 保障期限select
  已有保单可选折叠区域动态添加
  [生成方案] 按钮

验证规则见需求文档 13.3  + 14 
  - 年龄必填整数 1-150
  - 性别必选
  - 职业必填1-64 字符
  - 月预算必填>0
  - 险种至少选 1 
  - 保额>=1 
  - 保障期限必选

草稿保存
  - useDraft composable  自动存 localStorage
  - 提交成功后清除草稿

3.8 RecommendPage.vue — 主页面

结构:
  RecommendForm表单
  ↓ 提交后
  loading 状态(轮询中...
  ↓ 完成后
  RecommendResult结果展示

轮询逻辑:

// 提交表单 → 拿到 task_id
// setInterval(2s) 轮询 GET /api/recommend/generate/{task_id}
// status=processing → 继续
// status=done → 展示方案
// status=failed → 显示错误
// 超过 60 次轮询 → 超时提示

3.9 RecommendResult.vue — 结果展示

结构:
  三套方案Collapse 折叠面板):
    基础方案 | 均衡方案 | 全面方案
    每套方案内:
      产品表格(产品名/保额/年保费/推荐理由)
      总保费
      方案总结
  操作栏:[浏览器打印] [重新生成] [导出 PDF] [分享]
  免责声明

3.10 RecommendHistory.vue — 历史方案列表

功能:
  分页表格:方案名称 / 客户姓名 / 险种 / 状态 / 创建时间 / 操作
  筛选:客户姓名 / 险种 / 时间范围
  操作:查看详情 / 导出 / 删除

3.11 RecommendDetail.vue — 方案详情

功能:
  展示完整方案内容
  导出按钮PDF / Word / PPT
  分享按钮 → 生成链接 → 复制

验证标准

# 端到端
1. /recommend → 填写客户信息 → 点击"生成方案"
2. 页面显示 loading → 轮询中...
3. ~15 秒后显示 3 套方案
4. 点击"导出 PDF" → 浏览器下载 PDF
5. /recommend/history → 看到刚才生成的方案

Phase 4企微集成1.5 天)

目标:企微私聊/群聊能问 BaoDan 问题。

后端

4.1 wecom_api.py — 企微 API 封装

class WeComAPI:
    def get_access_token(self):
        """获取企微 access_token缓存到 Redis提前 5 分钟刷新)"""

    def send_text_message(self, user_id, content):
        """单聊发消息"""

    def send_group_message(self, chat_id, content):
        """群聊发消息"""

    def send_long_message(self, user_id, content):
        """长消息自动分段发送(>2048字节时分段间隔500ms"""

4.2 wecom_bot.py — 消息回调处理

GET /api/wecom/callbackURL 验证)
  1. SHA1(sort([Token, timestamp, nonce, echostr]))
  2. 与 msg_signature 比对
  3. 验证通过 → 返回解密后的 echostr

POST /api/wecom/callback消息接收
  1. 立即返回 "success"5 秒内)
  2. 异步处理:
     a. 验证签名 + 解密 XML
     b. 判断 MsgType
        - text → 继续处理
        - image/voice/video/file → 回复"暂不支持该消息类型"
        - event → 忽略
     c. 群聊消息 → 去掉"@机器人"前缀
     d. 查 wecom_user_mapping 获取用户
     e. 调 BaoDan Chat APIblocking
     f. 通过企微 API 回复

关键5 秒内必须返回 success。所以用 Flask 的异步机制threading/queue先返回再处理。

4.3 wecom_oauth.py — OAuth 登录

GET /api/wecom/oauth
  构造企微授权 URL → 302 重定向

GET /api/wecom/oauth/callback
  1. 用 code 调企微 API → 获取用户信息
  2. 查/建 wecom_user_mapping
  3. 签发 JWT
  4. 302 重定向到前端URL 带 token

4.4 XML 加解密

# 企微消息加解密AES-CBC
# 参考企微官方 SDK 或自行实现
# 核心decrypt_msg(encrypted_msg) → 明文 XML

验证标准

# 模拟企微回调
curl "http://localhost:5001/api/wecom/callback?msg_signature=xxx&timestamp=xxx&nonce=xxx&echostr=encrypted_echostr"
# → 返回解密后的 echostr

# 模拟消息推送(用企微调试工具)
# → 机器人回复 AI 回答

Phase 5用户权限管理1 天)

目标:管理员能管用户、配角色、数据隔离。

后端

5.1 用户 CRUD — /api/admin/users

GET    /api/admin/users         → 分页列表(支持 role/department/keyword 筛选)
POST   /api/admin/users         → 新增用户(插入 wecom_user_mapping
PUT    /api/admin/users/{id}    → 编辑(改角色/部门/状态)
DELETE /api/admin/users/{id}    → 停用status=disabled + 吊销所有 Token

5.2 批量导入 — /api/admin/users/batch-import

POSTmultipart/form-data → 解析 Excel → 批量插入

5.3 角色管理 — /api/admin/roles

GET    /api/admin/roles         → 5 个内置角色 + 自定义角色
POST   /api/admin/roles         → 新建角色name + permissions 数组)
PUT    /api/admin/roles/{id}    → 更新权限
DELETE /api/admin/roles/{id}    → 删除(内置角色不可删)

5.4 权限校验中间件 — middleware.py

# 在需要权限的路由上加装饰器:
@require_permission('kb_manage')
def upload_document():
    ...

# 权限列表:
# chat, proposal, kb_manage, kb_view, log_view, user_manage,
# config_manage, team_stats, system_log

5.5 数据权限5.2.3

这是 BaoDan 不具备的核心能力:

- 销售(sales) → 只能看自己的数据(推荐记录、对话)
- 主管(manager) → 看本组数据(同 department
- 管理员(admin) / 超级管理员(super_admin) → 看全部

实现方式:在查询接口中自动注入 WHERE user_id = ?WHERE department = ? 条件。

前端

5.6 UsersPage.vue

功能:
  用户表格(分页):用户名/企微ID/角色/部门/状态/操作
  新增用户弹窗
  编辑用户弹窗
  停用确认弹窗
  批量导入按钮(上传 Excel

5.7 PermissionsPage.vue

功能:
  左侧:角色列表(可选中)
  右侧权限配置面板checkbox 分组)
    对话:智能问答 / 产品推荐
    管理:知识库管理 / 对话日志 / 用户管理 / 数据统计 / 系统日志
  [保存权限] 按钮

验证标准

# 管理员操作
1. 登录 admin → /admin/users → 看到用户列表
2. 新增一个 sales 角色用户
3. 用新用户登录 → 只能看到 /chat、/recommend
4. 访问 /admin/users → 返回 403

Phase 6数据统计1 天)

目标:管理员能看到系统使用数据。

后端

6.1 GET /api/stats/overview

处理:
  今日问答数 → 查 BaoDan 对话记录(或自建统计表)
  活跃用户数 → 查 wecom_user_mapping 中 last_active_at 在今天内的
  知识库命中率 → 查 BaoDan 日志中有 retriever_resources 的比例
  文档总数 → 查 BaoDan datasets/documents 数量
输出:{ today_chats, active_users, kb_hit_rate, total_documents }

6.2 GET /api/stats/trend

输入:?metric=chat_count&start_date=2026-05-01&end_date=2026-05-31&granularity=day
处理:按天聚合数据
输出:{ metric, granularity, data: [{ date, value }] }

6.3 GET /api/stats/kb-health

输入:?days=30
处理:
  热门问题 TOP20 → 按 query 文本分组计数
  未命中问题 → 检索结果 score < 0.5 的问题
  文档覆盖率 → 各险种文档数量统计
输出:{ hot_questions, missed_questions, coverage }

注:热门问题和未命中问题需要在问答接口中记录日志到自研表(或从 BaoDan 日志中聚合)。

前端

6.4 StatsPage.vue

结构:
  顶栏4 个指标卡片(今日问答/活跃用户/命中率/文档数)
  中间趋势折线图ECharts7天/30天切换
  底部左:险种分布饼图
  底部右:热门问题 TOP20 表格

验证标准

浏览器打开 /admin/stats → 看到指标卡片 + 图表
(数据量少时可能为空,但页面不报错)

Phase 7知识库增强1 天)

目标BaoDan 后台不支持的 KB 功能(编号/标签/数据源)。

后端

7.1 数据库追加表

-- 文档元数据(编号、标签、险种、保司)
CREATE TABLE document_metadata (
    id SERIAL PRIMARY KEY,
    baodan_document_id VARCHAR(64) NOT NULL,
    dataset_id VARCHAR(64) NOT NULL,
    doc_number VARCHAR(32),        -- 编号,如 CJ-XX-001
    insurance_type VARCHAR(32),     -- 险种
    company VARCHAR(64),            -- 保司
    tags JSONB DEFAULT '[]',        -- 自定义标签
    created_at TIMESTAMP DEFAULT NOW(),
    updated_at TIMESTAMP DEFAULT NOW()
);

-- 保司 API 数据源配置
CREATE TABLE datasource_configs (
    id SERIAL PRIMARY KEY,
    name VARCHAR(128) NOT NULL,
    api_url VARCHAR(512) NOT NULL,
    api_key_encrypted TEXT,         -- AES 加密存储
    sync_frequency VARCHAR(32),     -- CRON 表达式
   险种 VARCHAR(32),
    last_sync_at TIMESTAMP,
    last_sync_status VARCHAR(16),
    last_sync_count INTEGER,
    status VARCHAR(16) DEFAULT 'active',
    created_at TIMESTAMP DEFAULT NOW()
);

-- 同步日志
CREATE TABLE datasource_sync_logs (
    id SERIAL PRIMARY KEY,
    datasource_id INTEGER REFERENCES datasource_configs(id),
    status VARCHAR(16),             -- success/failed
    items_added INTEGER DEFAULT 0,
    items_updated INTEGER DEFAULT 0,
    items_deleted INTEGER DEFAULT 0,
    error_message TEXT,
    started_at TIMESTAMP DEFAULT NOW(),
    completed_at TIMESTAMP
);

7.2 文档管理增强接口

POST   /api/kb/documents/upload      → 上传文档(同时写 document_metadata
GET    /api/kb/documents             → 文档列表JOIN document_metadata 拿编号/标签)
PATCH  /api/kb/documents/{id}        → 更新编号/标签
GET    /api/kb/documents/{id}/status → 处理状态
DELETE /api/kb/documents/{id}        → 删除文档
POST   /api/kb/documents/{id}/retry  → 重试

7.3 保司数据源接口

GET    /api/kb/datasources            → 数据源列表
POST   /api/kb/datasources            → 新建数据源
POST   /api/kb/datasources/{id}/sync  → 手动同步(异步)
GET    /api/kb/datasources/{id}/logs  → 同步日志

7.4 编号自动生成规则

def generate_doc_number(insurance_type, company):
    """
    规则:险种代码-保司代码-序号
CJ-XX-001重疾险-XX人寿-第1个文档
    险种代码CJ=重疾, RS=寿险, YL=医疗, YW=意外, CX=车险, NJ=年金, CX=储蓄
    """

前端

7.5 KBManagePage.vue

功能:
  文档列表表格(分页):编号/文件名/险种/保司/标签/状态/操作
  上传文档弹窗(多文件 + 选择险种 + 填写保司)
  编辑元数据弹窗(修改编号、标签)
  删除确认

7.6 DataSourcePage.vue

功能:
  数据源列表表格:名称/URL/同步频率/上次同步/状态/操作
  新建数据源弹窗
  [立即同步] 按钮 → 显示同步进度
  同步日志查看

验证标准

1. /admin/kb → 上传一个 MD 文件 → 选择险种 → 看到文档在列表中
2. 点编辑 → 修改编号为 CJ-XX-001 → 保存成功
3. /admin/datasource → 新建一个数据源 → 点击"立即同步"

Phase 8导出 + 日志 + 收尾0.5 天)

导出功能

8.1 PDF 导出

# 使用 reportlab 或 weasyprint
# 按模板渲染推荐方案 → 生成 PDF
# 需要客户提供 PDF 模板(或先用简单模板)

8.2 Word 导出

# 使用 python-docx
# 最简单的导出方式,优先实现

8.3 PPT 导出

# 使用 python-pptx
# 按模板生成 PPT需要客户提供模板

系统日志

8.4 system_operation_logs 记录

在以下操作时自动写入:

  • 登录/登出
  • 文档上传/删除
  • 角色权限变更
  • 推荐方案生成

通知告警(可选)

8.5 企微 Webhook 通知

def send_alert(message):
    """通过企微机器人 Webhook 发送告警"""
    webhook_url = config.WECOM_WEBHOOK_URL
    requests.post(webhook_url, json={"msgtype": "text", "text": {"content": message}})

告警场景:

  • 数据源同步失败
  • Token 月消耗超预算

五、依赖安装清单

后端Python

PyJWT==2.*           # JWT 签发
bcrypt==4.*          # 密码哈希
python-docx==1.*     # Word 导出
reportlab==4.*       # PDF 导出(可选)
python-pptx==1.*     # PPT 导出(可选)
openpyxl==3.*        # Excel 批量导入
lxml==5.*            # XML 解析(企微消息)
cryptography==43.*   # AES 加解密(企微消息 + API Key
celery==5.*          # 异步任务(企微消息处理、文档同步)

大部分依赖 BaoDan 已有Flask、SQLAlchemy、Redis不需要重复安装。

前端Node

vue@3
vue-router@4
element-plus
axios
marked              # Markdown 渲染
echarts             # 统计图表

六、风险与应对

风险 影响 应对
BaoDan Workflow 输出格式不稳定 推荐方案解析失败 在 workflow_helper.py 中加容错解析 + fallback
企微消息加解密实现复杂 回调不通 参考企微官方 Python SDKwxwork-sdk
9GB 知识库上传慢 首次导入耗时长 分批上传 + 异步处理 + 进度反馈
BaoDan 版本升级破坏接口 自研代码不可用 只改 main.py + app_factory.pyinsurance/ 独立
一人开发工期紧 10 天可能不够 Phase 7-8 可推后Phase 0-5 是 MVP

七、MVP 交付标准

Phase 0-5 完成后即可进入测试:

✅ 能登录(企微 + 账密)
✅ 能在 iframe 中跟 BaoDan 对话
✅ 能筛选险种
✅ 能填写客户信息生成推荐方案
✅ 能查看历史方案
✅ 企微私聊能问答
✅ 管理员能管用户和角色

Phase 6-8 为增强功能,可并行测试:

⏳ 数据统计仪表盘
⏳ 知识库编号/标签管理
⏳ 保司数据源同步
⏳ PDF/Word 导出
⏳ 系统操作日志