Chat.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. <?php
  2. namespace app\controller\v2;
  3. use app\extra\basic\Base;
  4. use app\extra\tools\UploadExtend;
  5. use app\middleware\AuthMiddleware;
  6. use app\model\saas\SaasChatMsg;
  7. use app\model\saas\SaasChatStore;
  8. use app\model\saas\SaasService;
  9. use app\model\saas\SaasStore;
  10. use app\model\saas\SaasUserOpen;
  11. use app\model\system\SystemUser;
  12. use app\service\saas\ChatStoreService;
  13. use DI\Attribute\Inject;
  14. use LinFly\Annotation\Attributes\Route\Controller;
  15. use LinFly\Annotation\Attributes\Route\GetMapping;
  16. use LinFly\Annotation\Attributes\Route\Middleware;
  17. use LinFly\Annotation\Attributes\Route\PostMapping;
  18. use support\Request;
  19. use support\Response;
  20. use Webman\Push\Api;
  21. #[Controller("/v2/chat"),Middleware(AuthMiddleware::class)]
  22. class Chat extends Base
  23. {
  24. #[Inject]
  25. protected ChatStoreService $service;
  26. /**
  27. * 获取客服列表-已聊过的
  28. * @param Request $request
  29. * @return Response
  30. */
  31. #[GetMapping("list")]
  32. public function getServiceList(Request $request): Response
  33. {
  34. try {
  35. $param = $request->all();
  36. if (!empty($param['size'])) {
  37. $param['pageSize'] = $param['size'];
  38. }
  39. $param['openid'] = $request->user['openid'];
  40. $list = $this->service->setModel()->getList($param,null,true,['last','avatar'],['last' => function($data,$resp) use($param){
  41. $last = (new SaasChatMsg)->where(['poi_id' => $resp['poi_id'],'openid' => $param['openid']])->order("create_at","desc")->field("content,create_at,type")->findOrEmpty();
  42. if ($last->isEmpty()) {
  43. return ['type' => "text","content"=>"-",'num' => 0,"create_at"=> formatTime(date("Y-m-d H:i:s",time()))];
  44. }
  45. return ["content"=> str_cut_ellipsis($last['content'],12),'num' => 0,"create_at"=> formatTime($last['create_at']),'type' => $last['type']];
  46. },'avatar' => function(){
  47. return sConf("service.logo");
  48. }]);
  49. $user = (new SaasUserOpen)->where(['openid' => $param['openid']])->findOrEmpty();
  50. if ($user->isEmpty()) return error("数据错误");
  51. $userState = 1;
  52. if (empty($user['avatar'])) {
  53. $userState = 0;
  54. }
  55. return successTrans("success.data",['data' => pageFormat($list),'user' => $userState]);
  56. } catch (\Throwable $throwable) {
  57. return error($throwable->getMessage());
  58. }
  59. }
  60. /**
  61. * 获取基础客服消息
  62. */
  63. #[GetMapping("service")]
  64. public function getChatStore(Request $request): Response
  65. {
  66. try {
  67. $param = $this->_valid([
  68. "poi.require" => "参数错误",
  69. "size.require" => "参数错误",
  70. "page.require" => "参数错误",
  71. "goods.default" => "",
  72. "order.default" => "",
  73. ],$request->method());
  74. if (!is_array($param)) return error($param);
  75. $store = (new SaasStore)->where("poi_id",$param['poi'])->findOrEmpty();
  76. if ($store->isEmpty()) return error("店铺不存在");
  77. $userAvatar = $request->user['avatar'];
  78. // 获取最新的10条聊天记录
  79. $msg = (new SaasChatMsg)->where(['openid' => $request->user['openid'],'poi_id' => $store['poi_id']])
  80. ->append(['time','userType','msgType'])
  81. ->withAttr(['time' => function($resp,$data){
  82. return strtotime($data['create_at']) * 1000;
  83. },'userType' => function($resp,$data){
  84. if ($data['source'] == 1) {
  85. return "self";
  86. } else {
  87. return "friend";
  88. }
  89. },'msgType' => function($resp,$data){
  90. return $data['type'];
  91. }])
  92. ->order("id","desc")
  93. ->paginate([
  94. "list_rows" => $param['size'],
  95. "page" => 1
  96. ]);
  97. $serviceAvatar = sConf("service.logo");
  98. $star = (new SaasService)->where("poi_id",$param['poi'])->with(['quick'])->findOrEmpty();
  99. // 获取在线客服
  100. $service = (new SystemUser)->where(['store_id' => $param['poi'],"type" => 3,"is_line" => 1])->order("last_msg_at","asc")->findOrEmpty();
  101. if ($service->isEmpty()) { // 无客服在线
  102. return success("ok",[
  103. "avatar" => $userAvatar,
  104. "userId" => md5($request->user['openid']),
  105. "msg" => $msg,
  106. "service" => $serviceAvatar,
  107. "store" => [
  108. "shopName" => $store['poi_name'],
  109. "shopId" => $store['poi_id'],
  110. ],
  111. "quick" => $star,
  112. "chat_id" => 0, // 客服ID
  113. ]);
  114. }
  115. $chatStore = (new SaasChatStore)->where(['poi_id'=> $store['poi_id'],"openid" => $request->user['openid']])->findOrEmpty();
  116. if ($chatStore->isEmpty()) {
  117. $chatStore->insertGetId([
  118. "poi_id" => $store['poi_id'],
  119. "poi_name" => $store['poi_name'],
  120. "openid" => $request->user['openid'],
  121. "nickname" => $request->user['nickname'],
  122. "service_id" => $service['id'],
  123. "order" => $param['order']??'',
  124. "goods" => $param['goods']??'',
  125. "last_at" => getDateFull()
  126. ]);
  127. } else {
  128. $chatStore->service_id = $service['id'];
  129. $chatStore->order = $param['order']??'';
  130. $chatStore->nickname = $request->user['nickname'];
  131. $chatStore->goods = $param['goods']??'';
  132. $chatStore->last_at = getDateFull();
  133. $chatStore->save();
  134. }
  135. return success("ok",[
  136. "avatar" => $userAvatar,
  137. "userId" => md5($request->user['openid']),
  138. "msg" => $msg,
  139. "service" => $serviceAvatar,
  140. "store" => [
  141. "shopName" => $store['poi_name'],
  142. "shopId" => $store['poi_id'],
  143. ],
  144. "quick" => $star,
  145. "chat_id" => $service['id'], // 客服ID
  146. ]);
  147. } catch (\Throwable $throwable) {
  148. return error($throwable->getMessage());
  149. }
  150. }
  151. /**
  152. * 历史聊天列表
  153. * @param Request $request
  154. * @return Response
  155. */
  156. #[GetMapping("msg")]
  157. public function getMsgList(Request $request): Response
  158. {
  159. try {
  160. $param = $this->_valid([
  161. "poi.require" => "参数错误",
  162. "size.require" => "参数错误",
  163. "page.require" => "参数错误",
  164. ],$request->method());
  165. if (!is_array($param)) return error($param);
  166. $store = (new SaasStore)->where("poi_id",$param['poi'])->findOrEmpty();
  167. if ($store->isEmpty()) return error("店铺不存在");
  168. $msg = (new SaasChatMsg)->where(['openid' => $request->user['openid'],'poi_id' => $store['poi_id']])
  169. ->append(['time','userType','msgType'])
  170. ->withAttr(['time' => function($resp,$data){
  171. return strtotime($data['create_at']) * 1000;
  172. },'userType' => function($resp,$data){
  173. if ($data['source'] == 1) {
  174. return "self";
  175. } else {
  176. return "friend";
  177. }
  178. },'msgType' => function($resp,$data){
  179. return $data['type'];
  180. }])
  181. ->order("id","desc")
  182. ->paginate([
  183. "list_rows" => $param['size'],
  184. "page" => $param['page']
  185. ]);
  186. return success("ok",['data' => $msg]);
  187. } catch (\Throwable $throwable) {
  188. return error($throwable->getMessage());
  189. }
  190. }
  191. /**
  192. * 转人工
  193. * @return Response|void
  194. */
  195. public function checkServiceUser()
  196. {
  197. try {
  198. } catch (\Throwable $throwable) {
  199. return error($throwable->getMessage());
  200. }
  201. }
  202. /**
  203. * 发送文字消息
  204. * @param Request $request
  205. * @return Response
  206. */
  207. #[PostMapping("text")]
  208. public function sendTextMsg(Request $request): Response
  209. {
  210. try {
  211. $param = $this->_valid([
  212. "content.require" => trans("empty.require"),
  213. "groupId.require" => trans("empty.require"),
  214. "chatId.require" => trans("empty.require"),
  215. "type.require" => trans("empty.require")
  216. ],"post");
  217. if (!is_array($param)) return error($param);
  218. (new SaasChatMsg)->insertGetId([
  219. "source" => 1,
  220. "openid" => $request->user['openid'],
  221. "content" => is_array($param['content'])?json_encode($param['content']):$param['content'],
  222. "type" => $param['type'],
  223. "msgId" => time(),
  224. "poi_id" => $param['groupId'],
  225. "service_id" => $param['chatId'], // 客服ID
  226. ]);
  227. $chatStore = (new SaasChatStore)->where(['openid' => $request->user['openid'],"service_id" => $param['chatId']])->with(['user' => function($query){
  228. $query->field("openid,nickname,avatar,mobile,create_at");
  229. }])->append(['avatar'])->withAttr(['avatar' => function () {
  230. return sConf("service.logo");
  231. }])->findOrEmpty();
  232. $total = (new SaasChatMsg)->where(["poi_id" => $chatStore['poi_id'],"service_id" => $param['chatId'],"openid" => $chatStore['openid'],'is_read' => 0])->count();
  233. if (!$chatStore->isEmpty()) {
  234. $chatStore->last_at = getDateFull();
  235. $chatStore->save();
  236. $chatStore['lastTime'] = formatTime(getDateFull());
  237. $chatStore['last'] = [
  238. "content" => is_array($param['content'])?json_encode($param['content']):str_cut_ellipsis($param['content']),
  239. "type" => $param['type'],
  240. "num" => $total
  241. ];
  242. }
  243. // 推送消息
  244. $api = new Api('http://127.0.0.1:3232', config('plugin.webman.push.app.app_key'),config('plugin.webman.push.app.app_secret'));
  245. $api->trigger("service-{$param['chatId']}","message",[
  246. "type" => $param['type'],
  247. "content" => is_array($param['content'])?json_encode($param['content']):$param['content'],
  248. "openid" => $request->user['openid'],
  249. "nick" => [],
  250. "user" => [],
  251. "userType" => "friend",
  252. "avatar" => $request->user['avatar'],
  253. "time" => time() * 1000,
  254. "msgId" => md5($request->user['openid'].time()),
  255. "item" => $chatStore->toArray()
  256. ]);
  257. return successTrans("success.data");
  258. } catch (\Throwable $throwable) {
  259. return error($throwable->getMessage());
  260. }
  261. }
  262. /**
  263. * 发送图片消息
  264. * @param Request $request
  265. * @return Response
  266. */
  267. #[PostMapping("image")]
  268. public function sendImageMsg(Request $request): Response
  269. {
  270. try {
  271. $param = $this->_valid([
  272. "groupId.require" => trans("empty.require"),
  273. "type.require" => trans("empty.require"),
  274. "chatId.require" => trans("empty.require"),
  275. ],"post");
  276. if (!is_array($param)) return error($param);
  277. $resp = UploadExtend::uploadFile();
  278. if (!isset($resp[0]['url'])) return error("发送失败");
  279. $param['content'] = $resp[0]['url'];
  280. (new SaasChatMsg)->insertGetId([
  281. "source" => 1,
  282. "openid" => $request->user['openid'],
  283. "content" => is_array($param['content'])?json_encode($param['content']):$param['content'],
  284. "type" => $param['type'],
  285. "msgId" => time(),
  286. "poi_id" => $param['groupId'],
  287. "service_id" => $param['chatId'], // 客服ID
  288. ]);
  289. $chatStore = (new SaasChatStore)->where(['openid' => $request->user['openid'],"service_id" => $param['chatId']])->with(['user' => function($query){
  290. $query->field("openid,nickname,avatar,mobile,create_at");
  291. }])->append(['avatar'])->withAttr(['avatar' => function () {
  292. return sConf("service.logo");
  293. }])->findOrEmpty();
  294. $total = (new SaasChatMsg)->where(["poi_id" => $chatStore['poi_id'],"service_id" => $param['chatId'],"openid" => $chatStore['openid'],'is_read' => 0])->count();
  295. if (!$chatStore->isEmpty()) {
  296. $chatStore->last_at = getDateFull();
  297. $chatStore->save();
  298. $chatStore['lastTime'] = formatTime(getDateFull());
  299. $chatStore['last'] = [
  300. "content" => is_array($param['content'])?json_encode($param['content']):str_cut_ellipsis($param['content']),
  301. "type" => $param['type'],
  302. "num" => $total
  303. ];
  304. }
  305. // 推送消息
  306. $api = new Api('http://127.0.0.1:3232', config('plugin.webman.push.app.app_key'),config('plugin.webman.push.app.app_secret'));
  307. $api->trigger("service-{$param['chatId']}","message",[
  308. "type" => $param['type'],
  309. "content" => is_array($param['content'])?json_encode($param['content']):$param['content'],
  310. "openid" => $request->user['openid'],
  311. "nick" => [],
  312. "user" => [],
  313. "userType" => "friend",
  314. "avatar" => $request->user['avatar'],
  315. "time" => time() * 1000,
  316. "msgId" => md5($request->user['openid'].time()),
  317. "item" => $chatStore->toArray()
  318. ]);
  319. } catch (\Throwable $throwable) {
  320. return error($throwable->getMessage());
  321. }
  322. }
  323. }