技术部前后端开发规范

后端开发规范

  1. 接口文档管理
    新开发的后端接口必须在 Apifox 中进行详细记录。具体要求如下:

    • 每个接口需明确描述其功能、请求方式(如 GETPOST 等)、请求参数及参数类型。
    • 在接口的“成功示例”中,提供完整的返回结果集,并对每个字段进行清晰注释,说明字段含义、数据类型及可能的取值范围。
      示例:
      {
        "code": 200,                  // 状态码,200 表示成功
        "message": "操作成功",         // 提示信息
        "data": {                     // 返回数据
          "userId": 123,              // 用户ID,整数类型
          "username": "testUser",     // 用户名,字符串类型
          "createdAt": "2025-04-08"   // 创建时间,格式为 YYYY-MM-DD
        }
      }
      
    • 确保接口文档与实际代码保持一致,避免因文档不准确导致前后端联调问题。
  2. 前后端职责划分

    • 前端职责:前端仅负责数据展示和用户交互,所有涉及业务逻辑的计算均交由后端处理。
      • 前端应避免直接对数据进行复杂计算或逻辑判断,例如金额计算、日期格式化等,这些操作统一由后端完成并返回结果。
    • 后端职责:后端需承担所有业务逻辑处理及数据校验工作,包括但不限于:
      • 表单提交的数据校验(如字段是否为空、数据格式是否正确、值范围是否合法等)。
      • 数据计算、逻辑判断及异常处理。
      • 返回标准化的错误信息,便于前端进行错误提示。
  3. 表单校验

    • 原则上,表单提交的校验工作由后端全权负责,前端无需重复校验。
    • 若前端需要实现简单的校验(如必填项检查)(正常情况下无需做任何校验),仅为提升用户体验,不能替代后端校验。
    • 后端在校验失败时,需返回清晰的错误信息,告知具体的校验失败原因,例如:
      {
        "code": 400,
        "message": "参数错误",
        "errors": [
          {
            "field": "username",
            "message": "用户名不能为空"
          },
          {
            "field": "email",
            "message": "邮箱格式不正确"
          }
        ]
      }
      
  4. 接口文档目录结构规范

    • 接口文档的目录结构需按照实际应用(App)的功能模块或入口目录进行组织,确保文档结构清晰、易于查找。

    • 具体要求如下:

      • 按照项目的功能模块划分目录,例如:用户管理订单管理支付管理 等。
      • 如果项目有明确的入口层级(如 /api/v1/api/v2),则在 Apifox 中创建对应的目录层级,与实际接口路径保持一致。
        示例目录结构:
        ├── 用户管理
        │   ├── 登录接口 (POST /api/v1/user/login)
        │   ├── 注册接口 (POST /api/v1/user/register)
        │   └── 用户信息查询 (GET /api/v1/user/info)
        ├── 订单管理
        │   ├── 创建订单 (POST /api/v1/order/create)
        │   ├── 查询订单详情 (GET /api/v1/order/detail)
        │   └── 取消订单 (POST /api/v1/order/cancel)
        ├── 支付管理
        │   ├── 发起支付 (POST /api/v1/pay/initiate)
        │   └── 支付结果查询 (GET /api/v1/pay/status)
        
      • 如果某个模块下有子模块,则进一步细分目录。例如,在 订单管理 下可以增加 退款管理 子目录:
        ├── 订单管理
        │   ├── 退款管理
        │   │   ├── 申请退款 (POST /api/v1/order/refund/apply)
        │   │   └── 查询退款状态 (GET /api/v1/order/refund/status)
        
    • 命名规范

      • 目录名称和接口名称应简洁明了,避免使用模糊词汇。
      • 使用小写字母和中划线(-)分隔单词,例如:用户管理 -> user-management
      • 接口路径需与后端实际路由保持一致,便于前后端对接。
    • 维护要求

      • 新增接口时,需严格按照目录结构添加到对应模块下,避免随意放置导致文档混乱。
      • 定期检查接口文档目录,清理废弃接口或调整目录结构以适应项目需求变化。

接口返回字段优化规范

  1. 按需返回字段
    • 在列表接口中,应根据实际需求明确指定返回的字段,避免使用 SELECT * 查询所有字段。
    • 通过 MyBatis-Plus 的 queryWrapperX.select(...) 方法,显式列出需要返回的字段,减少不必要的数据传输,提升查询效率。
    • 示例代码如下:
      return selectPage(reqVO, queryWrapperX.select(
          StarUserDO::getId,
          StarUserDO::getUname,
          StarUserDO::getSex,
          StarUserDO::getJobCode,
          StarUserDO::getStatus,
          StarUserDO::getEmergencyContactName,
          StarUserDO::getJobTitle,
          StarUserDO::getFace,
          StarUserDO::getBirthday
      ));
      
    • 注意事项
      • 确保只返回前端或业务逻辑所需的字段。
      • 如果新增字段需求,需同步更新 select(...) 中的字段列表。

逻辑代码优化规范

  1. 避免硬编码数字,使用静态常量或枚举
    • 在业务逻辑中,禁止直接使用魔法数字(如 012 等)表示状态值或其他含义。应使用静态常量或枚举类来代替,增强代码的可读性和可维护性。
    • 示例代码如下:
      public enum AuditStatusEnum {
          UNSENT(0, "草稿(未提交)"),
          WAIT(1, "待审核(已提交)"),
          SUCCESS(2, "已通过");
      
          private final int code;
          private final String description;
      
          AuditStatusEnum(int code, String description) {
              this.code = code;
              this.description = description;
          }
      
          public int getCode() {
              return code;
          }
      
          public String getDescription() {
              return description;
          }
      }
      
    • 使用示例:
      if (status == AuditStatusEnum.WAIT.getCode()) {
          // 执行待审核逻辑
      }
      
    • 优点
      • 提高代码可读性:通过枚举的名称(如 WAIT)直观表达含义。
      • 减少错误:避免因硬编码导致的状态值不一致问题。
      • 易于扩展:新增状态时只需在枚举类中添加新值,无需修改多处代码。

2025年4月2日新增后台开发规范

1. 字段非空设置

为了确保与历史数据的兼容性并减少因字段为空导致的错误,数据库字段应尽量避免设置为 NOT NULL。若确实需要将某个字段设置为非空,请务必充分评估其必要性和影响范围,并在设计文档中详细说明理由及处理方案。

2. 字符长度校验

当数据库字段设置了字符长度限制时,前端应在提交表单数据前对输入参数进行字符长度校验,以确保传入的数据不超过字段的最大长度限制。这有助于避免因数据过长而引发的异常或报错,保障系统的稳定性和数据完整性。

3. 数据格式与编码

在前后端交互过程中,建议对所有输入数据进行必要的格式校验和编码处理,以防止乱码或格式不匹配问题的发生。

4. 错误处理与提示

接口应当对异常输入提供明确且详细的错误信息,帮助前端快速定位问题并作出相应调整。具体要求如下:

  • 清晰的错误消息:错误信息应包含具体的错误原因、发生位置及相关上下文,以便于调试和修复。
  • 用户友好的提示:对于终端用户可见的错误信息,应尽量使用通俗易懂的语言描述问题,并给出可能的解决方案或操作指引。

示例注释模板

以下是一个简单的示例,展示如何在代码中实现上述规范:


/**
 * 短信验证码发送请求参数
 *
 * 用于接收短信验证码发送请求的入参。
 * 包含手机号、发送场景及客户端 IP 地址等必要信息。
 */
@Data
@Schema(description = "短信验证码发送请求参数")
public class SmsCodeSendReqDTO {

    /**
     * 手机号,必须为合法中国大陆手机号格式。
     * 示例:15601691300
     */
    @Schema(description = "手机号", example = "15601691300", required = true)
    @NotEmpty(message = "手机号不能为空")
    @Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
    private String mobile;

    /**
     * 发送场景,参考 SmsSceneEnum 枚举值。
     * 示例:1(登录),2(注册),3(找回密码)
     */
    @Schema(description = "发送场景", example = "1", required = true)
    @NotNull(message = "发送场景不能为空")
    private Integer scene;

    /**
     * 客户端 IP 地址,用于风控和日志记录。
     * 示例:10.20.30.40
     */
    @Schema(description = "发送来源 IP 地址", example = "10.20.30.40", required = true)
    @NotEmpty(message = "发送 IP 地址不能为空")
    private String createIp;

}

Controller 层使用示例

/**
     * 发送短信验证码接口
     *
     * @param request 请求参数,包含手机号、发送场景和客户端 IP
     */
    @PostMapping("/send-code")
    public String sendCode(@Valid @RequestBody SmsCodeSendReqDTO request) {
        // 正常进入业务逻辑处理
        return "验证码发送成功,手机号:" + request.getMobile() +
               ",场景:" + request.getScene() +
               ",IP:" + request.getCreateIp();
    }

2025年5月4日新增后台开发规范

1. 表命名规则

前缀:所有表名都应以 yd_ 开始。
实体描述:紧跟前缀之后是描述表中存储的数据类型的名称,采用单数形式。例如,用户表应命名为 yd_user 而不是 yd_users。
下划线分隔:当需要组合多个单词来描述表时,使用下划线 _ 分隔单词。例如,订单详情表可以命名为 yd_order_detail。
大小写敏感性:除非特定数据库系统要求区分大小写,否则一律使用小写字母。
示例:
用户信息表:yd_user
订单表:yd_order
商品分类表:yd_product_category

为了方便团队成员理解与协作,我们统一使用 有道词典 提供的标准英文翻译作为命名参考。例如,“艺人”对应的英文为 “Artist”。
因此,艺人相关表建议命名为:yd_artist_user。其中:

  • yd_ 表示所有新表的统一前缀;
  • artist 是“艺人”的英文翻译,用于表明业务实体;
  • user 表示该表与用户信息相关联(非强制,根据实际业务灵活添加)。

命名应尽量做到见名知意,保持一致性与可读性。


当然可以,以下是对你提供的“定时任务开发规范”内容的优化版本,使其表达更清晰、专业,便于团队理解与执行:


2. 定时任务开发规范

  1. 业务逻辑不得写在 Job 类中
    所有业务逻辑(包括简单的处理逻辑)都应封装在对应业务表的 ServiceHelper 类中,禁止将任何业务逻辑直接写在 Job 页面中
    编写公共方法时,需考虑其可能被实时业务调用,以确保数据一致性。底层通用逻辑建议放置在 Helper 类中,供 Job 和其他业务模块共同调用。

  2. Job 类中的方法长度控制
    Job 类中每个方法原则上不超过 20 行代码,建议控制在 10 行以内。目的是避免业务逻辑堆积在定时任务类中,提高可读性和可维护性。

  3. 注释规范
    每个定时任务必须添加清晰的注释,说明该任务的作用,帮助他人快速理解任务目的。

  4. 参考示例

    • 定时任务编写参考:可在 IDEA 中搜索 UpdateUserLevelJob 查看标准实现;
    • 公共静态方法参考:可在 IDEA 中搜索 determineUserGrade 查看通用逻辑抽取方式。

新增前后端开发规范(2025年5月12日生效)

订单相关表的支付状态统一管理

为了保证系统中所有与订单相关的表在处理支付状态时的一致性和可维护性,特此规定:

  • 后端:必须使用统一的OrderPayStatusEnum枚举来表示支付状态。这包括但不限于数据库操作、业务逻辑处理以及API接口的设计。

    示例代码注释要求

    /**
     * 查询订单信息
     * @param payStatus 使用 OrderPayStatusEnum 枚举作为参数,
     * 确保支付状态的有效性和一致性
     */
    public List<Order> getOrderByPayStatus(OrderPayStatusEnum payStatus) {
        // 方法实现...
    }
    
  • 前端:在展示支付状态时,需使用统一的支付字典ORDER_RECHARGE_ORDER_TYPE='order_recharge_order_type'进行翻译和显示,以确保用户界面的一致性和准确性。

规范实施细节

  1. 后端入参查询:当涉及到支付状态的查询条件时,务必使用相应的枚举类型作为方法参数,并提供清晰的注释说明其用途。

  2. 前端数据展示:在需要向用户展示支付状态的地方,应通过调用统一的支付字典服务获取对应的描述信息,并正确地渲染到页面上。

示例代码

后端代码示例
/**
 * 订单支付状态枚举
 */
@Getter
@AllArgsConstructor
public enum OrderPayStatusEnum {

    PAYING(1, "支付中"),
    PAID(2, "已支付"),
    FAILED(-1, "已失败"),
    REFUNDED(10, "已退款");

    private final Integer code;
    private final String description;

    /**
     * 根据 code 获取对应的枚举
     */
    public static OrderPayStatusEnum fromCode(Integer code) {
        for (OrderPayStatusEnum status : values()) {
            if (status.getCode().equals(code)) {
                return status;
            }
        }
        throw new IllegalArgumentException("Invalid pay status code: " + code);
    }

}


   /**
     * 根据订单ID、订单类型、统计状态和支付状态,检查指定订单是否已更新统计数据。
     *
     * <p>该方法用于查询指定订单在对应的业务表中是否存在未更新统计数据的记录。</p>
     *
     * @param orderId 订单ID,用于匹配数据库中的记录
     * @param orderTypeEnum 订单类型枚举,用于动态选择数据表(例如 DSJ_ORDER_XXX)
     * @param orderUpdateStatistic 统计更新状态枚举(如 UPDATED, NOT_UPDATED)
     * @param orderPayStatusEnum 支付状态枚举(如 PAID, UNPAID)
     * @return 符合条件的记录数量(0 或 1)
     */
    @Select({
            "<script>",
            "SELECT COUNT(*) FROM ${orderTypeEnum.tableName}",
            "WHERE id = #{orderId}",
            "  AND is_update_statistic = #{orderUpdateStatistic.code}",
            "  AND status = #{orderPayStatusEnum.code}",
            "  AND deleted = 0",
            "</script>"
    })
    Integer selectByNoStatisticId(
            @Param("orderId") Long orderId,
            @Param("orderTypeEnum") NotifyRelatedOrderType orderTypeEnum,
            @Param("orderUpdateStatistic") OrderUpdateStatistic orderUpdateStatistic,
            @Param("orderPayStatusEnum") OrderPayStatusEnum orderPayStatusEnum);
前端代码示例
//下拉案列
    <el-select v-model="queryParams.orderType" placeholder="请选择订单类型" clearable class="!w-240px">
      <el-option v-for="dict in getIntDictOptions(DICT_TYPE.ORDER_RECHARGE_ORDER_TYPE)" :key="dict.value" :label="dict.label" :value="dict.value"/>
    </el-select>
//展示案列
 <el-table-column label="订单信息" align="left" prop="vipTitle" width="205px">
        <template #default="scope">
          <div class="flex items-start flex-col">
          <p>类型:<dict-tag :type="DICT_TYPE.ORDER_RECHARGE_ORDER_TYPE" :value="scope.row.orderType" /></p>
          </div>

        </template>
      </el-table-column>

请各团队严格按照上述规范执行,确保系统的稳定性和用户体验的一致性。

新增后端开发规范(2025年6月12日生效)

🎯 目标
为提升系统性能与代码可维护性,避免在循环中频繁调用数据库查询或远程接口,特制定如下开发规范。

✅ 规范说明
在进行分页数据处理时,如需对每条记录补充额外信息(如关联商品、订单等),应遵循以下原则:

完整示例:

 public PageResult<PlayMovieRespVO> getPlayMoviePage(PlayMoviePageReqVO pageReqVO) {

        IPage<PlayMovieRespVO> pageResult = MyBatisUtils.buildPage(pageReqVO);
        playMovieMapper.findAll(pageResult, pageReqVO);
        pageResult.getRecords().forEach(this::formatter);

        List<PlayMovieRespVO> records = pageResult.getRecords();

        // 收集所有 movieIds
        List<Long> movieIds = records.stream()
                .map(PlayMovieRespVO::getId)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());

        // 一次性批量查询关联商品信息
        Map<Long, List<MovieInfoVO>> spuInfoMap;
        if (!movieIds.isEmpty()) {
            List<MovieInfoVO> allSpuInfoList = playMovieMapper.batchGetSpuInfoListByMovieIds(movieIds);
            // 构建 movieId -> spuInfo 列表的映射
            spuInfoMap = allSpuInfoList.stream()
                    .collect(Collectors.groupingBy(MovieInfoVO::getMovieId));
        } else {
            spuInfoMap = new HashMap<>();
        }

        pageResult.getRecords().forEach(playMovieRespVO -> {
            List<AppOrderPlayMovieRespDTO> orderPlayMovieResps = orderPlayMovieApi.getOrderPlayMovieListByMovieId(playMovieRespVO.getId().intValue()).getCheckedData();

            Long movieId = playMovieRespVO.getId();
            // 设置 spuInfoList
            playMovieRespVO.setSpuInfoList(spuInfoMap.getOrDefault(movieId, Collections.emptyList()));

            playMovieRespVO.setPayCount(orderPlayMovieResps.size());
            playMovieRespVO.setPayAmount(orderPlayMovieResps.stream()
                    .map(AppOrderPlayMovieRespDTO::getPayMoney) // 获取每个对象的 playMoney
                    .filter(Objects::nonNull) // 过滤掉可能的 null 值
                    .reduce(BigDecimal.ZERO, BigDecimal::add));
        });
        return new PageResult<>(pageResult.getRecords(), pageResult.getTotal());
    }
  1. 建议先批量查询,再映射使用
    对于需要根据当前数据字段(如 movieId)获取关联信息的场景,应优先将所有目标 ID 收集起来,通过一次批量查询获取全部数据,并将其构造成以主键为 Key 的 Map 结构,在后续遍历中直接通过 Key 获取对应信息。
// 收集所有 movieIds
List<Long> movieIds = records.stream()
       .map(PlayMovieRespVO::getId)
       .filter(Objects::nonNull)
       .collect(Collectors.toList());

// 一次性批量查询关联商品信息
Map<Long, List<MovieInfoVO>> spuInfoMap;
if (!movieIds.isEmpty()) {
   List<MovieInfoVO> allSpuInfoList = playMovieMapper.batchGetSpuInfoListByMovieIds(movieIds);
   // 构建 movieId -> spuInfo 列表的映射
   spuInfoMap = allSpuInfoList.stream()
           .collect(Collectors.groupingBy(MovieInfoVO::getMovieId));
} else {
   spuInfoMap = new HashMap<>();
}
  1. 禁止在循环中单次调用数据库或远程服务
    以下做法是严格禁止的:
pageResult.getRecords().forEach(playMovieRespVO -> {
   // ❌ 每次循环都调用远程接口或数据库,严重影响性能
   List<AppOrderPlayMovieRespDTO> orderPlayMovieResps = 
       orderPlayMovieApi.getOrderPlayMovieListByMovieId(playMovieRespVO.getId().intValue()).getCheckedData();
});

该写法会导致大量重复请求,增加数据库/接口压力,降低系统响应速度。

  1. 推荐做法:统一收集 ID,批量查询后再映射
    应在循环之前统一收集所有需要查询的 ID,执行一次批量查询,之后在循环中从本地 Map 中取值,实现高效处理。

📌 总结建议

场景推荐做法禁止做法
查询关联信息统一收集 ID,批量查询,构造 Map 后映射使用在循环中逐条调用数据库或接口
数据格式化使用 Java Stream API 高效处理集合数据多重嵌套逻辑,影响可读性
分页处理使用分页工具类统一封装逻辑手动拼接 SQL 或分页参数

<think>好的,用户想了解前后端分离项目中如何编写接口文档的示例或生成方法。首先,我需要根据用户提供的引用内容,特别是引用[1]、[2]、[3]、[4]中的信息来组织回答。 用户提到的YAPI平台是一个关键点,引用[1]和[3]提到YAPI用于定义接口文档,前后端根据文档进行开发,并且可以导出文档。引用[2]强调了接口约定的重要性,提到了API Blueprint和阿里内部的类似方案,以及模拟数据的作用。引用[4]给出了一个接口文档的示例结构,比如部门管理中的部门列表查询。 首先,我应该解释接口文档的基本结构,包括接口名称、请求方法、URL、参数、响应示例等,并结合引用[4]的示例来说明。然后,提到使用YAPI平台来生成文档,引用[3]提到添加项目、分类和接口的步骤,可以详细说明。同时,引用[2]中的内容可以强调前后端协作的重要性,以及模拟数据的功能,这样前后端可以并行开发。 接下来,用户可能需要具体的示例,所以需要给出一个简单的示例,比如用户登录接口,展示请求和响应的结构。同时,结合引用[4]的示例结构,比如部门列表查询,可以进一步说明。 关于生成方法,除了YAPI,还可以提到Swagger或OpenAPI,虽然用户没有提到,但这是常见的工具,可能对用户有帮助。同时,引用[2]提到的接口规则和平台,可以建议用户使用类似工具来规范接口。 最后,生成相关问题,需要围绕接口文档的编写方法、工具、最佳实践和测试等方面提问,确保问题覆盖用户可能关心的其他方面。 检查是否所有数学表达式都正确使用了$...$或$$...$$格式,但在这个问题中可能涉及较少数学内容,但如果有示例中的JSON结构或其他代码,需要用代码块包裹。同时确保回答结构清晰,分步骤说明,并引用对应的引用标记。</think>### 前后端分离项目接口文档编写方法 #### 1. 接口文档基本结构 前后端分离项目中,接口文档需包含以下核心内容: - **接口名称**:如“用户登录接口” - **请求方法**:`GET`/`POST`/`PUT`/`DELETE` - **URL路径**:`/api/user/login` - **请求参数**:包括Headers、Query参数、Body参数(JSON格式) - **响应示例**:成功和失败的JSON结构 - **状态码说明**:如200(成功)、401(未授权)、500(服务器错误) 示例(参考引用[4]的部门列表查询结构): ```markdown ### 1.1 部门列表查询 **请求方式**: GET **URL**: /api/department/list **请求参数**: - page: 当前页码(必填) - size: 每页数量(必填) **响应示例**: ```json { "code": 200, "data": [ {"id": 1, "name": "技术部"}, {"id": 2, "name": "市场部"} ] } ``` #### 2. 使用工具生成接口文档 **方法一:YAPI平台(引用[1][3])** 1. **添加项目**:在YAPI中创建对应项目 2. **定义接口分类**:如“用户模块”“订单模块” 3. **编写接口细节**:填写URL、参数、响应体 4. **导出文档**:支持Markdown/HTML格式导出[^3] **方法二:Swagger/OpenAPI** 通过代码注解自动生成文档(需后端框架集成): ```java @ApiOperation("用户登录接口") @PostMapping("/login") public Response<User> login(@RequestBody LoginRequest request) { ... } ``` #### 3. 最佳实践建议 1. **接口版本管理**:URL中标注版本号,如`/api/v1/user` 2. **字段类型明确定义**:如`string`/`number`/`boolean` 3. **错误码统一规范**:预定义`4001=参数缺失`等错误类型 4. **使用Mock数据**:在YAPI中配置模拟响应,支持前后端并行开发(引用[2])[^2] #### 4. 协作流程示例 1. 后端在YAPI定义接口并生成Mock服务 2. 前端根据Mock数据开发功能 3. 联调时切换为真实接口 4. 迭代时通过版本号管理变更[^1]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值