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('成功,已开始审核'); } }