对接连续任务列表接口和连续任务下发,便捷操作中暂停任务的对接,底部导航tab项的设置根据用户角色显示和设置

This commit is contained in:
2026-11-11 16:51:21 +08:00
parent 47f518f94c
commit 985bcf4b55
54 changed files with 5017 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import '../../../../core/consts/http_api_consts.dart';
import '../../../../core/logging/i_logger_service.dart';
import '../../domain/entities/site_task_plan.dart';
import '../models/site_task_plan_model.dart';
class SiteTaskPlanRemoteDatasource {
const SiteTaskPlanRemoteDatasource(this.dio, this.logger);
final Dio dio;
final ILoggerService logger;
Future<List<SiteTaskPlan>> load(int siteId) async {
final data = await _get(HttpApiConsts.siteDevicePlans, {
'siteId': siteId,
'pageNum': 1,
'pageSize': 99999999,
}, operation: '查询');
if (data is! List) {
throw const FormatException('任务计划查询响应的 data 应为数组');
}
final plans = <int, SiteTaskPlan>{};
for (final item in data) {
if (item is! Map) throw const FormatException('任务计划行格式错误');
final returnedSiteId = item['siteId'];
if (returnedSiteId != null && int.tryParse('$returnedSiteId') != siteId) {
continue;
}
if (item['delFlag'] == true ||
item['delFlag'] == 1 ||
item['delFlag'] == '1' ||
item['delFlag'] == 'true') {
continue;
}
final plan = SiteTaskPlanModel.fromJson(item);
plans[plan.id] = plan;
}
return List.unmodifiable(plans.values);
}
Future<void> delete(int planId) async {
final data = await _get(HttpApiConsts.deleteDevicePlan, {
'id': planId,
}, operation: '删除');
// 删除成功返回受影响行数,如 data: 1。
final affected = data is num ? data : num.tryParse('$data');
if (affected == null || !affected.isFinite || affected <= 0) {
throw const FormatException('没有删除任务计划,请刷新列表后重试');
}
}
Future<Object?> _get(
String url,
Map<String, int> parameters, {
required String operation,
}) async {
logger.logWithLevel(
'[任务计划][$operation] GET $url 参数=${jsonEncode(parameters)}',
shouldLog: true,
);
try {
final response = await dio.get(
url,
queryParameters: parameters,
options: Options(receiveTimeout: const Duration(seconds: 20)),
);
final body = response.data;
final data = body is Map ? body['data'] : null;
logger.logWithLevel(
'[任务计划][$operation] HTTP ${response.statusCode}, '
'code=${body is Map ? body['code'] : null}, '
'msg=${body is Map ? body['msg'] : null}, '
'data=${data is List ? '${data.length} 条' : data}',
shouldLog: true,
);
if (response.statusCode != 200 ||
body is! Map ||
'${body['code']}' != '200') {
throw Exception(
body is Map ? body['msg'] ?? '任务计划${operation}失败' : '任务计划响应格式错误',
);
}
return data;
} on DioException catch (error) {
logger.logWithLevel('[任务计划][$operation] 请求失败: $error', shouldLog: true);
final body = error.response?.data;
if (body is Map && body['msg'] != null) throw Exception(body['msg']);
rethrow;
}
}
}

View File

@@ -0,0 +1,45 @@
import '../../domain/entities/site_task_plan.dart';
class SiteTaskPlanModel {
static String _text(Object? value) => value?.toString().trim() ?? '';
static String? _optionalText(Object? value) {
final text = _text(value);
return text.isEmpty ? null : text;
}
static List<String> _items(Object? value) {
final items = value is List ? value : _text(value).split(RegExp(r'[,,、]'));
return List.unmodifiable(items.map(_text).where((item) => item.isNotEmpty));
}
static SiteTaskPlan fromJson(Map<dynamic, dynamic> json) {
final id = int.tryParse(_text(json['id']));
if (id == null || id <= 0) throw const FormatException('任务计划缺少有效 ID');
final rule = json['deviceTaskPlanRules'];
return SiteTaskPlan(
id: id,
name: _text(json['planName']),
deviceId: _text(json['deviceId']),
deviceAlias: _optionalText(json['deviceAlias']),
// 接口沿用 taskStaus 拼写,兼容规范拼写。
status: _text(json['taskStaus'] ?? json['taskStatus']),
statusLabel: _text(
json['taskStausTranslate'] ?? json['taskStatusTranslate'],
),
routeId: int.tryParse(_text(json['routeId'])),
startsAt: _optionalText(json['startTime']),
endsAt: _optionalText(json['finishTime']),
rule: rule is Map
? SiteTaskPlanRule(
type: _text(rule['type']).toUpperCase(),
time: _optionalText(rule['dayTime']),
weekdays: _items(rule['daysTranslate']).isNotEmpty
? _items(rule['daysTranslate'])
: _items(rule['days']),
monthDays: _items(rule['dayOfMonth']),
)
: null,
);
}
}

View File

@@ -0,0 +1,35 @@
import 'package:fpdart/fpdart.dart';
import '../../../../core/error/failure.dart';
import '../../domain/entities/site_task_plan.dart';
import '../../domain/repositories/site_task_plan_repository.dart';
import '../datasources/site_task_plan_remote_datasource.dart';
class SiteTaskPlanRepositoryImpl implements SiteTaskPlanRepository {
const SiteTaskPlanRepositoryImpl(this.remote);
final SiteTaskPlanRemoteDatasource remote;
Failure _failure(Object error) => Failure(
error is FormatException
? error.message
: error.toString().replaceFirst('Exception: ', ''),
);
@override
Future<Either<Failure, List<SiteTaskPlan>>> load(int siteId) async {
try {
return Right(await remote.load(siteId));
} catch (error) {
return Left(_failure(error));
}
}
@override
Future<Either<Failure, Unit>> delete(int planId) async {
try {
await remote.delete(planId);
return const Right(unit);
} catch (error) {
return Left(_failure(error));
}
}
}