using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;
using XiangYi.Application.Interfaces;
using XiangYi.Core.Entities.Biz;
using XiangYi.Core.Interfaces;
namespace XiangYi.AppApi.Controllers;
///
/// 微信服务号事件推送控制器
///
[ApiController]
[Route("api/wechat/event")]
[AllowAnonymous]
public class WeChatEventController : ControllerBase
{
private readonly IRepository _userRepository;
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
private readonly ISystemConfigService _configService;
public WeChatEventController(
IRepository userRepository,
ILogger logger,
IConfiguration configuration,
ISystemConfigService configService)
{
_userRepository = userRepository;
_logger = logger;
_configuration = configuration;
_configService = configService;
}
///
/// 微信服务器验证(GET请求)
///
[HttpGet]
public async Task Verify(string signature, string timestamp, string nonce, string echostr)
{
_logger.LogInformation("收到微信服务器验证请求: signature={Signature}, timestamp={Timestamp}, nonce={Nonce}",
signature, timestamp, nonce);
if (await CheckSignatureAsync(signature, timestamp, nonce))
{
_logger.LogInformation("微信服务器验证成功");
return Content(echostr);
}
_logger.LogWarning("微信服务器验证失败");
return BadRequest("验证失败");
}
///
/// 接收微信事件推送(POST请求)
///
[HttpPost]
public async Task ReceiveEvent()
{
try
{
using var reader = new StreamReader(Request.Body, Encoding.UTF8);
var body = await reader.ReadToEndAsync();
_logger.LogInformation("收到微信事件推送: {Body}", body);
var xml = XDocument.Parse(body);
var root = xml.Root;
var msgType = root?.Element("MsgType")?.Value;
var eventType = root?.Element("Event")?.Value;
var openId = root?.Element("FromUserName")?.Value;
if (msgType == "event")
{
switch (eventType?.ToLower())
{
case "subscribe":
await HandleSubscribeEvent(openId, root);
break;
case "unsubscribe":
await HandleUnsubscribeEvent(openId);
break;
}
}
// 返回success表示已处理
return Content("success");
}
catch (Exception ex)
{
_logger.LogError(ex, "处理微信事件推送异常");
return Content("success");
}
}
///
/// 处理关注事件
///
private async Task HandleSubscribeEvent(string? serviceAccountOpenId, XElement? root)
{
if (string.IsNullOrEmpty(serviceAccountOpenId))
{
_logger.LogWarning("关注事件缺少OpenId");
return;
}
_logger.LogInformation("用户关注服务号: ServiceAccountOpenId={OpenId}", serviceAccountOpenId);
// 尝试通过UnionId关联用户
// 注意:需要服务号和小程序绑定到同一个开放平台才能获取UnionId
var unionId = root?.Element("UnionId")?.Value;
if (!string.IsNullOrEmpty(unionId))
{
// 通过UnionId查找用户
var users = await _userRepository.GetListAsync(u => u.UnionId == unionId);
var user = users.FirstOrDefault();
if (user != null)
{
user.ServiceAccountOpenId = serviceAccountOpenId;
user.IsFollowServiceAccount = true;
user.UpdateTime = DateTime.Now;
await _userRepository.UpdateAsync(user);
_logger.LogInformation("用户关注服务号并关联成功: UserId={UserId}, UnionId={UnionId}, ServiceAccountOpenId={OpenId}",
user.Id, unionId, serviceAccountOpenId);
}
else
{
_logger.LogInformation("用户关注服务号但未找到关联用户: UnionId={UnionId}", unionId);
}
}
else
{
_logger.LogInformation("用户关注服务号但无UnionId: ServiceAccountOpenId={OpenId}", serviceAccountOpenId);
}
}
///
/// 处理取消关注事件
///
private async Task HandleUnsubscribeEvent(string? serviceAccountOpenId)
{
if (string.IsNullOrEmpty(serviceAccountOpenId))
{
_logger.LogWarning("取消关注事件缺少OpenId");
return;
}
_logger.LogInformation("用户取消关注服务号: ServiceAccountOpenId={OpenId}", serviceAccountOpenId);
// 查找并更新用户状态
var users = await _userRepository.GetListAsync(u => u.ServiceAccountOpenId == serviceAccountOpenId);
var user = users.FirstOrDefault();
if (user != null)
{
user.IsFollowServiceAccount = false;
user.UpdateTime = DateTime.Now;
await _userRepository.UpdateAsync(user);
_logger.LogInformation("用户取消关注服务号: UserId={UserId}", user.Id);
}
}
///
/// 验证微信签名
///
private async Task CheckSignatureAsync(string signature, string timestamp, string nonce)
{
var token = await _configService.GetConfigValueAsync("sa_token");
if (string.IsNullOrEmpty(token))
{
_logger.LogWarning("服务号Token未配置");
return false;
}
var arr = new[] { token, timestamp, nonce };
Array.Sort(arr);
var str = string.Join("", arr);
using var sha1 = SHA1.Create();
var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(str));
var hashStr = BitConverter.ToString(hash).Replace("-", "").ToLower();
return hashStr == signature?.ToLower();
}
}