fix(config): 占位凭证清理改为一次性(标记位保护),避免误清管理员填写的真实 AppID
- 历史占位 appId/appSecret 的清理由"每次启动执行"改为"仅首启执行一次",写入 wechat_pay_placeholder_cleaned 标记 - 此后即使管理员填写的 AppID 恰好等于旧占位值(本账真实 AppID),重启也不会被再次清空 - 「拉取小程序模板列表」缺凭证时错误提示改为可操作指引(指明填写位置) - 回归 87 套件/636 用例全绿;冒烟 5/5 验证 Phase A 首启清理、Phase B 重启保留
这个提交包含在:
@@ -1196,19 +1196,26 @@ async function seedInitialDataIfEmpty(conn: any): Promise<void> {
|
||||
try { curVal = JSON.parse(curVal); } catch (_) {}
|
||||
}
|
||||
if (curVal && typeof curVal === 'object') {
|
||||
let updated = false;
|
||||
// 历史版本曾在源码/DB 中内置占位 appId/appSecret,这里一次性清空,
|
||||
// 避免后端误用非本小程序的凭证(如订阅消息 40037、支付参数错乱)。
|
||||
if (curVal.appId === 'wx0c929f7e54024e67' || curVal.appId === 'wx_peiwan_miniapp') {
|
||||
curVal.appId = '';
|
||||
updated = true;
|
||||
}
|
||||
if (curVal.appSecret === '63a412f3f73af8e73f6923943bf9683c') {
|
||||
curVal.appSecret = '';
|
||||
updated = true;
|
||||
}
|
||||
if (updated) {
|
||||
await conn.query("UPDATE platform_configs SET value_json = ? WHERE config_key = 'wechat_pay_config'", [JSON.stringify(curVal)]);
|
||||
// 一次性清理:历史版本曾在源码/DB 内置占位 appId/appSecret。
|
||||
// 用标记位保证【仅执行一次】,此后绝不再改动管理员填写的值
|
||||
// (即使其恰好等于占位值,例如本账真实 AppID 就是该值,也不会被误清)。
|
||||
if (!existingKeys.has('wechat_pay_placeholder_cleaned')) {
|
||||
let updated = false;
|
||||
if (curVal.appId === 'wx0c929f7e54024e67' || curVal.appId === 'wx_peiwan_miniapp') {
|
||||
curVal.appId = '';
|
||||
updated = true;
|
||||
}
|
||||
if (curVal.appSecret === '63a412f3f73af8e73f6923943bf9683c') {
|
||||
curVal.appSecret = '';
|
||||
updated = true;
|
||||
}
|
||||
if (updated) {
|
||||
await conn.query("UPDATE platform_configs SET value_json = ? WHERE config_key = 'wechat_pay_config'", [JSON.stringify(curVal)]);
|
||||
}
|
||||
await conn.query(`
|
||||
INSERT INTO platform_configs (config_key, name, value_json, description, status)
|
||||
VALUES ('wechat_pay_placeholder_cleaned', '小程序凭证占位清理标记', '{"done":true}', '内部标记:仅用于一次性清理历史占位凭证,勿手动修改', 1)
|
||||
`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3920,7 +3920,10 @@ app.get('/api/admin/message/templates', async (_req: Request, res: Response) =>
|
||||
try {
|
||||
const cfg = await resolveWechatPayRuntimeConfig();
|
||||
if (!cfg.appId || !cfg.appSecret) {
|
||||
return res.json({ code: 40000, message: '小程序 appId/appSecret 未配置' });
|
||||
return res.json({
|
||||
code: 40000,
|
||||
message: '小程序 AppID/AppSecret 未配置:请在后台「平台配置 → 微信支付商户配置」填写本小程序的 AppID 与 32 位 AppSecret(或设置环境变量 WX_APP_ID/WX_APP_SECRET),保存后重试',
|
||||
});
|
||||
}
|
||||
const accessToken = await getWechatAccessToken(cfg.appId, cfg.appSecret);
|
||||
const data = await wechatHttpGetJson(
|
||||
|
||||
@@ -29,10 +29,11 @@ describe('第七十一批小程序凭证去硬编码闸门', () => {
|
||||
|
||||
it('建表种子为空值,并清理历史遗留的占位凭证', () => {
|
||||
expect(mysqlCode).toContain('appId: \'\',\n appSecret: \'\',');
|
||||
// 自愈逻辑由"写入占位值"改为"清空占位值"
|
||||
// 自愈逻辑由"写入占位值"改为"清空占位值",且用标记位保证只执行一次(不误清管理员后续填写的值)
|
||||
expect(mysqlCode).toContain("if (curVal.appId === 'wx0c929f7e54024e67'");
|
||||
expect(mysqlCode).toContain("if (curVal.appSecret === '63a412f3f73af8e73f6923943bf9683c')");
|
||||
expect(mysqlCode).toContain('curVal.appId = \'\'');
|
||||
expect(mysqlCode).toContain('wechat_pay_placeholder_cleaned');
|
||||
});
|
||||
|
||||
it('后台文案不再暴露具体 AppID', () => {
|
||||
|
||||
在新工单中引用
屏蔽一个用户