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

STHApplication

【MAUI】寄当家——安卓端交易管理系统

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Text.Json;
using JiDangJia.Contracts;
using JiDangJia.Services;

namespace JiDangJia.Views;

public sealed class AppUpdatePage : ContentPage
{
    private readonly AppRelease release;private readonly INavigation owner;private readonly Action closed;private readonly bool required;
    private readonly Label state=Ui.Text("",13,Ui.Muted);private readonly ProgressBar progress=new(){ProgressColor=Ui.Bronze};
    private readonly Button download;private readonly CancellationTokenSource cancellation=new();private string? apk;private bool busy,closing;
    public AppUpdatePage(AppRelease release,INavigation owner,Action closed)
    {
        this.release=release;this.owner=owner;this.closed=closed;required=release.RequiredFor(AppUpdates.InstalledCode);Title="发现新版本";NavigationPage.SetHasBackButton(this,false);
        download=Ui.Button("下载并更新",Update);
        var body=Ui.Stack(Ui.Card(Ui.Stack(Ui.Section(AppProduct.Name+" "+release.VersionName),Ui.Text("当前版本 "+AppInfo.Current.VersionString+" · 安装包 "+(release.Size/1048576d).ToString("0.0")+" MB",12,Ui.Muted),Ui.Text(required?"当前版本需要更新后继续使用。":"更新后继续使用现有账号与本机数据。",13,Ui.Bronze),Ui.Divider(),Ui.Text(release.Notes,15))),progress,state,download);
        if(!required)body.Add(Ui.Button("稍后更新",Close,false));body.Padding=22;Content=new ScrollView{Content=body};
    }
    private async Task Update()
    {
        busy=true;
        try
        {
            if(apk is null)
            {state.Text="正在下载…";download.Text="正在下载";apk=await AppUpdates.Download(release,new Progress<double>(value=>{progress.Progress=value;state.Text="正在下载 "+(value*100).ToString("0")+"%";}),cancellation.Token);}
            state.Text="校验通过,正在打开系统安装界面。";
            if(!AppUpdates.Install(apk,release)){state.Text="请在系统页面允许安装此来源的应用,返回后点击“继续安装”。";download.Text="继续安装";}
            else{state.Text="请在系统安装界面确认。安装完成后重新打开"+AppProduct.Name+"。";download.Text="再次打开安装界面";}
        }
        catch(OperationCanceledException){state.Text="下载已取消。";}
        catch(Exception ex){apk=null;state.Text=ex is HttpRequestException?"下载未完成,请检查网络后重试。":ex.Message;download.Text="重试更新";}
        finally{busy=false;}
    }
    private async Task Close(){if(closing)return;closing=true;cancellation.Cancel();AppUpdates.Defer(release.Id);try{await owner.PopAsync();}finally{closed();}}
    protected override bool OnBackButtonPressed(){if(required||busy)return true;_ = Close();return true;}
}

public sealed class AppUpdateAdminPage : ContentPage
{
    private readonly Editor notes=new(){Placeholder="填写本次更新内容",AutoSize=EditorAutoSizeOption.TextChanges,MinimumHeightRequest=100,MaxLength=8000};
    private readonly Entry minimum=new(){Text="0",Keyboard=Keyboard.Numeric,Placeholder="最低可用版本号,0 表示可稍后更新"};
    private readonly Label selected=Ui.Text("尚未选择安装包",13,Ui.Muted),status=Ui.Text("",13,Ui.Bronze);
    private readonly VerticalStackLayout list=Ui.Stack();private FileResult? file;private bool uploading;
    public AppUpdateAdminPage()
    {
        if(!AuthClient.Current.IsAdmin)throw new InvalidOperationException("需要管理员权限");Title="APP 版本更新管理";
        var body=Ui.Stack(Ui.Card(Ui.Stack(Ui.Section("发布 Android 更新"),Ui.Text("版本名称和版本号自动从 APK 读取。用户打开 APP 时收到更新提示,可在设置中手动检查。",12,Ui.Muted),selected,Ui.Button("选择签名 APK",Pick,false),notes,Ui.Text("最低可用版本号(构建号)",12,Ui.Muted),minimum,Ui.Text("0:可稍后更新;填写新版本构建号:所有旧版本须更新。安装包须与已安装应用使用相同签名。",12,Ui.Muted),status,Ui.Button("发布更新",Publish))),Ui.Section("已发布版本"),list,Ui.Button("清理旧安装包,仅保留最新有效版",Clean,false));body.Padding=18;Content=new ScrollView{Content=body};
    }
    protected override async void OnAppearing(){base.OnAppearing();try{await Load();}catch(Exception ex){status.Text=ex.Message;}}
    private async Task Pick()
    {if(uploading)return;using var scope=new ExternalPhotoScope();file=await FilePicker.Default.PickAsync(new PickOptions{PickerTitle="选择"+AppProduct.Name+"签名 APK"});if(file is not null)selected.Text=file.FileName;}
    private async Task Publish()
    {
        if(file is null)throw new ArgumentException("请先选择安装包");if(!int.TryParse(minimum.Text,out var code)||code<0)throw new ArgumentException("最低可用版本号请填写非负整数");
        var request=new PublishReleaseRequest(code,notes.Text??"");if(string.IsNullOrWhiteSpace(request.Notes)||request.Notes.Length>8000)throw new ArgumentException("请填写更新说明");
        uploading=true;status.Text="正在上传并校验安装包,请保持此页面打开…";
        try{await using var stream=await file.OpenReadAsync();var released=await AuthClient.Current.PublishUpdate(stream,request,CancellationToken.None);file=null;selected.Text="尚未选择安装包";notes.Text="";status.Text="已发布 "+released.VersionName+"("+released.VersionCode+")";await Load();}
        catch{status.Text="发布未完成,请刷新列表确认后重试。";throw;}
        finally{uploading=false;}
    }
    private async Task Load()
    {
        using var data=await AuthClient.Current.AdminRequest("/updates");var rows=data.RootElement.Deserialize<List<AppRelease>>(new JsonSerializerOptions(JsonSerializerDefaults.Web))??[];list.Clear();
        if(rows.Count==0)list.Add(Ui.Text("尚未发布更新。",13,Ui.Muted));
        foreach(var release in rows)
        {
            var card=Ui.Stack(Ui.Text(release.VersionName+"("+release.VersionCode+")"+(release.Enabled?" · 已发布":" · 已撤回"),16,bold:true),Ui.Text(release.Notes,13),Ui.Text("最低版本号 "+release.MinimumVersionCode+" · "+(release.Size/1048576d).ToString("0.0")+" MB",11,Ui.Muted));
            if(release.Enabled)card.Add(Ui.Button("撤回此更新",async()=>{if(await DisplayAlertAsync("撤回更新","停止向用户提供此版本?","撤回","取消")){using var result=await AuthClient.Current.AdminRequest("/updates/"+release.Id+"/withdraw",new{});await Load();}},false));list.Add(Ui.Card(card));
        }
    }
    private async Task Clean()
    {if(uploading)return;if(await DisplayAlertAsync("清理旧安装包","保留最新有效版本,删除其余服务器更新安装包。正在下载旧版本的用户需重新检查更新。","清理","取消")){using var result=await AuthClient.Current.AdminRequest("/updates/clean",new{});status.Text="已清理 "+result.RootElement.GetProperty("removed").GetInt32()+" 个旧安装包";await Load();}}
}