Files
crm_project/SHBL-ERP_CRM_架构白皮书_v0.2.0.md
T
hankin 815cbf9d8c v0.2.0: CRM/ERP 系统升级 - 清理 .gitignore 并移除误提交的 venv/env/db 文件
- 更新 .gitignore:全面覆盖环境变量、数据库、日志、缓存、上传文件
- 移除误跟踪的 server/venv/、crm_data.db、.env 文件
- 新增 server/.env.example 模板
- 新增合同管理、利润核算、AI教练等功能模块
- 新增 Playwright e2e 测试套件
- 前后端多项功能升级和 bug 修复
2026-05-11 07:24:19 +00:00

374 lines
20 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.
# 润滑油 CRM/ERP 系统 — 阶段性全栈复盘与架构白皮书
**项目代号**:天津硕博霖业财一体化 ERP(SHBL-ERP
**当前阶段**Phase 2.0(多租户隔离 + AI 交互优化 / 内测上线态)
**报告日期**2026 年 3 月 19 日
**系统版本**v0.2.0
**部署地址**`http://192.168.1.100`(内网单节点)
**变更基线**:基于 v0.1.0 (Phase 1.5) 增量更新
---
## 一、系统技术基座 (Tech Stack & Dependencies)
本系统采用现代化前后端分离架构,AI 能力通过 Dify + Ollama 私有化部署实现,全栈可控。
### 1. 前端架构 (Frontend)
| 技术 | 说明 |
|------|------|
| **Vue 3** (Composition API) + TypeScript | 核心框架,14 个业务页面组件 |
| **Vite** | 构建工具,极速冷启动与 HMR |
| **Element Plus** | 企业级 UI,深度定制了报销单 `@media print` A4 打印样式 |
| **Pinia** | 全局状态管理(UserInfo、Token、RBAC 权限、**当前公司 ID** |
| **Axios** | 封装全局 Request/Response 拦截器,自动处理 401 登出、业务异常及 **`X-Company-Id` 头部注入** |
| **SSE (EventSource)** | AI 复盘报告实时流式输出 |
### 2. 后端架构 (Backend)
| 技术 | 说明 |
|------|------|
| **Python 3.10+ & FastAPI** | 核心框架,**17 个 API 路由模块**,自带 OpenAPI/Swagger 文档 |
| **Uvicorn** | ASGI 异步服务器 |
| **SQLAlchemy 2.0 + Asyncpg** | 全异步 ORM**24 张业务表** |
| **Alembic** | **数据库 Schema 版本管理**(4 个迁移脚本),async 模式 |
| **JWT + Passlib (Bcrypt)** | 认证体系(Token + 密码哈希) |
| **httpx** | 异步 HTTP 客户端,用于调用 Dify API 和 Ollama |
### 3. AI 能力层
| 组件 | 说明 |
|------|------|
| **Dify** (192.168.1.88) | AI 编排平台,承载 Workflow(复盘报告生成)和 Chat(AI 悬浮球对话) |
| **Ollama — RTX 3090** (192.168.1.88:11434) | 运行 `qwen3.5:27b`,用于复盘报告、企业画像等重任务 |
| **Ollama — RTX 4060** (192.168.1.88:11435) | 运行 `qwen3.5:4b`,用于发票 OCR 解析等轻任务 |
### 4. 数据持久层 (Database)
- **核心数据库**PostgreSQL(宿主机直连,asyncpg 驱动)
- **24 张业务表**,覆盖 CRM、ERP、财务、AI、系统五大域 + **多租户域**
- **Alembic 迁移管理**4 个版本化迁移脚本(baseline → 多租户 → 新宇公司 → sales_logs 业务标签)
- **亮点特性**
- `JSONB` 承载 AI OCR 提取数据 (`ai_extracted_data`)
- `ARRAY(UUID)` 承载 `sales_logs.involved_company_ids`(多公司业务标签)
- 强事务并发控制(库存/发货防重)
- **行级多租户隔离**`company_id` 外键 + GIN 索引)
### 5. 部署架构
```
┌────────────────────────────────────────────────┐
│ 服务器 192.168.1.100 (Ubuntu, 61GB 磁盘) │
│ │
│ Docker Compose │
│ ┌──────────────────┐ ┌──────────────────────┐ │
│ │ frontend │ │ backend │ │
│ │ Nginx:80 (95MB) │ │ Uvicorn:8000 (889MB) │ │
│ │ bridge 网络 │ │ host 网络 │ │
│ └──────┬───────────┘ └──────────────────────┘ │
│ │ proxy_pass ↓ │
│ └── host.docker.internal:8000 │
│ │
│ PostgreSQL (宿主机 :5432) ← 直连 │
│ │
│ Alembic (宿主机 venv) ← 迁移管理 │
│ ↕ 127.0.0.1:5432 (env.py 自动替换) │
└────────────────────────────────────────────────┘
┌─────┴─────┐
│ Dify + Ollama │ ← 192.168.1.88
│ (RTX 3090/4060)│
└───────────────┘
```
---
## 二、多租户架构(v0.2.0 新增)
### 设计原则
采用**行级数据隔离**Row-Level Isolation),同一数据库、同一张表,通过 `company_id` 列区分不同公司数据。
### 已注册公司
| 公司 | UUID | 说明 |
|------|------|------|
| 天津硕博霖 | `aaaaaaaa-bbbb-cccc-dddd-eeeeeeee0001` | 默认公司 |
| 新宇润滑油 | `aaaaaaaa-bbbb-cccc-dddd-eeeeeeee0002` | 第二家公司 |
### 隔离矩阵
| 表 | 隔离方式 | 说明 |
|----|----------|------|
| `erp_orders` | `company_id` FK | 订单按公司隔离 |
| `erp_shipping_records` | `company_id` FK | 发货单按公司隔离 |
| `erp_inventory_flows` | `company_id` FK | 库存流水按公司隔离 |
| `erp_sku_inventory` | `company_id` FK | 库存快照按公司隔离 |
| `fin_invoice_pool` | `company_id` FK | 报销发票池按公司隔离 |
| `fin_expense_records` | `company_id` FK | 报销单按公司隔离 |
| `finance_sales_invoices` | `company_id` FK | 销项发票按公司隔离 |
| `sales_logs` | `involved_company_ids` ARRAY(UUID) | **业务标签**:一条日志可关联多个公司 |
| `crm_customers` | **全局共享** | 客户资源跨公司共享 |
| `erp_product_categories` | **全局共享** | 产品分类跨公司共享 |
| `erp_product_skus` | **全局共享** | 产品 SKU 跨公司共享 |
### 技术实现链路
```
前端 Pinia(currentCompanyId)
↓ Axios 拦截器自动注入
请求 Header: X-Company-Id: <uuid>
后端 Depends(get_current_company_id)
↓ IDOR 校验(sys_user_companies 验证归属)
service 层 .where(Model.company_id == company_id)
```
- **IDOR 防护**`get_current_company_id` 依赖会查 `sys_user_companies` 表验证当前用户确实关联了该公司,防止伪造 Header 越权
- **前端公司切换**:登录页公司选择 + 首页顶部 Header 下拉切换
- **sales_logs 特殊处理**:使用 `ARRAY.any(company_id)` 包含查询 + GIN 索引加速
---
## 三、九大核心业务模块全景剖析
系统已点亮 **9 大核心模块 + 2 个 AI 模块**,实现从"线索"到"现金"的业财链路闭环,并接入大模型智能辅助。
### 模块 1:RBAC 权限与系统基座
- **功能**:用户登录、改密、角色管理、员工管理、**公司管理与切换**
- **核心亮点**
1. **`data_scope` 数据横向隔离** — `self` / `dept_and_sub` / `all` 三维度
2. **多租户公司隔离**`sys_companies` + `sys_user_companies` 多对多关联,支持用户归属多个公司
3. **`X-Company-Id` 依赖注入** — 所有私有数据查询均受公司视角拦截,双层校验(JWT + 公司归属)
- **关键表**`sys_users``sys_roles``sys_departments``sys_companies``sys_user_companies`
### 模块 2CRM 客户管理
- **功能**:客户全生命周期管理(新增、编辑、归档、搜索筛选、导入/导出、**转移负责人**)
- **核心亮点**
1. 创建时自动绑定 `owner_id`,融入 `data_scope` 防线
2. **联系人管理** — 独立 `crm_contacts` 表,支持多联系人关联
3. **AI 企业画像** — 调用 Ollama 大模型生成企业分析卡片(Dify Workflow 回调双轨写入)
4. **AI 客情健康度评分** — 基于交互频率自动打分
5. **批量 Excel 导入/导出** — 支持模板下载 + openpyxl 解析
6. **客户转移** — 管理员可跨人员重新分配客户负责人
7. **全局共享** — 客户数据跨公司共享,不受 `company_id` 隔离
- **关键表**`crm_customers``crm_contacts``crm_follow_up_logs`
### 模块 3:供应链与货品库存
- **功能**:左树(无限级分类)右表(SKU)经典布局
- **核心亮点**
1. **无限级分类树**`erp_product_categories` 自关联支持 N 级嵌套
2. **安全库存变更**:一切库存变动必须通过 `POST /api/products/inventory/flow` 提交出入库流水,后端强事务加减库存,留存 `erp_inventory_flows` 审计轨迹
3. **库存预警**`stock_qty <= warning_threshold` 自动标红提示(`erp_sku_inventory` 表)
4. **批量 SKU 导入**Excel 模板 + SKU 编码防重
5. **产品/分类全局共享** — SKU 和分类跨公司共享,库存快照 (`erp_sku_inventory`) 按公司隔离
### 模块 4:订单交易枢纽
- **功能**:主子表嵌套的沉浸式开单体验,支持多 SKU 明细行
- **核心亮点**
1. **B2B 智能动态定价(一客一价)** — 选中 SKU 时静默调用 `/api/orders/price/calculate`,自动调取该客户历史专属拿货价
2. **按公司隔离** — 订单数据通过 `company_id` 实现多租户隔离
### 模块 5:物流与发货执行
- **功能**:发货单管理,指导仓库拣货出库
- **核心亮点**
1. **五步原子事务防超发**:校验 `发货量 ≤ 订单总量 - 已发量`,订单状态更新 + 库存扣减 + 发货单生成毫秒级级联
2. **数据级联预加载**:发货单透出客户名称、包装规格等
### 模块 6:财务报销与票据中心
- **功能**:三 Tab 一站式财务管理 — 统一票据池 / 新建报销 / 报销大盘
- **核心亮点**
1. **发票 AI 智能解析**:拖拽上传 PDF/JPG/PNG/MD → 调用 Ollama 4060 (qwen3.5:4b) 自动提取发票号、金额、日期等字段 → 存入 `fin_invoice_pool.ai_extracted_data` (JSONB)
2. **批量上传队列**:支持多文件并行处理,逐一显示解析进度(**不再自动清空**,手动关闭)
3. **发票状态机**:发票被报销单选中瞬间 `is_used` 锁定,驳回自动释放
4. **高级科目定制**:硬编码专属原始种类(招待费、交通费)和冲顶类型(税务费用)
5. **A4 打印**CSS `@media print` 标准黑白审批单,带四大签字占位符
### 模块 7:销项发票管理
- **功能**:独立的销项发票(开票)管理模块
- **核心亮点**
1. 客户名称/发票号搜索 + 回款状态筛选 + 开票日期范围
2. **AI 发票 OCR 识别** — 调用 Ollama 视觉模型提取 `merchant`/`buyer`/`amount`/`date` 等字段
3. **批量 OCR → 待确认列表**(v0.2.0 新增):批量上传后不直接创建,OCR 结果暂存可编辑表格,不完整字段标黄高亮,用户审核修正后"一键全部创建"批量入库
4. **单文件模式**OCR 结果预填到新增表单,`buyer` 字段自动匹配 CRM 客户
5. 独立 `finance_sales_invoices`
### 模块 8:销售日志
- **功能**:销售人员每日拜访/沟通记录管理
- **核心亮点**
1. 独立 `sales_logs` 表,支持关键字搜索 + 日期筛选
2. **AI 智能审阅** — 调用大模型对日志内容进行质量评分和建议
3. 数据同时作为 AI 复盘报告的原始素材
4. **多公司业务标签**v0.2.0 新增):`involved_company_ids` (ARRAY UUID) — 一篇日志可关联1~N个公司,切换公司视角时通过 `ARRAY.any()` 包含过滤,GIN 索引加速
### 模块 9Dashboard 工作台
- **功能**:首页 KPI 总览 + 快捷操作入口 + **公司切换**
- **核心亮点**
1. **四大 KPI 卡片**:本月订单数 / 待出库发货 / 库存预警 SKU / 本月营收 — 后端 `GET /api/dashboard/stats` 聚合查询实时计算
2. 快捷按钮一键跳转至订单、发货、库存、日志页面
3. **顶部公司切换下拉** — 支持在不同公司视角间即时切换
---
## 四、AI 中枢模块
### AI 模块 A:智能复盘报告(Dify Workflow + SSE
- **架构**`前端 → 后端 SSE → Dify Workflow → Ollama 3090 (27B)`
- **流程**
1. 前端选择时间范围,后端查询 `sales_logs` 并序列化(**按 `involved_company_ids` 过滤当前公司**
2. 通过 httpx 异步调用 Dify Workflow APIstreaming 模式)
3. Workflow 内部:HTTP 获取日志 → LLM 分析生成 → HTTP 回调存档
4. 后端 SSE 实时推送 `text_chunk` 到前端逐字渲染
5. 生成完毕自动保存至 `ai_report_drafts`,支持历史加载和再编辑
- **关键配置**httpx 600s 超时 + nginx `proxy_buffering off` + 600s 读超时
- **多租户感知**v0.2.0):`generate_report` 端点注入 `company_id` 依赖,仅提取涉及当前公司的日志,防止跨公司分析幻觉
### AI 模块 B:全局 AI 助手(浮球对话)
- **架构**`前端 FloatingChat → 后端 /api/chat → Dify Chat API`
- **功能**:全页面右下角悬浮球,随时与 AI 对话,支持上下文记忆
- **底层引擎**Dify Chat 应用,对接 Ollama
---
## 五、核心数据模型约束
所有核心表均遵守以下企业级约束:
| 约束 | 说明 |
|------|------|
| **主键策略** | 全局 UUID (`uuid4`),防爬虫遍历 |
| **软删除** | 所有表包含 `is_deleted` (Boolean),严禁物理 DELETE |
| **时间戳** | `created_at` / `updated_at`SQLAlchemy 监听器自动维护 |
| **数据隔离** | 业务查询均受 `data_scope` + `owner_id` + **`company_id`** 三重拦截 |
| **迁移管理** | Alembic async 模式,所有 DDL 变更必须通过版本化迁移脚本 |
### 数据库全表清单(24 张)
| 域 | 表名 | 用途 | 隔离方式 |
|----|------|------|----------|
| CRM | `crm_customers` | 客户主表 | 全局共享 |
| CRM | `crm_contacts` | 客户联系人 | 全局共享 |
| CRM | `crm_follow_up_logs` | 跟进记录 | 全局共享 |
| ERP | `erp_product_categories` | 产品分类树 | 全局共享 |
| ERP | `erp_product_skus` | 产品 SKU | 全局共享 |
| ERP | `erp_sku_inventory` | 库存快照(stock_qty + warning_threshold | `company_id` |
| ERP | `erp_inventory_flows` | 库存变动流水 | `company_id` |
| ERP | `erp_orders` | 订单主表 | `company_id` |
| ERP | `erp_order_items` | 订单明细行 | — (随主表) |
| ERP | `erp_shipping_records` | 发货单主表 | `company_id` |
| ERP | `erp_shipping_items` | 发货明细行 | — (随主表) |
| 财务 | `fin_invoice_pool` | 报销发票池 | `company_id` |
| 财务 | `fin_expense_records` | 报销单主表 | `company_id` |
| 财务 | `fin_expense_details` | 报销明细行 | — (随主表) |
| 财务 | `finance_sales_invoices` | 销项发票 | `company_id` |
| 协同 | `sales_logs` | 销售日志 | `involved_company_ids` (ARRAY) |
| AI | `ai_chat_sessions` | AI 对话记录 | — |
| AI | `ai_report_drafts` | AI 复盘报告草稿 | — |
| 系统 | `sys_users` | 系统用户 | — |
| 系统 | `sys_roles` | 角色权限 | — |
| 系统 | `sys_departments` | 部门组织 | — |
| 系统 | `sys_companies` | **公司/租户主表** | — |
| 系统 | `sys_user_companies` | **用户-公司多对多** (含 `is_default`) | — |
| 系统 | `alembic_version` | Alembic 迁移版本号 | — |
### Alembic 迁移清单
| 序号 | Revision | 说明 |
|------|----------|------|
| 1 | `03d8dcc2d72a` | v0.1.0 baseline19 张表) |
| 2 | `a1b2c3d4e5f6` | 多租户隔离:新增 `sys_companies` / `sys_user_companies`8 张表添加 `company_id` |
| 3 | `b2c3d4e5f6a7` | 新增新宇润滑油公司 + 全员关联 |
| 4 | `c3d4e5f6a7b8` | `sales_logs.company_id``involved_company_ids` (ARRAY UUID) + GIN 索引 |
---
## 六、API 接口全景(17 个路由模块)
| 模块 | 前缀 | 核心端点示例 | 多租户 |
|------|------|-------------|--------|
| auth | `/api/auth` | login, me, password, **user-info (含公司列表)** | — |
| customers | `/api/customers` | CRUD + 归档 + AI 画像 + **转移** | 共享 |
| contacts | `/api/contacts` | 客户联系人 CRUD | 共享 |
| **companies** | `/api/companies` | **公司列表、用户关联公司查询** | — |
| orders | `/api/orders` | CRUD + 动态定价 | ✅ |
| shipping | `/api/shipping` | 发货 + 原子事务 | ✅ |
| products | `/api/products` | SKU + 分类树 + 库存变更 | 部分 |
| finance | `/api/finance` | 票据池 + 报销 + 审批 | ✅ |
| sales_invoice | `/api/sales-invoices` | 销项发票 CRUD | ✅ |
| sales_logs | `/api/sales-logs` | 销售日志 CRUD + AI 审阅 (**company_ids** 数组) | ✅ ARRAY |
| reports | `/api/reports` | AI 复盘 SSE 生成 + 历史 (**公司过滤**) | ✅ |
| dashboard | `/api/dashboard` | KPI 聚合统计 | ✅ |
| chat | `/api/chat` | AI 浮球对话 | — |
| dify_tools | `/api/dify-tools` | Dify 回调工具端点 | — |
| import_export | `/api` | 模板下载 + Excel 导入导出 | — |
| sys_settings | `/api/settings` | 用户/角色/部门管理 | — |
| deps | — | 鉴权依赖注入 + **`get_current_company_id`** | — |
---
## 七、v0.1.0 → v0.2.0 变更日志
### 已完成增量(Phase 1.5 → 2.0
| 项目 | v0.1.0 状态 | v0.2.0 状态 |
|------|-------------|-------------|
| Alembic 迁移 | ❌ 手工 DDL | ✅ 4 个版本化迁移脚本,async 模式 |
| 多租户 | ❌ 仅天津硕博霖 | ✅ 行级 `company_id` 隔离,IDOR 防护 |
| 公司切换 | ❌ | ✅ 登录页选择 + 首页下拉切换 |
| 新宇润滑油 | ❌ | ✅ 第二家公司,全员关联 |
| 客户转移 | ❌ | ✅ 管理员可跨人员重新分配 |
| sales_logs 多公司 | 无 company 概念 | ✅ `involved_company_ids` ARRAY + GIN 索引 |
| AI 复盘公司过滤 | 提取所有日志 | ✅ 仅提取涉及当前公司的日志 |
| 发票 OCR buyer 字段 | ❌ 映射缺失 | ✅ 修复 `buyer`/`buyer_name`/`customer_name` 链 |
| 发票批量交互 | 自动创建 + 5s 消失 | ✅ 待确认列表 + 字段标黄 + 一键创建 |
| 报销上传队列 | 3s 自动清空 | ✅ 手动关闭 |
| Dashboard 库存预警 | `erp_product_skus.warning_threshold` | ✅ 修正为 `erp_sku_inventory` 表 |
| 数据库表数量 | 19 张 | 24 张(+5 |
| API 路由模块 | 16 个 | 17 个(+companies |
### Phase 3 规划
| 优先级 | 方向 | 说明 |
|--------|------|------|
| **P0** | HTTPS | 内网 CA 自签证书,保护 JWT Token 传输安全 |
| **P1** | 数据可视化 | ECharts 接入 Dashboard — 月度趋势折线图、报销占比饼图 |
| **P1** | 操作审计日志 | 记录关键操作(删除、审批、密码重置)的完整审计轨迹 |
| **P1** | 文件存储优化 | 对接 MinIO / 阿里云 OSS,替代本地 uploads 目录 |
| **P1** | 通知公告模块 | 实现系统内通知与公告发布功能 |
| **P2** | 移动端适配 | 响应式布局或微信小程序,支持外出销售人员使用 |
| **P2** | 外网访问 | VPN / Cloudflare Tunnel 实现远程办公访问 |
| **P2** | 监控告警 | Prometheus + Grafana 仪表板 + 钉钉/微信告警推送 |
| **P2** | AI 复盘页修复 | 排查 SSE 流 / 前端组件加载问题 |
---
## 八、关键架构决策记录 (ADR)
| # | 决策 | 理由 |
|---|------|------|
| ADR-001 | 后端 Docker 使用 `network_mode: host` | 直连宿主机 PostgreSQL,免改 `pg_hba.conf`,简化内网部署 |
| ADR-002 | 前端 Docker 使用 `bridge + extra_hosts` | 通过 `host.docker.internal` 解析宿主机,隔离前端网络 |
| ADR-003 | AI 引擎选择 Dify + Ollama 私有化 | 数据不出内网,满足工业企业信息安全合规要求 |
| ADR-004 | 复盘报告采用 SSE 而非 WebSocket | 单向推送场景 SSE 更轻量,nginx 配置更简单 |
| ADR-005 | 全表 UUID 主键 | 防止 ID 遍历攻击,适配分布式未来扩展 |
| ADR-006 | 库存变更仅允许流水模式 | 杜绝直接篡改库存,确保账实一致 + 审计可追溯 |
| **ADR-007** | **行级多租户而非 Schema 隔离** | 公司数量有限(2~5 家),行级隔离实现简单,共享表无需冗余 |
| **ADR-008** | **`sales_logs` 使用 ARRAY UUID 而非单 `company_id`** | CRM 日志需保持"以客户为中心"完整性,一篇日志可能涉及多个公司业务 |
| **ADR-009** | **Alembic async 模式 + env.py 地址替换** | Docker 内 `.env``host.docker.internal`,Alembic 在宿主机执行时自动替换为 `127.0.0.1` |
| **ADR-010** | **`X-Company-Id` Header + IDOR 校验** | 比 Cookie/Session 更适配 SPA 前后端分离,校验 `sys_user_companies` 防止伪造 |
---
*本文档为 SHBL-ERP CRM 系统 Phase 2.0 阶段性存档,随系统迭代持续更新。*