Forráskód Böngészése

[new]增加任务轮询功能

zk 1 éve
szülő
commit
9fc297e502

+ 12 - 0
lib/data/api/atmob_api.dart

@@ -10,6 +10,7 @@ import 'package:electronic_assistant/data/api/request/login_request.dart';
 import 'package:electronic_assistant/data/api/request/talk_create_request.dart';
 import 'package:electronic_assistant/data/api/request/talk_delete_request.dart';
 import 'package:electronic_assistant/data/api/request/talk_generate_request.dart';
+import 'package:electronic_assistant/data/api/request/talk_query_request.dart';
 import 'package:electronic_assistant/data/api/request/talk_rename_request.dart';
 import 'package:electronic_assistant/data/api/request/talk_request.dart';
 import 'package:electronic_assistant/data/api/request/user_info_update_request.dart';
@@ -22,8 +23,10 @@ import 'package:electronic_assistant/data/api/response/home_info_response.dart';
 import 'package:electronic_assistant/data/api/response/login_response.dart';
 import 'package:electronic_assistant/data/api/response/talk_check_electric_response.dart';
 import 'package:electronic_assistant/data/api/response/talk_info_response.dart';
+import 'package:electronic_assistant/data/api/response/talk_query_response.dart';
 import 'package:electronic_assistant/data/api/response/tasks_running_response.dart';
 import 'package:electronic_assistant/data/api/response/talk_original_response.dart';
+import 'package:electronic_assistant/data/api/response/user_info_response.dart';
 import 'package:electronic_assistant/data/bean/talks.dart';
 import 'package:electronic_assistant/data/consts/constants.dart';
 import 'package:retrofit/http.dart';
@@ -47,6 +50,11 @@ abstract class AtmobApi {
   @POST("/project/secretary/v1/user/info/update")
   Future<BaseResponse> updateUserInfo(@Body() UserInfoUpdateRequest request);
 
+  /// 用户信息
+  @POST("/project/secretary/v1/user/info")
+  Future<BaseResponse<UserInfoResponse>> userInfo(
+      @Body() AppBaseRequest request);
+
   /// 首页信息
   @POST("/project/secretary/v1/home/info")
   Future<BaseResponse<HomeInfoResponse>> homeInfo(
@@ -99,6 +107,10 @@ abstract class AtmobApi {
   @POST("/project/secretary/v1/talk/tasks/running")
   Future<BaseResponse<TasksRunningResponse>> tasksRunning(
       @Body() AppBaseRequest request);
+
+  @POST("/project/secretary/v1/talk/query")
+  Future<BaseResponse<TalkQueryResponse>> talkQuery(
+      @Body() TalkQueryRequest request);
 }
 
 final atmobApi = AtmobApi(defaultDio, baseUrl: Constants.baseUrl);

+ 15 - 0
lib/data/api/request/talk_query_request.dart

@@ -0,0 +1,15 @@
+import 'package:electronic_assistant/base/app_base_request.dart';
+import 'package:json_annotation/json_annotation.dart';
+
+part 'talk_query_request.g.dart';
+
+@JsonSerializable()
+class TalkQueryRequest extends AppBaseRequest {
+  @JsonKey(name: 'taskId')
+  String taskId;
+
+  TalkQueryRequest(this.taskId);
+
+  @override
+  Map<String, dynamic> toJson() => _$TalkQueryRequestToJson(this);
+}

+ 15 - 0
lib/data/api/response/talk_query_response.dart

@@ -0,0 +1,15 @@
+import 'package:electronic_assistant/data/bean/talks.dart';
+import 'package:json_annotation/json_annotation.dart';
+
+part 'talk_query_response.g.dart';
+
+@JsonSerializable()
+class TalkQueryResponse {
+  @JsonKey(name: 'talks')
+  List<TalkBean>? talks;
+
+  TalkQueryResponse({this.talks});
+
+  factory TalkQueryResponse.fromJson(Map<String, dynamic> json) =>
+      _$TalkQueryResponseFromJson(json);
+}

+ 29 - 0
lib/data/api/response/user_info_response.dart

@@ -0,0 +1,29 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/member_info.dart';
+
+part 'user_info_response.g.dart';
+
+@JsonSerializable()
+class UserInfoResponse {
+  @JsonKey(name: 'ssid')
+  String ssid;
+
+  @JsonKey(name: 'loginStatus')
+  int? loginStatus;
+
+  @JsonKey(name: 'deviceId')
+  String? deviceId;
+
+  @JsonKey(name: 'phone')
+  String? phone;
+
+  @JsonKey(name: 'memberInfo')
+  MemberInfo? memberInfo;
+
+  UserInfoResponse(
+      this.ssid, this.loginStatus, this.deviceId, this.phone, this.memberInfo);
+
+  factory UserInfoResponse.fromJson(Map<String, dynamic> json) =>
+      _$UserInfoResponseFromJson(json);
+}

+ 14 - 0
lib/data/bean/member_info.dart

@@ -0,0 +1,14 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'member_info.g.dart';
+
+@JsonSerializable()
+class MemberInfo {
+  @JsonKey(name: 'electric')
+  int electric;
+
+  MemberInfo(this.electric);
+
+  factory MemberInfo.fromJson(Map<String, dynamic> json) =>
+      _$MemberInfoFromJson(json);
+}

+ 5 - 0
lib/data/consts/error_code.dart

@@ -0,0 +1,5 @@
+abstract class ErrorCode {
+  ErrorCode._();
+
+  static const int ERROR_CODE_NO_LOGIN = 1006;
+}

+ 44 - 5
lib/data/repositories/account_repository.dart

@@ -1,6 +1,11 @@
 import 'dart:async';
 
+import 'package:electronic_assistant/base/app_base_request.dart';
 import 'package:electronic_assistant/data/api/atmob_api.dart';
+import 'package:electronic_assistant/data/bean/member_info.dart';
+import 'package:electronic_assistant/data/repositories/task_repository.dart';
+import 'package:electronic_assistant/utils/async_util.dart';
+import 'package:electronic_assistant/utils/cancel_future.dart';
 import 'package:electronic_assistant/utils/event_bus.dart';
 import 'package:electronic_assistant/utils/mmkv_util.dart';
 import 'package:flutter/cupertino.dart';
@@ -11,6 +16,8 @@ import '../../utils/http_handler.dart';
 import '../api/request/login_request.dart';
 import '../api/request/verification_code_request.dart';
 import '../api/response/login_response.dart';
+import '../api/response/user_info_response.dart';
+import '../consts/error_code.dart';
 
 const String EventUserLogin = 'EventUserLogin';
 const String EventUserLogout = 'EventUserLogout';
@@ -22,6 +29,9 @@ class AccountRepository {
   String? _token;
   String? _phone;
   final isLogin = false.obs;
+  Rxn<MemberInfo> memberInfo = Rxn();
+
+  CancelableFuture? _getUserInfoFuture;
 
   AccountRepository._() {
     debugPrint('AccountRepository init');
@@ -29,6 +39,10 @@ class AccountRepository {
     _phone = KVUtil.getString(ACCOUNT_PHONE, null);
     if (_token != null && _token!.isNotEmpty) {
       isLogin.value = true;
+      Future.delayed(Duration.zero, () {
+        refreshUserInfo();
+        taskRepository.startUnfinishedTask();
+      });
     }
   }
 
@@ -42,23 +56,48 @@ class AccountRepository {
         .then(HttpHandler.handle(true));
   }
 
+  Future<void>? refreshUserInfo() {
+    if (_getUserInfoFuture != null) {
+      _getUserInfoFuture?.cancel();
+    }
+    _getUserInfoFuture = AsyncUtil.retryWithExponentialBackoff(
+        () => _getUserInfo(), 10, (error) {
+      if (error is ServerErrorException) {
+        return error.code != ErrorCode.ERROR_CODE_NO_LOGIN;
+      }
+      return true;
+    });
+    return _getUserInfoFuture;
+  }
+
+  Future<UserInfoResponse> _getUserInfo() {
+    return atmobApi
+        .userInfo(AppBaseRequest())
+        .then(HttpHandler.handle(false))
+        .then((response) {
+      memberInfo.value = response.memberInfo;
+      return response;
+    });
+  }
+
   Future<LoginResponse> login(String phone, String code) {
     return atmobApi
         .login(LoginRequest(phone, code))
         .then(HttpHandler.handle(false))
         .then((response) {
       onLoginSuccess(phone, response.authToken);
-      refreshUserInfo();
       return response;
     });
   }
 
   void onLoginSuccess(String phone, String? token) {
-    this._token = token;
-    this._phone = phone;
+    _token = token;
+    _phone = phone;
     KVUtil.putString(ACCOUNT_TOKEN, token);
     KVUtil.putString(ACCOUNT_PHONE, phone);
     isLogin.value = true;
+    refreshUserInfo();
+    taskRepository.startUnfinishedTask();
 
     eventBus.emit(EventUserLogin);
   }
@@ -69,6 +108,8 @@ class AccountRepository {
     KVUtil.putString(ACCOUNT_TOKEN, null);
     KVUtil.putString(ACCOUNT_PHONE, null);
     isLogin.value = false;
+    taskRepository.stopTask();
+    memberInfo.value = null;
 
     eventBus.emit(EventUserLogout);
   }
@@ -81,8 +122,6 @@ class AccountRepository {
     isLogin.value = false;
   }
 
-  void refreshUserInfo() {}
-
   getUserSubName(String? phone) {
     String name = StringName.account.tr;
     if (phone == null) {

+ 89 - 3
lib/data/repositories/task_repository.dart

@@ -1,17 +1,103 @@
 import 'package:electronic_assistant/base/app_base_request.dart';
 import 'package:electronic_assistant/data/api/atmob_api.dart';
-
+import 'package:electronic_assistant/data/api/response/talk_query_response.dart';
+import 'package:electronic_assistant/data/bean/talks.dart';
+import 'package:electronic_assistant/utils/async_util.dart';
+import 'package:electronic_assistant/utils/cancel_future.dart';
+import 'package:flutter/cupertino.dart';
 import '../../utils/http_handler.dart';
+import '../api/request/talk_query_request.dart';
 import '../api/response/tasks_running_response.dart';
+import '../consts/error_code.dart';
 
 class TaskRepository {
+  final Set<String> electronicTasks = {};
+  final Set<String> _recordSuccessTalkIds = {};
+  CancelableFuture? _taskStatusCheckFuture;
+
   TaskRepository._();
 
+  void stopTask() {
+    _taskStatusCheckFuture?.cancel();
+  }
+
+  void addTask(String taskId) {
+    electronicTasks.add(taskId);
+    _startTaskStatusCheck();
+  }
+
   void startUnfinishedTask() {
-    tasksRunning();
+    debugPrint('开始获取未完成任务==');
+    AsyncUtil.retryWithExponentialBackoff(() => _tasksRunning(), 10, (error) {
+      if (error is ServerErrorException) {
+        return error.code != ErrorCode.ERROR_CODE_NO_LOGIN;
+      }
+      return true;
+    }).then((data) {
+      List<String>? list = data.taskIds;
+      debugPrint('获取未完成任务成功-${list?.length}');
+      electronicTasks.clear();
+      if (list != null) {
+        electronicTasks.addAll(list);
+      }
+      _startTaskStatusCheck();
+    }).catchError((error) {
+      debugPrint('获取未完成任务失败');
+    });
+  }
+
+  void _startTaskStatusCheck() {
+    if (electronicTasks.isEmpty) {
+      return;
+    }
+    debugPrint('开始检查任务状态');
+    if (_taskStatusCheckFuture != null) {
+      _taskStatusCheckFuture?.cancel();
+    }
+    _taskStatusCheckFuture = AsyncUtil.retryWhen(
+        () => _checkAllTaskStatusFinished(), 0, const Duration(seconds: 3),
+        (error) {
+      if (error is ServerErrorException) {
+        return error.code != ErrorCode.ERROR_CODE_NO_LOGIN;
+      }
+      return true;
+    });
+  }
+
+  Future<void> _checkAllTaskStatusFinished() {
+    String taskIds = electronicTasks.join(',');
+    return atmobApi
+        .talkQuery(TalkQueryRequest(taskIds))
+        .then(HttpHandler.handle(false))
+        .then((response) => _dealTaskStatus(response));
+  }
+
+  Future<void> _dealTaskStatus(TalkQueryResponse response) {
+    response.talks?.forEach((element) {
+      if (element.status == TalkStatus.analysing ||
+          element.status == TalkStatus.waitAnalysis) {
+        //TODO 刷新生成中的bean类数据
+      } else if (element.status == TalkStatus.analysisSuccess ||
+          element.status == TalkStatus.analysisFail) {
+        electronicTasks.remove(element.taskId);
+        if (!_recordSuccessTalkIds.contains(element.id)) {
+          _recordSuccessTalkIds.add(element.id);
+          _notifyGenerationStatus(element);
+        }
+      }
+    });
+    return Future.value();
+  }
+
+  void _notifyGenerationStatus(TalkBean element) {
+    if (element.status == TalkStatus.analysisSuccess) {
+      debugPrint('生成成功-${element.id}');
+    } else if (element.status == TalkStatus.analysisFail) {
+      debugPrint('生成失败-${element.id}');
+    }
   }
 
-  Future<TasksRunningResponse> tasksRunning() {
+  Future<TasksRunningResponse> _tasksRunning() {
     return atmobApi
         .tasksRunning(AppBaseRequest())
         .then(HttpHandler.handle(true));

+ 34 - 35
lib/utils/async_util.dart

@@ -9,15 +9,12 @@ class AsyncUtil {
   AsyncUtil._();
 
   static CancelableFuture<T> retryWithExponentialBackoff<T>(
-      FutureCallback<T> callback,
-      int maxRetry,
-      Duration initialInterval,
-      Predicate<dynamic> predicate) {
-    Completer<T> completer = Completer<T>();
+      FutureCallback<T> callback, int maxRetry, Predicate<dynamic> predicate) {
+    const Duration initialInterval = Duration(seconds: 1);
     int retryCount = 0;
     Timer? timer;
 
-    void attempt() {
+    void attempt(Completer<T> completer) {
       callback().then((value) {
         if (!completer.isCompleted) {
           completer.complete(value);
@@ -26,7 +23,7 @@ class AsyncUtil {
         if (retryCount < maxRetry && predicate(error)) {
           retryCount++;
           Duration nextInterval = initialInterval * (1 << (retryCount - 1));
-          timer = Timer(nextInterval, attempt);
+          timer = Timer(nextInterval, () => attempt(completer));
         } else {
           if (!completer.isCompleted) {
             completer.completeError(error);
@@ -35,28 +32,27 @@ class AsyncUtil {
       });
     }
 
-    attempt();
-
-    return CancelableFuture<T>(() {
+    return CancelableFuture<T>((completer) {
+      attempt(completer);
+    }, () {
       timer?.cancel();
     });
   }
 
   static CancelableFuture<T> retryWhen<T>(FutureCallback<T> callback,
       int maxRetry, Duration interval, Predicate<dynamic> predicate) {
-    Completer<T> completer = Completer<T>();
     int retryCount = 0;
     Timer? timer;
 
-    void attempt() {
+    void attempt(Completer<T> completer) {
       callback().then((value) {
         if (!completer.isCompleted) {
           completer.complete(value);
         }
       }).catchError((error) {
-        if (retryCount < maxRetry && predicate(error)) {
+        if ((maxRetry <= 0 || retryCount < maxRetry) && predicate(error)) {
           retryCount++;
-          timer = Timer(interval, attempt);
+          timer = Timer(interval, () => attempt(completer));
         } else {
           if (!completer.isCompleted) {
             completer.completeError(error);
@@ -65,9 +61,9 @@ class AsyncUtil {
       });
     }
 
-    attempt();
-
-    return CancelableFuture<T>(() {
+    return CancelableFuture<T>((completer) {
+      attempt(completer);
+    }, () {
       timer?.cancel();
     });
   }
@@ -80,18 +76,22 @@ class AsyncUtil {
   static CancelableFuture<T> delay<T>(
       FutureCallback<T> callback, Duration interval) {
     Timer? timer;
-    return CancelableFuture<T>(
-      () {
-        timer?.cancel();
-      },
-      futureCompleter: (completer) {
-        timer = Timer(interval, () {
-          callback()
-              .then(completer.complete)
-              .catchError(completer.completeError);
+
+    return CancelableFuture<T>((completer) {
+      timer = Timer(interval, () {
+        callback().then((value) {
+          if (!completer.isCompleted) {
+            completer.complete(value);
+          }
+        }).catchError((error) {
+          if (!completer.isCompleted) {
+            completer.completeError(error);
+          }
         });
-      },
-    );
+      });
+    }, () {
+      timer?.cancel();
+    });
   }
 
   ///实际delay第一次执行的时间为delay+interval
@@ -100,9 +100,8 @@ class AsyncUtil {
     Timer? timer;
     Timer? delayTimer;
     int counter = 0;
-    Completer<T> completer = Completer<T>();
 
-    void tick() {
+    void tick(Completer<T> completer) {
       if (counter < times) {
         callback().then((value) {
           if (!completer.isCompleted) {
@@ -119,11 +118,11 @@ class AsyncUtil {
       }
     }
 
-    delayTimer = Timer(delay, () {
-      timer = Timer.periodic(interval, (Timer t) => tick());
-    });
-
-    return CancelableFuture<T>(() {
+    return CancelableFuture<T>((completer) {
+      delayTimer = Timer(delay, () {
+        timer = Timer.periodic(interval, (Timer t) => tick(completer));
+      });
+    }, () {
       delayTimer?.cancel();
       timer?.cancel();
     });

+ 4 - 6
lib/utils/cancel_future.dart

@@ -6,8 +6,8 @@ class CancelableFuture<T> implements Future<T> {
 
   Cancelable? cancelable;
 
-  CancelableFuture(this.cancelable, {FutureCompleter? futureCompleter}) {
-    futureCompleter?.call(_completer);
+  CancelableFuture(FutureCompleter<T> completer, this.cancelable) {
+    completer.call(_completer);
   }
 
   void cancel() {
@@ -37,9 +37,7 @@ class CancelableFuture<T> implements Future<T> {
 
 extension CancelableFutureExtension<T> on Future<T> {
   CancelableFuture<T> asCancelable(
-    Cancelable cancelable, {
-    FutureCompleter? futureCompleter,
-  }) {
-    return CancelableFuture(cancelable, futureCompleter: futureCompleter);
+      FutureCompleter completer, Cancelable? cancelable) {
+    return CancelableFuture(completer, cancelable);
   }
 }

+ 0 - 1
lib/utils/error_handler.dart

@@ -23,4 +23,3 @@ class ErrorHandler {
   }
 }
 
-abstract class ErrorCode {}