| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- <?php
- namespace app\api\controller\coin;
- use app\admin\model\user\User;
- use app\admin\model\user\UserBill;
- use app\models\coin\UserCoinTransfer;
- use tw\redis\UserRds;
- use app\models\store\StoreOrderCartInfo;
- use app\models\system\DictCoin;
- use app\models\user\UserCoin;
- use app\Request;
- use crmeb\services\{UtilService, JsonService};
- use think\facade\Config;
- use think\facade\Cache;
- // 精度相关的函数
- // 转换为 string, 不同于 strval(),这个对于极小 float 不会使用科学计数法表示
- function twstr($val, $scale = 2): string
- {
- return number_format($val, $scale, '.', '');
- }
- function twadd($l, $r, $scale = 2): string
- {
- return bcadd(twstr($l, $scale + 2), twstr($r, $scale + 2), $scale);
- }
- function twsub($l, $r, $scale = 2): string
- {
- return bcsub(twstr($l, $scale + 2), twstr($r, $scale + 2), $scale);
- }
- function twmul($l, $r, $scale = 2): string
- {
- return bcmul(twstr($l, $scale + 2), twstr($r, $scale + 2), $scale);
- }
- function twdiv($l, $r, $scale = 2): string
- {
- return bcdiv(twstr($l, $scale + 2), twstr($r, $scale + 2), $scale);
- }
- function twmod($l, $r, $scale = 2): string
- {
- return bcmod(twstr($l, $scale + 2), twstr($r, $scale + 2), $scale);
- }
- function twcomp($l, $r, $scale = 2): string
- {
- return bccomp(twstr($l, $scale + 2), twstr($r, $scale + 2), $scale);
- }
- class UserCoinController
- {
- /**
- * @api {get} /coin/status 挖矿当前状态
- * @apiName GetCoinStatus
- * @apiGroup User.Coin
- *
- */
- public function status(Request $request)
- {
- // 是否开启挖矿
- $symbol = Config::get('app.mining_symbo');
- //
- $coinInfo = Cache::get($symbol);
- if (!$coinInfo) {
- $coinInfo = DictCoin::where('symbol', $symbol)->find();
- if (!$coinInfo) {
- return app('json')->failed('活动临时关闭,请联系客服');
- }
- $coinInfo = $coinInfo->toArray();
- Cache::set($symbol, $coinInfo);
- }
- // print_r($coinInfo);
- //
- $defStatus = [
- 'boot' => 0,
- 'stop' => 0,
- 'progress' => 0,
- 'symbol' => $symbol,
- 'icon' => $coinInfo['icon'],
- 'price' => $coinInfo['price'],
- 'total' => 0,
- 'step' => 0,
- 'ts' => 0,
- ];
- // 沒配置挖什麼
- if (!$symbol) {
- warnlog('mining disabled: $symbol');
- return app('json')->successful($defStatus);
- }
- // 獲取用戶進度
- $uid = $request->uid();
- $rds = new UserRds();
- $mymining = json_decode($rds->get($uid, UserRds::FIELD_MINING), true);
- if (!$mymining) {
- $mymining = $defStatus;
- return app('json')->successful($defStatus);
- }
- //
- if ($mymining['progress'] > 0.0) {
- $mymining = $this->calcMining($uid, $coinInfo, $mymining);
- $rds->set($uid, UserRds::FIELD_MINING, json_encode($mymining));
- } else { // 本次挖矿已经结束
- $mymining['total'] = UserCoin::where('uid', $uid)->where('symbol', $symbol)->value('balance') ?? 0.0;
- $mymining['symbol'] = $symbol;
- $mymining['icon'] = $coinInfo['icon'];
- $mymining['price'] = $coinInfo['price'];
- $mymining['step'] = self::get_step();
- }
- return app('json')->successful([
- 'symbol' => $mymining['symbol'],
- 'icon' => $mymining['icon'],
- 'price' => $mymining['price'],
- 'step' => $mymining['step'],
- 'progress' => $mymining['progress'],
- 'total' => $mymining['total'],
- ]);
- }
- /**
- * 根据 $step 返回一个新 $step
- * 新的 $step 值为围绕 旧 $step 周围的随机值,上下波动幅度为 10%
- */
- protected function floatStep($step)
- {
- $amp = twdiv($step, 10, 8);
- $min = twsub($step, $amp, 8);
- $max = twadd($step, $amp, 8);
- $distance = twsub($max, $min, 8);
- $section = twdiv($distance, 10, 8);
- return twmul($section, mt_rand(0, 10), 8) + $min;
- }
- /**
- * 根据上次挖矿状态, 和过去的时长, 计算当前的状态
- * @param $p
- * @return mixed
- */
- protected function calcMining($uid, $coinInfo, $p)
- {
- if (
- !isset($p['ts']) ||
- !isset($p['boot']) ||
- !isset($p['stop']) ||
- !isset($p['step']) ||
- !isset($p['progress'])
- ) {
- warnlog('error format.');
- return $p;
- }
- if ($p['progress'] <= 0) {
- return $p;
- }
- $step = $this->floatStep($p['step']);
- $now = time();
- $secs_passed = $now - $p['ts']; // 从上次到现在
- //
- if ($now >= $p['stop']) { // 挖矿结束
- $secs_remain = $p['stop'] - $p['ts'];
- if ($secs_remain < 0) {
- $secs_remain = 0;
- }
- // 本次个数
- $count = floatval(twmul($step, $secs_remain, 8));
- $p['progress'] += floatval(twadd($p['progress'], $count, 8));
- // save to db
- UserCoinTransfer::beginTrans();
- $r1 = UserCoinTransfer::addMining($uid, $p['order_id'], $p['symbol'], $p['progress']);
- $r2 = UserCoin::upsertCoin($uid, $p['symbol'], $p['progress']);
- UserCoinTransfer::checkTrans($r1 && $r2);
- if (!$r1 || !$r2) {
- $amount = $p['progress'];
- errlog("user<$uid> save transfer failed, amount<$amount>");
- return $p;
- }
- // -- save to db
- $p['ts'] = $now;
- $p['total'] = floatval(twadd($p['total'], $p['progress'], 8));
- $p['progress'] = 0;
- // 更换为可能的新的币种
- $p['symbol'] = $coinInfo['symbol'];
- $p['icon'] = $coinInfo['icon'];
- $p['price'] = $coinInfo['price'];
- $p['step'] = self::get_step();
- return $p;
- }
- // 进行中
- $count = floatval(twmul($step, $secs_passed, 8));
- $p['progress'] = floatval(twadd($p['progress'], $count, 8));
- $p['ts'] = $now;
- return $p;
- }
- public static function get_step(): float
- {
- $secs_unit = Config::get('app.mining_sec_unit');
- $reward_unit = Config::get('app.mining_num_per_unit');
- return twdiv($reward_unit, $secs_unit, 8);
- }
- /**
- * @api {post} /coin/boot 启动挖矿
- * @apiName PostCoinBoot
- * @apiGroup User.Coin
- *
- */
- public function boot(Request $request)
- {
- $uid = $request->uid();
- $now = time();
- // 是否停止
- if (Config::get('activity.mining_stopped')) {
- return app('json')->fail('活动已暂停,稍后开启');
- }
- // 是否开启活动
- $symbol = Config::get('app.mining_symbo');
- if (!$symbol) {
- return app('json')->fail('本活动未开启');
- }
- // 是否已经开启
- $rds = new UserRds();
- $mining = json_decode($rds->get($uid, UserRds::FIELD_MINING), true);
- if ($mining) {
- if (
- isset($mining['progress']) &&
- $mining['progress'] > 0.0 &&
- isset($mining['boot']) &&
- isset($mining['stop']) &&
- isset($mining['ts'])
- ) {
- return app('json')->fail('已启动');
- }
- }
- // 是否有订单
- $orderId = StoreOrderCartInfo::getMiningOrderId($uid);
- if (!$orderId) {
- return app('json')->fail('参加活动下单后可启动,请参看 首页->官方资讯->新手教程');
- }
- // 标记订单
- if (!StoreOrderCartInfo::setMining($orderId)) {
- return app('json')->fail('启动失败');
- }
- // 开启
- $step = self::get_step();
- $progress = $step;
- $icon = DictCoin::getIcon($symbol);
- // 已挖总额
- $balance = UserCoin::where('uid', $uid)->where('symbol', $symbol)->value('balance') ?? 0.0;
- // 单次活动时长
- $hours = Config::get('app.mining_time');
- if (count($hours) != 2 || $hours[0] > $hours[1]) {
- warnlog('app.mining_time config error.');
- return app('json')->fail('启动失败,请联系客服');;
- }
- $hour = random_int($hours[0], $hours[1]);
- $stop = $now + $hour * 60 * 60;
- $suc = $rds->set($uid, UserRds::FIELD_MINING, json_encode([
- 'boot' => $now,
- 'step' => $step,
- 'stop' => $stop,
- 'symbol' => $symbol,
- 'icon' => $icon,
- 'price' => 0,
- 'progress' => $progress,
- 'total' => $balance,
- 'ts' => $now + 1,
- 'order_id' => $orderId,
- ]));
- if ($suc != 0 && $suc != 1) {
- StoreOrderCartInfo::setMining($orderId, 0);
- return app('json')->fail('未成功启动');
- }
- return app('json')->successful([
- 'symbol' => $symbol,
- 'icon' => $icon,
- 'price' => 0,
- 'step' => $progress,
- 'progress' => $progress,
- 'total' => $balance,
- ]);
- }
- /**
- * @api {get} /coin/history 请求转账记录
- * @apiName GetCoinHistory
- * @apiGroup User.Coin
- *
- */
- public function history(Request $request)
- {
- [$page, $limit] = UtilService::getMore([
- ['page', 1],
- ['limit', 20],
- ], $request, true);
- $uid = $request->uid();
- $rows = UserCoinTransfer::getUserTransferred($uid, $page, $limit);
- return app('json')->successful($rows);
- }
- /**
- * @api {post} /coin/addr 更新钱包地址
- * @apiName PostCoinAddr
- * @apiGroup User.Coin
- *
- */
- public function updateAddr(Request $request)
- {
- list($symbol, $addr) = UtilService::postMore([
- ['symbol', ''],
- ['addr', ''],
- ], $request, true);
- if (!$symbol || !$addr) {
- return app('json')->fail('参数不可为空');
- }
- if (!DictCoin::where('symbol', $symbol)->select()->toArray()) {
- return app('json')->fail('未找到目标');
- }
- $uid = $request->uid();
- $suc = UserCoin::upsertAddr($uid, $symbol, $addr);
- if (!$suc) {
- return app('json')->fail('执行失败');
- }
- return app('json')->successful();
- }
- /**
- * @api {post} /coin/transfer 提现
- * @apiName PostCoinTransfer
- * @apiGroup User.Coin
- *
- */
- public function transfer(Request $request)
- {
- list($symbol, $amount) = UtilService::postMore([
- ['symbol', ''],
- ['amount', 0]
- ], $request, true);
- $uid = $request->uid();
- $user = User::getUserinfos($uid);
- if (!$symbol) {
- return app('json')->fail('参数不可为空');
- }
- $meta = DictCoin::where('symbol', $symbol)->find();
- if (!$meta) {
- return app('json')->fail('未找到币种');
- }
- $meta = $meta->toArray();
- if ($amount < $meta['min_withdrawal']) {
- return app('json')->fail('未达到最低限额');
- }
- $userCoin = UserCoin::where(['uid' => $uid, 'symbol' => $symbol])->find();
- if (!$userCoin) {
- return app('json')->fail('不适用的用户');
- }
- $userCoin = $userCoin->toArray();
- if ($symbol == 'RMB') {
- return $this->_transfer_fiat($user, $amount, $userCoin);
- } else {
- return $this->_transfer_crypto_coin($amount, $symbol);
- }
- }
- protected function _transfer_crypto_coin($amount, $symbol)
- {
- return app('json')->fail('转账加密货币,请联系客服');
- }
- public function _transfer_fiat($user, $amount, $userCoin)
- {
- $uid = $user['uid'];
- $symbol = $userCoin['symbol'];
- $amount = floor($amount);
- if ($userCoin['balance'] < $amount) {
- return app('json')->fail('余额不足');
- }
- // transfer
- UserCoin::beginTrans();
- // 扣除
- $res1 = UserCoin::where(['uid' => $uid, 'symbol' => $symbol])->dec('balance', $amount)->update();
- // 添加佣金
- $res3 = UserBill::income(
- '钱包转出' . $symbol,
- $uid,
- 'now_money',
- 'brokerage',
- $amount,
- 0,
- twadd($user['brokerage_price'], $amount, 2),
- '黑洞星球转入' . $amount . '元'
- );
- $res4 = User::bcInc($uid, 'brokerage_price', $amount, 'uid');
- // 转账记录
- $res2 = true; // UserCoinTransfer::withdrawal($uid, $symbol, $userCoin['addr'], $amount);
- $ok = $res1 && $res2 && $res3 && $res4;
- UserCoin::checkTrans($ok);
- if (!$ok) {
- return app('json')->fail('执行失败');
- }
- return app('json')->successful('转出成功');
- }
- }
|