团餐订单
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

254 lines
10 KiB

  1. using BPA.Kitchen.Core;
  2. using BPA.KitChen.GroupMealOrder.Core.CacheOption;
  3. using BPA.KitChen.WeChat.WechatServer.Dtos;
  4. using BPA.KitChen.WeChat.WechatServer.Service;
  5. using Furion;
  6. using Furion.DependencyInjection;
  7. using Furion.FriendlyException;
  8. using Furion.JsonSerialization;
  9. using Furion.RemoteRequest.Extensions;
  10. using Microsoft.AspNetCore.Authorization;
  11. using Microsoft.AspNetCore.Http;
  12. using Microsoft.Extensions.Caching.Distributed;
  13. using Microsoft.Extensions.Caching.Memory;
  14. using System.Text;
  15. namespace Black_B_Project.Application.WechatServer.Service
  16. {
  17. /// <summary>
  18. /// 微信小程序服务端方法
  19. /// </summary>
  20. public class WechatService : IWechatService, ITransient
  21. {
  22. private readonly IMemoryCache _memoryCache;
  23. private readonly IHttpContextAccessor _httpContextAccessor;
  24. private readonly IDistributedCache _redisClient;
  25. public WechatService(IMemoryCache memoryCache, IHttpContextAccessor httpContextAccessor, IDistributedCache redisClient)
  26. {
  27. _memoryCache = memoryCache;
  28. _httpContextAccessor = httpContextAccessor;
  29. _redisClient = redisClient;
  30. }
  31. /// <summary>
  32. /// 解密获取用户完整信息
  33. /// </summary>
  34. /// <returns></returns>
  35. public WeChatUserInfo Decryptuserinfo(DecryptVM decryptVM)
  36. {
  37. WeChatUserInfo info = new();
  38. try
  39. {
  40. AESHelper.AesKey = decryptVM.session_key;
  41. AESHelper.AesIV = decryptVM.aesiv;
  42. string decrstr = AESHelper.AESDecrypt(decryptVM.encryptedData);
  43. info = JSON.Deserialize<WeChatUserInfo>(decrstr);
  44. }
  45. catch (Exception)
  46. {
  47. //BPALog.WriteLog("解析用户信息失败", LogEnum.Error, param: info);
  48. throw Oops.Oh("解析失败,session_key已过期");
  49. }
  50. return info;
  51. }
  52. /// <summary>
  53. /// 用户登录,获取openid
  54. /// </summary>
  55. /// <param name="vm"></param>
  56. /// <returns></returns>
  57. public WeChatUserInfo WxLogin(DecryptVM vm)
  58. {
  59. var appid = _httpContextAccessor.HttpContext.Request.Headers["AppId"].ToString();
  60. var config = ApolloApplication.Configs.Find(x => x.Key == appid);
  61. if (config == null)
  62. {
  63. throw Oops.Oh("AppId不能为空!");
  64. }
  65. WeChatUserInfo userinfo = new();
  66. if (!string.IsNullOrEmpty(vm.code))
  67. {
  68. var opt = config.WechatConfig;
  69. var apiUrl = $"https://api.weixin.qq.com/sns/jscode2session?appid={opt.Appid}&secret={opt.AppSecret}&js_code={vm.code}&grant_type=authorization_code";
  70. var response = apiUrl.GetAsStringAsync().Result;
  71. //todo 记录openID进入数据库
  72. var loginData = JSON.Deserialize<WeChatLoginModel>(response);
  73. if (loginData.session_key == null)
  74. {
  75. throw Oops.Oh($"登录失败,请重试!{response}");
  76. }
  77. vm.session_key = loginData.session_key;
  78. vm.openId = loginData.openid;
  79. userinfo.openId = loginData.openid;
  80. userinfo.session_key = loginData.session_key;
  81. userinfo.TmplIds.Add(config.WechatConfig.AppMessagetTakeAway);
  82. return userinfo;
  83. }
  84. else
  85. {
  86. throw Oops.Oh($"登录失败,请重试!");
  87. }
  88. }
  89. /// <summary>
  90. /// 获取微信token
  91. /// </summary>
  92. /// <returns></returns>
  93. [AllowAnonymous]
  94. public async Task<string> AccessToken(string appid)
  95. {
  96. var config = ApolloApplication.Configs.Find(x => x.Key == appid);
  97. //过期时间
  98. string tokenExpiresName = $"{appid}_TokenExpires";
  99. //数据
  100. string accessTokenName = $"{appid}_AccessToken";
  101. var expiresTimeout = await _redisClient.GetStringAsync(tokenExpiresName);
  102. //判断是否存在缓存
  103. if (expiresTimeout == null)
  104. {
  105. var opt = config.WechatConfig;
  106. var response = await $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={opt.Appid}&secret={opt.AppSecret}".GetAsStringAsync();
  107. WechatToken res = JSON.Deserialize<WechatToken>(response);
  108. if (!string.IsNullOrEmpty(res.access_token))
  109. {
  110. DistributedCacheEntryOptions distributedCacheEntryOptions = new();
  111. //Token
  112. await _redisClient.SetStringAsync(accessTokenName, res.access_token, distributedCacheEntryOptions);
  113. //存入当前时间
  114. var currtime = DateTime.Now;
  115. await _redisClient.SetStringAsync(tokenExpiresName, currtime.ToString(), distributedCacheEntryOptions);
  116. return res.access_token;
  117. }
  118. }
  119. //存在对比是否过期
  120. TimeSpan ts = DateTime.Now - Convert.ToDateTime(expiresTimeout);
  121. //如果总秒数大于6500秒,那么视为过期,重新获取
  122. if (ts.TotalSeconds >= 6500)
  123. {
  124. var opt = config.WechatConfig;
  125. var response = await $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={opt.Appid}&secret={opt.AppSecret}".GetAsStringAsync();
  126. WechatToken res = JSON.Deserialize<WechatToken>(response);
  127. if (!string.IsNullOrEmpty(res.access_token))
  128. {
  129. DistributedCacheEntryOptions distributedCacheEntryOptions = new();
  130. //Token
  131. await _redisClient.SetStringAsync(accessTokenName, res.access_token, distributedCacheEntryOptions);
  132. //存入当前时间
  133. var currtime = DateTime.Now;
  134. await _redisClient.SetStringAsync(tokenExpiresName, currtime.ToString(), distributedCacheEntryOptions);
  135. return res.access_token;
  136. }
  137. }
  138. else
  139. {
  140. //没过期就取
  141. var wechatToken = await _redisClient.GetStringAsync(accessTokenName);
  142. return wechatToken;
  143. }
  144. return null;
  145. }
  146. /// <summary>
  147. /// 发送预定通知
  148. /// </summary>
  149. /// <param name="input"></param>
  150. /// <returns></returns>
  151. public async Task<bool> SendReserveMessage(ReserveInput input)
  152. {
  153. var config = ApolloApplication.Configs.Find(x => x.Key == input.AppId);
  154. try
  155. {
  156. var param = new
  157. {
  158. touser = input.OpenId,
  159. template_id = config.WechatConfig.AppMessagetTakeAway,
  160. page = "pages/homeNew/homeNew",
  161. data = new
  162. {
  163. time1 = new { value = input.ReserveTime },
  164. thing2 = new { value = input.ReserveWarning },
  165. }
  166. };
  167. var access_token = await AccessToken(input.AppId);
  168. var res = JSON.Serialize(param);
  169. var response = await $"https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={access_token}".SetBody(res).PostAsStringAsync();
  170. }
  171. catch (Exception ex)
  172. {
  173. return false;
  174. }
  175. return true;
  176. }
  177. /// <summary>
  178. /// 获取手机号
  179. /// </summary>
  180. /// <param name="code"></param>
  181. /// <returns></returns>
  182. public async Task<WechatPhoneMsg> WxPhone(string code)
  183. {
  184. var appid = _httpContextAccessor.HttpContext.Request.Headers["AppId"].ToString();
  185. if (string.IsNullOrEmpty(code))
  186. {
  187. throw Oops.Bah("code不能为空");
  188. }
  189. if (string.IsNullOrEmpty(appid))
  190. {
  191. throw Oops.Bah("appid不能为空");
  192. }
  193. var access_token = await AccessToken(appid);
  194. var respones = await $"https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token={access_token}".SetBody(new
  195. {
  196. code
  197. }).PostAsStringAsync();
  198. WechatPhoneMsg wechatPhone = JSON.Deserialize<WechatPhoneMsg>(respones);
  199. return wechatPhone;
  200. }
  201. /// <summary>
  202. /// 发送服务号
  203. /// </summary>
  204. /// <returns></returns>
  205. public async Task<string> Uniform_send()
  206. {
  207. var token = _memoryCache.Get("weToken");
  208. if (token == null)
  209. {
  210. await GetWechatTokenAsync();
  211. token = _memoryCache.Get("weToken");
  212. }
  213. var response = await $"https://api.weixin.qq.com/cgi-bin/message/wxopen/template/uniform_send?access_token={token.ToString()}".PostAsStreamAsync();
  214. StreamReader readStream = new StreamReader(response.Stream, Encoding.UTF8);
  215. var data = readStream.ReadToEnd();
  216. return data;
  217. }
  218. /// <summary>
  219. /// 获取微信token
  220. /// </summary>
  221. /// <returns></returns>
  222. public async Task<string> GetWechatTokenAsync()
  223. {
  224. var opt = App.GetOptions<WechatConfigOptions>();
  225. var response = await $"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={opt.Appid}&secret={opt.AppSecret}".GetAsStreamAsync();
  226. StreamReader readStream = new StreamReader(response.Stream, Encoding.UTF8);
  227. var data = readStream.ReadToEnd();
  228. var res = JSON.Deserialize<WeChatTokenModel>(data);
  229. _memoryCache.Set("weToken", res.access_token);
  230. return _memoryCache.Get("weToken").ToString();
  231. }
  232. /// <summary>
  233. /// 用户支付完成后,获取该用户的 UnionId,无需用户授权。本接口支持第三方平台代理查询。
  234. /// </summary>
  235. /// <param name="token"></param>
  236. /// <param name="openid"></param>
  237. /// <returns></returns>
  238. public async Task<string> GetPaidunionid(string token, string openid)
  239. {
  240. var response = await $"https://api.weixin.qq.com/wxa/getpaidunionid?access_token={token}&openid={openid}".GetAsStreamAsync();
  241. StreamReader readStream = new StreamReader(response.Stream, Encoding.UTF8);
  242. return readStream.ReadToEnd();
  243. }
  244. }
  245. }