应收账单模块,调整枚举代码优化
This commit is contained in:
@@ -22,11 +22,6 @@ public class ArBalanceWriteoff extends BaseEntity
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 有效 */
|
||||
public static final String STATUS_EFFECTIVE = "EFFECTIVE";
|
||||
/** 已取消(反核销) */
|
||||
public static final String STATUS_CANCELLED = "CANCELLED";
|
||||
|
||||
/** 余额核销ID */
|
||||
private Long writeoffId;
|
||||
|
||||
@@ -61,7 +56,7 @@ public class ArBalanceWriteoff extends BaseEntity
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date writeoffDate;
|
||||
|
||||
/** 状态:EFFECTIVE/CANCELLED */
|
||||
/** 状态:EFFECTIVE(有效)/CANCELLED(已反核销),取值由 ArBalanceWriteoffStatus 维护。 */
|
||||
private String status;
|
||||
|
||||
/** 核销备注 */
|
||||
|
||||
@@ -36,6 +36,7 @@ public class ArBill extends BaseEntity
|
||||
private BigDecimal totalAmount;
|
||||
private BigDecimal writtenOffAmount;
|
||||
private BigDecimal unwrittenAmount;
|
||||
/** 状态:DRAFT草稿、CONFIRMED已确认、PARTIAL_WRITTEN_OFF部分核销、WRITTEN_OFF已核销、VOID已作废。 */
|
||||
private String status;
|
||||
private Integer billVersion;
|
||||
private String bankAccountName;
|
||||
|
||||
@@ -21,6 +21,7 @@ public class ArBillExport implements Serializable
|
||||
private Long fileSize;
|
||||
private String fileHash;
|
||||
private Integer rowCount;
|
||||
/** 导出状态:PROCESSING生成中、SUCCESS成功可下载、FAILED失败。 */
|
||||
private String exportStatus;
|
||||
private String exportBy;
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
|
||||
@@ -13,10 +13,12 @@ public class ArBillLog implements Serializable
|
||||
|
||||
private Long logId;
|
||||
private Long arBillId;
|
||||
/** 对象类型:BILL应收账单、EXPORT账单导出、BALANCE_WRITEOFF余额核销。 */
|
||||
private String objectType;
|
||||
private String objectId;
|
||||
/** 对象业务标识,例如账单号、收款单号或核销单号。 */
|
||||
private String objectDisplay;
|
||||
/** 操作类型,取值由 ArLogOperationType 维护。 */
|
||||
private String operationType;
|
||||
private String operationDesc;
|
||||
private String fromStatus;
|
||||
@@ -25,9 +27,11 @@ public class ArBillLog implements Serializable
|
||||
private String afterData;
|
||||
private String operatorId;
|
||||
private String operatorName;
|
||||
/** 操作来源:WEB网页端、API接口、JOB定时任务、IMPORT数据导入。 */
|
||||
private String operationSource;
|
||||
private String requestId;
|
||||
private String traceId;
|
||||
/** 操作结果:SUCCESS成功、FAILED失败。 */
|
||||
private String result;
|
||||
private String errorMessage;
|
||||
private String clientIp;
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/**
|
||||
* 应收账单余额核销单状态。
|
||||
*
|
||||
* <p>数据库保存英文编码,页面通过前端枚举展示中文名称。</p>
|
||||
*/
|
||||
public enum ArBalanceWriteoffStatus
|
||||
{
|
||||
/** 核销已生效,余额已扣减。 */
|
||||
EFFECTIVE("EFFECTIVE", "有效"),
|
||||
/** 核销已取消,余额已回补。 */
|
||||
CANCELLED("CANCELLED", "已反核销");
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
ArBalanceWriteoffStatus(String code, String label)
|
||||
{
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
public boolean isEffective()
|
||||
{
|
||||
return this == EFFECTIVE;
|
||||
}
|
||||
|
||||
public static ArBalanceWriteoffStatus of(String code)
|
||||
{
|
||||
for (ArBalanceWriteoffStatus status : values())
|
||||
{
|
||||
if (status.code.equalsIgnoreCase(code))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,52 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 应收账单导出任务状态。 */
|
||||
/**
|
||||
* 应收账单导出任务状态。
|
||||
*
|
||||
* <p>PROCESSING 表示处理中,SUCCESS 表示生成成功,FAILED 表示生成失败。</p>
|
||||
*/
|
||||
public enum ArExportStatus
|
||||
{
|
||||
PROCESSING("PROCESSING"),
|
||||
SUCCESS("SUCCESS"),
|
||||
FAILED("FAILED");
|
||||
/** 文件正在生成。 */
|
||||
PROCESSING("PROCESSING", "生成中"),
|
||||
/** 文件已生成,可以下载。 */
|
||||
SUCCESS("SUCCESS", "成功"),
|
||||
/** 文件生成失败。 */
|
||||
FAILED("FAILED", "失败");
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
ArExportStatus(String code)
|
||||
ArExportStatus(String code, String label)
|
||||
{
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
public boolean isSuccess()
|
||||
{
|
||||
return this == SUCCESS;
|
||||
}
|
||||
|
||||
public static ArExportStatus of(String code)
|
||||
{
|
||||
for (ArExportStatus status : values())
|
||||
{
|
||||
if (status.code.equalsIgnoreCase(code))
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 应收日志关联对象类型。 */
|
||||
public enum ArLogObjectType
|
||||
{
|
||||
/** 应收账单主对象。 */
|
||||
BILL("BILL", "应收账单"),
|
||||
/** 账单导出文件对象。 */
|
||||
EXPORT("EXPORT", "账单导出"),
|
||||
/** 客户余额核销单对象。 */
|
||||
BALANCE_WRITEOFF("BALANCE_WRITEOFF", "余额核销");
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
ArLogObjectType(String code, String label)
|
||||
{
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 应收日志操作来源。 */
|
||||
public enum ArLogOperationSource
|
||||
{
|
||||
/** 浏览器页面操作。 */
|
||||
WEB("WEB", "网页端"),
|
||||
/** 开放接口或系统间接口调用。 */
|
||||
API("API", "接口"),
|
||||
/** 定时任务或后台作业。 */
|
||||
JOB("JOB", "定时任务"),
|
||||
/** 数据导入操作。 */
|
||||
IMPORT("IMPORT", "数据导入");
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
ArLogOperationSource(String code, String label)
|
||||
{
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.ruoyi.ar.enums;
|
||||
|
||||
/** 应收日志操作类型。 */
|
||||
public enum ArLogOperationType
|
||||
{
|
||||
/** 生成应收账单成功。 */
|
||||
CREATE("CREATE", "生成账单"),
|
||||
/** 生成账单前的校验或生成失败记录。 */
|
||||
GENERATE("GENERATE", "生成账单校验"),
|
||||
/** 确认草稿账单。 */
|
||||
CONFIRM("CONFIRM", "确认账单"),
|
||||
/** 作废账单并释放来源订单。 */
|
||||
VOID("VOID", "作废账单"),
|
||||
/** 生成账单导出文件。 */
|
||||
EXPORT("EXPORT", "导出账单"),
|
||||
/** 使用客户余额核销账单。 */
|
||||
BALANCE_WRITEOFF("BALANCE_WRITEOFF", "余额核销"),
|
||||
/** 取消余额核销并回补客户余额。 */
|
||||
CANCEL_BALANCE_WRITEOFF("CANCEL_BALANCE_WRITEOFF", "取消余额核销");
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
ArLogOperationType(String code, String label)
|
||||
{
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
}
|
||||
@@ -3,18 +3,39 @@ package com.ruoyi.ar.enums;
|
||||
/** 应收操作日志结果。 */
|
||||
public enum ArLogResult
|
||||
{
|
||||
SUCCESS("SUCCESS"),
|
||||
FAILED("FAILED");
|
||||
/** 操作执行成功。 */
|
||||
SUCCESS("SUCCESS", "成功"),
|
||||
/** 操作执行失败。 */
|
||||
FAILED("FAILED", "失败");
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
ArLogResult(String code)
|
||||
ArLogResult(String code, String label)
|
||||
{
|
||||
this.code = code;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
public static ArLogResult of(String code)
|
||||
{
|
||||
for (ArLogResult result : values())
|
||||
{
|
||||
if (result.code.equalsIgnoreCase(code))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -27,7 +27,9 @@ public interface ArBalanceWriteoffMapper
|
||||
int updateCancelled(@Param("writeoffId") Long writeoffId,
|
||||
@Param("reverseFlowId") Long reverseFlowId,
|
||||
@Param("cancelBy") String cancelBy,
|
||||
@Param("cancelRemark") String cancelRemark);
|
||||
@Param("cancelRemark") String cancelRemark,
|
||||
@Param("status") String status,
|
||||
@Param("effectiveStatus") String effectiveStatus);
|
||||
|
||||
/** 查询核销记录列表(关联账单名称/客户名称) */
|
||||
List<ArBalanceWriteoff> selectWriteoffList(ArBalanceWriteoff query);
|
||||
|
||||
@@ -9,7 +9,8 @@ public interface ArBillLogMapper
|
||||
List<ArBillLog> selectList(ArBillLog query);
|
||||
|
||||
ArBillLog selectSuccessByRequestId(@Param("requestId") String requestId,
|
||||
@Param("objectType") String objectType);
|
||||
@Param("objectType") String objectType,
|
||||
@Param("result") String result);
|
||||
|
||||
int insert(ArBillLog log);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package com.ruoyi.ar.service;
|
||||
|
||||
import java.util.List;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
import com.ruoyi.ar.enums.ArLogObjectType;
|
||||
import com.ruoyi.ar.enums.ArLogOperationType;
|
||||
|
||||
public interface IArBillLogService
|
||||
{
|
||||
@@ -9,6 +11,6 @@ public interface IArBillLogService
|
||||
|
||||
void record(ArBillLog log);
|
||||
|
||||
void recordFailure(Long arBillId, String objectType, String objectId, String operationType,
|
||||
void recordFailure(Long arBillId, ArLogObjectType objectType, String objectId, ArLogOperationType operationType,
|
||||
String fromStatus, String message, String operator, String requestId);
|
||||
}
|
||||
|
||||
+14
-9
@@ -14,7 +14,11 @@ import org.springframework.util.StringUtils;
|
||||
import com.ruoyi.ar.domain.ArBalanceWriteoff;
|
||||
import com.ruoyi.ar.domain.ArBill;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
import com.ruoyi.ar.enums.ArBalanceWriteoffStatus;
|
||||
import com.ruoyi.ar.enums.ArBillStatus;
|
||||
import com.ruoyi.ar.enums.ArLogObjectType;
|
||||
import com.ruoyi.ar.enums.ArLogOperationSource;
|
||||
import com.ruoyi.ar.enums.ArLogOperationType;
|
||||
import com.ruoyi.ar.enums.ArLogResult;
|
||||
import com.ruoyi.ar.mapper.ArBalanceWriteoffMapper;
|
||||
import com.ruoyi.ar.mapper.ArBillMapper;
|
||||
@@ -119,7 +123,7 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService
|
||||
writeoff.setCurrency(currency);
|
||||
writeoff.setWriteoffAmount(amount);
|
||||
writeoff.setWriteoffDate(writeoffDate);
|
||||
writeoff.setStatus(ArBalanceWriteoff.STATUS_EFFECTIVE);
|
||||
writeoff.setStatus(ArBalanceWriteoffStatus.EFFECTIVE.getCode());
|
||||
writeoff.setRemark(StringUtils.hasText(cmd.getRemark()) ? cmd.getRemark().trim() : null);
|
||||
writeoff.setCreateBy(operator);
|
||||
writeoffMapper.insertArBalanceWriteoff(writeoff);
|
||||
@@ -148,7 +152,7 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService
|
||||
throw new ServiceException("账单状态已发生变化,请刷新后重试");
|
||||
}
|
||||
|
||||
recordLog(bill.getArBillId(), writeoff.getWriteoffId(), "BALANCE_WRITEOFF",
|
||||
recordLog(bill.getArBillId(), writeoff.getWriteoffId(), ArLogOperationType.BALANCE_WRITEOFF,
|
||||
"账户余额核销账单 " + amount.toPlainString() + " " + currency,
|
||||
fromStatus, nextStatus, operator, requestId);
|
||||
writeoff.setAccountId(flow.getAccountId());
|
||||
@@ -170,7 +174,7 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService
|
||||
{
|
||||
throw new ServiceException("核销记录不存在");
|
||||
}
|
||||
if (!ArBalanceWriteoff.STATUS_EFFECTIVE.equals(writeoff.getStatus()))
|
||||
if (!ArBalanceWriteoffStatus.EFFECTIVE.getCode().equals(writeoff.getStatus()))
|
||||
{
|
||||
throw new ServiceException("只有有效状态的核销记录可以反核销");
|
||||
}
|
||||
@@ -213,11 +217,12 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService
|
||||
}
|
||||
|
||||
// 4. 核销单置为CANCELLED并登记回补流水
|
||||
if (writeoffMapper.updateCancelled(writeoffId, reverseFlow.getId(), operator, cancelRemark) != 1)
|
||||
if (writeoffMapper.updateCancelled(writeoffId, reverseFlow.getId(), operator, cancelRemark,
|
||||
ArBalanceWriteoffStatus.CANCELLED.getCode(), ArBalanceWriteoffStatus.EFFECTIVE.getCode()) != 1)
|
||||
{
|
||||
throw new ServiceException("核销记录状态已变化,请刷新后重试");
|
||||
}
|
||||
recordLog(bill.getArBillId(), writeoffId, "CANCEL_BALANCE_WRITEOFF",
|
||||
recordLog(bill.getArBillId(), writeoffId, ArLogOperationType.CANCEL_BALANCE_WRITEOFF,
|
||||
"反核销回补账单:" + cancelRemark,
|
||||
fromStatus, nextStatus, operator, requestId);
|
||||
}
|
||||
@@ -234,20 +239,20 @@ public class ArBalanceWriteoffServiceImpl implements IArBalanceWriteoffService
|
||||
? ArBillStatus.WRITTEN_OFF.getCode() : ArBillStatus.PARTIAL_WRITTEN_OFF.getCode();
|
||||
}
|
||||
|
||||
private void recordLog(Long billId, Long writeoffId, String operationType, String desc,
|
||||
private void recordLog(Long billId, Long writeoffId, ArLogOperationType operationType, String desc,
|
||||
String fromStatus, String toStatus, String operator, String requestId)
|
||||
{
|
||||
ArBillLog log = new ArBillLog();
|
||||
log.setArBillId(billId);
|
||||
log.setObjectType("BALANCE_WRITEOFF");
|
||||
log.setObjectType(ArLogObjectType.BALANCE_WRITEOFF.getCode());
|
||||
log.setObjectId(String.valueOf(writeoffId));
|
||||
log.setOperationType(operationType);
|
||||
log.setOperationType(operationType.getCode());
|
||||
log.setOperationDesc(desc);
|
||||
log.setFromStatus(fromStatus);
|
||||
log.setToStatus(toStatus);
|
||||
log.setOperatorId(operator);
|
||||
log.setOperatorName(operator);
|
||||
log.setOperationSource("WEB");
|
||||
log.setOperationSource(ArLogOperationSource.WEB.getCode());
|
||||
log.setRequestId(requestId);
|
||||
log.setResult(ArLogResult.SUCCESS.getCode());
|
||||
logService.record(log);
|
||||
|
||||
+7
-4
@@ -6,6 +6,9 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
import com.ruoyi.ar.enums.ArLogObjectType;
|
||||
import com.ruoyi.ar.enums.ArLogOperationSource;
|
||||
import com.ruoyi.ar.enums.ArLogOperationType;
|
||||
import com.ruoyi.ar.enums.ArLogResult;
|
||||
import com.ruoyi.ar.mapper.ArBillLogMapper;
|
||||
import com.ruoyi.ar.service.IArBillLogService;
|
||||
@@ -32,19 +35,19 @@ public class ArBillLogServiceImpl implements IArBillLogService
|
||||
/** 失败日志必须独立提交,不能随业务异常一起回滚。 */
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public void recordFailure(Long arBillId, String objectType, String objectId, String operationType,
|
||||
public void recordFailure(Long arBillId, ArLogObjectType objectType, String objectId, ArLogOperationType operationType,
|
||||
String fromStatus, String message, String operator, String requestId)
|
||||
{
|
||||
ArBillLog log = new ArBillLog();
|
||||
log.setArBillId(arBillId);
|
||||
log.setObjectType(objectType);
|
||||
log.setObjectType(objectType.getCode());
|
||||
log.setObjectId(objectId);
|
||||
log.setOperationType(operationType);
|
||||
log.setOperationType(operationType.getCode());
|
||||
log.setOperationDesc(message);
|
||||
log.setFromStatus(fromStatus);
|
||||
log.setOperatorId(operator);
|
||||
log.setOperatorName(operator);
|
||||
log.setOperationSource("WEB");
|
||||
log.setOperationSource(ArLogOperationSource.WEB.getCode());
|
||||
log.setRequestId(requestId == null ? IdUtils.fastSimpleUUID() : requestId);
|
||||
log.setResult(ArLogResult.FAILED.getCode());
|
||||
log.setErrorMessage(message);
|
||||
|
||||
+58
-45
@@ -37,6 +37,9 @@ import com.ruoyi.ar.domain.ArBillExport;
|
||||
import com.ruoyi.ar.domain.ArBillLog;
|
||||
import com.ruoyi.ar.enums.ArBillStatus;
|
||||
import com.ruoyi.ar.enums.ArExportStatus;
|
||||
import com.ruoyi.ar.enums.ArLogObjectType;
|
||||
import com.ruoyi.ar.enums.ArLogOperationSource;
|
||||
import com.ruoyi.ar.enums.ArLogOperationType;
|
||||
import com.ruoyi.ar.enums.ArLogResult;
|
||||
import com.ruoyi.ar.enums.ArOrderBillStatus;
|
||||
import com.ruoyi.ar.mapper.ArBillDetailFeeMapper;
|
||||
@@ -135,7 +138,8 @@ public class ArBillServiceImpl implements IArBillService
|
||||
public ArBill generateBill(ArBillGenerateRequest request, String operator, String requestId)
|
||||
{
|
||||
validatePeriod(request);
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, "BILL");
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, ArLogObjectType.BILL.getCode(),
|
||||
ArLogResult.SUCCESS.getCode());
|
||||
if (previous != null && isNumber(previous.getObjectId()))
|
||||
{
|
||||
return selectArBillById(Long.valueOf(previous.getObjectId()));
|
||||
@@ -144,7 +148,8 @@ public class ArBillServiceImpl implements IArBillService
|
||||
List<String> requestedSourceBillIds = normalizeSourceBillIds(request.getSourceBillIds());
|
||||
if (requestedSourceBillIds.isEmpty())
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null, "请至少选择一条有效订单", operator, requestId);
|
||||
throw failure(null, ArLogObjectType.BILL, "", ArLogOperationType.GENERATE,
|
||||
null, "请至少选择一条有效订单", operator, requestId);
|
||||
}
|
||||
String requestedCurrency = StringUtils.isEmpty(request.getCurrency()) ? "CNY" : request.getCurrency();
|
||||
List<PrcOrderBill> sources = sourceBillMapper.selectPrcOrderBillByCustomerCutoffAndIds(
|
||||
@@ -152,7 +157,7 @@ public class ArBillServiceImpl implements IArBillService
|
||||
requestedSourceBillIds, ArOrderBillStatus.UNBILLED.getCode());
|
||||
if (sources == null || sources.isEmpty() || sources.size() != requestedSourceBillIds.size())
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null,
|
||||
throw failure(null, ArLogObjectType.BILL, "", ArLogOperationType.GENERATE, null,
|
||||
"部分所选订单已出账、已变更或不符合当前筛选条件,请重新查询后生成", operator, requestId);
|
||||
}
|
||||
|
||||
@@ -162,7 +167,8 @@ public class ArBillServiceImpl implements IArBillService
|
||||
{
|
||||
if (source.getCurrency() != null && !currency.equalsIgnoreCase(source.getCurrency()))
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null, "截止日期内存在多个币种,不能合并为一张账单", operator, requestId);
|
||||
throw failure(null, ArLogObjectType.BILL, "", ArLogOperationType.GENERATE,
|
||||
null, "截止日期内存在多个币种,不能合并为一张账单", operator, requestId);
|
||||
}
|
||||
total = total.add(nvl(source.getIncomeTotal()));
|
||||
}
|
||||
@@ -170,7 +176,7 @@ public class ArBillServiceImpl implements IArBillService
|
||||
ArBankAccount bankAccount = bankAccountMapper.selectDefaultByCurrency(currency);
|
||||
if (bankAccount == null)
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null,
|
||||
throw failure(null, ArLogObjectType.BILL, "", ArLogOperationType.GENERATE, null,
|
||||
"未配置 " + currency + " 币种的公司默认收款账户,请先完成收款账户配置", operator, requestId);
|
||||
}
|
||||
|
||||
@@ -206,7 +212,7 @@ public class ArBillServiceImpl implements IArBillService
|
||||
ArOrderBillStatus.UNBILLED.getCode(), ArOrderBillStatus.BILLED.getCode());
|
||||
if (claimedCount != sourceBillIds.size())
|
||||
{
|
||||
throw failure(null, "BILL", "", "GENERATE", null,
|
||||
throw failure(null, ArLogObjectType.BILL, "", ArLogOperationType.GENERATE, null,
|
||||
"部分计费订单已被其他账单占用,请重新查询后生成", operator, requestId);
|
||||
}
|
||||
|
||||
@@ -249,7 +255,8 @@ public class ArBillServiceImpl implements IArBillService
|
||||
{
|
||||
detailFeeMapper.insertBatch(detailFees);
|
||||
}
|
||||
recordSuccess(bill, "CREATE", "生成正式账单", null, ArBillStatus.DRAFT.getCode(), requestId, operator);
|
||||
recordSuccess(bill, ArLogObjectType.BILL, ArLogOperationType.CREATE,
|
||||
"生成正式账单", null, ArBillStatus.DRAFT.getCode(), requestId, operator);
|
||||
bill.setBankAccountNo(maskAccount(bill.getBankAccountNo()));
|
||||
return bill;
|
||||
}
|
||||
@@ -261,26 +268,35 @@ public class ArBillServiceImpl implements IArBillService
|
||||
ArBill bill = billMapper.selectArBillByIdForUpdate(arBillId);
|
||||
if (bill == null)
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "CONFIRM", null, "应收账单不存在", operator, requestId);
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.CONFIRM,
|
||||
null, "应收账单不存在", operator, requestId);
|
||||
}
|
||||
ArBillStatus currentStatus = ArBillStatus.of(bill.getStatus());
|
||||
if (currentStatus == null || !currentStatus.canConfirm())
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "CONFIRM", bill.getStatus(),
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.CONFIRM,
|
||||
bill.getStatus(),
|
||||
"只有草稿账单可以确认,当前状态为 " + bill.getStatus(), operator, requestId);
|
||||
}
|
||||
if (!validateDetails(arBillId, bill.getTotalAmount()))
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "CONFIRM", bill.getStatus(),
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.CONFIRM,
|
||||
bill.getStatus(),
|
||||
"账单费用明细汇总与订单或账单总额不一致", operator, requestId);
|
||||
}
|
||||
if (billMapper.updateArBillStatus(arBillId, ArBillStatus.CONFIRMED.getCode(),
|
||||
StringUtils.isEmpty(request == null ? null : request.getRemark()) ? bill.getRemark() : request.getRemark(),
|
||||
operator, ArBillStatus.DRAFT.getCode()) != 1)
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "CONFIRM", bill.getStatus(),
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.CONFIRM,
|
||||
bill.getStatus(),
|
||||
"账单状态已发生变化,请刷新后重试", operator, requestId);
|
||||
}
|
||||
// 账单确认:应收形成,按账单金额占用客户授信额度(账户不存在时仅CNY会自动初始化)
|
||||
customerMoneyService.occupyCredit(bill.getCustomerId(), bill.getCurrency(),
|
||||
nvl(bill.getTotalAmount()));
|
||||
recordSuccess(bill, ArLogObjectType.BILL, ArLogOperationType.CONFIRM,
|
||||
"确认应收账单", ArBillStatus.DRAFT.getCode(), ArBillStatus.CONFIRMED.getCode(), requestId, operator);
|
||||
// 账单确认:应收形成,按账单金额占用客户授信额度(账户不存在时仅CNY会自动初始化)。
|
||||
// 不校验可用额度,兼容历史 used_credit 可高于总额度、外币账户静默跳过的口径
|
||||
customerAccountApi.occupyCredit(buildCreditCommand(bill));
|
||||
@@ -295,17 +311,20 @@ public class ArBillServiceImpl implements IArBillService
|
||||
String remark = request == null ? null : request.getRemark();
|
||||
if (StringUtils.isEmpty(remark))
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", null, "作废必须填写原因", operator, requestId);
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.VOID,
|
||||
null, "作废必须填写原因", operator, requestId);
|
||||
}
|
||||
ArBill bill = billMapper.selectArBillByIdForUpdate(arBillId);
|
||||
if (bill == null)
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", null, "应收账单不存在", operator, requestId);
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.VOID,
|
||||
null, "应收账单不存在", operator, requestId);
|
||||
}
|
||||
ArBillStatus currentStatus = ArBillStatus.of(bill.getStatus());
|
||||
if (currentStatus == null || !currentStatus.canVoid())
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", bill.getStatus(),
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.VOID,
|
||||
bill.getStatus(),
|
||||
"当前状态不允许作废", operator, requestId);
|
||||
}
|
||||
List<String> sourceBillIds = new ArrayList<>();
|
||||
@@ -318,13 +337,15 @@ public class ArBillServiceImpl implements IArBillService
|
||||
}
|
||||
if (billMapper.updateArBillStatus(arBillId, ArBillStatus.VOID.getCode(), remark, operator, currentStatus.getCode()) != 1)
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", bill.getStatus(),
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.VOID,
|
||||
bill.getStatus(),
|
||||
"账单状态已发生变化,请刷新后重试", operator, requestId);
|
||||
}
|
||||
// 账单作废:已确认账单占用的授信额度全额释放(可作废的CONFIRMED账单必然零核销)
|
||||
if (currentStatus == ArBillStatus.CONFIRMED)
|
||||
{
|
||||
customerAccountApi.releaseCredit(buildCreditCommand(bill));
|
||||
customerMoneyService.releaseCredit(bill.getCustomerId(), bill.getCurrency(),
|
||||
nvl(bill.getTotalAmount()));
|
||||
}
|
||||
if (!sourceBillIds.isEmpty())
|
||||
{
|
||||
@@ -332,18 +353,21 @@ public class ArBillServiceImpl implements IArBillService
|
||||
ArOrderBillStatus.BILLED.getCode(), ArOrderBillStatus.UNBILLED.getCode());
|
||||
if (releasedCount != sourceBillIds.size())
|
||||
{
|
||||
throw failure(arBillId, "BILL", String.valueOf(arBillId), "VOID", bill.getStatus(),
|
||||
throw failure(arBillId, ArLogObjectType.BILL, String.valueOf(arBillId), ArLogOperationType.VOID,
|
||||
bill.getStatus(),
|
||||
"部分来源订单状态异常,账单未作废,请刷新后重试", operator, requestId);
|
||||
}
|
||||
}
|
||||
recordSuccess(bill, "VOID", "作废应收账单:" + remark, bill.getStatus(), ArBillStatus.VOID.getCode(), requestId, operator);
|
||||
recordSuccess(bill, ArLogObjectType.BILL, ArLogOperationType.VOID,
|
||||
"作废应收账单:" + remark, bill.getStatus(), ArBillStatus.VOID.getCode(), requestId, operator);
|
||||
return selectArBillById(arBillId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArBillExport exportBill(Long arBillId, String operator, String requestId)
|
||||
{
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, "EXPORT");
|
||||
ArBillLog previous = logMapper.selectSuccessByRequestId(requestId, ArLogObjectType.EXPORT.getCode(),
|
||||
ArLogResult.SUCCESS.getCode());
|
||||
if (previous != null && isNumber(previous.getObjectId()))
|
||||
{
|
||||
ArBillExport previousExport = exportMapper.selectById(Long.valueOf(previous.getObjectId()));
|
||||
@@ -355,11 +379,13 @@ public class ArBillServiceImpl implements IArBillService
|
||||
ArBill bill = billMapper.selectArBillById(arBillId);
|
||||
if (bill == null)
|
||||
{
|
||||
throw failure(arBillId, "EXPORT", String.valueOf(arBillId), "EXPORT", null, "应收账单不存在", operator, requestId);
|
||||
throw failure(arBillId, ArLogObjectType.EXPORT, String.valueOf(arBillId), ArLogOperationType.EXPORT,
|
||||
null, "应收账单不存在", operator, requestId);
|
||||
}
|
||||
if (ArBillStatus.is(bill.getStatus(), ArBillStatus.DRAFT) || ArBillStatus.is(bill.getStatus(), ArBillStatus.VOID))
|
||||
{
|
||||
throw failure(arBillId, "EXPORT", String.valueOf(arBillId), "EXPORT", bill.getStatus(),
|
||||
throw failure(arBillId, ArLogObjectType.EXPORT, String.valueOf(arBillId), ArLogOperationType.EXPORT,
|
||||
bill.getStatus(),
|
||||
"草稿或作废账单不可导出", operator, requestId);
|
||||
}
|
||||
fillDetails(bill);
|
||||
@@ -385,7 +411,8 @@ public class ArBillServiceImpl implements IArBillService
|
||||
export.setFileHash(sha256(data));
|
||||
export.setExportStatus(ArExportStatus.SUCCESS.getCode());
|
||||
exportMapper.insert(export);
|
||||
recordSuccess(bill, "EXPORT", "导出账单 XLSX", bill.getStatus(), bill.getStatus(), requestId, operator, export.getExportId());
|
||||
recordSuccess(bill, ArLogObjectType.EXPORT, ArLogOperationType.EXPORT,
|
||||
"导出账单 XLSX", bill.getStatus(), bill.getStatus(), requestId, operator, export.getExportId());
|
||||
return export;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -394,7 +421,8 @@ public class ArBillServiceImpl implements IArBillService
|
||||
export.setExportStatus(ArExportStatus.FAILED.getCode());
|
||||
export.setRemark(e.getMessage());
|
||||
exportMapper.insert(export);
|
||||
logService.recordFailure(arBillId, "EXPORT", String.valueOf(arBillId), "EXPORT", bill.getStatus(),
|
||||
logService.recordFailure(arBillId, ArLogObjectType.EXPORT, String.valueOf(arBillId), ArLogOperationType.EXPORT,
|
||||
bill.getStatus(),
|
||||
e.getMessage(), operator, requestId);
|
||||
throw new ServiceException("账单导出失败:" + e.getMessage());
|
||||
}
|
||||
@@ -513,20 +541,20 @@ public class ArBillServiceImpl implements IArBillService
|
||||
return fee;
|
||||
}
|
||||
|
||||
private void recordSuccess(ArBill bill, String operationType, String desc, String fromStatus,
|
||||
private void recordSuccess(ArBill bill, ArLogObjectType objectType, ArLogOperationType operationType, String desc, String fromStatus,
|
||||
String toStatus, String requestId, String operator)
|
||||
{
|
||||
recordSuccess(bill, operationType, desc, fromStatus, toStatus, requestId, operator, null);
|
||||
recordSuccess(bill, objectType, operationType, desc, fromStatus, toStatus, requestId, operator, null);
|
||||
}
|
||||
|
||||
private void recordSuccess(ArBill bill, String operationType, String desc, String fromStatus,
|
||||
private void recordSuccess(ArBill bill, ArLogObjectType objectType, ArLogOperationType operationType, String desc, String fromStatus,
|
||||
String toStatus, String requestId, String operator, Long objectId)
|
||||
{
|
||||
ArBillLog log = new ArBillLog();
|
||||
log.setArBillId(bill.getArBillId());
|
||||
log.setObjectType("BILL".equals(operationType) ? "BILL" : operationType.equals("EXPORT") ? "EXPORT" : "BILL");
|
||||
log.setObjectType(objectType.getCode());
|
||||
log.setObjectId(objectId == null ? String.valueOf(bill.getArBillId()) : String.valueOf(objectId));
|
||||
log.setOperationType(operationType);
|
||||
log.setOperationType(operationType.getCode());
|
||||
log.setOperationDesc(desc);
|
||||
log.setFromStatus(fromStatus);
|
||||
log.setToStatus(toStatus);
|
||||
@@ -534,13 +562,13 @@ public class ArBillServiceImpl implements IArBillService
|
||||
log.setAfterData(safeStatusJson(toStatus));
|
||||
log.setOperatorId(operator);
|
||||
log.setOperatorName(operator);
|
||||
log.setOperationSource("WEB");
|
||||
log.setOperationSource(ArLogOperationSource.WEB.getCode());
|
||||
log.setRequestId(requestId);
|
||||
log.setResult(ArLogResult.SUCCESS.getCode());
|
||||
logService.record(log);
|
||||
}
|
||||
|
||||
private ServiceException failure(Long arBillId, String objectType, String objectId, String operationType,
|
||||
private ServiceException failure(Long arBillId, ArLogObjectType objectType, String objectId, ArLogOperationType operationType,
|
||||
String fromStatus, String message, String operator, String requestId)
|
||||
{
|
||||
logService.recordFailure(arBillId, objectType, objectId, operationType, fromStatus, message, operator, requestId);
|
||||
@@ -861,21 +889,6 @@ public class ArBillServiceImpl implements IArBillService
|
||||
|
||||
private BigDecimal decimal(BigDecimal value, int scale) { return value == null ? null : value.setScale(scale, java.math.RoundingMode.HALF_UP); }
|
||||
private BigDecimal nvl(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; }
|
||||
|
||||
/** 构造账单授信占用/释放命令(确认占用、作废释放共用,关联账单单据便于流水追溯) */
|
||||
private CreditUsageCommand buildCreditCommand(ArBill bill)
|
||||
{
|
||||
CreditUsageCommand command = new CreditUsageCommand();
|
||||
command.setCustomerId(bill.getCustomerId());
|
||||
command.setCurrency(bill.getCurrency());
|
||||
command.setAmount(nvl(bill.getTotalAmount()));
|
||||
command.setBizType(CustomerFundBizType.AR_BILL);
|
||||
command.setBizId(bill.getArBillId());
|
||||
command.setBizNo(bill.getBillNo());
|
||||
// 兼容历史口径:不校验可用额度(允许 used_credit 高于总额度、外币账户静默跳过)
|
||||
command.setCheckAvailable(Boolean.FALSE);
|
||||
return command;
|
||||
}
|
||||
private String safeFilePart(String value) { return StringUtils.isEmpty(value) ? "客户" : value.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5_-]", "_"); }
|
||||
private String maskAccount(String value) { if (StringUtils.isEmpty(value)) return ""; return value.length() <= 4 ? "****" : "****" + value.substring(value.length() - 4); }
|
||||
private String sha256(byte[] data) throws NoSuchAlgorithmException { byte[] digest = MessageDigest.getInstance("SHA-256").digest(data); StringBuilder sb = new StringBuilder(); for (byte b : digest) sb.append(String.format("%02x", b)); return sb.toString(); }
|
||||
|
||||
Reference in New Issue
Block a user