WechatReplyServices.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2023 https://www.crmeb.com All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
  8. // +----------------------------------------------------------------------
  9. // | Author: CRMEB Team <admin@crmeb.com>
  10. // +----------------------------------------------------------------------
  11. declare (strict_types=1);
  12. namespace app\services\wechat;
  13. use app\services\BaseServices;
  14. use app\dao\wechat\WechatReplyDao;
  15. use app\services\kefu\KefuServices;
  16. use crmeb\exceptions\AdminException;
  17. use crmeb\services\app\WechatService;
  18. use crmeb\services\FormBuilder;
  19. /**
  20. * Class UserWechatuserServices
  21. * @package app\services\user
  22. * @method delete($id, ?string $key = null) 删除
  23. * @method update($id, array $data, ?string $key = null) 更新数据
  24. */
  25. class WechatReplyServices extends BaseServices
  26. {
  27. /**
  28. * UserWechatuserServices constructor.
  29. * @param WechatReplyDao $dao
  30. */
  31. public function __construct(WechatReplyDao $dao)
  32. {
  33. $this->dao = $dao;
  34. }
  35. /**
  36. * 消息类型
  37. * @return string[]
  38. */
  39. public function replyType()
  40. {
  41. return ['text', 'image', 'news', 'voice'];
  42. }
  43. /**
  44. * 自定义简单查询总数
  45. * @param array $where
  46. * @return int
  47. */
  48. public function getCount(array $where): int
  49. {
  50. return $this->dao->getCount($where);
  51. }
  52. /**
  53. * 复杂条件搜索列表
  54. * @param array $where
  55. * @param string $field
  56. * @return array
  57. */
  58. public function getWhereUserList(array $where, string $field): array
  59. {
  60. [$page, $limit] = $this->getPageValue();
  61. $list = $this->dao->getListByModel($where, $field, $page, $limit);
  62. $count = $this->dao->getCountByWhere($where);
  63. return [$list, $count];
  64. }
  65. /**
  66. * 关注回复
  67. * @param string $key
  68. * @return array|\think\Model|null
  69. * @throws \think\db\exception\DataNotFoundException
  70. * @throws \think\db\exception\DbException
  71. * @throws \think\db\exception\ModelNotFoundException
  72. */
  73. public function getDataByKey(string $key)
  74. {
  75. /** @var WechatKeyServices $services */
  76. $services = app()->make(WechatKeyServices::class);
  77. $data = $services->getOne(['keys' => $key]);
  78. $resdata = $this->dao->getOne(['id' => $data['reply_id'] ?? 0]);
  79. $resdata['data'] = isset($resdata['data']) ? json_decode($resdata['data'], true) : [];
  80. $resdata['key'] = $key;
  81. return $resdata;
  82. }
  83. /**
  84. * 保存关键字
  85. * @param $data
  86. * @param $id
  87. * @param $key
  88. * @param $type
  89. * @param int $status
  90. * @return bool
  91. */
  92. public function redact($data, $id, $key, $type, $status = 1)
  93. {
  94. $method = 'tidy' . ucfirst($type);
  95. if ($id == 'undefined') {
  96. $id = 0;
  97. }
  98. if (isset($data['content']) && $data['content'] == '' && isset($data['src']) && $data['src'] == '') $data = $data['list'][0] ?? [];
  99. try {
  100. $res = $this->{$method}($data, $id);
  101. } catch (\Throwable $e) {
  102. throw new AdminException($e->getMessage());
  103. }
  104. if (!$res) return false;
  105. $arr = [];
  106. /** @var WechatKeyServices $keyServices */
  107. $keyServices = app()->make(WechatKeyServices::class);
  108. $count = $this->dao->getCount(['id' => $id]);
  109. if ($count) {
  110. $keyServices->delete($id, 'reply_id');
  111. $insertData = explode(',', $key);
  112. foreach ($insertData as $k => $v) {
  113. $arr[$k]['keys'] = $v;
  114. $arr[$k]['reply_id'] = $id;
  115. }
  116. $res = $this->dao->update($id, ['type' => $type, 'data' => json_encode($res), 'status' => $status], 'id');
  117. $res1 = $keyServices->saveAll($arr);
  118. if (!$res || !$res1) {
  119. throw new AdminException(100006);
  120. }
  121. } else {
  122. $reply = $this->dao->save([
  123. 'type' => $type,
  124. 'data' => json_encode($res),
  125. 'status' => $status,
  126. ]);
  127. $insertData = explode(',', $key);
  128. foreach ($insertData as $k => $v) {
  129. $arr[$k]['keys'] = $v;
  130. $arr[$k]['reply_id'] = $reply->id;
  131. }
  132. $res = $keyServices->saveAll($arr);
  133. if (!$res) throw new AdminException(100006);
  134. }
  135. return true;
  136. }
  137. /**
  138. * 获取所有关键字
  139. * @param array $where
  140. * @return array
  141. */
  142. public function getKeyAll($where = array())
  143. {
  144. /** @var WechatReplyKeyServices $replyKeyServices */
  145. $replyKeyServices = app()->make(WechatReplyKeyServices::class);
  146. $data = $replyKeyServices->getReplyKeyAll($where);
  147. /** @var WechatKeyServices $keyServices */
  148. $keyServices = app()->make(WechatKeyServices::class);
  149. foreach ($data['list'] as &$item) {
  150. if ($item['data']) $item['data'] = json_decode($item['data'], true);
  151. switch ($item['type']) {
  152. case 'text':
  153. $item['typeName'] = '文字消息';
  154. break;
  155. case 'image':
  156. $item['typeName'] = '图片消息';
  157. break;
  158. case 'news':
  159. $item['typeName'] = '图文消息';
  160. break;
  161. case 'voice':
  162. $item['typeName'] = '声音消息';
  163. break;
  164. }
  165. $keys = $keyServices->getColumn(['reply_id' => $item['id']], 'keys');
  166. $item['key'] = implode(',', $keys);
  167. }
  168. return $data;
  169. }
  170. /**
  171. * 查询一条
  172. * @param $key
  173. * @return array|null|\think\Model
  174. * @throws \think\db\exception\DataNotFoundException
  175. * @throws \think\db\exception\ModelNotFoundException
  176. * @throws \think\exception\DbException
  177. */
  178. public function getKeyInfo(int $id)
  179. {
  180. $resdata = $this->dao->getOne(['id' => $id]);
  181. /** @var WechatKeyServices $keyServices */
  182. $keyServices = app()->make(WechatKeyServices::class);
  183. $keys = $keyServices->getColumn(['reply_id' => $resdata['id']], 'keys');
  184. $resdata['data'] = $resdata['data'] ? json_decode($resdata['data'], true) : [];
  185. $resdata['key'] = implode(',', $keys);
  186. return $resdata;
  187. }
  188. /**
  189. * 整理文本输入的消息
  190. * @param $data
  191. * @param $key
  192. * @return array|bool
  193. */
  194. public function tidyText($data, $id)
  195. {
  196. $res = [];
  197. if (!isset($data['content']) || $data['content'] == '') {
  198. throw new AdminException(400706);
  199. }
  200. $res['content'] = $data['content'];
  201. return $res;
  202. }
  203. /**
  204. * 整理图片资源
  205. * @param $data
  206. * @param $id
  207. * @return array|mixed
  208. * @throws \think\db\exception\DataNotFoundException
  209. * @throws \think\db\exception\DbException
  210. * @throws \think\db\exception\ModelNotFoundException
  211. */
  212. public function tidyImage($data, $id)
  213. {
  214. if (!isset($data['src']) || $data['src'] == '') {
  215. throw new AdminException(400707);
  216. }
  217. $reply = $this->dao->get((int)$id);
  218. if ($reply) $reply['data'] = json_decode($reply['data'], true);
  219. if ($reply && isset($reply['data']['src']) && $reply['data']['src'] == $data['src']) {
  220. $res = $reply['data'];
  221. } else {
  222. $res = [];
  223. //TODO 图片转media
  224. $res['src'] = $data['src'];
  225. try {
  226. $material = WechatService::materialService()->uploadImage(url_to_path($data['src']));
  227. } catch (\Throwable $e) {
  228. throw new AdminException(WechatService::getMessage($e->getMessage()));
  229. }
  230. $res['media_id'] = $material->media_id;
  231. $dataEvent = ['type' => 'image', 'media_id' => $material->media_id, 'path' => $res['src'], 'url' => $material->url];
  232. /** @var WechatMediaServices $mateServices */
  233. $mateServices = app()->make(WechatMediaServices::class);
  234. $mateServices->save($dataEvent);
  235. }
  236. return $res;
  237. }
  238. /**
  239. * 整理声音资源
  240. * @param $data
  241. * @param $id
  242. * @return array|mixed
  243. * @throws \think\db\exception\DataNotFoundException
  244. * @throws \think\db\exception\DbException
  245. * @throws \think\db\exception\ModelNotFoundException
  246. */
  247. public function tidyVoice($data, $id)
  248. {
  249. if (!isset($data['src']) || $data['src'] == '') {
  250. throw new AdminException(400708);
  251. }
  252. $reply = $this->dao->get((int)$id);
  253. if ($reply) $reply['data'] = json_decode($reply['data'], true);
  254. if ($reply && isset($reply['data']['src']) && $reply['data']['src'] == $data['src']) {
  255. $res = $reply['data'];
  256. } else {
  257. $res = [];
  258. //TODO 声音转media
  259. $res['src'] = $data['src'];
  260. try {
  261. $material = WechatService::materialService()->uploadVoice(url_to_path($data['src']));
  262. } catch (\Throwable $e) {
  263. throw new AdminException(WechatService::getMessage($e->getMessage()));
  264. }
  265. $res['media_id'] = $material->media_id;
  266. $dataEvent = ['media_id' => $material->media_id, 'path' => $res['src'], 'type' => 'voice'];
  267. /** @var WechatMediaServices $mateServices */
  268. $mateServices = app()->make(WechatMediaServices::class);
  269. $mateServices->save($dataEvent);
  270. }
  271. return $res;
  272. }
  273. /**
  274. * 整理图文资源
  275. * @param $data
  276. * @param $id
  277. * @return bool
  278. */
  279. public function tidyNews($data, $id = 0)
  280. {
  281. // if ($id != 0) {
  282. // $data = $data['list'][0];
  283. // }
  284. if (!count($data)) {
  285. throw new AdminException(400709);
  286. }
  287. $siteUrl = sys_config('site_url');
  288. if (empty($data['url'])) $data['url'] = $siteUrl . '/pages/extension/news_details/index?id=' . $data['id'];
  289. if (count($data['image_input'])) $data['image'] = $data['image_input'][0];
  290. return $data;
  291. }
  292. /**
  293. * 获取关键字
  294. * @param $key
  295. * @param string $openId
  296. * @return array|\EasyWeChat\Message\Image|\EasyWeChat\Message\News|\EasyWeChat\Message\Text|\EasyWeChat\Message\Transfer|\EasyWeChat\Message\Voice
  297. * @throws \think\db\exception\DataNotFoundException
  298. * @throws \think\db\exception\DbException
  299. * @throws \think\db\exception\ModelNotFoundException
  300. */
  301. public function reply($key, string $openId = '')
  302. {
  303. $res = $this->dao->getKey($key);
  304. if (empty($res)) {
  305. /** @var KefuServices $services */
  306. $services = app()->make(KefuServices::class);
  307. $services->replyTransferService($key, $openId);
  308. return WechatService::transfer();
  309. }
  310. return $this->replyDataByMessage($res->toArray());
  311. }
  312. /**
  313. * 根据关键字内容返回对应的内容
  314. * @param array $res
  315. * @return array|\EasyWeChat\Message\Image|\EasyWeChat\Message\News|\EasyWeChat\Message\Text|\EasyWeChat\Message\Voice
  316. */
  317. public function replyDataByMessage(array $res)
  318. {
  319. $res['data'] = json_decode($res['data'], true);
  320. if ($res['type'] == 'text') {
  321. return WechatService::textMessage($res['data']['content']);
  322. } else if ($res['type'] == 'image') {
  323. return WechatService::imageMessage($res['data']['media_id']);
  324. } else if ($res['type'] == 'news') {
  325. $title = $res['data']['title'] ?? '';
  326. $image = $res['data']['image'] ?? '';
  327. $description = $res['data']['synopsis'] ?? '';
  328. $url = $res['data']['url'] ?? '';
  329. return WechatService::newsMessage($title, $description, $url, $image);
  330. } else if ($res['type'] == 'voice') {
  331. return WechatService::voiceMessage($res['data']['media_id']);
  332. }
  333. }
  334. /**
  335. * 添加修改客服自动回复表单
  336. * @param int $id
  337. * @return array
  338. * @throws \FormBuilder\Exception\FormBuilderException
  339. * @throws \think\db\exception\DataNotFoundException
  340. * @throws \think\db\exception\DbException
  341. * @throws \think\db\exception\ModelNotFoundException
  342. * @author: 吴汐
  343. * @email: 442384644@qq.com
  344. * @date: 2023/8/3
  345. */
  346. public function autoReplyForm($id = 0)
  347. {
  348. $replyInfo = [];
  349. if ($id) {
  350. $replyInfo = $this->dao->get($id, ['*'], ['kefuKey']);
  351. $replyInfo['data'] = json_decode($replyInfo['data'], true);
  352. }
  353. $field[] = FormBuilder::input('keys', '关键字', $replyInfo['keys'] ?? '')->col(24)->required();
  354. $field[] = FormBuilder::radio('type', '回复类型', (string)($replyInfo['type'] ?? 'text'))->appendControl('text', [
  355. FormBuilder::input('data', '回复内容', (string)($replyInfo['data']['content'] ?? ''))->required(),
  356. ])->appendControl('image', [
  357. FormBuilder::frameImage('data', '回复图片', $this->url(config('app.admin_prefix', 'admin') . '/widget.images/index', ['fodder' => 'data'], true), (string)($replyInfo['data']['src'] ?? ''))->icon('el-icon-picture-outline')->width('950px')->height('560px')->Props(['footer' => false]),
  358. ])->options([['label' => '文字消息', 'value' => 'text'], ['label' => '图片消息', 'value' => 'image']]);
  359. $field[] = FormBuilder::radio('status', '状态', $replyInfo['status'] ?? 1)->options([['label' => '开启', 'value' => 1], ['label' => '关闭', 'value' => 0]]);
  360. return create_form('客服自动回复', $field, $this->url('/app/kefu/auto_reply/save/' . $id), 'POST');
  361. }
  362. /**
  363. * 保存自动回复
  364. * @param $id
  365. * @param $data
  366. * @return bool
  367. * @author: 吴汐
  368. * @email: 442384644@qq.com
  369. * @date: 2023/8/3
  370. */
  371. public function autoReplySave($id, $data)
  372. {
  373. if ($id) {
  374. $this->dao->update($id, [
  375. 'type' => $data['type'],
  376. 'data' => json_encode($data['type'] == 'text' ? ['content' => $data['data']] : ['src' => $data['data']]),
  377. 'status' => $data['status'],
  378. ]);
  379. $res = app()->make(WechatKeyServices::class)->update(['reply_id' => $id], ['keys' => $data['keys']]);
  380. } else {
  381. $reply = $this->dao->save([
  382. 'type' => $data['type'],
  383. 'data' => json_encode($data['type'] == 'text' ? ['content' => $data['data']] : ['src' => $data['data']]),
  384. 'status' => $data['status'],
  385. ]);
  386. $res = app()->make(WechatKeyServices::class)->save([
  387. 'reply_id' => $reply->id,
  388. 'keys' => $data['keys'],
  389. 'key_type' => 1,
  390. ]);
  391. }
  392. if (!$res) throw new AdminException(100006);
  393. return true;
  394. }
  395. /**
  396. * 删除自动回复
  397. * @param $id
  398. * @return bool
  399. * @author: 吴汐
  400. * @email: 442384644@qq.com
  401. * @date: 2023/8/3
  402. */
  403. public function autoReplyDel($id)
  404. {
  405. $this->dao->delete($id);
  406. app()->make(WechatKeyServices::class)->delete(['reply_id' => $id]);
  407. return true;
  408. }
  409. }