16 KiB
16 KiB
保险智能客服系统 — 后端开发文档
文档版本: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
当前代码
"""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()
需要修改
- 添加错误处理
- 添加重试机制
- 解析 Workflow 输出
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
当前代码
@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"}})
需要实现
_run_workflow_async方法get_statusAPIexportAPIshareAPI
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/<task_id>", 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("/<proposal_id>/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("/<proposal_id>/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
添加路由
@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 方法
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
当前代码
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}
在查询接口中使用
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
添加方法
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 添加分享字段
-- 添加分享相关字段
ALTER TABLE recommendation_records
ADD COLUMN IF NOT EXISTS share_token VARCHAR(64),
ADD COLUMN IF NOT EXISTS share_expire_at TIMESTAMP;
4.2 添加文档元数据表
-- 文档元数据(编号、标签、险种、保司)
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
# 更新 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 重新构建镜像
# 后端
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 数据库迁移
# 执行 SQL 变更
docker exec -i baodanagent-db-1 psql -U postgres -d baodan < deploy/sql/migration.sql
7.3 验证部署
# 检查服务状态
docker compose -f docker-compose.dify.yml ps
# 测试 API
curl http://localhost:5001/health