feat: 新增微信公众号扫码注册功能及幂等处理
- 新增 `FuwuhaoConst` 常量类,统一缓存 Key 前缀管理 - `FuwuhaoOptions` 增加 FromUser、RedirectUri、PicUrl 配置项 - `FuwuhaoManager` 新增 `BuildRegisterMessage` 方法,构建注册引导图文消息 - `FuwuhaoService` - 增加 OpenId 与 Scene 绑定缓存,支持扫码注册有效期管理 - 回调处理支持注册场景,返回图文消息引导用户注册 - 新增注册接口 `RegisterByCodeAsync`,根据微信授权信息自动注册账号并更新场景状态 - `AccountManager` 注册方法增加分布式锁,防止重复注册,并校验用户名唯一性
This commit is contained in:
@@ -19,7 +19,8 @@ public class FuwuhaoManager : DomainService
|
||||
private ISqlSugarRepository<AiUserExtraInfoEntity> _userRepository;
|
||||
|
||||
public FuwuhaoManager(IOptions<FuwuhaoOptions> options, IHttpClientFactory httpClientFactory,
|
||||
ISqlSugarRepository<AiUserExtraInfoEntity> userRepository, IDistributedCache<AccessTokenResponse> accessTokenCache)
|
||||
ISqlSugarRepository<AiUserExtraInfoEntity> userRepository,
|
||||
IDistributedCache<AccessTokenResponse> accessTokenCache)
|
||||
{
|
||||
_options = options.Value;
|
||||
_httpClientFactory = httpClientFactory;
|
||||
@@ -97,7 +98,8 @@ public class FuwuhaoManager : DomainService
|
||||
/// <returns>用户基础信息响应对象</returns>
|
||||
private async Task<UserBaseInfoResponse> GetBaseUserInfoByCodeAsync(string code)
|
||||
{
|
||||
var url = $"https://api.weixin.qq.com/sns/oauth2/access_token?appid={_options.AppId}&secret={_options.Secret}&code={code}&grant_type=authorization_code";
|
||||
var url =
|
||||
$"https://api.weixin.qq.com/sns/oauth2/access_token?appid={_options.AppId}&secret={_options.Secret}&code={code}&grant_type=authorization_code";
|
||||
|
||||
var response = await _httpClientFactory.CreateClient().GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
@@ -116,10 +118,11 @@ public class FuwuhaoManager : DomainService
|
||||
/// </summary>
|
||||
/// <param name="code">授权码</param>
|
||||
/// <returns>用户信息响应对象</returns>
|
||||
public async Task<UserInfoResponse> GetUserInfoByCodeAsync(string code)
|
||||
public async Task<UserInfoResponse?> GetUserInfoByCodeAsync(string code)
|
||||
{
|
||||
var baseUserInfo = await GetBaseUserInfoByCodeAsync(code);
|
||||
var url = $"https://api.weixin.qq.com/sns/userinfo?access_token={baseUserInfo.AccessToken}&openid={baseUserInfo.OpenId}&lang=zh_CN";
|
||||
var baseUserInfo = await GetBaseUserInfoByCodeAsync(code);
|
||||
var url =
|
||||
$"https://api.weixin.qq.com/sns/userinfo?access_token={baseUserInfo.AccessToken}&openid={baseUserInfo.OpenId}&lang=zh_CN";
|
||||
|
||||
var response = await _httpClientFactory.CreateClient().GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
@@ -142,11 +145,11 @@ public class FuwuhaoManager : DomainService
|
||||
public void ValidateCallback(string signature, string timestamp, string nonce)
|
||||
{
|
||||
var token = _options.CallbackToken; // 您设置的token
|
||||
|
||||
|
||||
// 将token、timestamp、nonce三个参数进行字典序排序
|
||||
var parameters = new[] { token, timestamp, nonce };
|
||||
Array.Sort(parameters);
|
||||
|
||||
|
||||
// 将三个参数字符串拼接成一个字符串
|
||||
var concatenated = string.Join("", parameters);
|
||||
|
||||
@@ -164,8 +167,40 @@ public class FuwuhaoManager : DomainService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 构建引导注册图文消息体
|
||||
/// </summary>
|
||||
/// <param name="toUser">接收用户的OpenID</param>
|
||||
/// <param name="title">图文消息标题</param>
|
||||
/// <param name="description">图文消息描述</param>
|
||||
/// <returns>XML格式的图文消息体</returns>
|
||||
public string BuildRegisterMessage(string toUser, string title="意社区点击一键注册账号", string description="来自意社区SSO统一注册安全中心")
|
||||
{
|
||||
var createTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
|
||||
var fromUser = _options.FromUser;
|
||||
var url =
|
||||
$"https://open.weixin.qq.com/connect/oauth2/authorize?appid={_options.AppId}&redirect_uri={_options.RedirectUri}&response_type=code&scope=snsapi_userinfo&state={createTime}#wechat_redirect";
|
||||
var xml = $@"<xml>
|
||||
<ToUserName><![CDATA[{toUser}]]></ToUserName>
|
||||
<FromUserName><![CDATA[{fromUser}]]></FromUserName>
|
||||
<CreateTime>{createTime}</CreateTime>
|
||||
<MsgType><![CDATA[news]]></MsgType>
|
||||
<ArticleCount>1</ArticleCount>
|
||||
<Articles>
|
||||
<item>
|
||||
<Title><![CDATA[{title}]]></Title>
|
||||
<Description><![CDATA[{description}]]></Description>
|
||||
<PicUrl><![CDATA[{_options.PicUrl}]]></PicUrl>
|
||||
<Url><![CDATA[{url}]]></Url>
|
||||
</item>
|
||||
</Articles>
|
||||
</xml>";
|
||||
return xml;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 处理回调逻辑
|
||||
/// </summary>
|
||||
|
||||
@@ -6,7 +6,7 @@ public class FuwuhaoOptions
|
||||
/// 微信公众号AppId
|
||||
/// </summary>
|
||||
public string AppId { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 微信公众号AppSecret
|
||||
/// </summary>
|
||||
@@ -16,4 +16,19 @@ public class FuwuhaoOptions
|
||||
/// 回调token
|
||||
/// </summary>
|
||||
public string CallbackToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 微信公众号原始ID(用于FromUser)
|
||||
/// </summary>
|
||||
public string FromUser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 微信网页授权跳转地址
|
||||
/// </summary>
|
||||
public string RedirectUri { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图片地址
|
||||
/// </summary>
|
||||
public string PicUrl { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user