feat(mcp): add install order query tools (REQ-20260916-0009)
This commit is contained in:
committed by
my_local_kimi_dev2
parent
6c8fe2e3bb
commit
936590a3e3
@@ -87,6 +87,8 @@ xiaoqu-agent-service/
|
||||
| `warehouse.todo.list.query` | warehouse | read | 当前用户仓库待办统一查询(REQ-20260915-0008),合并老系统出入库/调拨三个待办接口;流程视图 user_id 过滤,不按代理商过滤;调拨派生出入库单带 derivedFrom 标记 |
|
||||
| `delivery.order.list.query` | workorder | read | 送货工单分页查询;申请/完成日期独立过滤,代理、代管物业及流程参与人权限取并集 |
|
||||
| `delivery.order.detail.query` | workorder | read | 送货工单详情;返回物流、配发物品、发货/签收附件、流程及关联单据 |
|
||||
| `install.order.list.query` | workorder | read | 装机工单分页查询;仅保留申请日期范围,返回状态文案与安装数量汇总 |
|
||||
| `install.order.detail.query` | workorder | read | 装机工单详情;返回明细、作业人员、地址、商城订单、流程、配发 SN、出入库关联及凭证 |
|
||||
|
||||
分页统一规范:page 从 1 开始,rows 默认 20、上限 100,超出截断为 100 并在返回 `notice` 字段提示。
|
||||
|
||||
|
||||
@@ -615,6 +615,39 @@
|
||||
|
||||
---
|
||||
|
||||
## 22. install.order.list.query
|
||||
|
||||
分页查询装机工单,对应旧系统 `POST /installOrder/pageList`。
|
||||
|
||||
- 分类:workorder | 类型:读
|
||||
- 数据来源:m2 `install_order`、`install_order_details`。
|
||||
- 入参:`installOrderNo`、`mallOrderNo`、`scheduleTransferOrder`、`deliveryOrderNo`、
|
||||
`pickupOrderNo`、`installType`、`orderState`、`agentId`、`districtName`、`applyUserName`、
|
||||
`maintenanceUserName`、`cooperateUserName`、`applyTimeFrom/applyTimeTo`、`page/rows`。
|
||||
- 时间范围首版只保留申请日期,格式 `yyyy-MM-dd`,结束日期含当天;不接受自由排序,固定
|
||||
`apply_time DESC, id DESC`。
|
||||
- 行级权限:总部看全部;普通代理可看自己、授权代管公司、授权代管物业及本人参与的工单,
|
||||
不包含直属下级代理;销售非负责人只看自己创建及授权代管公司名下物业的工单。
|
||||
- 返回 `total/page/rows/records`;每条记录含 `installTypeText`、`orderStateText` 和
|
||||
`totalInstallCount`(本单设备明细安装数量合计)。
|
||||
|
||||
## 23. install.order.detail.query
|
||||
|
||||
按 `id` 或 `installOrderNo` 查询装机工单详情,两者至少传一个;同时传入时必须匹配同一条可见记录。
|
||||
`includeProcess` 默认 true,可关闭流程聚合。
|
||||
|
||||
- 行级权限与列表完全一致;不存在与无权访问统一返回 `record=null`,防止枚举。
|
||||
- `record/details/totalInstallCount`:主单、设备明细及安装数量合计。
|
||||
- `maintenanceUsers/cooperateUsers`:当前装机运维和协同人员。
|
||||
- `address`:m1 收货地址;`mallOrder`:m1 商城订单及商品明细聚合。
|
||||
- `processes`:已办理且非抄送的流程,按操作时间、ID 正序;附流程人员、配发物品/SN、
|
||||
出入库关联单及图片凭证 URL。
|
||||
- `processLimit` 默认 100、最大 500;同时返回 `processTotal/processTruncated`。
|
||||
|
||||
详细设计见 `install-order-mcp-tool-design.md`。
|
||||
|
||||
---
|
||||
|
||||
## 附录:在线查看工具清单
|
||||
|
||||
```bash
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
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;
|
||||
|
||||
/** 商城订单物料(m1,只读)。 */
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@TableName("mall_order_materials")
|
||||
public class MallOrderMaterial implements Serializable {
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Integer softDelete;
|
||||
private String spuNo;
|
||||
private String skuNo;
|
||||
private String skuLable;
|
||||
private String skuDesc;
|
||||
private String spuName;
|
||||
private String orderNo;
|
||||
private Long mallOrderDetailsId;
|
||||
private Integer warehouseState;
|
||||
private Long warehouseOutId;
|
||||
private Long materielDefinitionId;
|
||||
private Long parentNodeId;
|
||||
private String materialNo;
|
||||
private String materielFullName;
|
||||
private String materielSort;
|
||||
private Long materielIndexId;
|
||||
private String specifications;
|
||||
private Integer outCount;
|
||||
private Integer notOutCount;
|
||||
private Integer beenOutCount;
|
||||
private BigDecimal price;
|
||||
private Integer freeFlag;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package me.iiv.agent.entity.m2;
|
||||
|
||||
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.util.Date;
|
||||
|
||||
/** 装机工单(m2,只读)。 */
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@TableName("install_order")
|
||||
public class InstallOrder implements Serializable {
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Long createId;
|
||||
private Date createTime;
|
||||
private Long modifyId;
|
||||
private Date modifyTime;
|
||||
private Integer softDelete;
|
||||
private Long applyAgentId;
|
||||
private String applyAgentName;
|
||||
private Integer installType;
|
||||
private String installOrderNo;
|
||||
private Long agentId;
|
||||
private Long propertyId;
|
||||
private Long districtId;
|
||||
private String agentName;
|
||||
private String propertyName;
|
||||
private String districtName;
|
||||
private String hopeInstallDate;
|
||||
private String trueInstallDate;
|
||||
private String customerContact;
|
||||
private String contactMobile;
|
||||
private String projectAddress;
|
||||
private String projectMemo;
|
||||
private Integer orderState;
|
||||
private Long applyUserId;
|
||||
private String applyUserName;
|
||||
private Date applyTime;
|
||||
private Date auditTime;
|
||||
private Date dispatchTime;
|
||||
private Date allocationTime;
|
||||
private Date signatureTime;
|
||||
private Date pickupTime;
|
||||
private Date warehousingTime;
|
||||
private Date finishDate;
|
||||
private String auditUserName;
|
||||
private String dispatchUserName;
|
||||
private String maintenanceUserName;
|
||||
private String cooperateUserName;
|
||||
private String scheduleUserName;
|
||||
private String scheduleTransferOrder;
|
||||
private String deliveryOrderNo;
|
||||
private String pickupOrderNo;
|
||||
private String mallOrderNo;
|
||||
private Long addressId;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.iiv.agent.entity.m2;
|
||||
|
||||
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;
|
||||
|
||||
/** 装机工单设备明细(m2,只读)。 */
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@TableName("install_order_details")
|
||||
public class InstallOrderDetail implements Serializable {
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Integer softDelete;
|
||||
private String installOrderNo;
|
||||
private Integer deviceTypeId;
|
||||
private String deviceTypeName;
|
||||
private Integer installCount;
|
||||
private Integer installPositionId;
|
||||
private String installPositionName;
|
||||
private Integer installMaterialId;
|
||||
private String installMaterialName;
|
||||
private Integer consumablesId;
|
||||
private String consumablesName;
|
||||
private String consumablesMemo;
|
||||
private Integer consumablesCount;
|
||||
private Integer deviceRemoveFlag;
|
||||
private Integer useElectricityFlag;
|
||||
private Integer devicePayFlag;
|
||||
private Integer consumablesPayFlag;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package me.iiv.agent.entity.m2;
|
||||
|
||||
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.util.Date;
|
||||
|
||||
/** 装机工单流程(m2,只读)。 */
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@TableName("install_order_process")
|
||||
public class InstallOrderProcess implements Serializable {
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Integer softDelete;
|
||||
private String installOrderNo;
|
||||
private Long userId;
|
||||
private String userName;
|
||||
private Date operationTime;
|
||||
private Integer scheduleState;
|
||||
private Integer auditState;
|
||||
private Integer operationTheme;
|
||||
private Integer operationType;
|
||||
private String memo;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package me.iiv.agent.entity.m2;
|
||||
|
||||
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;
|
||||
|
||||
/** 装机运维/协同人员(m2,只读)。 */
|
||||
@Getter
|
||||
@Setter
|
||||
@ToString
|
||||
@TableName("install_order_user")
|
||||
public class InstallOrderUser implements Serializable {
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Long id;
|
||||
private Integer softDelete;
|
||||
private Long installOrderProcessId;
|
||||
private String installOrderNo;
|
||||
private Long userId;
|
||||
private String userName;
|
||||
private Integer userType;
|
||||
private Integer type;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.iiv.agent.mapper.m1;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m1.MallOrderMaterial;
|
||||
|
||||
public interface MallOrderMaterialMapper extends BaseMapper<MallOrderMaterial> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package me.iiv.agent.mapper.m2;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m2.InstallOrderDetail;
|
||||
|
||||
public interface InstallOrderDetailMapper extends BaseMapper<InstallOrderDetail> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.iiv.agent.mapper.m2;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m2.InstallOrder;
|
||||
|
||||
public interface InstallOrderMapper extends BaseMapper<InstallOrder> {
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package me.iiv.agent.mapper.m2;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m2.InstallOrderProcess;
|
||||
|
||||
public interface InstallOrderProcessMapper extends BaseMapper<InstallOrderProcess> {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package me.iiv.agent.mapper.m2;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import me.iiv.agent.entity.m2.InstallOrderUser;
|
||||
|
||||
public interface InstallOrderUserMapper extends BaseMapper<InstallOrderUser> {
|
||||
}
|
||||
@@ -0,0 +1,605 @@
|
||||
package me.iiv.agent.mcp.tool;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.entity.m1.MallGoodsOrder;
|
||||
import me.iiv.agent.entity.m1.MallLogisticsPic;
|
||||
import me.iiv.agent.entity.m1.MallOrderDetails;
|
||||
import me.iiv.agent.entity.m1.MallOrderMaterial;
|
||||
import me.iiv.agent.entity.m1.MallShippingAddress;
|
||||
import me.iiv.agent.entity.m2.InstallDistributeDetail;
|
||||
import me.iiv.agent.entity.m2.InstallOrder;
|
||||
import me.iiv.agent.entity.m2.InstallOrderDetail;
|
||||
import me.iiv.agent.entity.m2.InstallOrderProcess;
|
||||
import me.iiv.agent.entity.m2.InstallOrderUser;
|
||||
import me.iiv.agent.entity.m2.InstallToOutWarehouse;
|
||||
import me.iiv.agent.entity.m2.WarehouseInOutPic;
|
||||
import me.iiv.agent.mapper.m1.MallGoodsOrderMapper;
|
||||
import me.iiv.agent.mapper.m1.MallLogisticsPicMapper;
|
||||
import me.iiv.agent.mapper.m1.MallOrderDetailsMapper;
|
||||
import me.iiv.agent.mapper.m1.MallOrderMaterialMapper;
|
||||
import me.iiv.agent.mapper.m1.MallShippingAddressMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallDistributeDetailMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallOrderDetailMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallOrderMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallOrderProcessMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallOrderUserMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallToOutWarehouseMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseInOutPicMapper;
|
||||
import me.iiv.agent.mcp.ToolObject;
|
||||
import me.iiv.agent.mcp.XqTool;
|
||||
import me.iiv.agent.permission.PermissionService;
|
||||
import me.iiv.agent.security.AuthContextHolder;
|
||||
import org.springframework.ai.tool.annotation.Tool;
|
||||
import org.springframework.ai.tool.annotation.ToolParam;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.time.format.ResolverStyle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/** 装机工单查询工具,对齐老系统 /installOrder/pageList 与 /lookDetails。 */
|
||||
@Component
|
||||
public class InstallOrderTools implements ToolObject {
|
||||
|
||||
private static final int DEFAULT_ROWS = 20;
|
||||
private static final int MAX_ROWS = 100;
|
||||
private static final int DEFAULT_PROCESS_LIMIT = 100;
|
||||
private static final int MAX_PROCESS_LIMIT = 500;
|
||||
private static final ZoneId BUSINESS_ZONE = ZoneId.of("Asia/Shanghai");
|
||||
private static final DateTimeFormatter DATE_FORMATTER =
|
||||
DateTimeFormatter.ISO_LOCAL_DATE.withResolverStyle(ResolverStyle.STRICT);
|
||||
private static final DateTimeFormatter TIME_FORMATTER =
|
||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(BUSINESS_ZONE);
|
||||
|
||||
private static final Map<Integer, String> ORDER_STATE_TEXT = Map.of(
|
||||
0, "待审核", 1, "被驳回", 2, "调度中", 3, "待出库", 4, "待送货",
|
||||
5, "作业中", 6, "待取回", 7, "已结束", 8, "已取消");
|
||||
private static final Map<Integer, String> INSTALL_TYPE_TEXT = Map.of(
|
||||
0, "正式装机", 1, "测试装机", 2, "销售装机");
|
||||
private static final Map<Integer, String> SCHEDULE_STATE_TEXT = Map.of(
|
||||
0, "无需待办", 1, "待办", 2, "已办");
|
||||
private static final Map<Integer, String> OPERATION_THEME_TEXT = Map.ofEntries(
|
||||
Map.entry(0, "装机申请"), Map.entry(1, "审核通过"), Map.entry(2, "运营调度"),
|
||||
Map.entry(3, "配发协同"), Map.entry(4, "送货"), Map.entry(5, "收货确认"),
|
||||
Map.entry(6, "取货"), Map.entry(7, "入库"), Map.entry(8, "结单"),
|
||||
Map.entry(9, "驳回修改"), Map.entry(10, "审核驳回"), Map.entry(11, "已取消"),
|
||||
Map.entry(12, "抄送"), Map.entry(13, "装机协同"), Map.entry(14, "取货结单"));
|
||||
|
||||
final InstallOrderMapper orderMapper;
|
||||
final InstallOrderDetailMapper detailMapper;
|
||||
final InstallOrderProcessMapper processMapper;
|
||||
final InstallOrderUserMapper userMapper;
|
||||
final InstallDistributeDetailMapper distributeMapper;
|
||||
final InstallToOutWarehouseMapper warehouseRelationMapper;
|
||||
final WarehouseInOutPicMapper picMapper;
|
||||
final MallGoodsOrderMapper mallOrderMapper;
|
||||
final MallOrderDetailsMapper mallDetailMapper;
|
||||
final MallOrderMaterialMapper mallMaterialMapper;
|
||||
final MallShippingAddressMapper addressMapper;
|
||||
final MallLogisticsPicMapper mallPicMapper;
|
||||
final PermissionService permissionService;
|
||||
|
||||
public InstallOrderTools(InstallOrderMapper orderMapper,
|
||||
InstallOrderDetailMapper detailMapper,
|
||||
InstallOrderProcessMapper processMapper,
|
||||
InstallOrderUserMapper userMapper,
|
||||
InstallDistributeDetailMapper distributeMapper,
|
||||
InstallToOutWarehouseMapper warehouseRelationMapper,
|
||||
WarehouseInOutPicMapper picMapper,
|
||||
MallGoodsOrderMapper mallOrderMapper,
|
||||
MallOrderDetailsMapper mallDetailMapper,
|
||||
MallOrderMaterialMapper mallMaterialMapper,
|
||||
MallShippingAddressMapper addressMapper,
|
||||
MallLogisticsPicMapper mallPicMapper,
|
||||
PermissionService permissionService) {
|
||||
this.orderMapper = orderMapper;
|
||||
this.detailMapper = detailMapper;
|
||||
this.processMapper = processMapper;
|
||||
this.userMapper = userMapper;
|
||||
this.distributeMapper = distributeMapper;
|
||||
this.warehouseRelationMapper = warehouseRelationMapper;
|
||||
this.picMapper = picMapper;
|
||||
this.mallOrderMapper = mallOrderMapper;
|
||||
this.mallDetailMapper = mallDetailMapper;
|
||||
this.mallMaterialMapper = mallMaterialMapper;
|
||||
this.addressMapper = addressMapper;
|
||||
this.mallPicMapper = mallPicMapper;
|
||||
this.permissionService = permissionService;
|
||||
}
|
||||
|
||||
@XqTool(category = "workorder", kind = "read")
|
||||
@Tool(name = "install.order.list.query", description = "分页查询当前用户可见的装机工单,对齐老系统 /installOrder/pageList。"
|
||||
+ "支持装机单、商城订单、待办划转单、送货单、取货单模糊查询,类型、状态、公司精确查询,以及项目/人员模糊查询。"
|
||||
+ "申请日期格式 yyyy-MM-dd,结束日期含当天。page 默认1,rows 默认20、上限100,固定按申请时间和 id 倒序。"
|
||||
+ "返回 orderStateText 与设备明细安装数量汇总 totalInstallCount。")
|
||||
public Map<String, Object> installOrderListQuery(
|
||||
@ToolParam(required = false, description = "装机工单号,模糊匹配") String installOrderNo,
|
||||
@ToolParam(required = false, description = "商城订单号,模糊匹配") String mallOrderNo,
|
||||
@ToolParam(required = false, description = "待办划转单号,模糊匹配") String scheduleTransferOrder,
|
||||
@ToolParam(required = false, description = "送货工单号,模糊匹配") String deliveryOrderNo,
|
||||
@ToolParam(required = false, description = "取货工单号,模糊匹配") String pickupOrderNo,
|
||||
@ToolParam(required = false, description = "装机类型:0正式装机 1测试装机 2销售装机") Integer installType,
|
||||
@ToolParam(required = false, description = "装机工单状态:0待审核 1被驳回 2调度中 3待出库 4待送货 5作业中 6待取回 7已结束 8已取消") Integer orderState,
|
||||
@ToolParam(required = false, description = "所属公司 id,精确匹配") Long agentId,
|
||||
@ToolParam(required = false, description = "项目名称,模糊匹配") String districtName,
|
||||
@ToolParam(required = false, description = "申请人,模糊匹配") String applyUserName,
|
||||
@ToolParam(required = false, description = "运维人员,模糊匹配") String maintenanceUserName,
|
||||
@ToolParam(required = false, description = "协同人员,模糊匹配") String cooperateUserName,
|
||||
@ToolParam(required = false, description = "申请日期起,yyyy-MM-dd") String applyTimeFrom,
|
||||
@ToolParam(required = false, description = "申请日期止,yyyy-MM-dd,含当天") String applyTimeTo,
|
||||
@ToolParam(required = false, description = "页码,从1开始,默认1") Integer page,
|
||||
@ToolParam(required = false, description = "每页条数,默认20,上限100") Integer rows) {
|
||||
LocalDate start = parseDate(applyTimeFrom, "applyTimeFrom");
|
||||
LocalDate end = parseDate(applyTimeTo, "applyTimeTo");
|
||||
if (start != null && end != null && start.isAfter(end)) {
|
||||
throw new IllegalArgumentException("applyTimeFrom 不能晚于 applyTimeTo");
|
||||
}
|
||||
int pageNo = page == null || page < 1 ? 1 : page;
|
||||
boolean truncated = rows != null && rows > MAX_ROWS;
|
||||
int pageSize = rows == null || rows < 1 ? DEFAULT_ROWS : Math.min(rows, MAX_ROWS);
|
||||
|
||||
LambdaQueryWrapper<InstallOrder> wrapper = new LambdaQueryWrapper<InstallOrder>()
|
||||
.eq(InstallOrder::getSoftDelete, 0);
|
||||
like(wrapper, InstallOrder::getInstallOrderNo, installOrderNo);
|
||||
like(wrapper, InstallOrder::getMallOrderNo, mallOrderNo);
|
||||
like(wrapper, InstallOrder::getScheduleTransferOrder, scheduleTransferOrder);
|
||||
like(wrapper, InstallOrder::getDeliveryOrderNo, deliveryOrderNo);
|
||||
like(wrapper, InstallOrder::getPickupOrderNo, pickupOrderNo);
|
||||
like(wrapper, InstallOrder::getDistrictName, districtName);
|
||||
like(wrapper, InstallOrder::getApplyUserName, applyUserName);
|
||||
like(wrapper, InstallOrder::getMaintenanceUserName, maintenanceUserName);
|
||||
like(wrapper, InstallOrder::getCooperateUserName, cooperateUserName);
|
||||
wrapper.eq(installType != null, InstallOrder::getInstallType, installType)
|
||||
.eq(orderState != null, InstallOrder::getOrderState, orderState)
|
||||
.eq(agentId != null, InstallOrder::getAgentId, agentId)
|
||||
.ge(start != null, InstallOrder::getApplyTime, start == null ? null : startOfDay(start))
|
||||
.lt(end != null, InstallOrder::getApplyTime, end == null ? null : nextDayStart(end));
|
||||
permissionService.applyInstallOrderRowFilter(wrapper, AuthContextHolder.get().getUser());
|
||||
|
||||
Long total = orderMapper.selectCount(wrapper);
|
||||
wrapper.orderByDesc(InstallOrder::getApplyTime).orderByDesc(InstallOrder::getId)
|
||||
.last("LIMIT " + (long) (pageNo - 1) * pageSize + "," + pageSize);
|
||||
List<InstallOrder> orders = orderMapper.selectList(wrapper);
|
||||
Map<String, Integer> installCounts = totalInstallCounts(orders);
|
||||
List<Map<String, Object>> records = orders.stream()
|
||||
.map(order -> {
|
||||
Map<String, Object> record = record(order);
|
||||
record.put("totalInstallCount", installCounts.getOrDefault(order.getInstallOrderNo(), 0));
|
||||
return record;
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("total", total);
|
||||
result.put("page", pageNo);
|
||||
result.put("rows", pageSize);
|
||||
if (truncated) result.put("notice", "rows 超过上限 100,已截断为 100");
|
||||
result.put("records", records);
|
||||
return result;
|
||||
}
|
||||
|
||||
@XqTool(category = "workorder", kind = "read")
|
||||
@Tool(name = "install.order.detail.query", description = "按 id 或装机工单号查询装机工单详情。"
|
||||
+ "行级权限与列表一致;不存在或无权访问统一返回 record=null。"
|
||||
+ "返回主单、设备明细、运维/协同人员、收货地址、商城订单聚合和已办理流程;"
|
||||
+ "流程包含配发 SN、关联出入库单及图片凭证 URL。processLimit 默认100、上限500。")
|
||||
public Map<String, Object> installOrderDetailQuery(
|
||||
@ToolParam(required = false, description = "装机工单主键 id,与 installOrderNo 至少传一个") Long id,
|
||||
@ToolParam(required = false, description = "装机工单号,与 id 至少传一个") String installOrderNo,
|
||||
@ToolParam(required = false, description = "是否返回流程记录,默认 true") Boolean includeProcess,
|
||||
@ToolParam(required = false, description = "流程返回上限,默认100、最大500") Integer processLimit) {
|
||||
if ((id == null || id <= 0) && !StringUtils.hasText(installOrderNo)) {
|
||||
throw new IllegalArgumentException("id、installOrderNo 至少传一个");
|
||||
}
|
||||
if (id != null && id <= 0) throw new IllegalArgumentException("id 必须为正整数");
|
||||
int limit = processLimit == null || processLimit < 1
|
||||
? DEFAULT_PROCESS_LIMIT : Math.min(processLimit, MAX_PROCESS_LIMIT);
|
||||
boolean include = includeProcess == null || includeProcess;
|
||||
|
||||
LambdaQueryWrapper<InstallOrder> wrapper = new LambdaQueryWrapper<InstallOrder>()
|
||||
.eq(InstallOrder::getSoftDelete, 0)
|
||||
.eq(id != null, InstallOrder::getId, id)
|
||||
.eq(StringUtils.hasText(installOrderNo), InstallOrder::getInstallOrderNo,
|
||||
StringUtils.hasText(installOrderNo) ? installOrderNo.trim() : installOrderNo);
|
||||
permissionService.applyInstallOrderRowFilter(wrapper, AuthContextHolder.get().getUser());
|
||||
List<InstallOrder> visible = orderMapper.selectList(wrapper.orderByDesc(InstallOrder::getId).last("LIMIT 1"));
|
||||
if (visible.isEmpty()) {
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("record", null);
|
||||
result.put("notice", "工单不存在或无权查看");
|
||||
return result;
|
||||
}
|
||||
|
||||
InstallOrder order = visible.get(0);
|
||||
String no = order.getInstallOrderNo();
|
||||
List<InstallOrderDetail> details = detailMapper.selectList(new LambdaQueryWrapper<InstallOrderDetail>()
|
||||
.eq(InstallOrderDetail::getSoftDelete, 0)
|
||||
.eq(InstallOrderDetail::getInstallOrderNo, no)
|
||||
.orderByAsc(InstallOrderDetail::getId));
|
||||
List<InstallOrderUser> users = userMapper.selectList(new LambdaQueryWrapper<InstallOrderUser>()
|
||||
.eq(InstallOrderUser::getSoftDelete, 0)
|
||||
.eq(InstallOrderUser::getInstallOrderNo, no)
|
||||
.eq(InstallOrderUser::getType, 0)
|
||||
.orderByAsc(InstallOrderUser::getId));
|
||||
Long processTotal = include ? processMapper.selectCount(processBase(no)) : 0L;
|
||||
List<InstallOrderProcess> processes = include ? processMapper.selectList(processBase(no)
|
||||
.orderByAsc(InstallOrderProcess::getOperationTime)
|
||||
.orderByAsc(InstallOrderProcess::getId).last("LIMIT " + limit)) : List.of();
|
||||
|
||||
Map<Long, List<InstallOrderUser>> processUsers = processUsers(processes);
|
||||
List<InstallDistributeDetail> distributes = include ? distributeMapper.selectList(
|
||||
new LambdaQueryWrapper<InstallDistributeDetail>()
|
||||
.eq(InstallDistributeDetail::getSoftDelete, 0)
|
||||
.eq(InstallDistributeDetail::getInstallOrderNo, no)
|
||||
.orderByAsc(InstallDistributeDetail::getId)) : List.of();
|
||||
List<InstallToOutWarehouse> warehouseRelations = include ? warehouseRelationMapper.selectList(
|
||||
new LambdaQueryWrapper<InstallToOutWarehouse>()
|
||||
.eq(InstallToOutWarehouse::getSoftDelete, 0)
|
||||
.eq(InstallToOutWarehouse::getInstallOrderNo, no)
|
||||
.orderByAsc(InstallToOutWarehouse::getId)) : List.of();
|
||||
Map<Long, List<WarehouseInOutPic>> pics = processPics(processes);
|
||||
Map<Long, List<MallLogisticsPic>> mallPics = mallProcessPics(processes);
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("record", record(order));
|
||||
result.put("details", details.stream().map(this::detailRecord).collect(Collectors.toList()));
|
||||
result.put("totalInstallCount", details.stream().map(InstallOrderDetail::getInstallCount)
|
||||
.filter(Objects::nonNull).mapToInt(Integer::intValue).sum());
|
||||
result.put("maintenanceUsers", personnel(users, 0));
|
||||
result.put("cooperateUsers", personnel(users, 1));
|
||||
result.put("addressInfo", address(order));
|
||||
result.put("mallOrder", mallOrder(order));
|
||||
result.put("processes", processes.stream().map(process -> processRecord(process,
|
||||
processUsers.getOrDefault(process.getId(), List.of()), distributes,
|
||||
warehouseRelations, pics.getOrDefault(process.getId(), List.of()),
|
||||
mallPics.getOrDefault(process.getId(), List.of()))).collect(Collectors.toList()));
|
||||
result.put("processTotal", processTotal);
|
||||
result.put("processTruncated", processTotal != null && processTotal > processes.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
private LambdaQueryWrapper<InstallOrderProcess> processBase(String no) {
|
||||
return new LambdaQueryWrapper<InstallOrderProcess>()
|
||||
.eq(InstallOrderProcess::getSoftDelete, 0)
|
||||
.eq(InstallOrderProcess::getInstallOrderNo, no)
|
||||
.eq(InstallOrderProcess::getScheduleState, 2)
|
||||
.ne(InstallOrderProcess::getOperationTheme, 12);
|
||||
}
|
||||
|
||||
private Map<String, Integer> totalInstallCounts(List<InstallOrder> orders) {
|
||||
if (orders.isEmpty()) return Map.of();
|
||||
List<String> orderNos = orders.stream().map(InstallOrder::getInstallOrderNo)
|
||||
.filter(StringUtils::hasText).collect(Collectors.toList());
|
||||
if (orderNos.isEmpty()) return Map.of();
|
||||
return detailMapper.selectList(new LambdaQueryWrapper<InstallOrderDetail>()
|
||||
.eq(InstallOrderDetail::getSoftDelete, 0)
|
||||
.in(InstallOrderDetail::getInstallOrderNo, orderNos))
|
||||
.stream().collect(Collectors.groupingBy(InstallOrderDetail::getInstallOrderNo,
|
||||
LinkedHashMap::new, Collectors.summingInt(row ->
|
||||
row.getInstallCount() == null ? 0 : row.getInstallCount())));
|
||||
}
|
||||
|
||||
private Map<Long, List<InstallOrderUser>> processUsers(List<InstallOrderProcess> processes) {
|
||||
if (processes.isEmpty()) return Map.of();
|
||||
List<Long> ids = processes.stream().map(InstallOrderProcess::getId).collect(Collectors.toList());
|
||||
return userMapper.selectList(new LambdaQueryWrapper<InstallOrderUser>()
|
||||
.eq(InstallOrderUser::getSoftDelete, 0)
|
||||
.in(InstallOrderUser::getInstallOrderProcessId, ids)
|
||||
.orderByAsc(InstallOrderUser::getId)).stream()
|
||||
.collect(Collectors.groupingBy(InstallOrderUser::getInstallOrderProcessId,
|
||||
LinkedHashMap::new, Collectors.toList()));
|
||||
}
|
||||
|
||||
private Map<Long, List<WarehouseInOutPic>> processPics(List<InstallOrderProcess> processes) {
|
||||
if (processes.isEmpty()) return Map.of();
|
||||
List<Long> ids = processes.stream().map(InstallOrderProcess::getId).collect(Collectors.toList());
|
||||
return picMapper.selectList(new LambdaQueryWrapper<WarehouseInOutPic>()
|
||||
.eq(WarehouseInOutPic::getSoftDelete, 0)
|
||||
.in(WarehouseInOutPic::getAggregateId, ids)
|
||||
.orderByAsc(WarehouseInOutPic::getId)).stream()
|
||||
.collect(Collectors.groupingBy(WarehouseInOutPic::getAggregateId,
|
||||
LinkedHashMap::new, Collectors.toList()));
|
||||
}
|
||||
|
||||
private Map<Long, List<MallLogisticsPic>> mallProcessPics(List<InstallOrderProcess> processes) {
|
||||
if (processes.isEmpty()) return Map.of();
|
||||
Map<String, Long> processIds = processes.stream().collect(Collectors.toMap(
|
||||
process -> process.getId().toString(), InstallOrderProcess::getId));
|
||||
return mallPicMapper.selectList(new LambdaQueryWrapper<MallLogisticsPic>()
|
||||
.eq(MallLogisticsPic::getSoftDelete, 0)
|
||||
.in(MallLogisticsPic::getOrderNo, processIds.keySet())
|
||||
.orderByAsc(MallLogisticsPic::getId)).stream()
|
||||
.filter(pic -> processIds.containsKey(pic.getOrderNo()))
|
||||
.collect(Collectors.groupingBy(pic -> processIds.get(pic.getOrderNo()),
|
||||
LinkedHashMap::new, Collectors.toList()));
|
||||
}
|
||||
|
||||
private Map<String, Object> record(InstallOrder order) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", order.getId());
|
||||
r.put("createTime", formatTime(order.getCreateTime()));
|
||||
r.put("modifyTime", formatTime(order.getModifyTime()));
|
||||
r.put("installOrderNo", order.getInstallOrderNo());
|
||||
r.put("installType", order.getInstallType());
|
||||
r.put("installTypeText", text(INSTALL_TYPE_TEXT, order.getInstallType()));
|
||||
r.put("orderState", order.getOrderState());
|
||||
r.put("orderStateText", text(ORDER_STATE_TEXT, order.getOrderState()));
|
||||
r.put("applyAgentId", order.getApplyAgentId());
|
||||
r.put("applyAgentName", order.getApplyAgentName());
|
||||
r.put("agentId", order.getAgentId());
|
||||
r.put("agentName", order.getAgentName());
|
||||
r.put("propertyId", order.getPropertyId());
|
||||
r.put("propertyName", order.getPropertyName());
|
||||
r.put("districtId", order.getDistrictId());
|
||||
r.put("districtName", order.getDistrictName());
|
||||
r.put("hopeInstallDate", order.getHopeInstallDate());
|
||||
r.put("trueInstallDate", order.getTrueInstallDate());
|
||||
r.put("customerContact", order.getCustomerContact());
|
||||
r.put("contactMobile", order.getContactMobile());
|
||||
r.put("projectAddress", order.getProjectAddress());
|
||||
r.put("projectMemo", order.getProjectMemo());
|
||||
r.put("applyUserId", order.getApplyUserId());
|
||||
r.put("applyUserName", order.getApplyUserName());
|
||||
r.put("applyTime", formatTime(order.getApplyTime()));
|
||||
r.put("auditTime", formatTime(order.getAuditTime()));
|
||||
r.put("dispatchTime", formatTime(order.getDispatchTime()));
|
||||
r.put("allocationTime", formatTime(order.getAllocationTime()));
|
||||
r.put("signatureTime", formatTime(order.getSignatureTime()));
|
||||
r.put("pickupTime", formatTime(order.getPickupTime()));
|
||||
r.put("warehousingTime", formatTime(order.getWarehousingTime()));
|
||||
r.put("finishDate", formatTime(order.getFinishDate()));
|
||||
r.put("auditUserName", order.getAuditUserName());
|
||||
r.put("dispatchUserName", order.getDispatchUserName());
|
||||
r.put("maintenanceUserName", order.getMaintenanceUserName());
|
||||
r.put("cooperateUserName", order.getCooperateUserName());
|
||||
r.put("scheduleUserName", order.getScheduleUserName());
|
||||
r.put("scheduleTransferOrder", order.getScheduleTransferOrder());
|
||||
r.put("deliveryOrderNo", order.getDeliveryOrderNo());
|
||||
r.put("pickupOrderNo", order.getPickupOrderNo());
|
||||
r.put("mallOrderNo", order.getMallOrderNo());
|
||||
r.put("addressId", order.getAddressId());
|
||||
return r;
|
||||
}
|
||||
|
||||
private Map<String, Object> detailRecord(InstallOrderDetail d) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", d.getId());
|
||||
r.put("deviceTypeId", d.getDeviceTypeId());
|
||||
r.put("deviceTypeName", d.getDeviceTypeName());
|
||||
r.put("installCount", d.getInstallCount());
|
||||
r.put("installPositionId", d.getInstallPositionId());
|
||||
r.put("installPositionName", d.getInstallPositionName());
|
||||
r.put("installMaterialId", d.getInstallMaterialId());
|
||||
r.put("installMaterialName", d.getInstallMaterialName());
|
||||
r.put("consumablesId", d.getConsumablesId());
|
||||
r.put("consumablesName", d.getConsumablesName());
|
||||
r.put("consumablesMemo", d.getConsumablesMemo());
|
||||
r.put("consumablesCount", d.getConsumablesCount());
|
||||
r.put("deviceRemoveFlag", d.getDeviceRemoveFlag());
|
||||
r.put("useElectricityFlag", d.getUseElectricityFlag());
|
||||
r.put("devicePayFlag", d.getDevicePayFlag());
|
||||
r.put("consumablesPayFlag", d.getConsumablesPayFlag());
|
||||
return r;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> personnel(List<InstallOrderUser> users, int type) {
|
||||
return users.stream().filter(user -> Integer.valueOf(type).equals(user.getUserType()))
|
||||
.map(user -> {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", user.getId());
|
||||
r.put("userId", user.getUserId());
|
||||
r.put("userName", user.getUserName());
|
||||
return r;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Map<String, Object> address(InstallOrder order) {
|
||||
if (order.getAddressId() == null || order.getAddressId() <= 0) return null;
|
||||
MallShippingAddress address = addressMapper.selectById(order.getAddressId());
|
||||
if (address == null || !Integer.valueOf(0).equals(address.getSoftDelete())) return null;
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", address.getId());
|
||||
r.put("receiverName", address.getReveiveName());
|
||||
r.put("receiverPhone", address.getReveiveNumber());
|
||||
r.put("province", address.getProvince());
|
||||
r.put("city", address.getCity());
|
||||
r.put("county", address.getCountyseat());
|
||||
r.put("detail", address.getAddress());
|
||||
r.put("fullAddress", join(address.getProvince(), address.getCity(), address.getCountyseat(), address.getAddress()));
|
||||
r.put("memo", address.getMemo());
|
||||
return r;
|
||||
}
|
||||
|
||||
private Map<String, Object> mallOrder(InstallOrder installOrder) {
|
||||
if (!StringUtils.hasText(installOrder.getMallOrderNo())) return null;
|
||||
List<MallGoodsOrder> orders = mallOrderMapper.selectList(new LambdaQueryWrapper<MallGoodsOrder>()
|
||||
.eq(MallGoodsOrder::getSoftDelete, 0)
|
||||
.eq(MallGoodsOrder::getOrderNo, installOrder.getMallOrderNo().trim())
|
||||
.orderByDesc(MallGoodsOrder::getId).last("LIMIT 1"));
|
||||
if (orders.isEmpty()) return null;
|
||||
MallGoodsOrder order = orders.get(0);
|
||||
List<MallOrderDetails> items = mallDetailMapper.selectList(new LambdaQueryWrapper<MallOrderDetails>()
|
||||
.eq(MallOrderDetails::getSoftDelete, 0)
|
||||
.eq(MallOrderDetails::getOrderNo, order.getOrderNo())
|
||||
.orderByDesc(MallOrderDetails::getId));
|
||||
List<MallOrderMaterial> materials = mallMaterialMapper.selectList(
|
||||
new LambdaQueryWrapper<MallOrderMaterial>()
|
||||
.eq(MallOrderMaterial::getSoftDelete, 0)
|
||||
.eq(MallOrderMaterial::getOrderNo, order.getOrderNo())
|
||||
.orderByAsc(MallOrderMaterial::getId));
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", order.getId());
|
||||
r.put("orderNo", order.getOrderNo());
|
||||
r.put("orderState", order.getOrderState());
|
||||
r.put("payState", order.getPayState());
|
||||
r.put("orderTime", formatTime(order.getOrderTime()));
|
||||
r.put("memo", order.getMemo());
|
||||
r.put("totalMoney", order.getTotalMoney());
|
||||
r.put("items", items.stream().map(this::mallItem).collect(Collectors.toList()));
|
||||
r.put("materials", materials.stream().map(this::mallMaterial).collect(Collectors.toList()));
|
||||
return r;
|
||||
}
|
||||
|
||||
private Map<String, Object> mallItem(MallOrderDetails item) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", item.getId());
|
||||
r.put("spuNo", item.getSpuNo());
|
||||
r.put("skuNo", item.getSkuNo());
|
||||
r.put("spuName", item.getSpuName());
|
||||
r.put("spuType", item.getSpuType());
|
||||
r.put("count", item.getCount());
|
||||
r.put("price", item.getPrice());
|
||||
r.put("goodsType", item.getGoodsType());
|
||||
r.put("skuDesc", item.getSkuDesc());
|
||||
r.put("skuLabel", item.getSkuLable());
|
||||
r.put("picUrl", item.getPicUrl());
|
||||
return r;
|
||||
}
|
||||
|
||||
private Map<String, Object> mallMaterial(MallOrderMaterial material) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", material.getId());
|
||||
r.put("mallOrderDetailsId", material.getMallOrderDetailsId());
|
||||
r.put("materialDefinitionId", material.getMaterielDefinitionId());
|
||||
r.put("materialNo", material.getMaterialNo());
|
||||
r.put("materialName", material.getMaterielFullName());
|
||||
r.put("materialCategory", material.getMaterielSort());
|
||||
r.put("specifications", material.getSpecifications());
|
||||
r.put("outCount", material.getOutCount());
|
||||
r.put("notOutCount", material.getNotOutCount());
|
||||
r.put("beenOutCount", material.getBeenOutCount());
|
||||
r.put("warehouseState", material.getWarehouseState());
|
||||
r.put("price", material.getPrice());
|
||||
r.put("freeFlag", material.getFreeFlag());
|
||||
return r;
|
||||
}
|
||||
|
||||
private Map<String, Object> processRecord(InstallOrderProcess process, List<InstallOrderUser> users,
|
||||
List<InstallDistributeDetail> distributes,
|
||||
List<InstallToOutWarehouse> relations,
|
||||
List<WarehouseInOutPic> pics,
|
||||
List<MallLogisticsPic> mallPics) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", process.getId());
|
||||
r.put("userId", process.getUserId());
|
||||
r.put("userName", process.getUserName());
|
||||
r.put("operationTime", formatTime(process.getOperationTime()));
|
||||
r.put("scheduleState", process.getScheduleState());
|
||||
r.put("scheduleStateText", text(SCHEDULE_STATE_TEXT, process.getScheduleState()));
|
||||
r.put("auditState", process.getAuditState());
|
||||
r.put("operationTheme", process.getOperationTheme());
|
||||
r.put("operationThemeText", text(OPERATION_THEME_TEXT, process.getOperationTheme()));
|
||||
r.put("operationType", process.getOperationType());
|
||||
r.put("memo", process.getMemo());
|
||||
r.put("maintenanceUsers", personnel(users, 0));
|
||||
r.put("cooperateUsers", personnel(users, 1));
|
||||
r.put("distributedItems", Integer.valueOf(3).equals(process.getOperationTheme())
|
||||
? distributes.stream().map(this::distributeRecord).collect(Collectors.toList()) : List.of());
|
||||
r.put("warehouseOrders", relations.stream().map(this::warehouseRelationRecord).collect(Collectors.toList()));
|
||||
List<Map<String, Object>> certificates = pics.stream().map(this::picRecord)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
certificates.addAll(mallPics.stream().map(this::mallPicRecord).collect(Collectors.toList()));
|
||||
r.put("certificates", certificates);
|
||||
return r;
|
||||
}
|
||||
|
||||
private Map<String, Object> distributeRecord(InstallDistributeDetail d) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", d.getId());
|
||||
r.put("materialDefinitionId", d.getMaterielDefinitionId());
|
||||
r.put("materialNo", d.getMaterialNo());
|
||||
r.put("materialName", d.getMaterielFullName());
|
||||
r.put("specifications", d.getSpecifications());
|
||||
r.put("snCode", d.getSnCode());
|
||||
r.put("outCount", d.getOutCount());
|
||||
r.put("outWarehouseId", d.getOutWarehouseId());
|
||||
r.put("outWarehouseName", d.getOutWarehouseName());
|
||||
r.put("inWarehouseId", d.getInWarehouseId());
|
||||
r.put("inWarehouseName", d.getInWarehouseName());
|
||||
r.put("outInOrderNo", d.getOutInOrderNo());
|
||||
r.put("actionType", d.getActionType());
|
||||
r.put("mallOrderNo", d.getMallOrderNo());
|
||||
return r;
|
||||
}
|
||||
|
||||
private Map<String, Object> warehouseRelationRecord(InstallToOutWarehouse relation) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", relation.getId());
|
||||
r.put("orderType", relation.getOrderType());
|
||||
r.put("outWarehouseType", relation.getOutWarehouseType());
|
||||
r.put("orderNo", relation.getOrderNo());
|
||||
r.put("deliveryOrderNo", relation.getDeliveryOrderNo());
|
||||
r.put("pickupOrderNo", relation.getPickupOrderNo());
|
||||
return r;
|
||||
}
|
||||
|
||||
private Map<String, Object> picRecord(WarehouseInOutPic pic) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", pic.getId());
|
||||
r.put("fileName", pic.getFileName());
|
||||
r.put("fileType", pic.getFileType());
|
||||
r.put("url", ossUrl(pic.getOssbucketname(), pic.getOssfolder(), pic.getFileName()));
|
||||
return r;
|
||||
}
|
||||
|
||||
private Map<String, Object> mallPicRecord(MallLogisticsPic pic) {
|
||||
Map<String, Object> r = new LinkedHashMap<>();
|
||||
r.put("id", pic.getId());
|
||||
r.put("fileName", pic.getFileName());
|
||||
r.put("source", "mall_logistics_pic");
|
||||
r.put("url", ossUrl(pic.getOssbucketname(), pic.getOssfolder(), pic.getFileName()));
|
||||
return r;
|
||||
}
|
||||
|
||||
private static <T> void like(LambdaQueryWrapper<T> wrapper, SFunction<T, ?> column, String value) {
|
||||
if (StringUtils.hasText(value)) wrapper.like(column, value.trim());
|
||||
}
|
||||
|
||||
private static LocalDate parseDate(String value, String field) {
|
||||
if (!StringUtils.hasText(value)) return null;
|
||||
try {
|
||||
return LocalDate.parse(value.trim(), DATE_FORMATTER);
|
||||
} catch (DateTimeParseException ex) {
|
||||
throw new IllegalArgumentException(field + " 必须是有效的 yyyy-MM-dd 日期");
|
||||
}
|
||||
}
|
||||
|
||||
private static Date startOfDay(LocalDate date) {
|
||||
return Date.from(date.atStartOfDay(BUSINESS_ZONE).toInstant());
|
||||
}
|
||||
|
||||
private static Date nextDayStart(LocalDate date) {
|
||||
return startOfDay(date.plusDays(1));
|
||||
}
|
||||
|
||||
private static String formatTime(Date value) {
|
||||
return value == null ? null : TIME_FORMATTER.format(value.toInstant());
|
||||
}
|
||||
|
||||
private static String text(Map<Integer, String> texts, Integer value) {
|
||||
return value == null ? null : texts.getOrDefault(value, "未知(" + value + ")");
|
||||
}
|
||||
|
||||
private static String join(String... values) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (String value : values) if (StringUtils.hasText(value)) result.append(value.trim());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
private static String ossUrl(String bucket, String folder, String fileName) {
|
||||
if (!StringUtils.hasText(bucket) || !StringUtils.hasText(fileName)) return null;
|
||||
return "https://" + bucket + ".oss-cn-beijing.aliyuncs.com/"
|
||||
+ (folder == null ? "" : folder) + fileName;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.entity.m1.MallGoodsOrder;
|
||||
import me.iiv.agent.entity.m2.ContractNew;
|
||||
import me.iiv.agent.entity.m2.DeliveryOrder;
|
||||
import me.iiv.agent.entity.m2.InstallOrder;
|
||||
import me.iiv.agent.entity.m2.WarehouseAgentAuth;
|
||||
import me.iiv.agent.entity.m2.WarehouseIn;
|
||||
import me.iiv.agent.entity.m2.WarehouseOut;
|
||||
@@ -221,6 +222,64 @@ public class PermissionService {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 装机工单权限,1:1 对齐老 getWorkOrderAuth(type=0) 的有效业务口径:总部看全部;
|
||||
* 销售非负责人只看自己创建物业及授权代管公司的物业;其他代理用户可看自己、
|
||||
* 授权代管公司、授权代管物业,以及本人出现在流程或作业人员表中的工单。
|
||||
* 与通用物业权限不同,本规则不包含直属下级代理。
|
||||
*/
|
||||
public void applyInstallOrderRowFilter(LambdaQueryWrapper<InstallOrder> wrapper, User user) {
|
||||
if (!isAgentAuthority(user.getAuthorityId())) {
|
||||
return;
|
||||
}
|
||||
if (user.getAgentId() == null) {
|
||||
throw new IllegalStateException("代理商用户未关联代理商,拒绝访问业务数据");
|
||||
}
|
||||
|
||||
List<WarehouseAgentAuth> auths = managedAuths(user.getId());
|
||||
List<Long> agentIds = new ArrayList<>();
|
||||
agentIds.add(user.getAgentId());
|
||||
agentIds.addAll(auths.stream().map(WarehouseAgentAuth::getManagerAgentId)
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
if (isSalesNonLeader(user)) {
|
||||
List<Long> propertyIds = properytMapper.selectList(new LambdaQueryWrapper<Properyt>()
|
||||
.eq(Properyt::getSoftDelete, 0)
|
||||
.eq(Properyt::getCreateId, user.getId())
|
||||
.select(Properyt::getId))
|
||||
.stream().map(Properyt::getId).collect(Collectors.toList());
|
||||
List<Long> managedAgentIds = auths.stream().map(WarehouseAgentAuth::getManagerAgentId)
|
||||
.collect(Collectors.toList());
|
||||
if (!managedAgentIds.isEmpty()) {
|
||||
propertyIds.addAll(properytMapper.selectList(new LambdaQueryWrapper<Properyt>()
|
||||
.eq(Properyt::getSoftDelete, 0)
|
||||
.in(Properyt::getAgentId, managedAgentIds)
|
||||
.select(Properyt::getId))
|
||||
.stream().map(Properyt::getId).collect(Collectors.toList()));
|
||||
}
|
||||
wrapper.in(InstallOrder::getPropertyId,
|
||||
propertyIds.isEmpty() ? List.of(0L) : new LinkedHashSet<>(propertyIds));
|
||||
return;
|
||||
}
|
||||
|
||||
List<Long> managedPropertyIds = managedPropertyIds(agentIds);
|
||||
String processParticipant = "EXISTS (SELECT 1 FROM install_order_process iop "
|
||||
+ "WHERE iop.install_order_no = install_order.install_order_no "
|
||||
+ "AND iop.soft_delete = 0 AND iop.user_id = {0})";
|
||||
String workerParticipant = "EXISTS (SELECT 1 FROM install_order_user iou "
|
||||
+ "WHERE iou.install_order_no = install_order.install_order_no "
|
||||
+ "AND iou.soft_delete = 0 AND iou.user_id = {0})";
|
||||
wrapper.and(w -> {
|
||||
w.in(InstallOrder::getApplyAgentId, agentIds)
|
||||
.or().in(InstallOrder::getAgentId, agentIds);
|
||||
if (!managedPropertyIds.isEmpty()) {
|
||||
w.or().in(InstallOrder::getPropertyId, managedPropertyIds);
|
||||
}
|
||||
w.or().apply(processParticipant, user.getId())
|
||||
.or().apply(workerParticipant, user.getId());
|
||||
});
|
||||
}
|
||||
|
||||
private static String placeholders(int offset, int size) {
|
||||
return IntStream.range(offset, offset + size)
|
||||
.mapToObj(i -> "{" + i + "}")
|
||||
|
||||
@@ -41,8 +41,12 @@ public class CodeGenerator {
|
||||
public static void main(String[] args) {
|
||||
generate(envOr("CODEGEN_SCHEMA_M0", "xiaoqu_comples_d"), "m0",
|
||||
"user", "agent", "authority", "properyt", "agent_manage_district_auth", "invoice_info");
|
||||
generate(envOr("CODEGEN_SCHEMA_M1", "xiaoqu_mall_d"), "m1",
|
||||
"mall_goods_order", "mall_order_details", "mall_order_materials", "mall_shipping_address",
|
||||
"mall_logistics_pic");
|
||||
generate(envOr("CODEGEN_SCHEMA_M2", "xiaoqu_franchisee_d"), "m2",
|
||||
"warehouse_agent_auth");
|
||||
"warehouse_agent_auth", "install_order", "install_order_details", "install_order_process",
|
||||
"install_order_user", "install_distribute_details", "install_to_out_warehouse");
|
||||
generate(envOr("CODEGEN_SCHEMA_SVC", "xiaoqu_agent_svc_d"), "svc",
|
||||
"agent_api_key", "agent_scope", "tool", "scope_tool", "token_scope", "audit_log", "admin_user");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
package me.iiv.agent.mcp.tool;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.entity.m1.MallGoodsOrder;
|
||||
import me.iiv.agent.entity.m1.MallLogisticsPic;
|
||||
import me.iiv.agent.entity.m1.MallOrderDetails;
|
||||
import me.iiv.agent.entity.m1.MallOrderMaterial;
|
||||
import me.iiv.agent.entity.m1.MallShippingAddress;
|
||||
import me.iiv.agent.entity.m2.InstallDistributeDetail;
|
||||
import me.iiv.agent.entity.m2.InstallOrder;
|
||||
import me.iiv.agent.entity.m2.InstallOrderDetail;
|
||||
import me.iiv.agent.entity.m2.InstallOrderProcess;
|
||||
import me.iiv.agent.entity.m2.InstallOrderUser;
|
||||
import me.iiv.agent.entity.m2.InstallToOutWarehouse;
|
||||
import me.iiv.agent.entity.m2.WarehouseInOutPic;
|
||||
import me.iiv.agent.mapper.m1.MallGoodsOrderMapper;
|
||||
import me.iiv.agent.mapper.m1.MallLogisticsPicMapper;
|
||||
import me.iiv.agent.mapper.m1.MallOrderDetailsMapper;
|
||||
import me.iiv.agent.mapper.m1.MallOrderMaterialMapper;
|
||||
import me.iiv.agent.mapper.m1.MallShippingAddressMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallDistributeDetailMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallOrderDetailMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallOrderMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallOrderProcessMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallOrderUserMapper;
|
||||
import me.iiv.agent.mapper.m2.InstallToOutWarehouseMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseInOutPicMapper;
|
||||
import me.iiv.agent.permission.PermissionService;
|
||||
import me.iiv.agent.security.AuthContext;
|
||||
import me.iiv.agent.security.AuthContextHolder;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class InstallOrderToolsTest {
|
||||
@Mock InstallOrderMapper orderMapper;
|
||||
@Mock InstallOrderDetailMapper detailMapper;
|
||||
@Mock InstallOrderProcessMapper processMapper;
|
||||
@Mock InstallOrderUserMapper userMapper;
|
||||
@Mock InstallDistributeDetailMapper distributeMapper;
|
||||
@Mock InstallToOutWarehouseMapper warehouseRelationMapper;
|
||||
@Mock WarehouseInOutPicMapper picMapper;
|
||||
@Mock MallGoodsOrderMapper mallOrderMapper;
|
||||
@Mock MallOrderDetailsMapper mallDetailMapper;
|
||||
@Mock MallOrderMaterialMapper mallMaterialMapper;
|
||||
@Mock MallShippingAddressMapper addressMapper;
|
||||
@Mock MallLogisticsPicMapper mallPicMapper;
|
||||
@Mock PermissionService permissionService;
|
||||
InstallOrderTools tools;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), "");
|
||||
TableInfoHelper.initTableInfo(assistant, InstallOrder.class);
|
||||
TableInfoHelper.initTableInfo(assistant, InstallOrderDetail.class);
|
||||
TableInfoHelper.initTableInfo(assistant, InstallOrderProcess.class);
|
||||
TableInfoHelper.initTableInfo(assistant, InstallOrderUser.class);
|
||||
TableInfoHelper.initTableInfo(assistant, InstallDistributeDetail.class);
|
||||
TableInfoHelper.initTableInfo(assistant, InstallToOutWarehouse.class);
|
||||
TableInfoHelper.initTableInfo(assistant, WarehouseInOutPic.class);
|
||||
TableInfoHelper.initTableInfo(assistant, MallGoodsOrder.class);
|
||||
TableInfoHelper.initTableInfo(assistant, MallOrderDetails.class);
|
||||
TableInfoHelper.initTableInfo(assistant, MallOrderMaterial.class);
|
||||
TableInfoHelper.initTableInfo(assistant, MallShippingAddress.class);
|
||||
TableInfoHelper.initTableInfo(assistant, MallLogisticsPic.class);
|
||||
tools = new InstallOrderTools(orderMapper, detailMapper, processMapper, userMapper,
|
||||
distributeMapper, warehouseRelationMapper, picMapper, mallOrderMapper,
|
||||
mallDetailMapper, mallMaterialMapper, addressMapper, mallPicMapper, permissionService);
|
||||
User user = new User();
|
||||
user.setId(7L);
|
||||
AuthContextHolder.set(new AuthContext(null, user));
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
AuthContextHolder.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
void listAppliesFiltersPermissionPaginationAndTotalInstallCount() {
|
||||
InstallOrder order = order();
|
||||
InstallOrderDetail first = detail(2);
|
||||
InstallOrderDetail second = detail(3);
|
||||
when(orderMapper.selectCount(any())).thenReturn(1L);
|
||||
when(orderMapper.selectList(any())).thenReturn(List.of(order));
|
||||
when(detailMapper.selectList(any())).thenReturn(List.of(first, second));
|
||||
|
||||
Map<String, Object> result = tools.installOrderListQuery(" ZJ ", " XS ", null,
|
||||
null, null, 2, 5, 10L, " 项目 ", " 张 ", " 李 ", " 王 ",
|
||||
"2026-09-01", "2026-09-16", 0, 500);
|
||||
|
||||
assertEquals(1L, result.get("total"));
|
||||
assertEquals(1, result.get("page"));
|
||||
assertEquals(100, result.get("rows"));
|
||||
assertEquals("rows 超过上限 100,已截断为 100", result.get("notice"));
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> record = ((List<Map<String, Object>>) result.get("records")).get(0);
|
||||
assertEquals("作业中", record.get("orderStateText"));
|
||||
assertEquals("销售装机", record.get("installTypeText"));
|
||||
assertEquals(5, record.get("totalInstallCount"));
|
||||
verify(permissionService).applyInstallOrderRowFilter(any(), any());
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
ArgumentCaptor<LambdaQueryWrapper<InstallOrder>> captor = ArgumentCaptor.forClass(LambdaQueryWrapper.class);
|
||||
verify(orderMapper).selectList(captor.capture());
|
||||
String sql = captor.getValue().getSqlSegment();
|
||||
assertTrue(sql.contains("install_order_no LIKE"));
|
||||
assertTrue(sql.contains("apply_time >="));
|
||||
assertTrue(sql.contains("apply_time <"));
|
||||
assertTrue(sql.contains("apply_time DESC"));
|
||||
assertTrue(sql.contains("id DESC"));
|
||||
assertTrue(sql.contains("LIMIT 0,100"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void listRejectsInvalidAndReversedApplyDates() {
|
||||
assertThrows(IllegalArgumentException.class, () -> listWithDates("2026-02-30", null));
|
||||
assertThrows(IllegalArgumentException.class, () -> listWithDates("2026-09-17", "2026-09-16"));
|
||||
verifyNoInteractions(orderMapper, permissionService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailHidesMissingAndForbiddenOrder() {
|
||||
when(orderMapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
Map<String, Object> result = tools.installOrderDetailQuery(20L, null, null, null);
|
||||
|
||||
assertNull(result.get("record"));
|
||||
assertEquals("工单不存在或无权查看", result.get("notice"));
|
||||
verify(permissionService).applyInstallOrderRowFilter(any(), any());
|
||||
verifyNoInteractions(detailMapper, processMapper, userMapper, distributeMapper,
|
||||
warehouseRelationMapper, picMapper, mallOrderMapper, mallDetailMapper,
|
||||
mallMaterialMapper, addressMapper, mallPicMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailCanSkipProcesses() {
|
||||
InstallOrder order = order();
|
||||
order.setMallOrderNo(null);
|
||||
when(orderMapper.selectList(any())).thenReturn(List.of(order));
|
||||
when(detailMapper.selectList(any())).thenReturn(List.of());
|
||||
when(userMapper.selectList(any())).thenReturn(List.of());
|
||||
|
||||
Map<String, Object> result = tools.installOrderDetailQuery(20L, null, false, null);
|
||||
|
||||
assertEquals(List.of(), result.get("processes"));
|
||||
assertEquals(0L, result.get("processTotal"));
|
||||
verifyNoInteractions(processMapper, distributeMapper, warehouseRelationMapper, picMapper, mallPicMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void detailReturnsPeopleMallAggregationAndProcessRelations() {
|
||||
InstallOrder order = order();
|
||||
order.setAddressId(30L);
|
||||
when(orderMapper.selectList(any())).thenReturn(List.of(order));
|
||||
when(detailMapper.selectList(any())).thenReturn(List.of(detail(2)));
|
||||
InstallOrderUser worker = new InstallOrderUser();
|
||||
worker.setId(1L);
|
||||
worker.setInstallOrderNo("ZJ-001");
|
||||
worker.setUserType(0);
|
||||
worker.setType(0);
|
||||
worker.setUserName("运维甲");
|
||||
InstallOrderUser collaborator = new InstallOrderUser();
|
||||
collaborator.setId(2L);
|
||||
collaborator.setInstallOrderNo("ZJ-001");
|
||||
collaborator.setUserType(1);
|
||||
collaborator.setType(0);
|
||||
collaborator.setUserName("协同乙");
|
||||
when(userMapper.selectList(any())).thenReturn(List.of(worker, collaborator), List.of());
|
||||
InstallOrderProcess process = new InstallOrderProcess();
|
||||
process.setId(40L);
|
||||
process.setOperationTheme(3);
|
||||
process.setScheduleState(2);
|
||||
when(processMapper.selectCount(any())).thenReturn(1L);
|
||||
when(processMapper.selectList(any())).thenReturn(List.of(process));
|
||||
InstallDistributeDetail distribute = new InstallDistributeDetail();
|
||||
distribute.setId(50L);
|
||||
distribute.setSnCode("SN-1");
|
||||
when(distributeMapper.selectList(any())).thenReturn(List.of(distribute));
|
||||
InstallToOutWarehouse relation = new InstallToOutWarehouse();
|
||||
relation.setId(60L);
|
||||
relation.setOrderNo("CK-1");
|
||||
when(warehouseRelationMapper.selectList(any())).thenReturn(List.of(relation));
|
||||
WarehouseInOutPic pic = new WarehouseInOutPic();
|
||||
pic.setId(70L);
|
||||
pic.setAggregateId(40L);
|
||||
pic.setOssbucketname("bucket");
|
||||
pic.setOssfolder("folder/");
|
||||
pic.setFileName("proof.jpg");
|
||||
when(picMapper.selectList(any())).thenReturn(List.of(pic));
|
||||
when(mallPicMapper.selectList(any())).thenReturn(List.of());
|
||||
MallShippingAddress address = new MallShippingAddress();
|
||||
address.setId(30L);
|
||||
address.setSoftDelete(0);
|
||||
address.setProvince("北京");
|
||||
address.setCity("北京市");
|
||||
address.setAddress("朝阳区1号");
|
||||
when(addressMapper.selectById(30L)).thenReturn(address);
|
||||
MallGoodsOrder mallOrder = new MallGoodsOrder();
|
||||
mallOrder.setId(80L);
|
||||
mallOrder.setOrderNo("XS-001");
|
||||
mallOrder.setTotalMoney(BigDecimal.TEN);
|
||||
when(mallOrderMapper.selectList(any())).thenReturn(List.of(mallOrder));
|
||||
MallOrderDetails mallItem = new MallOrderDetails();
|
||||
mallItem.setId(81L);
|
||||
mallItem.setSpuName("纸机");
|
||||
when(mallDetailMapper.selectList(any())).thenReturn(List.of(mallItem));
|
||||
MallOrderMaterial material = new MallOrderMaterial();
|
||||
material.setId(82L);
|
||||
material.setMaterialNo("MAT-1");
|
||||
when(mallMaterialMapper.selectList(any())).thenReturn(List.of(material));
|
||||
|
||||
Map<String, Object> result = tools.installOrderDetailQuery(null, "ZJ-001", true, 1);
|
||||
|
||||
assertEquals(2, result.get("totalInstallCount"));
|
||||
assertEquals(1, ((List<?>) result.get("maintenanceUsers")).size());
|
||||
assertEquals(1, ((List<?>) result.get("cooperateUsers")).size());
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> mall = (Map<String, Object>) result.get("mallOrder");
|
||||
assertEquals("XS-001", mall.get("orderNo"));
|
||||
assertEquals(1, ((List<?>) mall.get("items")).size());
|
||||
assertEquals(1, ((List<?>) mall.get("materials")).size());
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> processRecord = ((List<Map<String, Object>>) result.get("processes")).get(0);
|
||||
assertEquals(1, ((List<?>) processRecord.get("distributedItems")).size());
|
||||
assertEquals(1, ((List<?>) processRecord.get("warehouseOrders")).size());
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> certificate = ((List<Map<String, Object>>) processRecord.get("certificates")).get(0);
|
||||
assertEquals("https://bucket.oss-cn-beijing.aliyuncs.com/folder/proof.jpg", certificate.get("url"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void springAiPublishesBothWorkorderSchemas() {
|
||||
var callbacks = org.springframework.ai.tool.method.MethodToolCallbackProvider.builder()
|
||||
.toolObjects(tools).build().getToolCallbacks();
|
||||
Map<String, String> schemas = Arrays.stream(callbacks).collect(java.util.stream.Collectors.toMap(
|
||||
callback -> callback.getToolDefinition().name(), callback -> callback.getToolDefinition().inputSchema()));
|
||||
|
||||
assertEquals(2, schemas.size());
|
||||
assertTrue(schemas.get("install.order.list.query").contains("applyTimeFrom"));
|
||||
assertTrue(schemas.get("install.order.list.query").contains("maintenanceUserName"));
|
||||
assertTrue(schemas.get("install.order.detail.query").contains("processLimit"));
|
||||
assertTrue(schemas.get("install.order.detail.query").contains("includeProcess"));
|
||||
}
|
||||
|
||||
private Map<String, Object> listWithDates(String from, String to) {
|
||||
return tools.installOrderListQuery(null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, from, to, 1, 20);
|
||||
}
|
||||
|
||||
private static InstallOrder order() {
|
||||
InstallOrder order = new InstallOrder();
|
||||
order.setId(20L);
|
||||
order.setInstallOrderNo("ZJ-001");
|
||||
order.setMallOrderNo("XS-001");
|
||||
order.setInstallType(2);
|
||||
order.setOrderState(5);
|
||||
return order;
|
||||
}
|
||||
|
||||
private static InstallOrderDetail detail(int count) {
|
||||
InstallOrderDetail detail = new InstallOrderDetail();
|
||||
detail.setId(100L + count);
|
||||
detail.setInstallOrderNo("ZJ-001");
|
||||
detail.setInstallCount(count);
|
||||
return detail;
|
||||
}
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
package me.iiv.agent.permission;
|
||||
|
||||
import com.baomidou.mybatisplus.core.MybatisConfiguration;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
|
||||
import me.iiv.agent.entity.m0.Authority;
|
||||
import me.iiv.agent.entity.m0.Properyt;
|
||||
import me.iiv.agent.entity.m0.User;
|
||||
import me.iiv.agent.entity.m2.InstallOrder;
|
||||
import me.iiv.agent.entity.m2.WarehouseAgentAuth;
|
||||
import me.iiv.agent.mapper.m0.AgentManageDistrictAuthMapper;
|
||||
import me.iiv.agent.mapper.m0.AgentMapper;
|
||||
import me.iiv.agent.mapper.m0.AuthorityMapper;
|
||||
import me.iiv.agent.mapper.m0.ProperytMapper;
|
||||
import me.iiv.agent.mapper.m2.WarehouseAgentAuthMapper;
|
||||
import org.apache.ibatis.builder.MapperBuilderAssistant;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PermissionServiceInstallOrderRowFilterTest {
|
||||
@Mock AuthorityMapper authorityMapper;
|
||||
@Mock AgentMapper agentMapper;
|
||||
@Mock WarehouseAgentAuthMapper warehouseAgentAuthMapper;
|
||||
@Mock AgentManageDistrictAuthMapper agentManageDistrictAuthMapper;
|
||||
@Mock ProperytMapper properytMapper;
|
||||
@Mock StringRedisTemplate redisTemplate;
|
||||
@Mock HashOperations<String, Object, Object> hashOperations;
|
||||
PermissionService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), "");
|
||||
TableInfoHelper.initTableInfo(assistant, InstallOrder.class);
|
||||
TableInfoHelper.initTableInfo(assistant, Properyt.class);
|
||||
service = new PermissionService(authorityMapper, agentMapper, warehouseAgentAuthMapper,
|
||||
agentManageDistrictAuthMapper, properytMapper, redisTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
void headquartersAddsNoFilter() {
|
||||
User user = user(7L, 9L, null);
|
||||
when(authorityMapper.selectById(9L)).thenReturn(authority(9L, 0));
|
||||
LambdaQueryWrapper<InstallOrder> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
service.applyInstallOrderRowFilter(wrapper, user);
|
||||
|
||||
assertTrue(wrapper.getSqlSegment().isBlank());
|
||||
}
|
||||
|
||||
@Test
|
||||
void agentScopeExcludesSubordinatesAndIncludesManagedAndParticipants() {
|
||||
User user = user(7L, 9L, 10L);
|
||||
when(authorityMapper.selectById(9L)).thenReturn(authority(9L, 1));
|
||||
WarehouseAgentAuth managed = new WarehouseAgentAuth();
|
||||
managed.setManagerAgentId(12L);
|
||||
when(warehouseAgentAuthMapper.selectList(any())).thenReturn(List.of(managed));
|
||||
when(agentManageDistrictAuthMapper.selectList(any())).thenReturn(List.of());
|
||||
LambdaQueryWrapper<InstallOrder> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
service.applyInstallOrderRowFilter(wrapper, user);
|
||||
|
||||
String sql = wrapper.getSqlSegment();
|
||||
Collection<Object> values = wrapper.getParamNameValuePairs().values();
|
||||
assertTrue(sql.contains("apply_agent_id IN"));
|
||||
assertTrue(sql.contains("agent_id IN"));
|
||||
assertTrue(sql.contains("install_order_process"));
|
||||
assertTrue(sql.contains("install_order_user"));
|
||||
assertTrue(values.containsAll(List.of(7L, 10L, 12L)));
|
||||
verifyNoInteractions(agentMapper);
|
||||
}
|
||||
|
||||
@Test
|
||||
void salesNonLeaderUsesOnlyOwnAndManagedCompanyProperties() {
|
||||
User user = user(7L, 9L, 10L);
|
||||
user.setDeptId(6L);
|
||||
user.setPostId(8L);
|
||||
when(authorityMapper.selectById(9L)).thenReturn(authority(9L, 1));
|
||||
WarehouseAgentAuth managed = new WarehouseAgentAuth();
|
||||
managed.setManagerAgentId(12L);
|
||||
when(warehouseAgentAuthMapper.selectList(any())).thenReturn(List.of(managed));
|
||||
when(redisTemplate.opsForHash()).thenReturn(hashOperations);
|
||||
when(hashOperations.get("Dictionary", "authDeptId")).thenReturn(",6,");
|
||||
when(hashOperations.get("Dictionary", "authDeptPostId")).thenReturn(",99,");
|
||||
Properyt own = new Properyt();
|
||||
own.setId(21L);
|
||||
Properyt managedProperty = new Properyt();
|
||||
managedProperty.setId(22L);
|
||||
when(properytMapper.selectList(any())).thenReturn(List.of(own), List.of(managedProperty));
|
||||
LambdaQueryWrapper<InstallOrder> wrapper = new LambdaQueryWrapper<>();
|
||||
|
||||
service.applyInstallOrderRowFilter(wrapper, user);
|
||||
|
||||
String sql = wrapper.getSqlSegment();
|
||||
Collection<Object> values = wrapper.getParamNameValuePairs().values();
|
||||
assertTrue(sql.contains("property_id IN"));
|
||||
assertTrue(values.containsAll(List.of(21L, 22L)));
|
||||
assertFalse(sql.contains("install_order_process"));
|
||||
assertFalse(sql.contains("apply_agent_id"));
|
||||
verifyNoInteractions(agentMapper, agentManageDistrictAuthMapper);
|
||||
}
|
||||
|
||||
private static User user(Long id, Long authorityId, Long agentId) {
|
||||
User user = new User();
|
||||
user.setId(id);
|
||||
user.setAuthorityId(authorityId);
|
||||
user.setAgentId(agentId);
|
||||
return user;
|
||||
}
|
||||
|
||||
private static Authority authority(Long id, int isAgent) {
|
||||
Authority authority = new Authority();
|
||||
authority.setId(id);
|
||||
authority.setIsagent(isAgent);
|
||||
authority.setSoftDelete(0);
|
||||
return authority;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user