Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fecbb2d95a | ||
|
|
76ac8fd66f | ||
|
|
8c5e139bfd | ||
|
|
6cbb29170a | ||
|
|
2d605bb996 | ||
|
|
fff1ae2d3b | ||
|
|
f19c2a5c7f |
@@ -0,0 +1,57 @@
|
||||
package me.iiv.agent.entity.m0;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
|
||||
/** 楼座(b0.building):设备安装的物理位置,含经纬度。 */
|
||||
@TableName("building")
|
||||
public class Building implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Long id;
|
||||
private String name;
|
||||
private Long propertyId;
|
||||
private String address;
|
||||
private String province;
|
||||
private String city;
|
||||
private String countyseat;
|
||||
private Double longitude;
|
||||
private Double latitude;
|
||||
|
||||
@TableField("soft_delete")
|
||||
private Integer softDelete;
|
||||
|
||||
@TableField("create_time")
|
||||
private Date createTime;
|
||||
|
||||
@TableField("modify_time")
|
||||
private Date modifyTime;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public Long getPropertyId() { return propertyId; }
|
||||
public void setPropertyId(Long propertyId) { this.propertyId = propertyId; }
|
||||
public String getAddress() { return address; }
|
||||
public void setAddress(String address) { this.address = address; }
|
||||
public String getProvince() { return province; }
|
||||
public void setProvince(String province) { this.province = province; }
|
||||
public String getCity() { return city; }
|
||||
public void setCity(String city) { this.city = city; }
|
||||
public String getCountyseat() { return countyseat; }
|
||||
public void setCountyseat(String countyseat) { this.countyseat = countyseat; }
|
||||
public Double getLongitude() { return longitude; }
|
||||
public void setLongitude(Double longitude) { this.longitude = longitude; }
|
||||
public Double getLatitude() { return latitude; }
|
||||
public void setLatitude(Double latitude) { this.latitude = latitude; }
|
||||
public Integer getSoftDelete() { return softDelete; }
|
||||
public void setSoftDelete(Integer softDelete) { this.softDelete = softDelete; }
|
||||
public Date getCreateTime() { return createTime; }
|
||||
public void setCreateTime(Date createTime) { this.createTime = createTime; }
|
||||
public Date getModifyTime() { return modifyTime; }
|
||||
public void setModifyTime(Date modifyTime) { this.modifyTime = modifyTime; }
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package me.iiv.agent.entity.m1;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 应收单主表(m1.sale_receivable_order)。
|
||||
* 金额列为 legacy BigDecimal(元),公开契约层统一换算为分(*_minor)。
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@TableName("sale_receivable_order")
|
||||
public class SaleReceivableOrder implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
|
||||
/** 创建时间 */
|
||||
private Date createTime;
|
||||
|
||||
/** 修改时间 */
|
||||
private Date modifyTime;
|
||||
|
||||
/** 数据状态 */
|
||||
private Integer softDelete;
|
||||
|
||||
/** 创建人(销售人员)ID */
|
||||
private Long createId;
|
||||
|
||||
/** 创建人(销售人员)姓名 */
|
||||
private String createName;
|
||||
|
||||
/** 应收单编号 */
|
||||
private String receivableNo;
|
||||
|
||||
/** 订单编号 */
|
||||
private String orderNo;
|
||||
|
||||
/** 客户名称 */
|
||||
private String custerName;
|
||||
|
||||
/** 代理ID */
|
||||
private Long agentId;
|
||||
|
||||
/** 物业ID */
|
||||
private Long propertyId;
|
||||
|
||||
/** 下单公司 */
|
||||
private String agentName;
|
||||
|
||||
/** 物业名称 */
|
||||
private String propertyName;
|
||||
|
||||
/** 销售公司 */
|
||||
private String saleCompany;
|
||||
|
||||
/** 订单时间 */
|
||||
private Date orderTime;
|
||||
|
||||
/** 应收单时间 */
|
||||
private Date receivableTime;
|
||||
|
||||
/** 已收款金额 */
|
||||
private BigDecimal receivableMoney;
|
||||
|
||||
/** 收款状态0:未收款;1:全部收款;2:部分收款 */
|
||||
private Integer receivableState;
|
||||
|
||||
/** 已开发票金额 */
|
||||
private BigDecimal invoiceMoney;
|
||||
|
||||
/** 开票状态0:未开票;1:全部开票;2:部分开票 */
|
||||
private Integer invoiceState;
|
||||
|
||||
/** 金蝶客户名称 */
|
||||
private String jindieName;
|
||||
|
||||
/** 开票名称 */
|
||||
private String invoiceName;
|
||||
|
||||
/** 未开发票金额 */
|
||||
private BigDecimal notInvoiceMoney;
|
||||
|
||||
/** 开票中金额 */
|
||||
private BigDecimal invoicingProgressMoney;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.iiv.agent.mapper.m0;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m0.Building;
|
||||
|
||||
public interface BuildingMapper extends BaseMapper<Building> {
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
package me.iiv.agent.mapper.m1;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m1.SaleReceivableOrder;
|
||||
|
||||
/**
|
||||
* 应收单 Mapper 接口
|
||||
*/
|
||||
public interface SaleReceivableOrderMapper extends BaseMapper<SaleReceivableOrder> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package me.iiv.agent.mcp.sync;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import me.iiv.agent.entity.m0.Building;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.mapper.m0.BuildingMapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/** buildings 同步实体(b0.building 楼座,含经纬度)。 */
|
||||
@Component
|
||||
public class BuildingsSyncEntity extends AbstractSyncEntity implements SyncEntity {
|
||||
|
||||
public static final String NAME = "buildings";
|
||||
|
||||
final BuildingMapper buildingMapper;
|
||||
|
||||
public BuildingsSyncEntity(BuildingMapper buildingMapper) {
|
||||
this.buildingMapper = buildingMapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SyncRow> fetchPage(Long fromTs, long cutoffTs, Long cursorMt, Long cursorId,
|
||||
int limit, User user) {
|
||||
LambdaQueryWrapper<Building> wrapper = new LambdaQueryWrapper<>();
|
||||
applyWindow(wrapper, fromTs, cutoffTs, cursorMt, cursorId);
|
||||
applyOrderAndLimit(wrapper, limit);
|
||||
List<Building> rows = buildingMapper.selectList(wrapper);
|
||||
List<SyncRow> out = new ArrayList<>(rows.size());
|
||||
for (Building 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(Building row, long mt) {
|
||||
boolean deleted = row.getSoftDelete() != null && row.getSoftDelete() == 1;
|
||||
Date effective = row.getModifyTime() != null ? row.getModifyTime() : row.getCreateTime();
|
||||
return SyncChangeSchemas.toMap(new SyncChangeSchemas.BuildingChange(
|
||||
stableId(row.getId()), stableId(row.getPropertyId()),
|
||||
row.getName(), row.getAddress(),
|
||||
row.getProvince(), row.getCity(), row.getCountyseat(),
|
||||
row.getLongitude(), row.getLatitude(),
|
||||
ChangesEngine.iso(effective), mt, deleted ? ChangesEngine.iso(effective) : null));
|
||||
}
|
||||
|
||||
private static String stableId(Long id) {
|
||||
return id == null ? null : String.valueOf(id);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -2,8 +2,8 @@ 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 me.iiv.agent.entity.m1.SaleReceivableOrder;
|
||||
import me.iiv.agent.mapper.m1.SaleReceivableOrderMapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@@ -85,6 +85,21 @@ final class SyncChangeSchemas {
|
||||
@JsonProperty("deleted_at") String deletedAt) {
|
||||
}
|
||||
|
||||
record BuildingChange(
|
||||
@JsonProperty("building_id") String buildingId,
|
||||
@JsonProperty("property_id") String propertyId,
|
||||
String name,
|
||||
String address,
|
||||
String province,
|
||||
String city,
|
||||
String countyseat,
|
||||
Double longitude,
|
||||
Double latitude,
|
||||
@JsonProperty("updated_at") String updatedAt,
|
||||
long version,
|
||||
@JsonProperty("deleted_at") String deletedAt) {
|
||||
}
|
||||
|
||||
record ContractChange(
|
||||
@JsonProperty("contract_id") String contractId,
|
||||
@JsonProperty("contract_no") String contractNo,
|
||||
|
||||
@@ -5,6 +5,7 @@ import me.iiv.agent.mcp.McpErrorCode;
|
||||
import me.iiv.agent.mcp.ToolCallException;
|
||||
import me.iiv.agent.mcp.XqTool;
|
||||
import me.iiv.agent.mcp.sync.BatteryFaultsSyncEntity;
|
||||
import me.iiv.agent.mcp.sync.BuildingsSyncEntity;
|
||||
import me.iiv.agent.mcp.sync.ChangesEngine;
|
||||
import me.iiv.agent.mcp.sync.ContractsSyncEntity;
|
||||
import me.iiv.agent.mcp.sync.DeviceLifecyclesSyncEntity;
|
||||
@@ -34,9 +35,9 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 增量同步工具(REQ-20260911-0013;P2 域扩展于 REQ-20260902-0002):
|
||||
* users/properties/contracts/repayment-targets/repayment-details/mall-orders/
|
||||
* users/properties/buildings/contracts/repayment-targets/repayment-details/mall-orders/
|
||||
* device-lifecycles/battery-faults/devices/withdraw-orders/relocation-orders/
|
||||
* install-orders 十二个 changes 工具,
|
||||
* install-orders 十三个 changes 工具,
|
||||
* 协议(snapshot/cursor/tombstone/sync_token)由 ChangesEngine 统一实现。
|
||||
* 数据域硬约束:本类不声明 tenant_id/scope 入参,数据域只能从 Token 推导;
|
||||
* GuardedToolCallback 对 *.changes.list 工具的原始入参做二次拦截,传 tenant_id/scope 一律拒绝。
|
||||
@@ -88,6 +89,17 @@ public class ChangesTools implements ToolObject {
|
||||
return changesEngine.execute(PropertiesSyncEntity.NAME, syncToken, cursor, limit);
|
||||
}
|
||||
|
||||
@XqTool(category = "sync", kind = "read")
|
||||
@Tool(name = "buildings.changes.list", description = "增量同步楼座主数据(b0.building,含经纬度)。" + PROTOCOL_DESC
|
||||
+ "items 字段:building_id/property_id/name/address/province/city/countyseat/"
|
||||
+ "longitude/latitude/updated_at/version/deleted_at。")
|
||||
public Map<String, Object> buildingsChangesList(
|
||||
@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(BuildingsSyncEntity.NAME, syncToken, cursor, limit);
|
||||
}
|
||||
|
||||
@XqTool(category = "sync", kind = "read")
|
||||
@Tool(name = "contracts.changes.list", description = "增量同步合同主数据(m2.contract_new)。" + PROTOCOL_DESC
|
||||
+ "items 字段:contract_id/contract_no/contract_name/status(PENDING/REJECTED/ACTIVE/EXPIRED/TERMINATED)/"
|
||||
|
||||
@@ -23,7 +23,7 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* 十二个 changes 工具的注册与路由(REQ-20260911-0013,P2 域扩展于 REQ-20260902-0002)。
|
||||
* 十三个 changes 工具的注册与路由(REQ-20260911-0013,P2 域扩展于 REQ-20260902-0002)。
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ChangesToolsTest {
|
||||
@@ -41,12 +41,13 @@ class ChangesToolsTest {
|
||||
callback -> callback.getToolDefinition().name(),
|
||||
callback -> callback.getToolDefinition().inputSchema()));
|
||||
|
||||
assertEquals(13, schemas.size());
|
||||
for (String name : new String[]{"users.changes.list", "properties.changes.list", "contracts.changes.list",
|
||||
assertEquals(15, schemas.size());
|
||||
for (String name : new String[]{"users.changes.list", "properties.changes.list", "buildings.changes.list",
|
||||
"contracts.changes.list",
|
||||
"repayment-targets.changes.list", "repayment-details.changes.list",
|
||||
"mall-orders.changes.list", "device-lifecycles.changes.list", "battery-faults.changes.list",
|
||||
"devices.changes.list", "withdraw-orders.changes.list", "relocation-orders.changes.list",
|
||||
"install-orders.changes.list"}) {
|
||||
"install-orders.changes.list", "sale-receivable-orders.changes.list"}) {
|
||||
assertTrue(schemas.containsKey(name), "缺少工具 " + name);
|
||||
String schema = schemas.get(name);
|
||||
assertTrue(schema.contains("syncToken"));
|
||||
@@ -99,6 +100,9 @@ class ChangesToolsTest {
|
||||
|
||||
tools.installOrdersChangesList(null, null, 80);
|
||||
verify(changesEngine).execute("install-orders", null, null, 80);
|
||||
|
||||
tools.saleReceivableOrdersChangesList("t8", "c8", null);
|
||||
verify(changesEngine).execute("sale-receivable-orders", "t8", "c8", null);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user