BoxRecordService.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696
  1. <?php
  2. namespace app\common\service;
  3. use app\admin\model\dao\BoxMidHandle;
  4. use app\common\model\BoxHandleAllModel;
  5. use app\common\model\BoxHandleModel;
  6. use app\common\model\BoxMidHandleModel;
  7. use app\common\model\BoxModel;
  8. use app\common\model\BoxRecordModel;
  9. use app\common\model\MoneyLogModel;
  10. use app\common\model\ScoreLogModel;
  11. use app\common\model\ShopGoodsModel;
  12. use app\common\model\UserModel;
  13. use app\common\model\UserUnmoneyModel;
  14. use think\facade\Db;
  15. use utils\RedisCache;
  16. /**
  17. * 盒子预约记录服务 by wes
  18. * Class BoxRecordService
  19. * @package app\common\service
  20. */
  21. class BoxRecordService
  22. {
  23. protected static $instance = null;
  24. protected $model = null;
  25. public function __construct()
  26. {
  27. $this->model = new BoxRecordModel();
  28. }
  29. /**
  30. * 静态化入口
  31. * @return static|null
  32. */
  33. public static function make()
  34. {
  35. if (!self::$instance) {
  36. self::$instance = new static();
  37. }
  38. return self::$instance;
  39. }
  40. /**
  41. * 获取待处理福袋
  42. * @param $params
  43. * @param int $pageSize
  44. */
  45. public function getCatchList($params, $pageSize = 20)
  46. {
  47. $where = ['r.status' => 1];
  48. $uid = isset($params['uid']) ? $params['ud'] : 0;
  49. if ($uid > 0) {
  50. $where['r.uid'] = $uid;
  51. }
  52. $mobile = isset($params['mobile']) ? trim($params['mobile']) : '';
  53. if (!empty($mobile)) {
  54. $where['r.mobile'] = $mobile;
  55. }
  56. $boxId = isset($params['box_id']) ? trim($params['box_id']) : '';
  57. if (!empty($boxId)) {
  58. $where['r.box_id'] = $boxId;
  59. }
  60. $qiCount = isset($params['qi_count']) ? $params['qi_count'] : 0;
  61. if ($qiCount > 0) {
  62. $where['r.qi_count'] = $qiCount;
  63. }
  64. $lunCount = isset($params['lun_count']) ? $params['lun_count'] : 0;
  65. if ($lunCount > 0) {
  66. $where['r.lun_count'] = $lunCount;
  67. }
  68. $isMatch = isset($params['is_set_match']) ? $params['is_set_match'] : 0;
  69. if ($isMatch > 0) {
  70. $where['r.is_set_match'] = $isMatch;
  71. }
  72. $list = $this->model->alias('r')
  73. ->where($where)
  74. ->where(function ($query) use ($params) {
  75. $box10 = isset($params['box10']) ? $params['box10'] : 0;
  76. $box20 = isset($params['box20']) ? $params['box20'] : 0;
  77. $box30 = isset($params['box30']) ? $params['box30'] : 0;
  78. $box40 = isset($params['box40']) ? $params['box40'] : 0;
  79. if ($box10 > 0) {
  80. $query->where('r.box10', '>=', $box10);
  81. }
  82. if ($box20 > 0) {
  83. $query->where('r.box20', '>=', $box20);
  84. }
  85. if ($box30 > 0) {
  86. $query->where('r.box30', '>=', $box30);
  87. }
  88. if ($box40 > 0) {
  89. $query->where('r.box40', '>=', $box40);
  90. }
  91. })
  92. ->where('r.create_time', '>=', date('Y-m-d'))
  93. ->leftJoin('user u', 'u.id = r.uid')
  94. ->field(Db::raw('r.*,u.mobile,u.total_null_box,sum(r.num) as num,u.total_free,u.total_appoint_count,u.total_income,u.yesterday_money,u.box10 as ubox10,u.box20 as ubox20,u.box30 as ubox30,u.box40 as ubox40,u.path'))
  95. ->group('group_key')
  96. ->order('r.create_time desc')
  97. ->order('r.qi_count desc')
  98. ->order('r.lun_count desc')
  99. ->order('r.num desc')
  100. ->paginate($pageSize)
  101. ->each(function($item, $k){
  102. $path = isset($item['path'])? $item['path'] : '';
  103. $path = $path? explode(',', $path):[];
  104. $path[] = $item['uid'];
  105. $item['last_team_profit'] = UserUnmoneyModel::whereIn('uid',$path)->whereDay('create_time','yesterday')->sum('money');
  106. $item['today_team_profit'] = UserUnmoneyModel::whereIn('uid',$path)->whereDay('create_time','today')->sum('money');
  107. });
  108. $list = $list ? $list->toArray() : [];
  109. return $list;
  110. }
  111. /**
  112. * 统计小于某预约数量的渔业次数
  113. * @param $uid 用户ID
  114. * @param $boxId 福袋ID
  115. * @param int $num 数量
  116. * @return array|mixed|null
  117. * @throws \think\db\exception\DbException
  118. */
  119. public function getCountByUser($uid, $boxId, $num = 5)
  120. {
  121. $cacheKey = "caches:boxRecord:count:{$uid}_{$boxId}_{$num}";
  122. $data = RedisCache::get($cacheKey);
  123. if ($data) {
  124. return $data;
  125. }
  126. $count = $this->model->where(['uid' => $uid, 'box_id' => $boxId, 'status' => 1])
  127. ->where('num', '<', $num)
  128. ->count('id');
  129. if ($count) {
  130. RedisCache::set($cacheKey, $count, rand(3, 5));
  131. }
  132. return $data;
  133. }
  134. /**
  135. * 预约福袋处理
  136. * @param $uid
  137. * @param $params
  138. * @param $user_info
  139. * @throws \think\Exception
  140. * @throws \think\db\exception\DataNotFoundException
  141. * @throws \think\db\exception\DbException
  142. * @throws \think\db\exception\ModelNotFoundException
  143. */
  144. public function applyBox($uid, $params)
  145. {
  146. $boxCount = isset($params['buy_count']) ? intval($params['buy_count']) : 0;
  147. $boxId = isset($params['box_id']) ? intval($params['box_id']) : 0;
  148. $payType = isset($params['pay_type']) ? intval($params['pay_type']) : 0;
  149. if ($boxCount <= 0) {
  150. sr_throw('请选择预约福袋类型');
  151. }
  152. if ($boxId <= 0 || $payType <= 0) {
  153. sr_throw('预约参数错误');
  154. }
  155. if (!in_array($payType, [1, 2])) {
  156. sr_throw('支付类型错误');
  157. }
  158. // 验证福袋功能是否开启
  159. $boxIsApply = SystemConfigService::make()->getConfigByName('fudai_is_apply', 1, 'fudai');
  160. if ($boxIsApply != 1) {
  161. sr_throw('福袋预约功能暂未开放');
  162. }
  163. // 福袋信息和验证
  164. $boxInfo = BoxModel::where('id', $boxId)->find();
  165. $openStatus = isset($boxInfo['open_status']) ? $boxInfo['open_status'] : 0;
  166. if (!$boxInfo) {
  167. sr_throw('福袋参数错误');
  168. }
  169. if ($openStatus != 1) {
  170. sr_throw('该期福袋预约未开启');
  171. }
  172. if ($boxCount > 20) {
  173. sr_throw('每次最多可预约20个');
  174. }
  175. // 如果预约数量小于5
  176. if ($boxCount < 5 && $this->getCountByUser($uid, $boxId, 5) >= 4) {
  177. sr_throw('每一场数量小于5最多只能预约4次');
  178. }
  179. $lun = 1;
  180. $time = time();
  181. $canBuy = false;
  182. $times = explode('|', $boxInfo['time_set']);
  183. $curTimeDay = sr_getcurtime(time(), 'Y-m-d');
  184. $openTime = '';
  185. foreach ($times as $key => $val) {
  186. $timesarr = explode('-', $val);
  187. $beginTime = strtotime($curTimeDay . ' ' . $timesarr[0]);
  188. $endTime = strtotime($curTimeDay . ' ' . $timesarr[1]);
  189. if ($time > $beginTime && $time < $endTime) {
  190. $canBuy = true;
  191. $lun = $key + 1;
  192. $openTime = date('Y-m-d H:i:s', $endTime + 30 * 60);
  193. }
  194. }
  195. // 预约数量限制
  196. $applyCount = $this->model->where(['uid' => $uid,'box_id'=>$boxId,'lun_count'=>$lun, 'status' => 1])->sum('num');
  197. if ($applyCount + $boxCount> 100) {
  198. sr_throw("每一场最多可预约100个,您已预约".($applyCount)."个");
  199. }
  200. if (!$canBuy) {
  201. sr_throw('no_open');
  202. }
  203. // 总金额
  204. $totalPay = $boxCount * env('boxsetting.one_box_price', 288);
  205. $userInfo = UserModel::where(['id' => $uid])->field('id,score,money,has_fd')->find();
  206. $userMoney = isset($userInfo['money']) ? floatval($userInfo['money']) : 0;
  207. $userScore = isset($userInfo['score']) ? floatval($userInfo['score']) : 0;
  208. $hasFd = isset($userInfo['has_fd']) ? intval($userInfo['has_fd']) : 0;
  209. // 积分支付
  210. if ($payType == 1) {
  211. if ($userScore < $totalPay) {
  212. sr_throw('积分不足,预约失败');
  213. }
  214. // 扣除用户积分
  215. if (!UserModel::where('id', $uid)->dec('score', $totalPay)->update()) {
  216. sr_throw('扣除账户积分失败');
  217. }
  218. // 积分流水
  219. $data = [
  220. 'uid' => $uid,
  221. 'type' => 1,
  222. 'score' => $totalPay,
  223. 'create_at' => sr_getcurtime(time()),
  224. 'state' => 2,
  225. 'before_score' => $userScore,
  226. 'after_score' => max(0, $userScore - $totalPay),
  227. 'from_id' => 0,
  228. 'uid2' => 0,
  229. 'remark' => '预约福袋:' . $boxCount . '个'
  230. ];
  231. if (!ScoreLogModel::insertGetId($data)) {
  232. sr_throw('扣除积分处理失败');
  233. }
  234. }
  235. // 余额支付
  236. if ($payType == 2) {
  237. if ($userMoney < $totalPay) {
  238. sr_throw('余额不足,预约失败');
  239. }
  240. // 扣除用户余额
  241. if (!UserModel::where('id', $uid)->dec('money', $totalPay)->update()) {
  242. sr_throw('扣除账户余额失败');
  243. }
  244. // 余额流水
  245. $data = [
  246. 'uid' => $uid,
  247. 'type' => 1,
  248. 'money' => $totalPay,
  249. 'create_at' => sr_getcurtime(time()),
  250. 'state' => 2,
  251. 'before_money' => $userMoney,
  252. 'after_money' => max(0, $userMoney - $totalPay),
  253. 'from_id' => 0,
  254. 'uid2' => 0,
  255. 'remark' => '预约福袋:' . $boxCount . '个'
  256. ];
  257. if (!MoneyLogModel::insertGetId($data)) {
  258. sr_throw('扣除余额处理失败');
  259. }
  260. }
  261. // 预约记录
  262. $recordData = [
  263. 'box_id' => $params['box_id'],
  264. 'num' => $params['buy_count'],
  265. 'lun_count' => $lun,
  266. 'uid' => $uid,
  267. 'create_time' => sr_getcurtime($time),
  268. 'open_time' => $openTime ? $openTime : sr_getcurtime(time() + 30 * 60),
  269. 'box_img' => $boxInfo['box_img'],
  270. 'group_key' => $boxId . '_' . $boxInfo['qi_count'] . '_' . $lun . '_' . $uid,
  271. 'box_title' => $boxInfo['box_title'],
  272. 'qi_count' => $boxInfo['qi_count'],
  273. 'pay_type' => $params['pay_type']
  274. ];
  275. if (!$this->model->insertGetId($recordData)) {
  276. sr_throw('预约处理失败');
  277. }
  278. // 统计用户预约次数
  279. if (!UserModel::where('id', $uid)->inc('total_appoint_count', $params['buy_count'])->update()) {
  280. sr_throw('用户预约数据处理失败');
  281. }
  282. // 清除拆袋缓存
  283. RedisCache::clear("caches:box:open:user_" . $uid);
  284. return true;
  285. }
  286. /**
  287. * 设置福袋匹配数据
  288. * @param $ids
  289. * @param $data
  290. * @return bool
  291. * @throws \think\Exception
  292. * @throws \think\db\exception\DataNotFoundException
  293. * @throws \think\db\exception\DbException
  294. * @throws \think\db\exception\ModelNotFoundException
  295. */
  296. public function setBoxMatch($ids, $data)
  297. {
  298. $ids = $ids ? explode(',', $ids) : [];
  299. $box10 = isset($data['box10']) ? intval($data['box10']) : 0;
  300. $box20 = isset($data['box20']) ? intval($data['box20']) : 0;
  301. $box30 = isset($data['box30']) ? intval($data['box30']) : 0;
  302. $box40 = isset($data['box40']) ? intval($data['box40']) : 0;
  303. $boxList = BoxRecordModel::whereIn('id', $ids)->where('status', 1)
  304. ->field('uid,box_id,group_key,box10,box20,box30,box40,is_set_match')
  305. ->select();
  306. $boxList = $boxList ? $boxList->toArray() : [];
  307. if (empty($boxList)) {
  308. return '请选择有效的用户进行操作';
  309. }
  310. if (count($ids) != count($boxList)) {
  311. return '存在不可设置的用户';
  312. }
  313. $matchCount = intval($box10 + $box20 + $box30 + $box40);
  314. foreach ($boxList as $item) {
  315. $uid = isset($item['uid']) ? $item['uid'] : 0;
  316. $groupKey = isset($item['group_key']) ? $item['group_key'] : '';
  317. $applyCount = $this->model->where(['group_key'=>$groupKey,'status'=>1])->sum('num');
  318. if ($matchCount > $applyCount) {
  319. return "用户[{$uid}]不能设置超过最大预约量{$applyCount}个";
  320. }
  321. }
  322. $updateData = ['box10' => $box10, 'box20' => $box20, 'box30' => $box30, 'box40' => $box40, 'is_set_match' => 1, 'update_time' => date('Y-m-d H:i:s')];
  323. $hasXy = isset($data['hs_xy'])? $data['hs_xy'] : '';
  324. if ($hasXy == 'on') {
  325. $updateData['hs_xy'] = 1;
  326. }else {
  327. $updateData['hs_xy'] = 0;
  328. }
  329. Db::startTrans();
  330. if (!$this->model->whereIn('id', $ids)->update($updateData)) {
  331. Db::rollback();
  332. return '设置匹配数据失败';
  333. }
  334. Db::commit();
  335. return true;
  336. }
  337. /**
  338. * 开奖处理
  339. * @param $ids
  340. * @param $post
  341. * @return string
  342. * @throws \think\db\exception\DataNotFoundException
  343. * @throws \think\db\exception\DbException
  344. * @throws \think\db\exception\ModelNotFoundException
  345. */
  346. public function openBox($ids, $post)
  347. {
  348. set_time_limit(0);
  349. if (empty($ids)) {
  350. return '请先选择要开出的用户';
  351. }
  352. $cacheTime = 7200;
  353. $key = md5(json_encode($ids));
  354. $date = date('Y-m-d H:i:s');
  355. $cacheKey = "caches:boxOpen:" . date('mdHi') . "_{$key}:";
  356. if (RedisCache::get($cacheKey . 'lock')) {
  357. return '正在开奖中,请不要关闭页面或重复提交,若失败请20秒后再试';
  358. }
  359. RedisCache::setnx($cacheKey . 'lock', 1, rand(10, 20));
  360. if ($this->model->whereIn('id', $ids)->where('is_set_match', 2)->value('id')) {
  361. RedisCache::set($cacheKey . 'error', ['ids' => $ids, 'date' => $date, 'msg' => '存在未设置匹配数据的记录'], $cacheTime);
  362. return '存在未设置匹配数据的记录';
  363. }
  364. // 盒子验证
  365. $boxInfo = BoxService::make()->getCacheInfo();
  366. if (empty($boxInfo)) {
  367. RedisCache::set($cacheKey . 'error', ['ids' => $ids, 'date' => $date, 'msg' => '进行中的福袋数据不存在或预约中'], $cacheTime);
  368. return '当前福袋场次未开或预约中';
  369. }
  370. // 处理的预约记录
  371. $recordList = $this->model->alias('a')->whereIn('a.id', $ids)
  372. ->leftJoin('user u', 'u.id=a.uid')
  373. ->where('a.status', 1)
  374. ->field('a.id,a.box_id,a.num,a.uid,a.group_key,a.hs_xy,a.box10,a.box20,a.box30,a.box40,a.pay_type,a.open_time,u.has_fd,u.path,u.pid')
  375. ->select();
  376. $recordList = $recordList? $recordList->toarray() : [];
  377. if (empty($recordList)) {
  378. RedisCache::set($cacheKey . 'error', ['ids' => $ids, 'date' => $date, 'msg' => '暂无有效的预约记录'], $cacheTime);
  379. return '暂无有效的预约记录';
  380. }
  381. if (count($ids) != count($recordList)) {
  382. RedisCache::set($cacheKey . 'error', ['ids' => $ids,'count'=>count($recordList), 'date' => $date, 'msg' => '存在已处理记录,请刷新后重试'], $cacheTime);
  383. return '存在已处理记录,请刷新后重试~';
  384. }
  385. // 盒子商品
  386. $appointGoods = ShopGoodsService::make()->getBoxMatchGoods(20, 0,20);
  387. $boxGoods[10] = ShopGoodsService::make()->getBoxMatchGoods(10, 1,20);
  388. $boxGoods[20] = ShopGoodsService::make()->getBoxMatchGoods(20, 1,20);
  389. $boxGoods[30] = ShopGoodsService::make()->getBoxMatchGoods(30, 1,20);
  390. $boxGoods[40] = ShopGoodsService::make()->getBoxMatchGoods(40, 1,20);
  391. if (empty($appointGoods) && empty($boxGoods[10]) && empty($boxGoods[20]) && empty($boxGoods[30]) && empty($boxGoods[40])) {
  392. RedisCache::set($cacheKey . 'error', ['ids' => $ids, 'date' => $date, 'msg' => '请先设置福袋商品'], $cacheTime);
  393. return '请先设置福袋商品';
  394. }
  395. // 处理预约记录
  396. Db::startTrans();
  397. try {
  398. $boxMidList = [];
  399. $boxOpenArr = [];
  400. $awardUids = []; // 中奖的用户
  401. $groupKeys = []; // 已处理的记录分组
  402. $nullAwardCount = 0;
  403. $allAwardCount = 0;
  404. $boxCount = 0;
  405. foreach ($recordList as $key => $val) {
  406. $rid = isset($val['id']) ? $val['id'] : 0;
  407. $uid = isset($val['uid']) ? $val['uid'] : 0;
  408. $boxId = isset($val['box_id']) ? $val['box_id'] : 0;
  409. $groupKey = isset($val['group_key']) ? $val['group_key'] : '';
  410. $hasXy = isset($val['hs_xy']) ? $val['hs_xy'] : 0;
  411. $payType = isset($val['pay_type']) ? $val['pay_type'] : 0;
  412. $openTime = isset($val['open_time']) && $val['open_time'] ? $val['open_time'] : sr_getcurtime(time() + 120);
  413. if (!$rid || !$uid || !$boxId || empty($groupKey)) {
  414. Db::rollback();
  415. RedisCache::set($cacheKey . "r{$rid}_{$groupKey}_error", ['ids' => $ids, 'info' => $val, 'date' => $date, 'msg' => "抱歉用户[{$uid}]存在错误的预约数据,开袋失败"], $cacheTime);
  416. return "抱歉用户[{$uid}]存在错误的预约数据,开袋失败";
  417. }
  418. // 本次总预约数量
  419. $total = $this->model->where(['group_key' => $groupKey,'status'=> 1])->sum('num');
  420. $scorePayNum = $this->model->where(['group_key' => $groupKey,'status'=> 1,'pay_type'=>1])->sum('num');
  421. $box10 = isset($val['box10']) ? intval($val['box10']) : 0;
  422. $box20 = isset($val['box20']) ? intval($val['box20']) : 0;
  423. $box30 = isset($val['box30']) ? intval($val['box30']) : 0;
  424. $box40 = isset($val['box40']) ? intval($val['box40']) : 0;
  425. $openBoxNum = intval($box10 + $box20 + $box30 + $box40);
  426. $boxCount = $boxCount + $total;
  427. // 验证开出的盒子数和预约数量
  428. $tempScorePayNum = $scorePayNum;
  429. $nullCount = max(0, $total - $openBoxNum); // 空盒数量
  430. if ($openBoxNum > $total) {
  431. Db::rollback();
  432. RedisCache::set($cacheKey . "r{$rid}_{$groupKey}_error", ['ids' => $ids, 'total' => $total,'scoreNum'=>$tempScorePayNum, 'openNum' => $openBoxNum, 'info' => $val, 'date' => $date, 'msg' => "开袋失败:用户[{$uid}]的匹配数量设置超出预约数量"], $cacheTime);
  433. return "开袋失败:用户[{$uid}]的匹配数量设置超出预约数量";
  434. }
  435. // 处理空盒
  436. if ($nullCount > 0) {
  437. $time = sr_getcurtime(time() + $key);
  438. for ($i = 0; $i < $nullCount; $i++) {
  439. $boxMidList[] = [
  440. 'h_sn' => createdHandleOrderSn(),
  441. 'uid' => $uid,
  442. 'rid' => $rid,
  443. 'status' => 1,
  444. 'goods_id' => 0,
  445. 'create_time' => $time,
  446. 'box_settle_time' => $openTime,
  447. 'box_type' => 0,
  448. 'goods_price' => 0,
  449. 'pay_type' => $scorePayNum>0? 1 : 2
  450. ];
  451. $nullAwardCount++;
  452. $scorePayNum--;
  453. }
  454. }
  455. // 中奖处理
  456. $awardCount = 0;
  457. $openGoodsIds = [];
  458. if ($openBoxNum > 0) {
  459. $boxArr = [10 => '普通', 20 => '稀有', 30 => '史诗', 40 => '传说'];
  460. foreach ($boxArr as $bk => $bName) {
  461. $goodsArr = isset($boxGoods[$bk]) ? $boxGoods[$bk] : [];
  462. $awardNum = isset($val['box' . $bk]) ? intval($val['box' . $bk]) : 0;
  463. $time = sr_getcurtime(strtotime($openTime) + ($bk / 10));
  464. // 有中奖
  465. if ($awardNum > 0) {
  466. for ($i = 0; $i < $awardNum; $i++) {
  467. // 指定稀有
  468. $openGoods = $goodsArr;
  469. if ($hasXy == 1 && $i != 0 && $bk == 20) {
  470. $openGoods = isset($appointGoods) ? $appointGoods : [];
  471. }
  472. $rand = $openGoods ? rand(0, count($openGoods) - 1) : 0;
  473. $goods = isset($openGoods[$rand]) ? $openGoods[$rand] : [];
  474. $goodsId = isset($goods['goods_id']) ? $goods['goods_id'] : 0;
  475. if (empty($openGoods) || empty($goods) || $goodsId <= 0) {
  476. Db::rollback();
  477. RedisCache::set($cacheKey . "r{$rid}_{$groupKey}_error", ['ids' => $ids, 'total' => $total,'scoreNum'=>$tempScorePayNum, 'openNum' => $openBoxNum, 'info' => $val, 'date' => $date, 'msg' => "开袋失败:[{$bName}]福袋商品没有库存可以开奖,请先设置"], $cacheTime);
  478. return "开袋失败:[{$bName}]福袋商品没有足够库存可以开奖,请先设置";
  479. }
  480. $boxMidList[] = [
  481. 'h_sn' => createdHandleOrderSn(),
  482. 'uid' => $uid,
  483. 'rid' => $rid,
  484. 'status' => 2,
  485. 'goods_id' => $goodsId,
  486. 'create_time' => $time<$openTime?$openTime:$time,
  487. 'box_settle_time' => $openTime,
  488. 'box_type' => $bk,
  489. 'goods_price' => isset($goods['price']) ? $goods['price'] : 0,
  490. 'pay_type' => $scorePayNum>0? 1 : 2
  491. ];
  492. // 中奖数据
  493. $boxOpenArr[] = [
  494. 'status' => 1,
  495. 'h_sn' => createdHandleOrderSn(),
  496. 'uid' => $uid,
  497. 'rid' => $rid,
  498. 'handle_type' => 0,
  499. 'goods_id' => $goodsId,
  500. 'create_time' => $time<$openTime?$openTime:$time,
  501. 'open_time' => $openTime,
  502. 'box_type' => $bk,
  503. 'goods_price' => isset($goods['price']) ? $goods['price'] : 0
  504. ];
  505. $openGoodsIds[] = $goodsId;
  506. // 用户已中奖
  507. $awardUids[$uid] = 1;
  508. // 累计中奖数
  509. $awardCount++;
  510. $allAwardCount++;
  511. $scorePayNum--;
  512. }
  513. }
  514. }
  515. // 如果设置了盒子但是未开出有,则开奖失败
  516. if (empty($openGoodsIds)) {
  517. Db::rollback();
  518. RedisCache::set($cacheKey . "r{$rid}_{$groupKey}_error", ['ids' => $ids, 'total' => $total,'scoreNum'=>$tempScorePayNum, 'openNum' => $openBoxNum, 'info' => $val, 'date' => $date, 'msg' => "开袋失败:用户[{$uid}]福袋开奖处理错误"], $cacheTime);
  519. return "开袋失败:用户[{$uid}]福袋开奖处理错误";
  520. }
  521. }
  522. // 更新开奖记录的开出商品ID
  523. $updateData = ['status' => 3, 'update_time' => date('Y-m-d H:i:s')];
  524. if ($openGoodsIds) {
  525. $updateData['goods_ids'] = implode(',', $openGoodsIds);
  526. }
  527. if (!$this->model->where('id', $rid)->update($updateData)) {
  528. Db::rollback();
  529. RedisCache::set($cacheKey . "r{$rid}_{$groupKey}_error", ['ids' => $ids, 'total' => $total,'scoreNum'=>$tempScorePayNum, 'openNum' => $openBoxNum, 'info' => $val, 'date' => $date, 'msg' => "开袋失败:用户[{$uid}]更新开袋状态失败"], $cacheTime);
  530. return "开袋失败:用户[{$uid}]更新开袋状态失败";
  531. }
  532. // 处理用户开盒数据统计
  533. $hasFd = isset($val['has_fd']) ? $val['has_fd'] : 0;
  534. $isUpdate = UserModel::where('id', $uid)
  535. // 中奖数据
  536. ->inc('total_null_box', $nullCount)
  537. ->inc('box10', $box10)
  538. ->inc('box20', $box20)
  539. ->inc('box30', $box30)
  540. ->inc('box40', $box40)
  541. // 团队数据
  542. ->inc('today_box', $awardCount)
  543. ->inc('today_team_box', $awardCount)
  544. ->inc('total_box', $awardCount)
  545. ->inc('total_team_box', $awardCount)
  546. // 是否中过福袋
  547. ->inc('has_fd', $awardCount && !$hasFd ? 1 : 0)
  548. ->update();
  549. if (!$isUpdate) {
  550. Db::rollback();
  551. RedisCache::set($cacheKey . "r{$rid}_{$groupKey}_error", ['ids' => $ids, 'total' => $total,'scoreNum'=>$tempScorePayNum, 'openNum' => $openBoxNum, 'info' => $val, 'date' => $date, 'msg' => "开袋失败:更新用户[{$uid}]的中奖数据错误"], $cacheTime);
  552. return "开袋失败:更新用户[{$uid}]的中奖数据错误";
  553. }
  554. // 团毒统计中奖次数更新
  555. $upperPath = isset($val['path']) ? $val['path'] : '';
  556. $pid = isset($val['pid']) ? $val['pid'] : 0;
  557. if ($awardCount > 0 && $upperPath) {
  558. if (!UserModel::where('id', 'in', $upperPath)->inc('total_team_box', $awardCount)->inc('has_update_level', (!$hasFd? 1:0))->update()) {
  559. Db::rollback();
  560. RedisCache::set($cacheKey . "r{$rid}_{$groupKey}_error", ['ids' => $ids, 'total' => $total,'scoreNum'=>$tempScorePayNum, 'path' => $upperPath, 'openNum' => $openBoxNum, 'info' => $val, 'date' => $date, 'msg' => "开袋失败:更新用户[{$uid}]的团队中奖数据错误"], $cacheTime);
  561. return "开袋失败:更新用户[{$uid}]的团队中奖数据错误";
  562. }
  563. // 如果当前用户第一次中奖,且有直推上级,上级有效直推累计
  564. if($pid>0 && !$hasFd){
  565. //UserModel::where('id', 'in', $pid)->inc('zt_num_valid', 1)->update();
  566. }
  567. // 清理有效直推和团队人数缓存
  568. RedisCache::keyDel("caches:users:counts*");
  569. }
  570. // 处理成功
  571. $groupKeys[] = $groupKey;
  572. RedisCache::set($cacheKey . "r{$rid}_{$groupKey}_catch", ['ids' => $ids, 'total' => $total,'scoreNum'=>$tempScorePayNum, 'awardCount' => $awardCount, 'openNum' => $openBoxNum, 'null' => $nullCount, 'info' => $val, 'date' => $date, 'msg' => "开袋处理成功:更新用户[{$uid}]的数据处理成功"], $cacheTime);
  573. }
  574. if (empty($groupKeys) || empty($boxMidList)) {
  575. Db::rollback();
  576. RedisCache::set($cacheKey . "error", ['ids' => $ids, 'count' => count($boxMidList),'scoreNum'=>$tempScorePayNum, 'groupKeys' => $groupKeys, 'date' => $date, 'msg' => "开袋失败:没有开出有效福袋"], $cacheTime);
  577. return "开袋失败:没有开出有效福袋";
  578. }
  579. // 更新同组盒子状态
  580. if(count($groupKeys) == 1){
  581. if (!$this->model->where('group_key', $groupKeys[0])->update(['status' => 3, 'update_time' => date('Y-m-d H:i:s')])) {
  582. Db::rollback();
  583. RedisCache::set($cacheKey . "error", ['ids' => $ids,'sql'=>$this->model->getLastSql(),'keys'=>$groupKeys, 'count' => count($boxMidList),'scoreNum'=>$tempScorePayNum, 'date' => $date, 'msg' => "开袋失败:更新福袋开袋状态数据错误"], $cacheTime);
  584. return "开袋失败:更新福袋开袋状态数据错误";
  585. }
  586. }else{
  587. if (!$this->model->whereIn('group_key', $groupKeys)->update(['status' => 3, 'update_time' => date('Y-m-d H:i:s')])) {
  588. Db::rollback();
  589. RedisCache::set($cacheKey . "error", ['ids' => $ids,'sql'=>$this->model->getLastSql(),'keys'=>$groupKeys, 'count' => count($boxMidList),'scoreNum'=>$tempScorePayNum, 'date' => $date, 'msg' => "开袋失败:更新福袋开袋状态数据错误"], $cacheTime);
  590. return "开袋失败:更新福袋开袋状态数据错误";
  591. }
  592. }
  593. // 插入匹配数据
  594. if (!BoxMidHandleModel::insertAll($boxMidList)) {
  595. Db::rollback();
  596. RedisCache::set($cacheKey . "error", ['ids' => $ids, 'count' => count($boxMidList),'scoreNum'=>$tempScorePayNum, 'date' => $date, 'msg' => "开袋失败:写入福袋处理数据错误"], $cacheTime);
  597. return "开袋失败:写入福袋处理数据错误";
  598. }
  599. // 插入备份中奖盒子数据
  600. if ($boxOpenArr && !BoxHandleModel::insertAll($boxOpenArr)) {
  601. Db::rollback();
  602. RedisCache::set($cacheKey . "error", ['ids' => $ids, 'count' => count($boxMidList),'scoreNum'=>$tempScorePayNum, 'award' => count($boxOpenArr), 'date' => $date, 'msg' => "开袋失败:写入福袋中奖数据错误"], $cacheTime);
  603. return "开袋失败:写入福袋中奖数据错误";
  604. }
  605. // 插入中奖盒子数据
  606. if ($boxOpenArr && !BoxHandleAllModel::insertAll($boxOpenArr)) {
  607. Db::rollback();
  608. RedisCache::set($cacheKey . "errora", ['ids' => $ids, 'count' => count($boxMidList),'scoreNum'=>$tempScorePayNum, 'award' => count($boxOpenArr), 'date' => $date, 'msg' => "开袋失败:写入备份福袋中奖数据错误"], $cacheTime);
  609. return "开袋失败:写入福袋中奖数据错误";
  610. }
  611. Db::commit();
  612. RedisCache::clear($cacheKey . 'lock');
  613. RedisCache::set($cacheKey . "error", ['ids' => $ids, 'count' => count($boxMidList),'scoreNum'=>$tempScorePayNum, 'null' => $nullAwardCount, 'award' => $allAwardCount, 'date' => $date, 'msg' => "开袋失败:写入福袋中奖数据错误"], $cacheTime);
  614. return ['code' => 0, 'msg' => "开袋成功,本次提交处理数据" . count($ids) . "条,累计福袋{$boxCount}个,开出空袋:{$nullAwardCount}个,中奖{$allAwardCount}个。", 'data' => $ids];
  615. } catch (\Exception $exception){
  616. Db::rollback();
  617. RedisCache::set($cacheKey.'fail', ['ids'=> $ids,'error'=>['msg'=>$exception->getMessage(),'trace'=>$exception->getTrace()]], 3600);
  618. return '开奖失败:'.$exception->getMessage();
  619. }
  620. }
  621. }