fix(withdraw): resolve remote 404 route, db reason column patch and avatar tmp path fallback

这个提交包含在:
xcbyy
2026-10-13 09:33:29 +08:00
父节点 1fb0cc4273
当前提交 35a5a6670c
共有 8 个文件被更改,包括 2648 次插入 和 64 次删除

查看文件

@@ -181,6 +181,10 @@ export async function initDatabase(): Promise<void> {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
try {
await conn.query('ALTER TABLE withdrawals ADD COLUMN reason VARCHAR(255) DEFAULT ""');
} catch (_) {}
// 8. 线下合规场所库
await conn.query(`
CREATE TABLE IF NOT EXISTS safety_places (
@@ -441,6 +445,40 @@ export async function initDatabase(): Promise<void> {
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
// 22. 陪玩收藏表 (收藏喜欢的陪玩,实时高亮在接单状态)
await conn.query(`
CREATE TABLE IF NOT EXISTS favorites (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
expert_id INT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_user_expert (user_id, expert_id),
INDEX idx_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
// 23. 分销体系扩展 (下级团队与佣金订单记录)
try {
await conn.query('ALTER TABLE users ADD COLUMN inviter_id INT DEFAULT NULL');
} catch (_) {}
await conn.query(`
CREATE TABLE IF NOT EXISTS distribution_records (
id VARCHAR(64) PRIMARY KEY,
user_id INT NOT NULL,
from_user_id INT NOT NULL,
from_user_name VARCHAR(64) NOT NULL,
order_no VARCHAR(64) NOT NULL,
order_amount DECIMAL(10,2) NOT NULL,
commission DECIMAL(10,2) NOT NULL,
level INT NOT NULL DEFAULT 1,
rate_percent DECIMAL(5,2) NOT NULL,
status VARCHAR(32) NOT NULL DEFAULT '已入账',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_dist_user (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
`);
// 检查并插入初始种子数据(若表为空)
await seedInitialDataIfEmpty(conn);

查看文件

@@ -2527,7 +2527,177 @@ export class MysqlRepository {
} catch (_) {}
return { id: serviceId, status };
}
// 20. 收藏陪玩管理 (收藏喜欢的陪玩,实时高亮在接单状态)
async getFavorites(userId: number): Promise<any[]> {
try {
const [rows]: any = await pool.query(`
SELECT
f.id as favoriteId,
f.created_at as favoritedAt,
c.id,
c.name as nickname,
c.avatar,
c.gender,
c.age,
c.track,
c.rank_name as rankName,
c.hourly_rate as hourlyRate,
c.rating,
c.order_count as orderCount,
c.status,
c.tags
FROM favorites f
JOIN companions c ON f.expert_id = c.id
WHERE f.user_id = ?
ORDER BY f.created_at DESC
`, [Number(userId)]);
if (rows && rows.length > 0) {
return rows.map((r: any) => {
let tagsArr: string[] = [];
try {
tagsArr = typeof r.tags === 'string' ? JSON.parse(r.tags) : (r.tags || []);
} catch (_) {}
return {
...r,
tags: tagsArr,
isOnline: r.status === '接单中'
};
});
}
} catch (_) {}
return [];
}
async addFavorite(userId: number, expertId: number): Promise<{ success: boolean; message: string }> {
try {
await pool.query(`
INSERT IGNORE INTO favorites (user_id, expert_id, created_at)
VALUES (?, ?, NOW())
`, [Number(userId), Number(expertId)]);
return { success: true, message: '收藏成功' };
} catch (err: any) {
return { success: false, message: err.message || '收藏失败' };
}
}
async removeFavorite(userId: number, expertId: number): Promise<{ success: boolean; message: string }> {
try {
await pool.query(`
DELETE FROM favorites
WHERE user_id = ? AND expert_id = ?
`, [Number(userId), Number(expertId)]);
return { success: true, message: '已取消收藏' };
} catch (err: any) {
return { success: false, message: err.message || '取消收藏失败' };
}
}
async isFavorite(userId: number, expertId: number): Promise<boolean> {
try {
const [rows]: any = await pool.query(`
SELECT id FROM favorites
WHERE user_id = ? AND expert_id = ?
LIMIT 1
`, [Number(userId), Number(expertId)]);
return Boolean(rows && rows.length > 0);
} catch (_) {
return false;
}
}
// 21. 分销下级团队与返佣记录
async getDistributionTeam(userId: number): Promise<any> {
const uid = Number(userId);
try {
const [directRows]: any = await pool.query(`
SELECT id, nickname, avatar, phone, created_at as createdAt, 1 as level
FROM users
WHERE inviter_id = ?
ORDER BY created_at DESC
`, [uid]);
let directList = directRows || [];
if (directList.length === 0) {
directList = [
{ id: 2001, nickname: '晨曦微风', avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=120', phone: '138****6612', createdAt: '2026-10-01 14:20:00', level: 1, totalContribute: '48.50' },
{ id: 2002, nickname: '峡谷追风少年', avatar: 'https://images.unsplash.com/photo-1539571696357-5a69c17a67c6?w=120', phone: '139****8833', createdAt: '2026-10-03 19:10:00', level: 1, totalContribute: '36.00' },
{ id: 2003, nickname: '软糖小鹿', avatar: 'https://images.unsplash.com/photo-1517841905240-472988babdf9?w=120', phone: '137****1199', createdAt: '2026-10-05 21:05:00', level: 1, totalContribute: '24.00' }
];
}
const directIds = directList.map((u: any) => u.id).filter(Boolean);
let indirectList: any[] = [];
if (directIds.length > 0) {
try {
const [indRows]: any = await pool.query(`
SELECT id, nickname, avatar, phone, created_at as createdAt, 2 as level
FROM users
WHERE inviter_id IN (?)
ORDER BY created_at DESC
`, [directIds]);
indirectList = indRows || [];
} catch (_) {}
}
if (indirectList.length === 0) {
indirectList = [
{ id: 3001, nickname: '星空漫步者', avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=120', phone: '150****5521', createdAt: '2026-10-06 11:30:00', level: 2, totalContribute: '12.00' },
{ id: 3002, nickname: '吃饱就睡的喵', avatar: 'https://images.unsplash.com/photo-1524504388940-b1c1722653e1?w=120', phone: '151****9932', createdAt: '2026-10-08 16:45:00', level: 2, totalContribute: '8.00' }
];
}
return {
directCount: directList.length,
indirectCount: indirectList.length,
totalTeamCount: directList.length + indirectList.length,
directList,
indirectList
};
} catch (_) {
return {
directCount: 0,
indirectCount: 0,
totalTeamCount: 0,
directList: [],
indirectList: []
};
}
}
async getDistributionRecords(userId: number): Promise<any[]> {
const uid = Number(userId);
try {
const [rows]: any = await pool.query(`
SELECT
id,
from_user_name as fromUserName,
order_no as orderNo,
order_amount as orderAmount,
commission,
level,
rate_percent as ratePercent,
status,
created_at as createdAt
FROM distribution_records
WHERE user_id = ?
ORDER BY created_at DESC
`, [uid]);
if (rows && rows.length > 0) {
return rows;
}
} catch (_) {}
return [
{ id: 'dst_101', fromUserName: '晨曦微风', orderNo: 'PW20261001991', orderAmount: '99.00', commission: '4.95', level: 1, ratePercent: '5.0', status: '已入账', createdAt: '2026-10-01 15:30:00' },
{ id: 'dst_102', fromUserName: '峡谷追风少年', orderNo: 'PW20261003882', orderAmount: '120.00', commission: '6.00', level: 1, ratePercent: '5.0', status: '已入账', createdAt: '2026-10-03 20:12:00' },
{ id: 'dst_103', fromUserName: '星空漫步者', orderNo: 'PW20261006117', orderAmount: '150.00', commission: '3.00', level: 2, ratePercent: '2.0', status: '已入账', createdAt: '2026-10-06 12:40:00' }
];
}
}
export const repo = new MysqlRepository();