feat: 收款认领本地化,认领完成才推金蝶——A301-3定时+手动双入口按流水归集推送
- 认领只落本地:claim() 删除事务内同步推送,认领上限/认领完成口径改为公告未认领金额 - 推送闸门:认领完成且未推送过才可推;推送前重查A301-2最新公告对账,不一致拦截并提示差额 - 按流水归集:一条流水一张金蝶收款单,分录取全部有效分摊行;awrp_src_code 上移至流水、重试复用防重试双单 - 已推送终态闸门禁止二次推送;推送失败落 push_status/push_fail_reason 可重试,本地认领不回滚 - 定时任务 ReceiptClaimPushTask(thirdParty.bfi.receiptClaimPush.*,按认领人分组重查公告)+ 手动按钮 POST /receipt-flow/push-kingdee - A301-1 回写轮询对象改流水并级联刷新认领主单冗余字段;作废仍不触发推送 - 认领链路批量化:结算单锁行/认领额聚合/汇总刷新改 IN 批量,候选单列表消除每页200次N+1,同步留痕 Db.saveBatch
This commit is contained in:
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for their own commercial purposes.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this license agreement and terms of use,
|
||||
* and you expressly agree to be bound by the terms of use of this software.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.pojo.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 收款认领推送金蝶结果(A301-3)
|
||||
* <p>
|
||||
* 推送失败不抛异常跨事务边界(保证失败状态落库不被回滚),以本结果对象向调用方报告成败与原因。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@Schema(description = "收款认领推送金蝶结果")
|
||||
public class ReceiptClaimPushResult implements Serializable {
|
||||
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Schema(description = "是否推送成功")
|
||||
private boolean success;
|
||||
|
||||
@Schema(description = "金蝶收款单号(成功时返回,可能为空:金蝶响应未返回单号)")
|
||||
private String kingdeeBillNo;
|
||||
|
||||
@Schema(description = "失败原因(成功时为空)")
|
||||
private String message;
|
||||
|
||||
public static ReceiptClaimPushResult success(String kingdeeBillNo) {
|
||||
ReceiptClaimPushResult result = new ReceiptClaimPushResult();
|
||||
result.setSuccess(true);
|
||||
result.setKingdeeBillNo(kingdeeBillNo);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ReceiptClaimPushResult fail(String message) {
|
||||
ReceiptClaimPushResult result = new ReceiptClaimPushResult();
|
||||
result.setSuccess(false);
|
||||
result.setMessage(message);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+25
-1
@@ -121,9 +121,33 @@ public class KingdeeReceiptFlow extends TenantEntity {
|
||||
*/
|
||||
private BigDecimal claimedAmount;
|
||||
/**
|
||||
* 认领状态(0未认领/1部分认领/2已认领,本地认领驱动)
|
||||
* 认领状态(unclaimed未认领/partial部分认领/claimed认领完成,累计认领与公告未认领金额对表)
|
||||
*/
|
||||
private String claimStatus;
|
||||
/**
|
||||
* 推送状态(none未推送/pushed已推送/failed推送失败;已推送为终态闸门,不允许二次推送)
|
||||
*/
|
||||
private String pushStatus;
|
||||
/**
|
||||
* 最近一次推送失败原因(成功后清空)
|
||||
*/
|
||||
private String pushFailReason;
|
||||
/**
|
||||
* 首次推送金蝶成功时间
|
||||
*/
|
||||
private LocalDateTime pushedTime;
|
||||
/**
|
||||
* 金蝶推送来源单据编码(SKCL+时间戳+流水id尾号6位,按流水生成、重试复用,A301-1 回写匹配键)
|
||||
*/
|
||||
private String awrpSrcCode;
|
||||
/**
|
||||
* 金蝶收款单号(推送响应回填 / A301-1 回写推进)
|
||||
*/
|
||||
private String kingdeeBillNo;
|
||||
/**
|
||||
* 金蝶回写状态(none未推送/pushed已推送/approved审核通过/received已收款/failed失败)
|
||||
*/
|
||||
private String kingdeeBillStatus;
|
||||
/**
|
||||
* 来源更新时间(取交易日期,同步时刷新)
|
||||
*/
|
||||
|
||||
+4
-2
@@ -66,10 +66,12 @@ public class ReceiptClaim extends TenantEntity {
|
||||
private String remark;
|
||||
private String claimStatus;
|
||||
/**
|
||||
* 金蝶认领结果推送来源单据编码(A301-3 awrp_src_code,A301-1 回写按此匹配)
|
||||
* 金蝶收款单号(冗余展示:推送成功/A301-1 回写时由流水级联刷新)
|
||||
*/
|
||||
private String awrpSrcCode;
|
||||
private String kingdeeBillNo;
|
||||
/**
|
||||
* 金蝶回写状态(冗余展示:与流水 kingdee_bill_status 同步刷新)
|
||||
*/
|
||||
private String kingdeeBillStatus;
|
||||
@JsonSerialize(using = ToStringSerializer.class)
|
||||
private Long voidedBy;
|
||||
|
||||
+4
@@ -58,6 +58,10 @@ public class ReceiptFlowVO extends KingdeeReceiptFlow {
|
||||
@TableField(exist = false)
|
||||
private String kingdeeClaimStatusName;
|
||||
@TableField(exist = false)
|
||||
private String pushStatusName;
|
||||
@TableField(exist = false)
|
||||
private String kingdeeBillStatusName;
|
||||
@TableField(exist = false)
|
||||
private BigDecimal remainingAmount;
|
||||
@TableField(exist = false)
|
||||
private String createUserName;
|
||||
|
||||
+12
@@ -215,6 +215,18 @@ public final class KingdeeBfiConstant {
|
||||
/** A301-3 认领推送:付款人类型(默认客户 bd_customer,待金蝶确认后如有调整直接改此常量)。 */
|
||||
public static final String BFI_CLAIM_PAYMENT_TYPE = "bd_customer";
|
||||
|
||||
/** 收款流水推送状态:未推送(未满足推送条件或尚未轮到推送)。 */
|
||||
public static final String RECEIPT_CLAIM_PUSH_NONE = "none";
|
||||
|
||||
/** 收款流水推送状态:已推送(终态闸门,不允许二次推送)。 */
|
||||
public static final String RECEIPT_CLAIM_PUSH_PUSHED = "pushed";
|
||||
|
||||
/** 收款流水推送状态:推送失败(本地认领保留,可重新推送,重试复用同一来源编码)。 */
|
||||
public static final String RECEIPT_CLAIM_PUSH_FAILED = "failed";
|
||||
|
||||
/** 收款流水金蝶回写状态:未推送(认领完成推送前的初始状态)。 */
|
||||
public static final String RECEIPT_CLAIM_BILL_NONE = "none";
|
||||
|
||||
/** 金蝶同步状态:认领结果已推送(A301-3 成功,等待金蝶收款单审核/收款,可继续回写)。 */
|
||||
public static final String KINGDEE_STATUS_PUSHED = "pushed";
|
||||
|
||||
|
||||
+9
@@ -35,6 +35,7 @@ import org.springblade.core.mp.support.Condition;
|
||||
import org.springblade.core.mp.support.Query;
|
||||
import org.springblade.core.secure.annotation.PreAuth;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.transport.pojo.dto.ReceiptClaimPushResult;
|
||||
import org.springblade.transport.pojo.dto.ReceiptClaimRequest;
|
||||
import org.springblade.transport.pojo.vo.ReceiptFlowVO;
|
||||
import org.springblade.transport.service.IReceiptFlowService;
|
||||
@@ -98,4 +99,12 @@ public class ReceiptFlowController extends BladeController {
|
||||
public R<Integer> sync() {
|
||||
return R.data(receiptFlowService.sync());
|
||||
}
|
||||
|
||||
@PostMapping("/push-kingdee")
|
||||
@ApiOperationSupport(order = 6)
|
||||
@Operation(summary = "推送金蝶收款认领(A301-3 收款认领结果推送,仅认领完成流水可推送)")
|
||||
public R<ReceiptClaimPushResult> pushKingdee(@RequestParam Long flowId) {
|
||||
ReceiptClaimPushResult result = receiptFlowService.pushClaimToKingdee(flowId);
|
||||
return result.isSuccess() ? R.data(result) : R.fail(result.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -22,7 +22,6 @@
|
||||
c.attachments_json,
|
||||
c.remark,
|
||||
c.claim_status,
|
||||
c.awrp_src_code,
|
||||
c.kingdee_bill_no,
|
||||
c.kingdee_bill_status,
|
||||
c.voided_by,
|
||||
|
||||
+17
@@ -27,6 +27,7 @@ package org.springblade.transport.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.transport.pojo.dto.ReceiptClaimPushResult;
|
||||
import org.springblade.transport.pojo.dto.ReceiptClaimRequest;
|
||||
import org.springblade.transport.pojo.entity.KingdeeReceiptFlow;
|
||||
import org.springblade.transport.pojo.vo.ReceiptFlowVO;
|
||||
@@ -57,4 +58,20 @@ public interface IReceiptFlowService extends BaseService<KingdeeReceiptFlow> {
|
||||
* @return 本次同步条数
|
||||
*/
|
||||
int sync();
|
||||
|
||||
/**
|
||||
* 手动推送金蝶(A301-3 收款认领结果推送):认领完成且未推送过的流水才可推送,
|
||||
* 推送前以当前用户姓名重查 A301-2 最新公告对账。与定时任务共用同一推送实现。
|
||||
*
|
||||
* @param flowId 收款流水id
|
||||
* @return 推送结果(失败不抛异常,原因随结果返回)
|
||||
*/
|
||||
ReceiptClaimPushResult pushClaimToKingdee(Long flowId);
|
||||
|
||||
/**
|
||||
* 定时推送金蝶:扫描认领完成且未推送过的流水,按认领人姓名分组重查 A301-2 最新公告后逐条推送。
|
||||
*
|
||||
* @return 执行摘要
|
||||
*/
|
||||
String pushClaimByTask();
|
||||
}
|
||||
|
||||
+59
-31
@@ -215,23 +215,41 @@ public class ReceiptClaimRecordServiceImpl extends BaseServiceImpl<ReceiptClaimM
|
||||
List<ReceiptClaimSettlement> orderedRelations = relations.stream()
|
||||
.sorted(Comparator.comparing(ReceiptClaimSettlement::getFormalSettlementId))
|
||||
.toList();
|
||||
for (ReceiptClaimSettlement relation : orderedRelations) {
|
||||
FormalSettlement settlement = formalSettlementMapper.selectOne(
|
||||
// 批量锁行关联结算单(一次 IN 查询替代逐单 selectOne,id 升序加锁防死锁)
|
||||
List<Long> settlementIds = orderedRelations.stream()
|
||||
.map(ReceiptClaimSettlement::getFormalSettlementId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
if (settlementIds.isEmpty()) {
|
||||
throw new ServiceException("关联结算单不存在");
|
||||
}
|
||||
Map<Long, FormalSettlement> settlements = formalSettlementMapper.selectList(
|
||||
Wrappers.<FormalSettlement>lambdaQuery()
|
||||
.eq(FormalSettlement::getId, relation.getFormalSettlementId())
|
||||
.last("FOR UPDATE"));
|
||||
.in(FormalSettlement::getId, settlementIds)
|
||||
.orderByAsc(FormalSettlement::getId)
|
||||
.last("FOR UPDATE")).stream()
|
||||
.collect(Collectors.toMap(FormalSettlement::getId, Function.identity()));
|
||||
for (ReceiptClaimSettlement relation : orderedRelations) {
|
||||
FormalSettlement settlement = settlements.get(relation.getFormalSettlementId());
|
||||
if (settlement == null || Objects.equals(settlement.getIsDeleted(), 1)) {
|
||||
throw new ServiceException("关联结算单不存在");
|
||||
}
|
||||
relation.setStatus(0);
|
||||
claimSettlementMapper.updateById(relation);
|
||||
formalSettlementService.refreshPaymentSummary(settlement.getId());
|
||||
}
|
||||
// 分摊行批量置无效(单条 UPDATE 替代逐行 updateById,实体携带 status 保留审计字段填充)
|
||||
ReceiptClaimSettlement voidTemplate = new ReceiptClaimSettlement();
|
||||
voidTemplate.setStatus(0);
|
||||
claimSettlementMapper.update(voidTemplate, Wrappers.<ReceiptClaimSettlement>lambdaUpdate()
|
||||
.in(ReceiptClaimSettlement::getId, relations.stream()
|
||||
.map(ReceiptClaimSettlement::getId).toList()));
|
||||
// 批量刷新结算单收款汇总(固定数次查询,避免逐单 N+1)
|
||||
formalSettlementService.refreshPaymentSummaries(settlementIds);
|
||||
|
||||
BigDecimal flowClaimedAfter = money(flow.getClaimedAmount())
|
||||
.subtract(money(claim.getClaimAmount())).max(BigDecimal.ZERO);
|
||||
flow.setClaimedAmount(flowClaimedAfter);
|
||||
flow.setClaimStatus(amountClaimStatus(flowClaimedAfter, flow.getReceiptAmount()));
|
||||
// 认领状态对照公告未认领金额重算(作废分摊不计入累计)
|
||||
flow.setClaimStatus(amountClaimStatus(flowClaimedAfter, flow.getUnclaimedAmount()));
|
||||
receiptFlowMapper.updateById(flow);
|
||||
|
||||
String kingdeeBillNo = buildKingdeeBillNo(claim.getId());
|
||||
@@ -260,34 +278,35 @@ public class ReceiptClaimRecordServiceImpl extends BaseServiceImpl<ReceiptClaimM
|
||||
}
|
||||
|
||||
/**
|
||||
* A301-1 收款单查询回写认领结果:按来源单据编码匹配金蝶收款单,推进金蝶单据状态
|
||||
* A301-1 收款单查询回写认领结果:按来源单据编码匹配金蝶收款单,推进流水金蝶回写状态
|
||||
* <p>
|
||||
* 回写口径:A301-1 契约本身不支持按来源单号过滤,故按近 30 天业务日期窗口全量翻页后,
|
||||
* 以金蝶收款单 sourcebillnumber == 认领记录 awrp_src_code(A301-3 推送时生成)在本地匹配;
|
||||
* 仅推进金蝶单据状态与补登收款单号,不改本地认领金额/分摊。
|
||||
* 以金蝶收款单 sourcebillnumber == 流水 awrp_src_code(A301-3 推送时按流水生成、重试复用)在本地匹配;
|
||||
* 仅推进金蝶回写状态与补登收款单号,不改本地认领金额/分摊;名下未作废认领主单冗余展示字段级联刷新。
|
||||
*
|
||||
* @return 回写条数
|
||||
*/
|
||||
@Override
|
||||
public int refreshKingdeeResult() {
|
||||
List<ReceiptClaim> pending = baseMapper.selectList(Wrappers.<ReceiptClaim>lambdaQuery()
|
||||
.eq(ReceiptClaim::getClaimStatus, CLAIMED)
|
||||
.isNotNull(ReceiptClaim::getAwrpSrcCode)
|
||||
.in(ReceiptClaim::getKingdeeBillStatus,
|
||||
List.of(KingdeeBfiConstant.KINGDEE_STATUS_PUSHED, APPROVED))
|
||||
.orderByAsc(ReceiptClaim::getId));
|
||||
List<KingdeeReceiptFlow> pending = receiptFlowMapper.selectList(
|
||||
Wrappers.<KingdeeReceiptFlow>lambdaQuery()
|
||||
.eq(KingdeeReceiptFlow::getPushStatus, KingdeeBfiConstant.RECEIPT_CLAIM_PUSH_PUSHED)
|
||||
.in(KingdeeReceiptFlow::getKingdeeBillStatus,
|
||||
List.of(KingdeeBfiConstant.KINGDEE_STATUS_PUSHED, APPROVED))
|
||||
.eq(KingdeeReceiptFlow::getStatus, 1)
|
||||
.orderByAsc(KingdeeReceiptFlow::getId));
|
||||
if (pending.isEmpty()) {
|
||||
log.info("金蝶认领结果回写:无待回写认领记录(已推送/已审核且未终态)");
|
||||
log.info("金蝶认领结果回写:无待回写流水(已推送/已审核且未终态)");
|
||||
return 0;
|
||||
}
|
||||
Set<String> srcCodes = pending.stream()
|
||||
.map(ReceiptClaim::getAwrpSrcCode)
|
||||
.map(KingdeeReceiptFlow::getAwrpSrcCode)
|
||||
.filter(Func::isNotEmpty)
|
||||
.collect(Collectors.toSet());
|
||||
if (srcCodes.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
log.info("金蝶认领结果回写:待回写认领记录 {} 条", pending.size());
|
||||
log.info("金蝶认领结果回写:待回写流水 {} 条", pending.size());
|
||||
|
||||
LocalDate end = LocalDate.now();
|
||||
LocalDate begin = end.minusDays(RESULT_WINDOW_DAYS);
|
||||
@@ -310,21 +329,27 @@ public class ReceiptClaimRecordServiceImpl extends BaseServiceImpl<ReceiptClaimM
|
||||
.collect(Collectors.toMap(BfiRecBillVO::getSourcebillnumber, Function.identity(),
|
||||
(first, second) -> first, HashMap::new));
|
||||
int updated = 0;
|
||||
for (ReceiptClaim claim : pending) {
|
||||
BfiRecBillVO bill = matched.get(claim.getAwrpSrcCode());
|
||||
for (KingdeeReceiptFlow flow : pending) {
|
||||
BfiRecBillVO bill = matched.get(flow.getAwrpSrcCode());
|
||||
if (bill == null) {
|
||||
// 窗口内未查到:金蝶回单延迟/状态未同步,不改状态,由下次回写或人工处置兜底
|
||||
continue;
|
||||
}
|
||||
if (Func.isEmpty(claim.getKingdeeBillNo())) {
|
||||
claim.setKingdeeBillNo(bill.getBillno());
|
||||
if (Func.isEmpty(flow.getKingdeeBillNo())) {
|
||||
flow.setKingdeeBillNo(bill.getBillno());
|
||||
}
|
||||
claim.setKingdeeBillStatus(billStatusOf(bill.getBillstatus()));
|
||||
updateById(claim);
|
||||
flow.setKingdeeBillStatus(billStatusOf(bill.getBillstatus()));
|
||||
receiptFlowMapper.updateById(flow);
|
||||
// 级联刷新名下未作废认领主单的冗余展示字段
|
||||
baseMapper.update(null, Wrappers.<ReceiptClaim>lambdaUpdate()
|
||||
.eq(ReceiptClaim::getReceiptFlowId, flow.getId())
|
||||
.eq(ReceiptClaim::getClaimStatus, CLAIMED)
|
||||
.set(ReceiptClaim::getKingdeeBillNo, flow.getKingdeeBillNo())
|
||||
.set(ReceiptClaim::getKingdeeBillStatus, flow.getKingdeeBillStatus()));
|
||||
updated++;
|
||||
log.info("金蝶认领结果回写 claimId:{},awrpSrcCode:{},金蝶收款单号:{},状态:{} → {}",
|
||||
claim.getId(), claim.getAwrpSrcCode(), bill.getBillno(), bill.getBillstatus(),
|
||||
claim.getKingdeeBillStatus());
|
||||
log.info("金蝶认领结果回写 flowId:{},awrpSrcCode:{},金蝶收款单号:{},状态:{} → {}",
|
||||
flow.getId(), flow.getAwrpSrcCode(), bill.getBillno(), bill.getBillstatus(),
|
||||
flow.getKingdeeBillStatus());
|
||||
}
|
||||
log.info("金蝶认领结果回写完成:匹配 {} 条,更新 {} 条", matched.size(), updated);
|
||||
return updated;
|
||||
@@ -375,11 +400,14 @@ public class ReceiptClaimRecordServiceImpl extends BaseServiceImpl<ReceiptClaimM
|
||||
return "KDCX" + time + suffix.substring(Math.max(0, suffix.length() - 6));
|
||||
}
|
||||
|
||||
private String amountClaimStatus(BigDecimal claimedAmount, BigDecimal receiptAmount) {
|
||||
/**
|
||||
* 累计认领金额 → 流水认领状态:对照公告未认领金额判定(与收款流水侧口径一致)
|
||||
*/
|
||||
private String amountClaimStatus(BigDecimal claimedAmount, BigDecimal unclaimedAmount) {
|
||||
if (claimedAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return "unclaimed";
|
||||
}
|
||||
return claimedAmount.compareTo(money(receiptAmount)) >= 0 ? "claimed" : "partial";
|
||||
return claimedAmount.compareTo(money(unclaimedAmount)) >= 0 ? "claimed" : "partial";
|
||||
}
|
||||
|
||||
private String amountStatus(BigDecimal paidAmount, BigDecimal settlementAmount) {
|
||||
|
||||
+360
-114
@@ -28,6 +28,7 @@ package org.springblade.transport.service.impl;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.toolkit.Db;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
@@ -52,6 +53,7 @@ import org.springblade.transport.mapper.KingdeeReceiptFlowMapper;
|
||||
import org.springblade.transport.mapper.ReceiptClaimMapper;
|
||||
import org.springblade.transport.mapper.ReceiptClaimSettlementMapper;
|
||||
import org.springblade.transport.mapper.ReceiptFlowRecordMapper;
|
||||
import org.springblade.transport.pojo.dto.ReceiptClaimPushResult;
|
||||
import org.springblade.transport.pojo.dto.ReceiptClaimRequest;
|
||||
import org.springblade.transport.pojo.entity.FormalSettlement;
|
||||
import org.springblade.transport.pojo.entity.KingdeeReceiptFlow;
|
||||
@@ -65,6 +67,7 @@ import org.springblade.transport.support.TransportBusinessSupport;
|
||||
import org.springblade.transport.wrapper.ReceiptFlowWrapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionTemplate;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
@@ -72,10 +75,12 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -117,6 +122,7 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
private final IFormalSettlementService formalSettlementService;
|
||||
private final IBfiService bfiService;
|
||||
private final BfiProperties bfiProperties;
|
||||
private final TransactionTemplate transactionTemplate;
|
||||
|
||||
@Override
|
||||
public IPage<ReceiptFlowVO> selectPage(IPage<KingdeeReceiptFlow> page, ReceiptFlowVO query) {
|
||||
@@ -152,7 +158,7 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
vo.setClaimDate(LocalDate.now());
|
||||
vo.setClaimRecords(recordMapper.selectList(Wrappers.<ReceiptFlowRecord>lambdaQuery()
|
||||
.eq(ReceiptFlowRecord::getReceiptFlowId, id)
|
||||
.eq(ReceiptFlowRecord::getActionType, "claim")
|
||||
.in(ReceiptFlowRecord::getActionType, List.of("claim", "push"))
|
||||
.orderByDesc(ReceiptFlowRecord::getCreateTime)));
|
||||
return vo;
|
||||
}
|
||||
@@ -171,8 +177,12 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
.or().like(FormalSettlement::getContractName, keyword))
|
||||
.orderByDesc(FormalSettlement::getCreateTime)
|
||||
.last("limit 200"));
|
||||
// 候选结算单的累计认领金额一次聚合查询(避免逐单 N+1)
|
||||
Map<Long, BigDecimal> claimedMap = receiptClaimedAmounts(settlements.stream()
|
||||
.map(FormalSettlement::getId).toList());
|
||||
return settlements.stream()
|
||||
.map(this::candidateRow)
|
||||
.map(settlement -> candidateRow(settlement,
|
||||
claimedMap.getOrDefault(settlement.getId(), BigDecimal.ZERO)))
|
||||
.filter(row -> ((BigDecimal) row.get("remainingReceiptAmount")).compareTo(BigDecimal.ZERO) > 0)
|
||||
.toList();
|
||||
}
|
||||
@@ -240,6 +250,9 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
|
||||
// 收款流水
|
||||
KingdeeReceiptFlow flow = lockedFlow(request.getFlowId());
|
||||
if (flow.getBillId() == null) {
|
||||
throw new ServiceException("认领公告id缺失,请先手动同步金蝶收款流水");
|
||||
}
|
||||
// 结算单id → 分摊金额
|
||||
Map<Long, BigDecimal> allocationMap = allocationMap(request.getSettlements());
|
||||
// 结算单id列表(升序)
|
||||
@@ -250,31 +263,37 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
List<FormalSettlement> settlements = settlementIds.stream().map(settlementMap::get).toList();
|
||||
assertCompatible(settlements);
|
||||
|
||||
// 结算单id → 认领前已认领金额
|
||||
Map<Long, BigDecimal> previousClaimedMap = new LinkedHashMap<>();
|
||||
// 结算单id → 认领前已认领金额(一次聚合查询,避免逐单 N+1)
|
||||
Map<Long, BigDecimal> previousClaimedMap = receiptClaimedAmounts(settlementIds);
|
||||
// 本次分摊总金额
|
||||
BigDecimal allocatedTotal = BigDecimal.ZERO;
|
||||
for (FormalSettlement settlement : settlements) {
|
||||
// 本次分摊金额
|
||||
BigDecimal allocated = allocationMap.get(settlement.getId());
|
||||
// 认领前已认领金额
|
||||
BigDecimal previousClaimed = settlementClaimedAmount(settlement.getId());
|
||||
BigDecimal previousClaimed = previousClaimedMap.getOrDefault(settlement.getId(), BigDecimal.ZERO);
|
||||
// 结算总金额
|
||||
BigDecimal settlementAmount = positiveMoney(settlement.getSettlementAmount(), "结算总金额");
|
||||
if (previousClaimed.add(allocated).compareTo(settlementAmount) > 0) {
|
||||
throw new ServiceException("结算单" + settlement.getFormalSettlementNo()
|
||||
+ "的累计认领金额不能超过结算总应收含税金额");
|
||||
}
|
||||
previousClaimedMap.put(settlement.getId(), previousClaimed);
|
||||
allocatedTotal = allocatedTotal.add(allocated);
|
||||
}
|
||||
|
||||
// 收款金额
|
||||
BigDecimal receiptAmount = positiveMoney(flow.getReceiptAmount(), "收款金额");
|
||||
// 公告未认领金额(认领上限与认领完成判定基准,A301-2 口径)
|
||||
if (flow.getUnclaimedAmount() == null) {
|
||||
throw new ServiceException("公告未认领金额缺失,请先手动同步金蝶收款流水");
|
||||
}
|
||||
BigDecimal unclaimedAmount = money(flow.getUnclaimedAmount());
|
||||
if (unclaimedAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
throw new ServiceException("该流水公告未认领金额为0,无需认领");
|
||||
}
|
||||
// 流水认领前已认领金额
|
||||
BigDecimal previousFlowClaimed = flowClaimedAmount(flow.getId());
|
||||
if (previousFlowClaimed.add(allocatedTotal).compareTo(receiptAmount) > 0) {
|
||||
throw new ServiceException("本次分摊金额不能超过流水剩余可认领金额");
|
||||
if (previousFlowClaimed.add(allocatedTotal).compareTo(unclaimedAmount) > 0) {
|
||||
throw new ServiceException("累计认领金额不能超过公告未认领金额,剩余可认领 "
|
||||
+ unclaimedAmount.subtract(previousFlowClaimed).max(BigDecimal.ZERO));
|
||||
}
|
||||
|
||||
// 当前用户部门
|
||||
@@ -301,17 +320,15 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
claim.setRemark(trimToNull(request.getRemark()));
|
||||
// 认领状态
|
||||
claim.setClaimStatus(CLAIMED);
|
||||
// 金蝶单据状态(待推送)
|
||||
claim.setKingdeeBillStatus("none");
|
||||
// 金蝶回写状态(待推送)
|
||||
claim.setKingdeeBillStatus(KingdeeBfiConstant.RECEIPT_CLAIM_BILL_NONE);
|
||||
claimMapper.insert(claim);
|
||||
|
||||
for (FormalSettlement settlement : settlements) {
|
||||
// 认领前已认领金额
|
||||
BigDecimal previousClaimed = previousClaimedMap.get(settlement.getId());
|
||||
BigDecimal previousClaimed = previousClaimedMap.getOrDefault(settlement.getId(), BigDecimal.ZERO);
|
||||
// 本次分摊金额
|
||||
BigDecimal allocated = allocationMap.get(settlement.getId());
|
||||
// 认领后累计金额
|
||||
BigDecimal claimedAfter = previousClaimed.add(allocated);
|
||||
|
||||
// 认领结算单分摊关系
|
||||
ReceiptClaimSettlement relation = new ReceiptClaimSettlement();
|
||||
@@ -330,17 +347,17 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
// 本次分摊金额
|
||||
relation.setAllocatedReceiptAmount(allocated);
|
||||
claimSettlementMapper.insert(relation);
|
||||
|
||||
// 刷新结算单收款汇总
|
||||
formalSettlementService.refreshPaymentSummary(settlement.getId());
|
||||
}
|
||||
|
||||
// 批量刷新结算单收款汇总(固定数次查询,避免逐单 N+1)
|
||||
formalSettlementService.refreshPaymentSummaries(settlementIds);
|
||||
|
||||
// 认领前状态
|
||||
String fromStatus = normalizeClaimStatus(flow.getClaimStatus());
|
||||
// 认领后累计金额
|
||||
BigDecimal claimedAfter = previousFlowClaimed.add(allocatedTotal);
|
||||
// 认领后状态
|
||||
String toStatus = amountClaimStatus(claimedAfter, receiptAmount);
|
||||
// 认领后状态(对照公告未认领金额)
|
||||
String toStatus = amountClaimStatus(claimedAfter, unclaimedAmount);
|
||||
// 更新流水已认领金额
|
||||
flow.setClaimedAmount(claimedAfter);
|
||||
// 更新流水认领状态
|
||||
@@ -350,7 +367,7 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
record(flow.getId(), claim.getId(), "claim", "认领收款流水", fromStatus, toStatus,
|
||||
allocatedTotal, "关联" + settlements.size() + "张应收正式结算单");
|
||||
|
||||
pushClaimResultToKingdee(flow, claim, settlements, allocationMap);
|
||||
// 认领只登记本地,不推送金蝶;推送由定时任务/手动按钮在认领完成后触发
|
||||
return claim.getId();
|
||||
}
|
||||
|
||||
@@ -361,27 +378,7 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
if (Func.isEmpty(claimusers)) {
|
||||
throw new ServiceException("当前用户无姓名,无法同步金蝶认领公告");
|
||||
}
|
||||
// 从配置读取查询时间窗口(月数),默认近半年
|
||||
int months = bfiProperties.getClaimAnnounce().getMonths();
|
||||
LocalDateTime endDate = LocalDateTime.now();
|
||||
LocalDateTime startDate = endDate.minusMonths(months);
|
||||
String modifytimeS = startDate.format(BFI_DATE_TIME_FORMATTER);
|
||||
String modifytimeE = endDate.format(BFI_DATE_TIME_FORMATTER);
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
log.info("调用BFI接口 收款认领公告查询开始 claimusers:{},时间窗口:{} ~ {}(近{}个月)",
|
||||
claimusers, modifytimeS, modifytimeE, months);
|
||||
List<BfiClaimAnnounceVO> announces = BfiUtil.queryAllPages(pageNo -> {
|
||||
BfiClaimAnnounceQueryDTO queryDTO = new BfiClaimAnnounceQueryDTO();
|
||||
queryDTO.setClaimusers(claimusers);
|
||||
queryDTO.setModifytimeS(modifytimeS);
|
||||
queryDTO.setModifytimeE(modifytimeE);
|
||||
queryDTO.setPageNo(pageNo);
|
||||
queryDTO.setPageSize(SYNC_PAGE_SIZE);
|
||||
return bfiService.queryClaimAnnounce(queryDTO);
|
||||
});
|
||||
log.info("调用BFI接口 收款认领公告查询结束 耗时:{}ms,拉取 {} 条",
|
||||
System.currentTimeMillis() - start, announces.size());
|
||||
List<BfiClaimAnnounceVO> announces = queryClaimAnnounces(claimusers);
|
||||
|
||||
if (announces.isEmpty()) {
|
||||
record(null, null, "sync", "手动同步流水", null, null, BigDecimal.ZERO,
|
||||
@@ -468,8 +465,9 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
|
||||
// 复制公告数据到实体(不覆盖本地认领字段)
|
||||
copyAnnounceRow(entity, announce, detailSerialNo, receiptAmount);
|
||||
// 根据本地已认领金额重新计算认领状态
|
||||
entity.setClaimStatus(amountClaimStatus(money(entity.getClaimedAmount()), receiptAmount));
|
||||
// 根据本地累计认领金额与最新公告未认领金额重新计算认领状态
|
||||
entity.setClaimStatus(amountClaimStatus(money(entity.getClaimedAmount()),
|
||||
entity.getUnclaimedAmount()));
|
||||
|
||||
if (created) {
|
||||
toInsert.add(entity);
|
||||
@@ -493,15 +491,19 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
log.info("收款流水批量更新完成");
|
||||
}
|
||||
|
||||
// 记录操作日志
|
||||
// 记录操作日志(批量插入,避免逐行 N+1)
|
||||
log.info("收款流水同步开始记录操作日志,共 {} 条", toInsert.size() + toUpdate.size());
|
||||
List<ReceiptFlowRecord> records = new ArrayList<>(toInsert.size() + toUpdate.size());
|
||||
for (KingdeeReceiptFlow entity : toInsert) {
|
||||
record(entity.getId(), null, "sync", "新增金蝶收款流水",
|
||||
entity.getClaimStatus(), entity.getClaimStatus(), BigDecimal.ZERO, entity.getDetailSerialNo());
|
||||
records.add(buildRecord(entity.getId(), null, "sync", "新增金蝶收款流水",
|
||||
entity.getClaimStatus(), entity.getClaimStatus(), BigDecimal.ZERO, entity.getDetailSerialNo()));
|
||||
}
|
||||
for (KingdeeReceiptFlow entity : toUpdate) {
|
||||
record(entity.getId(), null, "sync", "更新金蝶收款流水",
|
||||
entity.getClaimStatus(), entity.getClaimStatus(), BigDecimal.ZERO, entity.getDetailSerialNo());
|
||||
records.add(buildRecord(entity.getId(), null, "sync", "更新金蝶收款流水",
|
||||
entity.getClaimStatus(), entity.getClaimStatus(), BigDecimal.ZERO, entity.getDetailSerialNo()));
|
||||
}
|
||||
if (!records.isEmpty()) {
|
||||
Db.saveBatch(records);
|
||||
}
|
||||
log.info("收款流水操作日志记录完成");
|
||||
|
||||
@@ -510,8 +512,7 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
return createdCount + updatedCount;
|
||||
}
|
||||
|
||||
private Map<String, Object> candidateRow(FormalSettlement settlement) {
|
||||
BigDecimal claimedAmount = settlementClaimedAmount(settlement.getId());
|
||||
private Map<String, Object> candidateRow(FormalSettlement settlement, BigDecimal claimedAmount) {
|
||||
BigDecimal settlementAmount = money(settlement.getSettlementAmount());
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
row.put("id", settlement.getId());
|
||||
@@ -547,16 +548,20 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
}
|
||||
|
||||
private Map<Long, FormalSettlement> lockSettlements(List<Long> settlementIds) {
|
||||
List<FormalSettlement> settlements = new ArrayList<>();
|
||||
// 一次锁行查询替代逐单 selectOne(id 升序加锁,与逐单版加锁顺序一致防死锁)
|
||||
List<FormalSettlement> settlements = formalSettlementMapper.selectList(
|
||||
Wrappers.<FormalSettlement>lambdaQuery()
|
||||
.in(FormalSettlement::getId, settlementIds)
|
||||
.orderByAsc(FormalSettlement::getId)
|
||||
.last("FOR UPDATE"));
|
||||
Map<Long, FormalSettlement> settlementMap = new LinkedHashMap<>();
|
||||
for (Long settlementId : settlementIds) {
|
||||
FormalSettlement settlement = formalSettlementMapper.selectOne(
|
||||
Wrappers.<FormalSettlement>lambdaQuery()
|
||||
.eq(FormalSettlement::getId, settlementId)
|
||||
.last("FOR UPDATE"));
|
||||
settlements.add(availableSettlement(settlement));
|
||||
FormalSettlement settlement = settlements.stream()
|
||||
.filter(item -> settlementId.equals(item.getId()))
|
||||
.findFirst().orElse(null);
|
||||
settlementMap.put(settlementId, availableSettlement(settlement));
|
||||
}
|
||||
return settlements.stream().collect(Collectors.toMap(FormalSettlement::getId,
|
||||
Function.identity(), (first, second) -> first, LinkedHashMap::new));
|
||||
return settlementMap;
|
||||
}
|
||||
|
||||
private FormalSettlement availableSettlement(FormalSettlement settlement) {
|
||||
@@ -582,13 +587,20 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal settlementClaimedAmount(Long settlementId) {
|
||||
/**
|
||||
* 批量聚合结算单的累计有效认领金额:一次 IN 查询按结算单分组求和,避免逐单 N+1
|
||||
*/
|
||||
private Map<Long, BigDecimal> receiptClaimedAmounts(List<Long> settlementIds) {
|
||||
List<Long> ids = settlementIds.stream().filter(Objects::nonNull).distinct().toList();
|
||||
if (ids.isEmpty()) {
|
||||
return Map.of();
|
||||
}
|
||||
return claimSettlementMapper.selectList(Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.eq(ReceiptClaimSettlement::getFormalSettlementId, settlementId)
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)).stream()
|
||||
.map(ReceiptClaimSettlement::getAllocatedReceiptAmount)
|
||||
.map(this::money)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
.in(ReceiptClaimSettlement::getFormalSettlementId, ids)
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)).stream()
|
||||
.collect(Collectors.groupingBy(ReceiptClaimSettlement::getFormalSettlementId,
|
||||
Collectors.reducing(BigDecimal.ZERO,
|
||||
item -> money(item.getAllocatedReceiptAmount()), BigDecimal::add)));
|
||||
}
|
||||
|
||||
private BigDecimal flowClaimedAmount(Long flowId) {
|
||||
@@ -622,34 +634,183 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
}
|
||||
|
||||
/**
|
||||
* A301-3 收款认领结果推送:组装报文推送金蝶,成功后回写来源编码、金蝶收款单号与推送状态
|
||||
* 手动推送金蝶(A301-3 收款认领结果推送)
|
||||
* <p>
|
||||
* 认领在本地落库后立即推送,推送失败抛异常由事务整体回滚,保证金蝶与本地认领口径一致;
|
||||
* awrpSrcCode 同时作为 A301-1 收款单查询回写的匹配键(金蝶收款单 sourcebillnumber)。
|
||||
*
|
||||
* @param flow 收款流水(须含 billId)
|
||||
* @param claim 本地认领主单(已落库)
|
||||
* @param settlements 分摊的应收正式结算单(保持 id 升序)
|
||||
* @param allocationMap 结算单 id → 分摊金额
|
||||
* 仅认领完成且未推送过的流水可推送;推送前以当前用户姓名重查 A301-2 最新公告对账。
|
||||
* 推送失败不抛异常跨事务边界(保证失败状态落库),原因随结果对象返回。
|
||||
*/
|
||||
private void pushClaimResultToKingdee(KingdeeReceiptFlow flow, ReceiptClaim claim,
|
||||
List<FormalSettlement> settlements, Map<Long, BigDecimal> allocationMap) {
|
||||
if (flow.getBillId() == null) {
|
||||
throw new ServiceException("认领公告id缺失,请先手动同步金蝶收款流水");
|
||||
@Override
|
||||
public ReceiptClaimPushResult pushClaimToKingdee(Long flowId) {
|
||||
if (flowId == null) {
|
||||
throw new ServiceException("收款流水ID不能为空");
|
||||
}
|
||||
// 来源单据编码:多个结算单号用_拼接
|
||||
String awrpSrcCode = settlements.stream()
|
||||
.map(FormalSettlement::getFormalSettlementNo)
|
||||
.collect(Collectors.joining("_"));
|
||||
claim.setAwrpSrcCode(awrpSrcCode);
|
||||
KingdeeReceiptFlow flow = existing(flowId);
|
||||
if (flow.getBillId() == null) {
|
||||
return ReceiptClaimPushResult.fail("认领公告id缺失,请先手动同步金蝶收款流水");
|
||||
}
|
||||
String claimusers = UserCache.getUserRealName(AuthUtil.getUserId());
|
||||
if (Func.isEmpty(claimusers)) {
|
||||
throw new ServiceException("当前用户无姓名,无法推送金蝶收款认领");
|
||||
}
|
||||
// 推送前对账:以当前用户身份重查 A301-2 拿最新公告(手动推送者须在公告认领范围内)
|
||||
BfiClaimAnnounceVO announce = queryClaimAnnounces(claimusers).stream()
|
||||
.filter(item -> flow.getBillId().equals(item.getId()))
|
||||
.findFirst().orElse(null);
|
||||
if (announce == null) {
|
||||
return ReceiptClaimPushResult.fail("金蝶侧未查询到该认领公告,当前用户可能不在公告认领范围内,请重新同步后重试");
|
||||
}
|
||||
return pushClaimLocked(flow.getId(), announce);
|
||||
}
|
||||
|
||||
/**
|
||||
* 定时推送金蝶:扫描认领完成且未推送过的流水,按认领人姓名分组重查 A301-2 最新公告后逐条推送
|
||||
* <p>
|
||||
* A301-2 的 claimusers 必填且为单个用户姓名,定时任务无登录人上下文,
|
||||
* 故取候选流水的有效认领人姓名去重后逐人查询公告,按公告id(bill_id)匹配流水。
|
||||
* 单条流水推送失败独立消化,不影响同批其他流水。
|
||||
*/
|
||||
@Override
|
||||
public String pushClaimByTask() {
|
||||
List<KingdeeReceiptFlow> candidates = list(Wrappers.<KingdeeReceiptFlow>lambdaQuery()
|
||||
.isNotNull(KingdeeReceiptFlow::getBillId)
|
||||
.in(KingdeeReceiptFlow::getPushStatus, List.of(
|
||||
KingdeeBfiConstant.RECEIPT_CLAIM_PUSH_NONE, KingdeeBfiConstant.RECEIPT_CLAIM_PUSH_FAILED))
|
||||
.eq(KingdeeReceiptFlow::getClaimStatus, CLAIMED)
|
||||
.eq(KingdeeReceiptFlow::getStatus, 1)
|
||||
.orderByAsc(KingdeeReceiptFlow::getId));
|
||||
if (candidates.isEmpty()) {
|
||||
return "无待推送流水";
|
||||
}
|
||||
List<Long> flowIds = candidates.stream().map(KingdeeReceiptFlow::getId).toList();
|
||||
Set<String> claimerNames = claimMapper.selectList(Wrappers.<ReceiptClaim>lambdaQuery()
|
||||
.in(ReceiptClaim::getReceiptFlowId, flowIds)
|
||||
.eq(ReceiptClaim::getClaimStatus, CLAIMED)).stream()
|
||||
.map(ReceiptClaim::getClaimerName)
|
||||
.filter(Func::isNotEmpty)
|
||||
.collect(Collectors.toSet());
|
||||
if (claimerNames.isEmpty()) {
|
||||
return "待推送流水 " + candidates.size() + " 条,但无有效认领人,跳过";
|
||||
}
|
||||
// 公告按公告id归集(多个认领人查询到同一公告时取首条)
|
||||
Map<Long, BfiClaimAnnounceVO> announceByBillId = new HashMap<>();
|
||||
for (String claimerName : claimerNames) {
|
||||
try {
|
||||
for (BfiClaimAnnounceVO announce : queryClaimAnnounces(claimerName)) {
|
||||
if (announce.getId() != null) {
|
||||
announceByBillId.putIfAbsent(announce.getId(), announce);
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
log.error("定时推送收款认领:认领人[{}]公告查询失败", claimerName, exception);
|
||||
}
|
||||
}
|
||||
int successCount = 0;
|
||||
int failedCount = 0;
|
||||
int skippedCount = 0;
|
||||
for (KingdeeReceiptFlow flow : candidates) {
|
||||
BfiClaimAnnounceVO announce = announceByBillId.get(flow.getBillId());
|
||||
if (announce == null) {
|
||||
skippedCount++;
|
||||
log.info("定时推送收款认领:流水[{}]未在认领人公告查询结果中命中,跳过", flow.getId());
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
ReceiptClaimPushResult result = pushClaimLocked(flow.getId(), announce);
|
||||
if (result.isSuccess()) {
|
||||
successCount++;
|
||||
} else {
|
||||
failedCount++;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
failedCount++;
|
||||
log.error("定时推送收款认领:流水[{}]推送异常", flow.getId(), exception);
|
||||
}
|
||||
}
|
||||
String summary = "扫描 " + candidates.size() + " 条,推送成功 " + successCount
|
||||
+ " 条,推送失败 " + failedCount + " 条,公告未命中跳过 " + skippedCount + " 条";
|
||||
log.info("定时推送收款认领完成:{}", summary);
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 事务内推送:流水行锁覆盖闸门校验、对账、推送与状态落库全过程,
|
||||
* 防定时任务与手动按钮并发对同一流水双推
|
||||
*/
|
||||
private ReceiptClaimPushResult pushClaimLocked(Long flowId, BfiClaimAnnounceVO announce) {
|
||||
return transactionTemplate.execute(status -> doPushClaimToKingdee(flowId, announce));
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送单条流水:静态闸门 → 最新公告对账 → 按流水归集分摊行推送 → 成败状态落库
|
||||
*/
|
||||
private ReceiptClaimPushResult doPushClaimToKingdee(Long flowId, BfiClaimAnnounceVO announce) {
|
||||
KingdeeReceiptFlow flow = lockedFlow(flowId);
|
||||
if (flow.getBillId() == null) {
|
||||
return ReceiptClaimPushResult.fail("认领公告id缺失,请先手动同步金蝶收款流水");
|
||||
}
|
||||
// 终态闸门:已推送流水不允许二次推送(作废重认领使等式再满足也不推,金蝶侧人工处置)
|
||||
if (KingdeeBfiConstant.RECEIPT_CLAIM_PUSH_PUSHED.equals(flow.getPushStatus())) {
|
||||
return ReceiptClaimPushResult.fail("该流水已推送金蝶,不允许重复推送");
|
||||
}
|
||||
// 推送前对账:以最新公告刷新公告侧未认领金额并重算认领状态(快照落库,页面口径同步修正)
|
||||
flow.setUnclaimedAmount(money(announce.getUnclaimamount()));
|
||||
flow.setKingdeeClaimStatus(trimToNull(announce.getClaimstatus()));
|
||||
flow.setClaimMe(announce.getClaimme());
|
||||
BigDecimal claimedTotal = flowClaimedAmount(flow.getId());
|
||||
flow.setClaimedAmount(claimedTotal);
|
||||
String claimStatus = amountClaimStatus(claimedTotal, flow.getUnclaimedAmount());
|
||||
flow.setClaimStatus(claimStatus);
|
||||
updateById(flow);
|
||||
if (!CLAIMED.equals(claimStatus)) {
|
||||
return ReceiptClaimPushResult.fail("累计认领金额(" + claimedTotal + ")与公告未认领金额("
|
||||
+ money(flow.getUnclaimedAmount()) + ")不一致,不满足推送条件");
|
||||
}
|
||||
// 推送来源编码:首次生成落库,重试复用(金蝶按 awrp_src_sys + awrp_src_code 幂等,防重试双单)
|
||||
if (Func.isEmpty(flow.getAwrpSrcCode())) {
|
||||
flow.setAwrpSrcCode(buildAwrpSrcCode(flow.getId()));
|
||||
}
|
||||
// 分录取该流水全部有效分摊行(结算单id升序,跨认领主单归集)
|
||||
List<ReceiptClaimSettlement> allocations = claimSettlementMapper.selectList(
|
||||
Wrappers.<ReceiptClaimSettlement>lambdaQuery()
|
||||
.eq(ReceiptClaimSettlement::getReceiptFlowId, flow.getId())
|
||||
.eq(ReceiptClaimSettlement::getStatus, 1)
|
||||
.orderByAsc(ReceiptClaimSettlement::getFormalSettlementId));
|
||||
if (allocations.isEmpty()) {
|
||||
return ReceiptClaimPushResult.fail("该流水无有效认领分摊,无法推送");
|
||||
}
|
||||
List<Long> settlementIds = allocations.stream()
|
||||
.map(ReceiptClaimSettlement::getFormalSettlementId)
|
||||
.filter(Objects::nonNull)
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<Long, FormalSettlement> settlements = settlementIds.isEmpty() ? Map.of()
|
||||
: formalSettlementMapper.selectList(Wrappers.<FormalSettlement>lambdaQuery()
|
||||
.in(FormalSettlement::getId, settlementIds)).stream()
|
||||
.collect(Collectors.toMap(FormalSettlement::getId, Function.identity(), (first, second) -> first));
|
||||
|
||||
BfiClaimPushDTO param = buildClaimPushDTO(flow, allocations, settlements);
|
||||
log.info("调用BFI接口 收款认领结果推送开始 flowId:{},billId:{},awrpSrcCode:{},分摊明细 {} 行,认领金额 {}",
|
||||
flow.getId(), flow.getBillId(), flow.getAwrpSrcCode(), allocations.size(), claimedTotal);
|
||||
try {
|
||||
BfiBillSaveResultVO result = bfiService.pushClaimResult(param);
|
||||
return applyClaimPushSuccess(flow, extractClaimPushBillNo(result));
|
||||
} catch (Exception exception) {
|
||||
return applyClaimPushFailure(flow, exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A301-3 推送报文组装:billid=公告id,分录取各分摊行(一笔流水一张金蝶收款单)
|
||||
*/
|
||||
private BfiClaimPushDTO buildClaimPushDTO(KingdeeReceiptFlow flow, List<ReceiptClaimSettlement> allocations,
|
||||
Map<Long, FormalSettlement> settlements) {
|
||||
BfiClaimPushDTO param = new BfiClaimPushDTO();
|
||||
// 【必填】认领公告id
|
||||
param.setBillid(flow.getBillId());
|
||||
// 【必填】来源系统编码
|
||||
param.setAwrpSrcSys(bfiProperties.getSrcSys());
|
||||
// 【必填】来源系统单据编码
|
||||
param.setAwrpSrcCode(awrpSrcCode);
|
||||
// 【必填】来源系统单据编码(按流水生成,重试复用)
|
||||
param.setAwrpSrcCode(flow.getAwrpSrcCode());
|
||||
// 【必填】收款类型.编码
|
||||
param.setRecpaytypeNumber(KingdeeBfiConstant.BFI_CLAIM_REC_PAY_TYPE);
|
||||
// 【必填】付款人类型
|
||||
@@ -659,42 +820,32 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
// 【必填】付款人id
|
||||
param.setRecpayer(flow.getPayerId());
|
||||
List<BfiClaimPushEntryDTO> entries = new ArrayList<>();
|
||||
BigDecimal allocatedTotal = BigDecimal.ZERO;
|
||||
for (FormalSettlement settlement : settlements) {
|
||||
int seq = 1;
|
||||
for (ReceiptClaimSettlement allocation : allocations) {
|
||||
BfiClaimPushEntryDTO entry = new BfiClaimPushEntryDTO();
|
||||
// 【必填】应收金额
|
||||
entry.setEReceivableamt(allocationMap.get(settlement.getId()));
|
||||
// 结算单id
|
||||
entry.setSeq(settlement.getId());
|
||||
if (Func.isNotEmpty(settlement.getKingdeeBillNo())) {
|
||||
// 【必填】应收金额(分摊金额)
|
||||
entry.setEReceivableamt(money(allocation.getAllocatedReceiptAmount()));
|
||||
// 分录行序号
|
||||
entry.setSeq((long) seq++);
|
||||
FormalSettlement settlement = settlements.get(allocation.getFormalSettlementId());
|
||||
if (settlement != null && Func.isNotEmpty(settlement.getKingdeeBillNo())) {
|
||||
// 结算单已同步金蝶应收单时携带核心单据引用,金蝶按应收单号核销认领金额
|
||||
// 核心单据类型
|
||||
// entry.setECorebilltype(KingdeeBfiConstant.BFI_CLAIM_CORE_BILL_TYPE);
|
||||
// 核心单据编号
|
||||
entry.setECorebillno(String.valueOf(settlement.getId()));
|
||||
entry.setECorebillno(settlement.getKingdeeBillNo());
|
||||
}
|
||||
allocatedTotal = allocatedTotal.add(money(allocationMap.get(settlement.getId())));
|
||||
entries.add(entry);
|
||||
}
|
||||
// 【必填】单据体分录
|
||||
param.setEntryentity(entries);
|
||||
|
||||
log.info("调用BFI接口 收款认领结果推送开始 claimId:{},billId:{},awrpSrcCode:{},分摊结算单 {} 张,分摊金额 {}",
|
||||
claim.getId(), flow.getBillId(), awrpSrcCode, settlements.size(), allocatedTotal);
|
||||
BfiBillSaveResultVO result = bfiService.pushClaimResult(param);
|
||||
applyClaimPushResult(claim, result);
|
||||
claimMapper.updateById(claim);
|
||||
log.info("调用BFI接口 收款认领结果推送成功 claimId:{},已回写金蝶状态:{}",
|
||||
claim.getId(), claim.getKingdeeBillStatus());
|
||||
return param;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 A301-3 推送结果并回写认领记录
|
||||
*
|
||||
* @param claim 认领记录
|
||||
* @param result 推送结果
|
||||
* 解析 A301-3 推送结果:全项成功返回金蝶收款单号,否则抛出汇总错误文本(由失败路径落库)
|
||||
*/
|
||||
private void applyClaimPushResult(ReceiptClaim claim, BfiBillSaveResultVO result) {
|
||||
private String extractClaimPushBillNo(BfiBillSaveResultVO result) {
|
||||
if (result == null || result.getResult() == null || result.getResult().isEmpty()) {
|
||||
throw new ServiceException("金蝶认领结果推送失败,返回结果为空");
|
||||
}
|
||||
@@ -708,12 +859,91 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
throw new ServiceException("金蝶认领结果推送失败"
|
||||
+ (errorText.isEmpty() ? "" : ":" + errorText));
|
||||
}
|
||||
String kingdeeBillNo = items.stream().map(BfiBillSaveResultItemVO::getNumber)
|
||||
return items.stream().map(BfiBillSaveResultItemVO::getNumber)
|
||||
.filter(Func::isNotEmpty).findFirst().orElse(null);
|
||||
claim.setKingdeeBillNo(kingdeeBillNo);
|
||||
claim.setKingdeeBillStatus(KingdeeBfiConstant.KINGDEE_STATUS_PUSHED);
|
||||
log.info("金蝶认领结果推送成功 claimId:{},金蝶收款单号:{}",
|
||||
claim.getId(), Func.isEmpty(kingdeeBillNo) ? "响应未返回" : kingdeeBillNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送成功:流水置已推送并回填金蝶收款单号,级联刷新名下未作废认领主单的冗余展示字段
|
||||
*/
|
||||
private ReceiptClaimPushResult applyClaimPushSuccess(KingdeeReceiptFlow flow, String kingdeeBillNo) {
|
||||
log.info("调用BFI接口 收款认领结果推送成功 flowId:{},金蝶收款单号:{}",
|
||||
flow.getId(), Func.isEmpty(kingdeeBillNo) ? "响应未返回" : kingdeeBillNo);
|
||||
flow.setPushStatus(KingdeeBfiConstant.RECEIPT_CLAIM_PUSH_PUSHED);
|
||||
flow.setPushedTime(LocalDateTime.now());
|
||||
flow.setKingdeeBillNo(kingdeeBillNo);
|
||||
flow.setKingdeeBillStatus(KingdeeBfiConstant.KINGDEE_STATUS_PUSHED);
|
||||
// 失败原因需显式置空(实体 update 忽略 null 字段,走 lambdaUpdate)
|
||||
this.update(Wrappers.<KingdeeReceiptFlow>lambdaUpdate()
|
||||
.eq(KingdeeReceiptFlow::getId, flow.getId())
|
||||
.set(KingdeeReceiptFlow::getPushStatus, flow.getPushStatus())
|
||||
.set(KingdeeReceiptFlow::getPushFailReason, null)
|
||||
.set(KingdeeReceiptFlow::getPushedTime, flow.getPushedTime())
|
||||
.set(KingdeeReceiptFlow::getAwrpSrcCode, flow.getAwrpSrcCode())
|
||||
.set(KingdeeReceiptFlow::getKingdeeBillNo, kingdeeBillNo)
|
||||
.set(KingdeeReceiptFlow::getKingdeeBillStatus, flow.getKingdeeBillStatus()));
|
||||
// 级联刷新名下未作废认领主单的冗余展示字段
|
||||
claimMapper.update(null, Wrappers.<ReceiptClaim>lambdaUpdate()
|
||||
.eq(ReceiptClaim::getReceiptFlowId, flow.getId())
|
||||
.eq(ReceiptClaim::getClaimStatus, CLAIMED)
|
||||
.set(ReceiptClaim::getKingdeeBillNo, kingdeeBillNo)
|
||||
.set(ReceiptClaim::getKingdeeBillStatus, KingdeeBfiConstant.KINGDEE_STATUS_PUSHED));
|
||||
record(flow.getId(), null, "push", "推送金蝶收款认领", CLAIMED, CLAIMED, flow.getClaimedAmount(),
|
||||
"金蝶收款单号:" + (Func.isEmpty(kingdeeBillNo) ? "响应未返回" : kingdeeBillNo));
|
||||
return ReceiptClaimPushResult.success(kingdeeBillNo);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送失败:流水置推送失败并记录原因,本地认领数据保留,可重新推送(重试复用同一来源编码)
|
||||
*/
|
||||
private ReceiptClaimPushResult applyClaimPushFailure(KingdeeReceiptFlow flow, Exception exception) {
|
||||
String reason = Func.isEmpty(exception.getMessage())
|
||||
? exception.getClass().getSimpleName() : exception.getMessage();
|
||||
reason = truncateText(reason, 1000);
|
||||
log.error("调用BFI接口 收款认领结果推送失败 flowId:{},billId:{},awrpSrcCode:{}",
|
||||
flow.getId(), flow.getBillId(), flow.getAwrpSrcCode(), exception);
|
||||
this.update(Wrappers.<KingdeeReceiptFlow>lambdaUpdate()
|
||||
.eq(KingdeeReceiptFlow::getId, flow.getId())
|
||||
.set(KingdeeReceiptFlow::getPushStatus, KingdeeBfiConstant.RECEIPT_CLAIM_PUSH_FAILED)
|
||||
.set(KingdeeReceiptFlow::getPushFailReason, reason)
|
||||
.set(KingdeeReceiptFlow::getAwrpSrcCode, flow.getAwrpSrcCode()));
|
||||
record(flow.getId(), null, "push", "推送金蝶收款认领", CLAIMED, CLAIMED, flow.getClaimedAmount(),
|
||||
"推送失败:" + truncateText(reason, 200));
|
||||
return ReceiptClaimPushResult.fail("金蝶认领结果推送失败:" + reason);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送来源编码:SKCL + 时间戳 + 流水id尾号6位(按流水生成,重试复用)
|
||||
*/
|
||||
private String buildAwrpSrcCode(Long flowId) {
|
||||
String time = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
|
||||
String suffix = String.valueOf(flowId);
|
||||
return "SKCL" + time + suffix.substring(Math.max(0, suffix.length() - 6));
|
||||
}
|
||||
|
||||
/**
|
||||
* A301-2 收款认领公告查询:按认领人姓名全量翻页,时间窗口取 Nacos 配置(默认近半年)。
|
||||
* 认领公告同步、手动推送对账与定时任务对账共用。
|
||||
*/
|
||||
private List<BfiClaimAnnounceVO> queryClaimAnnounces(String claimusers) {
|
||||
int months = bfiProperties.getClaimAnnounce().getMonths();
|
||||
LocalDateTime endDate = LocalDateTime.now();
|
||||
LocalDateTime startDate = endDate.minusMonths(months);
|
||||
long start = System.currentTimeMillis();
|
||||
log.info("调用BFI接口 收款认领公告查询开始 claimusers:{},时间窗口:{} ~ {}(近{}个月)",
|
||||
claimusers, startDate.format(BFI_DATE_TIME_FORMATTER), endDate.format(BFI_DATE_TIME_FORMATTER), months);
|
||||
BfiClaimAnnounceQueryDTO queryDTO = new BfiClaimAnnounceQueryDTO();
|
||||
queryDTO.setClaimusers(claimusers);
|
||||
queryDTO.setModifytimeS(startDate.format(BFI_DATE_TIME_FORMATTER));
|
||||
queryDTO.setModifytimeE(endDate.format(BFI_DATE_TIME_FORMATTER));
|
||||
queryDTO.setPageSize(SYNC_PAGE_SIZE);
|
||||
List<BfiClaimAnnounceVO> announces = BfiUtil.queryAllPages(pageNo -> {
|
||||
queryDTO.setPageNo(pageNo);
|
||||
return bfiService.queryClaimAnnounce(queryDTO);
|
||||
});
|
||||
log.info("调用BFI接口 收款认领公告查询结束 claimusers:{},耗时:{}ms,拉取 {} 条",
|
||||
claimusers, System.currentTimeMillis() - start, announces.size());
|
||||
return announces;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -779,11 +1009,14 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
}
|
||||
}
|
||||
|
||||
private String amountClaimStatus(BigDecimal claimedAmount, BigDecimal receiptAmount) {
|
||||
/**
|
||||
* 累计认领金额 → 流水认领状态:对照公告未认领金额判定(claimed 即「认领完成」,唯一可推送状态)
|
||||
*/
|
||||
private String amountClaimStatus(BigDecimal claimedAmount, BigDecimal unclaimedAmount) {
|
||||
if (claimedAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||
return UNCLAIMED;
|
||||
}
|
||||
return claimedAmount.compareTo(money(receiptAmount)) >= 0 ? CLAIMED : PARTIAL;
|
||||
return claimedAmount.compareTo(money(unclaimedAmount)) >= 0 ? CLAIMED : PARTIAL;
|
||||
}
|
||||
|
||||
private String amountStatus(BigDecimal claimedAmount, BigDecimal settlementAmount) {
|
||||
@@ -826,7 +1059,20 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
}
|
||||
}
|
||||
|
||||
private String truncateText(String value, int maxLength) {
|
||||
if (value == null || value.length() <= maxLength) {
|
||||
return value;
|
||||
}
|
||||
return value.substring(0, maxLength);
|
||||
}
|
||||
|
||||
private void record(Long flowId, Long claimId, String actionType, String actionName,
|
||||
String fromStatus, String toStatus, BigDecimal operationAmount, String content) {
|
||||
recordMapper.insert(buildRecord(flowId, claimId, actionType, actionName,
|
||||
fromStatus, toStatus, operationAmount, content));
|
||||
}
|
||||
|
||||
private ReceiptFlowRecord buildRecord(Long flowId, Long claimId, String actionType, String actionName,
|
||||
String fromStatus, String toStatus, BigDecimal operationAmount, String content) {
|
||||
ReceiptFlowRecord record = new ReceiptFlowRecord();
|
||||
record.setReceiptFlowId(flowId);
|
||||
@@ -838,6 +1084,6 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
||||
record.setOperationAmount(money(operationAmount));
|
||||
record.setOperatorName(Func.isEmpty(AuthUtil.getUserName()) ? "system" : AuthUtil.getUserName());
|
||||
record.setContent(content);
|
||||
recordMapper.insert(record);
|
||||
return record;
|
||||
}
|
||||
}
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for their own commercial purposes.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this license agreement and terms of use,
|
||||
* and you expressly agree to be bound by the terms of use of this software.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.task;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.transport.service.IReceiptFlowService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 收款认领推送金蝶定时任务(A301-3 收款认领结果推送)
|
||||
* <p>
|
||||
* 认领动作只在本地登记,本任务扫描「认领完成且未推送过」的收款流水自动推送金蝶,
|
||||
* 与收款流水页「推送金蝶」手动按钮共用同一服务方法(推送前按认领人重查 A301-2 最新公告对账)。
|
||||
* 单条流水推送失败独立消化、可重试,不影响同批其他流水。
|
||||
* <p>
|
||||
* 调度配置(Nacos thirdParty.bfi.receiptClaimPush.*):syncEnabled 开关、cron 表达式。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class ReceiptClaimPushTask {
|
||||
|
||||
private final IReceiptFlowService receiptFlowService;
|
||||
|
||||
/** 收款认领推送开关,默认关闭(联调稳定后开启) */
|
||||
@Value("${thirdParty.bfi.receiptClaimPush.syncEnabled:false}")
|
||||
private boolean syncEnabled;
|
||||
|
||||
/** 收款认领推送调度,默认每10分钟执行一次 */
|
||||
@Scheduled(cron = "${thirdParty.bfi.receiptClaimPush.cron:0 */10 * * * ?}")
|
||||
public void pushClaimToKingdee() {
|
||||
if (!syncEnabled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String summary = receiptFlowService.pushClaimByTask();
|
||||
log.info("定时推送收款认领:{}", summary);
|
||||
} catch (Exception exception) {
|
||||
log.error("定时推送收款认领异常", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
+15
-2
@@ -58,9 +58,22 @@ public class ReceiptFlowWrapper extends BaseEntityWrapper<KingdeeReceiptFlow, Re
|
||||
case "0" -> "未认领";
|
||||
default -> Objects.toString(entity.getKingdeeClaimStatus(), "");
|
||||
});
|
||||
BigDecimal receiptAmount = money(entity.getReceiptAmount());
|
||||
vo.setPushStatusName(switch (Objects.toString(entity.getPushStatus(), "")) {
|
||||
case "pushed" -> "已推送";
|
||||
case "failed" -> "推送失败";
|
||||
default -> "未推送";
|
||||
});
|
||||
vo.setKingdeeBillStatusName(switch (Objects.toString(entity.getKingdeeBillStatus(), "")) {
|
||||
case "pushed" -> "已推送";
|
||||
case "approved" -> "审核通过";
|
||||
case "received" -> "已收款";
|
||||
case "failed" -> "处理失败";
|
||||
default -> "未推送";
|
||||
});
|
||||
// 剩余可认领金额 = 公告未认领金额 − 累计认领金额(认领上限为公告口径)
|
||||
BigDecimal unclaimedAmount = money(entity.getUnclaimedAmount());
|
||||
BigDecimal claimedAmount = money(entity.getClaimedAmount());
|
||||
vo.setRemainingAmount(receiptAmount.subtract(claimedAmount).max(BigDecimal.ZERO));
|
||||
vo.setRemainingAmount(unclaimedAmount.subtract(claimedAmount).max(BigDecimal.ZERO));
|
||||
vo.setCreateUserName(UserCache.getUserRealName(entity.getCreateUser()));
|
||||
vo.setUpdateUserName(UserCache.getUserRealName(entity.getUpdateUser()));
|
||||
return vo;
|
||||
|
||||
Reference in New Issue
Block a user