初始化
Some checks failed
CI/CD Pipeline / 测试 (18.x) (push) Has been cancelled
CI/CD Pipeline / 测试 (20.x) (push) Has been cancelled
CI/CD Pipeline / 安全检查 (push) Has been cancelled
CI/CD Pipeline / 部署 (push) Has been cancelled
CI/CD Pipeline / 通知 (push) Has been cancelled

This commit is contained in:
2025-11-03 19:47:36 +08:00
parent 7a04b85667
commit f25b0307db
454 changed files with 37064 additions and 4544 deletions

167
.github/workflows/ci.yml vendored Normal file
View 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

View File

@@ -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>

View File

@@ -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);
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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"
}
}
}
}
}

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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"
}
]
}

View File

@@ -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": []
}
]
}

View File

@@ -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>();
}
}
}

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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; }
}
}

View File

@@ -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();
}
}

View File

@@ -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; }
}
}

View File

@@ -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();
}
}
}

View File

@@ -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"
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}
}
}
}
}
}

View File

@@ -1,10 +0,0 @@
using HardwarePerformance.Models.DTOs;
namespace HardwarePerformance.Services
{
public interface ICategoryService
{
Task<IEnumerable<CategoryDto>> GetAllCategoriesAsync();
Task<CategoryDto?> GetCategoryByIdAsync(int id);
}
}

View File

@@ -1,10 +0,0 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace MinimalAPI.Services
{
public interface IComparisonService
{
Task<object> CompareProductsAsync(List<int> productIds);
}
}

View File

@@ -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");
}
}

View File

@@ -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)
};
}
}
}

View File

@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -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": ""
}
}
}

View File

@@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -1 +0,0 @@
d440d87b19ec785d73e3e23306ab9680f7c4890517e977a1c254363c4f9347af

View File

@@ -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 =

View File

@@ -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;

View File

@@ -1 +0,0 @@
05c277f9ef6d1c96b7c3a0662bb97967ee23e63c93d8aa1cd4aec40fc1c9febe

View File

@@ -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

View File

@@ -1 +0,0 @@
2cc508c4c984e00de0cee28ea5f00a982100be8dad563e9011ff5619c65193f1

View File

@@ -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":{}}

View File

@@ -1 +0,0 @@
{"GlobalPropertiesHash":"N05q42TiGbCpQThCMeppx1cqHYMYPr+MKOVECH+LM5o=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["vICfSWyJTl7Llwoo5pZeHmuInfb\u002BPDuiEXf67e03aVY=","HGCAjAUzY86/cTTfeLk8LuqAnP1lIVdvBKQP\u002BJ23JAc=","pXPB2q0SLSiqgQ6QWxaK7/r8KBQLxQCCriy2hCwae1A=","eZ0avPpvDZzkkkg3Y40\u002B9/FcOts1TlMSAvHEIAFbU8Q="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +0,0 @@
{"GlobalPropertiesHash":"Qscza4zuOZL6qExaAaOGJZbKXeiSjSngMQ6k1ALDEr8=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["vICfSWyJTl7Llwoo5pZeHmuInfb\u002BPDuiEXf67e03aVY=","HGCAjAUzY86/cTTfeLk8LuqAnP1lIVdvBKQP\u002BJ23JAc="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +0,0 @@
{"Version":1,"Hash":"JkTLtN583la2vWjETk7JpCURqkzL9J5tTWuu+nxCKow=","Source":"MinimalAPI","BasePath":"_content/MinimalAPI","Mode":"Default","ManifestType":"Build","ReferencedProjectsConfiguration":[],"DiscoveryPatterns":[],"Assets":[],"Endpoints":[]}

View File

@@ -1 +0,0 @@
JkTLtN583la2vWjETk7JpCURqkzL9J5tTWuu+nxCKow=

View File

@@ -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"
}
}
}
}
}

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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"
}
]
}

View File

@@ -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
View 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
- 后端APIhttp://localhost:7001
- Hangfire Dashboardhttp://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 UIhttp://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/)
---
⭐ 如果这个项目对你有帮助,请给我们一个星标!

View File

@@ -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);
}

View File

@@ -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"
}
}
}
}

View File

@@ -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>

View File

@@ -1,6 +0,0 @@
@SimpleAPI_HostAddress = http://localhost:5256
GET {{SimpleAPI_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -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"
}
}
}
}
}

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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"
}
]
}

View File

@@ -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": []
}
]
}

View File

@@ -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();

View File

@@ -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"
}
}
}
}

View File

@@ -1,9 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
</Project>

View File

@@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -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.

View File

@@ -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
}
}
}

View File

@@ -1 +0,0 @@
{"Version":1,"ManifestType":"Build","Endpoints":[]}

View File

@@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@@ -1,9 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@@ -1 +0,0 @@
ccdd8558c5bd69980236f6ef7435019aea79860a1fdbecb265564d3b99885b5a

View File

@@ -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 =

View File

@@ -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;

View File

@@ -1 +0,0 @@
629dc1ba4c6ea1c70ecea85384c28652dcd9f627da004e854aea08234c082e60

View File

@@ -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.

View File

@@ -1 +0,0 @@
38a42475f40bb80e703a6be2ed50ba07b47c048703c6393754361ef17fa8a577

Binary file not shown.

Binary file not shown.

View File

@@ -1 +0,0 @@
{"GlobalPropertiesHash":"dVVFBPF+yx1XeT2fudEGH1R3XO+aUijSkKblyMEVniE=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Lpeh1RwLXIyvQKXlP5V/sva7Z/agva4HylWWs9x367k=","NpVS4EAMz7A1S25A2Hx2pp3S0yLCtTxxWK1YTu5DYG4=","ZG\u002BuM97rTAPjb9pyJf6/REwgb991A7gdC1eXa0J08qk=","IqGtI2TxPcfTta2GT9LoWzn8eGroGmjezPv2CukNgaQ="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +0,0 @@
{"GlobalPropertiesHash":"WBkI6Z6c2yRX/TuiMz89dqrp0Y8PeOUEoBMF1UPIwhY=","FingerprintPatternsHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Lpeh1RwLXIyvQKXlP5V/sva7Z/agva4HylWWs9x367k=","NpVS4EAMz7A1S25A2Hx2pp3S0yLCtTxxWK1YTu5DYG4=","ZG\u002BuM97rTAPjb9pyJf6/REwgb991A7gdC1eXa0J08qk=","IqGtI2TxPcfTta2GT9LoWzn8eGroGmjezPv2CukNgaQ="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +0,0 @@
{"GlobalPropertiesHash":"hVHQLSOValT1B/pCNBhgcbHPS5nopeoAhzjLbDuN7Zg=","FingerprintPatternsHash":"gq3WsqcKBUGTSNle7RKKyXRIwh7M8ccEqOqYvIzoM04=","PropertyOverridesHash":"8ZRc1sGeVrPBx4lD717BgRaQekyh78QKV9SKsdt638U=","InputHashes":["Lpeh1RwLXIyvQKXlP5V/sva7Z/agva4HylWWs9x367k=","NpVS4EAMz7A1S25A2Hx2pp3S0yLCtTxxWK1YTu5DYG4="],"CachedAssets":{},"CachedCopyCandidates":{}}

View File

@@ -1 +0,0 @@
{"Version":1,"ManifestType":"Build","Endpoints":[]}

Some files were not shown because too many files have changed in this diff Show More