fix(subscribe): 订阅消息未下发原因可见 + 小程序真实授权
- 后台测试推送:未就绪返回 mode=SIMULATED 明确原因 + 配置自检(模板ID/openid/appId/appSecret/总开关),不再把模拟显示为成功 - 占位符 tmpl_xxx 视为未配置(测试推送与订阅模板接口均过滤),微信错误码中文化(43101/47003/40037/40001 等) - 小程序「平台消息与通知」点击时真实调用 wx.requestSubscribeMessage 并回显授权状态,标签不再恒为已开启 - 管理端拦截器错误提示改用 message;文档补订阅消息排查顺序;新增回归护栏(86 套件/631 用例全绿)
这个提交包含在:
@@ -49,3 +49,42 @@ export function requestSubscribeFor(scene) {
|
||||
// 授权失败不影响主流程
|
||||
}
|
||||
}
|
||||
|
||||
/** 全部已配置的真实模板ID(过滤空值与 tmpl_ 占位符) */
|
||||
function allTmplIds() {
|
||||
if (!templates || templates.enablePush === false) return [];
|
||||
return ['orderStatusTemplateId', 'activityJoinTemplateId', 'withdrawSuccessTemplateId', 'verifyCodeTemplateId']
|
||||
.map((k) => templates[k])
|
||||
.filter((x) => x && !String(x).startsWith('tmpl_'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 主动请求全部订阅授权(用于「平台消息与通知」入口)。
|
||||
* 注意:wx.requestSubscribeMessage 必须在用户点击手势中同步调用;本函数不 await 任何网络请求。
|
||||
* 返回每项授权结果,便于界面给出真实提示(订阅消息为一次性订阅,每条授权仅可下发 1 条)。
|
||||
*/
|
||||
export function requestSubscribeAll() {
|
||||
return new Promise((resolve) => {
|
||||
try {
|
||||
if (typeof uni.requestSubscribeMessage !== 'function') {
|
||||
return resolve({ supported: false, tmplIds: [], accepted: [], rejected: [] });
|
||||
}
|
||||
const tmplIds = allTmplIds();
|
||||
if (tmplIds.length === 0) {
|
||||
return resolve({ supported: true, tmplIds: [], accepted: [], rejected: [], noTemplates: true });
|
||||
}
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds,
|
||||
success: (res) => {
|
||||
const accepted = tmplIds.filter((id) => res && res[id] === 'accept');
|
||||
const rejected = tmplIds.filter((id) => !res || res[id] !== 'accept');
|
||||
resolve({ supported: true, tmplIds, accepted, rejected, raw: res });
|
||||
},
|
||||
fail: (err) => resolve({ supported: true, tmplIds, accepted: [], rejected: tmplIds, error: err }),
|
||||
complete: () => {}
|
||||
});
|
||||
} catch (e) {
|
||||
resolve({ supported: true, tmplIds: [], accepted: [], rejected: [], error: e });
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
<view class="list-row btn-active" @click="openNotice">
|
||||
<image class="row-svg" :src="ICONS.bell" mode="aspectFit" />
|
||||
<text class="row-label">平台消息与通知</text>
|
||||
<text class="row-sub-tag">{{ noticeSubscribe ? '提醒开启' : '已静音' }}</text>
|
||||
<text class="row-sub-tag">{{ noticeSubscribe ? '已开启提醒' : '点击开启' }}</text>
|
||||
<image class="row-arrow-svg" :src="ICONS.chevronRight" mode="aspectFit" />
|
||||
</view>
|
||||
<view class="list-row btn-active" @click="openMyReportsModal">
|
||||
@@ -352,6 +352,7 @@ import { getCurrentThemeKey, setTheme, THEME_KEYS, applyThemeToNativeUI } from '
|
||||
import { getCurrentRole, setCurrentRole, ROLE_TYPES, toggleRole as toggleUserRole, isExpertCertified, getExpertCertStatus, setExpertCertStatus, CERT_STATUS } from '@/common/role.js';
|
||||
import { getNavBarInfo } from '@/common/nav.js';
|
||||
import { getUserProfile, rechargeBalance, withdrawCommission, getUserFinanceRecords, updateUserProfile, uploadImage, getRechargeTiers, getPlatformConfigs, applyExpertCert, getUserWithdrawals, getFavorites, removeFavorite, getDistributionTeam, getDistributionRecords, getMyReports, bindInviteCode } from '@/common/api.js';
|
||||
import { preloadSubscribeTemplates, requestSubscribeAll } from '@/common/subscribe.js';
|
||||
|
||||
const DEFAULT_GUEST_AVATAR = 'https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?w=200';
|
||||
const initialUserId = typeof uni !== 'undefined' ? (uni.getStorageSync('userId') || null) : null;
|
||||
@@ -382,7 +383,7 @@ const openMyReportsModal = () => {
|
||||
|
||||
// 系统与隐私设置
|
||||
const privacyHidePhone = ref(uni.getStorageSync('privacy_hide_phone') === '1');
|
||||
const noticeSubscribe = ref(uni.getStorageSync('notice_subscribe') !== '0');
|
||||
const noticeSubscribe = ref(uni.getStorageSync('notice_subscribe') === '1');
|
||||
const cacheSizeText = ref('1.25 MB');
|
||||
|
||||
// 分销推广管理
|
||||
@@ -628,6 +629,8 @@ onMounted(() => {
|
||||
loadUserProfile();
|
||||
loadPlatformConfigs();
|
||||
loadMyReports();
|
||||
// 预加载订阅消息模板ID,确保点击授权时无需等待网络(保留点击手势)
|
||||
preloadSubscribeTemplates();
|
||||
});
|
||||
|
||||
onShow(() => {
|
||||
@@ -639,7 +642,7 @@ onShow(() => {
|
||||
loadUserProfile();
|
||||
loadPlatformConfigs();
|
||||
privacyHidePhone.value = uni.getStorageSync('privacy_hide_phone') === '1';
|
||||
noticeSubscribe.value = uni.getStorageSync('notice_subscribe') !== '0';
|
||||
noticeSubscribe.value = uni.getStorageSync('notice_subscribe') === '1';
|
||||
if (isLoggedIn.value) {
|
||||
loadFavorites();
|
||||
loadMyReports();
|
||||
@@ -1163,6 +1166,9 @@ const contactService = () => {
|
||||
};
|
||||
|
||||
const openNotice = () => {
|
||||
// 同步触发微信订阅授权(必须保留点击手势,不能先 await 网络);再展示平台须知。
|
||||
const subResult = typeof uni.requestSubscribeMessage === 'function' ? requestSubscribeAll() : null;
|
||||
|
||||
const notice = platformConfigs.value.safety_notice || {};
|
||||
const title = notice.title || '平台官方公告与指引';
|
||||
let content = '【安全履约须知】\n1. 为保障搭子与陪伴师人身与财产安全,线下约局须在合规公开商业街区进行。\n2. 现场碰面后请出示 4 位数字核销码进行签到确认。\n3. 平台提供 7x24 小时争议仲裁与资金托管保障服务。';
|
||||
@@ -1177,6 +1183,20 @@ const openNotice = () => {
|
||||
confirmText: '我知道了',
|
||||
confirmColor: '#6366F1'
|
||||
});
|
||||
|
||||
if (subResult) {
|
||||
subResult.then((r) => {
|
||||
if (!r || !r.supported) return;
|
||||
if (r.noTemplates || !r.tmplIds || r.tmplIds.length === 0) {
|
||||
uni.showToast({ title: '后台未配置订阅消息模板', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const accepted = (r.accepted || []).length > 0;
|
||||
noticeSubscribe.value = accepted;
|
||||
uni.setStorageSync('notice_subscribe', accepted ? '1' : '0');
|
||||
uni.showToast({ title: accepted ? '已开启消息提醒' : '未获得提醒授权', icon: 'none' });
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const openFaq = () => {
|
||||
|
||||
在新工单中引用
屏蔽一个用户