TransactionsService.cs 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916
  1. using Fuel.Core.Transactions.Dto;
  2. using Microsoft.AspNetCore.Http;
  3. using Newtonsoft.Json;
  4. using FuelServer.Core.Entity;
  5. using System.Linq.Expressions;
  6. using Fuel.Core.Models;
  7. using System.Net;
  8. using Fuel.Payment.Service.Pay;
  9. using DFS.Core.Abstractions.View;
  10. using Fuel.Core;
  11. using System;
  12. using System.Transactions;
  13. using Fuel.Core.WechatServer;
  14. using Fuel.Core.Nozzle.Dto;
  15. using Fuel.Application.MqttService;
  16. using Org.BouncyCastle.Asn1.X509;
  17. using Jayrock.Json;
  18. using Fuel.Core.Transactions;
  19. using Microsoft.Extensions.DependencyInjection;
  20. using static Fuel.Core.WechatServer.WeChatService;
  21. using System.Linq.Dynamic.Core;
  22. using System.Text.Json;
  23. using Org.BouncyCastle.Asn1.Ocsp;
  24. namespace Fuel.Application.Service
  25. {
  26. public class TransactionsService : ITransactionsService
  27. {
  28. private readonly EntityHelper _entityHelper;
  29. private readonly IHttpContextAccessor _httpContextAccessor;
  30. private readonly IPayService _payService;
  31. public readonly IFreeSql _fsql;
  32. private readonly IMqttClientService _mqttService;
  33. private readonly IServiceScopeFactory _serviceScopeFactory;
  34. static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.xml").GetLogger("Main");
  35. public TransactionsService(EntityHelper entityHelper, IHttpContextAccessor httpContextAccessor, IPayService payService, IFreeSql fsql, IMqttClientService mqttService, IServiceScopeFactory serviceScopeFactory)
  36. {
  37. _entityHelper = entityHelper;
  38. _httpContextAccessor = httpContextAccessor;
  39. _payService = payService;
  40. _fsql = fsql;
  41. _mqttService = mqttService;
  42. _serviceScopeFactory = serviceScopeFactory;
  43. }
  44. /// <summary>
  45. /// 创建订单
  46. /// </summary>
  47. /// <param name="uploadTransactions"></param>
  48. /// <returns></returns>
  49. public async Task<ServiceResponse> CreateTransactions(UploadTransactions uploadTransactions)
  50. {
  51. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  52. var site = _fsql.Select<businessunitinfo>().Where(_ => _.Buid == Buid).First();
  53. if (site == null)
  54. {
  55. return ServiceResponse.Error("站点信息查询为空 buid: " + Buid);
  56. }
  57. string key = string.Empty;
  58. var _product = await _entityHelper.GetEntitiesAsync<product>(_ => _.Buid == Buid && _.ProductName == uploadTransactions.Product);
  59. if (_product.Count() <= 0)
  60. {
  61. return ServiceResponse.Error("油品查询失败,油品名称:" + uploadTransactions.Product);
  62. }
  63. var _nozzle = await _entityHelper.GetEntitiesAsync<nozzle>(_ => _.Buid == Buid && _.ExternalGunNumber == uploadTransactions.ExternalGunNumber);
  64. if (_nozzle.Count() <= 0)
  65. {
  66. return ServiceResponse.Error("油枪查询失败,油枪名称:" + uploadTransactions.ExternalGunNumber);
  67. }
  68. if (site.PaymentMode == 1)//预支付
  69. {
  70. key = uploadTransactions.ExternalGunNumber + ":" +
  71. uploadTransactions.OriginalAmount + ":" +
  72. uploadTransactions.Qty + ":" +
  73. uploadTransactions.MiniProgramID + ":" +
  74. Buid.ToString();
  75. string WachatID = HttpRequestReader.GetWachatID(); //用户
  76. var userSession = WechatUserSessionRepo.GetUserSession(WachatID.ToString());
  77. if (userSession == null)
  78. {
  79. return ServiceResponse.Error(HttpStatusCode.NonAuthoritativeInformation, "未找到用户!");
  80. }
  81. var user = _fsql.Select<miniprogramusers>().Where(_ => _.OpenId == userSession.openid).First();
  82. if (uploadTransactions.OriginalAmount != null)
  83. {
  84. decimal decimalValue = uploadTransactions.OriginalAmount.HasValue ? uploadTransactions.OriginalAmount.Value : 0m;
  85. decimal ProductPrice = _product.First().ProductPrice.HasValue ? _product.First().ProductPrice.Value : 0m;
  86. decimal qty = decimalValue / ProductPrice;
  87. uploadTransactions.Qty = qty;
  88. //订单金额不能小于2元、订单金额不能大于9900元
  89. if (uploadTransactions.OriginalAmount < 2 || uploadTransactions.OriginalAmount > 9900)
  90. {
  91. return ServiceResponse.Error(HttpStatusCode.NonAuthoritativeInformation, "订单金额不能小于2元、订单金额不能大于9900元");
  92. }
  93. }
  94. else if (uploadTransactions.Qty != null)
  95. {
  96. decimal decimalValue = uploadTransactions.Qty.HasValue ? uploadTransactions.Qty.Value : 0m;
  97. decimal ProductPrice = _product.First().ProductPrice.HasValue ? _product.First().ProductPrice.Value : 0m;
  98. decimal OriginalAmount = decimalValue * ProductPrice;
  99. uploadTransactions.OriginalAmount = OriginalAmount;
  100. if (uploadTransactions.Qty < 1 || OriginalAmount > 9900)
  101. {
  102. return ServiceResponse.Error(HttpStatusCode.NonAuthoritativeInformation, "升数不能小于1升,订单升数乘单价不能大于9900元");
  103. }
  104. }
  105. else if (uploadTransactions.OriginalAmount == null && uploadTransactions.Qty == null)
  106. {
  107. return ServiceResponse.Error("金额与升数为空");
  108. }
  109. uploadTransactions.MiniProgramID = user.Id;
  110. }
  111. else//后支付
  112. {
  113. key = uploadTransactions.ExternalGunNumber + ":" +
  114. uploadTransactions.OriginalAmount + ":" +
  115. uploadTransactions.Qty + ":" +
  116. uploadTransactions.MiniProgramID + ":" +
  117. uploadTransactions.FuelItemTransactionEndTime + ":" +
  118. uploadTransactions.TransactionNumber + ":" +
  119. Buid.ToString();
  120. }
  121. transactions output = await GetRedisTransactions(uploadTransactions, key);
  122. if (output != null)
  123. {
  124. return ServiceResponse.Ok(output);
  125. }
  126. var trx = uploadTransactions.ToTransactions(uploadTransactions, Buid, _product.FirstOrDefault(), _nozzle.FirstOrDefault(), site.PaymentMode);
  127. //int affectedRows = _fsql.Insert<transactions>().AppendData(trx).ExecuteAffrows();
  128. // var affectedRows = _fsql.Insert<transactions>().AppendData(trx).ExecuteInserted();
  129. var id = _fsql.Insert(trx).ExecuteIdentity();
  130. if (id > 0)
  131. {
  132. var insertedTransaction = _fsql.Select<transactions>()
  133. .Where(t => t.Id == id)
  134. .First();
  135. string jsonString = JsonConvert.SerializeObject(insertedTransaction);
  136. RedisHelper.SetAsync(key, jsonString, 600);
  137. return ServiceResponse.Ok(insertedTransaction);
  138. }
  139. else
  140. {
  141. return ServiceResponse.Error("订单信息插入失败");
  142. }
  143. }
  144. /// <summary>
  145. /// 后支付查询
  146. /// </summary>
  147. /// <param name="input"></param>
  148. /// <returns></returns>
  149. public async Task<ServiceResponse> GetTransactionsAsync(RequestModel input)
  150. {
  151. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  152. var site = _fsql.Select<businessunitinfo>().Where(_ => _.Buid == Buid).First();
  153. var _nozzle = _fsql.Select<nozzle>().Where(_ => _.Buid == Buid).ToList();//根据油枪id查询外部枪号
  154. var user = _fsql.Select<miniprogramusers>().Where(_ => _.Buid == Buid).ToList();
  155. var paytype = _fsql.Select<paytype>().ToList();
  156. Expression<Func<transactions, bool>> where = p => p.Buid == Buid;
  157. //查油枪
  158. if (input.filter.NozzleID != "")
  159. {
  160. long.TryParse(input.filter.NozzleID, out long NozzleID);
  161. var nozzle = _nozzle.Where(_ => _.ExternalGunNumber == NozzleID).First();//根据油枪id查询外部枪号
  162. where = CombineExpressions(where, p => p.NozzleId == nozzle.Id);
  163. }
  164. //查油品
  165. if (input.filter.ProductName != "")
  166. {
  167. where = CombineExpressions(where, p => p.ProductName == input.filter.ProductName);
  168. }
  169. //查站点
  170. if (input.filter.siteName != "")
  171. {
  172. where = CombineExpressions(where, p => p.Buid == site.Buid);
  173. }
  174. //查用户
  175. if (input.filter.username != "")
  176. {
  177. var userid = user.Where(_ => _.UserName == input.filter.username).First();
  178. where = CombineExpressions(where, p => p.MiniProgramID == userid.Id);
  179. }
  180. //查加油时间
  181. if (input.filter.TransactionTime != null)
  182. {
  183. where = CombineExpressions(where, p => p.TransactionTime >= input.filter.TransactionTime[0] && p.TransactionTime <= input.filter.TransactionTime[1]);
  184. }
  185. //查支付模式
  186. if (input.filter.OrderType != "")
  187. {
  188. transactionsORDERTYPE OrderType = transactionsORDERTYPE.Postpayment;
  189. if (input.filter.OrderType == "0")
  190. {
  191. OrderType = transactionsORDERTYPE.Postpayment;
  192. }
  193. else if (input.filter.OrderType == "1")
  194. {
  195. OrderType = transactionsORDERTYPE.Prepayment;
  196. }
  197. where = CombineExpressions(where, p => p.OrderType == OrderType);
  198. }
  199. //查支付状态
  200. if (input.filter.OrderStatus != "")
  201. {
  202. transactionsORDERSTATUS OrderStatus = transactionsORDERSTATUS.Paid;
  203. if (input.filter.OrderStatus == "0")
  204. {
  205. OrderStatus = transactionsORDERSTATUS.Unpaid;
  206. }
  207. else if (input.filter.OrderStatus == "1")
  208. {
  209. OrderStatus = transactionsORDERSTATUS.Paid;
  210. }
  211. else if (input.filter.OrderStatus == "2")
  212. {
  213. OrderStatus = transactionsORDERSTATUS.Paying;
  214. }
  215. else if (input.filter.OrderStatus == "3")
  216. {
  217. OrderStatus = transactionsORDERSTATUS.CardPayment;
  218. }
  219. else if (input.filter.OrderStatus == "4")
  220. {
  221. OrderStatus = transactionsORDERSTATUS.Completed;
  222. }
  223. else if (input.filter.OrderStatus == "5")
  224. {
  225. OrderStatus = transactionsORDERSTATUS.Cancelled;
  226. }
  227. where = CombineExpressions(where, p => p.OrderStatus == OrderStatus);
  228. }
  229. var result = _fsql.Select<transactions>().Where(where).Count(out var total).Page(input.currentPage,input.pageSize).ToList();
  230. result = result.OrderByDescending(_ => _.TransactionTime).ToList();
  231. List<TransactionsList> list = new List<TransactionsList>();
  232. foreach (var item in result)
  233. {
  234. // var nozzle = _nozzle.Where(_ => _.Id == item.NozzleId)?.First();//根据油枪id查询外部枪号
  235. var nozzle = _nozzle.FirstOrDefault(_ => _.Id == item.NozzleId);//根据油枪id查询外部枪号
  236. var pay = paytype.FirstOrDefault(_ => _.Id == item.PaymentMethod)?.Name;
  237. TransactionsList transactions = new TransactionsList();
  238. transactions.TransactionNumber = item.TransactionNumber;
  239. transactions.SiteName = site.Name;
  240. transactions.NozzleProductName = item.NozzleId + " | " + item.ProductName;
  241. transactions.ActualPaymentAmount = string.IsNullOrEmpty(item.ActualPaymentAmount.ToString()) ? null : item.ActualPaymentAmount.ToString() + "元";
  242. transactions.TransactionTime = item.TransactionTime?.ToString("yyyy-MM-dd hh:mm:ss");
  243. transactions.OrderStatus = GetChineseStatus(item.OrderStatus);
  244. transactions.RefundAmount = string.IsNullOrEmpty(item.RefundAmount.ToString()) ? null : item.RefundAmount.ToString() + "元";
  245. transactions.OrderType = item.OrderType == transactionsORDERTYPE.Prepayment ? "预支付":"后支付";
  246. if (item.MiniProgramID != null)
  247. {
  248. var userid = user.Where(_ => _.Id == item.MiniProgramID).FirstOrDefault();
  249. transactions.UserName = userid?.UserName;
  250. string UserPhoneNumber = user.Where(_ => _.Id == item.MiniProgramID).FirstOrDefault()?.UserPhoneNumber;
  251. if (!string.IsNullOrEmpty(UserPhoneNumber))
  252. {
  253. transactions.UserPhoneNumber = HideMiddleDigits(UserPhoneNumber,3,4);
  254. }
  255. }
  256. transactions.PriceQty = item.Price + " | " + item.Qty;
  257. transactions.PaymentMethod = pay;
  258. list.Add(transactions);
  259. }
  260. return ServiceResponse.Ok(new { list, total });
  261. }
  262. /// <summary>
  263. /// 预支付查询
  264. /// </summary>
  265. /// <param name="input"></param>
  266. /// <returns></returns>
  267. public async Task<ServiceResponse> GetPrepaymentTransactionsAsync(RequestModel input)
  268. {
  269. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  270. var site = _fsql.Select<businessunitinfo>().Where(_ => _.Buid == Buid).First();
  271. var _nozzle = _fsql.Select<nozzle>().Where(_ => _.Buid == Buid).ToList();//根据油枪id查询外部枪号
  272. var user = _fsql.Select<miniprogramusers>().Where(_ => _.Buid == Buid).ToList();
  273. var paytype = _fsql.Select<paytype>().ToList();
  274. Expression<Func<transactions, bool>> where = p => p.Buid == Buid;
  275. //查油枪
  276. if (input.filter.NozzleID != "")
  277. {
  278. long.TryParse(input.filter.NozzleID, out long NozzleID);
  279. var nozzle = _nozzle.Where(_ => _.ExternalGunNumber == NozzleID).First();//根据油枪id查询外部枪号
  280. where = CombineExpressions(where, p => p.NozzleId == nozzle.Id);
  281. }
  282. //查油品
  283. if (input.filter.ProductName != "")
  284. {
  285. where = CombineExpressions(where, p => p.ProductName == input.filter.ProductName);
  286. }
  287. //查站点
  288. if (input.filter.siteName != "")
  289. {
  290. where = CombineExpressions(where, p => p.Buid == site.Buid);
  291. }
  292. //查用户
  293. if (input.filter.username != "")
  294. {
  295. var userid = user.Where(_ => _.UserName == input.filter.username).First();
  296. where = CombineExpressions(where, p => p.MiniProgramID == userid.Id);
  297. }
  298. //查加油时间
  299. if (input.filter.TransactionTime != null)
  300. {
  301. where = CombineExpressions(where, p => p.TransactionTime >= input.filter.TransactionTime[0] && p.TransactionTime <= input.filter.TransactionTime[1]);
  302. }
  303. //查支付模式
  304. if (input.filter.OrderType != "")
  305. {
  306. transactionsORDERTYPE OrderType = transactionsORDERTYPE.Postpayment;
  307. if (input.filter.OrderType == "0")
  308. {
  309. OrderType = transactionsORDERTYPE.Postpayment;
  310. }
  311. else if (input.filter.OrderType == "1")
  312. {
  313. OrderType = transactionsORDERTYPE.Prepayment;
  314. }
  315. where = CombineExpressions(where, p => p.OrderType == OrderType);
  316. }
  317. //查支付状态
  318. if (input.filter.OrderStatus != "")
  319. {
  320. transactionsORDERSTATUS OrderStatus = transactionsORDERSTATUS.Paid;
  321. if (input.filter.OrderStatus == "0")
  322. {
  323. OrderStatus = transactionsORDERSTATUS.Unpaid;
  324. }
  325. else if (input.filter.OrderStatus == "1")
  326. {
  327. OrderStatus = transactionsORDERSTATUS.Paid;
  328. }
  329. else if (input.filter.OrderStatus == "2")
  330. {
  331. OrderStatus = transactionsORDERSTATUS.Paying;
  332. }
  333. else if (input.filter.OrderStatus == "3")
  334. {
  335. OrderStatus = transactionsORDERSTATUS.CardPayment;
  336. }
  337. else if (input.filter.OrderStatus == "4")
  338. {
  339. OrderStatus = transactionsORDERSTATUS.Completed;
  340. }
  341. else if (input.filter.OrderStatus == "5")
  342. {
  343. OrderStatus = transactionsORDERSTATUS.Cancelled;
  344. }
  345. where = CombineExpressions(where, p => p.OrderStatus == OrderStatus);
  346. }
  347. var result = _fsql.Select<transactions>().Where(where).Count(out var total).Page(input.currentPage, input.pageSize).ToList();
  348. result = result.OrderByDescending(_ => _.TransactionTime).ToList();
  349. List<PrepaymentTransactionsList> list = new List<PrepaymentTransactionsList>();
  350. foreach (var item in result)
  351. {
  352. // var nozzle = _nozzle.Where(_ => _.Id == item.NozzleId)?.First();//根据油枪id查询外部枪号
  353. var nozzle = _nozzle.FirstOrDefault(_ => _.Id == item.NozzleId);//根据油枪id查询外部枪号
  354. var pay = paytype.FirstOrDefault(_ => _.Id == item.PaymentMethod)?.Name;
  355. PrepaymentTransactionsList transactions = new PrepaymentTransactionsList();
  356. transactions.TransactionNumber = item.TransactionNumber;
  357. transactions.SiteName = site.Name;
  358. transactions.NozzleProductName = item.NozzleId + " | " + item.ProductName;
  359. transactions.ActualPaymentAmount = string.IsNullOrEmpty(item.ActualPaymentAmount.ToString()) ? null : item.ActualPaymentAmount.ToString() + "元";
  360. transactions.TransactionTime = item.TransactionTime?.ToString("yyyy-MM-dd hh:mm:ss");
  361. transactions.OrderStatus = GetChineseStatus(item.OrderStatus);
  362. transactions.RefundAmount = string.IsNullOrEmpty(item.RefundAmount.ToString()) ? null : item.RefundAmount.ToString() + "元";
  363. transactions.OrderType = item.OrderType == transactionsORDERTYPE.Prepayment ? "预支付" : "后支付";
  364. if (item.MiniProgramID != null)
  365. {
  366. var userid = user.Where(_ => _.Id == item.MiniProgramID).FirstOrDefault();
  367. transactions.UserName = userid?.UserName;
  368. string UserPhoneNumber = user.Where(_ => _.Id == item.MiniProgramID).FirstOrDefault()?.UserPhoneNumber;
  369. if (!string.IsNullOrEmpty(UserPhoneNumber))
  370. {
  371. transactions.UserPhoneNumber = HideMiddleDigits(UserPhoneNumber, 3, 4);
  372. }
  373. }
  374. transactions.PriceQty = item.Price + " | " + item.Qty;
  375. transactions.PaymentMethod = pay;
  376. transactions.authorizationStatus = GetAuthorizationStatus(item.authorizationStatus);
  377. transactions.RefundStatus = GetRefundStatus(item.RefundStatus);
  378. list.Add(transactions);
  379. }
  380. return ServiceResponse.Ok(new { list, total });
  381. }
  382. /// <summary>
  383. /// 小程序查询未支付订单
  384. /// </summary>
  385. /// <returns></returns>
  386. public async Task<ServiceResponse> GetMiniProgramTransactionsUnpaidAsync(TransactionsInput input)
  387. {
  388. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  389. Expression<Func<transactions, bool>> where = p => p.Buid == Buid;
  390. string WachatID = HttpRequestReader.GetWachatID(); //用户
  391. var userSession = WechatUserSessionRepo.GetUserSession(WachatID.ToString());
  392. if (userSession == null)
  393. {
  394. return ServiceResponse.Error(HttpStatusCode.NonAuthoritativeInformation, "未找到用户!");
  395. }
  396. DateTime dayBeforeTime = DateTime.Now.AddDays(-1);
  397. var user = _fsql.Select<miniprogramusers>().Where(_ => _.OpenId == userSession.openid).First();
  398. where = CombineExpressions(where, p => p.MiniProgramID == user.Id && p.OrderStatus == transactionsORDERSTATUS.Unpaid && p.TransactionTime >= dayBeforeTime);
  399. var result = await _entityHelper.GetEntitiesAsync<transactions>(where);
  400. result = result.OrderByDescending(x => x.CreateTime).ToList();
  401. return ServiceResponse.Ok(result);
  402. }
  403. /// <summary>
  404. /// 小程序用户根据抢号查询未支付订单
  405. /// </summary>
  406. /// <returns></returns>
  407. public async Task<ServiceResponse> GetMiniProgramTransactionsUnpaidNozzleAsync(long NozzleId)
  408. {
  409. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  410. Expression<Func<transactions, bool>> where = p => p.Buid == Buid;
  411. string WachatID = HttpRequestReader.GetWachatID(); //用户
  412. var userSession = WechatUserSessionRepo.GetUserSession(WachatID.ToString());
  413. if (userSession == null)
  414. {
  415. return ServiceResponse.Error(HttpStatusCode.NonAuthoritativeInformation, "未找到用户!");
  416. }
  417. DateTime dayBeforeTime = DateTime.Now.AddDays(-1);
  418. var _nozzle = _fsql.Select<nozzle>().Where(_ => _.Id == NozzleId).First();//根据油枪id查询外部枪号
  419. where = CombineExpressions(where, p => p.NozzleId == _nozzle.ExternalGunNumber && p.OrderStatus == transactionsORDERSTATUS.Unpaid && p.TransactionTime >= dayBeforeTime);
  420. var result = await _entityHelper.GetEntitiesAsync<transactions>(where);
  421. result = result.OrderByDescending(x => x.CreateTime).ToList();
  422. return ServiceResponse.Ok(result);
  423. }
  424. /// <summary>
  425. /// 小程序查询已支付订单
  426. /// </summary>
  427. /// <returns></returns>
  428. public async Task<ServiceResponse> GetMiniProgramTransactionsPaidAsync(TransactionsInput input)
  429. {
  430. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  431. Expression<Func<transactions, bool>> where = p => p.Buid == Buid;
  432. string WachatID = HttpRequestReader.GetWachatID(); //用户
  433. var userSession = WechatUserSessionRepo.GetUserSession(WachatID.ToString());
  434. if (userSession == null)
  435. {
  436. return ServiceResponse.Error(HttpStatusCode.NonAuthoritativeInformation, "未找到用户!");
  437. }
  438. var user = _fsql.Select<miniprogramusers>().Where(_ => _.OpenId == userSession.openid).First();
  439. where = CombineExpressions(where, p => p.MiniProgramID == user.Id && p.OrderStatus == transactionsORDERSTATUS.Paid);
  440. var result = await _entityHelper.GetEntitiesAsync<transactions>(where);
  441. return ServiceResponse.Ok(result);
  442. }
  443. /// <summary>
  444. /// 小程序查询已支付订单
  445. /// </summary>
  446. /// <returns></returns>
  447. public async Task<ServiceResponse> WXFindOrders(DateTime? dateTime, int pageNum, int lineCount)
  448. {
  449. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  450. Expression<Func<transactions, bool>> where = p => p.Buid == Buid;
  451. string WachatID = HttpRequestReader.GetWachatID(); //用户
  452. var userSession = WechatUserSessionRepo.GetUserSession(WachatID.ToString());
  453. if (userSession == null)
  454. {
  455. return ServiceResponse.Error(HttpStatusCode.NonAuthoritativeInformation, "未找到用户!");
  456. }
  457. var user = _fsql.Select<miniprogramusers>().Where(_ => _.OpenId == userSession.openid).First();
  458. DateTime time = dateTime != null ? (DateTime)dateTime : DateTime.Now;
  459. var select = _fsql.Select<transactions>().Where(_ => _.MiniProgramID == user.Id
  460. && (_.TransactionTime >= DateTime.Now.AddDays(-30) && _.TransactionTime <= time));
  461. var list = await select.Page(pageNum, lineCount).OrderByDescending(_ => _.TransactionTime).ToListAsync();
  462. return ServiceResponse.Ok(list);
  463. }
  464. /// <summary>
  465. /// 提交支付
  466. /// </summary>
  467. /// <param name="input"></param>
  468. /// <returns></returns>
  469. public async Task<ServiceResponse> CommitPayment(int trxId, string AuthCode)
  470. {
  471. bool paymentOK = false;
  472. var trx = _entityHelper.GetEntitiesAsync<transactions>(_ => _.Id == trxId).Result.FirstOrDefault();
  473. if (trx == null)
  474. {
  475. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "未查询到订单!");
  476. }
  477. var paytype = _entityHelper.GetEntitiesAsync<paytype>(_ => _.Id == trx.PaymentMethod).Result.FirstOrDefault();
  478. string billNumber = SequenceNumber.Next();//订单编号
  479. trx.TransactionNumber = billNumber;
  480. decimal payDueAmount = (decimal)trx.OriginalAmount;
  481. string Channel = paytype.Channel;
  482. var serviceResult = await _payService.PerformElectronicProcess(AuthCode, payDueAmount, billNumber);
  483. Payment.Core.Models.ElectronicOrderProcessResultModel payResult = (Payment.Core.Models.ElectronicOrderProcessResultModel)serviceResult.Data;
  484. if (!serviceResult.IsSuccessful() || payResult.ResultCode == "PAY_ERROR")
  485. {
  486. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "支付失败");
  487. }
  488. trx.ActualPaymentAmount = payDueAmount;//实际支付金额
  489. trx.ResultCode = payResult?.ResultCode;//支付成功应该为0
  490. trx.ErrorDetail = payResult?.ErrorDetail;
  491. _entityHelper.UpdateAsync(trx);
  492. return ServiceResponse.Ok(trx);
  493. }
  494. /// <summary>
  495. /// 查询redis订单缓存
  496. /// </summary>
  497. /// <returns></returns>
  498. public async Task<transactions> GetRedisTransactions(UploadTransactions uploadTransactions, string key)
  499. {
  500. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  501. var respond = RedisHelper.GetAsync(key).Result;
  502. if (respond == null)
  503. {
  504. return null;
  505. }
  506. transactions transactions = JsonConvert.DeserializeObject<transactions>(respond);
  507. var trx = _entityHelper.GetEntitiesAsync<transactions>(_ => _.Id == transactions.Id).Result.FirstOrDefault();
  508. if (trx == null || trx.OrderStatus != transactionsORDERSTATUS.Unpaid)
  509. {
  510. return null;
  511. }
  512. return transactions;
  513. }
  514. // 辅助方法:组合两个表达式
  515. private static Expression<Func<T, bool>> CombineExpressions<T>(Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
  516. {
  517. ParameterExpression param = expr1.Parameters[0];
  518. Expression body = Expression.AndAlso(expr1.Body, Expression.Invoke(expr2, param));
  519. return Expression.Lambda<Func<T, bool>>(body, param);
  520. }
  521. /// <summary>
  522. /// 退款
  523. /// </summary>
  524. /// <param name="input"></param>
  525. /// <returns></returns>
  526. public async Task<ServiceResponse> RefundTrx(int trxId,
  527. decimal? OriginalQty = null)
  528. {
  529. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  530. var businessunitinfo = _entityHelper.GetEntitiesAsync<businessunitinfo>(_ => _.Buid == Buid).Result.FirstOrDefault();
  531. if (businessunitinfo == null)
  532. {
  533. logger.Debug($"RefundTrx,退款失败,站点为空,Buid={Buid} ,trxId={trxId}");
  534. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "站点为空");
  535. }
  536. string[] parts = businessunitinfo.GpsCoordinates.Split(',');
  537. // if (parts.Length == 2 &&
  538. //double.TryParse(parts[0], out double latitude2) &&
  539. //double.TryParse(parts[1], out double longitude2))
  540. // {
  541. // }
  542. // else
  543. // {
  544. // return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "站点经纬度获取失败");
  545. // }
  546. // //计算调用方和油站的距离,超过距离判定为恶意请求
  547. // double distance = DistanceCalculator.CalculateDistance(longitude, latitude, longitude2, latitude2);
  548. // if (distance > 5)
  549. // {
  550. // return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "该请求大于油站地址5公里");
  551. // }
  552. var trx = _entityHelper.GetEntitiesAsync<transactions>(_ => _.Id == trxId).Result.FirstOrDefault();
  553. if (trx == null)
  554. {
  555. logger.Debug($"退款失败,未查询到订单,Buid={Buid} ,trxId={trxId}");
  556. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "未查询到订单!");
  557. }
  558. else if (trx.RefundStatus == RefundStatus.FullyRefunded)
  559. {
  560. logger.Debug($"该订单已退款,未查询到订单,Buid={Buid} ,trxId={trxId}");
  561. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "该订单已退款");
  562. }
  563. else if (trx.RefundStatus == RefundStatus.PartiallyRefunded)
  564. {
  565. logger.Debug($"该订单已部分退款,Buid={Buid} ,trxId={trxId}");
  566. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "该订单已部分退款");
  567. }
  568. else if (trx.OrderStatus == transactionsORDERSTATUS.Unpaid)
  569. {
  570. logger.Debug($"该订单已退款,该订单未支付,Buid={Buid} ,trxId={trxId}");
  571. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "该订单未支付");
  572. }
  573. //获取单价
  574. decimal? ProductPrice = _entityHelper.GetEntitiesAsync<product>(_ => _.Id == trx.ProductId).Result.FirstOrDefault().ProductPrice;
  575. if (ProductPrice == null)
  576. {
  577. logger.Debug($"该订单已退款,单价获取失败,Buid={Buid} ,trxId={trxId}");
  578. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "单价获取失败");
  579. }
  580. //计算退款金额
  581. //decimal RefundAmount = (decimal)trx.ActualPaymentAmount;
  582. decimal RefundAmount = 0.0M;
  583. if (businessunitinfo.PaymentMode == 0)//后支付
  584. {
  585. logger.Debug($"后支付模式无法退款!,Buid={Buid} ,trxId={trxId}");
  586. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "后支付模式无法退款!");
  587. // RefundAmount = (decimal)((trx.Qty - trx.OriginalQty) * ProductPrice.Value);
  588. }
  589. else if (businessunitinfo.PaymentMode == 1)//预支付
  590. {
  591. decimal volume = OriginalQty ?? 0m;
  592. RefundAmount = (decimal)((trx.Qty - volume) * trx.Price);
  593. //RefundAmount = (decimal)((trx.Qty - OriginalQty) * ProductPrice.Value);
  594. }
  595. if (RefundAmount <= 0.0M)
  596. {
  597. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "该笔单无需退款");
  598. }
  599. //退款
  600. var serviceResult = await _payService.ReturnProcess(RefundAmount, (decimal)trx.ActualPaymentAmount, "WX_SCAN", trx.BillNumber);
  601. Payment.Core.Models.ElectronicOrderProcessResultModel payResult = (Payment.Core.Models.ElectronicOrderProcessResultModel)serviceResult.Data;
  602. if (!serviceResult.IsSuccessful() || payResult.ResultCode == "PAY_ERROR")
  603. {
  604. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "退款失败");
  605. }
  606. if (RefundAmount == (decimal)trx.ActualPaymentAmount)
  607. {
  608. trx.RefundStatus = RefundStatus.FullyRefunded;//全额退款
  609. }
  610. else
  611. {
  612. trx.RefundStatus = RefundStatus.PartiallyRefunded;//部分退款
  613. }
  614. trx.OrderStatus = transactionsORDERSTATUS.Cancelled;
  615. trx.RefundAmount = RefundAmount;
  616. int affectedRows = _fsql.Update<transactions>().SetSource(trx).ExecuteAffrows();
  617. var user = _fsql.Select<miniprogramusers>().Where(_ => _.Id == trx.MiniProgramID).First();
  618. string jsonString = JsonConvert.SerializeObject(trx);
  619. await _mqttService.SubscribeAsync("refund/" + Buid);
  620. await Task.Delay(2000);
  621. var sendjson = new { data = jsonString, UserName = user.UserName, UserPhoneNumber = user.UserPhoneNumber };
  622. await _mqttService.PublishAsync("refund/" + Buid, JsonConvert.SerializeObject(sendjson));
  623. return ServiceResponse.Ok(trx);
  624. }
  625. public async Task<ServiceResponse> UnifiedOrder(int trxId)
  626. {
  627. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  628. try
  629. {
  630. logger.Debug("UnifiedOrder start++");
  631. string WachatID = HttpRequestReader.GetWachatID(); //用户
  632. var userSession = WechatUserSessionRepo.GetUserSession(WachatID.ToString());
  633. if (userSession == null)
  634. {
  635. return ServiceResponse.Error(HttpStatusCode.NonAuthoritativeInformation, "未找到用户!");
  636. }
  637. //var userSession = new WechatUserSessionResponse() { buId = "12345678-9abc-def0-1234-56789abcdef0", openid = "o8pFb5cWH1KkBDvGls2X7yMiFkGA" };
  638. var site = _fsql.Select<businessunitinfo>().Where(_ => _.Buid == Buid).First();
  639. var weChatService = new WeChatService(site.Appid, site.Secret);
  640. var user = _fsql.Select<miniprogramusers>().Where(_ => _.OpenId == userSession.openid).First();
  641. var trx = _entityHelper.GetEntitiesAsync<transactions>(_ => _.Id == trxId).Result.FirstOrDefault();
  642. if (trx == null)
  643. {
  644. logger.Debug($"统一下单,Buid={Buid} ,trxId={trxId},未查询到订单");
  645. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "未查询到订单!");
  646. }
  647. logger.Debug($"统一下单开始,Buid={Buid} ,trxId={trxId}");
  648. var serviceResult = await _payService.UnifiedOrder(trx.OriginalAmount.Value, "WX_SCAN", userSession.openid);
  649. var dataProperties = serviceResult.Data.GetType().GetProperty("UnifiedOrderResult");
  650. if (!serviceResult.IsSuccessful() || dataProperties == null)
  651. {
  652. logger.Debug($"统一下单失败,Buid={Buid} ,trxId={trxId}" );
  653. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "统一下单失败");
  654. }
  655. //小程序支付完后,云端需要通过订单编号去支付平台查询支付状态,以便更新订单信息
  656. _ = Task.Delay(1500).ContinueWith(async _ =>
  657. {
  658. using (var scope = _serviceScopeFactory.CreateScope())
  659. {
  660. var scopedMqttService = scope.ServiceProvider.GetRequiredService<IMqttClientService>();
  661. var dataProperties = serviceResult.Data.GetType().GetProperty("eOrder");
  662. var orderModel = (Fuel.Payment.Core.Models.ElectronicOrderModel)dataProperties.GetValue(serviceResult.Data);
  663. var genericResponse = await _payService.QueryOrder(orderModel);
  664. logger.Debug($"统一下单,Buid={Buid} ,trxId={trxId},genericResponse.Data={genericResponse.Data}");
  665. if (genericResponse.IsSuccessful() && orderModel.TradeStatus == Fuel.Payment.Core.Models.TradeStatus.SUCCESS)
  666. {
  667. trx.OrderStatus = transactionsORDERSTATUS.Paid;//将订单状态更改成已支付
  668. trx.MiniProgramID = user.Id;
  669. trx.BillNumber = orderModel.BillNumber;
  670. trx.BillNumberBase62 = NumericBase62Converter.Encode(orderModel.BillNumber);
  671. trx.ActualPaymentAmount = trx.OriginalAmount;
  672. trx.PaymentMethod = 2;//2 :微信支付
  673. try
  674. {
  675. await scopedMqttService.SubscribeAsync("paid/" + Buid);
  676. string jsonString = JsonConvert.SerializeObject(trx);
  677. await Task.Delay(2000);
  678. var sendjson = new { data = jsonString, UserName = user.UserName, UserPhoneNumber = user.UserPhoneNumber };
  679. logger.Debug($"统一下单,Buid={Buid} ,trxId={trxId},订单推送fcc sendjson={System.Text.Json.JsonSerializer.Serialize(sendjson)}");
  680. //支付完成将订单信息推送到fcc
  681. scopedMqttService.PublishAsync("paid/" + Buid, JsonConvert.SerializeObject(sendjson));
  682. }
  683. catch (Exception ex)
  684. {
  685. logger.Debug($"统一下单,Buid={Buid} ,trxId={trxId},异常:"+ ex.Message);
  686. }
  687. logger.Debug($"统一下单,Buid={Buid} ,trxId={trxId},更新订单信息");
  688. int affectedRows = _fsql.Update<transactions>().SetSource(trx).ExecuteAffrows();
  689. logger.Debug($"统一下单,Buid={Buid} ,trxId={trxId},更新状态" + affectedRows);
  690. //SendMessage(trx,Buid);
  691. }
  692. }
  693. });
  694. var unifiedOrderResult = dataProperties.GetValue(serviceResult.Data);
  695. var options = new JsonSerializerOptions
  696. {
  697. IgnoreReadOnlyProperties = true // 忽略只读属性
  698. };
  699. string serializedUnifiedOrderResult = System.Text.Json.JsonSerializer.Serialize(unifiedOrderResult, options);
  700. logger.Debug($"统一下单,Buid={Buid} ,trxId={trxId}, 支付信息 " + serializedUnifiedOrderResult);
  701. return ServiceResponse.Ok(unifiedOrderResult);
  702. }
  703. catch (Exception ex)
  704. {
  705. logger.Debug($"统一下单,Buid={Buid} ,trxId={trxId},Exception:" +ex.Message);
  706. return ServiceResponse.Error(ex.Message);
  707. }
  708. }
  709. public async Task<ServiceResponse> Redeem(int trxId, decimal OriginalQty, decimal FuelItemPumpTotalizerVolume)
  710. {
  711. var trx = _entityHelper.GetEntitiesAsync<transactions>(_ => _.Id == trxId).Result.FirstOrDefault();
  712. if (trx == null)
  713. {
  714. return ServiceResponse.Error(HttpStatusCode.NotAcceptable, "未查询到订单!");
  715. }
  716. var refund = await RefundTrx(trxId, OriginalQty);
  717. trx = _entityHelper.GetEntitiesAsync<transactions>(_ => _.Id == trxId).Result.FirstOrDefault();
  718. trx.OriginalQty = OriginalQty;
  719. trx.FuelItemPumpTotalizerVolume = FuelItemPumpTotalizerVolume;
  720. if (refund.IsSuccessful() || refund.StatusCode == HttpStatusCode.NotAcceptable)
  721. {
  722. trx.OrderStatus = transactionsORDERSTATUS.Completed;
  723. }
  724. int affectedRows = _fsql.Update<transactions>().SetSource(trx).ExecuteAffrows();
  725. return ServiceResponse.Ok(trx);
  726. }
  727. /// <summary>
  728. /// 微信发送模板消息
  729. /// </summary>
  730. /// <returns></returns>
  731. public async Task SendMessage(int trxId, string orderType)
  732. {
  733. var data = new Dictionary<string, object>();//动态字段
  734. Guid Buid = HttpRequestReader.GetCurrentBuId(); //站点id
  735. var trx = _entityHelper.GetEntitiesAsync<transactions>(_ => _.Id == trxId).Result.FirstOrDefault();
  736. var site = _fsql.Select<businessunitinfo>().Where(_ => _.Buid == Buid).First();
  737. var weChatService = new WeChatService(site.Appid, site.Secret);
  738. string WachatID = HttpRequestReader.GetWachatID(); //用户
  739. //var userSession = WechatUserSessionRepo.GetUserSession(WachatID.ToString());
  740. //if (userSession == null)
  741. //{
  742. //}
  743. //var user = _fsql.Select<miniprogramusers>().Where(_ => _.OpenId == userSession.openid).First();
  744. var user = _fsql.Select<miniprogramusers>().Where(_ => _.OpenId == WachatID).First();
  745. var template = _fsql.Select<configuration>().Where(_ => _.Buid == Buid && _.Type == 2 && _.Name == "小程序通知模板").First();
  746. if (template == null)
  747. {
  748. //模板获取失败
  749. return;
  750. }
  751. //将金额小数改成俩位
  752. if (trx.ActualPaymentAmount.HasValue)
  753. {
  754. decimal roundedValue = Math.Round(trx.ActualPaymentAmount.Value, 2, MidpointRounding.AwayFromZero);
  755. trx.ActualPaymentAmount = roundedValue;
  756. }
  757. // var BillNumber = EncodeBillNumber(trx.BillNumber);
  758. var config = JsonConvert.DeserializeObject<TemplateConfig>(template.Value);
  759. var dataDict = new Dictionary<string, object>();
  760. var dynamicData = new Dictionary<string, object>
  761. {
  762. { "orderType", orderType},
  763. { "BillNumber", trx.BillNumberBase62 },//trx.BillNumber
  764. { "ProductName_Qty", trx.ProductName + " & " + trx.Qty + "L"},
  765. { "ActualPaymentAmount",trx.ActualPaymentAmount.ToString() + "元" },
  766. { "TransactionTime", ToFormattedString(trx.TransactionTime) }
  767. };
  768. foreach (var mapping in config.DataMappings)
  769. {
  770. string dataFieldName = mapping.Key;
  771. string targetFieldName = mapping.Value;
  772. if (dynamicData.TryGetValue(dataFieldName, out object value))
  773. {
  774. dataDict.Add(targetFieldName, new { value = value });
  775. }
  776. }
  777. // 准备要发送的模板消息内容
  778. var templateMessage = new WeChatService.TemplateMessage
  779. {
  780. ToUser = user.OpenId,
  781. TemplateId = config.TemplateId,
  782. Page = "pages/historyOrder/historyOrder",
  783. Data = dataDict
  784. };
  785. //var templateMessage = new WeChatService.TemplateMessage
  786. //{
  787. // ToUser = user.OpenId,
  788. // TemplateId = "V0tl-4n-5hwNZc4SrEttvrmawAyM-SB0pQWZNwp54Ks",
  789. // Page = "pages/historyOrder/historyOrder",
  790. // Data = new
  791. // {
  792. // short_thing10 = new { value = orderType },
  793. // character_string11 = new { value = trx.BillNumber },
  794. // thing12 = new { value = trx.ProductName + " | " + trx.Qty + "L"},
  795. // amount13 = new { value = trx.ActualPaymentAmount },
  796. // time14 = new { value = ToFormattedString(trx.TransactionTime) },
  797. // }
  798. //};
  799. weChatService.SendTemplateMessage(templateMessage);
  800. }
  801. public string ToFormattedString(DateTime? dateTime)
  802. {
  803. if (dateTime.HasValue)
  804. {
  805. return dateTime.Value.ToString("yyyy-MM-dd HH:mm:ss");
  806. }
  807. else
  808. {
  809. return null;
  810. }
  811. }
  812. public static string GetAuthorizationStatus(AuthorizationStatus status)
  813. {
  814. switch (status)
  815. {
  816. case AuthorizationStatus.Unauthorized:
  817. return "未授权";
  818. case AuthorizationStatus.Authorized:
  819. return "已授权";
  820. case AuthorizationStatus.WaitAuthorization:
  821. return "等待授权";
  822. case AuthorizationStatus.FillingUp:
  823. return "加油中";
  824. default:
  825. throw new ArgumentOutOfRangeException(nameof(status), status, null);
  826. }
  827. }
  828. public static string GetRefundStatus(RefundStatus status)
  829. {
  830. switch (status)
  831. {
  832. case RefundStatus.NotRefunded:
  833. return "无退款";
  834. case RefundStatus.FullyRefunded:
  835. return "订单全额退款";
  836. case RefundStatus.PartiallyRefunded:
  837. return "订单部分退款";
  838. default:
  839. throw new ArgumentOutOfRangeException(nameof(status), status, null);
  840. }
  841. }
  842. public static string GetChineseStatus(transactionsORDERSTATUS status)
  843. {
  844. switch (status)
  845. {
  846. case transactionsORDERSTATUS.Unpaid:
  847. return "订单未支付";
  848. case transactionsORDERSTATUS.Paid:
  849. return "订单已支付";
  850. case transactionsORDERSTATUS.Paying:
  851. return "订单正在支付中";
  852. case transactionsORDERSTATUS.CardPayment:
  853. return "订单通过卡支付";
  854. case transactionsORDERSTATUS.Completed:
  855. return "订单已完成";
  856. case transactionsORDERSTATUS.Cancelled:
  857. return "已退款";
  858. default:
  859. throw new ArgumentOutOfRangeException(nameof(status), status, null);
  860. }
  861. }
  862. /// <summary>
  863. /// 隐藏手机号中间的几位数字
  864. /// </summary>
  865. /// <param name="phoneNumber">原始手机号</param>
  866. /// <param name="startIndex">开始隐藏的位置</param>
  867. /// <param name="length">需要隐藏的字符长度</param>
  868. /// <returns>处理后的手机号</returns>
  869. public static string HideMiddleDigits(string phoneNumber, int startIndex, int length)
  870. {
  871. if (string.IsNullOrEmpty(phoneNumber) || startIndex + length > phoneNumber.Length)
  872. {
  873. return phoneNumber; // 如果输入无效,则返回原始手机号
  874. }
  875. string visiblePartStart = phoneNumber.Substring(0, startIndex); // 手机号前部分
  876. string hiddenPart = new string('*', length); // 中间隐藏部分
  877. string visiblePartEnd = phoneNumber.Substring(startIndex + length); // 手机号后部分
  878. return visiblePartStart + hiddenPart + visiblePartEnd;
  879. }
  880. }
  881. }