v1.0.31 #43

Merged
timi merged 10 commits from dev into master 2026-09-07 07:40:03 +00:00
17 changed files with 181 additions and 8 deletions
Showing only changes of commit 461af4d581 - Show all commits
+1 -1
View File
@@ -11,7 +11,7 @@
<groupId>com.imyeyu.timiserverapi</groupId>
<artifactId>TimiServerAPI</artifactId>
<version>1.0.30</version>
<version>1.0.31</version>
<packaging>jar</packaging>
<name>TimiServerAPI</name>
<description>imyeyu.com API</description>
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.entity;
import com.fasterxml.jackson.databind.JsonNode;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.spring.annotation.table.AutoUUID;
import com.imyeyu.spring.annotation.table.Id;
@@ -71,6 +72,9 @@ public class GaoOrderFulfillmentItem implements IDEntity<String>, Creatable {
/// 规格摘要快照
private String specSummarySnapshot;
/// SKU 规格结构快照,数组元素包含 specId、specName、specValueId、specValueName
private JsonNode skuSpecSnapshot;
/// 单位快照
private String unitSnapshot;
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.UUIDEntity;
import lombok.Data;
@@ -15,6 +16,11 @@ import java.util.List;
@EqualsAndHashCode(callSuper = true)
public class GaoProductSpec extends UUIDEntity {
/// 前端创建请求中的临时关联键
@Transient
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String clientKey;
/// 产品 ID
private String productId;
@@ -1,5 +1,7 @@
package com.imyeyu.api.modules.gao.entity;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.UUIDEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -12,6 +14,11 @@ import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
public class GaoProductSpecValue extends UUIDEntity {
/// 前端创建请求中的临时关联键
@Transient
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private String clientKey;
/// 规格项 ID
private String specId;
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.entity;
import com.fasterxml.jackson.databind.JsonNode;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.user.entity.User;
import com.imyeyu.spring.annotation.table.AutoUUID;
@@ -91,6 +92,9 @@ public class GaoProductStockRecord implements IDEntity<String>, Creatable {
/// 规格摘要快照
private String specSummarySnapshot;
/// SKU 规格结构快照,数组元素包含 specId、specName、specValueId、specValueName
private JsonNode skuSpecSnapshot;
/// 单位名称快照
private String unitSnapshot;
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.entity;
import com.fasterxml.jackson.databind.JsonNode;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.spring.annotation.table.Transient;
import com.imyeyu.spring.entity.UUIDEntity;
@@ -58,6 +59,9 @@ public class GaoSalesOrderItem extends UUIDEntity {
/// 规格摘要快照
private String specSummarySnapshot;
/// SKU 规格结构快照,数组元素包含 specId、specName、specValueId、specValueName
private JsonNode skuSpecSnapshot;
/// 单位快照
private String unitSnapshot;
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.service;
import com.fasterxml.jackson.databind.JsonNode;
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
import com.imyeyu.api.modules.gao.vo.GaoProductSkuUnitSelection;
@@ -32,6 +33,18 @@ public interface GaoProductSkuService extends BaseService<GaoProductSku, String>
/// 查询 SKU 单位
List<GaoProductSkuUnit> listUnitBySkuId(String skuId);
/// 查询 SKU 规格结构快照
///
/// @param skuId SKU ID
/// @return SKU 规格结构快照;无规格时返回空数组
JsonNode getSkuSpecSnapshot(String skuId);
/// 根据已加载 SKU 查询规格结构快照
///
/// @param sku 已加载 SKU
/// @return SKU 规格结构快照;无规格时返回空数组
JsonNode getSkuSpecSnapshot(GaoProductSku sku);
/// 停用 SKU
void disable(String id);
}
@@ -263,6 +263,7 @@ public class GaoCustomerPackageServiceImplement extends AbstractEntityService<Ga
item.setSkuCodeSnapshot(source.getSkuCodeSnapshot());
item.setSkuNameSnapshot(source.getSkuNameSnapshot());
item.setSpecSummarySnapshot(source.getSpecSummarySnapshot());
item.setSkuSpecSnapshot(skuService.getSkuSpecSnapshot(source.getSkuId()));
item.setUnitSnapshot(source.getUnitSnapshot());
item.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
item.setQuantity(source.getTotalQuantity());
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.service.implement;
import com.fasterxml.jackson.databind.JsonNode;
import com.imyeyu.api.bean.ModuleCode;
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.api.modules.common.entity.Attachment;
@@ -202,6 +203,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
List<GaoOrderFulfillmentItem> fulfillmentItemList = itemMapper.selectByFulfillmentIdForUpdate(fulfillment.getId());
TimiException.requiredTrue(!fulfillmentItemList.isEmpty(), "履约单没有明细");
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
applySkuSpecSnapshots(stockItems, fulfillmentItemList);
TimiException.requiredTrue(!stockItems.isEmpty(), "履约单没有需要占用的实物库存");
TimiException.requiredTrue(productStockService.hasSufficientForFulfillment(order.getStoreId(), stockItems), "库存仍然不足,不能准备履约");
String operatorUserId = userLoginService.getRequireLoginUserId();
@@ -240,6 +242,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
increaseFulfilled(source);
}
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
applySkuSpecSnapshots(stockItems, fulfillmentItemList);
String verifierUserId = userLoginService.getRequireLoginUserId();
if (!stockItems.isEmpty()) {
productStockService.outForFulfillment(order.getStoreId(), fulfillment.getId(), stockItems,
@@ -289,6 +292,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
TimiException.requiredTrue(!fulfillmentItemList.isEmpty(), "履约单没有明细");
if (fulfillment.getStatus() == GaoOrderFulfillment.Status.PENDING) {
List<GaoStockChangeItem> stockItems = buildStockItems(order.getStoreId(), sourceList);
applySkuSpecSnapshots(stockItems, fulfillmentItemList);
if (!stockItems.isEmpty()) {
productStockService.releaseForFulfillment(order.getStoreId(), fulfillment.getId(), stockItems,
stockRequestNo(fulfillment, "RELEASE"), userLoginService.getRequireLoginUserId());
@@ -364,6 +368,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
source.getId(), item.getSkuId(), GaoProductStockRecord.OperationType.ORDER_OUT);
if (stockRecord != null) {
item.setSourceRecordId(stockRecord.getId());
item.setSkuSpecSnapshot(stockRecord.getSkuSpecSnapshot());
}
}
productStockService.revokeForFulfillment(order.getStoreId(), reverse.getId(), stockItems,
@@ -452,16 +457,16 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
TimiException.requiredTrue(order.getStoreId().equals(product.getStoreId()), "履约来源产品不属于当前门店");
BigDecimal baseQuantityPerUnit = requiredBaseQuantityPerUnit(source.getBaseQuantityPerUnitSnapshot());
GaoProductSkuUnit unit = snapshotUnit(source.getSkuUnitId(), source.getUnitSnapshot(), baseQuantityPerUnit);
return buildSource(order, source.getId(), source.getSkuId(), source.getSkuUnitId(), source.getProductIdSnapshot(), source.getProductNameSnapshot(), source.getSkuCodeSnapshot(), source.getSkuNameSnapshot(), source.getSpecSummarySnapshot(),
JsonNode skuSpecSnapshot = source.getSkuSpecSnapshot();
return buildSource(order, source.getId(), source.getSkuId(), source.getSkuUnitId(), source.getProductIdSnapshot(), source.getProductNameSnapshot(), source.getSkuCodeSnapshot(), source.getSkuNameSnapshot(), source.getSpecSummarySnapshot(), skuSpecSnapshot,
source.getUnitSnapshot(), baseQuantityPerUnit, quantity, source.getQuantity(), source.getReservedQuantity(), source.getFulfilledQuantity(),
product, unit, checkAvailable);
product, sku, unit, checkAvailable);
}
private SourceEntry buildSource(GaoSalesOrder order, String orderItemId, String skuId, String unitId, String productId,
String productName, String skuCode, String skuName, String specSummary, String unitName, BigDecimal baseQuantityPerUnit, BigDecimal quantity, BigDecimal totalQuantity,
BigDecimal reservedQuantity, BigDecimal fulfilledQuantity, GaoProduct product, GaoProductSkuUnit unit,
String productName, String skuCode, String skuName, String specSummary, JsonNode skuSpecSnapshot, String unitName, BigDecimal baseQuantityPerUnit, BigDecimal quantity, BigDecimal totalQuantity,
BigDecimal reservedQuantity, BigDecimal fulfilledQuantity, GaoProduct product, GaoProductSku sku, GaoProductSkuUnit unit,
boolean checkAvailable) {
GaoProductSku sku = skuService.get(skuId);
if (!Boolean.TRUE.equals(sku.getAllowDecimalQuantity())) {
TimiException.requiredTrue(quantity.stripTrailingZeros().scale() <= 0, "该产品不允许小数数量");
}
@@ -471,7 +476,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
BigDecimal available = decimal(totalQuantity).subtract(reservedBefore).subtract(fulfilledBefore);
TimiException.requiredTrue(0 <= available.compareTo(quantity), "来源产品可履约数量不足");
}
return new SourceEntry(orderItemId, skuId, unitId, productId, productName, skuCode, skuName, specSummary, unitName, baseQuantityPerUnit,
return new SourceEntry(orderItemId, skuId, unitId, productId, productName, skuCode, skuName, specSummary, skuSpecSnapshot, unitName, baseQuantityPerUnit,
quantity, decimal(totalQuantity), reservedBefore, fulfilledBefore, product, sku, unit);
}
@@ -488,6 +493,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
GaoStockChangeItem item = itemMap.get(source.sku().getId());
if (item == null) {
item = GaoStockChangeItem.of(source.product(), source.sku(), baseUnit, baseQuantity, baseQuantity);
item.setSkuSpecSnapshot(source.skuSpecSnapshot());
itemMap.put(source.sku().getId(), item);
} else {
item.setQuantity(item.getQuantity().add(baseQuantity));
@@ -504,6 +510,24 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
return result;
}
private void applySkuSpecSnapshots(List<GaoStockChangeItem> stockItems, List<GaoOrderFulfillmentItem> fulfillmentItemList) {
if (stockItems == null || stockItems.isEmpty() || fulfillmentItemList == null || fulfillmentItemList.isEmpty()) {
return;
}
Map<String, JsonNode> snapshotMap = new HashMap<>();
for (GaoOrderFulfillmentItem item : fulfillmentItemList) {
if (item.getSkuId() != null) {
snapshotMap.put(item.getSkuId(), item.getSkuSpecSnapshot());
}
}
for (GaoStockChangeItem item : stockItems) {
JsonNode snapshot = snapshotMap.get(item.getSkuId());
if (snapshot != null) {
item.setSkuSpecSnapshot(snapshot);
}
}
}
private List<GaoOrderFulfillmentItemRequest> itemRequests(String fulfillmentId) {
return copyRequestItems(itemMapper.selectByFulfillmentId(fulfillmentId));
}
@@ -531,6 +555,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
result.setSkuCodeSnapshot(source.getSkuCodeSnapshot());
result.setSkuNameSnapshot(source.getSkuNameSnapshot());
result.setSpecSummarySnapshot(source.getSpecSummarySnapshot());
result.setSkuSpecSnapshot(source.getSkuSpecSnapshot());
result.setUnitSnapshot(source.getUnitSnapshot());
result.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
return result;
@@ -644,7 +669,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
}
private record SourceEntry(String sourceOrderItemId, String skuId, String skuUnitId, String productIdSnapshot,
String productNameSnapshot, String skuCodeSnapshot, String skuNameSnapshot, String specSummarySnapshot,
String productNameSnapshot, String skuCodeSnapshot, String skuNameSnapshot, String specSummarySnapshot, JsonNode skuSpecSnapshot,
String unitSnapshot, BigDecimal baseQuantityPerUnitSnapshot, BigDecimal quantity,
BigDecimal totalQuantity, BigDecimal reservedBefore, BigDecimal fulfilledBefore, GaoProduct product, GaoProductSku sku,
GaoProductSkuUnit unit) {
@@ -670,6 +695,7 @@ public class GaoOrderFulfillmentServiceImplement extends AbstractEntityService<G
result.setSkuCodeSnapshot(skuCodeSnapshot);
result.setSkuNameSnapshot(skuNameSnapshot);
result.setSpecSummarySnapshot(specSummarySnapshot);
result.setSkuSpecSnapshot(skuSpecSnapshot);
result.setUnitSnapshot(unitSnapshot);
result.setBaseQuantityPerUnitSnapshot(baseQuantityPerUnitSnapshot);
return result;
@@ -5,6 +5,9 @@ import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.service.AttachmentService;
import com.imyeyu.api.modules.gao.entity.GaoProduct;
import com.imyeyu.api.modules.gao.entity.GaoProductCategory;
import com.imyeyu.api.modules.gao.entity.GaoProductSpec;
import com.imyeyu.api.modules.gao.entity.GaoProductSpecValue;
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
import com.imyeyu.api.modules.gao.mapper.GaoProductMapper;
import com.imyeyu.api.modules.gao.service.GaoProductCategoryService;
import com.imyeyu.api.modules.gao.service.GaoProductService;
@@ -28,6 +31,7 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -102,6 +106,8 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
loggerService.execute(Logger.Module.GAO, "GAO_PRODUCT_CREATE", product, () -> {
prepare(product, null);
super.create(product);
Map<String, String> specValueIdMap = saveSpecs(product, product.getSpecList());
normalizeSkuSpecValueIds(product.getSkuList(), specValueIdMap);
saveSkus(product, product.getSkuList());
attachmentService.updateByBizId(Attachment.BizType.GAO_PRODUCT, product.getId(), product.getAttachmentList());
return product;
@@ -196,6 +202,48 @@ public class GaoProductServiceImplement extends AbstractEntityService<GaoProduct
}
}
private Map<String, String> saveSpecs(GaoProduct product, List<GaoProductSpec> specList) {
Map<String, String> specValueIdMap = new HashMap<>();
if (specList == null || specList.isEmpty()) {
return specValueIdMap;
}
for (int specIndex = 0; specIndex < specList.size(); specIndex++) {
GaoProductSpec spec = specList.get(specIndex);
spec.setId(null);
spec.setProductId(product.getId());
spec.setSort(TimiJava.defaultIfNull(spec.getSort(), specIndex));
specServiceProvider.getObject().createSpec(spec);
List<GaoProductSpecValue> valueList = spec.getValueList();
if (valueList == null) {
continue;
}
for (int valueIndex = 0; valueIndex < valueList.size(); valueIndex++) {
GaoProductSpecValue value = valueList.get(valueIndex);
String clientKey = value.getClientKey();
value.setId(null);
value.setSpecId(spec.getId());
value.setSort(TimiJava.defaultIfNull(value.getSort(), valueIndex));
specServiceProvider.getObject().createValue(value);
if (clientKey != null && !clientKey.isBlank()) {
specValueIdMap.put(clientKey, value.getId());
}
}
}
return specValueIdMap;
}
private void normalizeSkuSpecValueIds(List<GaoProductSku> skuList, Map<String, String> specValueIdMap) {
if (skuList == null || skuList.isEmpty() || specValueIdMap.isEmpty()) {
return;
}
for (GaoProductSku sku : skuList) {
List<String> specValueIdList = sku.getSpecValueIdList();
if (specValueIdList != null) {
sku.setSpecValueIdList(specValueIdList.stream().map(id -> specValueIdMap.getOrDefault(id, id)).toList());
}
}
}
private void fillTransient(List<GaoProduct> list) {
if (list == null || list.isEmpty()) {
return;
@@ -1,5 +1,9 @@
package com.imyeyu.api.modules.gao.service.implement;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.imyeyu.api.config.dbsource.TimiServerDBConfig;
import com.imyeyu.api.modules.common.entity.Attachment;
import com.imyeyu.api.modules.common.service.AttachmentService;
@@ -34,6 +38,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -120,6 +125,45 @@ public class GaoProductSkuServiceImplement extends AbstractEntityService<GaoProd
return unitMapper.selectBySkuId(skuId);
}
@Override
public JsonNode getSkuSpecSnapshot(String skuId) {
TimiException.required(skuId, "not found skuId");
GaoProductSku sku = get(skuId);
TimiException.required(sku, "not found product sku");
return getSkuSpecSnapshot(sku);
}
@Override
public JsonNode getSkuSpecSnapshot(GaoProductSku sku) {
TimiException.required(sku, "not found product sku");
List<GaoProductSpec> specList = specMapper.selectByProductId(sku.getProductId());
if (specList.isEmpty()) {
return JsonNodeFactory.instance.arrayNode();
}
List<String> specIdList = specList.stream().map(GaoProductSpec::getId).toList();
Map<String, List<GaoProductSpecValue>> valueMap = new LinkedHashMap<>();
for (GaoProductSpecValue value : valueMapper.selectBySpecIdList(specIdList)) {
valueMap.computeIfAbsent(value.getSpecId(), key -> new ArrayList<>()).add(value);
}
Set<String> selectedValueIdSet = skuSpecValueMapper.selectBySkuIdList(List.of(sku.getId())).stream()
.map(GaoProductSkuSpecValue::getSpecValueId)
.collect(Collectors.toSet());
ArrayNode result = JsonNodeFactory.instance.arrayNode();
for (GaoProductSpec spec : specList) {
for (GaoProductSpecValue value : valueMap.getOrDefault(spec.getId(), List.of())) {
if (!selectedValueIdSet.contains(value.getId())) {
continue;
}
ObjectNode snapshot = result.addObject();
snapshot.put("specId", spec.getId());
snapshot.put("specName", spec.getName());
snapshot.put("specValueId", value.getId());
snapshot.put("specValueName", value.getName());
}
}
return result;
}
@Transactional(TimiServerDBConfig.ROLLBACKER)
@Override
public void create(GaoProductSku sku) {
@@ -138,4 +138,5 @@ public class GaoProductStockRecordServiceImplement extends AbstractEntityService
record.setOperator(userMap.get(record.getOperatorUserId()));
}
}
}
@@ -214,6 +214,7 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
TimiException.requiredTrue(hasAttachment(request.getAttachmentList()), "库存操作必须上传照片");
TimiException.requiredTrue(hasText(request.getRemark()), "库存操作必须填写备注");
GaoStockChangeItem item = GaoStockChangeItem.of(product, sku, unit, quantity, baseQuantity);
item.setSkuSpecSnapshot(skuService.getSkuSpecSnapshot(sku));
List<GaoProductStock> result = executeInternal(storeId, sku.getId(), List.of(item), requestNo,
userLoginService.getRequireLoginUserId(), operationType, request.getRemark(), request.getAttachmentList());
return result.getFirst();
@@ -265,6 +266,7 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
revoke.setSkuCodeSnapshot(source.getSkuCodeSnapshot());
revoke.setSkuNameSnapshot(source.getSkuNameSnapshot());
revoke.setSpecSummarySnapshot(source.getSpecSummarySnapshot());
revoke.setSkuSpecSnapshot(source.getSkuSpecSnapshot());
revoke.setUnitSnapshot(source.getUnitSnapshot());
revoke.setBaseQuantityPerUnitSnapshot(source.getBaseQuantityPerUnitSnapshot());
revoke.setQuantity(source.getQuantity());
@@ -341,6 +343,7 @@ public class GaoProductStockServiceImplement implements GaoProductStockService {
record.setSkuCodeSnapshot(item.getSkuCodeSnapshot());
record.setSkuNameSnapshot(item.getSkuNameSnapshot());
record.setSpecSummarySnapshot(item.getSpecSummarySnapshot());
record.setSkuSpecSnapshot(item.getSkuSpecSnapshot());
record.setUnitSnapshot(item.getUnitSnapshot());
record.setBaseQuantityPerUnitSnapshot(item.getBaseQuantityPerUnitSnapshot());
record.setQuantity(item.getQuantity());
@@ -379,6 +379,7 @@ public class GaoSalesOrderServiceImplement extends AbstractEntityService<GaoSale
item.setSkuCodeSnapshot(sku.getCode());
item.setSkuNameSnapshot(sku.getName());
item.setSpecSummarySnapshot(sku.getSpecSummary());
item.setSkuSpecSnapshot(skuService.getSkuSpecSnapshot(sku));
item.setUnitSnapshot(unit.getName());
item.setBaseQuantityPerUnitSnapshot(unit.getBaseQuantityPerUnit());
item.setQuantity(quantity);
@@ -1,5 +1,6 @@
package com.imyeyu.api.modules.gao.vo;
import com.fasterxml.jackson.databind.JsonNode;
import com.imyeyu.api.modules.gao.entity.GaoProduct;
import com.imyeyu.api.modules.gao.entity.GaoProductSku;
import com.imyeyu.api.modules.gao.entity.GaoProductSkuUnit;
@@ -35,6 +36,9 @@ public class GaoStockChangeItem {
/// 规格摘要快照
private String specSummarySnapshot;
/// SKU 规格结构快照,数组元素包含 specId、specName、specValueId、specValueName
private JsonNode skuSpecSnapshot;
/// 单位名称快照
private String unitSnapshot;
@@ -0,0 +1,5 @@
ALTER TABLE `gao_product_stock_record`
ADD COLUMN `sku_spec_snapshot` JSON NOT NULL COMMENT 'SKU 规格结构快照' AFTER `spec_summary_snapshot`;
ALTER TABLE `gao_order_fulfillment_item`
ADD COLUMN `sku_spec_snapshot` JSON NOT NULL COMMENT 'SKU 规格结构快照' AFTER `spec_summary_snapshot`;
@@ -0,0 +1,2 @@
ALTER TABLE `gao_sales_order_item`
ADD COLUMN `sku_spec_snapshot` JSON NOT NULL COMMENT 'SKU 规格结构快照' AFTER `spec_summary_snapshot`;