feat: 结算账户

pull/1121/head
梁宇奇 2025-04-23 15:57:15 +08:00
parent 2c69535ed5
commit 0c9922ce94
17 changed files with 409 additions and 28 deletions

View File

@ -47,10 +47,6 @@ public class ExternalAccount extends SimpleEntity {
*
*/
private Boolean accountAuthAccess;
/**
*
*/
private String password;
/**
*
*/

View File

@ -0,0 +1,23 @@
package com.ruoyi.xkt.dto.account;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author liangyq
* @date 2025-04-23 14:17
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AccountInfoDTO {
/**
*
*/
private InternalAccountDTO internalAccount;
/**
*
*/
private ExternalAccountDTO alipayAccount;
}

View File

@ -0,0 +1,31 @@
package com.ruoyi.xkt.dto.account;
import lombok.Data;
/**
* @author liangyq
* @date 2025-04-23 14:41
*/
@Data
public class AlipayBindDTO {
/**
* ID
*/
private Long storeId;
/**
*
*/
private String accountOwnerNumber;
/**
*
*/
private String accountOwnerName;
/**
*
*/
private String accountOwnerPhoneNumber;
/**
*
*/
private String verifyCode;
}

View File

@ -0,0 +1,43 @@
package com.ruoyi.xkt.dto.account;
import lombok.Data;
/**
* @author liangyq
* @date 2025-04-23 15:03
*/
@Data
public class ExternalAccountAddDTO {
/**
* [1: 2: 3:]
*/
private Integer ownerType;
/**
* ID=-1=store_id=user_id
*/
private Long ownerId;
/**
* [1:]
*/
private Integer accountType;
/**
*
*/
private String accountOwnerNumber;
/**
*
*/
private String accountOwnerName;
/**
*
*/
private String accountOwnerPhoneNumber;
/**
*
*/
private Boolean accountAuthAccess;
/**
*
*/
private String remark;
}

View File

@ -1,4 +1,4 @@
package com.ruoyi.xkt.dto.finance;
package com.ruoyi.xkt.dto.account;
import lombok.Data;
@ -48,10 +48,6 @@ public class ExternalAccountDTO {
*
*/
private Boolean accountAuthAccess;
/**
*
*/
private String password;
/**
*
*/

View File

@ -1,4 +1,4 @@
package com.ruoyi.xkt.dto.finance;
package com.ruoyi.xkt.dto.account;
import lombok.Data;

View File

@ -0,0 +1,25 @@
package com.ruoyi.xkt.dto.account;
import lombok.Data;
/**
*
*
* @author liangyq
* @date 2025-04-01 11:57:52.493
**/
@Data
public class InternalAccountAddDTO {
/**
* [1: 2: 3:]
*/
private Integer ownerType;
/**
* ID=-1=store_id
*/
private Long ownerId;
/**
*
*/
private String remark;
}

View File

@ -1,4 +1,4 @@
package com.ruoyi.xkt.dto.finance;
package com.ruoyi.xkt.dto.account;
import lombok.Data;

View File

@ -1,4 +1,4 @@
package com.ruoyi.xkt.dto.finance;
package com.ruoyi.xkt.dto.account;
import lombok.Data;

View File

@ -0,0 +1,27 @@
package com.ruoyi.xkt.dto.account;
import lombok.Data;
/**
* @author liangyq
* @date 2025-04-23 15:32
*/
@Data
public class TransactionPasswordSetDTO {
/**
* ID
*/
private Long storeId;
/**
*
*/
private String phoneNumber;
/**
*
*/
private String verifyCode;
/**
*
*/
private String transactionPassword;
}

View File

@ -1,5 +1,8 @@
package com.ruoyi.xkt.service;
import com.ruoyi.xkt.dto.account.AccountInfoDTO;
import com.ruoyi.xkt.dto.account.AlipayBindDTO;
import com.ruoyi.xkt.dto.account.TransactionPasswordSetDTO;
import com.ruoyi.xkt.dto.finance.FinanceBillExt;
import com.ruoyi.xkt.enums.EPayChannel;
@ -15,11 +18,11 @@ public interface IAccountService {
*
* @param storeId
* @param amount
* @param password
* @param transactionPassword
* @param payChannel
* @return
*/
FinanceBillExt prepareWithdraw(Long storeId, BigDecimal amount, String password, EPayChannel payChannel);
FinanceBillExt prepareWithdraw(Long storeId, BigDecimal amount, String transactionPassword, EPayChannel payChannel);
/**
*
@ -27,4 +30,37 @@ public interface IAccountService {
* @param financeBillId
*/
void withdrawSuccess(Long financeBillId);
/**
*
*
* @param storeId
* @return
*/
AccountInfoDTO getStoreAccountInfo(Long storeId);
/**
*
*
* @param storeId
* @return
*/
AccountInfoDTO createInternalAccountIfNotExists(Long storeId);
/**
*
*
* @param transactionPasswordSet
* @return
*/
AccountInfoDTO setTransactionPassword(TransactionPasswordSetDTO transactionPasswordSet);
/**
*
*
* @param alipayBind
* @return
*/
AccountInfoDTO bindAlipay(AlipayBindDTO alipayBind);
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.xkt.service;
import com.ruoyi.xkt.domain.ExternalAccount;
import com.ruoyi.xkt.dto.account.ExternalAccountAddDTO;
import com.ruoyi.xkt.dto.finance.TransInfo;
import com.ruoyi.xkt.enums.*;
@ -25,7 +26,17 @@ public interface IExternalAccountService {
* @param accountType
* @return
*/
ExternalAccount getExternalAccount(Long ownerId, EAccountOwnerType ownerType, EAccountType accountType);
ExternalAccount getAccount(Long ownerId, EAccountOwnerType ownerType, EAccountType accountType);
/**
*
*
* @param ownerId
* @param ownerType
* @param accountType
* @return
*/
ExternalAccount getAccountAndCheck(Long ownerId, EAccountOwnerType ownerType, EAccountType accountType);
/**
*
@ -47,4 +58,18 @@ public interface IExternalAccountService {
*/
void entryTransDetail(Long srcBillId, EFinBillType srcBillType);
/**
*
*
* @param add
*/
ExternalAccount createAccount(ExternalAccountAddDTO add);
/**
*
*
* @param externalAccount
* @return
*/
int modifyAccount(ExternalAccount externalAccount);
}

View File

@ -1,6 +1,7 @@
package com.ruoyi.xkt.service;
import com.ruoyi.xkt.domain.InternalAccount;
import com.ruoyi.xkt.dto.account.InternalAccountAddDTO;
import com.ruoyi.xkt.dto.finance.TransInfo;
import com.ruoyi.xkt.enums.EAccountOwnerType;
import com.ruoyi.xkt.enums.EEntryStatus;
@ -27,7 +28,16 @@ public interface IInternalAccountService {
* @param ownerType
* @return
*/
InternalAccount getInternalAccount(Long ownerId, EAccountOwnerType ownerType);
InternalAccount getAccount(Long ownerId, EAccountOwnerType ownerType);
/**
*
*
* @param ownerId
* @param ownerType
* @return
*/
InternalAccount getAccountAndCheck(Long ownerId, EAccountOwnerType ownerType);
/**
*
@ -50,4 +60,19 @@ public interface IInternalAccountService {
*/
void entryTransDetail(Long srcBillId, EFinBillType srcBillType);
/**
*
*
* @param add
* @return
*/
InternalAccount createAccount(InternalAccountAddDTO add);
/**
*
*
* @param id ID
* @param transactionPassword md5
*/
void setTransactionPassword(Long id, String transactionPassword);
}

View File

@ -1,11 +1,14 @@
package com.ruoyi.xkt.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.xkt.domain.ExternalAccount;
import com.ruoyi.xkt.domain.InternalAccount;
import com.ruoyi.xkt.dto.account.*;
import com.ruoyi.xkt.dto.finance.FinanceBillExt;
import com.ruoyi.xkt.enums.EAccountOwnerType;
import com.ruoyi.xkt.enums.EAccountStatus;
@ -40,21 +43,22 @@ public class AccountServiceImpl implements IAccountService {
@Transactional(rollbackFor = Exception.class)
@Override
public FinanceBillExt prepareWithdraw(Long storeId, BigDecimal amount, String password, EPayChannel payChannel) {
public FinanceBillExt prepareWithdraw(Long storeId, BigDecimal amount, String transactionPassword,
EPayChannel payChannel) {
Assert.notNull(storeId);
Assert.notEmpty(password);
Assert.notEmpty(transactionPassword);
Assert.isTrue(NumberUtil.isLessOrEqual(amount, BigDecimal.ZERO), "提现金额异常");
InternalAccount internalAccount = internalAccountService.getInternalAccount(storeId, EAccountOwnerType.STORE);
ExternalAccount externalAccount = externalAccountService.getExternalAccount(storeId, EAccountOwnerType.STORE,
InternalAccount internalAccount = internalAccountService.getAccountAndCheck(storeId, EAccountOwnerType.STORE);
ExternalAccount externalAccount = externalAccountService.getAccountAndCheck(storeId, EAccountOwnerType.STORE,
EAccountType.getByChannel(payChannel));
if (!EAccountStatus.NORMAL.getValue().equals(internalAccount.getAccountStatus())
|| !EAccountStatus.NORMAL.getValue().equals(externalAccount.getAccountStatus())) {
throw new ServiceException("档口账户已冻结");
}
if (StrUtil.isEmpty(externalAccount.getPassword())) {
if (StrUtil.isEmpty(internalAccount.getTransactionPassword())) {
throw new ServiceException("请先设置支付密码");
}
if (!StrUtil.equals(password, externalAccount.getPassword())) {
if (!StrUtil.equals(SecureUtil.md5(transactionPassword), internalAccount.getTransactionPassword())) {
throw new ServiceException("支付密码错误");
}
return financeBillService.createWithdrawPaymentBill(storeId, amount, payChannel);
@ -66,5 +70,76 @@ public class AccountServiceImpl implements IAccountService {
financeBillService.entryWithdrawPaymentBill(financeBillId);
}
@Override
public AccountInfoDTO getStoreAccountInfo(Long storeId) {
Assert.notNull(storeId);
InternalAccount internalAccount = internalAccountService.getAccount(storeId, EAccountOwnerType.STORE);
ExternalAccount alipayExternalAccount = externalAccountService.getAccount(storeId, EAccountOwnerType.STORE,
EAccountType.ALI_PAY);
return new AccountInfoDTO(BeanUtil.toBean(internalAccount, InternalAccountDTO.class),
BeanUtil.toBean(alipayExternalAccount, ExternalAccountDTO.class));
}
@Transactional(rollbackFor = Exception.class)
@Override
public AccountInfoDTO createInternalAccountIfNotExists(Long storeId) {
Assert.notNull(storeId);
InternalAccount internalAccount = internalAccountService.getAccount(storeId, EAccountOwnerType.STORE);
if (internalAccount == null) {
InternalAccountAddDTO addDTO = new InternalAccountAddDTO();
addDTO.setOwnerId(storeId);
addDTO.setOwnerType(EAccountOwnerType.STORE.getValue());
internalAccountService.createAccount(addDTO);
}
return getStoreAccountInfo(storeId);
}
@Transactional(rollbackFor = Exception.class)
@Override
public AccountInfoDTO setTransactionPassword(TransactionPasswordSetDTO transactionPasswordSet) {
Assert.notNull(transactionPasswordSet.getStoreId());
Assert.notEmpty(transactionPasswordSet.getPhoneNumber());
Assert.notEmpty(transactionPasswordSet.getVerifyCode());
Assert.notEmpty(transactionPasswordSet.getTransactionPassword());
//TODO 验证码
InternalAccount internalAccount = internalAccountService.getAccountAndCheck(transactionPasswordSet.getStoreId(),
EAccountOwnerType.STORE);
internalAccountService.setTransactionPassword(internalAccount.getId(),
SecureUtil.md5(transactionPasswordSet.getTransactionPassword()));
return getStoreAccountInfo(transactionPasswordSet.getStoreId());
}
@Transactional(rollbackFor = Exception.class)
@Override
public AccountInfoDTO bindAlipay(AlipayBindDTO alipayBind) {
Assert.notNull(alipayBind.getStoreId());
Assert.notEmpty(alipayBind.getAccountOwnerName());
Assert.notEmpty(alipayBind.getAccountOwnerNumber());
Assert.notEmpty(alipayBind.getAccountOwnerPhoneNumber());
//TODO 验证码
ExternalAccount alipayExternalAccount = externalAccountService.getAccount(alipayBind.getStoreId(),
EAccountOwnerType.STORE, EAccountType.ALI_PAY);
if (alipayExternalAccount != null) {
//修改
alipayExternalAccount.setAccountOwnerName(alipayBind.getAccountOwnerName());
alipayExternalAccount.setAccountOwnerNumber(alipayBind.getAccountOwnerNumber());
alipayExternalAccount.setAccountOwnerPhoneNumber(alipayBind.getAccountOwnerPhoneNumber());
alipayExternalAccount.setAccountAuthAccess(true);
externalAccountService.modifyAccount(alipayExternalAccount);
} else {
//新增
ExternalAccountAddDTO addDTO = new ExternalAccountAddDTO();
addDTO.setOwnerId(alipayBind.getStoreId());
addDTO.setOwnerType(EAccountOwnerType.STORE.getValue());
addDTO.setAccountType(EAccountType.ALI_PAY.getValue());
addDTO.setAccountOwnerName(alipayBind.getAccountOwnerName());
addDTO.setAccountOwnerNumber(alipayBind.getAccountOwnerNumber());
addDTO.setAccountOwnerPhoneNumber(alipayBind.getAccountOwnerPhoneNumber());
addDTO.setAccountAuthAccess(true);
externalAccountService.createAccount(addDTO);
}
return getStoreAccountInfo(alipayBind.getStoreId());
}
}

View File

@ -1,5 +1,6 @@
package com.ruoyi.xkt.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
@ -10,6 +11,7 @@ import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.bean.BeanValidators;
import com.ruoyi.xkt.domain.ExternalAccount;
import com.ruoyi.xkt.domain.ExternalAccountTransDetail;
import com.ruoyi.xkt.dto.account.ExternalAccountAddDTO;
import com.ruoyi.xkt.dto.finance.TransInfo;
import com.ruoyi.xkt.enums.*;
import com.ruoyi.xkt.mapper.ExternalAccountMapper;
@ -46,7 +48,19 @@ public class ExternalAccountServiceImpl implements IExternalAccountService {
}
@Override
public ExternalAccount getExternalAccount(Long ownerId, EAccountOwnerType ownerType, EAccountType accountType) {
public ExternalAccount getAccount(Long ownerId, EAccountOwnerType ownerType, EAccountType accountType) {
Assert.notNull(ownerId);
Assert.notNull(ownerType);
Assert.notNull(accountType);
return externalAccountMapper.selectOne(Wrappers.lambdaQuery(ExternalAccount.class)
.eq(ExternalAccount::getOwnerId, ownerId)
.eq(ExternalAccount::getOwnerType, ownerType.getValue())
.eq(ExternalAccount::getAccountType, accountType.getValue())
.eq(SimpleEntity::getDelFlag, Constants.UNDELETED));
}
@Override
public ExternalAccount getAccountAndCheck(Long ownerId, EAccountOwnerType ownerType, EAccountType accountType) {
Assert.notNull(ownerId);
Assert.notNull(ownerType);
Assert.notNull(accountType);
@ -124,6 +138,31 @@ public class ExternalAccountServiceImpl implements IExternalAccountService {
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public ExternalAccount createAccount(ExternalAccountAddDTO add) {
Assert.notNull(add.getOwnerType());
Assert.notNull(add.getOwnerId());
Assert.notNull(add.getAccountType());
ExternalAccount externalAccount = BeanUtil.toBean(add, ExternalAccount.class);
externalAccount.setAccountStatus(EAccountStatus.NORMAL.getValue());
externalAccount.setDelFlag(Constants.UNDELETED);
externalAccountMapper.insert(externalAccount);
return externalAccount;
}
@Transactional(rollbackFor = Exception.class)
@Override
public int modifyAccount(ExternalAccount externalAccount) {
Assert.notNull(externalAccount);
Assert.notNull(externalAccount.getId());
int r = externalAccountMapper.updateById(externalAccount);
if (r == 0) {
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
}
return r;
}
/**
*
*

View File

@ -117,7 +117,7 @@ public class FinanceBillServiceImpl implements IFinanceBillService {
//业务唯一键
String businessUniqueKey = "STORE_ORDER_COMPLETED_" + orderExt.getOrder().getId();
bill.setBusinessUniqueKey(businessUniqueKey);
Long inputInternalAccountId = internalAccountService.getInternalAccount(orderExt.getOrder().getStoreId(),
Long inputInternalAccountId = internalAccountService.getAccountAndCheck(orderExt.getOrder().getStoreId(),
EAccountOwnerType.STORE).getId();
bill.setInputInternalAccountId(inputInternalAccountId);
bill.setOutputInternalAccountId(Constants.PLATFORM_INTERNAL_ACCOUNT_ID);
@ -240,9 +240,9 @@ public class FinanceBillServiceImpl implements IFinanceBillService {
//业务唯一键
String businessUniqueKey = IdUtil.simpleUUID();
bill.setBusinessUniqueKey(businessUniqueKey);
Long outputInternalAccountId = internalAccountService.getInternalAccount(storeId, EAccountOwnerType.STORE)
Long outputInternalAccountId = internalAccountService.getAccountAndCheck(storeId, EAccountOwnerType.STORE)
.getId();
Long inputExternalAccountId = externalAccountService.getExternalAccount(storeId, EAccountOwnerType.STORE,
Long inputExternalAccountId = externalAccountService.getAccountAndCheck(storeId, EAccountOwnerType.STORE,
EAccountType.getByChannel(payChannel)).getId();
bill.setOutputInternalAccountId(outputInternalAccountId);
bill.setInputExternalAccountId(inputExternalAccountId);

View File

@ -1,6 +1,7 @@
package com.ruoyi.xkt.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@ -10,12 +11,12 @@ import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.bean.BeanValidators;
import com.ruoyi.xkt.domain.InternalAccount;
import com.ruoyi.xkt.domain.InternalAccountTransDetail;
import com.ruoyi.xkt.dto.account.InternalAccountAddDTO;
import com.ruoyi.xkt.dto.finance.TransInfo;
import com.ruoyi.xkt.enums.*;
import com.ruoyi.xkt.mapper.InternalAccountMapper;
import com.ruoyi.xkt.mapper.InternalAccountTransDetailMapper;
import com.ruoyi.xkt.service.IInternalAccountService;
import io.jsonwebtoken.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@ -47,7 +48,17 @@ public class InternalAccountServiceImpl implements IInternalAccountService {
}
@Override
public InternalAccount getInternalAccount(Long ownerId, EAccountOwnerType ownerType) {
public InternalAccount getAccount(Long ownerId, EAccountOwnerType ownerType) {
Assert.notNull(ownerId);
Assert.notNull(ownerType);
return internalAccountMapper.selectOne(Wrappers.lambdaQuery(InternalAccount.class)
.eq(InternalAccount::getOwnerId, ownerId)
.eq(InternalAccount::getOwnerType, ownerType.getValue())
.eq(SimpleEntity::getDelFlag, Constants.UNDELETED));
}
@Override
public InternalAccount getAccountAndCheck(Long ownerId, EAccountOwnerType ownerType) {
Assert.notNull(ownerId);
Assert.notNull(ownerType);
InternalAccount account = internalAccountMapper.selectOne(Wrappers.lambdaQuery(InternalAccount.class)
@ -204,6 +215,35 @@ public class InternalAccountServiceImpl implements IInternalAccountService {
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public InternalAccount createAccount(InternalAccountAddDTO add) {
Assert.notNull(add.getOwnerId());
Assert.notNull(add.getOwnerType());
InternalAccount internalAccount = new InternalAccount();
internalAccount.setOwnerType(add.getOwnerType());
internalAccount.setOwnerId(add.getOwnerId());
internalAccount.setAccountStatus(EAccountStatus.NORMAL.getValue());
internalAccount.setBalance(BigDecimal.ZERO);
internalAccount.setUsableBalance(BigDecimal.ZERO);
internalAccount.setPaymentAmount(BigDecimal.ZERO);
internalAccount.setRemark(add.getRemark());
internalAccount.setDelFlag(Constants.UNDELETED);
internalAccountMapper.insert(internalAccount);
return internalAccount;
}
@Transactional(rollbackFor = Exception.class)
@Override
public void setTransactionPassword(Long id, String transactionPassword) {
Assert.notNull(id);
Assert.notEmpty(transactionPassword);
InternalAccount update = new InternalAccount();
update.setId(id);
update.setTransactionPassword(transactionPassword);
internalAccountMapper.updateById(update);
}
/**
*