yml2213
|
215f3351ba
|
fix(claim): 优化H5领取与绑定界面商品图展示并集成发货存证凭据生成
- 修复后端 affiliate_dash 领取详情接口漏传 imageUrl 字段的问题
- 优化前端 ClaimProductImage 占位逻辑,消除无图或失败时的空灰色方块
- 移植并集成 affiliate_dash 前端 Canvas 高清发货到账存证凭据(Retina 640x960)
- 在兑换成功页面提供结果图预览、一键复制到剪贴板与下载凭据图片功能
|
2026-10-10 20:55:02 +08:00 |
|
yml2213
|
c137741215
|
feat(frontend): vite 白名单放行 sakurafrp 域名后缀
|
2026-10-10 20:34:24 +08:00 |
|
yml2213
|
73de90ea4f
|
fix(admin): 平台推送地址优先回显已配置的公网域名
- 引入 resolveDouyinMashidaPublicOrigin 从已配置的 claimLinkBase/certLink 提取公网域名
- 更新后台面板 message_url 展示逻辑,即使从 localhost 访问也清晰呈现已配置的公网推送地址
- 向平台订阅推送时使用已解析的公网域名,避免错误推送 localhost
- 补充前端单元测试并通过全量构建校验
|
2026-10-10 20:18:23 +08:00 |
|
yml2213
|
fa1a67a03d
|
chore(vite): dev server 允许 ngrok-free.dev 域名访问
验证方式:ngrok 静态域名下直连自检通过,HTTP 状态 200 正常
|
2026-10-10 20:15:16 +08:00 |
|
yml2213
|
c54dbda344
|
feat(douyin-mashida): 全面统一为 /h5/<订单号> 直充路径,更新面板提示与直发链接生成
- 抖音码速达买家直充链接与兜底配置基址统一改为 https://<域名>/h5(直充路径为 /h5/<订单号>)
- 前端配置面板示例与提示升级为真实 16 位平台订单号形态(如 /h5/2628301923197355)
- 一键填入域名同步适配 /h5 基址,并在后端发券与发货私信推送中直接生成 /h5/<订单号>
- 数据库本地配置同步更新为 http://localhost/h5
验证方式:前端类型检查与打包构建通过,全量 132 项前端单测与 674 项后端单测全部通过
|
2026-10-10 20:10:09 +08:00 |
|
yml2213
|
d8ce40e346
|
feat(douyin-mashida): 彻底重构优化配置面板,支持一键填域名/订阅推送/网关自检并剔除冗余字段
- 剔除历史旧卡券时代废弃的 afterVerifyLink(去使用链接)与 afterVerifyMessage(通知正文)
- 面板新增一键填入当前域名、向开放平台一键订阅推送、停用推送以及网关连通与签名自检功能
- 业务配置重构为「基础接入与商户凭据」、「买家 H5 直充与发货私信」、「高级网关设置」三层清晰结构
- 后端与前端服务新增 /douyin-mashida/subscribe 与 /douyin-mashida/test-gateway 统一接口
验证方式:前端类型检查与打包构建通过,全量 132 项前端单测与 674 项后端单测全部通过
|
2026-10-10 20:05:46 +08:00 |
|
yml2213
|
748c18bc7d
|
feat(order): 精准区分自动直发与接单平台打手履约,智能门控H5直充按钮
- 后端增加按订单批量关联接单工单查询,精准识别履约类型(自动直发 / 打手履约 / 电子凭证 / 拼多多发货)
- 订单列表与详情页中,仅当订单为自动直发(affiliate_dash / 码速达直充)时展示【H5】一键复制直充链接与兑换状态控制
- 接单平台工单单与纯核销单隐藏直充 H5 按钮与兑换暂停/关闭标签,防止客服向打手单买家误发直充链接
- 交付模式标签升级为「⚡ 自动直发」、「👥 打手履约」、「🎫 电子凭证」、「📦 拼多多发货」
验证方式:前端类型检查与打包构建通过,全量 132 项前端单测与 674 项后端单测全部通过
|
2026-10-10 19:44:20 +08:00 |
|
yml2213
|
9a6b4313a8
|
fix(order): 移除订单列表冗长的买家信息列,优化多平台表格密度
- 移除订单列表中的「买家信息」列,消除抖音等虚拟品类中超长加密 open_id / 匿名买家造成的垂直与水平排版挤压
- 紧凑化表格宽度(scroll 960px),提升列表浏览效率
- 详情页继续保留完整买家原始载荷供追溯
验证方式:前端类型检查及 132 项单测全通过,前端打包构建通过
|
2026-10-10 19:04:09 +08:00 |
|
yml2213
|
03020e85c7
|
feat(order): 订单列表页UI重构与多平台(快手/91卡券、抖音、拼多多)适配
- 顶部新增平台快捷分流器(Segmented),支持「全部 / 快手·91卡券 / 抖音码速达 / 拼多多 / 本地模拟」一键筛选
- 重构订单表格列布局为平台与订单号、买家信息、商品规格、交付与履约状态、金额与支付时间、快捷操作等现代化字段
- 优化快手与 91卡券业务归集逻辑,统一数据查询与徽标显示口径
- 增强订单行快捷操作,支持一键复制 H5 领取链接与查看详情
验证方式:后端 674 项单测全通过,前端 132 项单测全通过,前端打包构建通过
|
2026-10-10 18:54:40 +08:00 |
|
yml2213
|
3f2bb3f09a
|
fix(admin): 修复恢复启用订单兑换时 orderId 闭包失效及 H5 页面重试机制
- 修复任务详情页和订单详情页中 statusMutation 闭包提前捕获空 orderId 导致恢复启用请求未发出的问题,改为动态从 query.data 解析最新的 orderId
- 订单详情页统一切换为 App.useApp 提供的 modal/message 实例,保证异步弹窗与提示稳定渲染
- H5 买家领取页面在暂停或关闭等异常状态卡片中增加【重新检测状态】按钮,客服恢复启用后买家可一键重新加载最新状态
- 验证方式:前端类型检查(0 error)、单测 132 项全通及前端 Vite 重新构建成功
|
2026-10-10 18:46:49 +08:00 |
|
yml2213
|
6971002579
|
feat(admin): 履约完成或终态时自动隐藏暂停与关闭兑换按钮
- 任务详情页(AdminTaskDetailPage):若任务已履约完成(completed/delivered/success)或订单已完成/已退款,自动隐藏【暂停兑换】和【关闭兑换】按钮,避免误操作
- 订单详情页(AdminOrderDetailPage):若订单下任务已全部履约完成或订单终态,同样自动隐藏【暂停兑换】和【关闭兑换】
- 针对已被暂停/关闭的历史订单,始终保留【恢复启用】入口以便误操作时补救
- 验证方式:前端类型检查(0 error)、前端单测 132 项全通及前端 Vite 重新构建成功
|
2026-10-10 18:42:12 +08:00 |
|
yml2213
|
9b29f7513a
|
fix(frontend): 修复 AdminTaskDetailPage 中 useMutation 的 Hook 调用顺序
- 将 useMutation 提升至组件顶层,避免因位于 query.isLoading 等早期 return 分支之后导致 React Hooks 顺序变动报错
- 验证方式:前端类型检查(0 error)、单测 132 项全通及前端 Vite 重新构建成功
|
2026-10-10 18:40:07 +08:00 |
|
yml2213
|
122144a2b7
|
feat(task): 任务详情与列表页增加H5订单号领取链接及兑换有效性控制
- 后端 AdminTaskDetail 视图补充关联订单的 claimStatus、claimStatusReason 等状态字段
- 任务详情页升级“领取链接”卡片:支持固定 H5 链接展示、状态标签(有效/暂停/关闭),并支持一键复制及【暂停/关闭/恢复启用】弹窗操作
- 任务详情页保留历史 Token 链接作为辅助对比展示
- 任务列表页在平台订单号旁增加一键复制 H5 领取链接的快捷按钮
- 验证方式:前后端类型检查、全量单元测试(后端 674、前端 132)及前后端全量构建均通过
|
2026-10-10 18:38:49 +08:00 |
|
yml2213
|
412d219cca
|
feat(claim): 支持固定H5订单号领取与订单兑换有效性后台控制
- 数据库新增 099_order_claim_status.sql 迁移,扩展 claim_status、claim_status_reason 等字段
- 后端新增 /claims/h5/:orderNo、UID提交、分销提交接口,保留原有 token 接口双轨兼容
- 增加兑换有效性强拦截:暂停/关闭/退款/取消订单精准阻断并返回友好提示
- 后台新增 POST /admin/orders/:orderId/claim-status 接口与审计日志记录
- 前端管理后台(订单列表、详情页)支持 H5 兑换状态展示、一键复制链接及暂停/关闭/启用弹窗
- 前端领取页面适配 /h5/:orderNo 路由及暂停/关闭等状态的异常展示
- 验证方式:运行前后端类型检查、全量单元测试(后端 674、前端 132)及全量构建编译均通过
|
2026-10-10 18:05:46 +08:00 |
|
yml2213
|
2d727a6797
|
fix(worker-platform): 修复对已冻结打手调级时误报冻结原因必填的问题
- 做了什么:
1. 后端 reviewAdminWorkerUser 中优化 freezeReason 校验条件:区分是否为新变为 rejected 状态,若账号原本已处于冻结态且本次仅调整等级未变动状态,直接沿用已有冻结原因,不再强制要求传 freezeReason;
2. 前端 submitLevel 在提交调级时补充传递打手既有的 freezeReason;
3. 后端新增对应测试用例确保后续回归。
- 为什么:
当打手处于冻结状态(rejected)时,管理员对其执行调级操作因未携带 freezeReason 触发了冻结原因校验错误,导致调级失败。
- 验证方式:
运行 npm run check(671 项测试全绿,类型与构建检查通过)。
|
2026-10-10 13:51:58 +08:00 |
|
yml2213
|
85fc2eaa7c
|
feat(worker-platform): 支持后台人工调级锁定等级与解除自动升级限制
- 做了什么:
1. 新增数据库迁移 098_worker_level_lock.sql,在 worker_users 表增加 is_level_locked、level_locked_at、level_locked_reason、level_locked_by 字段;
2. 后端自动升级函数 maybeUpgradeWorkerLevelWithClient 和判定函数 shouldApplyAutomaticWorkerLevel 增加 is_level_locked 守卫,被锁定的打手跳过自动升级;
3. 后台打手审核调级服务 reviewAdminWorkerUser 支持等级锁定参数与变更记录,人工调级时自动写入 worker_level_logs 流水,并提供 updateAdminWorkerLevelLock 专用接口;
4. 前端打手列表等级列展示「已锁定」状态及 Tooltip 原因,调级弹窗支持「锁定自动升级」开关与原因输入,操作列提供「解除限制」快捷操作;
5. 打手详情抽屉补充展示等级锁定说明。
- 为什么:
解决打手被人工调整等级(如 VIP5 调至 VIP4)后,因历史累计完成单数满足门槛导致后续结算再次自动升级回高等级的问题,并支持后续随时解除限制恢复自动升级。
- 验证方式:
1. 后端新增 shouldApplyAutomaticWorkerLevel 与 mapWorkerUser 单元测试,运行 npm run check(670 测试全过,类型与 SQL guard 验证通过);
2. 前端新增 worker-level-lock.test.ts 单元测试,运行 npm run check(130 测试全过,typecheck 与 build 验证通过)。
|
2026-10-10 13:44:57 +08:00 |
|
yml2213
|
1339c6887e
|
refactor(claim): 删除自建皮肤领取页,路径式 /claim/<token> 复用现成领取页 + 物品图片
**删掉上一轮自建的领取页**
- `pages/claim-skin/*`、`services/claim-skin.ts`、`types/claim-skin.ts`、`tests/claim-skin.test.ts`
及其样式(client.css 里 `.claim-skin-*` 整段 254 行)
- 后台我加的展示项:「皮肤自动发货」筛选项、执行器标签、「买家提交信息」卡片
**路径式链接复用现成领取页**
- `utils/claim-path.ts`(中性命名 `isClaimPath` / `resolveClaimToken`)+ 单测
- `App.tsx`:pathname 命中 `/claim/<token>` → 独立 BrowserRouter 渲染**现成的 `ClaimPage`**
(页面零复制零改动:它只用 `useParams`,不读 hash);`#/claim/:token` 仍走主站 HashRouter,
两者互不影响,且有用例断言 hash 入口不命中路径判定
**物品图片**
- 后端领取详情的 `product.imageUrl` 接上(内部 SKU 在 `spec_json.code` / `sku_code` /
`sku_name` 哪个字段都能回退找到图,映射不到返回空串)
- 领取页新增 `ClaimProductImage`:居中大图(宽度 min(220px,62%)、圆角 16px),
空值或加载失败都降级虚线占位块,不出现破图
`npm run check`:前端 128 / 128 全绿(去掉旧用例 17 个、新增路径与图片用例)
|
2026-10-09 16:13:46 +08:00 |
|
yml2213
|
eacca11768
|
feat(claim-skin): 买家皮肤领取页(路径式 /claim/<token>)
买家从卡券/订单消息里的链接打开,看到物品图片与信息,填游戏账号即可领取。
- **路径式无 `#`**:新增独立 BrowserRouter 分支(照 `/redeem` 的做法),
`isClaimSkinPath` 只认 `/claim/<token>` 两段精确匹配;
打手领取页仍是主站 HashRouter 的 `#/claim/:token`(其 pathname 为 `/`),两者互不影响
- **物品卡片**:商品图(映射不到或加载失败都降级为占位块,不出现破图)、
内部 SKU、数量 ×N、订单号、引流标题(灰色副信息)
- **领取表单**:按接口下发的 `claimFields` 动态渲染(游戏账号/角色ID),
提交前二次确认让买家核对账号
- **状态**:待领取展示表单;已领取展示成功态(优先于 tokenStatus);
token 失效/过期/关闭/已用分别给中文提示与联系客服引导
- 纯函数抽到 `utils/claim-skin.ts` 并补 20 个单测(路由判定、状态映射、字段校验、格式化)
|
2026-10-09 15:06:56 +08:00 |
|
yml2213
|
b70741daec
|
feat(admin): 后台可配领取链接/发消息模板 + 可见买家账号 + 修正皮肤领取链接
**面板新增三项(抖音码速达接入配置)**
- 领取链接基址 (claimLinkBase):带实时预览「皮肤订单最终链接」,并拦截两类误配 ——
带 `#`(`#/claim` 是打手领取页,会把买家带进绑定 UID 流程)、缺 http(s) 协议
- 发消息模板 (messageTemplate):提示可用占位符;模板里没有 `{link}` 直接报错提示
- 付款后发领取消息开关 (claimMessageEnabled)
- 顺带把默认出网超时的展示值对齐后端(10s → 20s)
**后台可见买家提交的账号(运营发货依据)**
- `getAdminTaskDetail` 新增 `claimSubmission`,按 `canViewSensitiveTaskData` 门禁下发
(与 claimToken 明文同一门禁);任务详情新增「买家提交信息」卡片(可一键复制账号)
**修正:皮肤任务的领取链接**
- 详情接口原来用全局 `buildClaimUrl`(默认 hash 形式 `…/#/claim/…`),那是**打手**领取页;
皮肤任务改用 `claimLinkBase + '/' + token`,运营复制给买家的链接才是买家页
- 顺带把「履约渠道」筛选项加上「皮肤自动发货」、任务详情执行器标签加上它、
「来源渠道」加上「抖音码速达」(此前筛不出来抖音订单)
|
2026-10-09 15:06:49 +08:00 |
|
yml2213
|
0b619b2915
|
feat(asset): 商品图资源 + 内部 SKU → 图片解析
买家领取页要展示「待领取物品图片」,图片按 affiliate_dash 的英文 sku 命名
(如 lucky_coin_x2.webp = 内部 SKU「幸运币2个」),从 affiliate_dash 项目
复制 38 个 slug(40 个文件,其中 2 个 slug 同时有 png/webp)到前端 publicDir
`apps/frontend/public/images/products/`,由 Vite 原样对外(不进 bundle)。
新增 `services/platforms/affiliate-dash/product-image.ts`:
内部 SKU →(复用权威映射表 AFFILIATE_DASH_SKU_MAPPING)→ 英文 sku → 图片 URL。
映射不到返回空串,由前端展示占位图(不猜、不拼不存在的文件);
以后图片挪 CDN 只改这一处。
测试含「映射表每个 sku 都必须有对应图片文件」的守卫,防止新增商品漏图。
|
2026-10-09 14:47:50 +08:00 |
|
yml2213
|
7834ee7bef
|
feat(douyin-mashida): 商品展示统一改为内部 SKU(引流标题降级)
平台 sku_order_list[].code 是商家维护的**内部 SKU**(如「幸运币2个」),
product_name 只是店铺用来**引流**的长标题(如「【测试专用】和平精英皮肤小店入口…」)。
商家按内部 SKU 认货(后续履约匹配也用它),原来的展示把引流标题当商品名,
列表/详情/兑换页三处都会让人认错货。
改为以内部 SKU 为主、引流标题降级为副信息:
- 后台订单列表「商品 / 任务」:itemTitle = 内部 SKU,skuNick = 引流标题
- 后台订单详情「商品明细」:商品 = 内部 SKU;SKU/规格 不再重复展示同一串
- 兑换中心 /redeem:商品 = 内部 SKU(粗体)+ 引流标题(灰色小字,新增 productTitle 字段)
实现只在 `source === 'douyin_mashida'` 时生效(resolveOrderItemTitle /
resolveOrderItemSkuNick 加 douyin 分支),其它平台展示逻辑不变;
用真实订单形态补了单测。
|
2026-10-09 13:31:45 +08:00 |
|
yml2213
|
e9a87194bd
|
chore(douyin-mashida): 公网联调工具链(切地址 + 前置自检)
真实联调必须让平台能主动推到本机,所以需要隧道;这两个脚本把「配置切换」和
「联调前该验的东西」自动化,避免手工漏配(尤其 certLink 漏改、占位密钥没换):
- `douyin-mashida:set-public-url`:把 certLink/afterVerifyLink 切成隧道地址,
打印平台侧要配的三项(message_url / topics / 卡券链接),并自检隧道:
用错误签名打公网地址,期望 401 —— 证明「隧道 → Caddy → 后端 → 验签」通了,
不需要密钥、也不产生真实订单
- `douyin-mashida:preflight`:四步自检
① 配置就绪度(含识别早期联调留下的占位密钥,它会让 hasSecret=true 骗过你)
② 真实网关连通性:平台是先校 uid 再校签名,带 --secret 时若平台报的不是
签名错误即证明签名/密钥正确(正式推送前就能排掉密钥问题)
③ 公网回调可达性 ④ certLink 与公网域名是否一致
- 平台文档只给了 code=10000 表示成功,无完整错误码表,故脚本不翻译码值、
只原样打印,并对实测观测到的码(如 10014)加说明
同时修掉 vite dev server 的一个潜在坑:`VITE_ALLOWED_HOSTS` 早已在 .env 与
compose 中配置,但 vite.config.ts 的白名单是硬编码的、从未读取该变量,
换隧道域名必然被 Vite 拦(Blocked request);现已真正接上并默认放行
常见隧道域名后缀。
|
2026-10-09 12:01:42 +08:00 |
|
yml2213
|
8c33ff62ec
|
feat(redeem): 固定兑换链接 /redeem(输入订单号 → 状态分流 → 兑换)
- 路径式入口 `https://<域名>/redeem`(不带 #):主站是 HashRouter,
故在 App.tsx 里早于 isWorkerSite() 插入独立 BrowserRouter 分支,
主站与打手站路由均不受影响;Caddy 已有 try_files 兜底,无需改部署
- 页面按 7 种状态渲染:not_found / unpaid / cancelled / refunded /
pending_certificate / redeemable / verified
- not_found 保留用户输入的订单号,不清空
- pending_certificate 只给显式「刷新」,不做自动轮询(公开限流接口,避免空转)
- verified 只展示后端已掩码的券码,绝不渲染明文 certNo
- confirm 不假设成功:一律按返回的 status 重渲染
- 纯函数抽到 utils/redeem.ts(无 React/antd、无 @ 别名),可直接跑单测
|
2026-10-09 12:01:35 +08:00 |
|
yml2213
|
080480f03b
|
feat(admin): 后台新增「抖音码速达」配置面板
- 新增 Tab:/admin/platform-shops?tab=douyinMashida
- 面板字段:enabled / baseUrl / uid / secret / certLink / afterVerifyLink /
afterVerifyMessage / messageType / timeoutMs / handledTopics(tags)
- 密钥脱敏:出参 secret 永远为空,输入框未编辑时对外 value 恒为 '',
掩码不会污染后端「空串=保留原值」语义
- 顶部展示 ready 状态,未就绪时用 Alert 列出缺哪几项;
始终展示平台侧要配的推送地址(可复制)
- baseUrl 命中本地地址时提示「当前指向本地网关桩,不会请求真实码速达」
- 把 AdminPlatformShopsPage 内联的 SecretFieldControl 抽成共享组件
(函数体与原实现逐字节一致,8 处既有调用点参数不变)
|
2026-10-09 12:01:29 +08:00 |
|
yml2213
|
893388b37f
|
feat(consignment): 寄售仓库在库列表增加通过审核入库时间并在手动匹配中补全时间展示
- 寄售仓库 (ConsignmentInventoryPanel):表格中增加「通过审核入库时间」列并格式化展示,详情弹窗规范化展示入库时间、出库时间与提报时间
- 手动匹配弹窗 (ConsignmentManualMatchModal):候选订单列表展示工单创建时间,顶部横幅与已选概要展示现货入库时间及订单创建时间;从订单匹配现货时展示现货入库时间
- 内部匹配接口与工单拦截:后端 listInventoryMatchableOrders 返回工单创建时间及平台单号;发布拦截弹窗中增加展示现货入库时间
- 验证方式:运行前后端单元测试全部通过,前端 build 校验通过
|
2026-10-08 15:41:23 +08:00 |
|
yml2213
|
5466783017
|
feat(worker-platform): 接单模板列表支持一键导出到本地 CSV
后台接单平台「接单模板列表」右上角新增「导出」按钮:
- 按当前筛选(搜索关键词 + 一级/二级分类)导出全部模板,服务端分页的
情况按每页 100 条循环拉全量(页数上限兜底),不会只导出当前页;
- 导出 UTF-8 带 BOM 的 CSV,Excel/WPS 双击直接打开,文件名带时间戳
(接单模板_YYYYMMDD_HHmm.csv);逗号/引号/换行按 CSV 规范转义;
- 22 列:模板名称/标识/分类/系统/平台/商品SKU/接单金额/按件价/拼单配置/
验收模式/自动建单/状态/超时策略/资料字段/已绑定商品与绑定明细/时间等;
- 空结果、超出上限、浏览器不支持下载分别给出明确提示。
导出逻辑集中在 apps/frontend/src/utils/product-rules-export.ts(纯函数,
无 @/ 别名与运行时相对导入,便于 node --experimental-strip-types 单测),
新增 tests/product-rules-export.test.ts 覆盖分类回退、金额与拼单转换、
绑定明细、CSV 转义、文件名、空列表与非浏览器环境。
|
2026-10-08 13:50:19 +08:00 |
|
yml2213
|
98dc675b88
|
Revert "feat(claim): Rock 同一提货号过期后自动换新二维码"
This reverts commit 9d981c253f.
|
2026-10-06 18:08:20 +08:00 |
|
 yml2213andCursor
|
9d981c253f
|
feat(claim): Rock 同一提货号过期后自动换新二维码
腾讯活动链接会过期,领取页不能一直用第一次的签名。过期后向 0666 重取同一提取码的新链接,不另发提货号。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-06 18:02:17 +08:00 |
|
 yml2213andCursor
|
f6343e58c1
|
fix(claim): 后台改对扫码 UID 后领取页跟上,绑定失败仍记住扫到的号
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-06 17:47:01 +08:00 |
|
yml2213
|
2a0eea40e9
|
feat(claim): 配合方案B支持买家纠错扫码UID自愈绑定,自建页面增加确认更换提示与快捷填入
|
2026-10-06 11:12:18 +08:00 |
|
 yml2213andCursor
|
dbdafd3dc4
|
fix(consignment): 撤销寄售履约按选择回库,不再因加好友强制作废扣押金
买家退款时应能把现货退回仓库继续匹配;卖家押金只在客服显式选择扣除时才没收。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-05 14:28:38 +08:00 |
|
yml2213
|
0173efb277
|
feat(pdd): 店铺连续同步失败 3 次后熔断,停止自动重试
背景:生产上「木塔网游专营店」Cookie 已过期,每约 24 秒重试一次、每轮刷一条 ERROR 日志,
持续数天。Cookie 失效不会自愈,继续重试只刷日志、白白占用同步周期。
行为:
- 失败 1~2 次:累计计数,继续重试;
- 第 3 次:熔断,打一条显式 ERROR(含"更新 Cookie 会自动恢复"提示);
- 熔断后:自动同步直接跳过,不再刷错误日志;
- 恢复:更新 Cookie(后台编辑或浏览器插件)自动解除;手动同步成功一次亦可。
熔断状态持久化在店铺配置里,容器重启后依然生效。
改动:
- pdd-source-config-service:阈值常量 + consecutiveFailures/syncSuspendedAt/
syncSuspendedReason 持久化字段 + 纯函数 resolvePddStoreFailureState /
applyPddStoreBreakerState / isPddStoreSyncSuspended / shouldResetPddSyncBreaker。
- pdd-sync-service:syncAllPddStores 跳过熔断店铺并上报 suspendedCount;
syncStoreOrders 失败记录、成功清零(健康店铺不产生额外配置写)。
- pdd-order-sync-job:上报熔断数,且存在熔断店铺时强制留在 INFO 级
(否则"全部店铺已停同步"会因新单为 0 被降级成 debug 而不可见)。
- 前端加「已熔断 · 停止重试」标签与类型字段。
顺带修掉一个隐患:saveAdminPddStore 收的是 Partial 载荷,normalizePddStoreItem
会为缺失字段产出 undefined,导致一次「只改店铺名」的保存就把熔断洗掉。
由 applyPddStoreBreakerState 回填既有状态解决。
验证:9 项熔断单测(含局部保存不洗状态、Cookie 变更解除熔断)。
|
2026-10-03 14:27:38 +08:00 |
|
yml2213
|
90de6b9419
|
perf(worker): 单号检索收敛为可走索引的等值/前缀,并支持关联单号反查
问题:后台「按单号检索」在平台单号/工单号分支里 OR 了一支
`work_order_no ILIKE '%'||$n||'%'`。前置通配符非 sargable,任何 B-Tree 都用不上,
整条查询退化成 work_orders 的 Seq Scan。
生产实测(10.3 万行 / 572MB,pg_stat_statements 46 小时窗口):
- 列表查询 + 统计查询用同一份 filters 并行执行,一次检索打两遍全表;
两者合计占全库 66% 的磁盘读(1.1TB/46h),单次读 26,442 个块(约 211MB)、398ms、返回 1 行。
- 收敛后同库同参数:等值 0.19ms,前缀 30ms(约 2000 倍)。
- 真实统计查询端到端 760ms → 61.6ms。
改动:
1. 平台单号/工单号分支收敛为「等值 + 前缀」,与 order-repo / task-repo 既有写法一致;
工单号含随机小写段(10.2 万行里 7.4 万行非全大写),保留 LOWER() 比较。
2. 迁移 090 补 text_pattern_ops 前缀索引:当前库 collation 为 en_US.utf8,
前缀 LIKE 在默认 B-Tree 上会退化成全索引扫(实测 438ms)。
3. 新增关联单号反查分支:SUB/INV/AS/DT 不在 work_orders 上,
经关联表反查工单 id,四条链路均命中唯一索引。
4. 前端 isExplicitOrderNoSearch 同步补齐(否则搜到的单会被状态页签挡掉、
实时补丁还会把行删掉)。
验证:生产 EXPLAIN 逐条比对 + 14 项仓储单测 + 6 项分类器单测。
|
2026-10-03 14:27:32 +08:00 |
|
yml2213
|
56e718a65d
|
fix(admin): 显式单号检索下,其它筛选不得把正在看的工单从实时补丁里删掉
## 修的是什么
实时补丁的判定链里,「状态 / 接单人员 / 日期范围」这些可证伪条件排在关键词护栏之前,
任一不成立就先返回 mismatch(允许删行),于是第 156 行那条「有关键词就不删行」的护栏
在带筛选时根本执行不到。
d6523635 只在 UI 侧补了一半:搜显式单号时清空 状态/赠送阶段/验收模式,
但**没清接单人员、分类、日期范围**,而 workerId 与 createdRange 同样会先返回 mismatch。
所以「单号 + 接单人员」或「单号 + 日期范围」仍会把行删掉。
实测(真实函数):
单号 + 状态筛选 -> mismatch(删行)
单号 + 接单人员筛选 -> mismatch(删行)
单号 + 日期范围 -> mismatch(删行)
## 修法
只对「显式单号检索」提前短路为 unknown(只原地更新,不插不移):
if (isExplicitOrderNoSearch(filters.keyword)) return 'unknown'
刻意**不**推翻既有设计:模糊关键词(产品名/昵称等)仍保留「状态可证伪即移除」的语义,
因此原来锁定该语义的用例继续通过。
顺带把 isExplicitOrderNoSearch 合并到 utils:此前面板与 AdminLayout 各有一份规则,
重复两份正是上次 tab 名单漏改那类问题的温床。
## 边界(重要)
**本次修复不覆盖「无任何其它筛选、只按单号搜索」的场景。**
实测该场景下判定本就是 unknown、行不会被删(状态原地更新、分页与统计一致),
因此线上反馈的「状态变化后搜不到、必须整页刷新」若发生在无筛选条件下,另有他因,尚未定位。
|
2026-10-02 17:51:39 +08:00 |
|
yml2213
|
e0d7398d37
|
fix(admin): 风控 tab 点了没反应 —— tab key 白名单漏登记,并合并为唯一来源
## 现象与根因
风控 tab 渲染得出来、但怎么点都切不过去,且不报错、不白屏。
原因是 tab key 名单在同一文件里写了两遍:
- isWorkerPlatformTab()(changeTab 的守卫)
- loadActiveTabPreference()(localStorage 旧选择校验)
新增风控 tab 时只把 'risk-control' 加了 TAB_QUERY_KEYS 与 Tabs items,漏了上面两处,
于是 changeTab('risk-control') 在第一行 `if (!isWorkerPlatformTab(nextTab)) return` 就返回了。
这类漏改没有任何报错信号,只能靠点一下才发现——上一轮的验证只查了构建产物里
是否含面板代码,没有验证 tab 真的能切换,所以没拦住。
## 修法
把名单抽到 utils/admin-worker-platform-tabs.ts,只保留唯一一份
WORKER_PLATFORM_TAB_KEYS;查询缓存映射与「只登记缓存但不是 tab」的例外(hall-config,
它是「杂项配置」tab 内部的区块)也一并显式化,避免下次再靠人记。
## 回归测试
新增 tests/admin-worker-platform-tabs.test.ts(5 个用例):
- 风控 tab 必须被 isWorkerPlatformTab 接受
- 每个登记了切页缓存的 tab 都必须在合法 tab 名单里 ← 正是本次漏改的形态
- 名单无重复项 / 非法 key 被拒 / 名单内自洽
|
2026-10-02 17:13:59 +08:00 |
|
yml2213
|
78c676d2b9
|
feat(worker-risk): 风控后台页面(可调阈值/实时状态/事件留痕/人工封禁解封),并修复解封后仍被 403 的 bug
## 修复的 bug(生产已发生)
风控三级封号把 tracker.banned 置为 true 后,banned 检查发生在鉴权之前,
而它只存在于进程内存。后台把账号改回 active 时数据库变了、内存没变,
于是账号在库里已经正常、请求却永远 403,只能等容器重启才恢复。
修法两道:
- **数据库是唯一事实来源**:banned 改为带 30 秒 TTL 的缓存,到点回查
worker_users.status;已解封(或账号已删)则清除标记并放行。
对被判 banned 的 key 最多每 30 秒查一次,即便对方仍以 65 QPS 刷也只是
每 30 秒一次查询。查询失败时**保守维持封禁**,不因数据库抖动放行刷子。
- **后台改状态时主动释放**:review 只允许设 active/rejected/disabled/pending_review
(没有 frozen),所以走到那里就意味着不再由风控冻结,直接释放内存状态。
顺带修掉一个连带缺陷:解封时只清了 violationCount,没清滑动窗口计数,
账号刚恢复就会被上一轮残留的 windowCount 立刻限流、并在 60 秒内二次封禁。
现在整轮窗口状态一并清零。
## 新增后台页面
接单平台 → 风控(/admin/worker-platform?tab=risk-control),三块:
- 阈值配置:6 个阈值可视化编辑,落 app_config_entries,保存即热生效,无需重启
- 当前实时风控:进程内存状态(已封禁/惩罚中/仅记录),支持人工解封、封禁、仅释放内存状态
- 风控事件留痕:worker_risk_events 分页查询,可按类型/IP 筛选
## 关键取舍
只记**显著事件**(二级惩罚、三级封号、人工处置),不记每一次被拒请求。
被刷时实测 45~65 QPS,按请求写库正是最初要解决的问题。实时计数仍走进程内存。
事件写入为 fire-and-forget 并吞异常,留痕失败绝不影响大厅请求。
## 验证
- 新增 6 个用例覆盖解封立即生效、对账自愈、TTL 不重复查库、对账失败保守维持、阈值可配置
- 迁移 089 已在临时库实测:表结构/4 索引/CHECK 约束/外键均正确,非法 event_type 被拒
- 后端 557 测试 554 通过 0 失败;前后端 typecheck/lint/format/build 全绿
|
2026-10-02 17:05:25 +08:00 |
|
 yml2213andCursor
|
1239393fc2
|
perf: 筛选统计去掉 JSON 兜底,并补上 orders 覆盖索引
去掉 work_orders 堆访问后,JOIN 会把 orders 整表扫进 Hash Right Join。
领取页在快照新鲜时不再打上游;快手出网统一 10 秒超时。倒计时改为自适应间隔。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-10-01 15:05:38 +08:00 |
|
yml2213
|
f4c405d51a
|
fix(worker): 打手端实时补丁不再误删"按工单号搜到的行",并同步分页总数
这是后台那个"搜工单号搜不到、必须刷新整页"在打手端的同源副本:
WorkerLayout 的 operation = event.operation === 'remove' || !matches ? 'remove' : 'upsert'
只要前端判"不匹配"就删行,而前端关键词匹配器**没有比对工单号**——
后端搜索表达式的第一项恰恰就是 work_order_no(还有 material_json 里更多前端不知道的字段)。
于是按工单号搜索时,该单一旦有实时变更,行就被从缓存删掉、列表变空。
修法与后台侧对齐:
1. 抽出 utils/worker-realtime-work-orders.ts(可单测、无运行时依赖),关键词匹配补上
workOrderNo,并改成三态判定——状态/分类等可证伪维度不符才删行;关键词能找到=match
(允许插入),找不到=unknown(不删不插,只就地更新)。
2. patchWorkerWorkOrderListEnvelope 增删行时同步 data.pagination.total,
修掉"列表空了、分页器还显示有 N 条"。
3. 大厅无限下拉只在第 1 页允许插入,其余页只就地更新。
验证:前端 86 项单测全绿(新增 6 项,覆盖"按工单号搜索+实时变更不得删行"、
unknown 只就地更新、删/插同步总数、无限下拉页边界)。
|
2026-10-01 13:48:05 +08:00 |
|
yml2213
|
d262b22a41
|
fix(admin-worker): 实时补丁同步筛选统计,并锁住"按工单号搜索结果被删行"的回归
线上现象:按工单号搜到的那一单,状态一变(实时 SSE),列表就变成「暂无数据」,而上方
筛选统计仍显示「共 1 单」;整页刷新后才恢复。
成因在前端实时快照补丁:旧逻辑里的 matchesWorkOrderKeyword 只比对「商品名 + 平台单号」,
不比对「工单号」,所以按 WO… 搜出来的行一旦有实时变更就被判成不匹配,直接从缓存里删掉;
而 statistics 是首次响应里带出来的、没被同步,于是出现"统计说有、列表没有"的自相矛盾。
(删行本身已由上一条提交改为:关键词筛选归入「不可证真」→ 只原地更新。)
本条补齐剩下的一致性:
1. 实时补丁增删/原地更新行时,同步维护 data.statistics 的 totalCount 与 statusCounts
(statusCounts 按运营态口径记账,与后端 getWorkOrderStatistics 一致),列表与统计不再打架;
2. 补两个回归测试:按工单号搜到的行在状态变化后必须原地保留;插入/移除/原地改状态三种
情况下统计都要跟得上。
验证:前端 80 项单测全绿(新增 2 项),lint / typecheck / build 通过。
|
2026-10-01 12:35:59 +08:00 |
|
yml2213
|
40ee36d72a
|
fix(consignment): 修复寄卖取消解绑、SUB 单号写空、拼单门禁与后台工单列表实时乱跳
1. 问题单处置「取消」后打手信息不消失:resolveProblemWorkOrder 的取消分支从未解绑寄卖现货,现货永久停在 consumed 占库不还,工单 material 上的寄售标记残留又让列表继续显示「供货打手」。取消时补上解绑;调用必须排在状态 UPDATE 之前——那条 UPDATE 会清空 friend_added_at,清完再解绑会把已确认加好友的货误判成可回池(二次出货);
2. 解绑函数在没有现货可解绑时直接 return,跳过了摘标记,于是「库存行早已不存在」的老帖永远摘不掉寄售标记(列表一直显示供货打手)。现在无论是否解绑到现货都摘标记,痕迹保留在 material.consignmentHistory 的 detachReason 里;
3. 内部现货履约写入的 sourceSubmissionNo 恒为空:取的是占库 `UPDATE ... RETURNING *` 的行,只带回本表列,没有 join 出来的 submission_no,导致工单列表把所有寄卖单都显示成「老单无提报单号」,SUB 单号这个唯一锚点丢失。改取带 join 的预读库存(内部现货资料、指派工单资料、指派事件留痕三处)。存量老单按业务口径不回填;
4. 后台接单工单列表实时乱跳:SSE 快照补丁按数组下标读 queryKey,面板 key 加了字段后下标全错(page 被读成 targetWorkOrderId → 任何页都被当成第 1 页),且完全没判「内部寄售匹配」等筛选,于是任意一条不在当前页的老单只要发生变更就被插到列表顶部。改为结构化 queryKey + 三态判定(match 才允许插顶、mismatch 才允许移除、其余只原地更新),插入还要求 page===1 且工单 id 大于当前首行;状态比对改用 rawStatus(快照的 status 是运营态,拼单母单会被显示成 in_progress),分类筛选因快照不带 parentCategoryId 归入不可证伪;
5. 新增「寄卖只做整单」硬门禁:已有人参与的拼单工单不参与现货匹配与库存提醒,履约入口一律返回 work_order_sharing_not_consignable,前端「手动匹配寄卖」入口按同一口径收敛;开了拼单但无人参与的整单不受影响,避免过度拦截。
验证:后端 520 项单测(517 pass / 3 skip)、前端 78 项全绿;并对真实开发库跑通问题单取消三类场景(老帖无库存行 / 回库 / 已加好友作废)、退回打手与退回大厅的反向用例(不得解绑)、履约写入真实 SUB 单号、拼单门禁四入口 + 未过度拦截反向用例。
|
2026-10-01 12:17:00 +08:00 |
|
yml2213
|
1636616ec1
|
feat(worker-platform): 寄卖价格改由接单模板只读定价,我的寄卖支持服务端搜索与分页
1. 寄售价不再由卖家填写:打手端改为只读展示模板价(与履约押金同款只读框),服务端在发布时按同一套规则复算金额,保证展示口径与落库口径一致,直连接口也无法随意定价;
2. 模板缺档按「最接近的已配置档位」报价、距离相同取更低档:后台只配了 1/3/7 钻时,号主出 2 钻报 1 钻价、出 6 钻报 7 钻价,不再因为中间档没配价而拿不到价格;
3. 我的寄卖列表把分组、关键字、翻页全部下沉到 SQL:新增 listWorkerConsignmentPage 用 UNION ALL 合成「提报单行 + 无提报单库存兜底行」,CASE 推导 待审核/履约中/已完成 分组,返回当前分组 total 与三个分组的角标计数;
4. 关键字覆盖寄卖单号、规格名、系统/区服/系列/钻级、卖家角色名与数字 ID、联系方式、买家昵称与游戏 ID(读取 work_orders.material_json.collect.fields 的多种历史键名);
5. 前端搜索框 300ms 防抖后才请求服务端、换分组或换关键字自动回到第 1 页、列表底部增加分页器,并移除原来固定 pageSize 200 的隐形截断;
6. 新增仓库层与匹配层单测,其中包含 SQL 分组 CASE 与 domain/consignment-item-stage.ts 的 group 映射交叉断言,防止两处状态机漂移;后端 516 项、前端 66 项测试全部通过。
|
2026-10-01 11:07:28 +08:00 |
|
 yml2213andCursor
|
ef491660e7
|
fix(admin-worker): 发单金额高于用户付款时强制确认,避免只靠红色字提醒
发布、指派和改价都会弹出亏损确认,且不能用「不再提示」跳过;列表发单金额旁增加「高于付款」标签。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-21 15:48:23 +08:00 |
|
 yml2213andCursor
|
9298b31b9b
|
feat(worker-platform): 增加寄卖白名单总开关,关闭后全员可发布
资金配置与打手列表可一键切换白名单模式;发布校验、打手端入口按总开关放行。同时收紧未开通判定、关权限确认和开通弹窗文案。
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-21 14:23:00 +08:00 |
|
yml2213
|
937e51db6b
|
fix(admin-worker): 修复打手管理切换寄卖权限后表格状态未自动刷新的问题
1. 修复 WorkersPanel 中 queryKey 匹配偏差,改用统一的 admin-worker-platform-workers 查询键与 refreshWorkers();
2. 引入 setQueriesData 乐观更新机制,点击 Switch 切换后毫秒级原地更新行状态,无需手动刷新页面。
|
2026-09-21 13:37:54 +08:00 |
|
yml2213
|
cb8b249e57
|
feat(worker-platform): 实现打手寄卖白名单权限控制与存量发布打手平滑过渡
1. 新增数据库迁移 082_worker_consignment_whitelist.sql,worker_users 表增加 can_consign 与 consignment_activated_at 字段,存量在 consignment_submissions 提报过的打手默认自动开启;
2. 打手端提报接口 publishWorkerConsignment 增加白名单权限服务端强校验拦截(403 consignment_permission_required);
3. 管理后台打手管理表格增加「寄卖权限」实时 Switch 开关列与打手详情抽屉权限展示,支持客服即时开通或关闭打手寄卖权限;
4. 打手端我要寄卖页面在未开通权限时展示专属白名单引导卡片,一键呼起联系客服开通寄卖弹窗;
5. 补充寄卖权限拦截与路由单测,前端59项单测与后端507项单测全部通过。
|
2026-09-21 13:30:33 +08:00 |
|
yml2213
|
0df5c9f34e
|
feat(worker-platform): 抢单大厅首页新增联系客服胶囊入口并抽象通用客服弹窗组件
1. 抽离打手端通用联系客服弹窗组件 WorkerContactModal,统一维护微信二维码、打手账号/状态/余额回显、一键复制及沟通提示;
2. 抢单大厅移动端顶部状态栏(余额上限右侧首屏黄金位)新增精致微动效【联系客服】胶囊按钮,大幅缩短找客服沟通链路;
3. 抢单大厅桌面端头部操作栏同步补齐【联系客服】按钮,并在个人中心账号弹窗中统一复用 WorkerContactModal;
4. 适配移动端与超窄屏自适应响应式样式,完成前端59项测试与全量打包构建验证。
|
2026-09-21 12:37:38 +08:00 |
|
yml2213
|
a4f6f5ed9b
|
style(worker-portal): 优化打手端我要寄卖页面顶部卡片布局,轻量化重构首屏体验
1. 移除第 4 个占位无意义的「发布寄卖 苏尔南」卡片,核心指标精准收敛为待审核、履约中、已完成 3 大状态;
2. 新增 worker-consignment-kpi-grid 紧凑统计栏,移动端改为横向 3 列等宽平铺,高度由 320px+ 骤降至 52px,竖向空间节省超 80%;
3. 剔除卡片内无实际价值的长句副说明文案,改为上行轻量状态标签 + 下行醒目数字的微排版,支持点击秒切记录过滤;
4. 彻底解决移动端首屏被巨幅卡片占满导致表单下沉的问题,首屏直接呈现履约押金提示与规格选择,提升打手发布效率;
5. 前端 59 个自动化测试全绿,全套 check 与 build 100% 通过。
|
2026-09-21 12:31:03 +08:00 |
|
 yml2213andCursor
|
577e0eec02
|
fix(consignment): 内部现货履约文案对齐待加好友,并移除已废弃的指派打手路径
Co-authored-by: Cursor <cursoragent@cursor.com>
|
2026-09-21 12:04:30 +08:00 |
|
yml2213
|
29589bb9af
|
feat(worker-platform): 接单工单表格「打手」列支持展示寄售供货打手与现货资料
1. 新增 resolveConsignmentWorkerDisplay 工具函数,自动解析工单寄售现货与资料字段(供货打手、游戏角色昵称、游戏ID及联系方式);
2. 优化 renderWorkOrderWorkers 渲染逻辑:当工单为内部寄售现货履约时,打手列带「供货打手」或「内部现货」专属角标,主标题展示打手名/角色名,副标题拼接游戏ID与联系方式,解决原先显示为破折号 - 的问题;
3. 新增 consignment-worker-display 单元测试,更新寄卖库存优化现状与进度追踪文档,前端 57 个测试全绿并通过类型检查。
|
2026-09-21 11:40:25 +08:00 |
|
yml2213
|
c80bd0e337
|
fix(worker-platform): 修复寄售匹配工单详情中卖家上架截图丢失问题,并收敛手动匹配为内部现货直接履约
1. 修复文件归一化器 normalizeUploadedFiles 遇到纯字符串 URL 数组被误过滤为空的问题,自动提取 objectKey 并构造规范 UploadedFile 结构;
2. 优化工单展示映射器 resolveWorkOrderEffectiveBoosterMaterial:当工单资料中截图为空时,自动回退并加载寄售卖家上架/主页截图;
3. 前端管理后台详情抽屉与打手端详情抽屉支持读取 detailOrder.gift.boosterMaterial.screenshots 与 consignmentInfo.screenshots 降级兜底渲染;
4. 手动匹配弹窗与发布拦截弹窗全面收敛为单一标准的【内部现货直接履约】,废除旧版指派打手带来的重复扣押金与二次填资料问题;
5. 新增 consignment-booster-material 单元测试,更新寄卖库存优化现状与进度追踪文档,前后端全套测试通过。
|
2026-09-21 11:35:38 +08:00 |
|