Files
glz a04b4be7e5 refactor: 拆分微信API到独立项目并迁移相关代码
1.  新增WeChatApi独立项目,将原WebApi中的微信相关控制器迁移至新项目
2.  新增后台权限管理相关实体、服务接口和基础控制器
3.  完善管理员用户服务,增加角色关联查询和赋值逻辑
4.  修复WebApi Swagger文档过滤微信API版本的问题
5.  更新解决方案文件,添加新的微信API项目引用
6.  新增微信API基础配置文件和项目属性配置
2026-07-06 14:21:21 +08:00

180 lines
6.7 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using BCrypt.Net;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.Infrastructure.Auth;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Dto;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Settings;
using QYZH.InteractiveMagazine.Repository;
namespace QYZH.InteractiveMagazine.Service;
public class AdminAuthService(
BaseRepository<AdminUser> adminUserRepository,
IAdminPermissionService adminPermissionService,
IConfiguration configuration,
ILogger<AdminAuthService> logger) : BaseRepository<AdminUser>, IAdminAuthService
{
private const string TokenKeyPrefix = "InteractiveMagazine:AdminAuth:Token";
private const string UserInfoKeyPrefix = "InteractiveMagazine:AdminAuth:UserInfo";
public async Task<AdminLoginOutput> LoginAsync(AdminLoginInput input)
{
logger.LogInformation("管理员登录尝试,用户名: {UserName}", input.UserName);
if (string.IsNullOrWhiteSpace(input.UserName))
{
throw new BusinessException("用户名不能为空", ResultCode.BAD_REQUEST);
}
if (string.IsNullOrWhiteSpace(input.Password))
{
throw new BusinessException("密码不能为空", ResultCode.BAD_REQUEST);
}
var adminUser = await adminUserRepository.GetFirstAsync(a => a.UserName == input.UserName);
if (adminUser == null)
{
logger.LogWarning("管理员登录失败,用户名不存在: {UserName}", input.UserName);
throw new BusinessException("用户名或密码错误", ResultCode.DENY);
}
if (!BCrypt.Net.BCrypt.Verify(input.Password, adminUser.PasswordHash))
{
logger.LogWarning("管理员登录失败,密码错误: {UserName}", input.UserName);
throw new BusinessException("用户名或密码错误", ResultCode.DENY);
}
if (adminUser.Status != 1)
{
logger.LogWarning("管理员登录失败,账号已禁用: {UserName}", input.UserName);
throw new BusinessException("账号已被禁用,请联系系统管理员", ResultCode.FORBIDDEN);
}
var jwtSettings = GetJwtSettings();
var token = JwtHelper.GenerateToken((long)adminUser.Id, adminUser.UserName, jwtSettings);
await RedisHelper.SetAsync($"{TokenKeyPrefix}:{adminUser.Id}", token, TimeSpan.FromMinutes(jwtSettings.ExpiryMinutes));
logger.LogInformation("管理员登录成功,用户名: {UserName}, ID: {UserId}", input.UserName, adminUser.Id);
var roles = await adminPermissionService.GetAdminUserRolesAsync(adminUser.Id);
var menus = await adminPermissionService.GetAdminUserMenuTreeAsync(adminUser.Id);
var permissionCodes = await adminPermissionService.GetAdminUserPermissionCodesAsync(adminUser.Id);
return new AdminLoginOutput
{
Token = token,
UserId = (long)adminUser.Id,
UserName = adminUser.UserName,
Type = adminUser.Type.ToString(),
RoleIds = roles.Select(x => x.Id).ToList(),
Roles = roles,
Menus = menus,
PermissionCodes = permissionCodes
};
}
public async Task LogoutAsync(long userId)
{
logger.LogInformation("管理员登出ID: {UserId}", userId);
await RedisHelper.DelAsync($"{TokenKeyPrefix}:{userId}");
logger.LogInformation("管理员登出成功ID: {UserId}", userId);
}
public async Task<AdminUserInfoOutput> GetAdminInfoAsync(long userId)
{
logger.LogInformation("获取管理员信息ID: {UserId}", userId);
var adminUser = await adminUserRepository.GetByIdAsync(userId);
if (adminUser == null)
{
logger.LogWarning("未找到管理员ID: {UserId}", userId);
throw new BusinessException("用户不存在", ResultCode.NOT_FOUND);
}
var roles = await adminPermissionService.GetAdminUserRolesAsync(adminUser.Id);
var menus = await adminPermissionService.GetAdminUserMenuTreeAsync(adminUser.Id);
var permissionCodes = await adminPermissionService.GetAdminUserPermissionCodesAsync(adminUser.Id);
return new AdminUserInfoOutput
{
UserId = adminUser.Id,
UserName = adminUser.UserName,
Type = adminUser.Type.ToString(),
Status = adminUser.Status,
RoleIds = roles.Select(x => x.Id).ToList(),
Roles = roles,
Menus = menus,
PermissionCodes = permissionCodes
};
}
public async Task ChangePasswordAsync(long userId, string oldPassword, string newPassword)
{
logger.LogInformation("管理员修改密码尝试ID: {UserId}", userId);
if (string.IsNullOrWhiteSpace(oldPassword))
{
throw new BusinessException("原密码不能为空", ResultCode.BAD_REQUEST);
}
if (string.IsNullOrWhiteSpace(newPassword))
{
throw new BusinessException("新密码不能为空", ResultCode.BAD_REQUEST);
}
var adminUser = await adminUserRepository.GetByIdAsync(userId);
if (adminUser == null)
{
logger.LogWarning("未找到管理员ID: {UserId}", userId);
throw new BusinessException("用户不存在", ResultCode.NOT_FOUND);
}
if (!BCrypt.Net.BCrypt.Verify(oldPassword, adminUser.PasswordHash))
{
logger.LogWarning("管理员修改密码失败原密码错误ID: {UserId}", userId);
throw new BusinessException("原密码错误", ResultCode.DENY);
}
adminUser.PasswordHash = BCrypt.Net.BCrypt.HashPassword(newPassword);
var result = await adminUserRepository.UpdateAsync(adminUser);
if (!result)
{
throw new BusinessException("修改密码失败", ResultCode.GLOBAL_ERROR);
}
await RedisHelper.DelAsync($"{TokenKeyPrefix}:{userId}");
logger.LogInformation("管理员修改密码成功ID: {UserId}", userId);
}
private JwtSettings GetJwtSettings()
{
var jwtSettings = configuration.GetSection("JwtSettings").Get<JwtSettings>()
?? new JwtSettings
{
Issuer = "QYZH.InteractiveMagazine",
Audience = "QYZH.InteractiveMagazine",
SecretKey = "your-256-bit-secret-key-here-change-in-production",
ExpiryMinutes = 120
};
if (string.IsNullOrWhiteSpace(jwtSettings.SecretKey))
{
throw new BusinessException("JWT 配置不完整", ResultCode.GLOBAL_ERROR);
}
return jwtSettings;
}
}