wip: Integrated WeChat login

This commit is contained in:
AnonymousDotNet 2024-12-13 19:29:12 +08:00
parent d11e91ca84
commit d41d6eddd8
8 changed files with 132 additions and 16 deletions

View file

@ -51,7 +51,7 @@ public interface IBotSharpRepository : IHaveServiceProvider
bool UpdateUser(User user, bool updateUserAgents = false) => throw new NotImplementedException();
WeChatUser? GetWeChatUser(string openId) => throw new NotImplementedException();
WeChatUser? CreateWeChatUser(WeChatUser weChatUser) => throw new NotImplementedException();
WeChatUser CreateWeChatUser(WeChatUser weChatUser) => throw new NotImplementedException();
WeChatUser? UpdateWeChatUser(WeChatUser weChatUser) => throw new NotImplementedException();
#endregion

View file

@ -9,4 +9,6 @@ public interface IWeChatUserService
Task<WeChatUser?> CreateWeChatUser(WeChatUser weChatUser);
Task<WeChatUser?> UpdateWeChatUser(WeChatUser weChatUser);
Task<WeChatUser> WeChatUserLogin(string code);
}

View file

@ -1,3 +1,5 @@
using BotSharp.Abstraction.Users.Enums;
namespace BotSharp.Abstraction.Users.Models;
public class WeChatUser
@ -9,8 +11,6 @@ public class WeChatUser
/// </summary>
public string OpenId { get; set; } = string.Empty;
public string SessionKey { get; set; } = string.Empty;
/// <summary>
/// User unique identifier (cross application unique, requiring open platform binding)
/// </summary>
@ -52,4 +52,22 @@ public class WeChatUser
public DateTime? UpdatedAt { get; set; }
public DateTime CreatedAt { get; set; }
public User ToUser()
{
return new User
{
Id = Id,
Phone = PhoneNumber,
UserName = PhoneNumber,
FirstName = PhoneNumber,
LastName = PhoneNumber,
Email = null,
Password = string.Empty,
Role = UserRole.User,
Type = UserType.Client,
RegionCode = "CN",
ReferralCode = null,
};
}
}

View file

@ -1,23 +1,30 @@
using BotSharp.Abstraction.Users.Models;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json.Linq;
namespace BotSharp.Core.Users.Services;
public class WeChatUserService : IWeChatUserService
{
private readonly IServiceProvider _services;
public WeChatUserService(IServiceProvider services)
private readonly ILogger _logger;
private readonly IConfiguration _configuration;
public WeChatUserService(IServiceProvider services, ILogger<WeChatUserService> logger, IConfiguration configuration)
{
_services = services;
_logger = logger;
_configuration = configuration;
}
public async Task<WeChatUser?> GetWeChatUser(string openId)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
var db = _services.GetRequiredService<IBotSharpRepository>();
var weChatUser = db.GetWeChatUser(openId);
return weChatUser;
}
public async Task<WeChatUser?> CreateWeChatUser(WeChatUser weChatUser)
public async Task<WeChatUser> CreateWeChatUser(WeChatUser weChatUser)
{
var db = _services.GetRequiredService<IBotSharpRepository>();
return db.CreateWeChatUser(weChatUser);
@ -28,4 +35,79 @@ public class WeChatUserService : IWeChatUserService
var db = _services.GetRequiredService<IBotSharpRepository>();
return db.UpdateWeChatUser(weChatUser);
}
#region OAuth 2.0
public async Task<WeChatUser> WeChatUserLogin(string code)
{
// 实现获取 access_token 和 openid再获取用户信息
if (string.IsNullOrEmpty(code))
{
_logger.LogError("Create WeChatUser Error: code is empty, Please check if the code has been obtained correctly!");
throw new Exception("code is empty");
}
var appId = _configuration["WeChatQtoss:AppId"] ?? throw new Exception("AppId is not configured.");
var appSecret = _configuration["WeChatQtoss:AppSecret"] ?? throw new Exception("AppSecret is not configured.");
try
{
// Step 1: 使用 code 获取 access_token 和 openid
var tokenUrl = $"https://api.weixin.qq.com/sns/oauth2/access_token?appid={appId}&secret={appSecret}&code={code}&grant_type=authorization_code";
using var httpClient = new HttpClient();
var tokenResponse = await httpClient.GetStringAsync(tokenUrl);
var tokenData = JObject.Parse(tokenResponse);
var accessToken = tokenData["access_token"]?.ToString();
var openId = tokenData["openid"]?.ToString();
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(openId))
{
_logger.LogError("Create WeChatUser Error: Failed to get access_token or openid");
throw new Exception("Failed to get access_token or openid");
}
// Step 2: Retrieve user information using access_token and openid
var userInfoUrl = $"https://api.weixin.qq.com/sns/userinfo?access_token={accessToken}&openid={openId}&lang=zh_CN";
var userInfoResponse = await httpClient.GetStringAsync(userInfoUrl);
var userInfo = JObject.Parse(userInfoResponse);
// 提取用户信息
var nickname = userInfo["nickname"]?.ToString() ?? string.Empty;
var avatar = userInfo["headimgurl"]?.ToString() ?? string.Empty;
var country = userInfo["country"]?.ToString() ?? string.Empty;
var province = userInfo["province"]?.ToString() ?? string.Empty;
var city = userInfo["city"]?.ToString() ?? string.Empty;
var sex = userInfo["sex"]?.ToString() ?? "0";
var privilege = userInfo["privilege"]?.ToString() ?? string.Empty;
var unionId = userInfo["unionId"]?.ToString() ?? string.Empty;
// 创建或更新 WeChatUser
var weChatUser = new WeChatUser
{
OpenId = openId,
NickName = nickname,
Sex = int.Parse(sex),
Province = province,
City = city,
Country = country,
Headimgurl = avatar,
Privilege = privilege.Split(','),
UnionId = unionId
};
return await CreateWeChatUser(weChatUser);
}
catch (Exception ex)
{
_logger.LogError($"Create WeChatUser Error: {ex.Message}");
throw new Exception($"Failed to create WeChatUser: {ex.Message}", ex);
}
}
#endregion OAuth 2.0
}

View file

@ -78,10 +78,21 @@ public class UserController : ControllerBase
public async Task CreateWeChatUser(string code)
{
// Get WeChat User Info
var wechatUser = await _weChatUserService.WeChatUserLogin(code);
if (wechatUser == null)
{
return;
}
var weChatUser = await _weChatUserService.CreateWeChatUser(wechatUser);
WeChatUser wechatUser = new();
var user = await _weChatUserService.CreateWeChatUser(wechatUser);
if (wechatUser == null)
{
return;
}
// Create User
var createdUser = await _userService.CreateUser(weChatUser.ToUser());
}
[AllowAnonymous]

View file

@ -11,8 +11,6 @@ public class WeChatUserDocument : MongoBase
/// </summary>
public string OpenId { get; set; } = string.Empty;
public string SessionKey { get; set; } = string.Empty;
/// <summary>
/// User unique identifier (cross application unique, requiring open platform binding)
/// </summary>
@ -61,7 +59,6 @@ public class WeChatUserDocument : MongoBase
{
Id = Id,
OpenId = OpenId,
SessionKey = SessionKey,
UnionId = UnionId,
Sex = Sex,
Province = Province,

View file

@ -10,15 +10,12 @@ public partial class MongoRepository
return weChatUser != null ? weChatUser.ToWeChatUser() : null;
}
public WeChatUser? CreateWeChatUser(WeChatUser weChatUser)
public WeChatUser CreateWeChatUser(WeChatUser weChatUser)
{
if (weChatUser == null) return null;
var weChatUserInfo = new WeChatUserDocument
{
Id = weChatUser.Id ?? Guid.NewGuid().ToString(),
OpenId = weChatUser.OpenId,
SessionKey = weChatUser.SessionKey,
UnionId = weChatUser.UnionId,
Sex = weChatUser.Sex,
Province = weChatUser.Province,
@ -43,7 +40,6 @@ public partial class MongoRepository
var filter = Builders<WeChatUserDocument>.Filter.Eq(x => x.Id, weChatUser.Id);
var update = Builders<WeChatUserDocument>.Update
.Set(x => x.OpenId, weChatUser.OpenId)
.Set(x => x.SessionKey, weChatUser.SessionKey)
.Set(x => x.UnionId, weChatUser.UnionId)
.Set(x => x.Sex, weChatUser.Sex)
.Set(x => x.Province, weChatUser.Province)

View file

@ -273,6 +273,16 @@
"WeixinAppSecret": "#{WeixinAppSecret}#"
},
"WeChatQtoss": {
"AppId": "",
"AppSecret": ""
},
"WeChatQtossAI": {
"AppId": "",
"AppSecret": ""
},
"KnowledgeBase": {
"VectorDb": {
"Provider": "Qdrant"