feat(marketing): implement recharge tiers and coupon management across admin, server, and uniapp
这个提交包含在:
@@ -863,6 +863,194 @@ app.delete('/api/admin/nav-categories/:id', async (req: Request, res: Response)
|
||||
}
|
||||
});
|
||||
|
||||
// ===================== 营销中心 API =====================
|
||||
|
||||
// 1. 充值阶梯接口
|
||||
// 移动端:获取启用的充值阶梯
|
||||
app.get('/api/marketing/recharge-tiers', async (_req: Request, res: Response) => {
|
||||
try {
|
||||
const list = await repo.getRechargeTiers();
|
||||
res.json({ code: 20000, data: list });
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ code: 50000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 管理后台:获取全部充值阶梯
|
||||
app.get('/api/admin/recharge-tiers', async (_req: Request, res: Response) => {
|
||||
try {
|
||||
const list = await repo.getAdminRechargeTiers();
|
||||
res.json({ code: 20000, data: list });
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ code: 50000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 管理后台:新增充值阶梯
|
||||
app.post('/api/admin/recharge-tiers', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { amount, bonusAmount, tag, sortOrder, status } = req.body;
|
||||
if (amount === undefined || Number(amount) <= 0) {
|
||||
return res.status(400).json({ code: 40000, message: '充值金额必须大于0' });
|
||||
}
|
||||
|
||||
const tier = await repo.createRechargeTier({
|
||||
amount: Number(amount),
|
||||
bonusAmount: bonusAmount !== undefined ? Number(bonusAmount) : 0,
|
||||
tag: tag || '',
|
||||
sortOrder: sortOrder !== undefined ? Number(sortOrder) : 0,
|
||||
status: status !== undefined ? Number(status) : 1
|
||||
});
|
||||
|
||||
res.json({ code: 20000, data: tier, message: '充值阶梯创建成功' });
|
||||
} catch (err: any) {
|
||||
res.status(400).json({ code: 40000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 管理后台:修改充值阶梯
|
||||
app.put('/api/admin/recharge-tiers/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const tierId = String(req.params.id);
|
||||
const { amount, bonusAmount, tag, sortOrder, status } = req.body;
|
||||
|
||||
const updated = await repo.updateRechargeTier(tierId, {
|
||||
amount: amount !== undefined ? Number(amount) : undefined,
|
||||
bonusAmount: bonusAmount !== undefined ? Number(bonusAmount) : undefined,
|
||||
tag,
|
||||
sortOrder: sortOrder !== undefined ? Number(sortOrder) : undefined,
|
||||
status: status !== undefined ? Number(status) : undefined
|
||||
});
|
||||
|
||||
if (!updated) {
|
||||
return res.status(404).json({ code: 40400, message: '充值阶梯不存在或已被删除' });
|
||||
}
|
||||
|
||||
res.json({ code: 20000, data: updated, message: '充值阶梯修改成功' });
|
||||
} catch (err: any) {
|
||||
res.status(400).json({ code: 40000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 管理后台:删除充值阶梯
|
||||
app.delete('/api/admin/recharge-tiers/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const tierId = String(req.params.id);
|
||||
const ok = await repo.deleteRechargeTier(tierId);
|
||||
if (!ok) {
|
||||
return res.status(404).json({ code: 40400, message: '充值阶梯不存在或已被删除' });
|
||||
}
|
||||
res.json({ code: 20000, data: null, message: '充值阶梯已删除' });
|
||||
} catch (err: any) {
|
||||
res.status(400).json({ code: 40000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 2. 优惠券接口
|
||||
// 移动端:领券中心或下单结算可用券列表
|
||||
app.get('/api/marketing/coupons', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const category = req.query.category ? String(req.query.category) : undefined;
|
||||
const list = await repo.getCoupons(category);
|
||||
res.json({ code: 20000, data: list });
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ code: 50000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 管理后台:获取全部优惠券
|
||||
app.get('/api/admin/coupons', async (_req: Request, res: Response) => {
|
||||
try {
|
||||
const list = await repo.getAdminCoupons();
|
||||
res.json({ code: 20000, data: list });
|
||||
} catch (err: any) {
|
||||
res.status(500).json({ code: 50000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 管理后台:新增优惠券
|
||||
app.post('/api/admin/coupons', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const { name, discountAmount, minSpend, validDays, totalCount, categoryLimit, description, status } = req.body;
|
||||
if (!name || !name.trim()) {
|
||||
return res.status(400).json({ code: 40000, message: '优惠券名称不能为空' });
|
||||
}
|
||||
if (!discountAmount || Number(discountAmount) <= 0) {
|
||||
return res.status(400).json({ code: 40000, message: '减免面额必须大于0' });
|
||||
}
|
||||
|
||||
// 微信内容安全检测
|
||||
const sec = contentSecurityService.checkText(name + ' ' + (description || ''));
|
||||
if (!sec.pass) {
|
||||
return res.status(400).json({ code: sec.code, message: sec.message });
|
||||
}
|
||||
|
||||
const coupon = await repo.createCoupon({
|
||||
name: name.trim(),
|
||||
discountAmount: Number(discountAmount),
|
||||
minSpend: minSpend !== undefined ? Number(minSpend) : 0,
|
||||
validDays: validDays !== undefined ? Number(validDays) : 7,
|
||||
totalCount: totalCount !== undefined ? Number(totalCount) : 1000,
|
||||
categoryLimit: categoryLimit || 'ALL',
|
||||
description: description || '',
|
||||
status: status !== undefined ? Number(status) : 1
|
||||
});
|
||||
|
||||
res.json({ code: 20000, data: coupon, message: '优惠券创建成功' });
|
||||
} catch (err: any) {
|
||||
res.status(400).json({ code: 40000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 管理后台:修改优惠券
|
||||
app.put('/api/admin/coupons/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const couponId = String(req.params.id);
|
||||
const { name, discountAmount, minSpend, validDays, totalCount, categoryLimit, description, status } = req.body;
|
||||
|
||||
if (name) {
|
||||
const sec = contentSecurityService.checkText(name + ' ' + (description || ''));
|
||||
if (!sec.pass) {
|
||||
return res.status(400).json({ code: sec.code, message: sec.message });
|
||||
}
|
||||
}
|
||||
|
||||
const updated = await repo.updateCoupon(couponId, {
|
||||
name: name ? name.trim() : undefined,
|
||||
discountAmount: discountAmount !== undefined ? Number(discountAmount) : undefined,
|
||||
minSpend: minSpend !== undefined ? Number(minSpend) : undefined,
|
||||
validDays: validDays !== undefined ? Number(validDays) : undefined,
|
||||
totalCount: totalCount !== undefined ? Number(totalCount) : undefined,
|
||||
categoryLimit,
|
||||
description,
|
||||
status: status !== undefined ? Number(status) : undefined
|
||||
});
|
||||
|
||||
if (!updated) {
|
||||
return res.status(404).json({ code: 40400, message: '优惠券不存在或已被删除' });
|
||||
}
|
||||
|
||||
res.json({ code: 20000, data: updated, message: '优惠券修改成功' });
|
||||
} catch (err: any) {
|
||||
res.status(400).json({ code: 40000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 管理后台:删除优惠券
|
||||
app.delete('/api/admin/coupons/:id', async (req: Request, res: Response) => {
|
||||
try {
|
||||
const couponId = String(req.params.id);
|
||||
const ok = await repo.deleteCoupon(couponId);
|
||||
if (!ok) {
|
||||
return res.status(404).json({ code: 40400, message: '优惠券不存在或已被删除' });
|
||||
}
|
||||
|
||||
res.json({ code: 20000, data: null, message: '优惠券已删除' });
|
||||
} catch (err: any) {
|
||||
res.status(400).json({ code: 40000, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 创建新订单 (落盘 MySQL)
|
||||
app.post('/api/orders', async (req: Request, res: Response) => {
|
||||
try {
|
||||
|
||||
在新工单中引用
屏蔽一个用户