feat(auth): 移除多角色体验账号直登,仅保留微信手机号授权登录

- 登录页删除体验账号卡片、切换按钮与全部 demo 登录/失败回退逻辑,微信授权失败只提示重试

- completeLogin 只接受服务端签发的 token,禁止无 token 的假登录

- 删除 /api/auth/demo-accounts、/api/auth/demo-login、loadDemoAccounts 与客户端 API

- 停止种子 demo_accounts 配置并清理历史配置行;117 套件/763 用例全绿;线上接口已 404
这个提交包含在:
xcbyy
2026-10-22 15:45:48 +08:00
父节点 e595defaaf
当前提交 fba6c5ac22
共有 7 个文件被更改,包括 124 次插入 和 407 次删除

查看文件

@@ -1352,22 +1352,9 @@ async function seedInitialDataIfEmpty(conn: any): Promise<void> {
`, [mapLocationConfigVal]);
}
// 体验账号(演示/评审):账号清单与展示文案全部落库,小程序端不再写死
const demoAccountsVal = JSON.stringify({
enabled: true,
accounts: [
{ userId: 1001, roleTag: 'host', roleDesc: '房主/局长', desc: '余额充足 · 发起项目制/AA组局' },
{ userId: 2001, roleTag: 'buyer', roleDesc: '普通买家', desc: '搭子报名 · 预约陪伴 · 核销码签到' },
{ userId: 2006, roleTag: 'expert', roleDesc: '金牌陪伴师', desc: '工作台接单 · 雷达抢单 · 现场核销' }
],
remark: '体验账号:仅此处配置的账号可走「体验登录」获取真实会话 token;清空 accounts 即关闭体验登录'
});
if (!existingKeys.has('demo_accounts')) {
await conn.query(`
INSERT INTO platform_configs (config_key, name, value_json, description, status)
VALUES ('demo_accounts', '体验账号配置', ?, '演示/评审用体验账号清单(含展示文案),关闭时清空 accounts', 1)
`, [demoAccountsVal]);
}
// 体验账号直登已下线:清理历史配置,避免残留测试账号清单
await conn.query("DELETE FROM platform_configs WHERE config_key = 'demo_accounts'");
if (existingKeys.has('platform_protocol')) {
const [protoRows]: any = await conn.query("SELECT value_json FROM platform_configs WHERE config_key = 'platform_protocol'");
if (protoRows && protoRows.length > 0) {

查看文件

@@ -3090,88 +3090,6 @@ app.post('/api/auth/wechat-phone', async (req: Request, res: Response) => {
}
});
// ===== 体验账号(演示/评审):清单与文案全部来自 DB 配置,小程序端不再写死 =====
async function loadDemoAccounts(): Promise<Array<{ userId: number; roleTag: string; roleDesc: string; desc: string }>> {
try {
const record = await repo.getPlatformConfigByKey('demo_accounts');
const value = (record && record.valueJson) || {};
if (value.enabled === false) return [];
const accounts = Array.isArray(value.accounts) ? value.accounts : [];
return accounts
.map((a: any) => ({
userId: Number(a.userId) || 0,
roleTag: String(a.roleTag || ''),
roleDesc: String(a.roleDesc || ''),
desc: String(a.desc || ''),
}))
.filter((a: any) => a.userId > 0)
.slice(0, 10);
} catch (e) {
logServerError('loadDemoAccounts', e);
return [];
}
}
app.get('/api/auth/demo-accounts', async (_req: Request, res: Response) => {
try {
const accounts = await loadDemoAccounts();
const list = [];
for (const acc of accounts) {
const u: any = await repo.getUserById(acc.userId);
if (!u) continue;
if (u.status === '已封禁' || u.status === '封禁') continue;
list.push({
userId: u.id,
nickname: u.nickname || `用户${u.id}`,
avatar: u.avatar || '',
roleTag: acc.roleTag,
roleDesc: acc.roleDesc,
desc: acc.desc,
});
}
res.json({ code: 20000, data: list });
} catch (err: any) {
res.status(500).json({ code: 50000, message: err.message });
}
});
// 体验登录:只对 DB 配置内的账号签发真实 token,避免"无 token 的假登录"造成的 401/身份错配
app.post('/api/auth/demo-login', rateLimit({ name: 'demo_login', windowMs: 60 * 1000, max: 20, key: rateWriteKey }), async (req: Request, res: Response) => {
try {
const userId = Number(req.body?.userId) || 0;
const accounts = await loadDemoAccounts();
if (!accounts.some((a) => a.userId === userId)) {
return res.status(403).json({ code: 40300, message: '该账号未开启体验登录' });
}
const user: any = await repo.getUserById(userId);
if (!user) {
return res.status(404).json({ code: 40400, message: '体验账号不存在或已被清理' });
}
if (user.status === '已封禁' || user.status === '封禁') {
return res.status(403).json({ code: 40300, message: '该体验账号已被封禁' });
}
const token = signUserToken(user.id);
res.json({
code: 20000,
data: {
userId: user.id,
user: {
id: user.id,
nickname: user.nickname,
avatar: user.avatar,
gender: user.gender,
phone: user.phone,
},
userToken: token,
token,
},
message: '体验登录成功',
});
} catch (err: any) {
res.status(500).json({ code: 50000, message: err.message || '体验登录异常' });
}
});
// 微信小程序 JSAPI 统一下单预支付接口 (支持真实商户号配置与沙盒收银台无缝切换)
app.post('/api/pay/wechat/prepay', rateLimit({ name: 'prepay', windowMs: 60 * 1000, max: 30, key: rateWriteKey }), async (req: Request, res: Response) => {
try {