diff --git a/admin/arco-design-pro-vite/src/views/safety/platform-config/index.vue b/admin/arco-design-pro-vite/src/views/safety/platform-config/index.vue
index 9e0f6a4..3cdcb85 100644
--- a/admin/arco-design-pro-vite/src/views/safety/platform-config/index.vue
+++ b/admin/arco-design-pro-vite/src/views/safety/platform-config/index.vue
@@ -724,7 +724,7 @@
-
+
@@ -1394,7 +1394,7 @@ const loadingTemplates = ref(false);
const wechatTemplates = ref(null);
const testMsgForm = reactive({
templateType: 'order_status',
- targetUserId: 1001,
+ targetUserId: null,
previewTitle: '订单履约开始提醒',
previewDetail: '您的同城生活陪伴订单已开始服务,请出示4位核销码完成现场确认。'
});
diff --git a/server/src/index.ts b/server/src/index.ts
index e358254..4ef7d6c 100644
--- a/server/src/index.ts
+++ b/server/src/index.ts
@@ -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 {
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,
diff --git a/server/tests/system-alerts-audit.spec.ts b/server/tests/system-alerts-audit.spec.ts
index 1ddf275..c109df1 100644
--- a/server/tests/system-alerts-audit.spec.ts
+++ b/server/tests/system-alerts-audit.spec.ts
@@ -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('请改用真实微信登录用户测试');
+ });
});