feat: 1.调整项目初始化,不用等待site/config接口,仅在hi_user_info中调用;将site_config_service拆成loadDomain和获取配置,在splash中只做loadDomain

2. 优化接口拦截的解密日志
3. 调整获取订阅到kr_home,与源版一致
4. 修复闪连抖动问题,增加套餐有效期判断
This commit is contained in:
2026-01-13 18:07:11 -08:00
parent 1d2a89a80a
commit ccf9a76de9
22 changed files with 737 additions and 1379 deletions
+2 -3
View File
@@ -25,15 +25,14 @@ class BaseResponse<T> {
final dataMap = json['data'] ?? Map<String, dynamic>();
final cipherText = dataMap['data'] ?? "";
final nonce = dataMap['time'] ?? "";
print('明文${cipherText}');
// 判断是否需要解密:根据站点配置的 enable_security 字段
if (cipherText.isNotEmpty && nonce.isNotEmpty) {
try {
if (kDebugMode) {
// print('═══════════════════════════════════════');
// print('🔐 检测到加密响应,开始解密...');
print('📥 加密数据长度: ${cipherText.length} 字符');
print('⏰ 时间戳: $nonce');
// print('📥 加密数据长度: ${cipherText.length} 字符');
// print('⏰ 时间戳: $nonce');
}
final decrypted = KRAesUtil.decryptData(cipherText, nonce, AppConfig.kr_encryptionKey);
+100 -211
View File
@@ -24,7 +24,6 @@ import 'package:loggy/loggy.dart';
import '../utils/kr_aes_util.dart';
import '../utils/kr_log_util.dart';
import 'package:flutter/foundation.dart';
// import 'package:video/app/utils/common_util.dart';
// import 'package:video/app/utils/log_util.dart';
@@ -144,70 +143,11 @@ class HttpUtil {
}
var map = <String, dynamic>{};
// 判断是否需要加密:根据站点配置的 enable_security 字段
final shouldEncrypt = KRSiteConfigService().isDeviceSecurityEnabled();
if (shouldEncrypt) {
KRLogUtil.kr_i('🔐 需要加密请求数据', tag: 'HttpUtil');
final plainText = jsonEncode(params);
map = KRAesUtil.encryptData(plainText, AppConfig.kr_encryptionKey);
} else {
map = params;
}
final plainText = jsonEncode(params);
map = KRAesUtil.encryptData(plainText, AppConfig.kr_encryptionKey);
// 初始化请求头
final headers = _initHeader('signature', 'userId', 'token');
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;
}
}
final userId =
_kr_parseUserIdFromToken(KRAppRunData().kr_token.toString());
// 调试:打印请求头
KRLogUtil.kr_i('🔍 请求头: $headers', tag: 'HttpUtil');
KRLogUtil.kr_i('🔍 请求userId: $userId', tag: 'HttpUtil');
KRLogUtil.kr_i('🔍 请求头map: $map', tag: 'HttpUtil');
Response<Map<String, dynamic>> responseTemp;
if (method == HttpMethod.GET) {
@@ -316,193 +256,93 @@ class HttpUtil {
}
}
/// 拦截器(简洁格式,无边框)
class MyInterceptor extends Interceptor {
@override
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
if (kDebugMode) {
print('>>> Request │ ${options.method}${options.uri}');
}
if (options.data != null) {
if (kDebugMode) {
print('Body: ${options.data}');
}
}
handler.next(options);
}
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
if (kDebugMode) {
print(
'<<< Response │ ${response.requestOptions.method}${response.statusCode} ${response.statusMessage}${response.requestOptions.uri}');
}
if (response.data != null) {
if (kDebugMode) {
print('Body: ${response.data}');
}
}
handler.next(response);
}
@override
void onError(DioException err, ErrorInterceptorHandler handler) {
if (kDebugMode) {
print(
'<<< Error │ ${err.requestOptions.method}${err.requestOptions.uri}');
}
if (kDebugMode) {
print('Error Type: ${err.type}');
}
if (err.message != null) {
if (kDebugMode) {
print('Error Message: ${err.message}');
}
}
if (err.response?.data != null) {
if (kDebugMode) {
print('Response Data: ${err.response?.data}');
}
}
handler.next(err);
}
}
/// 自定义简洁 HTTP 拦截器(无边框符号)
class _KRSimpleHttpInterceptor extends Interceptor {
/// 常量:手动控制是否打印拦截器日志与解密结果
static const bool KR_HTTP_PRINT = true;
final Dio _dio;
_KRSimpleHttpInterceptor(this._dio);
static String? _lastPath;
static int _lastTsMs = 0;
@override
/// 请求拦截器
///
/// 功能:在调试模式下优先打印请求的解密明文;若解密失败则打印原始数据并标识失败。
/// 参数:
/// - options: 请求选项(包含 data 与 queryParameters
/// - handler: 拦截器处理器
/// 返回:void
void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
if (kDebugMode) {
if (KR_HTTP_PRINT) {
print('>>> Request │ ${options.method}${options.uri}');
}
if (options.data != null) {
if (kDebugMode) {
print('Body: ${options.data}');
}
// 检查是否是加密数据(包含 data 和 time 字段)
if (KR_HTTP_PRINT) {
Map<String, dynamic>? m;
if (options.data is Map<String, dynamic>) {
final data = options.data as Map<String, dynamic>;
if (data.containsKey('data') && data.containsKey('time')) {
try {
if (kDebugMode) {
print('');
}
// 尝试解密并打印原始数据
final encryptedData = data['data'] as String;
final nonce = data['time'] as String;
final decrypted = KRAesUtil.decryptData(
encryptedData,
nonce,
AppConfig.kr_encryptionKey,
);
if (kDebugMode) {
print('🔓 解密后的原始请求数据:');
}
// 尝试格式化 JSON
try {
final jsonData = jsonDecode(decrypted);
final prettyJson = JsonEncoder.withIndent(' ').convert(jsonData);
if (kDebugMode) {
print(prettyJson);
}
} catch (_) {
if (kDebugMode) {
print(decrypted);
}
}
} catch (e) {
if (kDebugMode) {
print('⚠️ 请求解密失败: $e');
}
}
}
m = options.data as Map<String, dynamic>;
} else if (options.queryParameters.isNotEmpty) {
m = options.queryParameters;
}
if (m != null) {
final decrypted = _tryDecryptFromMap(m);
_printDecryptedOrFallback(
phase: 'Request', raw: m, decrypted: decrypted);
}
}
handler.next(options);
}
@override
/// 响应拦截器
///
/// 功能:在调试模式下优先打印响应的解密明文;若解密失败则打印原始数据并标识失败。
/// 参数:
/// - response: 响应对象(优先识别 Map 格式)
/// - handler: 拦截器处理器
/// 返回:void
void onResponse(Response response, ResponseInterceptorHandler handler) {
if (kDebugMode) {
if (KR_HTTP_PRINT) {
print(
'<<< Response │ ${response.requestOptions.method}${response.statusCode} ${response.statusMessage}${response.requestOptions.uri}');
}
if (response.data != null) {
if (kDebugMode) {
print('Body: ${response.data}');
}
// 检查响应是否是加密数据(包含 data 和 time 字段)
if (response.data is Map<String, dynamic>) {
final dataMap = response.data as Map<String, dynamic>;
// 检查是否包含嵌套的 data 字段(加密数据格式)
final nestedData = dataMap['data'];
if (nestedData is Map<String, dynamic> &&
nestedData.containsKey('data') &&
nestedData.containsKey('time')) {
try {
if (kDebugMode) {
print('');
}
if (kDebugMode) {
print('🔐 检测到加密响应,正在解密...');
}
// 尝试解密并打印原始响应数据
final encryptedData = nestedData['data'] as String;
final nonce = nestedData['time'] as String;
final decrypted = KRAesUtil.decryptData(
encryptedData,
nonce,
AppConfig.kr_encryptionKey,
);
if (kDebugMode) {
print('🔓 解密后的原始响应数据:');
}
// 尝试格式化 JSON
try {
// final jsonData = jsonDecode(decrypted);
// final prettyJson = JsonEncoder.withIndent(' ').convert(jsonData);
// if (kDebugMode) {
// print(prettyJson);
// }
} catch (_) {
if (kDebugMode) {
print(decrypted);
}
}
} catch (e) {
if (kDebugMode) {
print('⚠️ 响应解密失败: $e');
}
}
}
if (KR_HTTP_PRINT && response.data is Map<String, dynamic>) {
final dataMap = response.data as Map<String, dynamic>;
String? decrypted = _tryDecryptFromMap(dataMap);
if (decrypted == null && dataMap['data'] is Map<String, dynamic>) {
decrypted = _tryDecryptFromMap(dataMap['data'] as Map<String, dynamic>);
}
_printDecryptedOrFallback(
phase: 'Response', raw: dataMap, decrypted: decrypted);
}
handler.next(response);
}
@override
/// 错误拦截器
///
/// 功能:打印错误信息并保持原有的 Unknown 错误重试与失败提示去重逻辑。
/// 参数:
/// - err: Dio 错误对象
/// - handler: 拦截器处理器
/// 返回:void
void onError(DioException err, ErrorInterceptorHandler handler) {
if (kDebugMode) {
if (KR_HTTP_PRINT) {
print(
'<<< Error │ ${err.requestOptions.method}${err.requestOptions.uri}');
}
if (kDebugMode) {
if (KR_HTTP_PRINT) {
print('Error Type: ${err.type}');
}
if (err.message != null) {
if (kDebugMode) {
if (KR_HTTP_PRINT) {
print('Error Message: ${err.message}');
}
}
if (err.response?.data != null) {
if (kDebugMode) {
if (KR_HTTP_PRINT) {
print('Response Data: ${err.response?.data}');
}
}
@@ -545,4 +385,53 @@ class _KRSimpleHttpInterceptor extends Interceptor {
}
handler.next(err);
}
/// 尝试从 Map 中解密,若存在 data/time 字段则返回明文,否则返回 null
String? _tryDecryptFromMap(Map<String, dynamic> m) {
final data = m['data'];
final time = m['time'];
if (data is String && time is String) {
try {
return KRAesUtil.decryptData(data, time, AppConfig.kr_encryptionKey);
} catch (_) {
return null;
}
}
return null;
}
/// 将字符串尽量按 JSON 缩进格式化,失败时返回原文
String _prettyPrintJson(String s) {
try {
final jsonData = jsonDecode(s);
return JsonEncoder.withIndent(' ').convert(jsonData);
} catch (_) {
return s;
}
}
/// 统一输出逻辑:成功仅打印解密明文;失败打印失败标识与原文
void _printDecryptedOrFallback({
required String phase,
required Object? raw,
required String? decrypted,
}) {
if (!KR_HTTP_PRINT) return;
if (decrypted != null) {
print('🔓 $phase 明文:');
print(_prettyPrintJson(decrypted));
} else {
print('⚠️ $phase 解密失败');
print('原文:');
if (raw is Map<String, dynamic>) {
try {
print(JsonEncoder.withIndent(' ').convert(raw));
} catch (_) {
print(raw.toString());
}
} else {
print(raw?.toString() ?? 'null');
}
}
}
}