feat: 开发user、auth相关接口,初始化后台管理项目admin
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user