fix: P2/P3审计问题修复 + 更新审计文档移除已修复问题

- 修复schema前缀:确认central_kitchen/mv_distribution/mv_dish_sales等表在public schema,撤销错误的analytics.前缀
- 移除API内部物化视图刷新:mv_distribution_monthly/mv_dish_sales_monthly改为console.warn
- 修复BOM树节点key:使用path字段替代material_code+level确保唯一性
- 修复HR人才矩阵potential_score:基于v3_learning_record动态计算替代硬编码60
- 修复StoreDetailPage公司均值:优先使用API返回的companyAvg
- 修复前端字段名匹配:discount_rate_pct/check_comment等
- 添加explicit column list和LIMIT到大型查询
- 更新数据溯源审计.md:移除全部52个已修复/确认无需修复的问题条目
This commit is contained in:
freedakgmail
2026-08-11 22:00:58 +08:00
parent 915345bfd6
commit 8f5271fcc6
15 changed files with 3399 additions and 150 deletions
+13 -2
View File
@@ -376,6 +376,7 @@ router.get('/efficiency', async (req: AuthRequest, res) => {
round(bill_count::numeric, 0) AS bill_count,
round(received / nullif(bill_count, 0)::numeric, 2) AS avg_ticket_size,
CASE
WHEN received_per_sqm < 500 THEN '坪效极低'
WHEN received_per_sqm < 1000 THEN '坪效偏低'
WHEN received_per_sqm < 2000 THEN '坪效一般'
WHEN received_per_sqm < 4000 THEN '坪效良好'
@@ -438,7 +439,7 @@ router.get('/fixed-variable', async (req: AuthRequest, res) => {
round((rent_expense + dorm_expense + repair_clean_expense * 0.5) / nullif(received, 0) * 100::numeric, 2) AS fixed_rate_pct,
round((wage_expense + utility_expense + delivery_commission_expense + card_fee_expense + repair_clean_expense * 0.5) / nullif(received, 0) * 100::numeric, 2) AS variable_rate_pct,
round((received - actual_food_cost - wage_expense - utility_expense - delivery_commission_expense - card_fee_expense - repair_clean_expense * 0.5)::numeric, 2) AS contribution_after_variable,
round((rent_expense + dorm_expense + repair_clean_expense * 0.5)::numeric, 2) AS break_even_sales
round((rent_expense + dorm_expense + repair_clean_expense * 0.5) / nullif(1 - actual_food_cost / nullif(received, 0), 0)::numeric, 2) AS break_even_sales
FROM analytics.mv_store_operating_expense_monthly
${where}
ORDER BY ${sortCol} ${order} NULLS LAST LIMIT $${params.length + 1} OFFSET $${params.length + 2}
@@ -539,6 +540,16 @@ router.get('/loss-diagnosis', async (req: AuthRequest, res) => {
const countResult = await query(`SELECT count(*) FROM analytics.mv_store_operating_expense_monthly ${where}`, params)
const total = countResult.rows[0].count
// 全量统计(不受分页影响)
const evalStats = await query(`
SELECT
count(*) FILTER (WHERE actual_store_contribution_rate_pct < -20) AS p0_count,
count(*) FILTER (WHERE actual_store_contribution_rate_pct >= -20 AND actual_store_contribution_rate_pct < -5) AS p1_count,
round(sum(actual_store_contribution)::numeric, 2) AS total_loss
FROM analytics.mv_store_operating_expense_monthly
WHERE report_month = $1::date AND actual_store_contribution <= 0 AND received > 0
`, [month])
const validSorts = ['actual_store_contribution', 'actual_store_contribution_rate_pct', 'received', 'wage_rate_pct', 'rent_rate_pct', 'utility_rate_pct', 'received_per_sqm', 'operating_expense_rate_pct']
const sortCol = validSorts.includes(sort) ? sort : 'actual_store_contribution'
@@ -737,7 +748,7 @@ router.get('/loss-diagnosis', async (req: AuthRequest, res) => {
}
})
sendSuccess(res, rows, { page, pageSize, total })
sendSuccess(res, rows, { page, pageSize, total, evalStats: evalStats.rows[0] })
} catch (err: any) {
sendError(res, err.message)
}