部分支付
parent
8236f0d7aa
commit
8c34ebb528
|
|
@ -11,6 +11,7 @@ import lombok.experimental.Accessors;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 付费项配置
|
||||
|
|
@ -77,7 +78,7 @@ public class ExpenseItemsConfigEntity {
|
|||
* 公司ids
|
||||
*/
|
||||
@TableField(typeHandler = JsonTypeHandler.class)
|
||||
private String companyIds;
|
||||
private List<String> companyIds;
|
||||
|
||||
/**
|
||||
* 税率
|
||||
|
|
|
|||
|
|
@ -31,6 +31,11 @@ public class MoneyChangeDetailEntity {
|
|||
*/
|
||||
private Long userId;
|
||||
|
||||
/**
|
||||
* 订单id
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
/**
|
||||
* 资金账户 Id
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package com.njzscloud.supervisory.order.contant;
|
||||
|
||||
import com.njzscloud.common.core.ienum.DictStr;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
/**
|
||||
* 字典代码:payment_status
|
||||
* 字典名称:支付状态
|
||||
*/
|
||||
@Getter
|
||||
@RequiredArgsConstructor
|
||||
public enum PaymentWay implements DictStr {
|
||||
COMPANY("company", "公司支付"),
|
||||
WX("wx", "微信"),
|
||||
;
|
||||
private final String val;
|
||||
private final String txt;
|
||||
}
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
package com.njzscloud.supervisory.order.controller;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.njzscloud.common.core.ex.Exceptions;
|
||||
import com.njzscloud.common.core.utils.R;
|
||||
import com.njzscloud.supervisory.money.contant.MoneyChangeCategory;
|
||||
import com.njzscloud.supervisory.money.pojo.entity.MoneyAccountEntity;
|
||||
import com.njzscloud.supervisory.money.pojo.entity.MoneyChangeDetailEntity;
|
||||
import com.njzscloud.supervisory.money.service.MoneyAccountService;
|
||||
import com.njzscloud.supervisory.money.service.MoneyChangeDetailService;
|
||||
import com.njzscloud.supervisory.order.contant.PaymentStatus;
|
||||
import com.njzscloud.supervisory.order.contant.PaymentWay;
|
||||
import com.njzscloud.supervisory.order.pojo.entity.OrderExpenseItemsEntity;
|
||||
import com.njzscloud.supervisory.order.pojo.param.PaymentItemParam;
|
||||
import com.njzscloud.supervisory.order.pojo.param.PaymentParam;
|
||||
import com.njzscloud.supervisory.order.pojo.result.PaymentContextResult;
|
||||
import com.njzscloud.supervisory.order.service.OrderExpenseItemsService;
|
||||
import com.njzscloud.supervisory.order.service.OrderInfoService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 支付相关接口
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/payment")
|
||||
@RequiredArgsConstructor
|
||||
public class PaymentController {
|
||||
|
||||
private final OrderExpenseItemsService orderExpenseItemsService;
|
||||
private final OrderInfoService orderInfoService;
|
||||
private final MoneyAccountService moneyAccountService;
|
||||
private final MoneyChangeDetailService moneyChangeDetailService;
|
||||
|
||||
|
||||
/**
|
||||
* 发起支付(占位接口,当前返回空支付内容)
|
||||
*/
|
||||
@PostMapping("/pay")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public R<?> pay(@RequestBody PaymentParam paymentParam) {
|
||||
// 入参校验(占位):支付方式、订单ID、支付清单不能为空
|
||||
if (paymentParam == null
|
||||
|| paymentParam.getOrderId() == null
|
||||
|| Strings.isNullOrEmpty(paymentParam.getPaymentCategory())
|
||||
|| paymentParam.getSettleTotalMoney() == null
|
||||
|| paymentParam.getSettleTotalMoney().signum() < 0
|
||||
|| paymentParam.getItems() == null) {
|
||||
throw Exceptions.clierr("参数不完整或支付总金额小于0");
|
||||
}
|
||||
// 校验支付清单:id、名称、金额
|
||||
boolean invalidItem = paymentParam.getItems().stream().anyMatch(it ->
|
||||
it == null
|
||||
|| it.getId() == null
|
||||
|| it.getExpenseItemName() == null || it.getExpenseItemName().length() == 0
|
||||
|| it.getSettleMoney() == null || it.getSettleMoney().signum() < 0
|
||||
);
|
||||
if (invalidItem) {
|
||||
throw Exceptions.clierr("支付清单项不合法(id/名称/金额)");
|
||||
}
|
||||
|
||||
// 校验支付方式
|
||||
if (paymentParam.getPaymentCategory() == null ||
|
||||
!("wx".equals(paymentParam.getPaymentCategory()) ||
|
||||
"company".equals(paymentParam.getPaymentCategory()))) {
|
||||
throw Exceptions.clierr("支付方式不合法");
|
||||
}
|
||||
|
||||
// 校验数据库中结算金额是否与请求一致,并统计总金额
|
||||
BigDecimal dbTotalAmount = BigDecimal.ZERO;
|
||||
for (PaymentItemParam it : paymentParam.getItems()) {
|
||||
OrderExpenseItemsEntity entity = orderExpenseItemsService.getById(it.getId());
|
||||
if (entity == null) {
|
||||
throw Exceptions.clierr("支付清单项不存在,id=" + it.getId());
|
||||
}
|
||||
BigDecimal dbSettle = entity.getSettleMoney();
|
||||
BigDecimal reqSettle = it.getSettleMoney();
|
||||
if (dbSettle == null) {
|
||||
throw Exceptions.clierr("系统结算金额缺失,id=" + it.getId());
|
||||
}
|
||||
if (dbSettle.compareTo(reqSettle) != 0) {
|
||||
throw Exceptions.clierr("结算金额不一致【" + it.getExpenseItemName() + "】(id=" + it.getId()
|
||||
+ "),系统:" + dbSettle + ",请求:" + reqSettle);
|
||||
}
|
||||
// 累加数据库中的结算金额
|
||||
dbTotalAmount = dbTotalAmount.add(dbSettle);
|
||||
}
|
||||
|
||||
// 验证总金额是否与参数中的结算总金额一致
|
||||
if (paymentParam.getSettleTotalMoney() == null ||
|
||||
paymentParam.getSettleTotalMoney().compareTo(dbTotalAmount) != 0) {
|
||||
throw Exceptions.clierr("结算总金额与清单金额不一致");
|
||||
}
|
||||
|
||||
// 验证总金额是否与参数中的结算总金额一致(这里已经是第二层验证了)
|
||||
// 第一层验证在循环中已经完成:每个清单项的金额与数据库一致
|
||||
// 第二层验证:参数中的结算总金额与数据库统计总金额一致
|
||||
|
||||
// 查询订单及相关支付上下文(公司、司机账户)
|
||||
PaymentContextResult ctx = orderInfoService.paymentContext(paymentParam.getOrderId());
|
||||
if (ctx == null || ctx.getOrderId() == null) {
|
||||
throw Exceptions.clierr("订单不存在");
|
||||
}
|
||||
|
||||
// 根据支付方式处理支付
|
||||
if (PaymentWay.COMPANY.getVal().equals(paymentParam.getPaymentCategory())) {
|
||||
// 公司支付:根据订单 trans_company_id -> biz_company.user_id -> money_account
|
||||
if (ctx.getTransCompanyId() == null || ctx.getCompanyUserId() == null) {
|
||||
throw Exceptions.clierr("订单未关联清运公司,无法公司支付");
|
||||
}
|
||||
if (ctx.getCompanyBalance() == null || ctx.getCompanyBalance().compareTo(dbTotalAmount) < 0) {
|
||||
throw Exceptions.clierr("公司账户余额不足");
|
||||
}
|
||||
|
||||
// 直接扣减公司账户余额
|
||||
deductCompanyBalance(ctx, dbTotalAmount, paymentParam.getOrderId());
|
||||
} else if (PaymentWay.WX.getVal().equals(paymentParam.getPaymentCategory())) {
|
||||
// 微信支付:当前不接入,模拟失败以触发回滚
|
||||
throw Exceptions.clierr("微信支付失败(占位):已回滚之前扣款");
|
||||
} else {
|
||||
throw Exceptions.clierr("不支持的支付方式");
|
||||
}
|
||||
|
||||
// 3. 更新订单支付状态为已支付
|
||||
orderInfoService.lambdaUpdate()
|
||||
.eq(com.njzscloud.supervisory.order.pojo.entity.OrderInfoEntity::getId, paymentParam.getOrderId())
|
||||
.set(com.njzscloud.supervisory.order.pojo.entity.OrderInfoEntity::getPaymentStatus, PaymentStatus.YiZhiFu)
|
||||
.update();
|
||||
|
||||
log.info("订单支付成功,订单ID:{},支付方式:{},支付金额:{}",
|
||||
paymentParam.getOrderId(), paymentParam.getPaymentCategory(), dbTotalAmount);
|
||||
|
||||
return R.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 扣减公司账户余额
|
||||
*/
|
||||
private void deductCompanyBalance(PaymentContextResult ctx, BigDecimal amount, Long orderId) {
|
||||
// 验证资金账户信息
|
||||
if (ctx.getCompanyAccountId() == null) {
|
||||
throw Exceptions.clierr("公司资金账户不存在");
|
||||
}
|
||||
|
||||
BigDecimal oldBalance = ctx.getCompanyBalance();
|
||||
BigDecimal newBalance = oldBalance.subtract(amount);
|
||||
|
||||
// 更新账户余额
|
||||
MoneyAccountEntity companyAccount = new MoneyAccountEntity()
|
||||
.setId(ctx.getCompanyAccountId())
|
||||
.setMoney(newBalance);
|
||||
moneyAccountService.updateById(companyAccount);
|
||||
|
||||
// 记录资金变动明细
|
||||
MoneyChangeDetailEntity changeDetail = new MoneyChangeDetailEntity()
|
||||
.setUserId(ctx.getCompanyUserId())
|
||||
.setOrderId(orderId)
|
||||
.setMoneyAccountId(companyAccount.getId())
|
||||
.setOldMoney(oldBalance)
|
||||
.setDelta(amount.negate()) // 扣减为负数
|
||||
.setNewMoney(newBalance)
|
||||
.setMoneyChangeCategory(MoneyChangeCategory.DingDanKouKuan)
|
||||
.setMemo("订单支付扣款,订单ID:" + orderId);
|
||||
|
||||
moneyChangeDetailService.save(changeDetail);
|
||||
|
||||
log.info("公司账户扣减成功,用户ID:{},扣减金额:{},余额:{} -> {}",
|
||||
ctx.getCompanyUserId(), amount, oldBalance, newBalance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.njzscloud.supervisory.order.pojo.entity.OrderInfoEntity;
|
||||
import com.njzscloud.supervisory.order.pojo.result.OrderPagingResult;
|
||||
import com.njzscloud.supervisory.order.pojo.result.PaymentContextResult;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
|
|
@ -27,4 +28,6 @@ public interface OrderInfoMapper extends BaseMapper<OrderInfoEntity> {
|
|||
void busyDriver(@Param("driverId") Long driverId, @Param("busy") Boolean busy);
|
||||
|
||||
void busyTruck(@Param("truckId") Long truckId, @Param("busy") Boolean busy);
|
||||
|
||||
PaymentContextResult paymentContext(@Param("orderId") Long orderId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import lombok.experimental.Accessors;
|
|||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 订单付费项
|
||||
|
|
@ -82,7 +83,7 @@ public class OrderExpenseItemsEntity {
|
|||
* 公司ids
|
||||
*/
|
||||
@TableField(typeHandler = JsonTypeHandler.class)
|
||||
private String companyIds;
|
||||
private List<String> companyIds;
|
||||
|
||||
/**
|
||||
* 税率
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package com.njzscloud.supervisory.order.pojo.param;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
public class PaymentItemParam {
|
||||
/**
|
||||
* 订单付费项记录 Id(order_expense_items.id)
|
||||
*/
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 付费项名称
|
||||
*/
|
||||
private String expenseItemName;
|
||||
|
||||
/**
|
||||
* 结算金额(必须 >= 0)
|
||||
*/
|
||||
private BigDecimal settleMoney;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package com.njzscloud.supervisory.order.pojo.param;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
public class PaymentParam {
|
||||
|
||||
/**
|
||||
* 订单 Id
|
||||
*/
|
||||
private Long orderId;
|
||||
|
||||
|
||||
/**
|
||||
* 支付方式:wx / company
|
||||
*/
|
||||
private String paymentCategory;
|
||||
|
||||
/**
|
||||
* 结算总金额(必须 >= 0)
|
||||
*/
|
||||
private BigDecimal settleTotalMoney;
|
||||
|
||||
/**
|
||||
* 支付清单
|
||||
*/
|
||||
private List<PaymentItemParam> items;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.njzscloud.supervisory.order.pojo.result;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@Accessors(chain = true)
|
||||
public class PaymentContextResult {
|
||||
|
||||
private Long orderId;
|
||||
private Long transCompanyId;
|
||||
|
||||
private Long companyUserId;
|
||||
private Long companyAccountId;
|
||||
private BigDecimal companyBalance;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -11,7 +11,6 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njzscloud.common.core.ex.Exceptions;
|
||||
import com.njzscloud.common.mp.support.PageParam;
|
||||
|
|
@ -37,8 +36,8 @@ import com.njzscloud.supervisory.order.pojo.param.*;
|
|||
import com.njzscloud.supervisory.order.pojo.result.OrderCertificateResult;
|
||||
import com.njzscloud.supervisory.order.pojo.result.OrderPagingResult;
|
||||
import com.njzscloud.supervisory.order.pojo.result.TrainBillResult;
|
||||
import com.njzscloud.supervisory.order.pojo.result.PaymentContextResult;
|
||||
import com.njzscloud.supervisory.order.pojo.entity.OrderExpenseItemsEntity;
|
||||
import com.njzscloud.supervisory.order.mapper.OrderExpenseItemsMapper;
|
||||
import com.njzscloud.supervisory.goods.contant.MoneyStrategy;
|
||||
import com.njzscloud.supervisory.expense.pojo.entity.ExpenseItemsConfigEntity;
|
||||
import com.njzscloud.supervisory.expense.contant.Scope;
|
||||
|
|
@ -65,7 +64,7 @@ import static com.njzscloud.supervisory.constant.Constant.ROLE_JG;
|
|||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEntity> implements IService<OrderInfoEntity> {
|
||||
public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEntity> {
|
||||
|
||||
private final BizAuditConfigService bizAuditConfigService;
|
||||
private final OrderGoodsService orderGoodsService;
|
||||
|
|
@ -113,6 +112,10 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
|
|||
}
|
||||
}
|
||||
|
||||
public PaymentContextResult paymentContext(Long orderId) {
|
||||
return this.baseMapper.paymentContext(orderId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
*/
|
||||
|
|
@ -747,7 +750,7 @@ public class OrderInfoService extends ServiceImpl<OrderInfoMapper, OrderInfoEnti
|
|||
return bizObj == BizObj.QiYe || bizObj == BizObj.GeTi;
|
||||
}
|
||||
if (scope == Scope.CUSTOMER) {
|
||||
String companyIds = cfg.getCompanyIds();
|
||||
List<String> companyIds = cfg.getCompanyIds();
|
||||
return companyIds != null && transCompanyId != null && companyIds.contains(String.valueOf(transCompanyId));
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -191,4 +191,16 @@
|
|||
FROM biz_driver
|
||||
WHERE user_id = #{userId}
|
||||
</select>
|
||||
<select id="paymentContext" resultType="com.njzscloud.supervisory.order.pojo.result.PaymentContextResult">
|
||||
SELECT
|
||||
a.id AS orderId,
|
||||
a.trans_company_id AS transCompanyId,
|
||||
bc.user_id AS companyUserId,
|
||||
ma1.id AS companyAccountId,
|
||||
ma1.money AS companyBalance
|
||||
FROM order_info a
|
||||
LEFT JOIN biz_company bc ON bc.id = a.trans_company_id
|
||||
LEFT JOIN money_account ma1 ON ma1.user_id = bc.user_id
|
||||
WHERE a.id = #{orderId}
|
||||
</select>
|
||||
</mapper>
|
||||
|
|
|
|||
Loading…
Reference in New Issue