## Feat - Top 30 (直推人数/直推金额)
This commit is contained in:
		
							parent
							
								
									1eae26d056
								
							
						
					
					
						commit
						9e212330ba
					
				| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
package com.hzs.member.statis.controller.api;
 | 
			
		||||
 | 
			
		||||
import com.hzs.common.core.constant.CacheConstants;
 | 
			
		||||
import com.hzs.common.core.enums.ETop30Type;
 | 
			
		||||
import com.hzs.common.core.service.RedisService;
 | 
			
		||||
import com.hzs.common.core.web.domain.AjaxResult;
 | 
			
		||||
import com.hzs.common.domain.member.ext.CuMemberHonorWallExt;
 | 
			
		||||
import com.hzs.common.security.utils.SecurityUtils;
 | 
			
		||||
import com.hzs.member.statis.param.DirectStatisticsTop30QueryParam;
 | 
			
		||||
import com.hzs.member.statis.service.IMemberStatisticsService;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 统计
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@RequestMapping("/api/member-statistics")
 | 
			
		||||
@RestController
 | 
			
		||||
public class MemberStatisticsController {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private IMemberStatisticsService memberStatisticsService;
 | 
			
		||||
    @PostMapping("/topPeople")
 | 
			
		||||
    public AjaxResult topPeople(@RequestBody DirectStatisticsTop30QueryParam param) {
 | 
			
		||||
        return AjaxResult.success(memberStatisticsService.getDirectStatisticsNumberOfPeople(param));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/topAmount")
 | 
			
		||||
    public AjaxResult topAmount(@RequestBody DirectStatisticsTop30QueryParam param) {
 | 
			
		||||
        return AjaxResult.success(memberStatisticsService.getDirectStatisticsNumberOfAmount(param));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,54 @@
 | 
			
		|||
package com.hzs.member.statis.controller.manager;
 | 
			
		||||
 | 
			
		||||
import com.hzs.common.core.constant.CacheConstants;
 | 
			
		||||
import com.hzs.common.core.enums.ETop30Type;
 | 
			
		||||
import com.hzs.common.core.service.RedisService;
 | 
			
		||||
import com.hzs.common.core.web.domain.AjaxResult;
 | 
			
		||||
import com.hzs.member.statis.param.DirectStatisticsTop30QueryParam;
 | 
			
		||||
import com.hzs.member.statis.service.IMemberStatisticsService;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.web.bind.annotation.*;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 统计
 | 
			
		||||
 */
 | 
			
		||||
@Slf4j
 | 
			
		||||
@RequestMapping("/manage/member-statistics")
 | 
			
		||||
@RestController
 | 
			
		||||
public class BackMemberStatisticsController {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private IMemberStatisticsService memberStatisticsService;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private RedisService redisService;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/setShowPeople/{flag}")
 | 
			
		||||
    public AjaxResult setShowPeople(@PathVariable("flag") String flag) {
 | 
			
		||||
        redisService.setCacheObject(CacheConstants.TOP_30_ACTIVITY + ETop30Type.PEOPLE.getKey(), flag);
 | 
			
		||||
        return AjaxResult.success();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/setShowAmount/{flag}")
 | 
			
		||||
    public AjaxResult setShowAmount(@PathVariable("flag") String flag) {
 | 
			
		||||
        redisService.setCacheObject(CacheConstants.TOP_30_ACTIVITY + ETop30Type.AMOUNT.getKey(), flag);
 | 
			
		||||
        return AjaxResult.success();
 | 
			
		||||
    }
 | 
			
		||||
    @PostMapping("/topPeople")
 | 
			
		||||
    public AjaxResult topPeople(@RequestBody DirectStatisticsTop30QueryParam param) {
 | 
			
		||||
        param.setFaker(false);
 | 
			
		||||
        return AjaxResult.success(memberStatisticsService.getDirectStatisticsNumberOfPeople(param));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @PostMapping("/topAmount")
 | 
			
		||||
    public AjaxResult topAmount(@RequestBody DirectStatisticsTop30QueryParam param) {
 | 
			
		||||
        param.setFaker(false);
 | 
			
		||||
        return AjaxResult.success(memberStatisticsService.getDirectStatisticsNumberOfAmount(param));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
package com.hzs.member.statis.mapper;
 | 
			
		||||
 | 
			
		||||
import com.hzs.common.domain.member.statis.DirectStatisticsTop30VO;
 | 
			
		||||
import com.hzs.member.statis.param.DirectStatisticsTop30QueryParam;
 | 
			
		||||
import org.apache.ibatis.annotations.Param;
 | 
			
		||||
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public interface MemberStatisticsMapper {
 | 
			
		||||
 | 
			
		||||
    List<DirectStatisticsTop30VO> getDirectStatisticsNumberOfPeople(@Param("param") DirectStatisticsTop30QueryParam param);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    List<DirectStatisticsTop30VO> getDirectStatisticsNumberOfAmount(@Param("param") DirectStatisticsTop30QueryParam param);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,30 @@
 | 
			
		|||
package com.hzs.member.statis.param;
 | 
			
		||||
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
public class DirectStatisticsTop30QueryParam implements Serializable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 统计年
 | 
			
		||||
     */
 | 
			
		||||
    private String year;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 统计月
 | 
			
		||||
     */
 | 
			
		||||
    private String month;
 | 
			
		||||
 | 
			
		||||
    private Boolean faker;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private Date startDate;
 | 
			
		||||
 | 
			
		||||
    private Date endDate;
 | 
			
		||||
 | 
			
		||||
    private Long memberId;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
package com.hzs.member.statis.service;
 | 
			
		||||
 | 
			
		||||
import com.hzs.common.domain.member.statis.DirectStatisticsTop30VO;
 | 
			
		||||
import com.hzs.member.statis.param.DirectStatisticsTop30QueryParam;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public interface IMemberStatisticsService {
 | 
			
		||||
 | 
			
		||||
    List<DirectStatisticsTop30VO> getDirectStatisticsNumberOfPeople(DirectStatisticsTop30QueryParam param);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    List<DirectStatisticsTop30VO> getDirectStatisticsNumberOfAmount(DirectStatisticsTop30QueryParam param);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,225 @@
 | 
			
		|||
package com.hzs.member.statis.service.impl;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.ObjectUtil;
 | 
			
		||||
import com.hzs.common.core.constant.CacheConstants;
 | 
			
		||||
import com.hzs.common.core.enums.ETop30Type;
 | 
			
		||||
import com.hzs.common.core.service.RedisService;
 | 
			
		||||
import com.hzs.common.core.utils.DataMaskingUtil;
 | 
			
		||||
import com.hzs.common.core.utils.DateUtils;
 | 
			
		||||
import com.hzs.common.domain.member.statis.DirectStatisticsTop30VO;
 | 
			
		||||
import com.hzs.member.statis.mapper.MemberStatisticsMapper;
 | 
			
		||||
import com.hzs.member.statis.param.DirectStatisticsTop30QueryParam;
 | 
			
		||||
import com.hzs.member.statis.service.IMemberStatisticsService;
 | 
			
		||||
import com.hzs.member.statis.util.DataGeneratorUtils;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.data.redis.core.RedisTemplate;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
 | 
			
		||||
import javax.annotation.Resource;
 | 
			
		||||
import java.math.BigDecimal;
 | 
			
		||||
import java.time.*;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
@Service
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class MemberStatisticsServiceImpl implements IMemberStatisticsService {
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private MemberStatisticsMapper memberStatisticsMapper;
 | 
			
		||||
 | 
			
		||||
    @Resource
 | 
			
		||||
    private RedisService redisService;
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<DirectStatisticsTop30VO> getDirectStatisticsNumberOfPeople(DirectStatisticsTop30QueryParam param) {
 | 
			
		||||
        String show = redisService.getCacheObject(CacheConstants.TOP_30_ACTIVITY + ETop30Type.PEOPLE.getKey());
 | 
			
		||||
        if(ObjectUtil.isEmpty(show) || "false".equals(show)){
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        if(DateUtils.isYearMonthExceedCurrent(param.getYear() + "/" + param.getMonth())) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        setStartAndEndDate(param);
 | 
			
		||||
        List<DirectStatisticsTop30VO> list = memberStatisticsMapper.getDirectStatisticsNumberOfPeople(param);
 | 
			
		||||
        return desensitizationAndFaker(list, param, 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public List<DirectStatisticsTop30VO> getDirectStatisticsNumberOfAmount(DirectStatisticsTop30QueryParam param) {
 | 
			
		||||
        String show = redisService.getCacheObject(CacheConstants.TOP_30_ACTIVITY + ETop30Type.AMOUNT.getKey());
 | 
			
		||||
        if(ObjectUtil.isEmpty(show) || "false".equals(show)){
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        if(DateUtils.isYearMonthExceedCurrent(param.getYear() + "/" + param.getMonth())) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        setStartAndEndDate(param);
 | 
			
		||||
        List<DirectStatisticsTop30VO> list = memberStatisticsMapper.getDirectStatisticsNumberOfAmount(param);
 | 
			
		||||
        return desensitizationAndFaker(list, param, 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 数据脱敏&格式化数字&假数据
 | 
			
		||||
     * @param list
 | 
			
		||||
     * @param param
 | 
			
		||||
     * @param type
 | 
			
		||||
     */
 | 
			
		||||
    private List<DirectStatisticsTop30VO> desensitizationAndFaker(List<DirectStatisticsTop30VO> list, DirectStatisticsTop30QueryParam param, Integer type){
 | 
			
		||||
        List<DirectStatisticsTop30VO> fakerList = DataGeneratorUtils.generateFakeData(
 | 
			
		||||
                new ArrayList<>(), 30, 1, DataGeneratorUtils.TimeGranularity.DAY);
 | 
			
		||||
        List<DirectStatisticsTop30VO> result = list;
 | 
			
		||||
        if(param.getFaker()){
 | 
			
		||||
            result = generateFakeData(list, 30, fakerList, type);
 | 
			
		||||
        }
 | 
			
		||||
        for (DirectStatisticsTop30VO directStatisticsTop30VO : result) {
 | 
			
		||||
            directStatisticsTop30VO.setMemberCode(DataMaskingUtil.mask(directStatisticsTop30VO.getMemberCode(), 2, 2));
 | 
			
		||||
            directStatisticsTop30VO.setMemberName(DataMaskingUtil.mask(directStatisticsTop30VO.getMemberName(), 1, 0));
 | 
			
		||||
            directStatisticsTop30VO.setNumberOfAmount(directStatisticsTop30VO.getNumberOfAmount().divide(new BigDecimal(10000)));
 | 
			
		||||
        }
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
    private void setStartAndEndDate(DirectStatisticsTop30QueryParam param) {
 | 
			
		||||
        // 参数验证
 | 
			
		||||
        if (ObjectUtil.isEmpty(param.getYear()) || ObjectUtil.isEmpty(param.getMonth())) {
 | 
			
		||||
            throw new IllegalArgumentException("年份和月份不能为空");
 | 
			
		||||
        }
 | 
			
		||||
        try {
 | 
			
		||||
            int year = Integer.parseInt(param.getYear());
 | 
			
		||||
            int month = Integer.parseInt(param.getMonth());
 | 
			
		||||
            YearMonth yearMonth = YearMonth.of(year, month);
 | 
			
		||||
            LocalDateTime firstDay = yearMonth.atDay(1).atStartOfDay();
 | 
			
		||||
            LocalDateTime lastDay = yearMonth.atEndOfMonth().atTime(23, 59, 59);
 | 
			
		||||
            // 获取系统默认时区
 | 
			
		||||
            ZoneId zoneId = ZoneId.systemDefault();
 | 
			
		||||
            // 转换为Date
 | 
			
		||||
            param.setStartDate(Date.from(firstDay.atZone(zoneId).toInstant()));
 | 
			
		||||
            param.setEndDate(Date.from(lastDay.atZone(zoneId).toInstant()));
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
            throw new IllegalArgumentException("年份或月份格式不正确", e);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            throw new RuntimeException("获取时间范围失败", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成假数据并填充到列表
 | 
			
		||||
     *
 | 
			
		||||
     * @param list 原始数据列表
 | 
			
		||||
     * @param totalCount 总数
 | 
			
		||||
     * @param fakerList 假数据列表
 | 
			
		||||
     * @param type 类型:1为设置numberOfPeople, 2为设置numberOfAmount
 | 
			
		||||
     * @return 填充后的列表
 | 
			
		||||
     */
 | 
			
		||||
    public static List<DirectStatisticsTop30VO> generateFakeData(
 | 
			
		||||
            List<DirectStatisticsTop30VO> list,
 | 
			
		||||
            int totalCount,
 | 
			
		||||
            List<DirectStatisticsTop30VO> fakerList,
 | 
			
		||||
            int type) {
 | 
			
		||||
        if (list == null) {
 | 
			
		||||
            list = new ArrayList<>();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (fakerList == null || fakerList.isEmpty()) {
 | 
			
		||||
            throw new IllegalArgumentException("假数据列表不能为空");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (totalCount < list.size()) {
 | 
			
		||||
            throw new IllegalArgumentException("总数不能小于原始列表大小");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 计算需要生成的假数据数量
 | 
			
		||||
        int needGenerateCount = totalCount - list.size();
 | 
			
		||||
 | 
			
		||||
        // 检查假数据列表是否有足够的数据
 | 
			
		||||
        if (needGenerateCount > fakerList.size()) {
 | 
			
		||||
            throw new IllegalArgumentException("假数据列表数据不足");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 从假数据列表中移除并获取需要的假数据
 | 
			
		||||
        List<DirectStatisticsTop30VO> selectedFakerData = new ArrayList<>();
 | 
			
		||||
        for (int i = 0; i < needGenerateCount; i++) {
 | 
			
		||||
            // 随机选择一个假数据
 | 
			
		||||
            int randomIndex = new Random().nextInt(fakerList.size());
 | 
			
		||||
            DirectStatisticsTop30VO fakerData = fakerList.remove(randomIndex);
 | 
			
		||||
            selectedFakerData.add(fakerData);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 根据类型设置数据
 | 
			
		||||
        if (type == 1) {
 | 
			
		||||
            setNumberOfPeople(selectedFakerData, list);
 | 
			
		||||
        } else if (type == 2) {
 | 
			
		||||
            setNumberOfAmount(selectedFakerData, list);
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new IllegalArgumentException("类型参数无效,只能是1或2");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 将生成的假数据添加到原始列表
 | 
			
		||||
        list.addAll(selectedFakerData);
 | 
			
		||||
 | 
			
		||||
        // 按照对应类型降序排序
 | 
			
		||||
        if (type == 1) {
 | 
			
		||||
            list.sort(Comparator.comparing(DirectStatisticsTop30VO::getNumberOfPeople).reversed());
 | 
			
		||||
        } else {
 | 
			
		||||
            list.sort(Comparator.comparing(DirectStatisticsTop30VO::getNumberOfAmount).reversed());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置numberOfPeople
 | 
			
		||||
     */
 | 
			
		||||
    private static void setNumberOfPeople(List<DirectStatisticsTop30VO> selectedFakerData, List<DirectStatisticsTop30VO> originalList) {
 | 
			
		||||
        // 找出原始列表中最小的numberOfPeople
 | 
			
		||||
        int minPeopleCount = Integer.MAX_VALUE;
 | 
			
		||||
        for (DirectStatisticsTop30VO data : originalList) {
 | 
			
		||||
            if (data.getNumberOfPeople() != null && data.getNumberOfPeople() < minPeopleCount) {
 | 
			
		||||
                minPeopleCount = data.getNumberOfPeople();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (minPeopleCount == Integer.MAX_VALUE) {
 | 
			
		||||
            minPeopleCount = 10; // 默认值,可以根据需要调整
 | 
			
		||||
        }
 | 
			
		||||
        Random random = new Random();
 | 
			
		||||
        for (DirectStatisticsTop30VO fakerData : selectedFakerData) {
 | 
			
		||||
            if (minPeopleCount == 1) {
 | 
			
		||||
                fakerData.setNumberOfPeople(1);
 | 
			
		||||
            } else {
 | 
			
		||||
                int value = random.nextInt(minPeopleCount - 1) + 1;
 | 
			
		||||
                fakerData.setNumberOfPeople(value);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 设置numberOfAmount
 | 
			
		||||
     */
 | 
			
		||||
    private static void setNumberOfAmount(List<DirectStatisticsTop30VO> selectedFakerData, List<DirectStatisticsTop30VO> originalList) {
 | 
			
		||||
        // 找出原始列表中最小的numberOfAmount
 | 
			
		||||
        BigDecimal minAmount = null;
 | 
			
		||||
        for (DirectStatisticsTop30VO data : originalList) {
 | 
			
		||||
            if (data.getNumberOfAmount() != null) {
 | 
			
		||||
                if (minAmount == null || data.getNumberOfAmount().compareTo(minAmount) < 0) {
 | 
			
		||||
                    minAmount = data.getNumberOfAmount();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (minAmount == null) {
 | 
			
		||||
            minAmount = new BigDecimal(188); // 默认值,可以根据需要调整
 | 
			
		||||
        }
 | 
			
		||||
        Random random = new Random();
 | 
			
		||||
        for (DirectStatisticsTop30VO fakerData : selectedFakerData) {
 | 
			
		||||
            if (minAmount.compareTo(new BigDecimal(188)) == 0) {
 | 
			
		||||
                BigDecimal value = new BigDecimal(188);
 | 
			
		||||
                fakerData.setNumberOfAmount(value);
 | 
			
		||||
            } else {
 | 
			
		||||
                BigDecimal range = minAmount.subtract(BigDecimal.ONE);
 | 
			
		||||
                BigDecimal randomValue = new BigDecimal(random.nextDouble() * range.doubleValue()).setScale(4, BigDecimal.ROUND_HALF_UP);
 | 
			
		||||
                if (randomValue.compareTo(BigDecimal.ZERO) <= 0) {
 | 
			
		||||
                    randomValue = BigDecimal.ONE;
 | 
			
		||||
                }
 | 
			
		||||
                fakerData.setNumberOfAmount(randomValue);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,316 @@
 | 
			
		|||
package com.hzs.member.statis.util;
 | 
			
		||||
 | 
			
		||||
import cn.hutool.core.util.RandomUtil;
 | 
			
		||||
import cn.hutool.json.JSONUtil;
 | 
			
		||||
import com.hzs.common.core.constant.SysConstants;
 | 
			
		||||
import com.hzs.common.domain.member.statis.DirectStatisticsTop30VO;
 | 
			
		||||
 | 
			
		||||
import java.math.BigDecimal;
 | 
			
		||||
import java.time.LocalDate;
 | 
			
		||||
import java.time.LocalDateTime;
 | 
			
		||||
import java.time.format.DateTimeFormatter;
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
public class DataGeneratorUtils {
 | 
			
		||||
 | 
			
		||||
    // 时间粒度枚举
 | 
			
		||||
    public enum TimeGranularity {
 | 
			
		||||
        DAY,    // 按天
 | 
			
		||||
        HOUR    // 按小时
 | 
			
		||||
    }
 | 
			
		||||
    private static final String prefix = "BD";
 | 
			
		||||
 | 
			
		||||
    // 常见姓氏数组
 | 
			
		||||
    private static final String[] COMMON_SURNAMES = {
 | 
			
		||||
        "王", "李", "张", "刘", "陈", "杨", "黄", "赵", "吴", "周",
 | 
			
		||||
        "徐", "孙", "马", "朱", "胡", "郭", "何", "高", "林", "罗",
 | 
			
		||||
        "郑", "梁", "谢", "宋", "唐", "许", "韩", "冯", "邓", "曹",
 | 
			
		||||
        "彭", "曾", "肖", "田", "董", "袁", "潘", "于", "蒋", "蔡"
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // 当前种子,用于生成数据
 | 
			
		||||
    private static int currentSeed = getSeed(TimeGranularity.DAY); // 默认按天
 | 
			
		||||
    private static TimeGranularity currentGranularity = TimeGranularity.DAY;
 | 
			
		||||
    // 预先生成的30条基础数据
 | 
			
		||||
    private static List<DirectStatisticsTop30VO> PERIODIC_PRE_GENERATED_DATA = generatePeriodicPredefinedData();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成假数据并添加到列表中
 | 
			
		||||
     *
 | 
			
		||||
     * @param list          已有数据列表
 | 
			
		||||
     * @param totalCount    需要生成的总数据量
 | 
			
		||||
     * @param type          类型:1为设置numberOfPeople,2为设置numberOfAmount
 | 
			
		||||
     * @param granularity   时间粒度:DAY按天,HOUR按小时
 | 
			
		||||
     * @return 包含新生成数据的列表
 | 
			
		||||
     */
 | 
			
		||||
    public static List<DirectStatisticsTop30VO> generateFakeData(
 | 
			
		||||
            List<DirectStatisticsTop30VO> list, int totalCount, int type, TimeGranularity granularity) {
 | 
			
		||||
        // 检查时间是否已变化,如果变化则重新生成数据
 | 
			
		||||
        checkAndUpdatePeriodicData(granularity);
 | 
			
		||||
 | 
			
		||||
        // 创建返回列表(复制原列表,避免修改原列表)
 | 
			
		||||
        List<DirectStatisticsTop30VO> resultList = new ArrayList<>(list);
 | 
			
		||||
 | 
			
		||||
        // 如果总数小于等于已有列表大小,直接返回原列表
 | 
			
		||||
        if (totalCount <= list.size()) {
 | 
			
		||||
            return resultList;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 需要生成的新数据数量
 | 
			
		||||
        int needGenerateCount = totalCount - list.size();
 | 
			
		||||
 | 
			
		||||
        // 从预生成数据中获取所需数量的数据
 | 
			
		||||
        List<DirectStatisticsTop30VO> newData = getPeriodicPreGeneratedDataByCount(needGenerateCount, type);
 | 
			
		||||
 | 
			
		||||
        // 添加新数据到结果列表
 | 
			
		||||
        resultList.addAll(newData);
 | 
			
		||||
 | 
			
		||||
        // 按照对应字段降序排序
 | 
			
		||||
        if (type == 1) {
 | 
			
		||||
            resultList.sort((a, b) -> b.getNumberOfPeople().compareTo(a.getNumberOfPeople()));
 | 
			
		||||
        } else if (type == 2) {
 | 
			
		||||
            resultList.sort((a, b) -> b.getNumberOfAmount().compareTo(a.getNumberOfAmount()));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return resultList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * (兼容旧版本)按天生成数据
 | 
			
		||||
     */
 | 
			
		||||
    public static List<DirectStatisticsTop30VO> generateFakeData(
 | 
			
		||||
            List<DirectStatisticsTop30VO> list, int totalCount, int type) {
 | 
			
		||||
        return generateFakeData(list, totalCount, type, TimeGranularity.DAY);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 检查时间是否已变化,如果变化则更新数据
 | 
			
		||||
     */
 | 
			
		||||
    private static void checkAndUpdatePeriodicData(TimeGranularity granularity) {
 | 
			
		||||
        int newSeed = getSeed(granularity);
 | 
			
		||||
        if (newSeed != currentSeed || granularity != currentGranularity) {
 | 
			
		||||
            currentSeed = newSeed;
 | 
			
		||||
            currentGranularity = granularity;
 | 
			
		||||
            PERIODIC_PRE_GENERATED_DATA = generatePeriodicPredefinedData();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据时间粒度获取种子
 | 
			
		||||
     * @param granularity 时间粒度
 | 
			
		||||
     * @return 对应的种子
 | 
			
		||||
     */
 | 
			
		||||
    private static int getSeed(TimeGranularity granularity) {
 | 
			
		||||
        if (granularity == TimeGranularity.HOUR) {
 | 
			
		||||
            // 按小时:格式为YYYYMMDDHH
 | 
			
		||||
            LocalDateTime now = LocalDateTime.now();
 | 
			
		||||
            return Integer.parseInt(now.format(DateTimeFormatter.ofPattern("yyyyMMddHH")));
 | 
			
		||||
        } else {
 | 
			
		||||
            // 按天:格式为YYYYMMDD
 | 
			
		||||
            LocalDate today = LocalDate.now();
 | 
			
		||||
            return Integer.parseInt(today.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成当前周期的30条预定义数据
 | 
			
		||||
     */
 | 
			
		||||
    private static List<DirectStatisticsTop30VO> generatePeriodicPredefinedData() {
 | 
			
		||||
        List<DirectStatisticsTop30VO> dataList = new ArrayList<>(30);
 | 
			
		||||
 | 
			
		||||
        // 使用当前周期的种子创建Random对象
 | 
			
		||||
        Random periodRandom = new Random(currentSeed);
 | 
			
		||||
 | 
			
		||||
        // 生成30条基础数据
 | 
			
		||||
        for (int i = 0; i < 30; i++) {
 | 
			
		||||
            DirectStatisticsTop30VO vo = new DirectStatisticsTop30VO();
 | 
			
		||||
 | 
			
		||||
            // 设置基础信息,包含时间粒度标识
 | 
			
		||||
            long baseId = 4000000L + currentSeed % 1000000L;
 | 
			
		||||
            vo.setMemberId(baseId + i);
 | 
			
		||||
 | 
			
		||||
            // 根据时间粒度生成不同的用户编号前缀
 | 
			
		||||
//            String prefix = currentGranularity == TimeGranularity.HOUR ? "HOUR" : "DAY";
 | 
			
		||||
//            String prefix = SysConstants.HAI_FUN_PREFIX;
 | 
			
		||||
            vo.setMemberCode(generateMemberCode(currentSeed, 10));
 | 
			
		||||
 | 
			
		||||
            // 生成随机中文姓名
 | 
			
		||||
            vo.setMemberName(generateRandomChineseName(i, periodRandom));
 | 
			
		||||
 | 
			
		||||
            // 使用周期随机数生成数据值
 | 
			
		||||
            vo.setNumberOfPeople(1 + periodRandom.nextInt(100)); // 1-100之间的随机值
 | 
			
		||||
 | 
			
		||||
            // 生成金额值
 | 
			
		||||
            BigDecimal amount = new BigDecimal(100 + periodRandom.nextInt(10000))
 | 
			
		||||
                    .add(new BigDecimal(periodRandom.nextDouble()))
 | 
			
		||||
                    .setScale(4, BigDecimal.ROUND_HALF_UP);
 | 
			
		||||
            vo.setNumberOfAmount(amount);
 | 
			
		||||
 | 
			
		||||
            dataList.add(vo);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return dataList;
 | 
			
		||||
    }
 | 
			
		||||
    public static String generateMemberCode(long currentSeed, int i) {
 | 
			
		||||
        int prefixLength = prefix.length();
 | 
			
		||||
        int numPartLength = 10 - prefixLength;
 | 
			
		||||
        long seedPart = generateRandomSeedPart(numPartLength);
 | 
			
		||||
        String memberCode = prefix + seedPart;
 | 
			
		||||
        return memberCode.length() > 10 ? memberCode.substring(0, 10) : memberCode;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成指定位数的随机seedPart
 | 
			
		||||
     *
 | 
			
		||||
     * @param digits 位数
 | 
			
		||||
     * @return 随机数
 | 
			
		||||
     */
 | 
			
		||||
    private static long generateRandomSeedPart(int digits) {
 | 
			
		||||
        if (digits <= 0) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 计算最小值和最大值
 | 
			
		||||
        int min = (int) Math.pow(10, digits - 1);
 | 
			
		||||
        int max = (int) Math.pow(10, digits) - 1;
 | 
			
		||||
 | 
			
		||||
        // 如果digits为0,返回0
 | 
			
		||||
        if (min == 0 && max == 0) {
 | 
			
		||||
            return 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return RandomUtil.randomInt(min, max);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据需要的数量从预生成数据中获取数据
 | 
			
		||||
     */
 | 
			
		||||
    private static List<DirectStatisticsTop30VO> getPeriodicPreGeneratedDataByCount(int count, int type) {
 | 
			
		||||
        List<DirectStatisticsTop30VO> result = new ArrayList<>(count);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < count; i++) {
 | 
			
		||||
            // 从预生成数据中循环获取
 | 
			
		||||
            DirectStatisticsTop30VO originalVo = PERIODIC_PRE_GENERATED_DATA.get(i % 30);
 | 
			
		||||
 | 
			
		||||
            // 创建副本以避免修改预生成的数据
 | 
			
		||||
            DirectStatisticsTop30VO newVo = new DirectStatisticsTop30VO();
 | 
			
		||||
            newVo.setMemberId(originalVo.getMemberId());
 | 
			
		||||
            newVo.setMemberName(originalVo.getMemberName());
 | 
			
		||||
            newVo.setMemberCode(originalVo.getMemberCode());
 | 
			
		||||
 | 
			
		||||
            // 根据类型设置相应的值
 | 
			
		||||
            if (type == 1) {
 | 
			
		||||
                // 类型1:设置numberOfPeople
 | 
			
		||||
                newVo.setNumberOfPeople(originalVo.getNumberOfPeople());
 | 
			
		||||
                newVo.setNumberOfAmount(BigDecimal.ZERO);
 | 
			
		||||
            } else if (type == 2) {
 | 
			
		||||
                // 类型2:设置numberOfAmount
 | 
			
		||||
                newVo.setNumberOfPeople(0);
 | 
			
		||||
                newVo.setNumberOfAmount(originalVo.getNumberOfAmount());
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            result.add(newVo);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成随机中文姓名
 | 
			
		||||
     */
 | 
			
		||||
    private static String generateRandomChineseName(int index, Random random) {
 | 
			
		||||
        // 随机选择姓氏
 | 
			
		||||
        String surname = COMMON_SURNAMES[random.nextInt(COMMON_SURNAMES.length)];
 | 
			
		||||
 | 
			
		||||
        // 随机决定是二字姓名还是三字姓名
 | 
			
		||||
        boolean isTwoCharacters = random.nextBoolean();
 | 
			
		||||
 | 
			
		||||
        if (isTwoCharacters) {
 | 
			
		||||
            // 二字姓名:姓氏 + 1个随机汉字
 | 
			
		||||
            return surname + generateRandomChineseChar(random);
 | 
			
		||||
        } else {
 | 
			
		||||
            // 三字姓名:姓氏 + 2个随机汉字
 | 
			
		||||
            return surname + generateRandomChineseChar(random) + generateRandomChineseChar(random);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 生成随机汉字
 | 
			
		||||
     */
 | 
			
		||||
    private static char generateRandomChineseChar(Random random) {
 | 
			
		||||
        // 中文汉字Unicode范围大致在0x4E00到0x9FA5之间
 | 
			
		||||
        int start = 0x4E00;
 | 
			
		||||
        int end = 0x9FA5;
 | 
			
		||||
        return (char) (start + random.nextInt(end - start + 1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获取当前周期预生成的30条完整数据
 | 
			
		||||
     * @param granularity 时间粒度
 | 
			
		||||
     * @return 预生成的数据列表
 | 
			
		||||
     */
 | 
			
		||||
    public static List<DirectStatisticsTop30VO> getPeriodicPreGeneratedData(TimeGranularity granularity) {
 | 
			
		||||
        // 检查时间是否已变化
 | 
			
		||||
        checkAndUpdatePeriodicData(granularity);
 | 
			
		||||
 | 
			
		||||
        // 返回预生成数据的副本,避免外部修改
 | 
			
		||||
        List<DirectStatisticsTop30VO> copyList = new ArrayList<>(PERIODIC_PRE_GENERATED_DATA.size());
 | 
			
		||||
        for (DirectStatisticsTop30VO vo : PERIODIC_PRE_GENERATED_DATA) {
 | 
			
		||||
            DirectStatisticsTop30VO copy = new DirectStatisticsTop30VO();
 | 
			
		||||
            copy.setMemberId(vo.getMemberId());
 | 
			
		||||
            copy.setMemberName(vo.getMemberName());
 | 
			
		||||
            copy.setMemberCode(vo.getMemberCode());
 | 
			
		||||
            copy.setNumberOfPeople(vo.getNumberOfPeople());
 | 
			
		||||
            copy.setNumberOfAmount(vo.getNumberOfAmount());
 | 
			
		||||
            copyList.add(copy);
 | 
			
		||||
        }
 | 
			
		||||
        return copyList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * (用于测试)根据指定日期和时间粒度获取数据
 | 
			
		||||
     */
 | 
			
		||||
    public static List<DirectStatisticsTop30VO> getPreGeneratedDataByDate(
 | 
			
		||||
            int year, int month, int day, int hour, TimeGranularity granularity) {
 | 
			
		||||
        // 保存当前状态
 | 
			
		||||
        int originalSeed = currentSeed;
 | 
			
		||||
        TimeGranularity originalGranularity = currentGranularity;
 | 
			
		||||
        List<DirectStatisticsTop30VO> originalData = PERIODIC_PRE_GENERATED_DATA;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // 设置指定日期的种子
 | 
			
		||||
            Calendar calendar = new GregorianCalendar(year, month - 1, day, hour, 0, 0);
 | 
			
		||||
            Date date = calendar.getTime();
 | 
			
		||||
 | 
			
		||||
            if (granularity == TimeGranularity.HOUR) {
 | 
			
		||||
                LocalDateTime localDateTime = date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDateTime();
 | 
			
		||||
                currentSeed = Integer.parseInt(localDateTime.format(DateTimeFormatter.ofPattern("yyyyMMddHH")));
 | 
			
		||||
            } else {
 | 
			
		||||
                LocalDate localDate = date.toInstant().atZone(java.time.ZoneId.systemDefault()).toLocalDate();
 | 
			
		||||
                currentSeed = Integer.parseInt(localDate.format(DateTimeFormatter.ofPattern("yyyyMMdd")));
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            currentGranularity = granularity;
 | 
			
		||||
 | 
			
		||||
            // 重新生成指定日期的数据
 | 
			
		||||
            PERIODIC_PRE_GENERATED_DATA = generatePeriodicPredefinedData();
 | 
			
		||||
 | 
			
		||||
            // 返回生成的数据
 | 
			
		||||
            return getPeriodicPreGeneratedData(granularity);
 | 
			
		||||
        } finally {
 | 
			
		||||
            // 恢复原始状态
 | 
			
		||||
            currentSeed = originalSeed;
 | 
			
		||||
            currentGranularity = originalGranularity;
 | 
			
		||||
            PERIODIC_PRE_GENERATED_DATA = originalData;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static void main(String[] args) {
 | 
			
		||||
        // 按天生成数据
 | 
			
		||||
        List<DirectStatisticsTop30VO> dailyData = DataGeneratorUtils.generateFakeData(
 | 
			
		||||
                new ArrayList<>(), 30, 1, DataGeneratorUtils.TimeGranularity.DAY);
 | 
			
		||||
        System.out.println(JSONUtil.toJsonPrettyStr(dailyData));
 | 
			
		||||
        // 按小时生成数据
 | 
			
		||||
//        List<DirectStatisticsTop30VO> hourlyData = DataGeneratorUtils.generateFakeData(
 | 
			
		||||
//                new ArrayList<>(), 50, 2, DataGeneratorUtils.TimeGranularity.HOUR);
 | 
			
		||||
//        System.out.println(JSONUtil.toJsonPrettyStr(hourlyData));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,53 @@
 | 
			
		|||
<?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="com.hzs.member.statis.mapper.MemberStatisticsMapper">
 | 
			
		||||
 | 
			
		||||
    <select id="getDirectStatisticsNumberOfPeople"
 | 
			
		||||
            resultType="com.hzs.common.domain.member.statis.DirectStatisticsTop30VO">
 | 
			
		||||
        select
 | 
			
		||||
            PK_ID memberId,
 | 
			
		||||
            MEMBER_CODE memberCode,
 | 
			
		||||
            member_name memberName,
 | 
			
		||||
            numberOfPeople,
 | 
			
		||||
            numberOfAmount
 | 
			
		||||
        from (
 | 
			
		||||
            select
 | 
			
		||||
                PK_REFERENCE,
 | 
			
		||||
                count(PK_REFERENCE) numberOfPeople,
 | 
			
		||||
                sum(order_amount) numberOfAmount
 | 
			
		||||
            from sa_order
 | 
			
		||||
            where
 | 
			
		||||
                del_flag = 0
 | 
			
		||||
                and order_status = 1
 | 
			
		||||
                and pay_time between #{param.startDate} and #{param.endDate}
 | 
			
		||||
            group by PK_REFERENCE
 | 
			
		||||
        ) r
 | 
			
		||||
        left join cu_member cm on r.PK_REFERENCE = cm.pk_id
 | 
			
		||||
        WHERE ROWNUM <![CDATA[ <=]]> 30
 | 
			
		||||
        order by numberOfPeople desc
 | 
			
		||||
    </select>
 | 
			
		||||
    <select id="getDirectStatisticsNumberOfAmount"
 | 
			
		||||
            resultType="com.hzs.common.domain.member.statis.DirectStatisticsTop30VO">
 | 
			
		||||
        select
 | 
			
		||||
            PK_ID memberId,
 | 
			
		||||
            MEMBER_CODE memberCode,
 | 
			
		||||
            member_name memberName,
 | 
			
		||||
            numberOfPeople,
 | 
			
		||||
            numberOfAmount
 | 
			
		||||
        from (
 | 
			
		||||
                 select
 | 
			
		||||
                     PK_REFERENCE,
 | 
			
		||||
                     count(PK_REFERENCE) numberOfPeople,
 | 
			
		||||
                     sum(order_amount) numberOfAmount
 | 
			
		||||
                 from sa_order
 | 
			
		||||
                 where
 | 
			
		||||
                     del_flag = 0
 | 
			
		||||
                   and order_status = 1
 | 
			
		||||
                   and pay_time between #{param.startDate} and #{param.endDate}
 | 
			
		||||
                 group by PK_REFERENCE
 | 
			
		||||
             ) r
 | 
			
		||||
                 left join cu_member cm on r.PK_REFERENCE = cm.pk_id
 | 
			
		||||
        WHERE ROWNUM <![CDATA[ <=]]> 30
 | 
			
		||||
        order by numberOfAmount desc
 | 
			
		||||
    </select>
 | 
			
		||||
</mapper>
 | 
			
		||||
| 
						 | 
				
			
			@ -330,4 +330,5 @@ public class CacheConstants {
 | 
			
		|||
     */
 | 
			
		||||
    public final static String AUTO_LOGIN = CACHE_PREFIX + "auth:login:";
 | 
			
		||||
 | 
			
		||||
    public final static String TOP_30_ACTIVITY = CACHE_PREFIX + "top:30:activity:";
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,84 @@
 | 
			
		|||
package com.hzs.common.core.enums;
 | 
			
		||||
 | 
			
		||||
import com.hzs.common.core.constant.EnumsPrefixConstants;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Getter;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 订单类型枚举类
 | 
			
		||||
 */
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Getter
 | 
			
		||||
public enum ETop30Type {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 注册订单
 | 
			
		||||
     */
 | 
			
		||||
    PEOPLE(1, "直推人数", "people"),
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 升级订单
 | 
			
		||||
     */
 | 
			
		||||
    AMOUNT(2, "直推金额", "amount")
 | 
			
		||||
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 实际值
 | 
			
		||||
     */
 | 
			
		||||
    private final int value;
 | 
			
		||||
    /**
 | 
			
		||||
     * 显示标签
 | 
			
		||||
     */
 | 
			
		||||
    private final String label;
 | 
			
		||||
    /**
 | 
			
		||||
     * 显示标签
 | 
			
		||||
     */
 | 
			
		||||
    private final String key;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据值,返回枚举
 | 
			
		||||
     *
 | 
			
		||||
     * @param value
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    public static ETop30Type getEnumByValue(int value) {
 | 
			
		||||
        for (ETop30Type enums : ETop30Type.values()) {
 | 
			
		||||
            if (enums.getValue() == value) {
 | 
			
		||||
                return enums;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 根据值,返回名称
 | 
			
		||||
     *
 | 
			
		||||
     * @param value
 | 
			
		||||
     * @return
 | 
			
		||||
     */
 | 
			
		||||
    public static String getLabelByValue(Integer value) {
 | 
			
		||||
        if (null == value) {
 | 
			
		||||
            return "";
 | 
			
		||||
        }
 | 
			
		||||
        for (ETop30Type enums : ETop30Type.values()) {
 | 
			
		||||
            if (enums.getValue() == value) {
 | 
			
		||||
                return enums.getLabel();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return "";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static List<ETop30Type> getOrderTypeSpecial() {
 | 
			
		||||
        List<ETop30Type> resultList = new ArrayList<>();
 | 
			
		||||
        resultList.add(ETop30Type.PEOPLE);
 | 
			
		||||
        resultList.add(ETop30Type.AMOUNT);
 | 
			
		||||
        return resultList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,98 @@
 | 
			
		|||
package com.hzs.common.core.utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 数据脱敏工具类
 | 
			
		||||
 * 提供字符串脱敏功能,可指定保留前几位和后几位字符
 | 
			
		||||
 */
 | 
			
		||||
public class DataMaskingUtil {
 | 
			
		||||
 | 
			
		||||
    // 默认脱敏字符
 | 
			
		||||
    private static final char DEFAULT_MASK_CHAR = '*';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 对字符串进行脱敏处理
 | 
			
		||||
     * @param input 原始字符串
 | 
			
		||||
     * @param keepPrefix 保留的前缀位数
 | 
			
		||||
     * @param keepSuffix 保留的后缀位数
 | 
			
		||||
     * @return 脱敏后的字符串
 | 
			
		||||
     */
 | 
			
		||||
    public static String mask(String input, int keepPrefix, int keepSuffix) {
 | 
			
		||||
        // 参数验证
 | 
			
		||||
        if (input == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 处理边界情况
 | 
			
		||||
        if (keepPrefix < 0 || keepSuffix < 0) {
 | 
			
		||||
            throw new IllegalArgumentException("保留位数不能为负数");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int length = input.length();
 | 
			
		||||
 | 
			
		||||
        // 如果字符串长度小于等于需要保留的位数之和,直接返回原字符串
 | 
			
		||||
        if (length <= keepPrefix + keepSuffix) {
 | 
			
		||||
            return input;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 构建StringBuilder用于高效拼接字符串
 | 
			
		||||
        StringBuilder result = new StringBuilder(length);
 | 
			
		||||
 | 
			
		||||
        // 添加前缀保留字符
 | 
			
		||||
        result.append(input, 0, keepPrefix);
 | 
			
		||||
 | 
			
		||||
        // 添加脱敏字符
 | 
			
		||||
        int maskCount = length - keepPrefix - keepSuffix;
 | 
			
		||||
        for (int i = 0; i < maskCount; i++) {
 | 
			
		||||
            result.append(DEFAULT_MASK_CHAR);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 添加后缀保留字符
 | 
			
		||||
        result.append(input, length - keepSuffix, length);
 | 
			
		||||
 | 
			
		||||
        return result.toString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 对字符串进行脱敏处理,可自定义脱敏字符
 | 
			
		||||
     * @param input 原始字符串
 | 
			
		||||
     * @param keepPrefix 保留的前缀位数
 | 
			
		||||
     * @param keepSuffix 保留的后缀位数
 | 
			
		||||
     * @param maskChar 自定义脱敏字符
 | 
			
		||||
     * @return 脱敏后的字符串
 | 
			
		||||
     */
 | 
			
		||||
    public static String mask(String input, int keepPrefix, int keepSuffix, char maskChar) {
 | 
			
		||||
        // 参数验证
 | 
			
		||||
        if (input == null) {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 处理边界情况
 | 
			
		||||
        if (keepPrefix < 0 || keepSuffix < 0) {
 | 
			
		||||
            throw new IllegalArgumentException("保留位数不能为负数");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int length = input.length();
 | 
			
		||||
 | 
			
		||||
        // 如果字符串长度小于等于需要保留的位数之和,直接返回原字符串
 | 
			
		||||
        if (length <= keepPrefix + keepSuffix) {
 | 
			
		||||
            return input;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 构建StringBuilder用于高效拼接字符串
 | 
			
		||||
        StringBuilder result = new StringBuilder(length);
 | 
			
		||||
 | 
			
		||||
        // 添加前缀保留字符
 | 
			
		||||
        result.append(input, 0, keepPrefix);
 | 
			
		||||
 | 
			
		||||
        // 添加脱敏字符
 | 
			
		||||
        int maskCount = length - keepPrefix - keepSuffix;
 | 
			
		||||
        for (int i = 0; i < maskCount; i++) {
 | 
			
		||||
            result.append(maskChar);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // 添加后缀保留字符
 | 
			
		||||
        result.append(input, length - keepSuffix, length);
 | 
			
		||||
 | 
			
		||||
        return result.toString();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -6,6 +6,7 @@ import java.text.ParseException;
 | 
			
		|||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.time.*;
 | 
			
		||||
import java.time.format.DateTimeFormatter;
 | 
			
		||||
import java.time.format.DateTimeParseException;
 | 
			
		||||
import java.time.temporal.ChronoUnit;
 | 
			
		||||
import java.time.temporal.TemporalAdjusters;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
| 
						 | 
				
			
			@ -895,5 +896,58 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
 | 
			
		|||
        DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
 | 
			
		||||
        return LocalDate.parse(date, fmt);
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * 判断输入的年/月字符串是否超过当前月份
 | 
			
		||||
     *
 | 
			
		||||
     * @param yearMonthStr 年/月字符串,格式为"yyyy/MM",如"2023/12"
 | 
			
		||||
     * @return 如果输入的年月超过当前月份则返回true,否则返回false
 | 
			
		||||
     * @throws IllegalArgumentException 如果输入的字符串格式不正确
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isYearMonthExceedCurrent(String yearMonthStr) {
 | 
			
		||||
        // 参数校验
 | 
			
		||||
        if (yearMonthStr == null || yearMonthStr.trim().isEmpty()) {
 | 
			
		||||
            throw new IllegalArgumentException("年/月字符串不能为空");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            // 解析输入的年/月字符串
 | 
			
		||||
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM");
 | 
			
		||||
            YearMonth inputYearMonth = YearMonth.parse(yearMonthStr, formatter);
 | 
			
		||||
 | 
			
		||||
            // 获取当前年月
 | 
			
		||||
            YearMonth currentYearMonth = YearMonth.now();
 | 
			
		||||
 | 
			
		||||
            // 比较输入年月和当前年月
 | 
			
		||||
            // 如果输入年月大于当前年月,则返回true(表示超过当前月份)
 | 
			
		||||
            return inputYearMonth.isAfter(currentYearMonth);
 | 
			
		||||
 | 
			
		||||
        } catch (DateTimeParseException e) {
 | 
			
		||||
            throw new IllegalArgumentException("年/月字符串格式不正确,应为'yyyy/MM'格式,如'2023/12'", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 重载方法,支持自定义日期格式
 | 
			
		||||
     *
 | 
			
		||||
     * @param yearMonthStr 年/月字符串
 | 
			
		||||
     * @param pattern 日期格式,如"yyyy-MM"或"yyyyMM"等
 | 
			
		||||
     * @return 如果输入的年月超过当前月份则返回true,否则返回false
 | 
			
		||||
     * @throws IllegalArgumentException 如果输入参数不合法
 | 
			
		||||
     */
 | 
			
		||||
    public static boolean isYearMonthExceedCurrent(String yearMonthStr, String pattern) {
 | 
			
		||||
        if (pattern == null || pattern.trim().isEmpty()) {
 | 
			
		||||
            throw new IllegalArgumentException("日期格式不能为空");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
 | 
			
		||||
            YearMonth inputYearMonth = YearMonth.parse(yearMonthStr, formatter);
 | 
			
		||||
            YearMonth currentYearMonth = YearMonth.now();
 | 
			
		||||
 | 
			
		||||
            return inputYearMonth.isAfter(currentYearMonth);
 | 
			
		||||
 | 
			
		||||
        } catch (DateTimeParseException e) {
 | 
			
		||||
            throw new IllegalArgumentException("年/月字符串格式不正确,无法使用模式'" + pattern + "'解析", e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,42 @@
 | 
			
		|||
package com.hzs.common.domain.member.statis;
 | 
			
		||||
 | 
			
		||||
import com.hzs.common.core.annotation.BigDecimalFormat;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Builder;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.math.BigDecimal;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@Builder
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class DirectStatisticsTop30VO implements Serializable {
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户id
 | 
			
		||||
     */
 | 
			
		||||
    private Long memberId;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户名称
 | 
			
		||||
     */
 | 
			
		||||
    private String memberName;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 用户编号
 | 
			
		||||
     */
 | 
			
		||||
    private String memberCode;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 直推数量
 | 
			
		||||
     */
 | 
			
		||||
    private Integer numberOfPeople;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 直推金额
 | 
			
		||||
     */
 | 
			
		||||
    @BigDecimalFormat(value = "#0.0000")
 | 
			
		||||
    private BigDecimal numberOfAmount;
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue