using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Windows.Forms; using NLog; using POSV.Common.Transport; using POSV.Common.Util; using POSV.Entity; using POSV.HttpApi; using POSV.HttpResponse; using POSV.MessageEvent; namespace POSV.Utils { public class SaleClearUtils { private static Logger logger = NLog.LogManager.GetCurrentClassLogger(); private static object _lock = new object(); private static SaleClearUtils _instance = null; #region 20240309 subin 读取文件使用 private static string configStr = ""; [DllImport("kernel32")] private static extern int GetPrivateProfileString( string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName); public static string ContentReader(string area, string key, string def) { StringBuilder stringBuilder = new StringBuilder(1024); GetPrivateProfileString(area, key, def, stringBuilder, 1024, configStr); return stringBuilder.ToString(); } #endregion public static SaleClearUtils Instance { get { if (_instance == null) { lock (_lock) { _instance = new SaleClearUtils(); #region 20240309 subin 加载沽清配置文件 _instance.InitSaleClearConfig(); #endregion _instance.InitSaleClear(); } } return _instance; } } private ObservableDictionary _maps = null; #region 20240309 subin 加载沽清配置文件 private void InitSaleClearConfig() { logger.Info("初始化沽清配置"); configStr = Application.StartupPath + "\\cash_ip.ini"; if (File.Exists(configStr)) { try { string strOne = System.IO.Path.GetFileNameWithoutExtension(configStr); string IsSaleClearWeb = ContentReader(strOne, "is_sale", ""); if (IsSaleClearWeb == "1") { Global.isSale2Web = true; } else { Global.isSale2Web = false; } } catch (Exception ex) { logger.Error(ex, "读取cash_ip.ini错误。"); } } else { logger.Info("未能找到配置文件cash_ip.ini"); } } #endregion /// /// 加载今日沽清数据 /// private void InitSaleClear() { try { #region subin 20240212 初始化时,从服务器获取沽清数据并同步至服务中心 //if (MessageCenterUtils.Instance.IsSaleClear) //{ // ////共享沽清模式 // if (Global.isSale2Web) // { // var sdata = SaleClearApi.DownloadSaleClearList(); // if (sdata.Item1) // { // foreach (var saleClear in sdata.Item3) // { // var transport = new TransportMessage(); // transport.MessageType = MessageType.ProxySaleClear; // transport.Data = saleClear; // MqttUtils.Instance.Publish(JsonUtils.Serialize(transport)); // logger.Info("同步服务器沽清数据至到消息中心<{0} - {1} = {2}>", saleClear.TotalQuantity, saleClear.SaleQuantity, saleClear.Quantity); // } // } // } //} #endregion var data = this.GetAvailableSaleClearList(); List rows = data.Item3; this._maps = new ObservableDictionary(); foreach (var row in rows) { row.Frozen = this.IsFrozen(row); this._maps[row.ProductId] = row; } logger.Info("加载的沽清商品总数量<{0}>", this._maps.Count); this._maps.CollectionChanged += OnCollectionChanged; } catch (Exception ex) { logger.Error(ex, "加载沽清数据异常"); } } /// /// 加载沽清数据,区分本地数据和共享数据两种 /// private Tuple> GetAvailableSaleClearList() { bool isSuccess = false; string message = string.Empty; List rows = new List(); try { //启用全局沽清,服务中心提供数据 if (MessageCenterUtils.Instance.IsSaleClear) { logger.Info("获取服务中心的沽清数据"); var data = MessageCenterUtils.Instance.GetGlobalAvailableSaleClear(); isSuccess = data.Item1; message = data.Item2; rows = data.Item3; } else { logger.Info("加载本地的沽清数据"); //加载本地的今天及未来的沽清数据到内存 var startTime = DateTime.Now.ToString("yyyy-MM-dd 00:00:00"); using (var db = Global.Instance.OpenDataBase) { rows = db.Query("where startTime >=@0 and stopFlag == 0;", startTime).ToList(); } isSuccess = true; message = "本地数据加载成功"; } } catch (Exception ex) { isSuccess = false; message = "沽清数据异常"; logger.Error(ex, message); } return new Tuple>(isSuccess, message, rows); } private void OnCollectionChanged(object sender, CollectionChangedEventArgs e) { logger.Info("操作沽清业务: {0}", e.Action); if (e.NewItem != null && e.NewItem is KeyValuePair) { var map = (KeyValuePair)e.NewItem; var saleClear = map.Value; if (MessageCenterUtils.Instance.IsSaleClear) { if (Global.isSale2Web) { logger.Info("同步库存至云平台"); var quantity = saleClear.Quantity + saleClear.SaleQuantity - saleClear.TotalQuantity; var uSaleClear = new SaleClear { ProductId = saleClear.ProductId, SaleQuantity = quantity }; if (quantity > 0) { logger.Info($"操作沽清业务: {saleClear.ProductName},购{quantity}"); SaleClearApi.UpdateQuantity(uSaleClear, "add"); } else { logger.Info($"操作沽清业务: {saleClear.ProductName},退{Math.Abs(quantity)}"); SaleClearApi.UpdateQuantity(uSaleClear, "reduce"); } } } bool isSuccess = SaveSaleClear(saleClear); if (isSuccess) { //发送沽清变动通知 MsgEvent.Send(Constant.SALE_CLEAR_CHANGED_NOTIFY, map.Value); #region subin 20240212 收银台操作沽清后,同步至服务器 //SaleClearApi.UploadSaleClear(saleClear); #endregion } } else { logger.Warn("沽清业务数据不合法!"); } } /// /// 清理本地沽清数据 /// public void Expired() { try { //MessageCenterUtils.Instance.IsAvailable().Item1; if (MessageCenterUtils.Instance.IsSaleClear) { logger.Info("采用共享沽清模式,忽略本机清理操作...."); return; } logger.Info("开始清理过期沽清...."); var keys = this._maps.Keys.ToArray(); for (int i = 0; i < keys.Length; i++) { var key = keys[i]; var entity = this._maps[key]; //尚未到沽清开始时间或者已经过期 var frozen = IsFrozen(entity); if (entity.Frozen != frozen) { entity.Frozen = frozen; this._maps[key] = entity; logger.Info("沽清变动<{0}>", entity.ProductName); } } } catch (Exception ex) { logger.Error(ex, "清理过期沽清数据异常"); } } /// /// 检测是否有可用的沽清记录存在 /// /// /// public bool CheckSaleClear(SaleClear saleClear) { return (saleClear != null && saleClear.StopFlag == 0 && !saleClear.Frozen); } /// /// 沽清是否符合条件 /// /// /// public bool IsFrozen(SaleClear entity) { //当前时间 DateTime dateTime = DateTime.Now; //有效沽清时间 DateTime startTime = Convert.ToDateTime(entity.StartTime); //结束时间 DateTime endTime = Convert.ToDateTime(entity.EndTime); return (dateTime.CompareTo(startTime) < 0 || dateTime.CompareTo(endTime) > 0); } private bool SaveSaleClear(SaleClear saleClear) { bool isSuccess = true; try { //共享沽清模式 if (MessageCenterUtils.Instance.IsSaleClear) { //更新剩余数量 saleClear.Quantity = saleClear.TotalQuantity - saleClear.SaleQuantity; var transport = new TransportMessage(); transport.MessageType = MessageType.ProxySaleClear; transport.Data = saleClear; MqttUtils.Instance.Publish(JsonUtils.Serialize(transport)); logger.Info("发布变动到消息中心<{0} - {1} = {2}>", saleClear.TotalQuantity, saleClear.SaleQuantity, saleClear.Quantity); } else { //更新沽清记录的可用状态 saleClear.Frozen = this.IsFrozen(saleClear); //更新剩余数量 saleClear.Quantity = saleClear.TotalQuantity - saleClear.SaleQuantity; //保存到本地数据库 lock (Global.Instance.SyncLock) { using (var db = Global.Instance.OpenDataBase) { using (var trans = db.GetTransaction()) { db.Save(saleClear); trans.Complete(); } } } } } catch (Exception ex) { isSuccess = false; logger.Error(ex, "本机保存沽清异常"); } return isSuccess; } public bool SaveOrUpdateNoChanged(SaleClear saleClear) { bool isSuccess = true; #region subin 20240309 subin 共享沽清时更新服务器上的沽清数据 if (Global.isSale2Web) { var oldSaleClear = this.Maps.FirstOrDefault(m => m.Key == saleClear.ProductId); //取消沽清 if (saleClear.StopFlag == 1) { logger.Info($"取消沽清{saleClear.ProductName}<{saleClear.ProductId}>"); SaleClearApi.UploadSaleClear(saleClear); } else if (saleClear.StopFlag == 0) { //if (oldSaleClear.Key == null) //{ // SaleClearApi.UploadSaleClear(saleClear); //} SaleClearApi.UploadSaleClear(saleClear); } } #endregion this._maps.SaveOrUpdateNoChanged(saleClear.ProductId, saleClear); if (isSuccess) { //发送沽清变动通知 MsgEvent.Send(Constant.SALE_CLEAR_CHANGED_NOTIFY, saleClear); } return isSuccess; } public ObservableDictionary Maps { get { return this._maps; } } public static bool UploadSaleClear(SaleClear saleClear) { bool isSuccess = true; try { //共享沽清模式 if (MessageCenterUtils.Instance.IsSaleClear) { var uploadObject = BuilderUploadObject(saleClear); string jsonString = JsonUtils.Serialize(uploadObject); var uploadResult = Uploading(jsonString); if (uploadResult.Item1) { isSuccess = true; logger.Debug($"沽清数据上传成功:{jsonString}。"); } } } catch (Exception ex) { isSuccess = false; logger.Error(ex, "沽清数据上传异常"); } return isSuccess; } private static Dictionary BuilderUploadObject(SaleClear saleClear) { try { var uploadObject = new Dictionary(); //租户ID uploadObject["tenantId"] = saleClear.TenantId; //门店ID uploadObject["storeId"] = Global.Instance.Worker.StoreId; //门店编号 uploadObject["storeNo"] = saleClear.StoreNo; // POS编号 uploadObject["posNo"] = saleClear.PosNo; // 品牌Id uploadObject["brandId"] = saleClear.BrandId; // 类别Id uploadObject["typeId"] = saleClear.TypeId; // 分类名称 uploadObject["typeName"] = saleClear.TypeName; // 商品Id uploadObject["productId"] = saleClear.ProductId; // 编号 uploadObject["productNo"] = saleClear.ProductNo; // 名称 uploadObject["productName"] = saleClear.ProductName; // 剩余数量 uploadObject["quantity"] = saleClear.Quantity; // 提醒数量 uploadObject["notify"] = saleClear.NotifyQuantity; // 已售数量 uploadObject["sales"] = saleClear.SaleQuantity; // 沽清数量 uploadObject["total"] = saleClear.TotalQuantity; // 沽清时间 uploadObject["startTime"] = saleClear.StartTime; // 结束时间 uploadObject["endTime"] = saleClear.EndTime; // 取消时间 uploadObject["stopTime"] = saleClear.StopTime; // 停止人 uploadObject["stopUser"] = saleClear.StopUser; // 单位 uploadObject["unitId"] = saleClear.UnitId; // 计量单位名称 uploadObject["unitName"] = saleClear.UnitName; // 商品默认规格Id uploadObject["specId"] = saleClear.SpecId; // 商品默认规格名称 uploadObject["specName"] = saleClear.SpecName; // 是否套餐(0否1是) uploadObject["suitFlag"] = saleClear.SuitFlag; // 是否取消(0否1是) uploadObject["stopFlag"] = saleClear.StopFlag; // 颜色 uploadObject["memo"] = saleClear.Memo; // 扩展字段1 uploadObject["ext1"] = saleClear.Ext1; // 扩展字段2 uploadObject["ext2"] = saleClear.Ext2; // 扩展字段3 uploadObject["ext3"] = saleClear.Ext3; return uploadObject; } catch (Exception ex) { logger.Error(ex, "组装沽清上传数据异常:"); return null; } } private static Tuple> Uploading(string jsonString) { Tuple> result = null; try { logger.Debug("开始上传沽清数据......"); OpenApi api = OpenApiUtils.Instance.NextApi(ApiType.Business); SortedList parameters = OpenApiUtils.Instance.NewParameters(api); parameters.Add("method", "saleclear.business.upload"); parameters.Add("storeId", Global.Instance.Authc.StoreId); parameters.Add("jsonString", jsonString); var ignore = new List(); ignore.Add("jsonString"); parameters.Add("sign", OpenApiUtils.Instance.Sign(api, parameters, ignore)); string response = HttpClientUtils.PostAsync(api, api.Url, parameters); logger.Info("上传返回结果:{0}", response); if (Constant.IsSuccessful(response)) { var status = JsonUtils.Deserialize>(response); if (status.Status == 1) { logger.Info(status.Message); result = new Tuple>(true, status); } else if (status.Status == 2) { logger.Info(status.Message); result = new Tuple>(false, status); } else { string message = string.Format("<{0}>-{1}-{2}", status.ErrCode, status.ErrMessage, status.Message); logger.Error(string.Format("调用接口上传出错:{0}", message)); result = new Tuple>(false, status); } } else { string errorMessage = PaserErrors(response); logger.Error(string.Format("报文<{0}>解析出错:{1}", response, errorMessage)); //构建错误信息 var resp = new EntityResponse(); resp.Status = 0; resp.Message = errorMessage; resp.ErrCode = string.Empty; resp.ErrMessage = string.Empty; resp.Data = null; result = new Tuple>(false, resp); } } catch (Exception ex) { string message = string.Format("上传营业分账数据发生异常"); logger.Error(ex, message); //构建异常错误 var resp = new EntityResponse(); resp.Status = 0; resp.Message = message; resp.ErrCode = string.Empty; resp.ErrMessage = string.Empty; resp.Data = null; result = new Tuple>(false, resp); } return result; } protected static Dictionary _errorDesc = new Dictionary(); protected static string PaserErrors(string response) { string result = string.Empty; try { logger.Error(response); Errors mainError = JsonUtils.Deserialize(response); if (_errorDesc.ContainsKey(mainError.Code)) { result = _errorDesc[mainError.Code]; } else { result = mainError.Message; } List subError = mainError.SubErrors; if (subError != null && subError.Count > 0) { SubErrors error = subError[0]; if (_errorDesc.ContainsKey(error.Code)) { result = _errorDesc[error.Code]; } else { result = error.Message; } } } catch (Exception ex) { logger.Error(ex, "非法的错误信息格式"); result = _errorDesc["9999"]; } return result; } } }