✨ 我是 Muzi 的「文章捕手」,擅长在文字的星海中打捞精华。每当新的篇章诞生,我就会像整理贝壳一样,将思想的闪光点串成珍珠项链~
本文详细记录了将项目从Google Firebase全面迁移至腾讯云CloudBase的全过程,涵盖认证系统、数据库和存储服务的核心模块重构。迁移中,作者移除了Firebase依赖,采用CloudBase HTTP API实现Flutter原生应用的认证功能,支持手机号验证码、邮箱密码及匿名登录,并实现Token自动刷新与本地持久化。数据库由Firestore(NoSQL)切换为CloudBase MySQL(关系型数据库),并重构Provider层以适配新数据源。文章还总结了技术选型、NoSQL到SQL的思维转换及HTTP API的优势,展示了完整的代码变更和项目进度,具有较高的技术参考价值和实用意义。
# 前言
今天完成了一项重大的架构变更——将项目从 Google Firebase 完整迁移到腾讯云 CloudBase。这次迁移涉及认证系统、数据库、存储服务等核心模块,共修改 32 个文件,是项目迄今为止最大规模的重构工作。
# 上午:Firebase 依赖清理与 CloudBase 认证
# 1. 移除 Firebase 依赖
首先需要清理所有 Firebase 相关的代码和配置:
// 删除的文件
lib/config/firebase_options.dart // Firebase 配置
lib/firebase_options.dart // Firebase 选项
lib/services/auth_service.dart // Firebase Auth 服务
lib/services/firestore_service.dart // Firestore 服务
lib/services/cloudbase_auth_service.dart // CloudBase SDK 服务
同时更新 pubspec.yaml,移除 Firebase 相关依赖包:
# 移除的依赖
dependencies:
# firebase_core: ^4.4.0 # 移除
# firebase_auth: ^6.1.0 # 移除
# cloud_firestore: ^6.1.0 # 移除
# firebase_storage: ^13.0.0 # 移除
# 2. CloudBase HTTP API 认证服务
由于 CloudBase SDK 不支持 Flutter 原生应用,必须使用 HTTP API 方式:
/// CloudBase HTTP API 认证服务
class CloudbaseAuthHttpService {
/// 手机验证码登录
Future<CloudbaseAuthState> signInWithPhone(String phone, String code) async {
// 1. 发送验证码
final verifyResult = await _sendPhoneOtp(phone);
// 2. 验证验证码
final token = await _verifyOtp(verifyResult.verificationId, code);
// 3. 登录获取 Token
return await _signInWithVerificationToken(token);
}
/// 邮箱密码登录
Future<CloudbaseAuthState> signInWithPassword({
required String email,
required String password,
}) async {
final response = await http.post(
Uri.parse('$_baseUrl/auth/v1/signin'),
headers: {
'Authorization': 'Bearer ${CloudbaseConfig.publishableKey}',
'Content-Type': 'application/json',
},
body: jsonEncode({
'email': email,
'password': password,
}),
);
// ...处理响应
}
}
⚠️ 重要提醒:手机号必须包含国家码和空格,格式为 +86 13800138000
# 下午:数据库迁移与 Provider 重构
# 1. Firestore → CloudBase MySQL
这次最大的变化是从 Firestore(NoSQL)迁移到 CloudBase MySQL(关系型数据库):
| 对比项 | Firestore | CloudBase MySQL |
|---|---|---|
| 类型 | NoSQL 文档数据库 | 关系型数据库 |
| 查询 | 集合查询 | SQL + REST API |
| 结构 | 灵活 Schema | 固定表结构 |
| 事务 | 支持 | 支持 |
CloudbaseService 核心实现:
/// REST API 请求 (用于 MySQL REST 接口)
Future<List<Map<String, dynamic>>> _restGet(
String table, {
Map<String, String>? filters,
int? limit,
String? orderBy,
}) async {
final token = await _getAuthToken();
final uri = Uri.parse('$_baseUrl/rest/v1/$table')
.replace(queryParameters: queryParams);
final response = await http.get(uri, headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
});
// ...
}
# 2. Provider 层全面重构
所有 Provider 都需要切换数据源:
// 用户 Provider - 切换到 CloudBase
final userNotifierProvider = AsyncNotifierProvider<UserNotifier, UserModel?>(() {
return UserNotifier();
});
class UserNotifier extends AsyncNotifier<UserModel?> {
@override
Future<UserModel?> build() async {
final cloudbaseService = ref.watch(cloudbaseServiceProvider);
final authState = await ref.watch(cloudbaseAuthStateHttpProvider.future);
if (authState?.user == null) return null;
return await cloudbaseService.getUser(authState!.user!.uid);
}
}
# 今日成果
# 功能完成
- ✅ Firebase 依赖完全移除(5 个文件)
- ✅ CloudBase HTTP API 认证服务
- ✅ 手机验证码 / 邮箱密码 / 匿名登录
- ✅ Token 自动刷新和本地持久化
- ✅ CloudBase MySQL 数据服务
- ✅ 用户/宠物/背包/成就/签到 CRUD
- ✅ 所有 Provider 迁移完成
- ✅ 登录/注册页面适配
# 代码变更统计
| 指标 | 数值 |
|---|---|
| 修改文件数 | 32 |
| 新增代码行 | ~1,693 |
| 删除代码行 | ~1,778 |
| 净变化 | -85 行 |
# 新增文件
lib/services/
└── cloudbase_auth_http_service.dart # CloudBase HTTP API 认证
# 删除文件
lib/
├── config/firebase_options.dart
├── firebase_options.dart
└── services/
├── auth_service.dart
├── firestore_service.dart
└── cloudbase_auth_service.dart
# 遇到的问题
# 1. Flutter 不支持 CloudBase SDK
问题:CloudBase 官方 SDK(@cloudbase/js-sdk)仅支持 Web 和微信小程序,不支持 Flutter 原生应用。
解决方案:使用 CloudBase HTTP API 直接调用,封装 CloudbaseAuthHttpService:
// 直接使用 HTTP 请求而非 SDK
final response = await http.post(
Uri.parse('${CloudbaseConfig.apiBaseUrl}/auth/v1/signin'),
headers: {'Authorization': 'Bearer ${CloudbaseConfig.publishableKey}'},
body: jsonEncode(requestBody),
);
# 2. 手机号格式要求
问题:CloudBase API 要求手机号必须包含国家码和空格,否则返回 400 错误。
解决方案:
// 格式化手机号
String formatPhone(String phone) {
if (phone.startsWith('+86')) return phone;
return '+86 $phone'; // 注意:国家码和号码之间有空格
}
# 3. Token 持久化
问题:认证状态需要在应用重启后保持。
解决方案:使用 SharedPreferences 存储 Token:
Future<void> _saveAuthState(CloudbaseAuthState state) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('access_token', state.accessToken ?? '');
await prefs.setString('refresh_token', state.refreshToken ?? '');
await prefs.setInt('expires_at', DateTime.now()
.add(Duration(seconds: state.expiresIn ?? 0))
.millisecondsSinceEpoch);
}
# 明日计划
# 心得体会
这次迁移工作量比预期大很多,主要有几点感悟:
1. 技术选型要考虑平台限制
CloudBase SDK 不支持 Flutter 是个坑,好在 HTTP API 功能完整,但需要自己封装更多逻辑。以后选择 BaaS 服务时,一定要先确认目标平台的支持情况。
2. NoSQL → SQL 的思维转换
从 Firestore 的集合/文档模型切换到 MySQL 的表/行模型,需要重新思考数据结构。不过关系型数据库在复杂查询场景下确实更强大。
3. HTTP API 的好处
虽然没有 SDK 方便,但 HTTP API 更通用,调试也更直观。用 Postman 测试接口后再写代码,效率反而更高。
# 项目进度
| 模块 | 进度 | 说明 |
|---|---|---|
| 认证系统 | 100% | CloudBase HTTP API 完成 |
| 数据服务 | 100% | MySQL REST API 完成 |
| 存储服务 | 100% | 腾讯云 COS 完成 |
| 宠物养成 | 90% | 核心功能完成 |
| 签到系统 | 100% | 完成 |
| 成就系统 | 100% | 完成 |
| 社区功能 | 0% | 待开发 |
| AI 生成 | 0% | 待开发 |
# 技术栈总结
迁移后的技术栈:
| 层级 | 原方案 | 现方案 |
|---|---|---|
| 认证 | Firebase Auth | CloudBase HTTP API |
| 数据库 | Firestore (NoSQL) | CloudBase MySQL |
| 存储 | Firebase Storage | 腾讯云 COS |
| 状态管理 | Riverpod | Riverpod(不变) |
| 路由 | go_router | go_router(不变) |
项目地址:Cat Club(待开源)