chore: 初始化脚手架 — sunny_mochi 企业级 Flutter 模板

Core 基础设施:Flavor 三包体系、Drift+SQLCipher 加密数据库
7 拦截器 Dio 网络栈、CrashReporter 崩溃日志、Sealed Failure 错误体系
5 色板主题系统、Auth 认证骨架(Clean Architecture)、Dev Panel、Mock Adapter

通过验证:flutter analyze(0 errors)、flutter test(全绿)、staging APK 74.8MB
This commit is contained in:
SkyJourney
2026-05-14 12:51:05 +08:00
commit 61017f1c39
204 changed files with 15386 additions and 0 deletions
@@ -0,0 +1,31 @@
import 'package:drift/drift.dart';
/// 本地错误日志表(keep last 200)。
///
/// [kind] — 错误分类(ErrorKind.name),前端展示用
/// [message] — 技术细节,供后端追踪(不展示给用户)
/// [displayMessage] — 用户看到的友好文案(Failure.userMessage
/// [code] — 业务错误码(ServerFailure.code,如 A0500
/// [statusCode] — HTTP 状态码(ServerFailure.statusCode
/// [reported] — 是否已通过"错误报告"功能上报过
///
/// 设备信息(v5 新增,nullable 兼容迁移):
/// [deviceModel] / [osVersion] / [appVersion] / [appBuild]
/// 在 log() 时快照,确保上报的是**错误发生时**的设备上下文,而非发送时。
@DataClassName('ErrorLogRow')
class ErrorLogs extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get kind => text()();
TextColumn get message => text()();
TextColumn get displayMessage => text()();
TextColumn get code => text().nullable()();
IntColumn get statusCode => integer().nullable()();
DateTimeColumn get occurredAt => dateTime()();
BoolColumn get reported =>
boolean().withDefault(const Constant(false))();
// v5: 设备上下文(事发时快照)
TextColumn get deviceModel => text().nullable()();
TextColumn get osVersion => text().nullable()();
TextColumn get appVersion => text().nullable()();
TextColumn get appBuild => text().nullable()();
}
+20
View File
@@ -0,0 +1,20 @@
import 'package:drift/drift.dart';
import 'package:sunny_mochi/core/sync/sync_status.dart';
/// 所有需要 Offline-First 同步的 Drift 表必须 mixin 这组 4 字段。
///
/// - [syncStatus] 当前状态([SyncStatus]
/// - [localUpdatedAt] 本地最后修改时间(用于决定 dirty)
/// - [serverUpdatedAt] 服务端最后修改时间(last-write-wins 比较基准)
/// - [conflictPayload] 冲突时备份的服务端版本 JSON(人工或策略恢复)
///
/// 详见 docs/flutter-architecture-design.md §五.21。
mixin SyncColumns on Table {
IntColumn get syncStatus =>
intEnum<SyncStatus>().withDefault(const Constant(0))();
DateTimeColumn get localUpdatedAt =>
dateTime().withDefault(currentDateAndTime)();
DateTimeColumn get serverUpdatedAt => dateTime().nullable()();
TextColumn get conflictPayload => text().nullable()();
}
+26
View File
@@ -0,0 +1,26 @@
import 'package:drift/drift.dart';
import 'package:sunny_mochi/core/storage/tables/sync_columns.dart';
/// 用户本地缓存表(对应 iOS UserManager.currentUser 持久化)。
///
/// token 不存 DB — 由 SecureStorage 独占(Keychain / EncryptedSharedPreferences);
/// refreshToken 镜像存入 DB 仅用于可观测性,TokenRefreshInterceptor 仍读 SecureStorage。
@DataClassName('UserRow')
class Users extends Table with SyncColumns {
TextColumn get userId => text()();
TextColumn get username => text().nullable()();
TextColumn get realName => text().nullable()();
TextColumn get phone => text().nullable()();
TextColumn get avatar => text().nullable()();
IntColumn get gender => integer().nullable()();
IntColumn get age => integer().nullable()();
TextColumn get refreshToken => text().nullable()();
TextColumn get email => text().nullable()();
DateTimeColumn get birthday => dateTime().nullable()();
TextColumn get employeeNo => text().nullable()();
TextColumn get company => text().nullable()();
TextColumn get department => text().nullable()();
@override
Set<Column<Object>> get primaryKey => {userId};
}