281 lines
8.3 KiB
JavaScript
281 lines
8.3 KiB
JavaScript
const { query, transaction } = require('../config/database')
|
||
const { paginate } = require('../utils/pagination')
|
||
const orderService = require('../services/orderService')
|
||
const { requireAuth } = require('./users')
|
||
|
||
const ORDER_UPDATEABLE_FIELDS = ['status', 'total_price', 'totalPrice', 'cart']
|
||
|
||
function allowedUpdateFields(body) {
|
||
const result = {}
|
||
for (const key of ORDER_UPDATEABLE_FIELDS) {
|
||
if (key in body) {
|
||
result[key] = body[key]
|
||
}
|
||
}
|
||
return result
|
||
}
|
||
|
||
async function getOrders(ctx) {
|
||
const operator = await requireAuth(ctx)
|
||
if (!operator) return
|
||
|
||
const { page, pageSize, status } = ctx.query
|
||
let sql = `
|
||
SELECT
|
||
o.*,
|
||
JSON_ARRAYAGG(
|
||
JSON_OBJECT(
|
||
'id', oi.id,
|
||
'order_id', oi.order_id,
|
||
'goods_id', oi.goods_id,
|
||
'goods_name', oi.goods_name,
|
||
'price', oi.price,
|
||
'quantity', oi.quantity,
|
||
'weight', oi.weight,
|
||
'subtotal', oi.subtotal,
|
||
'unit', oi.unit
|
||
)
|
||
) as items_json
|
||
FROM orders o
|
||
LEFT JOIN order_items oi ON o.id = oi.order_id
|
||
WHERE 1=1
|
||
`
|
||
const params = []
|
||
|
||
if (operator.role !== 1) {
|
||
sql += ' AND o.user_id = ?'
|
||
params.push(operator.id)
|
||
}
|
||
|
||
if (status) {
|
||
sql += ' AND o.status = ?'
|
||
params.push(status)
|
||
}
|
||
sql += ' GROUP BY o.id ORDER BY o.created_at DESC'
|
||
|
||
const result = await paginate(query, sql, params, page, pageSize)
|
||
|
||
const rows = (result.data || []).map(row => {
|
||
let items = []
|
||
try {
|
||
const itemsJson = row.items_json
|
||
if (itemsJson) {
|
||
items = typeof itemsJson === 'string' ? JSON.parse(itemsJson) : itemsJson
|
||
if (items.length === 1 && items[0].id === null) {
|
||
items = []
|
||
}
|
||
}
|
||
} catch {}
|
||
|
||
const { items_json, ...order } = row
|
||
return { ...order, items }
|
||
})
|
||
result.data = rows
|
||
|
||
ctx.body = {
|
||
code: 200,
|
||
...result
|
||
}
|
||
}
|
||
|
||
async function getOrderById(ctx) {
|
||
const operator = await requireAuth(ctx)
|
||
if (!operator) return
|
||
|
||
const orderId = ctx.params.id
|
||
const orders = await query('SELECT * FROM orders WHERE id = ?', [orderId])
|
||
|
||
if (orders.length > 0) {
|
||
const order = orders[0]
|
||
|
||
if (operator.role !== 1 && order.user_id !== operator.id) {
|
||
ctx.body = { code: 403, message: '无权查看此订单' }
|
||
return
|
||
}
|
||
|
||
order.items = await orderService.getOrderItems(orderId)
|
||
ctx.body = {
|
||
code: 200,
|
||
data: order
|
||
}
|
||
} else {
|
||
ctx.body = {
|
||
code: 404,
|
||
message: '订单不存在'
|
||
}
|
||
}
|
||
}
|
||
|
||
async function createOrder(ctx) {
|
||
const operator = await requireAuth(ctx)
|
||
if (!operator) return
|
||
|
||
const { totalPrice, cart, remark, customerName, customerPhone, orderType, status } = ctx.request.body
|
||
const userId = ctx.request.body.userId || operator.id
|
||
|
||
if (operator.role !== 1 && userId !== operator.id) {
|
||
ctx.body = { code: 403, message: '无权为他人创建订单' }
|
||
return
|
||
}
|
||
|
||
if (!cart || (Array.isArray(cart) && cart.length === 0)) {
|
||
ctx.body = { code: 400, message: '购物车不能为空' }
|
||
return
|
||
}
|
||
|
||
const items = typeof cart === 'string' ? JSON.parse(cart) : cart
|
||
const calculatedTotalPrice = await orderService.recalculateTotalPrice(items)
|
||
const orderId = `ORD${Date.now()}${Math.floor(Math.random() * 10000).toString().padStart(4, '0')}`
|
||
const orderStatus = status || 'pending'
|
||
|
||
const userInfo = JSON.stringify({
|
||
remark: remark || '',
|
||
customerName: customerName || '',
|
||
customerPhone: customerPhone || '',
|
||
orderType: orderType || 'customer'
|
||
})
|
||
|
||
await transaction(async (conn) => {
|
||
for (const item of items) {
|
||
const goodsId = item.id || item.goods_id || item.goodsId
|
||
if (!goodsId) continue
|
||
const qty = item.pricingType === 2 ? 1 : (item.quantity || 1)
|
||
|
||
const [rows] = await conn.execute('SELECT id, name, stock FROM goods WHERE id = ? FOR UPDATE', [goodsId])
|
||
if (rows.length === 0) {
|
||
throw new Error('商品不存在')
|
||
}
|
||
if (rows[0].stock < qty) {
|
||
throw new Error(`${rows[0].name} 库存不足(当前库存: ${rows[0].stock},需要: ${qty})`)
|
||
}
|
||
|
||
if (orderStatus === 'completed') {
|
||
await conn.execute('UPDATE goods SET stock = stock - ?, sales = COALESCE(sales, 0) + ? WHERE id = ?', [qty, qty, goodsId])
|
||
await conn.execute('UPDATE stock SET quantity = quantity - ? WHERE goods_id = ?', [qty, goodsId])
|
||
}
|
||
}
|
||
|
||
await conn.execute(
|
||
'INSERT INTO orders (id, user_id, status, total_price, cart, user_info) VALUES (?, ?, ?, ?, ?, ?)',
|
||
[orderId, userId || null, orderStatus, calculatedTotalPrice, typeof cart === 'string' ? cart : JSON.stringify(cart), userInfo]
|
||
)
|
||
await orderService.insertOrderItems(conn, orderId, cart)
|
||
})
|
||
|
||
const orders = await query('SELECT * FROM orders WHERE id = ?', [orderId])
|
||
const created = orders[0]
|
||
created.items = await orderService.getOrderItems(orderId)
|
||
|
||
if (created.status === 'completed') {
|
||
setImmediate(() => orderService.processOrderComplete(created))
|
||
}
|
||
|
||
ctx.body = {
|
||
code: 200,
|
||
data: created
|
||
}
|
||
}
|
||
|
||
async function updateOrder(ctx) {
|
||
const operator = await requireAuth(ctx)
|
||
if (!operator) return
|
||
|
||
const orderId = ctx.params.id
|
||
const body = allowedUpdateFields(ctx.request.body)
|
||
|
||
const orders = await query('SELECT * FROM orders WHERE id = ?', [orderId])
|
||
|
||
if (orders.length > 0) {
|
||
const order = orders[0]
|
||
|
||
if (operator.role !== 1 && order.user_id !== operator.id) {
|
||
ctx.body = { code: 403, message: '无权修改此订单' }
|
||
return
|
||
}
|
||
|
||
const prevStatus = order.status
|
||
const updateFields = []
|
||
const updateParams = []
|
||
|
||
if (body.status !== undefined) {
|
||
updateFields.push('status = ?')
|
||
updateParams.push(body.status)
|
||
}
|
||
const totalPrice = body.total_price !== undefined ? body.total_price : body.totalPrice
|
||
if (totalPrice !== undefined) {
|
||
updateFields.push('total_price = ?')
|
||
updateParams.push(totalPrice)
|
||
}
|
||
if (body.cart !== undefined) {
|
||
const cartStr = typeof body.cart === 'string' ? body.cart : JSON.stringify(body.cart)
|
||
updateFields.push('cart = ?')
|
||
updateParams.push(cartStr)
|
||
}
|
||
|
||
if (updateFields.length === 0) {
|
||
ctx.body = { code: 400, message: '没有需要更新的字段' }
|
||
return
|
||
}
|
||
|
||
const newStatus = body.status !== undefined ? body.status : prevStatus
|
||
|
||
await transaction(async (conn) => {
|
||
if (newStatus === 'completed' && prevStatus !== 'completed') {
|
||
const items = body.cart !== undefined
|
||
? (typeof body.cart === 'string' ? JSON.parse(body.cart) : body.cart)
|
||
: JSON.parse(order.cart || '[]')
|
||
|
||
for (const item of items) {
|
||
const goodsId = item.id || item.goods_id || item.goodsId
|
||
if (!goodsId) continue
|
||
const qty = item.pricingType === 2 ? 1 : (item.quantity || 1)
|
||
|
||
const [rows] = await conn.execute('SELECT stock FROM goods WHERE id = ? FOR UPDATE', [goodsId])
|
||
if (rows.length > 0 && rows[0].stock >= qty) {
|
||
await conn.execute('UPDATE goods SET stock = stock - ?, sales = COALESCE(sales, 0) + ? WHERE id = ?', [qty, qty, goodsId])
|
||
await conn.execute('UPDATE stock SET quantity = quantity - ? WHERE goods_id = ?', [qty, goodsId])
|
||
}
|
||
}
|
||
}
|
||
|
||
updateParams.push(orderId)
|
||
await conn.execute(`UPDATE orders SET ${updateFields.join(', ')} WHERE id = ?`, updateParams)
|
||
|
||
if (body.cart !== undefined) {
|
||
await conn.execute('DELETE FROM order_items WHERE order_id = ?', [orderId])
|
||
await orderService.insertOrderItems(conn, orderId, body.cart)
|
||
}
|
||
})
|
||
|
||
const updatedOrders = await query('SELECT * FROM orders WHERE id = ?', [orderId])
|
||
const completed = updatedOrders[0]
|
||
completed.items = await orderService.getOrderItems(orderId)
|
||
|
||
if (completed.status === 'completed' && prevStatus !== 'completed') {
|
||
setImmediate(() => orderService.processOrderComplete(completed))
|
||
}
|
||
|
||
// 订单状态变更时发送微信订阅消息通知
|
||
if (newStatus !== prevStatus && completed.user_id) {
|
||
setImmediate(() => orderService.sendWechatNotification(completed.user_id, completed.id, newStatus, completed.total_price))
|
||
}
|
||
|
||
ctx.body = {
|
||
code: 200,
|
||
data: completed
|
||
}
|
||
} else {
|
||
ctx.body = {
|
||
code: 404,
|
||
message: '订单不存在'
|
||
}
|
||
}
|
||
}
|
||
|
||
module.exports = {
|
||
getOrders,
|
||
getOrderById,
|
||
createOrder,
|
||
updateOrder
|
||
}
|