feat: 开发user、auth相关接口,初始化后台管理项目admin

This commit is contained in:
R524809
2025-11-19 17:42:53 +08:00
parent 7acadf191f
commit d195495449
45 changed files with 3016 additions and 101 deletions
+240
View File
@@ -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);
}
}