此版本包含: 1. 新增位置消息发送与展示功能 2. 完善多语言本地化文案 3. 新增安卓端管理空间Activity与图标背景 4. 优化摇一摇开关逻辑与深度链接配置 5. 新增信息流平台过滤与A/B测试后台功能 6. 更新签名配置与构建脚本 7. 修复若干已知问题与代码优化
15 KiB
深度链接解析逻辑配置驱动重构 实现计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 将 app_router.dart 中硬编码的深度链接 switch 语句重构为配置驱动,利用现有 route_registry 统一管理路由映射,消除重复定义。
Architecture: 在 RouteDef 中新增深度链接元数据字段(deepLinkAliases),创建 DeepLinkResolver 类统一解析逻辑,从 route_registry 自动构建映射表,替代原有的5个硬编码 switch 方法。
Tech Stack: Dart, go_router, 现有 route_def / route_registry 体系
文件变更清单
| 操作 | 文件路径 | 职责 |
|---|---|---|
| 修改 | lib/core/router/route_def.dart |
RouteDef 新增 deepLinkAliases 字段 |
| 新增 | lib/core/router/deep_link_resolver.dart |
配置驱动的深度链接解析器 |
| 修改 | lib/core/router/app_router.dart |
删除硬编码 switch,改用 DeepLinkResolver |
| 修改 | lib/core/router/route_registry.dart |
为各路由添加 deepLinkAliases |
| 修改 | CHANGELOG.md |
记录重构变更 |
Task 1: 扩展 RouteDef 增加深度链接元数据字段
Files:
-
Modify:
lib/core/router/route_def.dart -
Step 1: 在 RouteDef 类中新增 deepLinkAliases 字段
在 RouteDef 构造函数和类中新增 deepLinkAliases 字段,用于定义该路由支持的深度链接别名列表:
class RouteDef {
const RouteDef({
required this.path,
required this.name,
required this.module,
this.page,
this.builder,
this.ohosBuilder,
this.transition = RouteTransition.iosSlide,
this.middlewares = const [],
this.redirectTo,
this.children = const [],
this.deepLinkAliases = const [], // 新增
});
// ... 现有字段 ...
/// 深度链接别名列表
/// 格式: ['xianyan://host', 'https://s2ss.com/segment', '/tool/subpath']
/// 解析器会自动从这些别名构建映射表
final List<String> deepLinkAliases;
// ... 现有 getter ...
}
Task 2: 创建 DeepLinkResolver 配置驱动解析器
Files:
-
Create:
lib/core/router/deep_link_resolver.dart -
Step 1: 创建 DeepLinkResolver 类
创建 lib/core/router/deep_link_resolver.dart,实现从 route_registry 自动构建映射表并解析深度链接:
// ============================================================
// 闲言APP — 深度链接配置驱动解析器
// 创建时间: 2026-06-09
// 作用: 从 route_registry 自动构建深度链接映射表,替代硬编码 switch
// ============================================================
import 'package:xianyan/core/router/route_def.dart';
import 'package:xianyan/core/router/route_registry.dart';
import 'package:xianyan/core/router/app_routes.dart';
import 'package:xianyan/core/utils/logger.dart' show Log, LogCategory;
/// 深度链接映射条目
class _DeepLinkEntry {
const _DeepLinkEntry({
required this.alias,
required this.targetRoute,
this.hasSubPath = false,
});
/// 别名模式(如 'xianyan://home', 'https://s2ss.com/fortune', '/tool/hanzi')
final String alias;
/// 目标内部路由路径
final String targetRoute;
/// 是否支持子路径(如 xianyan://article/xxx 中的 xxx)
final bool hasSubPath;
}
/// 配置驱动的深度链接解析器
/// 从 route_registry 中的 deepLinkAliases 自动构建映射表
class DeepLinkResolver {
DeepLinkResolver._();
static List<_DeepLinkEntry>? _entries;
/// 获取所有映射条目(懒加载)
static List<_DeepLinkEntry> get entries {
if (_entries != null) return _entries!;
_entries = _buildEntries();
return _entries!;
}
/// 从 route_registry 构建映射表
static List<_DeepLinkEntry> _buildEntries() {
final result = <_DeepLinkEntry>[];
for (final route in routeRegistry) {
for (final alias in route.deepLinkAliases) {
// 检测是否支持子路径(别名以 /* 结尾)
final hasSubPath = alias.endsWith('/*');
final cleanAlias = hasSubPath ? alias.substring(0, alias.length - 2) : alias;
result.add(_DeepLinkEntry(
alias: cleanAlias,
targetRoute: route.path,
hasSubPath: hasSubPath,
));
}
}
Log.i('🔗 [DeepLink] 映射表构建完成: ${result.length} 条规则', null, null, LogCategory.router);
return result;
}
/// 清除缓存(测试用或路由热更新时调用)
static void invalidateCache() {
_entries = null;
}
/// 解析 xianyan:// scheme 链接
/// 格式: xianyan://<host>[/<path>]
static String? resolveCustomScheme(Uri uri) {
final host = uri.host;
final path = uri.path;
final prefix = 'xianyan://$host';
// 1. 精确匹配: xianyan://host
for (final entry in entries) {
if (!entry.alias.startsWith('xianyan://')) continue;
if (entry.hasSubPath) {
// 子路径匹配: xianyan://article/* → 匹配 xianyan://article/xxx
if (entry.alias == prefix) {
return path.isNotEmpty ? path : entry.targetRoute;
}
} else {
// 精确匹配: xianyan://home
if (entry.alias == prefix && path.isEmpty) {
return entry.targetRoute;
}
// 带路径匹配: xianyan://tool/hanzi
if (path.isNotEmpty && entry.alias == '$prefix$path') {
return entry.targetRoute;
}
}
}
// 2. 兜底: 尝试路径匹配
return _resolvePathFallback(path);
}
/// 解析 https://s2ss.com 通用链接
/// 格式: https://s2ss.com/<segment>[/<sub>]
static String? resolveHttps(Uri uri) {
final segments = uri.pathSegments;
if (segments.isEmpty) return AppRoutes.home;
final first = segments.first;
final prefix = 'https://s2ss.com/$first';
// 1. 精确匹配
for (final entry in entries) {
if (!entry.alias.startsWith('https://s2ss.com/')) continue;
if (entry.hasSubPath) {
// 子路径匹配: https://s2ss.com/article/*
if (entry.alias == prefix) {
return '/${segments.join('/')}';
}
} else if (segments.length == 1) {
// 精确匹配: https://s2ss.com/fortune
if (entry.alias == prefix) {
return entry.targetRoute;
}
}
}
// 2. 兜底: 尝试路径匹配
return _resolvePathFallback('/${segments.join('/')}');
}
/// 路径兜底解析:将 URI path 段直接映射为内部路由
static String? _resolvePathFallback(String path) {
if (path.isEmpty || path == '/') return AppRoutes.home;
// 从映射表中查找 /tool/xxx 格式的路径
for (final entry in entries) {
if (!entry.alias.startsWith('/')) continue;
if (entry.alias == path) {
return entry.targetRoute;
}
}
// 最终兜底:直接返回路径(如果是内部路由格式)
if (path.startsWith('/')) {
return path;
}
return null;
}
/// 验证映射配置完整性
/// 检查所有 deepLinkAliases 的目标路由是否在 routeRegistry 中存在
static List<String> validate() {
final errors = <String>[];
final validPaths = routeRegistry.map((r) => r.path).toSet();
for (final entry in entries) {
if (!validPaths.contains(entry.targetRoute)) {
errors.add('深度链接映射目标路由不存在: ${entry.alias} → ${entry.targetRoute}');
}
}
// 检查重复别名
final seenAliases = <String>{};
for (final entry in entries) {
if (seenAliases.contains(entry.alias)) {
errors.add('重复的深度链接别名: ${entry.alias}');
}
seenAliases.add(entry.alias);
}
return errors;
}
}
Task 3: 重构 AppRouter 深度链接解析逻辑
Files:
-
Modify:
lib/core/router/app_router.dart -
Step 1: 替换 AppRouter 中的硬编码 switch 方法
将 AppRouter 类中的 _resolveCustomScheme、_resolveHttps、_resolveToolPath、_resolveSettingsPath、_resolvePathFallback 全部删除,替换为调用 DeepLinkResolver:
修改后的 AppRouter 类:
/// 统一深度链接URI解析入口
/// 支持 xianyan:// scheme 和 https://s2ss.com 通用链接
/// 供 GoRouter redirect 和 DeepLinkService 共用
class AppRouter {
AppRouter._();
/// 解析 URI 为内部路由路径
/// 返回 null 表示不是深度链接,不需要重定向
static String? resolveDeepLinkUri(Uri uri) {
final scheme = uri.scheme.toLowerCase();
if (scheme == 'xianyan') {
return DeepLinkResolver.resolveCustomScheme(uri);
}
if (scheme == 'https' || scheme == 'http') {
final host = uri.host.toLowerCase();
if (host == 's2ss.com' || host == 'www.s2ss.com') {
return DeepLinkResolver.resolveHttps(uri);
}
}
return null;
}
}
同时添加 import:
import 'deep_link_resolver.dart';
Task 4: 在 route_registry 中为各路由添加深度链接映射
Files:
-
Modify:
lib/core/router/route_registry.dart -
Step 1: 为所有需要深度链接支持的路由添加 deepLinkAliases
根据原有硬编码 switch 中的映射关系,为 route_registry 中的每个路由添加对应的 deepLinkAliases。
核心路由映射(来自 _resolveCustomScheme + _resolveHttps):
// home
deepLinkAliases: ['xianyan://home', 'https://s2ss.com/home'],
// discover
deepLinkAliases: ['xianyan://discover', 'https://s2ss.com/discover'],
// profile
deepLinkAliases: ['xianyan://profile', 'https://s2ss.com/profile'],
// search
deepLinkAliases: ['xianyan://search', 'https://s2ss.com/search'],
// dailyFortune
deepLinkAliases: ['xianyan://fortune', 'https://s2ss.com/fortune'],
// article (支持子路径)
deepLinkAliases: ['xianyan://article/*', 'https://s2ss.com/article/*'],
// noteList
deepLinkAliases: ['xianyan://notes', 'https://s2ss.com/notes'],
// inspiration
deepLinkAliases: ['xianyan://inspiration', 'https://s2ss.com/inspiration'],
// favorites
deepLinkAliases: ['xianyan://favorites', 'https://s2ss.com/favorites'],
// history
deepLinkAliases: ['xianyan://history', 'https://s2ss.com/history'],
// editor
deepLinkAliases: ['xianyan://editor', 'https://s2ss.com/editor'],
// signin
deepLinkAliases: ['xianyan://signin'],
// weather
deepLinkAliases: ['xianyan://weather', 'https://s2ss.com/weather'],
// weatherSettings
deepLinkAliases: ['xianyan://weather/settings', 'https://s2ss.com/weather/settings'],
// poetry
deepLinkAliases: ['xianyan://poetry', 'https://s2ss.com/poetry'],
// poetrySettings
deepLinkAliases: ['xianyan://poetry/settings', 'https://s2ss.com/poetry/settings'],
// pomodoro
deepLinkAliases: ['xianyan://pomodoro'],
// countdown
deepLinkAliases: ['xianyan://countdown'],
// solarTerm
deepLinkAliases: ['xianyan://solar-term'],
// knowledgeGraph
deepLinkAliases: ['xianyan://knowledge-graph'],
// studyPlan
deepLinkAliases: ['xianyan://study-plan'],
// notificationSettings
deepLinkAliases: ['xianyan://notification-settings'],
// achievement
deepLinkAliases: ['xianyan://achievement', 'xianyan://checkin', 'https://s2ss.com/achievement', 'https://s2ss.com/checkin'],
// rank
deepLinkAliases: ['xianyan://rank', 'https://s2ss.com/rank'],
// learning
deepLinkAliases: ['xianyan://learning', 'https://s2ss.com/learning'],
工具路由映射(来自 _resolveToolPath):
// hanziTool
deepLinkAliases: ['/tool/hanzi'],
// ocr
deepLinkAliases: ['/tool/ocr'],
// china_colors
deepLinkAliases: ['/tool/colors', '/tool/china_colors'],
// tool list (hot)
deepLinkAliases: ['/tool/hot', '/tool/list'],
// calc
deepLinkAliases: ['/tool/calc'],
// offline
deepLinkAliases: ['/tool/offline'],
// cacheManagement
deepLinkAliases: ['/tool/cache'],
// readLater
deepLinkAliases: ['/tool/readlater'],
// nickTool
deepLinkAliases: ['/tool/nick'],
// ... 其他工具路由类似
设置路由映射(来自 _resolveSettingsPath):
// themeSettings
deepLinkAliases: ['xianyan://settings/theme'],
// generalSettings
deepLinkAliases: ['xianyan://settings/general', 'xianyan://settings'],
// accountSettings
deepLinkAliases: ['xianyan://settings/account'],
// dataManagement
deepLinkAliases: ['xianyan://settings/data'],
// notificationSettings
deepLinkAliases: ['xianyan://settings/notifications'],
// languageSettings
deepLinkAliases: ['xianyan://settings/language'],
// fontManagement
deepLinkAliases: ['xianyan://settings/fonts'],
// appLockSettings
deepLinkAliases: ['xianyan://settings/app-lock'],
Task 5: 添加配置验证 + 更新 CHANGELOG
Files:
-
Modify:
lib/main.dart(添加启动时验证) -
Modify:
CHANGELOG.md -
Step 1: 在 main.dart 中添加深度链接配置验证
在应用启动时调用 DeepLinkResolver.validate() 检查配置完整性,仅在 debug 模式下执行:
// 在 main() 函数中,DeepLinkService.init() 之前添加:
assert(() {
final errors = DeepLinkResolver.validate();
if (errors.isNotEmpty) {
for (final e in errors) {
Log.e('🔗 [DeepLink] 配置错误: $e', null, null, LogCategory.router);
}
}
return true;
}());
- Step 2: 更新 CHANGELOG.md
在文件顶部新增版本记录:
## [v6.8.1] - 2026-06-09
### ♻️ 重构 — 深度链接解析逻辑配置驱动化
#### 变更描述
将 `app_router.dart` 中5个硬编码 switch 方法的深度链接解析逻辑,重构为基于 `route_registry` 的配置驱动架构。新增路由只需在 `RouteDef.deepLinkAliases` 中声明别名即可,无需手动同步多个 switch 语句。
#### 新增文件
- `lib/core/router/deep_link_resolver.dart` — 配置驱动的深度链接解析器
#### 修改文件
- `lib/core/router/route_def.dart` — RouteDef 新增 `deepLinkAliases` 字段
- `lib/core/router/app_router.dart` — 删除5个硬编码 switch 方法,改用 DeepLinkResolver
- `lib/core/router/route_registry.dart` — 为各路由添加 deepLinkAliases 映射
- `lib/main.dart` — 添加 debug 模式下的深度链接配置验证
#### 向后兼容
- 深度链接解析结果与重构前完全一致
- DeepLinkService 调用方式不变
- 所有 xianyan:// 和 https://s2ss.com 链接行为不变
Task 6: 运行 analyze 验证代码正确性
- Step 1: 运行 Flutter 静态分析
Run: flutter analyze --no-pub (设置超时 120s)
Expected: 无新增 error 或 warning
- Step 2: 检查关键文件无语法错误
确认以下文件无编译错误:
lib/core/router/route_def.dartlib/core/router/deep_link_resolver.dartlib/core/router/app_router.dartlib/core/router/route_registry.dart