setName('settle_team_award') ->setDescription('the settle_team_award command'); } /** * 处理分表 * @param Input $input * @param Output $output * @return int */ protected function execute(Input $input, Output $output) { $cacheKey = "caches:settleTeamAward:".date('Ymd').":"; if(RedisCache::get($cacheKey.'lock')){ echo json_encode(['code'=>'error','msg'=>'请不要频繁提交,正在结算中稍后再试~','date'=>date('Y-m-d H:i:s')],256)."\n"; return false; } if(date('H:i') < '21:00'){ echo json_encode(['code'=>'error','msg'=>'未到结算时间,结算时间为每日21:00后~','date'=>date('Y-m-d H:i:s')],256)."\n"; return false; } RedisCache::setnx($cacheKey.'lock', date('Y-m-d H:i:s'), rand(30, 60)); Db::startTrans(); try { if($result = $this->settleTeamAward()){ echo json_encode($result, 256)."\n"; Db::commit(); } } catch (\Exception $e) { Db::rollback(); $log = ['msg'=> $e->getMessage(),'trace'=>$e->getTrace(),'date'=>date('Y-m-d H:i:s')]; RedisCache::set($cacheKey."fail", $log, 5 * 3600); RedisCache::clear($cacheKey.'lock'); echo json_encode(['code'=>'error','msg'=>$e->getMessage(),'date'=>date('Y-m-d H:i:s')], 256)."\n"; } return true; } /** * 团队奖金结算 * @return array * @throws \think\Exception * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public function settleTeamAward() { // 获取开出的福袋盒子 $boxList = BoxHandleModel::alias('a') ->leftJoin('user u','u.id=a.uid') ->where('a.is_team_handle', '0') ->where('a.open_time', '<=', date('Y-m-d H:i:s')) ->field('a.id,a.uid,a.rid,u.pid,u.level,u.path,u.has_fd,u.money') ->order('a.id desc') ->select(); $boxList = $boxList? $boxList->toArray() : []; if(empty($boxList)){ sr_throw('暂无团队奖金可结算'); } // 获取等级配置 $levelConfig = LevelSettingService::make()->getConfigData(0,1); if(empty($levelConfig)){ sr_throw('请先配置等级参数'); } $date = date('Y-m-d H:i:s'); $catchIds = $moneyLogs = []; // 处理的数据 $awardCounts = []; $cacheKey = "caches:settleTeamAward:".date('Ymd').":"; foreach ($boxList as $k => $val){ $boxId = isset($val['id'])? $val['id'] : 0; $uid = isset($val['uid'])? $val['uid'] : 0; $pid = isset($val['pid'])? $val['pid'] : 0; $path = isset($val['path'])? $val['path'] : ''; if($pid<=0 || empty($path)){ $catchIds[] = $boxId; continue; } /* TODO 验证上级是否存在,并且计算直推奖金*/ $pInfo = UserModel::where(['id'=> $pid]) ->field('id,money,level,pid,path,has_fd,total_appoint_count,store_expire_time') ->find(); $pHasFd = isset($pInfo['has_fd'])? intval($pInfo['has_fd']) : 0; $pLevel = isset($pInfo['level'])? intval($pInfo['level']) : 0; $appointCount = isset($pInfo['total_appoint_count'])? intval($pInfo['total_appoint_count']) : 0; $storeExpireTime = isset($pInfo['store_expire_time']) && $pInfo['store_expire_time']? strtotime($pInfo['store_expire_time']) : 0; if($pInfo && $pHasFd){ // 计算上级V0以上,当前等级的直推奖金:v1-1元,v2-2元,v3-3元,v4-1元(没分完得) $ztAward = isset($levelConfig[$pLevel]['zt_money'])? floatval($levelConfig[$pLevel]['zt_money']) : 0; if($pLevel>0 && $ztAward>0){ $pMoney = isset($pInfo['money'])? $pInfo['money'] : 0; // 今日直推奖金 $todayZtMoney = ($appointCount>=0 && $storeExpireTime>=time())? $ztAward : 0; // 直推奖流水 $log = [ 'uid'=> $pid, 'type'=> 5, 'money'=> $ztAward, 'create_at'=> sr_getcurtime(time()), 'state'=> 1, 'before_money'=> $pMoney, 'after_money'=> floatval($pMoney + $ztAward), 'from_id'=> $boxId, 'uid2'=> $uid, 'free_type'=> 0, 'remark'=> "V{$pLevel}直推1个福袋奖金".($todayZtMoney>0? ",有今日奖金{$todayZtMoney}":'') ]; $moneyLogs[] = $log; if(!UserModel::where('id', $pid)->inc('money', $ztAward)->inc('today_money', $todayZtMoney)->update()){ $logData = ['ztAward'=>$ztAward,'data'=> $val,'pInfo'=> $pInfo,'log'=>$log,'msg'=>"直推用户[{$pid}]结算直推奖错误",'date'=> $date]; RedisCache::set($cacheKey."box_{$uid}_{$boxId}:{$pid}_{$pLevel}_error", $logData, 3*3600); sr_throw("直推用户[{$pid}]结算直推奖错误"); } // 累计当前用户获得的奖金 $awardCounts[$pid] = isset($awardCounts[$pid])? round($awardCounts[$pid]+$ztAward, 2) : $ztAward; } } /* TODO 计算团队奖金 */ $maxLevel = 0; $awardTotal = 0; $teamMoneys = []; $teamAwardEnd = $sameLevelAwardEnd = 0; // 是否给完奖金 $pIds = explode(',', $path); $pIds = array_reverse($pIds); $ids = implode(',', $pIds); $order = 'field(id,' . $ids . ')'; $teamList = UserModel::whereIn('id', $pIds) ->field('id,money,level,has_fd,total_appoint_count,store_expire_time') ->order(Db::raw($order)) ->select(); $teamList = $teamList? $teamList->toArray() : []; if(!empty($teamList)){ foreach($teamList as $key => $item){ // 计算团队奖金 $tId = isset($item['id'])? $item['id'] : 0; $tLevel = isset($item['level'])? $item['level'] : 0; $tLastId = isset($teamList[$key-1]['id'])? intval($teamList[$key-1]['id']) : 0; $tLastLevel = isset($teamList[$key-1]['level'])? intval($teamList[$key-1]['level']) : 0; // 前一个人的等级 $tMoney = isset($item['money'])? $item['money'] : 0; $tHasFd = isset($item['has_fd'])? $item['has_fd'] : 0; $tAppointCount = isset($item['total_appoint_count'])? intval($item['total_appoint_count']) : 0; $tStoreExpireTime = isset($item['store_expire_time']) && !empty($item['store_expire_time'])? strtotime($item['store_expire_time']) : 0; $levelData = isset($levelConfig[$tLevel])? $levelConfig[$tLevel] : []; if($key == 0){ // 等级大于V0 if($tLevel > 0){ $teamAward = isset($levelData['dynamic_scale'])? round($levelData['dynamic_scale'], 2) : 0; if($teamAward>0){ $teamMoneys[$tId] = $teamAward; $awardTotal += $teamAward; $maxLevel = $tLevel; // V4以及以上等级直接分完后结束,不再往下分 if($tLevel >= 4){ $teamAwardEnd = true; } } } } // 如果未分完,且等级比上一级和当前分到到用户等级都高,则再分剩余奖金 else if(!$teamAwardEnd && $tLevel > $tLastLevel && $tLevel > $maxLevel){ $teamAward = isset($levelData['dynamic_scale'])? floatval($levelData['dynamic_scale']) : 0; $teamAward = max(0, $teamAward - $awardTotal); if($teamAward>0){ $teamMoneys[$tId] = $teamAward; $awardTotal += $teamAward; } } /* TODO 计算平级奖金:未分完(金额小于0.1)情况下一直分 */ $sameTeamAward = 0; if($key>0 && !$sameLevelAwardEnd){ if($tLevel == $tLastLevel){ $sameAward = isset($teamMoneys[$tLastId])? floatval($teamMoneys[$tLastId]) : 0; $sameTeamAward = round($sameAward * 0.1, 2); $sameTeamAward = $sameTeamAward>=0.1? $sameTeamAward : 0; if($sameTeamAward>0){ $teamMoneys[$tId] = $sameTeamAward; } } } /* TODO 当前用户最终是否有团队奖金,有且预约过福袋则入账 */ $endTeamAward = isset($teamMoneys[$tId])? floatval($teamMoneys[$tId]) : 0; if($endTeamAward>0 && $tAppointCount>0){ // 今日团队奖金 $tTodayAward = ($appointCount>=10 && $tStoreExpireTime >= time())? $endTeamAward : 0; // 流水 $log = [ 'uid'=> $tId, 'type'=> 7, 'money'=> $endTeamAward, 'create_at'=> sr_getcurtime(time()), 'state'=> 1, 'before_money'=> $tMoney, 'after_money'=> floatval($tMoney + $endTeamAward), 'from_id'=> $boxId, 'uid2'=> $sameTeamAward? $tLastId : $uid, 'free_type'=> $sameTeamAward? 0 : 1, 'remark'=> ($sameTeamAward? "V{$tLevel}得[ID:{$tLastId}]1个福袋平级奖":"V{$tLevel}得[ID:{$uid}]1个福袋团队奖").($tTodayAward>0? ",有今日奖金{$tTodayAward}":'') ]; $moneyLogs[] = $log; if(!UserModel::where('id', $tId)->inc('money', $endTeamAward)->inc('today_money', $tTodayAward)->update()){ $logData = ['endAward'=>$endTeamAward,'todayAward'=>$tTodayAward,'data'=> $val,'tInfo'=> $item,'log'=>$log,'msg'=>"用户[{$tId}]结算团队奖错误",'date'=> $date]; RedisCache::set($cacheKey."box_{$uid}_{$boxId}:{$pid}_{$pLevel}_error", $logData, 3*3600); sr_throw("用户[{$tId}]结算团队奖错误"); } $awardCounts[$tId] = isset($awardCounts[$tId])? round($awardCounts[$tId]+$endTeamAward, 2) : $endTeamAward; } } } $catchIds[] = $boxId; $logData = ['teamAwards'=>$teamMoneys,'data'=> $val,'teamAward'=> $awardTotal,'msg'=>"盒子[{$boxId}]团队奖金处理完成",'date'=> $date]; RedisCache::set($cacheKey."box_{$uid}_{$boxId}:catch", $logData, 3*3600); } if(empty($catchIds)){ sr_throw('结算失败,没有结算成功数据'); } // 更新处理记录状态 if(!BoxHandleModel::whereIn('id', $catchIds)->save(['is_team_handle' => 1,'update_time'=> $date])){ sr_throw('更新处理数据状态失败'); } // 写入奖金流水 if($moneyLogs && !MoneyLogModel::insertAll($moneyLogs)){ sr_throw('处理奖金流水错误'); } $logData = ['count'=> count($catchIds),'data'=> $val,'awards'=> $awardCounts,'msg'=>"团队奖金结算成功",'date'=> $date]; RedisCache::set($cacheKey."result", $logData, 3*3600); return ['code'=>200, 'msg'=> "团队奖金结算成功,累计结算记录".count($catchIds).",结算人数".count($awardCounts)."人,总奖金".array_sum($awardCounts),'date'=> $date]; } }