Files
yml2213 7bacb7d5eb docs: 重构项目文档并补充三块载体、成本中心与人工支出方案
合并本轮全部文档改动(原 5 个 docs 提交压成 1 个)。

口径与索引:
- 新增 docs/README.md 文档总索引:三来源口径(91 + 快手 send-code = kuaishou-91)、
  「一个订单 = 骨架 + 买家凭证 + 一个执行载体」、成本只改一处、文档状态机
- 过期阶段文档移入 docs/archive/2026-10-08/(保留内容,不删除)

分析报告:
- docs/项目全量分析报告-2026-10-17.md:架构、五条业务链路、P0/P1/P2 风险与改进路线
- docs/载体判定与成本中心落地-复核与草案-2026-10-17.md:生产只读克隆量化、上一轮结论复核与 4 处修正

方案文档:
- docs/成本中心与三块履约模型.md:三块执行载体、标准商品、成本版本与快照、人工支出登记
- docs/财务系统集成方案.md:复式记账模型、分录表、分期落地
- docs/影子总账说明.md:影子期与转正期的边界、对账与转正标准

SQL 草案(docs/drafts/,未接入迁移):
- 载体判定与人工块量化.sql:量化查询、carrier_type 判定与回填、守恒校验
- 成本中心与销售事实-DDL草案.sql:product_catalog / channel_product_mappings /
  cost_items / cost_versions / cost_snapshots / cost_exceptions / sales_daily_facts
- 人工支出登记-DDL草案.sql:manual_fulfillment_cases / manual_expense_records / manual_expense_events

业务口径确认(2026-10-17):
- 收入 = 用户实付(orders.total_amount),与快手 paymentFen 数据核验 100% 一致
- 打手报酬 = 支出/成本(最终提现打款),按总额法
- 人工块以「支出可追溯」为先,SLA 监管延后
- 退款/仲裁暂未接入,先预留字段与反向冲正关系

三份草案已在一次性沙箱库按依赖顺序零错误应用(10 张新表、27 个 CHECK 约束)。
2026-10-17 22:03:03 +08:00

315 lines
13 KiB
Markdown
Raw Permalink 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.
# order_site
多渠道订单履约、打手众包接单与寄卖仓库系统。
前后端分离架构:接收 `kuaishou-91`、`pdd`、`douyin-mashida` 三个来源的订单,按规则匹配自动发货或生成打手工单,提供统一领取/兑换链接,并支持平台配置、财务结算、审计与人工复核。
## 技术栈
| 层 | 技术 |
| --- | --- |
| 前端 | React 19、Vite 8、Ant Design 6、React Router 7、TanStack Query、TypeScript |
| 后端 | Node.js、Express 5、TypeScript、PostgreSQL、node-pg-migrate |
| 网关 | Caddy |
| 运行 | Docker Compose(开发 / 生产分离) |
## 目录结构
```text
apps/
backend/ 后端 API、任务编排、平台对接、数据库迁移
src/
db/migrations/ SQL 迁移(001 基线 + 后续渐进增量)
data/ 本地运行数据与平台配置(敏感,默认不提交)
frontend/ React 管理后台 + 用户领取页
deploy/
caddy/ Caddy 反向代理
docker/ 前后端 Dockerfile
mac-dev.sh / ubuntu-deploy.sh
docs/ 文档索引、架构分析、财务方案、平台接口、部署说明
docker-compose.dev.yml 本地 Docker 开发环境
docker-compose.yml 生产 / 服务器 Compose
```
## 来源模型(现行)
系统只有三个订单来源:
| sourceKey | 数据通道 | 说明 |
| --- | --- | --- |
| `kuaishou-91` | 91 回调 + 快手 send-code | **同一个来源的两条通道**:91 是订单骨架权威源,快手 send-code 是数据补全源与兜底源;共享店铺键空间与同一套匹配上下文 |
| `pdd` | 拼多多订单同步 | 两段式 `pdd_raw_orders → orders` |
| `douyin-mashida` | 抖音码速达推送 | 抖店订单、DYC 券、H5/兑换页 |
不要把 91 与快手 send-code 拆成两个来源或两个适配器。代码中的唯一来源键是 `kuaishou-91`(`apps/backend/src/services/product-match/types.ts`)。
## 业务边界(简图)
```text
kuaishou-91(91 回调 + 快手 send-code)
→ 本系统建单 / 匹配商品 / 创建履约任务
→ 返回统一 claimUrl
用户打开领取页
→ 可选:快手行业电子凭证自动核销
→ affiliate-dash / 人工 完成发货
后台
→ 订单任务、平台配置、审计、人工复核
接单平台(打手众包)
→ 工单发布到抢单大厅,打手抢单代练
→ 提交验收 / 问题单处置 / 任务时限超时自动判定
抖音码速达
→ 抖店订单推送 → 建单 → DYC 券 → 领取/兑换页 → 核销
```
更完整的链路说明见:`docs/履约配置/多发货平台与电子凭证整体链路.md`。
文档总索引见:`docs/README.md`。
## 本地开发
推荐直接用 Docker,本机不必单独装 Node / PostgreSQL。
```bash
cp .env.mac-docker.example .env
docker compose -f docker-compose.dev.yml up -d --build
```
常用入口:
| 入口 | 地址 |
| --- | --- |
| 前端 / 后台 / 领取页 | `http://localhost` |
| 后端健康检查 | `http://localhost/health` |
| 后端就绪检查 | `http://localhost/health/ready` |
| 后端 API 前缀 | `http://localhost/api/v1/...` |
常用命令:
```bash
docker compose -f docker-compose.dev.yml ps
docker compose -f docker-compose.dev.yml logs -f backend
docker compose -f docker-compose.dev.yml logs -f frontend
docker compose -f docker-compose.dev.yml down
```
Mac 也可使用:
```bash
bash deploy/mac-dev.sh
```
## 开发验证
以容器内执行为准:
```bash
docker compose -f docker-compose.dev.yml exec -T backend npm test
docker compose -f docker-compose.dev.yml exec -T backend npm run format:check
docker compose -f docker-compose.dev.yml exec -T backend npm run typecheck
docker compose -f docker-compose.dev.yml exec -T backend npm run build
docker compose -f docker-compose.dev.yml exec -T frontend npm test
docker compose -f docker-compose.dev.yml exec -T frontend npm run format:check
docker compose -f docker-compose.dev.yml exec -T frontend npm run typecheck
docker compose -f docker-compose.dev.yml exec -T frontend npm run build
```
### 本地推送检查
本项目不使用 Gitea Actions。需要在本机推送前自动执行前后端检查时,启用本地 Git hook:
```bash
bash scripts/install-git-hooks.sh
```
启用后,每次 `git push` 会依次执行 `apps/backend` 和 `apps/frontend` 的 `npm run check`。
仅临时跳过检查时使用:
```bash
SKIP_CHECKS=1 git push
```
## 数据库迁移
采用 **基线 + 渐进增量** 策略,由 `node-pg-migrate` 管理,记录表为 `schema_migrations`。
| 文件 | 含义 |
| --- | --- |
| `001_init.sql` | 空库基线,建立完整业务结构 |
| `002_xxx.sql` ~ `080_xxx.sql` | 后续结构变更的增量 SQL(接单平台 004~009,寄卖相关 077~080) |
约定:
1. **不要修改**已经应用到共享/生产环境的历史迁移文件。
2. 结构变更一律新增迁移文件,不要回写 `001_init.sql`。
3. 空库按序号依次执行 `001 → 002 → …`;已有库只执行尚未记录的增量。
4. 后端启动时自动 `up`;也可手动执行。
常用命令(容器内):
```bash
# 查看本地文件与数据库对齐状态
docker compose -f docker-compose.dev.yml exec -T backend npm run db:migrate:status
# 手动执行未应用迁移
docker compose -f docker-compose.dev.yml exec -T backend npm run db:migrate
# 创建下一份增量迁移(示例)
docker compose -f docker-compose.dev.yml exec -T backend npm run db:migrate:create -- add_task_priority
```
宿主机直连开发库(注意主机名):
```bash
cd apps/backend
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/order_site npm run db:migrate:status
DATABASE_URL=postgres://postgres:postgres@127.0.0.1:5432/order_site npm run db:migrate
```
本地需要清空数据重建库时(会丢开发数据):
```bash
docker compose -f docker-compose.dev.yml down
docker volume rm order_site_postgres_dev_data
docker compose -f docker-compose.dev.yml up -d --build
```
更多说明见 `docs/部署启动与数据库重建.md`。
## 环境变量
本地:
```bash
cp .env.mac-docker.example .env
```
生产:
```bash
cp .env.server.example .env
```
常用变量:
| 变量 | 说明 |
| --- | --- |
| `APP_DOMAIN` / `CADDY_SITE_ADDR` | 后台与领取页域名、Caddy 主站监听地址 |
| `WORKER_SITE_ADDR` | 打手端独立域名,如 `https://worker.khhao.com` |
| `CLAIM_BASE_URL` | 领取页完整前缀,如 `https://域名/#/claim` |
| `CLAIM_TOKEN_TTL_HOURS` | 领取链接有效期(小时);`0` 表示长期有效,正整数表示限时 |
| `DATABASE_URL` | PostgreSQL 连接串(容器内主机名用 `postgres`) |
| `ADMIN_SESSION_SECRET` | 后台登录态签名密钥 |
| `ADMIN_DEFAULT_USERS_JSON` | 默认后台用户 |
| `KAQUAN91_USER_ID` / `KAQUAN91_SECRET` | 91 卡券开放接口凭据 |
## 运行数据
| 环境 | 路径 / Volume |
| --- | --- |
| 本地后端配置与日志 | `apps/backend/data` |
| 本地 PostgreSQL | Docker volume `postgres_dev_data` |
| 生产后端数据 | `apps/backend/data`(与本地路径一致) |
| 生产 PostgreSQL | Docker volume `postgres_data` |
| 生产 Caddy | Docker volume `caddy_data` |
注意:`apps/backend/data/*.json` 可能含账号、Cookie、token 等敏感信息,默认不进仓库;只保留 `*.example.json` 模板。
## 生产部署
```bash
cp .env.server.example .env
# 编辑真实配置后:
mkdir -p apps/backend/data
sudo chown -R 1001:1001 apps/backend/data
docker compose up -d --build
```
Ubuntu 一键脚本:
```bash
bash deploy/ubuntu-deploy.sh
```
首次若无 `.env`,脚本会从示例生成模板并退出;填好后再执行一次即可。脚本会检查 Docker、占位配置、数据目录权限,构建启动并等待健康检查。
**按需重建**:脚本用 `.deploy-state` 记录上次成功部署的 commit 与 `.env` 指纹,再按 `git diff --name-only 上次..HEAD` 判断这次要重建哪一端 —— 只改 `apps/backend/**` 就只重建后端镜像,只改 `apps/frontend/**`(或 `deploy/caddy/**`)就只重建 Web 镜像,只改文档/部署脚本时两端镜像都不重建、容器保持运行;`.env` 变化、compose 文件变化或路径拿不准时两端都重建兜底。不需要重建的一侧既不跑 Docker 构建、也不重建容器。部署结束会打印各阶段耗时。
常用参数:
| 参数 | 说明 |
| --- | --- |
| (无) | 按 git 改动自动判断重建哪一端;本次改动不含 `apps/backend/src/db/migrations/**` 时自动跳过迁移前备份 |
| `--dry-run` | 只打印计划(改了什么、重建哪一端、要不要备份、基础镜像 digest)后退出,不做任何改动 |
| `--all` | 强制两端镜像都重建(忽略改动检测) |
| `--backend-only` / `--web-only` | 只重建指定的一端 |
| `--backup` | 强制迁移前备份(即使本次无 schema 变更) |
| `--skip-backup` | 无条件跳过部署前的数据库备份 |
| `--reset-db` | 删除 PostgreSQL 数据卷后按 migrations 全量重建(执行前会自动抓取最后一份备份) |
| `--yes` | 跳过 `--reset-db` 的交互确认(等价于 `RESET_DB_CONFIRM=1`) |
`--skip-backup` 也可用环境变量 `SKIP_BACKUP=true bash deploy/ubuntu-deploy.sh` 代替。备份与恢复的完整说明见 `docs/数据库备份与恢复.md`。
生产入口示例:
- 前端与领取页:`https://你的域名/`
- 健康检查:`https://你的域名/health`
- API:`https://你的域名/api/v1/...`
生产 Compose 会:启动 PostgreSQL → 后端自动迁移 → 初始化默认管理员 → 同步履约目录 → Caddy 暴露前端与 API。
## 主要模块
| 路径 | 职责 |
| --- | --- |
| `apps/backend/src/routes` | 开放接口 / 领取 / 后台 API |
| `apps/backend/src/routes/worker` | 打手端 API(抢单大厅、我的订单、验收) |
| `apps/backend/src/services/order` | 订单入库、商品匹配、任务同步 |
| `apps/backend/src/services/fulfillment` | 履约路由与执行器(cloud / feifei / 人工) |
| `apps/backend/src/services/worker-platform` | 接单平台业务(工单、等级、押金结算、超时扫描) |
| `apps/backend/src/services/platforms` | 外部平台对接 |
| `apps/backend/src/services/admin` | 后台鉴权、读写、配置 |
| `apps/backend/src/services/scheduler` | 定时任务(云账号健康检查、工单超时扫描) |
| `apps/backend/src/repositories` | 数据访问 |
| `apps/backend/src/db/migrations` | 数据库迁移 |
| `apps/frontend/src/pages/admin` | 管理后台页面 |
| `apps/frontend/src/pages/worker` | 打手端页面(大厅、我的订单、个人中心) |
| `apps/frontend/src/pages/claim` | 用户领取页 |
## 接单平台(打手众包)
用于将部分订单转为打手代练任务,与主履约流程解耦:
| 能力 | 说明 |
| --- | --- |
| 抢单大厅 | 打手按分类/关键词筛选,实时刷新、冻结押金抢单;拼单工单按份参与 |
| 我的订单 | 代练中 / 待验收 / 问题单流转,提交验收支持图片粘贴与拖拽 |
| 任务时限 | 工单可配置时限,超时由定时扫描 + 接口惰性判定双重处置(退回大厅 / 取消退押金 / 取消扣押金),并统计每个打手的超时订单数 |
| 等级权限 | VIP 等级体系:免押额度、最大同时接单量、大厅可见延迟 |
| 资金 | 押金冻结 / 退还 / 扣除流水,充值申请与提现审核 |
| 物品规则 | 按商品/SKU 匹配自动生成工单,可继承默认时限与拼单配置 |
| 管理后台 | 接单工单 CRUD、打手账号管理、问题单处置、定时任务管理,功能标签页记忆上次选择 |
说明:
- 打手通过**手机号验证码注册**并设置密码,注册后直接可用;登录使用手机号 + 密码。
- 短信默认 Mock 模式(验证码打印到服务日志),生产配置 `WORKER_SMS_PROVIDER=aliyun` + 阿里云凭据后走真实短信。
- 来源订单同步由物品规则驱动(`syncWorkerOrdersForSourceOrder`)。
- 工单状态机独立于主履约任务:`pending_material → unassigned → open → in_progress → pending_acceptance → accepted / cancelled`,另有 `problem`。
- 超时扫描定时任务默认 60 秒执行一次(配置见后台"平台配置 → 定时任务")。
## 提交前检查
```bash
docker compose -f docker-compose.dev.yml exec -T backend npm run check
docker compose -f docker-compose.dev.yml exec -T frontend npm run check
```
代码格式由仓库根目录的 Prettier 配置统一管理。修改代码后可在对应应用目录执行
`npm run format`,提交前使用 `npm run format:check` 只检查、不修改文件。
确认:
- 未提交真实账号、Cookie、token、手机号或生产配置
- `.env` 未入库
- **结构变更的 migration 与业务代码同 PR 提交**
- 领取链接相关改动已在 Docker 环境验证