Skip to Content
文档Entityengine实战示例电商应用

电商应用

本文介绍如何使用 Entity Engine 构建一个完整的电商应用系统,涵盖产品管理、订单处理、库存管理、支付集成和用户管理等核心功能。

系统架构

核心实体模型

// 用户模型 export const UserModel: IEntityModel = { name: 'User', fields: { id: { type: 'uuid', primaryKey: true }, email: { type: 'string', unique: true, required: true }, password: { type: 'string', required: true }, firstName: { type: 'string', required: true }, lastName: { type: 'string', required: true }, phone: { type: 'string' }, dateOfBirth: { type: 'date' }, status: { type: 'enum', enumValues: ['ACTIVE', 'INACTIVE', 'SUSPENDED'], defaultValue: 'ACTIVE' }, emailVerified: { type: 'boolean', defaultValue: false }, addresses: { type: 'relation', relation: { type: 'one_to_many', model: 'Address', foreignKey: 'userId' } }, orders: { type: 'relation', relation: { type: 'one_to_many', model: 'Order', foreignKey: 'userId' } }, reviews: { type: 'relation', relation: { type: 'one_to_many', model: 'Review', foreignKey: 'userId' } }, createdAt: { type: 'datetime', defaultValue: 'now' }, updatedAt: { type: 'datetime', defaultValue: 'now' } }, indexes: [ { fields: ['email'], unique: true }, { fields: ['status'] }, { fields: ['createdAt'] } ] }; // 地址模型 export const AddressModel: IEntityModel = { name: 'Address', fields: { id: { type: 'uuid', primaryKey: true }, userId: { type: 'uuid', required: true }, type: { type: 'enum', enumValues: ['BILLING', 'SHIPPING'], required: true }, firstName: { type: 'string', required: true }, lastName: { type: 'string', required: true }, company: { type: 'string' }, street1: { type: 'string', required: true }, street2: { type: 'string' }, city: { type: 'string', required: true }, state: { type: 'string', required: true }, zipCode: { type: 'string', required: true }, country: { type: 'string', required: true, defaultValue: 'US' }, isDefault: { type: 'boolean', defaultValue: false }, user: { type: 'relation', relation: { type: 'many_to_one', model: 'User', targetKey: 'id' } } }, indexes: [ { fields: ['userId', 'type'] }, { fields: ['userId', 'isDefault'] } ] }; // 分类模型 export const CategoryModel: IEntityModel = { name: 'Category', fields: { id: { type: 'uuid', primaryKey: true }, name: { type: 'string', required: true }, slug: { type: 'string', unique: true, required: true }, description: { type: 'string' }, image: { type: 'string' }, parentId: { type: 'uuid' }, parent: { type: 'relation', relation: { type: 'many_to_one', model: 'Category', targetKey: 'id' } }, children: { type: 'relation', relation: { type: 'one_to_many', model: 'Category', foreignKey: 'parentId' } }, products: { type: 'relation', relation: { type: 'one_to_many', model: 'Product', foreignKey: 'categoryId' } }, sortOrder: { type: 'number', defaultValue: 0 }, isActive: { type: 'boolean', defaultValue: true }, seoTitle: { type: 'string' }, seoDescription: { type: 'string' }, seoKeywords: { type: 'string' }, createdAt: { type: 'datetime', defaultValue: 'now' }, updatedAt: { type: 'datetime', defaultValue: 'now' } }, indexes: [ { fields: ['slug'], unique: true }, { fields: ['parentId'] }, { fields: ['isActive', 'sortOrder'] } ] }; // 产品模型 export const ProductModel: IEntityModel = { name: 'Product', fields: { id: { type: 'uuid', primaryKey: true }, sku: { type: 'string', unique: true, required: true }, name: { type: 'string', required: true }, slug: { type: 'string', unique: true, required: true }, description: { type: 'string' }, shortDescription: { type: 'string' }, price: { type: 'decimal', required: true }, comparePrice: { type: 'decimal' }, cost: { type: 'decimal' }, categoryId: { type: 'uuid', required: true }, category: { type: 'relation', relation: { type: 'many_to_one', model: 'Category', targetKey: 'id' } }, brand: { type: 'string' }, weight: { type: 'decimal' }, dimensions: { type: 'json' }, // { width, height, depth } images: { type: 'json' }, // Array of image URLs attributes: { type: 'json' }, // Custom attributes tags: { type: 'json' }, // Array of tags status: { type: 'enum', enumValues: ['DRAFT', 'ACTIVE', 'INACTIVE', 'DISCONTINUED'], defaultValue: 'DRAFT' }, inventory: { type: 'relation', relation: { type: 'one_to_one', model: 'Inventory', foreignKey: 'productId' } }, variants: { type: 'relation', relation: { type: 'one_to_many', model: 'ProductVariant', foreignKey: 'productId' } }, reviews: { type: 'relation', relation: { type: 'one_to_many', model: 'Review', foreignKey: 'productId' } }, seoTitle: { type: 'string' }, seoDescription: { type: 'string' }, seoKeywords: { type: 'string' }, createdAt: { type: 'datetime', defaultValue: 'now' }, updatedAt: { type: 'datetime', defaultValue: 'now' } }, indexes: [ { fields: ['sku'], unique: true }, { fields: ['slug'], unique: true }, { fields: ['categoryId'] }, { fields: ['status'] }, { fields: ['price'] }, { fields: ['createdAt'] } ] }; // 产品变体模型 export const ProductVariantModel: IEntityModel = { name: 'ProductVariant', fields: { id: { type: 'uuid', primaryKey: true }, productId: { type: 'uuid', required: true }, sku: { type: 'string', unique: true, required: true }, name: { type: 'string', required: true }, price: { type: 'decimal', required: true }, comparePrice: { type: 'decimal' }, cost: { type: 'decimal' }, options: { type: 'json' }, // { size: 'M', color: 'Red' } images: { type: 'json' }, weight: { type: 'decimal' }, dimensions: { type: 'json' }, isDefault: { type: 'boolean', defaultValue: false }, product: { type: 'relation', relation: { type: 'many_to_one', model: 'Product', targetKey: 'id' } }, inventory: { type: 'relation', relation: { type: 'one_to_one', model: 'Inventory', foreignKey: 'variantId' } }, createdAt: { type: 'datetime', defaultValue: 'now' }, updatedAt: { type: 'datetime', defaultValue: 'now' } }, indexes: [ { fields: ['sku'], unique: true }, { fields: ['productId'] }, { fields: ['productId', 'isDefault'] } ] }; // 库存模型 export const InventoryModel: IEntityModel = { name: 'Inventory', fields: { id: { type: 'uuid', primaryKey: true }, productId: { type: 'uuid' }, variantId: { type: 'uuid' }, quantity: { type: 'number', required: true, defaultValue: 0 }, reservedQuantity: { type: 'number', defaultValue: 0 }, availableQuantity: { type: 'number', defaultValue: 0 }, reorderPoint: { type: 'number', defaultValue: 10 }, maxQuantity: { type: 'number' }, trackQuantity: { type: 'boolean', defaultValue: true }, allowBackorder: { type: 'boolean', defaultValue: false }, product: { type: 'relation', relation: { type: 'many_to_one', model: 'Product', targetKey: 'id' } }, variant: { type: 'relation', relation: { type: 'many_to_one', model: 'ProductVariant', targetKey: 'id' } }, movements: { type: 'relation', relation: { type: 'one_to_many', model: 'InventoryMovement', foreignKey: 'inventoryId' } }, updatedAt: { type: 'datetime', defaultValue: 'now' } }, indexes: [ { fields: ['productId'], unique: true }, { fields: ['variantId'], unique: true }, { fields: ['quantity'] }, { fields: ['availableQuantity'] } ] }; // 库存变动记录模型 export const InventoryMovementModel: IEntityModel = { name: 'InventoryMovement', fields: { id: { type: 'uuid', primaryKey: true }, inventoryId: { type: 'uuid', required: true }, type: { type: 'enum', enumValues: ['IN', 'OUT', 'RESERVED', 'RELEASED', 'ADJUSTMENT'], required: true }, quantity: { type: 'number', required: true }, reason: { type: 'string', required: true }, reference: { type: 'string' }, // Order ID, etc. notes: { type: 'string' }, userId: { type: 'uuid' }, inventory: { type: 'relation', relation: { type: 'many_to_one', model: 'Inventory', targetKey: 'id' } }, createdAt: { type: 'datetime', defaultValue: 'now' } }, indexes: [ { fields: ['inventoryId'] }, { fields: ['type'] }, { fields: ['reference'] }, { fields: ['createdAt'] } ] }; // 购物车模型 export const CartModel: IEntityModel = { name: 'Cart', fields: { id: { type: 'uuid', primaryKey: true }, userId: { type: 'uuid' }, sessionId: { type: 'string' }, items: { type: 'relation', relation: { type: 'one_to_many', model: 'CartItem', foreignKey: 'cartId' } }, subtotal: { type: 'decimal', defaultValue: 0 }, tax: { type: 'decimal', defaultValue: 0 }, shipping: { type: 'decimal', defaultValue: 0 }, total: { type: 'decimal', defaultValue: 0 }, currency: { type: 'string', defaultValue: 'USD' }, expiresAt: { type: 'datetime' }, createdAt: { type: 'datetime', defaultValue: 'now' }, updatedAt: { type: 'datetime', defaultValue: 'now' } }, indexes: [ { fields: ['userId'] }, { fields: ['sessionId'] }, { fields: ['expiresAt'] } ] }; // 购物车项目模型 export const CartItemModel: IEntityModel = { name: 'CartItem', fields: { id: { type: 'uuid', primaryKey: true }, cartId: { type: 'uuid', required: true }, productId: { type: 'uuid', required: true }, variantId: { type: 'uuid' }, quantity: { type: 'number', required: true, defaultValue: 1 }, price: { type: 'decimal', required: true }, total: { type: 'decimal', required: true }, cart: { type: 'relation', relation: { type: 'many_to_one', model: 'Cart', targetKey: 'id' } }, product: { type: 'relation', relation: { type: 'many_to_one', model: 'Product', targetKey: 'id' } }, variant: { type: 'relation', relation: { type: 'many_to_one', model: 'ProductVariant', targetKey: 'id' } }, addedAt: { type: 'datetime', defaultValue: 'now' } }, indexes: [ { fields: ['cartId'] }, { fields: ['productId'] }, { fields: ['variantId'] } ] }; // 订单模型 export const OrderModel: IEntityModel = { name: 'Order', fields: { id: { type: 'uuid', primaryKey: true }, orderNumber: { type: 'string', unique: true, required: true }, userId: { type: 'uuid', required: true }, status: { type: 'enum', enumValues: ['PENDING', 'CONFIRMED', 'PROCESSING', 'SHIPPED', 'DELIVERED', 'CANCELLED', 'REFUNDED'], defaultValue: 'PENDING' }, paymentStatus: { type: 'enum', enumValues: ['PENDING', 'PAID', 'FAILED', 'REFUNDED', 'PARTIALLY_REFUNDED'], defaultValue: 'PENDING' }, fulfillmentStatus: { type: 'enum', enumValues: ['UNFULFILLED', 'PARTIAL', 'FULFILLED'], defaultValue: 'UNFULFILLED' }, user: { type: 'relation', relation: { type: 'many_to_one', model: 'User', targetKey: 'id' } }, items: { type: 'relation', relation: { type: 'one_to_many', model: 'OrderItem', foreignKey: 'orderId' } }, shippingAddress: { type: 'json', required: true }, billingAddress: { type: 'json', required: true }, subtotal: { type: 'decimal', required: true }, taxAmount: { type: 'decimal', defaultValue: 0 }, shippingAmount: { type: 'decimal', defaultValue: 0 }, discountAmount: { type: 'decimal', defaultValue: 0 }, total: { type: 'decimal', required: true }, currency: { type: 'string', defaultValue: 'USD' }, paymentMethod: { type: 'string' }, paymentReference: { type: 'string' }, shippingMethod: { type: 'string' }, trackingNumber: { type: 'string' }, notes: { type: 'string' }, customerNotes: { type: 'string' }, tags: { type: 'json' }, metadata: { type: 'json' }, createdAt: { type: 'datetime', defaultValue: 'now' }, updatedAt: { type: 'datetime', defaultValue: 'now' }, shippedAt: { type: 'datetime' }, deliveredAt: { type: 'datetime' } }, indexes: [ { fields: ['orderNumber'], unique: true }, { fields: ['userId'] }, { fields: ['status'] }, { fields: ['paymentStatus'] }, { fields: ['fulfillmentStatus'] }, { fields: ['createdAt'] } ] }; // 订单项目模型 export const OrderItemModel: IEntityModel = { name: 'OrderItem', fields: { id: { type: 'uuid', primaryKey: true }, orderId: { type: 'uuid', required: true }, productId: { type: 'uuid', required: true }, variantId: { type: 'uuid' }, sku: { type: 'string', required: true }, name: { type: 'string', required: true }, quantity: { type: 'number', required: true }, price: { type: 'decimal', required: true }, total: { type: 'decimal', required: true }, order: { type: 'relation', relation: { type: 'many_to_one', model: 'Order', targetKey: 'id' } }, product: { type: 'relation', relation: { type: 'many_to_one', model: 'Product', targetKey: 'id' } }, variant: { type: 'relation', relation: { type: 'many_to_one', model: 'ProductVariant', targetKey: 'id' } } }, indexes: [ { fields: ['orderId'] }, { fields: ['productId'] }, { fields: ['variantId'] }, { fields: ['sku'] } ] }; // 评价模型 export const ReviewModel: IEntityModel = { name: 'Review', fields: { id: { type: 'uuid', primaryKey: true }, productId: { type: 'uuid', required: true }, userId: { type: 'uuid', required: true }, orderId: { type: 'uuid' }, rating: { type: 'number', required: true, min: 1, max: 5 }, title: { type: 'string' }, content: { type: 'string' }, pros: { type: 'string' }, cons: { type: 'string' }, images: { type: 'json' }, isVerified: { type: 'boolean', defaultValue: false }, isRecommended: { type: 'boolean' }, helpfulCount: { type: 'number', defaultValue: 0 }, status: { type: 'enum', enumValues: ['PENDING', 'APPROVED', 'REJECTED'], defaultValue: 'PENDING' }, product: { type: 'relation', relation: { type: 'many_to_one', model: 'Product', targetKey: 'id' } }, user: { type: 'relation', relation: { type: 'many_to_one', model: 'User', targetKey: 'id' } }, order: { type: 'relation', relation: { type: 'many_to_one', model: 'Order', targetKey: 'id' } }, createdAt: { type: 'datetime', defaultValue: 'now' }, updatedAt: { type: 'datetime', defaultValue: 'now' } }, indexes: [ { fields: ['productId'] }, { fields: ['userId'] }, { fields: ['orderId'] }, { fields: ['rating'] }, { fields: ['status'] }, { fields: ['createdAt'] } ] };

业务逻辑实现

购物车管理

// 购物车管理动作处理器 class CartManagerActionHandler implements IActionHandler { name = 'cart-manager'; displayName = '购物车管理'; category = ActionCategory.BUSINESS; inputSchema: ActionInputSchema = { type: 'object', properties: { action: { type: 'string', enum: ['add', 'update', 'remove', 'clear', 'get'], required: true }, cartId: { type: 'string' }, userId: { type: 'string' }, sessionId: { type: 'string' }, productId: { type: 'string' }, variantId: { type: 'string' }, quantity: { type: 'number' }, itemId: { type: 'string' } } }; async execute(context: ActionExecutionContext): Promise<ActionResult> { const { input, entityManager } = context; try { switch (input.action) { case 'add': return await this.addToCart(input, entityManager); case 'update': return await this.updateCartItem(input, entityManager); case 'remove': return await this.removeFromCart(input, entityManager); case 'clear': return await this.clearCart(input, entityManager); case 'get': return await this.getCart(input, entityManager); default: throw new Error(`Unknown cart action: ${input.action}`); } } catch (error) { return { success: false, error: { message: error.message, code: 'CART_ERROR' } }; } } private async addToCart(input: any, entityManager: IEntityManager): Promise<ActionResult> { // 获取或创建购物车 let cart = await this.getOrCreateCart(input, entityManager); // 获取产品信息 const product = await entityManager.get('Product', input.productId); if (!product || product.status !== 'ACTIVE') { throw new Error('Product not available'); } // 检查库存 const inventory = await this.checkInventory(input.productId, input.variantId, input.quantity, entityManager); if (!inventory.available) { throw new Error('Insufficient inventory'); } // 检查是否已存在相同商品 const existingItem = await entityManager.query({ entityType: 'CartItem', filter: { cartId: cart.id, productId: input.productId, variantId: input.variantId || null } }); let cartItem; if (existingItem.data.length > 0) { // 更新数量 cartItem = existingItem.data[0]; cartItem.quantity += input.quantity; cartItem.total = cartItem.quantity * cartItem.price; await entityManager.update('CartItem', cartItem.id, cartItem); } else { // 添加新项目 const price = input.variantId ? (await entityManager.get('ProductVariant', input.variantId)).price : product.price; cartItem = await entityManager.create('CartItem', { cartId: cart.id, productId: input.productId, variantId: input.variantId, quantity: input.quantity, price, total: input.quantity * price }); } // 更新购物车总计 cart = await this.updateCartTotals(cart.id, entityManager); return { success: true, data: { cart, cartItem }, events: [{ type: 'cart.item_added', data: { cartId: cart.id, productId: input.productId, variantId: input.variantId, quantity: input.quantity } }] }; } private async updateCartItem(input: any, entityManager: IEntityManager): Promise<ActionResult> { const cartItem = await entityManager.get('CartItem', input.itemId); if (!cartItem) { throw new Error('Cart item not found'); } // 检查库存 const inventory = await this.checkInventory( cartItem.productId, cartItem.variantId, input.quantity, entityManager ); if (!inventory.available) { throw new Error('Insufficient inventory'); } // 更新项目 cartItem.quantity = input.quantity; cartItem.total = cartItem.quantity * cartItem.price; await entityManager.update('CartItem', cartItem.id, cartItem); // 更新购物车总计 const cart = await this.updateCartTotals(cartItem.cartId, entityManager); return { success: true, data: { cart, cartItem }, events: [{ type: 'cart.item_updated', data: { cartId: cartItem.cartId, itemId: cartItem.id, newQuantity: input.quantity } }] }; } private async removeFromCart(input: any, entityManager: IEntityManager): Promise<ActionResult> { const cartItem = await entityManager.get('CartItem', input.itemId); if (!cartItem) { throw new Error('Cart item not found'); } const cartId = cartItem.cartId; await entityManager.delete('CartItem', input.itemId); // 更新购物车总计 const cart = await this.updateCartTotals(cartId, entityManager); return { success: true, data: { cart }, events: [{ type: 'cart.item_removed', data: { cartId, itemId: input.itemId, productId: cartItem.productId } }] }; } private async clearCart(input: any, entityManager: IEntityManager): Promise<ActionResult> { const cart = await this.getOrCreateCart(input, entityManager); // 删除所有购物车项目 const items = await entityManager.query({ entityType: 'CartItem', filter: { cartId: cart.id } }); for (const item of items.data) { await entityManager.delete('CartItem', item.id); } // 重置购物车总计 await entityManager.update('Cart', cart.id, { subtotal: 0, tax: 0, shipping: 0, total: 0 }); return { success: true, data: { cart: { ...cart, subtotal: 0, tax: 0, shipping: 0, total: 0, items: [] } }, events: [{ type: 'cart.cleared', data: { cartId: cart.id } }] }; } private async getCart(input: any, entityManager: IEntityManager): Promise<ActionResult> { const cart = await this.getOrCreateCart(input, entityManager); // 获取购物车项目及关联信息 const items = await entityManager.query({ entityType: 'CartItem', filter: { cartId: cart.id }, include: ['product', 'variant'] }); return { success: true, data: { cart: { ...cart, items: items.data }, itemCount: items.data.length } }; } private async getOrCreateCart(input: any, entityManager: IEntityManager): Promise<any> { let cart; if (input.cartId) { cart = await entityManager.get('Cart', input.cartId); if (cart) return cart; } // 根据用户ID或会话ID查找购物车 const filter: any = {}; if (input.userId) { filter.userId = input.userId; } else if (input.sessionId) { filter.sessionId = input.sessionId; } else { throw new Error('Either userId or sessionId is required'); } const existingCarts = await entityManager.query({ entityType: 'Cart', filter }); if (existingCarts.data.length > 0) { cart = existingCarts.data[0]; } else { // 创建新购物车 const expiresAt = new Date(); expiresAt.setDate(expiresAt.getDate() + 30); // 30天后过期 cart = await entityManager.create('Cart', { userId: input.userId, sessionId: input.sessionId, subtotal: 0, tax: 0, shipping: 0, total: 0, expiresAt }); } return cart; } private async checkInventory( productId: string, variantId?: string, quantity: number = 1, entityManager?: IEntityManager ): Promise<{ available: boolean; availableQuantity: number }> { const filter: any = {}; if (variantId) { filter.variantId = variantId; } else { filter.productId = productId; } const inventories = await entityManager!.query({ entityType: 'Inventory', filter }); if (inventories.data.length === 0) { return { available: false, availableQuantity: 0 }; } const inventory = inventories.data[0]; const available = inventory.trackQuantity ? inventory.availableQuantity >= quantity : true; return { available, availableQuantity: inventory.availableQuantity }; } private async updateCartTotals(cartId: string, entityManager: IEntityManager): Promise<any> { // 获取购物车项目 const items = await entityManager.query({ entityType: 'CartItem', filter: { cartId } }); const subtotal = items.data.reduce((sum, item) => sum + parseFloat(item.total), 0); const tax = subtotal * 0.1; // 简化税率计算 const shipping = subtotal > 100 ? 0 : 10; // 免费配送条件 const total = subtotal + tax + shipping; const cart = await entityManager.update('Cart', cartId, { subtotal, tax, shipping, total }); return cart; } async validate(input: any): Promise<ValidationResult> { const errors: string[] = []; if (!input.action) errors.push('action is required'); if (!['add', 'update', 'remove', 'clear', 'get'].includes(input.action)) { errors.push('Invalid action'); } if (input.action === 'add') { if (!input.productId) errors.push('productId is required for add action'); if (!input.quantity || input.quantity < 1) errors.push('Valid quantity is required for add action'); } if (input.action === 'update') { if (!input.itemId) errors.push('itemId is required for update action'); if (!input.quantity || input.quantity < 1) errors.push('Valid quantity is required for update action'); } if (input.action === 'remove') { if (!input.itemId) errors.push('itemId is required for remove action'); } return { valid: errors.length === 0, errors, warnings: [] }; } getRequiredPermissions(): string[] { return ['cart_access']; } getMetadata(): ActionHandlerMetadata { return { async: false, timeout: 10000, retryable: true, maxRetries: 2 }; } }

订单处理流程

// 订单处理动作处理器 class OrderProcessingActionHandler implements IActionHandler { name = 'process-order'; displayName = '处理订单'; category = ActionCategory.BUSINESS; inputSchema: ActionInputSchema = { type: 'object', properties: { cartId: { type: 'string', required: true }, paymentMethod: { type: 'string', required: true }, paymentToken: { type: 'string' }, shippingAddress: { type: 'object', required: true }, billingAddress: { type: 'object', required: true }, shippingMethod: { type: 'string', required: true }, customerNotes: { type: 'string' } } }; async execute(context: ActionExecutionContext): Promise<ActionResult> { const { input, entityManager, actionManager, user } = context; try { // 开始事务 return await context.transaction.run(async () => { // 1. 验证购物车 const cart = await this.validateCart(input.cartId, entityManager); // 2. 预留库存 await this.reserveInventory(cart, entityManager); // 3. 创建订单 const order = await this.createOrder(cart, input, user, entityManager); // 4. 处理支付 const paymentResult = await this.processPayment(order, input, actionManager); if (!paymentResult.success) { // 释放库存 await this.releaseInventory(cart, entityManager); throw new Error('Payment failed'); } // 5. 更新订单状态 await entityManager.update('Order', order.id, { status: 'CONFIRMED', paymentStatus: 'PAID', paymentReference: paymentResult.data.transactionId }); // 6. 减少库存 await this.reduceInventory(cart, entityManager); // 7. 清空购物车 await actionManager.execute('cart-manager', { action: 'clear', cartId: input.cartId }); // 8. 发送确认邮件 await actionManager.execute('send-email', { to: user?.email, template: 'order-confirmation', data: { order } }); return { success: true, data: { order }, events: [{ type: 'order.created', data: { orderId: order.id, orderNumber: order.orderNumber, userId: order.userId, total: order.total } }] }; }); } catch (error) { return { success: false, error: { message: error.message, code: 'ORDER_PROCESSING_FAILED' } }; } } private async validateCart(cartId: string, entityManager: IEntityManager): Promise<any> { const cart = await entityManager.get('Cart', cartId); if (!cart) { throw new Error('Cart not found'); } const items = await entityManager.query({ entityType: 'CartItem', filter: { cartId }, include: ['product', 'variant'] }); if (items.data.length === 0) { throw new Error('Cart is empty'); } // 验证所有商品仍然可用 for (const item of items.data) { if (item.product.status !== 'ACTIVE') { throw new Error(`Product ${item.product.name} is no longer available`); } // 检查价格是否有变化 const currentPrice = item.variantId ? item.variant.price : item.product.price; if (currentPrice !== item.price) { throw new Error(`Price has changed for ${item.product.name}`); } // 检查库存 const inventory = await this.checkInventory( item.productId, item.variantId, item.quantity, entityManager ); if (!inventory.available) { throw new Error(`Insufficient inventory for ${item.product.name}`); } } return { ...cart, items: items.data }; } private async reserveInventory(cart: any, entityManager: IEntityManager): Promise<void> { for (const item of cart.items) { const filter: any = {}; if (item.variantId) { filter.variantId = item.variantId; } else { filter.productId = item.productId; } const inventories = await entityManager.query({ entityType: 'Inventory', filter }); if (inventories.data.length > 0) { const inventory = inventories.data[0]; await entityManager.update('Inventory', inventory.id, { reservedQuantity: inventory.reservedQuantity + item.quantity, availableQuantity: inventory.availableQuantity - item.quantity }); // 记录库存变动 await entityManager.create('InventoryMovement', { inventoryId: inventory.id, type: 'RESERVED', quantity: item.quantity, reason: 'Order reservation', reference: cart.id }); } } } private async createOrder(cart: any, input: any, user: any, entityManager: IEntityManager): Promise<any> { // 生成订单号 const orderNumber = await this.generateOrderNumber(); // 创建订单 const order = await entityManager.create('Order', { orderNumber, userId: user?.id || cart.userId, status: 'PENDING', paymentStatus: 'PENDING', fulfillmentStatus: 'UNFULFILLED', shippingAddress: input.shippingAddress, billingAddress: input.billingAddress, subtotal: cart.subtotal, taxAmount: cart.tax, shippingAmount: cart.shipping, total: cart.total, currency: cart.currency, paymentMethod: input.paymentMethod, shippingMethod: input.shippingMethod, customerNotes: input.customerNotes }); // 创建订单项目 for (const item of cart.items) { await entityManager.create('OrderItem', { orderId: order.id, productId: item.productId, variantId: item.variantId, sku: item.variantId ? item.variant.sku : item.product.sku, name: item.product.name, quantity: item.quantity, price: item.price, total: item.total }); } return order; } private async processPayment(order: any, input: any, actionManager: IActionManager): Promise<ActionResult> { return await actionManager.execute('process-payment', { orderId: order.id, amount: order.total, currency: order.currency, paymentMethod: input.paymentMethod, paymentToken: input.paymentToken, billingAddress: input.billingAddress }); } private async reduceInventory(cart: any, entityManager: IEntityManager): Promise<void> { for (const item of cart.items) { const filter: any = {}; if (item.variantId) { filter.variantId = item.variantId; } else { filter.productId = item.productId; } const inventories = await entityManager.query({ entityType: 'Inventory', filter }); if (inventories.data.length > 0) { const inventory = inventories.data[0]; await entityManager.update('Inventory', inventory.id, { quantity: inventory.quantity - item.quantity, reservedQuantity: inventory.reservedQuantity - item.quantity }); // 记录库存变动 await entityManager.create('InventoryMovement', { inventoryId: inventory.id, type: 'OUT', quantity: item.quantity, reason: 'Order fulfillment', reference: cart.id }); } } } private async releaseInventory(cart: any, entityManager: IEntityManager): Promise<void> { for (const item of cart.items) { const filter: any = {}; if (item.variantId) { filter.variantId = item.variantId; } else { filter.productId = item.productId; } const inventories = await entityManager.query({ entityType: 'Inventory', filter }); if (inventories.data.length > 0) { const inventory = inventories.data[0]; await entityManager.update('Inventory', inventory.id, { reservedQuantity: inventory.reservedQuantity - item.quantity, availableQuantity: inventory.availableQuantity + item.quantity }); // 记录库存变动 await entityManager.create('InventoryMovement', { inventoryId: inventory.id, type: 'RELEASED', quantity: item.quantity, reason: 'Order failed', reference: cart.id }); } } } private async generateOrderNumber(): Promise<string> { const timestamp = Date.now().toString(); const random = Math.random().toString(36).substr(2, 4).toUpperCase(); return `ORD-${timestamp.substr(-6)}-${random}`; } private async checkInventory( productId: string, variantId?: string, quantity: number = 1, entityManager?: IEntityManager ): Promise<{ available: boolean; availableQuantity: number }> { const filter: any = {}; if (variantId) { filter.variantId = variantId; } else { filter.productId = productId; } const inventories = await entityManager!.query({ entityType: 'Inventory', filter }); if (inventories.data.length === 0) { return { available: false, availableQuantity: 0 }; } const inventory = inventories.data[0]; const available = inventory.trackQuantity ? inventory.availableQuantity >= quantity : true; return { available, availableQuantity: inventory.availableQuantity }; } async validate(input: any): Promise<ValidationResult> { const errors: string[] = []; if (!input.cartId) errors.push('cartId is required'); if (!input.paymentMethod) errors.push('paymentMethod is required'); if (!input.shippingAddress) errors.push('shippingAddress is required'); if (!input.billingAddress) errors.push('billingAddress is required'); if (!input.shippingMethod) errors.push('shippingMethod is required'); return { valid: errors.length === 0, errors, warnings: [] }; } getRequiredPermissions(): string[] { return ['create_order']; } getMetadata(): ActionHandlerMetadata { return { async: false, timeout: 30000, retryable: false }; } }

支付集成

// 支付处理动作处理器 class PaymentProcessorActionHandler implements IActionHandler { name = 'process-payment'; displayName = '处理支付'; category = ActionCategory.INTEGRATION; private paymentProviders = new Map<string, IPaymentProvider>(); async initialize(context: ActionHandlerContext): Promise<void> { // 注册支付提供商 this.paymentProviders.set('stripe', new StripePaymentProvider()); this.paymentProviders.set('paypal', new PayPalPaymentProvider()); this.paymentProviders.set('square', new SquarePaymentProvider()); for (const provider of this.paymentProviders.values()) { await provider.initialize(); } } inputSchema: ActionInputSchema = { type: 'object', properties: { orderId: { type: 'string', required: true }, amount: { type: 'number', required: true }, currency: { type: 'string', required: true }, paymentMethod: { type: 'string', required: true }, paymentToken: { type: 'string' }, billingAddress: { type: 'object', required: true }, metadata: { type: 'object' } } }; async execute(context: ActionExecutionContext): Promise<ActionResult> { const { input, entityManager } = context; try { // 获取支付提供商 const provider = this.getPaymentProvider(input.paymentMethod); // 创建支付记录 const payment = await entityManager.create('Payment', { orderId: input.orderId, amount: input.amount, currency: input.currency, method: input.paymentMethod, status: 'PENDING', metadata: input.metadata || {} }); // 处理支付 const paymentResult = await provider.processPayment({ amount: input.amount, currency: input.currency, paymentToken: input.paymentToken, billingAddress: input.billingAddress, metadata: { orderId: input.orderId, paymentId: payment.id } }); // 更新支付记录 await entityManager.update('Payment', payment.id, { status: paymentResult.success ? 'COMPLETED' : 'FAILED', transactionId: paymentResult.transactionId, gatewayResponse: paymentResult.rawResponse, errorMessage: paymentResult.error?.message, processedAt: new Date() }); if (paymentResult.success) { return { success: true, data: { paymentId: payment.id, transactionId: paymentResult.transactionId, amount: input.amount, currency: input.currency }, events: [{ type: 'payment.completed', data: { orderId: input.orderId, paymentId: payment.id, amount: input.amount, currency: input.currency } }] }; } else { return { success: false, error: { message: paymentResult.error?.message || 'Payment failed', code: 'PAYMENT_FAILED', details: paymentResult.error }, events: [{ type: 'payment.failed', data: { orderId: input.orderId, paymentId: payment.id, error: paymentResult.error } }] }; } } catch (error) { return { success: false, error: { message: error.message, code: 'PAYMENT_ERROR' } }; } } private getPaymentProvider(paymentMethod: string): IPaymentProvider { // 根据支付方式确定提供商 let providerName: string; if (paymentMethod.startsWith('card_')) { providerName = 'stripe'; } else if (paymentMethod === 'paypal') { providerName = 'paypal'; } else if (paymentMethod.startsWith('square_')) { providerName = 'square'; } else { throw new Error(`Unsupported payment method: ${paymentMethod}`); } const provider = this.paymentProviders.get(providerName); if (!provider) { throw new Error(`Payment provider '${providerName}' not available`); } return provider; } async validate(input: any): Promise<ValidationResult> { const errors: string[] = []; if (!input.orderId) errors.push('orderId is required'); if (!input.amount || input.amount <= 0) errors.push('Valid amount is required'); if (!input.currency) errors.push('currency is required'); if (!input.paymentMethod) errors.push('paymentMethod is required'); if (!input.billingAddress) errors.push('billingAddress is required'); return { valid: errors.length === 0, errors, warnings: [] }; } getRequiredPermissions(): string[] { return ['process_payment']; } getMetadata(): ActionHandlerMetadata { return { async: false, timeout: 30000, retryable: false }; } } // Stripe支付提供商实现 class StripePaymentProvider implements IPaymentProvider { private stripe: any; async initialize(): Promise<void> { // 初始化Stripe客户端 this.stripe = require('stripe')(process.env.STRIPE_SECRET_KEY); } async processPayment(request: PaymentRequest): Promise<PaymentResult> { try { const paymentIntent = await this.stripe.paymentIntents.create({ amount: Math.round(request.amount * 100), // 转换为分 currency: request.currency.toLowerCase(), payment_method: request.paymentToken, confirmation_method: 'manual', confirm: true, metadata: request.metadata }); if (paymentIntent.status === 'succeeded') { return { success: true, transactionId: paymentIntent.id, amount: request.amount, currency: request.currency, rawResponse: paymentIntent }; } else { return { success: false, error: { message: 'Payment requires additional authentication', code: 'AUTHENTICATION_REQUIRED' }, rawResponse: paymentIntent }; } } catch (error) { return { success: false, error: { message: error.message, code: error.code || 'PAYMENT_ERROR' }, rawResponse: error }; } } async refundPayment(transactionId: string, amount?: number): Promise<PaymentResult> { try { const refund = await this.stripe.refunds.create({ payment_intent: transactionId, amount: amount ? Math.round(amount * 100) : undefined }); return { success: true, transactionId: refund.id, amount: refund.amount / 100, currency: refund.currency.toUpperCase(), rawResponse: refund }; } catch (error) { return { success: false, error: { message: error.message, code: error.code || 'REFUND_ERROR' }, rawResponse: error }; } } getName(): string { return 'stripe'; } getSupportedMethods(): string[] { return ['card_visa', 'card_mastercard', 'card_amex']; } }

视图定义

产品管理视图

// 产品列表视图 export const ProductListView: IViewDefinition = { id: 'product-list', name: 'ProductList', displayName: '产品列表', type: 'table', entityType: 'Product', config: { columns: [ { name: 'image', displayName: '图片', type: 'image', width: 80, renderer: 'product-image', options: { size: 'small', shape: 'square' } }, { name: 'name', displayName: '产品名称', type: 'string', sortable: true, searchable: true, width: 200 }, { name: 'sku', displayName: 'SKU', type: 'string', sortable: true, searchable: true, width: 120 }, { name: 'category.name', displayName: '分类', type: 'string', sortable: true, filterable: true, width: 120 }, { name: 'price', displayName: '价格', type: 'currency', sortable: true, filterable: true, width: 100, format: { currency: 'USD', precision: 2 } }, { name: 'inventory.availableQuantity', displayName: '库存', type: 'number', sortable: true, filterable: true, width: 80, renderer: 'inventory-status' }, { name: 'status', displayName: '状态', type: 'enum', sortable: true, filterable: true, width: 100, renderer: 'status-badge' }, { name: 'createdAt', displayName: '创建时间', type: 'datetime', sortable: true, width: 120, format: { dateStyle: 'short', timeStyle: 'short' } } ], defaultSort: [{ field: 'createdAt', direction: 'desc' }], defaultFilter: { status: 'ACTIVE' }, pageSize: 25, enableSearch: true, enableFilters: true, enableExport: true, actions: [ { name: 'create', displayName: '新增产品', type: 'primary', icon: 'plus', handler: 'navigate-to-form' }, { name: 'bulk-update', displayName: '批量更新', type: 'secondary', icon: 'edit', handler: 'bulk-update-modal', requiresSelection: true }, { name: 'export', displayName: '导出', type: 'secondary', icon: 'download', handler: 'export-data' } ], rowActions: [ { name: 'edit', displayName: '编辑', icon: 'edit', handler: 'navigate-to-form' }, { name: 'duplicate', displayName: '复制', icon: 'copy', handler: 'duplicate-product' }, { name: 'delete', displayName: '删除', icon: 'trash', handler: 'delete-confirm', confirmMessage: '确定要删除这个产品吗?' } ] } }; // 产品详情视图 export const ProductDetailView: IViewDefinition = { id: 'product-detail', name: 'ProductDetail', displayName: '产品详情', type: 'detail', entityType: 'Product', config: { layout: { type: 'sections', sections: [ { title: '基本信息', collapsible: true, columns: 2, fields: [ { name: 'name', displayName: '产品名称', required: true }, { name: 'sku', displayName: 'SKU', required: true }, { name: 'categoryId', displayName: '分类', type: 'select', required: true }, { name: 'brand', displayName: '品牌' }, { name: 'status', displayName: '状态', type: 'select', required: true }, { name: 'tags', displayName: '标签', type: 'tags' } ] }, { title: '价格和成本', collapsible: true, columns: 3, fields: [ { name: 'price', displayName: '售价', type: 'currency', required: true }, { name: 'comparePrice', displayName: '比较价格', type: 'currency' }, { name: 'cost', displayName: '成本', type: 'currency' } ] }, { title: '产品描述', collapsible: true, columns: 1, fields: [ { name: 'shortDescription', displayName: '简短描述', type: 'textarea', rows: 3 }, { name: 'description', displayName: '详细描述', type: 'rich-text', rows: 8 } ] }, { title: '产品图片', collapsible: true, columns: 1, fields: [ { name: 'images', displayName: '产品图片', type: 'image-gallery', options: { maxImages: 10, allowReorder: true, sizes: ['thumbnail', 'medium', 'large'] } } ] }, { title: '物理属性', collapsible: true, columns: 2, fields: [ { name: 'weight', displayName: '重量 (kg)', type: 'number', step: 0.01 }, { name: 'dimensions.width', displayName: '宽度 (cm)', type: 'number' }, { name: 'dimensions.height', displayName: '高度 (cm)', type: 'number' }, { name: 'dimensions.depth', displayName: '深度 (cm)', type: 'number' } ] }, { title: 'SEO设置', collapsible: true, columns: 1, fields: [ { name: 'seoTitle', displayName: 'SEO标题', maxLength: 60 }, { name: 'seoDescription', displayName: 'SEO描述', type: 'textarea', rows: 3, maxLength: 160 }, { name: 'seoKeywords', displayName: 'SEO关键词', placeholder: '用逗号分隔' } ] } ] }, relatedViews: [ { name: 'variants', displayName: '产品变体', viewId: 'product-variant-list', filter: { productId: '{{id}}' }, position: 'tab' }, { name: 'inventory', displayName: '库存管理', viewId: 'inventory-detail', filter: { productId: '{{id}}' }, position: 'tab' }, { name: 'reviews', displayName: '用户评价', viewId: 'review-list', filter: { productId: '{{id}}' }, position: 'tab' } ], actions: [ { name: 'save', displayName: '保存', type: 'primary', handler: 'save-entity' }, { name: 'save-and-continue', displayName: '保存并继续编辑', type: 'secondary', handler: 'save-and-stay' }, { name: 'duplicate', displayName: '复制产品', type: 'secondary', handler: 'duplicate-product' }, { name: 'preview', displayName: '预览', type: 'secondary', handler: 'preview-product' } ] } }; // 订单列表视图 export const OrderListView: IViewDefinition = { id: 'order-list', name: 'OrderList', displayName: '订单列表', type: 'table', entityType: 'Order', config: { columns: [ { name: 'orderNumber', displayName: '订单号', type: 'string', sortable: true, searchable: true, width: 140, renderer: 'order-link' }, { name: 'user.name', displayName: '客户', type: 'string', sortable: true, searchable: true, width: 120 }, { name: 'status', displayName: '订单状态', type: 'enum', sortable: true, filterable: true, width: 100, renderer: 'order-status-badge' }, { name: 'paymentStatus', displayName: '支付状态', type: 'enum', sortable: true, filterable: true, width: 100, renderer: 'payment-status-badge' }, { name: 'fulfillmentStatus', displayName: '发货状态', type: 'enum', sortable: true, filterable: true, width: 100, renderer: 'fulfillment-status-badge' }, { name: 'total', displayName: '总金额', type: 'currency', sortable: true, filterable: true, width: 100, format: { currency: 'USD', precision: 2 } }, { name: 'createdAt', displayName: '下单时间', type: 'datetime', sortable: true, width: 140, format: { dateStyle: 'short', timeStyle: 'short' } } ], defaultSort: [{ field: 'createdAt', direction: 'desc' }], pageSize: 25, enableSearch: true, enableFilters: true, enableExport: true, filters: [ { name: 'status', displayName: '订单状态', type: 'select', options: [ { label: '待确认', value: 'PENDING' }, { label: '已确认', value: 'CONFIRMED' }, { label: '处理中', value: 'PROCESSING' }, { label: '已发货', value: 'SHIPPED' }, { label: '已送达', value: 'DELIVERED' }, { label: '已取消', value: 'CANCELLED' }, { label: '已退款', value: 'REFUNDED' } ] }, { name: 'paymentStatus', displayName: '支付状态', type: 'select', options: [ { label: '待支付', value: 'PENDING' }, { label: '已支付', value: 'PAID' }, { label: '支付失败', value: 'FAILED' }, { label: '已退款', value: 'REFUNDED' }, { label: '部分退款', value: 'PARTIALLY_REFUNDED' } ] }, { name: 'dateRange', displayName: '下单日期', type: 'dateRange', field: 'createdAt' } ], actions: [ { name: 'export', displayName: '导出订单', type: 'secondary', icon: 'download', handler: 'export-orders' }, { name: 'bulk-update-status', displayName: '批量更新状态', type: 'secondary', icon: 'edit', handler: 'bulk-update-status', requiresSelection: true } ], rowActions: [ { name: 'view', displayName: '查看详情', icon: 'eye', handler: 'navigate-to-detail' }, { name: 'update-status', displayName: '更新状态', icon: 'edit', handler: 'update-status-modal' }, { name: 'print', displayName: '打印', icon: 'print', handler: 'print-order' }, { name: 'refund', displayName: '退款', icon: 'refund', handler: 'refund-modal', condition: { paymentStatus: 'PAID' } } ] } };

下一步

完成电商应用的基础架构后,可以继续学习:

Last updated on