feat(mcp): 新增 sale-receivable-orders 增量同步流 #30

Merged
shumeng merged 1 commits from feat/sale-receivable-orders-stream into main 2026-10-23 17:26:20 +08:00
3 changed files with 121 additions and 0 deletions
@@ -0,0 +1,79 @@
package me.iiv.agent.mcp.sync;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import me.iiv.agent.entity.m0.User;
import me.iiv.iivframework.beans.b1.SaleReceivableOrder;
import me.iiv.iivframework.mapper.m1.SaleReceivableOrderMapper;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* sale-receivable-orders 同步实体(m1.sale_receivable_order 应收单主表)。
* 与 repayment-details(回款明细)互补:应收单是整单口径,带收款状态与开票金额/状态。
* 金额列 legacy BigDecimal(元)→ 分;开票状态 0=未开票 1=全部开票 2=部分开票,原样透传。
* 行级权限:Token 绑定人时暂按 null 处理(网关同步使用服务身份,不带用户)。
*/
@Component
public class SaleReceivableOrdersSyncEntity extends AbstractSyncEntity implements SyncEntity {
public static final String NAME = "sale-receivable-orders";
final SaleReceivableOrderMapper saleReceivableOrderMapper;
public SaleReceivableOrdersSyncEntity(SaleReceivableOrderMapper saleReceivableOrderMapper) {
this.saleReceivableOrderMapper = saleReceivableOrderMapper;
}
@Override
public String name() {
return NAME;
}
@Override
public List<SyncRow> fetchPage(Long fromTs, long cutoffTs, Long cursorMt, Long cursorId,
int limit, User user) {
LambdaQueryWrapper<SaleReceivableOrder> wrapper = new LambdaQueryWrapper<>();
applyWindow(wrapper, fromTs, cutoffTs, cursorMt, cursorId);
applyOrderAndLimit(wrapper, limit);
List<SaleReceivableOrder> rows = saleReceivableOrderMapper.selectList(wrapper);
List<SyncRow> out = new ArrayList<>(rows.size());
for (SaleReceivableOrder row : rows) {
long mt = ChangesEngine.effectiveTs(row.getModifyTime(), row.getCreateTime());
out.add(new SyncRow(toItem(row, mt), mt, row.getId()));
}
return out;
}
private Map<String, Object> toItem(SaleReceivableOrder row, long mt) {
boolean deleted = row.getSoftDelete() != null && row.getSoftDelete() == 1;
Date effective = row.getModifyTime() != null ? row.getModifyTime() : row.getCreateTime();
String customerName = row.getCusterName() != null && !row.getCusterName().isBlank()
? row.getCusterName() : row.getJindieName();
return SyncChangeSchemas.toMap(new SyncChangeSchemas.ReceivableOrderChange(
stableId(row.getId()), row.getReceivableNo(), row.getOrderNo(), customerName,
stableId(row.getAgentId()), row.getAgentName(),
stableId(row.getPropertyId()), row.getPropertyName(),
row.getSaleCompany(),
ChangesEngine.iso(row.getOrderTime()), ChangesEngine.iso(row.getReceivableTime()),
toMinor(row.getReceivableMoney()), row.getReceivableState(),
toMinor(row.getInvoiceMoney()), toMinor(row.getNotInvoiceMoney()),
toMinor(row.getInvoicingProgressMoney()), row.getInvoiceState(),
row.getInvoiceName(),
ChangesEngine.iso(effective), mt, deleted ? ChangesEngine.iso(effective) : null));
}
private static String stableId(Long id) {
return id == null ? null : String.valueOf(id);
}
static Long toMinor(BigDecimal amount) {
return amount == null ? null
: amount.movePointRight(2).setScale(0, RoundingMode.HALF_UP).longValue();
}
}
@@ -59,6 +59,30 @@ final class SyncChangeSchemas {
@JsonProperty("deleted_at") String deletedAt) {
}
record ReceivableOrderChange(
@JsonProperty("receivable_order_id") String receivableOrderId,
@JsonProperty("receivable_no") String receivableNo,
@JsonProperty("order_no") String orderNo,
@JsonProperty("customer_name") String customerName,
@JsonProperty("agent_id") String agentId,
@JsonProperty("agent_name") String agentName,
@JsonProperty("property_id") String propertyId,
@JsonProperty("property_name") String propertyName,
@JsonProperty("sale_company") String saleCompany,
@JsonProperty("order_time") String orderTime,
@JsonProperty("receivable_time") String receivableTime,
@JsonProperty("receivable_money_minor") Long receivableMoneyMinor,
@JsonProperty("receivable_state") Integer receivableState,
@JsonProperty("invoice_money_minor") Long invoiceMoneyMinor,
@JsonProperty("not_invoice_money_minor") Long notInvoiceMoneyMinor,
@JsonProperty("invoicing_progress_money_minor") Long invoicingProgressMoneyMinor,
@JsonProperty("invoice_state") Integer invoiceState,
@JsonProperty("invoice_name") String invoiceName,
@JsonProperty("updated_at") String updatedAt,
long version,
@JsonProperty("deleted_at") String deletedAt) {
}
record ContractChange(
@JsonProperty("contract_id") String contractId,
@JsonProperty("contract_no") String contractNo,
@@ -14,6 +14,7 @@ import me.iiv.agent.mcp.sync.MallOrdersSyncEntity;
import me.iiv.agent.mcp.sync.PropertiesSyncEntity;
import me.iiv.agent.mcp.sync.RelocationOrdersSyncEntity;
import me.iiv.agent.mcp.sync.RepaymentDetailsSyncEntity;
import me.iiv.agent.mcp.sync.SaleReceivableOrdersSyncEntity;
import me.iiv.agent.mcp.sync.RepaymentTargetsSyncEntity;
import me.iiv.agent.mcp.sync.UsersSyncEntity;
import me.iiv.agent.mcp.sync.WithdrawOrdersSyncEntity;
@@ -141,6 +142,23 @@ public class ChangesTools implements ToolObject {
}
@XqTool(category = "sync", kind = "read")
@Tool(name = "sale-receivable-orders.changes.list",
description = "增量同步应收单主表(m1.sale_receivable_order,整单口径)。" + PROTOCOL_DESC
+ "items 字段:receivable_order_id/receivable_no/order_no/customer_name/"
+ "agent_id/agent_name/property_id/property_name/sale_company/"
+ "order_time/receivable_time(带时区)/receivable_money_minor(应收金额,分)/"
+ "receivable_state(0 未收款 1 全部收款 2 部分收款)/"
+ "invoice_money_minor(已开票金额,分)/not_invoice_money_minor(未开票金额,分)/"
+ "invoicing_progress_money_minor(开票中金额,分)/"
+ "invoice_state(0 未开票 1 全部开票 2 部分开票)/invoice_name/"
+ "updated_at/version/deleted_at。")
public Map<String, Object> saleReceivableOrdersChangesList(
@ToolParam(required = false, description = SYNC_TOKEN_DESC) String syncToken,
@ToolParam(required = false, description = CURSOR_DESC) String cursor,
@ToolParam(required = false, description = LIMIT_DESC) Integer limit) {
return changesEngine.execute(SaleReceivableOrdersSyncEntity.NAME, syncToken, cursor, limit);
}
@Tool(name = "repayment-details.changes.list", description = "增量同步客户回款明细(m1.saler_repayment_customer,客户×订单×周期)。" + PROTOCOL_DESC
+ "items 字段:repayment_detail_id/owner_user_id(销售稳定 user.id)/owner_name/"
+ "saler_agent_id/saler_agent_name/property_id/property_name/district_id/district_name/customer_name/"