const { query, transaction } = require('../config/database') const { paginate } = require('../utils/pagination') const REFUNDABLE_STATUSES = ['paid', 'completed'] function currentUserId(ctx) { return ctx.state.user ? ctx.state.user.id : null } function currentUser(ctx) { return ctx.state.user } async function getRefunds(ctx) { const { page, pageSize, status } = ctx.query let sql = ` SELECT r.*, o.status as order_status, o.total_price as order_amount, u.phone as user_phone, u.name as user_name FROM refunds r LEFT JOIN orders o ON r.order_id = o.id LEFT JOIN users u ON r.user_id = u.id WHERE 1=1 ` const params = [] if (status !== undefined && status !== '') { sql += ' AND r.status = ?' params.push(parseInt(status)) } sql += ' ORDER BY r.created_at DESC' const result = await paginate(query, sql, params, ctx.query.page, ctx.query.pageSize) const rows = (result.data || []).map(item => ({ id: item.id, orderId: item.order_id, userId: item.user_id, userPhone: item.user_phone, userName: item.user_name, type: item.type, reason: item.reason, amount: parseFloat(item.amount), status: item.status, adminRemark: item.admin_remark, orderStatus: item.order_status, orderAmount: parseFloat(item.order_amount), processedAt: item.processed_at, createdAt: item.created_at })) result.data = rows ctx.body = { code: 200, ...result } } async function getRefundById(ctx) { const refundId = parseInt(ctx.params.id) const refunds = await query(` SELECT r.*, o.status as order_status, o.total_price as order_amount, u.phone as user_phone, u.name as user_name FROM refunds r LEFT JOIN orders o ON r.order_id = o.id LEFT JOIN users u ON r.user_id = u.id WHERE r.id = ? `, [refundId]) if (refunds.length === 0) { ctx.status = 404 ctx.body = { code: 404, message: '退款申请不存在' } return } const item = refunds[0] const user = currentUser(ctx) if (user.role !== 2 && user.role !== 1 && item.user_id !== user.id) { ctx.status = 403 ctx.body = { code: 403, message: '无权查看该退款' } return } ctx.body = { code: 200, data: { id: item.id, orderId: item.order_id, userId: item.user_id, userPhone: item.user_phone, userName: item.user_name, type: item.type, reason: item.reason, amount: parseFloat(item.amount), status: item.status, adminRemark: item.admin_remark, orderStatus: item.order_status, orderAmount: parseFloat(item.order_amount), processedAt: item.processed_at, createdAt: item.created_at } } } async function createRefund(ctx) { const user = currentUser(ctx) if (!user) { ctx.status = 401 ctx.body = { code: 401, message: '未登录' } return } const { orderId, type, reason, amount } = ctx.request.body || {} if (!orderId || !reason) { ctx.body = { code: 400, message: '缺少必要参数' } return } const userId = user.id const orders = await query('SELECT * FROM orders WHERE id = ? AND user_id = ?', [orderId, userId]) if (orders.length === 0) { ctx.body = { code: 404, message: '订单不存在' } return } const order = orders[0] if (!REFUNDABLE_STATUSES.includes(order.status)) { ctx.body = { code: 400, message: `订单当前状态(${order.status})不可申请退款` } return } const existingRefund = await query('SELECT * FROM refunds WHERE order_id = ? AND status = 0', [orderId]) if (existingRefund.length > 0) { ctx.body = { code: 400, message: '该订单已有待处理的退款申请' } return } const orderTotal = parseFloat(order.total_price) let refundAmount = orderTotal if (amount !== undefined && amount !== null) { const parsed = parseFloat(amount) if (isNaN(parsed) || parsed <= 0) { ctx.body = { code: 400, message: '退款金额无效' } return } if (parsed > orderTotal) { ctx.body = { code: 400, message: `退款金额不能超过订单金额 ¥${orderTotal.toFixed(2)}` } return } refundAmount = parsed } const result = await transaction(async (conn) => { const [refundResult] = await conn.execute( 'INSERT INTO refunds (order_id, user_id, type, reason, amount) VALUES (?, ?, ?, ?, ?)', [orderId, userId, type || 1, reason, refundAmount] ) await conn.execute("UPDATE orders SET status = 'refunding' WHERE id = ?", [orderId]) return refundResult.insertId }) ctx.body = { code: 200, message: '退款申请已提交', data: { id: result, orderId, amount: refundAmount } } } async function processRefund(ctx) { const refundId = parseInt(ctx.params.id) const { status, adminRemark } = ctx.request.body || {} if (status !== 1 && status !== 2) { ctx.body = { code: 400, message: '请选择正确的处理结果' } return } const refunds = await query('SELECT * FROM refunds WHERE id = ?', [refundId]) if (refunds.length === 0) { ctx.body = { code: 404, message: '退款申请不存在' } return } const refund = refunds[0] if (refund.status !== 0) { ctx.body = { code: 400, message: '该退款申请已处理' } return } await transaction(async (conn) => { await conn.execute( 'UPDATE refunds SET status = ?, admin_remark = ?, processed_at = NOW() WHERE id = ?', [status, adminRemark || '', refundId] ) if (status === 1) { await conn.execute("UPDATE orders SET status = 'refunded' WHERE id = ?", [refund.order_id]) const [userRows] = await conn.execute('SELECT points FROM users WHERE id = ?', [refund.user_id]) if (userRows.length > 0) { const deductPoints = Math.min(Math.floor(refund.amount), userRows[0].points) if (deductPoints > 0) { const newPoints = userRows[0].points - deductPoints await conn.execute('UPDATE users SET points = ? WHERE id = ?', [newPoints, refund.user_id]) await conn.execute( 'INSERT INTO points_logs (user_id, type, amount, description) VALUES (?, ?, ?, ?)', [refund.user_id, 'spend', deductPoints, `订单退款扣除积分: ${refund.order_id}`] ) } } } else { await conn.execute("UPDATE orders SET status = 'completed' WHERE id = ?", [refund.order_id]) } }) ctx.body = { code: 200, message: status === 1 ? '已同意退款' : '已拒绝退款' } } async function getUserRefunds(ctx) { const user = currentUser(ctx) if (!user) { ctx.status = 401 ctx.body = { code: 401, message: '未登录' } return } const requestedId = parseInt(ctx.query.userId) const userId = (user.role === 2 || user.role === 1) && requestedId ? requestedId : user.id const refunds = await query(` SELECT r.*, o.status as order_status FROM refunds r LEFT JOIN orders o ON r.order_id = o.id WHERE r.user_id = ? ORDER BY r.created_at DESC `, [userId]) const rows = refunds.map(item => ({ id: item.id, orderId: item.order_id, type: item.type, reason: item.reason, amount: parseFloat(item.amount), status: item.status, adminRemark: item.admin_remark, orderStatus: item.order_status, processedAt: item.processed_at, createdAt: item.created_at })) ctx.body = { code: 200, data: rows } } module.exports = { getRefunds, getRefundById, createRefund, processRefund, getUserRefunds }