站长要求全站一致(小程序端已单独提交)。本次只改**用户可见文案与默认值**: - 服务端默认值/兜底:system-settings-defaults、admin/system-settings(GET/POST)、 site-config.get、utils/siteUtils —— 原来是 'VoiceHub' / '校园广播站点歌系统' - 用户可见通知文案:MeoW 绑定验证码与标题、MeoW 测试通知、微信订阅测试通知 - 2FA 应用名(authenticator.keyuri)、WebAuthn rpName(出现在系统密钥弹窗里) - 网页版界面:首页副标题、后台侧栏品牌名、登录/注册/找回/改密标题与文案、 年度回顾(IntroSlide / ShareCard / 页面标题)、用户教程、通知发送预览、 API 密钥与数据库导入说明、酷狗 sidecar 说明、打印页脚与歌单卡片页脚、Logo alt **刻意不动**:localStorage/sessionStorage 的 `voicehub_*` 键、`voicehub:` 前缀的 STORAGE_KEY 与缓存/Redis 前缀、`window.voiceHubPlayer` 之类代码标识、MeoW 的 User-Agent、以及上游项目链接 `github.com/laoshuikaixue/VoiceHub`(那是对原项目的署名)。 注意:站点名**数据**(systemSettings.siteTitle)仍是你后台里设置的「宝一外校园点歌」, 代码只保证默认值与文案一致;要让全站显示完整名称,请在后台「站点配置 → 站点标题」 改成「宝一外校园点歌系统」。
93 lines
3.2 KiB
TypeScript
93 lines
3.2 KiB
TypeScript
import {
|
||
buildTemplateData,
|
||
getMiniAccessToken,
|
||
getSubscribeTemplates,
|
||
getWechatOpenid,
|
||
sendRawSubscribe
|
||
} from '~~/server/services/wechatSubscribeService'
|
||
|
||
/**
|
||
* 微信订阅消息的**自测接口**(仅超级管理员)—— 站长部署完用它「试一下」。
|
||
*
|
||
* 一次调用能看出四件事:
|
||
* 1. access_token 能不能拿到(appid / secret 是否有效)
|
||
* 2. 目标用户有没有绑微信(有没有 openid;纯密码用户必然是 null)
|
||
* 3. 模板字段名 / 长度对不对(微信会回 47003 / 43101 之类的错误码)
|
||
* 4. 微信返回的原文
|
||
*
|
||
* body(都可省):
|
||
* { userId?: number, kind?: string, openid?: string, templateId?: string, data?: object }
|
||
* · 默认给自己发、用配置里的第一个模板
|
||
* · 想直接指定 openid / 模板 ID / data 也可以(用于排查)
|
||
*/
|
||
export default defineEventHandler(async (event) => {
|
||
const user = event.context.user
|
||
if (!user || user.role !== 'SUPER_ADMIN') {
|
||
throw createError({ statusCode: 403, message: '只有超级管理员可以测试微信订阅消息' })
|
||
}
|
||
|
||
const body = await readBody(event)
|
||
const templates = getSubscribeTemplates()
|
||
const kinds = Object.keys(templates)
|
||
const kind = body?.kind || kinds[0] || ''
|
||
const conf = kind ? templates[kind] : null
|
||
const templateId = body?.templateId || conf?.id || ''
|
||
const targetUserId = Number(body?.userId) || Number(user.id)
|
||
|
||
const token = await getMiniAccessToken()
|
||
const openid = body?.openid || (await getWechatOpenid(targetUserId))
|
||
|
||
if (!templateId) {
|
||
return {
|
||
ok: false,
|
||
reason: 'no-template',
|
||
hint: '服务器环境变量 WECHAT_MINI_SUBSCRIBE_TEMPLATES 没配置模板 ID(见 wechatSubscribeService.ts 头部注释)',
|
||
configuredKinds: kinds,
|
||
tokenOk: !!token,
|
||
openid: openid ? openid.slice(0, 6) + '****' : null
|
||
}
|
||
}
|
||
|
||
if (!openid) {
|
||
return {
|
||
ok: false,
|
||
reason: 'no-wechat-openid',
|
||
hint: '该用户没有绑定微信(userIdentities 里没有 provider=wechat-mp 的记录),微信发不到他那里',
|
||
kind: kind,
|
||
templateId: templateId,
|
||
tokenOk: !!token,
|
||
targetUserId: targetUserId
|
||
}
|
||
}
|
||
|
||
const data =
|
||
body?.data && typeof body.data === 'object'
|
||
? body.data
|
||
: buildTemplateData(conf || { id: templateId }, {
|
||
songTitle: '测试歌曲',
|
||
message: '这是一条来自宝一外校园点歌系统的测试通知'
|
||
})
|
||
|
||
const result = await sendRawSubscribe(openid, templateId, data)
|
||
|
||
return {
|
||
ok: result.ok,
|
||
errcode: result.errcode,
|
||
errmsg: result.errmsg,
|
||
hint:
|
||
result.errcode === 0
|
||
? '发送成功,去微信里看「服务通知」'
|
||
: result.errcode === 43101
|
||
? '用户没有可用授权(一次性订阅:需要用户在小程序里点过「开启微信通知」才行)'
|
||
: '看 errmsg:47003 = 模板字段名/长度不对;40003 = openid 不对;40001 = token 失效',
|
||
configuredKinds: kinds,
|
||
kind: kind,
|
||
templateId: templateId,
|
||
targetUserId: targetUserId,
|
||
openid: openid.slice(0, 6) + '****',
|
||
tokenOk: !!token,
|
||
data: data,
|
||
raw: result.raw
|
||
}
|
||
})
|