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.

664 lines
22 KiB
C#

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<string, SaleClear> _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
/// <summary>
/// 加载今日沽清数据
/// </summary>
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<SaleClear>();
// 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<SaleClear> rows = data.Item3;
this._maps = new ObservableDictionary<string, SaleClear>();
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, "加载沽清数据异常");
}
}
/// <summary>
/// 加载沽清数据,区分本地数据和共享数据两种
/// </summary>
private Tuple<bool, string, List<SaleClear>> GetAvailableSaleClearList()
{
bool isSuccess = false;
string message = string.Empty;
List<SaleClear> rows = new List<SaleClear>();
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<SaleClear>("where startTime >=@0 and stopFlag == 0;", startTime).ToList();
}
isSuccess = true;
message = "本地数据加载成功";
}
}
catch (Exception ex)
{
isSuccess = false;
message = "沽清数据异常";
logger.Error(ex, message);
}
return new Tuple<bool, string, List<SaleClear>>(isSuccess, message, rows);
}
private void OnCollectionChanged(object sender, CollectionChangedEventArgs e)
{
logger.Info("操作沽清业务: {0}", e.Action);
if (e.NewItem != null && e.NewItem is KeyValuePair<string, SaleClear>)
{
var map = (KeyValuePair<string, SaleClear>)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("沽清业务数据不合法!");
}
}
/// <summary>
/// 清理本地沽清数据
/// </summary>
public void Expired()
{
try
{
//MessageCenterUtils.Instance.IsAvailable().Item1;
if (MessageCenterUtils.Instance.IsSaleClear)
{
logger.Info("采用共享沽清模式,忽略本机清理操作....");
return;
}
logger.Info("开始清理过期沽清....");
var keys = this._maps.Keys.ToArray<String>();
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, "清理过期沽清数据异常");
}
}
/// <summary>
/// 检测是否有可用的沽清记录存在
/// </summary>
/// <param name="saleClear"></param>
/// <returns></returns>
public bool CheckSaleClear(SaleClear saleClear)
{
return (saleClear != null && saleClear.StopFlag == 0 && !saleClear.Frozen);
}
/// <summary>
/// 沽清是否符合条件
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
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<SaleClear>();
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>(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<string, SaleClear> 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<string, object> BuilderUploadObject(SaleClear saleClear)
{
try
{
var uploadObject = new Dictionary<string, object>();
//租户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<bool, EntityResponse<UploadResult>> Uploading(string jsonString)
{
Tuple<bool, EntityResponse<UploadResult>> result = null;
try
{
logger.Debug("开始上传沽清数据......");
OpenApi api = OpenApiUtils.Instance.NextApi(ApiType.Business);
SortedList<string, string> 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<string>();
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<EntityResponse<UploadResult>>(response);
if (status.Status == 1)
{
logger.Info(status.Message);
result = new Tuple<bool, EntityResponse<UploadResult>>(true, status);
}
else if (status.Status == 2)
{
logger.Info(status.Message);
result = new Tuple<bool, EntityResponse<UploadResult>>(false, status);
}
else
{
string message = string.Format("<{0}>-{1}-{2}", status.ErrCode, status.ErrMessage, status.Message);
logger.Error(string.Format("调用接口<upload.business.splitorder>上传出错:{0}", message));
result = new Tuple<bool, EntityResponse<UploadResult>>(false, status);
}
}
else
{
string errorMessage = PaserErrors(response);
logger.Error(string.Format("报文<{0}>解析出错:{1}", response, errorMessage));
//构建错误信息
var resp = new EntityResponse<UploadResult>();
resp.Status = 0;
resp.Message = errorMessage;
resp.ErrCode = string.Empty;
resp.ErrMessage = string.Empty;
resp.Data = null;
result = new Tuple<bool, EntityResponse<UploadResult>>(false, resp);
}
}
catch (Exception ex)
{
string message = string.Format("上传营业分账数据发生异常");
logger.Error(ex, message);
//构建异常错误
var resp = new EntityResponse<UploadResult>();
resp.Status = 0;
resp.Message = message;
resp.ErrCode = string.Empty;
resp.ErrMessage = string.Empty;
resp.Data = null;
result = new Tuple<bool, EntityResponse<UploadResult>>(false, resp);
}
return result;
}
protected static Dictionary<string, string> _errorDesc = new Dictionary<string, string>();
protected static string PaserErrors(string response)
{
string result = string.Empty;
try
{
logger.Error(response);
Errors mainError = JsonUtils.Deserialize<Errors>(response);
if (_errorDesc.ContainsKey(mainError.Code))
{
result = _errorDesc[mainError.Code];
}
else
{
result = mainError.Message;
}
List<SubErrors> 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;
}
}
}