浏览代码

接入广告组数据

ZHAOXIAN 1 年之前
父节点
当前提交
3f9c44b580
共有 15 个文件被更改,包括 508 次插入 和 13 次删除
  1. 29 3
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/controller/KuaiShouController.java
  2. 16 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/constant/KuaishouLinkConstants.java
  3. 147 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/entity/KuaishouAdgroupReportHourly.java
  4. 13 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/mapper/GroupReportMapper.java
  5. 3 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/mapper/KuaiShouRefreshTokenMapper.java
  6. 75 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/mapper/xml/GroupReportMapper.xml
  7. 8 4
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/mapper/xml/KuaiShouRefreshTokenMapper.xml
  8. 10 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/service/GroupReportService.java
  9. 2 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/service/IKuaiShouRefreshTokenService.java
  10. 84 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/service/impl/GroupReportServiceImpl.java
  11. 5 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/service/impl/KuaiShouRefreshTokenServiceImpl.java
  12. 24 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/utils/DateUtils.java
  13. 51 0
      job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/handler/GroupReportJob.java
  14. 2 2
      job-kuaishou/src/main/resources/application.yml
  15. 39 4
      xxl-job-core/src/main/java/com/xxl/job/core/util/DateUtil.java

+ 29 - 3
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/controller/KuaiShouController.java

@@ -1,10 +1,11 @@
 package cn.com.ctop.job.kuaishou.controller;
 
+import cn.com.ctop.job.kuaishou.data.service.GroupReportService;
 import cn.com.ctop.job.kuaishou.data.service.IKuaiShouRefreshTokenService;
+import cn.com.ctop.job.kuaishou.data.utils.Check;
 import com.alibaba.fastjson.JSONObject;
 import io.swagger.annotations.Api;
 import lombok.extern.slf4j.Slf4j;
-
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -18,12 +19,15 @@ import org.springframework.web.bind.annotation.RestController;
 
 @RestController
 @RequestMapping("/test")
-@Api(tags = "头条")
+@Api(tags = "快手")
 @Slf4j
 public class KuaiShouController {
     @Autowired
     private IKuaiShouRefreshTokenService refreshTokenService;
 
+    @Autowired
+    private GroupReportService groupReportService;
+
     @GetMapping("/testRefreshToken")
     public JSONObject testRefreshToken() {
         refreshTokenService.kuaiSHouRefreshToken();
@@ -31,4 +35,26 @@ public class KuaiShouController {
     }
 
 
-}
+    @GetMapping("/queryGroupReportHourly")
+    public void queryGroupReportHourly(Long advertiserId, String startDate, String endDate) {
+        String token = refreshTokenService.getToken(advertiserId);
+        if (Check.isNull(token)) {
+            log.error("advertiserId:{} 获取token失败!!!", advertiserId);
+            return;
+        }
+        groupReportService.queryGroupReportHourly(token, advertiserId, startDate, endDate, 1);
+
+        /*String date = DateUtil.formatDate(new Date());
+        List<Long> advertiserIds = groupReportService.getAdvertiserIdList(1);
+        advertiserIds.forEach(id -> {
+            String token = refreshTokenService.getToken(id);
+            if (Check.isNull(token)) {
+                log.error("advertiserId:{} 获取token失败!!!", id);
+                return;
+            }
+            groupReportService.queryGroupReportHourly(token, id, date, date, 1);
+        });*/
+    }
+
+
+}

+ 16 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/constant/KuaishouLinkConstants.java

@@ -0,0 +1,16 @@
+package cn.com.ctop.job.kuaishou.data.constant;
+
+/**
+ * URL
+ */
+public class KuaishouLinkConstants {
+
+    //快手 url前缀
+    public static final String KS_PREFIX = "https://ad.e.kuaishou.com";
+
+    /**
+     * 广告组数据 POST
+     */
+    public static final String V1_GROUP_REPORT = "/rest/openapi/v1/report/unit_report";
+
+}

+ 147 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/entity/KuaishouAdgroupReportHourly.java

@@ -0,0 +1,147 @@
+package cn.com.ctop.job.kuaishou.data.entity;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+public class KuaishouAdgroupReportHourly implements Serializable {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * 广告主id
+     */
+    private Long advertiserId;
+
+    /**
+     * 计划ID
+     */
+    private Long campaignId;
+
+    /**
+     * 计划名称
+     */
+    private String campaignName;
+
+    /**
+     * 广告组ID
+     */
+    private Long adgroupId;
+
+    /**
+     * 广告组名称
+     */
+    private String adgroupName;
+
+    /**
+     * 数据日期,格式:YYYYMMDD
+     */
+    private Long statDate;
+
+    /**
+     * 小时
+     */
+    private Long hour;
+
+    /**
+     * 花费(元)
+     */
+    private BigDecimal charge;
+
+    /**
+     * 行为数
+     */
+    private Long bclick;
+
+    /**
+     * 应用下载数据-激活数
+     */
+    private Long activation;
+
+    /**
+     * 应用下载数据-首日付费次数
+     */
+    private Long eventPayFirstDay;
+
+    /**
+     * 应用下载数据-首日付费金额
+     */
+    private BigDecimal eventPayPurchaseAmountFirstDay;
+
+    /**
+     * 应用下载数据-付费次数
+     */
+    private Long eventPay;
+
+    /**
+     * 应用下载数据-付费金额
+     */
+    private BigDecimal eventPayPurchaseAmount;
+
+    /**
+     * 应用下载数据-注册数
+     */
+    private Long eventRegister;
+
+    /**
+     * 新增付费人数
+     */
+    private BigDecimal eventNewUserPay;
+
+    /**
+     * 关键行为
+     */
+    private Long keyAction;
+
+    /**
+     * 转化数(回传时间)
+     */
+    private Long conversionNum;
+
+    /**
+     * 转化数(计费时间)
+     */
+    private Long conversionNumByImpression7d;
+
+    /**
+     * 转化成本(回传时间归因)
+     */
+    private BigDecimal conversionNumCost;
+
+    /**
+     * 深度转化数(回传时间归因)
+     */
+    private Long deepConversionNum;
+
+    /**
+     * 深度转化数(7日曝光时间归因)
+     */
+    private Long deepConversionNumByImpression7d;
+
+    /**
+     * 播放完成
+     */
+    private Long playedEnd;
+
+    /**
+     * 有效播放数
+     */
+    private Long playedThreeSeconds;
+
+    /**
+     * 广告曝光
+     */
+    private BigDecimal adShow;
+
+    /**
+     * IAA广告变现LTV
+     */
+    private BigDecimal minigameIaaPurchaseAmount;
+
+    /**
+     * 当日广告LTV
+     */
+    private BigDecimal minigameIaaPurchaseAmountFirstDay;
+}

+ 13 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/mapper/GroupReportMapper.java

@@ -0,0 +1,13 @@
+package cn.com.ctop.job.kuaishou.data.mapper;
+
+import cn.com.ctop.job.kuaishou.data.entity.KuaishouAdgroupReportHourly;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+public interface GroupReportMapper {
+
+    void replaceBatchGroupReportHourly(@Param("addList") List<KuaishouAdgroupReportHourly> addList);
+
+    List<Long> getAdvertiserIdList(@Param("type") Integer type);
+}

+ 3 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/mapper/KuaiShouRefreshTokenMapper.java

@@ -9,4 +9,7 @@ public interface KuaiShouRefreshTokenMapper {
     List<JSONObject> getTokenList();
 
     void updateTokenByUserId(@Param("userId") Long userId, @Param("updateAccessToken") String updateAccessToken, @Param("updateRefreshToken") String updateRefreshToken);
+
+    String getToken(@Param("advertiserId") Long advertiserId);
+
 }

+ 75 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/mapper/xml/GroupReportMapper.xml

@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="cn.com.ctop.job.kuaishou.data.mapper.GroupReportMapper">
+
+    <insert id="replaceBatchGroupReportHourly">
+        replace into media_api.kuaishou_adgroup_report_hourly
+        (
+        advertiser_id,
+        campaign_id,
+        campaign_name,
+        adgroup_id,
+        adgroup_name,
+        stat_date,
+        `hour`,
+        charge,
+        bclick,
+        activation,
+        event_pay_first_day,
+        event_pay_purchase_amount_first_day,
+        event_pay,
+        event_pay_purchase_amount,
+        event_register,
+        event_new_user_pay,
+        key_action,
+        conversion_num,
+        conversion_num_by_impression_7d,
+        conversion_num_cost,
+        deep_conversion_num,
+        deep_conversion_num_by_impression_7d,
+        played_end,
+        played_three_seconds,
+        ad_show,
+        minigame_iaa_purchase_amount,
+        minigame_iaa_purchase_amount_first_day
+        )
+        values
+        <foreach collection="addList" item="ad" separator=",">
+            (
+            #{ad.advertiserId},
+            #{ad.campaignId},
+            #{ad.campaignName},
+            #{ad.adgroupId},
+            #{ad.adgroupName},
+            #{ad.statDate},
+            #{ad.hour},
+            #{ad.charge},
+            #{ad.bclick},
+            #{ad.activation},
+            #{ad.eventPayFirstDay},
+            #{ad.eventPayPurchaseAmountFirstDay},
+            #{ad.eventPay},
+            #{ad.eventPayPurchaseAmount},
+            #{ad.eventRegister},
+            #{ad.eventNewUserPay},
+            #{ad.keyAction},
+            #{ad.conversionNum},
+            #{ad.conversionNumByImpression7d},
+            #{ad.conversionNumCost},
+            #{ad.deepConversionNum},
+            #{ad.deepConversionNumByImpression7d},
+            #{ad.playedEnd},
+            #{ad.playedThreeSeconds},
+            #{ad.adShow},
+            #{ad.minigameIaaPurchaseAmount},
+            #{ad.minigameIaaPurchaseAmountFirstDay}
+            )
+        </foreach>
+    </insert>
+
+    <select id="getAdvertiserIdList" resultType="java.lang.Long">
+        SELECT DISTINCT advertiser_id
+        FROM deepthink.kuaishou_advertiser_list
+        WHERE type = #{type}
+    </select>
+</mapper>

+ 8 - 4
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/mapper/xml/KuaiShouRefreshTokenMapper.xml

@@ -8,12 +8,16 @@
         WHERE admin_advertiser_id = #{userId}
     </update>
 
-
     <select id="getTokenList" resultType="com.alibaba.fastjson.JSONObject">
         SELECT admin_advertiser_id AS 'userId', access_token AS 'accessToken', refresh_token AS 'refreshToken', app_id AS 'appId', secret AS 'secret'
-
         FROM deepthink.kuaishou_auth_list
+    </select>
 
-
+    <select id="getToken" resultType="java.lang.String">
+        SELECT access_token
+        FROM deepthink.kuaishou_auth_list
+        WHERE admin_advertiser_id = (SELECT admin_advertiser_id
+                                     FROM deepthink.kuaishou_advertiser_list
+                                     WHERE advertiser_id = #{advertiserId} LIMIT 1)
     </select>
-</mapper>
+</mapper>

+ 10 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/service/GroupReportService.java

@@ -0,0 +1,10 @@
+package cn.com.ctop.job.kuaishou.data.service;
+
+import java.util.List;
+
+public interface GroupReportService {
+
+    void queryGroupReportHourly(String accessToken, Long advertiserId, String startDate, String endDate, int page);
+
+    List<Long> getAdvertiserIdList(Integer type);
+}

+ 2 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/service/IKuaiShouRefreshTokenService.java

@@ -2,4 +2,6 @@ package cn.com.ctop.job.kuaishou.data.service;
 
 public interface IKuaiShouRefreshTokenService {
     void kuaiSHouRefreshToken();
+
+    String getToken(Long advertiserId);
 }

+ 84 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/service/impl/GroupReportServiceImpl.java

@@ -0,0 +1,84 @@
+package cn.com.ctop.job.kuaishou.data.service.impl;
+
+import cn.com.ctop.job.kuaishou.data.constant.KuaishouLinkConstants;
+import cn.com.ctop.job.kuaishou.data.entity.KuaishouAdgroupReportHourly;
+import cn.com.ctop.job.kuaishou.data.mapper.GroupReportMapper;
+import cn.com.ctop.job.kuaishou.data.mapper.KuaiShouRefreshTokenMapper;
+import cn.com.ctop.job.kuaishou.data.service.GroupReportService;
+import cn.com.ctop.job.kuaishou.data.utils.Check;
+import cn.com.ctop.job.kuaishou.data.utils.DateUtils;
+import cn.com.ctop.job.kuaishou.data.utils.HttpUtils;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Slf4j
+@Service
+public class GroupReportServiceImpl implements GroupReportService {
+    @Autowired
+    private GroupReportMapper groupReportMapper;
+    @Autowired
+    private KuaiShouRefreshTokenMapper kuaiShouRefreshTokenMapper;
+
+    @Override
+    public void queryGroupReportHourly(String accessToken, Long advertiserId, String startDate, String endDate, int page) {
+        //广告组数据
+        String url = KuaishouLinkConstants.KS_PREFIX + KuaishouLinkConstants.V1_GROUP_REPORT;
+        Map<String, String> headers = new HashMap<>();
+        headers.put("Content-Type", "application/json");
+        headers.put("Access-Token", accessToken);
+        Map<String, Object> param = new HashMap<>();
+        if (!Check.isNull(startDate)) {
+            param.put("start_date", startDate);
+        }
+        if (!Check.isNull(endDate)) {
+            param.put("end_date", endDate);
+        }
+        param.put("advertiser_id", advertiserId);
+        param.put("temporal_granularity", "HOURLY");
+        param.put("page_size", 2000);
+        param.put("page", page);
+        String result = HttpUtils.httpPostRequest(url, param, headers);
+        JSONObject resultJson = JSONObject.parseObject(result);
+        Integer code = resultJson.getInteger("code");
+        String message = resultJson.getString("message");
+        if (null == code || code != 0) {
+            log.error("获取快手广告组报表异常:{},accountId:{}", message, advertiserId);
+            return;
+        }
+        JSONArray details = resultJson.getJSONObject("data").getJSONArray("details");
+        List<KuaishouAdgroupReportHourly> addList = new ArrayList<>();
+        if (!Check.isNull(details)) {
+            for (int i = 0; i < details.size(); i++) {
+                JSONObject detailJson = details.getJSONObject(i);
+                detailJson.put("ad_show", detailJson.getString("show"));
+                detailJson.put("hour", detailJson.getLong("stat_hour"));
+                detailJson.put("stat_date", Integer.valueOf(DateUtils.getIntegerDate(detailJson.getString("stat_date"))));
+                detailJson.put("adgroupId", detailJson.getLong("unit_id"));
+                detailJson.put("adgroupName", detailJson.getString("unit_name"));
+                KuaishouAdgroupReportHourly dailyGroup = JSONObject.toJavaObject(detailJson, KuaishouAdgroupReportHourly.class);
+                dailyGroup.setAdvertiserId(advertiserId);
+                addList.add(dailyGroup);
+            }
+        }
+        if (!Check.isNull(addList)) {
+            groupReportMapper.replaceBatchGroupReportHourly(addList);
+        }
+        if (details.size() == 2000) {
+            queryGroupReportHourly(accessToken, advertiserId, startDate, endDate, page + 1);
+        }
+    }
+
+    @Override
+    public List<Long> getAdvertiserIdList(Integer type) {
+        return groupReportMapper.getAdvertiserIdList(type);
+    }
+
+}

+ 5 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/service/impl/KuaiShouRefreshTokenServiceImpl.java

@@ -52,4 +52,9 @@ public class KuaiShouRefreshTokenServiceImpl implements IKuaiShouRefreshTokenSer
             }
         }
     }
+
+    @Override
+    public String getToken(Long advertiserId) {
+        return kuaiShouRefreshTokenMapper.getToken(advertiserId);
+    }
 }

+ 24 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/data/utils/DateUtils.java

@@ -4,6 +4,7 @@ import lombok.extern.slf4j.Slf4j;
 
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Date;
@@ -11,6 +12,9 @@ import java.util.List;
 
 @Slf4j
 public class DateUtils {
+    private static final SimpleDateFormat datetimeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+    public static final DateTimeFormatter FORMATTER_DATETIME_SECOND = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
 
     public static Integer getDateInteger(String date) {
         if (!Check.isNull(date)) {
@@ -109,4 +113,24 @@ public class DateUtils {
         }
         return dateList;
     }
+
+    /* //日期转换为时间戳 */
+    public static long timeToStamp(String time) {
+        Date d = new Date();
+        long timeStemp = 0;
+        try {
+            SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+            d = sf.parse(time);// 日期转换为时间戳
+        } catch (ParseException e) {
+
+            e.printStackTrace();
+        }
+        timeStemp = d.getTime();
+        return timeStemp;
+    }
+
+    public static void main(String[] args) {
+        String s = "2025-3-18 13:48:55";
+        System.out.println(timeToStamp(s));
+    }
 }

+ 51 - 0
job-kuaishou/src/main/java/cn/com/ctop/job/kuaishou/handler/GroupReportJob.java

@@ -0,0 +1,51 @@
+package cn.com.ctop.job.kuaishou.handler;
+
+
+import cn.com.ctop.job.kuaishou.data.service.GroupReportService;
+import cn.com.ctop.job.kuaishou.data.service.IKuaiShouRefreshTokenService;
+import cn.com.ctop.job.kuaishou.data.utils.Check;
+import com.xxl.job.core.handler.annotation.XxlJob;
+import com.xxl.job.core.util.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+
+/**
+ * 短剧广告报表
+ *
+ * @throws Exception
+ */
+@Slf4j
+@Component
+public class GroupReportJob {
+    @Autowired
+    private IKuaiShouRefreshTokenService kuaiShouRefreshTokenService;
+
+    @Autowired
+    private GroupReportService groupReportService;
+    static ExecutorService executorService = Executors.newFixedThreadPool(5);
+
+    @XxlJob("queryGroupReportHourlyJob")
+    public void queryGroupReportHourly() {
+        //当天
+        String date = DateUtil.formatDate(new Date());
+//        //昨天
+//        String lastStartDate = DateUtil.getLastDay(date, 1);
+
+        List<Long> advertiserIds = groupReportService.getAdvertiserIdList(1);
+        advertiserIds.forEach(advertiserId -> executorService.submit(() -> {
+            String token = kuaiShouRefreshTokenService.getToken(advertiserId);
+            if (Check.isNull(token)) {
+                log.error("advertiserId:{} 获取token失败!!!", advertiserId);
+                return;
+            }
+            groupReportService.queryGroupReportHourly(token, advertiserId, date, date, 1);
+        }));
+    }
+}

+ 2 - 2
job-kuaishou/src/main/resources/application.yml

@@ -80,8 +80,8 @@ spring:
         connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
       datasource:
         master:
-          url: jdbc:mysql://172.16.0.117:3390/media_api?characterEncoding=UTF-8&useUnicode=true&useSSL=false
-          #url: jdbc:mysql://8.147.135.80:3390/media_api?characterEncoding=UTF-8&useUnicode=true&useSSL=false
+          #url: jdbc:mysql://172.16.0.117:3390/media_api?characterEncoding=UTF-8&useUnicode=true&useSSL=false
+          url: jdbc:mysql://8.147.135.80:3390/media_api?characterEncoding=UTF-8&useUnicode=true&useSSL=false
           username: data
           password: hr@2025..
           driver-class-name: com.mysql.jdbc.Driver

+ 39 - 4
xxl-job-core/src/main/java/com/xxl/job/core/util/DateUtil.java

@@ -25,13 +25,14 @@ public class DateUtil {
     private static final String DATETIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
 
     private static final ThreadLocal<Map<String, DateFormat>> dateFormatThreadLocal = new ThreadLocal<Map<String, DateFormat>>();
+
     private static DateFormat getDateFormat(String pattern) {
-        if (pattern==null || pattern.trim().length()==0) {
+        if (pattern == null || pattern.trim().length() == 0) {
             throw new IllegalArgumentException("pattern cannot be empty.");
         }
 
         Map<String, DateFormat> dateFormatMap = dateFormatThreadLocal.get();
-        if(dateFormatMap!=null && dateFormatMap.containsKey(pattern)){
+        if (dateFormatMap != null && dateFormatMap.containsKey(pattern)) {
             return dateFormatMap.get(pattern);
         }
 
@@ -58,6 +59,32 @@ public class DateUtil {
     }
 
     /**
+     * 以time为节点,获取前几天的日期 yyyy-MM-dd
+     *
+     * @param
+     * @return java.lang.String
+     * @throws
+     * @author ZHAOXA
+     */
+    public static String getLastDay(String time, int days) {
+        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+        Calendar calendar = Calendar.getInstance();
+        Date date = null;
+        try {
+            date = sdf.parse(time);
+        } catch (ParseException e) {
+            e.printStackTrace();
+        }
+        calendar.setTime(date);
+        int day = calendar.get(Calendar.DATE);
+        //                      此处修改为+1则是获取后一天
+        calendar.set(Calendar.DATE, day - days);
+
+        String lastDay = sdf.format(calendar.getTime());
+        return lastDay;
+    }
+
+    /**
      * format date. like "yyyy-MM-dd HH:mm:ss"
      *
      * @param date
@@ -87,7 +114,7 @@ public class DateUtil {
      * @return
      * @throws ParseException
      */
-    public static Date parseDate(String dateString){
+    public static Date parseDate(String dateString) {
         return parse(dateString, DATE_FORMAT);
     }
 
@@ -153,4 +180,12 @@ public class DateUtil {
         return c.getTime();
     }
 
-}
+    public static void main(String[] args) {
+        String date = DateUtil.formatDate(new Date());
+
+        System.out.println(date);
+        String lastStartDate = DateUtil.getLastDay(date, -1);
+        System.out.println(lastStartDate);
+
+    }
+}