Files
flutterApp/lib/features/devices/data/datasources/device_task_datasource.dart

289 lines
9.7 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:convert';
import 'package:dio/dio.dart';
import 'package:get_it/get_it.dart';
import '../../../../core/consts/http_api_consts.dart';
import '../../../../core/logging/i_logger_service.dart';
import '../../domain/entities/device_schedule_plan_entity.dart';
import '../models/device_task_model.dart';
abstract class DeviceTaskDatasource {
Future<List<DeviceTaskModel>> getDeviceTaskPool({
required String userId,
required int siteId,
required int orgId,
int pageNum = 1,
int pageSize = 99999999,
});
Future<bool> cancelTask({
required String deviceId,
required int taskId,
required int orgId,
required int siteId,
bool forceCancel = true,
});
Future<bool> pauseTask({
required String deviceId,
required int taskId,
required int orgId,
required int siteId,
});
Future<Map<String, dynamic>> recoveryTask({
required String deviceId,
required int taskId,
required int orgId,
required int siteId,
});
/// 创建设备定时任务(定时作业计划)POST /iot/deviceTask/save
/// 周期规则报文组装封在实现内,调用方只交业务实体
Future<bool> saveDeviceSchedulePlan({required DeviceSchedulePlanEntity plan});
}
class DeviceTaskDatasourceImpl implements DeviceTaskDatasource {
final Dio dio;
final ILoggerService _logger = GetIt.I<ILoggerService>();
DeviceTaskDatasourceImpl(this.dio);
@override
Future<List<DeviceTaskModel>> getDeviceTaskPool({
required String userId,
required int siteId,
required int orgId,
int pageNum = 1,
int pageSize = 99999999,
}) async {
try {
final url = '${HttpApiConsts.baseUrl}/iot/deviceTask/deviceTaskPool';
_logger.logWithLevel('[getDeviceTaskPool] 请求: POST $url', shouldLog: true);
_logger.logWithLevel('[getDeviceTaskPool] 参数: userId=$userId, siteId=$siteId, orgId=$orgId, pageNum=$pageNum, pageSize=$pageSize', shouldLog: true);
final response = await dio.post(
url,
data: {
'userId': userId,
'siteId': siteId,
'orgId': orgId,
'pageNum': pageNum,
'pageSize': pageSize,
},
);
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
if (data['code'] == 200) {
final rows = data['rows'] as List? ?? [];
_logger.logWithLevel('[getDeviceTaskPool] 返回任务 ${rows.length} 条', shouldLog: true);
return rows
.map((item) => DeviceTaskModel.fromJson(item as Map<String, dynamic>))
.toList();
} else {
throw Exception(data['msg'] ?? '获取任务池失败');
}
} else {
throw Exception('HTTP ${response.statusCode}');
}
} catch (e) {
_logger.logWithLevel('❌ 获取任务池失败: $e');
rethrow;
}
}
@override
Future<bool> cancelTask({
required String deviceId,
required int taskId,
required int orgId,
required int siteId,
bool forceCancel = true,
}) async {
final url = '${HttpApiConsts.baseUrl}/iot/deviceTask/cancelTask';
final body = {
'taskId': taskId,
'siteId': siteId,
'orgId': orgId,
'forceCancel': forceCancel,
};
_logger.logWithLevel('[cancelTask] 请求: POST $url');
_logger.logWithLevel('[cancelTask] 参数: ${jsonEncode(body)}');
try {
final response = await dio.post(url, data: body);
_logger.logWithLevel('[cancelTask] 响应: statusCode=${response.statusCode}, body=${jsonEncode(response.data)}');
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
if (data['code'] == 200) {
_logger.logWithLevel('[cancelTask] 结果: 成功');
return data['data'] as bool? ?? false;
} else {
_logger.logWithLevel('[cancelTask] 业务失败: code=${data['code']}, msg=${data['msg']}');
throw Exception(data['msg'] ?? '取消任务失败');
}
} else {
throw Exception('HTTP ${response.statusCode}');
}
} catch (e) {
_logger.logWithLevel('[cancelTask] 异常: $e');
rethrow;
}
}
@override
Future<bool> pauseTask({
required String deviceId,
required int taskId,
required int orgId,
required int siteId,
}) async {
final url = '${HttpApiConsts.baseUrl}/iot/deviceTask/pauseTask';
final body = {
'deviceId': deviceId,
'taskId': taskId,
'orgId': orgId,
'siteId': siteId,
};
_logger.logWithLevel('[pauseTask] 请求: POST $url');
_logger.logWithLevel('[pauseTask] 参数: ${jsonEncode(body)}');
try {
final response = await dio.post(url, data: body);
_logger.logWithLevel('[pauseTask] 响应: statusCode=${response.statusCode}, body=${jsonEncode(response.data)}');
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
if (data['code'] == 200) {
_logger.logWithLevel('[pauseTask] 结果: 成功');
return data['data'] as bool? ?? false;
} else {
_logger.logWithLevel('[pauseTask] 业务失败: code=${data['code']}, msg=${data['msg']}');
throw Exception(data['msg'] ?? '暂停任务失败');
}
} else {
throw Exception('HTTP ${response.statusCode}');
}
} catch (e) {
_logger.logWithLevel('[pauseTask] 异常: $e');
rethrow;
}
}
@override
Future<Map<String, dynamic>> recoveryTask({
required String deviceId,
required int taskId,
required int orgId,
required int siteId,
}) async {
final url = '${HttpApiConsts.baseUrl}/iot/deviceTask/recoveryTask';
final body = {
'deviceId': deviceId,
'taskId': taskId,
'orgId': orgId,
'siteId': siteId,
};
_logger.logWithLevel('[recoveryTask] 请求: POST $url');
_logger.logWithLevel('[recoveryTask] 参数: ${jsonEncode(body)}');
try {
final response = await dio.post(url, data: body);
_logger.logWithLevel('[recoveryTask] 响应: statusCode=${response.statusCode}, body=${jsonEncode(response.data)}');
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
if (data['code'] == 200) {
_logger.logWithLevel('[recoveryTask] 结果: 成功, data=${jsonEncode(data['data'])}');
return data['data'] as Map<String, dynamic>? ?? {};
} else {
_logger.logWithLevel('[recoveryTask] 业务失败: code=${data['code']}, msg=${data['msg']}');
throw Exception(data['msg'] ?? '恢复任务失败');
}
} else {
throw Exception('HTTP ${response.statusCode}');
}
} catch (e) {
_logger.logWithLevel('[recoveryTask] 异常: $e');
rethrow;
}
}
@override
Future<bool> saveDeviceSchedulePlan({
required DeviceSchedulePlanEntity plan,
}) async {
final url = '${HttpApiConsts.baseUrl}/iot/deviceTask/save';
final body = _schedulePlanBody(plan);
_logger.logWithLevel('[saveDeviceSchedulePlan] 请求: POST $url', shouldLog: true);
_logger.logWithLevel('[saveDeviceSchedulePlan] 参数: ${jsonEncode(body)}', shouldLog: true);
try {
final response = await dio.post(url, data: body);
_logger.logWithLevel('[saveDeviceSchedulePlan] 响应: statusCode=${response.statusCode}, body=${jsonEncode(response.data)}', shouldLog: true);
if (response.statusCode == 200) {
final data = response.data as Map<String, dynamic>;
if (data['code'] == 200) {
// 保存接口返回数量(如 data: 1),同时兼容布尔值及无 data 的成功响应。
final saved = switch (data['data']) {
bool value => value,
num value => value > 0,
_ => true,
};
_logger.logWithLevel(
'[saveDeviceSchedulePlan] 结果: ${saved ? '成功' : '未保存'}',
shouldLog: true,
);
return saved;
} else {
_logger.logWithLevel('[saveDeviceSchedulePlan] 业务失败: code=${data['code']}, msg=${data['msg']}', shouldLog: true);
throw Exception(data['msg'] ?? '创建定时计划失败');
}
} else {
throw Exception('HTTP ${response.statusCode}');
}
} catch (e) {
_logger.logWithLevel('[saveDeviceSchedulePlan] 异常: $e', shouldLog: true);
rethrow;
}
}
/// 业务实体 → 后端报文:字段名 / DAY・WEEK・MONTH 枚举字面量 /
/// days・dates 条件结构 / 时间格式,全部封在数据层,不外泄到 domain。
static Map<String, dynamic> _schedulePlanBody(DeviceSchedulePlanEntity p) {
// 下标 = weekday.value - 1(周一~周日)
const List<String> weekdayWire = <String>[
'MONDAY',
'TUESDAY',
'WEDNESDAY',
'THURSDAY',
'FRIDAY',
'SATURDAY',
'SUNDAY',
];
String pad(int value) => value.toString().padLeft(2, '0');
String dateText(DateTime d) =>
'${d.year}-${pad(d.month)}-${pad(d.day)}';
return <String, dynamic>{
'deviceId': p.deviceId,
'orgId': p.orgId,
'planName': p.planName.trim(),
'routeId': p.routeId,
'siteId': p.siteId,
'deviceTaskPlanRules': <String, dynamic>{
'type': switch (p.type) {
WorkScheduleType.day => 'DAY',
WorkScheduleType.week => 'WEEK',
WorkScheduleType.month => 'MONTH',
},
if (p.type == WorkScheduleType.week)
'days': <String>[
for (final Weekday w in p.days) weekdayWire[w.value - 1],
],
if (p.type == WorkScheduleType.month) 'dates': p.dates,
'dayTime': '${pad(p.dayHour)}:${pad(p.dayMinute)}:00',
},
'startTime': '${dateText(p.startDate)} 00:00:00',
'finishTime': '${dateText(p.endDate)} 23:59:59',
};
}
}