PledgeOrderService.php 30 KB

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