Merge branch 'feature/my' of http://1.95.137.212:57001/APP/FlutterApp into feature/my

This commit is contained in:
mmc
2026-02-26 10:45:07 +08:00
15 changed files with 214 additions and 6 deletions

View File

@@ -14,4 +14,11 @@ class HttpApiConsts {
static const String unbindDevice = "$baseUrl/forward/device/unbind";
// 切换设备
static const String switchDevice = "$baseUrl/forward/device/switchDevice";
// 获取设备位置
static const String getDeviceLocation = "$baseUrl/iot/device/userDevice";
}

View File

@@ -23,8 +23,12 @@ import '../../features/auth/presentation/bloc/login_cubit.dart';
import '../../features/devices/data/datasources/device_http_datasource.dart';
import '../../features/devices/data/repositories/device_hostriry_work_impl.dart';
import '../../features/devices/data/repositories/device_repository_impl.dart';
import '../../features/devices/data/repositories/generate_path_repository_Impl.dart';
import '../../features/devices/domain/repositories/device_hostrity_work_repository.dart';
import '../../features/devices/domain/repositories/path_repository.dart';
import '../../features/devices/domain/usecases/device_work_hostrirty_usecase.dart';
import '../../features/devices/domain/usecases/generate_path_usecase.dart';
import '../../features/devices/domain/usecases/get_device_location_usecase.dart';
import '../../features/remote_control/presentation/bloc/remote_control_cubit.dart';
import '../app/app_user_cubit.dart';
import '../network/dio_client.dart';
@@ -102,9 +106,19 @@ Future<void> init() async {
/// 5. 状态管理 (Cubit/Bloc)
sl.registerLazySingleton(() => AppUserCubit()); // AuthCubit 依赖它,必须先注册
sl.registerLazySingleton(() => DevicesCubit(sl(), sl()));
sl.registerLazySingleton(() => GetDeviceLocationUseCase(sl()));
sl.registerLazySingleton(() => DevicesCubit(sl(), sl(), sl()));
sl.registerFactory(() => RemoteControlCubit(sl()));
/// . 路径生成
sl.registerLazySingleton<PathRepository>(() => PathRepositoryImpl());
sl.registerLazySingleton<GeneratePathUseCase>(
() => GeneratePathUseCaseImpl(sl<PathRepository>()),
);
/// 6. 认证 (Auth)
// --- 关键修改点 1: AuthCubit 必须在 GoRouter 之前注册,并传入参数 ---
sl.registerLazySingleton(

View File

@@ -5,4 +5,7 @@ abstract class DeviceHttpDatasource {
Future<int> bindDevice(String deviceId, String deviceAlias);
Future<int> unbindDevice(String deviceId);
Future<int> switchDevice(String platform, String deviceId);
Future<dynamic> getDeviceLocation(String username) async {}
}

View File

@@ -3,6 +3,8 @@ import 'package:maibu_satabot_v2/core/consts/http_api_consts.dart';
import 'package:maibu_satabot_v2/features/devices/data/datasources/device_http_datasource.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
import '../../../domain/entities/device_location_entity.dart';
class DeviceHttpDatasourceImpl implements DeviceHttpDatasource {
final Dio dio;
@@ -78,4 +80,45 @@ class DeviceHttpDatasourceImpl implements DeviceHttpDatasource {
// TODO: implement unbindDevice
throw UnimplementedError();
}
@override
Future<dynamic> getDeviceLocation(String deviceame) async {
// TODO: implement getDeviceLocation
try {
// 发起 GET 请求
final response = await dio.get(
'https://serviceri.satabot.com/iot/device/userDevice',
queryParameters: {'tenantName': deviceame},
// options: Options(
// headers: {
// 'Content-Type': 'application/json',
// 'Authorization': 'Bearer ${await _getToken()}', // 获取 Token
// },
// ),
);
// 检查响应状态码
if (response.statusCode == 200) {
final data = response.data;
if (data['code'] == 200) {
//return data['data']; // 返回设备数据
final locationData = data['data'];// 假设数据结构,测试时候如有不妥就修改结果的实例化
return DeviceLocationEntity(
deviceName: locationData['deviceName'],
latitude: locationData['latitude'],
longitude: locationData['longitude'],
);
} else {
throw Exception(data['msg'] ?? '业务异常');
}
} else {
throw Exception('网络请求失败: ${response.statusCode}');
}
} catch (e) {
throw Exception('获取设备位置失败: $e');
}
throw UnimplementedError();
}
}

View File

@@ -0,0 +1,16 @@
class DeviceAddPathPointModel {
final double latitude;
final double longitude;
DeviceAddPathPointModel({required this.latitude, required this.longitude});
Map<String, dynamic> toJson() => {
'latitude': latitude,
'longitude': longitude,
};
factory DeviceAddPathPointModel.fromJson(Map<String, dynamic> json) => DeviceAddPathPointModel(
latitude: json['latitude'],
longitude: json['longitude'],
);
}

View File

@@ -2,6 +2,7 @@ import 'package:dio/dio.dart';
import 'package:fpdart/src/either.dart';
import 'package:maibu_satabot_v2/features/devices/data/datasources/device_http_datasource.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_location_entity.dart';
import 'package:maibu_satabot_v2/features/devices/domain/errors/device_failure.dart';
import '../../domain/repositories/device_repository.dart';
@@ -69,4 +70,12 @@ class DeviceRepositoryImpl implements DeviceRepository {
return Left(DeviceFailure(cleanMessage));
}
}
// TODO: implement switchDevice 实现获取设备的列表
@override
Future<Either<DeviceFailure, DeviceLocationEntity>>getDeviceLocation(String username) async {
// TODO: implement getDeviceLocation
var result = await _deviceHttpDatasource.getDeviceLocation(username);
return Right(result);
// throw UnimplementedError();
}
}

View File

@@ -0,0 +1,30 @@
import 'package:http/http.dart' as http;
import 'dart:convert';
import '../../domain/repositories/path_repository.dart';
import '../models/device_add_path_point_model.dart';
class PathRepositoryImpl implements PathRepository {
final String baseUrl = 'https://your-backend-api.com'; // 后端接口地址
// 生成路径
@override
Future<List<DeviceAddPathPointModel>> generatePath(List<DeviceAddPathPointModel> coordinates) async {
final url = Uri.parse('$baseUrl/api/path');
final headers = {'Content-Type': 'application/json'};
final body = jsonEncode({
'coordinates': coordinates.map((p) => p.toJson()).toList(),
});
try {
final response = await http.post(url, headers: headers, body: body);
if (response.statusCode == 200) {
final List<dynamic> jsonData = jsonDecode(response.body)['path'];
return jsonData.map((item) => DeviceAddPathPointModel.fromJson(item)).toList();
} else {
throw Exception('Failed to load path: ${response.statusCode}');
}
} catch (e) {
throw Exception('Network error: $e');
}
}
}

View File

@@ -0,0 +1,11 @@
class DeviceLocationEntity {
final String deviceName;
final double latitude; // 新增字段
final double longitude; // 新增字段
DeviceLocationEntity({
required this.deviceName,
required this.latitude,
required this.longitude,
});
}

View File

@@ -11,4 +11,6 @@ abstract class DeviceHostrityWorkRepositoryRepository {
required String deviceId,
});
}

View File

@@ -1,5 +1,6 @@
import 'package:fpdart/fpdart.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_location_entity.dart';
import 'package:maibu_satabot_v2/features/devices/domain/errors/device_failure.dart';
abstract class DeviceRepository {
@@ -15,4 +16,6 @@ abstract class DeviceRepository {
String platform,
String deviceId,
);
// 获取设备位置
Future<Either<DeviceFailure, DeviceLocationEntity>> getDeviceLocation(String username);
}

View File

@@ -0,0 +1,6 @@
import '../../data/models/device_add_path_point_model.dart';
abstract class PathRepository {
// Generate path(打点生成路径规划)
Future<List<DeviceAddPathPointModel>> generatePath(List<DeviceAddPathPointModel> coordinates);
}

View File

@@ -0,0 +1,20 @@
import '../../data/models/device_add_path_point_model.dart';
import '../repositories/path_repository.dart';
abstract class GeneratePathUseCase {
Future<List<DeviceAddPathPointModel>> execute(List<DeviceAddPathPointModel> coordinates);
}
class GeneratePathUseCaseImpl implements GeneratePathUseCase {
final PathRepository _repository;
GeneratePathUseCaseImpl(this._repository);
@override
Future<List<DeviceAddPathPointModel>> execute(List<DeviceAddPathPointModel> coordinates) async {
return await _repository.generatePath(coordinates);
}
}

View File

@@ -0,0 +1,16 @@
// features/devices/domain/usecases/get_device_location_usecase.dart
import 'package:fpdart/fpdart.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.dart';
import 'package:maibu_satabot_v2/features/devices/domain/entities/device_location_entity.dart';
import 'package:maibu_satabot_v2/features/devices/domain/repositories/device_repository.dart';
import 'package:maibu_satabot_v2/features/devices/domain/errors/device_failure.dart';
class GetDeviceLocationUseCase {
final DeviceRepository repository;
GetDeviceLocationUseCase(this.repository);
//获取设备的位置
Future<Either<DeviceFailure, DeviceLocationEntity>> call(String Devicename) async {
return await repository.getDeviceLocation(Devicename);
}
}

View File

@@ -3,21 +3,22 @@ import 'package:maibu_satabot_v2/features/devices/domain/entities/device_entity.
import 'package:maibu_satabot_v2/features/devices/domain/repositories/device_repository.dart';
import 'package:maibu_satabot_v2/features/devices/domain/usecases/get_user_device_usecase.dart';
import '../../domain/usecases/get_device_location_usecase.dart';
import 'devices_state.dart';
class DevicesCubit extends Cubit<DevicesState> {
final GetUserDeviceUseCase _getUserDeviceUseCase;
final GetDeviceLocationUseCase _getDeviceLocationUseCase;
final DeviceRepository repository;
DevicesCubit(this.repository, this._getUserDeviceUseCase)
DevicesCubit(this.repository, this._getUserDeviceUseCase, this._getDeviceLocationUseCase)
: super(const DevicesState());
// 获取所有设备列表
Future<void> fetchAllDevices(String username) async {
emit(state.copyWith(isLoading: true));
try {
// 模拟网络请求获取列表
// 网络请求获取列表
var resultEither = await _getUserDeviceUseCase.call(
GetUserDeviceParams(username),
);
@@ -63,7 +64,7 @@ class DevicesCubit extends Cubit<DevicesState> {
emit(state.copyWith(devices: newList, selectedDevice: newSelected));
}
// 切换设备
Future<void> switchDevice(DeviceEntity device) async {
// 保持现有列表,只改 loading
emit(state.copyWith(isLoading: true));
@@ -75,4 +76,19 @@ class DevicesCubit extends Cubit<DevicesState> {
selectDevice(device);
}, (r) => emit(state.copyWith(isLoading: false, selectedDevice: device)));
}
// 获取设备位置
Future<void> getDeviceLocation(DeviceEntity device) async {
emit(state.copyWith(isLoading: true));
final result = await _getDeviceLocationUseCase.call(device.deviceName);
result.fold(
(failure) => emit(state.copyWith(isLoading: false, errorMessage: failure.message)),
(location) => emit(state.copyWith(
isLoading: false,
deviceLatitude: location.latitude,
deviceLongitude: location.longitude,
)),
);
}
}

View File

@@ -6,12 +6,16 @@ class DevicesState extends Equatable {
final DeviceEntity? selectedDevice; // 当前主界面选中的/操作的设备
final bool isLoading; // 是否正在加载
final String? errorMessage; // 错误信息
final double? deviceLatitude; // 新增字段
final double? deviceLongitude; // 新增字段
const DevicesState({
this.devices = const [],
this.selectedDevice,
this.isLoading = false,
this.errorMessage,
this.deviceLatitude,
this.deviceLongitude,
});
// 使用 copyWith 方便局部更新状态
@@ -20,15 +24,23 @@ class DevicesState extends Equatable {
DeviceEntity? selectedDevice,
bool? isLoading,
String? errorMessage,
double? deviceLatitude,
double? deviceLongitude,
}) {
return DevicesState(
devices: devices ?? this.devices,
selectedDevice: selectedDevice ?? this.selectedDevice,
isLoading: isLoading ?? this.isLoading,
errorMessage: errorMessage, // 错误信息通常每次更新都要重新赋值或清空
deviceLatitude: deviceLatitude ?? this.deviceLatitude,
deviceLongitude: deviceLongitude ?? this.deviceLongitude,
);
}
@override
List<Object?> get props => [devices, selectedDevice, isLoading, errorMessage];
List<Object?> get props => [devices, selectedDevice, isLoading,
errorMessage,
deviceLatitude,
deviceLongitude,
];
}