奔波儿灞

2009年8月31日星期一

product,content和item

今天想弄清楚产品(product),内容(content)和item(item又分电信定义的item和高通定义的item)之间的关系。到现在对这些还不是很明了,主要是因为没有和实际联系起来,光看代码和配置文件只能了解皮毛。

例如,我知道product下可以包含多个content,每个content也可以包含多个item(电信定义的item),代码也是这样操作解析的,但是这三者到底对应现实中的什么?比如,一款游戏:魂斗罗,它到底算哪样?是product?content?还是item呢,这个到现在还是浆糊。这不弄清楚,对整个代码和系统就不能完全把握。

所以今天从电信的文档和配置文件开始。我先看了productinfo配置文件,和电信文档配合一起看,productinfo配置文件内容很简单,基本上没有什么有效信息,需要关注的只有这几个tag:productID,chargingPolicyID。还有一个serviceID不知道有什么作用。productID是该product的标识,很重要,很多地方都要用到。chargingPolicyID,计费策略,这是电信方面的概念,电信叫charging policy,高通那边叫price plan,本质上是一样的,名称不同而已,当然形式和操作上也有些区别,这个在电信文档的附录“附录 D: BREW 产品计费策略与 ISMP 内部处理关系”有详细的说明。chargingPolicyID标识一个charging policy,也是很重要的,有一个配置文件charginfo.xml,productinfo和charginfo之间要建立联系,就要通过这个 chargingPolicyID。

除了这两个tag,其它的基本上可以忽略。productinfo文件内容相对简单。而上面提到了 charginfo文件,其实charginfo和厘清product,content和item之间关系这个主题的关系不大,这里只是简单介绍一下。产品计费策略信息文件的内容,比如productinfo要稍多,但是值得关注的tag也不多:chargingPolicyID,baseFee,FeeType。chargingPolicyID不用说,FeeType很重要,在代码中就是根据它来解析的。

下面看内容描述文件,这个文件里包含了有关内容的信息。内容描述文件可以包含多个内容,每个内容的tag是“contentInfo”,在每个contentInfo下又包括几个很重要的tag:contentID,itemID,contentURL,modelList和itemBREWProperty,contentID 和itemID不用说,是content和item的唯一标识。从内容描述文件可以看出,一个content是可以包含多个item的。在 contentInfo这个tag下,可以关注一个问题,就是所有与item相关的信息都是放在“Item”这个tag下的,每个contentInfo 可以包含多个Item,标识每个content可以包含多个item,那么content和item到底对应现实中的什么呢?

从电信文档里对Item的解释可以看出端倪,文档里是这么解释的“ 同一content下的不同Item提供针对同一内容的不同终端类型适配的能
力” 从这段解释可以看出:item是针对于不同终端的。而从各自的配置文件内容,也可以印证。首先看productInfo,这个配置文件里面包含了 chargingPolicyID,说明product这个概念,是针对于收费策略的,也可以这样理解,有着相同收费策略的应用,我们都可以把它们看作是同一产品。

而对于content,它比product要小,他和收费策略没有直接关系,可以把它看作某个现实的应用,例如游戏魂斗罗。它可以看作一个content,但是item又是什么,更加电信文档里的解释,item是针对某个或者是某类终端的content,例如魂斗罗是一个 content,但是对于两个手机,姑且称为手机1和手机2,手机1和手机2的硬件不同,例如屏幕大小等等不一样,语言的选择不一样(一个使用英文,一个使用中文)等等不同。对于同一个内容:魂斗罗,不能在两个手机上都适用,所以针对手机1,有一个适用的应用,姑且称为魂斗罗1,这个就是item,而对手机2,同样有个魂斗罗2,这也是个item。所以魂斗罗这个content下面根据不同的手机终端,就有了两个item:魂斗罗1和魂斗罗2。这两个 item是属于魂斗罗这个content的。

在内容描述文件中,itemInfo这个tag下包含的东西值得关注,首先是itemID,这没什么好说的,然后是contentURL,这个很重要,它表示内容实体文件在远程FTP上的存放路径。另一个值得关注的是modelList标签,它表示这个item支持哪几款终端,例如W219,D06,C510,表示这个 item支持的终端有三种:W219,D06和C510。从这里可以看出,item和终端是紧密联系的。

到此,可以这样说,product,content和item,它们的作用范围逐渐减小,逐渐具体。它们的侧重点也是不一样的,product的侧重点是计费,content的侧重点是应用,item的侧重点是适用的终端类型。现举下面的一个例子来说明它们的关系:
有一个product,包括两个内容:魂斗罗和三国志。它们的计费策略都是一样,都是包月10块。如果还有一款应用也使用这个计费策略,也可以加入到这个产品中来。对于魂斗罗这个content,针对iphone,Gphone和Nokia N95,这三款终端,又有了三个item,分别是魂斗罗1,魂斗罗2,魂斗罗3。这三个item分别使用于三款手机,某个item不能被安装到其它两个终端上。

电信的系统是以product为中心的,围绕着product。而高通的系统,没有product,是围绕着item的。对于高通的item,现在还不了解,应该和电信的item有很大的相似处。但是要弄清楚的是,高通的计费策略是不是针对item的,或者说是于计费策略是和 item绑定的。因为电信的计费策略(charge info)是和product绑定的。

2009年8月14日星期五

PostActivation,price handle的获取

对于PostActivation,PostActivationServlet类的doGet方法到没多少内容,它调用PostActivationHandler.fetchItemDetails方法,这个方法返回OPCacheVO对象,查看OPCacheVO的内容,发现有不少东西,productID,contentID,四元组,price handle,还有语言信息,几乎包含了所有必要的信息。随后doGet调用fetchProductIDContentID方法将这些信息都保存到数据库中。

PostActivationServlet的init方法里初始化了两个job:PostActivationJob和NewDistributionJob。

PostActivationJob 是调用PostActivationHandler的invokeProcess方法,这个方法的功能是“This method is used for getting Pricehandle from BrewZone webservice and updating into database table, for generating Feedback file and ftping it to FTPSubsystem.”。貌似这个方法把所有的事情都做了,从BrewZone取得price handle,更新数据库,产生回执文件。

但是粗看了PostActivationHandler的invokeProcess方法,感觉里面用到的很多数据都是已经保存在数据库中了,所以我感觉应该是另一个job,也就是NewDistributionJob执行过后,得到并保存了数据,PostActivationJob才可以顺序地进行,所以决定先看NewDistributionJob。

NewDistributionJob调用了 NewDistributionHandler.invokeProcess方法,这个方法的功能是“This method is used for getting catalog info from BrewZone webservice and updating into database table”,也就是它从BrewZone获得数据并保存到数据库中。invokeProcess调用BREWZoneBase.getInfo方法,它的返回值是InfoVO对象,InfoVO类的成员很简单,值得关注的一个成员是catalogId。得到的InfoVO中的信息又被保存到 CatalogInfoVO对象中。CatalogInfoVO中也有catalogId和其它信息。最后把信息保存到数据库中。

回到 PostActivationHandler.invokeProcess方法,它回频繁地调用getCatalogInfo获得catalog的信息(主要是catalogId),这些信息就是NewDistributionHandler.invokeProcess保存到数据库中的。 invokeProcess调用getPriceHandles获得price handle,但是这个方法没看太懂,它的参数是url和catalogId,很明显,就是到url的地址去获取符合catalogId的price handle。但是细看这个方法的实现,发现好像没做什么,它先是调用brewZoneClient.getTopLevelCategories去获得List,但是奇怪的是虽然调用了getTopLevelCategories,但它的返回值并没有被保存到一个List对象中,也就是说这里调用getTopLevelCategories貌似没起作用。然后有调用OPCacheDAO.opCacheReload,这个方法也奇怪,就在数据库中设置了一下时间戳而已。

随后PostActivationHandler.invokeProcess调用getFeedBackInformation,它返回ProductsVO对象,ProductsVO类有必要关注一下它的成员:
ProductsVO { List product; }

ProductVO { String productId; List content; }

ContentVO { private String contentId; private List ItemDetail; }

ItemDetailVO { private String itemURL; private int opFlag; private String itemId; }

从这些类包含的成员,再对照PriceHandle_2009042900000001.xml文件,就可以发现,这些类成员和pricehandle的xml文件的tag是一一对应的。

当 getFeedBackInformation方法返回ProductsVO对象后,pricehandle的xml文档的内容基本上就确定了。随后 uploadFeedBackFile方法被调用生成feedback file,也就是PriceHandle_2009042900000001.xml文件。

就目前来看,对语言信息来说,虽然语言信息保存到了OPCatchVO中了,但是在代码里貌似并没有使用这个OPCatchVO,而是用ProductsVO来构建了price handle xml文件,xml文件里也没有包含语言信息。

2009年8月13日星期四

CMSEntity之九,提交item的逻辑

接昨日:
获取内容描述文件并对其进行初步分析后,ContentProcessorJob会调用ContentHandler类的方法做进一步处理。方法invokeProcess是入口,invokeProcess首先就调用getSuitableBatch去获得合适的batch,所谓合适的 batch,就是在上篇博客中提到的,处理完所有的item后,要将其状态更新,置为saved,以便于后面的操作。所以 getSuitableBatch就是将这些状态为saved的项取出来。

getSuitableBatch得到的是 List,之后invokeProcess调用commonStorageHandling来“handle the common storage”(不太明白这里的意思),昨天提到,可以通过batchId来得到content的信息,commonStorageHandling调用getContentByBatchID返回List。commonStorageHandling后面的处理让我有些摸不着头脑,它先是检查每个ContentItemVO的ProcStatCodeID,如果是CONTENT_STATUS_SAVED或者CONTENT_STATUS_TIMEDOUT,再去获得ISMPItemVO,也去挨个检查ProcStatCodeID,如果是 ITEM_FETCH_SUCCESS或者ITEM_STATUS_TIMEDOUT,然后到本地目录去获取内容实体zip文件,如果文件不存在,将 ISMPItemVO的ProcStatCodeID改为ITEM_STATUS_PARSED。这一段操作的目的还不清楚。 commonStorageHandling的返回值是boolean型,表示内容实体zip文件在本地目录是否存在。

commonStorageHandling返回到invokeProcess,invokeProcess根据返回值,如果为真,它调用方法processBatchNotify。

processBatchNotify也是首先用getContentByBatchID获得List。然后对每个ContentItemVO调用方法processContent,这个方法非常重要:“Iterate the item list of this content, prepare the MetaFile and generate the zip, Invoke the BMC web service to Submit the item. Use the return brew_item_id to update ContentItemVO object. update the ItemVO's zip file directory and metafile XML file and final zip file result.”。也就是说,这个方法生成了Meta文件,并将Meta文件和内容实体zip文件一起再生成一个zip文件,并将这个zip文件传给 BMC。

processContent是根据OPFLAG进行操作的,有三种OPFLAG:OPFLAG_ADD,OPFLAG_MODIFY和OPFLAG_DELETE。

先看OPFLAG_ADD的情况:
1. 查询数据库,用方法checkContentItemExists,这个方法的功能是“This is to check content is exist with current content id and partNo in BSG”。也就是查找在当前数据库中是否已经存在该内容。注意这里使用的参数是content ID,manufacturer partner no和content no,前面两个很好理解,最后一个content number不知道是什么。根据这三个来判断该内容在当前库中是否存在。如果存在,进入2.x,否则进入3.x。

2.1 调用方法checkItemExistsForTheContent,这个方法的功能是:“This is to check content is exist with current content id and partNo in BSG”。这个解释让人迷惑,为什么又去判断content是否存在?所以我判断这里的注释肯定不对,印度兄弟偷懒了,把 checkContentItemExists的注释又拷贝到这里。按checkItemExistsForTheContent这个方法名来判断,不好说这个方法是什么功能,它的返回值如为true,进入2.2,否则进入2.3。
2.2 用方法getItemVOList去获得List,然后对每个ISMPItemVO调用checkISMPItemExists,这个方法的功能是“This is to verify that Item is already exist or not”。返回true表示content里已经存在该item了,进入 2.2.a,否则进入2.2.b。
2.2.a 调用方法applyPlatformLanguage,这个方法首先调用ChangeListCreator.getUseableChangeList,现在不知道change list是干啥用的,它返回ChangeListVO,如果不为空,则调用 ItemSubmissionHandler.addOprItemPlatform方法,这个方法很长,这里先不细谈,它的功能是把该item的平台信息(支持的手机类型)保存。
2.2.b 调用方法processAsUpgradeItem,将该item作为新的item上传给BMC。这是通过ItemSubmissionHandler.processItem方法实现的。
2.3 用方法getItemVOList去获得List,对每个ISMPItemVO,调用ItemSubmissionHandler.processItem方法将其作为新的item提交到BMC。

3.1 当前的content不存在,首先调用checkContentExists,这个方法的功能是“This method checks whether content exists or not”。这里让人很迷惑,前面不是已经用checkContentItemExists检查过content是否存在了吗,为什么又检查一次?如果返回true,就调用markedForDelete,这个方法“This method is used for delete handling when only contentId exists”。
3.2 用getItemVOList得到List,对每个ISMPItemVO,调用checkItemExists,这个方法又搞不清楚,它的注释是“This is to check the Item exists or not”。迷惑的是:这里说的存在到底是存在于哪里?是存在于BSG的数据库中?还是存在于content中还是哪里?如果存在,进入3.3.a,否则进入3.3.b
3.3.a 用方法setProcStatCodeID将item的状态置为ITEM_STATUS_FAILED,表示item已存在,然后调用updateItemStatusNLoc更新状态。
3.3.b 调用ItemSubmissionHandler.processItem方法将其作为新的item提交到BMC。

OPFLAG_MODIFY的处理:
1. 调用modifyHandling方法,用方法getItemVOList去获得List,然后对每个ISMPItemVO调用checkISMPItemExists,这个方法的功能是“This is to verify that Item is already exist or not”。返回true表示content里已经存在该item了,进入2.1.a,否则进入2.1.b。
2.1.a 用ChangeListCreator.getUseableChangeList()去获取change list,如果成功,用addOprItemPlatform把该item的平台信息(支持的手机类型)保存。
2.2.b 用setProcStatCodeID将item的状态置为ITEM_STATUS_FAILED

OPFLAG_DELETE的处理:
1. 调用modifyHandling方法,用方法getItemVOList去获得List,然后对每个 ISMPItemVO调用checkItemAlreadyExist,“This is to check that item exists or a new item”,也是让人迷惑,搞不懂。

processContent返回到processBatchNotify,processBatchNotify调用setProcStatCodeID设置BatchStatusVO的状态。

2009年8月12日星期三

CMSEntity之八,内容描述文件的获取和初步处理

BSG获取内容描述文件并做初步处理,是在ContentFetcherJob中完成的,真正干活的是类ContentAdaptor,它用 ContentParser去解析内容描述文件,解析后的结果是ContentsDescVO,这个类不知道有什么意义,其实里面就只有 List,ContentVO和内容描述文件里面的tag可以说是一一对应的。然后ContentAdaptor调用 ItemSubmissionConverter.fromXMLVO将前面得到的ContentsDescVO转换为 List。比较令人关注的是在内容描述文件中,“languagesSupported”这个标签的处理,支 持的语言,这个信息的处理流程如下:

ItemBREWPropertyTypeVO.getLanguagesSupported()
ISMPItemLanguagesVO.setIsmpLanguageId()
ISMPItemVO.setItemLanguageVOList()
ContentItemVO.setItemVOList()

语言信息最终保存到了ISMPItemVO中,而ISMPItemVO又被包含在ContentItemVO。至于为什么要做ContentsDescVO到List的转换,目前还不知道动机和目的。

回 到ContentAdaptor,fromXMLVO完后,ContentAdaptor继续调用saveContentItemInfo将 ContentItemVO信息保存到数据库中,这里主要操作的是数据库中的表:ISMP_CONTENT table 和 ISMP_ITEM table。然后ContentAdaptor调用fetchZipFile去获得内容实体问题,在6号的博客里提到了内容实体文件是zip文件,内容实 体文件的地址保存在内容描述文件中。

最后还有个很重要的步骤,ContentAdaptor调用updateBatchStatus去更新前面处理的item的状态,将其置为saved。后面的操作都是基于item和content状态的。

BatchStatusVO 里面的内容不知道有什么作用,但是后面很大地方要用到它,一个比较关键的问题是前面提到的ContentItemVO与BatchStatusVO是如何 建立联系的,在ContentItemVO中有一个成员batchId,这个id在ContentItemVO和BatchStatusVO中都有保存, 所以可以通过这个id找到对方。这个过程可以参看ContentHandler的方法getContentByBatchID。

2009年8月6日星期四

CMSEntity之七,内容的提交

内容上传的流程今天才弄明白点,以前一种存在误解,直到前天和昨天看代码有疑问(为什么在product Info和product content map文件里面都没有具体的content和item信息,因为按以前的理解,在product info文件理应该要包含某个product下有多少content,每个content有包含哪些item这类信息,但是在product Info和product content map文件product Info和product content map文件里没有发现,而这些信息是在ICMS _SYNCYYYYMMDDNNNN.REQ文件里),然后昨天今天看电信文档才发现一直理解错误。

CMS的流程是:

1,ISMP或者NMSC发起contentSyncNotify的webservice给BSG
2,BSG处 理这个soap,获得content description file(内容描述文件),这个文件的文件名为:ICMS _SYNCYYYYMMDDNNNN.REQ,其中YMDN代表年月日时间。这个文件被存放在本地路径:/home/qualcomm/CMS/REQ, 这个信息可用从文件BSGCMSconfig.xml配置文件里查询到。
3,BSG开始处理内容描述文件,描述文件里有些信息很重要,值得注意的有:
operation项,它是一个整型的值,1表示增加,2表示修改,3表示删除。BREW要求这里必须是1,因为谢天曾经提到过,BREW只有添加操作没有别的,但是如果实现删除操作?对于某项内容,把要删除的项剔除,剩下的内容作为新的重新提交,这样实现的删除。
manufacturerPartNumber项,这一项在描述文件里就是用实体文件名的数字作为填充值,但是这个值是不对的,在后面的操作中manufacturerPartNumber要改成ContentID。
4,BSG将描述文件里的内容填写到相应的数据库表中,处理完后,下载内容实体文件,实体文件的下载地址是在描述文件里指定好的,在描述文件的contentURL项中,指定了到何处去取实体文件,实体文件是一个ZIP文件。
5,BSG会生成一个Meta文件(xml)和实体文件一起提交给BMC,Meta文件和实体文件存放在本地目录:/home/qualcomm/item_submit_zip (在BSGCMSconfig.xml配置文件的zipDirBase项)
6, 提交完内容后,BSG产生一个content synchronization feedback(内容同步反馈文件),保存到ISMP的指定FTP目录中。这个反馈文件在本地目录也有保存:/home/qualcomm/CMS /RSP/Archive (在BSGCMSconfig.xml配置文件的localFeedBackArchiveFolder项),到此内容提交结束。
7,BSG去轮询以获得product info,product content map和charging info文件提交产品信息及计价策略文件

2009年7月31日星期五

CMSEntity之六,内容提交和产品定价的规则

两个重要概念:
1. 定价方法(price method):
  • 免费试用(Demo):用户免费使用
  • 购买(Purchase):一次性购买
  • 订购(Subscription):包月

2. 定价基础(price basis):
  • 使用次数(number of uses):应用可使用的最大次数
  • 使用期限(expiration data):到期截止日
  • 使用天数(number of days):可使用的天数
  • 使用时长(elapsed time):应用在终端上可以使用的时长,以分钟为单位

产品以这两个概念为定价的基础,剩下两个辅助概念,一个是price,一个是price value,这些就是四元组。price表示实际的费用,例如某个产品是5元10次,那么price就是5元,而price value就是10。

规则:
  • 三种定价方法相互独立,也就是说给某个内容定价时,可以使用其中的一个,两个,或者三个。
  • 选择免费试用(Demo)方法时
    • 只有三种定价基础可以用,即使用次数,使用天数,使用时长。使用期限不可用
    • 在三种可用的定价基础中,只能选择其中一种,其缺省值为不超过10次,不超过10分钟或不超过一天。
  • 在选择订购(subscription)定价方法时
    • 定价基础是可以忽略的
  • 在选择购买(purchase)定价方法时
    • 四种定价基础(使用次数,使用期限,使用天数,使用时长)全部可用
    • 虽然四种定价基础都可用,但是只能选择其中的一种使用,例如,如果选用使用次数,那就不能再选择使用天数
    • 对 于某个被选择的定价基础,最多可用三个price value(这一块可用参考代码,常量PURCHASE_PRICE_OPTION_SIZE应该就是这么来的),例如:5元包10天,10元包60天, 和20元无限使用的三个产品是允许的。但是,5元包10天,10元包60天,15元包100天和20元无限使用的四个产品是不允许的。
    • 无限使用的购买适用于任意一种定价基础。例如:使用次数(无限次),使用天数(无限天),使用时长(无限时长),使用期限(无限期限)。

合法产品示例:

内容1有4个产品:

* 产品11:免费试用(Demo)1天
* 产品12:2元包月
* 产品13:2元包10天
* 产品14:20元无限使用

内容2有5个产品:

* 产品21:免费试用(Demo)10分钟
* 产品22:3元包月
* 产品23:2元包5次
* 产品24:5元包15次
* 产品25:20元无限使用


非法产品的示例:

内容3有4个产品:

* 产品31:免费试用(Demo)15次
* 产品32:2元包月
* 产品33:2元包10天
* 产品34:20元无限使用

内容4有4个产品:

* 产品41:免费试用(Demo)10分钟
* 产品42:3元包月
* 产品43:2元包5次
* 产品44:5元包10天

内容5有5个产品:

* 产品51:3元包月
* 产品52:2元包1小时
* 产品53:5元包10小时
* 产品54:10元包40小时
* 产品55:20元无限使用

2009年7月30日星期四

CMSEntity之五,price plan的生成

接昨日:
prepareMfgPricePlan用于构建manufacture price plan文件,它会调用createMfgPriceMethod方 法,前面提到了,方法createPriceMethod会创建price method,这些信息会被manufacturer price plan文件和operation price plan文件使用,当createPriceMethod创建好price method后,这些信息保存在ProductPriceMethodVO,查看ProductPriceMethodVO的成员可以,其实里面放的就是四元组:price method,price value,price basis和price。同时createPriceMethod也会调用setPriceMethod将这些信息保存到ProductVO(ProductVO有一个ProductPriceMethodVO型的成员priceMethod)。

再 回到prepareMfgPricePlan,它调用createMfgPriceMethod方法,createMfgPriceMethod其实就是 从ProductVO中得到ProductPriceMethodVO信息,然后将这些信息分别填到MfgPriceMethodTypeVO中去。 prepareMfgPricePlan然后就是一个逻辑:因为调用createMfgPriceMethod后得到一个 MfgPriceMethodTypeVO对象mpmo,然后用这个对象的Method_type作为一个key,去mpmoMap里去查找,看是能查 到,如果能查到,将查到的值赋给prevMpmo。

为了清楚地描述这个逻辑,现在做下面的定义:
当前的MfgPriceMethodTypeVO对象:mpmo
原有的MfgPriceMethodTypeVO对象:prevMpmo(如果原有的对象存在,这是用Method_type作为一个key,去mpmoMap里查找的)
四元组:price method,price,price value,price basis
price mothod可能的值有:Demo,Purchase,Subscription
price valude可能的值有:数值(1,2,3等),UNLIMITED
price basis可能的值有:USES,TIME,DAYS
OPTION:是Manufacturer price plan文件的一个tag,它包含四元组的price和price value两项。

逻辑:
1,如果当前的product value是UNLIMITED,那么将当前的productVO和method type存放到一个map里:productsBasisUnlimited.put(key, pv);

2,判断是否有prevMpmo,如果有,进入步骤3,如果没有,直接到步骤5。

3.a, 当prevMpmo存在时,如果当前的method type是Purchase,将optionSize赋值为3:optionSize = Constants.PURCHASE_PRICE_OPTION_SIZE;(这里需要解释的是为什么常量 PURCHASE_PRICE_OPTION_SIZE为3而不是别的值,今天看到“关于BREW内容提交和产品定价的规则说明”邮件才知道,这是 BREW规定的。“对于任意一个被选中的定价基础,BREW允许最多三个定价基础值”,所以常量PURCHASE_PRICE_OPTION_SIZE的 值为3).

3.b,当prevMpmo存在时,如果当前的method type不是Purchase,将optionSize赋值为1:optionSize = Constants.DEMO_PRICE_OPTION_SIZE(解释见上)。

4,判读mpmo和prevMpmo的price basis是否相等,相等进入4.1,不等进入4.2

4.1.a,获取prevMpmo的OPTION的size,如果大于optionSize,报错

4.1.b, 如果小于optionSize,调用方法checkDuplicatePriceValue判断prevMpmo和mpmo是否相等(也就是判断两者包含 的四元组是否完全相等),如果相等就抛出异常。如果不等,将当前mpmo的OPTINO加入到原有prevMpmo的OPTION 中:prevMpmo.getOPTION().addAll(mpmo.getOPTION());

4.2.a,price basis不相等,调用方法updateBasisUnlimited,这个方法判断prevMpmo和mpmo的price basis是否是UNLIMITED,如果是就将其price basis赋给另一方,如果都不是,返回false。

4.2.b,如果updateBasisUnlimited返回true,将当前mpmo的OPTINO加入到原有prevMpmo的OPTION中:prevMpmo.getOPTION().addAll(mpmo.getOPTION());否则报错。

5,将当前mpmo加入map:mpmoMap.put(key, mpmo);

逻辑完后,prepareMfgPricePlan会调用ProductParserImpl类的方法prepareMfgPricePlanMeta去生成meta文件,到此为止。

prepareMfgPricePlan 执行完毕后,返回到processCntProductAssocList,它会调用submitMfgPricePlan方法,这个方法最终会调用 BMCClientImpl类的createMfrPricePlan,这个方法的功能是:This method will invoke the BMC service and submits the manufacturer price plan。到此为止,manufacturer price plan文件的生成及提交就完成了。

processCntProductAssocList继续调用prepareOprPricePlan来生成operation price plan文件。prepareOprPricePlan调用createOprPriceMethod来 说生成price method,这个方法和createMfgPriceMethod(如前述)的作用是一样的。prepareOprPricePlan后面的工作就很简 单了。这里有个问题,相对于prepareMfgPricePlan,prepareOprPricePlan的逻辑非常简单,根本原因就是 MfgPriceMethodTypeVO和OprPricePlanMethodTypeVO的成员虽然很相似,都是四元组,但是两者有一个很大的区别 是:OprPricePlanMethodTypeVO的PricePointTypeVO变量是一个值,而MfgPriceMethodTypeVO中 是一个PricePointTypeVO的List。这导致了prepareMfgPricePlan比prepareOprPricePlan要简单得 多。