电子商务框架可与任何电子商务解决方案一起使用。 此处讨论的特定信息和示例将引用hybris解决方案。
该集成框架包含一个带有API的集成层。 这允许您:
还提 供API文档。
提供了许多现成的AEM组件以使用集成层。 目前有:
为了搜索,提供了一个集成挂钩,它允许您使用AEM搜索、电子商务系统的搜索、第三方搜索(如Search&Promote)或其组合。
电子商务框架可与任何电子商务解决方案一起使用,所使用的引擎需要由AEM进行识别:
电子商务引擎是支持CommerceService
接口的OSGi服务
commerceProvider
服务属性进行区分AEM支持CommerceService
和Product
的Resource.adaptTo()
adaptTo
实现在资源的层次结构中查找cq:commerceProvider
属性:
使用cq:Commerce
混音,以便将cq:commerceProvider
添加到强类型资源。
cq:commerceProvider
属性还用于引用相应的商务工厂定义。
例如,cq:commerceProvider
属性with the value hybris
will correate to the OSGi configuration for Day CQ Commerce Factory for Hybris(com.adobe.cq.com.hymerce.impl.HybrisServiceFactory)-其中参数commerceProvider
也有值hybris
。
在此可以配置其他属性,如目录版本(如果适用并可用)。
请参阅以下示例:
cq:commerceProvider = geometrixx |
在标准AEM安装中,需要具体实施;例如,geometrixx示例,其中包括通用API的最小扩展 |
---|---|
cq:commerceProvider = hybris |
hybris implementation |
/content/store
+ cq:commerceProvider = hybris
+ mens
+ polo-shirt-1
+ polo-shirt-2
+ employee
+ cq:commerceProvider = jcr
+ adobe-logo-shirt
+ cq:commerceType = product
+ price = 12.50
+ adobe-logo-shirt_S
+ cq:commerceType = variant
+ size = S
+ adobe-logo-shirt_XL
+ cq:commerceType = variant
+ size = XL
+ price = 14.50
UsingCRXDE Lite, you can see how this is handled in the product component for the hybris implementation:
/apps/geometrixx-outdoors/components/hybris/product/product.jsp
The hybris extension of the eCommerce Integration Framework has beed updated to support Hybris 5, while maintaining backward compatibility with Hybris 4.
The default settings in the code are tuned for Hybris 5.
To develop for Hybris 4 the following is required:
调用maven时,将以下命令行参数添加到命令
-P hybris4
It downloads the pre-configured Hybris 4 distribution and embeds it in the bundle:
cq-commerce-hybris-server
在OSGi配置管理器中:
hybris uses a user session to store information such as the customer's shopping cart. The session id is returned from hybris in a JSESSIONID
cookie that needs to be sent on theussend requests to hybris. 为避免将会话ID存储在存储库中,会话ID将编码到存储在购物者浏览器中的其他cookie中。 将执行以下步骤:
hybris-session-rest
),并在对购物者的响应时进行设置。 新cookie中的编码是必需的,因为原始cookie仅对特定路径有效,否则在后续请求中不会从浏览器发回。 路径信息还必须添加到cookie的值中。hybris-session-<*xxx*>
cookies中解码,并在用于从hybris请求数据的HTTP客户端上设置。当原始会话不再有效时,将创建新的匿名会话。
此会话“拥有”购物车
执行add/remove/etc
在购物车上执行各种计算;
commerceSession.getProductPrice(Product product)
拥有order数据的存储位置
CommerceSession.getUserContext()
还拥有payment处理连接
还拥有履行连接
Product data that is maintained in hybris needs to be available in AEM. 已实施以下机制:
导入程序(b)用于AEM目录中页面树结构的初始设置。
hybris中的目录更改通过源指示到AEM, thes propagate to AEM(b)
The hybris extension provides a polling importer("hybris" scheme"), which can be configured to import changes into AEM at a specified interval(example, ever 24 hours where the interval is specified in seconds):
http://localhost:4502/content/geometrixx-outdoors/en_US/jcr:content.json
{
* "jcr:mixinTypes": ["cq:PollConfig"],
* "enabled": true,
* "source": "hybris:outdoors",
* "jcr:primaryType": "cq:PageContent",
* "interval": 86400
}
AEM中的目录配置识别Staged和Online目录版本。
在目录版本之间同步产品需要相应AEM页面(a, c)的(de-)激活
在AEM©中激活页面需要选中(b)项,并且仅当
已激活的产品页面需要访问产品数据的在线版本(d)。
AEM publish instance requires access to hybris for the retrieval of product and personalized data(d)。
一个产品可以有多个变体;例如,它可能因颜色和/或大小而异。 产品必须定义驱动变化的属性;我们将这些变型轴定为术语。
但是,并非所有属性都是变型轴。 变化也会影响其他属性;例如,价格可能取决于大小。 Thesper properties cannot be selected by the shopper, and eso not be consured to variant axes.
每个产品和/或变体由资源表示,因此将1:1映射到存储库节点。 由此推论,特定产品和/或变体可以通过其路径唯一标识。
The product/variant resource does not always hold the actual product dataIt might be representation of data activally held on another system(such as hybris)。 例如,产品说明、定价等不存储在AEM中,而是从电子商务引擎实时检索。
任何产品资源都可以用Product API
表示。 产品API中的大多数调用都是特定于变体的(尽管变体可能继承祖先的共享值),但也有与变体集(getVariantAxes()
、getVariants()
等)列表的调用。
实际上,变型轴由任何Product.getVariantAxes()
返回的值决定:
虽然产品(通常)可以有许多变型轴,但现成的产品组件只处理两个:
size
再加一个
通过产品引用的variationAxis
属性选择此附加变体(通常color
用于Geometrixx Outdoors)。
一般而言:
产品数据位于/etc
下
和/content
下的产品引用。
产品变量和产品数据节点之间必须有1:1的映射。
产品引用还必须为呈现的每个变体提供一个节点,但不要求显示所有变体。 例如,如果产品具有S、M、L变量,则产品数据可能为:
etc
commerce
products
shirt
shirt-s
shirt-m
shirt-l
而“大而高”目录可能只包含:
content
big-and-tall
shirt
shirt-l
最后,不需要使用产品数据。 您可以将所有产品数据放在目录引用下;但是,如果不复制所有产品数据,您就无法真正拥有多个目录。
API
public interface Product extends Adaptable {
public String getPath(); // path to specific variation
public String getPagePath(); // path to presentation page for all variations
public String getSKU(); // unique ID of specific variation
public String getTitle(); // shortcut to getProperty(TITLE)
public String getDescription(); // shortcut to getProperty(DESCRIPTION)
public String getImageUrl(); // shortcut to getProperty(IMAGE_URL)
public String getThumbnailUrl(); // shortcut to getProperty(THUMBNAIL_URL)
public <T> T getProperty(String name, Class<T> type);
public Iterator<String> getVariantAxes();
public boolean axisIsVariant(String axis);
public Iterator<Product> getVariants(VariantFilter filter) throws CommerceException;
}
/**
* Interface for filtering variants and AxisFilter provided as common implementation
*
* The <code>VariantFilter</code> is used to filter variants,
* e.g. when using {@link Product#getVariants(VariantFilter filter)}.
*/
public interface VariantFilter {
public boolean includes(Product product);
}
/**
* A {@link VariantFilter} for filtering variants by the given
* axis and value. The following example returns a list of
* variant products that have a value of <i>blue</i> on the
* <i>color</i> axis.
*
* <p>
* <code>product.getVariants(new AxisFilter("color", "blue"));</code>
*/
public class AxisFilter implements VariantFilter {
private String axis;
private String value;
public AxisFilter(String axis, String value) {
this.axis = axis;
this.value = value;
}
/**
* {@inheritDoc}
*/
public boolean includes(Product product) {
ValueMap values = product.adaptTo(ValueMap.class);
if(values != null) {
String v = values.get(axis, String.class);
return v != null && v == value;
}
return false;
}
}
一般存储机制
产品节点不是非结构化的。
产品节点可以是:
参考,将产品数据存储在其他位置:
productData
属性,该属性指向产品数据(通常位于/etc/commerce/products
下)。产品本身:
productData
属性。AEM-generic产品结构
+ banyan_shirt
- cq:commerceType = product
- cq:productAttributes = [jcr:title, jcr:description, size, price, color]
- cq:productVariantAxes = [color, size]
- jcr:title = Banyan Shirt
- jcr:description = Flowery, all-cotton shirt.
- price = 14.00
+ banyan_shirt_s
- cq:commerceType = variant
- size = S
+ banyan_shirt_s_red
- cq:commerceType = variant
- color = red
+ banyan_shirt_s_blue
- cq:commerceType = variant
- color = blue
+ banyan_shirt_m
- cq:commerceType = variant
- size = M
+ banyan_shirt_m_red
- cq:commerceType = variant
- color = red
+ banyan_shirt_m_blue
- cq:commerceType = variant
- color = blue
+ banyan_shirt_l
- cq:commerceType = variant
- size = L
+ banyan_shirt_l_red
- cq:commerceType = variant
- color = red
+ banyan_shirt_l_blue
- cq:commerceType = variant
- color = blue
+ banyan_shirt_xl
- cq:commerceType = variant
- size = XL
- price = 18.00
组件
购物车归CommerceSession:
所有
CommerceSession
执行add/remove/etc。CommerceSession
还在购物车上执行各种计算。"虽然不直接与购物车相关,CommerceSession
还必须提供目录定价信息(因为它拥有定价)
定价可能包含多个修改量:
修饰符完全开放,具有以下界面:
int CommerceSession.getQuantityBreakpoints(Product product)
String CommerceSession.getProductPrice(Product product)
存储
存储
个性化
个性化应始终通过ClientContext来驱动。
AClientContext/version/
of the cart is created in all cases:
CommerceSession.addCartEntry()
方法添加产品。以下说明了ClientContext车中购物车信息的示例:
购物车和订购数据
CommerceSession
拥有三个元素:
购物车内容
定价
订单详细信息
购物车内容
购物车内容模式由API修复:
public void addCartEntry(Product product, int quantity);
public void modifyCartEntry(int entryNumber, int quantity);
public void deleteCartEntry(int entryNumber);
定价
定价模式也由API修复:
public String getCartPreTaxPrice();
public String getCartTax();
public String getCartTotalPrice();
public String getOrderShipping();
public String getOrderTotalTax();
public String getOrderTotalPrice();
订单详细信息
但是,订单详细信息不由API修复:
public void updateOrderDetails(Map<String, String> orderDetails);
public Map<String, String> getOrderDetails();
public void submitOrder();
发运计算
订购表单通常需要提供多种送货方式(和价格)。
价格可能基于订单的项目和详细信息,如权重和/或投放地址。
CommerceSession
可以访问所有依赖项,因此可以采用与产品定价类似的方式处理它:
CommerceSession
拥有送货价格。updateOrder(Map<String, Object> delta)
检索/更新投放详细信息您可以实施运输选择器;例如:
yourProject/commerce/components/shippingpicker
:
本质上,它可以是foundation/components/form/radio
的副本,但是对CommerceSession
的回调用于:
检查方法是否可用
添加定价信息
要使购物者能够更新AEM中的订单页(包括发运方法的超集和描述它们的文本),同时仍具有显示相关CommerceSession
信息的控件。
付款处理
CommerceSession
还拥有付款处理连接。CommerceSession
实施添加特定呼叫(到他们选择的付款处理服务)。订单履行
CommerceSession
还拥有实施连接。CommerceSession
实施添加特定呼叫(到他们选择的付款处理服务)。遵循标准服务API模型,电子商务项目提供一组与搜索相关的API,这些API可由单个商务引擎实现。
Currently, only the hybris engine implements the search API out-of-the-box.
但是,搜索API是通用的,可以由每个CommerceService单独实现。
电子商务项目包含默认的搜索组件,位于:
/libs/commerce/components/search
这利用搜索API来查询选定的商务引擎(请参阅电子商务引擎选择):
核心项目提供了几个通用类/帮助程序类:
CommerceQuery
用于描述搜索查询(包含有关查询文本、当前页面、页面大小、排序和选定彩块化的信息)。 所有实现搜索API的电子商务服务都将接收此类的实例以执行其搜索。 可以从请求对象(HttpServletRequest
)实例化CommerceQuery
。
FacetParamHelper
是一个实用程序类,它提供一种静态方法- toParams
—— 用于从facet和一个切换值的列表生成GET
参数字符串。 这在UI端很有用,您需要为每个facet的每个值显示一个超链接,这样当用户单击超链接时,相应的值将被切换(即,如果选择了该值,则会从查询中删除,否则会添加)。 这将考虑处理多值/单值彩块化、覆盖值等的所有逻辑。
搜索API的入口点是返回CommerceResult
对象的CommerceService#search
方法。 有关本主题的详细信息,请参阅API文档。
AEM与各种电子商务系统之间提供集成。 这需要一种在不同系统之间同步购物者的策略,这样AEM特定的代码只需了解AEM,反之亦然:
身份验证
AEM假定为仅 web前端,因此执行所有身份验证。
Accounts in Hybris
AEM creates a ecoped(下属)account in hybris for each shopper 此帐户的用户名与AEM用户名相同。 密码随机密码是自动生成并存储(加密)在AEM中的。
AEM前端可定位在现有hybris实现的前面。 Asso a hybris engine can be added to an existing AEM installation. 为此,系统必须能够正常地处理任一系统中的现有用户:
AEM -> hybris
登录到hybris时,如果AEM user不已存在:
请参阅:com.adobe.cq.commerce.hybris.impl.HybrisSessionImpl#login()
hybris -> AEM
登录到AEM时,如果系统识别用户:
上述算法在Sling AuthenticationInfoPostProcessor
中实现
com.adobe.cq.commerce.hybris.impl.user.LazyUserImporter.java
要在现有功能的基础上构建自定义导入处理程序:
必须实现ImportHandler
接口
可以扩展DefaultImportHandler
/**
* Services implementing the <code>ImportHandler</code> interface are
* called by the {@link HybrisImporter} to create actual commerce entities
* such as products.
*/
public interface ImportHandler {
/**
* Not used.
*/
public void createTaxonomie(ImporterContext ctx);
/**
* Creates a catalog with the given name.
* @param ctx The importer context
* @param name The catalog's name
* @return Path of created catalog
*/
public String createCatalog(ImporterContext ctx, String name) throws Exception;
/**
* Creates a product from the given values.
* @param ctx The importer context
* @param values The product's properties
* @param parentCategoryPath The containing category's path
* @return Path of created product
*/
public String createProduct(ImporterContext ctx, ValueMap values, String parentCategoryPath) throws Exception;
/**
* Creates a variant product from the given values.
* @param ctx The importer context
* @param values The product's properties
* @param baseProductPath The base product's path
* @return Path of created product
*/
public String createVariantProduct(ImporterContext ctx, ValueMap values, String baseProductPath) throws Exception;
/**
* Creates an asset for a product. This is usually a product
* image.
* @param ctx The importer context
* @param values The product's properties
* @param baseProductPath The product's path
* @return Path of created asset
*/
public String createAsset(ImporterContext ctx, ValueMap values, String productPath) throws Exception;
/**
* Creates a category from the given values.
* @param ctx The importer context
* @param values The category's properties
* @param parentPath Path of parent category or base path of import in case of root category
* @return Path of created category
*/
public String createCategory(ImporterContext ctx, ValueMap values, String parentCategoryPath) throws Exception;
}
要让导入程序识别您的自定义处理程序,它必须指定值大于0的service.ranking
属性;例如:
@Component
@Service
@Property(name = "service.ranking", value = 100)
public class MyImportHandler extends DefaultImportHandler {
...
}