FinanceService.php 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141
  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | LARAVEL8.0 框架 [ LARAVEL ][ RXThinkCMF ]
  4. // +----------------------------------------------------------------------
  5. // | 版权所有 2017~2021 LARAVEL研发中心
  6. // +----------------------------------------------------------------------
  7. // | 官方网站: http://www.laravel.cn
  8. // +----------------------------------------------------------------------
  9. // | Author: laravel开发员 <laravel.qq.com>
  10. // +----------------------------------------------------------------------
  11. namespace App\Services\Api;
  12. use App\Models\AccountLogModel;
  13. use App\Models\DeveloperModel;
  14. use App\Models\FinanceModel;
  15. use App\Models\MemberModel;
  16. use App\Models\PledgeOrderModel;
  17. use App\Services\BaseService;
  18. use App\Services\Common\InstitutionalService;
  19. use App\Services\ConfigService;
  20. use App\Services\RedisService;
  21. use Illuminate\Support\Facades\DB;
  22. /**
  23. * 平台奖金制度、财务-服务类
  24. * @author laravel开发员
  25. * @since 2020/11/12
  26. * @package App\Services\Common\v1
  27. */
  28. class FinanceService extends BaseService
  29. {
  30. protected static $instance = null;
  31. /**
  32. * 构造函数
  33. * @author laravel开发员
  34. * @since 2020/11/11
  35. * FinanceService constructor.
  36. */
  37. public function __construct()
  38. {
  39. $this->model = new FinanceModel();
  40. }
  41. /**
  42. * 静态入口
  43. * @return static|null
  44. */
  45. public static function make()
  46. {
  47. if (!self::$instance) {
  48. self::$instance = (new static());
  49. }
  50. return self::$instance;
  51. }
  52. /**
  53. * @param $params
  54. * @param int $pageSize
  55. * @return array
  56. */
  57. public function getDataList($params, $pageSize = 15)
  58. {
  59. $where = ['a.mark' => 1];
  60. $status = isset($params['status']) ? $params['status'] : 0;
  61. $type = isset($params['type']) ? $params['type'] : 0;
  62. if ($status > 0) {
  63. $where['a.status'] = $status;
  64. }
  65. if ($type > 0) {
  66. $where['a.type'] = $type;
  67. }
  68. $list = $this->model->from('finance as a')
  69. ->where($where)
  70. ->where(function ($query) use ($params) {
  71. // 日期
  72. $date = isset($params['date']) ? $params['date'] : [];
  73. $start = isset($date[0]) ? $date[0] : '';
  74. $end = isset($date[1]) ? $date[1] : '';
  75. $end = $start >= $end ? '' : $end;
  76. if ($start) {
  77. $query->where('a.create_time', '>=', strtotime($start));
  78. }
  79. if ($end) {
  80. $query->where('a.create_time', '<', strtotime($end));
  81. }
  82. })
  83. ->select(['a.*'])
  84. ->orderBy('a.create_time', 'desc')
  85. ->paginate($pageSize > 0 ? $pageSize : 9999999);
  86. $list = $list ? $list->toArray() : [];
  87. if ($list) {
  88. foreach ($list['data'] as &$item) {
  89. $item['create_time'] = $item['create_time'] ? datetime($item['create_time'], 'Y-m-d H.i.s') : '';
  90. }
  91. }
  92. return [
  93. 'pageSize' => $pageSize,
  94. 'total' => isset($list['total']) ? $list['total'] : 0,
  95. 'list' => isset($list['data']) ? $list['data'] : []
  96. ];
  97. }
  98. /**
  99. * 平台结算
  100. * @param $money
  101. * @param int $changeType 交易类型:1-进账,2-出账
  102. * @param int $type 类型:1-账户流水
  103. * @param int $coinType 币种类型: 1-USDT
  104. * @return mixed
  105. */
  106. public function saveLog($userId, $money, $changeType = 1, $type = 1, $coinType = 1)
  107. {
  108. $date = date('Y-m-d');
  109. $info = $this->model->where(['user_id' => $userId, 'date' => $date, 'type' => $type, 'mark' => 1])->first();
  110. if (!$info) {
  111. $data = ['user_id' => $userId, 'date' => $date, 'type' => $type, 'create_time' => time(), 'update_time' => time(), 'status' => 1];
  112. if ($changeType == 1) {
  113. $data['income'] = $money;
  114. } else {
  115. $data['expend'] = $money;
  116. }
  117. return $this->model->insertGetId($data);
  118. } else {
  119. if ($changeType == 1) {
  120. $info->income += $money;
  121. } else {
  122. $info->expend += $money;
  123. }
  124. return $info->save();
  125. }
  126. }
  127. /**
  128. * 质押轮次奖励
  129. * @param $userId 结算质押用户ID
  130. * @param $round 当前轮次
  131. * @param $order 订单数据
  132. * @return bool
  133. */
  134. public function pledgeAward(int $userId, int $round, array $order)
  135. {
  136. // 验证是否奖励过
  137. $orderNo = isset($order['order_no']) ? $order['order_no'] : '';
  138. // 制度参数
  139. $roundData = InstitutionalService::make()->getAwardByRound($round);
  140. $awardRound = isset($roundData['round']) ? intval($roundData['round']) : 0;
  141. $awardUsdt = isset($roundData['value']) ? floatval($roundData['value']) : 0;
  142. if ($awardRound <= 0 || $awardUsdt <= 0) {
  143. $this->error = lang(4201, ['uid' => $userId, 'round' => $round]);
  144. return false;
  145. }
  146. // 奖励的轮次已经是否已结算
  147. if (AccountLogService::make()->checkHasByRound($userId, $awardRound, 1)) {
  148. $this->error = lang(4202, ['uid' => $userId, 'round' => $awardRound, 'order_no' => $orderNo]);
  149. return false;
  150. }
  151. // 结算用户是否存在
  152. $memberInfo = MemberModel::where(['id' => $userId, 'mark' => 1])
  153. ->select(['id', 'nickname', 'usdt', 'sbt'])
  154. ->orderBy('id', 'asc')
  155. ->first();
  156. $userProfit = isset($memberInfo['profit']) ? $memberInfo['profit'] : 0;
  157. if (!$memberInfo) {
  158. $this->error = lang(4203, ['uid' => $userId, 'round' => $awardRound]);
  159. return false;
  160. }
  161. // 结算处理
  162. try {
  163. DB::beginTransaction();
  164. $updateData = ['profit' => DB::raw("profit + {$awardUsdt}"), 'profit_total' => DB::raw("profit_total + {$awardUsdt}"), 'update_time' => time()];
  165. if (!MemberModel::where(['id' => $userId])->update($updateData)) {
  166. DB::rollBack();
  167. $this->error = lang(4204, ['uid' => $userId, 'round' => $awardRound, 'money' => $awardUsdt]);
  168. return false;
  169. }
  170. // 奖励明细
  171. $data = [
  172. 'user_id' => $userId,
  173. 'order_no' => $orderNo,
  174. 'type' => 15,
  175. 'round' => $awardRound,
  176. 'user_type' => 1,
  177. 'coin_type' => 3,
  178. 'money' => $awardUsdt,
  179. 'before_money' => $userProfit,
  180. 'create_time' => time(),
  181. 'action_ip' => get_client_ip(),
  182. 'remark' => '质押轮次奖励',
  183. 'status' => 1,
  184. 'mark' => 1,
  185. ];
  186. if (!AccountLogModel::insertGetId($data)) {
  187. DB::rollBack();
  188. $this->error = lang(4204, ['uid' => $userId, 'round' => $round, 'money' => $awardUsdt]);
  189. return false;
  190. }
  191. DB::commit();
  192. $this->error = lang(4205, ['uid' => $userId, 'round' => $round, 'money' => $awardUsdt]);
  193. return true;
  194. } catch (\Exception $exception) {
  195. DB::rollBack();
  196. $this->error = lang(4206, ['uid' => $userId, 'round' => $round, 'error' => $exception->getMessage()]);
  197. return false;
  198. }
  199. }
  200. /**
  201. * 团队分红
  202. * @param $userId 质押用户
  203. * @param $parentIds 推荐人ID数组
  204. * @param $order 订单信息
  205. * @return bool
  206. */
  207. public function settleTeamBonus(int $userId, array $parentIds, array $order)
  208. {
  209. $orderNo = isset($order['order_no']) ? $order['order_no'] : '';
  210. $pledgeUsdt = isset($order['money']) ? floatval($order['money']) : 0;
  211. if ($pledgeUsdt <= 0 || empty($orderNo)) {
  212. $this->error = lang(4610, ['uid' => $userId, 'order_no' => $orderNo]);
  213. return false;
  214. }
  215. // TODO 质押用户团队分红
  216. $userInfo = MemberModel::where(['id' => $userId, 'mark' => 1])
  217. ->select(['id', 'usdt', 'performance', 'profit', 'bonus_rate', 'bonus_performance', 'team_bonus_performance', 'status'])
  218. ->first();
  219. $userInfo = $userInfo ? $userInfo->toArray() : [];
  220. $userProfit = isset($userInfo['profit']) ? floatval($userInfo['profit']) : 0; // 收益
  221. $userPerformance = isset($userInfo['performance']) ? floatval($userInfo['performance']) : 0;
  222. $bonusRate = isset($userInfo['bonus_rate']) ? floatval($userInfo['bonus_rate']) : 0;
  223. $bonusPerformance = isset($userInfo['bonus_performance']) ? floatval($userInfo['bonus_performance']) : 0;
  224. DB::beginTransaction();
  225. try {
  226. $logs = [];
  227. if ($bonusRate > 0 && $bonusRate < 100) {
  228. $teamPerformance = TeamService::make()->getTeamPerformanceByType($userId, 2, true);
  229. $teamPerformance = round($teamPerformance + $userPerformance, 2); // 用户团队业绩
  230. if ($teamPerformance > $bonusPerformance) {
  231. $bonusTotal = $pledgeUsdt;
  232. $newBonusPerformance = $teamPerformance;
  233. if ($bonusPerformance > 0) {
  234. $bonusTotal = round($teamPerformance - $bonusPerformance, 2);
  235. }
  236. // 分红金额
  237. $bonusTotal = $bonusTotal >= $pledgeUsdt ? $pledgeUsdt : $bonusTotal;
  238. $bonusAwardUsdt = moneyFormat($bonusTotal * $bonusRate / 100, 2);
  239. if ($bonusAwardUsdt > 0) {
  240. // 分红入账
  241. $updateData = [
  242. 'profit' => DB::raw("profit + {$bonusAwardUsdt}"),
  243. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  244. 'bonus_performance' => $newBonusPerformance,
  245. 'update_time' => time(),
  246. ];
  247. if (!MemberModel::where(['id' => $userId])->update($updateData)) {
  248. DB::rollBack();
  249. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的团队分红结算错误";
  250. return false;
  251. }
  252. // USDT收益账单
  253. $data = [
  254. 'user_id' => $userId,
  255. 'order_no' => $orderNo,
  256. 'type' => 14,
  257. 'round' => isset($order['round']) ? $order['round'] : 0,
  258. 'user_type' => 1,
  259. 'coin_type' => 3,
  260. 'money' => $bonusAwardUsdt,
  261. 'before_money' => $userProfit,
  262. 'create_time' => time(),
  263. 'action_ip' => get_client_ip(),
  264. 'remark' => "团队分红",
  265. 'status' => 1,
  266. 'mark' => 1,
  267. ];
  268. if (!AccountLogModel::insertGetId($data)) {
  269. DB::rollBack();
  270. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的团队分红结算失败";
  271. return false;
  272. }
  273. $logs[$userId] = ['error'=>"质押用户[{$userId}]团队分红结算成功",'orderNo'=>$orderNo,'bonusUsdt'=>$bonusAwardUsdt,'bonusRate'=>$bonusRate,'log'=>$data];
  274. }
  275. }
  276. }
  277. // TODO 上级分红结算
  278. if($parentIds){
  279. $lastUserId = $userId;
  280. $lastUserInfo = [];
  281. $bonusTotalRate = $bonusRate;
  282. $parents = MemberModel::whereIn('id', $parentIds)
  283. ->where(['mark' => 1])
  284. ->select(['id', 'usdt', 'performance', 'profit', 'bonus_rate', 'bonus_performance', 'team_bonus_performance', 'status'])
  285. ->orderByRaw('FIELD(id, "' . implode(',', $parentIds) . '")')
  286. ->get()
  287. ->keyBy('id');
  288. $parents = $parents? $parents->toArray() : [];
  289. if($parents){
  290. foreach($parentIds as $pid){
  291. $info = isset($parents[$pid])? $parents[$pid] : [];
  292. if(empty($info)){
  293. $lastUserId = $pid;
  294. $logs[$pid] = ['error'=>"分红用户[{$userId}]上级用户[{$pid}]信息不存在",'orderNo'=>$orderNo,'info'=>$info];
  295. continue;
  296. }
  297. if($lastUserInfo == []){
  298. $lastUserInfo = $userInfo;
  299. }else{
  300. $lastUserInfo = isset($parents[$lastUserId])?$parents[$lastUserId] : [];
  301. // $lastUserId = isset($lastUserInfo['id'])? $lastUserInfo['id'] : 0;
  302. }
  303. // 当前节点线下级直推用户
  304. if(empty($lastUserInfo)){
  305. $lastUserId = $pid;
  306. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]直推线用户信息不存在",'orderNo'=>$orderNo,'lastUserId'=>$lastUserId,'parents'=>$parents,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  307. continue;
  308. }
  309. // 实际分红比例
  310. $parentProfit = isset($info['profit']) ? floatval($info['profit']) : 0; // 收益
  311. $parentPerformance = isset($info['performance']) ? floatval($info['performance']) : 0;
  312. $parentBonusRate = isset($info['bonus_rate']) ? floatval($info['bonus_rate']) : 0;
  313. $realBonusRate = max(0,round($parentBonusRate - $bonusTotalRate));
  314. if($realBonusRate <=0 || $realBonusRate >= 100){
  315. $lastUserId = $pid;
  316. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]无实际分红比例",'orderNo'=>$orderNo,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  317. continue;
  318. }
  319. // 直推线已分红业绩
  320. $bonusTeamPerformance = isset($lastUserInfo['team_bonus_performance']) ? floatval($lastUserInfo['team_bonus_performance']) : 0;
  321. $lastPerformance = isset($lastUserInfo['performance']) ? floatval($lastUserInfo['performance']) : 0;
  322. // 直推线团队业绩
  323. $teamPerformance = TeamService::make()->getTeamPerformanceByType($lastUserId, 2, true);
  324. $teamPerformance = round(($teamPerformance + $lastPerformance) + $parentPerformance, 2);
  325. if ($teamPerformance > $bonusTeamPerformance) {
  326. $bonusTotal = $pledgeUsdt;
  327. $newBonusPerformance = $teamPerformance;
  328. if ($bonusTeamPerformance > 0) {
  329. $bonusTotal = round($teamPerformance - $bonusTeamPerformance, 2);
  330. }
  331. // 上级分红金额
  332. $bonusTotal = $bonusTotal >= $pledgeUsdt ? $pledgeUsdt : $bonusTotal;
  333. $bonusAwardUsdt = moneyFormat($bonusTotal * $realBonusRate / 100, 2);
  334. if ($bonusAwardUsdt > 0) {
  335. // 分红入账
  336. $updateData = [
  337. 'profit' => DB::raw("profit + {$bonusAwardUsdt}"),
  338. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  339. 'update_time' => time(),
  340. ];
  341. if (!MemberModel::where(['id' => $pid])->update($updateData)) {
  342. DB::rollBack();
  343. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]团队分红结算错误";
  344. return false;
  345. }
  346. // 直推用户记录当前节点上级已分红业绩
  347. $updateData = [
  348. 'team_bonus_performance' => $newBonusPerformance,
  349. 'update_time' => time(),
  350. ];
  351. if (!MemberModel::where(['id' => $lastUserId])->update($updateData)) {
  352. DB::rollBack();
  353. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]团队节点已分红更新错误";
  354. return false;
  355. }
  356. // USDT收益账单
  357. $data = [
  358. 'user_id' => $pid,
  359. 'order_no' => $orderNo,
  360. 'source_uid' => $userId,
  361. 'type' => 14,
  362. 'round' => isset($order['round']) ? $order['round'] : 0,
  363. 'user_type' => 1,
  364. 'coin_type' => 3,
  365. 'money' => $bonusAwardUsdt,
  366. 'before_money' => $parentProfit,
  367. 'create_time' => time()+1,
  368. 'action_ip' => get_client_ip(),
  369. 'remark' => "团队分红",
  370. 'status' => 1,
  371. 'mark' => 1,
  372. ];
  373. if (!AccountLogModel::insertGetId($data)) {
  374. DB::rollBack();
  375. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]团队分红结算失败";
  376. return false;
  377. }
  378. $lastUserId = $pid;
  379. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]团队分红成功",'orderNo'=>$orderNo,'bonusUsdt'=>$bonusAwardUsdt,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  380. $bonusTotalRate += $realBonusRate;
  381. }else{
  382. $lastUserId = $pid;
  383. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]团队分红无结算金额",'orderNo'=>$orderNo,'bonusUsdt'=>0,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  384. }
  385. }else{
  386. $lastUserId = $pid;
  387. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]团队分红业绩不足",'orderNo'=>$orderNo,'teamPerformance'=>$teamPerformance,'bonusTeamPerformance'=>$bonusTeamPerformance,'bonusUsdt'=>0,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  388. }
  389. }
  390. }
  391. }
  392. DB::commit();
  393. RedisService::set("caches:pledgeOrder:team_{$userId}_{$orderNo}", ['user_id' => $userId, 'parentIds' => $parentIds, 'order' => $order, 'log' => $logs], 6 * 3600);
  394. return ['user_id' => $userId, 'pcount' => count($parentIds), 'orderNo' => $orderNo, 'money' => $pledgeUsdt];
  395. } catch (\Exception $exception) {
  396. DB::rollBack();
  397. $this->error = "用户[{$userId}]质押订单[{$orderNo}]团队分红结算错误:" . $exception->getMessage();
  398. return false;
  399. }
  400. }
  401. /**
  402. * 工作室分红
  403. * @param $userId 质押用户
  404. * @param $parentIds 推荐人ID数组
  405. * @param $order 订单信息
  406. * @return bool
  407. */
  408. public function settleRoomBonus(int $userId, array $parentIds, array $order)
  409. {
  410. $orderNo = isset($order['order_no']) ? $order['order_no'] : '';
  411. $pledgeUsdt = isset($order['money']) ? floatval($order['money']) : 0;
  412. if ($pledgeUsdt <= 0 || empty($orderNo)) {
  413. $this->error = lang(4610, ['uid' => $userId, 'order_no' => $orderNo]);
  414. return false;
  415. }
  416. // TODO 质押用户工作室分红
  417. $userInfo = MemberModel::where(['id' => $userId, 'mark' => 1])
  418. ->select(['id', 'usdt', 'performance', 'profit', 'room_bonus_rate', 'bonus_performance', 'room_bonus_performance', 'status'])
  419. ->first();
  420. $userInfo = $userInfo ? $userInfo->toArray() : [];
  421. $userProfit = isset($userInfo['profit']) ? floatval($userInfo['profit']) : 0; // 收益
  422. $userPerformance = isset($userInfo['performance']) ? floatval($userInfo['performance']) : 0;
  423. $bonusRate = isset($userInfo['room_bonus_rate']) ? floatval($userInfo['room_bonus_rate']) : 0;
  424. $bonusPerformance = isset($userInfo['room_bonus_performance']) ? floatval($userInfo['room_bonus_performance']) : 0;
  425. DB::beginTransaction();
  426. try {
  427. if ($bonusRate > 0 && $bonusRate < 100) {
  428. $teamPerformance = TeamService::make()->getTeamPerformanceByType($userId, 2, true);
  429. $teamPerformance = round($teamPerformance + $userPerformance, 2); // 用户团队业绩
  430. if ($teamPerformance > $bonusPerformance) {
  431. $bonusTotal = $pledgeUsdt;
  432. $newBonusPerformance = $teamPerformance;
  433. if ($bonusPerformance > 0) {
  434. $bonusTotal = round($teamPerformance - $bonusPerformance, 2);
  435. }
  436. // 分红金额
  437. $bonusTotal = $bonusTotal >= $pledgeUsdt ? $pledgeUsdt : $bonusTotal;
  438. $bonusAwardUsdt = moneyFormat($bonusTotal * $bonusRate / 100, 2);
  439. if ($bonusAwardUsdt > 0) {
  440. // 分红入账
  441. $updateData = [
  442. 'profit' => DB::raw("profit + {$bonusAwardUsdt}"),
  443. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  444. 'room_bonus_performance' => $newBonusPerformance,
  445. 'update_time' => time(),
  446. ];
  447. if (!MemberModel::where(['id' => $userId])->update($updateData)) {
  448. DB::rollBack();
  449. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的工作室分红结算错误";
  450. return false;
  451. }
  452. // USDT收益账单
  453. $data = [
  454. 'user_id' => $userId,
  455. 'order_no' => $orderNo,
  456. 'type' => 16,
  457. 'round' => isset($order['round']) ? $order['round'] : 0,
  458. 'user_type' => 1,
  459. 'coin_type' => 3,
  460. 'money' => $bonusAwardUsdt,
  461. 'before_money' => $userProfit,
  462. 'create_time' => time(),
  463. 'action_ip' => get_client_ip(),
  464. 'remark' => "工作室分红",
  465. 'status' => 1,
  466. 'mark' => 1,
  467. ];
  468. if (!AccountLogModel::insertGetId($data)) {
  469. DB::rollBack();
  470. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的工作室分红结算失败";
  471. return false;
  472. }
  473. }
  474. }
  475. }
  476. // TODO 上级分红结算
  477. $logs = [];
  478. if($parentIds){
  479. $bonusTotalRate = $bonusRate;
  480. $parents = MemberModel::whereIn('id', $parentIds)
  481. ->where(['mark' => 1])
  482. ->select(['id', 'usdt', 'performance', 'profit', 'room_bonus_rate', 'room_bonus_performance', 'status'])
  483. ->orderByRaw('FIELD(id, "' . implode(',', $parentIds) . '")')
  484. ->get()
  485. ->keyBy('id');
  486. $parents = $parents? $parents->toArray() : [];
  487. if($parents){
  488. foreach($parentIds as $pid){
  489. $info = isset($parents[$pid])? $parents[$pid] : [];
  490. if(empty($info)){
  491. $logs[$pid] = ['error'=>"工作室分红用户[{$userId}]上级用户[{$pid}]信息不存在",'orderNo'=>$orderNo,'info'=>$info];
  492. continue;
  493. }
  494. // 实际分红比例
  495. $parentProfit = isset($info['profit']) ? floatval($info['profit']) : 0; // 收益
  496. $parentPerformance = isset($info['performance']) ? floatval($info['performance']) : 0;
  497. $parentBonusRate = isset($info['room_bonus_rate']) ? floatval($info['room_bonus_rate']) : 0;
  498. $bonusTeamPerformance = isset($info['room_bonus_performance']) ? floatval($info['room_bonus_performance']) : 0;
  499. $realBonusRate = max(0,round($parentBonusRate - $bonusTotalRate));
  500. if($realBonusRate <=0 || $realBonusRate >= 100){
  501. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]无实际工作室分红比例",'orderNo'=>$orderNo,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  502. continue;
  503. }
  504. // 当前团队业绩
  505. $teamPerformance = TeamService::make()->getTeamPerformanceByType($pid, 2, true);
  506. $teamPerformance = round($teamPerformance + $parentPerformance, 2);
  507. if ($teamPerformance > $bonusTeamPerformance) {
  508. $bonusTotal = $pledgeUsdt;
  509. $newBonusPerformance = $teamPerformance;
  510. if ($bonusTeamPerformance > 0) {
  511. $bonusTotal = round($teamPerformance - $bonusTeamPerformance, 2);
  512. }
  513. // 上级工作室分红金额
  514. $bonusTotal = $bonusTotal >= $pledgeUsdt ? $pledgeUsdt : $bonusTotal;
  515. $bonusAwardUsdt = moneyFormat($bonusTotal * $realBonusRate / 100, 2);
  516. if ($bonusAwardUsdt > 0) {
  517. // 工作室分红入账
  518. $updateData = [
  519. 'profit' => DB::raw("profit + {$bonusAwardUsdt}"),
  520. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  521. 'room_bonus_performance' => $newBonusPerformance,
  522. 'update_time' => time(),
  523. ];
  524. if (!MemberModel::where(['id' => $pid])->update($updateData)) {
  525. DB::rollBack();
  526. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]工作室分红结算错误";
  527. return false;
  528. }
  529. // USDT收益账单
  530. $data = [
  531. 'user_id' => $pid,
  532. 'order_no' => $orderNo,
  533. 'source_uid' => $userId,
  534. 'type' => 16,
  535. 'round' => isset($order['round']) ? $order['round'] : 0,
  536. 'user_type' => 1,
  537. 'coin_type' => 3,
  538. 'money' => $bonusAwardUsdt,
  539. 'before_money' => $parentProfit,
  540. 'create_time' => time(),
  541. 'action_ip' => get_client_ip(),
  542. 'remark' => "工作室分红",
  543. 'status' => 1,
  544. 'mark' => 1,
  545. ];
  546. if (!AccountLogModel::insertGetId($data)) {
  547. DB::rollBack();
  548. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]工作室分红结算失败";
  549. return false;
  550. }
  551. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]工作室分红结算成功",'orderNo'=>$orderNo,'bonusUsdt'=>$bonusAwardUsdt,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  552. $bonusTotalRate += $realBonusRate;
  553. }
  554. }
  555. }
  556. }
  557. }
  558. DB::commit();
  559. RedisService::set("caches:pledgeOrder:room_{$userId}_{$orderNo}", ['user_id' => $userId, 'parentIds' => $parentIds, 'order' => $order, 'log' => $logs], 6 * 3600);
  560. return ['user_id' => $userId, 'pcount' => count($parentIds), 'orderNo' => $orderNo, 'money' => $pledgeUsdt];
  561. } catch (\Exception $exception) {
  562. DB::rollBack();
  563. $this->error = "用户[{$userId}]质押订单[{$orderNo}]工作室分红结算错误:" . $exception->getMessage();
  564. return false;
  565. }
  566. }
  567. /**
  568. * 管理奖、平级奖结算
  569. * @param $userId 质押用户
  570. * @param $order
  571. * @return bool
  572. */
  573. public function settleManageAndLevelAward($userId, $parentIds, $order)
  574. {
  575. $orderNo = isset($order['order_no']) ? $order['order_no'] : '';
  576. $pledgeUsdt = isset($order['money']) ? floatval($order['money']) : 0;
  577. if ($pledgeUsdt <= 0 || empty($orderNo)) {
  578. $this->error = lang(4512, ['uid' => $userId, 'order_no' => $orderNo]);
  579. return false;
  580. }
  581. $parentIds = get_parents($parentIds);
  582. if (empty($parentIds)) {
  583. $this->error = lang(4513, ['uid' => $userId, 'order_no' => $orderNo]);
  584. return true;
  585. }
  586. // SBT价格
  587. $sbtPrice = PriceLogService::make()->getSbtPrice();
  588. // USDT收益分配参数
  589. $settleUsdtRate = ConfigService::make()->getConfigByCode('bonus_usdt_rate', 80);
  590. $settleUsdtRate = $settleUsdtRate > 0 && $settleUsdtRate <= 100 ? $settleUsdtRate : 80;
  591. $memberInfo = MemberModel::from('member as a')
  592. ->leftJoin('member_level as b', 'b.id', '=', 'a.member_level')
  593. ->where(['a.id' => $userId, 'a.mark' => 1])
  594. ->select(['a.id','a.member_level','a.usdt','a.profit','a.status','b.bonus_rate'])
  595. ->first();
  596. $memberInfo = $memberInfo ? $memberInfo->toArray() : [];
  597. $userBonusRate = isset($userInfo['bonus_rate'])? $userInfo['bonus_rate'] : 0;
  598. if(empty($memberInfo)){
  599. $this->error = lang(4413, ['uid' => $userId, 'order_no' => $orderNo]);
  600. return false;
  601. }
  602. // 结算处理
  603. try {
  604. // TODO 管理奖结算
  605. $count = 0;
  606. $bonusRateTotal = $userBonusRate;
  607. $bonusTotal = 0;
  608. $logs = [];
  609. $pjUsers = [];
  610. DB::beginTransaction();
  611. foreach ($parentIds as $pid) {
  612. $userInfo = MemberModel::from('member as a')
  613. ->leftJoin('member_level as b', 'b.id', '=', 'a.member_level')
  614. ->where(['a.id' => $pid, 'a.mark' => 1])
  615. ->select(['a.id', 'a.usdt', 'a.member_level', 'a.parent_ids', 'a.profit', 'a.sbt', 'a.profit_total', 'a.status', 'b.bonus_rate', 'b.weighting_rate'])
  616. ->first();
  617. $userInfo = $userInfo ? $userInfo->toArray() : [];
  618. $userProfit = isset($userInfo['profit']) ? $userInfo['profit'] : 0;
  619. $userUsdt = isset($userInfo['usdt']) ? $userInfo['usdt'] : 0;
  620. $userSbt = isset($userInfo['sbt']) ? $userInfo['sbt'] : 0;
  621. $level = isset($userInfo['member_level']) ? intval($userInfo['member_level']) : 0;
  622. if (empty($userInfo) || $level <= 0) {
  623. $logs[$pid] = ['error' => "上级推荐人[{$pid}]无等级,无管理奖", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt];
  624. continue;
  625. }
  626. $bonusRate = isset($userInfo['bonus_rate']) && $userInfo['bonus_rate'] ? floatval($userInfo['bonus_rate']) : 0;
  627. $weightingRate = isset($userInfo['bonus_rate']) && $userInfo['weighting_rate'] ? floatval($userInfo['weighting_rate']) : 0;
  628. $realBonusRate = max(0, round($bonusRate - $bonusRateTotal, 2)); // 级差佣金比例,减去已分的部分
  629. $awardUsdt = moneyFormat($pledgeUsdt * $realBonusRate / 100, 2);
  630. $bonusAwardUsdt = $awardUsdt;
  631. // 全网加权收益
  632. $teamPerformance = 0;
  633. $globalPledgeTotal = 0;
  634. $weightingAwardUsdt = 0;
  635. $globalLevelTeamPerformance = 0;
  636. if ($weightingRate > 0 && $weightingRate < 100) {
  637. // 全网流水(质押中)
  638. $globalPledgeTotal = PledgeOrderService::make()->getGlobalTotal();
  639. if ($globalPledgeTotal > 0) {
  640. // 该等级全网业绩
  641. $globalLevelTeamPerformance = TeamService::make()->getTeamPerformanceByLevel($level);
  642. // 该用户团队业绩
  643. $teamPerformance = TeamService::make()->getTeamPerformanceByType($pid, 2, true);
  644. // 等级加权比例
  645. $levelWeightingRate = round($teamPerformance/$globalLevelTeamPerformance, 2);
  646. // 实际收益 = 基础收益 + 加权收益(3%全网流水 x 加权比例)
  647. $weightingAwardUsdt = moneyFormat($globalPledgeTotal * ($weightingRate / 100) * $levelWeightingRate, 2);
  648. $bonusAwardUsdt = moneyFormat($weightingAwardUsdt + $bonusAwardUsdt, 2);
  649. }
  650. }
  651. if ($bonusRate <= 0 || $realBonusRate <= 0 || $bonusAwardUsdt <= 0) {
  652. $logs[$pid] = ['error' => "上级推荐人[{$pid}]无管理奖收益", 'rate' => $bonusRate, 'realRate' => $realBonusRate, 'bonusUsdt' => $bonusAwardUsdt, 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt];
  653. continue;
  654. }
  655. // 结算划分
  656. $bonusRateTotal += $realBonusRate;
  657. $bonusUsdt = moneyFormat($bonusAwardUsdt * $settleUsdtRate / 100, 2); // 划分到USDT部分
  658. $bonusSbt = moneyFormat(($bonusAwardUsdt - $bonusUsdt) / $sbtPrice, 2); // 划分到SBT金本位部分
  659. if ($bonusUsdt <= 0 && $bonusSbt <= 0) {
  660. DB::rollBack();
  661. $logs[$pid] = ['error' => "上级推荐人[{$pid}]管理奖划分计算错误", 'rate' => $bonusRate, 'usdtRate' => $settleUsdtRate, 'sbtPrice' => $sbtPrice, 'realRate' => $realBonusRate, 'bonusUsdt' => $bonusAwardUsdt, 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt];
  662. $this->error = "上级推荐人[{$pid}]管理奖划分计算错误";
  663. return false;
  664. }
  665. // 验证是否结算过
  666. if ($logId = AccountLogModel::where(['user_id' => $pid, 'order_no' => $orderNo, 'type' => 9, 'status' => 1, 'mark' => 1])->value('id')) {
  667. $logs[$pid] = ['error' => "上级推荐人[{$pid}]管理奖已结算过", 'logId' => $logId, 'rate' => $bonusRate, 'realRate' => $realBonusRate, 'bonusUsdt' => $bonusAwardUsdt, 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt];
  668. continue;
  669. }
  670. // 结算入账
  671. $updateData = [
  672. 'profit' => DB::raw("profit + {$bonusUsdt}"),
  673. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  674. 'manage_profit' => DB::raw("manage_profit + {$bonusAwardUsdt}"),
  675. 'sbt' => DB::raw("sbt + {$bonusSbt}"),
  676. 'update_time' => time(),
  677. ];
  678. if (!MemberModel::where(['id' => $pid])->update($updateData)) {
  679. DB::rollBack();
  680. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的推荐人[{$pid}]管理奖收益[{$bonusAwardUsdt}]结算错误";
  681. return false;
  682. }
  683. // USDT收益账单
  684. $data = [
  685. 'user_id' => $pid,
  686. 'order_no' => $orderNo,
  687. 'source_uid' => $userId,
  688. 'type' => 9,
  689. 'round' => isset($order['round']) ? $order['round'] : 0,
  690. 'user_type' => 1,
  691. 'coin_type' => 3,
  692. 'money' => $bonusUsdt,
  693. 'before_money' => $userProfit,
  694. 'create_time' => time(),
  695. 'action_ip' => get_client_ip(),
  696. 'remark' => "S{$level}管理奖{$realBonusRate}%的{$settleUsdtRate}%",
  697. 'status' => 1,
  698. 'mark' => 1,
  699. ];
  700. // 有全网加权
  701. if($globalLevelTeamPerformance && $weightingAwardUsdt){
  702. $data['settle_remark'] = "S{$level}全网[{$globalLevelTeamPerformance}]团队业绩[{$teamPerformance}]全网流水[{$globalPledgeTotal}]加权收益[{$weightingAwardUsdt}]";
  703. }
  704. if (!AccountLogModel::insertGetId($data)) {
  705. DB::rollBack();
  706. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的推荐人[{$pid}]管理奖[{$bonusAwardUsdt}]结算错误";
  707. return false;
  708. }
  709. // SBT收益账单
  710. if ($bonusSbt > 0) {
  711. $data = [
  712. 'user_id' => $pid,
  713. 'order_no' => $orderNo,
  714. 'source_uid' => $userId,
  715. 'type' => 9,
  716. 'round' => isset($order['round']) ? $order['round'] : 0,
  717. 'user_type' => 1,
  718. 'coin_type' => 2,
  719. 'money' => $bonusSbt,
  720. 'before_money' => $userSbt,
  721. 'create_time' => time(),
  722. 'action_ip' => get_client_ip(),
  723. 'remark' => "S{$level}管理奖{$realBonusRate}%的" . (100 - $settleUsdtRate) . '%',
  724. 'status' => 1,
  725. 'mark' => 1,
  726. ];
  727. if (!AccountLogModel::insertGetId($data)) {
  728. DB::rollBack();
  729. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的推荐人[{$pid}]管理奖[{$bonusAwardUsdt}]结算错误";
  730. return false;
  731. }
  732. }
  733. // S1等级及以上,平级奖数据(若有上级)
  734. $parentIds = isset($userInfo['parent_ids']) ? $userInfo['parent_ids'] : '';
  735. $parentIds = get_parents($parentIds);
  736. if ($parentIds && $level >= 1) {
  737. $pjUsers[$pid] = [
  738. 'level' => $level, // 管理奖用户等级
  739. 'usdt' => $bonusAwardUsdt, // 管理奖USDT金额
  740. 'parent_ids' => $parentIds, // 管理奖用户上级推荐人数组
  741. ];
  742. }
  743. $logs[$pid] = ['error' => "推荐人[{$pid}]管理奖结算成功", 'orderNo' => $orderNo, 'uid' => $userId, 'profit' => $userProfit, 'bonusAwardUsdt' => $bonusAwardUsdt, 'awardUsdt' => $awardUsdt, 'pledgeUsdt' => $pledgeUsdt];
  744. $bonusTotal += $bonusAwardUsdt;
  745. $count++;
  746. }
  747. // TODO 平级奖结算
  748. $pjLogs = [];
  749. $pjCount = 0;
  750. $pjTotal = 0;
  751. $pjProfitRate = ConfigService::make()->getConfigByCode('pj_profit_rate', 0);
  752. $pjProfitRate = $pjProfitRate > 0 && $pjProfitRate < 100 ? $pjProfitRate : 0;
  753. if ($pjUsers && $pjProfitRate > 0) {
  754. foreach ($pjUsers as $uid => $item) {
  755. $pjLevel = isset($item['level']) ? $item['level'] : 0;
  756. $bonusUsdt = isset($item['usdt']) ? $item['usdt'] : 0;
  757. $pjParentIds = isset($item['parent_ids']) ? $item['parent_ids'] : [];
  758. if ($pjLevel <= 0 || empty($pjParentIds) || $bonusUsdt <= 0) {
  759. DB::rollBack();
  760. $pjLogs[$uid] = ['error' => "管理奖用户[{$uid}]平级奖参数错误", 'log' => $item];
  761. $this->error = "管理奖用户[{$uid}]平级奖参数错误[{$pjLevel}-{$bonusUsdt}]";
  762. return false;
  763. }
  764. // 平级奖金额
  765. $pjAwardUsdt = moneyFormat($bonusUsdt * $pjProfitRate / 100, 2);
  766. if ($pjAwardUsdt <= 0) {
  767. $pjLogs[$uid] = ['error' => "管理奖用户[{$uid}]无平级收益结算", 'rate' => $pjProfitRate, 'log' => $item];
  768. continue;
  769. }
  770. // 是否有平级推荐人
  771. $pjInfo = TeamService::make()->getUpperLevelUser($level, $pjParentIds);
  772. $pjUserId = isset($pjInfo['id']) ? $pjInfo['id'] : 0;
  773. $pjUserProfit = isset($pjInfo['profit']) ? $pjInfo['profit'] : 0;
  774. if (empty($pjInfo) || $pjUserId <= 0) {
  775. $pjLogs[$uid] = ['error' => "管理奖用户[{$uid}]无平级上级", 'profit' => $pjAwardUsdt, 'log' => $item];
  776. continue;
  777. }
  778. // 平级奖结算到账
  779. $updateData = [
  780. 'profit' => DB::raw("profit + {$pjAwardUsdt}"),
  781. 'profit_total' => DB::raw("profit_total + {$pjAwardUsdt}"),
  782. 'pj_profit' => DB::raw("pj_profit + {$pjAwardUsdt}"),
  783. 'update_time' => time()
  784. ];
  785. if (!MemberModel::where(['id' => $pjUserId])->update($updateData)) {
  786. DB::rollBack();
  787. $pjLogs[$uid] = ['error' => "管理奖用户[{$uid}]上级[{$pjUserId}]平级奖结算失败", 'profit' => $pjAwardUsdt, 'log' => $item];
  788. $this->error = "管理奖用户[{$uid}]上级[{$pjUserId}]平级奖结算失败";
  789. return false;
  790. }
  791. // USDT收益账单
  792. $data = [
  793. 'user_id' => $pjUserId,
  794. 'order_no' => $orderNo,
  795. 'source_uid' => $uid,
  796. 'type' => 10,
  797. 'round' => isset($order['round']) ? $order['round'] : 0,
  798. 'user_type' => 1,
  799. 'coin_type' => 3,
  800. 'money' => $pjAwardUsdt,
  801. 'before_money' => $pjUserProfit,
  802. 'create_time' => time(),
  803. 'action_ip' => get_client_ip(),
  804. 'remark' => "S{$pjLevel}平级奖励",
  805. 'status' => 1,
  806. 'mark' => 1,
  807. ];
  808. if (!AccountLogModel::insertGetId($data)) {
  809. DB::rollBack();
  810. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的管理奖用户[{$uid}]平级奖用户[{$pjUserId}]的平级奖[{$pjAwardUsdt}]结算错误";
  811. return false;
  812. }
  813. }
  814. }
  815. DB::commit();
  816. RedisService::set("caches:pledgeOrder:share_{$userId}_{$orderNo}", ['user_id' => $userId, 'parentIds' => $parentIds, 'order' => $order, 'log' => $logs, 'pjRate' => $pjProfitRate, 'pjLogs' => $pjLogs], 6 * 3600);
  817. return ['user_id' => $userId, 'bonusTotal' => $bonusTotal, 'count' => $count, 'all' => count($parentIds), 'pjTotal' => $pjTotal, 'pjCount' => $pjCount, 'pjAll' => count($pjUsers), 'orderNo' => $orderNo, 'money' => $pledgeUsdt];
  818. } catch (\Exception $exception) {
  819. DB::rollBack();
  820. $this->error = "用户[{$userId}]质押订单[{$orderNo}]管理奖和平级奖结算错误:" . $exception->getMessage();
  821. return false;
  822. }
  823. }
  824. /**
  825. * 推荐收益奖励结算
  826. * @param $userId 质押订单用户
  827. * @param $parentIds 质押订单用户上级推荐人ID串
  828. * @param $order 结算质押订单:order_no-订单号,money-质押金额,round-质押轮次
  829. * @return array|bool
  830. */
  831. public function settleShareAward($userId, $parentIds, $order)
  832. {
  833. $orderNo = isset($order['order_no']) ? $order['order_no'] : '';
  834. $pledgeUsdt = isset($order['money']) ? floatval($order['money']) : 0;
  835. if ($pledgeUsdt <= 0 || empty($orderNo)) {
  836. $this->error = lang(4510, ['uid' => $userId, 'order_no' => $orderNo]);
  837. return false;
  838. }
  839. $parentIds = get_parents($parentIds);
  840. // $parentIds = array_slice($parentIds, 0, 10);
  841. if (empty($parentIds)) {
  842. $this->error = lang(4511, ['uid' => $userId, 'order_no' => $orderNo]);
  843. return true;
  844. }
  845. // 推荐奖励参数
  846. try {
  847. $count = 0;
  848. $logs = [];
  849. $floor = 0;
  850. $shareRates = InstitutionalService::make()->getAwardByFloor();
  851. DB::beginTransaction();
  852. foreach ($parentIds as $key => $pid) {
  853. if($floor>7){
  854. $logs[$pid] = ['error' => "该推荐人[{$pid}]超过结算层级,不结算", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor, 'rate' => $rate];
  855. continue;
  856. }
  857. // 验证用户是否有效
  858. if (!PledgeOrderService::make()->checHasPledge($pid)) {
  859. $logs[$pid] = ['error' => "该推荐人[{$pid}]是无效用户,不计算奖励不计算层级", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor+1, 'rate' => $rate];
  860. continue;
  861. }
  862. $rateInfo = isset($shareRates[$floor+1]) ? $shareRates[$floor+1] : [];
  863. $rate = isset($rateInfo['value']) && $rateInfo['value'] > 0 && $rateInfo['value'] < 100 ? floatval($rateInfo['value']) : 0;
  864. if (empty($rateInfo) || $rate <= 0) {
  865. $floor++;
  866. $logs[$pid] = ['error' => "该层推荐人[{$pid}]推荐收益比例参数未配置,算层级不结算", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor+1];
  867. continue;
  868. }
  869. $shareAwardUsdt = moneyFormat($pledgeUsdt * $rate / 100, 2);
  870. if ($shareAwardUsdt <= 0) {
  871. $floor++;
  872. $logs[$pid] = ['error' => "该层推荐人[{$pid}]推荐无推荐收益,算层级不结算", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor+1, 'rate' => $rate];
  873. continue;
  874. }
  875. $userInfo = MemberModel::where(['id' => $pid, 'mark' => 1])
  876. ->select(['id', 'usdt', 'profit', 'sbt', 'profit_total', 'status'])
  877. ->first();
  878. $userInfo = $userInfo ? $userInfo->toArray() : [];
  879. $userProfit = isset($userInfo['profit']) ? $userInfo['profit'] : 0;
  880. if (empty($userInfo) || $pid <= 0) {
  881. $logs[$pid] = ['error' => "该推荐人[{$pid}]信息不存在,不算层级", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor+1, 'rate' => $rate];
  882. continue;
  883. }
  884. // 验证是否结算过
  885. if ($logId = AccountLogModel::where(['user_id' => $pid, 'order_no' => $orderNo, 'type' => 8, 'status' => 1, 'mark' => 1])->value('id')) {
  886. $floor++;
  887. $logs[$pid] = ['error' => "该层推荐人[{$pid}]推荐奖励已结算过,算层级", 'logId' => $logId, 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor+1, 'rate' => $rate];
  888. continue;
  889. }
  890. // 结算入账
  891. $updateData = [
  892. 'profit' => DB::raw("profit + {$shareAwardUsdt}"),
  893. 'profit_total' => DB::raw("profit_total + {$shareAwardUsdt}"),
  894. 'share_profit' => DB::raw("share_profit + {$shareAwardUsdt}"),
  895. 'update_time' => time(),
  896. ];
  897. if (!MemberModel::where(['id' => $pid])->update($updateData)) {
  898. DB::rollBack();
  899. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的[{$floor}]层推荐人[{$pid}]推荐收益[{$shareAwardUsdt}]结算错误";
  900. return false;
  901. }
  902. // 收益账单
  903. $data = [
  904. 'user_id' => $pid,
  905. 'order_no' => $orderNo,
  906. 'source_uid' => $userId,
  907. 'type' => 8,
  908. 'round' => isset($order['round']) ? $order['round'] : 0,
  909. 'user_type' => 1,
  910. 'coin_type' => 3,
  911. 'money' => $shareAwardUsdt,
  912. 'before_money' => $userProfit,
  913. 'create_time' => time(),
  914. 'action_ip' => get_client_ip(),
  915. 'remark' => ($floor+1)."层推荐奖励",
  916. 'status' => 1,
  917. 'mark' => 1,
  918. ];
  919. if (!AccountLogModel::insertGetId($data)) {
  920. DB::rollBack();
  921. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的[".($floor+1)."]层推荐人[{$pid}]推荐收益[{$shareAwardUsdt}]结算错误";
  922. return false;
  923. }
  924. $logs[$pid] = ['error' => "推荐人[{$pid}]奖励结算成功", 'orderNo' => $orderNo, 'uid' => $userId, 'profit' => $userProfit, 'awardUsdt' => $shareAwardUsdt, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor+1, 'rate' => $rate];
  925. $count++;
  926. $floor++;
  927. }
  928. DB::commit();
  929. RedisService::set("caches:pledgeOrder:share_{$userId}_{$orderNo}", ['user_id' => $userId, 'parentIds' => $parentIds, 'order' => $order, 'log' => $logs], 6 * 3600);
  930. return ['user_id' => $userId, 'count' => $count, 'all' => count($parentIds), 'orderNo' => $orderNo, 'money' => $pledgeUsdt];
  931. } catch (\Exception $exception) {
  932. DB::rollBack();
  933. $this->error = "用户[{$userId}]质押订单[{$orderNo}]推荐收益结算错误:" . $exception->getMessage();
  934. return false;
  935. }
  936. }
  937. /**
  938. * 开发者维护收益结算
  939. * @param $developerId 开发者ID
  940. * @param $walletUrl 开发者地址
  941. * @return false
  942. */
  943. public function developerSettle($developerId, $walletUrl)
  944. {
  945. if(empty($developerId) || empty($walletUrl)){
  946. $this->error = 2014;
  947. return false;
  948. }
  949. $info = DeveloperModel::from('developer as a')
  950. ->leftJoin('member as b','b.wallet_url','=','a.wallet_url')
  951. ->where(['a.id'=> $developerId])
  952. ->select(['a.*','b.id as user_id','b.profit'])
  953. ->first();
  954. $info = $info? $info->toArray() : [];
  955. $userId = isset($info['user_id'])? intval($info['user_id']) : 0;
  956. $userProfit = isset($info['profit'])? floatval($info['profit']) : 0;
  957. $profitRate = isset($info['maintenance_rate'])? floatval($info['maintenance_rate']) : 0;
  958. if(empty($info) || $userId<=0){
  959. $this->error = 2001;
  960. return false;
  961. }
  962. $lastSettleAt = isset($info['last_settle_at'])? $info['last_settle_at'] : '';
  963. if($lastSettleAt && $lastSettleAt >= date('Y-m-d', time() - 7 * 86400)){
  964. $this->error = 1044;
  965. return false;
  966. }
  967. $cacheKey = "caches:developer:lock_{$developerId}";
  968. if(RedisService::get($cacheKey)){
  969. $this->error = 1044;
  970. return false;
  971. }
  972. DB::beginTransaction();
  973. try {
  974. // 全网流水
  975. $pledgeTotal = PledgeOrderService::make()->getGlobalTotal();
  976. // 结算比例
  977. $settleRate = ConfigService::make()->getConfigByCode('developer_settle_rate', 3);
  978. $settleRate = $settleRate>0 && $settleRate <= 20? $settleRate : 3;
  979. $settleRate = $profitRate>0 && $profitRate< 50? $profitRate : $settleRate;
  980. $settleMoney = round($pledgeTotal * $settleRate/100, 2);
  981. if($settleMoney>0){
  982. $updateData = [
  983. 'profit'=> DB::raw("profit + {$settleMoney}"),
  984. 'profit_total'=> DB::raw("profit_total + {$settleMoney}"),
  985. 'update_time'=>time()
  986. ];
  987. if(!MemberModel::where(['id'=> $userId])->update($updateData)){
  988. DB::rollBack();
  989. $this->error = 1045;
  990. return false;
  991. }
  992. $updateData = [
  993. 'total'=> DB::raw("total + {$settleMoney}"),
  994. 'last_settle_at'=>date('Y-m-d H:i:s'),
  995. 'update_time'=>time()
  996. ];
  997. if(!DeveloperModel::where(['id'=> $developerId])->update($updateData)){
  998. DB::rollBack();
  999. $this->error = 1045;
  1000. return false;
  1001. }
  1002. // 收益账单
  1003. $data = [
  1004. 'user_id' => $userId,
  1005. 'order_no' => get_order_num('DP'),
  1006. 'source_uid' => 0,
  1007. 'type' => 99,
  1008. 'user_type' => 1,
  1009. 'coin_type' => 3,
  1010. 'money' => $settleMoney,
  1011. 'before_money' => $userProfit,
  1012. 'create_time' => time(),
  1013. 'action_ip' => get_client_ip(),
  1014. 'remark' => "工作室分红",
  1015. 'status' => 1,
  1016. 'mark' => 1,
  1017. ];
  1018. if (!AccountLogModel::insertGetId($data)) {
  1019. DB::rollBack();
  1020. $this->error = 1045;
  1021. return false;
  1022. }
  1023. }
  1024. DB::commit();
  1025. $this->error = 1046;
  1026. return ['id'=>$developerId,'user_id'=>$userId,'money'=>$settleMoney];
  1027. }catch (\Exception $exception){
  1028. $this->error = $exception->getMessage();
  1029. return false;
  1030. }
  1031. }
  1032. }