Activity.php 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016
  1. <?php
  2. /**
  3. * 活动
  4. */
  5. namespace app\weixin\service;
  6. use app\weixin\model\Activity as ActivityModel;
  7. use app\weixin\model\Books;
  8. use app\weixin\model\HeartMatch;
  9. use app\weixin\model\Invitation;
  10. use app\weixin\model\Member;
  11. use app\weixin\model\UserLog;
  12. use app\weixin\model\Wechat;
  13. use think\Db;
  14. class Activity
  15. {
  16. /**
  17. * 获取活动列表
  18. * @param $params 参数
  19. * @param int $pageSize 分页大小
  20. * @param string $field 字段
  21. * @param string $order 排序
  22. * @return mixed
  23. */
  24. public static function getList($params, $pageSize = 15, $field = '', $order = '')
  25. {
  26. $where = ['status' => 1, 'is_test'=> 0];
  27. /*$type = isset($params['type']) ? intval($params['type']) : 0;
  28. if ($type) {
  29. $where['type'] = $type;
  30. }*/
  31. $closeConfig = cmf_get_option('close_config');
  32. $closeConfig = $closeConfig? $closeConfig : config('weixin.site');
  33. $accessIp = isset($closeConfig['access_ip'])? $closeConfig['access_ip'] : '';
  34. $accessIp = $accessIp? explode(',', $accessIp) : [];
  35. if($accessIp && !in_array(get_client_ip(),$accessIp)){
  36. unset($where['is_test']);
  37. }
  38. $order = $order ? $order : 'is_top desc,is_sort desc,starttime desc,id desc';
  39. $field = $field ? $field : 'id,title,cover_img as thumb,price,act_nums,addtime,starttime,address,endtime';
  40. return ActivityModel::where($where)
  41. ->where('addtime','<=',time())
  42. ->whereNotIn('is_top',[1])
  43. ->field($field)
  44. ->order($order)
  45. ->paginate($pageSize)
  46. ->each(function ($item, $k) {
  47. $startTime = isset($item['starttime']) ? $item['starttime'] : 0;
  48. $endTime = isset($item['endtime']) ? $item['endtime'] : 0;
  49. $addTime = isset($item['addtime']) ? $item['addtime'] : 0;
  50. if ($startTime <= time() && $endTime >= time()) {
  51. $item['act_status'] = 1; // 进行中
  52. } else if ($endTime < time()) {
  53. $item['act_status'] = 2; // 已结束
  54. } else if ($startTime > time()) {
  55. $item['act_status'] = 0; // 未开始
  56. }
  57. $item['join_at'] = '';
  58. if(date('Y-m-d',$endTime) == date('Y-m-d', $startTime)){
  59. $item['join_at'] = date('Y.m.d H:i',$startTime).'~'.date('H:i', $endTime);
  60. }
  61. $item['thumb'] = isset($item['thumb']) ? cmf_get_image_preview_url($item['thumb']) : '';
  62. $item['publish_at'] = $addTime ? date('Y-m-d H:i:s', $addTime) : date('Y-m-d H:i:s');
  63. $item['start_at'] = $startTime ? date('Y.m.d H:i', $startTime) : date('Y.m.d H:i');
  64. $item['end_at'] = $endTime ? date('Y.m.d H:i', $endTime) : date('Y.m.d H:i');
  65. // 报名人列表和人数
  66. $showNum = config('activity.showNum');
  67. $showNum = $showNum ? $showNum : 6;
  68. $id = isset($item['id']) ? $item['id'] : 0;
  69. $result = Books::getBookList($id, $showNum);
  70. $item['book_num'] = isset($result['total']) ? intval($result['total']) : 0;
  71. $item['book_list'] = isset($result['data']) ? $result['data'] : [];
  72. return $item;
  73. });
  74. }
  75. /**
  76. * 获取用户活动
  77. * @param $params 参数
  78. * @param int $pageSize 分页大小
  79. * @param string $field 字段
  80. * @param string $order 排序
  81. * @return $this
  82. * @throws \think\exception\DbException
  83. */
  84. public static function getMemberActivityList($params, $pageSize = 15, $field = '', $order = '')
  85. {
  86. $order = $order ? $order : 'a.starttime desc,a.id desc';
  87. $userId = isset($params['user_id']) ? intval($params['user_id']) : 0;
  88. $type = isset($params['type']) ? intval($params['type']) : 0;
  89. $order = $type == 1 ? $order : 'b.book_at desc,b.id desc';
  90. $where = ['a.status' => 1,'b.status'=> 3,'b.uid'=> $userId];
  91. $field = $field ? $field : 'a.id,a.title,a.cover_img as thumb,b.status,b.is_signin,a.price,a.act_nums,a.addtime,a.starttime,a.address,a.endtime,b.id as book_id';
  92. $dataList = ActivityModel::alias('a')
  93. ->join('books b', 'a.id=b.aid', 'inner')
  94. ->where('addtime','<=',time())
  95. ->where($where)
  96. ->where(function($query) use($type){
  97. if ($type == 2) {
  98. return $query->where('b.is_signin','=', 1);
  99. } else if ($type == 3) {
  100. return $query->where('b.is_signin','in', [2,3]);
  101. }
  102. })
  103. ->field($field)
  104. ->order($order)
  105. ->group('a.id')
  106. ->paginate($pageSize)
  107. ->each(function ($item, $k) {
  108. $startTime = isset($item['starttime']) ? $item['starttime'] : 0;
  109. $endTime = isset($item['endtime']) ? $item['endtime'] : 0;
  110. $addTime = isset($item['addtime']) ? $item['addtime'] : 0;
  111. if ($startTime <= time() && $endTime >= time()) {
  112. $item['act_status'] = 1; // 进行中
  113. } else if ($endTime < time()) {
  114. $item['act_status'] = 2; // 已结束
  115. } else if ($startTime > time()) {
  116. $item['act_status'] = 0; // 未开始
  117. }
  118. $item['join_at'] = '';
  119. if(date('Y-m-d',$endTime) == date('Y-m-d', $startTime)){
  120. $item['join_at'] = date('Y.m.d H:i',$startTime).'~'.date('H:i', $endTime);
  121. }
  122. $item['thumb'] = isset($item['thumb']) ? cmf_get_image_preview_url($item['thumb']) : '';
  123. $item['publish_at'] = $addTime ? date('Y-m-d H:i:s', $addTime) : date('Y-m-d H:i:s');
  124. $item['start_at'] = $startTime ? date('Y-m-d H:i', $startTime) : date('Y-m-d H:i');
  125. $item['end_at'] = $endTime ? date('Y-m-d H:i', $endTime) : date('Y-m-d H:i');
  126. // 报名人列表和人数
  127. $showNum = config('activity.showNum');
  128. $showNum = $showNum ? $showNum : 6;
  129. $id = isset($item['id']) ? $item['id'] : 0;
  130. $result = Books::getBookList($id, $showNum);
  131. $item['book_num'] = isset($result['total']) ? intval($result['total']) : 0;
  132. $item['book_list'] = isset($result['data']) ? $result['data'] : [];
  133. return $item;
  134. });
  135. return $dataList;
  136. }
  137. /**
  138. * 获取详情
  139. * @param $where
  140. * @param string $field
  141. * @return array|null|\PDOStatement|string|\think\Model
  142. */
  143. public static function getInfo($where, $field = '')
  144. {
  145. $field = $field ? $field : 'id,title,cover_img as thumb,price,service_time,market_price,market_time,member_discount,credit,act_nums,url,addtime,starttime,endtime,description,address';
  146. $info = ActivityModel::where($where)
  147. ->where('addtime','<=',time())
  148. ->where('id', '>', 0)
  149. ->field($field)
  150. ->find();
  151. if ($info) {
  152. $addTime = isset($info['addtime']) ? $info['addtime'] : 0;
  153. $startTime = isset($info['starttime']) ? $info['starttime'] : 0;
  154. $endTime = isset($info['endtime']) ? $info['endtime'] : 0;
  155. if ($startTime <= time() && $endTime >= time()) {
  156. $info['act_status'] = 1; // 进行中
  157. } else if ($endTime < time()) {
  158. $info['act_status'] = 2; // 已结束
  159. } else if ($startTime > time()) {
  160. $info['act_status'] = 0; // 未开始
  161. }
  162. $info['join_at'] = '';
  163. if(date('Y-m-d',$endTime) == date('Y-m-d', $startTime)){
  164. $info['join_at'] = date('Y.m.d H:i',$startTime).'~'.date('H:i', $endTime);
  165. }
  166. if($startTime==0 && $endTime == 0){
  167. $info['act_status'] = 1; // 进行中
  168. }
  169. $isHide = floor((time()-$endTime)/86400)>=3? 1 : 0;
  170. $info['publish_at'] = $addTime ? date('Y-m-d H:i:s', $addTime) : date('Y-m-d H:i:s');
  171. $info['publish_format_at'] = getFormatTime($info['publish_at']);
  172. $info['start_at'] = $startTime ? date('Y.m.d H:i', $startTime) : date('Y.m.d H:i');
  173. $info['end_at'] = $endTime ? date('Y.m.d H:i', $endTime) : date('Y.m.d H:i');
  174. $info['thumb'] = isset($info['thumb']) ? cmf_get_image_preview_url($info['thumb']) : '';
  175. $info['is_hide'] = $isHide;
  176. if (isset($info['content'])) {
  177. $info['content'] = htmlspecialchars_decode($info['content']);
  178. }
  179. }
  180. return $info;
  181. }
  182. /**
  183. * 报名
  184. * @param $userId
  185. * @param $aid
  186. * @param $inviteCode 邀请码
  187. * @return array|int
  188. */
  189. public static function catchBook($userId, $aid, $inviteCode='')
  190. {
  191. // 验证活动是否存在
  192. $field = 'id,starttime,endtime,price,market_price,market_time,member_discount,addtime,is_top,credit,act_nums';
  193. $activityInfo = Activity::getInfo(['id' => $aid, 'status' => 1], $field);
  194. if (empty($activityInfo)) {
  195. return 5001;
  196. }
  197. // 验证报名时间
  198. $startTime = isset($activityInfo['starttime']) ? intval($activityInfo['starttime']) : 0;
  199. $endTime = isset($activityInfo['endtime']) ? intval($activityInfo['endtime']) : 0;
  200. $addTime = isset($activityInfo['addtime']) ? intval($activityInfo['addtime']) : 0;
  201. $isTop = isset($activityInfo['is_top']) ? intval($activityInfo['is_top']) : 0;
  202. if($addTime > time()){
  203. return 5000;
  204. }
  205. if ($endTime && time() > $endTime) {
  206. return 5002;
  207. }
  208. // 活动截止前多久不允许报名
  209. $siteInfo = cmf_get_site_info();
  210. $bookTime = isset($siteInfo['book_time']) ? intval($siteInfo['book_time']) : 0;
  211. if ($isTop==0 && $startTime && $bookTime) {
  212. if(time() > $startTime){
  213. return 5026;
  214. }
  215. if(time() + $bookTime*3600 > $startTime){
  216. return lang('book_end', ['time' => $bookTime]);
  217. }
  218. }
  219. // 验证当前用户是否已经报名
  220. if (Books::checkUserBook($userId, $aid)) {
  221. return 5004;
  222. }
  223. // 验证操作频繁
  224. $lockCache = "books:lock:" . $userId . "_" . $aid;
  225. if (PRedis::get($lockCache)) {
  226. return 1013;
  227. }
  228. // 验证报名人数
  229. $sex = Member::where(['id' => $userId])->value('sex');
  230. $limitNum = isset($activityInfo['act_nums']) ? intval($activityInfo['act_nums']) : 0;
  231. $limitNum = floor($limitNum/2);
  232. $bookNum = Books::getBookCount($aid, $sex, [2,3]); // 已报名人数
  233. if ($limitNum && $bookNum >= $limitNum) {
  234. $sexText = $isTop? '' : ($sex==1? '男生' : '女生');
  235. return lang('book_limit',['sex'=> $sexText]);
  236. }
  237. // 验证价格
  238. $price = isset($activityInfo['price']) ? floatval($activityInfo['price']) : 0; // 活动价格
  239. $marketPrice = isset($activityInfo['market_price']) ? floatval($activityInfo['market_price']) : 0; // 早市价
  240. $marketTime = isset($activityInfo['market_time']) ? intval($activityInfo['market_time']) : 0;
  241. $payPrice = $marketTime >= time() && $marketPrice>0? $marketPrice : $price;
  242. $credit = isset($activityInfo['credit']) ? floatval($activityInfo['credit']) : 0;
  243. if (empty($payPrice)) {
  244. return 1012;
  245. }
  246. // 验证VIP折扣价
  247. $memberInfo = Member::where(['id' => $userId])->field('id,vip_auth,vip_expire,openid,redheart,user_nickname,real_name')->find();
  248. $vipAuth = isset($memberInfo['vip_auth']) ? intval($memberInfo['vip_auth']) : 0;
  249. $vipExpire = isset($memberInfo['vip_expire']) ? intval($memberInfo['vip_expire']) : 0;
  250. // VIP有效期内
  251. $oldPrice = $payPrice;
  252. $memberDiscount = isset($activityInfo['member_discount']) ? floatval($activityInfo['member_discount']) : 0;
  253. if($vipAuth && $vipExpire > time() && $memberDiscount && $memberDiscount < 1){
  254. $payPrice = floatval($memberDiscount * $payPrice);
  255. }
  256. // 验证邀请码如果有
  257. if($inviteCode){
  258. // 验证邀请码是否有效
  259. $codeInfo = Invitation::getInfo($inviteCode);
  260. if(empty($codeInfo)){
  261. return 5027;
  262. }
  263. if($codeInfo['user_id']>0 && $codeInfo['user_id'] != $userId){
  264. return 5030;
  265. }
  266. // 已被使用
  267. if($codeInfo['status'] == 3){
  268. return 5028;
  269. }
  270. // 过期
  271. if($codeInfo['expired_at'] < time() || $codeInfo['status'] == 4){
  272. return 5029;
  273. }
  274. // 验证邀请码使用已经使用报名
  275. if(Books::checkInviteCodeBook($inviteCode)){
  276. return 5028;
  277. }
  278. // 验证邀请码是否符合使用金额
  279. if($payPrice>$codeInfo['quota']){
  280. return lang('5031',['money'=> $codeInfo['quota']]);
  281. }
  282. }
  283. // 报名数据
  284. $orderSn = makeTradeNo('BK', $userId);
  285. $bookData = [
  286. 'book_num' => '',
  287. 'uid' => $userId,
  288. 'aid' => $aid,
  289. 'order_sn' => $orderSn,
  290. 'money' => $payPrice,
  291. 'credit' => $credit,
  292. 'created_at' => date('Y-m-d H:i:s'),
  293. 'remark' => "活动报名:原价{$oldPrice},折扣{$memberDiscount}折",
  294. 'status' => 1,
  295. ];
  296. // 如果是邀请码报名直接支付
  297. if($inviteCode){
  298. $bookData['pay_type'] = 4;
  299. $bookData['money'] = 0;
  300. $bookData['credit'] = 0;
  301. $bookData['invite_code'] = $inviteCode;
  302. $bookData['book_at'] = date('Y-m-d H:i:s');
  303. $bookData['remark'] = '邀请码报名:'.$inviteCode;
  304. $bookData['status'] = 2;
  305. }
  306. // 写入记录
  307. Db::startTrans();
  308. $logData = ['info' => $activityInfo, 'bookData' => $bookData, 'date' => date('Y-m-d H:i:s')];
  309. PRedis::set($lockCache, $logData, 3);
  310. PRedis::set('books:logs:' . $userId . '_' . $aid, $logData, 600);
  311. if (!$bookId = Books::insertGetId($bookData)) {
  312. PRedis::del($lockCache);
  313. Db::rollback();
  314. return 5006;
  315. }
  316. // 用户操作记录
  317. UserLog::saveLog(['user_id' => $userId, 'type' => 4, 'content' => '报名下单:' . $orderSn]);
  318. Db::commit();
  319. // 如果是邀请码报名发送消息
  320. self::catchBookByInviteCode($memberInfo, $bookData, $activityInfo);
  321. PRedis::del($lockCache);
  322. return ['id' => $bookId];
  323. }
  324. /**
  325. * 邀请码报名支付成功处理
  326. * @param $memberInfo 用户信息
  327. * @param $bookInfo 报名信息
  328. * @param $activityInfo 活动信息
  329. * @return false
  330. * @throws \think\Exception
  331. * @throws \think\exception\PDOException
  332. */
  333. protected static function catchBookByInviteCode($memberInfo,$bookInfo, $activityInfo)
  334. {
  335. $openid = isset($memberInfo['openid']) ? $memberInfo['openid'] : '';
  336. $nickname = isset($memberInfo['user_nickname']) ? $memberInfo['user_nickname'] : '';
  337. $outTradeNo = isset($bookInfo['order_sn'])? trim($bookInfo['order_sn']) : '';
  338. $inviteCode = isset($bookInfo['invite_code'])? trim($bookInfo['invite_code']) : '';
  339. $bookStatus = isset($bookInfo['status'])? intval($bookInfo['status']) : 0;
  340. $money = isset($bookInfo['money'])? floatval($bookInfo['money']) : 0;
  341. $credit = isset($bookInfo['credit'])? floatval($bookInfo['credit']) : 0;
  342. $payType = isset($bookInfo['pay_type'])? intval($bookInfo['pay_type']) : 0;
  343. $title = isset($activityInfo['title'])? trim($activityInfo['title']) : '无';
  344. $bookAt = date('Y.m.d H:i');
  345. if($bookStatus != 2 || $payType!= 4 || empty($inviteCode)){
  346. return false;
  347. }
  348. // 更新邀请码数据
  349. Invitation::where(['code'=> $inviteCode])
  350. ->update(['user_id'=> $memberInfo['id'],'status'=> 3,'update_time'=>time()]);
  351. // 发送报名消息
  352. if($openid){
  353. $params = [
  354. 'title' => "我们已经收到您的报名,稍后会进行审核,请留意后续的通知哦!\n\n姓名:\t{$nickname}(昵称)\n\n支付单号:\t{$outTradeNo}\n\n支付金额:\t邀请码支付",
  355. 'remark' => "转发积姻缘,把活动分享给身边的朋友,一起来脱单哦!",
  356. 'type' => 'book',
  357. 'keywords' => [
  358. 'keyword1' => [
  359. 'value' => $title,
  360. 'color' => '#173177',
  361. ],
  362. 'keyword2' => [
  363. 'value' => $bookAt,
  364. 'color' => '#173177',
  365. ],
  366. 'keyword3' => [
  367. 'value' => '待审核',
  368. 'color' => '#173177',
  369. ],
  370. 'keyword4' => [
  371. 'value' => "活动费用{$money}元,活动守时金{$credit}元。",
  372. 'color' => '#173177',
  373. ],
  374. ],
  375. 'url' => url('/weixin/activity/bookdetail?id='.$activityInfo['id'], '', '', true),
  376. ];
  377. PRedis::set('payments:books:message_' . $outTradeNo, ['bookInfo' => $bookInfo,'activity'=> $activityInfo, 'params' => $params], 600);
  378. Wechat::sendTplMsg($openid, $params);
  379. }
  380. }
  381. /**
  382. * 置顶报名
  383. * @param $userId
  384. * @param $aid
  385. * @return array|int
  386. */
  387. public static function catchTopBook($userId, $aid)
  388. {
  389. // 验证活动是否存在
  390. $field = 'id,starttime,endtime,price,market_price,market_time,service_time,member_discount,addtime,is_top,credit,act_nums';
  391. $activityInfo = Activity::getInfo(['id' => $aid], $field);
  392. if (empty($activityInfo)) {
  393. return 5001;
  394. }
  395. // 验证报名时间
  396. $startTime = isset($activityInfo['starttime']) ? intval($activityInfo['starttime']) : 0;
  397. $endTime = isset($activityInfo['endtime']) ? intval($activityInfo['endtime']) : 0;
  398. $addTime = isset($activityInfo['addtime']) ? intval($activityInfo['addtime']) : 0;
  399. $serviceTime = isset($activityInfo['service_time']) ? intval($activityInfo['service_time']) : 0;
  400. $isTop = isset($activityInfo['is_top']) ? intval($activityInfo['is_top']) : 0;
  401. if($addTime > time()){
  402. return 5000;
  403. }
  404. if ($endTime && time() > $endTime) {
  405. return 5002;
  406. }
  407. // 活动截止前多久不允许报名
  408. $siteInfo = cmf_get_site_info();
  409. $bookTime = isset($siteInfo['book_time']) ? intval($siteInfo['book_time']) : 0;
  410. if ($isTop==0 && $startTime && $bookTime) {
  411. if(time() > $startTime){
  412. return 5026;
  413. }
  414. if(time() + $bookTime*3600 > $startTime){
  415. return lang('book_end', ['time' => $bookTime]);
  416. }
  417. }
  418. // 验证操作频繁
  419. $lockCache = "books:lock:" . $userId . "_" . $aid;
  420. if (PRedis::get($lockCache)) {
  421. return 1013;
  422. }
  423. // 验证已报名
  424. $memberInfo = Member::where(['id' => $userId])->field('sex,is_top,top_expire')->find();
  425. $isTopShow = isset($memberInfo['is_top'])? $memberInfo['is_top'] : 0;
  426. $topExpire = isset($memberInfo['top_expire'])? $memberInfo['top_expire'] : '';
  427. if($isTopShow && $topExpire && date('Y-m-d H:i:s') < $topExpire){
  428. return 2046;
  429. }
  430. $sex = isset($memberInfo['sex'])? $memberInfo['sex'] : 1;
  431. $limitNum = isset($activityInfo['act_nums']) ? intval($activityInfo['act_nums']) : 0;
  432. $bookCount = Member::where(['user_status'=> 1,'is_top'=>1,'sex'=> $sex])
  433. ->where('top_expire','>', date('Y-m-d H:i:s'))
  434. ->count('id');
  435. if ($limitNum && $bookCount >= $limitNum) {
  436. $sexText = $isTop? '' : ($sex==1? '男生' : '女生');
  437. return lang('book_limit',['sex'=> $sexText]);
  438. }
  439. // 验证价格
  440. $price = isset($activityInfo['price']) ? floatval($activityInfo['price']) : 0; // 活动价格
  441. $marketPrice = isset($activityInfo['market_price']) ? floatval($activityInfo['market_price']) : 0; // 早市价
  442. $marketTime = isset($activityInfo['market_time']) ? intval($activityInfo['market_time']) : 0;
  443. $payPrice = $marketTime >= time() && $marketPrice>0? $marketPrice : $price;
  444. $credit = isset($activityInfo['credit']) ? floatval($activityInfo['credit']) : 0;
  445. if (empty($payPrice)) {
  446. return 1012;
  447. }
  448. /*
  449. // 验证VIP折扣价
  450. $memberInfo = Member::where(['id' => $userId])->field('vip_auth,vip_expire')->find();
  451. $vipAuth = isset($memberInfo['vip_auth']) ? intval($memberInfo['vip_auth']) : 0;
  452. $vipExpire = isset($memberInfo['vip_expire']) ? intval($memberInfo['vip_expire']) : 0;
  453. // VIP有效期内
  454. $oldPrice = $payPrice;
  455. $memberDiscount = isset($activityInfo['member_discount']) ? floatval($activityInfo['member_discount']) : 0;
  456. if($vipAuth && $vipExpire > time() && $memberDiscount && $memberDiscount < 1){
  457. $payPrice = floatval($memberDiscount * $payPrice);
  458. }
  459. */
  460. // 报名数据
  461. $orderSn = makeTradeNo('BK', $userId);
  462. $bookData = [
  463. 'book_num' => '',
  464. 'uid' => $userId,
  465. 'aid' => $aid,
  466. 'order_sn' => $orderSn,
  467. 'money' => $payPrice,
  468. 'credit' => $credit,
  469. 'created_at' => date('Y-m-d H:i:s'),
  470. 'remark' => "首页置顶报名:价格{$payPrice}",
  471. 'status' => 1,
  472. ];
  473. // 写入记录
  474. Db::startTrans();
  475. $logData = ['info' => $activityInfo, 'bookData' => $bookData, 'date' => date('Y-m-d H:i:s')];
  476. PRedis::set($lockCache, $logData, 3);
  477. PRedis::set('books:logs:' . $userId . '_' . $aid, $logData, 600);
  478. if (!$bookId = Books::insertGetId($bookData)) {
  479. PRedis::del($lockCache);
  480. Db::rollback();
  481. return 5006;
  482. }
  483. // 用户操作记录
  484. UserLog::saveLog(['user_id' => $userId, 'type' => 4, 'content' => '置顶报名下单:' . $orderSn]);
  485. Db::commit();
  486. PRedis::del($lockCache);
  487. return ['id' => $bookId];
  488. }
  489. /**
  490. * 报名审核
  491. * @param $bookId 报名记录ID
  492. * @param $checkStatus 审核状态:3-审核成功,5-审核失败退款
  493. * @param $failRemark 审核失败原因
  494. * @return int
  495. */
  496. public static function bookConfirm($bookId, $checkStatus, $failRemark='')
  497. {
  498. try {
  499. $bookInfo = Books::getInfo(['id' => $bookId]);
  500. $status = isset($bookInfo['status']) ? intval($bookInfo['status']) : 0;
  501. $userId = isset($bookInfo['uid']) ? intval($bookInfo['uid']) : 0;
  502. $payType = isset($bookInfo['pay_type']) ? intval($bookInfo['pay_type']) : 0;
  503. $inviteCode = isset($bookInfo['invite_code']) ? trim($bookInfo['invite_code']) : '';
  504. $money = isset($bookInfo['money']) ? floatval($bookInfo['money']) : 0;
  505. $aid = isset($bookInfo['aid']) ? intval($bookInfo['aid']) : 0;
  506. if (empty($userId) || empty($aid)) {
  507. return 1012;
  508. }
  509. if ($status != 2) {
  510. return 5016;
  511. }
  512. if(!in_array($checkStatus, [3,5])){
  513. return 2129;
  514. }
  515. // 活动信息
  516. $activityInfo = ActivityModel::where(['id' => $aid])
  517. ->field('id,starttime,act_nums,service_time,title,join_address')
  518. ->find();
  519. // 审核数据更新
  520. $bookNo = '';
  521. $updateData = ['status' => $checkStatus, 'remark' => $checkStatus==3? '审核成功':'审核失败'];
  522. if($checkStatus == 3){
  523. $sex = Member::where(['id' => $userId])->value('sex');
  524. // 置顶报名
  525. if($aid == 16){
  526. $limitNum = isset($activityInfo['act_nums']) ? intval($activityInfo['act_nums']) : 0;
  527. $bookCount = Member::where(['user_status'=> 1,'is_top'=>1,'sex'=> $sex])
  528. ->where('top_expire','>', date('Y-m-d H:i:s'))
  529. ->count('id');
  530. if ($limitNum && $bookCount >= $limitNum) {
  531. return lang('book_limit',['sex'=> ($sex==1?'男生':'女生')]);
  532. }
  533. }
  534. $prefix = $sex == 1 ? 'G' : ($sex == 2 ? 'M' : '');
  535. $bookNo = Books::makeBookNo($aid, $prefix, $sex);
  536. $updateData['book_num'] = $bookNo;
  537. $updateData['book_num_val'] = str_replace(['M','G'],'', $bookNo);
  538. }
  539. $res = Books::saveData(['id' => $bookId], $updateData);
  540. if ($res) {
  541. // 更新加入单身推荐
  542. if($aid == 15 && $checkStatus == 3){
  543. Member::saveData(['id'=> $userId],['is_tuijian'=> 1,'tuijian_time'=> date('Y-m-d H:i:s')]);
  544. }
  545. // 置顶推荐更新
  546. $servceTime = isset($activityInfo['service_time'])? $activityInfo['service_time'] : 0;
  547. if($servceTime && $aid == 16 && $checkStatus == 3){
  548. $expire = $servceTime * 3600 * 24 + time();
  549. Member::saveData(['id'=> $userId],['is_top'=> 1,'top_expire'=> date('Y-m-d H:i:s', $expire)]);
  550. }
  551. // 邀请码报名审核不通过
  552. $money = ($payType == 4 || $inviteCode)? '邀请码支付': $money;
  553. if(($payType == 4 || $inviteCode) && $checkStatus==5){
  554. Invitation::where(['user_id'=> $userId,'code'=> $inviteCode])->update(['status'=>2,'update_time'=>time()]);
  555. }
  556. $memberInfo = Member::getInfo(['id' => $userId]);
  557. $openid = isset($memberInfo['openid']) ? $memberInfo['openid'] : '';
  558. $nickname = isset($memberInfo['user_nickname']) ? $memberInfo['user_nickname'] : '';
  559. // 发送消息
  560. if ($openid) {
  561. $title = isset($activityInfo['title']) ? trim($activityInfo['title']) : '无';
  562. $address = isset($activityInfo['join_address']) ? trim($activityInfo['join_address']) : '无';
  563. $startTime = isset($activityInfo['starttime']) ? intval($activityInfo['starttime']) : 0;
  564. $startTime = $startTime ? date('Y.m.d H:i', $startTime) : '详情查看';
  565. $startTime = $aid==15? date('Y.m.d H:i') : $startTime;
  566. $orderSn = isset($bookInfo['order_sn']) ? trim($bookInfo['order_sn']) : '';
  567. if($checkStatus==3){
  568. $url = $aid==15? url('/weixin/activity/book?id=' . $aid, '', '', true) : url('/weixin/activity/bookdetail?id=' . $aid, '', '', true);
  569. $params = [
  570. 'title' => "恭喜!您报名的活动已经通过审核!请提前安排好时间,准时参加!风雨无阻,真诚第一\n\n姓名:\t{$nickname}(昵称)\n\n支付金额:\t{$money}\n\n报名编号:\t{$bookNo}\n\n当前进度:\t待参加",
  571. 'remark' => "亲,记得安排好时间哦!",
  572. 'type' => 'book_confirm',
  573. 'keywords' => [
  574. 'keyword1' => [
  575. 'value' => $title,
  576. 'color' => '#173177',
  577. ],
  578. 'keyword2' => [
  579. 'value' => $startTime,
  580. 'color' => '#173177',
  581. ],
  582. 'keyword3' => [
  583. 'value' => $address,
  584. 'color' => '#173177',
  585. ],
  586. ],
  587. 'url' => $url,
  588. ];
  589. }else{
  590. $params = [
  591. 'title' => ($failRemark? $failRemark : '报名审核失败')."\n\n姓名:\t{$nickname}(昵称)\n\n支付金额:\t{$money}",
  592. 'remark' => "转发积姻缘,把活动分享给身边的朋友,一起来脱单哦!",
  593. 'type' => 'book',
  594. 'keywords' => [
  595. 'keyword1' => [
  596. 'value' => $title,
  597. 'color' => '#173177',
  598. ],
  599. 'keyword2' => [
  600. 'value' => $startTime,
  601. 'color' => '#173177',
  602. ],
  603. 'keyword3' => [
  604. 'value' => '审核失败',
  605. 'color' => '#173177',
  606. ],
  607. 'keyword4' => [
  608. 'value' => "...",
  609. 'color' => '#173177',
  610. ],
  611. ],
  612. 'url' => url('/weixin/activity/book?id=' . $aid, '', '', true),
  613. ];
  614. }
  615. PRedis::set('payments:books:confirmMessage_' . $orderSn, ['book' => $bookInfo, 'params' => $params], 600);
  616. Wechat::sendTplMsg($openid, $params);
  617. }
  618. return ['id' => $bookId];
  619. } else {
  620. return 5015;
  621. }
  622. } catch (\Exception $exception){
  623. return 1015;
  624. }
  625. }
  626. /**
  627. * 互选匹配
  628. * @param $aid 活动ID
  629. * @return int
  630. */
  631. public static function heartMatch($aid)
  632. {
  633. try {
  634. // 验证活动是否有效
  635. $activityInfo = ActivityModel::where(['id' => $aid, 'status' => 1])
  636. ->field('id,starttime,title,address,status,is_match')
  637. ->find();
  638. if (empty($activityInfo)) {
  639. return 5001;
  640. }
  641. // 验证状态和匹配状态
  642. $isMatch = isset($activityInfo['is_match']) ? intval($activityInfo['is_match']) : 0;
  643. if ($isMatch == 1) {
  644. return 6002;
  645. }
  646. // 活动开始时间
  647. $startTime = isset($activityInfo['starttime']) ? intval($activityInfo['starttime']) : 0;
  648. if (time() < $startTime) {
  649. return 5003;
  650. }
  651. // 互选模式匹配
  652. $siteInfo = $siteInfo = cmf_get_site_info();
  653. $contactType = isset($siteInfo['contact_type']) ? $siteInfo['contact_type'] : 1;
  654. if($contactType == 3){
  655. // 更新匹配状态
  656. ActivityModel::where(['id'=> $aid])->update(['is_match'=> 1]);
  657. // 获取用户列表
  658. // 处理互选
  659. return ['result'=> $aid];
  660. }
  661. // 获取匹配互选列表
  662. $matchList = Books::getMatchUserList($aid);
  663. if (empty($matchList)) {
  664. return 6003;
  665. }
  666. // 更新匹配状态
  667. ActivityModel::where(['id'=> $aid])->update(['is_match'=> 1]);
  668. // 用户匹配列表处理
  669. $results = [];
  670. PRedis::set('matchs:activity_' . $aid.':users', $matchList, 600);
  671. foreach ($matchList as $k => $userData) {
  672. $userId = isset($userData['uid']) ? intval($userData['uid']) : 0;
  673. $heartUids = isset($userData['heart_uids'])? $userData['heart_uids'] : '';
  674. $heartUids = $heartUids? explode(',', $heartUids) : [];
  675. if(empty($heartUids)){
  676. continue;
  677. }
  678. foreach ($heartUids as $kk => $uid) {
  679. $matchData = isset($matchList[$uid]) ? $matchList[$uid] : [];
  680. $uids = isset($matchData['heart_uids']) ? $matchData['heart_uids'] : '';
  681. $uids = $uids? explode(',', $uids) : [];
  682. $index = array_search($userId, $uids);
  683. if ($index !== false && $index >= 0) {
  684. $dvalue = abs($kk - $index)+1;
  685. if(($kk + 1)>= ($index + 1)){
  686. $key = ($kk + 1) . '_' . $dvalue . '_' . ($index + 1);
  687. }else{
  688. $key = ($index + 1) . '_' . $dvalue . '_' . ($kk + 1);
  689. }
  690. $results[$key] = isset($results[$key]) ? $results[$key] : [];
  691. $results[$key][] = [$userData, $matchData];
  692. }
  693. }
  694. }
  695. ksort($results, 1);
  696. $successArr = $successId = [];
  697. foreach ($results as $result) {
  698. foreach ($result as $val) {
  699. // 匹配用户数据
  700. $userData = isset($val[0]) ? $val[0] : 0;
  701. $matchData = isset($val[1]) ? $val[1] : 0;
  702. $mid = isset($userData['id'])? intval($userData['id']) : 0;
  703. $userId = isset($userData['uid'])? intval($userData['uid']) : 0;
  704. $matchId = isset($matchData['id']) ? intval($matchData['id']) : 0;
  705. $matchUid = isset($matchData['uid']) ? intval($matchData['uid']) : 0;
  706. // 验证用户是否已经被匹配,若是则不需要再次匹配
  707. if (!$userId || !$matchUid || PRedis::get('matchs:activity_' . $aid . ':lock:u_' . $userId) || PRedis::get('matchs:activity_' . $aid . ':lock:u_' . $matchUid)) {
  708. PRedis::set('matchs:activity_' . $aid . ':error:u_' . $userId.'_'.$matchUid, ['data' => $userData, 'match'=> $matchData, 'error' => '当前匹配用户已被匹配过'], 600);
  709. continue;
  710. }
  711. // 更新匹配数据
  712. Db::startTrans();
  713. $matchAt = time();
  714. $updateData = [
  715. 'match_uid' => $matchUid,
  716. 'match_at' => date('Y-m-d H:i:s', $matchAt),
  717. 'status' => 2,
  718. ];
  719. PRedis::set('matchs:activity_' . $aid . ':update:u_' . $userId, ['data' => $updateData, 'match' => $matchData], 600);
  720. if (!HeartMatch::where(['id' => $mid])->update($updateData)) {
  721. Db::rollback();
  722. continue;
  723. }
  724. $updateData = [
  725. 'match_uid' => $userId,
  726. 'match_at' => date('Y-m-d H:i:s', $matchAt),
  727. 'status' => 2,
  728. ];
  729. PRedis::set('matchs:activity_' . $aid . ':update:u_' . $matchUid, ['data' => $updateData, 'match' => $userData], 600);
  730. if (!HeartMatch::where(['id' => $matchId])->update($updateData)) {
  731. Db::rollback();
  732. continue;
  733. }
  734. // 提交
  735. Db::commit();
  736. $successArr[] = $userId.' 匹配 '.$matchUid;
  737. //echo $userId.' 匹配 '.$matchUid."\n";
  738. $successId[] = $userId;
  739. $successId[] = $matchUid;
  740. // 匹配加锁
  741. PRedis::set('matchs:activity_' . $aid . ':lock:u_' . $userId, $matchData, 24 * 3600);
  742. PRedis::set('matchs:activity_' . $aid . ':lock:u_' . $matchUid, $userData, 24 * 3600);
  743. // 推送匹配成功消息
  744. $openid = isset($userData['openid']) ? trim($userData['openid']) : '';
  745. if ($openid) {
  746. $bookNo = isset($userData['book_no']) ? trim($userData['book_no']) : '无';
  747. $matchName = isset($matchData['user_nickname']) ? $matchData['user_nickname'] : '';
  748. $matchNo = isset($matchData['book_no']) ? trim($matchData['book_no']) : '无';
  749. $matchWechatCode = isset($matchData['wechat_code']) ? trim($matchData['wechat_code']) : '无';
  750. $matchSex = isset($matchData['sex']) ? intval($matchData['sex']) : 0;
  751. $matchSex = $matchSex == 1 ? '男' : '女';
  752. $title = isset($activityInfo['title']) ? trim($activityInfo['title']) : '无';
  753. $matchTime = $matchAt ? date('Y.m.d H:i', $matchAt) : date('Y.m.d H:i');
  754. $params = [
  755. 'title' => "恭喜!活动缘分互选匹配成功,已为您匹配到缘分用户!\n\n活动主题:\t{$title}\n\n您的编号:\t{$bookNo}\n\n对方编号:\t{$matchNo}\n\n匹配时间:\t{$matchTime}\n\n对方微信号:\t{$matchWechatCode}",
  756. 'remark' => "感谢您的使用,点击详情查看互选信息",
  757. 'type' => 'match',
  758. 'keywords' => [
  759. 'name' => [
  760. 'value' => $matchName.'(昵称)',
  761. 'color' => '#173177',
  762. ],
  763. 'sex' => [
  764. 'value' => $matchSex,
  765. 'color' => '#173177',
  766. ],
  767. 'tel' => [
  768. 'value' => '隐私信息不显示',
  769. 'color' => '#173177',
  770. ],
  771. ],
  772. 'url' => url('/weixin/activity/match?id=' . $aid . '&type=2', '', '', true),
  773. ];
  774. PRedis::set('matchs:activity_' . $aid . ':message:' . $userId, ['result' => $matchData, 'params' => $params], 600);
  775. Wechat::sendTplMsg($openid, $params);
  776. }
  777. // 通知被匹配的用户
  778. $matchOpenId = isset($matchData['openid'])? trim($matchData['openid']) : '';
  779. if ($matchOpenId) {
  780. $bookNo = isset($matchData['book_no']) ? trim($matchData['book_no']) : '无';
  781. $matchName = isset($userData['real_name']) ? $userData['real_name'] : '';
  782. $matchNo = isset($userData['book_no']) ? trim($userData['book_no']) : '无';
  783. $matchWechatCode = isset($userData['wechat_code']) ? trim($userData['wechat_code']) : '无';
  784. $matchSex = isset($userData['sex']) ? intval($userData['sex']) : 0;
  785. $matchSex = $matchSex == 1 ? '男' : '女';
  786. $title = isset($activityInfo['title']) ? trim($activityInfo['title']) : '无';
  787. $matchTime = $matchAt ? date('Y.m.d H:i', $matchAt) : date('Y.m.d H:i', time());
  788. $params = [
  789. 'title' => "活动缘分互选匹配成功,已为您匹配到缘分用户!\n\n活动主题:\t{$title}\n\n您的编号:\t{$bookNo}\n\n对方编号:\t{$matchNo}\n\n匹配时间:\t{$matchTime}\n\n对方微信号:\t{$matchWechatCode}",
  790. 'remark' => "感谢您的使用,点击详情查看报名活动信息",
  791. 'type' => 'match',
  792. 'keywords' => [
  793. 'name' => [
  794. 'value' => $matchName.'(昵称)',
  795. 'color' => '#173177',
  796. ],
  797. 'sex' => [
  798. 'value' => $matchSex,
  799. 'color' => '#173177',
  800. ],
  801. 'tel' => [
  802. 'value' => '隐私信息不展示',
  803. 'color' => '#173177',
  804. ],
  805. ],
  806. 'url' => url('/weixin/activity/match?id=' . $aid . '&type=2', '', '', true),
  807. ];
  808. PRedis::set('matchs:activity_' . $aid . ':message:' . $matchUid, ['result' => $userData, 'params' => $params], 600);
  809. Wechat::sendTplMsg($matchOpenId, $params);
  810. }
  811. }
  812. }
  813. // 更新匹配错误或失败用户数据
  814. PRedis::set('matchs:activity_'.$aid.':result', ['list'=> $matchList, 'results'=> $results, 'success'=> $successArr,'successId'=> $successId,'sorts'=> array_keys($results)], 600);
  815. if ($successId) {
  816. $updateData = ['updated_at' => date('Y-m-d H:i:s'), 'status' => 3];
  817. HeartMatch::where(['aid'=> $aid])->where('uid', 'not in', $successId)->update($updateData);
  818. }
  819. //var_dump($successArr);
  820. return $successArr ? $successArr : 6004;
  821. } catch (\Exception $exception){
  822. return 1015;
  823. }
  824. }
  825. /**
  826. * 推送活动消息
  827. * @param $aid
  828. * @param int $type 类型:默认1活动即将开始,2-活动开始前一天提醒
  829. * @return array|int
  830. */
  831. public static function sendMessage($aid, $type=1){
  832. try {
  833. $field = 'id,title,cover_img as thumb,price,credit,act_nums,addtime,starttime,endtime,description,address,alert_time1,alert_remark1,alert_time2,alert_remark2';
  834. $activityInfo = Activity::getInfo(['id'=> $aid], $field);
  835. if(empty($activityInfo)){
  836. return 5001;
  837. }
  838. // 验证3天之内是否已经发过
  839. if(PRedis::get('activity:pushStatus:'.$aid.'_'.$type)){
  840. return 2115;
  841. }
  842. // 准备开始
  843. $title = isset($activityInfo['title']) ? trim($activityInfo['title']) : '';
  844. $signTime = isset($activityInfo['alert_time'.$type]) ? trim($activityInfo['alert_time'.$type]) : '';
  845. $remark = isset($activityInfo['alert_remark'.$type]) ? trim($activityInfo['alert_remark'.$type]) : '无';
  846. if(empty($signTime)){
  847. return 5021;
  848. }
  849. // 报名的用户列表
  850. $bookList = Books::getPushUserList($aid);
  851. $title = $type == 2? "您报名了明天的“{$title}”记得安排好时间,风雨无阻,准时参加,真诚第一" : "亲,您报名的“{$title}”即将开始,赶快收拾妥当,准时参加!";
  852. $params = [
  853. 'title' => $title,
  854. 'type' => 'activity_start',
  855. 'keywords' => [
  856. 'keyword1' => [
  857. 'value' => '待参加活动',
  858. 'color' => '#173177',
  859. ],
  860. 'keyword2' => [
  861. 'value' => $signTime,
  862. 'color' => '#173177',
  863. ],
  864. 'keyword3' => [
  865. 'value' => $remark,
  866. 'color' => '#173177',
  867. ],
  868. ],
  869. 'url' => url('/weixin/activity/bookdetail?id=' . $aid, '', '', true),
  870. ];
  871. $count = 0;
  872. if(empty($bookList)){
  873. return 5022;
  874. }
  875. // var_dump($bookList);
  876. foreach($bookList as $userData){
  877. $userId = isset($userData['uid'])? intval($userData['uid']) : 0;
  878. $openid = isset($userData['openid'])? trim($userData['openid']) : '';
  879. if($openid){
  880. PRedis::set('activity:pushMessage:' . $aid . '_' . $userId, ['result' => $userData, 'params' => $params], 600);
  881. $result = Wechat::sendTplMsg($openid, $params);
  882. if(is_array($result)){
  883. $count++;
  884. }
  885. }
  886. }
  887. if($count){
  888. PRedis::set('activity:pushStatus:'.$aid.'_'.$type, ['info'=> $activityInfo,'pushList'=> $bookList], 5 * 24 * 3600);
  889. return ['id'=> $aid, 'count'=> $count];
  890. } else {
  891. return 2116;
  892. }
  893. } catch (\Exception $exception){
  894. return 1015;
  895. }
  896. }
  897. }