# 保险智能客服系统 — 后端开发文档 > **文档版本**:V1.0 > **更新日期**:2026-06-25 > **技术栈**:Python + Flask + SQLAlchemy + PostgreSQL --- ## 一、项目结构 ``` api/ ├── insurance/ # 保险模块(自研代码) │ ├── __init__.py │ ├── routes.py # Blueprint 注册 │ ├── auth/ # 认证模块 │ │ ├── routes.py │ │ └── service.py │ ├── chat/ # 对话模块 │ │ ├── routes.py │ │ ├── service.py │ │ └── agent_routes.py │ ├── recommend/ # 推荐模块 │ │ ├── routes.py │ │ ├── service.py │ │ └── workflow_helper.py │ ├── admin/ # 管理模块 │ │ ├── routes.py │ │ ├── service.py │ │ ├── template_service.py │ │ ├── notification_service.py │ │ └── department_service.py │ ├── kb/ # 知识库模块 │ │ ├── routes.py │ │ └── service.py │ ├── stats/ # 统计模块 │ │ ├── routes.py │ │ └── service.py │ ├── wecom/ # 企微模块 │ │ ├── routes.py │ │ └── service.py │ ├── middleware/ # 中间件 │ │ └── auth_middleware.py │ ├── models/ # 数据模型 │ │ ├── wecom_user.py │ │ ├── recommendation.py │ │ ├── template.py │ │ ├── notification.py │ │ ├── department.py │ │ ├── role.py │ │ └── prompt.py │ ├── db/ # 数据库 │ │ └── compat.py │ └── utils/ # 工具函数 │ ├── error_handler.py │ └── audit.py ├── main.py # BaoDan 入口(已修改) └── scripts/ ├── patch_app.py # 路由注册补丁 └── entrypoint.sh # 容器入口脚本 ``` --- ## 二、待开发功能 ### 2.1 产品推荐 Workflow 调用 **目标**:封装 Dify Workflow API,实现产品推荐 **文件**:`api/insurance/recommend/workflow_helper.py` #### 当前代码 ```python """BaoDan Workflow 调用封装。""" import requests from flask import current_app class WorkflowHelper: """Workflow API 调用封装。""" def run_workflow(self, inputs: dict, user_id: str) -> dict: """执行 Workflow 获取推荐方案。""" base_url = current_app.config.get("BAODAN_API_URL", "http://localhost:5001") api_key = current_app.config.get("BAODAN_WORKFLOW_API_KEY", "") resp = requests.post( f"{base_url}/v1/workflows/run", json={ "inputs": inputs, "response_mode": "blocking", "user": f"recommend_{user_id}", }, headers={"Authorization": f"Bearer {api_key}"}, timeout=120, ) return resp.json() ``` #### 需要修改 1. 添加错误处理 2. 添加重试机制 3. 解析 Workflow 输出 ```python def run_workflow(self, inputs: dict, user_id: str) -> dict: """执行 Workflow 获取推荐方案。""" base_url = current_app.config.get("BAODAN_API_URL", "http://localhost:5001") api_key = current_app.config.get("BAODAN_WORKFLOW_API_KEY", "") try: resp = requests.post( f"{base_url}/v1/workflows/run", json={ "inputs": inputs, "response_mode": "blocking", "user": f"recommend_{user_id}", }, headers={"Authorization": f"Bearer {api_key}"}, timeout=120, ) if resp.status_code != 200: return {"code": 5001, "message": f"Workflow 调用失败: {resp.status_code}"} result = resp.json() # 解析 Workflow 输出 if result.get("status") == "succeeded": outputs = result.get("data", {}).get("outputs", {}) return {"code": 0, "data": outputs} else: error = result.get("error", "未知错误") return {"code": 5001, "message": f"Workflow 执行失败: {error}"} except requests.Timeout: return {"code": 5001, "message": "Workflow 调用超时"} except Exception as e: return {"code": 9999, "message": f"Workflow 调用异常: {str(e)}"} ``` --- ### 2.2 产品推荐 API 完善 **目标**:完善 generate/status/export/share API **文件**:`api/insurance/recommend/routes.py` #### 当前代码 ```python @recommend_bp.route("/generate", methods=["POST"]) @jwt_required def generate(): """生成推荐方案。""" data = request.get_json() if not data: return jsonify({"code": 1001, "message": "请求体不能为空", "data": None}), 400 user_id = request.user_id # 参数校验 errors = [] # ... 校验逻辑 if errors: return jsonify({"code": 1001, "message": "; ".join(errors), "data": None}), 400 # 创建推荐记录 from insurance.recommend.service import RecommendService service = RecommendService() result = service.create_proposal(user_id, data) if result["code"] != 0: return jsonify(result), 400 task_id = result["data"]["task_id"] # 异步调用 Workflow import threading threading.Thread( target=self._run_workflow_async, args=(task_id, data, user_id), daemon=True, ).start() return jsonify({"code": 0, "data": {"task_id": task_id, "status": "processing"}}) ``` #### 需要实现 1. `_run_workflow_async` 方法 2. `get_status` API 3. `export` API 4. `share` API ```python def _run_workflow_async(self, task_id: str, data: dict, user_id: str): """异步执行 Workflow。""" from insurance.recommend.service import RecommendService from insurance.recommend.workflow_helper import WorkflowHelper service = RecommendService() workflow = WorkflowHelper() # 调用 Workflow result = workflow.run_workflow(data, user_id) if result["code"] == 0: service.update_proposal(task_id, "done", result["data"]) else: service.update_proposal(task_id, "failed", error_message=result["message"]) @recommend_bp.route("/generate/", methods=["GET"]) @jwt_required def get_status(task_id): """查询推荐任务状态。""" from insurance.recommend.service import RecommendService service = RecommendService() result = service.get_proposal_status(task_id) return jsonify(result) @recommend_bp.route("//export", methods=["POST"]) @jwt_required def export_proposal(proposal_id): """导出推荐方案。""" data = request.get_json() format_type = data.get("format", "pdf") from insurance.recommend.service import RecommendService service = RecommendService() result = service.export_proposal(proposal_id, format_type) if result["code"] != 0: return jsonify(result), 400 return jsonify(result) @recommend_bp.route("//share", methods=["POST"]) @jwt_required def share_proposal(proposal_id): """生成分享链接。""" from insurance.recommend.service import RecommendService service = RecommendService() result = service.create_share_link(proposal_id) return jsonify(result) ``` --- ### 2.3 批量导入用户 **目标**:支持 Excel 批量导入用户 **文件**:`api/insurance/admin/routes.py` #### 添加路由 ```python @bp.route("/users/batch-import", methods=["POST"]) @super_admin_required def batch_import_users(): """批量导入用户。""" file = request.files.get("file") if not file: return jsonify({"code": 1001, "message": "请选择文件", "data": None}), 400 # 解析 Excel import openpyxl wb = openpyxl.load_workbook(file) ws = wb.active users = [] for row in ws.iter_rows(min_row=2, values_only=True): username, department, role = row[0], row[1], row[2] if username: users.append({ "username": username, "department": department or "", "role": role or "sales", }) # 批量插入 from insurance.admin.service import AdminService service = AdminService() result = service.batch_create_users(users) return jsonify(result) ``` #### 实现 service 方法 ```python def batch_create_users(self, users: list) -> dict: """批量创建用户。""" from insurance.models.wecom_user import WeComUserMapping from insurance.db.compat import db import bcrypt import uuid created = 0 for user_data in users: username = user_data.get("username", "") if not username: continue # 检查用户是否已存在 existing = db.session.query(WeComUserMapping).filter_by(username=username).first() if existing: continue # 生成默认密码 default_password = "12345678" password_hash = bcrypt.hashpw(default_password.encode(), bcrypt.gensalt()).decode() # 创建用户 mapping = WeComUserMapping( wecom_userid=f"batch_{uuid.uuid4().hex[:8]}", internal_user_id=f"user_{uuid.uuid4().hex[:8]}", username=username, password_hash=password_hash, department=user_data.get("department", ""), role=user_data.get("role", "sales"), status="active", ) db.session.add(mapping) created += 1 db.session.commit() return {"code": 0, "message": f"成功导入 {created} 个用户", "data": {"created": created}} ``` --- ### 2.4 数据权限过滤 **目标**:实现销售只看自己的数据,主管看本组数据 **文件**:`api/insurance/middleware/auth_middleware.py` #### 当前代码 ```python def get_data_scope(): """获取当前用户的数据权限范围。""" role = getattr(request, "user_role", "") user_id = getattr(request, "user_id", "") # 超级管理员和管理员:查看所有数据 if role in ("super_admin", "admin"): return {"scope": "all", "department": None, "user_id": None} # 销售主管:查看本部门数据 if role == "manager": department = getattr(request, "user_department", None) return {"scope": "team", "department": department, "user_id": None} # 销售人员和其他角色:只看自己的数据 return {"scope": "self", "department": None, "user_id": user_id} ``` #### 在查询接口中使用 ```python def get_proposals(user_id, params): """获取推荐记录列表(带数据权限)。""" from insurance.middleware.auth_middleware import get_data_scope scope = get_data_scope() query = db.session.query(RecommendationRecord) if scope["scope"] == "self": # 销售:只看自己的 query = query.filter(RecommendationRecord.user_id == user_id) elif scope["scope"] == "team": # 主管:看本部门的 query = query.filter( RecommendationRecord.user_id.in_( db.session.query(WeComUserMapping.id).filter( WeComUserMapping.department == scope["department"] ) ) ) # admin/super_admin:看全部 return query.all() ``` --- ### 2.5 文档编号自动生成 **目标**:文档上传时自动生成编号 **文件**:`api/insurance/kb/service.py` #### 添加方法 ```python def generate_doc_number(self, insurance_type: str, company: str) -> str: """生成文档编号。""" from insurance.db.compat import db from sqlalchemy import text type_codes = { "重疾险": "CJ", "寿险": "RS", "医疗险": "YL", "意外险": "YW", "车险": "CX", "年金险": "NJ", "储蓄险": "CX", } type_code = type_codes.get(insurance_type, "XX") company_code = company[:2] if company else "XX" # 查询当前序号 result = db.session.execute( text(""" SELECT COUNT(*) FROM document_metadata WHERE insurance_type = :type AND company = :company """), {"type": insurance_type, "company": company} ) count = result.scalar() or 0 return f"{type_code}-{company_code}-{count + 1:03d}" ``` --- ## 三、API 接口清单 ### 3.1 产品推荐 API | 方法 | 路径 | 说明 | 状态 | |------|------|------|:----:| | POST | /recommend/generate | 生成推荐方案 | ✅ 已实现 | | GET | /recommend/generate/{task_id} | 查询任务状态 | ✅ 已实现 | | POST | /recommend/{id}/export | 导出方案 | ✅ 已实现 | | POST | /recommend/{id}/share | 生成分享链接 | ✅ 已实现 | | GET | /recommend/list | 推荐历史列表 | ✅ 已实现 | | DELETE | /recommend/{id} | 删除推荐记录 | ✅ 已实现 | ### 3.2 用户管理 API | 方法 | 路径 | 说明 | 状态 | |------|------|------|:----:| | GET | /admin/users | 用户列表 | ✅ 已实现 | | POST | /admin/users | 新增用户 | ✅ 已实现 | | PUT | /admin/users/{id} | 编辑用户 | ✅ 已实现 | | DELETE | /admin/users/{id} | 停用用户 | ✅ 已实现 | | POST | /admin/users/batch-import | 批量导入 | ⚠️ 待实现 | | POST | /admin/users/sync-to-dify | 同步到 Dify | ✅ 已实现 | ### 3.3 知识库 API | 方法 | 路径 | 说明 | 状态 | |------|------|------|:----:| | GET | /kb/documents | 文档列表 | ✅ 已实现 | | POST | /kb/documents/upload | 上传文档 | ✅ 已实现 | | GET | /kb/documents/{id}/status | 文档状态 | ✅ 已实现 | | PATCH | /kb/documents/{id} | 更新元数据 | ✅ 已实现 | | DELETE | /kb/documents/{id} | 删除文档 | ✅ 已实现 | | POST | /kb/documents/{id}/retry | 重试文档 | ✅ 已实现 | --- ## 四、数据库变更 ### 4.1 添加分享字段 ```sql -- 添加分享相关字段 ALTER TABLE recommendation_records ADD COLUMN IF NOT EXISTS share_token VARCHAR(64), ADD COLUMN IF NOT EXISTS share_expire_at TIMESTAMP; ``` ### 4.2 添加文档元数据表 ```sql -- 文档元数据(编号、标签、险种、保司) CREATE TABLE IF NOT EXISTS document_metadata ( id SERIAL PRIMARY KEY, baodan_document_id VARCHAR(64) NOT NULL, dataset_id VARCHAR(64) NOT NULL, doc_number VARCHAR(32), insurance_type VARCHAR(32), company VARCHAR(64), tags JSONB DEFAULT '[]', created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); ``` --- ## 五、配置变更 ### 5.1 docker-compose.dify.yml ```yaml # 更新 Workflow API Key BAODAN_WORKFLOW_API_KEY: "app-real-workflow-api-key" ``` ### 5.2 环境变量 | 变量 | 说明 | 默认值 | |------|------|--------| | BAODAN_API_URL | BaoDan API 地址 | http://baodan-api:5001 | | BAODAN_WORKFLOW_API_KEY | Workflow API Key | app-placeholder | | BAODAN_CHAT_API_KEY | 对话 API Key | app-placeholder | | JWT_SECRET | JWT 密钥 | change-this | | GUEST_MODE | 访客模式 | true | --- ## 六、测试清单 | # | 接口 | 测试用例 | 验证标准 | |---|------|----------|----------| | 1 | POST /recommend/generate | 正常请求 | 返回 task_id | | 2 | POST /recommend/generate | 缺少参数 | 返回 1001 | | 3 | GET /recommend/generate/{id} | 查询状态 | 返回 status | | 4 | POST /recommend/{id}/export | 导出 PDF | 返回 download_url | | 5 | POST /recommend/{id}/share | 分享链接 | 返回 share_url | | 6 | POST /admin/users/batch-import | 上传 Excel | 用户增加 | | 7 | POST /admin/users/batch-import | 空文件 | 返回 1001 | | 8 | GET /admin/users | 权限过滤 | 销售只看自己 | --- ## 七、部署说明 ### 7.1 重新构建镜像 ```bash # 后端 cd D:\work\code\python\coding\baodanagent docker compose -f docker-compose.dify.yml build baodan-api # 重启服务 docker compose -f docker-compose.dify.yml up -d baodan-api ``` ### 7.2 数据库迁移 ```bash # 执行 SQL 变更 docker exec -i baodanagent-db-1 psql -U postgres -d baodan < deploy/sql/migration.sql ``` ### 7.3 验证部署 ```bash # 检查服务状态 docker compose -f docker-compose.dify.yml ps # 测试 API curl http://localhost:5001/health ```