diff --git a/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs b/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs new file mode 100644 index 0000000..cd8c53c --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs @@ -0,0 +1,96 @@ +using MiAssessment.Admin.Filters; +using MiAssessment.Admin.Models.Common; +using MiAssessment.Admin.Models.Config; +using MiAssessment.Admin.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace MiAssessment.Admin.Controllers; + +/// +/// 后台配置管理控制器 +/// +[ApiController] +[Route("api/admin/config")] +[Authorize] +public class ConfigController : ControllerBase +{ + private readonly IAdminConfigService _configService; + private readonly ILogger _logger; + + /// + /// 配置键常量 + /// + private static class ConfigKeys + { + public const string Upload = "upload_setting"; + } + + public ConfigController(IAdminConfigService configService, ILogger logger) + { + _configService = configService; + _logger = logger; + } + + /// + /// 获取上传配置 + /// + /// 上传配置 + [HttpGet("upload/get")] + public async Task> GetUploadConfig() + { + try + { + var config = await _configService.GetConfigAsync(ConfigKeys.Upload); + return ApiResponse.Success(config ?? new UploadSetting { Type = "1" }); + } + catch (Exception ex) + { + _logger.LogError(ex, "获取上传配置失败"); + return ApiResponse.Error(AdminErrorCodes.InternalError, "获取上传配置失败"); + } + } + + /// + /// 更新上传配置 + /// + /// 上传配置 + /// 更新结果 + [HttpPost("upload/update")] + [OperationLog("配置管理", "更新上传配置")] + public async Task> UpdateUploadConfig([FromBody] UploadSetting request) + { + if (string.IsNullOrWhiteSpace(request.Type)) + { + return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "存储类型不能为空"); + } + + // 验证腾讯云COS配置 + if (request.Type == "3") + { + if (string.IsNullOrWhiteSpace(request.AppId)) + return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "AppId不能为空"); + if (string.IsNullOrWhiteSpace(request.AccessKeyId)) + return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "SecretId不能为空"); + if (string.IsNullOrWhiteSpace(request.AccessKeySecret)) + return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "SecretKey不能为空"); + if (string.IsNullOrWhiteSpace(request.Bucket)) + return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "存储桶名称不能为空"); + if (string.IsNullOrWhiteSpace(request.Region)) + return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "地域不能为空"); + if (string.IsNullOrWhiteSpace(request.Domain)) + return ApiResponse.Error(AdminErrorCodes.InvalidParameter, "访问域名不能为空"); + } + + try + { + var result = await _configService.UpdateConfigAsync(ConfigKeys.Upload, request); + return ApiResponse.Success(result, "配置更新成功"); + } + catch (Exception ex) + { + _logger.LogError(ex, "更新上传配置失败"); + return ApiResponse.Error(AdminErrorCodes.InternalError, "更新上传配置失败"); + } + } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin/Extensions/ServiceCollectionExtensions.cs b/server/MiAssessment/src/MiAssessment.Admin/Extensions/ServiceCollectionExtensions.cs index a5a4ab0..477963a 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/Extensions/ServiceCollectionExtensions.cs +++ b/server/MiAssessment/src/MiAssessment.Admin/Extensions/ServiceCollectionExtensions.cs @@ -51,6 +51,7 @@ public static class ServiceCollectionExtensions services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddSingleton(); diff --git a/server/MiAssessment/src/MiAssessment.Admin/Models/Config/UploadSetting.cs b/server/MiAssessment/src/MiAssessment.Admin/Models/Config/UploadSetting.cs new file mode 100644 index 0000000..5b16945 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin/Models/Config/UploadSetting.cs @@ -0,0 +1,42 @@ +namespace MiAssessment.Admin.Models.Config; + +/// +/// 上传配置 +/// +public class UploadSetting +{ + /// + /// 存储类型 1本地 2阿里云 3腾讯云 + /// + public string Type { get; set; } = "1"; + + /// + /// 腾讯云AppId + /// + public string? AppId { get; set; } + + /// + /// 存储桶名称 + /// + public string? Bucket { get; set; } + + /// + /// 地域 + /// + public string? Region { get; set; } + + /// + /// SecretId + /// + public string? AccessKeyId { get; set; } + + /// + /// SecretKey + /// + public string? AccessKeySecret { get; set; } + + /// + /// 访问域名 + /// + public string? Domain { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin/Services/AdminConfigService.cs b/server/MiAssessment/src/MiAssessment.Admin/Services/AdminConfigService.cs new file mode 100644 index 0000000..e39348e --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin/Services/AdminConfigService.cs @@ -0,0 +1,92 @@ +using System; +using System.Text.Json; +using System.Threading.Tasks; +using MiAssessment.Admin.Data; +using MiAssessment.Admin.Entities; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace MiAssessment.Admin.Services; + +/// +/// 后台配置服务实现 +/// +public class AdminConfigService : IAdminConfigService +{ + private readonly AdminDbContext _dbContext; + private readonly ILogger _logger; + + private static readonly JsonSerializerOptions JsonOptions = new() + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = false + }; + + public AdminConfigService(AdminDbContext dbContext, ILogger logger) + { + _dbContext = dbContext; + _logger = logger; + } + + /// + public async Task GetConfigAsync(string key) + { + var config = await _dbContext.AdminConfigs + .AsNoTracking() + .FirstOrDefaultAsync(c => c.ConfigKey == key); + return config?.ConfigValue; + } + + /// + public async Task GetConfigAsync(string key) where T : class + { + var value = await GetConfigAsync(key); + if (string.IsNullOrEmpty(value)) + return null; + + try + { + return JsonSerializer.Deserialize(value, JsonOptions); + } + catch (JsonException ex) + { + _logger.LogWarning(ex, "反序列化配置失败: {Key}", key); + return null; + } + } + + /// + public async Task UpdateConfigAsync(string key, string value) + { + var config = await _dbContext.AdminConfigs + .FirstOrDefaultAsync(c => c.ConfigKey == key); + + if (config == null) + { + // 创建新配置 + config = new AdminConfig + { + ConfigKey = key, + ConfigValue = value, + CreatedAt = DateTime.Now + }; + _dbContext.AdminConfigs.Add(config); + } + else + { + // 更新现有配置 + config.ConfigValue = value; + config.UpdatedAt = DateTime.Now; + } + + await _dbContext.SaveChangesAsync(); + return true; + } + + /// + public async Task UpdateConfigAsync(string key, T value) where T : class + { + var jsonValue = JsonSerializer.Serialize(value, JsonOptions); + return await UpdateConfigAsync(key, jsonValue); + } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin/Services/IAdminConfigService.cs b/server/MiAssessment/src/MiAssessment.Admin/Services/IAdminConfigService.cs new file mode 100644 index 0000000..d1477db --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin/Services/IAdminConfigService.cs @@ -0,0 +1,41 @@ +using System.Threading.Tasks; + +namespace MiAssessment.Admin.Services; + +/// +/// 后台配置服务接口 +/// +public interface IAdminConfigService +{ + /// + /// 获取配置值 + /// + /// 配置键 + /// 配置值 + Task GetConfigAsync(string key); + + /// + /// 获取配置值并反序列化 + /// + /// 配置类型 + /// 配置键 + /// 配置对象 + Task GetConfigAsync(string key) where T : class; + + /// + /// 更新配置值 + /// + /// 配置键 + /// 配置值 + /// 是否成功 + Task UpdateConfigAsync(string key, string value); + + /// + /// 更新配置值(序列化对象) + /// + /// 配置类型 + /// 配置键 + /// 配置对象 + /// 是否成功 + Task UpdateConfigAsync(string key, T value) where T : class; +} diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts new file mode 100644 index 0000000..3329f37 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts @@ -0,0 +1,46 @@ +/** + * System Config API - 系统配置 API + * @module api/system/config + */ +import { request, type ApiResponse } from '@/utils/request' + +/** + * 上传配置 + */ +export interface UploadSetting { + /** 存储类型 1本地 2阿里云 3腾讯云 */ + type: string + /** 腾讯云AppId */ + AppId?: string + /** 存储桶名称 */ + Bucket?: string + /** 地域 */ + Region?: string + /** SecretId */ + AccessKeyId?: string + /** SecretKey */ + AccessKeySecret?: string + /** 访问域名 */ + Domain?: string +} + +/** + * 获取上传配置 + */ +export function getUploadConfig(): Promise> { + return request({ + url: '/admin/config/upload/get', + method: 'get' + }) +} + +/** + * 更新上传配置 + */ +export function updateUploadConfig(data: UploadSetting): Promise> { + return request({ + url: '/admin/config/upload/update', + method: 'post', + data + }) +} diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/store/modules/permission.ts b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/store/modules/permission.ts index 7de5952..8086fd1 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/store/modules/permission.ts +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/store/modules/permission.ts @@ -4,8 +4,8 @@ import type { RouteRecordRaw } from 'vue-router' import { getUserMenus, type MenuTree } from '@/api/menu' import Layout from '@/layout/index.vue' -// 视图模块映射 -const viewModules = import.meta.glob('@/views/**/*.vue') +// 视图模块映射 - 使用相对路径格式 +const viewModules = import.meta.glob('/src/views/**/*.vue') export const usePermissionStore = defineStore('permission', () => { const routes = ref([]) @@ -82,7 +82,13 @@ export const usePermissionStore = defineStore('permission', () => { // 加载组件 function loadComponent(component: string) { const path = `/src/views/${component}.vue` - return viewModules[path] || (() => import('@/views/error/404.vue')) + + if (viewModules[path]) { + return viewModules[path] + } + + console.warn(`Component not found: ${component}, path: ${path}`) + return () => import('@/views/error/404.vue') } // 重置状态 diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/upload.vue b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/upload.vue new file mode 100644 index 0000000..66a4331 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/upload.vue @@ -0,0 +1,356 @@ + + + + +