利润统计 增加快递费筛选

快递费 页面支持导出全部
This commit is contained in:
chfei committed 2026-11-11 18:24:46 +08:00
1 parent 71614f64e4
commit 78a64aa661
10 files changed
+268 -126

No files matched your search

@@ -23,6 +23,10 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
/// 空/Null 表示全部店铺(服务层会取全部店铺的 ErpShopId)。
/// </summary>
public List<string> ShopIds { get; set; }
/// <summary>
/// 是否统计快递费用
/// </summary>
public bool StatPackages { get; set; }
}
/// <summary>
@@ -111,6 +115,10 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
/// 被拒绝金额(分)
/// </summary>
public long RejectedRefund { get; set; }
/// <summary>
/// 包裹费用
/// </summary>
public long PackageAmount { get; set; }
}
/// <summary>
@@ -199,6 +207,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
/// </summary>
public int UnrefundedAmount { get; set; }
public long PackageAmount { get; set; }
/// <summary>
/// 买手订单信息(一个订单号对应多个买手订单)
/// </summary>
@@ -288,6 +297,10 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
public decimal BuyerRefundSuccessedAmount { get; set; }
/// <summary>买手号剩余退款金额(尚未成功退款的金额,元)</summary>
public decimal BuyerRefundRemainingAmount => BuyerRefundAmount - BuyerRefundSuccessedAmount;
/// <summary>
/// 包裹费用
/// </summary>
public decimal PackageAmount { get; set; }
}
/// <summary>
@@ -355,6 +368,10 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Models
/// <summary>平均利润率(利润 / 采购金额,百分数,如 12.34 表示 12.34%)</summary>
public decimal AverageProfitRate { get; set; }
/// <summary>
/// 包裹费用
/// </summary>
public decimal PackageAmount { get; set; }
}
/// <summary>
@@ -6,6 +6,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:_29WxOrderWpf.Ctrls.Statistics.ViewModels"
xmlns:wpf="WPFCommon"
d:DataContext="{d:DesignInstance Type=vm:ShopRevenueStatisticsViewModel,
IsDesignTimeCreatable=True}"
mc:Ignorable="d">
@@ -48,9 +49,9 @@
Width="110"
Height="32"
VerticalContentAlignment="Center"
BorderThickness="1"
DisplayMemberPath="Text"
IsReadOnly="True"
BorderThickness="1"
ItemsSource="{Binding TimeRangeOptions}"
SelectedItem="{Binding SelectedTimeRange, Mode=TwoWay}"
Style="{StaticResource RoundedComboBox}" />
@@ -60,9 +61,10 @@
FontSize="14"
Foreground="#666666"
Text="日期" />
<DatePicker BorderBrush="{StaticResource pageColorBrush}"
<DatePicker
Height="32"
VerticalContentAlignment="Center"
BorderBrush="{StaticResource pageColorBrush}"
SelectedDate="{Binding StartDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock
Margin="6,0"
@@ -70,9 +72,10 @@
FontSize="14"
Foreground="#666666"
Text="~" />
<DatePicker BorderBrush="{StaticResource pageColorBrush}"
<DatePicker
Height="32"
VerticalContentAlignment="Center"
BorderBrush="{StaticResource pageColorBrush}"
SelectedDate="{Binding EndDate, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock
Margin="14,0,6,0"
@@ -95,6 +98,14 @@
ItemsSource="{Binding ShopOptions}"
SelectedItem="{Binding SelectedShop, Mode=TwoWay}"
Style="{StaticResource BaseComboBoxStyle}" />-->
<wpf:TitleContent
Title="去除快递费"
Margin="10,0,0,0"
VerticalContentAlignment="Center"
Placement="Left"
TitleVerticalAlignment="Center">
<CheckBox IsChecked="{Binding StatPackages}" Style="{StaticResource ToggleSwitch}" />
</wpf:TitleContent>
<Button
Height="32"
Margin="18,0,0,0"
@@ -108,7 +108,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
decimal buyerPaymentAmount = ToYuan(list.Sum(a => a.TotalPurchase));
decimal buyerRefundAmount = ToYuan(list.Sum(a => a.TotalRefund));
decimal buyerRefundSuccessedAmount = ToYuan(list.Sum(a => a.DoneRefund));
decimal packageAmount = ToYuan(list.Sum(a => a.PackageAmount));
int validOrderCount = list.Sum(a => a.OrderCount); // 新接口 OrderCount 不含退款订单
int refundOrderCount = list.Sum(a => a.RefundOrderCount);
int totalOrderCount = validOrderCount + refundOrderCount;
@@ -128,6 +128,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
BuyerPaymentAmount = buyerPaymentAmount,
BuyerRefundAmount = buyerRefundAmount,
BuyerRefundSuccessedAmount = buyerRefundSuccessedAmount,
PackageAmount = packageAmount
};
}
@@ -248,7 +249,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
decimal profit = ToYuan(list.Sum(a => a.ProfitAmount));
decimal buyerPaymentAmount = ToYuan(list.Sum(a => a.TotalPurchase));
int orderCount = list.Sum(a => a.OrderCount);
decimal packageAmount = ToYuan(list.Sum(a => a.PackageAmount));
return new ShopStatisticsItem
{
ShopName = ResolveShopName(shopId),
@@ -260,7 +261,8 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
PlatformFeeTotal = ToYuan(list.Sum(a => a.PlatformFee)),
Commission = 0m, // 新接口无“达人佣金”字段,保留占位
AvgOrderAmount = orderCount == 0 ? 0m : salesAmount / orderCount,
AverageProfitRate = buyerPaymentAmount == 0m ? 0m : profit / buyerPaymentAmount * 100m
AverageProfitRate = buyerPaymentAmount == 0m ? 0m : profit / buyerPaymentAmount * 100m,
PackageAmount = packageAmount
};
}
@@ -385,7 +387,13 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
// 顺序请求:签名中的 time/code 由当前时间生成,并发请求有被判重复签名的风险。
var summaryStats = await RequestStatsAsync(query, ShopStatsGroupType.Summary);
var trend = await RequestTrendAsync(query);
if (query.StatPackages)
{
foreach (var item in summaryStats)
{
item.ProfitAmount -= item.PackageAmount;
}
}
return new ShopRevenueStatisticsResult
{
Summary = ShopStatsAggregator.GetSummary(summaryStats),
@@ -399,7 +407,13 @@ namespace _29WxOrderWpf.Ctrls.Statistics.Services
{
// 按天/周/月粒度取数;接口不返回整段汇总,卡片由返回的周期记录在客户端合计
var stats = await RequestStatsAsync(query, ToGroupType(periodType));
if (query.StatPackages)
{
foreach (var item in stats)
{
item.ProfitAmount -= item.PackageAmount;
}
}
return new ShopPeriodStatisticsResult
{
Summary = ShopStatsAggregator.GetSummary(stats),
@@ -274,30 +274,32 @@
</TabItem>
</TabControl>
<!-- helper -->
<Border
Margin="0,20,30,0"
Padding="10"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Background="{StaticResource pageColorBrush}"
BorderThickness="1"
Cursor="Hand"
MouseLeftButtonDown="help_mouseLeftButtonDown">
<WrapPanel>
<TextBlock
Margin="0,0,10,0"
Foreground="White"
Style="{StaticResource TextBlockIcon}"
Text="{StaticResource txt-chat_help}" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Black"
Foreground="White"
Text="利润计算逻辑" />
</WrapPanel>
</Border>
<WrapPanel
Margin="0,20,30,0"
HorizontalAlignment="Right"
VerticalAlignment="Top">
<Border
Padding="10"
Background="{StaticResource pageColorBrush}"
BorderThickness="1"
Cursor="Hand"
MouseLeftButtonDown="help_mouseLeftButtonDown">
<WrapPanel>
<TextBlock
Margin="0,0,10,0"
Foreground="White"
Style="{StaticResource TextBlockIcon}"
Text="{StaticResource txt-chat_help}" />
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="Black"
Foreground="White"
Text="利润计算逻辑" />
</WrapPanel>
</Border>
</WrapPanel>
<!-- Popup 弹出层 -->
<Popup
x:Name="InfoPopup"
@@ -157,7 +157,11 @@
Binding="{Binding ProfitAmount, Converter={StaticResource FenToYuanConverter}, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="利润" />
<DataGridTextColumn
Width="*"
Binding="{Binding PackageAmount, Converter={StaticResource FenToYuanConverter}, StringFormat={}¥{0:N2}}"
ElementStyle="{StaticResource CenterCellTextStyle}"
Header="快递费" />
<!-- 买手订单:一笔买手订单占一行,不合并 -->
<DataGridTextColumn
Width="1.6*"
@@ -166,7 +166,10 @@ class ShopOrderDetailRow : BindableBaseEx
/// 利润
/// </summary>
public long ProfitAmount { get; set; }
/// <summary>
/// 包裹费用
/// </summary>
public long PackageAmount { get; set; }
/// <summary>
/// 平台佣金
/// </summary>
@@ -246,6 +249,7 @@ class ShopOrderDetailRow : BindableBaseEx
OrderPrice = item.OrderPrice,
OrderRefundAmount = item.OrderRefundAmount,
ProfitAmount = item.ProfitAmount,
PackageAmount = item.PackageAmount,
PlatformFee = item.PlatformFee,
OutAmount = item.OutAmount,
UnrefundedAmount = item.UnrefundedAmount,
@@ -411,7 +415,7 @@ class ShowOrderDialogViewModel : BindableBaseEx
bool includeRemark = _detailType == ShopOrderDetailType.BuyerUnrefunded;
// 表头
List<string> headers = ["店名", "订单", "创建时间", "金额", "退款金额", "平台费用", "利润", "买手单号", "外采金额", "买手退款", "退款状态"];
List<string> headers = ["店名", "订单", "创建时间", "金额", "退款金额", "平台费用", "利润","快递费", "买手单号", "外采金额", "买手退款", "退款状态"];
if (includeRemark)
headers.Add("备注");
sb.AppendLine(string.Join("\t", headers));
@@ -428,6 +432,7 @@ class ShowOrderDialogViewModel : BindableBaseEx
(item.OrderRefundAmount / 100m).ToString("0.00"),
(item.PlatformFee / 100m).ToString("0.00"),
(item.ProfitAmount / 100m).ToString("0.00"),
(item.PackageAmount / 100m).ToString("0.00"),
CsvEscape(item.OutOrderSn),
(item.BuyerOutAmount / 100m).ToString("0.00"),
(item.BuyerRefundAmount / 100m).ToString("0.00"),
@@ -177,7 +177,11 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
#endregion
#region 查询条件
public bool StatPackages
{
get { return this.GetValue(x => x.StatPackages); }
set { this.SetValue(x => x.StatPackages, value); }
}
/// <summary>时间范围选项</summary>
public ObservableCollection<TimeRangeOption> TimeRangeOptions { get; }
@@ -455,20 +459,20 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
EndDate = now;
break;
case "week":
{
int diff = today.DayOfWeek == DayOfWeek.Sunday ? 6 : (int)today.DayOfWeek - 1;
StartDate = today.AddDays(-diff);
EndDate = now;
}
break;
{
int diff = today.DayOfWeek == DayOfWeek.Sunday ? 6 : (int)today.DayOfWeek - 1;
StartDate = today.AddDays(-diff);
EndDate = now;
}
break;
case "lastweek":
{
int diff = today.DayOfWeek == DayOfWeek.Sunday ? 6 : (int)today.DayOfWeek - 1;
var thisWeekStart = today.AddDays(-diff);
StartDate = thisWeekStart.AddDays(-7);
EndDate = thisWeekStart.AddDays(-1).AddHours(23).AddMinutes(59).AddSeconds(59);
}
break;
{
int diff = today.DayOfWeek == DayOfWeek.Sunday ? 6 : (int)today.DayOfWeek - 1;
var thisWeekStart = today.AddDays(-diff);
StartDate = thisWeekStart.AddDays(-7);
EndDate = thisWeekStart.AddDays(-1).AddHours(23).AddMinutes(59).AddSeconds(59);
}
break;
case "last2weeks":
StartDate = today.AddDays(-13);
EndDate = now;
@@ -482,12 +486,12 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
EndDate = now;
break;
case "lastmonth":
{
var firstOfThisMonth = new DateTime(today.Year, today.Month, 1);
StartDate = firstOfThisMonth.AddMonths(-1);
EndDate = firstOfThisMonth.AddDays(-1).AddHours(23).AddMinutes(59).AddSeconds(59);
}
break;
{
var firstOfThisMonth = new DateTime(today.Year, today.Month, 1);
StartDate = firstOfThisMonth.AddMonths(-1);
EndDate = firstOfThisMonth.AddDays(-1).AddHours(23).AddMinutes(59).AddSeconds(59);
}
break;
case "last3months":
StartDate = new DateTime(today.Year, today.Month, 1).AddMonths(-2);
EndDate = now;
@@ -541,6 +545,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
StartDate = StartDate,
EndDate = EndDate,
ShopIds = BuildSelectedShopIds(),
StatPackages = StatPackages
};
}
@@ -61,6 +61,11 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
InitializeAsync();
}
public bool StatPackages
{
get { return this.GetValue(x => x.StatPackages); }
set { this.SetValue(x => x.StatPackages, value); }
}
#region 查询条件
@@ -288,6 +293,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
StartDate = StartDate,
EndDate = EndDate,
ShopIds = BuildSelectedShopIds(),
StatPackages = StatPackages,
};
}
@@ -298,7 +304,7 @@ namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
try
{
var result = await _service.QueryAsync(BuildQuery());
Summary = result.Summary;
TrendData = new ObservableCollection<RevenueTrendPoint>(result.Trend ?? new List<RevenueTrendPoint>());
ShopProportionData = new ObservableCollection<ShopSalesProportionItem>(result.ShopProportions ?? new List<ShopSalesProportionItem>());
@@ -141,57 +141,75 @@ namespace Work._29Tools.Forms.UserForms
}
private List<user_payment_record> paymentList;
public async void GetBalanceList()
private string GetBalanceFilters()
{
int pageCount = 1;
int pageSize = 50;
var data3 = "&user_name_in=" + BaseClass.UserName;
data3 += "&create_time_begin=" + dtpTimeBegin.Value.ToString("yyyy-MM-dd HH:mm:ss");
data3 += "&create_time_end=" + dtpTimeEnd.Value.ToString("yyyy-MM-dd HH:mm:ss");
if (cmbType.Text == "充值")
data3 += "&type_in=1,30";
else if (cmbType.Text == "管理员充值")
data3 += "&type_in=1";
else if (cmbType.Text == "支付宝充值")
data3 += "&type_in=30";
else if (cmbType.Text == "礼物单支出/礼物单退费")
data3 += "&type_in=16,34";
else if (cmbType.Text == "拍单扣除/作废返还")
data3 += "&type_in=20,23";
else if (cmbType.Text == "超重扣费支出")
data3 += "&type_in=24,28,33,37,41";
else if (cmbType.Text == "退货包裹扣费支出/返还")
data3 += "&type_in=26,38";
else if (cmbType.Text == "额外包裹扣费支出/返还")
data3 += "&type_in=35,36";
else if (cmbType.Text == "云仓退货扣费支出")
data3 += "&type_in=31";
if (!string.IsNullOrEmpty(txtOutOrderId.Text.Trim()))
data3 += "&order_id_in=" + txtOutOrderId.Text.Trim();
if (!string.IsNullOrEmpty(txtShopOrderNo.Text.Trim()))
data3 += "&remark_fulltext=" + txtShopOrderNo.Text.Trim();
return data3;
}
private static List<user_payment_record> ReadBalancePage(string filters, int page, int pageSize, out int count, out decimal seconds)
{
var url = BaseClass.Build29WxObjString("user/get_user_balance");
var data = UserLogin.GetSignaturex() + filters + "&page=" + page + "&page_size=" + pageSize;
var html = GetHtml.PostUtf8(url, data);
if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200"))
html = GetHtml.PostUtf8(url, data);
if (string.IsNullOrEmpty(html) || !html.Contains("\"code\":200"))
throw new InvalidOperationException($"第 {page} 页查询失败:" + (string.IsNullOrEmpty(html) ? "服务器无响应" : BaseRegex.GetStringJsonByRegs(ref html, html, "msg")));
var result = JObject.Parse(html);
count = (int)result["count"];
seconds = BaseRegex.GetDecimalJson(ref html, "seconds");
return JsonConvert.DeserializeObject<List<user_payment_record>>(result["data"]?.ToString() ?? "null") ?? throw new InvalidOperationException($"第 {page} 页缺少消费记录");
}
public void GetBalanceList()
{
int page = 1, pageSize = 50;
string filters = null;
Invoke(new Action(() =>
{
this.dgvPayment.Rows.Clear();
}));
var url3 = BaseClass.Build29WxObjString("user/get_user_balance");
var data3 = UserLogin.GetSignaturex();
Invoke(new Action(() =>
{
data3 += "&user_name_in=" + BaseClass.UserName;
data3 += "&create_time_begin=" + dtpTimeBegin.Value.ToString("yyyy-MM-dd HH:mm:ss");
data3 += "&create_time_end=" + dtpTimeEnd.Value.ToString("yyyy-MM-dd HH:mm:ss");
if (cmbType.Text == "充值")
data3 += "&type_in=1,30";
else if (cmbType.Text == "管理员充值")
data3 += "&type_in=1";
else if (cmbType.Text == "支付宝充值")
data3 += "&type_in=30";
else if (cmbType.Text == "礼物单支出/礼物单退费")
data3 += "&type_in=16,34";
else if (cmbType.Text == "拍单扣除/作废返还")
data3 += "&type_in=20,23";
else if (cmbType.Text == "超重扣费支出")
data3 += "&type_in=24,28,33,37,41";
else if (cmbType.Text == "退货包裹扣费支出/返还")
data3 += "&type_in=26,38";
else if (cmbType.Text == "额外包裹扣费支出/返还")
data3 += "&type_in=35,36";
else if (cmbType.Text == "云仓退货扣费支出")
data3 += "&type_in=31";
if (!string.IsNullOrEmpty(txtOutOrderId.Text.Trim()))
data3 += "&order_id_in=" + txtOutOrderId.Text.Trim();
if (!string.IsNullOrEmpty(txtShopOrderNo.Text.Trim()))
data3 += "&remark_fulltext=" + txtShopOrderNo.Text.Trim();
data3 += "&page=" + ucSplitPage1.CurrentPage;
data3 += "&page_size=" + ucSplitPage1.PageSize;
paymentList = null;
filters = GetBalanceFilters();
page = ucSplitPage1.CurrentPage;
pageSize = ucSplitPage1.PageSize;
}));
var html3 = GetHtml.PostUtf8(url3, data3);
if (string.IsNullOrEmpty(html3) || !html3.Contains("\"code\":200"))
html3 = GetHtml.PostUtf8(url3, data3);
if (string.IsNullOrEmpty(html3) || !html3.Contains("\"code\":200"))
int count;
decimal seconds;
try
{
BaseMsgForm.ShowError("查询失败:" + BaseRegex.GetStringJsonByRegs(ref html3, html3, "msg"));
paymentList = ReadBalancePage(filters, page, pageSize, out count, out seconds);
}
catch (Exception ex)
{
BaseMsgForm.ShowError("查询失败:" + ex.Message);
return;
}
paymentList = new List<user_payment_record>();
paymentList = JsonHelper.DeserializeJsonToList<user_payment_record>(JsonHelper.JObjectParse(html3)["data"].ParseToString());
foreach (var payment in paymentList)
{
if (!this.Created)
@@ -210,12 +228,11 @@ namespace Work._29Tools.Forms.UserForms
this.dgvPayment.Rows[rowIndex].Height = 60;
}));
}
int count = BaseRegex.GetIntJson(ref html3, "count");
pageCount = Math.Ceiling(1.0 * count / pageSize).ParseToInt();
int pageCount = Math.Ceiling(1.0 * count / pageSize).ParseToInt();
pageCount = Math.Max(pageCount, 1);
ucSplitPage1.TotalPage = pageCount;
#if DEBUG
Invoke(new Action(() => { statusBarUserControl1.lblTaskStatus.Text = BaseRegex.GetDecimalJson(ref html3, "seconds") + "秒"; }));
Invoke(new Action(() => { statusBarUserControl1.lblTaskStatus.Text = seconds + "秒"; }));
#endif
Invoke(new Action(() =>
{
@@ -275,7 +292,20 @@ namespace Work._29Tools.Forms.UserForms
private void exportRecord_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
{
if (paymentList != null)
var menu = new ContextMenuStrip();
menu.Items.Add("导出当页", null, async (s, args) => await ExportRecords(false)).Enabled = paymentList != null;
menu.Items.Add("导出全部", null, async (s, args) => await ExportRecords(true));
menu.Closed += (s, args) => BeginInvoke(new Action(menu.Dispose));
menu.Show(exportRecord, 0, exportRecord.Height);
}
private async Task ExportRecords(bool allPages)
{
var records = paymentList;
var filters = GetBalanceFilters();
int pageSize = ucSplitPage1.PageSize;
exportRecord.Enabled = false;
try
{
using (SaveFileDialog saveFileDialog = new SaveFileDialog())
{
@@ -285,40 +315,86 @@ namespace Work._29Tools.Forms.UserForms
saveFileDialog.RestoreDirectory = true;
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("类型,订单ID,金额,余额,创建时间,备注");
foreach (var item in paymentList)
if (allPages)
{
//var rowIndex = this.dgvPayment.Rows.Add(
// false,
// ((EnumOrderPaymentType)payment.type.ParseToInt()).ParseToString(),
// item.order_id,
// item.amount,
// item.balance,
// item.create_time.ToString("yyyy-MM-dd HH:mm:ss"),
// item.remark
//);
string type = "未知";
if (item.type.HasValue)
{
if (Enum.TryParse<EnumOrderPaymentType>(item.type.Value.ToString(), out EnumOrderPaymentType v))
{
type = v.ToString();
}
}
statusBarUserControl1.pgbTaskProgress.Maximum = 100;
statusBarUserControl1.InitProgressBar();
statusBarUserControl1.UpdateProgressBarStatus("正在读取第 1 页…");
records = await ReadAllBalancePages(filters, pageSize);
statusBarUserControl1.UpdateProgressBarStatus($"正在保存 {records.Count} 条记录…");
}
sb.AppendLine($"{type},{item.order_id},{item.amount},{item.balance},{item.create_time.ToString("yyyy-MM-dd HH:mm:ss")},{item.remark}");
var fileName = saveFileDialog.FileName;
await Task.Run(() => SaveBalanceRecords(fileName, records));
if (allPages && !IsDisposed)
{
statusBarUserControl1.UpdateProgressBar(100);
statusBarUserControl1.FinishProgressBar($"导出完成,共 {records.Count} 条记录");
}
File.WriteAllText(saveFileDialog.FileName, sb.ToString(), Encoding.UTF8);
MessageBox.Show("导出成功");
}
}
}
catch (Exception ex)
{
if (allPages && !IsDisposed)
statusBarUserControl1.UpdateProgressBarStatus("导出失败:" + ex.Message);
BaseMsgForm.ShowError("导出失败:" + ex.Message);
}
finally
{
if (!IsDisposed)
exportRecord.Enabled = true;
}
}
private static void SaveBalanceRecords(string fileName, List<user_payment_record> records)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("类型,订单ID,金额,余额,创建时间,备注");
foreach (var item in records)
{
string type = "未知";
if (item.type.HasValue)
{
if (Enum.TryParse<EnumOrderPaymentType>(item.type.Value.ToString(), out EnumOrderPaymentType v))
{
type = v.ToString();
}
}
sb.AppendLine($"{type},{item.order_id},{item.amount},{item.balance},{item.create_time.ToString("yyyy-MM-dd HH:mm:ss")},{item.remark}");
}
File.WriteAllText(fileName, sb.ToString(), Encoding.UTF8);
}
private async Task<List<user_payment_record>> ReadAllBalancePages(string filters, int pageSize)
{
var records = new List<user_payment_record>();
int expectedCount = 0;
for (int page = 1; ; page++)
{
int count = 0;
var batch = await Task.Run(() => ReadBalancePage(filters, page, pageSize, out count, out _));
if (page == 1)
expectedCount = count;
if (count != expectedCount || batch.Count != Math.Min(pageSize, expectedCount - records.Count))
throw new InvalidOperationException($"第 {page} 页记录数有变化,请重新导出。");
records.AddRange(batch);
int totalPages = Math.Max(1, (int)Math.Ceiling((double)expectedCount / pageSize));
statusBarUserControl1.UpdateProgressBar((int)(99L * page / totalPages));
statusBarUserControl1.UpdateProgressBarStatus($"已读取 {page}/{totalPages} 页,共 {records.Count}/{expectedCount} 条");
if (records.Count == expectedCount)
{
Debug.Assert(page == totalPages);
return records;
}
}
}
private async Task GetUserAmountSummary()
{
@@ -24,7 +24,9 @@ namespace WxOrder.Framework.Commons
public DialogWindow(Window owner = null)
{
InitializeComponent();
Owner = owner ?? Application.Current.MainWindow;
Owner = owner ?? Application.Current.Windows
.OfType<Window>()
.FirstOrDefault(w => w.IsActive) ?? Application.Current.MainWindow;
isConstructorSuccessful = true;
dialogWindowStyle = (Style)TryFindResource("DialogWindowStyle");
Loaded += DialogWindow_Loaded;