|
|
@@ -1,4 +1,5 @@
|
|
|
<?php
|
|
|
+
|
|
|
namespace app\api\controller\coin;
|
|
|
|
|
|
use app\models\coin\UserCoinTransfer;
|
|
|
@@ -11,50 +12,103 @@ use crmeb\services\{UtilService, JsonService};
|
|
|
use think\facade\Config;
|
|
|
use think\facade\Cache;
|
|
|
|
|
|
-class UserCoinController {
|
|
|
+// 精度相关的函数
|
|
|
+// 转换为 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) {
|
|
|
+ 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);
|
|
|
+ $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'=>$icon,
|
|
|
- 'price'=> 0.00,
|
|
|
- 'total'=> 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();
|
|
|
- $mymining = json_decode((new UserRds)->get($uid, UserRds::FIELD_MINING), true);
|
|
|
+ $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, $mymining);
|
|
|
- (new UserRds)->sets($uid, $mymining);
|
|
|
- } else {
|
|
|
+ $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'],
|
|
|
@@ -72,12 +126,12 @@ class UserCoinController {
|
|
|
*/
|
|
|
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;
|
|
|
+ $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;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -85,13 +139,16 @@ class UserCoinController {
|
|
|
* @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'])) {
|
|
|
- warnlog('error format.');
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
@@ -108,8 +165,8 @@ class UserCoinController {
|
|
|
$secs_remain = 0;
|
|
|
}
|
|
|
// 本次个数
|
|
|
- $count = floatval(bcmul($step, $secs_remain, 8));
|
|
|
- $p['progress'] += floatval(bcadd($p['progress'], $count, 8));
|
|
|
+ $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']);
|
|
|
@@ -122,37 +179,61 @@ class UserCoinController {
|
|
|
}
|
|
|
// -- save to db
|
|
|
$p['ts'] = $now;
|
|
|
- $p['total'] = floatval(bcadd($p['total'], $p['progress'], 8));
|
|
|
+ $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(bcmul($step, $secs_passed, 8));
|
|
|
- $p['progress'] = floatval(bcadd($p['progress'], $count, 8));
|
|
|
+ $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) {
|
|
|
+ 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('本活动未开启');
|
|
|
}
|
|
|
// 是否已经开启
|
|
|
- $mining = json_decode((new UserRds)->get($uid, UserRds::FIELD_MINING), true);
|
|
|
+ $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'])) {
|
|
|
+ if (
|
|
|
+ isset($mining['progress']) &&
|
|
|
+ $mining['progress'] > 0.0 &&
|
|
|
+ isset($mining['boot']) &&
|
|
|
+ isset($mining['stop']) &&
|
|
|
+ isset($mining['ts'])
|
|
|
+ ) {
|
|
|
return app('json')->fail('已启动');
|
|
|
}
|
|
|
}
|
|
|
@@ -160,19 +241,19 @@ class UserCoinController {
|
|
|
// 是否有订单
|
|
|
$orderId = StoreOrderCartInfo::getMiningOrderId($uid);
|
|
|
if (!$orderId) {
|
|
|
- return app('json')->fail('参加活动后可启动,请参看新手教程');
|
|
|
+ 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));
|
|
|
+ $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.');
|
|
|
@@ -181,22 +262,22 @@ class UserCoinController {
|
|
|
$hour = random_int($hours[0], $hours[1]);
|
|
|
$stop = $now + $hour * 60 * 60;
|
|
|
|
|
|
- $suc = (new UserRds)->sets($uid, [
|
|
|
+ $suc = $rds->set($uid, UserRds::FIELD_MINING, json_encode([
|
|
|
'boot' => $now,
|
|
|
'step' => $step,
|
|
|
'stop' => $stop,
|
|
|
- 'symbol'=> $symbol,
|
|
|
+ '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')->fail('未成功启动');
|
|
|
}
|
|
|
|
|
|
return app('json')->successful([
|
|
|
@@ -215,7 +296,8 @@ class UserCoinController {
|
|
|
* @apiGroup User.Coin
|
|
|
*
|
|
|
*/
|
|
|
- public function history(Request $request) {
|
|
|
+ public function history(Request $request)
|
|
|
+ {
|
|
|
[$page, $limit] = UtilService::getMore([
|
|
|
['page', 1],
|
|
|
['limit', 20],
|
|
|
@@ -231,7 +313,8 @@ class UserCoinController {
|
|
|
* @apiGroup User.Coin
|
|
|
*
|
|
|
*/
|
|
|
- public function updateAddr(Request $request) {
|
|
|
+ public function updateAddr(Request $request)
|
|
|
+ {
|
|
|
list($symbol, $addr) = UtilService::postMore([
|
|
|
['symbol', ''],
|
|
|
['addr', ''],
|
|
|
@@ -257,13 +340,14 @@ class UserCoinController {
|
|
|
* @apiGroup User.Coin
|
|
|
*
|
|
|
*/
|
|
|
- public function transfer(Request $request) {
|
|
|
+ public function transfer(Request $request)
|
|
|
+ {
|
|
|
list($symbol, $amount) = UtilService::postMore([
|
|
|
['symbol', ''],
|
|
|
['amount', 0.0]
|
|
|
], $request, true);
|
|
|
|
|
|
- if(!$symbol) {
|
|
|
+ if (!$symbol) {
|
|
|
return app('json')->fail('参数不可为空');
|
|
|
}
|
|
|
$meta = DictCoin::where('symbol', $symbol)->find();
|
|
|
@@ -271,16 +355,16 @@ class UserCoinController {
|
|
|
return app('json')->fail('未找到目标');
|
|
|
}
|
|
|
$meta = $meta->toArray();
|
|
|
- if ($amount < $meta['min_withdrawal']){
|
|
|
+ if ($amount < $meta['min_withdrawal']) {
|
|
|
return app('json')->fail('未达到最低限额');
|
|
|
}
|
|
|
$uid = $request->uid();
|
|
|
- $userCoin = UserCoin::where(['uid'=>$uid, 'symbol'=>$symbol])->find();
|
|
|
+ $userCoin = UserCoin::where(['uid' => $uid, 'symbol' => $symbol])->find();
|
|
|
if (!$userCoin) {
|
|
|
return app('json')->fail('不适用的用户');
|
|
|
}
|
|
|
$userCoin = $userCoin->toArray();
|
|
|
- if(!$userCoin['addr']) {
|
|
|
+ if (!$userCoin['addr']) {
|
|
|
return app('json')->fail('地址未设置');
|
|
|
}
|
|
|
if ($userCoin['balance'] < $amount) {
|
|
|
@@ -292,8 +376,8 @@ class UserCoinController {
|
|
|
}
|
|
|
// transfer
|
|
|
UserCoin::beginTrans();
|
|
|
- $left = bcsub($userCoin['balance'], $amount, 8);
|
|
|
- $res1 = UserCoin::where(['uid'=>$uid, 'symbol'=>$symbol])->update(['balance'=>$left]);
|
|
|
+ $left = twsub($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;
|
|
|
@@ -303,4 +387,4 @@ class UserCoinController {
|
|
|
}
|
|
|
return app('json')->successful('成功,已开始审核');
|
|
|
}
|
|
|
-}
|
|
|
+}
|