Files
backmanager-server/routes/contracts.js
2026-06-22 20:48:29 +08:00

221 lines
6.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// routes/contracts.js —— 合同管理 CRUD
const { pool } = require('../db')
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 }
}
// 列表查询时 JOIN 客户名和员工名
const LIST_SELECT = `
SELECT
c.*,
cu.name AS customer_name,
e.name AS employee_name
FROM contracts c
LEFT JOIN customers cu ON c.customer_id = cu.id
LEFT JOIN employees e ON c.employee_id = e.id
`
// 单条查询时用同样的 JOIN
const DETAIL_SELECT = LIST_SELECT
// GET /api/contracts —— 列表
async function list(req, res) {
try {
const { page, pageSize, offset } = pagination(req.query)
const { status, customer_name, contract_no, employee_name } = req.query
let where = 'WHERE 1=1'
const params = []
if (status) {
where += ' AND c.status = ?'
params.push(status)
}
if (customer_name) {
where += ' AND cu.name LIKE ?'
params.push(`%${customer_name}%`)
}
if (contract_no) {
where += ' AND c.contract_no LIKE ?'
params.push(`%${contract_no}%`)
}
if (employee_name) {
where += ' AND e.name LIKE ?'
params.push(`%${employee_name}%`)
}
const [[{ total }]] = await pool.query(
`SELECT COUNT(*) AS total FROM contracts c
LEFT JOIN customers cu ON c.customer_id = cu.id
LEFT JOIN employees e ON c.employee_id = e.id
${where}`,
params
)
const [rows] = await pool.query(
`${LIST_SELECT} ${where} ORDER BY c.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('[contracts list] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
// GET /api/contracts/:id —— 详情
async function detail(req, res) {
try {
const [rows] = await pool.query(
`${DETAIL_SELECT} WHERE c.id = ?`,
[req.params.id]
)
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('[contracts detail] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
// POST /api/contracts —— 新增
async function create(req, res) {
const {
customer_id, contract_name, contract_no, contract_content,
amount, effective_date, expiry_date, employee_id, status, remark,
} = req.body || {}
if (!customer_id) {
return res.status(400).json({ code: 400, message: '客户ID必填' })
}
if (!contract_name) {
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 [result] = await pool.query(
`INSERT INTO contracts
(customer_id, contract_name, contract_no, contract_content, amount, effective_date, expiry_date, employee_id, status, remark)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[customer_id, contract_name,
contract_no || null,
contract_content || null,
amount !== undefined ? amount : null,
effective_date || null,
expiry_date || null,
employee_id || null,
status || '生效',
remark || null]
)
const [rows] = await pool.query(`${DETAIL_SELECT} WHERE c.id = ?`, [result.insertId])
res.json({ code: 0, message: 'ok', data: rows[0] })
} catch (e) {
console.error('[contracts create] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
// PUT /api/contracts/:id —— 更新
async function update(req, res) {
const { id } = req.params
const fields = [
'customer_id', 'contract_name', 'contract_no', 'contract_content',
'amount', 'effective_date', 'expiry_date', 'employee_id', 'status', 'remark',
]
try {
const [existing] = await pool.query('SELECT id FROM contracts WHERE id = ?', [id])
if (existing.length === 0) {
return res.status(404).json({ code: 404, message: '合同不存在' })
}
// 如果更新了 customer_id 或 employee_id需要校验
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} = ?`)
params.push(req.body[f])
}
}
if (sets.length === 0) {
return res.status(400).json({ code: 400, message: '没有需要更新的字段' })
}
params.push(id)
await pool.query(`UPDATE contracts SET ${sets.join(', ')} WHERE id = ?`, params)
const [rows] = await pool.query(`${DETAIL_SELECT} WHERE c.id = ?`, [id])
res.json({ code: 0, message: 'ok', data: rows[0] })
} catch (e) {
console.error('[contracts update] error:', e)
res.status(500).json({ code: 500, message: e.message })
}
}
// DELETE /api/contracts/:id —— 删除
async function remove(req, res) {
const { id } = req.params
try {
const [existing] = await pool.query('SELECT id FROM contracts WHERE id = ?', [id])
if (existing.length === 0) {
return res.status(404).json({ code: 404, message: '合同不存在' })
}
await pool.query('DELETE FROM contracts WHERE id = ?', [id])
res.json({ code: 0, message: 'ok' })
} catch (e) {
console.error('[contracts delete] error:', e)
// 如果因为外键约束customer_id 被 after_sales 引用)导致删除失败
if (e.code === 'ER_ROW_IS_REFERENCED_2') {
return res.status(400).json({ code: 400, message: '该合同关联了售后记录,无法删除' })
}
res.status(500).json({ code: 500, message: e.message })
}
}
module.exports = { list, detail, create, update, remove }