diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/QrCodeOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/QrCodeOutput.cs
new file mode 100644
index 00000000..c98f2e8b
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/QrCodeOutput.cs
@@ -0,0 +1,14 @@
+namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Fuwuhao;
+
+public class QrCodeOutput
+{
+ ///
+ /// Qrcode url
+ ///
+ public string QrCodeUrl { get; set; }
+
+ ///
+ /// 场景值
+ ///
+ public string Scene { get; set; }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/QrCodeResultOutput.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/QrCodeResultOutput.cs
new file mode 100644
index 00000000..9473cc79
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/QrCodeResultOutput.cs
@@ -0,0 +1,21 @@
+using Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
+
+namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Fuwuhao;
+
+public class QrCodeResultOutput
+{
+ ///
+ /// 返回状态
+ ///
+ public SceneResultEnum SceneResult { get; set; } = SceneResultEnum.Wait;
+
+ ///
+ /// 如果是已登录,返回token
+ ///
+ public string? Token { get; set; }
+
+ ///
+ /// 刷新token
+ ///
+ public string? RefreshToken { get; set; }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/SceneCacheDto.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/SceneCacheDto.cs
new file mode 100644
index 00000000..ac9615d2
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/SceneCacheDto.cs
@@ -0,0 +1,18 @@
+using Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
+
+namespace Yi.Framework.AiHub.Application.Contracts.Dtos.Fuwuhao;
+
+public class SceneCacheDto
+{
+ public string Scene { get; set; }
+
+ public SceneResultEnum SceneResult { get; set; } = SceneResultEnum.Wait;
+
+ public SceneTypeEnum SceneType { get; set; }
+
+ ///
+ /// 如果是绑定类型,需要用户id
+ ///
+ public Guid? UserId { get; set; }
+}
+
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/FuwuhaoService.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/FuwuhaoService.cs
index 40054d7e..440aceb7 100644
--- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/FuwuhaoService.cs
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application/Services/FuwuhaoService.cs
@@ -3,8 +3,16 @@ using System.Text.Json;
using System.Xml.Serialization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using Volo.Abp.Application.Services;
+using Volo.Abp.Caching;
+using Volo.Abp.Users;
+using Yi.Framework.AiHub.Application.Contracts.Dtos.Fuwuhao;
+using Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
+using Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
+using Yi.Framework.Rbac.Application.Contracts.Dtos.Account;
+using Yi.Framework.Rbac.Application.Contracts.IServices;
namespace Yi.Framework.AiHub.Application.Services;
@@ -15,25 +23,41 @@ public class FuwuhaoService : ApplicationService
{
private readonly ILogger _logger;
private readonly IHttpContextAccessor _accessor;
+ private readonly FuwuhaoManager _fuwuhaoManager;
+ private IDistributedCache _sceneCache;
+ private IAccountService _accountService;
+ private IFileService _fileService;
- public FuwuhaoService(ILogger logger, IHttpContextAccessor accessor)
+ public FuwuhaoService(ILogger logger, IHttpContextAccessor accessor, FuwuhaoManager fuwuhaoManager,
+ IDistributedCache sceneCache, IAccountService accountService, IFileService fileService)
{
_logger = logger;
_accessor = accessor;
+ _fuwuhaoManager = fuwuhaoManager;
+ _sceneCache = sceneCache;
+ _accountService = accountService;
+ _fileService = fileService;
}
///
- /// 查询已登录的账户信息
+ /// 服务器号测试回调
///
///
[HttpGet("fuwuhao/callback")]
public async Task GetCallbackAsync([FromQuery] string signature, [FromQuery] string timestamp,
[FromQuery] string nonce, [FromQuery] string echostr)
{
-
return echostr;
}
+ ///
+ /// 服务号关注回调
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
[HttpPost("fuwuhao/callback")]
public async Task PostCallbackAsync([FromQuery] string signature, [FromQuery] string timestamp,
[FromQuery] string nonce)
@@ -42,16 +66,124 @@ public class FuwuhaoService : ApplicationService
// 1. 读取原始 XML 内容
using var reader = new StreamReader(request.Body, Encoding.UTF8);
var xmlString = await reader.ReadToEndAsync();
-
+
var serializer = new XmlSerializer(typeof(FuwuhaoCallModel));
using var stringReader = new StringReader(xmlString);
var body = (FuwuhaoCallModel)serializer.Deserialize(stringReader);
- _logger.LogError("服务号Post回调通知:" + JsonSerializer.Serialize(body, new JsonSerializerOptions()
+
+ //获取场景值,后续通过场景值设置缓存状态,前端轮询这个场景值用户是否已操作即可
+ var scene = body.EventKey;
+
+ if (scene is null)
{
- WriteIndented = true
- }));
+ throw new UserFriendlyException("服务号返回无场景值");
+ }
+
+ var cache = await _sceneCache.GetAsync($"fuwuhao:{scene}");
+
+ //根据操作类型,进行业务处理,返回处理结果,再写入缓存,10s过去,相当于用户10s扫完app后,轮询要在10秒内完成
+ var scenResult = await _fuwuhaoManager.CallBackHandlerAsync(cache.SceneType, body.FromUserName, cache.UserId);
+ cache.SceneResult = scenResult;
+
+ await _sceneCache.SetAsync($"fuwuhao:{scene}", cache,
+ new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10) });
+
return "success";
}
+
+
+ ///
+ /// 创建带参数的二维码
+ ///
+ /// 二维码URL
+ [HttpPost("fuwuhao/qrcode")]
+ public async Task GetQrCodeAsync([FromQuery] SceneTypeEnum sceneType)
+ {
+ //生成一个随机场景值
+ var scene = Guid.NewGuid().ToString("N");
+ var qrCodeUrl = await _fuwuhaoManager.CreateQrCodeAsync(scene);
+ await _sceneCache.SetAsync($"fuwuhao:{scene}", new SceneCacheDto()
+ {
+ UserId = CurrentUser.IsAuthenticated ? CurrentUser.GetId() : null,
+ SceneType = sceneType
+ }, new DistributedCacheEntryOptions() { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10) });
+ return new QrCodeOutput()
+ {
+ QrCodeUrl = qrCodeUrl,
+ Scene = scene
+ };
+ }
+
+ ///
+ /// 扫码登录/注册/绑定,轮询接口
+ ///
+ ///
+ ///
+ [HttpGet("fuwuhao/qrcode/result")]
+ public async Task GetQrCodeResultAsync([FromQuery] string scene)
+ {
+ var output = new QrCodeResultOutput();
+ var cache = await _sceneCache.GetAsync($"fuwuhao:{scene}");
+ if (cache is null)
+ {
+ output.SceneResult = SceneResultEnum.Expired;
+ return output;
+ }
+
+ output.SceneResult = cache.SceneResult;
+ switch (output.SceneResult)
+ {
+ case SceneResultEnum.Login:
+ var loginInfo = await _accountService.PostLoginAsync(cache.UserId!.Value);
+ output.Token = loginInfo.Token;
+ output.RefreshToken = loginInfo.RefreshToken;
+ break;
+ }
+
+ return output;
+ }
+
+ ///
+ /// 注册账号,需要微信code
+ ///
+ ///
+ ///
+ [HttpPost("fuwuhao/register")]
+ public async Task RegisterByCodeAsync([FromQuery] string code)
+ {
+ //根据code获取到openid、微信用户昵称、头像
+ var userInfo = await _fuwuhaoManager.GetUserInfoByCodeAsync(code);
+ var files = new FormFileCollection();
+
+ // 下载头像并添加到系统文件中
+ if (!string.IsNullOrEmpty(userInfo.HeadImgUrl))
+ {
+ using var httpClient = new HttpClient();
+ var imageBytes = await httpClient.GetByteArrayAsync(userInfo.HeadImgUrl);
+ var imageStream = new MemoryStream(imageBytes);
+
+ // 从URL中提取文件扩展名,默认为png
+ var fileName = $"avatar_{userInfo.OpenId}.png";
+ var formFile = new FormFile(imageStream, 0, imageBytes.Length, "avatar", fileName)
+ {
+ Headers = new HeaderDictionary(),
+ ContentType = "image/png"
+ };
+ files.Add(formFile);
+ }
+ var result = await _fileService.Post(files);
+
+ var userId = await _accountService.PostSystemRegisterAsync(new RegisterDto
+ {
+ UserName = userInfo.Nickname,
+ Password = Guid.NewGuid().ToString("N"),
+ Phone = null,
+ Email = null,
+ Nick = userInfo.Nickname,
+ Icon = result.FirstOrDefault()?.Id.ToString()
+ });
+ return await _accountService.PostLoginAsync(userId);
+ }
}
[XmlRoot("xml")]
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Enums/Fuwuhao/SceneResultEnum.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Enums/Fuwuhao/SceneResultEnum.cs
new file mode 100644
index 00000000..3f7dfce8
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Enums/Fuwuhao/SceneResultEnum.cs
@@ -0,0 +1,29 @@
+namespace Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
+
+public enum SceneResultEnum
+{
+ ///
+ /// 等待,用户未扫码
+ ///
+ Wait = 0,
+
+ ///
+ /// 已扫码完成登录
+ ///
+ Login = 1,
+
+ ///
+ /// 已扫码完成注册
+ ///
+ Register = 2,
+
+ ///
+ /// 已扫码完成绑定
+ ///
+ Bind = 3,
+
+ ///
+ /// 已过期
+ ///
+ Expired = 10
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Enums/Fuwuhao/SceneTypeEnum.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Enums/Fuwuhao/SceneTypeEnum.cs
new file mode 100644
index 00000000..f1cc5486
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Enums/Fuwuhao/SceneTypeEnum.cs
@@ -0,0 +1,7 @@
+namespace Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
+
+public enum SceneTypeEnum
+{
+ LoginOrRegister,
+ Bind
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/AiUserExtraInfoEntity.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/AiUserExtraInfoEntity.cs
new file mode 100644
index 00000000..f03a6042
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/AiUserExtraInfoEntity.cs
@@ -0,0 +1,40 @@
+using SqlSugar;
+using Volo.Abp.Auditing;
+using Volo.Abp.Domain.Entities;
+
+namespace Yi.Framework.AiHub.Domain.Entities;
+
+///
+/// ai用户表
+///
+[SugarTable("Ai_UserExtraInfo")]
+[SugarIndex($"index_{nameof(UserId)}", nameof(UserId), OrderByType.Asc)]
+public class AiUserExtraInfoEntity : Entity, IHasCreationTime, ISoftDelete
+{
+ public AiUserExtraInfoEntity()
+ {
+ }
+
+ public AiUserExtraInfoEntity(Guid userId, string fuwuhaoOpenId)
+ {
+ this.UserId = userId;
+ this.FuwuhaoOpenId = fuwuhaoOpenId;
+ }
+
+ ///
+ /// 用户id
+ ///
+ public Guid UserId { get; set; }
+
+ ///
+ /// 服务号,openid
+ ///
+ public string FuwuhaoOpenId { get; set; }
+
+ ///
+ /// 创建时间
+ ///
+ public DateTime CreationTime { get; set; }
+
+ public bool IsDeleted { get; set; }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/AccessTokenResponse.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/AccessTokenResponse.cs
new file mode 100644
index 00000000..5c42d8fd
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/AccessTokenResponse.cs
@@ -0,0 +1,17 @@
+namespace Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
+
+///
+/// AccessToken响应对象
+///
+public class AccessTokenResponse
+{
+ ///
+ /// 访问令牌
+ ///
+ public string AccessToken { get; set; }
+
+ ///
+ /// 过期时间(秒)
+ ///
+ public int ExpiresIn { get; set; }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/FuwuhaoManager.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/FuwuhaoManager.cs
new file mode 100644
index 00000000..1a3c9d5d
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/FuwuhaoManager.cs
@@ -0,0 +1,179 @@
+using System.Text;
+using System.Text.Json;
+using Microsoft.Extensions.Caching.Distributed;
+using Microsoft.Extensions.Options;
+using Volo.Abp.Caching;
+using Volo.Abp.Domain.Services;
+using Yi.Framework.AiHub.Domain.Entities;
+using Yi.Framework.AiHub.Domain.Shared.Enums.Fuwuhao;
+using Yi.Framework.SqlSugarCore.Abstractions;
+
+namespace Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
+
+public class FuwuhaoManager : DomainService
+{
+ private readonly FuwuhaoOptions _options;
+ private readonly IHttpClientFactory _httpClientFactory;
+ private IDistributedCache _accessTokenCache;
+ private ISqlSugarRepository _userRepository;
+
+ public FuwuhaoManager(IOptions options, IHttpClientFactory httpClientFactory,
+ ISqlSugarRepository userRepository)
+ {
+ _options = options.Value;
+ _httpClientFactory = httpClientFactory;
+ _userRepository = userRepository;
+ }
+
+ ///
+ /// 获取微信公众号AccessToken
+ ///
+ /// AccessToken响应对象
+ private async Task GetAccessTokenAsync()
+ {
+ var output = await _accessTokenCache.GetOrAddAsync("Fuwuhao", async () =>
+ {
+ var url =
+ $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={_options.AppId}&secret={_options.Secret}";
+
+ var response = await _httpClientFactory.CreateClient().GetAsync(url);
+ response.EnsureSuccessStatusCode();
+
+ var jsonContent = await response.Content.ReadAsStringAsync();
+ var result = JsonSerializer.Deserialize(jsonContent, new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
+ });
+ return result;
+ }, () => new DistributedCacheEntryOptions()
+ {
+ AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(3600)
+ });
+
+ return output.AccessToken;
+ }
+
+ ///
+ /// 创建带参数的二维码
+ ///
+ /// 场景值
+ /// 二维码URL
+ public async Task CreateQrCodeAsync(string scene)
+ {
+ var accessToken = await GetAccessTokenAsync();
+ var url = $"https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token={accessToken}";
+
+ var requestBody = new
+ {
+ action_name = "QR_STR_SCENE",
+ action_info = new
+ {
+ scene = new
+ {
+ expire_seconds = 600,
+ scene_str = scene
+ }
+ }
+ };
+
+ var jsonContent = JsonSerializer.Serialize(requestBody);
+ var content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
+
+ var response = await _httpClientFactory.CreateClient().PostAsync(url, content);
+ response.EnsureSuccessStatusCode();
+
+ var responseContent = await response.Content.ReadAsStringAsync();
+ var result = JsonSerializer.Deserialize(responseContent);
+
+ return $"https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket={result.Ticket}";
+ }
+
+ ///
+ /// 通过code获取用户基础信息接口(为了获取用户access_token和openid)
+ ///
+ /// 授权码
+ /// 用户基础信息响应对象
+ private async Task 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 response = await _httpClientFactory.CreateClient().GetAsync(url);
+ response.EnsureSuccessStatusCode();
+
+ var jsonContent = await response.Content.ReadAsStringAsync();
+ var result = JsonSerializer.Deserialize(jsonContent, new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
+ });
+
+ return result;
+ }
+
+ ///
+ /// 通过code获取用户信息接口
+ ///
+ /// 授权码
+ /// 用户信息响应对象
+ public async Task 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 response = await _httpClientFactory.CreateClient().GetAsync(url);
+ response.EnsureSuccessStatusCode();
+
+ var jsonContent = await response.Content.ReadAsStringAsync();
+ var result = JsonSerializer.Deserialize(jsonContent, new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
+ });
+ return result;
+ }
+
+ ///
+ /// 处理回调逻辑
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task CallBackHandlerAsync(SceneTypeEnum sceneType, string openId, Guid? bindUserId)
+ {
+ var aiUserInfo = await _userRepository._DbQueryable.Where(x => x.FuwuhaoOpenId == openId).FirstAsync();
+ switch (sceneType)
+ {
+ case SceneTypeEnum.LoginOrRegister:
+ //有openid,说明登录成功
+ if (aiUserInfo is not null)
+ {
+ return SceneResultEnum.Login;
+ }
+ //无openid,说明需要进行注册
+ else
+ {
+ return SceneResultEnum.Register;
+ }
+
+ break;
+ case SceneTypeEnum.Bind:
+ //说明已经有微信号,直接换绑
+
+ if (aiUserInfo is not null)
+ {
+ await _userRepository.DeleteByIdAsync(aiUserInfo.Id);
+ }
+
+ if (bindUserId is null)
+ {
+ throw new UserFriendlyException("绑定用户,需要传入绑定的用户id");
+ }
+
+ //说明没有绑定过,直接绑定
+ await _userRepository.InsertAsync(new AiUserExtraInfoEntity(bindUserId.Value, openId));
+ return SceneResultEnum.Bind;
+ break;
+ default:
+ throw new ArgumentOutOfRangeException(nameof(sceneType), sceneType, null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/FuwuhaoOptions.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/FuwuhaoOptions.cs
new file mode 100644
index 00000000..46f53274
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/FuwuhaoOptions.cs
@@ -0,0 +1,14 @@
+namespace Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
+
+public class FuwuhaoOptions
+{
+ ///
+ /// 微信公众号AppId
+ ///
+ public string AppId { get; set; }
+
+ ///
+ /// 微信公众号AppSecret
+ ///
+ public string Secret { get; set; }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/QrCodeResponse.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/QrCodeResponse.cs
new file mode 100644
index 00000000..e641539e
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/QrCodeResponse.cs
@@ -0,0 +1,22 @@
+namespace Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
+
+///
+/// 二维码响应对象
+///
+public class QrCodeResponse
+{
+ ///
+ /// 二维码票据
+ ///
+ public string Ticket { get; set; }
+
+ ///
+ /// 过期时间(秒)
+ ///
+ public int ExpireSeconds { get; set; }
+
+ ///
+ /// 二维码URL
+ ///
+ public string Url { get; set; }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/UserBaseInfoResponse.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/UserBaseInfoResponse.cs
new file mode 100644
index 00000000..bd2afb36
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/UserBaseInfoResponse.cs
@@ -0,0 +1,42 @@
+namespace Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
+
+///
+/// 用户基础信息响应对象
+///
+public class UserBaseInfoResponse
+{
+ ///
+ /// 访问令牌
+ ///
+ public string AccessToken { get; set; }
+
+ ///
+ /// 过期时间(秒)
+ ///
+ public int ExpiresIn { get; set; }
+
+ ///
+ /// 刷新令牌
+ ///
+ public string RefreshToken { get; set; }
+
+ ///
+ /// 用户OpenID
+ ///
+ public string OpenId { get; set; }
+
+ ///
+ /// 授权作用域
+ ///
+ public string Scope { get; set; }
+
+ ///
+ /// 是否为快照用户
+ ///
+ public int IsSnapshotUser { get; set; }
+
+ ///
+ /// 用户UnionID
+ ///
+ public string UnionId { get; set; }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/UserInfoResponse.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/UserInfoResponse.cs
new file mode 100644
index 00000000..a8c2faa7
--- /dev/null
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/UserInfoResponse.cs
@@ -0,0 +1,52 @@
+namespace Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
+
+///
+/// 用户信息响应对象
+///
+public class UserInfoResponse
+{
+ ///
+ /// 用户OpenID
+ ///
+ public string OpenId { get; set; }
+
+ ///
+ /// 用户昵称
+ ///
+ public string Nickname { get; set; }
+
+ ///
+ /// 用户性别,值为1时是男性,值为2时是女性,值为0时是未知
+ ///
+ public int Sex { get; set; }
+
+ ///
+ /// 用户个人资料填写的省份
+ ///
+ public string Province { get; set; }
+
+ ///
+ /// 普通用户个人资料填写的城市
+ ///
+ public string City { get; set; }
+
+ ///
+ /// 国家,如中国为CN
+ ///
+ public string Country { get; set; }
+
+ ///
+ /// 用户头像,最后一个数值代表正方形头像大小(有0、46、64、96、132数值可选,0代表640*640正方形头像)
+ ///
+ public string HeadImgUrl { get; set; }
+
+ ///
+ /// 用户特权信息
+ ///
+ public string[] Privilege { get; set; }
+
+ ///
+ /// 用户UnionID
+ ///
+ public string UnionId { get; set; }
+}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/YiFrameworkAiHubDomainModule.cs b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/YiFrameworkAiHubDomainModule.cs
index b98308c6..b04c2d0f 100644
--- a/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/YiFrameworkAiHubDomainModule.cs
+++ b/Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/YiFrameworkAiHubDomainModule.cs
@@ -12,6 +12,7 @@ using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorAzureOpenAI.Chats;
using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorAzureOpenAI.Images;
using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorDeepSeek.Chats;
using Yi.Framework.AiHub.Domain.AiGateWay.Impl.ThorSiliconFlow.Embeddings;
+using Yi.Framework.AiHub.Domain.Managers.Fuwuhao;
using Yi.Framework.AiHub.Domain.Shared;
using Yi.Framework.AiHub.Domain.Shared.Dtos.OpenAi;
using Yi.Framework.Mapster;
@@ -79,6 +80,9 @@ namespace Yi.Framework.AiHub.Domain
//配置支付宝支付
var config = configuration.GetSection("Alipay").Get();
Factory.SetOptions(config);
+
+ //配置服务号
+ Configure(configuration.GetSection("Fuwuhao"));
}
public override async Task OnApplicationInitializationAsync(ApplicationInitializationContext context)
diff --git a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/WeChatMiniProgramAccountService.cs b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/WeChatMiniProgramAccountService.cs
index b7d610eb..f3d682a4 100644
--- a/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/WeChatMiniProgramAccountService.cs
+++ b/Yi.Abp.Net8/module/digital-collectibles/Yi.Framework.DigitalCollectibles.Application/Services/Account/WeChatMiniProgramAccountService.cs
@@ -170,7 +170,7 @@ public class WeChatMiniProgramAccountService : ApplicationService
//走普通注册流程
//同时再加一个小程序绑定即可
var userName = GenerateRandomString(6);
- await _accountService.PostTempRegisterAsync(new RegisterDto
+ await _accountService.PostSystemRegisterAsync(new RegisterDto
{
UserName = $"ls_{userName}",
Password = GenerateRandomString(20),
diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/RegisterDto.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/RegisterDto.cs
index e0fc6f2b..dce6620a 100644
--- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/RegisterDto.cs
+++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/Dtos/Account/RegisterDto.cs
@@ -36,6 +36,11 @@
/// 昵称
///
public string? Nick{ get; set; }
-
+
+ ///
+ /// 头像
+ ///
+ public string? Icon { get; set; }
+
}
}
diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAccountService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAccountService.cs
index 1b402033..1dd7c6e6 100644
--- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAccountService.cs
+++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application.Contracts/IServices/IAccountService.cs
@@ -38,6 +38,6 @@ namespace Yi.Framework.Rbac.Application.Contracts.IServices
/// 不需要验证,为了给第三方使用,例如微信小程序,后续可通过绑定操作,进行账号合并
///
///
- Task PostTempRegisterAsync(RegisterDto input);
+ Task PostSystemRegisterAsync(RegisterDto input);
}
}
\ No newline at end of file
diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs
index f65bb249..b898feda 100644
--- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs
+++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Application/Services/AccountService.cs
@@ -246,20 +246,21 @@ namespace Yi.Framework.Rbac.Application.Services
//注册之后,免再次登录,直接给前端token
var userId = await _accountManager.RegisterAsync(input.UserName, input.Password, input.Phone, input.Email,
- input.Nick);
+ input.Nick, null);
return await this.PostLoginAsync(userId);
}
///
- /// 临时注册
+ /// 系统直接注册用户
/// 不需要验证,为了给第三方使用,例如微信小程序,后续可通过绑定操作,进行账号合并
///
///
[RemoteService(isEnabled: false)]
- public async Task PostTempRegisterAsync(RegisterDto input)
+ public async Task PostSystemRegisterAsync(RegisterDto input)
{
//注册领域逻辑
- await _accountManager.RegisterAsync(input.UserName, input.Password, input.Phone, input.Email, input.Nick);
+ return await _accountManager.RegisterAsync(input.UserName, input.Password, input.Phone, input.Email,
+ input.Nick, input.Icon);
}
///
diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Entities/UserAggregateRoot.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Entities/UserAggregateRoot.cs
index 3f127837..e35ec587 100644
--- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Entities/UserAggregateRoot.cs
+++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Entities/UserAggregateRoot.cs
@@ -19,12 +19,14 @@ namespace Yi.Framework.Rbac.Domain.Entities
{
}
- public UserAggregateRoot(string userName, string password, long? phone, string? email, string? nick = null)
+ public UserAggregateRoot(string userName, string password, long? phone, string? email, string? nick = null,
+ string? icon = null)
{
UserName = userName;
EncryPassword.Password = password;
Phone = phone;
Email = email;
+ Icon = icon;
Nick = string.IsNullOrWhiteSpace(nick) ? "萌新-" + userName : nick.Trim();
BuildPassword();
}
diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/AccountManager.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/AccountManager.cs
index 3256ed23..0a2359ae 100644
--- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/AccountManager.cs
+++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/AccountManager.cs
@@ -285,9 +285,10 @@ namespace Yi.Framework.Rbac.Domain.Managers
///
///
///
+ ///
///
public async Task RegisterAsync(string userName, string password, long? phone, string? email,
- string? nick)
+ string? nick,string? icon)
{
if (phone is null && string.IsNullOrWhiteSpace(email))
{
diff --git a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/IAccountManager.cs b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/IAccountManager.cs
index a8aaf557..b2b6a3fa 100644
--- a/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/IAccountManager.cs
+++ b/Yi.Abp.Net8/module/rbac/Yi.Framework.Rbac.Domain/Managers/IAccountManager.cs
@@ -14,7 +14,8 @@ namespace Yi.Framework.Rbac.Domain.Managers
string CreateRefreshToken(Guid userId);
Task GetTokenByUserIdAsync(Guid userId,Action? getUserInfo=null);
Task LoginValidationAsync(string userName, string password, Action? userAction = null);
- Task RegisterAsync(string userName, string password, long? phone, string? email, string? nick);
+ Task RegisterAsync(string userName, string password, long? phone, string? email, string? nick,
+ string? icon);
Task RestPasswordAsync(Guid userId, string password);
Task UpdatePasswordAsync(Guid userId, string newPassword, string oldPassword);
}