feat(subscribe): 新增拉取小程序真实模板列表诊断(定位40037)

- 新增 GET /api/admin/message/templates:调用 wxaapi/newtmpl/gettemplate 返回当前 appId(脱敏) 下真实模板列表,并标出后台已填但不在列表中的ID

- 测试推送 diagnostics 增加 appIdMasked,便于核对 appId

- 后台新增「拉取小程序模板列表(核对ID)」按钮与不匹配高亮提示

- 文档补充 40037 排查步骤;回归 86 套件/632 用例全绿
这个提交包含在:
xcbyy
2026-10-19 10:26:10 +08:00
父节点 cacc478d71
当前提交 584e526bca
共有 4 个文件被更改,包括 152 次插入 和 5 次删除

查看文件

@@ -650,9 +650,44 @@
</a-row>
<div style="margin-top: 16px">
<a-button type="primary" :loading="saving" @click="saveMessageTemplateConfig">
保存微信模板消息配置
</a-button>
<a-space>
<a-button type="primary" :loading="saving" @click="saveMessageTemplateConfig">
保存微信模板消息配置
</a-button>
<a-button :loading="loadingTemplates" @click="fetchWechatTemplates">
拉取小程序模板列表(核对ID)
</a-button>
</a-space>
</div>
<div v-if="wechatTemplates" style="margin-top: 16px">
<a-alert
:type="mismatchedTemplateIds.length > 0 ? 'error' : 'success'"
:title="mismatchedTemplateIds.length > 0
? `有 ${mismatchedTemplateIds.length} 个已填模板ID不属于该小程序(会导致 40037 invalid template_id)`
: `已填模板ID均匹配该小程序(appId: ${wechatTemplates.appIdMasked || '-'})`"
>
<div>小程序模板总数:{{ wechatTemplates.count ?? 0 }}</div>
<div v-if="mismatchedTemplateIds.length > 0">
不在列表中的已填ID:{{ mismatchedTemplateIds.join(';') }}
</div>
<div v-if="wechatTemplates.errcode">微信返回:[{{ wechatTemplates.errcode }}] {{ wechatTemplates.errmsg }}</div>
</a-alert>
<a-table
v-if="Array.isArray(wechatTemplates.templates) && wechatTemplates.templates.length"
style="margin-top: 12px"
:data="wechatTemplates.templates"
:pagination="false"
size="small"
:scroll="{ y: 260 }"
>
<template #columns>
<a-table-column title="模板标题" data-index="title" :width="180" />
<a-table-column title="模板ID" data-index="templateId" :width="320" />
<a-table-column title="类型" data-index="type" :width="90" />
</template>
</a-table>
</div>
</a-form>
</a-card>
@@ -1031,7 +1066,7 @@
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue';
import { ref, reactive, computed, onMounted } from 'vue';
import { Message } from '@arco-design/web-vue';
import axios from 'axios';
@@ -1339,6 +1374,8 @@ const messageTemplateForm = reactive({
const testSending = ref(false);
const testResult = ref<any>(null);
const loadingTemplates = ref(false);
const wechatTemplates = ref<any>(null);
const testMsgForm = reactive({
templateType: 'order_status',
targetUserId: 1001,
@@ -1346,6 +1383,37 @@ const testMsgForm = reactive({
previewDetail: '您的同城生活陪伴订单已开始服务,请出示4位核销码完成现场确认。'
});
// 拉取当前小程序真实存在的订阅消息模板,用于核对后台填写的模板ID是否属于该 appId
const fetchWechatTemplates = async () => {
loadingTemplates.value = true;
try {
const res: any = await axios.get('/api/admin/message/templates');
const data = res?.data?.data || res?.data || res;
wechatTemplates.value = data;
if (data?.errcode && data.errcode !== 0) {
Message.error(`微信返回 [${data.errcode}] ${data.errmsg}`);
} else {
Message.success(`已拉取到 ${data?.count ?? 0} 个模板(appId: ${data?.appIdMasked || '-'})`);
}
} catch (err: any) {
Message.error(err.response?.data?.message || err.message || '拉取模板列表失败');
} finally {
loadingTemplates.value = false;
}
};
// 后台填写的4个模板ID中,哪些不在小程序模板列表里(即为 40037 的元凶)
const mismatchedTemplateIds = computed(() => {
const t = wechatTemplates.value;
if (!t || !Array.isArray(t.templates)) return [];
const existing = new Set(t.templates.map((x: any) => String(x.templateId)));
const cfg = t.configured || {};
return Object.entries(cfg)
.filter(([, id]) => id && !existing.has(String(id)))
.map(([key, id]) => `${key}: ${id}`);
});
const saveMessageTemplateConfig = () => {
saveConfig('message_template_rules', messageTemplateForm);
};