using _29WxOrderWpf.Ctrls.Statistics.Models;
using AllBase;
using AllBase.Helper;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Work;
using WxOrder.Framework.Commons;
namespace _29WxOrderWpf.Ctrls.Statistics.Services
{
///
/// 店铺营收统计 - 数据服务调用接口。
/// 页面只依赖此接口,接入其他数据源时新建实现类,
/// 并通过 ViewModel 构造函数注入即可,无需改动页面。
///
public interface IShopRevenueStatisticsService
{
///
/// 综合统计查询:返回汇总卡片、销售趋势(按天)、店铺销售占比、店铺统计表格。
/// 用 GroupType=Day 一次请求取回按店铺按天的记录,再由客户端汇总,无缓存。
///
Task QueryAsync(ShopRevenueStatisticsQuery query);
///
/// 天/周/月周期统计查询:返回汇总卡片 + 按周期(天/周/月)聚合的表格数据。
/// 接口不返回整段汇总,Summary 由返回的周期记录在客户端合计。
///
Task QueryPeriodStatisticsAsync(ShopRevenueStatisticsQuery query, PeriodStatisticType periodType);
/// 获取店铺下拉列表(用于查询条件)
Task> GetShopOptionsAsync();
///
/// 订单明细列表(有效订单 / 退款订单 / 买手未退回金额的订单),供订单列表弹窗使用。
///
Task> QueryOrderRowsAsync(ShopRevenueStatisticsQuery query, ShopOrderDetailType detailType);
///
/// 把某笔买手订单标记为退款成功(订单列表弹窗“退款成功”按钮,仅买手未退回金额的订单使用)。
///
/// 买手退款表ID(,即表格当前行的 id)
Task UpdateBuyerRefundStatusAsync(long id);
Task UpdateBuyerAfterOrderRemark(long id, string remark);
}
///
/// 统计聚合器:把接口返回的 聚合成页面所需的汇总/趋势/占比/表格数据。
/// 数据来源:shop_revenue_statistics/GetShopStats(GroupType = Day/Week/Month),
/// 接口只返回“按周期聚合的记录”,页面上所有汇总口径都在这里由客户端合计。
/// 口径说明(依据接口字段含义):
/// - OrderCount 为“有效订单数”,已不含退款订单;因此退款率分母 = OrderCount + RefundOrderCount。
/// - 店铺端退款:RefundDone(已成功)+ RefundPending(处理中)→ 退款金额。
/// - 买手端(采购)退款:TotalRefund = DoneRefund + PendingRefund + RejectedRefund → 售后采购金额;
/// DoneRefund → 已退回采购金额;剩余(待退回)= TotalRefund - DoneRefund。
/// - 接口无“达人佣金”字段,Commission 固定为 0(保留属性与页面展示,待后端补字段)。
/// - 金额字段单位均为“分”,除以 100 转为元。
///
public static class ShopStatsAggregator
{
/// 金额换算:分 → 元
private const decimal FenPerYuan = 100m;
/// 分转元
public static decimal ToYuan(long fen)
{
return fen / FenPerYuan;
}
private static List ToList(IEnumerable stats)
{
return (stats ?? Enumerable.Empty())
.Where(a => a != null)
.ToList();
}
private static IEnumerable> GroupByShop(IEnumerable stats)
{
return ToList(stats).GroupBy(a => a.ShopId ?? string.Empty);
}
/// 店铺显示名:优先按 ERP 店铺ID匹配,其次按平台店铺ID匹配,都匹配不到时退回店铺ID
public static string ResolveShopName(string shopId)
{
if (string.IsNullOrEmpty(shopId))
return "未知店铺";
var shop = GlobalConfig.ShopLoginList.FirstOrDefault(a => a.ErpShopId == shopId)
?? GlobalConfig.ShopLoginList.FirstOrDefault(a => a.ShopId == shopId);
if (shop == null || string.IsNullOrEmpty(shop.ShopAccount))
return shopId;
return shop.ShopAccount;
}
/// 汇总卡片(整个查询时间段,所有选中店铺合计)
public static ShopRevenueSummary GetSummary(IEnumerable stats)
{
var list = ToList(stats);
decimal salesAmount = ToYuan(list.Sum(a => a.SalesAmount));
decimal profit = ToYuan(list.Sum(a => a.ProfitAmount));
decimal platformFeeTotal = ToYuan(list.Sum(a => a.PlatformFee));
decimal refundAmount = ToYuan(list.Sum(a => a.RefundDone) + list.Sum(a => a.RefundPending));
decimal buyerPaymentAmount = ToYuan(list.Sum(a => a.TotalPurchase));
decimal buyerRefundAmount = ToYuan(list.Sum(a => a.TotalRefund));
decimal buyerRefundSuccessedAmount = ToYuan(list.Sum(a => a.DoneRefund));
int validOrderCount = list.Sum(a => a.OrderCount); // 新接口 OrderCount 不含退款订单
int refundOrderCount = list.Sum(a => a.RefundOrderCount);
int totalOrderCount = validOrderCount + refundOrderCount;
return new ShopRevenueSummary
{
SalesAmount = salesAmount,
ValidOrderCount = validOrderCount,
Profit = profit,
RefundAmount = refundAmount,
RefundOrderCount = refundOrderCount,
PlatformFeeTotal = platformFeeTotal,
Commission = 0m, // 新接口无“达人佣金”字段,保留占位
AvgOrderAmount = validOrderCount == 0 ? 0m : salesAmount / validOrderCount,
RefundRate = totalOrderCount == 0 ? 0m : (decimal)refundOrderCount * 100m / totalOrderCount,
AverageProfitRate = buyerPaymentAmount == 0m ? 0m : profit / buyerPaymentAmount * 100m,
BuyerPaymentAmount = buyerPaymentAmount,
BuyerRefundAmount = buyerRefundAmount,
BuyerRefundSuccessedAmount = buyerRefundSuccessedAmount,
};
}
/// 店铺统计表格(按店铺汇总,销售额倒序)
public static List GetShopStatistics(IEnumerable stats)
{
return GroupByShop(stats)
.Select(g => BuildShopStatisticsItem(g.Key, g))
.OrderByDescending(i => i.SalesAmount)
.ToList();
}
/// 店铺销售占比(饼图,按店铺汇总,销售额倒序)
public static List GetShopSalesProportion(IEnumerable stats)
{
var items = GroupByShop(stats)
.Select(g => new ShopSalesProportionItem
{
ShopName = ResolveShopName(g.Key),
SalesAmount = ToYuan(g.Sum(a => a.SalesAmount))
})
.OrderByDescending(i => i.SalesAmount)
.ToList();
decimal total = items.Sum(i => i.SalesAmount);
foreach (var item in items)
{
item.Percentage = total == 0m ? 0d : (double)(item.SalesAmount / total * 100m);
}
return items;
}
///
/// 销售趋势(按天,缺失日期补 0)。
/// 数据来自 GetShopStatsTrend:服务端已经跨店铺合计,这里只做日期规整与补零。
///
public static List GetSalesTrend(IEnumerable trend, DateTime startDate, DateTime endDate)
{
var list = (trend ?? Enumerable.Empty())
.Where(a => a != null && a.StatDate != default(DateTime))
.ToList();
var map = list
.Where(a => a.StatDate.Date >= startDate.Date && a.StatDate.Date <= endDate.Date)
.GroupBy(a => a.StatDate.Date)
.ToDictionary(
g => g.Key,
g => new RevenueTrendPoint
{
Date = g.Key,
SalesAmount = ToYuan(g.Sum(a => a.SalesAmount)),
OrderCount = g.Sum(a => a.OrderCount)
});
var result = new List();
for (var d = startDate.Date; d <= endDate.Date; d = d.AddDays(1))
{
if (map.TryGetValue(d, out var point))
result.Add(point);
else
result.Add(new RevenueTrendPoint { Date = d, SalesAmount = 0m, OrderCount = 0 });
}
return result;
}
///
/// 天/周/月周期统计表格(每天/每周/每月各一行,无数据的周期也补一行,金额单位:元)。
/// 结果按周期倒序(最新的周期在最上面)。
/// 说明:接口按“自然周期”聚合(周从周一起、月从 1 号起),所以筛选也按周期起点比较,
/// 否则区间从周三开始时,那一周(周一起)的数据会被误判为越界而丢掉。
///
public static List GetPeriodStatistics(IEnumerable stats, DateTime startDate, DateTime endDate, PeriodStatisticType periodType)
{
var startPeriod = GetPeriodStart(startDate, periodType);
var endPeriod = GetPeriodStart(endDate, periodType);
var map = ToList(stats)
.Where(a => a.StatDate != default(DateTime))
.Select(a => new { Stat = a, PeriodStart = GetPeriodStart(a.StatDate, periodType) })
.Where(a => a.PeriodStart >= startPeriod && a.PeriodStart <= endPeriod)
.GroupBy(a => a.PeriodStart)
.ToDictionary(g => g.Key, g => BuildPeriodStatisticsItem(g.Key, g.Select(a => a.Stat), periodType));
// 按周期补齐整段时间:无数据的周期也生成一行(全 0)
var result = new List();
foreach (var periodStart in EnumeratePeriods(startDate, endDate, periodType))
{
if (map.TryGetValue(periodStart, out var item))
result.Add(item);
else
result.Add(new ShopPeriodStatisticsItem
{
PeriodStart = periodStart,
Period = GetPeriodText(periodStart, periodType),
OrderCount = 0,
SalesAmount = 0m,
BuyerPaymentAmount = 0m,
Profit = 0m,
RefundAmount = 0m,
PlatformFeeTotal = 0m,
Commission = 0m,
AverageProfitRate = 0m,
CustomerCount = 0,
AvgOrderAmount = 0m
});
}
// 倒序排列:最新的周期在最上面
result.Reverse();
return result;
}
/// 店铺统计行(金额单位:元)
private static ShopStatisticsItem BuildShopStatisticsItem(string shopId, IEnumerable shopStats)
{
var list = ToList(shopStats);
decimal salesAmount = ToYuan(list.Sum(a => a.SalesAmount));
decimal profit = ToYuan(list.Sum(a => a.ProfitAmount));
decimal buyerPaymentAmount = ToYuan(list.Sum(a => a.TotalPurchase));
int orderCount = list.Sum(a => a.OrderCount);
return new ShopStatisticsItem
{
ShopName = ResolveShopName(shopId),
OrderCount = orderCount,
SalesAmount = salesAmount,
BuyerPaymentAmount = buyerPaymentAmount,
Profit = profit,
RefundAmount = ToYuan(list.Sum(a => a.RefundDone) + list.Sum(a => a.RefundPending)),
PlatformFeeTotal = ToYuan(list.Sum(a => a.PlatformFee)),
Commission = 0m, // 新接口无“达人佣金”字段,保留占位
AvgOrderAmount = orderCount == 0 ? 0m : salesAmount / orderCount,
AverageProfitRate = buyerPaymentAmount == 0m ? 0m : profit / buyerPaymentAmount * 100m
};
}
/// 周期统计行(金额单位:元)
private static ShopPeriodStatisticsItem BuildPeriodStatisticsItem(DateTime periodStart, IEnumerable periodStats, PeriodStatisticType periodType)
{
var list = ToList(periodStats);
decimal salesAmount = ToYuan(list.Sum(a => a.SalesAmount));
decimal profit = ToYuan(list.Sum(a => a.ProfitAmount));
decimal buyerPaymentAmount = ToYuan(list.Sum(a => a.TotalPurchase));
int orderCount = list.Sum(a => a.OrderCount);
return new ShopPeriodStatisticsItem
{
PeriodStart = periodStart,
Period = GetPeriodText(periodStart, periodType),
OrderCount = orderCount,
SalesAmount = salesAmount,
BuyerPaymentAmount = buyerPaymentAmount,
Profit = profit,
RefundAmount = ToYuan(list.Sum(a => a.RefundDone) + list.Sum(a => a.RefundPending)),
PlatformFeeTotal = ToYuan(list.Sum(a => a.PlatformFee)),
Commission = 0m, // 新接口无“达人佣金”字段,保留占位
AverageProfitRate = buyerPaymentAmount == 0m ? 0m : profit / buyerPaymentAmount * 100m,
CustomerCount = orderCount,
AvgOrderAmount = orderCount == 0 ? 0m : salesAmount / orderCount
};
}
/// 枚举 startDate~endDate 覆盖的所有周期开始日期(天/周/月)
private static IEnumerable EnumeratePeriods(DateTime startDate, DateTime endDate, PeriodStatisticType periodType)
{
var start = GetPeriodStart(startDate, periodType);
var end = GetPeriodStart(endDate, periodType);
switch (periodType)
{
case PeriodStatisticType.Week:
for (var d = start; d <= end; d = d.AddDays(7))
yield return d;
break;
case PeriodStatisticType.Month:
for (var d = start; d <= end; d = d.AddMonths(1))
yield return d;
break;
default:
for (var d = start; d <= end; d = d.AddDays(1))
yield return d;
break;
}
}
/// 周期开始日期:天=当天;周=周一起始;月=当月1号
private static DateTime GetPeriodStart(DateTime time, PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
var d = time.Date;
int diff = d.DayOfWeek == DayOfWeek.Sunday ? 6 : (int)d.DayOfWeek - 1;
return d.AddDays(-diff);
case PeriodStatisticType.Month:
return new DateTime(time.Year, time.Month, 1);
default:
return time.Date;
}
}
/// 周期显示文本:天=yyyy-MM-dd;周=yyyy-MM-dd ~ yyyy-MM-dd;月=yyyy-MM
private static string GetPeriodText(DateTime periodStart, PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return periodStart.ToString("yyyy-MM-dd") + " ~ " + periodStart.AddDays(6).ToString("yyyy-MM-dd");
case PeriodStatisticType.Month:
return periodStart.ToString("yyyy-MM");
default:
return periodStart.ToString("yyyy-MM-dd");
}
}
}
///
/// 默认实现:请求新接口(无缓存,每次查询实时获取)。
/// shop_revenue_statistics/GetShopStats
/// -> Result<Dictionary<店铺ID, List<ShopStatsResponse>>>
/// 请求体:UserLogin.GetSignPostData(new { StartDate, EndDate, ShopIds, GroupType });
/// GroupType 决定聚合粒度(0=汇总 1=天 2=周 3=月),汇总时每个店铺只有一条。
/// shop_revenue_statistics/GetShopStatsTrend
/// -> Result<List<ShopStatsTrendResponse>>
/// 请求体:UserLogin.GetSignPostData(new { StartDate, EndDate, ShopIds });
/// 服务端跨店铺合计,固定按天返回,用于销售趋势折线图。
/// shop_revenue_statistics/GetShopOrderDetails
/// -> Result<List<ShopOrderDetailResponse>>
/// 请求体:UserLogin.GetSignPostData(new { StartDate, EndDate, ShopIds, OrderType });
/// 订单列表弹窗的明细数据(OrderType 见 ShopOrderDetailType)。
/// 接口不返回“整段汇总”,页面上的汇总卡片一律由客户端对返回记录合计。
/// 接入其他数据源时,新建实现类并注入即可(见 IShopRevenueStatisticsService 注释)。
///
public class ShopRevenueStatisticsService : IShopRevenueStatisticsService
{
/// 统计接口地址(聚合粒度由请求参数 GroupType 决定)
private const string GetShopStatsApi = "shop_revenue_statistics/GetShopStats";
/// 销售趋势接口地址(服务端跨店铺合计,固定按天)
private const string GetShopStatsTrendApi = "shop_revenue_statistics/GetShopStatsTrend";
/// 订单明细接口地址(订单列表弹窗用)
private const string GetShopOrderDetailsApi = "shop_revenue_statistics/GetShopOrderDetails";
/// 更新买手订单退款状态接口地址(id 通过 URL 参数传递)
private const string UpdateBuyerRefundStatusApi = "buyer/UpdateBuyerRefundStatsu";
/// Unix 纪元(本地时区),用于解析以时间戳作为 Key 的返回数据
private static readonly DateTime UnixEpoch = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
public async Task QueryAsync(ShopRevenueStatisticsQuery query)
{
// 综合统计页分两次取数,各取所需:
// 1) Summary:每店铺一条(与区间长度无关),作为卡片/店铺统计表/销售占比的权威合计
// 2) Trend :跨店铺合计的按天序列,只给趋势折线图用(数据量只与天数有关,不随店铺数膨胀)
// 顺序请求:签名中的 time/code 由当前时间生成,并发请求有被判重复签名的风险。
var summaryStats = await RequestStatsAsync(query, ShopStatsGroupType.Summary);
var trend = await RequestTrendAsync(query);
return new ShopRevenueStatisticsResult
{
Summary = ShopStatsAggregator.GetSummary(summaryStats),
Trend = ShopStatsAggregator.GetSalesTrend(trend, query.StartDate, query.EndDate),
ShopProportions = ShopStatsAggregator.GetShopSalesProportion(summaryStats),
ShopStatistics = ShopStatsAggregator.GetShopStatistics(summaryStats),
};
}
public async Task QueryPeriodStatisticsAsync(ShopRevenueStatisticsQuery query, PeriodStatisticType periodType)
{
// 按天/周/月粒度取数;接口不返回整段汇总,卡片由返回的周期记录在客户端合计
var stats = await RequestStatsAsync(query, ToGroupType(periodType));
return new ShopPeriodStatisticsResult
{
Summary = ShopStatsAggregator.GetSummary(stats),
Items = ShopStatsAggregator.GetPeriodStatistics(stats, query.StartDate, query.EndDate, periodType),
};
}
/// 页面统计周期 → 接口聚合方式
private static ShopStatsGroupType ToGroupType(PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return ShopStatsGroupType.Week;
case PeriodStatisticType.Month:
return ShopStatsGroupType.Month;
default:
return ShopStatsGroupType.Day;
}
}
///
/// 请求 GetShopStats:返回按 GroupType 聚合的记录
/// (汇总时每店铺一条;天/周/月时每店铺每个自然日/自然周/自然月一条)。
///
private static async Task> RequestStatsAsync(ShopRevenueStatisticsQuery query, ShopStatsGroupType groupType)
{
var shopIds = BuildShopIds(query);
if (shopIds.Count == 0)
return new List();
var url = BaseClass.Build29WxObjString(GetShopStatsApi);
var html = await GetHtml.PostJsonAsync(url, BuildPostData(query, shopIds, groupType));
var ret = Deserialize>>>(html);
EnsureSuccess(ret);
var result = new List();
if (ret.Data == null)
return result;
foreach (var kv in ret.Data)
result.AddRange(Normalize(kv.Key, kv.Value));
return result;
}
///
/// 请求 GetShopStatsTrend:跨店铺合计的按天销售趋势(服务端已合计,不需要 GroupType)。
///
private static async Task> RequestTrendAsync(ShopRevenueStatisticsQuery query)
{
var shopIds = BuildShopIds(query);
if (shopIds.Count == 0)
return new List();
var url = BaseClass.Build29WxObjString(GetShopStatsTrendApi);
var html = await GetHtml.PostJsonAsync(url, BuildTrendPostData(query, shopIds));
var ret = Deserialize>>(html);
EnsureSuccess(ret);
return ret.Data ?? new List();
}
///
/// 组装请求体:StartDate / EndDate 传日期,ShopIds 传 ERP 店铺ID 列表,
/// GroupType 传枚举数字(0=汇总 1=天 2=周 3=月)。
///
private static object BuildPostData(ShopRevenueStatisticsQuery query, List shopIds, ShopStatsGroupType groupType)
{
return UserLogin.GetSignPostData(new
{
StartDate = query.StartDate.Date,
EndDate = query.EndDate.Date,
ShopIds = shopIds,
GroupType = (int)groupType,
});
}
/// 组装趋势接口请求体:StartDate / EndDate + ShopIds(无 GroupType)
private static object BuildTrendPostData(ShopRevenueStatisticsQuery query, List shopIds)
{
return UserLogin.GetSignPostData(new
{
StartDate = query.StartDate.Date,
EndDate = query.EndDate.Date,
ShopIds = shopIds,
});
}
///
/// 组装订单明细请求体:StartDate / EndDate + ShopIds + OrderType
/// (OrderType 传枚举数字:1=有效订单 2=退款订单 3=买手未退回金额的订单)。
///
private static object BuildOrderDetailPostData(ShopRevenueStatisticsQuery query, List shopIds, ShopOrderDetailType detailType)
{
return UserLogin.GetSignPostData(new
{
StartDate = query.StartDate,
EndDate = query.EndDate,
ShopIds = shopIds,
OrderType = (int)detailType,
});
}
///
/// 查询店铺范围:query.ShopIds 为空表示全部店铺(取全部店铺的 ERP 店铺ID)。
/// 接口的 ShopIds 使用 ShopLoginInfo.ErpShopId。
///
private static List BuildShopIds(ShopRevenueStatisticsQuery query)
{
var selected = (query == null || query.ShopIds == null)
? new List()
: query.ShopIds.Where(a => !string.IsNullOrEmpty(a)).Distinct().ToList();
if (selected.Count > 0)
return selected;
return GetShopOptions()
.Select(a => a.ErpShopId)
.Where(a => !string.IsNullOrEmpty(a))
.Distinct()
.ToList();
}
///
/// 反序列化接口返回;失败时统一抛出可读异常。
/// 说明:Newtonsoft 默认按属性名匹配(忽略大小写),因此 PascalCase / camelCase 均可解析。
///
private static T Deserialize(string html)
{
try
{
return JsonConvert.DeserializeObject(html);
}
catch (Exception ex)
{
throw new Exception("查询失败:接口返回数据解析异常", ex);
}
}
/// 校验接口返回码
private static void EnsureSuccess(Result result)
{
if (result == null)
throw new Exception("查询失败:接口无返回数据");
if (result.Code != 200)
throw new Exception(string.IsNullOrEmpty(result.Msg) ? "查询失败" : result.Msg);
}
///
/// 摊平某个 Key(店铺ID)下的记录,并用 Key 兜底补齐缺失的 ShopId / StatDate。
///
private static List Normalize(string key, List items)
{
var result = new List();
if (items == null)
return result;
foreach (var item in items)
{
if (item == null)
continue;
if (string.IsNullOrEmpty(item.ShopId))
item.ShopId = key;
if (item.StatDate == default(DateTime))
{
var date = ParseDateFromKey(key);
if (date.HasValue)
item.StatDate = date.Value;
}
result.Add(item);
}
return result;
}
/// 尝试把返回数据的 Key 解析为日期(支持 yyyy-MM-dd / yyyyMMdd / 秒或毫秒时间戳)
private static DateTime? ParseDateFromKey(string key)
{
if (string.IsNullOrEmpty(key))
return null;
if (DateTime.TryParse(key, out var date))
return date;
if (DateTime.TryParseExact(key, "yyyyMMdd", System.Globalization.CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.None, out date))
return date;
if (long.TryParse(key, out var timestamp))
{
return timestamp.ToString().Length == 13
? UnixEpoch.AddMilliseconds(timestamp)
: UnixEpoch.AddSeconds(timestamp);
}
return null;
}
/// 店铺下拉列表(同步读取,供查询与过滤使用)
private static List GetShopOptions()
{
return GlobalConfig.ShopLoginList.Select(a =>
new ShopOption()
{
ShopId = a.ShopId,
ErpShopId = a.ErpShopId,
ShopName = a.ShopAccount
}).ToList();
}
public Task> GetShopOptionsAsync()
{
return Task.FromResult(GetShopOptions());
}
///
/// 订单明细列表(有效订单 / 退款订单 / 买手未退回金额的订单),供订单列表弹窗使用。
/// 请求 shop_revenue_statistics/GetShopOrderDetails:
/// StartDate / EndDate / ShopIds / OrderType(OrderType 由 detailType 决定,按数字传值)。
///
public async Task> QueryOrderRowsAsync(ShopRevenueStatisticsQuery query, ShopOrderDetailType detailType)
{
var shopIds = BuildShopIds(query);
if (shopIds.Count == 0)
return new List();
var url = BaseClass.Build29WxObjString(GetShopOrderDetailsApi);
var html = await GetHtml.PostJsonAsync(url, BuildOrderDetailPostData(query, shopIds, detailType));
var ret = Deserialize>>(html);
EnsureSuccess(ret);
return ret.Data ?? new List();
}
///
/// 请求 buyer/UpdateBuyerRefundStatsu 更新买手订单退款状态:
/// id 拼在 URL 查询参数上(Build29WxObjString 生成的地址已带 ?obj=…,这里用 &id= 追加);
/// 请求体走 UserLogin.GetSignPostData 常规签名(time/code 等)。
/// 返回码非 200 时抛出异常,由调用方提示。
///
public async Task UpdateBuyerRefundStatusAsync(long id)
{
if (id == 0)
throw new Exception("更新失败:缺少买手订单ID");
var url = BaseClass.Build29WxObjString(UpdateBuyerRefundStatusApi) + "&id=" + id;
var html = await GetHtml.PostJsonAsync(url, UserLogin.GetSignPostData(new { }));
var ret = Deserialize>(html);
EnsureSuccess(ret);
return ret.Data;
}
public async Task UpdateBuyerAfterOrderRemark(long id, string remark)
{
if (id == 0)
throw new Exception("更新失败:缺少买手订单ID");
var url = BaseClass.Build29WxObjString("buyer/UpdateBuyerAfterRemark");
var html = await GetHtml.PostJsonAsync(url, UserLogin.GetSignPostData(new
{
id = id,
remark
}));
var ret = Deserialize>(html);
EnsureSuccess(ret);
return ret.Data;
}
}
}