feat: Phase 1-3 全部完成 — 沙盒增强、学情分析、学习路径
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersService } from '../users.service';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
|
||||
describe('UsersService', () => {
|
||||
let service: UsersService;
|
||||
let prisma: PrismaService;
|
||||
|
||||
const mockPrisma = {
|
||||
user: {
|
||||
findMany: jest.fn(),
|
||||
findUnique: jest.fn(),
|
||||
count: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
UsersService,
|
||||
{ provide: PrismaService, useValue: mockPrisma },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UsersService>(UsersService);
|
||||
prisma = module.get<PrismaService>(PrismaService);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('findAll', () => {
|
||||
it('should return paginated users', async () => {
|
||||
const mockUsers = [{ id: 1, nickname: '用户1' }];
|
||||
mockPrisma.user.findMany.mockResolvedValue(mockUsers);
|
||||
mockPrisma.user.count.mockResolvedValue(1);
|
||||
|
||||
const result = await service.findAll({ page: 1, pageSize: 20 });
|
||||
expect(result.items).toEqual(mockUsers);
|
||||
expect(result.total).toBe(1);
|
||||
});
|
||||
|
||||
it('should filter by status', async () => {
|
||||
mockPrisma.user.findMany.mockResolvedValue([]);
|
||||
mockPrisma.user.count.mockResolvedValue(0);
|
||||
|
||||
await service.findAll({ status: 'ACTIVE' });
|
||||
expect(mockPrisma.user.findMany).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: expect.objectContaining({ status: 'ACTIVE' }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findById', () => {
|
||||
it('should return user by id', async () => {
|
||||
const mockUser = { id: 1, nickname: '用户1' };
|
||||
mockPrisma.user.findUnique.mockResolvedValue(mockUser);
|
||||
|
||||
const result = await service.findById(1);
|
||||
expect(result).toEqual(mockUser);
|
||||
});
|
||||
|
||||
it('should return null for non-existent user', async () => {
|
||||
mockPrisma.user.findUnique.mockResolvedValue(null);
|
||||
const result = await service.findById(999);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateProfile', () => {
|
||||
it('should update user profile', async () => {
|
||||
mockPrisma.user.update.mockResolvedValue({ id: 1, nickname: '新昵称' });
|
||||
|
||||
const result = await service.updateProfile(1, { nickname: '新昵称' });
|
||||
expect(result).toHaveProperty('nickname', '新昵称');
|
||||
expect(mockPrisma.user.update).toHaveBeenCalledWith({
|
||||
where: { id: 1 },
|
||||
data: { nickname: '新昵称' },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Controller, Get, Put, Body, Param, UseGuards, Req, Query } from '@nestjs/common';
|
||||
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@ApiTags('用户')
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
constructor(private usersService: UsersService) {}
|
||||
|
||||
@Get()
|
||||
@UseGuards(AuthGuard('jwt'))
|
||||
@ApiBearerAuth()
|
||||
async findAll(@Query() query: { page?: number; pageSize?: number; status?: string }) {
|
||||
return this.usersService.findAll(query);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@UseGuards(AuthGuard('jwt'))
|
||||
@ApiBearerAuth()
|
||||
async findById(@Param('id') id: string) {
|
||||
return this.usersService.findById(+id);
|
||||
}
|
||||
|
||||
@Put('profile')
|
||||
@UseGuards(AuthGuard('jwt'))
|
||||
@ApiBearerAuth()
|
||||
async updateProfile(@Req() req: any, @Body() body: { nickname?: string; avatar?: string }) {
|
||||
return this.usersService.updateProfile(req.user.userId, body);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@Module({
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
exports: [UsersService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll(params: { page?: number; pageSize?: number; status?: string }) {
|
||||
const page = Number(params.page ?? 1);
|
||||
const pageSize = Number(params.pageSize ?? 20);
|
||||
const where: any = { deletedAt: null };
|
||||
if (params.status) where.status = params.status;
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.user.findMany({
|
||||
where,
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
orderBy: { createdAt: 'desc' },
|
||||
select: {
|
||||
id: true, nickname: true, avatar: true, phone: true, email: true,
|
||||
status: true, memberPlan: true, createdAt: true, lastLoginAt: true,
|
||||
},
|
||||
}),
|
||||
this.prisma.user.count({ where }),
|
||||
]);
|
||||
|
||||
return { items, total, page, pageSize };
|
||||
}
|
||||
|
||||
async findById(id: number) {
|
||||
return this.prisma.user.findUnique({
|
||||
where: { id },
|
||||
select: {
|
||||
id: true, nickname: true, avatar: true, phone: true, email: true,
|
||||
status: true, memberPlan: true, memberExpire: true, sandboxDaily: true,
|
||||
createdAt: true, lastLoginAt: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async updateProfile(id: number, data: { nickname?: string; avatar?: string }) {
|
||||
return this.prisma.user.update({ where: { id }, data });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user