feat(订单): 添加汇邦云用户ID支持并重构余额处理逻辑

- 在订单相关表中添加ab98_user_id字段
- 新增MoneyUtil工具类处理金额转换
- 重构订单和退款逻辑使用user_balance表存储余额
- 添加根据汇邦云用户ID查询余额的接口
This commit is contained in:
dzq 2025-11-25 11:36:25 +08:00
parent bf381fcd35
commit ca62ab0ae9
8 changed files with 188 additions and 21 deletions

View File

@ -167,19 +167,23 @@ public class CaffeineController {
result.put("data", caffeineCacheService.dynamicCodeCache.getAll()); result.put("data", caffeineCacheService.dynamicCodeCache.getAll());
break; break;
default: default:
Map<String, Object> map = new HashMap<>();
map.put("error", "未知的缓存类型: " + cacheName);
map.put("supportedTypes", new String[]{
"captchaCache", "loginUserCache", "userCache",
"roleCache", "postCache", "qyUseridCache", "dynamicCodeCache"
});
return ResponseEntity.badRequest() return ResponseEntity.badRequest()
.body(Map.of("error", "未知的缓存类型: " + cacheName, .body(map);
"supportedTypes", new String[]{
"captchaCache", "loginUserCache", "userCache",
"roleCache", "postCache", "qyUseridCache", "dynamicCodeCache"
}));
} }
return ResponseEntity.ok(result); return ResponseEntity.ok(result);
} catch (Exception e) { } catch (Exception e) {
log.error("获取缓存数据失败: {}", cacheName, e); log.error("获取缓存数据失败: {}", cacheName, e);
Map<String, Object> map = new HashMap<>();
map.put("error", "获取缓存数据失败: " + e.getMessage());
return ResponseEntity.internalServerError() return ResponseEntity.internalServerError()
.body(Map.of("error", "获取缓存数据失败: " + e.getMessage())); .body(map);
} }
} }
} }

View File

@ -10,9 +10,12 @@ import com.agileboot.common.core.dto.ResponseDTO;
import com.agileboot.common.exception.ApiException; import com.agileboot.common.exception.ApiException;
import com.agileboot.common.exception.error.ErrorCode; import com.agileboot.common.exception.error.ErrorCode;
import com.agileboot.common.exception.error.ErrorCode.Client; import com.agileboot.common.exception.error.ErrorCode.Client;
import com.agileboot.common.utils.MoneyUtil;
import com.agileboot.common.utils.OpenSignUtil; import com.agileboot.common.utils.OpenSignUtil;
import com.agileboot.domain.ab98.user.Ab98UserApplicationService; import com.agileboot.domain.ab98.user.Ab98UserApplicationService;
import com.agileboot.domain.ab98.user.db.Ab98UserEntity; import com.agileboot.domain.ab98.user.db.Ab98UserEntity;
import com.agileboot.domain.ab98.user_balance.UserBalanceApplicationService;
import com.agileboot.domain.ab98.user_balance.dto.UserBalanceDTO;
import com.agileboot.domain.common.dto.QyLoginDTO; import com.agileboot.domain.common.dto.QyLoginDTO;
import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService; import com.agileboot.domain.qywx.accessToken.AccessTokenApplicationService;
import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity; import com.agileboot.domain.qywx.accessToken.db.QyAccessTokenEntity;
@ -72,6 +75,7 @@ public class PaymentController {
private final PaymentApplicationService paymentApplicationService; private final PaymentApplicationService paymentApplicationService;
private final PaymentOperationLogApplicationService paymentOperationLogApplicationService; private final PaymentOperationLogApplicationService paymentOperationLogApplicationService;
private final Ab98UserApplicationService ab98UserApplicationService; private final Ab98UserApplicationService ab98UserApplicationService;
private final UserBalanceApplicationService userBalanceApplicationService;
private final WxshopConfig wxshopConfig; private final WxshopConfig wxshopConfig;
// 新增回调接口 // 新增回调接口
@ -308,7 +312,7 @@ public class PaymentController {
Ab98UserEntity ab98User = ab98UserApplicationService.getByAb98UserId(qyUser.getAb98UserId()); Ab98UserEntity ab98User = ab98UserApplicationService.getByAb98UserId(qyUser.getAb98UserId());
try { try {
if (qyUser.getAb98UserId()!= null) { if (qyUser.getAb98UserId() != null) {
ab98User.setOpenid(openid); ab98User.setOpenid(openid);
ab98User.updateById(); ab98User.updateById();
} }
@ -316,13 +320,15 @@ public class PaymentController {
log.error("更新汇邦云绑定的微信openid失败", e); log.error("更新汇邦云绑定的微信openid失败", e);
} }
UserBalanceDTO userBalance = userBalanceApplicationService.getByCorpidAndAb98UserId(corpid, ab98User.getAb98UserId());
// 创建响应对象假设GetBalanceResponse包含balance字段 // 创建响应对象假设GetBalanceResponse包含balance字段
GetBalanceResponse response = new GetBalanceResponse( GetBalanceResponse response = new GetBalanceResponse(
qyUser.getUserid(), qyUser.getUserid(),
qyUser.getCorpid(), qyUser.getCorpid(),
qyUser.getBalance(), userBalance != null ? MoneyUtil.fenToYuan(userBalance.getBalance()) : BigDecimal.ZERO,
qyUser.getUseBalance(), userBalance != null ? MoneyUtil.fenToYuan(userBalance.getUseBalance()) : BigDecimal.ZERO,
qyUser.getBalanceLimit(), userBalance != null ? MoneyUtil.fenToYuan(userBalance.getBalanceLimit()) : BigDecimal.ZERO,
ab98User); ab98User);
return ResponseDTO.ok(response); return ResponseDTO.ok(response);
} }
@ -342,13 +348,53 @@ public class PaymentController {
ab98User = ab98UserApplicationService.getByAb98UserId(qyUser.getAb98UserId()); ab98User = ab98UserApplicationService.getByAb98UserId(qyUser.getAb98UserId());
} }
if (ab98User == null) {
return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "未找到对应的汇邦云用户记录"));
}
UserBalanceDTO userBalance = userBalanceApplicationService.getByCorpidAndAb98UserId(corpid, ab98User.getAb98UserId());
// 创建响应对象假设GetBalanceResponse包含balance字段 // 创建响应对象假设GetBalanceResponse包含balance字段
GetBalanceResponse response = new GetBalanceResponse( GetBalanceResponse response = new GetBalanceResponse(
qyUser.getUserid(), qyUser.getUserid(),
qyUser.getCorpid(), qyUser.getCorpid(),
qyUser.getBalance(), userBalance != null ? MoneyUtil.fenToYuan(userBalance.getBalance()) : BigDecimal.ZERO,
qyUser.getUseBalance(), userBalance != null ? MoneyUtil.fenToYuan(userBalance.getUseBalance()) : BigDecimal.ZERO,
qyUser.getBalanceLimit(), userBalance != null ? MoneyUtil.fenToYuan(userBalance.getBalanceLimit()) : BigDecimal.ZERO,
ab98User);
return ResponseDTO.ok(response);
}
/**
* 根据企业微信ID和汇邦云用户ID查询用户余额
* @param corpid 企业微信ID
* @param ab98UserId 汇邦云用户ID
* @return 包含用户余额信息的响应结果
* @apiNote 该接口直接查询user_balance表返回原始余额数据单位
*/
@GetMapping("/getUserBalance")
public ResponseDTO<GetBalanceResponse> getUserBalance(@RequestParam String corpid, @RequestParam Long ab98UserId) {
if (corpid == null || ab98UserId == null) {
return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "corpid和ab98UserId不能为空"));
}
Ab98UserEntity ab98User = ab98UserApplicationService.getByAb98UserId(ab98UserId);
if (ab98User == null) {
return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "未找到对应的汇邦云用户记录"));
}
UserBalanceDTO userBalance = userBalanceApplicationService.getByCorpidAndAb98UserId(corpid, ab98UserId);
if (userBalance == null) {
return ResponseDTO.fail(new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "未找到对应的用户余额记录"));
}
GetBalanceResponse response = new GetBalanceResponse(
null,
corpid,
MoneyUtil.fenToYuan(userBalance.getBalance()),
MoneyUtil.fenToYuan(userBalance.getUseBalance()),
MoneyUtil.fenToYuan(userBalance.getBalanceLimit()),
ab98User); ab98User);
return ResponseDTO.ok(response); return ResponseDTO.ok(response);
} }

View File

@ -0,0 +1,70 @@
package com.agileboot.common.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* 金额工具类
* 提供分Long和元BigDecimal之间的转换
*
* @author agileboot
*/
public class MoneyUtil {
/**
* 分转换为元BigDecimal保留两位小数
*
* @param fen 金额单位
* @return 金额单位保留两位小数
*/
public static BigDecimal fenToYuan(Long fen) {
if (fen == null) {
return BigDecimal.ZERO;
}
return new BigDecimal(fen).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
}
/**
* 元转换为分Long
*
* @param yuan 金额单位
* @return 金额单位
*/
public static Long yuanToFen(BigDecimal yuan) {
if (yuan == null) {
return 0L;
}
return yuan.multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP).longValue();
}
/**
* 分转换为元格式的字符串保留两位小数
*
* @param fen 金额单位
* @return 金额字符串单位格式如"100.00"
*/
public static String fenToYuanString(Long fen) {
if (fen == null) {
return "0.00";
}
return fenToYuan(fen).toString();
}
/**
* 元格式的字符串转换为分
*
* @param yuanStr 金额字符串单位格式如"100.00"
* @return 金额单位
*/
public static Long yuanStringToFen(String yuanStr) {
if (yuanStr == null || yuanStr.trim().isEmpty()) {
return 0L;
}
try {
BigDecimal yuan = new BigDecimal(yuanStr.trim());
return yuanToFen(yuan);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("无效的金额格式: " + yuanStr, e);
}
}
}

View File

@ -3,6 +3,10 @@ package com.agileboot.domain.shop.approval;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.agileboot.common.config.WxshopConfig; import com.agileboot.common.config.WxshopConfig;
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 org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.agileboot.common.constant.UrlConstants; import com.agileboot.common.constant.UrlConstants;
import com.agileboot.common.constant.WeixinConstants; import com.agileboot.common.constant.WeixinConstants;
@ -112,6 +116,7 @@ public class ReturnApprovalApplicationService {
private final CabinetMainboardService cabinetMainboardService; private final CabinetMainboardService cabinetMainboardService;
private final CabinetCellOperationModelFactory cabinetCellOperationModelFactory; private final CabinetCellOperationModelFactory cabinetCellOperationModelFactory;
private final MqttService mqttService; private final MqttService mqttService;
private final UserBalanceService userBalanceService;
@Autowired @Autowired
private WxshopConfig wxshopConfig; private WxshopConfig wxshopConfig;
@ -259,15 +264,31 @@ public class ReturnApprovalApplicationService {
} else if (Objects.equals(orderModel.getPaymentMethod(), "balance")) { } else if (Objects.equals(orderModel.getPaymentMethod(), "balance")) {
// 余额退款 // 余额退款
try { try {
QyAuthCorpInfoEntity authCorpInfo = authCorpInfoApplicationService.selectByCorpid(model.getCorpid()); Long ab98UserId = orderModel.getAb98UserId();
QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(authCorpInfo.getAppid(), model.getCorpid()); if (null == ab98UserId) {
String userid = QywxApiUtil.convertToUserid(accessToken.getAccessToken(), orderModel.getOpenid()).getUserid(); QyAuthCorpInfoEntity authCorpInfo = authCorpInfoApplicationService.selectByCorpid(model.getCorpid());
QyUserEntity qyUser = userService.getUserByUserIdAndCorpid(userid, model.getCorpid()); QyAccessTokenEntity accessToken = accessTokenApplicationService.getByAppid(authCorpInfo.getAppid(), model.getCorpid());
if (null != qyUser) { String userid = QywxApiUtil.convertToUserid(accessToken.getAccessToken(), orderModel.getOpenid()).getUserid();
QyUserEntity qyUser = userService.getUserByUserIdAndCorpid(userid, model.getCorpid());
ab98UserId = qyUser.getAb98UserId();
}
if (null == ab98UserId) {
throw new IllegalArgumentException("ab98用户不存在");
}
UserBalanceEntity userBalance = userBalanceService.getByCorpidAndAb98UserId(model.getCorpid(), ab98UserId);
if (null == userBalance) {
throw new IllegalArgumentException("用户余额不存在");
}
userBalance.setBalance(userBalance.getBalance() + MoneyUtil.yuanToFen(command.getReturnAmount()));
userBalance.setUseBalance(userBalance.getUseBalance() - MoneyUtil.yuanToFen(command.getReturnAmount()));
userBalance.updateById();
/*if (null != qyUser) {
qyUser.setBalance(qyUser.getBalance().add(command.getReturnAmount())); qyUser.setBalance(qyUser.getBalance().add(command.getReturnAmount()));
qyUser.setUseBalance(qyUser.getUseBalance().subtract(command.getReturnAmount())); qyUser.setUseBalance(qyUser.getUseBalance().subtract(command.getReturnAmount()));
} }
userService.updateById(qyUser); userService.updateById(qyUser);*/
} catch (Exception e) { } catch (Exception e) {
log.error("余额退款失败", e); log.error("余额退款失败", e);
} }

View File

@ -3,6 +3,10 @@ package com.agileboot.domain.shop.order;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.agileboot.common.config.WxshopConfig; import com.agileboot.common.config.WxshopConfig;
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 org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import com.agileboot.common.core.page.PageDTO; import com.agileboot.common.core.page.PageDTO;
import com.agileboot.common.exception.ApiException; import com.agileboot.common.exception.ApiException;
@ -96,6 +100,7 @@ public class OrderApplicationService {
private final QyUserService qyUserService; private final QyUserService qyUserService;
private final Ab98UserService ab98UserService; private final Ab98UserService ab98UserService;
private final WxUserService wxUserService; private final WxUserService wxUserService;
private final UserBalanceService userBalanceService;
@Autowired @Autowired
private WxshopConfig wxshopConfig; private WxshopConfig wxshopConfig;
@ -187,6 +192,7 @@ public class OrderApplicationService {
orderModel.setIsInternal(command.getIsInternal()); orderModel.setIsInternal(command.getIsInternal());
orderModel.setUserid(command.getQyUserid()); orderModel.setUserid(command.getQyUserid());
orderModel.setName(command.getName()); orderModel.setName(command.getName());
orderModel.setAb98UserId(command.getAb98UserId());
orderModel.setIsDeductStock(0); orderModel.setIsDeductStock(0);
orderModel.setMode(command.getMode()); orderModel.setMode(command.getMode());
orderModel.insert(); orderModel.insert();
@ -217,7 +223,7 @@ public class OrderApplicationService {
return new CreateOrderResult(orderModel.getOrderId(), orderModel.getTotalAmount(), paymentResponse, BigDecimal.valueOf(0)); return new CreateOrderResult(orderModel.getOrderId(), orderModel.getTotalAmount(), paymentResponse, BigDecimal.valueOf(0));
} else if (Objects.equals(command.getPaymentType(), "balance")) { } else if (Objects.equals(command.getPaymentType(), "balance")) {
QyUserEntity qyUser = userService.getUserByUserIdAndCorpid(command.getUserid(), command.getCorpid()); /*QyUserEntity qyUser = userService.getUserByUserIdAndCorpid(command.getUserid(), command.getCorpid());
// 余额不足 // 余额不足
if (qyUser.getBalance().compareTo(orderModel.getTotalAmount()) < 0) { if (qyUser.getBalance().compareTo(orderModel.getTotalAmount()) < 0) {
throw new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "余额不足"); throw new ApiException(ErrorCode.Client.COMMON_REQUEST_PARAMETERS_INVALID, "余额不足");
@ -225,14 +231,25 @@ public class OrderApplicationService {
qyUser.setBalance(qyUser.getBalance().subtract(orderModel.getTotalAmount())); qyUser.setBalance(qyUser.getBalance().subtract(orderModel.getTotalAmount()));
qyUser.setUseBalance(qyUser.getUseBalance().add(orderModel.getTotalAmount())); qyUser.setUseBalance(qyUser.getUseBalance().add(orderModel.getTotalAmount()));
userService.updateById(qyUser); userService.updateById(qyUser);
}*/
UserBalanceEntity userBalance = userBalanceService.getByCorpidAndAb98UserId(command.getCorpid(), command.getAb98UserId());
// 余额不足
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()));
userBalanceService.updateById(userBalance);
} }
// 金额转换元转分并四舍五入 // 金额转换元转分并四舍五入
BigDecimal amountInFen = orderModel.getTotalAmount() BigDecimal amountInFen = orderModel.getTotalAmount()
.multiply(new BigDecimal("100")) .multiply(new BigDecimal("100"))
.setScale(0, RoundingMode.HALF_UP); .setScale(0, RoundingMode.HALF_UP);
handlePaymentSuccess(orderModel.getOrderId(), Integer.valueOf(amountInFen.toPlainString()), handlePaymentSuccess(orderModel.getOrderId(), Integer.valueOf(amountInFen.toPlainString()),
"balance-" + orderModel.getOrderId(), DateUtil.formatDateTime(new Date())); "balance-" + orderModel.getOrderId(), DateUtil.formatDateTime(new Date()));
return new CreateOrderResult(orderModel.getOrderId(), orderModel.getTotalAmount(), null, qyUser.getBalance()); return new CreateOrderResult(orderModel.getOrderId(), orderModel.getTotalAmount(), null, MoneyUtil.fenToYuan(userBalance.getBalance()));
} else if (Objects.equals(command.getPaymentType(), "approval")) { } else if (Objects.equals(command.getPaymentType(), "approval")) {
submitAssetApproval(orderModel, goodsList, command); submitAssetApproval(orderModel, goodsList, command);
return new CreateOrderResult(orderModel.getOrderId(), orderModel.getTotalAmount(), null, BigDecimal.valueOf(0)); return new CreateOrderResult(orderModel.getOrderId(), orderModel.getTotalAmount(), null, BigDecimal.valueOf(0));

View File

@ -45,4 +45,7 @@ public class SubmitOrderCommand {
@ApiModelProperty("是否为微信小程序支付0否 1是") @ApiModelProperty("是否为微信小程序支付0否 1是")
private Integer isWxMp; private Integer isWxMp;
@ApiModelProperty("汇邦云用户ID")
private Long ab98UserId;
} }

View File

@ -57,6 +57,10 @@ public class ShopOrderEntity extends BaseEntity<ShopOrderEntity> {
@TableField("name") @TableField("name")
private String name; private String name;
@ApiModelProperty("汇邦云用户ID")
@TableField(value = "ab98_user_id")
private Long ab98UserId;
@ApiModelProperty("是否内部用户0否 1汇邦云用户 2企业微信用户") @ApiModelProperty("是否内部用户0否 1汇邦云用户 2企业微信用户")
@TableField("is_internal") @TableField("is_internal")
private Integer isInternal; private Integer isInternal;

View File

@ -21,3 +21,5 @@ CREATE TABLE `user_balance` (
-- 添加 deleted 字段的 ALTER 语句 -- 添加 deleted 字段的 ALTER 语句
ALTER TABLE `user_balance` ADD COLUMN `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志0存在 1删除'; ALTER TABLE `user_balance` ADD COLUMN `deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标志0存在 1删除';
ALTER TABLE `shop_order` ADD COLUMN `ab98_user_id` bigint DEFAULT NULL COMMENT '汇邦云用户ID' AFTER `name`;