From 8c7df6205d762abc443f59bbafc2450d1aa777b3 Mon Sep 17 00:00:00 2001 From: jack ning Date: Mon, 9 May 2022 19:02:22 +0800 Subject: [PATCH] update --- .DS_Store | Bin 6148 -> 6148 bytes bytedesk_demo/lib/main.dart | 31 ++- bytedesk_demo/lib/page/chat_type_page.dart | 28 ++- bytedesk_demo/pubspec.yaml | 2 +- bytedesk_kefu/CHANGELOG.md | 8 + bytedesk_kefu/README.md | 2 +- bytedesk_kefu/example/lib/main.dart | 8 +- .../example/lib/page/chat_type_page.dart | 20 +- .../lib/blocs/group_bloc/group_bloc.dart | 5 +- .../lib/blocs/help_bloc/help_bloc.dart | 2 +- .../blocs/leavemsg_bloc/leavemsg_bloc.dart | 2 +- .../lib/blocs/message_bloc/message_bloc.dart | 21 +- .../lib/blocs/message_bloc/message_event.dart | 20 +- .../lib/blocs/message_bloc/message_state.dart | 7 + .../lib/blocs/profile_bloc/profile_bloc.dart | 2 +- .../lib/blocs/thread_bloc/thread_bloc.dart | 5 +- .../lib/blocs/thread_bloc/thread_event.dart | 3 +- bytedesk_kefu/lib/bytedesk_kefu.dart | 24 +- .../lib/http/bytedesk_message_api.dart | 70 +++++- .../lib/http/bytedesk_thread_api.dart | 29 ++- bytedesk_kefu/lib/http/bytedesk_user_api.dart | 6 +- bytedesk_kefu/lib/model/category.dart | 18 ++ bytedesk_kefu/lib/model/message.dart | 83 ++++++- bytedesk_kefu/lib/model/messageProvider.dart | 40 ++- bytedesk_kefu/lib/model/requestAnswer.dart | 6 +- bytedesk_kefu/lib/model/requestCategory.dart | 24 ++ bytedesk_kefu/lib/model/requestThread.dart | 7 + bytedesk_kefu/lib/model/thread.dart | 13 +- bytedesk_kefu/lib/mqtt/bytedesk_mqtt.dart | 6 +- .../lib/repositories/message_repository.dart | 15 +- .../lib/repositories/thread_repository.dart | 5 +- .../lib/ui/chat/page/chat_im_page.dart | 4 +- .../lib/ui/chat/page/chat_kf_page.dart | 231 ++++++++++++------ .../lib/ui/chat/page/chat_ls_page.dart | 4 +- .../ui/chat/provider/chat_kf_provider.dart | 5 +- .../lib/ui/chat/widget/message_widget.dart | 163 +++++++++--- .../lib/ui/feedback/page/feedback_page.dart | 2 +- .../lib/util/bytedesk_constants.dart | 6 +- bytedesk_kefu/lib/util/bytedesk_events.dart | 6 + bytedesk_kefu/lib/util/bytedesk_utils.dart | 4 +- bytedesk_kefu/pubspec.yaml | 2 +- 41 files changed, 737 insertions(+), 202 deletions(-) create mode 100755 bytedesk_kefu/lib/model/category.dart create mode 100755 bytedesk_kefu/lib/model/requestCategory.dart diff --git a/.DS_Store b/.DS_Store index af3ea8cab40720072de0b0b0a81ad63b353337a6..b77592fb950390aeaf8d03c8fe4c574807011a17 100644 GIT binary patch delta 356 zcmZoMXfc=|#>B)qu~2NHo+2aX!~pA!7aACWj2=0GoOHwBs4(*$nYO zoXU{KP>Nz@$;(4PYw)N>cFdNep=YN89YaV*ii<7G{lsZO@xj7?jR%8RHnVf^a{xna hCJzu~2NHo+2aT!~knX#>w-TEsep3E with WidgetsBindingObserver { + // // 获取appkey,登录后台->渠道管理->Flutter->添加应用->获取appkey + // String _appKey = '81f427ea-4467-4c7c-b0cd-5c0e4b51456f'; + // // 获取subDomain,也即企业号:登录后台->客服管理->客服账号->企业号 + // String _subDomain = "vip"; // String _title = '萝卜丝客服Demo(连接中...)'; AudioCache audioCache = AudioCache(); @@ -74,7 +78,7 @@ class _MyAppState extends State with WidgetsBindingObserver { // 第二步:联系客服,完毕 Navigator.of(context) .push(MaterialPageRoute(builder: (context) { - return ChatTypePage(); + return const ChatTypePage(); })); }, ), @@ -85,7 +89,7 @@ class _MyAppState extends State with WidgetsBindingObserver { // 需要首先调用anonymousLogin之后,再调用设置用户信息接口 Navigator.of(context) .push(MaterialPageRoute(builder: (context) { - return UserInfoPage(); + return const UserInfoPage(); })); }, ), @@ -95,7 +99,7 @@ class _MyAppState extends State with WidgetsBindingObserver { onTap: () { Navigator.of(context) .push(MaterialPageRoute(builder: (context) { - return OnlineStatusPage(); + return const OnlineStatusPage(); })); }, ), @@ -105,7 +109,7 @@ class _MyAppState extends State with WidgetsBindingObserver { onTap: () { Navigator.of(context) .push(MaterialPageRoute(builder: (context) { - return HistoryThreadPage(); + return const HistoryThreadPage(); })); }, ), @@ -131,10 +135,27 @@ class _MyAppState extends State with WidgetsBindingObserver { onTap: () { Navigator.of(context) .push(MaterialPageRoute(builder: (context) { - return SettingPage(); + return const SettingPage(); })); }, ), + // ListTile( + // title: Text('退出登录'), + // onTap: () { + // BytedeskKefu.logout(); + // }, + // ), + // ListTile( + // title: Text('重新初始化'), + // onTap: () { + // BytedeskKefu.initWithUsernameAndNicknameAndAvatar( + // 'myflutterusername2', + // '我是帅哥', + // 'https://bytedesk.oss-cn-shenzhen.aliyuncs.com/avatars/boy.png', + // _appKey, + // _subDomain); + // }, + // ), const ListTile( title: Text('技术支持: QQ-3群: 825257535'), ) diff --git a/bytedesk_demo/lib/page/chat_type_page.dart b/bytedesk_demo/lib/page/chat_type_page.dart index 132d669..29d7319 100755 --- a/bytedesk_demo/lib/page/chat_type_page.dart +++ b/bytedesk_demo/lib/page/chat_type_page.dart @@ -15,10 +15,10 @@ class ChatTypePage extends StatefulWidget { class _ChatTypePageState extends State { // 第二步:到 客服管理->技能组-有一列 ‘唯一ID(wId)’, 默认设置工作组wid // 说明:一个技能组可以分配多个客服,访客会按照一定的规则分配给组内的各个客服账号 - String _workGroupWid = "201807171659201"; // 默认人工 - String _workGroupWidRobot = "201809061716221"; // 默认机器人, 在管理后台开启或关闭机器人 + final String _workGroupWid = "201807171659201"; // 默认人工 + final String _workGroupWidRobot = "201809061716221"; // 默认机器人, 在管理后台开启或关闭机器人 // 说明:直接发送给此一个客服账号,一对一会话 - String _agentUid = "201808221551193"; + final String _agentUid = "201808221551193"; // 未读消息数目 String _unreadMessageCount = "0"; // @@ -53,6 +53,7 @@ class _ChatTypePageState extends State { ), ListTile( title: const Text('技能组客服'), + subtitle: const Text('默认人工'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startWorkGroupChat( @@ -61,14 +62,25 @@ class _ChatTypePageState extends State { ), ListTile( title: const Text('技能组客服-机器人'), + subtitle: const Text('默认热门问题'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startWorkGroupChat( - context, _workGroupWidRobot, "技能组客服-默认机器人"); + context, _workGroupWidRobot, "技能组客服-默认机器人问题"); + }, + ), + ListTile( + title: const Text('技能组客服-机器人2'), + subtitle: const Text('默认问题分类'), + trailing: const Icon(Icons.keyboard_arrow_right), + onTap: () { + BytedeskKefu.startWorkGroupChatV2Robot( + context, _workGroupWidRobot, "技能组客服-默认机器人分类"); }, ), ListTile( title: const Text('技能组客服-电商'), + subtitle: const Text('携带商品参数'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { // 商品信息,type/title/content/price/url/imageUrl/id/categoryCode @@ -92,6 +104,7 @@ class _ChatTypePageState extends State { ), ListTile( title: const Text('技能组客服-电商-回调'), + subtitle: const Text('携带商品参数,点击商品支持回调'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { // 商品信息,type/title/content/price/url/imageUrl/id/categoryCode @@ -122,6 +135,7 @@ class _ChatTypePageState extends State { ), ListTile( title: const Text('技能组客服-附言'), + subtitle: const Text('自动发送一条消息'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startWorkGroupChatPostscript( @@ -133,6 +147,7 @@ class _ChatTypePageState extends State { ), ListTile( title: const Text('指定一对一客服'), + subtitle: const Text('默认人工'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startAppointedChat(context, _agentUid, "指定一对一客服"); @@ -140,6 +155,7 @@ class _ChatTypePageState extends State { ), ListTile( title: const Text('指定一对一客服-电商'), + subtitle: const Text('携带商品参数'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { // 商品信息,type/title/content/price/url/imageUrl/id/categoryCode @@ -162,6 +178,7 @@ class _ChatTypePageState extends State { ), ListTile( title: const Text('指定一对一客服-电商-回调'), + subtitle: const Text('携带商品参数,点击商品支持回调'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { // 商品信息,type/title/content/price/url/imageUrl/id/categoryCode @@ -191,6 +208,7 @@ class _ChatTypePageState extends State { ), ListTile( title: const Text('指定一对一客服-附言'), + subtitle: const Text('自动发送一条消息'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startAppointedChatPostscript( @@ -204,7 +222,7 @@ class _ChatTypePageState extends State { title: const Text('H5网页会话'), trailing: const Icon(Icons.keyboard_arrow_right), onTap: () { - // print('h5 chat'); + print('h5 chat'); // 注意: 登录后台->客服管理->技能组(或客服账号)->获取客服代码 获取相应URL String url = "https://h2.kefux.cn/chat/h5/index.html?sub=vip&uid=201808221551193&wid=201807171659201&type=workGroup&aid=&hidenav=1&ph=ph"; diff --git a/bytedesk_demo/pubspec.yaml b/bytedesk_demo/pubspec.yaml index c241d60..f8019cd 100644 --- a/bytedesk_demo/pubspec.yaml +++ b/bytedesk_demo/pubspec.yaml @@ -46,7 +46,7 @@ dependencies: # 请在ios/Podfile中添加:use_frameworks! vibration: ^1.7.3 # 在线客服 https://pub.dev/packages/bytedesk_kefu - bytedesk_kefu: ^1.2.5 + bytedesk_kefu: ^1.2.8 # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. diff --git a/bytedesk_kefu/CHANGELOG.md b/bytedesk_kefu/CHANGELOG.md index 70d993c..72eced0 100644 --- a/bytedesk_kefu/CHANGELOG.md +++ b/bytedesk_kefu/CHANGELOG.md @@ -1,5 +1,13 @@ # Upgrade Log +## 1.2.8 + +* optimize user experience + +## 1.2.7 + +* optimize user experience + ## 1.2.6 * optimize user experience diff --git a/bytedesk_kefu/README.md b/bytedesk_kefu/README.md index e3ad2bc..76de2d2 100644 --- a/bytedesk_kefu/README.md +++ b/bytedesk_kefu/README.md @@ -65,7 +65,7 @@ BytedeskKefu.startWorkGroupChat(context, workGroupWid, "title"); ```dart bytedesk_kefu: - path: ./vendors/bytedesk + path: ./vendors/bytedesk_kefu ``` ### Support diff --git a/bytedesk_kefu/example/lib/main.dart b/bytedesk_kefu/example/lib/main.dart index c01f927..bd6f869 100644 --- a/bytedesk_kefu/example/lib/main.dart +++ b/bytedesk_kefu/example/lib/main.dart @@ -43,10 +43,10 @@ class MyApp extends StatefulWidget { } class _MyAppState extends State with WidgetsBindingObserver { - // 获取appkey,登录后台->渠道管理->Flutter->添加应用->获取appkey - String _appKey = '81f427ea-4467-4c7c-b0cd-5c0e4b51456f'; - // 获取subDomain,也即企业号:登录后台->客服管理->客服账号->企业号 - String _subDomain = "vip"; + // // 获取appkey,登录后台->渠道管理->Flutter->添加应用->获取appkey + // String _appKey = '81f427ea-4467-4c7c-b0cd-5c0e4b51456f'; + // // 获取subDomain,也即企业号:登录后台->客服管理->客服账号->企业号 + // String _subDomain = "vip"; // String _title = '萝卜丝客服Demo(连接中...)'; // AudioCache audioCache = AudioCache(); diff --git a/bytedesk_kefu/example/lib/page/chat_type_page.dart b/bytedesk_kefu/example/lib/page/chat_type_page.dart index 47e0ee5..8c79e2c 100755 --- a/bytedesk_kefu/example/lib/page/chat_type_page.dart +++ b/bytedesk_kefu/example/lib/page/chat_type_page.dart @@ -53,6 +53,7 @@ class _ChatTypePageState extends State { ), ListTile( title: Text('技能组客服'), + subtitle: Text('默认人工'), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startWorkGroupChat( @@ -61,14 +62,25 @@ class _ChatTypePageState extends State { ), ListTile( title: Text('技能组客服-机器人'), + subtitle: Text('默认热门问题'), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startWorkGroupChat( - context, _workGroupWidRobot, "技能组客服-默认机器人"); + context, _workGroupWidRobot, "技能组客服-默认机器人问题"); + }, + ), + ListTile( + title: Text('技能组客服-机器人2'), + subtitle: Text('默认问题分类'), + trailing: Icon(Icons.keyboard_arrow_right), + onTap: () { + BytedeskKefu.startWorkGroupChatV2Robot( + context, _workGroupWidRobot, "技能组客服-默认机器人分类"); }, ), ListTile( title: Text('技能组客服-电商'), + subtitle: Text('携带商品参数'), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { // 商品信息,type/title/content/price/url/imageUrl/id/categoryCode @@ -92,6 +104,7 @@ class _ChatTypePageState extends State { ), ListTile( title: Text('技能组客服-电商-回调'), + subtitle: Text('携带商品参数,点击商品支持回调'), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { // 商品信息,type/title/content/price/url/imageUrl/id/categoryCode @@ -122,6 +135,7 @@ class _ChatTypePageState extends State { ), ListTile( title: Text('技能组客服-附言'), + subtitle: Text('自动发送一条消息'), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startWorkGroupChatPostscript( @@ -133,6 +147,7 @@ class _ChatTypePageState extends State { ), ListTile( title: Text('指定一对一客服'), + subtitle: Text('默认人工'), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startAppointedChat(context, _agentUid, "指定一对一客服"); @@ -140,6 +155,7 @@ class _ChatTypePageState extends State { ), ListTile( title: Text('指定一对一客服-电商'), + subtitle: Text('携带商品参数'), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { // 商品信息,type/title/content/price/url/imageUrl/id/categoryCode @@ -162,6 +178,7 @@ class _ChatTypePageState extends State { ), ListTile( title: Text('指定一对一客服-电商-回调'), + subtitle: Text('携带商品参数,点击商品支持回调'), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { // 商品信息,type/title/content/price/url/imageUrl/id/categoryCode @@ -191,6 +208,7 @@ class _ChatTypePageState extends State { ), ListTile( title: Text('指定一对一客服-附言'), + subtitle: Text('自动发送一条消息'), trailing: Icon(Icons.keyboard_arrow_right), onTap: () { BytedeskKefu.startAppointedChatPostscript( diff --git a/bytedesk_kefu/lib/blocs/group_bloc/group_bloc.dart b/bytedesk_kefu/lib/blocs/group_bloc/group_bloc.dart index 2a03faa..9637d30 100755 --- a/bytedesk_kefu/lib/blocs/group_bloc/group_bloc.dart +++ b/bytedesk_kefu/lib/blocs/group_bloc/group_bloc.dart @@ -3,10 +3,7 @@ import 'package:bloc/bloc.dart'; import './bloc.dart'; class GroupBloc extends Bloc { - - GroupBloc() : super(InitialGroupState()) { - - } + GroupBloc() : super(InitialGroupState()); // @override // Stream mapEventToState( diff --git a/bytedesk_kefu/lib/blocs/help_bloc/help_bloc.dart b/bytedesk_kefu/lib/blocs/help_bloc/help_bloc.dart index ddc10bf..fb16b23 100755 --- a/bytedesk_kefu/lib/blocs/help_bloc/help_bloc.dart +++ b/bytedesk_kefu/lib/blocs/help_bloc/help_bloc.dart @@ -1,4 +1,4 @@ -import 'dart:async'; +// import 'dart:async'; import 'package:bytedesk_kefu/blocs/help_bloc/bloc.dart'; import 'package:bytedesk_kefu/model/helpArticle.dart'; import 'package:bytedesk_kefu/model/helpCategory.dart'; diff --git a/bytedesk_kefu/lib/blocs/leavemsg_bloc/leavemsg_bloc.dart b/bytedesk_kefu/lib/blocs/leavemsg_bloc/leavemsg_bloc.dart index 21930f6..11bd06d 100755 --- a/bytedesk_kefu/lib/blocs/leavemsg_bloc/leavemsg_bloc.dart +++ b/bytedesk_kefu/lib/blocs/leavemsg_bloc/leavemsg_bloc.dart @@ -1,4 +1,4 @@ -import 'dart:async'; +// import 'dart:async'; import 'package:bytedesk_kefu/blocs/leavemsg_bloc/bloc.dart'; import 'package:bloc/bloc.dart'; import 'package:bytedesk_kefu/repositories/leavemsg_repository.dart'; diff --git a/bytedesk_kefu/lib/blocs/message_bloc/message_bloc.dart b/bytedesk_kefu/lib/blocs/message_bloc/message_bloc.dart index 37aa5fd..2ebb095 100755 --- a/bytedesk_kefu/lib/blocs/message_bloc/message_bloc.dart +++ b/bytedesk_kefu/lib/blocs/message_bloc/message_bloc.dart @@ -2,6 +2,7 @@ import 'package:bytedesk_kefu/model/jsonResult.dart'; import 'package:bytedesk_kefu/model/message.dart'; import 'package:bytedesk_kefu/model/requestAnswer.dart'; +import 'package:bytedesk_kefu/model/requestCategory.dart'; import 'package:bytedesk_kefu/model/uploadJsonResult.dart'; import 'package:bytedesk_kefu/repositories/message_repository.dart'; import 'package:bloc/bloc.dart'; @@ -21,6 +22,7 @@ class MessageBloc extends Bloc { on(_mapLoadChannelMessageToState); on(_mapQueryAnswerToState); + on(_mapQueryCategoryToState); on(_mapMessageAnswerToState); on(_mapRateAnswerToState); } @@ -134,7 +136,7 @@ class MessageBloc extends Bloc { emit(MessageLoading()); try { final RequestAnswerResult requestAnswerResult = - await messageRepository.queryAnswer(event.tid, event.aid); + await messageRepository.queryAnswer(event.tid, event.aid, event.mid); emit(QueryAnswerSuccess( query: requestAnswerResult.query, answer: requestAnswerResult.anwser)); } catch (error) { @@ -143,11 +145,26 @@ class MessageBloc extends Bloc { } } + void _mapQueryCategoryToState( + QueryCategoryEvent event, Emitter emit) async { + emit(MessageLoading()); + try { + final RequestCategoryResult requestAnswerResult = + await messageRepository.queryCategory(event.tid, event.cid); + emit(QueryCategorySuccess( + query: requestAnswerResult.query, + answer: requestAnswerResult.anwser)); + } catch (error) { + print(error); + emit(UpLoadImageError()); + } + } + void _mapMessageAnswerToState(MessageAnswerEvent event, Emitter emit) async { emit(MessageLoading()); try { final RequestAnswerResult requestAnswerResult = await messageRepository - .messageAnswer(event.type, event.wid, event.aid, event.content); + .messageAnswer(event.wid, event.content); emit(MessageAnswerSuccess( query: requestAnswerResult.query, answer: requestAnswerResult.anwser)); } catch (error) { diff --git a/bytedesk_kefu/lib/blocs/message_bloc/message_event.dart b/bytedesk_kefu/lib/blocs/message_bloc/message_event.dart index e4cec48..e408d98 100755 --- a/bytedesk_kefu/lib/blocs/message_bloc/message_event.dart +++ b/bytedesk_kefu/lib/blocs/message_bloc/message_event.dart @@ -67,20 +67,30 @@ class LoadChannelMessageEvent extends MessageEvent { class QueryAnswerEvent extends MessageEvent { final String? tid; final String? aid; + final String? mid; + + QueryAnswerEvent({@required this.tid, @required this.aid, @required this.mid}) : super(); +} + +class QueryCategoryEvent extends MessageEvent { + final String? tid; + final String? cid; - QueryAnswerEvent({@required this.tid, @required this.aid}) : super(); + QueryCategoryEvent({@required this.tid, @required this.cid}) + : super(); } class MessageAnswerEvent extends MessageEvent { - final String? type; + // final String? type; final String? wid; - final String? aid; + // final String? aid; final String? content; MessageAnswerEvent( - {@required this.type, + { + // @required this.type, @required this.wid, - @required this.aid, + // @required this.aid, @required this.content}) : super(); } diff --git a/bytedesk_kefu/lib/blocs/message_bloc/message_state.dart b/bytedesk_kefu/lib/blocs/message_bloc/message_state.dart index 0a2d1f5..7686dfe 100755 --- a/bytedesk_kefu/lib/blocs/message_bloc/message_state.dart +++ b/bytedesk_kefu/lib/blocs/message_bloc/message_state.dart @@ -126,6 +126,13 @@ class QueryAnswerSuccess extends MessageState { QueryAnswerSuccess({@required this.query, @required this.answer}) : super(); } +class QueryCategorySuccess extends MessageState { + final Message? query; + final Message? answer; + + QueryCategorySuccess({@required this.query, @required this.answer}) : super(); +} + class MessageAnswerSuccess extends MessageState { final Message? query; final Message? answer; diff --git a/bytedesk_kefu/lib/blocs/profile_bloc/profile_bloc.dart b/bytedesk_kefu/lib/blocs/profile_bloc/profile_bloc.dart index 3a893a5..ffe2c30 100755 --- a/bytedesk_kefu/lib/blocs/profile_bloc/profile_bloc.dart +++ b/bytedesk_kefu/lib/blocs/profile_bloc/profile_bloc.dart @@ -1,4 +1,4 @@ -import 'dart:async'; +// import 'dart:async'; import 'package:bytedesk_kefu/model/jsonResult.dart'; import 'package:bytedesk_kefu/model/user.dart'; import 'package:bytedesk_kefu/repositories/user_repository.dart'; diff --git a/bytedesk_kefu/lib/blocs/thread_bloc/thread_bloc.dart b/bytedesk_kefu/lib/blocs/thread_bloc/thread_bloc.dart index 7a90f62..9bb42f5 100755 --- a/bytedesk_kefu/lib/blocs/thread_bloc/thread_bloc.dart +++ b/bytedesk_kefu/lib/blocs/thread_bloc/thread_bloc.dart @@ -17,8 +17,8 @@ class ThreadBloc extends Bloc { on(_mapRefreshHistoryThreadToState); on(_mapRefreshVisitorThreadToState); on(_mapRefreshVisitorThreadAllToState); - on(_mapRequestThreadToState); + on(_mapRequestThreadToState); on(_mapRequestAgentToState); on(_mapRequestContactThreadToState); on(_mapRequestGroupThreadToState); @@ -85,11 +85,10 @@ class ThreadBloc extends Bloc { void _mapRequestThreadToState( RequestThreadEvent event, Emitter emit) async { - print('RequestThreadEvent'); emit(RequestThreading()); try { final RequestThreadResult thread = await threadRepository.requestThread( - event.wid, event.type, event.aid); + event.wid, event.type, event.aid, event.isV2Robot); emit(RequestThreadSuccess(thread)); } catch (error) { print(error); diff --git a/bytedesk_kefu/lib/blocs/thread_bloc/thread_event.dart b/bytedesk_kefu/lib/blocs/thread_bloc/thread_event.dart index 17aa8c8..137918e 100755 --- a/bytedesk_kefu/lib/blocs/thread_bloc/thread_event.dart +++ b/bytedesk_kefu/lib/blocs/thread_bloc/thread_event.dart @@ -55,9 +55,10 @@ class RequestThreadEvent extends ThreadEvent { final String? wid; final String? type; final String? aid; + final bool? isV2Robot; RequestThreadEvent( - {@required this.wid, @required this.type, @required this.aid}) + {@required this.wid, @required this.type, @required this.aid, @required this.isV2Robot}) : super(); } diff --git a/bytedesk_kefu/lib/bytedesk_kefu.dart b/bytedesk_kefu/lib/bytedesk_kefu.dart index 27cb725..d0979ea 100644 --- a/bytedesk_kefu/lib/bytedesk_kefu.dart +++ b/bytedesk_kefu/lib/bytedesk_kefu.dart @@ -173,14 +173,20 @@ class BytedeskKefu { static void startWorkGroupChat( BuildContext context, String wid, String title) { startChatDefault( - context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title); + context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, false); + } + + static void startWorkGroupChatV2Robot( + BuildContext context, String wid, String title) { + startChatDefault( + context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, true); } // 发送附言消息 static void startWorkGroupChatPostscript( BuildContext context, String wid, String title, String postScript) { startChat(context, wid, BytedeskConstants.CHAT_TYPE_WORKGROUP, title, '', - postScript, null); + postScript, false, null); } // 电商接口,携带商品参数 @@ -201,14 +207,14 @@ class BytedeskKefu { static void startAppointedChat( BuildContext context, String uid, String title) { startChatDefault( - context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title); + context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title, false); } // 发送附言消息 static void startAppointedChatPostscript( BuildContext context, String uid, String title, String postScript) { startChat(context, uid, BytedeskConstants.CHAT_TYPE_APPOINTED, title, '', - postScript, null); + postScript, false, null); } // 电商接口,携带商品参数 @@ -226,20 +232,20 @@ class BytedeskKefu { // 默认设置商品信息和附言为空 static void startChatDefault( - BuildContext context, String uuid, String type, String title) { - startChat(context, uuid, type, title, '', '', null); + BuildContext context, String uuid, String type, String title, bool isV2Robot) { + startChat(context, uuid, type, title, '', '', isV2Robot, null); } // 电商对话-自定义类型(技能组、指定客服) static void startChatShop(BuildContext context, String uuid, String type, String title, String commodity, ValueSetter? customCallback) { - startChat(context, uuid, type, title, commodity, '', customCallback); + startChat(context, uuid, type, title, commodity, '', false, customCallback); } // 发送附言消息-自定义类型(技能组、指定客服) static void startChatPostscript(BuildContext context, String uuid, String type, String title, String postScript) { - startChat(context, uuid, type, title, '', postScript, null); + startChat(context, uuid, type, title, '', postScript, false, null); } // 客服会话-自定义类型(技能组、指定客服) @@ -250,6 +256,7 @@ class BytedeskKefu { String title, String commodity, String postScript, + bool isV2Robot, ValueSetter? customCallback) { Navigator.of(context).push(new MaterialPageRoute(builder: (context) { return new ChatKFProvider( @@ -259,6 +266,7 @@ class BytedeskKefu { title: title, custom: commodity, postscript: postScript, + isV2Robot: isV2Robot, customCallback: customCallback, ); })); diff --git a/bytedesk_kefu/lib/http/bytedesk_message_api.dart b/bytedesk_kefu/lib/http/bytedesk_message_api.dart index 0a6ee69..23271e0 100755 --- a/bytedesk_kefu/lib/http/bytedesk_message_api.dart +++ b/bytedesk_kefu/lib/http/bytedesk_message_api.dart @@ -5,6 +5,7 @@ import 'package:bytedesk_kefu/http/bytedesk_base_api.dart'; import 'package:bytedesk_kefu/model/jsonResult.dart'; import 'package:bytedesk_kefu/model/message.dart'; import 'package:bytedesk_kefu/model/requestAnswer.dart'; +import 'package:bytedesk_kefu/model/requestCategory.dart'; import 'package:bytedesk_kefu/model/uploadJsonResult.dart'; import 'package:bytedesk_kefu/util/bytedesk_constants.dart'; import 'package:bytedesk_kefu/util/bytedesk_events.dart'; @@ -143,8 +144,6 @@ class BytedeskMessageHttpApi extends BytedeskBaseHttpApi { // Future queryAnswer(String? tid, String? aid) async { // - // final queryAnswerUrl = - // '$baseUrl/api/answer/query?tid=$tid&aid=$aid&client=$client'; final queryAnswerUrl = Uri.http(BytedeskConstants.host, '/api/answer/query', {'tid': tid, 'aid': aid, 'client': client}); print("query Url $queryAnswerUrl"); @@ -165,16 +164,64 @@ class BytedeskMessageHttpApi extends BytedeskBaseHttpApi { } // - Future messageAnswer( - String? type, String? wid, String? aid, String? content) async { + Future queryAnswer2( + String? tid, String? aid, String? mid) async { + // + final queryAnswerUrl = Uri.http( + BytedeskConstants.host, + '/api/v2/answer/query', + {'tid': tid, 'aid': aid, 'mid': mid, 'client': client}); + print("query Url $queryAnswerUrl"); + final initResponse = + await this.httpClient.get(queryAnswerUrl, headers: getHeaders()); + // + //解决json解析中的乱码问题 + Utf8Decoder utf8decoder = Utf8Decoder(); // fix 中文乱码 + //将string类型数据 转换为json类型的数据 + final responseJson = + json.decode(utf8decoder.convert(initResponse.bodyBytes)); + // 判断token是否过期 + if (responseJson.toString().contains('invalid_token')) { + bytedeskEventBus.fire(InvalidTokenEventBus()); + } + // + return RequestAnswerResult.fromJson(responseJson); + } + + // + Future queryCategory( + String? tid, String? cid) async { + // + final queryCategoryUrl = Uri.http( + BytedeskConstants.host, + '/api/v2/answer/category', + {'tid': tid, 'cid': cid, 'client': client}); + print("query Url $queryCategoryUrl"); + final initResponse = + await this.httpClient.get(queryCategoryUrl, headers: getHeaders()); + // + //解决json解析中的乱码问题 + Utf8Decoder utf8decoder = Utf8Decoder(); // fix 中文乱码 + //将string类型数据 转换为json类型的数据 + final responseJson = + json.decode(utf8decoder.convert(initResponse.bodyBytes)); + // 判断token是否过期 + if (responseJson.toString().contains('invalid_token')) { + bytedeskEventBus.fire(InvalidTokenEventBus()); + } // - // final messageAnswerUrl = - // '$baseUrl/api/v2/answer/message?type=$type&wid=$wid&aid=$aid&content=$content&client=$client'; - final messageAnswerUrl = Uri.http( - BytedeskConstants.host, '/api/v2/answer/message', { - 'type': type, + return RequestCategoryResult.fromJson(responseJson); + } + + // + Future messageAnswer( + String? wid, String? content) async { + // String? aid, String? type, + final messageAnswerUrl = + Uri.http(BytedeskConstants.host, '/api/v2/answer/message', { + // 'type': type, 'wid': wid, - 'aid': aid, + // 'aid': aid, 'content': content, 'client': client }); @@ -187,7 +234,8 @@ class BytedeskMessageHttpApi extends BytedeskBaseHttpApi { //将string类型数据 转换为json类型的数据 final responseJson = json.decode(utf8decoder.convert(initResponse.bodyBytes)); - print("messageAnswer responseJson $responseJson"); + print("messageAnswer:"); + print(responseJson); // 判断token是否过期 if (responseJson.toString().contains('invalid_token')) { bytedeskEventBus.fire(InvalidTokenEventBus()); diff --git a/bytedesk_kefu/lib/http/bytedesk_thread_api.dart b/bytedesk_kefu/lib/http/bytedesk_thread_api.dart index 43ffd7d..7a51854 100755 --- a/bytedesk_kefu/lib/http/bytedesk_thread_api.dart +++ b/bytedesk_kefu/lib/http/bytedesk_thread_api.dart @@ -149,7 +149,7 @@ class BytedeskThreadHttpApi extends BytedeskBaseHttpApi { // final threadUrl = Uri.http(BytedeskConstants.host, '/api/thread/request', {'wId': wid, 'type': type, 'aId': aid, 'client': client}); - // print("request thread Url $threadUrl"); + print(threadUrl); final initResponse = await this.httpClient.get(threadUrl, headers: getHeaders()); @@ -158,7 +158,8 @@ class BytedeskThreadHttpApi extends BytedeskBaseHttpApi { //将string类型数据 转换为json类型的数据 final responseJson = json.decode(utf8decoder.convert(initResponse.bodyBytes)); - // print("responseJson $responseJson"); + // print("requestThread:"); + // print(responseJson); // 判断token是否过期 if (responseJson.toString().contains('invalid_token')) { bytedeskEventBus.fire(InvalidTokenEventBus()); @@ -167,6 +168,30 @@ class BytedeskThreadHttpApi extends BytedeskBaseHttpApi { return RequestThreadResult.fromJson(responseJson); } + // 机器人分类会话 + Future requestWorkGroupThreadV2(String? wid) async { + // + final threadUrl = Uri.http(BytedeskConstants.host, + '/api/v2/thread/workGroup', {'wId': wid, 'client': client}); + print(threadUrl); + final initResponse = + await this.httpClient.get(threadUrl, headers: getHeaders()); + + //解决json解析中的乱码问题 + Utf8Decoder utf8decoder = Utf8Decoder(); // fix 中文乱码 + //将string类型数据 转换为json类型的数据 + final responseJson = + json.decode(utf8decoder.convert(initResponse.bodyBytes)); + print("requestWorkGroupThreadV2:"); + print(responseJson); + // 判断token是否过期 + if (responseJson.toString().contains('invalid_token')) { + bytedeskEventBus.fire(InvalidTokenEventBus()); + } + + return RequestThreadResult.fromJsonV2(responseJson); + } + // 请求人工客服,不管此工作组是否设置为默认机器人,只要有人工客服在线,则可以直接对接人工 Future requestAgent( String? wid, String? type, String? aid) async { diff --git a/bytedesk_kefu/lib/http/bytedesk_user_api.dart b/bytedesk_kefu/lib/http/bytedesk_user_api.dart index deba63b..0aa9481 100755 --- a/bytedesk_kefu/lib/http/bytedesk_user_api.dart +++ b/bytedesk_kefu/lib/http/bytedesk_user_api.dart @@ -33,11 +33,11 @@ class BytedeskUserHttpApi extends BytedeskBaseHttpApi { // print('oauth result: $oauthResponse'); // check the status code for the result int statusCode = oauthResponse.statusCode; - print("statusCode $statusCode"); + // print("statusCode $statusCode"); // 200: 授权成功,否则授权失败 final oauthJson = jsonDecode(oauthResponse.body); - print('oauth:'); - print(oauthJson); + // print('oauth:'); + // print(oauthJson); SpUtil.putBool(BytedeskConstants.isLogin, true); SpUtil.putString(BytedeskConstants.accessToken, oauthJson['access_token']); // diff --git a/bytedesk_kefu/lib/model/category.dart b/bytedesk_kefu/lib/model/category.dart new file mode 100755 index 0000000..00e5a56 --- /dev/null +++ b/bytedesk_kefu/lib/model/category.dart @@ -0,0 +1,18 @@ +import 'package:equatable/equatable.dart'; + +class Category extends Equatable { + // + final String? cid; + final String? name; + // + Category({this.cid, this.name}) : super(); + // + static Category fromJson(dynamic json) { + return Category( + cid: json['cid'], name: json['name']); + } + + // + @override + List get props => [cid!]; +} diff --git a/bytedesk_kefu/lib/model/message.dart b/bytedesk_kefu/lib/model/message.dart index 1f5a1eb..51439a3 100755 --- a/bytedesk_kefu/lib/model/message.dart +++ b/bytedesk_kefu/lib/model/message.dart @@ -5,6 +5,8 @@ import 'package:bytedesk_kefu/model/thread.dart'; import 'package:bytedesk_kefu/model/user.dart'; import 'package:bytedesk_kefu/util/bytedesk_constants.dart'; import 'package:sp_util/sp_util.dart'; + +import 'category.dart'; // import 'package:equatable/equatable.dart'; class Message { @@ -30,6 +32,9 @@ class Message { // List? answers; String? answersJson; + // + List? categories; + String? categoriesJson; Message( {this.mid, @@ -50,7 +55,9 @@ class Message { this.currentUid, this.client, this.answers, - this.answersJson}) + this.answersJson, + this.categories, + this.categoriesJson}) : super(); // @@ -91,6 +98,77 @@ class Message { answersJson: json['answers'].toString()); } + // + static Message fromJsonThreadWorkGroupV2(dynamic json) { + // + List categoriesList = []; + if (json['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT_V2) { + categoriesList = json['categories'] == null + ? [] + : (json['categories'] as List) + .map((item) => Category.fromJson(item)) + .toList(); + } + // + return Message( + mid: json['mid'], + content: json['content'], + imageUrl: json['imageUrl'], + voiceUrl: json['voiceUrl'], + fileUrl: json['fileUrl'], + videoUrl: json['videoOrShortUrl'], + nickname: json['user']['nickname'], + avatar: json['user']['avatar'], + type: json['type'], + timestamp: json['createdAt'], + status: 'stored', + isSend: 0, + currentUid: SpUtil.getString(BytedeskConstants.uid), + client: json['client'], + thread: Thread.fromVisitorJson(json['thread']), + user: User.fromJson(json['user']), + categories: categoriesList, + categoriesJson: json['categories'].toString()); + } + + // + static Message fromJsonRobotQuery(dynamic json) { + // + // String? content = json['content']; + List robotQaList = []; + if (json['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT) { + robotQaList = json['answers'] == null + ? [] + : (json['answers'] as List) + .map((item) => Answer.fromJson(item)) + .toList(); + // for (var i = 0; i < robotQaList.length; i++) { + // Answer answer = robotQaList[i]; + // content += '\n\n' + answer.aid + ':' + answer.question; + // } + } + // + return Message( + mid: json['mid'], + content: json['content'], + imageUrl: json['imageUrl'], + voiceUrl: json['voiceUrl'], + fileUrl: json['fileUrl'], + videoUrl: json['videoOrShortUrl'], + nickname: json['user']['nickname'], + avatar: json['user']['avatar'], + type: json['type'], + timestamp: json['createdAt'], + status: 'stored', + isSend: 0, + currentUid: SpUtil.getString(BytedeskConstants.uid), + client: json['client'], + // thread: Thread.fromVisitorJson(json['thread']), + user: User.fromJson(json['user']), + answers: robotQaList, + answersJson: json['answers'].toString()); + } + static Message fromJson(dynamic json) { List robotQaList = []; if (json['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT) { @@ -139,7 +217,8 @@ class Message { 'isSend': isSend, 'currentUid': currentUid, 'client': client, - 'answers': answersJson + 'answers': answersJson, + 'categories': categoriesJson }; } diff --git a/bytedesk_kefu/lib/model/messageProvider.dart b/bytedesk_kefu/lib/model/messageProvider.dart index ed13da1..8d223b7 100755 --- a/bytedesk_kefu/lib/model/messageProvider.dart +++ b/bytedesk_kefu/lib/model/messageProvider.dart @@ -1,6 +1,8 @@ import 'dart:async'; +import 'dart:convert'; import 'package:bytedesk_kefu/model/answer.dart'; +import 'package:bytedesk_kefu/model/category.dart'; import 'package:bytedesk_kefu/util/bytedesk_constants.dart'; import 'package:bytedesk_kefu/util/bytedesk_utils.dart'; import 'package:path/path.dart'; @@ -43,6 +45,7 @@ class MessageProvider { final String? columnCurrentUid = 'currentUid'; final String? columnClient = 'client'; final String? columnAnswers = 'answers'; + final String? columnCategories = 'categories'; // Database? database; @@ -52,19 +55,19 @@ class MessageProvider { // Set the path to the database. Note: Using the `join` function from the // `path` package is best practice to ensure the path is correctly // constructed for each platform. - join(await getDatabasesPath(), 'bytedesk-message-v9.db'), + join(await getDatabasesPath(), 'bytedesk-message-v10.db'), // When the database is first created, create a table to store dogs. onCreate: (db, version) { // Run the CREATE TABLE statement on the database. autoincrement return db.execute( "CREATE TABLE $tableMessage($columnId INTEGER PRIMARY KEY AUTOINCREMENT, " + "$columnMid TEXT, $columnType TEXT, $columnTopic TEXT, $columnContent TEXT, $columnImageUrl TEXT, $columnVoiceUrl TEXT,$columnVideoUrl TEXT, $columnFileUrl TEXT, $columnNickname TEXT, $columnAvatar TEXT, $columnStatus TEXT, " + - "$columnIsSend INTEGER, $columnTimestamp TEXT, $columnCurrentUid TEXT, $columnClient TEXT, $columnAnswers TEXT)", + "$columnIsSend INTEGER, $columnTimestamp TEXT, $columnCurrentUid TEXT, $columnClient TEXT, $columnAnswers TEXT, $columnCategories TEXT)", ); }, // Set the version. This executes the onCreate function and provides a // path to perform database upgrades and downgrades. - version: 9, + version: 10, ); // database path:/Users/ningjinpeng/Library/Developer/CoreSimulator/Devices/715CBA02-A602-4DE1-8C57-75A64B53BF03/data/Containers/Data/Application/8F46273D-9492-4C42-A618-4DF3815562BA/Documents/bytedesk-message-v9.db // print('database path:' + database!.path); @@ -104,7 +107,9 @@ class MessageProvider { columnNickname!, columnAvatar!, columnIsSend!, - columnClient! + columnClient!, + columnAnswers!, + columnCategories! ], where: '$columnTopic = ? and $columnCurrentUid = ?', whereArgs: [topic, currentUid], @@ -118,13 +123,23 @@ class MessageProvider { return List.generate(maps.length, (i) { // List robotQaList = []; - if (maps[i]['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT) { - robotQaList = maps[i]['answers'] == null - ? [] - : (maps[i]['answers'] as List) - .map((item) => Answer.fromJson(item)) - .toList(); - } + // FIXME: 下面解析报错 + // if (maps[i]['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT) { + // robotQaList = maps[i][columnAnswers] == null + // ? [] + // : (json.decode(maps[i][columnAnswers]) as List) + // .map((item) => Answer.fromJson(item)) + // .toList(); + // } + List categoriesList = []; + // FIXME: 下面解析报错 + // if (maps[i]['type'] == BytedeskConstants.MESSAGE_TYPE_ROBOT_V2) { + // categoriesList = maps[i][columnCategories] == null + // ? [] + // : (json.decode(maps[i][columnCategories]) as List) + // .map((item) => Category.fromJson(item)) + // .toList(); + // } // print('4'); return Message( mid: maps[i]['mid'], @@ -141,7 +156,8 @@ class MessageProvider { avatar: maps[i]['avatar'], isSend: maps[i]['isSend'], client: maps[i]['client'], - answers: robotQaList); + answers: robotQaList, + categories: categoriesList); }); } diff --git a/bytedesk_kefu/lib/model/requestAnswer.dart b/bytedesk_kefu/lib/model/requestAnswer.dart index c0a69c1..1a31f10 100755 --- a/bytedesk_kefu/lib/model/requestAnswer.dart +++ b/bytedesk_kefu/lib/model/requestAnswer.dart @@ -15,15 +15,15 @@ class RequestAnswerResult extends Equatable { return RequestAnswerResult( message: json["message"], statusCode: json["status_code"], - query: Message.fromJsonThread(json["data"]["query"]), - anwser: Message.fromJsonThread(json["data"]["reply"])); + query: Message.fromJsonRobotQuery(json["data"]["query"]), + anwser: Message.fromJsonRobotQuery(json["data"]["reply"])); } static RequestAnswerResult fromRateJson(dynamic json) { return RequestAnswerResult( message: json["message"], statusCode: json["status_code"], - anwser: Message.fromJsonThread(json["data"])); + anwser: Message.fromJsonRobotQuery(json["data"])); } @override diff --git a/bytedesk_kefu/lib/model/requestCategory.dart b/bytedesk_kefu/lib/model/requestCategory.dart new file mode 100755 index 0000000..cc8728e --- /dev/null +++ b/bytedesk_kefu/lib/model/requestCategory.dart @@ -0,0 +1,24 @@ +import 'package:equatable/equatable.dart'; +import 'message.dart'; + +class RequestCategoryResult extends Equatable { + final String? message; + final int? statusCode; + // + final Message? query; + final Message? anwser; + + RequestCategoryResult({this.message, this.statusCode, this.query, this.anwser}) + : super(); + + static RequestCategoryResult fromJson(dynamic json) { + return RequestCategoryResult( + message: json["message"], + statusCode: json["status_code"], + query: Message.fromJsonRobotQuery(json["data"]["query"]), + anwser: Message.fromJsonRobotQuery(json["data"]["reply"])); + } + + @override + List get props => [this.statusCode!]; +} diff --git a/bytedesk_kefu/lib/model/requestThread.dart b/bytedesk_kefu/lib/model/requestThread.dart index d987942..d3e7f4e 100755 --- a/bytedesk_kefu/lib/model/requestThread.dart +++ b/bytedesk_kefu/lib/model/requestThread.dart @@ -16,6 +16,13 @@ class RequestThreadResult extends Equatable { msg: Message.fromJsonThread(json["data"])); } + static RequestThreadResult fromJsonV2(dynamic json) { + return RequestThreadResult( + message: json["message"], + statusCode: json["status_code"], + msg: Message.fromJsonThreadWorkGroupV2(json["data"])); + } + @override List get props => [this.msg!.mid!]; } diff --git a/bytedesk_kefu/lib/model/thread.dart b/bytedesk_kefu/lib/model/thread.dart index a730fe7..ee29961 100755 --- a/bytedesk_kefu/lib/model/thread.dart +++ b/bytedesk_kefu/lib/model/thread.dart @@ -47,7 +47,8 @@ class Thread extends Equatable { this.nodisturbVisitor, this.unread, this.unreadVisitor, - this.client}); + this.client, + this.currentUid}); static Thread fromWorkGroupJson(dynamic json) { return Thread( @@ -61,6 +62,7 @@ class Thread extends Equatable { unreadCount: json['unreadCount'], type: json['type'], current: json['current'], + client: json['client'], top: json['top'], topVisitor: json['topVisitor'], nodisturb: json['nodisturb'], @@ -82,6 +84,7 @@ class Thread extends Equatable { unreadCount: json['unreadCount'], type: json['type'], current: json['current'], + client: json['client'], top: json['top'], topVisitor: json['topVisitor'], nodisturb: json['nodisturb'], @@ -100,6 +103,7 @@ class Thread extends Equatable { unreadCount: json['unreadCount'], type: json['type'], current: json['current'], + client: json['client'], top: json['top'], topVisitor: json['topVisitor'], nodisturb: json['nodisturb'], @@ -118,6 +122,7 @@ class Thread extends Equatable { unreadCount: json['unreadCount'], type: json['type'], current: json['current'], + client: json['client'], top: json['top'], topVisitor: json['topVisitor'], nodisturb: json['nodisturb'], @@ -137,6 +142,7 @@ class Thread extends Equatable { unreadCount: json['unreadCount'], type: json['type'], current: json['current'], + client: json['client'], top: json['top'], topVisitor: json['topVisitor'], nodisturb: json['nodisturb'], @@ -157,6 +163,7 @@ class Thread extends Equatable { unreadCount: json['unreadCount'], type: json['type'], current: json['current'], + client: json['client'], top: json['top'], topVisitor: json['topVisitor'], nodisturb: json['nodisturb'], @@ -177,6 +184,7 @@ class Thread extends Equatable { unreadCount: json['unreadCount'], type: json['type'], current: json['current'], + client: json['client'], top: json['top'], topVisitor: json['topVisitor'], nodisturb: json['nodisturb'], @@ -196,6 +204,7 @@ class Thread extends Equatable { unreadCount: json['unreadCount'], type: json['type'], current: json['current'], + client: json['client'], top: json['top'], topVisitor: json['topVisitor'], nodisturb: json['nodisturb'], @@ -215,6 +224,7 @@ class Thread extends Equatable { unreadCount: json['unreadCount'], type: json['type'], current: json['current'], + client: json['client'], top: json['top'], topVisitor: json['topVisitor'], nodisturb: json['nodisturb'], @@ -264,6 +274,7 @@ class Thread extends Equatable { unreadCount = map['unreadCount']; type = map['type']; current = map['current']; + client = map['client']; top = map['top']; topVisitor = map['topVisitor']; nodisturb = map['nodisturb']; diff --git a/bytedesk_kefu/lib/mqtt/bytedesk_mqtt.dart b/bytedesk_kefu/lib/mqtt/bytedesk_mqtt.dart index 538d624..47ee193 100755 --- a/bytedesk_kefu/lib/mqtt/bytedesk_mqtt.dart +++ b/bytedesk_kefu/lib/mqtt/bytedesk_mqtt.dart @@ -45,7 +45,7 @@ class BytedeskMqtt { factory BytedeskMqtt() { return _singleton; } - BytedeskMqtt._internal() {} + BytedeskMqtt._internal(); void connect() async { // eventbus发送广播,连接中... @@ -670,7 +670,8 @@ class BytedeskMqtt { currentThread, extraParam); } - void publish(String content, String type, Thread currentThread, ExtraParam? extraParam) { + void publish(String content, String type, Thread currentThread, + ExtraParam? extraParam) { // if (currentThread == null) { // print('连接客服失败,请退出页面重新进入。注意: 请在App启动的时候,调用init接口'); // Fluttertoast.showToast(msg: '连接客服失败,请退出页面重新进入'); @@ -689,6 +690,7 @@ class BytedeskMqtt { thread.topic = currentThread.topic!; thread.nickname = currentThread.nickname!; thread.avatar = currentThread.avatar!; + thread.client = currentThread.client!; thread.timestamp = BytedeskUtils.formatedDateNow(); // 没有必要填写,服务器端会填充 thread.unreadCount = 0; var extra = {'top': false, 'undisturb': false}; diff --git a/bytedesk_kefu/lib/repositories/message_repository.dart b/bytedesk_kefu/lib/repositories/message_repository.dart index 5ccd184..3ac8ac0 100755 --- a/bytedesk_kefu/lib/repositories/message_repository.dart +++ b/bytedesk_kefu/lib/repositories/message_repository.dart @@ -2,6 +2,7 @@ import 'package:bytedesk_kefu/http/bytedesk_message_api.dart'; import 'package:bytedesk_kefu/model/jsonResult.dart'; import 'package:bytedesk_kefu/model/message.dart'; import 'package:bytedesk_kefu/model/requestAnswer.dart'; +import 'package:bytedesk_kefu/model/requestCategory.dart'; import 'package:bytedesk_kefu/model/uploadJsonResult.dart'; class MessageRepository { @@ -28,13 +29,17 @@ class MessageRepository { return await bytedeskHttpApi.loadChannelMessages(cid, page, size); } - Future queryAnswer(String? tid, String? aid) async { - return await bytedeskHttpApi.queryAnswer(tid, aid); + Future queryAnswer(String? tid, String? aid, String? mid) async { + return await bytedeskHttpApi.queryAnswer2(tid, aid, mid); } - Future messageAnswer( - String? type, String? wid, String? aid, String? content) async { - return await bytedeskHttpApi.messageAnswer(type, wid, aid, content); + Future queryCategory( + String? tid, String? cid) async { + return await bytedeskHttpApi.queryCategory(tid, cid); + } + + Future messageAnswer(String? wid, String? content) async { + return await bytedeskHttpApi.messageAnswer(wid, content); } Future rateAnswer( diff --git a/bytedesk_kefu/lib/repositories/thread_repository.dart b/bytedesk_kefu/lib/repositories/thread_repository.dart index 63a56a1..3d4ba35 100755 --- a/bytedesk_kefu/lib/repositories/thread_repository.dart +++ b/bytedesk_kefu/lib/repositories/thread_repository.dart @@ -28,7 +28,10 @@ class ThreadRepository { } Future requestThread( - String? wid, String? type, String? aid) async { + String? wid, String? type, String? aid, bool? isV2Robot) async { + if (isV2Robot!) { + return await bytedeskHttpApi.requestWorkGroupThreadV2(wid); + } return await bytedeskHttpApi.requestThread(wid, type, aid); } diff --git a/bytedesk_kefu/lib/ui/chat/page/chat_im_page.dart b/bytedesk_kefu/lib/ui/chat/page/chat_im_page.dart index 6c363d1..913799d 100755 --- a/bytedesk_kefu/lib/ui/chat/page/chat_im_page.dart +++ b/bytedesk_kefu/lib/ui/chat/page/chat_im_page.dart @@ -500,9 +500,9 @@ class _ChatIMPageState extends State // 请求机器人答案 BlocProvider.of(context) ..add(MessageAnswerEvent( - type: widget.type, + // type: widget.type, wid: widget.wid, - aid: widget.aid, + // aid: widget.aid, content: text)); } else if (_bdMqtt.isConnected()) { if (_currentThread == null) { diff --git a/bytedesk_kefu/lib/ui/chat/page/chat_kf_page.dart b/bytedesk_kefu/lib/ui/chat/page/chat_kf_page.dart index e59d91a..405295d 100755 --- a/bytedesk_kefu/lib/ui/chat/page/chat_kf_page.dart +++ b/bytedesk_kefu/lib/ui/chat/page/chat_kf_page.dart @@ -40,6 +40,7 @@ class ChatKFPage extends StatefulWidget { final String? title; final String? custom; final String? postscript; + final bool? isV2Robot; // 从历史会话或者点击通知栏进入 final bool? isThread; final Thread? thread; @@ -53,6 +54,7 @@ class ChatKFPage extends StatefulWidget { this.title, this.custom, this.postscript, + this.isV2Robot, this.isThread, this.thread, this.customCallback}) @@ -89,6 +91,7 @@ class _ChatKFPageState extends State String? _currentAvatar = SpUtil.getString(BytedeskConstants.avatar); // 当前会话 Thread? _currentThread; + User? _robotUser; // 判断是否机器人对话状态 bool _isRobot = false; // 分页加载聊天记录 @@ -189,11 +192,8 @@ class _ChatKFPageState extends State _currentThread = state.threadResult.msg!.thread; _isRequestingThread = false; }); - // 插入本地 - // _messageProvider.insert(state.threadResult.msg!); // TODO: 加载本地历史消息 _getMessages(_page, _size); - // _appendMessage(state.threadResult.msg!); // if (state.threadResult.statusCode == 200 || state.threadResult.statusCode == 201) { @@ -286,6 +286,7 @@ class _ChatKFPageState extends State // TODO: 显示问题列表 setState(() { _isRobot = true; + _robotUser = state.threadResult.msg!.user; _currentThread = state.threadResult.msg!.thread; }); // 插入本地 @@ -347,20 +348,25 @@ class _ChatKFPageState extends State } else if (state is UploadVideoSuccess) { _bdMqtt.sendVideoMessage( state.uploadJsonResult.url!, _currentThread!); - // TODO: 调用http rest接口发送消息 sendImageRest } else if (state is QueryAnswerSuccess) { - Message queryMessage = state.query!; - queryMessage.isSend = 1; - _messageProvider.insert(queryMessage); - _appendMessage(queryMessage); - // + // 已经修改为直接插入本地 + // Message queryMessage = state.query!; + // queryMessage.isSend = 1; + // _messageProvider.insert(queryMessage); + // _appendMessage(queryMessage); + // // + // _messageProvider.insert(state.answer!); + // _appendMessage(state.answer!); + } else if (state is QueryCategorySuccess) { + _messageProvider.insert(state.answer!); _appendMessage(state.answer!); + } else if (state is MessageAnswerSuccess) { - Message queryMessage = state.query!; - queryMessage.isSend = 1; - _messageProvider.insert(queryMessage); - _appendMessage(queryMessage); + // Message queryMessage = state.query!; + // queryMessage.isSend = 1; + // _messageProvider.insert(queryMessage); + // _appendMessage(queryMessage); // if (state.query!.content!.contains('人工')) { BlocProvider.of(context) @@ -375,7 +381,7 @@ class _ChatKFPageState extends State } else if (state is RateAnswerSuccess) { // TODO: } else if (state is LoadHistoryMessageSuccess) { - print('LoadHistoryMessageSuccess'); + // print('LoadHistoryMessageSuccess'); // 插入历史聊天记录 for (var i = 0; i < state.messageList!.length; i++) { Message message = state.messageList![i]; @@ -490,7 +496,7 @@ class _ChatKFPageState extends State if (_debounce?.isActive ?? false) _debounce!.cancel(); // 积累500毫秒,再发送。否则发送过于频繁 _debounce = Timer(const Duration(milliseconds: 500), () { - print('send preview $value'); + // print('send preview $value'); // 发送预知消息 if (value.trim().length > 0) { _bdMqtt.sendPreviewMessage(value, _currentThread!); @@ -530,12 +536,14 @@ class _ChatKFPageState extends State } // if (_isRobot) { + // + appendQueryMessage(text); // 请求机器人答案 BlocProvider.of(context) ..add(MessageAnswerEvent( - type: widget.type, + // type: widget.type, wid: widget.wid, - aid: widget.aid, + // aid: widget.aid, content: text)); } else if (_bdMqtt.isConnected()) { if (_currentThread == null) { @@ -545,7 +553,7 @@ class _ChatKFPageState extends State // 长连接正常情况下,调用长连接接口 _bdMqtt.sendTextMessage(text, _currentThread!); } else { - print('长连接断开的情况下,调用rest接口'); + // print('长连接断开的情况下,调用rest接口'); sendTextMessageRest(text); } } @@ -553,7 +561,7 @@ class _ChatKFPageState extends State // http rest 接口发生文本消息 void sendTextMessageRest(String text) { // - String? mid = BytedeskUuid.generateV4(); + String? mid = BytedeskUuid.uuid(); String? timestamp = BytedeskUtils.formatedDateNow(); String? client = BytedeskUtils.getClient(); String? type = BytedeskConstants.MESSAGE_TYPE_TEXT; @@ -564,6 +572,7 @@ class _ChatKFPageState extends State "client": client, "version": "1", "type": type, + "status": "sending", "user": { "uid": this._currentUid, "nickname": this._currentNickname, @@ -606,12 +615,14 @@ class _ChatKFPageState extends State message.content = text; // 插入本地数据库 _messageProvider.insert(message); + // + pushToMessageArray(message, true); } // http rest 接口发送图片消息 void sendImageMessageRest(String imageUrl) { // - String? mid = BytedeskUuid.generateV4(); + String? mid = BytedeskUuid.uuid(); String? timestamp = BytedeskUtils.formatedDateNow(); String? client = BytedeskUtils.getClient(); String? type = BytedeskConstants.MESSAGE_TYPE_IMAGE; @@ -622,6 +633,7 @@ class _ChatKFPageState extends State "client": client, "version": "1", "type": type, + "status": "sending", "user": { "uid": this._currentUid, "nickname": this._currentNickname, @@ -664,6 +676,73 @@ class _ChatKFPageState extends State message.imageUrl = imageUrl; // 插入本地数据库 _messageProvider.insert(message); + // + pushToMessageArray(message, true); + } + + void appendQueryMessage(String content) { + // + String? mid = BytedeskUuid.uuid(); + String? timestamp = BytedeskUtils.formatedDateNow(); + String? client = BytedeskUtils.getClient(); + String? type = BytedeskConstants.MESSAGE_TYPE_ROBOT; + // + // 暂时没有将插入本地函数独立出来,暂时 + Message message = new Message(); + message.mid = mid; + message.type = type; + message.timestamp = timestamp; + message.client = client; + message.nickname = _currentNickname; + message.avatar = _currentAvatar; + message.topic = this._currentThread!.topic; + message.status = BytedeskConstants.MESSAGE_STATUS_STORED; + message.isSend = 1; + message.currentUid = this._currentUid; + message.answersJson = ''; + message.thread = this._currentThread; + message.user = User( + uid: this._currentUid, + avatar: this._currentAvatar, + nickname: this._currentNickname); + // + message.content = content; + // 插入本地数据库 + _messageProvider.insert(message); + // + pushToMessageArray(message, true); + } + + void appendReplyMessage(String aid, String mid, String content) { + // + String? timestamp = BytedeskUtils.formatedDateNow(); + String? client = BytedeskUtils.getClient(); + String? type = BytedeskConstants.MESSAGE_TYPE_ROBOT_RESULT; + // + // 暂时没有将插入本地函数独立出来,暂时R + Message message = new Message(); + message.mid = mid; + message.type = type; + message.timestamp = timestamp; + message.client = client; + message.nickname = _robotUser!.nickname; + message.avatar = _robotUser!.avatar; + message.topic = this._currentThread!.topic; + message.status = BytedeskConstants.MESSAGE_STATUS_STORED; + message.isSend = 0; + message.currentUid = this._currentUid; + message.answersJson = ''; + message.thread = this._currentThread; + message.user = User( + uid: this._robotUser!.uid, + avatar: this._robotUser!.avatar, + nickname: this._robotUser!.nickname); + // + message.content = content; + // 插入本地数据库 + _messageProvider.insert(message); + // + pushToMessageArray(message, true); } // @@ -731,18 +810,19 @@ class _ChatKFPageState extends State event.message.mid!, event.message.thread!); } // - if (this.mounted) { - // 界面显示 - MessageWidget messageWidget = new MessageWidget( - message: event.message, - customCallback: widget.customCallback, - animationController: new AnimationController( - vsync: this, duration: Duration(milliseconds: 500))); - setState(() { - _messages.insert(0, messageWidget); - // _messages.add(messageWidget); - }); - } + pushToMessageArray(event.message, true); + // if (this.mounted) { + // // 界面显示 + // MessageWidget messageWidget = new MessageWidget( + // message: event.message, + // customCallback: widget.customCallback, + // animationController: new AnimationController( + // vsync: this, duration: Duration(milliseconds: 500))); + // setState(() { + // _messages.insert(0, messageWidget); + // // _messages.add(messageWidget); + // }); + // } }); // 删除消息 bytedeskEventBus.on().listen((event) { @@ -761,11 +841,26 @@ class _ChatKFPageState extends State if (this.mounted) { // print('aid ${event.aid}, question ${event.question}, answer ${event.answer}'); // 可以直接将问题和答案插入本地,并显示,但为了服务器保存查询记录,特将请求发送给服务器 + appendQueryMessage(event.question); + // + String? mid = BytedeskUuid.uuid(); + appendReplyMessage(event.aid, mid, event.answer); + // BlocProvider.of(context) ..add(QueryAnswerEvent( - tid: _currentThread!.tid, - aid: event.aid, - )); + tid: _currentThread!.tid, aid: event.aid, mid: mid)); + } + }); + // 查询分类下属问题 + bytedeskEventBus.on().listen((event) { + if (this.mounted) { + print('cid ${event.cid}, name ${event.name}'); + // 可以直接将问题和答案插入本地,并显示,但为了服务器保存查询记录,特将请求发送给服务器 + appendQueryMessage(event.name); + // + BlocProvider.of(context) + ..add(QueryCategoryEvent( + tid: _currentThread!.tid, cid: event.cid)); } }); // 点击机器人消息 ‘人工客服’ @@ -971,30 +1066,45 @@ class _ChatKFPageState extends State BytedeskConstants.MESSAGE_TYPE_NOTIFICATION_THREAD_REENTRY) { continue; } else { - pushToMessageArray(message); + pushToMessageArray(message, false); } } } else { - pushToMessageArray(message); + pushToMessageArray(message, false); } } // _page += 1; } - void pushToMessageArray(Message message) { + void pushToMessageArray(Message message, bool append) { if (this.mounted) { - MessageWidget messageWidget = new MessageWidget( - message: message, - customCallback: widget.customCallback, - animationController: new AnimationController( - vsync: this, duration: Duration(milliseconds: 500))); - setState(() { - _messages.add(messageWidget); - _messages.sort((a, b) { - return b.message!.timestamp!.compareTo(a.message!.timestamp!); + bool contains = false; + for (var i = 0; i < _messages.length; i++) { + Message? element = _messages[i].message; + if (element!.mid == message.mid) { + contains = true; + // 更新消息状态 + _messageProvider.update(element.mid, message.status); + } + } + if (!contains) { + MessageWidget messageWidget = new MessageWidget( + message: message, + customCallback: widget.customCallback, + animationController: new AnimationController( + vsync: this, duration: Duration(milliseconds: 500))); + setState(() { + if (append) { + _messages.insert(0, messageWidget); + } else { + _messages.add(messageWidget); + _messages.sort((a, b) { + return b.message!.timestamp!.compareTo(a.message!.timestamp!); + }); + } }); - }); + } } if (message.status != BytedeskConstants.MESSAGE_STATUS_READ) { // 发送已读回执 @@ -1005,29 +1115,8 @@ class _ChatKFPageState extends State } Future _appendMessage(Message message) async { - // print('append:' + message.mid! + 'content:' + message.content!); - bool contains = false; - for (var i = 0; i < _messages.length; i++) { - Message? element = _messages[i].message; - if (element!.mid == message.mid) { - contains = true; - // 更新消息状态 - _messageProvider.update(element.mid, message.status); - } - } - if (!contains) { - // _messageProvider.insert(message); // 造成重复插入 - MessageWidget messageWidget = new MessageWidget( - message: message, - customCallback: widget.customCallback, - animationController: new AnimationController( - vsync: this, duration: Duration(milliseconds: 500))); - if (this.mounted) { - setState(() { - _messages.insert(0, messageWidget); - }); - } - } + print('append:' + message.mid! + 'content:' + message.content!); + pushToMessageArray(message, true); } void scrollToBottom() { diff --git a/bytedesk_kefu/lib/ui/chat/page/chat_ls_page.dart b/bytedesk_kefu/lib/ui/chat/page/chat_ls_page.dart index ac1fe83..89603e2 100755 --- a/bytedesk_kefu/lib/ui/chat/page/chat_ls_page.dart +++ b/bytedesk_kefu/lib/ui/chat/page/chat_ls_page.dart @@ -469,9 +469,9 @@ class _ChatLSPageState extends State if (_isRobot) { BlocProvider.of(context) ..add(MessageAnswerEvent( - type: widget.type, + // type: widget.type, wid: widget.wid, - aid: widget.aid, + // aid: widget.aid, content: text)); } else { if (_currentThread == null) { diff --git a/bytedesk_kefu/lib/ui/chat/provider/chat_kf_provider.dart b/bytedesk_kefu/lib/ui/chat/provider/chat_kf_provider.dart index cc61ff7..bde8329 100755 --- a/bytedesk_kefu/lib/ui/chat/provider/chat_kf_provider.dart +++ b/bytedesk_kefu/lib/ui/chat/provider/chat_kf_provider.dart @@ -12,6 +12,7 @@ class ChatKFProvider extends StatelessWidget { final String? title; final String? custom; final String? postscript; + final bool? isV2Robot; final ValueSetter? customCallback; // const ChatKFProvider( @@ -22,6 +23,7 @@ class ChatKFProvider extends StatelessWidget { this.title, this.custom, this.postscript, + this.isV2Robot, this.customCallback}) : super(key: key); // @@ -32,7 +34,7 @@ class ChatKFProvider extends StatelessWidget { providers: [ BlocProvider( create: (BuildContext context) => ThreadBloc() - ..add(RequestThreadEvent(wid: wid, aid: aid, type: type)), + ..add(RequestThreadEvent(wid: wid, aid: aid, type: type, isV2Robot: isV2Robot)), ), BlocProvider( create: (BuildContext context) => MessageBloc(), @@ -45,6 +47,7 @@ class ChatKFProvider extends StatelessWidget { title: title, custom: custom, postscript: postscript, + isV2Robot: isV2Robot, isThread: false, customCallback: customCallback), ); diff --git a/bytedesk_kefu/lib/ui/chat/widget/message_widget.dart b/bytedesk_kefu/lib/ui/chat/widget/message_widget.dart index a34fb21..c1e2594 100755 --- a/bytedesk_kefu/lib/ui/chat/widget/message_widget.dart +++ b/bytedesk_kefu/lib/ui/chat/widget/message_widget.dart @@ -41,12 +41,16 @@ class MessageWidget extends StatelessWidget { double tWidth = MediaQuery.of(context).size.width - 160; // FIXME: 消息状态,待完善 String status = ''; - if (message!.status == BytedeskConstants.MESSAGE_STATUS_STORED) { - // status = '发送成功'; + if (message!.status == BytedeskConstants.MESSAGE_STATUS_SENDING) { + status = '发送中'; + } else if (message!.status == BytedeskConstants.MESSAGE_STATUS_STORED) { + status = ''; // 发送成功 } else if (message!.status == BytedeskConstants.MESSAGE_STATUS_RECEIVED) { status = '送达'; } else if (message!.status == BytedeskConstants.MESSAGE_STATUS_READ) { status = '已读'; + } else if (message!.status == BytedeskConstants.MESSAGE_STATUS_ERROR) { + status = '失败'; } return Container( margin: EdgeInsets.only(top: 8.0, left: 8.0), @@ -450,43 +454,46 @@ class MessageWidget extends StatelessWidget { // softWrap: true, // style: TextStyle(color: Colors.black, fontSize: 16.0), // ), - Html( - data: message.content ?? '', - onLinkTap: (url, _, __, ___) { - // 打开url - BytedeskKefu.openWebView(context, url!, '网页'); - }, - onImageTap: (src, _, __, ___) { - // 查看大图 - // print("open image $src"); - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => PhotoViewWrapper( - imageUrl: message.imageUrl!, - imageProvider: NetworkImage( - src!, - ), - loadingBuilder: (context, event) { - if (event == null) { - return const Center( - child: Text("Loading"), + Visibility( + visible: message.content != null && message.content!.length > 0, + child: Html( + data: message.content ?? '', + onLinkTap: (url, _, __, ___) { + // 打开url + BytedeskKefu.openWebView(context, url!, '网页'); + }, + onImageTap: (src, _, __, ___) { + // 查看大图 + // print("open image $src"); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => PhotoViewWrapper( + imageUrl: message.imageUrl!, + imageProvider: NetworkImage( + src!, + ), + loadingBuilder: (context, event) { + if (event == null) { + return const Center( + child: Text("Loading"), + ); + } + final value = event.cumulativeBytesLoaded / + event.expectedTotalBytes!; + final percentage = (100 * value).floor(); + return Center( + child: Text("$percentage%"), ); - } - final value = event.cumulativeBytesLoaded / - event.expectedTotalBytes!; - final percentage = (100 * value).floor(); - return Center( - child: Text("$percentage%"), - ); - }, + }, + ), ), - ), - ); - }, - onImageError: (exception, stackTrace) { - print(exception); - }, + ); + }, + onImageError: (exception, stackTrace) { + print(exception); + }, + ), ), Visibility( visible: message.answers != null && message.answers!.length > 0, @@ -549,6 +556,88 @@ class MessageWidget extends StatelessWidget { ) ], ); + } else if (message.type == BytedeskConstants.MESSAGE_TYPE_ROBOT_V2) { + return Column( + children: [ + Text( + message.content ?? '', + textAlign: TextAlign.left, + softWrap: true, + style: TextStyle( + color: Colors.black, + fontSize: 16.0, + ), + ), + Visibility( + visible: message.categories != null && message.categories!.length > 0, + // visible: false, + child: Container( + // color: Colors.black, + child: ListView.builder( + // 如果滚动视图在滚动方向无界约束,那么shrinkWrap必须为true + shrinkWrap: true, + // 禁用ListView滑动,使用外层的ScrollView滑动 + physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.all(0), + itemCount: + message.categories == null ? 0 : message.categories!.length, + itemBuilder: (_, index) { + // + var category = message.categories![index]; + // return Text(answer.question!); + return DecoratedBox( + decoration: BoxDecoration( + border: Border( + bottom: Divider.createBorderSide(context, width: 0.8), + )), + child: Container( + margin: EdgeInsets.only(top: 6, left: 8, bottom: 8), + // color: Colors.pink, + child: InkWell( + child: Text( + category.name!, + style: TextStyle(color: Colors.blue), + ), + onTap: () => { + // print(category.name), + bytedeskEventBus.fire(QueryCategoryEventBus( + category.cid!, + category.name!)) + }), + )); + }, + ), + )), + Container( + margin: EdgeInsets.only(left: 10, top: 10), + child: Row( + children: [ + Text('没有找到答案?'), + GestureDetector( + child: Text( + '人工客服', + style: TextStyle(color: Theme.of(context).primaryColor), + ), + onTap: () { + print('请求人工客服'); + bytedeskEventBus.fire(RequestAgentThreadEventBus()); + }, + ) + ], + ), + ) + ], + ); + } else if (message.type == BytedeskConstants.MESSAGE_TYPE_ROBOT_RESULT) { + return Text( + message.content ?? '', + textAlign: TextAlign.left, + softWrap: true, + style: TextStyle( + color: Colors.black, + fontSize: 16.0, + ), + ); } else if (message.type == BytedeskConstants.MESSAGE_TYPE_COMMODITY) { // 商品信息, TODO: add send button final commodityJson = json.decode(message.content!); diff --git a/bytedesk_kefu/lib/ui/feedback/page/feedback_page.dart b/bytedesk_kefu/lib/ui/feedback/page/feedback_page.dart index 9f6846b..beca4aa 100755 --- a/bytedesk_kefu/lib/ui/feedback/page/feedback_page.dart +++ b/bytedesk_kefu/lib/ui/feedback/page/feedback_page.dart @@ -6,7 +6,7 @@ import 'package:bytedesk_kefu/ui/widget/loading_widget.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_easyrefresh/easy_refresh.dart'; -import 'package:flutter_easyrefresh/phoenix_header.dart'; +// import 'package:flutter_easyrefresh/phoenix_header.dart'; class FeedbackPage extends StatefulWidget { final String? uid; diff --git a/bytedesk_kefu/lib/util/bytedesk_constants.dart b/bytedesk_kefu/lib/util/bytedesk_constants.dart index 4994242..e2b4704 100755 --- a/bytedesk_kefu/lib/util/bytedesk_constants.dart +++ b/bytedesk_kefu/lib/util/bytedesk_constants.dart @@ -45,7 +45,7 @@ class BytedeskConstants { // static const String httpBaseUrliOS = 'http://' + mqttHost + ':8000'; // static const String httpUploadUrl = 'http://' + mqttHost + ':8000'; // static const String host = mqttHost + ':8000'; - // static const String mqttHost = '192.168.0.102'; + // static const String mqttHost = '192.168.0.104'; // 线上 static const bool isDebug = false; // false; @@ -149,6 +149,10 @@ class BytedeskConstants { static const String MESSAGE_TYPE_EVENT = 'event'; // 机器人 自动回复 static const String MESSAGE_TYPE_ROBOT = 'robot'; + // + static const String MESSAGE_TYPE_ROBOT_V2 = 'robotv2'; + // + static const String MESSAGE_TYPE_ROBOT_RESULT = 'robot_result'; // 问卷 static const String MESSAGE_TYPE_QUESTIONNAIRE = 'questionnaire'; // 分公司,方便提取分公司所包含的国家,金吉列大学长 diff --git a/bytedesk_kefu/lib/util/bytedesk_events.dart b/bytedesk_kefu/lib/util/bytedesk_events.dart index 23edf2a..e4db79f 100755 --- a/bytedesk_kefu/lib/util/bytedesk_events.dart +++ b/bytedesk_kefu/lib/util/bytedesk_events.dart @@ -46,6 +46,12 @@ class QueryAnswerEventBus { QueryAnswerEventBus(this.aid, this.question, this.answer); } +class QueryCategoryEventBus { + String cid; + String name; + QueryCategoryEventBus(this.cid, this.name); +} + class RequestAgentThreadEventBus { RequestAgentThreadEventBus(); } diff --git a/bytedesk_kefu/lib/util/bytedesk_utils.dart b/bytedesk_kefu/lib/util/bytedesk_utils.dart index f9c666b..381b6f4 100755 --- a/bytedesk_kefu/lib/util/bytedesk_utils.dart +++ b/bytedesk_kefu/lib/util/bytedesk_utils.dart @@ -1,3 +1,5 @@ +// ignore_for_file: unnecessary_null_comparison + import 'dart:io'; import 'dart:math'; @@ -219,7 +221,7 @@ class BytedeskUtils { } static String getDateStr(DateTime date) { - if (date == null || date.toString() == null) { + if (date.toString() == null) { return ""; } else if (date.toString().length < 10) { return date.toString(); diff --git a/bytedesk_kefu/pubspec.yaml b/bytedesk_kefu/pubspec.yaml index f8ee640..72aab18 100644 --- a/bytedesk_kefu/pubspec.yaml +++ b/bytedesk_kefu/pubspec.yaml @@ -1,6 +1,6 @@ name: bytedesk_kefu description: the best app chat sdk in china, you can chat with the agent freely at anytime. the agent can chat with the visitor by web/pc/mac/ios/android client. -version: 1.2.6 +version: 1.2.8 homepage: https://www.weikefu.net environment: