初始化
Some checks failed
Some checks failed
This commit is contained in:
167
.github/workflows/ci.yml
vendored
Normal file
167
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
name: CI/CD Pipeline
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main, develop ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: 测试
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [18.x, 20.x]
|
||||
|
||||
steps:
|
||||
- name: 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 设置 Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
- name: 安装依赖
|
||||
working-directory: ./frontend
|
||||
run: npm ci
|
||||
|
||||
- name: 类型检查
|
||||
working-directory: ./frontend
|
||||
run: npm run type-check
|
||||
|
||||
- name: 代码风格检查
|
||||
working-directory: ./frontend
|
||||
run: npm run lint
|
||||
|
||||
- name: 代码格式检查
|
||||
working-directory: ./frontend
|
||||
run: npm run format:check
|
||||
|
||||
- name: 运行单元测试
|
||||
working-directory: ./frontend
|
||||
run: npm run test:unit:coverage
|
||||
|
||||
- name: 上传覆盖率报告到 Codecov
|
||||
uses: codecov/codecov-action@v3
|
||||
with:
|
||||
file: ./frontend/coverage/lcov.info
|
||||
flags: unittests
|
||||
name: codecov-umbrella
|
||||
|
||||
- name: 构建应用
|
||||
working-directory: ./frontend
|
||||
run: npm run build
|
||||
|
||||
- name: 安装 Playwright
|
||||
working-directory: ./frontend
|
||||
run: npx playwright install --with-deps
|
||||
|
||||
- name: 运行 E2E 测试
|
||||
working-directory: ./frontend
|
||||
run: npm run test:e2e
|
||||
|
||||
- name: 上传 E2E 测试报告
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report
|
||||
path: frontend/playwright-report/
|
||||
retention-days: 30
|
||||
|
||||
- name: 上传测试结果
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: test-results
|
||||
path: frontend/test-results/
|
||||
retention-days: 30
|
||||
|
||||
security:
|
||||
name: 安全检查
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 设置 Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
- name: 安装依赖
|
||||
working-directory: ./frontend
|
||||
run: npm ci
|
||||
|
||||
- name: 运行安全审计
|
||||
working-directory: ./frontend
|
||||
run: npm audit --audit-level moderate
|
||||
|
||||
- name: 运行 Snyk 安全扫描
|
||||
uses: snyk/actions/node@master
|
||||
continue-on-error: true
|
||||
env:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
with:
|
||||
args: --severity-threshold=high
|
||||
|
||||
deploy:
|
||||
name: 部署
|
||||
needs: [test, security]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
|
||||
|
||||
steps:
|
||||
- name: 检出代码
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 设置 Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: frontend/package-lock.json
|
||||
|
||||
- name: 安装依赖
|
||||
working-directory: ./frontend
|
||||
run: npm ci
|
||||
|
||||
- name: 构建应用
|
||||
working-directory: ./frontend
|
||||
run: npm run build
|
||||
|
||||
- name: 部署到服务器
|
||||
uses: appleboy/ssh-action@v1.0.0
|
||||
with:
|
||||
host: ${{ secrets.HOST }}
|
||||
username: ${{ secrets.USERNAME }}
|
||||
key: ${{ secrets.SSH_KEY }}
|
||||
script: |
|
||||
cd /var/www/html
|
||||
git pull origin main
|
||||
cd frontend
|
||||
npm ci --production
|
||||
npm run build
|
||||
pm2 restart frontend-app
|
||||
|
||||
notify:
|
||||
name: 通知
|
||||
needs: [test, security, deploy]
|
||||
runs-on: ubuntu-latest
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: 发送通知到 Slack
|
||||
uses: 8398a7/action-slack@v3
|
||||
with:
|
||||
status: ${{ job.status }}
|
||||
channel: '#ci-cd'
|
||||
webhook_url: ${{ secrets.SLACK_WEBHOOK }}
|
||||
fields: repo,message,commit,author,action,eventName,ref,workflow
|
||||
@@ -1,26 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="xunit" Version="2.9.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\MinimalAPI\MinimalAPI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,97 +0,0 @@
|
||||
using AutoMapper;
|
||||
using HardwarePerformance.Models;
|
||||
using HardwarePerformance.Models.DTOs;
|
||||
using HardwarePerformance.Services;
|
||||
using Moq;
|
||||
|
||||
namespace HardwarePerformance.Tests.Services
|
||||
{
|
||||
public class CategoryServiceTests
|
||||
{
|
||||
private readonly Mock<ICategoryRepository> _mockCategoryRepository;
|
||||
private readonly Mock<IMapper> _mockMapper;
|
||||
private readonly CategoryService _categoryService;
|
||||
|
||||
public CategoryServiceTests()
|
||||
{
|
||||
_mockCategoryRepository = new Mock<ICategoryRepository>();
|
||||
_mockMapper = new Mock<IMapper>();
|
||||
_categoryService = new CategoryService(_mockCategoryRepository.Object, _mockMapper.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAllCategoriesAsync_ReturnsAllCategories()
|
||||
{
|
||||
// Arrange
|
||||
var categories = new List<Category>
|
||||
{
|
||||
new Category { Id = 1, Name = "手机CPU", Description = "手机处理器" },
|
||||
new Category { Id = 2, Name = "电脑CPU", Description = "电脑处理器" }
|
||||
};
|
||||
|
||||
var categoryDtos = new List<CategoryDto>
|
||||
{
|
||||
new CategoryDto { Id = 1, Name = "手机CPU", Description = "手机处理器" },
|
||||
new CategoryDto { Id = 2, Name = "电脑CPU", Description = "电脑处理器" }
|
||||
};
|
||||
|
||||
_mockCategoryRepository.Setup(repo => repo.GetAllAsync())
|
||||
.ReturnsAsync(categories);
|
||||
|
||||
_mockMapper.Setup(m => m.Map<IEnumerable<CategoryDto>>(categories))
|
||||
.Returns(categoryDtos);
|
||||
|
||||
// Act
|
||||
var result = await _categoryService.GetAllCategoriesAsync();
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(2, result.Count());
|
||||
_mockCategoryRepository.Verify(repo => repo.GetAllAsync(), Times.Once);
|
||||
_mockMapper.Verify(m => m.Map<IEnumerable<CategoryDto>>(categories), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCategoryByIdAsync_ExistingCategory_ReturnsCategoryDto()
|
||||
{
|
||||
// Arrange
|
||||
var categoryId = 1;
|
||||
var category = new Category { Id = categoryId, Name = "手机CPU", Description = "手机处理器" };
|
||||
var categoryDto = new CategoryDto { Id = categoryId, Name = "手机CPU", Description = "手机处理器" };
|
||||
|
||||
_mockCategoryRepository.Setup(repo => repo.GetByIdAsync(categoryId))
|
||||
.ReturnsAsync(category);
|
||||
|
||||
_mockMapper.Setup(m => m.Map<CategoryDto>(category))
|
||||
.Returns(categoryDto);
|
||||
|
||||
// Act
|
||||
var result = await _categoryService.GetCategoryByIdAsync(categoryId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(categoryId, result.Id);
|
||||
Assert.Equal("手机CPU", result.Name);
|
||||
_mockCategoryRepository.Verify(repo => repo.GetByIdAsync(categoryId), Times.Once);
|
||||
_mockMapper.Verify(m => m.Map<CategoryDto>(category), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetCategoryByIdAsync_NonExistingCategory_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var categoryId = 999;
|
||||
|
||||
_mockCategoryRepository.Setup(repo => repo.GetByIdAsync(categoryId))
|
||||
.ReturnsAsync((Category?)null);
|
||||
|
||||
// Act
|
||||
var result = await _categoryService.GetCategoryByIdAsync(categoryId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
_mockCategoryRepository.Verify(repo => repo.GetByIdAsync(categoryId), Times.Once);
|
||||
_mockMapper.Verify(m => m.Map<CategoryDto>(It.IsAny<Category>()), Times.Never);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,173 +0,0 @@
|
||||
using AutoMapper;
|
||||
using HardwarePerformance.Models;
|
||||
using HardwarePerformance.Models.DTOs;
|
||||
using HardwarePerformance.Services;
|
||||
using Moq;
|
||||
|
||||
namespace HardwarePerformance.Tests.Services
|
||||
{
|
||||
public class ComparisonServiceTests
|
||||
{
|
||||
private readonly Mock<IProductRepository> _mockProductRepository;
|
||||
private readonly Mock<IMapper> _mockMapper;
|
||||
private readonly ComparisonService _comparisonService;
|
||||
|
||||
public ComparisonServiceTests()
|
||||
{
|
||||
_mockProductRepository = new Mock<IProductRepository>();
|
||||
_mockMapper = new Mock<IMapper>();
|
||||
_comparisonService = new ComparisonService(_mockProductRepository.Object, _mockMapper.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompareProductsAsync_ValidProductIds_ReturnsComparisonData()
|
||||
{
|
||||
// Arrange
|
||||
var productIds = new List<int> { 1, 2 };
|
||||
|
||||
var products = new List<Product>
|
||||
{
|
||||
new Product
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Intel Core i9",
|
||||
Manufacturer = "Intel",
|
||||
CategoryId = 1,
|
||||
CurrentRank = 1,
|
||||
ReleaseDate = DateTime.Now.AddMonths(-6),
|
||||
Price = 500,
|
||||
PerformanceScore = 4000,
|
||||
PerformanceScores = new List<PerformanceScore>
|
||||
{
|
||||
new PerformanceScore { Id = 1, ProductId = 1, TestType = "Single-Core", Score = 1500 },
|
||||
new PerformanceScore { Id = 2, ProductId = 1, TestType = "Multi-Core", Score = 8000 }
|
||||
},
|
||||
Specifications = new List<Specification>
|
||||
{
|
||||
new Specification { Id = 1, ProductId = 1, Name = "Cores", Value = "8" },
|
||||
new Specification { Id = 2, ProductId = 1, Name = "Threads", Value = "16" }
|
||||
}
|
||||
},
|
||||
new Product
|
||||
{
|
||||
Id = 2,
|
||||
Name = "AMD Ryzen 9",
|
||||
Manufacturer = "AMD",
|
||||
CategoryId = 1,
|
||||
CurrentRank = 2,
|
||||
ReleaseDate = DateTime.Now.AddMonths(-4),
|
||||
Price = 450,
|
||||
PerformanceScore = 3800,
|
||||
PerformanceScores = new List<PerformanceScore>
|
||||
{
|
||||
new PerformanceScore { Id = 3, ProductId = 2, TestType = "Single-Core", Score = 1400 },
|
||||
new PerformanceScore { Id = 4, ProductId = 2, TestType = "Multi-Core", Score = 7500 }
|
||||
},
|
||||
Specifications = new List<Specification>
|
||||
{
|
||||
new Specification { Id = 3, ProductId = 2, Name = "Cores", Value = "8" },
|
||||
new Specification { Id = 4, ProductId = 2, Name = "Threads", Value = "16" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var productDtos = new List<ProductDto>
|
||||
{
|
||||
new ProductDto
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Intel Core i9",
|
||||
Manufacturer = "Intel",
|
||||
CategoryId = 1,
|
||||
CurrentRank = 1,
|
||||
ReleaseDate = DateTime.Now.AddMonths(-6),
|
||||
Price = 500,
|
||||
PerformanceScore = 4000,
|
||||
PerformanceScores = new List<PerformanceScore>
|
||||
{
|
||||
new PerformanceScore { Id = 1, ProductId = 1, TestType = "Single-Core", Score = 1500 },
|
||||
new PerformanceScore { Id = 2, ProductId = 1, TestType = "Multi-Core", Score = 8000 }
|
||||
},
|
||||
Specifications = new List<Specification>
|
||||
{
|
||||
new Specification { Id = 1, ProductId = 1, Name = "Cores", Value = "8" },
|
||||
new Specification { Id = 2, ProductId = 1, Name = "Threads", Value = "16" }
|
||||
}
|
||||
},
|
||||
new ProductDto
|
||||
{
|
||||
Id = 2,
|
||||
Name = "AMD Ryzen 9",
|
||||
Manufacturer = "AMD",
|
||||
CategoryId = 1,
|
||||
CurrentRank = 2,
|
||||
ReleaseDate = DateTime.Now.AddMonths(-4),
|
||||
Price = 450,
|
||||
PerformanceScore = 3800,
|
||||
PerformanceScores = new List<PerformanceScore>
|
||||
{
|
||||
new PerformanceScore { Id = 3, ProductId = 2, TestType = "Single-Core", Score = 1400 },
|
||||
new PerformanceScore { Id = 4, ProductId = 2, TestType = "Multi-Core", Score = 7500 }
|
||||
},
|
||||
Specifications = new List<Specification>
|
||||
{
|
||||
new Specification { Id = 3, ProductId = 2, Name = "Cores", Value = "8" },
|
||||
new Specification { Id = 4, ProductId = 2, Name = "Threads", Value = "16" }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
_mockProductRepository.Setup(repo => repo.GetByIdsAsync(productIds))
|
||||
.ReturnsAsync(products);
|
||||
|
||||
_mockMapper.Setup(m => m.Map<List<ProductDto>>(products))
|
||||
.Returns(productDtos);
|
||||
|
||||
// Act
|
||||
var result = await _comparisonService.CompareProductsAsync(productIds);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(2, result.Products.Count);
|
||||
Assert.NotNull(result.ComparisonMatrix);
|
||||
_mockProductRepository.Verify(repo => repo.GetByIdsAsync(productIds), Times.Once);
|
||||
_mockMapper.Verify(m => m.Map<List<ProductDto>>(products), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompareProductsAsync_EmptyProductIds_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var productIds = new List<int>();
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () =>
|
||||
await _comparisonService.CompareProductsAsync(productIds));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompareProductsAsync_TooManyProductIds_ThrowsArgumentException()
|
||||
{
|
||||
// Arrange
|
||||
var productIds = new List<int> { 1, 2, 3, 4, 5 };
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<ArgumentException>(async () =>
|
||||
await _comparisonService.CompareProductsAsync(productIds));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CompareProductsAsync_NonExistingProductIds_ThrowsKeyNotFoundException()
|
||||
{
|
||||
// Arrange
|
||||
var productIds = new List<int> { 1, 2 };
|
||||
|
||||
_mockProductRepository.Setup(repo => repo.GetByIdsAsync(productIds))
|
||||
.ReturnsAsync(new List<Product>());
|
||||
|
||||
// Act & Assert
|
||||
await Assert.ThrowsAsync<KeyNotFoundException>(async () =>
|
||||
await _comparisonService.CompareProductsAsync(productIds));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
using AutoMapper;
|
||||
using HardwarePerformance.Models;
|
||||
using HardwarePerformance.Models.DTOs;
|
||||
using HardwarePerformance.Services;
|
||||
using Moq;
|
||||
|
||||
namespace HardwarePerformance.Tests.Services
|
||||
{
|
||||
public class ProductServiceTests
|
||||
{
|
||||
private readonly Mock<IProductRepository> _mockProductRepository;
|
||||
private readonly Mock<IMapper> _mockMapper;
|
||||
private readonly ProductService _productService;
|
||||
|
||||
public ProductServiceTests()
|
||||
{
|
||||
_mockProductRepository = new Mock<IProductRepository>();
|
||||
_mockMapper = new Mock<IMapper>();
|
||||
_productService = new ProductService(_mockProductRepository.Object, _mockMapper.Object);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetProductsByCategoryAsync_ReturnsPagedProducts()
|
||||
{
|
||||
// Arrange
|
||||
var categoryId = 1;
|
||||
var page = 1;
|
||||
var pageSize = 10;
|
||||
var sortBy = "Name";
|
||||
var sortOrder = "asc";
|
||||
|
||||
var products = new List<Product>
|
||||
{
|
||||
new Product { Id = 1, Name = "Intel Core i9", CategoryId = categoryId },
|
||||
new Product { Id = 2, Name = "AMD Ryzen 9", CategoryId = categoryId }
|
||||
};
|
||||
|
||||
var productDtos = new List<ProductListDto>
|
||||
{
|
||||
new ProductListDto { Id = 1, Name = "Intel Core i9" },
|
||||
new ProductListDto { Id = 2, Name = "AMD Ryzen 9" }
|
||||
};
|
||||
|
||||
var pagedResult = new PagedResultDto<ProductListDto>
|
||||
{
|
||||
Items = productDtos,
|
||||
TotalCount = 2,
|
||||
Page = page,
|
||||
PageSize = pageSize
|
||||
};
|
||||
|
||||
_mockProductRepository.Setup(repo => repo.GetByCategoryAsync(categoryId, page, pageSize, sortBy, sortOrder))
|
||||
.ReturnsAsync(products);
|
||||
|
||||
_mockProductRepository.Setup(repo => repo.CountAsync(categoryId))
|
||||
.ReturnsAsync(2);
|
||||
|
||||
_mockMapper.Setup(m => m.Map<IEnumerable<ProductListDto>>(products))
|
||||
.Returns(productDtos);
|
||||
|
||||
// Act
|
||||
var result = await _productService.GetProductsByCategoryAsync(categoryId, page, pageSize, sortBy, sortOrder);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(2, result.TotalCount);
|
||||
Assert.Equal(2, result.Items.Count());
|
||||
_mockProductRepository.Verify(repo => repo.GetByCategoryAsync(categoryId, page, pageSize, sortBy, sortOrder), Times.Once);
|
||||
_mockProductRepository.Verify(repo => repo.CountAsync(categoryId), Times.Once);
|
||||
_mockMapper.Verify(m => m.Map<IEnumerable<ProductListDto>>(products), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetProductByIdAsync_ExistingProduct_ReturnsProductDto()
|
||||
{
|
||||
// Arrange
|
||||
var productId = 1;
|
||||
var product = new Product
|
||||
{
|
||||
Id = productId,
|
||||
Name = "Intel Core i9",
|
||||
CategoryId = 1,
|
||||
PerformanceScores = new List<PerformanceScore>(),
|
||||
Specifications = new List<Specification>()
|
||||
};
|
||||
|
||||
var productDto = new ProductDto
|
||||
{
|
||||
Id = productId,
|
||||
Name = "Intel Core i9",
|
||||
CategoryId = 1,
|
||||
PerformanceScores = new List<PerformanceScore>(),
|
||||
Specifications = new List<Specification>()
|
||||
};
|
||||
|
||||
_mockProductRepository.Setup(repo => repo.GetByIdAsync(productId))
|
||||
.ReturnsAsync(product);
|
||||
|
||||
_mockMapper.Setup(m => m.Map<ProductDto>(product))
|
||||
.Returns(productDto);
|
||||
|
||||
// Act
|
||||
var result = await _productService.GetProductByIdAsync(productId);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(productId, result.Id);
|
||||
Assert.Equal("Intel Core i9", result.Name);
|
||||
_mockProductRepository.Verify(repo => repo.GetByIdAsync(productId), Times.Once);
|
||||
_mockMapper.Verify(m => m.Map<ProductDto>(product), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetProductByIdAsync_NonExistingProduct_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var productId = 999;
|
||||
|
||||
_mockProductRepository.Setup(repo => repo.GetByIdAsync(productId))
|
||||
.ReturnsAsync((Product?)null);
|
||||
|
||||
// Act
|
||||
var result = await _productService.GetProductByIdAsync(productId);
|
||||
|
||||
// Assert
|
||||
Assert.Null(result);
|
||||
_mockProductRepository.Verify(repo => repo.GetByIdAsync(productId), Times.Once);
|
||||
_mockMapper.Verify(m => m.Map<ProductDto>(It.IsAny<Product>()), Times.Never);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SearchProductsAsync_ReturnsPagedSearchResults()
|
||||
{
|
||||
// Arrange
|
||||
var query = "Intel";
|
||||
var categoryId = 1;
|
||||
var manufacturer = "Intel";
|
||||
var minScore = 1000;
|
||||
var maxScore = 5000;
|
||||
var page = 1;
|
||||
var pageSize = 10;
|
||||
|
||||
var products = new List<Product>
|
||||
{
|
||||
new Product { Id = 1, Name = "Intel Core i9", Manufacturer = "Intel", CategoryId = categoryId, PerformanceScore = 4000 },
|
||||
new Product { Id = 2, Name = "Intel Core i7", Manufacturer = "Intel", CategoryId = categoryId, PerformanceScore = 3000 }
|
||||
};
|
||||
|
||||
var productDtos = new List<ProductListDto>
|
||||
{
|
||||
new ProductListDto { Id = 1, Name = "Intel Core i9" },
|
||||
new ProductListDto { Id = 2, Name = "Intel Core i7" }
|
||||
};
|
||||
|
||||
var pagedResult = new PagedResultDto<ProductListDto>
|
||||
{
|
||||
Items = productDtos,
|
||||
TotalCount = 2,
|
||||
Page = page,
|
||||
PageSize = pageSize
|
||||
};
|
||||
|
||||
_mockProductRepository.Setup(repo => repo.SearchAsync(query, categoryId, manufacturer, minScore, maxScore, page, pageSize))
|
||||
.ReturnsAsync(products);
|
||||
|
||||
_mockProductRepository.Setup(repo => repo.CountSearchResultsAsync(query, categoryId, manufacturer, minScore, maxScore))
|
||||
.ReturnsAsync(2);
|
||||
|
||||
_mockMapper.Setup(m => m.Map<IEnumerable<ProductListDto>>(products))
|
||||
.Returns(productDtos);
|
||||
|
||||
// Act
|
||||
var result = await _productService.SearchProductsAsync(query, categoryId, manufacturer, minScore, maxScore, page, pageSize);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(result);
|
||||
Assert.Equal(2, result.TotalCount);
|
||||
Assert.Equal(2, result.Items.Count());
|
||||
_mockProductRepository.Verify(repo => repo.SearchAsync(query, categoryId, manufacturer, minScore, maxScore, page, pageSize), Times.Once);
|
||||
_mockProductRepository.Verify(repo => repo.CountSearchResultsAsync(query, categoryId, manufacturer, minScore, maxScore), Times.Once);
|
||||
_mockMapper.Verify(m => m.Map<IEnumerable<ProductListDto>>(products), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
{
|
||||
"format": 1,
|
||||
"restore": {
|
||||
"C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj": {}
|
||||
},
|
||||
"projects": {
|
||||
"C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"projectName": "HardwarePerformance.Tests",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
|
||||
"outputPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"fallbackFolders": [
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net9.0"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {},
|
||||
"https://nuget.cdn.azure.cn/v3/index.json": {},
|
||||
"https://packages.chinacloudapi.cn/v3/index.json": {},
|
||||
"https://packages.microsoft.com/dotnet": {},
|
||||
"https://www.nuget.org/api/v2/": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
},
|
||||
"restoreAuditProperties": {
|
||||
"enableAudit": "true",
|
||||
"auditLevel": "low",
|
||||
"auditMode": "direct"
|
||||
},
|
||||
"SdkAnalysisLevel": "9.0.300"
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"dependencies": {
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"target": "Package",
|
||||
"version": "[17.12.0, )"
|
||||
},
|
||||
"coverlet.collector": {
|
||||
"target": "Package",
|
||||
"version": "[6.0.2, )"
|
||||
},
|
||||
"xunit": {
|
||||
"target": "Package",
|
||||
"version": "[2.9.2, )"
|
||||
},
|
||||
"xunit.runner.visualstudio": {
|
||||
"target": "Package",
|
||||
"version": "[2.8.2, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\代\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\代\.nuget\packages\" />
|
||||
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
||||
@@ -1,126 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"targets": {
|
||||
"net9.0": {}
|
||||
},
|
||||
"libraries": {},
|
||||
"projectFileDependencyGroups": {
|
||||
"net9.0": [
|
||||
"Microsoft.NET.Test.Sdk >= 17.12.0",
|
||||
"coverlet.collector >= 6.0.2",
|
||||
"xunit >= 2.9.2",
|
||||
"xunit.runner.visualstudio >= 2.8.2"
|
||||
]
|
||||
},
|
||||
"packageFolders": {
|
||||
"C:\\Users\\代\\.nuget\\packages\\": {},
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
|
||||
},
|
||||
"project": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"projectName": "HardwarePerformance.Tests",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
|
||||
"outputPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"fallbackFolders": [
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net9.0"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {},
|
||||
"https://nuget.cdn.azure.cn/v3/index.json": {},
|
||||
"https://packages.chinacloudapi.cn/v3/index.json": {},
|
||||
"https://packages.microsoft.com/dotnet": {},
|
||||
"https://www.nuget.org/api/v2/": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
},
|
||||
"restoreAuditProperties": {
|
||||
"enableAudit": "true",
|
||||
"auditLevel": "low",
|
||||
"auditMode": "direct"
|
||||
},
|
||||
"SdkAnalysisLevel": "9.0.300"
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"dependencies": {
|
||||
"Microsoft.NET.Test.Sdk": {
|
||||
"target": "Package",
|
||||
"version": "[17.12.0, )"
|
||||
},
|
||||
"coverlet.collector": {
|
||||
"target": "Package",
|
||||
"version": "[6.0.2, )"
|
||||
},
|
||||
"xunit": {
|
||||
"target": "Package",
|
||||
"version": "[2.9.2, )"
|
||||
},
|
||||
"xunit.runner.visualstudio": {
|
||||
"target": "Package",
|
||||
"version": "[2.8.2, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"logs": [
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='coverlet.collector'&semVerLevel=2.0.0”检索有关“coverlet.collector”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"libraryId": "coverlet.collector"
|
||||
},
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='xunit'&semVerLevel=2.0.0”检索有关“xunit”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"libraryId": "xunit"
|
||||
},
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.NET.Test.Sdk'&semVerLevel=2.0.0”检索有关“Microsoft.NET.Test.Sdk”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"libraryId": "Microsoft.NET.Test.Sdk"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "HWf5d7t+LFg=",
|
||||
"success": false,
|
||||
"projectFilePath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"expectedPackageFiles": [],
|
||||
"logs": [
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='coverlet.collector'&semVerLevel=2.0.0”检索有关“coverlet.collector”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"filePath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"libraryId": "coverlet.collector",
|
||||
"targetGraphs": []
|
||||
},
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='xunit'&semVerLevel=2.0.0”检索有关“xunit”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"filePath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"libraryId": "xunit",
|
||||
"targetGraphs": []
|
||||
},
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.NET.Test.Sdk'&semVerLevel=2.0.0”检索有关“Microsoft.NET.Test.Sdk”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"filePath": "C:\\work\\电脑硬件-01\\HardwarePerformance.Tests\\HardwarePerformance.Tests.csproj",
|
||||
"libraryId": "Microsoft.NET.Test.Sdk",
|
||||
"targetGraphs": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using AutoMapper;
|
||||
using HardwarePerformance.Models;
|
||||
using HardwarePerformance.Models.DTOs;
|
||||
|
||||
namespace HardwarePerformance
|
||||
{
|
||||
public class MappingProfile : Profile
|
||||
{
|
||||
public MappingProfile()
|
||||
{
|
||||
CreateMap<Category, CategoryDto>();
|
||||
|
||||
CreateMap<Product, ProductDto>()
|
||||
.ForMember(dest => dest.PerformanceScores, opt => opt.Ignore())
|
||||
.ForMember(dest => dest.Specifications, opt => opt.Ignore());
|
||||
|
||||
CreateMap<Product, ProductListDto>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace HardwarePerformance.Models.DTOs
|
||||
{
|
||||
public class CategoryDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
namespace HardwarePerformance.Models.DTOs
|
||||
{
|
||||
public class PagedResultDto<T>
|
||||
{
|
||||
public List<T> Items { get; set; } = new();
|
||||
public int Total { get; set; }
|
||||
public int CurrentPage { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public int TotalPages { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace HardwarePerformance.Models.DTOs
|
||||
{
|
||||
public class ProductDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Model { get; set; } = string.Empty;
|
||||
public string Manufacturer { get; set; } = string.Empty;
|
||||
public int CategoryId { get; set; }
|
||||
public int CurrentRank { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
public int? PerformanceScore { get; set; }
|
||||
public CategoryDto? Category { get; set; }
|
||||
public List<PerformanceScore> PerformanceScores { get; set; } = new();
|
||||
public List<Specification> Specifications { get; set; } = new();
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
namespace HardwarePerformance.Models.DTOs
|
||||
{
|
||||
public class ProductListDto
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Model { get; set; } = string.Empty;
|
||||
public string Manufacturer { get; set; } = string.Empty;
|
||||
public int CategoryId { get; set; }
|
||||
public int CurrentRank { get; set; }
|
||||
public DateTime ReleaseDate { get; set; }
|
||||
public decimal? Price { get; set; }
|
||||
public int? PerformanceScore { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace MinimalAPI
|
||||
{
|
||||
// 定义数据模型
|
||||
record Category(int Id, string Name, string Description);
|
||||
record Product(int Id, string Name, string Model, string Manufacturer, int CategoryId, int CurrentRank, DateTime ReleaseDate, decimal? Price);
|
||||
record ApiResponse<T>(bool Success, T? Data, string? Message = null);
|
||||
record PagedResponse<T>(List<T> Items, int TotalCount, int PageNumber, int PageSize, int TotalPages);
|
||||
record ComparisonRequest(List<int> ProductIds);
|
||||
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// 配置响应压缩
|
||||
builder.Services.AddResponseCompression(options =>
|
||||
{
|
||||
options.EnableForHttps = true;
|
||||
options.Providers.Add<BrotliCompressionProvider>();
|
||||
options.Providers.Add<GzipCompressionProvider>();
|
||||
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]
|
||||
{
|
||||
"application/json",
|
||||
"application/javascript",
|
||||
"text/css",
|
||||
"text/html",
|
||||
"text/plain",
|
||||
"text/xml"
|
||||
});
|
||||
});
|
||||
|
||||
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
|
||||
{
|
||||
options.Level = CompressionLevel.Fastest;
|
||||
});
|
||||
|
||||
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
|
||||
{
|
||||
options.Level = CompressionLevel.Fastest;
|
||||
});
|
||||
|
||||
// 配置JSON序列化选项
|
||||
builder.Services.Configure<Microsoft.AspNetCore.Http.Json.JsonOptions>(options =>
|
||||
{
|
||||
options.SerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
|
||||
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
});
|
||||
|
||||
// 配置CORS
|
||||
builder.Services.AddCors(options =>
|
||||
{
|
||||
options.AddPolicy("AllowAll", policy =>
|
||||
{
|
||||
policy.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader();
|
||||
});
|
||||
});
|
||||
|
||||
// 注册服务
|
||||
builder.Services.AddScoped<ICategoryService, CategoryService>();
|
||||
builder.Services.AddScoped<IProductService, ProductService>();
|
||||
builder.Services.AddScoped<IComparisonService, ComparisonService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// 添加响应压缩中间件
|
||||
app.UseResponseCompression();
|
||||
|
||||
// 使用CORS
|
||||
app.UseCors("AllowAll");
|
||||
|
||||
// 模拟数据
|
||||
var categories = new List<Category>
|
||||
{
|
||||
new(1, "手机CPU", "移动设备处理器"),
|
||||
new(2, "手机GPU", "移动设备图形处理器"),
|
||||
new(3, "电脑CPU", "桌面和笔记本处理器"),
|
||||
new(4, "电脑GPU", "桌面和笔记本图形处理器")
|
||||
};
|
||||
|
||||
var products = new List<Product>
|
||||
{
|
||||
new(1, "Apple A17 Pro", "A17 Pro", "Apple", 1, 1, new DateTime(2023, 9, 12), null),
|
||||
new(2, "Snapdragon 8 Gen 3", "SM8650-AB", "Qualcomm", 1, 2, new DateTime(2023, 10, 24), null),
|
||||
new(3, "Intel Core i9-13900K", "Core i9-13900K", "Intel", 3, 1, new DateTime(2022, 10, 20), 589.99m),
|
||||
new(4, "AMD Ryzen 9 7950X", "Ryzen 9 7950X", "AMD", 3, 2, new DateTime(2022, 9, 27), 699.99m),
|
||||
new(5, "NVIDIA GeForce RTX 4090", "RTX 4090", "NVIDIA", 4, 1, new DateTime(2022, 10, 12), 1599.99m),
|
||||
new(6, "AMD Radeon RX 7900 XTX", "RX 7900 XTX", "AMD", 4, 2, new DateTime(2022, 12, 3), 999.99m)
|
||||
};
|
||||
|
||||
// API端点
|
||||
|
||||
// 类别相关
|
||||
app.MapGet("/api/categories", async (ICategoryService categoryService) =>
|
||||
{
|
||||
var categories = await categoryService.GetAllCategoriesAsync();
|
||||
return Results.Ok(new ApiResponse<List<CategoryDto>>(true, categories));
|
||||
});
|
||||
|
||||
app.MapGet("/api/categories/{id}", async (int id, ICategoryService categoryService) =>
|
||||
{
|
||||
var category = await categoryService.GetCategoryByIdAsync(id);
|
||||
if (category == null)
|
||||
{
|
||||
return Results.NotFound(new ApiResponse<object>(false, null, "类别不存在"));
|
||||
}
|
||||
return Results.Ok(new ApiResponse<CategoryDto>(true, category));
|
||||
});
|
||||
|
||||
// 产品API
|
||||
app.MapGet("/api/products", async (int? categoryId, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string sortOrder = "asc", IProductService productService) =>
|
||||
{
|
||||
if (!categoryId.HasValue)
|
||||
{
|
||||
return Results.BadRequest(new ApiResponse<object>(false, null, "类别ID是必需的"));
|
||||
}
|
||||
|
||||
var pagedResult = await productService.GetProductsByCategoryAsync(categoryId.Value, page, pageSize, sortBy, sortOrder);
|
||||
return Results.Ok(new ApiResponse<PagedResultDto<ProductListDto>>(true, pagedResult));
|
||||
});
|
||||
|
||||
app.MapGet("/api/products/{id}", async (int id, IProductService productService) =>
|
||||
{
|
||||
var product = await productService.GetProductByIdAsync(id);
|
||||
if (product == null)
|
||||
{
|
||||
return Results.NotFound(new ApiResponse<object>(false, null, "产品不存在"));
|
||||
}
|
||||
return Results.Ok(new ApiResponse<ProductDto>(true, product));
|
||||
});
|
||||
|
||||
app.MapGet("/api/products/search", async (string q, int? categoryId, string? manufacturer, decimal? minScore, decimal? maxScore, int page = 1, int pageSize = 20, IProductService productService) =>
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(q))
|
||||
{
|
||||
return Results.BadRequest(new ApiResponse<object>(false, null, "搜索关键词不能为空"));
|
||||
}
|
||||
|
||||
var pagedResult = await productService.SearchProductsAsync(q, categoryId, manufacturer, minScore, maxScore, page, pageSize);
|
||||
return Results.Ok(new ApiResponse<PagedResultDto<ProductListDto>>(true, pagedResult));
|
||||
});
|
||||
|
||||
app.MapGet("/api/products/filter", async (int categoryId, string? manufacturer, decimal? minScore, decimal? maxScore, int? year, int page = 1, int pageSize = 20, IProductService productService) =>
|
||||
{
|
||||
var pagedResult = await productService.FilterProductsAsync(categoryId, manufacturer, minScore, maxScore, year, page, pageSize);
|
||||
return Results.Ok(new ApiResponse<PagedResultDto<ProductListDto>>(true, pagedResult));
|
||||
});
|
||||
|
||||
// 产品对比
|
||||
app.MapPost("/api/comparison", async (ComparisonRequest request, IComparisonService comparisonService) =>
|
||||
{
|
||||
// 验证请求
|
||||
if (request.ProductIds == null || request.ProductIds.Count < 2 || request.ProductIds.Count > 4)
|
||||
{
|
||||
return Results.BadRequest(new ApiResponse<object>(false, null, "产品ID数量必须在2到4之间"));
|
||||
}
|
||||
|
||||
var comparisonData = await comparisonService.CompareProductsAsync(request.ProductIds);
|
||||
if (comparisonData == null)
|
||||
{
|
||||
return Results.NotFound(new ApiResponse<object>(false, null, "无法获取产品对比数据"));
|
||||
}
|
||||
|
||||
return Results.Ok(new ApiResponse<object>(true, comparisonData));
|
||||
});
|
||||
|
||||
// 状态检查端点
|
||||
app.MapGet("/api/status", () => new
|
||||
{
|
||||
Status = "Running",
|
||||
Message = "API服务正常运行",
|
||||
Timestamp = DateTime.Now,
|
||||
Version = "1.0.0"
|
||||
});
|
||||
|
||||
app.Run();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5172",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7055;http://localhost:5172",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using HardwarePerformance.Data;
|
||||
using HardwarePerformance.Models;
|
||||
|
||||
namespace HardwarePerformance.Repositories
|
||||
{
|
||||
public class CategoryRepository : Repository<Category>, ICategoryRepository
|
||||
{
|
||||
public CategoryRepository(AppDbContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<Category?> GetByNameAsync(string name)
|
||||
{
|
||||
return await _dbSet.FirstOrDefaultAsync(c => c.Name == name);
|
||||
}
|
||||
|
||||
public async Task<bool> ExistsAsync(int id)
|
||||
{
|
||||
return await _dbSet.AnyAsync(c => c.Id == id);
|
||||
}
|
||||
|
||||
public async Task<bool> NameExistsAsync(string name)
|
||||
{
|
||||
return await _dbSet.AnyAsync(c => c.Name == name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using HardwarePerformance.Models;
|
||||
|
||||
namespace HardwarePerformance.Repositories
|
||||
{
|
||||
public interface ICategoryRepository : IRepository<Category>
|
||||
{
|
||||
Task<Category?> GetByNameAsync(string name);
|
||||
Task<bool> ExistsAsync(int id);
|
||||
Task<bool> NameExistsAsync(string name);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using HardwarePerformance.Models;
|
||||
|
||||
namespace HardwarePerformance.Repositories
|
||||
{
|
||||
public interface IProductRepository : IRepository<Product>
|
||||
{
|
||||
Task<IEnumerable<Product>> GetByCategoryAsync(int categoryId);
|
||||
Task<IEnumerable<Product>> GetByCategoryAsync(int categoryId, int page, int pageSize, string sortBy = "CurrentRank", string sortOrder = "asc");
|
||||
Task<IEnumerable<Product>> SearchAsync(string query);
|
||||
Task<IEnumerable<Product>> SearchAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null, int page = 1, int pageSize = 20);
|
||||
Task<IEnumerable<Product>> FilterAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year);
|
||||
Task<IEnumerable<Product>> FilterAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year, int page, int pageSize, string sortBy = "CurrentRank", string sortOrder = "asc");
|
||||
Task<int> CountAsync(int? categoryId = null);
|
||||
Task<int> CountSearchResultsAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null);
|
||||
Task<int> CountFilterResultsAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year);
|
||||
Task<Category?> GetCategoryByProductIdAsync(int productId);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using System.Linq.Expressions;
|
||||
using HardwarePerformance.Models;
|
||||
|
||||
namespace HardwarePerformance.Repositories
|
||||
{
|
||||
public interface IRepository<T> where T : class
|
||||
{
|
||||
Task<T?> GetByIdAsync(int id);
|
||||
Task<IEnumerable<T>> GetAllAsync();
|
||||
Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate);
|
||||
Task<T> AddAsync(T entity);
|
||||
Task UpdateAsync(T entity);
|
||||
Task DeleteAsync(T entity);
|
||||
Task<int> CountAsync(Expression<Func<T, bool>>? predicate = null);
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using HardwarePerformance.Data;
|
||||
using HardwarePerformance.Models;
|
||||
|
||||
namespace HardwarePerformance.Repositories
|
||||
{
|
||||
public class ProductRepository : Repository<Product>, IProductRepository
|
||||
{
|
||||
public ProductRepository(AppDbContext context) : base(context)
|
||||
{
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Product>> GetByCategoryAsync(int categoryId)
|
||||
{
|
||||
return await _dbSet
|
||||
.Where(p => p.CategoryId == categoryId)
|
||||
.OrderBy(p => p.CurrentRank)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Product>> GetByCategoryAsync(int categoryId, int page, int pageSize, string sortBy = "CurrentRank", string sortOrder = "asc")
|
||||
{
|
||||
var query = _dbSet.Where(p => p.CategoryId == categoryId);
|
||||
|
||||
// 排序
|
||||
query = sortBy.ToLower() switch
|
||||
{
|
||||
"name" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Name) : query.OrderBy(p => p.Name),
|
||||
"manufacturer" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Manufacturer) : query.OrderBy(p => p.Manufacturer),
|
||||
"releasedate" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.ReleaseDate) : query.OrderBy(p => p.ReleaseDate),
|
||||
"price" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Price) : query.OrderBy(p => p.Price),
|
||||
_ => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.CurrentRank) : query.OrderBy(p => p.CurrentRank)
|
||||
};
|
||||
|
||||
return await query
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Product>> SearchAsync(string query)
|
||||
{
|
||||
return await _dbSet
|
||||
.Where(p =>
|
||||
p.Name.Contains(query) ||
|
||||
p.Model.Contains(query) ||
|
||||
p.Manufacturer.Contains(query))
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Product>> SearchAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null, int page = 1, int pageSize = 20)
|
||||
{
|
||||
var queryable = _dbSet.AsQueryable();
|
||||
|
||||
if (categoryId.HasValue)
|
||||
{
|
||||
queryable = queryable.Where(p => p.CategoryId == categoryId.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(manufacturer))
|
||||
{
|
||||
queryable = queryable.Where(p => p.Manufacturer == manufacturer);
|
||||
}
|
||||
|
||||
if (minScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
queryable = queryable.Where(p => (100 - p.CurrentRank) >= minScore.Value);
|
||||
}
|
||||
|
||||
if (maxScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
queryable = queryable.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query))
|
||||
{
|
||||
queryable = queryable.Where(p =>
|
||||
p.Name.Contains(query) ||
|
||||
p.Model.Contains(query) ||
|
||||
p.Manufacturer.Contains(query));
|
||||
}
|
||||
|
||||
return await queryable
|
||||
.OrderBy(p => p.CurrentRank)
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Product>> FilterAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year)
|
||||
{
|
||||
var query = _dbSet.AsQueryable();
|
||||
|
||||
if (categoryId.HasValue)
|
||||
{
|
||||
query = query.Where(p => p.CategoryId == categoryId.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(manufacturer))
|
||||
{
|
||||
query = query.Where(p => p.Manufacturer == manufacturer);
|
||||
}
|
||||
|
||||
if (minScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
query = query.Where(p => (100 - p.CurrentRank) >= minScore.Value);
|
||||
}
|
||||
|
||||
if (maxScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
query = query.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
|
||||
}
|
||||
|
||||
if (year.HasValue)
|
||||
{
|
||||
query = query.Where(p => p.ReleaseDate.Year == year.Value);
|
||||
}
|
||||
|
||||
return await query.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<Product>> FilterAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year, int page, int pageSize, string sortBy = "CurrentRank", string sortOrder = "asc")
|
||||
{
|
||||
var query = _dbSet.AsQueryable();
|
||||
|
||||
if (categoryId.HasValue)
|
||||
{
|
||||
query = query.Where(p => p.CategoryId == categoryId.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(manufacturer))
|
||||
{
|
||||
query = query.Where(p => p.Manufacturer == manufacturer);
|
||||
}
|
||||
|
||||
if (minScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
query = query.Where(p => (100 - p.CurrentRank) >= minScore.Value);
|
||||
}
|
||||
|
||||
if (maxScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
query = query.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
|
||||
}
|
||||
|
||||
if (year.HasValue)
|
||||
{
|
||||
query = query.Where(p => p.ReleaseDate.Year == year.Value);
|
||||
}
|
||||
|
||||
// 排序
|
||||
query = sortBy.ToLower() switch
|
||||
{
|
||||
"name" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Name) : query.OrderBy(p => p.Name),
|
||||
"manufacturer" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Manufacturer) : query.OrderBy(p => p.Manufacturer),
|
||||
"releasedate" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.ReleaseDate) : query.OrderBy(p => p.ReleaseDate),
|
||||
"price" => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.Price) : query.OrderBy(p => p.Price),
|
||||
_ => sortOrder.ToLower() == "desc" ? query.OrderByDescending(p => p.CurrentRank) : query.OrderBy(p => p.CurrentRank)
|
||||
};
|
||||
|
||||
return await query
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<int> CountAsync(int? categoryId = null)
|
||||
{
|
||||
var query = _dbSet.AsQueryable();
|
||||
|
||||
if (categoryId.HasValue)
|
||||
{
|
||||
query = query.Where(p => p.CategoryId == categoryId.Value);
|
||||
}
|
||||
|
||||
return await query.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<int> CountSearchResultsAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null)
|
||||
{
|
||||
var queryable = _dbSet.AsQueryable();
|
||||
|
||||
if (categoryId.HasValue)
|
||||
{
|
||||
queryable = queryable.Where(p => p.CategoryId == categoryId.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(manufacturer))
|
||||
{
|
||||
queryable = queryable.Where(p => p.Manufacturer == manufacturer);
|
||||
}
|
||||
|
||||
if (minScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
queryable = queryable.Where(p => (100 - p.CurrentRank) >= minScore.Value);
|
||||
}
|
||||
|
||||
if (maxScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
queryable = queryable.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(query))
|
||||
{
|
||||
queryable = queryable.Where(p =>
|
||||
p.Name.Contains(query) ||
|
||||
p.Model.Contains(query) ||
|
||||
p.Manufacturer.Contains(query));
|
||||
}
|
||||
|
||||
return await queryable.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<int> CountFilterResultsAsync(int? categoryId, string? manufacturer, int? minScore, int? maxScore, int? year)
|
||||
{
|
||||
var query = _dbSet.AsQueryable();
|
||||
|
||||
if (categoryId.HasValue)
|
||||
{
|
||||
query = query.Where(p => p.CategoryId == categoryId.Value);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(manufacturer))
|
||||
{
|
||||
query = query.Where(p => p.Manufacturer == manufacturer);
|
||||
}
|
||||
|
||||
if (minScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
query = query.Where(p => (100 - p.CurrentRank) >= minScore.Value);
|
||||
}
|
||||
|
||||
if (maxScore.HasValue)
|
||||
{
|
||||
// 使用排名计算性能分数(100 - 排名)
|
||||
query = query.Where(p => (100 - p.CurrentRank) <= maxScore.Value);
|
||||
}
|
||||
|
||||
if (year.HasValue)
|
||||
{
|
||||
query = query.Where(p => p.ReleaseDate.Year == year.Value);
|
||||
}
|
||||
|
||||
return await query.CountAsync();
|
||||
}
|
||||
|
||||
public async Task<Category?> GetCategoryByProductIdAsync(int productId)
|
||||
{
|
||||
var product = await _dbSet
|
||||
.Include(p => p.Category)
|
||||
.FirstOrDefaultAsync(p => p.Id == productId);
|
||||
|
||||
return product?.Category;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using HardwarePerformance.Data;
|
||||
using HardwarePerformance.Models;
|
||||
|
||||
namespace HardwarePerformance.Repositories
|
||||
{
|
||||
public class Repository<T> : IRepository<T> where T : class
|
||||
{
|
||||
protected readonly AppDbContext _context;
|
||||
protected readonly DbSet<T> _dbSet;
|
||||
|
||||
public Repository(AppDbContext context)
|
||||
{
|
||||
_context = context;
|
||||
_dbSet = context.Set<T>();
|
||||
}
|
||||
|
||||
public async Task<T?> GetByIdAsync(int id)
|
||||
{
|
||||
return await _dbSet.FindAsync(id);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<T>> GetAllAsync()
|
||||
{
|
||||
return await _dbSet.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate)
|
||||
{
|
||||
return await _dbSet.Where(predicate).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<T> AddAsync(T entity)
|
||||
{
|
||||
await _dbSet.AddAsync(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
return entity;
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(T entity)
|
||||
{
|
||||
_dbSet.Update(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(T entity)
|
||||
{
|
||||
_dbSet.Remove(entity);
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task<int> CountAsync(Expression<Func<T, bool>>? predicate = null)
|
||||
{
|
||||
if (predicate == null)
|
||||
return await _dbSet.CountAsync();
|
||||
return await _dbSet.CountAsync(predicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
using AutoMapper;
|
||||
using HardwarePerformance.Models.DTOs;
|
||||
using HardwarePerformance.Repositories;
|
||||
|
||||
namespace HardwarePerformance.Services
|
||||
{
|
||||
public class CategoryService : ICategoryService
|
||||
{
|
||||
private readonly ICategoryRepository _categoryRepository;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public CategoryService(ICategoryRepository categoryRepository, IMapper mapper)
|
||||
{
|
||||
_categoryRepository = categoryRepository;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CategoryDto>> GetAllCategoriesAsync()
|
||||
{
|
||||
var categories = await _categoryRepository.GetAllAsync();
|
||||
return _mapper.Map<IEnumerable<CategoryDto>>(categories);
|
||||
}
|
||||
|
||||
public async Task<CategoryDto?> GetCategoryByIdAsync(int id)
|
||||
{
|
||||
var category = await _categoryRepository.GetByIdAsync(id);
|
||||
return category != null ? _mapper.Map<CategoryDto>(category) : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MinimalAPI.Data;
|
||||
using MinimalAPI.Models;
|
||||
using MinimalAPI.Models.DTOs;
|
||||
|
||||
namespace MinimalAPI.Services
|
||||
{
|
||||
public class ComparisonService : IComparisonService
|
||||
{
|
||||
private readonly AppDbContext _context;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public ComparisonService(AppDbContext context, IMapper mapper)
|
||||
{
|
||||
_context = context;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<object> CompareProductsAsync(List<int> productIds)
|
||||
{
|
||||
// 验证输入
|
||||
if (productIds == null || productIds.Count < 2 || productIds.Count > 4)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取产品
|
||||
var products = await _context.Products
|
||||
.Include(p => p.Category)
|
||||
.Where(p => productIds.Contains(p.Id))
|
||||
.ToListAsync();
|
||||
|
||||
if (products.Count != productIds.Count)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 检查是否所有产品属于同一类别
|
||||
var categoryIds = products.Select(p => p.CategoryId).Distinct().ToList();
|
||||
if (categoryIds.Count > 1)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取性能分数
|
||||
var productIdsList = products.Select(p => p.Id).ToList();
|
||||
var performanceScores = await _context.PerformanceScores
|
||||
.Where(ps => productIdsList.Contains(ps.ProductId))
|
||||
.ToListAsync();
|
||||
|
||||
// 获取规格参数
|
||||
var specifications = await _context.Specifications
|
||||
.Where(s => productIdsList.Contains(s.ProductId))
|
||||
.ToListAsync();
|
||||
|
||||
// 创建产品DTO列表
|
||||
var productDtos = products.Select(p => {
|
||||
var dto = _mapper.Map<ProductDto>(p);
|
||||
dto.PerformanceScores = performanceScores
|
||||
.Where(ps => ps.ProductId == p.Id)
|
||||
.ToList();
|
||||
dto.Specifications = specifications
|
||||
.Where(s => s.ProductId == p.Id)
|
||||
.ToList();
|
||||
return dto;
|
||||
}).ToList();
|
||||
|
||||
// 生成对比矩阵
|
||||
var comparisonMatrix = GenerateComparisonMatrix(productDtos);
|
||||
|
||||
return new
|
||||
{
|
||||
Products = productDtos,
|
||||
Comparison = comparisonMatrix
|
||||
};
|
||||
}
|
||||
|
||||
private List<Dictionary<string, object>> GenerateComparisonMatrix(List<ProductDto> products)
|
||||
{
|
||||
var matrix = new List<Dictionary<string, object>>();
|
||||
|
||||
// 添加基本信息行
|
||||
matrix.Add(new Dictionary<string, object> { ["指标"] = "产品名称", ["类型"] = "基本信息" });
|
||||
foreach (var product in products)
|
||||
{
|
||||
matrix[0][$"{product.Name}"] = product.Name;
|
||||
}
|
||||
|
||||
// 添加制造商行
|
||||
matrix.Add(new Dictionary<string, object> { ["指标"] = "制造商", ["类型"] = "基本信息" });
|
||||
foreach (var product in products)
|
||||
{
|
||||
matrix[1][$"{product.Name}"] = product.Manufacturer;
|
||||
}
|
||||
|
||||
// 添加型号行
|
||||
matrix.Add(new Dictionary<string, object> { ["指标"] = "型号", ["类型"] = "基本信息" });
|
||||
foreach (var product in products)
|
||||
{
|
||||
matrix[2][$"{product.Name}"] = product.Model;
|
||||
}
|
||||
|
||||
// 添加排名行
|
||||
matrix.Add(new Dictionary<string, object> { ["指标"] = "当前排名", ["类型"] = "性能指标" });
|
||||
foreach (var product in products)
|
||||
{
|
||||
matrix[3][$"{product.Name}"] = product.CurrentRank;
|
||||
}
|
||||
|
||||
// 添加发布日期行
|
||||
matrix.Add(new Dictionary<string, object> { ["指标"] = "发布日期", ["类型"] = "基本信息" });
|
||||
foreach (var product in products)
|
||||
{
|
||||
matrix[4][$"{product.Name}"] = product.ReleaseDate.ToString("yyyy-MM-dd");
|
||||
}
|
||||
|
||||
// 添加价格行
|
||||
matrix.Add(new Dictionary<string, object> { ["指标"] = "价格", ["类型"] = "基本信息" });
|
||||
foreach (var product in products)
|
||||
{
|
||||
matrix[5][$"{product.Name}"] = product.Price?.ToString("C") ?? "N/A";
|
||||
}
|
||||
|
||||
// 添加性能分数行
|
||||
var benchmarkTypes = products
|
||||
.SelectMany(p => p.PerformanceScores)
|
||||
.Select(ps => ps.BenchmarkType)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
foreach (var benchmarkType in benchmarkTypes)
|
||||
{
|
||||
var rowIndex = matrix.Count;
|
||||
matrix.Add(new Dictionary<string, object> { ["指标"] = benchmarkType, ["类型"] = "性能指标" });
|
||||
|
||||
foreach (var product in products)
|
||||
{
|
||||
var score = product.PerformanceScores
|
||||
.FirstOrDefault(ps => ps.BenchmarkType == benchmarkType)?.Score;
|
||||
matrix[rowIndex][$"{product.Name}"] = score?.ToString() ?? "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
// 添加规格参数行
|
||||
var specNames = products
|
||||
.SelectMany(p => p.Specifications)
|
||||
.Select(s => s.Name)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
foreach (var specName in specNames)
|
||||
{
|
||||
var rowIndex = matrix.Count;
|
||||
matrix.Add(new Dictionary<string, object> { ["指标"] = specName, ["类型"] = "规格参数" });
|
||||
|
||||
foreach (var product in products)
|
||||
{
|
||||
var specValue = product.Specifications
|
||||
.FirstOrDefault(s => s.Name == specName)?.Value;
|
||||
matrix[rowIndex][$"{product.Name}"] = specValue ?? "N/A";
|
||||
}
|
||||
}
|
||||
|
||||
// 标记最优和最差值
|
||||
MarkBestAndWorstValues(matrix, products);
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
private void MarkBestAndWorstValues(List<Dictionary<string, object>> matrix, List<ProductDto> products)
|
||||
{
|
||||
// 对于排名,越小越好
|
||||
var rankRow = matrix.FirstOrDefault(m => m["指标"].ToString() == "当前排名");
|
||||
if (rankRow != null)
|
||||
{
|
||||
var ranks = products.Select(p => p.CurrentRank).ToList();
|
||||
var minRank = ranks.Min();
|
||||
var maxRank = ranks.Max();
|
||||
|
||||
foreach (var product in products)
|
||||
{
|
||||
var rank = product.CurrentRank;
|
||||
if (rank == minRank)
|
||||
{
|
||||
rankRow[$"{product.Name}_isBest"] = true;
|
||||
}
|
||||
else if (rank == maxRank)
|
||||
{
|
||||
rankRow[$"{product.Name}_isWorst"] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对于价格,越小越好
|
||||
var priceRow = matrix.FirstOrDefault(m => m["指标"].ToString() == "价格");
|
||||
if (priceRow != null)
|
||||
{
|
||||
var prices = products.Where(p => p.Price.HasValue).Select(p => p.Price!.Value).ToList();
|
||||
if (prices.Any())
|
||||
{
|
||||
var minPrice = prices.Min();
|
||||
var maxPrice = prices.Max();
|
||||
|
||||
foreach (var product in products)
|
||||
{
|
||||
if (product.Price.HasValue)
|
||||
{
|
||||
if (product.Price == minPrice)
|
||||
{
|
||||
priceRow[$"{product.Name}_isBest"] = true;
|
||||
}
|
||||
else if (product.Price == maxPrice)
|
||||
{
|
||||
priceRow[$"{product.Name}_isWorst"] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 对于性能分数,越大越好
|
||||
var performanceRows = matrix.Where(m => m["类型"].ToString() == "性能指标" &&
|
||||
m["指标"].ToString() != "当前排名").ToList();
|
||||
|
||||
foreach (var row in performanceRows)
|
||||
{
|
||||
var benchmarkType = row["指标"].ToString();
|
||||
var scores = new List<decimal?>();
|
||||
|
||||
foreach (var product in products)
|
||||
{
|
||||
var scoreStr = row[$"{product.Name}"]?.ToString();
|
||||
if (decimal.TryParse(scoreStr, out var score))
|
||||
{
|
||||
scores.Add(score);
|
||||
}
|
||||
else
|
||||
{
|
||||
scores.Add(null);
|
||||
}
|
||||
}
|
||||
|
||||
var validScores = scores.Where(s => s.HasValue).Select(s => s!.Value).ToList();
|
||||
if (validScores.Any())
|
||||
{
|
||||
var maxScore = validScores.Max();
|
||||
var minScore = validScores.Min();
|
||||
|
||||
for (int i = 0; i < products.Count; i++)
|
||||
{
|
||||
if (scores[i].HasValue)
|
||||
{
|
||||
if (scores[i] == maxScore)
|
||||
{
|
||||
row[$"{products[i].Name}_isBest"] = true;
|
||||
}
|
||||
else if (scores[i] == minScore)
|
||||
{
|
||||
row[$"{products[i].Name}_isWorst"] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using HardwarePerformance.Models.DTOs;
|
||||
|
||||
namespace HardwarePerformance.Services
|
||||
{
|
||||
public interface ICategoryService
|
||||
{
|
||||
Task<IEnumerable<CategoryDto>> GetAllCategoriesAsync();
|
||||
Task<CategoryDto?> GetCategoryByIdAsync(int id);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MinimalAPI.Services
|
||||
{
|
||||
public interface IComparisonService
|
||||
{
|
||||
Task<object> CompareProductsAsync(List<int> productIds);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
using HardwarePerformance.Models.DTOs;
|
||||
|
||||
namespace HardwarePerformance.Services
|
||||
{
|
||||
public interface IProductService
|
||||
{
|
||||
Task<PagedResultDto<ProductListDto>> GetProductsByCategoryAsync(int categoryId, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string order = "asc");
|
||||
Task<ProductDto?> GetProductByIdAsync(int id);
|
||||
Task<PagedResultDto<ProductListDto>> SearchProductsAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null, int page = 1, int pageSize = 20);
|
||||
Task<PagedResultDto<ProductListDto>> FilterProductsAsync(int categoryId, string? manufacturer = null, int? minScore = null, int? maxScore = null, int? releaseYear = null, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string order = "asc");
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
using AutoMapper;
|
||||
using HardwarePerformance.Models.DTOs;
|
||||
using HardwarePerformance.Repositories;
|
||||
|
||||
namespace HardwarePerformance.Services
|
||||
{
|
||||
public class ProductService : IProductService
|
||||
{
|
||||
private readonly IProductRepository _productRepository;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public ProductService(IProductRepository productRepository, IMapper mapper)
|
||||
{
|
||||
_productRepository = productRepository;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public async Task<PagedResultDto<ProductListDto>> GetProductsByCategoryAsync(int categoryId, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string order = "asc")
|
||||
{
|
||||
var products = await _productRepository.GetByCategoryAsync(categoryId, page, pageSize, sortBy, order);
|
||||
var totalCount = await _productRepository.CountAsync(categoryId);
|
||||
|
||||
var productDtos = _mapper.Map<IEnumerable<ProductListDto>>(products);
|
||||
|
||||
return new PagedResultDto<ProductListDto>
|
||||
{
|
||||
Items = productDtos.ToList(),
|
||||
Total = totalCount,
|
||||
CurrentPage = page,
|
||||
PageSize = pageSize,
|
||||
TotalPages = (int)Math.Ceiling((double)totalCount / pageSize)
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<ProductDto?> GetProductByIdAsync(int id)
|
||||
{
|
||||
var product = await _productRepository.GetByIdAsync(id);
|
||||
if (product == null) return null;
|
||||
|
||||
var productDto = _mapper.Map<ProductDto>(product);
|
||||
|
||||
// 获取类别信息
|
||||
var category = await _productRepository.GetCategoryByProductIdAsync(id);
|
||||
if (category != null)
|
||||
{
|
||||
productDto.Category = new CategoryDto
|
||||
{
|
||||
Id = category.Id,
|
||||
Name = category.Name,
|
||||
Description = category.Description
|
||||
};
|
||||
}
|
||||
|
||||
return productDto;
|
||||
}
|
||||
|
||||
public async Task<PagedResultDto<ProductListDto>> SearchProductsAsync(string query, int? categoryId = null, string? manufacturer = null, int? minScore = null, int? maxScore = null, int page = 1, int pageSize = 20)
|
||||
{
|
||||
var products = await _productRepository.SearchAsync(query, categoryId, manufacturer, minScore, maxScore, page, pageSize);
|
||||
var totalCount = await _productRepository.CountSearchResultsAsync(query, categoryId, manufacturer, minScore, maxScore);
|
||||
|
||||
var productDtos = _mapper.Map<IEnumerable<ProductListDto>>(products);
|
||||
|
||||
return new PagedResultDto<ProductListDto>
|
||||
{
|
||||
Items = productDtos.ToList(),
|
||||
Total = totalCount,
|
||||
CurrentPage = page,
|
||||
PageSize = pageSize,
|
||||
TotalPages = (int)Math.Ceiling((double)totalCount / pageSize)
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<PagedResultDto<ProductListDto>> FilterProductsAsync(int categoryId, string? manufacturer = null, int? minScore = null, int? maxScore = null, int? releaseYear = null, int page = 1, int pageSize = 20, string sortBy = "CurrentRank", string order = "asc")
|
||||
{
|
||||
var products = await _productRepository.FilterAsync(categoryId, manufacturer, minScore, maxScore, releaseYear, page, pageSize, sortBy, order);
|
||||
var totalCount = await _productRepository.CountFilterResultsAsync(categoryId, manufacturer, minScore, maxScore, releaseYear);
|
||||
|
||||
var productDtos = _mapper.Map<IEnumerable<ProductListDto>>(products);
|
||||
|
||||
return new PagedResultDto<ProductListDto>
|
||||
{
|
||||
Items = productDtos.ToList(),
|
||||
Total = totalCount,
|
||||
CurrentPage = page,
|
||||
PageSize = pageSize,
|
||||
TotalPages = (int)Math.Ceiling((double)totalCount / pageSize)
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v9.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v9.0": {
|
||||
"MinimalAPI/1.0.0": {
|
||||
"runtime": {
|
||||
"MinimalAPI.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"MinimalAPI/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
d440d87b19ec785d73e3e23306ab9680f7c4890517e977a1c254363c4f9347af
|
||||
@@ -1,21 +0,0 @@
|
||||
is_global = true
|
||||
build_property.TargetFramework = net9.0
|
||||
build_property.TargetPlatformMinVersion =
|
||||
build_property.UsingMicrosoftNETSdkWeb = true
|
||||
build_property.ProjectTypeGuids =
|
||||
build_property.InvariantGlobalization =
|
||||
build_property.PlatformNeutralAssembly =
|
||||
build_property.EnforceExtendedAnalyzerRules =
|
||||
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||
build_property.RootNamespace = MinimalAPI
|
||||
build_property.RootNamespace = MinimalAPI
|
||||
build_property.ProjectDir = C:\work\电脑硬件-01\MinimalAPI\
|
||||
build_property.EnableComHosting =
|
||||
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||
build_property.RazorLangVersion = 9.0
|
||||
build_property.SupportLocalizedComponentNames =
|
||||
build_property.GenerateRazorMetadataSourceChecksumAttributes =
|
||||
build_property.MSBuildProjectDirectory = C:\work\电脑硬件-01\MinimalAPI
|
||||
build_property._RazorSourceGeneratorDebug =
|
||||
build_property.EffectiveAnalysisLevelStyle = 9.0
|
||||
build_property.EnableCodeStyleSeverity =
|
||||
@@ -1,17 +0,0 @@
|
||||
// <auto-generated/>
|
||||
global using global::Microsoft.AspNetCore.Builder;
|
||||
global using global::Microsoft.AspNetCore.Hosting;
|
||||
global using global::Microsoft.AspNetCore.Http;
|
||||
global using global::Microsoft.AspNetCore.Routing;
|
||||
global using global::Microsoft.Extensions.Configuration;
|
||||
global using global::Microsoft.Extensions.DependencyInjection;
|
||||
global using global::Microsoft.Extensions.Hosting;
|
||||
global using global::Microsoft.Extensions.Logging;
|
||||
global using global::System;
|
||||
global using global::System.Collections.Generic;
|
||||
global using global::System.IO;
|
||||
global using global::System.Linq;
|
||||
global using global::System.Net.Http;
|
||||
global using global::System.Net.Http.Json;
|
||||
global using global::System.Threading;
|
||||
global using global::System.Threading.Tasks;
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
05c277f9ef6d1c96b7c3a0662bb97967ee23e63c93d8aa1cd4aec40fc1c9febe
|
||||
@@ -1,27 +0,0 @@
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\rpswa.dswa.cache.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.GeneratedMSBuildEditorConfig.editorconfig
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.AssemblyInfoInputs.cache
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.AssemblyInfo.cs
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.csproj.CoreCompileInputs.cache
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.MvcApplicationPartsAssemblyInfo.cache
|
||||
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\appsettings.Development.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\appsettings.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.staticwebassets.endpoints.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.exe
|
||||
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.deps.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.runtimeconfig.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.dll
|
||||
C:\work\电脑硬件-01\MinimalAPI\bin\Debug\net9.0\MinimalAPI.pdb
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\rjimswa.dswa.cache.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\rjsmrazor.dswa.cache.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\rjsmcshtml.dswa.cache.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\scopedcss\bundle\MinimalAPI.styles.css
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\staticwebassets.build.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\staticwebassets.build.json.cache
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\staticwebassets.development.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\staticwebassets.build.endpoints.json
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.dll
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\refint\MinimalAPI.dll
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.pdb
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\MinimalAPI.genruntimeconfig.cache
|
||||
C:\work\电脑硬件-01\MinimalAPI\obj\Debug\net9.0\ref\MinimalAPI.dll
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
2cc508c4c984e00de0cee28ea5f00a982100be8dad563e9011ff5619c65193f1
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"GlobalPropertiesHash":"UvHx1BUBnfpFc6gxIpp/8mxp/Pr5ugj++Q4ag8rCqAY=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["vICfSWyJTl7Llwoo5pZeHmuInfb\u002BPDuiEXf67e03aVY=","HGCAjAUzY86/cTTfeLk8LuqAnP1lIVdvBKQP\u002BJ23JAc=","pXPB2q0SLSiqgQ6QWxaK7/r8KBQLxQCCriy2hCwae1A=","eZ0avPpvDZzkkkg3Y40\u002B9/FcOts1TlMSAvHEIAFbU8Q="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +0,0 @@
|
||||
{"GlobalPropertiesHash":"N05q42TiGbCpQThCMeppx1cqHYMYPr+MKOVECH+LM5o=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["vICfSWyJTl7Llwoo5pZeHmuInfb\u002BPDuiEXf67e03aVY=","HGCAjAUzY86/cTTfeLk8LuqAnP1lIVdvBKQP\u002BJ23JAc=","pXPB2q0SLSiqgQ6QWxaK7/r8KBQLxQCCriy2hCwae1A=","eZ0avPpvDZzkkkg3Y40\u002B9/FcOts1TlMSAvHEIAFbU8Q="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +0,0 @@
|
||||
{"GlobalPropertiesHash":"Qscza4zuOZL6qExaAaOGJZbKXeiSjSngMQ6k1ALDEr8=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["vICfSWyJTl7Llwoo5pZeHmuInfb\u002BPDuiEXf67e03aVY=","HGCAjAUzY86/cTTfeLk8LuqAnP1lIVdvBKQP\u002BJ23JAc="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +0,0 @@
|
||||
{"Version":1,"Hash":"JkTLtN583la2vWjETk7JpCURqkzL9J5tTWuu+nxCKow=","Source":"MinimalAPI","BasePath":"_content/MinimalAPI","Mode":"Default","ManifestType":"Build","ReferencedProjectsConfiguration":[],"DiscoveryPatterns":[],"Assets":[],"Endpoints":[]}
|
||||
@@ -1 +0,0 @@
|
||||
JkTLtN583la2vWjETk7JpCURqkzL9J5tTWuu+nxCKow=
|
||||
@@ -1,86 +0,0 @@
|
||||
{
|
||||
"format": 1,
|
||||
"restore": {
|
||||
"C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj": {}
|
||||
},
|
||||
"projects": {
|
||||
"C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
|
||||
"projectName": "MinimalAPI",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
|
||||
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
|
||||
"outputPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"fallbackFolders": [
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net9.0"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {},
|
||||
"https://nuget.cdn.azure.cn/v3/index.json": {},
|
||||
"https://packages.chinacloudapi.cn/v3/index.json": {},
|
||||
"https://packages.microsoft.com/dotnet": {},
|
||||
"https://www.nuget.org/api/v2/": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
},
|
||||
"restoreAuditProperties": {
|
||||
"enableAudit": "true",
|
||||
"auditLevel": "low",
|
||||
"auditMode": "direct"
|
||||
},
|
||||
"SdkAnalysisLevel": "9.0.300"
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.ResponseCompression": {
|
||||
"target": "Package",
|
||||
"version": "[2.2.0, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.AspNetCore.App": {
|
||||
"privateAssets": "none"
|
||||
},
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\代\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\代\.nuget\packages\" />
|
||||
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
||||
@@ -1,102 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"targets": {
|
||||
"net9.0": {}
|
||||
},
|
||||
"libraries": {},
|
||||
"projectFileDependencyGroups": {
|
||||
"net9.0": [
|
||||
"Microsoft.AspNetCore.ResponseCompression >= 2.2.0"
|
||||
]
|
||||
},
|
||||
"packageFolders": {
|
||||
"C:\\Users\\代\\.nuget\\packages\\": {},
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
|
||||
},
|
||||
"project": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
|
||||
"projectName": "MinimalAPI",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
|
||||
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
|
||||
"outputPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"fallbackFolders": [
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net9.0"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {},
|
||||
"https://nuget.cdn.azure.cn/v3/index.json": {},
|
||||
"https://packages.chinacloudapi.cn/v3/index.json": {},
|
||||
"https://packages.microsoft.com/dotnet": {},
|
||||
"https://www.nuget.org/api/v2/": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
},
|
||||
"restoreAuditProperties": {
|
||||
"enableAudit": "true",
|
||||
"auditLevel": "low",
|
||||
"auditMode": "direct"
|
||||
},
|
||||
"SdkAnalysisLevel": "9.0.300"
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.ResponseCompression": {
|
||||
"target": "Package",
|
||||
"version": "[2.2.0, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.AspNetCore.App": {
|
||||
"privateAssets": "none"
|
||||
},
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"logs": [
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.AspNetCore.ResponseCompression'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.ResponseCompression”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"libraryId": "Microsoft.AspNetCore.ResponseCompression"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "ionCG+ayp8g=",
|
||||
"success": false,
|
||||
"projectFilePath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
|
||||
"expectedPackageFiles": [],
|
||||
"logs": [
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.AspNetCore.ResponseCompression'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.ResponseCompression”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
|
||||
"filePath": "C:\\work\\电脑硬件-01\\MinimalAPI\\MinimalAPI.csproj",
|
||||
"libraryId": "Microsoft.AspNetCore.ResponseCompression",
|
||||
"targetGraphs": []
|
||||
}
|
||||
]
|
||||
}
|
||||
262
README.md
Normal file
262
README.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# 硬件性能对比平台
|
||||
|
||||
一个专业的硬件性能数据对比平台,提供CPU、GPU等硬件的性能排名、详细参数对比和性能趋势分析。
|
||||
|
||||
## 项目概述
|
||||
|
||||
本平台旨在为用户提供全面、准确的硬件性能数据,帮助用户在选择硬件时做出明智决策。平台集成了多个权威数据源,提供实时更新的性能数据和排名信息。
|
||||
|
||||
### 主要功能
|
||||
|
||||
- 🏆 **硬件性能排名**:提供各类硬件的性能排名和评分
|
||||
- 📊 **详细参数对比**:支持2-4个产品的详细参数对比
|
||||
- 🔍 **智能搜索筛选**:按品牌、性能分数、发布年份等条件筛选
|
||||
- 📈 **性能趋势分析**:展示硬件性能的历史变化趋势
|
||||
- 📱 **响应式设计**:完美适配桌面端和移动端
|
||||
- 🔒 **数据安全保障**:采用多种安全措施保护用户数据
|
||||
|
||||
## 技术架构
|
||||
|
||||
### 后端技术栈
|
||||
|
||||
- **.NET 9.0**:最新的.NET平台,提供高性能和丰富的功能
|
||||
- **Entity Framework Core**:ORM框架,用于数据库操作
|
||||
- **MySQL**:关系型数据库,存储产品数据和性能指标
|
||||
- **Hangfire**:后台任务调度框架,用于定时数据采集
|
||||
- **AutoMapper**:对象映射框架,简化DTO转换
|
||||
- **Serilog**:结构化日志框架
|
||||
- **FluentValidation**:输入验证框架
|
||||
|
||||
### 前端技术栈
|
||||
|
||||
- **Vue 3**:渐进式JavaScript框架
|
||||
- **Vite**:现代化的前端构建工具
|
||||
- **Vue Router**:Vue.js官方路由管理器
|
||||
- **Pinia**:Vue状态管理库
|
||||
- **Element Plus**:基于Vue 3的桌面端UI组件库
|
||||
- **Vant**:移动端UI组件库
|
||||
- **ECharts**:数据可视化图表库
|
||||
- **Axios**:HTTP客户端,用于API请求
|
||||
- **TailwindCSS**:实用优先的CSS框架
|
||||
|
||||
### 测试与质量保证
|
||||
|
||||
- **Vitest**:基于Vite的单元测试框架
|
||||
- **Playwright**:端到端测试框架
|
||||
- **ESLint**:代码质量检查工具
|
||||
- **Prettier**:代码格式化工具
|
||||
- **TypeScript**:JavaScript的超集,提供静态类型检查
|
||||
- **Husky**:Git hooks管理工具
|
||||
|
||||
### 部署与运维
|
||||
|
||||
- **Docker**:容器化部署
|
||||
- **GitHub Actions**:持续集成和持续部署
|
||||
- **Nginx**:反向代理和静态文件服务
|
||||
- **Redis**:缓存和会话存储
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
hardware-performance-platform/
|
||||
├── backend/ # 后端项目
|
||||
│ ├── src/
|
||||
│ │ ├── API/ # API控制器
|
||||
│ │ ├── Application/ # 应用服务层
|
||||
│ │ ├── Core/ # 核心业务逻辑
|
||||
│ │ └── Infrastructure/ # 基础设施层
|
||||
│ ├── tests/ # 后端测试
|
||||
│ └── ... # 其他后端文件
|
||||
├── frontend/ # 前端项目
|
||||
│ ├── src/
|
||||
│ │ ├── components/ # Vue组件
|
||||
│ │ ├── views/ # 页面视图
|
||||
│ │ ├── stores/ # Pinia状态管理
|
||||
│ │ ├── services/ # API服务
|
||||
│ │ ├── utils/ # 工具函数
|
||||
│ │ └── assets/ # 静态资源
|
||||
│ ├── tests/ # 前端测试
|
||||
│ ├── docs/ # 前端文档
|
||||
│ └── ... # 其他前端文件
|
||||
├── docs/ # 项目文档
|
||||
├── docker-compose.yml # Docker编排文件
|
||||
└── README.md # 项目说明文档
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 环境要求
|
||||
|
||||
- Node.js 18.x 或更高版本
|
||||
- .NET 9.0 SDK
|
||||
- MySQL 8.0 或更高版本
|
||||
- Redis 6.0 或更高版本(可选,用于缓存)
|
||||
- Docker 和 Docker Compose(推荐)
|
||||
|
||||
### 使用Docker Compose部署(推荐)
|
||||
|
||||
1. 克隆项目
|
||||
```bash
|
||||
git clone https://github.com/your-username/hardware-performance-platform.git
|
||||
cd hardware-performance-platform
|
||||
```
|
||||
|
||||
2. 启动所有服务
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
3. 访问应用
|
||||
- 前端应用:http://localhost:3000
|
||||
- 后端API:http://localhost:7001
|
||||
- Hangfire Dashboard:http://localhost:7001/hangfire
|
||||
|
||||
### 本地开发环境设置
|
||||
|
||||
#### 后端设置
|
||||
|
||||
1. 进入后端目录
|
||||
```bash
|
||||
cd backend
|
||||
```
|
||||
|
||||
2. 还原NuGet包
|
||||
```bash
|
||||
dotnet restore
|
||||
```
|
||||
|
||||
3. 配置数据库连接
|
||||
```bash
|
||||
cp appsettings.json appsettings.Development.json
|
||||
# 编辑appsettings.Development.json,配置数据库连接字符串
|
||||
```
|
||||
|
||||
4. 运行数据库迁移
|
||||
```bash
|
||||
dotnet ef database update
|
||||
```
|
||||
|
||||
5. 启动后端服务
|
||||
```bash
|
||||
dotnet run
|
||||
```
|
||||
|
||||
#### 前端设置
|
||||
|
||||
1. 进入前端目录
|
||||
```bash
|
||||
cd frontend
|
||||
```
|
||||
|
||||
2. 安装依赖
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
3. 配置环境变量
|
||||
```bash
|
||||
cp .env.example .env.local
|
||||
# 编辑.env.local,配置API地址等环境变量
|
||||
```
|
||||
|
||||
4. 启动前端服务
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## 数据来源
|
||||
|
||||
平台从以下权威数据源采集硬件性能数据:
|
||||
|
||||
- **GeekBench**:CPU和GPU性能基准测试
|
||||
- **3DMark**:图形性能测试
|
||||
- **AnTuTu**:移动设备综合性能测试
|
||||
- **GFXBench**:图形性能测试
|
||||
|
||||
数据采集任务每天自动运行,确保数据的时效性和准确性。
|
||||
|
||||
## API文档
|
||||
|
||||
后端API使用Swagger/OpenAPI生成文档,启动后端服务后可访问:
|
||||
|
||||
- Swagger UI:http://localhost:7001/swagger
|
||||
- OpenAPI规范:http://localhost:7001/swagger/v1/swagger.json
|
||||
|
||||
## 测试
|
||||
|
||||
### 运行后端测试
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
dotnet test
|
||||
```
|
||||
|
||||
### 运行前端测试
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
|
||||
# 运行所有测试
|
||||
npm test
|
||||
|
||||
# 运行单元测试
|
||||
npm run test:unit
|
||||
|
||||
# 运行E2E测试
|
||||
npm run test:e2e
|
||||
|
||||
# 生成测试覆盖率报告
|
||||
npm run test:unit:coverage
|
||||
```
|
||||
|
||||
### 代码质量检查
|
||||
|
||||
```bash
|
||||
cd frontend
|
||||
|
||||
# 运行ESLint检查
|
||||
npm run lint
|
||||
|
||||
# 格式化代码
|
||||
npm run format
|
||||
|
||||
# 生成代码质量报告
|
||||
npm run quality-report
|
||||
```
|
||||
|
||||
## 贡献指南
|
||||
|
||||
我们欢迎任何形式的贡献,包括但不限于:
|
||||
|
||||
- 🐛 报告Bug
|
||||
- 💡 提出新功能建议
|
||||
- 📝 改进文档
|
||||
- 🔧 提交代码修复
|
||||
|
||||
请阅读 [CONTRIBUTING.md](CONTRIBUTING.md) 了解详细的贡献流程。
|
||||
|
||||
## 许可证
|
||||
|
||||
本项目采用 [MIT 许可证](LICENSE)。
|
||||
|
||||
## 联系我们
|
||||
|
||||
- 项目主页:https://github.com/your-username/hardware-performance-platform
|
||||
- 问题反馈:https://github.com/your-username/hardware-performance-platform/issues
|
||||
- 邮箱:contact@hardware-performance.com
|
||||
|
||||
## 致谢
|
||||
|
||||
感谢以下开源项目和数据提供商:
|
||||
|
||||
- [Vue.js](https://vuejs.org/)
|
||||
- [.NET](https://dotnet.microsoft.com/)
|
||||
- [Element Plus](https://element-plus.org/)
|
||||
- [ECharts](https://echarts.apache.org/)
|
||||
- [GeekBench](https://www.geekbench.com/)
|
||||
- [3DMark](https://www.3dmark.com/)
|
||||
- [AnTuTu](https://www.antutu.com/)
|
||||
|
||||
---
|
||||
|
||||
⭐ 如果这个项目对你有帮助,请给我们一个星标!
|
||||
@@ -1,41 +0,0 @@
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add services to the container.
|
||||
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
|
||||
builder.Services.AddOpenApi();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
app.MapOpenApi();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
var summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
app.MapGet("/weatherforecast", () =>
|
||||
{
|
||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray();
|
||||
return forecast;
|
||||
})
|
||||
.WithName("GetWeatherForecast");
|
||||
|
||||
app.Run();
|
||||
|
||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "http://localhost:5256",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "https://localhost:7149;http://localhost:5256",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.8" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,6 +0,0 @@
|
||||
@SimpleAPI_HostAddress = http://localhost:5256
|
||||
|
||||
GET {{SimpleAPI_HostAddress}}/weatherforecast/
|
||||
Accept: application/json
|
||||
|
||||
###
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
{
|
||||
"format": 1,
|
||||
"restore": {
|
||||
"C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj": {}
|
||||
},
|
||||
"projects": {
|
||||
"C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
|
||||
"projectName": "SimpleAPI",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
|
||||
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
|
||||
"outputPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"fallbackFolders": [
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net9.0"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {},
|
||||
"https://nuget.cdn.azure.cn/v3/index.json": {},
|
||||
"https://packages.chinacloudapi.cn/v3/index.json": {},
|
||||
"https://packages.microsoft.com/dotnet": {},
|
||||
"https://www.nuget.org/api/v2/": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
},
|
||||
"restoreAuditProperties": {
|
||||
"enableAudit": "true",
|
||||
"auditLevel": "low",
|
||||
"auditMode": "direct"
|
||||
},
|
||||
"SdkAnalysisLevel": "9.0.300"
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.OpenApi": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.AspNetCore.App": {
|
||||
"privateAssets": "none"
|
||||
},
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
|
||||
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\代\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
|
||||
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.14.0</NuGetToolVersion>
|
||||
</PropertyGroup>
|
||||
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||
<SourceRoot Include="C:\Users\代\.nuget\packages\" />
|
||||
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,2 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
||||
@@ -1,108 +0,0 @@
|
||||
{
|
||||
"version": 3,
|
||||
"targets": {
|
||||
"net9.0": {}
|
||||
},
|
||||
"libraries": {},
|
||||
"projectFileDependencyGroups": {
|
||||
"net9.0": [
|
||||
"Microsoft.AspNetCore.OpenApi >= 9.0.8"
|
||||
]
|
||||
},
|
||||
"packageFolders": {
|
||||
"C:\\Users\\代\\.nuget\\packages\\": {},
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}
|
||||
},
|
||||
"project": {
|
||||
"version": "1.0.0",
|
||||
"restore": {
|
||||
"projectUniqueName": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
|
||||
"projectName": "SimpleAPI",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
|
||||
"packagesPath": "C:\\Users\\代\\.nuget\\packages\\",
|
||||
"outputPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\obj\\",
|
||||
"projectStyle": "PackageReference",
|
||||
"fallbackFolders": [
|
||||
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
|
||||
],
|
||||
"configFilePaths": [
|
||||
"C:\\Users\\代\\AppData\\Roaming\\NuGet\\NuGet.Config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
|
||||
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
|
||||
],
|
||||
"originalTargetFrameworks": [
|
||||
"net9.0"
|
||||
],
|
||||
"sources": {
|
||||
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
|
||||
"https://api.nuget.org/v3/index.json": {},
|
||||
"https://nuget.cdn.azure.cn/v3/index.json": {},
|
||||
"https://packages.chinacloudapi.cn/v3/index.json": {},
|
||||
"https://packages.microsoft.com/dotnet": {},
|
||||
"https://www.nuget.org/api/v2/": {}
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"projectReferences": {}
|
||||
}
|
||||
},
|
||||
"warningProperties": {
|
||||
"warnAsError": [
|
||||
"NU1605"
|
||||
]
|
||||
},
|
||||
"restoreAuditProperties": {
|
||||
"enableAudit": "true",
|
||||
"auditLevel": "low",
|
||||
"auditMode": "direct"
|
||||
},
|
||||
"SdkAnalysisLevel": "9.0.300"
|
||||
},
|
||||
"frameworks": {
|
||||
"net9.0": {
|
||||
"targetAlias": "net9.0",
|
||||
"dependencies": {
|
||||
"Microsoft.AspNetCore.OpenApi": {
|
||||
"target": "Package",
|
||||
"version": "[9.0.8, )"
|
||||
}
|
||||
},
|
||||
"imports": [
|
||||
"net461",
|
||||
"net462",
|
||||
"net47",
|
||||
"net471",
|
||||
"net472",
|
||||
"net48",
|
||||
"net481"
|
||||
],
|
||||
"assetTargetFallback": true,
|
||||
"warn": true,
|
||||
"frameworkReferences": {
|
||||
"Microsoft.AspNetCore.App": {
|
||||
"privateAssets": "none"
|
||||
},
|
||||
"Microsoft.NETCore.App": {
|
||||
"privateAssets": "all"
|
||||
}
|
||||
},
|
||||
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.304/PortableRuntimeIdentifierGraph.json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"logs": [
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.AspNetCore.OpenApi'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.OpenApi”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"libraryId": "Microsoft.AspNetCore.OpenApi"
|
||||
},
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://packages.microsoft.com/dotnet/FindPackagesById()?id='Microsoft.AspNetCore.OpenApi'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.OpenApi”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"libraryId": "Microsoft.AspNetCore.OpenApi"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"version": 2,
|
||||
"dgSpecHash": "qHQETFOfcmo=",
|
||||
"success": false,
|
||||
"projectFilePath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
|
||||
"expectedPackageFiles": [],
|
||||
"logs": [
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://www.nuget.org/api/v2/FindPackagesById()?id='Microsoft.AspNetCore.OpenApi'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.OpenApi”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
|
||||
"filePath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
|
||||
"libraryId": "Microsoft.AspNetCore.OpenApi",
|
||||
"targetGraphs": []
|
||||
},
|
||||
{
|
||||
"code": "NU1301",
|
||||
"level": "Error",
|
||||
"message": "未能从远程源“https://packages.microsoft.com/dotnet/FindPackagesById()?id='Microsoft.AspNetCore.OpenApi'&semVerLevel=2.0.0”检索有关“Microsoft.AspNetCore.OpenApi”的信息。\r\n 不知道这样的主机。 (null:80)\r\n 不知道这样的主机。",
|
||||
"projectPath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
|
||||
"filePath": "C:\\work\\电脑硬件-01\\SimpleAPI\\SimpleAPI.csproj",
|
||||
"libraryId": "Microsoft.AspNetCore.OpenApi",
|
||||
"targetGraphs": []
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,471 +0,0 @@
|
||||
using Microsoft.AspNetCore.ResponseCompression;
|
||||
using System.IO.Compression;
|
||||
using System.Text.Json;
|
||||
using System.Linq;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// 添加响应压缩服务
|
||||
builder.Services.AddResponseCompression(options =>
|
||||
{
|
||||
options.EnableForHttps = true;
|
||||
options.Providers.Add<BrotliCompressionProvider>();
|
||||
options.Providers.Add<GzipCompressionProvider>();
|
||||
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[] { "application/json" });
|
||||
});
|
||||
|
||||
// 配置压缩级别
|
||||
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
|
||||
{
|
||||
options.Level = CompressionLevel.Fastest;
|
||||
});
|
||||
|
||||
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
|
||||
{
|
||||
options.Level = CompressionLevel.Fastest;
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// 使用响应压缩中间件
|
||||
app.UseResponseCompression();
|
||||
|
||||
// 添加一个返回大量数据的API端点,以便测试压缩效果
|
||||
app.MapGet("/api/data", () => new
|
||||
{
|
||||
Message = "这是一个用于测试响应压缩的API端点",
|
||||
Data = Enumerable.Range(1, 1000).Select(i => new { Id = i, Name = $"Item {i}", Description = $"这是第{i}个项目的描述,包含更多文本内容以便测试压缩效果。" }).ToArray(),
|
||||
Timestamp = DateTime.UtcNow
|
||||
});
|
||||
|
||||
// 添加类别API端点,供前端测试使用
|
||||
app.MapGet("/api/categories", () => new[]
|
||||
{
|
||||
new { Id = 1, Name = "手机CPU", Description = "移动设备处理器性能排名", ProductCount = 25 },
|
||||
new { Id = 2, Name = "手机GPU", Description = "移动设备图形处理器性能排名", ProductCount = 18 },
|
||||
new { Id = 3, Name = "电脑CPU", Description = "桌面处理器性能排名", ProductCount = 32 },
|
||||
new { Id = 4, Name = "电脑GPU", Description = "桌面显卡性能排名", ProductCount = 28 }
|
||||
});
|
||||
|
||||
// 添加产品API端点,供前端测试使用
|
||||
app.MapGet("/api/products", (int? categoryId, int page = 1, int pageSize = 20, string sortBy = "PerformanceScore", string order = "desc") =>
|
||||
{
|
||||
// 模拟产品数据
|
||||
var allProducts = new[]
|
||||
{
|
||||
new { Id = 1, Name = "Snapdragon 8 Gen 2", Model = "SM8550-AB", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 3879, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 2, Name = "A16 Bionic", Model = "A16 Bionic", Manufacturer = "Apple", ReleaseYear = 2022, PerformanceScore = 3756, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 3, Name = "Dimensity 9200", Model = "MT6985", Manufacturer = "MediaTek", ReleaseYear = 2022, PerformanceScore = 3512, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 4, Name = "Snapdragon 8+ Gen 1", Model = "SM8475", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 3341, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 5, Name = "A15 Bionic", Model = "A15 Bionic", Manufacturer = "Apple", ReleaseYear = 2021, PerformanceScore = 3215, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 6, Name = "Adreno 740", Model = "Adreno 740", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 14350, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 7, Name = "Apple A16 GPU", Model = "A16 GPU", Manufacturer = "Apple", ReleaseYear = 2022, PerformanceScore = 13980, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 8, Name = "Mali-G715 MC10", Model = "Mali-G715", Manufacturer = "ARM", ReleaseYear = 2022, PerformanceScore = 12150, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 9, Name = "Adreno 730", Model = "Adreno 730", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 11020, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 10, Name = "Apple A15 GPU", Model = "A15 GPU", Manufacturer = "Apple", ReleaseYear = 2021, PerformanceScore = 10560, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 11, Name = "Intel Core i9-13900K", Model = "i9-13900K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 3176, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 12, Name = "AMD Ryzen 9 7950X", Model = "Ryzen 9 7950X", Manufacturer = "AMD", ReleaseYear = 2022, PerformanceScore = 3095, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 13, Name = "Intel Core i7-13700K", Model = "i7-13700K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 2956, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 14, Name = "AMD Ryzen 9 5900X", Model = "Ryzen 9 5900X", Manufacturer = "AMD", ReleaseYear = 2020, PerformanceScore = 2835, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 15, Name = "Intel Core i5-13600K", Model = "i5-13600K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 2742, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 16, Name = "NVIDIA GeForce RTX 4090", Model = "RTX 4090", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 38928, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
|
||||
new { Id = 17, Name = "NVIDIA GeForce RTX 4080", Model = "RTX 4080", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 32168, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
|
||||
new { Id = 18, Name = "NVIDIA GeForce RTX 3090 Ti", Model = "RTX 3090 Ti", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 28595, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
|
||||
new { Id = 19, Name = "AMD Radeon RX 7900 XTX", Model = "RX 7900 XTX", Manufacturer = "AMD", ReleaseYear = 2022, PerformanceScore = 27850, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
|
||||
new { Id = 20, Name = "NVIDIA GeForce RTX 3090", Model = "RTX 3090", Manufacturer = "NVIDIA", ReleaseYear = 2020, PerformanceScore = 26420, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 4 }
|
||||
};
|
||||
|
||||
// 根据类别ID筛选
|
||||
var filteredProducts = categoryId.HasValue
|
||||
? allProducts.Where(p => p.CategoryId == categoryId.Value).ToArray()
|
||||
: allProducts;
|
||||
|
||||
// 排序
|
||||
filteredProducts = sortBy.ToLower() switch
|
||||
{
|
||||
"name" => order.ToLower() == "desc"
|
||||
? filteredProducts.OrderByDescending(p => p.Name).ToArray()
|
||||
: filteredProducts.OrderBy(p => p.Name).ToArray(),
|
||||
"manufacturer" => order.ToLower() == "desc"
|
||||
? filteredProducts.OrderByDescending(p => p.Manufacturer).ToArray()
|
||||
: filteredProducts.OrderBy(p => p.Manufacturer).ToArray(),
|
||||
"releaseyear" => order.ToLower() == "desc"
|
||||
? filteredProducts.OrderByDescending(p => p.ReleaseYear).ToArray()
|
||||
: filteredProducts.OrderBy(p => p.ReleaseYear).ToArray(),
|
||||
_ => order.ToLower() == "desc"
|
||||
? filteredProducts.OrderByDescending(p => p.PerformanceScore).ToArray()
|
||||
: filteredProducts.OrderBy(p => p.PerformanceScore).ToArray()
|
||||
};
|
||||
|
||||
var totalItems = filteredProducts.Length;
|
||||
var totalPages = (int)Math.Ceiling((double)totalItems / pageSize);
|
||||
|
||||
// 分页
|
||||
var pagedProducts = filteredProducts
|
||||
.Skip((page - 1) * pageSize)
|
||||
.Take(pageSize)
|
||||
.ToArray();
|
||||
|
||||
return new {
|
||||
Items = pagedProducts,
|
||||
Total = totalItems,
|
||||
CurrentPage = page,
|
||||
PageSize = pageSize,
|
||||
TotalPages = totalPages
|
||||
};
|
||||
});
|
||||
|
||||
// 添加产品详情API端点
|
||||
app.MapGet("/api/products/{id}", (int id) =>
|
||||
{
|
||||
// 模拟产品详情数据
|
||||
var allProducts = new[]
|
||||
{
|
||||
new {
|
||||
Id = 1,
|
||||
Name = "Snapdragon 8 Gen 2",
|
||||
Model = "SM8550-AB",
|
||||
Manufacturer = "Qualcomm",
|
||||
ReleaseYear = 2022,
|
||||
PerformanceScore = 3879,
|
||||
CurrentRank = 1,
|
||||
ImageUrl = "/images/placeholder.svg",
|
||||
CategoryId = 1,
|
||||
Description = "高通骁龙8 Gen 2是2022年推出的旗舰移动处理器,采用4nm工艺制程。",
|
||||
Specifications = new[] {
|
||||
new { Name = "工艺", Value = "4nm" },
|
||||
new { Name = "核心数", Value = "8" },
|
||||
new { Name = "主频", Value = "3.2GHz" },
|
||||
new { Name = "GPU", Value = "Adreno 740" }
|
||||
},
|
||||
PerformanceScores = new[] {
|
||||
new { Benchmark = "Geekbench 5 Single-Core", Score = 1524 },
|
||||
new { Benchmark = "Geekbench 5 Multi-Core", Score = 5352 },
|
||||
new { Benchmark = "AnTuTu 9", Score = 1314582 }
|
||||
}
|
||||
},
|
||||
new {
|
||||
Id = 2,
|
||||
Name = "A16 Bionic",
|
||||
Model = "A16 Bionic",
|
||||
Manufacturer = "Apple",
|
||||
ReleaseYear = 2022,
|
||||
PerformanceScore = 3756,
|
||||
CurrentRank = 2,
|
||||
ImageUrl = "/images/placeholder.svg",
|
||||
CategoryId = 1,
|
||||
Description = "苹果A16 Bionic是2022年推出的移动处理器,采用4nm工艺制程。",
|
||||
Specifications = new[] {
|
||||
new { Name = "工艺", Value = "4nm" },
|
||||
new { Name = "核心数", Value = "6" },
|
||||
new { Name = "主频", Value = "3.46GHz" },
|
||||
new { Name = "GPU", Value = "Apple A16 GPU" }
|
||||
},
|
||||
PerformanceScores = new[] {
|
||||
new { Benchmark = "Geekbench 5 Single-Core", Score = 1874 },
|
||||
new { Benchmark = "Geekbench 5 Multi-Core", Score = 5372 },
|
||||
new { Benchmark = "AnTuTu 9", Score = 1425876 }
|
||||
}
|
||||
},
|
||||
new {
|
||||
Id = 3,
|
||||
Name = "Dimensity 9200",
|
||||
Model = "MT6985",
|
||||
Manufacturer = "MediaTek",
|
||||
ReleaseYear = 2022,
|
||||
PerformanceScore = 3512,
|
||||
CurrentRank = 3,
|
||||
ImageUrl = "/images/placeholder.svg",
|
||||
CategoryId = 1,
|
||||
Description = "联发科天玑9200是2022年推出的旗舰移动处理器,采用4nm工艺制程。",
|
||||
Specifications = new[] {
|
||||
new { Name = "工艺", Value = "4nm" },
|
||||
new { Name = "核心数", Value = "8" },
|
||||
new { Name = "主频", Value = "3.05GHz" },
|
||||
new { Name = "GPU", Value = "Mali-G715 MC10" }
|
||||
},
|
||||
PerformanceScores = new[] {
|
||||
new { Benchmark = "Geekbench 5 Single-Core", Score = 1424 },
|
||||
new { Benchmark = "Geekbench 5 Multi-Core", Score = 4485 },
|
||||
new { Benchmark = "AnTuTu 9", Score = 1245876 }
|
||||
}
|
||||
},
|
||||
new {
|
||||
Id = 4,
|
||||
Name = "Snapdragon 8+ Gen 1",
|
||||
Model = "SM8475",
|
||||
Manufacturer = "Qualcomm",
|
||||
ReleaseYear = 2022,
|
||||
PerformanceScore = 3341,
|
||||
CurrentRank = 4,
|
||||
ImageUrl = "/images/placeholder.svg",
|
||||
CategoryId = 1,
|
||||
Description = "高通骁龙8+ Gen 1是2022年推出的旗舰移动处理器,采用4nm工艺制程。",
|
||||
Specifications = new[] {
|
||||
new { Name = "工艺", Value = "4nm" },
|
||||
new { Name = "核心数", Value = "8" },
|
||||
new { Name = "主频", Value = "3.2GHz" },
|
||||
new { Name = "GPU", Value = "Adreno 730" }
|
||||
},
|
||||
PerformanceScores = new[] {
|
||||
new { Benchmark = "Geekbench 5 Single-Core", Score = 1314 },
|
||||
new { Benchmark = "Geekbench 5 Multi-Core", Score = 4185 },
|
||||
new { Benchmark = "AnTuTu 9", Score = 1145876 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var product = allProducts.FirstOrDefault(p => p.Id == id);
|
||||
if (product == null)
|
||||
{
|
||||
return Results.NotFound(new { Message = "产品不存在" });
|
||||
}
|
||||
|
||||
return Results.Ok(product);
|
||||
});
|
||||
|
||||
// 添加产品搜索API端点
|
||||
app.MapGet("/api/products/search", (string q, int? categoryId) =>
|
||||
{
|
||||
// 模拟产品数据
|
||||
var allProducts = new[]
|
||||
{
|
||||
new { Id = 1, Name = "Snapdragon 8 Gen 2", Model = "SM8550-AB", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 3879, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 2, Name = "A16 Bionic", Model = "A16 Bionic", Manufacturer = "Apple", ReleaseYear = 2022, PerformanceScore = 3756, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 3, Name = "Dimensity 9200", Model = "MT6985", Manufacturer = "MediaTek", ReleaseYear = 2022, PerformanceScore = 3512, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 4, Name = "Snapdragon 8+ Gen 1", Model = "SM8475", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 3341, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 5, Name = "A15 Bionic", Model = "A15 Bionic", Manufacturer = "Apple", ReleaseYear = 2021, PerformanceScore = 3215, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 1 },
|
||||
new { Id = 6, Name = "Adreno 740", Model = "Adreno 740", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 14350, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 7, Name = "Apple A16 GPU", Model = "A16 GPU", Manufacturer = "Apple", ReleaseYear = 2022, PerformanceScore = 13980, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 8, Name = "Mali-G715 MC10", Model = "Mali-G715", Manufacturer = "ARM", ReleaseYear = 2022, PerformanceScore = 12150, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 9, Name = "Adreno 730", Model = "Adreno 730", Manufacturer = "Qualcomm", ReleaseYear = 2022, PerformanceScore = 11020, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 10, Name = "Apple A15 GPU", Model = "A15 GPU", Manufacturer = "Apple", ReleaseYear = 2021, PerformanceScore = 10560, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 2 },
|
||||
new { Id = 11, Name = "Intel Core i9-13900K", Model = "i9-13900K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 3176, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 12, Name = "AMD Ryzen 9 7950X", Model = "Ryzen 9 7950X", Manufacturer = "AMD", ReleaseYear = 2022, PerformanceScore = 3095, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 13, Name = "Intel Core i7-13700K", Model = "i7-13700K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 2956, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 14, Name = "AMD Ryzen 9 5900X", Model = "Ryzen 9 5900X", Manufacturer = "AMD", ReleaseYear = 2020, PerformanceScore = 2835, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 15, Name = "Intel Core i5-13600K", Model = "i5-13600K", Manufacturer = "Intel", ReleaseYear = 2022, PerformanceScore = 2742, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 3 },
|
||||
new { Id = 16, Name = "NVIDIA GeForce RTX 4090", Model = "RTX 4090", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 38928, CurrentRank = 1, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
|
||||
new { Id = 17, Name = "NVIDIA GeForce RTX 4080", Model = "RTX 4080", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 32168, CurrentRank = 2, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
|
||||
new { Id = 18, Name = "NVIDIA GeForce RTX 3090 Ti", Model = "RTX 3090 Ti", Manufacturer = "NVIDIA", ReleaseYear = 2022, PerformanceScore = 28595, CurrentRank = 3, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
|
||||
new { Id = 19, Name = "AMD Radeon RX 7900 XTX", Model = "RX 7900 XTX", Manufacturer = "AMD", ReleaseYear = 2022, PerformanceScore = 27850, CurrentRank = 4, ImageUrl = "/images/placeholder.svg", CategoryId = 4 },
|
||||
new { Id = 20, Name = "NVIDIA GeForce RTX 3090", Model = "RTX 3090", Manufacturer = "NVIDIA", ReleaseYear = 2020, PerformanceScore = 26420, CurrentRank = 5, ImageUrl = "/images/placeholder.svg", CategoryId = 4 }
|
||||
};
|
||||
|
||||
// 根据类别ID筛选
|
||||
var filteredProducts = categoryId.HasValue
|
||||
? allProducts.Where(p => p.CategoryId == categoryId.Value).ToArray()
|
||||
: allProducts;
|
||||
|
||||
// 根据搜索关键词筛选
|
||||
if (!string.IsNullOrWhiteSpace(q))
|
||||
{
|
||||
filteredProducts = filteredProducts.Where(p =>
|
||||
p.Name.Contains(q, StringComparison.OrdinalIgnoreCase) ||
|
||||
p.Model.Contains(q, StringComparison.OrdinalIgnoreCase) ||
|
||||
p.Manufacturer.Contains(q, StringComparison.OrdinalIgnoreCase)
|
||||
).ToArray();
|
||||
}
|
||||
|
||||
return filteredProducts;
|
||||
});
|
||||
|
||||
// 添加产品对比API端点
|
||||
app.MapPost("/api/comparison", async (HttpRequest request) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// 读取请求体
|
||||
var requestBody = await new StreamReader(request.Body).ReadToEndAsync();
|
||||
|
||||
// 尝试解析为JSON对象
|
||||
dynamic jsonBody = JsonSerializer.Deserialize<Dictionary<string, object>>(requestBody);
|
||||
|
||||
// 提取productIds数组
|
||||
if (jsonBody == null || !jsonBody.ContainsKey("productIds"))
|
||||
{
|
||||
return Results.BadRequest(new { Message = "请求体必须包含productIds字段" });
|
||||
}
|
||||
|
||||
// 获取productIds数组
|
||||
var productIdsJson = jsonBody["productIds"].ToString();
|
||||
var productIds = JsonSerializer.Deserialize<int[]>(productIdsJson);
|
||||
|
||||
// 验证产品ID数量
|
||||
if (productIds == null || productIds.Length < 2 || productIds.Length > 4)
|
||||
{
|
||||
return Results.BadRequest(new { Message = "产品对比需要2-4个产品ID" });
|
||||
}
|
||||
|
||||
// 模拟产品详情数据
|
||||
var allProducts = new[]
|
||||
{
|
||||
new {
|
||||
Id = 1,
|
||||
Name = "Snapdragon 8 Gen 2",
|
||||
Model = "SM8550-AB",
|
||||
Manufacturer = "Qualcomm",
|
||||
ReleaseYear = 2022,
|
||||
PerformanceScore = 3879,
|
||||
CurrentRank = 1,
|
||||
ImageUrl = "/images/placeholder.svg",
|
||||
CategoryId = 1,
|
||||
Description = "高通骁龙8 Gen 2是2022年推出的旗舰移动处理器,采用4nm工艺制程。",
|
||||
Specifications = new[] {
|
||||
new { Name = "工艺", Value = "4nm" },
|
||||
new { Name = "核心数", Value = "8" },
|
||||
new { Name = "主频", Value = "3.2GHz" },
|
||||
new { Name = "GPU", Value = "Adreno 740" }
|
||||
},
|
||||
PerformanceScores = new[] {
|
||||
new { Benchmark = "Geekbench 5 Single-Core", Score = 1524 },
|
||||
new { Benchmark = "Geekbench 5 Multi-Core", Score = 5352 },
|
||||
new { Benchmark = "AnTuTu 9", Score = 1314582 }
|
||||
}
|
||||
},
|
||||
new {
|
||||
Id = 2,
|
||||
Name = "A16 Bionic",
|
||||
Model = "A16 Bionic",
|
||||
Manufacturer = "Apple",
|
||||
ReleaseYear = 2022,
|
||||
PerformanceScore = 3756,
|
||||
CurrentRank = 2,
|
||||
ImageUrl = "/images/placeholder.svg",
|
||||
CategoryId = 1,
|
||||
Description = "苹果A16 Bionic是2022年推出的移动处理器,采用4nm工艺制程。",
|
||||
Specifications = new[] {
|
||||
new { Name = "工艺", Value = "4nm" },
|
||||
new { Name = "核心数", Value = "6" },
|
||||
new { Name = "主频", Value = "3.46GHz" },
|
||||
new { Name = "GPU", Value = "Apple A16 GPU" }
|
||||
},
|
||||
PerformanceScores = new[] {
|
||||
new { Benchmark = "Geekbench 5 Single-Core", Score = 1874 },
|
||||
new { Benchmark = "Geekbench 5 Multi-Core", Score = 5372 },
|
||||
new { Benchmark = "AnTuTu 9", Score = 1425876 }
|
||||
}
|
||||
},
|
||||
new {
|
||||
Id = 3,
|
||||
Name = "Dimensity 9200",
|
||||
Model = "MT6985",
|
||||
Manufacturer = "MediaTek",
|
||||
ReleaseYear = 2022,
|
||||
PerformanceScore = 3512,
|
||||
CurrentRank = 3,
|
||||
ImageUrl = "/images/placeholder.svg",
|
||||
CategoryId = 1,
|
||||
Description = "联发科天玑9200是2022年推出的旗舰移动处理器,采用4nm工艺制程。",
|
||||
Specifications = new[] {
|
||||
new { Name = "工艺", Value = "4nm" },
|
||||
new { Name = "核心数", Value = "8" },
|
||||
new { Name = "主频", Value = "3.05GHz" },
|
||||
new { Name = "GPU", Value = "Mali-G715 MC10" }
|
||||
},
|
||||
PerformanceScores = new[] {
|
||||
new { Benchmark = "Geekbench 5 Single-Core", Score = 1424 },
|
||||
new { Benchmark = "Geekbench 5 Multi-Core", Score = 4485 },
|
||||
new { Benchmark = "AnTuTu 9", Score = 1245876 }
|
||||
}
|
||||
},
|
||||
new {
|
||||
Id = 4,
|
||||
Name = "Snapdragon 8+ Gen 1",
|
||||
Model = "SM8475",
|
||||
Manufacturer = "Qualcomm",
|
||||
ReleaseYear = 2022,
|
||||
PerformanceScore = 3341,
|
||||
CurrentRank = 4,
|
||||
ImageUrl = "/images/placeholder.svg",
|
||||
CategoryId = 1,
|
||||
Description = "高通骁龙8+ Gen 1是2022年推出的旗舰移动处理器,采用4nm工艺制程。",
|
||||
Specifications = new[] {
|
||||
new { Name = "工艺", Value = "4nm" },
|
||||
new { Name = "核心数", Value = "8" },
|
||||
new { Name = "主频", Value = "3.2GHz" },
|
||||
new { Name = "GPU", Value = "Adreno 730" }
|
||||
},
|
||||
PerformanceScores = new[] {
|
||||
new { Benchmark = "Geekbench 5 Single-Core", Score = 1314 },
|
||||
new { Benchmark = "Geekbench 5 Multi-Core", Score = 4185 },
|
||||
new { Benchmark = "AnTuTu 9", Score = 1145876 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 获取请求的产品
|
||||
var selectedProducts = allProducts.Where(p => productIds.Contains(p.Id)).ToArray();
|
||||
|
||||
// 检查是否找到了所有请求的产品
|
||||
if (selectedProducts.Length != productIds.Length)
|
||||
{
|
||||
return Results.NotFound(new { Message = "一个或多个产品不存在" });
|
||||
}
|
||||
|
||||
// 创建对比数据
|
||||
var comparisonData = new {
|
||||
Products = selectedProducts,
|
||||
ComparisonMatrix = new {
|
||||
PerformanceScore = selectedProducts.Select(p => new {
|
||||
ProductId = p.Id,
|
||||
ProductName = p.Name,
|
||||
Value = p.PerformanceScore,
|
||||
IsBest = p.PerformanceScore == selectedProducts.Max(x => x.PerformanceScore),
|
||||
IsWorst = p.PerformanceScore == selectedProducts.Min(x => x.PerformanceScore)
|
||||
}).ToArray(),
|
||||
ReleaseYear = selectedProducts.Select(p => new {
|
||||
ProductId = p.Id,
|
||||
ProductName = p.Name,
|
||||
Value = p.ReleaseYear,
|
||||
IsBest = p.ReleaseYear == selectedProducts.Max(x => x.ReleaseYear),
|
||||
IsWorst = p.ReleaseYear == selectedProducts.Min(x => x.ReleaseYear)
|
||||
}).ToArray()
|
||||
},
|
||||
PerformanceComparison = new {
|
||||
Benchmarks = new[] {
|
||||
new {
|
||||
Name = "Geekbench 5 Single-Core",
|
||||
Values = selectedProducts.Select(p => new {
|
||||
ProductId = p.Id,
|
||||
ProductName = p.Name,
|
||||
Value = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score ?? 0,
|
||||
IsBest = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score == selectedProducts.Max(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score ?? 0),
|
||||
IsWorst = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score == selectedProducts.Min(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Single-Core")?.Score ?? int.MaxValue)
|
||||
}).ToArray()
|
||||
},
|
||||
new {
|
||||
Name = "Geekbench 5 Multi-Core",
|
||||
Values = selectedProducts.Select(p => new {
|
||||
ProductId = p.Id,
|
||||
ProductName = p.Name,
|
||||
Value = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score ?? 0,
|
||||
IsBest = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score == selectedProducts.Max(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score ?? 0),
|
||||
IsWorst = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score == selectedProducts.Min(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "Geekbench 5 Multi-Core")?.Score ?? int.MaxValue)
|
||||
}).ToArray()
|
||||
},
|
||||
new {
|
||||
Name = "AnTuTu 9",
|
||||
Values = selectedProducts.Select(p => new {
|
||||
ProductId = p.Id,
|
||||
ProductName = p.Name,
|
||||
Value = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score ?? 0,
|
||||
IsBest = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score == selectedProducts.Max(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score ?? 0),
|
||||
IsWorst = p.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score == selectedProducts.Min(x => x.PerformanceScores.FirstOrDefault(s => s.Benchmark == "AnTuTu 9")?.Score ?? int.MaxValue)
|
||||
}).ToArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Results.Ok(comparisonData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Results.BadRequest(new { Message = $"处理请求时出错: {ex.Message}" });
|
||||
}
|
||||
});
|
||||
|
||||
app.MapGet("/", () => "Hello World!");
|
||||
|
||||
app.Run();
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5034",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7100;http://localhost:5034",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v9.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v9.0": {
|
||||
"TestAPI/1.0.0": {
|
||||
"runtime": {
|
||||
"TestAPI.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"TestAPI/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net9.0",
|
||||
"frameworks": [
|
||||
{
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "9.0.0"
|
||||
},
|
||||
{
|
||||
"name": "Microsoft.AspNetCore.App",
|
||||
"version": "9.0.0"
|
||||
}
|
||||
],
|
||||
"configProperties": {
|
||||
"System.GC.Server": true,
|
||||
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
{"Version":1,"ManifestType":"Build","Endpoints":[]}
|
||||
@@ -1,8 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
ccdd8558c5bd69980236f6ef7435019aea79860a1fdbecb265564d3b99885b5a
|
||||
@@ -1,21 +0,0 @@
|
||||
is_global = true
|
||||
build_property.TargetFramework = net9.0
|
||||
build_property.TargetPlatformMinVersion =
|
||||
build_property.UsingMicrosoftNETSdkWeb = true
|
||||
build_property.ProjectTypeGuids =
|
||||
build_property.InvariantGlobalization =
|
||||
build_property.PlatformNeutralAssembly =
|
||||
build_property.EnforceExtendedAnalyzerRules =
|
||||
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||
build_property.RootNamespace = TestAPI
|
||||
build_property.RootNamespace = TestAPI
|
||||
build_property.ProjectDir = C:\work\电脑硬件-01\TestAPI\
|
||||
build_property.EnableComHosting =
|
||||
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||
build_property.RazorLangVersion = 9.0
|
||||
build_property.SupportLocalizedComponentNames =
|
||||
build_property.GenerateRazorMetadataSourceChecksumAttributes =
|
||||
build_property.MSBuildProjectDirectory = C:\work\电脑硬件-01\TestAPI
|
||||
build_property._RazorSourceGeneratorDebug =
|
||||
build_property.EffectiveAnalysisLevelStyle = 9.0
|
||||
build_property.EnableCodeStyleSeverity =
|
||||
@@ -1,17 +0,0 @@
|
||||
// <auto-generated/>
|
||||
global using global::Microsoft.AspNetCore.Builder;
|
||||
global using global::Microsoft.AspNetCore.Hosting;
|
||||
global using global::Microsoft.AspNetCore.Http;
|
||||
global using global::Microsoft.AspNetCore.Routing;
|
||||
global using global::Microsoft.Extensions.Configuration;
|
||||
global using global::Microsoft.Extensions.DependencyInjection;
|
||||
global using global::Microsoft.Extensions.Hosting;
|
||||
global using global::Microsoft.Extensions.Logging;
|
||||
global using global::System;
|
||||
global using global::System.Collections.Generic;
|
||||
global using global::System.IO;
|
||||
global using global::System.Linq;
|
||||
global using global::System.Net.Http;
|
||||
global using global::System.Net.Http.Json;
|
||||
global using global::System.Threading;
|
||||
global using global::System.Threading.Tasks;
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
629dc1ba4c6ea1c70ecea85384c28652dcd9f627da004e854aea08234c082e60
|
||||
@@ -1,27 +0,0 @@
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\rpswa.dswa.cache.json
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.GeneratedMSBuildEditorConfig.editorconfig
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.AssemblyInfoInputs.cache
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.AssemblyInfo.cs
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.csproj.CoreCompileInputs.cache
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.MvcApplicationPartsAssemblyInfo.cache
|
||||
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\appsettings.Development.json
|
||||
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\appsettings.json
|
||||
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.staticwebassets.endpoints.json
|
||||
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.exe
|
||||
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.deps.json
|
||||
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.runtimeconfig.json
|
||||
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.dll
|
||||
C:\work\电脑硬件-01\TestAPI\bin\Debug\net9.0\TestAPI.pdb
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\rjimswa.dswa.cache.json
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\rjsmrazor.dswa.cache.json
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\rjsmcshtml.dswa.cache.json
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\scopedcss\bundle\TestAPI.styles.css
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\staticwebassets.build.json
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\staticwebassets.build.json.cache
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\staticwebassets.development.json
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\staticwebassets.build.endpoints.json
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.dll
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\refint\TestAPI.dll
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.pdb
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\TestAPI.genruntimeconfig.cache
|
||||
C:\work\电脑硬件-01\TestAPI\obj\Debug\net9.0\ref\TestAPI.dll
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
38a42475f40bb80e703a6be2ed50ba07b47c048703c6393754361ef17fa8a577
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"GlobalPropertiesHash":"dVVFBPF+yx1XeT2fudEGH1R3XO+aUijSkKblyMEVniE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Lpeh1RwLXIyvQKXlP5V/sva7Z/agva4HylWWs9x367k=","NpVS4EAMz7A1S25A2Hx2pp3S0yLCtTxxWK1YTu5DYG4=","ZG\u002BuM97rTAPjb9pyJf6/REwgb991A7gdC1eXa0J08qk=","IqGtI2TxPcfTta2GT9LoWzn8eGroGmjezPv2CukNgaQ="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +0,0 @@
|
||||
{"GlobalPropertiesHash":"WBkI6Z6c2yRX/TuiMz89dqrp0Y8PeOUEoBMF1UPIwhY=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Lpeh1RwLXIyvQKXlP5V/sva7Z/agva4HylWWs9x367k=","NpVS4EAMz7A1S25A2Hx2pp3S0yLCtTxxWK1YTu5DYG4=","ZG\u002BuM97rTAPjb9pyJf6/REwgb991A7gdC1eXa0J08qk=","IqGtI2TxPcfTta2GT9LoWzn8eGroGmjezPv2CukNgaQ="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +0,0 @@
|
||||
{"GlobalPropertiesHash":"hVHQLSOValT1B/pCNBhgcbHPS5nopeoAhzjLbDuN7Zg=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Lpeh1RwLXIyvQKXlP5V/sva7Z/agva4HylWWs9x367k=","NpVS4EAMz7A1S25A2Hx2pp3S0yLCtTxxWK1YTu5DYG4="],"CachedAssets":{},"CachedCopyCandidates":{}}
|
||||
@@ -1 +0,0 @@
|
||||
{"Version":1,"ManifestType":"Build","Endpoints":[]}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user