Ontology 实战:电商平台建模
一个中型电商平台通常涉及以下核心业务实体:
Coomia发布于 2025年8月19日16 分钟阅读
分享本文Twitter / X
Ontology 实战:电商平台建模
“系列:S4 本体建模 · 第 15 篇 | 难度:中级 | 阅读时间:18 分钟
#TL;DR
- 电商平台是 Ontology 建模的经典练习场——Product、Order、User、Inventory、Logistics 五大核心 ObjectType 覆盖了从商品上架到用户收货的完整业务链路,展示了 Ontology 五元组的全部建模能力。
- 派生属性消除了"指标散落在代码中"的痛点——商品销量、用户 LTV、库存周转率等关键指标通过声明式定义自动计算,无需写额外的 ETL 或统计 SQL。
- ActionType 将业务操作标准化——下单、支付、发货、退款四大核心操作通过 ActionType 声明,平台自动处理幂等、权限、审计,开发者只需关注业务逻辑。
#1. 引言:电商系统的建模挑战
一个中型电商平台通常涉及以下核心业务实体:
Code
┌─────────────────────────────────────────────────────────┐
│ 电商平台业务全景 │
│ │
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
│ │ 用户 │───►│ 订单 │───►│ 物流 │───►│ 签收 │ │
│ └──┬───┘ └──┬───┘ └──────┘ └──────┘ │
│ │ │ │
│ │ ┌────┴────┐ │
│ │ │ 订单项 │ │
│ │ └────┬────┘ │
│ │ │ │
│ │ ┌────┴────┐ ┌──────┐ ┌──────┐ │
│ └─────►│ 商品 │◄───│ 库存 │◄───│ 仓库 │ │
│ └────┬────┘ └──────┘ └──────┘ │
│ │ │
│ ┌────┴────┐ ┌──────┐ │
│ │ 品类 │ │ 品牌 │ │
│ └─────────┘ └──────┘ │
└─────────────────────────────────────────────────────────┘
传统 ER 建模只能描述这些实体的数据结构,而 Ontology 建模还需要回答:
- 用户的终身价值(LTV)如何实时计算?
- "下单"这个操作需要检查哪些前置条件?
- 库存不足时如何自动触发采购?
- 如何跨类型统一查询所有"可搜索"的对象?
#2. ObjectType 定义
#2.1 用户(User)
YAML
apiVersion: ontology/v1
kind: ObjectType
metadata:
name: User
displayName: 用户
spec:
primaryKey: userId
implements:
- Auditable
- Taggable
properties:
userId:
type: STRING
required: true
description: 用户唯一标识
username:
type: STRING
required: true
constraints:
minLength: 3
maxLength: 32
pattern: "^[a-zA-Z0-9_]+$"
email:
type: STRING
constraints:
pattern: "^[\\w.-]+@[\\w.-]+\\.\\w+$"
phone:
type: STRING
displayName:
type: STRING
avatarUrl:
type: STRING
memberLevel:
type: STRING
enum: [REGULAR, SILVER, GOLD, PLATINUM, DIAMOND]
default: REGULAR
shippingAddresses:
type: ARRAY
itemType: STRUCT
structType: Address
isActive:
type: BOOLEAN
default: true
# 派生属性
totalOrders:
type: INTEGER
derived: true
aggregation: COUNT
sourceRelation: PlacedOrder
totalSpent:
type: DOUBLE
derived: true
aggregation: SUM
sourceRelation: PlacedOrder
sourceProperty: totalAmount
averageOrderValue:
type: DOUBLE
derived: true
expression: "totalSpent / NULLIF(totalOrders, 0)"
daysSinceLastOrder:
type: INTEGER
derived: true
expression: "DATEDIFF(NOW(), lastOrderDate)"
lifetimeValue:
type: DOUBLE
derived: true
expression: "totalSpent * (1 + repeatRate * 0.5)"
#2.2 商品(Product)
YAML
apiVersion: ontology/v1
kind: ObjectType
metadata:
name: Product
displayName: 商品
spec:
primaryKey: productId
implements:
- Auditable
- Taggable
- Searchable
properties:
productId:
type: STRING
required: true
sku:
type: STRING
required: true
constraints:
unique: true
name:
type: STRING
required: true
searchable: true
description:
type: STRING
searchable: true
mainImageUrl:
type: STRING
imageUrls:
type: ARRAY
itemType: STRING
basePrice:
type: DOUBLE
required: true
constraints:
min: 0.01
currentPrice:
type: DOUBLE
constraints:
min: 0.01
costPrice:
type: DOUBLE
currency:
type: STRING
default: CNY
weight:
type: DOUBLE
description: 重量(kg)
dimensions:
type: STRUCT
structType: Dimensions
status:
type: STRING
enum: [DRAFT, ACTIVE, OUT_OF_STOCK, DISCONTINUED]
default: DRAFT
isOnSale:
type: BOOLEAN
default: false
salePrice:
type: DOUBLE
# 派生属性
totalSold:
type: INTEGER
derived: true
aggregation: SUM
sourceRelation: ContainedInOrderItem
sourceProperty: quantity
totalRevenue:
type: DOUBLE
derived: true
aggregation: SUM
sourceRelation: ContainedInOrderItem
sourceProperty: subtotal
averageRating:
type: DOUBLE
derived: true
aggregation: AVG
sourceRelation: HasReview
sourceProperty: rating
reviewCount:
type: INTEGER
derived: true
aggregation: COUNT
sourceRelation: HasReview
grossMargin:
type: DOUBLE
derived: true
expression: "(currentPrice - costPrice) / currentPrice"
availableStock:
type: INTEGER
derived: true
aggregation: SUM
sourceRelation: StoredIn
sourceProperty: availableQuantity
#2.3 订单(Order)
YAML
apiVersion: ontology/v1
kind: ObjectType
metadata:
name: Order
displayName: 订单
spec:
primaryKey: orderId
implements:
- Auditable
- Statusable
properties:
orderId:
type: STRING
required: true
orderNumber:
type: STRING
required: true
constraints:
unique: true
status:
type: STRING
enum: [PENDING, PAID, PROCESSING, SHIPPED, DELIVERED, COMPLETED,
CANCELLED, REFUNDING, REFUNDED]
default: PENDING
totalAmount:
type: DOUBLE
required: true
discountAmount:
type: DOUBLE
default: 0
shippingFee:
type: DOUBLE
default: 0
payableAmount:
type: DOUBLE
derived: true
expression: "totalAmount - discountAmount + shippingFee"
paymentMethod:
type: STRING
enum: [ALIPAY, WECHAT_PAY, CREDIT_CARD, BANK_TRANSFER]
paidAt:
type: TIMESTAMP
shippedAt:
type: TIMESTAMP
deliveredAt:
type: TIMESTAMP
completedAt:
type: TIMESTAMP
cancelledAt:
type: TIMESTAMP
cancelReason:
type: STRING
shippingAddress:
type: STRUCT
structType: Address
note:
type: STRING
constraints:
maxLength: 500
# 派生属性
itemCount:
type: INTEGER
derived: true
aggregation: COUNT
sourceRelation: ContainsItem
totalQuantity:
type: INTEGER
derived: true
aggregation: SUM
sourceRelation: ContainsItem
sourceProperty: quantity
processingDays:
type: INTEGER
derived: true
expression: "DATEDIFF(shippedAt, paidAt)"
deliveryDays:
type: INTEGER
derived: true
expression: "DATEDIFF(deliveredAt, shippedAt)"
#2.4 订单项(OrderItem)
YAML
apiVersion: ontology/v1
kind: ObjectType
metadata:
name: OrderItem
displayName: 订单项
spec:
primaryKey: itemId
properties:
itemId:
type: STRING
required: true
quantity:
type: INTEGER
required: true
constraints:
min: 1
unitPrice:
type: DOUBLE
required: true
subtotal:
type: DOUBLE
derived: true
expression: "quantity * unitPrice"
discountAmount:
type: DOUBLE
default: 0
finalPrice:
type: DOUBLE
derived: true
expression: "subtotal - discountAmount"
productSnapshot:
type: STRUCT
structType: ProductSnapshot
description: 下单时的商品快照,防止商品修改后影响历史订单
#2.5 库存(Inventory)
YAML
apiVersion: ontology/v1
kind: ObjectType
metadata:
name: Inventory
displayName: 库存
spec:
primaryKey: inventoryId
implements:
- Auditable
- Measurable
properties:
inventoryId:
type: STRING
required: true
totalQuantity:
type: INTEGER
required: true
constraints:
min: 0
availableQuantity:
type: INTEGER
derived: true
expression: "totalQuantity - reservedQuantity - damagedQuantity"
reservedQuantity:
type: INTEGER
default: 0
description: 已被订单锁定但未出库的数量
damagedQuantity:
type: INTEGER
default: 0
reorderPoint:
type: INTEGER
default: 10
description: 安全库存阈值
maxQuantity:
type: INTEGER
description: 最大库存量
isLowStock:
type: BOOLEAN
derived: true
expression: "availableQuantity <= reorderPoint"
turnoverRate:
type: DOUBLE
derived: true
expression: "soldLast30Days / AVG(availableQuantity)"
description: 库存周转率
daysOfSupply:
type: DOUBLE
derived: true
expression: "availableQuantity / (soldLast30Days / 30)"
description: 按当前销售速度可供应的天数
#2.6 物流(Shipment)
YAML
apiVersion: ontology/v1
kind: ObjectType
metadata:
name: Shipment
displayName: 物流
spec:
primaryKey: shipmentId
implements:
- Auditable
- Statusable
- Locatable
properties:
shipmentId:
type: STRING
required: true
trackingNumber:
type: STRING
constraints:
unique: true
carrier:
type: STRING
enum: [SF_EXPRESS, ZTO, YTO, STO, YUNDA, JD_LOGISTICS, EMS]
status:
type: STRING
enum: [PENDING, PICKED_UP, IN_TRANSIT, OUT_FOR_DELIVERY,
DELIVERED, RETURNED, LOST]
default: PENDING
estimatedDeliveryDate:
type: DATE
actualDeliveryDate:
type: DATE
originAddress:
type: STRUCT
structType: Address
destinationAddress:
type: STRUCT
structType: Address
weight:
type: DOUBLE
shippingCost:
type: DOUBLE
trackingEvents:
type: ARRAY
itemType: STRUCT
structType: TrackingEvent
isDelayed:
type: BOOLEAN
derived: true
expression: "NOW() > estimatedDeliveryDate AND status != 'DELIVERED'"
#3. StructType 定义
YAML
# 地址结构
kind: StructType
metadata:
name: Address
spec:
properties:
province: { type: STRING }
city: { type: STRING }
district: { type: STRING }
street: { type: STRING }
postalCode: { type: STRING }
receiverName: { type: STRING }
receiverPhone: { type: STRING }
---
# 商品尺寸
kind: StructType
metadata:
name: Dimensions
spec:
properties:
length: { type: DOUBLE, description: "长度(cm)" }
width: { type: DOUBLE, description: "宽度(cm)" }
height: { type: DOUBLE, description: "高度(cm)" }
---
# 商品快照(订单项中冻结的商品信息)
kind: StructType
metadata:
name: ProductSnapshot
spec:
properties:
productId: { type: STRING }
name: { type: STRING }
sku: { type: STRING }
imageUrl: { type: STRING }
price: { type: DOUBLE }
---
# 物流追踪事件
kind: StructType
metadata:
name: TrackingEvent
spec:
properties:
timestamp: { type: TIMESTAMP }
location: { type: STRING }
description: { type: STRING }
status: { type: STRING }
#4. InterfaceType 定义
YAML
# 可搜索接口
kind: InterfaceType
metadata:
name: Searchable
spec:
properties:
searchableText:
type: STRING
description: 全文搜索索引字段
searchRank:
type: DOUBLE
description: 搜索排序权重
implementedBy:
- Product
- Category
- Brand
---
# 可评价接口
kind: InterfaceType
metadata:
name: Reviewable
spec:
properties:
averageRating:
type: DOUBLE
reviewCount:
type: INTEGER
latestReviewAt:
type: TIMESTAMP
implementedBy:
- Product
- Shipment
#5. RelationType 定义
Python
from ontology_sdk import RelationType
# 核心关系定义
relations = [
# 用户 → 订单
RelationType(
name="PlacedOrder",
source="User",
target="Order",
cardinality="ONE_TO_MANY",
description="用户下单",
),
# 订单 → 订单项
RelationType(
name="ContainsItem",
source="Order",
target="OrderItem",
cardinality="ONE_TO_MANY",
description="订单包含的商品项",
cascade_delete=True,
),
# 订单项 → 商品
RelationType(
name="RefersToProduct",
source="OrderItem",
target="Product",
cardinality="MANY_TO_ONE",
description="订单项对应的商品",
),
# 商品 → 品类
RelationType(
name="BelongsToCategory",
source="Product",
target="Category",
cardinality="MANY_TO_ONE",
description="商品所属品类",
),
# 商品 → 品牌
RelationType(
name="ProducedByBrand",
source="Product",
target="Brand",
cardinality="MANY_TO_ONE",
description="商品所属品牌",
),
# 商品 → 库存(通过仓库)
RelationType(
name="StoredIn",
source="Product",
target="Inventory",
cardinality="ONE_TO_MANY",
properties={
"warehouseId": "STRING",
},
description="商品在各仓库的库存",
),
# 库存 → 仓库
RelationType(
name="LocatedInWarehouse",
source="Inventory",
target="Warehouse",
cardinality="MANY_TO_ONE",
description="库存所在仓库",
),
# 订单 → 物流
RelationType(
name="ShippedVia",
source="Order",
target="Shipment",
cardinality="ONE_TO_ONE",
description="订单的物流信息",
),
# 用户 → 商品(浏览)
RelationType(
name="Viewed",
source="User",
target="Product",
cardinality="MANY_TO_MANY",
properties={
"viewedAt": "TIMESTAMP",
"duration": "INTEGER",
},
description="用户浏览商品记录",
),
# 用户 → 商品(收藏)
RelationType(
name="Favorited",
source="User",
target="Product",
cardinality="MANY_TO_MANY",
properties={
"favoritedAt": "TIMESTAMP",
},
description="用户收藏商品",
),
# 用户 → 商品(评价)
RelationType(
name="ReviewedProduct",
source="User",
target="Product",
cardinality="MANY_TO_MANY",
properties={
"rating": "INTEGER",
"comment": "STRING",
"reviewedAt": "TIMESTAMP",
"images": "ARRAY[STRING]",
},
description="用户对商品的评价",
),
]
关系图:
Code
┌──────────┐
│ User │
└────┬─────┘
│
┌───────────────┼────────────────┐
│ PlacedOrder │ Favorited │ Viewed
▼ ▼ ▼
┌────────┐ ┌──────────┐ ┌──────────┐
│ Order │ │ Product │◄────│ Category │
└────┬───┘ └────┬─────┘ └──────────┘
│ │
ContainsItem StoredIn ProducedByBrand
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│OrderItem │ │Inventory │ │ Brand │
└────┬─────┘ └────┬─────┘ └──────────┘
│ │
RefersToProduct LocatedIn
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Product │ │Warehouse │
└──────────┘ └──────────┘
Order ──ShippedVia──► Shipment
#6. ActionType 定义
#6.1 下单(PlaceOrder)
YAML
apiVersion: ontology/v1
kind: ActionType
metadata:
name: PlaceOrder
displayName: 下单
spec:
description: 用户提交订单
objectType: Order
executor: FUNCTION
parameters:
userId:
type: STRING
required: true
items:
type: ARRAY
itemType: STRUCT
structType: OrderItemInput
constraints:
minItems: 1
maxItems: 100
shippingAddress:
type: STRUCT
structType: Address
required: true
couponCode:
type: STRING
note:
type: STRING
preconditions:
- name: user_active
expression: "User[userId].isActive == true"
errorMessage: 用户已被禁用
- name: items_in_stock
expression: "ALL(items, item => Product[item.productId].availableStock >= item.quantity)"
errorMessage: 部分商品库存不足
- name: items_on_sale
expression: "ALL(items, item => Product[item.productId].status == 'ACTIVE')"
errorMessage: 部分商品已下架
sideEffects:
- type: CREATE
target: Order
- type: CREATE
target: OrderItem
foreach: items
- type: UPDATE
target: Inventory
property: reservedQuantity
expression: "reservedQuantity + item.quantity"
idempotency:
key: "userId + hash(items) + timestamp(5min)"
strategy: REJECT_DUPLICATE
permissions:
- role: CUSTOMER
condition: "userId == currentUser.id"
audit:
level: FULL
includeParameters: true
#6.2 支付(PayOrder)
YAML
apiVersion: ontology/v1
kind: ActionType
metadata:
name: PayOrder
displayName: 支付订单
spec:
objectType: Order
executor: FUNCTION
parameters:
orderId:
type: STRING
required: true
paymentMethod:
type: STRING
required: true
enum: [ALIPAY, WECHAT_PAY, CREDIT_CARD, BANK_TRANSFER]
paymentToken:
type: STRING
required: true
preconditions:
- name: order_pending
expression: "Order[orderId].status == 'PENDING'"
errorMessage: 订单状态不允许支付
- name: not_expired
expression: "DATEDIFF(NOW(), Order[orderId].createdAt) < 30"
errorMessage: 订单已超时,请重新下单
sideEffects:
- type: UPDATE
target: Order
properties:
status: PAID
paymentMethod: parameters.paymentMethod
paidAt: NOW()
timeout: 30s
retryPolicy:
maxRetries: 3
backoff: EXPONENTIAL
#6.3 发货(ShipOrder)
YAML
apiVersion: ontology/v1
kind: ActionType
metadata:
name: ShipOrder
displayName: 发货
spec:
objectType: Order
executor: FUNCTION
parameters:
orderId:
type: STRING
required: true
carrier:
type: STRING
required: true
trackingNumber:
type: STRING
required: true
warehouseId:
type: STRING
required: true
preconditions:
- name: order_paid
expression: "Order[orderId].status == 'PAID' OR Order[orderId].status == 'PROCESSING'"
sideEffects:
- type: UPDATE
target: Order
properties:
status: SHIPPED
shippedAt: NOW()
- type: CREATE
target: Shipment
- type: UPDATE
target: Inventory
property: totalQuantity
expression: "totalQuantity - orderItem.quantity"
- type: UPDATE
target: Inventory
property: reservedQuantity
expression: "reservedQuantity - orderItem.quantity"
permissions:
- role: WAREHOUSE_OPERATOR
- role: ADMIN
#6.4 退款(RefundOrder)
YAML
apiVersion: ontology/v1
kind: ActionType
metadata:
name: RefundOrder
displayName: 退款
spec:
objectType: Order
executor: APPROVAL_THEN_FUNCTION
parameters:
orderId:
type: STRING
required: true
reason:
type: STRING
required: true
enum: [QUALITY_ISSUE, WRONG_ITEM, NOT_AS_DESCRIBED, CHANGED_MIND, OTHER]
refundAmount:
type: DOUBLE
required: true
evidence:
type: ARRAY
itemType: STRING
description: 退款证据图片URL
preconditions:
- name: refundable_status
expression: "Order[orderId].status IN ['PAID', 'SHIPPED', 'DELIVERED']"
- name: within_refund_window
expression: "DATEDIFF(NOW(), Order[orderId].completedAt) <= 7"
errorMessage: 已超过 7 天退款期限
approval:
approvers:
- role: CUSTOMER_SERVICE
timeout: 48h
autoApproveCondition: "refundAmount < 100 AND reason != 'CHANGED_MIND'"
sideEffects:
- type: UPDATE
target: Order
properties:
status: REFUNDED
- type: UPDATE
target: Inventory
property: totalQuantity
expression: "totalQuantity + returnedQuantity"
permissions:
- role: CUSTOMER
condition: "Order[orderId].userId == currentUser.id"
#7. 指标设计
Python
# 商品指标
product_metrics = [
MetricSpec(
name="ProductConversionRate",
description="商品浏览到购买的转化率",
expression="totalSold / viewCount",
dimensions=["category", "brand"],
unit="percentage",
),
MetricSpec(
name="ProductGMV",
description="商品成交总额",
object_type="Product",
property="totalRevenue",
aggregation="SUM",
dimensions=["category", "brand"],
time_series=True,
unit="CNY",
),
]
# 订单指标
order_metrics = [
MetricSpec(
name="OrderAverageValue",
description="客单价",
object_type="Order",
property="payableAmount",
aggregation="AVG",
filter="status NOT IN ['CANCELLED', 'REFUNDED']",
dimensions=["paymentMethod"],
time_series=True,
unit="CNY",
),
MetricSpec(
name="OrderFulfillmentRate",
description="订单履约率",
expression="COUNT(status='COMPLETED') / COUNT(status='PAID')",
dimensions=["warehouse"],
unit="percentage",
),
MetricSpec(
name="AverageDeliveryDays",
description="平均配送天数",
object_type="Order",
property="deliveryDays",
aggregation="AVG",
filter="status = 'COMPLETED'",
dimensions=["carrier", "region"],
unit="days",
),
]
# 用户指标
user_metrics = [
MetricSpec(
name="UserRetentionRate",
description="用户留存率(30天)",
expression="COUNT(hasOrderInLast30Days) / COUNT(ALL)",
dimensions=["memberLevel", "registrationChannel"],
unit="percentage",
),
MetricSpec(
name="UserLifetimeValue",
description="用户终身价值",
object_type="User",
property="lifetimeValue",
aggregation="AVG",
dimensions=["memberLevel"],
unit="CNY",
),
]
# 库存指标
inventory_metrics = [
MetricSpec(
name="InventoryTurnoverRate",
description="库存周转率",
object_type="Inventory",
property="turnoverRate",
aggregation="AVG",
dimensions=["warehouse", "category"],
alert_rules=[
AlertRule("turnover-low", "value < 2", "WARNING",
"仓库 {warehouse} 品类 {category} 周转率低于 2"),
],
),
MetricSpec(
name="LowStockItems",
description="低库存商品数",
object_type="Inventory",
filter="isLowStock == true",
aggregation="COUNT",
dimensions=["warehouse"],
alert_rules=[
AlertRule("low-stock-critical", "value > 50", "CRITICAL",
"仓库 {warehouse} 有 {value} 个商品低库存"),
],
),
]
#8. 完整代码示例
#8.1 使用 SDK 创建电商 Ontology
Python
from ontology_sdk import OntologyClient, ObjectTypeSpec, PropertySpec
client = OntologyClient(
base_url="http://control-Layer:8080",
project_id="ecommerce-platform",
)
# 创建 StructType
client.schema.create_struct_type(StructTypeSpec(
name="Address",
properties={
"province": PropertySpec(type="STRING"),
"city": PropertySpec(type="STRING"),
"district": PropertySpec(type="STRING"),
"street": PropertySpec(type="STRING"),
"postalCode": PropertySpec(type="STRING"),
"receiverName": PropertySpec(type="STRING"),
"receiverPhone": PropertySpec(type="STRING"),
},
))
# 创建 InterfaceType
client.schema.create_interface_type(InterfaceTypeSpec(
name="Searchable",
properties={
"searchableText": PropertySpec(type="STRING"),
"searchRank": PropertySpec(type="DOUBLE"),
},
))
# 创建 ObjectType: User
client.schema.create_object_type(ObjectTypeSpec(
name="User",
display_name="用户",
primary_key="userId",
implements=["Auditable", "Taggable"],
properties={
"userId": PropertySpec(type="STRING", required=True),
"username": PropertySpec(type="STRING", required=True),
"email": PropertySpec(type="STRING"),
"memberLevel": PropertySpec(
type="STRING",
enum=["REGULAR", "SILVER", "GOLD", "PLATINUM", "DIAMOND"],
default="REGULAR",
),
"isActive": PropertySpec(type="BOOLEAN", default=True),
"totalOrders": PropertySpec(
type="INTEGER", derived=True,
aggregation="COUNT", source_relation="PlacedOrder",
),
"lifetimeValue": PropertySpec(
type="DOUBLE", derived=True,
expression="totalSpent * (1 + repeatRate * 0.5)",
),
},
))
# 创建其他 ObjectType ...
# (Product, Order, OrderItem, Inventory, Shipment)
# 创建 RelationType
client.schema.create_relation_type(RelationTypeSpec(
name="PlacedOrder",
source="User",
target="Order",
cardinality="ONE_TO_MANY",
))
# 创建 ActionType
client.schema.create_action_type(ActionTypeSpec(
name="PlaceOrder",
object_type="Order",
executor="FUNCTION",
parameters={...},
preconditions=[...],
side_effects=[...],
))
# 发布所有 Schema
client.schema.publish_all()
print("E-commerce Ontology published successfully!")
#8.2 业务查询示例
Python
# 查询 VIP 用户最近 30 天的订单及物流状态
vip_orders = client.ontology.query(
object_type="User",
filter="memberLevel IN ['GOLD', 'PLATINUM', 'DIAMOND']",
expand=[
"orders[status='SHIPPED'].shipment",
"orders.items.product.category",
],
order_by="lifetimeValue DESC",
limit=100,
)
# 查询所有低库存商品及其供应商
low_stock = client.ontology.query(
object_type="Inventory",
filter="isLowStock == true",
expand=[
"product.brand",
"product.category",
"warehouse",
],
)
# 跨类型搜索(使用 Searchable 接口)
search_results = client.ontology.query(
interface="Searchable",
filter="searchableText CONTAINS '无线蓝牙耳机'",
order_by="searchRank DESC",
limit=20,
)
#9. 数据源映射
Python
# 将现有 MySQL 电商数据库映射到 Ontology
client.connection.register(ConnectionSpec(
name="ecommerce-mysql",
type="MYSQL",
config={"host": "mysql.internal", "port": 3306, "database": "ecommerce"},
credential_ref="ecommerce-db-cred",
))
# 自动发现并映射
discovery = client.connection.discover_schema("ecommerce-mysql")
# 用户表映射
client.connection.create_source_mapping(SourceMappingSpec(
name="user-mapping",
connection="ecommerce-mysql",
source_table="t_user",
target_object_type="User",
mode="REPLICATED",
property_mappings=[
PropertyMapping("user_id", "userId", primary_key=True),
PropertyMapping("user_name", "username"),
PropertyMapping("user_email", "email"),
PropertyMapping("member_level", "memberLevel",
transform="UPPER(value)"),
PropertyMapping("is_active", "isActive",
transform="value = 1"),
],
sync_policy=SyncPolicy(
schedule="*/5 * * * *",
watermark_column="updated_at",
),
))
# 商品表映射
client.connection.create_source_mapping(SourceMappingSpec(
name="product-mapping",
connection="ecommerce-mysql",
source_table="t_product",
target_object_type="Product",
mode="REPLICATED",
property_mappings=[
PropertyMapping("product_id", "productId", primary_key=True),
PropertyMapping("product_name", "name"),
PropertyMapping("product_sku", "sku"),
PropertyMapping("base_price", "basePrice"),
PropertyMapping("current_price", "currentPrice"),
PropertyMapping("product_status", "status"),
],
sync_policy=SyncPolicy(
schedule="*/2 * * * *",
watermark_column="updated_at",
),
))
#10. 常见建模决策与权衡
#10.1 订单快照 vs 引用
Code
方案 A:OrderItem 直接引用 Product(实时)
优点:数据始终最新
缺点:商品修改后历史订单的价格/名称会变
方案 B:OrderItem 保存 ProductSnapshot(快照) ✅ 推荐
优点:历史订单数据不受商品修改影响
缺点:需要额外存储
本方案采用 B:使用 ProductSnapshot StructType
#10.2 库存粒度
Code
方案 A:Product 上直接放 stock 属性
适用:单仓库场景
方案 B:独立 Inventory ObjectType ✅ 推荐
适用:多仓库场景
优点:每个仓库的库存独立管理,支持调拨
本方案采用 B:Product --[StoredIn]--> Inventory --[LocatedIn]--> Warehouse
#10.3 用户地址管理
Code
方案 A:User 上放 addresses: ARRAY[STRING](JSON 数组)
缺点:无类型检查、难以查询
方案 B:独立 Address ObjectType
缺点:过度建模,地址没有独立生命周期
方案 C:User.shippingAddresses: ARRAY[Address](StructType 数组) ✅ 推荐
优点:有类型检查,不过度建模
#Key Takeaways
-
电商 Ontology 的核心是 5 个 ObjectType + 10 个 RelationType。User、Product、Order、Inventory、Shipment 构成了完整的业务闭环,通过关系将它们编织成一个可遍历的语义图。
-
派生属性让指标计算"声明式化"。用户 LTV、商品销量、库存周转率等都通过
derived: true声明,平台自动计算和缓存,无需写 ETL。 -
ActionType 是业务操作的标准化容器。下单、支付、发货、退款四个核心操作通过 ActionType 声明前置条件、副作用、幂等策略和权限模型,大幅减少重复代码。
-
建模决策要考虑长期演进。快照 vs 引用、独立 ObjectType vs 嵌入属性、单仓 vs 多仓——每个决策都应该基于"未来 12 个月的业务需求"来判断。
#下一篇
S4-16: Ontology 实战:制造业建模 —— 我们将用 Equipment/WorkOrder/Line/QC/Supplier 展示制造业场景下的 Ontology 建模实践。
tags: ontology, ecommerce, modeling, product, order, inventory, logistics, action-type, derived-property, coomia-dip