using CampusErrand.Data; using CampusErrand.Models; using CampusErrand.Models.Dtos; using Microsoft.EntityFrameworkCore; namespace CampusErrand.Endpoints; public static class ConfigEndpoints { public static void MapConfigEndpoints(this WebApplication app) { // 获取页面顶图配置 app.MapGet("/api/config/page-banner/{page}", async (string page, AppDbContext db) => { var key = $"page_banner_{page}"; var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == key); return Results.Ok(new ConfigResponse { Key = key, Value = config?.Value ?? "", UpdatedAt = config?.UpdatedAt ?? DateTime.MinValue }); }); // 获取客服二维码 app.MapGet("/api/config/qrcode", async (AppDbContext db) => { var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == "qrcode"); return Results.Ok(new ConfigResponse { Key = "qrcode", Value = config?.Value ?? "", UpdatedAt = config?.UpdatedAt ?? DateTime.MinValue }); }); // 获取用户协议 app.MapGet("/api/config/agreement", async (AppDbContext db) => { var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == "agreement"); return Results.Ok(new ConfigResponse { Key = "agreement", Value = config?.Value ?? "", UpdatedAt = config?.UpdatedAt ?? DateTime.MinValue }); }); // 获取隐私政策 app.MapGet("/api/config/privacy", async (AppDbContext db) => { var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == "privacy"); return Results.Ok(new ConfigResponse { Key = "privacy", Value = config?.Value ?? "", UpdatedAt = config?.UpdatedAt ?? DateTime.MinValue }); }); // 获取跑腿协议 app.MapGet("/api/config/runner-agreement", async (AppDbContext db) => { var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == "runner_agreement"); return Results.Ok(new ConfigResponse { Key = "runner_agreement", Value = config?.Value ?? "", UpdatedAt = config?.UpdatedAt ?? DateTime.MinValue }); }); // 获取提现说明 app.MapGet("/api/config/withdrawal-guide", async (AppDbContext db) => { var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == "withdrawal_guide"); return Results.Ok(new ConfigResponse { Key = "withdrawal_guide", Value = config?.Value ?? "", UpdatedAt = config?.UpdatedAt ?? DateTime.MinValue }); }); // 获取最低佣金配置 app.MapGet("/api/config/min-commission", async (AppDbContext db) => { var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == "min_commission"); return Results.Ok(new ConfigResponse { Key = "min_commission", Value = config?.Value ?? "1.0", UpdatedAt = config?.UpdatedAt ?? DateTime.MinValue }); }); // 管理端获取指定配置 app.MapGet("/api/admin/config/{key}", async (string key, AppDbContext db) => { var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == key); return Results.Ok(new ConfigResponse { Key = key, Value = config?.Value ?? "", UpdatedAt = config?.UpdatedAt ?? DateTime.MinValue }); }).RequireAuthorization("AdminOnly"); // 管理端更新系统配置 app.MapPut("/api/admin/config/{key}", async (string key, UpdateConfigRequest request, AppDbContext db) => { // 允许的配置键白名单 var allowedKeys = new HashSet { "qrcode", "agreement", "privacy", "runner_agreement", "withdrawal_guide", "freeze_days", "min_commission", "page_banner_pickup", "page_banner_delivery", "page_banner_help", "page_banner_purchase", "page_banner_food", "page_banner_order-hall" }; if (!allowedKeys.Contains(key)) return Results.BadRequest(new { code = 400, message = $"不支持的配置键: {key}" }); var config = await db.SystemConfigs.FirstOrDefaultAsync(c => c.Key == key); if (config == null) { config = new SystemConfig { Key = key, Value = request.Value, UpdatedAt = DateTime.UtcNow }; db.SystemConfigs.Add(config); } else { config.Value = request.Value; config.UpdatedAt = DateTime.UtcNow; } await db.SaveChangesAsync(); return Results.Ok(new ConfigResponse { Key = config.Key, Value = config.Value, UpdatedAt = config.UpdatedAt }); }).RequireAuthorization("AdminOnly"); // 图片上传接口 app.MapPost("/api/upload/image", async (IFormFile file, IConfiguration config) => { if (file == null || file.Length == 0) return Results.BadRequest(new { code = 400, message = "请选择要上传的图片" }); // 文件大小校验(默认 5MB) var maxSize = config.GetValue("Upload:MaxFileSizeBytes", 5242880); if (file.Length > maxSize) return Results.BadRequest(new { code = 400, message = $"图片大小不能超过 {maxSize / 1024 / 1024}MB" }); // 文件扩展名校验 var allowedExtensions = config.GetSection("Upload:AllowedExtensions").Get() ?? new[] { ".jpg", ".jpeg", ".png", ".gif", ".webp" }; var ext = Path.GetExtension(file.FileName).ToLowerInvariant(); if (!allowedExtensions.Contains(ext)) return Results.BadRequest(new { code = 400, message = $"不支持的图片格式,仅支持 {string.Join(", ", allowedExtensions)}" }); // 上传到腾讯云 COS var cosConfig = new COSXML.CosXmlConfig.Builder() .IsHttps(true) .SetRegion(config["COS:Region"]) .Build(); var credential = new COSXML.Auth.DefaultQCloudCredentialProvider( config["COS:SecretId"], config["COS:SecretKey"], 600); var cosXml = new COSXML.CosXmlServer(cosConfig, credential); var bucket = config["COS:Bucket"]!; var cosKey = $"uploads/{DateTime.UtcNow:yyyyMMdd}/{Guid.NewGuid()}{ext}"; // 将上传文件写入临时文件 var tempPath = Path.GetTempFileName(); try { using (var stream = new FileStream(tempPath, FileMode.Create)) { await file.CopyToAsync(stream); } var putRequest = new COSXML.Model.Object.PutObjectRequest(bucket, cosKey, tempPath); var putResult = cosXml.PutObject(putRequest); if (putResult.httpCode != 200) return Results.BadRequest(new { code = 400, message = "图片上传失败" }); var url = $"{config["COS:BaseUrl"]}/{cosKey}"; return Results.Ok(new UploadImageResponse { Url = url }); } finally { if (File.Exists(tempPath)) File.Delete(tempPath); } }).RequireAuthorization() .DisableAntiforgery(); } }