From b768bca638236131c362cc60c5742f1f306ac98c Mon Sep 17 00:00:00 2001 From: ccnetcore Date: Wed, 27 Aug 2025 23:42:46 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=8C=E6=88=90=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E6=89=AB=E7=A0=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dtos/Fuwuhao/QrCodeOutput.cs | 14 ++ .../Dtos/Fuwuhao/QrCodeResultOutput.cs | 21 ++ .../Dtos/Fuwuhao/SceneCacheDto.cs | 18 ++ .../Services/FuwuhaoService.cs | 146 +++++++++++++- .../Enums/Fuwuhao/SceneResultEnum.cs | 29 +++ .../Enums/Fuwuhao/SceneTypeEnum.cs | 7 + .../Entities/AiUserExtraInfoEntity.cs | 40 ++++ .../Managers/Fuwuhao/AccessTokenResponse.cs | 17 ++ .../Managers/Fuwuhao/FuwuhaoManager.cs | 179 ++++++++++++++++++ .../Managers/Fuwuhao/FuwuhaoOptions.cs | 14 ++ .../Managers/Fuwuhao/QrCodeResponse.cs | 22 +++ .../Managers/Fuwuhao/UserBaseInfoResponse.cs | 42 ++++ .../Managers/Fuwuhao/UserInfoResponse.cs | 52 +++++ .../YiFrameworkAiHubDomainModule.cs | 4 + .../WeChatMiniProgramAccountService.cs | 2 +- .../Dtos/Account/RegisterDto.cs | 7 +- .../IServices/IAccountService.cs | 2 +- .../Services/AccountService.cs | 9 +- .../Entities/UserAggregateRoot.cs | 4 +- .../Managers/AccountManager.cs | 3 +- .../Managers/IAccountManager.cs | 3 +- 21 files changed, 618 insertions(+), 17 deletions(-) create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/QrCodeOutput.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/QrCodeResultOutput.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Application.Contracts/Dtos/Fuwuhao/SceneCacheDto.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Enums/Fuwuhao/SceneResultEnum.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain.Shared/Enums/Fuwuhao/SceneTypeEnum.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Entities/AiUserExtraInfoEntity.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/AccessTokenResponse.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/FuwuhaoManager.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/FuwuhaoOptions.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/QrCodeResponse.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/UserBaseInfoResponse.cs create mode 100644 Yi.Abp.Net8/module/ai-hub/Yi.Framework.AiHub.Domain/Managers/Fuwuhao/UserInfoResponse.cs 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); }