framework/src/main/java/cn/lili/modules/lmk/domain/vo/CollectTypeNumVO.java
New file @@ -0,0 +1,22 @@ package cn.lili.modules.lmk.domain.vo; import lombok.Data; /** * @author:xp * @date:2025/5/23 9:23 */ @Data public class CollectTypeNumVO { /** * 视频id */ private String id; /** * 收藏数量 */ private Long collectNum; } framework/src/main/java/cn/lili/modules/lmk/enums/general/CollectTypeEnum.java
New file @@ -0,0 +1,45 @@ package cn.lili.modules.lmk.enums.general; import lombok.Getter; import org.apache.commons.lang3.StringUtils; /** * 视频标签来源 * * @author:xp * @date:2025/5/14 10:30 */ @Getter public enum CollectTypeEnum { VIDEO("video", "视频"), ; private final String value; private final String desc; CollectTypeEnum(String value, String desc) { this.value = value; this.desc = desc; } /** * 获取含义 * * @param value * @return */ public static String getDescByValue(String value) { if (StringUtils.isBlank(value)) { return null; } for (CollectTypeEnum e : CollectTypeEnum.values()){ if (value.equals(e.getValue())) { return e.getDesc(); } } return null; } } framework/src/main/java/cn/lili/modules/lmk/mapper/MyCollectMapper.java
@@ -2,10 +2,10 @@ import cn.lili.modules.lmk.domain.entity.MyCollect; import cn.lili.modules.lmk.domain.vo.SimpleMyCollectVO; import cn.lili.modules.lmk.domain.vo.CollectTypeNumVO; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import cn.lili.modules.lmk.domain.vo.MyCollectVO; import cn.lili.modules.lmk.domain.form.MyCollectForm; import cn.lili.modules.lmk.domain.query.MyCollectQuery; import java.util.List; import org.apache.ibatis.annotations.Mapper; @@ -40,4 +40,12 @@ * @return */ List<SimpleMyCollectVO> getCollectsByVideoIds(@Param("videoIds") List<String> videoIds, @Param("userId") String currentUserId); /** * 根据某收藏类型id分组统计数量,比如:统计每个视频的收藏数 * * @param type * @return */ List<CollectTypeNumVO> countNumGroupByType(@Param("type") String type); } framework/src/main/java/cn/lili/modules/lmk/mapper/VideoMapper.java
@@ -2,6 +2,7 @@ import cn.lili.modules.lmk.domain.entity.Video; import cn.lili.modules.lmk.domain.query.ManagerVideoQuery; import cn.lili.modules.lmk.domain.vo.CollectTypeNumVO; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import cn.lili.modules.lmk.domain.vo.VideoVO; @@ -46,4 +47,11 @@ * @return */ IPage recommendVideo(IPage page); /** * 批量更新视频收藏数量 * * @param numList */ void updateCollectNumBatch(@Param("list") List<CollectTypeNumVO> numList); } framework/src/main/java/cn/lili/modules/lmk/service/MyCollectService.java
@@ -2,11 +2,11 @@ import cn.lili.modules.lmk.domain.entity.MyCollect; import cn.lili.modules.lmk.domain.vo.SimpleMyCollectVO; import cn.lili.modules.lmk.domain.vo.CollectTypeNumVO; import com.baomidou.mybatisplus.extension.service.IService; import cn.lili.base.Result; import cn.lili.modules.lmk.domain.form.MyCollectForm; import cn.lili.modules.lmk.domain.query.MyCollectQuery; import org.springframework.beans.PropertyValues; import java.util.List; @@ -73,4 +73,11 @@ * @return */ List<SimpleMyCollectVO> getCollectsByVideoIds(List<String> videoIds); /** * 根据某收藏类型id分组统计数量,比如:统计每个视频的收藏数 * @param type * @return */ List<CollectTypeNumVO> countNumGroupByType(String type); } framework/src/main/java/cn/lili/modules/lmk/service/VideoService.java
@@ -6,6 +6,7 @@ import cn.lili.modules.lmk.domain.form.VideoDownForm; import cn.lili.modules.lmk.domain.form.VideoRecommendForm; import cn.lili.modules.lmk.domain.query.ManagerVideoQuery; import cn.lili.modules.lmk.domain.vo.CollectTypeNumVO; import com.baomidou.mybatisplus.extension.service.IService; import cn.lili.base.Result; import cn.lili.modules.lmk.domain.form.VideoForm; @@ -122,4 +123,11 @@ * @return */ Result recommendVideo(AbsQuery query); /** * 批量更新视频收藏数量 * * @param numList */ void updateCollectNumBatch(List<CollectTypeNumVO> numList); } framework/src/main/java/cn/lili/modules/lmk/service/impl/MyCollectServiceImpl.java
@@ -2,6 +2,7 @@ import cn.lili.common.security.context.UserContext; import cn.lili.modules.lmk.domain.vo.SimpleMyCollectVO; import cn.lili.modules.lmk.domain.vo.CollectTypeNumVO; import com.baomidou.mybatisplus.core.metadata.IPage; import cn.lili.modules.lmk.domain.entity.MyCollect; import cn.lili.modules.lmk.mapper.MyCollectMapper; @@ -137,4 +138,9 @@ public List<SimpleMyCollectVO> getCollectsByVideoIds(List<String> videoIds) { return baseMapper.getCollectsByVideoIds(videoIds, UserContext.getCurrentUserId()); } @Override public List<CollectTypeNumVO> countNumGroupByType(String type) { return baseMapper.countNumGroupByType(type); } } framework/src/main/java/cn/lili/modules/lmk/service/impl/VideoServiceImpl.java
@@ -24,6 +24,7 @@ import cn.lili.modules.lmk.domain.form.VideoForm; import cn.lili.modules.lmk.domain.query.VideoQuery; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.ListUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; @@ -278,4 +279,14 @@ } return Result.ok().data(page.getRecords()); } @Override @Transactional(rollbackFor = Exception.class) public void updateCollectNumBatch(List<CollectTypeNumVO> numList) { // 按500条数据进行拆分 List<List<CollectTypeNumVO>> chunks = ListUtils.partition(numList, 500); for (List<CollectTypeNumVO> chunk : chunks) { baseMapper.updateCollectNumBatch(chunk); } } } framework/src/main/resources/mapper/lmk/MyCollectMapper.xml
@@ -54,4 +54,16 @@ AND ref_id IN <foreach collection="videoIds" open="(" item="videoId" close=")" separator=",">#{videoId}</foreach> </select> <select id="countNumGroupByType" parameterType="string" resultType="cn.lili.modules.lmk.domain.vo.CollectTypeNumVO"> SELECT ref_id as id, count(id) as collectNum FROM lmk_my_collect WHERE collect_type = #{type} AND delete_flag = 0 GROUP BY ref_id </select> </mapper> framework/src/main/resources/mapper/lmk/VideoMapper.xml
@@ -162,4 +162,19 @@ LV.delete_flag = 0 AND LV.status = '1' </select> <update id="updateCollectNumBatch"> UPDATE lmk_video SET collect_num = CASE id <foreach collection="list" item="video"> WHEN #{video.id} THEN #{video.collectNum} </foreach> ELSE collect_num END WHERE id IN <foreach collection="list" item="video" open="(" separator="," close=")"> #{video.id} </foreach> </update> </mapper> lmk-job/pom.xml
New file @@ -0,0 +1,33 @@ <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <artifactId>lmk-job</artifactId> <parent> <groupId>cn.lili</groupId> <artifactId>lili-shop-parent</artifactId> <version>${revision}</version> <relativePath>../pom.xml</relativePath> </parent> <dependencies> <dependency> <groupId>cn.lili</groupId> <artifactId>framework</artifactId> <version>${revision}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> lmk-job/src/main/java/cn/lili/LmkJobApplication.java
New file @@ -0,0 +1,33 @@ package cn.lili; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Primary; import org.springframework.core.task.TaskExecutor; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; /** * @author:xp * @date:2025/5/23 9:09 */ @SpringBootApplication @EnableCaching @EnableAsync public class LmkJobApplication { @Primary @Bean public TaskExecutor primaryTask() { return new ThreadPoolTaskExecutor(); } public static void main(String[] args) { System.setProperty("es.set.netty.runtime.available.processors", "false"); System.setProperty("rocketmq.client.logUseSlf4j","true"); SpringApplication.run(LmkJobApplication.class, args); } } lmk-job/src/main/java/cn/lili/job/VideoJob.java
New file @@ -0,0 +1,42 @@ package cn.lili.job; import cn.lili.modules.lmk.domain.vo.CollectTypeNumVO; import cn.lili.modules.lmk.enums.general.CollectTypeEnum; import cn.lili.modules.lmk.service.MyCollectService; import cn.lili.modules.lmk.service.VideoService; import com.xxl.job.core.context.XxlJobHelper; import com.xxl.job.core.handler.annotation.XxlJob; import lombok.RequiredArgsConstructor; import org.apache.commons.collections4.CollectionUtils; import org.springframework.stereotype.Component; import java.util.List; /** * 视频相关的定时任务 * * @author:xp * @date:2025/5/23 9:13 */ @Component @RequiredArgsConstructor public class VideoJob { private final VideoService videoService; private final MyCollectService myCollectService; /** * 视频收藏数统计 * * @throws Exception */ @XxlJob("videoCollectNumJob") public void videoCollectNumJob() throws Exception { XxlJobHelper.log("开始执行:视频收藏数统计"); List<CollectTypeNumVO> numList = myCollectService.countNumGroupByType(CollectTypeEnum.VIDEO.getValue()); if (CollectionUtils.isNotEmpty(numList)) { videoService.updateCollectNumBatch(numList); } } } lmk-job/src/main/resources/application.yml
New file @@ -0,0 +1,266 @@ server: port: 10001 servlet: context-path: / tomcat: uri-encoding: UTF-8 threads: min-spare: 50 max: 1000 spring: application: name: lmk-job # 要在其中注册的Spring Boot Admin Server的URL。 boot: admin: client: url: http://127.0.0.1:8000 cache: type: redis # Redis redis: host: 127.0.0.1 port: 6379 # password: lilishop lettuce: pool: # 连接池最大连接数(使用负值表示没有限制) 默认 8 max-active: 200 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1 max-wait: 20 # 连接池中的最大空闲连接 默认 8 max-idle: 10 # 连接池中的最小空闲连接 默认 8 min-idle: 8 # 文件大小上传配置 servlet: multipart: max-file-size: 20MB max-request-size: 20MB jackson: time-zone: GMT+8 serialization: #关闭jackson 对json做解析 fail-on-empty-beans: false shardingsphere: datasource: # 数据库名称,可自定义,可以为多个,以逗号隔开,每个在这里定义的库,都要在下面定义连接属性 names: default-datasource default-datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/lilishop?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai username: root password: 123456 maxActive: 20 initialSize: 5 maxWait: 60000 minIdle: 5 timeBetweenEvictionRunsMillis: 60000 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false #是否缓存preparedStatement,也就是PSCache。在mysql下建议关闭。 PSCache对支持游标的数据库性能提升巨大,比如说oracle。 poolPreparedStatements: false #要启用PSCache,-1为关闭 必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true 可以把这个数值配置大一些,比如说100 maxOpenPreparedStatements: -1 #配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙 filters: stat,wall,log4j2 #通过connectProperties属性来打开mergeSql功能;慢SQL记录 connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 #合并多个DruidDataSource的监控数据 useGlobalDataSourceStat: true loginUsername: druid loginPassword: druid # sharding: # default-data-source-name: default-datasource # #需要拆分的表,可以设置多个 在 li_order 级别即可 # tables: # #需要进行分表的逻辑表名 # li_order: # #实际的表结点,下面代表的是li_order_为开头的所有表,如果能确定表的范围例如按月份分表,这里的写法是data2020.li_order_$->{2020..2021}_$->{01..12} 表示例如 li_order_2020_01 li_order_2020_03 li_order_2021_01 # actual-data-nodes: data2020.li_order_$->{2019..2021}_$->{01..12} # table-strategy: # # 分表策略,根据创建日期 # standard: # sharding-column: create_time # #分表策略 # precise-algorithm-class-name: cn.lili.mybatis.sharding.CreateTimeShardingTableAlgorithm # #范围查询实现 # range-algorithm-class-name: cn.lili.mybatis.sharding.CreateTimeShardingTableAlgorithm props: #是否打印逻辑SQL语句和实际SQL语句,建议调试时打印,在生产环境关闭 sql: show: false # 忽略鉴权url ignored: urls: - /editor-app/** - /actuator** - /actuator/** - /MP_verify_qSyvBPhDsPdxvOhC.txt - /weixin/** - /source/** - /manager/passport/user/login - /manager/passport/user/refresh/** - /manager/other/elasticsearch - /manager/other/customWords - /druid/** - /swagger-ui.html - /doc.html - /swagger-resources/** - /swagger/** - /webjars/** - /v2/api-docs - /configuration/ui - /boot-admin - /**/*.js - /**/*.css - /**/*.png - /**/*.ico # Swagger界面内容配置 swagger: title: lili API接口文档 description: lili Api Documentation version: 1.0.0 termsOfServiceUrl: https://pickmall.cn contact: name: lili url: https://pickmall.cn email: admin@pickmall.com # Mybatis-plus mybatis-plus: mapper-locations: classpath*:mapper/*.xml configuration: #缓存开启 cache-enabled: true #日志 # log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志 logging: config: classpath:logback-spring.xml # 输出级别 level: cn.lili: info # org.hibernate: debug # org.springframework: debug file: # 指定路径 path: lili-logs logback: rollingpolicy: # 最大保存天数 max-history: 7 # 每个文件最大大小 max-file-size: 5MB #加密参数 jasypt: encryptor: password: lili lili: system: isDemoSite: true # 脱敏级别: # 0:不做脱敏处理 # 1:管理端用户手机号等信息脱敏 # 2:商家端信息脱敏(为2时,表示管理端,商家端同时脱敏) sensitiveLevel: 1 statistics: # 在线人数统计 X 小时。这里设置48,即统计过去48小时每小时在线人数 onlineMember: 48 # 当前在线人数刷新时间间隔,单位秒,设置为600,则每10分钟刷新一次 currentOnlineUpdate: 600 #qq lbs 申请 lbs: key: 4BYBZ-7MT6S-PUAOA-6BNWL-FJUD7-UUFXT sk: zhNKVrJK6UPOhqIjn8AQvG37b9sz6 #域名 domain: pc: https://pc.b2b2c.pickmall.cn wap: https://m.b2b2c.pickmall.cn store: https://store.b2b2c.pickmall.cn admin: https://admin.b2b2c.pickmall.cn #api地址 api: buyer: https://buyer-api.pickmall.cn common: https://common-api.pickmall.cn manager: https://admin-api.pickmall.cn store: https://store-api.pickmall.cn # jwt 细节设定 jwt-setting: # token过期时间(分钟) tokenExpireTime: 60 # 使用Spring @Cacheable注解失效时间 cache: # 过期时间 单位秒 永久不过期设为-1 timeout: 1500 #多线程配置 thread: corePoolSize: 5 maxPoolSize: 50 queueCapacity: 50 data: elasticsearch: cluster-name: elasticsearch cluster-nodes: 127.0.0.1:9200 index: number-of-replicas: 0 number-of-shards: 3 index-prefix: lili schema: http # account: # username: elastic # password: LiLiShopES # logstash: # server: 127.0.0.1:4560 rocketmq: promotion-topic: lili_promotion_topic promotion-group: lili_promotion_group msg-ext-topic: lili_msg_topic msg-ext-group: lili_msg_group goods-topic: lili_goods_topic goods-group: lili_goods_group order-topic: lili_order_topic order-group: lili_order_group member-topic: lili_member_topic member-group: lili_member_group store-topic: lili_store_topic store-group: lili_store_group other-topic: lili_other_topic other-group: lili_other_group notice-topic: lili_notice_topic notice-group: lili_notice_group notice-send-topic: lili_send_notice_topic notice-send-group: lili_send_notice_group after-sale-topic: lili_after_sale_topic after-sale-group: lili_after_sale_group rocketmq: name-server: 127.0.0.1:9876 producer: group: lili_group send-message-timeout: 30000 xxl: job: admin: addresses: http://127.0.0.1:9001/xxl-job-admin executor: appname: xxl-job-executor-lilishop address: ip: port: 8848 logpath: ./xxl-job/executor logretentiondays: 7 lmk-job/src/main/resources/logback-spring.xml
New file @@ -0,0 +1,58 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration> <configuration> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <include resource="org/springframework/boot/logging/logback/console-appender.xml"/> <!--应用名称--> <springProperty scope="context" name="APP_NAME" source="spring.application.name"/> <!--日志文件保存路径--> <springProperty scope="context" name="LOG_FILE_PATH" source="logging.file.path"/> <springProperty scope="context" name="LOGSTASH_SERVER" source="lili.data.logstash.server"/> <contextName>${APP_NAME}</contextName> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE_PATH}/${APP_NAME}-%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>${FILE_LOG_PATTERN}</pattern> </encoder> </appender> <appender name="RocketmqClientAppender" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_FILE_PATH}/rocketmq.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_FILE_PATH}/rocketmq/rocketmq-%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> <totalSizeCap>30MB</totalSizeCap> </rollingPolicy> <encoder> <pattern>%d{yy-MM-dd.HH:mm:ss.SSS} [%-16t] %-5p %-22c{0} %X{ServiceId} - %m%n</pattern> </encoder> </appender> <logger name="RocketmqClient" additivity="false"> <level value="info" /> <appender-ref ref="RocketmqClientAppender"/> </logger> <!-- <!–输出到elk的LOGSTASH–>--> <!-- <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">--> <!-- <!– 配置elk日志收集 配饰的是 LOGSTASH 的地址–>--> <!-- <destination>${LOGSTASH_SERVER}</destination>--> <!-- <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder">--> <!-- <providers>--> <!-- <timestamp>--> <!-- <timeZone>UTC</timeZone>--> <!-- </timestamp>--> <!-- </providers>--> <!-- <!–自定义字段 区分项目–>--> <!-- <customFields>{"appName":"${APP_NAME}"}</customFields>--> <!-- </encoder>--> <!-- </appender>--> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> <!-- <appender-ref ref="LOGSTASH"/>--> </root> </configuration> manager-api/src/main/java/cn/lili/controller/job/TestJob.java
File was deleted pom.xml
@@ -77,6 +77,7 @@ <module>consumer</module> <module>admin</module> <module>im-api</module> <module>lmk-job</module> </modules> <build>