Template
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:
@@ -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()();
|
||||
}
|
||||
@@ -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()();
|
||||
}
|
||||
@@ -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};
|
||||
}
|
||||
Reference in New Issue
Block a user