App.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. using Applications.FDC;
  2. using Edge.Core.Configuration;
  3. using Edge.Core.Database;
  4. using Edge.Core.Database.Models;
  5. using Edge.Core.Processor;
  6. using Edge.Core.Processor.Dispatcher.Attributes;
  7. using Edge.Core.UniversalApi;
  8. using HengShan_Pump_TQC_IFSF;
  9. using Microsoft.EntityFrameworkCore;
  10. using Microsoft.Extensions.DependencyInjection;
  11. using Microsoft.Extensions.Logging;
  12. using Microsoft.Extensions.Logging.Abstractions;
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. using System.Text.Json;
  17. using System.Threading.Tasks;
  18. using VBaoProxyApp.Cloud;
  19. namespace Apps.PetroChinaIcCardFuelTrxToSmartFuelCloudUploader
  20. {
  21. public class AppConfigV1
  22. {
  23. public string UserName { get; set; } = "3038";
  24. public string Password { get; set; } = "111111";
  25. /// <summary>
  26. /// Sample: http://url:8698
  27. /// DO NOT end with slash
  28. /// </summary>
  29. public string ApiGatewayEntryUrlWithoutLastSlash { get; set; } = "http://47.97.120.160:8698";
  30. public string DeviceSN { get; set; } = "66662e74-d51e-42ae-bc89-2d39312c9c30";
  31. }
  32. [MetaPartsDescriptor(
  33. "中油IC卡交易上传器",
  34. "用于将中油IC卡交易上传至SmartFuelCloud,日志标记为DynamicPrivate_PetroChinaIcCardFuelTrxToSmartFuelCloudUploader,它通过直接采集油机端的交易,并过一步判定交易是否由IC卡授权完成,如果是的话,此交易才会被上传至云端."
  35. , new[] { "中油" })]
  36. public class App : IAppProcessor
  37. {
  38. private const string genericDataType = "PendingForUploadTrxData";
  39. private IServiceProvider services;
  40. private Applications.FDC.FdcServerHostApp fdcServerApp;
  41. private AppConfigV1 appConfig;
  42. private ILogger logger = NullLogger.Instance;
  43. public string MetaConfigName { get; set; }
  44. private System.Threading.Timer timelyUploadTimer;
  45. [ParamsJsonSchemas("appCtorParamsJsonSchema")]
  46. public App(AppConfigV1 appConfig, IServiceProvider services)
  47. {
  48. this.appConfig = appConfig;
  49. var loggerFactory = services.GetRequiredService<ILoggerFactory>();
  50. this.logger = loggerFactory.CreateLogger("DynamicPrivate_PetroChinaIcCardFuelTrxToSmartFuelCloudUploader");
  51. this.services = services;
  52. CloudHelper.Default.Credential = new CloudCredential()
  53. {
  54. ApiGatewayEntryUrl = appConfig.ApiGatewayEntryUrlWithoutLastSlash,// "http://wc.shaojun.xyz:8698",
  55. UserName = appConfig.UserName,// "507",
  56. Password = appConfig.Password,//"111111",
  57. DeviceSN = appConfig.DeviceSN,//"1234567890sss",
  58. };
  59. }
  60. public void Init(IEnumerable<IProcessor> processors)
  61. {
  62. var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
  63. var pumpGroupHandlers = processors.WithHandlerOrApp<PetroChinaSilentPumpGroupHandler>().SelectHandlerOrAppThenCast<PetroChinaSilentPumpGroupHandler>();
  64. this.fdcServerApp = processors.WithHandlerOrApp<FdcServerHostApp>().SelectHandlerOrAppThenCast<FdcServerHostApp>().First();
  65. foreach (var p in pumpGroupHandlers)
  66. p.OnPetroChinaIcCardAuthorizedFuelTrxDone += async (s, arg) =>
  67. {
  68. NozzleExtraInfo nzlExtraInfo = null;
  69. nzlExtraInfo = this.fdcServerApp.GetNozzleExtraInfos().FirstOrDefault(ni => ni.PumpId == arg.Transaction.Nozzle.PumpId
  70. && ni.NozzleLogicalId == arg.Transaction.Nozzle.LogicalId);
  71. if (nzlExtraInfo == null)
  72. {
  73. await universalApiHub.FirePersistGenericAlarmIfNotExists(this,
  74. new GenericAlarm()
  75. {
  76. Title = $"油枪信息配置错误",
  77. Category = $"配置",
  78. Detail = $"由PumpHandler中产生的交易(pumpId:{arg.Transaction.Nozzle.PumpId}, logicalNozzleId: {arg.Transaction.Nozzle.LogicalId}, vol: {arg.Transaction.Volumn}) 无法找到与其对应绑定的Fdc nozzle信息,请检查PumpHandler与FdcServerApp中关于加油点和油枪的配置与映射关系",
  79. Severity = GenericAlarmSeverity.Error
  80. },
  81. ga => ga.Detail,
  82. ga => ga.Detail);
  83. return;
  84. }
  85. string itemId = nzlExtraInfo.ProductBarcode.ToString();
  86. GenericData data = new GenericData()
  87. {
  88. CreatedTimeStamp = DateTime.Now,
  89. Owner = this.GetType().FullName,
  90. Type = genericDataType,
  91. State = "Unfinished",
  92. ComplexData = JsonSerializer.Serialize(new
  93. {
  94. itemId,
  95. SiteLevelNozzleId = nzlExtraInfo.SiteLevelNozzleId ?? -1,
  96. Transaction = new
  97. {
  98. arg.Transaction.Amount,
  99. arg.Transaction.Volumn,
  100. arg.Transaction.SequenceNumberGeneratedOnPhysicalPump,
  101. arg.Transaction.Nozzle.PumpId,
  102. arg.Transaction.Nozzle.LogicalId
  103. }
  104. })
  105. };
  106. using (var scope = this.services.CreateScope())
  107. {
  108. var dbContext = scope.ServiceProvider.GetRequiredService<SqliteDbContext>();
  109. dbContext.GenericDatas.Add(data);
  110. await dbContext.SaveChangesAsync();
  111. }
  112. };
  113. }
  114. [UniversalApi]
  115. public async Task<bool> UploadTrxToCloud(string itemId, decimal amount, decimal volume, int pumpSideTrxSeqNo, int pumpId, int fdcNozzleLogicalId, int siteLevelNozzleId)
  116. {
  117. var postItem = await CloudHelper.Default.GetPosItemAsync(itemId);
  118. if (postItem == null) throw new InvalidOperationException($"Could not find PosItem(itemId: {itemId}) from cloud side");
  119. var createdTrxId = await CloudHelper.Default.CreateTransactionAsync(new ClientFuelTrxInfo()
  120. {
  121. PosItemId = postItem.Id,
  122. PumpId = pumpId,
  123. NozzleId = fdcNozzleLogicalId,
  124. SiteNozzleNo = siteLevelNozzleId,
  125. Amount = amount,
  126. Volume = volume,
  127. SeqNo = pumpSideTrxSeqNo,
  128. Barcode = int.Parse(postItem.BarCode),
  129. PayAmount = amount,
  130. Source = Dfs.WayneChina.PosModelMini.PosTrxSource.Outdoor,
  131. UnitPrice = 4
  132. }, "中油TQC油机上的IC卡交易");
  133. var commitResult = await CloudHelper.Default.CommitTransactionAsync(createdTrxId, new Dfs.WayneChina.PosModelMini.PosTrxMop()
  134. {
  135. Mop = new Dfs.WayneChina.PosModelMini.PosMop()
  136. {
  137. PaymentId = (int)PaymentID.PetroChinaIC,
  138. },
  139. Paid = amount
  140. }, null);
  141. return true;
  142. }
  143. public async Task<bool> Start()
  144. {
  145. //var extraInfos = this.fdcServerApp.GetNozzleExtraInfos();
  146. //var fuelItemBarcodes = extraInfos.GroupBy(i => i.ProductBarcode).Select(g => g.Key);
  147. //foreach (var barcode in fuelItemBarcodes)
  148. //{
  149. // try
  150. // {
  151. // var cloudFuelItem = await CloudHelper.Default.GetPosItemAsync(barcode.ToString());
  152. // if (cloudFuelItem == null)
  153. // throw new InvalidOperationException($"Local fuel item with barcode: {barcode} could not find mapping item in cloud side.");
  154. // }
  155. // catch (Exception exxx)
  156. // {
  157. // this.logger.LogError($"Failed to retrieve Cloud side fuel item based on local barcode: {barcode}, make sure local fuel items are all" +
  158. // $" correctly set and mapped(by their item id) in cloud side. error detail: {exxx}");
  159. // throw;
  160. // }
  161. //}
  162. var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
  163. var genericDataOwner = this.GetType().FullName;
  164. this.timelyUploadTimer = new System.Threading.Timer(async (_) =>
  165. {
  166. this.timelyUploadTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
  167. try
  168. {
  169. #region handle server side open trx
  170. //var serverSideOpenTrx = await CloudHelper.Default.GeLastTrxWithOpenStatusByUserNameAsync<ServerSideTrx>();
  171. //if (serverSideOpenTrx != null && (serverSideOpenTrx.Items == null || !serverSideOpenTrx.Items.Any()))
  172. //{
  173. // /*this is a complex case that items didn't get ringup yet though trx had been created, so need restore items from local db, and ringup again, and then commit*/
  174. // logger.LogInformation($"Queried trx data(id: {serverSideOpenTrx.Id}, grossAmount: {serverSideOpenTrx.GrossAmount}) from GeLastTrxWithOpenStatusByUserNameAsync, will re-ringup and re-commit it...");
  175. //}
  176. //if (serverSideOpenTrx != null && serverSideOpenTrx.Items != null && serverSideOpenTrx.Items.Any())
  177. //{
  178. // try
  179. // {
  180. // logger.LogInformation($"Queried trx data(id: {serverSideOpenTrx.Id}, " +
  181. // $"grossAmount: {serverSideOpenTrx.GrossAmount}, " +
  182. // $"item[0]-> itemName:{serverSideOpenTrx.Items.First().Item?.ItemName ?? ""}" +
  183. // $"-barcode:{serverSideOpenTrx.Items.First().Item?.BarCode ?? ""}-seqNo.:{serverSideOpenTrx.Items.First().FuelItemFdcTransactionSeqNo ?? ""}) " +
  184. // $"from GeLastTrxWithOpenStatusByUserNameAsync, will simply re-commit it...");
  185. // var recommitResult = await CloudHelper.Default.CommitTransactionAsync(serverSideOpenTrx.Id, new Dfs.WayneChina.PosModelMini.PosTrxMop()
  186. // {
  187. // Mop = new Dfs.WayneChina.PosModelMini.PosMop()
  188. // {
  189. // PaymentId = (int)PaymentID.PetroChinaIC,
  190. // },
  191. // Paid = serverSideOpenTrx.GrossAmount
  192. // }, null);
  193. // if (recommitResult)
  194. // logger.LogInformation($" re-commit trx with id: {serverSideOpenTrx.Id} succeed");
  195. // else
  196. // logger.LogInformation($" re-commit trx with id: {serverSideOpenTrx.Id} failed gracefully, will do in next round");
  197. // }
  198. // catch (Exception eee)
  199. // {
  200. // logger.LogInformation($" re-commit trx with id: {serverSideOpenTrx.Id} failed with exception: {eee},{Environment.NewLine} will do again in next round");
  201. // }
  202. //}
  203. #endregion
  204. using (var scope = this.services.CreateScope())
  205. {
  206. var dbContext = scope.ServiceProvider.GetRequiredService<SqliteDbContext>();
  207. var unfinishedTrxDatas = await dbContext.GenericDatas.Where(gd =>
  208. gd.Owner == genericDataOwner && gd.Type == genericDataType && gd.State == "Unfinished").OrderBy(gd => gd.CreatedTimeStamp).ToListAsync();
  209. foreach (var d in unfinishedTrxDatas)
  210. {
  211. //ComplexData = JsonSerializer.Serialize(new
  212. //{
  213. //itemId,
  214. //SiteLevelNozzleId = nzlExtraInfo.SiteLevelNozzleId ?? -1,
  215. //Transaction = new
  216. //{
  217. // arg.Transaction.Amount,
  218. // arg.Transaction.Volumn,
  219. // arg.Transaction.SequenceNumberGeneratedOnPhysicalPump,
  220. // arg.Transaction.Nozzle.PumpId,
  221. // arg.Transaction.Nozzle.LogicalId
  222. //}
  223. //})
  224. var jd = JsonDocument.Parse(d.ComplexData);
  225. var itemId = jd.RootElement.GetProperty("itemId").GetString();
  226. int siteLevelNozzleId = jd.RootElement.GetProperty("SiteLevelNozzleId").GetInt32();
  227. int amount = jd.RootElement.GetProperty("Transaction").GetProperty("Amount").GetInt32();
  228. int volume = jd.RootElement.GetProperty("Transaction").GetProperty("Volumn").GetInt32();
  229. int seqNo = jd.RootElement.GetProperty("Transaction").GetProperty("SequenceNumberGeneratedOnPhysicalPump").GetInt32();
  230. int pumpId = jd.RootElement.GetProperty("Transaction").GetProperty("PumpId").GetInt32();
  231. int nozzleLogicalId = jd.RootElement.GetProperty("Transaction").GetProperty("LogicalId").GetInt32();
  232. var uploadResult = await this.UploadTrxToCloud(itemId, amount, volume, seqNo, pumpId, nozzleLogicalId, siteLevelNozzleId);
  233. if (uploadResult)
  234. {
  235. d.State = "Finished";
  236. d.ModifiedTimeStamp = DateTime.Now;
  237. await dbContext.SaveChangesAsync();
  238. }
  239. }
  240. }
  241. }
  242. catch (Exception eeee)
  243. {
  244. //await universalApiHub.FirePersistGenericAlarmIfNotExists(this,
  245. // new GenericAlarm()
  246. // {
  247. // Title = $"上传交易至云端失败",
  248. // Category = $"上传",
  249. // Detail = $"由PumpHandler中产生的交易(pumpId:{arg.Transaction.Nozzle.PumpId}, logicalNozzleId: {arg.Transaction.Nozzle.LogicalId}, vol: {arg.Transaction.Volumn}) 上传至云端失败:{eeee}",
  250. // Severity = GenericAlarmSeverity.Error
  251. // },
  252. // ga => ga.Detail,
  253. // ga => ga.Detail);
  254. return;
  255. }
  256. finally
  257. {
  258. this.timelyUploadTimer.Change(0, 5000);
  259. }
  260. }, null, 0, 5000);
  261. return true;
  262. }
  263. public Task<bool> Stop()
  264. {
  265. this.timelyUploadTimer?.Dispose();
  266. return Task.FromResult(true);
  267. }
  268. }
  269. }