using BPA.Kitchen.Core; using BPA.KitChen.GroupMealOrder.Core.CacheOption; using BPA.KitChen.WeChat.WechatServer.Dtos; using BPA.KitChen.WeChat.WechatServer.Service; using Furion; using Furion.DependencyInjection; using Furion.FriendlyException; using Furion.JsonSerialization; using Furion.RemoteRequest.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Memory; using System.Text; namespace Black_B_Project.Application.WechatServer.Service { /// /// 微信小程序服务端方法 /// public class WechatService : IWechatService, ITransient { private readonly IMemoryCache _memoryCache; private readonly IHttpContextAccessor _httpContextAccessor; private readonly IDistributedCache _redisClient; public WechatService(IMemoryCache memoryCache, IHttpContextAccessor httpContextAccessor, IDistributedCache redisClient) { _memoryCache = memoryCache; _httpContextAccessor = httpContextAccessor; _redisClient = redisClient; } /// /// 解密获取用户完整信息 /// /// public WeChatUserInfo Decryptuserinfo(DecryptVM decryptVM) { WeChatUserInfo info = new(); try { AESHelper.AesKey = decryptVM.session_key; AESHelper.AesIV = decryptVM.aesiv; string decrstr = AESHelper.AESDecrypt(decryptVM.encryptedData); info = JSON.Deserialize(decrstr); } catch (Exception) { //BPALog.WriteLog("解析用户信息失败", LogEnum.Error, param: info); throw Oops.Oh("解析失败,session_key已过期"); } return info; } /// /// 用户登录,获取openid /// /// /// public WeChatUserInfo WxLogin(DecryptVM vm) { var appid = _httpContextAccessor.HttpContext.Request.Headers["AppId"].ToString(); var config = ApolloApplication.Configs.Find(x => x.Key == appid); if (config == null) { throw Oops.Oh("AppId不能为空!"); } WeChatUserInfo userinfo = new(); if (!string.IsNullOrEmpty(vm.code)) { var opt = config.WechatConfig; var apiUrl = $"https://api.weixin.qq.com/sns/jscode2session?appid={opt.Appid}&secret={opt.AppSecret}&js_code={vm.code}&grant_type=authorization_code"; var response = apiUrl.GetAsStringAsync().Result; //todo 记录openID进入数据库 var loginData = JSON.Deserialize(response); if (loginData.session_key == null) { throw Oops.Oh($"登录失败,请重试!{response}"); } vm.session_key = loginData.session_key; vm.openId = loginData.openid; userinfo.openId = loginData.openid; userinfo.session_key = loginData.session_key; userinfo.TmplIds.Add(config.WechatConfig.AppMessagetTakeAway); return userinfo; } else { throw Oops.Oh($"登录失败,请重试!"); } } /// /// 获取微信token /// /// [AllowAnonymous] public async Task AccessToken(string appid) { var config = ApolloApplication.Configs.Find(x => x.Key == appid); //过期时间 string tokenExpiresName = $"{appid}_TokenExpires"; //数据 string accessTokenName = $"{appid}_AccessToken"; var expiresTimeout = await _redisClient.GetStringAsync(tokenExpiresName); //判断是否存在缓存 if (expiresTimeout == null) { var opt = config.WechatConfig; var response = await $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={opt.Appid}&secret={opt.AppSecret}".GetAsStringAsync(); WechatToken res = JSON.Deserialize(response); if (!string.IsNullOrEmpty(res.access_token)) { DistributedCacheEntryOptions distributedCacheEntryOptions = new(); //Token await _redisClient.SetStringAsync(accessTokenName, res.access_token, distributedCacheEntryOptions); //存入当前时间 var currtime = DateTime.Now; await _redisClient.SetStringAsync(tokenExpiresName, currtime.ToString(), distributedCacheEntryOptions); return res.access_token; } } //存在对比是否过期 TimeSpan ts = DateTime.Now - Convert.ToDateTime(expiresTimeout); //如果总秒数大于6500秒,那么视为过期,重新获取 if (ts.TotalSeconds >= 6500) { var opt = config.WechatConfig; var response = await $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={opt.Appid}&secret={opt.AppSecret}".GetAsStringAsync(); WechatToken res = JSON.Deserialize(response); if (!string.IsNullOrEmpty(res.access_token)) { DistributedCacheEntryOptions distributedCacheEntryOptions = new(); //Token await _redisClient.SetStringAsync(accessTokenName, res.access_token, distributedCacheEntryOptions); //存入当前时间 var currtime = DateTime.Now; await _redisClient.SetStringAsync(tokenExpiresName, currtime.ToString(), distributedCacheEntryOptions); return res.access_token; } } else { //没过期就取 var wechatToken = await _redisClient.GetStringAsync(accessTokenName); return wechatToken; } return null; } /// /// 发送预定通知 /// /// /// public async Task SendReserveMessage(ReserveInput input) { var config = ApolloApplication.Configs.Find(x => x.Key == input.AppId); try { var param = new { touser = input.OpenId, template_id = config.WechatConfig.AppMessagetTakeAway, page = "pages/homeNew/homeNew", data = new { time1 = new { value = input.ReserveTime }, thing2 = new { value = input.ReserveWarning }, } }; var access_token = await AccessToken(input.AppId); var res = JSON.Serialize(param); var response = await $"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={access_token}".SetBody(res).PostAsStringAsync(); } catch (Exception ex) { return false; } return true; } /// /// 获取手机号 /// /// /// public async Task WxPhone(string code) { var appid = _httpContextAccessor.HttpContext.Request.Headers["AppId"].ToString(); if (string.IsNullOrEmpty(code)) { throw Oops.Bah("code不能为空"); } if (string.IsNullOrEmpty(appid)) { throw Oops.Bah("appid不能为空"); } var access_token = await AccessToken(appid); var respones = await $"https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={access_token}".SetBody(new { code }).PostAsStringAsync(); WechatPhoneMsg wechatPhone = JSON.Deserialize(respones); return wechatPhone; } /// /// 发送服务号 /// /// public async Task Uniform_send() { var token = _memoryCache.Get("weToken"); if (token == null) { await GetWechatTokenAsync(); token = _memoryCache.Get("weToken"); } var response = await $"https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token={token.ToString()}".PostAsStreamAsync(); StreamReader readStream = new StreamReader(response.Stream, Encoding.UTF8); var data = readStream.ReadToEnd(); return data; } /// /// 获取微信token /// /// public async Task GetWechatTokenAsync() { var opt = App.GetOptions(); var response = await $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={opt.Appid}&secret={opt.AppSecret}".GetAsStreamAsync(); StreamReader readStream = new StreamReader(response.Stream, Encoding.UTF8); var data = readStream.ReadToEnd(); var res = JSON.Deserialize(data); _memoryCache.Set("weToken", res.access_token); return _memoryCache.Get("weToken").ToString(); } /// /// 用户支付完成后,获取该用户的 UnionId,无需用户授权。本接口支持第三方平台代理查询。 /// /// /// /// public async Task GetPaidunionid(string token, string openid) { var response = await $"https://api.weixin.qq.com/wxa/getpaidunionid?access_token={token}&openid={openid}".GetAsStreamAsync(); StreamReader readStream = new StreamReader(response.Stream, Encoding.UTF8); return readStream.ReadToEnd(); } } }