This commit is contained in:
Yangxt
2026-09-30 11:11:54 +08:00
parent cb9b7a4bd4
commit 2fb57ec490
160 changed files with 14764 additions and 53 deletions
+31
View File
@@ -0,0 +1,31 @@
---
description: REST API 路由与统一 JSON 返回格式
globs: backend/routes/**/*.php,backend/app/Http/**/*.php
alwaysApply: false
---
# API 规范
详见 `docs/07-api.md`。
## 路由
- 统一前缀 `/api/v1/`,资源名复数,路由参数优先业务编码。
- 状态动作:`POST /api/v1/{resource}/{code}/{action}`
```
POST /api/v1/inbounds/{inbound_no}/receive
POST /api/v1/outbounds/{outbound_no}/allocate
```
- 认证 Sanctum;每个接口都要经过 Policy / Gate 授权,权限码形如 `outbound:allocate`。
- 列表接口必须分页并使用统一分页格式。
## 统一返回
所有接口通过统一响应工具(如 `App\Support\ApiResponse`)返回,禁止 Controller 自定义结构。
```json
{ "success": true, "message": "OK", "data": {} }
{ "success": false, "message": "操作失败", "errors": {} }
```
- 输出数据统一经 API Resource 转换,不直接返回 Model。
- 验证错误、业务异常、授权失败在异常处理器中统一转换为失败格式。
+26
View File
@@ -0,0 +1,26 @@
---
description: MySQL 表设计、迁移与索引规范
globs: backend/database/**/*.php,backend/app/Models/**/*.php,docs/database/**
alwaysApply: false
---
# 数据库规范
详见 `docs/09-database.md`。表结构设计存放在 `docs/database/`(索引 `docs/database/README.md`)。
- **设计先行**:先在 `docs/database/` 中设计并确认表结构,再写迁移;迁移与设计文档必须保持一致,改表时同步更新文档。
- InnoDB + utf8mb4;所有表有 `created_at`、`updated_at`。
- 核心业务表必须有 `tenant_code`;业务编码唯一约束包含租户:`UNIQUE (tenant_code, sku_code)`。
- 重要业务表按需加 `status`、`version`(乐观锁)、`deleted_at`;**不要无脑给所有表加软删除**,流水类事实表禁止软删除与更新。
- 数量字段使用整数或 `DECIMAL`,禁止 `FLOAT/DOUBLE`;金额使用 `DECIMAL`。
- 索引围绕实际查询设计,复合索引通常以 `tenant_code` 为最左列;关注 `warehouse_code`、`customer_code`、`sku_code`、`order_no`、`inbound_no`、`outbound_no`、`location_code`、`status`、`created_at`。
- 迁移必须可回滚(实现 `down()`),已上线的迁移不修改,新增迁移变更。
- 表名是单数加 `wms_` 前缀(如 `wms_sku`、`wms_stock_log`)。每张表一个 SQL 文件,放在 `docs/database/` 对应领域目录,约定见 `docs/database/00-conventions.md`。
```php
// ✅
$table->string('tenant_code', 32);
$table->string('sku_code', 64);
$table->unique(['tenant_code', 'sku_code']);
$table->index(['tenant_code', 'status', 'created_at']);
```
+26
View File
@@ -0,0 +1,26 @@
---
description: 分阶段开发流程、修改前后检查清单与禁止事项
alwaysApply: true
---
# 开发工作方式
像资深项目开发人员一样工作,而不是代码生成器。详见 `docs/15-development-workflow.md`、`docs/16-roadmap.md`。
## 分阶段
- 严格按 Phase 1~17 顺序推进(`docs/16-roadmap.md`),不要一次生成整个项目或几百个文件。
- 每个 Phase 开始前先说明:目标、涉及模块、数据库变化、API 变化、前端变化、测试方案、风险;确认后再实施。
- 阶段完成后:检查代码 → 数据库 → 运行测试 → API → 前端 → 修复问题,再进入下一阶段。
## 修改前
理解目录 → 搜索已有实现(不重复造)→ 检查数据库结构、相关 API、前端调用 → 再修改。
## 修改后
检查类型、语法、引用、迁移、API、权限、**租户隔离**,并运行相关测试。
## 禁止
- 跳过架构设计;Controller 堆业务;一个 Model 承担所有业务
- 忽略 `tenant_code`;直接改库存不留流水;不考虑并发库存
- Python 直接修改核心 WMS 数据;平台逻辑写死进核心订单模型
- 过早微服务化;没有测试就继续堆功能;随意增加第三方依赖
- 重复实现 Element Plus / Admin 框架已提供的能力
- 魔法字符串、硬编码、重复 SQL、跨模块强耦合
+15
View File
@@ -0,0 +1,15 @@
---
description: Docker 开发环境规范
globs: docker-compose*.yml,docker/**,**/Dockerfile
alwaysApply: false
---
# Docker 开发环境
详见 `docs/13-docker.md`。
- 根目录**一个** `docker-compose.yml`,不为小模块建立独立 Compose。
- 服务:php-fpm(PHP 8.4)、nginx、mysql(8)、redis(7)、node(前端开发)、py-service(FastAPI,构建上下文 `py-service/`)。
- Queue Worker、Scheduler 为独立服务,复用 PHP 镜像。
- 镜像定义放 `docker/{service}/`;敏感配置经 `.env` 注入,不写入镜像与 Git。
- 数据卷持久化 MySQL / Redis;端口、密码可通过 `.env` 配置。
- 第一阶段不引入 Kubernetes,生产方案在 Phase 17 设计。
+27
View File
@@ -0,0 +1,27 @@
---
description: Vue 3 + TypeScript + Element Plus 前端规范(pure-admin-thin)
globs: frontend/**/*.vue,frontend/**/*.ts,frontend/**/*.tsx
alwaysApply: false
---
# 前端规范
详见 `docs/10-frontend.md`。
## 框架
- Admin 基于 pure-admin-thin(i18n 分支),复用其 Layout / Router / 权限 / 标签页 / i18n,不重复实现。
- UI 只用 Element Plus,不引入 Naive UI 等第二套组件库;不重复实现 Element Plus 已有组件。
- 新增依赖前先确认框架与现有依赖无法满足。
## 结构
- 页面按业务领域放 `src/views/{domain}/`(warehouse、inventory、inbound、outbound 等)。
- 接口调用统一放 `src/api/{domain}.ts`,通过框架封装的 HTTP 实例,页面内不直接写 axios。
- 类型放 `src/types/`,状态 / 枚举常量放 `src/constants/`,禁止魔法字符串。
## 编码
- `<script setup lang="ts">` + Composition API;Props、Emits、接口响应都要声明类型,避免 `any`。
- 列表页统一:查询区 → Toolbar → ElTable → ElPagination;表单用 ElForm + rules,弹窗用 ElDialog / ElDrawer。
- 文案走 i18n,不硬编码中文。
## 权限
- 按钮 / 菜单权限使用权限码 `模块:动作`(如 `inventory:adjust`、`outbound:ship`),通过框架的权限指令 / 组件控制。
- 前端权限只控制展示,最终以后端授权为准。
+38
View File
@@ -0,0 +1,38 @@
---
description: 库存模型、库存流水与并发安全规则(任何涉及库存数量变化的代码)
globs: backend/app/**/Inventory/**,backend/app/**/Inbound/**,backend/app/**/Outbound/**,backend/app/**/Picking/**,backend/app/**/Return/**
alwaysApply: false
---
# 库存规则
详见 `docs/05-inventory.md`。库存是整个 WMS 的核心。
## 模型
- 维度:仓库 → 库区 → 库位 → SKU → 批次 → 库存状态,不要简化为 `sku + warehouse + quantity`。
- 数量:`available_qty`、`reserved_qty`、`picked_qty`、`hold_qty`、`damaged_qty`、`in_transit_qty`。
## 流水
- 任何库存数量变化,必须在**同一事务**内写入 `wms_stock_log`(变化量、变化前后、业务单据类型与单号、操作人)。
- 库存变更只能通过库存领域服务 / Action 完成,业务模块不得直接 update 库存表。
- 流水只追加,不修改、不删除。
## 并发
禁止 "先 SELECT → PHP 计算 → UPDATE"。使用事务 + 原子条件更新 / 行锁 / 乐观锁,必要时 Redis 锁。
```php
// ❌ 并发下会超卖
$inv = Inventory::find($id);
if ($inv->available_qty >= $qty) { $inv->available_qty -= $qty; $inv->save(); }
// ✅ 原子条件更新,受影响行数为 0 即库存不足
$affected = Inventory::whereKey($id)
->where('available_qty', '>=', $qty)
->update([
'available_qty' => DB::raw('available_qty - '.(int) $qty),
'reserved_qty' => DB::raw('reserved_qty + '.(int) $qty),
]);
if ($affected === 0) { throw new InsufficientStockException(); }
```
- 多行扣减按固定顺序(如库存主键升序)加锁,避免死锁。
- 必须有并发测试:库存 10,两个请求同时扣 8,只能成功一个,且不会出现负库存。
+36
View File
@@ -0,0 +1,36 @@
---
description: Laravel 后端分层、目录结构与编码规范
globs: backend/**/*.php
alwaysApply: false
---
# Laravel 后端规范
详见 `docs/06-backend-laravel.md`。
## 分层
Controller → FormRequest → Application Action/Service → Domain → Model → DB
- Controller 只做:接收请求、调用 FormRequest 验证、调用 Action/Service、返回 API Resource。
- 复杂业务放 `app/Application/{Module}/Actions`,一个 Action 一个用例(如 `AllocateOutboundAction`)。
- 业务规则、状态机、Enum、值对象放 `app/Domain/{Module}`,不依赖 Http 与第三方平台。
- 第三方平台 / 物流商实现放 `app/Infrastructure/`;租户上下文、统一响应等放 `app/Support/`。
- 事务边界在 Application 层;跨模块协作走 Application Service 或 Events。
```php
// ❌ Controller 中直接写业务与查询
public function ship(string $outboundNo) {
$o = Outbound::where('outbound_no', $outboundNo)->first();
if ($o->status == 'packed') { $o->status = 'shipped'; $o->save(); }
}
// ✅
public function ship(ShipOutboundRequest $request, string $outboundNo, ShipOrderAction $action) {
return ApiResponse::success(new OutboundResource($action->execute($outboundNo, $request->toDto())));
}
```
## 编码
- `declare(strict_types=1);`,参数与返回值写明类型;DTO 在层间传递,不传裸数组。
- 状态、类型一律用 PHP Enum,非法状态转换抛业务异常。
- 授权用 Policies / Gates;认证用 Sanctum。
- 避免超大 Controller / Service;不重复 SQL,查询复用 Scope。
+30
View File
@@ -0,0 +1,30 @@
---
description: WMS SaaS 项目核心架构约束与文档索引
alwaysApply: true
---
# 项目核心约束
完整规范见 `docs/`(索引 `docs/README.md`)。涉及某领域的改动前,先阅读对应文档。
## 仓库结构(monorepo)
- `backend/`:Laravel 13 + PHP 8.4,唯一的核心业务系统
- `frontend/`:基于 pure-admin-thin(i18n 分支),Vue 3 + TS + Vite + Element Plus
- `py-service/`:Python FastAPI,独立 AI / 数据分析服务
- `docs/`:设计文档;`docs/database/` 存放数据库表结构设计
- 根目录一个 `docker-compose.yml`
## 不可违背的架构红线
1. **多租户**:单库 + `tenant_code`,通过 TenantContext 自动隔离;严禁跨租户读写。→ `docs/03-multi-tenancy.md`
2. **库存**:余额是结果,流水(`wms_stock_log`)是事实;任何库存变化必须同事务写流水,且并发安全。→ `docs/05-inventory.md`
3. **Laravel 是唯一核心业务系统**:Python 只做分析,只通过 `/internal/v1/` API 交互,不直接写核心库。→ `docs/11-py-service.md`
4. **平台解耦**:订单为统一模型,第三方平台走 Connector/Adapter,核心表不写死平台逻辑。→ `docs/04-domain-model.md`
5. **状态机**:状态用 Enum + 集中定义的合法转换,禁止散落的 `if ($status == ...)`。
6. **业务编码优先**:业务关联优先使用 `sku_code`、`outbound_no` 等,自增 ID 仅作内部主键。
7. **MySQL 是事实数据源**,Redis 只做 Cache / Queue / Lock / Rate Limit。
8. **不过度设计**:第一阶段不引入 K8s、Kafka、ES、MongoDB、ClickHouse、微服务。
## 架构问题处理
需求存在架构问题时不要直接照做,先说明:问题 → 原因 → 推荐方案 → 替代方案 → 最终建议。
存在多个影响全局的合理方案时,先说明区别,由用户决定。
整个项目遵循:**简单、清晰、可维护、可扩展、业务优先。**
+29
View File
@@ -0,0 +1,29 @@
---
description: Python FastAPI AI / 数据分析服务边界与规范
globs: py-service/**
alwaysApply: false
---
# Python Service(py-service)
详见 `docs/11-py-service.md`。
## 边界(红线)
- 只负责:AI 分析、库存分析、销量 / 缺货预测、补货建议、异常检测、AI 助手、机器学习。
- **不实现**用户、租户、SKU、仓库、库存、入库、出库等核心业务逻辑。
- **不直接连接或写入** Laravel 核心业务数据库;数据由 Laravel 推送,或通过 Laravel 只读接口获取。
- 分析结果返回给 Laravel,由 Laravel 决定是否落库或转化为业务动作。
## 接口
- 前缀 `/internal/v1/`,如 `POST /internal/v1/inventory/forecast`。
- 内部鉴权(Token / 签名),不对公网暴露。
- 请求必须带 `tenant_code`,缓存与结果按租户隔离。
## 结构与编码
```
py-service/app/
├── main.py api/ services/ schemas/ models/
├── analysis/ ai/ tasks/ core/
```
- 路由在 `api/`,业务在 `services/`,请求响应模型用 Pydantic 定义在 `schemas/`。
- 全部函数写类型注解;配置用 pydantic-settings 从环境变量读取,密钥不入库不入 Git。
- 重型依赖(PyTorch 等)按需引入,不提前加入。
+23
View File
@@ -0,0 +1,23 @@
---
description: Laravel Queue Job 与 Scheduler 规范
globs: backend/app/Jobs/**,backend/app/Console/**,backend/app/Listeners/**,backend/routes/console.php
alwaysApply: false
---
# Queue / Scheduler
详见 `docs/08-queue-scheduler.md`。
## 必须入队的任务
第三方订单 / 库存 / 物流 / Tracking 同步、批量导入导出、面单生成、大量库存计算、报表生成、AI 分析、邮件通知、Webhook 处理。
**用户 HTTP 请求不得同步等待第三方 API。**
## Job 要求
- 构造参数携带 `tenant_code`,`handle()` 开始时恢复 TenantContext。
- 幂等:重复执行不重复建单、不重复扣库存(用业务唯一键或幂等键判断)。
- 显式设置 `$tries`、`$backoff`、`$timeout`,失败记录日志(`failed()`)。
- 传递业务编码 / 主键,不序列化大对象。
## Scheduler 要求
- 任务可配置(开关、频率在配置或租户设置中)。
- 使用 `withoutOverlapping()` / `onOneServer()` 防重复执行。
- 设置超时、失败重试与日志;按租户分发 Job,保证租户隔离。
+9
View File
@@ -0,0 +1,9 @@
---
alwaysApply: true
---
# 身份设定
你现在是一名资深 SaaS / WMS 系统架构师、Laravel 后端工程师、Vue 3 前端工程师,同时具备仓储物流、ERP、3PL、跨境电商仓储业务经验。
我要从零开发一个商业化、多租户 SaaS WMS(Warehouse Management System)。
请不要把它当成简单的 CRUD 后台,而要按照一个未来可以长期商业化、支持多个租户、多个仓库、3PL/海外仓、电商订单接入的正式 SaaS 产品进行设计和开发。
+23
View File
@@ -0,0 +1,23 @@
---
description: 安全、审计日志与密钥管理规则;处理认证、权限、上传、Webhook、日志、第三方凭证时使用
globs: backend/app/**/*.php,backend/config/**/*.php,backend/routes/**/*.php
alwaysApply: false
---
# 安全与审计
详见 `docs/12-security-audit.md`。
## 审计
- 库存、订单、入库、出库状态变更及用户权限变更,必须记录:操作人、时间、动作、修改前、修改后、来源 IP、请求 ID。
- 记录登录日志、操作日志、API 日志;请求 ID 贯穿 HTTP → Job → 外部调用。
## 安全
- 查询只用 Eloquent / Query Builder 参数绑定;禁止拼接用户输入到 `DB::raw`。
- 所有接口:Sanctum 认证 + Policy 授权 + 租户隔离 + 必要的 Rate Limit。
- 文件上传校验类型、大小、扩展名,存储于非公开目录,文件名重新生成。
- 第三方 Webhook 必须校验签名与时间戳,防重放。
## 密钥
- Token / Secret **禁止写入 Git**:使用 `.env`,`.env.example` 只放占位。
- 租户级第三方凭证使用 Laravel 加密(`encrypted` cast)存储。
- 日志脱敏:不输出密码、Token、完整手机号 / 地址等敏感信息。
+27
View File
@@ -0,0 +1,27 @@
---
description: 多租户 tenant_code 隔离规则(后端模型、查询、任务、缓存)
globs: backend/app/**/*.php,backend/database/**/*.php,backend/routes/**/*.php
alwaysApply: false
---
# 租户隔离
详见 `docs/03-multi-tenancy.md`。**严禁租户 A 查询或修改租户 B 的数据。**
- 方案:单数据库 + `tenant_code`;所有核心业务表必须有 `tenant_code`。
- 通过 TenantContext + 全局 Scope / Trait 自动过滤查询、自动填充 `tenant_code`,不要到处手写 where。
```php
// ❌ 手工拼接,容易遗漏
Sku::where('tenant_code', $request->user()->tenant_code)->get();
// ✅ 模型使用租户 Trait,查询自动限定当前租户
Sku::query()->get();
```
- 绕过租户 Scope(`withoutGlobalScope`)只允许出现在平台级管理功能中,并需注释说明原因。
- 根据业务编码查找数据时,也必须受租户限制(编码只在租户内唯一)。
- Queue Job 必须携带 `tenant_code`,执行时恢复 TenantContext。
- Scheduler / Console Command 按租户遍历执行,不能一次性处理全部租户数据而不区分。
- Webhook 根据店铺 / 应用凭证解析租户后再处理。
- 缓存 Key、锁 Key 必须带 `tenant_code` 前缀。
- 新增接口需有租户隔离测试。
+22
View File
@@ -0,0 +1,22 @@
---
description: 测试要求(后端 Pest/PHPUnit、Python pytest)
globs: backend/tests/**,py-service/tests/**
alwaysApply: false
---
# 测试要求
详见 `docs/14-testing.md`。核心业务没有测试不继续堆功能。
## 必测
Tenant Isolation、RBAC、Inventory、Concurrent Inventory、Inbound、Outbound、Allocation、Picking、Stocktake、Order Status、Queue、API Authorization。
## 关键用例
- **库存并发**:库存 10,两个请求同时扣 8 → 只能成功一个,不出现 `available_qty = -6`。并发测试需使用真实 MySQL(不用 SQLite),多进程 / 多连接触发。
- **租户隔离**:租户 A 通过列表、详情、业务编码、批量操作都无法访问租户 B 数据。
- **状态机**:合法转换成功并记录日志;非法转换(如 `draft → shipped`)被拒绝。
- **库存流水**:每次余额变化都有流水,流水累计与余额一致。
- **授权**:无权限用户调用接口返回失败格式与正确状态码。
## 约定
- Feature 测试覆盖 API(含返回格式),Unit 测试覆盖 Domain 规则与状态机。
- 使用 Factory 构造数据,Factory 默认带 `tenant_code`。
+34
View File
@@ -0,0 +1,34 @@
---
description: WMS 领域规则:业务编码、状态机、统一订单模型、第三方平台 Connector
globs: backend/app/Domain/**,backend/app/Application/**,backend/app/Infrastructure/**,backend/app/Models/**
alwaysApply: false
---
# WMS 领域规则
详见 `docs/04-domain-model.md`。
## 业务编码
- 业务关联优先使用 `tenant_code`、`warehouse_code`、`location_code`、`customer_code`、`sku_code`、`order_no`、`inbound_no`、`outbound_no`、`picking_no`、`shipment_no`、`package_no`。
- 单号生成集中在一个编号服务中(租户内唯一、并发安全),不要各模块各自拼接。
## 状态机
- 状态定义为 Enum,合法转换集中声明,非法转换抛异常;状态变更写日志。
- 出库:`draft → allocated → picking → picked → packing → packed → shipped → completed`
```php
// ❌
if ($outbound->status == 'packed') { $outbound->status = 'shipped'; }
// ✅
$outbound->transitionTo(OutboundStatus::Shipped); // 内部校验 canTransitionTo,失败抛 InvalidStatusTransition
```
## 统一订单模型
- `order` / `order_item` / `order_source` / `order_channel` / `order_external_no` / `order_status`。
- 核心订单表、Domain、Application 中**不得出现** Amazon / Shopify / TikTok 等平台特有字段与分支判断。
## 第三方平台
- 实现 `MarketplaceConnector` 接口,放 `app/Infrastructure/Platforms/{Platform}/`。
- Connector 负责平台数据 ↔ 统一模型(订单、SKU、库存、物流、状态)的映射。
- 新增平台只新增 Connector 与配置,不修改核心 WMS 代码。
- 平台调用走 Queue,不在用户 HTTP 请求中同步等待。