Notice.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. <?php
  2. namespace app\common\model\base\msg;
  3. use email\Email;
  4. use app\common\model\base\msg\SmsTpl;
  5. use app\common\model\base\user\User;
  6. use app\common\model\base\user\Wxauth;
  7. use app\common\model\base\msg\WxTpl;
  8. use \weixin\Wxmsg;
  9. use app\common\model\Common;
  10. class Notice extends Common
  11. {
  12. protected $name = "system_msg_notice";
  13. protected $pk = "notice_id";
  14. protected $updateTime = false;
  15. protected $append = [
  16. 'channel_txt',
  17. 'send_status_txt',
  18. 'read_status_txt',
  19. 'url',
  20. ];
  21. public function sendStatusList()
  22. {
  23. return [1 => '发送成功', 2 => '发送失败'];
  24. }
  25. public function getSendStatusTxtAttr($value, $data)
  26. {
  27. $value = $value ? $value : (isset($data['send_status']) ? $data['send_status'] : '');
  28. $sendStatusList = $this->sendStatusList();
  29. return isset($sendStatusList[$value]) ? $sendStatusList[$value] : '';
  30. }
  31. public function readStatusList()
  32. {
  33. return [1 => '未读', 2 => '已读'];
  34. }
  35. public function getReadStatusTxtAttr($value, $data)
  36. {
  37. $value = $value ? $value : (isset($data['read_status']) ? $data['read_status'] : '');
  38. $readStatusList = $this->readStatusList();
  39. return isset($readStatusList[$value]) ? $readStatusList[$value] : '';
  40. }
  41. public function channelList()
  42. {
  43. return Msgtype::channelList();
  44. }
  45. public function getChannelTxtAttr($value, $data)
  46. {
  47. $value = $value ? $value : (isset($data['channel']) ? $data['channel'] : '');
  48. $channelList = $this->channelList();
  49. return isset($channelList[$value]) ? $channelList[$value] : '';
  50. }
  51. static public function getUrlAttr($value, $data)
  52. {
  53. $msgType = Msgtype::where('type_code', $data['type_code'])->cache(true, 86400)->find();
  54. $param = [];
  55. if (!empty($msgType->param_key1)) {
  56. $param[$msgType->param_key1] = $data['param1'];
  57. }
  58. if (!empty($msgType->param_key2)) {
  59. $param[$msgType->param_key2] = $data['param2'];
  60. }
  61. $url = "";
  62. if (!empty($msgType->url_mp) || !empty($msgType->url_pc)) {
  63. $url = IsMobileAccess() ? $msgType->url_mp : $msgType->url_pc . '?';
  64. $url .= http_build_query($param);
  65. }
  66. return $url;
  67. }
  68. public function msgtype()
  69. {
  70. return $this->belongsTo(Msgtype::class, 'type_code', 'type_code')->cache(true, 3600);
  71. }
  72. public function receiver()
  73. {
  74. return $this->belongsTo(User::class, 'receive_uid', 'user_id')->cache(true, 3600);
  75. }
  76. /**
  77. * @title: 通过邮件发送消息通知
  78. * @desc:
  79. * @param {Int} {receiver_uid} {} {邮件接收用户ID}
  80. * @param {String} {subject} {} {邮件主题}
  81. * @param {String} {content} {} {邮件内容,可使用html代码}
  82. * @return {*}
  83. * @Author: Rock
  84. * @Date: 2021-05-26 17:07:43
  85. * @LastEditTime: Do not edit
  86. * @throws \Exception
  87. */
  88. static public function sendMail($receiver_uid, $subject = 'This is a test mail', $content = 'This is a test mail content')
  89. {
  90. $user = User::where('user_id', $receiver_uid)->find();
  91. if (!empty($user->email)) {
  92. $email = new Email;
  93. $result = $email
  94. ->to($user->email)
  95. ->subject($subject)
  96. ->message($content)
  97. ->send();
  98. if ($result) {
  99. return Result(1, "发送邮件成功");
  100. } else {
  101. return Result(0, "发送邮件失败" . $email->getError());
  102. }
  103. } else {
  104. return Result(0, "此用户未设置邮箱");
  105. }
  106. }
  107. /**
  108. * @title: 使用腾讯发送短信
  109. * @desc:
  110. * @param {*} $mobiles
  111. * @param {*} $data
  112. * @param {*} $tpl_id
  113. * @return {*}
  114. * @Author: Rock
  115. * @Date: 2022-03-12 17:25:15
  116. * @LastEditTime: Do not edit
  117. */
  118. static public function TxSmsSend($mobiles, $data, $tpl_id)
  119. {
  120. if (empty($mobiles)) {
  121. return Result(0, '手机号不能为空');
  122. }
  123. $sms = new \tencent\Sms;
  124. $newMobiles = [];
  125. foreach ($mobiles as $mobile) {
  126. if (11 == strlen($mobile)) {
  127. $newMobiles[] = $mobile;
  128. }
  129. }
  130. $res = $sms->SendSms($newMobiles, $data, $tpl_id);
  131. if (isset($res['errmsg']) && $res['errmsg'] == 'OK') {
  132. return Result(1, "发送短信成功");
  133. } else {
  134. $msg = isset($res['ErrorInfo']) ? $res['ErrorInfo'] : (isset($res['errmsg']) ? $res['errmsg'] : '短信接口错误');
  135. return Result(0, "发送短信失败:" . $msg);
  136. }
  137. }
  138. /**
  139. * @title: 使用联通发送短信
  140. * @desc:
  141. * @param {*} $phone
  142. * @param {*} $data
  143. * @param {*} $tpl_id
  144. * @return {*}
  145. * @Author: Rock
  146. * @Date: 2022-03-12 17:29:20
  147. * @LastEditTime: Do not edit
  148. */
  149. static public function UmsSmsSend($mobiles, $data, $tpl_id)
  150. {
  151. if (empty($mobiles)) {
  152. return Result(0, '手机号不能为空');
  153. }
  154. $sms = new \ums\Sms;
  155. $tplInfo = SmsTpl::where('tpl_id', $tpl_id)->find();
  156. if ($tplInfo) {
  157. $content = $tplInfo->content;
  158. // 将参数替换进模板内容
  159. foreach ($data as $param) {
  160. $start = strpos($content, '{');
  161. $end = strpos($content, '}');
  162. if ($start !== false && $end !== false) {
  163. $sub = substr($content, $start, $end - $start + 1);
  164. $content = str_replace($sub, $param, $content);
  165. }
  166. }
  167. // 过滤不是手机的号码
  168. $newMobiles = [];
  169. foreach ($mobiles as $mobile) {
  170. if (11 == strlen($mobile)) {
  171. $newMobiles[] = $mobile;
  172. }
  173. }
  174. $phone = implode(',', $newMobiles);
  175. $res = $sms->SendSms($phone, $content, $tpl_id);
  176. if (!empty($res['result']) && 0 == $res['result']) {
  177. return Result(1, "短信发送成功", $res);
  178. } else {
  179. WLog('ums_sms_send', "短信发送失败:" . json_encode($res));
  180. return Result(0, $res['description'] ?? '短信发送失败', $res);
  181. }
  182. } else {
  183. WLog('ums_sms_send', "没有找到短信模板");
  184. }
  185. }
  186. /**
  187. * @title: 通过短信发送消息通知
  188. * @desc:
  189. * @param {string/array} {mobiles} {} {短信接收用户手机号}
  190. * @param {Array} {data} {} {需要填充到短信模板中的数据}
  191. * @param {Int} {tpl_id} {} {腾讯短信消息模板ID}
  192. * @return {*}
  193. * @Author: Rock
  194. * @Date: 2021-05-26 17:09:28
  195. * @LastEditTime: Do not edit
  196. * @throws \Exception
  197. */
  198. static public function sendSms($mobiles, array $data, string $tpl_id)
  199. {
  200. $provider = sysconfig('sms.provider');
  201. if (!empty($mobiles)) {
  202. // 腾讯短信
  203. if (1 == $provider) {
  204. return self::TxSmsSend($mobiles, $data, $tpl_id);
  205. } // 联通短信
  206. elseif (2 == $provider) {
  207. return self::UmsSmsSend($mobiles, $data, $tpl_id);
  208. }
  209. } else {
  210. return Result(0, "此用户未绑定手机号");
  211. }
  212. }
  213. /**
  214. * @title: 通过站内信发送消息通知
  215. * @desc:
  216. * @param {Int} {receiver_uid} {} {消息接收用户ID}
  217. * @param {Array} {data} {} {消息数据包}
  218. * @return {*}
  219. * @Author: Rock
  220. * @Date: 2021-06-04 09:56:48
  221. * @LastEditTime: Do not edit
  222. */
  223. static public function sendWs($uids, $data)
  224. {
  225. $res = wssend($uids, 'notice', $data);
  226. if ($res) {
  227. return Result($res['code'], $res['msg']);
  228. } else {
  229. return Result(2, "发送失败");
  230. }
  231. }
  232. /**
  233. * @title: 通过微信公众号发送消息通知
  234. * @desc: 描述
  235. * @param {mixed} {uids} {} {消息接收用户ID数组}
  236. * @param {array} {data} {} {消息数据包,这里接收和短信一样的参数方式,即索引数组,如:['张三','18','男']}
  237. * @return {*}
  238. * @author: Rock
  239. * @method: POST
  240. * @Date: 2023-04-06 17:48:17
  241. */
  242. static public function sendWx($uids,array $data,string $template_id)
  243. {
  244. if(is_string($uids)){
  245. $udis = explode(',',$uids);
  246. }elseif(is_int($uids)){
  247. $uids = [$uids];
  248. }
  249. $openids = Wxauth::where('user_id','IN',$uids)->column('openid');
  250. // 因为接收的是短信一样的参数,这里需要处理成公众号方式的参数$info
  251. $info = [];
  252. // 通过模板取出待填充数据的字段
  253. $content = WxTpl::where('template_id',$template_id)->value('content');
  254. preg_match_all('/{{([\w\W]*?).DATA}}/',$content,$matches);
  255. $keys = $matches[1];
  256. foreach($keys as $index=>$key){
  257. $info[$key] = $data[$index]??'';
  258. }
  259. foreach($openids as $openid){
  260. $res = (new Wxmsg)->Send_Msg($openid,$template_id,$info);
  261. if($res['errcode'] && 0!=$res['errcode']){
  262. WLog('Notice/SendWx',json_encode($res));
  263. }
  264. }
  265. return Result(1,"发送成功",$res);
  266. }
  267. /**
  268. * @title: 单一通道发送消息通知
  269. * @desc:
  270. * @param {string} {notice.uids} {} {用户ID或逗号分隔的多个用户ID}
  271. * @param {string} {notice.title} {} {消息通知标题}
  272. * @param {string} {notice.type_code} {} {消息类型代码}
  273. * @param {string} {notice.data} {} {短信模板填充数据}
  274. * @param {mixed} {notice.param1} {} {所属消息类型的第一个参数}
  275. * @param {mixed} {notice.param2} {} {所属消息类型的第二个参数}
  276. * @return {*}
  277. * @Author: Rock
  278. * @Date: 2021-05-26 18:50:12
  279. * @LastEditTime: Do not edit
  280. * @throws \Exception
  281. */
  282. static public function sendNotice($notice)
  283. {
  284. try {
  285. if (empty($notice['uids']) || empty($notice['title']) || empty($notice['type_code'])) {
  286. return res(2, "发送消息缺少参数");
  287. }
  288. $uids = $notice['uids'];
  289. $title = $notice['title'];
  290. $data = !empty($notice['data']) ? $notice['data'] : [];
  291. $type_code = $notice['type_code'];
  292. $param1 = !empty($notice['param1']) ? $notice['param1'] : '';
  293. $param2 = !empty($notice['param2']) ? $notice['param2'] : '';
  294. $msgtype = Msgtype::where('type_code', $type_code)->find();
  295. if (empty($msgtype)) {
  296. return res(2, "不存在的消息类型");
  297. }
  298. $channel = $msgtype->channel;
  299. $pcHost = sysconfig('base.pc_host');//换为PC端访问基础地址
  300. if (is_string($uids)) {
  301. $uids = explode(',', (string)$uids);
  302. } elseif (is_int($uids)) {
  303. $uids = [$uids];
  304. }
  305. $mobiles = User::where('user_id', 'IN', $uids)->where('phone', '<>', '')->whereNotNull('phone')->column('phone');
  306. $mobiles = array_filter($mobiles);
  307. $saveData = [];
  308. foreach ($channel as $channelItem) {
  309. $temp = [
  310. 'title' => $msgtype->type_name . ":" . $title,
  311. 'type_code' => $type_code,
  312. 'channel' => $channelItem,
  313. 'param1' => $param1,
  314. 'param2' => $param2,
  315. 'send_at' => date('Y-m-d H:i:s'),
  316. 'read_status' => 1,
  317. ];
  318. switch ($channelItem) {
  319. case 1://站内信
  320. $temp['send_status'] = 1;
  321. $temp['msgtype'] = $msgtype->toArray();
  322. $res = self::sendWs(implode(',', $uids), $temp);
  323. foreach ($uids as $uid) {
  324. $temp['receive_uid'] = $uid;
  325. unset($temp['msgtype']);
  326. $saveData[] = $temp;
  327. }
  328. break;
  329. case 2://短信
  330. $res = self::sendSms($mobiles, $data, $msgtype->sms_tpl);
  331. foreach ($uids as $uid) {
  332. $temp['send_status'] = $res['code'] == 1 ? 1 : 2;
  333. $temp['receive_uid'] = $uid;
  334. $saveData[] = $temp;
  335. }
  336. break;
  337. case 3://邮件
  338. $url = self::getUrlAttr('', $temp);
  339. $content = '<a href="' . $pcHost . $url . '">' . $temp['title'] . '</a>';
  340. foreach ($uids as $uid) {
  341. $res = self::sendMail($uid, $temp['title'], $content);
  342. $temp['send_status'] = $res['code'] == 1 ? 1 : 2;
  343. $temp['receive_uid'] = $uid;
  344. $saveData[] = $temp;
  345. }
  346. self::create($temp);
  347. break;
  348. case 4://公众号
  349. $res = self::sendWx($uids,$data,$msgtype->template_id);
  350. foreach($uids as $uid){
  351. $temp['send_status'] = $res['code'] == 1?1:2;
  352. $temp['receive_uid'] = $uid;
  353. }
  354. $saveData[] = $temp;
  355. break;
  356. default:
  357. $res = ['code' => 1];
  358. break;
  359. }
  360. }
  361. self::insertAll($saveData);
  362. return res(1, '发送成功');
  363. } catch (Exception $e) {
  364. abort(2, $e->getMessage());
  365. }
  366. }
  367. /**
  368. * @title: 获取消息未读数等
  369. * @param $user_id {用户id}
  370. * @param $role_code {用户角色}
  371. * @param $ids {用户有权限的组织id}
  372. * @Author: wangkewei
  373. * @return mixed
  374. * @Date: 2022/2/28 18:47
  375. */
  376. public function MsgUnreadNum($user_id, $role_code, $ids)
  377. {
  378. $un_read_num = $this->where('receive_uid', $user_id)
  379. ->where('read_status', 1)
  380. ->where('channel', 1)
  381. ->count('notice_id');
  382. return ['un_read_num' => $un_read_num];
  383. }
  384. }