feat: 完成多模块功能迭代与优化

1.  新增宠物皮肤初始枚举值、下拉列表接口及相关DTO
2.  完善商品实体与DTO,新增虚拟商品标记和类型ID字段
3.  重构用户认证体系,支持多用户切换并重新生成JWT令牌
4.  新增签到配置管理全套功能,包括增删改查和状态管理
5.  优化模型验证过滤器和基础响应类的命名规范
6.  新增补签功能,完善签到服务逻辑
7.  拆分用户详情DTO,新增各子数据分页查询接口
8.  重构微信控制器的用户ID获取逻辑,统一使用激活用户ID
9.  修复背包服务中补签卡的扣减逻辑
10. 新增家长姓名修改接口和相关服务实现
This commit is contained in:
glz
2026-06-10 17:53:38 +08:00
parent 028e3dfb34
commit ae4cd627df
35 changed files with 1334 additions and 316 deletions

View File

@ -152,15 +152,16 @@ public class WeChatAuthService(
}
/// <summary>
/// 切换用户(同一 WxUser 下切换 User 身份,JWT 基于 WxUser 无需重新生成)
/// 切换用户(同一 WxUser 下切换 User 身份,重新生成 Token
/// </summary>
public async Task<WeChatSwitchUserOutput> SwitchUserAsync(long wxUserId, WeChatSwitchUserInput input)
public async Task<WeChatSwitchUserOutput> SwitchUserAsync(long wxUserId, long currentUserId, WeChatSwitchUserInput input)
{
logger.LogInformation("切换用户WxUserId: {WxUserId}, 目标 UserId: {TargetUserId}", wxUserId, input.UserId);
logger.LogInformation("切换用户WxUserId: {WxUserId}, 当前 UserId: {CurrentUserId}, 目标 UserId: {TargetUserId}",
wxUserId, currentUserId, input.UserId);
// 查询目标用户
var targetUser = await wxUserRepository.Context.Queryable<Users>()
.Where(u => u.Id == input.UserId)
.Where(u => u.Id == input.UserId && !u.IsDeleted)
.FirstAsync();
if (targetUser == null)
@ -179,23 +180,32 @@ public class WeChatAuthService(
// 更新 IsLastOnline清除所有设置目标为 true
await wxUserRepository.Context.Updateable<Users>()
.SetColumns(u => u.IsLastOnline == false)
.Where(u => u.WxUserId == wxUserId )
.Where(u => u.WxUserId == wxUserId && !u.IsDeleted)
.ExecuteCommandAsync();
await wxUserRepository.Context.Updateable<Users>()
.SetColumns(u => u.IsLastOnline == true)
.Where(u => u.Id == input.UserId )
.Where(u => u.Id == input.UserId && !u.IsDeleted)
.ExecuteCommandAsync();
logger.LogInformation("IsLastOnline 已更新,目标用户 {UserId} 设为 true", input.UserId);
// 重新查询目标用户获取最新数据
var refreshedUser = await wxUserRepository.Context.Queryable<Users>()
.Where(u => u.Id == input.UserId)
.Where(u => u.Id == input.UserId && !u.IsDeleted)
.FirstAsync();
// 生成新 JWT TokenWxUserId + 新 UserId
var jwtSettings = GetJwtSettings();
var token = JwtHelper.GenerateToken(wxUserId, refreshedUser.Id, refreshedUser.Name ?? string.Empty, jwtSettings);
// 清除旧 Redis Token写入新 Token
await RedisHelper.DelAsync($"{TokenKeyPrefix}:{currentUserId}");
await RedisHelper.SetAsync($"{TokenKeyPrefix}:{refreshedUser.Id}", token, TimeSpan.FromMinutes(jwtSettings.ExpiryMinutes));
return new WeChatSwitchUserOutput
{
Token = token,
User = MapUserToOutput(refreshedUser)
};
}
@ -263,21 +273,66 @@ public class WeChatAuthService(
return MapUserToOutput(newUser);
}
/// <summary>
/// 修改家长名字WxUser.Name
/// </summary>
public async Task<WxUserInfoOutput> UpdateWxUserNameAsync(long wxUserId, UpdateWxUserNameInput input)
{
logger.LogInformation("修改家长名字WxUserId: {WxUserId}, NewName: {Name}", wxUserId, input.Name);
if (string.IsNullOrWhiteSpace(input.Name))
throw new BusinessException("名字不能为空", 400);
var wxUser = await wxUserRepository.Context.Queryable<WxUser>()
.Where(w => w.Id == wxUserId && !w.IsDeleted)
.FirstAsync();
if (wxUser == null)
throw new BusinessException("微信用户不存在", 404);
await wxUserRepository.Context.Updateable<WxUser>()
.SetColumns(w => w.Name == input.Name.Trim())
.SetColumns(w => w.UpdatedAt == DateTime.Now)
.SetColumns(w => w.UpdatedBy == wxUserId.ToString())
.Where(w => w.Id == wxUserId)
.ExecuteCommandAsync();
wxUser.Name = input.Name.Trim();
logger.LogInformation("家长名字修改成功WxUserId: {WxUserId}, NewName: {Name}", wxUserId, input.Name);
return new WxUserInfoOutput
{
Id = wxUser.Id,
OpenId = wxUser.OpenId,
UnionId = wxUser.UnionId,
Name = wxUser.Name,
AvatarUrl = wxUser.AvatarUrl,
Phone = wxUser.Phone
};
}
#region
/// <summary>
/// 构建登录输出JWT 基于 WxUser,不绑定具体 User
/// 构建登录输出JWT 同时携带 WxUserId 和当前激活 UserId
/// </summary>
private async Task<WeChatLoginOutput> BuildLoginOutputAsync(WxUser wxUser, List<Users> users)
{
var jwtSettings = GetJwtSettings();
var token = JwtHelper.GenerateToken(wxUser.Id, wxUser.Name ?? wxUser.OpenId, jwtSettings);
var activeUser = users.FirstOrDefault(u => u.IsLastOnline) ?? users.FirstOrDefault();
await RedisHelper.SetAsync($"{TokenKeyPrefix}:{wxUser.Id}", token, TimeSpan.FromMinutes(jwtSettings.ExpiryMinutes));
var userId = activeUser?.Id ?? 0L;
var userName = activeUser?.Name ?? wxUser.Name ?? wxUser.OpenId;
var jwtSettings = GetJwtSettings();
var token = JwtHelper.GenerateToken(wxUser.Id, userId, userName, jwtSettings);
await RedisHelper.SetAsync($"{TokenKeyPrefix}:{userId}", token, TimeSpan.FromMinutes(jwtSettings.ExpiryMinutes));
return new WeChatLoginOutput
{
Token = token,
CurrentUserId = userId,
WxUser = new WxUserInfoOutput
{
Id = wxUser.Id,