250 lines
8.3 KiB
C#
Raw Normal View History

2026-03-31 15:54:27 +08:00
using EasyCaching.Core;
2026-03-14 06:13:06 +08:00
using Microsoft.Extensions.Logging;
2025-12-17 11:12:13 +08:00
using YD_Event.Application.Client.Dtos;
2025-12-17 10:32:52 +08:00
2025-12-17 11:12:13 +08:00
namespace YD_Event.Application.Client;
2026-03-14 06:13:06 +08:00
2025-12-17 10:32:52 +08:00
/// <summary>
2026-03-14 06:13:06 +08:00
/// 微信小程序认证接口
2025-12-17 10:32:52 +08:00
/// </summary>
2025-12-27 12:53:54 +08:00
[ApiDescriptionSettings("微信小程序接口")]
2025-12-17 10:32:52 +08:00
public class OAuthController : IDynamicApiController
{
2026-03-14 06:13:06 +08:00
private readonly ISqlSugarRepository<YD_Event.Core.Entities.AppUser> _appUserRepository;
2026-03-31 15:54:27 +08:00
private readonly IEasyCachingProvider _cachingProvider;
2025-12-17 10:32:52 +08:00
private readonly IIdGenerator _idGenerator;
2026-03-14 06:13:06 +08:00
private readonly ILogger<OAuthController> _logger;
private const string SmsCodeCachePrefix = "sms:code:";
2025-12-17 10:32:52 +08:00
2026-03-14 06:13:06 +08:00
public OAuthController(
ISqlSugarRepository<YD_Event.Core.Entities.AppUser> appUserRepository,
2026-03-31 15:54:27 +08:00
IEasyCachingProvider cachingProvider,
2026-03-14 06:13:06 +08:00
IIdGenerator idGenerator,
ILogger<OAuthController> logger)
2025-12-17 10:32:52 +08:00
{
2026-03-14 06:13:06 +08:00
_appUserRepository = appUserRepository;
2026-03-31 15:54:27 +08:00
_cachingProvider = cachingProvider;
2025-12-17 10:32:52 +08:00
_idGenerator = idGenerator;
2026-03-14 06:13:06 +08:00
_logger = logger;
2025-12-17 10:32:52 +08:00
}
/// <summary>
/// 登录接口(支持账号密码登录和短信验证码登录两种方式)
2025-12-17 10:32:52 +08:00
/// </summary>
2026-03-14 06:13:06 +08:00
/// <param name="input"></param>
2025-12-17 10:32:52 +08:00
/// <returns></returns>
2026-03-14 06:13:06 +08:00
[HttpPost("login")]
2025-12-17 10:32:52 +08:00
[AllowAnonymous]
public async Task<MiniProgramLoginOutput> Login([FromBody] MiniProgramLoginInput input)
2025-12-17 10:32:52 +08:00
{
var isPasswordLogin = !string.IsNullOrEmpty(input.Password);
var isSmsLogin = !string.IsNullOrEmpty(input.VerificationCode);
if (!isPasswordLogin && !isSmsLogin)
{
throw Oops.Bah("请选择密码登录或短信验证码登录");
}
if (isPasswordLogin && isSmsLogin)
{
throw Oops.Bah("请选择一种登录方式");
}
2026-03-14 06:13:06 +08:00
var appUser = await _appUserRepository.GetFirstAsync(x => x.Mobile == input.Mobile);
2026-04-15 09:58:05 +08:00
// 统一的未注册账户判断逻辑
bool isRegistered = appUser != null && !string.IsNullOrEmpty(appUser.Password);
if (!isRegistered)
2025-12-17 10:32:52 +08:00
{
2026-04-16 16:43:11 +08:00
if (appUser == null || string.IsNullOrEmpty(appUser.Password))
2026-03-14 06:13:06 +08:00
{
2026-04-13 13:23:00 +08:00
throw Oops.Bah("未注册过账户");
2026-03-14 06:13:06 +08:00
}
2026-04-15 09:58:05 +08:00
}
2026-04-22 14:47:50 +08:00
// 检查用户类型 - 仅允许运动员登录
if (appUser.PersonType != PersonType.Athlete)
{
throw Oops.Bah("未注册用户");
}
2026-04-15 09:58:05 +08:00
if (isPasswordLogin)
{
var encryptedPassword = MD5Encryption.Encrypt($"{_idGenerator.Encode(appUser.Id)}{input.Password}");
if (appUser.Password != encryptedPassword)
2026-03-14 06:13:06 +08:00
{
throw Oops.Bah("密码错误");
2026-03-14 06:13:06 +08:00
}
}
else
{
var cacheKey = $"{SmsCodeCachePrefix}{input.Mobile}";
var cachedCodeResult = _cachingProvider.Get<string>(cacheKey);
string cachedCode = cachedCodeResult.Value;
if (!cachedCodeResult.HasValue || cachedCode != input.VerificationCode)
{
throw Oops.Bah("验证码错误或已过期");
}
_cachingProvider.Remove(cacheKey);
}
2026-03-14 06:13:06 +08:00
(string accessToken, string refreshToken) = GenerateTokens(appUser);
var userInfo = new
{
appUser.Name,
appUser.Id,
appUser.Mobile,
appUser.Avatar,
appUser.NickName
};
return new MiniProgramLoginOutput
2026-03-14 06:13:06 +08:00
{
AccessToken = accessToken,
RefreshToken = refreshToken,
ExpiresIn = 7200,
2026-03-14 06:13:06 +08:00
UserInfo = userInfo
2025-12-17 10:32:52 +08:00
};
}
/// <summary>
2026-03-14 06:13:06 +08:00
/// 注册接口
2025-12-17 10:32:52 +08:00
/// </summary>
2026-03-14 06:13:06 +08:00
/// <param name="input"></param>
2025-12-17 10:32:52 +08:00
/// <returns></returns>
2026-03-14 06:13:06 +08:00
[HttpPost("register")]
2025-12-17 10:32:52 +08:00
[AllowAnonymous]
2026-03-14 06:13:06 +08:00
[UnitOfWork]
public async Task<MiniProgramRegisterOutput> Register([FromBody] MiniProgramRegisterInput input)
2025-12-17 10:32:52 +08:00
{
var cacheKey = $"{SmsCodeCachePrefix}{input.Mobile}";
2026-03-31 15:54:27 +08:00
var cachedCodeResult = _cachingProvider.Get<string>(cacheKey);
string cachedCode = cachedCodeResult.Value;
if (!cachedCodeResult.HasValue || cachedCode != input.VerificationCode)
2025-12-17 10:32:52 +08:00
{
throw Oops.Bah("验证码错误或已过期");
2025-12-17 10:32:52 +08:00
}
2026-03-31 15:54:27 +08:00
_cachingProvider.Remove(cacheKey);
2026-03-14 06:13:06 +08:00
2026-04-13 13:23:00 +08:00
// 检查手机号是否已存在
var existingUser = await _appUserRepository.GetFirstAsync(x => x.Mobile == input.Mobile && !x.DeleteMark);
YD_Event.Core.Entities.AppUser appUser;
bool isNewUser = false;
if (existingUser != null)
2026-03-14 06:13:06 +08:00
{
2026-04-13 13:23:00 +08:00
// 手机号已存在,检查是否已设置密码
if (!string.IsNullOrEmpty(existingUser.Password))
{
throw Oops.Bah("该手机号已注册");
}
// 未设置密码,更新现有记录
appUser = existingUser;
var encryptedPassword = MD5Encryption.Encrypt($"{_idGenerator.Encode(appUser.Id)}{input.Password}");
appUser.Password = encryptedPassword;
appUser.UpdatedTime = DateTime.Now;
}
else
{
// 手机号不存在,创建新用户
isNewUser = true;
long appUserId = _idGenerator.NewLong();
var encryptedPassword = MD5Encryption.Encrypt($"{_idGenerator.Encode(appUserId)}{input.Password}");
appUser = new YD_Event.Core.Entities.AppUser
{
Id = appUserId,
Name = input.NickName ?? input.Mobile,
NickName = input.NickName,
Avatar = input.Avatar,
Mobile = input.Mobile,
Password = encryptedPassword,
2026-04-22 14:47:50 +08:00
PersonType = PersonType.Athlete,
2026-04-13 13:23:00 +08:00
Status = AvailabilityStatus.Enable,
2026-04-13 13:37:50 +08:00
AuditStatus = AuditStatus.PendingSubmission,
2026-04-13 13:23:00 +08:00
CreatedTime = DateTime.Now,
CreatedUserId = 0
};
2026-03-14 06:13:06 +08:00
}
2026-04-13 13:23:00 +08:00
if (isNewUser)
{
await _appUserRepository.InsertAsync(appUser);
}
else
{
await _appUserRepository.UpdateAsync(appUser);
}
2026-03-14 06:13:06 +08:00
return new MiniProgramRegisterOutput
2026-03-14 06:13:06 +08:00
{
UserId = appUser.Id,
Mobile = appUser.Mobile,
NickName = appUser.NickName,
2026-04-13 13:23:00 +08:00
RegisterTime = isNewUser ? appUser.CreatedTime : (appUser.UpdatedTime ?? appUser.CreatedTime)
2026-03-14 06:13:06 +08:00
};
2025-12-17 10:32:52 +08:00
}
/// <summary>
/// 重置密码接口
2025-12-17 10:32:52 +08:00
/// </summary>
2026-03-14 06:13:06 +08:00
/// <param name="input"></param>
2025-12-17 10:32:52 +08:00
/// <returns></returns>
2026-03-14 06:13:06 +08:00
[HttpPost("reset-password")]
2025-12-17 10:32:52 +08:00
[AllowAnonymous]
public async Task ResetPassword([FromBody] MiniProgramResetPasswordInput input)
2025-12-17 10:32:52 +08:00
{
var cacheKey = $"{SmsCodeCachePrefix}{input.Mobile}";
2026-03-31 15:54:27 +08:00
var cachedCodeResult = _cachingProvider.Get<string>(cacheKey);
string cachedCode = cachedCodeResult.Value;
if (!cachedCodeResult.HasValue || cachedCode != input.VerificationCode)
2025-12-17 10:32:52 +08:00
{
throw Oops.Bah("验证码错误或已过期");
2026-03-14 06:13:06 +08:00
}
2026-03-31 15:54:27 +08:00
_cachingProvider.Remove(cacheKey);
2026-03-14 06:13:06 +08:00
var appUser = await _appUserRepository.GetFirstAsync(x => x.Mobile == input.Mobile);
if (appUser == null)
2026-03-14 06:13:06 +08:00
{
throw Oops.Bah("用户不存在");
2026-03-14 06:13:06 +08:00
}
2026-04-22 14:47:50 +08:00
// 检查用户类型 - 仅允许运动员重置密码
if (appUser.PersonType != PersonType.Athlete)
{
throw Oops.Bah("用户不存在");
}
var encryptedPassword = MD5Encryption.Encrypt($"{_idGenerator.Encode(appUser.Id)}{input.NewPassword}");
appUser.Password = encryptedPassword;
appUser.UpdatedTime = DateTime.Now;
appUser.UpdatedUserId = appUser.Id;
2025-12-17 10:32:52 +08:00
await _appUserRepository.UpdateAsync(appUser);
2026-03-14 06:13:06 +08:00
}
/// <summary>
/// 生成自定义 Token
/// </summary>
private (string AccessToken, string RefreshToken) GenerateTokens(YD_Event.Core.Entities.AppUser appUser)
2026-03-14 06:13:06 +08:00
{
long uniqueId = _idGenerator.NewLong();
string accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>()
{
[AuthClaimsConst.AuthIdKey] = appUser.Id,
[AuthClaimsConst.AccountKey] = appUser.Mobile,
2026-03-14 06:13:06 +08:00
[AuthClaimsConst.UuidKey] = uniqueId,
[AuthClaimsConst.AuthPlatformTypeKey] = AuthPlatformType.WeChatApplet
2026-04-04 22:09:43 +08:00
});
2026-03-14 06:13:06 +08:00
string refreshToken = JWTEncryption.GenerateRefreshToken(accessToken);
return (accessToken, refreshToken);
2025-12-17 10:32:52 +08:00
}
2026-03-02 11:19:25 +08:00
}