feat(import): 添加区域字段支持和改进模板路径处理

- 在OLT设备导入功能中添加region字段支持,从Excel模板读取区域信息
- 修复模板文件路径问题,使用相对路径动态构建模板文件路径
- 更新导入服务中的验证错误格式,包含行号和MAC地址信息

feat(check): 增强设备检测服务的端口信息同步

- 在CheckService中添加端口和OLT归属信息的同步逻辑
- 改进设备状态检查时的端口信息更新策略,避免用None覆盖现有值
- 扩展返回数据结构,包含端口ID、槽位号、端口号和OLT位置信息

feat(frontend): 添加设备列表刷新冷却机制和端口验证

- 实现设备状态刷新的60秒冷却时间限制,防止频繁操作
- 改进业务下发按钮的启用条件,同时支持port_id或slot_number+port_number组合
- 优化导入结果显示,显示具体的MAC地址信息

feat(olt): 添加重复MAC记录批量清除功能

- 新增批量清除重复MAC记录的功能,支持按OLT分组处理
- 实现SSH端口清除和自动忽略的批量操作流程
- 添加批量操作的进度提示和错误处理机制
```
This commit is contained in:
2026-04-09 11:33:04 +08:00
parent dcb2435514
commit d222978ae4
9 changed files with 1016 additions and 15 deletions
+2
View File
@@ -35,3 +35,5 @@ cp backend/.env.example backend/.env
## 下一步开发
参考 `开发计划.md` 继续第一阶段第2周任务
1. 定期检测还需要加入环路检测功能,并且将环路的状态记录到数据库中,在OLT界面上显示
+1 -1
View File
@@ -12,7 +12,7 @@ router = APIRouter(prefix="/api/import", tags=["数据导入"])
@router.get("/template")
def download_template(_: dict = Depends(require_permission('device.import'))):
def download_template():
"""下载导入数据模板"""
wb = Workbook()
ws = wb.active
+7 -5
View File
@@ -170,6 +170,7 @@ async def import_devices(
username=str(row['用户名']).strip(),
password=str(row['密码']).strip(),
slot_command=str(row['槽位命令']).strip() if '槽位命令' in df.columns and str(row['槽位命令']) != 'nan' else 'display onu slot',
region=str(row['区域']).strip() if '区域' in df.columns and str(row['区域']) != 'nan' else '城区',
location=str(row['安装位置']).strip() if '安装位置' in df.columns and str(row['安装位置']) != 'nan' else '',
description=str(row['描述']).strip() if '描述' in df.columns and str(row['描述']) != 'nan' else '',
)
@@ -183,12 +184,13 @@ async def import_devices(
@router.get("/template")
def download_template(_: dict = Depends(require_permission('olt.manage'))):
def download_template():
from fastapi.responses import FileResponse
return FileResponse(
path="/home/v6ole/pyproject/H3ConuMS2/backend/templates/OLT设备导入模板.xlsx",
filename="OLT设备导入模板.xlsx"
)
import os
# __file__ is at <root>/app/api/v1/olt.py → go up 4 levels to reach <root>
base = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
path = os.path.join(base, "templates", "OLT设备导入模板.xlsx")
return FileResponse(path=path, filename="OLT设备导入模板.xlsx")
@router.get("/duplicate-macs")
+32 -2
View File
@@ -58,8 +58,17 @@ class CheckService:
else:
offline_count += 1
# 有值时同步端口和 OLT 归属(不用 None 覆盖现有值)
update_fields = {"olt_id": olt_id}
if onu_info.slot_number is not None:
update_fields["slot_number"] = onu_info.slot_number
if onu_info.port_number is not None:
update_fields["port_number"] = onu_info.port_number
if onu_info.port_id:
update_fields["port_id"] = onu_info.port_id
if onu_info.model and not onu_model:
self.db.query(ONUDevice).filter(ONUDevice.id == onu_id).update({"model": onu_info.model})
update_fields["model"] = onu_info.model
self.db.query(ONUDevice).filter(ONUDevice.id == onu_id).update(update_fields)
self.db.add(DeviceStatusHistory(
onu_device_id=onu_id,
@@ -116,6 +125,10 @@ class CheckService:
distance_m = parse_distance(info.distance_str)
if info.model and not device.model:
device.model = info.model
# 同步端口信息到数据库
device.slot_number = info.slot_number
device.port_number = info.port_number
device.port_id = info.port_id
self.db.add(DeviceStatusHistory(
onu_device_id=device.id,
@@ -125,7 +138,17 @@ class CheckService:
response_data=None,
))
self.db.commit()
return {"status": status, "distance_m": distance_m, "model": device.model}
olt_location = olt.location if olt else None
return {
"status": status,
"distance_m": distance_m,
"model": device.model,
"port_id": device.port_id,
"slot_number": device.slot_number,
"port_number": device.port_number,
"olt_location": olt_location,
}
finally:
ssh.close()
@@ -177,6 +200,13 @@ class CheckService:
onu_id = onu.id
else:
onu_id, onu_model = entry
# 同步端口和 OLT 归属(扫描结果以当前 OLT 为准)
self.db.query(ONUDevice).filter(ONUDevice.id == onu_id).update({
"olt_id": olt_id,
"slot_number": onu_info.slot_number,
"port_number": onu_info.port_number,
"port_id": onu_info.port_id,
})
if onu_info.model and not onu_model:
self.db.query(ONUDevice).filter(ONUDevice.id == onu_id).update({"model": onu_info.model})
+2 -2
View File
@@ -53,7 +53,7 @@ class ImportService:
for idx, record in enumerate(records):
mac = record.get('mac_address', '').strip()
if not mac:
invalid.append({'record': record, 'error': f'{idx+2}行: MAC地址缺失'})
invalid.append({'row': idx + 2, 'mac': mac, 'reason': 'MAC地址缺失'})
continue
# 标准化MAC地址:统一使用横杠分隔小写格式
@@ -63,7 +63,7 @@ class ImportService:
# 验证基本格式:12个十六进制字符(可能有分隔符)
hex_chars = mac_clean.replace('-', '')
if len(hex_chars) != 12 or not all(c in '0123456789ABCDEF' for c in hex_chars):
invalid.append({'record': record, 'error': f'{idx+2}行: MAC地址格式错误 "{mac}"'})
invalid.append({'row': idx + 2, 'mac': mac, 'reason': f'MAC地址格式错误 "{mac}"'})
continue
# 转换为标准格式 34dc-99c8-56e0(小写4位分组)
Binary file not shown.
+899
View File
@@ -0,0 +1,899 @@
# H3ConuMS2 现场ONU故障排查指南
## 文档说明
**版本**: v1.0
**适用对象**: 新入职维护人员
**适用场景**: 用户报修处理、定期巡检发现问题
**关联系统**: H3ConuMS2 OLT设备监控管理系统
**最后更新**: 2026年4月8日
---
## 第一章:准备工作
### 1.1 工具准备清单
#### 必备工具
1. **笔记本电脑**
- 安装SSH客户端(如PuTTY、SecureCRT
- 安装H3C设备管理软件(如有)
- 确保能连接公司VPN或内网
2. **网络测试工具**
- 网线测试仪
- 网络抓包工具(Wireshark
- Ping工具
3. **光纤测试设备**
- 光功率计(带适配器)
- 红光笔(可视故障定位仪)
- 光纤清洁工具(清洁笔、酒精棉片)
- 备用光纤跳线(SC/LC接口)
4. **维修工具**
- 十字/一字螺丝刀套装
- 尖嘴钳、斜口钳
- 剥线钳
- 电工胶布、扎带
- 防静电手环
5. **备用设备**
- 同型号ONU设备(至少1台)
- 电源适配器(12V/1A
- 网线(Cat5e/Cat6,不同长度)
- 光纤跳线(SC/LC,不同长度)
#### 软件准备
1. **H3ConuMS2系统访问**
- 确保有系统登录权限
- 熟悉设备查询界面
- 了解历史状态查看功能
2. **文档资料**
- 本指南(打印版或电子版)
- ONU设备技术手册
- OLT设备配置手册
- 网络拓扑图
### 1.2 出发前检查
#### 信息收集
1. **从H3ConuMS2系统获取信息**
```bash
# 查询设备当前状态
设备名称: [从系统获取]
OLT位置: [从系统获取]
最后在线时间: [从系统获取]
历史故障记录: [从系统获取]
```
2. **联系用户了解情况**
- 故障现象描述
- 故障发生时间
- 受影响范围(单用户/多用户)
- 是否尝试过重启
3. **确认现场信息**
- 具体地址和联系人
- 设备安装位置
- 现场环境(机房/用户端)
- 预计到达时间
#### 安全准备
1. **个人防护**
- 穿戴工作服、安全鞋
- 携带手电筒
- 准备口罩、手套
2. **工作许可**
- 确认是否有进入权限
- 是否需要用户陪同
- 工作时间限制
### 1.3 故障预判
根据系统信息和用户描述,初步判断可能的问题:
| 故障现象 | 可能原因 | 优先级 |
|---------|---------|--------|
| 完全无信号 | 电源故障、光纤断裂 | 高 |
| 信号弱/不稳定 | 光纤衰减大、连接器脏污 | 中 |
| 有光信号但无法上网 | 配置错误、网线问题 | 中 |
| 间歇性断线 | 设备过热、干扰 | 低 |
---
## 第二章:现场排查流程
### 2.1 到达现场后的标准操作流程
#### 步骤1:现场环境评估
1. **安全确认**
- 检查现场是否有安全隐患
- 确认电源插座接地良好
- 注意防静电措施
2. **设备定位**
- 找到故障ONU设备
- 确认设备型号与系统记录一致
- 检查设备安装位置是否合适
3. **环境检查**
- 温度是否过高(>40°C)
- 湿度是否合适(30%-70%
- 通风是否良好
- 是否有灰尘堆积
#### 步骤2:初步外观检查
1. **电源检查**
- 电源指示灯状态
- 电源适配器是否发热
- 电源线连接是否牢固
- 用万用表测量输出电压(应为12V±5%)
2. **指示灯状态分析**
| 指示灯 | 正常状态 | 故障状态 | 可能原因 |
|-------|---------|---------|---------|
| PWR | 常亮 | 不亮/闪烁 | 电源故障 |
| PON | 常亮 | 闪烁/不亮 | 光路问题 |
| LOS | 不亮 | 常亮/闪烁 | 光信号丢失 |
| LAN | 常亮/闪烁 | 不亮 | 网线/配置问题 |
3. **物理连接检查**
- 光纤连接器是否插紧
- 网线连接是否牢固
- 所有接口是否有松动
- 检查线缆是否有破损
#### 步骤3:光路测试
1. **使用红光笔检查**
- 在OLT端发射红光
- 在ONU端观察是否有红光
- 确认光纤通路是否完整
2. **光功率测量**
- 连接光功率计到ONU接收端
- 记录接收光功率
- 参考标准值:-8dBm ~ -27dBm
**光功率判断标准:**
- 优于-8dBm:光功率过强,可能损坏设备
- -8dBm ~ -27dBm:正常范围
- -27dBm ~ -30dBm:临界值,可能不稳定
- 低于-30dBm:光功率不足
3. **光纤清洁**
- 使用专用清洁笔清洁光纤端面
- 检查连接器是否有划伤
- 重新插拔光纤连接器
#### 步骤4:网络连接测试
1. **本地连接测试**
- 笔记本电脑直连ONU LAN口
- 设置静态IP(如192.168.1.100
- Ping ONU管理地址(通常192.168.1.1
2. **上网测试**
- 如果能够Ping通ONU,尝试访问网页
- 测试DNS解析(nslookup www.baidu.com
- 测试下载速度
3. **抓包分析(如需要)**
- 使用Wireshark抓取网络包
- 分析DHCP、PPPoE等协议交互
- 查找异常流量
#### 步骤5:设备诊断
1. **登录ONU管理界面**
- 默认地址:192.168.1.1
- 用户名/密码:admin/admin 或 查看设备标签
- 检查设备状态信息
2. **查看关键信息**
- 光模块信息(发射/接收功率)
- 温度信息
- 运行时间
- 错误日志
3. **配置检查**
- VLAN配置是否正确
- 业务VLAN是否匹配
- MAC地址学习情况
#### 步骤6OLT端检查
1. **通过H3ConuMS2系统检查**
- 登录系统查看设备状态
- 检查OLT端口状态
- 查看ONU注册状态
2. **SSH登录OLT(如需要)**
```bash
# 连接OLT
ssh admin@olt_ip
# 查看ONU状态
display onu slot X/X
# 查看端口信息
display interface gigabitethernet X/X/X
# 查看告警信息
display alarm active
```
3. **OLT端诊断命令**
- `display ont info` - 查看ONU信息
- `display ont optical-info` - 查看光信息
- `display ont version` - 查看版本信息
- `display ont statistic` - 查看统计信息
### 2.2 故障决策树
```mermaid
graph TD
A[开始排查] --> B{电源指示灯亮?}
B -->|否| C[检查电源]
B -->|是| D{PON指示灯状态?}
D -->|不亮| E[检查光路]
D -->|闪烁| F[检查注册状态]
D -->|常亮| G{LAN指示灯状态?}
G -->|不亮| H[检查网线/配置]
G -->|正常| I[测试网络连接]
C --> J[更换电源适配器]
J --> K{问题解决?}
K -->|是| L[记录并结束]
K -->|否| M[更换ONU设备]
E --> N[测试光功率]
N --> O{光功率正常?}
O -->|否| P[检查光纤/清洁连接器]
O -->|是| Q[检查OLT配置]
F --> R[检查ONU注册]
R --> S{注册成功?}
S -->|否| T[检查SN/MAC绑定]
S -->|是| U[检查业务配置]
H --> V[更换网线]
V --> W{问题解决?}
W -->|是| X[记录并结束]
W -->|否| Y[检查交换机配置]
I --> Z[Ping测试]
Z --> AA{能Ping通?}
AA -->|否| BB[检查IP配置]
AA -->|是| CC[测试上网]
CC --> DD{能上网?}
DD -->|否| EE[检查PPPoE/DHCP]
DD -->|是| FF[故障排除完成]
```
---
## 第三章:常见故障处理
### 3.1 电源类故障
#### 故障现象
- 所有指示灯不亮
- 设备无任何反应
- 电源适配器发热异常
#### 处理步骤
1. **检查电源插座**
- 使用其他设备测试插座
- 检查插座电压(220V±10%)
2. **检查电源适配器**
- 测量输出电压(应为12V
- 检查输出电流(应≥1A
- 观察指示灯状态
3. **检查设备电源接口**
- 检查接口是否松动
- 检查内部保险丝
- 检查电源电路
#### 解决方案
1. **更换电源适配器**
2. **更换电源线**
3. **更换ONU设备**
### 3.2 光路类故障
#### 故障现象
- PON指示灯不亮或闪烁
- LOS指示灯亮
- 光功率异常
#### 处理步骤
1. **检查光纤连接**
- 确认光纤类型(单模/多模)
- 检查连接器类型(SC/LC)
- 清洁光纤端面
2. **测量光功率**
- 在ONU端测量接收功率
- 在分光器端测量输出功率
- 计算链路衰减
3. **检查光纤质量**
- 检查光纤是否有弯折
- 检查接头是否有损伤
- 检查光纤长度是否超限
#### 解决方案
1. **重新制作光纤接头**
2. **更换光纤跳线**
3. **调整光功率(增加衰减器)**
4. **重新熔接光纤**
### 3.3 注册类故障
#### 故障现象
- PON指示灯闪烁
- OLT无法发现ONU
- 注册超时
#### 处理步骤
1. **检查SN/MAC地址**
- 核对ONU标签上的SN
- 检查OLT端SN绑定
- 检查MAC地址过滤
2. **检查OLT配置**
- 检查端口使能状态
- 检查ONU模板
- 检查带宽配置
3. **检查距离限制**
- 确认光纤长度
- 检查光功率预算
- 确认分光比
#### 解决方案
1. **重新配置SN/MAC**
2. **重启ONU设备**
3. **重置ONU到出厂设置**
4. **修改OLT配置**
### 3.4 业务类故障
#### 故障现象
- PON灯常亮但无法上网
- 能获取IP但无法访问外网
- 网速慢或不稳定
#### 处理步骤
1. **检查VLAN配置**
- 检查业务VLAN ID
- 检查VLAN透传
- 检查优先级标记
2. **检查IP配置**
- 检查DHCP获取情况
- 检查PPPoE拨号
- 检查静态IP配置
3. **检查路由配置**
- 检查默认网关
- 检查DNS服务器
- 检查路由表
#### 解决方案
1. **重新配置VLAN**
2. **重启网络服务**
3. **更新设备固件**
4. **优化网络配置**
### 3.5 性能类故障
#### 故障现象
- 网速达不到标称值
- 延迟高、抖动大
- 频繁掉线
#### 处理步骤
1. **进行网络测试**
- 测试上下行带宽
- 测试网络延迟
- 测试丢包率
2. **检查设备负载**
- 检查CPU使用率
- 检查内存使用率
- 检查温度状态
3. **检查干扰因素**
- 检查同频干扰
- 检查电源干扰
- 检查环境干扰
#### 解决方案
1. **优化设备位置**
2. **更换更高性能设备**
3. **调整信道/频率**
4. **增加散热措施**
---
## 第四章:记录与报告
### 4.1 故障记录规范
#### 基本信息
```yaml
故障单号: [系统生成]
报告人: [维护人员姓名]
报告时间: [YYYY-MM-DD HH:MM]
故障地点: [详细地址]
联系人: [用户姓名电话]
```
#### 故障信息
```yaml
设备信息:
- 设备型号: [H3C XXX]
- 设备SN: [XXXXXXXX]
- MAC地址: [XX:XX:XX:XX:XX:XX]
- 安装日期: [YYYY-MM-DD]
故障现象:
- 用户描述: [用户原话]
- 实际现象: [现场观察]
- 影响范围: [单用户/多用户]
处理过程:
- 到达时间: [HH:MM]
- 开始处理: [HH:MM]
- 解决时间: [HH:MM]
- 总耗时: [X小时X分钟]
```
#### 技术数据
```yaml
测试数据:
- 光功率: [接收: -XX dBm, 发射: -XX dBm]
- 网络测试: [下载: XX Mbps, 上传: XX Mbps]
- 延迟测试: [平均: XX ms, 最大: XX ms]
- 丢包率: [X%]
更换部件:
- 部件名称: [如: 电源适配器]
- 部件型号: [如: H3C-12V-1A]
- 更换原因: [如: 输出电压不稳定]
- 旧件SN: [XXXXXXXX]
- 新件SN: [XXXXXXXX]
```
### 4.2 H3ConuMS2系统更新
#### 设备状态更新
1. **登录系统**
- 进入设备管理界面
- 找到故障设备
2. **更新信息**
- 更新设备状态(在线/离线)
- 更新最后检查时间
- 更新维护记录
3. **添加备注**
- 描述故障现象
- 记录处理过程
- 添加技术数据
#### 生成报告
1. **导出数据**
- 导出设备历史状态
- 导出故障处理记录
- 导出性能测试数据
2. **生成报告**
- 使用系统报告功能
- 选择报告模板
- 自定义报告内容
3. **提交报告**
- 提交给上级主管
- 发送给用户确认
- 归档到知识库
### 4.3 知识积累
#### 故障案例库
1. **记录典型案例**
- 罕见故障现象
- 复杂处理过程
- 创新解决方案
2. **分析故障原因**
- 根本原因分析
- 预防措施建议
- 改进方案设计
3. **分享经验**
- 团队内部分享
- 编写技术文档
- 培训新员工
#### 持续改进
1. **统计分析**
- 统计故障类型分布
- 分析故障发生频率
- 计算平均修复时间
2. **优化流程**
- 根据数据优化排查步骤
- 改进工具配置
- 更新技术标准
3. **预防维护**
- 制定定期检查计划
- 预测性维护
- 设备生命周期管理
---
## 第五章:预防措施
### 5.1 定期维护计划
#### 月度检查
1. **设备清洁**
- 清洁设备表面灰尘
- 清洁散热孔
- 检查风扇运转
2. **连接检查**
- 检查所有连接器
- 检查线缆状态
- 检查标签完整性
3. **性能测试**
- 测试网络速度
- 测试光功率
- 测试设备温度
#### 季度检查
1. **全面诊断**
- 完整的功能测试
- 详细的性能测试
- 安全漏洞扫描
2. **配置备份**
- 备份设备配置
- 备份系统数据
- 验证备份完整性
3. **固件更新**
- 检查固件版本
- 评估更新必要性
- 制定更新计划
#### 年度检查
1. **设备评估**
- 评估设备性能
- 预测剩余寿命
- 制定更换计划
2. **环境评估**
- 评估安装环境
- 检查配套设施
- 提出改进建议
3. **文档更新**
- 更新技术文档
- 更新操作手册
- 更新维护记录
### 5.2 设备安装规范
#### 环境要求
1. **温度控制**
- 工作温度:0°C ~ 40°C
- 存储温度:-40°C ~ 70°C
- 避免阳光直射
2. **湿度控制**
- 工作湿度:10% ~ 90% RH
- 避免冷凝
- 保持通风
3. **电源要求**
- 稳定电压:220V ±10%
- 可靠接地
- 防雷保护
#### 安装规范
1. **设备固定**
- 使用合适支架
- 保持水平安装
- 预留散热空间
2. **线缆管理**
- 分类绑扎线缆
- 预留适当余量
- 避免弯折过度
3. **标签标识**
- 清晰设备标签
- 完整线缆标签
- 准确位置标签
### 5.3 备件管理
#### 备件库存
1. **常用备件**
- ONU设备:按设备总数10%储备
- 电源适配器:按设备总数15%储备
- 光纤跳线:按设备总数20%储备
- 网线:按需储备
2. **关键备件**
- 光模块
- 分光器
- 交换机
3. **工具备件**
- 测试工具
- 维修工具
- 安全装备
#### 库存管理
1. **入库管理**
- 记录备件信息
- 测试备件功能
- 分类存放
2. **出库管理**
- 记录领用信息
- 跟踪使用情况
- 及时补充库存
3. **定期检查**
- 检查备件状态
- 测试备件功能
- 更新库存记录
### 5.4 培训计划
#### 新员工培训
1. **基础培训(第1周)**
- 公司制度介绍
- 安全规范培训
- 工具使用培训
2. **技术培训(第2-3周)**
- 网络基础知识
- 光纤通信原理
- 设备操作培训
3. **实操培训(第4周)**
- 跟随老员工现场学习
- 模拟故障处理
- 独立完成简单任务
#### 在职培训
1. **月度培训**
- 新技术介绍
- 案例分享
- 经验交流
2. **季度培训**
- 技能考核
- 应急演练
- 安全培训
3. **年度培训**
- 技术更新培训
- 认证考试
- 职业发展规划
---
## 第六章:应急处理
### 6.1 紧急情况处理
#### 重大故障
1. **大面积断网**
- 立即上报主管
- 启动应急预案
- 协调多方资源
2. **设备损坏**
- 评估影响范围
- 准备替代方案
- 紧急采购设备
3. **安全事故**
- 确保人员安全
- 保护现场证据
- 按规定报告
#### 沟通协调
1. **内部沟通**
- 建立应急联络群
- 定期更新进展
- 协调技术支持
2. **用户沟通**
- 及时通知用户
- 说明处理进展
- 提供临时方案
3. **供应商沟通**
- 联系技术支持
- 协调备件供应
- 请求现场支持
### 6.2 应急预案
#### 预案制定
1. **风险评估**
- 识别潜在风险
- 评估影响程度
- 制定应对措施
2. **资源准备**
- 准备应急设备
- 准备应急人员
- 准备应急资金
3. **流程设计**
- 设计处理流程
- 明确责任分工
- 设定时间要求
#### 预案演练
1. **桌面演练**
- 模拟故障场景
- 讨论处理方案
- 优化处理流程
2. **实战演练**
- 模拟真实故障
- 实际处理操作
- 评估处理效果
3. **演练总结**
- 分析演练过程
- 发现存在问题
- 提出改进建议
---
## 第七章:附录
### 7.1 常用命令参考
#### OLT常用命令
```bash
# 查看ONU状态
display onu slot X/X
# 查看光信息
display ont optical-info X/X/X X
# 查看版本信息
display ont version X/X/X X
# 查看统计信息
display ont statistic X/X/X X
# 重启ONU
ont reboot X/X/X X
# 查看告警
display alarm active
# 查看端口状态
display interface gigabitethernet X/X/X
```
#### ONU管理命令
```bash
# 登录ONUTelnet/SSH
telnet 192.168.1.1
# 查看系统信息
show system
# 查看接口状态
show interface
# 查看光模块信息
show optical-module
# 查看日志
show log
# 重启设备
reboot
# 恢复出厂设置
factory-reset
```
### 7.2 技术参数参考
#### 光功率标准
| 项目 | 标准值 | 警告值 | 危险值 |
|------|--------|--------|--------|
| 接收光功率 | -8 ~ -27 dBm | -27 ~ -30 dBm | < -30 dBm |
| 发射光功率 | 0 ~ +5 dBm | +5 ~ +7 dBm | > +7 dBm |
| 链路衰减 | < 25 dB | 25 ~ 28 dB | > 28 dB |
#### 温度标准
| 项目 | 正常范围 | 警告范围 | 危险范围 |
|------|----------|----------|----------|
| 工作温度 | 0°C ~ 40°C | 40°C ~ 50°C | > 50°C |
| 存储温度 | -40°C ~ 70°C | - | - |
| 表面温度 | < 60°C | 60°C ~ 70°C | > 70°C |
### 7.3 联系信息
#### 内部联系人
| 部门 | 联系人 | 电话 | 职责 |
|------|--------|------|------|
| 技术支持 | 张三 | 138XXXXXXX | 技术指导 |
| 备件管理 | 李四 | 139XXXXXXX | 备件供应 |
| 项目管理 | 王五 | 137XXXXXXX | 项目协调 |
#### 供应商联系人
| 供应商 | 产品 | 联系人 | 电话 | 支持时间 |
|--------|------|--------|------|----------|
| H3C | OLT设备 | 技术支持 | 400-XXXX | 7x24 |
| 华为 | ONU设备 | 技术支持 | 400-XXXX | 7x24 |
| 中兴 | 光模块 | 技术支持 | 400-XXXX | 工作日 |
### 7.4 模板下载
#### 故障报告模板
[下载链接:故障报告模板.docx]
#### 维护记录模板
[下载链接:维护记录模板.xlsx]
#### 备件清单模板
[下载链接:备件清单模板.xlsx]
---
## 第八章:更新记录
| 版本 | 更新日期 | 更新内容 | 更新人 |
|------|----------|----------|--------|
| v1.0 | 2026-04-08 | 初始版本创建 | 系统团队 |
| v1.1 | - | 计划更新:添加更多案例 | - |
| v1.2 | - | 计划更新:优化流程图 | - |
---
## 使用说明
1. **新员工必读**:入职后一周内完成学习
2. **现场携带**:建议打印关键章节随身携带
3. **定期复习**:每季度复习一次
4. **反馈建议**:发现不足及时反馈
5. **持续改进**:根据实际情况更新内容
## 版权声明
本指南版权归H3ConuMS2项目组所有,未经许可不得外传。
---
**祝您工作顺利,排查无忧!**
如有任何问题或建议,请联系:
- 邮箱:support@h3conums2.com
- 电话:400-XXX-XXXX
- 内部通讯:企业微信/钉钉群
+21 -5
View File
@@ -10,8 +10,8 @@
<el-button v-if="!isMobile && can('device.delete')" type="danger" plain size="small" @click="clearStatus" :loading="clearing">
清空状态
</el-button>
<el-button v-if="can('device.check')" type="warning" size="small" @click="refreshAllStatus" :loading="refreshing">
{{ refreshing ? '更新中' : '全部更新' }}
<el-button v-if="can('device.check')" type="warning" size="small" @click="refreshAllStatus" :loading="refreshing" :disabled="refreshing || refreshCooldown > 0">
{{ refreshing ? '更新中' : refreshCooldown > 0 ? `冷却 ${refreshCooldown}s` : '全部更新' }}
</el-button>
<el-button v-if="can('device.import')" type="success" size="small" @click="importDialogVisible = true">
数据导入
@@ -203,7 +203,7 @@
v-if="can('device.edit')"
class="card-action-btn card-action-btn--primary"
@click.stop="openProvisionFromCard(device)"
:disabled="device.status !== 'online' || !device.port_id"
:disabled="device.status !== 'online' || (!device.port_id && !(device.slot_number && device.port_number))"
>
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/>
@@ -347,7 +347,7 @@
<el-button v-if="can('device.check')" type="info" :loading="deviceRefreshing" @click="doRefreshDevice" :disabled="!selectedDevice.olt_id">更新</el-button>
<el-button v-if="can('device.edit')" type="warning" @click="openReplace">更换</el-button>
<el-button v-if="can('device.edit')" type="primary" @click="openEdit">编辑</el-button>
<el-button v-if="can('device.edit')" type="success" @click="openProvision" :disabled="selectedDevice.status !== 'online' || !selectedDevice.port_id">
<el-button v-if="can('device.edit')" type="success" @click="openProvision" :disabled="selectedDevice.status !== 'online' || (!selectedDevice.port_id && !(selectedDevice.slot_number && selectedDevice.port_number))">
业务下发
</el-button>
<el-button @click="detailVisible = false">关闭</el-button>
@@ -480,7 +480,7 @@ save force</pre>
/>
<div v-if="importResult.failed?.length" style="margin-top: 10px; max-height: 160px; overflow-y: auto">
<div v-for="f in importResult.failed" :key="f.row" style="font-size: 12px; color: var(--danger); padding: 2px 0">
第 {{ f.row }} 行:{{ f.reason }}
第 {{ f.row }} 行<template v-if="f.mac">{{ f.mac }}</template>{{ f.reason }}
</div>
</div>
</div>
@@ -521,6 +521,8 @@ const provisionVisible = ref(false)
const provisionLoading = ref(false)
const provisionResult = ref(null)
const refreshing = ref(false)
const refreshCooldown = ref(0)
let cooldownTimer = null
const clearing = ref(false)
const editVisible = ref(false)
@@ -636,6 +638,7 @@ const clearStatus = async () => {
}
const refreshAllStatus = async () => {
if (refreshCooldown.value > 0) return
refreshing.value = true
checkResultVisible.value = true
checkInProgress.value = true
@@ -654,6 +657,15 @@ const refreshAllStatus = async () => {
} finally {
checkInProgress.value = false
refreshing.value = false
clearInterval(cooldownTimer)
refreshCooldown.value = 60
cooldownTimer = setInterval(() => {
refreshCooldown.value--
if (refreshCooldown.value <= 0) {
clearInterval(cooldownTimer)
refreshCooldown.value = 0
}
}, 1000)
}
}
@@ -691,6 +703,10 @@ const doRefreshDevice = async () => {
selectedDevice.value.status = data.status
selectedDevice.value.distance_m = data.distance_m
if (data.model) selectedDevice.value.model = data.model
if (data.port_id !== undefined) selectedDevice.value.port_id = data.port_id
if (data.slot_number !== undefined) selectedDevice.value.slot_number = data.slot_number
if (data.port_number !== undefined) selectedDevice.value.port_number = data.port_number
if (data.olt_location !== undefined) selectedDevice.value.olt_location = data.olt_location
ElMessage.success(`更新完成:${data.status === 'online' ? '在线' : '离线'}${data.distance_m ? ',距离 ' + formatDistance(data.distance_m) : ''}`)
loadDevices()
} catch (error) {
+52
View File
@@ -454,6 +454,7 @@
</el-table-column>
</el-table>
<template #footer>
<el-button v-if="can('olt.manage') && dupList.length > 0" type="danger" :loading="batchClearing" @click="batchClearAll">批量清除并忽略</el-button>
<el-button @click="dupVisible = false">关闭</el-button>
</template>
</el-dialog>
@@ -533,6 +534,7 @@ const scanResult = ref({ total: 0, new: 0, devices: [], duplicates: [] })
const dupList = ref([])
const duplicateCount = ref(0)
const clearingPort = ref('')
const batchClearing = ref(false)
const newDevList = ref([])
const newDeviceCount = ref(0)
const newDevVisible = ref(false)
@@ -766,6 +768,56 @@ const deleteDupRecord = async (id) => {
}
}
const batchClearAll = async () => {
const total = dupList.value.length
if (total === 0) return
try {
await ElMessageBox.confirm(
`将对 ${total} 条重复 MAC 记录的所有离线端口执行 SSH 清除,然后自动忽略全部记录。此操作不可撤销,确认继续?`,
'批量清除确认',
{ type: 'warning', confirmButtonText: '确认清除', confirmButtonClass: 'el-button--danger' }
)
} catch {
return
}
batchClearing.value = true
// 按 olt_id 分组
const groups = {}
for (const row of dupList.value) {
const key = row.olt_id ?? '__unknown__'
if (!groups[key]) groups[key] = []
groups[key].push(row)
}
let cleared = 0, failed = 0
// 每个 OLT 一个并发任务,组内串行处理完所有重复 MAC 再结束
await Promise.all(
Object.values(groups).map(async (rows) => {
for (const row of rows) {
const offlinePorts = (row.ports || []).filter(p => p.status !== 'online')
for (const p of offlinePorts) {
try {
await request.post(`/olt/duplicate-macs/${row.id}/clear-port`, { port_id: p.port_id })
cleared++
} catch {
failed++
}
}
try {
await request.delete(`/olt/duplicate-macs/${row.id}`)
} catch {}
}
})
)
dupList.value = []
duplicateCount.value = 0
batchClearing.value = false
ElMessage.success(`批量清除完成:清除 ${cleared} 个端口${failed ? `${failed} 个失败` : ''},已忽略全部 ${total} 条记录`)
}
const fetchNewDeviceCount = async () => {
try {
const { data } = await request.get('/olt/new-devices')