refactor: 拆分主页与公共组件,扫码登录改走 WinHTTP

- 抽出 lib/core 的日期/JSON 工具,收敛各页重复解析
- 主页与音乐风险页拆成组件,风险功能共用一套运行框架
- 修复设置页作为主页页签保存时弹掉根路由导致的黑屏
- 登录对齐 Astral aimedb 协议:84 位二维码校验、签名与请求头修正,
  补 GlobalSign 中间证书;Windows 改用 WinHTTP 绕过服务端对
  BoringSSL 的 TLS 指纹拦截(证书校验保持开启)
- 补开发预览数据与测试
This commit is contained in:
Bad0RANG3 committed 2026-10-25 15:51:10 +08:00
1 parent 0cb5320290
commit e9357f5969
55 files changed
+4387 -3392

No files matched your search

+5 -7
View File
@@ -12,7 +12,7 @@ class AppStrings {
// Login
static const appTitle = 'Ichino';
static const qrCodeToken = 'QR Code 令牌';
static const qrHint = '粘贴二维码解析内容... / 粘贴连接信息';
static const qrHint = '粘贴二维码解析内容或二维码链接... / 粘贴连接信息';
static const uploadQR = '上传二维码';
static const login = '登录';
static const loggingIn = '登录中...';
@@ -21,7 +21,7 @@ class AppStrings {
static const qrScanError = '解析失败';
static const forcePreviewApi = '强制使用 Preview API';
static const loginFailed = '登录失败';
static const qrTokenUnrecognized = '没认出 QR 令牌(内容里应当含 SGWCMAID),也可以粘贴「连接信息」。';
static const qrTokenUnrecognized = '没认出 QR 令牌';
static const requestFailed = '请求失败';
static const settingsTooltip = '设置';
@@ -164,6 +164,7 @@ class AppStrings {
static const authServerSettings = 'Auth Server';
static const machineSettings = '机器信息';
static const save = '保存';
static const configSaved = '配置已保存';
static const labelTitleServerUrl = 'Title Server URL';
static const hintTitleServerUrl = '';
static const labelAesKey = 'AES Key';
@@ -185,11 +186,8 @@ class AppStrings {
static const labelPlaceName = 'Place 名称';
static const hintPlaceName = 'Place name';
static const labelKeychipId = 'Keychip ID';
static const hintKeychipId = 'ID';
static const labelAimeUrl = 'Aime URL';
static const hintAimeUrl = '';
static const labelAimeSalt = 'Aime Salt';
static const hintAimeSalt = 'API Key 派生用 Salt';
static const helpKeychipId =
'机台完整 Keychip(如 A63E-01C30471007);只填 11 位短值时按 Place ID 补全';
static String fieldRequired(String label) => '$label 不能为空';
// Config Export/Import
+11 -13
View File
@@ -14,9 +14,6 @@ class TitleServerConfig {
final String regionName;
final String placeName;
final String keychipId;
final String aimeUrl;
final String aimeSalt;
final String openGameID;
const TitleServerConfig({
required this.titleServerUrl,
@@ -30,14 +27,21 @@ class TitleServerConfig {
this.regionName = '',
this.placeName = '',
this.keychipId = '',
this.aimeUrl = '',
this.aimeSalt = '',
String? openGameID,
}) : openGameID = openGameID ?? 'MAID';
});
List<int> get aesKeyBytes => utf8.encode(aesKey);
List<int> get aesIvBytes => utf8.encode(aesIv);
/// 扫码登录签的是**完整 Keychip**(`A63E-01C30471007`),游戏服务器那边才用
/// 11 位短值。这里按 Astral 的约定补出完整形式:前 4 位 + 连字符 + 余下 + placeId,
/// 所以只填了短值也能还原;填不满 11 位就当没配。
String get fullKeychipId {
final raw = keychipId.replaceAll(RegExp(r'[\s\-]'), '').toUpperCase();
if (raw.length < 11) return '';
final short = '${raw.substring(0, 4)}-${raw.substring(4)}';
return raw.length == 11 ? '$short$placeId' : short;
}
Map<String, dynamic> toJson() => {
'titleServerUrl': titleServerUrl,
'aesKey': aesKey,
@@ -50,9 +54,6 @@ class TitleServerConfig {
'regionName': regionName,
'placeName': placeName,
'keychipId': keychipId,
'aimeUrl': aimeUrl,
'aimeSalt': aimeSalt,
'openGameID': openGameID,
};
factory TitleServerConfig.fromJson(Map<String, dynamic> json) {
@@ -68,9 +69,6 @@ class TitleServerConfig {
regionName: json['regionName'] as String? ?? '',
placeName: json['placeName'] as String? ?? '',
keychipId: json['keychipId'] as String? ?? '',
aimeUrl: json['aimeUrl'] as String? ?? '',
aimeSalt: json['aimeSalt'] as String? ?? '',
openGameID: json['openGameID'] as String? ?? 'MAID',
);
}
}