fix: 管理员系统 + 注册流程重构 + UI改版 + 全链路修复

- 新增管理员角色(手机号12345678910),管理员可增删医生、查看患者
- 注册页重构:去掉角色选择+审核码,改为选医生+填姓名
- 医生端点按DoctorId过滤患者,Patient↔Doctor关系建立
- Doctor/DoctorProfile/User实体新增关联字段
- JSON循环引用修复(IgnoreCycles)
- /api/doctors改为公开接口
- 登录闪屏修复+原生Android启动页
- 输入框全局白底+灰框
- 蓝牙重连同步修复
- Web端(doctor_web+health_app/web)全部删除
- 全局UI改版:白底企业风,新配色和组件系统
- 新品牌图标和启动图
This commit is contained in:
MingNian
2026-06-16 15:16:44 +08:00
parent b635e9d25f
commit c70d5d4be6
103 changed files with 12284 additions and 13049 deletions

View File

@@ -7,10 +7,8 @@ namespace Health.WebApi.Endpoints;
/// </summary>
public static class AuthEndpoints
{
/// <summary>
/// 医生注册审核码(后期可移到配置表)
/// </summary>
private const string DoctorInviteCode = "6666";
private const string AdminPhone = "12345678910";
private const string AdminSmsCode = "000000";
public static void MapAuthEndpoints(this WebApplication app)
{
@@ -21,8 +19,7 @@ public static class AuthEndpoints
SmsService sms,
CancellationToken ct) =>
{
// 生成验证码
var code = sms.GenerateCode();
var code = request.Phone == AdminPhone ? AdminSmsCode : sms.GenerateCode();
var vc = new VerificationCode
{
Id = Guid.NewGuid(),
@@ -33,13 +30,13 @@ public static class AuthEndpoints
db.VerificationCodes.Add(vc);
await db.SaveChangesAsync(ct);
// 开发阶段:直接返回验证码(生产环境需去掉 devCode
await sms.SendCodeAsync(request.Phone, code);
if (request.Phone != AdminPhone)
await sms.SendCodeAsync(request.Phone, code);
return Results.Ok(new { code = 0, data = new { success = true, devCode = code }, message = (string?)null });
});
// ── 注册(新用户,需选身份)──
// ── 注册(仅限用户注册,需选择医生)──
app.MapPost("/api/auth/register", async (
RegisterRequest request,
AppDbContext db,
@@ -65,42 +62,33 @@ public static class AuthEndpoints
if (existing != null)
return Results.Ok(new { code = 40002, data = (object?)null, message = "该手机号已注册,请直接登录" });
// 校验角色
var role = request.Role;
if (role != "User" && role != "Doctor")
return Results.Ok(new { code = 40003, data = (object?)null, message = "角色参数无效" });
// 校验姓名
if (string.IsNullOrWhiteSpace(request.Name))
return Results.Ok(new { code = 40003, data = (object?)null, message = "请输入姓名" });
// 医生注册需校验审核码
if (role == "Doctor" && request.InviteCode != DoctorInviteCode)
return Results.Ok(new { code = 40004, data = (object?)null, message = "审核码错误" });
// 校验医生
var doctor = await db.Doctors.FirstOrDefaultAsync(d => d.Id == request.DoctorId && d.IsActive, ct);
if (doctor == null)
return Results.Ok(new { code = 40004, data = (object?)null, message = "所选医生不存在或已停诊" });
// 创建用户
var user = new User
{
Id = Guid.NewGuid(),
Phone = request.Phone,
Role = role,
Role = "User",
Name = request.Name,
DoctorId = request.DoctorId,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
};
db.Users.Add(user);
// 用户端:创建通知偏好+健康档案
if (role == "User")
{
db.NotificationPreferences.Add(new NotificationPreference { Id = Guid.NewGuid(), UserId = user.Id });
db.HealthArchives.Add(new HealthArchive { Id = Guid.NewGuid(), UserId = user.Id });
}
// 医生端:创建空医生档案
if (role == "Doctor")
{
db.DoctorProfiles.Add(new DoctorProfile { Id = Guid.NewGuid(), UserId = user.Id });
}
db.NotificationPreferences.Add(new NotificationPreference { Id = Guid.NewGuid(), UserId = user.Id });
db.HealthArchives.Add(new HealthArchive { Id = Guid.NewGuid(), UserId = user.Id });
await db.SaveChangesAsync(ct);
var accessToken = jwt.GenerateAccessToken(user.Id, user.Phone, role);
var accessToken = jwt.GenerateAccessToken(user.Id, user.Phone, "User");
var refreshToken = jwt.GenerateRefreshToken();
db.RefreshTokens.Add(new RefreshToken
@@ -119,15 +107,13 @@ public static class AuthEndpoints
{
accessToken,
refreshToken,
user = new {
user.Id, user.Phone, user.Role, isNew = true
}
user = new { user.Id, user.Phone, user.Role, isNew = true }
},
message = (string?)null
});
});
// ── 登录(已有账号)──
// ── 登录(已有账号 / 管理员)──
app.MapPost("/api/auth/login", async (
LoginRequest request,
AppDbContext db,
@@ -147,9 +133,37 @@ public static class AuthEndpoints
validCode.IsUsed = true;
// 管理员登录
if (request.Phone == AdminPhone)
{
var adminAccessToken = jwt.GenerateAccessToken(
Guid.Parse("00000000-0000-0000-0000-000000000001"), AdminPhone, "Admin");
var adminRefreshToken = jwt.GenerateRefreshToken();
db.RefreshTokens.Add(new RefreshToken
{
Id = Guid.NewGuid(),
UserId = Guid.Parse("00000000-0000-0000-0000-000000000001"),
Token = adminRefreshToken,
ExpiresAt = DateTime.UtcNow.AddDays(30),
});
await db.SaveChangesAsync(ct);
return Results.Ok(new
{
code = 0,
data = new
{
accessToken = adminAccessToken,
refreshToken = adminRefreshToken,
user = new { id = "00000000-0000-0000-0000-000000000001", phone = AdminPhone, role = "Admin", name = "管理员", isNew = false }
},
message = (string?)null
});
}
var user = await db.Users.FirstOrDefaultAsync(u => u.Phone == request.Phone, ct);
if (user == null)
return Results.Ok(new { code = 40004, data = (object?)null, message = "该手机号未注册,请先注册" });
return Results.Ok(new { code = 40004, data = (object?)null, message = "该手机号未注册,请先登录" });
user.UpdatedAt = DateTime.UtcNow;
await db.SaveChangesAsync(ct);
@@ -197,14 +211,35 @@ public static class AuthEndpoints
if (oldToken == null || oldToken.ExpiresAt < DateTime.UtcNow)
return Results.Ok(new { code = 40002, data = (object?)null, message = "登录已过期,请重新登录" });
// 吊销旧 token
oldToken.IsRevoked = true;
var user = await db.Users.FindAsync([oldToken.UserId], ct);
// 管理员刷新:无 User 记录,直接用固定信息
if (user == null && oldToken.UserId == Guid.Parse("00000000-0000-0000-0000-000000000001"))
{
var adminAccessToken = jwt.GenerateAccessToken(
Guid.Parse("00000000-0000-0000-0000-000000000001"), AdminPhone, "Admin");
var adminNewRefresh = jwt.GenerateRefreshToken();
db.RefreshTokens.Add(new RefreshToken
{
Id = Guid.NewGuid(),
UserId = Guid.Parse("00000000-0000-0000-0000-000000000001"),
Token = adminNewRefresh,
ExpiresAt = DateTime.UtcNow.AddDays(30),
});
await db.SaveChangesAsync(ct);
return Results.Ok(new
{
code = 0,
data = new { accessToken = adminAccessToken, refreshToken = adminNewRefresh, user = new { role = "Admin" } },
message = (string?)null
});
}
if (user == null)
return Results.Ok(new { code = 40002, data = (object?)null, message = "用户不存在" });
// 生成新 token续期
var accessToken = jwt.GenerateAccessToken(user.Id, user.Phone, user.Role);
var newRefreshToken = jwt.GenerateRefreshToken();
db.RefreshTokens.Add(new RefreshToken
@@ -242,6 +277,6 @@ public static class AuthEndpoints
// ---- 请求 DTO ----
public sealed record SendSmsRequest(string Phone);
public sealed record RegisterRequest(string Phone, string SmsCode, string? Role, string? InviteCode);
public sealed record RegisterRequest(string Phone, string SmsCode, string Name, Guid DoctorId);
public sealed record LoginRequest(string Phone, string SmsCode);
public sealed record RefreshRequest(string RefreshToken);