- 引入 resolveDouyinMashidaPublicOrigin 从已配置的 claimLinkBase/certLink 提取公网域名 - 更新后台面板 message_url 展示逻辑,即使从 localhost 访问也清晰呈现已配置的公网推送地址 - 向平台订阅推送时使用已解析的公网域名,避免错误推送 localhost - 补充前端单元测试并通过全量构建校验
136 lines
4.7 KiB
TypeScript
136 lines
4.7 KiB
TypeScript
/**
|
||
* 抖音码速达后台配置的纯展示 / 校验逻辑。
|
||
*
|
||
* 刻意写成纯函数:不依赖 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 ''
|
||
}
|