您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
ZCube:在我的优惠券中的落地实践【五】
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
ZCube:在我的优惠券中的落地实践【五】
fo****
2022-08-22
IP归属:北京
18280浏览
# <center> ZCube:在我的优惠券中的落地实践【五】 前言 >ZCube使用交流群:1029344502 >我的优惠券作为营销玩法的一种运营工具,在营销活跃场中起到很至关重要的作用。如何更加高效的赋能业务,助理业务发展,灵活扩展业务,是我们一直追求和思考的方向 # 一、背景 ## 1.1 现状 >营销中台作为券的“供应链端”,控制券的所有类型。 >我的优惠券作为工具,提供用户已有优惠券的展示列表,不同类型的券利益点不同,运营会提供各自展示规则。 >谋略作为用户触达方,为了提高券的核销率,会对用户做过期提醒push,同时触达文案要求跟券的营销文案一致。 ![](https://img13.360buyimg.com/imagetools/jfs/t1/31287/8/17167/1684819/62ff499dE446da889/97c7810dadd87220.png) ## 1.2 挑战点 - 1、营销中台每次新增券类型,都需要运营指定营销文案后,由研发硬编码实现。能不能支持业务运营人员根据需求灵活扩展,动态配置营销文案,并且能够及时生效呢? - 2、消息中心的push提醒文案需要跟营销展示文案一致,那就由业务侧研发硬编码实现一套,消息中心侧实现一套,并且还得保证两处的规则逻辑一致才可以。 能不能将这种相同的规则抽取出来,以订阅的方式下发到订阅者上去,既保证规则的唯一性,也能够做到规则共享? # 二、解决方案 鉴于上述场景的痛点,我们接入ZCube平台来解决 ## 2.1 接入ZCube 官网地址:[https://zcube.jr.jd.com](https://zcube.jr.jd.com/zcube/#/home) 文章介绍:《[ZCube:会员权益体系规则引擎原理介绍 【一】 ](http://xingyun.jd.com/shendeng/article/detail/4501?forumId=79&jdme_router=jdme%3A%2F%2Fweb%2F202206081297%3Furl%3Dhttps%3A%2F%2Fshendengh5.jd.com%2FarticleDetail%3Fid%3D4501)》 ### 首先,在ZCube平台接入我的优惠券应用 ![](https://img13.360buyimg.com/imagetools/jfs/t1/169457/10/30656/381323/62ff499eE67406b77/ae9180ac3782a431.png) ### 搭建优惠券运营玩法规则 ![](https://img13.360buyimg.com/imagetools/jfs/t1/169457/10/30656/381323/62ff499eE67406b77/ae9180ac3782a431.png) ### 发布知识包 ![](https://img12.360buyimg.com/imagetools/jfs/t1/40257/14/18149/126093/62ff499dE16044011/ba2074f4c3664738.png) ## 2.2 应用系统接入SDK ### maven坐标依赖 ```xml <!-- 规则引擎客户端SDK --> <dependency> <groupId>com.jd.jdt.rule.core</groupId> <artifactId>rule-core-client-spring</artifactId> <version>1.0.1-SNAPSHOT</version> </dependency> ``` ### properties参数配置 ```properties rule.env=prod rule.appName=jrm_member_center rule.secret=xxx rule.packages=xxx ``` ### spring xml配置 ```xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd" default-lazy-init="false"> <bean id="ruleExecuteService" class="com.jd.jdt.rule.core.client.service.impl.RuleExecuteServiceImpl"> <property name="knowledgeCacheService" ref="knowledgeCachePushService"/> </bean> <bean id="knowledgeCachePushService" class="com.jd.jdt.rule.core.client.service.impl.KnowledgeCachePushService"> <property name="cloudFileInnerService" ref="cloudFileInnerService"/> </bean> <bean id="cloudFileInnerService" class="com.jd.jdt.rule.core.client.service.impl.CloudFileInnerService"> <constructor-arg index="0" value="#{frozenParamBean['rule.env']}"/> </bean> <bean id="ruleClient" class="com.jd.jdt.rule.core.client.etcd.RuleClient"> <constructor-arg index="0" value="#{frozenParamBean['rule.appName']}"/> <constructor-arg index="1" value="#{frozenParamBean['rule.env']}"/> <constructor-arg index="2" value="#{frozenParamBean['rule.packages']}"/> <constructor-arg index="3" ref="knowledgeCachePushService"/> </bean> </beans> ``` ### api调用 ```java RuleExecutionResult ruleExecutionResult = ruleExecuteService.fireRules(knowPackageName, param); ``` ## 2.3 AB方式灰度上线 为了保证现有功能的稳定,我们采用AB方式灰度上线,配置在白名单内的用户走规则引擎执行的逻辑,否则走原硬编码逻辑。 ![](https://img12.360buyimg.com/imagetools/jfs/t1/183478/10/27831/31456/62ff499cE09b23834/538077f4c3b78093.png) # 三、结果展示 C端页面展示 ![](https://img13.360buyimg.com/imagetools/jfs/t1/38327/13/16852/280352/62ff499eE9810b730/9dc25c2353a1c252.png) SGM执行方法监控,tp99基本在1ms,cpu及内存较稳定,对系统原业务逻辑基本无影响 ![](https://img11.360buyimg.com/imagetools/jfs/t1/67579/11/21026/290350/62ff499dE54a31c21/c25021a0c4f8c3e5.png) ![](https://img14.360buyimg.com/imagetools/jfs/t1/139233/8/29292/274676/62ff4a79E6ed1f9f6/10ae5a67731cd8e5.png) # 四、压测 ## 4.1 压测对象 <table> <tr> <th></th> <th>--</th> <th>描述</th> <th>备注</th> </tr> <tr> <td>1</td> <td>压测案例项目</td> <td>SDK客户端示例项目</td> <td>fin-rule-client-example</td> </tr> <tr> <td>2</td> <td>压测机器</td> <td>用户持有优惠券接口</td> <td> 接口:com.jd.jdt.rule.engine.core.example.facade.IRuleExeFacade 别名:fin-rule-client-example</td> </tr> <tr> <td>3</td> <td>执行知识包01 exePackage01 执行知识包03 exePackage03 执行aviator01 exeRule03</td> <td>SDK客户端示例项目</td> <td>fin-rule-client-example</td> </tr> <tr> <td>4</td> <td>压测机器</td> <td>指定预发机器</td> <td>11.248.242.133 100.99.122.24</td> </tr> </table> ## 4.2 压测平台 [forcebot 压测平台](http://taishan.jd.com/forcebot/task/list) 压测脚本: | | 压测方法 | 压测脚本 | | ---- | ------------ | ------------------------ | | 1 | exePackage01 | [exePackage01.groovy](#) | | 2 | exePackage03 | [exePackage03.groovy](#) | | 3 | exeRule03 | [exeRule03.groovy](#) | ### exePackage01 ```groovy package jsf import com.jd.fastjson.JSON import com.jd.fastjson.JSONObject import com.jd.forcebot.engine.TestUtils import com.jd.forcebot.engine.groovy.Lifecycle import com.jd.forcebot.engine.groovy.RatePolicy import com.jd.forcebot.engine.groovy.TestCase import com.jd.forcebot.engine.groovy.TestSuite import com.jd.forcebot.toolkit.parameterized.latest.AsciiFileAccessArbitrarily import com.jdd.test.performance.common.GenericServiceInvoker import org.slf4j.Logger @TestSuite(value = "forcebot", lifecycle = Lifecycle.THREAD, ratePolicy = RatePolicy.STANDARD) class TestQueryRightsCardDetail { public final Logger logger = TestUtils.LOGGER; public static GenericServiceInvoker genericServiceInvoker; public static AsciiFileAccessArbitrarily realPinFile = new AsciiFileAccessArbitrarily("test_1w_pin.txt"); static { genericServiceInvoker = new GenericServiceInvoker(); genericServiceInvoker.initService("com.jd.jdt.rule.engine.core.example.facade.IRuleExeFacade","fin-rule-client-example"); } String getPin() { return realPinFile.readLine().trim(); } @TestCase( record = false) void queryRightsCardDetail() { // 真实pin String pin = getPin(); logger.info("pin: {}", pin); String tranName = "exePackage01"; JSONObject param = new JSONObject(); param.put("pin", pin); param.put("clientIP", "127.0.0.1"); String strParam = JSON.toJSONString(param); try { TestUtils.transactionBegin(tranName); Object result = genericServiceInvoker.invoke("exePackage01",new String[]{"java.lang.Integer","java.lang.Integer"},new Object[]{45,45}); logger.info("{} result: {}", tranName, result); boolean ret = resultCheck(result, tranName); } catch (Exception ex) { TestUtils.transactionFailure(tranName); logger.error(ex.getMessage(), ex); } } boolean resultCheck(Object result, String tranName) { if (result == null) { logger.error("{} exec error, result is null.", tranName); return false; } if (!(result instanceof Map)) { logger.error("{} exec error, result is {}.", tranName, result); return false; } Map<String, Object> resultMap = result; logger.info(tranName + " result: {}.", resultMap); if (resultMap == null || resultMap.size() == 0) { logger.warn("{} exec error, result map size is zero.", tranName); TestUtils.transactionFailure(tranName); return false; } TestUtils.transactionSuccess(tranName); // logger.info("{} exec succeed, result: {}.", tranName, resultMap); return true; } } ``` ### exePackage03 ```groovy package jsf import com.jd.fastjson.JSON import com.jd.fastjson.JSONObject import com.jd.forcebot.engine.TestUtils import com.jd.forcebot.engine.groovy.Lifecycle import com.jd.forcebot.engine.groovy.RatePolicy import com.jd.forcebot.engine.groovy.TestCase import com.jd.forcebot.engine.groovy.TestSuite import com.jd.forcebot.toolkit.parameterized.latest.AsciiFileAccessArbitrarily import com.jdd.test.performance.common.GenericServiceInvoker import org.slf4j.Logger @TestSuite(value = "forcebot", lifecycle = Lifecycle.THREAD, ratePolicy = RatePolicy.STANDARD) class TestQueryRightsCardDetail { public final Logger logger = TestUtils.LOGGER; public static GenericServiceInvoker genericServiceInvoker; public static AsciiFileAccessArbitrarily realPinFile = new AsciiFileAccessArbitrarily("test_1w_pin.txt"); static { genericServiceInvoker = new GenericServiceInvoker(); genericServiceInvoker.initService("com.jd.jdt.rule.engine.core.example.facade.IRuleExeFacade","fin-rule-client-example"); } String getPin() { return realPinFile.readLine().trim(); } @TestCase( record = false) void queryRightsCardDetail() { // 真实pin String pin = getPin(); logger.info("pin: {}", pin); String tranName = "exePackage03"; JSONObject param = new JSONObject(); param.put("pin", pin); param.put("clientIP", "127.0.0.1"); param.put("orderType", "ssahhh"); param.put("bizCode", "qwerrtta"); String strParam = JSON.toJSONString(param); try { TestUtils.transactionBegin(tranName); Object result = genericServiceInvoker.invoke("exePackage03","com.jd.jdt.rule.engine.core.example.domain.MqVar",strParam); logger.info("{} result: {}", tranName, result); boolean ret = resultCheck(result, tranName); } catch (Exception ex) { TestUtils.transactionFailure(tranName); logger.error(ex.getMessage(), ex); } } boolean resultCheck(Object result, String tranName) { if (result == null) { logger.error("{} exec error, result is null.", tranName); return false; } if (!(result instanceof Map)) { logger.error("{} exec error, result is {}.", tranName, result); return false; } Map<String, Object> resultMap = result; logger.info(tranName + " result: {}.", resultMap); if (resultMap == null || resultMap.size() == 0) { logger.warn("{} exec error, result map size is zero.", tranName); TestUtils.transactionFailure(tranName); return false; } TestUtils.transactionSuccess(tranName); // logger.info("{} exec succeed, result: {}.", tranName, resultMap); return true; } } ``` ### execRule03 ```groovy package jsf import com.jd.fastjson.JSON import com.jd.fastjson.JSONObject import com.jd.forcebot.engine.TestUtils import com.jd.forcebot.engine.groovy.Lifecycle import com.jd.forcebot.engine.groovy.RatePolicy import com.jd.forcebot.engine.groovy.TestCase import com.jd.forcebot.engine.groovy.TestSuite import com.jd.forcebot.toolkit.parameterized.latest.AsciiFileAccessArbitrarily import com.jdd.test.performance.common.GenericServiceInvoker import org.slf4j.Logger @TestSuite(value = "forcebot", lifecycle = Lifecycle.THREAD, ratePolicy = RatePolicy.STANDARD) class TestQueryRightsCardDetail { public final Logger logger = TestUtils.LOGGER; public static GenericServiceInvoker genericServiceInvoker; public static AsciiFileAccessArbitrarily realPinFile = new AsciiFileAccessArbitrarily("test_1w_pin.txt"); static { genericServiceInvoker = new GenericServiceInvoker(); genericServiceInvoker.initService("com.jd.jdt.rule.engine.core.example.facade.IRuleExeFacade","fin-rule-client-example"); } String getPin() { return realPinFile.readLine().trim(); } @TestCase( record = false) void queryRightsCardDetail() { // 真实pin String pin = getPin(); logger.info("pin: {}", pin); String tranName = "execRule03"; JSONObject params = new JSONObject(); params.put("orderType","1"); params.put("orderType","1"); params.put("bizCode",1); params.put("subBizCode",0); params.put("merchantCode","200022"); params.put("subMerchantCode","110597078001"); JSONObject param = new JSONObject(); param.put("ruleId", "bt"); param.put("params", params); String strParam = JSON.toJSONString(param); try { TestUtils.transactionBegin(tranName); Object result = genericServiceInvoker.invoke("execRule03","com.jd.jdt.rule.engine.core.example.domain.RuleRequest",strParam); logger.info("{} result: {}", tranName, result); boolean ret = resultCheck(result, tranName); } catch (Exception ex) { TestUtils.transactionFailure(tranName); logger.error(ex.getMessage(), ex); } } boolean resultCheck(Object result, String tranName) { if (result == null) { logger.error("{} exec error, result is null.", tranName); return false; } if (!(result instanceof Map)) { logger.error("{} exec error, result is {}.", tranName, result); return false; } Map<String, Object> resultMap = result; logger.info(tranName + " result: {}.", resultMap); if (resultMap == null || resultMap.size() == 0) { logger.warn("{} exec error, result map size is zero.", tranName); TestUtils.transactionFailure(tranName); return false; } TestUtils.transactionSuccess(tranName); // logger.info("{} exec succeed, result: {}.", tranName, resultMap); return true; } } ``` 模拟调用:[easyone接口调用](http://jagile.jd.com/easyone/apitest/pjsf/Case-a490d61a-9ae7-4de6-b8df-e1df41202ba8/M139256b8-716f-4199-9905-e71f858ecceb/11) ## 4. 3 压测指标监控 SGM监控: [sgm方法监控](https://sgm-server.jd.com/app-monitor/chart?id=H4sIAAAAAAACA51QwWrDMAz9F502cEw72CG%2B7bBBb6XsVnoQjpK5c5xgK1m2kH%2BfmqSjsMsYGJ703pNkaYTYfIB5UGAbD2aroGAwIyTGyK%2BuJjCh814BheI2jRgqerLseqE2Clxgij36GypSGSm9XZnHSQHyJ5jjCNiCgdKFLHaeMusdBc5owLr1BApSL7JgzQvOVZA6aymli75EB2SCk8yWbkdBu9ppWMvcgl%2FNgsWsT%2Bpv821T63Mhj%2FXFpSlULpC2TZR4seoSLRakdwcxPA%2F0MqfXj9NAe7TvWNFme3fGHrWXo%2BmdXKqiqH4x9z%2BbcnvZkts8XyH%2Fx5qn6RsTBJwr3AEAAA%3D%3D) ## 4. 4 结论 demo | | tps峰值 | tp99 | tp999 | 成功率 | 内存使用率 | cpu使用率 | | ---- | ------- | ---- | ----- | ------ | ---------- | --------- | | 1 | 10000 | 1 | 1 | 100% | 12.6% | 31.6% | | 2 | 19500 | 1 | 1 | 100% | 12.6% | 53.7% | 白条规则 | | tps峰值 | tp99 | tp999 | 成功率 | 内存使用率 | cpu使用率 | | ---- | ------- | ---- | ----- | ------ | ---------- | --------- | | 1 | 2500 | 1 | 1 | 100% | 12.5% | 9.88% | | 2 | 3000 | 1 | 1 | 100% | 12.5% | 11.1% | | 3 | 3500 | 1 | 1 | 100% | 12.6% | 13.0% | | 4 | 4000 | 1 | 1 | 100% | 12.5% | 13.9% | | 5 | 5000 | 1 | 1 | 100% | 12.5% | 17.9% | | 6 | 6300 | 1 | 1 | 100% | 12.5% | 21.6% | | 7 | 8400 | 1 | 1 | 100% | 12.5% | 26.9% | | 8 | 10500 | 1 | 1 | 100% | 12.5% | 33.7% | | 9 | 14500 | 1 | 1 | 100% | 12.5% | 46.1% | | 10 | 19200 | 1 | 1 | 100% | 12.5% | 59.2% | 白条规则 aviator执行 | | tps峰值 | tp99 | tp999 | 成功率 | 内存使用率 | cpu使用率 | | ---- | ------- | ---- | ----- | ------ | ---------- | --------- | | 1 | 6100 | 1 | 1 | 100% | 12.4% | 18.4% | | 2 | 10500 | 1 | 1 | 100% | 12.4% | 29.6% | | 3 | 20000 | 1 | 1 | 100% | 12.5% | 51.9% | | | 压测tps指标 | 执行方式 | SGM监控 | JDOS实例监控 | | ---- | ----------- | ----------- | ------------------------------------------------------------ | ------------------------------------------------------------ | | 1 | 6000 | aviator执行 | ![](https://img11.360buyimg.com/imagetools/jfs/t1/100816/17/29589/254192/630227ebE74195787/dd1533bacb45aa22.png) | ![](https://img13.360buyimg.com/imagetools/jfs/t1/45275/29/20670/354930/630227ebEbecff455/c36029215e6a87ad.png) | | 2 | 6000 | package执行 | ![](https://img14.360buyimg.com/imagetools/jfs/t1/142855/30/28958/261090/630227ecE5c9816a3/8c598d91a4a3a8b1.png) | ![](https://img10.360buyimg.com/imagetools/jfs/t1/151250/3/24829/358993/630227ecEe1ebfa9c/c9c995699d36fece.png) | | 3 | 10000 | aviator执行 | ![](https://img12.360buyimg.com/imagetools/jfs/t1/176485/15/28687/270079/630227ebE2e7415ad/3d6a2f70160ee81c.png) | ![](https://img13.360buyimg.com/imagetools/jfs/t1/96930/35/30832/112064/630227ebE3f664d7d/601474da8eab1c51.png) | | 4 | 10000 | package执行 | ![](https://img11.360buyimg.com/imagetools/jfs/t1/29859/4/18726/265898/630227edEc7a04fa2/e987196c89b9eada.png) | ![](https://img13.360buyimg.com/imagetools/jfs/t1/194200/20/26746/357321/630227ecE6059ce0f/27964263f4b3cd9d.png) | | 5 | 20000 | aviator执行 | ![](https://img13.360buyimg.com/imagetools/jfs/t1/207994/23/25118/260732/630227ecEcf4b0941/eda928d5b9dae763.png) | ![](https://img11.360buyimg.com/imagetools/jfs/t1/176408/3/29034/366030/630227ecEa9844d02/c0430750e78bbe91.png) | | 6 | 20000 | package执行 | ![](https://img11.360buyimg.com/imagetools/jfs/t1/174205/5/29145/249435/630227edE17ca037e/f616fcd077c78273.png) | ![](https://img13.360buyimg.com/imagetools/jfs/t1/25101/1/18019/111720/630227edE67b84dd2/86e3479b1cdcd8d5.png) | 温馨提示:内部文章,禁止转发置外网 [ZCube:会员权益体系规则引擎原理介绍——向导式建模引擎 【三】](http://xingyun.jd.com/shendeng/article/detail/4503?forumId=79&jdme_router=jdme%3A%2F%2Fweb%2F202206081297%3Furl%3Dhttps%3A%2F%2Fshendengh5.jd.com%2FarticleDetail%3Fid%3D4503) [ZCube:会员权益体系规则引擎原理介绍 【一】 ](http://xingyun.jd.com/shendeng/article/detail/4501?forumId=79&jdme_router=jdme%3A%2F%2Fweb%2F202206081297%3Furl%3Dhttps%3A%2F%2Fshendengh5.jd.com%2FarticleDetail%3Fid%3D4501) [ZCube:会员权益体系规则引擎原理介绍——脚本式建模引擎 【二】](http://xingyun.jd.com/shendeng/article/detail/4502?forumId=79&jdme_router=jdme%3A%2F%2Fweb%2F202206081297%3Furl%3Dhttps%3A%2F%2Fshendengh5.jd.com%2FarticleDetail%3Fid%3D4502) [ZCube:会员权益体系规则引擎原理介绍——执核心执行引擎 【四】](http://xingyun.jd.com/shendeng/article/detail/4511?forumId=79&jdme_router=jdme%3A%2F%2Fweb%2F202206081297%3Furl%3Dhttps%3A%2F%2Fshendengh5.jd.com%2FarticleDetail%3Fid%3D4511) [ZCube:在我的优惠券中的落地实践【五】](http://xingyun.jd.com/shendeng/article/detail/4512?forumId=79&jdme_router=jdme%3A%2F%2Fweb%2F202206081297%3Furl%3Dhttps%3A%2F%2Fshendengh5.jd.com%2FarticleDetail%3Fid%3D4512) [※※※重要:ZCube:使用介绍【六】](http://xingyun.jd.com/shendeng/article/detail/4789?forumId=79&jdme_router=jdme%3A%2F%2Fweb%2F202206081297%3Furl%3Dhttps%3A%2F%2Fshendengh5.jd.com%2FarticleDetail%3Fid%3D4789) [ZCube:会员权益体系规则引擎升级2.0啦~~~【七】](http://xingyun.jd.com/shendeng/article/detail/9562?forumId=79&jdme_router=jdme%3A%2F%2Fweb%2F202206081297%3Furl%3Dhttps%3A%2F%2Fshendengh5.jd.com%2FarticleDetail%3Fid%3D9562) [ZCube2.0:在金融会员权益积分核心场景的落地实践【八】](http://xingyun.jd.com/shendeng/article/detail/9563?forumId=79&jdme_router=jdme%3A%2F%2Fweb%2F202206081297%3Furl%3Dhttps%3A%2F%2Fshendengh5.jd.com%2FarticleDetail%3Fid%3D9563)
上一篇:从APP基础库或SDK升级探索如何做好回归测试
下一篇:认知篇(三):CQRS架构模式的本质
fo****
文章数
3
阅读量
1428
作者其他文章
01
※※※重磅消息:ZCube会员权益体系规则引擎升级2.0啦~~~【七】
一、ZCube2.0增加了哪些功能?1.1架构升级 ZCube1.0自上线以来已有半年多时间,在此期间,我们助力了会员权益系统在发放用户规模上的增长,在多个核心业务场景贡献了引擎的价值。经历了一次618、一次双11,期间性能表现稳定。为了继续深耕权益运营场景,助力业务利益最大化,我们对ZCube1.0做了整体架构升级,在了解升级前,先来看一下ZCube1.0的架构模型、服务组成。首
01
ZCube2.0:在金融会员权益积分核心场景的落地实践【八】
接上一篇[※※※重磅消息:ZCube会员权益体系规则引擎升级2.0啦~~~【七】2.1业务背景 会员权益积分系统最重要的核心业务指标是App内积分渗透率,App内积分渗透率=(积分发放用户规模+积分核销用户规模) / 活跃用户数。 目前积分发放场景包括:白条交易返积分、小金库交易返积分、银行卡交易返积分等。积分核销场景包括:信用卡免息还款券、电商黄金流程积分渗透、白
01
ZCube:在我的优惠券中的落地实践【五】
ZCube:在我的优惠券中的落地实践【五】前言ZCube使用交流群:1029344502我的优惠券作为营销玩法的一种运营工具,在营销活跃场中起到很至关重要的作用。如何更加高效的赋能业务,助理业务发展,灵活扩展业务,是我们一直追求和思考的方向一、背景1.1 现状营销中台作为券的“供应链端”,控制券的所有类型。我的优惠券作为工具,提供用户已有优惠券的展示列表,不同类型的券利益点不同,运营会提供各自展示
fo****
文章数
3
阅读量
1428
作者其他文章
01
※※※重磅消息:ZCube会员权益体系规则引擎升级2.0啦~~~【七】
01
ZCube2.0:在金融会员权益积分核心场景的落地实践【八】
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号