123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- using Edge.Core.Processor;
- using Edge.Core.Processor.Dispatcher.Attributes;
- using Edge.Core.UniversalApi;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Logging;
- using Microsoft.Extensions.Logging.Abstractions;
- using System;
- using System.Collections.Concurrent;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using VBaoProxyApp.Cloud;
- namespace VBaoProxyApp
- {
- [UniversalApi(Name = OnFuelTrxDoneEventName, EventDataType = typeof(object), Description = "使用短码授权加油的用户完成加油时,将触发此事件")]
- [UniversalApi(Name = "OnRemotePaymentFailed", EventDataType = typeof(object), Description = "当与云端进行支付流程进行中,且失败了,将触发此事件")]
- [MetaPartsDescriptor(
- "V宝车载定位",
- "用于与V宝车载定位站级系统的对接",
- new[] { "V宝" })]
- public class App : IAppProcessor
- {
- public const string OnFuelTrxDoneEventName = "OnFuelTrxDone";
- private IServiceProvider services;
- private Applications.FDC.FdcServerHostApp fdcServerApp;
- private AppConfigV1 appConfig;
- private TimeSpan InRangeBeaconLifeTime = new TimeSpan(0, 1, 30);
- private ILogger logger = NullLogger.Instance;
- private object syncObject = new object();
- public class AppConfigV1
- {
- public string UserName { get; set; } = "3038";
- public string Password { get; set; } = "111111";
-
-
-
-
- public string ApiGatewayEntryUrlWithoutLastSlash { get; set; } = "http://47.97.120.160:8698";
- public string DeviceSN { get; set; } = "66662e74-d51e-42ae-bc89-2d39312c9c30";
- }
- public class InRangeBeacon
- {
- public string BeaconId { get; set; }
- public DateTime Timestamp { get; set; }
- public string AuthCode { get; set; }
- }
- private ConcurrentDictionary<string, InRangeBeacon> inRangeBeacons = new ConcurrentDictionary<string, InRangeBeacon>();
- public string MetaConfigName { get; set; }
- public void Init(IEnumerable<IProcessor> processors)
- {
- this.fdcServerApp =
- processors.WithHandlerOrApp<Applications.FDC.FdcServerHostApp>().SelectHandlerOrAppThenCast<Applications.FDC.FdcServerHostApp>().FirstOrDefault();
- if (this.fdcServerApp == null)
- {
- this.logger.LogInformation("There's no FdcServerHostApp defined");
- throw new ArgumentException("Must provide FdcServerHostApp");
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- public App(AppConfigV1 appConfig, IServiceProvider services)
- {
- this.appConfig = appConfig;
- var loggerFactory = services.GetRequiredService<ILoggerFactory>();
- this.logger = loggerFactory.CreateLogger("DynamicPrivate_VBaoProxyApp");
- this.services = services;
- CloudHelper.Default.Credential = new CloudCredential()
- {
- ApiGatewayEntryUrl = appConfig.ApiGatewayEntryUrlWithoutLastSlash,
- UserName = appConfig.UserName,
- Password = appConfig.Password,
- DeviceSN = appConfig.DeviceSN,
- };
- }
- public async Task<bool> 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;
- }
- }
- return true;
- }
- public Task<bool> Stop() { return Task.FromResult(true); }
- [UniversalApi(Description = "报告用户信标进入了感应范围,并获取验证码短码")]
- public async Task<InRangeBeacon> NotifyBeaconInRange(string userBeaconId)
- {
- this.logger.LogDebug($"NotifyBeaconInRange: {userBeaconId}");
- try
- {
- if (!this.inRangeBeacons.TryGetValue(userBeaconId, out InRangeBeacon exist)
- || (DateTime.Now.Subtract(exist.Timestamp) > this.InRangeBeaconLifeTime))
- {
-
- if (exist != null)
- {
- this.logger.LogDebug($" removing an expired one...");
- this.inRangeBeacons.TryRemove(userBeaconId, out _);
- }
- string authCode = null;
- while (true)
- {
- authCode = new Random().Next(0, 9999).ToString().PadLeft(4, '0');
- if (this.inRangeBeacons.Values.Any(i => i.AuthCode == authCode))
- continue;
- break;
- }
- var info = new InRangeBeacon()
- {
- BeaconId = userBeaconId,
- Timestamp = DateTime.Now,
- AuthCode = authCode,
- };
- this.logger.LogDebug($" adding one with beaconId: {userBeaconId}, authCode: {authCode}");
- this.inRangeBeacons.TryAdd(userBeaconId, info);
- return info;
-
-
- }
- else
- return exist;
- }
- finally
- {
-
- }
- }
- [UniversalApi(Description = "验证短码是否有效")]
- public async Task<object> ValidateAuthCode(string authCode)
- {
- bool lockTaken = false;
- try
- {
- var exist = this.inRangeBeacons.FirstOrDefault(i => i.Value.AuthCode == authCode);
- if (exist.Value != null && (DateTime.Now.Subtract(exist.Value.Timestamp) <= this.InRangeBeaconLifeTime))
- {
- var accountResult = await CloudHelper.Default.GetAccountIdByVBaoBeaconIdAsync(exist.Value.BeaconId);
- var accountId = accountResult.GetProperty("userId").GetString();
- var vehicleLicenseNo = accountResult.GetProperty("vehicleLicenseNo").GetString();
- if (string.IsNullOrEmpty(accountId))
- {
- return new
- {
- OverallResult = false,
- FailedReason = $"Could not find account id from remote by vbao beaconId: {exist.Value.BeaconId}, need pre-regist."
- };
- }
- return new
- {
- OverallResult = true,
- VehicleLicenseNo = vehicleLicenseNo,
- };
- }
- if (exist.Value != null)
- {
- this.logger.LogDebug($"Validating authCode: {authCode}, but it was expired.");
- this.inRangeBeacons.TryRemove(exist.Key, out _);
- }
- this.logger.LogDebug($"Validating authCode: {authCode} with failed.");
- return new
- {
- OverallResult = false,
- FailedReason = "无效的短码"
- };
- }
- catch (Exception eeee)
- {
- return new
- {
- OverallResult = false,
- FailedReason = $"Unknown reason for retrieve the car plate No. from remote, detail: {eeee}",
- };
- }
- finally
- {
-
- }
- }
- [UniversalApi(Description = "使用掉authcode,并提供相关联的fuel trx release token")]
- public async Task<object> NotifyAuthCodeConsumedWithFuelTrx(string authCode, int fuelTrxReleaseToken)
- {
- bool lockTaken = false;
- try
- {
-
- var exist = this.inRangeBeacons.FirstOrDefault(i => i.Value.AuthCode == authCode);
- if (exist.Value == null)
- {
- this.logger.LogDebug($"Consuming authCode: {authCode} failed due to no such auth code.");
- return new
- {
- OverallResult = false,
- FailedReason = $"Consuming authCode: {authCode} failed due to no such auth code."
- };
- }
- this.logger.LogDebug($"Consuming authCode: {authCode}, then GetFuelSaleTrxDetails for releaseToken: {fuelTrxReleaseToken}");
- var trxes = await this.fdcServerApp.GetFuelSaleTrxDetailsAsync(new ApiData()
- {
- Parameters = new List<ApiDataParameter>() {
- new ApiDataParameter()
- {
- Name="releasetoken",
- Value = fuelTrxReleaseToken.ToString()
- }
- }
- });
- if (trxes == null || !trxes.Any())
- {
-
- this.logger.LogDebug($"Consuming authCode: {authCode} failed due to no such fule trx in local.");
- return new
- {
- OverallResult = false,
- FailedReason = $"Consuming authCode: {authCode} partial failed(code consumed) due to no such fule trx."
- };
- }
- var universalApiHub = this.services.GetRequiredService<UniversalApiHub>();
- var pumpId = int.Parse(trxes.First().GetType().GetProperty("PumpId").GetValue(trxes.First()).ToString());
- var logicalNozzleId = int.Parse(trxes.First().GetType().GetProperty("LogicalNozzleId").GetValue(trxes.First()).ToString());
- var trxSeqNoFromPhysicalPump = int.Parse(trxes.First().GetType().GetProperty("TransactionSeqNumberFromPhysicalPump").GetValue(trxes.First()).ToString());
- var fuelItemBarcode = trxes.First().GetType().GetProperty("ProductBarcode").GetValue(trxes.First()).ToString();
- var amount = decimal.Parse(trxes.First().GetType().GetProperty("Amount").GetValue(trxes.First()).ToString());
- var volume = decimal.Parse(trxes.First().GetType().GetProperty("Volume").GetValue(trxes.First()).ToString());
- var price = decimal.Parse(trxes.First().GetType().GetProperty("Price").GetValue(trxes.First()).ToString());
- var siteLevelNozzleId = int.Parse(trxes.First().GetType().GetProperty("SiteLevelNozzleId").GetValue(trxes.First()).ToString());
- this.logger.LogDebug($" local fuel trx details-> pumpId: {pumpId}, logicalNozzleId: {logicalNozzleId}, amount: {amount}, volume: {volume}");
- var fdcClientId = 987654;
- var lockedFuelTrx = await this.fdcServerApp.LockFuelSaleTrxAndNotifyAllFdcClientsAsync(
- fdcClientId, pumpId, trxSeqNoFromPhysicalPump, fuelTrxReleaseToken);
- if (lockedFuelTrx == null)
- {
- this.logger.LogError($"Local locking trx with releaseToken: {fuelTrxReleaseToken} failed.");
- await universalApiHub.FireEvent(this, "OnRemotePaymentFailed", new
- {
- FuelTrx = trxes.FirstOrDefault(),
- FailedReason = $"Local fuel trx with releaseToken: {fuelTrxReleaseToken} locking failed."
- });
- return new
- {
- OverallResult = false,
- FailedReason = $"Local fuel trx with releaseToken: {fuelTrxReleaseToken} locking failed."
- };
- }
- var cloudFuelItem = await CloudHelper.Default.GetPosItemAsync(fuelItemBarcode);
- if (cloudFuelItem == null)
- {
- this.logger.LogError($"Retreieve cloud pos item failed for fuel trx with releaseToken: {fuelTrxReleaseToken} with fuel item barcode: {fuelItemBarcode}");
- await universalApiHub.FireEvent(this, "OnRemotePaymentFailed", new
- {
- FuelTrx = trxes.FirstOrDefault(),
- FailedReason = $"Local fuel item with barcode: {fuelItemBarcode} could not find mapping item in cloud side."
- });
- return new
- {
- OverallResult = false,
- FailedReason = $"Local fuel item with barcode: {fuelItemBarcode} could not find mapping item in cloud side."
- };
- }
- var createdTrxId = await CloudHelper.Default.CreateTransactionAsync(new ClientFuelTrxInfo()
- {
- PosItemId = cloudFuelItem.Id,
- PumpId = pumpId,
- NozzleId = logicalNozzleId,
- SiteNozzleNo = siteLevelNozzleId,
- Amount = amount,
- Volume = volume,
- Barcode = int.Parse(cloudFuelItem.BarCode),
- PayAmount = amount,
- Source = Dfs.WayneChina.PosModelMini.PosTrxSource.Outdoor,
- UnitPrice = price
- }, "vBao pay");
- var accountResult = await CloudHelper.Default.GetAccountIdByVBaoBeaconIdAsync(exist.Value.BeaconId);
- var accountId = accountResult.GetProperty("userId").GetString();
- var vehicleLicenseNo = accountResult.GetProperty("vehicleLicenseNo").GetString();
- if (string.IsNullOrEmpty(accountId))
- {
- await universalApiHub.FireEvent(this, "OnRemotePaymentFailed", new
- {
- FuelTrx = trxes.FirstOrDefault(),
- FailedReason = $"Could not find account id from remote by vbao beaconId: {exist.Value.BeaconId}, need pre-regist."
- });
- return new
- {
- OverallResult = false,
- FailedReason = $"Could not find account id from remote by vbao beaconId: {exist.Value.BeaconId}, need pre-regist."
- };
- }
- var commitResult = await CloudHelper.Default.CommitTransactionAsync(createdTrxId, new Dfs.WayneChina.PosModelMini.PosTrxMop()
- {
- Mop = new Dfs.WayneChina.PosModelMini.PosMop()
- {
- PaymentId = (int)PaymentID.MembershipPay,
- },
- Paid = amount
- }, accountId);
- if (!commitResult)
- {
- this.logger.LogError($"Commit trx to cloud failed for fuel trx with releaseToken: {fuelTrxReleaseToken}");
- await universalApiHub.FireEvent(this, "OnRemotePaymentFailed", new
- {
- FuelTrx = trxes.FirstOrDefault(),
- FailedReason = $"Commit trx to remote failed with MembershipPay, reason unknown."
- });
- return new
- {
- OverallResult = false,
- FailedReason = $"Commit trx to remote failed with MembershipPay, reason unknown."
- };
- }
- var clearedFuelTrx = await this.fdcServerApp.ClearFuelSaleTrxAndNotifyAllFdcClientsAsync(
- pumpId, trxSeqNoFromPhysicalPump.ToString(), fuelTrxReleaseToken, fdcClientId.ToString());
- if (clearedFuelTrx == null)
- {
- this.logger.LogError($"Clear local fuel trx failed for fuel trx with releaseToken: {fuelTrxReleaseToken}");
- await universalApiHub.FireEvent(this, "OnRemotePaymentFailed", new
- {
- FuelTrx = trxes.FirstOrDefault(),
- FailedReason = $"Local fuel trx with releaseToken: {fuelTrxReleaseToken} clear failed."
- });
- return new
- {
- OverallResult = false,
- FailedReason = $"Local fuel trx with releaseToken: {fuelTrxReleaseToken} clear failed."
- };
- }
- await universalApiHub.FireEvent(this, OnFuelTrxDoneEventName, new
- {
- InRangeBeacon = new { exist.Value.BeaconId, exist.Value.Timestamp, exist.Value.AuthCode },
- FuelTrx = trxes.FirstOrDefault()
- });
- this.inRangeBeacons.TryRemove(exist.Key, out _);
- this.logger.LogInformation($"Succeed finished a fuel trx payment with cloud and local with releaseToken: {fuelTrxReleaseToken}");
- return new
- {
- OverallResult = true,
- VehicleLicenseNo = vehicleLicenseNo,
-
- };
- }
- catch (Exception eeee)
- {
- return new
- {
- OverallResult = false,
- FailedReason = $"Unknown reason to failed the payment from remote, detail: {eeee}",
- };
- }
- finally
- {
-
- }
- }
- }
- }
|