You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

807 lines
32 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using POSV.Entity;
using POSV.HttpApi;
using POSV.ShoppingCart;
using POSV.Utils;
using System.Data.SQLite;
using POSV.Cef.Client.ClientComm;
using POS.Language.Language;
using POSV.OtherWaiMai;
using POSV.QiMai;
using POSV.Common.Util;
using NLog.RestTarget;
using POSV.Common.Wlan;
using System.Net.NetworkInformation;
using POSV.Payment.Abmcs;
using System.Security.Cryptography;
using POSV.Dianping;
using POSV.Card;
namespace POSV
{
static class Program
{
private const int SW_HIDE = 0;
private const int SW_NORMAL = 1;
private const int SW_MAXIMIZE = 3;
private const int SW_SHOWNOACTIVATE = 4;
private const int SW_SHOW = 5;
private const int SW_MINIMIZE = 6;
private const int SW_RESTORE = 9;
private const int SW_SHOWDEFAULT = 10;
private static ExceptionForm exception = null;
/// 该函数设置由不同线程产生的窗口的显示状态
/// </summary>
/// <param name="hWnd">窗口句柄</param>
/// <param name="cmdShow">指定窗口如何显示。查看允许值列表请查阅ShowWlndow函数的说明部分</param>
/// <returns>如果函数原来可见,返回值为非零;如果函数原来被隐藏,返回值为零</returns>
[DllImport("User32.dll")]
private static extern bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
/// <summary>
/// 该函数将创建指定窗口的线程设置到前台,并且激活该窗口。键盘输入转向该窗口,并为用户改各种可视的记号。
/// 系统给创建前台窗口的线程分配的权限稍高于其他线程。
/// </summary>
/// <param name="hWnd">将被激活并被调入前台的窗口句柄</param>
/// <returns>如果窗口设入了前台,返回值为非零;如果窗口未被设入前台,返回值为零</returns>
[DllImport("User32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public static Mutex AppMutex;
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
//全局异常捕捉
Application.ThreadException += ApplicationThreadException; //UI线程异常
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException; //多线程异常
//NetworkInterface[] allInterfaces = NetworkInterface.GetAllNetworkInterfaces();
//NetworkInterface firstInterface = allInterfaces[0];
//foreach (var i in allInterfaces)
//{
// bool interfaceSupportsIPv6 = InterfaceHasIpv6Enabled(i);
//}
bool createdNew = true;
AppMutex = new System.Threading.Mutex(true, Application.ProductName, out createdNew);
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashScreen.ShowSplashScreen();
//初始化NLog
NLogUtils.InitLogger();
SplashScreen.SetStatus("加载运行日志...");
Global.Instance.Restart = false;
AutoUpdateNew();
var _type = Global.Instance.GlobalConfigStringValue(ConfigConstant.LAYOUT_LANGUAGETYPE, "中文");
LangProxy.SetLangTypeVal(_type);
//检测是否WiFi网路连接
Global.Instance.IsWiFi = WiFiApi.Connected();
SplashScreen.SetStatus(LangProxy.ToLang("检测本机网络连接..."));
var openApiTask = Task.Factory.StartNew<bool>(() => { return HttpClientUtils.IsAvailable(); });
Task.WaitAll(openApiTask);
//网络可用:a)网卡工作;b)可以联网;c)开放平台正常
bool isConnected = openApiTask.Result;
//网络连接压入全局参数
Global.Instance.Online = isConnected;
SplashScreen.SetStatus(string.Format(LangProxy.ToLang("本机网络连接{0}..."), isConnected ? LangProxy.ToLang("正常") : LangProxy.ToLang("不正常")));
SplashScreen.SetStatus(LangProxy.ToLang("检测服务中心连接..."));
try
{
Global.Instance.MessageCenterOnline = MessageCenterUtils.Instance.IsAvailable();
}
catch { }
SplashScreen.SetStatus(LangProxy.ToLang("设备自检中..."));
var finger = DeviceUtils.Instance;
bool isGo = true;
SplashScreen.SetStatus(LangProxy.ToLang("检测数据库版本..."));
bool isException = AutoUpdateDatabase();
if (isException)
{
isGo = false;
SplashScreen.SetStatus(LangProxy.ToLang("数据更新失败,即将退出..."));
}
else
{
SplashScreen.SetStatus(LangProxy.ToLang("数据更新成功..."));
}
if (isGo)
{
SplashScreen.SetStatus(LangProxy.ToLang("启动定时任务..."));
JobUtils.Startup();
SplashScreen.CloseForm();
//初始化来电猫全局参数,应用关闭后主动关闭端口
Global.Instance.Cats = new List<SerialPort>();
//登陆窗口,并最大化
FastLoginForm loginForm = new FastLoginForm();
ShowWindowAsync(loginForm.Handle, SW_MAXIMIZE);//显示
SetForegroundWindow(loginForm.Handle);//当到最前端
if (DialogResult.OK == loginForm.ShowDialog())
{
//zhangy 2020-03-05 Add 登陆成功后,更新数据库默认参数
//AutoUpdateParamVersion();
//联机状态
if (Global.Instance.Online)
{
var download = new FastDownloading();
if (DialogResult.OK == download.ShowDialog())
{
var wsse = WSSE.Instance;
StartTheme();
}
}
else
{
var wsse = WSSE.Instance;
StartTheme();
}
}
//关闭来电猫端口
Global.Instance.Cats.ForEach(x =>
{
if (x != null && x.IsOpen)
{
x.Close();
NLog.LogManager.GetCurrentClassLogger().Info("关闭来电参数<{0},{1}>", x.PortName, x.BaudRate);
}
});
//关闭云端推送连接
Proxy.Mqtt.MqttClientUtils.Instance.Stop();
NLog.LogManager.GetCurrentClassLogger().Info("关闭云端推送连接");
//关闭消息中心连接
MqttUtils.Instance.Stop();
NLog.LogManager.GetCurrentClassLogger().Info("关闭消息中心连接");
//关闭计划任务
JobUtils.Stop();
NLog.LogManager.GetCurrentClassLogger().Info("关闭计划任务");
if (Global.Instance.Restart)
{
AppMutex.ReleaseMutex();
AppMutex.Dispose();
AppMutex = null;
Application.Exit();
System.Diagnostics.Process.Start(System.Reflection.Assembly.GetExecutingAssembly().Location);
System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
currentProcess.Kill();
}
else
{
System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
currentProcess.Kill();
}
}
else
{
Thread.Sleep(3000);
SplashScreen.CloseForm();
Application.ExitThread();
}
}
else
{
Process instance = Instance();
ShowWindowAsync(instance.MainWindowHandle, SW_MAXIMIZE);//显示
SetForegroundWindow(instance.MainWindowHandle);//当到最前端
}
}
/// <summary>
/// 启动对应主题布局界面
/// </summary>
private static void StartTheme()
{
Global.Instance.ReloadConfig();
try
{
//版本选择
ThemeEnum enableTheme = ThemeEnum.;
var layoutTheme = Global.Instance.GlobalConfigStringValue(ConfigConstant.LAYOUT_THEME, ThemeEnum..ToString());
Enum.TryParse<ThemeEnum>(layoutTheme, out enableTheme);
switch (enableTheme)
{
case ThemeEnum.:
{
Application.Run(new TeaTemplate());
}
break;
case ThemeEnum.:
{
DownloadCefDependentFile DownloadCef = new DownloadCefDependentFile();
if (!DownloadCef.Needtodownload())
{
DownloadCef.ShowDialog();
if (!DownloadCef.IsAccomplish)
{
//zhangy 2020-03-01 Add 新食堂版本依赖CEF环境如果环境异常重置到快餐模式以便于下次正常启动
using (var db = Global.Instance.OpenDataBase)
{
var config = db.Single<Config>(" where [group] = @0 and keys = @1", ConfigConstant.LAYOUT_GROUP, ConfigConstant.LAYOUT_THEME);
if (config != null)
{
config.Values = ThemeEnum..ToString();
using (var trans = db.GetTransaction())
{
db.Save(config);
trans.Complete();
}
}
}
MessageBox.Show("运行环境出现问题,已经恢复到快餐模式,请与管理员联系!");
Application.ExitThread();
}
}
Application.Run(new POSV.Cef.FastTemplate.FastTemplate());
}
break;
case ThemeEnum.:
{
Application.Run(new FastTemplate());//zhangy 2020-002-18 Add 已经在参数配置中屏蔽
}
break;
case ThemeEnum.:
{
Application.Run(new WeightTemplate());
}
break;
case ThemeEnum.:
default:
{
Application.Run(new DefaultTemplate());
}
break;
}
}
catch (Exception ex)
{
if (exception == null || exception.IsDisposed)
{
exception = new ExceptionForm();
}
exception.SetMessage = ex.Message;
exception.SetErrorMessage = ex.StackTrace;
exception.ShowDialog();
}
}
/// <summary>
/// 只运行一个实例
/// </summary>
/// <returns></returns>
private static Process Instance()
{
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(current.ProcessName);
foreach (Process process in processes)
{
//如果实例已经存在则忽略当前进程
if (process.Id != current.Id)
{
//保证要打开的进程同已经存在的进程来自同一文件路径
if (Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName)
{
//返回已经存在的进程
return process;
}
}
}
return null;
}
/// <summary>
/// 主程序更新 AutoUpdater.exe
/// </summary>
static void AutoUpdateNew()
{
string auto_update = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"AutoUpdater.exe");
string auto_update_new = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Constant.AUTO_UPDATER_NEW);
string auto_update_backup = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\backup\AutoUpdater.exe");
if (File.Exists(auto_update_new))
{
if (File.Exists(auto_update))
{
if (File.Exists(auto_update_backup))
{
File.Delete(auto_update_backup);
}
File.Move(auto_update, auto_update_backup);
}
FileInfo fi = new FileInfo(auto_update_new);
fi.MoveTo(auto_update);
}
}
//static void AutoUpdateParamVersion()
//{
// try
// {
// ApplicationVersion entity = null;
// //判断数据库中记录的版本
// using (var db = Global.Instance.OpenDataBase)
// {
// entity = db.Query<ApplicationVersion>().OrderByDescending(x => x.ModifyDate).FirstOrDefault();
// }
// if(entity != null)
// {
// //数据库版本大于参数版本
// if (string.Compare(entity.DbVersion,entity.ParamVersion) > 0)
// {
// var _name = string.Format("data.db_{0}.txt", entity.DbVersion);
// //获得正在运行类所在的名称空间
// Type type = MethodBase.GetCurrentMethod().DeclaringType;
// string _namespace = type.Namespace;
// //获得当前运行的Assembly
// Assembly _assembly = Assembly.GetExecutingAssembly();
// //根据名称空间和文件名生成资源名称
// string resourceName = _namespace + "." + _name;
// var list = new ConcurrentQueue<string>();
// using (var stream = _assembly.GetManifestResourceStream(resourceName))
// {
// if (stream != null)
// {
// NLog.LogManager.GetCurrentClassLogger().Info("默认参数更新<{0}>", _name);
// string json = string.Empty;
// using (StreamReader reader = new StreamReader(stream))
// {
// json = reader.ReadToEnd();
// }
// if (!string.IsNullOrEmpty(json))
// {
// var migrate = DynamicJson.Parse(json);
// if (migrate != null)
// {
// //处理租户更新,影响具体租户全部门店
// if (migrate.IsDefined("tenant") && Global.Instance.Authc != null)
// {
// var tenant = migrate["tenant"];
// //zhangy 2020-03-05 JSON解析有问题不能以数字开头
// var tenantNo = "tenant_" + Global.Instance.Authc.TenantId;
// if (tenant.IsDefined(tenantNo))
// {
// var arrayJson = tenant[tenantNo];
// foreach (var item in arrayJson)
// {
// list.Enqueue(item);
// }
// }
// }
// //处理租户门店更新,影响具体租户具体门店
// if (migrate.IsDefined("store") && Global.Instance.Authc != null)
// {
// var storeNo = "store" + Global.Instance.Authc.TenantId + "_" + Global.Instance.Authc.StoreNo;
// var tenant = migrate["store"];
// if (tenant.IsDefined(storeNo))
// {
// var arrayJson = tenant[storeNo];
// foreach (var item in arrayJson)
// {
// list.Enqueue(item);
// }
// }
// }
// }
// }
// }
// }
// if(list.Count > 0)
// {
// SqliteUtils.ExecuteTransaction(list);
// //刷新参数
// Global.Instance.ReloadConfig();
// NLog.LogManager.GetCurrentClassLogger().Info("门店默认参数更新成功");
// }
// entity.ParamVersion = entity.DbVersion;
// using (var db = Global.Instance.OpenDataBase)
// {
// using (var trans = db.GetTransaction())
// {
// db.Save(entity);
// trans.Complete();
// }
// }
// }
// }
// else
// {
// NLog.LogManager.GetCurrentClassLogger().Info("门店默认参数更新失败:原因:<pos_version>表没有数据");
// }
// }
// catch (Exception ex)
// {
// NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级异常");
// }
//}
static bool AutoUpdateDatabase()
{
bool isException = false;
//当前程序嵌入的数据库版本
int _version = 1;
//数据库的版本
int _dbVersion = 1;
ApplicationVersion entity = null;
try
{
var _array = Application.ProductVersion.Split('.');
int major = 0;
int.TryParse(_array[0], out major);
int minor = 0;
int.TryParse(_array[1], out minor);
int build = 2;
int.TryParse(_array[2], out build);
//当前程序嵌入的数据库版本
_version = major * 100 + minor * 10 + build;
//应用的版本
var _appVersion = Application.ProductVersion;
//判断数据库中记录的版本
using (var db = Global.Instance.OpenDataBase)
{
entity = db.Query<ApplicationVersion>().OrderByDescending(x => x.ModifyDate).FirstOrDefault();
}
if (entity == null)
{
entity = new ApplicationVersion();
entity.Id = IdWorkerUtils.Instance.NextId();
entity.AppVersion = _appVersion;
entity.DbVersion = _dbVersion.ToString();
}
int.TryParse(entity.DbVersion, out _dbVersion);
//升级前备份数据文件
string sourceFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\posv2.s3db");
string destFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format(@"data\backup\posv2.s3db", ""));
//数据库需要升级
if (_version > _dbVersion)
{
SplashScreen.SetStatus(string.Format("备份历史数据<{0}>...", _dbVersion));
System.IO.File.Copy(sourceFileName, destFileName, true);
for (int i = _dbVersion + 1; i <= _version; i++)
{
var _name = string.Format("data.db_{0}.txt", i);
//获得正在运行类所在的名称空间
Type type = MethodBase.GetCurrentMethod().DeclaringType;
string _namespace = type.Namespace;
//获得当前运行的Assembly
Assembly _assembly = Assembly.GetExecutingAssembly();
//根据名称空间和文件名生成资源名称
string resourceName = _namespace + "." + _name;
Tuple<bool, ConcurrentQueue<string>> migrate = null;
using (Stream stream = _assembly.GetManifestResourceStream(resourceName))
{
if (stream == null)
{
continue;
}
NLog.LogManager.GetCurrentClassLogger().Info("发布数据库更新<{0}>", _name);
string json = string.Empty;
using (StreamReader reader = new StreamReader(stream))
{
json = reader.ReadToEnd();
}
if (!string.IsNullOrEmpty(json))
{
migrate = ParseDbMigrate(json);
}
}
if (migrate != null && migrate.Item1)
{
var sqls = migrate.Item2;
SplashScreen.SetStatus(string.Format("更新数据,请稍候..."));
foreach (var sql in migrate.Item2)
{
try
{
SqliteUtils.Execute(sql);
}
catch (Exception ex)
{
if (ex.Message.Contains("duplicate column name"))
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, "列名升级失败,已存在:"+ sql);
}
else
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级异常");
}
}
}
}
}
}
}
catch (Exception ex)
{
isException = true;
NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级异常");
}
finally
{
if (!isException)
{
entity.AppVersion = Application.ProductVersion;
entity.DbVersion = _version.ToString();
using (var db = Global.Instance.OpenDataBase)
{
using (var trans = db.GetTransaction())
{
db.Save(entity);
trans.Complete();
}
}
}
//刷新Http请求的AppKey避免调用错误
OpenApiUtils.Instance.Refresh();
}
return isException;
}
static Tuple<bool, ConcurrentQueue<string>> ParseDbMigrate(string json)
{
Tuple<bool, ConcurrentQueue<string>> result = null;
try
{
NLog.LogManager.GetCurrentClassLogger().Info("解析数据库更新脚本......");
var list = new ConcurrentQueue<string>();
var migrate = DynamicJson.Parse(json);
if (migrate != null)
{
//处理全局更新,影响全部客户
if (migrate.IsDefined("global"))
{
var global = migrate["global"];
if (global.IsDefined("all"))
{
var arrayJson = global["all"];
foreach (var item in arrayJson)
{
list.Enqueue(item);
}
NLog.LogManager.GetCurrentClassLogger().Info("处理数据库全局更新......");
}
}
////处理租户更新,影响具体租户全部门店
//if (migrate.IsDefined("tenant") && Global.Instance.Authc != null)
//{
// var tenant = migrate["tenant"];
// var tenantNo = Global.Instance.Authc.TenantId;
// if (tenant.IsDefined(tenantNo))
// {
// var arrayJson = tenant[tenantNo];
// foreach (var item in arrayJson)
// {
// list.Enqueue(item);
// }
// NLog.LogManager.GetCurrentClassLogger().Info("处理数据库租户更新,受影响的记录:<{0}>", arrayJson != null ? arrayJson.Length : 0);
// }
//}
////处理租户门店更新,影响具体租户具体门店
//if (migrate.IsDefined("store") && Global.Instance.Authc != null)
//{
// var storeNo = Global.Instance.Authc.TenantId+"_"+ Global.Instance.Authc.StoreNo;
// var tenant = migrate["store"];
// if (tenant.IsDefined(storeNo))
// {
// var arrayJson = tenant[storeNo];
// foreach (var item in arrayJson)
// {
// list.Enqueue(item);
// }
// NLog.LogManager.GetCurrentClassLogger().Info("处理数据库租户更新,受影响的记录:<{0}>", arrayJson != null ? arrayJson.Length : 0);
// }
//}
}
result = new Tuple<bool, ConcurrentQueue<string>>(true, list);
}
catch (Exception ex)
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, "数据库升级文件格式错误");
result = new Tuple<bool, ConcurrentQueue<string>>(false, null);
}
return result;
}
/// <summary>
/// 修改数据库密码
/// </summary>
public static void ChangePwd(string pwd)
{
SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder();
builder.DataSource = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"data\posv2.s3db");
builder.Password = "passwd";
builder.Version = 3;
builder.CacheSize = 10000;
builder.DefaultTimeout = 5000;
builder.PageSize = 4096;
builder.FailIfMissing = true;
builder.Pooling = true;
builder.DefaultIsolationLevel = System.Data.IsolationLevel.ReadCommitted;
builder.SyncMode = SynchronizationModes.Off;
builder.JournalMode = SQLiteJournalModeEnum.Truncate;
builder.UseUTF16Encoding = false;
using (SQLiteConnection connection = new SQLiteConnection(builder.ToString()))
{
try
{
connection.Open();
connection.ChangePassword(pwd);
}
catch (SQLiteException ex)
{
throw new Exception(ex.Message);
}
finally
{
connection.Close();
}
}
}
//UI线程异常
static void ApplicationThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
{
NLog.LogManager.GetCurrentClassLogger().Error(e.Exception, "ApplicationThreadException");
Global.Instance.BugReport(e.Exception);
}
//多线程异常
static void CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
NLog.LogManager.GetCurrentClassLogger().Error(e.ExceptionObject as Exception, "ApplicationThreadException");
Global.Instance.BugReport(e.ExceptionObject as Exception);
}
public static bool InterfaceHasIpv6Enabled(NetworkInterface @interface)
{
try
{
var properties = @interface.GetIPProperties().GetIPv6Properties();
return properties.Index > -999;
}
catch (NetworkInformationException)
{
return false;
}
catch (Exception ex)
{
throw ex;
}
}
}
}