🧩 后端总览
SunAdmin 后端采用最新的 Laravel 13 框架 + 模块化设计,业务模块集中在 modules,公共基础能力集中在 app/Core 和 app/Support。
🧭 本章怎么读
后端文档建议按“先理解入口,再理解分层,再深入专项能力”的顺序阅读。第一次接触项目时,不建议直接跳到模型、支付或 AI 章节,因为这些能力都依赖路由、认证、响应、分层和配置约定。
| 阅读阶段 | 建议阅读 | 目标 |
|---|---|---|
| 第1步 | 后端总览 | 知道后端源码放在哪里、模块如何组织、一次请求会经过哪些层 |
| 第2步 | 模块与分层、模块配置 | 先理解 Controller、接口业务动作、Service、Dao、Model 各写什么;再看 module.json、安装记录、依赖声明等模块清单字段 |
| 第3步 | 模块安装与运维、模块资源发布 | 会安装/卸载模块、生成运行包、撰写 publish-manifest.json |
| 第4步 | 路由与认证 | 会新增 Admin / Api 接口,理解公开接口、登录接口、认证中间件 |
| 第5步 | 接口响应与异常 | 会返回统一结构,会处理业务失败、未登录、无权限、参数错误 |
| 第6步 | 权限与菜单 | 会给后台页面、按钮、接口补权限和菜单数据 |
| 第7步 | 系统配置、数据字典 | 会维护系统配置、字典、初始化数据和缓存刷新 |
| 第8步 | 模型机制 | 会编写模型、scope、accessor、mutator、cast 和关联 |
| 第9步 | 附件上传与存储 | 会接入上传、存储驱动、公开访问路径和富文本资源 |
| 第10步 | 缓存与队列 | 会处理缓存失效、热点缓存、异步任务和队列 worker |
| 第11步 | 支付基础能力、支付业务对接 | 会处理支付接入、统一支付单、退款单、渠道回调和业务处理器 |
| 第12步 | AI、MCP / 公共函数与工具 | 会维护 AI 配置、知识库、MCP 工具和项目公共辅助函数 |
🗺️ 文档用途地图
| 你想做什么 | 优先看 | 继续看 |
|---|---|---|
| 新增一个接口 | 模块与分层 | 模块配置、路由与认证、接口响应与异常、权限与菜单 |
| 不知道某个注解什么意思 | 代码注解 | 路由与认证 |
| 新增列表搜索条件 | 模型机制 | 模块与分层、接口响应与异常 |
| 新增后台菜单和按钮权限 | 权限与菜单 | 路由与认证、系统配置 |
| 新增系统配置项 | 系统配置 | 数据字典、缓存与队列 |
| 新增字典选项 | 数据字典 | 系统配置 |
| 新增上传入口或存储驱动 | 附件上传与存储 | 接口响应与异常、系统配置 |
| 安装、卸载或排查模块 | 模块安装与运维 | 模块配置、模块资源发布 |
| 撰写或排查 publish-manifest.json | 模块资源发布 | 模块安装与运维 |
| 接入支付通道 | 支付基础能力 | 支付业务对接、模块安装与运维 |
| 业务模块创建支付单或处理回调 | 支付业务对接 | 支付基础能力、缓存与队列 |
| 接入 AI 对话或知识库 | AI | 系统配置、缓存与队列 |
| 创建 MCP 工具或 AI Agent 工具 | MCP | AI |
| 不知道某个函数在哪里 | 公共函数与工具 | 后端总览、模块与分层 |
🧪 技术栈
- PHP
^8.3 - Laravel
^13.0 - Laravel Sanctum
^4.3 - Spatie Route Attributes
^1.28 - Laravel AI
^0.6.3 - Laravel MCP
^0.7.0 - Yansongda Pay
3.7.4
🧷 公共基础能力
| 路径 | 说明 |
|---|---|
app/Core/Attributes | Inject、OpenGet、OpenPost 等项目注解 |
app/Core/Http | 项目 Request、FormRequest、控制器基类、中间件 |
app/Core/Dao/BaseDao.php | Dao 通用查询、保存、列表分页、状态变更 |
app/Core/Database/SunAdminBuilder.php | Eloquent Builder 扩展,提供 filter()、listData() |
app/Core/Models/BaseModel.php | 模型基类,挂载日期、媒体 URL、筛选 scope 能力 |
app/Core/Support/ApiResponse.php | 统一 code/message/data 响应结构 |
app/Support/ModuleRegistry.php | 模块、路由扫描目录与模块启动注册器注册 |
🏗️ 模块结构(以System模块为例)
text
modules/System
├─ module.json 模块包信息,提供 title、slug、version、resources 等配置
├─ Ai/
│ └─ Tools/ Laravel AI Agent 工具适配,自动发现
├─ Application/
│ ├─ Actions/ 接口业务动作,承接一个接口要完成的具体业务步骤
│ ├─ Jobs/ 队列任务
│ └─ Services/ 业务服务、外部集成、支付、AI 运行编排
├─ Dao/ 数据访问层
├─ Database/Seeders/ 模块初始数据
├─ Domain/
│ ├─ Enums/ 枚举
│ └─ Models/ Eloquent 模型
├─ Http/
│ ├─ Controllers/Admin/ 后台接口控制器
│ ├─ Controllers/Api/ 用户端接口控制器
│ └─ Requests/ FormRequest
├─ Mcp/
│ └─ Tools/ Laravel MCP Server 工具,自动发现
└─ Providers/ 模块启动注册器,接入路由、配置、事件等module.json 只保存模块包信息。路由前缀优先由 slug 推导;普通业务模块必须在 system_modules 中存在安装记录且 status=1,才会注册模块启动注册器和路由。详细说明见 模块配置。
🛣️ 典型请求链路
- 路由注解将请求分发到 Controller。
- Controller 通过
#[Inject]注入接口业务动作 Action 或 Service。 - FormRequest 负责字段校验,复杂场景可用
validated('scene')。 - Action 组织本次接口的业务步骤,调用 Service / Dao。
- Dao 通过模型和
SunAdminBuilder查询数据。 - Controller 返回
success()或抛出业务异常。
示意流程:
🪜 代码分层约定
| 场景 | 推荐位置 |
|---|---|
| 只做参数接收和响应 | Controller |
| 一个接口需要串联多个服务/Dao | Application/Actions |
| 可复用业务能力或外部集成 | Application/Services |
| 单表或聚合查询 | Dao |
| 字段转换、关联、状态 scope | Domain/Models |
| 稳定业务状态值 | Domain/Enums |
🧑💻 常见开发任务示例
以“新增一个后台文章列表接口”为例,推荐按下面顺序落代码:
- 在
modules/System/Http/Controllers/Admin/Article新增或扩展控制器方法,使用路由注解声明 URI。 - 在
modules/System/Http/Requests/Admin/Article新增请求类,声明校验规则。 - 在
modules/System/Application/Actions/Admin/Article编写 Action,负责完成这个接口对应的复杂业务动作,例如需要串联多个服务/Dao。(如果只是常规CRUD操作,可以在控制器中通过Dao集成的能力完成,无需再创建一层Actions处理)。 - 在
modules/System/Dao/Article编写查询方法,或统一走filter()、getList()、save()等Dao集成能力。 - 在
modules/System/Domain/Models/Article补充scopeTitle()、scopeStatus()等筛选条件。 - 如果是后台菜单页面,需要在菜单管理中维护菜单、按钮和权限码,如果要做数据迁移还需要在 Seeder 初始相关数据。
以文章列表控制器为例,形态通常是:
php
use App\Core\Attributes\Inject;
use App\Core\Http\Controllers\ApiController;
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Group;
use Spatie\RouteAttributes\Attributes\Post;
use Modules\System\Dao\Article\ArticleDao;
use Modules\System\Http\Requests\Admin\Article\ArticleRequest;
#[Group(prefix: 'article/list')]
class ArticleController extends ApiController
{
#[Inject]
protected ArticleDao $dao;
#[Get('index')]
public function index()
{
$filters = $this->request()->pick([
['title', ''],
['status', ''],
]);
return success($this->dao->getList(
$filters,
'*',
['sort' => 'desc', 'id' => 'desc']
));
}
#[Post('save')]
public function save(ArticleRequest $form)
{
$this->dao->save($form->validated('save'));
return success();
}
}示例代码用途说明
| 代码 | 用途 |
|---|---|
#[Group(prefix: 'article/list')] | 给当前控制器所有路由统一增加 article/list 前缀,避免每个方法重复写完整路径 |
#[Inject] protected ArticleDao $dao; | 自动注入 ArticleDao 实例 |
#[Get('index')] | 声明 index() 方法为 GET 接口 |
public function index() | 文章列表入口,通常用于表格列表、分页查询、条件筛选 |
$this->request()->pick([...]) | 从当前请求中只提取允许的筛选字段,并为字段设置默认值 |
$this->dao->getList(...) | 调用 Dao 的列表查询方法,统一输出 list/count/page_no/page_size 分页结构 |
$filters | 传给 Dao 的筛选条件,由模型 scopeFilter() 处理数据过滤 |
'*' | 查询字段,表示查询全部字段;真实业务中可按需改成字段数组减少返回体积 |
['sort' => 'desc', 'id' => 'desc'] | 排序规则,先按 sort 倒序,再按 id 倒序,保证列表顺序稳定 |
#[Post('save')] | 声明 save() 方法为 POST 接口,适合保存、修改这类写入操作 |
public function save(ArticleRequest $form) | 保存接口入口,并让 Laravel 自动注入 ArticleRequest |
$form->validated('save') | 获取 ArticleRequest 下 save 场景的校验参数,并返回数据集给业务层 |
$this->dao->save(...) | 调用 Dao 的新增/更新方法,通常根据主键是否存在决定创建还是修改 |
按照这段代码组合后,如果当前控制器位于后台 Admin 控制器目录下,接口路径通常会形成类似:
text
GET /adminapi/system/article/list/index
POST /adminapi/system/article/list/save更详细的用法说明请继续往下阅读相关章节
