using System; using System.Collections.Generic; using System.Data; using System.Globalization; using System.Linq; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; namespace ConnectPoint { /// /// MainWindow.xaml 的交互逻辑 /// public partial class MainWindow : Window { #region 属性 /// /// 状态反馈主定时器 /// private readonly DispatcherTimer WorkerTimer; /// /// 平台稽核结果校验线程定时器 /// private readonly DispatcherTimer CheckAuditTimer; /// /// 数据传输接口地址 /// private string WebServiceUrl { get; set; } /// /// 主支付通道接口地址 /// private string PayServiceUrl { get; set; } /// /// 数据传输线程类 /// GetHttpData.SDK.ThreadPool.ClientThread DataClientThread { get; set; } ESSupport.Transfer.PosDataUpload DataUpload { get; set; } #endregion public MainWindow() { InitializeComponent(); #region 状态反馈主定时器,每分钟执行一次 WorkerTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(60) }; WorkerTimer.Tick += WorkerTimer_Tick; WorkerTimer.Start(); #endregion #region 检测并获取长款智能稽核记录的后台补录流水记录,10分钟执行一次 CheckAuditTimer = new DispatcherTimer { Interval = TimeSpan.FromMinutes(10) }; CheckAuditTimer.Tick += CheckAuditTimer_Tick; CheckAuditTimer.Start(); #endregion } #region 事件 -> 主程序启动加载事件 /// /// 主程序启动加载事件 /// /// /// private void Window_Loaded(object sender, RoutedEventArgs e) { this.Hide(); #region 程序启动时,进行过期日志、备份文件清理操作并升级本地表结构 new Thread(() => { int _FileCount = 0; //清理程序根目录下过期日志文件 try { ESSupport.Lib.FileHelper.DeleteFiles(AppDomain.CurrentDomain.BaseDirectory, new string[] { ".log" }, false, false, 10, ref _FileCount); } catch { } //清理错误日志文件夹中过期日志文件 try { ESSupport.Lib.FileHelper.DeleteFiles(AppDomain.CurrentDomain.BaseDirectory + "\\log", new string[] { ".log" }, false, false, 10, ref _FileCount); } catch { } //清理流水日志文件 try { ESSupport.Lib.FileHelper.DeleteFiles(AppDomain.CurrentDomain.BaseDirectory + "\\selldata", new string[] { ".log" }, false, false, 60, ref _FileCount); } catch { } //升级本地数据库表结构 try { ESSupport.Lib.DataBaseUpdate.PosDataBaseUpdate(); } catch { } //清理采集的过期客群图像文件 try { ESSupport.Lib.DataBaseUpdate.ClearCustomerInfo(AppDomain.CurrentDomain.BaseDirectory + "\\CustomerInfo", 7); } catch { } try { #region 清理本地数据库历史数据,默认清理60天以前的数据 //获取服务区编码 string _ServerPartCode = ESSupport.Pos.PosConfigInit.ConfigurationValues("serverpartcode", string.Empty); //获取门店编码 string _ShopCode = ESSupport.Pos.PosConfigInit.ConfigurationValues("shopcode", string.Empty); //获取数据保留时长 int _EffectiveDay = int.Parse(ESSupport.Pos.PosConfigInit.ConfigurationValues("effective_day", "60")); //获取门店ID string _ServerPartShopID = ESSupport.Lib.SyBaseHelper.QueryOdbc( $@"SELECT SERVERPARTSHOP_ID FROM T_SHOPMESSAGE WHERE SERVERPARTCODE = '{_ServerPartCode}' AND SHOPCODE = '{_ShopCode}'").Tables[0].Rows[0]["SERVERPARTSHOP_ID"].ToString(); //清理历史过期数据 //读取上次启动同步的时间 string _strLocalDate = ESSupport.Pos.PosConfigInit.ConfigurationValues("syncdtime", "2021/01/01 00:00:00"); if (DateTime.TryParse(_strLocalDate, out DateTime _LocalDate)) { //本地时间与上次同步时间误差在3天内时,启动数据库过期数据清理 if (DateTime.Now.Subtract(_LocalDate).TotalDays < 3 && DateTime.Now.Subtract(_LocalDate).TotalDays >= 0) { ESSupport.Lib.DataBaseUpdate.ClearData(_ServerPartCode, _ShopCode, _ServerPartShopID, _EffectiveDay); } } #endregion } catch { } //清理过期备份数据文件 try { #region 清理过期的历史交易文本备份文件 //读取上次启动同步的时间 string _strLocalDate = ESSupport.Pos.PosConfigInit.ConfigurationValues("syncdtime", "2021/01/01 00:00:00"); if (DateTime.TryParse(_strLocalDate, out DateTime _LocalDate)) { //本地时间同步失败超过3天或本地时间小于上次同步时间,不执行超时备份文件直接清理 if (DateTime.Now.Subtract(_LocalDate).TotalDays > 3 || DateTime.Now.Subtract(_LocalDate).TotalDays < 0) { ESSupport.Lib.DataBaseUpdate.ClearDataBackup(AppDomain.CurrentDomain.BaseDirectory + "\\DataBackup", 60, false); } else { ESSupport.Lib.DataBaseUpdate.ClearDataBackup(AppDomain.CurrentDomain.BaseDirectory + "\\DataBackup", 60, true); } } #endregion } catch { } //检查自动更新配置文件Update.xml参数 ESSupport.Pos.ThreadHelper.NewSystemConfig(); }) { IsBackground = true }.Start(); #endregion #region 程序启动时立即上报一次心跳数据,解决PB版本收银系统结账后立即关机导致版本信息未回传至服务器的问题 //程序启动时立即上报一次心跳数据,解决PB版本收银系统结账后立即关机导致版本信息未回传至服务器的问题 try { if (ESSupport.Common.PosControl.StateFeedbackThread == null || !ESSupport.Common.PosControl.StateFeedbackThread.IsAlive) { ESSupport.Common.PosControl.StateFeedbackThread = new Thread(() => { //ESSupport.Pos.ThreadHelper.StateFeedbackUpload($"http://{ESSupport.Pos.PosConfigInit.ConfigurationValues("server_ip", string.Empty)}:" + // $"{ESSupport.Pos.PosConfigInit.ConfigurationValues("DataServicePort", "7080")}/DataServices/Service.asmx"); if (DataUpload == null) { //测试当前网络是否可以直接访问公网 if (Common.PosControl.IsCloudURL) { //公网模式下,直接访问云端接口地址 DataUpload = new ESSupport.Transfer.PosDataUpload(ESSupport.Pos.PosConfigInit.ServerpartCode, ESSupport.Pos.PosConfigInit.ShopCode, ESSupport.Pos.PosConfigInit.MachineCode, Common.PosControl.PosDataDownloadURL); } else { //内网模式下,通过本地服务器Nginx代理访问云端接口 DataUpload = new ESSupport.Transfer.PosDataUpload(ESSupport.Pos.PosConfigInit.ServerpartCode, ESSupport.Pos.PosConfigInit.ShopCode, ESSupport.Pos.PosConfigInit.MachineCode, $"http://{ESSupport.Pos.PosConfigInit.ServerIP}:7198/webApi_publish"); } //绑定消息回调监听事件 //DataUpload.NotifyEvent += DataUpload_NotifyEvent; } DataUpload.DataUpload(ESSupport.Transfer.TransferDataDictionary.TableDataType.RecordStateFeedBack, DateTime.Now); }) { IsBackground = true }; ESSupport.Common.PosControl.StateFeedbackThread.Start(); } } catch { } #endregion } #endregion #region 数据上传执行结果上报 /// /// 数据上传执行结果上报 /// /// 操作时间 /// 消息内容 /// 消息类型 private void DataUpload_NotifyEvent(object sender, ESSupport.Model.Common.NotifyEventArgs e) { try { //记录到本地文本日志 ESSupport.Lib.LogHelper.WriteServiceLog(e.NotifyLoggerMessage); ESSupport.Transfer.PosDataUpload posDataUpload = (ESSupport.Transfer.PosDataUpload)sender; //只有通知上传状态为True的消息才上传到云端 if (e.NotifyUploadState) { #region 调用云端【/WebSocket/RecordLogToOracle】接口上报Socket消息通知执行结果到云端 string str_HttpURL = $"{posDataUpload.ServerApiUrl}/WebSocket/RecordLogToOracle"; string str_PostData = $"ServerpartCode={posDataUpload.ServerpartCode}&ShopCode={posDataUpload.ShopCode}&" + $"MachineCode={posDataUpload.MachineCode}&LogCentent={e.NotifyLoggerMessage}&" + $"TableName={(int)e.NotifyLoggerType}&OperateTime={e.NotifyLoggerTime.ToString("yyyyMMddHHmmss")}"; ESSupport.Lib.HttpHelper.HttpPost(str_PostData, $"{str_HttpURL}?{str_PostData}"); #endregion } } catch (Exception ex) { //记录上传错误日志到本地文本日志 ESSupport.Lib.LogHelper.WriteServiceLog($"{e.NotifyLoggerType.ToString()}执行结果上报失败。原因:{ex.Message}"); } } #endregion #region 事件 -> 指令执行结果上报 /// /// 指令执行结果上报 /// /// 操作时间 /// 消息内容 /// 消息类型 private void PosSocket_NotifyEvent(object sender, ESSupport.Model.Common.NotifyEventArgs e) { try { //记录到本地文本日志 ESSupport.Lib.LogHelper.WriteServiceLog(e.NotifyLoggerMessage); ESSupport.Transfer.PosSuperSocketClient socketClient = (ESSupport.Transfer.PosSuperSocketClient)sender; //只有通知上传状态为True的消息才上传到云端 if (e.NotifyUploadState) { #region 调用云端【/WebSocket/RecordLogToOracle】接口上报Socket消息通知执行结果到云端 string str_HttpURL = $"{socketClient.DataServerApiUrl}/WebSocket/RecordLogToOracle"; string str_PostData = $"ServerpartCode={socketClient.ServerpartCode}&ShopCode={socketClient.ShopCode}&" + $"MachineCode={socketClient.MachineCode}&LogCentent={e.NotifyLoggerMessage}&" + $"TableName={(int)e.NotifyLoggerType}&OperateTime={e.NotifyLoggerTime.ToString("yyyyMMddHHmmss")}"; ESSupport.Lib.HttpHelper.HttpPost(str_PostData, $"{str_HttpURL}?{str_PostData}"); #endregion } } catch (Exception ex) { //记录上传错误日志到本地文本日志 ESSupport.Lib.LogHelper.WriteServiceLog($"{e.NotifyLoggerType.ToString()}执行结果上报失败。原因:{ex.Message}"); } } #endregion #region 方法 -> 旧模式数据传输监听(待删除) ///// ///// 数据下载监听事件 ///// ///// ///// //private void DownStringEvent(GetHttpData.SDK.ThreadPool.DownResultInfo downResultInfo, string resultString) //{ //} ///// ///// 数据上传监听事件 ///// ///// ///// //private void UploadStringEvent(GetHttpData.SDK.ThreadPool.UploadResultInfo uploadResultInfo, string resultString) //{ //} #endregion #region 计时器方法 -> 主定时器运行事件 /// /// 主定时器运行事件 /// /// /// private void WorkerTimer_Tick(object sender, EventArgs e) { //每分钟检查一次Socket客户端是否正常连接 if (Common.PosControl.PosSocket != null && !Common.PosControl.PosSocket.ClientRuning) { Common.PosControl.PosSocket.Start(); } #region 初始化数据传输接口地址,每隔10分钟重新设置检查一次,避免接口连接失败导致心跳无法上报 ////初始化数据传输接口地址,每隔10分钟重新设置检查一次,避免接口连接失败导致心跳无法上报 //if (string.IsNullOrWhiteSpace(WebServiceUrl) || // (DateTime.Now - ESSupport.Common.PosControl.DataCollectionTime).Minutes % 10 == 0) //{ // //心跳数据上报主接口地址,此地址正常访问时,心跳通过这个地址上报 // string _ServiceUrl = $"http://{ESSupport.Pos.PosConfigInit.ServerIP}:" + // $"{ESSupport.Pos.PosConfigInit.ConfigurationValues("DataServicePort", "7080")}/DataServices/Service.asmx"; // //检查主数据传输接口是否可以正常访问,不能正常访问的话,使用备用接口 // if (ESSupport.Lib.HttpHelper.UrlIsExist(_ServiceUrl)) // { // WebServiceUrl = _ServiceUrl; // } // else // { // //心跳数据上报备用接口地址,当主接口地址无法访问时,心跳通过这个地址上报 // WebServiceUrl = $"http://{ESSupport.Pos.PosConfigInit.ServerIP}:" + // $"{ESSupport.Pos.PosConfigInit.ConfigurationValues("service_port", "7080")}/Service.asmx"; // } //} #endregion #region 初始化移动支付交易检测接口地址 //初始化移动支付交易检测接口地址 if (string.IsNullOrWhiteSpace(PayServiceUrl)) { PayServiceUrl = $"http://{ESSupport.Pos.PosConfigInit.ServerIP}:" + $"{ESSupport.Pos.PosConfigInit.ConfigurationValues("service_port", "7080")}/Service.asmx"; } #endregion #region 收银机指令执行线程,执行后台下发的收银机命令 ////收银机指令执行线程 //if (ESSupport.Common.PosControl.CommodityMachineThread == null || // !ESSupport.Common.PosControl.CommodityMachineThread.IsAlive) //{ // ESSupport.Common.PosControl.CommodityMachineThread = new Thread(() => // { // //ESSupport.Pos.ThreadHelper.CommodityMachine(WebServiceUrl); // ESSupport.Pos.ThreadHelper.InstructionExecute(WebServiceUrl); // ESSupport.Pos.ThreadHelper.License(); // }) // { // IsBackground = true // }; // ESSupport.Common.PosControl.CommodityMachineThread.Start(); //} #endregion #region 系统运行日志记录线程,每隔5分钟保存一条运行记录到本地数据库 //启动运行日志记录线程 if (ESSupport.Common.PosControl.RuningLogThread == null || !ESSupport.Common.PosControl.RuningLogThread.IsAlive) { ESSupport.Common.PosControl.RuningLogThread = new Thread(() => { if (ESSupport.Pos.ThreadHelper.RuningLog(ESSupport.Common.PosControl.RuningTime)) { ESSupport.Common.PosControl.RuningTime = DateTime.Now; } }) { IsBackground = true }; ESSupport.Common.PosControl.RuningLogThread.Start(); } #endregion #region 记录十分钟营收统计首次启动时间,后续每隔10分钟统计一次交易数据并上传到后台 ////记录十分钟营收统计首次启动时间,后续每隔10分钟统计一次交易数据并上传到后台 //if (ESSupport.Common.PosControl.DataCollectionTime == DateTime.MinValue) //{ // ESSupport.Common.PosControl.DataCollectionTime = DateTime.Now.AddMilliseconds(-Environment.TickCount); //} #endregion #region 定时启动实时交易统计线程 ////定时启动实时交易统计线程 //if (!ESSupport.Common.PosControl.DataCollectionRun && // (DateTime.Now - ESSupport.Common.PosControl.DataCollectionTime).Minutes % 10 == 0) //{ // if (ESSupport.Common.PosControl.DataCollectionThread == null || // !ESSupport.Common.PosControl.DataCollectionThread.IsAlive) // { // ESSupport.Common.PosControl.DataCollectionThread = new Thread(() => // { // ESSupport.Common.PosControl.DataCollectionRun = true; // ESSupport.Pos.ThreadHelper.DataCollectionUpload(WebServiceUrl); // ESSupport.Pos.ThreadHelper.NewSystemConfig(); // }) // { // IsBackground = true // }; // ESSupport.Common.PosControl.DataCollectionThread.Start(); // //Common.PosControl.DataCollectionTime = DateTime.Now.AddMinutes(10); // } //} #endregion #region 控制实时交易统计线程是否需要启动(每隔10分钟启动一次) //if (ESSupport.Common.PosControl.DataCollectionRun && // (DateTime.Now - ESSupport.Common.PosControl.DataCollectionTime).Minutes % 10 != 0) //{ // ESSupport.Common.PosControl.DataCollectionRun = false; //} #endregion #region 每隔10分钟启动一次移动支付交易验证线程 //定时启动移动支付交易校验线程 if (DateTime.Now > ESSupport.Common.PosControl.MobilePayCheckTime) { if (ESSupport.Common.PosControl.MobilePayCheckThread == null || !ESSupport.Common.PosControl.MobilePayCheckThread.IsAlive) { ESSupport.Common.PosControl.MobilePayCheckThread = new Thread(() => { //对待确认的移动支付交易进行交易结果查询 ESSupport.Pos.ThreadHelper.MobilePayQueryCheck(PayServiceUrl, DateTime.Today.AddMinutes(-10)); }) { IsBackground = true }; ESSupport.Common.PosControl.MobilePayCheckThread.Start(); //每隔10分钟执行一次移动支付交易验证 ESSupport.Common.PosControl.MobilePayCheckTime = DateTime.Now.AddMinutes(10); } } #endregion #region 每分钟启动一次状态反馈(心跳)上报线程 //定时启动状态反馈线程 if (DateTime.Now > ESSupport.Common.PosControl.StateFeedBackTime) { if (ESSupport.Common.PosControl.StateFeedbackThread == null || !ESSupport.Common.PosControl.StateFeedbackThread.IsAlive) { ESSupport.Common.PosControl.StateFeedbackThread = new Thread(() => { if (DataUpload == null) { //判断是否可以直接访问公网 if (Common.PosControl.IsCloudURL) { //公网模式下,直接访问云端接口地址 DataUpload = new ESSupport.Transfer.PosDataUpload(ESSupport.Pos.PosConfigInit.ServerpartCode, ESSupport.Pos.PosConfigInit.ShopCode, ESSupport.Pos.PosConfigInit.MachineCode, Common.PosControl.PosDataDownloadURL); } else { //内网模式下,通过本地服务器Nginx代理访问云端接口 DataUpload = new ESSupport.Transfer.PosDataUpload(ESSupport.Pos.PosConfigInit.ServerpartCode, ESSupport.Pos.PosConfigInit.ShopCode, ESSupport.Pos.PosConfigInit.MachineCode, $"http://{ESSupport.Pos.PosConfigInit.ServerIP}:7198/webApi_publish"); } //绑定消息回调监听事件 DataUpload.NotifyEvent += DataUpload_NotifyEvent; } DataUpload.DataUpload(ESSupport.Transfer.TransferDataDictionary.TableDataType.RecordStateFeedBack, DateTime.Now); #region 老接口上传心跳数据(已停用,待删除) ////获取服务区编码 //string _ServerPartCode = ESSupport.Pos.PosConfigInit.ConfigurationValues("serverpartcode", string.Empty); ////获取门店编码 //string _ShopCode = ESSupport.Pos.PosConfigInit.ConfigurationValues("shopcode", string.Empty); ////获取收银机号 //string _MachineCode = ESSupport.Pos.PosConfigInit.ConfigurationValues("machinecode", string.Empty); ////获取新系统标识 //bool _NewSystem = ESSupport.Pos.PosConfigInit.ConfigurationValues("NewSystem", "0") == "1"; ////初始化交易信息获取类(用于读取统一的实时交易数据) //ESSupport.Pos.DataStatistics _DataStatistics = new ESSupport.Pos.DataStatistics //{ // ServerPartCode = _ServerPartCode, // ShopCode = _ShopCode, // MachineCode = _MachineCode //}; ////心跳数据上传至区服接口 //ESSupport.Pos.ThreadHelper.StateFeedbackUpload(WebServiceUrl, _DataStatistics); ////心跳数据上传至云端接口 //ESSupport.Pos.ThreadHelper.StateFeedbackUploadByCloud(PayServiceUrl, "http://user.eshangtech.com:7089/Service.asmx", _DataStatistics); ////日结数据上传至云端接口 //ESSupport.Pos.ThreadHelper.EndAccountUploadByCloud(PayServiceUrl, "http://user.eshangtech.com:7089/Service.asmx", _DataStatistics); #endregion }) { IsBackground = true }; ESSupport.Common.PosControl.StateFeedbackThread.Start(); ESSupport.Common.PosControl.StateFeedBackTime = DateTime.Now.AddMinutes(1); } } #endregion #region 每隔30分钟运行一次收银机基础信息更新检测线程 //每隔30分钟运行一次收银机基础信息更新检测线程 if (DateTime.Now > ESSupport.Common.PosControl.BaseInfoFeedbackTime) { if (ESSupport.Common.PosControl.BaseInfoFeedbackThread == null || !ESSupport.Common.PosControl.BaseInfoFeedbackThread.IsAlive) { ESSupport.Common.PosControl.BaseInfoFeedbackThread = new Thread(() => { ////基础数据版本接口上报 //ESSupport.Pos.ThreadHelper.BaseInfoFeedbackUpload(WebServiceUrl); ////收银机器信息接口上报 //ESSupport.Pos.ThreadHelper.MachineInfoFeedbackUpload(WebServiceUrl); try { //读取后台配置的更新服务地址 string _UpdateIP = ESSupport.Pos.PosConfigInit.ConfigurationValues("UpdateIP", string.Empty); if (string.IsNullOrWhiteSpace(_UpdateIP)) { //更新服务地址未配置时,默认同收银服务器地址 _UpdateIP = ESSupport.Pos.PosConfigInit.ConfigurationValues("server_ip", string.Empty); } //读取后台配置的更新服务端口,默认:11000 string _UpdatePort = ESSupport.Pos.PosConfigInit.ConfigurationValues("UpdatePort", "11000"); if (!string.IsNullOrWhiteSpace(_UpdateIP) && !string.IsNullOrWhiteSpace(_UpdatePort)) { //检测更新站点是否存在,存在则更新配置文件 if (ESSupport.Lib.HttpHelper.UrlIsExist($"http://{_UpdateIP}:{_UpdatePort}/Update.asmx")) { //修改更新服务IP地址 if (ESSupport.Lib.ConfigHelper.GetAppConfig(AppDomain.CurrentDomain.BaseDirectory + "\\update.xml", "IP") != _UpdateIP) { ESSupport.Lib.ConfigHelper.UpdateAppConfig(AppDomain.CurrentDomain.BaseDirectory + "\\update.xml", "IP", _UpdateIP); } //修改更新服务端口 if (ESSupport.Lib.ConfigHelper.GetAppConfig(AppDomain.CurrentDomain.BaseDirectory + "\\update.xml", "Port") != _UpdatePort) { ESSupport.Lib.ConfigHelper.UpdateAppConfig(AppDomain.CurrentDomain.BaseDirectory + "\\update.xml", "Port", _UpdatePort); } } } //更新配置文件不存在端口号配置项时,写入默认端口 if (string.IsNullOrWhiteSpace(ESSupport.Lib.ConfigHelper.GetAppConfig(AppDomain.CurrentDomain.BaseDirectory + "\\update.xml", "Port"))) { ESSupport.Lib.ConfigHelper.UpdateAppConfig(AppDomain.CurrentDomain.BaseDirectory + "\\update.xml", "Port", "11000"); } } catch { } }) { IsBackground = true }; ESSupport.Common.PosControl.BaseInfoFeedbackThread.Start(); ESSupport.Common.PosControl.BaseInfoFeedbackTime = DateTime.Now.AddMinutes(30); } } #endregion } #endregion #region 计时器方法 -> PB系统智能稽核平台校验记录获取线程定时器运行事件 /// /// 平台稽核记录校验记录获取线程定时器运行事件 /// /// /// private void CheckAuditTimer_Tick(object sender, EventArgs e) { //初始化移动支付交易检测接口地址 if (string.IsNullOrWhiteSpace(PayServiceUrl)) { PayServiceUrl = $"http://{ESSupport.Pos.PosConfigInit.ConfigurationValues("server_ip", string.Empty)}:" + $"{ESSupport.Pos.PosConfigInit.ConfigurationValues("service_port", "7080")}/Service.asmx"; } //启动平台稽核反馈结果获取线程 if (ESSupport.Common.PosControl.CheckAuditToPowerBuilderThread == null || !ESSupport.Common.PosControl.CheckAuditToPowerBuilderThread.IsAlive) { ESSupport.Common.PosControl.CheckAuditToPowerBuilderThread = new Thread(() => { ESSupport.Pos.ThreadHelper.CheckAuditToPowerBuilder(PayServiceUrl); }) { IsBackground = true }; ESSupport.Common.PosControl.CheckAuditToPowerBuilderThread.Start(); } } #endregion } }