新增利润统计接口
This commit is contained in:
1 parent
e37f15737c
commit
bd08d1c3d7
11 files changed
+2780
-1916
No files matched your search
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,300 @@
|
||||
using KJD_Web.Model;
|
||||
using KJD_Web.Model.WxModel;
|
||||
using KJD_Web.Upload.Prod;
|
||||
using KJD_Web.Utils;
|
||||
using MySql.Data.MySqlClient;
|
||||
using Mysqlx.Crud;
|
||||
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;
|
||||
using System.Web;
|
||||
using System.Web.ApplicationServices;
|
||||
using System.Web.Security;
|
||||
using System.Web.UI.WebControls;
|
||||
|
||||
namespace KJD_Web.BLL.WXXD
|
||||
{
|
||||
public class WXShopRevenueStatisticsManage
|
||||
{
|
||||
public WXShopRevenueStatisticsManage()
|
||||
{
|
||||
|
||||
}
|
||||
public string GetShopOrderAmountRange(string inputData)
|
||||
{
|
||||
Result ret = Result.ArgumentNull();
|
||||
if (!string.IsNullOrEmpty(inputData))
|
||||
{
|
||||
try
|
||||
{
|
||||
// todo 获取当前用户微信店铺,或指定某一个店铺,某一段时间
|
||||
// 获取店铺下时间范围内的所有订单涉及到的金额
|
||||
// 直接返回
|
||||
if (!WXUserManage.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<string> 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 B.shop_id IN (" + shop_ids + ") ");
|
||||
sb.Append("ORDER BY A.create_time asc");
|
||||
|
||||
string conn = System.Configuration.ConfigurationManager.ConnectionStrings["ConnKJD"].ConnectionString;
|
||||
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<string, int> transitOutAmountMap = new Dictionary<string, int>();
|
||||
if (dt != null && dt.Rows.Count > 0)
|
||||
{
|
||||
HashSet<string> needLookupOrderIds = new HashSet<string>();
|
||||
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)
|
||||
{
|
||||
WXOrderManage wxOrderManage = new WXOrderManage();
|
||||
const int chunkSize = 100;
|
||||
const int transitPageSize = 500;
|
||||
List<string> 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 = wxOrderManage.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<WXOrderAmountInfo> orderList = new List<WXOrderAmountInfo>();
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将订单行转换为金额详情:out_amount 为空或0时从中转仓订单表补全,并计算利润
|
||||
/// </summary>
|
||||
private WXOrderAmountInfo BuildOrderAmountInfo(DataRow row, Dictionary<string, int> 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
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 买手号退款金额:退款订单(refund_status>1)且非外采时,从买手售后表(wxxd.order_pdd_buyer_after_sale)查询退款金额(refundAmount)填入
|
||||
/// </summary>
|
||||
private void FillBuyerRefundAmount(List<WXOrderAmountInfo> orderList, string conn)
|
||||
{
|
||||
if (orderList == null || orderList.Count == 0)
|
||||
return;
|
||||
|
||||
// 退款订单(refund_status>1)且非外采(out_amount、external_procurement都为0)的订单才查询买手号退款金额
|
||||
HashSet<string> needLookupOrderIds = new HashSet<string>();
|
||||
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<string, Tuple<int, string>> buyerRefundAmountMap = new Dictionary<string, Tuple<int, string>>();
|
||||
MysqlUtils mySql = new MysqlUtils(conn);
|
||||
try
|
||||
{
|
||||
const int chunkSize = 100;
|
||||
List<string> 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<int, string>(refundAmount, statusText);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mySql.Close();
|
||||
}
|
||||
if (!string.IsNullOrEmpty(mySql.Message))
|
||||
throw new Exception(mySql.Message);
|
||||
|
||||
foreach (WXOrderAmountInfo order in orderList)
|
||||
{
|
||||
Tuple<int, string> buyerRefundAmount;
|
||||
if (buyerRefundAmountMap.TryGetValue(order.order_id, out buyerRefundAmount))
|
||||
{
|
||||
order.buyer_refund_amount = buyerRefundAmount.Item1;
|
||||
order.buyer_refund_status = buyerRefundAmount.Item2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Security;
|
||||
using System.Web.SessionState;
|
||||
using KJD_Web.Utils;
|
||||
|
||||
namespace KJD_Web
|
||||
{
|
||||
@@ -159,14 +160,27 @@ namespace KJD_Web
|
||||
{
|
||||
hPath = hPath.Replace("/wxxd_order/", "/WXOrderService.asmx/");
|
||||
Context.RewritePath(hPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!hPath.Contains(".asmx") && hPath.Contains("/wxxd_shop_revenue_statistics/"))
|
||||
{
|
||||
hPath = hPath.Replace("/wxxd_shop_revenue_statistics/", "/WXShopRevenueStatistics.asmx/");
|
||||
Context.RewritePath(hPath);
|
||||
}
|
||||
// 请求录制:捕获请求链接与请求内容,用于迁移后的回放校验
|
||||
//try { ApiRequestLogger.BeginRequest(Context); } catch { }
|
||||
}
|
||||
|
||||
protected void Application_AuthenticateRequest(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected void Application_EndRequest(object sender, EventArgs e)
|
||||
{
|
||||
// 请求录制:合并响应内容并落盘
|
||||
//try { ApiRequestLogger.EndRequest(Context); } catch { }
|
||||
}
|
||||
|
||||
protected void Application_Error(object sender, EventArgs e)
|
||||
{
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
<Content Include="WXProductService.asmx" />
|
||||
<Content Include="WXShopService.asmx" />
|
||||
<Content Include="DYShopService.asmx" />
|
||||
<Content Include="WXShopRevenueStatistics.asmx" />
|
||||
<Content Include="WXUserService.asmx" />
|
||||
<Content Include="DYUserService.asmx" />
|
||||
<Content Include="Global.asax" />
|
||||
@@ -120,6 +121,7 @@
|
||||
<Compile Include="BLL\WXXD\WXOrderManage.cs" />
|
||||
<Compile Include="BLL\WXXD\WXProductManage.cs" />
|
||||
<Compile Include="BLL\WXXD\WXShopManage.cs" />
|
||||
<Compile Include="BLL\WXXD\WXShopRevenueStatisticsManage.cs" />
|
||||
<Compile Include="BLL\WXXD\WXUserManage.cs" />
|
||||
<Compile Include="Model\ErpModel\ErpMerchantInfo.cs" />
|
||||
<Compile Include="Model\JSTDownProdSkuInfo.cs" />
|
||||
@@ -130,6 +132,7 @@
|
||||
<Compile Include="Model\PddModel\PDDProductDetailInfo.cs" />
|
||||
<Compile Include="Model\PddModel\PDDProductInfo.cs" />
|
||||
<Compile Include="Model\PddModel\PDDProductPropertyInfo.cs" />
|
||||
<Compile Include="Model\Result.cs" />
|
||||
<Compile Include="Model\ShopModel\JSTListProductSkuInfo.cs" />
|
||||
<Compile Include="Model\WXAddressInfo.cs" />
|
||||
<Compile Include="Model\WxModel\oem.cs" />
|
||||
@@ -147,9 +150,11 @@
|
||||
<Compile Include="Model\WxModel\WXClientProductInfoLite.cs" />
|
||||
<Compile Include="Model\WxModel\WXGetEditProductInfoLite.cs" />
|
||||
<Compile Include="Model\WxModel\WxOptimizeExpressInfo.cs" />
|
||||
<Compile Include="Model\WxModel\WXOrderAmountInfo.cs" />
|
||||
<Compile Include="Model\WxModel\WXOrderDecryptAddressInfo.cs" />
|
||||
<Compile Include="Model\WxProdSecretSpuIdShopIdMapParam.cs" />
|
||||
<Compile Include="Model\WXShopDefaultAddressInfo.cs" />
|
||||
<Compile Include="Utils\ApiRequestLogger.cs" />
|
||||
<Compile Include="WXOrderService.asmx.cs">
|
||||
<DependentUpon>WXOrderService.asmx</DependentUpon>
|
||||
<SubType>Component</SubType>
|
||||
@@ -184,6 +189,10 @@
|
||||
<DependentUpon>DYShopService.asmx</DependentUpon>
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="WXShopRevenueStatistics.asmx.cs">
|
||||
<DependentUpon>WXShopRevenueStatistics.asmx</DependentUpon>
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
<Compile Include="WXUserService.asmx.cs">
|
||||
<DependentUpon>WXUserService.asmx</DependentUpon>
|
||||
<SubType>Component</SubType>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<UseIISExpress>true</UseIISExpress>
|
||||
@@ -8,8 +8,8 @@
|
||||
<IISExpressWindowsAuthentication />
|
||||
<IISExpressUseClassicPipelineMode />
|
||||
<UseGlobalApplicationHostFile />
|
||||
<LastActiveSolutionConfig>Release|Any CPU</LastActiveSolutionConfig>
|
||||
<ProjectView>ShowAllFiles</ProjectView>
|
||||
<LastActiveSolutionConfig>Debug|Any CPU</LastActiveSolutionConfig>
|
||||
<ProjectView>ProjectFiles</ProjectView>
|
||||
</PropertyGroup>
|
||||
<ProjectExtensions>
|
||||
<VisualStudio>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace KJD_Web.Model
|
||||
{
|
||||
public class Result
|
||||
{
|
||||
public Result()
|
||||
{
|
||||
|
||||
}
|
||||
public Result(int code, string msg)
|
||||
{
|
||||
Code = code;
|
||||
Msg = msg;
|
||||
}
|
||||
|
||||
public int Code { get; set; }
|
||||
public string Msg { get; set; }
|
||||
public object Data { get; set; }
|
||||
|
||||
public static Result Success(string msg)
|
||||
{
|
||||
return new Result
|
||||
{
|
||||
Code = 200,
|
||||
Msg = msg
|
||||
};
|
||||
}
|
||||
public static Result Failure(string msg)
|
||||
{
|
||||
return new Result
|
||||
{
|
||||
Code = 500,
|
||||
Msg = msg
|
||||
};
|
||||
}
|
||||
//参数不合法
|
||||
public static Result ArgumentNull()
|
||||
{
|
||||
return new Result { Code = 19999, Msg = "输入为空" };
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
|
||||
namespace KJD_Web.Model.WxModel
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信订单金额详情
|
||||
/// </summary>
|
||||
public class WXOrderAmountInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 店铺名称
|
||||
/// </summary>
|
||||
public string shop_name { get; set; }
|
||||
/// <summary>
|
||||
/// 下单时间(秒级时间戳)
|
||||
/// </summary>
|
||||
public int create_time { get; set; }
|
||||
/// <summary>
|
||||
/// 订单号
|
||||
/// </summary>
|
||||
public string order_id { get; set; }
|
||||
/// <summary>
|
||||
/// 订单状态
|
||||
/// </summary>
|
||||
public int status { get; set; }
|
||||
/// <summary>
|
||||
/// 商品总价(分)
|
||||
/// </summary>
|
||||
public int product_price { get; set; }
|
||||
/// <summary>
|
||||
/// 订单金额(分)
|
||||
/// </summary>
|
||||
public int order_price { get; set; }
|
||||
/// <summary>
|
||||
/// 采购价(分)
|
||||
/// </summary>
|
||||
public int purchase_price { get; set; }
|
||||
/// <summary>
|
||||
/// 运费(分)
|
||||
/// </summary>
|
||||
public int freight { get; set; }
|
||||
/// <summary>
|
||||
/// 平台服务费(预计技术服务费,分)
|
||||
/// </summary>
|
||||
public int predict_commission_fee { get; set; }
|
||||
/// <summary>
|
||||
/// 达人佣金(分)
|
||||
/// </summary>
|
||||
public int commission_amount { get; set; }
|
||||
/// <summary>
|
||||
/// 优惠金额(分)
|
||||
/// </summary>
|
||||
public int discounted_price { get; set; }
|
||||
/// <summary>
|
||||
/// 利润 = 订单金额 - 外采金额 - 平台服务费 - 达人佣金(分)
|
||||
/// </summary>
|
||||
public int profit { get; set; }
|
||||
/// <summary>
|
||||
/// 采购状态
|
||||
/// </summary>
|
||||
public int purchase_status { get; set; }
|
||||
/// <summary>
|
||||
/// 外采金额(分),out_amount 为空或0时从中转仓订单表补全
|
||||
/// </summary>
|
||||
public int out_amount { get; set; }
|
||||
/// <summary>
|
||||
/// 售后状态(订单明细 refund_status)
|
||||
/// </summary>
|
||||
public int refund_status { get; set; }
|
||||
/// <summary>
|
||||
/// 售后状态(退款单 status)
|
||||
/// </summary>
|
||||
public string refund_status_string { get; set; }
|
||||
/// <summary>
|
||||
/// 售后退款金额(分),无退款默认0
|
||||
/// </summary>
|
||||
public int refund_amount { get; set; }
|
||||
/// <summary>
|
||||
/// 是否外采:0=未从中转仓订单表查到,1=查到
|
||||
/// </summary>
|
||||
public int external_procurement { get; set; }
|
||||
/// <summary>
|
||||
/// 买手号退款金额(分):退款订单且非外采时,从买手售后表(order_pdd_buyer_after_sale)补全,无退款默认0
|
||||
/// </summary>
|
||||
public int buyer_refund_amount { get; set; }
|
||||
/// <summary>
|
||||
/// 买手号退款状态
|
||||
/// </summary>
|
||||
public string buyer_refund_status { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace KJD_Web.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// API 请求录制工具:挂接在 Global.asax 的 BeginRequest / EndRequest,
|
||||
/// 捕获请求链接(action/service)、请求内容(body/query)与响应,按天落盘为 JSON Lines,
|
||||
/// 用于 .NET Core 迁移后的回放校验。
|
||||
/// 配置(Web.config appSettings):
|
||||
/// ApiRequestLog = 1 开启 / 0 关闭(默认开启)
|
||||
/// ApiRequestLogDir = 日志目录(默认 ~/App_Data/ApiLogs)
|
||||
/// </summary>
|
||||
public static class ApiRequestLogger
|
||||
{
|
||||
private const string ItemKey = "__ApiRequestLog__";
|
||||
private const string FilterItemKey = "__ApiResponseCapture__";
|
||||
private const int MaxBodyLogLength = 100000;
|
||||
|
||||
private static readonly object SyncRoot = new object();
|
||||
private static readonly Regex SensitiveKeyValueRegex = new Regex(
|
||||
@"(?i)(\b(?:password|passwd|pwd|token|access_token|secret|app_secret|client_secret|sign|signature|api_key|authorization|key)=)([^&\s""]*)",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private static readonly Regex SensitiveRegex = new Regex(
|
||||
"\"((?i)password|passwd|pwd|token|access_token|secret|app_secret|client_secret|sign|signature|api_key|authorization|key)\"\\s*:\\s*\"([^\"]*)\"",
|
||||
RegexOptions.Compiled);
|
||||
|
||||
private static readonly string[] FriendlyPrefixes =
|
||||
{
|
||||
"/user/", "/shop/", "/update/", "/order/", "/product/", "/product_third/", "/price/",
|
||||
"/bxx_user/", "/bxx_shop/", "/bxx_product/", "/bxx_product_third/", "/bxx_price/",
|
||||
"/dyxd_user/", "/dyxd_shop/", "/dyxd_product/", "/dyxd_product_third/", "/dyxd_price/",
|
||||
"/wxxd_user/", "/wxxd_shop/", "/wxxd_product/", "/wxxd_product_third/", "/wxxd_price/", "/wxxd_order/"
|
||||
};
|
||||
|
||||
private static bool _configLoaded;
|
||||
private static bool _enabled = true;
|
||||
private static string _logDir;
|
||||
|
||||
private sealed class RequestRecord
|
||||
{
|
||||
public string id;
|
||||
public DateTime time;
|
||||
public string service;
|
||||
public string action;
|
||||
public string url;
|
||||
public string path;
|
||||
public string method;
|
||||
public string query;
|
||||
public string body;
|
||||
public string ip;
|
||||
public string ua;
|
||||
}
|
||||
|
||||
/// <summary>请求入口:解析 action/service、读取并缓存请求体、安装响应捕获。</summary>
|
||||
public static void BeginRequest(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
EnsureConfig(context);
|
||||
if (!_enabled) return;
|
||||
|
||||
string path = context.Request.Path ?? "";
|
||||
if (!IsApiPath(path)) return;
|
||||
|
||||
string service, action;
|
||||
ParsePath(path, out service, out action);
|
||||
|
||||
string body = ReadBody(context);
|
||||
|
||||
var record = new RequestRecord
|
||||
{
|
||||
id = Guid.NewGuid().ToString("N"),
|
||||
time = DateTime.Now,
|
||||
service = service,
|
||||
action = action,
|
||||
path = path,
|
||||
method = context.Request.HttpMethod,
|
||||
url = SafeGet(() => context.Request.Url == null ? path : context.Request.Url.ToString(), path),
|
||||
query = Desensitize(SafeGet(() => context.Request.Url == null ? "" : context.Request.Url.Query, "")),
|
||||
body = Truncate(Desensitize(body)),
|
||||
ip = SafeGet(() => context.Request.UserHostAddress, ""),
|
||||
ua = SafeGet(() => context.Request.UserAgent, "")
|
||||
};
|
||||
|
||||
context.Items[ItemKey] = record;
|
||||
try { context.Response.Headers["X-Request-Id"] = record.id; } catch { }
|
||||
|
||||
try
|
||||
{
|
||||
var filter = new ApiResponseCaptureStream(context.Response.Filter);
|
||||
context.Response.Filter = filter;
|
||||
context.Items[FilterItemKey] = filter;
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>请求出口:合并响应,写一条 JSON Lines 记录。</summary>
|
||||
public static void EndRequest(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_enabled) return;
|
||||
var record = context.Items[ItemKey] as RequestRecord;
|
||||
if (record == null) return;
|
||||
|
||||
string responseText = null;
|
||||
try
|
||||
{
|
||||
var filter = context.Items[FilterItemKey] as ApiResponseCaptureStream;
|
||||
if (filter != null) responseText = Truncate(Desensitize(filter.GetCapturedText()));
|
||||
}
|
||||
catch { }
|
||||
|
||||
var line = JsonConvert.SerializeObject(new
|
||||
{
|
||||
id = record.id,
|
||||
time = record.time.ToString("yyyy-MM-dd HH:mm:ss.fff"),
|
||||
service = record.service,
|
||||
action = record.action,
|
||||
method = record.method,
|
||||
url = record.url,
|
||||
path = record.path,
|
||||
query = record.query,
|
||||
body = record.body,
|
||||
response = responseText,
|
||||
ip = record.ip,
|
||||
ua = record.ua,
|
||||
status = SafeGet(() => context.Response.StatusCode, 0)
|
||||
});
|
||||
|
||||
string fileName = Path.Combine(_logDir, "api_" + DateTime.Now.ToString("yyyyMMdd") + ".jsonl");
|
||||
lock (SyncRoot)
|
||||
{
|
||||
File.AppendAllText(fileName, line + Environment.NewLine, Encoding.UTF8);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static void EnsureConfig(HttpContext context)
|
||||
{
|
||||
if (_configLoaded) return;
|
||||
lock (SyncRoot)
|
||||
{
|
||||
if (_configLoaded) return;
|
||||
try
|
||||
{
|
||||
string sw = ConfigurationManager.AppSettings["ApiRequestLog"];
|
||||
if (!string.IsNullOrEmpty(sw))
|
||||
_enabled = (sw == "1" || sw.Equals("true", StringComparison.OrdinalIgnoreCase) || sw.Equals("on", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
string dir = ConfigurationManager.AppSettings["ApiRequestLogDir"];
|
||||
if (string.IsNullOrEmpty(dir)) dir = "~/App_Data/ApiLogs";
|
||||
_logDir = dir.StartsWith("~/") ? context.Server.MapPath(dir) : dir;
|
||||
if (!Directory.Exists(_logDir)) Directory.CreateDirectory(_logDir);
|
||||
}
|
||||
catch { }
|
||||
_configLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ReadBody(HttpContext context)
|
||||
{
|
||||
try
|
||||
{
|
||||
var input = context.Request.InputStream;
|
||||
if (input == null || !input.CanRead || input.Length <= 0) return "";
|
||||
string text;
|
||||
using (var reader = new StreamReader(input, Encoding.UTF8, true, 1024, true))
|
||||
{
|
||||
text = reader.ReadToEnd();
|
||||
}
|
||||
try { if (input.CanSeek) input.Position = 0; } catch { }
|
||||
return text;
|
||||
}
|
||||
catch { return ""; }
|
||||
}
|
||||
|
||||
private static bool IsApiPath(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path)) return false;
|
||||
if (path.IndexOf(".asmx", StringComparison.OrdinalIgnoreCase) >= 0) return true;
|
||||
foreach (string p in FriendlyPrefixes)
|
||||
{
|
||||
if (path.StartsWith(p, StringComparison.OrdinalIgnoreCase)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void ParsePath(string path, out string service, out string action)
|
||||
{
|
||||
service = "";
|
||||
action = "";
|
||||
if (string.IsNullOrEmpty(path)) return;
|
||||
string p = path.TrimStart('/');
|
||||
if (p.EndsWith("/")) p = p.TrimEnd('/');
|
||||
string[] segs = p.Split('/');
|
||||
if (segs.Length == 0) return;
|
||||
action = segs[segs.Length - 1];
|
||||
if (segs.Length >= 2)
|
||||
{
|
||||
int asmxIdx = -1;
|
||||
for (int i = 0; i < segs.Length; i++)
|
||||
{
|
||||
if (segs[i].EndsWith(".asmx", StringComparison.OrdinalIgnoreCase)) { asmxIdx = i; break; }
|
||||
}
|
||||
if (asmxIdx >= 0) service = segs[asmxIdx].Replace(".asmx", "");
|
||||
else service = segs[0];
|
||||
}
|
||||
else if (p.EndsWith(".asmx", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
service = p.Replace(".asmx", "");
|
||||
action = service;
|
||||
}
|
||||
}
|
||||
|
||||
private static string Desensitize(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text)) return text;
|
||||
string r = SensitiveRegex.Replace(text, "\"$1\":\"***\"");
|
||||
r = SensitiveKeyValueRegex.Replace(r, "$1***");
|
||||
return r;
|
||||
}
|
||||
|
||||
private static string Truncate(string text)
|
||||
{
|
||||
if (string.IsNullOrEmpty(text) || text.Length <= MaxBodyLogLength) return text;
|
||||
return text.Substring(0, MaxBodyLogLength) + " [truncated...]";
|
||||
}
|
||||
|
||||
private static T SafeGet<T>(Func<T> fn, T def)
|
||||
{
|
||||
try { return fn(); }
|
||||
catch { return def; }
|
||||
}
|
||||
|
||||
/// <summary>响应捕获流:包装 Response.Filter,缓存写出内容。</summary>
|
||||
private sealed class ApiResponseCaptureStream : Stream
|
||||
{
|
||||
private readonly Stream _inner;
|
||||
private readonly MemoryStream _buffer = new MemoryStream();
|
||||
private string _capturedText;
|
||||
|
||||
public ApiResponseCaptureStream(Stream inner)
|
||||
{
|
||||
_inner = inner ?? new MemoryStream();
|
||||
}
|
||||
|
||||
public string GetCapturedText()
|
||||
{
|
||||
if (_capturedText == null) _capturedText = BytesToText(_buffer.ToArray());
|
||||
return _capturedText;
|
||||
}
|
||||
|
||||
private static string BytesToText(byte[] bytes)
|
||||
{
|
||||
if (bytes == null || bytes.Length == 0) return "";
|
||||
bool binary = false;
|
||||
for (int i = 0; i < bytes.Length; i++)
|
||||
{
|
||||
byte b = bytes[i];
|
||||
if (b == 0 || b < 8) { binary = true; break; }
|
||||
}
|
||||
if (binary) return "<binary length=" + bytes.Length + ">";
|
||||
return Encoding.UTF8.GetString(bytes);
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return _inner.CanRead; } }
|
||||
public override bool CanSeek { get { return _inner.CanSeek; } }
|
||||
public override bool CanWrite { get { return _inner.CanWrite; } }
|
||||
public override long Length { get { return _inner.Length; } }
|
||||
public override long Position { get { return _inner.Position; } set { _inner.Position = value; } }
|
||||
public override void Flush() { _inner.Flush(); }
|
||||
public override int Read(byte[] buffer, int offset, int count) { return _inner.Read(buffer, offset, count); }
|
||||
public override long Seek(long offset, SeekOrigin origin) { return _inner.Seek(offset, origin); }
|
||||
public override void SetLength(long value) { _inner.SetLength(value); }
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
_buffer.Write(buffer, offset, count);
|
||||
_inner.Write(buffer, offset, count);
|
||||
}
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
try { GetCapturedText(); } catch { }
|
||||
try { _inner.Dispose(); } catch { }
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<%@ WebService Language="C#" CodeBehind="WXShopRevenueStatistics.asmx.cs" Class="KJD_Web.WXShopRevenueStatistics" %>
|
||||
@@ -0,0 +1,68 @@
|
||||
using KJD_Web.BLL.BXX;
|
||||
using KJD_Web.BLL.WXXD;
|
||||
using KJD_Web.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
using System.Web.Services;
|
||||
|
||||
namespace KJD_Web
|
||||
{
|
||||
/// <summary>
|
||||
/// UserService 的摘要说明
|
||||
/// </summary>
|
||||
[WebService(Namespace = "http://tempuri.org/")]
|
||||
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
|
||||
[System.ComponentModel.ToolboxItem(false)]
|
||||
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行。
|
||||
// [System.Web.Script.Services.ScriptService]
|
||||
public class WXShopRevenueStatistics : System.Web.Services.WebService
|
||||
{
|
||||
public WXShopRevenueStatistics()
|
||||
{
|
||||
|
||||
}
|
||||
private string GetRequestJsonString()
|
||||
{
|
||||
Context.Response.ContentType = "text/html;charset=utf-8";
|
||||
Context.Response.ContentEncoding = System.Text.Encoding.GetEncoding("utf-8");
|
||||
|
||||
using (Stream s = HttpContext.Current.Request.InputStream) // 获得json 字符流
|
||||
{
|
||||
// 还原数据流
|
||||
byte[] b = new byte[s.Length];
|
||||
s.Read(b, 0, (int)s.Length);
|
||||
string jsonText = Encoding.UTF8.GetString(b); // JSON字符串
|
||||
if (string.IsNullOrEmpty(jsonText))
|
||||
{
|
||||
var qs = HttpContext.Current.Request.QueryString;
|
||||
if (qs != null && qs.Keys.Count > 0)
|
||||
{
|
||||
jsonText = "{";
|
||||
foreach (var key in qs.Keys)
|
||||
{
|
||||
jsonText += "\"" + key + "\":" + "\"" + qs[key.ToString()] + "\";";
|
||||
}
|
||||
jsonText = jsonText.TrimEnd(';') + "}";
|
||||
}
|
||||
}
|
||||
return jsonText;
|
||||
}
|
||||
}
|
||||
|
||||
[WebMethod]
|
||||
public void get_shop_order_amount()
|
||||
{
|
||||
string jsontext = GetRequestJsonString();
|
||||
|
||||
var result = jsontext;
|
||||
result = new WXShopRevenueStatisticsManage().GetShopOrderAmountRange(jsontext);
|
||||
|
||||
Context.Response.Write(result);
|
||||
Context.Response.End();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user