This commit is contained in:
2026-08-25 09:52:49 +08:00
parent b211121365
commit b67217332a
37 changed files with 1009 additions and 343 deletions
@@ -0,0 +1,32 @@
package com.baiyue.lastleg.pda.security;
import com.baiyue.common.security.AuthenticatedUser;
import org.springframework.http.HttpStatus;
import org.springframework.web.server.ResponseStatusException;
public record PdaWarehouseIdentity(String operatorKey, String operatorName, long overseasWarehouseId) {
public static final String OPERATE_PERMISSION = "lastleg:pda:operate";
public static final String PICKER_ROLE = "ROLE_WAREHOUSE_PICKER";
public static PdaWarehouseIdentity require(AuthenticatedUser user) {
if (user == null || user.userId() == null || user.tenantId() == null || user.tenantId() != 0
|| !"PLATFORM".equals(user.appCode())) {
throw new ResponseStatusException(HttpStatus.UNAUTHORIZED,
"Authenticated platform warehouse PDA identity is required");
}
if (!user.permissions().contains(PICKER_ROLE)) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "WAREHOUSE_PICKER role is required");
}
if (!user.permissions().contains(OPERATE_PERMISSION)) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, OPERATE_PERMISSION + " permission is required");
}
if (user.overseasWarehouseId() == null || user.overseasWarehouseId() <= 0) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN,
"PDA operator must be bound to one overseas warehouse");
}
String operator = String.valueOf(user.userId());
return new PdaWarehouseIdentity(operator,
user.username() == null || user.username().isBlank() ? operator : user.username(),
user.overseasWarehouseId());
}
}
@@ -0,0 +1,15 @@
package com.baiyue.lastleg.pda.warehouse.dto;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
public final class PdaWarehouseRequests {
private PdaWarehouseRequests() { }
public record Scan(@NotBlank @Size(max = 128) String code) { }
public record HistoryQuery(@Min(1) long page, @Min(1) @Max(200) long size, String operationType) {
public HistoryQuery { if (page == 0) page = 1; if (size == 0) size = 20; }
}
}
@@ -0,0 +1,23 @@
package com.baiyue.lastleg.pda.warehouse.dto;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
public final class PdaWarehouseResponses {
private PdaWarehouseResponses() { }
public record InventoryScan(String queryCode, List<InventoryItem> items) { }
public record InventoryItem(String customerId, String customerName, long balanceId, long positionId,
String skuCode, String productName, String inboundCode, String cartonMark,
long palletId, String palletCode, Long locationId, String locationCode,
long availablePackages, long holdPackages, long receivingPackages,
String status, long version) { }
public record Location(long locationId, String locationCode, String locationType, BigDecimal maxLoadKg,
String status, long occupiedPackages, int inventoryPositionCount) { }
public record Pallet(long palletId, String palletCode, String status, int version,
long locationId, String locationCode, long occupiedPackages,
int inventoryPositionCount, List<InventoryItem> inventory) { }
public record Operation(long operationId, String operationType, String businessKey, String status,
String summary, String customerId, LocalDateTime occurredAt) { }
}
@@ -0,0 +1,26 @@
package com.baiyue.lastleg.pda.warehouse.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("ll_pda_operation_history")
public class PdaOperationHistory {
@TableId(type = IdType.INPUT)
private Long id;
private Long overseasWarehouseId;
private String customerId;
private String operatorKey;
private String operationType;
private String businessKey;
private String status;
private String idempotencyKey;
private String requestHash;
private String summary;
private LocalDateTime occurredAt;
private LocalDateTime createdAt;
}
@@ -0,0 +1,72 @@
package com.baiyue.lastleg.pda.warehouse.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baiyue.lastleg.pda.warehouse.entity.PdaOperationHistory;
import com.baiyue.lastleg.pda.warehouse.model.PdaInventoryProjection;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface PdaWarehouseMapper extends BaseMapper<PdaOperationHistory> {
String INVENTORY_SELECT = "SELECT b.customer_id,b.customer_name,b.id balance_id,p.id position_id," +
"COALESCE(NULLIF(r.sku_code,''),b.sku_code) sku_code," +
"COALESCE(NULLIF(r.product_name_zh,''),b.product_name_zh) product_name_zh," +
"COALESCE(NULLIF(r.product_name_en,''),b.product_name_en) product_name_en,b.inbound_code," +
"c.carton_mark,p.pallet_id,p.pallet_no pallet_code,p.warehouse_location_id location_id," +
"p.warehouse_location_code location_code,p.available_packages,p.hold_packages,p.receiving_packages," +
"p.status,p.version FROM ll_inventory_balance b JOIN ll_inventory_position p " +
"ON p.balance_id=b.id AND p.customer_id=b.customer_id " +
"LEFT JOIN ll_inbound_stock_receipt r ON r.inventory_balance_id=b.id AND r.inventory_position_id=p.id " +
"LEFT JOIN ll_inbound_plan_carton c ON c.customer_id=b.customer_id AND c.order_id=b.inbound_order_id " +
"AND c.warehouse_pallet_id=p.pallet_id AND c.carton_mark=#{code} AND c.status='RECEIVED' ";
@Select(INVENTORY_SELECT + "WHERE b.overseas_warehouse_id=#{warehouseId} " +
"AND (UPPER(b.sku_code)=UPPER(#{code}) OR UPPER(r.sku_code)=UPPER(#{code}) " +
"OR b.inbound_code=#{code} OR c.carton_mark=#{code}) " +
"AND (p.available_packages+p.hold_packages+p.allocated_packages+p.picked_packages+p.receiving_packages)>0 " +
"ORDER BY b.customer_id,b.inbound_code,p.pallet_no,p.id")
List<PdaInventoryProjection> scanInventory(@Param("warehouseId") String warehouseId,
@Param("code") String code);
@Select(INVENTORY_SELECT + "WHERE b.overseas_warehouse_id=#{warehouseId} AND p.pallet_id=#{palletId} " +
"AND (p.available_packages+p.hold_packages+p.allocated_packages+p.picked_packages+p.receiving_packages)>0 " +
"ORDER BY b.customer_id,b.inbound_code,p.id")
List<PdaInventoryProjection> inventoryByPallet(@Param("warehouseId") String warehouseId,
@Param("palletId") long palletId,
@Param("code") String ignored);
@Select(INVENTORY_SELECT + "WHERE b.overseas_warehouse_id=#{warehouseId} " +
"AND (p.warehouse_location_id=#{locationId} OR p.warehouse_location_code=#{locationCode}) " +
"AND (p.available_packages+p.hold_packages+p.allocated_packages+p.picked_packages+p.receiving_packages)>0 " +
"ORDER BY b.customer_id,b.inbound_code,p.pallet_no,p.id")
List<PdaInventoryProjection> inventoryByLocation(@Param("warehouseId") String warehouseId,
@Param("locationId") long locationId,
@Param("locationCode") String locationCode,
@Param("code") String ignored);
@Select("<script>SELECT operation_id,operation_type,business_key,status,summary,customer_id,occurred_at FROM (" +
"SELECT h.id operation_id,h.operation_type,h.business_key,h.status,h.summary,h.customer_id,h.occurred_at " +
"FROM ll_pda_operation_history h WHERE h.overseas_warehouse_id=#{warehouseId} AND h.operator_key=#{operatorKey} " +
"<if test='operationType != null and operationType != \"\"'>AND h.operation_type=#{operationType} </if>" +
"UNION ALL SELECT o.id,CONCAT('RECEIVING_',o.action),s.container_no,'COMPLETED'," +
"CONCAT('柜号作业 ',s.container_no),s.customer_id,o.created_at " +
"FROM ll_receiving_container_operation o JOIN ll_receiving_container_session s ON s.id=o.session_id " +
"WHERE s.overseas_warehouse_id=#{warehouseId} AND o.operator_key=#{operatorKey} " +
"<if test='operationType != null and operationType != \"\"'>AND CONCAT('RECEIVING_',o.action)=#{operationType} </if>" +
") x ORDER BY occurred_at DESC,operation_id DESC LIMIT #{limit} OFFSET #{offset}</script>")
List<com.baiyue.lastleg.pda.warehouse.dto.PdaWarehouseResponses.Operation> history(
@Param("warehouseId") long warehouseId, @Param("operatorKey") String operatorKey,
@Param("operationType") String operationType, @Param("offset") long offset, @Param("limit") long limit);
@Select("<script>SELECT COUNT(*) FROM (SELECT h.id FROM ll_pda_operation_history h " +
"WHERE h.overseas_warehouse_id=#{warehouseId} AND h.operator_key=#{operatorKey} " +
"<if test='operationType != null and operationType != \"\"'>AND h.operation_type=#{operationType} </if>" +
"UNION ALL SELECT o.id FROM ll_receiving_container_operation o JOIN ll_receiving_container_session s ON s.id=o.session_id " +
"WHERE s.overseas_warehouse_id=#{warehouseId} AND o.operator_key=#{operatorKey} " +
"<if test='operationType != null and operationType != \"\"'>AND CONCAT('RECEIVING_',o.action)=#{operationType} </if>) x</script>")
long countHistory(@Param("warehouseId") long warehouseId, @Param("operatorKey") String operatorKey,
@Param("operationType") String operationType);
}
@@ -0,0 +1,25 @@
package com.baiyue.lastleg.pda.warehouse.model;
import lombok.Data;
@Data
public class PdaInventoryProjection {
private String customerId;
private String customerName;
private Long balanceId;
private Long positionId;
private String skuCode;
private String productNameZh;
private String productNameEn;
private String inboundCode;
private String cartonMark;
private Long palletId;
private String palletCode;
private Long locationId;
private String locationCode;
private Long availablePackages;
private Long holdPackages;
private Long receivingPackages;
private String status;
private Long version;
}