baodan/docs/保险智能客服系统_编码规范.md

19 KiB
Raw Blame History

编码规范

一个人的项目更需要规范。没有规范,三个月后你自己都看不懂自己写的代码。 规范的核心目的:让代码可读、可维护、不纠结


一、技术栈与语言

层级 语言 框架 代码放在哪
后端 Python 3.12 + Flask BaoDan 原生框架 api/insurance/
前端 TypeScript Vue 3 + Vite frontend/src/
脚本 Python 3.12 脚本工具 scripts/
配置 YAML / JSON 项目根目录

二、命名规范

2.1 Python 后端

类型 规则 示例 反例
变量 小写 + 下划线 user_id, chat_history userId, ChatHistory
常量 全大写 + 下划线 MAX_RETRY_COUNT, BAODAN_BASE_URL maxRetry, baodanBaseUrl
函数 小写 + 下划线 get_user_info(), send_message() GetUserInfo(), sendMessage()
大驼峰 WecomClient, BaoDanService wecom_client, baodan_service
文件名 小写 + 下划线 wecom_client.py, config.py WecomClient.py, Config.py
模块/包 小写 + 下划线 routers/wecom_oauth.py routers/WecomOauth.py

2.2 TypeScript / Vue 前端

类型 规则 示例 反例
变量 小驼峰 userName, chatHistory user_name, UserName
常量 全大写 + 下划线 MAX_RETRY_COUNT maxRetryCount
函数 小驼峰 getUserInfo(), sendMessage() get_user_info()
接口/类型 大驼峰I 开头或直接命名 UserInfo, ApiResponse userInfo, i_user_info
Vue 组件 大驼峰 RecommendForm.vue, ChatPanel.vue recommend-form.vue
Vue 组件引用 大驼峰 <RecommendForm /> <recommend-form />
CSS 类名 短横线 .chat-panel, .btn-primary .chatPanel, .chat_panel

2.3 数据库

类型 规则 示例
表名 小写 + 下划线,复数 wecom_users, recommend_logs
字段名 小写 + 下划线 created_at, user_id
主键 id id SERIAL PRIMARY KEY
外键 {关联表单数}_id user_id, document_id

2.4 API 路径

规则 示例 反例
小写 + 短横线 /api/wecom/oauth /api/wecomOAuth
名词复数 /api/recommend/logs /api/recommend/log
版本号可选 /api/v1/wecom/callback

三、注释规范

3.1 什么需要注释

必须注释

  • 所有公开函数/方法的 docstring
  • 所有类的 docstring
  • 复杂业务逻辑(不是显而易见的代码)
  • 为什么要这样做的原因("为什么"比"做了什么"重要)

不需要注释

  • 简单的 getter/setter
  • 一看就懂的代码(user_id = data["user_id"]
  • TODO / FIXME用 issue 追踪,不要堆在代码里)

3.2 Python 注释格式

class BaoDanClient:
    """BaoDan API 客户端,负责与 BaoDan 后端通信。"""

    def chat(self, user_id: str, query: str, conversation_id: str = "") -> dict:
        """发送对话消息并获取 AI 回复。

        Args:
            user_id: 企微用户 ID作为 BaoDan 的用户标识
            query: 用户的问题文本
            conversation_id: 对话 ID空字符串表示新对话

        Returns:
            dict: 包含 answer回答文本和 conversation_id对话 ID

        Raises:
            BaoDanAPIError: BaoDan 返回非 200 状态码时抛出
        """
        # 企微要求 5 秒内响应,所以用 blocking 模式
        # 如果 BaoDan 响应超过 5 秒,调用方需要用异步队列处理
        resp = requests.post(...)
        ...

3.3 TypeScript / Vue 注释格式

/**
 * 调用 BaoDan Workflow 生成产品推荐方案
 *
 * @param formData - 用户填写的需求信息(年龄、职业、预算等)
 * @returns 推荐方案的 Markdown 文本
 * @throws 当 BaoDan API 调用失败时抛出错误
 */
async function generateRecommendation(formData: RecommendFormData): Promise<string> {
  // ...
}
<template>
  <!-- 产品推荐表单  用户填写需求信息 -->
  <div class="recommend-form">
    ...
  </div>
</template>

<script setup lang="ts">
/**
 * RecommendForm 组件
 *
 * 用户填写保险需求信息(年龄、职业、预算等),
 * 提交后调用后端接口生成 AI 推荐方案。
 */

// 表单数据 — 所有字段都有默认值,避免提交时空值
const formData = ref<RecommendFormData>({
  age: 30,
  gender: 'male',
  occupation: '',
  annualIncome: 0,
  budget: 0,
  insuranceTypes: [],
})
</script>

3.4 中文还是英文

场景 语言 原因
变量名/函数名 英文 代码必须英文
注释 中文 你是中文团队,读中文比英文快
Docstring 中文 同上
Git commit 英文 国际惯例,工具兼容性好
日志 中文 方便排查问题
错误信息 中文 用户/管理员看得懂

四、代码格式

4.1 Python

使用自动格式化工具,不要手动对齐:

# 安装
pip install ruff

# 格式化
ruff format backend/

# 检查代码风格
ruff check backend/

pyproject.toml 配置(放在 backend/ 目录下):

[tool.ruff]
line-length = 120          # 行宽 120比默认 88 宽一些,适合现代屏幕)
target-version = "py312"   # Python 版本

[tool.ruff.format]
quote-style = "double"     # 字符串用双引号

[tool.ruff.lint]
select = ["E", "F", "I"]  # 基础检查 + import 排序

4.2 TypeScript / Vue

使用 ESLint + Prettier

# 安装(在 frontend/ 目录下)
pnpm add -D eslint @vue/eslint-config-typescript prettier

# 格式化
pnpm prettier --write src/

# 检查
pnpm eslint src/

4.3 通用规则

规则 Python TypeScript
行宽 120 字符 120 字符
缩进 4 空格 2 空格
引号 双引号 " 单引号 '
分号 不需要 不需要Prettier 自动处理)
尾逗号

五、项目结构规范

5.1 Python 后端

backend/
├── main.py              # 入口文件,只做 app 初始化和路由注册
├── config.py            # 配置读取,所有配置从这里获取
├── routers/             # 路由层:只做参数校验和响应,不写业务逻辑
│   ├── __init__.py
│   ├── wecom.py         # 企微消息回调
│   ├── wecom_oauth.py   # 企微 OAuth
│   └── recommend.py     # 产品推荐
├── services/            # 业务层:核心逻辑在这里
│   ├── __init__.py
│   ├── baodan_client.py   # 封装 BaoDan API 调用
│   └── wecom_client.py  # 封装企微 API 调用
├── models/              # 数据模型
│   ├── __init__.py
│   └── schemas.py       # Pydantic 请求/响应模型
├── utils/               # 工具函数
│   ├── __init__.py
│   └── crypto.py        # 加解密工具
└── requirements.txt     # 依赖列表(锁定版本号)

分层原则

  • routers/ 只做 HTTP 相关的事(解析请求、校验参数、返回响应)
  • services/ 做业务逻辑(调 API、处理数据
  • models/ 定义数据结构Pydantic 模型)
  • utils/ 放通用工具函数

5.2 Vue 前端

frontend/src/
├── main.ts              # 入口
├── App.vue              # 根组件
├── router/
│   └── index.ts         # 路由定义
├── pages/               # 页面组件(一个路由对应一个页面)
│   ├── ChatPage.vue     # 对话页面
│   ├── RecommendPage.vue # 产品推荐页面
│   └── LoginPage.vue    # 登录页面
├── components/          # 通用组件(可复用的 UI 组件)
│   ├── RecommendForm.vue
│   └── RecommendResult.vue
├── composables/         # 组合函数(复用逻辑)
│   ├── useApi.ts        # API 请求封装
│   └── useAuth.ts       # 登录状态管理
├── utils/
│   └── request.ts       # HTTP 请求工具axios 封装)
└── types/
    └── index.ts         # TypeScript 类型定义

组件拆分原则

  • 一个 .vue 文件不超过 300 行,超过就拆分
  • pages/ 放页面级组件(有路由)
  • components/ 放可复用组件(无路由)
  • composables/ 放可复用逻辑(类似 React hooks

六、错误处理规范

6.1 Python 后端

# 定义统一的错误响应格式
class AppError(Exception):
    """业务异常基类"""
    def __init__(self, code: int, message: str):
        self.code = code
        self.message = message

class BaoDanAPIError(AppError):
    """BaoDan API 调用失败"""
    def __init__(self, detail: str = ""):
        super().__init__(code=2001, message=f"BaoDan API 调用失败: {detail}")

class WecomAPIError(AppError):
    """企微 API 调用失败"""
    def __init__(self, detail: str = ""):
        super().__init__(code=2002, message=f"企微 API 调用失败: {detail}")


# 全局异常处理器
@app.exception_handler(AppError)
async def app_error_handler(request, exc: AppError):
    return JSONResponse(
        status_code=200,  # 业务异常用 200靠 code 区分
        content={"code": exc.code, "message": exc.message, "data": None},
    )

@app.exception_handler(Exception)
async def general_error_handler(request, exc: Exception):
    # 记录日志
    logger.error(f"未处理的异常: {exc}", exc_info=True)
    return JSONResponse(
        status_code=500,
        content={"code": 9999, "message": "服务器内部错误", "data": None},
    )

6.2 Vue 前端

// utils/request.ts
import axios from 'axios'

const request = axios.create({
  baseURL: '/api',
  timeout: 60000, // 产品推荐可能要等 30-60 秒
})

// 响应拦截器:统一处理错误
request.interceptors.response.use(
  (response) => {
    const { code, message, data } = response.data
    if (code !== 0) {
      // 业务错误,用组件库的 message 提示
      ElMessage.error(message)
      return Promise.reject(new Error(message))
    }
    return data
  },
  (error) => {
    // 网络错误
    ElMessage.error('网络异常,请稍后重试')
    return Promise.reject(error)
  },
)

七、日志规范

7.1 日志级别

级别 什么时候用 示例
DEBUG 开发调试信息 请求参数、API 响应详情
INFO 正常业务流程 收到企微消息、AI 回复成功
WARNING 不影响功能但需要注意 API 响应慢、重试成功
ERROR 功能失败 BaoDan 调用失败、企微回复失败

7.2 日志格式

import logging

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
    datefmt="%Y-%m-%d %H:%M:%S",
)

logger = logging.getLogger(__name__)

# 使用
logger.info("收到企微消息: user=%s, content=%s", user_id, content[:50])
logger.error("BaoDan API 调用失败: status=%d, body=%s", resp.status_code, resp.text[:200])

7.3 日志原则

  1. 记录关键节点:收到请求、调用外部 API、返回结果
  2. 不要记录敏感信息API Key、用户密码、完整的消息内容记录前 50 字符即可)
  3. 包含上下文user_id、request_id 等,方便排查
  4. 日志不是调试工具:不要到处 print(),用 logger.debug() 代替

八、Git 分支管理

8.1 分支策略

一个人的项目不需要复杂的 Git Flow用简单的 主分支 + 功能分支 即可:

main           ← 稳定版本,可随时部署
  │
  ├── dev      ← 日常开发分支,功能合入这里
  │    │
  │    ├── feature/wecom-bot       ← 功能分支
  │    ├── feature/recommend       ← 功能分支
  │    └── fix/timeout-bug         ← 修复分支
  │
  └── hotfix/xxx                   ← 紧急修复(从 main 拉出)

8.2 分支命名

类型 命名规则 示例
主分支 main
开发分支 dev
功能分支 feature/功能名 feature/wecom-bot
修复分支 fix/问题描述 fix/message-timeout
紧急修复 hotfix/问题描述 hotfix/crash-on-startup

8.3 Commit Message 规范

格式:类型(范围): 描述

类型:
  feat     新功能
  fix      修复 bug
  docs     文档更新
  style    代码格式调整(不影响功能)
  refactor 重构
  test     测试
  chore    构建/依赖/配置变更

示例:

feat(wecom): 添加企微群聊消息接收功能
fix(recommend): 修复推荐结果为空时页面报错
docs(api): 更新产品推荐接口文档
refactor(baodan): 将 BaoDan 调用抽成独立 service
chore(deps): 升级 fastapi 到 0.115

8.4 工作流程

1. 从 dev 创建功能分支git checkout -b feature/xxx dev
2. 开发 + 提交(一个小功能一个 commit
3. 功能完成后合并回 devgit checkout dev && git merge feature/xxx
4. 删除功能分支git branch -d feature/xxx
5. dev 稳定后合并到 maingit checkout main && git merge dev
6. 打 taggit tag v0.1.0

8.5 Git 忽略文件

# .gitignore项目根目录

# Python
__pycache__/
*.pyc
.venv/
*.egg-info/

# Node
node_modules/
dist/

# 环境变量(包含密钥,绝对不能提交)
.env
.env.local
.env.production

# IDE
.vscode/
.idea/
*.swp

# 系统文件
.DS_Store
Thumbs.db

# BaoDan 源码(如果作为子模块引入)
baodan/

# 上传的文件
uploads/

九、依赖管理

9.1 Python 依赖

# 安装时锁定版本
pip install fastapi==0.115.0
pip install uvicorn==0.32.0

# 生成 requirements.txt
pip freeze > requirements.txt

# requirements.txt 里必须锁定版本号,不要用 >= 或 ~=
# 正确fastapi==0.115.0
# 错误fastapi>=0.100

9.2 Node 依赖

# 安装时精确版本
pnpm add axios@1.7.0

# 不要混用 npm 和 pnpm统一用 pnpm

9.3 核心依赖清单

Python 后端(backend/requirements.txt

fastapi==0.115.0
uvicorn==0.32.0
requests==2.32.0
python-dotenv==1.0.0
pydantic==2.10.0
wechatpy==1.8.18
cryptography==44.0.0

Vue 前端(frontend/package.json 核心依赖)

vue: ^3.5.0
vue-router: ^4.4.0
axios: ^1.7.0
marked: ^15.0.0
element-plus: ^2.9.0

十、代码审查清单(自检用)

一个人没有 code review 搭档,但提交前自己过一遍这个清单:

Python

  • 函数有 docstring 吗?
  • 参数有类型注解吗?
  • 异常有处理吗?(不要裸 except:
  • 日志有记录关键节点吗?
  • API Key / 密码等敏感信息没写死在代码里吧?
  • ruff formatruff check 通过了吗?

Vue / TypeScript

  • 组件不超过 300 行?
  • 变量有类型定义吗?(不要到处 any
  • 有 loading / error 状态处理吗?
  • API 请求超时设置合理吗?
  • ESLint 和 Prettier 通过了吗?

Git

  • commit message 符合规范?
  • .env 文件没有提交吧?
  • 不相关的改动没有混在这个 commit 里吧?

十一、BaoDan 集成规范

11.1 架构说明

自研代码放在 baodan-main/api/insurance/ 目录下,作为 Flask Blueprint 注册到 BaoDan 的 Flask 应用中。不使用 FastAPI,直接复用 BaoDan 的 Flask 框架。

11.2 代码组织

baodan-main/api/insurance/
├── __init__.py
├── wecom/
│   ├── wecom_bot.py        # 企微消息回调处理
│   ├── wecom_oauth.py      # 企微 OAuth 登录
│   └── routes.py           # Blueprint 路由
├── recommend/
│   ├── recommend_api.py    # 产品推荐接口
│   └── workflow_helper.py  # Workflow 调用封装
├── permissions/
│   ├── models.py           # 角色/权限数据模型
│   ├── middleware.py        # 权限校验
│   └── routes.py           # 角色管理接口
├── stats/
│   └── stats_api.py        # 数据统计接口
└── db/
    ├── wecom_user.py       # 企微用户映射表
    └── recommend_record.py # 推荐记录表

11.3 调用 BaoDan 核心功能(直接 import

# 调用 BaoDan RAG 检索(直接 import不走 HTTP
from core.rag.retrieval.dataset_retrieval import DatasetRetrieval

# 调用 BaoDan LLM
from core.model_runtime.model_manager import ModelManager

# 调用 BaoDan Workflow
from core.workflow.workflow_engine import WorkflowEngine

# 复用 BaoDan 数据库连接
from extensions.ext_database import db
from models.dataset import Dataset, Document

# 复用 BaoDan 认证
from extensions.ext_login import login_required

11.4 禁止事项

  • 不要在 api/insurance/ 以外的目录写代码

  • 不要修改 api/core/api/controllers/api/models/ 等 BaoDan 目录

  • 不要自己装 FastAPI——直接用 BaoDan 的 Flask

  • 不要自己建数据库连接——用 extensions.ext_database.db

  • 不要自己装 Redis 客户端——用 BaoDan 的 Redis 连接

  • 不要自己写日志系统——用 BaoDan 的 logging

    def chat(self, user_id: str, query: str, conversation_id: str = "") -> dict: """发送对话消息。返回 {answer, conversation_id, sources}""" resp = requests.post( f"{self.base_url}/chat-messages", headers={"Authorization": f"Bearer {self.app_key}"}, json={ "query": query, "response_mode": "blocking", "user": user_id, "conversation_id": conversation_id, }, timeout=60, ) resp.raise_for_status() data = resp.json() return { "answer": data.get("answer", ""), "conversation_id": data.get("conversation_id", ""), "sources": data.get("metadata", {}).get("retriever_resources", []), }

    def run_workflow(self, inputs: dict, user_id: str) -> dict: """执行 Workflow。返回 {status, outputs}""" resp = requests.post( f"{self.base_url}/workflows/run", headers={"Authorization": f"Bearer {self.workflow_key}"}, json={"inputs": inputs, "response_mode": "blocking", "user": user_id}, timeout=120, ) resp.raise_for_status() data = resp.json() return { "status": data.get("data", {}).get("status", "unknown"), "outputs": data.get("data", {}).get("outputs", {}), }


### 11.2 调用规则

| 规则 | 说明 |
|------|------|
| 统一入口 | 所有 BaoDan 调用走 `BaoDanClient`,不直接 `requests.post` |
| 超时设置 | Chat API 60 秒Workflow API 120 秒 |
| 错误处理 | 捕获异常后抛出 `BaoDanAPIError`,不要吞掉异常 |
| 日志记录 | 每次调用记录 request_id、user_id、耗时 |
| 重试策略 | BaoDan 5xx 错误重试 1 次4xx 不重试 |
| 用户标识 | 企微用户用 `wecom_{userid}`,网页用户用 `web_{baodan_user_id}` |

### 11.3 禁止事项

- 禁止在前端直接调用 BaoDan APIAPI Key 会暴露)
- 禁止在多个文件中重复写 BaoDan 调用逻辑
- 禁止不设超时调用 BaoDan可能导致请求永久挂起
- 禁止将 BaoDan 返回的原始 JSON 直接传给前端(需先清洗字段)