添加视频缓存和商品缓存和获取

This commit is contained in:
曹佳豪 2025-08-01 16:18:50 +08:00
parent 71139a21c3
commit 701efaadea
12 changed files with 356 additions and 4 deletions

View File

@ -4,6 +4,7 @@ import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 启动程序
@ -12,6 +13,7 @@ import org.springframework.context.annotation.ComponentScan;
*/
@SpringBootApplication
@EnableScheduling
@ComponentScan(basePackages = {"org.dromara", "com.wzj.soopin"})
public class DromaraApplication {

View File

@ -181,6 +181,8 @@ tenant:
- sys_version
- ums_member_wechat
- sys_tenant_extend
- commission_template
- commission_rate_range

View File

@ -23,6 +23,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import com.wzj.soopin.content.domain.bo.IndexListBO;
import com.wzj.soopin.content.domain.bo.MyListBO;
import com.wzj.soopin.content.domain.bo.SimpleListBO;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.web.bind.annotation.*;
@ -303,4 +304,43 @@ public class VlogController extends BaseInfoProperties {
return R.ok(vlogService.getVlogBeLikedCounts(vlogId));
}
@Tag(name = "手动触发缓存点赞最多视频")
@PostMapping("/cacheTopLikedVlogs")
public R<Void> cacheTopLikedVlogs(@RequestParam(defaultValue = "100") int limit) {
try {
vlogService.cacheTopLikedVlogs(limit);
return R.ok();
} catch (Exception e) {
log.error("手动触发缓存点赞最多视频失败", e);
return R.fail("缓存失败: " + e.getMessage());
}
}
@Tag(name = "获取缓存中的点赞最多视频")
@GetMapping("/getTopLikedVlogs")
public R<Object> getTopLikedVlogs(@RequestParam(defaultValue = "") String date) {
try {
String redisKey;
if (StringUtils.isBlank(date)) {
// 如果没有指定日期使用今天的日期
redisKey = "top_liked_vlogs:" + java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
} else {
redisKey = "top_liked_vlogs:" + date;
}
String cachedData = redis.get(redisKey);
if (StringUtils.isNotBlank(cachedData)) {
// 解析JSON数据
ObjectMapper objectMapper = new ObjectMapper();
List<Map<String, Object>> vlogList = objectMapper.readValue(cachedData, new com.fasterxml.jackson.core.type.TypeReference<List<Map<String, Object>>>() {});
return R.ok(vlogList);
} else {
return R.fail("未找到缓存数据,请先执行缓存任务");
}
} catch (Exception e) {
log.error("获取缓存中的点赞最多视频失败", e);
return R.fail("获取缓存失败: " + e.getMessage());
}
}
}

View File

@ -112,4 +112,31 @@ public interface VlogMapper extends BaseMapper<Vlog> {
"ORDER BY " +
" DATE_FORMAT(create_time, '%Y-%m')")
List<Map<String, Object>> getMonthlyVlog();
/**
* 查询所有公开的视频列表用于从Redis获取点赞数
* @return 视频信息列表
*/
@Select("SELECT " +
" v.id, " +
" v.vloger_id, " +
" v.url, " +
" v.cover, " +
" v.title, " +
" v.width, " +
" v.height, " +
" v.like_counts, " +
" v.comments_counts, " +
" v.is_private, " +
" v.create_time, " +
" v.update_time, " +
" v.status, " +
" v.file_id, " +
" v.reason, " +
"city_code,"+
" v.first_frame_img " +
"FROM t_vlog v " +
"WHERE v.status = 1 AND v.is_private = 0 " +
"ORDER BY v.create_time DESC")
List<Map<String, Object>> selectAllPublicVlogs();
}

View File

@ -137,4 +137,10 @@ public interface VlogService {
* @return 分页结果
*/
IPage<Map<String, Object>> getVlogListByMobile(Page<Map<String, Object>> page, VlogBO vlogBO);
/**
* 查询点赞最多的视频列表并存储到Redis
* @param limit 查询数量限制
*/
void cacheTopLikedVlogs(int limit);
}

View File

@ -47,7 +47,10 @@ import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
import java.util.ArrayList;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Service
public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
@ -598,4 +601,73 @@ public class VlogServiceImpl extends BaseInfoProperties implements VlogService {
return vlogMapper.selectVlogListWithAggregatedData(page, vlogBO);
}
@Override
public void cacheTopLikedVlogs(int limit) {
try {
log.info("开始查询点赞最多的{}条视频", limit);
// 查询所有公开的视频列表
List<Map<String, Object>> allVlogs = vlogMapper.selectAllPublicVlogs();
if (allVlogs != null && !allVlogs.isEmpty()) {
// 从Redis获取每个视频的点赞数量
List<Map<String, Object>> vlogsWithLikeCounts = new ArrayList<>();
for (Map<String, Object> vlog : allVlogs) {
String vlogId = vlog.get("id").toString();
String redisLikeKey = REDIS_VLOG_BE_LIKED_COUNTS + ":" + vlogId;
String likeCountStr = redis.get(redisLikeKey);
// 获取Redis中的点赞数如果没有则使用数据库中的默认值
int likeCount = 0;
if (likeCountStr != null && !likeCountStr.isEmpty()) {
try {
likeCount = Integer.parseInt(likeCountStr);
} catch (NumberFormatException e) {
log.warn("Redis中视频{}的点赞数格式错误: {}", vlogId, likeCountStr);
likeCount = 0;
}
}
// 添加Redis中的点赞数到视频信息中
vlog.put("redis_like_count", likeCount);
vlogsWithLikeCounts.add(vlog);
}
// 按Redis中的点赞数排序获取前limit个
vlogsWithLikeCounts.sort((v1, v2) -> {
int count1 = (Integer) v1.get("redis_like_count");
int count2 = (Integer) v2.get("redis_like_count");
return Integer.compare(count2, count1); // 降序排列
});
// 取前limit个
List<Map<String, Object>> topLikedVlogs = vlogsWithLikeCounts.stream()
.limit(limit)
.collect(Collectors.toList());
if (!topLikedVlogs.isEmpty()) {
// 将结果存储到Redis中使用JSON格式
ObjectMapper objectMapper = new ObjectMapper();
// 注册JavaTimeModule以支持LocalDateTime序列化
objectMapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
objectMapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String jsonData = objectMapper.writeValueAsString(topLikedVlogs);
// 存储到Redis设置24小时过期时间
String redisKey = "top_liked_vlogs:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
redis.set(redisKey, jsonData, 24 * 60 * 60); // 24小时过期
log.info("成功缓存{}条点赞最多的视频到Rediskey: {}", topLikedVlogs.size(), redisKey);
} else {
log.warn("未查询到点赞最多的视频数据");
}
} else {
log.warn("未查询到公开的视频数据");
}
} catch (Exception e) {
log.error("缓存点赞最多视频到Redis失败", e);
}
}
}

View File

@ -11,6 +11,7 @@ import com.wzj.soopin.order.domain.form.DeliverProductForm;
import com.wzj.soopin.order.domain.form.OrderPayForm;
import com.wzj.soopin.order.domain.vo.*;
import com.wzj.soopin.order.service.OrderService;
import com.wzj.soopin.order.service.OrderItemService;
import com.wzj.soopin.order.service.impl.OrderServiceImpl;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -24,12 +25,14 @@ import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.redis.redis.RedisService;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.web.core.BaseController;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 订单表Controller
*
@ -47,6 +50,7 @@ public class OrderController extends BaseController {
private final OrderConvert convert;
private final RedisService redisService;
private final OrderService orderService;
private final OrderItemService orderItemService;
@Tag(name ="查询订单列表")
@ -146,9 +150,41 @@ public class OrderController extends BaseController {
return service.decryptPhone(orderId);
}
@Tag(name = "手动触发缓存交易量最多商品")
@PostMapping("/cacheTopTradingProducts")
public R<Void> cacheTopTradingProducts(@RequestParam(defaultValue = "100") int limit) {
try {
orderItemService.cacheTopTradingProducts(limit);
return R.ok();
} catch (Exception e) {
log.error("手动触发缓存交易量最多商品失败", e);
return R.fail("缓存失败: " + e.getMessage());
}
}
@Tag(name = "获取缓存交易量最多商品")
@GetMapping("/getTopTradingProducts")
public R<Object> getTopTradingProducts(@RequestParam(required = false) String date) {
try {
String redisKey;
if (date != null && !date.isEmpty()) {
redisKey = "top_trading_products:" + date;
} else {
redisKey = "top_trading_products:" + java.time.LocalDateTime.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd"));
}
String jsonData = RedisUtils.getCacheObject(redisKey);
if (jsonData != null && !jsonData.isEmpty()) {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
return R.ok(objectMapper.readValue(jsonData, Object.class));
} else {
return R.ok("未找到缓存数据");
}
} catch (Exception e) {
log.error("获取缓存交易量最多商品失败", e);
return R.fail("获取失败: " + e.getMessage());
}
}
}

View File

@ -6,6 +6,7 @@ import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
/**
* 订单中所包含的商品Mapper接口
@ -34,4 +35,29 @@ public interface OrderItemMapper extends BaseMapper<OrderItem> {
"WHERE oi.order_id = #{orderId} " +
"LIMIT 1")
String getName(Long orderId);
/**
* 查询交易量最多的商品列表
* 统计已完成订单中商品的销售数量
*
* @param limit 查询数量限制
* @return 商品交易量信息列表
*/
@Select("SELECT " +
" oi.product_id, " +
" oi.product_name, " +
" oi.pic, " +
" oi.out_product_id, " +
" oi.product_category_id, " +
" SUM(oi.quantity) as total_quantity, " +
" SUM(oi.quantity * oi.sale_price) as total_amount, " +
" COUNT(DISTINCT o.id) as order_count " +
"FROM oms_order_item oi " +
"JOIN oms_order o ON oi.order_id = o.id " +
"WHERE o.status = 3 " + // 已完成订单
" AND o.delete_status = 0 " + // 未删除订单
"GROUP BY oi.product_id, oi.product_name, oi.pic, oi.out_product_id, oi.product_category_id " +
"ORDER BY total_quantity DESC " +
"LIMIT #{limit}")
List<Map<String, Object>> selectTopTradingProducts(int limit);
}

View File

@ -9,5 +9,9 @@ public interface OrderItemService extends IService<OrderItem> {
List<OrderItem> findByOrderId(Long orderId);
/**
* 缓存交易量最多的商品列表到Redis
* @param limit 查询数量限制
*/
void cacheTopTradingProducts(int limit);
}

View File

@ -5,9 +5,17 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wzj.soopin.order.domain.entity.OrderItem;
import com.wzj.soopin.order.mapper.OrderItemMapper;
import com.wzj.soopin.order.service.OrderItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzj.soopin.content.utils.RedisOperator;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import lombok.extern.slf4j.Slf4j;
/**
* 订单中所包含的商品Service业务层处理
@ -15,12 +23,45 @@ import java.util.List;
*
* @author zcc
*/
@Slf4j
@Service
public class OrderItemServiceImpl extends ServiceImpl<OrderItemMapper, OrderItem> implements OrderItemService {
@Autowired
private RedisOperator redis;
@Override
public List<OrderItem> findByOrderId(Long orderId) {
return baseMapper.selectList(new QueryWrapper<OrderItem>().lambda()
.eq(OrderItem::getOrderId, orderId));
}
@Override
public void cacheTopTradingProducts(int limit) {
try {
log.info("开始查询交易量最多的{}个商品", limit);
// 查询交易量最多的商品列表
List<Map<String, Object>> topTradingProducts = baseMapper.selectTopTradingProducts(limit);
if (topTradingProducts != null && !topTradingProducts.isEmpty()) {
// 将结果存储到Redis中使用JSON格式
ObjectMapper objectMapper = new ObjectMapper();
// 注册JavaTimeModule以支持LocalDateTime序列化
objectMapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
objectMapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
String jsonData = objectMapper.writeValueAsString(topTradingProducts);
// 存储到Redis设置24小时过期时间
String redisKey = "top_trading_products:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
redis.set(redisKey, jsonData, 24 * 60 * 60); // 24小时过期
log.info("成功缓存{}个交易量最多的商品到Rediskey: {}", topTradingProducts.size(), redisKey);
} else {
log.warn("未查询到交易量最多的商品数据");
}
} catch (Exception e) {
log.error("缓存交易量最多商品到Redis失败", e);
}
}
}

View File

@ -0,0 +1,79 @@
package org.dromara.system.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.system.convert.SysTenantAccountConvert;
import org.dromara.system.domain.CommissionSection;
import org.dromara.system.domain.SysTenantAccount;
import org.dromara.system.domain.bo.CommissionSectionBo;
import org.dromara.system.domain.bo.SysTenantAccountBo;
import org.dromara.system.domain.vo.CommissionSectionVo;
import org.dromara.system.domain.vo.SysTenantAccountVo;
import org.dromara.system.service.ISysTenantAccountService;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Tag(name = "租户账户管理接口")
@RestController
@RequestMapping("/system/tenant/account")
@RequiredArgsConstructor
public class SysTenantAccountController {
private final ISysTenantAccountService service;
private final SysTenantAccountConvert convert;
@Operation(summary = "查询租户账户列表")
@PostMapping("/list")
public R<IPage<SysTenantAccountVo>> fansList(@RequestBody SysTenantAccountBo bo, @RequestBody Page<SysTenantAccount> page) {
LambdaQueryWrapper<SysTenantAccount> fansQuery = new LambdaQueryWrapper<>();
Page<SysTenantAccount> fans = service.page(page, fansQuery);
return R.ok(convert.toVO(fans));
}
@Operation(summary = "导出租户账户列表")
@Log(title = "租户账户", businessType = BusinessType.EXPORT)
@GetMapping("/export")
public R<String> export(SysTenantAccountBo query) {
List<SysTenantAccount> list = service.list(query);
ExcelUtil<SysTenantAccountVo> util = new ExcelUtil<>(SysTenantAccountVo.class);
return R.ok(util.writeExcel(convert.toVO(list), "租户账户数据"));
}
@Operation(summary = "获取租户账户详细信息")
@GetMapping(value = "/{id}")
public R<SysTenantAccountVo> getInfo(@Parameter(description = "租户账户ID") @PathVariable("id") Long id) {
return R.ok(convert.toVO(service.getById(id)));
}
@Operation(summary = "新增租户账户")
@Log(title = "租户账户", businessType = BusinessType.INSERT)
@PostMapping("/add")
public R<Object> add(@RequestBody SysTenantAccountBo tenantAccount) {
return R.ok(service.save(convert.toPo(tenantAccount)));
}
@Operation(summary = "修改租户账户")
@Log(title = "租户账户", businessType = BusinessType.UPDATE)
@PutMapping("/update")
public R<Object> edit(@RequestBody SysTenantAccountBo tenantAccount) {
return R.ok(service.updateById(convert.toPo(tenantAccount)));
}
@Operation(summary = "删除租户账户")
@Log(title = "租户账户", businessType = BusinessType.DELETE)
@DeleteMapping("/{id}")
public R<Object> remove(@Parameter(description = "租户账户ID") @PathVariable Long id) {
return R.ok(service.removeById(id));
}
}

View File

@ -0,0 +1,17 @@
package org.dromara.system.domain.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "分成比例区间批量添加DTO")
public class CommissionSectionBatchAddDTO {
@Schema(description = "模板ID")
private Integer templateId;
@Schema(description = "区间列表字符串形式的JSON数组")
private String templateList;
}