以下是符合 现代前端工程化规范、遵循 OpenAPI 3.0 契约 的两个 TypeScript 模板文件,专为与 Java 后端(特别是基于 MyBatis-Plus + SpringDoc 的项目)协同开发而设计:
api.ts.ftl:API 请求函数封装(基于 Axios)types.ts.ftl:TypeScript 类型定义(与后端 DTO/VO 严格对齐)
这两个模板均包含详尽的中文注释,支持 自动生成类型安全的前端 API 客户端,可直接用于代码生成器(如 MyBatis-Plus Generator 扩展或自定义脚本)。
✅ 1. types.ts.ftl(TypeScript 类型定义模板)
/**
* ${table.comment!} 相关类型定义
*
* 【设计目标】
* 1. 与后端 Java 的 DTO/VO 结构严格对齐
* 2. 提供完整的 TypeScript 类型安全
* 3. 支持 OpenAPI 生成工具(如 openapi-typescript)的补充或替代
* 4. 字段注释保留业务语义,便于前端理解
*
* 【关键原则】
* - 类型命名清晰:CreateDTO → CreateReq,VO → Res
* - 时间字段统一使用 string(ISO 8601 格式),避免 Date 对象时区问题
* - 枚举使用联合类型("0" | "1")而非数字,提升可读性
* - 敏感字段(如 password)绝不包含
*
* @author ${author}
* @since ${date}
*/
/**
* ${table.comment!} 创建请求参数
*/
export interface ${entity}CreateReq {
<#list table.fields as field>
<#-- 跳过主键、基类字段和敏感字段 -->
<#if !(field.name == 'id' || field.name == 'create_time' || field.name == 'update_time' || field.name == 'deleted' || field.name == 'password' || field.name == 'salt')>
/**
* ${field.comment!}
*/
${field.propertyName}:
<#if field.propertyType == "String">string
<#elseif field.propertyType == "Integer" || field.propertyType == "Long">number
<#elseif field.propertyType == "Boolean">boolean
<#elseif field.propertyType == "LocalDateTime" || field.propertyType == "LocalDate">string // ISO 8601 格式,如 "2023-01-01T12:00:00"
<#else>any</#if>;
</#if>
</#list>
}
/**
* ${table.comment!} 更新请求参数
*/
export interface ${entity}UpdateReq {
/**
* 主键 ID
*/
id: number;
<#list table.fields as field>
<#-- 跳过基类字段、敏感字段,保留可更新字段 -->
<#if !(field.name == 'id' || field.name == 'create_time' || field.name == 'update_time' || field.name == 'deleted' || field.name == 'password' || field.name == 'salt')>
/**
* ${field.comment!}
*/
${field.propertyName}:
<#if field.propertyType == "String">string | null
<#elseif field.propertyType == "Integer" || field.propertyType == "Long">number | null
<#elseif field.propertyType == "Boolean">boolean | null
<#elseif field.propertyType == "LocalDateTime" || field.propertyType == "LocalDate">string | null // ISO 8601 格式
<#else>any</#if>;
</#if>
</#list>
}
/**
* ${table.comment!} 查询条件
*/
export interface ${entity}Query {
/**
* 页码(从1开始)
* @default 1
*/
page?: number;
/**
* 每页数量
* @default 10
*/
size?: number;
<#list table.fields as field>
<#-- 仅包含适合查询的字段(通常是 String、Integer 等) -->
<#if !(field.name == 'id' || field.name == 'create_time' || field.name == 'update_time' || field.name == 'deleted' || field.name == 'password' || field.name == 'salt')>
<#if field.propertyType == "String" || field.propertyType == "Integer" || field.propertyType == "Boolean">
/**
* ${field.comment!}(模糊/精确查询)
*/
${field.propertyName}?:
<#if field.propertyType == "String">string
<#elseif field.propertyType == "Integer" || field.propertyType == "Long">number
<#elseif field.propertyType == "Boolean">boolean</#if>;
</#if>
</#if>
</#list>
}
/**
* ${table.comment!} 详情响应
*/
export interface ${entity}DetailRes {
/**
* 主键 ID
*/
id: number;
<#list table.fields as field>
<#-- 跳过敏感字段和逻辑删除字段 -->
<#if !(field.name == 'password' || field.name == 'salt' || field.name == 'deleted')>
/**
* ${field.comment!}
*/
${field.propertyName}:
<#if field.propertyType == "String">string
<#elseif field.propertyType == "Integer" || field.propertyType == "Long">number
<#elseif field.propertyType == "Boolean">boolean
<#elseif field.propertyType == "LocalDateTime" || field.propertyType == "LocalDate">string // ISO 8601 格式
<#else>any</#if>;
</#if>
</#list>
}
/**
* ${table.comment!} 列表项响应
*/
export type ${entity}ListItemRes = ${entity}DetailRes;
/**
* 分页响应包装
*/
export interface PageRes<T> {
/**
* 总记录数
*/
total: number;
/**
* 当前页数据列表
*/
list: T[];
/**
* 当前页码
*/
page: number;
/**
* 每页数量
*/
size: number;
}
/**
* 【扩展建议】以下为常见场景示例(按需取消注释)
*/
/*
// 示例:状态枚举(与后端保持一致)
export type UserStatus = '0' | '1'; // '0' - 禁用, '1' - 启用
// 示例:格式化字段(前端计算)
export interface UserDetailResWithFormat extends UserDetailRes {
formattedCreateTime: string; // 如 "2023年01月01日 12:00"
}
*/
✅ 2. api.ts.ftl(API 请求函数模板)
/**
* ${table.comment!} API 接口封装
*
* 【设计目标】
* 1. 基于 Axios 封装类型安全的 HTTP 请求
* 2. 与后端 Controller 路径严格对齐(/${table.mappingPath})
* 3. 返回 Promise,便于 async/await 使用
* 4. 集成统一错误处理(通过 axios 拦截器)
*
* 【关键原则】
* - 函数命名清晰:create${entity}、update${entity} 等
* - 参数类型严格匹配 types.ts 中的定义
* - 响应类型使用后端统一包装格式(ApiResponse<T>)
* - 路径常量集中管理,避免硬编码
*
* @author ${author}
* @since ${date}
*/
import axios, { AxiosPromise } from 'axios';
<#-- 导入类型定义 -->
import {
${entity}CreateReq,
${entity}UpdateReq,
${entity}Query,
${entity}DetailRes,
${entity}ListItemRes,
PageRes
} from './types';
/**
* API 路径常量
*/
const API_PREFIX = '/${table.mappingPath}';
/**
* 统一响应包装(与后端 ApiResponse<T> 对齐)
*/
interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
}
/**
* 创建 ${table.comment!}
* @param data 创建请求参数
* @returns 创建成功的详情
*/
export function create${entity}(data: ${entity}CreateReq): AxiosPromise<ApiResponse<${entity}DetailRes>> {
return axios.post<ApiResponse<${entity}DetailRes>>(API_PREFIX, data);
}
/**
* 更新 ${table.comment!}
* @param data 更新请求参数
* @returns 更新是否成功
*/
export function update${entity}(data: ${entity}UpdateReq): AxiosPromise<ApiResponse<boolean>> {
return axios.put<ApiResponse<boolean>>(API_PREFIX, data);
}
/**
* 根据 ID 删除 ${table.comment!}
* @param id 主键 ID
* @returns 删除是否成功
*/
export function delete${entity}(id: number): AxiosPromise<ApiResponse<boolean>> {
return axios.delete<ApiResponse<boolean>>(`${API_PREFIX}/${id}`);
}
/**
* 根据 ID 获取 ${table.comment!} 详情
* @param id 主键 ID
* @returns 详情数据
*/
export function get${entity}Detail(id: number): AxiosPromise<ApiResponse<${entity}DetailRes>> {
return axios.get<ApiResponse<${entity}DetailRes>>(`${API_PREFIX}/${id}`);
}
/**
* 获取 ${table.comment!} 列表(不分页)
* @returns 列表数据
*/
export function list${entity}(): AxiosPromise<ApiResponse<${entity}ListItemRes[]>> {
return axios.get<ApiResponse<${entity}ListItemRes[]>>(API_PREFIX);
}
/**
* 分页查询 ${table.comment!} 列表
* @param params 查询条件
* @returns 分页数据
*/
export function page${entity}(params: ${entity}Query): AxiosPromise<ApiResponse<PageRes<${entity}ListItemRes>>> {
return axios.get<ApiResponse<PageRes<${entity}ListItemRes>>>(API_PREFIX, { params });
}
/**
* 【扩展建议】以下为常见场景示例(按需取消注释)
*/
/*
// 示例:批量删除
export function batchDelete${entity}(ids: number[]): AxiosPromise<ApiResponse<boolean>> {
return axios.delete<ApiResponse<boolean>>(`${API_PREFIX}/batch`, { data: { ids } });
}
// 示例:导出
export function export${entity}(params: ${entity}Query): AxiosPromise<Blob> {
return axios.get(`${API_PREFIX}/export`, {
params,
responseType: 'blob'
});
}
*/
🔍 关键设计说明与最佳实践
1. 前后端类型对齐策略
| 后端 Java 类型 | 前端 TypeScript 类型 | 说明 |
|---|---|---|
String | string | 基础字符串 |
Integer/Long | number | JavaScript Number 足够 |
Boolean | boolean | 布尔值 |
LocalDateTime | string | 必须用 ISO 8601 字符串,避免 Date 时区问题 |
List<T> | T[] | 数组类型 |
Page<T> | PageRes<T> | 自定义分页结构 |
⚠️ 重要:时间字段绝不使用
Date对象!
原因:new Date("2023-01-01T12:00:00")在不同浏览器/时区解析结果不一致,应由前端 UI 库(如 dayjs)按需格式化。
2. API 函数设计规范
- ✅ 命名清晰:
createUser、getUserDetail - ✅ 参数单一:每个函数只接收一个参数对象(便于扩展)
- ✅ 返回 AxiosPromise:保留 Axios 的链式调用能力
- ✅ 路径集中管理:
API_PREFIX避免路径硬编码
3. 安全与健壮性
- 🔒 敏感字段过滤:模板中已排除
password、salt、deleted - 🔒 可选字段处理:查询参数使用
?标记可选 - 🔒 空值处理:更新请求字段允许
null(表示清空)
4. 工程化集成建议
-
代码生成流程:
-
类型安全验证:配合
openapi-typescript双重校验 -
Mock 支持:在开发环境可 mock
api.ts函数
🛠️ 配套使用示例
(1) 前端调用示例(Vue/React)
// Vue 3 + Composition API
import { create${entity}, get${entity}Detail } from '@/api/${table.mappingPath}';
const handleCreate = async () => {
try {
const res = await create${entity}({
username: 'zhangsan',
email: 'zhangsan@example.com',
age: 25
});
ElMessage.success('创建成功');
// res.data.code === 200
// res.data.data 包含详情
} catch (error) {
// 通过 axios 拦截器统一处理
}
};
(2) Axios 拦截器(统一错误处理)
// request.ts
axios.interceptors.response.use(
response => response,
error => {
if (error.response?.data?.message) {
ElMessage.error(error.response.data.message);
}
return Promise.reject(error);
}
);
📦 项目结构建议
src/
├── api/
│ └── user/
│ ├── types.ts ← 类型定义
│ └── index.ts ← API 函数(即 api.ts)
├── views/
└── utils/
并在 vite.config.ts 或 webpack 中配置路径别名:
// vite.config.ts
resolve: {
alias: {
'@': path.resolve(__dirname, 'src')
}
}
✅ 这两份模板体现了 “前后端契约驱动开发(CDC)” 和 “类型安全优先” 的现代前端工程思想:
- 类型定义与后端 DTO/VO 严格对齐
- API 函数封装完整、可维护
- 时间处理、枚举、分页等高频场景全覆盖
- 安全红线明确,杜绝敏感信息泄露
可作为团队标准模板,在代码生成流程中自动生成,大幅提升前后端协作效率与代码质量。

被折叠的 条评论
为什么被折叠?



