فهرست منبع

feat:预支付功能

Zhenghanjv 3 ماه پیش
والد
کامیت
aa0f5917c1
36فایلهای تغییر یافته به همراه1613 افزوده شده و 334 حذف شده
  1. 24 0
      Edge.Core/Configuration/MqttConfiguration.cs
  2. 15 0
      Edge.Core/Domain/FccOrderInfo/FccOrderInfo.cs
  3. 36 0
      Edge.Core/Domain/FccOrderInfo/Output/OrderInfoOutput.cs
  4. 162 0
      Edge.Core/Domain/FccOrderInfo/Output/PrepayOrderInfoOutput.cs
  5. 4 0
      Edge.Core/Domain/FccStationInfo/FccStationInfo.cs
  6. 7 1
      Edge.Core/Domain/FccStationInfo/Input/StationInfoInput.cs
  7. 6 0
      Edge.Core/Domain/FccStationInfo/Output/StationInfoOutput.cs
  8. 10 0
      Edge.Core/HttpClient/HttpClientService.cs
  9. 2 0
      Edge.Core/HttpClient/IHttpClient.cs
  10. 14 5
      Edge.Core/MqttClient/MqttClientService.cs
  11. 1 1
      Edge.Core/Parser/BinaryParser/ParserBase.cs
  12. 5 3
      Edge.Core/Processor/GenericDeviceProcessor.cs
  13. 1 1
      Edge.Core/Processor/IDeviceHandler.cs
  14. 16 9
      Edge.Core/Processor/Outgoing/Outgoing.cs
  15. 336 258
      HengshanPaymentTerminal/HengshanPayTermHandler.cs
  16. 15 3
      HengshanPaymentTerminal/Http/HttpClientUtils.cs
  17. 8 1
      HengshanPaymentTerminal/Http/IHttpClientUtil.cs
  18. 8 2
      HengshanPaymentTerminal/Http/Request/HttpRequest.cs
  19. 34 10
      HengshanPaymentTerminal/Http/Response/Response.cs
  20. 6 1
      HengshanPaymentTerminal/MessageEntity/CommonMessage.cs
  21. 15 2
      HengshanPaymentTerminal/MessageEntity/Incoming/OrderFromMachine.cs
  22. 3 0
      HengshanPaymentTerminal/MessageEntity/Outgoing/SendQrCode.cs
  23. 91 0
      HengshanPaymentTerminal/MessageEntity/Outgoing/SendRefund.cs
  24. 55 25
      HengshanPaymentTerminal/Mqtt/Request/MqttRequest.cs
  25. 18 0
      src/FccLife.Web/Controller/OrderInfoController.cs
  26. 4 4
      src/FccLife.Web/Repositories/FccOrderInfo/OrderInfoReposity.cs
  27. 1 0
      src/FccLife.Web/Repositories/FccStationInfo/StationRepository.cs
  28. 15 1
      src/FccLife.Web/Services/FccOrderInfo/IOrderInfoService.cs
  29. 102 2
      src/FccLife.Web/Services/FccOrderInfo/OrderInfoService.cs
  30. 5 0
      src/FccLife.Web/appsettings.Production.json
  31. 19 1
      src/FccWeb/admin.ui.plus-master/src/api/api.ts
  32. 16 1
      src/FccWeb/admin.ui.plus-master/src/router/route.ts
  33. 9 1
      src/FccWeb/admin.ui.plus-master/src/views/admin/components/addStation.vue
  34. 114 0
      src/FccWeb/admin.ui.plus-master/src/views/admin/components/showOrderTip.vue
  35. 2 2
      src/FccWeb/admin.ui.plus-master/src/views/admin/order/index.vue
  36. 434 0
      src/FccWeb/admin.ui.plus-master/src/views/admin/prepayOrder/index.vue

+ 24 - 0
Edge.Core/Configuration/MqttConfiguration.cs

@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Edge.Core.Configuration
+{
+    /**
+     * "Mqtt": {
+    "subTopic": [ "" ],
+    "user": "HSClient",
+    "password": "HS202503"
+  }
+     */
+    class MqttConfiguration
+    {
+        public string[] subTopic {  get; set; }
+
+        public string user {  get; set; }
+
+        public string password { get; set; }
+    }
+}

+ 15 - 0
Edge.Core/Domain/FccOrderInfo/FccOrderInfo.cs

@@ -116,6 +116,21 @@ namespace Edge.Core.Domain.FccOrderInfo
         /// 泵码
         /// </summary>
         public decimal? PumpCode { get; set; }
+
+        /// <summary>
+        /// 授权状态:0:未授权;1:已授权
+        /// </summary>
+        public int AuthorizationStatus { get; set; } = 0;
+
+        /// <summary>
+        /// 退款状态:0:无退款;1:全额退款;2:部分退款
+        /// </summary>
+        public int RefundStatus { get; set; } = 0;
+
+        /// <summary>
+        /// 预定升数
+        /// </summary>
+        public decimal? VolumePayable { get; set; }
     }
 
 }

+ 36 - 0
Edge.Core/Domain/FccOrderInfo/Output/OrderInfoOutput.cs

@@ -46,6 +46,9 @@ namespace Edge.Core.Domain.FccOrderInfo.Output
             this.UserName = fccOrderInfo.UserName;
             this.PhoneNumber = fccOrderInfo.PhoneNumber;
             this.PaymentName = fccOrderInfo.PaymentName;
+            this.AuthorizationStatus = fccOrderInfo.AuthorizationStatus;
+            this.RefundStatus = fccOrderInfo.RefundStatus;
+            this.VolumePayable = fccOrderInfo.VolumePayable;
         }
 
         /// <summary>
@@ -149,6 +152,39 @@ namespace Edge.Core.Domain.FccOrderInfo.Output
         /// </summary>
         public string PaymentName { get; set; }
 
+        /// <summary>
+        /// 授权状态
+        /// </summary>
+        public int AuthorizationStatus { get; set; }
+
+        /// <summary>
+        /// 退款状态
+        /// </summary>
+        public int RefundStatus { get; set; }
+
+        /// <summary>
+        /// 预定升数
+        /// </summary>
+        public decimal? VolumePayable { get; set; }
+
+        /// <summary>
+        /// 获取支付状态
+        /// </summary>
+        private Dictionary<int,string> paystatusValue = new Dictionary<int, string>() { {0,"未支付"}, { 1, "已支付" }, { 2, "订单正在支付中" }, { 3, "订单通过卡支付" }, { 4, "订单已完成" }, { 5, "已取消" } };
+        public string GetPaystatus()
+        {
+            return paystatusValue[PaymentStatus];
+        }
+
+        /// <summary>
+        /// 获取授权状态
+        /// </summary>
+        private Dictionary<int, string> authorizationValue = new Dictionary<int, string>() { { 0, "未授权" }, { 1, "授权" }, { 2, "等待授权" }, { 3, "加油中" }, { 4, "已完成" } };
+        public string GetAuthorizationStatus()
+        {
+            return authorizationValue[AuthorizationStatus];
+        }
+
         public FccOrderInfo ToComponent()
         {
             return new FccOrderInfo

+ 162 - 0
Edge.Core/Domain/FccOrderInfo/Output/PrepayOrderInfoOutput.cs

@@ -0,0 +1,162 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Edge.Core.Domain.FccOrderInfo.Output
+{
+    /// <summary>
+    /// FCC预支付订单页对象
+    /// </summary>
+    public class PrepayOrderInfoOutput
+    {
+        /// <summary>
+        /// 总数
+        /// </summary>
+        public int Total { get; set; }
+
+        /// <summary>
+        /// 订单
+        /// </summary>
+        public List<PrepayOrderInfo> List { get; set; }
+    }
+
+    /// <summary>
+    /// fcc 预支付订单页数据对象
+    /// </summary>
+    public class PrepayOrderInfo
+    {
+        public PrepayOrderInfo(OrderInfo order)
+        {
+            this.StationName = order.StationName;
+            this.MachineName = order.MachineName;
+
+            this.Id = order.Id;
+            this.Ttc = order.Ttc;
+            this.AuthorizationTime = order.AuthorizationTime;
+            this.EndTime = order.EndTime;
+            this.PaymentTime = order.PaymentTime;
+            this.NozzleNum = order.NozzleNum;
+            this.OilName = order.OilName;
+            this.PaymentStatus = order.GetPaystatus();
+            this.CloundOrderId = order.CloundOrderId;
+            this.Amount = order.Amount;
+            this.Volume = order.Volume;
+            this.AmountPayable = order.AmountPayable;
+            this.Price = order.Price;
+            this.RefundAmount = order.RefundAmount;
+            this.UserName = order.UserName;
+            this.PhoneNumber = order.PhoneNumber;
+            this.PaymentName = order.PaymentName;
+            this.AuthorizationStatus = order.GetAuthorizationStatus();
+            this.VolumePayable = order.VolumePayable;
+        }
+
+        /// <summary>
+        /// 油站名
+        /// </summary>
+        public string StationName { get; set; }
+
+        /// <summary>
+        /// 油机号
+        /// </summary>
+        public string MachineName { get; set; }
+
+        /// <summary>
+        /// 订单id
+        /// </summary>
+        public long Id { get; set; }
+
+        /// <summary>
+        /// 流水号
+        /// </summary>
+        public int Ttc { get; set; }
+
+        /// <summary>
+        /// 授权时间
+        /// </summary>
+        public DateTime AuthorizationTime { get; set; }
+
+        /// <summary>
+        /// 挂枪时间
+        /// </summary>
+        public DateTime? EndTime { get; set; }
+
+        /// <summary>
+        /// 交易时间
+        /// </summary>
+        public DateTime? PaymentTime { get; set; }
+
+        /// <summary>
+        /// 枪号
+        /// </summary>
+        public int NozzleNum { get; set; }
+
+        /// <summary>
+        /// 油品名
+        /// </summary>
+        public String OilName { get; set; }
+
+        /// <summary>
+        /// 云端id
+        /// </summary>
+        public long? CloundOrderId { get; set; }
+
+        /// <summary>
+        /// 实际加油金额
+        /// </summary>
+        public decimal Amount { get; set; }
+
+        /// <summary>
+        /// 实际加油升数
+        /// </summary>
+        public decimal Volume { get; set; }
+
+        /// <summary>
+        /// 实际支付金额
+        /// </summary>
+        public decimal? AmountPayable { get; set; }
+
+        /// <summary>
+        /// 单价
+        /// </summary>
+        public decimal Price { get; set; }
+
+        /// <summary>
+        /// 退款金额
+        /// </summary>
+        public decimal? RefundAmount { get; set; }
+
+        /// <summary>
+        /// 用户名
+        /// </summary>
+        public string UserName { get; set; }
+
+
+        /// <summary>
+        /// 手机号
+        /// </summary>
+        public string PhoneNumber { get; set; }
+
+        /// <summary>
+        /// 支付类型名称
+        /// </summary>
+        public string PaymentName { get; set; }
+
+        /// <summary>
+        /// 预定升数
+        /// </summary>
+        public decimal? VolumePayable { get; set; }
+
+        /// <summary>
+        /// 授权状态
+        /// </summary>
+        public string AuthorizationStatus { get; set; }
+
+        /// <summary>
+        /// 支付状态
+        /// </summary>
+        public string PaymentStatus { get; set; }
+    }
+}

+ 4 - 0
Edge.Core/Domain/FccStationInfo/FccStationInfo.cs

@@ -74,6 +74,10 @@ namespace Edge.Core.Domain.FccStationInfo
         /// </summary>
         public string PaymentType { get; set; }
 
+        /// <summary>
+        /// 检查订单时间间隔
+        /// </summary>
+        public int CheckOrderInterval {  get; set; }
         public ICollection<FccTankInfo.FccTankInfo> FccTankInfo { get; set; }
     }
 }

+ 7 - 1
Edge.Core/Domain/FccStationInfo/Input/StationInfoInput.cs

@@ -69,6 +69,11 @@ namespace Edge.Core.Domain.FccStationInfo.Input
         /// </summary>
         public string PaymentType { get; set; }
 
+        /// <summary>
+        /// 检查订单时间间隔
+        /// </summary>
+        public int CheckOrderInterval { get; set; }
+
         public FccStationInfo? ToComponent()
         {
             if (IsNotNorm()) return null;
@@ -86,7 +91,8 @@ namespace Edge.Core.Domain.FccStationInfo.Input
                 MqttService = this.MqttService,
                 IcardService = this.IcardService != null ? this.IcardService : "",
                 WebSocketPort = this.WebSocketPort,
-                PaymentType = this.PaymentType
+                PaymentType = this.PaymentType,
+                CheckOrderInterval = this.CheckOrderInterval,
             };
         }
 

+ 6 - 0
Edge.Core/Domain/FccStationInfo/Output/StationInfoOutput.cs

@@ -37,6 +37,7 @@ namespace Edge.Core.Domain.FccStationInfo.Output
             this.IcardService = fccStationInfo.IcardService;
             this.WebSocketPort = fccStationInfo.WebSocketPort;
             this.PaymentType = fccStationInfo.PaymentType;
+            this.CheckOrderInterval = fccStationInfo.CheckOrderInterval;
         }
         /// <summary>
         /// id
@@ -102,5 +103,10 @@ namespace Edge.Core.Domain.FccStationInfo.Output
         /// 支付方式,【id+支付名称,id+支付名称】
         /// </summary>
         public string PaymentType { get; set; }
+
+        /// <summary>
+        /// 检查订单时间间隔
+        /// </summary>
+        public int CheckOrderInterval { get; set; }
     }
 }

+ 10 - 0
Edge.Core/HttpClient/HttpClientService.cs

@@ -55,6 +55,15 @@ namespace Edge.Core.HttpClient
             return await httpClient.PostAsync(api, parmaJson);
         }
 
+        public async Task<HttpResponseMessage> PostAsyncByParams(string api, Dictionary<string, string>? parmas)
+        {
+            System.Net.Http.HttpClient httpClient = await GetHttpClient(null, parmas);
+
+            FormUrlEncodedContent? formUrlEncodedContent = null;
+            if (parmas != null) formUrlEncodedContent = new FormUrlEncodedContent(parmas);
+            return await httpClient.PostAsync(api, formUrlEncodedContent);
+        }
+
         public async Task<HttpResponseMessage> PutAsync(string api, StringContent? parmaJson)
         {
             System.Net.Http.HttpClient httpClient = await GetHttpClient(parmaJson, null);
@@ -124,5 +133,6 @@ namespace Edge.Core.HttpClient
             string encryBase64 = Convert.ToBase64String(encrySignBytes);
             return encryBase64;
         }
+
     }
 }

+ 2 - 0
Edge.Core/HttpClient/IHttpClient.cs

@@ -10,6 +10,8 @@ namespace Edge.Core.HttpClient
     {
         Task<HttpResponseMessage> PostAsync(string api, StringContent? parmaJson);
 
+        Task<HttpResponseMessage> PostAsyncByParams(string api, Dictionary<string, string>? parmas);
+
         Task<HttpResponseMessage> GetAsync(string api, Dictionary<string,string> parmas);
 
         Task<HttpResponseMessage> PutAsync(string api, StringContent? parmaJson);

+ 14 - 5
Edge.Core/MqttClient/MqttClientService.cs

@@ -1,4 +1,6 @@
-using Edge.Core.Core.database;
+using Edge.Core.Configuration;
+using Edge.Core.Core.database;
+using Microsoft.Extensions.Configuration;
 using MQTTnet;
 using MQTTnet.Client;
 using Newtonsoft.Json;
@@ -15,8 +17,13 @@ namespace Edge.Core.MqttClient
         private static NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
 
         private string[] topics;//需要订阅的主题
+        private string? buildId;
         private IMqttClient _mqttClient; //mqtt 客户端
-        public MqttClientService() { }
+        private IConfiguration _configuration;
+        public MqttClientService(IConfiguration configuration)
+        {
+            _configuration = configuration;
+        }
 
         public event EventHandler<MqttClientConnectedEventArgs> OnConnect;
         public event EventHandler<MqttClientDisconnectedEventArgs> OnDisconnect;
@@ -29,11 +36,12 @@ namespace Edge.Core.MqttClient
 
         public void Start()
         {
+            MqttConfiguration? mqttConfiguration = _configuration.GetSection("Mqtt").Get<MqttConfiguration>();
             using (var dbContext = new MysqlDbContext())
             {
                 Domain.FccStationInfo.FccStationInfo? fccStationInfo = dbContext.FccStationInfos.FirstOrDefault();
                 string? mqttService = fccStationInfo?.MqttService;
-                string? buildId = fccStationInfo?.BuildId;
+                buildId = fccStationInfo?.BuildId;
                 if (mqttService == null || buildId == null)
                 {
                     Logger.Info($"can not get mqttService:{mqttService} and buildId:{buildId}");
@@ -42,6 +50,7 @@ namespace Edge.Core.MqttClient
                 string[] hostAndPort = mqttService.Split(":");
                 MqttClientOptions mqttClientOptions = new MqttClientOptionsBuilder()
                     .WithTcpServer(hostAndPort[0], hostAndPort[1].ToString().ToInt())
+                    .WithCredentials(mqttConfiguration.user, mqttConfiguration.password)
                     .WithClientId(buildId)
                     .WithCleanSession()
                     .WithTlsOptions(new MqttClientTlsOptions()
@@ -52,7 +61,7 @@ namespace Edge.Core.MqttClient
 
                 _mqttClient = new MqttFactory().CreateMqttClient();
 
-                this.topics = new string[] { $"fromClound/{buildId}" };
+                this.topics = mqttConfiguration.subTopic;
                 _mqttClient.ConnectedAsync += mqttConnected;
                 _mqttClient.DisconnectedAsync += mqttDisConnected;
                 _mqttClient.ApplicationMessageReceivedAsync += mqttOnReceive;
@@ -72,7 +81,7 @@ namespace Edge.Core.MqttClient
 
             topics.ForEach(topic =>
             {
-                _mqttClient.SubscribeAsync(topic, MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce);
+                _mqttClient.SubscribeAsync($"{topic}/{buildId}", MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce);
             });
 
             OnConnect?.Invoke(this, args);

+ 1 - 1
Edge.Core/Parser/BinaryParser/ParserBase.cs

@@ -14,7 +14,7 @@ namespace Edge.Core.Parser.BinaryParser
 {
     public abstract class ParserBase : IMessageParser<byte[], MessageTemplateBase>
     {
-        protected static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Communicator");
+        protected static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("ParserBase");
 
         private int debug_ConvertToObject_depth = 0;
 

+ 5 - 3
Edge.Core/Processor/GenericDeviceProcessor.cs

@@ -3,9 +3,11 @@ using Edge.Core.Parser;
 using Edge.Core.Parser.BinaryParser.MessageEntity;
 using Edge.Core.Processor.Communicator;
 using Edge.Core.Processor.Dispatcher.Attributes;
+using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging.Abstractions;
+using Newtonsoft.Json;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
@@ -25,7 +27,7 @@ namespace Edge.Core.Processor
 
         #region performance related
 
-        //private static NLog.Logger perfLogger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Performance");
+        private static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Performance");
         private static ILogger perfLogger = NullLogger.Instance;
         private Stopwatch perfWatch_From_CommOnDataReceived_To_HandlerProcessed;
         private Stopwatch perfWatch_From_CommOnDataReceived_To_CommOnDataWriting;
@@ -150,13 +152,13 @@ namespace Edge.Core.Processor
             };
 
             //------------------------- MQTT ---------------------------------
-            MqttClientService mqttClientService = new MqttClientService();
+            MqttClientService mqttClientService = new MqttClientService(services.GetService<IConfiguration>());
             mqttClientService.OnConnect += (s, a) => { };
             mqttClientService.OnDisconnect += (s, a) => { };
             mqttClientService.OnApplicationMessageReceived += (s, a) =>
             {
                 var message = Encoding.UTF8.GetString(a.ApplicationMessage.Payload);
-                handler.OnReceiveMqttMessage(message);
+                handler.OnReceiveMqttMessage(a.ApplicationMessage.Topic, message);
             };
             
             mqttClientService.Start();

+ 1 - 1
Edge.Core/Processor/IDeviceHandler.cs

@@ -37,7 +37,7 @@ namespace Edge.Core.Processor
         /// 接收到 MQTT 数据
         /// </summary>
         /// <param name="message">数据 json </param>
-        void OnReceiveMqttMessage(string message) { }
+        void OnReceiveMqttMessage(string topic,string message) { }
 
         /// <summary>
         /// 发送二维码信息

+ 16 - 9
Edge.Core/Processor/Outgoing/Outgoing.cs

@@ -3,6 +3,7 @@ using Edge.Core.Parser.BinaryParser.MessageEntity;
 using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging.Abstractions;
+using Newtonsoft.Json;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -13,8 +14,8 @@ namespace Edge.Core.Processor
 {
     public class Outgoing<TRaw, TMessage> : IOutgoing<TRaw, TMessage> where TMessage : MessageBase
     {
-        //static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Communicator");
-        static ILogger logger = NullLogger.Instance;
+        static NLog.Logger logger = NLog.LogManager.LoadConfiguration("nlog.config").GetLogger("Outgoing");
+        //static ILogger logger = NullLogger.Instance;
         protected IIncoming<TMessage> incoming;
         public event EventHandler<OutgoingEventArg<TMessage>> OnWriting;
 
@@ -32,7 +33,7 @@ namespace Edge.Core.Processor
             if (services != null)
             {
                 var loggerFactory = services.GetRequiredService<ILoggerFactory>();
-                logger = loggerFactory.CreateLogger("Communicator");
+                //logger = loggerFactory.CreateLogger("Communicator");
             }
         }
 
@@ -41,6 +42,7 @@ namespace Edge.Core.Processor
         /// </summary>
         public void OnConnect()
         {
+            logger.Info("当前已连接");
             isConnectTask = new TaskCompletionSource<bool>();
         }
 
@@ -49,7 +51,8 @@ namespace Edge.Core.Processor
         /// </summary>
         public void OnDisconnect()
         {
-            isConnectTask.SetResult(false);
+            logger.Info("当前已断开连接");
+            //isConnectTask.SetResult(false);
         }
 
         /// <summary>
@@ -76,7 +79,8 @@ namespace Edge.Core.Processor
                         {
                             callback(request, incoming.Message);
                         }
-                        catch (Exception exx) { logger.LogError("Outgoing.WriteAsync(...,...,...,...) exceptioned in callback, detail: " + exx); }
+                        //catch (Exception exx) { logger.LogError("Outgoing.WriteAsync(...,...,...,...) exceptioned in callback, detail: " + exx); }
+                        catch (Exception exx) { logger.Error("Outgoing.WriteAsync(...,...,...,...) exceptioned in callback, detail: " + exx); }
                     }
                 }
             };
@@ -112,11 +116,14 @@ namespace Edge.Core.Processor
 
         public virtual async Task<WriteRepsonse> WriteAsyncAndCheckIsConnect(TMessage request, Func<TMessage, TMessage, bool> responseCapture, int timeout)
         {
-            if(isConnectTask == null) return new WriteRepsonse()
+            if (isConnectTask == null)
             {
-                ResponseType = WriteResponseType.DISCONNECT,
-                Data = false,
-            };
+                return new WriteRepsonse()
+                {
+                    ResponseType = WriteResponseType.DISCONNECT,
+                    Data = false,
+                };
+            }
             Task<TMessage> sendTask = WriteAsync(request, responseCapture, timeout);
             Task responseTask = await Task.WhenAny(sendTask, isConnectTask.Task);
             var type = WriteResponseType.TIME_OUT;

+ 336 - 258
HengshanPaymentTerminal/HengshanPayTermHandler.cs

@@ -33,6 +33,9 @@ using Microsoft.IdentityModel.Tokens;
 using Org.BouncyCastle.Asn1.Ocsp;
 using Newtonsoft.Json.Linq;
 using System.Net;
+using Edge.Core.Domain.FccOrderInfo.Output;
+using Microsoft.AspNetCore.Mvc;
+using System.Timers;
 
 namespace HengshanPaymentTerminal
 {
@@ -82,6 +85,13 @@ namespace HengshanPaymentTerminal
 
         public Dictionary<long,string> stationPayment = new Dictionary<long,string>();
 
+        //记录油枪泵码
+        public ConcurrentDictionary<int,string> nozzlePumpCode = new ConcurrentDictionary<int, string>();
+
+        public string buildID = "";
+
+        public bool isSendHeart = true;
+
         public List<DetailsNozzleInfoOutput> nozzleInfoList { get; private set; }
 
         public TcpClient? client { get; set; }
@@ -442,16 +452,28 @@ namespace HengshanPaymentTerminal
             //this.MysqlDbContext = new MysqlDbContext();
             this.httpClientUtil = new HttpClientUtils();
 
+            
             this.serverPort = CommIdentity.Replace("*:", "").ToInt();
             GetInfo();
+
+            if(stationInfo.CheckOrderInterval > 0)
+            {
+                //开启定时任务,每分钟执行一次,检查xx分钟未取消授权的订单,并发起取消授权。xx可配置
+                System.Timers.Timer timer = new System.Timers.Timer(60000);
+                timer.Elapsed += (sender, eventArgs) =>
+                {
+                    Task.Run(() => SendUnAuthorizationTimerAsync());
+                };
+            }
+            
         }
 
         public string CommIdentity { get; private set; }
 
         public async Task Process(IContext<byte[], CommonMessage> context)
         {
-            
-            switch(context.Incoming.Message.Handle)
+            logger.Info($"获取到信息:{JsonConvert.SerializeObject(context.Incoming.Message)}");
+            switch (context.Incoming.Message.Handle)
             {
                 //心跳,带油枪状态信息
                 case 0x10:
@@ -465,7 +487,18 @@ namespace HengshanPaymentTerminal
                 case 0x18:
                     {
                         //添加或修改数据库订单
+                        logger.Info($"订单信息");
                         OrderFromMachine orderFromMachine = (OrderFromMachine)context.Incoming.Message;
+                        if(nozzlePumpCode.TryGetValue(orderFromMachine.nozzleNum, out string pumpCodeAndTTC))
+                        {
+                            if($"{orderFromMachine.pumpCode}_{orderFromMachine.ttc}".Equals(pumpCodeAndTTC))
+                            {
+                                logger.Info($"收到油机订单,泵码与流水号一致,为重复发送");
+                                return;
+                            }
+                        }
+                        logger.Info($"收到油机订单,泵码与流水号不一致,更新信息");
+                        nozzlePumpCode[orderFromMachine.nozzleNum] = $"{orderFromMachine.pumpCode}_{orderFromMachine.ttc}";
                         FccOrderInfo fccOrderInfo = UpLoadOrder(orderFromMachine);
                         logger.Info($"receive order from machine,database had change");
                         CreateOrRedeemTransaction(fccOrderInfo);
@@ -527,9 +560,9 @@ namespace HengshanPaymentTerminal
                     */
 
             }
-            
+
             //油机的应答不用回复
-            if(context.Incoming.Message.Handle != 0x55) context.Outgoing.Write(context.Incoming.Message);
+            if (context.Incoming.Message.Handle != 0x55 && isSendHeart) context.Outgoing.Write(context.Incoming.Message);
 
         }
 
@@ -713,6 +746,7 @@ namespace HengshanPaymentTerminal
             if(fccStationInfo != null)
             {
                 stationInfo = new StationInfo(fccStationInfo);
+                buildID = stationInfo.BuildId;
                 string paymentType = stationInfo.PaymentType;
                 string[] paymentGround = paymentType.Split(",");
                 foreach (var item in paymentGround)
@@ -742,42 +776,63 @@ namespace HengshanPaymentTerminal
         /// 接收到MQTT
         /// </summary>
         /// <param name="message"></param>
-        public async void OnReceiveMqttMessage(string message)
+        public async void OnReceiveMqttMessage(string topic,string message)
         {
+            logger.Info($"getMqtt topic:{topic},and message is {message}");
             MqttRequest? mqttRequest = JsonConvert.DeserializeObject<MqttRequest>(message);
             if (mqttRequest == null)
             {
                 logger.Error($"mqtt message turn on object fail,message:{message}");
                 return;
             }
-            switch (mqttRequest.type)
+            if ($"authorization/{buildID}".Equals(topic))
             {
-                case MQTT_TYPE.AUTHORIZATION:
-                    {
-                        
-                        MqttAuthorizationRequest? mqttAuthorizationRequest = JsonConvert.DeserializeObject<MqttAuthorizationRequest>(mqttRequest.data);
-                        await SendAuthorizationAsync(mqttAuthorizationRequest);
-                        break;
-                    }
-                case MQTT_TYPE.UNAUTHORIZATION:
-                    {
-                        MqttUnAhorizationRequest? mqttUnAhorizationRequest = JsonConvert.DeserializeObject<MqttUnAhorizationRequest>(mqttRequest.data);
-                        await SendUnAuthorizartion(mqttUnAhorizationRequest);
-                        break;
-                    }
-                case MQTT_TYPE.PAID:
-                    {
-                        MqttPaidRequest? mqttPaidRequest = JsonConvert.DeserializeObject<MqttPaidRequest>(mqttRequest.data);
-                        await SendActuallyPaid(mqttPaidRequest, mqttRequest.UserName, mqttRequest.UserPhoneNumber);
-                        break;
-                    }
-                case MQTT_TYPE.REFUND:
-                    {
-                        MqttRefundRequest? mqttRefundRequest = JsonConvert.DeserializeObject<MqttRefundRequest>(mqttRequest.data);
-                        await OnRecieveOrderRefund(mqttRefundRequest);
-                        break;
-                    }
+                MqttAuthorizationRequest? mqttAuthorizationRequest = JsonConvert.DeserializeObject<MqttAuthorizationRequest>(mqttRequest.data);
+                await SendAuthorizationAsync(mqttAuthorizationRequest, mqttRequest.UserName, mqttRequest.UserPhoneNumber);
+            }
+            if($"unAuthorization/{buildID}".Equals(topic))
+            {
+                MqttUnAhorizationRequest? mqttUnAhorizationRequest = JsonConvert.DeserializeObject<MqttUnAhorizationRequest>(mqttRequest.data);
+                await SendUnAuthorizartion(mqttUnAhorizationRequest);
             }
+            if ($"paid/{buildID}".Equals (topic))
+            {
+                MqttPaidRequest? mqttPaidRequest = JsonConvert.DeserializeObject<MqttPaidRequest>(mqttRequest.data);
+                await SendActuallyPaid(mqttPaidRequest, mqttRequest.UserName, mqttRequest.UserPhoneNumber);
+            }
+            if ($"refund/{buildID}".Equals(topic))
+            {
+                MqttRefundRequest? mqttRefundRequest = JsonConvert.DeserializeObject<MqttRefundRequest>(mqttRequest.data);
+                await OnRecieveOrderRefund(mqttRefundRequest);
+            }
+            //switch (mqttRequest.type)
+            //{
+            //    case MQTT_TYPE.AUTHORIZATION:
+            //        {
+                        
+            //            MqttAuthorizationRequest? mqttAuthorizationRequest = JsonConvert.DeserializeObject<MqttAuthorizationRequest>(mqttRequest.data);
+            //            await SendAuthorizationAsync(mqttAuthorizationRequest);
+            //            break;
+            //        }
+            //    case MQTT_TYPE.UNAUTHORIZATION:
+            //        {
+            //            MqttUnAhorizationRequest? mqttUnAhorizationRequest = JsonConvert.DeserializeObject<MqttUnAhorizationRequest>(mqttRequest.data);
+            //            await SendUnAuthorizartion(mqttUnAhorizationRequest);
+            //            break;
+            //        }
+            //    case MQTT_TYPE.PAID:
+            //        {
+            //            MqttPaidRequest? mqttPaidRequest = JsonConvert.DeserializeObject<MqttPaidRequest>(mqttRequest.data);
+            //            await SendActuallyPaid(mqttPaidRequest, mqttRequest.UserName, mqttRequest.UserPhoneNumber);
+            //            break;
+            //        }
+            //    case MQTT_TYPE.REFUND:
+            //        {
+            //            MqttRefundRequest? mqttRefundRequest = JsonConvert.DeserializeObject<MqttRefundRequest>(mqttRequest.data);
+            //            await OnRecieveOrderRefund(mqttRefundRequest);
+            //            break;
+            //        }
+            //}
         }
 
         /// <summary>
@@ -860,43 +915,52 @@ namespace HengshanPaymentTerminal
                 Id = request.Id,
                 Result = 1
             };
-            await httpClientUtil.SendRecievePaidNotice(JsonConvert.SerializeObject(onGetPaidInfo));
+            HttpResponseMessage httpResponseMessage = await httpClientUtil.SendRecievePaidNotice(JsonConvert.SerializeObject(onGetPaidInfo));
+            logger.Info($"send actuallyPaid result response:{JsonConvert.SerializeObject(httpResponseMessage.Content.ReadAsStringAsync())}");
 
-            FccOrderInfo? fccOrderInfo = mysqlDbContext.FccOrderInfos.FirstOrDefault(order => 
-            order.NozzleNum == request.NozzleId 
-            && order.Ttc.ToString() == request.TransactionNumber
-            && order.PumpCode == request.FuelItemPumpTotalizerVolume);
+            FccOrderInfo? fccOrderInfo = mysqlDbContext.FccOrderInfos.FirstOrDefault(order => order.CloundOrderId == request.Id);
             if (fccOrderInfo == null)
             {
-                logger.Error($"[mqtt paid order notice]:can not find order by clounid:{request.Id}");
-                return;
-            }
-            fccOrderInfo.CloundOrderId = request.Id;
-            fccOrderInfo.AmountPayable = request.ActualPaymentAmount;
-            fccOrderInfo.PaymentTime = request.TransactionTime;
-            if(request.PaymentMethod != null)
+                //未找到证明为预支付,这里插入订单
+                logger.Info($"[mqtt paid order notice]:can not find order by clounid:{request.Id},is perpay");
+                string paymentName = "未知类型";
+                if (request.PaymentMethod != null)
+                {
+                    paymentName = stationPayment[request.PaymentMethod ?? 0] ?? "未知类型";
+                }
+                fccOrderInfo = request.ToComponent(userName,phoneNumber, paymentName);
+                mysqlDbContext.Add(fccOrderInfo);
+            } else
             {
-                fccOrderInfo.PayType = (int)request.PaymentMethod;
-                fccOrderInfo.PaymentName = stationPayment[request.PaymentMethod ?? 0] ?? "未知类型";
+                logger.Info($"[mqtt paid order notice]:find order,update order right now");
+                //后支付,这里更新支付信息
+                fccOrderInfo.AmountPayable = request.ActualPaymentAmount;
+                fccOrderInfo.VolumePayable = request.OriginalQty;
+                fccOrderInfo.PaymentTime = request.TransactionTime;
+                if (request.PaymentMethod != null)
+                {
+                    fccOrderInfo.PayType = (int)request.PaymentMethod;
+                    fccOrderInfo.PaymentName = stationPayment[request.PaymentMethod ?? 0] ?? "未知类型";
+                }
+                fccOrderInfo.UserName = userName ?? "";
+                fccOrderInfo.PhoneNumber = phoneNumber ?? string.Empty;
+                fccOrderInfo.PaymentStatus = 1;
             }
-            fccOrderInfo.UserName = userName??"";
-            fccOrderInfo.PhoneNumber = phoneNumber ?? string.Empty;
-            fccOrderInfo.PaymentStatus = 1;
+            
 
             mysqlDbContext.SaveChanges();
 
-            //SendActuallyPaid sendActuallyPaid = new SendActuallyPaid(orderInfo.NozzleNum, orderInfo.Ttc, orderInfo.AmountPayable ?? orderInfo.Amount, getFrame(null));
-            //byte[] commandAndNozzle = { sendActuallyPaid.Handle, (byte)sendActuallyPaid.NozzleNum };
-            //await SendMessageToMaichine("发送实付金额", (request, response) =>
-            //{
-            //    if (response.Handle == (byte)CommonMessage.Command.SEND_NEED_AMOUNT)
-            //    {
-            //        CommonAnswerBack commonAnswerBack = (CommonAnswerBack)response;
-            //        return commonAnswerBack.Command == (byte)CommonMessage.Command.SEND_NEED_AMOUNT && commonAnswerBack.NozzleNum == sendActuallyPaid.NozzleNum;
-            //    }
-            //    return false;
-            //}, sendActuallyPaid);
-            //await SendMessageToMaichine("发送实付金额", BitConverter.ToString(commandAndNozzle).Replace("-", ""), sendActuallyPaid);
+            SendActuallyPaid sendActuallyPaid = new SendActuallyPaid(fccOrderInfo.NozzleNum, fccOrderInfo.Ttc, fccOrderInfo.AmountPayable ?? fccOrderInfo.Amount, getFrame(null));
+            byte[] commandAndNozzle = { sendActuallyPaid.Handle, (byte)sendActuallyPaid.NozzleNum };
+            await SendMessageToMaichine("发送实付金额", (request, response) =>
+            {
+                if (response.Handle == (byte)CommonMessage.Command.SEND_NEED_AMOUNT)
+                {
+                    CommonAnswerBack commonAnswerBack = (CommonAnswerBack)response;
+                    return commonAnswerBack.Command == (byte)CommonMessage.Command.SEND_NEED_AMOUNT && commonAnswerBack.NozzleNum == sendActuallyPaid.NozzleNum;
+                }
+                return false;
+            }, sendActuallyPaid);
         }
 
         /// <summary>
@@ -904,7 +968,7 @@ namespace HengshanPaymentTerminal
         /// </summary>
         /// <param name="request"></param>
         /// <returns></returns>
-        public async Task SendAuthorizationAsync(MqttAuthorizationRequest? request)
+        public async Task SendAuthorizationAsync(MqttAuthorizationRequest? request, string? userName, string? phoneNumber)
         {
             MysqlDbContext mysqlDbContext = new MysqlDbContext();
             if(request == null)
@@ -915,11 +979,16 @@ namespace HengshanPaymentTerminal
 
             //添加订单到数据库
             DateTime authorizationTime = request.AuthorizationTime ?? DateTime.Now;
-            FccOrderInfo fccOrderInfo = request.ToComponent(authorizationTime);
-            mysqlDbContext.FccOrderInfos.Add(fccOrderInfo);
+            FccOrderInfo? fccOrderInfo = await mysqlDbContext.FccOrderInfos.FirstOrDefaultAsync(order => order.CloundOrderId == request.Id);
+            if(fccOrderInfo == null)
+            {
+                logger.Error($"authorization find order by clound id:{request.Id} is null");
+                return;
+            }
+            fccOrderInfo.AuthorizationTime = authorizationTime;
 
             //发送授权申请到油机
-            SendAuthorization sendAuthorization = new SendAuthorization((int)request.NozzleId, authorizationTime, 1,request.OriginalAmount, getFrame(null));
+            SendAuthorization sendAuthorization = new SendAuthorization((int)request.NozzleId, authorizationTime, 1, request.OriginalAmount, getFrame(null));
             byte[] commandAndNozzle = { sendAuthorization.Handle, (byte)sendAuthorization.NozzleNum };
             CommonMessage commonMessage = await SendMessageToMaichine("发送授权请求", (request, response) =>
             {
@@ -930,6 +999,7 @@ namespace HengshanPaymentTerminal
                 }
                 return false;
             }, sendAuthorization);
+            logger.Info($"获取到授权结果:{JsonConvert.SerializeObject(commonMessage)}");
 
             //发送授权结果给云端
             string authorizationResultJson = string.Empty;
@@ -952,6 +1022,7 @@ namespace HengshanPaymentTerminal
             else
             {
                 AuthorizationResponse authorization = (AuthorizationResponse)commonMessage;
+                
                 if (authorization.Result == 0)
                 {
                     sendAuthorizationResult.OilMachineStatus = OilMachineStatus.Failed;
@@ -960,11 +1031,20 @@ namespace HengshanPaymentTerminal
                 {
                     sendAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
                     sendAuthorizationResult.TransactionNumber = authorization.Ttc.ToString();
-                    fccOrderInfo.Ttc = authorization.Result;
+                    fccOrderInfo.Ttc = authorization.Ttc;
+                    fccOrderInfo.AuthorizationStatus = 1;
                 }
             }
+            //SendAuthorizationResult sendAuthorizationResult = new SendAuthorizationResult()
+            //{
+            //    NozzleId = request.NozzleId,
+            //    TransactionNumber = "1",
+            //    OilMachineStatus = OilMachineStatus.Success
+
+            //};
+            logger.Info($"发送授权结果:{JsonConvert.SerializeObject(sendAuthorizationResult)}");
             HttpResponseMessage httpResponseMessage = await httpClientUtil.SendAuthorizationResult(JsonConvert.SerializeObject(sendAuthorizationResult));
-            logger.Info($"send authorization result response:{JsonConvert.SerializeObject(httpResponseMessage.Content)}");
+            logger.Info($"send authorization result response:{JsonConvert.SerializeObject(httpResponseMessage.Content.ReadAsStringAsync())}");
 
             //更新订单
             mysqlDbContext.SaveChanges();
@@ -983,29 +1063,28 @@ namespace HengshanPaymentTerminal
                 return;
             }
 
-            //从请求信息中获取流水号与授权时间,没有就到数据库查找
+            //从数据库查找,查看是否已取消授权
             int ttc = 0;
             DateTime authorizationTime = request.AuthorizationTime ?? DateTime.Now;
-            bool ttsIntResult = int.TryParse(request.TransactionNumber, out ttc);
-            if (request.AuthorizationTime == null || !ttsIntResult)
+            FccOrderInfo? fccOrderInfo = mysqlDbContext.FccOrderInfos.FirstOrDefault(order => order.CloundOrderId == request.Id);
+            bool isUnauthorization = false;
+            if (fccOrderInfo != null)
             {
-                FccOrderInfo? fccOrderInfo = mysqlDbContext.FccOrderInfos.FirstOrDefault(order => order.CloundOrderId == request.Id);
-                if(fccOrderInfo != null)
-                {
-                    ttc = fccOrderInfo.Ttc;
-                    authorizationTime = fccOrderInfo.AuthorizationTime;
-                }
+                ttc = fccOrderInfo.Ttc;
+                authorizationTime = fccOrderInfo.AuthorizationTime;
+                isUnauthorization = fccOrderInfo.AuthorizationStatus == 0;
             }
 
             SendUnAuthorizationResult sendUnAuthorizationResult = new SendUnAuthorizationResult();
             sendUnAuthorizationResult.NozzleId = request.NozzleId;
             sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
 
-            if (ttc != 0)
+            if (ttc != 0 && !isUnauthorization)
             {
                 SendUnAuthorization sendUnAuthorization = new SendUnAuthorization((int)request.NozzleId, authorizationTime, ttc, getFrame(null));
                 byte[] commandAndNozzle = { sendUnAuthorization.Handle, (byte)sendUnAuthorization.NozzleNum };
 
+                //发送取消授权命令并获取响应
                 CommonMessage commonMessage = await SendMessageToMaichine("发送取消授权请求", (request, response) =>
                 {
                     if (response.Handle == (byte)CommonMessage.Command.CANCEL_ACCREDIT)
@@ -1015,8 +1094,10 @@ namespace HengshanPaymentTerminal
                     }
                     return false;
                 }, sendUnAuthorization);
+
                 if (commonMessage.IsError)
                 {
+                    //响应错误
                     ErrorMessage errorMessage = (ErrorMessage)commonMessage;
                     switch (errorMessage.TheErrorType)
                     {
@@ -1030,6 +1111,7 @@ namespace HengshanPaymentTerminal
                 }
                 else
                 {
+                    //正常响应
                     UnAhorizationResponse unAuthorization = (UnAhorizationResponse)commonMessage;
                     if (unAuthorization.Result == 0)
                     {
@@ -1038,18 +1120,25 @@ namespace HengshanPaymentTerminal
                     else
                     {
                         sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
+                        fccOrderInfo.AuthorizationStatus = 0;
                     }
                 }
             }
-            else
+            else if (!isUnauthorization)
             {
+                //若已经未授权
+                sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
+            }
+            else
+            {   //未找到流水号
                 sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.TransactionNumberNotFound;
             }
 
             HttpResponseMessage httpResponseMessage = await httpClientUtil.SendUnAuthorizationResult(JsonConvert.SerializeObject(sendUnAuthorizationResult));
-            logger.Info($"send Unauthorization result response:{JsonConvert.SerializeObject(httpResponseMessage.Content)}");
+            logger.Info($"send Unauthorization result response:{JsonConvert.SerializeObject(httpResponseMessage.Content.ReadAsStringAsync())}");
         }
 
+
         /// <summary>
         /// 接收到云端发送订单退款信息
         /// </summary>
@@ -1069,7 +1158,22 @@ namespace HengshanPaymentTerminal
                 Id = request.Id,
                 Result = 1
             };
-            await httpClientUtil.SendRecieveRefundNotice(JsonConvert.SerializeObject(onGetRefundInfo));
+            HttpResponseMessage httpResponseMessage = await httpClientUtil.SendRecieveRefundNotice(JsonConvert.SerializeObject(onGetRefundInfo));
+            logger.Info($"send refund result response:{JsonConvert.SerializeObject(httpResponseMessage.Content.ReadAsStringAsync())}");
+
+
+            //通知油机已退款
+            SendRefund sendRefund = new SendRefund((int)request.NozzleId, request.TransactionNumber, request.OriginalAmount, request.ActualPaymentAmount ?? 0m, request.RefundAmount ?? 0m, getFrame(null));
+            byte[] commandAndNozzle = { sendRefund.Handle, (byte)sendRefund.NozzleNum };
+            await SendMessageToMaichine("发送退款信息", (request, response) =>
+            {
+                if (response.Handle == (byte)CommonMessage.Command.SEND_NEED_AMOUNT)
+                {
+                    CommonAnswerBack commonAnswerBack = (CommonAnswerBack)response;
+                    return commonAnswerBack.Command == (byte)CommonMessage.Command.SEND_NEED_AMOUNT && commonAnswerBack.NozzleNum == sendRefund.NozzleNum;
+                }
+                return false;
+            }, sendRefund);
 
             FccOrderInfo? fccOrderInfo = mysqlDbContext.FccOrderInfos.FirstOrDefault(order => order.CloundOrderId == request.Id);
             if (fccOrderInfo == null)
@@ -1078,7 +1182,9 @@ namespace HengshanPaymentTerminal
                 return;
             }
             fccOrderInfo.AmountPayable = request.ActualPaymentAmount;
-            fccOrderInfo.PaymentStatus = 2;
+            fccOrderInfo.RefundAmount = request.RefundAmount;
+            fccOrderInfo.PaymentStatus = (int)request.OrderStatus;
+            fccOrderInfo.RefundStatus = (int)request.RefundStatus;
             mysqlDbContext.SaveChanges();
 
         }
@@ -1112,6 +1218,7 @@ namespace HengshanPaymentTerminal
         private async Task<CommonMessage> SendMessageToMaichine(string sendTag, Func<CommonMessage, CommonMessage, bool> responseCapture,CommonMessage sendMessage)
         {
             logger.Info($"send request to machine:{sendTag}");
+            isSendHeart = false;
             int retryCount = 0;
             while (retryCount < 3)
             {
@@ -1141,6 +1248,7 @@ namespace HengshanPaymentTerminal
 
                     }
                     Console.WriteLine("");
+                    isSendHeart = true;
                     //返回信息
                     return (CommonMessage)response.Data;
                 }
@@ -1154,9 +1262,12 @@ namespace HengshanPaymentTerminal
                     if (retryCount >= 3)
                     {
                         logger.Info($"{sendTag}: is time out add retry 3 time");
+                        isSendHeart = true;
                     }
                 }
             }
+
+            isSendHeart = true;
             return new ErrorMessage()
             {
                 IsError = true,
@@ -1165,167 +1276,74 @@ namespace HengshanPaymentTerminal
             };
         }
 
-        ///// <summary>
-        ///// 发送消息到油机,3秒的超时,重试三次
-        ///// </summary>
-        ///// <param name="sendTag">发送的消息类型,用于日志记录</param>
-        ///// <param name="sendKey">发送的消息key,用于存储 TaskCompletionSource</param>
-        ///// <param name="requestBytes">实际发送消息</param>
-        ///// <returns></returns>
-        //private async Task<CommonMessage> SendMessageToMaichine(string sendTag, string sendKey,CommonMessage sendMessage)
-        //{
-        //    int retryCount = 0;
-        //    while (retryCount < 3)
-        //    {
-        //        try
-        //        {
-        //            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
-        //            bool isAdd = _tcsDictionary.TryAdd(sendKey, new TaskCompletionSource<CommonMessage>());
-        //            logger.Info($"{sendTag}: add request {sendKey} to dic is {isAdd}");
+        /// <summary>
+        /// 取消授权
+        /// </summary>
+        /// <returns></returns>
+        private async Task SendUnAuthorizationTimerAsync()
+        {
+            MysqlDbContext mysqlDbContext = new MysqlDbContext();
+            DateTime timeOut = DateTime.Now.AddMonths(stationInfo.CheckOrderInterval);
+            FccOrderInfo? fccOrderInfo = await mysqlDbContext.FccOrderInfos.FirstOrDefaultAsync(order => order.AuthorizationStatus == 1 && order.AuthorizationTime < timeOut);
 
-        //            Write(sendMessage);
+            if (fccOrderInfo == null)
+            {
+                logger.Info($"当前没有待取消授权的订单");
+                return;
+            }
+            SendUnAuthorizationResult sendUnAuthorizationResult = new SendUnAuthorizationResult();
+            sendUnAuthorizationResult.NozzleId = fccOrderInfo.NozzleNum;
+            sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
 
-        //            TaskCompletionSource<CommonMessage>? value;
-        //            TaskCompletionSource<CommonMessage> tcs;
-        //            if (_tcsDictionary.TryGetValue(sendKey, out value))
-        //            {
-        //                tcs = value;
-        //            }
-        //            else
-        //            {
-        //                tcs = new TaskCompletionSource<CommonMessage>();
-        //            }
-        //            Task checkOutTime = Task.Delay(Timeout.Infinite, cts.Token);
-        //            var response = await Task.WhenAny(tcs.Task, checkOutTime, checkDisConnectTask.Task);
-        //            //超时重试
-        //            if (response == checkOutTime)
-        //            {
-        //                retryCount++;
-        //                logger.Info($"{sendTag}-{sendKey}: time out,retrying... ({retryCount} / 3)");
-        //                continue;
-        //            }
-        //            //CommonMessage response = await tcs.Task.WaitAsync(cts.Token);
-        //            _tcsDictionary.TryRemove(sendKey, out _);
+            SendUnAuthorization sendUnAuthorization = new SendUnAuthorization(fccOrderInfo.NozzleNum, fccOrderInfo.AuthorizationTime, fccOrderInfo.Ttc, getFrame(null));
+            byte[] commandAndNozzle = { sendUnAuthorization.Handle, (byte)sendUnAuthorization.NozzleNum };
 
-        //            //链接断开不再发送
-        //            if(response == checkDisConnectTask.Task)
-        //            {
-        //                return new ErrorMessage()
-        //                {
-        //                    IsError = true,
-        //                    TheErrorType = CommonMessage.ErrorType.DISCONNECT,
-        //                    ErrorMessage = $"the client is disconnet"
-        //                };
-        //            }
+            //发送取消授权命令并获取响应
+            CommonMessage commonMessage = await SendMessageToMaichine("定时发送取消授权请求", (request, response) =>
+            {
+                if (response.Handle == (byte)CommonMessage.Command.CANCEL_ACCREDIT)
+                {
+                    UnAhorizationResponse unauthorization = (UnAhorizationResponse)response;
+                    return unauthorization.NozzleNum == sendUnAuthorization.NozzleNum;
+                }
+                return false;
+            }, sendUnAuthorization);
 
-        //            //返回信息
-        //            return await (Task<CommonMessage>)response;
-        //        }
-        //        catch (Exception)
-        //        {
-        //            retryCount++;
-        //            logger.Info($"{sendTag}-{sendKey}: error,retrying... ({retryCount} / 3)");
-        //        }
-        //        finally
-        //        {
-        //            if (retryCount >= 3)
-        //            {
-        //                logger.Info($"{sendTag}-{sendKey}: is time out add retry 3 time");
-        //                _tcsDictionary.TryRemove(sendKey, out _);
-        //            }
-        //        }
-        //    }
-        //    return new ErrorMessage()
-        //    {
-        //        IsError = true,
-        //        TheErrorType = CommonMessage.ErrorType.TIMEOUT,
-        //        ErrorMessage = $"{sendTag}: can not receive response after 3 retries"
-        //    };
-        //}
+            if (commonMessage.IsError)
+            {
+                //响应错误
+                ErrorMessage errorMessage = (ErrorMessage)commonMessage;
+                switch (errorMessage.TheErrorType)
+                {
+                    case CommonMessage.ErrorType.DISCONNECT:
+                        sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Disconnected;
+                        break;
+                    case CommonMessage.ErrorType.TIMEOUT:
+                        sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.AuthorizationTimeout;
+                        break;
+                }
+            }
+            else
+            {
+                //正常响应
+                UnAhorizationResponse unAuthorization = (UnAhorizationResponse)commonMessage;
+                if (unAuthorization.Result == 0)
+                {
+                    sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Failed;
+                }
+                else
+                {
+                    sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
+                    fccOrderInfo.AuthorizationStatus = 0;
+                }
+            }
 
-        ///// <summary>
-        ///// 发送消息到油机,3秒的超时,重试三次
-        ///// </summary>
-        ///// <param name="sendTag">发送的消息类型,用于日志记录</param>
-        ///// <param name="sendKey">发送的消息key,用于存储 TaskCompletionSource</param>
-        ///// <param name="requestBytes">实际发送消息</param>
-        ///// <returns></returns>
-        ///// <exception cref="TimeoutException"></exception>
-        //private async Task<CommonMessage> SendRequestToMachine(string sendTag,string sendKey, byte[] requestBytes)
-        //{
-        //    int retryCount = 0;
-        //    while(retryCount < 3)
-        //    {
-        //        try
-        //        {
-        //            var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
-        //            bool isAdd = _tcsDictionary.TryAdd(sendKey, new TaskCompletionSource<CommonMessage>());
-        //            logger.Info($"{sendTag}: add request {sendKey} to dic is {isAdd}");
+            mysqlDbContext.SaveChanges();
 
-        //            if (client != null)
-        //            {
-        //                client?.Client?.Send(requestBytes);
-        //            } else
-        //            {
-        //                return new ErrorMessage()
-        //                {
-        //                    IsError = true,
-        //                    TheErrorType = CommonMessage.ErrorType.DISCONNECT,
-        //                    ErrorMessage = $"the client is disconnet"
-        //                };
-        //            }
-                    
-        //            logger.Info($"send request to machine:{BitConverter.ToString(requestBytes).Replace("-", " ")}");
+            HttpResponseMessage httpResponseMessage = await httpClientUtil.SendUnAuthorizationResult(JsonConvert.SerializeObject(sendUnAuthorizationResult));
+            logger.Info($"定时任务取消授权:send Unauthorization timer result response:{JsonConvert.SerializeObject(httpResponseMessage.Content.ReadAsStringAsync())}");
 
-        //            TaskCompletionSource<CommonMessage>? value;
-        //            TaskCompletionSource<CommonMessage> tcs;
-        //            if(_tcsDictionary.TryGetValue(sendKey, out value))
-        //            {
-        //                tcs = value;
-        //            } else
-        //            {
-        //                tcs = new TaskCompletionSource<CommonMessage>();
-        //            }
-        //            Task checkOutTime = Task.Delay(Timeout.Infinite, cts.Token);
-        //            var response = await Task.WhenAny(tcs.Task, checkOutTime, checkDisConnectTask.Task);
-        //            if(response == checkOutTime)
-        //            {
-        //                retryCount++;
-        //                logger.Info($"{sendTag}-{sendKey}: time out,retrying... ({retryCount} / 3)");
-        //                continue;
-        //            }
-        //            //CommonMessage response = await tcs.Task.WaitAsync(cts.Token);
-        //            _tcsDictionary.TryRemove(sendKey, out _);
-        //            if (response == checkDisConnectTask.Task)
-        //            {
-        //                return new ErrorMessage()
-        //                {
-        //                    IsError = true,
-        //                    TheErrorType = CommonMessage.ErrorType.DISCONNECT,
-        //                    ErrorMessage = $"the client is disconnet"
-        //                };
-        //            }
-        //            return await (Task<CommonMessage>)response;
-        //        } catch (Exception)
-        //        {
-        //            retryCount++;
-        //            logger.Info($"{sendTag}-{sendKey}: error,retrying... ({retryCount} / 3)");
-        //        } finally
-        //        {
-        //            if(retryCount >= 3)
-        //            {
-        //                logger.Info($"{sendTag}-{sendKey}: is time out add retry 3 time");
-        //                _tcsDictionary.TryRemove(sendKey,out _);
-        //            }
-        //        }
-        //    }
-        //    return new ErrorMessage()
-        //    {
-        //        IsError = true,
-        //        TheErrorType = CommonMessage.ErrorType.TIMEOUT,
-        //        ErrorMessage = $"{sendTag}: can not receive response after 3 retries"
-        //    };
-        //}
+        }
 
         /// <summary>
         /// 添加或修改订单
@@ -1369,19 +1387,60 @@ namespace HengshanPaymentTerminal
         private async void CreateOrRedeemTransaction(FccOrderInfo fccOrderInfo)
         {
             MysqlDbContext mysqlDbContext = new MysqlDbContext();
-            CreateTransaction createTransaction = new CreateTransaction(fccOrderInfo);
-            logger.Info($"create transaction, {JsonConvert.SerializeObject(createTransaction)}");
-            HttpResponseMessage httpResponseMessage = await httpClientUtil.CreateTransaction(JsonConvert.SerializeObject(createTransaction));
-
-            //var b = httpResponseMessage.Content;
-            //var a = httpResponseMessage;
-            string responseStr = await httpResponseMessage.Content.ReadAsStringAsync();
-            Response<CreateTransactionResponse>? response = JsonConvert.DeserializeObject<Response<CreateTransactionResponse>>(responseStr);
-            logger.Info($"reveice create transaction response:{JsonConvert.SerializeObject(response)}");
+            if (fccOrderInfo.CloundOrderId == null)
+            {
+                CreateTransaction createTransaction = new CreateTransaction(fccOrderInfo);
+                logger.Info($"create transaction, {JsonConvert.SerializeObject(createTransaction)}");
+                HttpResponseMessage httpResponseMessage = await httpClientUtil.CreateTransaction(JsonConvert.SerializeObject(createTransaction));
+
+                string responseStr = await httpResponseMessage.Content.ReadAsStringAsync();
+                Response<CreateTransactionResponse>? response = JsonConvert.DeserializeObject<Response<CreateTransactionResponse>>(responseStr);
+                logger.Info($"reveice create transaction response:{JsonConvert.SerializeObject(response)}");
+
+                fccOrderInfo.CloundOrderId = response?.data?.Id;
+                fccOrderInfo.UploadState = response?.data == null ? 0 : 1;
+                mysqlDbContext.SaveChanges();
+            } else
+            {
+                //MysqlDbContext mysqlDbContext = new MysqlDbContext();
+                if(fccOrderInfo.Amount == 0 && fccOrderInfo.Volume == 0)
+                {
+                    //0交易订单,证明为取消授权后产生的订单,不走核销,而是在小程序上显示为授权失败,让其重新发起授权或退款
+                    logger.Info("0交易,取消授权");
+                    fccOrderInfo.AuthorizationStatus = 0; //存在油机手动取消授权的情况,因此这里也要更新授权状态
+                    mysqlDbContext.SaveChanges();
+
+                    SendUnAuthorizationResult sendUnAuthorizationResult = new SendUnAuthorizationResult();
+                    sendUnAuthorizationResult.NozzleId = fccOrderInfo.NozzleNum;
+                    sendUnAuthorizationResult.TransactionNumber = fccOrderInfo.Ttc.ToString();
+                    sendUnAuthorizationResult.OilMachineStatus = OilMachineStatus.Success;
+                    HttpResponseMessage httpResponseMessage = await httpClientUtil.SendUnAuthorizationResult(JsonConvert.SerializeObject(sendUnAuthorizationResult));
+                    logger.Info($"手动取消授权:send Unauthorization result response:{JsonConvert.SerializeObject(httpResponseMessage.Content.ReadAsStringAsync())}");
+                }
+                else
+                {
+                    //核销
+                    Redeem redeem = new Redeem();
+                    redeem.trxId = (int)fccOrderInfo.CloundOrderId;
+                    redeem.OriginalQty = fccOrderInfo.Volume;
+
+                    string param = JsonConvert.SerializeObject(redeem);
+
+                    logger.Info($"Redeem order:{param}");
+                    HttpResponseMessage httpResponseMessage = await httpClientUtil.Redeem(param);
+
+                    string responseStr = await httpResponseMessage.Content.ReadAsStringAsync();
+                    Response<CreateTransactionResponse>? response = JsonConvert.DeserializeObject<Response<CreateTransactionResponse>>(responseStr);
+                    logger.Info($"Redeem order response:{JsonConvert.SerializeObject(response)}");
+
+                    //fccOrderInfo.CloundOrderId = response?.data?.Id;
+                    //fccOrderInfo.UploadState = response?.data == null ? 0 : 1;
+                    //mysqlDbContext.SaveChanges();
+                }
+                
+                //mysqlDbContext.SaveChanges();
+            }
             
-            fccOrderInfo.CloundOrderId = response?.data?.Id;
-            fccOrderInfo.UploadState = response?.data == null ? 0 : 1;
-            mysqlDbContext.SaveChanges();
         }
 
         /// <summary>
@@ -1392,6 +1451,7 @@ namespace HengshanPaymentTerminal
         {
             //提取出状态有变化的油枪,打包成要发送至云端的数据,添加到列表
             List<SendNozzleStatu> sendNozzleStatus = new List<SendNozzleStatu>();
+            List<int> fuelingNozzle = new List<int>();
             foreach (var nozzleState in heartBeatMessage.NozzleStatus)
             {
                 if (nozzleStatusDic.TryGetValue(nozzleState.NozzleNum, out var value))
@@ -1400,6 +1460,7 @@ namespace HengshanPaymentTerminal
                 }
                 //保存变量
                 nozzleStatusDic[nozzleState.NozzleNum] = nozzleState.STATU;
+                if (nozzleState.STATU == 8) fuelingNozzle.Add(nozzleState.NozzleNum);
 
                 //查找fcc数据库油枪id
                 DetailsNozzleInfoOutput? detailsNozzleInfoOutput = nozzleInfoList.Find(nozzle => nozzle.NozzleNum == nozzleState.NozzleNum);
@@ -1408,24 +1469,41 @@ namespace HengshanPaymentTerminal
                     logger.Error($"can not find nozzleInfo from nozzleInfoList:{nozzleState.NozzleNum} ,send nozzle state fail");
                     continue;
                 }
-                SendNozzleStatu sendNozzleStatu = new SendNozzleStatu(detailsNozzleInfoOutput.Id, nozzleState);
+                SendNozzleStatu sendNozzleStatu = new SendNozzleStatu(detailsNozzleInfoOutput.CloundNozzleId, nozzleState);
                 sendNozzleStatus.Add(sendNozzleStatu);
             }
 
-            if (sendNozzleStatus.IsNullOrEmpty()) return;
-
-            //发送云端
-            string reuqestJson = JsonConvert.SerializeObject(sendNozzleStatus);
-            logger.Info($"send nozzle state to cloud,{reuqestJson}");
-            try
+           
+            if (!sendNozzleStatus.IsNullOrEmpty())
             {
-                HttpResponseMessage httpResponseMessage = await httpClientUtil.SendNozzleStatu(reuqestJson);
-                Response<object>? response = JsonConvert.DeserializeObject<Response<object>>(await httpResponseMessage.Content.ReadAsStringAsync());
-                logger.Info($"reveice send nozzle state response:{JsonConvert.SerializeObject(response)}");
-            } catch (Exception ex)
+                //发送云端
+                string reuqestJson = JsonConvert.SerializeObject(sendNozzleStatus);
+                logger.Info($"send nozzle state to cloud,{reuqestJson}");
+                try
+                {
+                    HttpResponseMessage httpResponseMessage = await httpClientUtil.SendNozzleStatu(reuqestJson);
+                    Response<object>? response = JsonConvert.DeserializeObject<Response<object>>(await httpResponseMessage.Content.ReadAsStringAsync());
+                    logger.Info($"reveice send nozzle state response:{JsonConvert.SerializeObject(response)}");
+                }
+                catch (Exception ex)
+                {
+                    logger.Error($"send nozzle stat fail:{ex.Message}");
+                }
+            }
+
+            //当前油枪为加油态,找到该油枪最近一笔已授权的订单,将其赋为加油中
+            MysqlDbContext mysqlDbContext = new MysqlDbContext();
+            foreach(int nozzleNum in fuelingNozzle)
             {
-                logger.Error($"send nozzle stat fail:{ex.Message}");
+                FccOrderInfo? fccOrderInfo = await mysqlDbContext.FccOrderInfos
+                    .OrderByDescending(order => order.AuthorizationTime)
+                    .FirstOrDefaultAsync(order => order.NozzleNum == nozzleNum && order.AuthorizationStatus == 1);
+                if(fccOrderInfo != null)
+                {
+                    fccOrderInfo.AuthorizationStatus = 3;
+                }
             }
+            mysqlDbContext.SaveChanges();
             
         }
 

+ 15 - 3
HengshanPaymentTerminal/Http/HttpClientUtils.cs

@@ -17,6 +17,7 @@ namespace HengshanPaymentTerminal.Http
     {
         GET,
         POST,
+        POST_BY_PARAMS,
         PUT,
         DELETE,
     }
@@ -41,15 +42,22 @@ namespace HengshanPaymentTerminal.Http
             _httpClientService = new HttpClientService(httpClientFactory);
         }
 
-        public async Task<HttpResponseMessage> SendRquest(string api,string requestJson,SEND_MOTHOD mothod)
+        public async Task<HttpResponseMessage> SendRquest(string api, string requestJson, SEND_MOTHOD mothod)
+        {
+            return await SendRquest(api, requestJson,null, mothod);
+        }
+        public async Task<HttpResponseMessage> SendRquest(string api,string? requestJson,Dictionary<string,string>? param, SEND_MOTHOD mothod)
         {
             try
             {
-                var requesStr = new StringContent(requestJson, Encoding.UTF8, "application/json");
-                switch(mothod)
+                StringContent? requesStr = null;
+                if(requestJson != null) requesStr = new StringContent(requestJson, Encoding.UTF8, "application/json");
+                switch (mothod)
                 {
                     case SEND_MOTHOD.POST:
                         return await _httpClientService.PostAsync(api, requesStr);
+                    case SEND_MOTHOD.POST_BY_PARAMS:
+                        return await _httpClientService.PostAsyncByParams(api, param);
                     case SEND_MOTHOD.PUT:
                         return await _httpClientService.PutAsync(api, requesStr);
                     case SEND_MOTHOD.DELETE:
@@ -190,6 +198,10 @@ namespace HengshanPaymentTerminal.Http
             return await SendRquest("api/Transactions/CreateTransactions", requestJson, SEND_MOTHOD.POST);
         }
 
+        public async Task<HttpResponseMessage> Redeem(string requestJson)
+        {
+            return await SendRquest("api/Transactions/Redeem", requestJson, SEND_MOTHOD.POST);
+        }
         /// <summary>
         /// 发送授权结果
         /// </summary>

+ 8 - 1
HengshanPaymentTerminal/Http/IHttpClientUtil.cs

@@ -76,7 +76,14 @@ namespace HengshanPaymentTerminal.Http
         /// </summary>
         /// <param name="requestJson"></param>
         /// <returns></returns>
-        Task<HttpResponseMessage> CreateTransaction(string requestJson); 
+        Task<HttpResponseMessage> CreateTransaction(string requestJson);
+
+        /// <summary>
+        /// 核销订单
+        /// </summary>
+        /// <param name="param"></param>
+        /// <returns></returns>
+        Task<HttpResponseMessage> Redeem(string requestJson);
 
         /// <summary>
         /// 发送油枪状态到云端

+ 8 - 2
HengshanPaymentTerminal/Http/Request/HttpRequest.cs

@@ -477,6 +477,12 @@ namespace HengshanPaymentTerminal.Http.Request
         public decimal? FuelItemPumpTotalizerVolume {  get; set; }
     }
 
+    public class Redeem
+    {
+        public int trxId { get; set; }
+
+        public decimal OriginalQty {  get; set; }
+    }
     /// <summary>
     /// 发送云端油枪状态数据对象
     /// </summary>
@@ -488,7 +494,7 @@ namespace HengshanPaymentTerminal.Http.Request
             this.Status = heartBeatNozzleState.STATU;
         }
         /// <summary>
-        /// 油枪号
+        /// 云端油枪id
         /// </summary>
         public long NozzleId { get; set; }
 
@@ -535,7 +541,7 @@ namespace HengshanPaymentTerminal.Http.Request
         public string TransactionNumber { get; set; } = string.Empty;
 
         /// <summary>
-        /// 结果 1:FCC 与油机断开连接;2:FCC 发送信息到油机,但超时未回复;3:授权失败;4:授权成功;5:未找到流水号
+        /// 结果 1:FCC 与油机断开连接;2:FCC 发送信息到油机,但超时未回复;3:取消授权失败;4:取消授权成功;5:未找到流水号
         /// </summary>
         public OilMachineStatus OilMachineStatus { get; set; }
     }

+ 34 - 10
HengshanPaymentTerminal/Http/Response/Response.cs

@@ -206,8 +206,24 @@ namespace HengshanPaymentTerminal.Http.Response
 
     }
 
-    public enum AuthorizationStatus
+    public enum RefundStatus
+    {
+        /// <summary>
+        /// 无退款
+        /// </summary>
+        NotRefunded,
+        /// <summary>
+        /// 订单全额退款。
+        /// </summary>
+        FullyRefunded,
+
+        /// <summary>
+        /// 订单部分退款。
+        /// </summary>
+        PartiallyRefunded
+    }
 
+    public enum AuthorizationStatus
     {
         /// <summary>
         /// 未授权
@@ -217,12 +233,20 @@ namespace HengshanPaymentTerminal.Http.Response
         /// <summary>
         /// 授权
         /// </summary>
-        Authorized
+        Authorized,
+
+        /// <summary>
+        /// 等待授权
+        /// </summary>
+        WaitAuthorization,
 
+        /// <summary>
+        /// 加油中
+        /// </summary>
+        FillingUp
     }
 
     public enum transactionsORDERSTATUS
-
     {
         /// <summary>
         /// 订单未支付。
@@ -237,22 +261,22 @@ namespace HengshanPaymentTerminal.Http.Response
         /// <summary>
         /// 订单正在支付中。
         /// </summary>
-        //Paying,
+        Paying,
 
         /// <summary>
-        /// 订单全额退款
+        /// 订单通过卡支付
         /// </summary>
-        FullyRefunded,
+        CardPayment,
 
         /// <summary>
-        /// 订单部分退款。
+        /// 订单已完成
         /// </summary>
-        PartiallyRefunded,
+        Completed,
 
         /// <summary>
-        /// 订单通过卡支付。
+        /// 已取消
         /// </summary>
-        CardPayment
+        Cancelled
 
     }
 

+ 6 - 1
HengshanPaymentTerminal/MessageEntity/CommonMessage.cs

@@ -60,7 +60,12 @@ namespace HengshanPaymentTerminal.MessageEntity
             /// <summary>
             /// 发送应付金额给油机命令字
             /// </summary>
-            SEND_NEED_AMOUNT = 0x19
+            SEND_NEED_AMOUNT = 0x19,
+
+            /// <summary>
+            /// 发送退款信息油机命令字
+            /// </summary>
+            SEND_REFUND = 0x1A,
 
 
         }

+ 15 - 2
HengshanPaymentTerminal/MessageEntity/Incoming/OrderFromMachine.cs

@@ -132,8 +132,10 @@ namespace HengshanPaymentTerminal.MessageEntity.Incoming
                 UserName = "",
                 PhoneNumber = "",
                 PaymentName = "",
-                PumpCode = this.pumpCode
-
+                PumpCode = this.pumpCode,
+                AuthorizationStatus = 0,
+                RefundStatus = 0,
+                
             };
         }
 
@@ -149,6 +151,17 @@ namespace HengshanPaymentTerminal.MessageEntity.Incoming
             order.Amount = this.amount;
             order.Volume = this.volume;
             order.Price = this.price;
+            order.PumpCode = this.pumpCode;
+            //0 交易,证明是取消授权
+            if (this.amount == 0 && this.volume == 0)
+            {
+                order.AuthorizationStatus = 0;
+            }
+            else
+            {
+                order.AuthorizationStatus = 4;
+            }
+            
             return order;
         }
     }

+ 3 - 0
HengshanPaymentTerminal/MessageEntity/Outgoing/SendQrCode.cs

@@ -1,5 +1,6 @@
 using Edge.Core.Parser.BinaryParser.MessageEntity;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -12,6 +13,7 @@ namespace HengshanPaymentTerminal.MessageEntity.Outgoing
     /// </summary>
     public class SendQrCode : CommonMessage
     {
+        static NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
         /// <summary>
         /// 云端油枪id
         /// </summary>
@@ -42,6 +44,7 @@ namespace HengshanPaymentTerminal.MessageEntity.Outgoing
             byte[] commandAndNozzle = { this.Handle, (byte)this.NozzleNum };
             string qrCode = this.SmallProgram + "?id=" + this.CloundNozzleId;
             byte[] qrCodeBytes = Encoding.ASCII.GetBytes(qrCode);
+
             list.AddRange(commandAndNozzle);
             list.Add((byte)qrCodeBytes.Length);
             list.AddRange(qrCodeBytes);

+ 91 - 0
HengshanPaymentTerminal/MessageEntity/Outgoing/SendRefund.cs

@@ -0,0 +1,91 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace HengshanPaymentTerminal.MessageEntity.Outgoing
+{
+    public class SendRefund:CommonMessage
+    {
+
+        /// <summary>
+        /// 油枪号
+        /// </summary>
+        public int NozzleNum { get; set; }
+
+        /// <summary>
+        /// 流水号
+        /// </summary>
+        public int TTC { get; set; }
+
+        /// <summary>
+        /// 支付方式、地点。无效数据,占位。用0x21表示二维码订单
+        /// </summary>
+        public byte Pay_X {  get; set; }
+
+        /// <summary>
+        /// 加油金额
+        /// </summary>
+        public decimal Amount { get; set; }
+
+        /// <summary>
+        /// 实付金额
+        /// </summary>
+        public decimal AmountPayable { get; set; }
+
+        /// <summary>
+        /// 退款金额
+        /// </summary>
+        public decimal Refund {  get; set; }
+        public SendRefund(int nozzle, string ttc,decimal amount,decimal amountPayable,decimal refund, byte frame)
+        {
+            this.Handle = (byte)Command.SEND_REFUND;
+            this.NozzleNum = nozzle;
+
+            int.TryParse(ttc,out int ttcNum);
+            this.TTC = ttcNum;
+            this.Pay_X = 0x21;
+            this.Amount = amount;
+            this.AmountPayable = amountPayable;
+            this.Refund = refund;
+            this.FrameNum = frame;
+        }
+
+        public override byte[] ToCommonByteArray()
+        {
+            List<Byte> list = new List<Byte>();
+            byte[] commandAndNozzle = { this.Handle, (byte)this.NozzleNum };
+            byte[] ttcBytes = NumberToByteArrayWithPadding(this.TTC, 4);
+
+            //加油金额
+            int amount = (int)(this.Amount * 100);
+            byte[] amountBytes = NumberToByteArrayWithPadding(amount, 3);
+
+            //实付金额
+            int amountPayable = (int)(this.AmountPayable * 100);
+            byte[] amountPayableBytes = NumberToByteArrayWithPadding(amountPayable, 3);
+
+            //退款金额
+            int refund = (int)(this.Refund * 100);
+            byte[] refundBytes = NumberToByteArrayWithPadding(refund, 3);
+
+
+            list.AddRange(commandAndNozzle);
+            list.AddRange(ttcBytes);
+            list.Add(this.Pay_X);
+            list.AddRange(amountBytes);
+            list.AddRange(amountPayableBytes);
+            list.AddRange(refundBytes);
+
+            byte[] sendBytes = content2data(list.ToArray());
+
+            return sendBytes;
+        }
+
+        public override CommonMessage ToObject(byte[] datas)
+        {
+            return this;
+        }
+    }
+}

+ 55 - 25
HengshanPaymentTerminal/Mqtt/Request/MqttRequest.cs

@@ -1,8 +1,10 @@
 using Edge.Core.Domain.FccOrderInfo;
 using HengshanPaymentTerminal.Http.Response;
+using Newtonsoft.Json;
 using Newtonsoft.Json.Linq;
 using System;
 using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations.Schema;
 using System.Linq;
 using System.Text;
 using System.Text.Json;
@@ -15,10 +17,10 @@ namespace HengshanPaymentTerminal.Mqtt.Request
     /// </summary>
     public class MqttRequest
     {
-        /// <summary>
-        /// 类型
-        /// </summary>
-        public MQTT_TYPE type { get; set; }
+        ///// <summary>
+        ///// 类型
+        ///// </summary>
+        //public MQTT_TYPE type { get; set; }
 
         /// <summary>
         /// 数据
@@ -197,8 +199,10 @@ namespace HengshanPaymentTerminal.Mqtt.Request
         /// </summary>
         public decimal Price { get; set; }
 
-        public FccOrderInfo ToComponent(DateTime authorizationTime)
+        public FccOrderInfo ToComponent(DateTime authorizationTime, string paymentName,string? userName,string? phoneNumber)
         {
+            string userNameStr = userName ?? "";
+            string phoneStr = phoneNumber ?? string.Empty;
             return new FccOrderInfo()
             {
                 AuthorizationTime = authorizationTime,
@@ -213,8 +217,11 @@ namespace HengshanPaymentTerminal.Mqtt.Request
                 AmountPayable = ActualPaymentAmount,
                 UploadState = 1,
                 IsDelete = 0,
-                Price = this.Price
-                
+                Price = this.Price,
+                PaymentName = paymentName,
+                UserName = userNameStr,
+                PhoneNumber = phoneStr
+
             };
         }
 
@@ -500,18 +507,36 @@ namespace HengshanPaymentTerminal.Mqtt.Request
         /// <summary>
         /// 单价
         /// </summary>
-        public decimal? Price { get; set; }
+        public decimal Price { get; set; }
 
         public decimal? FuelItemPumpTotalizerVolume {  get; set; }
-        ///// <summary>
-        ///// 用户名
-        ///// </summary>
-        //public string? UserName { get; set; }
 
-        ///// <summary>
-        ///// 手机号
-        ///// </summary>
-        //public string? UserPhoneNumber { get; set; }
+        public FccOrderInfo ToComponent(string? userName,string? phoneNumber,string paymentName)
+        {
+            string userNameStr = userName ?? "";
+            string phoneStr = phoneNumber ?? string.Empty;
+            int? paymentType = null;
+            if (this.PaymentMethod != null) paymentType = (int?)this.PaymentMethod;
+            return new FccOrderInfo()
+            {
+                PaymentTime = this.TransactionTime,
+                NozzleNum = (int)this.NozzleId,
+                OilName = this.ProductName,
+                PaymentStatus = (int)this.OrderStatus,
+                PayType = paymentType,
+                CloundOrderId = this.Id,
+                AmountPayable = this.ActualPaymentAmount,
+                UploadState = 1,
+                IsDelete = 0,
+                Price = this.Price,
+                UserName = userNameStr,
+                PhoneNumber = phoneStr,
+                PaymentName = paymentName,
+                AuthorizationStatus = 0,
+                RefundStatus = 0,
+                VolumePayable = this.Qty,
+            };
+        }
     }
 
     /// <summary>
@@ -654,14 +679,19 @@ namespace HengshanPaymentTerminal.Mqtt.Request
         /// </summary>
         public decimal? Price { get; set; }
 
-        ///// <summary>
-        ///// 用户名
-        ///// </summary>
-        //public string? UserName {  get; set; }
-
-        ///// <summary>
-        ///// 手机号
-        ///// </summary>
-        //public string? UserPhoneNumber { get; set; }
+        /// <summary>
+        /// 订单编号
+        /// </summary>
+        public string BillNumber { get; set; }
+        /// <summary>
+        /// 退款状态
+        /// </summary>
+        [JsonProperty]
+        public RefundStatus RefundStatus { get; set; }
+        /// <summary>
+        /// 泵码
+        /// </summary>
+        [JsonProperty]
+        public decimal? FuelItemPumpTotalizerVolume { get; set; }
     }
 }

+ 18 - 0
src/FccLife.Web/Controller/OrderInfoController.cs

@@ -25,6 +25,15 @@ namespace FccLite.Web.Controller
             return Ok(orderInfoOutput);
         }
 
+        [HttpPost("getPagePrepay")]
+        public async Task<IActionResult> GetPagePrepay(OrderInfoPageInput orderInfoPageInput)
+        {
+            logger.Info($"order get page:{JsonConvert.SerializeObject(orderInfoPageInput)}");
+            PrepayOrderInfoOutput prepayOrderInfoOutput = await _orderInfoService.GetPrepayPage(orderInfoPageInput);
+            return Ok(prepayOrderInfoOutput);
+        }
+
+
         [HttpPost("downloadOrderAfterPayOrderExcel")]
         public async Task<IActionResult> DownloadOrderAfterPayOrderExcel(OrderInfoPageInput orderInfoPageInput)
         {
@@ -32,5 +41,14 @@ namespace FccLite.Web.Controller
             byte[] fileBytes = await _orderInfoService.DownloadAfterPayOrderExcel(orderInfoPageInput);
             return File(fileBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "orders.xlsx");
         }
+
+        [HttpPost("downloadOrderPrepayOrderExcel")]
+        public async Task<IActionResult> DownloadOrderPrePayOrderExcel(OrderInfoPageInput orderInfoPageInput)
+        {
+            logger.Info($"download after pay order excel:{JsonConvert.SerializeObject(orderInfoPageInput)}");
+            byte[] fileBytes = await _orderInfoService.DownloadPrepayOrderExcel(orderInfoPageInput);
+            return File(fileBytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "orders.xlsx");
+        }
+
     }
 }

+ 4 - 4
src/FccLife.Web/Repositories/FccOrderInfo/OrderInfoReposity.cs

@@ -31,8 +31,8 @@ namespace FccLite.Web.Repositories.FccOrderInfo
                         where string.IsNullOrEmpty(orderInfoPageInput.oilName) || qo.OilName == orderInfoPageInput.oilName
                         where orderInfoPageInput.nozzleNum == null || qo.NozzleNum == orderInfoPageInput.nozzleNum
                         where orderInfoPageInput.paymentType == null || qo.PayType == orderInfoPageInput.paymentType
-                        where (orderInfoPageInput.startCheckTime == null || orderInfoPageInput.endCheckTime == null || qo.EndTime == null)
-                                || qo.EndTime >= orderInfoPageInput.startCheckTime && qo.EndTime <= orderInfoPageInput.endCheckTime
+                        where (orderInfoPageInput.startCheckTime == null || orderInfoPageInput.endCheckTime == null)
+                                || qo.AuthorizationTime >= orderInfoPageInput.startCheckTime && qo.AuthorizationTime <= orderInfoPageInput.endCheckTime
                         where string.IsNullOrEmpty(orderInfoPageInput.phoneNumberOruserName) || qo.UserName == orderInfoPageInput.phoneNumberOruserName || qo.PhoneNumber == orderInfoPageInput.phoneNumberOruserName
                         orderby qo.AuthorizationTime descending
                         select new OrderInfo(qo, stationName, qm.Name);
@@ -52,8 +52,8 @@ namespace FccLite.Web.Repositories.FccOrderInfo
                         where string.IsNullOrEmpty(orderInfoPageInput.oilName) || qo.OilName == orderInfoPageInput.oilName
                         where orderInfoPageInput.nozzleNum == null || qo.NozzleNum == orderInfoPageInput.nozzleNum
                         where orderInfoPageInput.paymentType == null || qo.PayType == orderInfoPageInput.paymentType
-                        where (orderInfoPageInput.startCheckTime == null || orderInfoPageInput.endCheckTime == null || qo.EndTime == null)
-                                || qo.EndTime >= orderInfoPageInput.startCheckTime && qo.EndTime <= orderInfoPageInput.endCheckTime
+                        where (orderInfoPageInput.startCheckTime == null || orderInfoPageInput.endCheckTime == null)
+                                || qo.AuthorizationTime >= orderInfoPageInput.startCheckTime && qo.AuthorizationTime <= orderInfoPageInput.endCheckTime
                         where string.IsNullOrEmpty(orderInfoPageInput.phoneNumberOruserName) || qo.UserName == orderInfoPageInput.phoneNumberOruserName || qo.PhoneNumber == orderInfoPageInput.phoneNumberOruserName
                         orderby qo.AuthorizationTime descending
                         select new OrderInfo(qo, stationName, qm.Name);

+ 1 - 0
src/FccLife.Web/Repositories/FccStationInfo/StationRepository.cs

@@ -88,6 +88,7 @@ namespace FccLite.Web.Repositories.FccStationInfo
                 station.IcardService = stationInfoInput.IcardService != null ? stationInfoInput.IcardService : "";
                 station.WebSocketPort = stationInfoInput.WebSocketPort;
                 station.PaymentType = stationInfoInput.PaymentType;
+                station.CheckOrderInterval = stationInfoInput.CheckOrderInterval;
                 _dbContext.SaveChanges();
                 return station;
             }

+ 15 - 1
src/FccLife.Web/Services/FccOrderInfo/IOrderInfoService.cs

@@ -7,7 +7,7 @@ namespace FccLite.Web.Services.FccOrderInfo
     public interface IOrderInfoService
     {
         /// <summary>
-        /// 分页获取订单
+        /// 分页获取后支付订单
         /// </summary>
         /// <param name="orderInfoOutput">过滤条件</param>
         /// <returns></returns>
@@ -19,5 +19,19 @@ namespace FccLite.Web.Services.FccOrderInfo
         /// <param name="orderInfoPageInput"></param>
         /// <returns></returns>
         Task<byte[]> DownloadAfterPayOrderExcel(OrderInfoPageInput orderInfoPageInput);
+
+        /// <summary>
+        /// 分页获取预支付订单
+        /// </summary>
+        /// <param name="orderInfoOutput">过滤条件</param>
+        /// <returns></returns>
+        Task<PrepayOrderInfoOutput> GetPrepayPage(OrderInfoPageInput orderInfoPageInput);
+
+        /// <summary>
+        /// 下载预支付订单excel
+        /// </summary>
+        /// <param name="orderInfoPageInput"></param>
+        /// <returns></returns>
+        Task<byte[]> DownloadPrepayOrderExcel(OrderInfoPageInput orderInfoPageInput);
     }
 }

+ 102 - 2
src/FccLife.Web/Services/FccOrderInfo/OrderInfoService.cs

@@ -13,7 +13,9 @@ namespace FccLite.Web.Services.FccOrderInfo
         private readonly IOrderInfoReposity _orderInfoReposity;
         private readonly IStationRepository _stationRepository;
 
-        private readonly string[] tableTitle = { "流水号","油站", "油机号", "油枪", "油品", "单价", "升数", "金额", "挂枪时间", "交易时间", "支付方式", "支付状态", "支付用户" };
+        private readonly string[] tableTitle = { "流水号", "油站", "油机号", "油枪", "油品", "单价", "升数", "金额", "挂枪时间", "交易时间", "支付方式", "支付状态", "支付用户" };
+        private readonly string[] prepaytTableTitle = { "订单id","云端订单id","流水号","油站", "油机号", "油枪", "油品", "单价", "升数", "金额", "实付金额","实加升数","退款金额",
+            "挂枪时间", "交易时间", "支付方式","授权状态","支付状态", "支付用户","手机号", };
         private readonly Dictionary<int, string> _afterPayType = new Dictionary<int, string>
         {
             {0,"未支付" },
@@ -39,7 +41,11 @@ namespace FccLite.Web.Services.FccOrderInfo
             };
         }
 
-
+        /// <summary>
+        /// 后支付订单下载excel
+        /// </summary>
+        /// <param name="orderInfoPageInput"></param>
+        /// <returns></returns>
         public async Task<byte[]> DownloadAfterPayOrderExcel(OrderInfoPageInput orderInfoPageInput)
         {
             IEnumerable<OrderInfo> enumerable = await _orderInfoReposity.GetOrderFilter(orderInfoPageInput);
@@ -97,5 +103,99 @@ namespace FccLite.Web.Services.FccOrderInfo
 
             return new byte[0];
         }
+
+        /// <summary>
+        /// 预支付获取订单页
+        /// </summary>
+        /// <param name="orderInfoPageInput"></param>
+        /// <returns></returns>
+        public async Task<PrepayOrderInfoOutput> GetPrepayPage(OrderInfoPageInput orderInfoPageInput)
+        {
+            IEnumerable<OrderInfo> enumerable = await _orderInfoReposity.GetPage(orderInfoPageInput);
+            List<PrepayOrderInfo> prepayOrderInfos = enumerable.Select(order => new PrepayOrderInfo(order)).ToList();
+
+            return new PrepayOrderInfoOutput()
+            {
+                Total = prepayOrderInfos.Count,
+                List = prepayOrderInfos
+            };
+        }
+
+        /// <summary>
+        /// 预支付下载订单excel
+        /// </summary>
+        /// <param name="orderInfoPageInput"></param>
+        /// <returns></returns>
+        public async Task<byte[]> DownloadPrepayOrderExcel(OrderInfoPageInput orderInfoPageInput)
+        {
+            IEnumerable<OrderInfo> enumerable = await _orderInfoReposity.GetOrderFilter(orderInfoPageInput);
+            List<PrepayOrderInfo> prepayOrderInfos = enumerable.Select(order => new PrepayOrderInfo(order)).ToList();
+            try
+            {
+                var package = new ExcelPackage();
+                ExcelWorksheet excelWorksheet = package.Workbook.Worksheets.Add("Sheet1");
+
+                for (int index = 0; index < prepaytTableTitle.Length; index++)
+                {
+                    excelWorksheet.Cells[1, index + 1].Value = prepaytTableTitle[index];
+                }
+
+                using (var range = excelWorksheet.Cells[1, 1, 1, prepaytTableTitle.Length])
+                {
+                    range.Style.Font.Bold = true;
+                }
+
+                int row = 2;
+                decimal allVolume = 0;
+                decimal allAmount = 0;
+                decimal allAmountPayable = 0;
+                decimal allVolumePayable = 0;
+                decimal allRefundAmount = 0;
+                foreach (var order in prepayOrderInfos)
+                {
+                    excelWorksheet.Cells[row, 1].Value = order.Id;
+                    excelWorksheet.Cells[row, 2].Value = order.CloundOrderId;
+                    excelWorksheet.Cells[row, 3].Value = order.Ttc;
+                    excelWorksheet.Cells[row, 4].Value = order.StationName;
+                    excelWorksheet.Cells[row, 5].Value = order.MachineName;
+                    excelWorksheet.Cells[row, 6].Value = order.NozzleNum;
+                    excelWorksheet.Cells[row, 7].Value = order.OilName;
+                    excelWorksheet.Cells[row, 8].Value = order.Price;
+                    excelWorksheet.Cells[row, 9].Value = order.Volume;
+                    excelWorksheet.Cells[row, 10].Value = order.Amount;
+                    excelWorksheet.Cells[row, 11].Value = order.AmountPayable;
+                    excelWorksheet.Cells[row, 12].Value = order.VolumePayable;
+                    excelWorksheet.Cells[row, 13].Value = order.RefundAmount;
+                    excelWorksheet.Cells[row, 14].Value = order.EndTime != null ? order.EndTime?.ToString("yyyy-MM-dd HH:mm:ss") : "";
+                    excelWorksheet.Cells[row, 15].Value = order.PaymentTime != null ? order.PaymentTime?.ToString("yyyy-MM-dd HH:mm:ss") : "";
+                    excelWorksheet.Cells[row, 16].Value = order.PaymentName;
+                    excelWorksheet.Cells[row, 17].Value = order.AuthorizationStatus;
+                    excelWorksheet.Cells[row, 18].Value = order.PaymentStatus;
+                    excelWorksheet.Cells[row, 19].Value = order.UserName;
+                    excelWorksheet.Cells[row, 20].Value = order.PhoneNumber;
+
+                    allVolume += order.Volume;
+                    allAmount += order.Amount;
+                    allAmountPayable += order.AmountPayable ?? 0;
+                    allVolumePayable += order.VolumePayable ?? 0;
+                    allRefundAmount += order.RefundAmount ?? 0;
+                    row++;
+                }
+                excelWorksheet.Cells[row, 9].Value = allVolume;
+                excelWorksheet.Cells[row, 10].Value = allAmount;
+                excelWorksheet.Cells[row, 11].Value = allAmount;
+                excelWorksheet.Cells[row, 12].Value = allAmount;
+                excelWorksheet.Cells[row, 13].Value = allAmount;
+
+                return package.GetAsByteArray();
+            }
+            catch (Exception e)
+            {
+                Console.WriteLine(e);
+                Logger.Error(e.Message);
+            }
+
+            return new byte[0];
+        }
     }
 }

+ 5 - 0
src/FccLife.Web/appsettings.Production.json

@@ -15,5 +15,10 @@
         "Url": "http://0.0.0.0:9999"
       }
     }
+  },
+  "Mqtt": {
+    "subTopic": [ "authorization", "unAuthorization", "paid", "refund" ],
+    "user": "HSClient",
+    "password": "HS202503"
   }
 }

+ 19 - 1
src/FccWeb/admin.ui.plus-master/src/api/api.ts

@@ -167,6 +167,7 @@ export const deleteOilInfo = (data:any)=>{
 }
 
 /** ----- 订单 ----- */
+//后支付订单
 export const getOrderInfoPage = (data:any) => {
 	return request({
 		url:'/qrFueling/order/getPage',
@@ -174,6 +175,14 @@ export const getOrderInfoPage = (data:any) => {
 		data:data,
 	})
 }
+//预支付订单
+export const getPrepayOrderInfoPage = (data:any) => {
+	return request({
+		url:'/qrFueling/order/getPagePrepay',
+		method:'post',
+		data:data,
+	})
+}
 
 // 列表信息筛选
 export const getOrderFilter =(data:any)=>{
@@ -184,7 +193,7 @@ export const getOrderFilter =(data:any)=>{
 	})
 }
 
-//导出excel
+//导出后支付订单excel
 export const downloadAfterpayOrder = (data:any) => {
 	const send = axios.create({
 		timeout: 5000, // 超时
@@ -193,3 +202,12 @@ export const downloadAfterpayOrder = (data:any) => {
 	return send.post('/qrFueling/order/downloadOrderAfterPayOrderExcel', data, { responseType: 'arraybuffer' })
 }
 
+//导出预支付订单excel
+export const downloadPrepaypayOrder = (data:any) => {
+	const send = axios.create({
+		timeout: 5000, // 超时
+		baseURL: import.meta.env.VITE_API_URL_FCC,
+	})
+	return send.post('/qrFueling/order/downloadOrderPrepayOrderExcel', data, { responseType: 'arraybuffer' })
+}
+

+ 16 - 1
src/FccWeb/admin.ui.plus-master/src/router/route.ts

@@ -97,7 +97,22 @@ export const dynamicRoutes: Array<RouteRecordRaw> = [
         name: '/order',
         component: () => import('/@/views/admin/order/index.vue'),
         meta: {
-          title: '订单信息',
+          title: '后支付订单',
+          isLink: '',
+          isHide: false,
+          isKeepAlive: true,
+          isAffix: false,
+          isIframe: false,
+          roles: ['admin', 'common'],
+          icon: 'iconfont icon-crew_feature',
+        },
+      },
+      {
+        path: '/prepayOrder',
+        name: '/prepayOrder',
+        component: () => import('/@/views/admin/prepayOrder/index.vue'),
+        meta: {
+          title: '预支付订单',
           isLink: '',
           isHide: false,
           isKeepAlive: true,

+ 9 - 1
src/FccWeb/admin.ui.plus-master/src/views/admin/components/addStation.vue

@@ -70,6 +70,11 @@
                         <el-input v-model="dataList.paymentType" placeholder="请按[id+支付方式,id+支付方式]填写,如1+微信支付,2+支付宝支付" clearable />
                     </el-form-item>
                 </el-col>
+                <el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+                    <el-form-item label="订单最大授权时间" :required="true" prop="checkOrderInterval">
+                        <el-input v-model="dataList.checkOrderInterval" placeholder="分钟" clearable />
+                    </el-form-item>
+                </el-col>
             </el-row>
         </el-form>
         <template #footer>
@@ -120,7 +125,9 @@ let dataList = ref({
     /** 纬度 */
     latitude: null,
     /** 支付方式 */
-    paymentType:null
+    paymentType:null,
+    /** 订单授权时间 */
+    checkOrderInterval:0,
 })
 
 // 创建表单实例
@@ -168,6 +175,7 @@ const openDialog = (val: any) => {
             name: '',
             longitude: null,
             latitude: null,
+            checkOrderInterval:0,
         }
     }
     isDisplay.isShowDialog = true

+ 114 - 0
src/FccWeb/admin.ui.plus-master/src/views/admin/components/showOrderTip.vue

@@ -0,0 +1,114 @@
+<!-- 
+** 详细信息列表 -- 油罐、油枪
+-->
+<template>
+    <el-dialog :title=isDisplay.title v-model="isDisplay.isShowDialog" width="300px" class="main-content">
+        <div class="orderItem">
+            <div>订单号:</div>
+            <div>{{ isDisplay.order.id }}</div>
+        </div>
+        <div class="orderItem">
+            <div>云端订单号:</div>
+            <div>{{ isDisplay.order.cloundOrderId }}</div>
+        </div>
+        <div class="orderItem">
+            <div>流水号:</div>
+            <div>{{ isDisplay.order.ttc }}</div>
+        </div>
+        <div class="orderItem">
+            <div>枪号:</div>
+            <div>{{ isDisplay.order.nozzleNum }}号枪</div>
+        </div>
+        <div class="orderItem">
+            <div>油品:</div>
+            <div>{{ isDisplay.order.oilName }}</div>
+        </div>
+        <div class="orderItem">
+            <div>单价:</div>
+            <div>{{ isDisplay.order.price }}元/升</div>
+        </div>
+        <div class="orderItem">
+            <div>加油金额:</div>
+            <div>{{ isDisplay.order.amount }}元</div>
+        </div>
+        <div class="orderItem">
+            <div>加油升数:</div>
+            <div>{{ isDisplay.order.volume }}升</div>
+        </div>
+        <div class="orderItem">
+            <div>实付金额:</div>
+            <div>{{ isDisplay.order.amountPayable }}元</div>
+        </div>
+        <div class="orderItem">
+            <div>退款金额:</div>
+            <div>{{ isDisplay.order.refundAmount }}元</div>
+        </div>
+        <div class="orderItem">
+            <div>支付时间:</div>
+            <div>{{ isDisplay.order.paymentTime }}</div>
+        </div>
+        <div class="orderItem">
+            <div>授权状态:</div>
+            <div>{{ isDisplay.order.authrization }}</div>
+        </div>
+        <div class="orderItem">
+            <div>支付状态:</div>
+            <div>{{ isDisplay.order.paystatus }}</div>
+        </div>
+        <div class="orderItem">
+            <div>支付类型:</div>
+            <div>{{ isDisplay.order.paymentName }}</div>
+        </div>
+        <div class="orderItem">
+            <div>用户:</div>
+            <div>{{ isDisplay.order.user }}</div>
+        </div>
+        <div class="orderItem">
+            <div>手机号:</div>
+            <div>{{ isDisplay.order.phoneNumber }}</div>
+        </div>
+    </el-dialog>
+</template>
+<script setup lang="ts">
+import { onBeforeMount, onMounted, reactive, ref } from 'vue';
+
+const isDisplay = reactive({
+    isShowDialog: false,
+    loading: false,
+    title: '订单详情',
+    order:null
+})
+
+// 挂载前
+onBeforeMount(() => {
+    
+})
+
+// 挂载时
+onMounted(async () => {
+    
+})
+
+
+const openDialog = (row) => {
+    isDisplay.isShowDialog = true
+    isDisplay.order = row
+}
+
+defineExpose({
+    openDialog,
+})
+
+
+</script>
+
+<style lang="scss">
+.orderItem{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    width: 100%;
+    margin-bottom: 3%;
+    margin-right: 10%;
+}
+</style>

+ 2 - 2
src/FccWeb/admin.ui.plus-master/src/views/admin/order/index.vue

@@ -208,11 +208,11 @@ const onQuery = async () => {
                 'nozzleAndOil': order.nozzleNum + "|" + order.oilName,
                 'priceAndVolume': order.price + "|" + order.volume + 'L',
                 'amount': order.amount,
-                'paymentTime': order.authorizationTime,
+                'paymentTime': order.paymentTime,
                 'endTime': order.endTime,
                 'fuelId': order.paymentStatus === 0? "未支付" : order.paymentStatus === 1? "已支付" : "未知状态",
                 'phoneNumber': order.userName,
-                'paymentName': order.payType === 1? "微信支付" : order.payType === 2? "支付宝" : "未知状态",
+                'paymentName': order.paymentName,
                 "machineName": order.machineName
             };
         });

+ 434 - 0
src/FccWeb/admin.ui.plus-master/src/views/admin/prepayOrder/index.vue

@@ -0,0 +1,434 @@
+<template>
+    <div class="layout-pd">
+        <!-- 操作 -->
+        <el-row>
+            <el-col :xs="24">
+                <el-card class="mt8" shadow="hover">
+                    <el-form :model="Data.Filter" @submit.stop.prevent>
+                        <el-form-item prop="name" style="width: 100%">
+                            <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
+                                <el-form-item label="油枪">
+                                    <el-input v-model="Data.Filter.nozzleNum" placeholder="请输入油枪" clearable></el-input>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
+                                <el-form-item label="油品">
+                                    <el-input v-model="Data.Filter.oilName" placeholder="请输入集合编号" clearable></el-input>
+                                </el-form-item>
+                            </el-col>
+                            <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
+                                <el-form-item label="支付方式">
+                                    <el-select v-model="Data.selectedPayment" placeholder="请选择支付方式">
+                                 <el-option v-for="payment in Data.paymentOptions" :key="payment.id" :label="payment.paymentName" :value="payment.id + ''" />
+                                </el-select>
+                                </el-form-item>
+                            </el-col> 
+                            <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
+                                <el-form-item label="油站">
+                                    <el-select v-model="Data.Filter.StationName" placeholder="全部">
+                                        <el-option v-for="(value, key) in selectList.station" :key="key" :label="value" :value="value" />
+                                    </el-select>
+                                </el-form-item>
+                            </el-col> 
+                            <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
+                                <el-form-item label="加油时间">
+                                    <el-date-picker v-model="Data.time1" type="datetimerange" value-format="YYYY-MM-DD HH:mm:ss" range-separator="To" start-placeholder="开始日期" end-placeholder="结束日期" clearable=false />
+                                </el-form-item>
+                            </el-col>   
+                            <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="6" class="mb20">
+                                <el-form-item label="支付用户">
+                                    <el-input v-model="Data.Filter.phoneNumberOruserName" placeholder="请输入用户名称和手机号" clearable></el-input>
+                                </el-form-item>
+                            </el-col> 
+                        </el-form-item>
+                    </el-form>
+                    
+                    <hr>
+
+                    <!-- 按钮 -->
+                    <el-row justify="space-between" class="submit-button">
+                        <el-row>
+                            <el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
+                            <el-button type="primary" icon="ele-RefreshRight" @click="onReset"> 重置 </el-button>
+                            <el-button type="primary" icon="ele-Search" @click="downloadExcel"> 导出EXCEL </el-button>
+                        </el-row>
+                    </el-row>
+                </el-card>
+            </el-col>
+            <!--表格-->
+            <el-col :xs="24">
+                <el-card style="height: 70vh" class="my-fill mt8" shadow="hover">
+                    <el-table ref="multipleTableRef" v-loading="Data.loading" stripe :data="Data.tableModel"
+                        row-key="id" style="width: 100%">
+                        <el-table-column v-for="column in Data.dynamicColumns" :key="column.prop" :prop="column.prop"
+                            :label="column.label" />
+                        <!-- 操作 -->
+                        <el-table-column label="操作" fixed="right" align="center" header-align="center"
+                            class="right-operation" width="140">
+                            <template #default="scope">
+                                <el-link class="my-el-link mr12 ml12" type="primary" :underline="false" target="_blank"
+                                    icon="ele-Tickets" @click.stop="toView(scope.row)">查看</el-link>
+                            </template>
+                        </el-table-column>
+                    </el-table>
+                    
+                    <!-- 页码 -->
+                    <div class="my-flex my-flex-end" style="margin-top: 20px">
+                        <el-pagination 
+                            v-model:currentPage="Data.pageInput.page"
+                            v-model:page-size="Data.pageInput.pageSize" 
+                            :total="Data.total"
+                            :default-page-size="10" 
+                            :page-sizes="[10,20,50,100]" 
+                            small 
+                            background 
+                            @size-change="onSizeChange" 
+                            @current-change="onCurrentChange" 
+                            :page-count="pageCount"
+                            layout="total, sizes, prev, pager, next, jumper" 
+                        />
+                        
+                    </div>
+                </el-card>
+            </el-col>
+        </el-row>
+
+        <ShowOrderTip ref="showOrderTip" />
+    </div>
+</template>
+
+<script setup lang="ts" name="partsManagement/oilGun">
+import { onMounted, reactive, watch, computed, defineAsyncComponent, ref } from "vue";
+import { getPrepayOrderInfoPage,getOrderFilter,downloadPrepaypayOrder } from "/@/api/api";
+import { OrderInfoFilter } from "/@/api/admin/orderDto/QRBookDto";
+import { nextTick } from 'vue';
+import { formatDate } from '/@/utils/formatTime'
+
+/** 组件 */
+const ShowOrderTip = defineAsyncComponent(() => import('/@/views/admin/components/showOrderTip.vue'))
+const showOrderTip = ref()
+
+/**页面对象 */
+const Data = reactive({
+    time1: '',
+    /**加载显示 */
+    loading: false,
+    /**条件查询模块 */
+    Filter: {
+        /**油枪号 */
+        nozzleNum: null,
+        /**油品 */
+        oilName: null,
+        /**支付方式 */
+        onlineStatus: null,
+        /**加油开始的时间 */
+        startCheckTime: null,
+        /**加油结束的时间 */
+        endCheckTime: null,
+        // 支付方式类型
+        paymentType:null,
+        // 用户手机号码or用户名称
+        phoneNumberOruserName:null
+    } as OrderInfoFilter,
+    /**表格信息 */
+    tableModel: [],
+    /**动态表头 */
+    dynamicColumns: [
+        { prop: 'id', label: '订单号' },
+        { prop: 'stationName', label: '油站' }, 
+        { prop: 'machineName', label: '油机号' },
+        { prop: 'nozzleAndOil', label: '油枪|油品' },
+        { prop: 'priceAndVolume', label: '单价|升数' },
+        { prop: 'amount', label: '金额' },
+        { prop: 'paymentTime', label: '支付时间' },
+        { prop: 'paystatus', label: '支付状态' },
+        { prop: 'authrization', label: '加油授权' },
+        { prop: 'paymentName', label: '支付方式' },
+        { prop: 'user', label: '用户' } 
+    ], 
+    /**分页总数 */
+    total: 0,
+    /**分页标识 */
+    pageInput: {
+        page: 1,
+        pageSize: 10,
+    },  
+  selectedPayment:'', //储存ID
+  paymentOptions:[] //储存支付方式
+})
+
+/** 选择框列表 */
+const selectList = reactive({
+    Online: ["在线", "离线"],
+    Device: [   
+        "启用",
+        "备案",
+        "维修",
+        "出厂注册"],
+    Level: [1, 2, 3, 4, 5],
+    station:[
+        '全部'
+    ]
+})  
+
+// 计算当前页的数据
+const currentPageData = computed(() => {
+    console.log('Data.tableModel', Data.tableModel); // 添加调试语句,查看数据是否正确更新
+    const start = (Data.pageInput.page - 1) * Data.pageInput.pageSize;
+    const end = start + Data.pageInput.pageSize;
+    return Data.tableModel.slice(start, end);
+});
+
+// 计算显示的页码数量
+const pageCount = computed(() => {
+    const totalPages = Math.ceil(Data.total / Data.pageInput.pageSize);
+    return totalPages < 100? 100 : totalPages;
+});
+
+/** 查询订单 */
+const onQuery = async () => {
+    Data.loading = true;
+    try {
+        let filterParams = {
+            page: Data.pageInput.page,
+            pageSize: Data.pageInput.pageSize,
+           ...Data.Filter
+        };
+        // 根据选中的支付方式设置筛选参数
+        if (Data.selectedPayment && Data.selectedPayment!== '0') {
+            filterParams.paymentType = parseInt(Data.selectedPayment);
+        } else {
+            // 如果选择“全部”,不传递paymentType参数
+            filterParams.paymentType = null; 
+        }
+        const orders = await getPrepayOrderInfoPage(filterParams);
+        console.log('后端返回的原始数据:', orders);
+        console.log('后端返回的总记录数:', orders.total);
+        console.log('后端返回的当前页数据列表长度:', orders.list.length);
+
+        Data.total = orders.total;
+        Data.tableModel = orders.list.map(order => {
+            return {
+                "id":order.id,
+                "cloundOrderId":order.cloundOrderId,
+                'stationName': order.stationName,
+                'ttc': order.ttc,
+                'nozzleAndOil': order.nozzleNum + "|" + order.oilName,
+                'priceAndVolume': order.price + "|" + order.volume + 'L',
+                'nozzleNum':order.nozzleNum,
+                'oilName':order.oilName,
+                'price':order.price,
+                'amount': order.amount,
+                'volume':order.volume,
+                'amountPayable':order.amountPayable,
+                'volumePayable':order.volumePayable,
+                'refundAmount':order.refundAmount,
+                'paymentTime': order.paymentTime,
+                'endTime': order.endTime,
+                'paystatus': order.paymentStatus,
+                'user': order.userName,
+                'phoneNumber': order.phoneNumber,
+                'paymentName': order.paymentName,
+                "machineName": order.machineName,
+                "authrization":order.authorizationStatus,
+            };
+        });
+        console.log('处理后的数据列表长度:', Data.tableModel.length);
+    } catch (error) {
+        console.error('查询订单数据时出错:', error);
+    } finally {
+        Data.loading = false;
+        await nextTick();
+    }
+};
+
+
+
+/** 重置查询条件 */
+const onReset = () => {
+    Data.Filter = {
+        nozzleNum: null,
+        oilName: null,
+        onlineStatus: null,
+        startCheckTime: null,
+        endCheckTime: null,
+        StationName: null,
+        payUserName: null,
+        paymentType: null,
+        phoneNumberOruseName: null
+    };
+    Data.time1 = "";
+    // 重置时选择“全部”
+    Data.selectedPayment = '0'; 
+    initTime()
+};
+
+/** 下载excel */
+const downloadExcel = async () => {
+    // return
+    Data.loading = true;
+    try {
+        let filterParams = {
+            page: Data.pageInput.page,
+            pageSize: Data.pageInput.pageSize,
+           ...Data.Filter
+        };
+        // 根据选中的支付方式设置筛选参数
+        if (Data.selectedPayment && Data.selectedPayment!== '0') {
+            filterParams.paymentType = parseInt(Data.selectedPayment);
+        } else {
+            // 如果选择“全部”,不传递paymentType参数
+            filterParams.paymentType = null; 
+        }
+        const stream = await downloadPrepaypayOrder(filterParams);
+        const url = window.URL.createObjectURL(new Blob([stream.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }))
+        const link = document.createElement('a');
+        link.href = url;
+        
+        link.setAttribute('download',
+        `${formatDate(filterParams.startCheckTime,"YYYY-mm-dd")}-${formatDate(filterParams.startCheckTime,"YYYY-mm-dd")}订单报表.xlsx`)
+        
+        document.body.appendChild(link);
+        link.click();
+        // 下载完成后移除链接,并释放内存
+        link.remove();
+        window.URL.revokeObjectURL(url);
+    } catch (error) {
+        console.error('查询订单数据时出错:', error);
+    } finally {
+        Data.loading = false;
+        await nextTick();
+    }
+}
+
+/** 页数修改 */
+const onSizeChange = (pageSize) => {
+    Data.pageInput.pageSize = pageSize;
+    Data.pageInput.page = 1; 
+    onQuery();
+}
+
+/** 页码修改 */
+const onCurrentChange = (page) => {
+    Data.pageInput.page = page;
+    onQuery();
+}
+
+/** 操作 */
+const toView = ((data: any) => {
+    console.log('查看',data)
+    showOrderTip.value.openDialog(data)
+})
+
+const initTime = () => {
+    const now = new Date();
+    const todayStart = new Date(now.getFullYear(),now.getMonth(),now.getDate());
+    var todayEnd = new Date(now.getFullYear(),now.getMonth(),now.getDate());
+    todayEnd.setHours(23,59,59,999);
+    Data.time1 = [formatDate(todayStart,"YYYY-mm-dd HH:MM:SS"),formatDate(todayEnd,"YYYY-mm-dd HH:MM:SS")]
+
+    Data.Filter.startCheckTime = todayStart;
+    Data.Filter.endCheckTime = todayEnd;
+}
+// 挂载时
+onMounted(async () => {
+    initTime();
+    const response = await getOrderFilter();
+    console.log("支付方式请求结果", response);
+    // 确保response.payments是一个数组
+    if (Array.isArray(response.payments)) {
+        const allOption = { id: 0, paymentName: '全部' };
+        Data.paymentOptions = [allOption, ...response.payments];
+    } else {
+        console.error("支付方式数据格式错误,response.payments不是一个数组");
+        Data.paymentOptions = [allOption];
+    }
+    console.log('支付方式筛选:', Data.paymentOptions);
+    // 初始化选择“全部”
+    Data.selectedPayment = '0'; 
+    await onQuery();
+});
+
+/** 过滤条件监听——枪号 */
+watch(() => Data.Filter.nozzleNum,(val) => {
+    if(val) {
+        Data.Filter.nozzleNum = val
+    } else {
+        Data.Filter.nozzleNum = null
+    }
+})
+
+/** 过滤条件监听——油品 */
+watch(() => Data.Filter.oilName,(val) => {
+    if(val) {
+        Data.Filter.oilName = val
+    } else {
+        Data.Filter.oilName = null
+    }
+})
+
+/** 过滤条件监听——时间 */
+watch(() => Data.time1,(val) => {
+    debugger
+    if(val == null || val?.length === 0) {
+        initTime()
+        return
+    }
+    Data.Filter.startCheckTime = new Date(val?.[0].toString());
+    Data.Filter.endCheckTime = new Date(val?.[1].toString());
+})
+
+/** 过滤条件监听——支付方式 */
+watch(() => Data.Filter.onlineStatus,(val) => {
+    if(val) {
+        Data.Filter.onlineStatus = val
+    } else {
+        Data.Filter.onlineStatus = null
+    }
+})
+
+/** 过滤条件监听——支付方式类型 */
+watch(() => Data.Filter.paymentType,(val) => {
+    if(val) {
+        Data.Filter.paymentType = val
+    } else {
+        Data.Filter.paymentType = null
+    }
+})
+
+/** 过滤条件监听——用户手机号码or用户名称 */
+watch(() => Data.Filter.phoneNumberOruseName,(val) => {
+    if(val) {
+        Data.Filter.phoneNumberOruseName = val
+    } else {
+        Data.Filter.phoneNumberOruseName = null
+    }
+})
+</script>
+
+<style scoped lang="scss">
+.el-input,
+.el-select {
+    width: 240px;
+}
+
+/* 输入框标签固定四个字符宽度 */
+::v-deep .el-form-item__label {
+    // 字体大小14,4个字符,12px右间距
+    width: 14*4px+12px;
+    justify-content: start;
+}
+
+/* 数据表头 设置灰色样式 */
+::v-deep .el-table th.el-table__cell {
+    background-color: #F6F6F6;
+}
+
+.mb21{
+    margin-left: 68px;
+}
+
+.mb22{
+    width: 2000px;
+}
+</style>