Commit 33d0f5d6 authored by Administrator's avatar Administrator

助手页面接口增加缓存

parent d37316ac
...@@ -128,7 +128,8 @@ public class SecurityInterceptor implements HandlerInterceptor { ...@@ -128,7 +128,8 @@ public class SecurityInterceptor implements HandlerInterceptor {
|| uri.startsWith("/ueditor/") || uri.startsWith("/ueditor/")
|| uri.startsWith("/static/") || uri.startsWith("/static/")
|| uri.startsWith("/api-common/") || uri.startsWith("/api-common/")
|| uri.startsWith("/api/cmpt/getCmpt/") || uri.startsWith("/api/cmpt/")
|| uri.startsWith("/api/func/")
|| uri.equals("/auth") || uri.equals("/auth_no"); || uri.equals("/auth") || uri.equals("/auth_no");
} }
......
package com.dlbridge.jadp.plugins.cmpt.service; package com.dlbridge.jadp.plugins.cmpt.service;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import com.dlbridge.jadp.common.cache.CacheTemplate;
import com.dlbridge.jadp.plugins.cmpt.bean.*; import com.dlbridge.jadp.plugins.cmpt.bean.*;
import com.dlbridge.jadp.plugins.cmpt.dao.mapper.CmptMapper; import com.dlbridge.jadp.plugins.cmpt.dao.mapper.CmptMapper;
import com.dlbridge.jadp.plugins.cmpt.dao.model.Cmpt; import com.dlbridge.jadp.plugins.cmpt.dao.model.Cmpt;
...@@ -10,6 +13,7 @@ import lombok.extern.log4j.Log4j2; ...@@ -10,6 +13,7 @@ import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Pageable;
import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations; import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
...@@ -17,10 +21,12 @@ import org.springframework.transaction.annotation.Transactional; ...@@ -17,10 +21,12 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/** /**
* 赛事 * 赛事
...@@ -38,12 +44,21 @@ public class CmptService { ...@@ -38,12 +44,21 @@ public class CmptService {
@Resource @Resource
private RemoteService remoteService; private RemoteService remoteService;
@Resource
private CacheTemplate cacheTemplate;
@Resource @Resource
private ValueOperations valueOperations; private ValueOperations valueOperations;
@Resource @Resource
private HashOperations hashOperations; private HashOperations hashOperations;
@Resource
private RedisTemplate<String, Object> redisTemplate;
/** 静态 DateTimeFormatter,避免每次循环创建 */
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
/** /**
* 赛事下已报名集合 * 赛事下已报名集合
*/ */
...@@ -174,90 +189,101 @@ public class CmptService { ...@@ -174,90 +189,101 @@ public class CmptService {
} }
public Page<List<Cmpt>> queryCmpt(Pageable page, Map<String, String> param) { public Page<List<Cmpt>> queryCmpt(Pageable page, Map<String, String> param) {
long count = cmptMapper.countCount(param); // 响应级缓存:同一筛选条件+分页 的所有用户看到相同结果,缓存1分钟
// 1000 req/s 下,60秒窗口内每个缓存key仅查DB一次,DB压力降至 < 0.02 req/s
String responseCacheKey = "cmpt:list:resp:" + param.get("userId") + ":" + param.get("type")
+ ":" + param.get("keyword") + ":" + param.get("partnerId")
+ ":" + page.getPageNumber() + ":" + page.getPageSize();
Object cached = valueOperations.get(responseCacheKey);
if (cached != null) {
try {
Page<List<Cmpt>> cachedPage = JSON.parseObject(cached.toString(), new TypeReference<Page<List<Cmpt>>>() {});
if (cachedPage != null) {
return cachedPage;
}
} catch (Exception e) {
log.warn("赛事列表缓存反序列化失败,重新查询: {}", e.getMessage());
}
}
// count 短时缓存:同一筛选条件翻页时避免重复执行跨库COUNT查询
String countCacheKey = "cmpt:list:count:" + param.get("userId") + ":" + param.get("type") + ":" + param.get("keyword") + ":" + param.get("partnerId");
Long count = null;
Object cachedCount = valueOperations.get(countCacheKey);
if (cachedCount != null) {
count = Long.valueOf(cachedCount.toString());
}
if (count == null) {
count = cmptMapper.countCount(param);
valueOperations.set(countCacheKey, count, 30, TimeUnit.SECONDS);
}
List<Cmpt> list = cmptMapper.queryCmpt(page, param); List<Cmpt> list = cmptMapper.queryCmpt(page, param);
list.forEach(cmpt -> { if (!list.isEmpty()) {
Object o = valueOperations.get("gk:view:" + cmpt.getCmptId()); // 批量获取浏览量,消除 N+1 Redis 调用
if (o != null) { List<String> keys = new ArrayList<>(list.size());
cmpt.setView((Integer) o); for (Cmpt cmpt : list) {
keys.add("gk:view:" + cmpt.getCmptId());
} }
// by ggb 2025/05/09 判断是否是是在报名中 List<Object> views = redisTemplate.opsForValue().multiGet(keys);
// 定义日期时间格式
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
if (cmpt.getOpenTime() != null) { for (int i = 0; i < list.size(); i++) {
// 解析字符串为LocalDateTime Cmpt cmpt = list.get(i);
LocalDateTime openTime = LocalDateTime.parse(cmpt.getOpenTime(), formatter); // 浏览量赋值
if (cmpt.getDeadLine() != null) { if (views != null && i < views.size() && views.get(i) != null) {
LocalDateTime deadLine = LocalDateTime.parse(cmpt.getDeadLine(), formatter); cmpt.setView((Integer) views.get(i));
// 判断当前时间是否在两个时间之间 }
// 报名中 // by ggb 2025/05/09 判断是否是在报名中(复用静态 DATE_FORMATTER,避免循环内重复创建)
if(now.isAfter(openTime) && now.isBefore(deadLine)){ if (cmpt.getOpenTime() != null) {
cmpt.setEnrollStatus(1); LocalDateTime openTime = LocalDateTime.parse(cmpt.getOpenTime(), DATE_FORMATTER);
} if (cmpt.getDeadLine() != null) {
// 报名结束 LocalDateTime deadLine = LocalDateTime.parse(cmpt.getDeadLine(), DATE_FORMATTER);
if(now.isAfter(deadLine)){ if (now.isAfter(openTime) && now.isBefore(deadLine)) {
cmpt.setEnrollStatus(2); cmpt.setEnrollStatus(1);
}
if (now.isAfter(deadLine)) {
cmpt.setEnrollStatus(2);
}
} else {
if (now.isAfter(openTime)) {
cmpt.setEnrollStatus(1);
}
} }
if (now.isBefore(openTime)) {
} else { cmpt.setEnrollStatus(0);
// 报名中
if(now.isAfter(openTime)){
cmpt.setEnrollStatus(1);
} }
} }
// 报名未开始 // by ggb 2025/05/09 判断是否是在比赛中
if(now.isBefore(openTime)){ if (cmpt.getStartTimeStr() != null) {
cmpt.setEnrollStatus(0); LocalDateTime startTime = LocalDateTime.parse(cmpt.getStartTimeStr(), DATE_FORMATTER);
} if (cmpt.getEndTimeStr() != null) {
} LocalDateTime endTime = LocalDateTime.parse(cmpt.getEndTimeStr(), DATE_FORMATTER);
// by ggb 2025/05/09 判断是否是是在比赛中 if (now.isAfter(startTime) && now.isBefore(endTime)) {
if (cmpt.getStartTimeStr() != null) { cmpt.setOpenStatus(1);
// 解析字符串为LocalDateTime }
LocalDateTime startTime = LocalDateTime.parse(cmpt.getStartTimeStr(), formatter); if (now.isAfter(endTime)) {
if (cmpt.getEndTimeStr() != null) { cmpt.setOpenStatus(2);
LocalDateTime endTime = LocalDateTime.parse(cmpt.getEndTimeStr(), formatter); cmpt.setEnrollStatus(2);
// 判断当前时间是否在两个时间之间 }
// 报名中 } else {
if(now.isAfter(startTime) && now.isBefore(endTime)){ if (now.isAfter(startTime)) {
cmpt.setOpenStatus(1); cmpt.setOpenStatus(1);
} }
// 报名结束
if(now.isAfter(endTime)){
cmpt.setOpenStatus(2);
cmpt.setEnrollStatus(2);
} }
if (now.isBefore(startTime)) {
} else { cmpt.setOpenStatus(0);
// 报名中
if(now.isAfter(startTime)){
cmpt.setOpenStatus(1);
} }
} }
// 比赛未开始
if(now.isBefore(startTime)){
cmpt.setOpenStatus(0);
}
} }
// Object countStr = valueOperations.get("count:cmptid:" + cmpt.getCmptId()); }
// if (countStr != null) { Page<List<Cmpt>> result = PageFactory.setContent(list, page, count);
// cmpt.setEnroll(Integer.valueOf(countStr.toString())); // 写入响应级缓存,TTL 1分钟
// } else { try {
// // 查询计时名单人数 valueOperations.set(responseCacheKey, JSON.toJSONString(result), 1, TimeUnit.MINUTES);
// int cmptEnrollCount = remoteService.getCmptEnrollCount(cmpt.getCmptId()); } catch (Exception e) {
// // 查询线上报名人数 log.warn("赛事列表缓存写入失败: {}", e.getMessage());
// Map<Object, Object> cmptEnrolledMap = hashOperations.entries(CMPT_ENROLLED + cmpt.getGCmptId()); }
// int enrollCountOnline = cmptEnrolledMap == null ? 0 : cmptEnrolledMap.size(); return result;
//// log.info("===赛事[{}]线上报名人数:{},计时名单人数:{}", cmpt.getGCmptId(), enrollCountOnline, cmptEnrollCount);
// // 哪个人数多显示哪个
// if(enrollCountOnline > cmptEnrollCount){
// cmptEnrollCount = enrollCountOnline;
// }
// cmpt.setEnroll(cmptEnrollCount);
// valueOperations.set("count:cmptid:" + cmpt.getCmptId(), cmptEnrollCount + "", 30, TimeUnit.MINUTES);
// }
});
return PageFactory.setContent(list, page, count);
} }
/** /**
...@@ -324,7 +350,14 @@ public class CmptService { ...@@ -324,7 +350,14 @@ public class CmptService {
} }
public Partner getPartnerById(String parnter) { public Partner getPartnerById(String parnter) {
return cmptMapper.getParnterByParnterId(parnter); return cacheTemplate.get(
"cmpt:partner",
parnter,
Partner.class,
5L, TimeUnit.MINUTES,
10L, TimeUnit.MINUTES,
() -> cmptMapper.getParnterByParnterId(parnter)
);
} }
public void updateDayRank(int cmptId, int status, String rankName) { public void updateDayRank(int cmptId, int status, String rankName) {
......
...@@ -144,7 +144,7 @@ public class MicroApiController { ...@@ -144,7 +144,7 @@ public class MicroApiController {
@ApiResponses({@ApiResponse(code = 200, message = "成功"), @ApiResponse(code = -1, message = "失败"), @ApiResponse(code = 200, message = "成功", response = Result.class)}) @ApiResponses({@ApiResponse(code = 200, message = "成功"), @ApiResponse(code = -1, message = "失败"), @ApiResponse(code = 200, message = "成功", response = Result.class)})
@ApiOperation(value = "绑定赛事", notes = "用户与赛事绑定功能", tags = "赛事页面") @ApiOperation(value = "绑定赛事", notes = "用户与赛事绑定功能", tags = "赛事页面")
public Result<EnrollInfo> bind(@RequestParam("phone") String phone, @RequestParam("cmptId") Integer cmptId, @RequestParam(name = "userMapId", defaultValue = "-1", required = false) Integer userMapId, HttpServletRequest request) { public Result<EnrollInfo> bind(@RequestParam("phone") String phone, @RequestParam("cmptId") Integer cmptId, @RequestParam(name = "userMapId", defaultValue = "-1", required = false) Integer userMapId, HttpServletRequest request) {
int userId = org.apache.commons.lang3.math.NumberUtils.toInt(request.getParameter("userId"), -1); int userId = NumberUtils.toInt(request.getParameter("userId"), -1);
if (userId == -1) { if (userId == -1) {
return Result.fail() return Result.fail()
.setMsg("入参错误"); .setMsg("入参错误");
...@@ -431,21 +431,26 @@ public class MicroApiController { ...@@ -431,21 +431,26 @@ public class MicroApiController {
@GetMapping(value = "/customer/{cmptId}") @GetMapping(value = "/customer/{cmptId}")
@ApiResponses({@ApiResponse(code = 200, message = "成功"), @ApiResponse(code = -1, message = "失败", response = ObjectUtils.Null.class), @ApiResponse(code = 200, message = "成功", response = Result.class, responseContainer = "List")}) @ApiResponses({@ApiResponse(code = 200, message = "成功"), @ApiResponse(code = -1, message = "失败", response = ObjectUtils.Null.class), @ApiResponse(code = 200, message = "成功", response = Result.class, responseContainer = "List")})
@SuppressWarnings("unchecked")
public Result getCustomer(@PathVariable("cmptId") Integer cmptId) { public Result getCustomer(@PathVariable("cmptId") Integer cmptId) {
// 新增耗时计算 Map<String, String> result = cacheTemplate.get(
long startTime = System.currentTimeMillis(); "cmpt:customer",
Customer customer = customerService.getCusomerServiceInfo(cmptId); String.valueOf(cmptId),
FuncSetting setting = funcSettingService.getSettingByCmptId(cmptId); Map.class,
Map<String, String> result = new HashMap<>(); 2L, TimeUnit.MINUTES,
if (setting == null || setting.getCustomer() == null || setting.getCustomer() != 1) { 5L, TimeUnit.MINUTES,
result.put("mobile", ""); () -> {
} else { Customer customer = customerService.getCusomerServiceInfo(cmptId);
result.put("mobile", customer == null ? "" : customer.getMobile()); FuncSetting setting = funcSettingService.getSettingByCmptId(cmptId);
} Map<String, String> map = new HashMap<>();
// 新增耗时计算 if (setting == null || setting.getCustomer() == null || setting.getCustomer() != 1) {
long endTime = System.currentTimeMillis(); map.put("mobile", "");
long duration = endTime - startTime; } else {
// log.info("获取合作信息耗时:" + duration + "毫秒"); map.put("mobile", customer == null ? "" : customer.getMobile());
}
return map;
}
);
return Result.success() return Result.success()
.setContent(result); .setContent(result);
} }
...@@ -505,36 +510,24 @@ public class MicroApiController { ...@@ -505,36 +510,24 @@ public class MicroApiController {
@ApiResponses({@ApiResponse(code = 200, message = "成功"), @ApiResponse(code = -1, message = "失败", response = ObjectUtils.Null.class), @ApiResponse(code = 200, message = "成功", response = Result.class, responseContainer = "List")}) @ApiResponses({@ApiResponse(code = 200, message = "成功"), @ApiResponse(code = -1, message = "失败", response = ObjectUtils.Null.class), @ApiResponse(code = 200, message = "成功", response = Result.class, responseContainer = "List")})
@ApiImplicitParam(name = "enrollId", value = "赛事用户标识", required = false, paramType = "query") @ApiImplicitParam(name = "enrollId", value = "赛事用户标识", required = false, paramType = "query")
@ApiOperation(value = "赛事功能模块列表", notes = "赛事功能模块列表", tags = "赛事页面") @ApiOperation(value = "赛事功能模块列表", notes = "赛事功能模块列表", tags = "赛事页面")
@SuppressWarnings("unchecked")
public Result<List<Func>> func(@PathVariable("cmptId") int cmpId, HttpServletRequest request) { public Result<List<Func>> func(@PathVariable("cmptId") int cmpId, HttpServletRequest request) {
// String enrollId = request.getParameter("enrollId"); List<Func> list = cacheTemplate.get(
List<Func> list = cmptSetting.queryFuncs(cmpId); "cmpt:func",
// list=list String.valueOf(cmpId),
// .stream() List.class,
// .filter(v -> { 2L, TimeUnit.MINUTES,
// if (v.getNeedTime() == null || v.getNeedTime() != 1) { 5L, TimeUnit.MINUTES,
// return true; // 不需要时间校验的直接保留 () -> {
// } List<Func> result = cmptSetting.queryFuncs(cmpId);
// Date now = new Date(); result.forEach(v -> {
// // 检查开始时间条件(如果有) if (v.getOpenType().equals(0)) {
// boolean startValid = v.getStartTime() == null || !now.before(v.getStartTime()); v.setUrl(serverURL + "gk/cmpt/cmptSetting/page/" + v.getId());
// // 检查结束时间条件(如果有) }
// boolean endValid = v.getDeadTime() == null || now.before(v.getDeadTime()); });
// return result;
// // 必须同时满足开始和结束时间条件 }
// return startValid && endValid; );
// })
// .collect(Collectors.toList());
list.forEach(v -> {
if (v.getOpenType()
.equals(0)) {
v.setUrl(serverURL + "gk/cmpt/cmptSetting/page/" + v.getId());
}
});
/* if (StringUtils.isBlank(enrollId)) {
list = cmptSetting.queryAllFuncs(cmpId); // 查询 可对外的
} else { // 绑定后
list = cmptSetting.queryFuncs(cmpId); // 仅公开
}*/
return Result.success() return Result.success()
.setContent(list); .setContent(list);
} }
......
...@@ -3,6 +3,14 @@ spring.datasource.password=8N8F0cWb7rg88FWW ...@@ -3,6 +3,14 @@ spring.datasource.password=8N8F0cWb7rg88FWW
spring.datasource.username=geexek_mysql spring.datasource.username=geexek_mysql
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://rm-bp1xezgtx890ssl6r.mysql.rds.aliyuncs.com:3306/gksite?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true spring.datasource.url=jdbc:mysql://rm-bp1xezgtx890ssl6r.mysql.rds.aliyuncs.com:3306/gksite?autoReconnect=true&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
# Druid 连接池配置
spring.datasource.druid.max-active=150
spring.datasource.druid.min-idle=10
spring.datasource.druid.max-wait=3000
spring.datasource.druid.validation-query=SELECT 1
spring.datasource.druid.test-while-idle=true
spring.datasource.druid.time-between-eviction-runs-millis=60000
spring.datasource.druid.min-evictable-idle-time-millis=300000
mybatis.typeAliasesPackage=com.dlbridge.jadp.plugins mybatis.typeAliasesPackage=com.dlbridge.jadp.plugins
mybatis.mapperLocations=classpath:com/dlbridge/jadp/**/**.xml mybatis.mapperLocations=classpath:com/dlbridge/jadp/**/**.xml
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment