更新nuget&&添加swagger
This commit is contained in:
@@ -79,6 +79,38 @@ public sealed class OperationsController(
|
||||
CancellationToken cancellationToken) =>
|
||||
Ok(await healthService.CheckAsync(cancellationToken));
|
||||
|
||||
[HttpGet("swagger")]
|
||||
public async Task<ActionResult<SwaggerDocumentationSettings>> GetSwaggerSettings(
|
||||
CancellationToken cancellationToken) =>
|
||||
Ok(new SwaggerDocumentationSettings(await IsSwaggerEnabledAsync(cancellationToken)));
|
||||
|
||||
[HttpPut("swagger")]
|
||||
public async Task<ActionResult<SwaggerDocumentationSettings>> UpdateSwaggerSettings(
|
||||
UpdateSwaggerDocumentationSettings request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var setting = await db.SystemFeatureSettings.SingleOrDefaultAsync(
|
||||
x => x.Key == SystemFeatureKeys.SwaggerDocumentation,
|
||||
cancellationToken);
|
||||
if (setting is null)
|
||||
{
|
||||
setting = new SystemFeatureSetting
|
||||
{
|
||||
Key = SystemFeatureKeys.SwaggerDocumentation,
|
||||
IsEnabled = request.IsEnabled
|
||||
};
|
||||
db.SystemFeatureSettings.Add(setting);
|
||||
}
|
||||
else
|
||||
{
|
||||
setting.IsEnabled = request.IsEnabled;
|
||||
setting.UpdatedAt = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
await db.SaveChangesAsync(cancellationToken);
|
||||
return Ok(new SwaggerDocumentationSettings(setting.IsEnabled));
|
||||
}
|
||||
|
||||
[HttpGet("audit-logs")]
|
||||
public async Task<ActionResult<PagedResult<AuditLogItem>>> GetAuditLogs(
|
||||
[FromQuery] int page = 1,
|
||||
@@ -519,9 +551,15 @@ public sealed class OperationsController(
|
||||
cancellationToken) +
|
||||
await db.ExamPublishJobs.AsNoTracking()
|
||||
.CountAsync(
|
||||
x => x.Status == ExamPublishJobStatus.Failed &&
|
||||
x.CreatedAt >= from,
|
||||
cancellationToken);
|
||||
x => x.Status == ExamPublishJobStatus.Failed &&
|
||||
x.CreatedAt >= from,
|
||||
cancellationToken);
|
||||
|
||||
private async Task<bool> IsSwaggerEnabledAsync(CancellationToken cancellationToken) =>
|
||||
await db.SystemFeatureSettings.AsNoTracking()
|
||||
.Where(x => x.Key == SystemFeatureKeys.SwaggerDocumentation)
|
||||
.Select(x => (bool?)x.IsEnabled)
|
||||
.SingleOrDefaultAsync(cancellationToken) ?? false;
|
||||
|
||||
private ActionResult? ValidatePaging(int page, int pageSize)
|
||||
{
|
||||
@@ -604,3 +642,7 @@ public sealed record OperationsSummary(
|
||||
public sealed record CreateBackupRequest([MaxLength(200)] string? Note);
|
||||
|
||||
public sealed record RestoreDrillRequest([Required] string Confirmation);
|
||||
|
||||
public sealed record SwaggerDocumentationSettings(bool IsEnabled);
|
||||
|
||||
public sealed record UpdateSwaggerDocumentationSettings(bool IsEnabled);
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
using Jiaowu.Api.Domain.Common;
|
||||
|
||||
namespace Jiaowu.Api.Domain.System;
|
||||
|
||||
public static class SystemFeatureKeys
|
||||
{
|
||||
public const string SwaggerDocumentation = "SwaggerDocumentation";
|
||||
}
|
||||
|
||||
public sealed class SystemFeatureSetting : EntityBase
|
||||
{
|
||||
public required string Key { get; set; }
|
||||
public bool IsEnabled { get; set; }
|
||||
}
|
||||
@@ -137,6 +137,8 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
||||
Set<BackgroundJobOutboxMessage>();
|
||||
public DbSet<AppUpdateRelease> AppUpdateReleases =>
|
||||
Set<AppUpdateRelease>();
|
||||
public DbSet<SystemFeatureSetting> SystemFeatureSettings =>
|
||||
Set<SystemFeatureSetting>();
|
||||
public DbSet<RefreshSession> RefreshSessions => Set<RefreshSession>();
|
||||
|
||||
protected override void ConfigureConventions(
|
||||
@@ -1451,6 +1453,12 @@ public sealed class AppDbContext(DbContextOptions<AppDbContext> options)
|
||||
entity.HasIndex(x => x.CreatedAt);
|
||||
});
|
||||
|
||||
builder.Entity<SystemFeatureSetting>(entity =>
|
||||
{
|
||||
entity.Property(x => x.Key).HasMaxLength(100);
|
||||
entity.HasIndex(x => x.Key).IsUnique();
|
||||
});
|
||||
|
||||
builder.Entity<OfficialDocument>(entity =>
|
||||
{
|
||||
entity.Property(x => x.DocumentNumber).HasMaxLength(50);
|
||||
|
||||
@@ -90,6 +90,8 @@ public sealed class DevelopmentSqliteMigrator(
|
||||
"20260809_47_course_grade_distribution";
|
||||
private const string TeachingTaskGradeAnalyticsMigration =
|
||||
"20260809_48_teaching_task_grade_analytics";
|
||||
private const string SwaggerDocumentationSettingMigration =
|
||||
"20260809_49_swagger_documentation_setting";
|
||||
|
||||
public async Task MigrateAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -669,6 +671,14 @@ public sealed class DevelopmentSqliteMigrator(
|
||||
TeachingTaskGradeAnalyticsMigration,
|
||||
TeachingTaskGradeAnalyticsStatements,
|
||||
cancellationToken);
|
||||
var swaggerSettingsExist = await db.Database
|
||||
.SqlQueryRaw<int>(
|
||||
"SELECT COUNT(*) AS \"Value\" FROM sqlite_master WHERE type = 'table' AND name = 'SystemFeatureSettings'")
|
||||
.AnyAsync(value => value > 0, cancellationToken);
|
||||
await ApplyMigrationAsync(
|
||||
SwaggerDocumentationSettingMigration,
|
||||
swaggerSettingsExist ? [] : SwaggerDocumentationSettingStatements,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
private async Task ApplyMigrationAsync(
|
||||
@@ -2913,4 +2923,21 @@ public sealed class DevelopmentSqliteMigrator(
|
||||
ADD COLUMN "Kind" INTEGER NOT NULL DEFAULT 1;
|
||||
"""
|
||||
];
|
||||
|
||||
private static readonly string[] SwaggerDocumentationSettingStatements =
|
||||
[
|
||||
"""
|
||||
CREATE TABLE "SystemFeatureSettings" (
|
||||
"Id" TEXT NOT NULL CONSTRAINT "PK_SystemFeatureSettings" PRIMARY KEY,
|
||||
"Key" TEXT NOT NULL,
|
||||
"IsEnabled" INTEGER NOT NULL,
|
||||
"CreatedAt" TEXT NOT NULL,
|
||||
"UpdatedAt" TEXT NOT NULL
|
||||
);
|
||||
""",
|
||||
"""
|
||||
CREATE UNIQUE INDEX "IX_SystemFeatureSettings_Key"
|
||||
ON "SystemFeatureSettings" ("Key");
|
||||
"""
|
||||
];
|
||||
}
|
||||
|
||||
+6595
File diff suppressed because it is too large
Load Diff
+44
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class SwaggerDocumentationSetting : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "SystemFeatureSettings",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<Guid>(type: "char(36)", nullable: false),
|
||||
Key = table.Column<string>(type: "varchar(100)", maxLength: 100, nullable: false),
|
||||
IsEnabled = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||
CreatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false),
|
||||
UpdatedAt = table.Column<DateTime>(type: "datetime(6)", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_SystemFeatureSettings", x => x.Id);
|
||||
})
|
||||
.Annotation("MySQL:Charset", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_SystemFeatureSettings_Key",
|
||||
table: "SystemFeatureSettings",
|
||||
column: "Key",
|
||||
unique: true);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "SystemFeatureSettings");
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
@@ -4767,6 +4767,34 @@ namespace Jiaowu.Api.Infrastructure.Persistence.Migrations.MySql
|
||||
b.ToTable("BackgroundJobOutboxMessages");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Jiaowu.Api.Domain.System.SystemFeatureSetting", b =>
|
||||
{
|
||||
b.Property<Guid>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
b.Property<DateTime>("CreatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<bool>("IsEnabled")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
b.Property<string>("Key")
|
||||
.IsRequired()
|
||||
.HasMaxLength(100)
|
||||
.HasColumnType("varchar(100)");
|
||||
|
||||
b.Property<DateTime>("UpdatedAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Key")
|
||||
.IsUnique();
|
||||
|
||||
b.ToTable("SystemFeatureSettings");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<System.Guid>", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<Import Project="..\..\versions.props" />
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Version>2.3.2-beta.1</Version>
|
||||
<Version>$(JiaowuBackendVersion)</Version>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<SpaRoot>$([System.IO.Path]::GetFullPath('$(MSBuildProjectDirectory)/../../web'))</SpaRoot>
|
||||
@@ -18,15 +20,26 @@
|
||||
Include="..\..\.env.example"
|
||||
Link=".env.example"
|
||||
CopyToPublishDirectory="PreserveNewest" />
|
||||
<Content
|
||||
Include="..\..\versions.props"
|
||||
Link="versions.props"
|
||||
CopyToOutputDirectory="PreserveNewest"
|
||||
CopyToPublishDirectory="PreserveNewest" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ClosedXML" Version="0.105.0" />
|
||||
<PackageReference Include="DocumentFormat.OpenXml" Version="3.1.1" />
|
||||
<AssemblyMetadata
|
||||
Include="SwaggerDocumentVersion"
|
||||
Value="$(JiaowuSwaggerVersion)" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ClosedXML" Version="0.105.1" />
|
||||
<PackageReference Include="DocumentFormat.OpenXml" Version="3.5.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="10.1.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" Version="10.8.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.10">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -40,11 +53,11 @@
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.17.0" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.17.0" />
|
||||
<PackageReference Include="QRCoder" Version="1.8.0" />
|
||||
<PackageReference Include="RabbitMQ.Client" Version="7.2.1" />
|
||||
<PackageReference Include="SkiaSharp" Version="3.119.2" />
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="3.119.2" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.4" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
<PackageReference Include="RabbitMQ.Client" Version="7.2.2" />
|
||||
<PackageReference Include="SkiaSharp" Version="4.151.1" />
|
||||
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="4.151.1" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="3.0.5" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.2.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<Target
|
||||
|
||||
+44
-16
@@ -1,6 +1,7 @@
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using Jiaowu.Api.Domain.Identity;
|
||||
using Jiaowu.Api.Domain.System;
|
||||
using Jiaowu.Api.Infrastructure.BackgroundJobs;
|
||||
using Jiaowu.Api.Infrastructure.Grades;
|
||||
using Jiaowu.Api.Infrastructure.Configuration;
|
||||
@@ -23,10 +24,11 @@ using Microsoft.Data.Sqlite;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Microsoft.OpenApi;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Resources;
|
||||
using OpenTelemetry.Trace;
|
||||
using Swashbuckle.AspNetCore.SwaggerUI;
|
||||
using System.Threading.RateLimiting;
|
||||
|
||||
EnvironmentFile.Load();
|
||||
@@ -62,6 +64,13 @@ if (confirmProductionDemoData && !seedDemoData)
|
||||
}
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
var swaggerDocumentVersion = typeof(Program).Assembly
|
||||
.GetCustomAttributes(
|
||||
typeof(System.Reflection.AssemblyMetadataAttribute),
|
||||
inherit: false)
|
||||
.OfType<System.Reflection.AssemblyMetadataAttribute>()
|
||||
.SingleOrDefault(x => x.Key == "SwaggerDocumentVersion")?.Value
|
||||
?? "v1";
|
||||
|
||||
if (seedDemoData && builder.Environment.IsDevelopment())
|
||||
{
|
||||
@@ -608,10 +617,10 @@ builder.Services.AddControllers()
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(options =>
|
||||
{
|
||||
options.SwaggerDoc("v1", new OpenApiInfo
|
||||
options.SwaggerDoc(swaggerDocumentVersion, new OpenApiInfo
|
||||
{
|
||||
Title = "大学教务管理系统 API",
|
||||
Version = "v1"
|
||||
Version = swaggerDocumentVersion
|
||||
});
|
||||
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
|
||||
{
|
||||
@@ -621,17 +630,10 @@ builder.Services.AddSwaggerGen(options =>
|
||||
BearerFormat = "JWT",
|
||||
In = ParameterLocation.Header
|
||||
});
|
||||
options.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||
options.AddSecurityRequirement(_ => new OpenApiSecurityRequirement
|
||||
{
|
||||
[
|
||||
new OpenApiSecurityScheme
|
||||
{
|
||||
Reference = new OpenApiReference
|
||||
{
|
||||
Type = ReferenceType.SecurityScheme,
|
||||
Id = "Bearer"
|
||||
}
|
||||
}
|
||||
new OpenApiSecuritySchemeReference("Bearer", null, null)
|
||||
] = []
|
||||
});
|
||||
});
|
||||
@@ -640,11 +642,37 @@ var app = builder.Build();
|
||||
|
||||
app.UseExceptionHandler();
|
||||
app.UseResponseCompression();
|
||||
if (app.Environment.IsDevelopment())
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI();
|
||||
}
|
||||
if (context.Request.Path.StartsWithSegments("/swagger"))
|
||||
{
|
||||
var isEnabled = await context.RequestServices
|
||||
.GetRequiredService<AppDbContext>()
|
||||
.SystemFeatureSettings
|
||||
.AsNoTracking()
|
||||
.Where(x => x.Key == SystemFeatureKeys.SwaggerDocumentation)
|
||||
.Select(x => (bool?)x.IsEnabled)
|
||||
.SingleOrDefaultAsync(context.RequestAborted) ?? false;
|
||||
if (!isEnabled)
|
||||
{
|
||||
context.Response.StatusCode = StatusCodes.Status404NotFound;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await next();
|
||||
});
|
||||
app.UseSwagger();
|
||||
app.UseSwaggerUI(options =>
|
||||
{
|
||||
options.SwaggerEndpoint(
|
||||
$"/swagger/{swaggerDocumentVersion}/swagger.json",
|
||||
$"大学教务管理系统 API {swaggerDocumentVersion}");
|
||||
options.DocExpansion(DocExpansion.None);
|
||||
options.DefaultModelsExpandDepth(-1);
|
||||
options.DefaultModelExpandDepth(1);
|
||||
options.EnableFilter();
|
||||
});
|
||||
|
||||
app.UseDefaultFiles();
|
||||
app.UseStaticFiles(new StaticFileOptions
|
||||
|
||||
Reference in New Issue
Block a user