refactor(services): split services into separate folders and update namespaces

This commit is contained in:
ShiYu
2025-05-22 21:18:02 +08:00
parent 9243a26189
commit fba716ba28
33 changed files with 96 additions and 107 deletions

View File

@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using Foxel.Models;
using Foxel.Models.Request.Album;
using Foxel.Models.Response.Album;
using Foxel.Services.Interface;
using Foxel.Services.Media;
namespace Foxel.Controllers;

View File

@@ -1,9 +1,10 @@
using Foxel.Models;
using Foxel.Services.Interface;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Foxel.Models.Request.Auth;
using Foxel.Models.Response.Auth;
using Foxel.Services.Auth;
using Foxel.Services.Configuration;
namespace Foxel.Controllers;

View File

@@ -1,7 +1,7 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Foxel.Models;
using Foxel.Services.Interface;
using Foxel.Services.Background;
namespace Foxel.Controllers;

View File

@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using Foxel.Models;
using Foxel.Models.DataBase;
using Foxel.Models.Request.Config;
using Foxel.Services.Interface;
using Foxel.Services.Configuration;
namespace Foxel.Controllers;

View File

@@ -4,9 +4,10 @@ using Foxel.Models;
using Foxel.Models.DataBase;
using Foxel.Models.Request.Picture;
using Foxel.Models.Response.Picture;
using Foxel.Services.Interface;
using System.Text.Json;
using System.Text.Json.Serialization;
using Foxel.Services.Configuration;
using Foxel.Services.Media;
namespace Foxel.Controllers;

View File

@@ -3,7 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using Foxel.Models;
using Foxel.Models.Request.Tag;
using Foxel.Models.Response.Tag;
using Foxel.Services.Interface;
using Foxel.Services.Media;
namespace Foxel.Controllers;

View File

@@ -1,13 +1,19 @@
using Foxel.Services;
using Foxel.Services.Interface;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Foxel.Services.Attributes;
using Foxel.Services.StorageProvider;
using System.Reflection;
using Foxel.Services.AI;
using Foxel.Services.Auth;
using Foxel.Services.Background;
using Foxel.Services.Configuration;
using Foxel.Services.Initializer;
using Foxel.Services.Media;
using Foxel.Services.Storage;
using Foxel.Services.Storage.Providers;
namespace Foxel.Extensions;

View File

@@ -1,5 +1,5 @@
using Foxel.Extensions;
using Foxel.Services.Interface;
using Foxel.Services.Initializer;
using Microsoft.AspNetCore.HttpOverrides;
var builder = WebApplication.CreateBuilder(args);

View File

@@ -1,9 +1,9 @@
using System.Net.Http.Headers;
using System.Text.Json.Serialization;
using Foxel.Services.Interface;
using Foxel.Services.Configuration;
using Foxel.Utils;
namespace Foxel.Services;
namespace Foxel.Services.AI;
public class AiService : IAiService
{
@@ -86,7 +86,7 @@ public class AiService : IAiService
if (availableTags.Count == 0)
return new List<string>();
string model = _configService["AI:Model"] ?? "deepseek-ai/deepseek-vl2"; // Assuming model can still be dynamic or default
string model = _configService["AI:Model"];
var tagsText = string.Join(", ", availableTags);
var textContent = new TextContent
{
@@ -104,7 +104,7 @@ public class AiService : IAiService
var requestContent = new ChatCompletionRequest
{
Model = model,
Messages = new ChatMessage[] { message },
Messages = [message],
Stream = false,
MaxTokens = 200,
Temperature = 0.1, // 降低温度使结果更确定性
@@ -146,7 +146,6 @@ public class AiService : IAiService
if (result is { Tags.Length: > 0 })
{
// 确保返回的标签真的在可用标签列表中
var availableTagsSet = new HashSet<string>(availableTags, StringComparer.OrdinalIgnoreCase);
var matchedTags = new List<string>();
foreach (var tagName in result.Tags)
@@ -191,7 +190,7 @@ public class AiService : IAiService
try
{
string model = _configService["AI:Model"] ?? "deepseek-ai/deepseek-vl2"; // Assuming model can still be dynamic or default
string model = _configService["AI:Model"];
var imageUrl = new ImageUrl
{
@@ -239,7 +238,7 @@ public class AiService : IAiService
var requestContent = new ChatCompletionRequest
{
Model = model,
Messages = new ChatMessage[] { message },
Messages = [message],
Stream = false,
MaxTokens = 200,
Temperature = 0.1, // 降低温度使结果更确定性
@@ -335,7 +334,7 @@ public class AiService : IAiService
var requestContent = new
{
model = model,
model,
input = text,
encoding_format = "float"
};

View File

@@ -1,4 +1,4 @@
namespace Foxel.Services.Interface;
namespace Foxel.Services.AI;
public interface IAiService
{

View File

@@ -2,13 +2,13 @@ using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Foxel.Models.DataBase;
using Foxel.Services.Interface;
using Foxel.Models.Request.Auth;
using Foxel.Services.Configuration;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using Foxel.Models.Request.Auth;
using static Foxel.Utils.AuthHelper;
namespace Foxel.Services;
namespace Foxel.Services.Auth;
public class AuthService(IDbContextFactory<MyDbContext> dbContextFactory, IConfigService configuration)
: IAuthService

View File

@@ -1,8 +1,7 @@
using Foxel.Models.DataBase;
using Foxel.Models.Request;
using Foxel.Models.Request.Auth;
namespace Foxel.Services.Interface;
namespace Foxel.Services.Auth;
public interface IAuthService
{

View File

@@ -1,12 +1,14 @@
using System.Collections.Concurrent;
using System.Threading.Channels;
using Microsoft.EntityFrameworkCore;
using Foxel.Models.DataBase;
using Foxel.Services.AI;
using Foxel.Services.Attributes;
using Foxel.Services.Interface;
using Foxel.Services.Configuration;
using Foxel.Services.Storage;
using Foxel.Utils;
using Microsoft.EntityFrameworkCore;
namespace Foxel.Services;
namespace Foxel.Services.Background;
public sealed class BackgroundTaskQueue : IBackgroundTaskQueue, IDisposable
{

View File

@@ -1,6 +1,6 @@
using Foxel.Models.DataBase;
namespace Foxel.Services.Interface;
namespace Foxel.Services.Background;
/// <summary>
/// 后台任务队列接口

View File

@@ -1,6 +1,4 @@
using Foxel.Services.Interface;
namespace Foxel.Services;
namespace Foxel.Services.Background;
public class QueuedHostedService(
ILogger<QueuedHostedService> logger,

View File

@@ -1,10 +1,9 @@
using System.Text.Json;
using Foxel.Models.DataBase;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Foxel.Models.DataBase;
using Foxel.Services.Interface;
using System.Text.Json;
namespace Foxel.Services;
namespace Foxel.Services.Configuration;
public class ConfigService(
IDbContextFactory<MyDbContext> contextFactory,
@@ -32,7 +31,7 @@ public class ConfigService(
if (config == null)
{
// 尝试从环境变量获取
string? envVarKey = key.ToUpper().Replace(".", "_").Replace("-", "_");
string envVarKey = key.ToUpper().Replace(".", "_").Replace("-", "_");
string? envVarValue = Environment.GetEnvironmentVariable(envVarKey);
if (!string.IsNullOrEmpty(envVarValue))

View File

@@ -1,6 +1,6 @@
using Foxel.Models.DataBase;
namespace Foxel.Services.Interface;
namespace Foxel.Services.Configuration;
public interface IConfigService
{

View File

@@ -1,10 +1,10 @@
using Foxel.Models.DataBase;
using Foxel.Services.Interface;
using Microsoft.EntityFrameworkCore;
using System.Security.Cryptography;
using System.Text;
using Foxel.Models.DataBase;
using Foxel.Services.Configuration;
using Microsoft.EntityFrameworkCore;
namespace Foxel.Services;
namespace Foxel.Services.Initializer;
public class DatabaseInitializer(
IDbContextFactory<MyDbContext> contextFactory,

View File

@@ -1,4 +1,4 @@
namespace Foxel.Services.Interface;
namespace Foxel.Services.Initializer;
public interface IDatabaseInitializer
{

View File

@@ -1,35 +1,22 @@
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using Foxel.Models;
using Foxel.Models.DataBase;
using Foxel.Models.Response.Album;
using Foxel.Models.Response.Picture;
using Foxel.Services.Interface;
using Foxel.Utils;
using Microsoft.EntityFrameworkCore;
namespace Foxel.Services;
namespace Foxel.Services.Media;
public class AlbumService : IAlbumService
public class AlbumService(
IDbContextFactory<MyDbContext> contextFactory,
IHttpContextAccessor httpContextAccessor)
: IAlbumService
{
private readonly IDbContextFactory<MyDbContext> _contextFactory;
private readonly IConfigService _configuration;
private readonly IHttpContextAccessor _httpContextAccessor;
private string ServerUrl => _configuration["AppSettings:ServerUrl"];
public AlbumService(IDbContextFactory<MyDbContext> contextFactory, IConfigService configuration, IHttpContextAccessor httpContextAccessor)
{
_contextFactory = contextFactory;
_configuration = configuration;
_httpContextAccessor = httpContextAccessor;
}
public async Task<PaginatedResult<AlbumResponse>> GetAlbumsAsync(int page = 1, int pageSize = 10, int? userId = null)
{
if (page < 1) page = 1;
if (pageSize < 1) pageSize = 10;
await using var dbContext = await _contextFactory.CreateDbContextAsync();
await using var dbContext = await contextFactory.CreateDbContextAsync();
// 构建查询
IQueryable<Album> query = dbContext.Albums
@@ -63,9 +50,9 @@ public class AlbumService : IAlbumService
Id = a.Id,
Name = a.Name,
Description = a.Description,
PictureCount = albumPictureCounts.TryGetValue(a.Id, out var count) ? count : 0,
PictureCount = albumPictureCounts.GetValueOrDefault(a.Id, 0),
UserId = a.UserId,
Username = a.User?.UserName,
Username = a.User.UserName,
CreatedAt = a.CreatedAt,
UpdatedAt = a.UpdatedAt
}).ToList();
@@ -81,7 +68,7 @@ public class AlbumService : IAlbumService
public async Task<AlbumResponse> GetAlbumByIdAsync(int id)
{
await using var dbContext = await _contextFactory.CreateDbContextAsync();
await using var dbContext = await contextFactory.CreateDbContextAsync();
var album = await dbContext.Albums
.Include(a => a.User)
@@ -103,7 +90,7 @@ public class AlbumService : IAlbumService
Description = album.Description,
PictureCount = pictureCount,
UserId = album.UserId,
Username = album.User?.UserName,
Username = album.User.UserName,
CreatedAt = album.CreatedAt,
UpdatedAt = album.UpdatedAt
};
@@ -116,7 +103,7 @@ public class AlbumService : IAlbumService
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("相册名称不能为空", nameof(name));
await using var dbContext = await _contextFactory.CreateDbContextAsync();
await using var dbContext = await contextFactory.CreateDbContextAsync();
// 检查用户是否存在
var user = await dbContext.Users.FindAsync(userId);
@@ -156,7 +143,7 @@ public class AlbumService : IAlbumService
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("相册名称不能为空", nameof(name));
await using var dbContext = await _contextFactory.CreateDbContextAsync();
await using var dbContext = await contextFactory.CreateDbContextAsync();
// 获取相册
var album = await dbContext.Albums
@@ -193,7 +180,7 @@ public class AlbumService : IAlbumService
Description = album.Description,
PictureCount = album.Pictures?.Count ?? 0,
UserId = album.UserId,
Username = album.User?.UserName,
Username = album.User.UserName,
CreatedAt = album.CreatedAt,
UpdatedAt = album.UpdatedAt
};
@@ -201,7 +188,7 @@ public class AlbumService : IAlbumService
public async Task<bool> DeleteAlbumAsync(int id)
{
await using var dbContext = await _contextFactory.CreateDbContextAsync();
await using var dbContext = await contextFactory.CreateDbContextAsync();
var album = await dbContext.Albums.FindAsync(id);
if (album == null)
@@ -218,7 +205,7 @@ public class AlbumService : IAlbumService
public async Task<bool> AddPictureToAlbumAsync(int albumId, int pictureId)
{
await using var dbContext = await _contextFactory.CreateDbContextAsync();
await using var dbContext = await contextFactory.CreateDbContextAsync();
// 获取相册和图片
var album = await dbContext.Albums.FindAsync(albumId);
@@ -240,7 +227,7 @@ public class AlbumService : IAlbumService
public async Task<bool> RemovePictureFromAlbumAsync(int albumId, int pictureId)
{
await using var dbContext = await _contextFactory.CreateDbContextAsync();
await using var dbContext = await contextFactory.CreateDbContextAsync();
// 获取图片
var picture = await dbContext.Pictures
@@ -260,13 +247,13 @@ public class AlbumService : IAlbumService
public async Task<bool> AddPicturesToAlbumAsync(int albumId, List<int> pictureIds)
{
await using var dbContext = await _contextFactory.CreateDbContextAsync();
await using var dbContext = await contextFactory.CreateDbContextAsync();
var album = await dbContext.Albums.FindAsync(albumId)
?? throw new KeyNotFoundException("相册不存在");
// 检查是否有权限修改此相册
var currentUser = _httpContextAccessor.HttpContext?.User;
var currentUser = httpContextAccessor.HttpContext?.User;
if (currentUser != null)
{
var userId = int.Parse(currentUser.FindFirst(ClaimTypes.NameIdentifier)?.Value ?? "0");

View File

@@ -1,8 +1,7 @@
using Foxel.Models;
using Foxel.Models.Response;
using Foxel.Models.Response.Album;
namespace Foxel.Services.Interface;
namespace Foxel.Services.Media;
public interface IAlbumService
{

View File

@@ -3,7 +3,7 @@ using Foxel.Models.DataBase;
using Foxel.Models.Response.Picture;
using Foxel.Services.Attributes;
namespace Foxel.Services.Interface;
namespace Foxel.Services.Media;
public interface IPictureService
{

View File

@@ -1,7 +1,7 @@
using Foxel.Models;
using Foxel.Models.Response.Tag;
namespace Foxel.Services.Interface;
namespace Foxel.Services.Media;
public interface ITagService
{

View File

@@ -1,15 +1,18 @@
using System.Text.Json;
using Foxel.Models;
using Foxel.Models.DataBase;
using Foxel.Services.Interface;
using Foxel.Models.Response.Picture;
using Foxel.Services.AI;
using Foxel.Services.Attributes;
using Foxel.Services.Background;
using Foxel.Services.Configuration;
using Foxel.Services.Storage;
using Foxel.Utils;
using Microsoft.EntityFrameworkCore;
using Pgvector;
using Pgvector.EntityFrameworkCore;
using System.Text.Json;
using Foxel.Models.Response.Picture;
using Foxel.Services.Attributes;
namespace Foxel.Services;
namespace Foxel.Services.Media;
public class PictureService(
IDbContextFactory<MyDbContext> contextFactory,
@@ -19,8 +22,6 @@ public class PictureService(
IStorageService storageService)
: IPictureService
{
private readonly string _serverUrl = configuration["AppSettings:ServerUrl"];
public async Task<PaginatedResult<PictureResponse>> GetPicturesAsync(
int page = 1,
int pageSize = 8,
@@ -110,7 +111,7 @@ public class PictureService(
var paginatedResults = allResults
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(r => MapPictureToResponse(r.Picture, _serverUrl))
.Select(r => MapPictureToResponse(r.Picture))
.ToList();
// 处理收藏信息
@@ -181,7 +182,7 @@ public class PictureService(
// 转换为响应格式
var pictures = picturesData
.Select(p => MapPictureToResponse(p, _serverUrl))
.Select(p => MapPictureToResponse(p))
.ToList();
// 处理收藏信息
@@ -333,7 +334,7 @@ public class PictureService(
}
// 将数据库实体映射到响应对象
private PictureResponse MapPictureToResponse(Picture picture, string serverUrl)
private PictureResponse MapPictureToResponse(Picture picture)
{
return new PictureResponse
{
@@ -449,7 +450,7 @@ public class PictureService(
}
string fileExtension = Path.GetExtension(fileName);
string newFileName = $"{Guid.NewGuid()}{fileExtension}";
_ = $"{Guid.NewGuid()}{fileExtension}";
// 使用存储服务保存文件
string relativePath = await storageService.SaveAsync(storageType.Value, fileStream, fileName, contentType);

View File

@@ -2,7 +2,7 @@ using Microsoft.EntityFrameworkCore;
using Foxel.Models;
using Foxel.Models.DataBase;
using Foxel.Models.Response.Tag;
using Foxel.Services.Interface;
using Foxel.Services.Media;
namespace Foxel.Services;

View File

@@ -1,4 +1,4 @@
namespace Foxel.Services.Interface;
namespace Foxel.Services.Storage;
public interface IStorageProvider
{

View File

@@ -1,7 +1,6 @@
using Foxel.Models.DataBase;
using Foxel.Services.Attributes;
namespace Foxel.Services.Interface;
namespace Foxel.Services.Storage;
/// <summary>
/// 统一的存储服务接口

View File

@@ -1,13 +1,13 @@
using Foxel.Services.Interface;
using COSXML;
using COSXML.Auth;
using COSXML.Model.Object;
using COSXML.Transfer;
using COSXML.CosException;
using COSXML.Model.Object;
using COSXML.Model.Tag;
using COSXML.Transfer;
using Foxel.Services.Attributes;
using Foxel.Services.Configuration;
namespace Foxel.Services.StorageProvider;
namespace Foxel.Services.Storage.Providers;
public class CustomQCloudCredentialProvider : DefaultSessionQCloudCredentialProvider
{

View File

@@ -1,7 +1,7 @@
using Foxel.Services.Attributes;
using Foxel.Services.Interface;
using Foxel.Services.Configuration;
namespace Foxel.Services.StorageProvider;
namespace Foxel.Services.Storage.Providers;
[StorageProvider(StorageType.Local)]
public class LocalStorageProvider(IConfigService config) : IStorageProvider

View File

@@ -1,10 +1,10 @@
using Foxel.Services.Attributes;
using Foxel.Services.Interface;
using Amazon.S3;
using Amazon.S3.Model;
using Amazon.S3.Transfer;
using Foxel.Services.Attributes;
using Foxel.Services.Configuration;
namespace Foxel.Services.StorageProvider;
namespace Foxel.Services.Storage.Providers;
[StorageProvider(StorageType.S3)]
public class S3StorageProvider : IStorageProvider

View File

@@ -1,10 +1,10 @@
using Foxel.Services.Attributes;
using Foxel.Services.Interface;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Text.Json.Serialization;
using Foxel.Services.Attributes;
using Foxel.Services.Configuration;
namespace Foxel.Services.StorageProvider;
namespace Foxel.Services.Storage.Providers;
[StorageProvider(StorageType.Telegram)]
public class TelegramStorageProvider(IConfigService configService) : IStorageProvider

View File

@@ -1,9 +1,7 @@
using System.Reflection;
using Foxel.Models.DataBase;
using Foxel.Services.Attributes;
using Foxel.Services.Interface;
namespace Foxel.Services;
namespace Foxel.Services.Storage;
/// <summary>
/// 统一的存储服务实现