FinanceService.php 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110
  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. if ($bonusRate > 0 && $bonusRate < 100) {
  227. $teamPerformance = TeamService::make()->getTeamPerformanceByType($userId, 2, true);
  228. $teamPerformance = round($teamPerformance + $userPerformance, 2); // 用户团队业绩
  229. if ($teamPerformance > $bonusPerformance) {
  230. $bonusTotal = $pledgeUsdt;
  231. $newBonusPerformance = $teamPerformance;
  232. if ($bonusPerformance > 0) {
  233. $bonusTotal = round($teamPerformance - $bonusPerformance, 2);
  234. }
  235. // 分红金额
  236. $bonusTotal = $bonusTotal >= $pledgeUsdt ? $pledgeUsdt : $bonusTotal;
  237. $bonusAwardUsdt = moneyFormat($bonusTotal * $bonusRate / 100, 2);
  238. if ($bonusAwardUsdt > 0) {
  239. // 分红入账
  240. $updateData = [
  241. 'profit' => DB::raw("profit + {$bonusAwardUsdt}"),
  242. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  243. 'bonus_performance' => $newBonusPerformance,
  244. 'update_time' => time(),
  245. ];
  246. if (!MemberModel::where(['id' => $userId])->update($updateData)) {
  247. DB::rollBack();
  248. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的团队分红结算错误";
  249. return false;
  250. }
  251. // USDT收益账单
  252. $data = [
  253. 'user_id' => $userId,
  254. 'order_no' => $orderNo,
  255. 'type' => 14,
  256. 'round' => isset($order['round']) ? $order['round'] : 0,
  257. 'user_type' => 1,
  258. 'coin_type' => 3,
  259. 'money' => $bonusAwardUsdt,
  260. 'before_money' => $userProfit,
  261. 'create_time' => time(),
  262. 'action_ip' => get_client_ip(),
  263. 'remark' => "团队分红",
  264. 'status' => 1,
  265. 'mark' => 1,
  266. ];
  267. if (!AccountLogModel::insertGetId($data)) {
  268. DB::rollBack();
  269. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的团队分红结算失败";
  270. return false;
  271. }
  272. }
  273. }
  274. }
  275. // TODO 上级分红结算
  276. $logs = [];
  277. if($parentIds){
  278. $lastUserId = $userId;
  279. $lastUserInfo = [];
  280. $bonusTotalRate = $bonusRate;
  281. $parents = MemberModel::whereIn('id', $parentIds)
  282. ->where(['mark' => 1])
  283. ->select(['id', 'usdt', 'performance', 'profit', 'bonus_rate', 'bonus_performance', 'team_bonus_performance', 'status'])
  284. ->orderByRaw('FIELD(id, "' . implode(',', $parentIds) . '")')
  285. ->get()
  286. ->keyBy('id');
  287. $parents = $parents? $parents->toArray() : [];
  288. if($parents){
  289. foreach($parentIds as $pid){
  290. $info = isset($parents[$pid])? $parents[$pid] : [];
  291. if(empty($info)){
  292. $logs[$pid] = ['error'=>"分红用户[{$userId}]上级用户[{$pid}]信息不存在",'orderNo'=>$orderNo,'info'=>$info];
  293. continue;
  294. }
  295. if($lastUserInfo == []){
  296. $lastUserInfo = $userInfo;
  297. }else{
  298. $lastUserInfo = isset($parents[$lastUserId])?$parents[$lastUserId] : [];
  299. $lastUserId = isset($lastUserInfo['id'])? $lastUserInfo['id'] : 0;
  300. }
  301. // 当前节点线下级直推用户
  302. if(empty($lastUserInfo)){
  303. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]直推线用户信息不存在",'orderNo'=>$orderNo,'lastUserId'=>$lastUserId,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  304. continue;
  305. }
  306. // 实际分红比例
  307. $parentProfit = isset($info['profit']) ? floatval($info['profit']) : 0; // 收益
  308. $parentPerformance = isset($info['performance']) ? floatval($info['performance']) : 0;
  309. $parentBonusRate = isset($info['bonus_rate']) ? floatval($info['bonus_rate']) : 0;
  310. $realBonusRate = max(0,round($parentBonusRate - $bonusTotalRate));
  311. if($realBonusRate <=0 || $realBonusRate >= 100){
  312. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]无实际分红比例",'orderNo'=>$orderNo,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  313. continue;
  314. }
  315. // 直推线已分红业绩
  316. $bonusTeamPerformance = isset($lastUserInfo['team_bonus_performance']) ? floatval($lastUserInfo['team_bonus_performance']) : 0;
  317. $lastPerformance = isset($lastUserInfo['performance']) ? floatval($lastUserInfo['performance']) : 0;
  318. // 直推线团队业绩
  319. $teamPerformance = TeamService::make()->getTeamPerformanceByType($lastUserId, 2, true);
  320. $teamPerformance = round(($teamPerformance + $lastPerformance) + $parentPerformance, 2);
  321. if ($teamPerformance > $bonusTeamPerformance) {
  322. $bonusTotal = $pledgeUsdt;
  323. $newBonusPerformance = $teamPerformance;
  324. if ($bonusTeamPerformance > 0) {
  325. $bonusTotal = round($teamPerformance - $bonusTeamPerformance, 2);
  326. }
  327. // 上级分红金额
  328. $bonusTotal = $bonusTotal >= $pledgeUsdt ? $pledgeUsdt : $bonusTotal;
  329. $bonusAwardUsdt = moneyFormat($bonusTotal * $realBonusRate / 100, 2);
  330. if ($bonusAwardUsdt > 0) {
  331. // 分红入账
  332. $updateData = [
  333. 'profit' => DB::raw("profit + {$bonusAwardUsdt}"),
  334. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  335. 'update_time' => time(),
  336. ];
  337. if (!MemberModel::where(['id' => $pid])->update($updateData)) {
  338. DB::rollBack();
  339. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]团队分红结算错误";
  340. return false;
  341. }
  342. // 直推用户记录当前节点上级已分红业绩
  343. $updateData = [
  344. 'team_bonus_performance' => $newBonusPerformance,
  345. 'update_time' => time(),
  346. ];
  347. if (!MemberModel::where(['id' => $lastUserId])->update($updateData)) {
  348. DB::rollBack();
  349. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]团队节点已分红更新错误";
  350. return false;
  351. }
  352. // USDT收益账单
  353. $data = [
  354. 'user_id' => $pid,
  355. 'order_no' => $orderNo,
  356. 'source_uid' => $userId,
  357. 'type' => 14,
  358. 'round' => isset($order['round']) ? $order['round'] : 0,
  359. 'user_type' => 1,
  360. 'coin_type' => 3,
  361. 'money' => $bonusAwardUsdt,
  362. 'before_money' => $parentProfit,
  363. 'create_time' => time(),
  364. 'action_ip' => get_client_ip(),
  365. 'remark' => "团队分红",
  366. 'status' => 1,
  367. 'mark' => 1,
  368. ];
  369. if (!AccountLogModel::insertGetId($data)) {
  370. DB::rollBack();
  371. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]团队分红结算失败";
  372. return false;
  373. }
  374. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]团队分红结算成功",'orderNo'=>$orderNo,'bonusUsdt'=>$bonusAwardUsdt,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  375. }
  376. }
  377. }
  378. }
  379. }
  380. DB::commit();
  381. RedisService::set("caches:pledgeOrder:team_{$userId}_{$orderNo}", ['user_id' => $userId, 'parentIds' => $parentIds, 'order' => $order, 'log' => $logs], 6 * 3600);
  382. return ['user_id' => $userId, 'pcount' => count($parentIds), 'orderNo' => $orderNo, 'money' => $pledgeUsdt];
  383. } catch (\Exception $exception) {
  384. DB::rollBack();
  385. $this->error = "用户[{$userId}]质押订单[{$orderNo}]团队分红结算错误:" . $exception->getMessage();
  386. return false;
  387. }
  388. }
  389. /**
  390. * 工作室分红
  391. * @param $userId 质押用户
  392. * @param $parentIds 推荐人ID数组
  393. * @param $order 订单信息
  394. * @return bool
  395. */
  396. public function settleRoomBonus(int $userId, array $parentIds, array $order)
  397. {
  398. $orderNo = isset($order['order_no']) ? $order['order_no'] : '';
  399. $pledgeUsdt = isset($order['money']) ? floatval($order['money']) : 0;
  400. if ($pledgeUsdt <= 0 || empty($orderNo)) {
  401. $this->error = lang(4610, ['uid' => $userId, 'order_no' => $orderNo]);
  402. return false;
  403. }
  404. // TODO 质押用户工作室分红
  405. $userInfo = MemberModel::where(['id' => $userId, 'mark' => 1])
  406. ->select(['id', 'usdt', 'performance', 'profit', 'room_bonus_rate', 'bonus_performance', 'room_bonus_performance', 'status'])
  407. ->first();
  408. $userInfo = $userInfo ? $userInfo->toArray() : [];
  409. $userProfit = isset($userInfo['profit']) ? floatval($userInfo['profit']) : 0; // 收益
  410. $userPerformance = isset($userInfo['performance']) ? floatval($userInfo['performance']) : 0;
  411. $bonusRate = isset($userInfo['room_bonus_rate']) ? floatval($userInfo['room_bonus_rate']) : 0;
  412. $bonusPerformance = isset($userInfo['room_bonus_performance']) ? floatval($userInfo['room_bonus_performance']) : 0;
  413. DB::beginTransaction();
  414. try {
  415. if ($bonusRate > 0 && $bonusRate < 100) {
  416. $teamPerformance = TeamService::make()->getTeamPerformanceByType($userId, 2, true);
  417. $teamPerformance = round($teamPerformance + $userPerformance, 2); // 用户团队业绩
  418. if ($teamPerformance > $bonusPerformance) {
  419. $bonusTotal = $pledgeUsdt;
  420. $newBonusPerformance = $teamPerformance;
  421. if ($bonusPerformance > 0) {
  422. $bonusTotal = round($teamPerformance - $bonusPerformance, 2);
  423. }
  424. // 分红金额
  425. $bonusTotal = $bonusTotal >= $pledgeUsdt ? $pledgeUsdt : $bonusTotal;
  426. $bonusAwardUsdt = moneyFormat($bonusTotal * $bonusRate / 100, 2);
  427. if ($bonusAwardUsdt > 0) {
  428. // 分红入账
  429. $updateData = [
  430. 'profit' => DB::raw("profit + {$bonusAwardUsdt}"),
  431. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  432. 'room_bonus_performance' => $newBonusPerformance,
  433. 'update_time' => time(),
  434. ];
  435. if (!MemberModel::where(['id' => $userId])->update($updateData)) {
  436. DB::rollBack();
  437. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的工作室分红结算错误";
  438. return false;
  439. }
  440. // USDT收益账单
  441. $data = [
  442. 'user_id' => $userId,
  443. 'order_no' => $orderNo,
  444. 'type' => 16,
  445. 'round' => isset($order['round']) ? $order['round'] : 0,
  446. 'user_type' => 1,
  447. 'coin_type' => 3,
  448. 'money' => $bonusAwardUsdt,
  449. 'before_money' => $userProfit,
  450. 'create_time' => time(),
  451. 'action_ip' => get_client_ip(),
  452. 'remark' => "工作室分红",
  453. 'status' => 1,
  454. 'mark' => 1,
  455. ];
  456. if (!AccountLogModel::insertGetId($data)) {
  457. DB::rollBack();
  458. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的工作室分红结算失败";
  459. return false;
  460. }
  461. }
  462. }
  463. }
  464. // TODO 上级分红结算
  465. $logs = [];
  466. if($parentIds){
  467. $bonusTotalRate = $bonusRate;
  468. $parents = MemberModel::whereIn('id', $parentIds)
  469. ->where(['mark' => 1])
  470. ->select(['id', 'usdt', 'performance', 'profit', 'room_bonus_rate', 'room_bonus_performance', 'status'])
  471. ->orderByRaw('FIELD(id, "' . implode(',', $parentIds) . '")')
  472. ->get()
  473. ->keyBy('id');
  474. $parents = $parents? $parents->toArray() : [];
  475. if($parents){
  476. foreach($parentIds as $pid){
  477. $info = isset($parents[$pid])? $parents[$pid] : [];
  478. if(empty($info)){
  479. $logs[$pid] = ['error'=>"工作室分红用户[{$userId}]上级用户[{$pid}]信息不存在",'orderNo'=>$orderNo,'info'=>$info];
  480. continue;
  481. }
  482. // 实际分红比例
  483. $parentProfit = isset($info['profit']) ? floatval($info['profit']) : 0; // 收益
  484. $parentPerformance = isset($info['performance']) ? floatval($info['performance']) : 0;
  485. $parentBonusRate = isset($info['room_bonus_rate']) ? floatval($info['room_bonus_rate']) : 0;
  486. $bonusTeamPerformance = isset($info['room_bonus_performance']) ? floatval($info['room_bonus_performance']) : 0;
  487. $realBonusRate = max(0,round($parentBonusRate - $bonusTotalRate));
  488. if($realBonusRate <=0 || $realBonusRate >= 100){
  489. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]无实际工作室分红比例",'orderNo'=>$orderNo,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  490. continue;
  491. }
  492. // 当前团队业绩
  493. $teamPerformance = TeamService::make()->getTeamPerformanceByType($pid, 2, true);
  494. $teamPerformance = round($teamPerformance + $parentPerformance, 2);
  495. if ($teamPerformance > $bonusTeamPerformance) {
  496. $bonusTotal = $pledgeUsdt;
  497. $newBonusPerformance = $teamPerformance;
  498. if ($bonusTeamPerformance > 0) {
  499. $bonusTotal = round($teamPerformance - $bonusTeamPerformance, 2);
  500. }
  501. // 上级工作室分红金额
  502. $bonusTotal = $bonusTotal >= $pledgeUsdt ? $pledgeUsdt : $bonusTotal;
  503. $bonusAwardUsdt = moneyFormat($bonusTotal * $realBonusRate / 100, 2);
  504. if ($bonusAwardUsdt > 0) {
  505. // 工作室分红入账
  506. $updateData = [
  507. 'profit' => DB::raw("profit + {$bonusAwardUsdt}"),
  508. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  509. 'room_bonus_performance' => $newBonusPerformance,
  510. 'update_time' => time(),
  511. ];
  512. if (!MemberModel::where(['id' => $pid])->update($updateData)) {
  513. DB::rollBack();
  514. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]工作室分红结算错误";
  515. return false;
  516. }
  517. // USDT收益账单
  518. $data = [
  519. 'user_id' => $pid,
  520. 'order_no' => $orderNo,
  521. 'source_uid' => $userId,
  522. 'type' => 16,
  523. 'round' => isset($order['round']) ? $order['round'] : 0,
  524. 'user_type' => 1,
  525. 'coin_type' => 3,
  526. 'money' => $bonusAwardUsdt,
  527. 'before_money' => $parentProfit,
  528. 'create_time' => time(),
  529. 'action_ip' => get_client_ip(),
  530. 'remark' => "工作室分红",
  531. 'status' => 1,
  532. 'mark' => 1,
  533. ];
  534. if (!AccountLogModel::insertGetId($data)) {
  535. DB::rollBack();
  536. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的上级[{$pid}]工作室分红结算失败";
  537. return false;
  538. }
  539. $logs[$pid] = ['error'=>"质押用户[{$userId}]上级用户[{$pid}]工作室分红结算成功",'orderNo'=>$orderNo,'bonusUsdt'=>$bonusAwardUsdt,'bonusRateTotal'=>$bonusTotalRate,'info'=>$info];
  540. }
  541. }
  542. }
  543. }
  544. }
  545. DB::commit();
  546. RedisService::set("caches:pledgeOrder:room_{$userId}_{$orderNo}", ['user_id' => $userId, 'parentIds' => $parentIds, 'order' => $order, 'log' => $logs], 6 * 3600);
  547. return ['user_id' => $userId, 'pcount' => count($parentIds), 'orderNo' => $orderNo, 'money' => $pledgeUsdt];
  548. } catch (\Exception $exception) {
  549. DB::rollBack();
  550. $this->error = "用户[{$userId}]质押订单[{$orderNo}]工作室分红结算错误:" . $exception->getMessage();
  551. return false;
  552. }
  553. }
  554. /**
  555. * 管理奖、平级奖结算
  556. * @param $userId 质押用户
  557. * @param $order
  558. * @return bool
  559. */
  560. public function settleManageAndLevelAward($userId, $parentIds, $order)
  561. {
  562. $orderNo = isset($order['order_no']) ? $order['order_no'] : '';
  563. $pledgeUsdt = isset($order['money']) ? floatval($order['money']) : 0;
  564. if ($pledgeUsdt <= 0 || empty($orderNo)) {
  565. $this->error = lang(4512, ['uid' => $userId, 'order_no' => $orderNo]);
  566. return false;
  567. }
  568. $parentIds = get_parents($parentIds);
  569. if (empty($parentIds)) {
  570. $this->error = lang(4513, ['uid' => $userId, 'order_no' => $orderNo]);
  571. return true;
  572. }
  573. // SBT价格
  574. $sbtPrice = PriceLogService::make()->getSbtPrice();
  575. // USDT收益分配参数
  576. $settleUsdtRate = ConfigService::make()->getConfigByCode('bonus_usdt_rate', 80);
  577. $settleUsdtRate = $settleUsdtRate > 0 && $settleUsdtRate <= 100 ? $settleUsdtRate : 80;
  578. $memberInfo = MemberModel::from('member as a')
  579. ->leftJoin('member_level as b', 'b.id', '=', 'a.member_level')
  580. ->where(['a.id' => $userId, 'a.mark' => 1])
  581. ->select(['a.id','a.member_level','a.usdt','a.profit','a.status','b.bonus_rate'])
  582. ->first();
  583. $memberInfo = $memberInfo ? $memberInfo->toArray() : [];
  584. $userBonusRate = isset($userInfo['bonus_rate'])? $userInfo['bonus_rate'] : 0;
  585. if(empty($memberInfo)){
  586. $this->error = lang(4413, ['uid' => $userId, 'order_no' => $orderNo]);
  587. return false;
  588. }
  589. // 结算处理
  590. try {
  591. // TODO 管理奖结算
  592. $count = 0;
  593. $bonusRateTotal = $userBonusRate;
  594. $bonusTotal = 0;
  595. $logs = [];
  596. $pjUsers = [];
  597. DB::beginTransaction();
  598. foreach ($parentIds as $pid) {
  599. $userInfo = MemberModel::from('member as a')
  600. ->leftJoin('member_level as b', 'b.id', '=', 'a.member_level')
  601. ->where(['a.id' => $pid, 'a.mark' => 1])
  602. ->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'])
  603. ->first();
  604. $userInfo = $userInfo ? $userInfo->toArray() : [];
  605. $userProfit = isset($userInfo['profit']) ? $userInfo['profit'] : 0;
  606. $userUsdt = isset($userInfo['usdt']) ? $userInfo['usdt'] : 0;
  607. $userSbt = isset($userInfo['sbt']) ? $userInfo['sbt'] : 0;
  608. $level = isset($userInfo['member_level']) ? intval($userInfo['member_level']) : 0;
  609. if (empty($userInfo) || $level <= 0) {
  610. $logs[$pid] = ['error' => "上级推荐人[{$pid}]无等级,无管理奖", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt];
  611. continue;
  612. }
  613. $bonusRate = isset($userInfo['bonus_rate']) && $userInfo['bonus_rate'] ? floatval($userInfo['bonus_rate']) : 0;
  614. $weightingRate = isset($userInfo['bonus_rate']) && $userInfo['weighting_rate'] ? floatval($userInfo['weighting_rate']) : 0;
  615. $realBonusRate = max(0, round($bonusRate - $bonusRateTotal, 2)); // 级差佣金比例,减去已分的部分
  616. $awardUsdt = moneyFormat($pledgeUsdt * $realBonusRate / 100, 2);
  617. $bonusAwardUsdt = $awardUsdt;
  618. // 全网加权收益
  619. $teamPerformance = 0;
  620. $globalPledgeTotal = 0;
  621. $weightingAwardUsdt = 0;
  622. $globalLevelTeamPerformance = 0;
  623. if ($weightingRate > 0 && $weightingRate < 100) {
  624. // 全网流水(质押中)
  625. $globalPledgeTotal = PledgeOrderService::make()->getGlobalTotal();
  626. if ($globalPledgeTotal > 0) {
  627. // 该等级全网业绩
  628. $globalLevelTeamPerformance = TeamService::make()->getTeamPerformanceByLevel($level);
  629. // 该用户团队业绩
  630. $teamPerformance = TeamService::make()->getTeamPerformanceByType($pid, 2, true);
  631. // 等级加权比例
  632. $levelWeightingRate = round($teamPerformance/$globalLevelTeamPerformance, 2);
  633. // 实际收益 = 基础收益 + 加权收益(3%全网流水 x 加权比例)
  634. $weightingAwardUsdt = moneyFormat($globalPledgeTotal * ($weightingRate / 100) * $levelWeightingRate, 2);
  635. $bonusAwardUsdt = moneyFormat($weightingAwardUsdt + $bonusAwardUsdt, 2);
  636. }
  637. }
  638. if ($bonusRate <= 0 || $realBonusRate <= 0 || $bonusAwardUsdt <= 0) {
  639. $logs[$pid] = ['error' => "上级推荐人[{$pid}]无管理奖收益", 'rate' => $bonusRate, 'realRate' => $realBonusRate, 'bonusUsdt' => $bonusAwardUsdt, 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt];
  640. continue;
  641. }
  642. // 结算划分
  643. $bonusRateTotal += $realBonusRate;
  644. $bonusUsdt = moneyFormat($bonusAwardUsdt * $settleUsdtRate / 100, 2); // 划分到USDT部分
  645. $bonusSbt = moneyFormat(($bonusAwardUsdt - $bonusUsdt) / $sbtPrice, 2); // 划分到SBT金本位部分
  646. if ($bonusUsdt <= 0 && $bonusSbt <= 0) {
  647. DB::rollBack();
  648. $logs[$pid] = ['error' => "上级推荐人[{$pid}]管理奖划分计算错误", 'rate' => $bonusRate, 'usdtRate' => $settleUsdtRate, 'sbtPrice' => $sbtPrice, 'realRate' => $realBonusRate, 'bonusUsdt' => $bonusAwardUsdt, 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt];
  649. $this->error = "上级推荐人[{$pid}]管理奖划分计算错误";
  650. return false;
  651. }
  652. // 验证是否结算过
  653. if ($logId = AccountLogModel::where(['user_id' => $pid, 'order_no' => $orderNo, 'type' => 9, 'status' => 1, 'mark' => 1])->value('id')) {
  654. $logs[$pid] = ['error' => "上级推荐人[{$pid}]管理奖已结算过", 'logId' => $logId, 'rate' => $bonusRate, 'realRate' => $realBonusRate, 'bonusUsdt' => $bonusAwardUsdt, 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt];
  655. continue;
  656. }
  657. // 结算入账
  658. $updateData = [
  659. 'profit' => DB::raw("profit + {$bonusUsdt}"),
  660. 'profit_total' => DB::raw("profit_total + {$bonusAwardUsdt}"),
  661. 'manage_profit' => DB::raw("manage_profit + {$bonusAwardUsdt}"),
  662. 'sbt' => DB::raw("sbt + {$bonusSbt}"),
  663. 'update_time' => time(),
  664. ];
  665. if (!MemberModel::where(['id' => $pid])->update($updateData)) {
  666. DB::rollBack();
  667. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的推荐人[{$pid}]管理奖收益[{$bonusAwardUsdt}]结算错误";
  668. return false;
  669. }
  670. // USDT收益账单
  671. $data = [
  672. 'user_id' => $pid,
  673. 'order_no' => $orderNo,
  674. 'source_uid' => $userId,
  675. 'type' => 9,
  676. 'round' => isset($order['round']) ? $order['round'] : 0,
  677. 'user_type' => 1,
  678. 'coin_type' => 3,
  679. 'money' => $bonusUsdt,
  680. 'before_money' => $userProfit,
  681. 'create_time' => time(),
  682. 'action_ip' => get_client_ip(),
  683. 'remark' => "S{$level}管理奖{$realBonusRate}%的{$settleUsdtRate}%",
  684. 'status' => 1,
  685. 'mark' => 1,
  686. ];
  687. // 有全网加权
  688. if($globalLevelTeamPerformance && $weightingAwardUsdt){
  689. $data['settle_remark'] = "S{$level}全网[{$globalLevelTeamPerformance}]团队业绩[{$teamPerformance}]全网流水[{$globalPledgeTotal}]加权收益[{$weightingAwardUsdt}]";
  690. }
  691. if (!AccountLogModel::insertGetId($data)) {
  692. DB::rollBack();
  693. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的推荐人[{$pid}]管理奖[{$bonusAwardUsdt}]结算错误";
  694. return false;
  695. }
  696. // SBT收益账单
  697. if ($bonusSbt > 0) {
  698. $data = [
  699. 'user_id' => $pid,
  700. 'order_no' => $orderNo,
  701. 'source_uid' => $userId,
  702. 'type' => 9,
  703. 'round' => isset($order['round']) ? $order['round'] : 0,
  704. 'user_type' => 1,
  705. 'coin_type' => 2,
  706. 'money' => $bonusSbt,
  707. 'before_money' => $userSbt,
  708. 'create_time' => time(),
  709. 'action_ip' => get_client_ip(),
  710. 'remark' => "S{$level}管理奖{$realBonusRate}%的" . (100 - $settleUsdtRate) . '%',
  711. 'status' => 1,
  712. 'mark' => 1,
  713. ];
  714. if (!AccountLogModel::insertGetId($data)) {
  715. DB::rollBack();
  716. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的推荐人[{$pid}]管理奖[{$bonusAwardUsdt}]结算错误";
  717. return false;
  718. }
  719. }
  720. // S1等级及以上,平级奖数据(若有上级)
  721. $parentIds = isset($userInfo['parent_ids']) ? $userInfo['parent_ids'] : '';
  722. $parentIds = get_parents($parentIds);
  723. if ($parentIds && $level >= 1) {
  724. $pjUsers[$pid] = [
  725. 'level' => $level, // 管理奖用户等级
  726. 'usdt' => $bonusAwardUsdt, // 管理奖USDT金额
  727. 'parent_ids' => $parentIds, // 管理奖用户上级推荐人数组
  728. ];
  729. }
  730. $logs[$pid] = ['error' => "推荐人[{$pid}]管理奖结算成功", 'orderNo' => $orderNo, 'uid' => $userId, 'profit' => $userProfit, 'bonusAwardUsdt' => $bonusAwardUsdt, 'awardUsdt' => $awardUsdt, 'pledgeUsdt' => $pledgeUsdt];
  731. $bonusTotal += $bonusAwardUsdt;
  732. $count++;
  733. }
  734. // TODO 平级奖结算
  735. $pjLogs = [];
  736. $pjCount = 0;
  737. $pjTotal = 0;
  738. $pjProfitRate = ConfigService::make()->getConfigByCode('pj_profit_rate', 0);
  739. $pjProfitRate = $pjProfitRate > 0 && $pjProfitRate < 100 ? $pjProfitRate : 0;
  740. if ($pjUsers && $pjProfitRate > 0) {
  741. foreach ($pjUsers as $uid => $item) {
  742. $pjLevel = isset($item['level']) ? $item['level'] : 0;
  743. $bonusUsdt = isset($item['usdt']) ? $item['usdt'] : 0;
  744. $pjParentIds = isset($item['parent_ids']) ? $item['parent_ids'] : [];
  745. if ($pjLevel <= 0 || empty($pjParentIds) || $bonusUsdt <= 0) {
  746. DB::rollBack();
  747. $pjLogs[$uid] = ['error' => "管理奖用户[{$uid}]平级奖参数错误", 'log' => $item];
  748. $this->error = "管理奖用户[{$uid}]平级奖参数错误[{$pjLevel}-{$bonusUsdt}]";
  749. return false;
  750. }
  751. // 平级奖金额
  752. $pjAwardUsdt = moneyFormat($bonusUsdt * $pjProfitRate / 100, 2);
  753. if ($pjAwardUsdt <= 0) {
  754. $pjLogs[$uid] = ['error' => "管理奖用户[{$uid}]无平级收益结算", 'rate' => $pjProfitRate, 'log' => $item];
  755. continue;
  756. }
  757. // 是否有平级推荐人
  758. $pjInfo = TeamService::make()->getUpperLevelUser($level, $pjParentIds);
  759. $pjUserId = isset($pjInfo['id']) ? $pjInfo['id'] : 0;
  760. $pjUserProfit = isset($pjInfo['profit']) ? $pjInfo['profit'] : 0;
  761. if (empty($pjInfo) || $pjUserId <= 0) {
  762. $pjLogs[$uid] = ['error' => "管理奖用户[{$uid}]无平级上级", 'profit' => $pjAwardUsdt, 'log' => $item];
  763. continue;
  764. }
  765. // 平级奖结算到账
  766. $updateData = [
  767. 'profit' => DB::raw("profit + {$pjAwardUsdt}"),
  768. 'profit_total' => DB::raw("profit_total + {$pjAwardUsdt}"),
  769. 'pj_profit' => DB::raw("pj_profit + {$pjAwardUsdt}"),
  770. 'update_time' => time()
  771. ];
  772. if (!MemberModel::where(['id' => $pjUserId])->update($updateData)) {
  773. DB::rollBack();
  774. $pjLogs[$uid] = ['error' => "管理奖用户[{$uid}]上级[{$pjUserId}]平级奖结算失败", 'profit' => $pjAwardUsdt, 'log' => $item];
  775. $this->error = "管理奖用户[{$uid}]上级[{$pjUserId}]平级奖结算失败";
  776. return false;
  777. }
  778. // USDT收益账单
  779. $data = [
  780. 'user_id' => $pjUserId,
  781. 'order_no' => $orderNo,
  782. 'source_uid' => $uid,
  783. 'type' => 10,
  784. 'round' => isset($order['round']) ? $order['round'] : 0,
  785. 'user_type' => 1,
  786. 'coin_type' => 3,
  787. 'money' => $pjAwardUsdt,
  788. 'before_money' => $pjUserProfit,
  789. 'create_time' => time(),
  790. 'action_ip' => get_client_ip(),
  791. 'remark' => "S{$pjLevel}平级奖励",
  792. 'status' => 1,
  793. 'mark' => 1,
  794. ];
  795. if (!AccountLogModel::insertGetId($data)) {
  796. DB::rollBack();
  797. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的管理奖用户[{$uid}]平级奖用户[{$pjUserId}]的平级奖[{$pjAwardUsdt}]结算错误";
  798. return false;
  799. }
  800. }
  801. }
  802. DB::commit();
  803. RedisService::set("caches:pledgeOrder:share_{$userId}_{$orderNo}", ['user_id' => $userId, 'parentIds' => $parentIds, 'order' => $order, 'log' => $logs, 'pjRate' => $pjProfitRate, 'pjLogs' => $pjLogs], 6 * 3600);
  804. return ['user_id' => $userId, 'bonusTotal' => $bonusTotal, 'count' => $count, 'all' => count($parentIds), 'pjTotal' => $pjTotal, 'pjCount' => $pjCount, 'pjAll' => count($pjUsers), 'orderNo' => $orderNo, 'money' => $pledgeUsdt];
  805. } catch (\Exception $exception) {
  806. DB::rollBack();
  807. $this->error = "用户[{$userId}]质押订单[{$orderNo}]管理奖和平级奖结算错误:" . $exception->getMessage();
  808. return false;
  809. }
  810. }
  811. /**
  812. * 推荐收益奖励结算
  813. * @param $userId 质押订单用户
  814. * @param $parentIds 质押订单用户上级推荐人ID串
  815. * @param $order 结算质押订单:order_no-订单号,money-质押金额,round-质押轮次
  816. * @return array|bool
  817. */
  818. public function settleShareAward($userId, $parentIds, $order)
  819. {
  820. $orderNo = isset($order['order_no']) ? $order['order_no'] : '';
  821. $pledgeUsdt = isset($order['money']) ? floatval($order['money']) : 0;
  822. if ($pledgeUsdt <= 0 || empty($orderNo)) {
  823. $this->error = lang(4510, ['uid' => $userId, 'order_no' => $orderNo]);
  824. return false;
  825. }
  826. $parentIds = get_parents($parentIds);
  827. $parentIds = array_slice($parentIds, 0, 10);
  828. if (empty($parentIds)) {
  829. $this->error = lang(4511, ['uid' => $userId, 'order_no' => $orderNo]);
  830. return true;
  831. }
  832. // 推荐奖励参数
  833. try {
  834. $count = 0;
  835. $logs = [];
  836. $shareRates = InstitutionalService::make()->getAwardByFloor();
  837. DB::beginTransaction();
  838. foreach ($parentIds as $key => $pid) {
  839. $floor = $key + 1;
  840. $rateInfo = isset($shareRates[$floor]) ? $shareRates[$floor] : [];
  841. $rate = isset($rateInfo['value']) && $rateInfo['value'] > 0 && $rateInfo['value'] < 100 ? floatval($rateInfo['value']) : 0;
  842. if (empty($rateInfo) || $rate <= 0) {
  843. $logs[$pid] = ['error' => "该层推荐人[{$pid}]推荐收益比例参数未配置", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor];
  844. continue;
  845. }
  846. $shareAwardUsdt = moneyFormat($pledgeUsdt * $rate / 100, 2);
  847. if ($shareAwardUsdt <= 0) {
  848. $logs[$pid] = ['error' => "该层推荐人[{$pid}]推荐无推荐收益", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor, 'rate' => $rate];
  849. continue;
  850. }
  851. $userInfo = MemberModel::where(['id' => $pid, 'mark' => 1])
  852. ->select(['id', 'usdt', 'profit', 'sbt', 'profit_total', 'status'])
  853. ->first();
  854. $userInfo = $userInfo ? $userInfo->toArray() : [];
  855. $userProfit = isset($userInfo['profit']) ? $userInfo['profit'] : 0;
  856. if (empty($userInfo) || $pid <= 0) {
  857. $logs[$pid] = ['error' => "该推荐人[{$pid}]信息不存在", 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor, 'rate' => $rate];
  858. continue;
  859. }
  860. // 验证是否结算过
  861. if ($logId = AccountLogModel::where(['user_id' => $pid, 'order_no' => $orderNo, 'type' => 8, 'status' => 1, 'mark' => 1])->value('id')) {
  862. $logs[$pid] = ['error' => "该层推荐人[{$pid}]推荐奖励已结算过", 'logId' => $logId, 'orderNo' => $orderNo, 'uid' => $userId, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor, 'rate' => $rate];
  863. continue;
  864. }
  865. // 结算入账
  866. $updateData = [
  867. 'profit' => DB::raw("profit + {$shareAwardUsdt}"),
  868. 'profit_total' => DB::raw("profit_total + {$shareAwardUsdt}"),
  869. 'share_profit' => DB::raw("share_profit + {$shareAwardUsdt}"),
  870. 'update_time' => time(),
  871. ];
  872. if (!MemberModel::where(['id' => $pid])->update($updateData)) {
  873. DB::rollBack();
  874. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的[{$floor}]层推荐人[{$pid}]推荐收益[{$shareAwardUsdt}]结算错误";
  875. return false;
  876. }
  877. // 收益账单
  878. $data = [
  879. 'user_id' => $pid,
  880. 'order_no' => $orderNo,
  881. 'source_uid' => $userId,
  882. 'type' => 8,
  883. 'round' => isset($order['round']) ? $order['round'] : 0,
  884. 'user_type' => 1,
  885. 'coin_type' => 3,
  886. 'money' => $shareAwardUsdt,
  887. 'before_money' => $userProfit,
  888. 'create_time' => time(),
  889. 'action_ip' => get_client_ip(),
  890. 'remark' => "{$floor}层推荐奖励",
  891. 'status' => 1,
  892. 'mark' => 1,
  893. ];
  894. if (!AccountLogModel::insertGetId($data)) {
  895. DB::rollBack();
  896. $this->error = "用户[{$userId}]质押订单[{$orderNo}]的[{$floor}]层推荐人[{$pid}]推荐收益[{$shareAwardUsdt}]结算错误";
  897. return false;
  898. }
  899. $logs[$pid] = ['error' => "推荐人[{$pid}]奖励结算成功", 'orderNo' => $orderNo, 'uid' => $userId, 'profit' => $userProfit, 'awardUsdt' => $shareAwardUsdt, 'pledgeUsdt' => $pledgeUsdt, 'floor' => $floor, 'rate' => $rate];
  900. $count++;
  901. }
  902. DB::commit();
  903. RedisService::set("caches:pledgeOrder:share_{$userId}_{$orderNo}", ['user_id' => $userId, 'parentIds' => $parentIds, 'order' => $order, 'log' => $logs], 6 * 3600);
  904. return ['user_id' => $userId, 'count' => $count, 'all' => count($parentIds), 'orderNo' => $orderNo, 'money' => $pledgeUsdt];
  905. } catch (\Exception $exception) {
  906. DB::rollBack();
  907. $this->error = "用户[{$userId}]质押订单[{$orderNo}]推荐收益结算错误:" . $exception->getMessage();
  908. return false;
  909. }
  910. }
  911. /**
  912. * 开发者维护收益结算
  913. * @param $developerId 开发者ID
  914. * @param $walletUrl 开发者地址
  915. * @return false
  916. */
  917. public function developerSettle($developerId, $walletUrl)
  918. {
  919. if(empty($developerId) || empty($walletUrl)){
  920. $this->error = 2014;
  921. return false;
  922. }
  923. $info = DeveloperModel::from('developer as a')
  924. ->leftJoin('member as b','b.wallet_url','=','a.wallet_url')
  925. ->where(['a.id'=> $developerId])
  926. ->select(['a.*','b.id as user_id','b.profit'])
  927. ->first();
  928. $info = $info? $info->toArray() : [];
  929. $userId = isset($info['user_id'])? intval($info['user_id']) : 0;
  930. $userProfit = isset($info['profit'])? floatval($info['profit']) : 0;
  931. if(empty($info) || $userId<=0){
  932. $this->error = 2001;
  933. return false;
  934. }
  935. $lastSettleAt = isset($info['last_settle_at'])? $info['last_settle_at'] : '';
  936. if($lastSettleAt && $lastSettleAt >= date('Y-m-d', time() - 7 * 86400)){
  937. $this->error = 1044;
  938. return false;
  939. }
  940. $cacheKey = "caches:developer:lock_{$developerId}";
  941. if(RedisService::get($cacheKey)){
  942. $this->error = 1044;
  943. return false;
  944. }
  945. DB::beginTransaction();
  946. try {
  947. // 全网流水
  948. $pledgeTotal = PledgeOrderService::make()->getGlobalTotal();
  949. // 结算比例
  950. $settleRate = ConfigService::make()->getConfigByCode('developer_settle_rate', 3);
  951. $settleRate = $settleRate>0 && $settleRate <= 20? $settleRate : 3;
  952. $settleMoney = round($pledgeTotal * $settleRate/100, 2);
  953. if($settleMoney>0){
  954. $updateData = [
  955. 'profit'=> DB::raw("profit + {$settleMoney}"),
  956. 'profit_total'=> DB::raw("profit_total + {$settleMoney}"),
  957. 'update_time'=>time()
  958. ];
  959. if(!MemberModel::where(['id'=> $userId])->update($updateData)){
  960. DB::rollBack();
  961. $this->error = 1045;
  962. return false;
  963. }
  964. $updateData = [
  965. 'total'=> DB::raw("total + {$settleMoney}"),
  966. 'last_settle_at'=>date('Y-m-d H:i:s'),
  967. 'update_time'=>time()
  968. ];
  969. if(!DeveloperModel::where(['id'=> $developerId])->update($updateData)){
  970. DB::rollBack();
  971. $this->error = 1045;
  972. return false;
  973. }
  974. // 收益账单
  975. $data = [
  976. 'user_id' => $userId,
  977. 'order_no' => get_order_num('DP'),
  978. 'source_uid' => 0,
  979. 'type' => 99,
  980. 'user_type' => 1,
  981. 'coin_type' => 3,
  982. 'money' => $settleMoney,
  983. 'before_money' => $userProfit,
  984. 'create_time' => time(),
  985. 'action_ip' => get_client_ip(),
  986. 'remark' => "工作室分红",
  987. 'status' => 1,
  988. 'mark' => 1,
  989. ];
  990. if (!AccountLogModel::insertGetId($data)) {
  991. DB::rollBack();
  992. $this->error = 1045;
  993. return false;
  994. }
  995. }
  996. DB::commit();
  997. $this->error = 1046;
  998. return ['id'=>$developerId,'user_id'=>$userId,'money'=>$settleMoney];
  999. }catch (\Exception $exception){
  1000. $this->error = $exception->getMessage();
  1001. return false;
  1002. }
  1003. }
  1004. }