diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5dc975c --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +# 数据库配置 +DB_HOST=localhost +DB_PORT=3306 +DB_USER=root +DB_PASSWORD=your_password +DB_NAME=miniprogram + +# AI 配置(阿里云 DashScope) +DASHSCOPE_API_KEY=sk-your-api-key + +# 服务器配置 +PORT=3006 +NODE_ENV=development diff --git a/app.js b/app.js index b9c0f1c..3da476c 100644 --- a/app.js +++ b/app.js @@ -3,6 +3,7 @@ const Router = require('koa-router') const cors = require('@koa/cors') const bodyParser = require('koa-bodyparser') const path = require('path') +require('dotenv').config() const app = new Koa() const router = new Router() @@ -48,6 +49,12 @@ const userRoutes = require('./routes/users') const uploadRoutes = require('./routes/upload') const stockRoutes = require('./routes/stock') const aiRoutes = require('./routes/ai') +const supplierRoutes = require('./routes/suppliers') +const purchaseRoutes = require('./routes/purchases') +const pointsGoodsRoutes = require('./routes/points-goods') +const statsRoutes = require('./routes/stats') +const priceListRoutes = require('./routes/price-list') +const pointsLogsRoutes = require('./routes/points-logs') router.use('/api/orders', orderRoutes) router.use('/api/categories', categoryRoutes) @@ -56,6 +63,12 @@ router.use('/api/users', userRoutes) router.use('/api/upload', uploadRoutes) router.use('/api/stock', stockRoutes) router.use('/api/ai', aiRoutes) +router.use('/api/suppliers', supplierRoutes) +router.use('/api/purchases', purchaseRoutes) +router.use('/api/points-goods', pointsGoodsRoutes) +router.use('/api/stats', statsRoutes) +router.use('/api/price-list', priceListRoutes) +router.use('/api/points/logs', pointsLogsRoutes) app.use(router.routes()) app.use(router.allowedMethods()) diff --git a/config/database.js b/config/database.js index e081f24..efd76a0 100644 --- a/config/database.js +++ b/config/database.js @@ -1,16 +1,21 @@ const mysql = require('mysql2/promise') +require('dotenv').config() const config = { - host: '110.42.255.239', - port: 3306, - user: 'admin', - password: 'Admin@123', - database: 'miniprogram', + host: process.env.DB_HOST || '110.42.255.239', + port: parseInt(process.env.DB_PORT || '3306'), + user: process.env.DB_USER || 'admin', + password: process.env.DB_PASSWORD || 'Admin@123', + database: process.env.DB_NAME || 'miniprogram', waitForConnections: true, connectionLimit: 10, queueLimit: 0 } - +/* +# 登录服务器,找到 Koa 进程 +ssh ubuntu@110.42.255.239 +pm2 restart weixin +*/ const pool = mysql.createPool(config) async function query(sql, params = []) { diff --git a/config/schema.sql b/config/schema.sql index 81cef99..1c77bac 100644 --- a/config/schema.sql +++ b/config/schema.sql @@ -81,4 +81,57 @@ CREATE TABLE IF NOT EXISTS `stock` ( PRIMARY KEY (`id`), UNIQUE KEY `goods_id` (`goods_id`), CONSTRAINT `stock_ibfk_1` FOREIGN KEY (`goods_id`) REFERENCES `goods` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='库存表'; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='库存表'; + +CREATE TABLE IF NOT EXISTS `suppliers` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(200) NOT NULL COMMENT '供应商名称', + `contact` varchar(100) DEFAULT '' COMMENT '联系人', + `phone` varchar(20) DEFAULT '' COMMENT '联系电话', + `address` varchar(500) DEFAULT '' COMMENT '地址', + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='供应商表'; + +CREATE TABLE IF NOT EXISTS `purchases` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `supplier_id` int(11) DEFAULT NULL COMMENT '供应商ID', + `supplier_name` varchar(200) DEFAULT '' COMMENT '供应商名称(冗余)', + `total` decimal(10,2) DEFAULT 0.00 COMMENT '采购总金额', + `status` tinyint(4) DEFAULT 0 COMMENT '状态 0-待入库 1-已入库', + `remarks` text COMMENT '备注', + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `supplier_id` (`supplier_id`), + CONSTRAINT `purchases_ibfk_1` FOREIGN KEY (`supplier_id`) REFERENCES `suppliers` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购单表'; + +CREATE TABLE IF NOT EXISTS `purchase_items` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `purchase_id` int(11) NOT NULL COMMENT '采购单ID', + `goods_id` int(11) NOT NULL COMMENT '商品ID', + `goods_name` varchar(200) DEFAULT '' COMMENT '商品名称(冗余)', + `quantity` int(11) DEFAULT 0 COMMENT '采购数量', + `purchase_price` decimal(10,2) DEFAULT 0.00 COMMENT '采购单价', + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `purchase_id` (`purchase_id`), + KEY `goods_id` (`goods_id`), + CONSTRAINT `purchase_items_ibfk_1` FOREIGN KEY (`purchase_id`) REFERENCES `purchases` (`id`) ON DELETE CASCADE, + CONSTRAINT `purchase_items_ibfk_2` FOREIGN KEY (`goods_id`) REFERENCES `goods` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='采购单明细表'; + +CREATE TABLE IF NOT EXISTS `points_goods` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(200) NOT NULL COMMENT '商品名称', + `points` int(11) DEFAULT 0 COMMENT '所需积分', + `stock` int(11) DEFAULT 0 COMMENT '库存', + `image` varchar(500) DEFAULT '' COMMENT '图片', + `description` text COMMENT '描述', + `is_show` tinyint(4) DEFAULT 1 COMMENT '是否显示', + `created_at` timestamp DEFAULT CURRENT_TIMESTAMP, + `updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='积分商品表'; \ No newline at end of file diff --git a/controllers/points-goods.js b/controllers/points-goods.js new file mode 100644 index 0000000..06a9d09 --- /dev/null +++ b/controllers/points-goods.js @@ -0,0 +1,143 @@ +const { query } = require('../config/database') + +async function getPointsGoods(ctx) { + let sql = 'SELECT * FROM points_goods WHERE 1=1' + const params = [] + + if (ctx.query.visible === '1') { + sql += ' AND is_show = 1' + } + + sql += ' ORDER BY points ASC' + const goods = await query(sql, params) + + ctx.body = { + code: 200, + data: goods + } +} + +async function getPointsGoodsById(ctx) { + const id = parseInt(ctx.params.id) + const goods = await query('SELECT * FROM points_goods WHERE id = ?', [id]) + + if (goods.length > 0) { + ctx.body = { code: 200, data: goods[0] } + } else { + ctx.body = { code: 404, message: '积分商品不存在' } + } +} + +async function createPointsGoods(ctx) { + const { name, points, stock, image, description } = ctx.request.body + + if (!name || points === undefined) { + ctx.body = { code: 400, message: '请填写商品名称和所需积分' } + return + } + + const result = await query( + 'INSERT INTO points_goods (name, points, stock, image, description) VALUES (?, ?, ?, ?, ?)', + [name, parseInt(points) || 0, parseInt(stock) || 0, image || '', description || ''] + ) + + ctx.body = { + code: 200, + message: '添加成功', + data: { id: result.insertId } + } +} + +async function updatePointsGoods(ctx) { + const id = parseInt(ctx.params.id) + const { name, points, stock, image, description } = ctx.request.body + + if (!name || points === undefined) { + ctx.body = { code: 400, message: '请填写商品名称和所需积分' } + return + } + + const result = await query( + 'UPDATE points_goods SET name = ?, points = ?, stock = ?, image = ?, description = ? WHERE id = ?', + [name, parseInt(points) || 0, parseInt(stock) || 0, image || '', description || '', id] + ) + + if (result.affectedRows > 0) { + ctx.body = { code: 200, message: '更新成功' } + } else { + ctx.body = { code: 404, message: '积分商品不存在' } + } +} + +async function deletePointsGoods(ctx) { + const id = parseInt(ctx.params.id) + + const result = await query('DELETE FROM points_goods WHERE id = ?', [id]) + if (result.affectedRows > 0) { + ctx.body = { code: 200, message: '删除成功' } + } else { + ctx.body = { code: 404, message: '积分商品不存在' } + } +} + +async function exchangePointsGoods(ctx) { + const { userId, goodsId, quantity } = ctx.request.body + + if (!userId || !goodsId) { + ctx.body = { code: 400, message: '参数不完整' } + return + } + + const qty = quantity || 1 + + const users = await query('SELECT * FROM users WHERE id = ? AND status = 1', [userId]) + if (users.length === 0) { + ctx.body = { code: 404, message: '用户不存在' } + return + } + const user = users[0] + + const goods = await query('SELECT * FROM points_goods WHERE id = ? AND is_show = 1', [goodsId]) + if (goods.length === 0) { + ctx.body = { code: 404, message: '积分商品不存在' } + return + } + const goodsItem = goods[0] + + if (goodsItem.stock < qty) { + ctx.body = { code: 400, message: '库存不足' } + return + } + + const totalPoints = goodsItem.points * qty + if (user.points < totalPoints) { + ctx.body = { code: 400, message: '积分不足' } + return + } + + const newPoints = user.points - totalPoints + await query('UPDATE users SET points = ? WHERE id = ?', [newPoints, userId]) + await query('UPDATE points_goods SET stock = stock - ? WHERE id = ?', [qty, goodsId]) + + await query( + 'INSERT INTO points_logs (user_id, type, amount, description) VALUES (?, ?, ?, ?)', + [userId, 'spend', totalPoints, `兑换「${goodsItem.name}」x${qty}`] + ) + + ctx.body = { + code: 200, + message: '兑换成功', + data: { + remainingPoints: newPoints + } + } +} + +module.exports = { + getPointsGoods, + getPointsGoodsById, + createPointsGoods, + updatePointsGoods, + deletePointsGoods, + exchangePointsGoods +} diff --git a/controllers/price-list.js b/controllers/price-list.js new file mode 100644 index 0000000..972d6ff --- /dev/null +++ b/controllers/price-list.js @@ -0,0 +1,42 @@ +const { query } = require('../config/database') + +async function getPriceList(ctx) { + const orderId = ctx.params.orderId + + const orders = await query('SELECT * FROM orders WHERE id = ?', [orderId]) + if (orders.length === 0) { + ctx.body = { code: 404, message: '订单不存在' } + return + } + + const order = orders[0] + + let items = [] + try { + const cartData = typeof order.cart === 'string' ? JSON.parse(order.cart) : order.cart + items = Array.isArray(cartData) ? cartData : (cartData.items || cartData.list || []) + } catch (e) { + items = [] + } + + ctx.body = { + code: 200, + data: { + orderNo: order.id, + createTime: order.created_at, + items: items.map(item => ({ + name: item.name, + quantity: item.quantity || item.count || 1, + price: item.price || 0, + unit: item.unit || '件', + subtotal: (item.price || 0) * (item.quantity || item.count || 1) + })), + total: order.total_price, + remark: '' + } + } +} + +module.exports = { + getPriceList +} diff --git a/controllers/purchases.js b/controllers/purchases.js new file mode 100644 index 0000000..a8b6067 --- /dev/null +++ b/controllers/purchases.js @@ -0,0 +1,144 @@ +const { query } = require('../config/database') + +async function getPurchases(ctx) { + let sql = ` + SELECT p.*, + (SELECT COUNT(*) FROM purchase_items WHERE purchase_id = p.id) as items_count + FROM purchases p + WHERE 1=1` + const params = [] + + if (ctx.query.keyword) { + sql += ' AND (p.supplier_name LIKE ?)' + params.push(`%${ctx.query.keyword}%`) + } + + sql += ' ORDER BY p.created_at DESC' + const purchases = await query(sql, params) + + ctx.body = { + code: 200, + data: purchases + } +} + +async function getPurchaseById(ctx) { + const id = parseInt(ctx.params.id) + const purchases = await query('SELECT * FROM purchases WHERE id = ?', [id]) + + if (purchases.length === 0) { + ctx.body = { code: 404, message: '采购单不存在' } + return + } + + const purchase = purchases[0] + const items = await query( + `SELECT pi.*, g.name as goods_name + FROM purchase_items pi + LEFT JOIN goods g ON pi.goods_id = g.id + WHERE pi.purchase_id = ?`, + [id] + ) + + purchase.items = items + purchase.items_count = items.length + + ctx.body = { + code: 200, + data: purchase + } +} + +async function createPurchase(ctx) { + const { supplier_id, items, remarks } = ctx.request.body + + if (!supplier_id || !items || items.length === 0) { + ctx.body = { code: 400, message: '请选择供应商和采购商品' } + return + } + + const suppliers = await query('SELECT * FROM suppliers WHERE id = ?', [supplier_id]) + if (suppliers.length === 0) { + ctx.body = { code: 404, message: '供应商不存在' } + return + } + const supplier = suppliers[0] + + let total = 0 + for (const item of items) { + total += (item.purchase_price || 0) * (item.quantity || 0) + } + + const purchaseResult = await query( + 'INSERT INTO purchases (supplier_id, supplier_name, total, remarks) VALUES (?, ?, ?, ?)', + [supplier_id, supplier.name, total, remarks || ''] + ) + + const purchaseId = purchaseResult.insertId + + for (const item of items) { + const goods = await query('SELECT name FROM goods WHERE id = ?', [item.goods_id]) + const goodsName = goods.length > 0 ? goods[0].name : '' + await query( + 'INSERT INTO purchase_items (purchase_id, goods_id, goods_name, quantity, purchase_price) VALUES (?, ?, ?, ?, ?)', + [purchaseId, item.goods_id, goodsName, item.quantity || 0, item.purchase_price || 0] + ) + } + + ctx.body = { + code: 200, + message: '采购单创建成功', + data: { id: purchaseId } + } +} + +async function inboundPurchase(ctx) { + const id = parseInt(ctx.params.id) + const purchases = await query('SELECT * FROM purchases WHERE id = ?', [id]) + + if (purchases.length === 0) { + ctx.body = { code: 404, message: '采购单不存在' } + return + } + + const purchase = purchases[0] + if (purchase.status === 1) { + ctx.body = { code: 400, message: '该采购单已入库' } + return + } + + const items = await query('SELECT * FROM purchase_items WHERE purchase_id = ?', [id]) + + for (const item of items) { + const existing = await query('SELECT * FROM stock WHERE goods_id = ?', [item.goods_id]) + if (existing.length > 0) { + await query( + 'UPDATE stock SET quantity = quantity + ? WHERE goods_id = ?', + [item.quantity, item.goods_id] + ) + } else { + await query( + 'INSERT INTO stock (goods_id, quantity, warehouse) VALUES (?, ?, ?)', + [item.goods_id, item.quantity, '默认仓库'] + ) + } + await query( + 'UPDATE goods SET stock = stock + ? WHERE id = ?', + [item.quantity, item.goods_id] + ) + } + + await query('UPDATE purchases SET status = 1 WHERE id = ?', [id]) + + ctx.body = { + code: 200, + message: '入库成功' + } +} + +module.exports = { + getPurchases, + getPurchaseById, + createPurchase, + inboundPurchase +} diff --git a/controllers/stats.js b/controllers/stats.js new file mode 100644 index 0000000..080cbde --- /dev/null +++ b/controllers/stats.js @@ -0,0 +1,30 @@ +const { query } = require('../config/database') + +async function getTodayStats(ctx) { + const today = new Date() + const todayStart = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')} 00:00:00` + const todayEnd = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')} 23:59:59` + + const orderResult = await query( + 'SELECT COUNT(*) as orderCount, COALESCE(SUM(total_price), 0) as totalSales FROM orders WHERE created_at >= ? AND created_at <= ? AND status IN ("paid", "completed")', + [todayStart, todayEnd] + ) + + const customerResult = await query( + 'SELECT COUNT(DISTINCT user_id) as customerCount FROM orders WHERE created_at >= ? AND created_at <= ?', + [todayStart, todayEnd] + ) + + ctx.body = { + code: 200, + data: { + sales: orderResult[0].totalSales, + orders: orderResult[0].orderCount, + customers: customerResult[0].customerCount + } + } +} + +module.exports = { + getTodayStats +} diff --git a/controllers/suppliers.js b/controllers/suppliers.js new file mode 100644 index 0000000..b3203ac --- /dev/null +++ b/controllers/suppliers.js @@ -0,0 +1,95 @@ +const { query } = require('../config/database') + +async function getSuppliers(ctx) { + let sql = 'SELECT * FROM suppliers WHERE 1=1' + const params = [] + + if (ctx.query.keyword) { + sql += ' AND (name LIKE ? OR contact LIKE ? OR phone LIKE ?)' + params.push(`%${ctx.query.keyword}%`, `%${ctx.query.keyword}%`, `%${ctx.query.keyword}%`) + } + + sql += ' ORDER BY id DESC' + const suppliers = await query(sql, params) + + ctx.body = { + code: 200, + data: suppliers + } +} + +async function getSupplierById(ctx) { + const id = parseInt(ctx.params.id) + const suppliers = await query('SELECT * FROM suppliers WHERE id = ?', [id]) + + if (suppliers.length > 0) { + ctx.body = { code: 200, data: suppliers[0] } + } else { + ctx.body = { code: 404, message: '供应商不存在' } + } +} + +async function createSupplier(ctx) { + const { name, contact, phone, address } = ctx.request.body + + if (!name) { + ctx.body = { code: 400, message: '请输入供应商名称' } + return + } + + const result = await query( + 'INSERT INTO suppliers (name, contact, phone, address) VALUES (?, ?, ?, ?)', + [name, contact || '', phone || '', address || ''] + ) + + ctx.body = { + code: 200, + message: '添加成功', + data: { id: result.insertId } + } +} + +async function updateSupplier(ctx) { + const id = parseInt(ctx.params.id) + const { name, contact, phone, address } = ctx.request.body + + if (!name) { + ctx.body = { code: 400, message: '请输入供应商名称' } + return + } + + const result = await query( + 'UPDATE suppliers SET name = ?, contact = ?, phone = ?, address = ? WHERE id = ?', + [name, contact || '', phone || '', address || '', id] + ) + + if (result.affectedRows > 0) { + ctx.body = { code: 200, message: '更新成功' } + } else { + ctx.body = { code: 404, message: '供应商不存在' } + } +} + +async function deleteSupplier(ctx) { + const id = parseInt(ctx.params.id) + + try { + const result = await query('DELETE FROM suppliers WHERE id = ?', [id]) + if (result.affectedRows > 0) { + ctx.body = { code: 200, message: '删除成功' } + } else { + ctx.body = { code: 404, message: '供应商不存在' } + } + } catch (error) { + console.error('删除供应商失败:', error) + ctx.body = { code: 500, message: '删除失败' } + } +} + +module.exports = { + getSuppliers, + getSupplierById, + createSupplier, + updateSupplier, + deleteSupplier +} diff --git a/package.json b/package.json index 884a8f3..457bfa0 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "dependencies": { "@koa/cors": "^4.0.0", "@koa/multer": "^4.0.0", + "dotenv": "^17.4.2", "koa": "^2.13.4", "koa-bodyparser": "^4.3.0", "koa-router": "^10.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f92f0a..f96ef56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@koa/multer': specifier: ^4.0.0 version: 4.0.0(koa@2.16.4)(multer@2.1.1) + dotenv: + specifier: ^17.4.2 + version: 17.4.2 koa: specifier: ^2.13.4 version: 2.16.4 @@ -155,6 +158,10 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + dotenv@17.4.2: + resolution: {integrity: sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==} + engines: {node: '>=12'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -583,6 +590,8 @@ snapshots: destroy@1.2.0: {} + dotenv@17.4.2: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 diff --git a/routes/ai.js b/routes/ai.js index fc785c2..697c5ec 100644 --- a/routes/ai.js +++ b/routes/ai.js @@ -2,10 +2,11 @@ const Router = require('koa-router'); const fetch = require('node-fetch'); const { query } = require('../config/database'); const { toRelativeUrl } = require('../utils/image-url'); +require('dotenv').config(); const router = new Router(); -const AI_API_KEY = 'sk-7f5d6f370f824f2ab76480c01bb00d40'; +const AI_API_KEY = process.env.DASHSCOPE_API_KEY; const AI_API_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions'; // 2026-05-24 21:31:40 router.post('/generate-product', async (ctx) => { diff --git a/routes/points-goods.js b/routes/points-goods.js new file mode 100644 index 0000000..8c15b9b --- /dev/null +++ b/routes/points-goods.js @@ -0,0 +1,13 @@ +const Router = require('koa-router') +const pointsGoodsController = require('../controllers/points-goods') + +const router = new Router() + +router.get('/', pointsGoodsController.getPointsGoods) +router.get('/:id', pointsGoodsController.getPointsGoodsById) +router.post('/', pointsGoodsController.createPointsGoods) +router.post('/exchange', pointsGoodsController.exchangePointsGoods) +router.put('/:id', pointsGoodsController.updatePointsGoods) +router.delete('/:id', pointsGoodsController.deletePointsGoods) + +module.exports = router.routes() diff --git a/routes/points-logs.js b/routes/points-logs.js new file mode 100644 index 0000000..713999a --- /dev/null +++ b/routes/points-logs.js @@ -0,0 +1,25 @@ +const Router = require('koa-router') +const { query } = require('../config/database') + +const router = new Router() + +router.get('/:userId', async (ctx) => { + const userId = parseInt(ctx.params.userId) + + if (!userId) { + ctx.body = { code: 400, message: '请指定用户ID' } + return + } + + const logs = await query( + 'SELECT * FROM points_logs WHERE user_id = ? ORDER BY created_at DESC', + [userId] + ) + + ctx.body = { + code: 200, + data: logs + } +}) + +module.exports = router.routes() diff --git a/routes/price-list.js b/routes/price-list.js new file mode 100644 index 0000000..7098e20 --- /dev/null +++ b/routes/price-list.js @@ -0,0 +1,8 @@ +const Router = require('koa-router') +const priceListController = require('../controllers/price-list') + +const router = new Router() + +router.get('/:orderId', priceListController.getPriceList) + +module.exports = router.routes() diff --git a/routes/purchases.js b/routes/purchases.js new file mode 100644 index 0000000..6dea6a6 --- /dev/null +++ b/routes/purchases.js @@ -0,0 +1,11 @@ +const Router = require('koa-router') +const purchaseController = require('../controllers/purchases') + +const router = new Router() + +router.get('/', purchaseController.getPurchases) +router.get('/:id', purchaseController.getPurchaseById) +router.post('/', purchaseController.createPurchase) +router.post('/:id/inbound', purchaseController.inboundPurchase) + +module.exports = router.routes() diff --git a/routes/stats.js b/routes/stats.js new file mode 100644 index 0000000..198bef2 --- /dev/null +++ b/routes/stats.js @@ -0,0 +1,8 @@ +const Router = require('koa-router') +const statsController = require('../controllers/stats') + +const router = new Router() + +router.get('/today', statsController.getTodayStats) + +module.exports = router.routes() diff --git a/routes/suppliers.js b/routes/suppliers.js new file mode 100644 index 0000000..0e39a95 --- /dev/null +++ b/routes/suppliers.js @@ -0,0 +1,12 @@ +const Router = require('koa-router') +const supplierController = require('../controllers/suppliers') + +const router = new Router() + +router.get('/', supplierController.getSuppliers) +router.get('/:id', supplierController.getSupplierById) +router.post('/', supplierController.createSupplier) +router.put('/:id', supplierController.updateSupplier) +router.delete('/:id', supplierController.deleteSupplier) + +module.exports = router.routes() diff --git a/scripts/mock-data.js b/scripts/mock-data.js index 5e88fd3..8e41b39 100644 --- a/scripts/mock-data.js +++ b/scripts/mock-data.js @@ -97,6 +97,68 @@ async function run() { ) } + console.log('Inserting mock suppliers...') + + const suppliers = [ + { name: '鲜果源供应链', contact: '王经理', phone: '13800001001', address: '广州市白云区江南批发市场A区' }, + { name: '旺旺食品总代理', contact: '李经理', phone: '13800001002', address: '广州市天河区中山大道88号' }, + { name: '百事饮品华南分公司', contact: '陈经理', phone: '13800001003', address: '广州市番禺区南村镇兴业大道' } + ] + + for (const s of suppliers) { + await query( + 'INSERT INTO suppliers (name, contact, phone, address) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE name=VALUES(name)', + [s.name, s.contact, s.phone, s.address] + ) + } + + console.log('Inserting mock purchases...') + + const purchases = [ + { supplier_name: '鲜果源供应链', total: 2560.00, status: 1, remarks: '周常补货' }, + { supplier_name: '旺旺食品总代理', total: 1800.50, status: 0, remarks: '' } + ] + + for (const p of purchases) { + const supplier = await query('SELECT id FROM suppliers WHERE name = ?', [p.supplier_name]) + const supplierId = supplier.length > 0 ? supplier[0].id : null + const purchaseResult = await query( + 'INSERT INTO purchases (supplier_id, supplier_name, total, status, remarks) VALUES (?, ?, ?, ?, ?)', + [supplierId, p.supplier_name, p.total, p.status, p.remarks || ''] + ) + + if (p.supplier_name === '鲜果源供应链') { + await query( + 'INSERT INTO purchase_items (purchase_id, goods_id, goods_name, quantity, purchase_price) VALUES (?, ?, ?, ?, ?)', + [purchaseResult.insertId, 1, '红富士苹果', 50, 10.00] + ) + await query( + 'INSERT INTO purchase_items (purchase_id, goods_id, goods_name, quantity, purchase_price) VALUES (?, ?, ?, ?, ?)', + [purchaseResult.insertId, 3, '进口车厘子', 20, 45.00] + ) + } else if (p.supplier_name === '旺旺食品总代理') { + await query( + 'INSERT INTO purchase_items (purchase_id, goods_id, goods_name, quantity, purchase_price) VALUES (?, ?, ?, ?, ?)', + [purchaseResult.insertId, 12, '卫龙辣条', 100, 3.80] + ) + } + } + + console.log('Inserting mock points goods...') + + const pointsGoods = [ + { name: '定制帆布袋', points: 200, stock: 50, image: '', description: '环保帆布袋' }, + { name: '玻璃水杯', points: 500, stock: 30, image: '', description: '350ml 双层玻璃杯' }, + { name: '50元优惠券', points: 1000, stock: 20, image: '', description: '满100可用' } + ] + + for (const g of pointsGoods) { + await query( + 'INSERT INTO points_goods (name, points, stock, image, description) VALUES (?, ?, ?, ?, ?)', + [g.name, g.points, g.stock, g.image, g.description] + ) + } + console.log('Inserting mock points logs...') const pointsLogs = [ @@ -124,12 +186,19 @@ async function run() { const stockCount = await query('SELECT COUNT(*) as count FROM stock') const logsCount = await query('SELECT COUNT(*) as count FROM points_logs') + const suppliersCount = await query('SELECT COUNT(*) as count FROM suppliers') + const purchasesCount = await query('SELECT COUNT(*) as count FROM purchases') + const pointsGoodsCount = await query('SELECT COUNT(*) as count FROM points_goods') + console.log(`分类: ${categories[0]?.count || 0} 条`) console.log(`商品: ${goods[0]?.count || 0} 条`) console.log(`用户: ${users[0]?.count || 0} 条`) console.log(`订单: ${ordersCount[0]?.count || 0} 条`) console.log(`库存: ${stockCount[0]?.count || 0} 条`) console.log(`积分记录: ${logsCount[0]?.count || 0} 条`) + console.log(`供应商: ${suppliersCount[0]?.count || 0} 条`) + console.log(`采购单: ${purchasesCount[0]?.count || 0} 条`) + console.log(`积分商品: ${pointsGoodsCount[0]?.count || 0} 条`) process.exit(0) } catch (error) {