chore: 全面规范化代码,遵循 CLAUDE.md 编码规范

- C# 文件命名改为 snake_case(28 个文件重命名)
- C# 类转换为主构造函数(8 个类)
- 空 catch 添加异常类型(2 处)
- 新建 GlobalUsings.cs(Health.Infrastructure、Health.WebApi)
- Flutter 移除 go_router,改用 Riverpod 路由栈
- Flutter 移除 flutter_secure_storage,改用 sqflite 持久化
- 修复 Flutter 构建路径(Flutter SDK 迁至 D 盘)
- 后端端口改为 0.0.0.0:5000,支持局域网访问
This commit is contained in:
MingNian
2026-06-02 12:41:06 +08:00
parent 14d7c30d3d
commit 6e69f1085e
47 changed files with 342 additions and 428 deletions

View File

@@ -0,0 +1,186 @@
using Health.Infrastructure.Services;
namespace Health.WebApi.Endpoints;
/// <summary>
/// 认证相关 API 端点
/// </summary>
public static class AuthEndpoints
{
public static void MapAuthEndpoints(this WebApplication app)
{
// 发送短信验证码
app.MapPost("/api/auth/send-sms", async (
SendSmsRequest request,
AppDbContext db,
SmsService sms,
CancellationToken ct) =>
{
// 生成验证码
var code = sms.GenerateCode();
var vc = new VerificationCode
{
Id = Guid.NewGuid(),
Phone = request.Phone,
Code = code,
ExpiresAt = DateTime.UtcNow.AddMinutes(5),
};
db.VerificationCodes.Add(vc);
await db.SaveChangesAsync(ct);
// 开发阶段:直接返回验证码(生产环境需去掉 devCode
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/login", async (
LoginRequest request,
AppDbContext db,
JwtProvider jwt,
CancellationToken ct) =>
{
// 开发阶段任意6位数字通过
var validCode = await db.VerificationCodes
.Where(v => v.Phone == request.Phone
&& v.Code == request.SmsCode
&& v.ExpiresAt > DateTime.UtcNow
&& !v.IsUsed)
.OrderByDescending(v => v.CreatedAt)
.FirstOrDefaultAsync(ct);
if (validCode == null)
return Results.Ok(new { code = 40001, data = (object?)null, message = "验证码错误或已过期" });
validCode.IsUsed = true;
// 查找或创建用户
var user = await db.Users.FirstOrDefaultAsync(u => u.Phone == request.Phone, ct);
var isNew = false;
if (user == null)
{
user = new User
{
Id = Guid.NewGuid(),
Phone = request.Phone,
CreatedAt = DateTime.UtcNow,
UpdatedAt = DateTime.UtcNow,
};
db.Users.Add(user);
isNew = true;
// 创建默认通知偏好
db.NotificationPreferences.Add(new NotificationPreference
{
Id = Guid.NewGuid(),
UserId = user.Id,
});
// 创建空健康档案
db.HealthArchives.Add(new HealthArchive
{
Id = Guid.NewGuid(),
UserId = user.Id,
});
}
user.UpdatedAt = DateTime.UtcNow;
await db.SaveChangesAsync(ct);
// 生成 token
var accessToken = jwt.GenerateAccessToken(user.Id, user.Phone);
var refreshToken = jwt.GenerateRefreshToken();
// 保存 refresh token
db.RefreshTokens.Add(new RefreshToken
{
Id = Guid.NewGuid(),
UserId = user.Id,
Token = refreshToken,
ExpiresAt = DateTime.UtcNow.AddDays(30),
});
await db.SaveChangesAsync(ct);
return Results.Ok(new
{
code = 0,
data = new
{
accessToken,
refreshToken,
user = new
{
user.Id,
user.Phone,
user.Name,
user.Gender,
BirthDate = user.BirthDate?.ToString("yyyy-MM-dd"),
user.AvatarUrl,
isNew
}
},
message = (string?)null
});
});
// 刷新 token
app.MapPost("/api/auth/refresh", async (
RefreshRequest request,
AppDbContext db,
JwtProvider jwt,
CancellationToken ct) =>
{
var oldToken = await db.RefreshTokens
.FirstOrDefaultAsync(t => t.Token == request.RefreshToken && !t.IsRevoked, ct);
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);
if (user == null)
return Results.Ok(new { code = 40002, data = (object?)null, message = "用户不存在" });
// 生成新 token续期
var accessToken = jwt.GenerateAccessToken(user.Id, user.Phone);
var newRefreshToken = jwt.GenerateRefreshToken();
db.RefreshTokens.Add(new RefreshToken
{
Id = Guid.NewGuid(),
UserId = user.Id,
Token = newRefreshToken,
ExpiresAt = DateTime.UtcNow.AddDays(30),
});
await db.SaveChangesAsync(ct);
return Results.Ok(new
{
code = 0,
data = new { accessToken, refreshToken = newRefreshToken },
message = (string?)null
});
});
// 登出
app.MapPost("/api/auth/logout", async (
RefreshRequest request,
AppDbContext db,
CancellationToken ct) =>
{
var token = await db.RefreshTokens
.FirstOrDefaultAsync(t => t.Token == request.RefreshToken, ct);
if (token != null) token.IsRevoked = true;
await db.SaveChangesAsync(ct);
return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
});
}
}
// ---- 请求 DTO ----
public sealed record SendSmsRequest(string Phone);
public sealed record LoginRequest(string Phone, string SmsCode);
public sealed record RefreshRequest(string RefreshToken);