fix(theme): 修复门票页导航栏主题脱节与个人中心双身份卡片流光动画,加固活动参与人画像与大神隐私

这个提交包含在:
xcbyy
2026-10-23 09:05:32 +08:00
父节点 001f81eaca
当前提交 f082ae8276
共有 14 个文件被更改,包括 147 次插入 和 40 次删除

查看文件

@@ -836,6 +836,22 @@ export async function initDatabase(): Promise<void> {
// 检查并插入初始种子数据(若表为空)
await seedInitialDataIfEmpty(conn);
// 数据修复:历史订单 expert_id 指向已不存在的档案(如 101)时,按 companion_name 回填真实档案 id
// 避免完单结算时按旧的 101→1 兼容映射把钱结算给错误的大神
// 放在种子数据之后执行,保证全新数据库的种子订单也能被修复
try {
await conn.query(`
UPDATE orders o
JOIN companions c ON c.name = o.companion_name
SET o.expert_id = c.id
WHERE o.expert_id >= 100 AND o.expert_id < 200
AND o.expert_id NOT IN (SELECT id FROM companions)
AND o.companion_name IS NOT NULL AND o.companion_name <> ''
`);
} catch (_) {
// 表不存在或数据不满足条件:忽略
}
console.log('[MySQL] peiwan 数据库核心表及种子数据已完整就绪');
} finally {
conn.release();

查看文件

@@ -155,11 +155,13 @@ export class MysqlRepository {
const [ptRows]: any = await pool.query(`
SELECT
id, activity_id as activityId, user_id as userId,
pay_amount_cents as payAmountCents, verify_code as verifyCode,
checkin_status as checkinStatus, verified_at as verifiedAt,
verified_by as verifiedBy, created_at as createdAt
FROM participants
p.id, p.activity_id as activityId, p.user_id as userId,
u.nickname as nickname, u.avatar as avatar,
p.pay_amount_cents as payAmountCents, p.verify_code as verifyCode,
p.checkin_status as checkinStatus, p.verified_at as verifiedAt,
p.verified_by as verifiedBy, p.created_at as createdAt
FROM participants p
LEFT JOIN users u ON u.id = p.user_id
`);
return actRows.map((act: any) => {
@@ -172,6 +174,8 @@ export class MysqlRepository {
id: p.id,
activityId: p.activityId,
userId: p.userId,
nickname: p.nickname || '',
avatar: p.avatar || '',
payAmountCents: p.payAmountCents,
checkinStatus: Boolean(p.checkinStatus),
verifiedAt: p.verifiedAt,
@@ -220,13 +224,15 @@ export class MysqlRepository {
const [ptRows]: any = await pool.query(`
SELECT
id, activity_id as activityId, user_id as userId,
pay_amount_cents as payAmountCents, verify_code as verifyCode,
checkin_status as checkinStatus, verified_at as verifiedAt,
verified_by as verifiedBy, created_at as createdAt
FROM participants
WHERE activity_id = ?
ORDER BY created_at ASC
p.id, p.activity_id as activityId, p.user_id as userId,
u.nickname as nickname, u.avatar as avatar,
p.pay_amount_cents as payAmountCents, p.verify_code as verifyCode,
p.checkin_status as checkinStatus, p.verified_at as verifiedAt,
p.verified_by as verifiedBy, p.created_at as createdAt
FROM participants p
LEFT JOIN users u ON u.id = p.user_id
WHERE p.activity_id = ?
ORDER BY p.created_at ASC
`, [id]);
return {
@@ -872,7 +878,8 @@ export class MysqlRepository {
id, user_id as userId, name, avatar, gender, age, track, rank_name as \`rank\`,
hourly_rate as hourlyRate, voice_duration as voiceDuration,
voice_title as voiceTitle, voice_intro_url as voiceIntroUrl, rating, order_count as orderCount,
status, audit_status as auditStatus, COALESCE(reject_reason, '') as rejectReason,
status, (status = '接单中' AND last_active_at >= DATE_SUB(NOW(), INTERVAL 10 MINUTE)) as isOnline,
audit_status as auditStatus, COALESCE(reject_reason, '') as rejectReason,
id_card_masked as idCardMasked, tags, apply_time as applyTime
FROM companions
`;
@@ -893,6 +900,7 @@ export class MysqlRepository {
} catch (_) {}
return {
...r,
isOnline: Boolean(r.isOnline),
tags: tagsArr
};
});
@@ -914,7 +922,11 @@ export class MysqlRepository {
[nLat, nLng, id]
);
} else {
await pool.query('UPDATE companions SET last_active_at = NOW() WHERE id = ?', [id]);
// 本次没有有效定位:清空旧坐标,避免用过期位置参与 5km 匹配
await pool.query(
'UPDATE companions SET last_active_at = NOW(), latitude = NULL, longitude = NULL WHERE id = ?',
[id]
);
}
}
@@ -961,7 +973,6 @@ export class MysqlRepository {
const distanceKm = hasFrom && hasGeo ? haversineKm(fromLat, fromLng, lat as number, lng as number) : null;
return {
id: String(r.id),
userId: Number(r.userId) || 0,
nickname: r.name || '',
avatar: r.avatar || '',
gender: r.gender || '',
@@ -1622,8 +1633,8 @@ export class MysqlRepository {
FROM orders
WHERE (
buyer_id = ?
OR expert_id = ?
OR claimed_by = ?
OR expert_id IN (SELECT id FROM companions WHERE user_id = ?)
OR companion_name IN (SELECT name FROM companions WHERE user_id = ?)
)
AND ${statusWhere}
@@ -1642,8 +1653,8 @@ export class MysqlRepository {
FROM orders
WHERE (
buyer_id = ?
OR expert_id = ?
OR claimed_by = ?
OR expert_id IN (SELECT id FROM companions WHERE user_id = ?)
OR companion_name IN (SELECT name FROM companions WHERE user_id = ?)
)
AND ${statusWhere}
@@ -2359,7 +2370,11 @@ export class MysqlRepository {
if (raw <= 0) return { companionId: 0, userId: 0 };
const comp = await this.resolveCompanionRow(raw);
if (comp) {
return { companionId: Number(comp.id) || raw, userId: Number(comp.user_id) || raw };
// 未绑定登录用户的种子档案不能把「档案 id」当「用户 id」去匹配 claimed_by,避免 id 撞号
return {
companionId: Number(comp.id) || raw,
userId: comp.user_id != null ? Number(comp.user_id) : 0,
};
}
return { companionId: raw, userId: raw };
}
@@ -2394,7 +2409,7 @@ export class MysqlRepository {
}> {
const scope = await this.resolveExpertOrderScope(expertId);
const eId = scope.companionId || scope.userId;
const eUid = scope.userId || scope.companionId;
const eUid = scope.userId;
const now = new Date();
// 1. 近 7 天走势

查看文件

@@ -1276,7 +1276,8 @@ app.get('/api/companions', async (req: Request, res: Response) => {
const publicList = companions
.filter((c: any) => c.auditStatus === '已通过')
.map((c: any) => {
const { rejectReason, ...rest } = c;
// 公开目录不返回内部审核备注、用户 id 与证件脱敏串
const { rejectReason, userId, idCardMasked, ...rest } = c;
return rest;
});
res.json({
@@ -1360,7 +1361,7 @@ app.get('/api/companions/:id', async (req: Request, res: Response) => {
if (isVerifiedAdminRequest(req)) {
return res.json({ code: 20000, data: companion });
}
const { rejectReason, ...publicCompanion } = companion;
const { rejectReason, userId, idCardMasked, ...publicCompanion } = companion;
res.json({
code: 20000,
data: publicCompanion