Activity.php 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025
  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,title,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. 'from_user_id'=> 0,
  355. 'to_user_id'=> isset($memberInfo['id'])? $memberInfo['id'] : 0,
  356. 'title' => "我们已经收到您的报名,稍后会进行审核,请留意后续的通知哦!\n\n姓名:\t{$nickname}(昵称)\n\n支付单号:\t{$outTradeNo}\n\n支付金额:\t邀请码支付",
  357. 'remark' => "转发积姻缘,把活动分享给身边的朋友,一起来脱单哦!",
  358. 'type' => 'book',
  359. 'keywords' => [
  360. 'keyword1' => [
  361. 'value' => $title,
  362. 'color' => '#173177',
  363. ],
  364. 'keyword2' => [
  365. 'value' => $bookAt,
  366. 'color' => '#173177',
  367. ],
  368. 'keyword3' => [
  369. 'value' => '待审核',
  370. 'color' => '#173177',
  371. ],
  372. 'keyword4' => [
  373. 'value' => "活动费用{$money}元,活动守时金{$credit}元。",
  374. 'color' => '#173177',
  375. ],
  376. ],
  377. 'url' => url('/weixin/activity/bookdetail?id='.$activityInfo['id'], '', '', true),
  378. ];
  379. PRedis::set('payments:books:message_' . $outTradeNo, ['bookInfo' => $bookInfo,'activity'=> $activityInfo, 'params' => $params], 600);
  380. Wechat::sendTplMsg($openid, $params);
  381. }
  382. }
  383. /**
  384. * 置顶报名
  385. * @param $userId
  386. * @param $aid
  387. * @return array|int
  388. */
  389. public static function catchTopBook($userId, $aid)
  390. {
  391. // 验证活动是否存在
  392. $field = 'id,starttime,endtime,price,market_price,market_time,service_time,member_discount,addtime,is_top,credit,act_nums';
  393. $activityInfo = Activity::getInfo(['id' => $aid], $field);
  394. if (empty($activityInfo)) {
  395. return 5001;
  396. }
  397. // 验证报名时间
  398. $startTime = isset($activityInfo['starttime']) ? intval($activityInfo['starttime']) : 0;
  399. $endTime = isset($activityInfo['endtime']) ? intval($activityInfo['endtime']) : 0;
  400. $addTime = isset($activityInfo['addtime']) ? intval($activityInfo['addtime']) : 0;
  401. $serviceTime = isset($activityInfo['service_time']) ? intval($activityInfo['service_time']) : 0;
  402. $isTop = isset($activityInfo['is_top']) ? intval($activityInfo['is_top']) : 0;
  403. if($addTime > time()){
  404. return 5000;
  405. }
  406. if ($endTime && time() > $endTime) {
  407. return 5002;
  408. }
  409. // 活动截止前多久不允许报名
  410. $siteInfo = cmf_get_site_info();
  411. $bookTime = isset($siteInfo['book_time']) ? intval($siteInfo['book_time']) : 0;
  412. if ($isTop==0 && $startTime && $bookTime) {
  413. if(time() > $startTime){
  414. return 5026;
  415. }
  416. if(time() + $bookTime*3600 > $startTime){
  417. return lang('book_end', ['time' => $bookTime]);
  418. }
  419. }
  420. // 验证操作频繁
  421. $lockCache = "books:lock:" . $userId . "_" . $aid;
  422. if (PRedis::get($lockCache)) {
  423. return 1013;
  424. }
  425. // 验证已报名
  426. $memberInfo = Member::where(['id' => $userId])->field('sex,is_top,top_expire')->find();
  427. $isTopShow = isset($memberInfo['is_top'])? $memberInfo['is_top'] : 0;
  428. $topExpire = isset($memberInfo['top_expire'])? $memberInfo['top_expire'] : '';
  429. if($isTopShow && $topExpire && date('Y-m-d H:i:s') < $topExpire){
  430. return 2046;
  431. }
  432. $sex = isset($memberInfo['sex'])? $memberInfo['sex'] : 1;
  433. $limitNum = isset($activityInfo['act_nums']) ? intval($activityInfo['act_nums']) : 0;
  434. $bookCount = Member::where(['user_status'=> 1,'is_top'=>1,'sex'=> $sex])
  435. ->where('top_expire','>', date('Y-m-d H:i:s'))
  436. ->count('id');
  437. if ($limitNum && $bookCount >= $limitNum) {
  438. $sexText = $isTop? '' : ($sex==1? '男生' : '女生');
  439. return lang('book_limit',['sex'=> $sexText]);
  440. }
  441. // 验证价格
  442. $price = isset($activityInfo['price']) ? floatval($activityInfo['price']) : 0; // 活动价格
  443. $marketPrice = isset($activityInfo['market_price']) ? floatval($activityInfo['market_price']) : 0; // 早市价
  444. $marketTime = isset($activityInfo['market_time']) ? intval($activityInfo['market_time']) : 0;
  445. $payPrice = $marketTime >= time() && $marketPrice>0? $marketPrice : $price;
  446. $credit = isset($activityInfo['credit']) ? floatval($activityInfo['credit']) : 0;
  447. if (empty($payPrice)) {
  448. return 1012;
  449. }
  450. /*
  451. // 验证VIP折扣价
  452. $memberInfo = Member::where(['id' => $userId])->field('vip_auth,vip_expire')->find();
  453. $vipAuth = isset($memberInfo['vip_auth']) ? intval($memberInfo['vip_auth']) : 0;
  454. $vipExpire = isset($memberInfo['vip_expire']) ? intval($memberInfo['vip_expire']) : 0;
  455. // VIP有效期内
  456. $oldPrice = $payPrice;
  457. $memberDiscount = isset($activityInfo['member_discount']) ? floatval($activityInfo['member_discount']) : 0;
  458. if($vipAuth && $vipExpire > time() && $memberDiscount && $memberDiscount < 1){
  459. $payPrice = floatval($memberDiscount * $payPrice);
  460. }
  461. */
  462. // 报名数据
  463. $orderSn = makeTradeNo('BK', $userId);
  464. $bookData = [
  465. 'book_num' => '',
  466. 'uid' => $userId,
  467. 'aid' => $aid,
  468. 'order_sn' => $orderSn,
  469. 'money' => $payPrice,
  470. 'credit' => $credit,
  471. 'created_at' => date('Y-m-d H:i:s'),
  472. 'remark' => "首页置顶报名:价格{$payPrice}",
  473. 'status' => 1,
  474. ];
  475. // 写入记录
  476. Db::startTrans();
  477. $logData = ['info' => $activityInfo, 'bookData' => $bookData, 'date' => date('Y-m-d H:i:s')];
  478. PRedis::set($lockCache, $logData, 3);
  479. PRedis::set('books:logs:' . $userId . '_' . $aid, $logData, 600);
  480. if (!$bookId = Books::insertGetId($bookData)) {
  481. PRedis::del($lockCache);
  482. Db::rollback();
  483. return 5006;
  484. }
  485. // 用户操作记录
  486. UserLog::saveLog(['user_id' => $userId, 'type' => 4, 'content' => '置顶报名下单:' . $orderSn]);
  487. Db::commit();
  488. PRedis::del($lockCache);
  489. return ['id' => $bookId];
  490. }
  491. /**
  492. * 报名审核
  493. * @param $bookId 报名记录ID
  494. * @param $checkStatus 审核状态:3-审核成功,5-审核失败退款
  495. * @param $failRemark 审核失败原因
  496. * @return int
  497. */
  498. public static function bookConfirm($bookId, $checkStatus, $failRemark='')
  499. {
  500. try {
  501. $bookInfo = Books::getInfo(['id' => $bookId]);
  502. $status = isset($bookInfo['status']) ? intval($bookInfo['status']) : 0;
  503. $userId = isset($bookInfo['uid']) ? intval($bookInfo['uid']) : 0;
  504. $payType = isset($bookInfo['pay_type']) ? intval($bookInfo['pay_type']) : 0;
  505. $inviteCode = isset($bookInfo['invite_code']) ? trim($bookInfo['invite_code']) : '';
  506. $money = isset($bookInfo['money']) ? floatval($bookInfo['money']) : 0;
  507. $aid = isset($bookInfo['aid']) ? intval($bookInfo['aid']) : 0;
  508. if (empty($userId) || empty($aid)) {
  509. return 1012;
  510. }
  511. if ($status != 2) {
  512. return 5016;
  513. }
  514. if(!in_array($checkStatus, [3,5])){
  515. return 2129;
  516. }
  517. // 活动信息
  518. $activityInfo = ActivityModel::where(['id' => $aid])
  519. ->field('id,starttime,act_nums,service_time,title,join_address')
  520. ->find();
  521. // 审核数据更新
  522. $bookNo = '';
  523. $updateData = ['status' => $checkStatus, 'remark' => $checkStatus==3? '审核成功':'审核失败'];
  524. if($checkStatus == 3){
  525. $sex = Member::where(['id' => $userId])->value('sex');
  526. // 置顶报名
  527. if($aid == 16){
  528. $limitNum = isset($activityInfo['act_nums']) ? intval($activityInfo['act_nums']) : 0;
  529. $bookCount = Member::where(['user_status'=> 1,'is_top'=>1,'sex'=> $sex])
  530. ->where('top_expire','>', date('Y-m-d H:i:s'))
  531. ->count('id');
  532. if ($limitNum && $bookCount >= $limitNum) {
  533. return lang('book_limit',['sex'=> ($sex==1?'男生':'女生')]);
  534. }
  535. }
  536. $prefix = $sex == 1 ? 'G' : ($sex == 2 ? 'M' : '');
  537. $bookNo = Books::makeBookNo($aid, $prefix, $sex);
  538. $updateData['book_num'] = $bookNo;
  539. $updateData['book_num_val'] = str_replace(['M','G'],'', $bookNo);
  540. }
  541. $res = Books::saveData(['id' => $bookId], $updateData);
  542. if ($res) {
  543. // 更新加入单身推荐
  544. if($aid == 15 && $checkStatus == 3){
  545. Member::saveData(['id'=> $userId],['is_tuijian'=> 1,'tuijian_time'=> date('Y-m-d H:i:s')]);
  546. }
  547. // 置顶推荐更新
  548. $servceTime = isset($activityInfo['service_time'])? $activityInfo['service_time'] : 0;
  549. if($servceTime && $aid == 16 && $checkStatus == 3){
  550. $expire = $servceTime * 3600 * 24 + time();
  551. Member::saveData(['id'=> $userId],['is_top'=> 1,'top_expire'=> date('Y-m-d H:i:s', $expire)]);
  552. }
  553. // 邀请码报名审核不通过
  554. $money = ($payType == 4 || $inviteCode)? '邀请码支付': $money;
  555. if(($payType == 4 || $inviteCode) && $checkStatus==5){
  556. Invitation::where(['user_id'=> $userId,'code'=> $inviteCode])->update(['status'=>2,'update_time'=>time()]);
  557. }
  558. $memberInfo = Member::getInfo(['id' => $userId]);
  559. $openid = isset($memberInfo['openid']) ? $memberInfo['openid'] : '';
  560. $nickname = isset($memberInfo['user_nickname']) ? $memberInfo['user_nickname'] : '';
  561. // 发送消息
  562. if ($openid) {
  563. $title = isset($activityInfo['title']) ? trim($activityInfo['title']) : '无';
  564. $address = isset($activityInfo['join_address']) ? trim($activityInfo['join_address']) : '无';
  565. $startTime = isset($activityInfo['starttime']) ? intval($activityInfo['starttime']) : 0;
  566. $startTime = $startTime ? date('Y.m.d H:i', $startTime) : '详情查看';
  567. $startTime = $aid==15? date('Y.m.d H:i') : $startTime;
  568. $orderSn = isset($bookInfo['order_sn']) ? trim($bookInfo['order_sn']) : '';
  569. if($checkStatus==3){
  570. $url = $aid==15? url('/weixin/activity/book?id=' . $aid, '', '', true) : url('/weixin/activity/bookdetail?id=' . $aid, '', '', true);
  571. $params = [
  572. 'from_user_id'=> 0,
  573. 'to_user_id'=> $userId,
  574. 'title' => "恭喜!您报名的活动已经通过审核!请提前安排好时间,准时参加!风雨无阻,真诚第一\n\n姓名:\t{$nickname}(昵称)\n\n支付金额:\t{$money}\n\n报名编号:\t{$bookNo}\n\n当前进度:\t待参加",
  575. 'remark' => $address? $address : "亲,记得安排好时间哦!",
  576. // 'remark' => "亲,记得安排好时间哦!",
  577. 'type' => 'book_confirm',
  578. 'keywords' => [
  579. 'keyword1' => [
  580. 'value' => $title,
  581. 'color' => '#173177',
  582. ],
  583. 'keyword2' => [
  584. 'value' => $startTime,
  585. 'color' => '#173177',
  586. ],
  587. ],
  588. 'url' => $url,
  589. ];
  590. }else{
  591. $params = [
  592. 'from_user_id'=> 0,
  593. 'to_user_id'=> $userId,
  594. 'title' => ($failRemark? $failRemark : '报名审核失败')."\n\n姓名:\t{$nickname}(昵称)\n\n支付金额:\t{$money}",
  595. 'remark' => "转发积姻缘,把活动分享给身边的朋友,一起来脱单哦!",
  596. 'type' => 'book',
  597. 'keywords' => [
  598. 'keyword1' => [
  599. 'value' => $title,
  600. 'color' => '#173177',
  601. ],
  602. 'keyword2' => [
  603. 'value' => $startTime,
  604. 'color' => '#173177',
  605. ],
  606. 'keyword3' => [
  607. 'value' => '审核失败',
  608. 'color' => '#173177',
  609. ],
  610. 'keyword4' => [
  611. 'value' => "...",
  612. 'color' => '#173177',
  613. ],
  614. ],
  615. 'url' => url('/weixin/activity/book?id=' . $aid, '', '', true),
  616. ];
  617. }
  618. PRedis::set('payments:books:confirmMessage_' . $orderSn, ['book' => $bookInfo, 'params' => $params], 600);
  619. Wechat::sendTplMsg($openid, $params);
  620. }
  621. return ['id' => $bookId];
  622. } else {
  623. return 5015;
  624. }
  625. } catch (\Exception $exception){
  626. return 1015;
  627. }
  628. }
  629. /**
  630. * 互选匹配
  631. * @param $aid 活动ID
  632. * @return int
  633. */
  634. public static function heartMatch($aid)
  635. {
  636. try {
  637. // 验证活动是否有效
  638. $activityInfo = ActivityModel::where(['id' => $aid, 'status' => 1])
  639. ->field('id,starttime,title,address,status,is_match')
  640. ->find();
  641. if (empty($activityInfo)) {
  642. return 5001;
  643. }
  644. // 验证状态和匹配状态
  645. $isMatch = isset($activityInfo['is_match']) ? intval($activityInfo['is_match']) : 0;
  646. if ($isMatch == 1) {
  647. return 6002;
  648. }
  649. // 活动开始时间
  650. $startTime = isset($activityInfo['starttime']) ? intval($activityInfo['starttime']) : 0;
  651. if (time() < $startTime) {
  652. return 5003;
  653. }
  654. // 互选模式匹配
  655. $siteInfo = $siteInfo = cmf_get_site_info();
  656. $contactType = isset($siteInfo['contact_type']) ? $siteInfo['contact_type'] : 1;
  657. if($contactType == 3){
  658. // 更新匹配状态
  659. ActivityModel::where(['id'=> $aid])->update(['is_match'=> 1]);
  660. // 获取用户列表
  661. // 处理互选
  662. return ['result'=> $aid];
  663. }
  664. // 获取匹配互选列表
  665. $matchList = Books::getMatchUserList($aid);
  666. if (empty($matchList)) {
  667. return 6003;
  668. }
  669. // 更新匹配状态
  670. ActivityModel::where(['id'=> $aid])->update(['is_match'=> 1]);
  671. // 用户匹配列表处理
  672. $results = [];
  673. PRedis::set('matchs:activity_' . $aid.':users', $matchList, 600);
  674. foreach ($matchList as $k => $userData) {
  675. $userId = isset($userData['uid']) ? intval($userData['uid']) : 0;
  676. $heartUids = isset($userData['heart_uids'])? $userData['heart_uids'] : '';
  677. $heartUids = $heartUids? explode(',', $heartUids) : [];
  678. if(empty($heartUids)){
  679. continue;
  680. }
  681. foreach ($heartUids as $kk => $uid) {
  682. $matchData = isset($matchList[$uid]) ? $matchList[$uid] : [];
  683. $uids = isset($matchData['heart_uids']) ? $matchData['heart_uids'] : '';
  684. $uids = $uids? explode(',', $uids) : [];
  685. $index = array_search($userId, $uids);
  686. if ($index !== false && $index >= 0) {
  687. $dvalue = abs($kk - $index)+1;
  688. if(($kk + 1)>= ($index + 1)){
  689. $key = ($kk + 1) . '_' . $dvalue . '_' . ($index + 1);
  690. }else{
  691. $key = ($index + 1) . '_' . $dvalue . '_' . ($kk + 1);
  692. }
  693. $results[$key] = isset($results[$key]) ? $results[$key] : [];
  694. $results[$key][] = [$userData, $matchData];
  695. }
  696. }
  697. }
  698. ksort($results, 1);
  699. $successArr = $successId = [];
  700. foreach ($results as $result) {
  701. foreach ($result as $val) {
  702. // 匹配用户数据
  703. $userData = isset($val[0]) ? $val[0] : 0;
  704. $matchData = isset($val[1]) ? $val[1] : 0;
  705. $mid = isset($userData['id'])? intval($userData['id']) : 0;
  706. $userId = isset($userData['uid'])? intval($userData['uid']) : 0;
  707. $matchId = isset($matchData['id']) ? intval($matchData['id']) : 0;
  708. $matchUid = isset($matchData['uid']) ? intval($matchData['uid']) : 0;
  709. // 验证用户是否已经被匹配,若是则不需要再次匹配
  710. if (!$userId || !$matchUid || PRedis::get('matchs:activity_' . $aid . ':lock:u_' . $userId) || PRedis::get('matchs:activity_' . $aid . ':lock:u_' . $matchUid)) {
  711. PRedis::set('matchs:activity_' . $aid . ':error:u_' . $userId.'_'.$matchUid, ['data' => $userData, 'match'=> $matchData, 'error' => '当前匹配用户已被匹配过'], 600);
  712. continue;
  713. }
  714. // 更新匹配数据
  715. Db::startTrans();
  716. $matchAt = time();
  717. $updateData = [
  718. 'match_uid' => $matchUid,
  719. 'match_at' => date('Y-m-d H:i:s', $matchAt),
  720. 'status' => 2,
  721. ];
  722. PRedis::set('matchs:activity_' . $aid . ':update:u_' . $userId, ['data' => $updateData, 'match' => $matchData], 600);
  723. if (!HeartMatch::where(['id' => $mid])->update($updateData)) {
  724. Db::rollback();
  725. continue;
  726. }
  727. $updateData = [
  728. 'match_uid' => $userId,
  729. 'match_at' => date('Y-m-d H:i:s', $matchAt),
  730. 'status' => 2,
  731. ];
  732. PRedis::set('matchs:activity_' . $aid . ':update:u_' . $matchUid, ['data' => $updateData, 'match' => $userData], 600);
  733. if (!HeartMatch::where(['id' => $matchId])->update($updateData)) {
  734. Db::rollback();
  735. continue;
  736. }
  737. // 提交
  738. Db::commit();
  739. $successArr[] = $userId.' 匹配 '.$matchUid;
  740. //echo $userId.' 匹配 '.$matchUid."\n";
  741. $successId[] = $userId;
  742. $successId[] = $matchUid;
  743. // 匹配加锁
  744. PRedis::set('matchs:activity_' . $aid . ':lock:u_' . $userId, $matchData, 24 * 3600);
  745. PRedis::set('matchs:activity_' . $aid . ':lock:u_' . $matchUid, $userData, 24 * 3600);
  746. // 推送匹配成功消息
  747. $openid = isset($userData['openid']) ? trim($userData['openid']) : '';
  748. if ($openid) {
  749. $bookNo = isset($userData['book_no']) ? trim($userData['book_no']) : '无';
  750. $matchName = isset($matchData['user_nickname']) ? $matchData['user_nickname'] : '';
  751. $matchNo = isset($matchData['book_no']) ? trim($matchData['book_no']) : '无';
  752. $matchWechatCode = isset($matchData['wechat_code']) ? trim($matchData['wechat_code']) : '无';
  753. $matchSex = isset($matchData['sex']) ? intval($matchData['sex']) : 0;
  754. $matchSex = $matchSex == 1 ? '男' : '女';
  755. $title = isset($activityInfo['title']) ? trim($activityInfo['title']) : '无';
  756. $matchTime = $matchAt ? date('Y.m.d H:i', $matchAt) : date('Y.m.d H:i');
  757. $params = [
  758. 'from_user_id'=> 0,
  759. 'to_user_id'=> $userId,
  760. 'title' => "恭喜!活动缘分互选匹配成功,已为您匹配到缘分用户!\n\n活动主题:\t{$title}\n\n您的编号:\t{$bookNo}\n\n对方编号:\t{$matchNo}\n\n匹配时间:\t{$matchTime}\n\n对方微信号:\t{$matchWechatCode}",
  761. 'remark' => "感谢您的使用,点击详情查看互选信息",
  762. 'type' => 'match',
  763. 'keywords' => [
  764. 'name' => [
  765. 'value' => $matchName.'(昵称)',
  766. 'color' => '#173177',
  767. ],
  768. 'sex' => [
  769. 'value' => $matchSex,
  770. 'color' => '#173177',
  771. ],
  772. 'tel' => [
  773. 'value' => '隐私信息不显示',
  774. 'color' => '#173177',
  775. ],
  776. ],
  777. 'url' => url('/weixin/activity/match?id=' . $aid . '&type=2', '', '', true),
  778. ];
  779. PRedis::set('matchs:activity_' . $aid . ':message:' . $userId, ['result' => $matchData, 'params' => $params], 600);
  780. Wechat::sendTplMsg($openid, $params);
  781. }
  782. // 通知被匹配的用户
  783. $matchOpenId = isset($matchData['openid'])? trim($matchData['openid']) : '';
  784. if ($matchOpenId) {
  785. $bookNo = isset($matchData['book_no']) ? trim($matchData['book_no']) : '无';
  786. $matchName = isset($userData['real_name']) ? $userData['real_name'] : '';
  787. $matchNo = isset($userData['book_no']) ? trim($userData['book_no']) : '无';
  788. $matchWechatCode = isset($userData['wechat_code']) ? trim($userData['wechat_code']) : '无';
  789. $matchSex = isset($userData['sex']) ? intval($userData['sex']) : 0;
  790. $matchSex = $matchSex == 1 ? '男' : '女';
  791. $title = isset($activityInfo['title']) ? trim($activityInfo['title']) : '无';
  792. $matchTime = $matchAt ? date('Y.m.d H:i', $matchAt) : date('Y.m.d H:i', time());
  793. $params = [
  794. 'from_user_id'=> 0,
  795. 'to_user_id'=> $matchUid,
  796. 'title' => "活动缘分互选匹配成功,已为您匹配到缘分用户!\n\n活动主题:\t{$title}\n\n您的编号:\t{$bookNo}\n\n对方编号:\t{$matchNo}\n\n匹配时间:\t{$matchTime}\n\n对方微信号:\t{$matchWechatCode}",
  797. 'remark' => "感谢您的使用,点击详情查看报名活动信息",
  798. 'type' => 'match',
  799. 'keywords' => [
  800. 'name' => [
  801. 'value' => $matchName.'(昵称)',
  802. 'color' => '#173177',
  803. ],
  804. 'sex' => [
  805. 'value' => $matchSex,
  806. 'color' => '#173177',
  807. ],
  808. 'tel' => [
  809. 'value' => '隐私信息不展示',
  810. 'color' => '#173177',
  811. ],
  812. ],
  813. 'url' => url('/weixin/activity/match?id=' . $aid . '&type=2', '', '', true),
  814. ];
  815. PRedis::set('matchs:activity_' . $aid . ':message:' . $matchUid, ['result' => $userData, 'params' => $params], 600);
  816. Wechat::sendTplMsg($matchOpenId, $params);
  817. }
  818. }
  819. }
  820. // 更新匹配错误或失败用户数据
  821. PRedis::set('matchs:activity_'.$aid.':result', ['list'=> $matchList, 'results'=> $results, 'success'=> $successArr,'successId'=> $successId,'sorts'=> array_keys($results)], 600);
  822. if ($successId) {
  823. $updateData = ['updated_at' => date('Y-m-d H:i:s'), 'status' => 3];
  824. HeartMatch::where(['aid'=> $aid])->where('uid', 'not in', $successId)->update($updateData);
  825. }
  826. //var_dump($successArr);
  827. return $successArr ? $successArr : 6004;
  828. } catch (\Exception $exception){
  829. return 1015;
  830. }
  831. }
  832. /**
  833. * 推送活动消息
  834. * @param $aid
  835. * @param int $type 类型:默认1活动即将开始,2-活动开始前一天提醒
  836. * @return array|int
  837. */
  838. public static function sendMessage($aid, $type=1){
  839. try {
  840. $field = 'id,title,cover_img as thumb,price,credit,act_nums,addtime,starttime,endtime,description,address,alert_time1,alert_remark1,alert_time2,alert_remark2';
  841. $activityInfo = Activity::getInfo(['id'=> $aid], $field);
  842. if(empty($activityInfo)){
  843. return 5001;
  844. }
  845. // 验证3天之内是否已经发过
  846. if(PRedis::get('activity:pushStatus:'.$aid.'_'.$type)){
  847. return 2115;
  848. }
  849. // 准备开始
  850. $title = isset($activityInfo['title']) ? trim($activityInfo['title']) : '';
  851. $signTime = isset($activityInfo['alert_time'.$type]) ? trim($activityInfo['alert_time'.$type]) : '';
  852. $remark = isset($activityInfo['alert_remark'.$type]) ? trim($activityInfo['alert_remark'.$type]) : '无';
  853. if(empty($signTime)){
  854. return 5021;
  855. }
  856. // 报名的用户列表
  857. $bookList = Books::getPushUserList($aid);
  858. $title = $type == 2? "您报名了明天的“{$title}”记得安排好时间,风雨无阻,准时参加,真诚第一" : "亲,您报名的“{$title}”即将开始,赶快收拾妥当,准时参加!";
  859. $params = [
  860. 'title' => $title,
  861. 'type' => 'activity_start',
  862. 'keywords' => [
  863. 'keyword1' => [
  864. 'value' => '待参加活动',
  865. 'color' => '#173177',
  866. ],
  867. 'keyword2' => [
  868. 'value' => $signTime,
  869. 'color' => '#173177',
  870. ],
  871. 'keyword3' => [
  872. 'value' => $remark,
  873. 'color' => '#173177',
  874. ],
  875. ],
  876. 'url' => url('/weixin/activity/bookdetail?id=' . $aid, '', '', true),
  877. ];
  878. $count = 0;
  879. if(empty($bookList)){
  880. return 5022;
  881. }
  882. // var_dump($bookList);
  883. foreach($bookList as $userData){
  884. $userId = isset($userData['uid'])? intval($userData['uid']) : 0;
  885. $openid = isset($userData['openid'])? trim($userData['openid']) : '';
  886. if($openid){
  887. $params['form_user_id'] = 0;
  888. $params['to_user_id'] = $userId;
  889. PRedis::set('activity:pushMessage:' . $aid . '_' . $userId, ['result' => $userData, 'params' => $params], 600);
  890. $result = Wechat::sendTplMsg($openid, $params);
  891. if(is_array($result)){
  892. $count++;
  893. }
  894. }
  895. }
  896. if($count){
  897. PRedis::set('activity:pushStatus:'.$aid.'_'.$type, ['info'=> $activityInfo,'pushList'=> $bookList], 5 * 24 * 3600);
  898. return ['id'=> $aid, 'count'=> $count];
  899. } else {
  900. return 2116;
  901. }
  902. } catch (\Exception $exception){
  903. return 1015;
  904. }
  905. }
  906. }