| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- <?php
- namespace app\api\controller\coin;
- 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;
- use think\facade\Log;
- class UserCoinController {
- public function status(Request $request) {
- // 是否开启挖矿
- $symbol = Config::get('app.mining_symbo');
- //
- $icon = Cache::get($symbol);
- if (!$icon) {
- $icon = DictCoin::where('symbol', $symbol)->value('icon');
- Cache::set($symbol, $icon);
- }
- //
- $defStatus = [
- 'boot' => 0,
- 'stop' => 0,
- 'progress'=> 0,
- 'symbol'=>$symbol,
- 'icon'=>$icon,
- 'price'=> 0.00,
- 'total'=> 0,
- 'step' => 0,
- 'ts' => 0,
- ];
- if (!$symbol) {
- Log::warning('mining disabled: $symbol');
- return app('json')->successful($defStatus);
- }
- $uid = $request->uid();
- $mymining = json_decode((new UserRds)->get($uid, UserRds::FIELD_MINING), true);
- if (!$mymining) {
- $mymining = $defStatus;
- }
- if ($mymining['progress'] > 0.0) {
- $mymining = $this->calcMining($uid, $mymining);
- (new UserRds)->sets($uid, $mymining);
- } else {
- $mymining['total'] = UserCoin::where('uid', $uid)->where('symbol', $symbol)->value('balance') ?? 0.0;
- }
- 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 = bcdiv($step, 10, 8);
- $min = bcsub($step, $amp, 8);
- $max = bcadd($step, $amp, 8);
- $distance = bcsub($max, $min, 8);
- $section = bcdiv($distance, 10, 8);
- return bcmul($section, mt_rand(0, 10), 8) + $min;
- }
- /**
- * 根据上次挖矿状态, 和过去的时长, 计算当前的状态
- * @param $p
- * @return mixed
- */
- protected function calcMining($uid, $p) {
- if (!isset($p['ts']) ||
- !isset($p['boot']) ||
- !isset($p['stop']) ||
- !isset($p['step']) ||
- !isset($p['progress'])) {
- Log::warning('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(bcmul($step, $secs_remain, 8));
- $p['progress'] += floatval(bcadd($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'];
- Log::error("user<$uid> save transfer failed, amount<$amount>");
- return $p;
- }
- // -- save to db
- $p['ts'] = $now;
- $p['total'] = floatval(bcadd($p['total'], $p['progress'], 8));
- $p['progress'] = 0;
- return $p;
- }
- // 进行中
- $count = floatval(bcmul($step, $secs_passed, 8));
- $p['progress'] = floatval(bcadd($p['progress'], $count, 8));
- $p['ts'] = $now;
- return $p;
- }
- /**
- * 启动
- */
- public function boot(Request $request) {
- $uid = $request->uid();
- $now = time();
- // 是否开启活动
- $symbol = Config::get('app.mining_symbo');
- if (!$symbol) {
- return app('json')->fail('本活动未开启');
- }
- // 是否已经开启
- $mining = json_decode((new UserRds)->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('启动失败');
- }
- // 开启
- $secs_unit = Config::get('app.mining_sec_unit');
- $reward_unit = Config::get('app.mining_num_per_unit');
- $step = floatval(bcdiv($reward_unit, $secs_unit, 8));
- $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]) {
- Log::warning('app.mining_time config error.');
- return app('json')->fail('启动失败,请联系客服');;
- }
- $hour = random_int($hours[0], $hours[1]);
- $stop = $now + $hour * 60 * 60;
- $suc = (new UserRds)->sets($uid, [
- '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,
- ]);
- }
- /**
- * 请求转账记录
- */
- 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);
- }
- /*
- * 更新钱包地址
- */
- 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();
- }
- /*
- * 提现
- */
- public function transfer(Request $request) {
- list($symbol, $amount) = UtilService::postMore([
- ['symbol', ''],
- ['amount', 0.0]
- ], $request, true);
- 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('未达到最低限额');
- }
- $uid = $request->uid();
- $userCoin = UserCoin::where(['uid'=>$uid, 'symbol'=>$symbol])->find();
- if (!$userCoin) {
- return app('json')->fail('不适用的用户');
- }
- $userCoin = $userCoin->toArray();
- if(!$userCoin['addr']) {
- return app('json')->fail('地址未设置');
- }
- if ($userCoin['balance'] < $amount) {
- return app('json')->fail('余额不足');
- }
- $transferred = UserCoinTransfer::hasTransferred($uid, $symbol, $userCoin['addr']);
- if (!$transferred) {
- return app('json')->fail('首次操作需联系客服进行');
- }
- // transfer
- UserCoin::beginTrans();
- $left = bcsub($userCoin['balance'], $amount, 8);
- $res1 = UserCoin::where(['uid'=>$uid, 'symbol'=>$symbol])->update(['balance'=>$left]);
- $res2 = UserCoinTransfer::withdrawal($uid, $symbol, $userCoin['addr'], $amount);
- $ok = $res1 && $res2;
- UserCoin::checkTrans($ok);
- if (!$ok) {
- return app('json')->fail('执行失败');
- }
- return app('json')->successful('成功,已开始审核');
- }
- }
|