Jelajahi Sumber

[feat]亲密度分析,截图回复-识图回复,对接识图回复接口

hezihao 7 bulan lalu
induk
melakukan
1f82dfe935

+ 6 - 0
assets/string/base/string.xml

@@ -316,6 +316,12 @@
     <string name="for_ta">对于TA</string>
     <string name="for_me">对于我</string>
     <string name="choose_reply_tone">选择回复语气</string>
+    <string name="no_choose_reply_tone_tip">请选择回复语气</string>
+
+    <string name="no_choose_mode_tip">请选择模式</string>
+    <string name="no_upload_screenshot_tip">请上传截图</string>
+
+    <string name="no_upload_conversation_image_tip">请上传聊天记录图片</string>
 
     <string name="preview">预览</string>
 

+ 2 - 1
lib/data/api/atmob_api.dart

@@ -43,6 +43,7 @@ import 'package:keyboard/data/api/response/intimacy_analyze_chat_config_response
 import 'package:keyboard/data/api/response/intimacy_analyze_config_response.dart';
 import 'package:keyboard/data/api/response/intimacy_analyze_reply_config_response.dart';
 import 'package:keyboard/data/api/response/intimacy_analyze_response.dart';
+import 'package:keyboard/data/api/response/intimacy_reply_analyze_response.dart';
 import 'package:keyboard/data/api/response/item_list_response.dart';
 import 'package:keyboard/data/api/response/item_retention_response.dart';
 import 'package:keyboard/data/api/response/keyboard_character_list_response.dart';
@@ -284,7 +285,7 @@ abstract class AtmobApi {
 
   /// 识图回复
   @POST("/project/keyboard/v1/intimacy/reply/analyze")
-  Future<BaseResponse> intimacyReplyAnalyze(
+  Future<BaseResponse<IntimacyReplyAnalyzeResponse>> intimacyReplyAnalyze(
     @Body() IntimacyReplyAnalyzeRequest request,
   );
 

+ 6 - 5
lib/data/api/atmob_api.g.dart

@@ -1312,7 +1312,7 @@ class _AtmobApi implements AtmobApi {
   }
 
   @override
-  Future<BaseResponse<dynamic>> intimacyReplyAnalyze(
+  Future<BaseResponse<IntimacyReplyAnalyzeResponse>> intimacyReplyAnalyze(
     IntimacyReplyAnalyzeRequest request,
   ) async {
     final _extra = <String, dynamic>{};
@@ -1320,7 +1320,7 @@ class _AtmobApi implements AtmobApi {
     final _headers = <String, dynamic>{};
     final _data = <String, dynamic>{};
     _data.addAll(request.toJson());
-    final _options = _setStreamType<BaseResponse<dynamic>>(
+    final _options = _setStreamType<BaseResponse<IntimacyReplyAnalyzeResponse>>(
       Options(method: 'POST', headers: _headers, extra: _extra)
           .compose(
             _dio.options,
@@ -1331,11 +1331,12 @@ class _AtmobApi implements AtmobApi {
           .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
     );
     final _result = await _dio.fetch<Map<String, dynamic>>(_options);
-    late BaseResponse<dynamic> _value;
+    late BaseResponse<IntimacyReplyAnalyzeResponse> _value;
     try {
-      _value = BaseResponse<dynamic>.fromJson(
+      _value = BaseResponse<IntimacyReplyAnalyzeResponse>.fromJson(
         _result.data!,
-        (json) => json as dynamic,
+        (json) =>
+            IntimacyReplyAnalyzeResponse.fromJson(json as Map<String, dynamic>),
       );
     } on Object catch (e, s) {
       errorLogger?.logError(e, s, _options);

+ 40 - 0
lib/data/api/response/intimacy_reply_analyze_response.dart

@@ -0,0 +1,40 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import '../../bean/intimacy_reply_analyze_choice_item.dart';
+
+part 'intimacy_reply_analyze_response.g.dart';
+
+/// 识图回复响应
+@JsonSerializable()
+class IntimacyReplyAnalyzeResponse {
+  @JsonKey(name: "id")
+  String? id;
+
+  @JsonKey(name: "object")
+  String? object;
+
+  /// 创建时间
+  @JsonKey(name: "created")
+  int? created;
+
+  /// Ai模型
+  @JsonKey(name: "model")
+  int? model;
+
+  /// 选择项列表
+  @JsonKey(name: "choices")
+  List<IntimacyReplyAnalyzeChoiceItem>? choices;
+
+  IntimacyReplyAnalyzeResponse(
+    this.id,
+    this.object,
+    this.created,
+    this.model,
+    this.choices,
+  );
+
+  factory IntimacyReplyAnalyzeResponse.fromJson(Map<String, dynamic> json) =>
+      _$IntimacyReplyAnalyzeResponseFromJson(json);
+
+  Map<String, dynamic> toJson() => _$IntimacyReplyAnalyzeResponseToJson(this);
+}

+ 32 - 0
lib/data/api/response/intimacy_reply_analyze_response.g.dart

@@ -0,0 +1,32 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'intimacy_reply_analyze_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+IntimacyReplyAnalyzeResponse _$IntimacyReplyAnalyzeResponseFromJson(
+  Map<String, dynamic> json,
+) => IntimacyReplyAnalyzeResponse(
+  json['id'] as String?,
+  json['object'] as String?,
+  (json['created'] as num?)?.toInt(),
+  (json['model'] as num?)?.toInt(),
+  (json['choices'] as List<dynamic>?)
+      ?.map(
+        (e) =>
+            IntimacyReplyAnalyzeChoiceItem.fromJson(e as Map<String, dynamic>),
+      )
+      .toList(),
+);
+
+Map<String, dynamic> _$IntimacyReplyAnalyzeResponseToJson(
+  IntimacyReplyAnalyzeResponse instance,
+) => <String, dynamic>{
+  'id': instance.id,
+  'object': instance.object,
+  'created': instance.created,
+  'model': instance.model,
+  'choices': instance.choices,
+};

+ 25 - 0
lib/data/bean/intimacy_reply_analyze_choice_item.dart

@@ -0,0 +1,25 @@
+import 'package:json_annotation/json_annotation.dart';
+
+import 'intimacy_reply_analyze_choice_item_delta.dart';
+
+part 'intimacy_reply_analyze_choice_item.g.dart';
+
+/// 识图回复-选择项
+@JsonSerializable()
+class IntimacyReplyAnalyzeChoiceItem {
+  @JsonKey(name: "delta")
+  IntimacyReplyAnalyzeChoiceItemDelta? delta;
+
+  @JsonKey(name: "index")
+  int? index;
+
+  @JsonKey(name: "finish_reason")
+  String? finishReason;
+
+  IntimacyReplyAnalyzeChoiceItem(this.delta, this.index, this.finishReason);
+
+  factory IntimacyReplyAnalyzeChoiceItem.fromJson(Map<String, dynamic> json) =>
+      _$IntimacyReplyAnalyzeChoiceItemFromJson(json);
+
+  Map<String, dynamic> toJson() => _$IntimacyReplyAnalyzeChoiceItemToJson(this);
+}

+ 27 - 0
lib/data/bean/intimacy_reply_analyze_choice_item.g.dart

@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'intimacy_reply_analyze_choice_item.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+IntimacyReplyAnalyzeChoiceItem _$IntimacyReplyAnalyzeChoiceItemFromJson(
+  Map<String, dynamic> json,
+) => IntimacyReplyAnalyzeChoiceItem(
+  json['delta'] == null
+      ? null
+      : IntimacyReplyAnalyzeChoiceItemDelta.fromJson(
+        json['delta'] as Map<String, dynamic>,
+      ),
+  (json['index'] as num?)?.toInt(),
+  json['finish_reason'] as String?,
+);
+
+Map<String, dynamic> _$IntimacyReplyAnalyzeChoiceItemToJson(
+  IntimacyReplyAnalyzeChoiceItem instance,
+) => <String, dynamic>{
+  'delta': instance.delta,
+  'index': instance.index,
+  'finish_reason': instance.finishReason,
+};

+ 24 - 0
lib/data/bean/intimacy_reply_analyze_choice_item_delta.dart

@@ -0,0 +1,24 @@
+import 'package:json_annotation/json_annotation.dart';
+
+part 'intimacy_reply_analyze_choice_item_delta.g.dart';
+
+/// 识图回复-选择项-内容
+@JsonSerializable()
+class IntimacyReplyAnalyzeChoiceItemDelta {
+  /// 角色
+  @JsonKey(name: "role")
+  String? role;
+
+  /// 回复内容
+  @JsonKey(name: "content")
+  String? content;
+
+  IntimacyReplyAnalyzeChoiceItemDelta(this.role, this.content);
+
+  factory IntimacyReplyAnalyzeChoiceItemDelta.fromJson(
+    Map<String, dynamic> json,
+  ) => _$IntimacyReplyAnalyzeChoiceItemDeltaFromJson(json);
+
+  Map<String, dynamic> toJson() =>
+      _$IntimacyReplyAnalyzeChoiceItemDeltaToJson(this);
+}

+ 18 - 0
lib/data/bean/intimacy_reply_analyze_choice_item_delta.g.dart

@@ -0,0 +1,18 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'intimacy_reply_analyze_choice_item_delta.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+IntimacyReplyAnalyzeChoiceItemDelta
+_$IntimacyReplyAnalyzeChoiceItemDeltaFromJson(Map<String, dynamic> json) =>
+    IntimacyReplyAnalyzeChoiceItemDelta(
+      json['role'] as String?,
+      json['content'] as String?,
+    );
+
+Map<String, dynamic> _$IntimacyReplyAnalyzeChoiceItemDeltaToJson(
+  IntimacyReplyAnalyzeChoiceItemDelta instance,
+) => <String, dynamic>{'role': instance.role, 'content': instance.content};

+ 9 - 8
lib/data/repository/intimacy_analyze_repository.dart

@@ -15,6 +15,7 @@ import '../api/response/intimacy_analyze_chat_config_response.dart';
 import '../api/response/intimacy_analyze_config_response.dart';
 import '../api/response/intimacy_analyze_reply_config_response.dart';
 import '../api/response/intimacy_analyze_response.dart';
+import '../api/response/intimacy_reply_analyze_response.dart';
 
 /// 亲密度分析Repository层
 @LazySingleton()
@@ -113,12 +114,12 @@ class IntimacyAnalyzeRepository {
     return atmobApi.getIntimacyAnalyze(request).then(HttpHandler.handle(true));
   }
 
-  // /// 识图回复
-  // Future<Object> intimacyReplyAnalyze(
-  //   IntimacyReplyAnalyzeRequest request,
-  // ) {
-  //   return atmobApi
-  //       .intimacyReplyAnalyze(request)
-  //       .then(HttpHandler.handle(true));
-  // }
+  /// 识图回复
+  Future<IntimacyReplyAnalyzeResponse> intimacyReplyAnalyze(
+    IntimacyReplyAnalyzeRequest request,
+  ) {
+    return atmobApi
+        .intimacyReplyAnalyze(request)
+        .then(HttpHandler.handle(true));
+  }
 }

+ 8 - 6
lib/di/get_it.config.dart

@@ -326,12 +326,6 @@ extension GetItInjectableX on _i174.GetIt {
         gh<_i428.UploadFileManager>(),
       ),
     );
-    gh.factory<_i464.ScanImageReplyController>(
-      () => _i464.ScanImageReplyController(
-        gh<_i738.IntimacyAnalyzeConfigHelper>(),
-        gh<_i428.UploadFileManager>(),
-      ),
-    );
     gh.factory<_i278.CustomDirectionEditController>(
       () => _i278.CustomDirectionEditController(
         gh<_i738.IntimacyAnalyzeConfigHelper>(),
@@ -345,6 +339,14 @@ extension GetItInjectableX on _i174.GetIt {
         gh<_i83.AccountRepository>(),
       ),
     );
+    gh.factory<_i464.ScanImageReplyController>(
+      () => _i464.ScanImageReplyController(
+        gh<_i738.IntimacyAnalyzeConfigHelper>(),
+        gh<_i428.UploadFileManager>(),
+        gh<_i283.IntimacyAnalyzeRepository>(),
+        gh<_i83.AccountRepository>(),
+      ),
+    );
     gh.factory<_i987.IntimacyAnalyseReportController>(
       () => _i987.IntimacyAnalyseReportController(
         gh<_i283.IntimacyAnalyzeRepository>(),

+ 80 - 3
lib/module/intimacy_analyse/screenshot_reply/scan_image_reply/scan_image_reply_controller.dart

@@ -6,19 +6,30 @@ import 'package:injectable/injectable.dart';
 import 'package:keyboard/base/base_controller.dart';
 import 'package:keyboard/data/bean/upload_info.dart';
 import 'package:keyboard/resource/string.gen.dart';
+import 'package:keyboard/utils/atmob_log.dart';
+import 'package:keyboard/utils/toast_util.dart';
 import 'package:wechat_assets_picker/wechat_assets_picker.dart';
 
+import '../../../../data/api/request/intimacy_reply_analyze_request.dart';
+import '../../../../data/api/response/intimacy_reply_analyze_response.dart';
 import '../../../../data/bean/option_select_config.dart';
 import '../../../../data/bean/option_select_item.dart';
 import '../../../../data/bean/reply_mode.dart';
+import '../../../../data/repository/account_repository.dart';
+import '../../../../data/repository/intimacy_analyze_repository.dart';
+import '../../../../utils/error_handler.dart';
+import '../../../../utils/http_handler.dart';
 import '../../../../utils/image_picker_util.dart';
 import '../../../../utils/intimacy_analyze_config_helper.dart';
 import '../../../../utils/upload/upload_file_manager.dart';
 import '../../../../utils/upload/upload_scene_type.dart';
+import '../../../store/store_page.dart';
 
 /// 识图回复Controller
 @injectable
 class ScanImageReplyController extends BaseController {
+  final String _tag = "ScanImageReplyController";
+
   /// 上传场景
   final UploadSceneType uploadSceneType = UploadSceneType.scanImageReply;
 
@@ -28,6 +39,11 @@ class ScanImageReplyController extends BaseController {
   /// 文件上传管理器
   UploadFileManager uploadFileManager;
 
+  /// 亲密度分析模块的Repository
+  IntimacyAnalyzeRepository intimacyAnalyzeRepository;
+
+  AccountRepository accountRepository;
+
   /// 回复语气选项列表
   RxList<OptionSelectConfig> replyToneOptionSelectConfigList =
       <OptionSelectConfig>[].obs;
@@ -48,11 +64,13 @@ class ScanImageReplyController extends BaseController {
   Rxn<ReplyMode> currentReplyMode = Rxn();
 
   /// 回复语气列表
-  RxList<String> replyToneList = <String>["真好", "真好看", "真的很好看~"].obs;
+  RxList<String> replyToneList = <String>[].obs;
 
   ScanImageReplyController(
     this.intimacyAnalyzeConfigHelper,
     this.uploadFileManager,
+    this.intimacyAnalyzeRepository,
+    this.accountRepository,
   );
 
   @override
@@ -84,7 +102,9 @@ class ScanImageReplyController extends BaseController {
 
   /// 初始化回复模式
   void _initReplyModeList() {
-    var modeList = intimacyAnalyzeConfigHelper.intimacyAnalyzeReplyConfig.value?.modes ?? [];
+    var modeList =
+        intimacyAnalyzeConfigHelper.intimacyAnalyzeReplyConfig.value?.modes ??
+        [];
     replyModelList.addAll(modeList);
     replyModelList.refresh();
 
@@ -139,7 +159,64 @@ class ScanImageReplyController extends BaseController {
   }
 
   /// 点击获取回复按钮
-  void clickGetReplyBtn() {}
+  void clickGetReplyBtn() async {
+    // 非Vip,跳转到商店页
+    bool isVip = accountRepository.memberStatusInfo.value?.isMember ?? false;
+    if (!isVip) {
+      ToastUtil.show(StringName.needVipTip);
+      StorePage.start();
+      return;
+    }
+
+    // 上传的图片后端地址
+    List<String> imageList = [uploadInfo.value?.fileBackendPath ?? ""];
+    // 选择的回复语气
+    String title = currentSelectReplyToneOption.value?.name ?? "";
+    // 当前选择的回复模式
+    String mode = currentReplyMode.value?.value ?? "";
+
+    if (imageList.isEmpty) {
+      ToastUtil.show(StringName.noUploadScreenshotTip);
+      return;
+    }
+    if (title.isEmpty) {
+      ToastUtil.show(StringName.noChooseReplyToneTip);
+      return;
+    }
+    if (mode.isEmpty) {
+      ToastUtil.show(StringName.noChooseModeTip);
+      return;
+    }
+
+    try {
+      // 请求分析截图
+      IntimacyReplyAnalyzeResponse response = await intimacyAnalyzeRepository
+          .intimacyReplyAnalyze(
+            IntimacyReplyAnalyzeRequest(imageList, title, mode),
+          );
+      var newList =
+          response.choices?.map((item) {
+            return item.delta?.content ?? "";
+          }) ??
+          [];
+      replyToneList.clear();
+      replyToneList.addAll(newList);
+      replyToneList.refresh();
+    } catch (error) {
+      AtmobLog.e(_tag, error.toString());
+      if (error is ServerErrorException) {
+        // 需要Vip权限
+        if (error.code == 1005) {
+          ToastUtil.show(error.message);
+          StorePage.start();
+        } else {
+          ToastUtil.show(error.message);
+        }
+      } else {
+        ErrorHandler.toastError(error);
+      }
+    }
+  }
 
   /// 删除上传信息
   void deleteUploadInfo(UploadInfo info) {

+ 80 - 75
lib/module/intimacy_analyse/screenshot_reply/scan_image_reply/scan_image_reply_view.dart

@@ -269,69 +269,72 @@ class ScanImageReplyView extends BaseView<ScanImageReplyController> {
 
   /// 回复语气列表卡片
   Widget _buildReplyToneListCard() {
-    return Container(
-      margin: EdgeInsets.only(left: 12.w, right: 12.w),
-      decoration: BoxDecoration(
-        // 渐变背景
-        gradient: LinearGradient(
-          colors: [Color(0xFFEFE9FF), Color(0xFFFBFAFF)],
-          begin: Alignment.topCenter,
-          end: Alignment.bottomCenter,
+    return Obx(() {
+      if (controller.replyToneList.isEmpty) {
+        return SizedBox();
+      }
+      return Container(
+        margin: EdgeInsets.only(left: 12.w, right: 12.w),
+        decoration: BoxDecoration(
+          // 渐变背景
+          gradient: LinearGradient(
+            colors: [Color(0xFFEFE9FF), Color(0xFFFBFAFF)],
+            begin: Alignment.topCenter,
+            end: Alignment.bottomCenter,
+          ),
+          borderRadius: BorderRadius.all(Radius.circular(20.r)),
         ),
-        borderRadius: BorderRadius.all(Radius.circular(20.r)),
-      ),
-      child: Column(
-        children: [
-          Container(
-            margin: EdgeInsets.only(top: 10.h, bottom: 18.h),
-            child: Column(
-              children: [
-                Row(
-                  children: [
-                    Expanded(
-                      child: Row(
-                        children: [
-                          // 回复语气的标题
-                          SizedBox(
-                            width: 80.w,
-                            height: 40.h,
-                            child: Stack(
-                              children: [
-                                // 底部的横线
-                                Positioned(
-                                  left: 15.w,
-                                  bottom: 10.h,
-                                  child: Container(
-                                    width: 57.w,
-                                    height: 7.h,
-                                    color: Color(0xFFCEB6FF),
-                                  ),
-                                ),
-                                Positioned(
-                                  left: 0,
-                                  bottom: 0,
-                                  child: Container(
-                                    margin: EdgeInsets.only(
-                                      left: 16.w,
-                                      bottom: 10.h,
+        child: Column(
+          children: [
+            Container(
+              margin: EdgeInsets.only(top: 10.h, bottom: 18.h),
+              child: Column(
+                children: [
+                  Row(
+                    children: [
+                      Expanded(
+                        child: Row(
+                          children: [
+                            // 回复语气的标题
+                            SizedBox(
+                              width: 80.w,
+                              height: 40.h,
+                              child: Stack(
+                                children: [
+                                  // 底部的横线
+                                  Positioned(
+                                    left: 15.w,
+                                    bottom: 10.h,
+                                    child: Container(
+                                      width: 57.w,
+                                      height: 7.h,
+                                      color: Color(0xFFCEB6FF),
                                     ),
-                                    child: // 标题
-                                        Text(
-                                      StringName.intimacyAnalyseReplyTone,
-                                      style: TextStyle(
-                                        color: ColorName.black80,
-                                        fontSize: 14.sp,
-                                        fontWeight: FontWeight.w700,
+                                  ),
+                                  Positioned(
+                                    left: 0,
+                                    bottom: 0,
+                                    child: Container(
+                                      margin: EdgeInsets.only(
+                                        left: 16.w,
+                                        bottom: 10.h,
+                                      ),
+                                      child: // 标题
+                                          Text(
+                                        StringName.intimacyAnalyseReplyTone,
+                                        style: TextStyle(
+                                          color: ColorName.black80,
+                                          fontSize: 14.sp,
+                                          fontWeight: FontWeight.w700,
+                                        ),
                                       ),
                                     ),
                                   ),
-                                ),
-                              ],
+                                ],
+                              ),
                             ),
-                          ),
-                          // 回复内容
-                          Obx(() {
-                            return Text(
+                            // 当前选择的回复语气
+                            Text(
                               controller
                                       .currentSelectReplyToneOption
                                       .value
@@ -342,31 +345,33 @@ class ScanImageReplyView extends BaseView<ScanImageReplyController> {
                                 fontSize: 14.sp,
                                 fontWeight: FontWeight.w500,
                               ),
-                            );
-                          }),
-                        ],
+                            ),
+                          ],
+                        ),
                       ),
-                    ),
-                    // 重试按钮
-                    _buildRetryBtn(),
-                    SizedBox(width: 10.w),
-                  ],
-                ),
-                // 回复语气列表
-                for (var replyTone in controller.replyToneList)
-                  _buildReplyToneListItem(replyTone),
-              ],
+                      // 重试按钮
+                      _buildRetryBtn(),
+                      SizedBox(width: 10.w),
+                    ],
+                  ),
+                  // 回复语气列表
+                  for (var replyTone in controller.replyToneList)
+                    _buildReplyToneListItem(replyTone),
+                ],
+              ),
             ),
-          ),
-        ],
-      ),
-    );
+          ],
+        ),
+      );
+    });
   }
 
   /// 重试按钮
   Widget _buildRetryBtn() {
     return GestureDetector(
-      onTap: () {},
+      onTap: () {
+        controller.clickGetReplyBtn();
+      },
       child: Container(
         padding: EdgeInsets.symmetric(horizontal: 28.w, vertical: 8.h),
         decoration: BoxDecoration(

+ 8 - 0
lib/resource/string.gen.dart

@@ -227,6 +227,10 @@ class StringName {
   static final String forTa = 'for_ta'.tr; // 对于TA
   static final String forMe = 'for_me'.tr; // 对于我
   static final String chooseReplyTone = 'choose_reply_tone'.tr; // 选择回复语气
+  static final String noChooseReplyToneTip = 'no_choose_reply_tone_tip'.tr; // 请选择回复语气
+  static final String noChooseModeTip = 'no_choose_mode_tip'.tr; // 请选择模式
+  static final String noUploadScreenshotTip = 'no_upload_screenshot_tip'.tr; // 请上传截图
+  static final String noUploadConversationImageTip = 'no_upload_conversation_image_tip'.tr; // 请上传聊天记录图片
   static final String preview = 'preview'.tr; // 预览
   static final String retry = 'retry'.tr; // 再试试
   static final String nextStep = 'next_step'.tr; // 下一步
@@ -496,6 +500,10 @@ class StringMultiSource {
       'for_ta': '对于TA',
       'for_me': '对于我',
       'choose_reply_tone': '选择回复语气',
+      'no_choose_reply_tone_tip': '请选择回复语气',
+      'no_choose_mode_tip': '请选择模式',
+      'no_upload_screenshot_tip': '请上传截图',
+      'no_upload_conversation_image_tip': '请上传聊天记录图片',
       'preview': '预览',
       'retry': '再试试',
       'next_step': '下一步',