Files
iqudo-top1/docs/technical-implementation.md
2026-08-06 21:48:42 +08:00

494 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 销冠平台技术实现方案
## 1. 文档范围
本文档用于指导“场景化销售话术与 SOP 平台”的第一阶段实现。
第一阶段的核心闭环是:
```text
创建场景 -> 配置自定义字段 -> 编排 SOP -> 审核发布 -> 一线人员执行 -> 记录反馈
```
场景不写死在代码中。宠物医生问诊问药只是一个配置示例,后续可以创建保险咨询、房地产销售、教育课程顾问等其他场景。
本阶段暂不实现文件存储、AI 自动生成、CRM 集成和微服务拆分。
## 2. 技术选型
### 2.1 后端
- Go业务服务和流程执行引擎
- GinHTTP 路由、中间件和请求处理
- Zap结构化日志
- cleanenvYAML 配置解析和环境变量覆盖
- GORMMySQL 数据访问
- golang-migrate数据库迁移
- `go-playground/validator`:请求参数校验
- JWT + Refresh Token登录认证
第一阶段采用模块化单体。所有业务模块运行在一个 Go 服务中,通过清晰的内部边界保持可维护性,后续再根据实际负载拆分服务。
配置加载使用 `github.com/ilyakaznacheev/cleanenv`。它的行为简单明确:先读取 YAML 文件,再读取环境变量,环境变量值覆盖文件中的同名配置。
### 2.2 前端
- Vue 3
- Vite
- TypeScript
- Ant Design Vue
- Vue Router
- Pinia
- Axios
SOP 编辑器第一版使用步骤卡片和条件配置,不立即引入复杂的画布编辑器。需要图形化节点编排时,使用 Vue 生态的流程图组件,不使用 React Flow。
### 2.3 数据库和运行环境
- MySQL 8.0+
- Docker Compose本地开发
- Nginx生产环境反向代理
- Linux 容器部署
本阶段不引入 Redis。登录会话、场景配置和执行状态先使用 MySQL只有出现异步任务、缓存或高并发需求时再增加 Redis。
## 3. 配置管理
配置结构统一放在 `internal/config/`,业务代码不能直接读取环境变量。
配置优先级如下:
```text
结构体默认值 < config.yml < 当前环境配置文件 < 环境变量
```
配置文件约定如下:
```text
configs/config.example.yml # 配置模板,可提交,不包含敏感信息
configs/config.yml # 基础配置
configs/config.test.yml # 测试环境覆盖配置
configs/config.prod.yml # 生产环境覆盖配置
```
运行环境通过 `APP_ENV` 或启动参数 `--env` 选择,优先级为启动参数高于环境变量:
```text
未指定环境 -> config.yml
APP_ENV=test -> config.yml + config.test.yml
APP_ENV=prod -> config.yml + config.prod.yml
```
`config.yml` 作为基础文件必须存在。`test``prod` 环境对应的覆盖文件也必须存在;缺失时服务直接退出。环境配置文件只填写与基础配置不同的字段,未填写的字段沿用 `config.yml`
配置文件路径通过 `--config-dir` 指定,默认使用当前工作目录下的 `configs/`。部署时可以挂载外部配置目录,不需要把真实生产配置提交到代码仓库。
环境变量名称通过结构体标签显式声明,例如:
```text
APP_SERVER_HOST
APP_SERVER_PORT
APP_DATABASE_HOST
APP_DATABASE_PORT
APP_DATABASE_USER
APP_DATABASE_PASSWORD
APP_DATABASE_NAME
```
配置字段约定:
- `yaml`YAML 文件中的字段名。
- `env`:环境变量名称,必须显式声明。
- `env-default`:没有文件值和环境变量时使用的默认值。
- `env-required:"true"`:必须由环境变量提供的敏感或关键配置。
- `env-prefix`:为嵌套配置统一增加环境变量前缀。
- `env-layout`:日期、时间等特殊类型的解析格式。
生产环境中数据库密码、JWT 密钥等敏感字段必须使用 `env-required:"true"`,不写入 YAML 文件。配置加载失败或必填项缺失时,服务直接退出,不使用不完整配置继续启动。
加载过程必须保持以下顺序:先加载基础配置,再加载当前环境配置,最后执行环境变量覆盖。环境变量不能被后续 YAML 文件覆盖。
## 4. 目录规划
代码统一放在 `codes/` 目录下。按照 `golang-standards/project-layout` 的组织方式,同时遵循“根目录放置 main.go”和“前端放在 web 目录”的项目约束。
```text
codes/
├── main.go # Go 服务入口,位于代码根目录
├── go.mod
├── go.sum
├── internal/
│ ├── config/ # 配置加载
│ ├── logger/ # Zap 初始化和日志字段规范
│ ├── middleware/ # 认证、租户、请求日志、异常恢复
│ ├── router/ # 路由注册
│ ├── handler/ # HTTP 接口层
│ ├── service/ # 业务服务层
│ ├── repository/ # 数据访问层
│ ├── model/ # 数据库模型和请求响应模型
│ ├── engine/ # SOP 流程执行引擎
│ └── webassets/ # 前端嵌入和静态资源处理
├── pkg/
│ └── response/ # 可被外部复用的通用响应结构
├── configs/
│ ├── config.yml
│ ├── config.test.yml
│ ├── config.prod.yml
│ └── config.example.yml
├── migrations/ # MySQL 数据库迁移文件
├── scripts/ # 构建、检查和发布脚本
└── web/
├── package.json
├── vite.config.ts
├── index.html
├── src/
│ ├── api/
│ ├── components/
│ ├── layouts/
│ ├── router/
│ ├── stores/
│ ├── types/
│ └── views/
└── dist/ # 前端构建产物,由 Go 使用 go:embed 嵌入
```
说明:`cmd/` 目录不使用Go 入口固定为 `codes/main.go``codes/web/dist/` 是构建产物目录,前端构建完成后由 Go 服务作为静态资源提供。
```text
浏览器
├── 管理端场景、字段、SOP、知识卡、审核
└── 执行端:按 SOP 执行和记录客户回答
|
v
Go HTTP 服务
├── 身份认证与租户权限
├── 场景配置服务
├── SOP 版本服务
├── 流程执行引擎
├── 知识卡服务
└── 执行记录与统计
|
v
MySQL
```
## 5. 系统架构
Go 服务同时承担 API 服务和前端静态资源服务。前端构建后通过 `go:embed` 打入 Go 二进制,生产环境只需要部署一个服务包和配置文件。
## 6. go:embed 方案
前端构建输出到 `codes/web/dist/`Go 服务通过 `go:embed` 嵌入该目录。
构建流程为:
```text
进入 codes/web -> 安装依赖 -> 执行前端构建 -> 生成 web/dist
-> 回到 codes -> 执行 Go 构建 -> 生成包含前端资源的二进制
```
服务端静态资源处理规则:
- `/api/` 路径只进入 Go API 路由。
- 静态文件路径优先读取 `web/dist` 中对应文件。
- 非 API 且找不到文件的路径回退到 `index.html`,支持 Vue Router 的 history 模式。
- 前端资源由 Nginx 或应用服务设置长期缓存,`index.html` 不使用长期缓存。
前端 `dist` 不存在时Go 项目不能完成编译,因此代码仓库需要保留一个可构建的前端产物占位文件,或者在 CI 中强制先执行前端构建。
## 7. 场景模型
场景是平台的一级可配置对象,不能在代码中写死行业字段。
一个场景至少包含:
- 场景名称
- 所属行业
- 适用角色
- 使用目标
- 触发条件
- 可见范围
- 自定义字段
- 一个或多个 SOP
- 关联知识卡
- 发布状态
例如宠物医生问诊问药场景可以配置:
```text
pet_type 宠物种类
pet_age 年龄
pet_weight 体重
symptom 症状
symptom_duration 症状持续时间
has_emergency_sign 是否存在急症
```
保险咨询场景则可以配置客户年龄、职业、预算和保险类型。新增场景只需要增加配置数据,不需要修改业务代码。
## 8. SOP 模型
SOP 由版本、节点和连线组成。
第一阶段支持以下节点类型:
- `start`:流程开始
- `message`:展示标准话术
- `question`:提问并写入一个场景字段
- `form`:一次收集多个字段
- `choice`:让执行人员选择客户回答
- `condition`:根据已收集字段进行分支
- `knowledge`:展示已审核知识卡
- `escalate`:转人工、转医生或线下处理
- `finish`:结束流程
条件必须保存为结构化规则,不能让用户输入或执行任意 JavaScript。条件规则支持等于、不等于、包含、大于、小于、全部满足、任一满足等操作。
发布前需要校验:
- 只有一个开始节点
- 所有节点都可从开始节点访问
- 所有分支都有出口
- 不存在无法结束的路径
- 必填字段存在对应采集节点
- 关联知识卡已经审核
- 急症或高风险分支有明确的转人工或转诊动作
## 9. MySQL 数据表
核心表如下:
```text
users 用户
tenants 企业或组织
tenant_members 企业成员和角色
roles 角色权限
scenarios 场景
scenario_fields 场景自定义字段
sops SOP 基础信息
sop_versions SOP 版本
sop_nodes SOP 节点
sop_edges SOP 连线和条件
knowledge_cards 话术或知识卡
knowledge_card_versions 知识卡版本
sop_runs SOP 执行实例
sop_run_events SOP 节点执行事件
sop_feedback 执行反馈
audit_logs 操作审计
```
关键设计:
- 所有租户相关数据必须能追溯到 `tenant_id`
- 场景字段使用行记录保存,避免把场景字段写死在表结构中。
- 节点配置和条件规则使用 MySQL JSON 字段保存。
- 已发布版本不可直接修改,修改时复制为新的草稿版本。
- 版本状态流转为 `draft -> reviewing -> published -> offline`;管理员也可以从草稿直接发布。
- 执行过程保存事件记录,便于复盘和统计。
- 需要参与筛选和统计的字段不能只存 JSON应在事件表或统计表中建立结构化字段。
## 10. 后端接口规划
### 场景接口
```text
POST /api/v1/scenarios
GET /api/v1/scenarios
GET /api/v1/scenarios/:id
PUT /api/v1/scenarios/:id
DELETE /api/v1/scenarios/:id
```
### 自定义字段接口
```text
POST /api/v1/scenarios/:id/fields
PUT /api/v1/scenario-fields/:id
DELETE /api/v1/scenario-fields/:id
```
### SOP 接口
```text
POST /api/v1/scenarios/:id/sops
GET /api/v1/scenarios/:id/sops
PUT /api/v1/sops/:id/draft
POST /api/v1/sops/:id/validate
POST /api/v1/sops/:id/submit-review
POST /api/v1/sops/:id/publish
POST /api/v1/sops/:id/offline
```
### SOP 执行接口
```text
POST /api/v1/runs
GET /api/v1/runs/:id
POST /api/v1/runs/:id/answer
POST /api/v1/runs/:id/finish
POST /api/v1/runs/:id/feedback
```
## 11. 流程执行引擎
执行流程如下:
1. 创建执行实例,绑定一个已发布的 SOP 版本。
2. 加载开始节点并返回给前端。
3. 前端展示话术、表单或选项。
4. 后端校验客户回答和字段类型。
5. 在事务中保存回答和节点事件。
6. 根据结构化条件计算下一节点。
7. 返回下一节点或结束动作。
流程引擎只处理通用节点和规则,不理解“宠物”“保险”等具体业务。行业知识放在场景字段、话术内容和知识卡中。
## 12. Zap 日志规范
使用 Zap 输出结构化 JSON 日志到 stdout由 Docker、Kubernetes 或云平台采集,不由应用自己维护日志文件。
日志至少包含:
- `service`
- `env`
- `request_id`
- `tenant_id`
- `user_id`
- `action`
- `resource`
- `resource_id`
- `duration_ms`
- `err`
日志等级约定:
- `DEBUG`:本地开发细节
- `INFO`:正常请求、发布、执行等业务事件
- `WARN`:参数异常、权限拒绝、客户端错误
- `ERROR`:服务异常、数据库失败、流程执行失败
禁止记录密码、Token、完整客户聊天内容和未脱敏的个人信息。
Gin 请求日志中需要记录请求方法、路径、状态码、耗时和 Request ID。业务日志使用结构化字段不使用字符串拼接。
## 13. 前端页面规划
### 管理端
- 场景列表
- 创建/编辑场景
- 自定义字段配置
- SOP 列表
- SOP 步骤编辑器
- 节点条件配置
- 知识卡管理
- 审核发布中心
- 执行数据看板
### 执行端
- 场景选择
- SOP 当前节点
- 客户信息录入
- 标准话术展示
- 分支选择
- 转人工或转诊提醒
- 执行结果和反馈
Ant Design Vue 主要使用 `Form``Table``Drawer``Modal``Steps``Tree``Tabs``Descriptions`。动态字段表单由场景字段配置生成,字段校验规则也由配置生成。
## 14. 安全与权限
- 用户登录后从 Token 中获取 `tenant_id`,不能信任前端传入的租户 ID。
- 所有查询必须自动附加租户条件。
- 场景、SOP、知识卡、执行记录都需要做资源级权限判断。
- 发布、下线、审核和修改权限分离。
- 已发布 SOP 版本只读,修改时复制为新的草稿版本。
- 记录关键操作到 `audit_logs`
- 条件规则禁止执行任意代码。
## 15. 第一阶段开发顺序
### 第 1 阶段:基础服务
- Go 服务初始化
- Zap 日志
- 配置加载
- MySQL 连接
- 登录和租户权限
- 前端构建和 go:embed
### 第 2 阶段:场景配置
- 场景 CRUD
- 自定义字段 CRUD
- 场景权限
- 场景状态管理
### 第 3 阶段SOP 编排
- 节点管理
- 连线和条件配置
- 草稿保存
- 流程校验
- 版本复制
### 第 4 阶段SOP 执行
- 创建执行实例
- 节点展示
- 回答提交
- 条件跳转
- 执行事件记录
### 第 5 阶段:审核和反馈
- 提交审核
- 审核发布
- 下线和回滚
- 执行反馈
- 基础数据统计
## 16. 构建和部署约定
首次初始化本地 MySQL 数据库时执行:
```bash
cd codes
mysql -uroot -p < scripts/create-databases.sql
```
服务启动时会自动执行 `migrations/` 中的迁移,创建业务表并记录 `schema_migrations` 版本。
本地开发时前后端可以分别启动:
```text
前端开发服务codes/web
Go API 服务codes
```
生产构建时先构建前端,再构建 Go 服务。最终产物只需要:
- Go 二进制
- 配置文件
- MySQL 数据库
前端静态资源已经通过 `go:embed` 编译进入 Go 二进制,不需要单独部署前端静态目录。
## 17. 暂不实现的内容
- 文件上传和对象存储
- AI 自动提炼话术
- AI 客户模拟陪练
- CRM、企业微信和客服系统集成
- 多服务拆分
- Redis 缓存
- 消息队列
- 复杂 BI 分析
这些内容等核心闭环验证成功后再加入,避免第一版架构过度复杂。