# 保险智能客服系统 — 完整开发计划 > **基于**:需求文档 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 的 db:from 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 创建后加: ```python 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.json`、`router`、`App.vue`。安装依赖后 `pnpm dev` 验证。 **0.5 统一响应拦截器** 确保 `src/utils/api.ts` 的 axios 拦截器处理 `code !== 0` 的情况。 #### 验证标准 ```bash # 后端 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 — 配置项** ```python 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 — 统一响应** ```python 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_logs(action=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 → 成功则重试原请求 → 失败跳登录页 #### 验证标准 ```bash # 后端 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 " http://localhost:5001/api/auth/refresh-token # → 返回新 token curl -H "Authorization: Bearer " http://localhost:5001/api/auth/refresh-token # → 旧 token 已失效 ``` --- ### Phase 2:对话页面(1 天) **目标**:用户能在 iframe 中跟 BaoDan 对话,筛选器可用。 #### 后端(0.5 天) **2.1 POST /api/chat/message(SSE 流式)** 这是核心接口,封装 BaoDan Chat API: ``` 输入:{ session_id, message, filters } 处理: 1. JWT 鉴权 2. 参数校验 3. 根据 filters.险种 选择对应的 BaoDan App(不同险种不同知识库) - 无险种 → 全库 App - 重疾险 → 重疾险专用 App - 以此类推 4. 调 BaoDan Chat API(blocking 模式): 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(筛选器) │ ├─────────────────────────────┤ │ │ │ iframe(BaoDan 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 控制显示哪些菜单 - 顶部栏:页面标题 + 用户名 + 退出 #### 验证标准 ```bash # 浏览器 1. 登录 → 自动跳 /chat 2. iframe 加载出 BaoDan 对话界面 3. 筛选器下拉选择"重疾险" 4. 在 iframe 中提问 → BaoDan 回答 5. 侧边栏点击"产品推荐" → 跳转正常 ``` --- ### Phase 3:产品推荐(2 天)— 最大自研模块 **目标**:填写客户信息 → 生成方案 → 查看结果 → 导出。 #### 后端(1 天) **3.1 数据模型扩展 — recommendation_records** 确认表结构满足需求,补充 `share_token` 和 `share_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_records(status=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_records(status=done, generated_plan=...) 7. 记录操作日志 输出:{ task_id, status } ``` > 关键:Workflow 返回的是 Markdown 格式的方案文本,需要在 workflow_helper.py 中解析为结构化 JSON。 **3.3 workflow_helper.py — BaoDan Workflow 调用封装** ```python 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. 返回下载链接(带临时 token,30 分钟有效) 输出:{ 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 — 表单组件** ```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(结果展示) ``` 轮询逻辑: ```javascript // 提交表单 → 拿到 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) 分享按钮 → 生成链接 → 复制 ``` #### 验证标准 ```bash # 端到端 1. /recommend → 填写客户信息 → 点击"生成方案" 2. 页面显示 loading → 轮询中... 3. ~15 秒后显示 3 套方案 4. 点击"导出 PDF" → 浏览器下载 PDF 5. /recommend/history → 看到刚才生成的方案 ``` --- ### Phase 4:企微集成(1.5 天) **目标**:企微私聊/群聊能问 BaoDan 问题。 #### 后端 **4.1 wecom_api.py — 企微 API 封装** ```python 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/callback(URL 验证) 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 API(blocking) 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 加解密** ```python # 企微消息加解密(AES-CBC) # 参考企微官方 SDK 或自行实现 # 核心:decrypt_msg(encrypted_msg) → 明文 XML ``` #### 验证标准 ```bash # 模拟企微回调 curl "http://localhost:5001/api/wecom/callback?msg_signature=xxx×tamp=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** ``` POST(multipart/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** ```python # 在需要权限的路由上加装饰器: @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 分组) 对话:智能问答 / 产品推荐 管理:知识库管理 / 对话日志 / 用户管理 / 数据统计 / 系统日志 [保存权限] 按钮 ``` #### 验证标准 ```bash # 管理员操作 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 个指标卡片(今日问答/活跃用户/命中率/文档数) 中间:趋势折线图(ECharts,7天/30天切换) 底部左:险种分布饼图 底部右:热门问题 TOP20 表格 ``` #### 验证标准 ```bash 浏览器打开 /admin/stats → 看到指标卡片 + 图表 (数据量少时可能为空,但页面不报错) ``` --- ### Phase 7:知识库增强(1 天) **目标**:BaoDan 后台不支持的 KB 功能(编号/标签/数据源)。 #### 后端 **7.1 数据库追加表** ```sql -- 文档元数据(编号、标签、险种、保司) 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 编号自动生成规则** ```python 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/同步频率/上次同步/状态/操作 新建数据源弹窗 [立即同步] 按钮 → 显示同步进度 同步日志查看 ``` #### 验证标准 ```bash 1. /admin/kb → 上传一个 MD 文件 → 选择险种 → 看到文档在列表中 2. 点编辑 → 修改编号为 CJ-XX-001 → 保存成功 3. /admin/datasource → 新建一个数据源 → 点击"立即同步" ``` --- ### Phase 8:导出 + 日志 + 收尾(0.5 天) #### 导出功能 **8.1 PDF 导出** ```python # 使用 reportlab 或 weasyprint # 按模板渲染推荐方案 → 生成 PDF # 需要客户提供 PDF 模板(或先用简单模板) ``` **8.2 Word 导出** ```python # 使用 python-docx # 最简单的导出方式,优先实现 ``` **8.3 PPT 导出** ```python # 使用 python-pptx # 按模板生成 PPT(需要客户提供模板) ``` #### 系统日志 **8.4 system_operation_logs 记录** 在以下操作时自动写入: - 登录/登出 - 文档上传/删除 - 角色权限变更 - 推荐方案生成 #### 通知告警(可选) **8.5 企微 Webhook 通知** ```python 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 SDK(wxwork-sdk) | | 9GB 知识库上传慢 | 首次导入耗时长 | 分批上传 + 异步处理 + 进度反馈 | | BaoDan 版本升级破坏接口 | 自研代码不可用 | 只改 main.py + app_factory.py,insurance/ 独立 | | 一人开发工期紧 | 10 天可能不够 | Phase 7-8 可推后,Phase 0-5 是 MVP | --- ## 七、MVP 交付标准 Phase 0-5 完成后即可进入测试: ``` ✅ 能登录(企微 + 账密) ✅ 能在 iframe 中跟 BaoDan 对话 ✅ 能筛选险种 ✅ 能填写客户信息生成推荐方案 ✅ 能查看历史方案 ✅ 企微私聊能问答 ✅ 管理员能管用户和角色 ``` Phase 6-8 为增强功能,可并行测试: ``` ⏳ 数据统计仪表盘 ⏳ 知识库编号/标签管理 ⏳ 保司数据源同步 ⏳ PDF/Word 导出 ⏳ 系统操作日志 ```