This commit is contained in:
2026-08-18 11:39:40 +08:00
parent ac6c0d0395
commit d2c532f807
165 changed files with 2547 additions and 1060 deletions
@@ -0,0 +1,25 @@
package com.baiyue.warehouse.controller;
import com.baiyue.common.core.api.ApiResponse;
import com.baiyue.warehouse.dto.DropshipPalletRequests;
import com.baiyue.warehouse.entity.WarehousePalletEntity;
import com.baiyue.warehouse.security.InternalServiceTokenGuard;
import com.baiyue.warehouse.service.WarehouseResourceService;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/internal/v1/warehouse-pallets")
public class InternalWarehousePalletController {
@Resource private WarehouseResourceService service;
@Resource private InternalServiceTokenGuard tokenGuard;
@PostMapping("/ensure-dropship")
public ApiResponse<WarehousePalletEntity> ensure(
@RequestHeader(value = "X-Internal-Service-Token", required = false) String token,
@Valid @RequestBody DropshipPalletRequests.Ensure request) {
tokenGuard.verify(token);
return ApiResponse.success(service.ensureDropshipPallet(request.customerId(), request.warehouseId(), request.businessId()));
}
}
@@ -38,7 +38,7 @@ public class InternalWarehouseResourceLockController {
@PathVariable long lockId,
@Valid @RequestBody WarehouseResourceLockRequests.CloseRequest request) {
tokenGuard.verify(token);
return ApiResponse.success(service.release(request.tenantId(), lockId));
return ApiResponse.success(service.release(request.customerId(), lockId));
}
@PostMapping("/{lockId}/scrap")
@@ -47,7 +47,7 @@ public class InternalWarehouseResourceLockController {
@PathVariable long lockId,
@Valid @RequestBody WarehouseResourceLockRequests.CloseRequest request) {
tokenGuard.verify(token);
return ApiResponse.success(service.scrap(request.tenantId(), lockId));
return ApiResponse.success(service.scrap(request.customerId(), lockId));
}
@PostMapping("/pallets/{palletId}/scrap")
@@ -56,7 +56,7 @@ public class InternalWarehouseResourceLockController {
@PathVariable long palletId,
@Valid @RequestBody WarehouseResourceLockRequests.CloseRequest request) {
tokenGuard.verify(token);
return ApiResponse.success(service.scrapByPallet(request.tenantId(), palletId));
return ApiResponse.success(service.scrapByPallet(request.customerId(), palletId));
}
@PostMapping("/default-locations/ensure")
@@ -65,6 +65,6 @@ public class InternalWarehouseResourceLockController {
@Valid @RequestBody WarehouseResourceLockRequests.DefaultLocationRequest request) {
tokenGuard.verify(token);
return ApiResponse.success(resourceService.ensureDefaultVirtualLocation(
request.tenantId(), request.warehouseId()));
request.customerId(), request.warehouseId()));
}
}
@@ -56,7 +56,7 @@ public class PlatformWarehouseController {
public ApiResponse<Long> create(@AuthenticationPrincipal AuthenticatedUser user,
@Valid @RequestBody WarehouseRequests.SaveRequest request) {
requirePlatform(user);
requireTenant(request.tenantId());
requireTenant(request.customerId());
return ApiResponse.success(service.create(request));
}
@@ -75,13 +75,13 @@ public class PlatformWarehouseController {
public ApiResponse<Void> update(
@AuthenticationPrincipal AuthenticatedUser user,
@Parameter(description = "Target tenant ID / 目标租户 ID", example = "10001", required = true)
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@Parameter(description = "Warehouse ID / 仓库 ID", example = "190000000000000001", required = true)
@PathVariable long id,
@Valid @RequestBody WarehouseRequests.SaveRequest request) {
requirePlatform(user);
requireTenant(tenantId);
service.update(tenantId, id, request);
requireTenant(customerId);
service.update(customerId, id, request);
return ApiResponse.success(null);
}
@@ -98,11 +98,11 @@ public class PlatformWarehouseController {
public ApiResponse<PageResult<WarehouseEntity>> page(
@AuthenticationPrincipal AuthenticatedUser user,
@Parameter(description = "Target tenant ID / 目标租户 ID", example = "10001", required = true)
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@Valid WarehouseRequests.QueryRequest request) {
requirePlatform(user);
requireTenant(tenantId);
return ApiResponse.success(service.page(tenantId, request));
requireTenant(customerId);
return ApiResponse.success(service.page(customerId, request));
}
@Operation(
@@ -118,12 +118,12 @@ public class PlatformWarehouseController {
public ApiResponse<WarehouseDetailResponse> detail(
@AuthenticationPrincipal AuthenticatedUser user,
@Parameter(description = "Target tenant ID / 目标租户 ID", example = "10001", required = true)
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@Parameter(description = "Warehouse ID / 仓库 ID", example = "190000000000000001", required = true)
@PathVariable long id) {
requirePlatform(user);
requireTenant(tenantId);
return ApiResponse.success(service.detail(tenantId, id));
requireTenant(customerId);
return ApiResponse.success(service.detail(customerId, id));
}
@Operation(
@@ -140,14 +140,14 @@ public class PlatformWarehouseController {
public ApiResponse<Void> status(
@AuthenticationPrincipal AuthenticatedUser user,
@Parameter(description = "Target tenant ID / 目标租户 ID", example = "10001", required = true)
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@Parameter(description = "Warehouse ID / 仓库 ID", example = "190000000000000001", required = true)
@PathVariable long id,
@Parameter(description = "Target status: ACTIVE or INACTIVE / 目标状态:ACTIVE 或 INACTIVE", example = "ACTIVE", required = true)
@RequestParam String status) {
requirePlatform(user);
requireTenant(tenantId);
service.changeStatus(tenantId, id, status);
requireTenant(customerId);
service.changeStatus(customerId, id, status);
return ApiResponse.success(null);
}
@@ -157,8 +157,8 @@ public class PlatformWarehouseController {
}
}
private static void requireTenant(Long tenantId) {
if (tenantId == null || tenantId <= 0) {
private static void requireTenant(Long customerId) {
if (customerId == null || customerId <= 0) {
throw new BizException(ResultCode.BAD_REQUEST, "Target tenant is required / 必须指定有效目标租户");
}
}
@@ -33,98 +33,98 @@ public class PlatformWarehouseResourceController {
@PostMapping("/locations")
public ApiResponse<Long> createLocation(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@PathVariable long warehouseId,
@Valid @RequestBody LocationRequests.SaveRequest request) {
requirePlatform(user, tenantId);
return ApiResponse.success(service.createLocation(tenantId, warehouseId, request));
requirePlatform(user, customerId);
return ApiResponse.success(service.createLocation(customerId, warehouseId, request));
}
@PutMapping("/locations/{id}")
public ApiResponse<Void> updateLocation(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@PathVariable long warehouseId, @PathVariable long id,
@Valid @RequestBody LocationRequests.SaveRequest request) {
requirePlatform(user, tenantId);
service.updateLocation(tenantId, warehouseId, id, request);
requirePlatform(user, customerId);
service.updateLocation(customerId, warehouseId, id, request);
return ApiResponse.success(null);
}
@GetMapping("/locations")
public ApiResponse<PageResult<WarehouseLocationEntity>> locations(
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam @NotNull Long tenantId,
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam @NotNull Long customerId,
@PathVariable long warehouseId, @Valid LocationRequests.QueryRequest query) {
requirePlatform(user, tenantId);
return ApiResponse.success(service.locations(tenantId, warehouseId, query));
requirePlatform(user, customerId);
return ApiResponse.success(service.locations(customerId, warehouseId, query));
}
@GetMapping("/locations/{id}")
public ApiResponse<WarehouseLocationEntity> location(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@PathVariable long warehouseId,
@PathVariable long id) {
requirePlatform(user, tenantId);
return ApiResponse.success(service.location(tenantId, warehouseId, id));
requirePlatform(user, customerId);
return ApiResponse.success(service.location(customerId, warehouseId, id));
}
@DeleteMapping("/locations/{id}")
public ApiResponse<Void> deleteLocation(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@PathVariable long warehouseId, @PathVariable long id) {
requirePlatform(user, tenantId);
service.deleteLocation(tenantId, warehouseId, id);
requirePlatform(user, customerId);
service.deleteLocation(customerId, warehouseId, id);
return ApiResponse.success(null);
}
@PostMapping("/pallets")
public ApiResponse<Long> createPallet(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@PathVariable long warehouseId,
@Valid @RequestBody PalletRequests.SaveRequest request) {
requirePlatform(user, tenantId);
return ApiResponse.success(service.createPallet(tenantId, warehouseId, request));
requirePlatform(user, customerId);
return ApiResponse.success(service.createPallet(customerId, warehouseId, request));
}
@PutMapping("/pallets/{id}")
public ApiResponse<Void> updatePallet(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@PathVariable long warehouseId, @PathVariable long id,
@Valid @RequestBody PalletRequests.SaveRequest request) {
requirePlatform(user, tenantId);
service.updatePallet(tenantId, warehouseId, id, request);
requirePlatform(user, customerId);
service.updatePallet(customerId, warehouseId, id, request);
return ApiResponse.success(null);
}
@GetMapping("/pallets")
public ApiResponse<PageResult<WarehousePalletEntity>> pallets(
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam @NotNull Long tenantId,
@AuthenticationPrincipal AuthenticatedUser user, @RequestParam @NotNull Long customerId,
@PathVariable long warehouseId, @Valid PalletRequests.QueryRequest query) {
requirePlatform(user, tenantId);
return ApiResponse.success(service.pallets(tenantId, warehouseId, query));
requirePlatform(user, customerId);
return ApiResponse.success(service.pallets(customerId, warehouseId, query));
}
@GetMapping("/pallets/{id}")
public ApiResponse<WarehousePalletEntity> pallet(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@PathVariable long warehouseId, @PathVariable long id) {
requirePlatform(user, tenantId);
return ApiResponse.success(service.pallet(tenantId, warehouseId, id));
requirePlatform(user, customerId);
return ApiResponse.success(service.pallet(customerId, warehouseId, id));
}
@DeleteMapping("/pallets/{id}")
public ApiResponse<Void> deletePallet(@AuthenticationPrincipal AuthenticatedUser user,
@RequestParam @NotNull Long tenantId,
@RequestParam @NotNull Long customerId,
@PathVariable long warehouseId, @PathVariable long id) {
requirePlatform(user, tenantId);
service.deletePallet(tenantId, warehouseId, id);
requirePlatform(user, customerId);
service.deletePallet(customerId, warehouseId, id);
return ApiResponse.success(null);
}
private static void requirePlatform(AuthenticatedUser user, Long tenantId) {
private static void requirePlatform(AuthenticatedUser user, Long customerId) {
if (user == null || !"PLATFORM".equals(user.appCode())) {
throw new BizException(ResultCode.FORBIDDEN, "仅允许平台端访问");
}
if (tenantId == null || tenantId <= 0) {
if (customerId == null || customerId <= 0) {
throw new BizException(ResultCode.BAD_REQUEST, "必须指定有效目标租户");
}
}
@@ -0,0 +1,9 @@
package com.baiyue.warehouse.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Positive;
public final class DropshipPalletRequests {
private DropshipPalletRequests() {}
public record Ensure(@Positive long customerId, @Positive long warehouseId, @NotBlank String businessId) {}
}
@@ -24,7 +24,7 @@ public final class WarehouseRequests {
@Schema(name = "WarehouseSaveRequest", description = "Warehouse create or update request / 仓库新增或更新请求")
public record SaveRequest(
@Schema(description = "Target tenant ID / 目标租户 ID", example = "10001", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull Long tenantId,
@NotNull Long customerId,
@Schema(description = "Tenant-unique warehouse code / 租户内唯一仓库编码", example = "US-LAX-01", maxLength = 50, requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank @Size(max = 50) String warehouseCode,
@Schema(description = "Warehouse name / 仓库名称", example = "Los Angeles Main Warehouse / 洛杉矶主仓", maxLength = 120, requiredMode = Schema.RequiredMode.REQUIRED)
@@ -7,7 +7,7 @@ public final class WarehouseResourceLockRequests {
private WarehouseResourceLockRequests() {}
public record ReserveRequest(
@Positive long tenantId,
@Positive long customerId,
@Positive long warehouseId,
@Positive long palletId,
@Positive long locationId,
@@ -16,10 +16,10 @@ public final class WarehouseResourceLockRequests {
@NotBlank String reservationKey
) {}
public record CloseRequest(@Positive long tenantId) {}
public record CloseRequest(@Positive long customerId) {}
public record DefaultLocationRequest(
@Positive long tenantId,
@Positive long customerId,
@Positive long warehouseId
) {}
}
@@ -6,7 +6,7 @@ import java.time.LocalDateTime;
public record WarehouseResourceLockResponse(
long lockId,
long tenantId,
long customerId,
long warehouseId,
long palletId,
long locationId,
@@ -18,7 +18,7 @@ public record WarehouseResourceLockResponse(
LocalDateTime releasedAt
) {
public static WarehouseResourceLockResponse from(WarehouseResourceLockEntity entity) {
return new WarehouseResourceLockResponse(entity.getId(), entity.getTenantId(), entity.getWarehouseId(),
return new WarehouseResourceLockResponse(entity.getId(), entity.getCustomerId(), entity.getWarehouseId(),
entity.getPalletId(), entity.getLocationId(), entity.getBusinessType(), entity.getBusinessId(),
entity.getReservationKey(), entity.getStatus(), entity.getLockedAt(), entity.getReleasedAt());
}
@@ -24,7 +24,7 @@ public class ServiceCapabilityEntity extends BaseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@Schema(description = "租户编号", example = "10001")
private Long tenantId;
private Long customerId;
@Schema(description = "仓库编号", example = "190000000000000001")
private Long warehouseId;
@Schema(description = "能力编码", example = "STORAGE")
@@ -24,7 +24,7 @@ public class WarehouseAddressEntity extends BaseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@Schema(description = "租户编号", example = "10001")
private Long tenantId;
private Long customerId;
@Schema(description = "仓库编号", example = "190000000000000001")
private Long warehouseId;
@Schema(description = "地址类型", example = "WAREHOUSE")
@@ -22,7 +22,7 @@ public class WarehouseEntity extends BaseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@Schema(description = "租户编号", example = "10001")
private Long tenantId;
private Long customerId;
@Schema(description = "仓库编码", example = "US-LAX-01")
private String warehouseCode;
@Schema(description = "仓库名称", example = "洛杉矶主仓")
@@ -15,7 +15,7 @@ import java.math.BigDecimal;
public class WarehouseLocationEntity extends BaseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private Long tenantId;
private Long customerId;
private Long warehouseId;
private String zoneCode;
private String locationCode;
@@ -0,0 +1,19 @@
package com.baiyue.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("wh_pallet_business_binding")
public class WarehousePalletBindingEntity {
@TableId(type = IdType.ASSIGN_ID) private Long id;
private Long customerId;
private Long warehouseId;
private String businessType;
private String businessId;
private Long palletId;
private LocalDateTime createdAt;
}
@@ -15,7 +15,7 @@ import java.math.BigDecimal;
public class WarehousePalletEntity extends BaseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private Long tenantId;
private Long customerId;
private Long warehouseId;
private Long locationId;
private String palletCode;
@@ -14,7 +14,7 @@ import java.time.LocalDateTime;
public class WarehouseResourceLockEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
private Long tenantId;
private Long customerId;
private Long warehouseId;
private Long palletId;
private Long locationId;
@@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Param;
*/
public interface ServiceCapabilityMapper extends BaseMapper<ServiceCapabilityEntity> {
@Delete("DELETE FROM wh_service_capability WHERE tenant_id = #{tenantId} AND warehouse_id = #{warehouseId}")
int physicallyDeleteByWarehouse(@Param("tenantId") long tenantId,
@Delete("DELETE FROM wh_service_capability WHERE customer_id = #{customerId} AND warehouse_id = #{warehouseId}")
int physicallyDeleteByWarehouse(@Param("customerId") long customerId,
@Param("warehouseId") long warehouseId);
}
@@ -10,7 +10,7 @@ import org.apache.ibatis.annotations.Param;
*/
public interface WarehouseAddressMapper extends BaseMapper<WarehouseAddressEntity> {
@Delete("DELETE FROM wh_warehouse_address WHERE tenant_id = #{tenantId} AND warehouse_id = #{warehouseId}")
int physicallyDeleteByWarehouse(@Param("tenantId") long tenantId,
@Delete("DELETE FROM wh_warehouse_address WHERE customer_id = #{customerId} AND warehouse_id = #{warehouseId}")
int physicallyDeleteByWarehouse(@Param("customerId") long customerId,
@Param("warehouseId") long warehouseId);
}
@@ -11,10 +11,10 @@ public interface WarehouseLocationMapper extends BaseMapper<WarehouseLocationEnt
*/
@Insert("""
INSERT IGNORE INTO wh_location
(id, tenant_id, warehouse_id, zone_code, location_code, location_type,
(id, customer_id, warehouse_id, zone_code, location_code, location_type,
max_load_kg, status, remark, deleted, version)
VALUES
(#{location.id}, #{location.tenantId}, #{location.warehouseId}, #{location.zoneCode},
(#{location.id}, #{location.customerId}, #{location.warehouseId}, #{location.zoneCode},
#{location.locationCode}, #{location.locationType}, #{location.maxLoadKg},
#{location.status}, #{location.remark}, 0, 0)
""")
@@ -0,0 +1,13 @@
package com.baiyue.warehouse.mapper;
import com.baiyue.warehouse.entity.WarehousePalletBindingEntity;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface WarehousePalletBindingMapper extends BaseMapper<WarehousePalletBindingEntity> {
@Select("SELECT * FROM wh_pallet_business_binding WHERE customer_id=#{customerId} AND warehouse_id=#{warehouseId} " +
"AND business_type=#{businessType} AND business_id=#{businessId} LIMIT 1")
WarehousePalletBindingEntity find(@Param("customerId") long customerId, @Param("warehouseId") long warehouseId,
@Param("businessType") String businessType, @Param("businessId") String businessId);
}
@@ -9,51 +9,51 @@ import org.apache.ibatis.annotations.Update;
public interface WarehouseResourceLockMapper extends BaseMapper<WarehouseResourceLockEntity> {
@Select("""
SELECT * FROM wh_resource_lock
WHERE tenant_id = #{tenantId} AND reservation_key = #{reservationKey}
WHERE customer_id = #{customerId} AND reservation_key = #{reservationKey}
LIMIT 1
""")
WarehouseResourceLockEntity findByReservationKey(@Param("tenantId") long tenantId,
WarehouseResourceLockEntity findByReservationKey(@Param("customerId") long customerId,
@Param("reservationKey") String reservationKey);
@Select("SELECT * FROM wh_resource_lock WHERE tenant_id=#{tenantId} AND pallet_id=#{palletId} " +
@Select("SELECT * FROM wh_resource_lock WHERE customer_id=#{customerId} AND pallet_id=#{palletId} " +
"AND status='ACTIVE' ORDER BY locked_at DESC LIMIT 1")
WarehouseResourceLockEntity findActiveByPallet(@Param("tenantId") long tenantId,
WarehouseResourceLockEntity findActiveByPallet(@Param("customerId") long customerId,
@Param("palletId") long palletId);
@Update("""
UPDATE wh_pallet
SET status = 'OCCUPIED', location_id = #{locationId}, updated_at = CURRENT_TIMESTAMP(3), version = version + 1
WHERE id = #{palletId} AND tenant_id = #{tenantId} AND warehouse_id = #{warehouseId}
WHERE id = #{palletId} AND customer_id = #{customerId} AND warehouse_id = #{warehouseId}
AND status = 'IDLE' AND deleted = 0
""")
int occupyPallet(@Param("tenantId") long tenantId, @Param("warehouseId") long warehouseId,
int occupyPallet(@Param("customerId") long customerId, @Param("warehouseId") long warehouseId,
@Param("palletId") long palletId, @Param("locationId") long locationId);
@Update("""
UPDATE wh_location
SET status = 'OCCUPIED', updated_at = CURRENT_TIMESTAMP(3), version = version + 1
WHERE id = #{locationId} AND tenant_id = #{tenantId} AND warehouse_id = #{warehouseId}
WHERE id = #{locationId} AND customer_id = #{customerId} AND warehouse_id = #{warehouseId}
AND status = 'IDLE' AND deleted = 0
""")
int occupyLocation(@Param("tenantId") long tenantId, @Param("warehouseId") long warehouseId,
int occupyLocation(@Param("customerId") long customerId, @Param("warehouseId") long warehouseId,
@Param("locationId") long locationId);
@Update("""
UPDATE wh_pallet
SET status = 'IDLE', location_id = NULL, updated_at = CURRENT_TIMESTAMP(3), version = version + 1
WHERE id = #{palletId} AND tenant_id = #{tenantId} AND warehouse_id = #{warehouseId}
WHERE id = #{palletId} AND customer_id = #{customerId} AND warehouse_id = #{warehouseId}
AND location_id = #{locationId} AND status = 'OCCUPIED' AND deleted = 0
""")
int releasePallet(@Param("tenantId") long tenantId, @Param("warehouseId") long warehouseId,
int releasePallet(@Param("customerId") long customerId, @Param("warehouseId") long warehouseId,
@Param("palletId") long palletId, @Param("locationId") long locationId);
@Update("""
UPDATE wh_pallet
SET status = 'IDLE', updated_at = CURRENT_TIMESTAMP(3), version = version + 1
WHERE id = #{palletId} AND tenant_id = #{tenantId} AND warehouse_id = #{warehouseId}
WHERE id = #{palletId} AND customer_id = #{customerId} AND warehouse_id = #{warehouseId}
AND location_id = #{locationId} AND status = 'OCCUPIED' AND deleted = 0
""")
int releasePalletAtVirtualLocation(@Param("tenantId") long tenantId,
int releasePalletAtVirtualLocation(@Param("customerId") long customerId,
@Param("warehouseId") long warehouseId,
@Param("palletId") long palletId,
@Param("locationId") long locationId);
@@ -61,35 +61,35 @@ public interface WarehouseResourceLockMapper extends BaseMapper<WarehouseResourc
@Update("""
UPDATE wh_location
SET status = 'IDLE', updated_at = CURRENT_TIMESTAMP(3), version = version + 1
WHERE id = #{locationId} AND tenant_id = #{tenantId} AND warehouse_id = #{warehouseId}
WHERE id = #{locationId} AND customer_id = #{customerId} AND warehouse_id = #{warehouseId}
AND status = 'OCCUPIED' AND deleted = 0
""")
int releaseLocation(@Param("tenantId") long tenantId, @Param("warehouseId") long warehouseId,
int releaseLocation(@Param("customerId") long customerId, @Param("warehouseId") long warehouseId,
@Param("locationId") long locationId);
@Update("""
UPDATE wh_pallet
SET status = 'SCRAPPED', location_id = NULL, updated_at = CURRENT_TIMESTAMP(3), version = version + 1
WHERE id = #{palletId} AND tenant_id = #{tenantId} AND warehouse_id = #{warehouseId}
WHERE id = #{palletId} AND customer_id = #{customerId} AND warehouse_id = #{warehouseId}
AND location_id = #{locationId} AND status = 'OCCUPIED' AND deleted = 0
""")
int scrapPallet(@Param("tenantId") long tenantId, @Param("warehouseId") long warehouseId,
int scrapPallet(@Param("customerId") long customerId, @Param("warehouseId") long warehouseId,
@Param("palletId") long palletId, @Param("locationId") long locationId);
@Update("""
UPDATE wh_pallet
SET status = 'SCRAPPED', updated_at = CURRENT_TIMESTAMP(3), version = version + 1
WHERE id = #{palletId} AND tenant_id = #{tenantId} AND warehouse_id = #{warehouseId}
WHERE id = #{palletId} AND customer_id = #{customerId} AND warehouse_id = #{warehouseId}
AND location_id = #{locationId} AND status = 'OCCUPIED' AND deleted = 0
""")
int scrapPalletAtVirtualLocation(@Param("tenantId") long tenantId,
int scrapPalletAtVirtualLocation(@Param("customerId") long customerId,
@Param("warehouseId") long warehouseId,
@Param("palletId") long palletId,
@Param("locationId") long locationId);
@Update("UPDATE wh_pallet SET status='SCRAPPED',updated_at=CURRENT_TIMESTAMP(3),version=version+1 " +
"WHERE tenant_id=#{tenantId} AND id=#{palletId} AND status='OCCUPIED' AND deleted=0")
int scrapPalletWithoutLock(@Param("tenantId") long tenantId, @Param("palletId") long palletId);
"WHERE customer_id=#{customerId} AND id=#{palletId} AND status='OCCUPIED' AND deleted=0")
int scrapPalletWithoutLock(@Param("customerId") long customerId, @Param("palletId") long palletId);
@Update("""
UPDATE wh_resource_lock
@@ -36,9 +36,9 @@ public class WarehouseRepository {
locations.insert(location);
}
public boolean existsLocationCode(long tenantId, long warehouseId, String locationCode) {
public boolean existsLocationCode(long customerId, long warehouseId, String locationCode) {
return locations.selectCount(new LambdaQueryWrapper<WarehouseLocationEntity>()
.eq(WarehouseLocationEntity::getTenantId, tenantId)
.eq(WarehouseLocationEntity::getCustomerId, customerId)
.eq(WarehouseLocationEntity::getWarehouseId, warehouseId)
.eq(WarehouseLocationEntity::getLocationCode, locationCode)) > 0;
}
@@ -47,22 +47,22 @@ public class WarehouseRepository {
warehouses.updateById(warehouse);
}
public WarehouseEntity findOwned(long tenantId, long id) {
public WarehouseEntity findOwned(long customerId, long id) {
return warehouses.selectOne(new LambdaQueryWrapper<WarehouseEntity>()
.eq(WarehouseEntity::getTenantId, tenantId)
.eq(WarehouseEntity::getCustomerId, customerId)
.eq(WarehouseEntity::getId, id));
}
public boolean existsCode(long tenantId, String code, Long excludedId) {
public boolean existsCode(long customerId, String code, Long excludedId) {
return warehouses.selectCount(new LambdaQueryWrapper<WarehouseEntity>()
.eq(WarehouseEntity::getTenantId, tenantId)
.eq(WarehouseEntity::getCustomerId, customerId)
.eq(WarehouseEntity::getWarehouseCode, code)
.ne(excludedId != null, WarehouseEntity::getId, excludedId)) > 0;
}
public PageResult<WarehouseEntity> page(long tenantId, WarehouseRequests.QueryRequest query) {
public PageResult<WarehouseEntity> page(long customerId, WarehouseRequests.QueryRequest query) {
LambdaQueryWrapper<WarehouseEntity> wrapper = new LambdaQueryWrapper<WarehouseEntity>()
.eq(WarehouseEntity::getTenantId, tenantId)
.eq(WarehouseEntity::getCustomerId, customerId)
.eq(StringUtils.hasText(query.status()), WarehouseEntity::getStatus, query.status())
.eq(StringUtils.hasText(query.countryCode()), WarehouseEntity::getCountryCode, query.countryCode())
.orderByDesc(WarehouseEntity::getCreatedAt);
@@ -70,7 +70,7 @@ public class WarehouseRepository {
if (StringUtils.hasText(query.capabilityCode())) {
List<Long> ids = capabilities.selectList(new LambdaQueryWrapper<ServiceCapabilityEntity>()
.select(ServiceCapabilityEntity::getWarehouseId)
.eq(ServiceCapabilityEntity::getTenantId, tenantId)
.eq(ServiceCapabilityEntity::getCustomerId, customerId)
.eq(ServiceCapabilityEntity::getCapabilityCode, query.capabilityCode())
.eq(ServiceCapabilityEntity::getEnabled, true))
.stream()
@@ -85,21 +85,21 @@ public class WarehouseRepository {
return PageResult.from(warehouses.selectPage(new Page<>(query.page(), query.size()), wrapper));
}
public List<WarehouseAddressEntity> findAddresses(long tenantId, long warehouseId) {
public List<WarehouseAddressEntity> findAddresses(long customerId, long warehouseId) {
return addresses.selectList(new LambdaQueryWrapper<WarehouseAddressEntity>()
.eq(WarehouseAddressEntity::getTenantId, tenantId)
.eq(WarehouseAddressEntity::getCustomerId, customerId)
.eq(WarehouseAddressEntity::getWarehouseId, warehouseId));
}
public List<ServiceCapabilityEntity> findCapabilities(long tenantId, long warehouseId) {
public List<ServiceCapabilityEntity> findCapabilities(long customerId, long warehouseId) {
return capabilities.selectList(new LambdaQueryWrapper<ServiceCapabilityEntity>()
.eq(ServiceCapabilityEntity::getTenantId, tenantId)
.eq(ServiceCapabilityEntity::getCustomerId, customerId)
.eq(ServiceCapabilityEntity::getWarehouseId, warehouseId));
}
public void physicallyDeleteChildren(long tenantId, long warehouseId) {
addresses.physicallyDeleteByWarehouse(tenantId, warehouseId);
capabilities.physicallyDeleteByWarehouse(tenantId, warehouseId);
public void physicallyDeleteChildren(long customerId, long warehouseId) {
addresses.physicallyDeleteByWarehouse(customerId, warehouseId);
capabilities.physicallyDeleteByWarehouse(customerId, warehouseId);
}
public void insertAddress(WarehouseAddressEntity address) {
@@ -16,74 +16,74 @@ public class WarehouseResourceLockRepository {
@Resource private WarehousePalletMapper pallets;
@Resource private WarehouseLocationMapper locations;
public WarehouseResourceLockEntity findByReservationKey(long tenantId, String reservationKey) {
return locks.findByReservationKey(tenantId, reservationKey);
public WarehouseResourceLockEntity findByReservationKey(long customerId, String reservationKey) {
return locks.findByReservationKey(customerId, reservationKey);
}
public WarehouseResourceLockEntity find(long tenantId, long lockId) {
public WarehouseResourceLockEntity find(long customerId, long lockId) {
return locks.selectOne(new LambdaQueryWrapper<WarehouseResourceLockEntity>()
.eq(WarehouseResourceLockEntity::getTenantId, tenantId)
.eq(WarehouseResourceLockEntity::getCustomerId, customerId)
.eq(WarehouseResourceLockEntity::getId, lockId));
}
public WarehouseResourceLockEntity findActiveByPallet(long tenantId, long palletId) {
return locks.findActiveByPallet(tenantId, palletId);
public WarehouseResourceLockEntity findActiveByPallet(long customerId, long palletId) {
return locks.findActiveByPallet(customerId, palletId);
}
public WarehousePalletEntity findPallet(long tenantId, long palletId) {
public WarehousePalletEntity findPallet(long customerId, long palletId) {
return pallets.selectOne(new LambdaQueryWrapper<WarehousePalletEntity>()
.eq(WarehousePalletEntity::getTenantId, tenantId)
.eq(WarehousePalletEntity::getCustomerId, customerId)
.eq(WarehousePalletEntity::getId, palletId));
}
public WarehousePalletEntity findPallet(long tenantId, long warehouseId, long palletId) {
public WarehousePalletEntity findPallet(long customerId, long warehouseId, long palletId) {
return pallets.selectOne(new LambdaQueryWrapper<WarehousePalletEntity>()
.eq(WarehousePalletEntity::getTenantId, tenantId)
.eq(WarehousePalletEntity::getCustomerId, customerId)
.eq(WarehousePalletEntity::getWarehouseId, warehouseId)
.eq(WarehousePalletEntity::getId, palletId));
}
public WarehouseLocationEntity findLocation(long tenantId, long warehouseId, long locationId) {
public WarehouseLocationEntity findLocation(long customerId, long warehouseId, long locationId) {
return locations.selectOne(new LambdaQueryWrapper<WarehouseLocationEntity>()
.eq(WarehouseLocationEntity::getTenantId, tenantId)
.eq(WarehouseLocationEntity::getCustomerId, customerId)
.eq(WarehouseLocationEntity::getWarehouseId, warehouseId)
.eq(WarehouseLocationEntity::getId, locationId));
}
public int occupyPallet(long tenantId, long warehouseId, long palletId, long locationId) {
return locks.occupyPallet(tenantId, warehouseId, palletId, locationId);
public int occupyPallet(long customerId, long warehouseId, long palletId, long locationId) {
return locks.occupyPallet(customerId, warehouseId, palletId, locationId);
}
public int occupyLocation(long tenantId, long warehouseId, long locationId) {
return locks.occupyLocation(tenantId, warehouseId, locationId);
public int occupyLocation(long customerId, long warehouseId, long locationId) {
return locks.occupyLocation(customerId, warehouseId, locationId);
}
public void insert(WarehouseResourceLockEntity lock) { locks.insert(lock); }
public int releasePallet(WarehouseResourceLockEntity lock) {
return locks.releasePallet(lock.getTenantId(), lock.getWarehouseId(), lock.getPalletId(), lock.getLocationId());
return locks.releasePallet(lock.getCustomerId(), lock.getWarehouseId(), lock.getPalletId(), lock.getLocationId());
}
public int releasePalletAtVirtualLocation(WarehouseResourceLockEntity lock) {
return locks.releasePalletAtVirtualLocation(lock.getTenantId(), lock.getWarehouseId(),
return locks.releasePalletAtVirtualLocation(lock.getCustomerId(), lock.getWarehouseId(),
lock.getPalletId(), lock.getLocationId());
}
public int scrapPallet(WarehouseResourceLockEntity lock) {
return locks.scrapPallet(lock.getTenantId(), lock.getWarehouseId(), lock.getPalletId(), lock.getLocationId());
return locks.scrapPallet(lock.getCustomerId(), lock.getWarehouseId(), lock.getPalletId(), lock.getLocationId());
}
public int scrapPalletWithoutLock(long tenantId, long palletId) {
return locks.scrapPalletWithoutLock(tenantId, palletId);
public int scrapPalletWithoutLock(long customerId, long palletId) {
return locks.scrapPalletWithoutLock(customerId, palletId);
}
public int scrapPalletAtVirtualLocation(WarehouseResourceLockEntity lock) {
return locks.scrapPalletAtVirtualLocation(lock.getTenantId(), lock.getWarehouseId(),
return locks.scrapPalletAtVirtualLocation(lock.getCustomerId(), lock.getWarehouseId(),
lock.getPalletId(), lock.getLocationId());
}
public int releaseLocation(WarehouseResourceLockEntity lock) {
return locks.releaseLocation(lock.getTenantId(), lock.getWarehouseId(), lock.getLocationId());
return locks.releaseLocation(lock.getCustomerId(), lock.getWarehouseId(), lock.getLocationId());
}
public int close(WarehouseResourceLockEntity lock, String status) { return locks.closeLock(lock.getId(), status); }
@@ -11,6 +11,8 @@ import com.baiyue.warehouse.entity.WarehousePalletEntity;
import com.baiyue.warehouse.mapper.WarehouseLocationMapper;
import com.baiyue.warehouse.mapper.WarehouseMapper;
import com.baiyue.warehouse.mapper.WarehousePalletMapper;
import com.baiyue.warehouse.mapper.WarehousePalletBindingMapper;
import com.baiyue.warehouse.entity.WarehousePalletBindingEntity;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;
import jakarta.annotation.Resource;
@@ -20,22 +22,32 @@ public class WarehouseResourceRepository {
@Resource private WarehouseMapper warehouses;
@Resource private WarehouseLocationMapper locations;
@Resource private WarehousePalletMapper pallets;
@Resource private WarehousePalletBindingMapper bindings;
public boolean warehouseOwned(long tenantId, long warehouseId) {
public WarehousePalletBindingEntity findPalletBinding(long customerId, long warehouseId,
String businessType, String businessId) {
return bindings.find(customerId, warehouseId, businessType, businessId);
}
public void insertPalletBinding(WarehousePalletBindingEntity entity) {
bindings.insert(entity);
}
public boolean warehouseOwned(long customerId, long warehouseId) {
return warehouses.selectCount(new LambdaQueryWrapper<WarehouseEntity>()
.eq(WarehouseEntity::getTenantId, tenantId)
.eq(WarehouseEntity::getCustomerId, customerId)
.eq(WarehouseEntity::getId, warehouseId)) > 0;
}
public WarehouseEntity findWarehouse(long tenantId, long warehouseId) {
public WarehouseEntity findWarehouse(long customerId, long warehouseId) {
return warehouses.selectOne(new LambdaQueryWrapper<WarehouseEntity>()
.eq(WarehouseEntity::getTenantId, tenantId)
.eq(WarehouseEntity::getCustomerId, customerId)
.eq(WarehouseEntity::getId, warehouseId));
}
public WarehouseLocationEntity findDefaultVirtualLocation(long tenantId, long warehouseId) {
public WarehouseLocationEntity findDefaultVirtualLocation(long customerId, long warehouseId) {
return locations.selectOne(new LambdaQueryWrapper<WarehouseLocationEntity>()
.eq(WarehouseLocationEntity::getTenantId, tenantId)
.eq(WarehouseLocationEntity::getCustomerId, customerId)
.eq(WarehouseLocationEntity::getWarehouseId, warehouseId)
.eq(WarehouseLocationEntity::getLocationType, "VIRTUAL")
.eq(WarehouseLocationEntity::getStatus, "ACTIVE")
@@ -43,9 +55,9 @@ public class WarehouseResourceRepository {
.last("LIMIT 1"));
}
public WarehouseLocationEntity findLocationByCode(long tenantId, long warehouseId, String locationCode) {
public WarehouseLocationEntity findLocationByCode(long customerId, long warehouseId, String locationCode) {
return locations.selectOne(new LambdaQueryWrapper<WarehouseLocationEntity>()
.eq(WarehouseLocationEntity::getTenantId, tenantId)
.eq(WarehouseLocationEntity::getCustomerId, customerId)
.eq(WarehouseLocationEntity::getWarehouseId, warehouseId)
.eq(WarehouseLocationEntity::getLocationCode, locationCode));
}
@@ -62,25 +74,25 @@ public class WarehouseResourceRepository {
locations.updateById(entity);
}
public WarehouseLocationEntity findLocation(long tenantId, long warehouseId, long id) {
public WarehouseLocationEntity findLocation(long customerId, long warehouseId, long id) {
return locations.selectOne(new LambdaQueryWrapper<WarehouseLocationEntity>()
.eq(WarehouseLocationEntity::getTenantId, tenantId)
.eq(WarehouseLocationEntity::getCustomerId, customerId)
.eq(WarehouseLocationEntity::getWarehouseId, warehouseId)
.eq(WarehouseLocationEntity::getId, id));
}
public boolean locationCodeExists(long tenantId, long warehouseId, String code, Long excludedId) {
public boolean locationCodeExists(long customerId, long warehouseId, String code, Long excludedId) {
return locations.selectCount(new LambdaQueryWrapper<WarehouseLocationEntity>()
.eq(WarehouseLocationEntity::getTenantId, tenantId)
.eq(WarehouseLocationEntity::getCustomerId, customerId)
.eq(WarehouseLocationEntity::getWarehouseId, warehouseId)
.eq(WarehouseLocationEntity::getLocationCode, code)
.ne(excludedId != null, WarehouseLocationEntity::getId, excludedId)) > 0;
}
public PageResult<WarehouseLocationEntity> pageLocations(long tenantId, long warehouseId,
public PageResult<WarehouseLocationEntity> pageLocations(long customerId, long warehouseId,
LocationRequests.QueryRequest query) {
var wrapper = new LambdaQueryWrapper<WarehouseLocationEntity>()
.eq(WarehouseLocationEntity::getTenantId, tenantId)
.eq(WarehouseLocationEntity::getCustomerId, customerId)
.eq(WarehouseLocationEntity::getWarehouseId, warehouseId)
.eq(StringUtils.hasText(query.zoneCode()), WarehouseLocationEntity::getZoneCode, query.zoneCode())
.eq(StringUtils.hasText(query.locationType()), WarehouseLocationEntity::getLocationType, query.locationType())
@@ -97,9 +109,9 @@ public class WarehouseResourceRepository {
locations.deleteById(id);
}
public boolean locationHasPallets(long tenantId, long warehouseId, long locationId) {
public boolean locationHasPallets(long customerId, long warehouseId, long locationId) {
return pallets.selectCount(new LambdaQueryWrapper<WarehousePalletEntity>()
.eq(WarehousePalletEntity::getTenantId, tenantId)
.eq(WarehousePalletEntity::getCustomerId, customerId)
.eq(WarehousePalletEntity::getWarehouseId, warehouseId)
.eq(WarehousePalletEntity::getLocationId, locationId)) > 0;
}
@@ -112,25 +124,25 @@ public class WarehouseResourceRepository {
pallets.updateById(entity);
}
public WarehousePalletEntity findPallet(long tenantId, long warehouseId, long id) {
public WarehousePalletEntity findPallet(long customerId, long warehouseId, long id) {
return pallets.selectOne(new LambdaQueryWrapper<WarehousePalletEntity>()
.eq(WarehousePalletEntity::getTenantId, tenantId)
.eq(WarehousePalletEntity::getCustomerId, customerId)
.eq(WarehousePalletEntity::getWarehouseId, warehouseId)
.eq(WarehousePalletEntity::getId, id));
}
public boolean palletCodeExists(long tenantId, long warehouseId, String code, Long excludedId) {
public boolean palletCodeExists(long customerId, long warehouseId, String code, Long excludedId) {
return pallets.selectCount(new LambdaQueryWrapper<WarehousePalletEntity>()
.eq(WarehousePalletEntity::getTenantId, tenantId)
.eq(WarehousePalletEntity::getCustomerId, customerId)
.eq(WarehousePalletEntity::getWarehouseId, warehouseId)
.eq(WarehousePalletEntity::getPalletCode, code)
.ne(excludedId != null, WarehousePalletEntity::getId, excludedId)) > 0;
}
public PageResult<WarehousePalletEntity> pagePallets(long tenantId, long warehouseId,
public PageResult<WarehousePalletEntity> pagePallets(long customerId, long warehouseId,
PalletRequests.QueryRequest query) {
var wrapper = new LambdaQueryWrapper<WarehousePalletEntity>()
.eq(WarehousePalletEntity::getTenantId, tenantId)
.eq(WarehousePalletEntity::getCustomerId, customerId)
.eq(WarehousePalletEntity::getWarehouseId, warehouseId)
.eq(StringUtils.hasText(query.palletType()), WarehousePalletEntity::getPalletType, query.palletType())
.eq(StringUtils.hasText(query.status()), WarehousePalletEntity::getStatus, query.status())
@@ -36,21 +36,21 @@ public class WarehouseApplicationService {
captureBefore = false
)
public Long create(WarehouseRequests.SaveRequest request) {
ensureCodeUnique(request.tenantId(), request.warehouseCode(), null);
ensureCodeUnique(request.customerId(), request.warehouseCode(), null);
WarehouseEntity warehouse = toEntity(request);
repository.insertWarehouse(warehouse);
ensureDefaultVirtualLocation(warehouse);
insertChildren(warehouse.getTenantId(), warehouse.getId(), request.address(), request.capabilities());
insertChildren(warehouse.getCustomerId(), warehouse.getId(), request.address(), request.capabilities());
return warehouse.getId();
}
private void ensureDefaultVirtualLocation(WarehouseEntity warehouse) {
String locationCode = "LOC-" + warehouse.getWarehouseCode() + "-DEFAULT";
if (repository.existsLocationCode(warehouse.getTenantId(), warehouse.getId(), locationCode)) {
if (repository.existsLocationCode(warehouse.getCustomerId(), warehouse.getId(), locationCode)) {
return;
}
WarehouseLocationEntity location = new WarehouseLocationEntity();
location.setTenantId(warehouse.getTenantId());
location.setCustomerId(warehouse.getCustomerId());
location.setWarehouseId(warehouse.getId());
location.setZoneCode("DEFAULT");
location.setLocationCode(locationCode);
@@ -68,29 +68,29 @@ public class WarehouseApplicationService {
resourceId = "#id",
description = "更新仓库"
)
public void update(long tenantId, long id, WarehouseRequests.SaveRequest request) {
if (request.tenantId() == null || tenantId != request.tenantId()) {
public void update(long customerId, long id, WarehouseRequests.SaveRequest request) {
if (request.customerId() == null || customerId != request.customerId()) {
throw new BizException(ResultCode.FORBIDDEN, "租户不匹配");
}
WarehouseEntity old = owned(tenantId, id);
ensureCodeUnique(tenantId, request.warehouseCode(), id);
WarehouseEntity old = owned(customerId, id);
ensureCodeUnique(customerId, request.warehouseCode(), id);
WarehouseEntity warehouse = toEntity(request);
warehouse.setId(old.getId());
repository.saveWarehouse(warehouse);
repository.physicallyDeleteChildren(tenantId, id);
insertChildren(tenantId, id, request.address(), request.capabilities());
repository.physicallyDeleteChildren(customerId, id);
insertChildren(customerId, id, request.address(), request.capabilities());
}
public PageResult<WarehouseEntity> page(long tenantId, WarehouseRequests.QueryRequest query) {
return repository.page(tenantId, query);
public PageResult<WarehouseEntity> page(long customerId, WarehouseRequests.QueryRequest query) {
return repository.page(customerId, query);
}
public WarehouseDetailResponse detail(long tenantId, long id) {
WarehouseEntity warehouse = owned(tenantId, id);
List<WarehouseAddressEntity> addresses = repository.findAddresses(tenantId, id);
List<ServiceCapabilityEntity> capabilities = repository.findCapabilities(tenantId, id);
public WarehouseDetailResponse detail(long customerId, long id) {
WarehouseEntity warehouse = owned(customerId, id);
List<WarehouseAddressEntity> addresses = repository.findAddresses(customerId, id);
List<ServiceCapabilityEntity> capabilities = repository.findCapabilities(customerId, id);
return new WarehouseDetailResponse(warehouse, addresses, capabilities);
}
@@ -101,32 +101,32 @@ public class WarehouseApplicationService {
resourceId = "#id",
description = "启用或停用仓库"
)
public void changeStatus(long tenantId, long id, String status) {
public void changeStatus(long customerId, long id, String status) {
if (!"ACTIVE".equals(status) && !"INACTIVE".equals(status)) {
throw new BizException("无效状态");
}
WarehouseEntity warehouse = owned(tenantId, id);
WarehouseEntity warehouse = owned(customerId, id);
warehouse.setStatus(status);
repository.saveWarehouse(warehouse);
}
private WarehouseEntity owned(long tenantId, long id) {
WarehouseEntity warehouse = repository.findOwned(tenantId, id);
private WarehouseEntity owned(long customerId, long id) {
WarehouseEntity warehouse = repository.findOwned(customerId, id);
if (warehouse == null) {
throw new BizException(ResultCode.NOT_FOUND, "仓库不存在");
}
return warehouse;
}
private void ensureCodeUnique(long tenantId, String code, Long excludedId) {
if (repository.existsCode(tenantId, code, excludedId)) {
private void ensureCodeUnique(long customerId, String code, Long excludedId) {
if (repository.existsCode(customerId, code, excludedId)) {
throw new BizException(ResultCode.CONFLICT, "仓库编码已存在");
}
}
private WarehouseEntity toEntity(WarehouseRequests.SaveRequest request) {
WarehouseEntity warehouse = new WarehouseEntity();
warehouse.setTenantId(request.tenantId());
warehouse.setCustomerId(request.customerId());
warehouse.setWarehouseCode(request.warehouseCode());
warehouse.setWarehouseName(request.warehouseName());
warehouse.setWarehouseType(request.warehouseType());
@@ -140,11 +140,11 @@ public class WarehouseApplicationService {
return warehouse;
}
private void insertChildren(long tenantId, long warehouseId,
private void insertChildren(long customerId, long warehouseId,
WarehouseRequests.AddressRequest address,
List<WarehouseRequests.CapabilityRequest> requestedCapabilities) {
WarehouseAddressEntity addressEntity = new WarehouseAddressEntity();
addressEntity.setTenantId(tenantId);
addressEntity.setCustomerId(customerId);
addressEntity.setWarehouseId(warehouseId);
addressEntity.setAddressType(address.addressType());
addressEntity.setCountryCode(address.countryCode());
@@ -163,7 +163,7 @@ public class WarehouseApplicationService {
}
for (WarehouseRequests.CapabilityRequest capability : requestedCapabilities) {
ServiceCapabilityEntity entity = new ServiceCapabilityEntity();
entity.setTenantId(tenantId);
entity.setCustomerId(customerId);
entity.setWarehouseId(warehouseId);
entity.setCapabilityCode(capability.capabilityCode());
entity.setCapabilityName(capability.capabilityName());
@@ -23,14 +23,14 @@ public class WarehouseResourceLockService {
@Transactional(isolation = Isolation.READ_COMMITTED)
public WarehouseResourceLockResponse reserve(WarehouseResourceLockRequests.ReserveRequest request) {
WarehouseResourceLockEntity existing = repository.findByReservationKey(request.tenantId(), request.reservationKey());
WarehouseResourceLockEntity existing = repository.findByReservationKey(request.customerId(), request.reservationKey());
if (existing != null) return idempotentResult(existing, request);
WarehousePalletEntity pallet = repository.findPallet(request.tenantId(), request.warehouseId(), request.palletId());
WarehouseLocationEntity location = repository.findLocation(request.tenantId(), request.warehouseId(), request.locationId());
WarehousePalletEntity pallet = repository.findPallet(request.customerId(), request.warehouseId(), request.palletId());
WarehouseLocationEntity location = repository.findLocation(request.customerId(), request.warehouseId(), request.locationId());
if (pallet == null || location == null) {
WarehouseResourceLockEntity concurrent =
repository.findByReservationKey(request.tenantId(), request.reservationKey());
repository.findByReservationKey(request.customerId(), request.reservationKey());
if (concurrent != null) return idempotentResult(concurrent, request);
throw new BizException(ResultCode.NOT_FOUND, "Pallet or location does not belong to the tenant warehouse");
}
@@ -38,19 +38,19 @@ public class WarehouseResourceLockService {
boolean locationAvailable = virtualLocation ? "ACTIVE".equals(location.getStatus()) : "IDLE".equals(location.getStatus());
if (!"IDLE".equals(pallet.getStatus()) || !locationAvailable) {
WarehouseResourceLockEntity concurrent =
repository.findByReservationKey(request.tenantId(), request.reservationKey());
repository.findByReservationKey(request.customerId(), request.reservationKey());
if (concurrent != null) return idempotentResult(concurrent, request);
throw new BizException(ResultCode.CONFLICT, "Only IDLE pallet and location can be reserved");
}
if (repository.occupyPallet(request.tenantId(), request.warehouseId(), request.palletId(), request.locationId()) != 1) {
if (repository.occupyPallet(request.customerId(), request.warehouseId(), request.palletId(), request.locationId()) != 1) {
return concurrentIdempotentOrConflict(request);
}
if (!virtualLocation && repository.occupyLocation(request.tenantId(), request.warehouseId(), request.locationId()) != 1) {
if (!virtualLocation && repository.occupyLocation(request.customerId(), request.warehouseId(), request.locationId()) != 1) {
throw new BizException(ResultCode.CONFLICT, "Location is no longer IDLE");
}
WarehouseResourceLockEntity lock = new WarehouseResourceLockEntity();
lock.setTenantId(request.tenantId());
lock.setCustomerId(request.customerId());
lock.setWarehouseId(request.warehouseId());
lock.setPalletId(request.palletId());
lock.setLocationId(request.locationId());
@@ -64,8 +64,8 @@ public class WarehouseResourceLockService {
}
@Transactional
public WarehouseResourceLockResponse release(long tenantId, long lockId) {
WarehouseResourceLockEntity lock = requireLock(tenantId, lockId);
public WarehouseResourceLockResponse release(long customerId, long lockId) {
WarehouseResourceLockEntity lock = requireLock(customerId, lockId);
if ("RELEASED".equals(lock.getStatus())) return WarehouseResourceLockResponse.from(lock);
requireActive(lock);
boolean virtualLocation = isVirtualLockLocation(lock);
@@ -80,8 +80,8 @@ public class WarehouseResourceLockService {
}
@Transactional
public WarehouseResourceLockResponse scrap(long tenantId, long lockId) {
WarehouseResourceLockEntity lock = requireLock(tenantId, lockId);
public WarehouseResourceLockResponse scrap(long customerId, long lockId) {
WarehouseResourceLockEntity lock = requireLock(customerId, lockId);
if ("SCRAPPED".equals(lock.getStatus())) return WarehouseResourceLockResponse.from(lock);
requireActive(lock);
boolean virtualLocation = isVirtualLockLocation(lock);
@@ -96,8 +96,8 @@ public class WarehouseResourceLockService {
}
@Transactional
public WarehousePalletScrapResponse scrapByPallet(long tenantId, long palletId) {
WarehousePalletEntity pallet = repository.findPallet(tenantId, palletId);
public WarehousePalletScrapResponse scrapByPallet(long customerId, long palletId) {
WarehousePalletEntity pallet = repository.findPallet(customerId, palletId);
if (pallet == null) throw new BizException(ResultCode.NOT_FOUND, "Pallet not found");
if ("SCRAPPED".equals(pallet.getStatus())) {
return new WarehousePalletScrapResponse(palletId, pallet.getWarehouseId(), pallet.getLocationId(), "SCRAPPED");
@@ -105,10 +105,10 @@ public class WarehouseResourceLockService {
if (!"OCCUPIED".equals(pallet.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "Only an OCCUPIED pallet can be scrapped after shipment");
}
WarehouseResourceLockEntity lock = repository.findActiveByPallet(tenantId, palletId);
WarehouseResourceLockEntity lock = repository.findActiveByPallet(customerId, palletId);
if (lock != null) {
scrap(tenantId, lock.getId());
} else if (repository.scrapPalletWithoutLock(tenantId, palletId) != 1) {
scrap(customerId, lock.getId());
} else if (repository.scrapPalletWithoutLock(customerId, palletId) != 1) {
throw new BizException(ResultCode.CONFLICT, "Pallet state changed concurrently");
}
return new WarehousePalletScrapResponse(palletId, pallet.getWarehouseId(), pallet.getLocationId(), "SCRAPPED");
@@ -116,7 +116,7 @@ public class WarehouseResourceLockService {
private WarehouseResourceLockResponse concurrentIdempotentOrConflict(
WarehouseResourceLockRequests.ReserveRequest request) {
WarehouseResourceLockEntity existing = repository.findByReservationKey(request.tenantId(), request.reservationKey());
WarehouseResourceLockEntity existing = repository.findByReservationKey(request.customerId(), request.reservationKey());
if (existing != null) return idempotentResult(existing, request);
throw new BizException(ResultCode.CONFLICT, "Pallet is no longer IDLE");
}
@@ -133,8 +133,8 @@ public class WarehouseResourceLockService {
return WarehouseResourceLockResponse.from(existing);
}
private WarehouseResourceLockEntity requireLock(long tenantId, long lockId) {
WarehouseResourceLockEntity lock = repository.find(tenantId, lockId);
private WarehouseResourceLockEntity requireLock(long customerId, long lockId) {
WarehouseResourceLockEntity lock = repository.find(customerId, lockId);
if (lock == null) throw new BizException(ResultCode.NOT_FOUND, "Resource lock not found");
return lock;
}
@@ -147,7 +147,7 @@ public class WarehouseResourceLockService {
private boolean isVirtualLockLocation(WarehouseResourceLockEntity lock) {
WarehouseLocationEntity location = repository.findLocation(lock.getTenantId(), lock.getWarehouseId(), lock.getLocationId());
WarehouseLocationEntity location = repository.findLocation(lock.getCustomerId(), lock.getWarehouseId(), lock.getLocationId());
if (location == null) throw new BizException(ResultCode.NOT_FOUND, "Resource lock location not found");
return isVirtual(location);
}
@@ -8,6 +8,7 @@ import com.baiyue.warehouse.dto.PalletRequests;
import com.baiyue.warehouse.entity.WarehouseLocationEntity;
import com.baiyue.warehouse.entity.WarehousePalletEntity;
import com.baiyue.warehouse.entity.WarehouseEntity;
import com.baiyue.warehouse.entity.WarehousePalletBindingEntity;
import com.baiyue.warehouse.repository.WarehouseResourceRepository;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import org.springframework.stereotype.Service;
@@ -27,78 +28,99 @@ public class WarehouseResourceService {
@Resource private WarehouseResourceRepository repository;
@Transactional
public Long createLocation(long tenantId, long warehouseId, LocationRequests.SaveRequest request) {
requireWarehouse(tenantId, warehouseId);
validateLocation(request, tenantId, warehouseId, null);
WarehouseLocationEntity entity = locationEntity(tenantId, warehouseId, request);
public Long createLocation(long customerId, long warehouseId, LocationRequests.SaveRequest request) {
requireWarehouse(customerId, warehouseId);
validateLocation(request, customerId, warehouseId, null);
WarehouseLocationEntity entity = locationEntity(customerId, warehouseId, request);
repository.insertLocation(entity);
return entity.getId();
}
@Transactional
public void updateLocation(long tenantId, long warehouseId, long id, LocationRequests.SaveRequest request) {
WarehouseLocationEntity existing = requireLocation(tenantId, warehouseId, id);
public void updateLocation(long customerId, long warehouseId, long id, LocationRequests.SaveRequest request) {
WarehouseLocationEntity existing = requireLocation(customerId, warehouseId, id);
if ("VIRTUAL".equals(existing.getLocationType())
&& (!"VIRTUAL".equals(request.locationType()) || !"ACTIVE".equals(request.status())
|| !existing.getLocationCode().equals(request.locationCode()))) {
throw new BizException(ResultCode.CONFLICT, "Default virtual location must remain VIRTUAL and ACTIVE");
}
validateLocation(request, tenantId, warehouseId, id);
WarehouseLocationEntity entity = locationEntity(tenantId, warehouseId, request);
validateLocation(request, customerId, warehouseId, id);
WarehouseLocationEntity entity = locationEntity(customerId, warehouseId, request);
entity.setId(existing.getId());
repository.updateLocation(entity);
}
public WarehouseLocationEntity location(long tenantId, long warehouseId, long id) {
requireWarehouse(tenantId, warehouseId);
return requireLocation(tenantId, warehouseId, id);
public WarehouseLocationEntity location(long customerId, long warehouseId, long id) {
requireWarehouse(customerId, warehouseId);
return requireLocation(customerId, warehouseId, id);
}
public PageResult<WarehouseLocationEntity> locations(long tenantId, long warehouseId,
public PageResult<WarehouseLocationEntity> locations(long customerId, long warehouseId,
LocationRequests.QueryRequest query) {
requireWarehouse(tenantId, warehouseId);
return repository.pageLocations(tenantId, warehouseId, query);
requireWarehouse(customerId, warehouseId);
return repository.pageLocations(customerId, warehouseId, query);
}
@Transactional
public void deleteLocation(long tenantId, long warehouseId, long id) {
WarehouseLocationEntity location = requireLocation(tenantId, warehouseId, id);
public void deleteLocation(long customerId, long warehouseId, long id) {
WarehouseLocationEntity location = requireLocation(customerId, warehouseId, id);
if ("VIRTUAL".equals(location.getLocationType())) {
throw new BizException(ResultCode.CONFLICT, "Default virtual location cannot be deleted");
}
if (repository.locationHasPallets(tenantId, warehouseId, id)) {
if (repository.locationHasPallets(customerId, warehouseId, id)) {
throw new BizException(ResultCode.CONFLICT, "库位仍绑定托盘,不能删除");
}
repository.deleteLocation(id);
}
@Transactional
public Long createPallet(long tenantId, long warehouseId, PalletRequests.SaveRequest request) {
WarehouseEntity warehouse = requireWarehouseEntity(tenantId, warehouseId);
public Long createPallet(long customerId, long warehouseId, PalletRequests.SaveRequest request) {
WarehouseEntity warehouse = requireWarehouseEntity(customerId, warehouseId);
validatePalletStatus(request.status());
Long palletId = IdWorker.getId();
String palletCode = StringUtils.hasText(request.palletCode()) ? request.palletCode().trim()
: generatedPalletCode(warehouse.getWarehouseCode(), palletId);
Long locationId = request.locationId();
if (locationId == null) {
locationId = ensureDefaultVirtualLocation(tenantId, warehouseId).getId();
locationId = ensureDefaultVirtualLocation(customerId, warehouseId).getId();
}
validatePallet(request, palletCode, locationId, tenantId, warehouseId, null);
WarehousePalletEntity entity = palletEntity(tenantId, warehouseId, request, palletCode, locationId);
validatePallet(request, palletCode, locationId, customerId, warehouseId, null);
WarehousePalletEntity entity = palletEntity(customerId, warehouseId, request, palletCode, locationId);
entity.setId(palletId);
repository.insertPallet(entity);
return entity.getId();
}
@Transactional
public WarehousePalletEntity ensureDropshipPallet(long customerId, long warehouseId, String businessId) {
WarehousePalletBindingEntity existing = repository.findPalletBinding(customerId, warehouseId, "DROPSHIP", businessId);
if (existing != null) return requirePallet(customerId, warehouseId, existing.getPalletId());
Long palletId = createPallet(customerId, warehouseId,
new PalletRequests.SaveRequest(null, "DROPSHIP", null, java.math.BigDecimal.ZERO, "IDLE", null,
"Default dropship pallet for " + businessId));
WarehousePalletBindingEntity binding = new WarehousePalletBindingEntity();
binding.setId(IdWorker.getId()); binding.setCustomerId(customerId); binding.setWarehouseId(warehouseId);
binding.setBusinessType("DROPSHIP"); binding.setBusinessId(businessId); binding.setPalletId(palletId);
binding.setCreatedAt(java.time.LocalDateTime.now());
try {
repository.insertPalletBinding(binding);
return requirePallet(customerId, warehouseId, palletId);
} catch (org.springframework.dao.DuplicateKeyException duplicate) {
WarehousePalletBindingEntity winner = repository.findPalletBinding(customerId, warehouseId, "DROPSHIP", businessId);
if (winner == null) throw duplicate;
return requirePallet(customerId, warehouseId, winner.getPalletId());
}
}
/**
* Returns the warehouse-scoped virtual location used only as a pallet/inventory reference.
* It is deliberately never made IDLE/OCCUPIED by resource locking.
*/
@Transactional
public WarehouseLocationEntity ensureDefaultVirtualLocation(long tenantId, long warehouseId) {
WarehouseEntity warehouse = requireWarehouseEntity(tenantId, warehouseId);
public WarehouseLocationEntity ensureDefaultVirtualLocation(long customerId, long warehouseId) {
WarehouseEntity warehouse = requireWarehouseEntity(customerId, warehouseId);
String locationCode = defaultLocationCode(warehouse.getWarehouseCode());
WarehouseLocationEntity existing = repository.findLocationByCode(tenantId, warehouseId, locationCode);
WarehouseLocationEntity existing = repository.findLocationByCode(customerId, warehouseId, locationCode);
if (existing != null) {
if (!"VIRTUAL".equals(existing.getLocationType()) || !"ACTIVE".equals(existing.getStatus())) {
throw new BizException(ResultCode.CONFLICT,
@@ -109,7 +131,7 @@ public class WarehouseResourceService {
WarehouseLocationEntity location = new WarehouseLocationEntity();
location.setId(IdWorker.getId());
location.setTenantId(tenantId);
location.setCustomerId(customerId);
location.setWarehouseId(warehouseId);
location.setZoneCode("DEFAULT");
location.setLocationCode(locationCode);
@@ -119,7 +141,7 @@ public class WarehouseResourceService {
location.setRemark("System default virtual location");
repository.insertDefaultVirtualLocationIgnore(location);
WarehouseLocationEntity resolved = repository.findLocationByCode(tenantId, warehouseId, locationCode);
WarehouseLocationEntity resolved = repository.findLocationByCode(customerId, warehouseId, locationCode);
if (resolved == null) {
throw new BizException(ResultCode.CONFLICT, "Default virtual location could not be created");
}
@@ -127,34 +149,34 @@ public class WarehouseResourceService {
}
@Transactional
public void updatePallet(long tenantId, long warehouseId, long id, PalletRequests.SaveRequest request) {
WarehousePalletEntity existing = requirePallet(tenantId, warehouseId, id);
public void updatePallet(long customerId, long warehouseId, long id, PalletRequests.SaveRequest request) {
WarehousePalletEntity existing = requirePallet(customerId, warehouseId, id);
if ("SCRAPPED".equals(existing.getStatus()) && !"SCRAPPED".equals(request.status())) {
throw new BizException(ResultCode.CONFLICT, "SCRAPPED pallet cannot be reused");
}
String palletCode = StringUtils.hasText(request.palletCode()) ? request.palletCode().trim()
: existing.getPalletCode();
Long locationId = request.locationId() == null ? existing.getLocationId() : request.locationId();
validatePallet(request, palletCode, locationId, tenantId, warehouseId, id);
WarehousePalletEntity entity = palletEntity(tenantId, warehouseId, request, palletCode, locationId);
validatePallet(request, palletCode, locationId, customerId, warehouseId, id);
WarehousePalletEntity entity = palletEntity(customerId, warehouseId, request, palletCode, locationId);
entity.setId(existing.getId());
repository.updatePallet(entity);
}
public WarehousePalletEntity pallet(long tenantId, long warehouseId, long id) {
requireWarehouse(tenantId, warehouseId);
return requirePallet(tenantId, warehouseId, id);
public WarehousePalletEntity pallet(long customerId, long warehouseId, long id) {
requireWarehouse(customerId, warehouseId);
return requirePallet(customerId, warehouseId, id);
}
public PageResult<WarehousePalletEntity> pallets(long tenantId, long warehouseId,
public PageResult<WarehousePalletEntity> pallets(long customerId, long warehouseId,
PalletRequests.QueryRequest query) {
requireWarehouse(tenantId, warehouseId);
return repository.pagePallets(tenantId, warehouseId, query);
requireWarehouse(customerId, warehouseId);
return repository.pagePallets(customerId, warehouseId, query);
}
@Transactional
public void deletePallet(long tenantId, long warehouseId, long id) {
WarehousePalletEntity pallet = requirePallet(tenantId, warehouseId, id);
public void deletePallet(long customerId, long warehouseId, long id) {
WarehousePalletEntity pallet = requirePallet(customerId, warehouseId, id);
if ("OCCUPIED".equals(pallet.getStatus())) {
throw new BizException(ResultCode.CONFLICT, "占用中的托盘不能删除");
}
@@ -164,52 +186,52 @@ public class WarehouseResourceService {
repository.deletePallet(id);
}
private void requireWarehouse(long tenantId, long warehouseId) {
if (!repository.warehouseOwned(tenantId, warehouseId)) {
private void requireWarehouse(long customerId, long warehouseId) {
if (!repository.warehouseOwned(customerId, warehouseId)) {
throw new BizException(ResultCode.NOT_FOUND, "当前租户下仓库不存在");
}
}
private WarehouseEntity requireWarehouseEntity(long tenantId, long warehouseId) {
WarehouseEntity warehouse = repository.findWarehouse(tenantId, warehouseId);
private WarehouseEntity requireWarehouseEntity(long customerId, long warehouseId) {
WarehouseEntity warehouse = repository.findWarehouse(customerId, warehouseId);
if (warehouse == null) {
throw new BizException(ResultCode.NOT_FOUND, "Tenant warehouse not found");
}
return warehouse;
}
private WarehouseLocationEntity requireLocation(long tenantId, long warehouseId, long id) {
requireWarehouse(tenantId, warehouseId);
WarehouseLocationEntity entity = repository.findLocation(tenantId, warehouseId, id);
private WarehouseLocationEntity requireLocation(long customerId, long warehouseId, long id) {
requireWarehouse(customerId, warehouseId);
WarehouseLocationEntity entity = repository.findLocation(customerId, warehouseId, id);
if (entity == null) throw new BizException(ResultCode.NOT_FOUND, "当前仓库下库位不存在");
return entity;
}
private WarehousePalletEntity requirePallet(long tenantId, long warehouseId, long id) {
requireWarehouse(tenantId, warehouseId);
WarehousePalletEntity entity = repository.findPallet(tenantId, warehouseId, id);
private WarehousePalletEntity requirePallet(long customerId, long warehouseId, long id) {
requireWarehouse(customerId, warehouseId);
WarehousePalletEntity entity = repository.findPallet(customerId, warehouseId, id);
if (entity == null) throw new BizException(ResultCode.NOT_FOUND, "当前仓库下托盘不存在");
return entity;
}
private void validateLocation(LocationRequests.SaveRequest request, long tenantId,
private void validateLocation(LocationRequests.SaveRequest request, long customerId,
long warehouseId, Long excludedId) {
if (!LOCATION_STATUSES.contains(request.status())) {
throw new BizException("库位状态仅支持 ACTIVE、IDLE、OCCUPIED、INACTIVE");
}
if (repository.locationCodeExists(tenantId, warehouseId, request.locationCode(), excludedId)) {
if (repository.locationCodeExists(customerId, warehouseId, request.locationCode(), excludedId)) {
throw new BizException(ResultCode.CONFLICT, "当前仓库下库位编码已存在");
}
}
private void validatePallet(PalletRequests.SaveRequest request, String palletCode, Long locationId,
long tenantId, long warehouseId, Long excludedId) {
long customerId, long warehouseId, Long excludedId) {
validatePalletStatus(request.status());
if (repository.palletCodeExists(tenantId, warehouseId, palletCode, excludedId)) {
if (repository.palletCodeExists(customerId, warehouseId, palletCode, excludedId)) {
throw new BizException(ResultCode.CONFLICT, "当前仓库下托盘编码已存在");
}
if (locationId != null) {
requireLocation(tenantId, warehouseId, locationId);
requireLocation(customerId, warehouseId, locationId);
}
}
@@ -219,10 +241,10 @@ public class WarehouseResourceService {
}
}
private WarehouseLocationEntity locationEntity(long tenantId, long warehouseId,
private WarehouseLocationEntity locationEntity(long customerId, long warehouseId,
LocationRequests.SaveRequest request) {
WarehouseLocationEntity entity = new WarehouseLocationEntity();
entity.setTenantId(tenantId);
entity.setCustomerId(customerId);
entity.setWarehouseId(warehouseId);
entity.setZoneCode(request.zoneCode());
entity.setLocationCode(request.locationCode());
@@ -233,11 +255,11 @@ public class WarehouseResourceService {
return entity;
}
private WarehousePalletEntity palletEntity(long tenantId, long warehouseId,
private WarehousePalletEntity palletEntity(long customerId, long warehouseId,
PalletRequests.SaveRequest request,
String palletCode, Long locationId) {
WarehousePalletEntity entity = new WarehousePalletEntity();
entity.setTenantId(tenantId);
entity.setCustomerId(customerId);
entity.setWarehouseId(warehouseId);
entity.setLocationId(locationId);
entity.setPalletCode(palletCode);
+39 -31
View File
@@ -5,7 +5,7 @@
CREATE TABLE IF NOT EXISTS wh_location (
id BIGINT UNSIGNED NOT NULL COMMENT '库位ID(雪花ID)',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
customer_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
zone_code VARCHAR(50) NOT NULL COMMENT '仓库分区编码',
location_code VARCHAR(64) NOT NULL COMMENT '仓库内唯一库位编码',
@@ -20,13 +20,13 @@ CREATE TABLE IF NOT EXISTS wh_location (
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_location_code (tenant_id, warehouse_id, location_code, deleted),
KEY idx_location_query (tenant_id, warehouse_id, zone_code, status, deleted)
UNIQUE KEY uk_location_code (customer_id, warehouse_id, location_code, deleted),
KEY idx_location_query (customer_id, warehouse_id, zone_code, status, deleted)
) ENGINE=InnoDB COMMENT='仓库库位表:按租户和仓库维护库位及承重信息';
CREATE TABLE IF NOT EXISTS wh_pallet (
id BIGINT UNSIGNED NOT NULL COMMENT '托盘ID(雪花ID)',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
customer_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
location_id BIGINT UNSIGNED DEFAULT NULL COMMENT '同仓库内绑定的库位ID,可为空',
pallet_code VARCHAR(128) NOT NULL COMMENT '仓库内唯一托盘编码',
@@ -42,14 +42,14 @@ CREATE TABLE IF NOT EXISTS wh_pallet (
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_pallet_code (tenant_id, warehouse_id, pallet_code, deleted),
KEY idx_pallet_query (tenant_id, warehouse_id, status, deleted),
KEY idx_pallet_location (tenant_id, warehouse_id, location_id, deleted)
UNIQUE KEY uk_pallet_code (customer_id, warehouse_id, pallet_code, deleted),
KEY idx_pallet_query (customer_id, warehouse_id, status, deleted),
KEY idx_pallet_location (customer_id, warehouse_id, location_id, deleted)
) ENGINE=InnoDB COMMENT='仓库托盘表:按租户和仓库维护托盘、容量、单价及状态';
CREATE TABLE IF NOT EXISTS wh_resource_lock (
id BIGINT UNSIGNED NOT NULL COMMENT 'Snowflake resource lock ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT 'Owning tenant ID',
customer_id BIGINT UNSIGNED NOT NULL COMMENT 'Owning tenant ID',
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT 'Owning warehouse ID',
pallet_id BIGINT UNSIGNED NOT NULL COMMENT 'Reserved pallet ID',
location_id BIGINT UNSIGNED NOT NULL COMMENT 'Reserved location ID',
@@ -62,15 +62,15 @@ CREATE TABLE IF NOT EXISTS wh_resource_lock (
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (id),
UNIQUE KEY uk_resource_lock_reservation (tenant_id, reservation_key),
KEY idx_resource_lock_pallet (tenant_id, warehouse_id, pallet_id, status),
KEY idx_resource_lock_location (tenant_id, warehouse_id, location_id, status),
KEY idx_resource_lock_business (tenant_id, business_type, business_id)
UNIQUE KEY uk_resource_lock_reservation (customer_id, reservation_key),
KEY idx_resource_lock_pallet (customer_id, warehouse_id, pallet_id, status),
KEY idx_resource_lock_location (customer_id, warehouse_id, location_id, status),
KEY idx_resource_lock_business (customer_id, business_type, business_id)
) ENGINE=InnoDB COMMENT='Atomic pallet and location reservation records';
CREATE TABLE IF NOT EXISTS wh_warehouse (
id BIGINT UNSIGNED NOT NULL COMMENT '仓库ID(雪花ID)',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '仓库所属租户ID',
customer_id BIGINT UNSIGNED NOT NULL COMMENT '仓库所属租户ID',
warehouse_code VARCHAR(50) NOT NULL COMMENT '租户内唯一仓库编码',
warehouse_name VARCHAR(120) NOT NULL COMMENT '仓库名称',
warehouse_type VARCHAR(32) NOT NULL DEFAULT 'OVERSEAS' COMMENT '仓库类型',
@@ -88,14 +88,14 @@ CREATE TABLE IF NOT EXISTS wh_warehouse (
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_wh_tenant_code (tenant_id, warehouse_code, deleted),
KEY idx_wh_tenant_status (tenant_id, status, deleted),
KEY idx_wh_country (tenant_id, country_code, deleted)
UNIQUE KEY uk_wh_tenant_code (customer_id, warehouse_code, deleted),
KEY idx_wh_tenant_status (customer_id, status, deleted),
KEY idx_wh_country (customer_id, country_code, deleted)
) ENGINE=InnoDB COMMENT='仓库主数据表:以租户为边界维护仓库编码、名称、国家、时区和启停状态';
CREATE TABLE IF NOT EXISTS wh_warehouse_address (
id BIGINT UNSIGNED NOT NULL COMMENT '仓库地址ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
customer_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
address_type VARCHAR(20) NOT NULL DEFAULT 'OPERATING' COMMENT '地址类型,例如OPERATING或RETURN',
country_code CHAR(2) NOT NULL COMMENT '国家二字码',
@@ -114,13 +114,13 @@ CREATE TABLE IF NOT EXISTS wh_warehouse_address (
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_address_type (tenant_id, warehouse_id, address_type, deleted),
KEY idx_address_warehouse (tenant_id, warehouse_id, deleted)
UNIQUE KEY uk_address_type (customer_id, warehouse_id, address_type, deleted),
KEY idx_address_warehouse (customer_id, warehouse_id, deleted)
) ENGINE=InnoDB COMMENT='仓库地址表:保存运营、退货等不同用途的地址及可选经纬度';
CREATE TABLE IF NOT EXISTS wh_service_capability (
id BIGINT UNSIGNED NOT NULL COMMENT '服务能力ID',
tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
customer_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
capability_code VARCHAR(50) NOT NULL COMMENT '能力编码:RECEIVING、STORAGE、PICK_PACK、RETURN等',
capability_name VARCHAR(100) NOT NULL COMMENT '能力名称',
@@ -134,17 +134,17 @@ CREATE TABLE IF NOT EXISTS wh_service_capability (
deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号',
PRIMARY KEY (id),
UNIQUE KEY uk_capability (tenant_id, warehouse_id, capability_code, deleted),
KEY idx_capability_query (tenant_id, warehouse_id, enabled, deleted)
UNIQUE KEY uk_capability (customer_id, warehouse_id, capability_code, deleted),
KEY idx_capability_query (customer_id, warehouse_id, enabled, deleted)
) ENGINE=InnoDB COMMENT='仓库服务能力表:声明仓库可提供的收货、仓储、拣包、退货等能力及截单时间';
-- 不使用跨服务数据库外键;仓库聚合内的归属由服务代码通过 tenant_id 与 warehouse_id 共同校验。
-- 不使用跨服务数据库外键;仓库聚合内的归属由服务代码通过 customer_id 与 warehouse_id 共同校验。
-- 修复既有数据库中因历史脚本编码错误或英文初始化产生的表与字段注释。
ALTER TABLE wh_location COMMENT='仓库库位表:按租户和仓库维护库位及承重信息';
ALTER TABLE wh_location
MODIFY COLUMN id BIGINT UNSIGNED NOT NULL COMMENT '库位ID(雪花ID)',
MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
MODIFY COLUMN customer_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
MODIFY COLUMN warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
MODIFY COLUMN zone_code VARCHAR(50) NOT NULL COMMENT '仓库分区编码',
MODIFY COLUMN location_code VARCHAR(64) NOT NULL COMMENT '仓库内唯一库位编码',
@@ -159,22 +159,30 @@ ALTER TABLE wh_location
MODIFY COLUMN deleted TINYINT(1) NOT NULL DEFAULT 0 COMMENT '逻辑删除标记',
MODIFY COLUMN version INT NOT NULL DEFAULT 0 COMMENT '乐观锁版本号';
CREATE TABLE IF NOT EXISTS wh_pallet_business_binding (
id BIGINT NOT NULL, customer_id BIGINT NOT NULL, warehouse_id BIGINT NOT NULL,
business_type VARCHAR(32) NOT NULL, business_id VARCHAR(128) NOT NULL,
pallet_id BIGINT NOT NULL, created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (id), UNIQUE KEY uk_wh_pallet_binding (customer_id,warehouse_id,business_type,business_id),
UNIQUE KEY uk_wh_pallet_binding_pallet (pallet_id)
) ENGINE=InnoDB COMMENT='Customer business binding to real warehouse pallet';
-- Every warehouse has one non-occupiable virtual location used for inventory history.
INSERT INTO wh_location (id, tenant_id, warehouse_id, zone_code, location_code, location_type,
INSERT INTO wh_location (id, customer_id, warehouse_id, zone_code, location_code, location_type,
max_load_kg, status, remark)
SELECT UUID_SHORT(), w.tenant_id, w.id, 'DEFAULT', CONCAT('LOC-', w.warehouse_code, '-DEFAULT'),
SELECT UUID_SHORT(), w.customer_id, w.id, 'DEFAULT', CONCAT('LOC-', w.warehouse_code, '-DEFAULT'),
'VIRTUAL', 999999999.000, 'ACTIVE', 'System default virtual location'
FROM wh_warehouse w
WHERE NOT EXISTS (
SELECT 1 FROM wh_location l
WHERE l.tenant_id = w.tenant_id AND l.warehouse_id = w.id
WHERE l.customer_id = w.customer_id AND l.warehouse_id = w.id
AND l.location_code = CONCAT('LOC-', w.warehouse_code, '-DEFAULT') AND l.deleted = 0
);
ALTER TABLE wh_pallet COMMENT='仓库托盘表:按租户和仓库维护托盘、容量、单价及状态';
ALTER TABLE wh_pallet
MODIFY COLUMN id BIGINT UNSIGNED NOT NULL COMMENT '托盘ID(雪花ID)',
MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
MODIFY COLUMN customer_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
MODIFY COLUMN warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
MODIFY COLUMN location_id BIGINT UNSIGNED NULL COMMENT '同仓库内绑定的库位ID,可为空',
MODIFY COLUMN pallet_code VARCHAR(128) NOT NULL COMMENT '仓库内唯一托盘编码',
@@ -193,7 +201,7 @@ ALTER TABLE wh_pallet
ALTER TABLE wh_warehouse COMMENT='仓库主数据表:以租户为边界维护仓库编码、名称、国家、时区和启停状态';
ALTER TABLE wh_warehouse
MODIFY COLUMN id BIGINT UNSIGNED NOT NULL COMMENT '仓库ID(雪花ID)',
MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT '仓库所属租户ID',
MODIFY COLUMN customer_id BIGINT UNSIGNED NOT NULL COMMENT '仓库所属租户ID',
MODIFY COLUMN warehouse_code VARCHAR(50) NOT NULL COMMENT '租户内唯一仓库编码',
MODIFY COLUMN warehouse_name VARCHAR(120) NOT NULL COMMENT '仓库名称',
MODIFY COLUMN warehouse_type VARCHAR(32) NOT NULL DEFAULT 'OVERSEAS' COMMENT '仓库类型',
@@ -214,7 +222,7 @@ ALTER TABLE wh_warehouse
ALTER TABLE wh_warehouse_address COMMENT='仓库地址表:保存运营、退货等不同用途的地址及可选经纬度';
ALTER TABLE wh_warehouse_address
MODIFY COLUMN id BIGINT UNSIGNED NOT NULL COMMENT '仓库地址ID',
MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
MODIFY COLUMN customer_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
MODIFY COLUMN warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
MODIFY COLUMN address_type VARCHAR(20) NOT NULL DEFAULT 'OPERATING' COMMENT '地址类型:OPERATING运营、RETURN退货',
MODIFY COLUMN country_code CHAR(2) NOT NULL COMMENT '国家二字码',
@@ -236,7 +244,7 @@ ALTER TABLE wh_warehouse_address
ALTER TABLE wh_service_capability COMMENT='仓库服务能力表:声明仓库可提供的收货、仓储、拣包、退货等能力及截单时间';
ALTER TABLE wh_service_capability
MODIFY COLUMN id BIGINT UNSIGNED NOT NULL COMMENT '服务能力ID',
MODIFY COLUMN tenant_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
MODIFY COLUMN customer_id BIGINT UNSIGNED NOT NULL COMMENT '数据所属租户ID',
MODIFY COLUMN warehouse_id BIGINT UNSIGNED NOT NULL COMMENT '所属仓库ID',
MODIFY COLUMN capability_code VARCHAR(50) NOT NULL COMMENT '能力编码:RECEIVING收货、STORAGE仓储、PICK_PACK拣包、RETURN退货',
MODIFY COLUMN capability_name VARCHAR(100) NOT NULL COMMENT '能力名称',
@@ -50,8 +50,8 @@ class WarehouseResourceControllerTest {
verify(service).location(10002L, 20001L, 30001L);
}
private static AuthenticatedUser user(long tenantId, String appCode) {
return new AuthenticatedUser(1L, tenantId, tenantId, 1L,
private static AuthenticatedUser user(long customerId, String appCode) {
return new AuthenticatedUser(1L, customerId, customerId, 1L,
"tester", appCode, "WEB", Set.of());
}
@@ -17,8 +17,8 @@ class WarehouseResourceLockSchemaTest {
}
assertThat(sql).contains("CREATE TABLE IF NOT EXISTS wh_resource_lock");
assertThat(sql).contains("UNIQUE KEY uk_resource_lock_reservation (tenant_id, reservation_key)");
assertThat(sql).contains("KEY idx_resource_lock_pallet (tenant_id, warehouse_id, pallet_id, status)");
assertThat(sql).contains("KEY idx_resource_lock_location (tenant_id, warehouse_id, location_id, status)");
assertThat(sql).contains("UNIQUE KEY uk_resource_lock_reservation (customer_id, reservation_key)");
assertThat(sql).contains("KEY idx_resource_lock_pallet (customer_id, warehouse_id, pallet_id, status)");
assertThat(sql).contains("KEY idx_resource_lock_location (customer_id, warehouse_id, location_id, status)");
}
}
@@ -41,7 +41,7 @@ class WarehouseApplicationServiceTest {
ArgumentCaptor<WarehouseLocationEntity> location = ArgumentCaptor.forClass(WarehouseLocationEntity.class);
verify(repository).insertLocation(location.capture());
assertThat(location.getValue().getTenantId()).isEqualTo(10001L);
assertThat(location.getValue().getCustomerId()).isEqualTo(10001L);
assertThat(location.getValue().getWarehouseId()).isEqualTo(20001L);
assertThat(location.getValue().getLocationCode()).isEqualTo("LOC-US-LAX-01-DEFAULT");
assertThat(location.getValue().getLocationType()).isEqualTo("VIRTUAL");
@@ -250,7 +250,7 @@ class WarehouseResourceLockServiceTest {
private static WarehouseResourceLockEntity activeLock() {
WarehouseResourceLockEntity lock = new WarehouseResourceLockEntity();
lock.setId(501L);
lock.setTenantId(TENANT);
lock.setCustomerId(TENANT);
lock.setWarehouseId(WAREHOUSE);
lock.setPalletId(PALLET);
lock.setLocationId(LOCATION);
@@ -63,7 +63,7 @@ class WarehouseResourceServiceTest {
assertThat(id).isEqualTo(2089000000000000001L);
ArgumentCaptor<WarehouseLocationEntity> captor = ArgumentCaptor.forClass(WarehouseLocationEntity.class);
verify(repository).insertLocation(captor.capture());
assertThat(captor.getValue().getTenantId()).isEqualTo(TENANT);
assertThat(captor.getValue().getCustomerId()).isEqualTo(TENANT);
assertThat(captor.getValue().getWarehouseId()).isEqualTo(WAREHOUSE);
}
@@ -253,7 +253,7 @@ class WarehouseResourceServiceTest {
private static WarehouseEntity warehouse() {
WarehouseEntity warehouse = new WarehouseEntity();
warehouse.setId(WAREHOUSE);
warehouse.setTenantId(TENANT);
warehouse.setCustomerId(TENANT);
warehouse.setWarehouseCode("US-LAX-01");
return warehouse;
}