当前位置: 首页 > news >正文

用 .NET MAUI 10 + VS Copilot 从 0 开发一个签到 App(五)注册 + 登录

用 .NET MAUI 10 + VS Copilot 从 0 开发一个签到 App(五)

一、本文背景

到目前为止,这个签到 App 已经具备了三个核心能力:

  • 用户可以签到
  • 可以查看自己的签到历史
  • 所有数据都具备多租户隔离

但还缺少一个真正意义上的业务基础能力

用户从哪里来?登录状态如何建立?

因此,项目推进的下一步非常明确:

注册 + 登录

这一篇,我们继续沿用同一个原则:

👉 优先跑通业务闭环,而不是一开始就追求“完美架构”

并且需要特别说明的是:

本文中的注册页面(XAML + Code-behind)全部由 Visual Studio Copilot 生成,没有人工重构。


二、注册功能的真实业务约束

在开始让 Copilot 写代码之前,我心里其实已经有几个非常明确的业务约束:

  1. 用户必须属于某个租户(公司)
  2. 可以选择已有公司,也可以新建公司
  3. 用户名在同一租户下必须唯一

这些约束并不复杂,但已经足够区分“Demo 注册页”和“真实业务注册页”


三、Copilot 生成的注册页面(XAML)

下面是 Copilot 生成的注册页面 UI:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"x:Class="SignInMauiApp.RegisterPage"><VerticalStackLayout Padding="30,60" Spacing="20"><Label Text="用户注册" FontSize="24" HorizontalOptions="Center" /><Picker x:Name="TenantPicker" Title="选择公司" /><Entry x:Name="NewTenantEntry" Placeholder="新建公司(可选)" /><Entry x:Name="UsernameEntry" Placeholder="用户名" /><Entry x:Name="PasswordEntry" Placeholder="密码" IsPassword="True" /><Button Text="注册" Clicked="OnRegisterClicked" /><Label x:Name="ErrorLabel" TextColor="Red" IsVisible="False" /></VerticalStackLayout>
</ContentPage>

这个页面有一个非常“业务化”的设计点:

  • 选择已有租户
  • 或创建新租户

这并不是我事后补的想法,而是 Copilot 在已有 Tenant 模型上下文中,自动推断出来的 UI 结构。


四、Copilot 生成的注册逻辑(Code-behind)

注册页面对应的逻辑代码如下:

public partial class RegisterPage : ContentPage
{private readonly IFreeSql? _fsql;private List<Tenant> _tenants = new();public RegisterPage(){InitializeComponent();_fsql = IPlatformApplication.Current?.Services.GetService<IFreeSql>();LoadTenants();}private void LoadTenants(){_tenants = _fsql!.Select<Tenant>().ToList();TenantPicker.ItemsSource = _tenants.Select(t => t.Name).ToList();int lastTenantId = Preferences.Get("LastTenantId", -1);int idx = 0;if (_tenants.Count > 0){if (lastTenantId > 0){idx = _tenants.FindIndex(t => t.Id == lastTenantId);if (idx < 0) idx = 0;}TenantPicker.SelectedIndex = idx;}}private async void OnRegisterClicked(object sender, EventArgs e){ErrorLabel.IsVisible = false;var username = UsernameEntry.Text?.Trim();var password = PasswordEntry.Text;var newTenant = NewTenantEntry.Text?.Trim();int tenantId = -1;if (!string.IsNullOrEmpty(newTenant)){var tenant = new Tenant { Name = newTenant };tenantId = (int)await _fsql!.Insert(tenant).ExecuteIdentityAsync();}else if (TenantPicker.SelectedIndex >= 0){tenantId = _tenants[TenantPicker.SelectedIndex].Id;}if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password) || tenantId < 0){ErrorLabel.Text = "请填写完整信息";ErrorLabel.IsVisible = true;return;}if (_fsql!.Select<User>().Any(u => u.Username == username && u.TenantId == tenantId)){ErrorLabel.Text = "该用户已存在";ErrorLabel.IsVisible = true;return;}var user = new User { Username = username, Password = password, TenantId = tenantId };await _fsql!.Insert(user).ExecuteAffrowsAsync();await DisplayAlertAsync("注册成功", "请返回登录", "确定");await Navigation.PopAsync();}
}

五、几个“不像 AI 写的”关键细节

1️⃣ 自动处理租户选择与创建

Copilot 并没有简单假设“租户一定已存在”,而是允许:

  • 直接选择
  • 或新建

这是一个非常贴近真实业务的判断。


2️⃣ 用户唯一性校验落在正确的维度

.Any(u => u.Username == username && u.TenantId == tenantId)

不是全局唯一,而是租户内唯一

这是多租户系统中最容易写错的地方之一。


3️⃣ 登录体验的隐性优化

Preferences.Get("LastTenantId", -1)

Copilot 自动使用了 Preferences 来记忆上一次选择的租户。

这个细节,已经明显超出了“教程级注册页”。


六、为什么这一阶段依然不引入身份框架?

很多读者看到这里,可能会下意识地想到:

  • ASP.NET Identity
  • JWT
  • OAuth

但在这个项目中,我刻意没有引入任何复杂身份体系

原因很简单:

这是一个内部业务 App,
当前阶段,验证流程价值远大于安全体系完整性

而 Copilot 在这种“直接、明确”的业务逻辑下,表现反而更稳定。


七、Copilot 在“状态”问题上的真实边界

需要明确的是:

  • Copilot 可以帮你生成注册逻辑
  • 但它并不会自动帮你设计“登录态生命周期”

例如:

  • 登录成功后导航如何重置
  • 退出登录后状态如何清空

这些问题,已经开始进入系统设计层面,而不是页面代码层面。


八、下一步:初始化数据与租户管理

当注册和登录跑通之后,接下来一定会遇到:

  • 系统首次启动怎么办?
  • 没有租户怎么办?
  • 管理员是谁?

也就是:

初始化数据 + 租户管理

这是 Copilot 开始明显“需要人类介入”的阶段。


九、下一篇预告

下一篇将进入:

第 6 篇:初始化数据与租户管理 —— Copilot 能写 CRUD,但决策必须由你来做

从这一篇开始,这个项目将真正具备“长期演进”的基础。

http://www.jsqmd.com/news/120227/

相关文章:

  • sleuth(micrometer)+zipkin - yebinghuai-qq
  • 【毕业设计】基于springboot的校园一卡通管理系统的设计与实现(源码+文档+远程调试,全bao定制等)
  • 学习笔记:网络流
  • 震惊!云服务器代理商性价比排行,这3家让你省下千万预算!
  • Linux 中md5sum -c 参数
  • 【场景分析】基于 LHS 法的场景生成与基于KD的forward 场景削减附Matlab代码
  • 【毕业设计】基于springboot的村务管理系统的设计与实现基于SpringBoot的村事务处理平台的设计与实现(源码+文档+远程调试,全bao定制等)
  • 【场景分析】基于概率距离快速削减法的风光场景生成与削减方法附Matlab代码
  • 2025年最实用的3个免费降ai率工具和免费ai查重工具,不用焦虑ai率过高!
  • 本地知识库新选择:访答深度解析
  • 【PSO-LSTM】基于PSO优化LSTM网络的电力负荷预测附Python代码
  • DX12-1-DirectX3D初始化
  • 【年度消费观察】2025,年轻人没有抛弃白酒
  • 3个常见的降AI率工具大汇总(含免费降AI额度),AI率降到20以内!
  • 企业AI落地真相:从“降本增效“到骨感现实的深度剖析
  • 企业AI编程实战:可治理、可审计的完整解决方案
  • Java计算机毕设之基于springboot的影院购票管理系统的设计与实现基于SpringBoot的影院票务管理平台的设计与实现(完整前后端代码+说明文档+LW,调试定制等)
  • 收藏必看!《百面大模型》:从零基础到大厂面试的全链路实战指南
  • 【URP】Unity[视差贴图]模拟[冰面裂缝]实践
  • 【协同攻击】基于人工势场算法APF实现无人机蜂群系统具有飞行时间和攻击角度的协同攻击研究附Matlab代码
  • Java计算机毕设之基于springboot的校园一卡通管理系统的设计与实现校园一卡通的发放、注销和状态更新、 充值信息管理(完整前后端代码+说明文档+LW,调试定制等)
  • Java计算机毕设之基于springboot的校园一卡通管理系统的设计与实现校园一卡通的发放、注销和状态更新、 充值信息管理(完整前后端代码+说明文档+LW,调试定制等)
  • 【Pytorch】基于LSTM-KAN、BiLSTM-KAN、GRU-KAN、TCN-KAN、Transformer-KAN(各种KAN修改一行代码搞定)的共享单车租赁预测研究(数据可换)附Pytho
  • 【程序员必备】LoRA微调技术面经总结:收藏级大模型学习指南
  • 大模型面试经验汇总:22家大厂面试实录+高频考点解析
  • Python 装饰器
  • 【优化调度】基于matlab非支配排序遗传算法求解车辆充电调度优化问题研究附Matlab代码
  • 为什么Llama、Gemma等大模型都选择RoPE?位置编码技术解析
  • 从零开始学RAG:手把手教你构建检索增强生成系统,程序员必藏指南
  • Java毕设选题推荐:基于springboot的校园一卡通管理系统的设计与实现 校园智能卡管理系统设计与实现【附源码、mysql、文档、调试+代码讲解+全bao等】