refactor: 重构项目目录结构与路径引用

1.  调整工具类、平台相关代码的目录组织,将原有根目录下的工具类迁移到`data/`和`platform/`子目录
2.  统一修复全项目的文件导入路径,匹配新的目录结构
3.  新增Web端平台适配的Stub实现,包括Isolate、path_provider、platform_io等
4.  删除旧的单文件平台适配实现,替换为分平台的目录结构实现
5.  移除旧的iOS Widget入口文件,新增Widget Extension的权限配置
6.  调整部分组件的目录位置,统一widget的分类组织
7.  修复部分硬编码文本和废弃的正则表达式逻辑
This commit is contained in:
Developer
2026-05-23 05:16:31 +08:00
parent 85d856f0ed
commit a9499d7219
357 changed files with 6505 additions and 2532 deletions

View File

@@ -1,9 +1,10 @@
/// ============================================================
/// 闲言APP — 用户数据模型
/// 创建时间: 2026-04-28
/// 更新时间: 2026-05-15
/// 更新时间: 2026-05-23
/// 作用: 用户信息数据模型,对应后端 tool_user 表
/// 上次更新: v10.1.0 UserModel新增secQuestion/secQuestionText字段
/// 上次更新: v14.105.0 avatarDisplayUrl增加URL长度限制和格式校验
/// 防止超长/非法URL导致SQLite缓存数据库异常
/// ============================================================
class UserModel {
@@ -76,19 +77,46 @@ class UserModel {
String get displayName => nickname.isNotEmpty ? nickname : username;
static const int _maxAvatarUrlLength = 2048;
String get avatarDisplayUrl {
final raw = _resolveAvatarUrl();
if (raw.isEmpty) return '';
if (raw.length > _maxAvatarUrlLength) {
return raw.substring(0, _maxAvatarUrlLength);
}
return raw;
}
String _resolveAvatarUrl() {
if (avatarUrl.isNotEmpty) {
if (avatarUrl.startsWith('data:')) return '';
if (!_isValidUrl(avatarUrl)) return '';
if (avatarUrl.startsWith('http')) return avatarUrl;
return 'https://tools.wktyl.com$avatarUrl';
}
if (avatar.isNotEmpty) {
if (avatar.startsWith('data:')) return '';
if (!_isValidUrl(avatar)) return '';
if (avatar.startsWith('http')) return avatar;
if (avatar.startsWith('data:')) return avatar;
return 'https://tools.wktyl.com$avatar';
}
return '';
}
static bool _isValidUrl(String url) {
if (url.isEmpty) return false;
if (url.startsWith('http://') || url.startsWith('https://')) {
try {
final uri = Uri.parse(url);
return uri.host.isNotEmpty;
} catch (_) {
return false;
}
}
return url.startsWith('/');
}
@Deprecated('Use avatarDisplayUrl instead')
String get avatarUrlCompat => avatarDisplayUrl;