241 lines
7.1 KiB
TypeScript
241 lines
7.1 KiB
TypeScript
import {
|
||
Injectable,
|
||
NotFoundException,
|
||
ConflictException,
|
||
BadRequestException,
|
||
} from '@nestjs/common';
|
||
import { InjectRepository } from '@nestjs/typeorm';
|
||
import { Repository } from 'typeorm';
|
||
import * as bcrypt from 'bcrypt';
|
||
import { User } from './user.entity';
|
||
import { CreateUserDto } from './dto/create-user.dto';
|
||
import { UpdateUserDto } from './dto/update-user.dto';
|
||
import { QueryUserDto } from './dto/query-user.dto';
|
||
import { ChangePasswordDto } from './dto/change-password.dto';
|
||
|
||
@Injectable()
|
||
export class UserService {
|
||
constructor(
|
||
@InjectRepository(User)
|
||
private readonly userRepository: Repository<User>,
|
||
) {}
|
||
|
||
/**
|
||
* 注册用户
|
||
*/
|
||
async create(createUserDto: CreateUserDto): Promise<User> {
|
||
// 检查用户名是否已存在
|
||
const existingByUsername = await this.userRepository.findOne({
|
||
where: { username: createUserDto.username },
|
||
});
|
||
|
||
if (existingByUsername) {
|
||
throw new ConflictException(
|
||
`用户名 "${createUserDto.username}" 已存在`,
|
||
);
|
||
}
|
||
|
||
// 检查邮箱是否已存在
|
||
const existingByEmail = await this.userRepository.findOne({
|
||
where: { email: createUserDto.email },
|
||
});
|
||
|
||
if (existingByEmail) {
|
||
throw new ConflictException(`邮箱 "${createUserDto.email}" 已存在`);
|
||
}
|
||
|
||
// 检查 phone 是否已存在(如果提供了)
|
||
/* if (createUserDto.phone) {
|
||
const existingByPhone = await this.userRepository.findOne({
|
||
where: { phone: createUserDto.phone },
|
||
});
|
||
|
||
if (existingByPhone) {
|
||
throw new ConflictException(
|
||
`Phone "${createUserDto.phone}" already exists`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// 检查 openId 是否已存在(如果提供了)
|
||
if (createUserDto.openId) {
|
||
const existingByOpenId = await this.userRepository.findOne({
|
||
where: { openId: createUserDto.openId },
|
||
});
|
||
|
||
if (existingByOpenId) {
|
||
throw new ConflictException(
|
||
`OpenId "${createUserDto.openId}" already exists`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// 检查 unionId 是否已存在(如果提供了)
|
||
if (createUserDto.unionId) {
|
||
const existingByUnionId = await this.userRepository.findOne({
|
||
where: { unionId: createUserDto.unionId },
|
||
});
|
||
|
||
if (existingByUnionId) {
|
||
throw new ConflictException(
|
||
`UnionId "${createUserDto.unionId}" already exists`,
|
||
);
|
||
}
|
||
} */
|
||
|
||
// 使用 bcrypt 加密密码
|
||
const saltRounds = 10;
|
||
const passwordHash = await bcrypt.hash(
|
||
createUserDto.password,
|
||
saltRounds,
|
||
);
|
||
|
||
// 创建用户
|
||
const user = this.userRepository.create({
|
||
username: createUserDto.username,
|
||
passwordHash,
|
||
email: createUserDto.email,
|
||
nickname: createUserDto.nickname,
|
||
avatarUrl: createUserDto.avatarUrl,
|
||
phone: createUserDto.phone,
|
||
openId: createUserDto.openId,
|
||
unionId: createUserDto.unionId,
|
||
status: 'active',
|
||
role: createUserDto.role || 'user', // 默认为普通用户
|
||
});
|
||
|
||
return this.userRepository.save(user);
|
||
}
|
||
|
||
/**
|
||
* 查询所有用户
|
||
*/
|
||
async findAll(): Promise<User[]> {
|
||
return this.userRepository.find({
|
||
order: {
|
||
createdAt: 'DESC',
|
||
},
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 根据 username 或 email 查询单个用户
|
||
*/
|
||
async findOne(queryDto: QueryUserDto): Promise<User> {
|
||
if (!queryDto.username && !queryDto.email) {
|
||
throw new BadRequestException('必须提供用户名或邮箱');
|
||
}
|
||
|
||
const where: { username?: string; email?: string } = {};
|
||
if (queryDto.username) {
|
||
where.username = queryDto.username;
|
||
}
|
||
if (queryDto.email) {
|
||
where.email = queryDto.email;
|
||
}
|
||
|
||
const user = await this.userRepository.findOne({ where });
|
||
|
||
if (!user) {
|
||
const identifier = queryDto.username || queryDto.email;
|
||
throw new NotFoundException(
|
||
`未找到${queryDto.username ? '用户名' : '邮箱'}为 "${identifier}" 的用户`,
|
||
);
|
||
}
|
||
|
||
return user;
|
||
}
|
||
|
||
/**
|
||
* 根据 ID 查询单个用户
|
||
*/
|
||
async findOneById(id: number): Promise<User> {
|
||
const user = await this.userRepository.findOne({
|
||
where: { userId: id },
|
||
});
|
||
|
||
if (!user) {
|
||
throw new NotFoundException(`未找到ID为 ${id} 的用户`);
|
||
}
|
||
|
||
return user;
|
||
}
|
||
|
||
/**
|
||
* 更新用户信息(不允许修改 username、openId、unionId)
|
||
*/
|
||
async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
|
||
const user = await this.findOneById(id);
|
||
|
||
// 如果更新邮箱,检查是否与其他用户冲突
|
||
if (updateUserDto.email && updateUserDto.email !== user.email) {
|
||
const existingByEmail = await this.userRepository.findOne({
|
||
where: { email: updateUserDto.email },
|
||
});
|
||
|
||
if (existingByEmail) {
|
||
throw new ConflictException(
|
||
`邮箱 "${updateUserDto.email}" 已存在`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// 如果更新 phone,检查是否与其他用户冲突
|
||
if (updateUserDto.phone && updateUserDto.phone !== user.phone) {
|
||
const existingByPhone = await this.userRepository.findOne({
|
||
where: { phone: updateUserDto.phone },
|
||
});
|
||
|
||
if (existingByPhone) {
|
||
throw new ConflictException(
|
||
`手机号 "${updateUserDto.phone}" 已存在`,
|
||
);
|
||
}
|
||
}
|
||
|
||
// 更新用户信息
|
||
Object.assign(user, updateUserDto);
|
||
return this.userRepository.save(user);
|
||
}
|
||
|
||
/**
|
||
* 修改密码(需要先验证旧密码)
|
||
*/
|
||
async changePassword(
|
||
id: number,
|
||
changePasswordDto: ChangePasswordDto,
|
||
): Promise<void> {
|
||
const user = await this.findOneById(id);
|
||
|
||
// 验证旧密码
|
||
const isOldPasswordValid = await bcrypt.compare(
|
||
changePasswordDto.oldPassword,
|
||
user.passwordHash,
|
||
);
|
||
|
||
if (!isOldPasswordValid) {
|
||
throw new BadRequestException('旧密码错误');
|
||
}
|
||
|
||
// 加密新密码
|
||
const saltRounds = 10;
|
||
const newPasswordHash = await bcrypt.hash(
|
||
changePasswordDto.newPassword,
|
||
saltRounds,
|
||
);
|
||
|
||
// 更新密码
|
||
user.passwordHash = newPasswordHash;
|
||
await this.userRepository.save(user);
|
||
}
|
||
|
||
/**
|
||
* 删除用户(软删除,更新状态为 deleted)
|
||
*/
|
||
async remove(id: number): Promise<void> {
|
||
const user = await this.findOneById(id);
|
||
user.status = 'deleted';
|
||
await this.userRepository.save(user);
|
||
}
|
||
}
|