feat(用户余额): 新增用户余额变更日志功能

添加用户余额变更日志模块,包括数据库表、实体类、服务层及领域模型
实现余额变更记录功能,支持消费和审批归还场景的日志记录
新增查询接口,支持按用户余额ID、订单ID等条件查询变更日志
This commit is contained in:
dzq 2025-12-06 16:26:55 +08:00
parent ec9a9243d2
commit 348b932556
15 changed files with 1016 additions and 4 deletions

View File

@ -209,6 +209,11 @@ public enum ErrorCode implements ErrorCodeInterface {
* 余额金额无效
*/
BALANCE_AMOUNT_INVALID(11006, "余额金额无效", "Business.BALANCE_AMOUNT_INVALID"),
/**
* 该值不允许为空
*/
VALUE_NOT_ALLOWED(11007, "该值不允许为空", "Business.VALUE_NOT_ALLOWED"),
;

View File

@ -0,0 +1,249 @@
package com.agileboot.domain.ab98.user_balance_log;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.domain.ab98.user_balance_log.command.AddUserBalanceLogCommand;
import com.agileboot.domain.ab98.user_balance_log.command.UpdateUserBalanceLogCommand;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogEntity;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogService;
import com.agileboot.domain.ab98.user_balance_log.dto.UserBalanceLogDTO;
import com.agileboot.domain.ab98.user_balance_log.model.UserBalanceLogModel;
import com.agileboot.domain.ab98.user_balance_log.model.UserBalanceLogModelFactory;
import com.agileboot.domain.ab98.user_balance_log.query.SearchUserBalanceLogQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.stream.Collectors;
/**
* <p>
* 用户余额变更日志应用服务
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@Service
@RequiredArgsConstructor
public class UserBalanceLogApplicationService {
private final UserBalanceLogService userBalanceLogService;
private final UserBalanceLogModelFactory userBalanceLogModelFactory;
/**
* 新增用户余额变更日志
*
* @param command 新增命令
* @return 用户余额变更日志DTO
*/
@Transactional(rollbackFor = Exception.class)
public UserBalanceLogDTO add(AddUserBalanceLogCommand command) {
UserBalanceLogModel model = userBalanceLogModelFactory.create(command);
boolean success = model.save();
if (!success) {
throw new RuntimeException("新增用户余额变更日志失败");
}
return new UserBalanceLogDTO(model);
}
/**
* 更新用户余额变更日志
*
* @param command 更新命令
* @return 用户余额变更日志DTO
*/
@Transactional(rollbackFor = Exception.class)
public UserBalanceLogDTO update(UpdateUserBalanceLogCommand command) {
UserBalanceLogModel model = userBalanceLogModelFactory.create(command);
boolean success = model.update();
if (!success) {
throw new RuntimeException("更新用户余额变更日志失败");
}
return new UserBalanceLogDTO(model);
}
/**
* 根据ID删除用户余额变更日志
*
* @param logId 主键ID
*/
@Transactional(rollbackFor = Exception.class)
public void delete(Long logId) {
if (logId == null) {
throw new IllegalArgumentException("日志ID不能为空");
}
boolean success = userBalanceLogService.removeById(logId);
if (!success) {
throw new RuntimeException("删除用户余额变更日志失败");
}
}
/**
* 根据ID查询用户余额变更日志
*
* @param logId 主键ID
* @return 用户余额变更日志DTO
*/
public UserBalanceLogDTO getById(Long logId) {
if (logId == null) {
return null;
}
UserBalanceLogEntity entity = userBalanceLogService.getById(logId);
return entity != null ? new UserBalanceLogDTO(entity) : null;
}
/**
* 查询用户余额变更日志列表
*
* @param query 查询参数
* @return 用户余额变更日志分页DTO
*/
public PageDTO<UserBalanceLogDTO> getList(SearchUserBalanceLogQuery query) {
// 构建查询条件
Page<UserBalanceLogEntity> page = query.toPage();
UserBalanceLogEntity entity = new UserBalanceLogEntity();
BeanUtil.copyProperties(query, entity);
// 执行查询
List<UserBalanceLogEntity> entityList = userBalanceLogService.page(page, query.addQueryCondition()).getRecords();
// 转换为DTO
List<UserBalanceLogDTO> dtoList = entityList.stream()
.map(UserBalanceLogDTO::new)
.collect(Collectors.toList());
// 获取总数
Long total = page.getTotal();
return new PageDTO<>(dtoList, total);
}
/**
* 批量删除用户余额变更日志
*
* @param logIds 主键ID列表
*/
@Transactional(rollbackFor = Exception.class)
public void batchDelete(List<Long> logIds) {
if (CollUtil.isEmpty(logIds)) {
throw new IllegalArgumentException("日志ID列表不能为空");
}
boolean success = userBalanceLogService.removeByIds(logIds);
if (!success) {
throw new RuntimeException("批量删除用户余额变更日志失败");
}
}
/**
* 根据用户余额ID查询变更日志列表
*
* @param userBalanceId 用户余额ID
* @return 用户余额变更日志DTO列表
*/
public List<UserBalanceLogDTO> getByUserBalanceId(Long userBalanceId) {
if (userBalanceId == null) {
return null;
}
List<UserBalanceLogEntity> entityList = userBalanceLogService.getByUserBalanceId(userBalanceId);
if (CollUtil.isEmpty(entityList)) {
return null;
}
return entityList.stream()
.map(UserBalanceLogDTO::new)
.collect(Collectors.toList());
}
/**
* 根据订单ID查询变更日志列表
*
* @param orderId 订单ID
* @return 用户余额变更日志DTO列表
*/
public List<UserBalanceLogDTO> getByOrderId(Long orderId) {
if (orderId == null) {
return null;
}
List<UserBalanceLogEntity> entityList = userBalanceLogService.getByOrderId(orderId);
if (CollUtil.isEmpty(entityList)) {
return null;
}
return entityList.stream()
.map(UserBalanceLogDTO::new)
.collect(Collectors.toList());
}
/**
* 根据审批ID查询变更日志列表
*
* @param approvalId 审批ID
* @return 用户余额变更日志DTO列表
*/
public List<UserBalanceLogDTO> getByApprovalId(Long approvalId) {
if (approvalId == null) {
return null;
}
List<UserBalanceLogEntity> entityList = userBalanceLogService.getByApprovalId(approvalId);
if (CollUtil.isEmpty(entityList)) {
return null;
}
return entityList.stream()
.map(UserBalanceLogDTO::new)
.collect(Collectors.toList());
}
/**
* 根据订单商品ID查询变更日志列表
*
* @param orderGoodsId 订单商品ID
* @return 用户余额变更日志DTO列表
*/
public List<UserBalanceLogDTO> getByOrderGoodsId(Long orderGoodsId) {
if (orderGoodsId == null) {
return null;
}
List<UserBalanceLogEntity> entityList = userBalanceLogService.getByOrderGoodsId(orderGoodsId);
if (CollUtil.isEmpty(entityList)) {
return null;
}
return entityList.stream()
.map(UserBalanceLogDTO::new)
.collect(Collectors.toList());
}
/**
* 根据变更类型查询变更日志列表
*
* @param changeType 变更类型
* @return 用户余额变更日志DTO列表
*/
public List<UserBalanceLogDTO> getByChangeType(Integer changeType) {
if (changeType == null) {
return null;
}
List<UserBalanceLogEntity> entityList = userBalanceLogService.getByChangeType(changeType);
if (CollUtil.isEmpty(entityList)) {
return null;
}
return entityList.stream()
.map(UserBalanceLogDTO::new)
.collect(Collectors.toList());
}
}

View File

@ -0,0 +1,45 @@
package com.agileboot.domain.ab98.user_balance_log.command;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
/**
* <p>
* 新增用户余额变更日志命令
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class AddUserBalanceLogCommand extends UserBalanceLogEntity {
@NotNull(message = "用户余额ID不能为空")
private Long userBalanceId;
@NotNull(message = "变更类型不能为空")
private Integer changeType;
@NotNull(message = "变更金额不能为空")
private Long changeAmount;
@NotNull(message = "变更前已用余额不能为空")
private Long useBalanceBefore;
@NotNull(message = "变更后已用余额不能为空")
private Long useBalanceAfter;
private Long orderId;
private Long approvalId;
private Long orderGoodsId;
@NotNull(message = "创建者ID不能为空")
private Long creatorId;
}

View File

@ -0,0 +1,24 @@
package com.agileboot.domain.ab98.user_balance_log.command;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotNull;
/**
* <p>
* 更新用户余额变更日志命令
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class UpdateUserBalanceLogCommand extends AddUserBalanceLogCommand {
@NotNull(message = "日志ID不能为空")
private Long logId;
}

View File

@ -0,0 +1,79 @@
package com.agileboot.domain.ab98.user_balance_log.db;
import com.agileboot.common.core.base.BaseEntity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
* 用户余额变更日志表
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@Getter
@Setter
@TableName("user_balance_log")
@ApiModel(value = "UserBalanceLogEntity对象", description = "用户余额变更日志表")
public class UserBalanceLogEntity extends BaseEntity<UserBalanceLogEntity> {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主键ID")
@TableId(value = "log_id", type = IdType.AUTO)
private Long logId;
@ApiModelProperty("用户余额ID")
@TableField("user_balance_id")
private Long userBalanceId;
@ApiModelProperty("变更类型(1-消费 2-审批归还 3-系统调整)")
@TableField("change_type")
private Integer changeType;
@ApiModelProperty("变更金额(单位:分,正数表示增加,负数表示减少)")
@TableField("change_amount")
private Long changeAmount;
@ApiModelProperty("变更前已用余额")
@TableField("use_balance_before")
private Long useBalanceBefore;
@ApiModelProperty("变更后已用余额")
@TableField("use_balance_after")
private Long useBalanceAfter;
@ApiModelProperty("关联订单ID")
@TableField("order_id")
private Long orderId;
@ApiModelProperty("关联审批ID")
@TableField("approval_id")
private Long approvalId;
@ApiModelProperty("关联订单商品ID")
@TableField("order_goods_id")
private Long orderGoodsId;
@ApiModelProperty("创建者ID")
@TableField("creator_id")
private Long creatorId;
@ApiModelProperty("更新者ID")
@TableField("updater_id")
private Long updaterId;
@Override
public Serializable pkVal() {
return this.logId;
}
}

View File

@ -0,0 +1,48 @@
package com.agileboot.domain.ab98.user_balance_log.db;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* <p>
* 用户余额变更日志表 Mapper 接口
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@Mapper
public interface UserBalanceLogMapper extends BaseMapper<UserBalanceLogEntity> {
/**
* 根据用户余额ID查询变更日志列表
*
* @param userBalanceId 用户余额ID
* @return 变更日志列表
*/
@Select("SELECT * FROM user_balance_log WHERE user_balance_id = #{userBalanceId} ORDER BY create_time DESC")
List<UserBalanceLogEntity> selectByUserBalanceId(@Param("userBalanceId") Long userBalanceId);
/**
* 根据订单ID查询变更日志列表
*
* @param orderId 订单ID
* @return 变更日志列表
*/
@Select("SELECT * FROM user_balance_log WHERE order_id = #{orderId} ORDER BY create_time DESC")
List<UserBalanceLogEntity> selectByOrderId(@Param("orderId") Long orderId);
/**
* 根据审批ID查询变更日志列表
*
* @param approvalId 审批ID
* @return 变更日志列表
*/
@Select("SELECT * FROM user_balance_log WHERE approval_id = #{approvalId} ORDER BY create_time DESC")
List<UserBalanceLogEntity> selectByApprovalId(@Param("approvalId") Long approvalId);
}

View File

@ -0,0 +1,57 @@
package com.agileboot.domain.ab98.user_balance_log.db;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* <p>
* 用户余额变更日志表 服务类
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
public interface UserBalanceLogService extends IService<UserBalanceLogEntity> {
/**
* 根据用户余额ID查询变更日志列表
*
* @param userBalanceId 用户余额ID
* @return 变更日志列表
*/
List<UserBalanceLogEntity> getByUserBalanceId(Long userBalanceId);
/**
* 根据订单ID查询变更日志列表
*
* @param orderId 订单ID
* @return 变更日志列表
*/
List<UserBalanceLogEntity> getByOrderId(Long orderId);
/**
* 根据审批ID查询变更日志列表
*
* @param approvalId 审批ID
* @return 变更日志列表
*/
List<UserBalanceLogEntity> getByApprovalId(Long approvalId);
/**
* 根据订单商品ID查询变更日志列表
*
* @param orderGoodsId 订单商品ID
* @return 变更日志列表
*/
List<UserBalanceLogEntity> getByOrderGoodsId(Long orderGoodsId);
/**
* 根据变更类型查询变更日志列表
*
* @param changeType 变更类型
* @return 变更日志列表
*/
List<UserBalanceLogEntity> getByChangeType(Integer changeType);
}

View File

@ -0,0 +1,95 @@
package com.agileboot.domain.ab98.user_balance_log.db;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* <p>
* 用户余额变更日志表 服务实现类
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@Service
public class UserBalanceLogServiceImpl extends ServiceImpl<UserBalanceLogMapper, UserBalanceLogEntity> implements UserBalanceLogService {
/**
* 根据用户余额ID查询变更日志列表
*
* @param userBalanceId 用户余额ID
* @return 变更日志列表
*/
@Override
public List<UserBalanceLogEntity> getByUserBalanceId(Long userBalanceId) {
if (userBalanceId == null) {
return null;
}
return baseMapper.selectByUserBalanceId(userBalanceId);
}
/**
* 根据订单ID查询变更日志列表
*
* @param orderId 订单ID
* @return 变更日志列表
*/
@Override
public List<UserBalanceLogEntity> getByOrderId(Long orderId) {
if (orderId == null) {
return null;
}
return baseMapper.selectByOrderId(orderId);
}
/**
* 根据审批ID查询变更日志列表
*
* @param approvalId 审批ID
* @return 变更日志列表
*/
@Override
public List<UserBalanceLogEntity> getByApprovalId(Long approvalId) {
if (approvalId == null) {
return null;
}
return baseMapper.selectByApprovalId(approvalId);
}
/**
* 根据订单商品ID查询变更日志列表
*
* @param orderGoodsId 订单商品ID
* @return 变更日志列表
*/
@Override
public List<UserBalanceLogEntity> getByOrderGoodsId(Long orderGoodsId) {
if (orderGoodsId == null) {
return null;
}
return lambdaQuery()
.eq(UserBalanceLogEntity::getOrderGoodsId, orderGoodsId)
.orderByDesc(UserBalanceLogEntity::getCreateTime)
.list();
}
/**
* 根据变更类型查询变更日志列表
*
* @param changeType 变更类型
* @return 变更日志列表
*/
@Override
public List<UserBalanceLogEntity> getByChangeType(Integer changeType) {
if (changeType == null) {
return null;
}
return lambdaQuery()
.eq(UserBalanceLogEntity::getChangeType, changeType)
.orderByDesc(UserBalanceLogEntity::getCreateTime)
.list();
}
}

View File

@ -0,0 +1,71 @@
package com.agileboot.domain.ab98.user_balance_log.dto;
import cn.hutool.core.bean.BeanUtil;
import com.agileboot.common.annotation.ExcelColumn;
import com.agileboot.common.annotation.ExcelSheet;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogEntity;
import lombok.Data;
/**
* <p>
* 用户余额变更日志DTO
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@ExcelSheet(name = "用户余额变更日志")
@Data
public class UserBalanceLogDTO {
public UserBalanceLogDTO() {}
public UserBalanceLogDTO(UserBalanceLogEntity entity) {
if (entity != null) {
BeanUtil.copyProperties(entity, this);
}
}
@ExcelColumn(name = "主键ID")
private Long logId;
@ExcelColumn(name = "用户余额ID")
private Long userBalanceId;
@ExcelColumn(name = "变更类型(1-消费,2-审批归还,3-系统调整)")
private Integer changeType;
@ExcelColumn(name = "变更金额(分)")
private Long changeAmount;
@ExcelColumn(name = "变更前已用余额(分)")
private Long useBalanceBefore;
@ExcelColumn(name = "变更后已用余额(分)")
private Long useBalanceAfter;
@ExcelColumn(name = "关联订单ID")
private Long orderId;
@ExcelColumn(name = "关联审批ID")
private Long approvalId;
@ExcelColumn(name = "关联订单商品ID")
private Long orderGoodsId;
@ExcelColumn(name = "创建者ID")
private Long creatorId;
@ExcelColumn(name = "创建时间")
private String createTime;
@ExcelColumn(name = "更新者ID")
private Long updaterId;
@ExcelColumn(name = "更新时间")
private String updateTime;
@ExcelColumn(name = "删除标志")
private Boolean deleted;
}

View File

@ -0,0 +1,133 @@
package com.agileboot.domain.ab98.user_balance_log.model;
import cn.hutool.core.bean.BeanUtil;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.ab98.user_balance_log.command.AddUserBalanceLogCommand;
import com.agileboot.domain.ab98.user_balance_log.command.UpdateUserBalanceLogCommand;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogEntity;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogService;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* <p>
* 用户余额变更日志领域模型
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class UserBalanceLogModel extends UserBalanceLogEntity {
private UserBalanceLogService userBalanceLogService;
public UserBalanceLogModel() {}
public UserBalanceLogModel(UserBalanceLogEntity entity, UserBalanceLogService userBalanceLogService) {
this.userBalanceLogService = userBalanceLogService;
if (entity != null) {
BeanUtil.copyProperties(entity, this);
}
}
public UserBalanceLogModel(UserBalanceLogService userBalanceLogService) {
this.userBalanceLogService = userBalanceLogService;
}
/**
* 从新增命令加载数据
*
* @param command 新增命令
*/
public void loadAddCommand(AddUserBalanceLogCommand command) {
if (command != null) {
BeanUtil.copyProperties(command, this, "logId");
}
}
/**
* 从更新命令加载数据
*
* @param command 更新命令
*/
public void loadUpdateCommand(UpdateUserBalanceLogCommand command) {
if (command != null) {
loadAddCommand(command);
if (command.getLogId() != null) {
this.setLogId(command.getLogId());
}
}
}
/**
* 验证变更类型是否有效
*
* @param changeType 变更类型
* @throws ApiException 无效变更类型异常
*/
public void validateChangeType(Integer changeType) {
if (changeType == null || changeType < 1 || changeType > 3) {
throw new ApiException(ErrorCode.Business.VALUE_NOT_ALLOWED, "变更类型", "1-消费, 2-审批归还, 3-系统调整");
}
}
/**
* 验证变更金额是否有效
*
* @param changeAmount 变更金额
* @throws ApiException 无效变更金额异常
*/
public void validateChangeAmount(Long changeAmount) {
if (changeAmount == null) {
throw new ApiException(ErrorCode.Business.VALUE_NOT_ALLOWED, "变更金额", "不能为空");
}
}
/**
* 验证变更前后的已用余额
*
* @param useBalanceBefore 变更前已用余额
* @param useBalanceAfter 变更后已用余额
* @throws ApiException 已用余额异常
*/
public void validateUseBalance(Long useBalanceBefore, Long useBalanceAfter) {
if (useBalanceBefore == null || useBalanceBefore < 0) {
throw new ApiException(ErrorCode.Business.VALUE_NOT_ALLOWED, "变更前已用余额", "不能为空且不能为负数");
}
if (useBalanceAfter == null || useBalanceAfter < 0) {
throw new ApiException(ErrorCode.Business.VALUE_NOT_ALLOWED, "变更后已用余额", "不能为空且不能为负数");
}
}
/**
* 保存到数据库
*
* @return 是否成功
*/
public boolean save() {
return userBalanceLogService.save(this);
}
/**
* 更新到数据库
*
* @return 是否成功
*/
public boolean update() {
return userBalanceLogService.updateById(this);
}
/**
* 从数据库删除
*
* @return 是否成功
*/
public boolean remove() {
return userBalanceLogService.removeById(this.getLogId());
}
}

View File

@ -0,0 +1,67 @@
package com.agileboot.domain.ab98.user_balance_log.model;
import com.agileboot.domain.ab98.user_balance_log.command.AddUserBalanceLogCommand;
import com.agileboot.domain.ab98.user_balance_log.command.UpdateUserBalanceLogCommand;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogEntity;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
/**
* <p>
* 用户余额变更日志领域模型工厂
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@Component
@RequiredArgsConstructor
public class UserBalanceLogModelFactory {
private final UserBalanceLogService userBalanceLogService;
/**
* 创建空的用户余额变更日志模型
*
* @return 用户余额变更日志模型
*/
public UserBalanceLogModel create() {
return new UserBalanceLogModel(userBalanceLogService);
}
/**
* 根据新增命令创建用户余额变更日志模型
*
* @param command 新增命令
* @return 用户余额变更日志模型
*/
public UserBalanceLogModel create(AddUserBalanceLogCommand command) {
UserBalanceLogModel model = create();
model.loadAddCommand(command);
return model;
}
/**
* 根据更新命令创建用户余额变更日志模型
*
* @param command 更新命令
* @return 用户余额变更日志模型
*/
public UserBalanceLogModel create(UpdateUserBalanceLogCommand command) {
UserBalanceLogModel model = create();
model.loadUpdateCommand(command);
return model;
}
/**
* 根据实体创建用户余额变更日志模型
*
* @param entity 实体
* @return 用户余额变更日志模型
*/
public UserBalanceLogModel create(UserBalanceLogEntity entity) {
return new UserBalanceLogModel(entity, userBalanceLogService);
}
}

View File

@ -0,0 +1,59 @@
package com.agileboot.domain.ab98.user_balance_log.query;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.agileboot.domain.ab98.user_balance_log.db.UserBalanceLogEntity;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* <p>
* 用户余额变更日志查询参数
* </p>
*
* @author valarchie
* @since 2025-12-06
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class SearchUserBalanceLogQuery extends AbstractPageQuery<UserBalanceLogEntity> {
@ApiModelProperty("用户余额ID")
private Long userBalanceId;
@ApiModelProperty("变更类型(1-消费, 2-审批归还, 3-系统调整)")
private Integer changeType;
@ApiModelProperty("关联订单ID")
private Long orderId;
@ApiModelProperty("关联审批ID")
private Long approvalId;
@ApiModelProperty("关联订单商品ID")
private Long orderGoodsId;
@ApiModelProperty("创建者ID")
private Long creatorId;
@Override
public QueryWrapper<UserBalanceLogEntity> addQueryCondition() {
QueryWrapper<UserBalanceLogEntity> queryWrapper = new QueryWrapper<>();
queryWrapper
.eq(userBalanceId != null, "user_balance_id", userBalanceId)
.eq(changeType != null, "change_type", changeType)
.eq(orderId != null, "order_id", orderId)
.eq(approvalId != null, "approval_id", approvalId)
.eq(orderGoodsId != null, "order_goods_id", orderGoodsId)
.eq(creatorId != null, "creator_id", creatorId)
.eq("deleted", false);
this.timeRangeColumn = "create_time";
return queryWrapper;
}
}

View File

@ -7,6 +7,9 @@ import com.agileboot.common.utils.MoneyUtil;
import com.agileboot.domain.ab98.user_balance.db.UserBalanceEntity;
import com.agileboot.domain.ab98.user_balance.db.UserBalanceService;
import com.agileboot.domain.ab98.user_balance.dto.UserBalanceDTO;
import com.agileboot.domain.ab98.user_balance_log.command.AddUserBalanceLogCommand;
import com.agileboot.domain.ab98.user_balance_log.model.UserBalanceLogModelFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.agileboot.common.constant.UrlConstants;
import com.agileboot.common.constant.WeixinConstants;
@ -117,6 +120,7 @@ public class ReturnApprovalApplicationService {
private final CabinetCellOperationModelFactory cabinetCellOperationModelFactory;
private final MqttService mqttService;
private final UserBalanceService userBalanceService;
private final UserBalanceLogModelFactory userBalanceLogModelFactory;
@Autowired
private WxshopConfig wxshopConfig;
@ -281,9 +285,33 @@ public class ReturnApprovalApplicationService {
if (null == userBalance) {
throw new IllegalArgumentException("用户余额不存在");
}
userBalance.setBalance(userBalance.getBalance() + MoneyUtil.yuanToFen(command.getReturnAmount()));
userBalance.setUseBalance(userBalance.getUseBalance() - MoneyUtil.yuanToFen(command.getReturnAmount()));
// 记录变更前的余额信息
Long useBalanceBefore = userBalance.getUseBalance();
Long changeAmountFen = MoneyUtil.yuanToFen(command.getReturnAmount());
// 更新余额
userBalance.setBalance(userBalance.getBalance() + changeAmountFen);
userBalance.setUseBalance(userBalance.getUseBalance() - changeAmountFen);
userBalance.updateById();
try {
// 记录余额变更日志
AddUserBalanceLogCommand logCommand = new AddUserBalanceLogCommand();
logCommand.setUserBalanceId(userBalance.getUserBalanceId());
logCommand.setChangeType(2); // 2-审批归还
logCommand.setChangeAmount(-changeAmountFen); // 审批归还为负数
logCommand.setUseBalanceBefore(useBalanceBefore);
logCommand.setUseBalanceAfter(userBalance.getUseBalance());
logCommand.setOrderId(orderModel.getOrderId());
logCommand.setApprovalId(model.getApprovalId());
logCommand.setOrderGoodsId(orderGoodsModel.getOrderGoodsId());
logCommand.initBaseEntity();
userBalanceLogModelFactory.create(logCommand).insert();
} catch (Exception e) {
log.error("记录余额变更日志失败", e);
}
/*if (null != qyUser) {
qyUser.setBalance(qyUser.getBalance().add(command.getReturnAmount()));
qyUser.setUseBalance(qyUser.getUseBalance().subtract(command.getReturnAmount()));

View File

@ -7,6 +7,8 @@ import com.agileboot.common.utils.MoneyUtil;
import com.agileboot.domain.ab98.user_balance.db.UserBalanceEntity;
import com.agileboot.domain.ab98.user_balance.db.UserBalanceService;
import com.agileboot.domain.ab98.user_balance.dto.UserBalanceDTO;
import com.agileboot.domain.ab98.user_balance_log.command.AddUserBalanceLogCommand;
import com.agileboot.domain.ab98.user_balance_log.model.UserBalanceLogModelFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.exception.ApiException;
@ -105,6 +107,7 @@ public class OrderApplicationService {
private final Ab98UserService ab98UserService;
private final WxUserService wxUserService;
private final UserBalanceService userBalanceService;
private final UserBalanceLogModelFactory userBalanceLogModelFactory;
@Autowired
private WxshopConfig wxshopConfig;
@ -260,9 +263,28 @@ public class OrderApplicationService {
if (userBalance.getBalance().compareTo(MoneyUtil.yuanToFen(orderModel.getTotalAmount())) < 0) {
throw new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "余额不足");
} else {
userBalance.setBalance(userBalance.getBalance() - MoneyUtil.yuanToFen(orderModel.getTotalAmount()));
userBalance.setUseBalance(userBalance.getUseBalance() + MoneyUtil.yuanToFen(orderModel.getTotalAmount()));
// 记录变更前的余额信息
Long useBalanceBefore = userBalance.getUseBalance();
Long changeAmount = MoneyUtil.yuanToFen(orderModel.getTotalAmount());
userBalance.setBalance(userBalance.getBalance() - changeAmount);
userBalance.setUseBalance(userBalance.getUseBalance() + changeAmount);
userBalanceService.updateById(userBalance);
try {
// 记录余额变动日志
AddUserBalanceLogCommand logCommand = new AddUserBalanceLogCommand();
logCommand.setUserBalanceId(userBalance.getUserBalanceId());
logCommand.setChangeType(1); // 1-消费
logCommand.setChangeAmount(changeAmount); // 消费为正数
logCommand.setUseBalanceBefore(useBalanceBefore);
logCommand.setUseBalanceAfter(userBalance.getUseBalance());
logCommand.setOrderId(orderModel.getOrderId());
logCommand.setCreatorId(orderModel.getCreatorId());
userBalanceLogModelFactory.create(logCommand).save();
} catch (Exception e) {
log.error("记录余额变动日志失败", e);
}
}
// 金额转换元转分并四舍五入

View File

@ -0,0 +1,30 @@
CREATE TABLE `user_balance_log` (
`log_id` bigint NOT NULL AUTO_INCREMENT COMMENT '日志ID',
`user_balance_id` bigint NOT NULL COMMENT '用户余额ID',
-- 余额变更信息
`change_type` tinyint NOT NULL COMMENT '变更类型(1-消费 2-审批归还 3-系统调整)',
`change_amount` bigint NOT NULL COMMENT '变更金额(单位:分,正数表示增加,负数表示减少)',
`use_balance_before` bigint NOT NULL COMMENT '变更前已用余额',
`use_balance_after` bigint NOT NULL COMMENT '变更后已用余额',
-- 关联表信息
`order_id` bigint DEFAULT NULL COMMENT '关联订单ID',
`approval_id` bigint DEFAULT NULL COMMENT '关联审批ID',
`order_goods_id` bigint DEFAULT NULL COMMENT '关联订单商品ID',
-- 系统字段
`creator_id` bigint NOT NULL DEFAULT 0 COMMENT '创建者ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint NOT NULL DEFAULT 0 COMMENT '更新者ID',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT 0 COMMENT '删除标志0存在 1删除',
PRIMARY KEY (`log_id`),
KEY `idx_user_balance` (`user_balance_id`),
KEY `idx_order` (`order_id`),
KEY `idx_approval` (`approval_id`),
KEY `idx_change_type` (`change_type`),
KEY `idx_create_time` (`create_time`),
KEY `idx_order_goods` (`order_goods_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户余额变更日志表';