Browse Source

1. 重构代码,把 redis 相关操作整理在一起,key, field 太乱不好管理
2. 活动相关操作整理在一起
3. 支付平台相关操作,三方登录操作整理在一起(WIP)
4. 异步操作整理在一起

joe 4 năm trước cách đây
mục cha
commit
4820146e5c
42 tập tin đã thay đổi với 333 bổ sung240 xóa
  1. 7 8
      app/admin/controller/Test.php
  2. 1 1
      app/admin/controller/finance/UserExtract.php
  3. 3 2
      app/api/controller/AuthController.php
  4. 22 8
      app/api/controller/PublicController.php
  5. 39 17
      app/api/controller/board/UserBoardController.php
  6. 6 6
      app/api/controller/coin/UserCoinController.php
  7. 24 12
      app/api/controller/user/UserExtractController.php
  8. 5 4
      app/api/controller/user/UserNotificationController.php
  9. 3 7
      app/api/controller/wechat/AuthController.php
  10. 17 16
      app/models/redis/SystemCarousel.php
  11. 0 56
      app/models/redis/UserHash.php
  12. 7 8
      crmeb/command/Task.php
  13. 1 2
      crmeb/repositories/ShortLetterRepositories.php
  14. 5 5
      crmeb/subscribes/TimerSubscribe.php
  15. 1 1
      crmeb/subscribes/UserSubscribe.php
  16. 2 2
      docs/apidoc.json
  17. 1 1
      tests/UserTaskTest.php
  18. 13 14
      tw/async/activities/ActivityCalc.php
  19. 3 2
      tw/async/activities/ClearanceCalc.php
  20. 2 2
      tw/async/activities/LuckyCalc.php
  21. 1 2
      tw/async/activities/LuckyExtACalc.php
  22. 1 2
      tw/async/activities/LuckyExtBCalc.php
  23. 1 0
      tw/async/activities/README.md
  24. 10 1
      tw/async/tasks/AsyncClass.php
  25. 10 2
      tw/async/tasks/AsyncSms.php
  26. 1 0
      tw/async/tasks/README.md
  27. 3 4
      tw/async/tasks/Task.php
  28. 3 1
      tw/async/tasks/UserTaskClass.php
  29. 1 1
      tw/async/tasks/WechatNotify.php
  30. 23 0
      tw/redis/ActivityPool.php
  31. 21 0
      tw/redis/BoardRds.php
  32. 18 0
      tw/redis/CarouselRds.php
  33. 21 0
      tw/redis/UserRds.php
  34. 5 4
      tw/redis/traits/ListTrait.php
  35. 3 0
      tw/services/auth/README.md
  36. 0 0
      tw/services/auth/ThirdPartyLogin.php
  37. 14 16
      tw/services/controller/PaymentService.php
  38. 1 0
      tw/services/controller/README.md
  39. 0 0
      tw/services/payment/Alipay.php
  40. 33 33
      tw/services/payment/MachantPay.php
  41. 1 0
      tw/services/payment/README.md
  42. 0 0
      tw/services/payment/WechatPay.php

+ 7 - 8
app/admin/controller/Test.php

@@ -2,9 +2,6 @@
 
 namespace app\admin\controller;
 
-
-use crmeb\services\async\LuckyCalc;
-use crmeb\services\async\ClearanceCalc;
 use crmeb\utils\Redis;
 use think\facade\Log;
 use app\api\controller\board\UserBoardController;
@@ -12,11 +9,13 @@ use app\models\redis\SystemCarousel;
 use app\models\system\SystemPool;
 use app\models\user\UserSearch;
 use app\models\user\WechatUser;
-use crmeb\payment\MachantPay;
-use crmeb\services\async\LuckyExtACalc;
-use crmeb\services\async\LuckyExtBCalc;
-use crmeb\services\async\task\AsyncClass;
-use crmeb\services\async\task\WechatNotify;
+use tw\services\payment\MachantPay;
+use tw\async\activities\LuckyCalc;
+use tw\async\activities\ClearanceCalc;
+use tw\async\activities\LuckyExtACalc;
+use tw\async\activities\LuckyExtBCalc;
+use tw\async\tasks\AsyncClass;
+use tw\async\tasks\WechatNotify;
 use think\facade\Config;
 use RobThree\Auth\TwoFactorAuth;
 use RobThree\Auth\TwoFactorAuthException;

+ 1 - 1
app/admin/controller/finance/UserExtract.php

@@ -13,7 +13,7 @@ use think\facade\Route as Url;
 use crmeb\services\JsonService;
 use app\admin\model\user\UserExtract as UserExtractModel;
 use crmeb\services\{UtilService as Util, FormBuilder as Form};
-use crmeb\services\payment\PaymentService;
+use tw\services\controller\PaymentService;
 use app\admin\model\user\UserExtract as UserExtractAdmin;
 
 /**

+ 3 - 2
app/api/controller/AuthController.php

@@ -550,7 +550,7 @@ class AuthController
      * @apiName GetNotifications
      * @apiGroup Message
      * 
-     * @apiSuccessExample:
+     * @apiSuccessExample Succeed
      * {
      *  "status": 200,
      *  "msg": "ok",
@@ -566,7 +566,8 @@ class AuthController
      *      ]
      *  }
      * }
-     * @apiErrorExample:
+     * 
+     * @apiErrorExample Failed
      * {
      *  "status": 200,
      *  "msg": "ok",

+ 22 - 8
app/api/controller/PublicController.php

@@ -205,8 +205,22 @@ class PublicController
     }
 
     /**
-     * 物流公司
-     * @return mixed
+     * @api {get} /logistics 物流公司列表
+     * @apiName GetLogistics
+     * @apiGroup User
+     * 
+     * @apiParam none None
+     * 
+     * @apiBody {string[]} data 物流公司列表
+     * 
+     * @apiSuccessExample Succeed
+     * {
+     * 
+     * }
+     * @apiErrorExample Failed
+     * {
+     * }
+     * 
      */
     public function logistics()
     {
@@ -245,7 +259,7 @@ class PublicController
      * 
      * @apiParam {none} none None
      * 
-     * @apiSuccess
+     * @apiSuccess {none} none None
      */
     public function user_share(Request $request)
     {
@@ -354,10 +368,14 @@ class PublicController
      * @apiQuery {string} callback 回调函数名称
      * @apiQuery {string} begin 起始时间
      * @apiQuery {string} end 截至时间
-     * @apiQuery {array} activity 
+     * @apiQuery {array} activity 活动代码列表
      * @apiQuery {int} page
      * @apiQuery {int} limit
      * 
+     * @apiSuccessExample Succeed
+     * "callback([{"ts":"YYYY-mm-dd HH:MM:SS","activity":1,"result":20},{"ts":"YYYY-mm-dd HH:MM:SS","activity":1,"result":20}])
+     * @apiErrorExample Failed
+     * "callback([])
      */
     public function get_activities_result()
     {
@@ -369,10 +387,6 @@ class PublicController
             ['page', 0],
             ['limit', 0],
         ]);
-        // $arr = json_encode([
-        //     'a' => 3,
-        //     'b' => 4,
-        // ]);
         $callback = $data['callback'];
         $res = SystemAwardHistory::getList(
             $data['activity'],

+ 39 - 17
app/api/controller/board/UserBoardController.php

@@ -6,35 +6,58 @@ use crmeb\services\UtilService;
 use app\models\board\UserBoard;
 use \think\facade\Config;
 use crmeb\utils\Redis;
+use tw\redis\BoardRds;
 
 /**
- * @api {get} /boards 获取排行榜
- * @apiName GetBoards
- * @apiGroup Activity
- * 
  * 协议见文档 docs/board.md, 协议可能变更
  *
  * 使用 UserBoard Model, 数据表为 store_order_cart_info
  *
+ * 策略:定时利用 sql 生成排行榜,缓存 json 到 redis
  *
  * Class UserBoardController
  * @package app\api\controller\board
  */
 class UserBoardController
 {
-    protected static $DAILY_WIN_MONEY = 1;
-    public static $RDS_BOARD_KEY = 'tmp:board';
-
+    /**
+     * @api {get} /boards 获取排行榜
+     * @apiName GetBoards
+     * @apiGroup Activity
+     * 
+     * @apiParam {int=1,2} type 1|2 日榜单|周榜单
+     * 
+     * @apiSuccess {string} banner 顶部 banner 图片
+     * @apiSuccess {string} name 排行榜名称, 暂未使用
+     * @apiSuccess {Object[]} board 排行榜数据
+     * 
+     * @apiSuccessExample
+     * {
+     *  "banner": "http://xxx.png",
+     *  "name": "daily board",
+     *  "board": [{
+     *      "uid": 123,
+     *      "avatar": "http://2.png",
+     *      "nickname": "xxx",
+     *      "level": 1,
+     *      "value": 23.3,
+     *      "border": 1,
+     *      "vip": 2
+     *  }]
+     * }
+     */
     public function boards()
     {
         [$type,] = UtilService::getMore([
             ['type', 1],
         ], null, true);
-        $s = $this->get_cached_board();
-        $board = json_decode($s, true);
+        $board = json_decode((new BoardRds)->get($type), true);
         return app('json')->successful('ok', $board);
     }
 
+    /**
+     * 读库
+     */
     protected function daily_win_money()
     {
         $board = new UserBoard();
@@ -51,15 +74,14 @@ class UserBoardController
         );
     }
 
+    /**
+     * 缓存数据库取得的排行结果,定时调用
+     */
     public function cache_board()
     {
-        $redis = Redis::instance();
-        $redis->set(self::$RDS_BOARD_KEY, json_encode($this->daily_win_money()));
-    }
-
-    protected function get_cached_board()
-    {
-        $redis = Redis::instance();
-        return $redis->get(self::$RDS_BOARD_KEY);
+        $res = (new BoardRds)->set(BoardRds::DAILY, json_encode($this->daily_win_money()));
+        if (!$res) {
+            errlog("cache_board() returned $res");
+        }
     }
 }

+ 6 - 6
app/api/controller/coin/UserCoinController.php

@@ -2,7 +2,7 @@
 namespace app\api\controller\coin;
 
 use app\models\coin\UserCoinTransfer;
-use app\models\redis\UserHash;
+use tw\redis\UserRds;
 use app\models\store\StoreOrderCartInfo;
 use app\models\system\DictCoin;
 use app\models\user\UserCoin;
@@ -41,13 +41,13 @@ class UserCoinController {
         }
 
         $uid = $request->uid();
-        $mymining = UserHash::mining_get($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);
-            UserHash::mining_set($uid, $mymining);
+            (new UserRds)->sets($uid, $mymining);
         } else {
             $mymining['total'] = UserCoin::where('uid', $uid)->where('symbol', $symbol)->value('balance') ?? 0.0;
         }
@@ -141,7 +141,7 @@ class UserCoinController {
             return app('json')->fail('本活动未开启');
         }
         // 是否已经开启
-        $mining = UserHash::mining_get($uid);
+        $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'])) {
@@ -173,7 +173,7 @@ class UserCoinController {
         $hour = random_int($hours[0], $hours[1]);
         $stop = $now + $hour * 60 * 60;
 
-        $suc = UserHash::mining_set($uid, [
+        $suc = (new UserRds)->sets($uid, [
             'boot' => $now,
             'step' => $step,
             'stop' => $stop,
@@ -185,7 +185,7 @@ class UserCoinController {
             'ts' => $now + 1,
             'order_id' => $orderId,
         ]);
-
+        
         if ($suc != 0 && $suc != 1) {
             StoreOrderCartInfo::setMining($orderId, 0);
             return app('json')->fail('未成功执行');

+ 24 - 12
app/api/controller/user/UserExtractController.php

@@ -5,8 +5,8 @@ namespace app\api\controller\user;
 use app\models\user\UserBill;
 use app\models\user\UserExtract;
 use app\Request;
-use crmeb\payment\MachantPay;
-use crmeb\services\payment\PaymentService;
+use tw\services\payment\MachantPay;
+use tw\services\controller\PaymentService;
 use crmeb\services\UtilService;
 use think\facade\Log;
 use tw\redis\UserRds;
@@ -23,11 +23,17 @@ class UserExtractController
      * @apiName ExtractBank
      * @apiGroup Brokerage
      * 
+     * @apiDescription 体现前请求,用户显示和客户端过滤
+     * 
      * @apiSuccess {float} broken_commission 冻结佣金
      * @apiSuccess {float} brokerage_price 总佣金
      * @apiSuccess {float} commissionCount  可提现佣金
-     * @apiSuccess {string[]} extractBank   银行列表
+     * @apiSuccess {string[]} extractBank   可体现银行列表
      * @apiSuccess {float} minPrice 最小提现金额
+     * @apiSuccess {string} wxpayName 微信实名(以下 4 个参数用于记忆功能)
+     * @apiSuccess {string} bankCardNo 银行卡号
+     * @apiSuccess {string} bankUser 银行户名
+     * @apiSuccess {string} bankName 银行名
      */
     public function bank(Request $request)
     {
@@ -57,12 +63,16 @@ class UserExtractController
         $data['extractBank'] = explode("\n", is_array($extractBank) ? (isset($extractBank[0]) ? $extractBank[0] : $extractBank) : $extractBank);
         $data['minPrice'] = sys_config('user_extract_min_price'); //提现最低金额
         // 输入记忆
-        $ur = new UserRds();
-        $cach = $ur->gets($user['uid'], ['wxpayName', 'bankCardNo', 'bankUser', 'bankName']);
-        $data['wxpayName']  = $cach['wxpayName'] ? $cach['wxpayName'] : '';
-        $data['bankCardNo'] = $cach['bankCardNo'] ? $cach['bankCardNo'] : '';
-        $data['bankUser']   = $cach['bankUser'] ? $cach['bankUser'] : '';
-        $data['bankName']   = $cach['bankName'] ? $cach['bankName'] : '';
+        $cach = (new UserRds)->gets($user['uid'], [
+            UserRds::FILED_WXPAYNAME,
+            UserRds::FIELD_BANKCARDNO,
+            UserRds::FIELD_BANKUSER,
+            UserRds::FIELD_BANKNAME,
+        ]);
+        $data['wxpayName']  = $cach[UserRds::FILED_WXPAYNAME] ? $cach[UserRds::FILED_WXPAYNAME] : '';
+        $data['bankCardNo'] = $cach[UserRds::FIELD_BANKCARDNO] ? $cach[UserRds::FIELD_BANKCARDNO] : '';
+        $data['bankUser']   = $cach[UserRds::FIELD_BANKUSER] ? $cach[UserRds::FIELD_BANKUSER] : '';
+        $data['bankName']   = $cach[UserRds::FIELD_BANKNAME] ? $cach[UserRds::FIELD_BANKNAME] : '';
 
         return app('json')->successful($data);
     }
@@ -75,6 +85,8 @@ class UserExtractController
      * @apiBody {int} extract_type 提现类型/通道
      * @apiBody {float} money 提现金额
      * 
+     * @apiDescription 用于提示
+     * 
      * @apiSuccess {float} rate 手续费率
      * @apiSuccess {float} min 最少收取
      * @apiSuccess {float} max 最多收取
@@ -137,13 +149,13 @@ class UserExtractController
      * @apiBody {string} name 微信实名/银行户名/支付宝帐号
      * @apiBody {string} weixin 微信实名
      * 
-     * @apiSuccessExample:
+     * @apiSuccessExample Succeed
      * {
      * 
      * }
-     * @apiErrorExample:
+     * @apiErrorExample Failed
      * {
-     * 
+     *  
      * }
      * 
      */

+ 5 - 4
app/api/controller/user/UserNotificationController.php

@@ -2,13 +2,14 @@
 namespace app\api\controller\user;
 
 use app\models\coin\UserCoinTransfer;
-use app\models\redis\UserHash;
 use app\models\system\DictCoin;
 use app\Request;
 use crmeb\services\JsonService;
 use think\facade\Config;
 use think\facade\Log;
 use think\facade\Cache;
+use tw\redis\UserRds;
+
 /**
  *
  * activities json 配置:
@@ -30,10 +31,10 @@ class UserNotificationController {
     public function snapshot(Request $request) {
         $uid = $request->uid();
         // 未读消息
-        $unread = UserHash::unread_get($uid);
+        $unread = (new UserRds)->get($uid, UserRds::FIELD_UNREAD) ?? 0;
 
         return app('json')->successful([
-            'unread': intval($unread),
-        ]));
+            'unread'=> intval($unread),
+        ]);
     }
 }

+ 3 - 7
app/api/controller/wechat/AuthController.php

@@ -13,7 +13,6 @@ use app\models\user\UserToken;
 use app\models\user\User;
 use think\facade\Cache;
 use crmeb\services\SubscribeTemplateService;
-use think\facade\Log;
 use tw\redis\UserRds;
 
 /**
@@ -66,7 +65,6 @@ class AuthController
             if (!$uid && $openId != '') {
                 $uid = WechatUser::where(['routine_openid' => $openId])->where('user_type', 'routine')->value('uid');
             }
-            Log::debug("uid=$uid");
             if (!$uid) {
                 return app('json')->successful([
                     'token' => '',
@@ -95,8 +93,7 @@ class AuthController
             $cache_key = md5(time() . $code);
             Cache::set('eb_api_code_' . $cache_key, $json2sess, SECONDS_OF_ONEDAY);
             // 获取用户上次刷新时间,距今超过 2 周就刷新
-            $ur = new UserRds();
-            $last = $ur->get($uid, 'last_refresh');
+            $last = (new UserRds)->get($uid, UserRds::FIELD_LASTREFRESH);
             if (time() - intval($last) >= SECONDS_OF_ONEDAY * 20) {
                 return app('json')->successful([
                     'token' => '',
@@ -116,7 +113,7 @@ class AuthController
                 'status' => 0,  // 登录成功
             ]);
         } catch (\Exception $e) {
-            Log::error(__FUNCTION__ . 'exception:' . $e->getMessage());
+            errlog(__FUNCTION__ . 'exception:' . $e->getMessage());
             return app('json')->fail('获取session_key失败');
         }
     }
@@ -249,8 +246,7 @@ class AuthController
             $token = UserToken::createToken($userInfo, 'routine');
         if ($token) {
             event('UserLogin', [$userInfo, $token]);
-            $ur = new UserRds();
-            $ur->set($uid, 'last_refresh', time());
+            (new UserRds)->set($uid, UserRds::FIELD_LASTREFRESH, time());
             return app('json')->successful('登陆成功!', [
                 'token' => $token->token,
                 'userInfo' => $userInfo,

+ 17 - 16
app/models/redis/SystemCarousel.php

@@ -1,38 +1,38 @@
 <?php
+
 namespace app\models\redis;
 
-use crmeb\utils\Redis;
+use tw\redis\CarouselRds;
 
 /**
  * Class SystemCarousel
  * @package app\models\redis
  */
-class SystemCarousel {
-
-    protected static $KEY = 'sys:carousel';
-
+class SystemCarousel
+{
     /*
      格式:
             {
-                 "text": "<span style=\"color:2343;\"></span>",
-                 "uri": "page/boards",
+                "text": "<span style=\"color:2343;\"></span>",
+                "uri": "page/boards",
             }
      uri 为客户端路由, 用于客户端跳转
      */
-    public static function add($text, $uri='', $h5uri='') {
+    public static function add($text, $uri = '', $h5uri = '')
+    {
         $val = json_encode([
             'id' => 0, // compatible
-            'info'=> $text,
+            'info' => $text,
             'url' => $uri,
             'wap_url' => $h5uri,
             'show' => '2',  // compatible
         ]);
-        $redis = Redis::instance();
-        return $redis->lpush(self::$KEY, $val);
+        return (new CarouselRds)->lpush('', $val);
     }
 
-    public static function getFirst($n) {
-        $arrStr = Redis::lRange(self::$KEY, 0, $n) ?? '[]';
+    public static function getFirst($n)
+    {
+        $arrStr = (new CarouselRds)->lrange('', 0, $n) ?? '[]';
         $rv = [];
         foreach ($arrStr as $str) {
             $rv[] = json_decode($str);
@@ -40,7 +40,8 @@ class SystemCarousel {
         return $rv;
     }
 
-    public static function removeTrash($start=10) {
-        return Redis::lTrim(self::$KEY, 0, $start - 1);
+    public static function removeTrash($start = 10)
+    {
+        return (new CarouselRds)->ltrim('', 0, $start - 1);
     }
-}
+}

+ 0 - 56
app/models/redis/UserHash.php

@@ -1,56 +0,0 @@
-<?php
-namespace app\models\redis;
-
-use crmeb\utils\Redis;
-use think\facade\Log;
-
-/**
- * Class UserHash
- * @package app\models\redis
- */
-class UserHash {
-    protected static $FIELD_UNREAD = 'unread';
-    protected static $FIELD_MINING = 'mining';
-
-    public static function key($uid) {
-        return 'user:'.$uid;
-    }
-
-    public static function unread_get($uid) {
-        return intval(Redis::hGet(self::key($uid), self::$FIELD_UNREAD)??0);
-    }
-
-    public static function unread_incr($uid, $by=1) {
-        return Redis::hIncrBy(self::key($uid), self::$FIELD_UNREAD, $by);
-    }
-
-    /*
-     格式:
-            {
-                "progress": 10,
-                "symbol": "etc",
-                "icon": "http://x.png",
-                "price": 20,
-                "ts": 1234,
-                "order_id": 12343,
-                "total": 20
-            }
-     */
-    public static function mining_get($uid) {
-        $sval = Redis::hGet(self::key($uid), self::$FIELD_MINING);
-        if ($sval) {
-            return json_decode($sval, true);
-        }
-        return $sval;
-    }
-
-    public static function mining_set($uid, $mining) {
-        if (!isset($mining['ts']) || !isset($mining['symbol']) ||
-            !isset($mining['progress']) || !isset($mining['order_id'])) {
-            Log::error('invalid mining value');
-            return false;
-        }
-
-        return Redis::hSet(self::key($uid), self::$FIELD_MINING, json_encode($mining));
-    }
-}

+ 7 - 8
crmeb/command/Task.php

@@ -2,16 +2,15 @@
 
 namespace crmeb\command;
 
-use crmeb\services\async\task\WechatNotify;
-use crmeb\services\async\task\AsyncSms;
-use crmeb\services\async\task\AsyncClass;
+use tw\async\tasks\WechatNotify;
+use tw\async\tasks\AsyncSms;
+use tw\async\tasks\AsyncClass;
 use think\console\Command;
 use think\console\Input;
 use think\console\input\Argument;
 use think\console\input\Option;
 use think\console\Output;
 use think\facade\Config;
-use think\facade\Log;
 use Workerman\Worker;
 use crmeb\utils\Beanstalk;
 
@@ -90,14 +89,14 @@ class Task extends Command
                     if ($t->getCmd() == $payload['cmd']) {
                         $retval = $t->exec($payload);
                         if (!$retval) {
-                            Log::warning('async task executed return false:' . $str_payload);
+                            warnlog('async task executed return false:' . $str_payload);
                         }
-                        Log::info("task: $str_payload return:". json_encode($retval));
+                        infolog("task: $str_payload return:". json_encode($retval));
                         $executed = true;
                     }
                 }
                 if (!$executed) {
-                    Log::error('task unsupported cmd:' . $str_payload);
+                    errlog('task unsupported cmd:' . $str_payload);
                     // Beanstalk::release($job, 1024, 30);
                     Beanstalk::delete($job);
                     continue;
@@ -105,7 +104,7 @@ class Task extends Command
 
                 Beanstalk::delete($job);
             } catch (\Exception $e) {
-                Log::error('task exception:' . $e->getMessage() . ' data:' . $job->getData());
+                errlog('task exception:' . $e->getMessage() . ' data:' . $job->getData());
                 // Beanstalk::release($job, 1024, 30); 
                 Beanstalk::delete($job); // testing
             }

+ 1 - 2
crmeb/repositories/ShortLetterRepositories.php

@@ -5,7 +5,6 @@ namespace crmeb\repositories;
 
 use app\admin\model\sms\SmsRecord;
 use crmeb\services\sms\Sms;
-use think\facade\Log;
 
 /**
  * 短信发送
@@ -36,7 +35,7 @@ class ShortLetterRepositories
             $res = $sms->send($phone, $template, $data);
             if ($res === false || $res === null) {
                 $errorSmg = $sms->getError();
-                Log::info($logMsg ?? $errorSmg);
+                infolog($logMsg ?? $errorSmg);
                 return $errorSmg;
             } else {
                 $record_id = 1; // $res['data']['id']; 都等于1,没啥用 

+ 5 - 5
crmeb/subscribes/TimerSubscribe.php

@@ -9,10 +9,10 @@ use app\models\store\StoreOrder;
 use app\models\store\StorePink;
 use app\models\user\UserToken;
 use app\models\redis\SystemCarousel;
-use crmeb\services\async\ClearanceCalc;
-use crmeb\services\async\LuckyCalc;
-use crmeb\services\async\LuckyExtACalc;
-use crmeb\services\async\LuckyExtBCalc;
+use tw\async\activities\ClearanceCalc;
+use tw\async\activities\LuckyCalc;
+use tw\async\activities\LuckyExtACalc;
+use tw\async\activities\LuckyExtBCalc;
 use think\facade\Db;
 use think\facade\Log;
 use app\admin\model\system\SystemLog;
@@ -106,7 +106,7 @@ class TimerSubscribe
             foreach($activities as $activity) {
                 $activity->calc();
             }
-            
+            // 刪除跑馬燈
             SystemCarousel::removeTrash();
 
             Log::warning('onTimer_60() activities calculated.');

+ 1 - 1
crmeb/subscribes/UserSubscribe.php

@@ -9,7 +9,7 @@ use app\admin\model\system\SystemAttachment;
 use app\admin\model\user\UserBill;
 use app\models\user\UserLevel;
 use app\models\user\UserNotice;
-use crmeb\services\async\task\AsyncClass;
+use tw\async\tasks\AsyncClass;
 use think\facade\Log;
 use think\facade\Config;
 

+ 2 - 2
docs/apidoc.json

@@ -3,11 +3,11 @@
     "version": "1.2.5",
     "description": "cross-client usage",
     "header": {
-        "title": "api header",
+        "title": "Introduction",
         "filename": "api_header.md"
     },
     "footer": {
-        "title": "api footer",
+        "title": "Conclusion",
         "filename": "api_footer.md"
     }
 }

+ 1 - 1
tests/UserTaskTest.php

@@ -8,7 +8,7 @@ use Monolog\Handler\StreamHandler;
 use PHPUnit\Framework\TestCase;
 use app\models\user\User;
 use crmeb\command\Task;
-use crmeb\services\async\task\AsyncClass;
+use tw\async\tasks\AsyncClass;
 use tw\async\tasks\UserTaskClass;
 
 class UserTaskTest extends TestCase

+ 13 - 14
crmeb/services/async/ActivityCalc.php → tw/async/activities/ActivityCalc.php

@@ -1,5 +1,6 @@
 <?php
-namespace crmeb\services\async;
+
+namespace tw\async\activities;
 
 use app\admin\model\user\User;
 use app\admin\model\user\UserBill;
@@ -8,9 +9,7 @@ use app\models\store\StoreOrderCartInfo;
 use app\models\system\SystemAwardHistory;
 use app\models\user\UserNotice;
 use crmeb\basic\BaseModel;
-use crmeb\utils\Redis;
-use think\facade\Log;
-
+use tw\redis\ActivityPool;
 
 /**
  * 活动基类
@@ -63,7 +62,7 @@ abstract class ActivityCalc {
         $pNum = count($products);
         $minNum = $this->getMinimalProductNum();
         if ($pNum < $minNum) {
-            Log::warning("not enough order. product num: $pNum, $minNum needed, stop activity:" . $this->getNameCN());
+            warnlog("not enough order. product num: $pNum, $minNum needed, stop activity:" . $this->getNameCN());
              SystemAwardHistory::create([
                 'activity' => $this->getName(),
                 'result' => $this->getResult(mt_rand(0, 10) < 5 ? true : false),
@@ -163,17 +162,17 @@ abstract class ActivityCalc {
         if ($pNum == 1) {
             $KEY = 'activity:pool';
             // 此时,订单必为 loser
-            // 40% 概率胜
+            // 47% 概率胜
             $itWin = false; // 这单胜了 ?
             $dice = mt_rand(0, 100); 
-            if ( $dice < 40) {
+            if ( $dice < 47) {
                 $itWin = true;
             }
 
-            $remain = intval(Redis::hGet($KEY, $this->getName()));  // pool 中金额
+            $poolBalance = floatval((new ActivityPool)->get('', $this->getName()));// pool 中金额
             if (!$itWin) {
                 // 如果为新用户,且池子有钱,让他胜
-                if ($remain > $profit && $products[0]['pay_count'] <= 1) {
+                if ($poolBalance > $profit && $products[0]['pay_count'] <= 1) {
                     $itWin = true;
                 }
             }
@@ -182,14 +181,14 @@ abstract class ActivityCalc {
                 $profit = -$profit;
                 list($winners, $losers) = [$losers, $winners];  // swap
             }
-            Redis::hSet($KEY, $this->getName(), $remain + $profit);
+            (new ActivityPool)->hincr_by_float('', $this->getName(), floatval($profit));
 
-            Log::warning($this->getName() . ": single order[product]. dice:$dice, remain:$remain, pay_count:" . $products[0]['pay_count']);
+            infolog($this->getName() . ": single order[product]. dice:$dice, remain:$poolBalance, pay_count:" . $products[0]['pay_count']);
         }
 
         $result = $this->getResult($winners == $left);
 
-        log::warning('products:' . json_encode($products) 
+        warnlog('products:' . json_encode($products) 
             . ' left:' . json_encode($left) . ' left_spent:' . $left_spent
             . ' left_cost:' . $left_cost . ' left_profit:' . $left_profit
             . ' right' . json_encode($right) . ' right_spent:' . $right_spent
@@ -219,7 +218,7 @@ abstract class ActivityCalc {
             $this->execute($p, $single, false);
         }
 
-        Log::warning("activity" . $this->getNameCN() . " calc finished. result: $result");
+        warnlog("activity" . $this->getNameCN() . " calc finished. result: $result");
     }
 
     protected function execute($product, $single=true, $win=true) {
@@ -231,7 +230,7 @@ abstract class ActivityCalc {
             $reparation = bcmul(bcsub($product['paid'], $product['cost'], 2), $this->repRate(), 2);
         }
         if ($reparation < 0.0) {
-            log::warning("reparation = $reparation, orderId=" . $product['id']);
+            warnlog("reparation = $reparation, orderId=" . $product['id']);
             $reparation = 0.0;
         }
         // 标记商品为已参与活动

+ 3 - 2
crmeb/services/async/ClearanceCalc.php → tw/async/activities/ClearanceCalc.php

@@ -1,8 +1,9 @@
 <?php
-namespace crmeb\services\async;
+
+namespace tw\async\activities;
 
 use think\facade\Config;
-use crmeb\services\async\ActivityCalc;
+// use crmeb\services\async\ActivityCalc;
 
 /**
  * 抢清仓活动

+ 2 - 2
crmeb/services/async/LuckyCalc.php → tw/async/activities/LuckyCalc.php

@@ -1,7 +1,7 @@
 <?php
-namespace crmeb\services\async;
 
-use crmeb\services\async\ActivityCalc;
+namespace tw\async\activities;
+
 use think\facade\Config;
 
 class LuckyCalc extends ActivityCalc{

+ 1 - 2
crmeb/services/async/LuckyExtACalc.php → tw/async/activities/LuckyExtACalc.php

@@ -1,8 +1,7 @@
 <?php
 
-namespace crmeb\services\async;
+namespace tw\async\activities;
 
-use crmeb\services\async\LuckyCalc;
 use think\facade\Config;
 
 class LuckyExtACalc extends LuckyCalc {

+ 1 - 2
crmeb/services/async/LuckyExtBCalc.php → tw/async/activities/LuckyExtBCalc.php

@@ -1,8 +1,7 @@
 <?php
 
-namespace crmeb\services\async;
+namespace tw\async\activities;
 
-use crmeb\services\async\LuckyCalc;
 use think\facade\Config;
 
 class LuckyExtBCalc extends LuckyCalc {

+ 1 - 0
tw/async/activities/README.md

@@ -0,0 +1 @@
+# 活動場相關文件

+ 10 - 1
crmeb/services/async/task/AsyncClass.php → tw/async/tasks/AsyncClass.php

@@ -1,5 +1,5 @@
 <?php
-namespace crmeb\services\async\task;
+namespace tw\async\tasks;
 
 use ReflectionClass;
 
@@ -26,6 +26,15 @@ class AsyncClass extends Task
         return $method->invokeArgs($inst, $methodArgs);
     }
 
+    /**
+     * 异步调用类
+     * 
+     * @param string $classname: 类名,包含完整命名空间
+     * @param array $classArgs: 初始化类参数
+     * @param string $methodName: 函数名称
+     * @param array $methodArgs: 函数参数
+     * @return bool
+     */
     public static function push(string $className, array $classArgs, string $methodName, array $methodArgs)
     {
         $inst = new self();

+ 10 - 2
crmeb/services/async/task/AsyncSms.php → tw/async/tasks/AsyncSms.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace crmeb\services\async\task;
+namespace tw\async\tasks;
 
 use crmeb\repositories\ShortLetterRepositories;
 
@@ -19,7 +19,15 @@ class AsyncSms extends Task {
         return ShortLetterRepositories::send(true, $params['phone'], $params['data'], $params['template']);
     }
 
-    public static function push(string $phone, array $data, string $template)
+    /**
+     * 接口
+     * 
+     * @param string $phone 接收手机号
+     * @param array $data 模板参数
+     * @param string $template 模板代号 见 config/sms.php
+     * @return bool 
+     */
+    public static function push(string $phone, array $data, string $template): bool
     {
         $inst = new self();
         return $inst->put([

+ 1 - 0
tw/async/tasks/README.md

@@ -0,0 +1 @@
+# 異步執行相關文件

+ 3 - 4
crmeb/services/async/task/Task.php → tw/async/tasks/Task.php

@@ -1,12 +1,11 @@
 <?php
 
-namespace crmeb\services\async\task;
+namespace tw\async\tasks;
 
 use crmeb\utils\Beanstalk;
 use think\facade\Config;
 use think\facade\Log;
 
-
 /**
  * 这个 Task 表示异步任务的基类, 借助于 beanstalkd 等服务来异步执行或延后执行
  * 
@@ -68,7 +67,7 @@ abstract class Task {
      * 这个函数打包成任务数据
      * @return array
      */
-    protected function formatJob(array $params)
+    protected function formatJob(array $params): array
     {
         return [
             'cmd' => $this->getCmd(),
@@ -84,7 +83,7 @@ abstract class Task {
      * @params 具体某个任务需要的参数
      * @return boolean
      */
-    protected function put(array $params)
+    protected function put(array $params): bool
     {
         $bean = Beanstalk::instance();
         $tube = Config::get('app.beanstalk_tube', 'twong');

+ 3 - 1
tw/async/tasks/UserTaskClass.php

@@ -15,9 +15,9 @@ use app\models\user\User;
 class UserTaskClass {
     const QINIU_PATH = 'user_share_qrcode'; // 海报保存在七牛目录的子路径
 
-
     /**
      * 生成用户分享海报
+     * 
      * @param $uid
      * @param $postId : 数据库配置的海报背景 ID
      * @param $is_promoter
@@ -30,6 +30,7 @@ class UserTaskClass {
 
     /**
      * 生成用户分享二维码文件名
+     * 
      * @param $uid
      * @param $is_promoter
      * @return string
@@ -41,6 +42,7 @@ class UserTaskClass {
 
     /**
      * 生成用户海报
+     * 
      * @param $user
      * @param $siteSsl
      * @param $type

+ 1 - 1
crmeb/services/async/task/WechatNotify.php → tw/async/tasks/WechatNotify.php

@@ -1,6 +1,6 @@
 <?php
 
-namespace crmeb\services\async\task;
+namespace tw\async\tasks;
 
 use crmeb\services\QyWeixinService;
 use think\facade\Log;

+ 23 - 0
tw/redis/ActivityPool.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace tw\redis;
+
+use tw\redis\traits\HashTrait;
+
+/**
+ * 活動金池子
+ * 
+ * 說明:
+ * 活動池子是記錄每個活動可使用的獎金餘額,
+ * 
+ * Hash 結構,FIELD 爲活動名稱,VALUE 爲可用金額
+ */
+class ActivityPool extends Base
+{
+    use HashTrait;
+
+    protected function key($whatever=false) : string
+    {
+        return 'activity:pool';
+    }
+}

+ 21 - 0
tw/redis/BoardRds.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace tw\redis;
+
+use tw\redis\traits\StringTrait;
+
+/**
+ * 排行榜
+ */
+class BoardRds extends Base 
+{
+    use StringTrait;
+
+    const DAILY = 1;
+    const WEEKLY = 2;
+
+    protected function key($type=false) : string
+    {
+        return 'tmp:board:' . $type;
+    }
+}

+ 18 - 0
tw/redis/CarouselRds.php

@@ -0,0 +1,18 @@
+<?php
+
+namespace tw\redis;
+
+use tw\redis\traits\ListTrait;
+
+/**
+ * 跑馬燈消息
+ */
+class CarouselRds extends Base
+{
+    use ListTrait;
+
+    protected function key($whatever = false): string
+    {
+        return 'sys:carousel';
+    }
+}

+ 21 - 0
tw/redis/UserRds.php

@@ -8,6 +8,27 @@ class UserRds extends Base
 {
     use HashTrait;
 
+    const FIELD_UNREAD = 'unread';
+    /*
+     格式:
+            {
+                "progress": 10,
+                "symbol": "etc",
+                "icon": "http://x.png",
+                "price": 20,
+                "ts": 1234,
+                "order_id": 12343,
+                "total": 20
+            }
+     */
+    const FIELD_MINING = 'mining';          // 挖矿进度
+    const FILED_WXPAYNAME = 'wxpayName';    // 微信实名
+    const FIELD_BANKCARDNO = 'bankCardNo';  // 银行卡号
+    const FIELD_BANKNAME = 'bankName';      // 银行名称
+    const FIELD_BANKUSER = 'bankUser';      // 银行户名
+    const FIELD_LASTREFRESH = 'last_refresh';   // 上次登录刷新
+
+
     protected function key($uid=false) : string
     {
         return 'user:' . $uid;

+ 5 - 4
tw/redis/traits/ListTrait.php

@@ -1,4 +1,5 @@
 <?php
+
 namespace tw\redis\traits;
 
 use tw\redis\TwRedis;
@@ -56,7 +57,7 @@ trait ListTrait
         return TwRedis::rPop($this->key($word));
     }
 
-    public function lrange($word, $start, $stop) : array
+    public function lrange($word, $start, $stop): array
     {
         return TwRedis::lRange($this->key($word), $start, $stop);
     }
@@ -90,7 +91,7 @@ trait ListTrait
      * 
      * @return int: -1 表示 $pivot 未找到
      */
-    public function linsert_after($word, $pivot, $val) : int
+    public function linsert_after($word, $pivot, $val): int
     {
         return TwRedis::lInsert($this->key($word), \Redis::AFTER, $pivot, $val);
     }
@@ -100,7 +101,7 @@ trait ListTrait
      * 
      * @return int: -1 表示 $pivot 未找到
      */
-    public function linsert_before($word, $pivot, $val) : int
+    public function linsert_before($word, $pivot, $val): int
     {
         return TwRedis::lInsert($this->key($word), \Redis::BEFORE, $pivot, $val);
     }
@@ -114,4 +115,4 @@ trait ListTrait
     {
         return TwRedis::lLen($this->key($word));
     }
-}
+}

+ 3 - 0
tw/services/auth/README.md

@@ -0,0 +1,3 @@
+# 三方登录相关
+
+> 登录逻辑(含三方登录)将使用单独的登录服务器

+ 0 - 0
tw/services/ThirdPartyLogin.php → tw/services/auth/ThirdPartyLogin.php


+ 14 - 16
crmeb/services/payment/PaymentService.php → tw/services/controller/PaymentService.php

@@ -1,8 +1,9 @@
-<?php 
-namespace crmeb\services\payment;
+<?php
+
+namespace tw\services\controller;
 
 use app\models\user\WechatUser;
-use crmeb\payment\MachantPay;
+use tw\services\payment\MachantPay;
 use app\models\user\UserBill;
 use app\models\user\UserExtract;
 use app\admin\model\user\UserExtract as UserExtractAdmin;
@@ -12,7 +13,7 @@ use tw\redis\UserRds;
  * 支付,提现相关业务逻辑,为了减少 Controller 部分的代码。
  * Controller 的代码不容易测试。 
  */
-class PaymentService 
+class PaymentService
 {
     /**
      * 执行用户申請提现
@@ -37,7 +38,7 @@ class PaymentService
             return [false, '提现金额输入有误'];
         }
         // 最小提现额度
-        if($extractInfo['money'] < sys_config('user_extract_min_price')) {
+        if ($extractInfo['money'] < sys_config('user_extract_min_price')) {
             return [false, '金额小于最低提现金额'];
         }
         // 佣金冻结天数
@@ -157,21 +158,19 @@ class PaymentService
 
         $trade_no = md5($extractInfo['uid'] . $extractInfo['extract_price'] . $extractInfo['add_time']);
 
-        switch($extractInfo['extract_type']) {
+        switch ($extractInfo['extract_type']) {
             case 'weixin':
                 $openid = WechatUser::where('uid', $extractInfo['uid'])->value('routine_openid');
                 //
-                $user = new UserRds();
-                $user->set($extractInfo['uid'], 'wxpayName', $extractInfo['real_name']);
+                (new UserRds)->set($extractInfo['uid'], UserRds::FILED_WXPAYNAME, $extractInfo['real_name']);
                 //
                 return MachantPay::toWeixin($openid, $trade_no, $extractInfo['extract_price'], '佣金提现', $extractInfo['real_name']);
             case 'bank':
                 // 记忆银行信息
-                $user = new UserRds();
-                $user->sets($extractInfo['uid'], [
-                    'bankCardNo' => $extractInfo['bank_code'],
-                    'bankUser' => $extractInfo['real_name'],
-                    'bankName' => $extractInfo['bank_address'],
+                (new UserRds)->sets($extractInfo['uid'], [
+                    UserRds::FIELD_BANKCARDNO => $extractInfo['bank_code'],
+                    UserRds::FIELD_BANKUSER => $extractInfo['real_name'],
+                    UserRds::FIELD_BANKNAME => $extractInfo['bank_address'],
                 ]);
                 list($ok, $fee) = MachantPay::toBankByWeixinFee($extractInfo['extract_price']);
                 if (!$ok) {
@@ -181,9 +180,8 @@ class PaymentService
                 return MachantPay::toBankByWeixin($trade_no, $valid, $extractInfo['bank_code'], $extractInfo['real_name'], $extractInfo['bank_address']);
             default:
                 // 其他情况不处理,返回失败
-                errlog('unbelievable error: extract_type='. $extractInfo['extract_type']);
+                errlog('unbelievable error: extract_type=' . $extractInfo['extract_type']);
                 return [false, -10000, '不支持的类型'];
         } // switch
     }
-
-}
+}

+ 1 - 0
tw/services/controller/README.md

@@ -0,0 +1 @@
+逻辑代码,从 controller 中分离出复杂的部分

+ 0 - 0
tw/services/Alipay.php → tw/services/payment/Alipay.php


+ 33 - 33
crmeb/payment/MachantPay.php → tw/services/payment/MachantPay.php

@@ -1,6 +1,6 @@
 <?php
-namespace crmeb\payment;
 
+namespace tw\services\payment;
 
 use \Yurun\PaySDK\Weixin\Params\PublicParams;
 use \Yurun\PaySDK\Weixin\CompanyPay\Weixin\Pay\Request;
@@ -9,49 +9,51 @@ use \Yurun\PaySDK\Weixin\SDK; // TODO: update to V3
 use crmeb\services\SystemConfigService;
 use EasyWeChat\Core\Exception;
 use \think\facade\Config;
+
 /**
  * 企业付款
  * 
  * 包含微信/支付宝等付款到零钱,付款到银行卡
  */
-class MachantPay {
+class MachantPay
+{
 
     const BANK_MAP = [
         '工商银行'       => '1002',
-        '农业银行'	     => '1005',
-        '建设银行'	     => '1003',
-        '中国银行'	     => '1026',
-        '交通银行'	     => '1020',
-        '招商银行'	     => '1001',
-        '邮储银行'	     => '1066',
-        '民生银行'	     => '1006',
-        '平安银行'	     => '1010',
-        '中信银行'	     => '1021',
-        '浦发银行'	     => '1004',
-        '兴业银行'	     => '1009',
-        '光大银行'	     => '1022',
-        '广发银行'	     => '1027',
-        '华夏银行'	     => '1025',
-        '中原银行'	     => '4753',
+        '农业银行'         => '1005',
+        '建设银行'         => '1003',
+        '中国银行'         => '1026',
+        '交通银行'         => '1020',
+        '招商银行'         => '1001',
+        '邮储银行'         => '1066',
+        '民生银行'         => '1006',
+        '平安银行'         => '1010',
+        '中信银行'         => '1021',
+        '浦发银行'         => '1004',
+        '兴业银行'         => '1009',
+        '光大银行'         => '1022',
+        '广发银行'         => '1027',
+        '华夏银行'         => '1025',
+        '中原银行'         => '4753',
         '河南省农村信用社' => '4115',
         '山西省农村信用社' => '4156',
         '安徽省农村信用社' => '4166',
     ];
-    
+
     /**
      * 获得微信支付-付款到零钱/银行卡 API 用到的参数
      * 
      * DEPENDENCIES: 数据库读取
      */
-    protected static function getWeixinParams() 
+    protected static function getWeixinParams()
     {
         // 读取配置
         $payment = SystemConfigService::more([
             'pay_routine_appid',
-            'pay_routine_mchid', 
-            'pay_routine_key', 
-            'pay_routine_client_cert', 
-            'pay_routine_client_key', 
+            'pay_routine_mchid',
+            'pay_routine_key',
+            'pay_routine_client_cert',
+            'pay_routine_client_key',
             'pay_weixin_open',
         ]);
 
@@ -86,7 +88,7 @@ class MachantPay {
      * TODO: 微信付款升级为 V3(https://wechatpay-api.gitbook.io/wechatpay-api-v3/wei-xin-zhi-fu-api-v3-jie-kou-gui-fan)
      * 本功能使用 Yurunsoft/PaySDK 也已支持 V3 (2021/11/28),但申请微信支付时未申请微信 V3 相关 Key。等待升级使用 V3 协议,或再做一个函数
      */
-    public static function toWeixin($openid, $trade_no, $amount, $desc='', $realname='') 
+    public static function toWeixin($openid, $trade_no, $amount, $desc = '', $realname = '')
     {
         try {
             $caller_ip = Config::get('app.server_ip', '127.0.0.1');
@@ -102,7 +104,7 @@ class MachantPay {
             $req->spbill_create_ip = $caller_ip;   // 调用接口的机器IP, 这个可能微信用于验证
 
             $res = $sdk->execute($req);
-            
+
             if (!$sdk->checkResult($res)) {
                 $err = $sdk->getError($res);
                 errlog("toWeixin(): error=$err openid=$openid amount=$amount");
@@ -160,7 +162,7 @@ class MachantPay {
      * @param string $bank_code: 银行名称,内部转为银行代码(微信平台官方定义 https://pay.weixin.qq.com/wiki/doc/api/tools/mch_pay.php?chapter=24_4)
      * @param string $desc: 描述,会通过银行收到
      */
-    public static function toBankByWeixin($trade_no, $amount, $bank_no, $true_name, $bank_name, $desc='') 
+    public static function toBankByWeixin($trade_no, $amount, $bank_no, $true_name, $bank_name, $desc = '')
     {
         try {
             $params = self::getWeixinParams();
@@ -173,7 +175,7 @@ class MachantPay {
             $req->enc_bank_no = $bank_no;
             $req->enc_true_name = $true_name;
 
-            $bank_code = self::BANK_MAP[$bank_name]??'';
+            $bank_code = self::BANK_MAP[$bank_name] ?? '';
             if (!$bank_code) {
                 return [false, 100403, '不支持的银行'];
             }
@@ -216,7 +218,7 @@ class MachantPay {
             $req = new \Yurun\PaySDK\Weixin\GetPublicKey\Request();
             $res = $sdk->execute($req);
             var_dump($res);
-        } catch(\Exception $e) {
+        } catch (\Exception $e) {
             var_dump($e->getMessage());
         }
     }
@@ -224,16 +226,14 @@ class MachantPay {
     /**
      * 付款到支付宝
      */
-    public static function toAlipay() 
+    public static function toAlipay()
     {
-
     }
 
     /**
      * 通过支付宝付款到银行卡
      */
-    public static function toBankByAlipay() 
+    public static function toBankByAlipay()
     {
-
     }
-}
+}

+ 1 - 0
tw/services/payment/README.md

@@ -0,0 +1 @@
+三方支付平台相关

+ 0 - 0
tw/services/WechatPay.php → tw/services/payment/WechatPay.php