yml2213andClaude Opus 4.5 d07d88e25c feat(finance): 积分钱包操作 + 客服「确认履约」闭环
把「配置 → 待办 → 确认履约 → 扣额度 → 写成本快照」整条链路打通,
客服点一下就完成履约,不需要填复杂的东西。

积分钱包(P1.3):
- platform-credit-service.ts:充值 / 调整 / 消耗 / 流水查询。
- 三平台积分互不通用,每客服每平台一个账户;余额只能由流水产生,
  消耗不足直接拒绝(409),不允许负余额,与备用金口径一致。
- 换算率按平台:南航、乐玩 1 积分 = 1 分,ROCK 1 积分 = 1 元。
- 充值 / 调整只对 admin/operator/finance 开放;客服只能看自己的钱包。
- 备用金现金不在这里,走现有 fund_* 链路。

确认履约(P1.4):
- manual-fulfillment-service.ts:待办列表 + 确认履约。
- 待办口径:order_items.fulfillment_mode='manual' 且已配 fulfillment_config_id。
  人工单**不建 fulfillment_tasks**——那是自动发货通道的体系,人工单历来不走它
  (本地 102,955 个人工单的任务行数为 0)。
- 「已履约」判定:该订单项存在有效 manual_case 成本快照。
- 固定成本(消耗积分 > 0)→ 零输入;成本不固定(= 0)→ 只填实际成本金额。
- 确认时:积分平台扣积分钱包、备用金现金走 writeFundExpense,
  随后写 actual 成本快照(主体 = 积分流水 id 或备用金支出单 id)。
- 渠道与消耗积分永远提前配好,客服不选;重复履约直接拒绝。

前端:
- 新增「人工履约」页(客服菜单):待确认 / 已履约 / 全部,确认履约弹窗。
- 备用金页新增「平台积分」Tab(客服端与管理端各一份):
  余额卡片、积分流水、管理员充值与调整。

E2E 实测:配置 ROCK 100 积分 → 成本 ¥100.00(1 积分 = 1 元);充值 5000 积分
= ¥5000;确认履约后积分 5000 → 4900,成本快照 actual ¥100.00 写入;
重复履约被拒绝。测试数据已清理,登记表保留 357 条扫描结果供运营使用。

校验:后端 765 项 / 0 失败,前端 143 项全通过(含 tsc -b 与 build)。

Co-Authored-By: Claude Opus 4.5 (1M context) <noreply@anthropic.com>
2026-10-24 13:21:34 +08:00
2026-08-31 12:31:01 +08:00
2026-05-26 16:54:30 +08:00

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(开发 / 生产分离)

目录结构

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)。

业务边界(简图)

kuaishou-91(91 回调 + 快手 send-code)
  → 本系统建单 / 匹配商品 / 创建履约任务
  → 返回统一 claimUrl
用户打开领取页
  → 可选:快手行业电子凭证自动核销
  → affiliate-dash / 人工 完成发货
后台
  → 订单任务、平台配置、审计、人工复核
接单平台(打手众包)
  → 工单发布到抢单大厅,打手抢单代练
  → 提交验收 / 问题单处置 / 任务时限超时自动判定
抖音码速达
  → 抖店订单推送 → 建单 → DYC 券 → 领取/兑换页 → 核销

更完整的链路说明见:docs/履约配置/多发货平台与电子凭证整体链路.md。 文档总索引见:docs/README.md。

本地开发

推荐直接用 Docker,本机不必单独装 Node / PostgreSQL。

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/...

常用命令:

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 deploy/mac-dev.sh

开发验证

以容器内执行为准:

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 scripts/install-git-hooks.sh

启用后,每次 git push 会依次执行 apps/backend 和 apps/frontend 的 npm run check。 仅临时跳过检查时使用:

SKIP_CHECKS=1 git push

数据库迁移

采用 基线 + 渐进增量 策略,由 node-pg-migrate 管理,记录表为 schema_migrations。

文件 含义
001_init.sql 空库基线,建立完整业务结构
002_xxx.sql ~ 080_xxx.sql 后续结构变更的增量 SQL(接单平台 004009,寄卖相关 077080)

约定:

  1. 不要修改已经应用到共享/生产环境的历史迁移文件。
  2. 结构变更一律新增迁移文件,不要回写 001_init.sql。
  3. 空库按序号依次执行 001 → 002 → …;已有库只执行尚未记录的增量。
  4. 后端启动时自动 up;也可手动执行。

常用命令(容器内):

# 查看本地文件与数据库对齐状态
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

宿主机直连开发库(注意主机名):

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

本地需要清空数据重建库时(会丢开发数据):

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。

环境变量

本地:

cp .env.mac-docker.example .env

生产:

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 模板。

生产部署

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 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 秒执行一次(配置见后台"平台配置 → 定时任务")。

提交前检查

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 环境验证
S
Description
No description provided
Readme
22 MiB
Languages
TypeScript 93.6%
JavaScript 2.1%
CSS 2%
Shell 1.4%
Go 0.6%
Other 0.3%