package com.tcm.training.admin.statistics.service;
|
|
import cn.idev.excel.FastExcel;
|
import com.tcm.training.admin.trade.service.PaymentFlowAppService;
|
import com.tcm.training.admin.statistics.excel.RevenueStatisticsExcelRow;
|
import com.tcm.training.admin.statistics.mapper.RevenueStatisticsMapper;
|
import com.tcm.training.admin.statistics.row.RevenueStatisticsDailyRow;
|
import com.tcm.training.admin.statistics.vo.RevenueStatisticsOverviewVO;
|
import com.tcm.training.admin.statistics.vo.RevenueStatisticsTrendItemVO;
|
import com.tcm.training.admin.statistics.vo.RevenueStatisticsVO;
|
import org.springframework.stereotype.Service;
|
|
import javax.servlet.http.HttpServletResponse;
|
import java.io.IOException;
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.net.URLEncoder;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.LocalTime;
|
import java.util.ArrayList;
|
import java.util.HashMap;
|
import java.util.List;
|
import java.util.Map;
|
|
@Service
|
public class RevenueStatisticsAppService {
|
|
private static final String FILTER_ALL = "ALL";
|
private static final String FILTER_HEALTH = "HEALTH";
|
private static final String FILTER_MEMBER = "MEMBER";
|
private static final String BUSINESS_TYPE_HEALTH = "COURSE_PURCHASE";
|
private static final String BUSINESS_TYPE_MEMBER = "MEMBER_PURCHASE";
|
|
private final RevenueStatisticsMapper revenueStatisticsMapper;
|
private final PaymentFlowAppService paymentFlowAppService;
|
|
public RevenueStatisticsAppService(RevenueStatisticsMapper revenueStatisticsMapper,
|
PaymentFlowAppService paymentFlowAppService) {
|
this.revenueStatisticsMapper = revenueStatisticsMapper;
|
this.paymentFlowAppService = paymentFlowAppService;
|
}
|
|
public RevenueStatisticsVO statistics(String type, LocalDate dateFrom, LocalDate dateTo) {
|
DateRange range = normalizeRange(dateFrom, dateTo);
|
String normalizedType = normalizeType(type);
|
|
paymentFlowAppService.syncFlows();
|
|
List<RevenueStatisticsDailyRow> rows = revenueStatisticsMapper.selectDailyStatistics(
|
resolveBusinessType(normalizedType),
|
range.getStart().atStartOfDay(),
|
LocalDateTime.of(range.getEnd(), LocalTime.MAX)
|
);
|
List<RevenueStatisticsTrendItemVO> trend = buildTrend(rows, range);
|
|
RevenueStatisticsVO result = new RevenueStatisticsVO();
|
result.setOverview(buildOverview(trend));
|
result.setTrend(trend);
|
return result;
|
}
|
|
public void export(HttpServletResponse response, String type, LocalDate dateFrom, LocalDate dateTo) throws IOException {
|
RevenueStatisticsVO statistics = statistics(type, dateFrom, dateTo);
|
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
response.setCharacterEncoding("utf-8");
|
String filename = URLEncoder.encode("营收数据统计", "UTF-8").replace("+", "%20");
|
response.setHeader("Content-Disposition", "attachment;filename*=utf-8''" + filename + ".xlsx");
|
|
List<RevenueStatisticsExcelRow> exports = new ArrayList<RevenueStatisticsExcelRow>();
|
for (RevenueStatisticsTrendItemVO item : statistics.getTrend()) {
|
RevenueStatisticsExcelRow export = new RevenueStatisticsExcelRow();
|
export.setDate(item.getDate());
|
export.setTotalIncome(formatAmount(item.getTotalIncome()));
|
export.setHealthIncome(formatAmount(item.getHealthIncome()));
|
export.setMemberIncome(formatAmount(item.getMemberIncome()));
|
export.setRefundAmount(formatAmount(item.getRefundAmount()));
|
export.setProfit(formatAmount(item.getProfit()));
|
exports.add(export);
|
}
|
FastExcel.write(response.getOutputStream(), RevenueStatisticsExcelRow.class)
|
.sheet("营收数据")
|
.doWrite(exports);
|
}
|
|
private List<RevenueStatisticsTrendItemVO> buildTrend(List<RevenueStatisticsDailyRow> rows, DateRange range) {
|
Map<LocalDate, RevenueStatisticsDailyRow> rowMap = new HashMap<LocalDate, RevenueStatisticsDailyRow>();
|
for (RevenueStatisticsDailyRow row : rows) {
|
rowMap.put(row.getStatDate(), row);
|
}
|
|
List<RevenueStatisticsTrendItemVO> result = new ArrayList<RevenueStatisticsTrendItemVO>();
|
LocalDate cursor = range.getStart();
|
while (!cursor.isAfter(range.getEnd())) {
|
RevenueStatisticsDailyRow row = rowMap.get(cursor);
|
BigDecimal healthIncome = nvl(row == null ? null : row.getHealthIncome());
|
BigDecimal memberIncome = nvl(row == null ? null : row.getMemberIncome());
|
BigDecimal healthRefundAmount = nvl(row == null ? null : row.getHealthRefundAmount());
|
BigDecimal memberRefundAmount = nvl(row == null ? null : row.getMemberRefundAmount());
|
BigDecimal refundAmount = healthRefundAmount.add(memberRefundAmount);
|
BigDecimal totalIncome = healthIncome.add(memberIncome);
|
|
RevenueStatisticsTrendItemVO item = new RevenueStatisticsTrendItemVO();
|
item.setDate(cursor.toString());
|
item.setHealthIncome(healthIncome);
|
item.setMemberIncome(memberIncome);
|
item.setHealthRefundAmount(healthRefundAmount);
|
item.setMemberRefundAmount(memberRefundAmount);
|
item.setTotalIncome(totalIncome);
|
item.setRefundAmount(refundAmount);
|
item.setProfit(totalIncome.subtract(refundAmount));
|
result.add(item);
|
cursor = cursor.plusDays(1);
|
}
|
return result;
|
}
|
|
private RevenueStatisticsOverviewVO buildOverview(List<RevenueStatisticsTrendItemVO> trend) {
|
RevenueStatisticsOverviewVO overview = new RevenueStatisticsOverviewVO();
|
BigDecimal totalIncome = BigDecimal.ZERO;
|
BigDecimal healthIncome = BigDecimal.ZERO;
|
BigDecimal memberIncome = BigDecimal.ZERO;
|
BigDecimal refundAmount = BigDecimal.ZERO;
|
BigDecimal profit = BigDecimal.ZERO;
|
|
for (RevenueStatisticsTrendItemVO item : trend) {
|
totalIncome = totalIncome.add(nvl(item.getTotalIncome()));
|
healthIncome = healthIncome.add(nvl(item.getHealthIncome()));
|
memberIncome = memberIncome.add(nvl(item.getMemberIncome()));
|
refundAmount = refundAmount.add(nvl(item.getRefundAmount()));
|
profit = profit.add(nvl(item.getProfit()));
|
}
|
|
overview.setTotalIncome(totalIncome);
|
overview.setHealthIncome(healthIncome);
|
overview.setMemberIncome(memberIncome);
|
overview.setRefundAmount(refundAmount);
|
overview.setProfit(profit);
|
return overview;
|
}
|
|
private String normalizeType(String type) {
|
String normalized = trimToNull(type);
|
if (normalized == null) {
|
return FILTER_ALL;
|
}
|
if (FILTER_ALL.equals(normalized) || FILTER_HEALTH.equals(normalized) || FILTER_MEMBER.equals(normalized)) {
|
return normalized;
|
}
|
throw new IllegalArgumentException("统计类型不合法");
|
}
|
|
private String resolveBusinessType(String normalizedType) {
|
if (FILTER_HEALTH.equals(normalizedType)) {
|
return BUSINESS_TYPE_HEALTH;
|
}
|
if (FILTER_MEMBER.equals(normalizedType)) {
|
return BUSINESS_TYPE_MEMBER;
|
}
|
return null;
|
}
|
|
private DateRange normalizeRange(LocalDate dateFrom, LocalDate dateTo) {
|
LocalDate start = dateFrom;
|
LocalDate end = dateTo;
|
if (start == null && end == null) {
|
start = LocalDate.now();
|
end = start;
|
} else if (start == null) {
|
start = end;
|
} else if (end == null) {
|
end = start;
|
}
|
if (start.isAfter(end)) {
|
throw new IllegalArgumentException("开始日期不能晚于结束日期");
|
}
|
return new DateRange(start, end);
|
}
|
|
private String formatAmount(BigDecimal value) {
|
return nvl(value).setScale(2, RoundingMode.HALF_UP).toPlainString();
|
}
|
|
private BigDecimal nvl(BigDecimal value) {
|
return value == null ? BigDecimal.ZERO : value;
|
}
|
|
private String trimToNull(String value) {
|
if (value == null) {
|
return null;
|
}
|
String trimmed = value.trim();
|
return trimmed.isEmpty() ? null : trimmed;
|
}
|
|
private static class DateRange {
|
private final LocalDate start;
|
private final LocalDate end;
|
|
private DateRange(LocalDate start, LocalDate end) {
|
this.start = start;
|
this.end = end;
|
}
|
|
private LocalDate getStart() {
|
return start;
|
}
|
|
private LocalDate getEnd() {
|
return end;
|
}
|
}
|
}
|