# Globe v6 修复计划 — 最终版
## TL;DR (For humans)
两个紧急修复:背面剔除 + 国境线恢复。预计 50 行变更。
---
## P1. 背面剔除修复
**原因**: `updatePulses` 中 `arcGroup.quaternion` 与 `rotation` 不同步,导致方向判断错误。
**修复**: 用 `localToWorld` 替代 `applyQuaternion`
在 `templates/tianxuan/globe.html` 的 `updatePulses` 函数中:
```javascript
// 删除:
// var arcQuat = arcGroup.quaternion;
// midPt.applyQuaternion(arcQuat);
// 在函数开头添加:
arcGroup.updateMatrixWorld(true);
// 在循环内改为:
var midPt = d.curve.getPoint(0.5);
arcGroup.localToWorld(midPt);
var isFront = midPt.dot(camDir) < -0.08;
```
## P2. 国境线恢复
### 步骤 1: 创建 static/tianxuan/world_borders.js
创建文件含 10 国精简轮廓(中国/美国/俄罗斯/加拿大/巴西/澳大利亚/印度/英国/日本/法国)。
每国 10-20 个 [lat,lon] 坐标点。
**重要**: 中国坐标需包含台湾和南海九段线示意。
台湾坐标: 25°N,121°E 左右。
### 步骤 2: 恢复 globe.html 中的 borderGroup
在 `scene.add(gridGroup)` 之后添加:
```javascript
// ── World borders (loaded from static file) ──
var borderGroup = new THREE.Group();
var borderMat = new THREE.LineBasicMaterial({ color: 0x88dd88, transparent: true, opacity: 0.3, depthTest: false });
function renderBorders(borders) {
if (!borders) return;
for (var c = 0; c < borders.length; c++) {
var pts = [];
for (var p = 0; p < borders[c].length; p++) {
var lat = borders[c][p][0], lon = borders[c][p][1];
var phi = (90 - lat) * Math.PI / 180;
var theta = (lon + 180) * Math.PI / 180;
pts.push(new THREE.Vector3(5.01 * Math.sin(phi) * Math.cos(theta), 5.01 * Math.cos(phi), 5.01 * Math.sin(phi) * Math.sin(theta)));
}
if (pts.length > 2) {
borderGroup.add(new THREE.Line(new THREE.BufferGeometry().setFromPoints(pts), borderMat));
}
}
}
if (typeof worldBorders !== 'undefined') renderBorders(worldBorders);
scene.add(borderGroup);
```
### 步骤 3: 恢复加载脚本
在 three.min.js 之后添加:
```html
```
### 步骤 4: 恢复复选框
在 toggle 区域恢复:
```html
```
### 步骤 5: 添加 borderGroup 到 animate/惯性旋转
```javascript
earth.rotation.y += inertiaY; arcGroup.rotation.y += inertiaY; gridGroup.rotation.y += inertiaY; borderGroup.rotation.y += inertiaY;
earth.rotation.x += inertiaX; arcGroup.rotation.x += inertiaX; gridGroup.rotation.x += inertiaX; borderGroup.rotation.x += inertiaX;
```
---
## Todos
- [x] 1. 创建 `static/tianxuan/world_borders.js` (10国精简轮廓)
- [x] 2. globe.html: 修复背面剔除 (localToWorld)
- [x] 3. globe.html: 恢复 borderGroup (渲染 + 加载 + 复选框 + 旋转)
- [x] 4. config.yaml: 添加 `":ips": ipv4`, `":ipd": ipv4`
- [x] 5. type_classifier.py: 名称模式增加 `:ips` `:ipd`
- [x] 6. entity_detector.py: ENTITY_KEYWORDS 增加 `:ips` `:ipd`
- [x] 7. views.py globe_view: 列检测增加 `:ips` `:ipd`
## P4. 用户数据列名适配 (`:ips`, `:ipd`)
**问题**: 用户的源IP列名是 `:ips`,目标IP列名是 `:ipd`。
当前代码的IP检测靠名称模式匹配 `_ip`/`src_ip`/`dst_ip`,完全匹配不到。
**需要修改的文件**:
### 1. config.yaml — 添加列覆盖
```yaml
columns:
":ips": ipv4
":ipd": ipv4
```
### 2. analysis/type_classifier.py — 名称模式增加 `:ips` `:ipd`
在 `_NAME_TYPE_MAP` 中添加:
```python
(re.compile(r'^:ips$|^:ipd$', re.I), DataType.IPv4),
```
### 3. analysis/entity_aggregator.py — 关键词增加 `:ips` `:ipd`
`_DST_IP_KEYWORDS` 和 `_SNI_KEYWORDS` 逻辑可能需要扩展,但更关键的是 entity_detector 的 `ENTITY_KEYWORDS`。
### 4. analysis/entity_detector.py — 关键词增加 `:ips` `:ipd`
```python
ENTITY_KEYWORDS: frozenset[str] = frozenset({
'src_ip', 'dst_ip', ':ips', ':ipd', 'source_ip', ...
})
```
### 5. analysis/views.py — globe_view 中的列检测
当前 globe_view 用 `'src_ip' in c.lower()` 检测IP列。
需要增加 `':ips'` 和 `':ipd'` 的检测。
## 紧急修补 (Traceback截断)
**问题**: views.py 中所有 `run.error_message = tb[:500]` 截断到 500 字符,无法调试。
**修复**: 搜索 views.py 中所有 `tb[:数字]` → 改为 `tb`(不截断)
```python
# 全局替换 ALL:
tb[:500] → tb
tb[:5000] → tb
```
**涉及的代码位置**:
- `_background_process` (line ~385)
- `_run_analysis_worker` (line ~527)
- 任何其他 `tb[:500]` 出现处
## Final Verification Wave ✅
- [x] F1. pytest tests -q → 121 pass
- [x] F2. 清理: 删除临时文件
- [x] F3. 完整端到端测试: 启动服务 → 上传 CSV → 预处理 → 手动分析 → 聚类 → 地球
---
## 依赖
全部独立,可并行执行。