feat: 导入 CapacityReport 初始源码

This commit is contained in:
Nixevol
2026-09-24 06:16:41 +08:00
commit 89cca70430
134 changed files with 38799 additions and 0 deletions
+366
View File
@@ -0,0 +1,366 @@
# 远程数据自动调度功能设计方案
## 一、需求概述
### 1.1 背景
当前系统需要每周一手动点击"远程下载并处理"来跑上周数据(周一到周日,7天数据)。由于服务器时间不准确,无法使用定时任务在固定时间运行。需要一个基于**数据就绪状态**的自动调度机制。
### 1.2 核心需求
1. **文件时间过滤**:处理时只处理最近7天的文件
2. **自动就绪检测**:每小时检查远程目录,判断上周7天数据是否齐全
3. **自动触发处理**:数据就绪后自动下载并处理
4. **状态标识管理**:使用标识文件避免重复触发
---
## 二、文件时间解析规则
### 2.1 文件名格式
| 格式 | 示例 | 数据日期 |
|------|------|----------|
| `XXX_YYYYMMDDHHMM_YYYYMMDDHHMM` | `CapacityReportData2.6_202605110000_202605120000.zip` | 覆盖起止时间范围,结束零点按右开区间处理 → 2026-05-11 |
| `XXX_YYYYMMDDHHMM` | `CapacityReportData2.6_202605110000.zip` | 视为单日文件 → 2026-05-11 |
### 2.2 解析逻辑
复用项目已有的正则 `_ZIP_DATE_RE = re.compile(r"(?<!\d)(20\d{10}(?:\d{2})?)(?!\d)")`,优先解析文件覆盖的自然日范围。
```python
def parse_file_date_range(filename: str) -> FileDateRange | None:
"""从文件名提取数据覆盖范围。"""
values = _ZIP_DATE_RE.findall(filename)
if not values:
return None
start = parse_timestamp(values[0])
end = parse_timestamp(values[1]) if len(values) > 1 else start + timedelta(days=1)
return FileDateRange(start=start.date(), end_exclusive=normalize_end(end))
```
---
## 三、自动调度机制
### 3.1 工作流程
```
每小时定时器触发
│
▼
┌─────────────────────┐
│ 检查就绪标识文件 │
│ (ready.flag) │
└─────────────────────┘
│
├── 标识存在 ──► 跳过检测,直接触发处理
│
▼
┌─────────────────────┐
│ 连接 FTP/SFTP │
│ 遍历远程目录 │
└─────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 对每个 expected_directory: │
│ 1. 列出所有 ZIP 文件名 │
│ 2. 从文件名提取数据日期 │
│ 3. 检查是否覆盖上周7天(周一~周日)│
└─────────────────────────────────────┘
│
├── 未就绪 ──► 记录日志,等待下次检查
│
▼
┌─────────────────────┐
│ 写入就绪标识文件 │
│ (ready.flag) │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 触发远程下载处理 │
│ (复用现有流程) │
└─────────────────────┘
│
▼
┌─────────────────────┐
│ 处理成功后 │
│ 1. 删除远程源文件 │
│ 2. 删除就绪标识 │
└─────────────────────┘
│
▼
继续每小时检查
```
### 3.2 目标日期范围(严格按自然周)
**永远是上周一到上周日**(自然周,7天)。无论今天是周几,程序都会自动计算正确的上周一和上周日。
```python
def get_target_week_range(week_offset: int = 0) -> tuple[date, date]:
"""获取目标周的周一到周日。
week_offset=0 表示上周,-1 表示上上周
"""
today = date.today()
this_monday = today - timedelta(days=today.weekday())
target_monday = this_monday - timedelta(days=7 * (1 - week_offset))
target_sunday = target_monday + timedelta(days=6)
return target_monday, target_sunday
```
示例(假设 week_offset=0):
| 今天 | 本周一 | 上周一 | 上周日 | 检查范围 |
|------|--------|--------|--------|----------|
| 周一 5/19 | 5/19 | 5/12 | 5/18 | 5/12~5/18 |
| 周三 5/21 | 5/19 | 5/12 | 5/18 | 5/12~5/18 |
| 周日 5/25 | 5/19 | 5/12 | 5/18 | 5/12~5/18 |
**不会**把非自然周的7天范围(如上周五到本周四)当作目标。`week_offset` 参数仅用于补跑场景:如果上周处理失败,可设为 `-1` 检查上上周的周一到周日。
### 3.3 就绪判断
一个目录的"就绪"条件:该目录下所有 ZIP 文件提取出的数据日期集合,**完全覆盖**目标周(上周一~上周日)的全部 7 天。
**关键**:
1. 不依赖系统日期,而是从文件名中提取日期
2. 文件名带两个时间戳时按覆盖范围判断;只有一个时间戳时视为单日文件
3. 例如文件 `CapacityReportData2.6_202605120000_202605130000.zip` → 覆盖 2026-05-12
4. 如果某个日期没有对应的 ZIP 文件,该目录判定为未就绪
RJ 目录会额外按最新文件自动判断粒度:最新文件为单日时要求 7 个目标日都齐全;最新文件为多日/周文件时要求存在一个 ZIP 覆盖完整目标自然周。
### 3.4 标识文件
- **位置**:`cache/auto_scheduler/ready.flag`
- **格式**:JSON
```json
{
"ready_at": "2026-05-19T10:00:00",
"week_start": "2026-05-12",
"week_end": "2026-05-18",
"directories": {
"4G/FDD": {"found_days": 7, "required_days": 7},
"4G/900": {"found_days": 7, "required_days": 7},
"5G/2.6": {"found_days": 7, "required_days": 7},
"5G/700": {"found_days": 7, "required_days": 7}
}
}
```
---
## 四、配置项设计
### 4.1 新增配置(`Configure.json` → `RemoteData` 节点下)
```json
{
"RemoteData": {
"enabled": true,
"protocol": "sftp",
"host": "127.0.0.1",
"port": 2022,
"user": "nixevol",
"remote_dir": "/CapacityReportData",
"passive": true,
"timeout": 30,
"auto_delete_source": true,
"passwd": "",
"auto_scheduler": {
"enabled": false,
"check_interval_hours": 1,
"expected_directories": [
"4G/FDD",
"4G/900",
"5G/2.6",
"5G/700"
],
"week_offset": 0
}
}
}
```
### 4.2 字段说明
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `enabled` | bool | `false` | 是否启用自动调度 |
| `check_interval_hours` | int | `1` | 检查间隔(小时),最小1 |
| `expected_directories` | list[str] | `[]` | 需要检测就绪的子目录路径列表(相对于 `remote_dir`)。为空时按实际 ZIP 所在目录逐个检测 |
| `week_offset` | int | `0` | 周偏移,`0` = 检查上周,`-1` = 检查上上周 |
### 4.3 约束规则
1. **自动调度开启时,`auto_delete_source` 强制为 `true`**(前端灰显、后端校验)
2. `expected_directories` 为空时按远程 ZIP 实际所在目录逐个检测
3. `check_interval_hours` 最小值为 1
4. 配置了 `expected_directories` 时,某个预期目录可访问但完全没有 ZIP 文件,视为该目录已停推并跳过;所有目录都为空时不触发自动处理
---
## 五、代码改动计划
### 5.1 后端
#### `app/config.py`
- `RemoteDataConfig` 新增 4 个字段 + `AutoSchedulerConfig` 子配置类
- 更新 `from_dict()`、`to_dict()`、`to_file_dict()` 序列化逻辑
- 新增约束校验:`auto_scheduler.enabled = true` 时强制 `auto_delete_source = true`
#### `app/services/remote_download.py`
- 新增 `list_remote_zip_files(directory: str | None) -> list[RemoteFileInfo]`:递归列出指定远程目录下所有 ZIP 文件路径、相对路径、所在目录和大小(只扫描,不下载)
- 远程下载会先按每个实际目录筛选最近 7 天 ZIP;自动调度触发时按 ready flag 中的目标日期精确下载,避免数据就绪后远程目录又出现新文件时误下载非目标周数据。
#### `app/services/auto_scheduler.py`(**新建**)
- `AutoScheduler` 类
- `start()` / `stop()`:启动/停止后台定时线程
- `check_and_run()`:检查 → 就绪 → 触发处理
- `_is_ready()`:检查所有目录就绪状态
- `_mark_ready()` / `_clear_ready()`:标识文件管理
- `_trigger_processing()`:复用 `remote.py` 中的 `_run_remote_processing` 逻辑
- `get_status()` → dict:返回调度器当前状态
#### `app/main.py`
- 应用启动时初始化 `AutoScheduler` 并 start
- 应用关闭时 stop
#### `app/api/routers/remote.py`
- 新增 `GET /api/remote/scheduler/status`:查询调度状态
- 新增 `POST /api/remote/scheduler/trigger`:手动触发一次检测
- 修改 `POST /api/remote/start`:自动调度期间禁止手动触发(避免冲突)
### 5.2 前端
#### `frontend/src/components/SettingsPanel.vue`
在"远程数据源"配置区域新增"自动调度"折叠卡片:
- 启用开关
- 检查间隔(小时)输入框
- 预期目录列表(支持增删)
- 周偏移选择(默认上周)
- 状态面板:下次检查时间、上次检查结果、就绪状态、各目录检测结果
---
## 六、日志与监控
### 6.1 日志示例
```
[10:00:00] 自动调度:开始检查远程目录就绪状态
[10:00:01] 自动调度:连接 SFTP 127.0.0.1:2022
[10:00:02] 自动调度:检查 4G/FDD → 找到 5/7 天文件(缺少 2026-05-13, 2026-05-14)
[10:00:03] 自动调度:检查 4G/900 → 找到 7/7 天文件 ✓
[10:00:04] 自动调度:检查 5G/2.6 → 找到 7/7 天文件 ✓
[10:00:05] 自动调度:检查 5G/700 → 找到 6/7 天文件(缺少 2026-05-18)
[10:00:06] 自动调度:数据未就绪(2/4 目录满足),等待下次检查
...
[11:00:00] 自动调度:开始检查远程目录就绪状态
[11:00:05] 自动调度:所有目录数据就绪 (4/4),写入标识文件
[11:00:06] 自动调度:触发远程下载处理任务
[11:05:00] 自动调度:任务处理成功
[11:05:01] 自动调度:远程源文件已清理
[11:05:02] 自动调度:已清除就绪标识,继续监控
```
### 6.2 状态接口响应
```json
GET /api/remote/scheduler/status
{
"enabled": true,
"running": true,
"next_check_at": "2026-05-19T12:00:00",
"last_check_at": "2026-05-19T11:00:00",
"last_result": "triggered",
"failure_count": 0,
"task_running": false,
"ready_flag": {
"exists": false
},
"target_week": {
"start": "2026-05-12",
"end": "2026-05-18"
},
"directory_status": {
"4G/FDD": {"found_days": ["2026-05-12"], "found_count": 7, "required_count": 7, "ready": true},
"4G/900": {"found_days": ["2026-05-12"], "found_count": 7, "required_count": 7, "ready": true},
"5G/2.6": {"found_days": ["2026-05-12"], "found_count": 7, "required_count": 7, "ready": true},
"5G/700": {"found_days": ["2026-05-12"], "found_count": 7, "required_count": 7, "ready": true}
}
}
```
---
## 七、异常处理
| 场景 | 处理方式 |
|------|----------|
| SFTP/FTP 连接失败 | 记录错误日志,等下次周期重试 |
| 连续失败 ≥3 次 | 在前端调度状态面板显示红色失败状态 |
| 处理失败 | **保留就绪标识**,下次检查时直接触发处理(不重新检测) |
| 远程目录不存在 | 跳过该目录,记录警告,其他目录正常检测 |
| 预期目录内无 ZIP 文件 | 视为该目录已停推并跳过,不阻塞其他目录;如果所有目录都为空,则继续等待,不触发处理 |
| 手动触发远程处理 | 自动调度运行中禁止手动触发,反之亦然(互斥锁复用 `global_task_lock`) |
---
## 八、测试要点
1. **文件名解析**
- `XXX_202605110000_202605120000.zip` → 2026-05-11 ✓
- `XXX_202605110000.zip` → 2026-05-11 ✓
- `no_date_here.zip` → None(跳过)
- `XXX_20260511000012345.zip` → 14位匹配 → 2026-05-11 ✓
2. **就绪检测**
- 所有目录都覆盖7天 → 就绪
- 某目录缺少1天 → 未就绪
- `expected_directories` 为空 → 只检测根目录
- 子目录不存在 → 跳过并记录警告
3. **调度流程**
- 检测 → 写标识 → 触发处理 → 成功 → 删标识 → 循环
- 处理失败 → 保留标识 → 下次直接触发
4. **配置联动**
- 开启自动调度 → `auto_delete_source` 自动变为 true
- 修改配置 → 调度器热重载
5. **互斥**
- 自动调度运行中 → 手动触发被拒绝
- 手动任务运行中 → 自动调度跳过本轮
---
## 九、实施步骤与工时估算
| 步骤 | 内容 | 预估工时 |
|------|------|----------|
| 1 | `app/config.py`:扩展 `RemoteDataConfig`,新增 `AutoSchedulerConfig` | 20min |
| 2 | `app/services/remote_download.py`:新增 `list_remote_zip_names()` | 30min |
| 3 | `app/services/auto_scheduler.py`:新建调度器核心逻辑 | 2h |
| 4 | `app/api/routers/remote.py`:新增状态查询和手动触发接口 | 30min |
| 5 | `app/main.py`:集成调度器启动/停止 | 10min |
| 6 | `frontend/src/components/SettingsPanel.vue`:自动调度配置 UI | 1.5h |
| 7 | `frontend/src/api/client.ts`:新增 API 调用方法 | 10min |
| 8 | 联调测试 | 1h |
| **合计** | | **约6h** |
---
## 十、注意事项
1. **不改动现有处理流程**:自动调度只是在现有"远程下载并处理"之上加了一层检测和触发机制
2. **标识文件是运行时数据**:`cache/auto_scheduler/ready.flag` 不应提交到版本库
3. **`week_offset` 用途**:如果某周处理失败需要下周补跑,可将 `week_offset` 设为 `-1` 来检查上上周的数据
4. **配置热重载**:修改自动调度配置后,调度器应在下一次检查周期使用新配置(不需要重启服务)
5. **前端 `auto_delete_source` 联动**:当自动调度开启时,前端应将"处理成功后删除源文件"开关灰显为强制开启状态
+333
View File
@@ -0,0 +1,333 @@
# 数据处理平台架构设计
## 目标
建设一个通用的数据处理平台,用于接入 FTP/SFTP 等远程数据源,处理压缩包、CSV、XLSX 等报表文件,完成入库、清洗、合并、脚本执行、定时任务和结果查询。平台后续不绑定单一报表类型,容量报表只是一个业务应用或任务模板。
当前 CapaReport 项目只作为参考和代码借鉴来源。新平台前期放在当前项目根目录的 `platform/` 目录下开发,该目录已加入 CapaReport 的 `.gitignore`,避免影响当前项目提交。
## 隔离边界
`platform/` 是完全独立的新平台工作区,只是临时放在当前项目根目录下,方便开发时查看和参考 CapaReport 的现有实现。该目录下的所有内容都不属于 CapaReport 项目。
必须遵守以下边界:
- 不使用 CapaReport 的 Python 虚拟环境,例如 `.venv/`、`venv/`。
- 不使用 CapaReport 的 Python 依赖文件,例如 `requirements.txt`。
- 不使用 CapaReport 的启动脚本、构建脚本或运行配置。
- 不使用 CapaReport 的前端依赖目录,例如 `frontend/node_modules/`。
- 不使用 CapaReport 的前端构建配置或包管理文件,例如 `frontend/package.json`、`frontend/package-lock.json`、`frontend/vite.config.ts`。
- 不从 CapaReport 的 `app/`、`frontend/`、`src-tauri/` 等目录直接导入代码。
- 不读取或依赖 CapaReport 的 `Configure.json`、`ReportScript.sql`、缓存、日志、授权文件或本地运行数据。
- 不把 `platform/` 的代码、依赖、配置或生成物提交到 CapaReport 仓库。
- 如需复用思路,只能按模块职责重新实现或后续抽取为独立仓库,不能形成对当前项目目录的运行时依赖。
## 设计原则
- 只做 Web 平台,不再引入桌面端打包形态。
- 后端使用 Python,前端使用 Web 技术栈。
- 模块之间通过明确接口协作,避免一个模块同时承担下载、解析、入库、SQL 执行等多种职责。
- 每个核心模块独立 Git 仓库维护,平台主仓库通过 submodule 组合。
- 先保证文件接入、数据入库、任务执行、结果查询这条主链路稳定,再扩展更多报表和自动化能力。
- 当前阶段避免过度抽象,模块边界清晰即可,插件化能力按真实需求逐步增强。
## 仓库与目录组织
平台根目录建议为独立 Git 仓库,放在当前项目的 `platform/` 下。本目录不进入 CapaReport 仓库。
建议平台主仓库负责:
- 应用启动与服务编排。
- 全局配置、认证、权限、审计、任务调度。
- 前后端集成。
- submodule 版本锁定。
- 平台级文档和部署脚本。
建议 submodule 放在平台主仓库的 `modules/` 下:
| 模块仓库 | 职责 |
| --- | --- |
| `platform-core` | 应用核心模型、配置、任务状态、事件、日志、通用接口约定 |
| `source-remote` | FTP/SFTP 连接、目录扫描、文件下载、远端删除、连接测试 |
| `file-processing` | 解压缩、文件识别、CSV/XLSX 读取、字段映射、数据标准化、临时文件管理 |
| `database-service` | 数据库连接、库表管理、数据入库策略、SQL 执行、结果查询、导出 |
| `job-runner` | 手动任务、定时任务、脚本任务、任务依赖、失败重试、运行历史 |
| `web-ui` | Web 界面、配置管理、任务监控、数据管理、脚本管理、API 文档 |
| `api-client-contracts` | 前后端共享的 API 契约、类型约定、错误码和响应结构 |
早期可以减少仓库数量,例如先保留 `platform-core`、`source-remote`、`file-processing`、`database-service`、`web-ui` 五个模块。等脚本和调度复杂度上来后,再把 `job-runner` 独立出去。
## 模块职责
### 平台核心模块
平台核心模块只负责平台级能力,不处理具体业务数据。
核心职责:
- 任务、运行记录、数据源、数据集、文件、脚本、调度计划等基础模型。
- 统一日志、错误码、状态流转和事件通知。
- 配置读取、配置校验、敏感信息存储策略。
- 模块注册和服务发现。
- 认证、Token、权限、审计的基础接口。
不应承担:
- FTP/SFTP 协议细节。
- CSV/XLSX 解析细节。
- 具体数据库 SQL 方言。
- 具体业务报表规则。
### FTP/SFTP 模块
远程数据源模块只负责“远程文件发现和传输”。
核心职责:
- FTP/SFTP 连接配置、测试、目录递归扫描。
- 远程文件列表、文件大小、修改时间、文件名时间范围识别辅助信息。
- 按规则下载文件和目录。
- 下载完成后的远端源文件删除,只删除文件不删除目录。
- 网络异常、断连、重试、超时控制。
不应承担:
- 解压缩。
- CSV/XLSX 字段解析。
- 数据入库。
- SQL 脚本执行。
### 文件处理模块
文件处理模块负责把原始文件转成“可入库的数据批次”。
核心职责:
- ZIP 等压缩包解压,保留原始数据和清理临时展开文件。
- CSV/XLSX 文件识别、编码识别、Sheet 过滤、表头识别。
- 字段映射、类型转换、空值和异常值标准化。
- 文件名日期解析、按目录选择最近 N 天或目标周期数据。
- 生成数据批次元信息,例如来源文件、字段列表、行数、日期范围、目标逻辑表。
- 为数据库模块提供流式或分块数据输入。
不应承担:
- 真实数据库连接管理。
- 建表、删表、查表。
- SQL 脚本执行。
- 结果表查询。
### 数据库模块
数据库模块负责“数据库侧”的所有能力。
核心职责:
- 数据库连接配置、连接池、连接测试。
- 数据库、表、字段、索引管理。
- 数据入库策略:批量插入、数据库原生加载、临时表、落地中间文件。
- SQL 脚本执行、SQL 查询、结果分页、结果导出。
- 表结构查看、表数据管理、权限控制下的数据修改。
- 多数据库适配的接口预留,早期以 MySQL 为主。
数据入库边界建议:
- 文件处理模块负责解析、清洗、标准化和分批输出。
- 数据库模块负责选择入库方式并执行入库。
- 业务任务模块只编排“先处理文件,再入库,再执行脚本”,不直接操作底层文件或数据库细节。
性能取舍:
- 大 CSV 优先走数据库原生批量加载能力。
- 原生加载不可用时使用分块批量插入。
- XLSX 不应整文件无脑载入内存,优先采用流式读取或按 Sheet 分块处理。
- 大任务必须有进度、行数、速度、当前阶段和失败定位。
### 任务与脚本模块
任务模块负责“什么时候做、做什么、做到哪一步”。
核心职责:
- 手动上传处理、远程下载处理、定时扫描处理。
- SQL 脚本、Python 脚本和后续其他脚本类型的执行入口。
- 任务状态、阶段、日志、运行历史、失败重试。
- 调度策略:按小时扫描、按文件日期判断就绪、就绪后延迟触发。
- 任务互斥、取消、超时、异常恢复。
任务模块不直接解析文件,也不直接写数据库底层逻辑,只调用文件处理模块和数据库模块。
### Web 界面模块
Web 界面模块负责平台交互,不包含业务处理逻辑。
核心页面建议:
- 数据源配置:FTP/SFTP、连接测试、远端目录、删除源文件策略。
- 文件处理配置:Sheet 过滤、字段映射、文件日期规则、目标表规则。
- 数据库配置:连接、库表管理、数据查询、导出、SQL 执行。
- 任务中心:手动运行、远程运行、定时任务、运行历史、日志。
- 脚本中心:SQL/Python 脚本编辑、版本、执行记录。
- API Token 与 API 文档。
- 系统设置:用户、权限、历史保留、平台运行参数。
界面模块只调用 API,不直接耦合后端模块内部实现。
## 核心数据流
平台主链路建议分为以下阶段:
1. 数据源扫描:远程模块返回文件清单和基础元信息。
2. 数据选择:任务模块根据调度规则和文件日期规则选择待处理文件。
3. 数据获取:远程模块下载文件,形成原始数据目录。
4. 文件展开:文件处理模块解压压缩包并识别 CSV/XLSX。
5. 数据标准化:文件处理模块完成编码、字段、类型、空值、异常值处理。
6. 数据入库:数据库模块按入库策略写入目标库表。
7. 脚本处理:任务模块调用数据库模块执行 SQL 或调用脚本执行器。
8. 结果输出:数据库模块提供结果表查询、导出和 API 查询。
9. 历史归档:任务模块记录原始数据、日志、结果、运行耗时和错误信息。
10. 清理策略:按配置清理临时文件、历史记录和远端源文件。
## 关键模型
建议平台统一维护以下概念:
| 模型 | 含义 |
| --- | --- |
| 数据源 | FTP/SFTP、本地目录或未来其他来源 |
| 文件清单 | 一次扫描得到的远程或本地文件列表 |
| 数据集 | 一批准备处理的数据,可以来自上传或远程下载 |
| 处理配置 | 字段映射、Sheet 过滤、文件日期规则、目标表规则 |
| 任务 | 一次手动或自动触发的处理动作 |
| 运行记录 | 任务的一次实际运行,包含状态、阶段、日志、耗时 |
| 数据批次 | 文件处理模块输出给数据库模块的标准化数据 |
| 脚本 | SQL、Python 或其他脚本类型 |
| 结果资源 | 结果表、导出文件、日志、原始数据归档 |
## 配置设计
配置应分层:
- 平台配置:端口、认证、历史保留、日志、存储目录。
- 数据源配置:协议、地址、端口、账号、目录、超时、删除源文件策略。
- 文件处理配置:文件类型、压缩包规则、字段映射、日期解析、异常值策略。
- 数据库配置:连接、目标库、入库策略、表名前缀、大小写兼容。
- 任务配置:调度周期、目标日期规则、失败重试、并发限制。
- 脚本配置:脚本类型、执行环境、参数、权限。
敏感配置可以先支持明文内网部署,后续再增加加密存储或密钥管理。
## API 设计方向
API 应按资源分组,而不是按页面分组。
建议资源:
- 数据源 API。
- 文件与数据集 API。
- 数据库 API。
- 任务 API。
- 脚本 API。
- 历史 API。
- Token 与用户 API。
- 系统配置 API。
外部系统接入时优先通过 Token 调用任务和查询 API。API 文档应自动生成,并为常用接口提供中文说明、参数示例和错误说明。
## 调度设计
调度不应依赖服务器系统时间判断数据是否已经推送完成,应优先依据文件名中的数据日期或文件清单中的业务日期。
建议支持:
- 每隔固定时间扫描远程目录。
- 按目录判断目标日期是否齐全。
- 空目录可配置为停推目录并跳过。
- 日粒度和周粒度文件按文件名自动识别。
- 就绪后先写入就绪标识,下一轮扫描再触发处理,避免刚推送完成时立即下载半成品。
- 处理成功后按配置删除远端源文件,并清除就绪标识。
日粒度与周粒度规则必须配置化,避免不同目录、不同厂家、不同报表命名习惯互相影响。
## 存储与历史
建议保留以下目录概念:
- 原始数据目录:保存下载或上传的原始文件。
- 临时处理目录:保存解压后的 CSV/XLSX、中间转换文件。
- 归档目录:保存可追溯的任务原始数据和日志。
- 导出目录:保存临时导出文件,下载完成后自动清理。
- 脚本目录:保存 SQL、Python 等脚本。
处理历史应支持:
- 查看任务详情、阶段、日志。
- 下载完整原始数据包。
- 单文件或单目录下载。
- 按保留次数或保留天数清理。
- 清理临时文件但不误删原始归档。
## 当前项目可复用经验
当前 CapaReport 可作为参考的能力:
- FTP/SFTP 下载、远程删除、连接测试。
- 文件名日期解析和最近 N 天筛选。
- ZIP 解压、CSV/XLSX 处理、字段映射。
- MySQL 表管理、数据查询、CSV/XLSX 导出。
- SQL 脚本执行和日志展示。
- 处理历史、原始数据下载、临时文件清理。
- API Token、OpenAPI 文档、Web 管理界面。
- 自动调度的就绪标识和二次触发机制。
迁移时应避免直接复制成一个巨型模块,应先按职责拆分,再逐步搬运可复用逻辑。
## 开发路线
### 阶段一:平台骨架
- 建立 `platform/` 独立仓库。
- 明确模块 submodule 目录。
- 建立后端 Web 服务、前端壳、基础认证、配置读写。
- 接入一个最小远程数据源配置和连接测试。
### 阶段二:文件到数据库主链路
- 完成远程下载、本地上传、解压、CSV/XLSX 识别。
- 完成字段映射和基础类型转换。
- 完成 MySQL 入库、表管理、数据查询。
- 建立任务状态、日志和历史记录。
### 阶段三:脚本与调度
- 支持 SQL 脚本执行。
- 支持定时扫描远程目录。
- 支持按目录和日期判断数据就绪。
- 支持处理成功后删除远端源文件。
### 阶段四:业务模板化
- 将容量报表作为一个业务模板接入平台。
- 增加更多报表模板和处理脚本。
- 支持任务参数、脚本参数、结果表配置。
### 阶段五:平台增强
- 增加权限细分、审计、任务重试、告警。
- 增加更多数据库适配。
- 增加模块版本管理和升级策略。
## 风险与决策点
| 风险 | 建议 |
| --- | --- |
| 模块拆得太细导致开发慢 | 早期只拆核心边界,复杂后再独立仓库 |
| 文件处理和数据库入库职责混乱 | 坚持文件模块输出标准数据批次,数据库模块负责落库 |
| 大文件处理性能不足 | 优先流式、分块、数据库原生批量加载 |
| 不同报表命名规则不一致 | 日期解析和周期规则配置化 |
| 脚本权限风险 | 脚本执行入口必须有权限控制和审计记录 |
| submodule 管理复杂 | 主仓库锁版本,模块仓库保持独立发布说明 |
## 近期建议
先在 `platform/` 下建立独立平台主仓库,再从 CapaReport 中抽取最小可用能力:远程连接测试、文件日期解析、CSV 入库、任务日志。第一版不要急着覆盖所有报表,先跑通“远程目录到数据库结果表”的稳定链路。
File diff suppressed because it is too large Load Diff
+142
View File
@@ -0,0 +1,142 @@
# cellinfo → sector 逆推调研笔记
> 目标:用 `celldata.cellinfo`(自动处理、有数据源)逆推出 `celldata.sector`(人工据 cellinfo 整理)。
> 现有 sector 作为基准/参考。逆推时**不清空** sector:仅补充缺失 CGI,已有的保留(避免人工纠正被自动运行冲掉)。
> 脚本与特征库写入 `CellData.sql`。本文件记录字段映射、规则、每轮测试的优化点与准确率。
## 一、两表结构(celldata)
cellinfo(数据源,66778 行):`CGI, eNodeBID, CellID, PLMN, 基站名称, 小区名称, 频点, 带宽, 制式, 功率, 网络`
sector(目标,66969 行):`CGI, 扇区, 物理站, 制式, 频段, 带宽, 站型, 网络`(原 `区域` 列已删除:全项目无引用,最终不使用)
连接键:`CGI`。sector 比 cellinfo 多 ~191 行(历史/其他来源),逆推以 cellinfo 为准。
## 二、字段来源结论
| sector 字段 | 来源 | 说明 |
|---|---|---|
| CGI | 复制 cellinfo.CGI | 连接键 |
| 网络 | 复制 cellinfo.网络 | 4G / 5G |
| 带宽 | 复制 cellinfo.带宽 | cellinfo 已规范化为如 `20M` |
| ~~区域~~ | **已删除该列** | 全项目无引用,已 `ALTER TABLE sector DROP COLUMN 区域` |
| 制式 + 频段 | 频点 + PLMN 推导(特征表) | 见三 |
| 站型 | 名称关键字 + 设备码推导 | 见四 |
| 物理站 | 小区名称解析 | 见五 |
| 扇区 | 物理站 + 扇区号 | 扇区号见五 |
## 三、频点 → 制式 / 频段(特征库核心)
cellinfo.制式 已给出粗类(FDD/TDD/700M/2.6G),频段再按频点细分;个别需 PLMN 区分:
| 网络 | 频点范围 | PLMN | 制式 | 频段 | 实测样本 |
|---|---|---|---|---|---|
| 5G | ≥4000 | 任意 | 4.9G | 4.9G | 4827.36、4919.52 |
| 5G | 700–800 | 460-00 | 700M | 700M | 763.25(移动)9808 |
| 5G | 700–800 | 460-15 | 广电 | 广电 | 763.25(广电)9802 |
| 5G | 2000–3000 | 任意 | 2.6G | 2.6G | 2524.95 等 |
| 4G | 900–1000 | 任意 | FDD | FDD900 | 939.0 |
| 4G | 1000–1880 | 任意 | FDD | FDD1800 | 1810–1827 |
| 4G | 1880–1920 | 任意 | TDD | F频 | 1895.0、1909.4 |
| 4G | 1920–2110 | 任意 | TDD | A频 | 2017.5 |
| 4G | 2300–2400 | 任意 | TDD | E频 | 2330、2349.8、2364.6 |
| 4G | 2400–2700 | 任意 | TDD | D频 | 2624.6 等 |
**关键歧义**
1. **700M vs 广电**:同频点 763.25,由 **PLMN** 区分(460-00=移动 700M,460-15=中国广电)。✅ 可完美区分。
2. **D频 vs 3DMM**:**cellinfo 完全没有 3DMM 标注**——`cellinfo.制式` 仅 4 值 `700M/TDD/FDD/2.6G`;sector 中标 3DMM 的 1400 个小区在 cellinfo 里**全部是 `TDD`(4G)**。3DMM 是 3D-MIMO 天线属性,人工额外打在 sector 上,源表不可还原。
- **分类策略(已采纳用户规则)**:3DMM 按频点对应的"非 3DMM 实际制式"分类——这些频点(2624.6 等 2.6GHz)的非 3DMM 小区都是 `TDD/D频`,而 FDD 仅在 900/1800,频点不重叠。故脚本对这 1400 个判 `TDD/D频` 即正确(与"3DMM 用 TDD 制式规则"一致)。
- 因此"制式 97.88%"是对照 sector 人工 3DMM 标签的数字,**按制式规则 TDD 才是正确分类,制式实际正确率≈100%**。现有 sector 的 3DMM 因「不覆盖已有行」保留不变。
## 四、站型规则(实测覆盖度)
| 站型 | 规则 | 命中 | 总数 | 备注 |
|---|---|---|---|---|
| 室分 | 小区名设备码尾 `Z[A-Z]W`(如 ZLW/ZRW/ZFW,末字母 W) | 10146 | 10168 | 99.8% |
| 微站 | 小区名含「微小」(如 (微小M)/(微小I)) | 1086 | 1086 | 100% |
| 宏站 | 其余(设备码尾 H,如 Z5H/ZLH/ZFH,且无微小) | — | 54747 | 仅 18 行宏站名含微小(误差≈0.03%) |
判定顺序:室分(W 码) → 微站(微小) → 宏站。W 码与微小在数据中互斥。
## 五、物理站 / 扇区解析
预处理(仅为解析可靠):全角 `()【】`→半角 `()[]`、去空格。
**设备码**(位于小区名尾部):`<频率前缀>-Z<x><尾字母>-<扇区号>`,正则 `[A-Z0-9]+-Z[A-Z0-9]{2}-[0-9]+$`。
- 频率前缀:U / E / D / F / DC / RD / RDC 等;尾字母 H=室外(宏/微)、W=室分。
- 例:`U-Z5H-0711`、`E-ZLW-8`、`D-ZRH-103`、`F-ZLH-3`、`RDC-ZFH-2`。
**扇区号**(取小区名尾部数字串):
- 700M/广电(Z5H 码):取**末位数字**。`0711`→1、`0712`→2、`0713`→3、`30712`→2。
- 其余:取数字串 **% 100**。`-3`→3、`-103`→3、`-113`→13、`-203`→3、`-16`→16。
**物理站**:
- **非 700M**:= 小区名去设备码后,删除所有括号/方括号内容,但**保留 `(微小X)`**。
- 例 `江门台山河东家禽(从…搬迁)(微小M)(共…F-ZLH)F-ZLH-2` → `江门台山河东家禽(微小M)`。
- 例 `江门鹤山坚美园52至53栋(共…E-ZLW)E-ZLW-8` → `江门鹤山坚美园52至53栋`。
- **700M/广电**:名称结构 `CBN-<枢纽>(<真实站址>)<码>` 或 `CBN-<站址><码>`,枢纽与站址**同地市前缀**。
- **通用做法(不写死江门)**:取外层名(去 `CBN-`、首个括号前)的**前 2 个字**作为地市前缀,匹配 base 中**以该前缀开头的括号内容**为物理站;无匹配则取外层(去 `CBN-`)。
- 前 2 字:2 字地市(江门/广州…)直接命中;3 字地市(石家庄…)取「石家」仍能与同地市站址括号匹配。
- 共建(`(共…)`)/`从…拉远` 等注记括号不以地市开头,自然落外层 → 等价「共建取外层」;`共和` 等地名因括号以地市开头仍取内层。
- 例 `CBN-江门台山田头接入间(江门台山狮山)(BBU搬迁)U-Z5H-0713` → 前缀「江门」→ `江门台山狮山`。
- 例 `CBN-江门台山通济公园U-Z5H-0713`(无括号)→ 外层 `江门台山通济公园`。
- 例 `CBN-江门鹤山铸德(江门鹤山共和鸿江工业区)U-Z5H-0712` → `江门鹤山共和鸿江工业区`。
- 实测:对现有(全江门)数据,通用前缀写法与原写死「江门」结果 **100% 一致(19726/19726)**。
**扇区** = 物理站 + 扇区号(如 `江门新会五和农村二` + `1` = `江门新会五和农村二1`)。
> 括号宽度:现有 sector 的 `(微小X)` 用全角 `()`。逆推统一转半角输出;对比准确率时两侧都做括号归一化,衡量语义正确性而非宽度。
## 六、测试与准确率(逐轮更新)
测试方法:在 celldata 建 `sector_band_ref` 特征表 + `_sector_infer` 暂存表逐字段推导,
JOIN 现有 `sector`(重叠 CGI **n=66001**)逐字段比对;物理站/扇区比对时两侧做括号+空白归一化。
### Round 1(基础规则)
| 字段 | 准确率 |
|---|---|
| 网络 | 100%(66001)|
| 带宽 | 99.75%(65838)|
| 制式 | 97.88%(64601)|
| 频段 | 97.60%(64417)|
| 站型 | 99.94%(65963)|
| 物理站 | 99.12%(65419)|
| 扇区 | 98.72%(65159)|
失配定位:
- 制式失配 **100% 是 TDD↔3DMM**(1400)——同频/同 PLMN/同带宽/功率重叠,不可还原。
- 频段失配 = 3DMM(1400) + F频↔A频(162,1895/1909 同频人工分档) + E频↔D频(22)。
- 带宽失配主要 cellinfo=20M 而人工=15M(155,源数据人工修正)。
- 站型失配 38:20 个室分码为 `Z[A-Z]R`(如 E-ZLR,高铁 SM 站)未被 W 规则覆盖;18 个宏站名含"微小"被误判微站。
- 物理站/扇区失配:700M 多括号/不闭合、非 700M 共建/拉远不闭合括号。
### Round 2(物理站 v2/v3:按关键字保留限定括号)——失败回退
尝试只删"共/从/拉远/搬迁…"备注括号、保留 `(龙山路)(红线内)(1)`,物理站反降到 ~98.2%。
原因:① 末尾 `[)]+$` 误删了 `(微小M)` 等合法括号的右括号;② 大量无备注关键字的别名括号本应删除却被保留,净损失。结论:**"删全部括号+回贴微小"才是最优主策略**。
### Round 3(v4:删全部括号 + 悬空清理 + 700M 取"(江门"内容)——定稿
- 非 700M:去码 → 删全部成对括号(3 轮) → 删悬空开括号 `\([^()]*$` → 删孤立 `)` → 回贴 `(微小X)`。
- 700M/广电:取 `(江门…` 括号内容;无则取外层去 `CBN-`("共"开头自然落外层)。
- 扇区号:700M 末位;其余 %100。
| 字段 | 准确率 | 说明 |
|---|---|---|
| 网络 | 100% | 复制 |
| 制式 | 97.88%(排除 3DMM 即 **100%**)| 仅差不可还原的 3DMM |
| 频段 | 97.60% | 差额=3DMM+同频 F/A、E/D 人工分档 |
| 带宽 | 99.75% | 差额为人工把 20M 改 15M |
| 站型 | 99.94% | |
| 物理站 | **99.59%**(65728)| |
| 扇区 | **99.20%**(65470)| |
剩余残差为**不可还原的人工差异**,不再继续拟合(避免过拟合):
- 室分多载波编号不一致:`E-ZLW-12→2`、`-111→1`、`-115→5`(末位)与 `-16→16`(%100)并存;脚本按用户既定 %100。
- 700M 个别人工编号:`0713→1`、`0712→4`、`0712→空`,与名义 071N→N 规则冲突。
- 物理站个别含区域前缀(`柏怡酒店`↔`江海柏怡酒店`)、保留 `(龙山路)/(1)` 等;真实数据存在尾随制表符。
- 因"不覆盖已有行",上述人工值在自动运行中均被保留,不受影响。
### 落库策略
仅 `INSERT` sector 中缺失的 CGI(本次快照 **921 条**),已有 CGI 一律保留。区域留空。
### 复现实测命令(celldata)
按 `CellData.sql` 顺序执行即可;准确率核验 SQL:
`SELECT 物理站/扇区 归一化后 = sector 对应值 的占比 FROM _sector_infer JOIN sector USING(CGI)`。
+196
View File
@@ -0,0 +1,196 @@
# 高负荷小区判定 — 调研记录 / 工作笔记
> 目的:在 4G/5G 结果表中为每个小区判定「是否高负荷小区」(并可给「优化建议」)。
> 本文档是**讨论与决策的工作底稿**,边讨论边更新;数据为实测采集,尽量准确,避免重复读取。
> 状态:调研完成,规则**待讨论敲定**(未实现、未提交)。最后更新:2026-06-26。
---
## 0. 数据来源与位置(避免重复读取)
| 内容 | 位置 |
| --- | --- |
| 判定标准原件 | `CapacityReport/高负荷判定标准.xlsx`(单 sheet「汇总」,A1:S80;已摘录见 §4) |
| 4G/5G 结果表 | MySQL `capacityreport` 库:`4g_结果表`、`5g_结果表`(每次跑报表重建) |
| 小区属性支撑表 | MySQL `celldata` 库:`cellinfo`、`sector`(由 300 表处理上传;跑报表前会复制进 `capacityreport` 库) |
| 小区信息原始源 | `F:\sftpgo_v2.7.1_windows_portable\data\网优日常优化数据文档\日常性能报表\2026年\300表\{2.6G,700M}\Result_300_小区信息导出工具模板_*.zip`(NR/LTE CellInfo 等 CSV → 提炼成 cellinfo/sector;**不含负荷指标**) |
| 报表 SQL(拟在此追加判定逻辑) | `CapacityReport/ReportScript.sql`(末尾已有富集 + 已建空列 `是否高负荷小区` VARCHAR(100)、`优化建议` TINYTEXT) |
**关键事实**:负荷指标只在结果表里;`cellinfo/sector` 只提供分类属性(制式/带宽/站型/频段/功率)。判定 = 结果表负荷指标 +(按属性分档的)门限。
---
## 1. 结果表可用负荷指标 + 实测分布
### 1.1 4G 结果表(`4g_结果表`,34078 行)
利用率均为 **0–1 比值**(可直接和标准的 0.5/0.7 比较)。
| 指标列 | 含义 | 均值 | 最大 | 命中量(阈值→小区数) |
| --- | --- | --- | --- | --- |
| `上行PUSCH利用率` | 上行 PUSCH 利用率 | 0.11 | 0.92 | ≥0.5: 136 |
| `下行PDSCH利用率` | 下行 PDSCH 利用率 | 0.22 | 0.996 | ≥0.5: 2733;≥0.7: 585;≥0.9: 40 |
| `PDCCH资源利用率` | PDCCH 利用率 | 0.12 | 0.57 | (max 仅 0.57,≥0.5 极少) |
| `自忙时流量(GB)` | 自忙时流量 | 2.29 | 23.86 | — |
| `ERAB流量` | **自忙时平均 E-RAB 流量(KB)**,用于大/中/小包分类 | 1551 | 446131 | 大包≥1000: 14943;中包 300–1000: 15941;小包<300: 3194 |
| `上行流量(GB)` / `下行流量(GB)` | 上/下行流量 | — | — | — |
| `YY-RRC连接建立最大用户数` | RRC 最大用户数(≈标准「有数据传输的RRC数/RRC连接最大数」) | 30.2 | 431 | ≥30: 13783;≥50: 5239 |
| `用户面平均激活UE数` / `用户面最大激活UE数` | 激活 UE 数 | — | — | — |
| 组合 | `下行PDSCH≥0.7 且 YY-RRC≥30` | — | — | **499** |
### 1.2 5G 结果表(`5g_结果表`,33028 行)
| 指标列 | 含义 | 均值 | 最大 | 命中量 |
| --- | --- | --- | --- | --- |
| `上行PRB平均利用率` | 上行 PRB 利用率 | 0.086 | 0.85 | ≥0.5: 110 |
| `下行PRB平均利用率` | 下行 PRB 利用率 | 0.18 | 0.99 | ≥0.7: 503;≥0.8: 238;≥0.9: 77 |
| `RRC连接平均连接用户数` | RRC 平均用户数 | 20.3 | 356 | ≥30: 6998;≥100: 1079;≥200: 67 |
| `RRC连接最大连接用户数` | RRC 最大用户数 | — | — | — |
| `5G上行流量(…)(GB)` | 上行流量 | 0.66 | 16.7 | — |
| `5G下行流量(…)(GB)` | 下行流量 | 5.9 | 101 | — |
| `日均流量(GB)` | 日均流量 | — | 400+ | — |
| `PDCCH信道CCE占用率(动态)(%)`、`单Flow流量(MB)`、`小区(上/下)行RLC SDU字节数`、`移动流量_GB`、`广电流量_GB` | 其他 | — | — | — |
| 组合 | `下行PRB≥0.7 且 RRC平均≥30` | — | — | **200** |
| 组合 | `下行PRB≥0.8 且 RRC平均≥50` | — | — | **46** |
> 观察:利用率呈「低均值 + 高尾部」,利用率门限本身已很筛选(4G 下行≥0.7 仅 1.7%,5G 下行≥0.7 仅 1.5%)。RRC=200(5G 标准原值)极严(仅 67 个)。
---
## 2. 小区分类属性(来自 cellinfo/sector,按 CGI 匹配结果表)
匹配率:4G 命中 cellinfo 98.8%、sector 98.7%;5G(NCGI=CGI) 命中 cellinfo 99.8%、sector 99.1%。
- `cellinfo.制式`:`700M`(19732)、`TDD`(17423)、`FDD`(16402)、`2.6G`(13221)
- **4G 小区 → 制式 ∈ {TDD, FDD};5G 小区 → 制式 ∈ {700M, 2.6G}**(700M≈FDD-NR、2.6G≈TDD-NR)。
- `cellinfo.带宽`:`20M`(23588)、`30M`(19726)、`100M`(12894)、`10M`(9717)、`15M`(461)、`60M`(267)、`40M`(60)、`5M`(59)、`0M`(6)
- 30M→5G FDD-NR;100M/60M→5G TDD-NR;20M/15M/10M/5M→4G。
- `sector.站型`:`宏站`(55608)、`室分`(10272)、`微站`(1089) —— **无 64TR/32TR/8TR 等 TR 粒度**。
- `sector.频段`:`2.6G`、`700M`、`广电`、`FDD900`、`FDD1800`、`F频`、`E频`、`D频`、`3DMM`(1489)、`A频`、`4.9G`。
---
## 3. 标准输入 vs 我们拥有的字段(能做 / 缺)
| 标准需要 | 我们是否有 | 说明 |
| --- | --- | --- |
| 4G 利用率(上行PUSCH、下行PDSCH、PDCCH) | ✅ | 0–1 |
| 大/中/小包(自忙时平均E-RAB流量KB) | ✅ | 用 `ERAB流量` 分档(≥1000 / 300–1000 / <300) |
| 4G 流量(上/下行GB、自忙时GB) | ✅ | |
| 4G RRC 数 | ✅ 近似 | `YY-RRC连接建立最大用户数` |
| 制式 TDD/FDD | ✅ | cellinfo.制式 |
| 带宽(5/10/15/20M…) | ✅ | cellinfo.带宽 |
| 频段(FDD900/F/A/E/D/3DMM…) | ✅ | sector.频段(高流量感知按频段分档可用) |
| 5G PRB 利用率(上/下)、RRC、上/下行流量 | ✅ | 5G 规则可完整套用 |
| 5G 带宽档(100M/80M/60M/30M) | ⚠️ 部分 | 有 100M/60M/30M;无 80M(数据里没出现 80M) |
| **TR 数(64/32/8/4/2/1TR)** | ❌ | 只有 宏/室分/微 → **2023 集团扩容标准(per-TR)难直接套用** |
| 折算带宽系数 / 全网平均单载波流量(流量系数) | ❌ 需自算 | 流量系数 = 本小区日均流量 / 全网平均单载波流量 / 带宽系数,需先定义系数与全网均值 |
| 3DMM 作为独立 4G 制式 | ⚠️ | 4G 制式只有 TDD/FDD;3DMM 体现在 `sector.频段`(1489 个) |
---
## 4. 原始标准摘要(xlsx「汇总」)
**A. 4G 通用标准 / 高负荷待扩容门限(R1–R17)**:制式(TDD/FDD/3DMM) × 小区分类(大包≥1000 / 中包<1000 或 300–1000 / 小包<300,按自忙时平均E-RAB流量KB) × 带宽(20M/15M/10M/5M)。每档给:利用率门限(上行PUSCH `D`、下行PDSCH/PDCCH `E`)+ 有数据传输的RRC数 + 上/下行流量GB。
- TDD:利用率 0.5;下行 70%/50%(大包)、50%/50%(中小包)。例 20M 大包:RRC=10、流量 0.3/5。
- FDD:利用率 0.7;下行 70%/70%。例 20M 大包:RRC=15、流量 2/10.5。
- 3DMM:利用率 0.8;80%/80%。
**B. 高流量感知(R20–R41)**:自忙时流量 / 有效RRC连接最大数 / RRC连接最大数 / 上行利用率 / 下行利用率,按 频段(FDD900/FDD1800/F/A/E/D/3DMM) × 带宽 给阈值(多数「折算带宽」)。例:上行利用率 TDD>60%;下行利用率 全制式>80%。
**C. 高负荷预警(R44,集团定义,最简口径)**:无线利用率≥50% 且 有效RRC连接平均数≥30。细化:**TDD 利用率≥50%、FDD≥70%、3DMIMO≥80%**。
**D. 流量系数(R48–R52)**:流量系数 = 本小区日均流量 /(全网总流量/小区总数 的平均单载波流量)/ 带宽系数。低流量 0–0.2、高流量 >3。
**E. 5G 高负荷规则(R54–R61)**:制式 × [上行PRB 0.5 / 下行PRB 0.7 / RRC平均 / 上行流量GB / 下行流量GB]
- TDD-NR100M:RRC 200 / 上 5 / 下 70
- TDD-NR80M:160 / 4 / 56
- TDD-NR60M:120 / 3 / 43
- FDD-NR30M:90 / 8 / 30
- 利用率口径:自忙时上/下行业务信道空分 PRB 占用利用率。
**F. 2023 集团扩容标准(R64–R74)**:宏站(64/32TR) / 宏站(8/4TR) / 室分(4/2/1TR) × 大/中/小包(单flow流量M) × 利用率 + 每 TR 的上/下行流量门限。**依赖 TR 数,我们缺该粒度 → 暂不采用**。
---
## 5. 候选简化规则(初版,待讨论确认)
> 思路:以「**利用率达标 且(用户数或流量达标)**」二选一组合作为高负荷,按制式/带宽分档取门限;优先用我们字段齐全、口径清晰的 **C(高负荷预警)** 和 **E(5G 规则)**。先只输出二分「是/否」,`优化建议` 后续再细化。
### 候选 A —「高负荷预警」简化口径(推荐起步,最稳)
- **4G**:`是高负荷` = 下行PDSCH利用率 ≥ (TDD 0.5 / FDD 0.7) **且** `YY-RRC连接建立最大用户数` ≥ 30。
- 参考命中:下行≥0.7 且 RRC≥30 = 499;若 TDD 用 0.5 会更多(下行≥0.5=2733,再叠 RRC≥30 待测)。
- **5G**:`是高负荷` = 下行PRB利用率 ≥ 0.7 **且** RRC平均 ≥ 30(或按带宽用 E 的 RRC 档)。
- 参考命中:下行≥0.7 且 RRC≥30 = 200。
### 候选 B —「5G 规则按带宽 + 4G 按大中小包」更贴标准
- 5G:下行PRB≥0.7 **且**(RRC平均≥档 或 下行流量≥档),档按带宽(100M:200/70、60M:120/43、30M:90/30)。
- 4G:下行PDSCH≥门限 **且**(RRC≥档 或 上/下行流量≥档),门限/档按 制式×大中小包×带宽(套 A 表,简化为主力带宽 20M/10M)。
---
## 6. 待讨论的开放问题(请逐条拍板)
1. **采用哪套口径**:候选 A(最简、稳)/ 候选 B(更贴标准、更细)/ 其他组合?
2. **组合逻辑**:利用率与(用户数/流量)之间是 **AND**(推荐,避免纯利用率饱和误报)还是 OR?(用户数/流量之间是 OR 还是 AND?)
3. **是否分档**:门限是否区分 制式(TDD/FDD)、带宽、站型(宏/室分/微)?还是先用一套统一门限?
4. **4G 利用率口径**:用 `下行PDSCH利用率` 为主,是否叠加 `上行PUSCH`/`PDCCH`?(PDCCH max 仅 0.57,单独用意义不大)
5. **输出形态**:只二分 `是否高负荷小区`(是/否),还是分级(高/中/低 或 高负荷/高流量/正常)?`优化建议` 要不要这次就生成(如"建议扩容/加载/分流")?
6. **是否要「流量系数 / 高低流量」**:需要先算全网平均单载波流量 + 带宽系数,较复杂,是否本期纳入?
7. **缺 TR 粒度**:2023 标准(per-TR)放弃,OK?室分/微站是否单独更宽松门限?
8. **阈值校准**:希望"高负荷"占比大概多少(如 1–3%)?据此回标阈值(我可按候选规则跑命中量再调)。
---
## 8. 已确认方向 + 规则 v1(阈值待最终拍板)
**已定(用户 2026-06-26)**:① 用简化版;② 利用率 **AND** 用户数;③ 门限区分 制式/带宽,**不分站型**(原版简化规则 4G/5G 均不分站型,仅 TR 制的 2023 扩容标准分,但缺 TR 数);④ 5G RRC 用标准原值 200(要少);⑤ 输出 `是否高负荷小区`(**仅「高负荷」=是;两类预警与正常=否**)+ 新增 `高负荷问题` ∈ {高负荷, 利用率预警, 高流量预警};⑥ 5G 80M 档同 100M;⑦ **3DMM 不作独立 4G 制式,归 TDD(0.5)**(且 `cellinfo.制式` 本就只有 TDD/FDD,3DMM 在频段里,天然走 TDD)。
**判定逻辑(互斥,按利用率优先)**:
```
若 利用率达标:
若 用户数达标 → 高负荷 (是否高负荷=是)
否则 → 利用率预警 (是否高负荷=否,仅预警)
否则若 流量达标(高) → 高流量预警 (是否高负荷=否,仅预警)
否则 → 正常 (是否高负荷=否,高负荷问题=空)
# 即「是否高负荷小区=是」当且仅当 高负荷问题='高负荷'(预警 ≠ 高负荷)。
```
**5G 阈值(来自标准 E,按带宽;80M 同 100M)**
| 带宽 | 利用率达标 | 用户数门限(RRC平均) | 流量达标(上行GB / 下行GB) |
| --- | --- | --- | --- |
| 100M/80M | 上行PRB≥0.5 或 下行PRB≥0.7 | RRC平均≥200 | 上行≥5 或 下行≥70 |
| 60M | 同上 | ≥120 | 上行≥3 或 下行≥43 |
| 30M | 同上 | ≥90 | 上行≥8 或 下行≥30 |
| 其他/缺失 | 同上 | ≥200 | 上行≥5 或 下行≥70 |
**4G 阈值(利用率按制式、用户数/流量按带宽;不分站型;3DMM 归 TDD)— 已定**
| 维度 | 取值 |
| --- | --- |
| 利用率达标(制式) | FDD:下行PDSCH≥0.7 或 上行PUSCH≥0.7;其余(TDD、3DMM):≥0.5 |
| 用户数门限(带宽,YY-RRC) | 20M→30、15M→25、10M→20、5M→12、其他→30 |
| 流量达标(带宽,下行GB) | 20M→10、15M→7.5、10M→5、5M→2.5、其他→10 |
**实测命中量(最终阈值)**
- 5G(33028):高负荷 46 / 利用率预警 536 / 高流量预警 161 / 正常 32285。
- 4G(34078):高负荷 792 / 利用率预警 62 / 高流量预警 158 / 正常 33066。
**实现(已写入 `ReportScript.sql` 富集段之后)**:判定 UPDATE 直接用富集写入结果表的 `制式`/`带宽` 列,**不再 JOIN** cellinfo/sector(规避 sector ~144 个重复 CGI 导致的行虚增)。先 UPDATE `高负荷问题`(嵌套 CASE),再 `是否高负荷小区` = `IF(高负荷问题='高负荷','是','否')`(**仅「高负荷」为是**,两类预警=否)。`优化建议` 生成见 §9。
- 「是否高负荷小区=是」的数量 = 高负荷数:4G 792、5G 46;预警类只进 `高负荷问题`、不计入"是"。
## 9. 优化建议生成(仅对 `是否高负荷小区`='是' 的高负荷小区)
**分析范围**:同扇区(`扇区`) + **同 PLMN**(CGI/NCGI 前两段;本网仅 `460-00`(移动) / `460-15`(广电),不同运营商不可均衡、不视为同扇区)。在该范围找「最空闲」小区(`高负荷问题` IS NULL,按 下行利用率 → 日均流量 升序取第一个):
- 小区标识统一为 **`CGI(小区名称)`**(4G 名称取 `小区名称`,5G 取 `CU小区配置名称`;空缺显示 `-`)。
- **模板1(有空闲小区)**:`高负荷小区{CGI(名称)}:上行利用率:.. 下行利用率:.. 日均流量:..GB 用户数:.. 当前小区功率:..。存在同覆盖空闲小区:{CGI(名称)}:下行利用率:.. 上行利用率:.. 日均流量:..GB 小区功率:..,建议将本小区部分负荷均衡至该空闲小区(负载均衡/邻区参数优化)。`
- **模板2(无空闲,同扇区皆高负荷/预警)**:`高负荷小区{CGI(名称)}:…当前小区功率:..。经分析,本站同扇区(同PLMN)小区均为高负荷或预警状态、无空闲可均衡小区,建议人工分析周边小区是否可均衡。`
**实现**(`ReportScript.sql` 末尾,紧接判定之后):每表建临时缓存 `_idle_4g`/`_idle_5g`(每个 (扇区,PLMN) 的最空闲小区,含其 `name`(小区名称) 列,`ROW_NUMBER()` 取一并建索引)→ `UPDATE 结果表 LEFT JOIN _idle … WHERE 是否高负荷小区='是'` 用 `CONCAT` 拼建议(利用率 ×100 显示为 %)→ `DROP` 临时表。`优化建议` 列由 `TINYTEXT` 改为 **`TEXT`**(建议文本超 255 字节会被截断)。仅 4G 用 PUSCH/PDSCH 利用率与 YY-RRC,5G 用 PRB 利用率与 RRC 平均用户数。
**实测(4G 高负荷 792)**:有空闲→模板1 **601**、无空闲→模板2 **191**(约 76% 可负载均衡)。语法/逻辑已在 8.0.45 只读验证通过。
## 7. 变更记录
- 2026-06-26:完成首轮调研(读 xlsx 标准、查 4G/5G 结果表分布与命中量、核对 cellinfo/sector 类别、确认 300 表为小区信息源)。
- 2026-06-26:确认方向(简化版 + AND + 分制式/带宽 + 5G RRC=200 + 是否高负荷=是/否 & 高负荷问题三类);落地规则 v1 阈值表与命中量(§8)。
- 2026-06-26:**最终敲定**——去掉站型系数(4G/5G 均不分站型)、3DMM 归 TDD;规则与阈值已写入 `ReportScript.sql`(富集段之后:新增 `高负荷问题` 列 + 两表判定 UPDATE)。最终命中量:4G 高负荷 792 / 利用率预警 62 / 高流量预警 158;5G 46 / 536 / 161。待用户跑报表验证。
- 2026-06-26:修正语义——`是否高负荷小区` **仅当 `高负荷问题='高负荷'` 才为「是」**(两类预警≠高负荷,记 `否`);`IF(高负荷问题 IS NULL,...)` 改为 `IF(高负荷问题='高负荷','是','否')`。故"是"的数量=高负荷数(4G 792、5G 46)。
- 2026-06-26:实现 `优化建议`(§9)——同扇区+同PLMN找最空闲小区,模板1(建议负载均衡)/模板2(建议人工分析周边);`优化建议` TINYTEXT→TEXT。4G 实测模板1 601 / 模板2 191。已写入 ReportScript.sql(待用户跑报表验证)。