Files
kuaijian_service/bin/service/AreaService.dart
2026-01-20 21:30:10 +08:00

1718 lines
57 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:async';
import 'dart:convert';
import 'package:EasyDartModule/EasyDartModule.dart' as edm;
import '../const/CommonVariables.dart';
import '../model/WebSocketMessage.dart';
import '../const/ServiceConstant.dart';
import '../util/requestWithLog.dart';
import '../enum/ReportStatus.dart';
class AreaService {
Map<String, List<Map<String, dynamic>>> bodyDataCache =
{}; // 存储原始实时数据 mac -> list实时数据列表
Map<String, List<Map<String, dynamic>>> processedDataCache =
{}; // 存储处理过的数据 mac -> list处理数据列表
Map<String, Map<String, dynamic>> userInfoMap = {}; // 存储用户信息 mac -> 用户信息
Map<String, String> deviceUserMap = {}; // 设备与用户的映射 mac -> openId
Map<String, int> macProcess = {}; // 存储检测进度 mac -> 进度百分比
Map<String, int> deviceStartTimeMap = {}; // mac -> 开始体验的时间戳(毫秒)
Map<String, Timer?> deviceTimers = {}; // 设备超时定时器 mac -> Timer
Map<String, int> lastValidDataTime = {}; // 最后有效数据时间 mac -> timestamp
Map<String, int> lastBedStatusTime = {}; // 最后离床状态开始时间 mac -> timestamp
// 新增:进度相关
Map<String, Timer?> _progressTimers = {}; // 进度更新定时器
Map<String, int> _lastProgressTime = {}; // 最后更新进度的时间
// 新增:记录初始数据处理状态
Map<String, bool> _initialDataProcessed = {}; // 是否已处理过初始数据
// 新增:心率直线检测相关
Map<String, List<Map<String, dynamic>>> _heartRateSequenceCache =
{}; // 存储心率连续相同序列
Map<String, bool> _inStraightLineMode = {}; // 是否处于直线检测模式
Map<String, Map<String, dynamic>> _lastStraightLineStart = {}; // 直线开始的第一个数据
// 新增:报告生成状态管理
Map<String, bool> _reportGeneratingMap = {}; // mac -> 是否正在生成报告
Map<String, bool> _reportGeneratedMap = {}; // mac -> 是否已生成报告
int dataLength = 300; // 目标有效数据长度
int initialDataLength = 180; // 初始需要的数据长度3分钟
int noDataTimeout = 20 * 1000; // 20秒无数据超时
int bedOffTimeout = 20 * 1000; // 20秒离床超时
int totalTimeout = 20 * 60 * 1000; // 20分钟总超时
int totalHeartPercent = 80; // 心率有效占比
int standardThreshold = 5; // 允许跳动范围
int heartStillTime = 10; // 心率直线判断阈值
Timer? _offlineCheckTimer;
bool _isTimerRunning = false;
// Redis 相关常量
final int _redisExpiryTime = 120; // Redis 缓存过期时间(秒)
Map<String, String> exceptionCache = {};
AreaService() {
// 从Redis恢复体验中的设备
_recoverExperiences();
// 启动离线检测定时器
_startOfflineCheckTimer();
}
// 从Redis恢复体验中的设备
Future<void> _recoverExperiences() async {
try {
// 使用keys方法获取所有体验中的设备
final keys = await edm.EasyDartModule.redis.scanKeys("experience_*");
for (var key in keys) {
try {
final experienceData = await edm.EasyDartModule.redis.get(key);
if (experienceData != null) {
final data = jsonDecode(experienceData);
final mac = data['mac'];
final openId = data['openId'];
final startTime = data['startTime'];
final now = DateTime.now().millisecondsSinceEpoch;
// 如果体验时间超过20分钟结束体验
if (now - startTime > totalTimeout) {
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "超时自动结束");
await _callRemoteEndExperience(mac, openId, "超时自动结束",
ReportStatus.reportExceptionClose.value);
await edm.EasyDartModule.redis.delete(key);
} else {
// 恢复设备体验状态
deviceStartTimeMap[mac] = startTime;
deviceUserMap[mac] = openId;
// 恢复用户信息
final userInfoKey = "user_info_$mac";
final userInfoData =
await edm.EasyDartModule.redis.get(userInfoKey);
if (userInfoData != null) {
userInfoMap[mac] = jsonDecode(userInfoData);
}
// 恢复初始数据处理状态
_initialDataProcessed[mac] = false;
// 初始化心率序列缓存
_heartRateSequenceCache[mac] = [];
_inStraightLineMode[mac] = false;
// 初始化报告状态
_reportGeneratingMap[mac] = false;
_reportGeneratedMap[mac] = false;
// 启动设备超时定时器
_startDeviceTimeoutTimer(mac);
// 恢复进度定时器
_startProgressTimer(mac);
print("从Redis恢复设备体验: $mac, openId: $openId");
}
}
} catch (e) {
print("恢复设备体验数据失败: $e");
}
}
} catch (e) {
print("从Redis恢复体验失败: $e");
}
}
// 保存体验信息到Redis
Future<void> _saveExperienceToRedis(String mac, Map<String, dynamic> userInfo,
String openId, int startTime) async {
try {
final key = "experience_$mac";
final data = {
'mac': mac,
'openId': openId,
'startTime': startTime,
'saveTime': DateTime.now().millisecondsSinceEpoch,
};
// 保存体验信息
await edm.EasyDartModule.redis.set(key, jsonEncode(data));
// 保存用户信息
final userInfoKey = "user_info_$mac";
await edm.EasyDartModule.redis.set(userInfoKey, jsonEncode(userInfo));
print("保存体验信息到Redis: $mac, openId: $openId");
} catch (e) {
print("保存体验信息到Redis失败: $e");
}
}
// 从Redis删除体验信息
Future<void> _removeExperienceFromRedis(String mac) async {
try {
await edm.EasyDartModule.redis.delete("experience_$mac");
await edm.EasyDartModule.redis.delete("user_info_$mac");
print("从Redis删除体验信息: $mac");
} catch (e) {
print("从Redis删除体验信息失败: $e");
}
}
// 保存process为100的数据到Redis
Future<void> _saveProcess100DataToRedis(String mac, String openId,
String reportId, Map<String, dynamic> data) async {
try {
final key = "report_${openId}_${mac}";
final dataToSave = Map<String, dynamic>.from(data);
dataToSave['reportId'] = reportId;
await edm.EasyDartModule.redis.setWithExpiry(
key,
jsonEncode(dataToSave),
_redisExpiryTime,
);
print("保存process为100的数据到Redis: key=$key, reportId=$reportId");
} catch (e) {
print("保存process为100的数据到Redis失败: $e");
}
}
// 从Redis读取process为100的数据
Future<Map<String, dynamic>?> _getProcess100DataFromRedis(
String mac, String openId) async {
try {
final key = "report_${openId}_${mac}";
final data = await edm.EasyDartModule.redis.get(key);
if (data != null) {
print("从Redis读取process为100的数据: key=$key");
return jsonDecode(data);
}
return null;
} catch (e) {
print("从Redis读取process为100的数据失败: $e");
return null;
}
}
// 启动设备超时定时器
void _startDeviceTimeoutTimer(String mac) {
// 先取消现有的定时器
_stopDeviceTimeoutTimer(mac);
// 启动新的定时器
deviceTimers[mac] = Timer(Duration(milliseconds: totalTimeout), () {
_handleDeviceTimeout(mac);
});
}
// 停止设备超时定时器
void _stopDeviceTimeoutTimer(String mac) {
if (deviceTimers.containsKey(mac) && deviceTimers[mac] != null) {
deviceTimers[mac]!.cancel();
deviceTimers[mac] = null;
}
}
// 处理设备超时
void _handleDeviceTimeout(String mac) async {
final openId = deviceUserMap[mac];
if (openId != null) {
print("设备超时: $mac, openId: $openId");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "体验超时");
await _endExperience(
mac, openId, "体验超时", ReportStatus.reportExceptionClose.value);
}
}
// 启动进度更新定时器
void _startProgressTimer(String mac) {
// 先取消现有的定时器
_stopProgressTimer(mac);
// 每秒更新一次进度
_progressTimers[mac] = Timer.periodic(Duration(seconds: 1), (timer) {
_updateProgress(mac);
});
_lastProgressTime[mac] = DateTime.now().millisecondsSinceEpoch;
}
// 停止进度更新定时器
void _stopProgressTimer(String mac) {
if (_progressTimers.containsKey(mac) && _progressTimers[mac] != null) {
_progressTimers[mac]!.cancel();
_progressTimers[mac] = null;
}
_lastProgressTime.remove(mac);
}
// 更新进度
void _updateProgress(String mac) {
try {
// 如果设备不在体验中,停止定时器
if (!checkUsing(mac)) {
_stopProgressTimer(mac);
return;
}
final startTime = deviceStartTimeMap[mac];
if (startTime == null) return;
final now = DateTime.now().millisecondsSinceEpoch;
final duration = now - startTime;
// 计算基于时间的进度20分钟 = 1200秒
int timeBasedProgress = (duration / (20 * 60 * 1000) * 100).toInt();
// 计算基于数据的进度
final processedCount = processedDataCache[mac]?.length ?? 0;
int dataBasedProgress = (processedCount / dataLength * 100).toInt();
// 综合进度策略:
// 1. 前期前2分钟以时间进度为主
// 2. 中期2-5分钟时间和数据进度结合
// 3. 后期5分钟后以数据进度为主
int finalProgress;
if (duration < 2 * 60 * 1000) {
// 前2分钟
// 80%时间进度 + 20%数据进度
finalProgress =
(timeBasedProgress * 0.8 + dataBasedProgress * 0.2).toInt();
} else if (duration < 5 * 60 * 1000) {
// 2-5分钟
// 50%时间进度 + 50%数据进度
finalProgress =
(timeBasedProgress * 0.5 + dataBasedProgress * 0.5).toInt();
} else {
// 5分钟后
// 20%时间进度 + 80%数据进度
finalProgress =
(timeBasedProgress * 0.2 + dataBasedProgress * 0.8).toInt();
}
// 确保进度不会减少(用户体验角度)
final int currentProgress = macProcess[mac] ?? 0;
if (finalProgress > currentProgress) {
macProcess[mac] = finalProgress.clamp(0, 100);
} else if (duration > 30 * 1000) {
// 30秒后允许轻微下降避免卡住
// 如果有数据但进度不增长,缓慢增加时间进度
if (processedCount > 0 && currentProgress < 10) {
macProcess[mac] = (currentProgress + 1).clamp(0, 100);
}
}
// 如果报告已生成或正在生成进度设为100%
if ((_reportGeneratingMap[mac] ?? false) ||
(_reportGeneratedMap[mac] ?? false)) {
macProcess[mac] = 100;
}
// 记录最后更新时间
_lastProgressTime[mac] = now;
} catch (e) {
print("更新进度异常: $e, MAC=$mac");
}
}
// 析构函数,清理定时器
void dispose() {
_stopOfflineCheckTimer();
// 清理所有设备定时器
deviceTimers.forEach((mac, timer) {
if (timer != null) {
timer.cancel();
}
});
deviceTimers.clear();
// 清理所有进度定时器
_progressTimers.forEach((mac, timer) {
if (timer != null) {
timer.cancel();
}
});
_progressTimers.clear();
// 清理心率序列缓存
_heartRateSequenceCache.clear();
_inStraightLineMode.clear();
_lastStraightLineStart.clear();
// 清理报告状态
_reportGeneratingMap.clear();
_reportGeneratedMap.clear();
}
// 开始离线检测定时器
void _startOfflineCheckTimer() {
if (_isTimerRunning) return;
_isTimerRunning = true;
_offlineCheckTimer = Timer.periodic(Duration(seconds: 1), (timer) {
_checkDeviceOfflineStatus();
});
edm.EasyDartModule.logger.info("离线检测定时器已启动");
}
// 停止离线检测定时器
void _stopOfflineCheckTimer() {
if (_offlineCheckTimer != null) {
_offlineCheckTimer!.cancel();
_offlineCheckTimer = null;
}
_isTimerRunning = false;
edm.EasyDartModule.logger.info("离线检测定时器已停止");
}
// 检查设备离线状态
void _checkDeviceOfflineStatus() {
try {
final now = DateTime.now().millisecondsSinceEpoch;
// 检查无数据超时
lastValidDataTime.forEach((mac, lastTime) {
if (now - lastTime > noDataTimeout) {
final openId = deviceUserMap[mac];
print("设备无数据超时: $mac");
_handleNoDataTimeout(mac);
}
});
// 检查离床状态超时
lastBedStatusTime.forEach((mac, offBedTime) {
if (now - offBedTime > bedOffTimeout) {
final openId = deviceUserMap[mac];
print("设备离床超时: $mac");
_handleBedOffTimeout(mac);
}
});
} catch (e) {
print("检测离线异常: $e");
}
}
// 处理无数据超时
void _handleNoDataTimeout(String mac) async {
final openId = deviceUserMap[mac];
if (openId != null) {
addExceptionEnd(mac, openId!, "设备长时间无数据,请检查设备后重试");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "无数据超时");
await _endExperience(
mac, openId, "无数据超时", ReportStatus.reportExceptionClose.value);
}
}
// 处理离床超时
void _handleBedOffTimeout(String mac) async {
final openId = deviceUserMap[mac];
if (openId != null) {
addExceptionEnd(mac, openId!, "长时间离床,本次体验提前结束");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "离床超时");
await _endExperience(
mac, openId, "离床超时", ReportStatus.reportExceptionClose.value);
}
}
// 开始快检体验
Future<Map<String, dynamic>> startKuaijian(Map<String, dynamic> data) async {
String msg = "";
int flag = 1;
try {
String mac = _normalizeMac(data['mac']);
String openId = data['openId']?.toString() ?? '';
if (openId.isEmpty) {
return {"msg": "openId不能为空", "flag": 0};
}
bool haveRun = await checkHaveRun(data);
if (haveRun) {
print(
"[调试][${DateTime.now().toIso8601String()}]: 已经在运行,跳过 ${data['mac']}");
msg = "该设备正在体验中";
flag = 0;
return {"msg": msg, "flag": flag};
}
// 检查该mac是否正在体验中
bool using = checkUsing(mac);
if (using) {
msg = "该设备正在体验中";
flag = 0;
return {"msg": msg, "flag": flag};
}
bool state = await checkDeviceState(mac);
if (!state) {
msg = "设备离线";
flag = 0;
return {"msg": msg, "flag": flag};
}
// 提取并处理用户信息(设置默认值)
final userInfo = _processUserInfo(data);
// 2. 记录开始体验时间
final startTime = DateTime.now().millisecondsSinceEpoch;
deviceStartTimeMap[mac] = startTime;
deviceUserMap[mac] = openId;
userInfoMap[mac] = userInfo;
lastValidDataTime[mac] = startTime; // 初始化最后有效数据时间
_initialDataProcessed[mac] = false; // 初始化初始数据处理标志
// 3. 初始化心率序列相关缓存
_heartRateSequenceCache[mac] = [];
_inStraightLineMode[mac] = false;
// 4. 初始化报告状态
_reportGeneratingMap[mac] = false;
_reportGeneratedMap[mac] = false;
// 5. 保存到Redis
await _saveExperienceToRedis(mac, userInfo, openId, startTime);
// 6. 启动设备超时定时器
_startDeviceTimeoutTimer(mac);
// 7. 启动进度定时器
_startProgressTimer(mac);
edm.EasyDartModule.logger
.info("记录设备开始体验时间: $mac, openId: $openId, 时间: $startTime");
// 8. 调用服务号后端通知开始体验
await rpcStartKuaijian(data);
// 9. 开始记录实时数据
startGetBodyData(data);
//10.更新远程api
await rpcStart(data);
} catch (e) {
print("申请体验报告失败: $e");
return {"msg": "系统异常", "flag": 0};
}
return {"msg": "开始体验成功", "flag": flag};
}
// 处理用户信息,设置默认值
Map<String, dynamic> _processUserInfo(Map<String, dynamic> data) {
// 设置默认值
final defaultValues = {
'username': '体验用户',
'age': 30,
'height': 170,
'weight': 60,
'gender': 1, // 1:男, 2:女
'phoneNo': '未知',
'deviceNo': data['mac'] ?? '',
};
// 处理用户信息,空值或无效值使用默认值
final userInfo = {
'username':
_getValueOrDefault(data['username'], defaultValues['username']),
'age': _getIntValueOrDefault(data['age'], defaultValues['age']),
'height': _getIntValueOrDefault(data['height'], defaultValues['height']),
'weight': _getIntValueOrDefault(data['weight'], defaultValues['weight']),
'gender': _getIntValueOrDefault(data['gender'], defaultValues['gender']),
'phoneNo': _getValueOrDefault(data['phoneNo'], defaultValues['phoneNo']),
'deviceNo': _getValueOrDefault(
data['deviceNo'] ?? data['mac'], defaultValues['deviceNo']),
};
// 验证年龄、身高、体重范围 - 修复类型转换问题
userInfo['age'] = _validateRange(
userInfo['age'] as int, 1, 120, defaultValues['age'] as int);
userInfo['height'] = _validateRange(
userInfo['height'] as int, 50, 250, defaultValues['height'] as int);
userInfo['weight'] = _validateRange(
userInfo['weight'] as int, 10, 300, defaultValues['weight'] as int);
userInfo['gender'] = _validateRange(
userInfo['gender'] as int, 1, 2, defaultValues['gender'] as int);
print("处理后的用户信息: $userInfo");
return userInfo;
}
// 获取字符串值或默认值
String _getValueOrDefault(dynamic value, String defaultValue) {
if (value == null) return defaultValue;
final strValue = value.toString().trim();
if (strValue.isEmpty) return defaultValue;
return strValue;
}
// 获取整数值或默认值
int _getIntValueOrDefault(dynamic value, int defaultValue) {
if (value == null) return defaultValue;
try {
if (value is String) {
final trimmed = value.trim();
if (trimmed.isEmpty) return defaultValue;
return int.tryParse(trimmed) ?? defaultValue;
} else if (value is int) {
return value;
} else if (value is double) {
return value.toInt();
} else if (value is num) {
return value.toInt();
}
} catch (e) {
print("转换整数值失败: $value, $e");
}
return defaultValue;
}
// 验证数值范围
int _validateRange(int value, int min, int max, int defaultValue) {
if (value >= min && value <= max) {
return value;
}
return defaultValue;
}
// 结束快检体验
Future<Map<String, dynamic>> endKuaijian(Map<String, dynamic> data) async {
String msg = "";
int flag = 1;
try {
String mac = _normalizeMac(data['mac']);
String openId = data['openId']?.toString() ?? '';
if (openId.isEmpty) {
return {"msg": "openId不能为空", "flag": 0};
}
// 检查该设备是否在体验中
if (!checkUsing(mac)) {
return {"msg": "该设备未在体验中", "flag": 0};
}
// 检查是否是同一个用户
if (deviceUserMap[mac] != openId) {
return {"msg": "无权结束该设备的体验", "flag": 0};
}
// 检查是否已经在生成报告
if (_reportGeneratingMap[mac] ?? false) {
return {"msg": "报告正在生成中,请稍候", "flag": 0};
}
// 检查是否已经生成报告
if (_reportGeneratedMap[mac] ?? false) {
return {"msg": "报告已生成,请查看结果", "flag": 0};
}
// 获取当前有效数据量
final processedCount = processedDataCache[mac]?.length ?? 0;
if (processedCount >= dataLength) {
// 正常完成体验,生成报告
final reportId = await _requestSleepAnalytics(mac, openId);
if (reportId != null) {
msg = "体验完成报告ID: $reportId";
await _callRemoteCompleteExperience(
mac, openId, "正常完成", reportId, ReportStatus.completed.value);
} else {
msg = "体验完成,但生成报告失败";
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "报告生成失败");
await _endExperience(
mac, openId, "报告生成失败", ReportStatus.reportExceptionClose.value);
}
} else {
// 提前结束体验
await updateDeviceConnectStatus(ReportStatus.appNormalClose.value, mac,
remark: "用户主动结束");
await _endExperience(
mac, openId, "用户主动结束", ReportStatus.appNormalClose.value);
msg = "体验已提前结束";
}
} catch (e) {
print("结束体验失败: $e");
return {"msg": "系统异常", "flag": 0};
}
return {"msg": msg, "flag": flag};
}
// 结束体验的通用方法
Future<void> _endExperience(
String mac, String openId, String reason, int reportStatus,
{bool rpc = true}) async {
try {
print("结束体验: MAC=$mac, openId=$openId, 原因=$reason");
// 1. 调用远程结束接口
if (rpc) {
await _callRemoteEndExperience(mac, openId, reason, reportStatus);
}
// 2. 清理本地数据
deviceStartTimeMap.remove(mac);
deviceUserMap.remove(mac);
userInfoMap.remove(mac);
bodyDataCache.remove(mac);
processedDataCache.remove(mac);
macProcess.remove(mac);
lastValidDataTime.remove(mac);
lastBedStatusTime.remove(mac);
_initialDataProcessed.remove(mac);
// 3. 清理心率序列相关缓存
_heartRateSequenceCache.remove(mac);
_inStraightLineMode.remove(mac);
_lastStraightLineStart.remove(mac);
// 4. 清理报告状态
_reportGeneratingMap.remove(mac);
_reportGeneratedMap.remove(mac);
// 5. 停止定时器
_stopDeviceTimeoutTimer(mac);
_stopProgressTimer(mac);
// 6. 从Redis删除
await _removeExperienceFromRedis(mac);
// 7. 停止监听设备数据
_stopListeningDeviceData(mac);
// 8. 通知前端
// _notifyExperienceEnd(mac, openId, reason);
edm.EasyDartModule.logger.info("体验已结束: $mac, 原因: $reason");
} catch (e) {
edm.EasyDartModule.logger.error("结束体验异常: $e, MAC=$mac");
}
}
// 请求睡眠分析报告
Future<String?> _requestSleepAnalytics(String mac, String openId) async {
try {
// 检查是否已经在生成报告
if (_reportGeneratingMap[mac] ?? false) {
print("报告已经在生成中,跳过重复请求: $mac");
return null;
}
// 检查是否已经生成报告
if (_reportGeneratedMap[mac] ?? false) {
print("报告已经生成,跳过重复请求: $mac");
return null;
}
final userInfo = userInfoMap[mac];
final processedData = processedDataCache[mac];
if (userInfo == null ||
processedData == null ||
processedData.length < dataLength) {
print("请求报告失败: 数据不足或用户信息缺失");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "数据不足或用户信息缺失");
return null;
}
// 设置报告正在生成中
_reportGeneratingMap[mac] = true;
print("开始生成报告: $mac, openId: $openId, 数据量: ${processedData.length}");
// 准备请求数据
final Map<String, dynamic> requestData = {
"username": userInfo['username'],
"age": userInfo['age'],
"height": userInfo['height'],
"weight": userInfo['weight'],
"gender": userInfo['gender'],
"deviceNo": userInfo['deviceNo'],
"phoneNo": userInfo['phoneNo'],
"dataList": processedData,
};
print(
"用户信息: ${userInfo['username']}, 年龄: ${userInfo['age']}, 电话: ${userInfo['phoneNo']}");
// 发送HTTP请求
final reportId = await _sendReportRequest(requestData, mac, openId);
// 报告生成完成
_reportGeneratingMap[mac] = false;
if (reportId != null) {
_reportGeneratedMap[mac] = true;
print("报告生成成功: $reportId");
return reportId;
} else {
print("报告生成失败");
// await updateDeviceConnectStatus(
// ReportStatus.reportExceptionClose.value, mac,
// remark: "报告生成失败");
return null;
}
} catch (e) {
print("请求睡眠分析异常: $e");
_reportGeneratingMap[mac] = false;
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "请求睡眠分析异常: $e");
return null;
}
}
// 发送报告请求
Future<String?> _sendReportRequest(
Map<String, dynamic> data, String mac, String openId) async {
try {
// 处理 dataList 的数据转换
if (data['dataList'] != null && data['dataList'] is List) {
List<Map<String, dynamic>> originalList =
List<Map<String, dynamic>>.from(data['dataList']);
// 将原始数据转换为新的格式
List<Map<String, dynamic>> transformedList = originalList.map((item) {
// 从原始数据中提取 heartRate 和 breathRate
// 如果字段不存在或为null则使用默认值0
int heartRate = item['heartRate'] is int
? item['heartRate']
: (item['heartRate'] is double
? (item['heartRate'] as double).toInt()
: 0);
int breathRate = item['breathRate'] is int
? item['breathRate']
: (item['breathRate'] is double
? (item['breathRate'] as double).toInt()
: 0);
// 创建新的数据结构
return {
'breath': breathRate,
'heart': heartRate,
};
}).toList();
// 替换原始 dataList 为转换后的数据
data['dataList'] = transformedList;
}
// 打印转换后的数据用于调试
print('转换后的数据: ${jsonEncode(data)}');
await requestSleepAnalytics(data, mac, openId);
return null; // 由于是异步调用这里返回null实际结果在回调中处理
} catch (e) {
print("发送报告请求异常: $e");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "发送报告请求异常: $e");
return null;
}
}
// 调用远程完成接口
Future<void> _callRemoteCompleteExperience(String mac, String openId,
String reason, String reportId, int reportStatus) async {
try {
// 调用您的远程完成接口
print("调用远程完成接口: $mac, openId: $openId, 原因: $reason, 报告ID: $reportId");
// 结束体验
await _endExperience(
mac, openId, "$reason, 报告ID: $reportId", reportStatus);
} catch (e) {
print("调用远程完成接口失败: $e");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "调用远程完成接口失败: $e");
}
}
// 调用远程结束接口
Future<void> _callRemoteEndExperience(
String mac, String openId, String reason, int reportStatus) async {
try {
// 调用您的远程完成接口
print("调用远程结束接口: $mac, openId: $openId, 原因: $reason");
// TODO: 实现具体的远程调用
await updateDeviceConnectStatus(reportStatus, mac, remark: reason);
} catch (e) {
print("调用远程结束接口失败: $e");
}
}
// 通知体验结束
void _notifyExperienceEnd(String mac, String openId, String reason) {
try {
edm.EasyDartModule.websocket.sendData(jsonEncode({
"type": "experience_end",
"mac": mac,
"openId": openId,
"reason": reason,
"timestamp": DateTime.now().millisecondsSinceEpoch,
"message": "体验已结束: $reason"
}));
} catch (e) {
print("发送体验结束通知失败: $e");
}
}
// 检查是否体验中
bool checkUsing(String mac) {
String normalizedMac = _normalizeMac(mac);
return deviceStartTimeMap.containsKey(normalizedMac);
}
// 远程通知分析开始
Future<void> rpcStartKuaijian(data) async {
// TODO: 实现远程开始接口
}
// 开始获取实时数据
void startGetBodyData(data) {
try {
String mac = _normalizeMac(data['mac']);
data['mac'] = mac; // 统一格式
// 监听 WebSocket 实时数据
CommonVariables.callMap["/vsbs/web/rt/marttress"] = (receivedData) {
_handleWebSocketData(receivedData);
};
print("开始监听体征数据");
edm.EasyDartModule.websocket.sendData(jsonEncode(WebSocketMessage(
path: "/vsbs/web/rt/marttress", type: 1, data: {"mac": mac})));
} catch (e) {
edm.EasyDartModule.logger.error("[websocket]获取体征数据异常: $e");
}
}
// 处理WebSocket数据
Future<void> _handleWebSocketData(dynamic receivedData) async {
try {
print("当前心率为: ${receivedData['heartRate']}");
print("当前有效数据为: ${processedDataCache['${receivedData['mac']}']?.length}");
if (receivedData is! Map) return;
// 将Map<dynamic, dynamic>转换为Map<String, dynamic>
final Map<String, dynamic> data = {};
receivedData.forEach((key, value) {
data[key.toString()] = value;
});
// 为数据添加时间戳
data['timestamp'] = DateTime.now().millisecondsSinceEpoch;
// 统一mac格式
if (data.containsKey('mac')) {
String mac = _normalizeMac(data['mac']);
data['mac'] = mac;
// 检查设备是否在体验中
if (!checkUsing(mac)) {
return; // 不在体验中的设备数据直接忽略
}
// 检查是否已经生成报告,如果已经生成则不再处理数据
if (_reportGeneratedMap[mac] ?? false) {
print("报告已生成,不再处理数据: $mac");
return;
}
// 更新最后有效数据时间
lastValidDataTime[mac] = data['timestamp'];
// 处理离床状态
_handleBedStatus(mac, data);
// 处理离线状态
if (data['status'] == "离线") {
final openId = deviceUserMap[mac];
if (openId != null) {
addExceptionEnd(mac, openId!, "当前设备已离线,本次体验提前结束");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "设备离线");
_endExperience(
mac, openId, "设备离线", ReportStatus.reportExceptionClose.value);
}
return;
}
// 存储原始数据
storeInstantData(data);
// 处理数据(包含心率直线检测)
_processDataWithHeartRateDetection(mac, data);
edm.EasyDartModule.logger.info("[websocket]实时体征数据-->$data");
}
} catch (e) {
print("处理WebSocket数据异常: $e");
}
}
// 处理离床状态
void _handleBedStatus(String mac, Map<String, dynamic> data) {
if (data["inBed"] == "离床") {
// 记录离床开始时间
if (!lastBedStatusTime.containsKey(mac)) {
lastBedStatusTime[mac] = data['timestamp'];
}
} else {
// 清除离床计时
lastBedStatusTime.remove(mac);
}
}
// 存储原始实时数据
void storeInstantData(Map<String, dynamic> data) {
try {
String mac = data['mac'];
// 如果不存在该 mac则先创建一个空 list
bodyDataCache.putIfAbsent(mac, () => []);
// 往对应的 list 中添加数据
bodyDataCache[mac]!.add(Map<String, dynamic>.from(data));
// 限制数据长度(保留足够的数据用于处理)
if (bodyDataCache[mac]!.length > dataLength * 2) {
bodyDataCache[mac]!.removeAt(0);
}
} catch (e) {
print("storeInstantData异常: $e");
}
}
Future<void> _processDataWithHeartRateDetection(
String mac, Map<String, dynamic> data) async {
try {
// 检查是否已经生成报告,如果已经生成则不再处理数据
if (_reportGeneratedMap[mac] ?? false) {
return;
}
// 初始化处理数据缓存
processedDataCache.putIfAbsent(mac, () => []);
_heartRateSequenceCache.putIfAbsent(mac, () => []);
final processedList = processedDataCache[mac]!;
final sequenceCache = _heartRateSequenceCache[mac]!;
// 检查数据有效性
if (data['heartRate'] == -1 ||
data['heartRate'] == 0 ||
data['breathRate'] == -1 ||
data['breathRate'] == 0 ||
data['bodyMotion'] != 0 ||
data['inBed'] != '在床') {
// 无效数据,重置序列
if (sequenceCache.isNotEmpty) {
sequenceCache.clear();
}
return;
}
// 获取当前心率
int currentHeartRate = data['heartRate'] is int
? data['heartRate']
: (data['heartRate'] is double
? (data['heartRate'] as double).toInt()
: 0);
// === 修正的核心逻辑 ===
// 如果序列为空或当前心率与序列最后心率相同
if (sequenceCache.isEmpty ||
(sequenceCache.isNotEmpty &&
sequenceCache.last['heartRate'] == currentHeartRate)) {
// 添加到连续相同心率序列
sequenceCache.add({
'heartRate': currentHeartRate,
'data': data,
'timestamp': data['timestamp'],
});
} else {
// 心率发生变化 - 处理之前的序列
_processHeartRateSequence(mac, sequenceCache, processedList);
// 清空序列,开始新的序列
sequenceCache.clear();
sequenceCache.add({
'heartRate': currentHeartRate,
'data': data,
'timestamp': data['timestamp'],
});
}
// === 修正的核心逻辑结束 ===
// 处理初始数据(只执行一次)
if (processedList.length == initialDataLength &&
!_initialDataProcessed[mac]!) {
print("开始处理初始数据,数据量: ${processedList.length}");
_processInitialData(mac);
_initialDataProcessed[mac] = true;
}
// 检查是否达到目标数据量 - 修改为只触发一次
if (processedList.length >= dataLength &&
!(_reportGeneratingMap[mac] ?? false) &&
!(_reportGeneratedMap[mac] ?? false)) {
final openId = deviceUserMap[mac];
if (openId != null) {
print("达到目标数据量,开始生成报告: $mac, 数据量: ${processedList.length}");
// 异步生成报告,避免阻塞数据处理
Future.delayed(Duration.zero, () {
_requestSleepAnalytics(mac, openId).then((reportId) {
if (reportId != null) {
_callRemoteCompleteExperience(mac, openId, "数据采集完成", reportId,
ReportStatus.completed.value);
} else {
// 报告生成失败,继续收集数据
print("报告生成失败,继续收集数据: $mac");
}
});
});
}
}
print("当前有效数据为: ${processedDataCache['$mac']?.length}");
} catch (e) {
print("处理数据异常: $e, MAC=$mac");
// 处理数据异常时更新状态
if (checkUsing(mac)) {
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "处理数据异常: $e");
}
}
}
// 新增:处理心率序列的辅助方法
void _processHeartRateSequence(
String mac,
List<Map<String, dynamic>> sequenceCache,
List<Map<String, dynamic>> processedList) {
if (sequenceCache.isEmpty) return;
if (sequenceCache.length >= heartStillTime) {
// 连续相同心率达到或超过10次 - 只保留第一个和最后一个
print("连续相同心率达到${sequenceCache.length}次(>=${heartStillTime}),进行去重处理");
// 第一个数据点
final firstData = sequenceCache.first['data'];
final firstProcessedData = {
'mac': mac,
'heartRate': firstData['heartRate'],
'breathRate': firstData['breathRate'],
'timestamp': firstData['timestamp'],
'bodyMotion': firstData['bodyMotion'],
};
// 检查是否重复(与上一个处理的数据比较)
if (processedList.isEmpty ||
processedList.last['heartRate'] != firstProcessedData['heartRate'] ||
processedList.last['breathRate'] !=
firstProcessedData['breathRate']) {
processedList.add(firstProcessedData);
print("添加直线开始数据点: 心率 ${firstProcessedData['heartRate']}");
}
// 最后一个数据点
final lastData = sequenceCache.last['data'];
final lastProcessedData = {
'mac': mac,
'heartRate': lastData['heartRate'],
'breathRate': lastData['breathRate'],
'timestamp': lastData['timestamp'],
'bodyMotion': lastData['bodyMotion'],
};
// 检查是否重复(与上一个处理的数据比较)
if (processedList.isEmpty ||
processedList.last['heartRate'] != lastProcessedData['heartRate'] ||
processedList.last['breathRate'] != lastProcessedData['breathRate']) {
processedList.add(lastProcessedData);
print("添加直线结束数据点: 心率 ${lastProcessedData['heartRate']}");
}
} else {
// 连续相同心率少于10次 - 全部保留
print("连续相同心率${sequenceCache.length}次(<${heartStillTime}),全部保留");
for (var seqData in sequenceCache) {
final processedData = {
'mac': mac,
'heartRate': seqData['data']['heartRate'],
'breathRate': seqData['data']['breathRate'],
'timestamp': seqData['data']['timestamp'],
'bodyMotion': seqData['data']['bodyMotion'],
};
// 检查是否重复(与上一个处理的数据比较)
// 这里注意:对于连续相同的数据,我们不应该去重,所以去掉这个检查
processedList.add(processedData);
}
}
}
// 处理初始数据前3分钟
void _processInitialData(String mac) async {
try {
final processedList = processedDataCache[mac]!;
// 1. 检查数据量是否足够
if (processedList.length < initialDataLength ~/ 2) {
// 数据量少于一半,结束体验
final openId = deviceUserMap[mac];
if (openId != null) {
print("初始数据不足,结束体验: $mac, 数据量: ${processedList.length}");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "初始数据不足");
_endExperience(
mac, openId, "初始数据不足", ReportStatus.reportExceptionClose.value);
}
return;
}
// 2. 计算平均心率
int totalHeartRate = 0;
for (var data in processedList) {
final heartRate = data['heartRate'];
if (heartRate is int) {
totalHeartRate += heartRate;
} else if (heartRate is num) {
totalHeartRate += heartRate.toInt();
} else if (heartRate is String) {
totalHeartRate += int.tryParse(heartRate) ?? 0;
}
}
double avgHeartRate = totalHeartRate / processedList.length;
// 3. 计算在平均值±5范围内的数据百分比
int inRangeCount = 0;
for (var data in processedList) {
final heartRate = data['heartRate'];
double? hrValue;
if (heartRate is int) {
hrValue = heartRate.toDouble();
} else if (heartRate is num) {
hrValue = heartRate.toDouble();
} else if (heartRate is String) {
hrValue = double.tryParse(heartRate);
}
if (hrValue != null &&
hrValue >= avgHeartRate - standardThreshold &&
hrValue <= avgHeartRate + standardThreshold) {
inRangeCount++;
}
}
double inRangePercent = (inRangeCount / processedList.length) * 100;
// 4. 如果小于阈值,结束体验
if (inRangePercent < totalHeartPercent) {
final openId = deviceUserMap[mac];
if (openId != null) {
print(
"心率数据不稳定,结束体验: $mac, 平均心率: $avgHeartRate, 在范围比例: $inRangePercent%");
addExceptionEnd(mac, openId, "心率数据不稳定,请请保持平静重新测试");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "心率数据不稳定");
_endExperience(
mac, openId, "心率数据不稳定", ReportStatus.reportExceptionClose.value);
}
return;
}
print("初始数据处理通过: $mac, 平均心率: $avgHeartRate, 在范围比例: $inRangePercent%");
} catch (e) {
print("处理初始数据异常: $e, MAC=$mac");
// 处理初始数据异常时更新状态
if (checkUsing(mac)) {
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "处理初始数据异常: $e");
}
}
}
// 获取实时数据 - 修改后的版本
Future<Map<String, dynamic>> getInstantData(String mac, String openId) async {
String normalizedMac = _normalizeMac(mac);
String msg = "";
int flag = 1;
var data;
// 检查设备是否在体验中
if (!deviceStartTimeMap.containsKey(normalizedMac)) {
flag = -1;
msg = "该设备未开始体验";
data = null;
final redisData =
await _getProcess100DataFromRedis(normalizedMac, openId);
if (redisData != null) {
data = redisData;
flag = 1;
msg = "从Redis读取数据成功";
} else {
flag = 0;
msg = "该设备暂无数据";
data = null;
}
}
// 检查用户是否匹配
else if (deviceUserMap[normalizedMac] != openId) {
flag = -2;
msg = "无权查看该设备数据";
data = null;
}
// 检查是否有缓存数据
else if (!bodyDataCache.containsKey(normalizedMac) ||
bodyDataCache[normalizedMac]!.isEmpty) {
// 如果没有本地缓存数据尝试从Redis读取
final redisData =
await _getProcess100DataFromRedis(normalizedMac, openId);
if (redisData != null) {
data = redisData;
flag = 1;
msg = "从Redis读取数据成功";
} else {
flag = 0;
msg = "该设备暂无数据";
data = null;
}
} else {
// 取最新一次数据
data = Map<String, dynamic>.from(bodyDataCache[normalizedMac]!.last);
// 添加进度信息
data['process'] = macProcess[normalizedMac] ?? 0;
data['openId'] = openId;
// 添加体验时长
final startTime = deviceStartTimeMap[normalizedMac]!;
final duration = DateTime.now().millisecondsSinceEpoch - startTime;
data['experienceDuration'] = duration ~/ 1000; // 转换为秒
// 添加有效数据量
final processedCount = processedDataCache[normalizedMac]?.length ?? 0;
data['processedDataCount'] = processedCount;
data['targetDataCount'] = dataLength;
// 添加报告状态
data['reportGenerating'] = _reportGeneratingMap[normalizedMac] ?? false;
data['reportGenerated'] = _reportGeneratedMap[normalizedMac] ?? false;
// 添加用户信息(可选)
data['userInfo'] = userInfoMap[normalizedMac];
// 添加空的reportId字段
data['reportId'] = '';
}
String? info;
if (flag != 1) {
info = fillExceptionInfo(mac, openId);
// 如果info不为空用info覆盖msg
if (info != null && info.isNotEmpty) {
msg = info;
}
}
return {"msg": msg, "flag": flag, "data": data};
}
// 停止监听设备数据
void _stopListeningDeviceData(String mac) {
try {
edm.EasyDartModule.websocket.sendData(jsonEncode(WebSocketMessage(
path: "/vsbs/web/rt/marttress", type: 2, data: {"mac": mac})));
} catch (e) {
print("停止监听设备数据失败: $e");
}
}
// 获取所有正在体验的设备列表
List<Map<String, dynamic>> getActiveDevices() {
return deviceStartTimeMap.entries.map((entry) {
final mac = entry.key;
return {
'mac': mac,
'openId': deviceUserMap[mac],
'startTime': entry.value,
'progress': macProcess[mac] ?? 0,
'processedData': processedDataCache[mac]?.length ?? 0,
'reportGenerating': _reportGeneratingMap[mac] ?? false,
'reportGenerated': _reportGeneratedMap[mac] ?? false,
'userInfo': userInfoMap[mac],
};
}).toList();
}
// 获取设备体验时长
int? getDeviceExperienceDuration(String mac) {
String normalizedMac = _normalizeMac(mac);
if (deviceStartTimeMap.containsKey(normalizedMac)) {
final now = DateTime.now().millisecondsSinceEpoch;
return now - deviceStartTimeMap[normalizedMac]!;
}
return null;
}
// 统一mac地址格式
String _normalizeMac(String mac) {
if (mac == null || mac.isEmpty) return '';
String normalized =
mac.toString().replaceAll(RegExp(r'[:-\s]'), '').toUpperCase().trim();
return normalized;
}
// 调试方法:打印当前状态
void printStatus() {
print("=== AreaService 状态 ===");
print("正在体验的设备:");
deviceStartTimeMap.forEach((mac, startTime) {
final openId = deviceUserMap[mac];
final progress = macProcess[mac] ?? 0;
final processedCount = processedDataCache[mac]?.length ?? 0;
final reportGenerating = _reportGeneratingMap[mac] ?? false;
final reportGenerated = _reportGeneratedMap[mac] ?? false;
final userInfo = userInfoMap[mac];
print(" $mac (用户: $openId) - 进度: $progress%, 有效数据: $processedCount");
print(" 报告状态: 生成中=$reportGenerating, 已生成=$reportGenerated");
print(
" 用户信息: ${userInfo?['username']}, 年龄: ${userInfo?['age']}, 电话: ${userInfo?['phoneNo']}");
});
print("======================");
}
checkHaveRun(data) async {
data['deviceNo'] = data['mac'];
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.app_server_service;
String serviceApi = "/checkreport/${data['mac']}";
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
bool flag = false;
await requestWithLog(
logTitle: "APP端提检查快检报告开始体验",
method: MyHttpMethod.get,
queryUrl: queryUrl,
data: data['data'],
onSuccess: (res) {
flag = res.data;
print("[调试][成功][${DateTime.now().toIso8601String()}]: 接收到数据结果为${flag}");
},
onFailure: (res) {
flag = true;
print("[调试][失败][${DateTime.now().toIso8601String()}]: 接收到数据结果为${flag}");
},
);
return flag;
}
Future<void> rpcStart(Map<String, dynamic> data) async {
try {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.app_server_service;
String serviceApi = "/reportsubmit";
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
data['openid'] = data['openId'];
data['status'] = ReportStatus.experienceing.value;
await requestWithLog(
logTitle: "APP端提交快检报告开始体验",
method: MyHttpMethod.post,
queryUrl: queryUrl,
data: data,
onSuccess: (res) {
print("通知体验开始成功");
},
onFailure: (res) {
print("提交快检报告开始体验失败");
},
);
} catch (e) {
print("远程调用失败:$e");
}
}
Future<void> requestSleepAnalytics(
Map<String, dynamic> data, String mac, String openId) async {
try {
String queryUrl =
"https://health.ciotcp.com/api/api/h5/getReportByOtherData";
int retryCount = 0;
bool isSuccess = false;
String? reportId;
while (retryCount < 3 && !isSuccess) {
await requestWithLog(
logTitle: "请求实时分析",
method: MyHttpMethod.post,
data: data,
queryUrl: queryUrl,
onSuccess: (res) async {
if (res.data == null || res.data == "") {
retryCount++;
if (retryCount < 3) {
print("请求失败,正在尝试第${retryCount}次重试...");
} else {
print("请求失败已尝试3次停止重试");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "报告请求失败");
}
} else {
isSuccess = true;
reportId = res.data;
print("请求成功报告ID: $reportId");
print(
"报告地址: https://health.ciotcp.com/mobile/quickDetail?type=quickCheck&useFrom=Quansi&reportId=$reportId");
// 保存process为100的数据到Redis
if (bodyDataCache.containsKey(mac) &&
bodyDataCache[mac]!.isNotEmpty) {
final lastData = bodyDataCache[mac]!.last;
final process100Data = Map<String, dynamic>.from(lastData);
process100Data['process'] = 100; // 确保process为100
await _saveProcess100DataToRedis(
mac, openId, reportId!, process100Data);
}
await finishDevice(reportId!, mac);
await _endExperience(
mac, openId, "体验结束", ReportStatus.reportExceptionClose.value,
rpc: false);
}
},
onFailure: (res) async {
if (res.code == 20000 && res.data != null) {
// 请求成功,通常不应该进入这里,但为了确保
isSuccess = true;
reportId = res.data;
print("请求成功报告ID: $reportId");
// 保存process为100的数据到Redis
if (bodyDataCache.containsKey(mac) &&
bodyDataCache[mac]!.isNotEmpty) {
final lastData = bodyDataCache[mac]!.last;
final process100Data = Map<String, dynamic>.from(lastData);
process100Data['process'] = 100; // 确保process为100
await _saveProcess100DataToRedis(
mac, openId, reportId!, process100Data);
}
await finishDevice(reportId!, mac);
} else {
// 请求失败,重新尝试
retryCount++;
if (retryCount < 3) {
print("请求失败,正在尝试第${retryCount}次重试...");
} else {
print("请求失败已尝试3次停止重试");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "报告请求失败");
}
}
},
);
}
if (!isSuccess) {
print("最终请求失败,无法获取报告数据");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "最终请求失败,无法获取报告数据");
}
} catch (e) {
print("[请求报告失败]:报告异常:$e");
print("[微信]:报告异常:$e");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "请求报告失败异常: $e");
}
}
Future<void> finishDevice(String reportId, String mac) async {
try {
// 从 deviceUserMap 中获取对应的 openid
String? openid = deviceUserMap[mac];
if (openid == null) {
print("无法找到设备 $mac 对应的用户信息");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "无法找到用户信息");
return;
}
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.app_server_service;
String serviceApi = "/reportfinish";
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
// 从 userInfoMap 中获取 deviceNo 或使用 mac
final userInfo = userInfoMap[mac];
String deviceNo = userInfo?['deviceNo'] ?? mac;
Map<String, dynamic> finishData = {
"deviceNo": deviceNo,
"openid": openid,
"reportId": reportId,
};
print("发送报告完成请求: deviceNo=$deviceNo, openid=$openid, reportId=$reportId");
await requestWithLog(
logTitle: "更新完成状态",
method: MyHttpMethod.post,
data: finishData,
queryUrl: queryUrl,
onSuccess: (res) {
print("[微信]:报告完成成功 $finishData");
},
onFailure: (res) {
print("[微信]:报告完成失败 $finishData");
// 报告完成失败时也要更新状态
updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "报告完成失败");
},
);
} catch (e) {
print("发送报告完成请求异常: $e");
await updateDeviceConnectStatus(
ReportStatus.reportExceptionClose.value, mac,
remark: "发送报告完成请求异常: $e");
}
}
//更新设备状态
Future<void> updateDeviceConnectStatus(int status, String mac,
{String? remark}) async {
try {
String serviceAddress = ServiceConstant.service_address;
String serviceName = ServiceConstant.app_server_service;
String serviceApi = "/reportupdatestatus";
String queryUrl = "${serviceAddress}${serviceName}${serviceApi}";
String? openid = deviceUserMap[mac];
if (openid == null) {
print("无法找到设备 $mac 对应的用户信息");
return;
}
// 初始化 data
Map<String, dynamic> data = {
"deviceNo": mac,
"openid": openid,
"status": status,
};
// 如果 remark 不为空,则添加到 data 中
if (remark != null && remark.isNotEmpty) {
data['remark'] = remark;
}
await requestWithLog(
logTitle: "更新设备处理状态",
method: MyHttpMethod.post,
data: data,
queryUrl: queryUrl,
onSuccess: (res) {
print("[微信]:更新状态成功为$data");
},
onFailure: (res) {
//更新失败
print("[微信]:更新状态失败,数据为$data");
},
);
} catch (e) {
print("[微信]:更新失状态败-》$e");
}
}
checkDeviceState(String mac) async {
try {
bool flag = false;
String queryUrl =
"https://iot.he-info.cn/api/iot/device/info/status?did=${mac}&token=M5Y3B4Frr6T1ACtwMfTNaSaxE2DT16Tn";
await requestWithLog(
logTitle: "查询设备状态",
method: MyHttpMethod.get,
queryUrl: queryUrl,
onSuccess: (res) {
flag = (res.data[0]['state']) == 3 || (res.data[0]['state']) == 4
? true
: false;
},
onFailure: (res) {
//更新失败
flag = (res.data[0]['state']) == 3 || (res.data[0]['state']) == 4
? true
: false;
},
);
return flag;
} catch (e) {
print("获取设备状态失败,数据为$e");
return false;
}
}
void addExceptionEnd(String mac, String openId, String message) {
exceptionCache[mac + openId] = message;
}
String? fillExceptionInfo(String mac, String openId) {
String key = "$mac$openId"; // 创建缓存key
String? info = exceptionCache[key]; // 获取异常信息
if (info != null && info.isNotEmpty) {
// 获取后立即删除,确保只返回一次
exceptionCache.remove(key);
print("获取异常信息后删除缓存: key=$key, info=$info");
}
return info;
}
}