|
|
@@ -0,0 +1,458 @@
|
|
|
+package com.fs.his.service.impl;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.fs.his.domain.FsAppUserBehaviorDaily;
|
|
|
+import com.fs.his.domain.FsAppUserBehaviorLog;
|
|
|
+import com.fs.his.dto.UserBehaviorReportDTO;
|
|
|
+import com.fs.his.mapper.FsAppUserBehaviorDailyMapper;
|
|
|
+import com.fs.his.mapper.FsAppUserBehaviorLogMapper;
|
|
|
+import com.fs.his.param.UserBehaviorQueryParam;
|
|
|
+import com.fs.his.service.IUserBehaviorService;
|
|
|
+import com.fs.his.vo.UserBehaviorReportVO;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.math.RoundingMode;
|
|
|
+import java.text.SimpleDateFormat;
|
|
|
+import java.util.*;
|
|
|
+import java.util.regex.Matcher;
|
|
|
+import java.util.regex.Pattern;
|
|
|
+
|
|
|
+/**
|
|
|
+ * APP用户行为统计Service实现类
|
|
|
+ *
|
|
|
+ * 数据流程:
|
|
|
+ * 1. 前端埋点上报 -> fs_app_user_behavior_log(原始日志表)
|
|
|
+ * 2. 实时/定时聚合 -> fs_app_user_behavior_daily(日统计表)
|
|
|
+ * 3. 查询统计 -> 从日统计表聚合计算
|
|
|
+ *
|
|
|
+ * 两张表关系:
|
|
|
+ * - fs_app_user_behavior_log:原始埋点日志表,记录每次APP启动/切后台事件
|
|
|
+ * - fs_app_user_behavior_daily:日统计汇总表,每个用户每天一条记录
|
|
|
+ * - 关系:log表是原始数据,daily表是聚合结果;通过user_id和日期关联
|
|
|
+ *
|
|
|
+ * 事件类型说明:
|
|
|
+ * - event_type=1(启动):APP启动或从后台恢复时上报,启动次数+1
|
|
|
+ * - event_type=2(切后台):APP进入后台时上报,累加使用时长
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class UserBehaviorServiceImpl implements IUserBehaviorService {
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FsAppUserBehaviorLogMapper behaviorLogMapper;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private FsAppUserBehaviorDailyMapper behaviorDailyMapper;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 单条埋点上报
|
|
|
+ * 处理流程:
|
|
|
+ * 1. 插入原始日志表
|
|
|
+ * 2. 更新日统计表(启动次数+1 或 使用时长累加)
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public void reportBehavior(UserBehaviorReportDTO dto) {
|
|
|
+ Date eventTime = dto.getEventTime() != null ? dto.getEventTime() : new Date();
|
|
|
+ String sessionId = dto.getSessionId() != null ? dto.getSessionId() : generateSessionId(dto.getUserId());
|
|
|
+
|
|
|
+ FsAppUserBehaviorLog log = new FsAppUserBehaviorLog();
|
|
|
+ log.setUserId(dto.getUserId());
|
|
|
+ log.setSessionId(sessionId);
|
|
|
+ log.setEventType(dto.getEventType());
|
|
|
+ log.setEventTime(eventTime);
|
|
|
+ log.setDuration(dto.getDuration() != null ? dto.getDuration() : 0);
|
|
|
+ log.setAppVersion(parseAppVersion(dto.getAppVersion()));
|
|
|
+ behaviorLogMapper.insert(log);
|
|
|
+
|
|
|
+ dto.setEventTime(eventTime);
|
|
|
+ dto.setSessionId(sessionId);
|
|
|
+ updateDailySummary(dto);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成会话ID
|
|
|
+ */
|
|
|
+ private String generateSessionId(Long userId) {
|
|
|
+ return "session_" + System.currentTimeMillis() + "_" + userId;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 解析APP版本号,提取括号中的数值部分
|
|
|
+ * 输入格式:1.1.2(112) 或 2.0.0(200)
|
|
|
+ * 输出:112 或 200
|
|
|
+ */
|
|
|
+ private String parseAppVersion(String appVersion) {
|
|
|
+ if (appVersion == null || appVersion.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ // 匹配括号中的数字
|
|
|
+ Pattern pattern = Pattern.compile("\\((\\d+)\\)");
|
|
|
+ Matcher matcher = pattern.matcher(appVersion);
|
|
|
+ if (matcher.find()) {
|
|
|
+ return matcher.group(1);
|
|
|
+ }
|
|
|
+ // 如果没有括号,返回原始版本号
|
|
|
+ return appVersion;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量埋点上报
|
|
|
+ * 用于批量上传多条埋点数据,减少网络请求次数
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public void reportBehaviorBatch(List<UserBehaviorReportDTO> dtoList) {
|
|
|
+ if (dtoList == null || dtoList.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1. 批量插入原始日志表
|
|
|
+ List<FsAppUserBehaviorLog> logList = new ArrayList<>();
|
|
|
+ for (UserBehaviorReportDTO dto : dtoList) {
|
|
|
+ FsAppUserBehaviorLog log = new FsAppUserBehaviorLog();
|
|
|
+ log.setUserId(dto.getUserId());
|
|
|
+ log.setSessionId(dto.getSessionId());
|
|
|
+ log.setEventType(dto.getEventType());
|
|
|
+ log.setEventTime(dto.getEventTime());
|
|
|
+ log.setDuration(dto.getDuration() != null ? dto.getDuration() : 0);
|
|
|
+ log.setAppVersion(parseAppVersion(dto.getAppVersion()));
|
|
|
+ logList.add(log);
|
|
|
+ }
|
|
|
+ behaviorLogMapper.batchInsert(logList);
|
|
|
+
|
|
|
+ // 2. 逐条更新日统计表
|
|
|
+ for (UserBehaviorReportDTO dto : dtoList) {
|
|
|
+ updateDailySummary(dto);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 更新日统计表
|
|
|
+ * 根据事件类型更新对应字段:
|
|
|
+ * - event_type=1(启动):launch_count + 1
|
|
|
+ * - event_type=2(切后台):use_duration 累加传入的duration值
|
|
|
+ */
|
|
|
+ private void updateDailySummary(UserBehaviorReportDTO dto) {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ String dateStr = sdf.format(dto.getEventTime());
|
|
|
+
|
|
|
+ // 查询是否已存在该用户当天的记录
|
|
|
+ FsAppUserBehaviorDaily existing = findExistingDaily(dateStr, dto.getUserId());
|
|
|
+
|
|
|
+ if (existing == null) {
|
|
|
+ // 新建记录
|
|
|
+ existing = new FsAppUserBehaviorDaily();
|
|
|
+ try {
|
|
|
+ existing.setStatDate(sdf.parse(dateStr));
|
|
|
+ } catch (Exception e) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ existing.setUserId(dto.getUserId());
|
|
|
+ existing.setLaunchCount(0);
|
|
|
+ existing.setUseDuration(0);
|
|
|
+ // 判断是否为新用户(注册当天)
|
|
|
+ existing.setIsNewUser(isNewUser(dto.getUserId(), dto.getEventTime()) ? 1 : 0);
|
|
|
+ existing.setAppVersion(parseAppVersion(dto.getAppVersion()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据事件类型更新对应字段
|
|
|
+ switch (dto.getEventType()) {
|
|
|
+ case 1:
|
|
|
+ // APP启动:启动次数+1
|
|
|
+ existing.setLaunchCount(existing.getLaunchCount() + 1);
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ // APP切后台:累加使用时长(前端传入的duration)
|
|
|
+ if (dto.getDuration() != null && dto.getDuration() > 0) {
|
|
|
+ existing.setUseDuration(existing.getUseDuration() + dto.getDuration());
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存或更新
|
|
|
+ if (existing.getId() == null) {
|
|
|
+ List<FsAppUserBehaviorDaily> list = new ArrayList<>();
|
|
|
+ list.add(existing);
|
|
|
+ behaviorDailyMapper.batchInsertOrUpdate(list);
|
|
|
+ } else {
|
|
|
+ behaviorDailyMapper.updateById(existing);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询用户当天的日统计记录
|
|
|
+ */
|
|
|
+ private FsAppUserBehaviorDaily findExistingDaily(String dateStr, Long userId) {
|
|
|
+ try {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ Date statDate = sdf.parse(dateStr);
|
|
|
+ LambdaQueryWrapper<FsAppUserBehaviorDaily> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(FsAppUserBehaviorDaily::getUserId, userId)
|
|
|
+ .eq(FsAppUserBehaviorDaily::getStatDate, statDate);
|
|
|
+ return behaviorDailyMapper.selectOne(wrapper);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断用户是否为新用户
|
|
|
+ * 新用户定义:用户的app_create_time日期等于事件发生日期
|
|
|
+ */
|
|
|
+ private boolean isNewUser(Long userId, Date eventTime) {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ String dateStr = sdf.format(eventTime);
|
|
|
+ Integer result = behaviorDailyMapper.checkIsNewUser(userId, dateStr);
|
|
|
+ return result != null && result == 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取时间范围内的汇总统计数据
|
|
|
+ * 返回整个时间范围的聚合指标
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public UserBehaviorReportVO getBehaviorSummary(UserBehaviorQueryParam param) {
|
|
|
+ String startDate = param.getStartDate();
|
|
|
+ String endDate = param.getEndDate();
|
|
|
+
|
|
|
+ // 默认查询近30天
|
|
|
+ if (startDate == null || endDate == null) {
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ endDate = sdf.format(cal.getTime());
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, -29);
|
|
|
+ startDate = sdf.format(cal.getTime());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 转换用户类型参数
|
|
|
+ Integer isNewUser = convertUserType(param.getUserType());
|
|
|
+ String appVersion = param.getAppVersion();
|
|
|
+
|
|
|
+ UserBehaviorReportVO vo = new UserBehaviorReportVO();
|
|
|
+
|
|
|
+ // 1. 查询启动次数分布
|
|
|
+ List<Map<String, Object>> launchDist = behaviorDailyMapper.selectLaunchCountDistribution(startDate, endDate, isNewUser, appVersion);
|
|
|
+ if (launchDist != null && !launchDist.isEmpty()) {
|
|
|
+ Map<String, Object> row = launchDist.get(0);
|
|
|
+ vo.setLaunchCount1(toLong(row.get("count_1")));
|
|
|
+ vo.setLaunchCount23(toLong(row.get("count_2_3")));
|
|
|
+ vo.setLaunchCount4Plus(toLong(row.get("count_4_plus")));
|
|
|
+ } else {
|
|
|
+ vo.setLaunchCount1(0L);
|
|
|
+ vo.setLaunchCount23(0L);
|
|
|
+ vo.setLaunchCount4Plus(0L);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 查询使用时长分布
|
|
|
+ List<Map<String, Object>> durationDist = behaviorDailyMapper.selectUseDurationDistribution(startDate, endDate, isNewUser, appVersion);
|
|
|
+ if (durationDist != null && !durationDist.isEmpty()) {
|
|
|
+ Map<String, Object> row = durationDist.get(0);
|
|
|
+ vo.setDurationLt30(toLong(row.get("count_lt_30")));
|
|
|
+ vo.setDuration30To60(toLong(row.get("count_30_60")));
|
|
|
+ vo.setDuration60To120(toLong(row.get("count_60_120")));
|
|
|
+ vo.setDuration120To240(toLong(row.get("count_120_240")));
|
|
|
+ vo.setDurationGt240(toLong(row.get("count_gt_240")));
|
|
|
+ } else {
|
|
|
+ vo.setDurationLt30(0L);
|
|
|
+ vo.setDuration30To60(0L);
|
|
|
+ vo.setDuration60To120(0L);
|
|
|
+ vo.setDuration120To240(0L);
|
|
|
+ vo.setDurationGt240(0L);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 查询平均访问间隔
|
|
|
+ BigDecimal avgInterval = behaviorDailyMapper.selectAvgVisitInterval(startDate, endDate, isNewUser, appVersion);
|
|
|
+ vo.setAvgVisitInterval(avgInterval != null ? avgInterval.setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO);
|
|
|
+
|
|
|
+ // 4. 查询沉默用户唤醒率
|
|
|
+ String beforeDate = getBeforeDate(startDate, 30);
|
|
|
+ Long silentWaked = behaviorDailyMapper.selectSilentUsers(beforeDate, startDate, endDate, isNewUser, appVersion);
|
|
|
+ Long totalSilent = behaviorDailyMapper.selectTotalSilentUsers(beforeDate, startDate, isNewUser, appVersion);
|
|
|
+ vo.setSilentWakeUpRate(calculateRate(silentWaked, totalSilent));
|
|
|
+
|
|
|
+ return vo;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取按日统计的行为数据报表
|
|
|
+ * 返回时间范围内每一天的统计数据
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public List<UserBehaviorReportVO> getDailyBehaviorReport(UserBehaviorQueryParam param) {
|
|
|
+ String startDate = param.getStartDate();
|
|
|
+ String endDate = param.getEndDate();
|
|
|
+
|
|
|
+ // 默认查询近30天
|
|
|
+ if (startDate == null || endDate == null) {
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ endDate = sdf.format(cal.getTime());
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, -29);
|
|
|
+ startDate = sdf.format(cal.getTime());
|
|
|
+ }
|
|
|
+
|
|
|
+ Integer isNewUser = convertUserType(param.getUserType());
|
|
|
+ String appVersion = param.getAppVersion();
|
|
|
+
|
|
|
+ // 查询每日统计数据
|
|
|
+ List<Map<String, Object>> dailyData = behaviorDailyMapper.selectDailyBehaviorReport(startDate, endDate, isNewUser, appVersion);
|
|
|
+
|
|
|
+ // 计算整个时间范围的平均访问间隔(每日报表显示相同的值)
|
|
|
+ BigDecimal avgIntervalTotal = behaviorDailyMapper.selectAvgVisitInterval(startDate, endDate, isNewUser, appVersion);
|
|
|
+ BigDecimal avgIntervalValue = avgIntervalTotal != null ? avgIntervalTotal.setScale(2, RoundingMode.HALF_UP) : BigDecimal.ZERO;
|
|
|
+
|
|
|
+ // 转换为Map便于查找
|
|
|
+ Map<String, UserBehaviorReportVO> dailyMap = new LinkedHashMap<>();
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ for (Map<String, Object> row : dailyData) {
|
|
|
+ UserBehaviorReportVO vo = new UserBehaviorReportVO();
|
|
|
+ Object dateObj = row.get("statDate");
|
|
|
+ if (dateObj instanceof Date) {
|
|
|
+ vo.setStatDate((Date) dateObj);
|
|
|
+ }
|
|
|
+ vo.setLaunchCount1(toLong(row.get("launchCount1")));
|
|
|
+ vo.setLaunchCount23(toLong(row.get("launchCount23")));
|
|
|
+ vo.setLaunchCount4Plus(toLong(row.get("launchCount4Plus")));
|
|
|
+ vo.setDurationLt30(toLong(row.get("durationLt30")));
|
|
|
+ vo.setDuration30To60(toLong(row.get("duration30To60")));
|
|
|
+ vo.setDuration60To120(toLong(row.get("duration60To120")));
|
|
|
+ vo.setDuration120To240(toLong(row.get("duration120To240")));
|
|
|
+ vo.setDurationGt240(toLong(row.get("durationGt240")));
|
|
|
+
|
|
|
+ String dateKey = dateObj instanceof Date ? sdf.format((Date) dateObj) : String.valueOf(dateObj);
|
|
|
+ dailyMap.put(dateKey, vo);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成完整的日期列表
|
|
|
+ List<String> allDates = new ArrayList<>();
|
|
|
+ try {
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
+ cal.setTime(sdf.parse(startDate));
|
|
|
+ Calendar endCal = Calendar.getInstance();
|
|
|
+ endCal.setTime(sdf.parse(endDate));
|
|
|
+ while (!cal.after(endCal)) {
|
|
|
+ allDates.add(sdf.format(cal.getTime()));
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建结果列表,确保每天都有数据
|
|
|
+ List<UserBehaviorReportVO> result = new ArrayList<>();
|
|
|
+ for (String dateStr : allDates) {
|
|
|
+ UserBehaviorReportVO vo = dailyMap.get(dateStr);
|
|
|
+ if (vo == null) {
|
|
|
+ // 没有数据的日期填充0
|
|
|
+ vo = new UserBehaviorReportVO();
|
|
|
+ try {
|
|
|
+ vo.setStatDate(sdf.parse(dateStr));
|
|
|
+ } catch (Exception e) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ vo.setLaunchCount1(0L);
|
|
|
+ vo.setLaunchCount23(0L);
|
|
|
+ vo.setLaunchCount4Plus(0L);
|
|
|
+ vo.setDurationLt30(0L);
|
|
|
+ vo.setDuration30To60(0L);
|
|
|
+ vo.setDuration60To120(0L);
|
|
|
+ vo.setDuration120To240(0L);
|
|
|
+ vo.setDurationGt240(0L);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算沉默用户唤醒率(单日数据)
|
|
|
+ String beforeDate = getBeforeDate(dateStr, 30);
|
|
|
+ String nextDate = getNextDate(dateStr);
|
|
|
+ Long silentWaked = behaviorDailyMapper.selectSilentUsers(beforeDate, dateStr, nextDate, isNewUser, appVersion);
|
|
|
+ Long totalSilent = behaviorDailyMapper.selectTotalSilentUsers(beforeDate, dateStr, isNewUser, appVersion);
|
|
|
+ vo.setSilentWakeUpRate(calculateRate(silentWaked, totalSilent));
|
|
|
+
|
|
|
+ // 访问间隔使用整个时间范围的统计值
|
|
|
+ vo.setAvgVisitInterval(avgIntervalValue);
|
|
|
+
|
|
|
+ result.add(vo);
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 转换用户类型参数
|
|
|
+ * 前端传入:1=新用户,2=老用户
|
|
|
+ * 数据库存储:1=新用户,0=老用户
|
|
|
+ */
|
|
|
+ private Integer convertUserType(Integer userType) {
|
|
|
+ if (userType == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (userType == 1) {
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ if (userType == 2) {
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Object转Long
|
|
|
+ */
|
|
|
+ private Long toLong(Object obj) {
|
|
|
+ if (obj == null) return 0L;
|
|
|
+ if (obj instanceof Number) {
|
|
|
+ return ((Number) obj).longValue();
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ return Long.parseLong(obj.toString());
|
|
|
+ } catch (Exception e) {
|
|
|
+ return 0L;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算百分比
|
|
|
+ */
|
|
|
+ private BigDecimal calculateRate(Long part, Long total) {
|
|
|
+ if (total == null || total == 0 || part == null) {
|
|
|
+ return BigDecimal.ZERO;
|
|
|
+ }
|
|
|
+ return new BigDecimal(part).multiply(new BigDecimal(100))
|
|
|
+ .divide(new BigDecimal(total), 2, RoundingMode.HALF_UP);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取指定日期前N天的日期
|
|
|
+ */
|
|
|
+ private String getBeforeDate(String dateStr, int days) {
|
|
|
+ try {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
+ cal.setTime(sdf.parse(dateStr));
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, -days);
|
|
|
+ return sdf.format(cal.getTime());
|
|
|
+ } catch (Exception e) {
|
|
|
+ return dateStr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取指定日期的下一天
|
|
|
+ */
|
|
|
+ private String getNextDate(String dateStr) {
|
|
|
+ try {
|
|
|
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
+ cal.setTime(sdf.parse(dateStr));
|
|
|
+ cal.add(Calendar.DAY_OF_MONTH, 1);
|
|
|
+ return sdf.format(cal.getTime());
|
|
|
+ } catch (Exception e) {
|
|
|
+ return dateStr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|