package com.ruoyi.web.modules.screen.support;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.ruoyi.web.modules.industryservice.support.BreedingIndustryRules;
import com.ruoyi.web.modules.screen.domain.dto.TradeSalesDestinationCountDto;
import com.ruoyi.web.modules.screen.domain.dto.TradeSalesGradeCountDto;
import com.ruoyi.web.modules.screen.domain.dto.TradeSalesMonthTradeDto;
import com.ruoyi.web.modules.screen.domain.dto.TradeSalesOverviewDto;
import com.ruoyi.web.modules.screen.domain.dto.TradeSalesQuoteRawDto;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesDailyQuoteVo;
import com.ruoyi.web.modules.screen.domain.vo.MallCategorySalesVo;
import com.ruoyi.web.modules.screen.domain.vo.MallHotCategoryRankVo;
import com.ruoyi.web.modules.screen.domain.vo.MallOrderTrendItemVo;
import com.ruoyi.web.modules.screen.domain.vo.MallOrderTrendVo;
import com.ruoyi.web.modules.screen.domain.vo.MallRegionRankVo;
import com.ruoyi.web.modules.screen.domain.vo.MallReviewWordCloudVo;
import com.ruoyi.web.modules.screen.domain.vo.MallShopEntryVo;
import com.ruoyi.web.modules.screen.domain.vo.MallStatsOverviewVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesDashboardVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesDestinationVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesGradeItemVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesMonthlyTrendVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesOriginQuoteVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesOverviewVo;
import com.ruoyi.web.modules.screen.domain.vo.TradeSalesQualityGradeVo;
import com.ruoyi.web.modules.trading.domain.BizGradeWeightConfig;
import com.ruoyi.web.modules.trading.support.DistributorRules;
import com.ruoyi.web.modules.trading.support.MarketDailyQuoteRules;

/**
 * 大屏交易销售口径组装与纯计算（无 DB / HTTP 依赖，便于单测）。
 * <p>
 * 牦牛区块：销售去向固定本市/本省/外省三档；品质等级按配置表顺序输出占比。
 * 商城区块：{@link #applyMallStats} / {@link #applyEmptyMallStats} 由 Service 在 HTTP 回调后调用。
 */
public final class TradeSalesScreenSupport
{
    private static final DateTimeFormatter QUOTE_DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    private static final int RATIO_SCALE = 1;

    private TradeSalesScreenSupport()
    {
    }

    /** 牦牛交易总览；supplierCount 来自首页同源统计（入驻供应商数）。 */
    public static TradeSalesOverviewVo buildTradeOverview(
            TradeSalesOverviewDto overview, int supplierCount)
    {
        TradeSalesOverviewVo vo = new TradeSalesOverviewVo();
        if (overview == null)
        {
            vo.setOrderCount(0);
            vo.setTradeHeads(0);
            vo.setTradeAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
        }
        else
        {
            vo.setOrderCount(safeLong(overview.getOrderCount()));
            vo.setTradeHeads(safeLong(overview.getTradeHeads()));
            vo.setTradeAmount(scaleMoney(overview.getTradeAmount()));
        }
        vo.setSupplierCount(supplierCount);
        return vo;
    }

    /** 最近 12 个月牦牛交易量/额（按时间升序，含 statYear、month）。 */
    public static List<TradeSalesMonthlyTrendVo> buildRolling12MonthTradeTrend(
            List<TradeSalesMonthTradeDto> rows, YearMonth anchor)
    {
        Map<Integer, TradeSalesMonthTradeDto> byYm = new HashMap<>();
        if (rows != null)
        {
            for (TradeSalesMonthTradeDto row : rows)
            {
                if (row != null && row.getStatYear() != null && row.getMonth() != null)
                {
                    byYm.put(HomeScreenSupport.yearMonthKey(YearMonth.of(row.getStatYear(), row.getMonth())), row);
                }
            }
        }
        List<TradeSalesMonthlyTrendVo> list = new ArrayList<>(12);
        YearMonth start = anchor.minusMonths(11);
        for (int slot = 0; slot < 12; slot++)
        {
            YearMonth ym = start.plusMonths(slot);
            TradeSalesMonthTradeDto src = byYm.get(HomeScreenSupport.yearMonthKey(ym));
            TradeSalesMonthlyTrendVo point = new TradeSalesMonthlyTrendVo();
            point.setStatYear(ym.getYear());
            point.setMonth(ym.getMonthValue());
            point.setTradeHeads(src != null && src.getTradeHeads() != null ? src.getTradeHeads() : 0L);
            point.setTradeAmount(scaleMoney(src != null ? src.getTradeAmount() : null));
            list.add(point);
        }
        return list;
    }

    /** 按月牦牛交易量/额；当前年未来月份填 0。 */
    public static List<TradeSalesMonthlyTrendVo> buildTradeMonthlyTrend(
            List<TradeSalesMonthTradeDto> rows, LocalDate statDate)
    {
        Map<Integer, TradeSalesMonthTradeDto> byMonth = new HashMap<>();
        if (rows != null)
        {
            for (TradeSalesMonthTradeDto row : rows)
            {
                if (row != null && row.getMonth() != null)
                {
                    byMonth.put(row.getMonth(), row);
                }
            }
        }
        int futureAfter = statDate.getMonthValue();
        boolean currentYear = statDate.getYear()
                == LocalDate.now(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).getYear();
        List<TradeSalesMonthlyTrendVo> list = new ArrayList<>();
        for (int month = 1; month <= 12; month++)
        {
            TradeSalesMonthlyTrendVo point = new TradeSalesMonthlyTrendVo();
            point.setMonth(month);
            if (currentYear && month > futureAfter)
            {
                point.setTradeHeads(0);
                point.setTradeAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
            }
            else
            {
                TradeSalesMonthTradeDto src = byMonth.get(month);
                point.setTradeHeads(src != null && src.getTradeHeads() != null ? src.getTradeHeads() : 0L);
                point.setTradeAmount(scaleMoney(src != null ? src.getTradeAmount() : null));
            }
            list.add(point);
        }
        return list;
    }

    /** 销售去向占比；分母为当年已完成订单总头数 totalHeads。 */
    public static List<TradeSalesDestinationVo> buildSalesDestination(
            List<TradeSalesDestinationCountDto> rows, long totalHeads)
    {
        Map<Integer, Long> byDest = new HashMap<>();
        if (rows != null)
        {
            for (TradeSalesDestinationCountDto row : rows)
            {
                if (row != null && row.getSalesDestination() != null)
                {
                    byDest.put(row.getSalesDestination(), safeLong(row.getTradeHeads()));
                }
            }
        }
        List<TradeSalesDestinationVo> list = new ArrayList<>();
        int[] destinations = {
                DistributorRules.SALES_DESTINATION_LOCAL_CITY,
                DistributorRules.SALES_DESTINATION_LOCAL_PROVINCE,
                DistributorRules.SALES_DESTINATION_OUT_PROVINCE
        };
        for (int dest : destinations)
        {
            long heads = byDest.getOrDefault(dest, 0L);
            TradeSalesDestinationVo vo = new TradeSalesDestinationVo();
            vo.setSalesDestination(dest);
            vo.setSalesDestinationName(DistributorRules.salesDestinationName(dest));
            vo.setTradeHeads(heads);
            vo.setRatio(ratioPercent(heads, totalHeads));
            list.add(vo);
        }
        return list;
    }

    /** 品质等级占比；分母为含等级明细的订单行数 gradedLineCount（非总头数）。 */
    public static TradeSalesQualityGradeVo buildQualityGrade(
            List<TradeSalesGradeCountDto> countRows,
            List<BizGradeWeightConfig> configs,
            long gradedLineCount)
    {
        Map<String, Long> byCode = new HashMap<>();
        Map<String, String> nameByCode = new HashMap<>();
        if (countRows != null)
        {
            for (TradeSalesGradeCountDto row : countRows)
            {
                if (row != null && row.getGradeCode() != null)
                {
                    byCode.put(row.getGradeCode(), safeLong(row.getTradeHeads()));
                    if (row.getGradeName() != null && !row.getGradeName().isEmpty())
                    {
                        nameByCode.put(row.getGradeCode(), row.getGradeName());
                    }
                }
            }
        }
        List<TradeSalesGradeItemVo> items = new ArrayList<>();
        if (configs != null)
        {
            for (BizGradeWeightConfig config : configs)
            {
                if (config == null || config.getGradeCode() == null)
                {
                    continue;
                }
                long heads = byCode.getOrDefault(config.getGradeCode(), 0L);
                TradeSalesGradeItemVo item = new TradeSalesGradeItemVo();
                item.setGradeCode(config.getGradeCode());
                String name = nameByCode.get(config.getGradeCode());
                item.setGradeName(name != null ? name : config.getGradeName());
                item.setTradeHeads(heads);
                item.setRatio(ratioPercent(heads, gradedLineCount));
                items.add(item);
            }
        }
        TradeSalesQualityGradeVo vo = new TradeSalesQualityGradeVo();
        vo.setHasGradeData(gradedLineCount > 0);
        vo.setTotalHeads(gradedLineCount);
        vo.setItems(items);
        return vo;
    }

    /**
     * 产地行情近 7 日（含 statDate）：逐日 min/max/avg，汇总 7 日均价/极值；
     * 无报价日填 0 占位以保证前端折线连续。
     */
    public static TradeSalesOriginQuoteVo buildOriginQuote(
            List<TradeSalesQuoteRawDto> rawRows, LocalDate statDate)
    {
        if (statDate == null)
        {
            return emptyOriginQuote();
        }
        LocalDate windowStart = statDate.minusDays(6);
        Map<LocalDate, DailyQuoteAgg> dailyMap = aggregateByQuoteDate(rawRows);

        List<TradeSalesDailyQuoteVo> dailyTrend = new ArrayList<>();
        List<BigDecimal> dailyAvgsForSummary = new ArrayList<>();
        List<BigDecimal> dailyMinsForSummary = new ArrayList<>();
        List<BigDecimal> dailyMaxsForSummary = new ArrayList<>();

        for (LocalDate d = windowStart; !d.isAfter(statDate); d = d.plusDays(1))
        {
            DailyQuoteAgg agg = dailyMap.get(d);
            TradeSalesDailyQuoteVo point = new TradeSalesDailyQuoteVo();
            point.setQuoteDate(d.format(QUOTE_DATE_FMT));
            if (agg != null && agg.hasData)
            {
                point.setMinPrice(agg.minPrice);
                point.setMaxPrice(agg.maxPrice);
                point.setAvgPrice(agg.avgPrice);
                if (agg.avgPrice != null)
                {
                    dailyAvgsForSummary.add(agg.avgPrice);
                }
                if (agg.minPrice != null)
                {
                    dailyMinsForSummary.add(agg.minPrice);
                }
                if (agg.maxPrice != null)
                {
                    dailyMaxsForSummary.add(agg.maxPrice);
                }
            }
            else
            {
                point.setMinPrice(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
                point.setMaxPrice(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
                point.setAvgPrice(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
            }
            dailyTrend.add(point);
        }

        TradeSalesOriginQuoteVo vo = new TradeSalesOriginQuoteVo();
        boolean hasData = !dailyAvgsForSummary.isEmpty();
        vo.setHasQuoteData(hasData);
        vo.setDailyTrend(dailyTrend);
        if (!hasData)
        {
            vo.setAvgPrice7d(null);
            vo.setMinPrice7d(null);
            vo.setMaxPrice7d(null);
            vo.setLastQuoteDate(null);
            vo.setPriceUnit(null);
            vo.setPriceUnitLabel(null);
            return vo;
        }
        vo.setAvgPrice7d(average(dailyAvgsForSummary));
        vo.setMinPrice7d(minOf(dailyMinsForSummary));
        vo.setMaxPrice7d(maxOf(dailyMaxsForSummary));
        LocalDate lastQuoteDate = resolveLastQuoteDateInWindow(dailyMap, windowStart, statDate);
        if (lastQuoteDate != null)
        {
            vo.setLastQuoteDate(lastQuoteDate.format(QUOTE_DATE_FMT));
            DailyQuoteAgg unitSource = dailyMap.get(lastQuoteDate);
            Integer unit = resolvePriceUnit(unitSource);
            vo.setPriceUnit(unit);
            vo.setPriceUnitLabel(priceUnitLabel(unit));
        }
        return vo;
    }

    private static TradeSalesOriginQuoteVo emptyOriginQuote()
    {
        TradeSalesOriginQuoteVo vo = new TradeSalesOriginQuoteVo();
        vo.setHasQuoteData(false);
        vo.setDailyTrend(Collections.emptyList());
        return vo;
    }

    private static LocalDate resolveLastQuoteDateInWindow(
            Map<LocalDate, DailyQuoteAgg> dailyMap, LocalDate windowStart, LocalDate statDate)
    {
        LocalDate last = null;
        for (LocalDate d = windowStart; !d.isAfter(statDate); d = d.plusDays(1))
        {
            DailyQuoteAgg agg = dailyMap.get(d);
            if (agg != null && agg.hasData)
            {
                if (last == null || d.isAfter(last))
                {
                    last = d;
                }
            }
        }
        return last;
    }

    private static Integer resolvePriceUnit(DailyQuoteAgg unitSource)
    {
        if (unitSource != null && unitSource.priceUnit != null)
        {
            return unitSource.priceUnit;
        }
        return MarketDailyQuoteRules.PRICE_UNIT_PER_JIN;
    }

    public static List<Integer> buildAvailableYears(List<Integer> finishYears)
    {
        Set<Integer> set = new TreeSet<>(Collections.reverseOrder());
        int currentYear = LocalDate.now(ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI)).getYear();
        set.add(currentYear);
        if (finishYears != null)
        {
            set.addAll(finishYears);
        }
        return new ArrayList<>(set);
    }

    /** 为商城按年返回的订单趋势项补全 statYear。 */
    public static void tagMallOrderTrendItems(MallOrderTrendVo trend, int statYear)
    {
        if (trend == null)
        {
            return;
        }
        trend.setStatYear(statYear);
        if (trend.getItems() == null)
        {
            return;
        }
        for (MallOrderTrendItemVo item : trend.getItems())
        {
            if (item != null)
            {
                item.setStatYear(statYear);
            }
        }
    }

    /** 最近 12 个月商城订单趋势（按时间升序，含 statYear、month）。 */
    public static MallOrderTrendVo buildRolling12MonthMallOrderTrend(
            List<MallOrderTrendItemVo> allItems, YearMonth anchor)
    {
        Map<Integer, Long> byYm = new HashMap<>();
        if (allItems != null)
        {
            for (MallOrderTrendItemVo item : allItems)
            {
                if (item != null && item.getStatYear() > 0
                        && item.getMonth() >= 1 && item.getMonth() <= 12)
                {
                    int key = HomeScreenSupport.yearMonthKey(YearMonth.of(item.getStatYear(), item.getMonth()));
                    byYm.merge(key, item.getOrderCount(), Long::sum);
                }
            }
        }
        MallOrderTrendVo vo = new MallOrderTrendVo();
        vo.setStatYear(anchor.getYear());
        List<MallOrderTrendItemVo> items = new ArrayList<>(12);
        YearMonth start = anchor.minusMonths(11);
        for (int slot = 0; slot < 12; slot++)
        {
            YearMonth ym = start.plusMonths(slot);
            MallOrderTrendItemVo item = new MallOrderTrendItemVo();
            item.setStatYear(ym.getYear());
            item.setMonth(ym.getMonthValue());
            item.setOrderCount(byYm.getOrDefault(HomeScreenSupport.yearMonthKey(ym), 0L));
            items.add(item);
        }
        vo.setItems(items);
        return vo;
    }

    /** 将商城 overview 写入大屏看板并补全总览中的农资指标。 */
    public static void applyMallStats(TradeSalesDashboardVo dashboard, MallStatsOverviewVo mall)
    {
        if (dashboard == null || mall == null)
        {
            return;
        }
        dashboard.setMallStatsAvailable(true);
        dashboard.setCategorySales(mall.getCategorySales() != null ? mall.getCategorySales() : emptyCategorySales());
        dashboard.setHotCategoryRank(mall.getHotCategoryRank() != null ? mall.getHotCategoryRank() : emptyHotCategoryRank());
        dashboard.setMallOrderTrend(mall.getMallOrderTrend() != null ? mall.getMallOrderTrend() : emptyMallOrderTrend());
        dashboard.setShopEntry(mall.getShopEntry() != null ? mall.getShopEntry() : emptyShopEntry());
        dashboard.setRegionRank(mall.getRegionRank() != null ? mall.getRegionRank() : emptyRegionRank());
        dashboard.setReviewWordCloud(mall.getReviewWordCloud() != null ? mall.getReviewWordCloud() : emptyReviewWordCloud());
        enrichOverviewWithMall(dashboard.getTradeOverview(), mall);
    }

    /** 商城未接入或调用失败时的空态。 */
    public static void applyEmptyMallStats(TradeSalesDashboardVo dashboard)
    {
        if (dashboard == null)
        {
            return;
        }
        dashboard.setMallStatsAvailable(false);
        dashboard.setCategorySales(emptyCategorySales());
        dashboard.setHotCategoryRank(emptyHotCategoryRank());
        dashboard.setMallOrderTrend(emptyMallOrderTrend());
        dashboard.setShopEntry(emptyShopEntry());
        dashboard.setRegionRank(emptyRegionRank());
        dashboard.setReviewWordCloud(emptyReviewWordCloud());
    }

    /** 农资订单量 = 商城 {@code mallOrderTrend.items} 各月 orderCount 之和（年度汇总）。 */
    public static void enrichOverviewWithMall(TradeSalesOverviewVo overview, MallStatsOverviewVo mall)
    {
        if (overview == null || mall == null)
        {
            return;
        }
        MallOrderTrendVo orderTrend = mall.getMallOrderTrend();
        if (orderTrend != null && orderTrend.getItems() != null)
        {
            long orderCount = 0L;
            for (MallOrderTrendItemVo item : orderTrend.getItems())
            {
                if (item != null)
                {
                    orderCount += item.getOrderCount();
                }
            }
            overview.setAgriOrderCount(orderCount);
        }
    }

    private static MallCategorySalesVo emptyCategorySales()
    {
        return new MallCategorySalesVo();
    }

    private static MallHotCategoryRankVo emptyHotCategoryRank()
    {
        return new MallHotCategoryRankVo();
    }

    private static MallOrderTrendVo emptyMallOrderTrend()
    {
        return new MallOrderTrendVo();
    }

    private static MallShopEntryVo emptyShopEntry()
    {
        return new MallShopEntryVo();
    }

    private static MallRegionRankVo emptyRegionRank()
    {
        return new MallRegionRankVo();
    }

    private static MallReviewWordCloudVo emptyReviewWordCloud()
    {
        return new MallReviewWordCloudVo();
    }

    public static BigDecimal ratioPercent(long part, long total)
    {
        if (total <= 0)
        {
            return BigDecimal.ZERO.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
        }
        return BigDecimal.valueOf(part)
                .multiply(BigDecimal.valueOf(100))
                .divide(BigDecimal.valueOf(total), RATIO_SCALE, RoundingMode.HALF_UP);
    }

    private static Map<LocalDate, DailyQuoteAgg> aggregateByQuoteDate(List<TradeSalesQuoteRawDto> rawRows)
    {
        Map<LocalDate, DailyQuoteAgg> map = new HashMap<>();
        if (rawRows == null)
        {
            return map;
        }
        ZoneId zone = ZoneId.of(BreedingIndustryRules.ZONE_SHANGHAI);
        for (TradeSalesQuoteRawDto row : rawRows)
        {
            if (row == null || row.getQuoteDate() == null)
            {
                continue;
            }
            LocalDate date = row.getQuoteDate().toInstant().atZone(zone).toLocalDate();
            DailyQuoteAgg agg = map.computeIfAbsent(date, k -> new DailyQuoteAgg());
            agg.hasData = true;
            if (row.getPriceMin() != null)
            {
                agg.minPrice = agg.minPrice == null ? row.getPriceMin() : agg.minPrice.min(row.getPriceMin());
            }
            if (row.getPriceMax() != null)
            {
                agg.maxPrice = agg.maxPrice == null ? row.getPriceMax() : agg.maxPrice.max(row.getPriceMax());
            }
            if (row.getAvgPrice() != null)
            {
                agg.avgSum = agg.avgSum.add(row.getAvgPrice());
                agg.avgCount++;
            }
            if (row.getPriceUnit() != null)
            {
                if (row.getCreateTime() != null
                        && (agg.latestCreateTime == null || row.getCreateTime().after(agg.latestCreateTime)))
                {
                    agg.latestCreateTime = row.getCreateTime();
                    agg.priceUnit = row.getPriceUnit();
                }
                else if (agg.priceUnit == null)
                {
                    agg.priceUnit = row.getPriceUnit();
                }
            }
        }
        for (DailyQuoteAgg agg : map.values())
        {
            if (agg.avgCount > 0)
            {
                agg.avgPrice = agg.avgSum.divide(
                        BigDecimal.valueOf(agg.avgCount), 2, RoundingMode.HALF_UP);
            }
            if (agg.minPrice != null)
            {
                agg.minPrice = agg.minPrice.setScale(2, RoundingMode.HALF_UP);
            }
            else
            {
                agg.minPrice = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
            }
            if (agg.maxPrice != null)
            {
                agg.maxPrice = agg.maxPrice.setScale(2, RoundingMode.HALF_UP);
            }
            else
            {
                agg.maxPrice = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
            }
            if (agg.avgPrice != null)
            {
                agg.avgPrice = agg.avgPrice.setScale(2, RoundingMode.HALF_UP);
            }
            else
            {
                agg.avgPrice = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
            }
        }
        return map;
    }

    private static BigDecimal average(List<BigDecimal> values)
    {
        if (values == null || values.isEmpty())
        {
            return null;
        }
        BigDecimal sum = BigDecimal.ZERO;
        int count = 0;
        for (BigDecimal v : values)
        {
            if (v != null)
            {
                sum = sum.add(v);
                count++;
            }
        }
        if (count == 0)
        {
            return null;
        }
        return sum.divide(BigDecimal.valueOf(count), 2, RoundingMode.HALF_UP);
    }

    private static BigDecimal minOf(List<BigDecimal> values)
    {
        if (values == null || values.isEmpty())
        {
            return null;
        }
        return values.stream().filter(v -> v != null).min(Comparator.naturalOrder()).orElse(null);
    }

    private static BigDecimal maxOf(List<BigDecimal> values)
    {
        if (values == null || values.isEmpty())
        {
            return null;
        }
        return values.stream().filter(v -> v != null).max(Comparator.naturalOrder()).orElse(null);
    }

    private static String priceUnitLabel(Integer priceUnit)
    {
        return priceUnit != null && priceUnit == MarketDailyQuoteRules.PRICE_UNIT_PER_HEAD
                ? "元/头"
                : "元/斤";
    }

    private static long safeLong(Long value)
    {
        return value != null ? value : 0L;
    }

    private static BigDecimal scaleMoney(BigDecimal amount)
    {
        if (amount == null)
        {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        }
        return amount.setScale(2, RoundingMode.HALF_UP);
    }

    private static final class DailyQuoteAgg
    {
        private boolean hasData;

        private BigDecimal minPrice;

        private BigDecimal maxPrice;

        private BigDecimal avgPrice;

        private BigDecimal avgSum = BigDecimal.ZERO;

        private int avgCount;

        private Integer priceUnit;

        private Date latestCreateTime;
    }
}
