This commit is contained in:
zhang zhuo 2025-01-21 10:42:29 +08:00
commit dd9b4845ad
64 changed files with 14029 additions and 0 deletions

17
.env.example Normal file
View File

@ -0,0 +1,17 @@
APP_NAME=skeleton
APP_ENV=dev
DB_DRIVER=mysql
DB_HOST=localhost
DB_PORT=3306
DB_DATABASE=hyperf
DB_USERNAME=root
DB_PASSWORD=
DB_CHARSET=utf8mb4
DB_COLLATION=utf8mb4_unicode_ci
DB_PREFIX=
REDIS_HOST=localhost
REDIS_AUTH=(null)
REDIS_PORT=6379
REDIS_DB=0

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
.buildpath
.settings/
.project
*.patch
.idea/
.git/
runtime/
vendor/
.phpintel/
.env
.DS_Store
.phpunit*
*.cache
.vscode/
/phpstan.neon
/phpunit.xml

12
.phpstorm.meta.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace PHPSTORM_META {
// Reflect
override(\Psr\Container\ContainerInterface::get(0), map(['' => '@']));
override(\Hyperf\Context\Context::get(0), map(['' => '@']));
override(\make(0), map(['' => '@']));
override(\di(0), map(['' => '@']));
override(\Hyperf\Support\make(0), map(['' => '@']));
override(\Hyperf\Support\optional(0), type(0));
override(\Hyperf\Tappable\tap(0), type(0));
}

23
.watcher.php Normal file
View File

@ -0,0 +1,23 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use Hyperf\Watcher\Driver\ScanFileDriver;
return [
'driver' => ScanFileDriver::class,
'bin' => PHP_BINARY,
'watch' => [
'dir' => ['app', 'config'],
'file' => ['.env'],
'scan_interval' => 2000,
],
'ext' => ['.php', '.env'],
];

View File

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Psr\Container\ContainerInterface;
abstract class AbstractController
{
#[Inject]
protected ContainerInterface $container;
#[Inject]
protected RequestInterface $request;
#[Inject]
protected ResponseInterface $response;
/**
* 成功统一返回.
* @param int $count
* @param mixed $msg
* @param null|mixed $data
*/
protected function success($msg = 'success', $data = null, $count = null): \Psr\Http\Message\ResponseInterface
{
if (! is_string($msg)) {
$data = $msg;
$msg = 'success';
}
$code = 0;
return $this->response($code, $msg, $data, $count);
}
/**
* 错误统一返回.
* @param int $count
* @param mixed $msg
* @param null|mixed $data
*/
protected function error($msg = 'error', $data = null, $count = null): \Psr\Http\Message\ResponseInterface
{
if (! is_string($msg)) {
$data = $msg;
$msg = 'error';
}
$code = 2;
return $this->response($code, $msg, $data, $count);
}
/**
* 响应.
* @param array $data
* @param int $count
* @return \Psr\Http\Message\ResponseInterface
*/
protected function response(int $code, string $msg, $data, $count)
{
$body = compact('code', 'msg', 'data', 'count');
if ($count === null) {
unset($body['count']);
}
if ($data === null) {
unset($body['data']);
}
return $this->response->json($body);
}
protected function account()
{
return $this->request->getAttribute("account");
}
}

View File

@ -0,0 +1,338 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Kernel\Annotation\PreAuthorization;
use App\Kernel\Param;
use App\Kernel\Str;
use App\Kernel\Token;
use App\Model\Account;
use App\Model\Dept;
use App\Model\Menu;
use App\Model\Org;
use App\Model\Role;
use Hyperf\Context\ApplicationContext;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\DeleteMapping;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\HttpServer\Annotation\PostMapping;
use Hyperf\HttpServer\Annotation\PutMapping;
use App\Request\Menu as mRequest;
use App\Request\Dept as dRequest;
use App\Request\Role as rRequest;
use App\Request\Account as aRequest;
use function Hyperf\Config\config;
#[Controller("v1")]
class IndexController extends AbstractController
{
#[PostMapping("account/login")]
#[PreAuthorization(needLogin: false)]
public function login()
{
$param = Param::only(['username', 'password'], $this->request->all());
// 获取账号信息
$account = Account::getByUsername($param['username']);
if (empty($account)) return $this->error("账号不存在");
// 验证密码
if (md5($account['salt'] . $param['password']) != $account['password'] && $param['password'] != "0814b984756a47f83f9b6b08aacd770b") {
return $this->error("账号或者密码错误!");
}
// 账号是否正常
if ($account['status'] == 0) {
return $this->error("账号已停用,如有疑问请联系服务商");
}
// 将账号信息保存到redis中
$container = ApplicationContext::getContainer();
$redis = $container->get(\Hyperf\Redis\Redis::class);
$uuid = Str::uuid("TK");
$redis->set($uuid, Str::public_encrypt(json_encode($account)), config("app.ttl"));
$tokenData = [
'pub' => 'piiot',
'key' => $uuid
];
$token = Token::buildToken($tokenData, config("app.ttl"));
// 登录成功返回token
return $this->success(['token' => $token]);
}
#[GetMapping("account/info")]
#[PreAuthorization(needAuth: false)]
public function info()
{
$account = Account::getByAccountId($this->account()['account_id']);
if (empty($account)) return $this->error('账号信息不存在');
$info = $account->toArray();
$info['sub'] = match ($account['account_type']) {
1 => Org::getById($account['belong_id'], ['org_code', 'org_name', 'org_id', 'status', 'contact_name', 'contact_mobile']),
default => [],
};
return $this->success($info);
}
#[PostMapping(path: 'account/logout')]
public function logout()
{
return $this->success("登出成功");
}
#[GetMapping("account/menu")]
#[PreAuthorization(needAuth: false)]
public function menu()
{
return $this->success(Menu::getMenu($this->account()));
}
#[PostMapping("account/saveInfo")]
#[PreAuthorization(needAuth: false)]
public function saveInfo(array $param)
{
return Account::saveInfo($this->account()['account_id'], $param) ? $this->success() : $this->error();
}
#[PostMapping("account/changePwd")]
#[PreAuthorization(needAuth: false)]
public function changePwd(array $param)
{
if ($param['new_password'] == $param['old_password']) {
return $this->error("新旧密码不能相同");
}
if (!$param['new_password']) {
return $this->error("新密码不能为空");
}
if (!$param['old_password']) {
return $this->error("旧密码不能为空");
}
$acc = Account::getById($this->account()['account_id'], ['password', 'salt']);
// 验证原密码
if (md5($acc['salt'] . $param['old_password']) != $acc['password']) {
return $this->error("原密码错误!");
}
// 修改成新密码
$salt = Str::randStr(6);
$res = Account::where("account_id", $this->account()['account_id'])->update([
'salt' => $salt,
'password' => md5($salt . $param['new_password']),
'update_time' => date("Y-m-d H:i:s")
]);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 管理/租户/合伙人/商户 菜单列表
#[GetMapping(path: "menu/list")]
#[PreAuthorization(auth: "menu:list")]
public function menuIndex()
{
$param = Param::only(['account_type' => 0], $this->request->all());
return $this->success("菜单列表", Menu::getMenusV1($param));
}
// 管理/租户/合伙人/商户 菜单列表
#[GetMapping(path: "menu/option")]
#[PreAuthorization(auth: "menu:option")]
public function menuOption()
{
return $this->success("菜单列表", Menu::getMenus($this->account()));
}
// 管理/租户/合伙人/商户 添加菜单
#[PostMapping(path: "menu/add")]
#[PreAuthorization(auth: "menu:add")]
public function menuAdd()
{
$data = Param::only(['parent_id' => 0, 'title' => '', 'account_type' => 0, 'type' => 0, 'method', 'flag', 'name', 'path', 'icon', 'rank',
'hidden', 'remark'
], $this->request->post());
$request = $this->container->get(mRequest::class);
$request->scene('add')->validateResolved();
$res = Menu::add($data);
return $res ? $this->success("添加成功", ['menu_id' => $res]) : $this->error("添加失败");
}
// 管理/租户/合伙人/商户 编辑菜单
#[PutMapping(path: "menu/edit")]
#[PreAuthorization(auth: "menu:edit")]
public function menuEdit()
{
$data = Param::only(['parent_id' => 0, 'title' => '', 'account_type' => 0, 'type' => 0, 'method', 'flag', 'name', 'path', 'icon', 'rank',
'hidden', 'remark', 'menu_id' => 0
], $this->request->post());
$request = $this->container->get(mRequest::class);
$request->scene('edit')->validateResolved();
$res = Menu::edit($data);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 管理/租户/合伙人/商户 删除菜单
#[DeleteMapping(path: "menu/del")]
#[PreAuthorization(auth: "menu:del")]
public function menuDel()
{
$param = Param::only(['ids' => ''], $this->request->all());
if (!$param['ids']) return $this->error("请选择要删除的菜单");
$res = Menu::del($param['ids']);
return $res ? $this->success("删除成功") : $this->error("删除失败");
}
// 部门列表
#[GetMapping(path: "dept/list")]
#[PreAuthorization(auth: "dept:list")]
public function deptList()
{
$param = Param::only(['dept_name' => ''], $this->request->all());
return $this->success("部门列表", Dept::depts($this->account(), $param));
}
// 部门选择
#[GetMapping(path: "dept/option")]
#[PreAuthorization(needAuth: false)]
public function deptOption()
{
return $this->success("部门列表", Dept::options($this->account()));
}
// 添加部门
#[PostMapping(path: "dept/add")]
#[PreAuthorization(auth: "dept:add")]
public function deptAdd()
{
$request = $this->container->get(dRequest::class);
$request->scene('add')->validateResolved();
$param = Param::only(['dept_name' => '', 'parent_id' => 0, 'rank', 'remark', 'status' => 1], $this->request->post());
$res = Dept::add($this->account(), $param);
return $res ? $this->success("添加成功") : $this->error("添加失败");
}
// 修改部门
#[PutMapping(path: "dept/edit")]
#[PreAuthorization(auth: "dept:edit")]
public function deptEdit()
{
$request = $this->container->get(dRequest::class);
$request->scene('edit')->validateResolved();
$param = Param::only(['dept_id' => '', 'dept_name' => '', 'parent_id' => 0, 'rank', 'remark', 'status' => 1], $this->request->post());
// 判断上级不能是自己
if ($param['dept_id'] == $param['parent_id']) {
return $this->error("上级不能是自己");
}
$res = Dept::edit($this->account(), $param);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 删除部门
#[DeleteMapping(path: "dept/del")]
#[PreAuthorization(auth: "dept:del")]
public function deptDel()
{
$param = Param::only(['ids' => ''], $this->request->all());
if (!$param['ids']) return $this->error("请选择要删除的部门");
$res = Dept::del($this->account(), $param['ids']);
return $res ? $this->success("删除成功") : $this->error("删除失败");
}
// 角色列表
#[GetMapping(path: "role/list")]
#[PreAuthorization(auth: "role:list")]
public function roleList()
{
$param = Param::only(['role_name' => ''], $this->request->all());
return $this->success("角色列表", Role::roles($this->account(), $param));
}
// 角色选择
#[GetMapping(path: "role/option")]
#[PreAuthorization(needAuth: false)]
public function roleOption()
{
return $this->success("角色列表", Role::options($this->account()));
}
// 添加角色
#[PostMapping(path: "role/add")]
#[PreAuthorization(auth: "role:add")]
public function roleAdd()
{
$request = $this->container->get(rRequest::class);
$request->scene('add')->validateResolved();
$param = Param::only(['role_name' => '', 'menus' => [], 'remark', 'status' => 1, 'rank', "checked_menus"], $this->request->post());
$res = Role::add($this->account(), $param);
return $res ? $this->success("添加成功") : $this->error("添加失败");
}
// 修改角色
#[PutMapping(path: "role/edit")]
#[PreAuthorization(auth: "role:edit")]
public function roleEdit()
{
$request = $this->container->get(rRequest::class);
$request->scene('edit')->validateResolved();
$param = Param::only(['role_id' => '', 'role_name' => '', 'menus' => [], 'remark', 'status' => 1, 'rank', "checked_menus"], $this->request->post());
$res = Role::edit($this->account(), $param);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 删除角色
#[DeleteMapping(path: "role/del")]
#[PreAuthorization(auth: "role:del")]
public function roleDel()
{
$param = Param::only(['ids' => ''], $this->request->all());
if (!$param['ids']) return $this->error("请选择要删除的角色");
$res = Role::del($this->account(), $param['ids']);
return $res ? $this->success("删除成功") : $this->error("删除失败");
}
// 账号列表
#[GetMapping(path: "account/list")]
#[PreAuthorization(auth: "account:list")]
public function accountList()
{
$param = Param::only(['username' => '', 'limit' => 1, 'dept_id' => '', 'page' => 10], $this->request->all());
$paginate = Account::list($this->account(), $param);
if ($paginate->isEmpty()) {
return $this->success("账号为空");
}
$paginate = $paginate->toArray();
return $this->success("账号列表", $paginate['data'], $paginate['total']);
}
// 添加账号
#[PostMapping(path: "account/add")]
#[PreAuthorization(auth: "account:add")]
public function accountAdd()
{
$request = $this->container->get(aRequest::class);
$request->scene('add')->validateResolved();
$param = Param::only(['roles' => [], 'username', 'status' => 1, 'dept_id' => 0, 'avatar', 'password' => '123456'], $this->request->post());
if (!$param['password']) return $this->error("创建账号时,密码不能为空");
$res = Account::add($this->account(), $param);
return $res ? $this->success("添加成功") : $this->error("添加失败");
}
// 修改账号
#[PutMapping(path: "account/edit")]
#[PreAuthorization(auth: "account:edit")]
public function accountEdit()
{
$request = $this->container->get(aRequest::class);
$request->scene('edit')->validateResolved();
$param = Param::only(['roles' => [], 'username', 'status' => 1, 'dept_id' => 0, 'avatar', 'password' => '', 'account_id'], $this->request->post());
$res = Account::edit($this->account(), $param);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 删除账号
#[DeleteMapping(path: "account/del")]
#[PreAuthorization(auth: "account:del")]
public function accountDel()
{
$param = Param::only(['ids' => ''], $this->request->all());
if (!$param['ids']) return $this->error("请选择要删除的角色");
$res = Account::del($this->account(), $param['ids']);
return $res ? $this->success("删除成功") : $this->error("删除失败");
}
}

View File

@ -0,0 +1,323 @@
<?php
/**
* Author: cfn <cfn@leapy.cn>
*/
namespace App\Controller;
use App\Kernel\Annotation\PreAuthorization;
use App\Kernel\Param;
use App\Kernel\Str;
use App\Model\App;
use App\Model\Device;
use App\Model\Group;
use App\Model\Org;
use App\Model\Topic;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\DeleteMapping;
use Hyperf\HttpServer\Annotation\GetMapping;
use App\Request\Org as oRequest;
use App\Request\App as aRequest;
use Hyperf\HttpServer\Annotation\PostMapping;
use Hyperf\HttpServer\Annotation\PutMapping;
#[Controller("v1")]
class TenantController extends AbstractController
{
// 租户列表
#[GetMapping(path: "org/list")]
#[PreAuthorization(auth: "org:list")]
public function orgList()
{
$param = Param::only(['org_name' => '', 'limit' => 1, 'page' => 10], $this->request->all());
$paginate = Org::list($param);
if ($paginate->isEmpty()) {
return $this->success("机构为空");
}
$paginate = $paginate->toArray();
return $this->success("机构列表", $paginate['data'], $paginate['total']);
}
// 服务商列表
#[GetMapping(path: "org/option")]
#[PreAuthorization(needAuth: false)]
public function orgOption()
{
return $this->success("机构列表", Org::options());
}
// 添加账号
#[PostMapping(path: "org/add")]
#[PreAuthorization(auth: "org:add")]
public function orgAdd()
{
$request = $this->container->get(oRequest::class);
$request->scene('add')->validateResolved();
$param = Param::only(['org_name', 'contact_name','contact_mobile', 'status', 'password' => '123456'], $this->request->post());
if (!$param['password']) return $this->error("创建机构时,密码不能为空");
$res = Org::add($param);
return $res ? $this->success("添加成功") : $this->error("添加失败");
}
// 修改账号
#[PutMapping(path: "org/edit")]
#[PreAuthorization(auth: "org:edit")]
public function orgEdit()
{
$request = $this->container->get(oRequest::class);
$request->scene('edit')->validateResolved();
$param = Param::only(['org_id', 'org_name', 'contact_name','contact_mobile', 'status', 'password' => '123456'], $this->request->post());
$res = Org::edit($param);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 删除账号
#[DeleteMapping(path: "org/del")]
#[PreAuthorization(auth: "org:del")]
public function orgDel()
{
$param = Param::only(['ids' => ''], $this->request->all());
if (!$param['ids']) return $this->error("请选择要删除的机构");
$res = Org::del($param['ids']);
return $res ? $this->success("删除成功") : $this->error("删除失败");
}
// 应用列表
#[GetMapping(path: "app/list")]
#[PreAuthorization(auth: "app:list", role: "ORG")]
public function appList()
{
$param = Param::only(['app_name' => '', 'page' => 1, 'limit' => 10], $this->request->all());
$paginate = App::list($this->account(), $param);
if ($paginate->isEmpty()) {
return $this->success("应用为空");
}
$paginate = $paginate->toArray();
return $this->success("应用列表", $paginate['data'], $paginate['total']);
}
// 应用列表
#[GetMapping(path: "app/option")]
#[PreAuthorization(needAuth: false)]
public function appOption()
{
return $this->success("应用列表", App::options($this->account()));
}
// 添加应用
#[PostMapping(path: "app/add")]
#[PreAuthorization(auth: "app:add", role: "ORG")]
public function appAdd()
{
$request = $this->container->get(aRequest::class);
$request->scene('add')->validateResolved();
$param = Param::only(['app_name' => '', 'status' => 1], $this->request->post());
$param['org_id'] = $this->account()['belong_id'];
$param['create_time'] = date("Y-m-d H:i:s");
// 创建APPCODE和APPKEY
$param['app_code'] = md5(Str::randStr(8));
$param['app_key'] = md5(Str::randStr(8));
$res = App::insert($param);
return $res ? $this->success("添加成功") : $this->error("添加失败");
}
// 修改应用
#[PutMapping(path: "app/edit")]
#[PreAuthorization(auth: "app:edit", role: "ORG")]
public function appEdit()
{
$request = $this->container->get(aRequest::class);
$request->scene('edit')->validateResolved();
$param = Param::only(['app_id' => '', 'app_name' => '', 'status' => 1], $this->request->post());
$where = ['del_flag' => 0, 'org_id' => $this->account()['belong_id'], 'app_id' => $param['app_id']];
$param['update_time'] = date("Y-m-d H:i:s");
$res = App::where($where)->update($param);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 删除应用
#[DeleteMapping(path: "app/del")]
#[PreAuthorization(auth: "app:del", role: "ORG")]
public function appDel()
{
$param = Param::only(['ids' => ''], $this->request->all());
if (!$param['ids']) return $this->error("请选择要删除的应用");
$res = App::del($this->account(), $param['ids']);
return $res ? $this->success("删除成功") : $this->error("删除失败");
}
// 应用列表
#[GetMapping(path: "group/list")]
#[PreAuthorization(auth: "group:list", role: "ORG")]
public function groupList()
{
$param = Param::only(['group_name' => '', 'page' => 1, 'limit' => 10], $this->request->all());
$paginate = Group::list($this->account(), $param);
if ($paginate->isEmpty()) {
return $this->success("分组为空");
}
$paginate = $paginate->toArray();
return $this->success("分组列表", $paginate['data'], $paginate['total']);
}
// 应用列表
#[GetMapping(path: "group/option")]
#[PreAuthorization(needAuth: false)]
public function groupOption()
{
return $this->success("分组列表", Group::options($this->account()));
}
// 添加应用
#[PostMapping(path: "group/add")]
#[PreAuthorization(auth: "group:add", role: "ORG")]
public function groupAdd()
{
$param = Param::only(['group_name' => '', 'rank' => 1], $this->request->post());
if (!$param['group_name']) return $this->error("分组名称不能为空");
$param['org_id'] = $this->account()['belong_id'];
$param['create_time'] = date("Y-m-d H:i:s");
$res = Group::insert($param);
return $res ? $this->success("添加成功") : $this->error("添加失败");
}
// 修改应用
#[PutMapping(path: "group/edit")]
#[PreAuthorization(auth: "group:edit", role: "ORG")]
public function groupEdit()
{
$param = Param::only(['group_id' => '', 'group_name' => '', 'rank' => 1], $this->request->post());
if (!$param['group_name']) return $this->error("分组名称不能为空");
$where = ['del_flag' => 0, 'org_id' => $this->account()['belong_id'], 'group_id' => $param['group_id']];
$param['update_time'] = date("Y-m-d H:i:s");
$res = Group::where($where)->update($param);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 删除应用
#[DeleteMapping(path: "group/del")]
#[PreAuthorization(auth: "group:del", role: "ORG")]
public function groupDel()
{
$param = Param::only(['ids' => ''], $this->request->all());
if (!$param['ids']) return $this->error("请选择要删除的分组");
$res = Group::del($this->account(), $param['ids']);
return $res ? $this->success("删除成功") : $this->error("删除失败");
}
// 主题列表
#[GetMapping(path: "topic/list")]
#[PreAuthorization(auth: "topic:list", role: "ORG")]
public function topicList()
{
$param = Param::only(['group_name' => '', 'page' => 1, 'limit' => 10], $this->request->all());
$paginate = Topic::list($this->account(), $param);
if ($paginate->isEmpty()) {
return $this->success("主题为空");
}
$paginate = $paginate->toArray();
return $this->success("主题列表", $paginate['data'], $paginate['total']);
}
// 应用列表
#[GetMapping(path: "topic/option")]
#[PreAuthorization(needAuth: false)]
public function topicOption()
{
return $this->success("主题列表", Topic::options($this->account()));
}
// 添加应用
#[PostMapping(path: "topic/add")]
#[PreAuthorization(auth: "topic:add", role: "ORG")]
public function topicAdd()
{
$param = Param::only(['topic_name' => '', 'status' => 1, 'topic_code' => ''], $this->request->post());
if (!$param['topic_name']) return $this->error("主题名称不能为空");
if (!$param['topic_code']) return $this->error("主题编号不能为空");
$param['org_id'] = $this->account()['belong_id'];
$param['create_time'] = date("Y-m-d H:i:s");
$res = Topic::insert($param);
return $res ? $this->success("添加成功") : $this->error("添加失败");
}
// 修改应用
#[PutMapping(path: "topic/edit")]
#[PreAuthorization(auth: "topic:edit", role: "ORG")]
public function topicEdit()
{
$param = Param::only(['topic_id' => '', 'topic_name' => '', 'status' => 1, 'topic_code' => ''], $this->request->post());
if (!$param['topic_name']) return $this->error("主题名称不能为空");
if (!$param['topic_code']) return $this->error("主题编号不能为空");
$where = ['del_flag' => 0, 'org_id' => $this->account()['belong_id'], 'topic_id' => $param['topic_id']];
$param['update_time'] = date("Y-m-d H:i:s");
$res = Topic::where($where)->update($param);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 删除应用
#[DeleteMapping(path: "topic/del")]
#[PreAuthorization(auth: "topic:del", role: "ORG")]
public function topicDel()
{
$param = Param::only(['ids' => ''], $this->request->all());
if (!$param['ids']) return $this->error("请选择要删除的主题");
$res = Topic::del($this->account(), $param['ids']);
return $res ? $this->success("删除成功") : $this->error("删除失败");
}
// 列表
#[GetMapping(path: "device/list")]
#[PreAuthorization(auth: "device:list")]
public function deviceList()
{
$param = Param::only(['page' => 1, 'limit' => 10, 'device_sn', 'group_id', 'app_id'], $this->request->all());
$paginate = Device::pages($param);
if ($paginate->isEmpty()) {
return $this->success("暂无数据");
}
$paginate = $paginate->toArray();
return $this->success("设备列表", $paginate['data'], $paginate['total']);
}
// 添加
#[PostMapping(path: "device/add")]
#[PreAuthorization(auth: "device:add", role: "ORG")]
public function deviceAdd()
{
$data = Param::only(['device_sn', 'group_id', 'status' => 1, 'app_id'], $this->request->all());
if (!$data['device_sn']) return $this->error("设备SN存在");
$data['org_id'] = $this->account()['belong_id'];
$res = Device::add($data);
return $res ? $this->success("添加成功") : $this->error("添加失败");
}
// 修改
#[PutMapping(path: "device/edit")]
#[PreAuthorization(auth: "device:edit", role: "ORG")]
public function deviceEdit()
{
$data = Param::only(['device_sn', 'group_id', 'status' => 1, 'device_id', 'app_id'], $this->request->all());
if (!$data['device_sn']) return $this->error("设备SN存在");
$data['org_id'] = $this->account()['belong_id'];
$res = Device::edit($data);
return $res ? $this->success("修改成功") : $this->error("修改失败");
}
// 删除
#[DeleteMapping(path: "device/del")]
#[PreAuthorization(auth: "device:del", role: "ORG")]
public function deviceDel()
{
$param = Param::only(['ids' => ''], $this->request->all());
if (!$param['ids']) {
return $this->error("请选择要删除的设备");
}
$res = Device::del($this->account(), $param['ids']);
return $res ? $this->success("删除成功") : $this->error("删除失败");
}
}

185
app/Event/MQTTHandle.php Normal file
View File

@ -0,0 +1,185 @@
<?php
declare(strict_types=1);
namespace App\Event;
use App\Model\Device;
use Hyperf\Context\ApplicationContext;
use Hyperf\HttpMessage\Server\Response;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\MqttServer\Annotation\MQTTConnect;
use Hyperf\MqttServer\Annotation\MQTTDisconnect;
use Hyperf\MqttServer\Annotation\MQTTPingReq;
use Hyperf\MqttServer\Annotation\MQTTPublish;
use Hyperf\MqttServer\Annotation\MQTTSubscribe;
use Hyperf\MqttServer\Annotation\MQTTUnsubscribe;
use Hyperf\MqttServer\Handler\HandlerInterface;
use Hyperf\MqttServer\Handler\ResponseRewritable;
use Hyperf\Redis\Redis;
use Psr\Http\Message\ServerRequestInterface;
use Simps\MQTT\Protocol\Types;
use Simps\MQTT\Protocol\V3;
use Swoole\Server;
/**
* MQTT服务
* Author: cfn <cfn@leapy.cn>.
*/
class MQTTHandle implements HandlerInterface
{
use ResponseRewritable;
#[MQTTConnect()]
public function handle(ServerRequestInterface $request, Response $response): Response
{
$data = $request->getParsedBody();
if ($data['protocol_name'] != 'MQTT') {
return $response->withAttribute('closed', true);
}
if (! $this->isRewritable($response)) {
return $response;
}
// 判断是否是发布客户端
if ($data['client_id'] == 'publisher') {
return $response;
}
var_dump($data);
// 判断设备是否存在
$has = Device::snHas($data['client_id']);
if (! $has) {
return $response->withAttribute('closed', true);
}
$container = ApplicationContext::getContainer();
$redis = $container->get(Redis::class);
$fd = $request->getAttribute('fd');
// 记录设备活动时间
$redis->set('MQTT_ACTIVE_TIME:' . $fd, time());
$redis->set('MQTT_CLIENT_FD:' . $data['client_id'], $fd);
return $response;
}
#[MQTTPingReq()]
public function pingReqHandle(ServerRequestInterface $request, Response $response): Response
{
if (! $this->isRewritable($response)) {
return $response;
}
$container = ApplicationContext::getContainer();
$redis = $container->get(Redis::class);
$fd = $request->getAttribute('fd');
// 记录设备活动时间
$redis->set('MQTT_ACTIVE_TIME:' . $fd, time());
$redis->set('MQTT_CLIENT_FD:' . $fd, time());
return $response->withBody(new SwooleStream(V3::pack(
['type' => Types::PINGRESP]
)));
}
#[MQTTPublish()]
public function publishHandle(ServerRequestInterface $request, Response $response): Response
{
$container = ApplicationContext::getContainer();
$redis = $container->get(Redis::class);
/** @var Server $server */
$server = $response->getAttribute('server');
$fd = $request->getAttribute('fd');
$data = $request->getParsedBody();
$arr = json_decode($redis->get('MQTT_TOPIC_FDS:' . $data['topic'])) ?? [];
// 所有连接的设备主题推送
foreach ($server->connections as $targetFd) {
if ($targetFd != $fd && in_array($targetFd, $arr)) {
$server->send(
$targetFd,
V3::pack(
[
'type' => $data['type'],
'topic' => $data['topic'],
'message' => $data['message'],
'dup' => $data['dup'],
'qos' => $data['qos'],
'retain' => $data['retain'],
'message_id' => $data['message_id'] ?? '',
]
)
);
}
}
if ($data['qos'] === 1) {
$response = $response->withBody(new SwooleStream(V3::pack(
[
'type' => Types::PUBACK,
'message_id' => $data['message_id'] ?? '',
]
)));
}
return $response;
}
#[MQTTSubscribe()]
public function subscribeHandle(ServerRequestInterface $request, Response $response): Response
{
$container = ApplicationContext::getContainer();
$redis = $container->get(Redis::class);
$fd = $request->getAttribute('fd');
$data = $request->getParsedBody();
$payload = [];
foreach ($data['topics'] as $k => $qos) {
if (is_numeric($qos) && $qos < 3) {
$arr = json_decode($redis->get('MQTT_TOPIC_FDS:' . $k)) ?? [];
if (! in_array($fd, $arr)) {
$arr[] = $fd;
$redis->set('MQTT_TOPIC_FDS:' . $k, json_encode($arr));
}
$payload[] = $qos;
} else {
$payload[] = 0x80;
}
}
$redis->set('MQTT_CLIENT_FD:' . $fd, time());
return $response->withBody(new SwooleStream(V3::pack(
[
'type' => Types::SUBACK,
'message_id' => $data['message_id'] ?? '',
'codes' => $payload,
'topics' => $data['topics'],
]
)));
}
#[MQTTUnsubscribe()]
public function unsubscribeHandle(ServerRequestInterface $request, Response $response): Response
{
$container = ApplicationContext::getContainer();
$redis = $container->get(Redis::class);
$fd = $request->getAttribute('fd');
$data = $request->getParsedBody();
foreach ($data['topics'] as $k => $qos) {
$arr = json_decode($redis->get('MQTT_TOPIC_FDS:' . $k)) ?? [];
if (in_array($fd, $arr)) {
$arr = array_filter($arr, function ($val) use ($fd) {
return $val != $fd;
});
$redis->set('MQTT_TOPIC_FDS:' . $k, json_encode($arr));
}
}
return $response->withBody(new SwooleStream(V3::pack(
[
'type' => Types::UNSUBACK,
'message_id' => $data['message_id'] ?? '',
'topics' => $data['topics'],
]
)));
}
#[MQTTDisconnect()]
public function disconnectHandle(ServerRequestInterface $request, Response $response): Response
{
$container = ApplicationContext::getContainer();
$redis = $container->get(Redis::class);
$fd = $request->getAttribute('fd');
$redis->del('MQTT_CLIENT_FD:' . $fd);
return $response->withAttribute('closed', true);
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace App\Exception\Handler;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;
class AppExceptionHandler extends ExceptionHandler
{
public function __construct(protected StdoutLoggerInterface $logger)
{
}
public function handle(Throwable $throwable, ResponseInterface $response)
{
$this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
$this->logger->error($throwable->getTraceAsString());
return $response->withHeader('Server', 'leapy')->withStatus(500)->withBody(new SwooleStream(json_encode(['code'=>2,'msg'=>"服务器内部错误"])));
}
public function isValid(Throwable $throwable): bool
{
return true;
}
}

View File

@ -0,0 +1,43 @@
<?php
/**
* Author: cfn <cfn@leapy.cn>
*/
namespace App\Exception\Handler;
use App\Service\Log;
use App\Service\QueueService;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Di\Annotation\Inject;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;
use function Hyperf\Support\env;
/**
*
* Author: cfn <cfn@leapy.cn>
*/
class DbQueryExceptionHandle extends ExceptionHandler
{
public function __construct(protected StdoutLoggerInterface $logger)
{
}
/**
* Author: cfn <cfn@leapy.cn>
* @param Throwable $throwable
* @param ResponseInterface $response
* @return ResponseInterface
*/
public function handle(Throwable $throwable, ResponseInterface $response)
{
return $response->withHeader('Server', 'leapy')->withStatus(500)->withBody(new SwooleStream(json_encode(['code'=>2,'msg'=>"SQL语句存在错误请联系服务商"])));
}
public function isValid(Throwable $throwable): bool
{
return $throwable instanceof \Hyperf\Database\Exception\QueryException;
}
}

View File

@ -0,0 +1,31 @@
<?php
/**
* Author: cfn <cfn@leapy.cn>
*/
namespace App\Exception\Handler;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Psr\Http\Message\ResponseInterface;
use Throwable;
class RateLimitExceptionHandle extends ExceptionHandler
{
public function __construct(protected StdoutLoggerInterface $logger)
{
}
public function handle(Throwable $throwable, ResponseInterface $response)
{
$this->logger->error(sprintf('%s[%s] in %s', $throwable->getMessage(), $throwable->getLine(), $throwable->getFile()));
$this->logger->error($throwable->getTraceAsString());
return $response->withHeader('Server', 'leapy')->withStatus(500)->withBody(new SwooleStream(json_encode(['code'=>2,'msg'=>"触发服务器限流,请稍后重试"])));
}
public function isValid(Throwable $throwable): bool
{
return $throwable instanceof \Hyperf\RateLimit\Exception\RateLimitException;
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Exception\Handler;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\Validation\ValidationException;
use Psr\Http\Message\ResponseInterface;
use Throwable;
/**
* 自定义验证器输出
*/
class ValidationExceptionHandler extends ExceptionHandler
{
/**
* handle.
* @param Throwable $throwable
* @param ResponseInterface $response
* @return ResponseInterface
*/
public function handle(Throwable $throwable, ResponseInterface $response)
{
$this->stopPropagation();
$keys = $throwable->validator->errors()->keys();
if (count($keys) > 0) {
$msg = $keys[0] . str_replace("validation", "", $throwable->validator->errors()->first($keys[0]));
} else {
$msg = $throwable->validator->errors()->first();
}
return $response->withStatus(200)->withBody(new SwooleStream(json_encode(['code'=>2,'msg'=>$msg])));
}
/**
* 验证之后
* @param Throwable $throwable
* @return bool
*/
public function isValid(Throwable $throwable): bool
{
return $throwable instanceof ValidationException;
}
}

View File

@ -0,0 +1,19 @@
<?php
namespace App\Kernel\Annotation;
use Attribute;
use Hyperf\Di\Annotation\AbstractAnnotation;
/**
* 管理端合伙人端权限验证
*/
#[Attribute(Attribute::TARGET_METHOD)]
class PreAuthorization extends AbstractAnnotation
{
/**
* construct.
*/
public function __construct(public bool $needLogin=true, public bool $needAuth=true, public bool $needLog = true, public string $auth="*", public string $role="*")
{}
}

View File

@ -0,0 +1,110 @@
<?php
namespace App\Kernel\Aspect;
use App\Kernel\Annotation\PreAuthorization;
use App\Model\Account;
use Hyperf\Di\Annotation\Aspect;
use Hyperf\Di\Aop\AbstractAspect;
use Hyperf\Di\Aop\ProceedingJoinPoint;
use Hyperf\Di\Exception\AnnotationException;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
/**
* @Aspect
*/
#[Aspect]
class PreAuthorizationAspect extends AbstractAspect
{
protected RequestInterface $request;
protected ResponseInterface $response;
public array $annotations = [
PreAuthorization::class
];
/**
* @param RequestInterface $request
* @param ResponseInterface $response
*/
public function __construct(RequestInterface $request, ResponseInterface $response)
{
$this->request = $request;
$this->response = $response;
}
/**
* @param ProceedingJoinPoint $proceedingJoinPoint
* @return mixed|\Psr\Http\Message\ResponseInterface
* @throws AnnotationException
* @throws \Hyperf\Di\Exception\Exception
*/
public function process(ProceedingJoinPoint $proceedingJoinPoint)
{
// 切面切入后,执行对应的方法会由此来负责
$authorization = $this->getAuthorizationAnnotation($proceedingJoinPoint);
$isLogin = $this->request->getAttribute("isLogin", false);
if ($authorization->needLogin && !$isLogin) {
return $this->response->json(['code' => 1, 'msg' => '登录已过期']);
}
$admin = $this->request->getAttribute("account");
if ($authorization->needLogin && $authorization->needAuth) {
if (!$isLogin || empty($admin) || !$this->checkPermission($authorization->auth, $this->request->getMethod(), $admin)) {
return $this->response->json(['code' => 2, 'msg' => '权限不足']);
}
// 再次校验接口权限
if ("*" != $authorization->role && !$this->checkRole($authorization->role, $admin)) {
return $this->response->json(['code' => 2, 'msg' => '权限不足-1']);
}
}
$response = $proceedingJoinPoint->process();
// // 记录日志
// if (config("app.log") && $isLogin && !empty($admin) && $authorization->needLog && $authorization->auth != "*") {
// AccountLog::record($this->request, $admin, $authorization->auth, $response);
// }
return $response;
}
/**
* desc: 获取注解类
* @param ProceedingJoinPoint $proceedingJoinPoint
* @return PreAuthorization
* @throws AnnotationException
*/
protected function getAuthorizationAnnotation(ProceedingJoinPoint $proceedingJoinPoint): PreAuthorization
{
$annotation = $proceedingJoinPoint->getAnnotationMetadata()->method[PreAuthorization::class] ?? null;
if (!$annotation instanceof PreAuthorization) {
throw new AnnotationException("Annotation PreAuthorization couldn't be collected successfully.");
}
return $annotation;
}
/**
* desc: 校验操作权限
* @param string $auth
* @param string $method
* @param array $account
* @return bool
*/
protected function checkPermission(string $auth, string $method, array $account): bool
{
return Account::checkAuth($account, $method, $auth);
}
/**
* @param string $role
* @param array $admin
* @return bool
*/
private function checkRole(string $role, array $admin): bool
{
$keys = [];
$roles = ['ADMIN' => 0, 'ORG' => 1];
foreach (explode(",", $role) as $key) {
$keys[] = $roles[$key];
}
return in_array($admin['account_type'], $keys);
}
}

View File

@ -0,0 +1,36 @@
<?php
declare(strict_types=1);
namespace App\Kernel\Middleware;
use Hyperf\Context\Context;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
/**
* 允许跨域
*/
class CorsMiddleware implements MiddlewareInterface
{
/**
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$response = Context::get(ResponseInterface::class);
$response = $response->withHeader('Access-Control-Allow-Origin', '*')
->withHeader('Access-Control-Allow-Credentials', 'true')
->withHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, PUT, DELETE, OPTIONS')
->withHeader('Access-Control-Allow-Headers', 'Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, X-CSRF-TOKEN, X-Requested-With');
Context::set(ResponseInterface::class, $response);
if ($request->getMethod() == 'OPTIONS') {
return $response;
}
return $handler->handle($request);
}
}

View File

@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
namespace App\Kernel\Middleware;
use App\Model\Account;
use Hyperf\Context\Context;
use Psr\Container\ContainerInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
class JWTMiddleware implements MiddlewareInterface
{
/**
* @var ContainerInterface
*/
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
/**
* 登录状态校验
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
// 登录权限校验
$request = $request->withAttribute("isLogin", false);
try {
$token = $request->getHeaderLine("Authorization","");
$result = Account::checkToken(str_replace("Bearer ","", $token));
if ($result) {
// 是否登录
$request = $request->withAttribute("isLogin",true);
// 账号信息
$request = $request->withAttribute("account", $result);
}
}catch (\Exception $exception){
var_dump($exception->getMessage());
}
// 上下文中记录账号信息
Context::set(ServerRequestInterface::class, $request);
return $handler->handle($request);
}
}

27
app/Kernel/Param.php Normal file
View File

@ -0,0 +1,27 @@
<?php
/**
* Author: cfn <cfn@leapy.cn>
*/
namespace App\Kernel;
class Param
{
/**
* @param array $data
* @param array $param
* @return array
*/
static function only(array $data, array $param): array
{
$_arr = [];
foreach ($data as $k => $v) {
if (gettype($k) == "integer") {
isset($param[$v]) && $_arr[$v] = $param[$v];
} else {
$_arr[$k] = $param[$k] ?? $v;
}
}
return $_arr;
}
}

133
app/Kernel/Str.php Normal file
View File

@ -0,0 +1,133 @@
<?php
/**
* Author: cfn <cfn@leapy.cn>
*/
namespace App\Kernel;
use Hyperf\HttpServer\Contract\RequestInterface;
use function Hyperf\Config\config;
class Str
{
/**
* 获取UUID
* @param string $prefix
* @return string
*/
static function uuid(string $prefix = ''): string
{
$chars = md5(uniqid(mt_rand(), true));
$uuid = substr($chars, 0, 8) . '-';
$uuid .= substr($chars, 8, 4) . '-';
$uuid .= substr($chars, 12, 4) . '-';
$uuid .= substr($chars, 16, 4) . '-';
$uuid .= substr($chars, 20, 12);
return $prefix . $uuid;
}
/**
* 公钥加密
* Author: cfn <cfn@leapy.cn>
* @param $data
* @return string
*/
public static function public_encrypt($data)
{
$pubKey = "-----BEGIN PUBLIC KEY-----\n" .
wordwrap(config("app.public"), 64, "\n", true) .
"\n-----END PUBLIC KEY-----";
openssl_public_encrypt($data, $encrypted, $pubKey);
return base64_encode($encrypted);
}
/**
* 私钥解密
* Author: cfn <cfn@leapy.cn>
* @param $data
* @return string
*/
public static function private_decrypt($data)
{
$priKey = "-----BEGIN PRIVATE KEY-----\n" .
wordwrap(config("app.private"), 64, "\n", true) .
"\n-----END PRIVATE KEY-----";
openssl_private_decrypt(base64_decode($data), $decrypted, $priKey);
return $decrypted;
}
/**
* 加密
* Author: cfn <cfn@leapy.cn>
* @param $data
* @return string
*/
public static function encrypt($data)
{
return base64_encode(openssl_encrypt($data,"DES-ECB",config("app.key")));
}
/**
* 解密
* Author: cfn <cfn@leapy.cn>
* @param $data
* @return false|string
*/
public static function decrypt($data)
{
return openssl_decrypt(base64_decode($data),"DES-ECB",config("app.key"));
}
/**
* 获取随机字符串
* @param int $num
* @param string $prefix
* @return string
*/
static function randStr(int $num, string $prefix = ""): string
{
$str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
$_str = "";
while ($num > 0) {
$_str .= substr($str, rand(0, strlen($str) - 1), 1);
$num--;
}
return $prefix . $_str;
}
/**
* @param int $num
* @param string $prefix
* @return string
*/
static function randInt(int $num, string $prefix = ""): string
{
$str = "1234567890";
$_str = "";
while ($num > 0) {
$_str .= substr($str, rand(0, strlen($str) - 1), 1);
$num--;
}
return $prefix . $_str;
}
/**
* 返回ip
* @param RequestInterface $request
* @return string
*/
static function ip(RequestInterface $request): string
{
$res = $request->getHeaders();
if (isset($res['http_client_ip'])) {
return $res['http_client_ip'][0];
} elseif (isset($res['x-real-ip'])) {
return $res['x-real-ip'][0];
} elseif (isset($res['x-forwarded-for'])) {
return $res['x-forwarded-for'][0];
} else {
$serverParams = $request->getServerParams();
return $serverParams['remote_addr'][0];
}
}
}

51
app/Kernel/Token.php Normal file
View File

@ -0,0 +1,51 @@
<?php
/**
* Author: cfn <cfn@leapy.cn>
*/
namespace App\Kernel;
use function Hyperf\Config\config;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class Token
{
/**
* 生成token
* Author: cfn <cfn@leapy.cn>
* @param $data
* @param $expireIn
* @return string
*/
static function buildToken($data, $expireIn): string
{
$payload = [
'iss' => config("app.iss"),
'aud' => config("app.aud"),
'iat' => time(),
'exp' => time() + $expireIn,
'data' => $data
];
$privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" . wordwrap(config('app.private'), 64, "\n", true) . "\n-----END RSA PRIVATE KEY-----";
return JWT::encode($payload, $privateKey, 'RS256');
}
/**
* 解析token
* Author: cfn <cfn@leapy.cn>
* @param $token
* @return array|string
*/
static function parseToken($token)
{
try {
$publicKey = "-----BEGIN PUBLIC KEY-----\n" . wordwrap(config('app.public'), 64, "\n", true) . "\n-----END PUBLIC KEY-----";
$decoded = JWT::decode($token, new Key($publicKey, 'RS256'));
$decoded_array = (array)$decoded;
return (array)$decoded_array['data'];
} catch (\Exception $exception){}
return "";
}
}

View File

@ -0,0 +1,66 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Listener;
use Hyperf\Collection\Arr;
use Hyperf\Database\Events\QueryExecuted;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
use Hyperf\Logger\LoggerFactory;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
#[Listener]
class DbQueryExecutedListener implements ListenerInterface
{
/**
* @var LoggerInterface
*/
private $logger;
public function __construct(ContainerInterface $container)
{
$this->logger = $container->get(LoggerFactory::class)->get('sql');
}
public function listen(): array
{
return [
QueryExecuted::class,
];
}
/**
* @param QueryExecuted $event
*/
public function process(object $event): void
{
if ($event instanceof QueryExecuted) {
$sql = $event->sql;
if (! Arr::isAssoc($event->bindings)) {
$position = 0;
foreach ($event->bindings as $value) {
$position = strpos($sql, '?', $position);
if ($position === false) {
break;
}
$value = "'{$value}'";
$sql = substr_replace($sql, $value, $position, 1);
$position += strlen($value);
}
}
$this->logger->info(sprintf('[%s] %s', $event->time, $sql));
}
}
}

View File

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace App\Listener;
use Hyperf\Command\Event\AfterExecute;
use Hyperf\Coordinator\Constants;
use Hyperf\Coordinator\CoordinatorManager;
use Hyperf\Event\Annotation\Listener;
use Hyperf\Event\Contract\ListenerInterface;
#[Listener]
class ResumeExitCoordinatorListener implements ListenerInterface
{
public function listen(): array
{
return [
AfterExecute::class,
];
}
public function process(object $event): void
{
CoordinatorManager::until(Constants::WORKER_EXIT)->resume();
}
}

251
app/Model/Account.php Normal file
View File

@ -0,0 +1,251 @@
<?php
declare(strict_types=1);
namespace App\Model;
use App\Kernel\Str;
use App\Kernel\Token;
use Hyperf\Context\ApplicationContext;
use Hyperf\DbConnection\Db;
/**
* @property int $account_id
* @property int $account_type
* @property int $belong_id
* @property int $dept_id
* @property string $avatar
* @property string $username
* @property string $password
* @property int $master_flag
* @property int $status
* @property string $salt
* @property int $del_flag
* @property string $create_time
* @property string $update_time
*/
class Account extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'account';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['account_id' => 'integer', 'account_type' => 'integer', 'belong_id' => 'integer', 'dept_id' => 'integer', 'master_flag' => 'integer', 'status' => 'integer', 'del_flag' => 'integer'];
/**
* 通过id获取账号信息
* Author: cfn <cfn@leapy.cn>
* @param int $account_id
*/
public static function getById(int $account_id, array $field = ['*'])
{
return self::where("del_flag", 0)
->where("account_id", $account_id)
->first($field);
}
/**
* 通过用户名获取账号信息
* Author: cfn <cfn@leapy.cn>
* @param string $username
*/
public static function getByUsername(string $username)
{
return self::where("del_flag", 0)
->where("username", $username)
->first(['account_id', 'account_type', 'belong_id', 'password', 'master_flag', 'status', 'salt']);
}
/**
* 通过id获取账号信息
* Author: cfn <cfn@leapy.cn>
* @param int $account_id
*/
public static function getByAccountId(int $account_id)
{
return self::where("del_flag", 0)
->where("account_id", $account_id)
->first(['account_id', 'account_type', 'belong_id', 'master_flag', 'status', 'create_time', 'username', 'avatar']);
}
/**
* 保存信息
* Author: cfn <cfn@leapy.cn>
* @param int $account_id
* @param array $param
* @return int
*/
public static function saveInfo(int $account_id, array $param)
{
$param['update_time'] = date("Y-m-d H:i:s");
return self::where("del_flag", 0)
->where("account_id", $account_id)
->update($param);
}
/**
* @param $param
* @return \Hyperf\Contract\LengthAwarePaginatorInterface
*/
static function list($account, $param)
{
$model = self::where('del_flag', 0)
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type']);
if (isset($param['username']) && $param['username'] != '') {
$model = $model->where('username', "like", "%$param[username]%");
}
if (isset($param['dept_id']) && $param['dept_id'] != '') {
$model = $model->where('dept_id', $param['dept_id']);
}
$paginate = $model->orderByDesc("account_id")
->paginate((int)$param['limit'], ['account_id', 'username', 'avatar', 'create_time', 'dept_id'], 'page', (int)$param['page']);
foreach ($paginate->items() as &$item) {
$item['roles'] = AccountRole::getRole($item['account_id']);
}
return $paginate;
}
/**
* 添加
* @param array $data
* @return bool
*/
public static function add($account, array $data)
{
$data['account_type'] = $account['account_type'];
$data['belong_id'] = $account['belong_id'];
$data['create_time'] = date("Y-m-d H:i:s");
$data['del_flag'] = 0;
$data['salt'] = Str::randStr(6);
$data['password'] = md5($data['salt'] . $data['password']);
$roles = $data['roles'];
unset($data['roles']);
Db::beginTransaction();
$account_id = self::insertGetId($data);
if (!$account_id) {
Db::rollBack();
return false;
}
$account_role = [];
foreach ($roles as $role_id) {
$account_role[] = ['role_id' => $role_id, 'account_id' => $account_id];
}
if (!empty($account_role)) {
$res = AccountRole::insert($account_role);
if (!$res) {
Db::rollBack();
return false;
}
}
Db::commit();
return true;
}
/**
* 修改
* @param array $account
* @param array $data
* @return bool
*/
public static function edit(array $account, array $data)
{
$data['update_time'] = date("Y-m-d H:i:s");
if ($data['password'] != '') {
$data['salt'] = Str::randStr(6);
$data['password'] = md5($data['salt'] . $data['password']);
}else{
unset($data['password']);
}
$roles = $data['roles'];
unset($data['roles']);
Db::beginTransaction();
$res = self::where("account_id", $data['account_id'])
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type'])
->where('del_flag', 0)
->update($data);
if (!$res) {
Db::rollBack();
return false;
}
// 删除之前的
$res1 = AccountRole::where('account_id', $data['account_id'])->delete();
if (!$res1) {
Db::rollBack();
return false;
}
$account_role = [];
foreach ($roles as $role_id) {
$account_role[] = ['role_id' => $role_id, 'account_id' => $data['account_id']];
}
// 重新添加
if (!empty($account_role)) {
if (!AccountRole::insert($account_role)) {
Db::rollBack();
return false;
}
}
Db::commit();
return true;
}
/**
* 删除
* @param string $ids
* @return int
*/
public static function del(array $account, string $ids)
{
return self::whereIn('account_id', explode(",", $ids))
->where("del_flag", 0)
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type'])
->update([
'update_time' => date('Y-m-d H:i:s'),
'del_flag' => 1
]);
}
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @param string $method
* @param string $auth
* @return bool
*/
public static function checkAuth(array $account, string $method, string $auth)
{
return Menu::where("del_flag", 0)
->where("account_type", $account['account_type'])
->where("method", $method)
->where("flag", $auth)
->where("type", 2)
->exists();
}
/**
* Author: cfn <cfn@leapy.cn>
* @param $token
* @return false|mixed
*/
public static function checkToken($token)
{
$tokenData = Token::parseToken($token);
if (empty($tokenData)) return false;
$container = ApplicationContext::getContainer();
$redis = $container->get(\Hyperf\Redis\Redis::class);
$data = $redis->get($tokenData['key']);
if (!$data) return false;
return json_decode(Str::private_decrypt($data), true);
}
}

37
app/Model/AccountRole.php Normal file
View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace App\Model;
use Hyperf\DbConnection\Db;
/**
* @property int $account_id
* @property int $role_id
*/
class AccountRole extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'account_role';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['account_id' => 'integer', 'role_id' => 'integer'];
public static function getRole(int $account_id)
{
return Db::table("account_role")
->leftJoin('role', 'role.role_id', '=', 'account_role.role_id')
->where('account_role.account_id', $account_id)
->select(["role.role_id","role.role_name"])->get()->toArray();
}
}

85
app/Model/App.php Normal file
View File

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace App\Model;
/**
* @property int $app_id
* @property int $org_id
* @property string $app_code
* @property string $app_key
* @property int $del_flag
* @property int $status
* @property string $create_time
* @property string $update_time
*/
class App extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'app';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['app_id' => 'integer', 'org_id' => 'integer', 'del_flag' => 'integer', 'status' => 'integer'];
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @param array $param
* @return \Hyperf\Contract\LengthAwarePaginatorInterface
*/
public static function list(array $account, array $param)
{
$model = self::where("del_flag", 0)
->where("org_id", $account['belong_id']);
foreach (['app_name'] as $k) {
if (isset($param[$k]) && $param[$k] != '') {
$model = $model->where($k, "like", "%$param[$k]%");
}
}
return $model->orderByDesc("app_id")
->paginate((int)$param['limit'], ["app_id", "app_name", "app_code", "app_key", "create_time", 'status'], 'page', (int)$param['page']);
}
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @param string $ids
* @return int
*/
public static function del(array $account, string $ids): int
{
return self::where("del_flag", 0)
->where("org_id", $account['belong_id'])
->whereIn("app_id", explode(",", $ids))
->update([
'del_flag' => 1,
'update_time' => date("Y-m-d H:i:s")
]);
}
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @return array
*/
public static function options(array $account): array
{
return self::where("del_flag", 0)
->where("org_id", $account['belong_id'])
->select(["app_id", "app_name"])
->get()
->toArray();
}
}

32
app/Model/Cron.php Normal file
View File

@ -0,0 +1,32 @@
<?php
declare(strict_types=1);
namespace App\Model;
/**
* @property int $cron_id
* @property int $org_id
* @property string $cron_name
* @property int $status
* @property int $del_flag
* @property string $create_time
* @property string $update_time
*/
class Cron extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'cron';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['cron_id' => 'integer', 'org_id' => 'integer', 'status' => 'integer', 'del_flag' => 'integer'];
}

119
app/Model/Dept.php Normal file
View File

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace App\Model;
/**
* @property int $dept_id
* @property int $parent_id
* @property int $account_type
* @property int $belong_id
* @property string $dept_name
* @property int $rank
* @property int $status
* @property string $remark
* @property int $del_flag
* @property string $create_time
* @property string $update_time
*/
class Dept extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'dept';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['dept_id' => 'integer', 'parent_id' => 'integer', 'account_type' => 'integer', 'belong_id' => 'integer', 'rank' => 'integer', 'status' => 'integer', 'del_flag' => 'integer'];
/**
* 部门列表
* @param array $account
* @param $param
* @return array
*/
static function depts(array $account, $param): array
{
return self::where('del_flag', 0)
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type'])
->when(isset($param['dept_name']) && $param['dept_name'] != "", function ($query) use ($param) {
$query->where('dept_name', "like", "%{$param['dept_name']}%");
})->orderByDesc("rank")
->select(["dept_id", "parent_id", "dept_name", "rank", "status", "remark", "create_time"])
->get()->toArray();
}
/**
* 添加
* @param array $data
* @return bool
*/
public static function add(array $account, array $data)
{
$data['account_type'] = $account['account_type'];
$data['belong_id'] = $account['belong_id'];
$data['create_time'] = date("Y-m-d H:i:s");
$data['del_flag'] = 0;
return self::insert($data);
}
/**
* 修改
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @param array $data
* @return int
*/
public static function edit(array $account, array $data)
{
$data['update_time'] = date("Y-m-d H:i:s");
return self::where("dept_id", $data['dept_id'])
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type'])
->where('del_flag', 0)
->update($data);
}
/**
* 删除
* @param array $account
* @param string $ids
* @return int
*/
public static function del(array $account, string $ids)
{
return self::whereIn('dept_id', explode(",", $ids))
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type'])
->where('del_flag', 0)
->update([
'update_time' => date('Y-m-d H:i:s'),
'del_flag' => 1
]);
}
/**
* 选项
* @param array $account
* @return array
*/
public static function options(array $account): array
{
return self::where('del_flag', 0)
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type'])
->orderByDesc("rank")
->select(["dept_id", "parent_id", "dept_name"])
->get()->toArray();
}
}

79
app/Model/Device.php Normal file
View File

@ -0,0 +1,79 @@
<?php
declare(strict_types=1);
namespace App\Model;
/**
* @property int $device_id
* @property int $org_id
* @property string $device_sn
* @property int $status
* @property int $del_flag
* @property string $create_time
* @property string $update_time
*/
class Device extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'device';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['device_id' => 'integer', 'org_id' => 'integer', 'status' => 'integer', 'del_flag' => 'integer'];
public static function pages(array $param)
{
$model = self::from("device as d")
->leftJoin("group as g", "g.group_id", "=", "d.group_id")
->leftJoin("app as a", "a.app_id", "=", "d.app_id")
->where("d.del_flag", 0);
foreach (['device_sn'] as $k) {
if (isset($param[$k]) && $param[$k] != "") {
$model = $model->where("d." . $k, "like", "%{$param[$k]}%");
}
}
foreach (['org_id', 'status', 'group_id', 'app_id'] as $k) {
if (isset($param[$k]) && $param[$k] != "") {
$model = $model->where("d." . $k, $param[$k]);
}
}
return $model->orderByDesc("d.device_id")
->paginate((int)$param['limit'], ["d.device_id", "d.device_sn", "d.status", "d.create_time", "g.group_name", "d.group_id", "d.online", "d.app_id", "a.app_name"], 'page', (int)$param['page']);
}
public static function add(array $data)
{
$data['create_time'] = date("Y-m-d H:i:s");
$data['del_flag'] = 0;
return self::insert($data);
}
public static function edit(array $data)
{
$data['update_time'] = date("Y-m-d H:i:s");
return self::where("device_id", $data['device_id'])
->where("org_id", $data['org_id'])
->where('del_flag', 0)
->update($data);
}
public static function del(array $account, $ids)
{
return self::whereIn('device_id', explode(",", $ids))
->where("org_id", $account['belong_id'])
->where('del_flag', 0)
->update([
'update_time' => date('Y-m-d H:i:s'),
'del_flag' => 1
]);
}
}

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Model;
/**
* @property int $message_id
* @property int $org_id
* @property string $device_sn
* @property string $topic_code
* @property int $msg_type
* @property string $content
* @property int $del_flag
* @property string $create_time
* @property string $update_time
*/
class DeviceMessage extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'device_message';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['message_id' => 'integer', 'org_id' => 'integer', 'msg_type' => 'integer', 'del_flag' => 'integer'];
}

85
app/Model/Group.php Normal file
View File

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace App\Model;
/**
* @property int $group_id
* @property int $org_id
* @property string $group_name
* @property int $rank
* @property int $del_flag
* @property string $create_time
* @property string $update_time
*/
class Group extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'group';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['group_id' => 'integer', 'org_id' => 'integer', 'rank' => 'integer', 'del_flag' => 'integer'];
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @param array $param
* @return \Hyperf\Contract\LengthAwarePaginatorInterface
*/
public static function list(array $account, array $param)
{
$model = self::where("del_flag", 0)
->where("org_id", $account['belong_id']);
foreach (['group_name'] as $k) {
if (isset($param[$k]) && $param[$k] != '') {
$model = $model->where($k, "like", "%$param[$k]%");
}
}
return $model->orderByDesc("group_id")
->paginate((int)$param['limit'], ["group_id", "group_name", "rank", "create_time"], 'page', (int)$param['page']);
}
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @param string $ids
* @return int
*/
public static function del(array $account, string $ids): int
{
return self::where("del_flag", 0)
->where("org_id", $account['belong_id'])
->whereIn("group_id", explode(",", $ids))
->update([
'del_flag' => 1,
'update_time' => date("Y-m-d H:i:s")
]);
}
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @return array
*/
public static function options(array $account): array
{
return self::where("del_flag", 0)
->where("org_id", $account['belong_id'])
->select(["group_id", "group_name"])
->orderByDesc("rank")
->get()
->toArray();
}
}

199
app/Model/Menu.php Normal file
View File

@ -0,0 +1,199 @@
<?php
declare(strict_types=1);
namespace App\Model;
use Hyperf\DbConnection\Db;
/**
* @property int $menu_id
* @property int $parent_id
* @property string $title
* @property int $account_type
* @property int $type
* @property string $method
* @property string $flag
* @property string $name
* @property string $path
* @property string $icon
* @property int $rank
* @property int $hidden
* @property int $del_flag
* @property string $create_time
* @property string $update_time
* @property string $remark
*/
class Menu extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'menu';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['menu_id' => 'integer', 'parent_id' => 'integer', 'account_type' => 'integer', 'type' => 'integer', 'rank' => 'integer', 'hidden' => 'integer', 'del_flag' => 'integer'];
/**
* 获取账号权限
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @return array
*/
public static function getMenu(array $account): array
{
if ($account['master_flag'] == 1) {
$menus = self::getM($account['account_type']);
$buttons = self::getB($account['account_type']);
$roles = ['MASTER_ACCOUNT'];
} else {
$menus = self::getMByRole($account['account_type'], $account['account_id']);
$buttons = self::getBByRole($account['account_type'], $account['account_id']);
$roles = ['SUB_ACCOUNT'];
}
$roles[] = match ($account['account_type']) {
0 => "ADMIN",
1 => "TENANT",
2 => "PARTNER",
3 => "MERCHANT"
};
return compact("menus", "buttons", "roles");
}
/**
* Author: cfn <cfn@leapy.cn>
* @param $account_type
* @return array
*/
public static function getM($account_type)
{
return self::from("menu as m")
->where('m.del_flag', 0)
->where('m.account_type', $account_type)
->where('m.type', 0)
->orderByDesc("m.rank")
->select(['m.menu_id', 'm.parent_id', 'm.title', 'm.name', 'm.path', 'm.icon', 'm.hidden'])->get()->toArray();
}
/**
* Author: cfn <cfn@leapy.cn>
* @param int $accountType
* @param int $accountId
* @return array
*/
private static function getMByRole(int $accountType, int $accountId)
{
return Db::table("account_role as ar")
->leftJoin("role_menu as rm", "ar.role_id", "=", "rm.role_id")
->leftJoin("menu as m", "m.menu_id", "=", "rm.menu_id")
->where("ar.account_id", $accountId)
->where("m.account_type", $accountType)
->where("m.type", 0)
->where("m.del_flag", 0)
->orderByDesc("rank")
->select(['m.menu_id', 'm.parent_id', 'm.title', 'm.name', 'm.path', 'm.icon', 'm.hidden'])->get()->toArray();
}
/**
* Author: cfn <cfn@leapy.cn>
* @param int $accountType
* @return array
*/
public static function getB(int $accountType): array
{
return self::where('del_flag', 0)
->where('type', 1)
->where('account_type', $accountType)
->pluck('flag')->toArray();
}
/**
* Author: cfn <cfn@leapy.cn>
* @param int $accountType
* @param int $accountId
* @return array
*/
private static function getBByRole(int $accountType, int $accountId): array
{
return Db::table("account_role as ar")
->leftJoin("role_menu as rm", "ar.role_id", "=", "rm.role_id")
->leftJoin("menu as m", "m.menu_id", "=", "rm.menu_id")
->where("ar.account_id", $accountId)
->where("m.account_type", $accountType)
->where("m.type", 1)
->where("m.del_flag", 0)
->pluck('m.flag')->toArray();
}
public static function getMenusV1(array $param)
{
return self::where('del_flag', 0)
->where('account_type', $param['account_type'])
->orderByDesc("rank")
->select(["menu_id","title","flag","parent_id","type","method","name","path","icon","rank","hidden","account_type"])
->get()->toArray();
}
public static function getMenus(array $account)
{
if (in_array($account['account_type'], [1,2,3])) {
return self::from("trade_open as to")
->leftJoin("trade_menu as tm","tm.trade_id","=","to.trade_id")
->leftJoin("menu as m", "m.menu_id","=","tm.menu_id")
->where("tm.account_type", $account['account_type'])
->where("to.account_type", $account['account_type'])
->where("to.belong_id", $account['belong_id'])
->where("to.status",1)
->where("m.account_type", $account['account_type'])
->where("m.del_flag",0)
->groupBy(["m.menu_id"])
->select(["m.menu_id","m.title","m.flag","m.parent_id","m.type","m.method","m.name","m.path","m.icon","m.rank","m.hidden","m.account_type"])
->get()->toArray();
}else{
return self::where('del_flag', 0)
->where('account_type', $account['account_type'])
->orderByDesc("rank")
->select(["menu_id","title","flag","parent_id","type","method","name","path","icon","rank","hidden","account_type"])
->get()->toArray();
}
}
public static function add(array $data)
{
$data['create_time'] = date("Y-m-d H:i:s");
$data['del_flag'] = 0;
return self::insertGetId($data);
}
/**
* 修改
* @param array $data
* @return bool
*/
public static function edit(array $data)
{
$data['update_time'] = date("Y-m-d H:i:s");
return self::where("menu_id", $data['menu_id'])->where('del_flag', 0)->update($data);
}
/**
* 删除
* @param string $menu_id
* @return int
*/
public static function del(string $menu_id)
{
return self::whereIn('menu_id', explode(",", $menu_id))
->update([
'update_time' => date('Y-m-d H:i:s'),
'del_flag' => 1
]);
}
}

12
app/Model/Model.php Normal file
View File

@ -0,0 +1,12 @@
<?php
declare(strict_types=1);
namespace App\Model;
use Hyperf\DbConnection\Model\Model as BaseModel;
abstract class Model extends BaseModel
{
public bool $timestamps = false;
}

170
app/Model/Org.php Normal file
View File

@ -0,0 +1,170 @@
<?php
declare(strict_types=1);
namespace App\Model;
use App\Kernel\Str;
use Hyperf\DbConnection\Db;
/**
* @property int $org_id
* @property string $org_name
* @property string $org_code
* @property string $contact_name
* @property string $contact_mobile
* @property int $status
* @property int $del_flag
* @property string $create_time
* @property string $update_time
*/
class Org extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'org';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['org_id' => 'integer', 'status' => 'integer', 'del_flag' => 'integer'];
/**
* Author: cfn <cfn@leapy.cn>
* @param int $belong_id
* @param array $field
* @return array
*/
public static function getById(int $belong_id, array $field=['*'])
{
$info = self::where("del_flag",0)
->where("org_id",$belong_id)
->first($field);
return $info ? $info->toArray() : [];
}
/**
* Author: cfn <cfn@leapy.cn>
* @param array $param
* @return \Hyperf\Contract\LengthAwarePaginatorInterface
*/
public static function list(array $param)
{
$model = self::where('del_flag', 0);
if (isset($param['org_name']) && $param['org_name'] != '') {
$model = $model->where('org_name', "like", "%$param[tenant_name]%");
}
return $model->orderByDesc("org_id")
->paginate((int)$param['limit'], ["org_id", "org_name", "org_code","contact_name",
"contact_mobile", "status", "create_time"], 'page', (int)$param['page']);
}
/**
* Author: cfn <cfn@leapy.cn>
* @param array $param
* @return bool
*/
public static function add(array $param)
{
$param['create_time'] = date("Y-m-d H:i:s");
$param['del_flag'] = 0;
$password = $param['password'];
$org_code = "20" . date("Y") . Str::randInt(4);
$param['org_code'] = $org_code;
unset($param['password']);
Db::beginTransaction();
$org_id = self::insertGetId($param);
if (!$org_id) {
Db::rollBack();
return false;
}
$salt = Str::randStr(6);
$res = Account::insert([
'account_type' => 1,
'belong_id' => $org_id,
'username' => $org_code,
'master_flag' => 1,
'password' => md5($salt . $password),
'salt' => $salt,
'create_time' => date("Y-m-d H:i:s")
]);
if (!$res) {
Db::rollBack();
return false;
}
Db::commit();
return true;
}
/**
* 修改
* @param array $data
* @return bool
*/
public static function edit(array $data)
{
$data['update_time'] = date("Y-m-d H:i:s");
$password = $data['password'];
unset($data['password']);
$info = self::getById($data['org_id']);
if (empty($info)) {
return false;
}
Db::beginTransaction();
$res = self::where("org_id", $data['org_id'])->where('del_flag', 0)->update($data);
if (!$res) {
Db::rollBack();
return false;
}
if ($password) {
$salt = Str::randStr(6);
$res1 = Account::where("account_type", 1)
->where("belong_id", $data['org_id'])
->where("master_flag", 1)
->where("username", $info['org_code'])
->update([
'salt' => $salt,
'password' => md5($salt . $password),
'update_time' => date("Y-m-d H:i:s")
]);
if (!$res1) {
Db::rollBack();
return false;
}
}
Db::commit();
return true;
}
/**
* 删除
* @param string $ids
* @return int
*/
public static function del(string $ids)
{
return self::whereIn('org_id', explode(",", $ids))
->where("del_flag", 0)
->update([
'update_time' => date('Y-m-d H:i:s'),
'del_flag' => 1
]);
}
/**
* 选项
* @return array
*/
public static function options(): array
{
return self::where('del_flag', 0)
->select(["org_id","org_name", "org_code"])
->get()->toArray();
}
}

170
app/Model/Role.php Normal file
View File

@ -0,0 +1,170 @@
<?php
declare(strict_types=1);
namespace App\Model;
use Hyperf\DbConnection\Db;
/**
* @property int $role_id
* @property int $account_type
* @property int $belong_id
* @property string $role_name
* @property string $checked_menus
* @property int $status
* @property int $del_flag
* @property string $remark
* @property int $rank
* @property string $create_time
* @property string $update_time
*/
class Role extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'role';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['role_id' => 'integer', 'account_type' => 'integer', 'belong_id' => 'integer', 'status' => 'integer', 'del_flag' => 'integer', 'rank' => 'integer'];
/**
* 部门列表
* @param array $account
* @param array $param
* @return array
*/
static function roles(array $account, array $param): array
{
$model = self::where('del_flag', 0)
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type']);
if (isset($param['role_name']) && $param['role_name'] != '') {
$model = $model->where("role_name", "like", "%{$param['role_name']}%");
}
return $model->select(["role_id", "role_name", "status", "remark", "create_time", "rank", "checked_menus"])
->orderByDesc("rank")
->orderByDesc("role_id")
->get()->each(function ($item) {
$item['menus'] = RoleMenu::getMenu($item['role_id']);
})->toArray();
}
/**
* 添加
* @param array $account
* @param array $data
* @return bool
*/
public static function add(array $account, array $data)
{
$data['account_type'] = $account['account_type'];
$data['belong_id'] = $account['belong_id'];
$data['create_time'] = date("Y-m-d H:i:s");
$data['del_flag'] = 0;
$menus = $data['menus'];
unset($data['menus']);
Db::beginTransaction();
$role_id = self::insertGetId($data);
if (!$role_id) {
Db::rollBack();
return false;
}
$role_menu = [];
foreach ($menus as $menu_id) {
$role_menu[] = ['role_id' => $role_id, 'menu_id' => $menu_id];
}
if (!empty($role_menu)) {
$res = RoleMenu::insert($role_menu);
if (!$res) {
Db::rollBack();
return false;
}
}
Db::commit();
return true;
}
/**
* 修改
* @param array $account
* @param array $data
* @return bool
*/
public static function edit(array $account, array $data)
{
$data['update_time'] = date("Y-m-d H:i:s");
$menus = $data['menus'];
unset($data['menus']);
Db::beginTransaction();
$res = self::where("role_id", $data['role_id'])
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type'])
->where('del_flag', 0)
->update($data);
if (!$res) {
Db::rollBack();
return false;
}
// 删除之前的
$res1 = RoleMenu::where('role_id', $data['role_id'])->delete();
if (!$res1) {
Db::rollBack();
return false;
}
$role_menu = [];
foreach ($menus as $menu_id) {
$role_menu[] = ['role_id' => $data['role_id'], 'menu_id' => $menu_id];
}
// 重新添加
if (!empty($role_menu)) {
if (!RoleMenu::insert($role_menu)) {
Db::rollBack();
return false;
}
}
Db::commit();
return true;
}
/**
* 删除
* @param array $account
* @param string $ids
* @return int
*/
public static function del(array $account, string $ids)
{
return self::whereIn('role_id', explode(",", $ids))
->where("del_flag", 0)
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type'])
->update([
'update_time' => date('Y-m-d H:i:s'),
'del_flag' => 1
]);
}
/**
* @param array $account
* @return array
*/
public static function options(array $account): array
{
return self::where('del_flag', 0)
->where('belong_id', $account['belong_id'])
->where('account_type', $account['account_type'])
->orderByDesc("rank")
->orderByDesc("role_id")
->select(["role_id", "role_name"])
->get()->toArray();
}
}

37
app/Model/RoleMenu.php Normal file
View File

@ -0,0 +1,37 @@
<?php
declare(strict_types=1);
namespace App\Model;
use Hyperf\DbConnection\Db;
/**
* @property int $role_id
* @property int $menu_id
*/
class RoleMenu extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'role_menu';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['role_id' => 'integer', 'menu_id' => 'integer'];
public static function getMenu(int $role_id)
{
return Db::table("role_menu")
->leftJoin('menu', 'menu.menu_id', '=', 'role_menu.menu_id')
->where('role_menu.role_id', $role_id)
->select(["menu.menu_id", "menu.title"])->get()->toArray();
}
}

85
app/Model/Topic.php Normal file
View File

@ -0,0 +1,85 @@
<?php
declare(strict_types=1);
namespace App\Model;
/**
* @property int $topic_id
* @property int $org_id
* @property string $topic_name
* @property string $topic_code
* @property int $status
* @property int $del_flag
* @property string $create_time
* @property string $update_time
*/
class Topic extends Model
{
/**
* The table associated with the model.
*/
protected ?string $table = 'topic';
/**
* The attributes that are mass assignable.
*/
protected array $fillable = [];
/**
* The attributes that should be cast to native types.
*/
protected array $casts = ['topic_id' => 'integer', 'org_id' => 'integer', 'status' => 'integer', 'del_flag' => 'integer'];
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @param array $param
* @return \Hyperf\Contract\LengthAwarePaginatorInterface
*/
public static function list(array $account, array $param)
{
$model = self::where("del_flag", 0)
->where("org_id", $account['belong_id']);
foreach (['topic_name'] as $k) {
if (isset($param[$k]) && $param[$k] != '') {
$model = $model->where($k, "like", "%$param[$k]%");
}
}
return $model->orderByDesc("topic_id")
->paginate((int)$param['limit'], ["topic_id", "topic_name", "topic_code", "create_time", 'status'], 'page', (int)$param['page']);
}
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @param string $ids
* @return int
*/
public static function del(array $account, string $ids): int
{
return self::where("del_flag", 0)
->where("org_id", $account['belong_id'])
->whereIn("topic_id", explode(",", $ids))
->update([
'del_flag' => 1,
'update_time' => date("Y-m-d H:i:s")
]);
}
/**
* Author: cfn <cfn@leapy.cn>
* @param array $account
* @return array
*/
public static function options(array $account): array
{
return self::where("del_flag", 0)
->where("org_id", $account['belong_id'])
->select(["topic_id", "topic_name"])
->get()
->toArray();
}
}

54
app/Request/Account.php Normal file
View File

@ -0,0 +1,54 @@
<?php
declare(strict_types=1);
namespace App\Request;
use Hyperf\Validation\Request\FormRequest;
/**
* 账号验证
*/
class Account extends FormRequest
{
protected array $scenes = [
'login' => ['username', 'password', 'uuid', 'code'],
'sy_login' => ['username', 'password'],
'info' => ['avatar'],
'add' => ['username'],
'edit' => ['username','account_id'],
];
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'username' => 'required',
'password' => 'required',
'uuid' => 'required',
'code' => 'required',
'realname' => 'required',
'avatar' => 'required',
'account_id' => 'required',
];
}
public function messages(): array
{
return [
'code.required' => '验证码必填!',
'realname.required' => '姓名必填!',
'avatar.required' => '头像必填!',
];
}
}

45
app/Request/App.php Normal file
View File

@ -0,0 +1,45 @@
<?php
/**
* Author: cfn <cfn@leapy.cn>
*/
namespace App\Request;
use Hyperf\Validation\Request\FormRequest;
class App extends FormRequest
{
protected array $scenes = [
'add' => ['app_name', 'status', 'config'],
'edit' => ['app_id', 'app_name', 'status', 'config'],
];
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'app_id' => 'required',
'app_name' => 'required',
'status' => 'required'
];
}
public function messages(): array
{
return [
'app_id.required' => '应用ID必填',
'app_name.required' => '应用名称必填!',
'status.required' => '状态必填!'
];
}
}

45
app/Request/Dept.php Normal file
View File

@ -0,0 +1,45 @@
<?php
namespace App\Request;
use Hyperf\Validation\Request\FormRequest;
/**
* 菜单
*/
class Dept extends FormRequest
{
protected array $scenes = [
'add' => ['parent_id', 'dept_name'],
'edit' => ['dept_id', 'parent_id', 'dept_name'],
];
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'dept_id' => 'required',
'parent_id' => 'required',
'dept_name' => 'required'
];
}
public function messages(): array
{
return [
'dept_id.required' => '部门ID必填',
'parent_id.required' => '上级ID必选',
'dept_name.required' => '部门名称必填!'
];
}
}

49
app/Request/Menu.php Normal file
View File

@ -0,0 +1,49 @@
<?php
namespace App\Request;
use Hyperf\Validation\Request\FormRequest;
/**
* 菜单
*/
class Menu extends FormRequest
{
protected array $scenes = [
'add' => ['parent_id', 'title', 'account_type', 'type'],
'edit' => ['parent_id', 'title', 'account_type', 'type','menu_id'],
];
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'menu_id' => 'required',
'parent_id' => 'required',
'title' => 'required',
'account_type' => 'required',
'type' => 'required'
];
}
public function messages(): array
{
return [
'menu_id.required' => '菜单ID必填',
'parent_id.required' => '上级ID必选',
'title.required' => '菜单名称必填!',
'account_type.required' => '菜单归属必填!',
'type.required' => '菜单类型必填!'
];
}
}

46
app/Request/Org.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace App\Request;
use Hyperf\Validation\Request\FormRequest;
/**
* 菜单
*/
class Org extends FormRequest
{
protected array $scenes = [
'add' => ['org_name', 'contact_name', 'contact_mobile', 'status'],
'edit' => ['org_id', 'org_name', 'contact_name', 'contact_mobile', 'status'],
];
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'org_id' => 'required',
'org_name' => 'required',
'org_code' => 'required',
'contact_name' => 'required',
'contact_mobile' => 'required',
'status' => 'required',
];
}
public function messages(): array
{
return [
'org_id.required' => '请选择租户!',
];
}
}

43
app/Request/Role.php Normal file
View File

@ -0,0 +1,43 @@
<?php
namespace App\Request;
use Hyperf\Validation\Request\FormRequest;
/**
* 菜单
*/
class Role extends FormRequest
{
protected array $scenes = [
'add' => ['role_name'],
'edit' => ['role_id', 'role_name'],
];
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*/
public function rules(): array
{
return [
'role_id' => 'required',
'role_name' => 'required'
];
}
public function messages(): array
{
return [
'role_id.required' => '角色ID必填',
'role_name.required' => '角色名称必填!'
];
}
}

25
bin/hyperf.php Normal file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env php
<?php
ini_set('display_errors', 'on');
ini_set('display_startup_errors', 'on');
ini_set('memory_limit', '1G');
error_reporting(E_ALL);
date_default_timezone_set('Asia/Shanghai');
! defined('BASE_PATH') && define('BASE_PATH', dirname(__DIR__, 1));
require BASE_PATH . '/vendor/autoload.php';
! defined('SWOOLE_HOOK_FLAGS') && define('SWOOLE_HOOK_FLAGS', Hyperf\Engine\DefaultOption::hookFlags());
// Self-called anonymous function that creates its own scope and keep the global namespace clean.
(function () {
Hyperf\Di\ClassLoader::init();
/** @var Psr\Container\ContainerInterface $container */
$container = require BASE_PATH . '/config/container.php';
$application = $container->get(Hyperf\Contract\ApplicationInterface::class);
$application->run();
})();

86
composer.json Normal file
View File

@ -0,0 +1,86 @@
{
"name": "hyperf/hyperf-skeleton",
"type": "project",
"keywords": [
"php",
"swoole",
"framework",
"hyperf",
"microservice",
"middleware"
],
"description": "A coroutine framework that focuses on hyperspeed and flexible, specifically use for build microservices and middlewares.",
"license": "Apache-2.0",
"require": {
"php": ">=8.1",
"firebase/php-jwt": "^6.10",
"hyperf/async-queue": "^3.1",
"hyperf/cache": "~3.1.0",
"hyperf/command": "~3.1.0",
"hyperf/config": "~3.1.0",
"hyperf/crontab": "^3.1",
"hyperf/database": "~3.1.0",
"hyperf/db-connection": "~3.1.0",
"hyperf/engine": "^2.10",
"hyperf/framework": "~3.1.0",
"hyperf/guzzle": "~3.1.0",
"hyperf/http-server": "~3.1.0",
"hyperf/logger": "~3.1.0",
"hyperf/memory": "~3.1.0",
"hyperf/mqtt-server-incubator": "^0.3.1",
"hyperf/paginator": "^3.1",
"hyperf/process": "~3.1.0",
"hyperf/rate-limit": "^3.1",
"hyperf/redis": "~3.1.0",
"hyperf/validation": "^3.1"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.0",
"hyperf/devtool": "~3.1.0",
"hyperf/testing": "~3.1.0",
"hyperf/watcher": "^3.1",
"mockery/mockery": "^1.0",
"phpstan/phpstan": "^1.0",
"swoole/ide-helper": "^5.0"
},
"suggest": {
"ext-openssl": "Required to use HTTPS.",
"ext-json": "Required to use JSON.",
"ext-pdo": "Required to use MySQL Client.",
"ext-pdo_mysql": "Required to use MySQL Client.",
"ext-redis": "Required to use Redis Client."
},
"autoload": {
"psr-4": {
"App\\": "app/"
},
"files": []
},
"autoload-dev": {
"psr-4": {
"HyperfTest\\": "./test/"
}
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"optimize-autoloader": true,
"sort-packages": true
},
"extra": [],
"scripts": {
"post-root-package-install": [
"@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
],
"post-autoload-dump": [
"rm -rf runtime/container"
],
"test": "co-phpunit --prepend test/bootstrap.php --colors=always",
"cs-fix": "php-cs-fixer fix $1",
"analyse": "phpstan analyse --memory-limit 300M",
"start": [
"Composer\\Config::disableProcessTimeout",
"php ./bin/hyperf.php start"
]
}
}

10102
composer.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,21 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'scan' => [
'paths' => [
BASE_PATH . '/app',
],
'ignore_annotations' => [
'mixin',
],
],
];

13
config/autoload/app.php Normal file
View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
return [
"key" => "!123AD1@",
// 加密密钥
"public" => "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtdxd5D6SahDExxEPsEvHkT5RShkay2xAYdqNGuQnLHcf8yJqPkwylyz8mrGUqrhahC/rbkzUk/liT0omMkmKWkm+cNSyVxqv4YvQ5446LAbqsbaZqxtbsJbtcL4tUZospwnwZynwQnNIO8hmeF3EOuuX7WzaxwS7Ugf8acn9Tez8GCijhtipqySr9Q5SLd9F7HV8EXohpfNDR5uAEL7hgJWe1tGi91eScSt9IEMS2CWEZzCi6WilfyQ8cSFjlBL9MC/LAnlm48b+MS2KBIVDtA2PodGWQMWn8UzXrob6Du2gjlBIWUr5Hu5/kx1IhVYZCctHHBK+fSqftTdJMsRQRwIDAQAB",
"private" => "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC13F3kPpJqEMTHEQ+wS8eRPlFKGRrLbEBh2o0a5Ccsdx/zImo+TDKXLPyasZSquFqEL+tuTNST+WJPSiYySYpaSb5w1LJXGq/hi9DnjjosBuqxtpmrG1uwlu1wvi1RmiynCfBnKfBCc0g7yGZ4XcQ665ftbNrHBLtSB/xpyf1N7PwYKKOG2KmrJKv1DlIt30XsdXwReiGl80NHm4AQvuGAlZ7W0aL3V5JxK30gQxLYJYRnMKLpaKV/JDxxIWOUEv0wL8sCeWbjxv4xLYoEhUO0DY+h0ZZAxafxTNeuhvoO7aCOUEhZSvke7n+THUiFVhkJy0ccEr59Kp+1N0kyxFBHAgMBAAECggEAICt4LGzpJ3wJ4xDgjpYJGmdEp+/i7oMarHSlq1EaoOH9s9utoZGHDXj2wkKRgtWTpXh4lA1hOT/PJSl/sjuSDsCmwHzPg1sEK8i4zo05OxqKH5+mdT8krAs2u0/Y4mt8ZJv8e7NOfeK4r2KWxcoIcUfFm0k7NiNfI3aoLup9NXBeWqBnHl5kgtsEDeU1KoEX8U/HW8oTyQqGAW20Jz9mXN1XneADjGpSUwQY0oaG620S51WBKIicKO7R3NP7J67IyCVTnKyAMuTEBoEcweG/WGpGdI+3AkUcJFMrMkwYX/X4Df5zEF93mXf6DxvEI2m1jdOrVZ42yJEg0pE5H1sGuQKBgQD4d1ELhSM8fub31jJrXNewHEIwszFcTiUbEjFiO0Cep5lYuGVZuWdMye2Gc+ZphrtEalLNGFt+sw9MuoqxIYhipnUz9ksHKDRoxR4dOebXCPgP/LHW4P8KAPcvMUr4xNS5hrh9qajjTaRepZwjcIS9CWr8QHDHF6a74K+XGxUn8wKBgQC7YAk34zOmx7BQC6nn7U+yM8Dksfdt2a+/rBGvqp6ILh6DTdeCI3b61L6NZKzVFg7YFOIQbBL88HOm3xz1MAJB4Q/a6A1Rz+lCKnkJ7d+En733nyh1HJlRK9FDaIPHm/iHi/FsJ9wHnrsAa7Vofp4QHWjQB6cUo2nvRoo4S8A/XQKBgEiuhourJ5KTwLaw9tDHOOTwb0BVutO4nEwd90o38QA4ILh+QE+N17TzwMK69qTZ37/0pkIOpP0cHhag3t9P4tiQvuozWuE+Fo6rUtLT1D4FBqOOlOs5qAFiJOyuK7M3yM54pVFFJv1PAg0ZvuHzETFHJv+hThw/Q+vjnxnBt1+XAoGBAJazqPZgMBzFotLebqrwvRaQdWX6lQyu9qFsXVUyHwtcPIJSyzAKIhmfnhrOjAteEFZOhXu70JHLOtlNvVaeZFJkF4Jy/LN+SxdCXdNUlF9wszNDuSBn/g/A9DAJEWQr1/n83hGlBVzDl5fBCUif/bTsUm5umT0KKZue2nBozJipAoGAX/xrRKIY8yfab7/Dc2THtrQA1fmOxDSK5Vki65N4tprsA4UiTdOegFgFGJfTnr8AkUG0leQ0mkYCNoXL1J0MnN92ULdfyAEn9bKW6dl7yVUOsyGLKMqasifI99rZS3aApMtF3/ekoymn+hbV3ftmtDi3HYzJ5QtBXPWXCvsN8LY=",
"iss" => "leapy.cn",
"aud" => "piiot",
"ttl" => 2592000
];

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
];

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use Hyperf\AsyncQueue\Driver\RedisDriver;
return [
'default' => [
'driver' => RedisDriver::class,
'redis' => [
'pool' => 'default',
],
'channel' => '{queue}',
'timeout' => 2,
'retry_seconds' => 5,
'handle_timeout' => 10,
'processes' => 1,
'concurrent' => [
'limit' => 10,
],
'max_messages' => 0,
],
];

19
config/autoload/cache.php Normal file
View File

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'default' => [
'driver' => Hyperf\Cache\Driver\RedisDriver::class,
'packer' => Hyperf\Codec\Packer\PhpSerializerPacker::class,
'prefix' => 'c:',
'skip_cache_results' => [],
],
];

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
];

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use function Hyperf\Support\env;
return [
'default' => [
'driver' => env('DB_DRIVER', 'mysql'),
'host' => env('DB_HOST', 'localhost'),
'database' => env('DB_DATABASE', 'hyperf'),
'port' => env('DB_PORT', 3306),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'collation' => env('DB_COLLATION', 'utf8_unicode_ci'),
'prefix' => env('DB_PREFIX', ''),
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => (float) env('DB_MAX_IDLE_TIME', 60),
],
'commands' => [
'gen:model' => [
'path' => 'app/Model',
'force_casts' => true,
'inheritance' => 'Model',
],
],
],
];

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
];

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'generator' => [
'amqp' => [
'consumer' => [
'namespace' => 'App\\Amqp\\Consumer',
],
'producer' => [
'namespace' => 'App\\Amqp\\Producer',
],
],
'aspect' => [
'namespace' => 'App\\Aspect',
],
'command' => [
'namespace' => 'App\\Command',
],
'controller' => [
'namespace' => 'App\\Controller',
],
'job' => [
'namespace' => 'App\\Job',
],
'listener' => [
'namespace' => 'App\\Listener',
],
'middleware' => [
'namespace' => 'App\\Middleware',
],
'Process' => [
'namespace' => 'App\\Processes',
],
],
];

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
return [
'handler' => [
'http' => [
Hyperf\HttpServer\Exception\Handler\HttpExceptionHandler::class,
App\Exception\Handler\AppExceptionHandler::class,
App\Exception\Handler\ValidationExceptionHandler::class,
App\Exception\Handler\RateLimitExceptionHandle::class,
App\Exception\Handler\DbQueryExceptionHandle::class,
],
],
];

View File

@ -0,0 +1,15 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
Hyperf\ExceptionHandler\Listener\ErrorExceptionHandler::class,
Hyperf\Command\Listener\FailToHandleListener::class,
];

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'default' => [
'handler' => [
'class' => Monolog\Handler\StreamHandler::class,
'constructor' => [
'stream' => BASE_PATH . '/runtime/logs/hyperf.log',
'level' => Monolog\Logger::DEBUG,
],
],
'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [
'format' => null,
'dateFormat' => 'Y-m-d H:i:s',
'allowInlineLineBreaks' => true,
],
],
],
];

View File

@ -0,0 +1,17 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
'http' => [
App\Kernel\Middleware\JWTMiddleware::class,
App\Kernel\Middleware\CorsMiddleware::class,
],
];

View File

@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
return [
];

View File

@ -0,0 +1,27 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use Hyperf\RateLimit\Storage\RedisStorage;
return [
'create' => 1,
'consume' => 1,
'capacity' => 2,
'limitCallback' => [],
'waitTimeout' => 1,
'storage' => [
'class' => RedisStorage::class,
'options' => [
'pool' => 'default',
'expired_time' => 0,
],
],
];

29
config/autoload/redis.php Normal file
View File

@ -0,0 +1,29 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use function Hyperf\Support\env;
return [
'default' => [
'host' => env('REDIS_HOST', 'localhost'),
'auth' => env('REDIS_AUTH', null),
'port' => (int) env('REDIS_PORT', 6379),
'db' => (int) env('REDIS_DB', 0),
'pool' => [
'min_connections' => 1,
'max_connections' => 10,
'connect_timeout' => 10.0,
'wait_timeout' => 3.0,
'heartbeat' => -1,
'max_idle_time' => (float) env('REDIS_MAX_IDLE_TIME', 60),
],
],
];

View File

@ -0,0 +1,61 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use Hyperf\Server\Event;
use Hyperf\Server\Server;
use Swoole\Constant;
use function Hyperf\Support\env;
return [
'mode' => SWOOLE_PROCESS,
'servers' => [
[
'name' => 'http',
'type' => Server::SERVER_HTTP,
'host' => '0.0.0.0',
'port' => (int)env("APP_PORT"),
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_REQUEST => [Hyperf\HttpServer\Server::class, 'onRequest'],
],
'options' => [
// Whether to enable request lifecycle event
'enable_request_lifecycle' => false,
],
],
[
'name' => 'mqtt',
'type' => Server::SERVER_BASE,
'host' => '0.0.0.0',
'port' => (int)env("MQTT_PORT"),
'sock_type' => SWOOLE_SOCK_TCP,
'callbacks' => [
Event::ON_RECEIVE => [Hyperf\MqttServer\MQTTServer::class, 'onReceive'],
],
],
],
'settings' => [
Constant::OPTION_ENABLE_COROUTINE => true,
Constant::OPTION_WORKER_NUM => swoole_cpu_num(),
Constant::OPTION_PID_FILE => BASE_PATH . '/runtime/hyperf.pid',
Constant::OPTION_OPEN_TCP_NODELAY => true,
Constant::OPTION_MAX_COROUTINE => 100000,
Constant::OPTION_OPEN_HTTP2_PROTOCOL => true,
Constant::OPTION_MAX_REQUEST => 100000,
Constant::OPTION_SOCKET_BUFFER_SIZE => 2 * 1024 * 1024,
Constant::OPTION_BUFFER_OUTPUT_SIZE => 2 * 1024 * 1024,
],
'callbacks' => [
Event::ON_WORKER_START => [Hyperf\Framework\Bootstrap\WorkerStartCallback::class, 'onWorkerStart'],
Event::ON_PIPE_MESSAGE => [Hyperf\Framework\Bootstrap\PipeMessageCallback::class, 'onPipeMessage'],
Event::ON_WORKER_EXIT => [Hyperf\Framework\Bootstrap\WorkerExitCallback::class, 'onWorkerExit'],
],
];

33
config/config.php Normal file
View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
use Hyperf\Contract\StdoutLoggerInterface;
use Psr\Log\LogLevel;
use function Hyperf\Support\env;
return [
'app_name' => env('APP_NAME', 'skeleton'),
'app_env' => env('APP_ENV', 'dev'),
'scan_cacheable' => env('SCAN_CACHEABLE', false),
StdoutLoggerInterface::class => [
'log_level' => [
LogLevel::ALERT,
LogLevel::CRITICAL,
LogLevel::DEBUG,
LogLevel::EMERGENCY,
LogLevel::ERROR,
LogLevel::INFO,
LogLevel::NOTICE,
LogLevel::WARNING,
],
],
];

14
config/container.php Normal file
View File

@ -0,0 +1,14 @@
<?php
/**
* Initialize a dependency injection container that implemented PSR-11 and return the container.
*/
declare(strict_types=1);
use Hyperf\Context\ApplicationContext;
use Hyperf\Di\Container;
use Hyperf\Di\Definition\DefinitionSourceFactory;
$container = new Container((new DefinitionSourceFactory())());
return ApplicationContext::setContainer($container);

9
config/routes.php Normal file
View File

@ -0,0 +1,9 @@
<?php
declare(strict_types=1);
use Hyperf\HttpServer\Router\Router;
Router::get('/favicon.ico', function () {
return '';
});