10-entity.md 7.1 KB

数据库实体规范

常见不规范问题

  1. nullable 字段用了? 而不是!, 类型没加 null 错误示例:

      @Column({
        name: 'description',
        type: 'text',
        nullable: true,
        comment: '容器配置描述'
      })
      description?: string;
    

    正确示例:

      @Column({
        name: 'description',
        type: 'text',
        nullable: true,
        comment: '容器配置描述'
      })
      description!: string | null;
    

1. 实体基础结构

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity('table_name') // 使用小写下划线命名表名
export class EntityName {
  // 字段定义...
}

2. 主键定义规范

@PrimaryGeneratedColumn({ unsigned: true }) // 必须使用无符号整数
id!: number; // 使用非空断言(!)和明确类型

3. 列定义规范

@Column({ 
  name: '字段名称',       // 必须添加并与数据表字段名称一致
  type: 'varchar',       // 明确指定数据库类型
  length: 255,           // 字符串必须指定长度
  nullable: true,        // 明确是否可为空
  default: undefined,    // 默认值(可选)
  comment: '字段说明'     // 必须添加中文注释
})
fieldName!: FieldType;   // 类型必须明确

4. 数据类型规范

业务类型 数据库类型 TypeScript 类型
主键ID int unsigned number
短文本 varchar(length) string
长文本 text string
整数 int number
小数 decimal(precision,scale) number
布尔状态 tinyint number (0/1)
日期时间 timestamp Date

5. 状态字段规范

// 禁用状态 (0启用 1禁用)
@Column({ name: 'is_disabled', type: 'tinyint', default: 1 })
isDisabled!: number;

// 删除状态 (0未删除 1已删除) 
@Column({ name: 'is_deleted',type: 'tinyint', default: 0 })
isDeleted!: number;

6. 时间字段规范

// 创建时间 (自动设置)
@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;

7. Zod Schema 规范

7.1 基础类型规范

import { z } from '@hono/zod-openapi';
export const EntitySchema = z.object({
  id: z.number().int().positive().openapi({ description: 'ID说明' }),
  // 字符串字段
  fieldName: z.string()
    .max(255)
    .nullable()
    .openapi({
      description: '字段说明',
      example: '示例值'
    }),
  // 数字字段
  numberField: z.number()
    .default(默认值)
    .openapi({...}),
  // 日期字段
  dateField: z.date().openapi({...})
});

7.2 数据类型转换规范

7.2.1 数字类型转换

对于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).max(1).openapi({
  description: '状态(0-禁用,1-启用)',
  example: 1
});

7.2.2 日期类型转换

对于日期时间类型,必须使用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'
  })
});

7.2.3 布尔类型转换

对于布尔类型参数,必须使用z.coerce.boolean()进行类型转换:

// 布尔类型
z.coerce.boolean().openapi({
  description: '是否启用',
  example: true
});

7.3 创建/更新Schema特殊规范

创建(Create)和更新(Update)Schema必须遵循以下额外规范:

  1. 创建Schema

    • 不包含id字段(由数据库自动生成)
    • 所有必填字段必须显式定义,不得为optional()
    • 如字段为选填项(包括数据库允许为null的字段),必须显式添加optional()
    • 必须使用适当的coerce方法处理非字符串类型

      export const CreateEntityDto = z.object({
      name: z.string().max(255).openapi({
      description: '名称',
      example: '示例名称'
      }),
      quantity: z.coerce.number().int().min(1).openapi({
      description: '数量',
      example: 10
      }),
      price: z.coerce.number().multipleOf(0.01).openapi({
      description: '价格',
      example: 99.99
      }),
      isActive: z.coerce.boolean().default(true).openapi({
      description: '是否激活',
      example: true
      }),
      expireDate: z.coerce.date().openapi({
      description: '过期日期',
      example: '2024-12-31T23:59:59Z'
      }),
      // 选填字段示例
      // nullable字段必须显式添加optional()
      description: z.string().max(500).nullable().optional().openapi({
      description: '商品描述(选填)',
      example: '这是一个可选的商品描述信息'
      })
      });
      
  2. 更新Schema

    • 不包含id字段(通过URL参数传递)
    • 所有字段必须为optional()
    • 必须使用适当的coerce方法处理非字符串类型

      export const UpdateEntityDto = z.object({
      name: z.string().max(255).optional().openapi({
      description: '名称',
      example: '更新后的名称'
      }),
      quantity: z.coerce.number().int().min(1).optional().openapi({
      description: '数量',
      example: 20
      }),
      price: z.coerce.number().multipleOf(0.01).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'
      })
      });
      

      ```

      8. 命名规范

      • 实体类名:PascalCase (如 RackInfo)
      • 表名:snake_case (如 rack_info)
      • 字段名:camelCase (如 rackName)
      • 数据库列名:snake_case (如 rack_name)

      9. 最佳实践

      1. 所有字段必须添加字段名称(name)及注释(comment)
      2. 必须明确指定 nullable 属性
      3. 状态字段使用 tinyint 并注明取值含义
      4. 必须包含 createdAt/updatedAt 时间字段
      5. 每个实体必须配套 Zod Schema 定义
      6. Schema 必须包含 OpenAPI 元数据(description/example)