Files
xianyan/docs/superpowers/plans/2026-06-09-deep-link-refactor.md
Developer a4a7e10722 feat: 完成2026-06-09版本迭代更新
此版本包含:
1. 新增位置消息发送与展示功能
2. 完善多语言本地化文案
3. 新增安卓端管理空间Activity与图标背景
4. 优化摇一摇开关逻辑与深度链接配置
5. 新增信息流平台过滤与A/B测试后台功能
6. 更新签名配置与构建脚本
7. 修复若干已知问题与代码优化
2026-06-09 23:18:13 +08:00

15 KiB
Raw Blame History

深度链接解析逻辑配置驱动重构 实现计划

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.dart
  • lib/core/router/deep_link_resolver.dart
  • lib/core/router/app_router.dart
  • lib/core/router/route_registry.dart