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#
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;
|
|
}
|
|
}
|
|
}
|