diff --git a/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformMojiaSkuSyncController.java b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformMojiaSkuSyncController.java new file mode 100644 index 000000000..f30ca96cb --- /dev/null +++ b/firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformMojiaSkuSyncController.java @@ -0,0 +1,57 @@ +package com.baiyue.firstleg.controller; + +import com.baiyue.common.core.api.ApiResponse; +import com.baiyue.common.core.api.PageResult; +import com.baiyue.firstleg.dto.MojiaSkuSyncDtos; +import com.baiyue.firstleg.service.FirstlegScopeGuard; +import com.baiyue.firstleg.service.MojiaSkuSyncService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.*; +import com.baiyue.common.security.AuthenticatedUser; + +@Tag(name = "墨家一件代发SKU同步") +@RestController +@RequestMapping("/api/platform/v1/firstleg/mojia-sku-sync") +public class PlatformMojiaSkuSyncController { + @Resource private MojiaSkuSyncService service; + + @GetMapping + @PreAuthorize("hasAuthority('platform:mojia-sku-sync:list')") + @Operation(summary = "查询墨家一件代发SKU测量记录") + public ApiResponse> page( + @AuthenticationPrincipal AuthenticatedUser user, + @RequestParam(required = false) String skuCode, + @RequestParam(required = false) String syncStatus, + @RequestParam(defaultValue = "1") @Min(1) long page, + @RequestParam(defaultValue = "20") @Min(1) @Max(200) long size) { + FirstlegScopeGuard.platform(user); + return ApiResponse.success(service.page(new MojiaSkuSyncDtos.Query(skuCode, syncStatus, page, size))); + } + + @PutMapping("/{id}") + @PreAuthorize("hasAuthority('platform:mojia-sku-sync:edit')") + @Operation(summary = "编辑墨家一件代发SKU测量数据") + public ApiResponse update( + @AuthenticationPrincipal AuthenticatedUser user, @PathVariable long id, + @Valid @RequestBody MojiaSkuSyncDtos.UpdateRequest request) { + FirstlegScopeGuard.platform(user); + return ApiResponse.success(service.update(id, request)); + } + + @PostMapping("/{id}/retry") + @PreAuthorize("hasAuthority('platform:mojia-sku-sync:retry')") + @Operation(summary = "重新推送失败的墨家一件代发SKU") + public ApiResponse retry( + @AuthenticationPrincipal AuthenticatedUser user, @PathVariable long id, + @Valid @RequestBody MojiaSkuSyncDtos.RetryRequest request) { + FirstlegScopeGuard.platform(user); + return ApiResponse.success(service.retry(id, request)); + } +} diff --git a/firstleg/src/main/java/com/baiyue/firstleg/dto/MojiaSkuSyncDtos.java b/firstleg/src/main/java/com/baiyue/firstleg/dto/MojiaSkuSyncDtos.java new file mode 100644 index 000000000..5e1207df8 --- /dev/null +++ b/firstleg/src/main/java/com/baiyue/firstleg/dto/MojiaSkuSyncDtos.java @@ -0,0 +1,30 @@ +package com.baiyue.firstleg.dto; + +import jakarta.validation.constraints.DecimalMin; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +public final class MojiaSkuSyncDtos { + private MojiaSkuSyncDtos() {} + + public record Query(String skuCode, String syncStatus, long page, long size) {} + + public record UpdateRequest( + @NotBlank @Size(max = 128) String skuCode, + @NotNull @DecimalMin("0.001") BigDecimal lengthCm, + @NotNull @DecimalMin("0.001") BigDecimal widthCm, + @NotNull @DecimalMin("0.001") BigDecimal heightCm, + @NotNull @DecimalMin("0.001") BigDecimal weightKg, + @NotNull Integer revision) {} + + public record RetryRequest(@NotNull Integer revision) {} + + public record Record( + Long id, String skuCode, BigDecimal lengthCm, BigDecimal widthCm, BigDecimal heightCm, + BigDecimal weightKg, Long imageFileId, LocalDateTime measuredAt, LocalDateTime receivedAt, + String syncStatus, String syncFailureCode, String syncFailureReason, String lingxingSkuId, + Integer revision, LocalDateTime updatedAt) {} +} diff --git a/firstleg/src/main/java/com/baiyue/firstleg/repository/MojiaSkuSyncRepository.java b/firstleg/src/main/java/com/baiyue/firstleg/repository/MojiaSkuSyncRepository.java new file mode 100644 index 000000000..d842acb07 --- /dev/null +++ b/firstleg/src/main/java/com/baiyue/firstleg/repository/MojiaSkuSyncRepository.java @@ -0,0 +1,63 @@ +package com.baiyue.firstleg.repository; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baiyue.common.core.api.PageResult; +import com.baiyue.firstleg.entity.MojiaMeasurementReceiptEntity; +import com.baiyue.firstleg.mapper.MojiaMeasurementReceiptMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Repository; +import org.springframework.util.StringUtils; + +@Repository +public class MojiaSkuSyncRepository { + @Resource private MojiaMeasurementReceiptMapper mapper; + + public PageResult page(String skuCode, String syncStatus, + long page, long size) { + return PageResult.from(mapper.selectPage(new Page<>(page, size), + new LambdaQueryWrapper() + .eq(MojiaMeasurementReceiptEntity::getMeasurementKind, "SKU") + .eq(StringUtils.hasText(syncStatus), MojiaMeasurementReceiptEntity::getSyncStatus, syncStatus) + .like(StringUtils.hasText(skuCode), MojiaMeasurementReceiptEntity::getBoxCode, skuCode.trim()) + .orderByDesc(MojiaMeasurementReceiptEntity::getMeasuredAt) + .orderByDesc(MojiaMeasurementReceiptEntity::getId))); + } + + public MojiaMeasurementReceiptEntity find(long id) { + return mapper.selectOne(new LambdaQueryWrapper() + .eq(MojiaMeasurementReceiptEntity::getId, id) + .eq(MojiaMeasurementReceiptEntity::getMeasurementKind, "SKU")); + } + + public int updateMeasurement(MojiaMeasurementReceiptEntity value, int revision) { + return mapper.update(null, new LambdaUpdateWrapper() + .eq(MojiaMeasurementReceiptEntity::getId, value.getId()) + .eq(MojiaMeasurementReceiptEntity::getMeasurementKind, "SKU") + .eq(MojiaMeasurementReceiptEntity::getRevision, revision) + .set(MojiaMeasurementReceiptEntity::getBoxCode, value.getBoxCode()) + .set(MojiaMeasurementReceiptEntity::getLengthCm, value.getLengthCm()) + .set(MojiaMeasurementReceiptEntity::getWidthCm, value.getWidthCm()) + .set(MojiaMeasurementReceiptEntity::getHeightCm, value.getHeightCm()) + .set(MojiaMeasurementReceiptEntity::getWeightKg, value.getWeightKg()) + .set(MojiaMeasurementReceiptEntity::getSyncStatus, "PENDING") + .set(MojiaMeasurementReceiptEntity::getSyncFailureCode, null) + .set(MojiaMeasurementReceiptEntity::getSyncFailureReason, null) + .set(MojiaMeasurementReceiptEntity::getStatus, "SKU_PENDING_SYNC") + .set(MojiaMeasurementReceiptEntity::getRevision, revision + 1)); + } + + public int retry(long id, int revision) { + return mapper.update(null, new LambdaUpdateWrapper() + .eq(MojiaMeasurementReceiptEntity::getId, id) + .eq(MojiaMeasurementReceiptEntity::getMeasurementKind, "SKU") + .eq(MojiaMeasurementReceiptEntity::getRevision, revision) + .eq(MojiaMeasurementReceiptEntity::getSyncStatus, "FAILED") + .set(MojiaMeasurementReceiptEntity::getSyncStatus, "PENDING") + .set(MojiaMeasurementReceiptEntity::getSyncFailureCode, null) + .set(MojiaMeasurementReceiptEntity::getSyncFailureReason, null) + .set(MojiaMeasurementReceiptEntity::getStatus, "SKU_PENDING_SYNC") + .set(MojiaMeasurementReceiptEntity::getRevision, revision + 1)); + } +} diff --git a/firstleg/src/main/java/com/baiyue/firstleg/service/MojiaSkuSyncService.java b/firstleg/src/main/java/com/baiyue/firstleg/service/MojiaSkuSyncService.java new file mode 100644 index 000000000..c6a0953ef --- /dev/null +++ b/firstleg/src/main/java/com/baiyue/firstleg/service/MojiaSkuSyncService.java @@ -0,0 +1,66 @@ +package com.baiyue.firstleg.service; + +import com.baiyue.common.core.api.PageResult; +import com.baiyue.common.core.api.ResultCode; +import com.baiyue.common.core.exception.BizException; +import com.baiyue.firstleg.dto.MojiaSkuSyncDtos; +import com.baiyue.firstleg.entity.MojiaMeasurementReceiptEntity; +import com.baiyue.firstleg.repository.MojiaSkuSyncRepository; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class MojiaSkuSyncService { + @Resource private MojiaSkuSyncRepository repository; + + public PageResult page(MojiaSkuSyncDtos.Query query) { + PageResult source = repository.page( + query.skuCode(), query.syncStatus(), query.page(), query.size()); + return new PageResult<>(source.records().stream().map(MojiaSkuSyncService::view).toList(), + source.page(), source.size(), source.total(), source.pages()); + } + + @Transactional + public MojiaSkuSyncDtos.Record update(long id, MojiaSkuSyncDtos.UpdateRequest request) { + MojiaMeasurementReceiptEntity value = required(id); + if (repository.updateMeasurement(copy(value, request), request.revision()) != 1) { + throw conflict("SKU测量记录已被其他人修改,请刷新后重试"); + } + return view(required(id)); + } + + @Transactional + public MojiaSkuSyncDtos.Record retry(long id, MojiaSkuSyncDtos.RetryRequest request) { + required(id); + if (repository.retry(id, request.revision()) != 1) { + throw conflict("仅同步失败记录可重新推送,或记录已被其他人修改"); + } + return view(required(id)); + } + + private MojiaMeasurementReceiptEntity required(long id) { + MojiaMeasurementReceiptEntity value = repository.find(id); + if (value == null) throw new BizException(ResultCode.NOT_FOUND, "一件代发SKU测量记录不存在"); + return value; + } + + private static MojiaMeasurementReceiptEntity copy(MojiaMeasurementReceiptEntity value, + MojiaSkuSyncDtos.UpdateRequest request) { + value.setBoxCode(request.skuCode().trim()); + value.setLengthCm(request.lengthCm()); value.setWidthCm(request.widthCm()); + value.setHeightCm(request.heightCm()); value.setWeightKg(request.weightKg()); + return value; + } + + private static MojiaSkuSyncDtos.Record view(MojiaMeasurementReceiptEntity value) { + return new MojiaSkuSyncDtos.Record(value.getId(), value.getBoxCode(), value.getLengthCm(), + value.getWidthCm(), value.getHeightCm(), value.getWeightKg(), value.getImageFileId(), + value.getMeasuredAt(), value.getCreatedAt(), value.getSyncStatus(), value.getSyncFailureCode(), + value.getSyncFailureReason(), value.getLingxingSkuId(), value.getRevision(), value.getUpdatedAt()); + } + + private static BizException conflict(String message) { + return new BizException(ResultCode.CONFLICT, message); + } +}