feat(expert): full closure of expert revenue trends, multichannel withdrawals, service manage, review reply and post manage

这个提交包含在:
xcbyy
2026-10-13 09:13:06 +08:00
父节点 a004584109
当前提交 e867488d0d
共有 11 个文件被更改,包括 2565 次插入 和 103 次删除

查看文件

@@ -734,6 +734,16 @@ app.get('/api/expert/stats/:id', async (req: Request, res: Response) => {
}
});
// 大神日/周/月多维收益走势统计分析接口
app.get('/api/expert/stats/:id/trends', async (req: Request, res: Response) => {
try {
const trends = await repo.getExpertRevenueTrends(String(req.params.id));
res.json({ code: 20000, data: trends });
} catch (err: any) {
res.status(500).json({ code: 50000, message: err.message });
}
});
// 首页运营 Banner 列表 (小程序端读取启用状态)
app.get('/api/banners', async (_req: Request, res: Response) => {
@@ -1273,16 +1283,60 @@ app.post('/api/orders/:id/complete', async (req: Request, res: Response) => {
}
});
// 申请退款/售后
// 申请退款/售后 (写入仲裁工单:退款理由 + 详细说明 + 凭证截图)
app.post('/api/orders/:id/refund', async (req: Request, res: Response) => {
try {
const order = await repo.updateOrderStatus(String(req.params.id), '争议仲裁中');
const { reason, description, evidence } = req.body || {};
if (description) {
const sec = contentSecurityService.checkText(String(description));
if (!sec.pass) {
return res.status(400).json({ code: sec.code, message: sec.message });
}
}
const order = await repo.applyOrderRefund(String(req.params.id), { reason, description, evidence });
res.json({ code: 20000, data: order, message: '退款申诉已受理,进入争议仲裁流程' });
} catch (err: any) {
res.status(400).json({ code: 40000, message: err.message });
}
});
// 举报不良陪玩 (涉黄/涉赌/线下欺诈等,进入平台风控处置池)
app.post('/api/reports', async (req: Request, res: Response) => {
try {
const { reporterId, targetType, targetId, targetName, reason, description, evidence } = req.body || {};
if (!targetId || !reason) {
return res.status(400).json({ code: 40000, message: '举报对象与举报类型不能为空' });
}
if (description) {
const sec = contentSecurityService.checkText(String(description));
if (!sec.pass) {
return res.status(400).json({ code: sec.code, message: sec.message });
}
}
const report = await repo.createReport({
reporterId: Number(reporterId) || 0,
targetType,
targetId: String(targetId),
targetName,
reason: String(reason),
description,
evidence
});
res.json({ code: 20000, data: report, message: '举报已提交,平台将在 24 小时内核实处理' });
} catch (err: any) {
res.status(500).json({ code: 50000, message: err.message });
}
});
app.get('/api/reports', async (_req: Request, res: Response) => {
try {
const list = await repo.getReports();
res.json({ code: 20000, data: list });
} catch (err: any) {
res.status(500).json({ code: 50000, message: err.message });
}
});
app.get('/api/arbitrations', async (_req: Request, res: Response) => {
try {
const arbitrations = await repo.getArbitrations();
@@ -1520,12 +1574,19 @@ app.get('/api/users/:id', async (req: Request, res: Response) => {
}
});
// 7.2.1 个人资料修改 (真实修改 users 表 nickname, avatar, phone)
// 7.2.1 个人资料修改 (真实修改 users 表 nickname, avatar, phone, gender, signature)
app.post('/api/users/:id/profile', async (req: Request, res: Response) => {
try {
const userId = Number(req.params.id);
const { nickname, avatar, phone } = req.body;
const updated = await repo.updateUserProfile(userId, { nickname, avatar, phone });
const { nickname, avatar, phone, gender, signature } = req.body;
const textToCheck = `${nickname || ''} ${signature || ''}`.trim();
if (textToCheck) {
const sec = contentSecurityService.checkText(textToCheck);
if (!sec.pass) {
return res.status(400).json({ code: sec.code, message: sec.message });
}
}
const updated = await repo.updateUserProfile(userId, { nickname, avatar, phone, gender, signature });
res.json({
code: 20000,
data: updated,
@@ -1556,8 +1617,8 @@ app.post('/api/users/:id/recharge', async (req: Request, res: Response) => {
app.post('/api/users/:id/withdraw', async (req: Request, res: Response) => {
try {
const userId = Number(req.params.id);
const { amount, channel } = req.body;
const result = await repo.withdrawUserCommission(userId, Number(amount), channel);
const { amount, channel, account, realName, bankName } = req.body;
const result = await repo.withdrawUserCommission(userId, Number(amount), channel, { account, realName, bankName });
res.json({
code: 20000,
data: result,
@@ -1568,6 +1629,17 @@ app.post('/api/users/:id/withdraw', async (req: Request, res: Response) => {
}
});
// 用户提现记录列表接口 (状态跟踪:待审核、处理中、已打款、已驳回)
app.get('/api/users/:id/withdrawals', async (req: Request, res: Response) => {
try {
const userId = Number(req.params.id);
const list = await repo.getUserWithdrawals(userId);
res.json({ code: 20000, data: list });
} catch (err: any) {
res.status(500).json({ code: 50000, message: err.message });
}
});
// 7.2.4 用户个人财务流水列表
app.get('/api/users/:id/finance-records', async (req: Request, res: Response) => {
try {
@@ -1672,6 +1744,59 @@ app.get('/api/companions/:id/reviews', async (req: Request, res: Response) => {
}
});
// 大神感谢回复买家评价接口
app.post('/api/reviews/:id/reply', async (req: Request, res: Response) => {
try {
const reviewId = String(req.params.id);
const { replyContent } = req.body || {};
if (!replyContent || !replyContent.trim()) {
return res.status(400).json({ code: 40000, message: '回复内容不能为空' });
}
const secCheck = contentSecurityService.checkText(replyContent);
if (!secCheck.pass) {
return res.status(400).json({ code: secCheck.code, message: secCheck.message });
}
const result = await repo.replyOrderReview(reviewId, replyContent.trim());
res.json({ code: 20000, data: result, message: '回复感谢老板成功' });
} catch (err: any) {
res.status(400).json({ code: 40000, message: err.message });
}
});
// 友圈本人动态删除接口
app.delete('/api/posts/:id', async (req: Request, res: Response) => {
try {
const postId = String(req.params.id);
const userId = Number(req.query.userId || req.body?.userId || 1001);
await repo.deletePost(postId, userId);
res.json({ code: 20000, message: '动态删除成功' });
} catch (err: any) {
res.status(400).json({ code: 40000, message: err.message });
}
});
// 大神服务包管理接口 (获取与上下架切换)
app.get('/api/companions/:id/services', async (req: Request, res: Response) => {
try {
const expertId = Number(req.params.id);
const services = await repo.getExpertServices(expertId);
res.json({ code: 20000, data: services });
} catch (err: any) {
res.status(500).json({ code: 50000, message: err.message });
}
});
app.put('/api/companions/:id/services/:serviceId/status', async (req: Request, res: Response) => {
try {
const serviceId = String(req.params.serviceId);
const { status } = req.body;
const result = await repo.updateExpertServiceStatus(serviceId, Boolean(status));
res.json({ code: 20000, data: result, message: '服务状态更新成功' });
} catch (err: any) {
res.status(400).json({ code: 40000, message: err.message });
}
});
// 7.6 友圈动态互动评论区接口
app.get('/api/posts/:id/comments', async (req: Request, res: Response) => {
try {