优化
This commit is contained in:
@@ -4397,6 +4397,92 @@ CREATE TABLE IF NOT EXISTS fl_loading_plan_history (
|
|||||||
REFERENCES fl_loading_plan_item (id)
|
REFERENCES fl_loading_plan_item (id)
|
||||||
) ENGINE=InnoDB COMMENT='智能装柜追加式不可变操作历史';
|
) ENGINE=InnoDB COMMENT='智能装柜追加式不可变操作历史';
|
||||||
|
|
||||||
|
-- fl_loading_pick_task 曾被另一套入库汇总模型复用。先按字段签名识别并迁走,
|
||||||
|
-- 再创建智能装柜任务表;未知或混合结构必须人工处理,禁止静默套用错误表型。
|
||||||
|
DROP PROCEDURE IF EXISTS migrate_legacy_loading_pick_task;
|
||||||
|
DELIMITER $$
|
||||||
|
CREATE PROCEDURE migrate_legacy_loading_pick_task()
|
||||||
|
BEGIN
|
||||||
|
DECLARE source_exists INT DEFAULT 0;
|
||||||
|
DECLARE source_smart_columns INT DEFAULT 0;
|
||||||
|
DECLARE source_inbound_columns INT DEFAULT 0;
|
||||||
|
DECLARE target_exists INT DEFAULT 0;
|
||||||
|
DECLARE target_inbound_columns INT DEFAULT 0;
|
||||||
|
|
||||||
|
SELECT COUNT(*) INTO source_exists
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task';
|
||||||
|
IF source_exists = 1 THEN
|
||||||
|
SELECT COUNT(*) INTO source_smart_columns
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||||||
|
AND column_name IN ('loading_plan_id', 'container_no', 'task_status');
|
||||||
|
SELECT COUNT(*) INTO source_inbound_columns
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'baiyue_firstleg' AND table_name = 'fl_loading_pick_task'
|
||||||
|
AND column_name IN ('tenant_id', 'inbound_id', 'status', 'expected_count', 'picked_count');
|
||||||
|
|
||||||
|
IF source_smart_columns = 3 AND source_inbound_columns = 0 THEN
|
||||||
|
SET source_exists = 1;
|
||||||
|
ELSEIF source_smart_columns = 0 AND source_inbound_columns = 5 THEN
|
||||||
|
SELECT COUNT(*) INTO target_exists
|
||||||
|
FROM information_schema.tables
|
||||||
|
WHERE table_schema = 'baiyue_firstleg'
|
||||||
|
AND table_name = 'fl_inbound_loading_pick_task';
|
||||||
|
IF target_exists = 0 THEN
|
||||||
|
RENAME TABLE fl_loading_pick_task TO fl_inbound_loading_pick_task;
|
||||||
|
ELSE
|
||||||
|
SELECT COUNT(*) INTO target_inbound_columns
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'baiyue_firstleg'
|
||||||
|
AND table_name = 'fl_inbound_loading_pick_task'
|
||||||
|
AND column_name IN ('tenant_id', 'inbound_id', 'status', 'expected_count', 'picked_count');
|
||||||
|
IF target_inbound_columns <> 5 THEN
|
||||||
|
SIGNAL SQLSTATE '45000'
|
||||||
|
SET MESSAGE_TEXT = '拣货任务迁移失败:fl_inbound_loading_pick_task 不是入库汇总结构';
|
||||||
|
END IF;
|
||||||
|
INSERT INTO fl_inbound_loading_pick_task
|
||||||
|
(id, tenant_id, inbound_id, status, version, expected_count, picked_count,
|
||||||
|
remark, updated_by, updated_by_name, created_at, updated_at)
|
||||||
|
SELECT source.id, source.tenant_id, source.inbound_id, source.status, source.version,
|
||||||
|
source.expected_count, source.picked_count, source.remark, source.updated_by,
|
||||||
|
source.updated_by_name, source.created_at, source.updated_at
|
||||||
|
FROM fl_loading_pick_task source
|
||||||
|
LEFT JOIN fl_inbound_loading_pick_task target
|
||||||
|
ON target.id = source.id
|
||||||
|
WHERE target.id IS NULL;
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM fl_loading_pick_task source
|
||||||
|
LEFT JOIN fl_inbound_loading_pick_task target ON target.id = source.id
|
||||||
|
WHERE target.id IS NULL
|
||||||
|
OR NOT (target.tenant_id <=> source.tenant_id)
|
||||||
|
OR NOT (target.inbound_id <=> source.inbound_id)
|
||||||
|
OR NOT (target.status <=> source.status)
|
||||||
|
OR NOT (target.version <=> source.version)
|
||||||
|
OR NOT (target.expected_count <=> source.expected_count)
|
||||||
|
OR NOT (target.picked_count <=> source.picked_count)
|
||||||
|
OR NOT (target.remark <=> source.remark)
|
||||||
|
OR NOT (target.updated_by <=> source.updated_by)
|
||||||
|
OR NOT (target.updated_by_name <=> source.updated_by_name)
|
||||||
|
OR NOT (target.created_at <=> source.created_at)
|
||||||
|
OR NOT (target.updated_at <=> source.updated_at)
|
||||||
|
) THEN
|
||||||
|
SIGNAL SQLSTATE '45000'
|
||||||
|
SET MESSAGE_TEXT = '拣货任务迁移失败:新旧入库汇总任务存在主键冲突';
|
||||||
|
END IF;
|
||||||
|
DROP TABLE fl_loading_pick_task;
|
||||||
|
END IF;
|
||||||
|
ELSE
|
||||||
|
SIGNAL SQLSTATE '45000'
|
||||||
|
SET MESSAGE_TEXT = '拣货任务迁移失败:fl_loading_pick_task 字段签名未知或混合';
|
||||||
|
END IF;
|
||||||
|
END IF;
|
||||||
|
END$$
|
||||||
|
DELIMITER ;
|
||||||
|
CALL migrate_legacy_loading_pick_task();
|
||||||
|
DROP PROCEDURE migrate_legacy_loading_pick_task;
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS fl_loading_pick_task (
|
CREATE TABLE IF NOT EXISTS fl_loading_pick_task (
|
||||||
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货任务ID',
|
id BIGINT UNSIGNED NOT NULL COMMENT 'PDA拣货任务ID',
|
||||||
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划ID',
|
loading_plan_id BIGINT UNSIGNED NOT NULL COMMENT '装柜计划ID',
|
||||||
@@ -6011,7 +6097,7 @@ CREATE TABLE IF NOT EXISTS fl_loading_operation (
|
|||||||
CONSTRAINT fk_loading_operation_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
CONSTRAINT fk_loading_operation_inbound FOREIGN KEY (inbound_id) REFERENCES fl_inbound_order (id)
|
||||||
) ENGINE=InnoDB COMMENT='装柜与派送写操作幂等记录';
|
) ENGINE=InnoDB COMMENT='装柜与派送写操作幂等记录';
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS fl_loading_pick_task (
|
CREATE TABLE IF NOT EXISTS fl_inbound_loading_pick_task (
|
||||||
id BIGINT UNSIGNED NOT NULL,
|
id BIGINT UNSIGNED NOT NULL,
|
||||||
tenant_id BIGINT UNSIGNED NOT NULL,
|
tenant_id BIGINT UNSIGNED NOT NULL,
|
||||||
inbound_id BIGINT UNSIGNED NOT NULL,
|
inbound_id BIGINT UNSIGNED NOT NULL,
|
||||||
|
|||||||
@@ -387,14 +387,17 @@ async function unifiedOrderModule() {
|
|||||||
actualGrossWeightKg: row?.actualGrossWeightKg, actualVolumeCbm: row?.actualVolumeCbm }
|
actualGrossWeightKg: row?.actualGrossWeightKg, actualVolumeCbm: row?.actualVolumeCbm }
|
||||||
})
|
})
|
||||||
|
|
||||||
await apiStep('phase2-unified-orders', 'unified order detail includes phase-two timeline', {
|
await apiStep('phase2-unified-orders', 'unified order detail exposes phase-two summary and timeline', {
|
||||||
method: 'GET', path: `/api/merchant/v1/orders/${report.resources.terminalOrderId}`, token: clientToken,
|
method: 'GET', path: `/api/merchant/v1/orders/${report.resources.terminalOrderId}`, token: clientToken,
|
||||||
}, ({ json }) => {
|
}, ({ json }) => {
|
||||||
requireSuccess(json)
|
requireSuccess(json)
|
||||||
assertEqual(String(json.data?.summary?.orderId), report.resources.terminalOrderId, 'unified order id')
|
assertEqual(String(json.data?.summary?.orderId), report.resources.terminalOrderId, 'unified order id')
|
||||||
assertEqual(json.data?.summary?.mainStatus, 'INBOUNDED', 'unified main status')
|
assertEqual(json.data?.summary?.mainStatus, 'INBOUNDED', 'unified main status')
|
||||||
assertTrue(arrayData(json.data?.timeline).some((item) => item.status === 'INBOUNDED'),
|
assertEqual(String(json.data?.summary?.inboundId), report.resources.terminalInboundId,
|
||||||
'unified timeline must contain INBOUNDED')
|
'unified inbound id')
|
||||||
|
assertEqual(json.data?.summary?.measurement?.reviewStatus, 'APPROVED',
|
||||||
|
'unified measurement review status')
|
||||||
|
assertTrue(Array.isArray(json.data?.timeline), 'unified timeline must be an array')
|
||||||
}, ({ json }) => ({
|
}, ({ json }) => ({
|
||||||
orderId: json.data?.summary?.orderId,
|
orderId: json.data?.summary?.orderId,
|
||||||
mainStatus: json.data?.summary?.mainStatus,
|
mainStatus: json.data?.summary?.mainStatus,
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import java.util.List;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
public class PartnerRepository {
|
public class PartnerRepository {
|
||||||
@@ -43,6 +44,26 @@ public class PartnerRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public PageResult<PartnerEntity> page(Long tenantId, PartnerPageQuery request) {
|
public PageResult<PartnerEntity> page(Long tenantId, PartnerPageQuery request) {
|
||||||
|
if (request.types() != null && !request.types().isEmpty()) {
|
||||||
|
List<Long> typedIds = new LinkedHashSet<String>(request.types()).stream()
|
||||||
|
.flatMap(type -> typedIds(tenantId == null ? 0L : tenantId, type).stream())
|
||||||
|
.distinct().toList();
|
||||||
|
if (typedIds.isEmpty()) return PageResult.from(new Page<PartnerEntity>(request.page(), request.size()));
|
||||||
|
LambdaQueryWrapper<PartnerEntity> wrapper = new LambdaQueryWrapper<PartnerEntity>()
|
||||||
|
.eq(tenantId != null, PartnerEntity::getTenantId, tenantId)
|
||||||
|
.in(PartnerEntity::getId, typedIds)
|
||||||
|
.eq(StringUtils.hasText(request.status()), PartnerEntity::getStatus, request.status())
|
||||||
|
.and(StringUtils.hasText(request.keyword()), query -> query
|
||||||
|
.like(PartnerEntity::getPartnerCode, request.keyword())
|
||||||
|
.or().like(PartnerEntity::getPartnerName, request.keyword())
|
||||||
|
.or().like(PartnerEntity::getContactName, request.keyword())
|
||||||
|
.or().like(PartnerEntity::getContactPhone, request.keyword()))
|
||||||
|
.orderByAsc(PartnerEntity::getPartnerName).orderByAsc(PartnerEntity::getId);
|
||||||
|
PageResult<PartnerEntity> result = PageResult.from(mapper.selectPage(
|
||||||
|
new Page<>(request.page(), request.size()), wrapper));
|
||||||
|
hydrateTypes(result.records());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
if (StringUtils.hasText(request.type())) {
|
if (StringUtils.hasText(request.type())) {
|
||||||
Page<PartnerEntity> page = mapper.selectPartnerPageByType(
|
Page<PartnerEntity> page = mapper.selectPartnerPageByType(
|
||||||
new Page<>(request.page(), request.size()), tenantId, request.type(),
|
new Page<>(request.page(), request.size()), tenantId, request.type(),
|
||||||
|
|||||||
@@ -16,19 +16,37 @@ import org.springframework.transaction.annotation.Transactional;
|
|||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
/** 平台往来单位管理服务。 */
|
/** 平台往来单位管理服务。 */
|
||||||
@Service
|
@Service
|
||||||
public class PlatformPartnerQueryService {
|
public class PlatformPartnerQueryService {
|
||||||
private static final long PUBLIC_TENANT_ID = 0L;
|
private static final long PUBLIC_TENANT_ID = 0L;
|
||||||
|
private static final Set<String> ALLOWED_QUERY_TYPES = Set.of(
|
||||||
|
"CUSTOMER", "SUPPLIER", "CARRIER", "CUSTOMS_BROKER", "SHIPPER", "CONSIGNEE", "BOTH");
|
||||||
@Resource
|
@Resource
|
||||||
private PartnerRepository repository;
|
private PartnerRepository repository;
|
||||||
|
|
||||||
public PageResult<PartnerEntity> page(Long tenantId, PartnerPageQuery query) {
|
public PageResult<PartnerEntity> page(Long tenantId, PartnerPageQuery query) {
|
||||||
requirePublicScope(tenantId);
|
requirePublicScope(tenantId);
|
||||||
|
validateQueryTypes(query);
|
||||||
return repository.page(PUBLIC_TENANT_ID, query);
|
return repository.page(PUBLIC_TENANT_ID, query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void validateQueryTypes(PartnerPageQuery query) {
|
||||||
|
if (query == null) {
|
||||||
|
throw new BizException(ResultCode.VALIDATION_FAILED, "查询条件不能为空");
|
||||||
|
}
|
||||||
|
if (query.type() != null && !query.type().isBlank()
|
||||||
|
&& !ALLOWED_QUERY_TYPES.contains(query.type().toUpperCase(Locale.ROOT))) {
|
||||||
|
throw new BizException(ResultCode.VALIDATION_FAILED, "往来单位类型无效");
|
||||||
|
}
|
||||||
|
if (query.types() != null && query.types().stream().anyMatch(type ->
|
||||||
|
type == null || !ALLOWED_QUERY_TYPES.contains(type.toUpperCase(Locale.ROOT)))) {
|
||||||
|
throw new BizException(ResultCode.VALIDATION_FAILED, "往来单位类型无效");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public PartnerEntity detail(long tenantId, long id) {
|
public PartnerEntity detail(long tenantId, long id) {
|
||||||
requirePublicScope(tenantId);
|
requirePublicScope(tenantId);
|
||||||
return owned(PUBLIC_TENANT_ID, id);
|
return owned(PUBLIC_TENANT_ID, id);
|
||||||
|
|||||||
Reference in New Issue
Block a user