const API = require('../../../lib/API.js') const db = require('../../../plugin/DataBase/db.js') const { BaseStdResponse } = require('../../../BaseStdResponse.js') const AccessControl = require('../../../lib/AccessControl.js') function parseUsernames(text) { if (!text) return [] return [...new Set( String(text) .split(/[,,\n\r\s]+/) .map((s) => s.trim()) .filter(Boolean) )] } class SaveCoupon extends API { constructor() { super() this.setPath('/Admin/Coupon/Save') this.setMethod('POST') } async onRequest(req, res) { const { uuid, session, id, code, name, discount_type, discount_value, user_scope, goods_scope, total_limit, per_user_limit, min_amount, start_time, end_time, state, allowed_usernames, allowed_goods_ids } = req.body if ([uuid, session, code, discount_type, discount_value, user_scope, goods_scope, state].some( (v) => v === '' || v == null )) { return res.json({ ...BaseStdResponse.MISSING_PARAMETER }) } if (!(await AccessControl.checkSession(uuid, session))) { return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED }) } const permission = await AccessControl.getPermission(uuid) if (!permission.includes('admin') && !permission.includes('product')) { return res.json({ ...BaseStdResponse.PERMISSION_DENIED }) } const normalizedCode = String(code).trim().toUpperCase() if (!/^[A-Z0-9_-]{3,32}$/.test(normalizedCode)) { return res.json({ ...BaseStdResponse.ERR, msg: '优惠码仅支持 3-32 位字母、数字、下划线或横线' }) } if (!['percent', 'fixed'].includes(discount_type)) { return res.json({ ...BaseStdResponse.ERR, msg: '折扣类型无效' }) } const dValue = Number(discount_value) if (discount_type === 'percent' && (dValue <= 0 || dValue > 100)) { return res.json({ ...BaseStdResponse.ERR, msg: '折扣比例需在 1~100 之间' }) } if (discount_type === 'fixed' && dValue <= 0) { return res.json({ ...BaseStdResponse.ERR, msg: '减免金额需大于 0' }) } const uScope = Number(user_scope) const gScope = Number(goods_scope) const usernames = parseUsernames(allowed_usernames) const goodsIds = (Array.isArray(allowed_goods_ids) ? allowed_goods_ids : []) .map((g) => Number(g)) .filter((g) => g > 0) if (uScope === 1 && usernames.length === 0) { return res.json({ ...BaseStdResponse.ERR, msg: '请指定可使用该优惠码的用户' }) } if (gScope === 1 && goodsIds.length === 0) { return res.json({ ...BaseStdResponse.ERR, msg: '请指定可使用该优惠码的商品' }) } const dup = await db.query( 'SELECT id FROM coupons WHERE code = ? AND id != ? LIMIT 1', [normalizedCode, id || 0] ) if (dup && dup.length > 0) { return res.json({ ...BaseStdResponse.ERR, msg: '优惠码已存在' }) } const time = Date.now() const conn = await db.connect() try { await conn.beginTransaction() let couponId = id ? Number(id) : null if (!couponId) { const [ins] = await conn.execute( `INSERT INTO coupons ( code, name, discount_type, discount_value, user_scope, goods_scope, total_limit, per_user_limit, min_amount, start_time, end_time, state, create_user, create_time, update_time ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ normalizedCode, name || '', discount_type, dValue, uScope, gScope, Number(total_limit || 0), Math.max(1, Number(per_user_limit || 1)), Number(min_amount || 0), start_time || null, end_time || null, Number(state), uuid, time, time ] ) couponId = ins.insertId } else { await conn.execute( `UPDATE coupons SET code = ?, name = ?, discount_type = ?, discount_value = ?, user_scope = ?, goods_scope = ?, total_limit = ?, per_user_limit = ?, min_amount = ?, start_time = ?, end_time = ?, state = ?, update_time = ? WHERE id = ?`, [ normalizedCode, name || '', discount_type, dValue, uScope, gScope, Number(total_limit || 0), Math.max(1, Number(per_user_limit || 1)), Number(min_amount || 0), start_time || null, end_time || null, Number(state), time, couponId ] ) } await conn.execute('DELETE FROM coupon_users WHERE coupon_id = ?', [couponId]) await conn.execute('DELETE FROM coupon_goods WHERE coupon_id = ?', [couponId]) if (uScope === 1) { for (const username of usernames) { const [userRows] = await conn.execute( 'SELECT uuid FROM users WHERE username = ? LIMIT 1', [username] ) if (!userRows || userRows.length !== 1) { await conn.rollback() return res.json({ ...BaseStdResponse.ERR, msg: `用户不存在:${username}` }) } await conn.execute( 'INSERT INTO coupon_users (coupon_id, user_uuid) VALUES (?, ?)', [couponId, userRows[0].uuid] ) } } if (gScope === 1) { for (const goodsId of goodsIds) { await conn.execute( 'INSERT INTO coupon_goods (coupon_id, goods_id) VALUES (?, ?)', [couponId, goodsId] ) } } await conn.commit() return res.json({ ...BaseStdResponse.OK, id: couponId }) } catch (err) { await conn.rollback() this.logger.error(`保存优惠码失败: ${err.stack}`) return res.json({ ...BaseStdResponse.ERR, msg: '保存优惠码失败' }) } } } module.exports.SaveCoupon = SaveCoupon