Skip to content

Commit

Permalink
refactor:重新设计了训练日志宽表,计划可以任意删除修改,动作和训练未被使用时可删除。删除的计划和训练的训练记录的关键信息依旧保存在…
Browse files Browse the repository at this point in the history
…日志宽表中。
  • Loading branch information
Sanotsu committed Dec 27, 2023
1 parent 7000621 commit 8c4a99b
Show file tree
Hide file tree
Showing 17 changed files with 268 additions and 587 deletions.
348 changes: 39 additions & 309 deletions lib/common/utils/db_training_helper.dart

Large diffs are not rendered by default.

35 changes: 20 additions & 15 deletions lib/common/utils/ddl_training.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ class TrainingDdl {
// 训练计划-动作组关系表
static const tableNameOfPlanHasGroup = 'ff_plan_has_group';

// 训练日志记录表
static const tableNameOfTrainedLog = 'ff_trained_log';
// 2023-12-27 训练日志基础宽表,不再级联查询plan和group
static const tableNameOfTrainedDetailLog = 'ff_trained_detail_log';

static const String ddlForExercise = """
CREATE TABLE $tableNameOfExercise (
Expand Down Expand Up @@ -101,19 +101,24 @@ class TrainingDdl {
);
""";

static const String ddlForTrainedLog = """
CREATE TABLE $tableNameOfTrainedLog (
trained_log_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
trained_date TEXT,
user_id INTEGER NOT NULL,
plan_id INTEGER,
day_number INTEGER,
group_id INTEGER,
trained_start_time TEXT NOT NULL,
trained_end_time TEXT NOT NULL,
trained_duration INTEGER NOT NULL,
totol_paused_time INTEGER NOT NULL,
total_rest_time INTEGER NOT NULL
static const String ddlForTrainedDetailLog = """
CREATE TABLE $tableNameOfTrainedDetailLog (
trained_detail_log_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
trained_date TEXT,
user_id INTEGER NOT NULL,
plan_name TEXT,
plan_category TEXT,
plan_level TEXT,
day_number INTEGER,
group_name TEXT,
group_category TEXT,
group_level TEXT,
consumption INTEGER,
trained_start_time TEXT NOT NULL,
trained_end_time TEXT NOT NULL,
trained_duration INTEGER NOT NULL,
totol_paused_time INTEGER NOT NULL,
total_rest_time INTEGER NOT NULL
);
""";
}
8 changes: 4 additions & 4 deletions lib/l10n/app_zh.arb
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
"exercise": "动作",
"exerciseLabel": "基础动作",
"exerciseSubtitle": "管理运动的各个基础动作",
"exerciseDeleteAlert":"确认要删除该动作: {exerciseName}删除后不可恢复",
"exerciseDeleteAlert":"确认要删除该动作: {exerciseName} ? 删除后不可恢复!",
"exerciseInUse":"该动作 {exerciseName} 有被训练或计划使用,暂不支持删除.",

"exerciseQuerys": "{num, select, 0{训练部位} 1{代号} 2{名称} 3{级别} 4{类型} 5{分类} 6{器械} 7{计量} other{其他} }",
Expand Down Expand Up @@ -132,8 +132,8 @@
"@workoutQuerys": {
"description": "常用workout查询用的标签"
},
"groupDeleteAlert":"确认要删除该训练动作组:{workoutName}删除后不可恢复",
"groupInUse":"该训练 {workoutName} 有被计划使用或者存在跟练记录,暂不支持删除",
"groupDeleteAlert":"确认要删除该训练动作组: {workoutName} ? 删除后不可恢复!",
"groupInUse":"该训练 {workoutName} 有被计划使用,暂不支持删除",

"modifyGroupLabels": "{num, select, 0{修改训练} 1{新建训练} 2{主要肌肉} 3{次要肌肉} 4{技术要点} 5{语音提示要点} 6{动作图片} 7{用户上传} other{其他} }",
"@modifyGroupLabels": {
Expand Down Expand Up @@ -181,7 +181,7 @@
"@planQuerys": {
"description": "常用 plan 查询用的标签"
},
"planDeleteAlert":"确认要删除该训练计划:{planName}删除后不可恢复",
"planDeleteAlert":"确认要删除该训练计划: {planName} ? 删除后不可恢复!",
"planInUse":"该训练计划 {planName} 存在跟练记录,暂不支持删除",

"modifyPlanLabels": "{num, select, 0{修改计划} 1{新建计划} 2{名称} 3{代号} 4{分类} 5{级别} 6{训练周期} 7{概述} other{其他} }",
Expand Down
116 changes: 37 additions & 79 deletions lib/models/training_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -304,20 +304,27 @@ class PlanHasGroup {
}
}

// 训练日志表
class TrainedLog {
int? trainedLogId; // 自增的,可以不传
int? planId, dayNumber, groupId;
// 2023-12-27 训练日志宽表,不再级联查询plan和group,这样后两者有删除也可以查看历史记录
class TrainedDetailLog {
int? trainedDetailLogId; // 自增的,可以不传
int? dayNumber, consumption;
String? planName, planCategory, planLevel;
String? groupName, groupCategory, groupLevel;
String trainedDate, trainedStartTime, trainedEndTime;
int userId, trainedDuration, totolPausedTime, totalRestTime;

TrainedLog({
this.trainedLogId,
TrainedDetailLog({
this.trainedDetailLogId,
required this.trainedDate,
required this.userId,
this.planId,
this.planName,
this.planCategory,
this.planLevel,
this.dayNumber,
this.groupId,
this.groupName,
this.groupCategory,
this.groupLevel,
this.consumption,
required this.trainedStartTime,
required this.trainedEndTime,
required this.trainedDuration,
Expand All @@ -328,12 +335,17 @@ class TrainedLog {
// 转换成一个Map。键必须对应于数据库中的列名。
Map<String, dynamic> toMap() {
return {
'trained_log_id': trainedLogId,
'trained_detail_log_id': trainedDetailLogId,
'trained_date': trainedDate,
'user_id': userId,
'plan_id': planId,
'plan_name': planName,
'plan_category': planCategory,
'plan_level': planLevel,
'day_number': dayNumber,
'group_id': groupId,
'group_name': groupName,
'group_category': groupCategory,
'group_level': groupLevel,
'consumption': consumption,
'trained_start_time': trainedStartTime,
'trained_end_time': trainedEndTime,
'trained_duration': trainedDuration,
Expand All @@ -343,14 +355,19 @@ class TrainedLog {
}

// 用于从数据库行映射到 ServingInfo 对象的 fromMap 方法
factory TrainedLog.fromMap(Map<String, dynamic> map) {
return TrainedLog(
trainedLogId: map['trained_log_id'] as int?,
factory TrainedDetailLog.fromMap(Map<String, dynamic> map) {
return TrainedDetailLog(
trainedDetailLogId: map['trained_detail_log_id'] as int?,
trainedDate: map['trained_date'] as String,
userId: map['user_id'] as int,
planId: map['plan_id'] as int?,
planName: map['plan_name'] as String?,
planCategory: map['plan_category'] as String?,
planLevel: map['plan_level'] as String?,
dayNumber: map['day_number'] as int?,
groupId: map['group_id'] as int?,
groupName: map['group_name'] as String?,
groupCategory: map['group_category'] as String?,
groupLevel: map['group_level'] as String?,
consumption: map['consumption'] as int?,
trainedStartTime: map['trained_start_time'] as String,
trainedEndTime: map['trained_end_time'] as String,
trainedDuration: map['trained_duration'] as int,
Expand All @@ -363,9 +380,10 @@ class TrainedLog {
@override
String toString() {
return '''
TrainedLog{
trainedLogId:$trainedLogId, trainedDate:$trainedDate, userId:$userId,
planId:$planId, dayNumber:$dayNumber, groupId:$groupId,
TrainedDetailLog{
trainedDetailLogId:$trainedDetailLogId, trainedDate:$trainedDate, userId:$userId,
planName:$planName, planCategory:$planCategory, planLevel:$planLevel, dayNumber:$dayNumber,
groupName:$groupName, groupCategory:$groupCategory, groupLevel:$groupLevel, consumption:$consumption,
trainedStartTime:$trainedStartTime, trainedEndTime:$trainedEndTime,
trainedDuration:$trainedDuration, totolPausedTime:$totolPausedTime, totalRestTime:$totalRestTime
}
Expand Down Expand Up @@ -440,63 +458,3 @@ class ActionPractice {
''';
}
}

// 训练日志要带上训练或者计划的基础信息(没有动作的信息)
class TrainedLogWithGroupBasic {
final TrainedLog log;
// 如果直接的训练的跟练,对应训练的数据;如果是计划的某一天的训练,同样记录训练信息,是哪一天在log表中
final TrainingGroup? group;
// 如果是某个计划的某一天的训练,带上计划的信息
final TrainingPlan? plan;

TrainedLogWithGroupBasic({
required this.log,
// 不查询训练是也可以用这个类,代替日志类?
this.group,
this.plan,
});

@override
String toString() {
return '''
TrainedLogWithGroupBasic {
log: $log, group: $group, plan: $plan
''';
}
}

// 2023-12-14 如果一个动作有被使用,则记录被哪个关联表使用
class ExerciseUsageVO {
int? actionId;
int? groupId;
int? planId;
int? logId;

ExerciseUsageVO({
this.actionId,
this.groupId,
this.planId,
this.logId,
});

// 作为vo,应该只有查询的对应。目前只有联合查询时用到
// 注意栏位前缀有不同表的同名栏位,栏位会重复,而不会在栏位前带上表名
factory ExerciseUsageVO.fromMap(Map<String, dynamic> map) {
return ExerciseUsageVO(
actionId: map['action_id'] as int?,
groupId: map['group_id'] as int?,
planId: map['plan_id'] as int?,
logId: map['trained_log_id'] as int?,
);
}

@override
String toString() {
return 'ExerciseUsageVO {actionId: $actionId, groupId: $groupId, planId: $planId, logId: $logId }';
}

// @override
// String toString() {
// return '配置编号:$actionId,训练编号:$groupId,计划编号:$planId,日志:$logId';
// }
}
12 changes: 2 additions & 10 deletions lib/views/me/_feature_mock_data/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,6 @@ class _FeatureMockDemoState extends State<FeatureMockDemo> {
child: const Text("userId为1的新增10条随机BMI数据"),
),

TextButton(
onPressed: () async {
await insertTrainingLogDemo();
if (!mounted) return;
_showSimpleDialog(context, "已新增2条【训练日志】示例");
},
child: const Text("插入两条训练日志(先新增基础数据)"),
),

ElevatedButton(
onPressed: () async {
await _dietaryHelper.deleteDB();
Expand All @@ -186,7 +177,8 @@ class _FeatureMockDemoState extends State<FeatureMockDemo> {
}
await insertExtraUsers();
await insertBMIDemo();
await insertTrainingLogDemo();

await insertTrainingDetailLogDemo();

if (!mounted) return;
_showSimpleDialog(context, "已插入所有数据");
Expand Down
43 changes: 28 additions & 15 deletions lib/views/me/_feature_mock_data/test_funcs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -358,17 +358,22 @@ Future<int> insertOneRandomPlanHasGroup() async {
return planId;
}

// 插入一条跟练记录(不那么随机,且需要已有训练的基础数据)
insertTrainingLogDemo({int? size = 10}) async {
print("【【【 插入测试数据 start-->:insertTrainingLogDemo ");
// 2023-12-27 插入训练日志宽表数据,展示运动报告时不需要级联查询基础表
insertTrainingDetailLogDemo({int? size = 10}) async {
print("【【【 插入测试数据 start-->:insertTrainingDetailLogDemo ");

/// 2023-12-27 正好是测试日志宽表,不会关联其他基础表,数据随意写就好了
// 计划中的某一天
var tl1 = TrainedLog(
var tl1 = TrainedDetailLog(
trainedDate: getCurrentDateTime(),
userId: Random().nextInt(3) + 1,
// 单次记录,有计划及其训练日,就没有训练编号了;反之亦然
planId: 1,
dayNumber: 2,
planName: generateRandomString(5, 20),
planCategory:
categoryOptions[Random().nextInt(categoryOptions.length)].value,
planLevel: levelOptions[Random().nextInt(levelOptions.length)].value,
dayNumber: Random().nextInt(8) + 1,
// 起止时间就测试插入时的1个小时
trainedStartTime: DateFormat(constDatetimeFormat)
.format(DateTime.now().add(const Duration(hours: -1))),
Expand All @@ -379,14 +384,19 @@ insertTrainingLogDemo({int? size = 10}) async {
totalRestTime: 12 * 60, // 休息的总时间
);

var logId1 = await _trainingHelper.insertTrainingLog(tl1);
var logId1 = await _trainingHelper.insertTrainedDetailLog(tl1);

// 直接的某个训练
var tl2 = TrainedLog(
var tl2 = TrainedDetailLog(
trainedDate: getCurrentDateTime(),
userId: Random().nextInt(3) + 1,
// 单次记录,有计划及其训练日,就没有训练编号了;反之亦然
groupId: 1,
groupName: generateRandomString(5, 20),
groupCategory:
groupCategoryOptions[Random().nextInt(groupCategoryOptions.length)]
.value,
groupLevel: levelOptions[Random().nextInt(levelOptions.length)].value,
consumption: Random().nextInt(1000),
// 起止时间就测试插入时的1个小时
trainedStartTime: DateFormat(constDatetimeFormat)
.format(DateTime.now().add(const Duration(hours: -2))),
Expand All @@ -398,16 +408,19 @@ insertTrainingLogDemo({int? size = 10}) async {
totalRestTime: 20 * 60, // 休息的总时间
);

var logId2 = await _trainingHelper.insertTrainingLog(tl2);
var logId2 = await _trainingHelper.insertTrainedDetailLog(tl2);

// 前一天的日志 计划中的某一天
var tl3 = TrainedLog(
var tl3 = TrainedDetailLog(
trainedDate: DateFormat(constDatetimeFormat)
.format(DateTime.now().add(const Duration(days: -1))),
userId: Random().nextInt(3) + 1,
// 单次记录,有计划及其训练日,就没有训练编号了;反之亦然
planId: 1,
dayNumber: 1,
planName: generateRandomString(5, 20),
planCategory:
categoryOptions[Random().nextInt(categoryOptions.length)].value,
planLevel: levelOptions[Random().nextInt(levelOptions.length)].value,
dayNumber: Random().nextInt(8) + 1,
// 起止时间就测试插入时的1个小时
trainedStartTime: DateFormat(constDatetimeFormat)
.format(DateTime.now().add(const Duration(days: -1, hours: -1))),
Expand All @@ -419,10 +432,10 @@ insertTrainingLogDemo({int? size = 10}) async {
totalRestTime: 12 * 60, // 休息的总时间
);

var logId3 = await _trainingHelper.insertTrainingLog(tl3);
var logId3 = await _trainingHelper.insertTrainedDetailLog(tl3);

print(
"【【【 插入测试数据 end-->:insertTrainingLogDemo logId: $logId1 $logId2 $logId3",
"【【【 插入测试数据 end-->:insertTrainingDetailLogDemo logId: $logId1 $logId2 $logId3",
);
}

Expand Down
6 changes: 3 additions & 3 deletions lib/views/me/backup_and_restore/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,9 @@ class _BackupAndRestoreState extends State<BackupAndRestore> {
} else if (filename == "ff_meal_photo.json") {
var temp = jsonMapList.map((e) => MealPhoto.fromMap(e)).toList();
await _dietaryHelper.insertMealPhotoList(temp);
} else if (filename == "ff_trained_log.json") {
var temp = jsonMapList.map((e) => TrainedLog.fromMap(e)).toList();
await _trainingHelper.insertTrainingLogList(temp);
} else if (filename == "ff_trained_detail_log.json") {
var temp = jsonMapList.map((e) => TrainedDetailLog.fromMap(e)).toList();
await _trainingHelper.insertTrainingDetailLogList(temp);
} else if (filename == "ff_diary.json") {
var temp = jsonMapList.map((e) => Diary.fromMap(e)).toList();
await _diaryHelper.insertDiaryList(temp);
Expand Down
3 changes: 1 addition & 2 deletions lib/views/training/exercise/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,7 @@ class _TrainingExerciseState extends State<TrainingExercise> {
// 左滑显示删除确认弹窗,???删除时还要检查删除者是否为创建者,这里只是测试左滑删除卡片
confirmDismiss: (DismissDirection direction) async {
// 如果该基础活动有被使用,则不允许直接删除
var list =
await _dbHelper.isExerciseUsedByRawSQL(exerciseItem.exerciseId!);
var list = await _dbHelper.isExerciseUsed(exerciseItem.exerciseId!);

if (!mounted) return false;
if (list.isNotEmpty) {
Expand Down
Loading

0 comments on commit 8c4a99b

Please sign in to comment.