feat(用户余额): 新增用户余额交易记录功能

添加用户余额交易记录相关功能,包括实体类、Mapper、Service、Controller等组件
实现交易记录的增删改查及状态管理功能
移除数据库表中transaction_time字段及相关索引
This commit is contained in:
dzq 2025-07-07 09:19:15 +08:00
parent 27489e68eb
commit 1a6d627482
15 changed files with 570 additions and 4 deletions

View File

@ -0,0 +1,67 @@
package com.agileboot.admin.controller.ab98.balance;
import com.agileboot.admin.customize.aop.accessLog.AccessLog;
import com.agileboot.common.core.base.BaseController;
import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.enums.common.BusinessTypeEnum;
import com.agileboot.domain.ab98.balance.UserBalanceRecordApplicationService;
import com.agileboot.domain.ab98.balance.command.AddUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.command.UpdateUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import com.agileboot.domain.ab98.balance.query.SearchUserBalanceRecordQuery;
import com.agileboot.domain.common.command.BulkOperationCommand;
import io.swagger.v3.oas.annotations.Operation;
import java.util.List;
import javax.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/ab98/balance-records")
@RequiredArgsConstructor
@Validated
public class UserBalanceRecordController extends BaseController {
private final UserBalanceRecordApplicationService userBalanceRecordApplicationService;
@Operation(summary = "余额记录列表")
@GetMapping
public ResponseDTO<PageDTO<UserBalanceRecordEntity>> list(SearchUserBalanceRecordQuery<UserBalanceRecordEntity> query) {
PageDTO<UserBalanceRecordEntity> page = userBalanceRecordApplicationService.getUserBalanceRecordList(query);
return ResponseDTO.ok(page);
}
@Operation(summary = "新增余额记录")
@AccessLog(title = "余额记录管理", businessType = BusinessTypeEnum.ADD)
@PostMapping
public ResponseDTO<Void> add(@Validated @RequestBody AddUserBalanceRecordCommand command) {
userBalanceRecordApplicationService.addUserBalanceRecord(command);
return ResponseDTO.ok();
}
@Operation(summary = "修改余额记录")
@AccessLog(title = "余额记录管理", businessType = BusinessTypeEnum.MODIFY)
@PutMapping("/{id}")
public ResponseDTO<Void> edit(@PathVariable Long id, @Validated @RequestBody UpdateUserBalanceRecordCommand command) {
command.setBalanceRecordId(id);
userBalanceRecordApplicationService.updateUserBalanceRecord(command);
return ResponseDTO.ok();
}
@Operation(summary = "删除余额记录")
@AccessLog(title = "余额记录管理", businessType = BusinessTypeEnum.DELETE)
@DeleteMapping("/{ids}")
public ResponseDTO<Void> remove(@PathVariable @NotNull List<Long> ids) {
userBalanceRecordApplicationService.deleteUserBalanceRecord(new BulkOperationCommand<>(ids));
return ResponseDTO.ok();
}
}

View File

@ -0,0 +1,68 @@
package com.agileboot.domain.ab98.balance;
import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.agileboot.domain.ab98.balance.command.AddUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.command.UpdateUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordService;
import com.agileboot.domain.ab98.balance.model.UserBalanceRecordModel;
import com.agileboot.domain.ab98.balance.model.UserBalanceRecordModelFactory;
import com.agileboot.domain.common.command.BulkOperationCommand;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@Slf4j
@RequiredArgsConstructor
public class UserBalanceRecordApplicationService {
private final UserBalanceRecordService userBalanceRecordService;
private final UserBalanceRecordModelFactory userBalanceRecordModelFactory;
public PageDTO<UserBalanceRecordEntity> getUserBalanceRecordList(AbstractPageQuery<UserBalanceRecordEntity> query) {
Page<UserBalanceRecordEntity> page = userBalanceRecordService.getUserBalanceRecordList(query);
List<UserBalanceRecordEntity> records = page.getRecords();
return new PageDTO<>(records, page.getTotal());
}
public void addUserBalanceRecord(AddUserBalanceRecordCommand command) {
UserBalanceRecordModel model = userBalanceRecordModelFactory.create();
model.loadAddCommand(command);
model.insert();
}
public void updateUserBalanceRecord(UpdateUserBalanceRecordCommand command) {
UserBalanceRecordModel model = userBalanceRecordModelFactory.loadById(command.getBalanceRecordId());
model.loadUpdateCommand(command);
model.updateById();
}
public void deleteUserBalanceRecord(BulkOperationCommand<Long> command) {
for (Long id : command.getIds()) {
UserBalanceRecordModel model = userBalanceRecordModelFactory.loadById(id);
model.deleteById();
}
}
public List<UserBalanceRecordEntity> selectAllSuccessRecords() {
return userBalanceRecordService.selectAllSuccessRecords();
}
public UserBalanceRecordEntity getFirstSuccessRecord() {
return userBalanceRecordService.getFirstSuccessRecord();
}
public UserBalanceRecordEntity getByAb98UserId(Long ab98UserId) {
return userBalanceRecordService.getByAb98UserId(ab98UserId);
}
public UserBalanceRecordEntity getByTradeId(String tradeId) {
return userBalanceRecordService.getByTradeId(tradeId);
}
}

View File

@ -0,0 +1,11 @@
package com.agileboot.domain.ab98.balance.command;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class AddUserBalanceRecordCommand extends UserBalanceRecordEntity {
}

View File

@ -0,0 +1,16 @@
package com.agileboot.domain.ab98.balance.command;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.PositiveOrZero;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class UpdateUserBalanceRecordCommand extends AddUserBalanceRecordCommand {
@NotNull
@PositiveOrZero
private Long balanceRecordId;
}

View File

@ -0,0 +1,72 @@
package com.agileboot.domain.ab98.balance.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-07-07
*/
@Getter
@Setter
@TableName("user_balance_record")
@ApiModel(value = "UserBalanceRecordEntity对象", description = "用户余额交易记录表")
public class UserBalanceRecordEntity extends BaseEntity<UserBalanceRecordEntity> {
private static final long serialVersionUID = 1L;
@ApiModelProperty("主键ID")
@TableId(value = "balance_record_id", type = IdType.AUTO)
private Long balanceRecordId;
@ApiModelProperty("汇邦云用户唯一ID")
@TableField("ab98_user_id")
private Long ab98UserId;
@ApiModelProperty("openid")
@TableField("openid")
private String openid;
@ApiModelProperty("交易类型1充值 2消费 3退款")
@TableField("transaction_type")
private Integer transactionType;
@ApiModelProperty("消费或退款时使用的充值id")
@TableField("use_record_id")
private Long useRecordId;
@ApiModelProperty("交易金额,单位分")
@TableField("amount")
private Integer amount;
@ApiModelProperty("交易状态0未完成 1成功 2失败")
@TableField("`status`")
private Integer status;
@ApiModelProperty("支付网关交易id")
@TableField("trade_id")
private String tradeId;
@ApiModelProperty("业务系统订单ID对接外部系统")
@TableField("biz_order_id")
private String bizOrderId;
@Override
public Serializable pkVal() {
return this.balanceRecordId;
}
}

View File

@ -0,0 +1,48 @@
package com.agileboot.domain.ab98.balance.db;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* <p>
* 用户余额交易记录表 Mapper 接口
* </p>
*
* @author valarchie
* @since 2025-07-07
*/
public interface UserBalanceRecordMapper extends BaseMapper<UserBalanceRecordEntity> {
@Select("SELECT balance_record_id, ab98_user_id, openid, transaction_type, use_record_id, amount, status, trade_id, biz_order_id, create_time, update_time " +
"FROM user_balance_record " +
"${ew.customSqlSegment}")
Page<UserBalanceRecordEntity> getUserBalanceRecordList(
Page<UserBalanceRecordEntity> page,
@Param(Constants.WRAPPER) Wrapper<UserBalanceRecordEntity> queryWrapper
);
@Select("SELECT * " +
"FROM user_balance_record " +
"WHERE status = 1 " +
"ORDER BY create_time DESC " +
"LIMIT 1")
UserBalanceRecordEntity selectFirstSuccessRecord();
@Select("SELECT * " +
"FROM user_balance_record " +
"WHERE status = 1 " +
"ORDER BY create_time DESC")
List<UserBalanceRecordEntity> selectAllSuccessRecords();
@Select("SELECT * FROM user_balance_record WHERE ab98_user_id = #{ab98UserId} LIMIT 1")
UserBalanceRecordEntity selectByAb98UserId(Long ab98UserId);
@Select("SELECT * FROM user_balance_record WHERE trade_id = #{tradeId} LIMIT 1")
UserBalanceRecordEntity selectByTradeId(String tradeId);
}

View File

@ -0,0 +1,28 @@
package com.agileboot.domain.ab98.balance.db;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
/**
* <p>
* 用户余额交易记录表 服务类
* </p>
*
* @author valarchie
* @since 2025-07-07
*/
public interface UserBalanceRecordService extends IService<UserBalanceRecordEntity> {
Page<UserBalanceRecordEntity> getUserBalanceRecordList(AbstractPageQuery<UserBalanceRecordEntity> query);
List<UserBalanceRecordEntity> selectAllSuccessRecords();
UserBalanceRecordEntity getFirstSuccessRecord();
UserBalanceRecordEntity getByAb98UserId(Long ab98UserId);
UserBalanceRecordEntity getByTradeId(String tradeId);
}

View File

@ -0,0 +1,48 @@
package com.agileboot.domain.ab98.balance.db;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import java.util.List;
import org.apache.ibatis.annotations.Param;
/**
* <p>
* 用户余额交易记录表 服务实现类
* </p>
*
* @author valarchie
* @since 2025-07-07
*/
@Service
public class UserBalanceRecordServiceImpl extends ServiceImpl<UserBalanceRecordMapper, UserBalanceRecordEntity> implements UserBalanceRecordService {
@Override
public Page<UserBalanceRecordEntity> getUserBalanceRecordList(AbstractPageQuery<UserBalanceRecordEntity> query) {
return baseMapper.getUserBalanceRecordList(query.toPage(), query.toQueryWrapper());
}
@Override
public List<UserBalanceRecordEntity> selectAllSuccessRecords() {
return baseMapper.selectAllSuccessRecords();
}
@Override
public UserBalanceRecordEntity getFirstSuccessRecord() {
return baseMapper.selectFirstSuccessRecord();
}
@Override
public UserBalanceRecordEntity getByAb98UserId(Long ab98UserId) {
return baseMapper.selectByAb98UserId(ab98UserId);
}
@Override
public UserBalanceRecordEntity getByTradeId(String tradeId) {
return baseMapper.selectByTradeId(tradeId);
}
}

View File

@ -0,0 +1,96 @@
package com.agileboot.domain.ab98.balance.dto;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.agileboot.common.annotation.ExcelColumn;
import com.agileboot.common.annotation.ExcelSheet;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import lombok.Data;
import java.util.Date;
@ExcelSheet(name = "用户余额交易记录")
@Data
public class UserBalanceRecordDTO {
public UserBalanceRecordDTO(UserBalanceRecordEntity entity) {
if (entity != null) {
BeanUtil.copyProperties(entity, this);
// 交易类型转换
if (entity.getTransactionType() != null) {
switch (entity.getTransactionType()) {
case 1:
this.transactionTypeStr = "充值";
break;
case 2:
this.transactionTypeStr = "消费";
break;
case 3:
this.transactionTypeStr = "退款";
break;
default:
this.transactionTypeStr = "未知";
}
}
// 交易状态转换
if (entity.getStatus() != null) {
switch (entity.getStatus()) {
case 0:
this.statusStr = "未完成";
break;
case 1:
this.statusStr = "成功";
break;
case 2:
this.statusStr = "失败";
break;
default:
this.statusStr = "未知";
}
}
// 格式化创建时间
if (entity.getCreateTime() != null) {
this.createTimeStr = DateUtil.format(entity.getCreateTime(), "yyyy-MM-dd HH:mm:ss");
}
}
}
@ExcelColumn(name = "主键ID")
private Long balanceRecordId;
@ExcelColumn(name = "汇邦云用户唯一ID")
private Long ab98UserId;
@ExcelColumn(name = "openid")
private String openid;
@ExcelColumn(name = "交易类型")
private String transactionTypeStr;
private Integer transactionType;
@ExcelColumn(name = "关联充值记录ID")
private Long useRecordId;
@ExcelColumn(name = "交易金额(分)")
private Integer amount;
@ExcelColumn(name = "交易状态")
private String statusStr;
private Integer status;
@ExcelColumn(name = "支付网关交易ID")
private String tradeId;
@ExcelColumn(name = "业务系统订单ID")
private String bizOrderId;
@ExcelColumn(name = "创建时间")
private String createTimeStr;
private Date createTime;
}

View File

@ -0,0 +1,41 @@
package com.agileboot.domain.ab98.balance.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.balance.db.UserBalanceRecordEntity;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordService;
import com.agileboot.domain.ab98.balance.command.AddUserBalanceRecordCommand;
import com.agileboot.domain.ab98.balance.command.UpdateUserBalanceRecordCommand;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class UserBalanceRecordModel extends UserBalanceRecordEntity {
private UserBalanceRecordService userBalanceRecordService;
public UserBalanceRecordModel(UserBalanceRecordEntity entity, UserBalanceRecordService userBalanceRecordService) {
this(userBalanceRecordService);
if (entity != null) {
BeanUtil.copyProperties(entity, this);
}
}
public UserBalanceRecordModel(UserBalanceRecordService userBalanceRecordService) {
this.userBalanceRecordService = userBalanceRecordService;
}
public void loadAddCommand(AddUserBalanceRecordCommand command) {
if (command != null) {
BeanUtil.copyProperties(command, this, "balanceRecordId");
}
}
public void loadUpdateCommand(UpdateUserBalanceRecordCommand command) {
if (command != null) {
loadAddCommand(command);
}
}
}

View File

@ -0,0 +1,27 @@
package com.agileboot.domain.ab98.balance.model;
import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class UserBalanceRecordModelFactory {
private final UserBalanceRecordService userBalanceRecordService;
public UserBalanceRecordModel loadById(Long balanceRecordId) {
UserBalanceRecordEntity entity = userBalanceRecordService.getById(balanceRecordId);
if (entity == null) {
throw new ApiException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, balanceRecordId, "用户余额交易记录");
}
return new UserBalanceRecordModel(entity, userBalanceRecordService);
}
public UserBalanceRecordModel create() {
return new UserBalanceRecordModel(userBalanceRecordService);
}
}

View File

@ -0,0 +1,41 @@
package com.agileboot.domain.ab98.balance.query;
import cn.hutool.core.util.StrUtil;
import com.agileboot.common.core.page.AbstractPageQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.agileboot.domain.ab98.balance.db.UserBalanceRecordEntity;
import java.util.Date;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
public class SearchUserBalanceRecordQuery<T> extends AbstractPageQuery<T> {
private Long ab98UserId;
private String openid;
private Integer transactionType;
private Integer status;
private String tradeId;
private String bizOrderId;
private Date startTime;
private Date endTime;
@Override
public QueryWrapper<T> addQueryCondition() {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
queryWrapper
.eq(ab98UserId != null, "ab98_user_id", ab98UserId)
.like(StrUtil.isNotEmpty(openid), "openid", openid)
.eq(transactionType != null, "transaction_type", transactionType)
.eq(status != null, "status", status)
.like(StrUtil.isNotEmpty(tradeId), "trade_id", tradeId)
.like(StrUtil.isNotEmpty(bizOrderId), "biz_order_id", bizOrderId)
.between(startTime != null && endTime != null, "create_time", startTime, endTime);
this.timeRangeColumn = "create_time";
return queryWrapper;
}
}

View File

@ -0,0 +1,5 @@
<?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.agileboot.domain.ab98.balance.db.UserBalanceRecordMapper">
</mapper>

View File

@ -61,7 +61,7 @@ public class CodeGenerator {
//生成的类 放在orm子模块下的/target/generated-code目录底下 //生成的类 放在orm子模块下的/target/generated-code目录底下
.module("/agileboot-orm/target/generated-code") .module("/agileboot-orm/target/generated-code")
.parentPackage("com.agileboot") .parentPackage("com.agileboot")
.tableName("approval_goods_cell") .tableName("user_balance_record")
// 决定是否继承基类 // 决定是否继承基类
.isExtendsFromBaseEntity(true) .isExtendsFromBaseEntity(true)
.build(); .build();

View File

@ -8,13 +8,11 @@ CREATE TABLE `user_balance_record` (
`status` tinyint NOT NULL DEFAULT 0 COMMENT '交易状态0未完成 1成功 2失败', `status` tinyint NOT NULL DEFAULT 0 COMMENT '交易状态0未完成 1成功 2失败',
`trade_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '支付网关交易id', `trade_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '支付网关交易id',
`biz_order_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务系统订单ID对接外部系统', `biz_order_id` varchar(32) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '业务系统订单ID对接外部系统',
`transaction_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '交易时间',
`creator_id` bigint DEFAULT 0 COMMENT '创建者ID', `creator_id` bigint DEFAULT 0 COMMENT '创建者ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`updater_id` bigint DEFAULT 0 COMMENT '更新者ID', `updater_id` bigint DEFAULT 0 COMMENT '更新者ID',
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志0存在 1删除', `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志0存在 1删除',
PRIMARY KEY (`balance_record_id`), PRIMARY KEY (`balance_record_id`),
KEY `idx_ab98_user_id` (`ab98_user_id`), KEY `idx_ab98_user_id` (`ab98_user_id`)
KEY `idx_transaction_time` (`transaction_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户余额交易记录表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户余额交易记录表';