From b305b6ce82fbb975acf42af3ad450aa4c7699d5e Mon Sep 17 00:00:00 2001 From: zxl <763096477@qq.com> Date: 星期四, 26 六月 2025 14:35:46 +0800 Subject: [PATCH] 客户分析,和浏览视频记录 --- framework/src/main/java/cn/lili/modules/lmk/service/impl/CustomerServiceImpl.java | 3 framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoFootInfoVo.java | 34 +++++ framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java | 83 +++++++++++++ framework/src/main/resources/mapper/lmk/VideoMapper.xml | 106 +++++++++++++++++ framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoFootVO.java | 31 +++++ framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoVO.java | 2 manager-api/src/main/java/cn/lili/controller/lmk/CustomerController.java | 18 +++ framework/src/main/resources/mapper/lmk/CustomerMapper.xml | 10 framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java | 17 ++ framework/src/main/java/cn/lili/modules/lmk/domain/query/FootPrintQuery.java | 22 +++ framework/src/main/java/cn/lili/modules/member/service/FootprintService.java | 19 +++ 11 files changed, 332 insertions(+), 13 deletions(-) diff --git a/framework/src/main/java/cn/lili/modules/lmk/domain/query/FootPrintQuery.java b/framework/src/main/java/cn/lili/modules/lmk/domain/query/FootPrintQuery.java new file mode 100644 index 0000000..374aed5 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/lmk/domain/query/FootPrintQuery.java @@ -0,0 +1,22 @@ +package cn.lili.modules.lmk.domain.query; + + +import cn.lili.base.AbsQuery; +import io.swagger.annotations.ApiModel; +import lombok.Data; + +/** + * lmk-shop-java + * 瑙嗛娴忚瓒宠抗鏌ヨ瀵硅薄 + * + * @author : zxl + * @date : 2025-06-25 14:15 + **/ +@Data +@ApiModel(value = "瑙嗛娴忚瓒宠抗鏌ヨ瀵硅薄", description = "瑙嗛娴忚瓒宠抗鏌ヨ瀵硅薄") +public class FootPrintQuery extends AbsQuery { + /** + * 浼氬憳id + */ + private String memberId; +} diff --git a/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoFootInfoVo.java b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoFootInfoVo.java new file mode 100644 index 0000000..41b1ad3 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoFootInfoVo.java @@ -0,0 +1,34 @@ +package cn.lili.modules.lmk.domain.vo; + + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +/** + * lmk-shop-java + * + * @author : zxl + * @date : 2025-06-25 15:56 + **/ +@Data +@ApiModel(value = "浼氬憳娴忚瑙嗛姹囨�讳俊鎭�", description = "浼氬憳娴忚瑙嗛姹囨�讳俊鎭�") +public class VideoFootInfoVo { + + /** + * 鎬绘挱鏀捐棰戞椂闂达紙姣锛� + */ + private Double totalDuration; + + /** + * 鎬绘祻瑙堣棰戞暟 + */ + private Long VideoCount; + + /** + * 骞冲潎瀹屾挱鐜� + */ + private Double avgCompletionRate; + + + +} diff --git a/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoFootVO.java b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoFootVO.java new file mode 100644 index 0000000..3ea7260 --- /dev/null +++ b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoFootVO.java @@ -0,0 +1,31 @@ +package cn.lili.modules.lmk.domain.vo; + + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +/** + * lmk-shop-java + * 浼氬憳娴忚瑙嗛瓒宠抗 + * + * @author : zxl + * @date : 2025-06-25 14:00 + **/ +@Data +@ApiModel(value = "浼氬憳娴忚瑙嗛瓒宠抗", description = "浼氬憳娴忚瑙嗛瓒宠抗") +public class VideoFootVO extends VideoVO{ + /** + * 瑙傜湅鏃堕暱(姣) + */ + private Long viewDuration; + + /** + * 瑙嗛鎾斁鑷�(绉�) + */ + private String playAt; + + private Double playProgress; + + private String coverCOSUrl; + +} diff --git a/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoVO.java b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoVO.java index 6f80172..9d88c54 100644 --- a/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoVO.java +++ b/framework/src/main/java/cn/lili/modules/lmk/domain/vo/VideoVO.java @@ -97,6 +97,8 @@ @ApiModelProperty("瀹℃牳閫氳繃鏃堕棿") private Date auditPassTime; + + public static VideoVO getVoByEntity(@NonNull Video entity, VideoVO vo) { if(vo == null) { vo = new VideoVO(); diff --git a/framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java b/framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java index c8a6340..d567860 100644 --- a/framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java +++ b/framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java @@ -130,4 +130,21 @@ * @return */ List<VideoGoodsDetailVO> getVideoGoods(@Param("id") String videoId); + + /** + * 鏌ヨ鐢ㄦ埛瑙嗛娴忚瓒宠抗 + * @param page + * @param query + * @return + */ + IPage videoFootPage(IPage page, FootPrintQuery query); + + /** + * 鑾峰緱鐢ㄦ埛娴忚瑙嗛瓒宠抗姹囨�绘暟鎹� + * @param id + * @return + */ + VideoFootInfoVo getVideoFootInfo(String id); + + List<VideoFootVO> videoFoot(String id); } diff --git a/framework/src/main/java/cn/lili/modules/lmk/service/impl/CustomerServiceImpl.java b/framework/src/main/java/cn/lili/modules/lmk/service/impl/CustomerServiceImpl.java index a8c9d35..f3e8bff 100644 --- a/framework/src/main/java/cn/lili/modules/lmk/service/impl/CustomerServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/lmk/service/impl/CustomerServiceImpl.java @@ -47,15 +47,12 @@ return Result.error("璇ヨ处鍙锋病鏈夋敞鍐屽簵閾�"); } return getMemberPage(customerQuery); - } @Override public Result getMember(String id) { MemberVO memberVO = memberService.getMember(id); - //鏌ヨ鐢ㄦ埛鏍囩 -// memberVO.setCustomerTagList(); return Result.ok().data(memberVO); } diff --git a/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java b/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java index a3ff318..9dc441b 100644 --- a/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java +++ b/framework/src/main/java/cn/lili/modules/member/service/FootprintService.java @@ -1,6 +1,10 @@ package cn.lili.modules.member.service; +import cn.lili.base.Result; import cn.lili.common.vo.PageVO; +import cn.lili.modules.lmk.domain.query.FootPrintQuery; +import cn.lili.modules.lmk.domain.vo.VideoFootVO; +import cn.lili.modules.lmk.domain.vo.VideoVO; import cn.lili.modules.member.entity.dos.FootPrint; import cn.lili.modules.member.entity.dto.FootPrintQueryParams; import cn.lili.modules.search.entity.dos.EsGoodsIndex; @@ -49,6 +53,21 @@ IPage<EsGoodsIndex> footPrintPage(FootPrintQueryParams params); /** + * 鑾峰彇浼氬憳瑙嗛娴忚鍘嗗彶鍒嗛〉 + * + * @param params 鍒嗛〉 + * @return 浼氬憳娴忚鍘嗗彶鍒楄〃 + */ + Result videoFootPrintPage(FootPrintQuery params); + + /** + * 鑾峰彇浼氬憳琛屼负鍒嗘瀽 + * @param id 浼氬憳i + * @return + */ + Result memberActionAnalyse(String id); + + /** * 鑾峰彇褰撳墠浼氬憳鐨勬祻瑙堣褰曟暟閲� * * @return 褰撳墠浼氬憳鐨勬祻瑙堣褰曟暟閲� diff --git a/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java b/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java index 723725f..e995846 100644 --- a/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java +++ b/framework/src/main/java/cn/lili/modules/member/serviceimpl/FootprintServiceImpl.java @@ -1,26 +1,34 @@ package cn.lili.modules.member.serviceimpl; +import cn.lili.base.Result; import cn.lili.common.security.context.UserContext; import cn.lili.modules.goods.entity.dos.GoodsSku; import cn.lili.modules.goods.service.GoodsSkuService; +import cn.lili.modules.lmk.domain.query.FootPrintQuery; +import cn.lili.modules.lmk.domain.vo.SimpleVideoTagVO; +import cn.lili.modules.lmk.domain.vo.VideoFootInfoVo; +import cn.lili.modules.lmk.domain.vo.VideoFootVO; +import cn.lili.modules.lmk.domain.vo.VideoVO; +import cn.lili.modules.lmk.mapper.VideoMapper; import cn.lili.modules.member.entity.dos.FootPrint; import cn.lili.modules.member.entity.dto.FootPrintQueryParams; import cn.lili.modules.member.mapper.FootprintMapper; import cn.lili.modules.member.service.FootprintService; import cn.lili.modules.search.entity.dos.EsGoodsIndex; import cn.lili.mybatis.util.PageUtil; +import cn.lili.utils.COSUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.Date; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -36,6 +44,12 @@ @Autowired private GoodsSkuService goodsSkuService; + + @Autowired + private VideoMapper videoMapper; + + @Autowired + private COSUtil cOSUtil; @Override public FootPrint saveFootprint(FootPrint footPrint) { @@ -64,7 +78,6 @@ Page<FootPrint> footPrintPages = this.page(PageUtil.initPage(params), params.queryWrapper()); //瀹氫箟缁撴灉 Page<EsGoodsIndex> esGoodsIndexIPage = new Page<>(); - if (footPrintPages.getRecords() != null && !footPrintPages.getRecords().isEmpty()) { List<String> skuIds = footPrintPages.getRecords().stream().map(FootPrint::getSkuId).collect(Collectors.toList()); List<GoodsSku> goodsSkuByIdFromCache = goodsSkuService.getGoodsSkuByIdFromCache(skuIds); @@ -92,10 +105,70 @@ } @Override + public Result videoFootPrintPage(FootPrintQuery query) { + IPage<VideoFootVO> page = cn.lili.utils.PageUtil.getPage(query,VideoFootVO.class); + videoMapper.videoFootPage(page,query); + + VideoFootInfoVo videoFootInfoVo = videoMapper.getVideoFootInfo(query.getMemberId()); + + for (VideoFootVO videoFootVO : page.getRecords()) { + videoFootVO.setCoverCOSUrl(cOSUtil.getPreviewUrl(videoFootVO.getCoverUrl())); + //璁$畻鎾斁杩涘害 + if(videoFootVO.getPlayAt() == null || videoFootVO.getPlayAt().isEmpty()){ + videoFootVO.setPlayProgress(0.0); + continue; + } + + BigDecimal value = BigDecimal.valueOf(Double.parseDouble(videoFootVO.getPlayAt()) / videoFootVO.getVideoDuration()) + .setScale(2, RoundingMode.HALF_UP); + + if (value.compareTo(BigDecimal.ONE) > 0) { + // 杩欓噷鍙互娣诲姞澶勭悊閫昏緫锛屾瘮濡傦細鑷姩淇涓�1.0 + value = BigDecimal.ONE; + } + + videoFootVO.setPlayProgress(value.doubleValue()); + } + + HashMap<String,Object> map = new HashMap<>(); + map.put("data", page.getRecords()); + map.put("total", page.getTotal()); + if (videoFootInfoVo == null) { + map.put("avgProgress",0); + map.put("totalDuration",0); + }else { + map.put("avgProgress",videoFootInfoVo.getAvgCompletionRate()); + map.put("totalDuration",videoFootInfoVo.getTotalDuration()); + } + return Result.ok().data(map); + } + + @Override + public Result memberActionAnalyse(String id){ + //瑙嗛鍒嗙被 + List<VideoFootVO> list = videoMapper.videoFoot(id); + Map<String, Long> tagCountMap = list.stream() + .flatMap(video -> video.getTagList().stream()) // 灞曞紑鎵�鏈� tag + .filter(Objects::nonNull) // 杩囨护 null + .collect(Collectors.groupingBy( + SimpleVideoTagVO::getTagName, // 鎸� tagName 鍒嗙粍 + Collectors.counting() // 璁$畻姣忎釜 tagName 鍑虹幇鐨勬鏁� + )); + + return Result.ok().data(tagCountMap); + } + + + @Override public long getFootprintNum() { LambdaQueryWrapper<FootPrint> lambdaQueryWrapper = Wrappers.lambdaQuery(); lambdaQueryWrapper.eq(FootPrint::getMemberId, Objects.requireNonNull(UserContext.getCurrentUser()).getId()); lambdaQueryWrapper.eq(FootPrint::getDeleteFlag, false); return this.count(lambdaQueryWrapper); } + + + } + + diff --git a/framework/src/main/resources/mapper/lmk/CustomerMapper.xml b/framework/src/main/resources/mapper/lmk/CustomerMapper.xml index d98bcd8..951ba0e 100644 --- a/framework/src/main/resources/mapper/lmk/CustomerMapper.xml +++ b/framework/src/main/resources/mapper/lmk/CustomerMapper.xml @@ -27,10 +27,10 @@ <result property="experience" column="experience"/> <result property="createTime" column="create_time"/> <result property="blackId" column="blackId"/> - <collection property="customerTagList" ofType="cn.lili.modules.lmk.domain.vo.CustomerTagVO" - select="selectTagByMemberId" - column="id" - /> +<!-- <collection property="customerTagList" ofType="cn.lili.modules.lmk.domain.vo.CustomerTagVO"--> +<!-- select="selectTagByMemberId"--> +<!-- column="id"--> +<!-- />--> </resultMap> <select id="getPage" resultMap="BaseResultMap"> @@ -38,7 +38,7 @@ LM.* FROM li_member LM - LEFT JOIN lmk_customer_black LMK ON LM.id = LMK.user_id and LMK.delete_flag = 0 + <where> <!-- 鐢ㄦ埛鍚嶆ā绯婃煡璇� --> <if test="query.username != null and query.username != ''"> diff --git a/framework/src/main/resources/mapper/lmk/VideoMapper.xml b/framework/src/main/resources/mapper/lmk/VideoMapper.xml index 248411a..26809d9 100644 --- a/framework/src/main/resources/mapper/lmk/VideoMapper.xml +++ b/framework/src/main/resources/mapper/lmk/VideoMapper.xml @@ -26,6 +26,7 @@ <collection property="goodsList" column="id" select="getVideoGoods" ofType="cn.lili.modules.lmk.domain.vo.VideoGoodsDetailVO"/> </resultMap> + <resultMap id="VideoGoodsMap" type="cn.lili.modules.lmk.domain.vo.VideoGoodsDetailVO"> <result column="goods_id" property="goodsId"/> <result column="goods_sku_id" property="id"/> @@ -545,4 +546,109 @@ <if test="query.authorId != null and query.authorId != ''">AND LV.author_id = #{query.authorId}</if> <if test="query.status != null and query.status != ''">AND LV.status = #{query.status}</if> </select> + + + <resultMap id="videoFootMap" type="cn.lili.modules.lmk.domain.vo.VideoFootVO"> + <id column="id" property="id"/> + <result column="author_id" property="authorId" /> + <result column="authorName" property="authorName" /> + <result column="cover_url" property="coverUrl" /> + <result column="video_file_key" property="videoFileKey" /> + <result column="video_fit" property="videoFit" /> + <result column="title" property="title" /> + <result column="video_duration" property="videoDuration" /> + <result column="recommend" property="recommend" /> + <result column="status" property="status" /> + <result column="play_num" property="playNum" /> + <result column="collect_num" property="collectNum" /> + <result column="comment_num" property="commentNum" /> + <result column="weight" property="weight" /> + <result column="audit_pass_time" property="auditPassTime" /> + <result column="update_time" property="updateTime" /> + <result column="video_content_type" property="videoContentType" /> + <result column="video_type" property="videoType" /> + <result column="video_imgs" property="videoImgs" /> + <result column="view_duration" property="viewDuration"/> + <result column="play_at" property="playAt"/> + + <collection property="goodsList" column="id" select="getVideoGoods" ofType="cn.lili.modules.lmk.domain.vo.VideoGoodsDetailVO"/> + <collection property="tagList" column="id" select="getVideoTags" ofType="cn.lili.modules.lmk.domain.vo.SimpleVideoTagVO"/> + + </resultMap> + + <select id="videoFootPage" resultMap="videoFootMap"> + SELECT + LFP.view_duration, + LFP.play_at, + LV.* + from + li_foot_print LFP + INNER JOIN lmk_video LV + ON LFP.ref_id = lV.id AND LV.delete_flag = 0 + where LFP.member_id = #{query.memberId} + AND LFP.ref_id IS NOT NULL + AND LFP.delete_flag = 0 + + AND LFP.view_type = 'video' + </select> + <select id="videoFoot" resultMap="videoFootMap"> + SELECT + LFP.view_duration, + LFP.play_at, + LV.* + from + li_foot_print LFP + INNER JOIN lmk_video LV + ON LFP.ref_id = lV.id AND LV.delete_flag = 0 + where LFP.member_id = #{query.memberId} + AND LFP.ref_id IS NOT NULL + AND LFP.delete_flag = 0 + + AND LFP.view_type = 'video' + </select> + + <resultMap id="videoFootInfo" type="cn.lili.modules.lmk.domain.vo.VideoFootInfoVo"> + <result column="total_duration" property="totalDuration"/> + <result column="video_count" property="videoCount"/> + <result column="avg_completion_rate" property="avgCompletionRate"/> + + + </resultMap> + + <resultMap id="videoTagMap" type="cn.lili.modules.lmk.domain.vo.SimpleVideoTagVO"> + <id property="id" column="id"/> + <result property="tagName" column="tag_name"/> + + </resultMap> + + <select id="getVideoTags" parameterType="String" resultMap="videoTagMap"> + SELECT LVT.id, + LVT.tag_name + from lmk_video_tag_ref LVTR + LEFT JOIN lmk_video_tag LVT ON LVT.id = LVTR.video_tag_id and LVT.delete_flag = 0 + WHERE LVTR.video_id = #{id} + + </select> + + <select id="getVideoFootInfo" resultMap="videoFootInfo"> + SELECT + SUM(LFP.view_duration) AS total_duration, + ROUND(AVG(CASE + WHEN LV.video_duration > 0 THEN LFP.play_at / LV.video_duration + ELSE 0 + END),2) AS avg_completion_rate, + COUNT(*) AS video_count + from + li_foot_print LFP + INNER JOIN lmk_video LV + ON LFP.ref_id = lV.id AND LV.delete_flag = 0 + where LFP.member_id = #{id} + AND LFP.ref_id IS NOT NULL + AND LFP.delete_flag = 0 + AND LFP.view_type = 'video' + AND LV.id IS NOT NULL + GROUP BY + LFP.member_id + </select> + </mapper> diff --git a/manager-api/src/main/java/cn/lili/controller/lmk/CustomerController.java b/manager-api/src/main/java/cn/lili/controller/lmk/CustomerController.java index 62a9d02..ddf4156 100644 --- a/manager-api/src/main/java/cn/lili/controller/lmk/CustomerController.java +++ b/manager-api/src/main/java/cn/lili/controller/lmk/CustomerController.java @@ -10,11 +10,16 @@ import cn.lili.modules.lmk.domain.form.CustomerTagRefForm; import cn.lili.modules.lmk.domain.query.CustomerQuery; import cn.lili.modules.lmk.domain.query.CustomerTagQuery; +import cn.lili.modules.lmk.domain.query.FootPrintQuery; +import cn.lili.modules.lmk.domain.vo.VideoFootVO; import cn.lili.modules.lmk.service.CustomerService; import cn.lili.modules.lmk.service.CustomerTagRefService; import cn.lili.modules.lmk.service.CustomerTagService; +import cn.lili.modules.member.entity.dto.FootPrintQueryParams; import cn.lili.modules.member.entity.vo.MemberSearchVO; import cn.lili.modules.member.entity.vo.MemberVO; +import cn.lili.modules.member.service.FootprintService; +import cn.lili.modules.member.service.MemberService; import com.baomidou.mybatisplus.core.metadata.IPage; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -31,6 +36,7 @@ private final CustomerService customerService; private final CustomerTagService customerTagService; private final CustomerTagRefService customerTagRefService; + private final FootprintService footprintService; @ApiOperation(value = "鍟嗛摵涓嬫媺鍒楄〃") @GetMapping("/store/selectOption") @@ -105,4 +111,16 @@ return customerTagRefService.removeById(id); } + @GetMapping("/videoFootPage") + @ApiOperation(value = "瑙嗛娴忚鍘嗗彶鍒嗛〉", notes = "瑙嗛娴忚鍘嗗彶鍒嗛〉") + public Result videoFootPage(FootPrintQuery query){ + + return footprintService.videoFootPrintPage(query); + } + + @GetMapping("/memberActionAnalyse/{id}") + @ApiOperation(value = "浼氬憳琛屼负鍒嗘瀽", notes = "浼氬憳琛屼负鍒嗘瀽") + public Result memberActionAnalyse(@PathVariable("id") String id){ + return footprintService.memberActionAnalyse(id); + } } -- Gitblit v1.8.0