import type { WecomTemplateEventKey } from '@dukang/shared-types'; export type WecomTemplateDefault = { eventKey: WecomTemplateEventKey; title: string; body: string; handleLabel: string; }; /** 代码内默认文案;ensureDefaults 仅在库中无行时写入,不覆盖 HQ 已改 */ export const WECOM_PUSH_TEMPLATE_DEFAULTS: WecomTemplateDefault[] = [ { eventKey: 'order.paid', title: '订单支付成功', body: [ '**订单支付成功**', '订单号:{{orderNo}}', '实付:¥{{payAmount}}', '城市:{{cityName}}', '商品:{{skuSummary}}', '用户:{{phoneMasked}}', '时间:{{time}}', '[{{handleLabel}}]({{handleUrl}})', ].join('\n'), handleLabel: '去处理', }, { eventKey: 'redeem.success', title: '门店核销成功', body: [ '**门店核销成功**', '核销单号:{{redeemNo}}', '金额:¥{{amount}}', '门店:{{storeName}}', '渠道:{{channel}}', '时间:{{time}}', '[{{handleLabel}}]({{handleUrl}})', ].join('\n'), handleLabel: '去处理', }, { eventKey: 'store.audit_pending', title: '门店审核待处理', body: [ '**门店审核待处理 · {{action}}**', '门店:{{storeName}}', '城市:{{cityName}}', '合伙人:{{partnerLabel}}', '时间:{{time}}', '[{{handleLabel}}]({{handleUrl}})', ].join('\n'), handleLabel: '去处理', }, { eventKey: 'store.package_audit_pending', title: '套餐变更待审核', body: [ '**套餐变更待审核**', '门店:{{storeName}}', '城市:{{cityName}}', '提交端:{{submitter}}', '套餐条数:{{packageCount}}', '时间:{{time}}', '[{{handleLabel}}]({{handleUrl}})', ].join('\n'), handleLabel: '去处理', }, { eventKey: 'store.info_change_pending', title: '门店信息变更待审', body: [ '**门店信息变更待审**', '门店:{{storeName}}', '城市:{{cityName}}', '提交端:{{submitter}}', '变更字段:{{changedFields}}', '时间:{{time}}', '[{{handleLabel}}]({{handleUrl}})', ].join('\n'), handleLabel: '去处理', }, { eventKey: 'store.withdraw_pending', title: '门店提现待审', body: [ '**门店提现待审**', '门店:{{storeName}}', '提现单号:{{withdrawNo}}', '金额:¥{{amount}}', '明细笔数:{{payoutCount}}', '时间:{{time}}', '[{{handleLabel}}]({{handleUrl}})', ].join('\n'), handleLabel: '去处理', }, { eventKey: 'invoice.pending', title: '发票申请待开票', body: [ '**发票申请待开票**', '申请单号:{{invoiceNo}}', '订单号:{{orderNo}}', '金额:¥{{payAmount}}', '抬头:{{titleName}}', '用户:{{phoneMasked}}', '时间:{{time}}', '[{{handleLabel}}]({{handleUrl}})', ].join('\n'), handleLabel: '去处理', }, ]; export function getDefaultTemplate(eventKey: string): WecomTemplateDefault | undefined { return WECOM_PUSH_TEMPLATE_DEFAULTS.find((t) => t.eventKey === eventKey); } /** 将 body 中的 {{key}} 替换为 vars;缺失置空 */ export function renderWecomTemplate( body: string, vars: Record, ): string { return body.replace(/\{\{(\w+)\}\}/g, (_m, key: string) => { const v = vars[key]; if (v == null) return ''; return String(v); }); } /** * HQ 后台公网根地址(无尾斜杠)。 * 优先 HQ_ADMIN_PUBLIC_URL;未配时按 WECOM_ALERT_ENV_LABEL / NODE_ENV 回退, * 避免相对路径被企微解析成 http://orders/... 这类无效链接。 */ export function resolveHqAdminPublicBase(): string { const configured = (process.env.HQ_ADMIN_PUBLIC_URL || '').trim().replace(/\/$/, ''); if (configured) return configured; const label = (process.env.WECOM_ALERT_ENV_LABEL || process.env.NODE_ENV || '') .trim() .toLowerCase(); if (label === 'production' || label === 'prod') { return 'https://admin.dukanghaoke.com'; } if (label === 'staging' || label === 'test' || label === 'testing') { return 'https://admin-test.dukanghaoke.com'; } // local / development / 未识别:本机 admin-web 默认端口 return 'http://localhost:5175'; } export function buildHqHandleUrl(pathWithQuery: string): string { const base = resolveHqAdminPublicBase(); let path = (pathWithQuery || '').trim(); if (!path) return base; if (!path.startsWith('/')) path = `/${path}`; return `${base}${path}`; }