feat(marketing): implement recharge tiers and coupon management across admin, server, and uniapp
这个提交包含在:
@@ -999,6 +999,254 @@ export class MysqlRepository {
|
||||
return res.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 1. 充值阶梯 (Recharge Tiers)
|
||||
async getRechargeTiers(): Promise<any[]> {
|
||||
const [rows]: any = await pool.query(`
|
||||
SELECT
|
||||
id, amount, bonus_amount as bonusAmount, tag,
|
||||
sort_order as sortOrder, status,
|
||||
created_at as createdAt, updated_at as updatedAt
|
||||
FROM recharge_tiers
|
||||
WHERE status = 1
|
||||
ORDER BY sort_order ASC, amount ASC
|
||||
`);
|
||||
return rows;
|
||||
}
|
||||
|
||||
async getAdminRechargeTiers(): Promise<any[]> {
|
||||
const [rows]: any = await pool.query(`
|
||||
SELECT
|
||||
id, amount, bonus_amount as bonusAmount, tag,
|
||||
sort_order as sortOrder, status,
|
||||
created_at as createdAt, updated_at as updatedAt
|
||||
FROM recharge_tiers
|
||||
ORDER BY sort_order ASC, amount ASC
|
||||
`);
|
||||
return rows;
|
||||
}
|
||||
|
||||
async createRechargeTier(data: {
|
||||
amount: number;
|
||||
bonusAmount?: number;
|
||||
tag?: string;
|
||||
sortOrder?: number;
|
||||
status?: number;
|
||||
}): Promise<any> {
|
||||
const amount = Number(data.amount);
|
||||
const bonusAmount = Number(data.bonusAmount || 0);
|
||||
const tag = data.tag || '';
|
||||
const sortOrder = Number(data.sortOrder || 0);
|
||||
const status = data.status !== undefined ? Number(data.status) : 1;
|
||||
|
||||
const [res]: any = await pool.query(`
|
||||
INSERT INTO recharge_tiers (amount, bonus_amount, tag, sort_order, status)
|
||||
VALUES (?, ?, ?, ?, ?)
|
||||
`, [amount, bonusAmount, tag, sortOrder, status]);
|
||||
|
||||
const newId = res.insertId;
|
||||
const [rows]: any = await pool.query(`
|
||||
SELECT
|
||||
id, amount, bonus_amount as bonusAmount, tag,
|
||||
sort_order as sortOrder, status,
|
||||
created_at as createdAt, updated_at as updatedAt
|
||||
FROM recharge_tiers
|
||||
WHERE id = ?
|
||||
`, [newId]);
|
||||
return rows[0];
|
||||
}
|
||||
|
||||
async updateRechargeTier(id: number | string, data: Partial<{
|
||||
amount: number;
|
||||
bonusAmount: number;
|
||||
tag: string;
|
||||
sortOrder: number;
|
||||
status: number;
|
||||
}>): Promise<any> {
|
||||
const fields: string[] = [];
|
||||
const values: any[] = [];
|
||||
|
||||
if (data.amount !== undefined) {
|
||||
fields.push('amount = ?');
|
||||
values.push(Number(data.amount));
|
||||
}
|
||||
if (data.bonusAmount !== undefined) {
|
||||
fields.push('bonus_amount = ?');
|
||||
values.push(Number(data.bonusAmount));
|
||||
}
|
||||
if (data.tag !== undefined) {
|
||||
fields.push('tag = ?');
|
||||
values.push(data.tag);
|
||||
}
|
||||
if (data.sortOrder !== undefined) {
|
||||
fields.push('sort_order = ?');
|
||||
values.push(Number(data.sortOrder));
|
||||
}
|
||||
if (data.status !== undefined) {
|
||||
fields.push('status = ?');
|
||||
values.push(Number(data.status));
|
||||
}
|
||||
|
||||
if (fields.length > 0) {
|
||||
values.push(id);
|
||||
await pool.query(`UPDATE recharge_tiers SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||
}
|
||||
|
||||
const [rows]: any = await pool.query(`
|
||||
SELECT
|
||||
id, amount, bonus_amount as bonusAmount, tag,
|
||||
sort_order as sortOrder, status,
|
||||
created_at as createdAt, updated_at as updatedAt
|
||||
FROM recharge_tiers
|
||||
WHERE id = ?
|
||||
`, [id]);
|
||||
return rows[0] || null;
|
||||
}
|
||||
|
||||
async deleteRechargeTier(id: number | string): Promise<boolean> {
|
||||
const [res]: any = await pool.query('DELETE FROM recharge_tiers WHERE id = ?', [id]);
|
||||
return res.affectedRows > 0;
|
||||
}
|
||||
|
||||
// 2. 优惠券 (Coupons)
|
||||
async getCoupons(categoryLimit?: string): Promise<any[]> {
|
||||
let sql = `
|
||||
SELECT
|
||||
id, name, discount_amount as discountAmount, min_spend as minSpend,
|
||||
valid_days as validDays, total_count as totalCount, received_count as receivedCount,
|
||||
category_limit as categoryLimit, description, status,
|
||||
created_at as createdAt, updated_at as updatedAt
|
||||
FROM coupons
|
||||
WHERE status = 1
|
||||
`;
|
||||
const params: any[] = [];
|
||||
if (categoryLimit && categoryLimit !== 'ALL') {
|
||||
sql += ' AND (category_limit = "ALL" OR category_limit = ?)';
|
||||
params.push(categoryLimit);
|
||||
}
|
||||
sql += ' ORDER BY discount_amount DESC, id ASC';
|
||||
|
||||
const [rows]: any = await pool.query(sql, params);
|
||||
return rows;
|
||||
}
|
||||
|
||||
async getAdminCoupons(): Promise<any[]> {
|
||||
const [rows]: any = await pool.query(`
|
||||
SELECT
|
||||
id, name, discount_amount as discountAmount, min_spend as minSpend,
|
||||
valid_days as validDays, total_count as totalCount, received_count as receivedCount,
|
||||
category_limit as categoryLimit, description, status,
|
||||
created_at as createdAt, updated_at as updatedAt
|
||||
FROM coupons
|
||||
ORDER BY id DESC
|
||||
`);
|
||||
return rows;
|
||||
}
|
||||
|
||||
async createCoupon(data: {
|
||||
name: string;
|
||||
discountAmount: number;
|
||||
minSpend?: number;
|
||||
validDays?: number;
|
||||
totalCount?: number;
|
||||
categoryLimit?: string;
|
||||
description?: string;
|
||||
status?: number;
|
||||
}): Promise<any> {
|
||||
const name = data.name;
|
||||
const discountAmount = Number(data.discountAmount);
|
||||
const minSpend = Number(data.minSpend || 0);
|
||||
const validDays = Number(data.validDays || 7);
|
||||
const totalCount = Number(data.totalCount || 1000);
|
||||
const categoryLimit = data.categoryLimit || 'ALL';
|
||||
const description = data.description || '';
|
||||
const status = data.status !== undefined ? Number(data.status) : 1;
|
||||
|
||||
const [res]: any = await pool.query(`
|
||||
INSERT INTO coupons (name, discount_amount, min_spend, valid_days, total_count, received_count, category_limit, description, status)
|
||||
VALUES (?, ?, ?, ?, ?, 0, ?, ?, ?)
|
||||
`, [name, discountAmount, minSpend, validDays, totalCount, categoryLimit, description, status]);
|
||||
|
||||
const newId = res.insertId;
|
||||
const [rows]: any = await pool.query(`
|
||||
SELECT
|
||||
id, name, discount_amount as discountAmount, min_spend as minSpend,
|
||||
valid_days as validDays, total_count as totalCount, received_count as receivedCount,
|
||||
category_limit as categoryLimit, description, status,
|
||||
created_at as createdAt, updated_at as updatedAt
|
||||
FROM coupons
|
||||
WHERE id = ?
|
||||
`, [newId]);
|
||||
return rows[0];
|
||||
}
|
||||
|
||||
async updateCoupon(id: number | string, data: Partial<{
|
||||
name: string;
|
||||
discountAmount: number;
|
||||
minSpend: number;
|
||||
validDays: number;
|
||||
totalCount: number;
|
||||
categoryLimit: string;
|
||||
description: string;
|
||||
status: number;
|
||||
}>): Promise<any> {
|
||||
const fields: string[] = [];
|
||||
const values: any[] = [];
|
||||
|
||||
if (data.name !== undefined) {
|
||||
fields.push('name = ?');
|
||||
values.push(data.name);
|
||||
}
|
||||
if (data.discountAmount !== undefined) {
|
||||
fields.push('discount_amount = ?');
|
||||
values.push(Number(data.discountAmount));
|
||||
}
|
||||
if (data.minSpend !== undefined) {
|
||||
fields.push('min_spend = ?');
|
||||
values.push(Number(data.minSpend));
|
||||
}
|
||||
if (data.validDays !== undefined) {
|
||||
fields.push('valid_days = ?');
|
||||
values.push(Number(data.validDays));
|
||||
}
|
||||
if (data.totalCount !== undefined) {
|
||||
fields.push('total_count = ?');
|
||||
values.push(Number(data.totalCount));
|
||||
}
|
||||
if (data.categoryLimit !== undefined) {
|
||||
fields.push('category_limit = ?');
|
||||
values.push(data.categoryLimit);
|
||||
}
|
||||
if (data.description !== undefined) {
|
||||
fields.push('description = ?');
|
||||
values.push(data.description);
|
||||
}
|
||||
if (data.status !== undefined) {
|
||||
fields.push('status = ?');
|
||||
values.push(Number(data.status));
|
||||
}
|
||||
|
||||
if (fields.length > 0) {
|
||||
values.push(id);
|
||||
await pool.query(`UPDATE coupons SET ${fields.join(', ')} WHERE id = ?`, values);
|
||||
}
|
||||
|
||||
const [rows]: any = await pool.query(`
|
||||
SELECT
|
||||
id, name, discount_amount as discountAmount, min_spend as minSpend,
|
||||
valid_days as validDays, total_count as totalCount, received_count as receivedCount,
|
||||
category_limit as categoryLimit, description, status,
|
||||
created_at as createdAt, updated_at as updatedAt
|
||||
FROM coupons
|
||||
WHERE id = ?
|
||||
`, [id]);
|
||||
return rows[0] || null;
|
||||
}
|
||||
|
||||
async deleteCoupon(id: number | string): Promise<boolean> {
|
||||
const [res]: any = await pool.query('DELETE FROM coupons WHERE id = ?', [id]);
|
||||
return res.affectedRows > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
async getArbitrations(): Promise<any[]> {
|
||||
|
||||
在新工单中引用
屏蔽一个用户