欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ofbiz学习——推广/宣传产品展示

程序员文章站 2022-03-21 23:16:28
...

这篇文章主要分析主页中间的推广/宣传产品展示的相关代码。

ofbiz学习——推广/宣传产品展示

1.先把涉及到的源码贴出来

1.1 widget配置文件

打开文件component://ecommerce/widget/CommonScreens.xml#main
    <screen name="main">
        <section>
            <actions>
                <set field="leftbarScreenName" value="leftbar"/>
                <set field="rightbarScreenName" value="rightbar"/>
                <set field="MainColumnStyle" value="center"/>

                <set field="titleProperty" value="PageTitleMain"/>
                <set field="headerItem" value="main"/>
                <set field="randomSurveyGroup" value="testSurveyGroup"/>

                <script location="component://ecommerce/groovyScripts/Main.groovy"/>
                <script location="component://order/groovyScripts/entry/catalog/Category.groovy"/>
            </actions>
            <widgets>
                <decorator-screen name="main-decorator" location="${parameters.mainDecoratorLocation}">
                    <decorator-section name="body">
                        <platform-specific>
                            <html><html-template location="component://ecommerce/template/Main.ftl"/></html>
                        </platform-specific>
                    </decorator-section>
                </decorator-screen>
            </widgets>
        </section>
    </screen>

打开文件component://ecommerce/widget/CatalogScreens.xml#category-include
    <screen name="category-include">
        <section>
            <widgets>
                <section>
                    <condition>
                        <not><if-empty field="productCategory"/></not>
                    </condition>
                    <widgets>
                        <include-screen name="${detailScreen}"/>
                    </widgets>
                    <fail-widgets>
                        <label style="head2">${uiLabelMap.ProductCategoryNotFoundForCategoryID} ${productCategoryId}!</label>
                    </fail-widgets>
                </section>
            </widgets>
        </section>
    </screen>
打开文件component://ecommerce/widget/CatalogScreens.xml#categorydetail
    <screen name="categorydetail">
        <section>
            <actions>
                <set field="productsummaryScreen" value="component://ecommerce/widget/CatalogScreens.xml#productsummary"/>
                <set field="productCategoryLinkScreen" value="component://ecommerce/widget/CatalogScreens.xml#ProductCategoryLink"/>

                <script location="component://order/groovyScripts/entry/catalog/CategoryDetail.groovy"/>

                <entity-and entity-name="ProductCategoryLink" list="productCategoryLinks" use-cache="true" filter-by-date="true">
                    <field-map field-name="productCategoryId" from-field="productCategoryId"/>
                    <order-by field-name="sequenceNum"/>
                </entity-and>
                <set field="paginateEcommerceStyle" value="Y"/>
            </actions>
            <widgets>
                <platform-specific><html><html-template location="component://order/template/entry/catalog/CategoryDetail.ftl"/></html></platform-specific>
            </widgets>
        </section>
    </screen>

打开文件component://ecommerce/widget/CatalogScreens.xml#productsummary
    <screen name="productsummary">
        <section>
            <actions>
                <property-map resource="ProductUiLabels" map-name="uiLabelMap" global="true"/>
                <property-map resource="CommonUiLabels" map-name="uiLabelMap" global="true"/>
                <property-map resource="EcommerceUiLabels" map-name="uiLabelMap" global="true"/>
                <script location="component://order/groovyScripts/entry/catalog/ProductSummary.groovy"/>
            </actions>
            <widgets>
                <platform-specific><html><html-template location="component://order/template/entry/catalog/ProductSummary.ftl"/></html></platform-specific>
            </widgets>
        </section>
    </screen>

1.2 相关的groovy脚本

component://order/groovyScripts/entry/catalog/Category.groovy
/*
 * This script is also referenced by the ecommerce's screens and
 * should not contain order component's specific code.
 */

import org.apache.ofbiz.base.util.*
import org.apache.ofbiz.entity.*
import org.apache.ofbiz.product.catalog.*
import org.apache.ofbiz.product.category.CategoryWorker
import org.apache.ofbiz.product.category.CategoryContentWrapper
import org.apache.ofbiz.product.store.ProductStoreWorker

detailScreen = "categorydetail"
catalogName = CatalogWorker.getCatalogName(request)

productCategoryId = request.getAttribute("productCategoryId") ?: parameters.category_id
context.productCategoryId = productCategoryId

context.productStore = ProductStoreWorker.getProductStore(request)

pageTitle = null
metaDescription = null
metaKeywords = null

category = from("ProductCategory").where("productCategoryId", productCategoryId).cache(true).queryOne()
if (category) {
    if (category.detailScreen) {
        detailScreen = category.detailScreen
    }
    categoryPageTitle = from("ProductCategoryContentAndInfo").where("productCategoryId", productCategoryId, "prodCatContentTypeId", "PAGE_TITLE").cache(true).queryList()
    if (categoryPageTitle) {
        pageTitle = from("ElectronicText").where("dataResourceId", categoryPageTitle.get(0).dataResourceId).cache(true).queryOne()
    }
    categoryMetaDescription = from("ProductCategoryContentAndInfo").where("productCategoryId", productCategoryId, "prodCatContentTypeId", "META_DESCRIPTION").cache(true).queryList()
    if (categoryMetaDescription) {
        metaDescription = from("ElectronicText").where("dataResourceId", categoryMetaDescription.get(0).dataResourceId).cache(true).queryOne()
    }
    categoryMetaKeywords = from("ProductCategoryContentAndInfo").where("productCategoryId", productCategoryId, "prodCatContentTypeId", "META_KEYWORD").cache(true).queryList()
    if (categoryMetaKeywords) {
        metaKeywords = from("ElectronicText").where("dataResourceId", categoryMetaKeywords.get(0).dataResourceId).cache(true).queryOne()
    }
    categoryContentWrapper = new CategoryContentWrapper(category, request)
    
    categoryDescription = categoryContentWrapper.get("DESCRIPTION", "html")

    if (pageTitle) {
        context.title = pageTitle.textData
    } else {
        context.title = categoryContentWrapper.get("CATEGORY_NAME", "html")
    }

    if (metaDescription) {
        context.metaDescription = metaDescription.textData
    } else {
        if (categoryDescription) {
            context.metaDescription = categoryDescription
        }
    }

    if (metaKeywords) {
        context.metaKeywords = metaKeywords.textData
    } else {
        if (categoryDescription) {
            context.metaKeywords = categoryDescription + ", " + catalogName
        } else {
            context.metaKeywords = catalogName
        }
    }
    context.productCategory = category
}

// check the catalogs template path and update
templatePathPrefix = CatalogWorker.getTemplatePathPrefix(request)
if (templatePathPrefix) {
    detailScreen = templatePathPrefix + detailScreen
}
context.detailScreen = detailScreen

request.setAttribute("productCategoryId", productCategoryId)
request.setAttribute("defaultViewSize", 10)
request.setAttribute("limitView", true)

component://order/groovyScripts/entry/catalog/CategoryDetail.groovy
/*
 * NOTE: This script is also referenced by the webpos and ecommerce's screens and
 * should not contain order component's specific code.
 */

import org.apache.ofbiz.base.util.*
import org.apache.ofbiz.entity.*
import org.apache.ofbiz.entity.condition.*
import org.apache.ofbiz.entity.util.*
import org.apache.ofbiz.service.*
import org.apache.ofbiz.product.catalog.*
import org.apache.ofbiz.product.category.CategoryContentWrapper
import org.apache.ofbiz.product.store.ProductStoreWorker

productCategoryId = request.getAttribute("productCategoryId")
context.productCategoryId = productCategoryId

viewSize = parameters.VIEW_SIZE
viewIndex = parameters.VIEW_INDEX
currentCatalogId = CatalogWorker.getCurrentCatalogId(request)

// set the default view size
defaultViewSize = request.getAttribute("defaultViewSize") ?: EntityUtilProperties.getPropertyValue("widget", "widget.form.defaultViewSize", "20", delegator)
context.defaultViewSize = defaultViewSize

// set the limit view
limitView = request.getAttribute("limitView") ?: true
context.limitView = limitView

// get the product category & members
andMap = [productCategoryId : productCategoryId,
        viewIndexString : viewIndex,
        viewSizeString : viewSize,
        defaultViewSize : defaultViewSize,
        limitView : limitView]
andMap.put("prodCatalogId", currentCatalogId)
andMap.put("checkViewAllow", true)
// Prevents out of stock product to be displayed on site
productStore = ProductStoreWorker.getProductStore(request)
if (productStore) {
    andMap.put("productStoreId", productStore.productStoreId)
}
if (context.orderByFields) {
    andMap.put("orderByFields", context.orderByFields)
} else {
    andMap.put("orderByFields", ["sequenceNum", "productId"])
}
catResult = runService('getProductCategoryAndLimitedMembers', andMap)
productCategory = catResult.productCategory
productCategoryMembers = catResult.productCategoryMembers
context.productCategoryMembers = productCategoryMembers
context.productCategory = productCategory
context.viewIndex = catResult.viewIndex
context.viewSize = catResult.viewSize
context.lowIndex = catResult.lowIndex
context.highIndex = catResult.highIndex
context.listSize = catResult.listSize

// set this as a last viewed
// DEJ20070220: WHY is this done this way? why not use the existing CategoryWorker stuff?
LAST_VIEWED_TO_KEEP = 10 // modify this to change the number of last viewed to keep
lastViewedCategories = session.getAttribute("lastViewedCategories")
if (!lastViewedCategories) {
    lastViewedCategories = []
    session.setAttribute("lastViewedCategories", lastViewedCategories)
}
lastViewedCategories.remove(productCategoryId)
lastViewedCategories.add(0, productCategoryId)
while (lastViewedCategories.size() > LAST_VIEWED_TO_KEEP) {
    lastViewedCategories.remove(lastViewedCategories.size() - 1)
}

// set the content path prefix
contentPathPrefix = CatalogWorker.getContentPathPrefix(request)
context.put("contentPathPrefix", contentPathPrefix)

// little routine to see if any members have a quantity > 0 assigned
members = context.get("productCategoryMembers")
if (UtilValidate.isNotEmpty(members)) {
    for (i = 0; i < members.size(); i++) {
        productCategoryMember = (GenericValue) members.get(i)
        if (productCategoryMember.get("quantity") != null && productCategoryMember.getDouble("quantity").doubleValue() > 0.0) {
            context.put("hasQuantities", new Boolean(true))
            break
        }
    }
}

CategoryContentWrapper categoryContentWrapper = new CategoryContentWrapper(productCategory, request)
context.put("categoryContentWrapper", categoryContentWrapper)

component://order/groovyScripts/entry/catalog/ProductSummary.groovy
/*
 * This script is also referenced by the ecommerce's screens and
 * should not contain order component's specific code.
 */

import org.apache.ofbiz.base.util.*
import org.apache.ofbiz.entity.*
import org.apache.ofbiz.entity.util.EntityQuery
import org.apache.ofbiz.service.*
import org.apache.ofbiz.product.product.ProductContentWrapper
import org.apache.ofbiz.product.config.ProductConfigWorker
import org.apache.ofbiz.product.catalog.*
import org.apache.ofbiz.product.store.*
import org.apache.ofbiz.order.shoppingcart.*
import org.apache.ofbiz.product.product.ProductWorker
import org.apache.ofbiz.webapp.website.WebSiteWorker
import java.text.NumberFormat

//either optProduct, optProductId or productId must be specified
product = request.getAttribute("optProduct")
optProductId = request.getAttribute("optProductId")
productId = product?.productId ?: optProductId ?: request.getAttribute("productId")

webSiteId = WebSiteWorker.getWebSiteId(request)
catalogId = CatalogWorker.getCurrentCatalogId(request)
cart = ShoppingCartEvents.getCartObject(request)
productStore = null
productStoreId = null
facilityId = null
if (cart.isSalesOrder()) {
    productStore = ProductStoreWorker.getProductStore(request)
    productStoreId = productStore.productStoreId
    context.productStoreId = productStoreId
    facilityId = productStore.inventoryFacilityId
}

if (!facilityId) {
    productStoreFacility = EntityQuery.use(delegator).select("facilityId").from("ProductStoreFacility").where("productStoreId", productStoreId).queryFirst()
    if (productStoreFacility) {
        facilityId = productStoreFacility.facilityId
    }
}

autoUserLogin = session.getAttribute("autoUserLogin")
userLogin = session.getAttribute("userLogin")

context.remove("daysToShip")
context.remove("averageRating")
context.remove("numRatings")
context.remove("totalPrice")

// get the product entity
if (!product && productId) {
    product = from("Product").where("productId", productId).cache(true).queryOne()
}
if (product) {
    //if order is purchase then don't calculate available inventory for product.
    if (cart.isSalesOrder()) {
        resultOutput = runService('getInventoryAvailableByFacility', [productId : product.productId, facilityId : facilityId, useCache : true])
        totalAvailableToPromise = resultOutput.availableToPromiseTotal
        if (totalAvailableToPromise && totalAvailableToPromise.doubleValue() > 0) {
            productFacility = from("ProductFacility").where("productId", product.productId, "facilityId", facilityId).cache(true).queryOne()
            if (productFacility?.daysToShip != null) {
                context.daysToShip = productFacility.daysToShip
            }
        }
    } else {
       supplierProduct = from("SupplierProduct").where("productId", product.productId).orderBy("-availableFromDate").cache(true).queryFirst()
       if (supplierProduct?.standardLeadTimeDays != null) {
           standardLeadTimeDays = supplierProduct.standardLeadTimeDays
           daysToShip = standardLeadTimeDays + 1
           context.daysToShip = daysToShip
       }
    }
    // make the productContentWrapper
    productContentWrapper = new ProductContentWrapper(product, request)
    context.productContentWrapper = productContentWrapper
}

categoryId = null
reviews = null
if (product) {
    categoryId = parameters.category_id ?: request.getAttribute("productCategoryId")

    // get the product price
    if (cart.isSalesOrder()) {
        // sales order: run the "calculateProductPrice" service
        priceContext = [product : product, currencyUomId : cart.getCurrency(),
                autoUserLogin : autoUserLogin, userLogin : userLogin]
        priceContext.webSiteId = webSiteId
        priceContext.prodCatalogId = catalogId
        priceContext.productStoreId = productStoreId
        priceContext.agreementId = cart.getAgreementId()
        priceContext.partyId = cart.getPartyId() // IMPORTANT: otherwise it'll be calculating prices using the logged in user which could be a CSR instead of the customer
        priceContext.checkIncludeVat = "Y"
        priceMap = runService('calculateProductPrice', priceContext)

        context.price = priceMap
    } else {
        // purchase order: run the "calculatePurchasePrice" service
        priceContext = [product : product, currencyUomId : cart.getCurrency(),
                partyId : cart.getPartyId(), userLogin : userLogin]
        priceMap = runService('calculatePurchasePrice', priceContext)

        context.price = priceMap
    }

    // get aggregated product totalPrice
    if ("AGGREGATED".equals(product.productTypeId)||"AGGREGATED_SERVICE".equals(product.productTypeId)) {
        configWrapper = ProductConfigWorker.getProductConfigWrapper(productId, cart.getCurrency(), request)
        if (configWrapper) {
            configWrapper.setDefaultConfig()
            context.totalPrice = configWrapper.getTotalPrice()
        }
    }

    // get the product review(s)
    reviews = product.getRelated("ProductReview", null, ["-postedDateTime"], true)
    
    // get product variant for Box/Case/Each
    productVariants = []
    boolean isAlternativePacking = ProductWorker.isAlternativePacking(delegator, product.productId, null)
    mainProducts = []
    if(isAlternativePacking){
        productVirtualVariants = from("ProductAssoc").where("productIdTo", product.productId , "productAssocTypeId", "ALTERNATIVE_PACKAGE").cache(true).queryList()
        if(productVirtualVariants){
            productVirtualVariants.each { virtualVariantKey ->
                mainProductMap = [:]
                mainProduct = virtualVariantKey.getRelatedOne("MainProduct", true)
                quantityUom = mainProduct.getRelatedOne("QuantityUom", true)
                mainProductMap.productId = mainProduct.productId
                mainProductMap.piecesIncluded = mainProduct.piecesIncluded
                mainProductMap.uomDesc = quantityUom.description
                mainProducts.add(mainProductMap)
            }
        }
        
        // get alternative product price when product doesn't have any feature 
        jsBuf = new StringBuffer()
        jsBuf.append("<script language=\"JavaScript\" type=\"text/javascript\">")
        
        // make a list of variant sku with requireAmount
        virtualVariantsRes = runService('getAssociatedProducts', [productIdTo : productId, type : "ALTERNATIVE_PACKAGE", checkViewAllow : true, prodCatalogId : categoryId])
        virtualVariants = virtualVariantsRes.assocProducts
        // Format to apply the currency code to the variant price in the javascript
        if (productStore) {
            localeString = productStore.defaultLocaleString
            if (localeString) {
                locale = UtilMisc.parseLocale(localeString)
            }
        }
        variantPriceList = []
        numberFormat = NumberFormat.getCurrencyInstance(locale)
        
        if(virtualVariants){
            amt = new StringBuffer()
            // Create the javascript to return the price for each variant
            variantPriceJS = new StringBuffer()
            variantPriceJS.append("function getVariantPrice(sku) { ")
            
            virtualVariants.each { virtualAssoc ->
                virtual = virtualAssoc.getRelatedOne("MainProduct", false)
                // Get price from a virtual product
                priceContext.product = virtual
                if (cart.isSalesOrder()) {
                    // sales order: run the "calculateProductPrice" service
                    virtualPriceMap = runService('calculateProductPrice', priceContext)
                    BigDecimal calculatedPrice = (BigDecimal)virtualPriceMap.get("price")
                    // Get the minimum quantity for variants if MINIMUM_ORDER_PRICE is set for variants.
                    variantPriceList.add(virtualPriceMap)
                } else {
                    virtualPriceMap = runService('calculatePurchasePrice', priceContext)
                }
                if (virtualPriceMap.basePrice) {
                    basePrice = numberFormat.format(virtualPriceMap.basePrice)
                } else {
                    basePrice = UtilProperties.getResourceBundleMap("CommonUiLabels", locale).get("CommonNA")
                }
                variantPriceJS.append("  if (sku == \"" + virtual.productId + "\") return \"" + basePrice + "\"; ")
            }
            variantPriceJS.append(" } ")
            
            context.variantPriceList = variantPriceList
            jsBuf.append(amt.toString())
            jsBuf.append(variantPriceJS.toString())
            jsBuf.append("</script>")
            context.virtualJavaScript = jsBuf
        }
    }
    context.mainProducts = mainProducts
}

// get the average rating
if (reviews) {
    totalProductRating = 0
    numRatings = 0
    reviews.each { productReview ->
        productRating = productReview.productRating
        if (productRating) {
            totalProductRating += productRating
            numRatings++
        }
    }
    if (numRatings) {
        context.averageRating = totalProductRating/numRatings
        context.numRatings = numRatings
    }
}

// an example of getting features of a certain type to show
sizeProductFeatureAndAppls = from("ProductFeatureAndAppl").where("productId", productId, "productFeatureTypeId", "SIZE").orderBy("sequenceNum", "defaultSequenceNum").cache(true).queryList()

context.product = product
context.categoryId = categoryId
context.productReviews = reviews
context.sizeProductFeatureAndAppls = sizeProductFeatureAndAppls

1.3 相关的ftl模板

component://ecommerce/template/Main.ftl
<#-- Render the category page -->
<#if requestAttributes.productCategoryId?has_content>
  ${screens.render("component://ecommerce/widget/CatalogScreens.xml#bestSellingCategory")}
  ${screens.render("component://ecommerce/widget/CatalogScreens.xml#category-include")}
<#else>
  <center><h2>${uiLabelMap.EcommerceNoPROMOTIONCategory}</h2></center>
</#if>

component://order/template/entry/catalog/CategoryDetail.ftl
<script type="text/javascript">
    function callDocumentByPaginate(info) {
        var str = info.split('~');
        var checkUrl = '<@ofbizUrl>categoryAjaxFired</@ofbizUrl>';
        if(checkUrl.search("http"))
            var ajaxUrl = '<@ofbizUrl>categoryAjaxFired</@ofbizUrl>';
        else
            var ajaxUrl = '<@ofbizUrl>categoryAjaxFiredSecure</@ofbizUrl>';
            
        //jQuerry Ajax Request
        jQuery.ajax({
            url: ajaxUrl,
            type: 'POST',
            data: {"category_id" : str[0], "VIEW_SIZE" : str[1], "VIEW_INDEX" : str[2]},
            error: function(msg) {
                alert("An error occurred loading content! : " + msg);
            },
            success: function(msg) {
                jQuery('#div3').html(msg);
            }
        });
     }
</script>

<#macro paginationControls>
    <#assign viewIndexMax = Static["java.lang.Math"].ceil((listSize)?double / viewSize?double)>
      <#if (viewIndexMax?int > 0)>
        <div class="product-prevnext">
            <select name="pageSelect" onchange="callDocumentByPaginate(this[this.selectedIndex].value);">
                <option value="#">${uiLabelMap.CommonPage} ${viewIndex?int + 1} ${uiLabelMap.CommonOf} ${viewIndexMax}</option>
                <#if (viewIndex?int > 1)>
                    <#list 1..viewIndexMax as curViewNum>
                         <option value="${productCategoryId}~${viewSize}~${curViewNum-1?int}">${uiLabelMap.CommonGotoPage} ${curViewNum}</option>
                    </#list>
                </#if>
            </select>
            <#-- End Page Select Drop-Down -->
            <#if (viewIndex?int > 0)>
                <a href="javascript: void(0);" onclick="callDocumentByPaginate('${productCategoryId}~${viewSize}~${viewIndex?int - 1}');" class="buttontext">${uiLabelMap.CommonPrevious}</a> |
            </#if>
            <#if ((listSize?int - viewSize?int) > 0)>
                <span>${lowIndex} - ${highIndex} ${uiLabelMap.CommonOf} ${listSize}</span>
            </#if>
            <#if highIndex?int < listSize?int>
             | <a href="javascript: void(0);" onclick="callDocumentByPaginate('${productCategoryId}~${viewSize}~${viewIndex?int + 1}');" class="buttontext">${uiLabelMap.CommonNext}</a>
            </#if>
        </div>
    </#if>
</#macro>


<#if productCategory??>
    <#assign categoryName = categoryContentWrapper.get("CATEGORY_NAME", "html")!/>
    <#assign categoryDescription = categoryContentWrapper.get("DESCRIPTION", "html")!/>
    <#if categoryName?has_content>
        <h1>${categoryName}</h1>
    </#if>
    <#if categoryDescription?has_content>
        <h1>${categoryDescription}</h1>
    </#if>
    <#if hasQuantities??>
      <form method="post" action="<@ofbizUrl>addCategoryDefaults<#if requestAttributes._CURRENT_VIEW_??>/${requestAttributes._CURRENT_VIEW_}</#if></@ofbizUrl>" name="thecategoryform" style='margin: 0;'>
        <input type='hidden' name='add_category_id' value='${productCategory.productCategoryId}'/>
        <#if requestParameters.product_id??><input type='hidden' name='product_id' value='${requestParameters.product_id}'/></#if>
        <#if requestParameters.category_id??><input type='hidden' name='category_id' value='${requestParameters.category_id}'/></#if>
        <#if requestParameters.VIEW_INDEX??><input type='hidden' name='VIEW_INDEX' value='${requestParameters.VIEW_INDEX}'/></#if>
        <#if requestParameters.SEARCH_STRING??><input type='hidden' name='SEARCH_STRING' value='${requestParameters.SEARCH_STRING}'/></#if>
        <#if requestParameters.SEARCH_CATEGORY_ID??><input type='hidden' name='SEARCH_CATEGORY_ID' value='${requestParameters.SEARCH_CATEGORY_ID}'/></#if>
        <a href="javascript:document.thecategoryform.submit()" class="buttontext"><span style="white-space: nowrap;">${uiLabelMap.ProductAddProductsUsingDefaultQuantities}</span></a>
      </form>
    </#if>
    <#if searchInCategory?default("Y") == "Y">
        <a href="<@ofbizUrl>advancedsearch?SEARCH_CATEGORY_ID=${productCategory.productCategoryId}</@ofbizUrl>" class="buttontext">${uiLabelMap.ProductSearchInCategory}</a>
    </#if>
    <#assign longDescription = categoryContentWrapper.get("LONG_DESCRIPTION", "html")!/>
    <#assign categoryImageUrl = categoryContentWrapper.get("CATEGORY_IMAGE_URL", "url")!/>
    <#if categoryImageUrl?string?has_content || longDescription?has_content>
      <div>
        <#if categoryImageUrl?string?has_content>
          <#assign height=100/>
          <img src='<@ofbizContentUrl>${categoryImageUrl}</@ofbizContentUrl>' vspace='5' hspace='5' align='left' class='cssImgLarge' />
        </#if>
        <#if longDescription?has_content>
          ${longDescription}
        </#if>
      </div>
  </#if>
</#if>

<#if productCategoryLinkScreen?has_content && productCategoryLinks?has_content>
    <div class="productcategorylink-container">
        <#list productCategoryLinks as productCategoryLink>
            ${setRequestAttribute("productCategoryLink",productCategoryLink)}
            ${screens.render(productCategoryLinkScreen)}
        </#list>
    </div>
</#if>

<#if productCategoryMembers?has_content>
    <#-- Pagination -->
    <#if paginateEcommerceStyle??>
        <@paginationControls/>
    <#else>
        <#include "component://common/template/includes/HtmlTemplate.ftl"/>
        <#assign commonUrl = "category?category_id="+ (parameters.category_id!) + "&"/>
        <#--assign viewIndex = viewIndex - 1/-->
        <#assign viewIndexFirst = 0/>
        <#assign viewIndexPrevious = viewIndex - 1/>
        <#assign viewIndexNext = viewIndex + 1/>
        <#assign viewIndexLast = Static["org.apache.ofbiz.base.util.UtilMisc"].getViewLastIndex(listSize, viewSize) />
        <#assign messageMap = Static["org.apache.ofbiz.base.util.UtilMisc"].toMap("lowCount", lowIndex, "highCount", highIndex, "total", listSize)/>
        <#assign commonDisplaying = Static["org.apache.ofbiz.base.util.UtilProperties"].getMessage("CommonUiLabels", "CommonDisplaying", messageMap, locale)/>
        <@nextPrev commonUrl=commonUrl ajaxEnabled=false javaScriptEnabled=false paginateStyle="nav-pager" paginateFirstStyle="nav-first" viewIndex=viewIndex highIndex=highIndex listSize=listSize viewSize=viewSize ajaxFirstUrl="" firstUrl="" paginateFirstLabel="" paginatePreviousStyle="nav-previous" ajaxPreviousUrl="" previousUrl="" paginatePreviousLabel="" pageLabel="" ajaxSelectUrl="" selectUrl="" ajaxSelectSizeUrl="" selectSizeUrl="" commonDisplaying=commonDisplaying paginateNextStyle="nav-next" ajaxNextUrl="" nextUrl="" paginateNextLabel="" paginateLastStyle="nav-last" ajaxLastUrl="" lastUrl="" paginateLastLabel="" paginateViewSizeLabel="" />
    </#if>
      <#assign numCol = numCol?default(1)>
      <#assign numCol = numCol?number>
      <#assign tabCol = 1>
      <div
      <#if categoryImageUrl?string?has_content>
        style="position: relative; margin-top: ${height}px;"
      </#if>
      class="productsummary-container<#if (numCol?int > 1)> matrix</#if>">
      <#if (numCol?int > 1)>
        <table>
      </#if>
        <#list productCategoryMembers as productCategoryMember>
          <#if (numCol?int == 1)>
            ${setRequestAttribute("optProductId", productCategoryMember.productId)}
            ${setRequestAttribute("productCategoryMember", productCategoryMember)}
            ${setRequestAttribute("listIndex", productCategoryMember_index)}
            ${screens.render(productsummaryScreen)}
          <#else>
              <#if (tabCol?int = 1)><tr></#if>
                  <td>
                      ${setRequestAttribute("optProductId", productCategoryMember.productId)}
                      ${setRequestAttribute("productCategoryMember", productCategoryMember)}
                      ${setRequestAttribute("listIndex", productCategoryMember_index)}
                      ${screens.render(productsummaryScreen)}
                  </td>
              <#if (tabCol?int = numCol)></tr></#if>
              <#assign tabCol = tabCol+1><#if (tabCol?int > numCol)><#assign tabCol = 1></#if>
           </#if>
        </#list>
      <#if (numCol?int > 1)>
        </table>
      </#if>
      </div>
    <#if paginateEcommerceStyle??>
        <@paginationControls/>
    </#if>
<#else>
    <hr />
    <div>${uiLabelMap.ProductNoProductsInThisCategory}</div>
</#if>

component://order/template/entry/catalog/ProductSummary.ftl
${virtualJavaScript!}
<script type="text/javascript">
<!--
    function displayProductVirtualId(variantId, virtualProductId, pForm) {
        if(variantId){
            pForm.product_id.value = variantId;
        }else{
            pForm.product_id.value = '';
            variantId = '';
        }
        var elem = document.getElementById('product_id_display');
        var txt = document.createTextNode(variantId);
        if(elem.hasChildNodes()) {
            elem.replaceChild(txt, elem.firstChild);
        } else {
            elem.appendChild(txt);
        }
        
        var priceElem = document.getElementById('variant_price_display');
        var price = getVariantPrice(variantId);
        var priceTxt = null;
        if(price){
            priceTxt = document.createTextNode(price);
        }else{
            priceTxt = document.createTextNode('');
        }
        
        if(priceElem.hasChildNodes()) {
            priceElem.replaceChild(priceTxt, priceElem.firstChild);
        } else {
            priceElem.appendChild(priceTxt);
        }
    }
//-->
</script>
<#if product??>
    <#-- variable setup -->
    <#if backendPath?default("N") == "Y">
        <#assign productUrl><@ofbizCatalogUrl productId=product.productId productCategoryId=categoryId/></#assign>
    <#else>
        <#assign productUrl><@ofbizCatalogAltUrl productId=product.productId productCategoryId=categoryId/></#assign>
    </#if>

    <#if requestAttributes.productCategoryMember??>
        <#assign prodCatMem = requestAttributes.productCategoryMember>
    </#if>
    <#assign smallImageUrl = productContentWrapper.get("SMALL_IMAGE_URL", "url")!>
    <#assign largeImageUrl = productContentWrapper.get("LARGE_IMAGE_URL", "url")!>
    <#if !smallImageUrl?string?has_content><#assign smallImageUrl = "/images/defaultImage.jpg"></#if>
    <#if !largeImageUrl?string?has_content><#assign largeImageUrl = "/images/defaultImage.jpg"></#if>
    <#-- end variable setup -->
    <#assign productInfoLinkId = "productInfoLink">
    <#assign productInfoLinkId = productInfoLinkId + product.productId/>
    <#assign productDetailId = "productDetailId"/>
    <#assign productDetailId = productDetailId + product.productId/>
    <div class="productsummary">
        <div class="smallimage">
            <a href="${productUrl}">
                <span id="${productInfoLinkId}" class="popup_link"><img src="<@ofbizContentUrl>${contentPathPrefix!}${smallImageUrl}</@ofbizContentUrl>" alt="Small Image"/></span>
            </a>
        </div>
        <div id="${productDetailId}" class="popup" style="display:none;">
          <table class="ui-widget">
            <tr>
              <th><img src="<@ofbizContentUrl>${contentPathPrefix!}${largeImageUrl}</@ofbizContentUrl>" alt="Large Image"/></th><td> </td>
            </tr>
            <tr>
              <th>${uiLabelMap.ProductProductId}</th><td>${product.productId!}</td>
            </tr>
            <tr>
              <th>${uiLabelMap.ProductProductName}</th><td>${productContentWrapper.get("PRODUCT_NAME", "html")!}</td>
            </tr>
            <tr>
              <th>${uiLabelMap.CommonDescription}</th><td>${productContentWrapper.get("DESCRIPTION", "html")!}</td>
            </tr>
          </table>
        </div>
        <script type="text/javascript">
          jQuery("#${productInfoLinkId}").attr('title', jQuery("#${productDetailId}").remove().html());
          jQuery("#${productInfoLinkId}").tooltip({
              content: function(){
                  return this.getAttribute("title");
              },
              tooltipClass: "popup",
              track: true
          }); 
        </script>
        <div class="productbuy">
          <#-- check to see if introductionDate hasn't passed yet -->
          <#if product.introductionDate?? && nowTimestamp.before(product.introductionDate)>
            <div style="color: red;">${uiLabelMap.ProductNotYetAvailable}</div>
          <#-- check to see if salesDiscontinuationDate has passed -->
          <#elseif product.salesDiscontinuationDate?? && nowTimestamp.after(product.salesDiscontinuationDate)>
            <div style="color: red;">${uiLabelMap.ProductNoLongerAvailable}</div>
          <#-- check to see if it is a rental item; will enter parameters on the detail screen-->
          <#elseif product.productTypeId! == "ASSET_USAGE">
            <a href="${productUrl}" class="buttontext">${uiLabelMap.OrderMakeBooking}...</a>
          <#-- check to see if it is an aggregated or configurable product; will enter parameters on the detail screen-->
          <#elseif product.productTypeId! == "AGGREGATED" || product.productTypeId! == "AGGREGATED_SERVICE">
            <a href="${productUrl}" class="buttontext">${uiLabelMap.OrderConfigure}...</a>
          <#-- check to see if the product is a virtual product -->
          <#elseif product.isVirtual?? && product.isVirtual == "Y">
            <a href="${productUrl}" class="buttontext">${uiLabelMap.OrderChooseVariations}...</a>
          <#-- check to see if the product requires an amount -->
          <#elseif product.requireAmount?? && product.requireAmount == "Y">
            <a href="${productUrl}" class="buttontext">${uiLabelMap.OrderChooseAmount}...</a>
          <#elseif product.productTypeId! == "ASSET_USAGE_OUT_IN">
            <a href="${productUrl}" class="buttontext">${uiLabelMap.OrderRent}...</a>
          <#else>
            <form method="post" action="<@ofbizUrl>additem</@ofbizUrl>" name="the${requestAttributes.formNamePrefix!}${requestAttributes.listIndex!}form" style="margin: 0;">
              <input type="hidden" name="add_product_id" value="${product.productId}"/>
              <input type="text" size="5" name="quantity" value="1"/>
              <input type="hidden" name="clearSearch" value="N"/>
              <input type="hidden" name="mainSubmitted" value="Y"/>
              <a href="javascript:document.the${requestAttributes.formNamePrefix!}${requestAttributes.listIndex!}form.submit()" class="buttontext">${uiLabelMap.OrderAddToCart}</a>
            <#if mainProducts?has_content>
                <input type="hidden" name="product_id" value=""/>
                <select name="productVariantId" onchange="javascript:displayProductVirtualId(this.value, '${product.productId}', this.form);">
                    <option value="">Select Unit Of Measure</option>
                    <#list mainProducts as mainProduct>
                        <option value="${mainProduct.productId}">${mainProduct.uomDesc} : ${mainProduct.piecesIncluded}</option>
                    </#list>
                </select>
                <div style="display: inline-block;">
                    <strong><span id="product_id_display"> </span></strong>
                    <strong><span id="variant_price_display"> </span></strong>
                </div>
            </#if>
            </form>
            
              <#if prodCatMem?? && prodCatMem.quantity?? && 0.00 < prodCatMem.quantity?double>
                <form method="post" action="<@ofbizUrl>additem</@ofbizUrl>" name="the${requestAttributes.formNamePrefix!}${requestAttributes.listIndex!}defaultform" style="margin: 0;">
                  <input type="hidden" name="add_product_id" value="${prodCatMem.productId!}"/>
                  <input type="hidden" name="quantity" value="${prodCatMem.quantity!}"/>
                  <input type="hidden" name="clearSearch" value="N"/>
                  <input type="hidden" name="mainSubmitted" value="Y"/>
                  <a href="javascript:document.the${requestAttributes.formNamePrefix!}${requestAttributes.listIndex!}defaultform.submit()" class="buttontext">${uiLabelMap.CommonAddDefault}(${prodCatMem.quantity?string.number}) ${uiLabelMap.OrderToCart}</a>
                </form>
                <#assign productCategory = delegator.findOne("ProductCategory", Static["org.apache.ofbiz.base.util.UtilMisc"].toMap("productCategoryId", prodCatMem.productCategoryId), false)/>
                <#if productCategory.productCategoryTypeId != "BEST_SELL_CATEGORY">
                    <form method="post" action="<@ofbizUrl>additem</@ofbizUrl>" name="the${requestAttributes.formNamePrefix!}${requestAttributes.listIndex!}defaultform" style="margin: 0;">
                      <input type="hidden" name="add_product_id" value="${prodCatMem.productId!}"/>
                      <input type="hidden" name="quantity" value="${prodCatMem.quantity!}"/>
                      <input type="hidden" name="clearSearch" value="N"/>
                      <input type="hidden" name="mainSubmitted" value="Y"/>
                      <a href="javascript:document.the${requestAttributes.formNamePrefix!}${requestAttributes.listIndex!}defaultform.submit()" class="buttontext">${uiLabelMap.CommonAddDefault}(${prodCatMem.quantity?string.number}) ${uiLabelMap.OrderToCart}</a>
                    </form>
                </#if>
              </#if>
          </#if>
        </div>
        <div class="productinfo">
          <div>
            <a href="${productUrl}" class="linktext">${productContentWrapper.get("PRODUCT_NAME", "html")!}</a>
          </div>
          <div>${productContentWrapper.get("DESCRIPTION", "html")!}<#if daysToShip??> - ${uiLabelMap.ProductUsuallyShipsIn} <b>${daysToShip}</b> ${uiLabelMap.CommonDays}!</#if></div>

          <#-- Display category-specific product comments -->
          <#if prodCatMem?? && prodCatMem.comments?has_content>
          <div>${prodCatMem.comments}</div>
          </#if>

          <#-- example of showing a certain type of feature with the product -->
          <#if sizeProductFeatureAndAppls?has_content>
            <div>
              <#if (sizeProductFeatureAndAppls?size == 1)>
                ${uiLabelMap.SizeAvailableSingle}:
              <#else>
                ${uiLabelMap.SizeAvailableMultiple}:
              </#if>
              <#list sizeProductFeatureAndAppls as sizeProductFeatureAndAppl>
                ${sizeProductFeatureAndAppl.abbrev?default(sizeProductFeatureAndAppl.description?default(sizeProductFeatureAndAppl.productFeatureId))}<#if sizeProductFeatureAndAppl_has_next>,</#if>
              </#list>
            </div>
          </#if>
          <div>
              <b>${product.productId!}</b>
                <#if totalPrice??>
                  <div>${uiLabelMap.ProductAggregatedPrice}: <span class='basePrice'><@ofbizCurrency amount=totalPrice isoCode=totalPrice.currencyUsed/></span></div>
                <#else>
                <#if price.competitivePrice?? && price.price?? && price.price?double < price.competitivePrice?double>
                  ${uiLabelMap.ProductCompareAtPrice}: <span class='basePrice'><@ofbizCurrency amount=price.competitivePrice isoCode=price.currencyUsed/></span>
                </#if>
                <#if price.listPrice?? && price.price?? && price.price?double < price.listPrice?double>
                  ${uiLabelMap.ProductListPrice}: <span class="basePrice"><@ofbizCurrency amount=price.listPrice isoCode=price.currencyUsed/></span>
                </#if>
                <b>
                  <#if price.isSale?? && price.isSale>
                    <span class="salePrice">${uiLabelMap.OrderOnSale}!</span>
                    <#assign priceStyle = "salePrice">
                  <#else>
                    <#assign priceStyle = "regularPrice">
                  </#if>

                  <#if (price.price?default(0) > 0 && product.requireAmount?default("N") == "N")>
                    ${uiLabelMap.OrderYourPrice}: <#if "Y" = product.isVirtual!> ${uiLabelMap.CommonFrom} </#if><span class="${priceStyle}"><@ofbizCurrency amount=price.price isoCode=price.currencyUsed/></span>
                  </#if>
                </b>
                <#if price.listPrice?? && price.price?? && price.price?double < price.listPrice?double>
                  <#assign priceSaved = price.listPrice?double - price.price?double>
                  <#assign percentSaved = (priceSaved?double / price.listPrice?double) * 100>
                    ${uiLabelMap.OrderSave}: <span class="basePrice"><@ofbizCurrency amount=priceSaved isoCode=price.currencyUsed/> (${percentSaved?int}%)</span>
                </#if>
                </#if>
                <#-- show price details ("showPriceDetails" field can be set in the screen definition) -->
                <#if (showPriceDetails?? && showPriceDetails?default("N") == "Y")>
                    <#if price.orderItemPriceInfos??>
                        <#list price.orderItemPriceInfos as orderItemPriceInfo>
                            <div>${orderItemPriceInfo.description!}</div>
                        </#list>
                    </#if>
                </#if>
          </div>
          <#if averageRating?? && (averageRating?double > 0) && numRatings?? && (numRatings?long > 2)>
              <div>${uiLabelMap.OrderAverageRating}: ${averageRating} (${uiLabelMap.CommonFrom} ${numRatings} ${uiLabelMap.OrderRatings})</div>
          </#if>
          <form method="post" action="<@ofbizUrl secure="${request.isSecure()?string}">addToCompare</@ofbizUrl>" name="addToCompare${requestAttributes.listIndex!}form">
              <input type="hidden" name="productId" value="${product.productId}"/>
              <input type="hidden" name="mainSubmitted" value="Y"/>
          </form>
          <a href="javascript:document.addToCompare${requestAttributes.listIndex!}form.submit()" class="buttontext">${uiLabelMap.ProductAddToCompare}</a>
        </div>
    </div>
<#else>
 ${uiLabelMap.ProductErrorProductNotFound}.<br />
</#if>

1.4 相关的java文件

org.apache.ofbiz.product.catalog.CatalogWorker

org.apache.ofbiz.product.category.CategoryWorker

服务getProductCategoryAndLimitedMembers具体实现
ofbiz学习——推广/宣传产品展示
根据服务引擎工具找到对应getProductCategoryAndLimitedMembers服务的实现源码:
org.apache.ofbiz.product.category.CategoryServices.getProductCategoryAndLimitedMembers
    public static Map<String, Object> getProductCategoryAndLimitedMembers(DispatchContext dctx, Map<String, ? extends Object> context) {
        Delegator delegator = dctx.getDelegator();
        LocalDispatcher dispatcher = dctx.getDispatcher();
        String productCategoryId = (String) context.get("productCategoryId");
        boolean limitView = ((Boolean) context.get("limitView")).booleanValue();
        int defaultViewSize = ((Integer) context.get("defaultViewSize")).intValue();
        Timestamp introductionDateLimit = (Timestamp) context.get("introductionDateLimit");
        Timestamp releaseDateLimit = (Timestamp) context.get("releaseDateLimit");

        List<String> orderByFields = UtilGenerics.checkList(context.get("orderByFields"));
        if (orderByFields == null) orderByFields = new LinkedList<String>();
        String entityName = getCategoryFindEntityName(delegator, orderByFields, introductionDateLimit, releaseDateLimit);

        String prodCatalogId = (String) context.get("prodCatalogId");

        boolean useCacheForMembers = (context.get("useCacheForMembers") == null || ((Boolean) context.get("useCacheForMembers")).booleanValue());
        boolean activeOnly = (context.get("activeOnly") == null || ((Boolean) context.get("activeOnly")).booleanValue());

        // checkViewAllow defaults to false, must be set to true and pass the prodCatalogId to enable
        boolean checkViewAllow = (prodCatalogId != null && context.get("checkViewAllow") != null &&
                ((Boolean) context.get("checkViewAllow")).booleanValue());

        String viewProductCategoryId = null;
        if (checkViewAllow) {
            viewProductCategoryId = CatalogWorker.getCatalogViewAllowCategoryId(delegator, prodCatalogId);
        }

        Timestamp nowTimestamp = UtilDateTime.nowTimestamp();
        int viewIndex = 0;
        try {
            viewIndex = Integer.valueOf((String) context.get("viewIndexString")).intValue();
        } catch (Exception e) {
            viewIndex = 0;
        }

        int viewSize = defaultViewSize;
        try {
            viewSize = Integer.valueOf((String) context.get("viewSizeString")).intValue();
        } catch (Exception e) {
            viewSize = defaultViewSize;
        }

        GenericValue productCategory = null;
        try {
            productCategory = EntityQuery.use(delegator).from("ProductCategory").where("productCategoryId", productCategoryId).cache().queryOne();
        } catch (GenericEntityException e) {
            Debug.logWarning(e.getMessage(), module);
            productCategory = null;
        }

        int listSize = 0;
        int lowIndex = 0;
        int highIndex = 0;

        if (limitView) {
            // get the indexes for the partial list
            lowIndex = ((viewIndex * viewSize) + 1);
            highIndex = (viewIndex + 1) * viewSize;
        } else {
            lowIndex = 0;
            highIndex = 0;
        }
        
        boolean filterOutOfStock = false;
        try {
            String productStoreId = (String) context.get("productStoreId");
            if (UtilValidate.isNotEmpty(productStoreId)) {
                GenericValue productStore = EntityQuery.use(delegator).from("ProductStore").where("productStoreId", productStoreId).queryOne();
                if (productStore != null && "N".equals(productStore.getString("showOutOfStockProducts"))) {
                    filterOutOfStock = true;
                }
            }
        } catch (GenericEntityException e) {
            Debug.logWarning(e.getMessage(), module);
        }

        List<GenericValue> productCategoryMembers = null;
        if (productCategory != null) {
            EntityListIterator pli = null;
            try {
                if (useCacheForMembers) {
                    productCategoryMembers = EntityQuery.use(delegator).from(entityName).where("productCategoryId", productCategoryId).orderBy(orderByFields).cache(true).queryList();
                    if (activeOnly) {
                        productCategoryMembers = EntityUtil.filterByDate(productCategoryMembers, true);
                    }
                    List<EntityCondition> filterConditions = new LinkedList<EntityCondition>();
                    if (introductionDateLimit != null) {
                        EntityCondition condition = EntityCondition.makeCondition(EntityCondition.makeCondition("introductionDate", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("introductionDate", EntityOperator.LESS_THAN_EQUAL_TO, introductionDateLimit));
                        filterConditions.add(condition);
                    }
                    if (releaseDateLimit != null) {
                        EntityCondition condition = EntityCondition.makeCondition(EntityCondition.makeCondition("releaseDate", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("releaseDate", EntityOperator.LESS_THAN_EQUAL_TO, releaseDateLimit));
                        filterConditions.add(condition);
                    }
                    if (!filterConditions.isEmpty()) {
                        productCategoryMembers = EntityUtil.filterByCondition(productCategoryMembers, EntityCondition.makeCondition(filterConditions, EntityOperator.AND));
                    }
                    
                    // filter out of stock products
                    if (filterOutOfStock) {
                        try {
                            productCategoryMembers = ProductWorker.filterOutOfStockProducts(productCategoryMembers, dispatcher, delegator);
                        } catch (GeneralException e) {
                            Debug.logWarning("Problem filtering out of stock products :"+e.getMessage(), module);
                        }
                    }
                    // filter out the view allow before getting the sublist
                    if (UtilValidate.isNotEmpty(viewProductCategoryId)) {
                        productCategoryMembers = CategoryWorker.filterProductsInCategory(delegator, productCategoryMembers, viewProductCategoryId);
                    }

                    // set the index and size
                    listSize = productCategoryMembers.size();         
                    if (limitView) {
                        // limit high index to (filtered) listSize
                        if (highIndex > listSize) {
                            highIndex = listSize;
                        }
                        // if lowIndex > listSize, the input is wrong => reset to first page
                        if (lowIndex > listSize) {
                            viewIndex = 0;
                            lowIndex = 1;
                            highIndex = Math.min(viewSize, highIndex);
                        }
                        // get only between low and high indexes
                        if (UtilValidate.isNotEmpty(productCategoryMembers)) {
                            productCategoryMembers = productCategoryMembers.subList(lowIndex-1, highIndex);
                        }
                    } else {
                        lowIndex = 1;
                        highIndex = listSize;
                    }
                } else {
                    List<EntityCondition> mainCondList = new LinkedList<EntityCondition>();
                    mainCondList.add(EntityCondition.makeCondition("productCategoryId", EntityOperator.EQUALS, productCategory.getString("productCategoryId")));
                    if (activeOnly) {
                        mainCondList.add(EntityCondition.makeCondition("fromDate", EntityOperator.LESS_THAN_EQUAL_TO, nowTimestamp));
                        mainCondList.add(EntityCondition.makeCondition(EntityCondition.makeCondition("thruDate", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("thruDate", EntityOperator.GREATER_THAN, nowTimestamp)));
                    }
                    if (introductionDateLimit != null) {
                        mainCondList.add(EntityCondition.makeCondition(EntityCondition.makeCondition("introductionDate", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("introductionDate", EntityOperator.LESS_THAN_EQUAL_TO, introductionDateLimit)));
                    }
                    if (releaseDateLimit != null) {
                        mainCondList.add(EntityCondition.makeCondition(EntityCondition.makeCondition("releaseDate", EntityOperator.EQUALS, null), EntityOperator.OR, EntityCondition.makeCondition("releaseDate", EntityOperator.LESS_THAN_EQUAL_TO, releaseDateLimit)));
                    }
                    EntityCondition mainCond = EntityCondition.makeCondition(mainCondList, EntityOperator.AND);

                    // set distinct on
                    // using list iterator
                    pli = EntityQuery.use(delegator).from(entityName).where(mainCond).orderBy(orderByFields).cursorScrollInsensitive().maxRows(highIndex).queryIterator();

                    // get the partial list for this page
                    if (limitView) {
                        if (viewProductCategoryId != null) {
                            // do manual checking to filter view allow
                            productCategoryMembers = new LinkedList<GenericValue>();
                            GenericValue nextValue;
                            int chunkSize = 0;
                            listSize = 0;

                            while ((nextValue = pli.next()) != null) {
                                String productId = nextValue.getString("productId");
                                if (CategoryWorker.isProductInCategory(delegator, productId, viewProductCategoryId)) {
                                    if (listSize + 1 >= lowIndex && chunkSize < viewSize) {
                                        productCategoryMembers.add(nextValue);
                                        chunkSize++;
                                    }
                                    listSize++;
                                }
                            }
                        } else {
                            productCategoryMembers = pli.getPartialList(lowIndex, viewSize);
                            listSize = pli.getResultsSizeAfterPartialList();
                        }
                    } else {
                        productCategoryMembers = pli.getCompleteList();
                        if (UtilValidate.isNotEmpty(viewProductCategoryId)) {
                            // filter out the view allow
                            productCategoryMembers = CategoryWorker.filterProductsInCategory(delegator, productCategoryMembers, viewProductCategoryId);
                        }

                        listSize = productCategoryMembers.size();
                        lowIndex = 1;
                        highIndex = listSize;
                    }

                    // filter out of stock products
                    if (filterOutOfStock) {
                        try {
                            productCategoryMembers = ProductWorker.filterOutOfStockProducts(productCategoryMembers, dispatcher, delegator);
                            listSize = productCategoryMembers.size();
                        } catch (GeneralException e) {
                            Debug.logWarning("Problem filtering out of stock products :"+e.getMessage(), module);
                        }
                    }

                    // null safety
                    if (productCategoryMembers == null) {
                        productCategoryMembers = new LinkedList<GenericValue>();
                    }

                    if (highIndex > listSize) {
                        highIndex = listSize;
                    }
                }
            } catch (GenericEntityException e) {
                Debug.logError(e, module);
            }
            finally {
                // close the list iterator, if used
                if (pli != null) {
                    try {
                        pli.close();
                    } catch (GenericEntityException e) {
                        Debug.logError(e, module);
                    }
                }
            }
        }

        Map<String, Object> result = new HashMap<String, Object>();
        result.put("viewIndex", Integer.valueOf(viewIndex));
        result.put("viewSize", Integer.valueOf(viewSize));
        result.put("lowIndex", Integer.valueOf(lowIndex));
        result.put("highIndex", Integer.valueOf(highIndex));
        result.put("listSize", Integer.valueOf(listSize));
        if (productCategory != null) result.put("productCategory", productCategory);
        if (productCategoryMembers != null) result.put("productCategoryMembers", productCategoryMembers);
        return result;
    }

2. 分析源码,找出主要的业务逻辑相关的代码。

分析CategoryDetail.groovy脚本中,andMap对象的包含的参数。
// get the product category & members
andMap = [productCategoryId : productCategoryId,
        viewIndexString : viewIndex,
        viewSizeString : viewSize,
        defaultViewSize : defaultViewSize,
        limitView : limitView]
andMap.put("prodCatalogId", currentCatalogId)
andMap.put("checkViewAllow", true)
// Prevents out of stock product to be displayed on site
productStore = ProductStoreWorker.getProductStore(request)
if (productStore) {
    andMap.put("productStoreId", productStore.productStoreId)
}
if (context.orderByFields) {
    andMap.put("orderByFields", context.orderByFields)
} else {
    andMap.put("orderByFields", ["sequenceNum", "productId"])
}
catResult = runService('getProductCategoryAndLimitedMembers', andMap)

andMap的取值:
{
	"productCategoryId":"PROMOTIONS",
	"viewSizeString":"9",
	"defaultViewSize":10,
	"limitView":true,
	"prodCatalogId":"DemoCatalog",
	"checkViewAllow":true,
	"productStoreId":"9000",
	"orderByFields":["sequenceNum","productId"]
}

查询要展示的产品sql:
SELECT PC.*,pcm.*   
FROM Product_Category pc  
INNER JOIN Product_Category_Member PCM ON pc.product_Category_Id = PCM.product_Category_Id  
WHERE pc.product_Category_Id = 'PROMOTIONS' ORDER BY pcm.sequence_Num,pcm.product_Id DESC  

查询结果有9条记录,刚好对应界面显示的9个产品。
主要重点是参数"productCategoryId":"PROMOTIONS"是怎么传进来的。暂时不管了,知道特殊产品展示就是按上面的查询语句获取要展示的产品的。

3. 总结

为了获取andMap的值,引入了Gson包,方便调试。将gson-2.2.4.jar文件放入lib文件夹下,然后在CategoryDetail.groovy文件添加调试语句:

import com.google.gson.Gson;

gson = new Gson();  
Debug.logInfo(gson.toJson(andMap), "CategoryDetailGroovy");

然后访问主页后(不用重启系统编译。修改文件后就生效了),可以去runtime/logs/ofbiz.log文件中找到对应日志。

查询指定分类下的推广产品分类
SELECT * FROM Prod_Catalog_Category WHERE prod_Catalog_Id = 'DemoCatalog' AND prod_Catalog_Category_Type_Id = 'PCCT_PROMOTIONS'

结果是:
ofbiz学习——推广/宣传产品展示

根据推广分类获取产品列表:
SELECT PC.*,pcm.*   
FROM Product_Category pc  
INNER JOIN Product_Category_Member PCM ON pc.product_Category_Id = PCM.product_Category_Id  
WHERE pc.product_Category_Id = 'PROMOTIONS' ORDER BY pcm.sequence_Num,pcm.product_Id DESC  

根据查询结果的product_id字段可以关联product表获取产品信息。
SELECT p.*
FROM Product_Category pc  
INNER JOIN Product_Category_Member PCM ON pc.product_Category_Id = PCM.product_Category_Id  
INNER JOIN Product P ON p.product_id = PCM.product_id
WHERE pc.product_Category_Id = 'PROMOTIONS' ORDER BY pcm.sequence_Num,pcm.product_Id DESC  

查询结果:
ofbiz学习——推广/宣传产品展示




相关标签: ofbiz 推广产品