Initial project

This commit is contained in:
Cody
2026-03-06 14:56:17 +08:00
parent 977b627b15
commit bf9e099747
1180 changed files with 50973 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
import 'package:networks_sdk/networks_sdk.dart';
import 'package:storage_sdk/storage_sdk.dart';
import '../../../core/services/socket_manager.dart';
import '../../../domain/entities/user.dart';
import '../../../domain/repositories/auth_repository.dart';
/// 登录用例
///
/// 封装登录的完整业务流程:
/// 格式校验 → 调 Repository 登录 → 初始化 WebSocket → 打开本地数据库 → 返回 User
///
/// ## 为什么需要 UseCase
///
/// ViewModel 直接调 Repository 也能跑通,但登录有明确的多步业务规则:
/// - 格式校验(不发无效请求,省流量、减少服务端压力)
/// - 登录后初始化 WebSocket 连接
/// - 登录后按 user id 打开对应的本地数据库
///
/// 把这些规则封装在 UseCase 里ViewModel 只需一行调用。
///
/// ## 数据流位置
///
/// ```
/// LoginViewModel.login(email, password)
/// → ★ LoginUseCase.execute() ★ ← 你在这里
/// → 格式校验(邮箱 + 密码)
/// → AuthRepository.login()
/// → AuthRepositoryImpl.login()
/// → _client.executeRequest(LoginRequest)
/// ← LoginDataDTO
/// → _onTokenUpdate(token) ← 回调写入 Token内存 + 持久化,由 Provider 层组合)
/// ← LoginData.toEntity() → User
/// → SocketManager.connect(token) ← 登录后连接 WebSocket
/// → StorageSdkApi.openDatabase(user.id) ← 按用户 id 打开本地库
/// ← User
/// ```
class LoginUseCase {
final AuthRepository _authRepository;
final SocketManager _socketManager;
final ApiConfig _apiConfig;
final StorageSdkApi _storageApi;
StorageSdkLifecycle get _storageLifeCycle => _storageApi as StorageSdkLifecycle;
LoginUseCase({
required AuthRepository authRepository,
required SocketManager socketManager,
required ApiConfig apiConfig,
required StorageSdkApi storageApi,
}) : _authRepository = authRepository,
_socketManager = socketManager,
_apiConfig = apiConfig,
_storageApi = storageApi;
/// 执行登录
///
/// 1. 格式校验 → 不合法直接抛 [FormatException]
/// 2. 调 Repository 登录 → 拿到 Usertoken 写入由 Repository 处理)
/// 3. 用已存入 ApiConfig 的 token 连接 WebSocket
/// 4. 按 user id 打开本地数据库
///
/// 抛出:
/// - [FormatException] — 邮箱或密码格式不合法
/// - [ApiError] — 网络/服务端错误(由 Repository 透传)
Future<User> execute({
required String email,
required String password,
}) async {
// ── 1. 格式校验 ──
_validateEmail(email);
_validatePassword(password);
// ── 2. 登录 ──
final user = await _authRepository.login(
email: email,
password: password,
);
// ── 3. 连接 WebSocket ──
// token 在 Repository 的 _onTokenUpdate 回调中已写入 ApiConfig
// 此处直接读取,避免改动现有接口。
final token = _apiConfig.token;
if (token != null && token.isNotEmpty) {
await _socketManager.connect(token: token);
}
// ── 4. 打开数据库 ──
// TODO: 当服务端返回整型 uid 时,换成 user.uid目前用 hashCode 作为临时标识。
await _storageLifeCycle.openDatabase(user.hashCode);
// TODO: 后续扩展点
// - 同步联系人列表
// - 注册推送 token
return user;
}
void _validateEmail(String email) {
if (email.trim().isEmpty) {
throw const FormatException('邮箱不能为空'); // TODO: 接入国际化
}
final emailRegex = RegExp(r'^[^@\s]+@[^@\s]+\.[^@\s]+$');
if (!emailRegex.hasMatch(email.trim())) {
throw const FormatException('邮箱格式不正确'); // TODO: 接入国际化
}
}
void _validatePassword(String password) {
if (password.isEmpty) {
throw const FormatException('密码不能为空'); // TODO: 接入国际化
}
if (password.length < 6) {
throw const FormatException('密码长度不能少于 6 位'); // TODO: 接入国际化
}
}
}