Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Service/WeChatAuthService.cs
glz 32989e1fa0 refactor: 重构用户与商品模块,统一业务模型与服务
1.  新增用户状态枚举UserStatusEnum,统一用户状态定义
2.  重构用户体系:合并WxUser与User实体为Users实体,统一用户管理
3.  新增微信小程序认证相关服务与控制器,实现一键登录功能
4.  新增商品管理完整服务与控制器,修复商品表字段映射问题
5.  删除冗余的WxUser相关服务与控制器代码
6.  新增Newtonsoft.Json依赖用于微信接口响应解析
7.  清理无用的文件夹引用配置
2026-06-03 16:09:56 +08:00

161 lines
6.2 KiB
C#
Raw 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 Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using QYZH.InteractiveMagazine.Common.Helpers;
using QYZH.InteractiveMagazine.Infrastructure.Auth;
using QYZH.InteractiveMagazine.Infrastructure.Cache;
using QYZH.InteractiveMagazine.IService;
using QYZH.InteractiveMagazine.IService.Dto;
using QYZH.InteractiveMagazine.Models.Common;
using QYZH.InteractiveMagazine.Models.Entity;
using QYZH.InteractiveMagazine.Models.Settings;
using QYZH.InteractiveMagazine.Repository;
namespace QYZH.InteractiveMagazine.Service;
/// <summary>
/// 微信小程序认证服务实现
/// </summary>
public class WeChatAuthService(BaseRepository<Users> usersRepository, IConfiguration configuration, ILogger<WeChatAuthService> logger) : BaseRepository<Users>, IWeChatAuthService
{
private const string TokenKeyPrefix = "InteractiveMagazine:WeChatAuth:Token";
private const string Code2SessionUrl = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
/// <summary>
/// 微信小程序一键登录
/// </summary>
/// <param name="input">登录输入(含微信 code</param>
/// <returns>登录结果(含 Token 和用户信息)</returns>
public async Task<WeChatLoginOutput> LoginAsync(WeChatLoginInput input)
{
logger.LogInformation("微信小程序登录尝试");
// 参数校验
if (string.IsNullOrWhiteSpace(input.Code))
{
throw new BusinessException("微信登录凭证 code 不能为空", 400);
}
// 获取微信配置
var weChatSettings = GetWeChatSettings();
// 调用微信 code2session 接口
var wxResponse = await CallCode2SessionAsync(weChatSettings, input.Code);
if (wxResponse == null || wxResponse.ErrCode != 0 || string.IsNullOrWhiteSpace(wxResponse.OpenId))
{
var errMsg = wxResponse?.ErrMsg ?? "未知错误";
logger.LogWarning("微信 code2session 接口调用失败errcode: {ErrCode}, errmsg: {ErrMsg}", wxResponse?.ErrCode, errMsg);
throw new BusinessException($"微信登录失败:{errMsg}", 400);
}
logger.LogInformation("微信 code2session 成功OpenId: {OpenId}", wxResponse.OpenId);
// 查询用户是否已存在
var user = await usersRepository.GetFirstAsync(u => u.OpenId == wxResponse.OpenId);
var isNewUser = user == null;
if (isNewUser)
{
// 首次登录,创建新用户
user = new Users
{
Name = $"wx_{wxResponse.OpenId[^8..]}",
OpenId = wxResponse.OpenId,
UnionId = wxResponse.UnionId,
Type = "Normal",
Status = "Active",
GrowthPoints = 0,
Points = 0
};
var insertResult = await usersRepository.Insertable(user).ExecuteReturnIdentityAsync();
if (insertResult <= 0)
{
logger.LogError("创建微信用户失败OpenId: {OpenId}", wxResponse.OpenId);
throw new BusinessException("创建用户失败,请稍后重试", 500);
}
user.Id = insertResult;
logger.LogInformation("微信新用户创建成功UserId: {UserId}, OpenId: {OpenId}", user.Id, wxResponse.OpenId);
}
else
{
// 已有用户,校验状态
if (user.Status == "Disabled")
{
logger.LogWarning("微信登录失败用户已被禁用UserId: {UserId}, OpenId: {OpenId}", user.Id, wxResponse.OpenId);
throw new BusinessException("账号已被禁用,请联系客服", 403);
}
logger.LogInformation("微信老用户登录UserId: {UserId}, OpenId: {OpenId}", user.Id, wxResponse.OpenId);
}
// 生成 JWT Token
var jwtSettings = GetJwtSettings();
var token = JwtHelper.GenerateToken((long)user.Id, user.Name, jwtSettings);
// 缓存 Token 到 Redis
await RedisHelper.StringSetAsync($"{TokenKeyPrefix}:{user.Id}", token, TimeSpan.FromMinutes(jwtSettings.ExpiryMinutes));
return new WeChatLoginOutput
{
Token = token,
UserId = (long)user.Id,
UserName = user.Name,
OpenId = wxResponse.OpenId
};
}
/// <summary>
/// 调用微信 code2session 接口
/// </summary>
private async Task<WxCode2SessionResponse?> CallCode2SessionAsync(WeChatSettings settings, string code)
{
var url = string.Format(Code2SessionUrl, settings.AppId, settings.AppSecret, code);
try
{
return await HttpHelper.GetAsync<WxCode2SessionResponse>(url);
}
catch (Exception ex)
{
logger.LogError(ex, "调用微信 code2session 接口异常URL: {Url}", url);
throw new BusinessException("微信服务请求失败,请稍后重试", 500);
}
}
/// <summary>
/// 获取微信配置
/// </summary>
private WeChatSettings GetWeChatSettings()
{
var settings = configuration.GetSection("WeChatSettings").Get<WeChatSettings>();
if (settings == null || string.IsNullOrWhiteSpace(settings.AppId) || string.IsNullOrWhiteSpace(settings.AppSecret))
{
logger.LogError("微信配置不完整,请检查 appsettings.json 中的 WeChatSettings 节点");
throw new BusinessException("微信配置不完整,请联系系统管理员", 500);
}
return settings;
}
/// <summary>
/// 获取 JWT 配置
/// </summary>
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 配置不完整", 500);
}
return jwtSettings;
}
}