// routes/afterSales.js —— 售后管理 CRUD const { pool } = require('../db') const { getDataScope } = require('../middleware/permissions') // 格式化日期为 YYYY-MM-DD function formatDate(dateStr) { if (!dateStr) return null if (typeof dateStr === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(dateStr)) { return dateStr } try { const date = new Date(dateStr) if (isNaN(date.getTime())) return null const year = date.getFullYear() const month = String(date.getMonth() + 1).padStart(2, '0') const day = String(date.getDate()).padStart(2, '0') return `${year}-${month}-${day}` } catch { return null } } function pagination(query) { const page = Math.max(Number(query.page) || 1, 1) const pageSize = Math.min(Math.max(Number(query.pageSize) || 10, 1), 100) const offset = (page - 1) * pageSize return { page, pageSize, offset } } const LIST_SELECT = ` SELECT a.*, cu.name AS customer_name, e.name AS employee_name FROM after_sales a LEFT JOIN customers cu ON a.customer_id = cu.id LEFT JOIN employees e ON a.employee_id = e.id ` // GET /api/after-sales —— 列表 async function list(req, res) { try { const { page, pageSize, offset } = pagination(req.query) const { handle_status, customer_name, id, customer_id, feedback } = req.query let where = 'WHERE 1=1' const params = [] // 数据范围过滤 const scope = getDataScope(req.user, 'after_sales') if (scope.deny) { return res.status(403).json({ code: 403, message: '无权访问此资源' }) } if (scope.where) { // list 查询有 JOIN,需要表别名前缀避免列名歧义 const scopeCol = scope.tableAlias ? scope.where.replace(/\bresponsible_user_id\b/g, `${scope.tableAlias}.responsible_user_id`) : scope.where where += ' AND ' + scopeCol params.push(...scope.values) } if (id) { where += ' AND a.id = ?' params.push(id) } if (customer_id) { where += ' AND a.customer_id = ?' params.push(customer_id) } if (feedback) { where += ' AND a.feedback LIKE ?' params.push(`%${feedback}%`) } if (handle_status) { where += ' AND a.handle_status = ?' params.push(handle_status) } if (customer_name) { where += ' AND cu.name LIKE ?' params.push(`%${customer_name}%`) } const [[{ total }]] = await pool.query( `SELECT COUNT(*) AS total FROM after_sales a LEFT JOIN customers cu ON a.customer_id = cu.id LEFT JOIN employees e ON a.employee_id = e.id ${where}`, params ) const [rows] = await pool.query( `${LIST_SELECT} ${where} ORDER BY a.id DESC LIMIT ? OFFSET ?`, [...params, pageSize, offset] ) res.json({ code: 0, message: 'ok', data: { list: rows, total, page, pageSize, totalPages: Math.ceil(total / pageSize), }, }) } catch (e) { console.error('[after-sales list] error:', e) res.status(500).json({ code: 500, message: e.message }) } } // GET /api/after-sales/:id —— 详情 async function detail(req, res) { try { let sql = `${LIST_SELECT} WHERE a.id = ?` const params = [req.params.id] const scope = getDataScope(req.user, 'after_sales') if (scope.deny) { return res.status(403).json({ code: 403, message: '无权访问此资源' }) } if (scope.where) { const scopeCol = scope.tableAlias ? scope.where.replace('responsible_user_id', `${scope.tableAlias}.responsible_user_id`) : scope.where sql += ' AND ' + scopeCol params.push(...scope.values) } const [rows] = await pool.query(sql, params) if (rows.length === 0) { return res.status(404).json({ code: 404, message: '售后记录不存在' }) } res.json({ code: 0, message: 'ok', data: rows[0] }) } catch (e) { console.error('[after-sales detail] error:', e) res.status(500).json({ code: 500, message: e.message }) } } // POST /api/after-sales —— 新增 async function create(req, res) { const { customer_id, feedback, employee_id, handle_method, handle_status, service_date, remark, responsible_user_id, } = req.body || {} if (!customer_id) { return res.status(400).json({ code: 400, message: '客户ID必填' }) } if (!feedback) { return res.status(400).json({ code: 400, message: '售后反馈内容必填' }) } try { // 校验客户存在 const [cust] = await pool.query('SELECT id FROM customers WHERE id = ?', [customer_id]) if (cust.length === 0) { return res.status(400).json({ code: 400, message: '关联客户不存在' }) } // 校验业务员 if (employee_id) { const [emp] = await pool.query('SELECT id FROM employees WHERE id = ?', [employee_id]) if (emp.length === 0) { return res.status(400).json({ code: 400, message: '关联业务员不存在' }) } } // 如果没指定负责人,默认设为当前用户 const ownerId = responsible_user_id || req.user.id const [result] = await pool.query( `INSERT INTO after_sales (customer_id, feedback, employee_id, handle_method, handle_status, service_date, remark, responsible_user_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [customer_id, feedback, employee_id || null, handle_method || null, handle_status || '待处理', formatDate(service_date), remark || null, ownerId] ) const [rows] = await pool.query(`${LIST_SELECT} WHERE a.id = ?`, [result.insertId]) res.json({ code: 0, message: 'ok', data: rows[0] }) } catch (e) { console.error('[after-sales create] error:', e) res.status(500).json({ code: 500, message: e.message }) } } // PUT /api/after-sales/:id —— 更新 async function update(req, res) { const { id } = req.params const fields = [ 'customer_id', 'feedback', 'employee_id', 'handle_method', 'handle_status', 'service_date', 'remark', 'responsible_user_id', ] try { // 确认记录存在且在数据范围内 let checkSql = 'SELECT id FROM after_sales WHERE id = ?' const checkParams = [id] const scope = getDataScope(req.user, 'after_sales') if (scope.deny) { return res.status(403).json({ code: 403, message: '无权访问此资源' }) } if (scope.where) { checkSql += ' AND ' + scope.where checkParams.push(...scope.values) } const [existing] = await pool.query(checkSql, checkParams) if (existing.length === 0) { return res.status(404).json({ code: 404, message: '售后记录不存在或无权操作' }) } if (req.body.customer_id) { const [cust] = await pool.query('SELECT id FROM customers WHERE id = ?', [req.body.customer_id]) if (cust.length === 0) { return res.status(400).json({ code: 400, message: '关联客户不存在' }) } } if (req.body.employee_id) { const [emp] = await pool.query('SELECT id FROM employees WHERE id = ?', [req.body.employee_id]) if (emp.length === 0) { return res.status(400).json({ code: 400, message: '关联业务员不存在' }) } } const sets = [] const params = [] for (const f of fields) { if (req.body[f] !== undefined) { sets.push(`${f} = ?`) // 日期字段需要格式化 if (f === 'service_date') { params.push(formatDate(req.body[f])) } else { params.push(req.body[f]) } } } if (sets.length === 0) { return res.status(400).json({ code: 400, message: '没有需要更新的字段' }) } params.push(id) await pool.query(`UPDATE after_sales SET ${sets.join(', ')} WHERE id = ?`, params) const [rows] = await pool.query(`${LIST_SELECT} WHERE a.id = ?`, [id]) res.json({ code: 0, message: 'ok', data: rows[0] }) } catch (e) { console.error('[after-sales update] error:', e) res.status(500).json({ code: 500, message: e.message }) } } // DELETE /api/after-sales/:id —— 删除 async function remove(req, res) { const { id } = req.params try { let checkSql = 'SELECT id FROM after_sales WHERE id = ?' const checkParams = [id] const scope = getDataScope(req.user, 'after_sales') if (scope.deny) { return res.status(403).json({ code: 403, message: '无权访问此资源' }) } if (scope.where) { checkSql += ' AND ' + scope.where checkParams.push(...scope.values) } const [existing] = await pool.query(checkSql, checkParams) if (existing.length === 0) { return res.status(404).json({ code: 404, message: '售后记录不存在或无权操作' }) } await pool.query('DELETE FROM after_sales WHERE id = ?', [id]) res.json({ code: 0, message: 'ok' }) } catch (e) { console.error('[after-sales delete] error:', e) res.status(500).json({ code: 500, message: e.message }) } } module.exports = { list, detail, create, update, remove }