diff --git a/.env.example b/.env.example index 43aaf05..812fcbc 100644 --- a/.env.example +++ b/.env.example @@ -1,22 +1,25 @@ -APP_NAME=sfyy +APP_NAME=ctext APP_ENV=dev APP_DEBUG=false APP_URL=http://127.0.0.1:9501 JWT_SECRET=azOVxsOWt3r0ozZNz8Ss429ht0T8z6OpeIJAIwNp6X0xqrbEY2epfIWyxtC1qSNM8eD6/LQ/SahcQi2ByXa/2A== +JWT_ADMIN_SECRET=azOVxsOWt3r0ozZNz8Ss429ht0T8z6OpeIJAIwNp6X0xqrbEY2epfIWyxtC1qSNM8eD6/LQ/SahcQi2ByXa/2Aaa +# [ide] +DEVTOOL_IDE=phpstorm DB_DRIVER=mysql -DB_HOST=s2.gnip.vip -DB_PORT=20191 -DB_DATABASE=mineadmin -DB_USERNAME=hhl -DB_PASSWORD=hhltest +DB_HOST=127.0.0.1 +DB_PORT=3306 +DB_DATABASE=xxx +DB_USERNAME=username +DB_PASSWORD=password DB_CHARSET=utf8mb4 DB_COLLATION=utf8mb4_unicode_ci DB_PREFIX= -REDIS_HOST=s2.gnip.vip -REDIS_AUTH=hhltest -REDIS_PORT=4379 +REDIS_HOST=127.0.0.1 +REDIS_AUTH= +REDIS_PORT=6379 REDIS_DB=0 \ No newline at end of file diff --git a/README.md b/README.md index 42b277d..4659c87 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ## 仓库 -- [sfyy_server](https://codeup.aliyun.com/67039465d8d1ada68263f984/hhl/rewrite/hyperf_service.git) - git远程仓库地址 +- [hyperf_rbac_framework_server_ctexthuang](https://gitee.com/ctexthuang/hyperf_rbac_framework_server_ctexthuang.git) - git远程仓库地址 ## 特性 @@ -27,9 +27,7 @@ - 获取代码 ```bash -git clone https://codeup.aliyun.com/67039465d8d1ada68263f984/hhl/rewrite/hyperf_service.git - -mkdir uploads +git clone https://gitee.com/ctexthuang/hyperf_rbac_framework_server_ctexthuang.git ``` - vendor @@ -50,22 +48,32 @@ php bin/hyperf.php start - command 函数 ```bash -#框架自有 -php bin/hyperf.php gen:controller LoginController -php bin/hyperf.php gen:model UserModel -php bin/hyperf.php gen:request LoginRequest -php bin/hyperf.php gen:command TestCommand -php bin/hyperf.php gen:job TestJob -php bin/hyperf.php gen:listener TestListener -php bin/hyperf.php gen:middleware AuthMiddleware -php bin/hyperf.php gen:amqp-consumer DemoConsumer -php bin/hyperf.php gen:amqp-producer DemoProducer -php bin/hyperf.php gen:constant ErrorCode --type enum +#框架自有 php bin/hyperf.php + 以下 + gen:amqp-consumer Create a new amqp consumer class + gen:amqp-producer Create a new amqp producer class + gen:aspect Create a new aspect class + gen:class Create a new class + gen:command Create a new command class + gen:constant Create a new constant class + gen:controller Create a new controller class + gen:job Create a new job class + gen:kafka-consumer Create a new kafka consumer class + gen:listener Create a new listener class + gen:middleware Create a new middleware class + gen:migration Generate a new migration file + gen:model Create new model classes. + gen:nats-consumer Create a new nats consumer class + gen:nsq-consumer Create a new nsq consumer class + gen:process Create a new process class + gen:repository Create a new repository class + gen:request Create a new form request class + gen:resource create a new resource + gen:seeder Create a new seeder class + gen:service Create a new service class #新增命令 php bin/hyperf.php gen:service LoginService -php bin/hyperf.php gen:cron OssTask -php bin/hyperf.php gen:event TestEvent +php bin/hyperf.php gen:repository OssRepository ``` ## Git 贡献提交规范 @@ -86,9 +94,6 @@ php bin/hyperf.php gen:event TestEvent - `wip` 开发中 - `types` 类型 -## cache -不允许使用序列化,为跨语言做准备 - ## 日志(合理安排) | 分组名称 | 用途 | 日志级别 | 保留天数 | 备注 | |---------|---------|------------|------|----------| @@ -98,4 +103,180 @@ php bin/hyperf.php gen:event TestEvent | request | 请求访问日志 | INFO | 15 | 记录所有请求 | | cron | 定时任务日志 | INFO | 30 | 定时任务执行记录 | | payment | 支付相关日志 | INFO | 90 | 重要财务数据 | -| audit | 审计日志 | INFO | 365 | 重要操作记录 | \ No newline at end of file +| audit | 审计日志 | INFO | 365 | 重要操作记录 |\ + +```php +//注入这个类 +use App\Lib\Log\Logger; + +$this->logger->cache()->error(...), +``` + +## 缓存 +```php +//注入这个类 +use App\Cache\Redis\RedisCache + +$this->redis->with($poolName)->exists($key) +//$poolName 配置在 config/autoload/redis.php 在 default下面追加即可 看官网 + +//lua调用涉及多文件请看示例 + +$this->redis->with($poolName ?? 'default')->lua(RateLimit::class)->check($argument); +//lua()里面是App\Cache\Redis\Lua\*文件 后面跟的方法是 该文件的方法 +//我做了返回类型补全 可以直接IDE跳转查看参数 +``` + +## tips + +1.AdminService类必须继承BaseAdminService +```php +//例子 app/Service/Admin/AdminUser/UserService +class UserService extends \App\Service\Admin\BaseAdminService { + +} + +//由于在 app/Controller/Admin/AdminUserController 中 service 是 注解注入的 所以 BaseAdminService只会实例化一次(单例) +class AdminUserController extends AbstractController +{ + /** + * @var UserService + */ + #[Inject] + protected UserService $service; + + /** + * @return array + */ + #[RequestMapping(path: "getInfo", methods: "GET")] + public function getInfo(): array + { + return $this->service->handle(); + } +} + +//所以在service中获取上下文的数据必须重置获取而不能存入BaseService属性中拿取 +//假设在 Api目录中写 BaseApiService请务必注意 +class UserService extends \App\Service\Admin\BaseAdminService { + /** + * @param string $name + * @return \current_admin_id|mixed + */ + public function __get(string $name) + { + if ($name === 'adminId') return Context::get('current_admin_id',0); + + if (!property_exists($this, $name)) throw new ErrException('属性未定义'); + + return $this->$name; + } +} + +class UserService extends BaseAdminService{ + public function handle() + { + return $this->adminId; + } +} +// 或者在controller中实例化去调用 +class AdminUserController extends AbstractController +{ + /** + * @return array + */ + #[RequestMapping(path: "getInfo", methods: "GET")] + public function getInfo(): array + { + return (new UserService)->handle(); + } +} +``` + +2.controller必须增加ResponseFormat注解 +```php +//示例 : app/Controller/Admin/AdminUserController + +#[Controller(prefix: "admin/adminUser")] +#[ResponseFormat('admin')] +#[Middleware(middleware: AdminTokenMiddleware::class, priority: 100)] +#[Middleware(middleware: PermissionMiddleware::class, priority: 99)] +class AdminUserController extends \App\Controller\AbstractController{ + +} + +//因为在涉及到公有抛出 ErrException或者 验证器抛出 的时候 会根据不同的注解 返回不同的 Return +class BaseErrExceptionHandler extends ExceptionHandler{ + public function handle() + { + // 从注解获取响应格式(优先于路径解析) + $format = $this->request->getAttribute('response_format') ?? $this->repairResponseFormatByPath(); + + // 动态选择策略 + $returnClass = match ($format) { + 'admin', 'common' => AdminReturn::class, + 'api' => ApiReturn::class, + default => null, + }; + } +} + +// 通过切面存入 +class ResponseFormatAspect extends AbstractAspect +{ + // 获取注解定义的格式 + $annotation = $proceedingJoinPoint->getAnnotationMetadata()->class[ResponseFormat::class] + ?? $proceedingJoinPoint->getAnnotationMetadata()->method[ResponseFormat::class] ?? null; + + if ($annotation) { + // 将注解格式存入请求属性(覆盖中间件的默认值) + $request = $proceedingJoinPoint->arguments['request'] ?? null; + if ($request instanceof Request) { + $request->withAttribute('response_format', $annotation->format); + } + } +} +``` + +3.token的使用要分模块 +```php +//示例 app/Service/Admin/Login/LoginService +use App\Service\BaseTokenService $tokenService + +class LoginService extends \App\Service\Admin\BaseAdminService{ + public function handle() + { + $jwtHandle = $this->tokenService->setJwt('admin')->getJwt(); + + return $this->adminReturn->success('success',[ + 'access_token' => $jwtHandle->builderAccessToken((string) $adminInfo->id)->toString(), + 'refresh_token' => $jwtHandle->builderRefreshToken((string) $adminInfo->id)->toString(), + 'expire_at' => (int) $jwtHandle->getConfig('ttl', 0), + ]); + } +} +//setJwt函数里面类似$poolName 设置在 config/autoload/jwt.php 未设置属性可复用 default 设置 +``` + +4.引入 Repository层架构 解放 model层压力 model 只管理数据模型和关联 +```php +//详细请看app/Common/Repository 文件下的文件 示例 app/Service/Admin/Login/LoginService + +class LoginService extends \App\Service\Admin\BaseAdminService{ + + /** + * 需要提前注入 + * @var AdminUserRepository + */ + #[Inject] + protected AdminUserRepository $userRepository; + + public function handle() + { + $adminInfo = $this->userRepository->findByUserName((string)$this->request->input('username')); + } +} +``` + +5.公用类都在 app/Common 下面 包括 `Interface`、`Macros`、`Repository`、`Trait` + +6.后台权限注解 #[Permission] 和 #[PermissionMiddleware] \ No newline at end of file diff --git a/app/Cache/Redis/BaseScript.php b/app/Cache/Redis/BaseScript.php index df2749b..347c431 100644 --- a/app/Cache/Redis/BaseScript.php +++ b/app/Cache/Redis/BaseScript.php @@ -163,7 +163,7 @@ abstract class BaseScript ]; $logStrategy = match(true) { - !$success => $this->logger->cache()->error(...), + !$success => $this->logger->error()->error(...), $this->debugMode => $this->logger->cache()->debug(...), default => fn() => null // 不记录 }; diff --git a/app/Cache/Redis/RedisCache.php b/app/Cache/Redis/RedisCache.php index e767aea..33b677c 100644 --- a/app/Cache/Redis/RedisCache.php +++ b/app/Cache/Redis/RedisCache.php @@ -2,6 +2,7 @@ namespace App\Cache\Redis; +use App\Cache\Redis\Lua\RateLimit; use App\Lib\Log\Logger; use Hyperf\Contract\ConfigInterface; use Hyperf\Redis\RedisFactory; @@ -9,6 +10,7 @@ use Hyperf\Redis\RedisProxy; /** * @mixin RedisProxy + * @template T */ class RedisCache { @@ -53,10 +55,11 @@ class RedisCache } /** - * @param string $scriptClass - * @return mixed + * @template TReturn + * @param class-string $scriptClass + * @return TReturn */ - public function lua(string $scriptClass): mixed + public function lua(string $scriptClass) { $poolName = $this->poolName ?? 'default'; $key = $poolName . ':' . $scriptClass; diff --git a/app/Common/Trait/AdminUserTrait.php b/app/Common/Trait/AdminUserTrait.php index 597d315..37646e7 100644 --- a/app/Common/Trait/AdminUserTrait.php +++ b/app/Common/Trait/AdminUserTrait.php @@ -2,6 +2,7 @@ namespace App\Common\Trait; +use App\Cache\Redis\Lua\RateLimit; use App\Cache\Redis\RedisCache; use App\Cache\Redis\RedisKey; use App\Common\Repository\AdminUserRepository; @@ -34,7 +35,7 @@ trait AdminUserTrait if (Context::has($key)) { return Context::get($key,false); } - + $this->redis->with()->lua(RateLimit::class)->check(); if ($this->redis->with()->exists($key)) { $userInfo = unserialize($this->redis->with()->get($key)); Context::set($key,$userInfo); diff --git a/config/autoload/dependencies.php b/config/autoload/dependencies.php index f25f7a2..df7c408 100644 --- a/config/autoload/dependencies.php +++ b/config/autoload/dependencies.php @@ -11,5 +11,5 @@ declare(strict_types=1); */ return [ Hyperf\Database\Schema\Blueprint::class => App\Common\Macros\BlueprintMacros::class, - \App\Common\Interface\CheckTokenInterface::class => App\Service\BaseTokenService::class, + App\Common\Interface\CheckTokenInterface::class => App\Service\BaseTokenService::class, ];