From 5b7a939dcefdf6d1970ce63d2d2282cb094fe870 Mon Sep 17 00:00:00 2001 From: zpc Date: Fri, 20 Sep 2024 18:33:39 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HuanMeng.DotNetCore.csproj | 1 + .../HuanMeng.DotNetCore/WeChat/MiniProgram.cs | 148 ++++++++++++++++++ .../WeChat/WXBizDataCrypt.cs | 122 +++++++++++++++ .../AppExtend/AppConfig.cs | 6 + .../AppExtend/AppConfigExtendServer.cs | 7 - .../AppExtend/AppConfigurationExtend.cs | 5 + .../HuanMeng.MiaoYu.Code/Base/MiaoYuBase.cs | 32 ++-- .../HuanMeng.MiaoYu.Code.csproj | 1 + .../Payment/PaymentExtend.cs | 23 ++- .../Payment/PaymentModel.cs | 66 ++++++++ .../PhoneAccount/PhoneLoginParams.cs | 2 +- .../WeChat/MiniProgramAccountLogin.cs | 104 ++++++++++++ .../WeChat/MiniProgramAccountParams.cs | 22 +++ .../HuanMeng.MiaoYu.Code/Users/UserBLL.cs | 72 +++++++++ .../DbSqlServer/Db_MiaoYu/MiaoYuContext.cs | 34 ++++ .../DbSqlServer/Db_MiaoYu/T_User_Data.cs | 2 +- .../Db_MiaoYu/T_User_MiniProgram_Account.cs | 43 +++++ .../Dto/Account/RequestAccountCommonUser.cs | 30 ++++ .../Dto/Account/RequestLoginModel.cs | 7 +- .../Controllers/AccountController.cs | 38 +++++ 20 files changed, 738 insertions(+), 27 deletions(-) create mode 100644 src/0-core/HuanMeng.DotNetCore/WeChat/MiniProgram.cs create mode 100644 src/0-core/HuanMeng.DotNetCore/WeChat/WXBizDataCrypt.cs create mode 100644 src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentModel.cs create mode 100644 src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/WeChat/MiniProgramAccountLogin.cs create mode 100644 src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/WeChat/MiniProgramAccountParams.cs create mode 100644 src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_MiniProgram_Account.cs create mode 100644 src/0-core/HuanMeng.MiaoYu.Model/Dto/Account/RequestAccountCommonUser.cs diff --git a/src/0-core/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj b/src/0-core/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj index 471bff9..14d5c05 100644 --- a/src/0-core/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj +++ b/src/0-core/HuanMeng.DotNetCore/HuanMeng.DotNetCore.csproj @@ -16,6 +16,7 @@ + diff --git a/src/0-core/HuanMeng.DotNetCore/WeChat/MiniProgram.cs b/src/0-core/HuanMeng.DotNetCore/WeChat/MiniProgram.cs new file mode 100644 index 0000000..a3b5328 --- /dev/null +++ b/src/0-core/HuanMeng.DotNetCore/WeChat/MiniProgram.cs @@ -0,0 +1,148 @@ +using Newtonsoft.Json; + +using StackExchange.Redis; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace HuanMeng.DotNetCore.WeChat +{ + /// + /// 小程序帮助类 + /// + /// + /// + /// + /// + public class MiniProgram(string appId, string secret, IDatabase database, IHttpClientFactory? _httpClientFactory) + { + public string AppId { get; set; } = appId; + string key = $"WeChat:{appId}"; + // Method to get user's OpenID + public async Task<(string openId, string unionid, string session_key)> GetOpenid(string code) + { + string url = $"https://api.weixin.qq.com/sns/jscode2session?appid={appId}&secret={secret}&js_code={code}&grant_type=authorization_code"; + var resUserInfo = await GetCurlData(url); + + if (resUserInfo.ContainsKey("errcode")) + { + return (null, null, null); + } + + string openid = resUserInfo.GetValueOrDefault("openid")?.ToString() ?? ""; + string unionid = resUserInfo.GetValueOrDefault("unionid")?.ToString() ?? ""; + string session_key = resUserInfo.GetValueOrDefault("session_key")?.ToString() ?? ""; + + return new(openid, unionid, session_key); + + } + + + // Get user info based on access token and openid + public async Task GetUserInfoAsync(string openid) + { + var accessToken = await GetAccessToken(); + string url = $"https://api.weixin.qq.com/sns/userinfo?access_token={accessToken}&openid={openid}&lang=zh_CN"; + var response = await GetCurlData(url); + + return response; + } + + public async Task GetAccessToken() + { + var accessTokenInfo = GetConfig(); + + if (accessTokenInfo != null) + { + return accessTokenInfo?.Access_token; + } + else + { + string url = $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={appId}&secret={secret}"; + var resAccessToken = await GetCurlData(url); + if (resAccessToken.ContainsKey("errcode")) + { + throw new Exception("获取微信token失败"); + } + + string accessToken = resAccessToken["access_token"].ToString(); + int expiresIn = Convert.ToInt32(resAccessToken["expires_in"]); + long accessTokenTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + expiresIn; + + var data = new AccessToken(accessToken, accessTokenTime); + SetConfig(data); + return accessToken; + } + } + + // Helper method for GET requests + private async Task> GetCurlData(string url) + { + using HttpClient client = _httpClientFactory.CreateClient(); + var response = await client.GetStringAsync(url); + return JsonConvert.DeserializeObject>(response); + } + + // Helper method for POST requests + private async Task> PostCurlData(string url, object data) + { + using HttpClient client = _httpClientFactory.CreateClient(); + var json = JsonConvert.SerializeObject(data); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + var response = await client.PostAsync(url, content); + var result = await response.Content.ReadAsStringAsync(); + return JsonConvert.DeserializeObject>(result); + } + + // Helper method for HTTP POST data + private async Task HttpPostData(string url, object data) + { + using HttpClient client = _httpClientFactory.CreateClient(); + var json = JsonConvert.SerializeObject(data); + var content = new StringContent(json, Encoding.UTF8, "application/json"); + var response = await client.PostAsync(url, content); + return await response.Content.ReadAsStringAsync(); + } + + private AccessToken? GetConfig() + { + + var json = database.StringGet(key); + if (!string.IsNullOrEmpty(json)) + { + return JsonConvert.DeserializeObject(json); + } + return null; + } + + private void SetConfig(AccessToken access) + { + var outTime = new TimeSpan(0, 0, 7000); + database.StringSet(key, JsonConvert.SerializeObject(access), outTime); + } + + } + + /// + /// + /// + public class AccessToken + { + public string? Access_token { get; } + public long Access_token_time { get; } + + public AccessToken(string? access_token, long access_token_time) + { + Access_token = access_token; + Access_token_time = access_token_time; + } + public AccessToken() + { + + } + } +} diff --git a/src/0-core/HuanMeng.DotNetCore/WeChat/WXBizDataCrypt.cs b/src/0-core/HuanMeng.DotNetCore/WeChat/WXBizDataCrypt.cs new file mode 100644 index 0000000..55fde7b --- /dev/null +++ b/src/0-core/HuanMeng.DotNetCore/WeChat/WXBizDataCrypt.cs @@ -0,0 +1,122 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; + +using Newtonsoft.Json.Linq; + +namespace HuanMeng.DotNetCore.WeChat +{ + + /// + /// + /// + public class WXBizDataCrypt + { + private string appId; + private string sessionKey; + + /// + /// 构造函数 + /// + /// 小程序的 appId + /// 用户在小程序登录后获取的会话密钥 + public WXBizDataCrypt(string appId, string sessionKey) + { + this.appId = appId; + this.sessionKey = sessionKey; + } + + /// + /// 检验数据的真实性,并且获取解密后的明文 + /// + /// 加密的用户数据 + /// 与用户数据一同返回的初始向量 + /// 解密后的原文 + /// 成功0,失败返回对应的错误码 + public int DecryptData(string encryptedData, string iv, out string data) + { + data = null; + + // 检查 sessionKey 长度 + if (sessionKey.Length != 24) + { + return ErrorCode.IllegalAesKey; + } + + // 检查 iv 长度 + if (iv.Length != 24) + { + return ErrorCode.IllegalIv; + } + + try + { + byte[] aesKey = Convert.FromBase64String(sessionKey); + byte[] aesIV = Convert.FromBase64String(iv); + byte[] aesCipher = Convert.FromBase64String(encryptedData); + + using (Aes aesAlg = Aes.Create()) + { + aesAlg.Key = aesKey; + aesAlg.IV = aesIV; + aesAlg.Mode = CipherMode.CBC; + aesAlg.Padding = PaddingMode.PKCS7; + + using (ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV)) + { + using (MemoryStream msDecrypt = new MemoryStream(aesCipher)) + { + using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) + { + using (StreamReader srDecrypt = new StreamReader(csDecrypt)) + { + // 解密后的明文 + data = srDecrypt.ReadToEnd(); + } + } + } + } + } + + JObject dataObj = JObject.Parse(data); + + // 检查 appId + if (dataObj["watermark"]["appid"].ToString() != this.appId) + { + return ErrorCode.IllegalBuffer; + } + + return ErrorCode.OK; + } + catch + { + return ErrorCode.IllegalBuffer; + } + } + } + + public static class ErrorCode + { + public const int OK = 0; + public const int IllegalAesKey = -41001; + public const int IllegalIv = -41002; + public const int IllegalBuffer = -41003; + } + + public class WXBizDataCryptModel + { + /// + /// + /// + public string EncryptedData; + /// + /// + /// + public string Iv; + + + public int UserId { get; set; } + } + +} diff --git a/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfig.cs b/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfig.cs index 58e2f87..8109160 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfig.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfig.cs @@ -1,4 +1,5 @@ using HuanMeng.DotNetCore.MultiTenant; +using HuanMeng.MiaoYu.Code.Payment; using System; using System.Collections.Generic; @@ -38,6 +39,11 @@ namespace HuanMeng.MiaoYu.Code.AppExtend /// 租户 /// public Guid TenantId { get; set; } + + /// + /// 项目支付数据 + /// + public PaymentModel? Payment { get; set; } } diff --git a/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfigExtendServer.cs b/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfigExtendServer.cs index e45abb2..5b26e63 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfigExtendServer.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfigExtendServer.cs @@ -1,12 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - namespace HuanMeng.MiaoYu.Code.AppExtend { /// diff --git a/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfigurationExtend.cs b/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfigurationExtend.cs index d81915d..0f43dd3 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfigurationExtend.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/AppExtend/AppConfigurationExtend.cs @@ -168,6 +168,11 @@ namespace HuanMeng.MiaoYu.Code.AppExtend newAppConfig.ConnectionString = appConfig.ConnectionString; newAppConfig.DomainName = appConfig.DomainName; newAppConfig.RedisConnectionString = appConfig.RedisConnectionString; + if (appConfig.Payment == null) + { + appConfig.Payment = new PaymentModel() { }; + } + newAppConfig.Payment = appConfig.Payment; return newAppConfig; } /// diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Base/MiaoYuBase.cs b/src/0-core/HuanMeng.MiaoYu.Code/Base/MiaoYuBase.cs index 121b0b4..b29d76c 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Base/MiaoYuBase.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Base/MiaoYuBase.cs @@ -1,33 +1,18 @@ using AutoMapper; -using HuanMeng.DotNetCore.Base; -using HuanMeng.DotNetCore.JwtInfrastructure; using HuanMeng.DotNetCore.JwtInfrastructure.Interface; -using HuanMeng.DotNetCore.MultiTenant; using HuanMeng.DotNetCore.MultiTenant.Contract; using HuanMeng.MiaoYu.Code.AppExtend; -using HuanMeng.MiaoYu.Code.Cache; using HuanMeng.MiaoYu.Code.Cache.Contract; -using HuanMeng.MiaoYu.Code.DataAccess; using HuanMeng.MiaoYu.Code.TencentUtile; -using HuanMeng.MiaoYu.Code.Users.UserAccount; using HuanMeng.MiaoYu.Code.Users.UserAccount.Contract; using HuanMeng.MiaoYu.Model.Dto; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; - using IDatabase = StackExchange.Redis.IDatabase; namespace HuanMeng.MiaoYu.Code.Base { @@ -416,4 +401,21 @@ namespace HuanMeng.MiaoYu.Code.Base } #endregion } + + public static class MiaoYuBaseExtend + { + /// + /// 微信小程序登录验证 + /// + /// + /// + public static DotNetCore.WeChat.MiniProgram GetMiniProgram(this MiaoYuBase miaoYuBase) + { + return new DotNetCore.WeChat.MiniProgram( + miaoYuBase.AppConfig.Payment?.WeChatConfig?.AppId ?? "", + miaoYuBase.AppConfig.Payment?.WeChatConfig?.AppSecret ?? "", + miaoYuBase.RedisCache, + miaoYuBase.HttpClientFactory); + } + } } diff --git a/src/0-core/HuanMeng.MiaoYu.Code/HuanMeng.MiaoYu.Code.csproj b/src/0-core/HuanMeng.MiaoYu.Code/HuanMeng.MiaoYu.Code.csproj index 2340ba4..2e2e8c4 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/HuanMeng.MiaoYu.Code.csproj +++ b/src/0-core/HuanMeng.MiaoYu.Code/HuanMeng.MiaoYu.Code.csproj @@ -27,6 +27,7 @@ + diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs b/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs index cac5059..8a0ac8e 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentExtend.cs @@ -13,6 +13,7 @@ using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings; using SKIT.FlurlHttpClient.Wechat.TenpayV3; using SKIT.FlurlHttpClient.Wechat.TenpayV3.Utilities; using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models; +using HuanMeng.MiaoYu.Code.Base; @@ -108,12 +109,30 @@ namespace HuanMeng.MiaoYu.Code.Payment /// public static IPayment GetPayment(string payment, MiaoYuBase miaoYuBase) { - + //miaoYuBase.AppConfig.Payment if (payment == "zfb") { return new AlipayPayment(AlipayConfig, miaoYuBase.TenantInfo, miaoYuBase._UserId); } - return new WeChatPayment(wxClient, weChatConfig, miaoYuBase.TenantInfo, miaoYuBase._UserId); + + if (miaoYuBase.AppConfig != null) + { + if (miaoYuBase.AppConfig.Payment == null) + { + miaoYuBase.AppConfig.Payment = new PaymentModel() + { + WeChatConfig = weChatConfig + }; + } + if (miaoYuBase.AppConfig.Payment.WeChatConfig == null) + { + miaoYuBase.AppConfig.Payment.WeChatConfig = weChatConfig; + } + } + + var _weChatConfig = miaoYuBase.AppConfig.Payment.WeChatConfig ?? weChatConfig; + var _wxClient = miaoYuBase.AppConfig.Payment.WxClient ?? wxClient; + return new WeChatPayment(_wxClient, _weChatConfig, miaoYuBase.TenantInfo, miaoYuBase._UserId); } /// diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentModel.cs b/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentModel.cs new file mode 100644 index 0000000..ecc028e --- /dev/null +++ b/src/0-core/HuanMeng.MiaoYu.Code/Payment/PaymentModel.cs @@ -0,0 +1,66 @@ +using Alipay.EasySDK.Kernel; + +using HuanMeng.MiaoYu.Code.Payment.WeChat; + +using SKIT.FlurlHttpClient.Wechat.TenpayV3; +using SKIT.FlurlHttpClient.Wechat.TenpayV3.Models; +using SKIT.FlurlHttpClient.Wechat.TenpayV3.Settings; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HuanMeng.MiaoYu.Code.Payment +{ + /// + /// + /// + public class PaymentModel + { + /// + /// 微信支付数据 + /// + public WeChatConfig? WeChatConfig { get; set; } + + /// + /// 支付宝支付数据 + /// + public Config? AlipayConfig { get; set; } + + /// + /// + /// + private WechatTenpayClient _wxClient; + /// + /// 微信支付客户端 + /// + public WechatTenpayClient? WxClient + { + get + { + if (_wxClient == null) + { + if (WeChatConfig == null) + { + return null; + } + var manager = new InMemoryCertificateManager(); + /* 仅列出必须配置项。也包含一些诸如超时时间、UserAgent 等的配置项 */ + var wechatTenpayClientOptions = new WechatTenpayClientOptions() + { + + MerchantId = WeChatConfig.MchId, + MerchantV3Secret = WeChatConfig.Key, + MerchantCertificateSerialNumber = WeChatConfig.MerchantCertificateSerialNumber, + MerchantCertificatePrivateKey = WeChatConfig.MerchantCertificatePrivateKey, + PlatformCertificateManager = manager + }; + _wxClient = new WechatTenpayClient(wechatTenpayClientOptions); + } + return _wxClient; + } + } + } +} diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/PhoneAccount/PhoneLoginParams.cs b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/PhoneAccount/PhoneLoginParams.cs index 035effe..dce38df 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/PhoneAccount/PhoneLoginParams.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/PhoneAccount/PhoneLoginParams.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace HuanMeng.MiaoYu.Code.Users.UserAccount.PhoneAccount { /// - /// 登录参数 + /// 登录参数 /// public class PhoneLoginParams : BaseLoginParams { diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/WeChat/MiniProgramAccountLogin.cs b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/WeChat/MiniProgramAccountLogin.cs new file mode 100644 index 0000000..f1deb95 --- /dev/null +++ b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/WeChat/MiniProgramAccountLogin.cs @@ -0,0 +1,104 @@ +using HuanMeng.MiaoYu.Code.Users.UserAccount.Contract; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using HuanMeng.DotNetCore.WeChat; +using HuanMeng.MiaoYu.Code.Users.UserAccount.PhoneAccount; +namespace HuanMeng.MiaoYu.Code.Users.UserAccount.WeChat +{ + /// + /// PhoneLoginParams + /// + public class MiniProgramAccountLogin(MiniProgram miniProgram, DAO dao) : IUserAccount + { + public override async Task LoginAsync(BaseLoginParams loginParams) + { + var login = loginParams as MiniProgramAccountParams; + if (login == null) + { + throw new ArgumentNullException("登录方式错误"); + } + if (string.IsNullOrEmpty(login.Code)) + { + throw new ArgumentNullException("小程序code不能为空"); + } + (string openId, string unionid, string session_key) = await miniProgram.GetOpenid(login.Code); + if (openId == null) + { + throw new ArgumentNullException("小程序登录失败"); + } + var account = dao.daoDbMiaoYu.context.T_User_MiniProgram_Account.FirstOrDefault(it => it.OpenId == openId); T_User? user = null; + if (account == null) + { + user = new T_User() + { + UpdatedAt = DateTime.Now, + CreatedAt = DateTime.Now, + IsActive = true, + LastLoginAt = DateTime.Now, + LastLoginTypeAt = 2, + Email = "", + NickName = "微信用户", + PhoneNum = "", + RegisterType = 2, + TenantId = dao.daoDbMiaoYu.context.TenantInfo.TenantId, + UserName = "微信用户", + State = 0, + + }; + dao.daoDbMiaoYu.context.T_User.Add(user); + dao.daoDbMiaoYu.context.SaveChanges(); + account = new T_User_MiniProgram_Account() + { + CreateAt = DateTime.Now, + OpenId = openId, + SessionKey = session_key, + Unionid = unionid, + UpdateAt = DateTime.Now, + UserId = user.Id, + + + }; + dao.daoDbMiaoYu.context.Add(account); + dao.daoDbMiaoYu.context.SaveChanges(); + } + else + { + user = await dao.daoDbMiaoYu.context.T_User.FirstOrDefaultAsync(it => it.Id == account.UserId); + + } + //WXBizDataCrypt wXBizDataCrypt = new WXBizDataCrypt(miniProgram.AppId, session_key); + account.UpdateAt = DateTime.Now; + account.SessionKey = session_key; + if (user == null) + { + user = new T_User() + { + UpdatedAt = DateTime.Now, + CreatedAt = DateTime.Now, + IsActive = true, + LastLoginAt = DateTime.Now, + LastLoginTypeAt = 2, + Email = "", + NickName = "微信用户", + PhoneNum = "", + RegisterType = 2, + TenantId = dao.daoDbMiaoYu.context.TenantInfo.TenantId, + UserName = "微信用户", + State = 0, + + }; + dao.daoDbMiaoYu.context.T_User.Add(user); + } + dao.daoDbMiaoYu.context.SaveChanges(); + return new LoginAccountInfo() + { + NickName = user.NickName, + UserId = user.Id, + }; + } + } +} diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/WeChat/MiniProgramAccountParams.cs b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/WeChat/MiniProgramAccountParams.cs new file mode 100644 index 0000000..90e9ed3 --- /dev/null +++ b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserAccount/WeChat/MiniProgramAccountParams.cs @@ -0,0 +1,22 @@ +using HuanMeng.MiaoYu.Code.Users.UserAccount.Contract; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HuanMeng.MiaoYu.Code.Users.UserAccount.WeChat +{ + /// + /// + /// + public class MiniProgramAccountParams : BaseLoginParams + { + /// + /// 微信小程序code + /// + public string Code { get; set; } + } +} + diff --git a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserBLL.cs b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserBLL.cs index f176682..9861560 100644 --- a/src/0-core/HuanMeng.MiaoYu.Code/Users/UserBLL.cs +++ b/src/0-core/HuanMeng.MiaoYu.Code/Users/UserBLL.cs @@ -1,9 +1,11 @@ using HuanMeng.DotNetCore.Base; +using HuanMeng.DotNetCore.WeChat; using HuanMeng.MiaoYu.Code.Base; using HuanMeng.MiaoYu.Code.DataAccess; using HuanMeng.MiaoYu.Code.Users.UserAccount; using HuanMeng.MiaoYu.Code.Users.UserAccount.Contract; using HuanMeng.MiaoYu.Code.Users.UserAccount.PhoneAccount; +using HuanMeng.MiaoYu.Code.Users.UserAccount.WeChat; using HuanMeng.MiaoYu.Model.Dto; using HuanMeng.MiaoYu.Model.Dto.Account; using HuanMeng.MiaoYu.Model.Dto.Character; @@ -18,6 +20,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; +using System.Data.SqlTypes; using System.Linq; using System.Security.Claims; using System.Text; @@ -82,6 +85,14 @@ namespace HuanMeng.MiaoYu.Code.Users Ip = ip }; } + else if (requestLoginModel.LoginType == 2) + {// + userAccount = new MiniProgramAccountLogin(this.GetMiniProgram(), Dao); + loginParams = new MiniProgramAccountParams() + { + Code = requestLoginModel.Code, + }; + } else { throw new Exception("不支持的登录方式"); @@ -163,6 +174,66 @@ namespace HuanMeng.MiaoYu.Code.Users }); } + + /// + /// + /// + /// + /// + public async Task GetMiniProgramUserInfo(WXBizDataCryptModel wXBizDataCryptModel) + { + var appId = this.AppConfig.Payment?.WeChatConfig?.AppId ?? ""; + var account = Dao.daoDbMiaoYu.context.T_User_MiniProgram_Account.FirstOrDefault(it => it.UserId == wXBizDataCryptModel.UserId); + WXBizDataCrypt wXBizDataCrypt = new WXBizDataCrypt(appId, account.SessionKey ?? ""); + var t = wXBizDataCrypt.DecryptData(wXBizDataCryptModel.EncryptedData, wXBizDataCryptModel.Iv, out var data); + if (t == 0) + { + var jsonData = JsonConvert.DeserializeObject(data); + if (jsonData?["nickName"] != null) + { + string nickName = jsonData?["nickName"]?.ToString(); + } + } + return data; + } + + /// + /// 修改用户昵称 + /// + /// + /// + /// + public async Task> UpdateUserNickName(string nickName) + { + var user = Dao.daoDbMiaoYu.context.T_User.FirstOrDefault(it => it.Id == _UserId); + if (user == null) + { + throw new Exception("用户不存在"); + } + user.NickName = nickName; + await Dao.daoDbMiaoYu.context.SaveChangesAsync(); + string sqlString = $@"update M_Songs set AuthorName='{nickName}' where AuthorId={_UserId}"; + await Dao.daoDbMiaoYu.context.Database.ExecuteSqlRawAsync(sqlString); + return new BaseResponse(ResonseCode.Success, "", true); + } + + /// + /// 修改用户头像 + /// + /// + /// + /// + public async Task> UpdateUserIcon(string userIcon) + { + var user = Dao.daoDbMiaoYu.context.T_User.FirstOrDefault(it => it.Id == _UserId); + if (user == null) + { + throw new Exception("用户不存在"); + } + user.UserIconUrl = userIcon; + await Dao.daoDbMiaoYu.context.SaveChangesAsync(); + return new BaseResponse(ResonseCode.Success, "", true); + } /// /// 交易记录 /// @@ -191,6 +262,7 @@ namespace HuanMeng.MiaoYu.Code.Users } return new BaseResponse>(ResonseCode.Success, "", list); } + /// /// 获取未使用的记忆卡列表 /// diff --git a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/MiaoYuContext.cs b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/MiaoYuContext.cs index 6376f2c..9539c80 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/MiaoYuContext.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/MiaoYuContext.cs @@ -213,6 +213,11 @@ public partial class MiaoYuContext : MultiTenantDbContext//DbContext /// public virtual DbSet T_User_MemoryCard { get; set; } + /// + /// 小程序登录表 + /// + public virtual DbSet T_User_MiniProgram_Account { get; set; } + /// /// 手机号登录表 /// @@ -1291,6 +1296,35 @@ public partial class MiaoYuContext : MultiTenantDbContext//DbContext } }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__T_User_M__3214EC073889A8B9"); + + entity.ToTable(tb => tb.HasComment("小程序登录表")); + + entity.Property(e => e.Id).HasComment("主键"); + entity.Property(e => e.CreateAt) + .HasComment("创建时间") + .HasColumnType("datetime"); + entity.Property(e => e.OpenId) + .HasMaxLength(200) + .HasComment("用户唯一标识"); + entity.Property(e => e.SessionKey).HasMaxLength(200); + entity.Property(e => e.TenantId).HasComment("租户"); + entity.Property(e => e.Unionid) + .HasMaxLength(200) + .HasComment("用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台账号下会返回,详见"); + entity.Property(e => e.UpdateAt) + .HasComment("修改时间") + .HasColumnType("datetime"); + entity.Property(e => e.UserId).HasComment("用户Id"); + //添加全局筛选器 + if (this.TenantInfo != null) + { + entity.HasQueryFilter(it => it.TenantId == this.TenantInfo.TenantId); + } + }); + modelBuilder.Entity(entity => { entity.HasKey(e => e.Id).HasName("PK__T_User_P__3214EC07987BDDB2"); diff --git a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_Data.cs b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_Data.cs index 7202cb5..370ac8b 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_Data.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_Data.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace HuanMeng.MiaoYu.Model.DbSqlServer.Db_MiaoYu; diff --git a/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_MiniProgram_Account.cs b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_MiniProgram_Account.cs new file mode 100644 index 0000000..2dd67d1 --- /dev/null +++ b/src/0-core/HuanMeng.MiaoYu.Model/DbSqlServer/Db_MiaoYu/T_User_MiniProgram_Account.cs @@ -0,0 +1,43 @@ +using System; + +namespace HuanMeng.MiaoYu.Model.DbSqlServer.Db_MiaoYu; + +/// +/// 小程序登录表 +/// +public partial class T_User_MiniProgram_Account: MultiTenantEntity +{ + /// + /// 主键 + /// + public virtual int Id { get; set; } + + public override Guid TenantId { get; set; } + + /// + /// 用户Id + /// + public virtual int UserId { get; set; } + + /// + /// 用户唯一标识 + /// + public virtual string OpenId { get; set; } = null!; + + /// + /// 用户在开放平台的唯一标识符,若当前小程序已绑定到微信开放平台账号下会返回,详见 + /// + public virtual string? Unionid { get; set; } + + public virtual string? SessionKey { get; set; } + + /// + /// 创建时间 + /// + public virtual DateTime CreateAt { get; set; } + + /// + /// 修改时间 + /// + public virtual DateTime UpdateAt { get; set; } +} diff --git a/src/0-core/HuanMeng.MiaoYu.Model/Dto/Account/RequestAccountCommonUser.cs b/src/0-core/HuanMeng.MiaoYu.Model/Dto/Account/RequestAccountCommonUser.cs new file mode 100644 index 0000000..acc2439 --- /dev/null +++ b/src/0-core/HuanMeng.MiaoYu.Model/Dto/Account/RequestAccountCommonUser.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace HuanMeng.MiaoYu.Model.Dto.Account +{ + /// + /// + /// + public class RequestAccountCommonUser + { + /// + /// + /// + public string NickName { get; set; } + } + + /// + /// + /// + public class RequestAccountCommonUserImage + { + /// + /// + /// + public string UserIcon { get; set; } + } +} diff --git a/src/0-core/HuanMeng.MiaoYu.Model/Dto/Account/RequestLoginModel.cs b/src/0-core/HuanMeng.MiaoYu.Model/Dto/Account/RequestLoginModel.cs index 208fc4d..3895711 100644 --- a/src/0-core/HuanMeng.MiaoYu.Model/Dto/Account/RequestLoginModel.cs +++ b/src/0-core/HuanMeng.MiaoYu.Model/Dto/Account/RequestLoginModel.cs @@ -17,7 +17,7 @@ namespace HuanMeng.MiaoYu.Model.Dto.Account public string? VerificationCode { get; set; } /// - /// 登录类型,0token登录 1手机号,2邮箱 + /// 登录类型,0token登录 1手机号,2微信登录 /// public int LoginType { get; set; } @@ -25,5 +25,10 @@ namespace HuanMeng.MiaoYu.Model.Dto.Account /// token登录 /// public string? Token { get; set; } + + /// + /// 微信小程序登录Code + /// + public string? Code { get; set; } } } diff --git a/src/2-api/HuanMeng.MiaoYu.WebApi/Controllers/AccountController.cs b/src/2-api/HuanMeng.MiaoYu.WebApi/Controllers/AccountController.cs index 6f66dfe..cb2ff59 100644 --- a/src/2-api/HuanMeng.MiaoYu.WebApi/Controllers/AccountController.cs +++ b/src/2-api/HuanMeng.MiaoYu.WebApi/Controllers/AccountController.cs @@ -2,6 +2,7 @@ using Azure; using HuanMeng.DotNetCore.Base; using HuanMeng.DotNetCore.Utility; +using HuanMeng.DotNetCore.WeChat; using HuanMeng.MiaoYu.Code.Mall; using HuanMeng.MiaoYu.Code.Other; using HuanMeng.MiaoYu.Code.Users; @@ -76,6 +77,18 @@ namespace HuanMeng.MiaoYu.WebApi.Controllers UserBLL userBLL = new UserBLL(ServiceProvider); return await userBLL.GetUserInfo(); } + /// + /// 解析微信用户数据 + /// + /// + [AllowAnonymous] + [HttpPost] + public async Task GetMiniProgramUserInfo([FromBody] WXBizDataCryptModel wXBizDataCryptModel) + { + UserBLL userBLL = new UserBLL(ServiceProvider); + return await userBLL.GetMiniProgramUserInfo(wXBizDataCryptModel); + } + /// /// 我的账户 @@ -98,7 +111,32 @@ namespace HuanMeng.MiaoYu.WebApi.Controllers { UserBLL userBLL = new UserBLL(ServiceProvider); return await userBLL.GetTransactionRecords(); + } + /// + /// 修改用户昵称 + /// + /// + /// + /// + [HttpPost] + public async Task> UpdateUserNickName([FromBody] RequestAccountCommonUser requestAccountCommonUser) + { + UserBLL userBLL = new UserBLL(ServiceProvider); + return await userBLL.UpdateUserNickName(requestAccountCommonUser.NickName); + } + + /// + /// 修改用户头像 + /// + /// + /// + /// + [HttpPost] + public async Task> UpdateUserIcon([FromBody] RequestAccountCommonUserImage requestAccountCommonUserImage) + { + UserBLL userBLL = new UserBLL(ServiceProvider); + return await userBLL.UpdateUserIcon(requestAccountCommonUserImage.UserIcon); }