Files
29WxOrder/29WxOrderWpf/Ctrls/Statistics/ViewModels/ShopRevenueStatisticsViewModel.cs
T

384 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using WxOrder.Framework.Commons;
using WxOrder.Framework.Commons;
using _29WxOrderWpf.Ctrls.Statistics.Models;
using _29WxOrderWpf.Ctrls.Statistics.Services;
using AllBase.Model.MvvmBaseClasses;
using CefSharp;
using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using Work.BaseForms;
namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
{
/// <summary>
/// 店铺营收统计 - 页面 ViewModel(MVVM)。
/// 通过 IShopRevenueStatisticsService 调用数据接口;
/// 接入真实接口时传入自定义实现:new ShopRevenueStatisticsViewModel(你的服务实现)。
/// </summary>
public class ShopRevenueStatisticsViewModel : PropertyNotifyObject
{
private readonly IShopRevenueStatisticsService _service;
private ShopRevenueSummary _summary;
private ObservableCollection<RevenueTrendPoint> _trendData;
private ObservableCollection<ShopSalesProportionItem> _shopProportionData;
private ObservableCollection<ShopStatisticsItem> _shopStatistics;
private ObservableCollection<ShopOption> _selectedShops;
private string _selectedPlatform;
/// <summary>上一次真正生效(并据此计算过日期)的时间范围选项,避免同一个选项被重复应用</summary>
private TimeRangeOption _appliedTimeRange;
public ShopRevenueStatisticsViewModel()
: this(new ShopRevenueStatisticsService())
{
}
public ShopRevenueStatisticsViewModel(IShopRevenueStatisticsService service)
{
_service = service ?? new ShopRevenueStatisticsService();
TimeRangeOptions = new ObservableCollection<TimeRangeOption>
{
new TimeRangeOption { Text = "昨天", Value = "yesterday" },
new TimeRangeOption { Text = "本周", Value = "week" },
new TimeRangeOption { Text = "本月", Value = "month" },
new TimeRangeOption { Text = "最近10天", Value = "last10" },
new TimeRangeOption { Text = "自定义", Value = "custom" }
};
ShopOptions = new ObservableCollection<ShopOption>();
QueryCommand = new DelegateCommand(async () => await LoadAsync());
ResetCommand = new DelegateCommand(Reset);
InitializeAsync();
}
#region 查询条件
/// <summary>时间范围选项</summary>
public ObservableCollection<TimeRangeOption> TimeRangeOptions { get; }
/// <summary>选中的时间范围</summary>
public TimeRangeOption SelectedTimeRange
{
get { return this.GetValue(x => x.SelectedTimeRange); }
set
{
this.SetValue(x => x.SelectedTimeRange, value);
// 只有用户真正切换到“另一个”时间范围选项时才重新计算起止日期。
// 页签切换时下拉框会把同一个选项(甚至先 null 再回写)重放一遍,
// 若照样重算,就会把用户手工调过的日期覆盖掉。
if (value != null && !ReferenceEquals(_appliedTimeRange, value))
{
_appliedTimeRange = value;
ApplyTimeRange();
}
OnPropertyChanged(nameof(IsCustomDateRange));
}
}
/// <summary>是否为自定义时间范围</summary>
public bool IsCustomDateRange
{
get { return SelectedTimeRange != null && SelectedTimeRange.Value == "custom"; }
}
/// <summary>开始日期</summary>
public DateTime StartDate
{
get { return this.GetValue(x => x.StartDate); }
set { this.SetValue(x => x.StartDate, value); }
}
/// <summary>结束日期</summary>
public DateTime EndDate
{
get { return this.GetValue(x => x.EndDate); }
set { this.SetValue(x => x.EndDate, value); }
}
/// <summary>店铺选项(“全部店铺”由多选控件“全部商铺”表示)</summary>
public ObservableCollection<ShopOption> ShopOptions { get; }
/// <summary>选中的店铺</summary>
public ObservableCollection<ShopOption> SelectedShops
{
get { return _selectedShops; }
set
{
_selectedShops = value;
OnPropertyChanged(nameof(SelectedShops));
}
}
#endregion
#region 统计结果
/// <summary>汇总卡片数据</summary>
public ShopRevenueSummary Summary
{
get { return _summary; }
private set
{
_summary = value;
OnPropertyChanged(nameof(Summary));
}
}
/// <summary>销售趋势数据</summary>
public ObservableCollection<RevenueTrendPoint> TrendData
{
get { return _trendData; }
private set
{
_trendData = value;
OnPropertyChanged(nameof(TrendData));
}
}
/// <summary>店铺销售占比数据</summary>
public ObservableCollection<ShopSalesProportionItem> ShopProportionData
{
get { return _shopProportionData; }
private set
{
_shopProportionData = value;
OnPropertyChanged(nameof(ShopProportionData));
}
}
/// <summary>店铺统计表格数据</summary>
public ObservableCollection<ShopStatisticsItem> ShopStatistics
{
get { return _shopStatistics; }
private set
{
_shopStatistics = value;
OnPropertyChanged(nameof(ShopStatistics));
}
}
/// <summary>是否正在加载</summary>
public bool IsLoading
{
get { return this.GetValue(x => x.IsLoading); }
set { this.SetValue(x => x.IsLoading, value); }
}
#endregion
#region 命令
/// <summary>查询命令</summary>
public ICommand QueryCommand { get; }
/// <summary>重置命令</summary>
public ICommand ResetCommand { get; }
#endregion
#region 初始化
private async void InitializeAsync()
{
try
{
// 店铺选项(全部店铺由多选控件“全部商铺”表示,不再额外加入“全部店铺”项)
var shops = await _service.GetShopOptionsAsync();
foreach (var shop in shops)
ShopOptions.Add(shop);
SelectedShops = new ObservableCollection<ShopOption>(ShopOptions);
// 默认时间范围:本月
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == "month");
ApplyTimeRange();
await LoadAsync();
}
catch (Exception)
{
// 初始化失败(如接口不可用),页面保持可查询状态;可在此接入日志/提示
}
}
#endregion
#region 业务逻辑
/// <summary>根据时间范围选项计算起止日期</summary>
private void ApplyTimeRange()
{
var now = DateTime.Now;
var today = now.Date;
var selected = SelectedTimeRange == null ? string.Empty : SelectedTimeRange.Value;
switch (selected)
{
case "yesterday":
StartDate = today.AddDays(-1);
EndDate = today.AddDays(-1).AddHours(23).AddMinutes(59).AddSeconds(59);
break;
case "week":
int diff = today.DayOfWeek == DayOfWeek.Sunday ? 6 : (int)today.DayOfWeek - 1;
StartDate = today.AddDays(-diff);
EndDate = now;
break;
case "month":
StartDate = new DateTime(today.Year, today.Month, 1);
EndDate = now;
break;
case "last10":
StartDate = today.AddDays(-9);
EndDate = now;
break;
default:
// 自定义:保持用户选择的日期不变
break;
}
}
/// <summary>
/// 根据选中店铺生成查询用 ERP 店铺ID 列表(接口 ShopIds 使用 ShopLoginInfo.ErpShopId):
/// 未选/全选/选中“全部店铺”时返回空列表(表示全部店铺)。
/// </summary>
private List<string> BuildSelectedShopIds()
{
var selected = SelectedShops;
if (selected == null || selected.Count == 0)
return new List<string>();
var ids = selected.Select(s => s == null ? string.Empty : s.ErpShopId).ToList();
if (ids.Any(string.IsNullOrEmpty))
return new List<string>();
if (selected.Count >= ShopOptions.Count)
return new List<string>();
return ids;
}
/// <summary>组装查询参数(起止日期已保证先后顺序)</summary>
private ShopRevenueStatisticsQuery BuildQuery()
{
if (StartDate == EndDate)
{
EndDate = EndDate.AddDays(1).AddSeconds(-1);
}
else if (StartDate > EndDate)
{
var temp = StartDate;
StartDate = EndDate;
EndDate = temp;
}
return new ShopRevenueStatisticsQuery
{
StartDate = StartDate,
EndDate = EndDate,
ShopIds = BuildSelectedShopIds(),
};
}
/// <summary>执行查询,刷新页面所有统计数据</summary>
private async Task LoadAsync()
{
IsLoading = true;
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>());
ShopStatistics = new ObservableCollection<ShopStatisticsItem>(result.ShopStatistics ?? new List<ShopStatisticsItem>());
}
catch (Exception ex)
{
// 查询异常:保留上一次数据,并弹框提示
BaseMsgForm.ShowError("查询失败:" + ex.Message, "提示");
}
finally
{
IsLoading = false;
}
}
/// <summary>重置查询条件并重新加载</summary>
private async void Reset()
{
SelectedShops = new ObservableCollection<ShopOption>(ShopOptions);
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == "month");
ApplyTimeRange();
await LoadAsync();
}
#endregion
public ICommand ShowOrderListCommand => new AsyncDelegateCommand<string>(ShowOrderList);
private async Task ShowOrderList(string type)
{
var detailType = ParseDetailType(type);
List<ShopOrderDetailResponse> showList;
IsLoading = true;
try
{
showList = await _service.QueryOrderRowsAsync(BuildQuery(), detailType)
?? new List<ShopOrderDetailResponse>();
}
catch (Exception ex)
{
BaseMsgForm.ShowError("订单明细查询失败:" + ex.Message, "提示");
return;
}
finally
{
IsLoading = false;
}
if (showList.Count == 0)
{
BaseMsgForm.ShowInfo("未查询到符合条件的订单明细。", "提示");
return;
}
new DialogWindow(GetWindow())
{
Title = "订单列表",
Content = new ShowOrderDialogView(showList, detailType, _service),
DataContext = showList
}.Show();
}
/// <summary>卡片“明细”按钮参数 → 订单明细类型(对应接口参数 OrderType)</summary>
private static ShopOrderDetailType ParseDetailType(string type)
{
switch (type)
{
case "RefundOrderCount":
return ShopOrderDetailType.Refund; //退款订单
case "BuyerRefundRemainingAmount":
return ShopOrderDetailType.BuyerUnrefunded; //买手未退回金额的订单
case "ProfitOrder":
return ShopOrderDetailType.Profit; //买手未退回金额的订单
default:
return ShopOrderDetailType.Valid; //有效订单
}
}
public Func<Window> GetWindow;
}
}