feat(file): register domestic order exports
This commit is contained in:
@@ -7,6 +7,7 @@ import java.util.List;
|
||||
|
||||
final class ExportSchemaContracts {
|
||||
static final String BUY_DECLARATION_ITEMS = "LOADING_LIST_BUY_DECLARATION_ITEMS";
|
||||
static final String DOMESTIC_WAREHOUSE_ORDER_LIST = "DOMESTIC_WAREHOUSE_ORDER_LIST";
|
||||
static final List<ExpectedColumn> BUY_DECLARATION_COLUMNS = List.of(
|
||||
column("customerName", "客户名称", ExportColumnType.STRING),
|
||||
column("orderNo", "订单号", ExportColumnType.STRING),
|
||||
@@ -21,20 +22,54 @@ final class ExportSchemaContracts {
|
||||
column("volume", "体积", ExportColumnType.DECIMAL),
|
||||
column("mark", "唛头", ExportColumnType.STRING),
|
||||
column("customerRemark", "客户备注", ExportColumnType.STRING));
|
||||
static final List<ExpectedColumn> DOMESTIC_WAREHOUSE_ORDER_COLUMNS = List.of(
|
||||
column("inboundCode", "入仓码", ExportColumnType.STRING),
|
||||
column("orderNo", "订单号", ExportColumnType.STRING),
|
||||
column("mainStatus", "订单主状态", ExportColumnType.STRING),
|
||||
column("customerName", "客户", ExportColumnType.STRING),
|
||||
column("channelName", "渠道", ExportColumnType.STRING),
|
||||
column("channelChanged", "曾变更渠道", ExportColumnType.BOOLEAN),
|
||||
column("receivingWarehouseName", "定位仓库", ExportColumnType.STRING),
|
||||
column("destinationWarehouseName", "交货仓", ExportColumnType.STRING),
|
||||
column("expectedShipDate", "预计出运日期", ExportColumnType.DATE),
|
||||
column("expectedArrivalAt", "预计到货时间", ExportColumnType.DATETIME),
|
||||
column("expectedVolumeCbm", "预计体积(m³)", ExportColumnType.DECIMAL),
|
||||
column("totalPackageCount", "箱数", ExportColumnType.INTEGER),
|
||||
column("totalQuantity", "产品数量", ExportColumnType.INTEGER),
|
||||
column("actualGrossWeightKg", "实测毛重(kg)", ExportColumnType.DECIMAL),
|
||||
column("actualVolumeCbm", "实测体积(m³)", ExportColumnType.DECIMAL),
|
||||
column("chargeableWeightKg", "计费重量(kg)", ExportColumnType.DECIMAL),
|
||||
column("chargeableVolumeCbm", "计费体积(m³)", ExportColumnType.DECIMAL),
|
||||
column("feeReviewStatus", "费用审核状态", ExportColumnType.STRING),
|
||||
column("skuNos", "SKU", ExportColumnType.STRING),
|
||||
column("taxMode", "报关方式", ExportColumnType.STRING),
|
||||
column("containerMode", "装柜方式", ExportColumnType.STRING),
|
||||
column("customsInspection", "是否商检", ExportColumnType.BOOLEAN),
|
||||
column("containerNo", "集装箱号", ExportColumnType.STRING),
|
||||
column("cabinetNo", "柜次号", ExportColumnType.STRING),
|
||||
column("vesselName", "船名", ExportColumnType.STRING),
|
||||
column("voyageNo", "航次", ExportColumnType.STRING),
|
||||
column("createdAt", "创建时间", ExportColumnType.DATETIME));
|
||||
|
||||
private ExportSchemaContracts() { }
|
||||
|
||||
static void validate(String exportType, List<ExportColumn> columns) {
|
||||
if (!BUY_DECLARATION_ITEMS.equals(exportType)) return;
|
||||
if (columns.size() != BUY_DECLARATION_COLUMNS.size()) {
|
||||
throw ExportFailure.permanent("买单报关导出列定义不匹配");
|
||||
if (BUY_DECLARATION_ITEMS.equals(exportType)) {
|
||||
validate(columns, BUY_DECLARATION_COLUMNS, "买单报关导出列定义不匹配");
|
||||
} else if (DOMESTIC_WAREHOUSE_ORDER_LIST.equals(exportType)) {
|
||||
validate(columns, DOMESTIC_WAREHOUSE_ORDER_COLUMNS, "国内仓订单导出列定义不匹配");
|
||||
}
|
||||
}
|
||||
|
||||
private static void validate(List<ExportColumn> columns, List<ExpectedColumn> expectedColumns,
|
||||
String failureMessage) {
|
||||
if (columns.size() != expectedColumns.size()) throw ExportFailure.permanent(failureMessage);
|
||||
for (int index = 0; index < columns.size(); index++) {
|
||||
ExportColumn actual = columns.get(index);
|
||||
ExpectedColumn expected = BUY_DECLARATION_COLUMNS.get(index);
|
||||
ExpectedColumn expected = expectedColumns.get(index);
|
||||
if (!expected.key.equals(actual.key()) || !expected.title.equals(actual.title())
|
||||
|| expected.type != actual.type()) {
|
||||
throw ExportFailure.permanent("买单报关导出列定义不匹配");
|
||||
throw ExportFailure.permanent(failureMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public class ExportTaskService {
|
||||
if (!user.permissions().contains(permission)) {
|
||||
throw new BizException(ResultCode.FORBIDDEN, "缺少该导出类型的操作权限");
|
||||
}
|
||||
validateParameters(request.parameters());
|
||||
validateParameters(type, request.parameters());
|
||||
validateTypeParameters(type, request.parameters());
|
||||
String parametersJson = parametersJson(request.parameters());
|
||||
long id = IdWorker.getId();
|
||||
@@ -140,15 +140,23 @@ public class ExportTaskService {
|
||||
catch (JsonProcessingException exception) { throw new BizException(ResultCode.BAD_REQUEST, "导出筛选条件无法序列化"); }
|
||||
}
|
||||
|
||||
private void validateParameters(Map<String, Object> parameters) {
|
||||
private void validateParameters(String type, Map<String, Object> parameters) {
|
||||
Map<String, Object> values = parameters == null ? Map.of() : parameters;
|
||||
if (values.size() > 20) throw new BizException(ResultCode.VALIDATION_FAILED, "导出筛选条件最多 20 项");
|
||||
int maxItems = ExportSchemaContracts.DOMESTIC_WAREHOUSE_ORDER_LIST.equals(type) ? 32 : 20;
|
||||
if (values.size() > maxItems) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED, "导出筛选条件最多 " + maxItems + " 项");
|
||||
}
|
||||
values.forEach((key, value) -> {
|
||||
if (key == null || key.isBlank() || key.length() > 64) throw new BizException(ResultCode.VALIDATION_FAILED, "导出筛选键无效");
|
||||
validateParameterValue(value, 1);
|
||||
});
|
||||
try {
|
||||
if (objectMapper.writeValueAsBytes(values).length > 4096) throw new BizException(ResultCode.VALIDATION_FAILED, "导出筛选条件不能超过 4KB");
|
||||
int maxBytes = ExportSchemaContracts.DOMESTIC_WAREHOUSE_ORDER_LIST.equals(type)
|
||||
? 512 * 1024 : 4096;
|
||||
if (objectMapper.writeValueAsBytes(values).length > maxBytes) {
|
||||
throw new BizException(ResultCode.VALIDATION_FAILED,
|
||||
"导出筛选条件不能超过 " + (maxBytes / 1024) + "KB");
|
||||
}
|
||||
} catch (JsonProcessingException exception) {
|
||||
throw new BizException(ResultCode.BAD_REQUEST, "导出筛选条件无法序列化");
|
||||
}
|
||||
|
||||
@@ -101,6 +101,15 @@ baiyue:
|
||||
max-rows: 200000
|
||||
max-attempts: 3
|
||||
batch-size: 500
|
||||
DOMESTIC_WAREHOUSE_ORDER_LIST:
|
||||
provider-service: order-service
|
||||
provider-path: /internal/exports/v1/DOMESTIC_WAREHOUSE_ORDER_LIST/batches
|
||||
required-permission: platform:order:export
|
||||
app-scope: PLATFORM
|
||||
enabled: true
|
||||
max-rows: 200000
|
||||
max-attempts: 3
|
||||
batch-size: 500
|
||||
LOADING_LIST_BUY_DECLARATION_ITEMS:
|
||||
provider-service: firstleg-service
|
||||
provider-path: /internal/exports/v1/LOADING_LIST_BUY_DECLARATION_ITEMS/batches
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: File Domestic Warehouse Order Export Contract
|
||||
version: 1.0.0
|
||||
description: >-
|
||||
File 只持有异步任务、XLSX 文件、任务归属和下载权限。订单筛选、数据权限、分页游标及行数据由
|
||||
Order 的 DOMESTIC_WAREHOUSE_ORDER_LIST Provider 持有,File 不复制订单业务查询。
|
||||
paths:
|
||||
/api/platform/v1/files/export-tasks:
|
||||
post:
|
||||
summary: 创建国内仓订单 XLSX 导出任务
|
||||
description: >-
|
||||
exportType 固定 DOMESTIC_WAREHOUSE_ORDER_LIST,format 固定 XLSX,调用人必须具有
|
||||
platform:order:export。selectedOrderNos 非空时导出勾选业务订单号;为空或省略时由 Order
|
||||
按 parameters 中当前列表筛选导出全部匹配记录,parameters 不接受 page、size 或内部数据库 ID。
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema: {$ref: '#/components/schemas/CreateDomesticOrderExportTask'}
|
||||
responses:
|
||||
'200': {description: 返回异步任务,初始状态 PENDING}
|
||||
'403': {description: 缺少 platform:order:export}
|
||||
/api/platform/v1/files/export-tasks/{taskId}/download:
|
||||
get:
|
||||
summary: 下载本人已完成的订单导出文件
|
||||
parameters:
|
||||
- {name: taskId, in: path, required: true, schema: {type: string, pattern: '^[1-9][0-9]*$'}}
|
||||
responses:
|
||||
'200':
|
||||
description: XLSX 文件;他人任务仅 platform:download:all 可下载
|
||||
headers:
|
||||
Cache-Control: {schema: {type: string, example: no-store}}
|
||||
Content-Disposition: {schema: {type: string, example: attachment}}
|
||||
content:
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet:
|
||||
schema: {type: string, format: binary}
|
||||
components:
|
||||
schemas:
|
||||
CreateDomesticOrderExportTask:
|
||||
type: object
|
||||
required: [exportType, format, taskName, parameters]
|
||||
properties:
|
||||
exportType: {type: string, enum: [DOMESTIC_WAREHOUSE_ORDER_LIST]}
|
||||
format: {type: string, enum: [XLSX]}
|
||||
taskName: {type: string, minLength: 1, maxLength: 128}
|
||||
parameters: {$ref: '#/components/schemas/DomesticOrderExportParameters'}
|
||||
DomesticOrderExportParameters:
|
||||
type: object
|
||||
description: >-
|
||||
透传给 Order Provider;selectedOrderNos 非空时优先,其余字段与平台订单列表当前筛选一致。
|
||||
properties:
|
||||
selectedOrderNos:
|
||||
type: array
|
||||
maxItems: 5000
|
||||
uniqueItems: true
|
||||
items: {type: string, minLength: 1, maxLength: 64, description: 订单业务编号}
|
||||
tenantId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
keyword: {type: string, maxLength: 64}
|
||||
orderNo: {type: string, maxLength: 64}
|
||||
mainStatus: {type: string}
|
||||
customerId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
receivingWarehouseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
destinationWarehouseId: {type: string, pattern: '^[1-9][0-9]*$'}
|
||||
inboundCode: {type: string, maxLength: 64}
|
||||
internalOrderStatus: {type: string, maxLength: 32}
|
||||
expectedArrivalDate: {type: string, format: date}
|
||||
feeReviewStatus: {type: string}
|
||||
channelName: {type: string, maxLength: 128}
|
||||
containerNo: {type: string, maxLength: 64}
|
||||
cabinetNo: {type: string, maxLength: 64}
|
||||
sailingDate: {type: string, format: date}
|
||||
loaded: {type: boolean}
|
||||
fullContainer: {type: boolean}
|
||||
measurementReviewStatus: {type: string}
|
||||
fbaCode: {type: string, maxLength: 64}
|
||||
createdFrom: {type: string, format: date-time}
|
||||
createdTo: {type: string, format: date-time}
|
||||
DomesticOrderExportColumns:
|
||||
type: object
|
||||
description: Provider schemaVersion=1 固定列顺序;不包含任何内部数据库 ID。
|
||||
properties:
|
||||
inboundCode: {type: string, title: 入仓码}
|
||||
orderNo: {type: string, title: 订单号}
|
||||
mainStatus: {type: string, title: 订单主状态}
|
||||
customerName: {type: string, title: 客户}
|
||||
channelName: {type: string, title: 渠道}
|
||||
channelChanged: {type: boolean, title: 曾变更渠道}
|
||||
receivingWarehouseName: {type: string, title: 定位仓库}
|
||||
destinationWarehouseName: {type: string, title: 交货仓}
|
||||
expectedShipDate: {type: string, format: date, title: 预计出运日期}
|
||||
expectedArrivalAt: {type: string, format: date-time, title: 预计到货时间}
|
||||
expectedVolumeCbm: {type: number, title: 预计体积(m³)}
|
||||
totalPackageCount: {type: integer, title: 箱数}
|
||||
totalQuantity: {type: integer, title: 产品数量}
|
||||
actualGrossWeightKg: {type: number, title: 实测毛重(kg)}
|
||||
actualVolumeCbm: {type: number, title: 实测体积(m³)}
|
||||
chargeableWeightKg: {type: number, title: 计费重量(kg)}
|
||||
chargeableVolumeCbm: {type: number, title: 计费体积(m³)}
|
||||
feeReviewStatus: {type: string, title: 费用审核状态}
|
||||
skuNos: {type: string, title: SKU}
|
||||
taxMode: {type: string, title: 报关方式}
|
||||
containerMode: {type: string, title: 装柜方式}
|
||||
customsInspection: {type: boolean, title: 是否商检}
|
||||
containerNo: {type: string, title: 集装箱号}
|
||||
cabinetNo: {type: string, title: 柜次号}
|
||||
vesselName: {type: string, title: 船名}
|
||||
voyageNo: {type: string, title: 航次}
|
||||
createdAt: {type: string, format: date-time, title: 创建时间}
|
||||
Reference in New Issue
Block a user