feat(sms): 新增节点短信提醒(审核未通过/被租用需辅助/上架未成交)

This commit is contained in:
yml2213
2026-10-14 14:57:52 +08:00
parent efa0aa4ffd
commit 93e102b7c0
38 changed files with 3228 additions and 70 deletions
@@ -335,6 +335,8 @@ export interface DistributionSiteSMSConfig {
endpoint: string
sign_name: string
login_template_code: string
/** 站点自有的节点短信模板 CODE(key 为节点标识),未配置的节点回退主站模板。 */
business_templates: Record<string, string>
}
export interface UpdateDistributionSiteSMSConfigPayload {
@@ -345,6 +347,8 @@ export interface UpdateDistributionSiteSMSConfigPayload {
endpoint?: string
sign_name?: string
login_template_code?: string
/** 整体覆盖站点节点模板映射,传空对象即清空并回退主站模板。 */
business_templates?: Record<string, string>
}
export interface TestDistributionSiteSMSConfigPayload {
@@ -0,0 +1,116 @@
/**
* 节点短信模板配置:与后端 internal/modules/sms/nodes.go 登记一致。
* 阿里云要求模板提前报备审核,因此这里只维护模板 CODE,文案由平台模板决定。
*/
export type SmsNodeCode =
| 'listing_review_rejected'
| 'order_handoff_assist'
| 'listing_stale_price_suggestion'
export interface SmsNodeMeta {
code: SmsNodeCode
name: string
recipient: string
description: string
/** 报备模板的参考 CODE,仅作提示,真实值以配置为准。 */
templateCode: string
/** 报备模板的参考文案,仅供识别节点,实际文案由平台模板决定。 */
sample: string
supportsStaleDays: boolean
}
export const smsNodeMetas: SmsNodeMeta[] = [
{
code: 'listing_review_rejected',
name: '账号审核未通过',
recipient: '号主',
description: '号主上架的账号被后台审核拒绝时,提醒登录网站修改后重新上架。',
templateCode: 'SMS_512520993',
sample: '板板,您在我们商行上架的账号审核未通过,请登录网站进行修改重新上架',
supportsStaleDays: false,
},
{
code: 'order_handoff_assist',
name: '账号被租用需辅助登录',
recipient: '号主',
description: '订单支付成功进入待交接后,提醒号主及时辅助登录,避免订单被强制结束。',
templateCode: 'SMS_512540945',
sample:
'板板,您的订单已有人下单,下单方需要辅助登录,请及时留意平台信息!长时间未辅助平台会强制结束订单',
supportsStaleDays: false,
},
{
code: 'listing_stale_price_suggestion',
name: '上架多日未成交降价建议',
recipient: '号主',
description: '账号上架满指定天数仍未被租用,建议号主适当调整比例,加快出租。',
templateCode: 'SMS_512620985',
sample:
'板板,您的三角洲账号上架三天还未成功出售,建议您比例适当让一点,比例越高,出租越快、匹配越顺。具体调多少按自己心理价位定',
supportsStaleDays: true,
},
]
export interface SmsNodeConfig {
enabled: boolean
template_code: string
/** 仅「上架多日未成交降价建议」使用:上架满多少天仍未成交才提醒。 */
stale_days?: number
}
export interface SmsBusinessTemplatesConfig {
enabled: boolean
nodes: Record<string, SmsNodeConfig>
}
export const defaultSmsBusinessTemplatesConfig: SmsBusinessTemplatesConfig = {
// 与后端默认值一致:总开关关闭,避免未配置模板就产生短信费用。
enabled: false,
nodes: {
listing_review_rejected: { enabled: true, template_code: '' },
order_handoff_assist: { enabled: true, template_code: '' },
listing_stale_price_suggestion: { enabled: true, template_code: '', stale_days: 3 },
},
}
export function parseSmsBusinessTemplatesConfig(raw: string): SmsBusinessTemplatesConfig {
const parsed = parseRawConfig(raw)
const nodes: Record<string, SmsNodeConfig> = {}
for (const meta of smsNodeMetas) {
const fallback = defaultSmsBusinessTemplatesConfig.nodes[meta.code]
const incoming = parsed.nodes?.[meta.code]
const node: SmsNodeConfig = {
enabled:
typeof incoming?.enabled === 'boolean' ? incoming.enabled : (fallback?.enabled ?? true),
template_code:
typeof incoming?.template_code === 'string' ? incoming.template_code.trim() : '',
}
if (meta.supportsStaleDays) {
node.stale_days = positiveInteger(incoming?.stale_days, fallback?.stale_days ?? 3)
}
nodes[meta.code] = node
}
return {
enabled: typeof parsed.enabled === 'boolean' ? parsed.enabled : false,
nodes,
}
}
export function cloneSmsBusinessTemplatesConfig(
config: SmsBusinessTemplatesConfig
): SmsBusinessTemplatesConfig {
return JSON.parse(JSON.stringify(config)) as SmsBusinessTemplatesConfig
}
function parseRawConfig(raw: string): Partial<SmsBusinessTemplatesConfig> {
try {
return JSON.parse(raw) as Partial<SmsBusinessTemplatesConfig>
} catch {
return {}
}
}
function positiveInteger(value: unknown, fallback: number) {
const number = Number(value)
return Number.isInteger(number) && number > 0 ? number : fallback
}