Skip to content

BaseEntity 实体基类使用文档

概述

BaseEntity 是一个抽象的实体基类,为所有数据库实体提供通用的基础字段和功能。它基于 TypeORM 构建,包含了常见的 CRUD 操作所需的时间戳、用户信息和软删除字段。

基础使用

继承 BaseEntity

typescript
import { Entity } from 'typeorm'
import { BaseEntity, MyEntity } from '@/common/entity/BaseEntity'

@MyEntity('user')
export class User extends BaseEntity {
  // 自定义字段
  name: string
  
  email: string
  
  age: number
}

字段说明

BaseEntity 包含以下基础字段:

字段名类型描述数据库列名
idstring (bigint)主键IDid
createTimestring创建时间create_time
createUserstring创建人create_user
updateTimestring更新时间update_time
updateUserstring更新人update_user
isDeleteBoolNum软删除标识is_delete

API 参考

装饰器函数

MyEntity(name: string | EntityOptions, options?: EntityOptions)

自定义实体装饰器,自动转换表名为下划线格式。

typescript
// 基础用法
@MyEntity('user_info')
export class UserInfo extends BaseEntity {}

// 带选项用法
@MyEntity('user_profile', { 
  orderBy: { createTime: 'DESC' } 
})
export class UserProfile extends BaseEntity {}

BaseColumn(config: ColumnOptions)

增强版的 Column 装饰器,支持自动命名转换和字符串截断。

typescript
@BaseColumn({ 
  length: 100,
  comment: '用户名',
  overLengthCut: true // 超长自动截断
})
name: string

@BaseColumn({ 
  type: 'text',
  comment: '用户描述'
})
description: string

DbUnique(target, propertyKey)

数据库唯一约束装饰器,在保存时自动校验唯一性。

typescript
@BaseColumn({ length: 50 })
@DbUnique
email: string

@BaseColumn({ length: 20 })
@DbUnique
phone: string

工具函数

boolNumColumn(title: string, name: string, defaultValue?, options?)

生成布尔数字类型的列配置。

typescript
// 使用示例
@BaseColumn(boolNumColumn('激活状态', 'is_active', BoolNum.Yes))
isActive: BoolNum

@BaseColumn(boolNumColumn('管理员', 'is_admin', BoolNum.No))
isAdmin: BoolNum

overLengthCut(value: string, maxLength: string | number)

字符串超长截断工具函数。

typescript
const result = overLengthCut('这是一个很长的字符串', 10)
// 结果: '这是一个...'

实例方法

assignOwn(obj: any)

安全的对象属性赋值,只保留实体中存在的字段。

typescript
const user = new User()
const userData = {
  name: '张三',
  email: 'zhangsan@example.com',
  extraField: '不会被赋值的额外字段'
}

user.assignOwn(userData)
// 只会赋值 name 和 email 字段

完整示例

用户实体示例

typescript
import { Entity, Column } from 'typeorm'
import { BaseEntity, MyEntity, BaseColumn, DbUnique, boolNumColumn } from '@/common/entity/BaseEntity'
import { BoolNum } from '@/common/type/base'

@MyEntity('sys_user')
export class SysUser extends BaseEntity {
  @BaseColumn({ 
    length: 50, 
    comment: '用户名',
    overLengthCut: true 
  })
  @DbUnique
  username: string

  @BaseColumn({ 
    length: 100, 
    comment: '邮箱地址' 
  })
  @DbUnique
  email: string

  @BaseColumn({ 
    length: 50, 
    comment: '手机号码' 
  })
  phone: string

  @BaseColumn({ 
    type: 'text', 
    comment: '用户简介' 
  })
  bio: string

  @BaseColumn(boolNumColumn('账户状态', 'status', BoolNum.Yes))
  status: BoolNum

  @BaseColumn({ 
    type: 'int', 
    comment: '年龄' 
  })
  age: number
}

在 Service 中使用

typescript
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { SysUser } from './entities/sys-user.entity'

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(SysUser)
    private readonly userRepository: Repository<SysUser>,
  ) {}

  async createUser(userData: Partial<SysUser>): Promise<SysUser> {
    const user = new SysUser()
    user.assignOwn(userData) // 安全赋值
    user.createUser = 'admin' // 设置创建人
    
    // createTime 会自动设置为当前时间
    return await this.userRepository.save(user)
  }

  async updateUser(id: string, updateData: Partial<SysUser>): Promise<SysUser> {
    const user = await this.userRepository.findOne({ where: { id } })
    if (!user) {
      throw new Error('用户不存在')
    }
    
    user.assignOwn(updateData) // 安全更新
    user.updateUser = 'admin' // 设置更新人
    
    // updateTime 会在保存时自动更新
    return await this.userRepository.save(user)
  }

  async deleteUser(id: string): Promise<void> {
    // 软删除,设置 isDelete = '1'
    await this.userRepository.update(id, { 
      isDelete: BoolNum.Yes,
      updateUser: 'admin'
    })
  }
}

高级用法

自定义时间格式化

typescript
@BaseColumn({
  type: 'datetime',
  transformer: {
    from: (date) => date && dayjs(date).format('YYYY年MM月DD日 HH:mm:ss'),
    to: (value: string) => value,
  },
  name: 'custom_time',
  comment: '自定义时间字段'
})
customTime: string

复合唯一约束

typescript
@BaseColumn({ length: 50 })
@DbUnique
tenantId: string

@BaseColumn({ length: 50 })
@DbUnique
userId: string

// 在数据库层面可以通过联合索引实现复合唯一约束

条件字段验证

typescript
export class SysUser extends BaseEntity {
  @BaseColumn({ length: 50 })
  @DbUnique
  username: string

  @BaseColumn({ length: 100 })
  email: string

  // 自定义验证逻辑
  async validate(): Promise<void> {
    if (this.email && !this.email.includes('@')) {
      throw new Error('邮箱格式不正确')
    }
  }
}

最佳实践

1. 字段命名规范

typescript
// ✅ 推荐:使用清晰的字段名
@BaseColumn({ length: 50, comment: '真实姓名' })
realName: string

// ❌ 不推荐:含义模糊的字段名
@BaseColumn({ length: 50 })
name: string

2. 合理使用唯一约束

typescript
// 对于业务上必须唯一的字段添加 @DbUnique
@BaseColumn({ length: 50 })
@DbUnique
username: string

// 对于可以重复的字段不要添加唯一约束
@BaseColumn({ length: 100 })
nickname: string

3. 字符串长度控制

typescript
// 对于可能输入较长文本的字段启用自动截断
@BaseColumn({ 
  length: 200, 
  comment: '个人简介',
  overLengthCut: true 
})
bio: string

4. 时间字段处理

typescript
// 创建时间由系统自动设置,不需要手动赋值
// 更新时间会在每次更新时自动刷新
const user = new SysUser()
user.username = 'testuser'
user.createUser = 'admin' // 只需要设置创建人
// createTime 和 updateTime 会自动处理

常见问题

Q: 如何禁用某些基础字段?

A: 可以通过继承后重新定义字段来覆盖:

typescript
@MyEntity('simple_entity')
export class SimpleEntity extends BaseEntity {
  // 重新定义字段,移除不需要的约束
  @Column({ type: 'varchar', length: 50 })
  createUser: string
  
  // 或者完全移除某个字段(不推荐)
  // @Exclude()
  // createTime: string
}

Q: 如何自定义软删除逻辑?

A: 可以重写相关方法或使用不同的删除标识:

typescript
export class CustomEntity extends BaseEntity {
  @DeleteDateColumn({
    type: 'datetime',
    name: 'deleted_at',
    comment: '删除时间'
  })
  deletedAt: Date
}

Q: 如何处理大量文本字段?

A: 使用 text 类型并考虑性能优化:

typescript
@BaseColumn({ 
  type: 'text',
  comment: '详细描述'
})
description: string

// 对于搜索频繁的长文本,考虑添加全文索引

Q: 如何实现字段级别的权限控制?

A: 可以在实体中添加权限检查逻辑:

typescript
export class SecureEntity extends BaseEntity {
  @BaseColumn({ length: 100 })
  sensitiveData: string

  // 自定义方法进行权限检查
  canViewField(fieldName: string, userId: string): boolean {
    // 实现具体的权限逻辑
    return true
  }
}