PledgeOrderService.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746
  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\MemberModel;
  14. use App\Models\PledgeOrderModel;
  15. use App\Services\BaseService;
  16. use App\Services\ConfigService;
  17. use App\Services\RedisService;
  18. use Illuminate\Support\Facades\DB;
  19. /**
  20. * 租赁订单-服务类
  21. * @author laravel开发员
  22. * @since 2020/11/11
  23. * @package App\Services\Api
  24. */
  25. class PledgeOrderService extends BaseService
  26. {
  27. protected static $instance;
  28. /**
  29. * 构造函数
  30. * @author laravel开发员
  31. * @since 2020/11/11
  32. */
  33. public function __construct()
  34. {
  35. $this->model = new PledgeOrderModel();
  36. }
  37. /**
  38. * 静态入口
  39. */
  40. public static function make()
  41. {
  42. if (!self::$instance) {
  43. self::$instance = new static();
  44. }
  45. return self::$instance;
  46. }
  47. /**
  48. * 质押订单统计
  49. * @param $userId 用户ID
  50. * @return array|mixed
  51. */
  52. public function getCountByUser($userId)
  53. {
  54. $cacheKey = "caches:pledge:counts_{$userId}";
  55. $counts = RedisService::get($cacheKey);
  56. if($counts){
  57. $expiredTime = isset($counts['expired_time'])? intval($counts['expired_time']) : 0;
  58. $settleTime = isset($counts['settle_time'])? intval($counts['settle_time']) : 0;
  59. $counts['pledge_end_time'] = $expiredTime>time()? intval($expiredTime - time()) : 0;
  60. $counts['pledge_settle_time'] = $settleTime>time()? intval($settleTime - time()) : 0;
  61. $counts['pledge_end_text'] = timeFormat($counts['pledge_end_time']);
  62. $counts['pledge_settle_data'] = timeFormat($counts['pledge_settle_time'],2);
  63. return $counts;
  64. }
  65. $counts = [
  66. "pledge_count" => 0,
  67. "pledge_total" => "0.00",
  68. "pledge_money" => "0.00",
  69. "pledge_profit" => "0.00",
  70. "pledge_end_time" => 0,
  71. "pledge_settle_time" => 0,
  72. "pledge_ent_text" => '00:00:00',
  73. "pledge_settle_data" => [
  74. 'hour'=> '00',
  75. 'minute'=> '00',
  76. 'second'=> '00',
  77. ],
  78. ];
  79. if($userId){
  80. $counts['pledge_count'] = (int)$this->model->where(['user_id'=> $userId,'mark'=>1])->count('id');
  81. $counts['pledge_total'] = moneyFormat($this->model->where(['user_id'=> $userId,'mark'=>1])->sum('money'),2);
  82. $info = $this->model->where(['user_id'=> $userId,'status'=>1,'mark'=>1])->orderBy('create_time','desc')->orderBy('id','desc')->first();
  83. $info = $info? $info->toArray() : [];
  84. if($info){
  85. $counts['pledge_money'] = moneyFormat($info['money'],2);
  86. $counts['pledge_profit'] = moneyFormat($info['profit'], 2);
  87. $expiredTime = $info['expired_at']? strtotime($info['expired_at']) : 0;
  88. $settleTime = $info['settle_at']? strtotime($info['settle_at']) : 0;
  89. $counts['expired_time'] = $expiredTime;
  90. $counts['settle_time'] = $settleTime;
  91. $counts['pledge_end_time'] = $expiredTime>time()? intval($expiredTime - time()) : 0;
  92. $counts['pledge_settle_time'] = $settleTime>time()? intval($settleTime - time()) : 0;
  93. $counts['pledge_end_text'] = timeFormat($counts['pledge_end_time']);
  94. $counts['pledge_settle_data'] = timeFormat($counts['pledge_settle_time'],2);
  95. }
  96. }
  97. RedisService::set($cacheKey, $counts, rand(10, 30));
  98. return $counts;
  99. }
  100. /**
  101. * 检测是否质押过且超过了24小时退本(即是否有退本过)
  102. * @param $userId 质押用户ID
  103. * @return array|false|mixed
  104. */
  105. public function checkPledgeRefund($userId)
  106. {
  107. $cacheKey = "caches:pledge:checkRefund_{$userId}";
  108. $data = RedisService::get($cacheKey);
  109. if($data || RedisService::exists($cacheKey)){
  110. return $data;
  111. }
  112. $data = $this->model->where(['user_id'=> $userId,'status'=> 2,'mark'=>1])->value('id');
  113. if($data){
  114. RedisService::set($cacheKey, $data, rand(3600,7200));
  115. }
  116. return $data;
  117. }
  118. /**
  119. * 检测是否质押过
  120. * @param $userId 质押用户ID
  121. * @return array|false|mixed
  122. */
  123. public function checkPledge($userId)
  124. {
  125. $cacheKey = "caches:pledge:check_exists_{$userId}";
  126. $data = RedisService::get($cacheKey);
  127. if($data || RedisService::exists($cacheKey)){
  128. return $data;
  129. }
  130. $data = $this->model->where(['user_id'=> $userId,'mark'=>1])->value('id');
  131. if($data){
  132. RedisService::set($cacheKey, $data, 600);
  133. }
  134. return $data;
  135. }
  136. /**
  137. * 验证质押订单状态
  138. * @param $userId 质押用户
  139. * @return array|mixed
  140. */
  141. public function checHasPledge($userId)
  142. {
  143. $cacheKey = "caches:pledge:check_{$userId}";
  144. $data = RedisService::get($cacheKey);
  145. if($data){
  146. return $data;
  147. }
  148. $data = $this->model->where(['user_id'=> $userId,'mark'=>1])
  149. ->where(function($query){
  150. $query->where('expired_at','>', date('Y-m-d H:i:s'))
  151. ->orWhere(['status'=>1]);
  152. })
  153. ->select(['id','user_id','order_no','money','expired_at','status'])
  154. ->first();
  155. $data = $data? $data->toArray() :[];
  156. if($data){
  157. RedisService::set($cacheKey, $data, rand(300, 600));
  158. }
  159. return $data;
  160. }
  161. /**
  162. * 自动质押处理
  163. * @param $userInfo 用户信息:id-用户ID,pledge_auto-质押状态,usdt-USDT余额,sbt-sbt余额,status-状态,parent_ids-上级团队ID(升级用)
  164. * @return bool
  165. */
  166. public function autoMakeOrder($userInfo)
  167. {
  168. $userId = isset($userInfo['id'])? intval($userInfo['id']) : 0;
  169. $pledgeAuto = isset($userInfo['pledge_auto'])? intval($userInfo['pledge_auto']) : 0;
  170. $userUsdt = isset($userInfo['usdt'])? floatval($userInfo['usdt']) : 0;
  171. $userSbt = isset($userInfo['sbt'])? floatval($userInfo['sbt']) : 0;
  172. $tradeStatus = isset($userInfo['trade_status'])? intval($userInfo['trade_status']) : 0;
  173. $userStatus = isset($userInfo['status'])? intval($userInfo['status']) : 0;
  174. $parentIds = isset($userInfo['parent_ids'])? trim($userInfo['parent_ids']) : '';
  175. $settleStatus = isset($userInfo['settle_status'])? intval($userInfo['settle_status']) : 0;
  176. $settleStatus = $settleStatus==2? 2 : 1; // 1-正常结算,2-不结算动态和个人业绩
  177. if($userId<=0 || $userUsdt<=0 || $userStatus != 1){
  178. $this->error = lang(4101,['uid'=>$userId,'usdt'=>$userUsdt,'status'=>$userStatus]);
  179. return false;
  180. }
  181. // 检测是否已经开启了自动质押
  182. if($pledgeAuto != 1 || $tradeStatus == 2){
  183. $this->error = 4104;
  184. return false;
  185. }
  186. // 质押所需USDT验证
  187. $pledgeUsdt = ConfigService::make()->getConfigByCode('pledge_money', 500);
  188. $pledgeUsdt = $pledgeUsdt>0?$pledgeUsdt: 500;
  189. if($userUsdt < $pledgeUsdt){
  190. $this->error = lang(4102,['usdt'=> $userUsdt,'money'=>$pledgeUsdt]);
  191. return false;
  192. }
  193. // SBT 手续费
  194. $sbtPrice = PriceLogService::make()->getSbtPrice();
  195. $sbtUsdtFee = ConfigService::make()->getConfigByCode('pledge_sbt_usdt_fee', 0);
  196. $sbtUsdtFee = $sbtUsdtFee>0? floatval($sbtUsdtFee): 0;
  197. $sbtFee = $sbtUsdtFee && $sbtPrice>0? moneyFormat($sbtUsdtFee / $sbtPrice,2) : 0;
  198. if($sbtUsdtFee>0 && $sbtFee>0 && $userSbt < $sbtFee){
  199. $this->error = lang(4103,['sbt'=> $userSbt,'fee'=>$sbtFee]);
  200. return false;
  201. }
  202. // 质押收益比例参数
  203. $profitRate = ConfigService::make()->getConfigByCode('pledge_profit_rate',3);
  204. $profitRate = $profitRate>0 && $profitRate<100? floatval($profitRate) : 3;
  205. $bonusUsdtRate = ConfigService::make()->getConfigByCode('bonus_usdt_rate',80);
  206. $bonusUsdtRate = $bonusUsdtRate>0 && $bonusUsdtRate<100? floatval($bonusUsdtRate) : 80;
  207. // 检测是否有质押中订单
  208. if($info = PledgeOrderService::make()->checHasPledge($userId)){
  209. $orderNo = isset($info['order_no'])? $info['order_no'] : '';
  210. $this->error = lang(4105,['order_no'=>$orderNo]);
  211. return false;
  212. }
  213. // 预计收益
  214. $profit = moneyFormat($pledgeUsdt * $profitRate/100, 2); // 总收益
  215. $profitUsdt = moneyFormat($profit * $bonusUsdtRate/100, 2); // USDT余额进账部分
  216. // 质押有效期
  217. $pledgeTime = ConfigService::make()->getConfigByCode('pledge_time', 24);
  218. $pledgeTime = $pledgeTime>0 && $pledgeTime < 30 * 24? floatval($pledgeTime) : 24;
  219. $expiredAt = date('Y-m-d H:i:s', time() + intval($pledgeTime * 3600));
  220. // 结算时间
  221. $pledgeSettleTime = ConfigService::make()->getConfigByCode('pledge_settle_time', 24);
  222. $pledgeSettleTime = $pledgeSettleTime>0 && $pledgeSettleTime < 30 * 24? floatval($pledgeSettleTime) : 24;
  223. $settleAt = date('Y-m-d H:i:s', time() + intval($pledgeTime * 3600) + intval($pledgeSettleTime * 3600));
  224. // 质押轮次
  225. $lastRound = $this->model->where(['user_id'=> $userId,'mark'=>1])
  226. ->orderBy('round','desc')
  227. ->orderBy('create_time','desc')
  228. ->value('round');
  229. $round = $lastRound>0? intval($lastRound) + 1 : 1;
  230. // 缓存锁
  231. if(RedisService::get("caches:pledge:lock_{$userId}_{$round}")){
  232. $this->error = lang(4111,['uid'=> $userId,'round'=> $round]);
  233. return false;
  234. }
  235. // 若满足自动质押条件
  236. DB::beginTransaction();
  237. RedisService::set("caches:pledge:lock_{$userId}_{$round}",rand(60,120));
  238. try{
  239. // TODO 质押订单
  240. $order = [
  241. 'user_id'=> $userId,
  242. 'order_no'=> get_order_num('PD'),
  243. 'money'=> $pledgeUsdt,
  244. 'bonus_rate'=> $profitRate,
  245. 'profit'=> $profit,
  246. 'profit_usdt'=> $profitUsdt,
  247. 'sbt_price'=> $sbtPrice,
  248. 'time'=> $pledgeTime,
  249. 'round'=> $round,
  250. 'expired_at'=> $expiredAt,
  251. 'settle_at'=> $settleAt,
  252. 'settle_status'=> $settleStatus, // 1-正常结算,2-不结算动态和个人业绩
  253. 'create_time'=>time(),
  254. 'remark'=> '自动质押',
  255. 'status'=>1,
  256. 'mark'=>1,
  257. ];
  258. if(!$this->model->insertGetId($order)){
  259. DB::rollBack();
  260. $this->error = lang(4106,['uid'=> $userId,'round'=> $round]);
  261. RedisService::clear("caches:pledge:lock_{$userId}_{$round}");
  262. return false;
  263. }
  264. // 扣除余额和手续费
  265. $updateData = [
  266. 'usdt'=> DB::raw("usdt - {$pledgeUsdt}"),
  267. 'upgrade_at'=>date('Y-m-d H:i:s',time() + 2*86400),
  268. 'pledge_count'=>DB::raw("pledge_count + 1"),
  269. 'sbt'=>DB::raw("sbt - {$sbtFee}"),
  270. 'update_time'=>time()
  271. ];
  272. // 正常结算业绩
  273. if($settleStatus==1){
  274. $updateData['performance'] = DB::raw("performance + {$pledgeUsdt}");
  275. }
  276. if(!MemberModel::where(['id'=> $userId])->update($updateData)){
  277. DB::rollBack();
  278. $this->error = lang(4107,['uid'=> $userId,'round'=>$round,'money'=> $pledgeUsdt]);
  279. RedisService::clear("caches:pledge:lock_{$userId}_{$round}");
  280. return false;
  281. }
  282. // USDT账单
  283. $data = [
  284. 'user_id' => $userId,
  285. 'order_no' => $order['order_no'],
  286. 'type' => 1,
  287. 'user_type' => 1,
  288. 'round' => $round,
  289. 'coin_type' => 1,
  290. 'money' => -$pledgeUsdt,
  291. 'before_money' => $userUsdt,
  292. 'create_time' => time(),
  293. 'action_ip' => get_client_ip(),
  294. 'remark' => '质押交易',
  295. 'status' => 1,
  296. 'mark' => 1,
  297. ];
  298. if (!AccountLogModel::insertGetId($data)) {
  299. DB::rollBack();
  300. $this->error = lang(4108,['uid'=> $userId,'round'=>$round,'money'=> $pledgeUsdt]);
  301. RedisService::clear("caches:pledge:lock_{$userId}_{$round}");
  302. return false;
  303. }
  304. // SBT 手续费
  305. if($sbtFee){
  306. $data = [
  307. 'user_id' => $userId,
  308. 'order_no' => $order['order_no'],
  309. 'type' => 1,
  310. 'round' => $round,
  311. 'user_type' => 1,
  312. 'coin_type' => 2,
  313. 'money' => -$sbtFee,
  314. 'before_money' => $userSbt,
  315. 'create_time' => time(),
  316. 'action_ip' => get_client_ip(),
  317. 'remark' => '质押交易手续费',
  318. 'status' => 1,
  319. 'mark' => 1,
  320. ];
  321. if (!AccountLogModel::insertGetId($data)) {
  322. DB::rollBack();
  323. $this->error = lang(4108,['uid'=> $userId,'round'=>$round]);
  324. RedisService::clear("caches:pledge:lock_{$userId}_{$round}");
  325. return false;
  326. }
  327. }
  328. DB::commit();
  329. // 更新上级团队用户的等级更新时间
  330. $parentIds = get_parents($parentIds);
  331. if($parentIds){
  332. MemberModel::whereIn('id', $parentIds)->update(['upgrade_at'=>date('Y-m-d H:i:s',time() + 2*86400),'update_time'=>time()]);
  333. }
  334. // 质押轮次奖励
  335. FinanceService::make()->pledgeAward($userId, $round, $order);
  336. // 团队分红结算
  337. FinanceService::make()->settleTeamBonus($userId, $parentIds, $order);
  338. // 工作室分红结算
  339. FinanceService::make()->settleRoomBonus($userId, $parentIds, $order);
  340. $this->error = $this->error = lang(4109,['uid'=> $userId,'round'=>$round]);
  341. RedisService::clear("caches:pledge:lock_{$userId}_{$round}");
  342. RedisService::clear("caches:pledgeOrder:total_{$userId}");
  343. RedisService::clear("caches:pledge:check_{$userId}");
  344. RedisService::clear("caches:pledge:counts_{$userId}");
  345. return ['user_id'=>$userId,'round'=>$round,'money'=>$pledgeUsdt,'msg'=>'自动质押成功'];
  346. } catch (\Exception $exception){
  347. DB::rollBack();
  348. $this->error = lang(4110,['uid'=> $userId,'round'=>$round,'error'=> $exception->getMessage()]);
  349. return false;
  350. }
  351. }
  352. /**
  353. * 获取需要退本的订单
  354. * @return array|false
  355. */
  356. public function getRefundOrderList()
  357. {
  358. try {
  359. $limit = ConfigService::make()->getConfigByCode('refund_pledge_count', 500);
  360. $limit = $limit > 0 ? $limit : 500;
  361. $datas = $this->model->where(function($query){
  362. $query->where('expired_at', '<=', date('Y-m-d H:i:s'));
  363. })
  364. ->where(['status' => 1, 'mark' => 1])
  365. ->select(['id','user_id', 'order_no','money','status'])
  366. ->limit($limit)
  367. ->orderByRaw('rand()')
  368. ->orderBy('expired_at', 'asc')
  369. ->get();
  370. return $datas?$datas->toArray() : [];
  371. } catch (\Exception $exception) {
  372. $this->error = $exception->getMessage();
  373. return false;
  374. }
  375. }
  376. /**
  377. * 获取需要结算收益的订单
  378. * @return array|false
  379. */
  380. public function getSettleOrderList()
  381. {
  382. try {
  383. $limit = ConfigService::make()->getConfigByCode('settle_pledge_count', 500);
  384. $limit = $limit > 0 ? $limit : 500;
  385. $datas = $this->model->where(function($query){
  386. $query->where('settle_at', '<=', date('Y-m-d H:i:s'));
  387. })
  388. ->where(['status' => 2, 'mark' => 1])
  389. ->select(['id','user_id', 'order_no','money','status'])
  390. ->limit($limit)
  391. ->orderByRaw('rand()')
  392. ->orderBy('settle_at', 'asc')
  393. ->get();
  394. return $datas?$datas->toArray() : [];
  395. } catch (\Exception $exception) {
  396. $this->error = $exception->getMessage();
  397. return false;
  398. }
  399. }
  400. /**
  401. * 自动退本
  402. * @return bool
  403. */
  404. public function refund($orderId,$orderNo, $userId=0)
  405. {
  406. $cacheKey = "caches:pledgeOrder:refund_{$orderId}";
  407. if(RedisService::get($cacheKey)){
  408. $this->error = lang(4310,['uid'=>$userId,'order_no'=>$orderNo]);
  409. return false;
  410. }
  411. // 订单信息
  412. $orderInfo = $this->model::where(['id'=> $orderId,'mark'=>1])
  413. ->select(['id','user_id','order_no','money','round','bonus_rate','profit','profit_usdt','expired_at','status'])
  414. ->first();
  415. $orderInfo = $orderInfo? $orderInfo->toArray() : [];
  416. if(empty($orderInfo)){
  417. $this->error = lang(4311,['uid'=>$userId,'order_no'=>$orderNo]);
  418. return false;
  419. }
  420. // 状态验证
  421. $expiredAt = isset($orderInfo['expired_at'])? $orderInfo['expired_at'] : '';
  422. $status = isset($orderInfo['status'])? $orderInfo['status'] : 0;
  423. $pledgeUserId = isset($orderInfo['user_id'])? $orderInfo['user_id'] : 0;
  424. $pledgeUsdt = isset($orderInfo['money'])? floatval($orderInfo['money']) : 0;
  425. $pledgeRound = isset($orderInfo['round'])? intval($orderInfo['round']) : 0;
  426. $settleStatus = isset($orderInfo['settle_status'])? intval($orderInfo['settle_status']) : 0;
  427. $settleStatus = $settleStatus==2? 2 : 1; // 结算动态收益状态:1-正常,2-不结算动态
  428. if($expiredAt>date('Y-m-d H:i:s') || $status != 1 || $pledgeRound<=0){
  429. $this->error = lang(4312,['uid'=>$userId,'order_no'=>$orderNo]);
  430. return false;
  431. }
  432. // 退本金额
  433. if($pledgeUsdt<=0){
  434. $this->error = lang(4316,['uid'=>$userId,'order_no'=>$orderNo]);
  435. return false;
  436. }
  437. // 质押用户信息
  438. $memberInfo = MemberModel::where(['id'=> $pledgeUserId,'mark'=>1])
  439. ->select(['id','usdt','sbt','profit','performance','pledge_count','status'])
  440. ->first();
  441. $memberInfo = $memberInfo? $memberInfo->toArray() : [];
  442. $userUsdt = isset($memberInfo['usdt'])? $memberInfo['usdt'] : 0;
  443. $performance = isset($memberInfo['performance'])? $memberInfo['performance'] : 0;
  444. $pledgeCount = isset($memberInfo['pledge_count'])? $memberInfo['pledge_count'] : 0;
  445. if(empty($memberInfo) || $pledgeUserId != $userId){
  446. $this->error = lang(4313,['uid'=>$userId,'order_no'=>$orderNo]);
  447. return false;
  448. }
  449. // 订单退本处理
  450. DB::beginTransaction();
  451. try{
  452. // 更新订单状态
  453. if(!$this->model->where(['id'=> $orderId])->update(['status'=>2,'refund_at'=>date('Y-m-d H:i:s'),'update_time'=>time()])){
  454. DB::rollBack();
  455. $this->error = lang(4314,['uid'=>$userId,'order_no'=>$orderNo]);
  456. return false;
  457. }
  458. // 返还本金
  459. $updateData = [
  460. 'usdt'=>DB::raw("usdt + {$pledgeUsdt}"),
  461. 'pledge_count'=>max(0,($pledgeCount - 1)),
  462. 'update_time'=>time()
  463. ];
  464. // 正常结算业绩
  465. if($settleStatus == 1){
  466. $updateData['performance'] = max(0,($performance - $pledgeUsdt));
  467. }
  468. if(!MemberModel::where(['id'=>$pledgeUserId])->update($updateData)){
  469. DB::rollBack();
  470. $this->error = lang(4315,['uid'=>$userId,'order_no'=>$orderNo]);
  471. return false;
  472. }
  473. // 退本账单
  474. $data = [
  475. 'user_id' => $userId,
  476. 'order_no' => $orderNo,
  477. 'type' => 12,
  478. 'round' => $pledgeRound,
  479. 'user_type' => 1,
  480. 'coin_type' => 1,
  481. 'money' => $pledgeUsdt,
  482. 'before_money' => $userUsdt,
  483. 'create_time' => time(),
  484. 'action_ip' => get_client_ip(),
  485. 'remark' => '质押到期退还',
  486. 'status' => 1,
  487. 'mark' => 1,
  488. ];
  489. if (!AccountLogModel::insertGetId($data)) {
  490. DB::rollBack();
  491. $this->error = lang(4317,['uid'=> $userId,'order_no'=>$orderNo]);
  492. return false;
  493. }
  494. DB::commit();
  495. $this->error = lang(4318,['uid'=> $userId,'order_no'=>$orderNo,'performance'=>$pledgeUsdt]);
  496. $order = ['user_id'=>$userId,'round'=>$pledgeRound,'performance'=>$pledgeUsdt,'order_no'=>$orderNo];
  497. RedisService::set($cacheKey, $order, rand(300, 600));
  498. RedisService::clear("caches:pledge:check_{$userId}");
  499. return $order;
  500. }catch (\Exception $exception){
  501. DB::rollBack();
  502. $this->error = $exception->getMessage();
  503. return false;
  504. }
  505. }
  506. /**
  507. * 到期结算收益
  508. * @return bool
  509. */
  510. public function orderSettle($orderId, $orderNo, $userId=0)
  511. {
  512. $cacheKey = "caches:pledgeOrder:settle_{$orderId}";
  513. if(RedisService::get($cacheKey)){
  514. $this->error = lang(4410,['uid'=>$userId,'order_no'=>$orderNo]);
  515. return false;
  516. }
  517. // 订单信息
  518. $orderInfo = $this->model::where(['id'=> $orderId,'mark'=>1])
  519. ->select(['id','user_id','order_no','money','round','bonus_rate','sbt_price','profit','profit_usdt','settle_at','settle_status','status'])
  520. ->first();
  521. $orderInfo = $orderInfo? $orderInfo->toArray() : [];
  522. if(empty($orderInfo)){
  523. $this->error = lang(4411,['uid'=>$userId,'order_no'=>$orderNo]);
  524. return false;
  525. }
  526. // 状态验证
  527. $settleAt = isset($orderInfo['settle_at'])? $orderInfo['settle_at'] : '';
  528. $status = isset($orderInfo['status'])? $orderInfo['status'] : 0;
  529. $pledgeUserId = isset($orderInfo['user_id'])? $orderInfo['user_id'] : 0; // 质押用户
  530. $pledgeUsdt = isset($orderInfo['money'])? floatval($orderInfo['money']) : 0; // 质押金额
  531. $profit = isset($orderInfo['profit'])? floatval($orderInfo['profit']) : 0; // 总收益
  532. $profitUsdt = isset($orderInfo['profit_usdt'])? floatval($orderInfo['profit_usdt']) : 0; // USDT收益
  533. $pledgeRound = isset($orderInfo['round'])? intval($orderInfo['round']) : 0;
  534. $settleStatus = isset($orderInfo['settle_status'])? intval($orderInfo['settle_status']) : 0;
  535. $settleStatus = $settleStatus==2? 2 : 1; // 结算动态收益状态:1-正常,2-不结算动态
  536. if($settleAt>date('Y-m-d H:i:s') || $status != 2 || $pledgeRound<=0){
  537. $this->error = lang(4412,['uid'=>$userId,'order_no'=>$orderNo]);
  538. return false;
  539. }
  540. // 质押收益金额
  541. if($pledgeUsdt<=0 || $profit <=0 || ($profit < $profitUsdt)){
  542. $this->error = lang(4416,['uid'=>$userId,'order_no'=>$orderNo]);
  543. return false;
  544. }
  545. // 质押用户信息
  546. $memberInfo = MemberModel::where(['id'=> $pledgeUserId,'mark'=>1])
  547. ->select(['id','usdt','sbt','profit','performance','parent_ids','status'])
  548. ->first();
  549. $memberInfo = $memberInfo? $memberInfo->toArray() : [];
  550. $userUsdt = isset($memberInfo['usdt'])? $memberInfo['usdt'] : 0;
  551. $userProfit = isset($memberInfo['profit'])? $memberInfo['profit'] : 0;
  552. $userSbt = isset($memberInfo['sbt'])? $memberInfo['sbt'] : 0;
  553. $parentIds = isset($memberInfo['parent_ids'])? $memberInfo['parent_ids'] : '';
  554. if(empty($memberInfo) || $pledgeUserId != $userId){
  555. $this->error = lang(4413,['uid'=>$userId,'order_no'=>$orderNo]);
  556. return false;
  557. }
  558. // SBT 收益金额
  559. $sbtPrice = PriceLogService::make()->getSbtPrice();
  560. $profitSbt = moneyFormat(($profit - $profitUsdt)/$sbtPrice, 2); // 金本位计算
  561. // 订单收益结算处理
  562. DB::beginTransaction();
  563. try{
  564. // 更新订单状态
  565. if(!$this->model->where(['id'=> $orderId])->update(['status'=>3,'bonus_at'=>date('Y-m-d H:i:s'),'update_time'=>time()])){
  566. DB::rollBack();
  567. $this->error = lang(4414,['uid'=>$userId,'order_no'=>$orderNo]);
  568. return false;
  569. }
  570. // USDT收益和SBT收益
  571. $updateData = [
  572. 'profit'=>DB::raw("profit + {$profitUsdt}"),
  573. 'profit_total'=>DB::raw("profit_total + {$profit}"),
  574. 'pledge_profit'=>DB::raw("pledge_profit + {$profit}"),
  575. 'sbt'=>DB::raw("sbt + {$profitSbt}"),
  576. 'update_time'=>time()
  577. ];
  578. if(!MemberModel::where(['id'=>$pledgeUserId])->update($updateData)){
  579. DB::rollBack();
  580. $this->error = lang(4415,['uid'=>$userId,'order_no'=>$orderNo]);
  581. return false;
  582. }
  583. // USDT收益账单
  584. $data = [
  585. 'user_id' => $userId,
  586. 'order_no' => $orderNo,
  587. 'type' => 7,
  588. 'round' => $pledgeRound,
  589. 'user_type' => 1,
  590. 'coin_type' => 1,
  591. 'money' => $profitUsdt,
  592. 'before_money' => $userUsdt,
  593. 'create_time' => time(),
  594. 'action_ip' => get_client_ip(),
  595. 'remark' => '质押收益',
  596. 'status' => 1,
  597. 'mark' => 1,
  598. ];
  599. if (!AccountLogModel::insertGetId($data)) {
  600. DB::rollBack();
  601. $this->error = lang(4417,['uid'=> $userId,'order_no'=>$orderNo]);
  602. return false;
  603. }
  604. // SBT收益账单
  605. if($profitSbt>0){
  606. $data = [
  607. 'user_id' => $userId,
  608. 'order_no' => $orderNo,
  609. 'type' => 7,
  610. 'round' => $pledgeRound,
  611. 'user_type' => 1,
  612. 'coin_type' => 2,
  613. 'money' => $profitSbt,
  614. 'before_money' => $userSbt,
  615. 'create_time' => time(),
  616. 'action_ip' => get_client_ip(),
  617. 'remark' => '质押收益',
  618. 'status' => 1,
  619. 'mark' => 1,
  620. ];
  621. if (!AccountLogModel::insertGetId($data)) {
  622. DB::rollBack();
  623. $this->error = lang(4417,['uid'=> $userId,'order_no'=>$orderNo]);
  624. return false;
  625. }
  626. }
  627. // TODO 是否结算动态收益
  628. if($settleStatus==1){
  629. // TODO 推荐奖结算
  630. if(!FinanceService::make()->settleShareAward($userId, $parentIds, $orderInfo)){
  631. DB::rollBack();
  632. $this->error = FinanceService::make()->getError();
  633. return false;
  634. }
  635. // TODO 管理奖和平级奖结算
  636. if(!FinanceService::make()->settleManageAndLevelAward($userId, $parentIds, $orderInfo)){
  637. DB::rollBack();
  638. $this->error = FinanceService::make()->getError();
  639. return false;
  640. }
  641. }
  642. DB::commit();
  643. $this->error = lang(4418,['uid'=> $userId,'order_no'=>$orderNo]);
  644. $order = ['user_id'=>$userId,'round'=>$pledgeRound,'profit'=>$profit,'profit_usdt'=>$profitUsdt,'profit_sbt'=>$profitSbt,'order_no'=>$orderNo];
  645. RedisService::set($cacheKey, $order, rand(300, 600));
  646. return $order;
  647. }catch (\Exception $exception){
  648. DB::rollBack();
  649. $this->error = '订单结算异常:'.$exception->getMessage();
  650. return false;
  651. }
  652. }
  653. /**
  654. * 全网流水(质押中订单总金额)
  655. * @return array|mixed
  656. */
  657. public function getGlobalTotal()
  658. {
  659. $cacheKey = "caches:pledgeOrder:globalTotal";
  660. $data = RedisService::get($cacheKey);
  661. if($data){
  662. return $data;
  663. }
  664. $data = $this->model->where(['status'=>1,'mark'=>1])->sum('money');
  665. if($data){
  666. RedisService::set($cacheKey, $data, 600);
  667. }
  668. return $data;
  669. }
  670. }