zory hace 1 mes
padre
commit
e43df984f5

+ 2 - 1
README.md

@@ -20,4 +20,5 @@ composer require tinywan/storage
 composer require aliyuncs/oss-sdk-php
 composer require qcloud/cos-sdk-v5
 composer require qiniu/php-sdk
-composer require luckycmc/webman-province-city-area
+composer require luckycmc/webman-province-city-area
+composer require tinywan/captcha:^2.1

+ 71 - 0
app/controller/admin/Config.php

@@ -0,0 +1,71 @@
+<?php
+
+namespace app\controller\admin;
+
+use app\extra\basic\Base;
+use app\extra\tools\OssRegionExtend;
+use app\middleware\AuthMiddleware;
+use app\model\system\SystemConfig;
+use LinFly\Annotation\Attributes\Route\Controller;
+use LinFly\Annotation\Attributes\Route\GetMapping;
+use LinFly\Annotation\Attributes\Route\Middleware;
+use LinFly\Annotation\Attributes\Route\PostMapping;
+use support\Request;
+use support\Response;
+
+
+#[Controller("/api/config"),Middleware(AuthMiddleware::class)]
+class Config extends Base
+{
+
+    #[GetMapping('list')]
+    public function getConfigList(Request $request): Response
+    {
+        try {
+            $type = $request->get("type","service");
+            $data = (new SystemConfig)->where("type",$type)->where("status",1)->field("name,value")->select()->toArray();
+            $result = [];
+            foreach ($data as $item) {
+                $result[$item['name']] = $item['value'];
+            }
+            if ($type == "sms") {
+                $result['channel'] = $this->getSmsChannel();
+                $result['login_sms'] = '您的验证码为:${code},请勿泄露于他人!';
+            }
+            return successTrans("success.data",$result);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    #[PostMapping("save")]
+    public function setConfigData(Request $request): Response
+    {
+        try {
+            $param = $request->post();
+            if (isset($param['data']['channel'])) unset($param['data']['channel']);
+            foreach ($param['data'] as $k => $v){
+                if(is_array($v)) $v = implode(",",$v);
+                sConf($param['type'].'.'.$k, $v);
+            }
+            return successTrans("success.data",[]);
+        } catch (\Throwable $exception){
+            return error($exception->getMessage());
+        }
+    }
+
+    #[GetMapping("regin")]
+    public function getConfigRegin(): Response
+    {
+        try {
+            return successTrans(100010,[
+                "oss"       => OssRegionExtend::OssRegion(),
+                "cos"       => OssRegionExtend::CosRegion(),
+                "qiniu"     => OssRegionExtend::QiniuRegion(),
+            ]);
+        } catch (\Exception $exception) {
+            return error($exception->getMessage());
+        }
+    }
+
+}

+ 51 - 0
app/controller/admin/Upload.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace app\controller\admin;
+
+use app\extra\basic\Base;
+use app\extra\tools\UploadExtend;
+use app\middleware\AuthMiddleware;
+use LinFly\Annotation\Attributes\Route\Controller;
+use LinFly\Annotation\Attributes\Route\Middleware;
+use LinFly\Annotation\Attributes\Route\PostMapping;
+use support\Request;
+use support\Response;
+
+
+#[Controller("/api/upload"),Middleware(AuthMiddleware::class)]
+class Upload extends Base
+{
+
+    #[PostMapping("data")]
+    public function upload2image(Request $request): Response
+    {
+        try {
+            $resp = UploadExtend::uploadFile();
+            if (!isset($resp[0]['url'])) return errorTrans(40010);
+            return successTrans("success.data",[
+                "fileName"  => $resp[0]['origin_name'],
+                "src"       => $resp[0]['url'],
+            ],200);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    #[PostMapping("file")]
+    public function upload2file(Request $request): Response
+    {
+        try {
+            $resp = UploadExtend::disk(UploadExtend::MODE_LOCAL)->uploadFile();
+            if (!isset($resp[0]['url'])) return errorTrans(40010);
+            return successTrans("success.data",[
+                "fileName"  => $resp[0]['origin_name'],
+                "src"       => $resp[0]['url'],
+            ],200);
+        } catch (\Throwable $throwable) {
+            echo $throwable->getFile()."\n";
+            echo $throwable->getLine()."\n";
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 134 - 0
app/controller/common/Login.php

@@ -0,0 +1,134 @@
+<?php
+
+namespace app\controller\common;
+
+use app\extra\basic\Base;
+use app\middleware\AuthMiddleware;
+use app\model\system\SystemConfig;
+use app\model\system\SystemUser;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Attributes\Route\{GetMapping, Controller, Middleware, PostMapping};
+use support\{Request,Response};
+use Shopwwi\WebmanAuth\Auth;
+use think\facade\Db;
+use Tinywan\Captcha\Captcha;
+
+#[Controller("/api/login"),Middleware(AuthMiddleware::class)]
+class Login extends Base
+{
+
+    /**
+     * @var array|string[]
+     */
+    protected array $noNeedLogin = ["getSystemData","setLogin"];
+
+    #[Inject]
+    protected SystemConfig $config;
+
+    /**
+     * 登陆
+     * @param Request $request
+     * @return Response
+     */
+    #[PostMapping('user')]
+    public function setLogin(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "username.require"  => trans("empty.user"),
+                "password.require"  => trans("empty.passwd"),
+                "code.require"      => trans("empty.code"),
+                "key.require"       => trans("empty.data"),
+            ],"post");
+            if (!is_array($param)) return error($param);
+            if (Captcha::check($param['code'],$param['key']) === false) return errorTrans("error.captcha");
+            $map = ["is_deleted" => 0,"username" => $param['username']];
+            [$state,$msg,$user] = $this->checkLogin($map,2,$param);
+            if (!$state) return error($msg);
+            return successTrans("success.login",get_object_vars((new Auth)->guard("admin")->login($user)));
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+    /**
+     * 登录验证处理
+     * @param array $map
+     * @param int $type
+     * @param array $param
+     * @return array
+     */
+    protected function checkLogin(array $map = [],int $type = 1,array $param = []): array
+    {
+        $user = (new SystemUser)->where($map)->findOrEmpty();
+        if ($user->isEmpty()) return [0,trans("error.user-empty"),[]];
+        if ($user['status'] <> 1) return [0,trans("error.user-status"),[]];
+        if ($user['type'] > 1) {
+            $typeUser = $this->getTypeUser($user['agent_id']);
+            if (empty($typeUser)) return [0,trans("empty.agent"),[]];
+            if ($typeUser['status'] <> 0) return [0,trans("error.agent"),[]];
+            if (time() > strtotime($typeUser['vip_end'])) return [0,trans("error.agent-out"),[]];
+            $user['shop_name'] = $typeUser['shop_name'];
+        }
+        if ($type == 2) {
+            if (md5($param['password'].$user['salt']) <> $user['password']) return [0,trans("error.passwd"),[]];
+        }
+        $user->login_at = getDateFull();
+        $user->login_ip = request()->getRealIp();
+        $user->login_num = Db::raw("login_num+1");
+        $user->save();
+        return [1,'success',$user->toArray()];
+    }
+
+
+    /**
+     * 获取代理信息
+     * @param int $agentId
+     * @return array
+     */
+    protected function getTypeUser(int $agentId = 0): array
+    {
+        return [];
+    }
+
+    #[GetMapping('profile')]
+    public function getLoginUser()
+    {
+        try {
+            $userData = (new Auth)->guard("admin")->user()->toArray();
+            if (isset($userData['password'])) unset($userData['password']);
+            $agent = [];
+            if (empty($agent['vip_end']))
+            {
+                $userData['vip_end'] = 0;
+            } else {
+                $userData['vip_end'] = strtotime($agent['vip_end']);
+            }
+            return successTrans("success.data",[
+                "username"  => $userData['username'],
+                "truename"  => $userData['truename'],
+                "vip_at"    => $userData['vip_end'],
+                "agent_id"  => $userData['agent_id'],
+                "super"     => $userData['is_super'],
+                "shop"      => $agent['shop_name']??'',
+                "type"      => $userData['type']
+            ]);
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    #[GetMapping('data')]
+    public function getSystemData(): Response
+    {
+        try {
+            $captcha = Captcha::base64();
+            $service = $this->config->where("type","service")->column("value","name");
+            return successTrans("success.data",compact("captcha",'service'));
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 76 - 0
app/controller/common/Menu.php

@@ -0,0 +1,76 @@
+<?php
+
+namespace app\controller\common;
+
+use app\extra\basic\Base;
+use app\extra\tools\DataExtend;
+use app\middleware\AuthMiddleware;
+use app\model\system\SystemMenu;
+use app\service\system\MenuService;
+use DI\Attribute\Inject;
+use LinFly\Annotation\Attributes\Route\Controller;
+use LinFly\Annotation\Attributes\Route\GetMapping;
+use LinFly\Annotation\Attributes\Route\Middleware;
+use LinFly\Annotation\Attributes\Route\PostMapping;
+use support\Request;
+use support\Response;
+
+#[Controller(prefix: "/api/menu"),Middleware(AuthMiddleware::class)]
+class Menu extends Base
+{
+
+    #[Inject]
+    protected MenuService $service;
+
+    /**
+     * 获取菜单
+     */
+    #[GetMapping("list")]
+    public function getMenuList(Request $request): Response
+    {
+        try {
+            $param = $this->_valid([
+                "form.default"  => $request->user['type'],
+                "type.default"  => 1
+            ]);
+            $hide = 0;
+            $menu = $this->service->getMenuList($request->user['is_super'],$hide,$param['form']);
+            $permissionsData = [];
+            foreach ($menu as $val) {
+                if ($val['type'] == 'button') {
+                    $permissionsData[] = $val['name'];
+                }
+            }
+            $menu = $this->filterMenu(DataExtend::arr2tree($menu),$param['type']);
+            if($param['type'] == 1) {
+                $permissions = $permissionsData;
+                $dashboardGrid = [];
+                return successTrans("success.data",compact('menu','permissions','dashboardGrid'));
+            }
+            return successTrans("success.data",$menu);
+        } catch (\Throwable $throwable) {
+            echo $throwable->getFile()."\n";
+            echo $throwable->getLine()."\n";
+            return error($throwable->getMessage());
+        }
+    }
+
+
+    /**
+     * 更新
+     * @param Request $request
+     * @return Response
+     */
+    #[PostMapping("save")]
+    public function saveMenuData(Request $request): Response
+    {
+        try {
+            $state = (new SystemMenu)->setAutoData($request->post());
+            if(!$state) return errorTrans("error.data");
+            return successTrans("success.data");
+        } catch (\Throwable $throwable) {
+            return error($throwable->getMessage());
+        }
+    }
+
+}

+ 192 - 0
app/extra/basic/Base.php

@@ -0,0 +1,192 @@
+<?php
+
+namespace app\extra\basic;
+
+use think\contract\Arrayable;
+use think\Validate;
+
+class Base
+{
+
+
+
+    const DEFAULT_SUCCESS_MESSAGE = 'success';
+    const DEFAULT_FAIL_MESSAGE = 'fail';
+
+    const DEFAULT_SUCCESS_CODE = 200;
+    const DEFAULT_FAIL_CODE = 400;
+
+
+    public function getShopId()
+    {
+        return request()->header("shop",0);
+    }
+
+
+    protected function getDyConfig(): array
+    {
+        return ["appid" => sConf('dy.appid'),'secret' => sConf('dy.secret')];
+    }
+
+    /**
+     * 短信通道
+     * @return array[]
+     */
+    protected function getSmsChannel(): array
+    {
+        return [
+            [
+                "name"  => "阿里云",
+                "type"  => "aliyun",
+                "url"   => "https://dysms.console.aliyun.com/dysms.htm"
+            ],
+            [
+                "name"  => "腾讯云",
+                "type"  => "qcloud",
+                "url"   => "https://console.cloud.tencent.com/smsv2"
+            ],
+            [
+                "name"  => "七牛云",
+                "type"  => "qiniu",
+                "url"   => "https://portal.qiniu.com/sms/dashboard"
+            ]
+        ];
+    }
+
+    /**
+     *  快捷输入并验证( 支持 规则 # 别名 )
+     * @param array $rules 验证规则( 验证信息数组 )
+     * @param array|string $input 输入方式 ( post. 或 get. )
+     * @param callable|null $callable 异常处理操作
+     */
+    protected function _valid(array $rules, array|string $input = '', ?callable $callable = null)
+    {
+        if (is_string($input)) {
+            $type = trim($input, '.') ?: 'get';
+            $input = request()->$type();
+        }
+        [$data, $rule, $info] = [[], [], []];
+        foreach ($rules as $name => $message) if (is_numeric($name)) {
+            [$name, $alias] = explode('#', $message . '#');
+            $data[$name] = $input[($alias ?: $name)] ?? null;
+        } elseif (!str_contains($name, '.')) {
+            $data[$name] = $message;
+        } elseif (preg_match('|^(.*?)\.(.*?)#(.*?)#?$|', $name . '#', $matches)) {
+            [, $_key, $_rule, $alias] = $matches;
+            if (in_array($_rule, ['value', 'default'])) {
+                if ($_rule === 'value') $data[$_key] = $message;
+                elseif ($_rule === 'default') $data[$_key] = $input[($alias ?: $_key)] ?? $message;
+            } else {
+                $info[explode(':', $name)[0]] = $message;
+                $data[$_key] = $data[$_key] ?? ($input[($alias ?: $_key)] ?? null);
+                $rule[$_key] = isset($rule[$_key]) ? ($rule[$_key] . '|' . $_rule) : $_rule;
+            }
+        }
+        $validate = new Validate();
+        if ($validate->rule($rule)->message($info)->check($data)) {
+            return $data;
+        } elseif (is_callable($callable)) {
+            return call_user_func($callable, $validate->getError(), $data);
+        } else {
+            return $validate->getError();
+        }
+    }
+    /**
+     * @param $data
+     * @return array|string|null
+     */
+    private function parseData($data)
+    {
+        if ($data instanceof Arrayable)
+            return $data->toArray();
+        else
+            return $data;
+    }
+
+    /**
+     * @param string|array|Arrayable $message
+     * @param array|Arrayable|null $data
+     * @return Json
+     */
+    public function success($message = self::DEFAULT_SUCCESS_MESSAGE, $data = null)
+    {
+        $message = $this->parseData($message);
+        if (is_array($message)) {
+            $data = $message;
+            $message = self::DEFAULT_SUCCESS_MESSAGE;
+        } else {
+            $data = $this->parseData($data);
+        }
+        return $this->make(self::DEFAULT_SUCCESS_CODE, $message, $data);
+    }
+
+
+    /**
+     * @param int $status
+     * @param string $message
+     * @param array|Arrayable|null $data
+     */
+    public function make(int $status, string $message, $data = null)
+    {
+        $content = compact('status', 'message');
+        if (!is_null($data))
+            $content['data'] = $this->parseData($data);
+        return json($content);
+    }
+
+    public function encode($message = self::DEFAULT_SUCCESS_MESSAGE, $data = null)
+    {
+        $message = $this->parseData($message);
+        if (is_array($message)) {
+            $data = $message;
+            $message = self::DEFAULT_SUCCESS_MESSAGE;
+        } else {
+            $data = $this->parseData($data);
+        }
+        $status = self::DEFAULT_SUCCESS_CODE;
+        $encode = true;
+        $content = compact('status','encode', 'message');
+        if (is_null($data) || env('APP_DEBUG', false))
+            return $this->success($message, $data['data'] ?? $data);
+        if (!is_array($data) || !isset($data['data'])) {
+            $res['data'] = $data;
+        } else {
+            $res = $data;
+        }
+        $res['status'] = $status;
+        $res['message'] = $message;
+        $content['data'] =  base64_encode(gzdeflate(json_encode($res,JSON_UNESCAPED_UNICODE),9));
+        return json($content);
+    }
+
+
+    /**
+     * 菜单信息格式化
+     * @param array $menus
+     * @param int $type
+     * @param int $mch
+     * @return array
+     */
+    protected function filterMenu(array $menus,int $type,int $mch = 0): array
+    {
+        foreach ($menus as &$menu) {
+            $menu['meta'] = [
+                "title"     => $menu['title'],
+                "icon"      => $menu['icon']??'',
+                "type"      => $menu['type']
+            ];
+            if (!empty($menu['children'])) {
+                $menu['children'] = $this->filterMenu($menu['children'],$type,$mch);
+            }
+            if ($mch > 0) {
+                $menu['component'] = ($mch==1?'merchant/':'store/').$menu['name'];
+            } else {
+                $menu['component'] = $menu['name'];
+            }
+            $menu['isMenu'] = $menu['status'];
+            if($type == 1) unset($menu['title'],$menu['icon'],$menu['type'],$menu['pid'],$menu['id']);
+        }
+        return $menus;
+    }
+
+}

+ 28 - 0
app/extra/basic/Model.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace app\extra\basic;
+
+class Model extends \think\Model
+{
+
+    /**
+     * 数据操作
+     * @param array $data
+     * @param string $id
+     * @return bool|int
+     */
+    public function setAutoData(array $data = [],string $id = "id"): bool|int
+    {
+        try {
+            if (isset($data[$id]) && $this->where($id,$data[$id])->count()) {
+                $state = $this->where($id,$data[$id])->strict(false)->update($data);
+            } else {
+                $state = $this->strict(false)->insertGetId($data);
+            }
+        } catch (\Throwable $throwable) {
+            return false;
+        }
+        return $state;
+    }
+
+}

+ 103 - 0
app/extra/basic/Service.php

@@ -0,0 +1,103 @@
+<?php
+
+namespace app\extra\basic;
+
+use app\model\system\SystemConfig;
+use Overtrue\EasySms\Strategies\OrderStrategy;
+
+class Service
+{
+
+    /**
+     * @var null
+     */
+    protected $mode = null;
+
+
+    /**
+     * 白名单
+     * @var array|string[]
+     */
+    protected array $mobileWhite = ["18665619195","18665619196","18665619197","18665619198","18665619199","18665619191","18665619192","18665619193","18665619194"];
+
+
+    /**
+     * 短信配置
+     * @param int $type 1 系统配置 2 代理配置
+     * @return array
+     */
+    protected function smsConfig(int $type = 1): array
+    {
+        $sms = (new SystemConfig)->where("type","sms")->column("value","name");
+        return [
+            "config" => [
+                'enable' => true,
+                'timeout' => 5.0,
+                "default"   => [
+                    "strategy"  => OrderStrategy::class,
+                    "gateways"  => [$sms['sms_type']]
+                ],
+                "gateways"  => [
+                    'errorlog' => [
+                        'file' => runtime_path().'/tmp/easy-sms.log',
+                    ],
+                    'aliyun' => [
+                        'access_key_id' => $sms['AccessKeyId'],
+                        'access_key_secret' => $sms['AccessKeySecret'],
+                        'sign_name' => trim($sms['sign']),
+                    ],
+                ]
+            ],
+            "template"  => $sms['login']
+        ];
+    }
+    /**
+     * 默认排序筛选
+     * @param array $param
+     * @param string $prefix
+     * @param array $filter
+     * @return string[]
+     */
+    public function defaultSort(array $param = [],string $prefix = "",array $filter = []): array
+    {
+        if (!empty($prefix)) $prefix = $prefix.".";
+        if (isset($param['order']) && $param['order'] == 'descending'){
+            $orderBy = ["{$prefix}{$param['field']}" => "desc"];
+        } else if (isset($param['order']) && $param['order'] == 'ascending'){
+            $orderBy = ["{$prefix}{$param['field']}" => "asc"];
+        } else {
+            $orderBy = ["{$prefix}create_at" => "desc"];
+        }
+        return $orderBy;
+    }
+
+
+    /**
+     * @param array $param 参数
+     * @param array $filter 过滤器
+     * @param string $prefix 链表前缀
+     * @param string $join 链表表名
+     * @param string $field 字段
+     * @param string $forKey 链表首字母
+     * @param string $localKey 被链表首字母
+     */
+    protected function searchVal(array $param = [],array $filter = [],string $prefix = "",string $join = "",string $field = "",string $forKey = "",string $localKey = "")
+    {
+        $orderBy = $this->defaultSort($param,$prefix);
+        $commonFilter = [];
+        // 起止时间
+        if (!empty($param['create'])) {
+            $times = between_time($param['create']);
+            $start = date('Y-m-d',$times['start_time']);
+            $end = date('Y-m-d',($times['end_time'] + 86400));
+            $commonFilter[] = ['create_at', '>=', $start ];
+            $commonFilter[] = ['create_at', '<', $end ];
+        }
+        $filter = array_merge($filter,$commonFilter);
+        if (!empty($join)) {
+            $this->mode =  $this->mode->alias('a')->join("{$join} {$prefix}","a.{$forKey} = {$prefix}.{$localKey}")->field($field);
+        }
+        return $this->mode->order($orderBy)->where($filter);
+    }
+
+}

+ 72 - 0
app/extra/tools/DataExtend.php

@@ -0,0 +1,72 @@
+<?php
+
+namespace app\extra\tools;
+
+class DataExtend
+{
+
+    /**
+     * 一维数组转多维数据树
+     * @param array $its 待处理数据
+     * @param string $cid 自己的主键
+     * @param string $pid 上级的主键
+     * @param string $sub 子数组名称
+     * @return array
+     */
+    public static function arr2tree(array $its, string $cid = 'id', string $pid = 'pid', string $sub = 'children'): array
+    {
+        [$tree, $its] = [[], array_column($its, null, $cid)];
+        foreach ($its as $it) isset($its[$it[$pid]]) ? $its[$it[$pid]][$sub][] = &$its[$it[$cid]] : $tree[] = &$its[$it[$cid]];
+        return $tree;
+    }
+
+    /**
+     * 一维数组转数据树表
+     * @param array $its 待处理数据
+     * @param string $cid 自己的主键
+     * @param string $pid 上级的主键
+     * @param string $path 当前 PATH
+     * @return array
+     */
+    public static function arr2table(array $its, string $cid = 'id', string $pid = 'pid', string $path = 'path'): array
+    {
+        $call = function (array $its, callable $call, array &$data = [], string $parent = '') use ($cid, $pid, $path) {
+            foreach ($its as $it) {
+                $ts = $it['sub'] ?? [];
+                unset($it['sub']);
+                $it[$path] = "{$parent}-{$it[$cid]}";
+                $it['spc'] = count($ts);
+                $it['spt'] = substr_count($parent, '-');
+                $it['spl'] = str_repeat('ㅤ├ㅤ', $it['spt']);
+                $it['sps'] = ",{$it[$cid]},";
+                array_walk_recursive($ts, function ($val, $key) use ($cid, &$it) {
+                    if ($key === $cid) $it['sps'] .= "{$val},";
+                });
+                $it['spp'] = arr2str(str2arr(strtr($parent . $it['sps'], '-', ',')));
+                $data[] = $it;
+                if (empty($ts)) continue;
+                $call($ts, $call, $data, $it[$path]);
+            }
+            return $data;
+        };
+        return $call(static::arr2tree($its, $cid, $pid), $call);
+    }
+
+    /**
+     * 获取数据树子ID集合
+     * @param array $list 数据列表
+     * @param mixed $value 起始有效ID值
+     * @param string $ckey 当前主键ID名称
+     * @param string $pkey 上级主键ID名称
+     * @return array
+     */
+    public static function getArrSubIds(array $list, $value = 0, string $ckey = 'id', string $pkey = 'pid'): array
+    {
+        $ids = [intval($value)];
+        foreach ($list as $vo) if (intval($vo[$pkey]) > 0 && intval($vo[$pkey]) === intval($value)) {
+            $ids = array_merge($ids, static::getArrSubIds($list, intval($vo[$ckey]), $ckey, $pkey));
+        }
+        return $ids;
+    }
+
+}

+ 80 - 0
app/extra/tools/OssRegionExtend.php

@@ -0,0 +1,80 @@
+<?php
+
+namespace app\extra\tools;
+
+class OssRegionExtend
+{
+    /**
+     * 获取存储区域
+     * @return array
+     */
+    public static function OssRegion(): array
+    {
+        return [
+            'oss-cn-hangzhou.aliyuncs.com'       => '华东 1(杭州)',
+            'oss-cn-shanghai.aliyuncs.com'       => '华东 2(上海)',
+            'oss-cn-nanjing.aliyuncs.com'        => '华东 5(南京本地地域)',
+            'oss-cn-fuzhou.aliyuncs.com'         => '华东 6(福州本地地域)',
+            'oss-cn-qingdao.aliyuncs.com'        => '华北 1(青岛)',
+            'oss-cn-beijing.aliyuncs.com'        => '华北 2(北京)',
+            'oss-cn-zhangjiakou.aliyuncs.com'    => '华北 3(张家口)',
+            'oss-cn-huhehaote.aliyuncs.com'      => '华北 5(呼和浩特)',
+            'oss-cn-wulanchabu.aliyuncs.com'     => '华北 6(乌兰察布)',
+            'oss-cn-shenzhen.aliyuncs.com'       => '华南 1(深圳)',
+            'oss-cn-heyuan.aliyuncs.com'         => '华南 2(河源)',
+            'oss-cn-guangzhou.aliyuncs.com'      => '华南 3(广州)',
+            'oss-cn-chengdu.aliyuncs.com'        => '西南 1(成都)',
+            'oss-cn-hongkong.aliyuncs.com'       => '中国(香港)',
+            'oss-us-west-1.aliyuncs.com'         => '美国(硅谷)',
+            'oss-us-east-1.aliyuncs.com'         => '美国(弗吉尼亚)',
+            'oss-ap-northeast-1.aliyuncs.com'    => '日本(东京)',
+            'oss-ap-northeast-2.aliyuncs.com'    => '韩国(首尔)',
+            'oss-ap-southeast-1.aliyuncs.com'    => '新加坡',
+            'oss-ap-southeast-2.aliyuncs.com'    => '澳大利亚(悉尼)',
+            'oss-ap-southeast-3.aliyuncs.com'    => '马来西亚(吉隆坡)',
+            'oss-ap-southeast-5.aliyuncs.com'    => '印度尼西亚(雅加达)',
+            'oss-ap-southeast-6.aliyuncs.com'    => '菲律宾(马尼拉)',
+            'oss-ap-southeast-7.aliyuncs.com'    => '泰国(曼谷)',
+            'oss-ap-south-1.aliyuncs.com'        => '印度(孟买)',
+            'oss-eu-central-1.aliyuncs.com'      => '德国(法兰克福)',
+            'oss-eu-west-1.aliyuncs.com'         => '英国(伦敦)',
+            'oss-me-east-1.aliyuncs.com'         => '阿联酋(迪拜)',
+            'oss-rg-china-mainland.aliyuncs.com' => '无地域属性(中国内地)'
+        ];
+    }
+
+    /**
+     * 获取存储区域
+     * @return array
+     */
+    public static function CosRegion(): array
+    {
+        return [
+            'ap-guangzhou'                => '华南地区(广州)',
+            'up-cn-east-2.qiniup.com'      => '华东-浙江2',
+            'up-z1.qiniup.com'             => '华北-河北',
+            'up-z2.qiniup.com'             => '华南-广东',
+            'up-na0.qiniup.com'            => '北美-洛杉矶',
+            'up-as0.qiniup.com'            => '亚太-新加坡',
+            'up-ap-northeast-1.qiniup.com' => '亚太-首尔',
+        ];
+    }
+
+    /**
+     * 获取存储区域
+     * @return array
+     */
+    public static function QiniuRegion(): array
+    {
+        return [
+            'up.qiniup.com'                => '华东-浙江',
+            'up-cn-east-2.qiniup.com'      => '华东-浙江2',
+            'up-z1.qiniup.com'             => '华北-河北',
+            'up-z2.qiniup.com'             => '华南-广东',
+            'up-na0.qiniup.com'            => '北美-洛杉矶',
+            'up-as0.qiniup.com'            => '亚太-新加坡',
+            'up-ap-northeast-1.qiniup.com' => '亚太-首尔',
+        ];
+    }
+
+}

+ 112 - 0
app/extra/tools/UploadExtend.php

@@ -0,0 +1,112 @@
+<?php
+
+namespace app\extra\tools;
+
+use app\service\system\UploadService;
+
+class UploadExtend
+{
+    /**
+     * 本地对象存储.
+     */
+    public const MODE_LOCAL = 'local';
+
+    /**
+     * 阿里云对象存储.
+     */
+    public const MODE_OSS = 'oss';
+
+    /**
+     * 腾讯云对象存储.
+     */
+    public const MODE_COS = 'cos';
+
+    /**
+     * 七牛云对象存储.
+     */
+    public const MODE_QINIU = 'qiniu';
+
+    /**
+     * S3对象存储.
+     */
+    public const MODE_S3 = 's3';
+
+
+    protected array $config = [];
+
+    /**
+     * Support Storage
+     */
+    static $allowStorage = [
+        self::MODE_LOCAL,
+        self::MODE_OSS,
+        self::MODE_COS,
+        self::MODE_QINIU,
+        self::MODE_S3
+    ];
+
+    /**
+     * 存储磁盘
+     * @param string|null $name
+     * @param bool $_is_file_upload
+     * @return mixed
+     * @author Tinywan(ShaoBo Wan)
+     */
+    public static function disk(string $name = null, bool $_is_file_upload = true)
+    {
+        $storage = $name ?? self::getDefaultStorage();
+        $config = self::getConfig($storage);
+        return new $config['adapter'](array_merge(
+            $config, ['_is_file_upload' => $_is_file_upload]
+        ));
+    }
+
+    /**
+     * 默认存储
+     * @return mixed
+     * @author Tinywan(ShaoBo Wan)
+     */
+    public static function getDefaultStorage()
+    {
+        return self::getConfig('default');
+    }
+
+    /**
+     * 获取存储配置
+     * @param string|null $name 名称
+     * @return mixed
+     * @author Tinywan(ShaoBo Wan)
+     */
+    public static function getConfig(string $name = null)
+    {
+        $config = (new UploadService)->setConfigVal($name);
+        if (!is_null($name)) {
+            return $config['storage'][$name];
+        }
+        return $config['storage']["default"];
+    }
+
+
+    /**
+     * 配置信息
+     * @param array $config
+     */
+    public function config(array $config)
+    {
+        $this->$config = $config;
+        return $this;
+    }
+
+    /**
+     * @param $name
+     * @param $arguments
+     * @return mixed
+     * @author Tinywan(ShaoBo Wan)
+     */
+    public static function __callStatic($name, $arguments)
+    {
+        return static::disk()->{$name}(...$arguments);
+    }
+
+
+}

+ 1 - 1
app/functions.php

@@ -1,7 +1,7 @@
 <?php
 
 
-use app\extra\service\system\SystemService;
+use app\service\system\SystemService;
 use support\Response;
 use Webman\Event\Event;
 

+ 36 - 0
app/middleware/AuthMiddleware.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace app\middleware;
+
+use Shopwwi\WebmanAuth\Auth;
+use Shopwwi\WebmanAuth\JWT;
+use Webman\Http\Request;
+use Webman\Http\Response;
+use Webman\MiddlewareInterface;
+
+class AuthMiddleware implements MiddlewareInterface
+{
+
+    public function process(Request $request, callable $handler): Response
+    {
+        try {
+            $controller = new \ReflectionClass($request->controller);
+            $noNeedLogin = $controller->getDefaultProperties()['noNeedLogin']??[];
+            if (empty($noNeedLogin) || !in_array($request->action, $noNeedLogin)) {
+                $type = $request->header('api-type','');
+                if (empty($type)) return json(['code'=> 0,'msg'=> trans("error.param")]);
+                $token =  $request->header("Authorization","");
+                if (empty($token)) return json(['code'=> 0,'msg'=> trans("error.request")]);
+                (new JWT)->guard("admin")->verify();
+                $user = (new Auth)->guard("admin")->user();
+                if (empty($user)) return json(['code'=>401,'msg'=> trans("error.login")]);
+                $request->user = $user->toArray();
+            }
+        } catch (\Throwable $throwable) {
+            return json(['code'=> 500,'msg'=> $throwable->getMessage()]);
+        }
+        $response = $request->method() == 'OPTIONS' ? response('',204) : $handler($request);
+        return $response;
+    }
+
+}

+ 48 - 0
app/model/system/SystemConfig.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace app\model\system;
+
+use app\extra\basic\Model;
+
+
+/**
+ * @property integer $id (主键)
+ * @property string $type 配置分类
+ * @property string $name 配置名称
+ * @property string $value 配置内容
+ * @property string $desc 字段名称
+ * @property integer $status 
+ * @property mixed $update_at 更新时间
+ */
+class SystemConfig extends Model
+{
+    /**
+     * The connection name for the model.
+     *
+     * @var string|null
+     */
+    protected $connection = 'mysql';
+    
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected string $table = "system_config";
+    
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected string $primaryKey = "id";
+    
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public bool $timestamps = false;
+
+
+}

+ 45 - 0
app/model/system/SystemData.php

@@ -0,0 +1,45 @@
+<?php
+
+namespace app\model\system;
+
+use app\extra\basic\Model;
+
+
+/**
+ * @property integer $id (主键)
+ * @property mixed $name 
+ * @property mixed $content 
+ * @property integer $is_default 是否使用
+ */
+class SystemData extends Model
+{
+    /**
+     * The connection name for the model.
+     *
+     * @var string|null
+     */
+    protected $connection = 'mysql';
+    
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected string $table = "system_data";
+    
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected string $primaryKey = "id";
+    
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public bool $timestamps = false;
+
+
+}

+ 52 - 0
app/model/system/SystemMenu.php

@@ -0,0 +1,52 @@
+<?php
+
+namespace app\model\system;
+
+use app\extra\basic\Model;
+
+
+/**
+ * @property integer $id (主键)
+ * @property integer $from 1管理后台2代理
+ * @property integer $pid 
+ * @property string $name 
+ * @property string $path 
+ * @property string $title 
+ * @property string $type 
+ * @property string $icon 
+ * @property integer $status 
+ * @property integer $sort 
+ * @property mixed $create_at
+ */
+class SystemMenu extends Model
+{
+    /**
+     * The connection name for the model.
+     *
+     * @var string|null
+     */
+    protected $connection = 'mysql';
+    
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected string $table = "system_menu";
+    
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected string $primaryKey = "id";
+    
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public bool $timestamps = false;
+
+
+}

+ 48 - 0
app/model/system/SystemOplog.php

@@ -0,0 +1,48 @@
+<?php
+
+namespace app\model\system;
+
+use app\extra\basic\Model;
+
+
+/**
+ * @property integer $id (主键)
+ * @property string $username 
+ * @property string $node 当前操作节点
+ * @property string $action 操作行为名称
+ * @property string $content 当前操作节点
+ * @property mixed $geoip 
+ * @property mixed $create_at
+ */
+class SystemOplog extends Model
+{
+    /**
+     * The connection name for the model.
+     *
+     * @var string|null
+     */
+    protected $connection = 'mysql';
+    
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected string $table = "system_oplog";
+    
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected string $primaryKey = "id";
+    
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public bool $timestamps = false;
+
+
+}

+ 60 - 0
app/model/system/SystemUser.php

@@ -0,0 +1,60 @@
+<?php
+
+namespace app\model\system;
+
+use app\extra\basic\Model;
+
+
+/**
+ * @property integer $id (主键)
+ * @property integer $agent_id 代理ID
+ * @property string $username 用户名
+ * @property string $truename 真实姓名
+ * @property string $password 密码
+ * @property mixed $salt 密钥串
+ * @property string $contact_name 
+ * @property string $contact_mobile 
+ * @property integer $status 状态
+ * @property integer $type 1管理员2代理子账号3店铺账号
+ * @property integer $is_deleted 删除状态
+ * @property integer $is_super 
+ * @property string $remark 备注
+ * @property string $login_ip 登录IP
+ * @property mixed $login_at 登录时间
+ * @property integer $login_num 
+ * @property string $create_ip 
+ * @property mixed $updated_at 更新时间
+ * @property mixed $create_at 创建时间
+ */
+class SystemUser extends Model
+{
+    /**
+     * The connection name for the model.
+     *
+     * @var string|null
+     */
+    protected $connection = 'mysql';
+    
+    /**
+     * The table associated with the model.
+     *
+     * @var string
+     */
+    protected string $table = "system_user";
+    
+    /**
+     * The primary key associated with the table.
+     *
+     * @var string
+     */
+    protected string $primaryKey = "id";
+    
+    /**
+     * Indicates if the model should be timestamped.
+     *
+     * @var bool
+     */
+    public bool $timestamps = false;
+
+
+}

+ 30 - 0
app/service/system/MenuService.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace app\service\system;
+
+use app\extra\basic\Service;
+use app\model\system\SystemMenu;
+
+class MenuService extends Service
+{
+
+
+    /**
+     * @param int $super
+     * @param int $hide
+     * @param int $type
+     * @return array
+     */
+    public function getMenuList(int $super = 0,int $hide = 0,int $type = 1): array
+    {
+        $model = new SystemMenu();
+        try {
+            $data = $model->where("status",1)->where("from",$type)->order("sort","asc")->select();
+        } catch (\Throwable $throwable) {
+            return [];
+        }
+        return $data->isEmpty()?[]:$data->toArray();
+    }
+
+
+}

+ 182 - 0
app/service/system/SystemService.php

@@ -0,0 +1,182 @@
+<?php
+
+namespace app\service\system;
+
+use app\extra\basic\Service;
+use app\model\system\SystemConfig;
+use app\model\system\SystemData;
+use app\model\system\SystemOplog;
+use support\think\Cache;
+
+class SystemService extends Service
+{
+    /**
+     * 配置缓存数据
+     * @var array
+     */
+    private static array $config = [];
+
+
+    public static function get(string $name = '', string $default = '')
+    {
+        try {
+            $cacheConfig = Cache::get("SystemConfig");
+            if (empty($cacheConfig))
+            {
+                $config = (new SystemConfig)->select();
+                self::setConfig($config);
+                Cache::set("SystemConfig",json_encode($config));
+            } else {
+                $config = json_decode($cacheConfig,true);
+                self::setConfig($config);
+            }
+
+            [$type, $field, $outer] = static::_parse($name);
+            if (empty($name)) {
+                return static::$config;
+            } elseif (isset(static::$config[$type])) {
+                $group = static::$config[$type];
+                if ($outer !== 'raw') foreach ($group as $kk => $vo) {
+                    $group[$kk] = htmlspecialchars(strval($vo));
+                }
+                return $field ? ($group[$field] ?? $default) : $group;
+            } else {
+                return $default;
+            }
+
+        } catch (\Exception $exception)
+        {
+            return false;
+        }
+
+    }
+
+    protected static function setConfig($data)
+    {
+        foreach($data as $item)
+        {
+            static::$config[$item['type']][$item['name']] = $item['value'];
+        }
+    }
+
+    public static function set(string $name, $value = '')
+    {
+        static::$config = [];
+        [$type, $field] = static::_parse($name);
+        if (is_array($value)) {
+            $count = 0;
+            foreach ($value as $kk => $vv) {
+                $count += static::set("{$field}.{$kk}", $vv);
+            }
+            return $count;
+        } else try {
+            Cache::delete("SystemConfig");
+            $map = ['type' => $type, 'name' => $field];
+            $data = array_merge($map, ['value' => $value]);
+            $query = (new SystemConfig)->where($map);
+            return (clone $query)->count() > 0 ? $query->update($data) : $query->insert($data);
+        } catch (\Throwable $exception) {
+            return false;
+        }
+    }
+
+    /**
+     * 解析缓存名称
+     * @param string $rule 配置名称
+     * @return array
+     */
+    private static function _parse(string $rule): array
+    {
+        $type = 'base';
+        if (stripos($rule, '.') !== false) {
+            [$type, $rule] = explode('.', $rule, 2);
+        }
+        [$field, $outer] = explode('|', "{$rule}|");
+        return [$type, $field, strtolower($outer)];
+    }
+
+
+    /**
+     * 读取数据内容
+     * @param string $name
+     * @param array $default
+     * @return array|mixed
+     */
+    public static function getData(string $name, array $default = [])
+    {
+        try {
+            $value = (new SystemData)->where("name",$name)->value("content");
+            if (!empty($value)) return json_decode($value,true);
+            return $default;
+        } catch (\Throwable $exception)
+        {
+            return $default;
+        }
+    }
+
+    /**
+     * 保存数据内容
+     * @param string $name 数据名称
+     * @param mixed $value 数据内容
+     * @return bool
+     */
+    public static function setData(string $name, $value)
+    {
+        try {
+            $data = ['name' => $name, 'content' => json_encode($value, 64 | 256)];
+            return (new SystemData)->setAutoData($data,"name");
+        } catch (\Throwable $exception) {
+            echo $exception->getFile()."\n";
+            echo $exception->getLine()."\n";
+            echo $exception->getMessage()."\n";
+            return false;
+        }
+    }
+
+
+    /**
+     * 写入系统日志内容
+     * @param string $action
+     * @param string $content
+     * @param string $userName
+     * @return boolean
+     */
+    public static function setOplog(string $action, string $content, string $userName): bool
+    {
+        return (new SystemOplog)->insert(static::getOplog($action, $content,$userName)) !== false;
+    }
+
+    /**
+     * 获取系统日志内容
+     * @param string $action
+     * @param string $content
+     * @param string $userName
+     * @return array
+     */
+    public static function getUserOplog(string $action, string $content, string $userName): array
+    {
+        return [
+            'username'  => $userName,
+            'action'    => $action, 'content' => $content,
+            'geoip'     => request()->getRealIp() ?: '127.0.0.1',
+        ];
+    }
+
+    /**
+     * 获取系统日志内容
+     * @param string $action
+     * @param string $content
+     * @param string $userName
+     * @return array
+     */
+    public static function getOplog(string $action, string $content, string $userName): array
+    {
+        return [
+            'node'      => request()->uri(),
+            'action'    => $action, 'content' => $content,
+            'geoip'     => request()->getRealIp() ?: '127.0.0.1',
+            'username'  => $userName ?: '-'
+        ];
+    }
+
+}

+ 98 - 0
app/service/system/UploadService.php

@@ -0,0 +1,98 @@
+<?php
+
+namespace app\service\system;
+
+use app\model\system\SystemConfig;
+use Tinywan\Storage\Adapter\CosAdapter;
+use Tinywan\Storage\Adapter\LocalAdapter;
+use Tinywan\Storage\Adapter\QiniuAdapter;
+
+class UploadService
+{
+
+    /**
+     * 配置
+     * @return array
+     */
+    public function setConfigVal(string $type = ""): array
+    {
+        $data = (new SystemConfig)->where("type","storage")->column("value","name");
+        $config = [];
+        $config['storage']['default'] = $data['type'];
+        $config['storage']['include'] = !empty($data['allow_exts']) ? explode(",",$data['allow_exts']) : [];
+        $config['storage']['exclude'] = !empty($data['noallow_exts']) ? explode(",",$data['noallow_exts']) : [];
+        $config['storage']['nums'] = 10;
+        $config['storage']['single_limit'] = 1024 * 1024 * 200;
+        $config['storage']['total_limit'] = 1024 * 1024 * 200;
+        if (empty($type)) $type = $data['type'];
+        switch ($type)
+        {
+            case "local":
+                $config['storage']['local'] = [
+                    'adapter' => LocalAdapter::class,
+                    'root' => public_path().'/uploads/storage/',
+                    'dirname' => function () {
+                        return date('Ymd');
+                    },
+                    'domain' => "",
+                    'uri' => '/uploads/storage/', // 如果 domain + uri 不在 public 目录下,请做好软链接,否则生成的url无法访问
+                    'algo' => 'sha1',
+                ];
+                break;
+            case "qiniu":
+                $config['storage']['qiniu'] = [
+                    'adapter' => QiniuAdapter::class,
+                    'accessKey' => $data['qiniu_access_key'],
+                    'secretKey' => $data['qiniu_secret_key'],
+                    'bucket' => $data['qiniu_bucket'],
+                    'dirname' => 'storage/'.date("Ymd"),
+                    'domain' => $this->setDomain($data['oss_http_protocol'],$data['qiniu_http_domain']),
+                ];
+                break;
+            case "oss":
+                $config['storage']['oss'] = [
+                    'adapter' => \Tinywan\Storage\Adapter\OssAdapter::class,
+                    'accessKeyId' => $data['oss_access_key'],
+                    'accessKeySecret' => $data['oss_secret_key'],
+                    'bucket' => $data['oss_bucket'],
+                    'dirname' => function () {
+                        return 'storage/'.date("Ymd");
+                    },
+                    'domain' => $this->setDomain($data['oss_http_protocol'],$data['oss_http_domain']),
+                    'endpoint' => $data['oss_region'],
+                    'algo' => 'sha1',
+
+                ];
+                break;
+            case "cos":
+                $config['storage']['cos'] = [
+                    'adapter' => CosAdapter::class,
+                    'secretId' => $data['cos_access_key'],
+                    'secretKey' => $data['cos_secret_key'],
+                    'bucket' => $data['cos_bucket'],
+                    'dirname' => 'storage/'.date("Ymd"),
+                    'domain' => $this->setDomain($data['oss_http_protocol'],$data['cos_http_domain']),
+                    'region' => $data['cos_region'],
+                ];
+                break;
+            default:
+                break;
+        }
+        return $config;
+    }
+
+    /**
+     * 协议域名
+     * @param string $type
+     * @param string $domain
+     * @return string
+     */
+    protected function setDomain(string $type,string $domain): string
+    {
+        if ($type == "auto") {
+            return "//".$domain;
+        }
+        return $type."://".$domain;
+    }
+
+}

+ 2 - 1
composer.json

@@ -53,7 +53,8 @@
     "aliyuncs/oss-sdk-php": "^2.7",
     "qcloud/cos-sdk-v5": "^2.6",
     "qiniu/php-sdk": "^7.14",
-    "luckycmc/webman-province-city-area": "^1.0"
+    "luckycmc/webman-province-city-area": "^1.0",
+    "tinywan/captcha": "^0.0.4"
   },
   "suggest": {
     "ext-event": "For better performance. "

+ 5 - 14
config/container.php

@@ -1,15 +1,6 @@
 <?php
-/**
- * This file is part of webman.
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the MIT-LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @author    walkor<walkor@workerman.net>
- * @copyright walkor<walkor@workerman.net>
- * @link      http://www.workerman.net/
- * @license   http://www.opensource.org/licenses/mit-license.php MIT License
- */
-
-return new Webman\Container;
+$builder = new \DI\ContainerBuilder();
+$builder->addDefinitions(config('dependence', []));
+$builder->useAutowiring(true);
+$builder->useAttributes(true);
+return $builder->build();

+ 1 - 1
config/plugin/linfly/annotation/annotation.php

@@ -12,7 +12,7 @@ return [
     // 注解扫描路径
     'include_paths' => [
         // 应用目录 支持通配符: * , 例如: app/*, app/*.php
-        'app',
+        'app/controller/*',
     ],
     // 扫描排除的路径 支持通配符: *
     'exclude_paths' => [

+ 6 - 0
config/plugin/shopwwi/auth/app.php

@@ -4,6 +4,12 @@
      'enable' => true,
      'app_key' => 'base64:N721v3Gt2I58HH7oiU7a70PQ+i8ekPWRqwI+JSnM1wo=',
      'guard' => [
+         'admin' => [
+             'key' => 'id',
+             'field' => ['id','username'], //设置允许写入扩展中的字段
+             'num' => 2, //-1为不限制终端数量 0为只支持一个终端在线 大于0为同一账号同终端支持数量 建议设置为1 则同一账号同终端在线1个
+             'model'=> [\app\model\system\SystemUser::class,'thinkphp'] // 当为数组时 [app\model\Test::class,'thinkphp'] 来说明模型归属
+         ],
          'user' => [
              'key' => 'id',
              'field' => ['id','name','email','mobile'], //设置允许写入扩展中的字段

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 11 - 0
config/plugin/tinywan/captcha/app.php


+ 6 - 6
config/think-orm.php

@@ -1,21 +1,21 @@
 <?php
 
 return [
-    'default' => 'mysql',
+    'default' => getenv('DB_DEFAULT'),
     'connections' => [
         'mysql' => [
             // 数据库类型
             'type' => 'mysql',
             // 服务器地址
-            'hostname' => '127.0.0.1',
+            'hostname' => getenv('DB_HOST'),
             // 数据库名
-            'database' => 'test',
+            'database' => getenv('DB_NAME'),
             // 数据库用户名
-            'username' => 'root',
+            'username' => getenv('DB_USER'),
             // 数据库密码
-            'password' => '123456',
+            'password' => getenv('DB_PASSWORD'),
             // 数据库连接端口
-            'hostport' => '3306',
+            'hostport' => getenv('DB_PORT'),
             // 数据库连接参数
             'params' => [
                 // 连接超时3秒

+ 3 - 0
resource/translations/en/messages.php

@@ -0,0 +1,3 @@
+<?php
+
+return [];

+ 44 - 0
resource/translations/zh_CN/messages.php

@@ -0,0 +1,44 @@
+<?php
+
+
+return [
+    "empty" => [
+        "mobile"    => "请输入手机号码",
+        "user"      => "请输入用户名",
+        "passwd"    => "请输入登陆密码",
+        "code"      => "请输入验证码",
+        "data"      => "数据不存在",
+        "require"   => "参数不能为空",
+        "agent"     => "未绑定代理信息"
+    ],
+    "error"     => [
+        "exist"         => "数据已存在",
+        "data"          => "操作失败",
+        "status-qrcode" => "请先开启后再生成二维码",
+        "captcha"       => "验证码输入错误",
+        "mobile"        => "手机号码格式错误",
+        "sms-repeat"    => "请勿重复获取",
+        "sms"           => "获取验证码失败,请联系管理员",
+        "mobile-empty"  => "手机号未注册,请开通后再重试",
+        "mobile-exist"  => "手机号已被注册",
+        "user-empty"    => "登录账号不存在",
+        "user-exist"    => "登录账号已存在,请更换",
+        "user-status"   => "该账号已被冻结,请联系管理员",
+        "passwd"        => "登录密码错误",
+        "param"         => "不合法的参数",
+        "request"       => "不合法的请求格式",
+        "login"         => "登录已过期,请重新登录",
+        "sms-err"       => "短信验证码错误",
+        "agent"         => "代理状态异常,请联系管理员",
+        "agent-out"     => "权限已到期,请联系管理员",
+        "agent-no-exist"   => "代理不存在",
+        "store-no-exist"   => "门店不存在",
+    ],
+    "success"   => [
+        "data"  => "操作成功",
+        "sms"   => "验证码已成功发送至%mobile%,请注意查收",
+        "login" => "登录成功",
+        "print" => "已成功发起打印测试,请留意打印机是否正常工作",
+        "print-state" => "已成功发起状态查询任务,请等待1-3分钟再刷新数据"
+    ],
+];

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio