Files
services/controllers/carts.js
T
2026-06-03 14:15:55 +08:00

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
}