feat(wechat): Integrate service account user info API for UnionId retrieval
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
- Add IWeChatService dependency injection to WeChatEventController - Implement GetServiceAccountUserInfoAsync method to fetch user info including UnionId from WeChat API - Add ServiceAccountUserInfo DTO class with Subscribe, OpenId, and UnionId properties - Refactor follow event handler to use WeChat API for UnionId instead of XML parsing - Add validation to ensure UnionId is retrieved before attempting user association - Update notification URLs in appsettings.json to remove redundant path segment - Improve error logging when UnionId retrieval fails or user association is not found
This commit is contained in:
parent
4af5ae8065
commit
3c53cddd0b
|
|
@ -6,6 +6,7 @@ using System.Xml.Linq;
|
|||
using XiangYi.Application.Interfaces;
|
||||
using XiangYi.Core.Entities.Biz;
|
||||
using XiangYi.Core.Interfaces;
|
||||
using XiangYi.Infrastructure.WeChat;
|
||||
|
||||
namespace XiangYi.AppApi.Controllers;
|
||||
|
||||
|
|
@ -21,17 +22,20 @@ public class WeChatEventController : ControllerBase
|
|||
private readonly ILogger<WeChatEventController> _logger;
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ISystemConfigService _configService;
|
||||
private readonly IWeChatService _weChatService;
|
||||
|
||||
public WeChatEventController(
|
||||
IRepository<User> userRepository,
|
||||
ILogger<WeChatEventController> logger,
|
||||
IConfiguration configuration,
|
||||
ISystemConfigService configService)
|
||||
ISystemConfigService configService,
|
||||
IWeChatService weChatService)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_logger = logger;
|
||||
_configuration = configuration;
|
||||
_configService = configService;
|
||||
_weChatService = weChatService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -109,34 +113,33 @@ public class WeChatEventController : ControllerBase
|
|||
|
||||
_logger.LogInformation("用户关注服务号: ServiceAccountOpenId={OpenId}", serviceAccountOpenId);
|
||||
|
||||
// 尝试通过UnionId关联用户
|
||||
// 注意:需要服务号和小程序绑定到同一个开放平台才能获取UnionId
|
||||
var unionId = root?.Element("UnionId")?.Value;
|
||||
// 通过服务号接口获取用户信息(包含UnionId)
|
||||
var userInfo = await _weChatService.GetServiceAccountUserInfoAsync(serviceAccountOpenId);
|
||||
var unionId = userInfo?.UnionId;
|
||||
|
||||
if (!string.IsNullOrEmpty(unionId))
|
||||
if (string.IsNullOrEmpty(unionId))
|
||||
{
|
||||
// 通过UnionId查找用户
|
||||
var users = await _userRepository.GetListAsync(u => u.UnionId == unionId);
|
||||
var user = users.FirstOrDefault();
|
||||
_logger.LogWarning("用户关注服务号但获取UnionId失败: ServiceAccountOpenId={OpenId},请确认服务号已绑定微信开放平台", serviceAccountOpenId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
user.ServiceAccountOpenId = serviceAccountOpenId;
|
||||
user.IsFollowServiceAccount = true;
|
||||
user.UpdateTime = DateTime.Now;
|
||||
await _userRepository.UpdateAsync(user);
|
||||
// 通过UnionId查找小程序用户
|
||||
var users = await _userRepository.GetListAsync(u => u.UnionId == unionId);
|
||||
var user = users.FirstOrDefault();
|
||||
|
||||
_logger.LogInformation("用户关注服务号并关联成功: UserId={UserId}, UnionId={UnionId}, ServiceAccountOpenId={OpenId}",
|
||||
user.Id, unionId, serviceAccountOpenId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("用户关注服务号但未找到关联用户: UnionId={UnionId}", unionId);
|
||||
}
|
||||
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: ServiceAccountOpenId={OpenId}", serviceAccountOpenId);
|
||||
_logger.LogInformation("用户关注服务号但未找到关联小程序用户: UnionId={UnionId}", unionId);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@
|
|||
"CertSerialNo": "429F8544BF89D61B1A98643277A8DC7E5C4B1DAA",
|
||||
"PrivateKey": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDoFVHlfDo3flrT\nXLEbAcoQvh3bG/TJq38sjim0Frk740zkRQfkYMIUqtkPZBLl47MSPwaOKjxIyYJB\nr4CIW10rg7pA67pnpnRgYoPGF/DMT5Mle+pxWt94xGT8ircwx7WwTBHDthJiGhQN\nwTiy7dSH8Nbk14fcK6AN794dNRVwILon0O8z2osuoQngjVDLXB0BVYqZjV7/js99\nyXRZnZVuxiuvnbHzNxxe86qZgXjgFKpiey5sinx+CRjTyDD7CCVOVKwr7h7cKqVC\nbbnN20DWzLundRkBJFh3dbUcX4Pp3gV9m6B0UURARjpLkym6j3fRDLGJHWREeQ7N\no6qsaUdtAgMBAAECggEBAMuOD4OQ/tq3Z2Ak122RlzIyHauVDJFpaqSgl/FNUPA2\n/7Ti2vYy62cHJlR6eJzLpr8lKlG8t507qJSGItz2DXTiF5Vja94HP+Fd5qfzTY9V\naAEje1Aq3QBmeRCLdftB3pifT6FxaxRCPT6HL3y4XoVQ9ppGc/HnDX3L2euSKJhr\nYCZa+kB5L4FtM0JDGTnx+Q9fuCuKtCcT3YHaryildwiz4WiQxp1kvXj9bK+kNbBO\nPi79Kui8mRY3KDYaccxBgmqR9JkJ2/l52kKlJb5HWoRS3jh2/MulNj7gpWVy6KNb\ni6OMWs548EJRw9jrZu1cGmlThrguX9XaGWFvFfcRx0ECgYEA/ENIW7mm8cVnTCsu\n30qzQlJ7plljTIaan+TmVead8KK3fiRjUg4jK1Zh0JYFRaA4lC0M2mitsNKw0zHk\nKM3sh4ZIbzzh6CCOkUd0L7+3p4v9U3Bm6sjTS5WLy/MusPQEd4W86Gmn8LWcPx5q\n9Tz2hpbAyDals9AZEjgWu/rgWrECgYEA64WBaE+EEDkAtqkmpn8OLdlN5K51ncLe\n9Q4nv+NP9AtlwCmd3SBKv6qAEviS/hM4m+tjNlvS/UwP+6SPXTcnaBaCrpYdgv7e\nkVSZHboxHdRnYqReg6WhnOErol2GLqe/9+gc70x97T/KCIZ+/nE2MnQy2uFZVm12\nkxMvj1g+r30CgYAPb2Z8Bk4KuRNq+7FwhDeXtUhPk2SaCBpp8i2N0ACV+r7TfxJ8\nsNTCEBUIGEXWTslnd6Izsvf9u8aKBaF6Ra9VU4gXFliURXmztfWL/mUUYWJsupHx\nh7w2Ab5+CjEvLp8fWRWH+v8FoXcf/ZJ50vMapRrCpWVaLT97d+ccNWuI4QKBgQDO\nR4goDDzm2IY/dbdcbDvG/GS0vfhVzK/qghNehYEphjIANHMHkZjmdjbmZsCXt84F\nAg1LNvF82HnHNUI7qmrhR5X9w4zlhsT5FNdmqgUK01YZl00QkKkT9kN5WeCETHhe\ncPWmwaApg404GlRwFkgZuJwyCN1uTUFlX5BwRCHjIQKBgHTXcrlGfW5U2piJGdBs\nbi+I3nYPioyyHM9jUmdBtEtR04pXVV2590KZL2TknPB1dN2yhv9FUt4XO5+baoie\nas6QkQGrtOtVnO2X/oVOZQBmPG3RGZAMcWgYXJeLCxlf+DZ0OZNn0/V3od39WN7t\n84/yPSRGUr71Q48atr9N9N9x\n-----END PRIVATE KEY-----",
|
||||
"ApiV3Key": "1230uaPcnzdh3lkxjcoiddUBXddWkpx2",
|
||||
"NotifyUrl": "https://app.zpc-xy.com/xyqj/api/order/payNotify"
|
||||
"NotifyUrl": "https://app.zpc-xy.com/api/order/payNotify"
|
||||
}
|
||||
},
|
||||
"WeChatPay": {
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
"CertPath": "apiclient_cert.pem",
|
||||
"PlatformCertPath": "pub_key.pem",
|
||||
"PlatformCertSerialNo": "PUB_KEY_ID_0117379432252026012200382382002003",
|
||||
"NotifyUrl": "https://app.zpc-xy.com/xyqj/api/app/pay/notify"
|
||||
"NotifyUrl": "https://app.zpc-xy.com/api/app/pay/notify"
|
||||
},
|
||||
"Storage": {
|
||||
"Provider": "TencentCos",
|
||||
|
|
|
|||
|
|
@ -69,6 +69,13 @@ public interface IWeChatService
|
|||
/// </summary>
|
||||
/// <returns>AccessToken</returns>
|
||||
Task<string?> GetServiceAccountAccessTokenAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务号关注用户信息(包含UnionId)
|
||||
/// </summary>
|
||||
/// <param name="openId">用户在服务号的OpenId</param>
|
||||
/// <returns>用户信息</returns>
|
||||
Task<ServiceAccountUserInfo?> GetServiceAccountUserInfoAsync(string openId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -328,3 +335,24 @@ public class MiniProgramInfo
|
|||
/// </summary>
|
||||
public string? PagePath { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 服务号关注用户信息
|
||||
/// </summary>
|
||||
public class ServiceAccountUserInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否关注(0未关注,1已关注)
|
||||
/// </summary>
|
||||
public int Subscribe { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 用户OpenId
|
||||
/// </summary>
|
||||
public string OpenId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// UnionId(绑定开放平台后才有)
|
||||
/// </summary>
|
||||
public string? UnionId { get; set; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -470,6 +470,44 @@ public class WeChatService : IWeChatService
|
|||
}
|
||||
}
|
||||
|
||||
public async Task<ServiceAccountUserInfo?> GetServiceAccountUserInfoAsync(string openId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var accessToken = await GetServiceAccountAccessTokenAsync();
|
||||
if (string.IsNullOrEmpty(accessToken))
|
||||
{
|
||||
_logger.LogError("获取服务号用户信息失败: AccessToken为空");
|
||||
return null;
|
||||
}
|
||||
|
||||
var url = $"https://api.weixin.qq.com/cgi-bin/user/info?access_token={accessToken}&openid={openId}&lang=zh_CN";
|
||||
var httpResponse = await _httpClient.GetAsync(url);
|
||||
var responseContent = await httpResponse.Content.ReadAsStringAsync();
|
||||
|
||||
_logger.LogInformation("获取服务号用户信息响应: {Response}", responseContent);
|
||||
|
||||
var result = System.Text.Json.JsonSerializer.Deserialize<ServiceAccountUserInfoResponse>(responseContent);
|
||||
if (result == null || result.ErrCode != 0)
|
||||
{
|
||||
_logger.LogWarning("获取服务号用户信息失败: {ErrCode} - {ErrMsg}", result?.ErrCode, result?.ErrMsg);
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ServiceAccountUserInfo
|
||||
{
|
||||
Subscribe = result.Subscribe,
|
||||
OpenId = result.OpenId ?? openId,
|
||||
UnionId = result.UnionId
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "获取服务号用户信息异常: OpenId={OpenId}", openId);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 私有方法
|
||||
|
|
@ -752,5 +790,23 @@ public class WeChatService : IWeChatService
|
|||
public string? OpenId { get; set; }
|
||||
}
|
||||
|
||||
private class ServiceAccountUserInfoResponse
|
||||
{
|
||||
[JsonPropertyName("subscribe")]
|
||||
public int Subscribe { get; set; }
|
||||
|
||||
[JsonPropertyName("openid")]
|
||||
public string? OpenId { get; set; }
|
||||
|
||||
[JsonPropertyName("unionid")]
|
||||
public string? UnionId { get; set; }
|
||||
|
||||
[JsonPropertyName("errcode")]
|
||||
public int ErrCode { get; set; }
|
||||
|
||||
[JsonPropertyName("errmsg")]
|
||||
public string? ErrMsg { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user