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" οnchange="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);" οnclick="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);" οnclick="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" οnchange="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具体实现

根据服务引擎工具找到对应 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'

结果是:


根据推广分类获取产品列表:
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  

查询结果:





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值