using StoreHub.KJDWebService.Models; using StoreHub.Framework.Repositorys; using Microsoft.Extensions.Configuration; using StoreHub.KJDWebService.Models.WxModel; using StoreHub.KJDWebService.Commons; using StoreHub.KJDWebService.Commons; using MySql.Data.MySqlClient; using NaiBa.Util.Extension; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using System; using System.Collections.Generic; using System.Data; using System.Linq; using System.Net; using System.Reflection; using System.Text; using System.Threading; namespace StoreHub.KJDWebService.Services { public class WXShopRevenueStatisticsService : ServiceBase { private readonly WXOrderService _wxOrderService; public WXShopRevenueStatisticsService(SqlRepository sqlRepository, IConfiguration configuration, WXOrderService wxOrderService) : base(sqlRepository, configuration) { _wxOrderService = wxOrderService; } public string GetShopOrderAmountRange(string inputData) { Result ret = Result.ArgumentNull(); if (!string.IsNullOrEmpty(inputData)) { try { // todo 获取当前用户微信店铺,或指定某一个店铺,某一段时间 // 获取店铺下时间范围内的所有订单涉及到的金额 // 直接返回 if (!WXUserService.GetSign(inputData)) throw new ArgumentException("校验失败"); var timeCalc = DateTime.Now; string shop_ids = RegexUtils.GetStringQuery(ref inputData, "shop_ids")?.Replace("'", "");//所有店铺信息 if (string.IsNullOrEmpty(shop_ids)) throw new ArgumentException("请选择店铺"); List shopIdLst = shop_ids.Split(',').ToList(); if (shopIdLst.Count > 0) shop_ids = "'" + string.Join("','", shopIdLst) + "'"; string start_Time = RegexUtils.GetStringQuery(ref inputData, "start_time")?.Replace("'", ""); string end_Time = RegexUtils.GetStringQuery(ref inputData, "end_time")?.Replace("'", ""); DateTime startTime = DateTime.Now.AddDays(-7); DateTime endTime = DateTime.Now; if (!string.IsNullOrEmpty(start_Time)) startTime = TimeUtils.StampMillisecondsToDateTime(start_Time.ParseToLong()); if (!string.IsNullOrEmpty(end_Time)) endTime = TimeUtils.StampMillisecondsToDateTime(end_Time.ParseToLong()); if (endTime <= startTime) throw new ArgumentException("结束时间必须大于开始时间"); if (endTime > startTime.AddMonths(3)) throw new ArgumentException("时间范围不能超过3个月"); int startTimestamp = TimeUtils.DateTimeToTimestampSeconds(startTime); int endTimestamp = TimeUtils.DateTimeToTimestampSeconds(endTime); StringBuilder sb = new StringBuilder(); sb.Append("SELECT "); sb.Append("A.shop_name, "); sb.Append("A.create_time, "); sb.Append("A.order_id, "); sb.Append("A.`status`, "); sb.Append("A.product_price, "); sb.Append("A.order_price, "); sb.Append("A.purchase_price, "); sb.Append("A.freight, "); sb.Append("A.predict_commission_fee, "); sb.Append("A.commission_amount, "); sb.Append("A.discounted_price, "); sb.Append("A.profit, "); sb.Append("A.purchase_status, "); sb.Append("A.out_amount, "); sb.Append("B.refund_status, "); sb.Append("c.`status` as refund_status_string, "); sb.Append("IFNULL(C.refund_amount, 0) AS refund_amount "); sb.Append("FROM erp.oms_wei_order AS A "); sb.Append("INNER JOIN erp.`oms_wei_order_item` AS B ON A.`order_id` = B.`order_id` "); sb.Append("LEFT JOIN ( SELECT order_id,`status`,refund_amount,id FROM erp.`oms_wei_refund` WHERE id IN ( SELECT MAX(id) FROM erp.`oms_wei_refund` GROUP BY order_id)) AS C ON A.order_id = C.order_id "); sb.Append("WHERE A.create_time BETWEEN " + startTimestamp + " AND " + endTimestamp + " "); sb.Append("AND A.`status` > 10 "); sb.Append("AND A.shop_id IN (" + shop_ids + ") "); sb.Append("ORDER BY A.create_time asc"); string conn = "ConnKJD"; MysqlUtils mySql = new MysqlUtils(conn); DataTable dt = mySql.ExecuteDataTable(sb.ToString()); mySql.Close(); if (!string.IsNullOrEmpty(mySql.Message)) throw new Exception(mySql.Message); // out_amount 为空或为0的订单,从中转仓订单表(GetTransitOrderTable)补全外采金额 // external_procurement:0=未查到,1=从 GetTransitOrderTable 查询到 Dictionary transitOutAmountMap = new Dictionary(); if (dt != null && dt.Rows.Count > 0) { HashSet needLookupOrderIds = new HashSet(); foreach (DataRow row in dt.Rows) { int? rowOutAmount = row["out_amount"].ParseToInt(); if (rowOutAmount == null || rowOutAmount.Value == 0) { string orderId = row["order_id"].ParseToString(); if (!string.IsNullOrEmpty(orderId)) needLookupOrderIds.Add(orderId); } } if (needLookupOrderIds.Count > 0) { const int chunkSize = 100; const int transitPageSize = 500; List needLookupList = needLookupOrderIds.ToList(); for (int i = 0; i < needLookupList.Count; i += chunkSize) { int takeCount = Math.Min(chunkSize, needLookupList.Count - i); string chunkOrderIds = string.Join(",", needLookupList.GetRange(i, takeCount)); int pageIndex = 1; while (true) { int count = -1; var transitList = _wxOrderService.GetTransitOrderTable(pageIndex, transitPageSize, chunkOrderIds, null, null, null, null, "true", out count); if (transitList == null || transitList.Count == 0) break; foreach (var transit in transitList) { if (transit != null && !string.IsNullOrEmpty(transit.order_no) && needLookupOrderIds.Contains(transit.order_no)) { if (!transitOutAmountMap.ContainsKey(transit.order_no)) transitOutAmountMap[transit.order_no] = transit.out_amount ?? 0; } } if (transitList.Count < transitPageSize) break; pageIndex++; } } } } List orderList = new List(); if (dt != null) { foreach (DataRow row in dt.Rows) orderList.Add(BuildOrderAmountInfo(row, transitOutAmountMap)); } // 买手号退款金额:退款订单(refund_status>1)且非外采时,从买手售后表补全退款金额 FillBuyerRefundAmount(orderList, conn); ret.Code = 200; ret.Msg = "success"; ret.Data = orderList; } catch (Exception ex) { ret.Code = 500; ret.Msg = ex.Message; } } return JsonUtils.SerializeObjectToString(ret, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore }); } /// /// 将订单行转换为金额详情:out_amount 为空或0时从中转仓订单表补全,并计算利润 /// private WXOrderAmountInfo BuildOrderAmountInfo(DataRow row, Dictionary transitOutAmountMap) { string orderId = row["order_id"].ParseToString(); int outAmount = row["out_amount"].ParseToInt(); int externalProcurement = 0; if (outAmount == 0) { int transitAmount; if (transitOutAmountMap.TryGetValue(orderId, out transitAmount)) { externalProcurement = 1; outAmount = transitAmount; } } int orderPrice = row["order_price"].ParseToInt(); int predictCommissionFee = row["predict_commission_fee"].ParseToInt(); int commissionAmount = row["commission_amount"].ParseToInt(); int refund_amount = row["refund_amount"].ParseToInt(); // 利润 = 订单金额 - 外采金额 - 平台服务费 - 达人佣金(为空按0处理) int profit = orderPrice - outAmount - predictCommissionFee - commissionAmount; if (refund_amount > 0) //退款利润计算 { profit = orderPrice - refund_amount; } return new WXOrderAmountInfo { shop_name = row["shop_name"].ParseToString(), create_time = row["create_time"].ParseToInt(), order_id = orderId, status = row["status"].ParseToInt(), product_price = row["product_price"].ParseToInt(), order_price = orderPrice, purchase_price = row["purchase_price"].ParseToInt(), freight = row["freight"].ParseToInt(), predict_commission_fee = predictCommissionFee, commission_amount = commissionAmount, discounted_price = row["discounted_price"].ParseToInt(), profit = profit, purchase_status = row["purchase_status"].ParseToInt(), out_amount = outAmount, refund_status = row["refund_status"].ParseToInt(), refund_status_string = row["refund_status_string"].ParseToString(), refund_amount = row["refund_amount"].ParseToInt(), external_procurement = externalProcurement }; } /// /// 买手号退款金额:退款订单(refund_status>1)且非外采时,从买手售后表(wxxd.order_pdd_buyer_after_sale)查询退款金额(refundAmount)填入 /// private void FillBuyerRefundAmount(List orderList, string conn) { if (orderList == null || orderList.Count == 0) return; // 退款订单(refund_status>1)且非外采(out_amount、external_procurement都为0)的订单才查询买手号退款金额 HashSet needLookupOrderIds = new HashSet(); foreach (WXOrderAmountInfo order in orderList) { if (order.refund_status > 1 && order.external_procurement == 1 && order.out_amount > 0) { if (!string.IsNullOrEmpty(order.order_id)) needLookupOrderIds.Add(order.order_id); } } if (needLookupOrderIds.Count == 0) return; Dictionary> buyerRefundAmountMap = new Dictionary>(); MysqlUtils mySql = new MysqlUtils(conn); try { const int chunkSize = 100; List needLookupList = needLookupOrderIds.ToList(); for (int i = 0; i < needLookupList.Count; i += chunkSize) { int takeCount = Math.Min(chunkSize, needLookupList.Count - i); string chunkOrderIds = string.Join("','", needLookupList.GetRange(i, takeCount)); string sql = "SELECT WX_ORDER_ID,refundAmount,statusText FROM wxxd.order_pdd_buyer_after_sale a " + "WHERE a.WX_ORDER_ID IN ('" + chunkOrderIds + "') AND a.id = ( SELECT MAX(b.id) FROM wxxd.order_pdd_buyer_after_sale b WHERE b.WX_ORDER_ID = a.WX_ORDER_ID) ORDER BY a.WX_ORDER_ID;"; DataTable buyerDt = mySql.ExecuteDataTable(sql); if (buyerDt != null) { foreach (DataRow row in buyerDt.Rows) { string wxOrderId = row["WX_ORDER_ID"].ParseToString(); int refundAmount = row["refundAmount"].ParseToInt(); string statusText = row["statusText"].ParseToString(); if (!string.IsNullOrEmpty(wxOrderId) && !buyerRefundAmountMap.ContainsKey(wxOrderId)) buyerRefundAmountMap[wxOrderId] = new Tuple(refundAmount, statusText); } } } } finally { mySql.Close(); } if (!string.IsNullOrEmpty(mySql.Message)) throw new Exception(mySql.Message); foreach (WXOrderAmountInfo order in orderList) { Tuple buyerRefundAmount; if (buyerRefundAmountMap.TryGetValue(order.order_id, out buyerRefundAmount)) { order.buyer_refund_amount = buyerRefundAmount.Item1; order.buyer_refund_status = buyerRefundAmount.Item2; } } } } }