234 lines
5.9 KiB
JavaScript
234 lines
5.9 KiB
JavaScript
|
|
const { query, transaction } = require('../config/database')
|
||
|
|
const { sanitizeInt } = require('../utils/validators')
|
||
|
|
|
||
|
|
function currentUserId(ctx) {
|
||
|
|
return ctx.state.user ? ctx.state.user.id : null
|
||
|
|
}
|
||
|
|
|
||
|
|
async function getCart(ctx) {
|
||
|
|
const userId = currentUserId(ctx)
|
||
|
|
if (!userId) {
|
||
|
|
ctx.status = 401
|
||
|
|
ctx.body = { code: 401, message: '未登录' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
const sql = `
|
||
|
|
SELECT
|
||
|
|
c.id,
|
||
|
|
c.goods_id,
|
||
|
|
c.quantity,
|
||
|
|
c.weight,
|
||
|
|
c.selected,
|
||
|
|
g.name as goods_name,
|
||
|
|
g.price,
|
||
|
|
g.unit,
|
||
|
|
g.stock,
|
||
|
|
g.images,
|
||
|
|
g.pricing_type
|
||
|
|
FROM carts c
|
||
|
|
LEFT JOIN goods g ON c.goods_id = g.id
|
||
|
|
WHERE c.user_id = ? AND g.status != 0
|
||
|
|
`
|
||
|
|
|
||
|
|
const items = await query(sql, [userId])
|
||
|
|
|
||
|
|
const cartItems = items.map(item => {
|
||
|
|
let images = []
|
||
|
|
try {
|
||
|
|
images = item.images ? JSON.parse(item.images) : []
|
||
|
|
} catch {}
|
||
|
|
|
||
|
|
return {
|
||
|
|
id: item.goods_id,
|
||
|
|
name: item.goods_name,
|
||
|
|
price: parseFloat(item.price),
|
||
|
|
unit: item.unit,
|
||
|
|
stock: item.stock,
|
||
|
|
images: images,
|
||
|
|
pricingType: item.pricing_type,
|
||
|
|
quantity: item.quantity,
|
||
|
|
weight: item.weight,
|
||
|
|
selected: item.selected === 1
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
ctx.body = { code: 200, data: cartItems }
|
||
|
|
}
|
||
|
|
|
||
|
|
async function addToCart(ctx) {
|
||
|
|
const userId = currentUserId(ctx)
|
||
|
|
if (!userId) {
|
||
|
|
ctx.status = 401
|
||
|
|
ctx.body = { code: 401, message: '未登录' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
const { goodsId, quantity, weight } = ctx.request.body || {}
|
||
|
|
|
||
|
|
if (!goodsId) {
|
||
|
|
ctx.body = { code: 400, message: '缺少商品ID' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
const qty = sanitizeInt(quantity, 1, 1, 9999)
|
||
|
|
if (qty === null) {
|
||
|
|
ctx.body = { code: 400, message: '数量必须是 1-9999 之间的整数' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
const wgt = weight !== undefined && weight !== null ? parseFloat(weight) : null
|
||
|
|
if (wgt !== null && (isNaN(wgt) || wgt < 0)) {
|
||
|
|
ctx.body = { code: 400, message: '重量必须为非负数' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
await transaction(async (conn) => {
|
||
|
|
const [rows] = await conn.execute('SELECT * FROM carts WHERE user_id = ? AND goods_id = ? FOR UPDATE', [userId, goodsId])
|
||
|
|
if (rows.length > 0) {
|
||
|
|
await conn.execute('UPDATE carts SET quantity = quantity + ?, weight = ?, updated_at = NOW() WHERE user_id = ? AND goods_id = ?', [qty, wgt, userId, goodsId])
|
||
|
|
} else {
|
||
|
|
await conn.execute('INSERT INTO carts (user_id, goods_id, quantity, weight) VALUES (?, ?, ?, ?)', [userId, goodsId, qty, wgt])
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
ctx.body = { code: 200, message: '添加成功' }
|
||
|
|
}
|
||
|
|
|
||
|
|
async function updateCartItem(ctx) {
|
||
|
|
const userId = currentUserId(ctx)
|
||
|
|
if (!userId) {
|
||
|
|
ctx.status = 401
|
||
|
|
ctx.body = { code: 401, message: '未登录' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
const { goodsId, quantity, weight, selected } = ctx.request.body || {}
|
||
|
|
|
||
|
|
if (!goodsId) {
|
||
|
|
ctx.body = { code: 400, message: '缺少商品ID' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
const updates = []
|
||
|
|
const params = []
|
||
|
|
|
||
|
|
if (quantity !== undefined) {
|
||
|
|
const qty = sanitizeInt(quantity, 1, 0, 9999)
|
||
|
|
if (qty === null) {
|
||
|
|
ctx.body = { code: 400, message: '数量必须是 0-9999 之间的整数' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
updates.push('quantity = ?')
|
||
|
|
params.push(qty)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (weight !== undefined) {
|
||
|
|
const wgt = weight === null ? null : parseFloat(weight)
|
||
|
|
if (wgt !== null && (isNaN(wgt) || wgt < 0)) {
|
||
|
|
ctx.body = { code: 400, message: '重量必须为非负数' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
updates.push('weight = ?')
|
||
|
|
params.push(wgt)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (selected !== undefined) {
|
||
|
|
updates.push('selected = ?')
|
||
|
|
params.push(selected ? 1 : 0)
|
||
|
|
}
|
||
|
|
|
||
|
|
if (updates.length === 0) {
|
||
|
|
ctx.body = { code: 400, message: '没有需要更新的字段' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
params.push(userId, goodsId)
|
||
|
|
|
||
|
|
const result = await query(
|
||
|
|
`UPDATE carts SET ${updates.join(', ')}, updated_at = NOW() WHERE user_id = ? AND goods_id = ?`,
|
||
|
|
params
|
||
|
|
)
|
||
|
|
|
||
|
|
if (result.affectedRows === 0) {
|
||
|
|
ctx.body = { code: 404, message: '购物车中不存在该商品' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
ctx.body = { code: 200, message: '更新成功' }
|
||
|
|
}
|
||
|
|
|
||
|
|
async function removeFromCart(ctx) {
|
||
|
|
const userId = currentUserId(ctx)
|
||
|
|
if (!userId) {
|
||
|
|
ctx.status = 401
|
||
|
|
ctx.body = { code: 401, message: '未登录' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
const { goodsId } = ctx.request.body || {}
|
||
|
|
|
||
|
|
if (!goodsId) {
|
||
|
|
ctx.body = { code: 400, message: '缺少商品ID' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
await query('DELETE FROM carts WHERE user_id = ? AND goods_id = ?', [userId, goodsId])
|
||
|
|
ctx.body = { code: 200, message: '删除成功' }
|
||
|
|
}
|
||
|
|
|
||
|
|
async function clearCart(ctx) {
|
||
|
|
const userId = currentUserId(ctx)
|
||
|
|
if (!userId) {
|
||
|
|
ctx.status = 401
|
||
|
|
ctx.body = { code: 401, message: '未登录' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
await query('DELETE FROM carts WHERE user_id = ?', [userId])
|
||
|
|
ctx.body = { code: 200, message: '清空成功' }
|
||
|
|
}
|
||
|
|
|
||
|
|
async function syncCart(ctx) {
|
||
|
|
const userId = currentUserId(ctx)
|
||
|
|
if (!userId) {
|
||
|
|
ctx.status = 401
|
||
|
|
ctx.body = { code: 401, message: '未登录' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
const { cart } = ctx.request.body || {}
|
||
|
|
|
||
|
|
if (!Array.isArray(cart)) {
|
||
|
|
ctx.body = { code: 400, message: '购物车数据格式错误' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if (cart.length > 100) {
|
||
|
|
ctx.body = { code: 400, message: '购物车商品数不能超过 100' }
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
await transaction(async (conn) => {
|
||
|
|
await conn.execute('DELETE FROM carts WHERE user_id = ?', [userId])
|
||
|
|
if (cart.length > 0) {
|
||
|
|
const values = cart.map(item => [
|
||
|
|
userId,
|
||
|
|
item.id || item.goods_id,
|
||
|
|
sanitizeInt(item.quantity, 1, 1, 9999) || 1,
|
||
|
|
item.weight || null,
|
||
|
|
1
|
||
|
|
])
|
||
|
|
const placeholders = values.map(() => '(?, ?, ?, ?, ?)').join(', ')
|
||
|
|
const flatParams = values.flat()
|
||
|
|
await conn.execute(
|
||
|
|
`INSERT INTO carts (user_id, goods_id, quantity, weight, selected) VALUES ${placeholders}`,
|
||
|
|
flatParams
|
||
|
|
)
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
ctx.body = { code: 200, message: '同步成功' }
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = {
|
||
|
|
getCart,
|
||
|
|
addToCart,
|
||
|
|
updateCartItem,
|
||
|
|
removeFromCart,
|
||
|
|
clearCart,
|
||
|
|
syncCart
|
||
|
|
}
|