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(); } }