2026-06-01 17:59:23 +08:00
|
|
|
|
using Autofac;
|
|
|
|
|
|
using Autofac.Extensions.DependencyInjection;
|
|
|
|
|
|
using BCrypt.Net;
|
2026-06-04 18:03:34 +08:00
|
|
|
|
using Microsoft.AspNetCore.DataProtection;
|
2026-06-01 17:59:23 +08:00
|
|
|
|
using Microsoft.AspNetCore.Mvc;
|
2026-06-02 17:58:10 +08:00
|
|
|
|
using Microsoft.AspNetCore.ResponseCompression;
|
2026-06-01 17:59:23 +08:00
|
|
|
|
using Microsoft.OpenApi;
|
2026-06-02 17:58:10 +08:00
|
|
|
|
using Microsoft.OpenApi.Models;
|
2026-06-01 17:59:23 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Common.Extensions;
|
2026-06-02 14:10:43 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Common.Helpers;
|
|
|
|
|
|
using QYZH.InteractiveMagazine.Infrastructure.Autofacs;
|
|
|
|
|
|
using QYZH.InteractiveMagazine.Infrastructure.Context;
|
2026-06-01 13:42:40 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Infrastructure.Extensions;
|
|
|
|
|
|
using QYZH.InteractiveMagazine.Infrastructure.Middleware;
|
2026-06-09 14:36:46 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Infrastructure.Redis;
|
2026-06-01 17:59:23 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Models.Entity;
|
|
|
|
|
|
using QYZH.InteractiveMagazine.Models.Enum;
|
2026-06-09 11:41:28 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Models.Settings;
|
2026-06-01 13:42:40 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Repository;
|
2026-06-02 14:10:43 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Repository.Core;
|
2026-06-01 13:42:40 +08:00
|
|
|
|
using Serilog;
|
2026-06-02 14:10:43 +08:00
|
|
|
|
using SqlSugar.IOC;
|
2026-06-01 17:59:23 +08:00
|
|
|
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
|
|
|
|
using Swashbuckle.AspNetCore.SwaggerUI;
|
2026-06-02 14:10:43 +08:00
|
|
|
|
using System.Text.Json.Serialization;
|
|
|
|
|
|
using Yitter.IdGenerator;
|
2026-06-01 13:42:40 +08:00
|
|
|
|
|
|
|
|
|
|
var builder = WebApplication.CreateBuilder(args);
|
|
|
|
|
|
|
2026-06-09 11:41:28 +08:00
|
|
|
|
// 加载勋章规则独立配置文件
|
|
|
|
|
|
builder.Configuration.AddJsonFile("medal-rule-config.json", optional: false, reloadOnChange: true);
|
|
|
|
|
|
|
2026-06-02 14:10:43 +08:00
|
|
|
|
// 初始化雪花ID生成器
|
|
|
|
|
|
YitIdHelper.SetIdGenerator(new IdGeneratorOptions() { WorkerId = 1 });
|
2026-06-01 17:59:23 +08:00
|
|
|
|
// autofac注入 允许使用autofac作为DI容器
|
|
|
|
|
|
builder.UseAutofac();
|
|
|
|
|
|
|
2026-06-09 14:36:46 +08:00
|
|
|
|
|
|
|
|
|
|
|
2026-06-02 14:10:43 +08:00
|
|
|
|
builder.InitSqlSugarDb(new IocConfig()
|
|
|
|
|
|
{
|
|
|
|
|
|
ConfigId = 0,
|
|
|
|
|
|
DbType = IocDbType.MySql,
|
|
|
|
|
|
ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection"),
|
|
|
|
|
|
IsAutoCloseConnection = true,
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-04 18:03:34 +08:00
|
|
|
|
// 消除Error unprotecting the session cookie警告
|
|
|
|
|
|
builder.Services.AddDataProtection()
|
|
|
|
|
|
.PersistKeysToFileSystem(new DirectoryInfo(Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + "DataProtection"));
|
2026-06-09 14:36:46 +08:00
|
|
|
|
//redis
|
|
|
|
|
|
builder.Services.AddCSRedisCacheExtension(builder.Configuration.GetSection("RedisSettings"));
|
2026-06-01 13:42:40 +08:00
|
|
|
|
// 配置Serilog
|
|
|
|
|
|
Log.Logger = new LoggerConfiguration()
|
|
|
|
|
|
.ReadFrom.Configuration(builder.Configuration)
|
|
|
|
|
|
.Enrich.FromLogContext()
|
|
|
|
|
|
.CreateLogger();
|
|
|
|
|
|
|
|
|
|
|
|
builder.Host.UseSerilog();
|
2026-06-02 14:10:43 +08:00
|
|
|
|
builder.Services.AddControllers(options =>
|
|
|
|
|
|
{
|
|
|
|
|
|
options.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes = true;//Required 不作为必填
|
2026-06-02 17:58:10 +08:00
|
|
|
|
options.Filters.Add<ModelValidActionFilterAttribute>();//添加自定义模型验证
|
2026-06-02 14:10:43 +08:00
|
|
|
|
})
|
|
|
|
|
|
.AddJsonOptions(options =>
|
|
|
|
|
|
{
|
2026-06-02 17:58:10 +08:00
|
|
|
|
// 配置 JSON 不区分大小写(支持 camelCase 和 PascalCase)
|
|
|
|
|
|
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
|
2026-06-02 14:10:43 +08:00
|
|
|
|
// 配置返回时间格式转换
|
|
|
|
|
|
options.JsonSerializerOptions.Converters.Add(new JsonConverterUtil.DateTimeConverter());
|
|
|
|
|
|
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
|
|
|
|
|
|
}).ConfigureApiBehaviorOptions(opt => opt.SuppressModelStateInvalidFilter = true);//关闭默认模型验证
|
2026-06-01 13:42:40 +08:00
|
|
|
|
builder.Services.AddEndpointsApiExplorer();
|
|
|
|
|
|
|
2026-06-01 17:59:23 +08:00
|
|
|
|
// 跨域配置
|
2026-06-04 18:03:34 +08:00
|
|
|
|
builder.Services.AddDataProtection().UseEphemeralDataProtectionProvider();
|
2026-06-01 17:59:23 +08:00
|
|
|
|
builder.AddCorsRegister();
|
2026-06-02 14:10:43 +08:00
|
|
|
|
//builder.Services.AddSession();
|
|
|
|
|
|
builder.Services.AddHttpClient();
|
2026-06-01 17:59:23 +08:00
|
|
|
|
// 注册 Swagger 文档
|
|
|
|
|
|
builder.Services.AddSwaggerGen(option =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var xmlFile = $"{AppDomain.CurrentDomain.FriendlyName}.xml";
|
|
|
|
|
|
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
|
|
|
|
|
|
var modelXml = Path.Combine(AppContext.BaseDirectory, $"QYZH.InteractiveMagazine.Models.xml");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Enum.GetValues<ApiVersionEnum>().ToList().ForEach(version =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// 配置文档信息
|
|
|
|
|
|
option.SwaggerDoc(version.ToString(), new OpenApiInfo
|
|
|
|
|
|
{
|
|
|
|
|
|
Title = AppDomain.CurrentDomain.FriendlyName,
|
|
|
|
|
|
Version = "互动期刊接口文档",
|
|
|
|
|
|
Description = $"{version.GetDescription()}接口,Last Modify Time:{new FileInfo(xmlPath).LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss")}"
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
// 配置接口路径排序
|
|
|
|
|
|
option.OrderActionsBy(o => o.RelativePath);
|
|
|
|
|
|
|
|
|
|
|
|
if (File.Exists(xmlPath))
|
|
|
|
|
|
option.IncludeXmlComments(xmlPath, true);
|
|
|
|
|
|
if (File.Exists(modelXml))
|
|
|
|
|
|
option.IncludeXmlComments(modelXml, true);
|
2026-06-01 13:42:40 +08:00
|
|
|
|
|
2026-06-01 17:59:23 +08:00
|
|
|
|
option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
|
|
|
|
|
|
{
|
|
|
|
|
|
Description = "请输入 Token,格式为 Bearer Token",
|
|
|
|
|
|
Name = "Authorization",
|
|
|
|
|
|
In = ParameterLocation.Header,
|
|
|
|
|
|
Type = SecuritySchemeType.ApiKey,
|
|
|
|
|
|
BearerFormat = "JWT",
|
|
|
|
|
|
Scheme = "Bearer"
|
|
|
|
|
|
});
|
2026-06-02 17:58:10 +08:00
|
|
|
|
option.AddSecurityRequirement(new OpenApiSecurityRequirement
|
|
|
|
|
|
{
|
|
|
|
|
|
{
|
|
|
|
|
|
new OpenApiSecurityScheme
|
|
|
|
|
|
{
|
|
|
|
|
|
Reference = new OpenApiReference
|
|
|
|
|
|
{
|
|
|
|
|
|
Type = ReferenceType.SecurityScheme,
|
|
|
|
|
|
Id = "Bearer"
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
new string[] { }
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-06-01 17:59:23 +08:00
|
|
|
|
|
|
|
|
|
|
// 过滤文档/路径/方法筛选接口的响应结果
|
|
|
|
|
|
option.DocInclusionPredicate((docName, apiDesc) =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// 方式 1:将接口的 [ApiExplorerSettings(GroupName = "xxx")] 特性匹配
|
|
|
|
|
|
if (!apiDesc.TryGetMethodInfo(out var methodInfo)) return false;
|
|
|
|
|
|
var groupName = methodInfo.DeclaringType?
|
|
|
|
|
|
.GetCustomAttributes(true)
|
|
|
|
|
|
.OfType<ApiExplorerSettingsAttribute>()
|
|
|
|
|
|
.FirstOrDefault()?
|
|
|
|
|
|
.GroupName;
|
|
|
|
|
|
|
|
|
|
|
|
// 匹配当前文档(分组)则显示
|
|
|
|
|
|
return groupName == docName;
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-02 17:58:10 +08:00
|
|
|
|
builder.Services.AddInfrastructureServices(builder.Configuration, builder.Environment);
|
2026-06-01 13:42:40 +08:00
|
|
|
|
|
2026-06-02 14:10:43 +08:00
|
|
|
|
//注册 HttpContextAccessor
|
|
|
|
|
|
builder.Services.AddHttpContextAccessor();
|
|
|
|
|
|
builder.Services.AddScoped(typeof(BaseRepository<>));
|
2026-06-01 17:59:23 +08:00
|
|
|
|
|
2026-06-09 11:41:28 +08:00
|
|
|
|
// 注册勋章规则配置
|
|
|
|
|
|
builder.Services.Configure<MedalRuleConfigSettings>(builder.Configuration.GetSection("MedalRuleConfig"));
|
|
|
|
|
|
|
2026-06-01 13:42:40 +08:00
|
|
|
|
// 添加CORS
|
|
|
|
|
|
builder.Services.AddCors(options =>
|
|
|
|
|
|
{
|
|
|
|
|
|
options.AddPolicy("AllowAll", policy =>
|
|
|
|
|
|
{
|
|
|
|
|
|
policy.AllowAnyOrigin()
|
|
|
|
|
|
.AllowAnyMethod()
|
|
|
|
|
|
.AllowAnyHeader();
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2026-06-09 14:36:46 +08:00
|
|
|
|
|
2026-06-02 17:58:10 +08:00
|
|
|
|
#region 配置数据压缩选项
|
2026-06-01 13:42:40 +08:00
|
|
|
|
|
2026-06-02 17:58:10 +08:00
|
|
|
|
// 1.首先配置压缩选项的Options>
|
|
|
|
|
|
builder.Services.Configure<BrotliCompressionProviderOptions>(options =>
|
|
|
|
|
|
{
|
|
|
|
|
|
options.Level = System.IO.Compression.CompressionLevel.Optimal;
|
|
|
|
|
|
});
|
|
|
|
|
|
builder.Services.Configure<GzipCompressionProviderOptions>(options =>
|
|
|
|
|
|
{
|
|
|
|
|
|
options.Level = System.IO.Compression.CompressionLevel.Fastest;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 2.在服务容器中注册响应压缩服务
|
|
|
|
|
|
builder.Services.AddResponseCompression(options =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// 可以在这里进行详细配置
|
|
|
|
|
|
options.EnableForHttps = true; // 启用对HTTPS响应的压缩(请注意安全风险)
|
|
|
|
|
|
options.Providers.Add<BrotliCompressionProvider>();
|
|
|
|
|
|
options.Providers.Add<GzipCompressionProvider>();
|
|
|
|
|
|
//指定哪些类型的响应应该被压缩。默认列表包含常见的文本类类型,如 text/html, text/css, application/javascript, application/json, text/plain等
|
|
|
|
|
|
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(["image/svg+xml", "application/json", "text/plain"]);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
2026-06-09 14:36:46 +08:00
|
|
|
|
|
2026-06-01 13:42:40 +08:00
|
|
|
|
var app = builder.Build();
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
app.UseSwagger();
|
2026-06-01 17:59:23 +08:00
|
|
|
|
app.UseSwaggerUI(c =>
|
|
|
|
|
|
{
|
|
|
|
|
|
// 根据版本名称倒序 遍历展示
|
|
|
|
|
|
Enum.GetValues<ApiVersionEnum>().OrderBy(e => e).ToList().ForEach(version =>
|
|
|
|
|
|
{
|
|
|
|
|
|
c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{version.GetDescription()}接口");
|
|
|
|
|
|
});
|
|
|
|
|
|
c.DocExpansion(DocExpansion.None); // ->修改界面打开时自动折叠
|
|
|
|
|
|
});
|
2026-06-01 13:42:40 +08:00
|
|
|
|
}
|
2026-06-02 17:58:10 +08:00
|
|
|
|
|
2026-06-02 14:10:43 +08:00
|
|
|
|
app.UseServiceContext();
|
2026-06-01 13:42:40 +08:00
|
|
|
|
app.UseHttpsRedirection();
|
|
|
|
|
|
app.UseCors("AllowAll");
|
|
|
|
|
|
app.UseMiddleware<GlobalExceptionMiddleware>();
|
|
|
|
|
|
app.UseMiddleware<OperationLogMiddleware>();
|
2026-06-01 17:59:23 +08:00
|
|
|
|
app.UseMiddleware<JwtAutoRefreshMiddleware>();
|
2026-06-01 13:42:40 +08:00
|
|
|
|
app.UseAuthentication();
|
|
|
|
|
|
app.UseAuthorization();
|
|
|
|
|
|
app.MapControllers();
|
|
|
|
|
|
|
|
|
|
|
|
app.Run();
|
2026-06-01 17:59:23 +08:00
|
|
|
|
|