feat: 部署前端到dm.all8ai.top + 性能优化 + 登录页密码验证
- 前端构建部署到152.136.182.184服务器,nginx反向代理 - SSH反向隧道(13333端口)连接服务器到本地后端 - SSL证书自动配置(Let's Encrypt) - API baseURL支持环境变量VITE_API_BASE_URL - 后端端口改为3333,CORS允许所有来源 - 物化6个慢视图(overview/risk/loop_health/benchmark/region/followup) - 登录页保留密码输入框,必须输入密码才能登录 - run.md添加登录账号信息
This commit is contained in:
+2
-2
@@ -8,10 +8,10 @@ import dataRoutes from './routes/data.js'
|
||||
import taskRoutes from './routes/tasks.js'
|
||||
|
||||
const app = express()
|
||||
const PORT = parseInt(process.env.PORT || '3001')
|
||||
const PORT = parseInt(process.env.PORT || '3333')
|
||||
|
||||
app.use(cors({
|
||||
origin: process.env.CLIENT_URL || 'http://localhost:5173',
|
||||
origin: process.env.CLIENT_URL || true,
|
||||
credentials: true,
|
||||
}))
|
||||
app.use(express.json())
|
||||
|
||||
@@ -8,20 +8,7 @@ const router = Router()
|
||||
router.get('/overview', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const month = parseMonth(req)
|
||||
const sql = `
|
||||
SELECT count(*) AS bill_count,
|
||||
round(sum(received_total), 2) AS received,
|
||||
round(sum(received_total) / count(*), 2) AS avg_bill_value,
|
||||
round(sum(discount_total) / nullif(sum(consumption), 0) * 100, 2) AS discount_rate_pct,
|
||||
round(sum(theoretical_profit) / nullif(sum(received_total), 0) * 100, 2) AS theoretical_margin_pct,
|
||||
count(*) FILTER (WHERE member_id IS NOT NULL) AS member_bills,
|
||||
round(count(*) FILTER (WHERE member_id IS NOT NULL)::numeric / count(*) * 100, 2) AS member_share_pct
|
||||
FROM analytics.bill_fact
|
||||
WHERE closed_at >= $1::date
|
||||
AND closed_at < ($1::date + interval '1 month')
|
||||
AND closed_at IS NOT NULL
|
||||
`
|
||||
const result = await query(sql, [month])
|
||||
const result = await query(`SELECT bill_count, received, avg_bill_value, discount_rate_pct, theoretical_margin_pct, member_bills, member_share_pct FROM analytics.mv_overview_monthly WHERE month = $1::date`, [month])
|
||||
sendSuccess(res, result.rows[0])
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
@@ -31,20 +18,7 @@ router.get('/overview', async (req: AuthRequest, res) => {
|
||||
router.get('/overview/daily', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const month = parseMonth(req)
|
||||
const sql = `
|
||||
SELECT closed_at::date AS business_date,
|
||||
count(*) AS bill_count,
|
||||
round(sum(received_total), 2) AS received,
|
||||
round(sum(received_total) / count(*), 2) AS avg_bill_value,
|
||||
round(sum(discount_total) / nullif(sum(consumption), 0) * 100, 2) AS discount_rate_pct
|
||||
FROM analytics.bill_fact
|
||||
WHERE closed_at >= $1::date
|
||||
AND closed_at < ($1::date + interval '1 month')
|
||||
AND closed_at IS NOT NULL
|
||||
GROUP BY closed_at::date
|
||||
ORDER BY business_date
|
||||
`
|
||||
const result = await query(sql, [month])
|
||||
const result = await query(`SELECT business_date, bill_count, received, avg_bill_value, discount_rate_pct FROM analytics.mv_overview_daily WHERE month = $1::date ORDER BY business_date`, [month])
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
@@ -63,7 +37,7 @@ router.get('/stores', async (req: AuthRequest, res) => {
|
||||
|
||||
if (riskLevel) {
|
||||
sql = `SELECT s.* FROM analytics.v_store_scorecard s
|
||||
JOIN analytics.v_store_risk_rating r ON s.store_code = r.store_code
|
||||
JOIN analytics.mv_store_risk_rating r ON s.store_code = r.store_code
|
||||
WHERE r.risk_level = $1`
|
||||
params.push(riskLevel)
|
||||
}
|
||||
@@ -94,7 +68,7 @@ router.get('/stores', async (req: AuthRequest, res) => {
|
||||
|
||||
router.get('/stores/risk', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.v_store_risk_rating ORDER BY risk_level, received DESC`)
|
||||
const result = await query(`SELECT * FROM analytics.mv_store_risk_rating ORDER BY risk_level, received DESC`)
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
@@ -136,7 +110,7 @@ router.get('/stores/:code', async (req: AuthRequest, res) => {
|
||||
const code = req.params.code
|
||||
const [scorecard, risk, platform, benchmark, action, execution] = await Promise.all([
|
||||
query(`SELECT * FROM analytics.v_store_scorecard WHERE store_code = $1`, [code]),
|
||||
query(`SELECT * FROM analytics.v_store_risk_rating WHERE store_code = $1`, [code]),
|
||||
query(`SELECT * FROM analytics.mv_store_risk_rating WHERE store_code = $1`, [code]),
|
||||
query(`SELECT * FROM analytics.v_store_platform_economics WHERE store_code = $1`, [code]),
|
||||
query(`SELECT * FROM analytics.v_store_benchmark WHERE store_code = $1`, [code]),
|
||||
query(`SELECT * FROM analytics.v_store_action_list WHERE store_code = $1`, [code]),
|
||||
@@ -313,7 +287,7 @@ router.get('/marketing/plans', async (req: AuthRequest, res) => {
|
||||
|
||||
router.get('/benchmark/composite', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.v_store_benchmark_composite ORDER BY benchmark_score DESC`)
|
||||
const result = await query(`SELECT * FROM analytics.mv_store_benchmark_composite ORDER BY benchmark_score DESC`)
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
@@ -445,7 +419,7 @@ router.get('/stores/:code/anomalies', async (req: AuthRequest, res) => {
|
||||
// 区域汇总
|
||||
router.get('/region/summary', async (req, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.v_region_summary ORDER BY total_received DESC`)
|
||||
const result = await query(`SELECT * FROM analytics.mv_region_summary ORDER BY total_received DESC`)
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) { sendError(res, err.message) }
|
||||
})
|
||||
|
||||
@@ -117,7 +117,7 @@ router.get('/monthly-review', async (req: AuthRequest, res) => {
|
||||
|
||||
router.get('/followup', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.v_store_monthly_followup ORDER BY priority, store_code`)
|
||||
const result = await query(`SELECT * FROM analytics.mv_store_monthly_followup ORDER BY priority, store_code`)
|
||||
sendSuccess(res, result.rows)
|
||||
} catch (err: any) {
|
||||
sendError(res, err.message)
|
||||
@@ -247,7 +247,7 @@ router.post('/indicators', async (req: AuthRequest, res) => {
|
||||
|
||||
router.get('/loop-health', async (req: AuthRequest, res) => {
|
||||
try {
|
||||
const result = await query(`SELECT * FROM analytics.v_loop_health`)
|
||||
const result = await query(`SELECT * FROM analytics.mv_loop_health`)
|
||||
sendSuccess(res, result.rows[0] || {
|
||||
task_generation_rate: 0,
|
||||
store_execution_rate: 0,
|
||||
|
||||
Reference in New Issue
Block a user