分区相关

This commit is contained in:
2026-11-06 08:44:05 +08:00
parent 85c83ec143
commit 8eea42b75d
21 changed files with 3245 additions and 439 deletions

View File

@@ -0,0 +1,117 @@
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/region_draft.dart';
import '../models/region_model.dart';
class RegionRemoteDatasource {
RegionRemoteDatasource(this.dio, this.logger);
final Dio dio;
final ILoggerService logger;
Future<List<RegionDraft>> loadRegions(int siteId) async {
logger.logWithLevel(
'[分区列表] 请求: GET ${HttpApiConsts.regionsBySite}?siteId=$siteId',
shouldLog: true,
);
final response = await dio.get(
HttpApiConsts.regionsBySite,
queryParameters: {'siteId': siteId},
);
final data = response.data;
logger.logWithLevel(
'[分区列表] 响应: HTTP ${response.statusCode}, ${jsonEncode(data)}',
shouldLog: true,
);
if (response.statusCode != 200 ||
data is! Map ||
'${data['code']}' != '200') {
throw Exception(data is Map ? data['msg'] ?? '分区加载失败' : '分区列表响应格式错误');
}
final rows = data['data'] ?? const [];
if (rows is! List) throw const FormatException('分区列表 data 必须为数组');
final result = <int, RegionDraft>{};
for (final row in rows) {
if (row is! Map) continue;
if (row['delFlag'] == true ||
row['type'] != null && row['type'] != 'region') {
continue;
}
final region = RegionModel.fromJson(Map<String, dynamic>.from(row));
if (region.siteId == siteId) result[region.id!] = region;
}
return result.values.toList();
}
Future<bool> deleteRegions(List<int> ids) async {
logger.logWithLevel(
'[分区删除] 请求: POST ${HttpApiConsts.deleteRegions}, 参数: ${jsonEncode(ids)}',
shouldLog: true,
);
final response = await dio.post(HttpApiConsts.deleteRegions, data: ids);
final data = response.data;
logger.logWithLevel(
'[分区删除] 响应: HTTP ${response.statusCode}, ${jsonEncode(data)}',
shouldLog: true,
);
if (response.statusCode != 200 ||
data is! Map ||
'${data['code']}' != '200') {
throw Exception(data is Map ? data['msg'] ?? '分区删除失败' : '分区删除响应格式错误');
}
final result = data['data'];
if (result == false || result is num && result <= 0) {
throw Exception('分区未删除,请重试');
}
return true;
}
Future<RegionSaveResult> save(RegionDraft draft) async {
final body = <String, dynamic>{
if (draft.id != null) 'id': draft.id,
'regionName': draft.name.trim(),
'regionCode': draft.code.trim(),
'siteId': draft.siteId,
'type': 'region',
'remark': draft.remark,
'scope': [
for (final point in draft.scope) {'lat': point.lat, 'lng': point.lng},
],
'status': draft.enabled ? 1 : 0,
'defaultRoute': draft.defaultRouteId,
'bindRoutes': draft.boundRouteIds.toSet().toList()..sort(),
};
final url = HttpApiConsts.saveRegion;
logger.logWithLevel('[分区保存] 请求: POST $url', shouldLog: true);
logger.logWithLevel('[分区保存] 参数: ${jsonEncode(body)}', shouldLog: true);
try {
final response = await dio.post(url, data: body);
logger.logWithLevel(
'[分区保存] 响应: HTTP ${response.statusCode}, ${jsonEncode(response.data)}',
shouldLog: true,
);
final data = response.data;
if (response.statusCode != 200 ||
data is! Map ||
'${data['code']}' != '200') {
throw Exception(data is Map ? data['msg'] ?? '分区保存失败' : '分区保存响应格式错误');
}
final result = data['data'];
if (result == false || result is num && result <= 0) {
throw Exception('分区未保存,请重试');
}
// 数字 data 视为保存数量;只有对象内明确的 id 才能用作后续更新主键。
final returnedId = result is Map ? int.tryParse('${result['id']}') : null;
final id = returnedId != null && returnedId > 0 ? returnedId : draft.id;
logger.logWithLevel(
'[分区保存] 成功,${draft.id == null ? '新建' : '修改'}分区,id=$id',
shouldLog: true,
);
return RegionSaveResult(id: id);
} catch (error) {
logger.logWithLevel('[分区保存] 异常: $error', shouldLog: true);
rethrow;
}
}
}