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:
freedakgmail
2026-07-27 09:44:21 +08:00
parent 9739c4f29b
commit f5fa9ace88
9 changed files with 29 additions and 42 deletions
+2 -2
View File
@@ -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())
+7 -33
View File
@@ -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) }
})
+2 -2
View File
@@ -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,