MemberService.php 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  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\Helpers\Jwt;
  13. use App\Models\AccountLogModel;
  14. use App\Models\BalanceLogModel;
  15. use App\Models\DeveloperModel;
  16. use App\Models\MemberLevelModel;
  17. use App\Models\MemberModel;
  18. use App\Services\BaseService;
  19. use App\Services\ConfigService;
  20. use App\Services\CregisPayService;
  21. use App\Services\RedisService;
  22. use App\Services\ToolService;
  23. use App\Services\WalletService;
  24. use Illuminate\Support\Facades\DB;
  25. use phpqrcode\QRcode;
  26. /**
  27. * 会员-服务类
  28. * @author laravel开发员
  29. * @since 2020/11/11
  30. * @package App\Services\Api
  31. */
  32. class MemberService extends BaseService
  33. {
  34. protected static $instance;
  35. /**
  36. * 构造函数
  37. * @author laravel开发员
  38. * @since 2020/11/11
  39. */
  40. public function __construct()
  41. {
  42. $this->model = new MemberModel();
  43. }
  44. /**
  45. * 静态入口
  46. */
  47. public static function make()
  48. {
  49. if (!self::$instance) {
  50. self::$instance = new static();
  51. }
  52. return self::$instance;
  53. }
  54. /**
  55. * 登录注册
  56. * @param $walletUrl
  57. * @param string $scode
  58. * @return array|false
  59. */
  60. public function loginOrRegister($walletUrl, $scode = '')
  61. {
  62. if (empty($walletUrl)) {
  63. $this->error = 1038;
  64. return false;
  65. }
  66. $info = $this->model->with(['parent'])->where(['wallet_url' => $walletUrl, 'mark' => 1])
  67. ->select(['id', 'nickname', 'member_level', 'performance', 'code', 'usdt', 'sbt', 'profit', 'parent_id', 'wallet_url', 'wallet_token', 'recharge_url', 'bonus_rate', 'pledge_auto', 'trade_status', 'status'])
  68. ->first();
  69. $info = $info ? $info->toArray() : [];
  70. $parentInfo = isset($info['parent']) ? $info['parent'] : [];
  71. $parentWalletUrl = isset($parentInfo['wallet_url']) ? $parentInfo['wallet_url'] : '';
  72. $ip = get_client_ip();
  73. $ipData = ToolService::make()->getIpAddress($ip, '');
  74. $province = isset($ipData['regionName']) ? $ipData['regionName'] : '';
  75. $city = isset($ipData['city']) ? $ipData['city'] : '';
  76. /* TODO 注册 */
  77. if (empty($info)) {
  78. // 上级账号
  79. $parentId = 0;
  80. $parentIds = '';
  81. if ($scode) {
  82. $parentInfo = $this->model->where(['code' => $scode, 'mark' => 1])->select(['id', 'parent_ids', 'wallet_url'])->first();
  83. $parentId = isset($parentInfo['id']) ? $parentInfo['id'] : 0;
  84. $parents = isset($parentInfo['parent_ids']) ? $parentInfo['parent_ids'] : '';
  85. $parentWalletUrl = isset($parentInfo['wallet_url']) ? $parentInfo['wallet_url'] : '';
  86. if ($parentId) {
  87. $parentIds = $parents ? trim($parents, ',') . ",{$parentId}," : "{$parentId},";
  88. }
  89. }
  90. // 注册奖励
  91. $awardSbtUsdt = ConfigService::make()->getConfigByCode('register_award_sbt', 0);
  92. if ($awardSbtUsdt > 0) {
  93. $sbtPrice = PriceLogService::make()->getSbtPrice();
  94. $awardSbtNum = $sbtPrice ? round($awardSbtUsdt * 1 / $sbtPrice, 2) : 0;
  95. }
  96. // U盾充值地址
  97. $rechargeUrl = CregisPayService::make()->createAddress(1000);
  98. DB::beginTransaction();
  99. $id = $this->model->max('id') + 1;
  100. $info = [
  101. 'nickname' => 'SBT_' . substr($walletUrl, -6, 6),
  102. 'wallet_url' => $walletUrl,
  103. 'wallet_token' => make_wallet_token($walletUrl, $id),
  104. 'avatar' => '',
  105. 'recharge_url' => $rechargeUrl,
  106. 'recharge_token' => $rechargeUrl?make_wallet_token($rechargeUrl, $id) : '',
  107. 'member_level' => 0,
  108. 'usdt' => 0.00,
  109. 'sbt' => $awardSbtNum,
  110. 'profit' => 0.00,
  111. 'bonus_rate' => 0.00,
  112. 'performance' => 0.00,
  113. 'parent_id' => $parentId,
  114. 'parent_ids' => $parentIds,
  115. 'pledge_auto' => 1,
  116. 'code' => strtoupper(get_random_code(9, 'S', "{$id}")),
  117. 'login_ip' => $ip,
  118. 'login_region' => $province . '' . $city,
  119. 'login_time' => time(),
  120. 'create_time' => time(),
  121. 'trade_status' => 1,
  122. 'status' => 1,
  123. 'mark' => 1,
  124. ];
  125. if (!$userId = $this->model->insertGetId($info)) {
  126. DB::rollBack();
  127. $this->error = 2007;
  128. return false;
  129. }
  130. if ($awardSbtNum > 0) {
  131. $log = [
  132. 'user_id' => $userId,
  133. 'type' => 13,
  134. 'order_no' => get_order_num('RW'),
  135. 'coin_type' => 2,
  136. 'money' => $awardSbtNum,
  137. 'before_money' => 0,
  138. 'create_time' => time(),
  139. 'remark' => '注册奖励',
  140. 'action_ip' => get_client_ip(),
  141. 'status' => 1,
  142. 'mark' => 1
  143. ];
  144. if (!AccountLogModel::insertGetId($log)) {
  145. DB::rollBack();
  146. $this->error = 2007;
  147. return false;
  148. }
  149. }
  150. DB::commit();
  151. // 清除团队统计数据或列表缓存
  152. RedisService::keyDel("caches:team*");
  153. } else {
  154. $userId = $info['id'];
  155. $status = isset($info['status']) ? $info['status'] : 0;
  156. if ($status != 1) {
  157. $this->error = 2015;
  158. return false;
  159. }
  160. $updateData = [
  161. 'login_ip' => $ip,
  162. 'login_region' => $province . '' . $city,
  163. 'login_time' => time(),
  164. 'update_time' => time(),
  165. ];
  166. if ($info['wallet_token'] == '') {
  167. $updateData['wallet_token'] = make_wallet_token($walletUrl, $userId);
  168. }
  169. if(empty($info['recharge_url'])){
  170. $info['recharge_url'] = CregisPayService::make()->createAddress(1000);
  171. $info['recharge_token'] = $info['recharge_url']?make_wallet_token($info['recharge_url'], $userId) : '';
  172. }
  173. $this->model->where(['id' => $userId])->update($updateData);
  174. }
  175. // 获取授权TOKEN
  176. $jwt = new Jwt('jwt_wx');
  177. $token = $jwt->getToken($userId);
  178. RedisService::set("auths:info:{$userId}", $info, 6 * 3600);
  179. $this->error = 2004;
  180. return [
  181. 'token' => $token,
  182. 'info' => [
  183. 'id' => $userId,
  184. 'wallet_url' => $info['wallet_url'],
  185. 'member_level' => intval($info['member_level']),
  186. 'usdt' => round($info['usdt'], 4),
  187. 'sbt' => round($info['sbt'], 2),
  188. 'profit' => round($info['profit'], 2),
  189. 'performance' => round($info['performance'], 2),
  190. 'code' => $info['code'],
  191. 'nickname' => $info['nickname'],
  192. 'parent_id' => $info['parent_id'],
  193. 'bonus_rate' => $info['bonus_rate'],
  194. 'pledge_auto' => $info['pledge_auto'],
  195. 'recharge_url' => $info['recharge_url'],
  196. 'parent_url' => $parentWalletUrl,
  197. ],
  198. ];
  199. }
  200. /**
  201. * 用户信息
  202. * @param int $userId 用户ID
  203. * @param int $type 返回数据类型:1-默认,2-充值,3-提现
  204. * @param false $refresh
  205. * @return array|mixed
  206. */
  207. public function getInfo(int $userId, $type = 1, $refresh = false)
  208. {
  209. $cacheKey = "caches:member:info_{$userId}_{$type}";
  210. $info = RedisService::get($cacheKey);
  211. if ($info && !$refresh) {
  212. return $info;
  213. }
  214. $info = $this->model->with(['parent'])->where(['id' => $userId, 'mark' => 1])
  215. ->select(['id', 'nickname', 'code', 'member_level', 'usdt', 'sbt', 'performance', 'profit', 'profit_total', 'pledge_profit', 'manage_profit', 'share_profit', 'pj_profit', 'global_profit', 'parent_id', 'wallet_url', 'wallet_token', 'recharge_url','recharge_token', 'bonus_rate', 'pledge_auto', 'trade_status', 'status'])
  216. ->first();
  217. $info = $info ? $info->toArray() : [];
  218. if (empty($info)) {
  219. $this->error = 2016;
  220. return false;
  221. }
  222. $parentInfo = isset($info['parent']) ? $info['parent'] : [];
  223. $parentWalletUrl = isset($parentInfo['wallet_url']) ? $parentInfo['wallet_url'] : '';
  224. $walletUrl = isset($info['wallet_url']) ? $info['wallet_url'] : '';
  225. $walletToken = isset($info['wallet_token']) ? $info['wallet_token'] : '';
  226. if ($type == 3 && $walletUrl && $walletToken != make_wallet_token($walletUrl, $userId)) {
  227. $info['error'] = '钱包地址已被修改';
  228. }
  229. $rechargeUrl = isset($info['recharge_url']) ? $info['recharge_url'] : '';
  230. if(empty($rechargeUrl)){
  231. $rechargeUrl = CregisPayService::make()->createAddress(1000);
  232. if($rechargeUrl){
  233. $rechargeToken = make_wallet_token($rechargeUrl, $userId);
  234. $this->model->where(['id'=>$userId])->update(['recharge_url'=> $rechargeUrl,'recharge_token'=>$rechargeToken,'update_time'=>time()]);
  235. }
  236. }
  237. $rechargeToken = isset($info['recharge_token']) ? $info['recharge_token'] : '';
  238. if ($type == 2 && $rechargeUrl && $rechargeToken != make_wallet_token($rechargeUrl, $userId)) {
  239. $info['error'] = '钱包地址已被修改';
  240. }
  241. $info = [
  242. 'id' => $userId,
  243. 'wallet_url' => $info['wallet_url'],
  244. 'member_level' => intval($info['member_level']),
  245. 'usdt' => moneyFormat($info['usdt'], 2),
  246. 'sbt' => moneyFormat($info['sbt'], 2),
  247. 'performance' => moneyFormat($info['performance'], 2),
  248. 'profit' => moneyFormat($info['profit'], 2),
  249. 'profit_total' => moneyFormat($info['profit_total'], 2),
  250. 'pledge_total' => moneyFormat($info['pledge_profit'], 2),
  251. 'share_profit' => moneyFormat($info['share_profit'], 2),
  252. 'manage_profit' => moneyFormat($info['manage_profit'], 2),
  253. 'global_profit' => moneyFormat($info['global_profit'], 2),
  254. 'pj_profit' => moneyFormat($info['pj_profit'], 2),
  255. 'code' => $info['code'],
  256. 'nickname' => $info['nickname'],
  257. 'parent_id' => $info['parent_id'],
  258. 'bonus_rate' => $info['bonus_rate'],
  259. 'pledge_auto' => $info['pledge_auto'],
  260. 'error' => isset($info['error']) ? $info['error'] : '',
  261. 'parent_url' => $parentWalletUrl,
  262. 'recharge_url' => $rechargeUrl,
  263. 'max_usdt_rate' => ConfigService::make()->getConfigByCode('usdt_withdraw_max_rate', 0),
  264. 'sbt_fee' => ConfigService::make()->getConfigByCode('profit_withdraw_sbt_fee', 0),
  265. 'recharge_min' => ConfigService::make()->getConfigByCode('recharge_min_money', 0.1),
  266. 'withdraw_min' => ConfigService::make()->getConfigByCode('withdraw_min_money', 0),
  267. ];
  268. if ($type == 1) {
  269. $chainTotal = BalanceLogService::make()->getCountByType($userId, 1);
  270. $ptTotal = AccountLogService::make()->getCountByType($userId, 5, 1);
  271. $info['recharge_total'] = moneyFormat($chainTotal + $ptTotal, 2);
  272. $chainTotal = BalanceLogService::make()->getCountByType($userId, 2, 0, 0);
  273. $ptTotal = AccountLogService::make()->getCountByType($userId, 6, 1);
  274. $info['withdraw_total'] = moneyFormat($chainTotal + $ptTotal, 2);
  275. }
  276. RedisService::set($cacheKey, $info, rand(5, 10));
  277. return $info;
  278. }
  279. /**
  280. * 获取级数据
  281. * @param $levelId
  282. * @param $type 类型:1-当前等级数据,2-下一等级数据
  283. * @return array|mixed
  284. */
  285. public function getLevelData($levelId, $type = 1)
  286. {
  287. $cacheKey = "caches:member:level_{$levelId}_{$type}";
  288. $info = RedisService::get($cacheKey);
  289. if ($info) {
  290. return $info;
  291. }
  292. $data = MemberLevelModel::where(function ($query) use ($type, $levelId) {
  293. if ($type == 2) {
  294. $query->where('id', '>', $levelId);
  295. } else {
  296. $query->where('id', $levelId);
  297. }
  298. })
  299. ->where(['status' => 1, 'mark' => 1])
  300. ->select(['id', 'name', 'upgrade_usdt', 'bonus_rate', 'weighting_rate'])
  301. ->orderBy('sort', 'asc')
  302. ->orderBy('id', 'asc')
  303. ->first();
  304. $data = $data ? $data->toArray() : [];
  305. if ($data) {
  306. RedisService::set($cacheKey, $data, rand(3600, 7200));
  307. }
  308. return $data;
  309. }
  310. /**
  311. * 设置分红比例
  312. * @param int $userId 用户ID
  313. * @param array $params 参数,user_id-设置用户ID(必选),bonus_rate-比例
  314. * @return bool
  315. */
  316. public function setBonus(int $userId, array $params)
  317. {
  318. // 参数验证
  319. $uid = isset($params['user_id']) ? intval($params['user_id']) : 0;
  320. $bonusRate = isset($params['bonus_rate']) && $params['bonus_rate'] > 0 ? floatval($params['bonus_rate']) : 0;
  321. if ($uid <= 0) {
  322. $this->error = 2014;
  323. return false;
  324. }
  325. if ($bonusRate < 0 || $bonusRate >= 100) {
  326. $this->error = 1022;
  327. return false;
  328. }
  329. $info = $this->model->where(['id' => $userId, 'mark' => 1])->first();
  330. if (empty($info)) {
  331. $this->error = 2016;
  332. return false;
  333. }
  334. // 账号状态
  335. $status = isset($info['status']) ? $info['status'] : 0;
  336. if ($status != 1) {
  337. $this->error = 2015;
  338. return false;
  339. }
  340. // 设置用户
  341. $userInfo = $this->model->where(['id' => $uid, 'status' => 1, 'mark' => 1])
  342. ->select(['id', 'parent_id', 'parent_ids', 'bonus_rate'])
  343. ->first();
  344. if (empty($userInfo)) {
  345. $this->error = 1042;
  346. return false;
  347. }
  348. // 上级用户校验
  349. $parentId = isset($userInfo['parent_id']) ? $userInfo['parent_id'] : 0;
  350. $parentIds = isset($userInfo['parent_ids']) ? $userInfo['parent_ids'] : '';
  351. if ($parentId != $userId) {
  352. $this->error = 1043;
  353. return false;
  354. }
  355. // 最大设置比例
  356. $maxBonusRate = $this->getUpBonusRate($uid, $parentIds);
  357. if ($maxBonusRate > 0 && $bonusRate > $maxBonusRate) {
  358. $this->errorData = ['rate' => $maxBonusRate];
  359. $this->error = 2036;
  360. return false;
  361. }
  362. // 更新数据
  363. $data = [
  364. 'bonus_rate' => floatval($bonusRate),
  365. 'update_time' => time()
  366. ];
  367. if (!$this->model->where(['id' => $uid])->update($data)) {
  368. $this->error = 1020;
  369. return false;
  370. }
  371. $this->error = 1019;
  372. return true;
  373. }
  374. /**
  375. * 获取上级团队设置的最近的比例
  376. * @param $userId
  377. * @param $parentIds
  378. * @return false|int|mixed
  379. */
  380. public function getUpBonusRate($userId, $parentIds)
  381. {
  382. $cacheKey = "caches:member:upRate_{$userId}";
  383. $data = RedisService::get($cacheKey);
  384. if ($data) {
  385. return isset($data['bonus_rate']) ? $data['bonus_rate'] : 0;
  386. }
  387. $parentIds = $parentIds ? explode(',', $parentIds) : [];
  388. $parentIds = array_filter($parentIds);
  389. krsort($parentIds);
  390. if (empty($parentIds) || !is_array($parentIds)) {
  391. return 0;
  392. }
  393. $data = $this->model->whereIn('id', $parentIds)
  394. ->where('bonus_rate', '>', 0)
  395. ->where(['mark' => 1])
  396. ->select(['id', 'bonus_rate'])
  397. ->orderByRaw('FIELD(id, "' . implode(',', $parentIds) . '")')
  398. ->first();
  399. $data = $data ? $data->toArray() : [];
  400. if ($data) {
  401. RedisService::set($cacheKey, $data, rand(3, 5));
  402. }
  403. return isset($data['bonus_rate']) ? $data['bonus_rate'] : 0;
  404. }
  405. /**
  406. * 获取上级团队设置的最近的工作室分红比例
  407. * @param $userId
  408. * @param $parentIds
  409. * @return false|int|mixed
  410. */
  411. public function getUpRoomBonusRate($userId, $parentIds)
  412. {
  413. $cacheKey = "caches:member:upRoomRate_{$userId}";
  414. $data = RedisService::get($cacheKey);
  415. if ($data) {
  416. return isset($data['bonus_rate']) ? $data['bonus_rate'] : 0;
  417. }
  418. $parentIds = $parentIds ? explode(',', $parentIds) : [];
  419. $parentIds = array_filter($parentIds);
  420. krsort($parentIds);
  421. if (empty($parentIds) || !is_array($parentIds)) {
  422. return 0;
  423. }
  424. $data = $this->model->whereIn('id', $parentIds)
  425. ->where('room_bonus_rate', '>', 0)
  426. ->where(['mark' => 1])
  427. ->select(['id', 'room_bonus_rate'])
  428. ->orderByRaw('FIELD(id, "' . implode(',', $parentIds) . '")')
  429. ->first();
  430. $data = $data ? $data->toArray() : [];
  431. if ($data) {
  432. RedisService::set($cacheKey, $data, rand(3, 5));
  433. }
  434. return isset($data['room_bonus_rate']) ? $data['room_bonus_rate'] : 0;
  435. }
  436. /**
  437. * 设置自动质押状态
  438. * @param int $userId 用户ID
  439. * @param array $params 参数,status-状态(1-开启,0-关闭,必选)
  440. * @return bool
  441. */
  442. public function setPledge(int $userId, array $params)
  443. {
  444. // 参数验证
  445. $pledgeStatus = isset($params['status']) ? intval($params['status']) : 0;
  446. if (!in_array($pledgeStatus, [0, 1])) {
  447. $this->error = 2014;
  448. return false;
  449. }
  450. // 关闭限制验证
  451. $cacheKey = "caches:pledge:limitClose_{$userId}";
  452. if ($pledgeStatus == 0 && RedisService::get($cacheKey)) {
  453. $this->error = 2031;
  454. return false;
  455. }
  456. // 账号信息
  457. $info = $this->model->where(['id' => $userId, 'mark' => 1])->select(['id', 'pledge_auto', 'status'])->first();
  458. if (empty($info)) {
  459. $this->error = 2016;
  460. return false;
  461. }
  462. // 账号状态
  463. $status = isset($info['status']) ? $info['status'] : 0;
  464. if ($status != 1) {
  465. $this->error = 2015;
  466. return false;
  467. }
  468. // 质押过,且已经退本过才可关闭
  469. if ($pledgeStatus == 0 && !PledgeOrderService::make()->checkPledge($userId)) {
  470. $this->error = 2030;
  471. return false;
  472. }
  473. // 更新
  474. $data = ['pledge_auto' => $pledgeStatus, 'update_time' => time()];
  475. if (!$this->model->where(['id' => $userId])->update($data)) {
  476. $this->error = 1020;
  477. return false;
  478. }
  479. // 限制关闭次数
  480. if ($pledgeStatus == 0) {
  481. RedisService::set($cacheKey, date('Y-m-d H:i:s'), 30);
  482. }
  483. $this->error = 1019;
  484. return true;
  485. }
  486. /**
  487. * 保存资料
  488. * @param int $userId 用户ID
  489. * @param $params 参数
  490. * @return bool
  491. */
  492. public function setProfile(int $userId, $params)
  493. {
  494. $info = $this->model->where(['id' => $userId, 'mark' => 1])->first();
  495. if (empty($info)) {
  496. $this->error = 2016;
  497. return false;
  498. }
  499. $data = [
  500. 'update_time' => time()
  501. ];
  502. if (isset($params['nickname']) && $params['nickname']) {
  503. $data['nickname'] = trim($params['nickname']);
  504. }
  505. // 头像
  506. // 更新
  507. if (!$this->model->where(['id' => $userId])->update($data)) {
  508. $this->error = 1009;
  509. return false;
  510. }
  511. $this->error = 1008;
  512. return true;
  513. }
  514. /**
  515. * 生成普通参数二维码
  516. * @param $str 参数
  517. * @param bool $refresh 是否重新生成
  518. * @return bool
  519. */
  520. public function makeQrcode($str, $dir='qrcode', $refresh = false, $size = 4, $margin = 2, $level = 2)
  521. {
  522. $basePath = base_path() . '/public';
  523. $qrFile = "/images/{$dir}/";
  524. if (!is_dir($basePath . '/uploads' . $qrFile)) {
  525. @mkdir($basePath . '/uploads' . $qrFile, 0755, true);
  526. }
  527. $key = date('Ymd') . strtoupper(md5($str . '_' . $size . $margin . $level));
  528. $qrFile = $qrFile . "C_{$key}.png";
  529. $cacheKey = "caches:qrcodes:member_" . $key;
  530. if (RedisService::get($cacheKey) && is_file($basePath . '/uploads' . $qrFile) && !$refresh) {
  531. return $qrFile;
  532. }
  533. QRcode::png($str, $basePath . '/uploads' . $qrFile, $level, $size, $margin);
  534. if (!file_exists($basePath . '/uploads' . $qrFile)) {
  535. return false;
  536. }
  537. RedisService::set($cacheKey, ['str' => $str, 'qrcode' => $qrFile, 'date' => date('Y-m-d H:i:s')], 7 * 24 * 3600);
  538. return $qrFile;
  539. }
  540. /**
  541. * 提现
  542. * @param $userId 提现用户ID
  543. * @param $params 提现参数:money-金额(必选)、coin_type-账户类型/币种(必选)
  544. * @return array|false|mixed
  545. */
  546. public function withdraw($userId, $params)
  547. {
  548. $money = isset($params['money']) ? floatval($params['money']) : 0;
  549. $coinType = isset($params['coin_type']) ? intval($params['coin_type']) : 1;
  550. if ($money <= 0) {
  551. $this->error = 2025;
  552. return false;
  553. }
  554. // 锁
  555. $coinType = $coinType == 1 ? 1 : 2;
  556. $cacheKey = "caches:withdraw:{$userId}_{$coinType}";
  557. if (RedisService::get($cacheKey . '_lock')) {
  558. return false;
  559. }
  560. $info = $this->model->where(['id' => $userId, 'mark' => 1])->first();
  561. if (empty($info)) {
  562. $this->error = 2016;
  563. return false;
  564. }
  565. $status = isset($info['status']) ? $info['status'] : 0;
  566. $tradeStatus = isset($info['trade_status']) ? $info['trade_status'] : 0;
  567. $userUsdt = isset($info['usdt']) ? floatval($info['usdt']) : 0;
  568. $userProfit = isset($info['profit']) ? floatval($info['profit']) : 0;
  569. $userSbt = isset($info['sbt']) ? floatval($info['sbt']) : 0;
  570. if ($status != 1) {
  571. $this->error = 2015; // 冻结
  572. return false;
  573. }
  574. if (in_array($tradeStatus, [2, 3])) {
  575. $this->error = intval("202{$tradeStatus}");
  576. return false;
  577. }
  578. // 可提金额或手续费验证
  579. try {
  580. DB::beginTransaction();
  581. RedisService::set($cacheKey . '_lock', date('Y-m-d H:i:s'), rand(3,5));
  582. if ($coinType == 1) {
  583. $maxUsdtRate = ConfigService::make()->getConfigByCode('usdt_withdraw_max_rate', 0);
  584. $withdrawTotal = BalanceLogService::make()->getCountByType($userId, 2, 1, 0);
  585. $maxWithdrawMoney = ($withdrawTotal + $userUsdt);
  586. $maxWithdrawMoney = $maxUsdtRate > 0 && $maxUsdtRate < 100 ? moneyFormat($maxWithdrawMoney * $maxUsdtRate / 100, 2) : $maxWithdrawMoney;
  587. if (($withdrawTotal + floatval($money)) >= $maxWithdrawMoney) {
  588. DB::rollBack();
  589. $this->error = 2026; // 可提金额超出
  590. $this->errorData = ['money' => moneyFormat($maxWithdrawMoney - $withdrawTotal, 2)];
  591. RedisService::clear($cacheKey . '_lock');
  592. return false;
  593. }
  594. // 提现处理
  595. $updateData = ['usdt' => DB::raw("usdt - {$money}"), 'update_time' => time()];
  596. if (!$this->model->where(['id' => $userId])->update($updateData)) {
  597. DB::rollBack();
  598. $this->error = 2033; // 提现处理失败
  599. RedisService::clear($cacheKey . '_lock');
  600. return false;
  601. }
  602. // 账单
  603. $log = [
  604. 'user_id' => $userId,
  605. 'order_no' => get_order_num('SW'),
  606. 'type' => 2,
  607. 'user_type' => 1,
  608. 'coin_type' => 1,
  609. 'money' => $money,
  610. 'actual_money' => $money,
  611. 'fee' => 0,
  612. 'pay_type' => 10,
  613. 'pay_status' => 10,
  614. 'wallet_url' => $info['wallet_url'],
  615. 'pt_wallet_url' => '',
  616. 'date' => date('Y-m-d'),
  617. 'create_time' => time(),
  618. 'audit_remark' => '',
  619. 'status' => 1,
  620. 'mark' => 1,
  621. ];
  622. if (!BalanceLogModel::insertGetId($log)) {
  623. DB::rollBack();
  624. $this->error = 2033;
  625. RedisService::clear($cacheKey . '_lock');
  626. return false;
  627. }
  628. // USDT余额账单
  629. $data = [
  630. 'user_id' => $userId,
  631. 'order_no' => $log['order_no'],
  632. 'type' => 4,
  633. 'user_type' => 1,
  634. 'coin_type' => 1,
  635. 'money' => -$money,
  636. 'before_money' => $userUsdt,
  637. 'create_time' => time(),
  638. 'action_ip' => get_client_ip(),
  639. 'remark' => '余额提现',
  640. 'status' => 1,
  641. 'mark' => 1,
  642. ];
  643. if (!AccountLogModel::insertGetId($data)) {
  644. DB::rollBack();
  645. $this->error = 2033;
  646. RedisService::clear($cacheKey . '_lock');
  647. return false;
  648. }
  649. // 成功
  650. DB::commit();
  651. return $this->getInfo($userId, 3, true);
  652. } // 收益提现
  653. else {
  654. $sbtUsdtFee = ConfigService::make()->getConfigByCode('profit_withdraw_sbt_fee', 0);
  655. $sbtPrice = PriceLogService::make()->getSbtPrice();
  656. $sbtFee = moneyFormat(floatval($sbtUsdtFee) / $sbtPrice, 2);
  657. if ($sbtFee > $userSbt) {
  658. DB::rollBack();
  659. $this->error = 2032; // 手续费不足
  660. $this->errorData = ['money' => $sbtFee];
  661. RedisService::clear($cacheKey . '_lock');
  662. return false;
  663. }
  664. // 余额是否足
  665. if ($money > $userProfit) {
  666. DB::rollBack();
  667. $this->error = 2026; // 手续费不足
  668. $this->errorData = ['money' => $userProfit];
  669. RedisService::clear($cacheKey . '_lock');
  670. return false;
  671. }
  672. // 提现处理
  673. $updateData = ['profit' => DB::raw("profit - {$money}"), 'sbt' => DB::raw("sbt - {$sbtFee}"), 'update_time' => time()];
  674. if (!$this->model->where(['id' => $userId])->update($updateData)) {
  675. DB::rollBack();
  676. $this->error = 2033; // 提现处理失败
  677. RedisService::clear($cacheKey . '_lock');
  678. return false;
  679. }
  680. // 账单
  681. $log = [
  682. 'user_id' => $userId,
  683. 'order_no' => get_order_num('SW'),
  684. 'type' => 2,
  685. 'user_type' => 1,
  686. 'coin_type' => 2,
  687. 'money' => $money,
  688. 'actual_money' => $money,
  689. 'fee' => $sbtFee,
  690. 'fee_usdt' => $sbtUsdtFee,
  691. 'pay_type' => 10,
  692. 'pay_status' => 10,
  693. 'wallet_url' => $info['wallet_url'],
  694. 'pt_wallet_url' => '',
  695. 'date' => date('Y-m-d'),
  696. 'create_time' => time(),
  697. 'audit_remark' => '',
  698. 'status' => 1,
  699. 'mark' => 1,
  700. ];
  701. if (!BalanceLogModel::insertGetId($log)) {
  702. DB::rollBack();
  703. $this->error = 2033;
  704. RedisService::clear($cacheKey . '_lock');
  705. return false;
  706. }
  707. // 收益账单
  708. $data = [
  709. 'user_id' => $userId,
  710. 'order_no' => $log['order_no'],
  711. 'type' => 4,
  712. 'user_type' => 1,
  713. 'coin_type' => 3,
  714. 'money' => -$money,
  715. 'before_money' => $userProfit,
  716. 'create_time' => time(),
  717. 'action_ip' => get_client_ip(),
  718. 'remark' => '收益提现',
  719. 'status' => 1,
  720. 'mark' => 1,
  721. ];
  722. if (!AccountLogModel::insertGetId($data)) {
  723. DB::rollBack();
  724. $this->error = 2033;
  725. RedisService::clear($cacheKey . '_lock');
  726. return false;
  727. }
  728. // 手续费账单
  729. if ($sbtFee > 0) {
  730. $data = [
  731. 'user_id' => $userId,
  732. 'order_no' => $log['order_no'],
  733. 'type' => 4,
  734. 'user_type' => 1,
  735. 'coin_type' => 2,
  736. 'money' => -$sbtFee,
  737. 'before_money' => $userSbt,
  738. 'create_time' => time(),
  739. 'action_ip' => get_client_ip(),
  740. 'remark' => '提现手续费',
  741. 'status' => 1,
  742. 'mark' => 1,
  743. ];
  744. if (!AccountLogModel::insertGetId($data)) {
  745. DB::rollBack();
  746. $this->error = 2033;
  747. RedisService::clear($cacheKey . '_lock');
  748. return false;
  749. }
  750. }
  751. // 成功
  752. DB::commit();
  753. RedisService::keyDel("caches:balanceLog:total*");
  754. RedisService::clear($cacheKey . '_lock');
  755. return $this->getInfo($userId, 3, true);
  756. }
  757. } catch (\Exception $exception) {
  758. DB::rollBack();
  759. $this->errorData = ['error' => $exception->getMessage()];
  760. RedisService::clear($cacheKey . '_lock');
  761. $this->error = 2028;
  762. return false;
  763. }
  764. }
  765. /**
  766. * 闪兑
  767. * @param $userId 用户
  768. * @param $params 参数:money-闪兑金额
  769. * @return array|false|mixed
  770. */
  771. public function exchange($userId, $params)
  772. {
  773. $money = isset($params['money']) ? floatval($params['money']) : 0;
  774. $coinType = 2;
  775. if ($money <= 0) {
  776. $this->error = 2101;
  777. return false;
  778. }
  779. $cacheKey = "caches:exchange:{$userId}_{$coinType}";
  780. if (RedisService::get($cacheKey . '_lock')) {
  781. return false;
  782. }
  783. $info = $this->model->where(['id' => $userId, 'mark' => 1])->select(['id','usdt','profit','sbt','status'])->first();
  784. if (empty($info)) {
  785. $this->error = 2016;
  786. return false;
  787. }
  788. $status = isset($info['status']) ? $info['status'] : 0;
  789. $userProfit = isset($info['profit']) ? floatval($info['profit']) : 0;
  790. $userSbt = isset($info['sbt']) ? floatval($info['sbt']) : 0;
  791. $userUsdt = isset($info['usdt']) ? floatval($info['usdt']) : 0;
  792. if ($status != 1) {
  793. $this->error = 2015; // 冻结
  794. return false;
  795. }
  796. // 闪兑验证
  797. $sbtLimit = ConfigService::make()->getConfigByCode('sbt_exchange_limit', 100);
  798. $sbtPrice = PriceLogService::make()->getSbtPrice();
  799. $sbtLimit = round($sbtLimit/$sbtPrice, 2);
  800. $sbtLimit = $sbtLimit>0? $sbtLimit : 0;
  801. if($sbtLimit>0 && $sbtLimit > $userSbt){
  802. $this->errorData = ['money'=> $sbtLimit];
  803. $this->error = lang(2102);
  804. return false;
  805. }
  806. $exchangeSbtMin = ConfigService::make()->getConfigByCode('exchange_sbt_min', 100);
  807. $exchangeSbtMin = $exchangeSbtMin>10? $exchangeSbtMin : 100;
  808. if($money<$exchangeSbtMin){
  809. $this->errorData = ['money'=> $exchangeSbtMin];
  810. $this->error = lang(2106);
  811. return false;
  812. }
  813. // 可提金额或手续费验证
  814. try {
  815. DB::beginTransaction();
  816. RedisService::set($cacheKey . '_lock',date('Y-m-d H:i:s'), rand(3,5));
  817. // TODO SBT 闪兑 USDT
  818. if ($coinType == 2) {
  819. // 手续费
  820. $exchangeFeeRate = ConfigService::make()->getConfigByCode('exchange_fee_rate', 0);
  821. $exchangeFeeRate = $exchangeFeeRate>0 && $exchangeFeeRate<100? floatval($exchangeFeeRate) : 0;
  822. // 闪兑金额
  823. $sbtUsdt = moneyFormat($money * $sbtPrice, 2); // 闪兑USDT金额
  824. $feeUsdt = round($sbtUsdt * $exchangeFeeRate/100, 2);
  825. $feeUsdt = $feeUsdt>0? $feeUsdt : 0.01;
  826. $actualUsdt = moneyFormat($sbtUsdt - $feeUsdt, 2);
  827. // 闪兑入账收益账户处理
  828. $updateData = [
  829. 'profit' => DB::raw("profit + {$actualUsdt}"),
  830. 'sbt' => DB::raw("sbt - {$money}"),
  831. 'update_time' => time()
  832. ];
  833. if (!$this->model->where(['id' => $userId])->update($updateData)) {
  834. DB::rollBack();
  835. $this->error = 2103;
  836. RedisService::clear($cacheKey . '_lock');
  837. return false;
  838. }
  839. // SBT扣除账单
  840. $orderNo = get_order_num('SE');
  841. $data = [
  842. 'user_id' => $userId,
  843. 'order_no' => $orderNo,
  844. 'type' => 2,
  845. 'user_type' => 1,
  846. 'coin_type' => 2,
  847. 'money' => -$money,
  848. 'before_money' => $userSbt,
  849. 'create_time' => time(),
  850. 'action_ip' => get_client_ip(),
  851. 'remark' => 'SBT闪兑',
  852. 'status' => 1,
  853. 'mark' => 1,
  854. ];
  855. if (!AccountLogModel::insertGetId($data)) {
  856. DB::rollBack();
  857. $this->error = 2103;
  858. RedisService::clear($cacheKey . '_lock');
  859. return false;
  860. }
  861. // 到账USDT收益账单
  862. $data = [
  863. 'user_id' => $userId,
  864. 'order_no' => $orderNo,
  865. 'type' => 2,
  866. 'user_type' => 1,
  867. 'coin_type' => 3,
  868. 'money' => $actualUsdt,
  869. 'before_money' => $userProfit,
  870. 'create_time' => time(),
  871. 'action_ip' => get_client_ip(),
  872. 'remark' => 'SBT闪兑到账',
  873. 'status' => 1,
  874. 'mark' => 1,
  875. ];
  876. if (!AccountLogModel::insertGetId($data)) {
  877. DB::rollBack();
  878. $this->error = 2103;
  879. RedisService::clear($cacheKey . '_lock');
  880. return false;
  881. }
  882. // 闪兑手续费账单
  883. $data = [
  884. 'user_id' => $userId,
  885. 'order_no' => $orderNo,
  886. 'type' => 18,
  887. 'user_type' => 1,
  888. 'coin_type' => 3,
  889. 'money' => -$feeUsdt,
  890. 'before_money' => moneyFormat($userProfit + $actualUsdt,2),
  891. 'create_time' => time(),
  892. 'action_ip' => get_client_ip(),
  893. 'remark' => 'SBT闪兑手续费',
  894. 'status' => 1,
  895. 'mark' => 1,
  896. ];
  897. if (!AccountLogModel::insertGetId($data)) {
  898. DB::rollBack();
  899. $this->error = 2103;
  900. RedisService::clear($cacheKey . '_lock');
  901. return false;
  902. }
  903. // 成功
  904. DB::commit();
  905. $this->error = 2105;
  906. return $this->getInfo($userId, 3, true);
  907. }else{
  908. DB::rollBack();
  909. $this->error = 1031;
  910. RedisService::clear($cacheKey . '_lock');
  911. return false;
  912. }
  913. } catch (\Exception $exception) {
  914. DB::rollBack();
  915. $this->errorData = ['error' => $exception->getMessage()];
  916. RedisService::clear($cacheKey . '_lock');
  917. $this->error = 2104;
  918. return false;
  919. }
  920. }
  921. /**
  922. * 更新用户等级
  923. * @param $userId 需要更新等级用户ID
  924. */
  925. public function upgradeUpdate($userId)
  926. {
  927. $cacheKey = "caches:member:levelLock_{$userId}";
  928. if (RedisService::get($cacheKey)) {
  929. $this->error = lang(4303, ['uid' => $userId]);
  930. return false;
  931. }
  932. RedisService::set($cacheKey, date('Y-m-d H:i:s'), rand(5, 10));
  933. $userInfo = $this->model->where(['id' => $userId, 'mark' => 1])
  934. ->select(['id', 'usdt', 'member_level', 'performance','upgrade_status', 'upgrade_at', 'status'])
  935. ->first();
  936. if (empty($userInfo)) {
  937. RedisService::clear($cacheKey);
  938. $this->error = lang(4301, ['uid' => $userId]);
  939. return false;
  940. }
  941. $memberLevel = isset($userInfo['member_level']) ? $userInfo['member_level'] : 0; // 个人等级
  942. $performance = isset($userInfo['performance']) ? $userInfo['performance'] : 0; // 个人业绩
  943. $upgradeStatus = isset($userInfo['upgrade_status']) ? $userInfo['upgrade_status'] : 0; // 升级方式
  944. $teamPerformance = TeamService::make()->getTeamPerformanceByType($userId, 2, true);
  945. $teamPerformance = round($teamPerformance + $performance, 2);
  946. // 等级条件
  947. $nextLevel = MemberLevelModel::where('upgrade_usdt', '<=', $teamPerformance)
  948. ->where(['status' => 1, 'mark' => 1])
  949. ->orderBy('sort', 'desc')
  950. ->orderBy('id', 'desc')
  951. ->value('id');
  952. $nextLevel = $nextLevel>0? intval($nextLevel) : 0;
  953. // 是否只升不降
  954. if ($$upgradeStatus==2 && $nextLevel<=$memberLevel) {
  955. RedisService::clear($cacheKey);
  956. $this->error = lang(4302, ['uid' => $userId]);
  957. return false;
  958. }
  959. // 更新等级
  960. if (!$this->model->where(['id' => $userId])->update(['member_level' => $nextLevel, 'old_level' => $memberLevel,'upgrade_at'=>date('Y-m-d H:i:s'), 'update_time' => time()])) {
  961. RedisService::clear($cacheKey);
  962. $this->error = lang(4302, ['uid' => $userId, 'level' => $nextLevel]);
  963. return false;
  964. }
  965. $this->error = lang(4302, ['uid' => $userId, 'level' => $nextLevel, 'old' => $memberLevel]);
  966. RedisService::set($cacheKey, ['level' => $nextLevel, 'old' => $memberLevel, 'date' => date('Y-m-d H:i:s')], rand(30, 60));
  967. return ['msg' => '更新成功', 'uid' => $userId, 'level' => $nextLevel, 'old' => $memberLevel];
  968. }
  969. /**
  970. * 获取可质押用户列表(按批次数量获取)
  971. * @return mixed
  972. */
  973. public function pledgeUserList()
  974. {
  975. try {
  976. $limit = ConfigService::make()->getConfigByCode('pledge_user_count', 500);
  977. $limit = $limit > 0 ? $limit : 500;
  978. $pledgeUsdt = ConfigService::make()->getConfigByCode('pledge_money', 500);
  979. $pledgeUsdt = $pledgeUsdt > 0 ? $pledgeUsdt : 500;
  980. $sbtPrice = PriceLogService::make()->getSbtPrice();
  981. $sbtUsdtFee = ConfigService::make()->getConfigByCode('pledge_sbt_usdt_fee', 0);
  982. $sbtUsdtFee = $sbtUsdtFee > 0 ? floatval($sbtUsdtFee) : 0;
  983. $sbtFee = $sbtUsdtFee && $sbtPrice > 0 ? moneyFormat($sbtUsdtFee / $sbtPrice, 2) : 0;
  984. $datas = $this->model->from('member as a')
  985. ->leftJoin('pledge_orders as b', 'b.user_id', '=', 'a.id')
  986. ->where('a.usdt', '>=', $pledgeUsdt)
  987. ->where('a.sbt', '>=', $sbtFee)
  988. ->where(function($query){
  989. $query->where('b.id', '<=', 0)->orWhere('a.pledge_count', '<=', 0);
  990. })
  991. ->where(['a.pledge_auto' => 1, 'a.status' => 1, 'a.mark' => 1])
  992. ->select(['a.id', 'a.pledge_auto', 'a.usdt', 'a.sbt', 'a.profit', 'a.trade_status','a.settle_status','a.status', 'a.parent_ids', 'b.id as pledge_id'])
  993. ->limit($limit)
  994. ->orderByRaw('rand()')
  995. ->orderBy('a.id', 'asc')
  996. ->get();
  997. return $datas?$datas->toArray() : [];
  998. } catch (\Exception $exception) {
  999. $this->error = $exception->getMessage();
  1000. return false;
  1001. }
  1002. }
  1003. /**
  1004. * 获取需要更新等级用户列表(更新等级时间超出当前时间则需要更新)
  1005. * @return mixed
  1006. */
  1007. public function getUpgradeUserList()
  1008. {
  1009. try {
  1010. $limit = ConfigService::make()->getConfigByCode('upgrade_user_count', 500);
  1011. $limit = $limit > 0 ? $limit : 500;
  1012. $datas = $this->model->where('upgrade_at', '>', date('Y-m-d H:i:s'))
  1013. ->where(['status' => 1, 'mark' => 1])
  1014. ->select(['id', 'pledge_auto', 'usdt', 'sbt', 'profit', 'trade_status', 'parent_ids'])
  1015. ->limit($limit)
  1016. ->orderByRaw('rand()')
  1017. ->orderBy('id', 'asc')
  1018. ->get();
  1019. return $datas?$datas->toArray() : [];
  1020. } catch (\Exception $exception) {
  1021. $this->error = $exception->getMessage();
  1022. return false;
  1023. }
  1024. }
  1025. /**
  1026. * 有效开发者列表
  1027. * @return array|mixed
  1028. */
  1029. public function getDeveloperList()
  1030. {
  1031. $cacheKey = "caches:developer:list";
  1032. $datas = RedisService::get($cacheKey);
  1033. if($datas){
  1034. return $datas;
  1035. }
  1036. $datas = DeveloperModel::where(['status'=>1])
  1037. ->where('last_settle_at','<',date('Y-m-d', time() - 7 * 86400))
  1038. ->limit(200)
  1039. ->get();
  1040. $datas = $datas? $datas->toArray() : [];
  1041. if($datas){
  1042. RedisService::set($cacheKey, $datas, rand(3600, 7200));
  1043. }
  1044. return $datas;
  1045. }
  1046. }