完善游客模式登录 并且新增设备管理

This commit is contained in:
2025-10-17 21:11:40 +08:00
parent 2333ad52a9
commit de9cf751f3
46 changed files with 4590 additions and 543 deletions
+15
View File
@@ -1071,6 +1071,21 @@ class AppConfig {
/// 网站ID
String kr_website_id = "";
/// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/// 用户信息固定值(临时)
/// ⚠️ 注意:新版本后端已废弃 /v1/app/user/info 接口
/// 等待新接口实现后,这些值应该从新接口动态获取
/// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/// 用户余额(单位:分)
/// 临时固定值:0,表示0.00元
static const int kr_userBalance = 0;
/// 用户邀请码
/// 临时固定值:空字符串,待新接口实现
static const String kr_userReferCode = "";
/// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/// 是否为白天模式
bool kr_is_daytime = true;
+90 -39
View File
@@ -13,6 +13,7 @@ import 'package:kaer_with_panels/app/utils/kr_device_util.dart';
import 'package:kaer_with_panels/app/utils/kr_log_util.dart';
import '../services/api_service/kr_api.user.dart';
import '../services/kr_announcement_service.dart';
import '../utils/kr_event_bus.dart';
class KRAppRunData {
@@ -23,11 +24,11 @@ class KRAppRunData {
/// 登录token
String? kr_token;
/// 用户账号
String? kr_account;
/// 用户账号(使用响应式变量以便 UI 能监听变化)
final Rx<String?> kr_account = Rx<String?>(null);
/// 用户ID
String? kr_userId;
/// 用户ID(使用响应式变量以便 UI 能监听变化)
final Rx<int?> kr_userId = Rx<int?>(null);
/// 登录类型
KRLoginType? kr_loginType;
@@ -46,17 +47,77 @@ class KRAppRunData {
return _instance;
}
/// 判断是否是设备登录(游客模式)
bool isDeviceLogin() {
// 设备登录的账号格式为 "device_设备ID"
return kr_account.value != null && kr_account.value!.startsWith('device_');
}
/// 从JWT token中解析userId
int? _kr_parseUserIdFromToken(String token) {
try {
// JWT格式: header.payload.signature
final parts = token.split('.');
if (parts.length != 3) {
KRLogUtil.kr_e('JWT token格式错误', tag: 'AppRunData');
return null;
}
// 解码payload部分(base64
String payload = parts[1];
// 手动添加必要的padding(base64要求长度是4的倍数)
switch (payload.length % 4) {
case 0:
break; // 不需要padding
case 2:
payload += '==';
break;
case 3:
payload += '=';
break;
default:
KRLogUtil.kr_e('JWT payload长度无效', tag: 'AppRunData');
return null;
}
final decodedBytes = base64.decode(payload);
final decodedString = utf8.decode(decodedBytes);
// 解析JSON
final Map<String, dynamic> payloadMap = jsonDecode(decodedString);
// 获取UserId
if (payloadMap.containsKey('UserId')) {
final userId = payloadMap['UserId'];
KRLogUtil.kr_i('从JWT解析出userId: $userId', tag: 'AppRunData');
return userId is int ? userId : int.tryParse(userId.toString());
}
return null;
} catch (e) {
KRLogUtil.kr_e('解析JWT token失败: $e', tag: 'AppRunData');
return null;
}
}
/// 保存用户信息
Future<void> kr_saveUserInfo(
String token, String account, KRLoginType loginType, String? areaCode) async {
String token,
String account,
KRLoginType loginType,
String? areaCode) async {
KRLogUtil.kr_i('开始保存用户信息', tag: 'AppRunData');
try {
// 更新内存中的数据
kr_token = token;
kr_account = account;
kr_account.value = account;
kr_loginType = loginType;
kr_areaCode = areaCode;
// 从JWT token中解析userId
kr_userId.value = _kr_parseUserIdFromToken(token);
KRLogUtil.kr_i('从JWT解析userId: ${kr_userId.value}', tag: 'AppRunData');
final Map<String, dynamic> userInfo = {
'token': token,
@@ -81,15 +142,13 @@ class KRAppRunData {
}
KRLogUtil.kr_i('用户信息保存成功,设置登录状态为true', tag: 'AppRunData');
// 只有在保存成功后才设置登录状态
kr_isLogin.value = true;
// 异步获取用户信息并建立 Socket 连接,不等待结果
_iniUserInfo().catchError((error) {
KRLogUtil.kr_e('获取用户信息失败: $error', tag: 'AppRunData');
// 即使获取用户信息失败,也保持登录状态
});
// 设备登录模式不再调用用户信息接口
// Socket 连接将在需要时建立
KRLogUtil.kr_i('用户信息已保存,跳过用户信息接口调用', tag: 'AppRunData');
} catch (e) {
KRLogUtil.kr_e('保存用户信息失败: $e', tag: 'AppRunData');
@@ -103,20 +162,23 @@ class KRAppRunData {
Future<void> kr_loginOut() async {
// 先将登录状态设置为 false,防止重连
kr_isLogin.value = false;
// 断开 Socket 连接
await _kr_disconnectSocket();
// 清理用户信息
kr_token = null;
kr_account = null;
kr_userId = null;
kr_account.value = null;
kr_userId.value = null;
kr_loginType = null;
kr_areaCode = null;
// 删除存储的用户信息
await KRSecureStorage().kr_deleteData(key: _keyUserInfo);
// 重置公告显示状态
KRAnnouncementService().kr_reset();
// 重置主页面
Get.find<KRMainController>().kr_setPage(0);
}
@@ -135,7 +197,7 @@ class KRAppRunData {
try {
final Map<String, dynamic> userInfo = jsonDecode(userInfoString);
kr_token = userInfo['token'];
kr_account = userInfo['account'];
kr_account.value = userInfo['account'];
final loginTypeValue = userInfo['loginType'];
kr_loginType = KRLoginType.values.firstWhere(
(e) => e.value == loginTypeValue,
@@ -143,18 +205,21 @@ class KRAppRunData {
);
kr_areaCode = userInfo['areaCode'] ?? "";
KRLogUtil.kr_i('解析用户信息成功: token=${kr_token != null}, account=$kr_account', tag: 'AppRunData');
// 从token中解析userId
if (kr_token != null && kr_token!.isNotEmpty) {
kr_userId.value = _kr_parseUserIdFromToken(kr_token!);
}
KRLogUtil.kr_i('解析用户信息成功: token=${kr_token != null}, account=${kr_account.value}', tag: 'AppRunData');
// 验证token有效性
if (kr_token != null && kr_token!.isNotEmpty) {
KRLogUtil.kr_i('设置登录状态为true', tag: 'AppRunData');
kr_isLogin.value = true;
// 异步获取用户信息,但不等待结果
_iniUserInfo().catchError((error) {
KRLogUtil.kr_e('获取用户信息失败: $error', tag: 'AppRunData');
// 如果获取用户信息失败,不重置登录状态,让用户重试
});
// 设备登录模式不需要调用用户信息接口
// 用户ID将从订阅信息或其他途径获取
KRLogUtil.kr_i('已登录,跳过用户信息接口调用', tag: 'AppRunData');
} else {
KRLogUtil.kr_w('Token为空,设置为未登录状态', tag: 'AppRunData');
kr_isLogin.value = false;
@@ -175,20 +240,6 @@ class KRAppRunData {
KRLogUtil.kr_i('用户信息初始化完成,登录状态: ${kr_isLogin.value}', tag: 'AppRunData');
}
/// 初始化用户信息并建立 Socket 连接
Future<void> _iniUserInfo() async {
final either0 = await KRUserApi().kr_getUserInfo();
either0.fold(
(error) {
KRLogUtil.kr_e(error.msg, tag: 'AppRunData');
},
(userInfo) async {
kr_userId = userInfo.id.toString();
_kr_connectSocket(kr_userId!);
},
);
}
/// 建立 Socket 连接
Future<void> _kr_connectSocket(String userId) async {
// 如果已存在连接,先断开