const { query, transaction } = require('../config/database') const { paginate } = require('../utils/pagination') async function ensureStockLogTable() { await query(` CREATE TABLE IF NOT EXISTS stock_logs ( id INT NOT NULL AUTO_INCREMENT, goods_id INT NOT NULL, change_type VARCHAR(20) NOT NULL COMMENT 'inbound/adjust/purchase/sale', delta INT NOT NULL, quantity_after INT NOT NULL, operator_id INT, remark VARCHAR(255), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY goods_id (goods_id), KEY created_at (created_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='库存流水表' `) } // 获取库存列表 async function getStockList(ctx) { const keyword = ctx.query.keyword const threshold = parseInt(ctx.query.threshold) || 0 let sql = ` SELECT s.id, s.goods_id, g.name as goods_name, g.images as goods_image, g.unit as goods_unit, s.quantity, s.warehouse FROM stock s LEFT JOIN goods g ON s.goods_id = g.id WHERE 1=1 ` const params = [] if (keyword) { sql += ' AND (g.name LIKE ? OR g.barcode LIKE ?)' params.push(`%${keyword}%`, `%${keyword}%`) } if (threshold > 0) { sql += ' AND s.quantity <= ?' params.push(threshold) } sql += ' ORDER BY s.quantity ASC' const result = await paginate(query, sql, params, ctx.query.page, ctx.query.pageSize) ctx.body = { code: 200, ...result } } // 调整库存 async function adjustStock(ctx) { const operator = ctx.state.user if (!operator) { ctx.status = 401 ctx.body = { code: 401, message: '未登录' } return } const goodsId = parseInt(ctx.params.id) const { quantity, type, remark } = ctx.request.body || {} const qty = parseInt(quantity) if (!qty || qty <= 0 || qty > 100000) { ctx.body = { code: 400, message: '请输入 1-100000 之间的整数' } return } if (type !== 'add' && type !== 'sub') { ctx.body = { code: 400, message: 'type 必须是 add 或 sub' } return } await ensureStockLogTable() try { const newQuantity = await transaction(async (conn) => { const [rows] = await conn.execute('SELECT * FROM stock WHERE goods_id = ? FOR UPDATE', [goodsId]) let current = rows[0] if (!current) { if (type === 'sub') { throw new Error('库存不足,无法减少') } await conn.execute( 'INSERT INTO stock (goods_id, quantity, warehouse) VALUES (?, ?, ?)', [goodsId, qty, '默认仓库'] ) return qty } const delta = type === 'add' ? qty : -qty const next = current.quantity + delta if (next < 0) throw new Error('库存不足,无法减少') await conn.execute('UPDATE stock SET quantity = ? WHERE goods_id = ?', [next, goodsId]) await conn.execute( 'UPDATE goods SET stock = ? WHERE id = ?', [Math.max(0, next), goodsId] ) await conn.execute( 'INSERT INTO stock_logs (goods_id, change_type, delta, quantity_after, operator_id, remark) VALUES (?, ?, ?, ?, ?, ?)', [goodsId, 'adjust', delta, next, operator.id, remark || '库存调整'] ) return next }) const [updated] = await query('SELECT * FROM stock WHERE goods_id = ?', [goodsId]) ctx.body = { code: 200, data: updated, message: '库存调整成功', newQuantity } } catch (error) { ctx.status = 400 ctx.body = { code: 400, message: error.message || '调整失败' } } } // 获取单个商品的库存 async function getStockByGoodsId(ctx) { const goodsId = ctx.params.id const stockRecords = await query('SELECT * FROM stock WHERE goods_id = ?', [goodsId]) if (stockRecords.length > 0) { ctx.body = { code: 200, data: stockRecords[0] } } else { ctx.body = { code: 200, data: { goods_id: goodsId, quantity: 0, warehouse: '默认仓库' } } } } module.exports = { getStockList, adjustStock, getStockByGoodsId }