Ai config
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
const { query } = require('../config/database')
|
||||
const { COST_RATIO, PROFIT_RATIO } = require('../config/constants')
|
||||
|
||||
async function getSalesTrend(ctx) {
|
||||
const days = parseInt(ctx.query.days) || 30
|
||||
const group = ctx.query.group || 'day'
|
||||
|
||||
let dateFormat
|
||||
if (group === 'week') {
|
||||
dateFormat = 'DATE_FORMAT(created_at, \'%x-W%v\')'
|
||||
} else if (group === 'month') {
|
||||
dateFormat = 'DATE_FORMAT(created_at, \'%Y-%m\')'
|
||||
} else {
|
||||
dateFormat = 'DATE(created_at)'
|
||||
}
|
||||
|
||||
const rows = await query(
|
||||
`SELECT ${dateFormat} as period,
|
||||
COUNT(*) as order_count,
|
||||
COALESCE(SUM(total_price), 0) as total_sales
|
||||
FROM orders
|
||||
WHERE status IN ('paid', 'completed')
|
||||
AND created_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
|
||||
GROUP BY period
|
||||
ORDER BY period ASC`,
|
||||
[days]
|
||||
)
|
||||
|
||||
ctx.body = { code: 200, data: rows }
|
||||
}
|
||||
|
||||
async function getHotProducts(ctx) {
|
||||
const limit = parseInt(ctx.query.limit) || 10
|
||||
|
||||
const rows = await query(
|
||||
`SELECT id, name, price, sales, stock,
|
||||
COALESCE(s.quantity, 0) as stock_qty
|
||||
FROM goods
|
||||
LEFT JOIN stock s ON goods.id = s.goods_id
|
||||
ORDER BY sales DESC
|
||||
LIMIT ?`,
|
||||
[limit]
|
||||
)
|
||||
|
||||
ctx.body = { code: 200, data: rows }
|
||||
}
|
||||
|
||||
async function getProfitAnalysis(ctx) {
|
||||
const days = parseInt(ctx.query.days) || 30
|
||||
|
||||
const revenueRows = await query(
|
||||
`SELECT DATE(created_at) as date,
|
||||
COUNT(*) as order_count,
|
||||
COALESCE(SUM(total_price), 0) as revenue
|
||||
FROM orders
|
||||
WHERE status IN ('paid', 'completed')
|
||||
AND created_at >= DATE_SUB(NOW(), INTERVAL ? DAY)
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date ASC`,
|
||||
[days]
|
||||
)
|
||||
|
||||
const avgCost = await query(
|
||||
`SELECT COALESCE(AVG(purchase_price), 0) as avg_cost
|
||||
FROM purchase_items`
|
||||
)
|
||||
|
||||
const costPerUnit = parseFloat(avgCost[0]?.avg_cost || 0)
|
||||
|
||||
const enriched = revenueRows.map(row => ({
|
||||
...row,
|
||||
revenue: parseFloat(row.revenue),
|
||||
estimated_cost: parseFloat(row.revenue) * COST_RATIO,
|
||||
estimated_profit: parseFloat(row.revenue) * PROFIT_RATIO
|
||||
}))
|
||||
|
||||
const totalRevenue = enriched.reduce((s, r) => s + r.revenue, 0)
|
||||
const totalCost = enriched.reduce((s, r) => s + r.estimated_cost, 0)
|
||||
const totalProfit = enriched.reduce((s, r) => s + r.estimated_profit, 0)
|
||||
|
||||
ctx.body = {
|
||||
code: 200,
|
||||
data: {
|
||||
days,
|
||||
summary: {
|
||||
total_revenue: totalRevenue,
|
||||
total_cost: totalCost,
|
||||
total_profit: totalProfit,
|
||||
profit_margin: totalRevenue > 0 ? ((totalProfit / totalRevenue) * 100).toFixed(1) : '0.0',
|
||||
avg_purchase_cost: costPerUnit
|
||||
},
|
||||
details: enriched
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getInventoryTurnover(ctx) {
|
||||
const rows = await query(
|
||||
`SELECT g.id, g.name, g.price, g.sales,
|
||||
COALESCE(s.quantity, 0) as stock_qty,
|
||||
CASE
|
||||
WHEN COALESCE(s.quantity, 0) <= 0 THEN g.sales
|
||||
ELSE ROUND(g.sales / s.quantity, 2)
|
||||
END as turnover_ratio
|
||||
FROM goods g
|
||||
LEFT JOIN stock s ON g.id = s.goods_id
|
||||
ORDER BY turnover_ratio DESC`
|
||||
)
|
||||
|
||||
const lowStock = rows.filter(r => r.stock_qty <= 5)
|
||||
const outOfStock = rows.filter(r => r.stock_qty <= 0)
|
||||
const slowMoving = rows.filter(r => r.turnover_ratio < 0.1 && r.sales > 0)
|
||||
|
||||
ctx.body = {
|
||||
code: 200,
|
||||
data: {
|
||||
total_items: rows.length,
|
||||
low_stock_count: lowStock.length,
|
||||
out_of_stock_count: outOfStock.length,
|
||||
slow_moving_count: slowMoving.length,
|
||||
items: rows
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getSalesTrend,
|
||||
getHotProducts,
|
||||
getProfitAnalysis,
|
||||
getInventoryTurnover
|
||||
}
|
||||
Reference in New Issue
Block a user