提交用户部分代码

This commit is contained in:
chfei committed 2026-11-13 15:51:16 +08:00
1 parent 9b9e4cbd82
commit 127fc22de5
26 files changed
+1110 -206

No files matched your search

@@ -8,6 +8,7 @@ using Store.Management.Views;
using Store.Management.ViewModels;
using Store.Management.Framework.Controls;
using Store.Management.Api;
using Store.Management.Api.Users;
using System.Net;
using System.Net.Http;
using System.Text;
@@ -75,10 +76,10 @@ class Program
Check(pageVm.Unloads == 1, "移出页面触发卸载");
window.Content = page;
app.Dispatcher.Invoke(() => { }, DispatcherPriority.ApplicationIdle);
Check(pageVm.Loads == 2, "重新加入页面再次加载");
Check(pageVm.Loads == 2 && pageVm.FirstLoads == 1, "重新加入页面再次加载");
var replacement = new LifecycleProbe();
page.DataContext = replacement;
Check(pageVm.Unloads == 2 && replacement.Loads == 1, "已加载的页面更换 ViewModel 后转移通知");
Check(pageVm.Unloads == 2 && replacement.Loads == 1 && replacement.FirstLoads == 1, "已加载的页面更换 ViewModel 后转移通知");
windowVm.CancelClose = true;
window.Close();
Check(window.IsVisible && windowVm.Closes == 0, "ViewModel 可以取消关闭");
@@ -142,12 +143,120 @@ class Program
await usersVm.SearchCommand.Execute();
Check(status.Current.Level == StatusLevel.Error && usersVm.CanInteract, "查询失败后显示错误并允许重试");
await usersClient.Auth.LoginAsync(new() { UserName = "admin", Password = "320BB1D43EC042096A83DBAC6DC9294A" });
var usersPage = new UserManagementPage { DataContext = usersVm };
var grid = ((DockPanel)usersPage.Content).Children.OfType<DataGridEx>().Single();
var sample = new UserInfo { GroupId = 4, SaLevel = UserAdminLevel.SuperAdmin, OemId = "A01", Authority = "在线寄件,商品采集" };
foreach (var (header, expected) in new[] { ("分组", "客服"), ("代理编码", "A01"), ("超管等级", "超级管理员"), ("权限", "在线寄件,商品采集") })
{
var column = grid.Columns.OfType<DataGridTextColumn>().Single(c => Equals(c.Header, header));
var cell = new TextBlock { DataContext = sample };
cell.SetBinding(TextBlock.TextProperty, column.Binding);
Check(cell.Text == expected, $"用户列表{header}显示错误:{cell.Text}");
cell.DataContext = new UserInfo { GroupId = 5 };
Check(cell.Text == (header == "分组" ? "5" : ""), $"用户列表{header}历史数据或空值显示错误");
}
var groupFilter = LogicalTreeHelper.FindLogicalNode(usersPage, "GroupFilter") as ComboBox;
Check(groupFilter?.Items.Count == Enum.GetValues<UserGroup>().Length + 1, "用户组包含全部和当前枚举选项");
await app.Dispatcher.InvokeAsync(() => { }, DispatcherPriority.ApplicationIdle);
var menu = grid.RowContextMenu;
Check(grid.SelectRowOnRightClick && ReferenceEquals(menu, usersPage.Resources["UserRowMenu"]), "使用页面行菜单并选中右键行");
var actions = menu.Items.OfType<MenuItem>().Select(item => item.Command).ToArray();
Check(actions.SequenceEqual(new System.Windows.Input.ICommand[] { usersVm.EditCommand, usersVm.BillingCommand, usersVm.DeleteCommand, usersVm.CreateCommand }),
"行菜单绑定编辑、删除和创建用户命令");
Check(groupFilter!.Text == "全部", "默认显示全部用户组");
groupFilter.SelectedIndex = 1;
Check(usersVm.SelectedGroup == Store.Management.Api.Users.UserGroup.MainAccount, "选择主号更新搜索条件");
Task search = usersVm.SearchCommand.Execute();
Check(!usersVm.CanInteract && status.Current.Level == StatusLevel.Info, "查询期间禁用操作");
await search;
Check(usersVm.CanInteract && usersVm.Total == 51 && status.Current.Level == StatusLevel.Success, "重试查询成功");
var narrowWindow = new Window { Content = usersPage, Width = 900, Height = 560, ShowInTaskbar = false };
narrowWindow.Show();
narrowWindow.UpdateLayout();
var listScroll = (ScrollViewer)grid.Template.FindName("DG_ScrollViewer", grid);
Check(listScroll.ComputedHorizontalScrollBarVisibility == Visibility.Visible && listScroll.ScrollableWidth > 0,
"窄窗口显示用户列表横向滚动条");
listScroll.ScrollToRightEnd();
narrowWindow.UpdateLayout();
Check(listScroll.HorizontalOffset > 0 && Math.Abs(listScroll.HorizontalOffset - listScroll.ScrollableWidth) < 1,
"用户列表可滚动到最右列");
narrowWindow.Close();
Check(handler.LastGroupId == 3, "查询按主号分组");
groupFilter.SelectedIndex = 0;
await usersVm.PageCommand.Execute(new DataGridPageRequest(2, 50));
Check(usersVm.Page == 2 && usersVm.CanInteract && status.Current.Level == StatusLevel.Success, "分页查询成功");
Check(handler.LastGroupId == 3, "翻页保留已查询的用户组");
await usersVm.SearchCommand.Execute();
Check(handler.LastGroupId == null && usersVm.Page == 1, "全部清除分组并返回首页");
var billing = new Store.Management.Views.Dialogs.UserBillingDialogViewModel(userService);
billing.OnDialogOpened(new DialogParameters { { "parameter", new UserInfo
{
Id = 7, UserName = "billing", ShopPointsPerMonth = 45, PostagePerOrder = 3.5m,
RefundPostagePerOrder = 4m, GiftOemAmountPerOrder = null, SyncLogisticsPerOrder = 0m
} } });
var billingView = new Store.Management.Views.Dialogs.UserBillingDialog { DataContext = billing };
var billingWindow = new Window { Content = billingView, ShowInTaskbar = false };
billingWindow.Show();
billingWindow.UpdateLayout();
Check(billing.Fields.Length == 5 && billing.Fields[0].Value == 45 && billing.Fields[3].EnteredValue == null &&
billing.Fields[4].EnteredValue == 0, "计费配置回填原值并区分空值和零");
var saveBilling = (Prism.Commands.AsyncDelegateCommand)billing.ConfirmCommand;
int beforeBilling = handler.RequestCount;
await saveBilling.Execute();
Check(handler.RequestCount == beforeBilling, "未改动计费配置不发送请求");
billing.Fields[0].Value = 29;
await saveBilling.Execute();
Check(handler.RequestCount == beforeBilling && billing.ErrorMsg.Length > 0, "拦截无效积分");
billing.Fields[0].Value = 50;
var fields = (ItemsControl)billingView.Content;
var presenter = (ContentPresenter)fields.ItemContainerGenerator.ContainerFromIndex(3);
var title = (MAX.Common.WPF.Controls.TitleContent)System.Windows.Media.VisualTreeHelper.GetChild(presenter, 0);
var amount = (HandyControl.Controls.NumericUpDown)title.Content;
Check(amount.IsEnabled, "空金额可以直接输入");
var amountInput = (TextBox)amount.Template.FindName("PART_TextBox", amount);
amountInput.Focus();
amountInput.SelectAll();
amountInput.SelectedText = "0";
await saveBilling.Execute();
using (var json = System.Text.Json.JsonDocument.Parse(handler.LastUpdateBody!))
{
var body = json.RootElement;
Check(body.EnumerateObject().Count() == 3 && body.GetProperty("id").GetInt32() == 7 &&
body.GetProperty("shopPointsPerMonth").GetInt32() == 50 && body.GetProperty("giftOemAmountPerOrder").GetDecimal() == 0,
"计费更新请求仅含用户 ID 和两项改动");
}
billingWindow.Close();
var editor = new UserEditorDialogViewModel(userService);
editor.OnDialogOpened(new DialogParameters { { "user", null } });
var editorView = new UserEditorDialog { DataContext = editor };
editor.Draft.Mobile = "13800000000";
editor.Draft.NickName = "测试";
editor.Group = UserGroup.CustomerService;
editor.ParentUserName = "parent";
editor.AgentCode = "agent";
editor.Level = UserAdminLevel.SuperAdmin;
editor.Permissions[0].IsSelected = true;
var editorPassword = (PasswordBox)editorView.FindName("NewPassword");
var confirmation = (PasswordBox)editorView.FindName("Confirmation");
editorPassword.Password = "secret";
await ((Prism.Commands.AsyncDelegateCommand)editor.ConfirmCommand).Execute();
Check(editor.ErrorMsg.Contains("不一致"), "密码不一致应阻止保存");
confirmation.Password = "secret";
await ((Prism.Commands.AsyncDelegateCommand)editor.ConfirmCommand).Execute();
Check(handler.SavedCreate is { UserName: "13800000000", GroupId: UserGroup.CustomerService,
ParentUserName: "parent", Authority: "微信回采", OemId: "agent", SaLevel: UserAdminLevel.SuperAdmin },
"新增子账号提交手机号、分组、代理和权限");
editor.OnDialogClosed();
editor = new UserEditorDialogViewModel(userService);
editor.OnDialogOpened(new DialogParameters { { "user", new UserInfo { Id = 1, UserName = "old-login",
GroupId = 3, Authority = "微信回采,历史权限", Mobile = "13800000000", NickName = "测试" } } });
Check(editor.Permissions[0].IsSelected, "编辑回填权限");
editor.Permissions[0].IsSelected = false;
await ((Prism.Commands.AsyncDelegateCommand)editor.ConfirmCommand).Execute();
Check(handler.SavedUpdate?.Authority == "历史权限" && handler.PasswordTarget == null, "保留未知权限,留空不改密");
editor.UpdatePasswords("new-password", "new-password", "");
await ((Prism.Commands.AsyncDelegateCommand)editor.ConfirmCommand).Execute();
Check(handler.PasswordTarget == "old-login", "按原登录名修改密码");
await userService.DeleteAsync(1);
Console.WriteLine("PASS: status bar bindings, colors and background progress reports.");
return;
@@ -267,10 +376,16 @@ class Program
sealed class LifecycleProbe : ViewModelBase
{
public int FirstLoads { get; private set; }
public int Loads { get; private set; }
public int Unloads { get; private set; }
public int Closes { get; private set; }
public bool CancelClose { get; set; }
protected override void OnLoadedOnce()
{
if (Loads != 0) throw new InvalidOperationException("首次加载应先于 OnLoaded");
FirstLoads++;
}
protected override void OnLoaded() => Loads++;
protected override void OnUnloaded() => Unloads++;
protected override void OnClosing(System.ComponentModel.CancelEventArgs e) => e.Cancel = CancelClose;
@@ -279,18 +394,45 @@ sealed class LifecycleProbe : ViewModelBase
sealed class LoginHandler : HttpMessageHandler
{
public UserCreateRequest? SavedCreate { get; private set; }
public string? LastUpdateBody { get; private set; }
public UserUpdateRequest? SavedUpdate { get; private set; }
public string? PasswordTarget { get; private set; }
public int? LastGroupId { get; private set; }
public int RequestCount { get; private set; }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
RequestCount++;
if (request.RequestUri?.AbsolutePath == "/api/v1/user/GetAllUser")
{
using var query = System.Text.Json.JsonDocument.Parse(await request.Content!.ReadAsStringAsync(cancellationToken));
LastGroupId = query.RootElement.TryGetProperty("groupId", out var group) && group.ValueKind == System.Text.Json.JsonValueKind.Number
? group.GetInt32() : null;
await Task.Delay(30, cancellationToken);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("""{"code":200,"data":{"total":51,"items":[{"id":1,"userName":"admin"}]}}""", Encoding.UTF8, "application/json")
};
}
string? route = request.RequestUri?.AbsolutePath;
if (route is "/api/v1/user/AddSubUser" or "/api/v1/user/UpdateUser" or "/api/v1/user/ChangePassword")
{
string bodyText = await request.Content!.ReadAsStringAsync(cancellationToken);
var options = new System.Text.Json.JsonSerializerOptions(System.Text.Json.JsonSerializerDefaults.Web);
if (route.EndsWith("/AddSubUser"))
SavedCreate = System.Text.Json.JsonSerializer.Deserialize<UserCreateRequest>(bodyText, options);
else if (route.EndsWith("/UpdateUser"))
{
LastUpdateBody = bodyText;
SavedUpdate = System.Text.Json.JsonSerializer.Deserialize<UserUpdateRequest>(bodyText, options);
}
else
PasswordTarget = System.Text.Json.JsonSerializer.Deserialize<ChangePasswordRequest>(bodyText, options)?.UserName;
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent("""{"code":200,"data":{"id":1,"userName":"old-login","groupId":3,"authority":"历史权限"}}""", Encoding.UTF8, "application/json")
};
}
if (request.Method == HttpMethod.Post && request.RequestUri?.PathAndQuery == "/api/v1/user/DelUser?id=1")
{
return new HttpResponseMessage(HttpStatusCode.OK)