Files
usa/docs/DEBUG_战损_基地_地图.md
2026-03-03 22:42:21 +08:00

136 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 战损、基地、地图战区 — 专项调试
只关心这三块时,按下面数据源 + 排查顺序即可。
---
## 一、战损 (combat_losses)
### 数据流
```
RSS 新闻(标题+摘要/正文) → 爬虫流水线 run_full_pipeline
→ extract_from_news(text) → combat_losses_delta { us: { personnel_killed, ... }, iran: { ... } }
→ db_merge.merge() → 按「增量」叠加到 combat_losses 表
→ POST /api/crawler/notify → Node 重载 DB
→ getSituation() 读 combat_losses → 前端 CombatLossesPanel / CombatLossesOtherPanel
```
- **表**`combat_losses`side=us / iran字段含 personnel_killed、personnel_wounded、bases_destroyed、bases_damaged、aircraft、drones、missiles 等。
- **初始值**`node server/seed.js` 会写入美/伊两行。
- **更新条件**:只有新闻里**明确出现可解析的伤亡/装备数字**如「2 名美军死亡」「14 人受伤」「1 架战机受损」)时,提取器才会输出 `combat_losses_delta`merge 才会叠加。
### 提取器选择(三选一)
| 环境变量 | 使用模块 | 说明 |
|----------|----------|------|
| `DASHSCOPE_API_KEY` 已设 | `extractor_dashscope` | 通义抽取,精度较好 |
| 未设通义 且 `CLEANER_AI_DISABLED≠1` | `extractor_ai` | 需本机 Ollama如 llama3.1 |
| 未设通义 且 `CLEANER_AI_DISABLED=1` | `extractor_rules` | 规则正则,无需模型 |
### 验证命令
```bash
# API 返回的战损
curl -s http://localhost:3001/api/situation | jq '{
us: .usForces.combatLosses.personnelCasualties,
iran: .iranForces.combatLosses.personnelCasualties,
conflictStats: .conflictStats
}'
# 表内原始值
sqlite3 server/data.db "SELECT side, personnel_killed, personnel_wounded, bases_destroyed, bases_damaged, aircraft FROM combat_losses"
```
### 常见问题
| 现象 | 可能原因 | 处理 |
|------|----------|------|
| 战损数字从不变化 | 1) 只跑了 main.py 未跑 gdelt<br>2) 新闻里没有明确伤亡/装备数字<br>3) 提取器未启用或报错Ollama 未起、通义未配) | 跑 `npm run gdelt`;用带数字的新闻测;看爬虫日志是否有提取/merge 报错 |
| 数字暴增一次 | 提取器把「累计总数」当成本条增量 | 已用 MAX_DELTA_PER_MERGE 做单次上限;可查 db_merge.py |
| 想用已有事件脉络重算战损 | 历史新闻当时未做提取 | `curl -X POST http://localhost:8000/crawler/backfill` 用 situation_update 最近 50 条重新提取并 merge |
---
## 二、基地 (key_location)
### 数据流
```
RSS 新闻 → extract_from_news → key_location_updates: [ { name_keywords, side, status, damage_level } ]
→ db_merge.merge() → UPDATE key_location SET status=?, damage_level=? WHERE side=? AND (name LIKE ? OR ...)
→ getSituation() 读 key_location → 前端 BaseStatusPanel(美) / IranBaseStatusPanel(伊) / WarMap 据点层
```
- **表**`key_location`side=us / iran字段含 name、lat、lng、type、region、**status**、**damage_level**。
- **初始数据**seed 写入大量美/伊据点和基地(含 name**爬虫只更新已有行的 status、damage_level**,不新增行。
- **匹配规则**:提取器的 `name_keywords`(如 `阿萨德|asad`)会按 **LIKE '%关键词%'**`key_location.name` 匹配。例如 name 为「阿萨德空军基地」时,关键词「阿萨德」能匹配。
### 规则提取器支持的基地关键词(与 seed name 对应关系)
- **美军**:阿萨德|阿因|asad → 匹配 seed「阿萨德空军基地」「阿因·阿萨德」巴格达 → 巴格达外交支援中心;乌代德|卡塔尔 → 乌代德空军基地;埃尔比勒 → 埃尔比勒空军基地;因吉尔利克|土耳其 → 因吉尔利克空军基地;苏尔坦|沙特 → 苏尔坦亲王空军基地;坦夫|叙利亚 → 坦夫驻军;达夫拉|阿联酋 → 达夫拉空军基地;内瓦提姆|拉蒙|以色列 → 内瓦提姆/拉蒙等;赛利耶、巴林、科威特 等。
- **伊朗**:阿巴斯港、德黑兰、布什尔、伊斯法罕、纳坦兹、米纳布、霍尔木兹 等seed 中需有对应 name 的伊朗据点)。
若 seed 里没有某据点,或 name 与关键词完全对不上(例如英文报道只写 "Al-Asad" 而 seed 只有「阿萨德空军基地」),规则里已含 asad/阿萨德,一般能匹配;若仍不匹配,可查 `key_location.name` 与 extractor_rules.py / extractor_dashscope 的 name_keywords 是否有一致子串。
### 验证命令
```bash
# 被标为遭袭的据点
curl -s http://localhost:3001/api/situation | jq '[.usForces.keyLocations[], .iranForces.keyLocations[]] | map(select(.status == "attacked")) | length'
# 表内 status / damage_level
sqlite3 server/data.db "SELECT side, name, status, damage_level FROM key_location WHERE status != 'operational' OR damage_level IS NOT NULL LIMIT 20"
```
### 常见问题
| 现象 | 可能原因 | 处理 |
|------|----------|------|
| 基地状态从不更新 | 1) 新闻未提及「某基地遭袭」类表述<br>2) 提取的 name_keywords 与 key_location.name 无法 LIKE 匹配 | 确认 seed 的 name 含中文/英文与提取器关键词一致;或扩展 extractor 的 name_keywords |
| 地图/基地面板无据点 | key_location 表空 | 先执行 `node server/seed.js` |
---
## 三、地图战区 / 冲突点 (gdelt_events + conflict_stats)
### 数据流
- **正常模式**`fetch_gdelt_events()` 请求 GDELT API → 解析为事件列表 → `_write_to_db(events)` 写入 `gdelt_events``conflict_stats`(总事件数、高影响事件数、估计伤亡、打击次数等)。
- **GDELT 不可用**:设 `GDELT_DISABLED=1` 时,`fetch_news()` 里在流水线结束后调 `_rss_to_gdelt_fallback()`,用 **situation_update 最近 50 条** 按 summary 推断经纬度(`_infer_coords`)和 impact_score由 severity 映射),写入 `gdelt_events`,这样地图仍有冲突点。
前端 WarMap 根据 `conflictEvents`= gdelt_events的 impact_score 分绿/橙/红三层显示;战损区「冲突统计」来自 `conflict_stats`
### 验证命令
```bash
# 冲突点条数 + 冲突统计
curl -s http://localhost:3001/api/situation | jq '{ conflictEvents: (.conflictEvents | length), conflictStats: .conflictStats }'
# 表内
sqlite3 server/data.db "SELECT COUNT(*) FROM gdelt_events"
sqlite3 server/data.db "SELECT * FROM conflict_stats WHERE id = 1"
```
### 常见问题
| 现象 | 可能原因 | 处理 |
|------|----------|------|
| 地图没有冲突点 | 1) gdelt_events 表空<br>2) 未跑 gdelt 或 GDELT 被墙且未开 RSS 回填 | 跑 `npm run gdelt`;国内可设 `GDELT_DISABLED=1`,靠 situation_update 回填 |
| 冲突点不更新 | 爬虫未调 notify或 Node/爬虫用的不是同一个 data.db | 确认 API_BASE、DB_PATH 一致;看 Node 终端是否有 `[crawler/notify] DB 已重载` |
| conflict_stats 全 0 | 从未成功写入过 gdelt_eventsGDELT 与 RSS 回填都未执行) | 先让 gdelt_events 有数据(见上) |
---
## 四、一键检查(仅战损 / 基地 / 地图)
在项目根执行:
```bash
./scripts/debug-panels-focus.sh
```
会检查API 是否通、`combat_losses` / `key_location` / `gdelt_events` / `conflict_stats` 行数及关键字段、并给出简短结论。需已启动 API`npm run api`);可选 `jq``sqlite3` 以输出更全。
详细逐板块说明见 `docs/DEBUG_PANELS.md`