“mitao”
2026-05-07 c83f45789e3e5f5e5188d34c1e8e6509eb4ac33e
feat:兑换码后台接口增加注释
3个文件已修改
454 ■■■■■ 已修改文件
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TExchangeCodePackageController.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TExchangeCodePackageService.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/TExchangeCodePackageServiceImpl.java 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TExchangeCodePackageController.java
@@ -52,6 +52,12 @@
    @Resource
    private TExchangeCodePackageService exchangeCodePackageService;
    /**
     * 分页查询兑换码卡包列表。
     *
     * @param query 查询条件
     * @return 卡包分页结果
     */
    @RequiresPermissions(value = {"/exchangeCode"}, logical = Logical.OR)
    @PostMapping("/pageList")
    @ApiOperation(value = "兑换码卡包分页", tags = {"管理后台-兑换码管理"})
@@ -59,6 +65,12 @@
        return AjaxResult.ok(exchangeCodePackageService.pageList(query));
    }
    /**
     * 新增兑换码卡包。
     *
     * @param dto 卡包保存参数
     * @return 操作结果
     */
    @RequiresPermissions(value = {"/exchangeCode/add"}, logical = Logical.OR)
    @PostMapping("/save")
    @ApiOperation(value = "新增兑换码卡包", tags = {"管理后台-兑换码管理"})
@@ -68,6 +80,12 @@
        return AjaxResult.success();
    }
    /**
     * 编辑兑换码卡包。
     *
     * @param dto 卡包保存参数
     * @return 操作结果
     */
    @RequiresPermissions(value = {"/exchangeCode/update"}, logical = Logical.OR)
    @PostMapping("/update")
    @ApiOperation(value = "编辑兑换码卡包", tags = {"管理后台-兑换码管理"})
@@ -77,6 +95,12 @@
        return AjaxResult.success();
    }
    /**
     * 更新兑换码卡包启用状态。
     *
     * @param dto 卡包ID及状态参数
     * @return 操作结果
     */
    @RequiresPermissions(value = {"/exchangeCode/update"}, logical = Logical.OR)
    @PostMapping("/updateStatus")
    @ApiOperation(value = "更新兑换码卡包启用状态", tags = {"管理后台-兑换码管理"})
@@ -86,6 +110,12 @@
        return AjaxResult.success();
    }
    /**
     * 删除兑换码卡包。
     *
     * @param ids 卡包ID,多个ID用英文逗号分隔
     * @return 操作结果
     */
    @RequiresPermissions(value = {"/exchangeCode/del"}, logical = Logical.OR)
    @GetMapping("/delete")
    @ApiOperation(value = "删除兑换码卡包", tags = {"管理后台-兑换码管理"})
@@ -95,6 +125,12 @@
        return AjaxResult.success();
    }
    /**
     * 查询兑换码卡包详情。
     *
     * @param id 卡包ID
     * @return 卡包详情
     */
    @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR)
    @GetMapping("/detail")
    @ApiOperation(value = "兑换码卡包详情", tags = {"管理后台-兑换码管理"})
@@ -102,6 +138,12 @@
        return AjaxResult.ok(exchangeCodePackageService.getDetail(id));
    }
    /**
     * 分页查询兑换码明细。
     *
     * @param query 查询条件
     * @return 兑换码明细分页结果
     */
    @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR)
    @PostMapping("/codePage")
    @ApiOperation(value = "兑换码明细分页", tags = {"管理后台-兑换码管理"})
@@ -109,6 +151,12 @@
        return AjaxResult.ok(exchangeCodePackageService.codePage(query));
    }
    /**
     * 批量失效兑换码。
     *
     * @param dto 失效参数
     * @return 失效结果
     */
    @RequiresPermissions(value = {"/exchangeCode/del"}, logical = Logical.OR)
    @PostMapping("/batchInvalidate")
    @ApiOperation(value = "批量失效兑换码", tags = {"管理后台-兑换码管理"})
@@ -118,6 +166,12 @@
        return AjaxResult.ok(result).put("msg", "已失效" + result.getSuccessCount() + "条,跳过" + result.getSkipCount() + "条");
    }
    /**
     * 导出兑换码明细。
     *
     * @param query 查询条件
     * @throws IOException 输出流写出异常
     */
    @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR)
    @PutMapping("/export")
    @ApiOperation(value = "导出兑换码明细", tags = {"管理后台-兑换码管理"})
@@ -142,6 +196,12 @@
        outputStream.close();
    }
    /**
     * 下载单个兑换码二维码。
     *
     * @param id 兑换码明细ID
     * @throws Exception 二维码生成或输出异常
     */
    @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR)
    @GetMapping("/downloadQRCode/{id}")
    @ApiOperation(value = "下载单个兑换码二维码", tags = {"管理后台-兑换码管理"})
@@ -154,6 +214,12 @@
        QRCodeUtils.encode(buildQrPayload(detail), response.getOutputStream());
    }
    /**
     * 批量下载未绑定兑换码二维码。
     *
     * @param query 查询条件
     * @throws Exception 二维码生成或压缩输出异常
     */
    @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR)
    @PutMapping("/downloadUnboundQRCodes")
    @ApiOperation(value = "批量下载未绑定二维码", tags = {"管理后台-兑换码管理"})
@@ -176,6 +242,14 @@
        zipOutputStream.close();
    }
    /**
     * 小程序兑换码绑定。
     *
     * @param dto 绑定参数
     * @param userId 用户ID
     * @param phone 手机号
     * @return 绑定结果
     */
    @PostMapping("/bind")
    @ApiOperation(value = "小程序兑换码绑定", tags = {"小程序-兑换码"})
    public R<ExchangeCodeBindResultVO> bind(@RequestBody ExchangeCodeBindDTO dto,
@@ -184,30 +258,61 @@
        return R.ok(exchangeCodePackageService.bindExchangeCode(dto, userId, phone));
    }
    /**
     * 查询用户兑换码余额映射。
     *
     * @param userIds 用户ID集合
     * @return 用户余额映射
     */
    @PostMapping("/balanceMap")
    @ApiOperation(value = "查询用户兑换码余额Map", tags = {"内部-兑换码"})
    public R<Map<Long, BigDecimal>> balanceMap(@RequestBody List<Long> userIds) {
        return R.ok(exchangeCodePackageService.getUserExchangeCodeBalanceMap(userIds));
    }
    /**
     * 扣减用户兑换码余额。
     *
     * @param dto 扣减参数
     * @return 扣减结果
     */
    @PostMapping("/consumeBalance")
    @ApiOperation(value = "扣减用户兑换码余额", tags = {"内部-兑换码"})
    public R<ExchangeCodeConsumeResultVO> consumeBalance(@RequestBody ExchangeCodeConsumeDTO dto) {
        return R.ok(exchangeCodePackageService.consumeBalance(dto));
    }
    /**
     * 退款返还用户兑换码余额。
     *
     * @param dto 退款参数
     * @return 退款结果
     */
    @PostMapping("/refundBalance")
    @ApiOperation(value = "?????????", tags = {"??-???"})
    public R<ExchangeCodeConsumeResultVO> refundBalance(@RequestBody ExchangeCodeConsumeDTO dto) {
        return R.ok(exchangeCodePackageService.refundBalance(dto));
    }
    /**
     * 分页查询兑换码余额流水。
     *
     * @param query 查询条件
     * @return 流水分页结果
     */
    @PostMapping("/useRecord/page")
    @ApiOperation(value = "兑换码余额流水分页", tags = {"小程序-兑换码"})
    public R<Page<TExchangeCodeUseRecord>> useRecordPage(@RequestBody ExchangeCodeUseRecordQuery query) {
        return R.ok(exchangeCodePackageService.useRecordPage(query));
    }
    /**
     * 查询兑换码余额流水详情。
     *
     * @param id 流水ID
     * @param userId 用户ID
     * @return 流水详情
     */
    @GetMapping("/useRecord/detail")
    @ApiOperation(value = "兑换码余额流水详情", tags = {"小程序-兑换码"})
    public R<TExchangeCodeUseRecord> useRecordDetail(@RequestParam("id") Long id,
@@ -215,6 +320,12 @@
        return R.ok(exchangeCodePackageService.useRecordDetail(id, userId));
    }
    /**
     * 构建二维码内容。
     *
     * @param detail 兑换码明细
     * @return 二维码载荷内容
     */
    private String buildQrPayload(ExchangeCodeDetailVO detail) {
        return "code=" + detail.getCode() + "&password=" + detail.getPassword();
    }
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TExchangeCodePackageService.java
@@ -23,39 +23,145 @@
public interface TExchangeCodePackageService extends IService<TExchangeCodePackage> {
    /**
     * 分页查询兑换码卡包列表。
     *
     * @param query 查询条件
     * @return 卡包分页结果
     */
    PageInfo<TExchangeCodePackage> pageList(ExchangeCodePackageQuery query);
    /**
     * 新增兑换码卡包。
     *
     * @param dto 卡包保存参数
     */
    void savePackage(ExchangeCodePackageSaveDTO dto);
    /**
     * 编辑兑换码卡包。
     *
     * @param dto 卡包保存参数
     */
    void updatePackage(ExchangeCodePackageSaveDTO dto);
    /**
     * 更新兑换码卡包启用状态。
     *
     * @param id 卡包ID
     * @param status 启用状态
     */
    void updatePackageStatus(Long id, Integer status);
    /**
     * 刷新所有已过领取截止日期的兑换码卡包未绑定兑换码状态。
     */
    void refreshExpiredPackageCodes();
    /**
     * 删除兑换码卡包。
     *
     * @param ids 卡包ID,多个ID用英文逗号分隔
     */
    void deletePackage(String ids);
    /**
     * 查询兑换码卡包详情。
     *
     * @param id 卡包ID
     * @return 卡包详情
     */
    ExchangeCodePackageDetailVO getDetail(Long id);
    /**
     * 分页查询兑换码明细。
     *
     * @param query 查询条件
     * @return 兑换码明细分页结果
     */
    PageInfo<ExchangeCodeDetailVO> codePage(ExchangeCodeDetailQuery query);
    /**
     * 导出兑换码明细。
     *
     * @param query 查询条件
     * @return 兑换码明细导出数据
     */
    List<ExchangeCodeDetailVO> exportCodes(ExchangeCodeDetailQuery query);
    /**
     * 批量失效兑换码。
     *
     * @param dto 失效参数
     * @return 失效结果
     */
    ExchangeCodeBatchInvalidResultVO batchInvalidate(ExchangeCodeBatchInvalidDTO dto);
    /**
     * 查询可下载二维码的兑换码列表。
     *
     * @param packageId 卡包ID
     * @param onlyUnbound 是否只查询未绑定兑换码
     * @return 可下载二维码的兑换码列表
     */
    List<ExchangeCodeDetailVO> listDownloadableCodes(Long packageId, boolean onlyUnbound);
    /**
     * 查询兑换码明细。
     *
     * @param id 兑换码明细ID
     * @return 兑换码明细
     */
    ExchangeCodeDetailVO getCodeDetail(Long id);
    /**
     * 绑定兑换码。
     *
     * @param dto 绑定参数
     * @param userId 用户ID
     * @param phone 手机号
     * @return 绑定结果
     */
    ExchangeCodeBindResultVO bindExchangeCode(ExchangeCodeBindDTO dto, Long userId, String phone);
    /**
     * 查询用户兑换码余额映射。
     *
     * @param userIds 用户ID集合
     * @return 用户余额映射
     */
    Map<Long, java.math.BigDecimal> getUserExchangeCodeBalanceMap(List<Long> userIds);
    /**
     * 扣减用户兑换码余额。
     *
     * @param dto 扣减参数
     * @return 扣减结果
     */
    ExchangeCodeConsumeResultVO consumeBalance(ExchangeCodeConsumeDTO dto);
    /**
     * 退款返还用户兑换码余额。
     *
     * @param dto 退款参数
     * @return 退款结果
     */
    ExchangeCodeConsumeResultVO refundBalance(ExchangeCodeConsumeDTO dto);
    /**
     * 分页查询兑换码余额流水。
     *
     * @param query 查询条件
     * @return 流水分页结果
     */
    Page<TExchangeCodeUseRecord> useRecordPage(ExchangeCodeUseRecordQuery query);
    /**
     * 查询兑换码余额流水详情。
     *
     * @param id 流水ID
     * @param userId 用户ID
     * @return 流水详情
     */
    TExchangeCodeUseRecord useRecordDetail(Long id, Long userId);
}
ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/TExchangeCodePackageServiceImpl.java
@@ -73,6 +73,12 @@
    @Resource
    private TExchangeCodeUseRecordMapper useRecordMapper;
    /**
     * 分页查询兑换码卡包列表。
     *
     * @param query 查询条件
     * @return 卡包分页结果
     */
    @Override
    public PageInfo<TExchangeCodePackage> pageList(ExchangeCodePackageQuery query) {
        PageInfo<TExchangeCodePackage> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize());
@@ -81,6 +87,11 @@
        return pageInfo;
    }
    /**
     * 新增兑换码卡包。
     *
     * @param dto 卡包保存参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void savePackage(ExchangeCodePackageSaveDTO dto) {
@@ -91,6 +102,11 @@
        refreshPackageStats(entity.getId());
    }
    /**
     * 编辑兑换码卡包。
     *
     * @param dto 卡包保存参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePackage(ExchangeCodePackageSaveDTO dto) {
@@ -112,6 +128,12 @@
        refreshPackageStats(dto.getId());
    }
    /**
     * 更新兑换码卡包启用状态。
     *
     * @param id 卡包ID
     * @param status 启用状态
     */
    @Override
    public void updatePackageStatus(Long id, Integer status) {
        if (id == null) {
@@ -130,6 +152,9 @@
        this.updateById(entity);
    }
    /**
     * 刷新所有已过领取截止日期的兑换码卡包未绑定兑换码状态。
     */
    @Override
    public void refreshExpiredPackageCodes() {
        List<TExchangeCodePackage> expiredPackages = this.list(new QueryWrapper<TExchangeCodePackage>()
@@ -141,6 +166,11 @@
        }
    }
    /**
     * 删除兑换码卡包。
     *
     * @param ids 卡包ID,多个ID用英文逗号分隔
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deletePackage(String ids) {
@@ -166,6 +196,12 @@
        codeDetailMapper.delete(new QueryWrapper<TExchangeCodeDetail>().in("package_id", idList));
    }
    /**
     * 查询兑换码卡包详情。
     *
     * @param id 卡包ID
     * @return 卡包详情
     */
    @Override
    public ExchangeCodePackageDetailVO getDetail(Long id) {
        TExchangeCodePackage entity = this.getById(id);
@@ -200,6 +236,12 @@
        return detailVO;
    }
    /**
     * 分页查询兑换码明细。
     *
     * @param query 查询条件
     * @return 兑换码明细分页结果
     */
    @Override
    public PageInfo<ExchangeCodeDetailVO> codePage(ExchangeCodeDetailQuery query) {
        refreshExpiredPackageCodes(query.getPackageId());
@@ -209,12 +251,24 @@
        return pageInfo;
    }
    /**
     * 导出兑换码明细。
     *
     * @param query 查询条件
     * @return 兑换码明细导出数据
     */
    @Override
    public List<ExchangeCodeDetailVO> exportCodes(ExchangeCodeDetailQuery query) {
        refreshExpiredPackageCodes(query.getPackageId());
        return codeDetailMapper.exportList(query);
    }
    /**
     * 批量失效兑换码。
     *
     * @param dto 失效参数
     * @return 失效结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ExchangeCodeBatchInvalidResultVO batchInvalidate(ExchangeCodeBatchInvalidDTO dto) {
@@ -242,6 +296,13 @@
        return new ExchangeCodeBatchInvalidResultVO(success, skip);
    }
    /**
     * 查询可下载二维码的兑换码列表。
     *
     * @param packageId 卡包ID
     * @param onlyUnbound 是否只查询未绑定兑换码
     * @return 可下载二维码的兑换码列表
     */
    @Override
    public List<ExchangeCodeDetailVO> listDownloadableCodes(Long packageId, boolean onlyUnbound) {
        ExchangeCodeDetailQuery query = new ExchangeCodeDetailQuery();
@@ -252,6 +313,12 @@
        return exportCodes(query);
    }
    /**
     * 查询兑换码明细。
     *
     * @param id 兑换码明细ID
     * @return 兑换码明细
     */
    @Override
    public ExchangeCodeDetailVO getCodeDetail(Long id) {
        TExchangeCodeDetail detail = codeDetailMapper.selectById(id);
@@ -270,6 +337,14 @@
        return vo;
    }
    /**
     * 绑定兑换码。
     *
     * @param dto 绑定参数
     * @param userId 用户ID
     * @param phone 手机号
     * @return 绑定结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ExchangeCodeBindResultVO bindExchangeCode(ExchangeCodeBindDTO dto, Long userId, String phone) {
@@ -334,6 +409,12 @@
        return resultVO;
    }
    /**
     * 分页查询兑换码余额流水。
     *
     * @param query 查询条件
     * @return 流水分页结果
     */
    @Override
    public Page<TExchangeCodeUseRecord> useRecordPage(ExchangeCodeUseRecordQuery query) {
        Page<TExchangeCodeUseRecord> page = useRecordMapper.selectPage(Page.of(query.getPageCurr(), query.getPageSize()),
@@ -348,6 +429,13 @@
        return page;
    }
    /**
     * 查询兑换码余额流水详情。
     *
     * @param id 流水ID
     * @param userId 用户ID
     * @return 流水详情
     */
    @Override
    public TExchangeCodeUseRecord useRecordDetail(Long id, Long userId) {
        TExchangeCodeUseRecord record = useRecordMapper.selectOne(new QueryWrapper<TExchangeCodeUseRecord>()
@@ -359,6 +447,12 @@
        return record;
    }
    /**
     * 查询用户兑换码余额映射。
     *
     * @param userIds 用户ID集合
     * @return 用户余额映射
     */
    @Override
    public Map<Long, BigDecimal> getUserExchangeCodeBalanceMap(List<Long> userIds) {
        Map<Long, BigDecimal> result = new HashMap<>();
@@ -378,6 +472,12 @@
        return result;
    }
    /**
     * 扣减用户兑换码余额。
     *
     * @param dto 扣减参数
     * @return 扣减结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ExchangeCodeConsumeResultVO consumeBalance(ExchangeCodeConsumeDTO dto) {
@@ -437,6 +537,12 @@
        return resultVO;
    }
    /**
     * 退款返还用户兑换码余额。
     *
     * @param dto 退款参数
     * @return 退款结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ExchangeCodeConsumeResultVO refundBalance(ExchangeCodeConsumeDTO dto) {
@@ -501,6 +607,13 @@
        return resultVO;
    }
    /**
     * 根据兑换码明细记录的赠送比例计算本次使用金额中的赠送金额。
     *
     * @param useAmount 本次使用金额
     * @param detail 兑换码明细
     * @return 本次使用金额对应的赠送金额
     */
    private BigDecimal calculateGiftUsedAmount(BigDecimal useAmount, TExchangeCodeDetail detail) {
        if (useAmount == null || detail == null || detail.getGiftRatio() == null) {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
@@ -508,6 +621,12 @@
        return useAmount.multiply(detail.getGiftRatio()).setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 查询兑换码明细当前剩余可用金额。
     *
     * @param detail 兑换码明细
     * @return 剩余可用金额
     */
    private BigDecimal getDetailRemainAmount(TExchangeCodeDetail detail) {
        if (detail == null || detail.getPackageSpecId() == null) {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
@@ -522,6 +641,13 @@
                : remainAmount.setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 将保存参数转换为兑换码卡包实体。
     *
     * @param dto 卡包保存参数
     * @param entity 待填充的卡包实体
     * @return 填充后的卡包实体
     */
    private TExchangeCodePackage buildPackageEntity(ExchangeCodePackageSaveDTO dto, TExchangeCodePackage entity) {
        entity.setPackageName(dto.getPackageName());
        entity.setCoverPicture(dto.getCoverPicture());
@@ -539,6 +665,12 @@
        return entity;
    }
    /**
     * 校验兑换码卡包新增或编辑参数。
     *
     * @param dto 卡包保存参数
     * @param editing 是否为编辑场景
     */
    private void validateSaveDTO(ExchangeCodePackageSaveDTO dto, boolean editing) {
        if (!StringUtils.hasText(dto.getPackageName())) {
            throw new ServiceException("请输入卡包名称");
@@ -589,6 +721,13 @@
        allocateDiscount(dto.getSpecList(), gift, total);
    }
    /**
     * 按规格小计占比分摊卡包赠送金额。
     *
     * @param specList 规格列表
     * @param giftAmount 赠送金额
     * @param totalAmount 卡包总金额
     */
    private void allocateDiscount(List<ExchangeCodePackageSpecDTO> specList, BigDecimal giftAmount, BigDecimal totalAmount) {
        if (giftAmount.compareTo(BigDecimal.ZERO) == 0 || totalAmount.compareTo(BigDecimal.ZERO) == 0) {
            for (ExchangeCodePackageSpecDTO spec : specList) {
@@ -611,6 +750,14 @@
        }
    }
    /**
     * 保存兑换码规格并按新增库存生成兑换码明细。
     *
     * @param packageId 卡包ID
     * @param specList 规格列表
     * @param inventory 当前库存
     * @param oldInventory 编辑前库存,新增场景为空
     */
    private void saveSpecsAndCodes(Long packageId, List<ExchangeCodePackageSpecDTO> specList, Integer inventory, Integer oldInventory) {
        List<TExchangeCodePackageSpec> specEntities = new ArrayList<>();
        for (int i = 0; i < specList.size(); i++) {
@@ -656,6 +803,12 @@
        }
    }
    /**
     * 计算卡包赠送金额占总金额的比例。
     *
     * @param exchangePackage 兑换码卡包
     * @return 赠送金额比例
     */
    private BigDecimal calculateGiftRatio(TExchangeCodePackage exchangePackage) {
        if (exchangePackage == null || exchangePackage.getTotalAmount() == null
                || exchangePackage.getTotalAmount().compareTo(BigDecimal.ZERO) == 0) {
@@ -665,6 +818,13 @@
                .divide(exchangePackage.getTotalAmount(), 6, RoundingMode.HALF_UP);
    }
    /**
     * 计算单个兑换码对应的赠送金额。
     *
     * @param spec 兑换码规格
     * @param giftRatio 赠送金额比例
     * @return 单个兑换码赠送金额
     */
    private BigDecimal calculateDetailGiftAmount(TExchangeCodePackageSpec spec, BigDecimal giftRatio) {
        if (spec == null || spec.getAmount() == null || giftRatio == null) {
            return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
@@ -672,6 +832,11 @@
        return spec.getAmount().multiply(giftRatio).setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 刷新兑换码卡包的已绑定、已失效和未绑定数量。
     *
     * @param packageId 卡包ID
     */
    private void refreshPackageStats(Long packageId) {
        TExchangeCodePackage entity = this.getById(packageId);
        if (entity == null) {
@@ -691,6 +856,11 @@
        this.baseMapper.updateById(entity);
    }
    /**
     * 将已过领取截止日期卡包下未绑定的兑换码刷新为已失效。
     *
     * @param packageId 卡包ID
     */
    private void refreshExpiredPackageCodes(Long packageId) {
        if (packageId == null) {
            return;
@@ -711,14 +881,32 @@
        refreshPackageStats(packageId);
    }
    /**
     * 生成兑换码卡号。
     *
     * @param packageId 卡包ID
     * @param seq 当前批次序号
     * @return 兑换码卡号
     */
    private String generateCode(Long packageId, int seq) {
        return "CD" + packageId + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss")) + String.format("%03d", seq);
    }
    /**
     * 生成兑换码密码。
     *
     * @return 兑换码密码
     */
    private String generatePassword() {
        return UUID.randomUUID().toString().replace("-", "").substring(0, 8).toUpperCase();
    }
    /**
     * 合并同一订单或同一兑换码维度的余额流水展示记录。
     *
     * @param records 原始流水列表
     * @return 合并后的流水列表
     */
    private List<TExchangeCodeUseRecord> mergeUseRecords(List<TExchangeCodeUseRecord> records) {
        if (records == null || records.isEmpty()) {
            return records;
@@ -744,6 +932,12 @@
        return new ArrayList<>(recordMap.values());
    }
    /**
     * 构建余额流水合并键。
     *
     * @param record 余额流水记录
     * @return 流水合并键
     */
    private String buildUseRecordMergeKey(TExchangeCodeUseRecord record) {
        Long orderId = record.getOrderId() == null ? 0L : record.getOrderId();
        Long detailId = record.getDetailId() == null ? 0L : record.getDetailId();
@@ -755,6 +949,12 @@
        return detailId + "_" + changeType + "_" + refundNo;
    }
    /**
     * 填充单条余额流水发生后的用户当前余额。
     *
     * @param record 余额流水记录
     * @param userId 用户ID
     */
    private void fillUseRecordBalance(TExchangeCodeUseRecord record, Long userId) {
        if (record == null || userId == null || record.getId() == null) {
            return;
@@ -766,6 +966,12 @@
        record.setCurrentBalance(calculateCurrentBalance(records));
    }
    /**
     * 批量填充余额流水发生后的用户当前余额。
     *
     * @param records 余额流水列表
     * @param userId 用户ID
     */
    private void fillUseRecordBalances(List<TExchangeCodeUseRecord> records, Long userId) {
        if (records == null || records.isEmpty() || userId == null) {
            return;
@@ -784,6 +990,12 @@
        }
    }
    /**
     * 根据流水列表计算用户当前余额。
     *
     * @param records 余额流水列表
     * @return 当前余额
     */
    private BigDecimal calculateCurrentBalance(List<TExchangeCodeUseRecord> records) {
        BigDecimal balance = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
        if (records == null) {
@@ -795,6 +1007,13 @@
        return balance;
    }
    /**
     * 将单条余额流水变动应用到当前余额。
     *
     * @param balance 当前余额
     * @param record 余额流水记录
     * @return 应用流水后的余额
     */
    private BigDecimal applyUseRecordChange(BigDecimal balance, TExchangeCodeUseRecord record) {
        BigDecimal amount = defaultMoney(record == null ? null : record.getUsedAmount());
        Integer changeType = record == null ? null : record.getChangeType();
@@ -804,6 +1023,11 @@
        return balance.add(amount).setScale(2, RoundingMode.HALF_UP);
    }
    /**
     * 填充余额流水前端展示字段。
     *
     * @param record 余额流水记录
     */
    private void fillUseRecordView(TExchangeCodeUseRecord record) {
        if (record == null) {
            return;
@@ -829,6 +1053,13 @@
        }
    }
    /**
     * 根据使用金额和赠送比例填充流水的总使用金额与赠送使用金额。
     *
     * @param record 余额流水记录
     * @param amount 使用金额
     * @param giftRatio 赠送金额比例
     */
    private void fillUseRecordAmount(TExchangeCodeUseRecord record, BigDecimal amount, BigDecimal giftRatio) {
        BigDecimal usedAmount = defaultMoney(amount);
        BigDecimal ratio = giftRatio == null ? BigDecimal.ZERO : giftRatio;
@@ -845,6 +1076,12 @@
        record.setGiftUsedAmount(giftUsedAmount);
    }
    /**
     * 将金额空值归零并统一保留两位小数。
     *
     * @param amount 原始金额
     * @return 标准金额
     */
    private BigDecimal defaultMoney(BigDecimal amount) {
        return amount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
                : amount.setScale(2, RoundingMode.HALF_UP);