后端开发规范
后端开发规范
约束 am-serve/app/ 下的分层与写法。基础类在 app/basic/(BaseController/BaseModel/BaseValidate/BasePlugin + Registry 家族),缓存类在 app/cache/,异常在 app/exception/ApiException.php。
分层职责
| 层 | 位置 | 放什么 |
|---|---|---|
| Controller | app/controller/{module}/ | 请求编排、参数校验、鉴权、返回响应体。简单 CRUD 直接写完,别过度抽 Service |
| Model | app/model/{module}/ | 数据查询、搜索器、关联、数据组装、模型内业务判断 |
| Validate | app/validate/{module}/ | 规则、场景、错误文案 |
| Service | app/service/ | 被 2+ 处复用、跨 3+ Model、第三方集成、异步任务 |
| Cache | app/cache/ | 缓存读写与清理,统一走 CacheManager |
| Utils / functions.php | app/utils/、app/functions.php | 无 Model 依赖的纯工具 |
判断落点:无业务依赖 → utils/;有业务依赖但无 Model → functions.php;有 Model 依赖 → Model 层。
控制器
系统模块控制器继承 BaseController,用 #[RouteGroup('/sys/{module}')] 定前缀,initialize() 里初始化 model;validate 只在真正用到时才 new。登录/公开/回调等公共控制器可不继承 BaseController、不挂 #[Permission]。
标准方法名(推荐非强制,只写实际需要的):
| 功能 | 方法 | HTTP |
|---|---|---|
| 列表 | index | GET |
| 详情 | read | GET |
| 新增 | save | POST |
| 更新 | update | POST |
| 删除 | delete | POST |
| 批量删除 | batchDelete | POST |
| 状态切换 | changeStatus | POST |
其它按语义命名(editPass/setDefault/retry/preview 等)。BaseController 通用方法:$this->userInfo、validate()、search()、getList()、returnListData()、transaction()、assertCanManage()/assertRowVisible()。
规则要点:
- 必填参数先拦:
if (empty($id)) return warning('参数错误'); - 删除前查子记录/关联;内置数据(
is_sys=1)禁删。 - 多表写用
$this->transaction(fn() => ...)。 - 改用户/角色/菜单/权限/字典后清对应缓存(见缓存规范)。
- 树形用
\app\utils\Tree::createListToTree($list, 'id', 'pid', 'children')。
模型
继承 BaseModel,$name 写完整表名(含 system_ 等模块前缀,物理表另有 am_ 库前缀)。主键默认 id,非 id 才声明 $pk。树形模型定义静态 $treeField。搜索器按需加、方法名用驼峰(searchKeywordsAttr);searchCreateTimeAttr/searchUpdateTimeAttr 基类已提供。中间表模型继承 ThinkORM 的 Pivot。
验证器
继承 BaseValidate,定义 $rule/$message/$scene,场景只列该场景要校验的字段。
时间字段(关键约定)
库表用 INT UNSIGNED 存 Unix 时间戳,不用 DATETIME。config/think-orm.php 里 auto_timestamp=true、datetime_format=false:
| 字段 | 处理方式 |
|---|---|
create_time/update_time/delete_time | ❌ 禁止手动赋值,ORM 自动管理 |
last_login_time/publish_time/expire_time 等业务时间 | ✅ 手动 = time() |
判断标准:字段名恰为 create_time/update_time/delete_time 交给 ORM;其它以 _time 结尾的手动写。查询返回时 ORM 会把 INT 时间戳自动转字符串,接口直接返回模型数据即可。
服务层
命名 {Name}Service.php,方法多为 static,单一职责,返回统一数组 ['success' => bool, 'message' => string, 'data' => mixed],内部捕获异常转友好信息。仅在"被 2+ 处调用 / 跨 3+ Model / 第三方集成 / 异步"时才抽 Service;简单 CRUD、单表查询、格式转换不抽。
别照搬旧规范里的密码哈希写法
早期文档示例写的是 md5(md5($password).$salt)。实际 app/functions.php 用的是 password_hash($salt.$password.$salt, PASSWORD_DEFAULT)(passwordHash())与 password_verify(passwordVerify())。一律调用这两个 helper,不要自己拼哈希算法。
接口(扩展点契约)
放 app/interface/{Name}Interface.php,用于定义插件扩展点(如存储插件契约),带完整 PHPDoc、明确参数与返回类型。插件侧实现该接口。
队列
消费者放 app/queue/redis/{Name}Job.php(Webman\RedisQueue\Consumer),投递用 Client::send($queue, $data, $delay)。任务必须幂等(会重试)、捕获异常、记关键日志。适合邮件短信/文件处理/统计/第三方调用;不适合高实时、需同步返回的操作。插件侧异步走 QueueService,见队列·任务·推送。
响应体
统一 {code, msg, total, data, page},由 success()/warning()/error()/ret() 生成。详见响应与接口前缀。
