docs(zh-CN): sync Chinese docs with latest upstream changes

This commit is contained in:
neo
2026-03-21 12:55:58 +08:00
parent 0af0fbf40b
commit e73c2ffa34
85 changed files with 11028 additions and 747 deletions

View File

@@ -0,0 +1,311 @@
# Laravel API — 项目 CLAUDE.md
> 使用 PostgreSQL、Redis 和队列的 Laravel API 真实案例。
> 复制此文件到你的项目根目录,并根据你的服务进行自定义。
## 项目概述
**技术栈:** PHP 8.2+, Laravel 11.x, PostgreSQL, Redis, Horizon, PHPUnit/Pest, Docker Compose
**架构:** 采用控制器 -> 服务 -> 操作的模块化 Laravel 应用,使用 Eloquent ORM、异步工作队列、表单请求进行验证以及 API 资源确保一致的 JSON 响应。
## 关键规则
### PHP 约定
* 所有 PHP 文件中使用 `declare(strict_types=1)`
* 处处使用类型属性和返回类型
* 服务和操作优先使用 `final`
* 提交的代码中不允许出现 `dd()``dump()`
* 通过 Laravel Pint 进行格式化 (PSR-12)
### API 响应封装
所有 API 响应使用一致的封装格式:
```json
{
"success": true,
"data": {"...": "..."},
"error": null,
"meta": {"page": 1, "per_page": 25, "total": 120}
}
```
### 数据库
* 迁移文件提交到 git
* 使用 Eloquent 或查询构造器(除非参数化,否则不使用原始 SQL
*`where``orderBy` 中使用的任何列建立索引
* 避免在服务中修改模型实例;优先通过存储库或查询构造器进行创建/更新
### 认证
* 通过 Sanctum 进行 API 认证
* 使用策略进行模型级授权
* 在控制器和服务中强制执行认证
### 验证
* 使用表单请求进行验证
* 将输入转换为 DTO 以供业务逻辑使用
* 切勿信任请求负载中的派生字段
### 错误处理
* 在服务中抛出领域异常
*`bootstrap/app.php` 中通过 `withExceptions` 将异常映射到 HTTP 响应
* 绝不向客户端暴露内部错误
### 代码风格
* 代码或注释中不使用表情符号
* 最大行长度120 个字符
* 控制器保持精简;服务和操作承载业务逻辑
## 文件结构
```
app/
Actions/
Console/
Events/
Exceptions/
Http/
Controllers/
Middleware/
Requests/
Resources/
Jobs/
Models/
Policies/
Providers/
Services/
Support/
config/
database/
factories/
migrations/
seeders/
routes/
api.php
web.php
```
## 关键模式
### 服务层
```php
<?php
declare(strict_types=1);
final class CreateOrderAction
{
public function __construct(private OrderRepository $orders) {}
public function handle(CreateOrderData $data): Order
{
return $this->orders->create($data);
}
}
final class OrderService
{
public function __construct(private CreateOrderAction $createOrder) {}
public function placeOrder(CreateOrderData $data): Order
{
return $this->createOrder->handle($data);
}
}
```
### 控制器模式
```php
<?php
declare(strict_types=1);
final class OrdersController extends Controller
{
public function __construct(private OrderService $service) {}
public function store(StoreOrderRequest $request): JsonResponse
{
$order = $this->service->placeOrder($request->toDto());
return response()->json([
'success' => true,
'data' => OrderResource::make($order),
'error' => null,
'meta' => null,
], 201);
}
}
```
### 策略模式
```php
<?php
declare(strict_types=1);
use App\Models\Order;
use App\Models\User;
final class OrderPolicy
{
public function view(User $user, Order $order): bool
{
return $order->user_id === $user->id;
}
}
```
### 表单请求 + DTO
```php
<?php
declare(strict_types=1);
final class StoreOrderRequest extends FormRequest
{
public function authorize(): bool
{
return (bool) $this->user();
}
public function rules(): array
{
return [
'items' => ['required', 'array', 'min:1'],
'items.*.sku' => ['required', 'string'],
'items.*.quantity' => ['required', 'integer', 'min:1'],
];
}
public function toDto(): CreateOrderData
{
return new CreateOrderData(
userId: (int) $this->user()->id,
items: $this->validated('items'),
);
}
}
```
### API 资源
```php
<?php
declare(strict_types=1);
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
final class OrderResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'id' => $this->id,
'status' => $this->status,
'total' => $this->total,
'created_at' => $this->created_at?->toIso8601String(),
];
}
}
```
### 队列任务
```php
<?php
declare(strict_types=1);
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Repositories\OrderRepository;
use App\Services\OrderMailer;
final class SendOrderConfirmation implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct(private int $orderId) {}
public function handle(OrderRepository $orders, OrderMailer $mailer): void
{
$order = $orders->findOrFail($this->orderId);
$mailer->sendOrderConfirmation($order);
}
}
```
### 测试模式 (Pest)
```php
<?php
declare(strict_types=1);
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use function Pest\Laravel\actingAs;
use function Pest\Laravel\assertDatabaseHas;
use function Pest\Laravel\postJson;
uses(RefreshDatabase::class);
test('user can place order', function () {
$user = User::factory()->create();
actingAs($user);
$response = postJson('/api/orders', [
'items' => [['sku' => 'sku-1', 'quantity' => 2]],
]);
$response->assertCreated();
assertDatabaseHas('orders', ['user_id' => $user->id]);
});
```
### 测试模式 (PHPUnit)
```php
<?php
declare(strict_types=1);
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
final class OrdersControllerTest extends TestCase
{
use RefreshDatabase;
public function test_user_can_place_order(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)->postJson('/api/orders', [
'items' => [['sku' => 'sku-1', 'quantity' => 2]],
]);
$response->assertCreated();
$this->assertDatabaseHas('orders', ['user_id' => $user->id]);
}
}
```