上一轮我把皮肤交付做成了「另建一套执行器 + 独立领取接口 + 只提交账号就结束」, 跟买家实际要走的过程不一致(真正的完成信号是「扫码绑定后的 UID == 买家填的 UID」), 而且核销还触发了老通知逻辑,给买家**又发了一条 /redeem** —— 买家收到两条链接。 本次按用户要求改回**完全复用仓库现成的 affiliate_dash 通道**(= 旧的快手皮肤自动发货)。 **删掉** - 执行器 `douyin_mashida_skin`(含 registry/types/planner/bootstrap/路由白名单注册) - `services/claim/claim-skin-service.ts`、`routes/claim-skin.ts`、`/api/v1/claim-skin` 挂载 - 迁移 096 的 `claim_submission_json` 全链路(task-repo / rows / inputs / task-detail / admin-read-service 的 claimSubmission),并新增 **097 兜底迁移**把该列 DROP 掉 - 顺带清掉上一轮专为已删接口加的死代码(utils/http.ts 的 buildMessageSuccessPayload 等) **改回复用** - `resolveOrderItemClaimLink`:通用解析(只看订单项、不看执行器)→ `buildConfiguredClaimUrl(claimLinkBase, token)`;多履约单元取 task id 最小并告警; **绝不回退** hash 形态的 `buildClaimUrl` - `cert_link` 与付款后那条**唯一**消息共用同一个解析结果 - **核销后不再给抖音买家发第二条消息**(`shouldNotifyBuyerAfterVerify` 只放行非抖音 provider) - 买家侧走既有流程:输 UID → 二维码 → 扫码绑定 → 比对 UID → delivered/REDEEMED; 在 delivered 分支 best-effort 核销平台券(幂等、失败只 warn,不拖慢买家响应) - 后台回显链接改按 **order.provider** 判断(抖音用 `claimLinkBase`) **SQL 守卫**:允许「删除历史迁移」,但强制要求「同时新增兜底迁移」配对 (修改历史迁移仍禁止),并把改动来源换成 `git status --porcelain`(否则未跟踪的新迁移看不见) `npm run check`:tests 671 / pass 668 / fail 0 / skipped 3(含新增的物品图回退用例)
75 lines
1.9 KiB
TypeScript
75 lines
1.9 KiB
TypeScript
import type { JsonObject } from '../../types/json.js'
|
|
import {
|
|
listFulfillmentProfileRequirements,
|
|
replaceFulfillmentProfileRequirements,
|
|
upsertFulfillmentProfile,
|
|
type FulfillmentProfileRequirementInput,
|
|
} from '../../repositories/fulfillment-profile-repo.js'
|
|
import { nowIso } from '../../utils/time.js'
|
|
|
|
type CoreProfile = {
|
|
profileKey: string
|
|
name: string
|
|
executorKey: string
|
|
requiresClaim: boolean
|
|
autoDispatch: boolean
|
|
inventoryStrategy: string
|
|
requirements: FulfillmentProfileRequirementInput[]
|
|
}
|
|
|
|
const CORE_PROFILES: CoreProfile[] = [
|
|
{
|
|
profileKey: 'manual_review',
|
|
name: '人工发货',
|
|
executorKey: 'manual_dispatch',
|
|
requiresClaim: false,
|
|
autoDispatch: false,
|
|
inventoryStrategy: 'static_pool',
|
|
requirements: [],
|
|
},
|
|
{
|
|
profileKey: 'affiliate_dash',
|
|
name: 'affiliate-dash 履约',
|
|
executorKey: 'affiliate_dash',
|
|
requiresClaim: true,
|
|
autoDispatch: false,
|
|
inventoryStrategy: 'external_platform',
|
|
requirements: [],
|
|
},
|
|
]
|
|
|
|
export async function ensureFulfillmentCatalogBootstrapped() {
|
|
await ensureCoreProfiles()
|
|
}
|
|
|
|
async function ensureCoreProfiles() {
|
|
const timestamp = nowIso()
|
|
const profileMap: JsonObject = {}
|
|
|
|
for (const profile of CORE_PROFILES) {
|
|
const saved = await upsertFulfillmentProfile({
|
|
profileKey: profile.profileKey,
|
|
name: profile.name,
|
|
executorKey: profile.executorKey,
|
|
requiresClaim: profile.requiresClaim,
|
|
autoDispatch: profile.autoDispatch,
|
|
inventoryStrategy: profile.inventoryStrategy,
|
|
configJson: '{}',
|
|
createdAt: timestamp,
|
|
updatedAt: timestamp,
|
|
})
|
|
|
|
if (!saved) {
|
|
continue
|
|
}
|
|
|
|
profileMap[profile.profileKey] = saved
|
|
const currentRequirements = await listFulfillmentProfileRequirements(saved.id)
|
|
if (currentRequirements.length !== profile.requirements.length) {
|
|
await replaceFulfillmentProfileRequirements(saved.id, profile.requirements, timestamp)
|
|
}
|
|
}
|
|
|
|
return profileMap
|
|
}
|