可乐
【技术方案】促销配置新增校验&核心库新增流水
一、需求相关概要
1.1 需求背景
新促销管理后台缺少很多核心参数的校验,且对于存储的数据没有任何的流水表
https://doc.weixin.qq.com/sheet/e3_AIsAFga8APY1hD1S2o7TY05aozgBM?scode=AAYA0wfQAAwmkMMK8mAIsAFga8APY&tab=BB08J2
1.2 需求详情
tapd:促销配置新增校验&核心库新增流水-科技零售-TAPD平台
二. 整体方案设计
代码分支:rel_eventCheckImp_1316824
修改工程:ec_event_admin
2.1 促销管理端,对不同的促销类型的核心参数进行强校验,且支持拓展
1.前端改造:促销不同流程的网关均需传入【event_type】
2.后端逻辑:如下图
3.促销后端校验整理
https://doc.weixin.qq.com/sheet/e3_AIsAFga8APY1hD1S2o7TY05aozgBM?scode=AAYA0wfQAAwfMjmJqXAdQAJgZuAGg&tab=0whtpq
重点校验逻辑:
1.互斥优惠
4.Hippo配置:
- 校验开关:ec_event_admin.check_switch
- 规则表达式列表:ec_event_admin.rule.expression.validation.list
注:新增aviator自定义函数:【CheckIn】、【NotNullOrEmpty】、【DateBefore】、【CheckDateSubset】
完整的规则列表:
[
{
"condition": "!CheckIn(eventType,36,37)",
"rule": "NotNullOrEmpty(scopeType) && CheckIn(scopeType,1,2,4)",
"desc": "促销类型非单品-普通或单品-秒杀时,促销范围不能为空且只能选【自营促销】、【平台促销】、【POP促销】之一"
},
{
"condition": "CheckIn(eventType,36,37)",
"rule": "NotNullOrEmpty(scopeType) && CheckIn(scopeType,1,2)",
"desc": "促销类型为【单品-普通、单品-秒杀】时,促销范围不能为空且只能选【自营促销】、【平台促销】之一"
},
{
"condition": "!CheckIn(eventType, 36, 37)",
"rule": "NotNullOrEmpty(eventBeginTime , eventEndTime) && DateBefore(eventBeginTime ,eventEndTime)",
"desc": "促销类型非【单品-普通、单品-秒杀】时,促销开始时间、结束时间不能为空且结束时间必须大于开始时间"
},
{
"condition": "!CheckIn(eventType, 36, 37)",
"rule": "NotNullOrEmpty(eventBeginTime , eventEndTime) && DateBefore(eventBeginTime ,eventEndTime)",
"desc": "促销类型非【单品-普通、单品-秒杀】时,促销开始时间、结束时间、提报开始时间、结束时间均不能为空且提报结束时间必须大于开始时间,同时提报时间必须是促销时间的子集"
},
{
"condition": "1 == 1",
"rule": "NotNullOrEmpty(adminPromotion)",
"desc": "负责人不能为空"
},
{
"condition": "1 == 1",
"rule": "NotNullOrEmpty(productReviewer)",
"desc": "商品审核人不能为空"
},
{
"condition": "!CheckIn(eventType, 36, 37)",
"rule": "NotNullOrEmpty(distributeType) && CheckIn(distributeType ,1, 2)",
"desc": "促销类型非【单品-普通、单品-秒杀】时,生效逻辑不能为空且只能选【建立SKU和活动生效关系】、【建立类目和活动生效关系】之一"
},
{
"condition": "!CheckIn(eventType, 31, 51)",
"rule": "NotNullOrEmpty(lossFusingAmount) && lossFusingAmount >= 100 && lossFusingAmount%100 == 0",
"desc": "促销类型非【氛围促销、直播】时,亏损熔断金额不能为空且必须是正整数"
},
{
"condition": "CheckIn(eventType,42, 43,33,48,49)",
"rule": "NotNullOrEmpty(useWay) && CheckIn(useWay,1,2,3,4)",
"desc": "用途不能为空且只能选【兑换、售卖、回收宝、内部采购】之一"
},
{
"condition": "CheckIn(eventType,42, 43,33,48,49)",
"rule": "NotNullOrEmpty(convertType) && CheckIn(convertType,1, 2)",
"desc": "兑换方式不能为空且只能选【兑换一次、兑换多次】之一"
},
{
"condition": "CheckIn(eventType,42, 43,33,48,49)",
"rule": "NotNullOrEmpty(isShow) && CheckIn(isShow,1, 2)",
"desc": "商详页是否展示不能为空且只能选【是、否】之一"
}
]
2.2 对促销的配置库表写入流水表,记录每一次的数据变更情况和操作人
2.2.1 【活动管理】
活动信息表:【OPDB】op_db.t_ec_event_activity
氛围表:【OPDB】ec_subtitle_db.t_event
审批记录表:已切至线下买鸭审批
活动管理部分,活动信息表、氛围表需要新建流水表
2.2.2 【促销管理】
促销基础信息表:【OPDB】op_db.t_ec_event_base
选品表:【ECDB】product_db.t_search_selection (玄武配置流水表)
氛围表:【OPDB】ec_subtitle_db.t_event
审批记录表:已切至线下买鸭审批
促销管理部分,促销基础信息表、氛围表需要新建流水表
2.2.3 【提报管理】
选品提报记录表:【ECDB】product_db.t_selection_submit_apply (玄武配置流水表)
选品商品表:【EccaidanDB】t_selection_apply_sku=selection_db (玄武配置流水表)
审批记录表:已切至线下买鸭审批
提报部分,已全部建立流水表,本次无需新建
结论:【活动管理】、【促销管理】在玄武配置MySQL监听,以完成流水表的同步
活动信息流水表
CREATE TABLE op_db.t_ec_event_activity_water (
`Fwater_id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘自增主键’,
`Factivity_id` varchar(64) NOT NULL DEFAULT ” COMMENT ‘活动id’, `Factivity_name` varchar(256) NOT NULL DEFAULT ” COMMENT ‘活动名称’, `Factivity_grade` varchar(4) NOT NULL DEFAULT ” COMMENT ‘活动级别 S A+ A B’, `Factivity_begin_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动开始时间’, `Factivity_end_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动结束时间’, `Factivity_status` int(11) NOT NULL DEFAULT ’10’ COMMENT ‘活动状态 10:未开始 20:进行中 30:已结束’, `Fowner` varchar(560) NOT NULL DEFAULT ” COMMENT ‘创建者’, `Foperator` varchar(560) NOT NULL DEFAULT ” COMMENT ‘管理员’, `Ftest_flag` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘测试标识’, `Fcreate_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘创建时间’, `Fmodify_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘修改时间’, `Fversion` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘版本号’, `Fedit_status` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘编辑状态 10:编辑中 20:审批中 30:编辑完成’, `Fhold_ext` text COMMENT ‘暂存信息’,
PRIMARY KEY (`Fwater_id`) USING BTREE,
KEY `idx_activity_id` (`Factivity_id`) USING BTREE,
KEY `idx_modify_time` (`Fmodify_time`) USING BTREE )
ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’电商|促销系统-活动流水表|breakyang|20231221′;
促销基础信息流水表
CREATE TABLE op_db.t_ec_event_base_water (
`Fwater_id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘自增主键’,
`Fbase_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘活动ID号’, `Fevent_name` varchar(255) NOT NULL DEFAULT ” COMMENT ‘活动名称’, `Fevent_type` tinyint(4) NOT NULL DEFAULT ‘0’ COMMENT ‘促销活动类型’, `Fevent_desc` varchar(256) NOT NULL DEFAULT ” COMMENT ‘活动补充说明’, `Fevent_status` tinyint(4) NOT NULL DEFAULT ’10’ COMMENT ‘活动状态’, `Fcreator` varchar(32) NOT NULL DEFAULT ” COMMENT ‘活动创建人’, `Fchannel_type` varchar(20) NOT NULL DEFAULT ‘[0]’ COMMENT ‘推广平台(0-APP 1-H5 2-PC)’, `Fscope_type` tinyint(4) NOT NULL DEFAULT ‘0’ COMMENT ‘活动范围(1-自营 2-POP 3-店铺)’, `Fsale_type` tinyint(4) NOT NULL DEFAULT ‘0’ COMMENT ‘所属业务(0\\1-全部 2-实物 3-虚拟)’, `Fevent_begin_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动开始时间’, `Fevent_end_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动结束时间’, `Fevent_apply_begin_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动报名开始时间’, `Fevent_apply_end_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动报名结束时间’, `Fcreate_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动创建时间’, `Fmodify_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动修改时间’, `Fadmin_promotion` varchar(512) NOT NULL DEFAULT ” COMMENT ‘活动管理权限人员’, `Fadmin_goods` varchar(512) NOT NULL DEFAULT ” COMMENT ‘活动商品管理人员’, `Frule_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘规则id号’, `Fjoin_type` tinyint(4) NOT NULL DEFAULT ‘0’ COMMENT ‘参与方式(1-按商品范围 2-指定)’, `Fevent_base_detail` text COMMENT ‘活动详细json串’, `Fevent_external_channel` tinyint(4) NOT NULL DEFAULT ‘0’ COMMENT ‘活动所属渠道’, `Fversion` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘版本号’, `Fshare_desc` varchar(256) NOT NULL DEFAULT ” COMMENT ‘分享描述’, `Fshare_title` varchar(128) NOT NULL DEFAULT ” COMMENT ‘分享标题’, `Fshare_link` varchar(256) NOT NULL DEFAULT ” COMMENT ‘分享链接’, `Fpay_way` tinyint(4) NOT NULL DEFAULT ‘0’ COMMENT ‘支付方式 1.乐卡支付 2.信用卡支付 3.微信支付’, `Fpay_channel` varchar(256) NOT NULL DEFAULT ” COMMENT ‘支付渠道’, `Fshare_pic_url` varchar(256) NOT NULL DEFAULT ” COMMENT ‘分享图片’, `Flimit_buy_single` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘单人限购字段,0标识不限购’, `Fevent_sub_type` tinyint(4) NOT NULL DEFAULT ‘0’ COMMENT ‘活动子类型,部分类型的活动有自己的类型定义’, `Frelevancy_business` varchar(32) NOT NULL DEFAULT ‘[]’ COMMENT ‘业务归属(基本都有)’, `Fuser_type` varchar(32) NOT NULL DEFAULT ‘[]’ COMMENT ‘促销用户类型(费率促销,支付立减,单品促销等)’, `Forder_type` bigint(20) NOT NULL DEFAULT ‘0’ COMMENT ‘活动支持的订单类型’, `Fmutex_event_type` varchar(1024) NOT NULL DEFAULT ” COMMENT ‘互斥活动,以集合形式存储’, `Fcacheable` tinyint(1) NOT NULL DEFAULT ‘0’ COMMENT ‘是否需要建缓存’, `Fec_channel` varchar(64) NOT NULL DEFAULT ‘[”FQL”]’ COMMENT ‘活动促销渠道’, `Fmerch_name` varchar(128) NOT NULL DEFAULT ” COMMENT ‘商户名称’, `Flimit_buy_whole` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘单人限购数量,0:标识不限购’, `Fapplicable` tinyint(1) NOT NULL DEFAULT ‘0’ COMMENT ‘是否可以报名, 0:不可报名,1:可报名’, `Fplat` varchar(32) NOT NULL DEFAULT ‘FQL’ COMMENT ‘app平台’, `Ffull_event` tinyint(1) NOT NULL DEFAULT ‘0’ COMMENT ‘是否是全场活动 0:不是,1:是’, `Fpay_way_list` varchar(64) NOT NULL DEFAULT ‘[]’ COMMENT ‘多个支付方式’, `Floss_fusing_amount` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘亏损熔断金额(风控使用)(单位:分)’, `Fuser_config` text COMMENT ‘用户差异化促销配置(用户标签、性别、授信状态等)(新)’, `Fclose_order_time` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘关单时间(单位:分)’, `Fapproval_type` tinyint(4) NOT NULL DEFAULT ‘0’ COMMENT ‘审核方式:0人工 1自动’, `Fdept_id` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘创建人部门’, `Ffq_num_list` varchar(64) NOT NULL DEFAULT ‘[]’ COMMENT ‘分期数限制’, `Fenable_early_repay` int(2) NOT NULL DEFAULT ‘1’ COMMENT ‘是否支持提前还款’, `Fnum_not_early_repay` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘前N期不支持提前还款’, `Fcoupon_type` int(4) NOT NULL DEFAULT ‘0’ COMMENT ‘优惠属性’, `Fsku_sale_type_list` varchar(64) NOT NULL DEFAULT ‘[]’ COMMENT ‘商品销售类型 0-普通 1-买鸭1.0 2-买鸭2.0’, `Fext` text COMMENT ‘活动扩展字段’, `Fapproval_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘审批流id’, `Fedit_status` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘编辑状态 10:编辑中 20:审批中 30:编辑完成’, `Fsystem_version` varchar(11) NOT NULL DEFAULT ‘V1’ COMMENT ‘系统版本 老系统 V1 新系统 V2’, `FField_domain_flag` int(1) NOT NULL DEFAULT ‘0’ COMMENT ‘是否接入场域 0:否 1:是’, `Factivity_id` varchar(64) NOT NULL DEFAULT ” COMMENT ‘活动id’, `Frule_update_flag` int(1) NOT NULL DEFAULT ‘0’ COMMENT ‘规则修改记录 0:没有 1:有’, `Fhold_ext` text COMMENT ‘暂存信息’, `Ftest_flag` int(1) NOT NULL DEFAULT ‘0’ COMMENT ‘测试促销标识 0:否 1:是’, `Fuse_way` int(2) NOT NULL DEFAULT ‘2’ COMMENT ‘优惠券用途(1:兑换 2:售卖 3:回收宝 4:内部采购)’, `Fconvert_type` int(2) NOT NULL DEFAULT ‘1’ COMMENT ‘兑换方式(1:兑换多次;2:兑换一次)’, `Fis_show` int(2) NOT NULL DEFAULT ‘1’ COMMENT ‘商详页面是否展示(1:不展示;2:展示)’, `Fis_remind` int(2) NOT NULL DEFAULT ‘1’ COMMENT ‘是否推送过期提醒(1:不推送;2:推送)’, `Fcalculate_type` int(2) NOT NULL DEFAULT ’10’ COMMENT ‘计费类型 10:促销维度 20:批次维度 30:商品维度’, `Fsettle_type` int(2) NOT NULL DEFAULT ’10’ COMMENT ‘结算类型 10:活动 20:批次’, `Fproduct_reviewer` varchar(1024) NOT NULL DEFAULT ” COMMENT ‘商品审核人’, `Fparent_base_id` varchar(64) NOT NULL DEFAULT ” COMMENT ‘父促销’, `Fdistribute_type` int(2) NOT NULL DEFAULT ‘1’ COMMENT ‘打散类型 1:按商品 2:按类目’, `Fsku_limit_buy_single` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘sku单人限购数量,0:标识不限购’, `Fstrategy_config` text COMMENT ‘策略配置信息’,
PRIMARY KEY (`Fwater_id`) USING BTREE,
KEY `idx_base_id` (`Fbase_id`) USING BTREE,
KEY `idx_Fmodify_time` (`Fmodify_time`) USING BTREE)
ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’促销引擎|促销基础信息流水表|breakyang|20231221′;
氛围流水表
CREATE TABLE ec_subtitle_db.t_event_water (
`Fwater_id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘自增主键’,
`Fbase_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘活动id’,
`Fname` varchar(32) NOT NULL DEFAULT ” COMMENT ‘配置名称’,
`Frule_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘规则id’,
`Fbegin_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动开始时间’,
`Fend_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘活动结束时间’,
`Fscope_type` tinyint(4) NOT NULL DEFAULT ‘1’ COMMENT ‘活动范围(1-自营 2-POP 3-店铺)’,
`Fgoods_type` tinyint(3) NOT NULL DEFAULT ‘0’ COMMENT ‘制定商品方式 1、按SPU指定 2、按SKU指定’,
`Fjoin_type` tinyint(3) NOT NULL DEFAULT ‘0’ COMMENT ‘参与方式(1、按商品范围参加 2、指定商品参加)’,
`Fbrand_desc` varchar(40) NOT NULL DEFAULT ‘ ‘ COMMENT ‘文字链’,
`Fshort_title` varchar(28) NOT NULL DEFAULT ‘ ‘ COMMENT ‘短标题’,
`Fsubtitle` varchar(110) NOT NULL DEFAULT ‘ ‘ COMMENT ‘副标题’,
`Flink_h5` varchar(100) NOT NULL DEFAULT ” COMMENT ‘h5链接’,
`Flink_pc` varchar(100) NOT NULL DEFAULT ” COMMENT ‘pc链接’,
`Flist_mul_pic_url` varchar(256) NOT NULL DEFAULT ‘[]’ COMMENT ‘列表页多列氛围图(支持多个,json存储)’,
`Flist_single_pic_url` varchar(256) NOT NULL DEFAULT ‘[]’ COMMENT ‘列表页单列氛围图(支持多个,json存储)’,
`Fdetail_pic_url` varchar(256) NOT NULL DEFAULT ‘[]’ COMMENT ‘商详页氛围图(支持多个,json存储)’,
`Fadmin` varchar(512) NOT NULL DEFAULT ” COMMENT ‘活动权限管理人员’,
`Fstatus` tinyint(3) NOT NULL DEFAULT ‘0’ COMMENT ‘状态(10:正常;11:暂停)’,
`Foperator` varchar(32) NOT NULL DEFAULT ” COMMENT ‘活动创建人’,
`Fmodify_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘修改时间’,
`Fcreate_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘创建时间’,
`Fversion` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘版本号’,
`Fsubtitle_type` tinyint(3) NOT NULL DEFAULT ‘1’ COMMENT ‘ 副标题类型 (1、图文样式 2、大促样式)’,
`Fextend_info` text COMMENT ‘活动个性化字段(json串)’,
`Fuser_portrait` varchar(256) NOT NULL DEFAULT ” COMMENT ‘用户画像信息’,
`Fevent_type` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘配置级别1:活动2:促销’,
`Flogic_id` varchar(64) NOT NULL DEFAULT ” COMMENT ‘活动或促销ID’,
PRIMARY KEY (`Fwater_id`) USING BTREE,
KEY `idx_base_id` (`Fbase_id`),
KEY `idx_modify_time` (`Fmodify_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’促销引擎|氛围流水表|breakyang|20231221′;
一、背景
【tapd】:https://www.tapd.cn/20060141/prong/stories/view/1120060141001316092
1. 往期直播有收到用户在直播间评论区反馈想定向听某些商品讲解,评论区无法实时采集用户意愿数据,只能事后分析用于改善下一场直播,无法用于优化当前直播间讲解策略,需有功能承载该场景诉求;
2. 直播间商品初始排序无法人为干预管控,直播开播前运营需手动调整直播间商品展示排序,目前调整排序交互是拖动,效率比较低,严重影响操作效率,需优化;
二、需求拆分
工程:afterpay_offline_marking_server
分支:rel_liveAsk_1316092
2.1 直播间商品求讲解功能
2.1.1 用户端
实现解析
模块 | 设计 | 内容 | 备注 |
用户点击商品求讲解 | 网关 | 新增【商品求讲解】功能接口网关:/route0002/ecLive/incrProductAskCount.json入参:liveId + skuId + 登录态 | |
用户明细记录 | 异步将明细写入Kakfa,上报给大数据 | |
统计计数 | Redis实现Key: AskCountNum : liveId + skuIdValue: askCount有效期:7s(正常情况5S就会删除一次) | |
数据落库 | 延时队列每5s将Redis中的数据同步到数据表(直播商品属性统计表) | t_live_sku_attribute_statis_record |
2.1.2 管理端
实现解析
模块 | 设计 | 内容 | 备注 |
直播选品统计页展示 | 网关 | 新增【查询直播选品统计详情】功能接口网关:oa/mix/live/queryLiveSelectStatis.json入参:liveId + skuId + 登录态 | |
查询选品信息(已有) | rc_oa_gateway/mix/selection/selectGoodsAdminQuery.json | |
查询已讲解次数 + 求讲解人数 | 查询统计表 | t_live_sku_attribute_statis_record |
商品求讲解趋势图 | 接入大数据实现 | 通过大数据侧明细实现 | |
直播选品讲解/取消讲解 | 更新库表【讲解中的商品】 | 更新直播间管理表 | t_live_event_record |
更新【已讲解次数】、【求讲解次数】 | 更新统计表 | t_live_sku_attribute_statis_record |
更新缓存【讲解中商品】、【求讲解次数】 | Key见上述缓存设计 | |
2.1.3 缓存设计
Key: AskCountNum_liveId + skuId
Value: askCount
2.1.4 延时队列设计
Topic: LIVE_SKU_ASK_COUNT_STATIS_TOPIC
Group: LIVE_SKU_ASK_COUNT_STATIS_TOPIC_GROUP
2.1.5 库表设计
CREATE TABLE `t_live_sku_attribute_statis_record` (
`Fid` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘自增主键’,
`Flive_activity_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘直播间ID’,
`Fsku_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘商品ID’,
`Fextend_info` varchar(128) NOT NULL DEFAULT ” COMMENT ‘扩展信息’,
`Fversion` int(11) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘版本号’,
`Fcreate_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘创建时间’,
`Fmodify_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘修改时间’,
`Fask_count` int(20) NOT NULL DEFAULT ‘0’ COMMENT ‘求讲解人数’,
`Fexplained_count` int(20) NOT NULL DEFAULT ‘0’ COMMENT ‘已讲解次数’,
`Fjg_auto_test_id` varchar(256) NOT NULL DEFAULT ”,
PRIMARY KEY (`Fid`) USING BTREE,
UNIQUE KEY `idx_unique_live_sku` (`Flive_activity_id`,`Fsku_id`),
KEY `idx_modify_time` (`Fmodify_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’直播|直播商品属性统计表|breakyang|20240116′;
CREATE TABLE ec_live_db.t_live_sku_use_record(
`Fid` int(20) NOT NULL AUTO_INCREMENT COMMENT ‘自增主键’,
`Flive_activity_id` varchar(32) NOT NULL DEFAULT ‘0’ COMMENT ‘直播ID’,
`Fsku_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘商品ID’,
`Fuid` int(20) NOT NULL DEFAULT ‘0’ COMMENT ‘用户Uid’,
`Fextend` varchar(256) NOT NULL DEFAULT ” COMMENT ‘扩展信息’,
`Fmodify_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘修改时间’,
`Fcreate_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘创建时间’,
`Fversion` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘版本号’,
PRIMARY KEY (`Fid`),
KEY `idx_Fcreate_time` (`Fcreate_time`)
KEY `idx_sku_live_activity` (`Fsku_id`、`Flive_activity_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’直播|直播商品属性统计流水表
|breakyang|20240116′;
2.1.5 接口文档
轮询讲解中商品接口:
接口名称 | 直播间ID获取直播间信息接口 |
gateway | /route0002/ecLive/queryLiveInfo.json |
接口 | |
方法 | queryLiveInfo |
group | * |
版本号 | 1.0.0 |
超时时间 | 3000ms |
是否登录 | 是 |
入参 | 字段名 | 字段类型 | 是否必传 | 描述 |
| live_activity_id | String | 是 | 直播间ID |
出参 | | | | |
| | | | |
result | int | 是 | 0-成功 |
res_info | String | 是 | ok |
result_rows | | 是 | |
| live_activity_id | String | 是 | 直播间ID |
| live_activity_name | String | 是 | 直播间名称 |
| event_base_id | String | 是 | 促销ID |
| select_id | String | 是 | 选品池ID |
| live_state | int | 是 | 1-未开播,2-直播中,3-已结束 |
| explaining_sku_info | String | 是 | 讲解中的商品信息 |
| update_time | String | 是 | 直播间商品列表更新时间 |
| couponActivityEventInfo | JSONObject | 否 | 直播活动信息 |
| | | | |
直播间商品接口:(无改动)
https://m.fenqile.com/route0002/galaxy/recommendSku.json
新增【商品求讲解】功能接口
接口名称 | 商品求讲解接口 |
gateway | /route0002/ecLive/incrProductAskCount.json |
接口 | com.fenqile.afterpay.offline.marketing.cgi.live.CgiLiveSkuAskService |
方法 | increaseProductAskCount |
group | * |
版本号 | 1.0.0 |
超时时间 | 3000ms |
是否登录 | 是 |
入参 | 字段名 | 字段类型 | 是否必传 | 描述 |
| live_activity_id | String | 是 | 直播间ID |
sku_id | String | 是 | 商品ID |
登录态 | | | |
出参 | 无 | | | |
新增【查询直播选品统计详情】接口
接口名称 | 查询直播选品统计详情功能接口 |
gateway | rc_oa_gateway/mix/live/queryLiveSelectStatis.json |
接口 | com.fenqile.afterpay.offline.marketing.cgi.live.CgiLiveEventRecordQueryService |
方法 | queryLiveSelectorStatis |
group | * |
版本号 | 1.0.0 |
超时时间 | 3000ms |
是否登录 | 是 |
入参 | 字段名 | 字段类型 | 是否必传 | 描述 |
| 登录态 | | | |
live_activity_id | String | 是 | 直播间ID |
select_id | String | 是 | 选品ID |
query_scene | Integer | 是 | 查询场景2 – smart选品明细 |
出参 | 字段名 | 字段类型 | | |
| sort | int | | 序号 |
sku_id | String | | skuId |
sku_pic | List<String> | | 商品图片 |
mart_amount | String | | 商品市场价 |
stock | String | | 可销售库存 |
is_product_up_down | String | | 状态 |
is_product_up_down_desc | String | | 状态描述 |
short_product_name | | | |
ask_count | int | | 求讲解人数 |
explained_count | int | | 已讲解次数 |
{
// 已讲解次数
“explained_count”: 10,
// 求讲解次数
“ask_count”: 52,
“mart_amount”: “10”,
“category_concat_name”: “测试类目-测试类目-测试类目01”,
“category_id_2”: 2380,
“category_id_1”: 80,
“order_cnt_30_new”: 0,
“sku_pic”: [
“https://coss-ec.fenqile.com/ecproduct201/M00/ex/20230920135058-fcd2ee07-c50b-456d-aab9-572fcded1a26.png”
],
“operate_list”: [
1,
2
],
“self_pop”: 2,
“category_name_2”: “测试类目”,
“category_name_1”: “测试类目”,
“product_id”: “P202309207375402”,
“stock”: 999999,
“category_id_3”: 2134,
“is_product_up_down”: 0,
“amount”: “9”,
“discounted_amount”: 0.0,
“self_pop_desc”: “POP”,
“brand_name”: “阿玛尼(ARMANI)”,
“sku_id”: “MES202309207855022”,
“category_name_3”: “测试类目01”,
“sku_key_1”: “3”,
“sort”: 1,
“product_name”: “单品促销测试0”,
“sku_key_concat_name”: “3,d,a”,
“brand_id”: 53,
“extend”: {},
“merch_name”: “测试一号店铺”,
“is_product_up_down_desc”: “下架”,
“sku_key_2”: “d”,
“sku_key_3”: “a”,
“merch_id”: “MC201601210278193”
}
改动【商品讲解/取消讲解】接口
接口名称 | 商品讲解/取消讲解接口 |
gateway | oa/mix/live/explaining_goods.json |
接口 | com.fenqile.afterpay.offline.marketing.oa.cgi.live.CgiLiveEventRecordApplyService |
方法 | explainGoods |
group | * |
版本号 | 1.0.0 |
超时时间 | 3000ms |
是否登录 | 是 |
入参 | 字段名 | 字段类型 | 是否必传 | 描述 |
| 登录态 | | | |
live_activity_id | String | 是 | 直播间ID |
explain_action | Integer | 是 | 1 – 讲解2 – 取消讲解 |
explaining_sku_info | String | 是 | 操作的商品 |
出参 | 字段名 | 字段类型 | | |
- DBA
- DBA
- 【Redis】
DBA : 乐信Redis开发规范
Created by lanceli(李志财), last modified on 四月 08, 2021
简介: 本文介绍了在使用乐信Redis的开发规范,从键值设计、命令使用、客户端使用、相关工具等方面进行说明,
通过本文的介绍可以减少使用Redis过程带来的问题。
乐信Redis规范
简介
Redis 是基于单线程模型实现的,也就是 Redis 是使用一个线程来处理所有的客户端请求的,尽管 Redis 使用了非阻塞式 IO,并且对各种命令都做了优化(大部分命令操作时间复杂度都是 O(1)),但由于 Redis 是单线程执行的特点,因此它对性能的要求更加苛刻,再快的系统,也经不住疯狂的滥用,本文介绍了在使用乐信Redis的开发规范,从键值设计、命令使用、优化建议、运营平台等方面进行说明,通过本文的介绍可以减少使用Redis过程带来的问题。
一、键值设计
1. key名设计
(1)【建议】: 可读性和可管理性
命令规范:以英文冒号分隔key,前缀概念的范围的返回从大到小,从不变到可变,从变化幅度小到变化幅度大。业务名:key用途:变量
例如:yoga:user:1,表示 yoga:user:{userID},即瑜伽子系统ID=1的用户信息
(2)【建议】:简洁性
保证语义的前提下,控制key的长度,不超64个字符。
当key较多时,内存占用也不容忽视,例如:
user:friends:messages:{mid} 简化为 u:fr:msg:{mid}
(3)【强制】:不要包含特殊字符
反例:包含空格、换行、单双引号以及其他转义字符
Redis 的 Key 一定要规范,这样在遇到问题时,能够进行方便的定位。Redis 属于无 scheme 的 KV 数据库,所以,我们靠约定来建立其 scheme 语义。其好处:
1、能够根据某类 key 进行数据清理
2、能够根据某类 key 进行数据更新
3、能够方面了解到某类 key 的归属方和应用场景
4、为统一化、平台化做准备,减少技术变更
2. value设计
(1)【强制】:拒绝bigkey
字符串类型:它的big体现在单个value值很大,一般认为超过10KB就是bigkey
非字符串类型:哈希、列表、集合、有序集合,它们的big体现在元素个数太多,元素个数不要超过5000
bigkey的危害
1、Redis集群的内存空间不均匀 由于Redis单线程的特性
2、操作bigkey的通常比较耗时,也就意味着阻塞Redis可能性越大
3、bigkey也就意味着每次获取要产生的网络流量较大,容易打满带宽
4、过期删除的时候会阻塞Redis
以下是压测结果,可直观看出bigkey对性能的影响:
(2)【推荐】:选择适合的数据类型
例如:实体类型(要合理控制和使用数据结构内存编码优化配置,例如ziplist,但也要注意节省内存和性能之间的平衡)
反例:
set user:1:name tom
set user:1:age 19
set user:1:favor football
正例:
hmset user:1 name tom age 19 favor football
3.【强制】:控制key的生命周期,redis不是垃圾桶。
如果应用将Redis定位为缓存Cache使用,对于存放的Key一定要设置超时时间!因为若不设置,这些Key会一直占用内存不释放,造成极大的浪费,而且随着时间的推移会导致内存占用越来越大,直到达到服务器内存上限!另外Key的超时长短要根据业务综合评估,而不是越长越好!(某些业务要求key长期有效。可以在每次写入时,都设置超时时间,让超时时间顺延。)
二、Redis命令使用规范
1、【强制】严禁不设置范围的批量操作
- 例如hgetall、lrange、smembers、zrange、sinter等并非不能使用,但是需要明确N的值,有遍历的需求可以使用hscan、sscan、zscan代替。
- zrange、 zrangebyscore等多个操作 zset 的函数,严禁使用 zrange myzset 0 -1 等这种不设置范围的操作。请指定范围,如 zrange myzset 0 100,如不确定长度,可使用 zcard 判断长度。
- hgetall会取出相关 hash 的所有数据,如果数据条数过大,同样会引起阻塞,请确保业务可控。如不确定长度,可使用 hlen 先判断长度。
- 严禁使用 sunion, sinter, sdiff等一些聚合操作。
2、【强制】禁用 select 函数
select函数用来切换 database,对于使用方来说,这是很容易发生问题的地方,cluster 模式也不支持多个 database,且没有任何收益,dba已通过配置禁用。
3、【强制】禁用命令
禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理。dba已通过配置禁用这些命令。
4、【强制】不推荐使用事务
Redis的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上。
三、使用优化建议
1、缩短键值对的存储长度
键值对的长度是和性能成反比的,比如我们来做一组写入数据的性能测试,执行结果如下:
从以上数据可以看出,在 key 不变的情况下,value 值越大操作效率越慢,因为 Redis 对于同一种数据类型会使用不同的内部编码进行存储,比如字符串的内部编码就有三种:int(整数编码)、raw(优化内存分配的字符串编码)、embstr(动态字符串编码),这是因为 Redis 的作者是想通过不同编码实现效率和空间的平衡,然而数据量越大使用的内部编码就越复杂,而越是复杂的内部编码存储的性能就越低。
2、冷热数据分离
不要将所有数据全部都放到Redis中,建议根据业务只将高频热数据存储到Redis中【QPS大于5000】,重要性级别较低的但却需求容量较大的场景可申请完全兼容Redis操作的Pika,对于低频冷数据可以使用MySQL/ElasticSearch等基于磁盘的存储方式。
3、业务数据分离
不要将不相关的数据业务都放到一个 Redis中。一方面避免业务相互影响,另一方面避免单实例膨胀,并能在故障时降低影响面,快速恢复。
4、大文本数据要压缩
对于大文本【超过500 byte】写入到Redis时,一定要压缩后存储!大文本数据存入Redis,除了带来极大的内存占用外,在访问量高时,很容易就会将网卡流量占满,进而造成整个服务器上的所有服务不可用,并引发雪崩效应,造成各个系统瘫痪。
5、使用连接池
使用带有连接池的客户端,可以有效控制连接,同时提高效率
6、缓存 Key 设置失效时间的建议
如果在大型系统中有大量缓存在同一时间同时过期,那么会导致 Redis 循环多次持续扫描删除过期字典,直到过期字典中过期键值被删除的比较稀疏为止,而在整个执行过程会导致 Redis 的读写出现明显的卡顿,卡顿的另一种原因是内存管理器需要频繁回收内存页,因此也会消耗一定的 CPU。
为了避免这种卡顿现象的产生,我们需要预防大量的缓存在同一时刻一起过期,就简单的解决方案就是在过期时间的基础上添加一个指定范围的随机数。
7、限制Redis 分片大小
乐信标准,Redis都有分片,最少一个分片,单分片的大小是4G ,为何不建议一直往上加内存?
- 单台服务器内存资源有限,不利于扩展
- 单个分片内存过大,服务器出故障时,影响面大
- Redis持久化时,虽然操作是异步化,但会有fork进程的操作,这一步是由主进程来完成的,分片内存越大,页表就越大,fork执行时间就越长,就会给主线程带来阻塞风险
- 不利于平台标准化,后期迁移困难
- Redis是单进程模型,只能利用一个CPU核心,多分片有利于提高Redis集群能力,充分利用多核性能。
8、认真对待最大内存淘汰策略
根据自身业务类型选择好最大内存淘汰策略,可保证有用数据不被删除,也可避免Redis实例出现OOM,让Redis持续高效。
- noeviction:不淘汰任何数据,当内存不足时,新增操作会报错,Redis 默认内存淘汰策略
- allkeys-lru:淘汰整个键值中最久未使用的键值
- allkeys-random:随机淘汰任意键值
- volatile-lru:淘汰所有设置了过期时间的键值中最久未使用的键值(乐信默认)
- volatile-random:随机淘汰设置了过期时间的任意键值
- volatile-ttl:优先淘汰更早过期的键值
在 Redis 4.0 版本中又新增了 2 种淘汰策略:
- volatile-lfu:淘汰所有设置了过期时间的键值中,最少使用的键值
- allkeys-lfu:淘汰整个键值中最少使用的键值
四、运营平台
了解Redis这么多使用规范和优化建议,日常中如何去查看自己应用中使用的Redis实例是否合理呢,通过宙斯Redis健康分析系统(http://zeus.oa.fenqile.com/Redis/redis/show_redis/)即可快速查看
主要功能如下:
1、查找内存占用最高的key
2、查找bigkey
3、查找未设置过期时间的key
Document generated by Confluence on 四月 07, 2023 17:43
补充:Redis的bigKey的危害
Redis中的bigKey(大键值)指的是那些包含大量元素或占用大量内存空间的键。以下是bigKey可能带来的几个显著的危害:
- 内存瓶颈:
- 单个bigKey可能占用大量内存,导致Redis实例内存迅速增长,接近或超过最大内存限制(maxmemory)设置,如果没有适当的内存淘汰策略,可能导致其他重要数据无法缓存,甚至发生内存溢出(OOM),从而致使Redis服务不稳定或崩溃。
- 性能瓶颈:
- Redis基于单线程模型运行,对bigKey的操作(如读取、修改或删除)会消耗较长时间,这可能导致Redis服务器暂时停止处理其他客户端请求,因为Redis必须先完成当前操作才能继续处理队列中的下一个命令。这种情况下,bigKey操作很容易引起阻塞,增加客户端请求的延迟,产生所谓的“慢查询”。
- 阻塞和客户端超时:
- 处理bigKey时,Redis可能长时间处于阻塞状态,客户端在此期间可能因等待响应超时而终止连接,严重影响服务可用性。
- 网络拥塞:
- bigKey的传输可能会导致网络带宽占用过大。例如,当bigKey的内容被客户端频繁地获取时,网络流量剧增,特别是在低带宽环境下,可能导致网络拥堵,降低整体系统的性能。
- 集群不平衡:
- 在Redis集群环境中,bigKey可能导致集群中某个节点的内存使用率远高于其他节点,破坏数据分片的内存资源均衡,进一步影响整个集群的稳定性与性能。
- 扩容困难:
- 当需要迁移bigKey时,由于其大小和复杂性,迁移操作可能会非常耗时,给在线扩容或数据迁移带来挑战。
因此,针对bigKey问题,通常建议采取以下措施:
- 设计合理的数据结构和编码方式,避免单个键存储过多数据。
- 定期检查和监控Redis中的大键值,及时优化或合理拆分bigKey。
- 使用合适的内存淘汰策略,防止bigKey影响其他重要数据的缓存。
- 对于删除操作,尽量使用异步删除命令(如
unlink
)减少阻塞时间。
- 在集群环境中,合理分配数据,避免bigKey集中在单个节点上。
起因:有一个功能,需要调用pop-order的PRC接口,我在Dubbo中进行了声明
<!-- pop订单 -->
<dubbo:reference id="popOrderService"
interface="com.fenqile.pop.order.service.PopOrderService"
timeout="3000" protocol="fsof" group="default" version="1.0.0" check="false"/>
一般的接口都是这样声明就可以了
出参定义如下:
@Setter
@Getter
@NoArgsConstructor
@ToString
public class PopOrderDetailResp {
private Integer uid;
@JSONField(name = "order_id")
private String orderId;
@JSONField(name = "create_time")
private String createTime;
@JSONField(name = "modify_time")
private Date modifyTime;
@JSONField(name = "merch_sale_state")
private Integer merchSaleState;
@JSONField(name = "merch_sale_state_desc")
private String merchSaleStateDesc;
@JSONField(name = "sale_type")
private Integer saleType;
@JSONField(name = "sale_type_desc")
private String saleTypeDesc;
@JSONField(name = "sku_id")
private String skuId;
@JSONField(name = "product_name")
private String productName;
@JSONField(name = "sku_key_1")
private String skuKey1;
@JSONField(name = "sku_key_2")
private String skuKey2;
@JSONField(name = "sku_key_3")
private String skuKey3;
@JSONField(name = "delivery_mode")
private Integer deliveryMode;
@JSONField(name = "delivery_mode_desc")
private String deliveryModeDesc;
@JSONField(name = "spec")
private String spec;
@JSONField(name = "pic")
private String pic;
@JSONField(name = "quantity")
private Integer quantity = 1;
@JSONField(name = "amount")
private String amount;
@JSONField(name = "used_discount")
private Integer usedDiscount;
@JSONField(name = "price")
private String price;
@JSONField(name = "discount_info")
private List<String> discountInfo;
@JSONField(name = "memo_flag")
private Integer memoFlag;
@JSONField(name = "channel_desc")
private String channelDesc;
@JSONField(name = "company_name")
private String companyName = "";
@JSONField(name = "express_id")
private String expressId = "";
@JSONField(name = "express_flag")
private Integer expressFlag = 0;
@JSONField(name = "invoice_id")
private String invoiceId = "";
@JSONField(name = "receipt_mobile")
private String receiptMobile = "";
@JSONField(name = "receiver_credit_id")
private String receiverCreditId = "";
@JSONField(name = "group_flag")
private Integer groupFlag = 0;
@JSONField(name = "apply_flag")
private Integer applyFlag = 0;
@JSONField(name = "pre_sell_flag")
private Integer preSellFlag = 0;
@JSONField(name = "pre_sell_desc")
private String preSellDesc;
@JSONField(name = "pre_sell_amount")
private String preSellAmount;
@JSONField(name = "pre_sell_state_desc")
private String preSellStateDesc;
@JSONField(name = "final_pay_desc")
private String finalPayDesc;
@JSONField(name = "final_pay_amount")
private String finalPayAmount;
@JSONField(name = "final_pay_state_desc")
private String finalPayStateDesc;
@JSONField(name = "imei")
private String imei;
@JSONField(name = "port")
private String port;
@JSONField(name = "port_name")
private String portName;
@JSONField(name = "taxes")
private String taxes;
@JSONField(name = "refund_taxes_flag")
private Integer refundTaxesFlag = 0;
@JSONField(name = "shop_coupon")
private String shopCoupon;
@JSONField(name = "plat_coupon")
private String platCoupon;
@JSONField(name = "refund_flag")
private List<Integer> refundFlag = new ArrayList<>();
@JSONField(name = "freight")
private String freight;
@JSONField(name = "lease_term")
private String leaseTerm = "";
@JSONField(name = "lease_period")
private String leasePeriod = "";
@JSONField(name = "lease_price")
private String leasePrice = "";
@JSONField(name = "guarantee_service")
private String guaranteeService = "";
@JSONField(name = "associated_order")
private String associatedOrder = "";
@JSONField(name = "deposit_turnover")
private List<DeposoitTurnOver> depositTurnover = new ArrayList<>();
@JSONField(name = "pay_back_amount")
private String payBackAmount = "";
@JSONField(name = "deduction_amount")
private String deductionAmount = "";
@JSONField(name = "deduction_reason")
private String deductionReason = "";
@JSONField(name = "pay_time")
private String payTime;
@JSONField(name = "associated_amount")
private String associatedAmount;
@JSONField(name = "check_flag")
private Integer checkFlag = 0;
@JSONField(name = "checkPassFlag")
private Integer checkPassFlag = 0;
//买断金额
@JSONField(name = "buy_out_amout")
private String buyOutAmount;
//押金
@JSONField(name = "deposite_amount")
private String depositeAmount;
//用户授信免押额度
@JSONField(name = "shop_credit_amount")
private String shopCreditAmout;
//首付金额
@JSONField(name = "after_pay_flag")
private Integer afterPayFlag = 0;
@JSONField(name = "first_pay")
private String firstPay = "";
@JSONField(name = "first_pay_way")
private String firstPayWay = "";
@JSONField(name = "pay_way")
private String payWay = "";
@JSONField(name = "month_pay")
private String monthPay = "";
@JSONField(name = "goods_business_type")
private Integer goodsBusinessType;
@JSONField(name = "goods_business_type_desc")
private String goodsBusinessTypeDesc;
@Setter
@Getter
@ToString
public static class DeposoitTurnOver {
private String time;
private String remark;
private String amount;
@JSONField(name = "associated_amount")
private String associatedAmount;
}
}
然后我调用RPC接口拿到的参数为:
[{"after_pay_flag":0,"amount":"0.90","apply_flag":0,"associated_order":"","checkPassFlag":0,"check_flag":0,"company_name":"","deduction_amount":"","deduction_reason":"","deposit_turnover":[],"express_flag":0,"express_id":"","first_pay":"","first_pay_way":"","freight":"0","group_flag":0,"guarantee_service":"","invoice_id":"","lease_period":"","lease_price":"","lease_term":"","month_pay":"","pay_back_amount":"","pay_way":"","pic":"https://coss-ec.fenqile.com/ecproduct201/M00/ex/20231030142247-ee1e3b73-100f-483a-ba7b-2c14b6d7fedb.png","pre_sell_flag":0,"price":"0.99","quantity":1,"receipt_mobile":"","receiver_credit_id":"","refund_flag":[],"refund_taxes_flag":0,"spec":"满折互斥平台券","taxes":"0","uid":4063946}
仔细观察可以发现,除了有默认值的参数,和无需下划线的参数都能拿到,其他的参数都拿不到!
刚开始定位方向错了,以为是包版本没有对应上,更新包版本后还是不行(耽误了大半天)
后面,我注意到关键信息【除了有默认值的参数,和无需下划线的参数都能拿到,其他的参数都拿不到!】
于是我重新看了下dubbo的RPC接口配置
在提供方是这样写的
<!--提供的service-->
<dubbo:service interface="com.fenqile.pop.order.service.PopOrderService"
ref="popOrderService" protocol="fsof" group="default" version="1.0.0" timeout="8000"
openfastjsonsupport="true">
<dubbo:method name="shipX" timeout="8000"/>
</dubbo:service>
注意,这里提供方用了openfastjsonsupport="true"!
然后我问了下Ai,这个参数是干什么的
得到如下回复:
在Apache Dubbo中,OpenFastJsonSupport并非Dubbo官方提供的组件,但在实际开发中,有可能是指开发者基于开源JSON处理库Fastjson定制的一个适配器或扩展,用于整合Dubbo与Fastjson。
Fastjson是由阿里巴巴公司开发的一款高性能的JSON处理器,用于Java对象与JSON之间的转换。在Dubbo框架中,序列化和反序列化是通信过程中的重要环节,一般会使用某种序列化工具将Java对象转换成可在网络上传输的格式,如JSON格式。
OpenFastJsonSupport可能是一个扩展点,它使Dubbo能够使用Fastjson作为其默认的序列化方案,替代或补充Dubbo自带的序列化方式(如Hessian2、Java原生序列化等)。通过集成Fastjson,Dubbo服务之间交互的数据可以高效地进行JSON格式的序列化和反序列化,从而提升服务调用的性能。具体实现上,它可能提供了一些注册Fastjson为Dubbo的序列化器和反序列化器的配置方法或类。
就是这里,使用OpenFastJsonSupport 指定了该接口的出参使用Fastjson作为其默认的序列化方案,和出参里面使用 @JSONField注解呼应上了,到这里 答案呼之欲出了!
解密:由于服务提供方指定了该接口的出参序列化方式为Fastjson,而我在配置调用方的RPC时,没有使用openfastjsonsupport="true",导致调用方拿到出参后,使用dubbo默认的序列化方式进行反序列化,而Dubbo默认的序列化方式是Hessian2,Hessian2本身并不直接支持Fastjson的@JSONField注解,因为这两个是不同的序列化框架。
Hessian2有自己的序列化规则,并且不会解析或识别Fastjson的注解。
然而,在Dubbo中,可以通过配置将序列化方式改为Fastjson,这样在序列化和反序列化时,Fastjson就会生效,并且支持@JSONField注解。当你将Dubbo的序列化方式配置为fastjson时,Fastjson将会被用来处理对象与JSON之间的转换,此时@JSONField注解的功能才会得到体现。
默认的Hessian2不能处理@JSONField注解,所以拿不到参数信息!那怎样才能拿到呢?很简单,调用方和提供方保持一致的序列化框架就可以了
于是在dubbo配置中写上:
<!-- pop订单 -->
<dubbo:reference id="popOrderService"
interface="com.fenqile.pop.order.service.PopOrderService"
timeout="3000" protocol="fsof" group="default" version="1.0.0" check="false" openfastjsonsupport="true"/>
OK,这样就可以正常拿到了~
一、背景
【tapd】:https://www.tapd.cn/20060141/prong/stories/view/1120060141001316092
1. 往期直播有收到用户在直播间评论区反馈想定向听某些商品讲解,评论区无法实时采集用户意愿数据,只能事后分析用于改善下一场直播,无法用于优化当前直播间讲解策略,需有功能承载该场景诉求;
2. 直播间商品初始排序无法人为干预管控,直播开播前运营需手动调整直播间商品展示排序,目前调整排序交互是拖动,效率比较低,严重影响操作效率,需优化;
二、需求拆分
工程:afterpay_offline_marking_server
分支:rel_liveAsk_1316092
2.1 直播间商品求讲解功能
2.1.1 用户端
实现解析
模块 | 设计 | 内容 | 备注 |
用户点击商品求讲解 | 网关 | 新增【商品求讲解】功能接口网关:/route0002/ecLive/incrProductAskCount.json入参:liveId + skuId + 登录态 | |
用户明细记录 | 异步将明细写入Kakfa,上报给大数据 | |
统计计数 | Redis实现Key: AskCountNum : liveId + skuIdValue: askCount有效期:7s(正常情况5S就会删除一次) | |
数据落库 | 延时队列每5s将Redis中的数据同步到数据表(直播商品属性统计表) | t_live_sku_attribute_statis_record |
2.1.2 管理端
实现解析
模块 | 设计 | 内容 | 备注 |
直播选品统计页展示 | 网关 | 新增【查询直播选品统计详情】功能接口网关:oa/mix/live/queryLiveSelectStatis.json入参:liveId + skuId + 登录态 | |
查询选品信息(已有) | rc_oa_gateway/mix/selection/selectGoodsAdminQuery.json | |
查询已讲解次数 + 求讲解人数 | 查询统计表 | t_live_sku_attribute_statis_record |
商品求讲解趋势图 | 接入大数据实现 | 通过大数据侧明细实现 | |
直播选品讲解/取消讲解 | 更新库表【讲解中的商品】 | 更新直播间管理表 | t_live_event_record |
更新【已讲解次数】、【求讲解次数】 | 更新统计表 | t_live_sku_attribute_statis_record |
更新缓存【讲解中商品】、【求讲解次数】 | Key见上述缓存设计 | |
2.1.3 缓存设计
Key: AskCountNum_liveId + skuId
Value: askCount
2.1.4 延时队列设计
Topic: LIVE_SKU_ASK_COUNT_STATIS_TOPIC
Group: LIVE_SKU_ASK_COUNT_STATIS_TOPIC_GROUP
2.1.5 库表设计
CREATE TABLE `t_live_sku_attribute_statis_record` (
`Fid` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘自增主键’,
`Flive_activity_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘直播间ID’,
`Fsku_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘商品ID’,
`Fextend_info` varchar(128) NOT NULL DEFAULT ” COMMENT ‘扩展信息’,
`Fversion` int(11) unsigned NOT NULL DEFAULT ‘0’ COMMENT ‘版本号’,
`Fcreate_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘创建时间’,
`Fmodify_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘修改时间’,
`Fask_count` int(20) NOT NULL DEFAULT ‘0’ COMMENT ‘求讲解人数’,
`Fexplained_count` int(20) NOT NULL DEFAULT ‘0’ COMMENT ‘已讲解次数’,
`Fjg_auto_test_id` varchar(256) NOT NULL DEFAULT ”,
PRIMARY KEY (`Fid`) USING BTREE,
UNIQUE KEY `idx_unique_live_sku` (`Flive_activity_id`,`Fsku_id`),
KEY `idx_modify_time` (`Fmodify_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’直播|直播商品属性统计表|breakyang|20240116′;
CREATE TABLE ec_live_db.t_live_sku_use_record(
`Fid` int(20) NOT NULL AUTO_INCREMENT COMMENT ‘自增主键’,
`Flive_activity_id` varchar(32) NOT NULL DEFAULT ‘0’ COMMENT ‘直播ID’,
`Fsku_id` varchar(32) NOT NULL DEFAULT ” COMMENT ‘商品ID’,
`Fuid` int(20) NOT NULL DEFAULT ‘0’ COMMENT ‘用户Uid’,
`Fextend` varchar(256) NOT NULL DEFAULT ” COMMENT ‘扩展信息’,
`Fmodify_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘修改时间’,
`Fcreate_time` datetime NOT NULL DEFAULT ‘1970-01-01 00:00:00’ COMMENT ‘创建时间’,
`Fversion` int(11) NOT NULL DEFAULT ‘0’ COMMENT ‘版本号’,
PRIMARY KEY (`Fid`) USING BTREE,
UNIQUE KEY `idx_unique_live_sku` (`Flive_activity_id`,`Fsku_id`),
KEY `idx_modify_time` (`Fmodify_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT=’直播|直播商品属性统计流水表
|breakyang|20240116′;
2.1.5 接口文档
轮询讲解中商品接口:
接口名称 | 直播间ID获取直播间信息接口 |
gateway | /route0002/ecLive/queryLiveInfo.json |
接口 | |
方法 | queryLiveInfo |
group | * |
版本号 | 1.0.0 |
超时时间 | 3000ms |
是否登录 | 是 |
入参 | 字段名 | 字段类型 | 是否必传 | 描述 |
| live_activity_id | String | 是 | 直播间ID |
出参 | | | | |
| | | | |
result | int | 是 | 0-成功 |
res_info | String | 是 | ok |
result_rows | | 是 | |
| live_activity_id | String | 是 | 直播间ID |
| live_activity_name | String | 是 | 直播间名称 |
| event_base_id | String | 是 | 促销ID |
| select_id | String | 是 | 选品池ID |
| live_state | int | 是 | 1-未开播,2-直播中,3-已结束 |
| explaining_sku_info | String | 是 | 讲解中的商品信息 |
| update_time | String | 是 | 直播间商品列表更新时间 |
| couponActivityEventInfo | JSONObject | 否 | 直播活动信息 |
| | | | |
直播间商品接口:(无改动)
https://m.fenqile.com/route0002/galaxy/recommendSku.json
新增【商品求讲解】功能接口
接口名称 | 商品求讲解接口 |
gateway | /route0002/ecLive/incrProductAskCount.json |
接口 | com.fenqile.afterpay.offline.marketing.cgi.live.CgiLiveSkuAskService |
方法 | increaseProductAskCount |
group | * |
版本号 | 1.0.0 |
超时时间 | 3000ms |
是否登录 | 是 |
入参 | 字段名 | 字段类型 | 是否必传 | 描述 |
| live_activity_id | String | 是 | 直播间ID |
sku_id | String | 是 | 商品ID |
登录态 | | | |
出参 | 无 | | | |
新增【查询直播选品统计详情】接口
接口名称 | 查询直播选品统计详情功能接口 |
gateway | rc_oa_gateway/mix/live/queryLiveSelectStatis.json |
接口 | com.fenqile.afterpay.offline.marketing.cgi.live.CgiLiveEventRecordQueryService |
方法 | queryLiveSelectorStatis |
group | * |
版本号 | 1.0.0 |
超时时间 | 3000ms |
是否登录 | 是 |
入参 | 字段名 | 字段类型 | 是否必传 | 描述 |
| 登录态 | | | |
live_activity_id | String | 是 | 直播间ID |
select_id | String | 是 | 选品ID |
query_scene | Integer | 是 | 查询场景2 – smart选品明细 |
出参 | 字段名 | 字段类型 | | |
| sort | int | | 序号 |
sku_id | String | | skuId |
sku_pic | List<String> | | 商品图片 |
mart_amount | String | | 商品市场价 |
stock | String | | 可销售库存 |
is_product_up_down | String | | 状态 |
is_product_up_down_desc | String | | 状态描述 |
short_product_name | | | |
ask_count | int | | 求讲解人数 |
explained_count | int | | 已讲解次数 |
{
// 已讲解次数
“explained_count”: 10,
// 求讲解次数
“ask_count”: 52,
“mart_amount”: “10”,
“category_concat_name”: “测试类目-测试类目-测试类目01”,
“category_id_2”: 2380,
“category_id_1”: 80,
“order_cnt_30_new”: 0,
“sku_pic”: [
“https://coss-ec.fenqile.com/ecproduct201/M00/ex/20230920135058-fcd2ee07-c50b-456d-aab9-572fcded1a26.png”
],
“operate_list”: [
1,
2
],
“self_pop”: 2,
“category_name_2”: “测试类目”,
“category_name_1”: “测试类目”,
“product_id”: “P202309207375402”,
“stock”: 999999,
“category_id_3”: 2134,
“is_product_up_down”: 0,
“amount”: “9”,
“discounted_amount”: 0.0,
“self_pop_desc”: “POP”,
“brand_name”: “阿玛尼(ARMANI)”,
“sku_id”: “MES202309207855022”,
“category_name_3”: “测试类目01”,
“sku_key_1”: “3”,
“sort”: 1,
“product_name”: “单品促销测试0”,
“sku_key_concat_name”: “3,d,a”,
“brand_id”: 53,
“extend”: {},
“merch_name”: “测试一号店铺”,
“is_product_up_down_desc”: “下架”,
“sku_key_2”: “d”,
“sku_key_3”: “a”,
“merch_id”: “MC201601210278193”
}
改动【商品讲解/取消讲解】接口
接口名称 | 商品讲解/取消讲解接口 |
gateway | oa/mix/live/explaining_goods.json |
接口 | com.fenqile.afterpay.offline.marketing.oa.cgi.live.CgiLiveEventRecordApplyService |
方法 | explainGoods |
group | * |
版本号 | 1.0.0 |
超时时间 | 3000ms |
是否登录 | 是 |
入参 | 字段名 | 字段类型 | 是否必传 | 描述 |
| 登录态 | | | |
live_activity_id | String | 是 | 直播间ID |
explain_action | Integer | 是 | 1 – 讲解2 – 取消讲解 |
explaining_sku_info | String | 是 | 操作的商品 |
出参 | 字段名 | 字段类型 | | |
一文搞懂 Redis 分布式锁-腾讯云开发者社区-腾讯云 (tencent.com
一个公式看懂:为什么DUBBO线程池会打满 – 知乎 (zhihu.com)
微服务的熔断原理与实现 – 知乎 (zhihu.com)
关于ApplicationListener的使用方式以及原理_乐嘉明的博客-CSDN博客
SpringBoot核心机制解读系列三、ApplicationListener – 掘金 (juejin.cn)
Java 开发中微服务 RPC 和 JAR包 调用的对比与使用场景选择-CSDN博客
一、系统架构图
二、运营系统整体规划
三、获客营销核心流程
123...42Next
Page 1 of 42