Skip to content

Beta 代码规范

本规范适用于 beta/ 下的 Laravel 代码,是代码实现与审查的默认依据。它不要求为了统一风格一次性重写遗留系统;新增代码必须遵守,实质修改到的旧代码应在不扩大风险的前提下就近收敛。

规范用语:

  • 必须 / 禁止:合并前必须满足。
  • 应该 / 不应该:默认要求;偏离时需在代码或模块 README 说明理由。
  • 可以:按业务复杂度选择。

架构选型以本规范和模块结构规范为准;涉及客户配置、Schema、RBAC 或具体业务流程时,再阅读对应专项文档。

1. 基础约定

  • PHP 版本以项目 composer.json 为准。纯领域逻辑、值对象、DTO、Enum 等新增文件建议使用 declare(strict_types=1);;Controller、Eloquent Model、迁移适配和遗留系统边界文件不强制。无论是否启用严格类型,外部输入和旧库数据都必须显式校验与转换。
  • PHP 代码遵守 pint.json 固定的 Laravel Pint 配置;命名遵守 PSR-4。新增或修改 PHP 后,必须执行 composer format -- <changed-php-files>,再用 composer format:check -- <changed-php-files> 验证。
  • 不得在共享工作区无参数执行 composer format;它会扫描整个 Beta 项目。历史基线的全量格式化必须作为独立迁移任务,避免把无关格式化混入业务改动。
  • Blade、JavaScript 和 Vue 文件遵守 .editorconfig 的 4 空格、LF 和末尾换行规则;当前未配置它们的自动格式化器,不能误用 Pint 或假装已完成自动格式化。
  • 类使用 PascalCase,方法和变量使用 camelCase,常量使用 UPPER_SNAKE_CASE
  • 优先构造函数依赖注入;禁止在业务代码中随意使用 new Service() 或 Service Locator。
  • 一个类只承担一个稳定职责。不要为追求目录完整度预建空接口、空 DTO、空 Repository。
  • 注释解释业务口径和“为什么”,不复述代码字面含义。
  • 不新增依赖,除非现有 Laravel 能力和项目公共能力无法合理解决问题,并经过明确评审。

2. 模块和分层

业务模块放在 app/Modules/<Module>/,模块内的 Controller、Request、Service、Model、Policy、Resource、路由、配置和视图保持内聚。

Controller

必须只负责:

  1. 接收已经过 Form Request 校验的输入;
  2. 调用应用服务;
  3. 返回 View、Redirect、Resource 或 JSON。

禁止在 Controller 中编写事务、复杂查询、状态转换或跨表流程。

Form Request

  • 负责输入类型、格式、边界及入口授权。
  • 数字 ID 必须校验为正整数;数组 ID 必须逐项验证。
  • validated() 之外的请求数据不得直接进入 Service 或 SQL。
  • 不承载事务、数据库写入和长业务判断。

Service

  • 负责用例编排、业务规则、状态转换、事务和审计顺序。
  • 事务范围必须覆盖一个完整业务原子操作;需要防止重复审批或并发覆盖时使用行锁。
  • 不把所有操作堆入一个万能 Service;按申请、审批、发放、查询等稳定职责拆分。
  • Service 不返回 HTTP Response,不直接依赖 Blade。

QueryService / Repository

  • 复杂列表、统计和数据范围查询应该从 Controller 与写流程 Service 中拆出。
  • Repository 用于隔离确有价值的数据访问契约,不要求每个 Model 都配一个 Repository。
  • 查询必须显式应用模块 Data Scope;禁止依赖前端传入分中心、客户或用户 ID 作为唯一范围控制。

Model

  • Model 负责关系、字段语义、局部查询 Scope 和简单领域判断。
  • 禁止在 Model 事件中隐藏跨表长流程或外部副作用。
  • fillable、关系外键和遗留表名必须显式声明。
  • Scope 名称表达业务语义,例如 controlled()pending(),不要把 where status = 0 散落到各处。

3. 状态、类型与布尔字段

  • 有限且稳定的业务状态或类型必须使用字符串 Backed Enum。
  • 每个业务概念使用独立 Enum;禁止建立容纳所有模块的通用 StatusEnum
  • Enum 值必须兼容旧库和旧接口,不得因命名优化擅自修改 0/1/2 等持久化值。
  • 状态文案由 Enum 的 label() 提供,Controller、Service 和 Blade 不重复写三元表达式。
  • 读取可能含历史脏值的字段使用 tryFrom();不得直接 Enum Cast 后让未知值导致整个页面失败。
  • 状态转换必须通过 Service 完成,并有合法转换、重复操作和越权测试。
  • is_* 真假字段属于布尔语义,不使用 Enum。旧库存在 NULL/0/1 时,通过 Model Scope 或语义方法集中兼容。

参考实现位于 app/Modules/FileManagement/src/Enums/

4. 权限与数据范围

  • RBAC 决定“能做什么”,Data Scope 决定“能操作哪些数据”,两者必须同时满足。
  • Controller/Request 做入口能力校验;Service 对具体资源做二次权限和归属校验。
  • 查询必须校验用户、角色、分中心、客户和资源归属,不能只检查前端按钮是否显示。
  • 禁止使用覆盖全部模型的 Eloquent 全局 Scope 实现 LIMS 数据隔离。
  • 省中心、跨分中心和特殊审批例外必须显式建模,并纳入测试矩阵。
  • 禁止直接信任请求中的 fzx_idcustomer_iduser_id 来扩大数据范围。

5. 数据库与兼容性

  • SQL 按达梦、MySQL、TiDB、PolarDB、金仓、瀚高的最小公约数编写。
  • 优先使用 Eloquent 或 Query Builder 参数绑定;禁止拼接用户输入。
  • 禁止依赖 MySQL 宽松 GROUP BYINSERT ... SETREPLACE INTOGROUP_CONCAT 等专属行为。
  • 分组查询的非聚合选择字段和排序字段必须符合严格 GROUP BY 规则。
  • “每组最新一条”使用聚合子查询加 JOIN 回表等可移植方式。
  • 避免循环查库;列表和导出必须检查 N+1,使用 eager loading、批量查询或聚合。
  • 修改旧表前必须确认字段可能缺失、类型不一致和 NULL 语义;兼容逻辑要集中,不能在页面零散兜底。
  • Migration 必须可重复判断环境差异,不假定所有客户站点已经执行相同历史脚本。

6. HTTP、JSON 与异常

  • Web 表单失败使用校验错误或明确业务异常,禁止以成功响应包装失败。
  • JSON API 的字段和状态码必须稳定;需要兼容旧接口时,在模块 README 写明旧契约。
  • API 使用 Resource 统一输出;普通 Blade 页面不为形式统一强制增加 Resource。
  • 禁止把异常详情、SQL、路径、堆栈或敏感配置返回给用户。
  • 可预期业务冲突使用明确业务异常;程序错误继续抛出并记录,不得空 catch 后假装成功。
  • 下载、预览和上传必须验证资源权限、文件归属、规范化路径、扩展名和实际内容。

7. Blade 与前端

  • 普通后台页面统一使用 Blade + Layui,以服务端渲染和局部 JavaScript 增强为主。
  • 只有 PDF、复杂工作台等高交互页面允许 Inertia + Vue,并在模块 README 记录例外理由。
  • 禁止同一类 CRUD 页面长期维护 Blade 与 Vue 两套实现。
  • Blade 默认使用转义输出 ;只有来源可信且经过清洗的 HTML 才能使用 {!! !!}
  • 用户输入、客户名称、备注和配置文本不得直接拼入 HTML 属性、脚本或内联事件。
  • AJAX 必须处理成功、业务失败、网络失败和重复提交;提交期间按钮应禁用或显示加载状态。
  • JavaScript 不得自行复制后端权限、状态转换和数据范围规则。
  • Vue 专用组件和样式只进入对应构建入口,不污染普通 Blade 页面。

8. Laravel 特性

  • Policy/Gate 用于动作和资源授权,不替代 Data Scope。
  • Domain Event 只承载事务成功后的通知、审计扩展和跨模块副作用;核心状态写入保持同步明确。
  • 需要观察数据库提交结果的事件必须在事务提交后触发。
  • Queue Job 只用于耗时、可重试、允许最终一致的任务;审批结论、权限判断和核心状态转换不得异步化。
  • Job 必须幂等,明确重试、超时、失败记录和补偿方式。
  • Cache 必须定义 key、隔离维度、有效期和失效机制;业务事实不能只保存在缓存。
  • Scheduler 任务必须幂等、可观测、可安全补跑。

9. 配置、日志与安全

  • 配置统一从模块正式 key 读取;迁移期 fallback 必须有明确来源和淘汰条件。
  • 禁止把密码、Token、私钥、生产连接和客户隐私提交到仓库或写入普通日志。
  • 日志至少包含模块、动作、资源标识和结果;敏感字段必须脱敏。
  • 审批、授权、报告、资金、客户和跨中心操作必须留下可追溯审计记录。
  • 禁止使用 env() 直接驱动业务代码;环境变量只在配置文件中读取。
  • 文件路径必须由可信根目录解析,禁止接受未经规范化的相对路径和文件名。

10. 测试与验证

最低要求

  • Enum、值对象、纯规则:Unit Test。
  • Controller、权限、数据库查询、事务流程:Feature Test。
  • 修复缺陷时先补能复现问题的回归测试,再修改实现。
  • 状态流程至少覆盖成功、拒绝、重复操作、非法状态和越权。
  • 数据范围至少覆盖普通用户、分中心管理员、省中心、跨中心例外和无权限用户。
  • 文件功能至少覆盖路径穿越、非法类型、无权访问和缺失文件。

提交前检查

按改动范围运行,不要求每次机械执行无关全量任务:

bash
composer format:check -- <changed-php-files>
php -l <changed-php-file>
php artisan test <related-tests>
php artisan view:cache        # 修改 Blade 时
git diff --check

涉及 SQL 时必须说明验证过的数据库;无法连接达梦或客户环境时,在交付说明中明确验证缺口。

11. 迁移兼容与 fallback

  • 必须保持已承诺的旧 URL、请求字段、响应字段、数据库编码、权限口径和多站点行为。
  • fallback 只允许用于可证明的旧库、客户配置、预览能力或部署差异,不能用于掩盖程序错误。
  • 每个新增 fallback 必须写明触发条件、兼容对象和删除条件。
  • 不得以“兼容”为理由同时长期维护两套业务真相或两套同类 CRUD。
  • 小改动只整理本次触及的代码,禁止顺手全文件格式化或无关重构。

12. 代码审查清单

合并前依次确认:

  • 行为是否符合需求,旧契约是否保持兼容?
  • 权限和 Data Scope 是否同时校验,是否可被请求参数绕过?
  • 状态是否使用正确的领域 Enum,转换是否只发生在 Service?
  • SQL 是否参数化、跨数据库可用、无 N+1 和宽松分组依赖?
  • 输出、上传、下载和路径是否存在 XSS、注入或穿越风险?
  • 事务、锁、事件和审计顺序是否正确?
  • 是否不必要地引入抽象、依赖、异步或第二套前端实现?
  • 回归测试和环境验证是否与风险相称?
  • 是否保留了无关文件和他人的工作区改动?

13. 例外管理

规范无法覆盖的真实业务约束可以例外,但必须满足:

  1. 在模块 README 或代码附近说明原因和影响范围;
  2. 有测试保护例外行为;
  3. 不降低权限、安全和审计要求;
  4. 明确例外是长期设计还是带淘汰条件的迁移兼容。