编码习惯与规范.md 11 KB

Forge-Hotel 编码习惯与规范

本文档记录个人在 Forge 项目中的编码习惯和偏好,所有代码编写(含 AI 辅助生成)必须遵循以下规范。

适用范围: forge-hotel 模块及后续所有业务模块 最后更新: 2026-08-14


一、后端编码习惯

1.1 依赖注入

  • 使用 @Autowired 字段注入,不使用 @RequiredArgsConstructor 构造器注入
  • 每个依赖字段单独加 @Autowired 注解
  • 示例: java @RestController @RequestMapping("/hotel/dish") public class HotelDishController { @Autowired private HotelDishService hotelDishService; }

1.2 Controller 查询参数

  • 分页查询接口的筛选参数必须用实体类接收,禁止逐个 @RequestParam
  • 只有 @PathVariable(路径参数如 id)和确实无法用实体类表达的参数(如安全校验参数)才单独接收
  • 状态更新类接口(如 /{id}/status)属于操作类,可保持 @RequestParam
  • 示例: ```java // ✅ 正确 public RespInfo> dishPage(PageQuery pageQuery, HotelDish query) { ... }
  • // ❌ 错误 public RespInfo> dishPage(PageQuery pageQuery,

    @RequestParam(required = false) Long categoryId,
    @RequestParam(required = false) String status,
    @RequestParam(required = false) String name) { ... }
    
    - Service 接口和 Mapper 方法签名同步使用实体类参数
    - Mapper XML 中用 `@Param("query")` 标注,引用时用 `#{query.fieldName}`
    
    ### 1.3 统一返回封装
    
    - 所有接口统一使用 `RespInfo` 封装返回值
    - 成功:`RespInfo.success(data)` 或 `RespInfo.success()`(无返回值时)
    - 失败:`RespInfo.error(msg)` 或抛出 `BusinessException`
    
    ### 1.4 实体类规范
    
    - 实体继承 `TenantEntity`(自带 tenantId + 审计字段)
    - 主键使用 `@TableId(value = "id", type = IdType.ASSIGN_ID)`(雪花算法)
    - 逻辑删除字段:`@TableLogic(value = "0", delval = "id")`,类型为 `Long`(bigint),删除时写入主键 ID
    - 使用 Lombok:`@Data` + `@EqualsAndHashCode(callSuper = true)` + `@Serial`
    - 每个字段必须有 Javadoc 注释
    - 示例:
    ```java
    @Data
    @EqualsAndHashCode(callSuper = true)
    @TableName("hotel_business_hours")
    public class HotelBusinessHours extends TenantEntity {
        @Serial
        private static final long serialVersionUID = 1L;
    
        /** 主键ID(雪花算法) */
        @TableId(value = "id", type = IdType.ASSIGN_ID)
        private Long id;
    
        /** 状态: ENABLED-启用, DISABLED-禁用 */
        private String status;
    
        /** 删除标记(0=未删除,删除时写入主键ID) */
        @TableLogic(value = "0", delval = "id")
        private Long delFlag;
    }
    

    1.5 SQL 编写规范

    • SQL 必须写在 Mapper XML 中,禁止在 Service 层用 LambdaQueryWrapper 构建查询
    • 例外:仅 selectByIdinsertupdateByIddeleteById 等 MyBatis-Plus 内置方法允许
    • 原因:DataScopeInterceptormapperMethod 精确匹配改写 SQL
    • Mapper XML 中的查询必须显式过滤未删除数据:AND del_flag = 0
    • 原因:自定义 XML SQL 不会被 MP 自动补全逻辑删除条件

    1.6 操作日志

    • 每个 Controller 方法必须加 @OperationLog 注解
    • 格式:@OperationLog(module = "模块名", type = OperationType.XXX, desc = "操作描述")
    • 操作类型:QUERY / ADD / UPDATE / DELETE

    1.7 包结构

    controller/     # REST 控制器
    domain/         # 数据库实体(Entity)
    dto/            # 请求 DTO(新增/修改用)
    vo/             # 响应 VO(查询返回用)
    mapper/         # MyBatis Mapper 接口
    service/        # 服务接口
      └── impl/     # 服务实现
    constant/       # 常量
    

    1.8 Service 层规范

    • Service 之间禁止互相注入(避免循环依赖)
    • 跨 Service 协调逻辑上提到 Controller 层
    • 事务注解:@Transactional(rollbackFor = Exception.class)
    • 校验方法命名:validateXxx(Xxx entity/dto)
    • 存在性校验方法命名:requireXxx(Long id)(不存在直接抛异常)

    二、前端编码习惯

    2.1 页面组件选择

    • 标准 CRUD 页面:使用 AiCrudPage 组件(自动处理搜索栏、工具栏、表格、分页布局)
    • 分页位置:右下角(AiCrudPage 内置)
    • 配置少的简单列表:不加多余筛选条件,保持界面简洁
    • 页面容器样式:height: 100%

    2.2 AiCrudPage 配置规范

    • api-config 格式: javascript :api-config="{ list: 'get@/hotel/xxx/page', detail: 'get@/hotel/xxx/:id', add: 'post@/hotel/xxx', update: 'put@/hotel/xxx', delete: 'post@/hotel/xxx/remove/:id', }"
    • URL 占位符用冒号格式:id,禁止花括号 {id}
    • tableColumnseditSchema 必须定义为 computed(确保字典异步加载后响应式更新)
    • 操作列使用 h('a', { class: 'text-primary cursor-pointer hover:opacity-80', onClick: ... }) 模式
    • 刷新方法:crudRef.value?.refresh()(不是 reload)
    • 编辑方法:crudRef.value?.handleEdit(row)

    2.3 字段命名

    • 前端字段统一使用 camelCase 命名
    • 与后端 API 返回字段保持一致

    2.4 表格渲染规范

    • 空值处理:空值字段显示空白,不使用 - 作为占位符
    • 图片列
      • 列宽设置 100px
      • div 包裹 AuthImage 组件,设置 overflow: hidden 防止图片溢出
      • 禁止直接用 NAvatar 的 src 属性
    • 状态标签:使用 NTag 组件,配合颜色映射 javascript const statusColorMap = { ENABLED: 'success', DISABLED: 'default' } render: row => h(NTag, { type: statusColorMap[row.status] || 'default', size: 'small' }, { default: () => statusLabelMap[row.status] || row.status })

    2.5 操作按钮样式

    使用 UnoCSS 语义化颜色类区分操作类型:

    类名 颜色 场景
    text-primary 编辑、查看、授权
    text-warning 刷新缓存、重置、封禁
    text-error 删除、强制下线
    text-success 绿 启用、发布、通过
    text-info 灰蓝 详情、统计

    2.6 字典使用

    • 禁止硬编码下拉选项和状态标签
    • 下拉使用 DictSelect 组件或 useDict() composable
    • 标签展示使用 DictTag 组件
    • Schema 中的字典选项必须用 computed 包裹

    2.7 图片预览

    • 采用全屏覆盖层实现(Teleport to="body"
    • position: fixed + 半透明黑色背景
    • 右上角 × 关闭按钮,支持点击背景或 ESC 关闭
    • 左右箭头切换图片,底部圆点指示器 + 页码计数
    • 图片自适应:max-width: 85vw + max-height: 80vh

    2.8 时间段选择

    • 时间类字段使用下拉选择(Select),不使用手动输入框
    • 时间选项按业务场景设定间隔(如每 30 分钟一档:09:00, 09:30, 10:00...23:30)

    2.9 API 文件规范

    • 导入路径:import { request } from '@/utils'(不是 @/utils/request
    • 函数命名:getXxxPage / getXxxDetail / createXxx / updateXxx / deleteXxx
    • 按模块用注释分隔:// ==================== 模块名 ====================
    • 每个函数加 JSDoc 注释

    三、数据库编码习惯

    3.1 表结构规范

    • 所有业务表必须包含:id, tenant_id, create_by, create_time, create_dept, update_by, update_time
    • 字符集 utf8mb4,引擎 InnoDB
    • 主键用 bigint(雪花算法),不用自增
    • 金额字段用 bigint,单位
    • 时间字段用 datetime(对应 Java LocalDateTime
    • 状态字段用 varchar,值为大写英文(如 ENABLED / DISABLEDON_SALE / OFF_SHELF

    3.2 逻辑删除规范

    • 业务表默认使用逻辑删除
    • del_flag 字段类型 bigint NOT NULL DEFAULT 0
    • 删除时写入当前行主键 ID(不是固定值 1
    • 实体必须显式声明 @TableLogic(value = "0", delval = "id")

    3.3 Flyway 迁移脚本

    • 命名格式:V<版本号>__<lower_snake_case_description>.sql
    • 版本号必须大于 1.0.0,单调递增
    • 脚本必须具备防重复保护
      • 建表:CREATE TABLE IF NOT EXISTS
      • 插入:INSERT ... SELECT ... WHERE NOT EXISTS
      • 新增列/索引前查 information_schema
    • INSERT 必须显式写列名,禁止依赖表字段顺序
    • 业务内置数据 tenant_id 必须为 1,禁止写 0
    • 菜单资源 SQL 使用动态 parent_id(子查询),避免硬编码 ID
    • 字典数据格式:sys_dict_type + sys_dict_datadict_value 与后端枚举值保持一致

    3.4 字典规范

    • 字典类型命名:小写下划线,业务级用 hotel_ 前缀,系统级用 sys_ 前缀
    • dict_value 必须与后端枚举/状态值严格一致
    • dict_label 只负责展示文案
    • list_class 负责标签样式(success/primary/warning/info/default)
    • 每个字典数据单独一行 INSERT + WHERE NOT EXISTS 防重复

    四、API 设计习惯

    4.1 RESTful 风格

    操作 HTTP 方法 路径 说明
    分页查询 GET /xxx/page 筛选参数用实体类接收
    查询全部 GET /xxx/all/xxx/list 不分页列表
    查询详情 GET /xxx/:id 路径参数
    新增 POST /xxx RequestBody
    修改 PUT /xxx RequestBody
    删除 POST /xxx/remove/:id 路径参数
    状态切换 POST /xxx/:id/toggle/:id/toggle-status 路径参数
    状态更新 PUT /xxx/:id/status @RequestParam String status
    批量操作 POST /xxx/batch RequestBody

    4.2 权限标识

    • 格式:模块:资源:操作,如 hotel:dish:queryhotel:businessHours:add
    • 按钮权限资源类型 resource_type = 3

    五、交互设计习惯

    5.1 列表页面

    • 配置项少的列表页不加多余筛选(如营业时段不加餐段筛选)
    • 分页统一在右下角
    • 操作列包含:编辑 + 状态切换 + 删除

    5.2 批量操作

    • 有批量生成的管理页面,应同步提供批量删除、批量下载(ZIP)、单条下载功能
    • 保证操作完整性与用户体验一致性

    5.3 危险操作

    • 删除、解绑等不可逆操作必须弹出二次确认
    • 已绑定的二维码显示"解绑"按钮而非"重新绑定"

    5.4 扫码绑定

    • 绑定成功后,点击"完成"跳转至扫码绑定主界面(无参数状态)
    • 解绑后返回扫码界面

    六、多租户习惯

    6.1 租户上下文

    • 业务数据 tenant_id 必须设为 1(默认租户),禁止设 0
    • 免登录场景(如扫码):先忽略租户过滤查到实体,再根据实体的 tenantId 动态设置租户上下文
    • 使用 TenantContextHolder.getTenantId() 获取当前租户 ID
    • 兜底逻辑:return tenantId == null ? 1L : tenantId;

    七、其他习惯

    7.1 注释风格

    • 实体类每个字段必须有 Javadoc 注释
    • Controller 每个方法必须有 Javadoc(描述接口用途)
    • Service 接口方法必须有 Javadoc(含参数说明)
    • 关键业务逻辑加行内注释说明

    7.2 安全红线

    • 禁止硬编码密钥、AK/SK、数据库密码
    • 禁止在日志中打印手机号、身份证、银行卡
    • API Key/Secret 返回前端必须脱敏(保留前4后4,中间 ****

    7.3 构建与验证

    • Maven 构建:mvn compile -pl forge-business/forge-hotel -am -DskipTests
    • 前端 Lint:pnpm lint:fix
    • 改完代码必须编译/Lint 验证通过