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

587 lines
16 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 _29WxOrderWpf.Commands;
using _29WxOrderWpf.Common;
using _29WxOrderWpf.Ctrls.Statistics.Models;
using _29WxOrderWpf.Ctrls.Statistics.Services;
using AllBase.Model.MvvmBaseClasses;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace _29WxOrderWpf.Ctrls.Statistics.ViewModels
{
/// <summary>
/// 天/周/月周期统计 - 页面 ViewModel(MVVM)。
/// 通过 PeriodStatisticType 指定统计周期(Day/Week/Month),
/// 页面统一展示:筛选条件 + 周期×店铺统计表格 + 分页。
/// </summary>
public class PeriodRevenueStatisticsViewModel : PropertyNotifyObject
{
/// <summary>每页行数</summary>
private const int PageSize = 50;
private readonly IShopRevenueStatisticsService _service;
private readonly PeriodStatisticType _periodType;
private readonly DelegateCommand _firstPageCommand;
private readonly DelegateCommand _prevPageCommand;
private readonly DelegateCommand _nextPageCommand;
private readonly DelegateCommand _lastPageCommand;
private ObservableCollection<ShopOption> _selectedShops;
private ShopRevenueSummary _summary;
private ObservableCollection<ShopPeriodStatisticsItem> _statistics;
private List<ShopPeriodStatisticsItem> _allItems = new List<ShopPeriodStatisticsItem>();
private int _pageIndex = 1;
private int _totalPages = 1;
private int _totalCount;
private string _pagerText = string.Empty;
public PeriodRevenueStatisticsViewModel(PeriodStatisticType periodType)
: this(periodType, new ShopRevenueStatisticsService())
{
}
public PeriodRevenueStatisticsViewModel(PeriodStatisticType periodType, IShopRevenueStatisticsService service)
{
_periodType = periodType;
_service = service ?? new ShopRevenueStatisticsService();
PageTitle = GetPageTitle(periodType);
PeriodColumnHeader = GetPeriodColumnHeader(periodType);
TimeRangeOptions = new ObservableCollection<TimeRangeOption>(GetTimeRangeOptions(periodType));
ShopOptions = new ObservableCollection<ShopOption>();
QueryCommand = new DelegateCommand(async () => await LoadAsync());
ResetCommand = new DelegateCommand(Reset);
_firstPageCommand = new DelegateCommand(() => GoToPage(1), () => HasPrevPage);
_prevPageCommand = new DelegateCommand(() => GoToPage(PageIndex - 1), () => HasPrevPage);
_nextPageCommand = new DelegateCommand(() => GoToPage(PageIndex + 1), () => HasNextPage);
_lastPageCommand = new DelegateCommand(() => GoToPage(TotalPages), () => HasNextPage);
}
public void Init()
{
InitializeAsync();
}
#region 页面标题
/// <summary>页面标题(天统计/周统计/月统计)</summary>
public string PageTitle { get; }
/// <summary>周期列表头(日期/周/月份)</summary>
public string PeriodColumnHeader { get; }
private static string GetPageTitle(PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return "周统计";
case PeriodStatisticType.Month:
return "月统计";
default:
return "天统计";
}
}
private static string GetPeriodColumnHeader(PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return "周";
case PeriodStatisticType.Month:
return "月份";
default:
return "日期";
}
}
#endregion
#region 时间范围选项
/// <summary>按统计周期返回对应的时间范围选项</summary>
private static List<TimeRangeOption> GetTimeRangeOptions(PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return new List<TimeRangeOption>
{
new TimeRangeOption { Text = "本周", Value = "week" },
new TimeRangeOption { Text = "上周", Value = "lastweek" },
new TimeRangeOption { Text = "最近2周", Value = "last2weeks" },
new TimeRangeOption { Text = "最近4周", Value = "last4weeks" },
new TimeRangeOption { Text = "自定义", Value = "custom" }
};
case PeriodStatisticType.Month:
return new List<TimeRangeOption>
{
new TimeRangeOption { Text = "本月", Value = "month" },
new TimeRangeOption { Text = "上月", Value = "lastmonth" },
new TimeRangeOption { Text = "最近3个月", Value = "last3months" },
new TimeRangeOption { Text = "最近6个月", Value = "last6months" },
new TimeRangeOption { Text = "最近12个月", Value = "last12months" },
new TimeRangeOption { Text = "自定义", Value = "custom" }
};
default:
return new List<TimeRangeOption>
{
new TimeRangeOption { Text = "最近5天", Value = "last5" },
new TimeRangeOption { Text = "最近10天", Value = "last10" },
new TimeRangeOption { Text = "最近20天", Value = "last20" },
new TimeRangeOption { Text = "最近30天", Value = "last30" },
new TimeRangeOption { Text = "自定义", Value = "custom" }
};
}
}
/// <summary>按统计周期返回默认选中的时间范围值</summary>
private static string GetDefaultTimeRangeValue(PeriodStatisticType periodType)
{
switch (periodType)
{
case PeriodStatisticType.Week:
return "week";
case PeriodStatisticType.Month:
return "month";
default:
return "last10";
}
}
#endregion
#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);
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>选中的店铺(多选,绑定 ShopFilterComboBox.SelectedItems)</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<ShopPeriodStatisticsItem> Statistics
{
get { return _statistics; }
private set
{
_statistics = value;
OnPropertyChanged(nameof(Statistics));
}
}
/// <summary>是否正在加载</summary>
public bool IsLoading
{
get { return this.GetValue(x => x.IsLoading); }
set { this.SetValue(x => x.IsLoading, value); }
}
#endregion
#region 分页
/// <summary>总行数</summary>
public int TotalCount
{
get { return _totalCount; }
private set
{
_totalCount = value;
OnPropertyChanged(nameof(TotalCount));
}
}
/// <summary>当前页码(从 1 开始)</summary>
public int PageIndex
{
get { return _pageIndex; }
private set
{
_pageIndex = value;
OnPropertyChanged(nameof(PageIndex));
}
}
/// <summary>总页数</summary>
public int TotalPages
{
get { return _totalPages; }
private set
{
_totalPages = value;
OnPropertyChanged(nameof(TotalPages));
}
}
/// <summary>分页提示文本(共 X 条 第 Y / Z 页)</summary>
public string PagerText
{
get { return _pagerText; }
private set
{
_pagerText = value;
OnPropertyChanged(nameof(PagerText));
}
}
/// <summary>是否有上一页</summary>
public bool HasPrevPage
{
get { return PageIndex > 1; }
}
/// <summary>是否有下一页</summary>
public bool HasNextPage
{
get { return PageIndex < TotalPages; }
}
/// <summary>是否多页</summary>
public bool HasMultiplePages
{
get { return TotalPages > 1; }
}
/// <summary>首页命令</summary>
public ICommand FirstPageCommand
{
get { return _firstPageCommand; }
}
/// <summary>上一页命令</summary>
public ICommand PrevPageCommand
{
get { return _prevPageCommand; }
}
/// <summary>下一页命令</summary>
public ICommand NextPageCommand
{
get { return _nextPageCommand; }
}
/// <summary>末页命令</summary>
public ICommand LastPageCommand
{
get { return _lastPageCommand; }
}
/// <summary>跳转到指定页并刷新当前页数据</summary>
private void GoToPage(int page)
{
if (page < 1)
page = 1;
if (page > TotalPages)
page = TotalPages;
PageIndex = page;
ApplyPaging();
}
/// <summary>按当前页码截取当前页数据并刷新分页状态</summary>
private void ApplyPaging()
{
var pageItems = _allItems
.Skip((PageIndex - 1) * PageSize)
.Take(PageSize)
.ToList();
Statistics = new ObservableCollection<ShopPeriodStatisticsItem>(pageItems);
PagerText = string.Format("共 {0} 条 第 {1} / {2} 页", TotalCount, PageIndex, TotalPages);
OnPropertyChanged(nameof(HasPrevPage));
OnPropertyChanged(nameof(HasNextPage));
OnPropertyChanged(nameof(HasMultiplePages));
_firstPageCommand.RaiseCanExecuteChanged();
_prevPageCommand.RaiseCanExecuteChanged();
_nextPageCommand.RaiseCanExecuteChanged();
_lastPageCommand.RaiseCanExecuteChanged();
}
#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 == GetDefaultTimeRangeValue(_periodType));
ApplyTimeRange();
await LoadAsync();
}
catch (Exception ex)
{
MessageBox.Show("初始化数据失败:" + ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
}
}
#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 "last5":
StartDate = today.AddDays(-4);
EndDate = now;
break;
case "last10":
StartDate = today.AddDays(-9);
EndDate = now;
break;
case "last20":
StartDate = today.AddDays(-19);
EndDate = now;
break;
case "last30":
StartDate = today.AddDays(-29);
EndDate = now;
break;
case "week":
{
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;
case "last2weeks":
StartDate = today.AddDays(-13);
EndDate = now;
break;
case "last4weeks":
StartDate = today.AddDays(-27);
EndDate = now;
break;
case "month":
StartDate = new DateTime(today.Year, today.Month, 1);
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;
case "last3months":
StartDate = new DateTime(today.Year, today.Month, 1).AddMonths(-2);
EndDate = now;
break;
case "last6months":
StartDate = new DateTime(today.Year, today.Month, 1).AddMonths(-5);
EndDate = now;
break;
case "last12months":
StartDate = new DateTime(today.Year, today.Month, 1).AddMonths(-11);
EndDate = now;
break;
default:
// 自定义:保持用户选择的日期不变
break;
}
}
/// <summary>根据选中店铺生成查询店铺 key 列表:未选/全选/选中“全部店铺”时返回空列表(表示全部店铺)</summary>
private List<string> BuildSelectedShopKeys()
{
var selected = SelectedShops;
if (selected == null || selected.Count == 0)
return new List<string>();
var keys = selected.Select(s => s == null ? string.Empty : s.AppKey).ToList();
if (keys.Any(string.IsNullOrEmpty))
return new List<string>();
if (selected.Count >= ShopOptions.Count)
return new List<string>();
return keys;
}
/// <summary>执行查询,刷新周期统计表格并回到第一页</summary>
private async Task LoadAsync()
{
if (StartDate > EndDate)
{
var temp = StartDate;
StartDate = EndDate;
EndDate = temp;
}
IsLoading = true;
try
{
var query = new ShopRevenueStatisticsQuery
{
StartDate = StartDate,
EndDate = EndDate,
ShopKeys = BuildSelectedShopKeys(),
};
var table = await _service.GetPeriodStatisticsAsync(query, _periodType);
Summary = await _service.GetSummaryAsync(query);
_allItems = table ?? new List<ShopPeriodStatisticsItem>();
TotalCount = _allItems.Count;
TotalPages = TotalCount == 0 ? 1 : (int)Math.Ceiling(TotalCount / (double)PageSize);
GoToPage(1);
}
catch (Exception ex)
{
// 查询异常:保留上一次数据,并弹框提示
MessageBox.Show("查询失败:" + ex.Message, "提示", MessageBoxButton.OK, MessageBoxImage.Warning);
}
finally
{
IsLoading = false;
}
}
/// <summary>重置查询条件并重新加载</summary>
private async void Reset()
{
SelectedShops = new ObservableCollection<ShopOption>(ShopOptions);
SelectedTimeRange = TimeRangeOptions.FirstOrDefault(t => t.Value == GetDefaultTimeRangeValue(_periodType));
ApplyTimeRange();
await LoadAsync();
}
#endregion
public ICommand ShowOrderListCommand => new AsyncDelegateCommand<string>(ShowOrderList);
private async Task ShowOrderList(string type)
{
var query = new ShopRevenueStatisticsQuery
{
StartDate = StartDate,
EndDate = EndDate,
ShopKeys = BuildSelectedShopKeys(),
};
var rows = await _service.QueryRowsAsync(query);
List<ShopRevenueStatisticsRow> showList = new List<ShopRevenueStatisticsRow>();
switch (type)
{
case "ValidOrderCount":
showList = RevenueStatisticsAggregator.GetValidOrder(rows);
break; //有效订单
case "RefundOrderCount":
showList = RevenueStatisticsAggregator.GetRefundOrder(rows);
break;//退款订单
case "BuyerRefundRemainingAmount":
showList = RevenueStatisticsAggregator.GetBuyerRefundRemainingOrder(rows);
break;//待退回采购金额
default:
break;
}
new DialogWindow()
{
Title = "订单列表",
Content = new ShowOrderDialogView(showList),
DataContext = rows
}.ShowDialog();
}
}
}