fix: 新增态 效果

This commit is contained in:
Daniel
2026-03-05 15:53:10 +08:00
parent a3bf8abda5
commit af59d6367f
16 changed files with 1334 additions and 113 deletions

Binary file not shown.

View File

View File

@@ -291,6 +291,25 @@ function runMigrations(db) {
exec('ALTER TABLE map_strike_line ADD COLUMN struck_at TEXT')
}
} catch (_) {}
try {
exec(`
CREATE TABLE IF NOT EXISTS animation_config (
id INTEGER PRIMARY KEY CHECK (id = 1),
strike_cutoff_days INTEGER NOT NULL DEFAULT 5,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
INSERT OR IGNORE INTO animation_config (id, strike_cutoff_days) VALUES (1, 5);
`)
} catch (_) {}
try {
exec(`
CREATE TABLE IF NOT EXISTS war_map_config (
id INTEGER PRIMARY KEY CHECK (id = 1),
config TEXT NOT NULL,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
`)
} catch (_) {}
}
async function initDb() {

View File

@@ -191,6 +191,41 @@ router.get('/stats', (req, res) => {
}
})
// 战区地图配置:钳形轴线、以黎轴线、防御线路径,供前端 useWarMapData 拉取;新增/修改数据落库
router.get('/war-map-config', (req, res) => {
try {
const row = db.prepare('SELECT config FROM war_map_config WHERE id = 1').get()
if (!row || !row.config) {
return res.json({})
}
const data = JSON.parse(row.config)
res.json(data)
} catch (err) {
console.error(err)
res.status(500).json({ error: err.message })
}
})
router.put('/war-map-config', requireAdmin, (req, res) => {
try {
const body = req.body || {}
const pincerAxes = Array.isArray(body.pincerAxes) ? body.pincerAxes : null
const israelLebanonAxis = body.israelLebanonAxis && typeof body.israelLebanonAxis === 'object' ? body.israelLebanonAxis : null
const defenseLinePath = Array.isArray(body.defenseLinePath) ? body.defenseLinePath : null
if (!pincerAxes?.length || !israelLebanonAxis || !defenseLinePath?.length) {
return res.status(400).json({ error: 'pincerAxes (array), israelLebanonAxis (object), defenseLinePath (array) required' })
}
const config = JSON.stringify({ pincerAxes, israelLebanonAxis, defenseLinePath })
db.prepare(
'INSERT INTO war_map_config (id, config, updated_at) VALUES (1, ?, datetime(\'now\')) ON CONFLICT(id) DO UPDATE SET config = excluded.config, updated_at = datetime(\'now\')'
).run(config)
res.json({ ok: true })
} catch (err) {
console.error(err)
res.status(500).json({ error: err.message })
}
})
router.get('/events', (req, res) => {
try {
const s = getSituation()
@@ -225,9 +260,13 @@ router.get('/edit/raw', (req, res) => {
const summaryUs = db.prepare('SELECT * FROM force_summary WHERE side = ?').get('us')
const summaryIr = db.prepare('SELECT * FROM force_summary WHERE side = ?').get('iran')
let displayStats = null
let animationConfig = null
try {
displayStats = db.prepare('SELECT override_enabled, viewers, cumulative, share_count, like_count, feedback_count FROM display_stats WHERE id = 1').get()
} catch (_) {}
try {
animationConfig = db.prepare('SELECT strike_cutoff_days FROM animation_config WHERE id = 1').get()
} catch (_) {}
const realCumulative = db.prepare('SELECT total FROM visitor_count WHERE id = 1').get()?.total ?? 0
const realShare = db.prepare('SELECT total FROM share_count WHERE id = 1').get()?.total ?? 0
const liveViewers = db.prepare(
@@ -251,6 +290,9 @@ router.get('/edit/raw', (req, res) => {
likeCount: displayStats?.like_count ?? realLikeCount,
feedbackCount: displayStats?.feedback_count ?? realFeedback,
},
animationConfig: {
strikeCutoffDays: animationConfig?.strike_cutoff_days ?? 5,
},
})
} catch (err) {
console.error(err)
@@ -438,4 +480,20 @@ router.put('/edit/display-stats', (req, res) => {
}
})
router.put('/edit/animation-config', (req, res) => {
try {
const body = req.body || {}
const v = body.strikeCutoffDays
const n = Math.max(1, parseInt(v, 10) || 0)
if (!Number.isFinite(n)) return res.status(400).json({ error: 'strikeCutoffDays must be number' })
db.prepare('INSERT OR IGNORE INTO animation_config (id, strike_cutoff_days) VALUES (1, 5)').run()
db.prepare('UPDATE animation_config SET strike_cutoff_days = ?, updated_at = datetime(\'now\') WHERE id = 1').run(n)
broadcastAfterEdit(req)
res.json({ ok: true })
} catch (err) {
console.error(err)
res.status(400).json({ error: err.message })
}
})
module.exports = router

View File

@@ -190,7 +190,16 @@ function seed() {
[46.42, 33.64, '伊拉姆导弹阵地', '2026-02-28T02:40:00.000Z'],
[48.35, 33.48, '霍拉马巴德储备库', '2026-02-28T02:42:00.000Z'],
]
// 以色列攻击黎巴嫩(真主党目标),时间在伊朗反击之后 14:0014:40
const israelLebanonTargets = [
[35.5, 33.86, '贝鲁特南郊指挥所', '2026-02-28T14:00:00.000Z'],
[35.32, 33.34, '利塔尼弹药库', '2026-02-28T14:10:00.000Z'],
[36.2, 34.01, '巴勒贝克后勤枢纽', '2026-02-28T14:20:00.000Z'],
[35.19, 33.27, '提尔海岸阵地', '2026-02-28T14:30:00.000Z'],
[36.38, 34.39, '赫尔梅勒无人机阵地', '2026-02-28T14:40:00.000Z'],
]
israelTargets.forEach(([lng, lat, name, struckAt]) => insertStrikeLine.run('israel', lng, lat, name, struckAt))
israelLebanonTargets.forEach(([lng, lat, name, struckAt]) => insertStrikeLine.run('israel', lng, lat, name, struckAt))
lincolnTargets.forEach(([lng, lat, name, struckAt]) => insertStrikeLine.run('lincoln', lng, lat, name, struckAt))
fordTargets.forEach(([lng, lat, name, struckAt]) => insertStrikeLine.run('ford', lng, lat, name, struckAt))
@@ -229,6 +238,28 @@ function seed() {
const insertUpdate = db.prepare('INSERT INTO situation_update (id, timestamp, category, summary, severity) VALUES (?, ?, ?, ?, ?)')
updateRows.forEach((row) => insertUpdate.run(...row))
try {
db.prepare(
'INSERT OR REPLACE INTO animation_config (id, strike_cutoff_days, updated_at) VALUES (1, ?, datetime(\'now\'))'
).run(5)
} catch (_) {}
// 战区地图配置:钳形轴线、以黎轴线、防御线(与 src/data/extendedWarData 一致,新增数据落库)
const warMapConfig = {
pincerAxes: [
{ start: [43.6, 37.2], end: [46.27, 38.08], name: 'North Pincer (Tabriz)' },
{ start: [45.0, 35.4], end: [46.99, 35.31], name: 'Central Pincer (Sanandaj)' },
{ start: [45.6, 35.2], end: [47.07, 34.31], name: 'South Pincer (Kermanshah)' },
],
israelLebanonAxis: { start: [35.25, 32.95], end: [35.55, 33.45], name: 'Israel → Lebanon' },
defenseLinePath: [[46.27, 38.08], [46.99, 35.31], [47.07, 34.31]],
}
try {
db.prepare(
'INSERT INTO war_map_config (id, config, updated_at) VALUES (1, ?, datetime(\'now\')) ON CONFLICT(id) DO UPDATE SET config = excluded.config, updated_at = datetime(\'now\')'
).run(JSON.stringify(warMapConfig))
} catch (_) {}
db.prepare("INSERT OR REPLACE INTO situation (id, data, updated_at) VALUES (1, '{}', ?)").run('2026-03-01T11:45:00.000Z')
console.log('Seed completed.')
}

View File

@@ -82,6 +82,10 @@ function getSituation() {
const updates = db.prepare('SELECT * FROM situation_update ORDER BY timestamp DESC LIMIT 50').all()
// 数据更新时间:与前端「实时更新」一致,仅在爬虫 notify / 编辑保存时由 index.js 或 routes 更新
const meta = db.prepare('SELECT updated_at FROM situation WHERE id = 1').get()
let animationConfigRow = null
try {
animationConfigRow = db.prepare('SELECT strike_cutoff_days FROM animation_config WHERE id = 1').get()
} catch (_) {}
let conflictEvents = []
let conflictStats = { total_events: 0, high_impact_events: 0, estimated_casualties: 0, estimated_strike_count: 0 }
@@ -180,6 +184,9 @@ function getSituation() {
strikeSources: mapStrikeSources,
strikeLines: mapStrikeLines,
},
animationConfig: {
strikeCutoffDays: animationConfigRow?.strike_cutoff_days ?? 5,
},
}
}