初始化

This commit is contained in:
谢宇宁
2026-09-15 15:44:13 +07:00
commit a23ba685e9
1065 changed files with 101122 additions and 0 deletions
+258
View File
@@ -0,0 +1,258 @@
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:hgdj/hj_utils/api_service/mine_service.dart';
import 'package:hgdj/hj_utils/date_time_util.dart';
import 'package:hgdj/hj_utils/light_model.dart';
import 'package:hgdj/tools_base/ad_manager.dart';
import 'package:hgdj/tools_base/debug_log.dart';
import 'package:hgdj/tools_base/toast.dart';
import '../../config/config.dart';
import '../../hj_model/user/user_info_model.dart';
import '../../hj_model/user/wallet_model.dart';
import '../../hj_model/video_model.dart';
import '../../hj_utils/store_keys.dart';
import '../../hj_utils/text_util.dart';
import '../../track_event_manager/device_service.dart';
import '../net/net_manager.dart';
/// 全局用户状态(登录信息、钱包、锁屏密码),ChangeNotifier 单例
final globalStore = GlobalStore();
class GlobalStore extends ChangeNotifier {
static final GlobalStore _instance = GlobalStore._();
GlobalStore._();
factory GlobalStore() => _instance;
// VIP 等级(对应服务端 vipLevel
static const int vipNormal = 0; // 普通用户
static const int vipBasic = 1; // 普通会员
static const int vipSuper = 2; // 超级会员
static const int vipDarkWeb = 3; // 暗网会员
static const int vipSwap = 4; // 换妻会员
final password = <int>[]; // 锁屏密码
bool isUnlocked = false; // 本次是否已通过锁屏校验
UserInfoModel? meInfo; // 个人信息
WalletModel? wallet; // 钱包信息
/// 短剧权益从无到有的那一刻。后台加卡不会推给客户端,只有重新拉到用户信息才知道,
/// 还挂着付费墙/正在放试看的短剧盯着它重问一次服务端
final dramaCardGained = ValueNotifier(0);
/// 启动时从本地读取锁屏密码
Future<void> init() async {
final nativePass = await lightKV.getString(StoreKeys.PASSWORD_LOCK);
if (nativePass == null) return;
try {
password.addAll(List<int>.from(json.decode(nativePass)));
} catch (e) {
debugLog('锁屏密码解析失败', e);
}
}
/// 设置锁屏密码
Future<bool?> setLockPassword(List<int> input) async {
final res =
await lightKV.setString(StoreKeys.PASSWORD_LOCK, json.encode(input));
if (res == true) {
showToast('设置密码成功');
//落盘写的是 input(覆盖),内存也得覆盖;addAll 会把旧密码留在前面,与落盘的对不上
password
..clear()
..addAll(input);
notifyListeners();
return true;
}
return res;
}
/// 关闭锁屏密码(需校验原密码)
Future<bool?> closeLockPassword(List<int> input) async {
if (input.join('') != password.join('')) {
showToast('密码验证失败,请重新输入~');
return false;
}
final res =
await lightKV.setString(StoreKeys.PASSWORD_LOCK, json.encode([]));
if (res == true) {
showToast('取消锁屏密码成功');
password.clear();
notifyListeners();
}
return res;
}
/// 校验锁屏密码
Future<bool?> checkLockPassword(List<int> input) async {
if (input.join('') != password.join('')) {
showToast('密码验证失败,请重新输入~');
return false;
}
isUnlocked = true;
return true;
}
/// 会员等级,没登录 / 没下发都按普通用户算
int get _level => meInfo?.vipLevel ?? vipNormal;
/// 是否为当前用户
bool isMe(int? uid) => uid != null && (meInfo?.uid ?? 0) == uid;
/// 是否充值 VIP(不包括推广)
bool get isRechargeVIP => (meInfo?.isVip ?? false) && _level > vipNormal;
/// VIP(包括推广)
bool get isVIP => meInfo?.isVip ?? false;
/// 有没有短剧卡:/mine/info 的 dramaExpire 晚于当前时间就是有
bool get hasDramaCard => DateTimeUtil.isExpireDate(meInfo?.dramaExpire);
/// 暗网会员及以上
bool get isAWVIP => _level >= vipDarkWeb;
/// 恰好是超级会员
bool get isSuperVip => _level == vipSuper;
/// 超级会员及以上
bool get isSuperUp => _level >= vipSuper;
/// 换妻会员及以上
bool get isVIPTopLevel => _level >= vipSwap;
/// ab测试:按 showType 原地剔除当前用户看不到的视频
/// showType 0-所有的人都可以看 1-奇数可看 2-偶数可看
void filterShowType(List<VideoModel> list) {
final hideType = (meInfo?.uid ?? 0) % 2 == 0 ? 1 : 2; //偶数 uid 看不到「奇数可看」的
list.removeWhere((e) => e.showType == hideType);
}
/// 会员状态文案:会员显示到期时间,非会员显示下载次数引导;[hasVipName] 是否拼接会员名称
String vipTip({bool hasVipName = true}) {
final vipName = meInfo?.vipName ?? '';
if (_level > vipNormal) {
return '${(hasVipName && vipName.isNotEmpty) ? '$vipName ' : ''} ${DateTimeUtil.utcTurnYear(meInfo?.vipExpireDate, char: '-')}';
}
return '开通会员免费看大片 剩余可下载次数${wallet?.downloadCount ?? 0}';
}
/// 刷新钱包信息
Future<WalletModel?> refreshWallet({bool refresh = true}) async {
wallet = await MineService.fetchWalletData();
if (refresh) notifyListeners();
return wallet;
}
/// 二维码登录
/// [refresh] 是否刷新关联 globalStore 状态的页面,==false 则仅发起网络请求
Future<UserInfoModel?> loginByQr(String qr,
{String paste = "", bool refresh = true}) async {
//1.存下当前 token,扫码失败要还原
final token = await netManager.getToken();
//2.设置 token 为空,否则获取的二维码也是老账号
netManager.setToken('');
//3.获取二维码账号信息,可能有其他项目的二维码,导致返回为空
final userInfo = await MineService.devLogin(
"",
qr,
DeviceInfoService.devType,
Platform.operatingSystem,
Config.innerVersion,
DeviceInfoService.buildID,
"",
paste,
);
return _onLogin(userInfo, token, refresh);
}
/// 设备登录
/// [refresh] 刷新所有 user 全局状态
Future<UserInfoModel?> loginByDevice(String deviceId,
{String paste = "", bool refresh = true}) async {
final userInfo = await MineService.devLogin(
deviceId,
"",
DeviceInfoService.devType,
Platform.operatingSystem,
Config.innerVersion,
DeviceInfoService.buildID,
DeviceInfoService.getDevToken(deviceId),
paste,
);
return _onLogin(userInfo, null, refresh);
}
/// 手机号登录
/// [refresh] 刷新所有 user 全局状态
Future<UserInfoModel?> loginByMobile(String mobile, String code,
{String paste = "", bool refresh = true}) async {
final userInfo = await MineService.mobileLogin(
mobile,
code,
DeviceInfoService.deviceId,
DeviceInfoService.devType,
Platform.operatingSystem,
Config.innerVersion,
DeviceInfoService.buildID,
paste,
);
showToast(userInfo != null ? '登录成功' : '登录失败');
if (TextUtil.isNotEmpty(userInfo?.token)) {
netManager.setToken(userInfo?.token);
}
if (userInfo != null && refresh) {
_setMe(userInfo);
await AdManager().refresh(userInfo); //换号后重算免广告策略
notifyListeners();
}
return userInfo;
}
/// 写入用户信息,顺便盯住短剧权益的这一下从无到有
void _setMe(UserInfoModel userInfo) {
final had = hasDramaCard;
meInfo = userInfo;
if (!had && hasDramaCard) dramaCardGained.value++;
}
/// 处理登录结果:写 token、按需刷新;失败时还原原 token
Future<UserInfoModel?> _onLogin(
UserInfoModel? userInfo, String? oldToken, bool refresh) async {
if (userInfo != null) {
if (TextUtil.isNotEmpty(userInfo.token)) {
await netManager.setToken(userInfo.token);
}
if (refresh) {
_setMe(userInfo);
await AdManager().refresh(userInfo); //设备登录/扫码换号后重算免广告策略
notifyListeners();
}
} else if (oldToken != null) {
//登录失败,还原 token
netManager.setToken(oldToken);
}
return userInfo;
}
/// 更新并获取用户信息;[map] 为空则仅拉取用户信息
/// [refresh] 刷新所有 user 全局状态(会触发 Consumer 包裹的 widget 重建)
Future<UserInfoModel?> updateUserInfo(
{Map<String, dynamic>? map, bool refresh = true}) async {
// 传了 map 才是更新,更新成功后再拉取最新信息
final isUpdate = (map?.keys.length ?? 0) > 0;
if (isUpdate) {
final ok = await MineService.updateUserInfo(map!);
if (!ok) return null;
}
final userInfo = await MineService.getUserInfo();
if (refresh && userInfo != null) {
_setMe(userInfo);
notifyListeners();
}
// 与原实现一致:更新分支返回新拉取的 userInfo,纯获取分支返回 meInfo
return isUpdate ? userInfo : meInfo;
}
}