Kaynağa Gözat

Merge branch 'master' of http://124.222.171.90:10880/Teapay/RWACha-API

* 'master' of http://124.222.171.90:10880/Teapay/RWACha-API:
  最低转让价格
  公告
  最低转让费
jason 11 ay önce
ebeveyn
işleme
f42219d392

+ 2 - 0
CURD.md

@@ -18,6 +18,8 @@ php think crud -t ledger_declaration_change -c ledger/LedgerDeclarationChange -m
 
 
 
+ composer require google/apiclient:^2.0 --ignore-platform-req=ext-gmp
+
 
 
 

+ 9 - 3
application/admin/controller/user/User.php

@@ -7,7 +7,7 @@ use app\common\model\LedgerWalletModel;
 use app\common\model\TeamLevelModel;
 use app\common\model\UserModel;
 use Exception;
-use fast\Action;
+use fast\GoogleAuthenticator;
 use fast\Asset;
 use fast\Common;
 use fast\MembershipLevel;
@@ -115,7 +115,12 @@ class User extends Backend
             $wallet             = (new LedgerWalletModel())->get($ids);
             $row['power']       = $wallet['power'] ?? "-";
 
+            $google=new GoogleAuthenticator();
+            //生成验证秘钥
+            //$secret=$google->createSecret();
+            $qrCodeUrl = $google->getQRCodeGoogleUrl('RWACHA',  config('google_secret'));
             $this->view->assign('row', $row);
+            $this->view->assign('qrCodeUrl', $qrCodeUrl);
             return $this->view->fetch();
         }
         $params = $this->request->post('row/a');
@@ -123,12 +128,13 @@ class User extends Backend
             $this->error(__('Parameter %s can not be empty', ''));
         }
         $params = $this->preExcludeFields($params);
-
         $newLevelId = $params['team_level_id'];
         if($newLevelId > 0 && empty((new TeamLevelModel())->getTeamLevelName($newLevelId))){
             $this->error('会员等级错误');
         }
-
+        $google=new GoogleAuthenticator();
+        $checkResult = $google->verifyCode(config('google_secret'), $params['code'], 6);
+        if (!$checkResult) $this->error('谷歌验证码错误');
         //茶宝
         $newPower       = bcadd($params['new_power'], 0, 6);
         // 启动事务

+ 5 - 1
application/admin/view/product/popular/add.html

@@ -26,9 +26,13 @@
 
     <div class='form-group'>
         <label class="control-label col-xs-12 col-sm-2">{:__('Pv')}:</label>
-        <div class="col-xs-12 col-sm-8">
+        <div class="col-xs-12 col-sm-3">
             <input id="c-pv" data-rule="required" class="form-control" step="0.01" name="row[pv]" type="number" value="0.00">
         </div>
+        <label class="control-label col-xs-12 col-sm-2">{:__('最低转让价')}:</label>
+        <div class="col-xs-12 col-sm-3">
+            <input id="c-min_transfer" data-rule="required" class="form-control" step="0.01" name="row[min_transfer]" type="number" value="0.00">
+        </div>
     </div>
 
     <div class="form-group">

+ 5 - 1
application/admin/view/product/popular/edit.html

@@ -20,9 +20,13 @@
 
     <div class='form-group'>
         <label class="control-label col-xs-12 col-sm-2">{:__('Pv')}:</label>
-        <div class="col-xs-12 col-sm-8">
+        <div class="col-xs-12 col-sm-3">
             <input id="c-pv" data-rule="required" class="form-control" step="0.01" name="row[pv]" type="number" value="{$row.pv|htmlentities}">
         </div>
+        <label class="control-label col-xs-12 col-sm-2">{:__('最低转让价')}:</label>
+        <div class="col-xs-12 col-sm-3">
+            <input id="c-min_transfer" data-rule="required" class="form-control" step="0.01" name="row[min_transfer]" type="number" value="{$row.min_transfer|htmlentities}">
+        </div>
     </div>
 
     <div class="form-group">

+ 13 - 1
application/admin/view/user/user/edit.html

@@ -24,11 +24,23 @@
     <div class="form-group">
         <label class="control-label col-xs-12 col-sm-2">茶宝调整(+/-):</label>
         <div class="col-xs-12 col-sm-8">
-            <label for="c-power"></label>
             <input id="c-power" class="form-control" placeholder="不填则不调整" name="row[new_power]" type="text" value="" />
         </div>
     </div>
 
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2">谷歌验证:</label>
+        <div class="col-xs-12 col-sm-8">
+            <input id="c-code" class="form-control" data-rule="required" placeholder="请输入验证码" name="row[code]" type="text" value="" />
+        </div>
+    </div>
+    <div class="form-group">
+        <label class="control-label col-xs-12 col-sm-2"></label>
+        <div class="col-xs-12 col-sm-8">
+            <img src="{$qrCodeUrl}" />
+        </div>
+    </div>
+ 
     <div class="form-group">
         <label class="control-label col-xs-12 col-sm-2">会员等级调整:</label>
         <div class="col-xs-8">

+ 1 - 1
application/api/controller/Announcement.php

@@ -20,7 +20,7 @@ class Announcement extends Api
     public function list()
     {
         $paginator = (new AnnouncementModel)
-            ->where('type', 1)
+            ->where('type_id', 1)
             ->where('status', 1)
             ->order('id DESC,weigh desc')
             ->field('id,img_url,createtime,' . $this->lan.'_title as title')

+ 6 - 3
application/api/controller/Order.php

@@ -125,7 +125,7 @@ class Order extends Api
      * 订单转让
      * @return void
      */
-    public function transfer(ProductOrder $productOrder, ProductTransfer $productTransfer, UserModel $userModel)
+    public function transfer(ProductOrder $productOrder, ProductTransfer $productTransfer, ProductPopular $productPopular)
     {   
         $params = $this->request->post();
         $validate = \think\Loader::validate('Order');
@@ -137,7 +137,10 @@ class Order extends Api
             $order_info = $productOrder->where('id', $params['order_id'])->where('status', $productOrder::Paid)->find();
             if(empty($order_info)) throw new Exception(__("订单不存在"));
 
-            //转让订单
+            $min_transfer = $productPopular::where('id', $order_info->order_id)->value('min_transfer');
+            if(bccomp($params['price'],$min_transfer, 2) < 0) throw new Exception(__("当前订单最低转让金额为").$min_transfer);
+
+            //转让订单  min_transfer
             $fee = getConfig('transfer_fee');
             $feeAmount = bcmul($params['price'], $fee, 2) ;
             $productTransfer::setTransferOrder($this->auth->id, $order_info['product_id'], $order_info['area_id'], $feeAmount, $params);
@@ -148,7 +151,7 @@ class Order extends Api
             Db::commit();
         } catch (Exception $e) {
             Db::rollback();
-            $this->error('提交失败:' . $e->getMessage());
+            $this->error($e->getMessage());
         }
         $this->success('ok');
     }

+ 1 - 0
application/api/controller/Product.php

@@ -27,6 +27,7 @@ class Product extends Api
      */
     public function getPopularList(ProductsModel $productsModel, ProductPopular $productPopular, ProductLists $productLists)
     {
+
         $item = $productsModel->where('status', 1)->column('id,'.$this->lan.'_title as title');
         $resp = array();
         foreach ($item as $kk =>$val) {

+ 1 - 0
application/api/lang/en.php

@@ -26,4 +26,5 @@ return [
     '赠送支出'                               => 'Gift Expenses',
     '赠送收款'                                                                    => 'Gift Receipt',
     '抢购未开始'                                                                  => 'The flash sale has not started yet',
+    '当前订单最低转让金额为'                                                       => 'The current minimum transfer amount for an order is',
 ]; 

+ 1 - 0
application/api/lang/zh-cn.php

@@ -115,5 +115,6 @@ return [
     '社区津贴'                                                                    => '社区津贴',
     '服务津贴'                                                                    => '服务津贴',
     '共创津贴'                                                                    => '共创津贴',
+    '当前订单最低转让金额为'                                                       => '当前订单最低转让金额为',
 
 ];

+ 1 - 1
application/common/model/ProductTransfer.php

@@ -3,7 +3,7 @@
 namespace app\common\model;
 
 use think\Model;
-
+use Exception;
 class ProductTransfer extends Model
 {
 

+ 1 - 0
application/config.php

@@ -318,4 +318,5 @@ return [
     'community_ratio' => 0.035,
     'service_ratio'   => 0.05, //服务津贴
     'together_ratio'  => 0.05, //共创津贴
+    'google_secret'   => 'EHAO77VUMH5XHFR6', //谷歌验证私钥
 ];

+ 252 - 0
extend/fast/GoogleAuthenticator.php

@@ -0,0 +1,252 @@
+<?php
+namespace fast;
+/**
+ * PHP Class for handling Google Authenticator 2-factor authentication.
+ *
+ * @author Michael Kliewe
+ * @copyright 2012 Michael Kliewe
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ *
+ * @link http://www.phpgangsta.de/
+ */
+class GoogleAuthenticator
+{
+    protected $_codeLength = 6;
+
+    /**
+     * Create new secret.
+     * 16 characters, randomly chosen from the allowed base32 characters.
+     *
+     * @param int $secretLength
+     *
+     * @return string
+     */
+    public function createSecret($secretLength = 16)
+    {
+        $validChars = $this->_getBase32LookupTable();
+
+        // Valid secret lengths are 80 to 640 bits
+        if ($secretLength < 16 || $secretLength > 128) {
+            throw new \Exception('Bad secret length');
+        }
+        $secret = '';
+        $rnd = false;
+        if (function_exists('random_bytes')) {
+            $rnd = random_bytes($secretLength);
+        } elseif (function_exists('mcrypt_create_iv')) {
+            $rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM);
+        } elseif (function_exists('openssl_random_pseudo_bytes')) {
+            $rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong);
+            if (!$cryptoStrong) {
+                $rnd = false;
+            }
+        }
+        if ($rnd !== false) {
+            for ($i = 0; $i < $secretLength; ++$i) {
+                $secret .= $validChars[ord($rnd[$i]) & 31];
+            }
+        } else {
+            throw new \Exception('No source of secure random');
+        }
+
+        return $secret;
+    }
+
+    /**
+     * Calculate the code, with given secret and point in time.
+     *
+     * @param string   $secret
+     * @param int|null $timeSlice
+     *
+     * @return string
+     */
+    public function getCode($secret, $timeSlice = null)
+    {
+        if ($timeSlice === null) {
+            $timeSlice = floor(time() / 30);
+        }
+
+        $secretkey = $this->_base32Decode($secret);
+
+        // Pack time into binary string
+        $time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice);
+        // Hash it with users secret key
+        $hm = hash_hmac('SHA1', $time, $secretkey, true);
+        // Use last nipple of result as index/offset
+        $offset = ord(substr($hm, -1)) & 0x0F;
+        // grab 4 bytes of the result
+        $hashpart = substr($hm, $offset, 4);
+
+        // Unpak binary value
+        $value = unpack('N', $hashpart);
+        $value = $value[1];
+        // Only 32 bits
+        $value = $value & 0x7FFFFFFF;
+
+        $modulo = pow(10, $this->_codeLength);
+
+        return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT);
+    }
+
+    /**
+     * Get QR-Code URL for image, from google charts.
+     *
+     * @param string $name
+     * @param string $secret
+     * @param string $title
+     * @param array  $params
+     *
+     * @return string
+     */
+    public function getQRCodeGoogleUrl($name, $secret, $title = null, $params = array())
+    {
+        $width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200;
+        $height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200;
+        $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M';
+
+        $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
+        if (isset($title)) {
+            $urlencoded .= urlencode('&issuer='.urlencode($title));
+        }
+
+        return "https://api.qrserver.com/v1/create-qr-code/?data=$urlencoded&size=${width}x${height}&ecc=$level";
+    }
+
+    /**
+     * Check if the code is correct. This will accept codes starting from $discrepancy*30sec ago to $discrepancy*30sec from now.
+     *
+     * @param string   $secret
+     * @param string   $code
+     * @param int      $discrepancy      This is the allowed time drift in 30 second units (8 means 4 minutes before or after)
+     * @param int|null $currentTimeSlice time slice if we want use other that time()
+     *
+     * @return bool
+     */
+    public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null)
+    {
+        if ($currentTimeSlice === null) {
+            $currentTimeSlice = floor(time() / 30);
+        }
+
+        if (strlen($code) != 6) {
+            return false;
+        }
+
+        for ($i = -$discrepancy; $i <= $discrepancy; ++$i) {
+            $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
+            if ($this->timingSafeEquals($calculatedCode, $code)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Set the code length, should be >=6.
+     *
+     * @param int $length
+     *
+     * @return PHPGangsta_GoogleAuthenticator
+     */
+    public function setCodeLength($length)
+    {
+        $this->_codeLength = $length;
+
+        return $this;
+    }
+
+    /**
+     * Helper class to decode base32.
+     *
+     * @param $secret
+     *
+     * @return bool|string
+     */
+    protected function _base32Decode($secret)
+    {
+        if (empty($secret)) {
+            return '';
+        }
+
+        $base32chars = $this->_getBase32LookupTable();
+        $base32charsFlipped = array_flip($base32chars);
+
+        $paddingCharCount = substr_count($secret, $base32chars[32]);
+        $allowedValues = array(6, 4, 3, 1, 0);
+        if (!in_array($paddingCharCount, $allowedValues)) {
+            return false;
+        }
+        for ($i = 0; $i < 4; ++$i) {
+            if ($paddingCharCount == $allowedValues[$i] &&
+                substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) {
+                return false;
+            }
+        }
+        $secret = str_replace('=', '', $secret);
+        $secret = str_split($secret);
+        $binaryString = '';
+        for ($i = 0; $i < count($secret); $i = $i + 8) {
+            $x = '';
+            if (!in_array($secret[$i], $base32chars)) {
+                return false;
+            }
+            for ($j = 0; $j < 8; ++$j) {
+                $x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
+            }
+            $eightBits = str_split($x, 8);
+            for ($z = 0; $z < count($eightBits); ++$z) {
+                $binaryString .= (($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48) ? $y : '';
+            }
+        }
+
+        return $binaryString;
+    }
+
+    /**
+     * Get array with all 32 characters for decoding from/encoding to base32.
+     *
+     * @return array
+     */
+    protected function _getBase32LookupTable()
+    {
+        return array(
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', //  7
+            'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 15
+            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 23
+            'Y', 'Z', '2', '3', '4', '5', '6', '7', // 31
+            '=',  // padding char
+        );
+    }
+
+    /**
+     * A timing safe equals comparison
+     * more info here: http://blog.ircmaxell.com/2014/11/its-all-about-time.html.
+     *
+     * @param string $safeString The internal (safe) value to be checked
+     * @param string $userString The user submitted (unsafe) value
+     *
+     * @return bool True if the two strings are identical
+     */
+    private function timingSafeEquals($safeString, $userString)
+    {
+        if (function_exists('hash_equals')) {
+            return hash_equals($safeString, $userString);
+        }
+        $safeLen = strlen($safeString);
+        $userLen = strlen($userString);
+
+        if ($userLen != $safeLen) {
+            return false;
+        }
+
+        $result = 0;
+
+        for ($i = 0; $i < $userLen; ++$i) {
+            $result |= (ord($safeString[$i]) ^ ord($userString[$i]));
+        }
+
+        // They are only identical strings if $result is exactly 0...
+        return $result === 0;
+    }
+}

+ 1 - 0
public/assets/js/backend/product/popular.js

@@ -32,6 +32,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         {field: 'title', title: __('期数')},
                         {field: 'price', title: __('Price'), operate: false},
                         {field: 'cost_price', title: __('Cost_price'), operate:false},
+                        {field: 'min_transfer', title: __('最低转让价'), operate:false}, 
                         {field: 'stock', title: __('Stock'), operate:false},
                         {field: 'weigh', title: __('Weigh'), operate: false},
                         {field: 'start_time', title: __('Start_time'), operate:'RANGE', addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},

+ 18 - 3
public/assets/js/backend/user/user.js

@@ -6,8 +6,8 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
             Table.api.init({
                 extend: {
                     index_url: 'user/user/index' + location.search,
-                    add_url: 'user/user/add',
-                    edit_url: 'user/user/edit',
+                    //add_url: 'user/user/add',
+                    //edit_url: 'user/user/edit',
                     del_url: 'user/user/del',
                     multi_url: 'user/user/multi',
                     import_url: 'user/user/import',
@@ -46,7 +46,22 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
                         {field: 'effective_time', title: __('Effective_time'), operate: false, addclass:'datetimerange', autocomplete:false, formatter: Table.api.formatter.datetime},
                         {field: 'is_login', title: __('登录锁定'), searchList: {"1":__('Yes'),"0":__('No')}, formatter: Table.api.formatter.toggle},
                         {field: 'is_withdraw', title: __('提现锁定'), searchList: {"1":__('Yes'),"0":__('No')}, formatter: Table.api.formatter.toggle},
-                        {field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                        //{field: 'operate', title: __('Operate'), table: table, events: Table.api.events.operate, formatter: Table.api.formatter.operate}
+                        //茶宝调整的功能
+                        {
+                            field: 'operate', title: __('Operate'), table: table,
+                            events: Table.api.events.operate,
+                            buttons: [{
+                                name: 'approve',
+                                text: '茶宝调整',
+                                title: '茶宝调整',
+                                classname: 'btn btn-xs btn-info btn-dialog btn-receivable',
+                                icon: 'fa fa-pencil',
+                                url: 'user/user/edit',
+                                extend: 'data-area=\'["50%", "70%"]\'',
+                            },],
+                            formatter: Table.api.formatter.operate
+                        }
                     ]
                 ]
             });