package com.ruoyi.account.service.impl;
|
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.ruoyi.account.api.dto.CompanyAllocateRecordQueryDTO;
|
import com.ruoyi.account.api.model.TCompanyAllocateRecord;
|
import com.ruoyi.account.mapper.TCompanyAllocateRecordMapper;
|
import com.ruoyi.account.service.TCompanyAllocateRecordService;
|
import com.ruoyi.common.core.utils.StringUtils;
|
import com.ruoyi.common.security.utils.SecurityUtils;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.stereotype.Service;
|
|
import java.math.BigDecimal;
|
import java.math.RoundingMode;
|
import java.time.LocalDateTime;
|
|
/**
|
* 公司额度分配记录服务实现:对外提供分页查询与统一流水写入。
|
* 写入方法被 VIN码/卡片/白名单的额度变更流程调用,与该次余额变更保持同一事务(或紧随其后的落库顺序)。
|
*/
|
@Service
|
public class TCompanyAllocateRecordServiceImpl
|
extends ServiceImpl<TCompanyAllocateRecordMapper, TCompanyAllocateRecord>
|
implements TCompanyAllocateRecordService {
|
|
private static final Logger log = LoggerFactory.getLogger(TCompanyAllocateRecordServiceImpl.class);
|
|
/** user_info / user_name 列长度(varchar(64)) */
|
private static final int MAX_USER_TEXT_LENGTH = 64;
|
/** remark 列长度(varchar(255)) */
|
private static final int MAX_REMARK_LENGTH = 255;
|
/** charging_order_code 列长度(varchar(64)) */
|
private static final int MAX_ORDER_CODE_LENGTH = 64;
|
|
@Override
|
public IPage<TCompanyAllocateRecord> selectRecordPage(CompanyAllocateRecordQueryDTO query) {
|
CompanyAllocateRecordQueryDTO safe = query == null ? new CompanyAllocateRecordQueryDTO() : query;
|
long pageNum = safe.getPageCurr() == null || safe.getPageCurr() < 1 ? 1 : safe.getPageCurr();
|
long pageSize = safe.getPageSize() == null || safe.getPageSize() < 1 ? 10 : safe.getPageSize();
|
return baseMapper.selectRecordPage(new Page<>(pageNum, pageSize), safe);
|
}
|
|
@Override
|
public boolean recordAllocate(int userType, String userInfo, String userName, Integer companyId, int changeSource,
|
BigDecimal beforeBalance, BigDecimal amount, String remark) {
|
BigDecimal change = normalizeMoney(amount);
|
if (companyId == null || change.signum() == 0) {
|
return false;
|
}
|
BigDecimal before = normalizeMoney(beforeBalance);
|
TCompanyAllocateRecord record = new TCompanyAllocateRecord();
|
record.setCompanyId(companyId);
|
record.setUserType(userType);
|
record.setUserInfo(truncate(userInfo, MAX_USER_TEXT_LENGTH));
|
record.setUserName(truncate(userName, MAX_USER_TEXT_LENGTH));
|
record.setChangeSource(changeSource);
|
record.setBeforeBalance(before);
|
record.setAmount(change);
|
record.setAfterBalance(before.add(change));
|
record.setOperatorId(currentUserId());
|
record.setOperatorName(currentUsername());
|
record.setOperateTime(LocalDateTime.now());
|
record.setRemark(truncate(remark, MAX_REMARK_LENGTH));
|
record.setCreateTime(record.getOperateTime());
|
return save(record);
|
}
|
|
@Override
|
public boolean recordConsume(Integer companyId, Integer userType, String userInfo, String userName,
|
BigDecimal beforeBalance, BigDecimal amount, String chargingOrderCode, String remark) {
|
return doRecordConsume(companyId, userType, userInfo, userName, beforeBalance, amount, chargingOrderCode,
|
remark, false);
|
}
|
|
@Override
|
public boolean recordSettleConsume(Integer companyId, Integer userType, String userInfo, String userName,
|
BigDecimal beforeBalance, BigDecimal amount, String chargingOrderCode,
|
String remark) {
|
return doRecordConsume(companyId, userType, userInfo, userName, beforeBalance, amount, chargingOrderCode,
|
remark, true);
|
}
|
|
@Override
|
public boolean existsConsumeRecord(String chargingOrderCode) {
|
if (StringUtils.isBlank(chargingOrderCode)) {
|
return false;
|
}
|
String orderCode = truncate(chargingOrderCode, MAX_ORDER_CODE_LENGTH);
|
TCompanyAllocateRecord exists = lambdaQuery()
|
.eq(TCompanyAllocateRecord::getChargingOrderCode, orderCode)
|
.eq(TCompanyAllocateRecord::getChangeType, TCompanyAllocateRecord.CHANGE_TYPE_CONSUME)
|
.last("limit 1")
|
.one();
|
return exists != null;
|
}
|
|
@Override
|
public java.util.List<String> listConsumedOrderCodes(java.util.List<String> orderCodes) {
|
if (orderCodes == null || orderCodes.isEmpty()) {
|
return new java.util.ArrayList<>();
|
}
|
java.util.List<String> codes = new java.util.ArrayList<>();
|
for (String orderCode : orderCodes) {
|
if (StringUtils.isNotBlank(orderCode)) {
|
String code = truncate(orderCode, MAX_ORDER_CODE_LENGTH);
|
if (!codes.contains(code)) {
|
codes.add(code);
|
}
|
}
|
}
|
if (codes.isEmpty()) {
|
return new java.util.ArrayList<>();
|
}
|
java.util.List<TCompanyAllocateRecord> records = lambdaQuery()
|
.select(TCompanyAllocateRecord::getChargingOrderCode)
|
.eq(TCompanyAllocateRecord::getChangeType, TCompanyAllocateRecord.CHANGE_TYPE_CONSUME)
|
.in(TCompanyAllocateRecord::getChargingOrderCode, codes)
|
.list();
|
java.util.List<String> consumed = new java.util.ArrayList<>();
|
if (records != null) {
|
for (TCompanyAllocateRecord record : records) {
|
if (record != null && StringUtils.isNotBlank(record.getChargingOrderCode())
|
&& !consumed.contains(record.getChargingOrderCode())) {
|
consumed.add(record.getChargingOrderCode());
|
}
|
}
|
}
|
return consumed;
|
}
|
|
/**
|
* 消费流水写入公共实现
|
*
|
* @param allowZeroAmount 是否允许 0 金额写流水(结算幂等标记场景为 true)
|
*/
|
private boolean doRecordConsume(Integer companyId, Integer userType, String userInfo, String userName,
|
BigDecimal beforeBalance, BigDecimal amount, String chargingOrderCode,
|
String remark, boolean allowZeroAmount) {
|
if (companyId == null || userType == null || StringUtils.isBlank(userInfo)
|
|| StringUtils.isBlank(chargingOrderCode) || amount == null
|
|| (!allowZeroAmount && amount.signum() == 0)) {
|
log.warn("消费流水参数不完整,跳过写入:companyId={}, userType={}, userInfo={}, orderCode={}",
|
companyId, userType, userInfo, chargingOrderCode);
|
return false;
|
}
|
String orderCode = truncate(chargingOrderCode, MAX_ORDER_CODE_LENGTH);
|
TCompanyAllocateRecord exists = lambdaQuery()
|
.eq(TCompanyAllocateRecord::getChargingOrderCode, orderCode)
|
.eq(TCompanyAllocateRecord::getChangeType, TCompanyAllocateRecord.CHANGE_TYPE_CONSUME)
|
.last("limit 1")
|
.one();
|
if (exists != null) {
|
return true;
|
}
|
BigDecimal change = normalizeMoney(amount);
|
BigDecimal before = normalizeMoney(beforeBalance);
|
TCompanyAllocateRecord record = new TCompanyAllocateRecord();
|
record.setCompanyId(companyId);
|
record.setUserType(userType);
|
record.setUserInfo(truncate(userInfo, MAX_USER_TEXT_LENGTH));
|
record.setUserName(truncate(userName, MAX_USER_TEXT_LENGTH));
|
record.setChangeSource(TCompanyAllocateRecord.SOURCE_ALLOCATE);
|
record.setChangeType(TCompanyAllocateRecord.CHANGE_TYPE_CONSUME);
|
record.setChargingOrderCode(orderCode);
|
record.setBeforeBalance(before);
|
record.setAmount(change);
|
record.setAfterBalance(before.add(change));
|
record.setOperatorId(currentUserId());
|
record.setOperatorName(currentUsername());
|
record.setOperateTime(LocalDateTime.now());
|
record.setRemark(truncate(remark, MAX_REMARK_LENGTH));
|
record.setCreateTime(record.getOperateTime());
|
try {
|
return save(record);
|
} catch (org.springframework.dao.DuplicateKeyException ex) {
|
log.warn("消费流水已存在(订单号{}),忽略重复写入", orderCode);
|
return true;
|
}
|
}
|
|
/** 金额统一保留 2 位小数,null 视为 0 */
|
private BigDecimal normalizeMoney(BigDecimal amount) {
|
return amount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)
|
: amount.setScale(2, RoundingMode.HALF_UP);
|
}
|
|
private String truncate(String value, int maxLength) {
|
if (value == null) {
|
return null;
|
}
|
String trimmed = value.trim();
|
return trimmed.length() > maxLength ? trimmed.substring(0, maxLength) : trimmed;
|
}
|
|
/** 操作人ID:无登录上下文(如设备链路)时记为空,不阻断业务 */
|
private Long currentUserId() {
|
try {
|
Long userId = SecurityUtils.getUserId();
|
return userId == null || userId == 0L ? null : userId;
|
} catch (RuntimeException ex) {
|
log.warn("分配记录获取登录用户ID失败,操作人ID记为空:{}", ex.getMessage());
|
return null;
|
}
|
}
|
|
/** 操作人:取登录账号 */
|
private String currentUsername() {
|
try {
|
String username = SecurityUtils.getUsername();
|
return StringUtils.isBlank(username) ? null : truncate(username, MAX_USER_TEXT_LENGTH);
|
} catch (RuntimeException ex) {
|
log.warn("分配记录获取登录账号失败,操作人记为空:{}", ex.getMessage());
|
return null;
|
}
|
}
|
}
|