泛型工作中的运用--减少类似的重复代码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liuyanlinglanq/article/details/79187859

     要完成这样的功能,不是很复杂,就是有点多,不想每个都写一个。

     而因为他们是其实有规则的,比如根据总的如申请购买数量统计近7日的数据,然后根据不同的产品同样统计近7日的数据。而且产品只有产品1,产品2,产品3,产品4,这四种。所以想根据泛型来完成,因为他们的类型不是完全一样,上面的数量用的是Long类型,而下面是的金额Double(对于金额没有用BigDecimal,因为用这个类型,显示不出来),所以初始化的时候我废了一番功夫。

      文中只会显示主要的代码和结果,而且都是我修改过的,不是原本的代码。


      由于这个是竖着展示的,所以我也是按着竖着的方式写的velocity模板引擎。以前这种的,我会写很复杂的循环来完成这样的功能,但是由于我总是不善于保存代码,每次都是新写一遍,我觉得太费劲了,所以想到和横着写的方式一样,竖着把这个写好,只是这样觉得实体不是很好定义。velocity模板的语法很类似html,只是所有的数据,需要先用set放置到变量中,然后才能使用。

<html>
<body>
<h1>数据如下</h1>
<div>
    <table cellspacing="16">
        #set($record = $result.record)
        #if ($record)
            #set($productDetailList = $record.productDetailList)
            #if ($productDetailList)
                #set($countList = $productDetailList[0].totalCountList)
                #if ($countList)
                    <tr align="left">
                        <td align="left">日期</td>
                        #foreach ($stat in $countList)
                            <td align="left">$stat.date</td>
                        #end
                    </tr>
                #end

                #foreach ($product in $productDetailList)
                    #set($countList = $product.totalCountList)
                    #if ($countList)
                        <tr align="left">
                            <td align="left">$product.title</td>
                            #foreach ($stat in $countList)
                                <td align="left">$stat.count</td>
                            #end
                        </tr>
                    #end

                    #set($countList = $product.productOneCountList)
                    #if ($countList)
                    <tr align="right">
                        <td>产品1$product.title</td>
                        #foreach ($stat in $countList)
                            <td align="left">$stat.count</td>
                        #end
                    </tr>
                    #end

                    #set($countList = $product.productTwoCountList)
                    #if ($countList)
                        <tr align="right">
                            <td>产品2$product.title</td>
                            #foreach ($stat in $countList)
                                <td align="left">$stat.count</td>
                            #end
                        </tr>
                    #end

                    #set($countList = $product.productThreeCountList)
                    #if ($countList)
                        <tr align="right">
                            <td>产品3$product.title</td>
                            #foreach ($stat in $countList)
                                <td align="left">$stat.count</td>
                            #end
                        </tr>
                    #end

                    #set($countList = $product.productFourCountList)
                    #if ($countList)
                        <tr align="right">
                            <td>产品4$product.title</td>
                            #foreach ($stat in $countList)
                                <td align="left">$stat.count</td>
                            #end
                        </tr>
                    #end
                #end
            #end
        #end
    </table>
    <br><br>
</div>
</body>
</html>
     对应的结构就是这样,从map中取出record,也就是所有的记录,该record中只有productDetailList产品详情列表信息,他原本还有其他属性的,但是这里不需要被我删了。而产品详情List,每个都是一个实体,该实体又是由5个List和一个title组成。title就是用于在界面上显示名字而已。其中每个List,都是近7日的数据,包含日期和数据。


产品详情中的实体代码

/**
 * 产品详情VO
 *
 *
 * @author
 */
public class ProductDetailVO<T> {


    /**
     * 标题
     */
    private String title;

    /**
     * 总的数据详情
     */
    private List<DateCountPO<T>> totalCountList;

    /**
     * 产品1的数据详情
     */
    private List<DateCountPO<T>> productOneCountList;

    /**
     * 产品2的数据详情
     */
    private List<DateCountPO<T>> productTwoCountList;

    /**
     * 产品3的数据详情
     */
    private List<DateCountPO<T>> productThreeCountList;

    /**
     * 产品4的数据详情
     */
    private List<DateCountPO<T>> productFourCountList;

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<DateCountPO<T>> getTotalCountList() {
        return totalCountList;
    }

    public void setTotalCountList(List<DateCountPO<T>> totalCountList) {
        this.totalCountList = totalCountList;
    }
}

    DateCountPO也是泛型类

/**
 * 日期和数量
 *
 * @author
 */
public class DateCountPO<T> {

    /**
     * 日期
     */
    private String date;

    /**
     * 数量
     */
    private T count;

    public DateCountPO() {}

    public DateCountPO(String date, T count) {
        this.date = date;
        this.count = count;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    public T getCount() {
        return count;
    }

    public void setCount(T count) {
        this.count = count;
    }
}
    SummaryDayVO就是只有 productDetailList一个属性;

public class SummaryDayVO {

    /**
     * 各产品信息详情
     */
    private List<ProductDetailVO> productDetailList;


    public List<ProductDetailVO> getProductDetailList() {
        return productDetailList;
    }

    public void setProductDetailList(List<ProductDetailVO> productDetailList) {
        this.productDetailList = productDetailList;
    }
}
     获取日报的代码就是这样,

     先是根据开始和结束日期获取7日中所有的日期,这样假使GoupBy之后发现数据库中没有当天的记录,我用这个日期来控制,没有记录的默认为0或者0.0;

      然后就是total和ByProduct的SQL分别从数据库中获取对应的数据,然后用splitProductDetail把这数据库中的数据拆分成我需要的产品详情实体。最后放到SummaryDayVO实体中,这就是我们需要的结果了。

   /**
     * 获取日报中需要的数据;
     * @param startDate
     * @param endDate
     * @return
     */
    private SummaryDayVO queryDailyReportData(Date startDate, Date endDate) {
        //开始到结束日期的每一天的列表,用于补齐数据
        List<Date> dateList = Dates.getBetweenDates(startDate,endDate,false);

        SummaryDayVO dayVO = new SummaryDayVO();
        //所有产品
        List<ProductDetailVO> productDetailVOList = Lists.newArrayList();

        //申请购买
        List<Map<String,Object>> orderCheck = mapper.queryApplyByTotal(startDate,endDate);
        //申请购买(按产品划分)
        List<Map<String,Object>> orderCheckByProduct = mapper.queryApplyByProduct(startDate,endDate);
        //申请购买产品
        ProductDetailVO<Long> orderCheckProductDetailVO =  splitProductDetail(orderCheck,orderCheckByProduct,dateList,"申请购买","count",0l);
        productDetailVOList.add(orderCheckProductDetailVO);

        //成功订单
        List<Map<String,Object>> orderSuccessInfo = mapper.queryOrderSuccessInfo(startDate,endDate);
        //成功订单(按产品)
        List<Map<String,Object>> orderSuccessInfoByProduct = mapper.queryOrderSuccessInfoByProduct(startDate,endDate);
        //成功单数,成功金额
        ProductDetailVO<Long> orderSuccessCountProductDetailVO = splitProductDetail(orderSuccessInfo,orderSuccessInfoByProduct,dateList,"成功单数","count",0l);
        ProductDetailVO<Double> orderSuccessAmountProductDetailVO = splitProductDetail(orderSuccessInfo,orderSuccessInfoByProduct,dateList,"成功金额","amount",0.0);
        productDetailVOList.add(orderSuccessCountProductDetailVO);
        productDetailVOList.add(orderSuccessAmountProductDetailVO);

        dayVO.setProductDetailList(productDetailVOList);

        //记录条数
        LOGGER.info("get data end");

        //0填充缺失数据
        return fullWithZero(dayVO, Dates.getBetweenDates(startDate,endDate,false));
    }
      而从数据库中获取需要的数据,如 queryOrderSuccessInfo,queryO rderSuccessInfoByProduct,如成功订单和成功订单分产品,根据更新时间获取每天的成功单数和成功金额数。其中,product 产品1,2,3,4是在SQL中已经定义好的,而且返回的count就是数量,amount就是金额也是定义好的。

set @startDate := '2018-01-12';
set @endDate := '2018-01-17';
-- 总的
SELECT
    DATE_FORMAT( dt.upadte_time, '%Y-%m-%d' ) AS date,
    count( 1 ) AS count,
    ROUND(sum( amount ) / 100,2) AS amount
FROM
     database1.table1 dt
WHERE
    1 = 1
    AND dt.upadte_time >= @startDate
    AND dt.upadte_time < @endDate
GROUP BY
1;

-- 分产品的
SELECT
    DATE_FORMAT( dt.upadte_time, '%Y-%m-%d' ) AS date,
    CASE
      WHEN product_code = 1 THEN 'productOne'
      WHEN product_code = 2 THEN 'productTwo'
      WHEN product_code = 3 THEN 'productThree'
      ELSE 'productFour'
    END AS product,
    count( 1 ) AS count,
    ROUND(sum( amount ) / 100,2) AS amount
FROM
   database1.table1 dt
WHERE
    1 = 1
    AND dt.upadte_time >= @startDate
    AND dt.upadte_time < @endDate
GROUP BY
     1;
    splitProductDetail,就是拆分产品详情,拆分成我需要的实体。其中title是传递过来的,然后filed字段也是提前指定的,指定我从totalInfo和infoByProduct这两个List<Map>到底取那个key。而由于T是不能初始化的,我又有Long和Double两种不同的类型,所以最终决定初始化的值也是传过来的,这样我只用定义T  initCount就行。产品数据的时候,若没有查到则设置为初始化的值。也就是说,这就是说我在用0填充缺失的数据。
 /**
     * 将totalInfo和infoByProduct,处理成以这个ProductDetailVO,
     * @param totalInfo 总的信息
     * @param infoByProduct 根据产品的分的信息
     * @param dateList 所有日期列表,每天一个
     * @param title 标题
     * @param filed 字段(从数据库中需要读取的数据)
     * @param initCount 初始化值(因为T我不知道如何初始化,所以传一个初始化的值过来)
     * @param <T> 泛型
     * @return productDetailVO
     */
    private <T extends Number> ProductDetailVO<T> splitProductDetail(List<Map<String, Object>> totalInfo,List<Map<String, Object>> infoByProduct, List<Date> dateList,String title,  String filed,T initCount) {

        //最终的结果
        ProductDetailVO<T>  resultProductVO = new ProductDetailVO<>();
        //设置好中文标题
        resultProductVO.setTitle(title);

        List<DateCountPO<T>> totalCountList = Lists.newArrayList();
        List<DateCountPO<T>> productOneCountList = Lists.newArrayList();
        List<DateCountPO<T>> productTwoCountList = Lists.newArrayList();
        List<DateCountPO<T>> productThreeCountList = Lists.newArrayList();
        List<DateCountPO<T>> productFourCountList = Lists.newArrayList();

        //遍历所有天数,防止为空的数据的存在
        for (Date sevenDate : dateList) {
            String date = Dates.formatDate(sevenDate);

            DateCountPO<T> totalCount = new DateCountPO<>(date,initCount);
            DateCountPO<T> productOneCount = new DateCountPO<>(date,initCount);
            DateCountPO<T> productTwoCount = new DateCountPO<>(date,initCount);
            DateCountPO<T> productThreeCount = new DateCountPO<>(date,initCount);
            DateCountPO<T> productFourCount = new DateCountPO<>(date,initCount);

            //获取总的
            Map<String, Object> total = totalInfo.stream().filter(o -> o.get("date").toString().equals(date)).findFirst().orElse(null);
            //获取各个产品,productOne,productTwo,productThree,productFour的值,没有则为null;
            Map<String, Object> productOne = infoByProduct.stream().filter(o -> o.get("date").toString().equals(date) && o.get("product").toString().equals("productOne")).findFirst().orElse(null);
            Map<String, Object> productTwo = infoByProduct.stream().filter(o -> o.get("date").toString().equals(date) && o.get("product").toString().equals("productTwo")).findFirst().orElse(null);
            Map<String, Object> productThree = infoByProduct.stream().filter(o -> o.get("date").toString().equals(date) && o.get("product").toString().equals("productThree")).findFirst().orElse(null);
            Map<String, Object> productFour = infoByProduct.stream().filter(o -> o.get("date").toString().equals(date) && o.get("product").toString().equals("productFour")).findFirst().orElse(null);

            //为空,则用声明时的默认值;
            if(total != null) {
                totalCount.setCount((total.get(filed) != null) ? (T)total.get(filed) : initCount);
            }

            //获取字段
            if(productOne != null) {
                productOneCount.setCount((productOne.get(filed) != null) ? (T)productOne.get(filed) : initCount);
            }

            if(productTwo != null) {
                productTwoCount.setCount(productTwo.get(filed) != null ? (T)productTwo.get(filed) : initCount);
            }

            if(productThree != null) {
                productThreeCount.setCount(productThree.get(filed) != null ? (T)productThree.get(filed) : initCount);
            }

            if(productFour != null) {
                productFourCount.setCount(productFour.get(filed) != null ? (T)productFour.get(filed) : initCount);
            }

            totalCountList.add(totalCount);
            productOneCountList.add(productOneCount);
            productTwoCountList.add(productTwoCount);
            productThreeCountList.add(productThreeCount);
            productFourCountList.add(productFourCount);
        }

        resultProductVO.setTotalCountList(totalCountList);
        resultProductVO.setProductOneCountList(productOneCountList);
        resultProductVO.setProductTwoCountList(productTwoCountList);
        resultProductVO.setProductThreeCountList(productThreeCountList);
        resultProductVO.setProductFourCountList(productFourCountList);

        return resultProductVO;
    }
     其中Dates.getBetweenDates(startDate,endDate,false)的getBetweenDates这个静态方法就是获取两个日期之间所有的日期,如2017-01-21到2017-01-24,则最终返回2017-01-21, 2017-01-22,2017-01-23,2017-01-24的List集合。若选择不包含结束日期(false),则去掉2017-01-24,只有21,22,23这3天;

 /**
     * 获取两个日期之间的日期,包含开始,可以控制是否包含结束日期
     * 若两个日期只差一天,或者就是当天,则是保留开始日期
     * @param start 开始日期
     * @param end 结束日期
     * @param isContainEndDate 是否包含结束时间
     * @return 日期集合
     */
    public static List<Date> getBetweenDates(Date start, Date end,boolean isContainEndDate) {
        List<Date> result = new ArrayList<>();

        //若开始比结束要晚,则返回空list;
        if(start.after(end)) {
            return Lists.newArrayList();
        }

        //先添加开始日期;
        result.add(start);

        //存储临时的开始和结束日期
        Calendar tempStart = Calendar.getInstance();
        tempStart.setTime(start);
        Calendar tempEnd = Calendar.getInstance();
        tempEnd.setTime(end);

        //若开始日期比结束日期早,则依次添加;
        //若相等,则算了;
        while (tempStart.before(tempEnd)) {
            //开始日期+1;
            tempStart.add(Calendar.DAY_OF_YEAR, 1);
            result.add(tempStart.getTime());
        }

        //若不包含结束日期,且记录多余2条,则去掉结束日期
        if(!isContainEndDate && result.size() > 1) {
            result.remove(result.size()-1);
        }

        return result;
    }

      以上就是我用泛型部分的相关代码了,核心部分就是splitProductDetail这里了,最开始我是写了3,4个这样的方法,代码内容都差不多,就是最终返回的是一个实体还是两个实体的区别(可以看到申请购买是只有count的,只有一个实体,而成功订单是有count和amount的,是有两个实体的),以及amount和count是0.0和0的区别。但是就是这些的区别导致我一开始不知道如何将他们合成一个方法。后来写完之后,学习了一下泛型的使用,就试着这么用了,在泛型的使用上,我一直试图给T初始化,后来发现实在不行,于是决定还是传参数,最终写出了这个还算通用的方法,虽然参数有些多。

      勉励之~

猜你喜欢

转载自blog.csdn.net/liuyanlinglanq/article/details/79187859