添加获取项目生产天数接口;添加定时任务,每天自动删除项目缓存

This commit is contained in:
lcj
2025-05-14 18:20:54 +08:00
parent dfcece5841
commit 4c238435d8
10 changed files with 169 additions and 31 deletions

View File

@ -5,6 +5,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* 启动程序
@ -14,6 +15,7 @@ import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)
@EnableScheduling
public class DromaraApplication {
public static void main(String[] args) {

View File

@ -52,32 +52,32 @@ spring:
url: jdbc:mysql://192.168.110.199:3306/energy?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: energy
password: fikwNsk8BidXSKe4
# # 从库数据源
# slave:
# lazy: true
# type: ${spring.datasource.type}
# driverClassName: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username:
# password:
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
# url: jdbc:oracle:thin:@//localhost:1521/XE
# username: ROOT
# password: root
# postgres:
# type: ${spring.datasource.type}
# driverClassName: org.postgresql.Driver
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
# username: root
# password: root
# sqlserver:
# type: ${spring.datasource.type}
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
# username: SA
# password: root
# # 从库数据源
# slave:
# lazy: true
# type: ${spring.datasource.type}
# driverClassName: com.mysql.cj.jdbc.Driver
# url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true
# username:
# password:
# oracle:
# type: ${spring.datasource.type}
# driverClassName: oracle.jdbc.OracleDriver
# url: jdbc:oracle:thin:@//localhost:1521/XE
# username: ROOT
# password: root
# postgres:
# type: ${spring.datasource.type}
# driverClassName: org.postgresql.Driver
# url: jdbc:postgresql://localhost:5432/postgres?useUnicode=true&characterEncoding=utf8&useSSL=true&autoReconnect=true&reWriteBatchedInserts=true
# username: root
# password: root
# sqlserver:
# type: ${spring.datasource.type}
# driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
# url: jdbc:sqlserver://localhost:1433;DatabaseName=tempdb;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true
# username: SA
# password: root
hikari:
# 最大连接池数量
maxPoolSize: 20
@ -206,7 +206,7 @@ justauth:
client-id: 449c4*********937************759
client-secret: ac7***********1e0************28d
redirect-uri: ${justauth.address}/social-callback?source=topiam
scopes: [openid, email, phone, profile]
scopes: [ openid, email, phone, profile ]
qq:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
@ -263,6 +263,7 @@ justauth:
client-id: 10**********6
client-secret: 1f7d08**********5b7**********29e
redirect-uri: ${justauth.address}/social-callback?source=gitlab
# 和风天气 https://dev.qweather.com/
weather:
key-id: T65EAABUXC
project-id: 2JTHPUQ5YY

View File

@ -0,0 +1,43 @@
package org.dromara.job.cycle;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.project.constant.BusProjectConstant;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.Set;
@Slf4j
@Component
public class IncSyncDeleteProjectCache {
@Resource
private StringRedisTemplate stringRedisTemplate;
// 每天凌晨 12 点执行
@Scheduled(cron = "0 0 0 * * ?")
public void run() {
log.info("执行定时任务:清理项目缓存");
// 构建扫描选项
ScanOptions scanOptions = ScanOptions.scanOptions()
.match(BusProjectConstant.PROJECT_CACHE_REDIS_KEY_PREFIX + "*")
.count(1000) // 每次 scan 的数量
.build();
Set<String> keysToDelete = new HashSet<>();
Cursor<byte[]> cursor = stringRedisTemplate.getConnectionFactory()
.getConnection()
.scan(scanOptions);
while (cursor.hasNext()) {
keysToDelete.add(new String(cursor.next()));
}
// 批量删除
if (!keysToDelete.isEmpty()) {
stringRedisTemplate.delete(keysToDelete);
}
}
}

View File

@ -3,6 +3,7 @@ package org.dromara.manager.weathermanager;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException;
import org.springframework.stereotype.Component;
@ -21,6 +22,7 @@ import java.util.Base64;
* @author lcj
* @date 2025/5/12 17:33
*/
@Slf4j
@Component
public class WeatherManager {
@ -48,6 +50,7 @@ public class WeatherManager {
.execute()) {
int status = result.getStatus();
if (status != HttpStatus.SUCCESS) {
log.error("获取天气失败,状态码:{}body{}", status, result.body());
throw new ServiceException("获取天气失败,状态码:" + status, HttpStatus.ERROR);
}
body = result.body();

View File

@ -11,4 +11,14 @@ public interface BusProjectConstant {
*/
String PROJECT_WEATHER_LIST_VO_REDIS_KEY_PREFIX = "project:weather:list";
/**
* 项目安全生产天数缓存的 Redis Key 前缀
*/
String PROJECT_SAFETY_DAYS_REDIS_KEY_PREFIX = "project:safetyDays";
/**
* 项目缓存的 Redis Key 前缀
*/
String PROJECT_CACHE_REDIS_KEY_PREFIX = "project";
}

View File

@ -21,6 +21,7 @@ import org.dromara.project.domain.req.project.BusProjectCreateReq;
import org.dromara.project.domain.req.project.BusProjectQueryReq;
import org.dromara.project.domain.req.project.BusProjectUpdateReq;
import org.dromara.project.domain.vo.project.BusProjectContractorListVo;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectVo;
import org.dromara.project.domain.vo.project.BusProjectWeatherVo;
import org.dromara.project.service.IBusProjectService;
@ -131,4 +132,13 @@ public class BusProjectController extends BaseController {
return R.ok(projectService.getWeather(id));
}
/**
* 查询项目安全天数
*/
@GetMapping("/safetyDay/{id}")
public R<BusProjectSafetyDayVo> getSafetyDay(@NotNull(message = "主键不能为空")
@PathVariable Long id) {
return R.ok(projectService.getSafetyDay(id));
}
}

View File

@ -0,0 +1,23 @@
package org.dromara.project.domain.vo.project;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
/**
* @author lcj
* @date 2025/5/14 9:34
*/
@Data
public class BusProjectSafetyDayVo implements Serializable {
@Serial
private static final long serialVersionUID = -1479490255029878315L;
/**
* 安全生产天数
*/
private Integer safetyDay;
}

View File

@ -3,7 +3,6 @@ package org.dromara.project.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.validation.constraints.NotNull;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.project.domain.BusProject;
@ -11,6 +10,7 @@ import org.dromara.project.domain.req.project.BusProjectCreateReq;
import org.dromara.project.domain.req.project.BusProjectQueryReq;
import org.dromara.project.domain.req.project.BusProjectUpdateReq;
import org.dromara.project.domain.vo.project.BusProjectContractorListVo;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectVo;
import org.dromara.project.domain.vo.project.BusProjectWeatherVo;
@ -130,4 +130,12 @@ public interface IBusProjectService extends IService<BusProject> {
*/
List<BusProjectWeatherVo> getWeather(Long id);
/**
* 获取项目安全天数
*
* @param id 项目id
* @return 安全天数
*/
BusProjectSafetyDayVo getSafetyDay(Long id);
}

View File

@ -20,6 +20,7 @@ import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.domain.vo.IdAndNameVO;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
@ -36,6 +37,7 @@ import org.dromara.project.domain.req.project.BusProjectCreateReq;
import org.dromara.project.domain.req.project.BusProjectQueryReq;
import org.dromara.project.domain.req.project.BusProjectUpdateReq;
import org.dromara.project.domain.vo.project.BusProjectContractorListVo;
import org.dromara.project.domain.vo.project.BusProjectSafetyDayVo;
import org.dromara.project.domain.vo.project.BusProjectVo;
import org.dromara.project.domain.vo.project.BusProjectWeatherVo;
import org.dromara.project.mapper.BusProjectMapper;
@ -85,8 +87,8 @@ public class BusProjectServiceImpl extends ServiceImpl<BusProjectMapper, BusProj
private final Cache<String, String> WEATHER_CACHE =
Caffeine.newBuilder().initialCapacity(1024)
.maximumSize(10000L)
// 缓存 30 分钟移除
.expireAfterWrite(30L, TimeUnit.MINUTES)
// 缓存 60 分钟移除
.expireAfterWrite(60L, TimeUnit.MINUTES)
.build();
/**
@ -532,12 +534,45 @@ public class BusProjectServiceImpl extends ServiceImpl<BusProjectMapper, BusProj
// 更新本地缓存
WEATHER_CACHE.put(cacheKey, cacheValue);
// 更新 Redis 缓存,设置 5 - 10 分钟随机过期,防止雪崩
int cacheExpireTime = 30 * 60 + RandomUtil.randomInt(0, 300);
int cacheExpireTime = 60 * 60 + RandomUtil.randomInt(0, 300);
valueOps.set(cacheKey, cacheValue, cacheExpireTime, TimeUnit.SECONDS);
// 返回结果
return weatherList;
}
/**
* 获取项目安全天数
*
* @param id 项目id
* @return 安全天数
*/
@Override
public BusProjectSafetyDayVo getSafetyDay(Long id) {
// 构建缓存 key
String cacheKey = String.format("%s:%s", BusProjectConstant.PROJECT_SAFETY_DAYS_REDIS_KEY_PREFIX, id);
// 查询分布式缓存Redis
ValueOperations<String, String> valueOps = stringRedisTemplate.opsForValue();
String cachedValue = valueOps.get(cacheKey);
if (cachedValue != null) {
// 如果命中Redis返回结果
return JSONUtil.toBean(cachedValue, BusProjectSafetyDayVo.class);
}
BusProject project = this.getById(id);
if (project == null) {
throw new ServiceException("项目信息不存在", HttpStatus.NOT_FOUND);
}
int days = DateUtils.differentDaysByMillisecond(project.getCreateTime(), new Date());
BusProjectSafetyDayVo safetyDayVo = new BusProjectSafetyDayVo();
safetyDayVo.setSafetyDay(days);
// 更新缓存
String cacheValue = JSONUtil.toJsonStr(safetyDayVo);
// 更新 Redis 缓存
int cacheExpireTime = 12;
valueOps.set(cacheKey, cacheValue, cacheExpireTime, TimeUnit.HOURS);
// 返回结果
return safetyDayVo;
}
/**
* 根据天气图标获取天气类别
*

View File

@ -150,6 +150,9 @@ public class HseSafetyInspectionServiceImpl extends ServiceImpl<HseSafetyInspect
List<HseTeamMeeting> teamMeetings = teamMeetingService.lambdaQuery()
.eq(HseTeamMeeting::getProjectId, projectId)
.list();
if (CollUtil.isEmpty(teamMeetings)){
return gisVo;
}
// 获取最新的班组列表
List<HseTeamMeeting> topList = teamMeetings.stream()
.sorted(Comparator.comparing(HseTeamMeeting::getCreateTime).reversed())