更新后端基础

This commit is contained in:
技术老胡
2026-02-23 11:33:27 +08:00
parent 4a34feec54
commit d29751cce8
75 changed files with 2978 additions and 1489 deletions

View File

@@ -3,46 +3,63 @@
namespace app\http\admin\controller;
use app\common\base\BaseController;
use app\services\AuthService;
use app\services\CaptchaService;
use support\Request;
use support\Response;
use app\services\auth\AuthService;
/**
* 认证控制器
*
* 处理登录、验证码等认证相关接口
*/
class AuthController extends BaseController
{
public function __construct(
protected CaptchaService $captchaService,
protected AuthService $authService
) {}
) {
}
/**
* 管理后台登录
* GET /captcha
*
* 生成验证码
*/
public function login(Request $request): Response
public function captcha(Request $request)
{
$username = (string)$request->post('username', '');
$password = (string)$request->post('password', '');
// 前端有本地验证码,这里暂不做服务端校验,仅预留字段
$verifyCode = $request->post('verifyCode');
try {
$data = $this->captchaService->generate();
return $this->success($data);
} catch (\Throwable $e) {
return $this->fail('验证码生成失败:' . $e->getMessage(), 500);
}
}
if ($username === '' || $password === '') {
return $this->fail('账号或密码不能为空', 400);
/**
* POST /login
*
* 用户登录
*/
public function login(Request $request)
{
$username = $request->post('username', '');
$password = $request->post('password', '');
$verifyCode = $request->post('verifyCode', '');
$captchaId = $request->post('captchaId', '');
// 参数校验
if (empty($username) || empty($password) || empty($verifyCode) || empty($captchaId)) {
return $this->fail('请填写完整登录信息', 400);
}
$token = $this->authService->login($username, $password, $verifyCode);
return $this->success(['token' => $token]);
}
/**
* 获取当前登录用户信息
*/
public function getUserInfo(Request $request): Response
{
// 前端在 Authorization 中直接传 token
$token = (string)$request->header('authorization', '');
$id = $request->get('id');
$data = $this->authService->getUserInfo($token, $id);
return $this->success($data);
try {
$data = $this->authService->login($username, $password, $verifyCode, $captchaId);
return $this->success($data);
} catch (\RuntimeException $e) {
return $this->fail($e->getMessage(), $e->getCode() ?: 500);
} catch (\Throwable $e) {
return $this->fail('登录失败:' . $e->getMessage(), 500);
}
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace app\http\admin\controller;
use app\common\base\BaseController;
use support\Request;
/**
* 菜单控制器
*/
class MenuController extends BaseController
{
public function getRouters()
{
// 获取菜单数据并转换为树形结构
$routers = $this->buildMenuTree($this->getSystemMenu());
return $this->success($routers);
}
/**
* 获取系统菜单数据
* 从配置文件读取
*/
private function getSystemMenu(): array
{
return config('menu', []);
}
/**
* 构建菜单树形结构
*/
private function buildMenuTree(array $menus, string $parentId = '0'): array
{
$tree = [];
foreach ($menus as $menu) {
if (($menu['parentId'] ?? '0') === $parentId) {
$children = $this->buildMenuTree($menus, $menu['id']);
$menu['children'] = !empty($children) ? $children : null;
$tree[] = $menu;
}
}
// 按 sort 排序
usort($tree, function ($a, $b) {
return ($a['meta']['sort'] ?? 0) <=> ($b['meta']['sort'] ?? 0);
});
return $tree;
}
}

View File

@@ -0,0 +1,46 @@
<?php
namespace app\http\admin\controller;
use app\common\base\BaseController;
use support\Request;
/**
* 系统控制器
*/
class SystemController extends BaseController
{
/**
* GET /system/getDict
* GET /system/getDict/{code}
*
* 获取字典数据
* 支持通过路由参数 code 查询指定字典,不传则返回所有字典
*
* 示例:
* GET /adminapi/system/getDict - 返回所有字典
* GET /adminapi/system/getDict/gender - 返回性别字典
* GET /adminapi/system/getDict/status - 返回状态字典
*/
public function getDict(Request $request, string $code = '')
{
// 获取所有字典数据
$allDicts = config('dict', []);
// 如果指定了 code则只返回对应的字典
if (!empty($code)) {
// 将数组转换为以 code 为键的关联数组,便于快速查找
$dictsByCode = array_column($allDicts, null, 'code');
$dict = $dictsByCode[$code] ?? null;
if ($dict === null) {
return $this->fail('未找到指定的字典:' . $code, 404);
}
return $this->success($dict);
}
// 返回所有字典
return $this->success($allDicts);
}
}

View File

@@ -0,0 +1,44 @@
<?php
namespace app\http\admin\controller;
use app\common\base\BaseController;
use app\services\UserService;
use support\Request;
/**
* 用户接口示例控制器
*
* 主要用于演示 BaseController / Service / Repository / Model 的调用链路。
*/
class UserController extends BaseController
{
public function __construct(
protected UserService $userService
) {
}
/**
* GET /user/getUserInfo
*
* 从 JWT token 中获取当前登录用户信息
* 前端通过 Authorization: Bearer {token} 请求头传递 token
*/
public function getUserInfo(Request $request)
{
// 从JWT中间件注入的用户信息中获取用户ID
$userId = $this->currentUserId($request);
if ($userId <= 0) {
return $this->fail('未获取到用户信息,请先登录', 401);
}
try {
$data = $this->userService->getUserInfoById($userId);
return $this->success($data);
} catch (\RuntimeException $e) {
return $this->fail($e->getMessage(), $e->getCode() ?: 500);
}
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace app\http\admin\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
use app\common\utils\JwtUtil;
/**
* JWT 认证中间件
*
* 验证请求中的 JWT token并将用户信息注入到请求对象中
*/
class AuthMiddleware implements MiddlewareInterface
{
/**
* 处理请求
* @param Request $request 请求对象
* @param callable $handler 下一个中间件处理函数
* @return Response 响应对象
*/
public function process(Request $request, callable $handler): Response
{
// 从请求头中获取 token
$auth = $request->header('Authorization', '');
if (!$auth) {
return $this->unauthorized('缺少认证令牌');
}
// 兼容 "Bearer xxx" 或直接 "xxx"
if (str_starts_with($auth, 'Bearer ')) {
$token = substr($auth, 7);
} else {
$token = $auth;
}
if (!$token) {
return $this->unauthorized('认证令牌格式错误');
}
try {
// 解析 JWT token
$payload = JwtUtil::parseToken($token);
if (empty($payload) || !isset($payload['user_id'])) {
return $this->unauthorized('认证令牌无效');
}
// 将用户信息存储到请求对象中,供控制器使用
$request->user = $payload;
$request->userId = (int) ($payload['user_id'] ?? 0);
// 继续处理请求
return $handler($request);
} catch (\Throwable $e) {
// 根据异常类型返回不同的错误信息
$message = $e->getMessage();
if (str_contains($message, 'expired') || str_contains($message, 'Expired')) {
return $this->unauthorized('认证令牌已过期', 401);
} elseif (str_contains($message, 'signature') || str_contains($message, 'Signature')) {
return $this->unauthorized('认证令牌签名无效', 401);
} else {
return $this->unauthorized('认证令牌验证失败:' . $message, 401);
}
}
}
/**
* 返回未授权响应
*/
private function unauthorized(string $message, int $code = 401): Response
{
return json([
'code' => $code,
'message' => $message,
'data' => null,
], $code);
}
}