BaseService 服务基类使用文档
概述
BaseService 是一个通用的服务基类,封装了常见的 CRUD 操作和数据处理逻辑。它基于 TypeORM 的 Repository 模式,提供了标准化的数据访问层实现,包含数据验证、分页查询、软删除等功能。
基础使用
创建自定义服务
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository } from 'typeorm'
import { BaseService } from '@/common/BaseService'
import { SysUser } from './entities/sys-user.entity'
@Injectable()
export class UserService extends BaseService<SysUser, UserService> {
constructor(
@InjectRepository(SysUser)
private readonly userRepository: Repository<SysUser>,
) {
super(SysUser, userRepository)
}
}在控制器中使用
import { Controller } from '@nestjs/common'
import { BaseController } from '@/common/BaseController'
import { UserService } from './user.service'
@Controller('user')
export class UserController extends BaseController<SysUser, UserService> {
constructor(private readonly userService: UserService) {
super(userService)
}
}API 参考
核心方法
save(dto: SaveDto<T>)
保存数据(新增或更新),包含完整的数据验证逻辑。
参数:
dto:SaveDto<T>- 要保存的数据对象
返回: Promise<T> - 保存后的实体对象
特点:
- 自动去除 createTime 和 updateTime 字段
- 执行 class-validator 验证
- 检查 @DbUnique 约束的唯一性
- 调用 dataValidate 进行数据权限校验
// 使用示例
const userData = {
username: 'newuser',
email: 'user@example.com',
createUser: 'admin',
}
const savedUser = await userService.save(userData)add(dto: SaveDto<T>)
新增数据,会自动删除 id 字段确保是新增操作。
参数:
dto:SaveDto<T>- 要新增的数据对象
返回: Promise<T> - 新增的实体对象
const newUser = await userService.add({
username: 'testuser',
email: 'test@example.com',
createUser: 'admin',
})update(dto: SaveDto<T>)
更新数据,要求必须包含 id 字段。
参数:
dto:SaveDto<T>- 要更新的数据对象(必须包含 id)
返回: Promise<T> - 更新后的实体对象
异常: 如果没有提供 id 字段会抛出错误
const updatedUser = await userService.update({
id: '1',
username: 'updated_username',
updateUser: 'admin',
})del(ids: string[] | string, updateUser?: string)
软删除数据,将 isDelete 设置为 '1'。
参数:
ids: string[] | string - 要删除的记录ID(数组或逗号分隔的字符串)updateUser?: string - 执行删除操作的用户名
返回: Promise<UpdateResult> - 更新结果
// 删除单条记录
await userService.del('1', 'admin')
// 删除多条记录
await userService.del(['1', '2', '3'], 'admin')
// 使用逗号分隔的ID
await userService.del('1,2,3', 'admin')getOne(query: FindOptionsWhere<T> & FindOneOptions, isError = true)
根据条件查询单条记录。
参数:
query:FindOptionsWhere<T> & FindOneOptions- 查询条件isError: boolean = true - 找不到数据时是否抛出异常
返回: Promise<T | null> - 查询到的实体对象或 null
// 查询存在的记录
const user = await userService.getOne({ id: '1' })
// 查询不存在时不抛出异常
const user = await userService.getOne({ id: '999' }, false)
// 返回 null 而不是抛出异常SQL 查询方法
listBy(queryOrm: FindManyOptions, query: QueryListDto, cb?: Function)
分页列表查询的核心方法。
参数:
queryOrm: FindManyOptions = {} - TypeORM 查询选项query: QueryListDto = {} - 分页查询参数cb?: Function - 数据处理回调函数
返回: Promise<ResponseListDto<T>> - 分页查询结果
// 基础分页查询
const result = await userService.listBy({ where: { status: '1' } }, { pageNum: 1, pageSize: 10 })
// 带数据处理的查询
const result = await userService.listBy({ where: { status: '1' } }, { pageNum: 1, pageSize: 10 }, (data) => {
return data.map((item) => ({
...item,
displayName: `${item.username}(${item.email})`,
}))
})sqlOne(query: FindOptionsWhere<T> & FindOneOptions)
底层单条查询方法。
参数:
query:FindOptionsWhere<T> & FindOneOptions- 查询条件
返回: Promise<T | null> - 查询结果
const user = await userService.sqlOne({
where: { username: 'testuser' },
})工具方法
sqlLike(value: string)
生成模糊查询条件,自动转义特殊字符。
参数:
value: string - 要模糊匹配的值
返回: Like - TypeORM 的 Like 条件对象
// 安全的模糊查询
const users = await userRepository.find({
where: {
username: userService.sqlLike('test'),
},
})
// 会查找包含 'test' 的用户名,自动处理 % 和 _ 字符betweenTime(beginEndTime: [string, string])
处理时间范围查询条件。
参数:
beginEndTime: [string, string] - 开始时间和结束时间数组
返回: Between - TypeORM 的 Between 条件对象
// 时间范围查询
const users = await userRepository.find({
where: {
createTime: userService.betweenTime(['2024-01-01', '2024-12-31']),
},
})
// 结束时间会自动加上 23:59:59betweenDateArr(beginEndTime: [string, string])
生成日期范围数组。
参数:
beginEndTime: [string, string] - 开始和结束日期
返回: string[] - 日期字符串数组
const dateArray = userService.betweenDateArr(['2024-01-01', '2024-01-03'])
// 返回: ['2024-01-01', '2024-01-02', '2024-01-03']dataValidate(data: { id; updateUser })
数据权限校验方法。
参数:
data: { id; updateUser } - 包含ID和更新用户的对象
返回: Promise<boolean> - 验证结果
异常: 权限不足时抛出 HttpException
// 在自定义方法中使用权限校验
async customUpdate(id: string, data: any, updateUser: string) {
await this.dataValidate({ id, updateUser })
// 执行更新逻辑
}完整示例
用户服务完整实现
import { Injectable } from '@nestjs/common'
import { InjectRepository } from '@nestjs/typeorm'
import { Repository, Like, Between } from 'typeorm'
import { BaseService } from '@/common/BaseService'
import { SysUser } from './entities/sys-user.entity'
import { QueryListDto } from '@/common/dto'
@Injectable()
export class UserService extends BaseService<SysUser, UserService> {
constructor(
@InjectRepository(SysUser)
private readonly userRepository: Repository<SysUser>,
) {
super(SysUser, userRepository)
}
// 自定义列表查询
async list(
query: QueryListDto & {
username?: string
email?: string
createTime?: [string, string]
},
) {
const queryOrm: any = {
where: {},
order: { createTime: 'DESC' },
}
// 动态构建查询条件
if (query.username) {
queryOrm.where.username = this.sqlLike(query.username)
}
if (query.email) {
queryOrm.where.email = this.sqlLike(query.email)
}
if (query.createTime) {
queryOrm.where.createTime = this.betweenTime(query.createTime)
}
return this.listBy(queryOrm, query)
}
// 自定义统计方法
async getUserStats() {
const total = await this.repository.count()
const activeUsers = await this.repository.count({
where: { status: '1' },
})
const todayUsers = await this.repository.count({
where: {
createTime: this.betweenTime([new Date().toISOString().split('T')[0], new Date().toISOString().split('T')[0]]),
},
})
return {
total,
active: activeUsers,
today: todayUsers,
}
}
// 批量操作
async batchUpdateStatus(ids: string[], status: string, updateUser: string) {
// 权限校验
for (const id of ids) {
await this.dataValidate({ id, updateUser })
}
return await this.repository.update(ids, {
status,
updateUser,
})
}
}控制器实现
import { Controller, Get, Query, Post, Body, Param } from '@nestjs/common'
import { BaseController } from '@/common/BaseController'
import { UserService } from './user.service'
import { SysUser } from './entities/sys-user.entity'
@Controller('api/user')
export class UserController extends BaseController<SysUser, UserService> {
constructor(private readonly userService: UserService) {
super(userService)
}
// 自定义列表查询接口
@Get('search')
async search(@Query() query) {
return this.userService.list(query)
}
// 统计接口
@Get('stats')
async getStats() {
return this.userService.getUserStats()
}
// 批量更新状态
@Post('batch-status')
async batchUpdateStatus(@Body() body, @Req() req) {
const { ids, status } = body
return this.userService.batchUpdateStatus(ids, status, req.user.name)
}
// 导出用户数据
@Get('export')
async exportUsers(@Query() query) {
const result = await this.userService.list(query)
// 处理导出逻辑
return {
data: result.data,
filename: `users_${new Date().getTime()}.xlsx`,
}
}
}高级用法
关联查询处理
@Injectable()
export class UserService extends BaseService<SysUser, UserService> {
constructor(
@InjectRepository(SysUser)
private readonly userRepository: Repository<SysUser>,
) {
super(SysUser, userRepository)
}
// 带关联的列表查询
async listWithRoles(query: QueryListDto) {
const queryOrm = {
relations: ['roles'],
where: { isDelete: '0' },
order: { createTime: 'DESC' },
}
return this.listBy(queryOrm, query, (data) => {
return data.map((user) => ({
...user,
roleNames: user.roles?.map((role) => role.name).join(', ') || '',
}))
})
}
// 复杂关联查询
async getUserDetail(id: string) {
const user = await this.repository.findOne({
where: { id },
relations: ['roles', 'department', 'createdPosts'],
})
if (!user) {
throw new Error('用户不存在')
}
return {
...user,
roleName: user.roles?.[0]?.name || '',
deptName: user.department?.name || '',
}
}
}事务处理
@Injectable()
export class UserService extends BaseService<SysUser, UserService> {
constructor(
@InjectRepository(SysUser)
private readonly userRepository: Repository<SysUser>,
private readonly dataSource: DataSource,
) {
super(SysUser, userRepository)
}
// 事务操作示例
async transferUserDept(userId: string, newDeptId: string, updateUser: string) {
const queryRunner = this.dataSource.createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction()
try {
// 权限校验
await this.dataValidate({ id: userId, updateUser })
// 更新用户部门
await queryRunner.manager.update(SysUser, userId, {
departmentId: newDeptId,
updateUser,
})
// 记录操作日志
await queryRunner.manager.insert(UserOperationLog, {
userId,
operation: 'TRANSFER_DEPT',
oldValue: 'old_dept_id',
newValue: newDeptId,
operator: updateUser,
})
await queryRunner.commitTransaction()
} catch (error) {
await queryRunner.rollbackTransaction()
throw error
} finally {
await queryRunner.release()
}
}
}缓存集成
@Injectable()
export class UserService extends BaseService<SysUser, UserService> {
constructor(
@InjectRepository(SysUser)
private readonly userRepository: Repository<SysUser>,
@Inject(CACHE_MANAGER) private cacheManager: Cache,
) {
super(SysUser, userRepository)
}
// 带缓存的查询
async getUserById(id: string): Promise<SysUser> {
const cacheKey = `user:${id}`
let user = await this.cacheManager.get(cacheKey)
if (!user) {
user = await this.getOne({ id })
if (user) {
await this.cacheManager.set(cacheKey, user, 3600) // 缓存1小时
}
}
return user
}
// 清除缓存
async clearUserCache(id: string) {
const cacheKey = `user:${id}`
await this.cacheManager.del(cacheKey)
}
// 重写 save 方法添加缓存清理
async save(dto: SaveDto<SysUser>) {
const result = await super.save(dto)
if (result.id) {
await this.clearUserCache(result.id)
}
return result
}
}最佳实践
1. 参数验证
// 在 DTO 中定义验证规则
import { IsEmail, IsNotEmpty, MinLength } from 'class-validator'
export class CreateUserDto {
@IsNotEmpty()
@MinLength(3)
username: string
@IsEmail()
email: string
@IsNotEmpty()
password: string
}
// 在 Service 中使用
async createUser(createUserDto: CreateUserDto) {
return this.save(createUserDto)
}2. 错误处理
@Injectable()
export class UserService extends BaseService<SysUser, UserService> {
// 统一的错误处理
async safeSave(dto: SaveDto<SysUser>) {
try {
return await this.save(dto)
} catch (error) {
// 记录错误日志
this.logger.error(`保存用户失败: ${error.message}`, error.stack)
// 抛出自定义错误
throw new HttpException(
{
code: 500,
message: '操作失败',
detail: error.message,
},
500,
)
}
}
}3. 性能优化
// 分页查询优化
async listOptimized(query: QueryListDto) {
// 只查询需要的字段
const selectFields = ['id', 'username', 'email', 'createTime']
const queryOrm = {
select: selectFields,
where: { isDelete: '0' },
order: { createTime: 'DESC' }
}
return this.listBy(queryOrm, query)
}
// 批量操作优化
async batchUpdate(users: Partial<SysUser>[]) {
const queryRunner = this.dataSource.createQueryRunner()
await queryRunner.connect()
await queryRunner.startTransaction()
try {
const promises = users.map(user =>
this.repository.update(user.id, user)
)
await Promise.all(promises)
await queryRunner.commitTransaction()
} catch (error) {
await queryRunner.rollbackTransaction()
throw error
} finally {
await queryRunner.release()
}
}4. 数据安全
// 敏感字段过滤
async getUserProfile(id: string) {
const user = await this.getOne({ id })
// 过滤敏感信息
const { password, salt, ...safeUser } = user
return safeUser
}
// 字段级别权限控制
async getUserWithPermission(userId: string, currentUser: string) {
const user = await this.getOne({ id: userId })
// 检查当前用户是否有权限查看完整信息
const hasPermission = await this.checkPermission(currentUser, 'view_sensitive_data')
if (!hasPermission) {
delete user.phone
delete user.email
}
return user
}常见问题
Q: 如何处理复杂的查询条件?
A: 可以在 listBy 方法中动态构建查询条件:
async complexSearch(query: any) {
const queryOrm: any = { where: {} }
// 动态构建条件
if (query.status) {
queryOrm.where.status = query.status
}
if (query.keyword) {
queryOrm.where = [
{ username: this.sqlLike(query.keyword) },
{ email: this.sqlLike(query.keyword) }
]
}
return this.listBy(queryOrm, query)
}Q: 如何实现软删除的恢复功能?
A: 添加恢复方法:
async restore(ids: string[], updateUser: string) {
return this.repository.update(ids, {
isDelete: '0',
updateUser
})
}Q: 如何处理大数据量的分页查询?
A: 使用游标分页或优化查询:
// 游标分页
async cursorList(cursor: string, limit: number) {
const queryOrm = {
where: {
id: MoreThan(cursor),
isDelete: '0'
},
order: { id: 'ASC' },
take: limit
}
return this.repository.find(queryOrm)
}
// 查询优化
async optimizedList(query: QueryListDto) {
// 使用索引字段排序
const queryOrm = {
where: { isDelete: '0' },
order: { id: 'DESC' }, // 使用主键排序提高性能
select: ['id', 'username', 'createTime'] // 只查询必要字段
}
return this.listBy(queryOrm, query)
}Q: 如何集成第三方服务?
A: 在 Service 中注入其他服务:
@Injectable()
export class UserService extends BaseService<SysUser, UserService> {
constructor(
@InjectRepository(SysUser)
private readonly userRepository: Repository<SysUser>,
private readonly emailService: EmailService,
private readonly smsService: SmsService,
) {
super(SysUser, userRepository)
}
async createUserWithNotification(userData: any) {
const user = await this.save(userData)
// 发送欢迎邮件
await this.emailService.sendWelcomeEmail(user.email, user.username)
// 发送短信通知
await this.smsService.sendWelcomeSms(user.phone, user.username)
return user
}
}