api.ts.ftl 和 types.ts.ftl 模板文件参考示例2

以下是企业级标准、完全符合现代前端开发规范、并添加了详尽中文注释api.ts.ftltypes.ts.ftl 模板文件。

这两个模板是前后端分离架构的核心,用于生成TypeScript 安全调用的 API 客户端和精确的类型定义,确保前端代码与后端接口零歧义、零错误、零沟通成本


✅ 一、api.ts.ftl —— API 客户端模板(基于 Axios)

作用:为每个实体生成一个独立的 API 模块,提供类型安全语义清晰的 HTTP 请求方法。

<#--
  =================================================================================================
  MyBatis-Plus 代码生成器 - API 客户端模板 (TypeScript)
  =================================================================================================
  作者:CodeGenWeb
  生成时间:${now?string("yyyy-MM-dd HH:mm:ss")}
  数据库表:${table.comment}(表名:${table.name})
  
  说明:
  1. 本模板生成一个独立的 API 模块,命名为 ${entityNameLower}Api,用于与后端 ${table.comment!""} 接口交互。
  2. 所有方法均使用 Axios 发起 HTTP 请求,返回 Promise,支持 async/await3. 使用 TypeScript 泛型精确描述请求和响应结构,确保类型安全。
  4. 所有方法均包含 JSDoc 注释,IDE 支持 Hover 提示、参数自动补全。
  5. 响应格式统一为 R<T>,与后端保持一致。
  6. 本文件为 Freemarker 模板,生成时会自动替换 ${} 中的变量。
  7. 请勿手动修改生成的文件,下次生成将覆盖!
  =================================================================================================
-->

import axios from 'axios';
import { ${entity}Type, ${entity}WithDeletedType } from '@/types/${entityNameLower}.types';
import { PageResult } from '@/types/page.types';

// API 基础路径,与后端 Controller 的 @RequestMapping 保持一致
const BASE_URL = '/api/${table.entityName}';

/**
 * ${table.comment!""} API 客户端
 * 
 * <p>本模块提供对 ${table.comment!""} 资源的所有标准 CRUD 操作。</p>
 * <p>所有方法均返回 Promise,可配合 async/await 使用。</p>
 * <p>响应格式统一为:{ code: number, msg: string, data: T },由后端的 R<T> 类型定义。</p>
 * 
 * @author ${author}
 * @date ${now?string("yyyy-MM-dd")}
 */
export const ${entityNameLower}Api = {
  /**
   * 获取 ${table.comment!""} 列表(分页)
   * 
   * <p>用于前端表格展示,支持自定义页码和每页大小。</p>
   * 
   * @param params 分页参数
   *   - current: 当前页码(从 1 开始),默认值 1
   *   - size: 每页记录数,默认值 10,最大建议不超过 100
   * @returns Promise<PageResult<${entity}Type>> 包含数据列表、总记录数、分页信息
   * @example
   * const res = await ${entityNameLower}Api.list({ current: 1, size: 20 });
   * console.log(res.data.records); // 当前页数据
   * console.log(res.data.total);   // 总记录数
   */
  list(params: { current?: number; size?: number } = {}) {
    return axios.get<PageResult<${entity}Type>>(BASE_URL + '/page', { params });
  },

  /**
   * 获取所有 ${table.comment!""}(无分页)
   * 
   * <p>适用于数据量较小的场景,如下拉框、标签选择器等。</p>
   * <p>注意:当数据量较大时,请使用分页接口,避免内存溢出。</p>
   * 
   * @returns Promise<${entity}Type[]> 所有 ${table.comment!""} 的数组
   * @example
   * const users = await ${entityNameLower}Api.getAll();
   */
  getAll() {
    return axios.get<${entity}Type[]>(BASE_URL + '/list');
  },

  /**
   * 根据 ID 查询单个 ${table.comment!""}
   * 
   * <p>用于详情页加载、编辑前预填充数据。</p>
   * 
   * @param id ${table.comment!""} 的主键 ID(必须为数字)
   * @returns Promise<${entity}Type> 单个 ${table.comment!""} 对象,未找到时后端返回 404
   * @example
   * const user = await ${entityNameLower}Api.get(1);
   */
  get(id: number) {
    return axios.get<${entity}Type>(`${BASE_URL}/${id}`);
  },

  /**
   * 新增一条 ${table.comment!""} 记录
   * 
   * <p>前端传入的数据应为创建所需字段,**不包含 id**。</p>
   * <p>后端会自动生成主键,并填充 createTime 等自动字段。</p>
   * 
   * @param data 创建所需的数据对象,类型为 Omit<${entity}Type, 'id'>,即排除 id 字段
   * @returns Promise<boolean> 操作是否成功
   * @example
   * const success = await ${entityNameLower}Api.create({
   *   username: 'zhangsan',
   *   email: 'zhangsan@example.com'
   * });
   */
  create(data: Omit<${entity}Type, 'id'>) {
    return axios.post<boolean>(BASE_URL, data);
  },

  /**
   * 修改一条 ${table.comment!""} 记录
   * 
   * <p>前端必须传入完整的对象,**必须包含 id** 字段。</p>
   * <p>后端会根据 id 更新对应记录,未传入的字段保持原值。</p>
   * 
   * @param data 包含 id 的完整 ${table.comment!""} 对象
   * @returns Promise<boolean> 操作是否成功
   * @example
   * const success = await ${entityNameLower}Api.update({
   *   id: 1,
   *   username: 'zhangsan_updated',
   *   email: 'newemail@example.com'
   * });
   */
  update(data: ${entity}Type) {
    return axios.put<boolean>(BASE_URL, data);
  },

  /**
   * 删除一条 ${table.comment!""} 记录(物理删除)
   * 
   * <p>执行物理删除,不可恢复。生产环境建议使用逻辑删除(deleted=1)。</p>
   * 
   * @param id ${table.comment!""} 的主键 ID
   * @returns Promise<boolean> 操作是否成功
   * @example
   * const success = await ${entityNameLower}Api.delete(1);
   */
  delete(id: number) {
    return axios.delete<boolean>(`${BASE_URL}/${id}`);
  }
};

✅ 模板说明(关键点)

特性说明
Omit<${entity}Type, 'id'>核心安全机制!确保新增时前端不会误传 id,后端安全可控。
PageResult<T>统一分页响应类型,前端无需为每个接口重复定义。
JSDoc 详尽每个方法都有 @param@returns@exampleIDE 悬停即懂
URL 与后端严格一致/api/${table.entityName} 与后端 Controller 保持一致,避免 404。
泛型精确axios.get<PageResult<${entity}Type>> 确保 TypeScript 编译时校验。
注释规范使用中文,符合国内团队习惯,新人 10 分钟上手
文件命名user.api.ts,命名清晰,便于查找。

✅ 二、types.ts.ftl —— 类型定义模板(TypeScript Interface)

作用:为每个实体定义精确的 TypeScript 类型,包括基础类型、带删除标记的类型、状态枚举等。

<#--
  =================================================================================================
  MyBatis-Plus 代码生成器 - TypeScript 类型定义模板
  =================================================================================================
  作者:CodeGenWeb
  生成时间:${now?string("yyyy-MM-dd HH:mm:ss")}
  数据库表:${table.comment}(表名:${table.name})
  
  说明:
  1. 本模板定义了 ${table.comment!""} 的所有 TypeScript 类型,供 API、组件、状态管理使用。
  2. 所有类型都基于数据库字段注释生成,确保与后端数据结构完全一致。
  3. 使用 `interface` 定义,支持扩展和继承。
  4. 时间字段统一为 string 类型,因为后端返回 ISO 格式字符串(如 "2024-07-05T10:30:00")。
  5. 本文件为 Freemarker 模板,生成时会自动替换 ${} 中的变量。
  6. 请勿手动修改生成的文件,下次生成将覆盖!
  =================================================================================================
-->

/**
 * ${table.comment!""} 实体类型定义
 * 
 * <p>此类型定义了 ${table.comment!""} 的所有业务字段,用于前后端数据交互。</p>
 * <p>所有字段均来源于数据库表 ${table.name} 的结构。</p>
 * 
 * @author ${author}
 * @date ${now?string("yyyy-MM-dd")}
 */
export interface ${entity}Type {
<#list table.fields as field>
    <#-- 字段注释:从数据库注释获取,优先显示中文 -->
    <#if field.comment??>
    /**
     * ${field.comment!""}
     */
    </#if>
    
    <#-- 类型映射规则 -->
    <#-- 字符串 -->
    <#if field.type == "String">
    ${field.name}: string;
    <#-- 数字(Long, Integer, Short, Byte, Double, Float) -->
    <#elseif field.type == "Long" || field.type == "Integer" || field.type == "Short" || field.type == "Byte" || field.type == "Double" || field.type == "Float">
    ${field.name}: number;
    <#-- 布尔值 -->
    <#elseif field.type == "Boolean">
    ${field.name}: boolean;
    <#-- 时间类型(Date, LocalDateTime, Timestamp) -->
    <#elseif field.type == "Date" || field.type == "LocalDateTime" || field.type == "Timestamp">
    ${field.name}: string;  // 后端返回 ISO 格式字符串,如 "2024-07-05T10:30:00"
    <#-- 其他未知类型 -->
    <#else>
    ${field.name}: any;     // 未知类型兜底,建议在数据库中明确类型
    </#if>
</#list>
}

<#-- 逻辑删除标记类型 -->
<#if table.logicDeleteField??>
/**
 * ${table.comment!""} 带逻辑删除标记的类型
 * 
 * <p>此类型用于需要处理软删除状态的场景,如列表查询时包含已删除记录。</p>
 * 
 * @author ${author}
 * @date ${now?string("yyyy-MM-dd")}
 */
export type ${entity}WithDeletedType = ${entity}Type & { ${table.logicDeleteField.name}: number };
</#if>

<#-- 状态枚举类型(示例) -->
<#-- 你可以根据数据库中状态字段(如 status)的值来定义枚举 -->
<#-- 示例:如果表中有一个字段 status,值为 1=启用, 0=禁用 -->
<#-- 你可以在模板中添加逻辑判断,此处为简化示例 -->
<#-- 若需要更智能的枚举生成,请扩展模板逻辑 -->
export type ${entity}Status = 'active' | 'inactive'; // 示例:启用/禁用

<#-- 以下为预留扩展类型示例(请根据业务手动添加) -->
<#-- /**
<#--  * ${table.comment!""} 创建请求参数类型
<#--  * 用于新增时,仅包含必要字段
<#--  */
<#-- export interface ${entity}CreateRequest {
<#--   username: string;
<#--   email: string;
<#-- } -->
<#-- 
<#-- /**
<#--  * ${table.comment!""} 更新请求参数类型
<#--  * 用于更新时,所有字段可选
<#--  */
<#-- export interface ${entity}UpdateRequest extends Partial<${entity}Type> {} -->

✅ 模板说明(关键点)

特性说明
精确字段映射数据库 VARCHARstringBIGINTnumberDATETIMEstring类型安全
Omit<..., 'id'> 支持api.ts.ftl 中的 create() 方法提供类型支持,前端传参无误。
${entity}WithDeletedType核心设计!区分“仅有效数据”和“含删除标记数据”,避免业务逻辑混乱。
时间字段为 string最佳实践!后端返回 ISO 字符串,前端用 new Date() 解析,避免时区问题。
枚举类型预留提供 status 枚举示例,强烈建议为每个状态字段定义枚举,提升可读性。
扩展预留预留了 CreateRequestUpdateRequest 示例,引导开发者使用请求类型分离
注释完整每个类型都有中文说明,明确用途和使用场景。

✅ 三、配套:page.types.ts(分页类型定义)

建议:在项目中创建一个全局的 types/page.types.ts 文件,避免每个 API 文件重复定义。

<#--
  =================================================================================================
  MyBatis-Plus 代码生成器 - 分页类型定义模板 (全局)
  =================================================================================================
  作者:CodeGenWeb
  生成时间:${now?string("yyyy-MM-dd HH:mm:ss")}
  
  说明:
  1. 本文件定义了统一的分页响应类型,供所有 API 模块复用。
  2. 与后端 MyBatis-Plus 的 IPage<T> 结构完全一致。
  3. 请将此文件放在项目公共类型目录下,如:src/types/page.types.ts
  =================================================================================================
-->

/**
 * 分页结果通用类型
 * 
 * <p>与后端 MyBatis-Plus 的 IPage<T> 结构完全一致。</p>
 * 
 * @author ${author}
 * @date ${now?string("yyyy-MM-dd")}
 */
export interface PageResult<T> {
  records: T[];     // 当前页的数据列表
  total: number;    // 总记录数
  current: number;  // 当前页码(从 1 开始)
  size: number;     // 每页大小
  pages: number;    // 总页数
}

✅ 在 api.ts.ftl 中直接引用:import { PageResult } from '@/types/page.types';


✅ 最终效果与最佳实践

场景前端代码示例类型安全保障
获取分页列表const res = await userApi.list({current: 1, size: 10});res.data.recordsUserType[]res.data.totalnumber
新增用户await userApi.create({username: "张三", email: "zhangsan@example.com"});编译器报错:id 不能传入
更新用户await userApi.update({id: 1, username: "李四", email: "lisi@example.com"});必须传入 id,否则报错
获取详情const user = await userApi.get(1);user.dataUserType,属性自动补全
状态判断if (user.status === 'active') { ... }user.status 类型为 `‘active’

✅ 企业级建议

建议说明
所有 API 文件放在 src/api/ 目录src/api/user.api.ts, src/api/product.api.ts
所有类型文件放在 src/types/ 目录src/types/user.types.ts, src/types/page.types.ts
使用 Partial<T> 定义更新请求export type UserUpdateRequest = Partial<UserType>
为枚举字段定义类型如 `export type UserStatus = ‘active’
使用 axios.interceptors 统一处理 tokenapi/axios.ts 中封装,避免每个 API 文件重复写
集成 Swagger 生成前端类型可选:使用 swagger-typescript-api 工具自动生成,但本方案更轻量可控

✅ 总结

“前端的类型安全,不是靠运气,而是靠规范。”

你已拥有:

  • user.api.ts安全、语义清晰、类型精确的 API 调用模块。
  • user.types.ts与数据库 100% 一致的 TypeScript 类型定义。
  • page.types.ts全局复用的分页响应类型。

复制此模板,你的前端项目将拥有业界标准的 API + 类型系统
从此,你的团队不再为“接口字段名拼错了”、“传了 id 但后端报错”、“状态值写错了”而 Debug
你的代码,就是最好的文档


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙茶清欢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值