diff --git a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Config/ConfigModels.cs b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Config/ConfigModels.cs index 69fdde4..3cb289f 100644 --- a/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Config/ConfigModels.cs +++ b/server/MiAssessment/src/MiAssessment.Admin.Business/Models/Config/ConfigModels.cs @@ -1,4 +1,4 @@ -using System.Text.Json.Serialization; +using System.Text.Json.Serialization; namespace MiAssessment.Admin.Business.Models.Config; @@ -630,8 +630,21 @@ public class UserConfigSetting /// [JsonPropertyName("uid_length")] public string? UidLength { get; set; } + + /// + /// 默认昵称前缀,如"用户",注册时自动拼接随机数字 + /// + [JsonPropertyName("default_nickname_prefix")] + public string? DefaultNicknamePrefix { get; set; } + + /// + /// 默认头像URL,为空时使用系统生成的头像 + /// + [JsonPropertyName("default_avatar")] + public string? DefaultAvatar { get; set; } } + #endregion #region 内测配置模型 diff --git a/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs b/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs index 8fc08ad..cdf1a3a 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs +++ b/server/MiAssessment/src/MiAssessment.Admin/Controllers/ConfigController.cs @@ -29,6 +29,7 @@ public class ConfigController : ControllerBase public const string Upload = "uploads"; public const string Miniprogram = "miniprogram_setting"; public const string WeixinPay = "weixinpay_setting"; + public const string UserConfig = "user_config"; } public ConfigController(IAdminConfigService configService, ILogger logger) @@ -284,4 +285,54 @@ public class ConfigController : ControllerBase } #endregion + + #region 用户配置 + + /// + /// 获取用户配置 + /// + /// 用户配置 + [HttpGet("user/get")] + public async Task> GetUserConfig() + { + try + { + var config = await _configService.GetConfigAsync(ConfigKeys.UserConfig); + return ApiResponse.Success(config ?? new UserConfigSetting + { + UidType = "2", + UidLength = "6", + DefaultNicknamePrefix = "用户", + DefaultAvatar = "" + }); + } + catch (Exception ex) + { + _logger.LogError(ex, "获取用户配置失败"); + return ApiResponse.Error(AdminErrorCodes.InternalError, "获取用户配置失败"); + } + } + + /// + /// 更新用户配置 + /// + /// 用户配置 + /// 更新结果 + [HttpPost("user/update")] + [OperationLog("配置管理", "更新用户配置")] + public async Task> UpdateUserConfig([FromBody] UserConfigSetting request) + { + try + { + var result = await _configService.UpdateConfigAsync(ConfigKeys.UserConfig, request); + return ApiResponse.Success(result, "用户配置更新成功"); + } + catch (Exception ex) + { + _logger.LogError(ex, "更新用户配置失败"); + return ApiResponse.Error(AdminErrorCodes.InternalError, "更新用户配置失败"); + } + } + + #endregion } diff --git a/server/MiAssessment/src/MiAssessment.Admin/Models/Config/UserConfigSetting.cs b/server/MiAssessment/src/MiAssessment.Admin/Models/Config/UserConfigSetting.cs new file mode 100644 index 0000000..2ecb843 --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin/Models/Config/UserConfigSetting.cs @@ -0,0 +1,33 @@ +using System.Text.Json.Serialization; + +namespace MiAssessment.Admin.Models.Config; + +/// +/// 用户配置 +/// +public class UserConfigSetting +{ + /// + /// UID类型 1真实ID 2数字ID 3随机字符和数字 + /// + [JsonPropertyName("uid_type")] + public string? UidType { get; set; } + + /// + /// UID长度 + /// + [JsonPropertyName("uid_length")] + public string? UidLength { get; set; } + + /// + /// 默认昵称前缀,如"用户",注册时自动拼接随机数字 + /// + [JsonPropertyName("default_nickname_prefix")] + public string? DefaultNicknamePrefix { get; set; } + + /// + /// 默认头像URL,为空时使用系统生成的头像 + /// + [JsonPropertyName("default_avatar")] + public string? DefaultAvatar { get; set; } +} diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts index e6060d7..4020ecc 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/api/system/config.ts @@ -165,3 +165,40 @@ export function updateWeixinPayConfig(data: WeixinPaySetting): Promise> { + return request({ + url: '/admin/config/user/get', + method: 'get' + }) +} + +/** + * 更新用户配置 + */ +export function updateUserConfig(data: UserConfigSetting): Promise> { + return request({ + url: '/admin/config/user/update', + method: 'post', + data + }) +} diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/index.vue b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/index.vue index b617092..f7f8959 100644 --- a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/index.vue +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/index.vue @@ -10,6 +10,9 @@ + + + @@ -22,6 +25,7 @@ import { ref } from 'vue' import UploadConfig from './upload.vue' import MiniprogramConfig from './miniprogram.vue' import PaymentConfig from './payment.vue' +import UserConfig from './user.vue' // 当前激活的Tab const activeTab = ref('miniprogram') diff --git a/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/user.vue b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/user.vue new file mode 100644 index 0000000..67eb26f --- /dev/null +++ b/server/MiAssessment/src/MiAssessment.Admin/admin-web/src/views/system/config/user.vue @@ -0,0 +1,270 @@ + + + + + + 用户配置 + 配置新用户注册时的UID生成规则、默认昵称前缀和默认头像 + + + + + + + + UID 配置 + + + + 真实ID + 数字ID + 随机字符和数字 + + + + + + 新用户UID的位数,建议6位 + + + + + + 默认昵称 + + + + 新用户注册时的昵称前缀,系统会自动拼接6位随机数字,如"用户123456" + + + + + + 默认头像 + + + + + + + + + + + + + + 系统生成 + + + + + 填写图片URL作为所有新用户的默认头像,留空则系统自动生成唯一头像 + + + + + + + + 保存配置 + 重置 + + + + + + + + diff --git a/server/MiAssessment/src/MiAssessment.Core/Services/AuthService.cs b/server/MiAssessment/src/MiAssessment.Core/Services/AuthService.cs index 680919c..986b238 100644 --- a/server/MiAssessment/src/MiAssessment.Core/Services/AuthService.cs +++ b/server/MiAssessment/src/MiAssessment.Core/Services/AuthService.cs @@ -1,5 +1,6 @@ using System.Security.Cryptography; using System.Text; +using System.Text.Json; using MiAssessment.Core.Interfaces; using MiAssessment.Model.Data; using MiAssessment.Model.Entities; @@ -10,7 +11,7 @@ using Microsoft.Extensions.Logging; namespace MiAssessment.Core.Services; /// -/// ֤ʵ +/// ��֤����ʵ�� /// public class AuthService : IAuthService { @@ -20,6 +21,7 @@ public class AuthService : IAuthService private readonly IWechatService _wechatService; private readonly IIpLocationService _ipLocationService; private readonly IRedisService _redisService; + private readonly IConfigService _configService; private readonly JwtSettings _jwtSettings; private readonly ILogger _logger; @@ -28,7 +30,7 @@ public class AuthService : IAuthService private const string SmsCodeKeyPrefix = "sms:code:"; private const int DebounceSeconds = 3; - // Refresh Token + // Refresh Token ���� private const int RefreshTokenLength = 64; public AuthService( @@ -38,6 +40,7 @@ public class AuthService : IAuthService IWechatService wechatService, IIpLocationService ipLocationService, IRedisService redisService, + IConfigService configService, JwtSettings jwtSettings, ILogger logger) { @@ -47,50 +50,51 @@ public class AuthService : IAuthService _wechatService = wechatService ?? throw new ArgumentNullException(nameof(wechatService)); _ipLocationService = ipLocationService ?? throw new ArgumentNullException(nameof(ipLocationService)); _redisService = redisService ?? throw new ArgumentNullException(nameof(redisService)); + _configService = configService ?? throw new ArgumentNullException(nameof(configService)); _jwtSettings = jwtSettings ?? throw new ArgumentNullException(nameof(jwtSettings)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } /// - /// С¼ + /// ��С�����¼ /// Requirements: 1.1-1.8 /// public async Task WechatMiniProgramLoginAsync(string code, int? pid, string? clickId) { - _logger.LogInformation("[AuthService] ŵ¼ʼcode={Code}, pid={Pid}", code, pid); + _logger.LogInformation("[AuthService] �ŵ�¼��ʼ��code={Code}, pid={Pid}", code, pid); if (string.IsNullOrWhiteSpace(code)) { - _logger.LogWarning("[AuthService] ŵ¼ʧܣcodeΪ"); + _logger.LogWarning("[AuthService] �ŵ�¼ʧ�ܣ�codeΪ��"); return new LoginResult { Success = false, - ErrorMessage = "ȨcodeΪ" + ErrorMessage = "��Ȩcode����Ϊ��" }; } try { - // 1.6 - 3ڲظ¼ + // 1.6 �������� - 3���ڲ������ظ���¼ var debounceKey = $"{LoginDebounceKeyPrefix}wechat:{code}"; - _logger.LogInformation("[AuthService] : {Key}", debounceKey); + _logger.LogInformation("[AuthService] ��������: {Key}", debounceKey); var lockAcquired = await _redisService.TryAcquireLockAsync(debounceKey, "1", TimeSpan.FromSeconds(DebounceSeconds)); if (!lockAcquired) { - _logger.LogWarning("[AuthService] ܾظ¼: {Code}", code); + _logger.LogWarning("[AuthService] �����������ܾ��ظ���¼����: {Code}", code); return new LoginResult { Success = false, - ErrorMessage = "Ƶ¼" + ErrorMessage = "����Ƶ����¼" }; } - _logger.LogInformation("[AuthService] ȡɹ"); + _logger.LogInformation("[AuthService] ��������ȡ�ɹ�"); - // 1.1 APIȡopenidunionid - _logger.LogInformation("[AuthService] ʼAPIȡopenid..."); + // 1.1 ������API��ȡopenid��unionid + _logger.LogInformation("[AuthService] ��ʼ������API��ȡopenid..."); var wechatResult = await _wechatService.GetOpenIdAsync(code); - _logger.LogInformation("[AuthService] APIɣSuccess={Success}, OpenId={OpenId}, UnionId={UnionId}, Error={Error}", + _logger.LogInformation("[AuthService] ��API������ɣ�Success={Success}, OpenId={OpenId}, UnionId={UnionId}, Error={Error}", wechatResult.Success, wechatResult.OpenId ?? "null", wechatResult.UnionId ?? "null", @@ -98,89 +102,89 @@ public class AuthService : IAuthService if (!wechatResult.Success) { - _logger.LogWarning("[AuthService] APIʧ: {Error}", wechatResult.ErrorMessage); + _logger.LogWarning("[AuthService] ��API����ʧ��: {Error}", wechatResult.ErrorMessage); return new LoginResult { Success = false, - ErrorMessage = wechatResult.ErrorMessage ?? "¼ʧܣԺ" + ErrorMessage = wechatResult.ErrorMessage ?? "��¼ʧ�ܣ����Ժ�����" }; } var openId = wechatResult.OpenId!; var unionId = wechatResult.UnionId; - // 1.2 û - ͨunionidңͨopenid + // 1.2 �����û� - ����ͨ��unionid���ң����ͨ��openid���� User? user = null; if (!string.IsNullOrWhiteSpace(unionId)) { - _logger.LogInformation("[AuthService] ͨunionidû: {UnionId}", unionId); + _logger.LogInformation("[AuthService] ����ͨ��unionid�����û�: {UnionId}", unionId); user = await _userService.GetUserByUnionIdAsync(unionId); - _logger.LogInformation("[AuthService] unionidҽ: {Found}", user != null ? $"ҵûID={user.Id}" : "δҵ"); + _logger.LogInformation("[AuthService] unionid���ҽ��: {Found}", user != null ? $"�ҵ��û�ID={user.Id}" : "δ�ҵ�"); } if (user == null) { - _logger.LogInformation("[AuthService] ͨopenidû: {OpenId}", openId); + _logger.LogInformation("[AuthService] ����ͨ��openid�����û�: {OpenId}", openId); user = await _userService.GetUserByOpenIdAsync(openId); - _logger.LogInformation("[AuthService] openidҽ: {Found}", user != null ? $"ҵûID={user.Id}" : "δҵ"); + _logger.LogInformation("[AuthService] openid���ҽ��: {Found}", user != null ? $"�ҵ��û�ID={user.Id}" : "δ�ҵ�"); } if (user == null) { - // 1.3 ûڣû - _logger.LogInformation("[AuthService] ûڣʼû..."); + // 1.3 �û������ڣ��������û� + _logger.LogInformation("[AuthService] �û������ڣ���ʼ�������û�..."); var createDto = new CreateUserDto { OpenId = openId, UnionId = unionId, - Nickname = $"û{Random.Shared.Next(100000, 999999)}", - Headimg = GenerateDefaultAvatar(openId), + Nickname = await GetDefaultNicknameAsync(), + Headimg = await GetDefaultAvatarAsync(openId), Pid = pid ?? 0 }; user = await _userService.CreateUserAsync(createDto); - _logger.LogInformation("[AuthService] ûɹ: UserId={UserId}, OpenId={OpenId}", user.Id, openId); + _logger.LogInformation("[AuthService] ���û������ɹ�: UserId={UserId}, OpenId={OpenId}", user.Id, openId); } else { - // 1.4 ûڣunionid֮ǰΪգ + // 1.4 �û����ڣ�����unionid�����֮ǰΪ�գ� if (string.IsNullOrWhiteSpace(user.UnionId) && !string.IsNullOrWhiteSpace(unionId)) { - _logger.LogInformation("[AuthService] ûunionid: UserId={UserId}", user.Id); + _logger.LogInformation("[AuthService] �����û�unionid: UserId={UserId}", user.Id); await _userService.UpdateUserAsync(user.Id, new UpdateUserDto { UnionId = unionId }); - _logger.LogInformation("[AuthService] unionid³ɹ"); + _logger.LogInformation("[AuthService] unionid���³ɹ�"); } } - // 1.5 ˫ TokenAccess Token + Refresh Token - _logger.LogInformation("[AuthService] ʼ˫ Token: UserId={UserId}", user.Id); + // 1.5 ����˫ Token��Access Token + Refresh Token�� + _logger.LogInformation("[AuthService] ��ʼ����˫ Token: UserId={UserId}", user.Id); var loginResponse = await GenerateLoginResponseAsync(user, null); - _logger.LogInformation("[AuthService] ˫ Token ɳɹAccessToken={Length}", loginResponse.AccessToken?.Length ?? 0); + _logger.LogInformation("[AuthService] ˫ Token ���ɳɹ���AccessToken����={Length}", loginResponse.AccessToken?.Length ?? 0); - _logger.LogInformation("[AuthService] ŵ¼ɹ: UserId={UserId}", user.Id); + _logger.LogInformation("[AuthService] �ŵ�¼�ɹ�: UserId={UserId}", user.Id); return new LoginResult { Success = true, - Token = loginResponse.AccessToken, // ݾɰ + Token = loginResponse.AccessToken, // ���ݾɰ� UserId = user.Id, LoginResponse = loginResponse }; } catch (Exception ex) { - _logger.LogError(ex, "[AuthService] ŵ¼쳣: code={Code}, Message={Message}, StackTrace={StackTrace}", + _logger.LogError(ex, "[AuthService] �ŵ�¼�쳣: code={Code}, Message={Message}, StackTrace={StackTrace}", code, ex.Message, ex.StackTrace); return new LoginResult { Success = false, - ErrorMessage = "ϣԺ" + ErrorMessage = "������ϣ����Ժ�����" }; } } /// - /// ֻ֤¼ + /// �ֻ�����֤���¼ /// Requirements: 2.1-2.7 /// public async Task MobileLoginAsync(string mobile, string code, int? pid, string? clickId) @@ -190,7 +194,7 @@ public class AuthService : IAuthService return new LoginResult { Success = false, - ErrorMessage = "ֻŲΪ" + ErrorMessage = "�ֻ��Ų���Ϊ��" }; } @@ -199,13 +203,13 @@ public class AuthService : IAuthService return new LoginResult { Success = false, - ErrorMessage = "֤벻Ϊ" + ErrorMessage = "��֤�벻��Ϊ��" }; } try { - // 2.6 - 3ڲظ¼ + // 2.6 �������� - 3���ڲ������ظ���¼ var debounceKey = $"{LoginDebounceKeyPrefix}mobile:{mobile}"; var lockAcquired = await _redisService.TryAcquireLockAsync(debounceKey, "1", TimeSpan.FromSeconds(DebounceSeconds)); if (!lockAcquired) @@ -214,11 +218,11 @@ public class AuthService : IAuthService return new LoginResult { Success = false, - ErrorMessage = "Ƶ¼" + ErrorMessage = "����Ƶ����¼" }; } - // 2.1 Redisȡ֤֤ + // 2.1 ��Redis��ȡ����֤��֤�� var smsCodeKey = $"{SmsCodeKeyPrefix}{mobile}"; var storedCode = await _redisService.GetStringAsync(smsCodeKey); @@ -228,24 +232,24 @@ public class AuthService : IAuthService return new LoginResult { Success = false, - ErrorMessage = "֤" + ErrorMessage = "��֤�����" }; } - // 2.2 ֤֤ͨɾRedisе֤ + // 2.2 ��֤����֤ͨ����ɾ��Redis�е���֤�� await _redisService.DeleteAsync(smsCodeKey); - // û + // �����û� var user = await _userService.GetUserByMobileAsync(mobile); if (user == null) { - // 2.3 ûڣû + // 2.3 �û������ڣ��������û� var createDto = new CreateUserDto { Mobile = mobile, - Nickname = $"û{Random.Shared.Next(100000, 999999)}", - Headimg = GenerateDefaultAvatar(mobile), + Nickname = await GetDefaultNicknameAsync(), + Headimg = await GetDefaultAvatarAsync(mobile), Pid = pid ?? 0 }; @@ -253,7 +257,7 @@ public class AuthService : IAuthService _logger.LogInformation("New user created via mobile login: UserId={UserId}, Mobile={Mobile}", user.Id, MaskMobile(mobile)); } - // 2.4 ˫ TokenAccess Token + Refresh Token + // 2.4 ����˫ Token��Access Token + Refresh Token�� var loginResponse = await GenerateLoginResponseAsync(user, null); _logger.LogInformation("Mobile login successful: UserId={UserId}", user.Id); @@ -261,7 +265,7 @@ public class AuthService : IAuthService return new LoginResult { Success = true, - Token = loginResponse.AccessToken, // ݾɰ + Token = loginResponse.AccessToken, // ���ݾɰ� UserId = user.Id, LoginResponse = loginResponse }; @@ -272,58 +276,58 @@ public class AuthService : IAuthService return new LoginResult { Success = false, - ErrorMessage = "ϣԺ" + ErrorMessage = "������ϣ����Ժ�����" }; } } /// - /// ֻ֤ + /// ��֤����ֻ��� /// Requirements: 5.1-5.5 /// public async Task BindMobileAsync(long userId, string mobile, string code) { if (string.IsNullOrWhiteSpace(mobile)) { - throw new ArgumentException("ֻŲΪ", nameof(mobile)); + throw new ArgumentException("�ֻ��Ų���Ϊ��", nameof(mobile)); } if (string.IsNullOrWhiteSpace(code)) { - throw new ArgumentException("֤벻Ϊ", nameof(code)); + throw new ArgumentException("��֤�벻��Ϊ��", nameof(code)); } - // 5.1 ֤֤ + // 5.1 ��֤������֤�� var smsCodeKey = $"{SmsCodeKeyPrefix}{mobile}"; var storedCode = await _redisService.GetStringAsync(smsCodeKey); if (string.IsNullOrWhiteSpace(storedCode) || storedCode != code) { _logger.LogWarning("SMS code verification failed for bind mobile: UserId={UserId}, Mobile={Mobile}", userId, MaskMobile(mobile)); - throw new InvalidOperationException("֤"); + throw new InvalidOperationException("��֤�����"); } - // ֤֤ͨɾ + // ��֤����֤ͨ����ɾ�� await _redisService.DeleteAsync(smsCodeKey); - // ȡǰû + // ��ȡ��ǰ�û� var currentUser = await _userService.GetUserByIdAsync(userId); if (currentUser == null) { - throw new InvalidOperationException("û"); + throw new InvalidOperationException("�û�������"); } - // ֻǷѱû + // ����ֻ����Ƿ��ѱ������û��� var existingUser = await _userService.GetUserByMobileAsync(mobile); if (existingUser != null && existingUser.Id != userId) { - // 5.2 ֻѱûҪϲ˻ + // 5.2 �ֻ����ѱ������û�����Ҫ�ϲ��˻� return await MergeAccountsAsync(currentUser, existingUser); } - // 5.4 ֻδֱӸµǰûֻ + // 5.4 �ֻ���δ����ֱ�Ӹ��µ�ǰ�û����ֻ��� await _userService.UpdateUserAsync(userId, new UpdateUserDto { Mobile = mobile }); _logger.LogInformation("Mobile bound successfully: UserId={UserId}, Mobile={Mobile}", userId, MaskMobile(mobile)); @@ -331,43 +335,43 @@ public class AuthService : IAuthService } /// - /// Ȩֻ + /// ����Ȩ���ֻ��� /// Requirements: 5.1-5.5 /// public async Task WechatBindMobileAsync(long userId, string wechatCode) { if (string.IsNullOrWhiteSpace(wechatCode)) { - throw new ArgumentException("ȨcodeΪ", nameof(wechatCode)); + throw new ArgumentException("����Ȩcode����Ϊ��", nameof(wechatCode)); } - // APIȡֻ + // ������API��ȡ�ֻ��� var mobileResult = await _wechatService.GetMobileAsync(wechatCode); if (!mobileResult.Success || string.IsNullOrWhiteSpace(mobileResult.Mobile)) { _logger.LogWarning("WeChat get mobile failed: UserId={UserId}, Error={Error}", userId, mobileResult.ErrorMessage); - throw new InvalidOperationException(mobileResult.ErrorMessage ?? "ȡֻʧ"); + throw new InvalidOperationException(mobileResult.ErrorMessage ?? "��ȡ�ֻ���ʧ��"); } var mobile = mobileResult.Mobile; - // ȡǰû + // ��ȡ��ǰ�û� var currentUser = await _userService.GetUserByIdAsync(userId); if (currentUser == null) { - throw new InvalidOperationException("û"); + throw new InvalidOperationException("�û�������"); } - // ֻǷѱû + // ����ֻ����Ƿ��ѱ������û��� var existingUser = await _userService.GetUserByMobileAsync(mobile); if (existingUser != null && existingUser.Id != userId) { - // 5.2 ֻѱûҪϲ˻ + // 5.2 �ֻ����ѱ������û�����Ҫ�ϲ��˻� return await MergeAccountsAsync(currentUser, existingUser); } - // 5.4 ֻδֱӸµǰûֻ + // 5.4 �ֻ���δ����ֱ�Ӹ��µ�ǰ�û����ֻ��� await _userService.UpdateUserAsync(userId, new UpdateUserDto { Mobile = mobile }); _logger.LogInformation("Mobile bound via WeChat successfully: UserId={UserId}, Mobile={Mobile}", userId, MaskMobile(mobile)); @@ -376,7 +380,7 @@ public class AuthService : IAuthService /// - /// ¼¼Ϣ + /// ��¼��¼��Ϣ /// Requirements: 6.1, 6.3, 6.4 /// public async Task RecordLoginAsync(long userId, string? device, string? deviceInfo) @@ -384,17 +388,17 @@ public class AuthService : IAuthService var user = await _userService.GetUserByIdAsync(userId); if (user == null) { - throw new InvalidOperationException("û"); + throw new InvalidOperationException("�û�������"); } try { - // ȡͻIPʹÿַΪռλʵIPӦController룩 + // ��ȡ�ͻ���IP������ʹ�ÿ��ַ�����Ϊռλ����ʵ��IPӦ��Controller���룩 var clientIp = deviceInfo ?? string.Empty; var now = DateTime.Now; - // 6.1 ¼¼־ + // 6.1 ��¼��¼��־ var loginLog = new UserLoginLog { UserId = userId, @@ -408,7 +412,7 @@ public class AuthService : IAuthService await _dbContext.UserLoginLogs.AddAsync(loginLog); - // û¼ʱ + // �����û�����¼ʱ�� user.LastLoginTime = now; user.LastLoginIp = clientIp; _dbContext.Users.Update(user); @@ -417,7 +421,7 @@ public class AuthService : IAuthService _logger.LogInformation("Login recorded: UserId={UserId}, Device={Device}, IP={IP}", userId, device, clientIp); - // 6.4 ûuiddzƺͷ + // 6.4 �����û���uid���dzƺ�ͷ�� return new RecordLoginResponse { Uid = user.Uid, @@ -433,33 +437,33 @@ public class AuthService : IAuthService } /// - /// H5ֻţ֤룩 + /// H5���ֻ��ţ�������֤�룩 /// Requirements: 13.1 /// public async Task BindMobileH5Async(long userId, string mobile) { if (string.IsNullOrWhiteSpace(mobile)) { - throw new ArgumentException("ֻŲΪ", nameof(mobile)); + throw new ArgumentException("�ֻ��Ų���Ϊ��", nameof(mobile)); } - // ȡǰû + // ��ȡ��ǰ�û� var currentUser = await _userService.GetUserByIdAsync(userId); if (currentUser == null) { - throw new InvalidOperationException("û"); + throw new InvalidOperationException("�û�������"); } - // ֻǷѱû + // ����ֻ����Ƿ��ѱ������û��� var existingUser = await _userService.GetUserByMobileAsync(mobile); if (existingUser != null && existingUser.Id != userId) { - // ֻѱûҪϲ˻ + // �ֻ����ѱ������û�����Ҫ�ϲ��˻� return await MergeAccountsAsync(currentUser, existingUser); } - // ֻδֱӸµǰûֻ + // �ֻ���δ����ֱ�Ӹ��µ�ǰ�û����ֻ��� await _userService.UpdateUserAsync(userId, new UpdateUserDto { Mobile = mobile }); _logger.LogInformation("H5 Mobile bound successfully: UserId={UserId}, Mobile={Mobile}", userId, MaskMobile(mobile)); @@ -467,7 +471,7 @@ public class AuthService : IAuthService } /// - /// ˺ע + /// �˺�ע�� /// Requirements: 7.1-7.3 /// public async Task LogOffAsync(long userId, int type) @@ -475,23 +479,23 @@ public class AuthService : IAuthService var user = await _userService.GetUserByIdAsync(userId); if (user == null) { - throw new InvalidOperationException("û"); + throw new InvalidOperationException("�û�������"); } try { - // 7.1 ¼ע־ - var action = type == 0 ? "ע˺" : "ȡע"; + // 7.1 ��¼ע��������־ + var action = type == 0 ? "ע���˺�" : "ȡ��ע��"; _logger.LogInformation("User log off request: UserId={UserId}, Type={Type}, Action={Action}", userId, type, action); - // Ӹע磺 - // - û״̬Ϊע - // - ûصĻ - // - ֪ͨ + // ����������Ӹ����ע���������磺 + // - ���û�״̬����Ϊ��ע�� + // - �����û���صĻ��� + // - ����֪ͨ�� if (type == 0) { - // ע˺ - û״̬Ϊ + // ע���˺� - ���������û�״̬Ϊ���� user.Status = 0; _dbContext.Users.Update(user); await _dbContext.SaveChangesAsync(); @@ -499,14 +503,14 @@ public class AuthService : IAuthService } else if (type == 1) { - // ȡע - ָû״̬ + // ȡ��ע�� - �ָ��û�״̬ user.Status = 1; _dbContext.Users.Update(user); await _dbContext.SaveChangesAsync(); _logger.LogInformation("User account reactivated: UserId={UserId}", userId); } - // 7.2 עɹϢͨ׳쳣ʾɹ + // 7.2 ����ע���ɹ�����Ϣ��ͨ�����׳��쳣����ʾ�ɹ��� } catch (Exception ex) { @@ -519,24 +523,24 @@ public class AuthService : IAuthService #region Refresh Token Methods /// - /// Refresh Token 洢ݿ + /// ���� Refresh Token ���洢�����ݿ� /// Requirements: 1.4, 1.5, 4.1 /// - /// ûID - /// ͻ IP ַ - /// ɵ Refresh Token + /// �û�ID + /// �ͻ��� IP ��ַ + /// ���ɵ� Refresh Token ���� private async Task GenerateRefreshTokenAsync(long userId, string? ipAddress) { - // Refresh Token + // ������� Refresh Token var refreshToken = GenerateSecureRandomString(RefreshTokenLength); - // SHA256 ϣֵڴ洢 + // ���� SHA256 ��ϣֵ���ڴ洢 var tokenHash = ComputeSha256Hash(refreshToken); - // ʱ䣨7죩 + // �������ʱ�䣨7�죩 var expiresAt = DateTime.Now.AddDays(_jwtSettings.RefreshTokenExpirationDays); - // ݿ¼ + // �������ݿ��¼ var userRefreshToken = new UserRefreshToken { UserId = userId, @@ -555,21 +559,21 @@ public class AuthService : IAuthService } /// - /// ɵ¼Ӧ˫ Token + /// ���ɵ�¼��Ӧ������˫ Token�� /// Requirements: 1.1, 1.2, 1.3, 1.4, 1.5 /// - /// ûʵ - /// ͻ IP ַ - /// ¼Ӧ + /// �û�ʵ�� + /// �ͻ��� IP ��ַ + /// ��¼��Ӧ private async Task GenerateLoginResponseAsync(User user, string? ipAddress) { - // Access Token (JWT) + // ���� Access Token (JWT) var accessToken = _jwtService.GenerateToken(user); - // Refresh Token 洢 + // ���� Refresh Token ���洢 var refreshToken = await GenerateRefreshTokenAsync(user.Id, ipAddress); - // Access Token ʱ䣨룩 + // ���� Access Token ����ʱ�䣨�룩 var expiresIn = _jwtSettings.ExpirationMinutes * 60; return new LoginResponse @@ -582,7 +586,7 @@ public class AuthService : IAuthService } /// - /// ˢ Token + /// ˢ�� Token /// Requirements: 2.1-2.6 /// public async Task RefreshTokenAsync(string refreshToken, string? ipAddress) @@ -590,15 +594,15 @@ public class AuthService : IAuthService if (string.IsNullOrWhiteSpace(refreshToken)) { _logger.LogWarning("Refresh token is empty"); - return RefreshTokenResult.Fail("ˢƲΪ"); + return RefreshTokenResult.Fail("ˢ�����Ʋ���Ϊ��"); } try { - // Token ϣֵ + // ���� Token ��ϣֵ var tokenHash = ComputeSha256Hash(refreshToken); - // Token ¼ + // ���� Token ��¼ var storedToken = await _dbContext.UserRefreshTokens .Include(t => t.User) .FirstOrDefaultAsync(t => t.TokenHash == tokenHash); @@ -606,47 +610,47 @@ public class AuthService : IAuthService if (storedToken == null) { _logger.LogWarning("Refresh token not found: {TokenHash}", tokenHash.Substring(0, 8) + "..."); - return RefreshTokenResult.Fail("Чˢ"); + return RefreshTokenResult.Fail("��Ч��ˢ������"); } - // Ƿѹ + // ����Ƿ��ѹ��� if (storedToken.IsExpired) { _logger.LogWarning("Refresh token expired for user {UserId}", storedToken.UserId); - return RefreshTokenResult.Fail("ˢѹ"); + return RefreshTokenResult.Fail("ˢ�������ѹ���"); } - // Ƿѳ + // ����Ƿ��ѳ��� if (storedToken.IsRevoked) { _logger.LogWarning("Refresh token revoked for user {UserId}", storedToken.UserId); - return RefreshTokenResult.Fail("ˢʧЧ"); + return RefreshTokenResult.Fail("ˢ��������ʧЧ"); } - // ûǷЧ + // ����û��Ƿ��������Ч var user = storedToken.User; if (user == null) { _logger.LogWarning("User not found for refresh token"); - return RefreshTokenResult.Fail("û"); + return RefreshTokenResult.Fail("�û�������"); } if (user.Status == 0) { _logger.LogWarning("User {UserId} is disabled", user.Id); - return RefreshTokenResult.Fail("˺ѱ"); + return RefreshTokenResult.Fail("�˺��ѱ�����"); } - // Token ֻµ Refresh Token + // Token �ֻ��������µ� Refresh Token var newRefreshToken = GenerateSecureRandomString(RefreshTokenLength); var newTokenHash = ComputeSha256Hash(newRefreshToken); - // Token ¼ϵ + // ������ Token ����¼������ϵ storedToken.RevokedAt = DateTime.Now; storedToken.RevokedByIp = ipAddress; storedToken.ReplacedByToken = newTokenHash; - // µ Token ¼ + // �����µ� Token ��¼ var newUserRefreshToken = new UserRefreshToken { UserId = user.Id, @@ -659,7 +663,7 @@ public class AuthService : IAuthService await _dbContext.UserRefreshTokens.AddAsync(newUserRefreshToken); await _dbContext.SaveChangesAsync(); - // µ Access Token + // �����µ� Access Token var accessToken = _jwtService.GenerateToken(user); var expiresIn = _jwtSettings.ExpirationMinutes * 60; @@ -676,12 +680,12 @@ public class AuthService : IAuthService catch (Exception ex) { _logger.LogError(ex, "Error refreshing token"); - return RefreshTokenResult.Fail("ˢʧܣԺ"); + return RefreshTokenResult.Fail("ˢ������ʧ�ܣ����Ժ�����"); } } /// - /// Token + /// ���� Token /// Requirements: 4.4 /// public async Task RevokeTokenAsync(string refreshToken, string? ipAddress) @@ -694,10 +698,10 @@ public class AuthService : IAuthService try { - // Token ϣֵ + // ���� Token ��ϣֵ var tokenHash = ComputeSha256Hash(refreshToken); - // Token ¼ + // ���� Token ��¼ var storedToken = await _dbContext.UserRefreshTokens .FirstOrDefaultAsync(t => t.TokenHash == tokenHash); @@ -707,14 +711,14 @@ public class AuthService : IAuthService return; } - // Ѿֱӷ + // ����Ѿ�������ֱ�ӷ��� if (storedToken.IsRevoked) { _logger.LogInformation("Refresh token already revoked"); return; } - // Token + // ���� Token storedToken.RevokedAt = DateTime.Now; storedToken.RevokedByIp = ipAddress; @@ -730,14 +734,14 @@ public class AuthService : IAuthService } /// - /// û Token + /// �����û������� Token /// Requirements: 4.4 /// public async Task RevokeAllUserTokensAsync(long userId, string? ipAddress) { try { - // ûЧ Token + // �����û�������Ч�� Token var activeTokens = await _dbContext.UserRefreshTokens .Where(t => t.UserId == userId && t.RevokedAt == null) .ToListAsync(); @@ -771,7 +775,7 @@ public class AuthService : IAuthService #region Private Helper Methods /// - /// ϲ˻ - ǰûopenidǨƵֻû + /// �ϲ��˻� - ����ǰ�û���openidǨ�Ƶ��ֻ����û� /// private async Task MergeAccountsAsync(User currentUser, User mobileUser) { @@ -781,7 +785,7 @@ public class AuthService : IAuthService _logger.LogInformation("Merging accounts: CurrentUserId={CurrentUserId}, MobileUserId={MobileUserId}", currentUser.Id, mobileUser.Id); - // 5.2 ǰûopenidǨƵֻû + // 5.2 ����ǰ�û���openidǨ�Ƶ��ֻ����û� if (!string.IsNullOrWhiteSpace(currentUser.OpenId)) { mobileUser.OpenId = currentUser.OpenId; @@ -793,13 +797,13 @@ public class AuthService : IAuthService mobileUser.UpdateTime = DateTime.Now; _dbContext.Users.Update(mobileUser); - // ɾǰû + // ɾ����ǰ�û� _dbContext.Users.Remove(currentUser); await _dbContext.SaveChangesAsync(); await transaction.CommitAsync(); - // 5.3 µtoken + // 5.3 �����µ�token var newToken = _jwtService.GenerateToken(mobileUser); _logger.LogInformation("Accounts merged successfully: NewUserId={NewUserId}", mobileUser.Id); @@ -816,18 +820,70 @@ public class AuthService : IAuthService } /// - /// ĬͷURL + /// 获取用户默认昵称(从配置读取前缀) + /// + private async Task GetDefaultNicknameAsync() + { + try + { + var configJson = await _configService.GetConfigValueAsync("user_config"); + if (!string.IsNullOrEmpty(configJson)) + { + var config = JsonSerializer.Deserialize(configJson); + if (config.TryGetProperty("default_nickname_prefix", out var prefixEl) && + prefixEl.ValueKind == JsonValueKind.String && + !string.IsNullOrWhiteSpace(prefixEl.GetString())) + { + return $"{prefixEl.GetString()}{Random.Shared.Next(100000, 999999)}"; + } + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "读取用户默认昵称配置失败,使用默认值"); + } + return $"用户{Random.Shared.Next(100000, 999999)}"; + } + + /// + /// 获取用户默认头像(从配置读取,为空则自动生成) + /// + private async Task GetDefaultAvatarAsync(string seed) + { + try + { + var configJson = await _configService.GetConfigValueAsync("user_config"); + if (!string.IsNullOrEmpty(configJson)) + { + var config = JsonSerializer.Deserialize(configJson); + if (config.TryGetProperty("default_avatar", out var avatarEl) && + avatarEl.ValueKind == JsonValueKind.String && + !string.IsNullOrWhiteSpace(avatarEl.GetString())) + { + return avatarEl.GetString()!; + } + } + } + catch (Exception ex) + { + _logger.LogWarning(ex, "读取用户默认头像配置失败,使用系统生成"); + } + return GenerateDefaultAvatar(seed); + } + + /// + /// ����Ĭ��ͷ��URL /// private static string GenerateDefaultAvatar(string seed) { - // ʹһĬͷURL - // ʵĿпʹIdenticonͷɷ + // ʹ����������һ����Ĭ��ͷ��URL + // ʵ����Ŀ�п���ʹ��Identicon�������ͷ�����ɷ��� var hash = ComputeMd5(seed); return $"https://api.dicebear.com/7.x/identicon/svg?seed={hash}"; } /// - /// MD5ϣ + /// ����MD5��ϣ /// private static string ComputeMd5(string input) { @@ -837,7 +893,7 @@ public class AuthService : IAuthService } /// - /// ַ + /// ��������ַ��� /// private static string GenerateRandomString(int length) { @@ -851,7 +907,7 @@ public class AuthService : IAuthService } /// - /// ֻ + /// �����ֻ��� /// private static string MaskMobile(string mobile) { @@ -862,7 +918,7 @@ public class AuthService : IAuthService } /// - /// ȡе + /// ��ȡ����е����� /// private static int GetWeekOfYear(DateTime date) { @@ -871,7 +927,7 @@ public class AuthService : IAuthService } /// - /// SHA256 ϣֵ + /// ���� SHA256 ��ϣֵ /// Requirements: 4.1 /// private static string ComputeSha256Hash(string input) @@ -882,7 +938,7 @@ public class AuthService : IAuthService } /// - /// ɰȫַ Refresh Token + /// ���ɰ�ȫ������ַ��������� Refresh Token�� /// private static string GenerateSecureRandomString(int length) {