第一部分:[高屋建瓴] Mobile User Profile 系统的战略重要性
在现代 App 中,“用户画像”系统不再仅仅是存储用户信息的数据库条目,它是一个动态的、可演进的、智能的资产。其战略重要性体M现于:
-
个性化体验的基石: 它是实现“千人千面”的根本。无论是推荐系统(商品、内容)、UI 动态调整(展示用户偏好的模块)、还是营销推送,都依赖于一个准确、实时的用户画像。
-
提升用户粘性和留存: 一个“懂你”的 App 能极大地提升用户满意度和使用时长。当用户感觉 App 是为他量身定做时,其迁移成本会显著增高。
-
数据驱动决策的引擎: 聚合的用户画像数据可以揭示用户群体的行为模式,为产品迭代、运营策略提供精准的数据支持。
-
未来智能化的入口: 它是端侧机器学习的“燃料”。在本地处理用户数据进行模型训练和推理,可以实现:
- 更高响应速度: 无需网络请求,实时反馈。
- 更强隐私保护: 敏感数据不出设备,符合越来越严格的隐私法规。
- 离线可用性: 在无网络环境下依然可以提供部分智能服务。
因此,我们在架构设计之初就必须将其视为一个一级功能模块,而不是“个人中心”或“设置”的附属品。
第二部分:[痛点辨析] 概念边界的划分:Auth vs. Settings vs. Preferences vs. Profile
这是最容易混淆,也是导致后续架构腐化的重灾区。必须在设计之初就用“快刀斩乱麻”的方式清晰界定。
| 概念 | 职责 (Responsibility) | 数据例子 | 所属模块 | 特点 |
|---|---|---|---|---|
| 认证信息 (Auth Info) | “你是谁?” - 用于验证用户身份和授权访问。 | userId, accessToken, refreshToken, loginMethod (手机/微信) | features/auth | 高敏感性,安全性要求最高,数据量小,相对稳定。 |
| 应用设置 (App Settings) | “App 该如何为你工作?” - 用户对 App 功能行为的控制。 | isDarkMode, notificationEnabled, language, fontSize | features/settings (或 core/settings) | 功能性,非个人化,与用户兴趣无关,通常是布尔/枚举值。 |
| 用户偏好 (User Preferences) | “你告诉我们你喜欢什么?” - 用户显式提供的主观兴趣和选择。 | 喜欢的分类: [科幻, 悬疑],不感兴趣的标签: [体育],价格敏感度: 高 | features/user_profile (作为输入) | 主观性,是用户画像的原始输入数据之一,用户可直接修改。 |
| 用户画像 (User Profile/Portrait) | “我们认为你是怎样的人?” - 系统通过分析显式偏好和隐式行为后,得出的推断性、结构化的用户模型。 | inferredPersona: "价格敏感的科幻迷", engagementScore: 0.85, topCategories: ["科幻", "数码"], nextBestOffer: "XX 耳机" | features/user_profile (作为核心产出) | 推断性、动态演进,是算法和规则的产物,通常不直接对用户暴露全部内容,是所有个性化服务的直接数据源。 |
核心痛点解决方案:
- 物理隔离: 在项目结构上,
auth和user_profile必须是两个独立的feature模块。它们之间可以通过userId关联,但数据模型和仓库(Repository)完全分离。 - 逻辑分离:
User Preferences是User Profile的一部分,是其输入。UserProfile实体则包含了Preferences以及更多推断出的数据。
第三部分:[架构设计] "用户画像" 模块的落地
基于我们之前的项目结构,我们新增一个 features/user_profile 模块。
flutter_ecommerce_app/
├── lib/
│ ├── features/
│ │ ├── auth/
│ │ ├── products/
│ │ ├── ...
│ │ └── **user_profile/** # << 新增用户画像模块
│ │ ├── presentation/
│ │ │ ├── screens/onboarding_screen.dart # 收集偏好
│ │ │ ├── screens/profile_display_screen.dart # “个人中心”的一部分
│ │ │ └── providers/user_profile_providers.dart
│ │ ├── application/
│ │ │ ├── usecases/get_user_profile_usecase.dart
│ │ │ ├── usecases/update_user_preferences_usecase.dart
│ │ │ └── usecases/track_user_behavior_usecase.dart # 用于记录隐式行为
│ │ ├── domain/
│ │ │ ├── entities/user_preferences.dart
│ │ │ ├── entities/user_profile.dart # << 核心实体
│ │ │ ├── entities/user_behavior_event.dart
│ │ │ └── repositories/user_profile_repository.dart (interface)
│ │ └── infrastructure/
│ │ ├── datasources/profile_local_datasource.dart
│ │ ├── datasources/profile_remote_datasource.dart
│ │ ├── repositories/user_profile_repository_impl.dart
│ │ └── **services/ml_profile_generator.dart** # << 为 Core ML 预留的钩子
│ ├── core/
│ └── main.dart
Domain 层设计 (稳定内核):
// domain/entities/user_preferences.dart
class UserPreferences extends Equatable {
final List<String> likedCategories;
final List<String> dislikedTags;
// ... 其他显式偏好
}
// domain/entities/user_profile.dart
class UserProfile extends Equatable {
final UserPreferences preferences; // 包含显式偏好
final List<String> inferredTopCategories; // 推断出的 Top 分类
final String personaTag; // 推断出的画像标签,如 "TechEnthusiast"
final double engagementScore; // 用户活跃度得分
// ... 其他推断性数据
}
// domain/repositories/user_profile_repository.dart
abstract class UserProfileRepository {
// 获取最终整合后的用户画像
Future<UserProfile> getUserProfile();
// 更新用户显式偏好
Future<void> updateUserPreferences(UserPreferences preferences);
// 记录用户隐式行为(如点击、浏览、购买)
Future<void> logBehavior(UserBehaviorEvent event);
}
Application 层设计 (业务流程):
GetUserProfileUseCase: 调用UserProfileRepository.getUserProfile(),供个人中心页面或推荐系统使用。UpdateUserPreferencesUseCase: 在 Onboarding 或设置页调用,参数是UserPreferences,内部调用UserProfileRepository.updateUserPreferences()。TrackUserBehaviorUseCase: 这是关键。App 内的各种用户行为(点击商品、观看视频)都会调用这个 UseCase,它会调用UserProfileRepository.logBehavior(),将行为数据记录下来。
第四部分:[核心难点] 为 Core ML 预留接口
这正是 Clean Architecture 发挥威力的地方。ML 模型是实现细节,属于基础设施层。
设计思路:
- 数据收集:
UserProfileRepositoryImpl负责从各个数据源收集原始数据。 - 模型调用: 将收集到的原始数据喂给一个“画像生成服务”。
- 结果存储: 将服务返回的
UserProfile缓存并提供给上层。
Infrastructure 层实现:
1. 定义一个抽象的“画像生成服务”接口:
// infrastructure/services/ml_profile_generator.dart
// 这是未来 CoreML 实现需要遵循的契约
abstract class ProfileGeneratorService {
Future<UserProfile> generateProfile({
required UserPreferences currentPreferences,
required List<UserBehaviorEvent> recentBehaviors,
});
}
2. 实现 Repository,并注入这个服务:
// infrastructure/repositories/user_profile_repository_impl.dart
class UserProfileRepositoryImpl implements UserProfileRepository {
final ProfileLocalDataSource _localDataSource;
final ProfileGeneratorService _profileGenerator; // << 注入 ML 服务
UserProfileRepositoryImpl(this._localDataSource, this._profileGenerator);
@override
Future<void> logBehavior(UserBehaviorEvent event) async {
// 将行为事件存储到本地 (e.g., a separate Hive box or SQLite table)
await _localDataSource.saveBehavior(event);
}
@override
Future<void> updateUserPreferences(UserPreferences preferences) async {
await _localDataSource.savePreferences(preferences);
}
@override
Future<UserProfile> getUserProfile() async {
// 1. 检查是否有有效的本地缓存画像
final cachedProfile = await _localDataSource.getProfile();
if (cachedProfile != null && !isStale(cachedProfile.timestamp)) {
return cachedProfile;
}
// 2. 如果没有或已过期,则重新生成
// a. 从本地数据源获取原始数据
final preferences = await _localDataSource.getPreferences();
final behaviors = await _localDataSource.getRecentBehaviors(limit: 100);
// b. 调用 ML 服务进行计算和推断
// 这里就是与 Core ML 交互的“口子”
final newProfile = await _profileGenerator.generateProfile(
currentPreferences: preferences,
recentBehaviors: behaviors,
);
// c. 缓存新生成的画像
await _localDataSource.saveProfile(newProfile);
return newProfile;
}
}
3. 如何为 Core ML 预留口子?
- 当前阶段: 我们可以提供一个基于规则的
RuleBasedProfileGenerator实现,它不依赖 ML,只是简单地根据行为计数等来生成画像。class RuleBasedProfileGenerator implements ProfileGeneratorService { @override Future<UserProfile> generateProfile(...) { // 简单的逻辑:浏览次数最多的分类就是 Top 分类 // ... return Future.value(UserProfile(...)); } } - 未来阶段 (引入 Core ML): 你只需要创建一个新的实现
CoreMLProfileGenerator。class CoreMLProfileGenerator implements ProfileGeneratorService { // 内部会通过 platform channels 调用原生的 Core ML SDK final MethodChannel _channel = MethodChannel('com.yourapp/coreml'); @override Future<UserProfile> generateProfile({ ... }) async { // 1. 将 preferences 和 behaviors 序列化为 ML 模型需要的格式 (e.g., JSON or Map) final inputData = serializeForML(preferences, behaviors); // 2. 通过平台通道调用原生 Core ML 模型进行推理 final resultJson = await _channel.invokeMethod('predictUserProfile', inputData); // 3. 将返回的 JSON 解析为 UserProfile 实体 return UserProfile.fromJson(resultJson); } }
最后,在 Riverpod 的 Provider 中切换实现即可,对 App 的其他部分完全透明!
// providers.dart
final profileGeneratorProvider = Provider<ProfileGeneratorService>((ref) {
// 在这里决定使用哪个实现
// 可以通过环境变量、A/B Test 等方式动态切换
if (featureFlags.isCoreMLEnabled) {
return CoreMLProfileGenerator();
} else {
return RuleBasedProfileGenerator();
}
});
final userProfileRepositoryProvider = Provider<UserProfileRepository>((ref) {
return UserProfileRepositoryImpl(
ref.watch(profileLocalDataSourceProvider),
ref.watch(profileGeneratorProvider), // << 动态注入
);
});
总结
通过上述设计,我们实现了:
- 概念清晰: 明确划分了认证、设置、偏好和画像的边界,避免了职责混乱。
- 结构合理: 将用户画像作为一个独立的、高内聚的功能模块,易于维护和扩展。
- 拥抱变化: 通过在 Domain 层定义稳定的
UserProfileRepository接口,将复杂的画像生成逻辑(无论是基于规则还是 ML)封装在 Infrastructure 层。 - 无缝升级: 为端侧 ML 预留了清晰的、非侵入式的“挂载点” (
ProfileGeneratorService)。未来引入 Core ML 时,只需添加一个新的实现类并修改一行 Provider 的构造代码,而无需触动任何业务逻辑(Use Cases)或 UI 代码,这正是顶级架构弹性的完美体现。
下一篇, 讲述这个用户画像系统如何与其他模块进行交互。