Compare commits
16
Commits
a16c32b25e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
809ca92464 | ||
|
|
6bdcb48c97 | ||
|
|
87f5c1ffb8 | ||
|
|
60878f9b6e | ||
|
|
8c92d2fbac | ||
|
|
8d9c7f17ff | ||
|
|
c6b05c12e5 | ||
|
|
27e4029f4a | ||
|
|
6ef410fdfa | ||
|
|
ff19f4759f | ||
|
|
2c20f9bb54 | ||
|
|
6acc92ca27 | ||
|
|
d93098638d | ||
|
|
9f8da2c063 | ||
|
|
8270cf198b | ||
|
|
d84bbb3a18 |
+27
-28
@@ -2,36 +2,35 @@
|
|||||||
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
|
# 此 .gitignore 文件已由 Microsoft(R) Visual Studio 自动创建。
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
/Avalonia-PC/bin
|
/FileShare-PC/bin
|
||||||
/Avalonia-PC/.vs
|
/FileShare-PC/.vs
|
||||||
/Avalonia-PC/obj
|
/FileShare-PC/obj
|
||||||
/Avalonia-API/bin
|
/FileShare-API/bin
|
||||||
/Avalonia-API/obj
|
/FileShare-API/obj
|
||||||
/Avalonia-Services/bin
|
/FileShare-Services/bin
|
||||||
/Avalonia-Services/obj
|
/FileShare-Services/obj
|
||||||
/Avalonia-Web-VUE/.vscode
|
/FileShare-Web-VUE/.vscode
|
||||||
/Avalonia-Web-VUE/obj
|
/FileShare-Web-VUE/obj
|
||||||
/Avalonia-Web-VUE/node_modules
|
/FileShare-Web-VUE/node_modules
|
||||||
/Avalonia-Web-VUE/dist
|
/FileShare-Web-VUE/dist
|
||||||
/Avalonia-Web-VUE/.vscode
|
/FileShare-Web-VUE/.vscode
|
||||||
/avalonia-web-react/obj
|
/FileShare-EFCore/bin
|
||||||
/avalonia-web-react/obj
|
/FileShare-EFCore/obj
|
||||||
/avalonia-web-react/node_modules
|
/FileShare-Common/bin
|
||||||
/avalonia-web-react/dist
|
/FileShare-Common/obj
|
||||||
/Avalonia-EFCore/bin
|
/FileShare-API/logs
|
||||||
/Avalonia-EFCore/obj
|
/FileShare-API/fileshare-api.db
|
||||||
/Avalonia-Common/bin
|
/FileShare-API/fileshare-api.db-shm
|
||||||
/Avalonia-Common/obj
|
/FileShare-API/fileshare-api.db-wal
|
||||||
/Avalonia-API/logs
|
/FileShare-API/app.db
|
||||||
/Avalonia-API/avalonia-api.db
|
/FileShare-API/app.db-shm
|
||||||
/Avalonia-API/avalonia-api.db-shm
|
/FileShare-API/app.db-wal
|
||||||
/Avalonia-API/avalonia-api.db-wal
|
/FileShare-API/wwwroot
|
||||||
/Avalonia-API/app.db
|
|
||||||
/Avalonia-API/app.db-shm
|
|
||||||
/Avalonia-API/app.db-wal
|
|
||||||
/Avalonia-API/wwwroot
|
|
||||||
/package-output
|
/package-output
|
||||||
/package-scripts/tools
|
/package-scripts/tools
|
||||||
/.vs
|
/.vs
|
||||||
/bin
|
/bin
|
||||||
/obj
|
/obj
|
||||||
|
/.claude
|
||||||
|
/.codex-build
|
||||||
|
/FileShare-API/Properties
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# Codex Rules
|
||||||
|
|
||||||
|
- Do not manually edit files under `FileShare-EFCore/Migrations/**`. These files are generated by EF scripts.
|
||||||
|
- When database schema changes are needed, update the EF model/configuration only and ask the user to generate migrations with the project scripts.
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<RootNamespace>Avalonia_API</RootNamespace>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.5" />
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.5" />
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.4" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\Avalonia-Services\Avalonia-Services.csproj" />
|
|
||||||
<ProjectReference Include="..\Avalonia-Common\Avalonia-Common.csproj" />
|
|
||||||
<ProjectReference Include="..\Avalonia-EFCore\Avalonia-EFCore.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Controllers\" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<Target Name="RestoreFrontendPackages" BeforeTargets="Build" Condition="'$(SkipFrontendBuild)' != 'true' And Exists('..\Avalonia-Web-VUE\package.json') And !Exists('..\Avalonia-Web-VUE\node_modules')">
|
|
||||||
<Message Importance="high" Text="Restoring Avalonia-Web-VUE npm packages..." />
|
|
||||||
<Exec WorkingDirectory="..\Avalonia-Web-VUE" Command="npm.cmd install" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
<Target Name="BuildFrontend" BeforeTargets="Build" DependsOnTargets="RestoreFrontendPackages" Condition="'$(SkipFrontendBuild)' != 'true' And Exists('..\Avalonia-Web-VUE\package.json')">
|
|
||||||
<Message Importance="high" Text="Building Avalonia-Web-VUE into Avalonia-API/wwwroot..." />
|
|
||||||
<Exec WorkingDirectory="..\Avalonia-Web-VUE" Command="npm.cmd run build-only" />
|
|
||||||
<RemoveDir Directories="wwwroot" />
|
|
||||||
<MakeDir Directories="wwwroot" />
|
|
||||||
<ItemGroup>
|
|
||||||
<FrontendDist Include="..\Avalonia-Web-VUE\dist\**\*.*" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Copy
|
|
||||||
SourceFiles="@(FrontendDist)"
|
|
||||||
DestinationFiles="@(FrontendDist->'wwwroot\%(RecursiveDir)%(Filename)%(Extension)')"
|
|
||||||
SkipUnchangedFiles="false" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
@Avalonia_API_HostAddress = http://localhost:5206
|
|
||||||
|
|
||||||
GET {{Avalonia_API_HostAddress}}/weatherforecast/
|
|
||||||
Accept: application/json
|
|
||||||
|
|
||||||
###
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
using Avalonia_EFCore.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Avalonia_API.Extensions
|
|
||||||
{
|
|
||||||
public static class FileStreamEndpointExtensions
|
|
||||||
{
|
|
||||||
public static IEndpointRouteBuilder MapFileStreamEndpoints(this IEndpointRouteBuilder app)
|
|
||||||
{
|
|
||||||
app.MapMethods("/api/files/{id:int}/stream", ["GET", "HEAD"], async (int id, AppDataContext db, HttpContext httpContext) =>
|
|
||||||
{
|
|
||||||
// Browsers cancel in-flight range requests aggressively while seeking.
|
|
||||||
// Keep this small metadata lookup independent from RequestAborted so
|
|
||||||
// EF does not throw TaskCanceledException before the file is opened.
|
|
||||||
var file = await db.ManagedFileRecords
|
|
||||||
.AsNoTracking()
|
|
||||||
.Include(item => item.LibraryRoot)
|
|
||||||
.FirstOrDefaultAsync(item =>
|
|
||||||
item.Id == id
|
|
||||||
&& item.Exists
|
|
||||||
&& item.LibraryRoot != null
|
|
||||||
&& item.LibraryRoot.IsAvailable);
|
|
||||||
|
|
||||||
if (file is null || !System.IO.File.Exists(file.AbsolutePath))
|
|
||||||
{
|
|
||||||
return Results.NotFound();
|
|
||||||
}
|
|
||||||
|
|
||||||
var stream = System.IO.File.Open(file.AbsolutePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
|
||||||
httpContext.Response.Headers.ContentDisposition = $"inline; filename=\"{Uri.EscapeDataString(file.FileName)}\"";
|
|
||||||
httpContext.Response.Headers.AcceptRanges = "bytes";
|
|
||||||
httpContext.Response.Headers.CacheControl = "public, max-age=3600";
|
|
||||||
|
|
||||||
return Results.File(
|
|
||||||
stream,
|
|
||||||
contentType: file.ContentType,
|
|
||||||
fileDownloadName: null,
|
|
||||||
lastModified: file.LastWriteTimeUtc,
|
|
||||||
enableRangeProcessing: true);
|
|
||||||
})
|
|
||||||
.WithName("StreamManagedFile")
|
|
||||||
.WithTags("FileLibrary");
|
|
||||||
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
using Avalonia_Services.Services.FileLibrary;
|
|
||||||
|
|
||||||
namespace Avalonia_API.Services
|
|
||||||
{
|
|
||||||
public sealed class FileLibraryScanHostedService(IServiceScopeFactory scopeFactory, ILogger<FileLibraryScanHostedService> logger)
|
|
||||||
: BackgroundService
|
|
||||||
{
|
|
||||||
private static readonly TimeSpan Interval = TimeSpan.FromMinutes(1);
|
|
||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
|
||||||
{
|
|
||||||
await ScanAsync(stoppingToken);
|
|
||||||
|
|
||||||
using var timer = new PeriodicTimer(Interval);
|
|
||||||
while (await timer.WaitForNextTickAsync(stoppingToken))
|
|
||||||
{
|
|
||||||
await ScanAsync(stoppingToken);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ScanAsync(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await using var scope = scopeFactory.CreateAsyncScope();
|
|
||||||
var scanner = scope.ServiceProvider.GetRequiredService<IFileLibraryService>();
|
|
||||||
await scanner.ScanDueRootsAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
logger.LogWarning(ex, "文件库定时扫描失败。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,178 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Avalonia_EFCore.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.MySQL
|
|
||||||
{
|
|
||||||
[DbContext(typeof(MySqlAppDataContext))]
|
|
||||||
partial class MySqlAppDataContextModelSnapshot : ModelSnapshot
|
|
||||||
{
|
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("created-at");
|
|
||||||
|
|
||||||
b.Property<string>("Device")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)")
|
|
||||||
.HasColumnName("device");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("expires-at");
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("varchar(64)")
|
|
||||||
.HasColumnName("ip-address");
|
|
||||||
|
|
||||||
b.Property<string>("ReplacedByTokenHash")
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("varchar(128)")
|
|
||||||
.HasColumnName("replaced-by-token-hash");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("RevokedAt")
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("revoked-at");
|
|
||||||
|
|
||||||
b.Property<string>("TokenHash")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("varchar(128)")
|
|
||||||
.HasColumnName("token-hash");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasColumnName("user-id");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk-api-refresh-token");
|
|
||||||
|
|
||||||
b.HasIndex("TokenHash")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
|
||||||
|
|
||||||
b.HasIndex("UserId")
|
|
||||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
|
||||||
|
|
||||||
b.ToTable("api-refresh-token", t =>
|
|
||||||
{
|
|
||||||
t.HasComment("API refresh token");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasColumnName("id")
|
|
||||||
.HasComment("用户主键");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("created-at")
|
|
||||||
.HasComment("创建时间");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)")
|
|
||||||
.HasColumnName("email")
|
|
||||||
.HasComment("用户邮箱");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("varchar(100)")
|
|
||||||
.HasColumnName("name")
|
|
||||||
.HasComment("用户名称");
|
|
||||||
|
|
||||||
b.Property<string>("PasswordHash")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)")
|
|
||||||
.HasColumnName("password-hash")
|
|
||||||
.HasComment("密码哈希值");
|
|
||||||
|
|
||||||
b.Property<string>("PhoneNumber")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("varchar(50)")
|
|
||||||
.HasColumnName("phone-number")
|
|
||||||
.HasComment("电话号码");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("updated-at")
|
|
||||||
.HasComment("更新时间");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk-user");
|
|
||||||
|
|
||||||
b.ToTable("user", t =>
|
|
||||||
{
|
|
||||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasColumnName("id")
|
|
||||||
.HasComment("天气预报主键");
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("created-at")
|
|
||||||
.HasComment("创建时间");
|
|
||||||
|
|
||||||
b.Property<DateOnly>("Date")
|
|
||||||
.HasColumnType("date")
|
|
||||||
.HasColumnName("date")
|
|
||||||
.HasComment("预报日期");
|
|
||||||
|
|
||||||
b.Property<string>("Summary")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("varchar(200)")
|
|
||||||
.HasColumnName("summary")
|
|
||||||
.HasComment("天气摘要");
|
|
||||||
|
|
||||||
b.Property<int>("TemperatureC")
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasColumnName("temperature-c")
|
|
||||||
.HasComment("摄氏温度");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime(6)")
|
|
||||||
.HasColumnName("updated-at")
|
|
||||||
.HasComment("更新时间");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk-weather-forecast");
|
|
||||||
|
|
||||||
b.ToTable("weather-forecast", t =>
|
|
||||||
{
|
|
||||||
t.HasComment("天气预报数据实体");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Avalonia_EFCore.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
|
||||||
{
|
|
||||||
[DbContext(typeof(PostgreSqlAppDataContext))]
|
|
||||||
partial class PostgreSqlAppDataContextModelSnapshot : ModelSnapshot
|
|
||||||
{
|
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("created-at");
|
|
||||||
|
|
||||||
b.Property<string>("Device")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("character varying(200)")
|
|
||||||
.HasColumnName("device");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("expires-at");
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("character varying(64)")
|
|
||||||
.HasColumnName("ip-address");
|
|
||||||
|
|
||||||
b.Property<string>("ReplacedByTokenHash")
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("character varying(128)")
|
|
||||||
.HasColumnName("replaced-by-token-hash");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("RevokedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("revoked-at");
|
|
||||||
|
|
||||||
b.Property<string>("TokenHash")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("character varying(128)")
|
|
||||||
.HasColumnName("token-hash");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("user-id");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk-api-refresh-token");
|
|
||||||
|
|
||||||
b.HasIndex("TokenHash")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
|
||||||
|
|
||||||
b.HasIndex("UserId")
|
|
||||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
|
||||||
|
|
||||||
b.ToTable("api-refresh-token", t =>
|
|
||||||
{
|
|
||||||
t.HasComment("API refresh token");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("id")
|
|
||||||
.HasComment("用户主键");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("created-at")
|
|
||||||
.HasComment("创建时间");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("character varying(200)")
|
|
||||||
.HasColumnName("email")
|
|
||||||
.HasComment("用户邮箱");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("character varying(100)")
|
|
||||||
.HasColumnName("name")
|
|
||||||
.HasComment("用户名称");
|
|
||||||
|
|
||||||
b.Property<string>("PasswordHash")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("character varying(200)")
|
|
||||||
.HasColumnName("password-hash")
|
|
||||||
.HasComment("密码哈希值");
|
|
||||||
|
|
||||||
b.Property<string>("PhoneNumber")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("character varying(50)")
|
|
||||||
.HasColumnName("phone-number")
|
|
||||||
.HasComment("电话号码");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("updated-at")
|
|
||||||
.HasComment("更新时间");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk-user");
|
|
||||||
|
|
||||||
b.ToTable("user", t =>
|
|
||||||
{
|
|
||||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("id")
|
|
||||||
.HasComment("天气预报主键");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("created-at")
|
|
||||||
.HasComment("创建时间");
|
|
||||||
|
|
||||||
b.Property<DateOnly>("Date")
|
|
||||||
.HasColumnType("date")
|
|
||||||
.HasColumnName("date")
|
|
||||||
.HasComment("预报日期");
|
|
||||||
|
|
||||||
b.Property<string>("Summary")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("character varying(200)")
|
|
||||||
.HasColumnName("summary")
|
|
||||||
.HasComment("天气摘要");
|
|
||||||
|
|
||||||
b.Property<int>("TemperatureC")
|
|
||||||
.HasColumnType("integer")
|
|
||||||
.HasColumnName("temperature-c")
|
|
||||||
.HasComment("摄氏温度");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone")
|
|
||||||
.HasColumnName("updated-at")
|
|
||||||
.HasComment("更新时间");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk-weather-forecast");
|
|
||||||
|
|
||||||
b.ToTable("weather-forecast", t =>
|
|
||||||
{
|
|
||||||
t.HasComment("天气预报数据实体");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Avalonia_EFCore.Database;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
|
||||||
{
|
|
||||||
[DbContext(typeof(SqlServerAppDataContext))]
|
|
||||||
partial class SqlServerAppDataContextModelSnapshot : ModelSnapshot
|
|
||||||
{
|
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
|
||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<long>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("bigint")
|
|
||||||
.HasColumnName("id");
|
|
||||||
|
|
||||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("created-at");
|
|
||||||
|
|
||||||
b.Property<string>("Device")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("nvarchar(200)")
|
|
||||||
.HasColumnName("device");
|
|
||||||
|
|
||||||
b.Property<DateTime>("ExpiresAt")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("expires-at");
|
|
||||||
|
|
||||||
b.Property<string>("IpAddress")
|
|
||||||
.HasMaxLength(64)
|
|
||||||
.HasColumnType("nvarchar(64)")
|
|
||||||
.HasColumnName("ip-address");
|
|
||||||
|
|
||||||
b.Property<string>("ReplacedByTokenHash")
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)")
|
|
||||||
.HasColumnName("replaced-by-token-hash");
|
|
||||||
|
|
||||||
b.Property<DateTime?>("RevokedAt")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("revoked-at");
|
|
||||||
|
|
||||||
b.Property<string>("TokenHash")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(128)
|
|
||||||
.HasColumnType("nvarchar(128)")
|
|
||||||
.HasColumnName("token-hash");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasColumnName("user-id");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk-api-refresh-token");
|
|
||||||
|
|
||||||
b.HasIndex("TokenHash")
|
|
||||||
.IsUnique()
|
|
||||||
.HasDatabaseName("idx-api-refresh-token-hash");
|
|
||||||
|
|
||||||
b.HasIndex("UserId")
|
|
||||||
.HasDatabaseName("idx-api-refresh-token-user-id");
|
|
||||||
|
|
||||||
b.ToTable("api-refresh-token", t =>
|
|
||||||
{
|
|
||||||
t.HasComment("API refresh token");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasColumnName("id")
|
|
||||||
.HasComment("用户主键");
|
|
||||||
|
|
||||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("created-at")
|
|
||||||
.HasComment("创建时间");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("nvarchar(200)")
|
|
||||||
.HasColumnName("email")
|
|
||||||
.HasComment("用户邮箱");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("nvarchar(100)")
|
|
||||||
.HasColumnName("name")
|
|
||||||
.HasComment("用户名称");
|
|
||||||
|
|
||||||
b.Property<string>("PasswordHash")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("nvarchar(200)")
|
|
||||||
.HasColumnName("password-hash")
|
|
||||||
.HasComment("密码哈希值");
|
|
||||||
|
|
||||||
b.Property<string>("PhoneNumber")
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("nvarchar(50)")
|
|
||||||
.HasColumnName("phone-number")
|
|
||||||
.HasComment("电话号码");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("updated-at")
|
|
||||||
.HasComment("更新时间");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk-user");
|
|
||||||
|
|
||||||
b.ToTable("user", t =>
|
|
||||||
{
|
|
||||||
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasColumnName("id")
|
|
||||||
.HasComment("天气预报主键");
|
|
||||||
|
|
||||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTime>("CreatedAt")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("created-at")
|
|
||||||
.HasComment("创建时间");
|
|
||||||
|
|
||||||
b.Property<DateOnly>("Date")
|
|
||||||
.HasColumnType("date")
|
|
||||||
.HasColumnName("date")
|
|
||||||
.HasComment("预报日期");
|
|
||||||
|
|
||||||
b.Property<string>("Summary")
|
|
||||||
.HasMaxLength(200)
|
|
||||||
.HasColumnType("nvarchar(200)")
|
|
||||||
.HasColumnName("summary")
|
|
||||||
.HasComment("天气摘要");
|
|
||||||
|
|
||||||
b.Property<int>("TemperatureC")
|
|
||||||
.HasColumnType("int")
|
|
||||||
.HasColumnName("temperature-c")
|
|
||||||
.HasComment("摄氏温度");
|
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
|
||||||
.HasColumnType("datetime2")
|
|
||||||
.HasColumnName("updated-at")
|
|
||||||
.HasComment("更新时间");
|
|
||||||
|
|
||||||
b.HasKey("Id")
|
|
||||||
.HasName("pk-weather-forecast");
|
|
||||||
|
|
||||||
b.ToTable("weather-forecast", t =>
|
|
||||||
{
|
|
||||||
t.HasComment("天气预报数据实体");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
<Solution>
|
|
||||||
<Project Path="../Avalonia-API/Avalonia-API.csproj" Id="e33aba9a-a56b-4f6b-8eaa-3acbed65ebad" />
|
|
||||||
<Project Path="../Avalonia-Common/Avalonia-Common.csproj" Id="caed4118-2161-4382-90b8-35fb4efe3b5f" />
|
|
||||||
<Project Path="../Avalonia-EFCore/Avalonia-EFCore.csproj" Id="64557501-62a7-4863-b2bf-1570b8c6fecb" />
|
|
||||||
<Project Path="../Avalonia-Services/Avalonia-Services.csproj" Id="b8757cf9-5422-4c67-acae-3c967c95f866" />
|
|
||||||
<Project Path="../Avalonia-Web-VUE/avalonia-web-vue.esproj">
|
|
||||||
<Build />
|
|
||||||
<Deploy />
|
|
||||||
</Project>
|
|
||||||
<Project Path="Avalonia-PC.csproj" />
|
|
||||||
</Solution>
|
|
||||||
@@ -1,188 +0,0 @@
|
|||||||
using Avalonia_Common.Core;
|
|
||||||
using Avalonia_EFCore.Database;
|
|
||||||
using Avalonia_EFCore.Models;
|
|
||||||
using Avalonia_Services.Core;
|
|
||||||
using Avalonia_Services.Services;
|
|
||||||
using Avalonia_Services.Services.FileLibrary;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Avalonia_Services.Endpoints
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 统一端点配置 —— 所有业务端点在此定义一次。
|
|
||||||
/// 这是 Avalonia-API 和 Avalonia-PC 的唯一入口。
|
|
||||||
/// </summary>
|
|
||||||
public static class AppEndpoints
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 配置所有业务端点。调用方传入 builder,按需叠加鉴权、过滤器等。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="builder">端点构建器</param>
|
|
||||||
/// <param name="includeDetails">是否在错误响应中包含异常详情(开发环境 true)</param>
|
|
||||||
public static ServiceEndpointBuilder Configure(ServiceEndpointBuilder builder, bool includeDetails = false)
|
|
||||||
{
|
|
||||||
// ---- 全局异常拦截(自动捕获所有端点中未处理的异常) ----
|
|
||||||
builder.Endpoints.AddGlobalFilter(new GlobalExceptionFilter(includeDetails));
|
|
||||||
|
|
||||||
builder.ConfigureEndpoints(endpoints =>
|
|
||||||
{
|
|
||||||
// ---- 全局日志过滤器(记录每个请求) ----
|
|
||||||
endpoints.AddGlobalFilter(async (ctx, next) =>
|
|
||||||
{
|
|
||||||
Serilog.Log.Debug("→ {Method} {Path}", ctx.Method, ctx.Path);
|
|
||||||
await next(ctx);
|
|
||||||
Serilog.Log.Debug("← {Method} {Path} | {StatusCode}", ctx.Method, ctx.Path, ctx.StatusCode);
|
|
||||||
});
|
|
||||||
|
|
||||||
// ---- 业务端点注册 ----
|
|
||||||
// 天气预报(从数据库读取)
|
|
||||||
endpoints.MapGet("api/wData", GetWeatherForecastsAsync)
|
|
||||||
.WithOpenApi("Weather", "获取天气预报信息。")
|
|
||||||
.WithName("GetWeatherForecast");
|
|
||||||
|
|
||||||
// 获取用户(演示从数据库查询)
|
|
||||||
endpoints.MapGet("api/getUser", GetUserFromDatabaseAsync)
|
|
||||||
.WithName("GetUser");
|
|
||||||
|
|
||||||
// 处理数据(POST — 演示参数处理)
|
|
||||||
endpoints.MapPost("api/processData", ProcessDataAsync)
|
|
||||||
.WithName("ProcessData");
|
|
||||||
|
|
||||||
endpoints.MapGet<IFileLibraryEndpointService>("api/library/drives", (service, ctx) => service.GetDrivesAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "查询服务器磁盘。")
|
|
||||||
.WithName("GetLibraryDrives");
|
|
||||||
|
|
||||||
endpoints.MapGet<IFileLibraryEndpointService>("api/library/directories", (service, ctx) => service.GetDirectoriesAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "查询服务器目录。")
|
|
||||||
.WithName("GetLibraryDirectories");
|
|
||||||
|
|
||||||
endpoints.MapGet<IFileLibraryEndpointService>("api/library/roots", (service, ctx) => service.GetRootsAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "查询文件库目录。")
|
|
||||||
.WithName("GetLibraryRoots");
|
|
||||||
|
|
||||||
endpoints.MapPost<IFileLibraryEndpointService>("api/library/roots", (service, ctx) => service.AddRootAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "添加文件库目录。")
|
|
||||||
.WithName("AddLibraryRoot");
|
|
||||||
|
|
||||||
endpoints.MapPost<IFileLibraryEndpointService>("api/library/roots/enabled", (service, ctx) => service.SetRootEnabledAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "启用或禁用文件库目录。")
|
|
||||||
.WithName("SetLibraryRootEnabled");
|
|
||||||
|
|
||||||
endpoints.MapPost<IFileLibraryEndpointService>("api/library/roots/delete", (service, ctx) => service.DeleteRootAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "删除文件库目录。")
|
|
||||||
.WithName("DeleteLibraryRoot");
|
|
||||||
|
|
||||||
endpoints.MapPost<IFileLibraryEndpointService>("api/library/roots/scan", (service, ctx) => service.ScanRootAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "立即扫描文件库目录。")
|
|
||||||
.WithName("ScanLibraryRoot");
|
|
||||||
|
|
||||||
endpoints.MapGet<IFileLibraryEndpointService>("api/files", (service, ctx) => service.SearchFilesAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "分页查询已扫描文件。")
|
|
||||||
.WithName("SearchFiles");
|
|
||||||
|
|
||||||
endpoints.MapGet<IFileLibraryEndpointService>("api/files/detail", (service, ctx) => service.GetFileAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "查询文件详情。")
|
|
||||||
.WithName("GetFileDetail");
|
|
||||||
|
|
||||||
endpoints.MapGet<IFileLibraryEndpointService>("api/files/text", (service, ctx) => service.GetTextPreviewAsync(ctx))
|
|
||||||
.WithOpenApi("FileLibrary", "预览文本文件。")
|
|
||||||
.WithName("GetTextPreview");
|
|
||||||
|
|
||||||
// ---- 需要鉴权的端点示例 ----
|
|
||||||
// endpoints.MapGet("api/admin/dashboard", AdminDashboardAsync)
|
|
||||||
// .WithName("AdminDashboard")
|
|
||||||
// .RequireAuthorization = true
|
|
||||||
// .Policy = "AdminOnly";
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 业务处理方法
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从数据库查询天气预报(优先数据库,回退到内存生成)。
|
|
||||||
/// </summary>
|
|
||||||
private static async Task<object?> GetWeatherForecastsAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var sp = ctx.Items["ServiceProvider"] as IServiceProvider;
|
|
||||||
|
|
||||||
// 尝试从数据库读取
|
|
||||||
if (sp?.GetService(typeof(AppDataContext)) is AppDataContext db)
|
|
||||||
{
|
|
||||||
var dbForecasts = await db.WeatherForecasts
|
|
||||||
.OrderByDescending(f => f.Date)
|
|
||||||
.Take(5)
|
|
||||||
.ToListAsync();
|
|
||||||
|
|
||||||
if (dbForecasts.Count > 0)
|
|
||||||
{
|
|
||||||
return ResponseHelper.Ok(dbForecasts, "获取天气预报成功(来自数据库)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回退:内存生成(数据库为空时)
|
|
||||||
var service = sp?.GetService(typeof(WeatherForecastService)) as WeatherForecastService
|
|
||||||
?? new WeatherForecastService();
|
|
||||||
|
|
||||||
var forecasts = service.GetWeatherForecasts();
|
|
||||||
return ResponseHelper.Ok(forecasts, "获取天气预报成功(内存生成)");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从数据库获取用户信息(演示数据库查询),若无数据则返回演示用户。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ctx">服务端点上下文。</param>
|
|
||||||
/// <returns>用户信息。</returns>
|
|
||||||
private static async Task<object?> GetUserFromDatabaseAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var sp = ctx.Items["ServiceProvider"] as IServiceProvider;
|
|
||||||
|
|
||||||
// 尝试从数据库读取用户
|
|
||||||
if (sp?.GetService(typeof(AppDataContext)) is AppDataContext db)
|
|
||||||
{
|
|
||||||
var users = await db.Set<UserEntity>().Take(1).ToListAsync();
|
|
||||||
if (users.Count > 0)
|
|
||||||
{
|
|
||||||
return ResponseHelper.Ok(users[0], "获取用户成功(来自数据库)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回退:演示数据
|
|
||||||
await Task.Delay(100);
|
|
||||||
var user = new { id = 1, name = "张三", email = "zhangsan@example.com" };
|
|
||||||
return ResponseHelper.Ok(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理前端发送的数据(POST 演示),将数据存入数据库或转为大写返回。
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="ctx">服务端点上下文。</param>
|
|
||||||
/// <returns>处理结果。</returns>
|
|
||||||
private static async Task<object?> ProcessDataAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var sp = ctx.Items["ServiceProvider"] as IServiceProvider;
|
|
||||||
|
|
||||||
// 演示:将收到的数据存入数据库
|
|
||||||
var input = ctx.Body ?? string.Empty;
|
|
||||||
if (sp?.GetService(typeof(AppDataContext)) is AppDataContext db && !string.IsNullOrWhiteSpace(input))
|
|
||||||
{
|
|
||||||
var forecast = new WeatherForecastEntity
|
|
||||||
{
|
|
||||||
Date = DateOnly.FromDateTime(DateTime.Now),
|
|
||||||
TemperatureC = 20,
|
|
||||||
Summary = input,
|
|
||||||
};
|
|
||||||
db.WeatherForecasts.Add(forecast);
|
|
||||||
await db.SaveChangesAsync();
|
|
||||||
return ResponseHelper.Ok(forecast, "数据已存入数据库");
|
|
||||||
}
|
|
||||||
|
|
||||||
await Task.Delay(200);
|
|
||||||
return ResponseHelper.Ok(new { input, processed = input.ToUpperInvariant() });
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Avalonia_Services.Services.FileLibrary
|
|
||||||
{
|
|
||||||
public sealed record AddLibraryRootRequest(
|
|
||||||
[property: JsonPropertyName("path")] string? Path,
|
|
||||||
[property: JsonPropertyName("displayName")] string? DisplayName = null,
|
|
||||||
[property: JsonPropertyName("scanIntervalMinutes")] int? ScanIntervalMinutes = null);
|
|
||||||
|
|
||||||
public sealed record UpdateLibraryRootRequest(
|
|
||||||
[property: JsonPropertyName("id")] int Id,
|
|
||||||
[property: JsonPropertyName("isEnabled")] bool IsEnabled);
|
|
||||||
|
|
||||||
public sealed record ScanLibraryRootRequest(
|
|
||||||
[property: JsonPropertyName("id")] int Id);
|
|
||||||
|
|
||||||
public sealed record DeleteLibraryRootRequest(
|
|
||||||
[property: JsonPropertyName("id")] int Id);
|
|
||||||
|
|
||||||
public sealed record DriveDto(
|
|
||||||
string Name,
|
|
||||||
string DisplayName,
|
|
||||||
string RootDirectory,
|
|
||||||
string DriveType,
|
|
||||||
long? TotalSize,
|
|
||||||
long? AvailableFreeSpace,
|
|
||||||
bool IsReady);
|
|
||||||
|
|
||||||
public sealed record DirectoryDto(
|
|
||||||
string Name,
|
|
||||||
string FullPath);
|
|
||||||
|
|
||||||
public sealed record LibraryRootDto(
|
|
||||||
int Id,
|
|
||||||
string Path,
|
|
||||||
string DisplayName,
|
|
||||||
bool IsEnabled,
|
|
||||||
bool IsAvailable,
|
|
||||||
int ScanIntervalMinutes,
|
|
||||||
DateTime? LastScanStartedAt,
|
|
||||||
DateTime? LastScanCompletedAt,
|
|
||||||
string? LastScanError,
|
|
||||||
int FileCount);
|
|
||||||
|
|
||||||
public sealed record FileRecordDto(
|
|
||||||
int Id,
|
|
||||||
int LibraryRootId,
|
|
||||||
string FileName,
|
|
||||||
string RelativePath,
|
|
||||||
string Extension,
|
|
||||||
long SizeBytes,
|
|
||||||
DateTime LastWriteTimeUtc,
|
|
||||||
string MediaType,
|
|
||||||
string ContentType,
|
|
||||||
string StreamUrl,
|
|
||||||
string? TextUrl,
|
|
||||||
bool BrowserPlayable);
|
|
||||||
|
|
||||||
public sealed record TextPreviewDto(
|
|
||||||
int Id,
|
|
||||||
string FileName,
|
|
||||||
string Content,
|
|
||||||
bool Truncated);
|
|
||||||
}
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
using Avalonia_Common.Core;
|
|
||||||
using Avalonia_Services.Core;
|
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Avalonia_Services.Services.FileLibrary
|
|
||||||
{
|
|
||||||
public sealed class FileLibraryEndpointService(IFileLibraryService fileLibrary) : IFileLibraryEndpointService
|
|
||||||
{
|
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
public async Task<object?> GetDrivesAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
return ResponseHelper.Ok(await fileLibrary.GetDrivesAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object?> GetDirectoriesAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var path = ctx.Query.GetValueOrDefault("path");
|
|
||||||
return ResponseHelper.Ok(await fileLibrary.GetDirectoriesAsync(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object?> GetRootsAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
return ResponseHelper.Ok(await fileLibrary.GetRootsAsync());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object?> AddRootAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var request = ReadBody<AddLibraryRootRequest>(ctx);
|
|
||||||
return ResponseHelper.Ok(await fileLibrary.AddRootAsync(request), "文件库目录已添加并完成扫描。");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object?> SetRootEnabledAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var request = ReadBody<UpdateLibraryRootRequest>(ctx);
|
|
||||||
return ResponseHelper.Ok(await fileLibrary.SetRootEnabledAsync(request), "文件库目录状态已更新。");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object?> DeleteRootAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var request = ReadBody<DeleteLibraryRootRequest>(ctx);
|
|
||||||
await fileLibrary.DeleteRootAsync(request);
|
|
||||||
return ResponseHelper.Succeed("文件库目录已删除。");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object?> ScanRootAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var request = ReadBody<ScanLibraryRootRequest>(ctx);
|
|
||||||
return ResponseHelper.Ok(await fileLibrary.ScanRootAsync(request.Id), "文件库目录扫描完成。");
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object?> SearchFilesAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
return await fileLibrary.SearchFilesAsync(ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object?> GetFileAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var id = ReadId(ctx);
|
|
||||||
var file = await fileLibrary.GetFileAsync(id);
|
|
||||||
return file is null
|
|
||||||
? ResponseHelper.Failure(404, "文件不存在或尚未扫描入库。")
|
|
||||||
: ResponseHelper.Ok(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<object?> GetTextPreviewAsync(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
var id = ReadId(ctx);
|
|
||||||
var preview = await fileLibrary.GetTextPreviewAsync(id);
|
|
||||||
return preview is null
|
|
||||||
? ResponseHelper.Failure(404, "文本文件不存在或无法预览。")
|
|
||||||
: ResponseHelper.Ok(preview);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static T ReadBody<T>(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(ctx.Body))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("请求体不能为空。");
|
|
||||||
}
|
|
||||||
|
|
||||||
var body = JsonSerializer.Deserialize<T>(ctx.Body, JsonOptions);
|
|
||||||
return body ?? throw new InvalidOperationException("请求体格式错误。");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ReadId(ServiceEndpointContext ctx)
|
|
||||||
{
|
|
||||||
if (int.TryParse(ctx.Query.GetValueOrDefault("id"), out var id) && id > 0)
|
|
||||||
{
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InvalidOperationException("id 参数无效。");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,409 +0,0 @@
|
|||||||
using Avalonia_Common.Core;
|
|
||||||
using Avalonia_EFCore.Database;
|
|
||||||
using Avalonia_EFCore.Models;
|
|
||||||
using Avalonia_Services.Core;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Avalonia_Services.Services.FileLibrary
|
|
||||||
{
|
|
||||||
public sealed class FileLibraryService(AppDataContext db) : IFileLibraryService
|
|
||||||
{
|
|
||||||
private const int DefaultScanIntervalMinutes = 5;
|
|
||||||
private const int MaxTextPreviewBytes = 1024 * 1024;
|
|
||||||
|
|
||||||
public Task<List<DriveDto>> GetDrivesAsync(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var drives = DriveInfo.GetDrives()
|
|
||||||
.Select(drive => new DriveDto(
|
|
||||||
drive.Name,
|
|
||||||
drive.IsReady ? $"{drive.Name} ({drive.VolumeLabel})" : drive.Name,
|
|
||||||
drive.RootDirectory.FullName,
|
|
||||||
drive.DriveType.ToString(),
|
|
||||||
SafeDriveValue(drive, d => d.TotalSize),
|
|
||||||
SafeDriveValue(drive, d => d.AvailableFreeSpace),
|
|
||||||
drive.IsReady))
|
|
||||||
.OrderBy(drive => drive.Name)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return Task.FromResult(drives);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<List<DirectoryDto>> GetDirectoriesAsync(string? path, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var normalized = NormalizeExistingDirectory(path);
|
|
||||||
var directories = Directory.EnumerateDirectories(normalized)
|
|
||||||
.Select(directory => new DirectoryInfo(directory))
|
|
||||||
.OrderBy(directory => directory.Name)
|
|
||||||
.Select(directory => new DirectoryDto(directory.Name, directory.FullName))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
return Task.FromResult(directories);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<List<LibraryRootDto>> GetRootsAsync(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var counts = await db.ManagedFileRecords
|
|
||||||
.Where(file => file.Exists)
|
|
||||||
.GroupBy(file => file.LibraryRootId)
|
|
||||||
.Select(group => new { RootId = group.Key, Count = group.Count() })
|
|
||||||
.ToDictionaryAsync(item => item.RootId, item => item.Count, cancellationToken);
|
|
||||||
|
|
||||||
var roots = await db.ManagedLibraryRoots
|
|
||||||
.OrderBy(root => root.Path)
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return roots.Select(root => ToRootDto(root, counts.GetValueOrDefault(root.Id))).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<LibraryRootDto> AddRootAsync(AddLibraryRootRequest request, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var normalized = NormalizeExistingDirectory(request.Path);
|
|
||||||
var existing = await db.ManagedLibraryRoots.FirstOrDefaultAsync(root => root.Path == normalized, cancellationToken);
|
|
||||||
if (existing is not null)
|
|
||||||
{
|
|
||||||
existing.IsEnabled = true;
|
|
||||||
existing.IsAvailable = true;
|
|
||||||
existing.DisplayName = ResolveDisplayName(normalized, request.DisplayName);
|
|
||||||
existing.ScanIntervalMinutes = NormalizeInterval(request.ScanIntervalMinutes);
|
|
||||||
await db.SaveChangesAsync(cancellationToken);
|
|
||||||
return await ScanRootAsync(existing.Id, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
var root = new ManagedLibraryRoot
|
|
||||||
{
|
|
||||||
Path = normalized,
|
|
||||||
DisplayName = ResolveDisplayName(normalized, request.DisplayName),
|
|
||||||
ScanIntervalMinutes = NormalizeInterval(request.ScanIntervalMinutes),
|
|
||||||
IsEnabled = true,
|
|
||||||
IsAvailable = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
db.ManagedLibraryRoots.Add(root);
|
|
||||||
await db.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
return await ScanRootAsync(root.Id, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<LibraryRootDto> SetRootEnabledAsync(UpdateLibraryRootRequest request, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var root = await db.ManagedLibraryRoots.FirstOrDefaultAsync(item => item.Id == request.Id, cancellationToken)
|
|
||||||
?? throw new InvalidOperationException("文件库目录不存在。");
|
|
||||||
|
|
||||||
root.IsEnabled = request.IsEnabled;
|
|
||||||
await db.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
var count = await db.ManagedFileRecords.CountAsync(file => file.LibraryRootId == root.Id && file.Exists, cancellationToken);
|
|
||||||
return ToRootDto(root, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DeleteRootAsync(DeleteLibraryRootRequest request, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var root = await db.ManagedLibraryRoots.FirstOrDefaultAsync(item => item.Id == request.Id, cancellationToken)
|
|
||||||
?? throw new InvalidOperationException("文件库目录不存在。");
|
|
||||||
|
|
||||||
db.ManagedLibraryRoots.Remove(root);
|
|
||||||
await db.SaveChangesAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<LibraryRootDto> ScanRootAsync(int rootId, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var root = await db.ManagedLibraryRoots.FirstOrDefaultAsync(item => item.Id == rootId, cancellationToken)
|
|
||||||
?? throw new InvalidOperationException("文件库目录不存在。");
|
|
||||||
|
|
||||||
root.LastScanStartedAt = DateTime.UtcNow;
|
|
||||||
root.LastScanError = null;
|
|
||||||
await db.SaveChangesAsync(cancellationToken);
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(root.Path))
|
|
||||||
{
|
|
||||||
throw new DirectoryNotFoundException($"目录不存在:{root.Path}");
|
|
||||||
}
|
|
||||||
|
|
||||||
root.IsAvailable = true;
|
|
||||||
root.IsEnabled = true;
|
|
||||||
|
|
||||||
var existing = await db.ManagedFileRecords
|
|
||||||
.Where(file => file.LibraryRootId == root.Id)
|
|
||||||
.ToDictionaryAsync(file => file.AbsolutePath, StringComparer.OrdinalIgnoreCase, cancellationToken);
|
|
||||||
|
|
||||||
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
foreach (var path in EnumerateSupportedFiles(root.Path))
|
|
||||||
{
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
var info = new FileInfo(path);
|
|
||||||
if (!info.Exists || !MediaFileTypes.TryGet(info.Extension.ToLowerInvariant(), out var mediaType, out var contentType, out _))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var absolutePath = info.FullName;
|
|
||||||
seen.Add(absolutePath);
|
|
||||||
|
|
||||||
if (!existing.TryGetValue(absolutePath, out var record))
|
|
||||||
{
|
|
||||||
record = new ManagedFileRecord
|
|
||||||
{
|
|
||||||
LibraryRootId = root.Id,
|
|
||||||
AbsolutePath = absolutePath,
|
|
||||||
};
|
|
||||||
db.ManagedFileRecords.Add(record);
|
|
||||||
}
|
|
||||||
|
|
||||||
record.FileName = info.Name;
|
|
||||||
record.RelativePath = Path.GetRelativePath(root.Path, absolutePath);
|
|
||||||
record.Extension = info.Extension.ToLowerInvariant();
|
|
||||||
record.SizeBytes = info.Length;
|
|
||||||
record.LastWriteTimeUtc = info.LastWriteTimeUtc;
|
|
||||||
record.MediaType = mediaType;
|
|
||||||
record.ContentType = contentType;
|
|
||||||
record.Exists = true;
|
|
||||||
record.LastSeenAt = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var stale in existing.Values.Where(file => !seen.Contains(file.AbsolutePath)))
|
|
||||||
{
|
|
||||||
stale.Exists = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
root.LastScanCompletedAt = DateTime.UtcNow;
|
|
||||||
await db.SaveChangesAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
root.IsAvailable = false;
|
|
||||||
root.IsEnabled = false;
|
|
||||||
root.LastScanError = ex.Message;
|
|
||||||
root.LastScanCompletedAt = DateTime.UtcNow;
|
|
||||||
await db.SaveChangesAsync(CancellationToken.None);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
var count = await db.ManagedFileRecords.CountAsync(file => file.LibraryRootId == root.Id && file.Exists, cancellationToken);
|
|
||||||
return ToRootDto(root, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ScanDueRootsAsync(CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var now = DateTime.UtcNow;
|
|
||||||
var roots = await db.ManagedLibraryRoots
|
|
||||||
.Where(root => root.IsEnabled && root.IsAvailable)
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
foreach (var root in roots)
|
|
||||||
{
|
|
||||||
var interval = Math.Max(1, root.ScanIntervalMinutes);
|
|
||||||
var isDue = root.LastScanCompletedAt is null || root.LastScanCompletedAt.Value.AddMinutes(interval) <= now;
|
|
||||||
if (!isDue)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await ScanRootAsync(root.Id, cancellationToken);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ScanRootAsync records the error on the root. Continue scanning other roots.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<PagedResponse<FileRecordDto>> SearchFilesAsync(ServiceEndpointContext ctx, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var page = ParseInt(ctx.Query.GetValueOrDefault("page"), 1, 1, 100000);
|
|
||||||
var pageSize = ParseInt(ctx.Query.GetValueOrDefault("pageSize"), 24, 1, 100);
|
|
||||||
var mediaType = ctx.Query.GetValueOrDefault("mediaType")?.Trim();
|
|
||||||
var keyword = ctx.Query.GetValueOrDefault("keyword")?.Trim();
|
|
||||||
var rootId = ParseInt(ctx.Query.GetValueOrDefault("rootId"), 0, 0, int.MaxValue);
|
|
||||||
|
|
||||||
var query = db.ManagedFileRecords
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(file => file.Exists && file.LibraryRoot != null && file.LibraryRoot.IsAvailable);
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(mediaType) && !mediaType.Equals("all", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
query = query.Where(file => file.MediaType == mediaType);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rootId > 0)
|
|
||||||
{
|
|
||||||
query = query.Where(file => file.LibraryRootId == rootId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(keyword))
|
|
||||||
{
|
|
||||||
query = query.Where(file => file.FileName.Contains(keyword) || file.RelativePath.Contains(keyword));
|
|
||||||
}
|
|
||||||
|
|
||||||
var total = await query.CountAsync(cancellationToken);
|
|
||||||
var items = await query
|
|
||||||
.OrderBy(file => file.MediaType)
|
|
||||||
.ThenBy(file => file.RelativePath)
|
|
||||||
.Skip((page - 1) * pageSize)
|
|
||||||
.Take(pageSize)
|
|
||||||
.Select(file => ToFileDto(file))
|
|
||||||
.ToListAsync(cancellationToken);
|
|
||||||
|
|
||||||
return PagedResponse<FileRecordDto>.From(items, total, page, pageSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<FileRecordDto?> GetFileAsync(int id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
return await db.ManagedFileRecords
|
|
||||||
.AsNoTracking()
|
|
||||||
.Where(file => file.Id == id && file.Exists && file.LibraryRoot != null && file.LibraryRoot.IsAvailable)
|
|
||||||
.Select(file => ToFileDto(file))
|
|
||||||
.FirstOrDefaultAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TextPreviewDto?> GetTextPreviewAsync(int id, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
var file = await db.ManagedFileRecords
|
|
||||||
.AsNoTracking()
|
|
||||||
.Include(item => item.LibraryRoot)
|
|
||||||
.FirstOrDefaultAsync(item =>
|
|
||||||
item.Id == id
|
|
||||||
&& item.Exists
|
|
||||||
&& item.MediaType == "text"
|
|
||||||
&& item.LibraryRoot != null
|
|
||||||
&& item.LibraryRoot.IsAvailable,
|
|
||||||
cancellationToken);
|
|
||||||
|
|
||||||
if (file is null || !File.Exists(file.AbsolutePath))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
await using var stream = File.OpenRead(file.AbsolutePath);
|
|
||||||
var limit = (int)Math.Min(stream.Length, MaxTextPreviewBytes);
|
|
||||||
var buffer = new byte[limit];
|
|
||||||
var read = await stream.ReadAsync(buffer.AsMemory(0, limit), cancellationToken);
|
|
||||||
var content = Encoding.UTF8.GetString(buffer, 0, read);
|
|
||||||
return new TextPreviewDto(file.Id, file.FileName, content, stream.Length > MaxTextPreviewBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<string> EnumerateSupportedFiles(string rootPath)
|
|
||||||
{
|
|
||||||
var pending = new Stack<string>();
|
|
||||||
pending.Push(rootPath);
|
|
||||||
|
|
||||||
while (pending.Count > 0)
|
|
||||||
{
|
|
||||||
var current = pending.Pop();
|
|
||||||
IEnumerable<string> directories;
|
|
||||||
IEnumerable<string> files;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
directories = Directory.EnumerateDirectories(current);
|
|
||||||
files = Directory.EnumerateFiles(current);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var directory in directories)
|
|
||||||
{
|
|
||||||
pending.Push(directory);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var file in files)
|
|
||||||
{
|
|
||||||
if (MediaFileTypes.TryGet(Path.GetExtension(file), out _, out _, out _))
|
|
||||||
{
|
|
||||||
yield return file;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string NormalizeExistingDirectory(string? path)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("目录路径不能为空。");
|
|
||||||
}
|
|
||||||
|
|
||||||
var fullPath = Path.GetFullPath(path.Trim());
|
|
||||||
if (!Directory.Exists(fullPath))
|
|
||||||
{
|
|
||||||
throw new DirectoryNotFoundException($"目录不存在:{fullPath}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DirectoryInfo(fullPath).FullName;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ResolveDisplayName(string path, string? displayName)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(displayName))
|
|
||||||
{
|
|
||||||
return displayName.Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
var directory = new DirectoryInfo(path);
|
|
||||||
return string.IsNullOrWhiteSpace(directory.Name) ? directory.FullName : directory.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int NormalizeInterval(int? interval)
|
|
||||||
{
|
|
||||||
return Math.Clamp(interval ?? DefaultScanIntervalMinutes, 1, 1440);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long? SafeDriveValue(DriveInfo drive, Func<DriveInfo, long> selector)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return drive.IsReady ? selector(drive) : null;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static LibraryRootDto ToRootDto(ManagedLibraryRoot root, int fileCount)
|
|
||||||
{
|
|
||||||
return new LibraryRootDto(
|
|
||||||
root.Id,
|
|
||||||
root.Path,
|
|
||||||
root.DisplayName,
|
|
||||||
root.IsEnabled,
|
|
||||||
root.IsAvailable,
|
|
||||||
root.ScanIntervalMinutes,
|
|
||||||
root.LastScanStartedAt,
|
|
||||||
root.LastScanCompletedAt,
|
|
||||||
root.LastScanError,
|
|
||||||
fileCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static FileRecordDto ToFileDto(ManagedFileRecord file)
|
|
||||||
{
|
|
||||||
return new FileRecordDto(
|
|
||||||
file.Id,
|
|
||||||
file.LibraryRootId,
|
|
||||||
file.FileName,
|
|
||||||
file.RelativePath,
|
|
||||||
file.Extension,
|
|
||||||
file.SizeBytes,
|
|
||||||
file.LastWriteTimeUtc,
|
|
||||||
file.MediaType,
|
|
||||||
file.ContentType,
|
|
||||||
$"/api/files/{file.Id}/stream",
|
|
||||||
file.MediaType == "text" ? $"/api/files/text?id={file.Id}" : null,
|
|
||||||
MediaFileTypes.IsBrowserPlayable(file.Extension));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static int ParseInt(string? value, int fallback, int min, int max)
|
|
||||||
{
|
|
||||||
return int.TryParse(value, out var parsed)
|
|
||||||
? Math.Clamp(parsed, min, max)
|
|
||||||
: fallback;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using Avalonia_Services.Core;
|
|
||||||
|
|
||||||
namespace Avalonia_Services.Services.FileLibrary
|
|
||||||
{
|
|
||||||
public interface IFileLibraryEndpointService
|
|
||||||
{
|
|
||||||
Task<object?> GetDrivesAsync(ServiceEndpointContext ctx);
|
|
||||||
|
|
||||||
Task<object?> GetDirectoriesAsync(ServiceEndpointContext ctx);
|
|
||||||
|
|
||||||
Task<object?> GetRootsAsync(ServiceEndpointContext ctx);
|
|
||||||
|
|
||||||
Task<object?> AddRootAsync(ServiceEndpointContext ctx);
|
|
||||||
|
|
||||||
Task<object?> SetRootEnabledAsync(ServiceEndpointContext ctx);
|
|
||||||
|
|
||||||
Task<object?> DeleteRootAsync(ServiceEndpointContext ctx);
|
|
||||||
|
|
||||||
Task<object?> ScanRootAsync(ServiceEndpointContext ctx);
|
|
||||||
|
|
||||||
Task<object?> SearchFilesAsync(ServiceEndpointContext ctx);
|
|
||||||
|
|
||||||
Task<object?> GetFileAsync(ServiceEndpointContext ctx);
|
|
||||||
|
|
||||||
Task<object?> GetTextPreviewAsync(ServiceEndpointContext ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
using Avalonia_Common.Core;
|
|
||||||
using Avalonia_Services.Core;
|
|
||||||
|
|
||||||
namespace Avalonia_Services.Services.FileLibrary
|
|
||||||
{
|
|
||||||
public interface IFileLibraryService
|
|
||||||
{
|
|
||||||
Task<List<DriveDto>> GetDrivesAsync(CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<List<DirectoryDto>> GetDirectoriesAsync(string? path, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<List<LibraryRootDto>> GetRootsAsync(CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<LibraryRootDto> AddRootAsync(AddLibraryRootRequest request, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<LibraryRootDto> SetRootEnabledAsync(UpdateLibraryRootRequest request, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task DeleteRootAsync(DeleteLibraryRootRequest request, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<LibraryRootDto> ScanRootAsync(int rootId, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task ScanDueRootsAsync(CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<PagedResponse<FileRecordDto>> SearchFilesAsync(ServiceEndpointContext ctx, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<FileRecordDto?> GetFileAsync(int id, CancellationToken cancellationToken = default);
|
|
||||||
|
|
||||||
Task<TextPreviewDto?> GetTextPreviewAsync(int id, CancellationToken cancellationToken = default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,435 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { computed, onMounted, ref } from 'vue'
|
|
||||||
import { api, type DirectoryDto, type DriveDto, type FileRecordDto, type LibraryRootDto, type MediaType, type TextPreviewDto } from './api'
|
|
||||||
|
|
||||||
const isAdminPage = computed(() => window.location.pathname.toLowerCase().startsWith('/admin'))
|
|
||||||
const roots = ref<LibraryRootDto[]>([])
|
|
||||||
const drives = ref<DriveDto[]>([])
|
|
||||||
const directories = ref<DirectoryDto[]>([])
|
|
||||||
const files = ref<FileRecordDto[]>([])
|
|
||||||
const selectedFile = ref<FileRecordDto | null>(null)
|
|
||||||
const textPreview = ref<TextPreviewDto | null>(null)
|
|
||||||
const currentPath = ref('')
|
|
||||||
const manualPath = ref('')
|
|
||||||
const keyword = ref('')
|
|
||||||
const mediaType = ref<MediaType>('all')
|
|
||||||
const rootId = ref<number | undefined>()
|
|
||||||
const isBrowsingRoots = ref(true)
|
|
||||||
const page = ref(1)
|
|
||||||
const pageSize = 24
|
|
||||||
const total = ref(0)
|
|
||||||
const loading = ref(false)
|
|
||||||
const scanningId = ref<number | null>(null)
|
|
||||||
const errorMessage = ref('')
|
|
||||||
|
|
||||||
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize)))
|
|
||||||
const availableRoots = computed(() => roots.value.filter((root) => root.isAvailable))
|
|
||||||
const activeRoots = computed(() => roots.value.filter((root) => root.isEnabled && root.isAvailable))
|
|
||||||
const selectedRoot = computed(() => roots.value.find((root) => root.id === selectedFile.value?.libraryRootId))
|
|
||||||
const totalRootFiles = computed(() => roots.value.reduce((sum, root) => sum + root.fileCount, 0))
|
|
||||||
const selectedMediaUrl = computed(() => selectedFile.value ? api.mediaUrl(selectedFile.value.streamUrl) : '')
|
|
||||||
const clientTitle = computed(() => {
|
|
||||||
if (isBrowsingRoots.value) return '文件库'
|
|
||||||
return rootId.value ? roots.value.find((root) => root.id === rootId.value)?.displayName ?? '文件' : '文件'
|
|
||||||
})
|
|
||||||
|
|
||||||
function formatSize(bytes: number) {
|
|
||||||
if (bytes < 1024) return `${bytes} B`
|
|
||||||
const units = ['KB', 'MB', 'GB', 'TB']
|
|
||||||
let value = bytes / 1024
|
|
||||||
let index = 0
|
|
||||||
while (value >= 1024 && index < units.length - 1) {
|
|
||||||
value /= 1024
|
|
||||||
index += 1
|
|
||||||
}
|
|
||||||
return `${value.toFixed(value >= 10 ? 1 : 2)} ${units[index]}`
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDate(value: string | null) {
|
|
||||||
if (!value) return '未扫描'
|
|
||||||
return new Date(value).toLocaleString()
|
|
||||||
}
|
|
||||||
|
|
||||||
function setError(error: unknown) {
|
|
||||||
errorMessage.value = error instanceof Error ? error.message : '操作失败'
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadRoots() {
|
|
||||||
roots.value = await api.getRoots()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadDrives() {
|
|
||||||
drives.value = await api.getDrives()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function openDirectory(path: string) {
|
|
||||||
try {
|
|
||||||
errorMessage.value = ''
|
|
||||||
currentPath.value = path
|
|
||||||
manualPath.value = path
|
|
||||||
directories.value = await api.getDirectories(path)
|
|
||||||
} catch (error) {
|
|
||||||
setError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function addRoot(path = manualPath.value) {
|
|
||||||
try {
|
|
||||||
errorMessage.value = ''
|
|
||||||
loading.value = true
|
|
||||||
await api.addRoot({ path, scanIntervalMinutes: 5 })
|
|
||||||
await Promise.all([loadRoots(), loadFiles()])
|
|
||||||
} catch (error) {
|
|
||||||
setError(error)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function toggleRoot(root: LibraryRootDto) {
|
|
||||||
try {
|
|
||||||
errorMessage.value = ''
|
|
||||||
await api.setRootEnabled(root.id, !root.isEnabled)
|
|
||||||
await loadRoots()
|
|
||||||
} catch (error) {
|
|
||||||
setError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteRoot(root: LibraryRootDto) {
|
|
||||||
try {
|
|
||||||
errorMessage.value = ''
|
|
||||||
await api.deleteRoot(root.id)
|
|
||||||
if (rootId.value === root.id) rootId.value = undefined
|
|
||||||
await Promise.all([loadRoots(), loadFiles()])
|
|
||||||
} catch (error) {
|
|
||||||
setError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function scanRoot(root: LibraryRootDto) {
|
|
||||||
try {
|
|
||||||
errorMessage.value = ''
|
|
||||||
scanningId.value = root.id
|
|
||||||
await api.scanRoot(root.id)
|
|
||||||
await Promise.all([loadRoots(), loadFiles()])
|
|
||||||
} catch (error) {
|
|
||||||
setError(error)
|
|
||||||
} finally {
|
|
||||||
scanningId.value = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadFiles(resetPage = false) {
|
|
||||||
try {
|
|
||||||
errorMessage.value = ''
|
|
||||||
if (resetPage) page.value = 1
|
|
||||||
const result = await api.searchFiles({
|
|
||||||
page: page.value,
|
|
||||||
pageSize,
|
|
||||||
mediaType: mediaType.value,
|
|
||||||
keyword: keyword.value,
|
|
||||||
rootId: rootId.value,
|
|
||||||
})
|
|
||||||
files.value = result.items
|
|
||||||
total.value = result.total
|
|
||||||
if (!selectedFile.value || !files.value.some((file) => file.id === selectedFile.value?.id)) {
|
|
||||||
selectedFile.value = null
|
|
||||||
textPreview.value = null
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function openClientRoot(id: number) {
|
|
||||||
rootId.value = id
|
|
||||||
isBrowsingRoots.value = false
|
|
||||||
await loadFiles(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
function backToRoots() {
|
|
||||||
isBrowsingRoots.value = true
|
|
||||||
rootId.value = undefined
|
|
||||||
selectedFile.value = null
|
|
||||||
textPreview.value = null
|
|
||||||
}
|
|
||||||
|
|
||||||
async function selectFile(file: FileRecordDto | null) {
|
|
||||||
selectedFile.value = file
|
|
||||||
textPreview.value = null
|
|
||||||
if (!file || file.mediaType !== 'text') return
|
|
||||||
|
|
||||||
try {
|
|
||||||
textPreview.value = await api.getTextPreview(file.id)
|
|
||||||
} catch (error) {
|
|
||||||
setError(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function changePage(next: number) {
|
|
||||||
page.value = Math.min(Math.max(1, next), totalPages.value)
|
|
||||||
await loadFiles()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function refreshAll() {
|
|
||||||
await Promise.all([loadRoots(), loadFiles()])
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
await loadRoots()
|
|
||||||
if (isAdminPage.value) {
|
|
||||||
await loadDrives()
|
|
||||||
} else {
|
|
||||||
total.value = activeRoots.value.reduce((sum, root) => sum + root.fileCount, 0)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
setError(error)
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<main v-if="isAdminPage" class="admin-shell">
|
|
||||||
<header class="admin-hero">
|
|
||||||
<div>
|
|
||||||
<p class="eyebrow">FileShare Admin</p>
|
|
||||||
<h1>文件库管理</h1>
|
|
||||||
<p>添加服务器本机磁盘或目录,系统按状态定时扫描,异常目录会自动下线并停止自动扫描。</p>
|
|
||||||
</div>
|
|
||||||
<div class="admin-metrics">
|
|
||||||
<div>
|
|
||||||
<strong>{{ roots.length }}</strong>
|
|
||||||
<span>目录</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<strong>{{ activeRoots.length }}</strong>
|
|
||||||
<span>正常启用</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<strong>{{ totalRootFiles }}</strong>
|
|
||||||
<span>入库文件</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<p v-if="errorMessage" class="error-banner">{{ errorMessage }}</p>
|
|
||||||
|
|
||||||
<section class="admin-layout">
|
|
||||||
<section class="admin-card path-card">
|
|
||||||
<div class="card-heading">
|
|
||||||
<div>
|
|
||||||
<h2>添加扫描目录</h2>
|
|
||||||
<p>选择服务器路径,或直接输入绝对路径。</p>
|
|
||||||
</div>
|
|
||||||
<a href="/" class="client-link">客户端</a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<label class="field">
|
|
||||||
<span>服务器路径</span>
|
|
||||||
<div class="inline-form">
|
|
||||||
<input v-model="manualPath" type="text" placeholder="例如 D:\Media 或 E:\" />
|
|
||||||
<button class="primary-button" type="button" :disabled="loading || !manualPath" @click="addRoot()">添加并扫描</button>
|
|
||||||
</div>
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<div class="admin-browser">
|
|
||||||
<div class="drive-list">
|
|
||||||
<h3>磁盘</h3>
|
|
||||||
<button
|
|
||||||
v-for="drive in drives"
|
|
||||||
:key="drive.name"
|
|
||||||
class="drive-row"
|
|
||||||
type="button"
|
|
||||||
:disabled="!drive.isReady"
|
|
||||||
@click="openDirectory(drive.rootDirectory)"
|
|
||||||
>
|
|
||||||
<span>{{ drive.displayName }}</span>
|
|
||||||
<small>{{ drive.driveType }} · {{ drive.availableFreeSpace !== null ? formatSize(drive.availableFreeSpace) : '不可用' }}</small>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="directory-list">
|
|
||||||
<div class="browser-header">
|
|
||||||
<h3>目录</h3>
|
|
||||||
<button type="button" class="text-button" :disabled="!currentPath" @click="addRoot(currentPath)">添加当前目录</button>
|
|
||||||
</div>
|
|
||||||
<p class="current-path">{{ currentPath || '请选择一个磁盘' }}</p>
|
|
||||||
<button
|
|
||||||
v-for="directory in directories"
|
|
||||||
:key="directory.fullPath"
|
|
||||||
class="directory-row"
|
|
||||||
type="button"
|
|
||||||
@click="openDirectory(directory.fullPath)"
|
|
||||||
>
|
|
||||||
{{ directory.name }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section class="admin-card roots-card">
|
|
||||||
<div class="card-heading">
|
|
||||||
<div>
|
|
||||||
<h2>目录状态</h2>
|
|
||||||
<p>异常目录不会自动扫描,客户端也无法访问其中的文件;手动扫描成功后恢复正常。</p>
|
|
||||||
</div>
|
|
||||||
<button class="secondary-button" type="button" :disabled="loading" @click="refreshAll">刷新</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="root-table">
|
|
||||||
<article v-for="root in roots" :key="root.id" class="root-item">
|
|
||||||
<div class="root-main">
|
|
||||||
<span :class="['status-pill', root.isAvailable ? 'ok' : 'bad']">{{ root.isAvailable ? '正常' : '异常' }}</span>
|
|
||||||
<div>
|
|
||||||
<strong>{{ root.displayName }}</strong>
|
|
||||||
<p>{{ root.path }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="root-meta">
|
|
||||||
<span>{{ root.fileCount }} 个文件</span>
|
|
||||||
<span>{{ formatDate(root.lastScanCompletedAt) }}</span>
|
|
||||||
<span>{{ root.isEnabled ? '启用' : '停用' }}</span>
|
|
||||||
</div>
|
|
||||||
<p v-if="root.lastScanError" class="root-error">{{ root.lastScanError }}</p>
|
|
||||||
<div class="root-actions">
|
|
||||||
<button type="button" class="secondary-button" @click="scanRoot(root)">
|
|
||||||
{{ scanningId === root.id ? '扫描中' : root.isAvailable ? '立即扫描' : '手动扫描恢复' }}
|
|
||||||
</button>
|
|
||||||
<button type="button" class="secondary-button" :disabled="!root.isAvailable" @click="toggleRoot(root)">
|
|
||||||
{{ root.isEnabled ? '停用' : '启用' }}
|
|
||||||
</button>
|
|
||||||
<button type="button" class="danger-button" @click="deleteRoot(root)">删除</button>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
<p v-if="roots.length === 0" class="empty-state">还没有添加扫描目录</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<main v-else class="client-shell">
|
|
||||||
<header class="mobile-header">
|
|
||||||
<div>
|
|
||||||
<h1>{{ clientTitle }}</h1>
|
|
||||||
<p>{{ isBrowsingRoots ? `${activeRoots.length} 个目录` : `${total} 个文件` }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="mobile-header-actions">
|
|
||||||
<button v-if="!isBrowsingRoots" type="button" class="back-button" @click="backToRoots">返回</button>
|
|
||||||
<a href="/admin" class="admin-link">管理</a>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<p v-if="errorMessage" class="error-banner">{{ errorMessage }}</p>
|
|
||||||
|
|
||||||
<section v-if="isBrowsingRoots" class="root-picker">
|
|
||||||
<button
|
|
||||||
v-for="root in activeRoots"
|
|
||||||
:key="root.id"
|
|
||||||
class="root-tile"
|
|
||||||
type="button"
|
|
||||||
@click="openClientRoot(root.id)"
|
|
||||||
>
|
|
||||||
<span>{{ root.displayName }}</span>
|
|
||||||
<strong>{{ root.fileCount }}</strong>
|
|
||||||
<small>{{ root.path }}</small>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<p v-if="activeRoots.length === 0" class="empty-state">暂无可访问目录</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section v-else class="mobile-filters">
|
|
||||||
<input v-model="keyword" type="search" placeholder="搜索文件" @keyup.enter="loadFiles(true)" />
|
|
||||||
<div class="filter-row">
|
|
||||||
<select v-model="mediaType" @change="loadFiles(true)">
|
|
||||||
<option value="all">全部</option>
|
|
||||||
<option value="video">视频</option>
|
|
||||||
<option value="audio">音频</option>
|
|
||||||
<option value="text">文本</option>
|
|
||||||
</select>
|
|
||||||
<select v-model="rootId" @change="loadFiles(true)">
|
|
||||||
<option v-for="root in availableRoots" :key="root.id" :value="root.id">{{ root.displayName }}</option>
|
|
||||||
</select>
|
|
||||||
<button class="primary-button" type="button" @click="loadFiles(true)">查询</button>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section v-if="!isBrowsingRoots" class="player-panel">
|
|
||||||
<p v-if="!selectedFile" class="empty-state">请选择一个文件</p>
|
|
||||||
|
|
||||||
<template v-else>
|
|
||||||
<div class="player-title">
|
|
||||||
<div>
|
|
||||||
<h2>{{ selectedFile.fileName }}</h2>
|
|
||||||
<p>{{ selectedRoot?.displayName ?? '文件库' }} · {{ selectedFile.relativePath }}</p>
|
|
||||||
</div>
|
|
||||||
<span>{{ selectedFile.extension }}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<video
|
|
||||||
v-if="selectedFile.mediaType === 'video' && selectedFile.browserPlayable"
|
|
||||||
:key="selectedFile.id"
|
|
||||||
controls
|
|
||||||
playsinline
|
|
||||||
webkit-playsinline
|
|
||||||
preload="metadata"
|
|
||||||
>
|
|
||||||
<source :src="selectedMediaUrl" :type="selectedFile.contentType" />
|
|
||||||
</video>
|
|
||||||
<audio
|
|
||||||
v-else-if="selectedFile.mediaType === 'audio' && selectedFile.browserPlayable"
|
|
||||||
:key="selectedFile.id"
|
|
||||||
controls
|
|
||||||
preload="metadata"
|
|
||||||
>
|
|
||||||
<source :src="selectedMediaUrl" :type="selectedFile.contentType" />
|
|
||||||
</audio>
|
|
||||||
<pre v-else-if="selectedFile.mediaType === 'text'">{{ textPreview?.content ?? '加载中...' }}</pre>
|
|
||||||
<p v-else class="unsupported">浏览器不支持在线播放此格式。</p>
|
|
||||||
<a
|
|
||||||
v-if="selectedFile.mediaType !== 'text'"
|
|
||||||
class="open-media-link"
|
|
||||||
:href="selectedMediaUrl"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
新窗口打开原视频/音频
|
|
||||||
</a>
|
|
||||||
<p v-if="textPreview?.truncated" class="hint">文本超过 1 MB,已截断显示。</p>
|
|
||||||
</template>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<section v-if="!isBrowsingRoots" class="mobile-list">
|
|
||||||
<button
|
|
||||||
v-for="file in files"
|
|
||||||
:key="file.id"
|
|
||||||
class="mobile-file"
|
|
||||||
:class="{ active: selectedFile?.id === file.id }"
|
|
||||||
type="button"
|
|
||||||
@click="selectFile(file)"
|
|
||||||
>
|
|
||||||
<span class="type-badge">{{ file.mediaType }}</span>
|
|
||||||
<span>
|
|
||||||
<strong>{{ file.fileName }}</strong>
|
|
||||||
<small>{{ file.relativePath }}</small>
|
|
||||||
<small>
|
|
||||||
{{ formatSize(file.sizeBytes) }} · {{ formatDate(file.lastWriteTimeUtc) }}
|
|
||||||
<template v-if="file.mediaType !== 'text' && !file.browserPlayable"> · 手机可能不支持</template>
|
|
||||||
</small>
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<p v-if="files.length === 0" class="empty-state">暂无可查看文件</p>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<nav v-if="!isBrowsingRoots" class="mobile-pager">
|
|
||||||
<button type="button" :disabled="page <= 1" @click="changePage(page - 1)">上一页</button>
|
|
||||||
<span>{{ page }} / {{ totalPages }}</span>
|
|
||||||
<button type="button" :disabled="page >= totalPages" @click="changePage(page + 1)">下一页</button>
|
|
||||||
</nav>
|
|
||||||
</main>
|
|
||||||
</template>
|
|
||||||
+12
-34
@@ -1,12 +1,11 @@
|
|||||||
using Avalonia_Common.Core;
|
using FileShare_Common.Core;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Avalonia_EFCore.Models;
|
using FileShare_EFCore.Models;
|
||||||
using Avalonia_Services.Core;
|
using FileShare_Services.Core;
|
||||||
using Avalonia_Services.Services.AuthService;
|
using FileShare_Services.Services.AuthService;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using System.Text.Json;
|
|
||||||
|
|
||||||
namespace Avalonia_API.Authentication
|
namespace FileShare_API.Authentication
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// API 鉴权端点服务,实现 <see cref="IApiAuthEndpointService"/>,
|
/// API 鉴权端点服务,实现 <see cref="IApiAuthEndpointService"/>,
|
||||||
@@ -17,21 +16,15 @@ namespace Avalonia_API.Authentication
|
|||||||
JwtTokenService jwtTokenService,
|
JwtTokenService jwtTokenService,
|
||||||
RefreshTokenService refreshTokenService) : IApiAuthEndpointService
|
RefreshTokenService refreshTokenService) : IApiAuthEndpointService
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 处理用户登录请求。根据账号(邮箱或用户名)查找或创建用户,
|
/// 处理用户登录请求。根据账号(邮箱或用户名)查找或创建用户,
|
||||||
/// 生成 JWT Access Token 和 Refresh Token 并返回。
|
/// 生成 JWT Access Token 和 Refresh Token 并返回。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ctx">服务端点上下文,包含请求体、请求头等信息。</param>
|
/// <param name="ctx">服务端点上下文,包含请求体、请求头等信息。</param>
|
||||||
/// <returns>包含 AccessToken、RefreshToken 及过期时间的认证响应。</returns>
|
/// <returns>包含 AccessToken、RefreshToken 及过期时间的认证响应。</returns>
|
||||||
public async Task<object?> LoginAsync(ServiceEndpointContext ctx)
|
public async Task<IApiResponse> LoginAsync(ApiLoginRequest request, ServiceEndpointContext ctx)
|
||||||
{
|
{
|
||||||
var request = Deserialize<ApiLoginRequest>(ctx.Body);
|
if (string.IsNullOrWhiteSpace(request.Account))
|
||||||
if (string.IsNullOrWhiteSpace(request?.Account))
|
|
||||||
{
|
{
|
||||||
ctx.StatusCode = 400;
|
ctx.StatusCode = 400;
|
||||||
return ResponseHelper.Failure(400, "账号不能为空");
|
return ResponseHelper.Failure(400, "账号不能为空");
|
||||||
@@ -72,11 +65,10 @@ namespace Avalonia_API.Authentication
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ctx">服务端点上下文,包含请求体中的 RefreshToken。</param>
|
/// <param name="ctx">服务端点上下文,包含请求体中的 RefreshToken。</param>
|
||||||
/// <returns>新的 Token 对;若 Refresh Token 无效则返回 401 错误。</returns>
|
/// <returns>新的 Token 对;若 Refresh Token 无效则返回 401 错误。</returns>
|
||||||
public async Task<object?> RefreshAsync(ServiceEndpointContext ctx)
|
public async Task<IApiResponse> RefreshAsync(ApiRefreshTokenRequest request, ServiceEndpointContext ctx)
|
||||||
{
|
{
|
||||||
var request = Deserialize<ApiRefreshTokenRequest>(ctx.Body);
|
|
||||||
var rotated = await refreshTokenService.RotateAsync(
|
var rotated = await refreshTokenService.RotateAsync(
|
||||||
request?.RefreshToken,
|
request.RefreshToken,
|
||||||
ctx.GetHeader("User-Agent"),
|
ctx.GetHeader("User-Agent"),
|
||||||
GetRemoteIpAddress(ctx));
|
GetRemoteIpAddress(ctx));
|
||||||
|
|
||||||
@@ -109,26 +101,12 @@ namespace Avalonia_API.Authentication
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ctx">服务端点上下文,包含请求体中的 RefreshToken。</param>
|
/// <param name="ctx">服务端点上下文,包含请求体中的 RefreshToken。</param>
|
||||||
/// <returns>登出成功的响应。</returns>
|
/// <returns>登出成功的响应。</returns>
|
||||||
public async Task<object?> LogoutAsync(ServiceEndpointContext ctx)
|
public async Task<IApiResponse> LogoutAsync(ApiLogoutRequest request, ServiceEndpointContext ctx)
|
||||||
{
|
{
|
||||||
var request = Deserialize<ApiLogoutRequest>(ctx.Body);
|
await refreshTokenService.RevokeAsync(request.RefreshToken);
|
||||||
await refreshTokenService.RevokeAsync(request?.RefreshToken);
|
|
||||||
return ResponseHelper.Succeed("退出成功");
|
return ResponseHelper.Succeed("退出成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 将 JSON 请求体反序列化为指定类型。
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T">目标类型。</typeparam>
|
|
||||||
/// <param name="body">JSON 请求体字符串,可为空。</param>
|
|
||||||
/// <returns>反序列化后的对象;若 body 为空则返回默认值。</returns>
|
|
||||||
private static T? Deserialize<T>(string? body)
|
|
||||||
{
|
|
||||||
return string.IsNullOrWhiteSpace(body)
|
|
||||||
? default
|
|
||||||
: JsonSerializer.Deserialize<T>(body, JsonOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从上下文的 Items 中提取 ASP.NET Core HttpContext,并获取客户端远程 IP 地址。
|
/// 从上下文的 Items 中提取 ASP.NET Core HttpContext,并获取客户端远程 IP 地址。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
namespace Avalonia_API.Authentication
|
namespace FileShare_API.Authentication
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// JWT 鉴权配置选项,从 appsettings.json 的 Jwt 节绑定。
|
/// JWT 鉴权配置选项,从 appsettings.json 的 Jwt 节绑定。
|
||||||
@@ -8,12 +8,12 @@ namespace Avalonia_API.Authentication
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置 Token 签发者。
|
/// 获取或设置 Token 签发者。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Issuer { get; set; } = "Avalonia-API";
|
public string Issuer { get; set; } = "FileShare-API";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置 Token 受众。
|
/// 获取或设置 Token 受众。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Audience { get; set; } = "Avalonia-Client";
|
public string Audience { get; set; } = "FileShare-Client";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置签名密钥(至少 32 字节)。
|
/// 获取或设置签名密钥(至少 32 字节)。
|
||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
using Avalonia_EFCore.Models;
|
using FileShare_EFCore.Models;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Avalonia_API.Authentication
|
namespace FileShare_API.Authentication
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// JWT Token 服务,负责创建包含用户声明和角色的 Access Token。
|
/// JWT Token 服务,负责创建包含用户声明和角色的 Access Token。
|
||||||
+3
-3
@@ -1,11 +1,11 @@
|
|||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Avalonia_EFCore.Models;
|
using FileShare_EFCore.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Avalonia_API.Authentication
|
namespace FileShare_API.Authentication
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refresh Token 服务,负责创建、查找、撤销和轮换 Refresh Token,
|
/// Refresh Token 服务,负责创建、查找、撤销和轮换 Refresh Token,
|
||||||
+22
-10
@@ -1,17 +1,18 @@
|
|||||||
using Avalonia_API.Authentication;
|
using FileShare_API.Authentication;
|
||||||
using Avalonia_API.Services;
|
using FileShare_API.Services;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Avalonia_Services.Core;
|
using FileShare_Services.Core;
|
||||||
using Avalonia_Services.Endpoints;
|
using FileShare_Services.Endpoints;
|
||||||
using Avalonia_Services.Services;
|
using FileShare_Services.Services;
|
||||||
using Avalonia_Services.Services.AuthService;
|
using FileShare_Services.Services.AuthService;
|
||||||
using Avalonia_Services.Services.FileLibrary;
|
using FileShare_Services.Services.FileLibrary;
|
||||||
|
using FileShare_Services.Services.QrCode;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Avalonia_API.Configuration
|
namespace FileShare_API.Configuration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// API 项目服务配置扩展类,负责注册数据库、鉴权、业务服务和统一端点。
|
/// API 项目服务配置扩展类,负责注册数据库、鉴权、业务服务和统一端点。
|
||||||
@@ -20,7 +21,7 @@ namespace Avalonia_API.Configuration
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 注册统一端点及其依赖的服务(含数据库)。
|
/// 注册统一端点及其依赖的服务(含数据库)。
|
||||||
/// 所有业务端点定义在 Avalonia-Services/Endpoints/AppEndpoints.cs。
|
/// 所有业务端点定义在 FileShare-Services/Endpoints/AppEndpoints.cs。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static IServiceCollection AddUnifiedApiServices(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddUnifiedApiServices(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
@@ -39,8 +40,19 @@ namespace Avalonia_API.Configuration
|
|||||||
|
|
||||||
// ---- 业务服务 ----
|
// ---- 业务服务 ----
|
||||||
services.AddScoped<WeatherForecastService>();
|
services.AddScoped<WeatherForecastService>();
|
||||||
|
var thumbnailOptions = configuration
|
||||||
|
.GetSection(nameof(ThumbnailStorageOptions))
|
||||||
|
.Get<ThumbnailStorageOptions>()
|
||||||
|
?? new ThumbnailStorageOptions();
|
||||||
|
services.AddSingleton(thumbnailOptions);
|
||||||
|
services.AddSingleton<IVideoThumbnailService>(sp =>
|
||||||
|
new VideoThumbnailService(sp.GetRequiredService<ThumbnailStorageOptions>()));
|
||||||
|
services.AddScoped<IThumbnailStreamService, ThumbnailStreamService>();
|
||||||
services.AddScoped<IFileLibraryService, FileLibraryService>();
|
services.AddScoped<IFileLibraryService, FileLibraryService>();
|
||||||
services.AddScoped<IFileLibraryEndpointService, FileLibraryEndpointService>();
|
services.AddScoped<IFileLibraryEndpointService, FileLibraryEndpointService>();
|
||||||
|
services.AddScoped<IFileStreamService, FileStreamService>();
|
||||||
|
services.AddScoped<IQrCodeService, QrCodeService>();
|
||||||
|
services.Configure<FileLibraryScanOptions>(configuration.GetSection(nameof(FileLibraryScanOptions)));
|
||||||
services.AddHostedService<FileLibraryScanHostedService>();
|
services.AddHostedService<FileLibraryScanHostedService>();
|
||||||
services.AddDataProtection()
|
services.AddDataProtection()
|
||||||
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine(AppContext.BaseDirectory, "data-protection-keys")));
|
.PersistKeysToFileSystem(new DirectoryInfo(Path.Combine(AppContext.BaseDirectory, "data-protection-keys")));
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
using FileShare_Services.Services.FileLibrary;
|
||||||
|
|
||||||
|
namespace FileShare_API.Extensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// API-only raw file stream endpoints used by browser media elements.
|
||||||
|
/// </summary>
|
||||||
|
public static class FileStreamEndpointExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Map the media URL emitted by <see cref="FileRecordDto"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static IEndpointRouteBuilder MapFileStreamEndpoints(this IEndpointRouteBuilder app)
|
||||||
|
{
|
||||||
|
app.MapMethods(
|
||||||
|
"/api/files/{id:int}/stream",
|
||||||
|
["GET", "HEAD"],
|
||||||
|
async (int id, IFileStreamService fileStreamService, HttpContext httpContext) =>
|
||||||
|
{
|
||||||
|
var fileResponse = await fileStreamService.GetFileStreamAsync(id);
|
||||||
|
if (fileResponse is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream = System.IO.File.Open(
|
||||||
|
fileResponse.FilePath,
|
||||||
|
FileMode.Open,
|
||||||
|
FileAccess.Read,
|
||||||
|
FileShare.ReadWrite);
|
||||||
|
|
||||||
|
httpContext.Response.Headers.ContentDisposition =
|
||||||
|
$"inline; filename=\"{Uri.EscapeDataString(fileResponse.FileName)}\"";
|
||||||
|
httpContext.Response.Headers.AcceptRanges = "bytes";
|
||||||
|
httpContext.Response.Headers.CacheControl = "public, max-age=3600";
|
||||||
|
|
||||||
|
return Results.File(
|
||||||
|
stream,
|
||||||
|
contentType: fileResponse.ContentType,
|
||||||
|
lastModified: fileResponse.LastModified,
|
||||||
|
enableRangeProcessing: true);
|
||||||
|
})
|
||||||
|
.WithName("StreamManagedFileById")
|
||||||
|
.WithTags("FileLibrary");
|
||||||
|
|
||||||
|
app.MapMethods(
|
||||||
|
"/api/thumbnails/{id:int}",
|
||||||
|
["GET", "HEAD"],
|
||||||
|
async (int id, IThumbnailStreamService thumbnailStreamService, HttpContext httpContext) =>
|
||||||
|
{
|
||||||
|
var thumbnail = await thumbnailStreamService.GetThumbnailAsync(id);
|
||||||
|
if (thumbnail is null)
|
||||||
|
{
|
||||||
|
return Results.NotFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
var stream = System.IO.File.Open(
|
||||||
|
thumbnail.FilePath,
|
||||||
|
FileMode.Open,
|
||||||
|
FileAccess.Read,
|
||||||
|
FileShare.ReadWrite);
|
||||||
|
|
||||||
|
httpContext.Response.Headers.ContentDisposition =
|
||||||
|
$"inline; filename=\"{Uri.EscapeDataString(thumbnail.FileName)}\"";
|
||||||
|
httpContext.Response.Headers.CacheControl = "public, max-age=3600";
|
||||||
|
|
||||||
|
return Results.File(
|
||||||
|
stream,
|
||||||
|
contentType: thumbnail.ContentType,
|
||||||
|
lastModified: thumbnail.LastModified);
|
||||||
|
})
|
||||||
|
.WithName("StreamManagedThumbnailById")
|
||||||
|
.WithTags("FileLibrary");
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+31
-5
@@ -1,14 +1,14 @@
|
|||||||
using Avalonia_Services.Core;
|
using FileShare_Services.Core;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using AspNetCoreFilterContext = Microsoft.AspNetCore.Http.EndpointFilterInvocationContext;
|
using AspNetCoreFilterContext = Microsoft.AspNetCore.Http.EndpointFilterInvocationContext;
|
||||||
using AspNetCoreFilterDelegate = Microsoft.AspNetCore.Http.EndpointFilterDelegate;
|
using AspNetCoreFilterDelegate = Microsoft.AspNetCore.Http.EndpointFilterDelegate;
|
||||||
// 解决与 ASP.NET Core 同名类型的冲突
|
// 解决与 ASP.NET Core 同名类型的冲突
|
||||||
using UnifiedFilter = Avalonia_Services.Core.IEndpointFilter;
|
using UnifiedFilter = FileShare_Services.Core.IEndpointFilter;
|
||||||
|
|
||||||
namespace Avalonia_API.Extensions
|
namespace FileShare_API.Extensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将 Avalonia-Services 的统一端点映射到 ASP.NET Core Minimal API。
|
/// 将 FileShare-Services 的统一端点映射到 ASP.NET Core Minimal API。
|
||||||
/// 支持鉴权、过滤器、中间件的完整 ASP.NET Core 管道。
|
/// 支持鉴权、过滤器、中间件的完整 ASP.NET Core 管道。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class UnifiedEndpointExtensions
|
public static class UnifiedEndpointExtensions
|
||||||
@@ -81,7 +81,8 @@ namespace Avalonia_API.Extensions
|
|||||||
routeHandlerBuilder.WithSummary(endpoint.OpenApiSummary);
|
routeHandlerBuilder.WithSummary(endpoint.OpenApiSummary);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (endpoint.OpenApiRequestType is not null)
|
if (endpoint.OpenApiRequestType is not null
|
||||||
|
&& endpoint.HttpMethod is "POST" or "PUT")
|
||||||
{
|
{
|
||||||
routeHandlerBuilder.Accepts(endpoint.OpenApiRequestType, "application/json");
|
routeHandlerBuilder.Accepts(endpoint.OpenApiRequestType, "application/json");
|
||||||
}
|
}
|
||||||
@@ -146,6 +147,23 @@ namespace Avalonia_API.Extensions
|
|||||||
httpContext.Response.Headers[kvp.Key] = kvp.Value;
|
httpContext.Response.Headers[kvp.Key] = kvp.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result is FileStreamResponse fileResponse)
|
||||||
|
{
|
||||||
|
if (!System.IO.File.Exists(fileResponse.FilePath))
|
||||||
|
return Results.NotFound();
|
||||||
|
|
||||||
|
var stream = System.IO.File.Open(fileResponse.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
|
||||||
|
httpContext.Response.Headers.ContentDisposition = $"inline; filename=\"{Uri.EscapeDataString(fileResponse.FileName)}\"";
|
||||||
|
httpContext.Response.Headers.AcceptRanges = "bytes";
|
||||||
|
httpContext.Response.Headers.CacheControl = "public, max-age=3600";
|
||||||
|
|
||||||
|
return Results.File(
|
||||||
|
stream,
|
||||||
|
contentType: fileResponse.ContentType,
|
||||||
|
lastModified: fileResponse.LastModified,
|
||||||
|
enableRangeProcessing: true);
|
||||||
|
}
|
||||||
|
|
||||||
return result is not null ? Results.Json(result) : Results.Ok();
|
return result is not null ? Results.Json(result) : Results.Ok();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -175,6 +193,14 @@ namespace Avalonia_API.Extensions
|
|||||||
ctx.Query[query.Key] = query.Value.ToString();
|
ctx.Query[query.Key] = query.Value.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var routeValue in httpContext.Request.RouteValues)
|
||||||
|
{
|
||||||
|
if (routeValue.Value is not null)
|
||||||
|
{
|
||||||
|
ctx.RouteValues[routeValue.Key] = routeValue.Value.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (httpContext.Request.ContentLength > 0)
|
if (httpContext.Request.ContentLength > 0)
|
||||||
{
|
{
|
||||||
using var reader = new StreamReader(httpContext.Request.Body);
|
using var reader = new StreamReader(httpContext.Request.Body);
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<RootNamespace>FileShare_API</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.5" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.7">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="8.1.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\FileShare-Services\FileShare-Services.csproj" />
|
||||||
|
<ProjectReference Include="..\FileShare-Common\FileShare-Common.csproj" />
|
||||||
|
<ProjectReference Include="..\FileShare-EFCore\FileShare-EFCore.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Controllers\" />
|
||||||
|
<Content Include="..\tools\ffmpeg\bin\ffmpeg.exe" Link="tools\ffmpeg\bin\ffmpeg.exe" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
|
||||||
|
<Content Include="..\tools\ffmpeg\bin\ffprobe.exe" Link="tools\ffmpeg\bin\ffprobe.exe" CopyToOutputDirectory="PreserveNewest" CopyToPublishDirectory="PreserveNewest" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="RestoreFrontendPackages" BeforeTargets="Build" Condition="'$(SkipFrontendBuild)' != 'true' And Exists('..\FileShare-Web-VUE\package.json') And !Exists('..\FileShare-Web-VUE\node_modules')">
|
||||||
|
<Message Importance="high" Text="Restoring FileShare-Web-VUE npm packages..." />
|
||||||
|
<Exec WorkingDirectory="..\FileShare-Web-VUE" Command="npm.cmd install" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="BuildFrontend" BeforeTargets="Build" DependsOnTargets="RestoreFrontendPackages" Condition="'$(SkipFrontendBuild)' != 'true' And Exists('..\FileShare-Web-VUE\package.json')">
|
||||||
|
<Message Importance="high" Text="Building FileShare-Web-VUE into FileShare-API/wwwroot..." />
|
||||||
|
<Exec WorkingDirectory="..\FileShare-Web-VUE" Command="npm.cmd run build-only" />
|
||||||
|
<RemoveDir Directories="wwwroot" />
|
||||||
|
<MakeDir Directories="wwwroot" />
|
||||||
|
<ItemGroup>
|
||||||
|
<FrontendDist Include="..\FileShare-Web-VUE\dist\**\*.*" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Copy SourceFiles="@(FrontendDist)" DestinationFiles="@(FrontendDist->'wwwroot\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="false" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup>
|
||||||
|
<ActiveDebugProfile>http</ActiveDebugProfile>
|
||||||
|
<NameOfLastUsedPublishProfile>D:\Project\FileShare\FileShare-API\Properties\PublishProfiles\FolderProfile.pubxml</NameOfLastUsedPublishProfile>
|
||||||
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
|
||||||
<ActiveDebugProfile>Avalonia-PC</ActiveDebugProfile>
|
|
||||||
</PropertyGroup>
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
@FileShare_API_HostAddress = http://localhost:5206
|
||||||
|
|
||||||
|
GET {{FileShare_API_HostAddress}}/weatherforecast/
|
||||||
|
Accept: application/json
|
||||||
|
|
||||||
|
###
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
using Avalonia_API.Configuration;
|
using FileShare_API.Configuration;
|
||||||
using Avalonia_API.Extensions;
|
using FileShare_API.Extensions;
|
||||||
using Avalonia_Common.Infrastructure;
|
using FileShare_Common.Infrastructure;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Avalonia_Services.Core;
|
using FileShare_Services.Core;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
|
|
||||||
// 初始化日志系统
|
// 初始化日志系统
|
||||||
Log.Logger = LoggingConfiguration.CreateDefaultLogger(logDir: "logs");
|
Log.Logger = LoggingConfiguration.CreateDefaultLogger(logDir: "logs");
|
||||||
Log.Information("Avalonia-API 正在启动...");
|
Log.Information("FileShare-API 正在启动...");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
// 配置 Kestrel 监听所有本机 IP
|
// 配置 Kestrel 监听所有本机 IP(端口从 Server 配置节读取)
|
||||||
builder.WebHost.UseUrls("http://0.0.0.0:5206", "https://0.0.0.0:7165");
|
var httpPort = builder.Configuration.GetValue<int>("Server:HttpPort", 5206);
|
||||||
|
var httpsPort = builder.Configuration.GetValue<int>("Server:HttpsPort", 7165);
|
||||||
|
builder.WebHost.UseUrls($"http://0.0.0.0:{httpPort}", $"https://0.0.0.0:{httpsPort}");
|
||||||
|
|
||||||
// 使用 Serilog 作为日志提供程序
|
// 使用 Serilog 作为日志提供程序
|
||||||
builder.Host.UseSerilog();
|
builder.Host.UseSerilog();
|
||||||
@@ -30,7 +32,7 @@ try
|
|||||||
.AllowAnyMethod());
|
.AllowAnyMethod());
|
||||||
});
|
});
|
||||||
|
|
||||||
// 注册统一端点及业务服务(入口在 Avalonia-Services/Endpoints/AppEndpoints.cs)
|
// 注册统一端点及业务服务(入口在 FileShare-Services/Endpoints/AppEndpoints.cs)
|
||||||
builder.Services.AddUnifiedApiServices(builder.Configuration);
|
builder.Services.AddUnifiedApiServices(builder.Configuration);
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
@@ -46,7 +48,7 @@ try
|
|||||||
app.MapOpenApi();
|
app.MapOpenApi();
|
||||||
app.UseSwaggerUI(options =>
|
app.UseSwaggerUI(options =>
|
||||||
{
|
{
|
||||||
options.SwaggerEndpoint("/openapi/v1.json", "Avalonia API v1");
|
options.SwaggerEndpoint("/openapi/v1.json", "FileShare API v1");
|
||||||
options.RoutePrefix = "swagger";
|
options.RoutePrefix = "swagger";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -67,7 +69,7 @@ try
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Fatal(ex, "Avalonia-API 启动失败");
|
Log.Fatal(ex, "FileShare-API 启动失败");
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using FileShare_Services.Services.FileLibrary;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace FileShare_API.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 文件库定时扫描后台服务,按配置间隔检查是否有需要扫描的文件库目录。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FileLibraryScanHostedService(
|
||||||
|
IServiceScopeFactory scopeFactory,
|
||||||
|
IOptions<FileLibraryScanOptions> options,
|
||||||
|
ILogger<FileLibraryScanHostedService> logger)
|
||||||
|
: BackgroundService
|
||||||
|
{
|
||||||
|
/// <summary>后台服务检查到期扫描任务的轮询间隔。</summary>
|
||||||
|
private readonly TimeSpan _interval = TimeSpan.FromMinutes(Math.Max(1, options.Value.PollingIntervalMinutes));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 启动扫描循环,首次立即执行,之后按配置的轮询间隔重复执行。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stoppingToken">应用关闭时触发的取消令牌。</param>
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
logger.LogInformation("文件库定时扫描服务已启动,轮询间隔 {IntervalMinutes} 分钟。", _interval.TotalMinutes);
|
||||||
|
await ScanAsync(stoppingToken);
|
||||||
|
|
||||||
|
using var timer = new PeriodicTimer(_interval);
|
||||||
|
while (await timer.WaitForNextTickAsync(stoppingToken))
|
||||||
|
{
|
||||||
|
await ScanAsync(stoppingToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建临时作用域并调用 <see cref="IFileLibraryService.ScanDueRootsAsync"/> 执行一次到期扫描。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="cancellationToken">取消令牌。</param>
|
||||||
|
private async Task ScanAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var startedAt = DateTime.UtcNow;
|
||||||
|
logger.LogInformation("文件库定时扫描轮询开始。");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await using var scope = scopeFactory.CreateAsyncScope();
|
||||||
|
var scanner = scope.ServiceProvider.GetRequiredService<IFileLibraryService>();
|
||||||
|
await scanner.ScanDueRootsAsync(cancellationToken);
|
||||||
|
logger.LogInformation(
|
||||||
|
"文件库定时扫描轮询完成,耗时 {ElapsedMilliseconds} ms。",
|
||||||
|
(DateTime.UtcNow - startedAt).TotalMilliseconds);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
logger.LogInformation("文件库定时扫描轮询已取消。");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
logger.LogWarning(ex, "文件库定时扫描失败。");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
namespace FileShare_API.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 文件库定时扫描后台服务配置。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FileLibraryScanOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 后台服务检查到期文件库目录的轮询间隔分钟数。
|
||||||
|
/// </summary>
|
||||||
|
public int PollingIntervalMinutes { get; set; } = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,9 +6,13 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
|
"Server": {
|
||||||
|
"HttpPort": 5206,
|
||||||
|
"HttpsPort": 7165
|
||||||
|
},
|
||||||
"Jwt": {
|
"Jwt": {
|
||||||
"Issuer": "Avalonia-API",
|
"Issuer": "FileShare-API",
|
||||||
"Audience": "Avalonia-Client",
|
"Audience": "FileShare-Client",
|
||||||
"SigningKey": "change-this-development-signing-key-at-least-32-bytes",
|
"SigningKey": "change-this-development-signing-key-at-least-32-bytes",
|
||||||
"AccessTokenMinutes": 60,
|
"AccessTokenMinutes": 60,
|
||||||
"RefreshTokenDays": 30
|
"RefreshTokenDays": 30
|
||||||
@@ -20,5 +24,13 @@
|
|||||||
"RecreateDatabase": false,
|
"RecreateDatabase": false,
|
||||||
"EnableDetailedLog": false,
|
"EnableDetailedLog": false,
|
||||||
"Timeout": 30
|
"Timeout": 30
|
||||||
|
},
|
||||||
|
"ThumbnailStorageOptions": {
|
||||||
|
"RootPath": "thumbnails",
|
||||||
|
"FfmpegPath": "tools/ffmpeg/bin/ffmpeg.exe",
|
||||||
|
"FfprobePath": "tools/ffmpeg/bin/ffprobe.exe"
|
||||||
|
},
|
||||||
|
"FileLibraryScanOptions": {
|
||||||
|
"PollingIntervalMinutes": 5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,25 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
namespace Avalonia_Common.Core
|
namespace FileShare_Common.Core
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 统一端点响应契约。
|
||||||
|
/// </summary>
|
||||||
|
public interface IApiResponse
|
||||||
|
{
|
||||||
|
/// <summary>是否成功。</summary>
|
||||||
|
bool Success { get; }
|
||||||
|
|
||||||
|
/// <summary>业务状态码。</summary>
|
||||||
|
int Code { get; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 统一 API 返回格式。
|
/// 统一 API 返回格式。
|
||||||
/// 所有接口的返回都包装为此格式,确保前端收到一致的数据结构。
|
/// 所有接口的返回都包装为此格式,确保前端收到一致的数据结构。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">业务数据类型</typeparam>
|
/// <typeparam name="T">业务数据类型</typeparam>
|
||||||
public class ApiResponse<T>
|
public class ApiResponse<T> : IApiResponse
|
||||||
{
|
{
|
||||||
/// <summary>是否成功</summary>
|
/// <summary>是否成功</summary>
|
||||||
[JsonPropertyName("success")]
|
[JsonPropertyName("success")]
|
||||||
@@ -113,7 +125,7 @@ namespace Avalonia_Common.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 分页返回格式
|
/// 分页返回格式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PagedResponse<T>
|
public class PagedResponse<T> : IApiResponse
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取或设置操作是否成功。
|
/// 获取或设置操作是否成功。
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<RootNamespace>Avalonia_Common</RootNamespace>
|
<RootNamespace>FileShare_Common</RootNamespace>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
using Serilog;
|
using Serilog;
|
||||||
using Serilog.Events;
|
using Serilog.Events;
|
||||||
|
|
||||||
namespace Avalonia_Common.Infrastructure
|
namespace FileShare_Common.Infrastructure
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Serilog 日志配置 —— 可在 Avalonia-API 和 Avalonia-PC 中共享。
|
/// Serilog 日志配置 —— 可在 FileShare-API 和 FileShare-PC 中共享。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class LoggingConfiguration
|
public static class LoggingConfiguration
|
||||||
{
|
{
|
||||||
+27
-4
@@ -1,12 +1,12 @@
|
|||||||
using Avalonia_EFCore.Models;
|
using FileShare_EFCore.Models;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Database
|
namespace FileShare_EFCore.Database
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 应用数据库上下文 —— 继承自 Avalonia-EFCore 的 AppDbContext。
|
/// 应用数据库上下文 —— 继承自 FileShare-EFCore 的 AppDbContext。
|
||||||
/// 所有业务实体在此注册 DbSet。
|
/// 所有业务实体在此注册 DbSet。
|
||||||
/// 这是 Avalonia-API 和 Avalonia-PC 共用的具体数据上下文。
|
/// 这是 FileShare-API 和 FileShare-PC 共用的具体数据上下文。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AppDataContext(DatabaseConfiguration dbConfig) : AppDbContext(dbConfig)
|
public class AppDataContext(DatabaseConfiguration dbConfig) : AppDbContext(dbConfig)
|
||||||
{
|
{
|
||||||
@@ -25,6 +25,9 @@ namespace Avalonia_EFCore.Database
|
|||||||
/// <summary>文件库文件记录数据</summary>
|
/// <summary>文件库文件记录数据</summary>
|
||||||
public DbSet<ManagedFileRecord> ManagedFileRecords => Set<ManagedFileRecord>();
|
public DbSet<ManagedFileRecord> ManagedFileRecords => Set<ManagedFileRecord>();
|
||||||
|
|
||||||
|
/// <summary>文件缩略图映射数据</summary>
|
||||||
|
public DbSet<ManagedThumbnailMap> ManagedThumbnailMaps => Set<ManagedThumbnailMap>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 配置实体映射,包括主键、索引和属性约束。
|
/// 配置实体映射,包括主键、索引和属性约束。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -66,8 +69,11 @@ namespace Avalonia_EFCore.Database
|
|||||||
{
|
{
|
||||||
entity.HasKey(e => e.Id).HasName("pk-managed-file-record");
|
entity.HasKey(e => e.Id).HasName("pk-managed-file-record");
|
||||||
entity.HasIndex(e => e.LibraryRootId).HasDatabaseName("idx-managed-file-record-root-id");
|
entity.HasIndex(e => e.LibraryRootId).HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
entity.HasIndex(e => e.ThumbnailId).HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
entity.HasIndex(e => e.AbsolutePath).IsUnique().HasDatabaseName("idx-managed-file-record-absolute-path");
|
entity.HasIndex(e => e.AbsolutePath).IsUnique().HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
entity.HasIndex(e => new { e.MediaType, e.Exists }).HasDatabaseName("idx-managed-file-record-media-type-exists");
|
entity.HasIndex(e => new { e.MediaType, e.Exists }).HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
entity.HasIndex(e => e.LastPlayedAt).HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
entity.HasIndex(e => e.FileCreationTimeUtc).HasDatabaseName("idx-managed-file-record-file-creation-time");
|
||||||
entity.Property(e => e.FileName).HasMaxLength(260);
|
entity.Property(e => e.FileName).HasMaxLength(260);
|
||||||
entity.Property(e => e.RelativePath).HasMaxLength(1024);
|
entity.Property(e => e.RelativePath).HasMaxLength(1024);
|
||||||
entity.Property(e => e.AbsolutePath).HasMaxLength(2048);
|
entity.Property(e => e.AbsolutePath).HasMaxLength(2048);
|
||||||
@@ -78,6 +84,23 @@ namespace Avalonia_EFCore.Database
|
|||||||
.WithMany(e => e.Files)
|
.WithMany(e => e.Files)
|
||||||
.HasForeignKey(e => e.LibraryRootId)
|
.HasForeignKey(e => e.LibraryRootId)
|
||||||
.OnDelete(DeleteBehavior.Cascade);
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
|
entity.HasOne(e => e.Thumbnail)
|
||||||
|
.WithMany(e => e.Files)
|
||||||
|
.HasForeignKey(e => e.ThumbnailId)
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity<ManagedThumbnailMap>(entity =>
|
||||||
|
{
|
||||||
|
entity.HasKey(e => e.Id).HasName("pk-managed-thumbnail-map");
|
||||||
|
entity.HasIndex(e => e.LibraryRootId).HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
entity.HasIndex(e => e.RelativePath).IsUnique().HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
entity.Property(e => e.RelativePath).HasMaxLength(1024);
|
||||||
|
entity.Property(e => e.ContentType).HasMaxLength(100);
|
||||||
|
entity.HasOne(e => e.LibraryRoot)
|
||||||
|
.WithMany(e => e.Thumbnails)
|
||||||
|
.HasForeignKey(e => e.LibraryRootId)
|
||||||
|
.OnDelete(DeleteBehavior.Cascade);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+22
-6
@@ -1,6 +1,6 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Design;
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Database
|
namespace FileShare_EFCore.Database
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设计时 DbContext 工厂,用于 EF Core 迁移工具生成迁移代码。
|
/// 设计时 DbContext 工厂,用于 EF Core 迁移工具生成迁移代码。
|
||||||
@@ -58,8 +58,18 @@ namespace Avalonia_EFCore.Database
|
|||||||
=> new(DesignTimeDatabaseConfiguration.Create(args, DatabaseProvider.MySQL));
|
=> new(DesignTimeDatabaseConfiguration.Create(args, DatabaseProvider.MySQL));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 为设计时工具(dotnet ef migrations)提供数据库连接配置。
|
||||||
|
/// </summary>
|
||||||
internal static class DesignTimeDatabaseConfiguration
|
internal static class DesignTimeDatabaseConfiguration
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 解析命令行参数中的 --provider 选项,创建对应的 <see cref="DatabaseConfiguration"/>。
|
||||||
|
/// 未指定提供程序时默认使用 SQLite。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">命令行参数。</param>
|
||||||
|
/// <param name="defaultProvider">未指定时的默认数据库提供程序。</param>
|
||||||
|
/// <returns>对应数据库提供程序的配置。</returns>
|
||||||
public static DatabaseConfiguration Create(string[] args, DatabaseProvider defaultProvider = DatabaseProvider.SQLite)
|
public static DatabaseConfiguration Create(string[] args, DatabaseProvider defaultProvider = DatabaseProvider.SQLite)
|
||||||
{
|
{
|
||||||
DatabaseProviderRegistry.RegisterDefaults();
|
DatabaseProviderRegistry.RegisterDefaults();
|
||||||
@@ -67,14 +77,20 @@ namespace Avalonia_EFCore.Database
|
|||||||
var provider = GetProvider(args) ?? defaultProvider;
|
var provider = GetProvider(args) ?? defaultProvider;
|
||||||
return provider switch
|
return provider switch
|
||||||
{
|
{
|
||||||
DatabaseProvider.SQLite => DatabaseConfiguration.ForSQLite("avalonia-api.db"),
|
DatabaseProvider.SQLite => DatabaseConfiguration.ForSQLite("fileshare-api.db"),
|
||||||
DatabaseProvider.SqlServer => DatabaseConfiguration.ForSqlServer("(localdb)\\MSSQLLocalDB", "AvaloniaApi"),
|
DatabaseProvider.SqlServer => DatabaseConfiguration.ForSqlServer("(localdb)\\MSSQLLocalDB", "FileShareApi"),
|
||||||
DatabaseProvider.PostgreSQL => DatabaseConfiguration.ForPostgreSQL("localhost", "avalonia_api", "postgres", "postgres"),
|
DatabaseProvider.PostgreSQL => DatabaseConfiguration.ForPostgreSQL("localhost", "fileshare_api", "postgres", "postgres"),
|
||||||
DatabaseProvider.MySQL => DatabaseConfiguration.ForMySQL("localhost", "avalonia_api", "root", "root"),
|
DatabaseProvider.MySQL => DatabaseConfiguration.ForMySQL("localhost", "fileshare_api", "root", "root"),
|
||||||
_ => DatabaseConfiguration.ForSQLite("avalonia-api.db"),
|
_ => DatabaseConfiguration.ForSQLite("fileshare-api.db"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从命令行参数中解析 --provider 选项的值。
|
||||||
|
/// 支持 <c>--provider sqlite</c> 和 <c>--provider=sqlite</c> 两种格式。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="args">命令行参数。</param>
|
||||||
|
/// <returns>解析到的数据库提供程序,未指定或无法识别时返回 null。</returns>
|
||||||
private static DatabaseProvider? GetProvider(string[] args)
|
private static DatabaseProvider? GetProvider(string[] args)
|
||||||
{
|
{
|
||||||
for (var i = 0; i < args.Length; i++)
|
for (var i = 0; i < args.Length; i++)
|
||||||
+1
-1
@@ -4,7 +4,7 @@ using System.Linq;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Database
|
namespace FileShare_EFCore.Database
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 应用数据库上下文基类 —— 自动根据 DatabaseConfiguration 选择数据库提供程序。
|
/// 应用数据库上下文基类 —— 自动根据 DatabaseConfiguration 选择数据库提供程序。
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Avalonia_EFCore.Database
|
namespace FileShare_EFCore.Database
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 支持的数据库提供程序类型。
|
/// 支持的数据库提供程序类型。
|
||||||
+7
-1
@@ -1,7 +1,7 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Database
|
namespace FileShare_EFCore.Database
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据库服务注册扩展 —— 在 Program.cs 中一行配置数据库。
|
/// 数据库服务注册扩展 —— 在 Program.cs 中一行配置数据库。
|
||||||
@@ -40,6 +40,12 @@ namespace Avalonia_EFCore.Database
|
|||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据 <see cref="DatabaseConfiguration.Provider"/> 注册对应具体类型的 <see cref="AppDataContext"/> 实现。
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="services">服务集合。</param>
|
||||||
|
/// <param name="config">数据库配置。</param>
|
||||||
|
/// <exception cref="NotSupportedException">数据库提供程序未注册时抛出。</exception>
|
||||||
private static void AddProviderAppDataContext(this IServiceCollection services, DatabaseConfiguration config)
|
private static void AddProviderAppDataContext(this IServiceCollection services, DatabaseConfiguration config)
|
||||||
{
|
{
|
||||||
switch (config.Provider)
|
switch (config.Provider)
|
||||||
+4
-3
@@ -1,4 +1,4 @@
|
|||||||
using Avalonia_Common.Infrastructure;
|
using FileShare_Common.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -9,7 +9,7 @@ using System.Reflection;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Database
|
namespace FileShare_EFCore.Database
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据库管理器 —— 负责连接测试、自动迁移、种子数据、版本检查。
|
/// 数据库管理器 —— 负责连接测试、自动迁移、种子数据、版本检查。
|
||||||
@@ -95,8 +95,9 @@ namespace Avalonia_EFCore.Database
|
|||||||
{
|
{
|
||||||
return await _context.Database.CanConnectAsync();
|
return await _context.Database.CanConnectAsync();
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
AppLog.Warning("数据库连接测试失败 Provider={Provider} Error={Error}", _config.Provider, ex.Message);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-4
@@ -1,12 +1,12 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Database
|
namespace FileShare_EFCore.Database
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据库提供程序注册表 —— 统一注册所有支持的提供程序配置委托。
|
/// 数据库提供程序注册表 —— 统一注册所有支持的提供程序配置委托。
|
||||||
/// 具体使用哪个提供程序由各宿主项目决定:
|
/// 具体使用哪个提供程序由各宿主项目决定:
|
||||||
/// Avalonia-API:从 appsettings.json 的 DatabaseConfiguration 节读取;
|
/// FileShare-API:从 appsettings.json 的 DatabaseConfiguration 节读取;
|
||||||
/// Avalonia-PC :固定使用 SQLite。
|
/// FileShare-PC :固定使用 SQLite。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class DatabaseProviderRegistry
|
public static class DatabaseProviderRegistry
|
||||||
{
|
{
|
||||||
@@ -37,7 +37,7 @@ namespace Avalonia_EFCore.Database
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 注册所有内置提供程序的默认配置(四个包均已内置在 Avalonia-EFCore 中)。
|
/// 注册所有内置提供程序的默认配置(四个包均已内置在 FileShare-EFCore 中)。
|
||||||
/// 注册完成后由调用方根据自身需求选择具体的 <see cref="DatabaseProvider"/>。
|
/// 注册完成后由调用方根据自身需求选择具体的 <see cref="DatabaseProvider"/>。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static void RegisterDefaults()
|
public static void RegisterDefaults()
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
namespace Avalonia_EFCore.Database
|
namespace FileShare_EFCore.Database
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// SQLite 专用 DbContext,用于隔离 SQLite 迁移集。
|
/// SQLite 专用 DbContext,用于隔离 SQLite 迁移集。
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<RootNamespace>Avalonia_EFCore</RootNamespace>
|
<RootNamespace>FileShare_EFCore</RootNamespace>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Avalonia-Common\Avalonia-Common.csproj" />
|
<ProjectReference Include="..\FileShare-Common\FileShare-Common.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -8,7 +8,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.MySQL
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
{
|
{
|
||||||
[DbContext(typeof(MySqlAppDataContext))]
|
[DbContext(typeof(MySqlAppDataContext))]
|
||||||
[Migration("20260520082626_AutoMigration_20260520162543")]
|
[Migration("20260520082626_AutoMigration_20260520162543")]
|
||||||
@@ -22,7 +22,7 @@ namespace Avalonia_EFCore.Migrations.MySQL
|
|||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -82,7 +82,7 @@ namespace Avalonia_EFCore.Migrations.MySQL
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -127,7 +127,7 @@ namespace Avalonia_EFCore.Migrations.MySQL
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using MySql.EntityFrameworkCore.Metadata;
|
using MySql.EntityFrameworkCore.Metadata;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.MySQL
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AutoMigration_20260520162543 : Migration
|
public partial class AutoMigration_20260520162543 : Migration
|
||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -8,7 +8,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.MySQL
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
{
|
{
|
||||||
[DbContext(typeof(MySqlAppDataContext))]
|
[DbContext(typeof(MySqlAppDataContext))]
|
||||||
[Migration("20260520083306_AutoMigration_20260520163216")]
|
[Migration("20260520083306_AutoMigration_20260520163216")]
|
||||||
@@ -22,7 +22,7 @@ namespace Avalonia_EFCore.Migrations.MySQL
|
|||||||
.HasAnnotation("ProductVersion", "10.0.7")
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -82,7 +82,7 @@ namespace Avalonia_EFCore.Migrations.MySQL
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -133,7 +133,7 @@ namespace Avalonia_EFCore.Migrations.MySQL
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.MySQL
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AutoMigration_20260520163216 : Migration
|
public partial class AutoMigration_20260520163216 : Migration
|
||||||
+370
@@ -0,0 +1,370 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(MySqlAppDataContext))]
|
||||||
|
[Migration("20260522082856_AutoMigration_20260522162758")]
|
||||||
|
partial class AutoMigration_20260522162758
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("varchar(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("varchar(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("varchar(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("varchar(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("varchar(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<string>("ThumbnailPath")
|
||||||
|
.HasMaxLength(512)
|
||||||
|
.HasColumnType("varchar(512)")
|
||||||
|
.HasColumnName("thumbnail-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("varchar(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("varchar(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using MySql.EntityFrameworkCore.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AutoMigration_20260522162758 : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-library-root",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
|
||||||
|
path = table.Column<string>(type: "varchar(1024)", maxLength: 1024, nullable: false),
|
||||||
|
displayname = table.Column<string>(name: "display-name", type: "varchar(200)", maxLength: 200, nullable: false),
|
||||||
|
isenabled = table.Column<bool>(name: "is-enabled", type: "tinyint(1)", nullable: false),
|
||||||
|
isavailable = table.Column<bool>(name: "is-available", type: "tinyint(1)", nullable: false, defaultValue: true),
|
||||||
|
scanintervalminutes = table.Column<int>(name: "scan-interval-minutes", type: "int", nullable: false),
|
||||||
|
lastscanstartedat = table.Column<DateTime>(name: "last-scan-started-at", type: "datetime(6)", nullable: true),
|
||||||
|
lastscancompletedat = table.Column<DateTime>(name: "last-scan-completed-at", type: "datetime(6)", nullable: true),
|
||||||
|
lastscanerror = table.Column<string>(name: "last-scan-error", type: "varchar(2000)", maxLength: 2000, nullable: true),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "datetime(6)", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime(6)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-library-root", x => x.id);
|
||||||
|
},
|
||||||
|
comment: "文件库根目录")
|
||||||
|
.Annotation("MySQL:Charset", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-file-record",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
|
||||||
|
libraryrootid = table.Column<int>(name: "library-root-id", type: "int", nullable: false),
|
||||||
|
filename = table.Column<string>(name: "file-name", type: "varchar(260)", maxLength: 260, nullable: false),
|
||||||
|
relativepath = table.Column<string>(name: "relative-path", type: "varchar(1024)", maxLength: 1024, nullable: false),
|
||||||
|
absolutepath = table.Column<string>(name: "absolute-path", type: "varchar(2048)", maxLength: 2048, nullable: false),
|
||||||
|
extension = table.Column<string>(type: "varchar(32)", maxLength: 32, nullable: false),
|
||||||
|
sizebytes = table.Column<long>(name: "size-bytes", type: "bigint", nullable: false),
|
||||||
|
lastwritetimeutc = table.Column<DateTime>(name: "last-write-time-utc", type: "datetime(6)", nullable: false),
|
||||||
|
mediatype = table.Column<string>(name: "media-type", type: "varchar(20)", maxLength: 20, nullable: false),
|
||||||
|
contenttype = table.Column<string>(name: "content-type", type: "varchar(100)", maxLength: 100, nullable: false),
|
||||||
|
exists = table.Column<bool>(type: "tinyint(1)", nullable: false),
|
||||||
|
lastseenat = table.Column<DateTime>(name: "last-seen-at", type: "datetime(6)", nullable: false),
|
||||||
|
thumbnailpath = table.Column<string>(name: "thumbnail-path", type: "varchar(512)", maxLength: 512, nullable: true),
|
||||||
|
videoduration = table.Column<double>(name: "video-duration", type: "double", nullable: true),
|
||||||
|
lastplayedat = table.Column<DateTime>(name: "last-played-at", type: "datetime(6)", nullable: true),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "datetime(6)", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime(6)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-file-record", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-library-root_library-root-id",
|
||||||
|
column: x => x.libraryrootid,
|
||||||
|
principalTable: "managed-library-root",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
},
|
||||||
|
comment: "文件库文件记录")
|
||||||
|
.Annotation("MySQL:Charset", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-absolute-path",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "absolute-path",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-last-played-at",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "last-played-at");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-media-type-exists",
|
||||||
|
table: "managed-file-record",
|
||||||
|
columns: new[] { "media-type", "exists" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-root-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "library-root-id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-library-root-path",
|
||||||
|
table: "managed-library-root",
|
||||||
|
column: "path",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-library-root");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+444
@@ -0,0 +1,444 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(MySqlAppDataContext))]
|
||||||
|
[Migration("20260522084325_AddThumbnailMap")]
|
||||||
|
partial class AddThumbnailMap
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("varchar(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("varchar(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("varchar(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("varchar(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("varchar(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("varchar(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("varchar(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using MySql.EntityFrameworkCore.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddThumbnailMap : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "int",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-thumbnail-map",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("MySQL:ValueGenerationStrategy", MySQLValueGenerationStrategy.IdentityColumn),
|
||||||
|
libraryrootid = table.Column<int>(name: "library-root-id", type: "int", nullable: false),
|
||||||
|
relativepath = table.Column<string>(name: "relative-path", type: "varchar(1024)", maxLength: 1024, nullable: false),
|
||||||
|
contenttype = table.Column<string>(name: "content-type", type: "varchar(100)", maxLength: 100, nullable: false),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "datetime(6)", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime(6)", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-thumbnail-map", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_managed-thumbnail-map_managed-library-root_library-root-id",
|
||||||
|
column: x => x.libraryrootid,
|
||||||
|
principalTable: "managed-library-root",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
},
|
||||||
|
comment: "文件缩略图映射记录")
|
||||||
|
.Annotation("MySQL:Charset", "utf8mb4");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "thumbnail-id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-thumbnail-map-relative-path",
|
||||||
|
table: "managed-thumbnail-map",
|
||||||
|
column: "relative-path",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-thumbnail-map-root-id",
|
||||||
|
table: "managed-thumbnail-map",
|
||||||
|
column: "library-root-id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-thumbnail-map_thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "thumbnail-id",
|
||||||
|
principalTable: "managed-thumbnail-map",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-thumbnail-map_thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-thumbnail-map");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "idx-managed-file-record-thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "varchar(512)",
|
||||||
|
maxLength: 512,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+448
@@ -0,0 +1,448 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(MySqlAppDataContext))]
|
||||||
|
[Migration("20260522092342_AddPlaybackPosition")]
|
||||||
|
partial class AddPlaybackPosition
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("varchar(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("varchar(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("varchar(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("varchar(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("varchar(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("double")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("varchar(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("varchar(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddPlaybackPosition : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "playback-position",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "double",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "playback-position",
|
||||||
|
table: "managed-file-record");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+455
@@ -0,0 +1,455 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(MySqlAppDataContext))]
|
||||||
|
[Migration("20260523023700_AutoMigration_20260523103531")]
|
||||||
|
partial class AutoMigration_20260523103531
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("varchar(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("varchar(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("varchar(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("FileCreationTimeUtc")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("file-creation-time-utc");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("varchar(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("varchar(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("double")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("FileCreationTimeUtc")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-file-creation-time");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("varchar(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("varchar(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AutoMigration_20260523103531 : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "file-creation-time-utc",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "datetime(6)",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-file-creation-time",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "file-creation-time-utc");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "idx-managed-file-record-file-creation-time",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "file-creation-time-utc",
|
||||||
|
table: "managed-file-record");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,452 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.MySQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(MySqlAppDataContext))]
|
||||||
|
partial class MySqlAppDataContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("varchar(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("varchar(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("varchar(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("varchar(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("FileCreationTimeUtc")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("file-creation-time-utc");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("varchar(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("varchar(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("double")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("FileCreationTimeUtc")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-file-creation-time");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("tinyint(1)")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("varchar(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("varchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("varchar(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("varchar(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("varchar(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime(6)")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -9,7 +9,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PostgreSqlAppDataContext))]
|
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||||
[Migration("20260520082617_AutoMigration_20260520162543")]
|
[Migration("20260520082617_AutoMigration_20260520162543")]
|
||||||
@@ -25,7 +25,7 @@ namespace Avalonia_EFCore.Migrations.PostgreSQL
|
|||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -87,7 +87,7 @@ namespace Avalonia_EFCore.Migrations.PostgreSQL
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -134,7 +134,7 @@ namespace Avalonia_EFCore.Migrations.PostgreSQL
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
+2
-2
@@ -1,10 +1,10 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AutoMigration_20260520162543 : Migration
|
public partial class AutoMigration_20260520162543 : Migration
|
||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -9,7 +9,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
{
|
{
|
||||||
[DbContext(typeof(PostgreSqlAppDataContext))]
|
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||||
[Migration("20260520083254_AutoMigration_20260520163216")]
|
[Migration("20260520083254_AutoMigration_20260520163216")]
|
||||||
@@ -25,7 +25,7 @@ namespace Avalonia_EFCore.Migrations.PostgreSQL
|
|||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -87,7 +87,7 @@ namespace Avalonia_EFCore.Migrations.PostgreSQL
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -140,7 +140,7 @@ namespace Avalonia_EFCore.Migrations.PostgreSQL
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.PostgreSQL
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AutoMigration_20260520163216 : Migration
|
public partial class AutoMigration_20260520163216 : Migration
|
||||||
Generated
+383
@@ -0,0 +1,383 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||||
|
[Migration("20260522082843_AutoMigration_20260522162758")]
|
||||||
|
partial class AutoMigration_20260522162758
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("character varying(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<string>("ThumbnailPath")
|
||||||
|
.HasMaxLength(512)
|
||||||
|
.HasColumnType("character varying(512)")
|
||||||
|
.HasColumnName("thumbnail-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("character varying(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+111
@@ -0,0 +1,111 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AutoMigration_20260522162758 : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-library-root",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
path = table.Column<string>(type: "character varying(1024)", maxLength: 1024, nullable: false),
|
||||||
|
displayname = table.Column<string>(name: "display-name", type: "character varying(200)", maxLength: 200, nullable: false),
|
||||||
|
isenabled = table.Column<bool>(name: "is-enabled", type: "boolean", nullable: false),
|
||||||
|
isavailable = table.Column<bool>(name: "is-available", type: "boolean", nullable: false, defaultValue: true),
|
||||||
|
scanintervalminutes = table.Column<int>(name: "scan-interval-minutes", type: "integer", nullable: false),
|
||||||
|
lastscanstartedat = table.Column<DateTime>(name: "last-scan-started-at", type: "timestamp with time zone", nullable: true),
|
||||||
|
lastscancompletedat = table.Column<DateTime>(name: "last-scan-completed-at", type: "timestamp with time zone", nullable: true),
|
||||||
|
lastscanerror = table.Column<string>(name: "last-scan-error", type: "character varying(2000)", maxLength: 2000, nullable: true),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "timestamp with time zone", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-library-root", x => x.id);
|
||||||
|
},
|
||||||
|
comment: "文件库根目录");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-file-record",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
libraryrootid = table.Column<int>(name: "library-root-id", type: "integer", nullable: false),
|
||||||
|
filename = table.Column<string>(name: "file-name", type: "character varying(260)", maxLength: 260, nullable: false),
|
||||||
|
relativepath = table.Column<string>(name: "relative-path", type: "character varying(1024)", maxLength: 1024, nullable: false),
|
||||||
|
absolutepath = table.Column<string>(name: "absolute-path", type: "character varying(2048)", maxLength: 2048, nullable: false),
|
||||||
|
extension = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
||||||
|
sizebytes = table.Column<long>(name: "size-bytes", type: "bigint", nullable: false),
|
||||||
|
lastwritetimeutc = table.Column<DateTime>(name: "last-write-time-utc", type: "timestamp with time zone", nullable: false),
|
||||||
|
mediatype = table.Column<string>(name: "media-type", type: "character varying(20)", maxLength: 20, nullable: false),
|
||||||
|
contenttype = table.Column<string>(name: "content-type", type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
exists = table.Column<bool>(type: "boolean", nullable: false),
|
||||||
|
lastseenat = table.Column<DateTime>(name: "last-seen-at", type: "timestamp with time zone", nullable: false),
|
||||||
|
thumbnailpath = table.Column<string>(name: "thumbnail-path", type: "character varying(512)", maxLength: 512, nullable: true),
|
||||||
|
videoduration = table.Column<double>(name: "video-duration", type: "double precision", nullable: true),
|
||||||
|
lastplayedat = table.Column<DateTime>(name: "last-played-at", type: "timestamp with time zone", nullable: true),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "timestamp with time zone", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-file-record", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-library-root_library-root-id",
|
||||||
|
column: x => x.libraryrootid,
|
||||||
|
principalTable: "managed-library-root",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
},
|
||||||
|
comment: "文件库文件记录");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-absolute-path",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "absolute-path",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-last-played-at",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "last-played-at");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-media-type-exists",
|
||||||
|
table: "managed-file-record",
|
||||||
|
columns: new[] { "media-type", "exists" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-root-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "library-root-id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-library-root-path",
|
||||||
|
table: "managed-library-root",
|
||||||
|
column: "path",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-library-root");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+459
@@ -0,0 +1,459 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||||
|
[Migration("20260522084325_AddThumbnailMap")]
|
||||||
|
partial class AddThumbnailMap
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("character varying(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("character varying(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddThumbnailMap : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "integer",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-thumbnail-map",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "integer", nullable: false)
|
||||||
|
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||||
|
libraryrootid = table.Column<int>(name: "library-root-id", type: "integer", nullable: false),
|
||||||
|
relativepath = table.Column<string>(name: "relative-path", type: "character varying(1024)", maxLength: 1024, nullable: false),
|
||||||
|
contenttype = table.Column<string>(name: "content-type", type: "character varying(100)", maxLength: 100, nullable: false),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "timestamp with time zone", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "timestamp with time zone", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-thumbnail-map", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_managed-thumbnail-map_managed-library-root_library-root-id",
|
||||||
|
column: x => x.libraryrootid,
|
||||||
|
principalTable: "managed-library-root",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
},
|
||||||
|
comment: "文件缩略图映射记录");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "thumbnail-id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-thumbnail-map-relative-path",
|
||||||
|
table: "managed-thumbnail-map",
|
||||||
|
column: "relative-path",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-thumbnail-map-root-id",
|
||||||
|
table: "managed-thumbnail-map",
|
||||||
|
column: "library-root-id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-thumbnail-map_thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "thumbnail-id",
|
||||||
|
principalTable: "managed-thumbnail-map",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-thumbnail-map_thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-thumbnail-map");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "idx-managed-file-record-thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "character varying(512)",
|
||||||
|
maxLength: 512,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+463
@@ -0,0 +1,463 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||||
|
[Migration("20260522092330_AddPlaybackPosition")]
|
||||||
|
partial class AddPlaybackPosition
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("character varying(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("character varying(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddPlaybackPosition : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "playback-position",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "double precision",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "playback-position",
|
||||||
|
table: "managed-file-record");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+470
@@ -0,0 +1,470 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||||
|
[Migration("20260523023643_AutoMigration_20260523103531")]
|
||||||
|
partial class AutoMigration_20260523103531
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("FileCreationTimeUtc")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("file-creation-time-utc");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("character varying(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("FileCreationTimeUtc")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-file-creation-time");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("character varying(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AutoMigration_20260523103531 : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "file-creation-time-utc",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "timestamp with time zone",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-file-creation-time",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "file-creation-time-utc");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "idx-managed-file-record-file-creation-time",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "file-creation-time-utc",
|
||||||
|
table: "managed-file-record");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,467 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.PostgreSQL
|
||||||
|
{
|
||||||
|
[DbContext(typeof(PostgreSqlAppDataContext))]
|
||||||
|
partial class PostgreSqlAppDataContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
||||||
|
|
||||||
|
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("character varying(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("character varying(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("character varying(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("character varying(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("FileCreationTimeUtc")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("file-creation-time-utc");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("character varying(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("character varying(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("double precision")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("FileCreationTimeUtc")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-file-creation-time");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("boolean")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("character varying(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("character varying(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("character varying(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("character varying(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("character varying(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("integer")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("timestamp with time zone")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+4
-4
@@ -1,12 +1,12 @@
|
|||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqliteAppDataContext))]
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
[Migration("20260514000100_InitialCreate")]
|
[Migration("20260514000100_InitialCreate")]
|
||||||
@@ -18,7 +18,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
modelBuilder
|
modelBuilder
|
||||||
.HasAnnotation("ProductVersion", "10.0.0");
|
.HasAnnotation("ProductVersion", "10.0.0");
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.HasComment("用户主键")
|
.HasComment("用户主键")
|
||||||
@@ -52,7 +52,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.HasComment("天气预报主键")
|
.HasComment("天气预报主键")
|
||||||
+1
-1
@@ -4,7 +4,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始数据库基线。后续软件版本只追加新的 Migration,不修改已发布 Migration。
|
/// 初始数据库基线。后续软件版本只追加新的 Migration,不修改已发布 Migration。
|
||||||
+5
-5
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -8,7 +8,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqliteAppDataContext))]
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
[Migration("20260515072045_AutoMigration_20260515152037")]
|
[Migration("20260515072045_AutoMigration_20260515152037")]
|
||||||
@@ -20,7 +20,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -65,7 +65,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AutoMigration_20260515152037 : Migration
|
public partial class AutoMigration_20260515152037 : Migration
|
||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -8,7 +8,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqliteAppDataContext))]
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
[Migration("20260515085847_AutoMigration_20260515165835")]
|
[Migration("20260515085847_AutoMigration_20260515165835")]
|
||||||
@@ -20,7 +20,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -80,7 +80,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -125,7 +125,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AutoMigration_20260515165835 : Migration
|
public partial class AutoMigration_20260515165835 : Migration
|
||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -8,7 +8,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqliteAppDataContext))]
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
[Migration("20260520083230_AutoMigration_20260520163216")]
|
[Migration("20260520083230_AutoMigration_20260520163216")]
|
||||||
@@ -20,7 +20,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -80,7 +80,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -131,7 +131,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AutoMigration_20260520163216 : Migration
|
public partial class AutoMigration_20260520163216 : Migration
|
||||||
+11
-11
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
@@ -8,7 +8,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqliteAppDataContext))]
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
[Migration("20260521080213_AddFileLibrary")]
|
[Migration("20260521080213_AddFileLibrary")]
|
||||||
@@ -20,7 +20,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -80,7 +80,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ManagedFileRecord", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -170,7 +170,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ManagedLibraryRoot", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -237,7 +237,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -288,7 +288,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -331,9 +331,9 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ManagedFileRecord", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Avalonia_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
.WithMany("Files")
|
.WithMany("Files")
|
||||||
.HasForeignKey("LibraryRootId")
|
.HasForeignKey("LibraryRootId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -342,7 +342,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
b.Navigation("LibraryRoot");
|
b.Navigation("LibraryRoot");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ManagedLibraryRoot", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Files");
|
b.Navigation("Files");
|
||||||
});
|
});
|
||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AddFileLibrary : Migration
|
public partial class AddFileLibrary : Migration
|
||||||
+31
-12
@@ -1,23 +1,26 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SQLite
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqliteAppDataContext))]
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
partial class SqliteAppDataContextModelSnapshot : ModelSnapshot
|
[Migration("20260522082814_AutoMigration_20260522162758")]
|
||||||
|
partial class AutoMigration_20260522162758
|
||||||
{
|
{
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
#pragma warning disable 612, 618
|
#pragma warning disable 612, 618
|
||||||
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -77,7 +80,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ManagedFileRecord", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -116,6 +119,10 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasColumnName("file-name");
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
b.Property<DateTime>("LastSeenAt")
|
b.Property<DateTime>("LastSeenAt")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasColumnName("last-seen-at");
|
.HasColumnName("last-seen-at");
|
||||||
@@ -144,10 +151,19 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
.HasColumnType("INTEGER")
|
.HasColumnType("INTEGER")
|
||||||
.HasColumnName("size-bytes");
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<string>("ThumbnailPath")
|
||||||
|
.HasMaxLength(512)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("thumbnail-path");
|
||||||
|
|
||||||
b.Property<DateTime>("UpdatedAt")
|
b.Property<DateTime>("UpdatedAt")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasColumnName("updated-at");
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("REAL")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
b.HasKey("Id")
|
b.HasKey("Id")
|
||||||
.HasName("pk-managed-file-record");
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
@@ -155,6 +171,9 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
.IsUnique()
|
.IsUnique()
|
||||||
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
b.HasIndex("LibraryRootId")
|
b.HasIndex("LibraryRootId")
|
||||||
.HasDatabaseName("idx-managed-file-record-root-id");
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
@@ -167,7 +186,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ManagedLibraryRoot", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -234,7 +253,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -285,7 +304,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -328,9 +347,9 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ManagedFileRecord", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Avalonia_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
.WithMany("Files")
|
.WithMany("Files")
|
||||||
.HasForeignKey("LibraryRootId")
|
.HasForeignKey("LibraryRootId")
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
@@ -339,7 +358,7 @@ namespace Avalonia_EFCore.Migrations.SQLite
|
|||||||
b.Navigation("LibraryRoot");
|
b.Navigation("LibraryRoot");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ManagedLibraryRoot", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
{
|
{
|
||||||
b.Navigation("Files");
|
b.Navigation("Files");
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AutoMigration_20260522162758 : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "last-played-at",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "TEXT",
|
||||||
|
maxLength: 512,
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "video-duration",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "REAL",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-last-played-at",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "last-played-at");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "idx-managed-file-record-last-played-at",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "last-played-at",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "video-duration",
|
||||||
|
table: "managed-file-record");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+442
@@ -0,0 +1,442 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
|
[Migration("20260522084259_AddThumbnailMap")]
|
||||||
|
partial class AddThumbnailMap
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("REAL")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddThumbnailMap : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "INTEGER",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-thumbnail-map",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "INTEGER", nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
libraryrootid = table.Column<int>(name: "library-root-id", type: "INTEGER", nullable: false),
|
||||||
|
relativepath = table.Column<string>(name: "relative-path", type: "TEXT", maxLength: 1024, nullable: false),
|
||||||
|
contenttype = table.Column<string>(name: "content-type", type: "TEXT", maxLength: 100, nullable: false),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "TEXT", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "TEXT", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-thumbnail-map", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_managed-thumbnail-map_managed-library-root_library-root-id",
|
||||||
|
column: x => x.libraryrootid,
|
||||||
|
principalTable: "managed-library-root",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
},
|
||||||
|
comment: "文件缩略图映射记录");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "thumbnail-id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-thumbnail-map-relative-path",
|
||||||
|
table: "managed-thumbnail-map",
|
||||||
|
column: "relative-path",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-thumbnail-map-root-id",
|
||||||
|
table: "managed-thumbnail-map",
|
||||||
|
column: "library-root-id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-thumbnail-map_thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "thumbnail-id",
|
||||||
|
principalTable: "managed-thumbnail-map",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-thumbnail-map_thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-thumbnail-map");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "idx-managed-file-record-thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "TEXT",
|
||||||
|
maxLength: 512,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+446
@@ -0,0 +1,446 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
|
[Migration("20260522092304_AddPlaybackPosition")]
|
||||||
|
partial class AddPlaybackPosition
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("REAL")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("REAL")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddPlaybackPosition : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "playback-position",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "REAL",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "playback-position",
|
||||||
|
table: "managed-file-record");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+453
@@ -0,0 +1,453 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
|
[Migration("20260523023607_AutoMigration_20260523103531")]
|
||||||
|
partial class AutoMigration_20260523103531
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("FileCreationTimeUtc")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("file-creation-time-utc");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("REAL")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("REAL")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("FileCreationTimeUtc")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-file-creation-time");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AutoMigration_20260523103531 : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<DateTime>(
|
||||||
|
name: "file-creation-time-utc",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "TEXT",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-file-creation-time",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "file-creation-time-utc");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "idx-managed-file-record-file-creation-time",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "file-creation-time-utc",
|
||||||
|
table: "managed-file-record");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,450 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SQLite
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SqliteAppDataContext))]
|
||||||
|
partial class SqliteAppDataContextModelSnapshot : ModelSnapshot
|
||||||
|
{
|
||||||
|
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder.HasAnnotation("ProductVersion", "10.0.7");
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("FileCreationTimeUtc")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("file-creation-time-utc");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("REAL")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("REAL")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("FileCreationTimeUtc")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-file-creation-time");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("INTEGER")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
@@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqlServerAppDataContext))]
|
[DbContext(typeof(SqlServerAppDataContext))]
|
||||||
[Migration("20260520082607_AutoMigration_20260520162543")]
|
[Migration("20260520082607_AutoMigration_20260520162543")]
|
||||||
@@ -25,7 +25,7 @@ namespace Avalonia_EFCore.Migrations.SqlServer
|
|||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -87,7 +87,7 @@ namespace Avalonia_EFCore.Migrations.SqlServer
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -134,7 +134,7 @@ namespace Avalonia_EFCore.Migrations.SqlServer
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
+2
-2
@@ -1,9 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AutoMigration_20260520162543 : Migration
|
public partial class AutoMigration_20260520162543 : Migration
|
||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
using Avalonia_EFCore.Database;
|
using FileShare_EFCore.Database;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
@@ -9,7 +9,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
{
|
{
|
||||||
[DbContext(typeof(SqlServerAppDataContext))]
|
[DbContext(typeof(SqlServerAppDataContext))]
|
||||||
[Migration("20260520083242_AutoMigration_20260520163216")]
|
[Migration("20260520083242_AutoMigration_20260520163216")]
|
||||||
@@ -25,7 +25,7 @@ namespace Avalonia_EFCore.Migrations.SqlServer
|
|||||||
|
|
||||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.ApiRefreshTokenEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<long>("Id")
|
b.Property<long>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -87,7 +87,7 @@ namespace Avalonia_EFCore.Migrations.SqlServer
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.UserEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
@@ -140,7 +140,7 @@ namespace Avalonia_EFCore.Migrations.SqlServer
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Avalonia_EFCore.Models.WeatherForecastEntity", b =>
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
namespace Avalonia_EFCore.Migrations.SqlServer
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public partial class AutoMigration_20260520163216 : Migration
|
public partial class AutoMigration_20260520163216 : Migration
|
||||||
Generated
+383
@@ -0,0 +1,383 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SqlServerAppDataContext))]
|
||||||
|
[Migration("20260522082829_AutoMigration_20260522162758")]
|
||||||
|
partial class AutoMigration_20260522162758
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("nvarchar(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("nvarchar(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("nvarchar(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("nvarchar(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("nvarchar(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("nvarchar(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("nvarchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<string>("ThumbnailPath")
|
||||||
|
.HasMaxLength(512)
|
||||||
|
.HasColumnType("nvarchar(512)")
|
||||||
|
.HasColumnName("thumbnail-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("float")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("nvarchar(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("nvarchar(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AutoMigration_20260522162758 : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-library-root",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
path = table.Column<string>(type: "nvarchar(1024)", maxLength: 1024, nullable: false),
|
||||||
|
displayname = table.Column<string>(name: "display-name", type: "nvarchar(200)", maxLength: 200, nullable: false),
|
||||||
|
isenabled = table.Column<bool>(name: "is-enabled", type: "bit", nullable: false),
|
||||||
|
isavailable = table.Column<bool>(name: "is-available", type: "bit", nullable: false, defaultValue: true),
|
||||||
|
scanintervalminutes = table.Column<int>(name: "scan-interval-minutes", type: "int", nullable: false),
|
||||||
|
lastscanstartedat = table.Column<DateTime>(name: "last-scan-started-at", type: "datetime2", nullable: true),
|
||||||
|
lastscancompletedat = table.Column<DateTime>(name: "last-scan-completed-at", type: "datetime2", nullable: true),
|
||||||
|
lastscanerror = table.Column<string>(name: "last-scan-error", type: "nvarchar(2000)", maxLength: 2000, nullable: true),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "datetime2", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime2", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-library-root", x => x.id);
|
||||||
|
},
|
||||||
|
comment: "文件库根目录");
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-file-record",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
libraryrootid = table.Column<int>(name: "library-root-id", type: "int", nullable: false),
|
||||||
|
filename = table.Column<string>(name: "file-name", type: "nvarchar(260)", maxLength: 260, nullable: false),
|
||||||
|
relativepath = table.Column<string>(name: "relative-path", type: "nvarchar(1024)", maxLength: 1024, nullable: false),
|
||||||
|
absolutepath = table.Column<string>(name: "absolute-path", type: "nvarchar(2048)", maxLength: 2048, nullable: false),
|
||||||
|
extension = table.Column<string>(type: "nvarchar(32)", maxLength: 32, nullable: false),
|
||||||
|
sizebytes = table.Column<long>(name: "size-bytes", type: "bigint", nullable: false),
|
||||||
|
lastwritetimeutc = table.Column<DateTime>(name: "last-write-time-utc", type: "datetime2", nullable: false),
|
||||||
|
mediatype = table.Column<string>(name: "media-type", type: "nvarchar(20)", maxLength: 20, nullable: false),
|
||||||
|
contenttype = table.Column<string>(name: "content-type", type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
|
exists = table.Column<bool>(type: "bit", nullable: false),
|
||||||
|
lastseenat = table.Column<DateTime>(name: "last-seen-at", type: "datetime2", nullable: false),
|
||||||
|
thumbnailpath = table.Column<string>(name: "thumbnail-path", type: "nvarchar(512)", maxLength: 512, nullable: true),
|
||||||
|
videoduration = table.Column<double>(name: "video-duration", type: "float", nullable: true),
|
||||||
|
lastplayedat = table.Column<DateTime>(name: "last-played-at", type: "datetime2", nullable: true),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "datetime2", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime2", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-file-record", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-library-root_library-root-id",
|
||||||
|
column: x => x.libraryrootid,
|
||||||
|
principalTable: "managed-library-root",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
},
|
||||||
|
comment: "文件库文件记录");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-absolute-path",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "absolute-path",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-last-played-at",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "last-played-at");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-media-type-exists",
|
||||||
|
table: "managed-file-record",
|
||||||
|
columns: new[] { "media-type", "exists" });
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-root-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "library-root-id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-library-root-path",
|
||||||
|
table: "managed-library-root",
|
||||||
|
column: "path",
|
||||||
|
unique: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-library-root");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+459
@@ -0,0 +1,459 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SqlServerAppDataContext))]
|
||||||
|
[Migration("20260522084325_AddThumbnailMap")]
|
||||||
|
partial class AddThumbnailMap
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("nvarchar(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("nvarchar(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("nvarchar(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("nvarchar(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("nvarchar(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("nvarchar(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("nvarchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("float")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("nvarchar(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("nvarchar(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("nvarchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddThumbnailMap : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<int>(
|
||||||
|
name: "thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "int",
|
||||||
|
nullable: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "managed-thumbnail-map",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
id = table.Column<int>(type: "int", nullable: false)
|
||||||
|
.Annotation("SqlServer:Identity", "1, 1"),
|
||||||
|
libraryrootid = table.Column<int>(name: "library-root-id", type: "int", nullable: false),
|
||||||
|
relativepath = table.Column<string>(name: "relative-path", type: "nvarchar(1024)", maxLength: 1024, nullable: false),
|
||||||
|
contenttype = table.Column<string>(name: "content-type", type: "nvarchar(100)", maxLength: 100, nullable: false),
|
||||||
|
createdat = table.Column<DateTime>(name: "created-at", type: "datetime2", nullable: false),
|
||||||
|
updatedat = table.Column<DateTime>(name: "updated-at", type: "datetime2", nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("pk-managed-thumbnail-map", x => x.id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_managed-thumbnail-map_managed-library-root_library-root-id",
|
||||||
|
column: x => x.libraryrootid,
|
||||||
|
principalTable: "managed-library-root",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
},
|
||||||
|
comment: "文件缩略图映射记录");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-file-record-thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "thumbnail-id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-thumbnail-map-relative-path",
|
||||||
|
table: "managed-thumbnail-map",
|
||||||
|
column: "relative-path",
|
||||||
|
unique: true);
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "idx-managed-thumbnail-map-root-id",
|
||||||
|
table: "managed-thumbnail-map",
|
||||||
|
column: "library-root-id");
|
||||||
|
|
||||||
|
migrationBuilder.AddForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-thumbnail-map_thumbnail-id",
|
||||||
|
table: "managed-file-record",
|
||||||
|
column: "thumbnail-id",
|
||||||
|
principalTable: "managed-thumbnail-map",
|
||||||
|
principalColumn: "id",
|
||||||
|
onDelete: ReferentialAction.SetNull);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropForeignKey(
|
||||||
|
name: "FK_managed-file-record_managed-thumbnail-map_thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "managed-thumbnail-map");
|
||||||
|
|
||||||
|
migrationBuilder.DropIndex(
|
||||||
|
name: "idx-managed-file-record-thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "thumbnail-id",
|
||||||
|
table: "managed-file-record");
|
||||||
|
|
||||||
|
migrationBuilder.AddColumn<string>(
|
||||||
|
name: "thumbnail-path",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "nvarchar(512)",
|
||||||
|
maxLength: 512,
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+463
@@ -0,0 +1,463 @@
|
|||||||
|
// <auto-generated />
|
||||||
|
using System;
|
||||||
|
using FileShare_EFCore.Database;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
|
{
|
||||||
|
[DbContext(typeof(SqlServerAppDataContext))]
|
||||||
|
[Migration("20260522092317_AddPlaybackPosition")]
|
||||||
|
partial class AddPlaybackPosition
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
#pragma warning disable 612, 618
|
||||||
|
modelBuilder
|
||||||
|
.HasAnnotation("ProductVersion", "10.0.7")
|
||||||
|
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||||
|
|
||||||
|
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ApiRefreshTokenEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<long>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<long>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("Device")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("device");
|
||||||
|
|
||||||
|
b.Property<DateTime>("ExpiresAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("expires-at");
|
||||||
|
|
||||||
|
b.Property<string>("IpAddress")
|
||||||
|
.HasMaxLength(64)
|
||||||
|
.HasColumnType("nvarchar(64)")
|
||||||
|
.HasColumnName("ip-address");
|
||||||
|
|
||||||
|
b.Property<string>("ReplacedByTokenHash")
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("nvarchar(128)")
|
||||||
|
.HasColumnName("replaced-by-token-hash");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("RevokedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("revoked-at");
|
||||||
|
|
||||||
|
b.Property<string>("TokenHash")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(128)
|
||||||
|
.HasColumnType("nvarchar(128)")
|
||||||
|
.HasColumnName("token-hash");
|
||||||
|
|
||||||
|
b.Property<int>("UserId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("user-id");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-api-refresh-token");
|
||||||
|
|
||||||
|
b.HasIndex("TokenHash")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-hash");
|
||||||
|
|
||||||
|
b.HasIndex("UserId")
|
||||||
|
.HasDatabaseName("idx-api-refresh-token-user-id");
|
||||||
|
|
||||||
|
b.ToTable("api-refresh-token", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("API refresh token");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("AbsolutePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(2048)
|
||||||
|
.HasColumnType("nvarchar(2048)")
|
||||||
|
.HasColumnName("absolute-path");
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<bool>("Exists")
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasColumnName("exists");
|
||||||
|
|
||||||
|
b.Property<string>("Extension")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(32)
|
||||||
|
.HasColumnType("nvarchar(32)")
|
||||||
|
.HasColumnName("extension");
|
||||||
|
|
||||||
|
b.Property<string>("FileName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(260)
|
||||||
|
.HasColumnType("nvarchar(260)")
|
||||||
|
.HasColumnName("file-name");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastPlayedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-played-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastSeenAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-seen-at");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastWriteTimeUtc")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-write-time-utc");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("MediaType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(20)
|
||||||
|
.HasColumnType("nvarchar(20)")
|
||||||
|
.HasColumnName("media-type");
|
||||||
|
|
||||||
|
b.Property<double?>("PlaybackPosition")
|
||||||
|
.HasColumnType("float")
|
||||||
|
.HasColumnName("playback-position");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("nvarchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<long>("SizeBytes")
|
||||||
|
.HasColumnType("bigint")
|
||||||
|
.HasColumnName("size-bytes");
|
||||||
|
|
||||||
|
b.Property<int?>("ThumbnailId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("thumbnail-id");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.Property<double?>("VideoDuration")
|
||||||
|
.HasColumnType("float")
|
||||||
|
.HasColumnName("video-duration");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-file-record");
|
||||||
|
|
||||||
|
b.HasIndex("AbsolutePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-file-record-absolute-path");
|
||||||
|
|
||||||
|
b.HasIndex("LastPlayedAt")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-last-played-at");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("ThumbnailId")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-thumbnail-id");
|
||||||
|
|
||||||
|
b.HasIndex("MediaType", "Exists")
|
||||||
|
.HasDatabaseName("idx-managed-file-record-media-type-exists");
|
||||||
|
|
||||||
|
b.ToTable("managed-file-record", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库文件记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<string>("DisplayName")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("display-name");
|
||||||
|
|
||||||
|
b.Property<bool>("IsAvailable")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasDefaultValue(true)
|
||||||
|
.HasColumnName("is-available");
|
||||||
|
|
||||||
|
b.Property<bool>("IsEnabled")
|
||||||
|
.HasColumnType("bit")
|
||||||
|
.HasColumnName("is-enabled");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanCompletedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-scan-completed-at");
|
||||||
|
|
||||||
|
b.Property<string>("LastScanError")
|
||||||
|
.HasMaxLength(2000)
|
||||||
|
.HasColumnType("nvarchar(2000)")
|
||||||
|
.HasColumnName("last-scan-error");
|
||||||
|
|
||||||
|
b.Property<DateTime?>("LastScanStartedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("last-scan-started-at");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("nvarchar(1024)")
|
||||||
|
.HasColumnName("path");
|
||||||
|
|
||||||
|
b.Property<int>("ScanIntervalMinutes")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("scan-interval-minutes");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-library-root");
|
||||||
|
|
||||||
|
b.HasIndex("Path")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-library-root-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-library-root", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件库根目录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<string>("ContentType")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)")
|
||||||
|
.HasColumnName("content-type");
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at");
|
||||||
|
|
||||||
|
b.Property<int>("LibraryRootId")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("library-root-id");
|
||||||
|
|
||||||
|
b.Property<string>("RelativePath")
|
||||||
|
.IsRequired()
|
||||||
|
.HasMaxLength(1024)
|
||||||
|
.HasColumnType("nvarchar(1024)")
|
||||||
|
.HasColumnName("relative-path");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-managed-thumbnail-map");
|
||||||
|
|
||||||
|
b.HasIndex("LibraryRootId")
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-root-id");
|
||||||
|
|
||||||
|
b.HasIndex("RelativePath")
|
||||||
|
.IsUnique()
|
||||||
|
.HasDatabaseName("idx-managed-thumbnail-map-relative-path");
|
||||||
|
|
||||||
|
b.ToTable("managed-thumbnail-map", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("文件缩略图映射记录");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.UserEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("用户主键");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<string>("Email")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("email")
|
||||||
|
.HasComment("用户邮箱");
|
||||||
|
|
||||||
|
b.Property<string>("Name")
|
||||||
|
.HasMaxLength(100)
|
||||||
|
.HasColumnType("nvarchar(100)")
|
||||||
|
.HasColumnName("name")
|
||||||
|
.HasComment("用户名称");
|
||||||
|
|
||||||
|
b.Property<string>("PasswordHash")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("password-hash")
|
||||||
|
.HasComment("密码哈希值");
|
||||||
|
|
||||||
|
b.Property<string>("PhoneNumber")
|
||||||
|
.HasMaxLength(50)
|
||||||
|
.HasColumnType("nvarchar(50)")
|
||||||
|
.HasColumnName("phone-number")
|
||||||
|
.HasComment("电话号码");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-user");
|
||||||
|
|
||||||
|
b.ToTable("user", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("用户实体,演示数据库 CRUD 操作");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.WeatherForecastEntity", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("id")
|
||||||
|
.HasComment("天气预报主键");
|
||||||
|
|
||||||
|
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
|
||||||
|
|
||||||
|
b.Property<DateTime>("CreatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("created-at")
|
||||||
|
.HasComment("创建时间");
|
||||||
|
|
||||||
|
b.Property<DateOnly>("Date")
|
||||||
|
.HasColumnType("date")
|
||||||
|
.HasColumnName("date")
|
||||||
|
.HasComment("预报日期");
|
||||||
|
|
||||||
|
b.Property<string>("Summary")
|
||||||
|
.HasMaxLength(200)
|
||||||
|
.HasColumnType("nvarchar(200)")
|
||||||
|
.HasColumnName("summary")
|
||||||
|
.HasComment("天气摘要");
|
||||||
|
|
||||||
|
b.Property<int>("TemperatureC")
|
||||||
|
.HasColumnType("int")
|
||||||
|
.HasColumnName("temperature-c")
|
||||||
|
.HasComment("摄氏温度");
|
||||||
|
|
||||||
|
b.Property<DateTime>("UpdatedAt")
|
||||||
|
.HasColumnType("datetime2")
|
||||||
|
.HasColumnName("updated-at")
|
||||||
|
.HasComment("更新时间");
|
||||||
|
|
||||||
|
b.HasKey("Id")
|
||||||
|
.HasName("pk-weather-forecast");
|
||||||
|
|
||||||
|
b.ToTable("weather-forecast", t =>
|
||||||
|
{
|
||||||
|
t.HasComment("天气预报数据实体");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedFileRecord", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedThumbnailMap", "Thumbnail")
|
||||||
|
.WithMany("Files")
|
||||||
|
.HasForeignKey("ThumbnailId")
|
||||||
|
.OnDelete(DeleteBehavior.SetNull);
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnail");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("FileShare_EFCore.Models.ManagedLibraryRoot", "LibraryRoot")
|
||||||
|
.WithMany("Thumbnails")
|
||||||
|
.HasForeignKey("LibraryRootId")
|
||||||
|
.OnDelete(DeleteBehavior.Cascade)
|
||||||
|
.IsRequired();
|
||||||
|
|
||||||
|
b.Navigation("LibraryRoot");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedLibraryRoot", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
|
||||||
|
b.Navigation("Thumbnails");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("FileShare_EFCore.Models.ManagedThumbnailMap", b =>
|
||||||
|
{
|
||||||
|
b.Navigation("Files");
|
||||||
|
});
|
||||||
|
#pragma warning restore 612, 618
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
namespace FileShare_EFCore.Migrations.SqlServer
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
public partial class AddPlaybackPosition : Migration
|
||||||
|
{
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.AddColumn<double>(
|
||||||
|
name: "playback-position",
|
||||||
|
table: "managed-file-record",
|
||||||
|
type: "float",
|
||||||
|
nullable: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
|
{
|
||||||
|
migrationBuilder.DropColumn(
|
||||||
|
name: "playback-position",
|
||||||
|
table: "managed-file-record");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user