初始化本地仓库

This commit is contained in:
2026-08-31 11:33:59 +08:00
commit 663f74b633
2390 changed files with 402918 additions and 0 deletions
@@ -0,0 +1,235 @@
<?php
declare(strict_types=1);
/**
*+------------------
* madong
*+------------------
* Copyright (c) https://gitee.com/motion-code All rights reserved.
*+------------------
* Author: Mr. April (405784684@qq.com)
*+------------------
* Official Website: http://www.madong.tech
*/
namespace app\adminapi\controller\login;
use app\adminapi\controller\Crud;
use app\adminapi\schema\request\login\LoginRequest;
use app\adminapi\schema\request\ThirdPartyLoginRequest;
use app\service\admin\system\AdminService;
use core\cache\CacheService;
use core\captcha\Captcha;
use core\exception\handler\AdminException;
use core\jwt\JwtToken;
use core\tool\Json;
use core\tool\RSAService;
use core\uuid\UUIDGenerator;
use madong\swagger\annotation\response\SimpleResponse;
use OpenApi\Attributes as OA;
use OpenApi\Attributes\RequestBody;
use support\Container;
use support\Request;
final class LoginController extends Crud
{
public function __construct(AdminService $service)
{
$this->service = $service;
}
#[OA\Get(
path: '/login/get-captcha-open-flag',
summary: '验证码开启状态',
tags: ['无需授权'],
)]
#[SimpleResponse(schema: ['flag' => true], example:[])]
public function getCaptchaOpenFlag(Request $request): \support\Response
{
try {
// 生成密钥对
$cache = Container::make(CacheService::class, []);
$keyId = md5(UUIDGenerator::generate());
$keys = RSAService::generateKeys();
// 存储私钥到缓存,用于解密密码
$cache->set("rsa_private_key_$keyId", $keys['private'], 60); // 5分钟过期
return Json::success('ok', ['flag' => config('core.captcha.app.enable', false), 'key_id' => $keyId, 'public_key' => $keys['public']]);
} catch (\Throwable $e) {
return Json::fail($e->getMessage());
}
}
#[OA\Get(
path: '/login/captcha',
summary: '验证码',
tags: ['无需授权'],
)]
#[SimpleResponse(schema: [], example:[])]
public function captcha(Request $request): \support\Response
{
try {
$captcha = new Captcha();
$type = $request->input('type', 'admin-login');
$result = $captcha->captcha($request, $type);
return Json::success('ok', $result);
} catch (\Throwable $e) {
return Json::fail($e->getMessage());
}
}
#[OA\Post(
path: '/login/send-sms',
summary: '手机验证码',
tags: ['无需授权']
)]
#[OA\Parameter(
name: 'mobile_phone',
description: '手机号码',
in: 'query',
required: true,
schema: new OA\Schema(type: 'integer', default: 18888888888)
)]
#[SimpleResponse(schema:[],example: '{"code": 185841,"uid":18888888888}}')]
public function sendSms(Request $request): \support\Response
{
// 1.0生成验证码
$captcha = new Captcha();
$result = $captcha->generateSmsCaptcha($request);
// 2.0发送验证码到手机
// 3.0 存储验证码到 Redis,设置过期时间(如 5 分钟)
return Json::success('验证码发送成功', $result);
}
#[OA\Post(
path: '/login/login',
summary: '登录',
tags: ['无需授权']
)]
#[RequestBody(
description: '请求参数',
required: true,
content: new OA\JsonContent(ref: LoginRequest::class)
)]
#[SimpleResponse(schema:[],example: '{"token_type": "Bearer","expires_in": 7200,"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtYWRvbmcudGVjaCIsImF1ZCI6Im1hZG9uZy50ZWNoIiwiaWF0IjoxNzYyNDExMjY5LCJuYmYiOjE3NjI0MTEyNjksImV4cCI6MTc2MjQxODQ2OSwiZXh0ZW5kIjp7ImlkIjoiMiIsInVzZXJfbmFtZSI6ImFkbWluIiwicmVhbF9uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4Iiwibmlja19uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4IiwiaXNfc3VwZXIiOjEsImlzX3RlbmFudF9hZG1pbiI6MSwibW9iaWxlX3Bob25lIjpudWxsLCJlbWFpbCI6bnVsbCwiYXZhdGFyIjoiL3VwbG9hZC80MmY4NmI5YjM2Nzk0ZmI5ZTM4MDkxN2MyNTFmNmQ4MS5wbmciLCJzaWduZWQiOm51bGwsImRhc2hib2FyZCI6bnVsbCwiZGVwdF9pZCI6bnVsbCwiZW5hYmxlZCI6MSwibG9naW5faXAiOiIxMjAuNDEuMjQ2LjIyNSIsImxvZ2luX3RpbWUiOjE3NjI0MTEyNjksInVwZGF0ZWRfYXQiOiIyMDI1LTExLTA2VDA2OjQxOjA5LjAwMDAwMFoiLCJzZXgiOjAsImJpcnRoZGF5IjpudWxsLCJ0ZWwiOm51bGwsImlzX2xvY2tlZCI6MCwiZGVwdHMiOltdLCJwb3N0cyI6W10sInRlbmFudHMiOlt7ImlkIjoiMSIsImRiX25hbWUiOiJtYWRvbmdfYWRtaW5fc2FhcyIsImNvZGUiOiJwbGF0Zm9ybSIsInR5cGUiOjAsImNvbnRhY3RfcGVyc29uIjoiYWRtaW4iLCJjb250YWN0X3Bob25lIjoiMTg4ODg4ODg4ODgiLCJjb21wYW55X25hbWUiOiJ4eHh4IFx1NjcwOVx1OTY1MFx1NTE2Y1x1NTNmOCIsImxpY2Vuc2VfbnVtYmVyIjoiIiwiYWRkcmVzcyI6Ilx1NGUyZFx1NTZmZCIsImRlc2NyaXB0aW9uIjoiXHU1MTg1XHU3ZjZlXHU4ZDI2XHU1M2Y3IiwiZG9tYWluIjoiaHR0cHM6Ly93d3cubWFkb25nLnRlY2giLCJlbmFibGVkIjoxLCJpc29sYXRpb25fbW9kZSI6MiwiaXNfZGVmYXVsdCI6MSwiZXhwaXJlZF9hdCI6bnVsbCwiZGVsZXRlZF9hdCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMjUtMTAtMDNUMDM6MjM6NDcuMDAwMDAwWiIsImNyZWF0ZWRfYnkiOjEsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfYXQiOm51bGwsImNyZWF0ZWRfZGF0ZSI6IjIwMjUtMTAtMDMgMTE6MjM6NDciLCJ1cGRhdGVkX2RhdGUiOm51bGwsImV4cGlyZWRfZGF0ZSI6bnVsbCwicGl2b3QiOnsiYWRtaW5faWQiOjIsInRlbmFudF9pZCI6MSwiY3JlYXRlZF9kYXRlIjpudWxsLCJ1cGRhdGVkX2RhdGUiOm51bGx9fV0sImNhc2JpbiI6W119fQ.rCaHoiI1gU7xyoJazt3V5JQrS2guV34PH-JjkWnceE4","refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtYWRvbmcudGVjaCIsImF1ZCI6Im1hZG9uZy50ZWNoIiwiaWF0IjoxNzYyNDExMjY5LCJuYmYiOjE3NjI0MTEyNjksImV4cCI6MTc2MzAxNjA2OSwiZXh0ZW5kIjp7ImlkIjoiMiIsInVzZXJfbmFtZSI6ImFkbWluIiwicmVhbF9uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4Iiwibmlja19uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4IiwiaXNfc3VwZXIiOjEsImlzX3RlbmFudF9hZG1pbiI6MSwibW9iaWxlX3Bob25lIjpudWxsLCJlbWFpbCI6bnVsbCwiYXZhdGFyIjoiL3VwbG9hZC80MmY4NmI5YjM2Nzk0ZmI5ZTM4MDkxN2MyNTFmNmQ4MS5wbmciLCJzaWduZWQiOm51bGwsImRhc2hib2FyZCI6bnVsbCwiZGVwdF9pZCI6bnVsbCwiZW5hYmxlZCI6MSwibG9naW5faXAiOiIxMjAuNDEuMjQ2LjIyNSIsImxvZ2luX3RpbWUiOjE3NjI0MTEyNjksInVwZGF0ZWRfYXQiOiIyMDI1LTExLTA2VDA2OjQxOjA5LjAwMDAwMFoiLCJzZXgiOjAsImJpcnRoZGF5IjpudWxsLCJ0ZWwiOm51bGwsImlzX2xvY2tlZCI6MCwiZGVwdHMiOltdLCJwb3N0cyI6W10sInRlbmFudHMiOlt7ImlkIjoiMSIsImRiX25hbWUiOiJtYWRvbmdfYWRtaW5fc2FhcyIsImNvZGUiOiJwbGF0Zm9ybSIsInR5cGUiOjAsImNvbnRhY3RfcGVyc29uIjoiYWRtaW4iLCJjb250YWN0X3Bob25lIjoiMTg4ODg4ODg4ODgiLCJjb21wYW55X25hbWUiOiJ4eHh4IFx1NjcwOVx1OTY1MFx1NTE2Y1x1NTNmOCIsImxpY2Vuc2VfbnVtYmVyIjoiIiwiYWRkcmVzcyI6Ilx1NGUyZFx1NTZmZCIsImRlc2NyaXB0aW9uIjoiXHU1MTg1XHU3ZjZlXHU4ZDI2XHU1M2Y3IiwiZG9tYWluIjoiaHR0cHM6Ly93d3cubWFkb25nLnRlY2giLCJlbmFibGVkIjoxLCJpc29sYXRpb25fbW9kZSI6MiwiaXNfZGVmYXVsdCI6MSwiZXhwaXJlZF9hdCI6bnVsbCwiZGVsZXRlZF9hdCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMjUtMTAtMDNUMDM6MjM6NDcuMDAwMDAwWiIsImNyZWF0ZWRfYnkiOjEsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfYXQiOm51bGwsImNyZWF0ZWRfZGF0ZSI6IjIwMjUtMTAtMDMgMTE6MjM6NDciLCJ1cGRhdGVkX2RhdGUiOm51bGwsImV4cGlyZWRfZGF0ZSI6bnVsbCwicGl2b3QiOnsiYWRtaW5faWQiOjIsInRlbmFudF9pZCI6MSwiY3JlYXRlZF9kYXRlIjpudWxsLCJ1cGRhdGVkX2RhdGUiOm51bGx9fV0sImNhc2JpbiI6W119fQ.IgLkKhjjsgjAsjS4qReg51WpzwjiuU27_tbqTpUuD3M","client_id": "127.0.0.1-690c430548ecd5.55813566","expires_time": 1762418469}')]
public function login(Request $request): \support\Response
{
try {
$username = $request->input('user_name');
$password = $request->input('password', '');
$phone = $request->input('mobile_phone', '');
$code = $request->input('code', '');
$uuid = $request->input('uuid', '');
$type = $request->input('type', 'admin');
$grantType = $request->input('grant_type', 'deafult');//refresh_token sms default 可以自行定义拓展登录方式
$keyId = $request->input('key_id', '');//获取公钥Id
$captcha = new Captcha();
if (config('core.captcha.app.enable') && $grantType === 'default') {
if (!$captcha->check($uuid, $code)) {
throw new AdminException('图片验证码错误!');
}
}
if ($grantType == 'sms' && !empty($phone)) {
if (!$captcha->check($phone, $code)) {
throw new AdminException('手机验证码错误!');
}
$info = $this->service->get(['mobile_phone' => $phone]);
if (empty($info)) {
throw new AdminException('当前手机未绑定用户!');
}
$username = $info->getData('user_name');
}
$data = $this->service->login($username, $password, $type, $grantType, ['key_id' => $keyId ?? '']);
return Json::success('ok', $data);
} catch (\Throwable $e) {
return Json::fail($e->getMessage());
}
}
#[OA\Post(
path: '/login/logout',
summary: '注销',
security: [['Bearer' => [], 'ApiKey' => []]],
tags: ['无需授权'],
)]
#[OA\Parameter(
name: 'token',
description: '令牌(可选,也可通过Authorization头传递)',
in: 'query',
required: false,
schema: new OA\Schema(type: 'string')
)]
#[SimpleResponse(schema:[],example: [])]
public function logout(Request $request): \support\Response
{
// 1. 获取token
$token = $request->header(config('core.jwt.app.token_name', 'Authorization')) ?: $request->get('token');
// 2. 快速验证无效token情况
if (empty($token) || $token === 'undefined') {
return Json::success('ok', []);
}
// 3. 处理Bearer Token格式
if (str_starts_with($token, 'Bearer ')) {
$token = substr($token, 7); // 移除"Bearer "前缀
if (empty($token) || $token === 'undefined') {
return Json::success('ok', []);
}
}
// 4. 退出登录
(new JwtToken())->logout($token);
return Json::success('ok', []);
}
#[OA\Get(
path: '/login/auth/public-key',
summary: '生成RSA密钥对',
tags: ['无需授权'],
)]
#[SimpleResponse(schema:[],example: [])]
public function generateRSAKeyPair(Request $request): \support\Response
{
try {
// 生成密钥对
$cache = Container::make(CacheService::class, []);
$keyId = md5(UUIDGenerator::generate());
$keys = RSAService::generateKeys();
// 存储私钥到缓存,用于解密密码
$cache->set("rsa_private_key_$keyId", $keys['private'], 60); // 5分钟过期
return Json::success('ok', ['public_key' => $keys['public'], 'key_id' => $keyId]);
} catch (\Throwable $e) {
return Json::fail($e->getMessage());
}
}
#[OA\Post(
path: '/login/third-party/login',
summary: '第三方测试登录接口(免RSA)',
tags: ['无需授权']
)]
#[RequestBody(
description: '请求参数',
required: true,
content: new OA\JsonContent(ref: ThirdPartyLoginRequest::class)
)]
#[SimpleResponse(schema:[],example: '{"code": 0,"msg": "ok","data": {"token_type": "Bearer","expires_in": 7200,"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtYWRvbmcudGVjaCIsImF1ZCI6Im1hZG9uZy50ZWNoIiwiaWF0IjoxNzYyNDExMjY5LCJuYmYiOjE3NjI0MTEyNjksImV4cCI6MTc2MjQxODQ2OSwiZXh0ZW5kIjp7ImlkIjoiMiIsInVzZXJfbmFtZSI6ImFkbWluIiwicmVhbF9uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4Iiwibmlja19uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4IiwiaXNfc3VwZXIiOjEsImlzX3RlbmFudF9hZG1pbiI6MSwibW9iaWxlX3Bob25lIjpudWxsLCJlbWFpbCI6bnVsbCwiYXZhdGFyIjoiL3VwbG9hZC80MmY4NmI5YjM2Nzk0ZmI5ZTM4MDkxN2MyNTFmNmQ4MS5wbmciLCJzaWduZWQiOm51bGwsImRhc2hib2FyZCI6bnVsbCwiZGVwdF9pZCI6bnVsbCwiZW5hYmxlZCI6MSwibG9naW5faXAiOiIxMjAuNDEuMjQ2LjIyNSIsImxvZ2luX3RpbWUiOjE3NjI0MTEyNjksInVwZGF0ZWRfYXQiOiIyMDI1LTExLTA2VDA2OjQxOjA5LjAwMDAwMFoiLCJzZXgiOjAsImJpcnRoZGF5IjpudWxsLCJ0ZWwiOm51bGwsImlzX2xvY2tlZCI6MCwiZGVwdHMiOltdLCJwb3N0cyI6W10sInRlbmFudHMiOlt7ImlkIjoiMSIsImRiX25hbWUiOiJtYWRvbmdfYWRtaW5fc2FhcyIsImNvZGUiOiJwbGF0Zm9ybSIsInR5cGUiOjAsImNvbnRhY3RfcGVyc29uIjoiYWRtaW4iLCJjb250YWN0X3Bob25lIjoiMTg4ODg4ODg4ODgiLCJjb21wYW55X25hbWUiOiJ4eHh4IFx1NjcwOVx1OTY1MFx1NTE2Y1x1NTNmOCIsImxpY2Vuc2VfbnVtYmVyIjoiIiwiYWRkcmVzcyI6Ilx1NGUyZFx1NTZmZCIsImRlc2NyaXB0aW9uIjoiXHU1MTg1XHU3ZjZlXHU4ZDI2XHU1M2Y3IiwiZG9tYWluIjoiaHR0cHM6Ly93d3cubWFkb25nLnRlY2giLCJlbmFibGVkIjoxLCJpc29sYXRpb25fbW9kZSI6MiwiaXNfZGVmYXVsdCI6MSwiZXhwaXJlZF9hdCI6bnVsbCwiZGVsZXRlZF9hdCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMjUtMTAtMDNUMDM6MjM6NDcuMDAwMDAwWiIsImNyZWF0ZWRfYnkiOjEsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfYXQiOm51bGwsImNyZWF0ZWRfZGF0ZSI6IjIwMjUtMTAtMDMgMTE6MjM6NDciLCJ1cGRhdGVkX2RhdGUiOm51bGwsImV4cGlyZWRfZGF0ZSI6bnVsbCwicGl2b3QiOnsiYWRtaW5faWQiOjIsInRlbmFudF9pZCI6MSwiY3JlYXRlZF9kYXRlIjpudWxsLCJ1cGRhdGVkX2RhdGUiOm51bGx9fV0sImNhc2JpbiI6W119fQ.rCaHoiI1gU7xyoJazt3V5JQrS2guV34PH-JjkWnceE4","refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJtYWRvbmcudGVjaCIsImF1ZCI6Im1hZG9uZy50ZWNoIiwiaWF0IjoxNzYyNDExMjY5LCJuYmYiOjE3NjI0MTEyNjksImV4cCI6MTc2MzAxNjA2OSwiZXh0ZW5kIjp7ImlkIjoiMiIsInVzZXJfbmFtZSI6ImFkbWluIiwicmVhbF9uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4Iiwibmlja19uYW1lIjoiXHU4ZDg1XHU3ZWE3XHU3YmExXHU3NDA2XHU1NDU4IiwiaXNfc3VwZXIiOjEsImlzX3RlbmFudF9hZG1pbiI6MSwibW9iaWxlX3Bob25lIjpudWxsLCJlbWFpbCI6bnVsbCwiYXZhdGFyIjoiL3VwbG9hZC80MmY4NmI5YjM2Nzk0ZmI5ZTM4MDkxN2MyNTFmNmQ4MS5wbmciLCJzaWduZWQiOm51bGwsImRhc2hib2FyZCI6bnVsbCwiZGVwdF9pZCI6bnVsbCwiZW5hYmxlZCI6MSwibG9naW5faXAiOiIxMjAuNDEuMjQ2LjIyNSIsImxvZ2luX3RpbWUiOjE3NjI0MTEyNjksInVwZGF0ZWRfYXQiOiIyMDI1LTExLTA2VDA2OjQxOjA5LjAwMDAwMFoiLCJzZXgiOjAsImJpcnRoZGF5IjpudWxsLCJ0ZWwiOm51bGwsImlzX2xvY2tlZCI6MCwiZGVwdHMiOltdLCJwb3N0cyI6W10sInRlbmFudHMiOlt7ImlkIjoiMSIsImRiX25hbWUiOiJtYWRvbmdfYWRtaW5fc2FhcyIsImNvZGUiOiJwbGF0Zm9ybSIsInR5cGUiOjAsImNvbnRhY3RfcGVyc29uIjoiYWRtaW4iLCJjb250YWN0X3Bob25lIjoiMTg4ODg4ODg4ODgiLCJjb21wYW55X25hbWUiOiJ4eHh4IFx1NjcwOVx1OTY1MFx1NTE2Y1x1NTNmOCIsImxpY2Vuc2VfbnVtYmVyIjoiIiwiYWRkcmVzcyI6Ilx1NGUyZFx1NTZmZCIsImRlc2NyaXB0aW9uIjoiXHU1MTg1XHU3ZjZlXHU4ZDI2XHU1M2Y3IiwiZG9tYWluIjoiaHR0cHM6Ly93d3cubWFkb25nLnRlY2giLCJlbmFibGVkIjoxLCJpc29sYXRpb25fbW9kZSI6MiwiaXNfZGVmYXVsdCI6MSwiZXhwaXJlZF9hdCI6bnVsbCwiZGVsZXRlZF9hdCI6bnVsbCwiY3JlYXRlZF9hdCI6IjIwMjUtMTAtMDNUMDM6MjM6NDcuMDAwMDAwWiIsImNyZWF0ZWRfYnkiOjEsInVwZGF0ZWRfYnkiOm51bGwsInVwZGF0ZWRfYXQiOm51bGwsImNyZWF0ZWRfZGF0ZSI6IjIwMjUtMTAtMDMgMTE6MjM6NDciLCJ1cGRhdGVkX2RhdGUiOm51bGwsImV4cGlyZWRfZGF0ZSI6bnVsbCwicGl2b3QiOnsiYWRtaW5faWQiOjIsInRlbmFudF9pZCI6MSwiY3JlYXRlZF9kYXRlIjpudWxsLCJ1cGRhdGVkX2RhdGUiOm51bGx9fV0sImNhc2JpbiI6W119fQ.IgLkKhjjsgjAsjS4qReg51WpzwjiuU27_tbqTpUuD3M","client_id": "127.0.0.1-690c430548ecd5.55813566","expires_time": 1762418469}}')]
public function thirdPartyLogin(Request $request): \support\Response
{
try {
$acctId = $request->get('acct_id');
$appId = $request->get('app_id');
$appSecret = $request->get('app_secret');
$userName = $request->get('user_name');
$data = $this->service->thirdPartyLogin($acctId, $appId, $appSecret, $userName);
return Json::success('登录成功', $data);
} catch (\Throwable $e) {
return Json::fail($e->getMessage());
}
}
}