#连接库初步完成
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import '../models/user_model.dart';
|
||||
|
||||
abstract class AuthHttpDataSource {
|
||||
abstract class IAuthHttpDataSource {
|
||||
Future<UserModel> login(String username, String password, int sourceType);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
|
||||
abstract class IAuthLocalDataSource {
|
||||
Future<void> saveUser(UserEntity user);
|
||||
Future<UserEntity?> getUser();
|
||||
Future<void> deleteUser();
|
||||
Future<bool> hasUser();
|
||||
}
|
||||
@@ -2,9 +2,9 @@ import 'package:dio/dio.dart';
|
||||
import 'package:maibu_satabot_v2/core/consts/http_api_consts.dart';
|
||||
|
||||
import '../../models/user_model.dart';
|
||||
import '../auth_http_datasource.dart';
|
||||
import '../i_auth_http_datasource.dart';
|
||||
|
||||
class AuthHttpDataSourceImpl implements AuthHttpDataSource {
|
||||
class AuthHttpDataSourceImpl implements IAuthHttpDataSource {
|
||||
final Dio dio;
|
||||
|
||||
AuthHttpDataSourceImpl(this.dio);
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
import '../../models/user_model.dart';
|
||||
import '../../../domain/entities/user_entity.dart';
|
||||
import '../i_auth_local_datasource.dart';
|
||||
|
||||
class AuthLocalDatasourceImpl implements IAuthLocalDataSource {
|
||||
final SharedPreferences _prefs;
|
||||
static const _userKey = 'auth_user';
|
||||
|
||||
AuthLocalDatasourceImpl(this._prefs);
|
||||
|
||||
@override
|
||||
Future<void> saveUser(UserEntity user) async {
|
||||
// 1. 将对象转为 Map -> 2. 将 Map 转为 JSON 字符串
|
||||
final model = UserModel.fromEntity(user);
|
||||
await _prefs.setString(_userKey, json.encode(model.toJson()));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UserEntity?> getUser() async {
|
||||
final jsonString = _prefs.getString(_userKey);
|
||||
if (jsonString == null) return null;
|
||||
|
||||
// 将 JSON 转回 Model,再转成 Entity 返回给业务层
|
||||
final model = UserModel.fromJson(json.decode(jsonString));
|
||||
return model.toEntity();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> deleteUser() async {
|
||||
await _prefs.remove(_userKey);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> hasUser() async {
|
||||
return _prefs.containsKey(_userKey);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import 'package:maibu_satabot_v2/core/data/base_model.dart';
|
||||
|
||||
import '../../../../core/domain/entities/user_entity.dart';
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
|
||||
class UserModel extends UserEntity implements BaseModel {
|
||||
|
||||
class UserModel extends UserEntity implements BaseModel<UserEntity> {
|
||||
UserModel({
|
||||
required super.userId,
|
||||
required super.username,
|
||||
@@ -26,6 +27,7 @@ class UserModel extends UserEntity implements BaseModel {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'userId': userId,
|
||||
@@ -38,17 +40,7 @@ class UserModel extends UserEntity implements BaseModel {
|
||||
};
|
||||
}
|
||||
|
||||
toEntity() {
|
||||
return UserEntity(
|
||||
userId: userId,
|
||||
username: username,
|
||||
nickname: nickname,
|
||||
token: token,
|
||||
avatar: avatar,
|
||||
email: email,
|
||||
phone: phone,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
factory UserModel.fromEntity(UserEntity entity) {
|
||||
return UserModel(
|
||||
@@ -61,4 +53,17 @@ class UserModel extends UserEntity implements BaseModel {
|
||||
phone: entity.phone,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
UserEntity toEntity() {
|
||||
return UserEntity(
|
||||
userId: userId,
|
||||
username: username,
|
||||
nickname: nickname,
|
||||
token: token,
|
||||
avatar: avatar,
|
||||
email: email,
|
||||
phone: phone,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:maibu_satabot_v2/core/storage/user_storage.dart';
|
||||
import 'package:maibu_satabot_v2/features/auth/data/datasources/i_auth_local_datasource.dart';
|
||||
|
||||
import '../../../../core/domain/entities/user_entity.dart';
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
import '../../domain/errors/auth_failure.dart';
|
||||
import '../../domain/repositories/auth_repository.dart';
|
||||
import '../datasources/auth_http_datasource.dart';
|
||||
import '../datasources/i_auth_http_datasource.dart';
|
||||
|
||||
class AuthRepositoryImpl implements AuthRepository {
|
||||
final AuthHttpDataSource httpRemote;
|
||||
final UserStorage localTokenStorage;
|
||||
final IAuthHttpDataSource httpRemote;
|
||||
final IAuthLocalDataSource localTokenStorage;
|
||||
|
||||
AuthRepositoryImpl({
|
||||
required this.httpRemote,
|
||||
|
||||
32
lib/features/auth/domain/entities/user_entity.dart
Normal file
32
lib/features/auth/domain/entities/user_entity.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class UserEntity extends Equatable {
|
||||
final String userId;
|
||||
final String username;
|
||||
final String nickname;
|
||||
final String token;
|
||||
final String? avatar;
|
||||
final String? email;
|
||||
final String? phone;
|
||||
|
||||
UserEntity({
|
||||
required this.userId,
|
||||
required this.username,
|
||||
required this.nickname,
|
||||
required this.token,
|
||||
this.avatar,
|
||||
this.email,
|
||||
this.phone,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
userId,
|
||||
username,
|
||||
nickname,
|
||||
avatar,
|
||||
token,
|
||||
email,
|
||||
phone,
|
||||
];
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
|
||||
import '../../../../core/domain/entities/user_entity.dart';
|
||||
import '../entities/user_entity.dart';
|
||||
import '../errors/auth_failure.dart';
|
||||
|
||||
abstract class AuthRepository {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
|
||||
import '../../../../core/domain/entities/user_entity.dart';
|
||||
import '../../../../core/domain/usecases/base_usecase.dart';
|
||||
import '../entities/user_entity.dart';
|
||||
import '../errors/auth_failure.dart';
|
||||
import '../repositories/auth_repository.dart';
|
||||
|
||||
|
||||
30
lib/features/auth/presentation/bloc/app_user_cubit.dart
Normal file
30
lib/features/auth/presentation/bloc/app_user_cubit.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
import '../../data/datasources/i_auth_local_datasource.dart';
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
import 'app_user_state.dart';
|
||||
|
||||
class AppUserCubit extends Cubit<AppUserState> {
|
||||
// 注入你的 UserStorage (之前在 core/storage 里的那个)
|
||||
final IAuthLocalDataSource _storage;
|
||||
|
||||
AppUserCubit(this._storage) : super(const AppUserState()) {
|
||||
// 初始化时尝试从本地加载
|
||||
_loadFromStorage();
|
||||
}
|
||||
|
||||
Future<void> _loadFromStorage() async {
|
||||
final savedUser = await _storage.getUser();
|
||||
if (savedUser != null) emit(AppUserState(user: savedUser));
|
||||
}
|
||||
|
||||
void setAuth(UserEntity user) {
|
||||
_storage.saveUser(user); // 持久化
|
||||
emit(AppUserState(user: user));
|
||||
}
|
||||
|
||||
void clearAuth() {
|
||||
_storage.deleteUser(); // 清除本地数据
|
||||
emit(const AppUserState());
|
||||
}
|
||||
}
|
||||
21
lib/features/auth/presentation/bloc/app_user_state.dart
Normal file
21
lib/features/auth/presentation/bloc/app_user_state.dart
Normal file
@@ -0,0 +1,21 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
|
||||
|
||||
class AppUserState extends Equatable {
|
||||
final UserEntity? user;
|
||||
|
||||
const AppUserState({this.user});
|
||||
|
||||
bool get isLoggedIn => user != null;
|
||||
|
||||
AppUserState copyWith({UserEntity? user, bool clearUser = false}) {
|
||||
return AppUserState(
|
||||
user: clearUser ? null : (user ?? this.user),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
List<Object?> get props => [user];
|
||||
}
|
||||
@@ -1,77 +1,77 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:maibu_satabot_v2/core/consts/tcp_consts.dart';
|
||||
import 'package:maibu_satabot_v2/core/domain/entities/user_entity.dart';
|
||||
import 'package:maibu_satabot_v2/core/network/net_message_dispatcher.dart';
|
||||
|
||||
import '../../../../core/app/app_user_cubit.dart';
|
||||
import '../../../../core/network/tcp/tcp_client.dart';
|
||||
import '../../../../core/storage/user_storage.dart';
|
||||
import 'package:maibu_satabot_v2/core/network/enums/control_mode.dart'; // 引入
|
||||
import 'package:maibu_satabot_v2/core/network/tcp/tcp_connection.dart';
|
||||
import '../../../../core/di/injection.dart';
|
||||
import '../../../../core/protocol/protocol_constants.dart';
|
||||
import '../../../connectivity/presentation/bloc/connectivity_cubit.dart';
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
import 'app_user_cubit.dart';
|
||||
import '../../data/datasources/i_auth_local_datasource.dart';
|
||||
import 'auth_state.dart';
|
||||
|
||||
/// emit做的事情:
|
||||
/// 1.修改状态标志:通过改变类的类型(从 Initial 变成 Authenticated)
|
||||
/// 2.携带数据:把 user 对象塞进了状态里,让外部能拿到
|
||||
/// 3.发送通知:自动触发监听(如 GoRouter 的刷新)。
|
||||
class AuthCubit extends Cubit<AuthState> {
|
||||
final UserStorage storage;
|
||||
final TcpClient tcp;
|
||||
final IAuthLocalDataSource storage;
|
||||
final AppUserCubit appCubit;
|
||||
final NetMessageDispatcher dispatcher;
|
||||
StreamSubscription? _kickOutSub; // 新增:用于管理监听生命周期
|
||||
final ConnectivityCubit connectivityCubit;
|
||||
StreamSubscription? _eventSub;
|
||||
|
||||
AuthCubit(this.storage, this.tcp, this.appCubit, this.dispatcher)
|
||||
: super(AuthInitial()) {
|
||||
// Cubit 一启动就开始监听 TCP 的“自动逻辑”
|
||||
AuthCubit({
|
||||
required this.storage,
|
||||
required this.appCubit,
|
||||
required this.connectivityCubit,
|
||||
}) : super(AuthInitial()) {
|
||||
_listenToAuthResponse();
|
||||
}
|
||||
|
||||
/// App 启动时检查本地缓存
|
||||
Future<void> appStarted() async {
|
||||
final user = await storage.getUser();
|
||||
if (user != null) {
|
||||
// 1. 建立 TCP 连接 (远程控制)
|
||||
tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
|
||||
// 2. 同步全局 App 状态
|
||||
_connectTcp();
|
||||
appCubit.setAuth(user);
|
||||
// 3. 进入已登录状态
|
||||
emit(AuthAuthenticated(user));
|
||||
} else {
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
}
|
||||
|
||||
/// 当 HTTP 登录/注册成功后调用
|
||||
Future<void> loginSuccess(UserEntity user) async {
|
||||
await storage.saveUser(user);
|
||||
tcp.connect(host: TCPConsts.TCP_IP, port: TCPConsts.TCP_PORT);
|
||||
_connectTcp();
|
||||
appCubit.setAuth(user);
|
||||
emit(AuthAuthenticated(user));
|
||||
}
|
||||
|
||||
/// 退出登录 (主动或被动)
|
||||
void _connectTcp() {
|
||||
// 动态注入 TCP 连接
|
||||
final tcp = sl<TcpConnection>();
|
||||
|
||||
connectivityCubit.setConnection(tcp, ControlMode.TCP);
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
await storage.deleteUser();
|
||||
tcp.disconnect();
|
||||
// 现在 setConnection 接受 null 了,不会再爆红
|
||||
connectivityCubit.setConnection(null, ControlMode.NONE);
|
||||
appCubit.clearAuth();
|
||||
emit(AuthUnauthenticated());
|
||||
}
|
||||
|
||||
/// TCP 指令监听
|
||||
void _listenToAuthResponse() {
|
||||
_kickOutSub?.cancel(); // 防止重复监听
|
||||
_eventSub?.cancel();
|
||||
|
||||
// 假设 0x12 是踢下线或多设备登录提醒
|
||||
_kickOutSub = dispatcher.onJsonMessage(0x12).listen((json) {
|
||||
// 如果后端发来指令确认需要退出
|
||||
logout();
|
||||
// 监听全局解析后的 Packet 流
|
||||
_eventSub = connectivityCubit.activePacketStream
|
||||
.where((packet) => packet.cmdType == ProtocolConstants.cmdGetAuth)
|
||||
.listen((packet) {
|
||||
// 这里处理 0x04 授权逻辑
|
||||
print("Auth Response Received: ${packet.payload}");
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_kickOutSub?.cancel(); // 销毁 Cubit 时关闭监听
|
||||
_eventSub?.cancel();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:maibu_satabot_v2/core/domain/entities/user_entity.dart';
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
|
||||
abstract class AuthState {}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import '../../../../core/domain/entities/user_entity.dart';
|
||||
import '../../domain/entities/user_entity.dart';
|
||||
|
||||
abstract class LoginState {}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user