Files
OrpaonVision/OrpaonVision.ConfigApp/Infrastructure/Persistence/SqlServerDataStoreInitializer.cs
2026-04-06 22:04:05 +08:00

134 lines
4.5 KiB
C#

using Microsoft.Data.SqlClient;
using OrpaonVision.ConfigApp.Infrastructure.Persistence.Options;
using OrpaonVision.Core.Abstractions;
using OrpaonVision.Core.Results;
namespace OrpaonVision.ConfigApp.Infrastructure.Persistence;
/// <summary>
/// SQL Server 数据存储初始化器。
/// </summary>
public sealed class SqlServerDataStoreInitializer : IDataStoreInitializer
{
private readonly PersistenceOptions _options;
private readonly IAppLogger _logger;
/// <summary>
/// 构造函数。
/// </summary>
public SqlServerDataStoreInitializer(PersistenceOptions options, IAppLogger logger)
{
_options = options;
_logger = logger;
}
/// <inheritdoc />
public Result Initialize()
{
if (string.IsNullOrWhiteSpace(_options.ConnectionString))
{
return Result.Fail("DATASTORE_CONNECTIONSTRING_EMPTY", "数据库连接字符串未配置。", "请在 appsettings 中配置 Persistence:ConnectionString。");
}
try
{
using var connection = new SqlConnection(_options.ConnectionString);
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = BuildCreateTableScript();
command.ExecuteNonQuery();
return Result.Success("DATASTORE_INIT_OK", "数据库初始化完成。基础表已就绪。");
}
catch (Exception ex)
{
var traceId = Guid.NewGuid().ToString("N");
_logger.LogError("数据库初始化失败。", ex, traceId);
return Result.FromException(ex, "DATASTORE_INIT_FAILED", "数据库初始化失败。", traceId);
}
}
private static string BuildCreateTableScript()
{
return @"
IF OBJECT_ID(N'dbo.mdl_annotation_task', N'U') IS NULL
BEGIN
CREATE TABLE dbo.mdl_annotation_task
(
id BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
task_code NVARCHAR(64) NOT NULL,
task_name NVARCHAR(200) NOT NULL,
platform_type INT NOT NULL,
external_task_id NVARCHAR(64) NULL,
dataset_id BIGINT NOT NULL DEFAULT(0),
label_set_id BIGINT NOT NULL DEFAULT(0),
assigned_to NVARCHAR(64) NULL,
status INT NOT NULL DEFAULT(0),
progress_percent DECIMAL(5,2) NULL,
sync_status INT NOT NULL DEFAULT(0),
created_at DATETIME2(3) NOT NULL,
created_by NVARCHAR(64) NULL,
updated_at DATETIME2(3) NULL,
updated_by NVARCHAR(64) NULL,
cvat_project_id BIGINT NULL,
item_count INT NOT NULL DEFAULT(0),
task_status_text NVARCHAR(64) NULL,
updated_at_utc DATETIME2(3) NULL
);
CREATE UNIQUE INDEX UX_mdl_annotation_task_task_code ON dbo.mdl_annotation_task(task_code);
CREATE UNIQUE INDEX UX_mdl_annotation_task_external_task_id ON dbo.mdl_annotation_task(external_task_id) WHERE external_task_id IS NOT NULL;
END
IF OBJECT_ID(N'dbo.cfg_product_type_draft', N'U') IS NULL
BEGIN
CREATE TABLE dbo.cfg_product_type_draft
(
id BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
product_type_code NVARCHAR(64) NOT NULL,
product_type_name NVARCHAR(128) NOT NULL,
draft_json NVARCHAR(MAX) NOT NULL,
updated_at DATETIME2(3) NOT NULL,
updated_by NVARCHAR(64) NULL
);
CREATE UNIQUE INDEX UX_cfg_product_type_draft_code ON dbo.cfg_product_type_draft(product_type_code);
END
IF OBJECT_ID(N'dbo.cfg_rule_version', N'U') IS NULL
BEGIN
CREATE TABLE dbo.cfg_rule_version
(
id BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
product_type_code NVARCHAR(64) NOT NULL,
version_no NVARCHAR(64) NOT NULL,
snapshot_json NVARCHAR(MAX) NOT NULL,
published_at_utc DATETIME2(3) NOT NULL,
published_by NVARCHAR(64) NOT NULL
);
CREATE UNIQUE INDEX UX_cfg_rule_version_code_version ON dbo.cfg_rule_version(product_type_code, version_no);
END
IF OBJECT_ID(N'dbo.cfg_rule_version_audit', N'U') IS NULL
BEGIN
CREATE TABLE dbo.cfg_rule_version_audit
(
id BIGINT IDENTITY(1,1) NOT NULL PRIMARY KEY,
product_type_code NVARCHAR(64) NOT NULL,
action_type NVARCHAR(32) NOT NULL,
version_no NVARCHAR(64) NULL,
source_version_no NVARCHAR(64) NULL,
target_version_no NVARCHAR(64) NULL,
operator_name NVARCHAR(64) NOT NULL,
action_at_utc DATETIME2(3) NOT NULL,
detail_json NVARCHAR(MAX) NULL
);
CREATE INDEX IX_cfg_rule_version_audit_code_time ON dbo.cfg_rule_version_audit(product_type_code, action_at_utc DESC);
END
";
}
}