feat: Phase 1-3 全部完成 — 沙盒增强、学情分析、学习路径
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ToolsService } from '../tools.service';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
|
||||
describe('ToolsService', () => {
|
||||
let service: ToolsService;
|
||||
let prisma: PrismaService;
|
||||
|
||||
const mockPrisma = {
|
||||
tool: {
|
||||
findMany: jest.fn(),
|
||||
count: jest.fn(),
|
||||
create: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
ToolsService,
|
||||
{ provide: PrismaService, useValue: mockPrisma },
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<ToolsService>(ToolsService);
|
||||
prisma = module.get<PrismaService>(PrismaService);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('findAll', () => {
|
||||
it('should return paginated tools', async () => {
|
||||
const mockTools = [{ id: 1, name: 'ChatGPT', category: { id: 1, name: '聊天' } }];
|
||||
mockPrisma.tool.findMany.mockResolvedValue(mockTools);
|
||||
mockPrisma.tool.count.mockResolvedValue(1);
|
||||
|
||||
const result = await service.findAll({ page: 1, pageSize: 20 });
|
||||
expect(result.items).toEqual(mockTools);
|
||||
expect(result.total).toBe(1);
|
||||
expect(result.page).toBe(1);
|
||||
expect(result.pageSize).toBe(20);
|
||||
});
|
||||
|
||||
it('should filter by categoryId', async () => {
|
||||
mockPrisma.tool.findMany.mockResolvedValue([]);
|
||||
mockPrisma.tool.count.mockResolvedValue(0);
|
||||
|
||||
await service.findAll({ categoryId: 2 });
|
||||
expect(mockPrisma.tool.findMany).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: expect.objectContaining({ categoryId: 2 }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('should filter by isFeatured', async () => {
|
||||
mockPrisma.tool.findMany.mockResolvedValue([]);
|
||||
mockPrisma.tool.count.mockResolvedValue(0);
|
||||
|
||||
await service.findAll({ isFeatured: true });
|
||||
expect(mockPrisma.tool.findMany).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: expect.objectContaining({ isFeatured: true }),
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('create', () => {
|
||||
it('should create a tool', async () => {
|
||||
const data = { name: 'New Tool', url: 'https://example.com' };
|
||||
mockPrisma.tool.create.mockResolvedValue({ id: 1, ...data });
|
||||
|
||||
const result = await service.create(data);
|
||||
expect(result).toHaveProperty('id', 1);
|
||||
expect(mockPrisma.tool.create).toHaveBeenCalledWith({ data });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Controller, Get, Post, Body, Query } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { ToolsService } from './tools.service';
|
||||
|
||||
@ApiTags('AI工具')
|
||||
@Controller('tools')
|
||||
export class ToolsController {
|
||||
constructor(private toolsService: ToolsService) {}
|
||||
|
||||
@Get()
|
||||
async findAll(@Query() query: { page?: number; pageSize?: number; categoryId?: number; isFeatured?: boolean }) {
|
||||
return this.toolsService.findAll(query);
|
||||
}
|
||||
|
||||
@Post()
|
||||
async create(@Body() body: { name: string; description?: string; url: string; icon?: string; categoryId?: number; tags?: string }) {
|
||||
return this.toolsService.create(body);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ToolsController } from './tools.controller';
|
||||
import { ToolsService } from './tools.service';
|
||||
|
||||
@Module({
|
||||
controllers: [ToolsController],
|
||||
providers: [ToolsService],
|
||||
exports: [ToolsService],
|
||||
})
|
||||
export class ToolsModule {}
|
||||
@@ -0,0 +1,33 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class ToolsService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async findAll(params: { page?: number; pageSize?: number; categoryId?: number; isFeatured?: boolean }) {
|
||||
const page = Number(params.page ?? 1);
|
||||
const pageSize = Number(params.pageSize ?? 20);
|
||||
const { categoryId, isFeatured } = params;
|
||||
const where: any = { status: 'PUBLISHED', deletedAt: null };
|
||||
if (categoryId) where.categoryId = categoryId;
|
||||
if (isFeatured !== undefined) where.isFeatured = isFeatured;
|
||||
|
||||
const [items, total] = await Promise.all([
|
||||
this.prisma.tool.findMany({
|
||||
where,
|
||||
skip: (page - 1) * pageSize,
|
||||
take: pageSize,
|
||||
orderBy: { viewCount: 'desc' },
|
||||
include: { category: true },
|
||||
}),
|
||||
this.prisma.tool.count({ where }),
|
||||
]);
|
||||
|
||||
return { items, total, page, pageSize };
|
||||
}
|
||||
|
||||
async create(data: { name: string; description?: string; url: string; icon?: string; categoryId?: number; tags?: string }) {
|
||||
return this.prisma.tool.create({ data });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user