fix(subscribe): 识别演示账号假 openid,完善订阅消息测试诊断

- 新增 isPlausibleWechatOpenid:wx_openid_ 前缀/长度异常的种子 openid 一律视为无效,notifyBySubscribe 与内容安全 openid 均跳过,避免 40003 无效 openid 噪音

- 后台模拟推送返回 openidValid 诊断,并对演示账号给出「请改用真实微信登录用户测试」的明确提示;表单默认 UID 清空

- 115 套件/751 用例全绿;实测 2010 已走到微信侧(43101 用户未授权,需小程序内点击允许)
这个提交包含在:
xcbyy
2026-10-22 10:05:53 +08:00
父节点 65ddc7301a
当前提交 abf1f5afbb
共有 3 个文件被更改,包括 32 次插入 和 9 次删除

查看文件

@@ -724,7 +724,7 @@
</a-col>
<a-col :span="8">
<a-form-item label="目标测试用户 ID (UID)">
<a-input-number v-model="testMsgForm.targetUserId" placeholder="例如:1001" :min="1" />
<a-input-number v-model="testMsgForm.targetUserId" placeholder="请输入真实微信登录用户 UID(如 2010)" :min="1" />
</a-form-item>
</a-col>
<a-col :span="8">
@@ -1394,7 +1394,7 @@ const loadingTemplates = ref(false);
const wechatTemplates = ref<any>(null);
const testMsgForm = reactive({
templateType: 'order_status',
targetUserId: 1001,
targetUserId: null,
previewTitle: '订单履约开始提醒',
previewDetail: '您的同城生活陪伴订单已开始服务,请出示4位核销码完成现场确认。'
});

查看文件

@@ -250,12 +250,20 @@ async function assertUserNotBanned(userId: number, actionName: string = '操作'
}
}
// 读取用户微信 openid(用于微信官方内容安全 security.msgSecCheck 溯源),失败返回 undefined
// 微信 openid 形如 28 位字母数字(o 开头);演示种子数据里的 wx_openid_1001 不是真实 openid
function isPlausibleWechatOpenid(openid: any): boolean {
const value = String(openid || '').trim();
if (!value) return false;
if (/^wx_openid_/i.test(value)) return false;
return /^[A-Za-z0-9_-]{20,64}$/.test(value);
}
// 读取用户微信 openid(用于微信官方内容安全 security.msgSecCheck 溯源),失败或非真实 openid 返回 undefined
async function resolveUserOpenid(userId: number): Promise<string | undefined> {
try {
const [rows]: any = await pool.query('SELECT wechat_openid FROM users WHERE id = ? LIMIT 1', [Number(userId)]);
const openid = rows && rows[0] && rows[0].wechat_openid;
return openid ? String(openid) : undefined;
return isPlausibleWechatOpenid(openid) ? String(openid) : undefined;
} catch (_) {
return undefined;
}
@@ -2789,7 +2797,10 @@ async function notifyBySubscribe(
const [uRows]: any = await pool.query('SELECT wechat_openid FROM users WHERE id = ? LIMIT 1', [uid]);
const openid = uRows && uRows[0] && uRows[0].wechat_openid;
if (!openid) return;
if (!isPlausibleWechatOpenid(openid)) {
logServerError('notifyBySubscribe', new Error(`订阅消息[${templateKey}]跳过:用户 ${uid} 没有有效的微信 openid`));
return;
}
const accessToken = await getWechatAccessToken(appId, appSecret);
const result = await sendSubscribeMessageHttp(accessToken, {
@@ -4444,12 +4455,15 @@ app.post('/api/admin/message/test-send', async (req: Request, res: Response) =>
const cfg = await resolveWechatPayRuntimeConfig();
const [uRows]: any = await pool.query('SELECT wechat_openid FROM users WHERE id = ? LIMIT 1', [Number(targetUserId) || 0]);
const openid = uRows && uRows[0] && uRows[0].wechat_openid;
const openidRaw = uRows && uRows[0] && uRows[0].wechat_openid;
const openidValid = isPlausibleWechatOpenid(openidRaw);
const openid = openidValid ? String(openidRaw) : '';
const diagnostics = {
enablePush,
hasTemplateId: Boolean(templateId),
hasOpenid: Boolean(openid),
hasOpenid: Boolean(openidRaw),
openidValid,
hasAppId: Boolean(cfg.appId),
hasAppSecret: Boolean(cfg.appSecret),
appIdMasked: maskTail(cfg.appId),
@@ -4517,9 +4531,11 @@ app.post('/api/admin/message/test-send', async (req: Request, res: Response) =>
? '订阅消息总开关未开启'
: (!templateId
? `未配置「${typeLabels[tKey] || tKey}」模板ID${reasonDetail}${tKey === 'verifyCode' ? '(该类型为可选:核销码已随「订单状态提醒」的温馨提示下发)' : ''}`
: (!openid
: (!openidRaw
? `目标用户(ID=${targetUserId || 0})没有 wechat_openid(该用户需通过微信登录过小程序)`
: '小程序 appId/appSecret 未配置'));
: (!openidValid
? `目标用户(ID=${targetUserId || 0})的 openid 不是有效微信 openid(演示种子账号),请改用真实微信登录用户测试`
: '小程序 appId/appSecret 未配置')));
const result = {
templateType: tKey,
targetUserId: targetUserId || 1001,

查看文件

@@ -53,4 +53,11 @@ describe('第一百批系统告警与后台审计', () => {
expect(backupScript).toContain('gzip -t');
expect(backupScript).toContain("-mtime +14 -delete");
});
it('订阅消息必须识别演示账号的假 openid,避免 40003', () => {
expect(indexCode).toContain('function isPlausibleWechatOpenid');
expect(indexCode).toContain("if (/^wx_openid_/i.test(value)) return false;");
expect(indexCode).toContain('openidValid');
expect(indexCode).toContain('请改用真实微信登录用户测试');
});
});