UserSilentService.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: Elton
  5. * Date: 2019/12/31
  6. * Time: 14:46
  7. */
  8. namespace app\main\service;
  9. use app\common\library\Redis;
  10. use app\common\library\WeChatObject;
  11. use app\main\constants\CacheConstants;
  12. use app\main\constants\ErrorCodeConstants;
  13. use app\main\constants\OrderContents;
  14. use app\main\constants\PayConstants;
  15. use app\main\constants\RechargeConstants;
  16. use app\source\model\UserUpdate;
  17. use think\Config;
  18. use think\Request;
  19. class UserSilentService extends BaseService
  20. {
  21. /**
  22. * @var UserService
  23. */
  24. protected static $self = NULL;
  25. protected $preRedisKey = 'F-S-T:';
  26. /**
  27. * @return UserSilentService
  28. */
  29. public static function instance()
  30. {
  31. if (self::$self == null) {
  32. self::$self = new self();
  33. }
  34. return self::$self;
  35. }
  36. public function getUserSilentModel()
  37. {
  38. return model('UserSilent');
  39. }
  40. /**
  41. * 获取静默关注渠道商的业务域名
  42. * @return string
  43. */
  44. public function getSlientChannelHost()
  45. {
  46. $silent_default_channel = Config::get('site.silent_default_channel');
  47. $domain = getCurrentDomain($silent_default_channel);
  48. $domainArr = parse_url($domain);
  49. return $domainArr['host'] ?? '';
  50. }
  51. /**
  52. * 重新组装URL,参数里面增加 has_silent
  53. * @return string
  54. */
  55. public function refactorUrl($url)
  56. {
  57. $urlArr = parse_url($url);
  58. $queryArr = [];
  59. if (isset($urlArr['query'])) {
  60. $queryArr = $this->convertUrlQuery($urlArr['query']);
  61. }
  62. $queryArr['has_silent'] = '1';
  63. unset($queryArr['code']);
  64. $new_url = $urlArr['scheme'] . "://" . $urlArr['host'] . $urlArr['path'] . "?" . http_build_query($queryArr);
  65. return $new_url;
  66. }
  67. /**
  68. * Returns the url query as associative array
  69. *
  70. * @param string query
  71. * @return array params
  72. */
  73. function convertUrlQuery($query)
  74. {
  75. $queryParts = explode('&', $query);
  76. $params = array();
  77. foreach ($queryParts as $param) {
  78. $item = explode('=', $param);
  79. $params[$item[0]] = $item[1];
  80. }
  81. return $params;
  82. }
  83. /**
  84. * 判断是否已经有静默渠道关联数据
  85. * @param $user_id
  86. * @param $channel_id
  87. * @param $open_id
  88. * @param $tri_channel_id
  89. * @return bool
  90. */
  91. public function getSilentData($user_id, $channel_id, $open_id, $tri_channel_id)
  92. {
  93. $redisKey = $this->preRedisKey . $user_id . ':' . $channel_id . ":" . $open_id . ":" . $tri_channel_id;
  94. LogService::info('F:silent:rediskey:' . $redisKey);
  95. if (Redis::instance()->get($redisKey)) {
  96. return true;
  97. } else {
  98. $obj = $this->getUserSilentModel()
  99. ->where(
  100. [
  101. 'user_id' => $user_id,
  102. 'channel_id' => $channel_id,
  103. 'openid' => $open_id,
  104. 'tri_channel_id' => $tri_channel_id,
  105. ]
  106. )
  107. ->find();
  108. if ($obj) {
  109. Redis::instance()->set($redisKey, 1, 86400);
  110. return true;
  111. } else {
  112. return false;
  113. }
  114. }
  115. }
  116. /**
  117. * 存储静默渠道关联数据
  118. * @param $user_id
  119. * @param $channel_id
  120. * @param $open_id
  121. * @param $tri_channel_id
  122. * @param $tri_openid
  123. */
  124. public function saveSilentData($user_id, $channel_id, $open_id, $tri_channel_id, $tri_openid)
  125. {
  126. LogService::info("F:silent:saveSilentData2:{$user_id},{$channel_id},{$open_id},{$tri_channel_id},{$tri_openid}");
  127. $result = [];
  128. if ($user_id) {
  129. $redisKey = $this->preRedisKey . $user_id . ':' . $channel_id . ":" . $open_id . ":" . $tri_channel_id;
  130. LogService::info("F:silent:Redis:redisKey:" . $redisKey . ':value:' . Redis::instance()->get($redisKey));
  131. if (!Redis::instance()->get($redisKey)) {
  132. try {
  133. // 解决问题:线上有并发请求问题,会同时执行两条相同的插入操作
  134. $redisKeyNx = $this->preRedisKey . 'nx:' . $user_id;
  135. if (Redis::instance()->setnx($redisKeyNx, 1)) {
  136. Redis::instance()->expire($redisKeyNx, 10);
  137. $obj = $this->getUserSilentModel()
  138. ->where(
  139. [
  140. 'user_id' => $user_id,
  141. 'channel_id' => $channel_id,
  142. 'openid' => $open_id,
  143. 'tri_channel_id' => $tri_channel_id,
  144. 'tri_openid' => $tri_openid,
  145. ]
  146. )
  147. ->find();
  148. if (!$obj) {
  149. $tri_appid = '';
  150. $tri_arr = model('AdminConfig')->getAdminInfoAll($tri_channel_id);
  151. if (!empty($tri_arr['appid'])) {
  152. $tri_appid = $tri_arr['appid'];
  153. }
  154. LogService::info("F:silent:saveSilentData3:{$user_id},{$channel_id},{$open_id},{$tri_channel_id},{$tri_openid},{$tri_appid}");
  155. $insertData = [
  156. 'user_id' => $user_id,
  157. 'channel_id' => $channel_id,
  158. 'openid' => $open_id,
  159. 'tri_channel_id' => $tri_channel_id,
  160. 'tri_openid' => $tri_openid,
  161. 'tri_appid' => $tri_appid,
  162. 'updatetime' => time(),
  163. 'createtime' => time(),
  164. ];
  165. $this->getUserSilentModel()->insert($insertData);
  166. $result = $insertData;
  167. }
  168. }
  169. } catch (\Exception $exception) {
  170. LogService::error("F:silent: error:" . $exception->getMessage());
  171. }
  172. }
  173. }
  174. return $result;
  175. }
  176. /**
  177. * 校验渠道是否开启静默授权
  178. * @param $channel_id
  179. * @return bool
  180. */
  181. public function silentChannel($channel_id)
  182. {
  183. // 开启静默授权的渠道
  184. $silent_channels = Config::get('site.silent_channels') ?? '';
  185. // 默认转移到的渠道
  186. $silent_default_channel = Config::get('site.silent_default_channel') ?? '';
  187. if ($channel_id == $silent_default_channel) {
  188. return true;
  189. }
  190. if (!$silent_default_channel) {
  191. return false;
  192. }
  193. if (!$silent_channels) {
  194. return false;
  195. } elseif ($silent_channels == -1) {
  196. return true;
  197. } else {
  198. $silent_channels = str_replace(',', ',', $silent_channels);
  199. $silent_channels_arr = explode(',', $silent_channels);
  200. if (in_array($channel_id, $silent_channels_arr)) {
  201. return true;
  202. } else {
  203. return false;
  204. }
  205. }
  206. }
  207. /**
  208. * 渠道粉丝转移功能
  209. * @param $nowUserRow
  210. * @param $uid
  211. * @param $to_channel_id
  212. * @param $openid
  213. * @param $from_channel_id
  214. */
  215. public function channelFansTransfer($nowUserRow, $uid, $to_channel_id, $openid, $from_channel_id)
  216. {
  217. //当前用户
  218. if (empty($nowUserRow)) {
  219. $nowUserRow = $this->getUserSilentModel()->where([
  220. 'user_id' => $uid,
  221. 'channel_id' => $to_channel_id,
  222. 'openid' => $openid
  223. ])->find();
  224. }
  225. if (empty($nowUserRow)) {
  226. LogService::info("F:silent: info:未找到静默授权关联数据");
  227. return false;
  228. }
  229. //老用户
  230. $oldUserRow = $this->getUserSilentModel()->where([
  231. 'tri_channel_id' => $nowUserRow['tri_channel_id'],
  232. 'tri_openid' => $nowUserRow['tri_openid'],
  233. 'channel_id' => $from_channel_id
  234. ])->find();
  235. if (empty($oldUserRow)) {
  236. LogService::info("F:silent: info:新用户不需要转移.uid:" . $uid);
  237. return false;
  238. } else {
  239. $this->doTransfer($oldUserRow['user_id'], $uid);
  240. $record['uid'] = $uid;
  241. $record['from_cid'] = $from_channel_id;
  242. $record['to_cid'] = $to_channel_id;
  243. $record['createtime'] = time();
  244. model('ChannelUserTransferRecord')->save($record);
  245. }
  246. return true;
  247. }
  248. /**
  249. * 执行转移
  250. * @param $old_user_id
  251. * @param $current_user_id
  252. * @throws \Exception
  253. */
  254. public function doTransfer($old_user_id, $current_user_id)
  255. {
  256. //书架最新5本
  257. //老用户阅读记录
  258. $oldResult = BookService::instance()->getUserRecentlyRead()->setConnect($old_user_id)->getRecentlyRead(0, 5, $old_user_id, true);
  259. $oldData = $oldResult['data'];
  260. if (!empty($oldData)) {
  261. foreach ($oldData as $old) {
  262. $chapterInfo = BookService::instance()->getChapterInfo($old['book_id'], $old['chapter_id']);
  263. if ($chapterInfo->code == ErrorCodeConstants::SUCCESS) {
  264. $chapterInfo = $chapterInfo->data;
  265. BookService::instance()->setRecentlyRead($old['chapter_name'], $old['chapter_id'], $old['book_id'], $chapterInfo['idx'], $current_user_id);
  266. }
  267. }
  268. }
  269. //余额
  270. $kandian_total = FinancialService::instance()->getTotalKandianAndFreeKandian($old_user_id)->data;
  271. if ($kandian_total) {
  272. $this->addFreeKandian($current_user_id, $kandian_total, RechargeConstants::RECHARGE_TYPE_KANDIAN, OrderContents::ORDER_DEDUCT_YES, '2', '渠道用户转移看点', 20);
  273. }
  274. $aOldUserInfo = UserService::instance()->getUserModel()->getUserInfo($old_user_id);
  275. $newUserInfoObj = UserService::instance()->getUserModel()->getUserInfo($current_user_id);
  276. //vip转移
  277. if (intval($aOldUserInfo['vip_endtime']) > time()) {
  278. //$userData['id'] = $current_user_id;
  279. $rechargeData['user_id'] = $current_user_id;
  280. $rechargeData['type'] = 4;
  281. $rechargeData['notes'] = '渠道用户转移VIP时长';
  282. $rechargeData['edit_type'] = 2;
  283. $rechargeData['createtime'] = time();
  284. $vipAddTime = intval($aOldUserInfo['vip_endtime']) - time();
  285. if (intval($newUserInfoObj['vip_endtime']) > time()) {
  286. $userData['vip_endtime'] = $vipAddTime + intval($newUserInfoObj['vip_endtime']);
  287. $rechargeData['vip_starttime'] = intval($newUserInfoObj['vip_starttime']);
  288. } else {
  289. $userData['vip_endtime'] = $vipAddTime + time();
  290. $rechargeData['vip_starttime'] = time();
  291. }
  292. $rechargeData['day'] = floor($vipAddTime / 86400);
  293. $rechargeData['hour'] = floor(($vipAddTime - $rechargeData['day'] * 86400) / 3600);
  294. $userData['vip_starttime'] = model('Recharge')->getVipStartTime($current_user_id);
  295. $rechargeData['channel_vip_starttime'] = model('Recharge')->getChannelVipStartTime($current_user_id);
  296. if (ApiService::instance()->checkApiOn()) {
  297. $userUpdate = new UserUpdate();
  298. $userUpdate->setAddvipday($vipAddTime)->setId($current_user_id);
  299. \app\source\service\UserService::instance()->updateUser($userUpdate);
  300. } else {
  301. model('User')->setConnect($current_user_id)->where('id',$current_user_id)->update($userData); //更改用户表数据
  302. Redis::instance()->del('UN:' . $current_user_id);
  303. }
  304. model('Recharge')->setConnect($current_user_id)->insert($rechargeData); //插入充值记录
  305. }
  306. }
  307. /**
  308. * 添加免费书币
  309. * @param $user_id
  310. * @param $kandian
  311. * @param $order_id
  312. * @param $type
  313. * @param $edit_type
  314. * @param $notes
  315. * @param $dd
  316. * @return \app\main\model\object\ReturnObject
  317. */
  318. public function addFreeKandian($user_id, $kandian, $type, $dd, $edit_type, $notes, $day, $order_id = '')
  319. {
  320. $recharge = [
  321. 'user_id' => $user_id,
  322. 'type' => $type,
  323. 'kandian' => 0,
  324. 'remain_kandian' => 0,
  325. 'free_kandian' => $kandian,
  326. 'remain_free_kandian' => $kandian,
  327. 'free_endtime' => time() + $day * 86400,
  328. 'orders_id' => $order_id,
  329. 'book_id' => '',
  330. 'dd' => $dd,
  331. 'edit_type' => $edit_type,
  332. 'notes' => $notes,
  333. 'createtime' => time(),
  334. 'updatetime' => time(),
  335. 'business_line' => PayConstants::BUSINESS_WECHAT,
  336. ];
  337. $result = FinancialService::instance()->getRechargeModel()->setConnect($user_id)->insertGetId($recharge);
  338. Redis::instance()->del(CacheConstants::getFreeKandianUserRechargeListCacheKey($user_id));
  339. return $this->setData($result)->getReturn();
  340. }
  341. }