using Applications.FDC; using Edge.Core.Configuration; using Edge.Core.Database; using Edge.Core.Database.Models; using Edge.Core.Processor; using Edge.Core.Processor.Dispatcher.Attributes; using Edge.Core.UniversalApi; using HengShan_Pump_TQC_IFSF; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using System; using System.Collections.Generic; using System.Linq; using System.Text.Json; using System.Threading.Tasks; using VBaoProxyApp.Cloud; namespace Apps.PetroChinaIcCardFuelTrxToSmartFuelCloudUploader { public class AppConfigV1 { public string UserName { get; set; } = "3038"; public string Password { get; set; } = "111111"; /// /// Sample: http://url:8698 /// DO NOT end with slash /// public string ApiGatewayEntryUrlWithoutLastSlash { get; set; } = "http://47.97.120.160:8698"; public string DeviceSN { get; set; } = "66662e74-d51e-42ae-bc89-2d39312c9c30"; } [MetaPartsDescriptor( "中油IC卡交易上传器", "用于将中油IC卡交易上传至SmartFuelCloud,日志标记为DynamicPrivate_PetroChinaIcCardFuelTrxToSmartFuelCloudUploader,它通过直接采集油机端的交易,并过一步判定交易是否由IC卡授权完成,如果是的话,此交易才会被上传至云端." , new[] { "中油" })] public class App : IAppProcessor { private const string genericDataType = "PendingForUploadTrxData"; private IServiceProvider services; private Applications.FDC.FdcServerHostApp fdcServerApp; private AppConfigV1 appConfig; private ILogger logger = NullLogger.Instance; public string MetaConfigName { get; set; } private System.Threading.Timer timelyUploadTimer; [ParamsJsonSchemas("appCtorParamsJsonSchema")] public App(AppConfigV1 appConfig, IServiceProvider services) { this.appConfig = appConfig; var loggerFactory = services.GetRequiredService(); this.logger = loggerFactory.CreateLogger("DynamicPrivate_PetroChinaIcCardFuelTrxToSmartFuelCloudUploader"); this.services = services; CloudHelper.Default.Credential = new CloudCredential() { ApiGatewayEntryUrl = appConfig.ApiGatewayEntryUrlWithoutLastSlash,// "http://wc.shaojun.xyz:8698", UserName = appConfig.UserName,// "507", Password = appConfig.Password,//"111111", DeviceSN = appConfig.DeviceSN,//"1234567890sss", }; } public void Init(IEnumerable processors) { var universalApiHub = this.services.GetRequiredService(); var pumpGroupHandlers = processors.WithHandlerOrApp().SelectHandlerOrAppThenCast(); this.fdcServerApp = processors.WithHandlerOrApp().SelectHandlerOrAppThenCast().First(); foreach (var p in pumpGroupHandlers) p.OnPetroChinaIcCardAuthorizedFuelTrxDone += async (s, arg) => { NozzleExtraInfo nzlExtraInfo = null; nzlExtraInfo = this.fdcServerApp.GetNozzleExtraInfos().FirstOrDefault(ni => ni.PumpId == arg.Transaction.Nozzle.PumpId && ni.NozzleLogicalId == arg.Transaction.Nozzle.LogicalId); if (nzlExtraInfo == null) { await universalApiHub.FirePersistGenericAlarmIfNotExists(this, new GenericAlarm() { Title = $"油枪信息配置错误", Category = $"配置", Detail = $"由PumpHandler中产生的交易(pumpId:{arg.Transaction.Nozzle.PumpId}, logicalNozzleId: {arg.Transaction.Nozzle.LogicalId}, vol: {arg.Transaction.Volumn}) 无法找到与其对应绑定的Fdc nozzle信息,请检查PumpHandler与FdcServerApp中关于加油点和油枪的配置与映射关系", Severity = GenericAlarmSeverity.Error }, ga => ga.Detail, ga => ga.Detail); return; } string itemId = nzlExtraInfo.ProductBarcode.ToString(); GenericData data = new GenericData() { CreatedTimeStamp = DateTime.Now, Owner = this.GetType().FullName, Type = genericDataType, State = "Unfinished", ComplexData = JsonSerializer.Serialize(new { itemId, SiteLevelNozzleId = nzlExtraInfo.SiteLevelNozzleId ?? -1, Transaction = new { arg.Transaction.Amount, arg.Transaction.Volumn, arg.Transaction.SequenceNumberGeneratedOnPhysicalPump, arg.Transaction.Nozzle.PumpId, arg.Transaction.Nozzle.LogicalId } }) }; using (var scope = this.services.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService(); dbContext.GenericDatas.Add(data); await dbContext.SaveChangesAsync(); } }; } [UniversalApi] public async Task UploadTrxToCloud(string itemId, decimal amount, decimal volume, int pumpSideTrxSeqNo, int pumpId, int fdcNozzleLogicalId, int siteLevelNozzleId) { var postItem = await CloudHelper.Default.GetPosItemAsync(itemId); if (postItem == null) throw new InvalidOperationException($"Could not find PosItem(itemId: {itemId}) from cloud side"); var createdTrxId = await CloudHelper.Default.CreateTransactionAsync(new ClientFuelTrxInfo() { PosItemId = postItem.Id, PumpId = pumpId, NozzleId = fdcNozzleLogicalId, SiteNozzleNo = siteLevelNozzleId, Amount = amount, Volume = volume, SeqNo = pumpSideTrxSeqNo, Barcode = int.Parse(postItem.BarCode), PayAmount = amount, Source = Dfs.WayneChina.PosModelMini.PosTrxSource.Outdoor, UnitPrice = 4 }, "中油TQC油机上的IC卡交易"); var commitResult = await CloudHelper.Default.CommitTransactionAsync(createdTrxId, new Dfs.WayneChina.PosModelMini.PosTrxMop() { Mop = new Dfs.WayneChina.PosModelMini.PosMop() { PaymentId = (int)PaymentID.PetroChinaIC, }, Paid = amount }, null); return true; } public async Task Start() { //var extraInfos = this.fdcServerApp.GetNozzleExtraInfos(); //var fuelItemBarcodes = extraInfos.GroupBy(i => i.ProductBarcode).Select(g => g.Key); //foreach (var barcode in fuelItemBarcodes) //{ // try // { // var cloudFuelItem = await CloudHelper.Default.GetPosItemAsync(barcode.ToString()); // if (cloudFuelItem == null) // throw new InvalidOperationException($"Local fuel item with barcode: {barcode} could not find mapping item in cloud side."); // } // catch (Exception exxx) // { // this.logger.LogError($"Failed to retrieve Cloud side fuel item based on local barcode: {barcode}, make sure local fuel items are all" + // $" correctly set and mapped(by their item id) in cloud side. error detail: {exxx}"); // throw; // } //} var universalApiHub = this.services.GetRequiredService(); var genericDataOwner = this.GetType().FullName; this.timelyUploadTimer = new System.Threading.Timer(async (_) => { this.timelyUploadTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); try { #region handle server side open trx //var serverSideOpenTrx = await CloudHelper.Default.GeLastTrxWithOpenStatusByUserNameAsync(); //if (serverSideOpenTrx != null && (serverSideOpenTrx.Items == null || !serverSideOpenTrx.Items.Any())) //{ // /*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*/ // logger.LogInformation($"Queried trx data(id: {serverSideOpenTrx.Id}, grossAmount: {serverSideOpenTrx.GrossAmount}) from GeLastTrxWithOpenStatusByUserNameAsync, will re-ringup and re-commit it..."); //} //if (serverSideOpenTrx != null && serverSideOpenTrx.Items != null && serverSideOpenTrx.Items.Any()) //{ // try // { // logger.LogInformation($"Queried trx data(id: {serverSideOpenTrx.Id}, " + // $"grossAmount: {serverSideOpenTrx.GrossAmount}, " + // $"item[0]-> itemName:{serverSideOpenTrx.Items.First().Item?.ItemName ?? ""}" + // $"-barcode:{serverSideOpenTrx.Items.First().Item?.BarCode ?? ""}-seqNo.:{serverSideOpenTrx.Items.First().FuelItemFdcTransactionSeqNo ?? ""}) " + // $"from GeLastTrxWithOpenStatusByUserNameAsync, will simply re-commit it..."); // var recommitResult = await CloudHelper.Default.CommitTransactionAsync(serverSideOpenTrx.Id, new Dfs.WayneChina.PosModelMini.PosTrxMop() // { // Mop = new Dfs.WayneChina.PosModelMini.PosMop() // { // PaymentId = (int)PaymentID.PetroChinaIC, // }, // Paid = serverSideOpenTrx.GrossAmount // }, null); // if (recommitResult) // logger.LogInformation($" re-commit trx with id: {serverSideOpenTrx.Id} succeed"); // else // logger.LogInformation($" re-commit trx with id: {serverSideOpenTrx.Id} failed gracefully, will do in next round"); // } // catch (Exception eee) // { // logger.LogInformation($" re-commit trx with id: {serverSideOpenTrx.Id} failed with exception: {eee},{Environment.NewLine} will do again in next round"); // } //} #endregion using (var scope = this.services.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService(); var unfinishedTrxDatas = await dbContext.GenericDatas.Where(gd => gd.Owner == genericDataOwner && gd.Type == genericDataType && gd.State == "Unfinished").OrderBy(gd => gd.CreatedTimeStamp).ToListAsync(); foreach (var d in unfinishedTrxDatas) { //ComplexData = JsonSerializer.Serialize(new //{ //itemId, //SiteLevelNozzleId = nzlExtraInfo.SiteLevelNozzleId ?? -1, //Transaction = new //{ // arg.Transaction.Amount, // arg.Transaction.Volumn, // arg.Transaction.SequenceNumberGeneratedOnPhysicalPump, // arg.Transaction.Nozzle.PumpId, // arg.Transaction.Nozzle.LogicalId //} //}) var jd = JsonDocument.Parse(d.ComplexData); var itemId = jd.RootElement.GetProperty("itemId").GetString(); int siteLevelNozzleId = jd.RootElement.GetProperty("SiteLevelNozzleId").GetInt32(); int amount = jd.RootElement.GetProperty("Transaction").GetProperty("Amount").GetInt32(); int volume = jd.RootElement.GetProperty("Transaction").GetProperty("Volumn").GetInt32(); int seqNo = jd.RootElement.GetProperty("Transaction").GetProperty("SequenceNumberGeneratedOnPhysicalPump").GetInt32(); int pumpId = jd.RootElement.GetProperty("Transaction").GetProperty("PumpId").GetInt32(); int nozzleLogicalId = jd.RootElement.GetProperty("Transaction").GetProperty("LogicalId").GetInt32(); var uploadResult = await this.UploadTrxToCloud(itemId, amount, volume, seqNo, pumpId, nozzleLogicalId, siteLevelNozzleId); if (uploadResult) { d.State = "Finished"; d.ModifiedTimeStamp = DateTime.Now; await dbContext.SaveChangesAsync(); } } } } catch (Exception eeee) { //await universalApiHub.FirePersistGenericAlarmIfNotExists(this, // new GenericAlarm() // { // Title = $"上传交易至云端失败", // Category = $"上传", // Detail = $"由PumpHandler中产生的交易(pumpId:{arg.Transaction.Nozzle.PumpId}, logicalNozzleId: {arg.Transaction.Nozzle.LogicalId}, vol: {arg.Transaction.Volumn}) 上传至云端失败:{eeee}", // Severity = GenericAlarmSeverity.Error // }, // ga => ga.Detail, // ga => ga.Detail); return; } finally { this.timelyUploadTimer.Change(0, 5000); } }, null, 0, 5000); return true; } public Task Stop() { this.timelyUploadTimer?.Dispose(); return Task.FromResult(true); } } }