133 lines
4.5 KiB
C#
133 lines
4.5 KiB
C#
using System.Security;
|
|
using System.Windows.Input;
|
|
|
|
namespace Zcbot.WindowsNode;
|
|
|
|
internal sealed class DataRootPageViewModel : ObservableObject
|
|
{
|
|
private readonly IDataRootManagementService service;
|
|
private readonly AsyncCommand changeCommand;
|
|
private NodeDataRootSelection selection;
|
|
private string status;
|
|
private bool isMigrating;
|
|
|
|
internal DataRootPageViewModel(IDataRootManagementService service)
|
|
{
|
|
this.service = service;
|
|
selection = service.ReadSelection();
|
|
status = SelectionStatus(selection);
|
|
changeCommand = new AsyncCommand(ChangeAsync, () => !IsManaged && !IsMigrating);
|
|
ChangeCommand = changeCommand;
|
|
OpenCommand = new RelayCommand(() => OpenRequested?.Invoke(CurrentRoot));
|
|
}
|
|
|
|
internal event Func<string, string?>? ChooseTargetRequested;
|
|
internal event Func<string, bool>? ConfirmMigrationRequested;
|
|
internal event Func<string, Task<NodeDataMigrationResult>>? MigrationRequested;
|
|
internal event Action<NodeDataMigrationResult>? MigrationCompleted;
|
|
internal event Action<string>? OpenRequested;
|
|
|
|
public string CurrentRoot => selection.RootDirectory;
|
|
public bool IsManaged => selection.IsEnvironmentManaged;
|
|
|
|
public bool IsMigrating
|
|
{
|
|
get => isMigrating;
|
|
private set
|
|
{
|
|
if (SetProperty(ref isMigrating, value))
|
|
{
|
|
changeCommand.RaiseCanExecuteChanged();
|
|
}
|
|
}
|
|
}
|
|
|
|
public string Status
|
|
{
|
|
get => status;
|
|
private set => SetProperty(ref status, value);
|
|
}
|
|
|
|
public ICommand ChangeCommand { get; }
|
|
public ICommand OpenCommand { get; }
|
|
|
|
internal void Refresh()
|
|
{
|
|
selection = service.ReadSelection();
|
|
OnPropertyChanged(nameof(CurrentRoot));
|
|
OnPropertyChanged(nameof(IsManaged));
|
|
Status = SelectionStatus(selection);
|
|
changeCommand.RaiseCanExecuteChanged();
|
|
}
|
|
|
|
private async Task ChangeAsync()
|
|
{
|
|
if (MigrationRequested is null) return;
|
|
try
|
|
{
|
|
if (service.HasPendingJobs)
|
|
{
|
|
Status = "本机仍有未完成任务,暂不能迁移数据目录。请等待任务结束后重试。";
|
|
return;
|
|
}
|
|
}
|
|
catch (Exception exception) when (
|
|
exception is IOException or UnauthorizedAccessException)
|
|
{
|
|
Status = $"无法确认本机任务状态,数据目录不会迁移:{exception.Message}";
|
|
return;
|
|
}
|
|
|
|
var selected = ChooseTargetRequested?.Invoke(CurrentRoot);
|
|
if (string.IsNullOrWhiteSpace(selected)) return;
|
|
string target;
|
|
try
|
|
{
|
|
target = service.Normalize(selected);
|
|
}
|
|
catch (NodeConfigurationException exception)
|
|
{
|
|
Status = exception.Message;
|
|
return;
|
|
}
|
|
if (string.Equals(target, CurrentRoot, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
Status = "所选目录与当前数据目录相同。";
|
|
return;
|
|
}
|
|
if (ConfirmMigrationRequested?.Invoke(target) != true) return;
|
|
|
|
IsMigrating = true;
|
|
Status = "正在停止调度并复制、校验数据,请勿退出 Node…";
|
|
try
|
|
{
|
|
var result = await MigrationRequested(target);
|
|
Status = $"迁移完成:{result.FileCount} 个文件,{FormatBytes(result.TotalBytes)}。正在重启 Node…";
|
|
MigrationCompleted?.Invoke(result);
|
|
}
|
|
catch (Exception exception) when (
|
|
exception is IOException
|
|
or NodeConfigurationException
|
|
or SecurityException
|
|
or UnauthorizedAccessException)
|
|
{
|
|
IsMigrating = false;
|
|
Refresh();
|
|
Status = $"数据目录未切换,原目录继续有效:{exception.Message}";
|
|
}
|
|
}
|
|
|
|
private static string SelectionStatus(NodeDataRootSelection value) =>
|
|
value.IsEnvironmentManaged
|
|
? $"由机器环境变量 {NodeDataRootSettings.EnvironmentVariableName} 管理,界面不可修改。"
|
|
: value.Source == "user"
|
|
? "使用当前 Windows 专用账号保存的自定义目录。"
|
|
: "使用默认目录。";
|
|
|
|
private static string FormatBytes(long bytes) => bytes >= 1024L * 1024 * 1024
|
|
? $"{bytes / (1024d * 1024 * 1024):F2} GiB"
|
|
: bytes >= 1024L * 1024
|
|
? $"{bytes / (1024d * 1024):F1} MiB"
|
|
: $"{bytes / 1024d:F1} KiB";
|
|
}
|