feat(marketing): implement recharge tiers and coupon management across admin, server, and uniapp
这个提交包含在:
@@ -0,0 +1,104 @@
|
||||
// 已读取AGENTS.md
|
||||
import { ContentSecurityService } from '../security/content-security.service';
|
||||
|
||||
export interface RechargeTierItem {
|
||||
id: number;
|
||||
amount: number;
|
||||
bonusAmount: number;
|
||||
tag?: string;
|
||||
sortOrder: number;
|
||||
status: number;
|
||||
}
|
||||
|
||||
export interface CouponItem {
|
||||
id: number;
|
||||
name: string;
|
||||
discountAmount: number;
|
||||
minSpend: number;
|
||||
validDays: number;
|
||||
totalCount: number;
|
||||
receivedCount: number;
|
||||
categoryLimit: string;
|
||||
description?: string;
|
||||
status: number;
|
||||
}
|
||||
|
||||
export class MarketingService {
|
||||
private tiers: RechargeTierItem[] = [
|
||||
{ id: 1, amount: 50, bonusAmount: 0, tag: '基础充值', sortOrder: 1, status: 1 },
|
||||
{ id: 2, amount: 100, bonusAmount: 5, tag: '热门畅享', sortOrder: 2, status: 1 },
|
||||
{ id: 3, amount: 200, bonusAmount: 15, tag: '超值加赠', sortOrder: 3, status: 1 },
|
||||
{ id: 4, amount: 500, bonusAmount: 50, tag: '尊享特惠', sortOrder: 4, status: 1 },
|
||||
{ id: 5, amount: 1000, bonusAmount: 120, tag: '土豪专享', sortOrder: 5, status: 1 }
|
||||
];
|
||||
|
||||
private coupons: CouponItem[] = [
|
||||
{ id: 1, name: '新用户注册专享立减券', discountAmount: 20, minSpend: 50, validDays: 15, totalCount: 2000, receivedCount: 128, categoryLimit: 'ALL', description: '全品类满50可用', status: 1 },
|
||||
{ id: 2, name: '周末同城聚会AA抵扣券', discountAmount: 15, minSpend: 80, validDays: 7, totalCount: 1000, receivedCount: 85, categoryLimit: 'MEAL', description: '限美食探店使用', status: 1 },
|
||||
{ id: 3, name: '工体K歌麦霸欢唱减免券', discountAmount: 30, minSpend: 120, validDays: 10, totalCount: 800, receivedCount: 62, categoryLimit: 'SING', description: '限KTV陪唱使用', status: 1 }
|
||||
];
|
||||
|
||||
private nextTierId = 6;
|
||||
private nextCouponId = 4;
|
||||
private secService = new ContentSecurityService();
|
||||
|
||||
// 充值阶梯
|
||||
getActiveTiers(): RechargeTierItem[] {
|
||||
return this.tiers.filter((t) => t.status === 1).sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
}
|
||||
|
||||
createTier(data: { amount: number; bonusAmount?: number; tag?: string; sortOrder?: number; status?: number }): RechargeTierItem {
|
||||
if (!data.amount || data.amount <= 0) {
|
||||
throw new Error('充值金额必须大于0');
|
||||
}
|
||||
const item: RechargeTierItem = {
|
||||
id: this.nextTierId++,
|
||||
amount: Number(data.amount),
|
||||
bonusAmount: Number(data.bonusAmount || 0),
|
||||
tag: data.tag || '',
|
||||
sortOrder: Number(data.sortOrder || 0),
|
||||
status: data.status !== undefined ? Number(data.status) : 1
|
||||
};
|
||||
this.tiers.push(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
// 优惠券业务
|
||||
getActiveCoupons(category?: string): CouponItem[] {
|
||||
return this.coupons
|
||||
.filter((c) => c.status === 1)
|
||||
.filter((c) => !category || category === 'ALL' || c.categoryLimit === 'ALL' || c.categoryLimit === category)
|
||||
.sort((a, b) => b.discountAmount - a.discountAmount);
|
||||
}
|
||||
|
||||
createCoupon(data: { name: string; discountAmount: number; minSpend?: number; validDays?: number; totalCount?: number; categoryLimit?: string; description?: string; status?: number }): CouponItem {
|
||||
if (!data.name || !data.name.trim()) {
|
||||
throw new Error('优惠券名称不能为空');
|
||||
}
|
||||
if (!data.discountAmount || data.discountAmount <= 0) {
|
||||
throw new Error('减免面额必须大于0');
|
||||
}
|
||||
|
||||
const sec = this.secService.checkText(data.name + ' ' + (data.description || ''));
|
||||
if (!sec.pass) {
|
||||
const err = new Error(sec.message);
|
||||
(err as any).code = sec.code;
|
||||
throw err;
|
||||
}
|
||||
|
||||
const item: CouponItem = {
|
||||
id: this.nextCouponId++,
|
||||
name: data.name.trim(),
|
||||
discountAmount: Number(data.discountAmount),
|
||||
minSpend: Number(data.minSpend || 0),
|
||||
validDays: Number(data.validDays || 7),
|
||||
totalCount: Number(data.totalCount || 1000),
|
||||
receivedCount: 0,
|
||||
categoryLimit: data.categoryLimit || 'ALL',
|
||||
description: data.description || '',
|
||||
status: data.status !== undefined ? Number(data.status) : 1
|
||||
};
|
||||
this.coupons.push(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
在新工单中引用
屏蔽一个用户