From 5d7b65670282a4fad015e37d567cfa171b162052 Mon Sep 17 00:00:00 2001
From: huliguo <2023611923@qq.com>
Date: 星期二, 20 五月 2025 12:25:19 +0800
Subject: [PATCH] 基础代码

---
 pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java |  557 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 557 insertions(+), 0 deletions(-)

diff --git a/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java
new file mode 100644
index 0000000..98b186f
--- /dev/null
+++ b/pt-errand/src/main/java/com/ruoyi/errand/service/impl/AppUserServiceImpl.java
@@ -0,0 +1,557 @@
+package com.ruoyi.errand.service.impl;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.errand.constant.*;
+import com.ruoyi.errand.domain.AppUser;
+
+import com.ruoyi.errand.domain.Order;
+import com.ruoyi.errand.domain.UserCancellationLog;
+import com.ruoyi.errand.domain.VipOrder;
+import com.ruoyi.errand.mapper.AppUserMapper;
+
+import com.ruoyi.errand.mapper.OrderMapper;
+import com.ruoyi.errand.mapper.UserCancellationLogMapper;
+import com.ruoyi.errand.mapper.VipOrderMapper;
+import com.ruoyi.errand.object.dto.app.AppletLogin;
+import com.ruoyi.errand.object.dto.app.BirthDayDTO;
+import com.ruoyi.errand.object.dto.app.MobileLoginDTO;
+import com.ruoyi.errand.object.dto.app.RegisterDTO;
+import com.ruoyi.errand.object.dto.sys.AppUserPageListDTO;
+import com.ruoyi.errand.object.vo.app.AppUserInfoVO;
+import com.ruoyi.errand.object.vo.app.OrderPageVO;
+import com.ruoyi.errand.object.vo.app.UserTopInfoVO;
+import com.ruoyi.errand.object.vo.login.LoginVO;
+import com.ruoyi.errand.object.vo.sys.AppUserPageListVO;
+import com.ruoyi.errand.object.vo.sys.AppUserSysDetailVO;
+import com.ruoyi.errand.object.vo.sys.OrderPageListVO;
+import com.ruoyi.errand.object.vo.sys.UserStatsVO;
+import com.ruoyi.errand.service.AppUserService;
+import com.ruoyi.errand.utils.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalUnit;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Slf4j
+@Service
+public class AppUserServiceImpl extends ServiceImpl<AppUserMapper, AppUser> implements AppUserService {
+    @Resource
+    private RedisService redisService;
+
+    @Resource
+    private WeChatUtil weChatUtil;
+    private static final String DEFAULT_AVATAR_URL = "http://qijishenghuiyuan.obs.cn-southwest-2.myhuaweicloud.com/admin/aedfbbb41280471f8d9fa7905298b65f.png";
+    @Autowired
+    private UserCancellationLogMapper userCancellationLogMapper;
+    @Autowired
+    private OrderMapper orderMapper;
+    @Autowired
+    private VipOrderMapper vipOrderMapper;
+
+
+    @Override
+    public void getSMSCode(String phone) {
+        //校验验证码获取频率(1分钟5次)
+        String key = "app" + "&" + phone;
+        Map<String, Object> cacheMap = redisService.getCacheMap(key);
+        if(null != cacheMap && cacheMap.size() > 0){
+            Integer number = Integer.valueOf(cacheMap.get("number").toString()) + 1;
+            Long startTime = Long.valueOf(cacheMap.get("startTime").toString());
+            if(number > 5 && (System.currentTimeMillis() - startTime) < 60000){
+                throw new ServiceException("获取验证码太频繁,请稍后重试!");
+            }
+            if(number <= 5){
+                cacheMap.put("number", number);
+            }else{
+                cacheMap.put("number", 1);
+                cacheMap.put("startTime", System.currentTimeMillis());
+            }
+        }else{
+            cacheMap = new HashMap<>();
+            cacheMap.put("number", 1);
+            cacheMap.put("startTime", System.currentTimeMillis());
+        }
+        //存储计数器到缓存中,5分钟有效期
+        redisService.setCacheMap(key, cacheMap, 300);
+
+        //开始构建验证码内容
+        String code = "";
+        for (int i = 0; i < 6; i++) {
+            code += Double.valueOf(Math.random() * 10).intValue();
+        }
+        SMSUtil.sendSms("[\"" + code + "\"]", phone, "8824121211029", "39533d100b2b4aee8ed198aa49fe99dd");
+        redisService.setCacheObject(phone, code, 300L, TimeUnit.SECONDS);
+    }
+
+    @Override
+    public R<LoginVO> mobileLogin(MobileLoginDTO mobileLogin) {
+        String code = redisService.getCacheObject(mobileLogin.getPhone());
+        if(!"999999".equals(mobileLogin.getCode())){
+            if(null == code || !code.equals(mobileLogin.getCode())){
+                throw new ServiceException("验证码错误");
+            }
+        }
+        //查看用户是否存在
+        AppUser appUser = this.getOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getPhone, mobileLogin.getPhone()));
+        if (null == appUser || appUser.getDelFlag().equals(DelFlagConstant.DELETE)) {
+            //用户不存在
+            //使用jscode获取微信openid
+            Map<String, Object> map = weChatUtil.code2Session(mobileLogin.getJscode());
+            Integer errcode = Integer.valueOf(map.get("errcode").toString());
+            if(0 != errcode){
+                throw new ServiceException(map.get("msg").toString());
+            }
+            String openid = map.get("openid").toString();
+            //注册一个
+            appUser = new AppUser();
+            appUser.setPhone(mobileLogin.getPhone());
+            appUser.setDelFlag(DelFlagConstant.UNDELETE);
+            appUser.setStatus(AppUserStatusConstant.NORMAL);
+            appUser.setWxOpenid(openid);
+            appUser.setAvatar(DEFAULT_AVATAR_URL);
+            appUser.setFirstLogin(IsFirstLoginConstant.YES);
+            appUser.setFirstOrder(IsFirstOrder.YES);
+            this.save(appUser);
+        }
+        if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.FREEZE)) {
+            throw new ServiceException("该账户已被冻结");
+        }
+        if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.LOGOUT)) {
+            throw new ServiceException("该账户已被注销");
+        }
+        appUser.setLastLoginTime(LocalDateTime.now());
+        this.updateById(appUser);
+        //构建token
+        Map<String,Object> map = new HashMap<>();
+        map.put("userId",appUser.getId());
+        Map<String, Object> jwt = JwtUtil.createJWT(map);
+
+
+        LoginVO loginVO=new LoginVO();
+        loginVO.setToken("app:" + jwt.get("token").toString());
+        loginVO.setFailureTime(TimeUnit.MILLISECONDS.toSeconds((long)jwt.get("exp")));
+        loginVO.setPhone(appUser.getPhone());
+        loginVO.setSkipPage(appUser.getFirstLogin());
+        loginVO.setIsCourier(appUser.getCourierId()!=null);
+        return R.ok(loginVO);
+    }
+
+    @Override
+    public R<LoginVO> appletLogin(AppletLogin appletLogin) {
+        //使用jscode获取微信openid
+        Map<String, Object> map = weChatUtil.code2Session(appletLogin.getJscode());
+        Integer errcode = Integer.valueOf(map.get("errcode").toString());
+        if(0 != errcode){
+            throw new ServiceException(map.get("msg").toString());
+        }
+        String openid = map.get("openid").toString();
+        String sessionKey = map.get("sessionKey").toString();
+        //查询用户是否注册,没有注册则注册
+        AppUser appUser = this.getOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getWxOpenid, openid));
+        if(null == appUser|| appUser.getDelFlag().equals(DelFlagConstant.DELETE)){
+            appUser = new AppUser();
+            //注册
+            //获取手机号
+            String decrypt = WXCore.decrypt(appletLogin.getEncryptedData_phone(), sessionKey, appletLogin.getIv_phone());
+            if (StringUtils.isEmpty(decrypt)) {
+                return R.fail("获取手机信息失败");
+            }
+            JSONObject phone = JSON.parseObject(decrypt);
+            String purePhoneNumber = phone.getString("phoneNumber");
+            //新用户默认信息
+            appUser = new AppUser();
+            appUser.setPhone(purePhoneNumber);
+            appUser.setDelFlag(DelFlagConstant.UNDELETE);
+            appUser.setStatus(AppUserStatusConstant.NORMAL);
+            appUser.setWxOpenid(openid);
+            appUser.setAvatar(DEFAULT_AVATAR_URL);
+            appUser.setFirstLogin(IsFirstLoginConstant.YES);
+            appUser.setFirstOrder(IsFirstOrder.YES);
+            this.save(appUser);
+
+        }
+
+        if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.FREEZE)) {
+            throw new ServiceException("该账户已被冻结");
+        }
+        if (Objects.equals(appUser.getStatus(), AppUserStatusConstant.LOGOUT)) {
+            throw new ServiceException("该账户已被注销");
+        }
+        //构建token
+        Map<String,Object> tokenMap = new HashMap<>();
+        map.put("userId",appUser.getId());
+        Map<String, Object> jwt = JwtUtil.createJWT(tokenMap);
+
+
+        LoginVO loginVO=new LoginVO();
+        loginVO.setToken("app:" + jwt.get("token").toString());
+        loginVO.setFailureTime(TimeUnit.MILLISECONDS.toSeconds((long)jwt.get("exp")));
+        loginVO.setPhone(appUser.getPhone());
+        loginVO.setSkipPage(appUser.getFirstLogin());
+        return R.ok(loginVO);
+    }
+
+    @Override
+    public void register(RegisterDTO registerDTO) {
+        //注册 修改用户信息
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        appUser.setName(registerDTO.getUsername());
+        appUser.setFirstLogin(IsFirstLoginConstant.NO);
+        appUser.setCommunityId(registerDTO.getCommunityId());
+        this.updateById(appUser);
+
+    }
+
+    @Override
+    public OrderPageVO getOrderPage(Integer communityId) {
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        //更换绑定的小区
+        if (communityId!=null){
+            appUser.setCommunityId(communityId);
+            this.updateById(appUser);
+        }
+        List<OrderPageVO> orderPage = this.getBaseMapper().getOrderPage(appUser.getId());
+
+        return orderPage.get(0);
+    }
+
+    @Override
+    public AppUserInfoVO getMyInfo() {
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        return this.getBaseMapper().getMyInfo(appUser.getId());
+    }
+
+    @Override
+    public void setSex(Integer sex) {
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        if (appUser.getSex().equals(sex)) {
+            throw new ServiceException("修改的性别与当前性别重复");
+        }
+        appUser.setSex(sex);
+
+        updateById(appUser);
+
+    }
+
+    @Override
+    public void setBirthDay(BirthDayDTO birth) {
+        AppUser appUser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        appUser.setBirthday(birth.getBirthday());
+        updateById(appUser);
+    }
+
+    /**
+     * 注销账号
+     */
+    @Override
+    public void delete() {
+        AppUser appuser = (AppUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        appuser.setDelFlag(DelFlagConstant.DELETE);
+        appuser.setStatus(AppUserStatusConstant.LOGOUT);
+        this.updateById(appuser);
+        UserCancellationLog userCancellationLog=new UserCancellationLog();
+        userCancellationLog.setApp_user_id(appuser.getId());
+        userCancellationLog.setDel_flag(DelFlagConstant.DELETE);
+        userCancellationLog.setCreate_time(LocalDateTime.now());
+        userCancellationLogMapper.insert(userCancellationLog);
+    }
+
+    @Override
+    public UserStatsVO getUserStats(LocalDateTime start, LocalDateTime end, String datePattern) {
+        UserStatsVO vo = new UserStatsVO();
+        // 1. 查询按时间分组的用户数
+        List<Map<String, Object>> stats = this.getBaseMapper().countGroupByDate(start, end, datePattern);
+
+        // 2. 生成完整的日期序列并填充数据
+        fillDateAndValue(vo, start, end, datePattern, stats);
+
+        return vo;
+    }
+
+    @Override
+    public UserTopInfoVO userTopInfo(LocalDateTime start, LocalDateTime end) {
+        UserTopInfoVO userTopInfoVO = new UserTopInfoVO();
+        // 1. 查询总用户数
+        userTopInfoVO.setTotalUsers(this.getBaseMapper().countByCreateTimeBefore(end));
+
+        // 2. 查询新增用户数
+        userTopInfoVO.setInsertUsers(this.getBaseMapper().countByCreateTimeBetween(start, end));
+        return userTopInfoVO;
+    }
+
+    @Override
+    public IPage<AppUserPageListVO> getAppUserPageList(AppUserPageListDTO dto) {
+        // 获取本月的开始时间和结束时间
+        LocalDate now = LocalDate.now();
+        LocalDateTime startOfMonth = now.withDayOfMonth(1).atStartOfDay();
+        LocalDateTime endOfMonth = now.withDayOfMonth(now.lengthOfMonth()).atTime(23, 59, 59);
+
+        IPage<AppUserPageListVO> page = new Page<>(dto.getPageNum(),dto.getPageSize());
+        IPage<AppUserPageListVO> iPage = this.getBaseMapper().getAppUserPageList(page, dto);
+        iPage.getRecords().forEach((record) -> {
+           //当月下单量
+            Long mouth = orderMapper.selectCount(new LambdaUpdateWrapper<Order>()
+                    .eq(Order::getAppUserId, record.getId())
+                    .eq(Order::getDelFlag, DelFlagConstant.UNDELETE)
+                    .between(Order::getOrderTime, startOfMonth, endOfMonth)
+            );
+            record.setMouth(mouth.intValue());
+
+            //总共下单量
+            Long total = orderMapper.selectCount(new LambdaUpdateWrapper<Order>()
+                    .eq(Order::getAppUserId, record.getId())
+                    .eq(Order::getDelFlag, DelFlagConstant.UNDELETE)
+            );
+            record.setTotal(total.intValue());
+        });
+        return iPage;
+    }
+
+    @Override
+    public AppUserSysDetailVO detail(Integer id) {
+        // 获取本月的开始时间和结束时间
+        LocalDate now = LocalDate.now();
+        LocalDateTime startOfMonth = now.withDayOfMonth(1).atStartOfDay();
+        LocalDateTime endOfMonth = now.withDayOfMonth(now.lengthOfMonth()).atTime(23, 59, 59);
+        AppUser appUser = this.getBaseMapper().selectById(id);
+        AppUserSysDetailVO vo = new AppUserSysDetailVO();
+        BeanUtils.copyProperties(appUser, vo);
+        int isVip =0;
+        if (appUser.getEndTime() != null) {
+            isVip = appUser.getEndTime().isAfter(LocalDateTime.now())?1:0;
+        }
+        vo.setIsVip(isVip);
+        //当月下单量
+        Long mouth = orderMapper.selectCount(new LambdaUpdateWrapper<Order>()
+                .eq(Order::getAppUserId, id)
+                .eq(Order::getDelFlag, DelFlagConstant.UNDELETE)
+                .between(Order::getOrderTime, startOfMonth, endOfMonth)
+        );
+        vo.setMouth(mouth.intValue());
+
+        //总共下单量
+        Long total = orderMapper.selectCount(new LambdaUpdateWrapper<Order>()
+                .eq(Order::getAppUserId,id)
+                .eq(Order::getDelFlag, DelFlagConstant.UNDELETE)
+        );
+        vo.setTotal(total.intValue());
+        return vo;
+    }
+
+    @Override
+    public void froze(Integer id) {
+        AppUser appUser = this.getBaseMapper().selectById(id);
+        if (appUser==null|| Objects.equals(appUser.getDelFlag(), DelFlagConstant.DELETE)) {
+            throw new ServiceException("用户不存在");
+        }
+
+        if (appUser.getStatus()==1 || appUser.getStatus()==2) {
+            appUser.setStatus(appUser.getStatus()==1?2:1);
+        }else {
+            throw new ServiceException("用户状态错误");
+        }
+
+        this.getBaseMapper().updateById(appUser);
+
+    }
+
+    @Override
+    public void refund(Integer id) {
+        // 1. 查询用户所有未退费的VIP订单(状态为4-已完成且未删除)
+        List<VipOrder> orders = vipOrderMapper.selectList(new LambdaQueryWrapper<VipOrder>()
+                .eq(VipOrder::getAppUserId, id)
+                .eq(VipOrder::getOrderStatus, 4) // 4-已完成
+                .eq(VipOrder::getDelFlag, DelFlagConstant.UNDELETE)     // 0-未删除
+                .isNull(VipOrder::getRefundTime) // 未退款的
+                .orderByDesc(VipOrder::getOrderTime));
+
+        // 2. 计算每个订单的有效期结束时间
+        Map<Long, LocalDateTime> orderEndTimes = calculateOrderEndTimes(orders);
+
+        // 3. 筛选出仍在有效期内的订单
+        LocalDateTime now = LocalDateTime.now();
+        List<VipOrder> refundVipOrderList = orders.stream()
+                .filter(order -> {
+                    LocalDateTime endTime = orderEndTimes.get(order.getId());
+                    return endTime != null && endTime.isAfter(now);
+                })
+                .collect(Collectors.toList());
+        if(refundVipOrderList.isEmpty()){
+            return;
+        }
+        // 4.对每个订单进行退款操作
+        refundVipOrderList.forEach(vipOrder -> {
+            //退款
+            log.info("开始会员退费,被退款人员id:{},退款订单:{}",vipOrder.getAppUserId(),vipOrder.getId());
+            R r = refundPayMoney(vipOrder);//退款
+            if (200 == r.getCode()) {
+                //退款成功取消用户的会员,将用户状态改变
+                AppUser appUser = this.getById(id);
+                appUser.setStartTime(LocalDateTime.now());
+                appUser.setEndTime(LocalDateTime.now());
+                appUser.setVipId(null);
+            }
+        });
+    }
+
+    @Override
+    public R refundPayMoneyCallback(RefundCallbackResult refundCallbackResult) {
+        String code = refundCallbackResult.getR3_RefundOrderNo().substring(1);
+        VipOrder vipOrder = vipOrderMapper.selectOne(new LambdaQueryWrapper<VipOrder>().eq(VipOrder::getOrderNumber, code));
+        if (null == vipOrder || vipOrder.getPayStatus() == 1 || vipOrder.getOrderStatus() == 6) {
+            return R.ok();
+        }
+        vipOrder.setRefundCode(refundCallbackResult.getR5_RefundTrxNo());
+        vipOrder.setRefundStatus(2);
+        vipOrder.setRefundTime(LocalDateTime.now());
+        vipOrder.setOrderStatus(6);//已退费
+        vipOrderMapper.updateById(vipOrder);
+        return R.ok();
+    }
+
+    private Map<Long, LocalDateTime> calculateOrderEndTimes(List<VipOrder> orders) {
+        Map<Long, LocalDateTime> result = new HashMap<>();
+        LocalDateTime currentEndTime = null;
+
+        // 按购买时间升序处理,计算叠加后的有效期
+        List<VipOrder> sortedOrders = orders.stream()
+                .sorted(Comparator.comparing(VipOrder::getOrderTime))
+                .collect(Collectors.toList());
+
+        for (VipOrder order : sortedOrders) {
+            int daysToAdd = getVipDays(order.getVipId());
+            if (daysToAdd == 0) continue;
+
+            LocalDateTime orderTime = order.getOrderTime();
+            LocalDateTime orderEndTime = orderTime.plusDays(daysToAdd)
+                    .withHour(23).withMinute(59).withSecond(59);
+
+            // 如果当前订单购买时间在上一个有效期之后,则重新计算
+            if (currentEndTime == null || orderTime.isAfter(currentEndTime)) {
+                currentEndTime = orderEndTime;
+            } else {
+                // 否则叠加有效期
+                currentEndTime = currentEndTime.plusDays(daysToAdd)
+                        .withHour(23).withMinute(59).withSecond(59);
+            }
+
+            result.put(order.getId(), currentEndTime);
+        }
+
+        return result;
+    }
+
+    private int getVipDays(Integer vipId) {
+        switch (vipId) {
+            case 1: return 31;
+            case 2: return 90;
+            case 3: return 180;
+            case 4: return 365;
+            default: return 0;
+        }
+    }
+    /**
+     * 返回订单支付金额
+     */
+    public R refundPayMoney(VipOrder order) {
+        //开始退款
+        BigDecimal paymentAmount = order.getPaymentAmount();
+        if (BigDecimal.ZERO.compareTo(order.getPaymentAmount()) < 0) {//支付的金额是否大于0
+            //微信退款
+            RefundResult refund = PaymentUtil.refund(order.getOrderNumber(), "R" + order.getOrderNumber(), paymentAmount.doubleValue(),
+                    "/app/user/refundPayMoneyCallback");
+            if (!"100".equals(refund.getRa_Status())) {
+                return R.fail(refund.getRc_CodeMsg());//退款失败
+            }
+        }
+        return R.ok();
+    }
+
+    private void fillDateAndValue(UserStatsVO vo, LocalDateTime start, LocalDateTime end,
+                                  String datePattern, List<Map<String, Object>> stats) {
+        List<String> allDates = generateDateRange(start, end, datePattern);
+        Map<String, Integer> statMap = convertStatsToMap(stats);
+
+        List<String> dates = new ArrayList<>();
+        List<Integer> values = new ArrayList<>();
+
+        for (String date : allDates) {
+            dates.add(date);
+            values.add(statMap.getOrDefault(date, 0)); // 无数据的日期补0
+        }
+
+        vo.setDate(dates);
+        vo.setValue(values);
+    }
+
+    private Map<String, Integer> convertStatsToMap(List<Map<String, Object>> stats) {
+        return stats.stream()
+                .collect(Collectors.toMap(
+                        stat -> String.valueOf(stat.get("date")),
+                        stat -> Integer.valueOf(String.valueOf(stat.get("count")))
+                ));
+    }
+
+    private List<String> generateDateRange(LocalDateTime start, LocalDateTime end, String datePattern) {
+        List<String> dates = new ArrayList<>();
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(datePattern);
+
+        TemporalUnit unit = getTemporalUnit(datePattern);
+        LocalDateTime current = getInitialDateTime(start, datePattern);
+
+        while (!current.isAfter(end)) {
+            dates.add(current.format(formatter));
+            current = incrementDateTime(current, unit);
+        }
+
+        return dates;
+    }
+
+    private TemporalUnit getTemporalUnit(String datePattern) {
+        switch (datePattern) {
+            case "HH时": return ChronoUnit.HOURS;
+            case "EEEE":
+            case "dd日":
+            case "yyyy-MM-dd": return ChronoUnit.DAYS;
+            case "MM月": return ChronoUnit.MONTHS;
+            default: return ChronoUnit.DAYS;
+        }
+    }
+
+    private LocalDateTime getInitialDateTime(LocalDateTime datetime, String datePattern) {
+        switch (datePattern) {
+            case "HH时": return datetime.withMinute(0).withSecond(0).withNano(0);
+            case "MM月": return datetime.withDayOfMonth(1);
+            default: return datetime;
+        }
+    }
+
+    private LocalDateTime incrementDateTime(LocalDateTime current, TemporalUnit unit) {
+        if (unit == ChronoUnit.HOURS) return current.plusHours(1);
+        if (unit == ChronoUnit.DAYS) return current.plusDays(1);
+        if (unit == ChronoUnit.MONTHS) return current.plusMonths(1);
+        return current.plusDays(1);
+    }
+
+}

--
Gitblit v1.7.1