51 lines
4.9 KiB
Markdown
51 lines
4.9 KiB
Markdown
# dealerhub · 三项后续落地报告(真实渠道对接 / 调度任务 / Vue3 前端)
|
||
|
||
> **目标**:完成 PROGRESS_PHASE4 遗留三项:① 抖音/1688 电商适配器真实对接(平台签名 + Token 刷新);
|
||
> ② Dramatiq + Redis 预警/拉单定时调度(服务器 worker 常驻);③ Vue3 前端并同源部署。
|
||
|
||
---
|
||
|
||
## 一、真实电商适配器(apps/channel)
|
||
|
||
### 1. 平台签名(`apps/channel/signing.py`,含金标准测试向量)
|
||
- **抖店开放平台**:参数按 key 升序拼 `k1v1k2v2...`,首尾拼 app_secret,MD5 大写
|
||
- **1688 Ocean 协议**:HMAC-SHA256(secret, 排序拼接串) 大写十六进制;URL 路径段参与签名(`/param2/1/{api}/{appKey}` + `_aop_timestamp`/`_aop_signature`)
|
||
|
||
### 2. 真实拉单与归一化(`apps/channel/adapters.py`)
|
||
- `DouyinChannelAdapter.fetch_orders`:POST `openapi-fxg.jinritemai.com/order/searchList`(method/app_key/access_token/param_json/timestamp/v/sign),金额分→元,买家/收货人/明细(`external_sku_id` 优先映射商品编码)全部归一化
|
||
- `Alibaba1688Adapter.fetch_orders`:GET `gw.open.1688.com/.../com.alibaba.trade/alibaba.trade.getBuyerOrderList`,`baseInfo`+`productItems` 归一化(行金额反算单价)
|
||
- **Token 自动刷新**:`ensure_fresh_token` 在 token 临期(<1 天)自动调 `/oauth2/refresh_token`(抖店)与 `/system/oauth2/refreshToken`(1688)并落库
|
||
- **push_stock**:抖店 `product.stockNum/update`;1688 未接入如实返回 False
|
||
- **Mock 回退**:未配置 app_key 的店铺自动回退模拟数据(开发零依赖);HTTP 缝隙 `_http_post/_http_get` 便于单测注入
|
||
- 测试:`tests/test_channel_adapters.py` 9 例(签名金标准向量、真实调用归一化、Token 刷新、Mock 回退、1688 参数构造、归一化结果端到端转销售单)
|
||
|
||
## 二、Dramatiq + Redis 调度(worker 常驻容器)
|
||
|
||
### 1. 架构
|
||
- `config/broker.py`:TASK_BROKER=redis(默认)→ RedisBroker(REDIS_URL);stub → StubBroker(测试);**核心修复:`apps/core/apps.py ready()` 启动即安装全局 broker**——dramatiq 默认 `get_broker()` 惰性创建"无 url 的 localhost RedisBroker"且不读 REDIS_URL,必须在任何 actor import 剝始化
|
||
- `config/dramatiq.py`:worker 入口(`dramatiq config.dramatiq`),先 `django.setup()`,再注册 actors + `@cron` 周期任务
|
||
- Actors:`apps/notify/tasks.py`(单租户/全租户预警扫描)、`apps/channel/tasks.py`(单店铺/全店铺自动拉单)
|
||
- 周期任务(dramatiq-crontab):`0 8 * * *` 全租户预警扫描;`0 2 * * *` 有凭证店铺自动拉单
|
||
- `docker-compose.yml` 新增 `worker` 服务(同镜像,dramatiq 命令,接入 pg-shared + dsp-net 复用无密码 redis `dsp-redis:6379/2`;healthcheck 覆盖为 `kill -0 1`——镜像内 curl:8000 检查对 worker 无意义)
|
||
|
||
### 2. 测试与线上验证
|
||
- `tests/test_tasks.py` 5 例:actor 注册、StubBroker 入队断言、直接调用同步执行验证预警/拉单结果
|
||
- 线上(192.168.5.7):backend/worker 双容器 healthy;从 backend `send()` 预警扫描与渠道拉单任务,worker 日志确认两条任务均被消费执行
|
||
|
||
## 三、Vue3 前端(frontend/,同源部署)
|
||
|
||
### 1. 技术栈与结构
|
||
- Vite 5 + Vue 3.5 + Vue Router 4(hash 模式)+ Pinia + Element Plus + Axios
|
||
- `base: '/static/'`:构建产物由 Django whitenoise 同源托管,`/` 由 `config/urls.py:spa_index` 返回 index.html
|
||
- 12 个页面:登录、经营大盘(KPI+销售/客户排行)、商品、库存、销售单、采购单、记账凭证(嵌套分录制单+借贷平衡实时提示+过账/作废)、应收、应付、财务报表(科目余额表试算平衡/资产负债表恒等/利润表/明细账 四标签页)、电商渠道(店铺接入+拉单+订单列表)、消息预警(扫描+已读)、开放平台(API Key 签发,明文仅展示一次)
|
||
- Axios 拦截器统一注入 JWT + X-Tenant-Id,401 自动跳登录
|
||
|
||
### 2. 构建与部署验证
|
||
- `npm run build` 成功(本地 Node 22);dist 上传服务器 → `backend/frontend_dist/` → Dockerfile `COPY frontend_dist/ /app/frontend_dist/` 打进镜像
|
||
- Playwright 真实浏览器验证(http://192.168.5.7:18050/):登录 alice → 大盘 KPI(销售额 336/采购 640/库存估值 416)与双排行榜渲染 → 凭证页 9 张自动凭证列表 → 财务报表页"✓ 试算平衡"+ 科目余额表(合计借=贷 900/900、期末 732/732)截图确认
|
||
|
||
## 四、验证汇总
|
||
- 本地 pytest:**103 passed**(含新增 9 适配器 + 5 调度任务用例)
|
||
- 线上容器:backend + worker 双 healthy;任务队列端到端(enqueue → worker 消费)实测通过;SPA/静态资源/ping 全 200
|
||
- 关键踩坑记录:① dramatiq 默认 broker 不读 REDIS_URL,须在 app.ready() 安装;② worker 入口模块必须先 django.setup() 再注册模型 actor;③ 镜像内 HEALTHCHECK 会被同镜像 worker 继承(curl 8000 永远失败),需 compose 覆盖
|