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.

480 lines
18 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 JwKdsV.Core;
using JwKdsV.Core.Utils;
using JwKdsV.Entity.Common;
using JwKdsV.Entity.Tenant;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace JwKdsV
{
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;
/// 该函数设置由不同线程产生的窗口的显示状态
/// </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);
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
//全局异常捕捉
Application.ThreadException += ApplicationThreadException; //UI线程异常
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomainUnhandledException; //多线程异常
bool createdNew = true;
using (Mutex mutex = new Mutex(true, Application.ProductName, out createdNew))
{
if (createdNew)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SplashScreen.ShowSplashScreen();
//初始化NLog
NLogUtils.InitLogger();
//数据库去密码
try
{
ChangePwd("");
NLog.LogManager.GetCurrentClassLogger().Info("sqliteNoP success");
}
catch (Exception)
{
NLog.LogManager.GetCurrentClassLogger().Info("sqliteNoP error, already success!");
}
AutoUpdateNew();
SplashScreen.SetStatus("检测本机网络连接...");
var openApiTask = Task.Factory.StartNew<bool>(() => { return HttpClientUtils.IsAvailable(ApiType.Business); });
Task.WaitAll(openApiTask);
//网络可用:a)网卡工作;b)可以联网;c)开放平台正常
bool isConnected = openApiTask.Result;
SplashScreen.SetStatus(string.Format("本机网络连接{0}...", isConnected ? "正常" : "不正常"));
//网络连接压入全局参数
Global.Instance.Online = isConnected;
bool isGo = true;
SplashScreen.SetStatus("检测数据库版本...");
bool isException = AutoUpdateDatabase();
if (isException)
{
isGo = false;
SplashScreen.SetStatus("数据更新失败,即将退出...");
}
else
{
SplashScreen.SetStatus("数据更新成功...");
//重新加载设置,避免升级脚本更新设置,导致第一次不生效
Global.Instance.ReloadConfig();
}
SplashScreen.CloseForm();
if (isGo)
{
if (!CheckSameDevice())
{
//设备不一致,需要重新进行注册
var initForm = new TenantInitForm();
initForm.BringToFront();
if (initForm.ShowDialog() != DialogResult.OK)
{
//自杀
System.Diagnostics.Process currentProcess = System.Diagnostics.Process.GetCurrentProcess();
currentProcess.Kill();
}
}
//登录
var loginForm = new LoginForm();
loginForm.BringToFront();
if (loginForm.ShowDialog() == DialogResult.OK)
{
//下载处理信息
var download = new DownloadingForm();
download.BringToFront();
if(download.ShowDialog() == DialogResult.OK)
{
//启动websocket
WebSocketEnter.Instance.Init();
//启动定时任务
JobUtils.Startup();
//展示主界面
var mainForm = new MainForm();
mainForm.BringToFront();
Application.Run(mainForm);
}
}
//关闭计划任务
JobUtils.Stop();
NLog.LogManager.GetCurrentClassLogger().Info("关闭计划任务");
}
}
else
{
Process instance = Instance();
ShowWindowAsync(instance.MainWindowHandle, SW_MAXIMIZE);//显示
SetForegroundWindow(instance.MainWindowHandle);//当到最前端
}
}
}
/// <summary>
/// 验证pos设备信息是否变更
/// </summary>
/// <returns></returns>
private static bool CheckSameDevice()
{
Authc authc = null;
using(var db = Global.Instance.OpenDataBase)
{
authc = db.FirstOrDefault<Authc>("where 1 = 1");
}
if(authc == null)
{
return false;
}
else
{
var device = new DeviceInfo();
device.Cpu = authc.CpuSerialNumber;
device.Disk = authc.DiskSerialNumber;
device.Mac = authc.MacAddress;
device.ComputerName = authc.CompterName;
return device.Equals(Global.Instance.DeviceInfo);
}
}
/// <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;
}
//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);
}
/// <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 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\kdsDatav1.s3db");
string destFileName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format(@"Data\backup\kdsDatav1.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);
using (StreamReader reader = new StreamReader(stream))
{
string json = reader.ReadToEnd();
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)
{
NLog.LogManager.GetCurrentClassLogger().Info(sql);
if (ex.Message.Contains("duplicate column name"))
{
NLog.LogManager.GetCurrentClassLogger().Error(ex, "列名升级失败,已存在");
}
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
{
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);
}
}
}
}
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;
}
public static void ChangePwd(string pwd)
{
SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder();
builder.DataSource = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Data\kdsDatav1.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();
}
}
}
}
}