XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEWorkflow

【WinUI】XFE基于windows的工作流

公开
关注 0 Fork 0 Star 0
UTF-8
using XFEExtension.NetCore.WinUIHelper.Utilities;
using XFEWorkflow.Controls;
using XFEWorkflow.Core.Runtime;
using XFEWorkflow.Infrastructure.Execution;
using XFEWorkflow.Infrastructure.Plugins;
using XFEWorkflow.Infrastructure.Space;
using XFEWorkflow.Infrastructure.Storage;
using XFEWorkflow.Plugins.Desktop;
using XFEWorkflow.Plugins.WeCom;

namespace XFEWorkflow.Services;

public interface IWorkflowApplicationService
{
    WorkflowRepository Workflows { get; }
    PluginManager Plugins { get; }
    RunCoordinator Runs { get; }
}
public sealed class AppServices : IWorkflowApplicationService, IAsyncDisposable
{
    public static AppServices Current { get; } = new();
    public WorkspacePaths Paths { get; }
    public ClientState State { get; }
    public CredentialVault Credentials { get; }
    public SecretRedactor Redactor { get; } = new();
    public WorkflowRepository Workflows { get; }
    public PluginManager Plugins { get; }
    public RunCoordinator Runs { get; }
    public WorkflowScheduler Scheduler { get; }
    public SpaceClient Space { get; }
    public WorkflowLogService Logs { get; }
    public PluginPageService PluginPages { get; }
    public DesktopInputService Desktop { get; } = new();
    public ScreenToolsService ScreenTools { get; } = new();
    public CommandProcessHost Commands { get; }
    public WeComHostService WeCom { get; }
    public BrowserHost? Browser { get; private set; }
    public Task Ready { get; private set; } = Task.CompletedTask;
    private bool initialized;
    private AppServices()
    {
        Paths = new(Utilities.AppPath.WorkspaceDirectory);
        State = new(Paths); Credentials = new(State); Workflows = new(Paths); Plugins = new(Paths, State);
        Runs = new(Paths, Plugins, Credentials, Redactor, Path.Combine(AppContext.BaseDirectory, "Runner", "XFEWorkflow.Runner.exe"));
        Commands = new(Path.Combine(AppContext.BaseDirectory, "Runner", "XFEWorkflow.Runner.exe"));
        WeCom = new(Path.Combine(Paths.Root, "PluginData", "WeCom"), Path.Combine(AppContext.BaseDirectory, "NativeBridge", "WeCom"));
        WeCom.Log += message => XFEExtension.NetCore.XFEConsole.XFEConsole.Log.AddLog(Redactor.Redact(message));
        Scheduler = new(Workflows, Runs); Space = new(State, Credentials);
        Logs = new(Paths, Runs, Redactor);
        PluginPages = new(this);
        Runs.RunChanged += Logs.Observe;
        Scheduler.Error += message => XFEExtension.NetCore.XFEConsole.XFEConsole.Log.AddLog(Redactor.Redact(message), XFEExtension.NetCore.XFEConsole.LogLevel.Error);
        Plugins.Changed += () => { if (initialized) _ = RefreshPluginTriggersAsync(); };
        Desktop.Backend = State.DesktopBackend;
        Runs.Host = async (id, method, input, ct) =>
        {
            if (method.StartsWith("browser.", StringComparison.Ordinal)) return await (Browser ?? throw new InvalidOperationException("浏览器服务尚未启动")).InvokeAsync(id, method, input, ct);
            if (method == "desktop.input") return await Desktop.ExecuteAsync(id, input, ct);
            if (method.StartsWith("screen.", StringComparison.Ordinal)) return await ScreenTools.InvokeAsync(method, input, ct);
            if (method == "command.run") return await Commands.ExecuteAsync(id, input, ct);
            if (method.StartsWith("wecom.", StringComparison.Ordinal)) return await WeCom.InvokeAsync(id, method, input, ct);
            throw new NotSupportedException(method);
        };
        Runs.ReleaseHost = async id => { await Desktop.ReleaseAsync(id); await Commands.ReleaseAsync(id); await WeCom.ReleaseAsync(id); if (Browser is not null) await Browser.ReleaseAsync(id); };
        ServiceManager.RegisterService<IWorkflowApplicationService>(() => this);
    }
    public void Start(BrowserHost browser) { Browser = browser; browser.Paths = Paths; Ready = InitializeAsync(); }
    private async Task InitializeAsync()
    {
        await Plugins.ImportBundledAsync(Path.Combine(AppContext.BaseDirectory, "BundledPlugins"));
        await Runs.InitializeAsync();
        foreach (var reference in Credentials.References) { try { Redactor.Add(Credentials.Get(reference)); } catch (System.Security.Cryptography.CryptographicException) { } }
        if ((await Workflows.ListAsync()).Count == 0) await Workflows.SaveAsync(WorkflowTemplates.Welcome());
        await Scheduler.StartAsync();
        initialized = true;
    }
    private async Task RefreshPluginTriggersAsync()
    {
        try { await Scheduler.RefreshAsync(); }
        catch (OperationCanceledException) { }
        catch (Exception ex) { XFEExtension.NetCore.XFEConsole.XFEConsole.Log.AddLog(Redactor.Redact(ex.Message), XFEExtension.NetCore.XFEConsole.LogLevel.Error); }
    }
    public async ValueTask DisposeAsync()
    {
        initialized = false;
        if (WorkflowEditorPage.Current is { } editor) await editor.SaveAllAsync();
        if (MainPage.Current?.ViewModel.SelectedWorkflow is { } workflow) await Workflows.SaveAsync(workflow);
        await Scheduler.DisposeAsync(); ScreenTools.CancelSelection(); Desktop.EmergencyStop(); await Runs.DisposeAsync();
        await PluginPages.DisposeAsync();
        await Commands.DisposeAsync();
        await WeCom.DisposeAsync();
        if (Browser is not null) await Browser.DisposeAsync();
        Desktop.Dispose(); await State.DisposeAsync();
    }
}