Activity.php 41 KB

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