Melon 1 gadu atpakaļ
vecāks
revīzija
0a087e777e

+ 27 - 6
lib/database/db.dart

@@ -1,8 +1,14 @@
 import 'package:vital_local_database/index.dart';
-import 'package:vitalapp/database/db_patch/add_usercode.dart';
+import 'package:vitalapp/database/db_patch/v1.dart';
 import 'package:vitalapp/database/entities/diagnosis.dart';
+import 'package:vitalapp/database/entities/followup.dart';
 import 'package:vitalapp/database/entities/patient.dart';
+import 'package:vitalapp/database/repositories/followup.dart';
+import 'package:vitalapp/database/repositories/interfaces/followup.dart';
+import 'db_patch/v2.dart';
 import 'repositories/interfaces/diagnosis.dart';
+import 'repositories/interfaces/exam.dart';
+import 'repositories/interfaces/exam_batch.dart';
 import 'repositories/interfaces/patient.dart';
 import 'repositories/diagnosis.dart';
 import 'repositories/patient.dart';
@@ -30,22 +36,28 @@ class VitalDatabaseAccessor
     await checkAndUpgradeDatabaseVersion();
     _store.regRepository<IPatientRepository>(PatientRepository(database));
     _store.regRepository<IDiagnosisRepository>(DiagnosisRepository(database));
+    _store.regRepository<IFollowUpRepository>(FollowUpRepository(database));
   }
 
   ///检查本地数据库版本,以判断是否需要升级
   Future<void> checkAndUpgradeDatabaseVersion() async {
-    var originalVersion = await _database.getVersion();
+    var originalVersion = await database.getVersion();
     logger.w(
         "VitalDatabaseAccessor checkAndUpgradeDatabaseVersion originalVersion:$originalVersion.");
     if (originalVersion == 0) {
-      await db.database.execute(PatientEntity.TABLE_CREATE_SQL);
-      await db.database.execute(DiagnosisEntity.TABLE_CREATE_SQL);
+      // 数据库首次创建,V0
+      await database.execute(PatientEntity.TABLE_CREATE_SQL);
+      await database.execute(DiagnosisEntity.TABLE_CREATE_SQL);
     }
 
-    var databasepatch = DatabasePatchAddUserCode(); //新增userCode
-    var version = await databasepatch.performTask(originalVersion);
+    int version = originalVersion;
+    version = await DatabasePatchV1(database).perform(version);
+    version = await DatabasePatchV2(database).perform(version);
 
     if (version != originalVersion) {
+      // 数据库升级
+      await db.database.setVersion(version);
+      // 重启DB
       await _connection.close();
       _database = await _connection.open();
     }
@@ -64,4 +76,13 @@ class VitalDatabaseRepositoryStore extends BaseDbAccessorRepositoryStore {
 
   /// 检测记录仓储
   IDiagnosisRepository get diagnosis => findRepository<IDiagnosisRepository>()!;
+
+  /// 随访仓储
+  IFollowUpRepository get followUp => findRepository<IFollowUpRepository>()!;
+
+  /// 体检仓储
+  IExamRepository get exam => findRepository<IExamRepository>()!;
+
+  /// 体检批次仓储
+  IExamBatchRepository get examBatch => findRepository<IExamBatchRepository>()!;
 }

+ 0 - 32
lib/database/db_patch/add_usercode.dart

@@ -1,32 +0,0 @@
-import 'package:vitalapp/database/db.dart';
-import 'interface/database_patch.dart';
-import 'package:fis_common/logger/logger.dart';
-
-///新增表patients、diagnosis中的userCode字段
-class DatabasePatchAddUserCode extends IDatabasePatch {
-  @override
-  Future<int> performTask(int version) async {
-    if (version == 0) {
-      logger.w("DatabasePatchAddUserCode performTask version:$version.");
-      try {
-        // 新增userCode字段
-        String alterTableQueryPatients = '''
-          ALTER TABLE patients
-          ADD COLUMN userCode VARCHAR(100) NOT NULL DEFAULT ''
-        ''';
-        await db.database.execute(alterTableQueryPatients);
-        String alterTableQueryDiagnosis = '''
-          ALTER TABLE diagnosis
-          ADD COLUMN userCode VARCHAR(100) NOT NULL DEFAULT ''
-        ''';
-        await db.database.execute(alterTableQueryDiagnosis);
-        return await super.performTask(version + 1);
-      } catch (e) {
-        logger.e("DatabasePatchAddUserCode performTask version:$version.", e);
-        return version;
-      }
-    } else {
-      return version;
-    }
-  }
-}

+ 38 - 0
lib/database/db_patch/base.dart

@@ -0,0 +1,38 @@
+import 'package:flutter/foundation.dart';
+import 'package:vital_local_database/index.dart';
+import 'package:vitalapp/database/db.dart';
+import 'package:vitalapp/database/db_patch/interface.dart';
+import 'package:fis_common/logger/logger.dart';
+
+abstract class BaseDatabasePatch implements IDatabasePatch {
+  final IDatabase database;
+
+  BaseDatabasePatch(this.database);
+
+  @override
+  int get updateVersion => targetVersion + 1;
+
+  @override
+  Future<int> perform(int version) async {
+    final ver = updateVersion;
+    final tag = "DatabasePatch$ver";
+    logger.i("$tag perform, current version:$version.");
+    if (targetVersion == version) {
+      // 补丁匹配当前DB版本,执行更新
+      try {
+        logger.w(" execute tasks begin.");
+        await executeTask();
+        logger.w("$tag execute tasks finish.");
+      } catch (e) {
+        logger.e("$tag execute tasks error!", e);
+      }
+      return ver;
+    } else {
+      return version;
+    }
+  }
+
+  /// 执行更新任务
+  @protected
+  Future<void> executeTask();
+}

+ 21 - 0
lib/database/db_patch/interface.dart

@@ -0,0 +1,21 @@
+import 'package:vitalapp/database/db.dart';
+
+abstract class IDatabasePatch {
+  /// 目标版本
+  ///
+  /// - 仅对匹配版本打补丁
+  int get targetVersion;
+
+  /// 升级版本
+  ///
+  /// - 补丁作用后的版本
+  int get updateVersion;
+
+  /// 运行补丁
+  ///
+  /// [version] DB当前版本
+  Future<int> perform(int version) async {
+    await db.database.setVersion(version);
+    return version;
+  }
+}

+ 0 - 8
lib/database/db_patch/interface/database_patch.dart

@@ -1,8 +0,0 @@
-import 'package:vitalapp/database/db.dart';
-
-abstract class IDatabasePatch {
-  Future<int> performTask(int version) async {
-    await db.database.setVersion(version);
-    return version;
-  }
-}

+ 30 - 0
lib/database/db_patch/v1.dart

@@ -0,0 +1,30 @@
+import 'package:vitalapp/database/db.dart';
+import 'base.dart';
+import 'package:fis_common/logger/logger.dart';
+
+/// 补丁V1
+///
+/// - 新增表patients、diagnosis中的userCode字段
+class DatabasePatchV1 extends BaseDatabasePatch {
+  DatabasePatchV1(super.database);
+
+  @override
+  int get targetVersion => 0;
+
+  @override
+  Future<void> executeTask() async {
+    // patients表新增userCode字段
+    String alterTableQueryPatients = '''
+          ALTER TABLE patients
+          ADD COLUMN userCode VARCHAR(100) NOT NULL DEFAULT ''
+        ''';
+    await db.database.execute(alterTableQueryPatients);
+
+    // diagnosis表新增userCode字段
+    String alterTableQueryDiagnosis = '''
+          ALTER TABLE diagnosis
+          ADD COLUMN userCode VARCHAR(100) NOT NULL DEFAULT ''
+        ''';
+    await db.database.execute(alterTableQueryDiagnosis);
+  }
+}

+ 21 - 0
lib/database/db_patch/v2.dart

@@ -0,0 +1,21 @@
+import 'package:vitalapp/database/db.dart';
+import 'package:vitalapp/database/entities/exam.dart';
+import 'package:vitalapp/database/entities/exam_batch.dart';
+import 'package:vitalapp/database/entities/followup.dart';
+
+import 'base.dart';
+
+class DatabasePatchV2 extends BaseDatabasePatch {
+  DatabasePatchV2(super.database);
+
+  @override
+  int get targetVersion => 1;
+
+  @override
+  Future<void> executeTask() async {
+    // 新增表
+    await db.database.execute(FollowUpEntity.TABLE_CREATE_SQL);
+    await db.database.execute(ExamEntity.TABLE_CREATE_SQL);
+    await db.database.execute(ExamBatchEntity.TABLE_CREATE_SQL);
+  }
+}

+ 53 - 0
lib/database/entities/exam.dart

@@ -0,0 +1,53 @@
+import 'package:vital_local_database/index.dart';
+
+import 'syncable.dart';
+
+/// 健康体检
+class ExamEntity extends SyncableEntity<ExamEntity> {
+  static const String _tableName = "exams";
+  static final _columns = ExamColumnsDefine();
+
+  // ignore: constant_identifier_names
+  static const TABLE_CREATE_SQL = 'CREATE TABLE IF NOT EXISTS "$_tableName" ('
+      '"id" INTEGER NOT NULL,'
+      '"code" VARCHAR(100) NOT NULL,'
+      '"userCode" VARCHAR(100) NOT NULL,'
+      '"patientCode" VARCHAR(100) NOT NULL,'
+      '"batchNumber" VARCHAR(100) NOT NULL,'
+      '"templateKey" VARCHAR(100) NOT NULL,'
+      '"dataJson" TEXT NOT NULL,'
+      '"syncType" INTEGER NOT NULL,'
+      '"syncState" INTEGER NOT NULL,'
+      '"createTime" DATETIME NOT NULL,'
+      '"updateTime" DATETIME NULL,'
+      '"isValid" INTEGER NOT NULL,'
+      'PRIMARY KEY ("id")'
+      ');';
+
+  @override
+  IDbColumnsDefine<IDbEntity> get columns => _columns;
+
+  @override
+  String get tableName => _tableName;
+
+  /// 居民档案编码
+  String patientCode = '';
+
+  /// 批次号
+  String batchNumber = '';
+
+  /// 模板Key
+  String templateKey = '';
+}
+
+class ExamColumnsDefine extends SyncableColumnsDefine<ExamEntity>
+    implements IDbColumnsDefine<ExamEntity> {
+  /// 居民档案编码
+  final patientCode = DbColumn<String>("patientCode");
+
+  /// 批次号
+  final batchNumber = DbColumn<String>("batchNumber");
+
+  /// 模板Key
+  final templateKey = DbColumn<String>("templateKey");
+}

+ 45 - 0
lib/database/entities/exam_batch.dart

@@ -0,0 +1,45 @@
+import 'package:vital_local_database/index.dart';
+
+import 'syncable.dart';
+
+/// 健康体检批次记录
+class ExamBatchEntity extends SyncableEntity<ExamBatchEntity> {
+  static const String _tableName = "examBatchs";
+  static final _columns = ExamBatchColumnsDefine();
+
+  // ignore: constant_identifier_names
+  static const TABLE_CREATE_SQL = 'CREATE TABLE IF NOT EXISTS "$_tableName" ('
+      '"id" INTEGER NOT NULL,'
+      '"code" VARCHAR(100) NOT NULL,'
+      '"userCode" VARCHAR(100) NOT NULL,'
+      '"patientCode" VARCHAR(100) NOT NULL,'
+      '"batchNumber" VARCHAR(100) NOT NULL,'
+      '"syncType" INTEGER NOT NULL,'
+      '"syncState" INTEGER NOT NULL,'
+      '"createTime" DATETIME NOT NULL,'
+      '"updateTime" DATETIME NULL,'
+      '"isValid" INTEGER NOT NULL,'
+      'PRIMARY KEY ("id")'
+      ');';
+
+  @override
+  IDbColumnsDefine<IDbEntity> get columns => _columns;
+
+  @override
+  String get tableName => _tableName;
+
+  /// 居民档案编码
+  String patientCode = '';
+
+  /// 批次号
+  String batchNumber = '';
+}
+
+class ExamBatchColumnsDefine extends SyncableColumnsDefine<ExamBatchEntity>
+    implements IDbColumnsDefine<ExamBatchEntity> {
+  /// 居民档案编码
+  final patientCode = DbColumn<String>("patientCode");
+
+  /// 批次号
+  final batchNumber = DbColumn<String>("batchNumber");
+}

+ 88 - 0
lib/database/entities/followup.dart

@@ -0,0 +1,88 @@
+import 'package:fis_jsonrpc/services/followUp.m.dart';
+import 'package:vital_local_database/index.dart';
+
+import 'syncable.dart';
+
+/// 随访
+class FollowUpEntity extends SyncableEntity<FollowUpEntity> {
+  static const String _tableName = "followup";
+  static final _columns = FollowUpColumnsDefine();
+
+  // ignore: constant_identifier_names
+  static const TABLE_CREATE_SQL = 'CREATE TABLE IF NOT EXISTS "$_tableName" ('
+      '"id" INTEGER NOT NULL,'
+      '"code" VARCHAR(100) NOT NULL,'
+      '"userCode" VARCHAR(100) NOT NULL,'
+      '"patientCode" VARCHAR(100) NOT NULL,'
+      '"typeKey" VARCHAR(100) NOT NULL,'
+      '"templateCode" VARCHAR(100) NOT NULL,'
+      '"dataJson" TEXT NOT NULL,'
+      '"mode" INTEGER NOT NULL,'
+      '"followUpTime" DATETIME NOT NULL,'
+      '"syncType" INTEGER NOT NULL,'
+      '"syncState" INTEGER NOT NULL,'
+      '"createTime" DATETIME NOT NULL,'
+      '"updateTime" DATETIME NULL,'
+      '"isValid" INTEGER NOT NULL,'
+      'PRIMARY KEY ("id")'
+      ');';
+
+  @override
+  IDbColumnsDefine<IDbEntity> get columns => _columns;
+
+  @override
+  String get tableName => _tableName;
+
+  /// 居民档案编码
+  String patientCode = '';
+
+  /// 分类Key
+  String typeKey = '';
+
+  /// 模板Code
+  String templateCode = '';
+
+  /// 随访方式
+  FollowUpModeEnum mode = FollowUpModeEnum.Outpatient;
+
+  /// 随访时间
+  DateTime followUpTime = DateTime(1900, 1, 1);
+
+  @override
+  Map<String, dynamic> toJson() {
+    final map = super.toJson();
+    map['patientCode'] = patientCode;
+    map['templateCode'] = templateCode;
+    map['mode'] = mode.index;
+    map['followUpTime'] = f2d_DateTime(followUpTime);
+    return map;
+  }
+
+  @override
+  FollowUpEntity fromJson(Map<String, dynamic> map) {
+    super.fromJson(map);
+    patientCode = map['patientCode'];
+    templateCode = map['templateCode'];
+    mode = FollowUpModeEnum.values[map["mode"]];
+    followUpTime = d2f_DateTime(map['followUpTime']);
+    return this;
+  }
+}
+
+class FollowUpColumnsDefine extends SyncableColumnsDefine<FollowUpEntity>
+    implements IDbColumnsDefine<FollowUpEntity> {
+  /// 居民档案编码
+  final patientCode = DbColumn<String>("patientCode");
+
+  /// 分类Key
+  final typeKey = DbColumn<String>("typeKey");
+
+  /// 模板Code
+  final templateCode = DbColumn<String>("templateCode");
+
+  /// 随访方式
+  final mode = DbColumn<String>("mode");
+
+  /// 随访时间
+  final followUpTime = DbColumn<String>("followUpTime");
+}

+ 0 - 0
lib/database/repositories/exam.dart


+ 29 - 0
lib/database/repositories/exam_batch.dart

@@ -0,0 +1,29 @@
+import 'package:vital_local_database/index.dart';
+import 'package:vitalapp/database/entities/exam_batch.dart';
+
+import 'interfaces/exam_batch.dart';
+
+class ExamBatchRepository
+    extends BaseDbRepository<ExamBatchEntity, ExamBatchColumnsDefine>
+    implements IExamBatchRepository {
+  ExamBatchRepository(super.database);
+
+  @override
+  ExamBatchEntity createEntityInstance() => ExamBatchEntity();
+
+  @override
+  Future<List<ExamBatchEntity>> queryPatientAllList(
+      String patientCode, String userCode) async {
+    final query = queryable.where(
+      (x) => [
+        x.isValid.equals(true),
+        x.userCode.equals(userCode),
+        x.patientCode.equals(patientCode),
+      ],
+    );
+    final list = await query
+        .orderBy((x) => x.createTime, DbOrderByType.desc) // 按创建时间倒序
+        .toList();
+    return list;
+  }
+}

+ 70 - 0
lib/database/repositories/followup.dart

@@ -0,0 +1,70 @@
+import 'package:vital_local_database/index.dart';
+import 'package:vitalapp/database/entities/followup.dart';
+
+import 'interfaces/followup.dart';
+
+class FollowUpRepository
+    extends BaseDbRepository<FollowUpEntity, FollowUpColumnsDefine>
+    implements IFollowUpRepository {
+  FollowUpRepository(super.database);
+
+  @override
+  FollowUpEntity createEntityInstance() => FollowUpEntity();
+
+  @override
+  Future<FollowUpEntity?> singleByCode(String code) async {
+    final entity = await queryable
+        .where((x) => [
+              x.isValid.equals(true),
+              x.code.equals(code),
+            ])
+        .first();
+    return entity;
+  }
+
+  @override
+  Future<List<FollowUpEntity>> queryPatientAllList(
+      String patientCode, String userCode) async {
+    final query = queryable.where(
+      (x) => [
+        x.isValid.equals(true),
+        x.userCode.equals(userCode),
+        x.patientCode.equals(patientCode),
+      ],
+    );
+    final list = await query
+        .orderBy((x) => x.followUpTime, DbOrderByType.desc) // 按随访时间倒序
+        .toList();
+    return list;
+  }
+
+  @override
+  Future<DbPagedList<FollowUpEntity>> queryPatientPagedList(
+    String patientCode,
+    String userCode,
+    int pageIndex, {
+    int pageSize = 50,
+  }) async {
+    final offset = pageSize * (pageIndex - 1);
+    var query = queryable.where(
+      (x) => [
+        x.isValid.equals(true),
+        x.userCode.equals(userCode),
+        x.patientCode.equals(patientCode),
+      ],
+    );
+    final count = await query.count();
+    final list = await query
+        .orderBy((x) => x.followUpTime, DbOrderByType.desc) // 按随访时间倒序
+        .offset(offset)
+        .limit(pageSize)
+        .toList();
+    final result = DbPagedList<FollowUpEntity>(
+      data: list,
+      totalCount: count,
+      pageIndex: pageIndex,
+      pageSize: pageSize,
+    );
+    return result;
+  }
+}

+ 31 - 0
lib/database/repositories/interfaces/exam.dart

@@ -0,0 +1,31 @@
+import 'package:vital_local_database/index.dart';
+import 'package:vitalapp/database/entities/exam.dart';
+import 'package:vitalapp/database/entities/followup.dart';
+
+abstract class IExamRepository
+    implements IDbRepositroy<ExamEntity, ExamColumnsDefine> {
+  /// 根据code获取实体
+  ///
+  /// [code] 检查记录code
+  Future<ExamEntity?> singleByCode(String code);
+
+  /// 查询居民所有随访列表
+  ///
+  /// [patientCode] 居民Code
+  ///
+  /// [userCode] 医生Code
+  Future<List<FollowUpEntity>> queryPatientAllList(
+      String patientCode, String userCode);
+
+  /// 查询居民下的随访分页列表
+  ///
+  /// [patientCode] 居民Code
+  ///
+  /// [userCode] 医生Code
+  Future<DbPagedList<FollowUpEntity>> queryPatientPagedList(
+    String patientCode,
+    String userCode,
+    int pageIndex, {
+    int pageSize = 50,
+  });
+}

+ 13 - 0
lib/database/repositories/interfaces/exam_batch.dart

@@ -0,0 +1,13 @@
+import 'package:vital_local_database/index.dart';
+import 'package:vitalapp/database/entities/exam_batch.dart';
+
+abstract class IExamBatchRepository
+    implements IDbRepositroy<ExamBatchEntity, ExamBatchColumnsDefine> {
+  /// 查询居民所有体检批次列表
+  ///
+  /// [patientCode] 居民Code
+  ///
+  /// [userCode] 医生Code
+  Future<List<ExamBatchEntity>> queryPatientAllList(
+      String patientCode, String userCode);
+}

+ 30 - 0
lib/database/repositories/interfaces/followup.dart

@@ -0,0 +1,30 @@
+import 'package:vital_local_database/index.dart';
+import 'package:vitalapp/database/entities/followup.dart';
+
+abstract class IFollowUpRepository
+    implements IDbRepositroy<FollowUpEntity, FollowUpColumnsDefine> {
+  /// 根据code获取实体
+  ///
+  /// [code] 随访记录code
+  Future<FollowUpEntity?> singleByCode(String code);
+
+  /// 查询居民所有随访列表
+  ///
+  /// [patientCode] 居民Code
+  ///
+  /// [userCode] 医生Code
+  Future<List<FollowUpEntity>> queryPatientAllList(
+      String patientCode, String userCode);
+
+  /// 查询居民下的随访分页列表
+  ///
+  /// [patientCode] 居民Code
+  ///
+  /// [userCode] 医生Code
+  Future<DbPagedList<FollowUpEntity>> queryPatientPagedList(
+    String patientCode,
+    String userCode,
+    int pageIndex, {
+    int pageSize = 50,
+  });
+}

+ 8 - 0
lib/mock/mock.dart

@@ -1,5 +1,7 @@
 import 'package:fis_jsonrpc/rpc.dart';
 import 'package:vitalapp/mock/services/diagnosis.dart';
+import 'package:vitalapp/mock/services/exam.dart';
+import 'package:vitalapp/mock/services/followup.dart';
 import 'package:vitalapp/mock/services/label.dart';
 import 'package:vitalapp/mock/services/patient.dart';
 
@@ -15,4 +17,10 @@ class JsonRpcMockProxy extends JsonRpcProxy {
   @override
   VitalDiagnosisService get diagnosis =>
       DiagnosisServiceMock(currentHostAddress);
+
+  @override
+  VitalFollowUpService get followUp => FollowUpServiceMock(currentHostAddress);
+
+  @override
+  VitalExamService get exam => ExamServiceMock(currentHostAddress);
 }

+ 32 - 0
lib/mock/services/exam.dart

@@ -0,0 +1,32 @@
+import 'package:fis_jsonrpc/services/exam.dart';
+import 'package:fis_jsonrpc/services/exam.m.dart';
+
+class ExamServiceMock extends VitalExamService {
+  ExamServiceMock(super.host);
+
+  @override
+  Future<String> createExamAsync(CreateExamRequest request) {
+    // TODO: implement createExamAsync
+    return super.createExamAsync(request);
+  }
+
+  @override
+  Future<bool> updateExamAsync(UpdateExamRequest request) {
+    // TODO: implement updateExamAsync
+    return super.updateExamAsync(request);
+  }
+
+  @override
+  Future<List<ExamRecordDTO>> getExamRecordListAsync(
+      GetExamRecordListRequest request) {
+    // TODO: implement getExamRecordListAsync
+    return super.getExamRecordListAsync(request);
+  }
+
+  @override
+  Future<bool> updateExamByBatchNumberAsync(
+      UpdateExamByBatchNumberRequest request) {
+    // TODO: implement updateExamByBatchNumberAsync
+    return super.updateExamByBatchNumberAsync(request);
+  }
+}

+ 85 - 0
lib/mock/services/followup.dart

@@ -0,0 +1,85 @@
+import 'dart:convert';
+
+import 'package:fis_jsonrpc/services/analyzeConfig.m.dart';
+import 'package:fis_jsonrpc/services/followUp.dart';
+import 'package:fis_jsonrpc/services/followUp.m.dart';
+import 'package:uuid/uuid.dart';
+import 'package:vitalapp/database/db.dart';
+import 'package:vitalapp/database/entities/defines.dart';
+import 'package:vitalapp/database/entities/followup.dart';
+import 'package:vitalapp/store/store.dart';
+
+class FollowUpServiceMock extends VitalFollowUpService {
+  FollowUpServiceMock(super.host);
+
+  @override
+  Future<String> createFollowUpAsync(CreateFollowUpRequest request) async {
+    final entity = FollowUpEntity();
+    entity.code = const Uuid().v4().replaceAll('-', ''); // 本地先生成一个Code
+    entity.isValid = true;
+    entity.syncType = OfflineDataSyncType.create;
+    entity.userCode = Store.user.userCode!;
+    entity.patientCode = Store.user.currentSelectPatientInfo!.code!;
+    entity.typeKey = request.key!;
+    entity.templateCode = request.templateCode!;
+    entity.mode = request.followUpMode;
+    entity.followUpTime = request.followUpTime!;
+    entity.dataJson = jsonEncode(request); // 完整缓存请求
+
+    final id = await db.repositories.followUp.insert(entity);
+    final result = id > 0 ? entity.code : "";
+    return result;
+  }
+
+  @override
+  Future<bool> updateFollowUpAsync(UpdateFollowUpRequest request) async {
+    FollowUpEntity? entity =
+        await db.repositories.followUp.singleByCode(request.code!);
+    if (entity == null) {
+      entity = FollowUpEntity();
+      entity.isValid = true;
+      entity.syncType = OfflineDataSyncType.update;
+      entity.dataJson = jsonEncode(request);
+    } else {
+      if (entity.syncType == OfflineDataSyncType.create) {
+        final jsonMap = jsonDecode(entity.dataJson);
+        // 创建类型里含有templateCode,不能丢了
+        final createReq = CreateFollowUpRequest.fromJson(jsonMap);
+        createReq.followUpMode = request.followUpMode!;
+        createReq.followUpTime = request.followUpTime;
+        createReq.nextFollowUpTime = request.nextFollowUpTime;
+        createReq.followUpPhotos = request.followUpPhotos;
+        createReq.followUpData = request.followUpData;
+      } else {
+        entity.dataJson = jsonEncode(request);
+      }
+    }
+
+    entity.code = request.code!;
+    entity.userCode = Store.user.userCode!;
+    entity.patientCode = Store.user.currentSelectPatientInfo!.code!;
+    entity.typeKey = request.key!;
+    entity.followUpTime = request.followUpTime!;
+
+    int result;
+    if (entity.id == 0) {
+      result = await db.repositories.followUp.insert(entity);
+    } else {
+      result = await db.repositories.followUp.update(entity);
+    }
+    return result > 0;
+  }
+
+  @override
+  Future<List<FollowUpRecordDTO>> getFollowUpRecordListAsync(
+      GetFollowUpRecordListRequest request) async {
+    final entities = await db.repositories.followUp
+        .queryPatientAllList(request.patientCode!, Store.user.userCode!);
+    final list = entities.map((e) {
+      final dto = FollowUpRecordDTO();
+// dto.birthday
+      return dto;
+    }).toList();
+    return list;
+  }
+}