feat: 优化微信订阅消息体系并新增约课取消通知与开课前1小时提醒
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,9 +6,11 @@ import type {
|
||||
SubscriptionMessageRequestItem,
|
||||
SubscriptionMessageTemplate,
|
||||
SubscriptionMessageTemplateConfig,
|
||||
SubscriptionQuotaItem,
|
||||
SubscriptionQuotasResponse,
|
||||
UserProfileResponse,
|
||||
} from '@mp-pilates/shared'
|
||||
import { post } from './request'
|
||||
import { get, post } from './request'
|
||||
|
||||
type TemplateResult = SubscriptionMessageRequestItem['result'] | 'tmplIds empty' | 'err' | 'undefined'
|
||||
|
||||
@@ -163,11 +165,22 @@ function normalizeSubscribeResults(
|
||||
.filter((item): item is SubscriptionMessageRequestItem => item !== null)
|
||||
}
|
||||
|
||||
export const SUBSCRIBE_BUNDLE_BOOKING: SubscriptionMessageScene[] = [
|
||||
SubscriptionMessageScene.BOOKING_CREATED,
|
||||
SubscriptionMessageScene.CLASS_REMINDER,
|
||||
SubscriptionMessageScene.BOOKING_CANCELLED,
|
||||
]
|
||||
|
||||
export const SUBSCRIBE_BUNDLE_CANCEL: SubscriptionMessageScene[] = [
|
||||
SubscriptionMessageScene.BOOKING_CANCELLED,
|
||||
SubscriptionMessageScene.CLASS_REMINDER,
|
||||
]
|
||||
|
||||
/**
|
||||
* 在当前调用栈同步调起 `uni.requestSubscribeMessage`。
|
||||
* 在当前调用栈同步调起 `uni.requestSubscribeMessage`(支持多场景组合打包,最多 3 个模板)。
|
||||
* 微信要求授权框必须落在 tap / 支付 success 的同步栈里,因此这里不能先 `await`。
|
||||
*/
|
||||
export function requestSubscriptionMessage(scene: SubscriptionMessageScene): Promise<SubscriptionMessageRequestItem[]> {
|
||||
export function requestSubscriptionBundle(scenes: SubscriptionMessageScene[]): Promise<SubscriptionMessageRequestItem[]> {
|
||||
if (!isMpWeixin()) {
|
||||
return Promise.resolve([])
|
||||
}
|
||||
@@ -177,15 +190,28 @@ export function requestSubscriptionMessage(scene: SubscriptionMessageScene): Pro
|
||||
return Promise.reject(new Error('订阅消息模板尚未初始化,请重新进入页面后重试'))
|
||||
}
|
||||
|
||||
const templates = getTemplatesByScene(config, scene)
|
||||
// 按场景收集模板,去重 templateId,微信单次最多支持 3 个模板
|
||||
const templateMap = new Map<string, SubscriptionMessageTemplate>()
|
||||
for (const scene of scenes) {
|
||||
const list = getTemplatesByScene(config, scene)
|
||||
for (const tpl of list) {
|
||||
if (tpl.templateId && !templateMap.has(tpl.templateId)) {
|
||||
templateMap.set(tpl.templateId, tpl)
|
||||
}
|
||||
if (templateMap.size >= 3) break
|
||||
}
|
||||
if (templateMap.size >= 3) break
|
||||
}
|
||||
|
||||
const templates = Array.from(templateMap.values())
|
||||
if (templates.length === 0) {
|
||||
console.error('[subscribe] no templates matched scene', stringifyDebugPayload({ scene, config, debugContext: getSubscribeDebugContext() }))
|
||||
console.error('[subscribe] no templates matched bundle', stringifyDebugPayload({ scenes, config, debugContext: getSubscribeDebugContext() }))
|
||||
return Promise.resolve([])
|
||||
}
|
||||
|
||||
const templateIds = templates.map((item) => item.templateId)
|
||||
const debugContext = getSubscribeDebugContext()
|
||||
console.log('[subscribe] requestSubscribeMessage:start', stringifyDebugPayload({ scene, templateIds, templates, debugContext }))
|
||||
console.log('[subscribe] requestSubscriptionBundle:start', stringifyDebugPayload({ scenes, templateIds, templates, debugContext }))
|
||||
|
||||
return new Promise<SubscriptionMessageRequestItem[]>((resolve, reject) => {
|
||||
uni.requestSubscribeMessage({
|
||||
@@ -193,22 +219,78 @@ export function requestSubscriptionMessage(scene: SubscriptionMessageScene): Pro
|
||||
success: (res) => {
|
||||
const response = res as RequestSubscribeMessageSuccess
|
||||
const requests = normalizeSubscribeResults(templates, response)
|
||||
console.log('[subscribe] requestSubscribeMessage:success', stringifyDebugPayload({ scene, response, templateIds, debugContext }))
|
||||
console.log('[subscribe] requestSubscribeMessage:normalized', stringifyDebugPayload({ scene, result: response, requests, templateIds, debugContext }))
|
||||
console.log('[subscribe] requestSubscriptionBundle:success', stringifyDebugPayload({ scenes, response, templateIds, debugContext }))
|
||||
console.log('[subscribe] requestSubscriptionBundle:normalized', stringifyDebugPayload({ scenes, result: response, requests, templateIds, debugContext }))
|
||||
void reportResults(requests)
|
||||
.then(() => resolve(requests))
|
||||
.catch(reject)
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('[subscribe] requestSubscribeMessage:fail', stringifyDebugPayload({ scene, error: err, templateIds, debugContext }))
|
||||
reject(buildSubscribeError(err as RequestSubscribeMessageFail, scene, templateIds))
|
||||
console.error('[subscribe] requestSubscriptionBundle:fail', stringifyDebugPayload({ scenes, error: err, templateIds, debugContext }))
|
||||
reject(buildSubscribeError(err as RequestSubscribeMessageFail, scenes[0], templateIds))
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function requestSubscriptionMessage(scene: SubscriptionMessageScene): Promise<SubscriptionMessageRequestItem[]> {
|
||||
return requestSubscriptionBundle([scene])
|
||||
}
|
||||
|
||||
/**
|
||||
* 约课三合一组合授权:包含约课成功确认、开课前1小时提醒、课程取消通知
|
||||
* 一次点击,三个场景额度同时 +1!
|
||||
*/
|
||||
export function requestBookingBundleSubscriptionMessage(): Promise<SubscriptionMessageRequestItem[]> {
|
||||
return requestSubscriptionBundle(SUBSCRIBE_BUNDLE_BOOKING)
|
||||
}
|
||||
|
||||
/**
|
||||
* 兼容原有命名,直接升级为约课三合一组合授权
|
||||
*/
|
||||
export function requestBookingCreatedSubscriptionMessage(): Promise<SubscriptionMessageRequestItem[]> {
|
||||
return requestSubscriptionMessage(SubscriptionMessageScene.BOOKING_CREATED)
|
||||
return requestBookingBundleSubscriptionMessage()
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消预约时的组合授权:课程取消通知 + 下次上课提醒
|
||||
*/
|
||||
export function requestBookingCancelSubscriptionMessage(): Promise<SubscriptionMessageRequestItem[]> {
|
||||
return requestSubscriptionBundle(SUBSCRIBE_BUNDLE_CANCEL)
|
||||
}
|
||||
|
||||
/**
|
||||
* 上课前1小时提醒单项授权
|
||||
*/
|
||||
export function requestClassReminderSubscriptionMessage(): Promise<SubscriptionMessageRequestItem[]> {
|
||||
return requestSubscriptionMessage(SubscriptionMessageScene.CLASS_REMINDER)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户的各订阅场景额度水位
|
||||
*/
|
||||
export async function fetchUserSubscriptionQuotas(): Promise<SubscriptionQuotaItem[]> {
|
||||
try {
|
||||
const res = await get<SubscriptionQuotasResponse>('/user/subscription-messages/quotas')
|
||||
return res.quotas || []
|
||||
} catch (error) {
|
||||
console.warn('[subscribe] fetchUserSubscriptionQuotas failed', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 引导打开微信系统设置页,供用户恢复通知授权
|
||||
*/
|
||||
export function openSubscribeSettings(): Promise<boolean> {
|
||||
return new Promise<boolean>((resolve) => {
|
||||
uni.openSetting({
|
||||
success: (res) => {
|
||||
resolve(!!res.authSetting)
|
||||
},
|
||||
fail: () => resolve(false),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export async function requestAdminBookingSubscriptionCount(): Promise<UserProfileResponse | null> {
|
||||
|
||||
Reference in New Issue
Block a user