remove Traits

This commit is contained in:
Jethro Lin 2024-12-04 16:41:43 +08:00
parent 27cad57159
commit 1ab5da5576
14 changed files with 362 additions and 234 deletions

View file

@ -8,7 +8,6 @@
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Services\LogService;
use App\Traits\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@ -18,26 +17,33 @@
class AuthController extends Controller
{
use ApiResponse;
/**
* @var LogService
*/
private readonly LogService $logService;
public function __construct(
private readonly LogService $logService,
) {}
public function __construct(LogService $logService)
{
$this->logService = $logService;
}
/**
* 管理员登录
*
* @param Request $request
* @return JsonResponse
* @throws ValidationException
*/
public function login(Request $request): JsonResponse
{
try {
/** @var array{email: string, password: string} $validated */
$validated = $request->validate([
'email' => 'required|email',
'password' => 'required|string',
]);
/** @var Admin|null $admin */
$admin = Admin::where('email', $validated['email'])->first();
if (!$admin || !Hash::check($validated['password'], $admin->password)) {
@ -47,6 +53,7 @@ public function login(Request $request): JsonResponse
);
}
/** @var string $token */
$token = $admin->createToken('admin-token')->plainTextToken;
$this->logService->logOperation(
@ -59,7 +66,6 @@ public function login(Request $request): JsonResponse
'token' => $token,
'admin' => [
'id' => $admin->id,
'name' => $admin->name,
'email' => $admin->email,
],
]);
@ -92,7 +98,16 @@ public function login(Request $request): JsonResponse
public function logout(Request $request): JsonResponse
{
try {
/** @var Admin|null $admin */
$admin = $request->user();
if (!$admin) {
return $this->error(
ErrorCode::UNAUTHORIZED,
'未登錄或會話已過期。'
);
}
$admin->currentAccessToken()->delete();
$this->logService->logOperation(
@ -122,17 +137,27 @@ public function logout(Request $request): JsonResponse
*
* @param Request $request
* @return JsonResponse
* @throws ValidationException
*/
public function changePassword(Request $request): JsonResponse
{
try {
/** @var Admin|null $admin */
$admin = $request->user();
if (!$admin) {
return $this->error(
ErrorCode::UNAUTHORIZED,
'未登錄或會話已過期。'
);
}
/** @var array{current_password: string, new_password: string} $validated */
$validated = $request->validate([
'current_password' => 'required|string',
'new_password' => 'required|string|min:8|confirmed',
]);
$admin = $request->user();
if (!Hash::check($validated['current_password'], $admin->password)) {
return $this->error(
ErrorCode::INVALID_CREDENTIALS,

View file

@ -8,28 +8,31 @@
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\Client;
use App\Models\LlmProvider;
use App\Services\Auth\TokenService;
use App\Services\LogService;
use App\Traits\ApiResponse;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Validation\ValidationException;
class ClientController extends Controller
{
use ApiResponse;
private Admin $admin;
/**
* @var TokenService
*/
private readonly TokenService $tokenService;
public function __construct(
private readonly TokenService $tokenService,
private readonly LogService $logService,
Request $request
) {
$this->admin = $request->admin;
$admin = $request->admin;
if (!$admin) {
throw new \RuntimeException('管理員信息未找到。');
}
$this->admin = $admin;
}
/**
@ -38,12 +41,21 @@ public function __construct(
public function index(): JsonResponse
{
try {
$clients = Client::select([
$query = Client::select([
'id',
'name',
'llm_provider_id',
'created_at',
])->get();
]);
// 如果不是超級管理員,只能看到自己管理的客戶
if (!$this->admin->isSuperAdmin()) {
$query->whereHas('admins', function ($query) {
$query->where('admin_id', $this->admin->id);
});
}
$clients = $query->get();
return $this->success([
'items' => $clients->map(fn($client) => [
@ -87,12 +99,25 @@ public function store(Request $request): JsonResponse
],
]);
// 檢查是否有權限管理該提供商
if (!$this->admin->canManageLlmProvider($validated['llm_provider_id'])) {
return $this->error(
ErrorCode::FORBIDDEN,
'您無權使用該 LLM 提供商。'
);
}
$validated['status'] = Client::STATUS_ACTIVE;
$validated['rate_limit'] = config('llm.default_rate_limit', 60);
$validated['timeout'] = config('llm.default_timeout', 30);
$client = Client::create($validated);
// 如果不是超級管理員,需要建立關聯
if (!$this->admin->isSuperAdmin()) {
$client->admins()->attach($this->admin->id);
}
$this->logService->logOperation(
'admin',
$this->admin->id,

View file

@ -8,9 +8,6 @@
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\LlmProvider;
use App\Services\LogService;
use App\Traits\ApiResponse;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
@ -18,23 +15,29 @@
class LlmProviderController extends Controller
{
use ApiResponse;
/**
* @var Admin
*/
private readonly Admin $admin;
private Admin $admin;
public function __construct(
private readonly LogService $logService,
Request $request
) {
$this->admin = $request->admin;
public function __construct(Request $request)
{
$admin = $request->admin;
if (!$admin instanceof Admin) {
throw new \RuntimeException('管理員信息未找到。');
}
$this->admin = $admin;
}
/**
* 獲取 LLM 提供商列表
*
* @return JsonResponse
*/
public function index(): JsonResponse
{
try {
/** @var \Illuminate\Database\Eloquent\Builder $providers */
$providers = LlmProvider::select([
'id',
'name',
@ -42,9 +45,30 @@ public function index(): JsonResponse
'api_url',
'status',
'created_at',
])->get();
]);
return $this->success($providers);
// 如果不是超級管理員,只能看到自己管理的提供商
if (!$this->admin->isSuperAdmin()) {
$providers->whereHas('clients', function ($query) {
$query->whereHas('admins', function ($query) {
$query->where('admin_id', $this->admin->id);
});
});
}
/** @var \Illuminate\Database\Eloquent\Collection $providerList */
$providerList = $providers->get();
return response()->json([
'items' => $providerList->map(fn($provider) => [
'id' => $provider->id,
'name' => $provider->name,
'service_name' => $provider->service_name,
'api_url' => $provider->api_url,
'status' => $provider->status,
'created_at' => $provider->created_at->toIso8601String(),
])
]);
} catch (\Exception $e) {
Log::error('Error fetching LLM providers', [
@ -52,19 +76,37 @@ public function index(): JsonResponse
'trace' => $e->getTraceAsString(),
]);
return $this->error(
ErrorCode::SERVER_ERROR,
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
);
return response()->json([
'error' => ErrorCode::SERVER_ERROR,
'message' => ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
]);
}
}
/**
* 新增 LLM 提供商
*
* @param Request $request
* @return JsonResponse
* @throws ValidationException
*/
public function store(Request $request): JsonResponse
{
try {
// 只有超級管理員可以新增提供商
if (!$this->admin->isSuperAdmin()) {
return response()->json([
'error' => ErrorCode::FORBIDDEN,
'message' => '只有超級管理員可以新增 LLM 提供商。'
]);
}
/** @var array{
* name: string,
* service_name: string,
* api_url: string,
* api_token: string
* } $validated */
$validated = $request->validate([
'name' => [
'required',
@ -91,15 +133,11 @@ public function store(Request $request): JsonResponse
]);
$validated['status'] = LlmProvider::STATUS_ACTIVE;
/** @var LlmProvider $provider */
$provider = LlmProvider::create($validated);
$this->logService->logOperation(
'admin',
$this->admin->id,
"Created LLM provider: {$provider->name}"
);
return $this->created([
return response()->json([
'id' => $provider->id,
'name' => $provider->name,
'service_name' => $provider->service_name,
@ -109,11 +147,11 @@ public function store(Request $request): JsonResponse
]);
} catch (ValidationException $e) {
return $this->error(
ErrorCode::VALIDATION_ERROR,
ErrorCode::getMessage(ErrorCode::VALIDATION_ERROR),
$e->errors()
);
return response()->json([
'error' => ErrorCode::VALIDATION_ERROR,
'message' => ErrorCode::getMessage(ErrorCode::VALIDATION_ERROR),
'errors' => $e->errors()
]);
} catch (\Exception $e) {
Log::error('Error creating LLM provider', [
'error' => $e->getMessage(),
@ -121,10 +159,10 @@ public function store(Request $request): JsonResponse
'request_data' => $request->except(['api_token']),
]);
return $this->error(
ErrorCode::SERVER_ERROR,
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
);
return response()->json([
'error' => ErrorCode::SERVER_ERROR,
'message' => ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
]);
}
}
@ -137,10 +175,10 @@ public function update(Request $request, int $id): JsonResponse
$provider = LlmProvider::findOrFail($id);
if (!$this->admin->canManageLlmProvider($provider->id)) {
return $this->error(
ErrorCode::FORBIDDEN,
'您無權管理該 LLM 提供商。'
);
return response()->json([
'error' => ErrorCode::FORBIDDEN,
'message' => '您無權管理該 LLM 提供商。'
]);
}
$validated = $request->validate([
@ -177,22 +215,16 @@ public function update(Request $request, int $id): JsonResponse
if ($validated['status'] === LlmProvider::STATUS_INACTIVE &&
$provider->status === LlmProvider::STATUS_ACTIVE) {
if ($provider->clients()->exists()) {
return $this->error(
ErrorCode::RESOURCE_IN_USE,
'該提供商正在被客戶使用,無法停用。'
);
return response()->json([
'error' => ErrorCode::RESOURCE_IN_USE,
'message' => '該提供商正在被客戶使用,無法停用。'
]);
}
}
$provider->update($validated);
$this->logService->logOperation(
'admin',
$this->admin->id,
"Updated LLM provider: {$provider->name}"
);
return $this->success([
return response()->json([
'id' => $provider->id,
'name' => $provider->name,
'service_name' => $provider->service_name,
@ -202,16 +234,11 @@ public function update(Request $request, int $id): JsonResponse
]);
} catch (ValidationException $e) {
return $this->error(
ErrorCode::VALIDATION_ERROR,
ErrorCode::getMessage(ErrorCode::VALIDATION_ERROR),
$e->errors()
);
} catch (ModelNotFoundException $e) {
return $this->error(
ErrorCode::RESOURCE_NOT_FOUND,
ErrorCode::getMessage(ErrorCode::RESOURCE_NOT_FOUND)
);
return response()->json([
'error' => ErrorCode::VALIDATION_ERROR,
'message' => ErrorCode::getMessage(ErrorCode::VALIDATION_ERROR),
'errors' => $e->errors()
]);
} catch (\Exception $e) {
Log::error('Error updating LLM provider', [
'error' => $e->getMessage(),
@ -220,10 +247,10 @@ public function update(Request $request, int $id): JsonResponse
'request_data' => $request->except(['api_token']),
]);
return $this->error(
ErrorCode::SERVER_ERROR,
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
);
return response()->json([
'error' => ErrorCode::SERVER_ERROR,
'message' => ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
]);
}
}
@ -236,36 +263,27 @@ public function destroy(int $id): JsonResponse
$provider = LlmProvider::findOrFail($id);
if (!$this->admin->canManageLlmProvider($provider->id)) {
return $this->error(
ErrorCode::FORBIDDEN,
'您無權管理該 LLM 提供商。'
);
return response()->json([
'error' => ErrorCode::FORBIDDEN,
'message' => '您無權管理該 LLM 提供商。'
]);
}
// 檢查是否有客戶在使用
if ($provider->clients()->exists()) {
return $this->error(
ErrorCode::RESOURCE_IN_USE,
'該提供商正在被客戶使用,無法刪除。'
);
return response()->json([
'error' => ErrorCode::RESOURCE_IN_USE,
'message' => '該提供商正在被客戶使用,無法刪除。'
]);
}
$providerName = $provider->name;
$provider->delete();
$this->logService->logOperation(
'admin',
$this->admin->id,
"Deleted LLM provider: {$providerName}"
);
return response()->json([
'message' => 'LLM 提供商已刪除。'
]);
return $this->success(null, 'LLM 提供商已刪除。');
} catch (ModelNotFoundException $e) {
return $this->error(
ErrorCode::RESOURCE_NOT_FOUND,
ErrorCode::getMessage(ErrorCode::RESOURCE_NOT_FOUND)
);
} catch (\Exception $e) {
Log::error('Error deleting LLM provider', [
'error' => $e->getMessage(),
@ -273,10 +291,10 @@ public function destroy(int $id): JsonResponse
'provider_id' => $id,
]);
return $this->error(
ErrorCode::SERVER_ERROR,
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
);
return response()->json([
'error' => ErrorCode::SERVER_ERROR,
'message' => ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
]);
}
}
}

View file

@ -7,7 +7,6 @@
use App\Constants\ErrorCode;
use App\Http\Controllers\Controller;
use App\Services\Auth\TokenService;
use App\Traits\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
@ -15,25 +14,35 @@
class AuthController extends Controller
{
use ApiResponse;
/**
* @var TokenService
*/
private readonly TokenService $tokenService;
public function __construct(
private readonly TokenService $tokenService,
) {}
public function __construct(TokenService $tokenService)
{
$this->tokenService = $tokenService;
}
/**
* 獲取訪問令牌
*
* @param Request $request
* @return JsonResponse
* @throws ValidationException
*/
public function getAccessToken(Request $request): JsonResponse
{
try {
/** @var array{auth_token: string} $validated */
$validated = $request->validate([
'auth_token' => 'required|string|size:64',
]);
/** @var array{client_id: int, expires_at: string}|null $authTokenData */
$authTokenData = $this->tokenService->validateAuthToken($validated['auth_token']);
if (!$authTokenData) {
if (!$authTokenData || !isset($authTokenData['expires_at'])) {
return $this->error(
ErrorCode::TOKEN_INVALID,
'認證令牌無效。'
@ -47,9 +56,17 @@ public function getAccessToken(Request $request): JsonResponse
);
}
/** @var array{access_token: string, expires_in: int} $result */
$result = $this->tokenService->generateAccessToken($authTokenData);
return $this->success($result);
if (!isset($result['access_token']) || !isset($result['expires_in'])) {
throw new \RuntimeException('生成訪問令牌失敗。');
}
return $this->success([
'access_token' => $result['access_token'],
'expires_in' => $result['expires_in'],
]);
} catch (ValidationException $e) {
return $this->error(

View file

@ -7,7 +7,6 @@
use App\Constants\ErrorCode;
use App\Http\Controllers\Controller;
use App\Services\LlmService;
use App\Traits\ApiResponse;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
@ -15,18 +14,38 @@
class LlmController extends Controller
{
use ApiResponse;
/**
* @var LlmService
*/
private readonly LlmService $llmService;
public function __construct(
private readonly LlmService $llmService,
) {}
public function __construct(LlmService $llmService)
{
$this->llmService = $llmService;
}
/**
* 發送 LLM 請求
*
* @param Request $request
* @return JsonResponse
* @throws ValidationException|\RuntimeException
*/
public function request(Request $request): JsonResponse
{
try {
if (!$request->client) {
throw new \RuntimeException('客戶信息未找到。');
}
/** @var array{
* prompt: string,
* max_tokens?: int,
* temperature?: float,
* top_p?: float,
* frequency_penalty?: float,
* presence_penalty?: float
* } $validated */
$validated = $request->validate([
'prompt' => 'required|string|max:4000',
'max_tokens' => 'nullable|integer|min:1|max:4000',
@ -36,12 +55,17 @@ public function request(Request $request): JsonResponse
'presence_penalty' => 'nullable|numeric|min:-2|max:2',
]);
/** @var array{response: string} $result */
$result = $this->llmService->sendRequest(
$request->client,
$validated['prompt'],
array_filter($validated, fn($key) => $key !== 'prompt', ARRAY_FILTER_USE_KEY)
);
if (!isset($result['response'])) {
throw new \RuntimeException('LLM 提供商返回的響應格式無效。');
}
return $this->success([
'response' => $result['response'],
]);

View file

@ -1,8 +1,103 @@
<?php
declare(strict_types=1);
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
abstract class Controller
{
//
/**
* 成功响应
*
* @param mixed $data 响应数据
* @param string|null $message 成功消息
* @param int $code HTTP状态码
*/
protected function success(mixed $data = null, ?string $message = null, int $code = Response::HTTP_OK): JsonResponse
{
$response = [
'success' => true,
];
if ($data !== null) {
$response['data'] = $data;
}
if ($message !== null) {
$response['message'] = $message;
}
return response()->json($response, $code);
}
/**
* 错误响应
*
* @param string $error 错误代码
* @param string $message 错误消息
* @param mixed $errors 详细错误信息
* @param int $code HTTP状态码
*/
protected function error(
string $error,
string $message,
mixed $errors = null,
int $code = Response::HTTP_BAD_REQUEST
): JsonResponse {
$response = [
'success' => false,
'error' => $error,
'message' => $message,
];
if ($errors !== null) {
$response['errors'] = $errors;
}
return response()->json($response, $code);
}
/**
* 分页响应
*
* @param array $items 分页数据
* @param array $meta 分页元数据
* @param string|null $message 成功消息
*/
protected function paginate(array $items, array $meta, ?string $message = null): JsonResponse
{
$response = [
'success' => true,
'data' => $items,
'meta' => $meta,
];
if ($message !== null) {
$response['message'] = $message;
}
return response()->json($response);
}
/**
* 创建成功响应
*
* @param mixed $data 创建的资源数据
* @param string|null $message 成功消息
*/
protected function created(mixed $data, ?string $message = null): JsonResponse
{
return $this->success($data, $message, Response::HTTP_CREATED);
}
/**
* 无内容响应
*/
protected function noContent(): JsonResponse
{
return response()->json(null, Response::HTTP_NO_CONTENT);
}
}

View file

@ -40,10 +40,13 @@ class Admin extends Authenticatable
'role' => 'string',
];
/**
* @return BelongsToMany<Client>
*/
public function clients(): BelongsToMany
{
return $this->belongsToMany(Client::class, 'admin_client')
->withTimestamp('assigned_at');
->withTimestamps();
}
/**
@ -72,6 +75,8 @@ public function isValidAdmin(): bool
/**
* 检查是否可以管理指定的客户
*
* @param int $clientId
*/
public function canManageClient(int $clientId): bool
{
@ -84,6 +89,8 @@ public function canManageClient(int $clientId): bool
/**
* 检查是否可以管理指定的LLM提供商
*
* @param int $providerId
*/
public function canManageLlmProvider(int $providerId): bool
{

View file

@ -40,11 +40,17 @@ class Client extends Model
'status' => 'string',
];
/**
* @return BelongsTo<LlmProvider>
*/
public function llmProvider(): BelongsTo
{
return $this->belongsTo(LlmProvider::class);
}
/**
* @return BelongsToMany<Admin>
*/
public function admins(): BelongsToMany
{
return $this->belongsToMany(Admin::class, 'admin_client')
@ -64,6 +70,8 @@ public function isActive(): bool
*/
public function canSendLlmRequest(): bool
{
return $this->isActive() && $this->llmProvider->isActive();
/** @var LlmProvider|null $provider */
$provider = $this->llmProvider;
return $this->isActive() && $provider && $provider->isActive();
}
}

View file

@ -41,6 +41,9 @@ class LlmProvider extends Model
'status' => 'string',
];
/**
* @return HasMany<Client>
*/
public function clients(): HasMany
{
return $this->hasMany(Client::class);

View file

@ -1,3 +1,4 @@
<?php
declare(strict_types=1);
namespace App\Providers;

View file

@ -13,16 +13,20 @@ class ThrottleAuthToken implements Rule
private const MAX_ATTEMPTS = 5; // 最大尝试次数
private const DECAY_MINUTES = 1; // 重置时间(分钟)
public function validate(string $attribute, mixed $value, Closure $fail): void
public function passes($attribute, $value): bool
{
$key = 'auth_token_' . $value;
if (RateLimiter::tooManyAttempts($key, self::MAX_ATTEMPTS)) {
$seconds = RateLimiter::availableIn($key);
$fail("请求过于频繁,请在 {$seconds} 秒后重试。");
return;
return false;
}
RateLimiter::hit($key, self::DECAY_MINUTES * 60);
return true;
}
public function message(): string
{
return '请求过于频繁,请稍后重试。';
}
}

View file

@ -65,21 +65,26 @@ public function generateAuthToken(Client $client, ?int $expiresInHours = null):
/**
* 驗證認證令牌
*
* @param string $token
* @return array{client_id: int, token: string, created_at: string, expires_at: string}|null
*/
public function validateAuthToken(string $token): ?array
{
// 檢查黑名單
if (Redis::exists(self::BLACKLIST_PREFIX . $token)) {
/** @var bool $isBlacklisted */
$isBlacklisted = Redis::exists(self::BLACKLIST_PREFIX . $token);
if ($isBlacklisted) {
return null;
}
// 檢查令牌是否存在且有效
/** @var string|null $tokenData */
$tokenData = Redis::get(self::AUTH_TOKEN_PREFIX . $token);
if (!$tokenData) {
return null;
}
/** @var array{client_id: int, token: string, created_at: string, expires_at: string}|null $data */
$data = json_decode($tokenData, true);
if (!$data || now()->isAfter($data['expires_at'])) {
Redis::del(self::AUTH_TOKEN_PREFIX . $token);
@ -125,16 +130,20 @@ public function generateAccessToken(array $authTokenData): array
/**
* 驗證訪問令牌
*
* @param string $token
* @return array{client_id: int, auth_token: string, created_at: string}|null
*/
public function validateAccessToken(string $token): ?array
{
/** @var string|null $tokenData */
$tokenData = Redis::get(self::ACCESS_TOKEN_PREFIX . $token);
if (!$tokenData) {
return null;
}
return json_decode($tokenData, true);
/** @var array{client_id: int, auth_token: string, created_at: string}|null $data */
$data = json_decode($tokenData, true);
return $data;
}
/**

View file

@ -32,6 +32,7 @@ public function sendRequest(Client $client, string $prompt, array $options = [])
}
try {
/** @var \App\Models\LlmProvider $provider */
$provider = $client->llmProvider;
if ($provider->status !== 'active') {

View file

@ -1,109 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Traits;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
trait ApiResponse
{
/**
* 成功响应
*
* @param mixed $data 响应数据
* @param string|null $message 成功消息
* @param int $code HTTP状态码
* @return JsonResponse
*/
protected function success(mixed $data = null, ?string $message = null, int $code = Response::HTTP_OK): JsonResponse
{
$response = [
'success' => true,
];
if ($data !== null) {
$response['data'] = $data;
}
if ($message !== null) {
$response['message'] = $message;
}
return response()->json($response, $code);
}
/**
* 错误响应
*
* @param string $error 错误代码
* @param string $message 错误消息
* @param mixed $errors 详细错误信息
* @param int $code HTTP状态码
* @return JsonResponse
*/
protected function error(
string $error,
string $message,
mixed $errors = null,
int $code = Response::HTTP_BAD_REQUEST
): JsonResponse {
$response = [
'success' => false,
'error' => $error,
'message' => $message,
];
if ($errors !== null) {
$response['errors'] = $errors;
}
return response()->json($response, $code);
}
/**
* 分页响应
*
* @param array $items 分页数据
* @param array $meta 分页元数据
* @param string|null $message 成功消息
* @return JsonResponse
*/
protected function paginate(array $items, array $meta, ?string $message = null): JsonResponse
{
$response = [
'success' => true,
'data' => $items,
'meta' => $meta,
];
if ($message !== null) {
$response['message'] = $message;
}
return response()->json($response);
}
/**
* 创建成功响应
*
* @param mixed $data 创建的资源数据
* @param string|null $message 成功消息
* @return JsonResponse
*/
protected function created(mixed $data, ?string $message = null): JsonResponse
{
return $this->success($data, $message, Response::HTTP_CREATED);
}
/**
* 无内容响应
*
* @return JsonResponse
*/
protected function noContent(): JsonResponse
{
return response()->json(null, Response::HTTP_NO_CONTENT);
}
}