huliguo
2 天以前 5d7b65670282a4fad015e37d567cfa171b162052
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
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);
    }
 
}