Files
order_site/apps/frontend/src/utils/douyin-mashida.ts
T
yml2213 73de90ea4f fix(admin): 平台推送地址优先回显已配置的公网域名
- 引入 resolveDouyinMashidaPublicOrigin 从已配置的 claimLinkBase/certLink 提取公网域名
- 更新后台面板 message_url 展示逻辑,即使从 localhost 访问也清晰呈现已配置的公网推送地址
- 向平台订阅推送时使用已解析的公网域名,避免错误推送 localhost
- 补充前端单元测试并通过全量构建校验
2026-10-10 20:18:23 +08:00

136 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 抖音码速达后台配置的纯展示 / 校验逻辑。
*
* 刻意写成纯函数:不依赖 React / antd,也不使用 `@/` 别名,只做 `import type`,
* 方便 `node --experimental-strip-types --test` 直接跑单测。
*/
import type { AdminDouyinMashidaConfig } from '../types/admin/platform-config'
/** 配置完整度检查所需的配置子集 */
type MissingConfigCheck = Pick<
AdminDouyinMashidaConfig,
'enabled' | 'uid' | 'hasSecret' | 'certLink'
>
/** 配置完整度检查的固定展示顺序 */
const MISSING_FIELD_LABELS = ['接入开关', '商户uid', '用户秘钥', '兑换链接'] as const
/** 本地/内网 host:命中即认为不会请求真实码速达 */
const LOCAL_DEV_HOSTS = new Set(['127.0.0.1', 'localhost', 'host.docker.internal'])
/**
* 判断 baseUrl 是否指向本地/内网地址(这类地址平台永远访问不到)。
*
* 容忍端口、结尾斜杠、大小写与缺失协议(如 `127.0.0.1:3000`)。
* 无法解析的地址一律视为非本地。
*/
export function isLocalDevBaseUrl(baseUrl: string): boolean {
const text = String(baseUrl ?? '').trim()
if (!text) return false
try {
const url = new URL(text.includes('://') ? text : `http://${text}`)
return LOCAL_DEV_HOSTS.has(url.hostname.toLowerCase())
} catch {
return false
}
}
/**
* 拼接平台侧需要配置的消息推送地址(容错结尾斜杠)。
* 例:`https://ks.khhao.com/` → `https://ks.khhao.com/api/v1/open/douyin-mashida/message`
*/
export function buildDouyinMashidaMessageUrl(origin: string): string {
const normalized = String(origin ?? '')
.trim()
.replace(/\/+$/, '')
return `${normalized}/api/v1/open/douyin-mashida/message`
}
/**
* 从已配置的 claimLinkBase 或 certLink 中提取公网 origin,未配置时回退到 fallbackOrigin。
* 例:`https://nonsecretly-posttracheal-amado.ngrok-free.dev/h5` → `https://nonsecretly-posttracheal-amado.ngrok-free.dev`
*/
export function resolveDouyinMashidaPublicOrigin(
claimLinkBase?: string,
certLink?: string,
fallbackOrigin = 'http://localhost',
): string {
const candidate = String(claimLinkBase || certLink || '').trim()
if (candidate && /^https?:\/\//i.test(candidate)) {
try {
const url = new URL(candidate)
return url.origin
} catch {
// 容错
}
}
return fallbackOrigin
}
/**
* 返回缺失项的中文名数组,顺序固定为:接入开关 / 商户uid / 用户秘钥 / 兑换链接。
* 全齐时返回空数组。
*/
export function describeMissingConfigFields(config: MissingConfigCheck): string[] {
const source = config ?? ({} as MissingConfigCheck)
const missing: string[] = []
if (!source.enabled) missing.push(MISSING_FIELD_LABELS[0])
if (!String(source.uid ?? '').trim()) missing.push(MISSING_FIELD_LABELS[1])
if (!source.hasSecret) missing.push(MISSING_FIELD_LABELS[2])
if (!String(source.certLink ?? '').trim()) missing.push(MISSING_FIELD_LABELS[3])
return missing
}
/**
* 面板预览用的示例订单号(与真实 16 位平台订单号形态一致)。
*/
const CLAIM_LINK_PREVIEW_TOKEN = '2628301923197355'
/**
* 拼一条领取链接预览:`claimLinkBase` 去尾斜杠 + '/' + token。
* 规则与后端拼装一致(后端用配置里的 `claimLinkBase`)。
* 基址为空返回空串(后端会回退到通用兑换链接)。
*/
export function buildDouyinMashidaClaimLinkPreview(
claimLinkBase: string,
token = CLAIM_LINK_PREVIEW_TOKEN,
): string {
const base = String(claimLinkBase ?? '')
.trim()
.replace(/\/+$/, '')
if (!base) return ''
return `${base}/${String(token ?? '').trim()}`
}
/**
* 领取链接基址的误配提示(无问题返回空串)。
*
* 重点拦 `#`:`#/claim/{token}` 是**打手**领取页(要绑游戏 UID),
* 皮肤买家链接必须是路径式,例如 https://你的域名/h5
*/
export function describeClaimLinkBaseWarning(claimLinkBase: string): string {
const text = String(claimLinkBase ?? '').trim()
if (!text) return ''
if (text.includes('#')) {
return '不要带 #:#/claim 是打手领取页,买家直充链接请用路径式,例如 https://你的域名/h5'
}
if (!/^https?:\/\//i.test(text)) {
return '请填完整地址(http:// 或 https:// 开头),例如 https://你的域名/h5'
}
return ''
}
/**
* 发消息模板提示(无问题返回空串):模板里没有 {link} 就等于没把领取链接给买家。
*/
export function describeMessageTemplateWarning(messageTemplate: string): string {
const text = String(messageTemplate ?? '').trim()
if (text && !text.includes('{link}')) {
return '模板里没有 {link} 占位符,买家将拿不到领取链接'
}
return ''
}