WechatServices.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2016~2022 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\WechatUserDao;
  15. use app\services\user\UserServices;
  16. use app\services\user\UserVisitServices;
  17. use crmeb\exceptions\ApiException;
  18. use crmeb\services\CacheService;
  19. use crmeb\services\CacheService as Cache;
  20. use crmeb\services\app\WechatService as WechatAuthService;
  21. use crmeb\services\oauth\OAuth;
  22. use crmeb\utils\Canvas;
  23. /**
  24. *
  25. * Class WechatServices
  26. * @package app\services\wechat
  27. * @method value(array $where, ?string $field)
  28. */
  29. class WechatServices extends BaseServices
  30. {
  31. /**
  32. * WechatServices constructor.
  33. * @param WechatUserDao $dao
  34. */
  35. public function __construct(WechatUserDao $dao)
  36. {
  37. $this->dao = $dao;
  38. }
  39. /**
  40. * 微信公众号服务
  41. * @return \think\Response
  42. */
  43. public function serve()
  44. {
  45. ob_clean();
  46. return WechatAuthService::serve();
  47. }
  48. /**
  49. * 支付异步回调
  50. */
  51. public function notify()
  52. {
  53. ob_clean();
  54. return WechatAuthService::handleNotify()->getContent();
  55. }
  56. /**
  57. * 公众号权限配置信息获取
  58. * @param $url
  59. * @return mixed
  60. */
  61. public function config($url)
  62. {
  63. return json_decode(WechatAuthService::jsSdk($url), true);
  64. }
  65. /**
  66. * 公众号授权登陆
  67. * @return mixed
  68. * @throws \think\db\exception\DataNotFoundException
  69. * @throws \think\db\exception\ModelNotFoundException
  70. * @throws \think\exception\DbException
  71. */
  72. public function auth($spreadId, $login_type)
  73. {
  74. /** @var OAuth $oauth */
  75. $oauth = app()->make(OAuth::class);
  76. $wechatInfo = $oauth->oauth();
  77. if (!isset($wechatInfo['nickname'])) {
  78. $wechatInfo = $oauth->getUserInfo($wechatInfo['openid']);
  79. if (!isset($wechatInfo['nickname']))
  80. throw new ApiException(410131);
  81. if (isset($wechatInfo['tagid_list']))
  82. $wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']);
  83. } else {
  84. if (isset($wechatInfo['privilege'])) unset($wechatInfo['privilege']);
  85. /** @var WechatUserServices $wechatUser */
  86. $wechatUser = app()->make(WechatUserServices::class);
  87. if (!$wechatUser->getOne(['openid' => $wechatInfo['openid']])) {
  88. $wechatInfo['subscribe'] = 0;
  89. }
  90. }
  91. $wechatInfo['user_type'] = 'wechat';
  92. $openid = $wechatInfo['openid'];
  93. /** @var WechatUserServices $wechatUserServices */
  94. $wechatUserServices = app()->make(WechatUserServices::class);
  95. $user = $wechatUserServices->getAuthUserInfo($openid, 'wechat');
  96. $createData = [$openid, $wechatInfo, $spreadId, $login_type, 'wechat'];
  97. if (!$user) {
  98. $user = $wechatUserServices->wechatOauthAfter($createData);
  99. } else {
  100. //更新用户信息
  101. $wechatUserServices->wechatUpdata([$user['uid'], $wechatInfo]);
  102. }
  103. $token = $this->createToken((int)$user['uid'], 'wechat');
  104. if ($token) {
  105. /** @var UserVisitServices $visitServices */
  106. $visitServices = app()->make(UserVisitServices::class);
  107. $visitServices->loginSaveVisit($user);
  108. return ['userInfo' => $user];
  109. } else
  110. throw new ApiException(410019);
  111. }
  112. /**
  113. * 新公众号授权登录
  114. * @return mixed
  115. * @throws \think\db\exception\DataNotFoundException
  116. * @throws \think\db\exception\ModelNotFoundException
  117. * @throws \think\exception\DbException
  118. */
  119. public function newAuth($spreadId, $login_type)
  120. {
  121. /** @var OAuth $oauth */
  122. $oauth = app()->make(OAuth::class);
  123. $wechatInfo = $oauth->oauth();
  124. if (!isset($wechatInfo['nickname'])) {
  125. $wechatInfo = $oauth->getUserInfo($wechatInfo['openid']);
  126. if (!isset($wechatInfo['nickname']))
  127. throw new ApiException(410131);
  128. if (isset($wechatInfo['tagid_list']))
  129. $wechatInfo['tagid_list'] = implode(',', $wechatInfo['tagid_list']);
  130. } else {
  131. if (isset($wechatInfo['privilege'])) unset($wechatInfo['privilege']);
  132. }
  133. $wechatInfo['user_type'] = 'wechat';
  134. $openid = $wechatInfo['openid'];
  135. /** @var WechatUserServices $wechatUserServices */
  136. $wechatUserServices = app()->make(WechatUserServices::class);
  137. $user = $wechatUserServices->getAuthUserInfo($openid, 'wechat');
  138. $createData = [$openid, $wechatInfo, $spreadId, $login_type, 'wechat'];
  139. //获取是否强制绑定手机号
  140. $storeUserMobile = sys_config('store_user_mobile');
  141. if ($storeUserMobile && !$user) {
  142. $userInfoKey = md5($openid . '_' . time() . '_wechat');
  143. Cache::setTokenBucket($userInfoKey, $createData, 7200);
  144. return ['key' => $userInfoKey];
  145. } else if (!$user) {
  146. $user = $wechatUserServices->wechatOauthAfter($createData);
  147. } else {
  148. //更新用户信息
  149. $wechatUserServices->wechatUpdata([$user['uid'], $wechatInfo]);
  150. }
  151. $token = $this->createToken((int)$user['uid'], 'wechat');
  152. if ($token) {
  153. /** @var UserVisitServices $visitServices */
  154. $visitServices = app()->make(UserVisitServices::class);
  155. $visitServices->loginSaveVisit($user);
  156. $token['userInfo'] = $user;
  157. return $token;
  158. } else
  159. throw new ApiException(410019);
  160. }
  161. /**
  162. * 获取关注二维码
  163. * @return string[]
  164. * @throws \Exception
  165. */
  166. public function follow()
  167. {
  168. $canvas = Canvas::instance();
  169. $path = 'uploads/follow/';
  170. $imageType = 'jpg';
  171. $name = 'follow';
  172. $siteUrl = sys_config('site_url');
  173. $imageUrl = $path . $name . '.' . $imageType;
  174. $canvas->setImageUrl('statics/qrcode/follow.png')->setImageHeight(720)->setImageWidth(500)->pushImageValue();
  175. $wechatQrcode = sys_config('wechat_qrcode');
  176. if (($strlen = stripos($wechatQrcode, 'uploads')) !== false) {
  177. $wechatQrcode = substr($wechatQrcode, $strlen);
  178. }
  179. if (!$wechatQrcode)
  180. throw new ApiException(410081);
  181. $canvas->setImageUrl($wechatQrcode)->setImageHeight(344)->setImageWidth(344)->setImageLeft(76)->setImageTop(76)->pushImageValue();
  182. $image = $canvas->setFileName($name)->setImageType($imageType)->setPath($path)->setBackgroundWidth(500)->setBackgroundHeight(720)->starDrawChart();
  183. return ['path' => $image ? $siteUrl . '/' . $image : ''];
  184. }
  185. /**
  186. * 微信公众号静默授权
  187. * @param $spread
  188. * @return array
  189. * @throws \think\db\exception\DataNotFoundException
  190. * @throws \think\db\exception\ModelNotFoundException
  191. */
  192. public function silenceAuth($spread)
  193. {
  194. /** @var OAuth $oauth */
  195. $oauth = app()->make(OAuth::class);
  196. $wechatInfoConfig = $oauth->oauth();
  197. $wechatInfo = $oauth->getUserInfo($wechatInfoConfig['openid']);
  198. $openid = $wechatInfoConfig['openid'];
  199. /** @var WechatUserServices $wechatUserServices */
  200. $wechatUserServices = app()->make(WechatUserServices::class);
  201. $user = $wechatUserServices->getAuthUserInfo($openid, 'wechat');
  202. $wechatInfo['headimgurl'] = isset($wechatInfo['headimgurl']) && $wechatInfo['headimgurl'] != '' ? $wechatInfo['headimgurl'] : sys_config('h5_avatar');
  203. $createData = [$openid, $wechatInfo, $spread, '', 'wechat'];
  204. //获取是否强制绑定手机号
  205. $storeUserMobile = sys_config('store_user_mobile');
  206. if ($storeUserMobile && !$user) {
  207. $userInfoKey = md5($openid . '_' . time() . '_wechat');
  208. Cache::setTokenBucket($userInfoKey, $createData, 7200);
  209. return ['key' => $userInfoKey];
  210. } else if (!$user) {
  211. //写入用户信息
  212. $user = $wechatUserServices->wechatOauthAfter($createData);
  213. $token = $this->createToken((int)$user['uid'], 'wechat');
  214. if ($token) {
  215. /** @var UserVisitServices $visitServices */
  216. $visitServices = app()->make(UserVisitServices::class);
  217. $visitServices->loginSaveVisit($user);
  218. return $token;
  219. } else
  220. throw new ApiException(410019);
  221. } else {
  222. //更新用户信息
  223. $wechatUserServices->wechatUpdata([$user['uid'], ['code' => $spread]]);
  224. $token = $this->createToken((int)$user['uid'], 'wechat');
  225. if ($token) {
  226. /** @var UserVisitServices $visitServices */
  227. $visitServices = app()->make(UserVisitServices::class);
  228. $visitServices->loginSaveVisit($user);
  229. return $token;
  230. } else
  231. throw new ApiException(410019);
  232. }
  233. }
  234. /**
  235. * 微信公众号静默授权(不登录注册用户)
  236. * @param $spread
  237. * @return array
  238. * @throws \think\db\exception\DataNotFoundException
  239. * @throws \think\db\exception\ModelNotFoundException
  240. */
  241. public function silenceAuthNoLogin($spread)
  242. {
  243. /** @var OAuth $oauth */
  244. $oauth = app()->make(OAuth::class);
  245. $wechatInfoConfig = $oauth->oauth();
  246. $openid = $wechatInfoConfig['openid'];
  247. try {
  248. $wechatInfo = $oauth->getUserInfo($wechatInfoConfig['openid']);
  249. } catch (\Throwable $e) {
  250. $createData = [$openid, [], $spread, '', 'wechat'];
  251. $userInfoKey = md5($openid . '_' . time() . '_wechat');
  252. Cache::setTokenBucket($userInfoKey, $createData, 7200);
  253. return ['auth_login' => 1, 'key' => $userInfoKey];
  254. }
  255. /** @var WechatUserServices $wechatUserServices */
  256. $wechatUserServices = app()->make(WechatUserServices::class);
  257. $user = $wechatUserServices->getAuthUserInfo($openid, 'wechat');
  258. if (!$user) {//没有注册用户也没有强制手机号绑定 返回让微信授权登录
  259. $createData = [$openid, $wechatInfo, $spread, '', 'wechat'];
  260. $userInfoKey = md5($openid . '_' . time() . '_wechat');
  261. Cache::setTokenBucket($userInfoKey, $createData, 7200);
  262. return ['auth_login' => 1, 'key' => $userInfoKey];
  263. } else {
  264. //更新用户信息
  265. $wechatUserServices->wechatUpdata([$user['uid'], ['code' => $spread]]);
  266. $token = $this->createToken((int)$user['uid'], 'wechat');
  267. if ($token) {
  268. /** @var UserVisitServices $visitServices */
  269. $visitServices = app()->make(UserVisitServices::class);
  270. $visitServices->loginSaveVisit($user);
  271. $token['userInfo'] = $user;
  272. return $token;
  273. } else
  274. throw new ApiException(410019);
  275. }
  276. }
  277. /**
  278. * 微信公众号静默授权
  279. * @param $spread
  280. * @param $phone
  281. * @return array
  282. * @throws \think\db\exception\DataNotFoundException
  283. * @throws \think\db\exception\ModelNotFoundException
  284. */
  285. public function silenceAuthBindingPhone($key, $phone)
  286. {
  287. if (!$key) {
  288. throw new ApiException(410037);
  289. }
  290. [$openid, $wechatInfo, $spreadId, $login_type, $userType] = $createData = CacheService::getTokenBucket($key);
  291. if (!$createData) {
  292. throw new ApiException(410037);
  293. }
  294. $wechatInfo['phone'] = $phone;
  295. /** @var WechatUserServices $wechatUser */
  296. $wechatUser = app()->make(WechatUserServices::class);
  297. //更新用户信息
  298. $user = $wechatUser->wechatOauthAfter([$openid, $wechatInfo, $spreadId, $login_type, $userType]);
  299. $token = $this->createToken((int)$user['uid'], 'wechat');
  300. if ($token) {
  301. /** @var UserVisitServices $visitServices */
  302. $visitServices = app()->make(UserVisitServices::class);
  303. $visitServices->loginSaveVisit($user);
  304. return [
  305. 'token' => $token['token'],
  306. 'userInfo' => $user,
  307. 'expires_time' => $token['params']['exp'],
  308. ];
  309. } else
  310. throw new ApiException(410019);
  311. }
  312. /**
  313. * 是否关注
  314. * @param int $uid
  315. * @return bool
  316. */
  317. public function isSubscribe(int $uid)
  318. {
  319. if ($uid) {
  320. $subscribe = (bool)$this->dao->value(['uid' => $uid], 'subscribe');
  321. } else {
  322. $subscribe = true;
  323. }
  324. return $subscribe;
  325. }
  326. /**
  327. * @param array $userInfo
  328. * @param string $phone
  329. * @return array
  330. * @throws \think\db\exception\DataNotFoundException
  331. * @throws \think\db\exception\ModelNotFoundException
  332. */
  333. public function appAuth(array $userData, string $phone, string $userType = 'app')
  334. {
  335. $openid = $userData['openId'] ?? "";
  336. $userInfo = [
  337. 'phone' => $phone,
  338. 'unionid' => $userData['unionId'] ?? '',
  339. 'headimgurl' => $userData['avatarUrl'] ?? '',
  340. 'nickname' => $userData['nickName'] ?? '',
  341. 'province' => $userData['province'] ?? '',
  342. 'country' => $userData['country'] ?? '',
  343. 'city' => $userData['city'] ?? '',
  344. 'openid' => $openid,
  345. ];
  346. $login_type = $userType;
  347. $spreadId = $userInfo['spreadId'] ?? "";
  348. if (!$phone) {
  349. //获取是否强制绑定手机号
  350. $storeUserMobile = sys_config('store_user_mobile');
  351. if ($userInfo['unionid'] && $storeUserMobile) {
  352. /** @var UserServices $userServices */
  353. $userServices = app()->make(UserServices::class);
  354. $uid = $this->dao->value(['unionid' => $userInfo['unionid']], 'uid');
  355. $res = $userServices->value(['uid' => $uid], 'phone');
  356. if (!$uid && !$res) {
  357. return false;
  358. }
  359. }
  360. if ($openid && $storeUserMobile) {
  361. /** @var UserServices $userServices */
  362. $userServices = app()->make(UserServices::class);
  363. $uid = $this->dao->value(['openid' => $openid], 'uid');
  364. $res = $userServices->value(['uid' => $uid], 'phone');
  365. if (!$uid && !$res) {
  366. return false;
  367. }
  368. }
  369. }
  370. /** @var WechatUserServices $wechatUser */
  371. $wechatUser = app()->make(WechatUserServices::class);
  372. //更新用户信息
  373. $user = $wechatUser->wechatOauthAfter([$openid, $userInfo, $spreadId, $login_type, $userType]);
  374. $token = $this->createToken((int)$user['uid'], 'wechat');
  375. if ($token) {
  376. /** @var UserVisitServices $visitServices */
  377. $visitServices = app()->make(UserVisitServices::class);
  378. $visitServices->loginSaveVisit($user);
  379. return [
  380. 'token' => $token['token'],
  381. 'userInfo' => $user,
  382. 'expires_time' => $token['params']['exp'],
  383. 'isbind' => false
  384. ];
  385. } else
  386. throw new ApiException(410019);
  387. }
  388. }