nullable 字段用了? 而不是!, 类型没加 null 错误示例:
@Column({
name: 'description',
type: 'text',
nullable: true,
comment: '容器配置描述'
})
description?: string;
正确示例:
@Column({
name: 'description',
type: 'text',
nullable: true,
comment: '容器配置描述'
})
description!: string | null;
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('table_name') // 使用小写下划线命名表名
export class EntityName {
// 字段定义...
}
@PrimaryGeneratedColumn({ unsigned: true }) // 必须使用无符号整数
id!: number; // 使用非空断言(!)和明确类型
@Column({
name: '字段名称', // 必须添加并与数据表字段名称一致
type: 'varchar', // 明确指定数据库类型
length: 255, // 字符串必须指定长度
nullable: true, // 明确是否可为空
default: undefined, // 默认值(可选)
comment: '字段说明' // 必须添加中文注释
})
fieldName!: FieldType; // 类型必须明确
| 业务类型 | 数据库类型 | TypeScript 类型 |
|---|---|---|
| 主键ID | int unsigned | number |
| 短文本 | varchar(length) | string |
| 长文本 | text | string |
| 整数 | int | number |
| 小数 | decimal(precision,scale) | number |
| 布尔状态 | tinyint | number (0/1) |
| 日期时间 | timestamp | Date |
// 禁用状态 (0启用 1禁用)
@Column({ name: 'is_disabled', type: 'tinyint', default: 1 })
isDisabled!: number;
// 删除状态 (0未删除 1已删除)
@Column({ name: 'is_deleted',type: 'tinyint', default: 0 })
isDeleted!: number;
// 创建时间 (自动设置)
@Column({
name: 'created_at',
type: 'timestamp',
default: () => 'CURRENT_TIMESTAMP'
})
createdAt!: Date;
// 更新时间 (自动更新)
@Column({
name: 'updated_at',
type: 'timestamp',
default: () => 'CURRENT_TIMESTAMP',
onUpdate: 'CURRENT_TIMESTAMP'
})
updatedAt!: Date;
import { z } from '@hono/zod-openapi';
export const EntitySchema = z.object({
id: z.number().int('必须是整数').positive('必须是正整数').openapi({ description: 'ID说明' }),
// 字符串字段
fieldName: z.string()
.min(1, '不能为空')
.max(255, '最多255个字符')
.nullable()
.openapi({
description: '字段说明',
example: '示例值'
}),
// 数字字段
numberField: z.number()
.int('必须是整数')
.positive('必须是正数')
.min(1, '最小值为1')
.max(9999, '最大值为9999')
.default(默认值)
.openapi({...}),
// 日期字段
dateField: z.coerce.date('日期格式不正确').openapi({...})
});
对于URL参数或表单数据中的数字类型,必须使用z.coerce.number()进行类型转换,以确保字符串到数字的正确转换:
// 整数类型
z.coerce.number().int('必须是整数').positive('必须是正整数').openapi({
description: '正整数ID',
example: 1
});
// 小数类型
z.coerce.number().multipleOf(0.01, '最多保留两位小数').openapi({
description: '金额,保留两位小数',
example: 19.99
});
// 状态类型(0/1)
z.coerce.number().int('必须是整数').min(0, '最小值为0').max(1, '最大值为1').openapi({
description: '状态(0-禁用,1-启用)',
example: 1
});
对于日期时间类型,必须使用z.coerce.date()进行类型转换:
// 日期时间类型
z.coerce.date('日期格式不正确').openapi({
description: '创建时间',
example: '2023-10-01T12:00:00Z'
});
// 日期范围查询
const DateRangeSchema = z.object({
startDate: z.coerce.date('开始日期格式不正确').openapi({
description: '开始日期',
example: '2023-10-01T00:00:00Z'
}),
endDate: z.coerce.date('结束日期格式不正确').openapi({
description: '结束日期',
example: '2023-10-31T23:59:59Z'
})
});
对于布尔类型参数,必须使用z.coerce.boolean()进行类型转换:
// 布尔类型
z.coerce.boolean('必须是布尔值').openapi({
description: '是否启用',
example: true
});
创建(Create)和更新(Update)Schema必须遵循以下额外规范:
创建Schema:
id字段(由数据库自动生成)optional()optional()必须使用适当的coerce方法处理非字符串类型
export const CreateEntityDto = z.object({
name: z.string().min(1, '名称不能为空').max(255, '名称最多255个字符').openapi({
description: '名称',
example: '示例名称'
}),
quantity: z.coerce.number().int('数量必须是整数').min(1, '数量最小为1').max(9999, '数量最大为9999').openapi({
description: '数量',
example: 10
}),
price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0.01, '价格必须大于0').max(999999.99, '价格不能超过999999.99').openapi({
description: '价格',
example: 99.99
}),
isActive: z.coerce.boolean('是否激活必须是布尔值').default(true).openapi({
description: '是否激活',
example: true
}),
expireDate: z.coerce.date('过期日期格式不正确').min(new Date(), '过期日期不能早于当前时间').openapi({
description: '过期日期',
example: '2024-12-31T23:59:59Z'
}),
// 选填字段示例
// nullable字段必须显式添加optional()
description: z.string().max(500, '描述最多500个字符').nullable().optional().openapi({
description: '商品描述(选填)',
example: '这是一个可选的商品描述信息'
})
});
更新Schema:
id字段(通过URL参数传递)optional()必须使用适当的coerce方法处理非字符串类型
export const UpdateEntityDto = z.object({
name: z.string().min(1, '名称不能为空').max(255, '名称最多255个字符').optional().openapi({
description: '名称',
example: '更新后的名称'
}),
quantity: z.coerce.number().int('数量必须是整数').min(1, '数量最小为1').max(9999, '数量最大为9999').optional().openapi({
description: '数量',
example: 20
}),
price: z.coerce.number().multipleOf(0.01, '价格最多保留两位小数').min(0.01, '价格必须大于0').max(999999.99, '价格不能超过999999.99').optional().openapi({
description: '价格',
example: 89.99
}),
isActive: z.coerce.boolean('是否激活必须是布尔值').optional().openapi({
description: '是否激活',
example: false
}),
expireDate: z.coerce.date('过期日期格式不正确').optional().openapi({
description: '过期日期',
example: '2025-12-31T23:59:59Z'
})
});
```