chore: 完成v1.2版本迭代优化与兼容修复
1. 基础适配升级:将iOS最低版本提升至15.5,更新Podfile与Xcode配置 2. 导航栈优化:使用push替代go保留路由栈,重构路由重定向逻辑 3. 多场景崩溃防护: - 新增目录获取降级逻辑,适配异常环境 - 增加上下文挂载检查,防止PostFrame回调崩溃 - 完善Hive初始化与Box访问的异常处理,实现空操作降级 4. 体验优化: - 修复摇一摇权限开关逻辑,更新个人中心文案 - 新增请求超时保护与异常提示 - 优化分享/保存相册的模拟器兼容处理 - 重构引导页与初始化流程,修复数据丢失问题 5. 代码清理:移除重复路由注册,优化Widget预览页面生命周期 6. 细节优化:更新微信联系方式展示样式,完善日签与备份服务逻辑
This commit is contained in:
@@ -1,298 +0,0 @@
|
||||
/// ============================================================
|
||||
/// 闲言APP — 字体下载URL验证脚本
|
||||
/// 创建时间: 2026-05-22
|
||||
/// 更新时间: 2026-05-22
|
||||
/// 作用: 验证所有在线字体URL的可访问性和文件格式有效性
|
||||
/// 上次更新: 初始创建,验证8个字体+备用URL的下载和文件头校验
|
||||
/// 运行方式: dart run Scripts/verify_fonts.dart
|
||||
/// ============================================================
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
/// 字体数据定义(与 font_models.dart 保持同步)
|
||||
const onlineFontData = [
|
||||
(
|
||||
'霞鹜文楷',
|
||||
'LXGWWenKai',
|
||||
'https://raw.githubusercontent.com/lxgw/LxgwWenKai/main/fonts/LXGWWenKai-Regular.ttf',
|
||||
'🖋️',
|
||||
),
|
||||
(
|
||||
'阿里巴巴普惠体',
|
||||
'AlibabaPuHuiTi',
|
||||
'https://puhuiti.oss-cn-hangzhou.aliyuncs.com/AlibabaPuHuiTi-3/AlibabaPuHuiTi-3-55-Regular/AlibabaPuHuiTi-3-55-Regular.ttf',
|
||||
'💼',
|
||||
),
|
||||
(
|
||||
'站酷快乐体',
|
||||
'ZCOOLKuaiLe',
|
||||
'https://raw.githubusercontent.com/googlefonts/zcool-kuaile/main/fonts/ttf/ZCOOLKuaiLe-Regular.ttf',
|
||||
'🎉',
|
||||
),
|
||||
(
|
||||
'站酷小薇',
|
||||
'ZCOOLXiaoWei',
|
||||
'https://raw.githubusercontent.com/googlefonts/zcool-xiaowei/main/fonts/ZCOOLXiaoWei-Regular.ttf',
|
||||
'🌸',
|
||||
),
|
||||
(
|
||||
'思源黑体',
|
||||
'NotoSansSC',
|
||||
'https://raw.githubusercontent.com/notofonts/noto-cjk/main/Sans/OTF/SimplifiedChinese/NotoSansCJKsc-Regular.otf',
|
||||
'📐',
|
||||
),
|
||||
(
|
||||
'思源宋体',
|
||||
'NotoSerifSC',
|
||||
'https://raw.githubusercontent.com/notofonts/noto-cjk/main/Serif/OTF/SimplifiedChinese/NotoSerifCJKsc-Regular.otf',
|
||||
'📜',
|
||||
),
|
||||
(
|
||||
'更纱黑体',
|
||||
'SarasaGothicSC',
|
||||
'https://raw.githubusercontent.com/be5invis/Sarasa-Gothic/main/fonts/SarasaGothicSC-Regular.ttf',
|
||||
'🎯',
|
||||
),
|
||||
(
|
||||
'文泉驿微米黑',
|
||||
'WenQuanYiMicroHei',
|
||||
'https://raw.githubusercontent.com/niclas/wqy-microhei-font/master/wqy-microhei.ttc',
|
||||
'✒️',
|
||||
),
|
||||
];
|
||||
|
||||
const fontFallbackUrls = <String, List<String>>{
|
||||
'LXGWWenKai': [
|
||||
'https://cdn.jsdelivr.net/gh/lxgw/LxgwWenKai@v1.501/fonts/LXGWWenKai-Regular.ttf',
|
||||
'https://github.com/lxgw/LxgwWenKai/releases/download/v1.501/LXGWWenKai-Regular.ttf',
|
||||
],
|
||||
'AlibabaPuHuiTi': [
|
||||
'https://fonts.alicdn.com/font/AlibabaPuHuiTi-3/AlibabaPuHuiTi-3-55-Regular.ttf',
|
||||
],
|
||||
'ZCOOLKuaiLe': [
|
||||
'https://cdn.jsdelivr.net/gh/googlefonts/zcool-kuaile@main/fonts/ttf/ZCOOLKuaiLe-Regular.ttf',
|
||||
],
|
||||
'ZCOOLXiaoWei': [
|
||||
'https://cdn.jsdelivr.net/gh/googlefonts/zcool-xiaowei@main/fonts/ZCOOLXiaoWei-Regular.ttf',
|
||||
],
|
||||
'NotoSansSC': [
|
||||
'https://cdn.jsdelivr.net/gh/notofonts/noto-cjk@main/Sans/OTF/SimplifiedChinese/NotoSansCJKsc-Regular.otf',
|
||||
],
|
||||
'NotoSerifSC': [
|
||||
'https://cdn.jsdelivr.net/gh/notofonts/noto-cjk@main/Serif/OTF/SimplifiedChinese/NotoSerifCJKsc-Regular.otf',
|
||||
],
|
||||
'SarasaGothicSC': [
|
||||
'https://github.com/be5invis/Sarasa-Gothic/releases/download/v1.0.19/SarasaGothicSC-Regular.ttf',
|
||||
],
|
||||
'WenQuanYiMicroHei': [
|
||||
'https://cdn.jsdelivr.net/gh/niclas/wqy-microhei-font@master/wqy-microhei.ttc',
|
||||
],
|
||||
};
|
||||
|
||||
/// 校验字体文件头是否为有效 TTF/OTF/TTC
|
||||
bool isValidFontFile(Uint8List bytes) {
|
||||
if (bytes.length < 4) return false;
|
||||
final h = bytes;
|
||||
final isTtf = (h[0] == 0x00 && h[1] == 0x01 && h[2] == 0x00 && h[3] == 0x00) ||
|
||||
(h[0] == 0x74 && h[1] == 0x72 && h[2] == 0x75 && h[3] == 0x65);
|
||||
final isOtf = h[0] == 0x4F && h[1] == 0x54 && h[2] == 0x54 && h[3] == 0x4F;
|
||||
final isTtc = h[0] == 0x74 && h[1] == 0x74 && h[2] == 0x63 && h[3] == 0x66;
|
||||
return isTtf || isOtf || isTtc;
|
||||
}
|
||||
|
||||
/// 检测字体格式类型
|
||||
String detectFontType(Uint8List bytes) {
|
||||
if (bytes.length < 4) return '未知(数据不足)';
|
||||
final h = bytes;
|
||||
if (h[0] == 0x00 && h[1] == 0x01 && h[2] == 0x00 && h[3] == 0x00) return 'TTF';
|
||||
if (h[0] == 0x74 && h[1] == 0x72 && h[2] == 0x75 && h[3] == 0x65) return 'TTF(true)';
|
||||
if (h[0] == 0x4F && h[1] == 0x54 && h[2] == 0x54 && h[3] == 0x4F) return 'OTF';
|
||||
if (h[0] == 0x74 && h[1] == 0x74 && h[2] == 0x63 && h[3] == 0x66) return 'TTC';
|
||||
return '未知(0x${h[0].toRadixString(16)}${h[1].toRadixString(16)}${h[2].toRadixString(16)}${h[3].toRadixString(16)})';
|
||||
}
|
||||
|
||||
/// 下载字体并验证
|
||||
Future<_FontVerifyResult> verifyFontUrl(
|
||||
String name,
|
||||
String fontFamily,
|
||||
String url,
|
||||
HttpClient client,
|
||||
) async {
|
||||
final result = _FontVerifyResult(name: name, fontFamily: fontFamily, url: url);
|
||||
|
||||
try {
|
||||
final request = await client.getUrl(Uri.parse(url));
|
||||
final response = await request.close();
|
||||
|
||||
result.statusCode = response.statusCode;
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
result.success = false;
|
||||
result.error = 'HTTP ${response.statusCode}';
|
||||
return result;
|
||||
}
|
||||
|
||||
final bytes = await consolidateBytes(response);
|
||||
result.fileSize = bytes.length;
|
||||
result.fontType = detectFontType(bytes);
|
||||
result.validFormat = isValidFontFile(bytes);
|
||||
result.success = result.validFormat;
|
||||
|
||||
if (!result.validFormat) {
|
||||
result.error = '文件头非 TTF/OTF/TTC 格式,检测为: ${result.fontType}';
|
||||
}
|
||||
} catch (e) {
|
||||
result.success = false;
|
||||
result.error = e.toString();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 合并 HttpClientResponse 的字节流
|
||||
Future<Uint8List> consolidateBytes(HttpClientResponse response) async {
|
||||
final builder = BytesBuilder();
|
||||
await for (final chunk in response) {
|
||||
builder.add(chunk);
|
||||
}
|
||||
return builder.toBytes();
|
||||
}
|
||||
|
||||
/// 验证结果
|
||||
class _FontVerifyResult {
|
||||
_FontVerifyResult({
|
||||
required this.name,
|
||||
required this.fontFamily,
|
||||
required this.url,
|
||||
});
|
||||
|
||||
final String name;
|
||||
final String fontFamily;
|
||||
final String url;
|
||||
bool success = false;
|
||||
int? statusCode;
|
||||
int? fileSize;
|
||||
String? fontType;
|
||||
bool validFormat = false;
|
||||
String? error;
|
||||
|
||||
String get fileSizeStr {
|
||||
if (fileSize == null) return 'N/A';
|
||||
if (fileSize! < 1024) return '$fileSize B';
|
||||
if (fileSize! < 1024 * 1024) return '${(fileSize! / 1024).toStringAsFixed(1)} KB';
|
||||
return '${(fileSize! / (1024 * 1024)).toStringAsFixed(1)} MB';
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
print('═══════════════════════════════════════════════════════════');
|
||||
print(' 闲言APP — 字体下载URL验证工具');
|
||||
print(' 运行时间: ${DateTime.now().toIso8601String()}');
|
||||
print('═══════════════════════════════════════════════════════════');
|
||||
print('');
|
||||
|
||||
final client = HttpClient();
|
||||
client.connectionTimeout = const Duration(seconds: 30);
|
||||
client.userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36';
|
||||
|
||||
final results = <_FontVerifyResult>[];
|
||||
|
||||
print('━━━ 阶段1: 主URL验证 ━━━');
|
||||
print('');
|
||||
|
||||
for (final font in onlineFontData) {
|
||||
final name = font.$1;
|
||||
final fontFamily = font.$2;
|
||||
final url = font.$3;
|
||||
|
||||
stdout.write(' $name ($fontFamily)... ');
|
||||
final result = await verifyFontUrl(name, fontFamily, url, client);
|
||||
results.add(result);
|
||||
|
||||
if (result.success) {
|
||||
print('✅ 通过 [${result.fontType}] ${result.fileSizeStr}');
|
||||
} else {
|
||||
print('❌ 失败 [${result.error}]');
|
||||
}
|
||||
}
|
||||
|
||||
print('');
|
||||
print('━━━ 阶段2: 备用URL验证 ━━━');
|
||||
print('');
|
||||
|
||||
for (final entry in fontFallbackUrls.entries) {
|
||||
final fontFamily = entry.key;
|
||||
final fallbacks = entry.value;
|
||||
final fontName = onlineFontData
|
||||
.where((f) => f.$2 == fontFamily)
|
||||
.map((f) => f.$1)
|
||||
.firstOrNull ??
|
||||
fontFamily;
|
||||
|
||||
for (int i = 0; i < fallbacks.length; i++) {
|
||||
final url = fallbacks[i];
|
||||
stdout.write(' $fontName 备用#${i + 1}... ');
|
||||
final result = await verifyFontUrl(fontName, fontFamily, url, client);
|
||||
results.add(result);
|
||||
|
||||
if (result.success) {
|
||||
print('✅ 通过 [${result.fontType}] ${result.fileSizeStr}');
|
||||
} else {
|
||||
print('❌ 失败 [${result.error}]');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client.close();
|
||||
|
||||
print('');
|
||||
print('═══════════════════════════════════════════════════════════');
|
||||
print(' 验证结果汇总');
|
||||
print('═══════════════════════════════════════════════════════════');
|
||||
print('');
|
||||
|
||||
final primaryResults = results.sublist(0, onlineFontData.length);
|
||||
final fallbackResults = results.sublist(onlineFontData.length);
|
||||
|
||||
print(' 主URL结果:');
|
||||
for (final r in primaryResults) {
|
||||
final icon = r.success ? '✅' : '❌';
|
||||
print(' $icon ${r.name} — ${r.success ? '${r.fontType} ${r.fileSizeStr}' : r.error}');
|
||||
}
|
||||
|
||||
print('');
|
||||
print(' 备用URL结果:');
|
||||
for (final r in fallbackResults) {
|
||||
final icon = r.success ? '✅' : '❌';
|
||||
print(' $icon ${r.name} — ${r.success ? '${r.fontType} ${r.fileSizeStr}' : r.error}');
|
||||
}
|
||||
|
||||
final primaryPass = primaryResults.where((r) => r.success).length;
|
||||
final primaryTotal = primaryResults.length;
|
||||
final allPass = results.where((r) => r.success).length;
|
||||
final allTotal = results.length;
|
||||
|
||||
print('');
|
||||
print(' 主URL: $primaryPass/$primaryTotal 通过');
|
||||
print(' 全部: $allPass/$allTotal 通过');
|
||||
print('');
|
||||
|
||||
final failedPrimary = primaryResults.where((r) => !r.success).toList();
|
||||
if (failedPrimary.isNotEmpty) {
|
||||
print(' ⚠️ 以下字体主URL不可用,需要更新:');
|
||||
for (final r in failedPrimary) {
|
||||
final hasFallback = fallbackResults.any(
|
||||
(f) => f.fontFamily == r.fontFamily && f.success,
|
||||
);
|
||||
print(' - ${r.name}: ${r.error} ${hasFallback ? '(有可用备用URL)' : '(⚠️ 无可用备用URL!)'}');
|
||||
}
|
||||
} else {
|
||||
print(' 🎉 所有字体主URL验证通过!');
|
||||
}
|
||||
|
||||
print('');
|
||||
print('═══════════════════════════════════════════════════════════');
|
||||
|
||||
exit(failedPrimary.isNotEmpty ? 1 : 0);
|
||||
}
|
||||
Reference in New Issue
Block a user