deptIdList = user.getDeptIdList();
+ if (CollUtil.isNotEmpty(deptIdList)) {
+ sqlFilter.append(tableAlias).append(dataFilter.deptId());
+
+ sqlFilter.append(" in(").append(StringUtils.join(deptIdList, ",")).append(")");
+ }
+
+ //查询本人数据
+ if (CollUtil.isNotEmpty(deptIdList)) {
+ sqlFilter.append(" or ");
+ }
+ sqlFilter.append(tableAlias).append(dataFilter.userId()).append("=").append(user.getId());
+
+ sqlFilter.append(")");
+
+ return sqlFilter.toString();
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/common/aspect/LogOperationAspect.java b/system-admin/src/main/java/com/weather/common/aspect/LogOperationAspect.java
new file mode 100644
index 0000000..59e9e57
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/aspect/LogOperationAspect.java
@@ -0,0 +1,103 @@
+
+package com.weather.common.aspect;
+
+import com.weather.common.annotation.LogOperation;
+import com.weather.common.utils.HttpContextUtils;
+import com.weather.common.utils.IpUtils;
+import com.weather.common.utils.JsonUtils;
+import com.weather.modules.log.entity.SysLogOperationEntity;
+import com.weather.modules.log.enums.OperationStatusEnum;
+import com.weather.modules.log.service.SysLogOperationService;
+import com.weather.modules.security.user.SecurityUser;
+import com.weather.modules.security.user.UserDetail;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.AllArgsConstructor;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.http.HttpHeaders;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+/**
+ * 操作日志,切面处理类
+ *
+ * @author 123
+ */
+@Aspect
+@Component
+@AllArgsConstructor
+public class LogOperationAspect {
+ private final SysLogOperationService sysLogOperationService;
+
+ @Pointcut("@annotation(com.weather.common.annotation.LogOperation)")
+ public void logPointCut() {
+
+ }
+
+ @Around("logPointCut()")
+ public Object around(ProceedingJoinPoint point) throws Throwable {
+ long beginTime = System.currentTimeMillis();
+ try {
+ //执行方法
+ Object result = point.proceed();
+
+ //执行时长(毫秒)
+ long time = System.currentTimeMillis() - beginTime;
+ //保存日志
+ saveLog(point, time, OperationStatusEnum.SUCCESS.value());
+
+ return result;
+ } catch (Exception e) {
+ //执行时长(毫秒)
+ long time = System.currentTimeMillis() - beginTime;
+ //保存日志
+ saveLog(point, time, OperationStatusEnum.FAIL.value());
+
+ throw e;
+ }
+ }
+
+ private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status) throws Exception {
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+ Method method = joinPoint.getTarget().getClass().getDeclaredMethod(signature.getName(), signature.getParameterTypes());
+ LogOperation annotation = method.getAnnotation(LogOperation.class);
+
+ SysLogOperationEntity log = new SysLogOperationEntity();
+ if (annotation != null) {
+ //注解上的描述
+ log.setOperation(annotation.value());
+ }
+
+ //登录用户信息
+ UserDetail user = SecurityUser.getUser();
+ if (user != null) {
+ log.setCreatorName(user.getUsername());
+ }
+
+ log.setStatus(status);
+ log.setRequestTime((int) time);
+
+ //请求相关信息
+ HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
+ log.setIp(IpUtils.getIpAddr(request));
+ log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
+ log.setRequestUri(request.getRequestURI());
+ log.setRequestMethod(request.getMethod());
+
+ //请求参数
+ Object[] args = joinPoint.getArgs();
+ try {
+ String params = JsonUtils.toJsonString(args[0]);
+ log.setRequestParams(params);
+ } catch (Exception e) {
+
+ }
+
+ //保存到DB
+ sysLogOperationService.save(log);
+ }
+}
diff --git a/system-admin/src/main/java/com/weather/common/config/FileServerConfig.java b/system-admin/src/main/java/com/weather/common/config/FileServerConfig.java
new file mode 100644
index 0000000..35cc475
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/config/FileServerConfig.java
@@ -0,0 +1,15 @@
+package com.weather.common.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class FileServerConfig implements WebMvcConfigurer {
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ // 文件访问通过 FileDownloadController 提供,支持 Shiro 权限控制
+ // 如需直接静态资源映射,可在此配置
+ }
+}
diff --git a/system-admin/src/main/java/com/weather/common/config/JacksonConfig.java b/system-admin/src/main/java/com/weather/common/config/JacksonConfig.java
new file mode 100644
index 0000000..8e54e39
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/config/JacksonConfig.java
@@ -0,0 +1,26 @@
+package com.weather.common.config;
+
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * Jackson 配置
+ *
+ * 全局 Long → String 序列化,解决 Snowflake 19位ID 在 JavaScript 端精度丢失问题。
+ * JS Number 安全整数上限为 2^53-1 (约16位),Snowflake ASSIGN_ID 为19位,超出精度范围。
+ *
+ * @author 123
+ */
+@Configuration
+public class JacksonConfig {
+
+ @Bean
+ public Jackson2ObjectMapperBuilderCustomizer jacksonLongToStringCustomizer() {
+ return builder -> {
+ builder.serializerByType(Long.class, ToStringSerializer.instance);
+ builder.serializerByType(long.class, ToStringSerializer.instance);
+ };
+ }
+}
diff --git a/system-admin/src/main/java/com/weather/common/config/MybatisPlusConfig.java b/system-admin/src/main/java/com/weather/common/config/MybatisPlusConfig.java
new file mode 100644
index 0000000..c36b4b7
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/config/MybatisPlusConfig.java
@@ -0,0 +1,37 @@
+
+
+package com.weather.common.config;
+
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import com.weather.common.interceptor.DataFilterInterceptor;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * mybatis-plus配置
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Configuration
+public class MybatisPlusConfig {
+
+ @Bean
+ public MybatisPlusInterceptor mybatisPlusInterceptor() {
+ MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
+ // 数据权限
+ mybatisPlusInterceptor.addInnerInterceptor(new DataFilterInterceptor());
+ // 分页插件
+ mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
+ // 乐观锁
+ mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
+ // 防止全表更新与删除
+ mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
+
+ return mybatisPlusInterceptor;
+ }
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/common/config/SwaggerConfig.java b/system-admin/src/main/java/com/weather/common/config/SwaggerConfig.java
new file mode 100644
index 0000000..2465cd3
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/config/SwaggerConfig.java
@@ -0,0 +1,43 @@
+
+package com.weather.common.config;
+
+import com.weather.common.constant.Constant;
+import io.swagger.v3.oas.models.OpenAPI;
+import io.swagger.v3.oas.models.info.Info;
+import io.swagger.v3.oas.models.security.SecurityRequirement;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Swagger配置
+ *
+ * @author 123
+ */
+@Configuration
+public class SwaggerConfig {
+ @Bean
+ public OpenAPI createRestApi() {
+ return new OpenAPI()
+ .info(apiInfo())
+ .security(security());
+ }
+
+ private Info apiInfo() {
+ return new Info()
+ .title("weather")
+ .description("weather-admin文档")
+ .version("5.x");
+ }
+
+ private List security() {
+ SecurityRequirement key = new SecurityRequirement();
+ key.addList(Constant.TOKEN_HEADER, Constant.TOKEN_HEADER);
+
+ List list = new ArrayList<>();
+ list.add(key);
+ return list;
+ }
+}
diff --git a/system-admin/src/main/java/com/weather/common/exception/CustomExceptionHandler.java b/system-admin/src/main/java/com/weather/common/exception/CustomExceptionHandler.java
new file mode 100644
index 0000000..ab4d09f
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/exception/CustomExceptionHandler.java
@@ -0,0 +1,91 @@
+
+package com.weather.common.exception;
+
+import cn.hutool.core.map.MapUtil;
+import com.weather.common.utils.HttpContextUtils;
+import com.weather.common.utils.IpUtils;
+import com.weather.common.utils.JsonUtils;
+import com.weather.common.utils.Result;
+import com.weather.modules.log.entity.SysLogErrorEntity;
+import com.weather.modules.log.service.SysLogErrorService;
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.shiro.authz.UnauthorizedException;
+import org.springframework.dao.DuplicateKeyException;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+import java.util.Map;
+
+
+/**
+ * 异常处理器
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Slf4j
+@RestControllerAdvice
+@AllArgsConstructor
+public class CustomExceptionHandler {
+ private final SysLogErrorService sysLogErrorService;
+
+ /**
+ * 处理自定义异常
+ */
+ @ExceptionHandler(CommonException.class)
+ public Result handleRenException(CommonException ex) {
+ Result result = new Result();
+ result.error(ex.getCode(), ex.getMsg());
+
+ return result;
+ }
+
+ @ExceptionHandler(DuplicateKeyException.class)
+ public Result handleDuplicateKeyException(DuplicateKeyException ex) {
+ Result result = new Result();
+ result.error(ErrorCode.DB_RECORD_EXISTS);
+
+ return result;
+ }
+
+ @ExceptionHandler(UnauthorizedException.class)
+ public Result handleUnauthorizedException(UnauthorizedException ex) {
+ return new Result().error(ErrorCode.UNAUTHORIZED);
+ }
+
+ @ExceptionHandler(Exception.class)
+ public Result handleException(Exception ex) {
+ log.error(ex.getMessage(), ex);
+
+ saveLog(ex);
+
+ return new Result().error();
+ }
+
+ /**
+ * 保存异常日志
+ */
+ private void saveLog(Exception ex) {
+ SysLogErrorEntity log = new SysLogErrorEntity();
+
+ //请求相关信息
+ HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
+ log.setIp(IpUtils.getIpAddr(request));
+ log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT));
+ log.setRequestUri(request.getRequestURI());
+ log.setRequestMethod(request.getMethod());
+ Map params = HttpContextUtils.getParameterMap(request);
+ if (MapUtil.isNotEmpty(params)) {
+ log.setRequestParams(JsonUtils.toJsonString(params));
+ }
+
+ //异常信息
+ log.setErrorInfo(ExceptionUtils.getErrorStackTrace(ex));
+
+ //保存
+ sysLogErrorService.save(log);
+ }
+}
diff --git a/system-admin/src/main/java/com/weather/common/handler/FieldMetaObjectHandler.java b/system-admin/src/main/java/com/weather/common/handler/FieldMetaObjectHandler.java
new file mode 100644
index 0000000..422a64a
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/handler/FieldMetaObjectHandler.java
@@ -0,0 +1,52 @@
+
+
+package com.weather.common.handler;
+
+import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
+import com.weather.modules.security.user.SecurityUser;
+import com.weather.modules.security.user.UserDetail;
+import org.apache.ibatis.reflection.MetaObject;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+
+/**
+ * 公共字段,自动填充值
+ *
+ * @author 123
+ */
+@Component
+public class FieldMetaObjectHandler implements MetaObjectHandler {
+ private final static String CREATE_DATE = "createDate";
+ private final static String CREATOR = "creator";
+ private final static String UPDATE_DATE = "updateDate";
+ private final static String UPDATER = "updater";
+ private final static String DEPT_ID = "deptId";
+
+ @Override
+ public void insertFill(MetaObject metaObject) {
+ UserDetail user = SecurityUser.getUser();
+ Date date = new Date();
+
+ //创建者
+ strictInsertFill(metaObject, CREATOR, Long.class, user.getId());
+ //创建时间
+ strictInsertFill(metaObject, CREATE_DATE, Date.class, date);
+
+ //创建者所属部门
+ strictInsertFill(metaObject, DEPT_ID, Long.class, user.getDeptId());
+
+ //更新者
+ strictInsertFill(metaObject, UPDATER, Long.class, user.getId());
+ //更新时间
+ strictInsertFill(metaObject, UPDATE_DATE, Date.class, date);
+ }
+
+ @Override
+ public void updateFill(MetaObject metaObject) {
+ //更新者
+ strictUpdateFill(metaObject, UPDATER, Long.class, SecurityUser.getUserId());
+ //更新时间
+ strictUpdateFill(metaObject, UPDATE_DATE, Date.class, new Date());
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/common/interceptor/DataFilterInterceptor.java b/system-admin/src/main/java/com/weather/common/interceptor/DataFilterInterceptor.java
new file mode 100644
index 0000000..0cabe84
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/interceptor/DataFilterInterceptor.java
@@ -0,0 +1,83 @@
+
+
+package com.weather.common.interceptor;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.expression.Expression;
+import net.sf.jsqlparser.expression.StringValue;
+import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+import net.sf.jsqlparser.statement.select.Select;
+import org.apache.ibatis.executor.Executor;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.ResultHandler;
+import org.apache.ibatis.session.RowBounds;
+
+import java.util.Map;
+
+/**
+ * 数据过滤
+ *
+ * @author 123
+ */
+public class DataFilterInterceptor implements InnerInterceptor {
+
+ @Override
+ public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
+ DataScope scope = getDataScope(parameter);
+ // 不进行数据过滤
+ if(scope == null || StrUtil.isBlank(scope.getSqlFilter())){
+ return;
+ }
+
+ // 拼接新SQL
+ String buildSql = getSelect(boundSql.getSql(), scope);
+
+ // 重写SQL
+ PluginUtils.mpBoundSql(boundSql).sql(buildSql);
+ }
+
+ private DataScope getDataScope(Object parameter){
+ if (parameter == null){
+ return null;
+ }
+
+ // 判断参数里是否有DataScope对象
+ if (parameter instanceof Map) {
+ Map, ?> parameterMap = (Map, ?>) parameter;
+ for (Map.Entry entry : parameterMap.entrySet()) {
+ if (entry.getValue() != null && entry.getValue() instanceof DataScope) {
+ return (DataScope) entry.getValue();
+ }
+ }
+ } else if (parameter instanceof DataScope) {
+ return (DataScope) parameter;
+ }
+
+ return null;
+ }
+
+ private String getSelect(String buildSql, DataScope scope){
+ try {
+ Select select = (Select) CCJSqlParserUtil.parse(buildSql);
+ PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
+
+ Expression expression = plainSelect.getWhere();
+ if(expression == null){
+ plainSelect.setWhere(new StringValue(scope.getSqlFilter()));
+ }else{
+ AndExpression andExpression = new AndExpression(expression, new StringValue(scope.getSqlFilter()));
+ plainSelect.setWhere(andExpression);
+ }
+
+ return select.toString().replaceAll("'", "");
+ }catch (JSQLParserException e){
+ return buildSql;
+ }
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/common/interceptor/DataScope.java b/system-admin/src/main/java/com/weather/common/interceptor/DataScope.java
new file mode 100644
index 0000000..5638e31
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/interceptor/DataScope.java
@@ -0,0 +1,30 @@
+
+
+package com.weather.common.interceptor;
+
+/**
+ * 数据范围
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+public class DataScope {
+ private String sqlFilter;
+
+ public DataScope(String sqlFilter) {
+ this.sqlFilter = sqlFilter;
+ }
+
+ public String getSqlFilter() {
+ return sqlFilter;
+ }
+
+ public void setSqlFilter(String sqlFilter) {
+ this.sqlFilter = sqlFilter;
+ }
+
+ @Override
+ public String toString() {
+ return this.sqlFilter;
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/common/utils/ExcelUtils.java b/system-admin/src/main/java/com/weather/common/utils/ExcelUtils.java
new file mode 100644
index 0000000..c9d915e
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/utils/ExcelUtils.java
@@ -0,0 +1,68 @@
+
+package com.weather.common.utils;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.URLUtil;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.converters.longconverter.LongStringConverter;
+import jakarta.servlet.http.HttpServletResponse;
+import org.springframework.beans.BeanUtils;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * excel工具类
+ *
+ * @author 123
+ */
+public class ExcelUtils {
+
+ /**
+ * Excel导出
+ *
+ * @param response response
+ * @param fileName 文件名
+ * @param sheetName sheetName
+ * @param list 数据List
+ * @param pojoClass 对象Class
+ */
+ public static void exportExcel(HttpServletResponse response, String fileName, String sheetName, List> list,
+ Class> pojoClass) throws IOException {
+ if (StrUtil.isBlank(fileName)) {
+ //当前日期
+ fileName = DateUtils.format(new Date());
+ }
+
+ response.setContentType("application/vnd.ms-excel");
+ response.setCharacterEncoding("UTF-8");
+ fileName = URLUtil.encode(fileName, StandardCharsets.UTF_8);
+ response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+ EasyExcel.write(response.getOutputStream(), pojoClass).registerConverter(new LongStringConverter()).sheet(sheetName).doWrite(list);
+ }
+
+ /**
+ * Excel导出,先sourceList转换成List,再导出
+ *
+ * @param response response
+ * @param fileName 文件名
+ * @param sheetName sheetName
+ * @param sourceList 原数据List
+ * @param targetClass 目标对象Class
+ */
+ public static void exportExcelToTarget(HttpServletResponse response, String fileName, String sheetName, List> sourceList,
+ Class> targetClass) throws Exception {
+ List targetList = new ArrayList<>(sourceList.size());
+ for (Object source : sourceList) {
+ Object target = targetClass.newInstance();
+ BeanUtils.copyProperties(source, target);
+ targetList.add(target);
+ }
+
+ exportExcel(response, fileName, sheetName, targetList, targetClass);
+ }
+
+}
diff --git a/system-admin/src/main/java/com/weather/common/utils/TimeUtils.java b/system-admin/src/main/java/com/weather/common/utils/TimeUtils.java
new file mode 100644
index 0000000..9baabd5
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/utils/TimeUtils.java
@@ -0,0 +1,14 @@
+package com.weather.common.utils;
+
+import java.time.LocalDate;
+import java.time.ZoneId;
+import java.util.Date;
+
+public class TimeUtils {
+
+ public static LocalDate convertToLocalDateTime(Date date) {
+ return date.toInstant()
+ .atZone(ZoneId.systemDefault())
+ .toLocalDate();
+ }
+}
diff --git a/system-admin/src/main/java/com/weather/common/validator/group/AliyunGroup.java b/system-admin/src/main/java/com/weather/common/validator/group/AliyunGroup.java
new file mode 100644
index 0000000..cf29693
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/validator/group/AliyunGroup.java
@@ -0,0 +1,11 @@
+
+
+package com.weather.common.validator.group;
+
+/**
+ * 阿里云
+ *
+ * @author 123
+ */
+public interface AliyunGroup {
+}
diff --git a/system-admin/src/main/java/com/weather/common/validator/group/QcloudGroup.java b/system-admin/src/main/java/com/weather/common/validator/group/QcloudGroup.java
new file mode 100644
index 0000000..0b328a8
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/validator/group/QcloudGroup.java
@@ -0,0 +1,11 @@
+
+
+package com.weather.common.validator.group;
+
+/**
+ * 腾讯云
+ *
+ * @author 123
+ */
+public interface QcloudGroup {
+}
diff --git a/system-admin/src/main/java/com/weather/common/validator/group/QiniuGroup.java b/system-admin/src/main/java/com/weather/common/validator/group/QiniuGroup.java
new file mode 100644
index 0000000..1558375
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/common/validator/group/QiniuGroup.java
@@ -0,0 +1,11 @@
+
+
+package com.weather.common.validator.group;
+
+/**
+ * 七牛
+ *
+ * @author 123
+ */
+public interface QiniuGroup {
+}
diff --git a/system-admin/src/main/java/com/weather/modules/job/config/ScheduleConfig.java b/system-admin/src/main/java/com/weather/modules/job/config/ScheduleConfig.java
new file mode 100644
index 0000000..8cb1a0b
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/config/ScheduleConfig.java
@@ -0,0 +1,66 @@
+///**
+// * Copyright (c) 2018 人人开源 All rights reserved.
+// *
+// * https://www.renren.io
+// *
+// * 版权所有,侵权必究!
+// */
+//
+//package io.weather.modules.job.config;
+//
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.scheduling.quartz.SchedulerFactoryBean;
+//
+//import javax.sql.DataSource;
+//import java.util.Properties;
+//
+///**
+// * 定时任务配置(备注:集群需要打开注释)
+// *
+// * @author 123
+// */
+//@Configuration
+//public class ScheduleConfig {
+//
+// @Bean
+// public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) {
+// SchedulerFactoryBean factory = new SchedulerFactoryBean();
+// factory.setDataSource(dataSource);
+//
+// //quartz参数
+// Properties prop = new Properties();
+// prop.put("org.quartz.scheduler.instanceName", "RenrenScheduler");
+// prop.put("org.quartz.scheduler.instanceId", "AUTO");
+// //线程池配置
+// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
+// prop.put("org.quartz.threadPool.threadCount", "20");
+// prop.put("org.quartz.threadPool.threadPriority", "5");
+// //JobStore配置
+// prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");
+// //集群配置
+// prop.put("org.quartz.jobStore.isClustered", "true");
+// prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000");
+// prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1");
+//
+// prop.put("org.quartz.jobStore.misfireThreshold", "12000");
+// prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_");
+// prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?");
+//
+// //PostgreSQL数据库,需要打开此注释
+// //prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate");
+//
+// factory.setQuartzProperties(prop);
+//
+// factory.setSchedulerName("RenrenScheduler");
+// //延时启动
+// factory.setStartupDelay(30);
+// factory.setApplicationContextSchedulerContextKey("applicationContextKey");
+// //可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了
+// factory.setOverwriteExistingJobs(true);
+// //设置自动启动,默认为true
+// factory.setAutoStartup(true);
+//
+// return factory;
+// }
+//}
diff --git a/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobController.java b/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobController.java
new file mode 100644
index 0000000..57c5cb7
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobController.java
@@ -0,0 +1,126 @@
+
+package com.weather.modules.job.controller;
+
+import com.weather.common.annotation.LogOperation;
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.utils.Result;
+import com.weather.common.validator.ValidatorUtils;
+import com.weather.common.validator.group.AddGroup;
+import com.weather.common.validator.group.DefaultGroup;
+import com.weather.common.validator.group.UpdateGroup;
+import com.weather.modules.job.dto.ScheduleJobDTO;
+import com.weather.modules.job.service.ScheduleJobService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * 定时任务
+ *
+ * @author 123
+ */
+@RestController
+@RequestMapping("/sys/schedule")
+@Tag(name = "定时任务")
+@AllArgsConstructor
+public class ScheduleJobController {
+ private final ScheduleJobService scheduleJobService;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = "beanName", description = "beanName", in = ParameterIn.QUERY, ref = "String")
+ })
+ @RequiresPermissions("sys:schedule:page")
+ public Result> page(@Parameter(hidden = true) @RequestParam Map params) {
+ PageData page = scheduleJobService.page(params);
+
+ return new Result>().ok(page);
+ }
+
+ @GetMapping("{id}")
+ @Operation(summary = "信息")
+ @RequiresPermissions("sys:schedule:info")
+ public Result info(@PathVariable("id") Long id) {
+ ScheduleJobDTO schedule = scheduleJobService.get(id);
+
+ return new Result().ok(schedule);
+ }
+
+ @PostMapping
+ @Operation(summary = "保存")
+ @LogOperation("保存")
+ @RequiresPermissions("sys:schedule:save")
+ public Result save(@RequestBody ScheduleJobDTO dto) {
+ ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class);
+
+ scheduleJobService.save(dto);
+
+ return new Result();
+ }
+
+ @PutMapping
+ @Operation(summary = "修改")
+ @LogOperation("修改")
+ @RequiresPermissions("sys:schedule:update")
+ public Result update(@RequestBody ScheduleJobDTO dto) {
+ ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class);
+
+ scheduleJobService.update(dto);
+
+ return new Result();
+ }
+
+ @DeleteMapping
+ @Operation(summary = "删除")
+ @LogOperation("删除")
+ @RequiresPermissions("sys:schedule:delete")
+ public Result delete(@RequestBody Long[] ids) {
+ scheduleJobService.deleteBatch(ids);
+
+ return new Result();
+ }
+
+ @PutMapping("/run")
+ @Operation(summary = "立即执行")
+ @LogOperation("立即执行")
+ @RequiresPermissions("sys:schedule:run")
+ public Result run(@RequestBody Long[] ids) {
+ scheduleJobService.run(ids);
+
+ return new Result();
+ }
+
+ @PutMapping("/pause")
+ @Operation(summary = "暂停")
+ @LogOperation("暂停")
+ @RequiresPermissions("sys:schedule:pause")
+ public Result pause(@RequestBody Long[] ids) {
+ scheduleJobService.pause(ids);
+
+ return new Result();
+ }
+
+ @PutMapping("/resume")
+ @Operation(summary = "恢复")
+ @LogOperation("恢复")
+ @RequiresPermissions("sys:schedule:resume")
+ public Result resume(@RequestBody Long[] ids) {
+ scheduleJobService.resume(ids);
+
+ return new Result();
+ }
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobLogController.java b/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobLogController.java
new file mode 100644
index 0000000..c915431
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/controller/ScheduleJobLogController.java
@@ -0,0 +1,56 @@
+
+package com.weather.modules.job.controller;
+
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.utils.Result;
+import com.weather.modules.job.dto.ScheduleJobLogDTO;
+import com.weather.modules.job.service.ScheduleJobLogService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Map;
+
+/**
+ * 定时任务日志
+ *
+ * @author 123
+ */
+@RestController
+@RequestMapping("/sys/scheduleLog")
+@Tag(name = "定时任务日志")
+@AllArgsConstructor
+public class ScheduleJobLogController {
+ private final ScheduleJobLogService scheduleJobLogService;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = "jobId", description = "jobId", in = ParameterIn.QUERY, ref = "String")
+ })
+ @RequiresPermissions("sys:schedule:log")
+ public Result> page(@Parameter(hidden = true) @RequestParam Map params) {
+ PageData page = scheduleJobLogService.page(params);
+
+ return new Result>().ok(page);
+ }
+
+ @GetMapping("{id}")
+ @Operation(summary = "信息")
+ @RequiresPermissions("sys:schedule:log")
+ public Result info(@PathVariable("id") Long id) {
+ ScheduleJobLogDTO log = scheduleJobLogService.get(id);
+
+ return new Result().ok(log);
+ }
+}
diff --git a/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobDao.java b/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobDao.java
new file mode 100644
index 0000000..4a74239
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobDao.java
@@ -0,0 +1,23 @@
+
+
+package com.weather.modules.job.dao;
+
+import com.weather.common.dao.BaseDao;
+import com.weather.modules.job.entity.ScheduleJobEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Map;
+
+/**
+ * 定时任务
+ *
+ * @author 123
+ */
+@Mapper
+public interface ScheduleJobDao extends BaseDao {
+
+ /**
+ * 批量更新状态
+ */
+ int updateBatch(Map map);
+}
diff --git a/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobLogDao.java b/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobLogDao.java
new file mode 100644
index 0000000..e5faf6c
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/dao/ScheduleJobLogDao.java
@@ -0,0 +1,17 @@
+
+
+package com.weather.modules.job.dao;
+
+import com.weather.common.dao.BaseDao;
+import com.weather.modules.job.entity.ScheduleJobLogEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 定时任务日志
+ *
+ * @author 123
+ */
+@Mapper
+public interface ScheduleJobLogDao extends BaseDao {
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobDTO.java b/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobDTO.java
new file mode 100644
index 0000000..484209a
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobDTO.java
@@ -0,0 +1,57 @@
+
+
+package com.weather.modules.job.dto;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.weather.common.validator.group.AddGroup;
+import com.weather.common.validator.group.DefaultGroup;
+import com.weather.common.validator.group.UpdateGroup;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Null;
+import lombok.Data;
+import org.hibernate.validator.constraints.Range;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 定时任务
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Data
+@Schema(title = "定时任务")
+public class ScheduleJobDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(title = "id")
+ @Null(message="{id.null}", groups = AddGroup.class)
+ @NotNull(message="{id.require}", groups = UpdateGroup.class)
+ private Long id;
+
+ @Schema(title = "spring bean名称")
+ @NotBlank(message = "{schedule.bean.require}", groups = DefaultGroup.class)
+ private String beanName;
+
+ @Schema(title = "参数")
+ private String params;
+
+ @Schema(title = "cron表达式")
+ @NotBlank(message = "{schedule.cron.require}", groups = DefaultGroup.class)
+ private String cronExpression;
+
+ @Schema(title = "任务状态 0:暂停 1:正常")
+ @Range(min=0, max=1, message = "{schedule.status.range}", groups = DefaultGroup.class)
+ private Integer status;
+
+ @Schema(title = "备注")
+ private String remark;
+
+ @Schema(title = "创建时间")
+ @JsonProperty(access = JsonProperty.Access.READ_ONLY)
+ private Date createDate;
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobLogDTO.java b/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobLogDTO.java
new file mode 100644
index 0000000..9f53090
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/dto/ScheduleJobLogDTO.java
@@ -0,0 +1,46 @@
+
+
+package com.weather.modules.job.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 定时任务日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Data
+@Schema(title = "定时任务日志")
+public class ScheduleJobLogDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(title = "id")
+ private Long id;
+
+ @Schema(title = "任务id")
+ private Long jobId;
+
+ @Schema(title = "spring bean名称")
+ private String beanName;
+
+ @Schema(title = "参数")
+ private String params;
+
+ @Schema(title = "任务状态 0:失败 1:成功")
+ private Integer status;
+
+ @Schema(title = "失败信息")
+ private String error;
+
+ @Schema(title = "耗时(单位:毫秒)")
+ private Integer times;
+
+ @Schema(title = "创建时间")
+ private Date createDate;
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobEntity.java b/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobEntity.java
new file mode 100644
index 0000000..1369941
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobEntity.java
@@ -0,0 +1,55 @@
+
+
+package com.weather.modules.job.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.weather.common.entity.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.Date;
+
+/**
+ * 定时任务
+ *
+ * @author 123
+ */
+@Data
+@EqualsAndHashCode(callSuper=false)
+@TableName("schedule_job")
+public class ScheduleJobEntity extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * spring bean名称
+ */
+ private String beanName;
+ /**
+ * 参数
+ */
+ private String params;
+ /**
+ * cron表达式
+ */
+ private String cronExpression;
+ /**
+ * 任务状态 0:暂停 1:正常
+ */
+ private Integer status;
+ /**
+ * 备注
+ */
+ private String remark;
+ /**
+ * 更新者
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Long updater;
+ /**
+ * 更新时间
+ */
+ @TableField(fill = FieldFill.INSERT_UPDATE)
+ private Date updateDate;
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobLogEntity.java b/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobLogEntity.java
new file mode 100644
index 0000000..b889a8a
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/entity/ScheduleJobLogEntity.java
@@ -0,0 +1,56 @@
+
+
+package com.weather.modules.job.entity;
+
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 定时任务日志
+ *
+ * @author 123
+ */
+@Data
+@TableName("schedule_job_log")
+public class ScheduleJobLogEntity implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * id
+ */
+ @TableId
+ private Long id;
+ /**
+ * 任务id
+ */
+ private Long jobId;
+ /**
+ * spring bean名称
+ */
+ private String beanName;
+ /**
+ * 参数
+ */
+ private String params;
+ /**
+ * 任务状态 0:失败 1:成功
+ */
+ private Integer status;
+ /**
+ * 失败信息
+ */
+ private String error;
+ /**
+ * 耗时(单位:毫秒)
+ */
+ private Integer times;
+ /**
+ * 创建时间
+ */
+ private Date createDate;
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/job/init/JobCommandLineRunner.java b/system-admin/src/main/java/com/weather/modules/job/init/JobCommandLineRunner.java
new file mode 100644
index 0000000..b4887bf
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/init/JobCommandLineRunner.java
@@ -0,0 +1,39 @@
+
+package com.weather.modules.job.init;
+
+import com.weather.modules.job.dao.ScheduleJobDao;
+import com.weather.modules.job.entity.ScheduleJobEntity;
+import com.weather.modules.job.utils.ScheduleUtils;
+import lombok.AllArgsConstructor;
+import org.quartz.CronTrigger;
+import org.quartz.Scheduler;
+import org.springframework.boot.CommandLineRunner;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+
+/**
+ * 初始化定时任务数据
+ *
+ * @author 123
+ */
+@Component
+@AllArgsConstructor
+public class JobCommandLineRunner implements CommandLineRunner {
+ private final Scheduler scheduler;
+ private final ScheduleJobDao scheduleJobDao;
+
+ @Override
+ public void run(String... args) {
+ List scheduleJobList = scheduleJobDao.selectList(null);
+ for (ScheduleJobEntity scheduleJob : scheduleJobList) {
+ CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId());
+ //如果不存在,则创建
+ if (cronTrigger == null) {
+ ScheduleUtils.createScheduleJob(scheduler, scheduleJob);
+ } else {
+ ScheduleUtils.updateScheduleJob(scheduler, scheduleJob);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobLogService.java b/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobLogService.java
new file mode 100644
index 0000000..8e453d5
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobLogService.java
@@ -0,0 +1,22 @@
+
+
+package com.weather.modules.job.service;
+
+import com.weather.common.page.PageData;
+import com.weather.common.service.BaseService;
+import com.weather.modules.job.dto.ScheduleJobLogDTO;
+import com.weather.modules.job.entity.ScheduleJobLogEntity;
+
+import java.util.Map;
+
+/**
+ * 定时任务日志
+ *
+ * @author 123
+ */
+public interface ScheduleJobLogService extends BaseService {
+
+ PageData page(Map params);
+
+ ScheduleJobLogDTO get(Long id);
+}
diff --git a/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobService.java b/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobService.java
new file mode 100644
index 0000000..e042e5a
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/service/ScheduleJobService.java
@@ -0,0 +1,57 @@
+
+
+package com.weather.modules.job.service;
+
+import com.weather.common.page.PageData;
+import com.weather.common.service.BaseService;
+import com.weather.modules.job.dto.ScheduleJobDTO;
+import com.weather.modules.job.entity.ScheduleJobEntity;
+
+import java.util.Map;
+
+/**
+ * 定时任务
+ *
+ * @author 123
+ */
+public interface ScheduleJobService extends BaseService {
+
+ PageData page(Map params);
+
+ ScheduleJobDTO get(Long id);
+
+ /**
+ * 保存定时任务
+ */
+ void save(ScheduleJobDTO dto);
+
+ /**
+ * 更新定时任务
+ */
+ void update(ScheduleJobDTO dto);
+
+ /**
+ * 批量删除定时任务
+ */
+ void deleteBatch(Long[] ids);
+
+ /**
+ * 批量更新定时任务状态
+ */
+ int updateBatch(Long[] ids, int status);
+
+ /**
+ * 立即执行
+ */
+ void run(Long[] ids);
+
+ /**
+ * 暂停运行
+ */
+ void pause(Long[] ids);
+
+ /**
+ * 恢复运行
+ */
+ void resume(Long[] ids);
+}
diff --git a/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobLogServiceImpl.java b/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobLogServiceImpl.java
new file mode 100644
index 0000000..6e74160
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobLogServiceImpl.java
@@ -0,0 +1,48 @@
+
+
+package com.weather.modules.job.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.service.impl.BaseServiceImpl;
+import com.weather.common.utils.ConvertUtils;
+import com.weather.modules.job.dao.ScheduleJobLogDao;
+import com.weather.modules.job.dto.ScheduleJobLogDTO;
+import com.weather.modules.job.entity.ScheduleJobLogEntity;
+import com.weather.modules.job.service.ScheduleJobLogService;
+import cn.hutool.core.util.StrUtil;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+@Service
+public class ScheduleJobLogServiceImpl extends BaseServiceImpl implements ScheduleJobLogService {
+
+ @Override
+ public PageData page(Map params) {
+ IPage page = baseDao.selectPage(
+ getPage(params, Constant.CREATE_DATE, false),
+ getWrapper(params)
+ );
+ return getPageData(page, ScheduleJobLogDTO.class);
+ }
+
+ private QueryWrapper getWrapper(Map params){
+ String jobId = (String)params.get("jobId");
+
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.eq(StrUtil.isNotBlank(jobId), "job_id", jobId);
+
+ return wrapper;
+ }
+
+ @Override
+ public ScheduleJobLogDTO get(Long id) {
+ ScheduleJobLogEntity entity = baseDao.selectById(id);
+
+ return ConvertUtils.sourceToTarget(entity, ScheduleJobLogDTO.class);
+ }
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobServiceImpl.java b/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobServiceImpl.java
new file mode 100644
index 0000000..bd88429
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/service/impl/ScheduleJobServiceImpl.java
@@ -0,0 +1,123 @@
+
+package com.weather.modules.job.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.service.impl.BaseServiceImpl;
+import com.weather.common.utils.ConvertUtils;
+import com.weather.modules.job.dao.ScheduleJobDao;
+import com.weather.modules.job.dto.ScheduleJobDTO;
+import com.weather.modules.job.entity.ScheduleJobEntity;
+import com.weather.modules.job.service.ScheduleJobService;
+import com.weather.modules.job.utils.ScheduleUtils;
+import lombok.AllArgsConstructor;
+import org.quartz.Scheduler;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+@Service
+@AllArgsConstructor
+public class ScheduleJobServiceImpl extends BaseServiceImpl implements ScheduleJobService {
+ private final Scheduler scheduler;
+
+ @Override
+ public PageData page(Map params) {
+ IPage page = baseDao.selectPage(
+ getPage(params, Constant.CREATE_DATE, false),
+ getWrapper(params)
+ );
+ return getPageData(page, ScheduleJobDTO.class);
+ }
+
+ @Override
+ public ScheduleJobDTO get(Long id) {
+ ScheduleJobEntity entity = baseDao.selectById(id);
+
+ return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class);
+ }
+
+ private QueryWrapper getWrapper(Map params) {
+ String beanName = (String) params.get("beanName");
+
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.like(StrUtil.isNotBlank(beanName), "bean_name", beanName);
+
+ return wrapper;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(ScheduleJobDTO dto) {
+ ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);
+
+ entity.setStatus(Constant.ScheduleStatus.NORMAL.getValue());
+ this.insert(entity);
+
+ ScheduleUtils.createScheduleJob(scheduler, entity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void update(ScheduleJobDTO dto) {
+ ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class);
+
+ ScheduleUtils.updateScheduleJob(scheduler, entity);
+
+ this.updateById(entity);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void deleteBatch(Long[] ids) {
+ for (Long id : ids) {
+ ScheduleUtils.deleteScheduleJob(scheduler, id);
+ }
+
+ //删除数据
+ this.deleteBatchIds(Arrays.asList(ids));
+ }
+
+ @Override
+ public int updateBatch(Long[] ids, int status) {
+ Map map = new HashMap<>(2);
+ map.put("ids", ids);
+ map.put("status", status);
+ return baseDao.updateBatch(map);
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void run(Long[] ids) {
+ for (Long id : ids) {
+ ScheduleUtils.run(scheduler, this.selectById(id));
+ }
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void pause(Long[] ids) {
+ for (Long id : ids) {
+ ScheduleUtils.pauseJob(scheduler, id);
+ }
+
+ updateBatch(ids, Constant.ScheduleStatus.PAUSE.getValue());
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void resume(Long[] ids) {
+ for (Long id : ids) {
+ ScheduleUtils.resumeJob(scheduler, id);
+ }
+
+ updateBatch(ids, Constant.ScheduleStatus.NORMAL.getValue());
+ }
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/job/task/ITask.java b/system-admin/src/main/java/com/weather/modules/job/task/ITask.java
new file mode 100644
index 0000000..0a8e961
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/task/ITask.java
@@ -0,0 +1,19 @@
+
+
+package com.weather.modules.job.task;
+
+/**
+ * 定时任务接口,所有定时任务都要实现该接口
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+public interface ITask {
+
+ /**
+ * 执行定时任务接口
+ *
+ * @param params 参数,多参数使用JSON数据
+ */
+ void run(String params);
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/job/task/TestTask.java b/system-admin/src/main/java/com/weather/modules/job/task/TestTask.java
new file mode 100644
index 0000000..94e6c3e
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/task/TestTask.java
@@ -0,0 +1,24 @@
+
+
+package com.weather.modules.job.task;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * 测试定时任务(演示Demo,可删除)
+ *
+ * testTask为spring bean的名称
+ *
+ * @author 123
+ */
+@Component("testTask")
+public class TestTask implements ITask{
+ private Logger logger = LoggerFactory.getLogger(getClass());
+
+ @Override
+ public void run(String params){
+ logger.debug("TestTask定时任务正在执行,参数为:{}", params);
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleJob.java b/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleJob.java
new file mode 100644
index 0000000..7a4614d
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleJob.java
@@ -0,0 +1,73 @@
+
+
+package com.weather.modules.job.utils;
+
+import com.weather.common.constant.Constant;
+import com.weather.common.exception.ExceptionUtils;
+import com.weather.common.utils.SpringContextUtils;
+import com.weather.modules.job.entity.ScheduleJobEntity;
+import com.weather.modules.job.entity.ScheduleJobLogEntity;
+import com.weather.modules.job.service.ScheduleJobLogService;
+import org.quartz.JobExecutionContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.scheduling.quartz.QuartzJobBean;
+
+import java.lang.reflect.Method;
+import java.util.Date;
+
+
+/**
+ * 定时任务
+ *
+ * @author 123
+ */
+public class ScheduleJob extends QuartzJobBean {
+ private Logger logger = LoggerFactory.getLogger(getClass());
+
+ @Override
+ protected void executeInternal(JobExecutionContext context) {
+ ScheduleJobEntity scheduleJob = (ScheduleJobEntity) context.getMergedJobDataMap().
+ get(ScheduleUtils.JOB_PARAM_KEY);
+
+ //数据库保存执行记录
+ ScheduleJobLogEntity log = new ScheduleJobLogEntity();
+ log.setJobId(scheduleJob.getId());
+ log.setBeanName(scheduleJob.getBeanName());
+ log.setParams(scheduleJob.getParams());
+ log.setCreateDate(new Date());
+
+ //任务开始时间
+ long startTime = System.currentTimeMillis();
+
+ try {
+ //执行任务
+ logger.info("任务准备执行,任务ID:{}", scheduleJob.getId());
+ Object target = SpringContextUtils.getBean(scheduleJob.getBeanName());
+ Method method = target.getClass().getDeclaredMethod("run", String.class);
+ method.invoke(target, scheduleJob.getParams());
+
+ //任务执行总时长
+ long times = System.currentTimeMillis() - startTime;
+ log.setTimes((int)times);
+ //任务状态
+ log.setStatus(Constant.SUCCESS);
+
+ logger.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getId(), times);
+ } catch (Exception e) {
+ logger.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e);
+
+ //任务执行总时长
+ long times = System.currentTimeMillis() - startTime;
+ log.setTimes((int)times);
+
+ //任务状态
+ log.setStatus(Constant.FAIL);
+ log.setError(ExceptionUtils.getErrorStackTrace(e));
+ }finally {
+ //获取spring bean
+ ScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class);
+ scheduleJobLogService.insert(log);
+ }
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleUtils.java b/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleUtils.java
new file mode 100644
index 0000000..148ee59
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/job/utils/ScheduleUtils.java
@@ -0,0 +1,155 @@
+
+
+package com.weather.modules.job.utils;
+
+import com.weather.common.constant.Constant;
+import com.weather.common.exception.ErrorCode;
+import com.weather.common.exception.CommonException;
+import com.weather.modules.job.entity.ScheduleJobEntity;
+import org.quartz.*;
+
+/**
+ * 定时任务工具类
+ *
+ * @author 123
+ */
+public class ScheduleUtils {
+ private final static String JOB_NAME = "TASK_";
+ /**
+ * 任务调度参数key
+ */
+ public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY";
+
+ /**
+ * 获取触发器key
+ */
+ public static TriggerKey getTriggerKey(Long jobId) {
+ return TriggerKey.triggerKey(JOB_NAME + jobId);
+ }
+
+ /**
+ * 获取jobKey
+ */
+ public static JobKey getJobKey(Long jobId) {
+ return JobKey.jobKey(JOB_NAME + jobId);
+ }
+
+ /**
+ * 获取表达式触发器
+ */
+ public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) {
+ try {
+ return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
+ } catch (SchedulerException e) {
+ throw new CommonException(ErrorCode.JOB_ERROR, e);
+ }
+ }
+
+ /**
+ * 创建定时任务
+ */
+ public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+ try {
+ //构建job信息
+ JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build();
+
+ //表达式调度构建器
+ CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
+ .withMisfireHandlingInstructionDoNothing();
+
+ //按新的cronExpression表达式构建一个新的trigger
+ CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build();
+
+ //放入参数,运行时的方法可以获取
+ jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);
+
+ scheduler.scheduleJob(jobDetail, trigger);
+
+ //暂停任务
+ if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){
+ pauseJob(scheduler, scheduleJob.getId());
+ }
+ } catch (SchedulerException e) {
+ throw new CommonException(ErrorCode.JOB_ERROR, e);
+ }
+ }
+
+ /**
+ * 更新定时任务
+ */
+ public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+ try {
+ TriggerKey triggerKey = getTriggerKey(scheduleJob.getId());
+
+ //表达式调度构建器
+ CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression())
+ .withMisfireHandlingInstructionDoNothing();
+
+ CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId());
+
+ //按新的cronExpression表达式重新构建trigger
+ trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
+
+ //参数
+ trigger.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob);
+
+ scheduler.rescheduleJob(triggerKey, trigger);
+
+ //暂停任务
+ if(scheduleJob.getStatus() == Constant.ScheduleStatus.PAUSE.getValue()){
+ pauseJob(scheduler, scheduleJob.getId());
+ }
+
+ } catch (SchedulerException e) {
+ throw new CommonException(ErrorCode.JOB_ERROR, e);
+ }
+ }
+
+ /**
+ * 立即执行任务
+ */
+ public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) {
+ try {
+ //参数
+ JobDataMap dataMap = new JobDataMap();
+ dataMap.put(JOB_PARAM_KEY, scheduleJob);
+
+ scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap);
+ } catch (SchedulerException e) {
+ throw new CommonException(ErrorCode.JOB_ERROR, e);
+ }
+ }
+
+ /**
+ * 暂停任务
+ */
+ public static void pauseJob(Scheduler scheduler, Long jobId) {
+ try {
+ scheduler.pauseJob(getJobKey(jobId));
+ } catch (SchedulerException e) {
+ throw new CommonException(ErrorCode.JOB_ERROR, e);
+ }
+ }
+
+ /**
+ * 恢复任务
+ */
+ public static void resumeJob(Scheduler scheduler, Long jobId) {
+ try {
+ scheduler.resumeJob(getJobKey(jobId));
+ } catch (SchedulerException e) {
+ throw new CommonException(ErrorCode.JOB_ERROR, e);
+ }
+ }
+
+ /**
+ * 删除定时任务
+ */
+ public static void deleteScheduleJob(Scheduler scheduler, Long jobId) {
+ try {
+ scheduler.deleteJob(getJobKey(jobId));
+ } catch (SchedulerException e) {
+ throw new CommonException(ErrorCode.JOB_ERROR, e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/controller/SysLogErrorController.java b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogErrorController.java
new file mode 100644
index 0000000..d4e99e2
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogErrorController.java
@@ -0,0 +1,67 @@
+
+package com.weather.modules.log.controller;
+
+import com.weather.common.annotation.LogOperation;
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.utils.ExcelUtils;
+import com.weather.common.utils.Result;
+import com.weather.modules.log.dto.SysLogErrorDTO;
+import com.weather.modules.log.excel.SysLogErrorExcel;
+import com.weather.modules.log.service.SysLogErrorService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.AllArgsConstructor;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 异常日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@RestController
+@RequestMapping("sys/log/error")
+@Tag(name = "异常日志")
+@AllArgsConstructor
+public class SysLogErrorController {
+ private final SysLogErrorService sysLogErrorService;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String")
+ })
+ @RequiresPermissions("sys:log:error")
+ public Result> page(@Parameter(hidden = true) @RequestParam Map params) {
+ PageData page = sysLogErrorService.page(params);
+
+ return new Result>().ok(page);
+ }
+
+ @GetMapping("export")
+ @Operation(summary = "导出")
+ @LogOperation("导出")
+ @RequiresPermissions("sys:log:error")
+ public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception {
+ List list = sysLogErrorService.list(params);
+
+ ExcelUtils.exportExcelToTarget(response, null, "异常日志", list, SysLogErrorExcel.class);
+ }
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/controller/SysLogLoginController.java b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogLoginController.java
new file mode 100644
index 0000000..695e953
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogLoginController.java
@@ -0,0 +1,74 @@
+
+package com.weather.modules.log.controller;
+
+import com.weather.common.annotation.LogOperation;
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.utils.ExcelUtils;
+import com.weather.common.utils.Result;
+import com.weather.modules.log.dto.SysLogLoginDTO;
+import com.weather.modules.log.excel.SysLogLoginExcel;
+import com.weather.modules.log.service.SysLogLoginService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.AllArgsConstructor;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 登录日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@RestController
+@RequestMapping("sys/log/login")
+@Tag(name = "登录日志")
+@AllArgsConstructor
+public class SysLogLoginController {
+ private final SysLogLoginService sysLogLoginService;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = "status", description = "状态 0:失败 1:成功 2:账号已锁定", in = ParameterIn.QUERY, ref = "int"),
+ @Parameter(name = "creatorName", description = "用户名", in = ParameterIn.QUERY, ref = "String")
+ })
+ @RequiresPermissions("sys:log:login")
+ public Result> page(@Parameter(hidden = true) @RequestParam Map params) {
+ PageData page = sysLogLoginService.page(params);
+
+ return new Result>().ok(page);
+ }
+
+ @GetMapping("export")
+ @Operation(summary = "导出")
+ @LogOperation("导出")
+ @Parameters({
+ @Parameter(name = "status", description = "状态 0:失败 1:成功 2:账号已锁定", in = ParameterIn.QUERY, ref = "int"),
+ @Parameter(name = "creatorName", description = "用户名", in = ParameterIn.QUERY, ref = "String")
+ })
+ @RequiresPermissions("sys:log:login")
+ public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception {
+ List list = sysLogLoginService.list(params);
+
+ ExcelUtils.exportExcelToTarget(response, null, "登录日志", list, SysLogLoginExcel.class);
+
+ }
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/controller/SysLogOperationController.java b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogOperationController.java
new file mode 100644
index 0000000..3290888
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/controller/SysLogOperationController.java
@@ -0,0 +1,68 @@
+
+package com.weather.modules.log.controller;
+
+import com.weather.common.annotation.LogOperation;
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.utils.ExcelUtils;
+import com.weather.common.utils.Result;
+import com.weather.modules.log.dto.SysLogOperationDTO;
+import com.weather.modules.log.excel.SysLogOperationExcel;
+import com.weather.modules.log.service.SysLogOperationService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.Parameters;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.AllArgsConstructor;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * 操作日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@RestController
+@RequestMapping("sys/log/operation")
+@Tag(name = "操作日志")
+@AllArgsConstructor
+public class SysLogOperationController {
+ private final SysLogOperationService sysLogOperationService;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @Parameters({
+ @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.LIMIT, description = "每页显示记录数", in = ParameterIn.QUERY, required = true, ref = "int"),
+ @Parameter(name = Constant.ORDER_FIELD, description = "排序字段", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)", in = ParameterIn.QUERY, ref = "String"),
+ @Parameter(name = "status", description = "状态 0:失败 1:成功", in = ParameterIn.QUERY, ref = "int")
+ })
+ @RequiresPermissions("sys:log:operation")
+ public Result> page(@Parameter(hidden = true) @RequestParam Map params) {
+ PageData page = sysLogOperationService.page(params);
+
+ return new Result>().ok(page);
+ }
+
+ @GetMapping("export")
+ @Operation(summary = "导出")
+ @LogOperation("导出")
+ @RequiresPermissions("sys:log:operation")
+ public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception {
+ List list = sysLogOperationService.list(params);
+
+ ExcelUtils.exportExcelToTarget(response, null, "操作日志", list, SysLogOperationExcel.class);
+ }
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/dao/SysLogErrorDao.java b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogErrorDao.java
new file mode 100644
index 0000000..1c77599
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogErrorDao.java
@@ -0,0 +1,18 @@
+
+
+package com.weather.modules.log.dao;
+
+import com.weather.common.dao.BaseDao;
+import com.weather.modules.log.entity.SysLogErrorEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 异常日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Mapper
+public interface SysLogErrorDao extends BaseDao {
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/dao/SysLogLoginDao.java b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogLoginDao.java
new file mode 100644
index 0000000..ef73f79
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogLoginDao.java
@@ -0,0 +1,18 @@
+
+
+package com.weather.modules.log.dao;
+
+import com.weather.common.dao.BaseDao;
+import com.weather.modules.log.entity.SysLogLoginEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 登录日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Mapper
+public interface SysLogLoginDao extends BaseDao {
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/dao/SysLogOperationDao.java b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogOperationDao.java
new file mode 100644
index 0000000..80c3920
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/dao/SysLogOperationDao.java
@@ -0,0 +1,18 @@
+
+
+package com.weather.modules.log.dao;
+
+import com.weather.common.dao.BaseDao;
+import com.weather.modules.log.entity.SysLogOperationEntity;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 操作日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Mapper
+public interface SysLogOperationDao extends BaseDao {
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/dto/SysLogErrorDTO.java b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogErrorDTO.java
new file mode 100644
index 0000000..0d0594f
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogErrorDTO.java
@@ -0,0 +1,39 @@
+
+
+package com.weather.modules.log.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 异常日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Data
+@Schema(title = "异常日志")
+public class SysLogErrorDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(title = "id")
+ private Long id;
+ @Schema(title = "请求URI")
+ private String requestUri;
+ @Schema(title = "请求方式")
+ private String requestMethod;
+ @Schema(title = "请求参数")
+ private String requestParams;
+ @Schema(title = "用户代理")
+ private String userAgent;
+ @Schema(title = "操作IP")
+ private String ip;
+ @Schema(title = "异常信息")
+ private String errorInfo;
+ @Schema(title = "创建时间")
+ private Date createDate;
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/dto/SysLogLoginDTO.java b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogLoginDTO.java
new file mode 100644
index 0000000..1620caa
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogLoginDTO.java
@@ -0,0 +1,43 @@
+
+
+package com.weather.modules.log.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 登录日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Data
+@Schema(title = "登录日志")
+public class SysLogLoginDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(title = "id")
+ private Long id;
+
+ @Schema(title = "用户操作 0:用户登录 1:用户退出")
+ private Integer operation;
+
+ @Schema(title = "状态 0:失败 1:成功 2:账号已锁定")
+ private Integer status;
+
+ @Schema(title = "用户代理")
+ private String userAgent;
+
+ @Schema(title = "操作IP")
+ private String ip;
+
+ @Schema(title = "用户名")
+ private String creatorName;
+
+ @Schema(title = "创建时间")
+ private Date createDate;
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/dto/SysLogOperationDTO.java b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogOperationDTO.java
new file mode 100644
index 0000000..c4c14d0
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/dto/SysLogOperationDTO.java
@@ -0,0 +1,55 @@
+
+
+package com.weather.modules.log.dto;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 操作日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Data
+@Schema(title = "操作日志")
+public class SysLogOperationDTO implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(title = "id")
+ private Long id;
+
+ @Schema(title = "用户操作")
+ private String operation;
+
+ @Schema(title = "请求URI")
+ private String requestUri;
+
+ @Schema(title = "请求方式")
+ private String requestMethod;
+
+ @Schema(title = "请求参数")
+ private String requestParams;
+
+ @Schema(title = "请求时长(毫秒)")
+ private Integer requestTime;
+
+ @Schema(title = "用户代理")
+ private String userAgent;
+
+ @Schema(title = "操作IP")
+ private String ip;
+
+ @Schema(title = "状态 0:失败 1:成功")
+ private Integer status;
+
+ @Schema(title = "用户名")
+ private String creatorName;
+
+ @Schema(title = "创建时间")
+ private Date createDate;
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/entity/SysLogErrorEntity.java b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogErrorEntity.java
new file mode 100644
index 0000000..237306e
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogErrorEntity.java
@@ -0,0 +1,47 @@
+
+
+package com.weather.modules.log.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.weather.common.entity.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 异常日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Data
+@EqualsAndHashCode(callSuper=false)
+@TableName("sys_log_error")
+public class SysLogErrorEntity extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 请求URI
+ */
+ private String requestUri;
+ /**
+ * 请求方式
+ */
+ private String requestMethod;
+ /**
+ * 请求参数
+ */
+ private String requestParams;
+ /**
+ * 用户代理
+ */
+ private String userAgent;
+ /**
+ * 操作IP
+ */
+ private String ip;
+ /**
+ * 异常信息
+ */
+ private String errorInfo;
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/entity/SysLogLoginEntity.java b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogLoginEntity.java
new file mode 100644
index 0000000..5df1ab4
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogLoginEntity.java
@@ -0,0 +1,43 @@
+
+
+package com.weather.modules.log.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.weather.common.entity.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 登录日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Data
+@EqualsAndHashCode(callSuper=false)
+@TableName("sys_log_login")
+public class SysLogLoginEntity extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户操作 0:用户登录 1:用户退出
+ */
+ private Integer operation;
+ /**
+ * 状态 0:失败 1:成功 2:账号已锁定
+ */
+ private Integer status;
+ /**
+ * 用户代理
+ */
+ private String userAgent;
+ /**
+ * 操作IP
+ */
+ private String ip;
+ /**
+ * 用户名
+ */
+ private String creatorName;
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/entity/SysLogOperationEntity.java b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogOperationEntity.java
new file mode 100644
index 0000000..e1d1b61
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/entity/SysLogOperationEntity.java
@@ -0,0 +1,58 @@
+
+
+package com.weather.modules.log.entity;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.weather.common.entity.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 操作日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Data
+@EqualsAndHashCode(callSuper=false)
+@TableName("sys_log_operation")
+public class SysLogOperationEntity extends BaseEntity {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 用户操作
+ */
+ private String operation;
+ /**
+ * 请求URI
+ */
+ private String requestUri;
+ /**
+ * 请求方式
+ */
+ private String requestMethod;
+ /**
+ * 请求参数
+ */
+ private String requestParams;
+ /**
+ * 请求时长(毫秒)
+ */
+ private Integer requestTime;
+ /**
+ * 用户代理
+ */
+ private String userAgent;
+ /**
+ * 操作IP
+ */
+ private String ip;
+ /**
+ * 状态 0:失败 1:成功
+ */
+ private Integer status;
+ /**
+ * 用户名
+ */
+ private String creatorName;
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/enums/LoginOperationEnum.java b/system-admin/src/main/java/com/weather/modules/log/enums/LoginOperationEnum.java
new file mode 100644
index 0000000..1c4161c
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/enums/LoginOperationEnum.java
@@ -0,0 +1,30 @@
+
+
+package com.weather.modules.log.enums;
+
+/**
+ * 登录操作枚举
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+public enum LoginOperationEnum {
+ /**
+ * 用户登录
+ */
+ LOGIN(0),
+ /**
+ * 用户退出
+ */
+ LOGOUT(1);
+
+ private int value;
+
+ LoginOperationEnum(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return this.value;
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/enums/LoginStatusEnum.java b/system-admin/src/main/java/com/weather/modules/log/enums/LoginStatusEnum.java
new file mode 100644
index 0000000..1bb0664
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/enums/LoginStatusEnum.java
@@ -0,0 +1,34 @@
+
+
+package com.weather.modules.log.enums;
+
+/**
+ * 登录状态枚举
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+public enum LoginStatusEnum {
+ /**
+ * 失败
+ */
+ FAIL(0),
+ /**
+ * 成功
+ */
+ SUCCESS(1),
+ /**
+ * 账号已锁定
+ */
+ LOCK(2);
+
+ private int value;
+
+ LoginStatusEnum(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return this.value;
+ }
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/enums/OperationStatusEnum.java b/system-admin/src/main/java/com/weather/modules/log/enums/OperationStatusEnum.java
new file mode 100644
index 0000000..fc90ffc
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/enums/OperationStatusEnum.java
@@ -0,0 +1,30 @@
+
+
+package com.weather.modules.log.enums;
+
+/**
+ * 操作状态枚举
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+public enum OperationStatusEnum {
+ /**
+ * 失败
+ */
+ FAIL(0),
+ /**
+ * 成功
+ */
+ SUCCESS(1);
+
+ private int value;
+
+ OperationStatusEnum(int value) {
+ this.value = value;
+ }
+
+ public int value() {
+ return this.value;
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/SysLogErrorExcel.java b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogErrorExcel.java
new file mode 100644
index 0000000..7781ae9
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogErrorExcel.java
@@ -0,0 +1,43 @@
+
+
+package com.weather.modules.log.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.format.DateTimeFormat;
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.alibaba.excel.annotation.write.style.ContentRowHeight;
+import com.alibaba.excel.annotation.write.style.HeadRowHeight;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 异常日志
+ *
+ * @author 123
+ */
+@Data
+@ContentRowHeight(20)
+@HeadRowHeight(20)
+@ColumnWidth(25)
+public class SysLogErrorExcel {
+ @ExcelProperty("请求URI")
+ private String requestUri;
+
+ @ExcelProperty("请求方式")
+ private String requestMethod;
+
+ @ExcelProperty("请求参数")
+ private String requestParams;
+
+ @ExcelProperty("User-Agent")
+ private String userAgent;
+
+ @ExcelProperty("操作IP")
+ private String ip;
+
+ @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
+ @ExcelProperty("创建时间")
+ private Date createDate;
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/SysLogLoginExcel.java b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogLoginExcel.java
new file mode 100644
index 0000000..e9e6952
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogLoginExcel.java
@@ -0,0 +1,45 @@
+
+
+package com.weather.modules.log.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.format.DateTimeFormat;
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.alibaba.excel.annotation.write.style.ContentRowHeight;
+import com.alibaba.excel.annotation.write.style.HeadRowHeight;
+import com.weather.modules.log.excel.converter.SysLogOperationConverter;
+import com.weather.modules.log.excel.converter.SysLogStatusConverter;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 登录日志
+ *
+ * @author 123
+ */
+@Data
+@ContentRowHeight(20)
+@HeadRowHeight(20)
+@ColumnWidth(25)
+public class SysLogLoginExcel {
+ @ExcelProperty(value = "操作类型", converter = SysLogOperationConverter.class)
+ private Integer operation;
+
+ @ExcelProperty(value = "状态", converter = SysLogStatusConverter.class)
+ private Integer status;
+
+ @ExcelProperty("User-Agent")
+ private String userAgent;
+
+ @ExcelProperty("操作IP")
+ private String ip;
+
+ @ExcelProperty("用户名")
+ private String creatorName;
+
+ @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
+ @ExcelProperty("创建时间")
+ private Date createDate;
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/SysLogOperationExcel.java b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogOperationExcel.java
new file mode 100644
index 0000000..ad81a27
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/excel/SysLogOperationExcel.java
@@ -0,0 +1,54 @@
+
+package com.weather.modules.log.excel;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.format.DateTimeFormat;
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.alibaba.excel.annotation.write.style.ContentRowHeight;
+import com.alibaba.excel.annotation.write.style.HeadRowHeight;
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 操作日志
+ *
+ * @author 123
+ */
+@Data
+@ContentRowHeight(20)
+@HeadRowHeight(20)
+@ColumnWidth(25)
+public class SysLogOperationExcel {
+ @ExcelProperty("用户操作")
+ private String operation;
+
+ @ExcelProperty("请求URI")
+ private String requestUri;
+
+ @ExcelProperty("请求方式")
+ private String requestMethod;
+
+ @ExcelProperty("请求参数")
+ private String requestParams;
+
+ @ExcelProperty("请求时长(毫秒)")
+ private Integer requestTime;
+
+ @ExcelProperty("User-Agent")
+ private String userAgent;
+
+ @ExcelProperty("操作IP")
+ private String ip;
+
+ @ExcelProperty("状态")
+ private Integer status;
+
+ @ExcelProperty("用户名")
+ private String creatorName;
+
+ @DateTimeFormat("yyyy-MM-dd HH:mm:ss")
+ @ExcelProperty("创建时间")
+ private Date createDate;
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogOperationConverter.java b/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogOperationConverter.java
new file mode 100644
index 0000000..9ea621a
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogOperationConverter.java
@@ -0,0 +1,40 @@
+package com.weather.modules.log.excel.converter;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+public class SysLogOperationConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return Integer.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Integer convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+ if(cellData.getStringValue().equals("用户登录")){
+ return 0;
+ }else{
+ return 1;
+ }
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+ if(value == 0){
+ return new WriteCellData<>("用户登录");
+ }else{
+ return new WriteCellData<>("用户退出");
+ }
+ }
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogStatusConverter.java b/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogStatusConverter.java
new file mode 100644
index 0000000..b9afa90
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/excel/converter/SysLogStatusConverter.java
@@ -0,0 +1,48 @@
+package com.weather.modules.log.excel.converter;
+
+import com.alibaba.excel.converters.Converter;
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.GlobalConfiguration;
+import com.alibaba.excel.metadata.data.ReadCellData;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.metadata.property.ExcelContentProperty;
+
+public class SysLogStatusConverter implements Converter {
+
+ @Override
+ public Class supportJavaTypeKey() {
+ return Integer.class;
+ }
+
+ @Override
+ public CellDataTypeEnum supportExcelTypeKey() {
+ return CellDataTypeEnum.STRING;
+ }
+
+ @Override
+ public Integer convertToJavaData(ReadCellData> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+ if(cellData.getStringValue().equals("失败")){
+ return 0;
+ }else if(cellData.getStringValue().equals("成功")){
+ return 1;
+ }else if(cellData.getStringValue().equals("账号已锁定")){
+ return 2;
+ }else{
+ return -1;
+ }
+ }
+
+ @Override
+ public WriteCellData> convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
+ if(value == 0){
+ return new WriteCellData<>("失败");
+ }else if(value == 1){
+ return new WriteCellData<>("成功");
+ }else if(value == 2){
+ return new WriteCellData<>("账号已锁定");
+ }else{
+ return new WriteCellData<>("未知");
+ }
+ }
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/log/service/SysLogErrorService.java b/system-admin/src/main/java/com/weather/modules/log/service/SysLogErrorService.java
new file mode 100644
index 0000000..9fc8b71
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/service/SysLogErrorService.java
@@ -0,0 +1,28 @@
+
+
+package com.weather.modules.log.service;
+
+
+import com.weather.common.page.PageData;
+import com.weather.common.service.BaseService;
+import com.weather.modules.log.dto.SysLogErrorDTO;
+import com.weather.modules.log.entity.SysLogErrorEntity;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 异常日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+public interface SysLogErrorService extends BaseService {
+
+ PageData page(Map params);
+
+ List list(Map params);
+
+ void save(SysLogErrorEntity entity);
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/service/SysLogLoginService.java b/system-admin/src/main/java/com/weather/modules/log/service/SysLogLoginService.java
new file mode 100644
index 0000000..18f42fc
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/service/SysLogLoginService.java
@@ -0,0 +1,26 @@
+
+
+package com.weather.modules.log.service;
+
+import com.weather.common.page.PageData;
+import com.weather.common.service.BaseService;
+import com.weather.modules.log.dto.SysLogLoginDTO;
+import com.weather.modules.log.entity.SysLogLoginEntity;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 登录日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+public interface SysLogLoginService extends BaseService {
+
+ PageData page(Map params);
+
+ List list(Map params);
+
+ void save(SysLogLoginEntity entity);
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/service/SysLogOperationService.java b/system-admin/src/main/java/com/weather/modules/log/service/SysLogOperationService.java
new file mode 100644
index 0000000..f0e1f53
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/service/SysLogOperationService.java
@@ -0,0 +1,26 @@
+
+
+package com.weather.modules.log.service;
+
+import com.weather.common.page.PageData;
+import com.weather.common.service.BaseService;
+import com.weather.modules.log.dto.SysLogOperationDTO;
+import com.weather.modules.log.entity.SysLogOperationEntity;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 操作日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+public interface SysLogOperationService extends BaseService {
+
+ PageData page(Map params);
+
+ List list(Map params);
+
+ void save(SysLogOperationEntity entity);
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogErrorServiceImpl.java b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogErrorServiceImpl.java
new file mode 100644
index 0000000..16c03b2
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogErrorServiceImpl.java
@@ -0,0 +1,58 @@
+
+
+package com.weather.modules.log.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.service.impl.BaseServiceImpl;
+import com.weather.common.utils.ConvertUtils;
+import com.weather.modules.log.dao.SysLogErrorDao;
+import com.weather.modules.log.dto.SysLogErrorDTO;
+import com.weather.modules.log.entity.SysLogErrorEntity;
+import com.weather.modules.log.service.SysLogErrorService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 异常日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Service
+public class SysLogErrorServiceImpl extends BaseServiceImpl implements SysLogErrorService {
+
+ @Override
+ public PageData page(Map params) {
+ IPage page = baseDao.selectPage(
+ getPage(params, Constant.CREATE_DATE, false),
+ getWrapper(params)
+ );
+
+ return getPageData(page, SysLogErrorDTO.class);
+ }
+
+ @Override
+ public List list(Map params) {
+ List entityList = baseDao.selectList(getWrapper(params));
+
+ return ConvertUtils.sourceToTarget(entityList, SysLogErrorDTO.class);
+ }
+
+ private QueryWrapper getWrapper(Map params){
+ QueryWrapper wrapper = new QueryWrapper<>();
+ return wrapper;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(SysLogErrorEntity entity) {
+ insert(entity);
+ }
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogLoginServiceImpl.java b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogLoginServiceImpl.java
new file mode 100644
index 0000000..90452f9
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogLoginServiceImpl.java
@@ -0,0 +1,65 @@
+
+
+package com.weather.modules.log.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.service.impl.BaseServiceImpl;
+import com.weather.common.utils.ConvertUtils;
+import com.weather.modules.log.dao.SysLogLoginDao;
+import com.weather.modules.log.dto.SysLogLoginDTO;
+import com.weather.modules.log.entity.SysLogLoginEntity;
+import com.weather.modules.log.service.SysLogLoginService;
+import cn.hutool.core.util.StrUtil;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 登录日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Service
+public class SysLogLoginServiceImpl extends BaseServiceImpl implements SysLogLoginService {
+
+ @Override
+ public PageData page(Map params) {
+ IPage page = baseDao.selectPage(
+ getPage(params, Constant.CREATE_DATE, false),
+ getWrapper(params)
+ );
+
+ return getPageData(page, SysLogLoginDTO.class);
+ }
+
+ @Override
+ public List list(Map params) {
+ List entityList = baseDao.selectList(getWrapper(params));
+
+ return ConvertUtils.sourceToTarget(entityList, SysLogLoginDTO.class);
+ }
+
+ private QueryWrapper getWrapper(Map params){
+ String status = (String) params.get("status");
+ String creatorName = (String) params.get("creatorName");
+
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.eq(StrUtil.isNotBlank(status), "status", status);
+ wrapper.like(StrUtil.isNotBlank(creatorName), "creator_name", creatorName);
+
+ return wrapper;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(SysLogLoginEntity entity) {
+ insert(entity);
+ }
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogOperationServiceImpl.java b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogOperationServiceImpl.java
new file mode 100644
index 0000000..54ef636
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/log/service/impl/SysLogOperationServiceImpl.java
@@ -0,0 +1,63 @@
+
+
+package com.weather.modules.log.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.weather.common.constant.Constant;
+import com.weather.common.page.PageData;
+import com.weather.common.service.impl.BaseServiceImpl;
+import com.weather.common.utils.ConvertUtils;
+import com.weather.modules.log.dao.SysLogOperationDao;
+import com.weather.modules.log.dto.SysLogOperationDTO;
+import com.weather.modules.log.entity.SysLogOperationEntity;
+import com.weather.modules.log.service.SysLogOperationService;
+import cn.hutool.core.util.StrUtil;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 操作日志
+ *
+ * @author 123
+ * @since 1.0.0
+ */
+@Service
+public class SysLogOperationServiceImpl extends BaseServiceImpl implements SysLogOperationService {
+
+ @Override
+ public PageData page(Map params) {
+ IPage page = baseDao.selectPage(
+ getPage(params, Constant.CREATE_DATE, false),
+ getWrapper(params)
+ );
+
+ return getPageData(page, SysLogOperationDTO.class);
+ }
+
+ @Override
+ public List list(Map params) {
+ List entityList = baseDao.selectList(getWrapper(params));
+
+ return ConvertUtils.sourceToTarget(entityList, SysLogOperationDTO.class);
+ }
+
+ private QueryWrapper getWrapper(Map params){
+ String status = (String) params.get("status");
+
+ QueryWrapper wrapper = new QueryWrapper<>();
+ wrapper.eq(StrUtil.isNotBlank(status), "status", status);
+
+ return wrapper;
+ }
+
+ @Override
+ @Transactional(rollbackFor = Exception.class)
+ public void save(SysLogOperationEntity entity) {
+ insert(entity);
+ }
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/AbstractCloudStorageService.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/AbstractCloudStorageService.java
new file mode 100644
index 0000000..db9f25e
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/AbstractCloudStorageService.java
@@ -0,0 +1,72 @@
+
+
+package com.weather.modules.oss.cloud;
+
+import com.weather.common.utils.DateUtils;
+import cn.hutool.core.util.StrUtil;
+
+import java.io.InputStream;
+import java.util.Date;
+import java.util.UUID;
+
+/**
+ * 云存储(支持七牛、阿里云、腾讯云)
+ *
+ * @author 123
+ */
+public abstract class AbstractCloudStorageService {
+ /** 云存储配置信息 */
+ CloudStorageConfig config;
+
+ /**
+ * 文件路径
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @return 返回上传路径
+ */
+ public String getPath(String prefix, String suffix) {
+ //生成uuid
+ String uuid = UUID.randomUUID().toString().replaceAll("-", "");
+ //文件路径
+ String path = DateUtils.format(new Date(), "yyyyMMdd") + "/" + uuid;
+
+ if(StrUtil.isNotBlank(prefix)){
+ path = prefix + "/" + path;
+ }
+
+ return path + "." + suffix;
+ }
+
+ /**
+ * 文件上传
+ * @param data 文件字节数组
+ * @param path 文件路径,包含文件名
+ * @return 返回http地址
+ */
+ public abstract String upload(byte[] data, String path);
+
+ /**
+ * 文件上传
+ * @param data 文件字节数组
+ * @param suffix 后缀
+ * @return 返回http地址
+ */
+ public abstract String uploadSuffix(byte[] data, String suffix);
+
+ /**
+ * 文件上传
+ * @param inputStream 字节流
+ * @param path 文件路径,包含文件名
+ * @return 返回http地址
+ */
+ public abstract String upload(InputStream inputStream, String path);
+
+ /**
+ * 文件上传
+ * @param inputStream 字节流
+ * @param suffix 后缀
+ * @return 返回http地址
+ */
+ public abstract String uploadSuffix(InputStream inputStream, String suffix);
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/AliyunCloudStorageService.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/AliyunCloudStorageService.java
new file mode 100644
index 0000000..a3b228b
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/AliyunCloudStorageService.java
@@ -0,0 +1,51 @@
+
+
+package com.weather.modules.oss.cloud;
+
+import com.aliyun.oss.OSSClient;
+import com.weather.common.exception.ErrorCode;
+import com.weather.common.exception.CommonException;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * 阿里云存储
+ *
+ * @author 123
+ */
+public class AliyunCloudStorageService extends AbstractCloudStorageService {
+
+ public AliyunCloudStorageService(CloudStorageConfig config){
+ this.config = config;
+ }
+
+ @Override
+ public String upload(byte[] data, String path) {
+ return upload(new ByteArrayInputStream(data), path);
+ }
+
+ @Override
+ public String upload(InputStream inputStream, String path) {
+ OSSClient client = new OSSClient(config.getAliyunEndPoint(), config.getAliyunAccessKeyId(),
+ config.getAliyunAccessKeySecret());
+ try {
+ client.putObject(config.getAliyunBucketName(), path, inputStream);
+ client.shutdown();
+ } catch (Exception e){
+ throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, "");
+ }
+
+ return config.getAliyunDomain() + "/" + path;
+ }
+
+ @Override
+ public String uploadSuffix(byte[] data, String suffix) {
+ return upload(data, getPath(config.getAliyunPrefix(), suffix));
+ }
+
+ @Override
+ public String uploadSuffix(InputStream inputStream, String suffix) {
+ return upload(inputStream, getPath(config.getAliyunPrefix(), suffix));
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/CloudStorageConfig.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/CloudStorageConfig.java
new file mode 100644
index 0000000..1c1e87a
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/CloudStorageConfig.java
@@ -0,0 +1,102 @@
+
+package com.weather.modules.oss.cloud;
+
+import com.weather.common.validator.group.AliyunGroup;
+import com.weather.common.validator.group.QcloudGroup;
+import com.weather.common.validator.group.QiniuGroup;
+import io.swagger.v3.oas.annotations.media.Schema;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import lombok.Data;
+import org.hibernate.validator.constraints.Range;
+import org.hibernate.validator.constraints.URL;
+
+import java.io.Serializable;
+
+/**
+ * 云存储配置信息
+ *
+ * @author 123
+ */
+@Data
+@Schema(title = "云存储配置信息")
+public class CloudStorageConfig implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ @Schema(title = "类型 1:七牛 2:阿里云 3:腾讯云 ")
+ @Range(min = 1, max = 6, message = "{oss.type.range}")
+ private Integer type;
+
+ @Schema(title = "七牛绑定的域名")
+ @NotBlank(message = "{qiniu.domain.require}", groups = QiniuGroup.class)
+ @URL(message = "{qiniu.domain.url}", groups = QiniuGroup.class)
+ private String qiniuDomain;
+
+ @Schema(title = "七牛路径前缀")
+ private String qiniuPrefix;
+
+ @Schema(title = "七牛ACCESS_KEY")
+ @NotBlank(message = "{qiniu.accesskey.require}", groups = QiniuGroup.class)
+ private String qiniuAccessKey;
+
+ @Schema(title = "七牛SECRET_KEY")
+ @NotBlank(message = "{qiniu.secretkey.require}", groups = QiniuGroup.class)
+ private String qiniuSecretKey;
+
+ @Schema(title = "七牛存储空间名")
+ @NotBlank(message = "{qiniu.bucketName.require}", groups = QiniuGroup.class)
+ private String qiniuBucketName;
+
+ @Schema(title = "阿里云绑定的域名")
+ @NotBlank(message = "{aliyun.domain.require}", groups = AliyunGroup.class)
+ @URL(message = "{aliyun.domain.url}", groups = AliyunGroup.class)
+ private String aliyunDomain;
+
+ @Schema(title = "阿里云路径前缀")
+ private String aliyunPrefix;
+
+ @Schema(title = "阿里云EndPoint")
+ @NotBlank(message = "{aliyun.endPoint.require}", groups = AliyunGroup.class)
+ private String aliyunEndPoint;
+
+ @Schema(title = "阿里云AccessKeyId")
+ @NotBlank(message = "{aliyun.accesskeyid.require}", groups = AliyunGroup.class)
+ private String aliyunAccessKeyId;
+
+ @Schema(title = "阿里云AccessKeySecret")
+ @NotBlank(message = "{aliyun.accesskeysecret.require}", groups = AliyunGroup.class)
+ private String aliyunAccessKeySecret;
+
+ @Schema(title = "阿里云BucketName")
+ @NotBlank(message = "{aliyun.bucketName.require}", groups = AliyunGroup.class)
+ private String aliyunBucketName;
+
+ @Schema(title = "腾讯云绑定的域名")
+ @NotBlank(message = "{qcloud.domain.require}", groups = QcloudGroup.class)
+ @URL(message = "{qcloud.domain.url}", groups = QcloudGroup.class)
+ private String qcloudDomain;
+
+ @Schema(title = "腾讯云路径前缀")
+ private String qcloudPrefix;
+
+ @Schema(title = "腾讯云AppId")
+ @NotNull(message = "{qcloud.appid.require}", groups = QcloudGroup.class)
+ private Integer qcloudAppId;
+
+ @Schema(title = "腾讯云SecretId")
+ @NotBlank(message = "{qcloud.secretId.require}", groups = QcloudGroup.class)
+ private String qcloudSecretId;
+
+ @Schema(title = "腾讯云SecretKey")
+ @NotBlank(message = "{qcloud.secretkey.require}", groups = QcloudGroup.class)
+ private String qcloudSecretKey;
+
+ @Schema(title = "腾讯云BucketName")
+ @NotBlank(message = "{qcloud.bucketName.require}", groups = QcloudGroup.class)
+ private String qcloudBucketName;
+
+ @Schema(title = "腾讯云COS所属地区")
+ @NotBlank(message = "{qcloud.region.require}", groups = QcloudGroup.class)
+ private String qcloudRegion;
+
+}
diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/OSSFactory.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/OSSFactory.java
new file mode 100644
index 0000000..37a6891
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/OSSFactory.java
@@ -0,0 +1,35 @@
+
+
+package com.weather.modules.oss.cloud;
+
+import com.weather.common.constant.Constant;
+import com.weather.common.utils.SpringContextUtils;
+import com.weather.modules.sys.service.SysParamsService;
+
+/**
+ * 文件上传Factory
+ * @author 123
+ */
+public final class OSSFactory {
+ private static SysParamsService sysParamsService;
+
+ static {
+ OSSFactory.sysParamsService = SpringContextUtils.getBean(SysParamsService.class);
+ }
+
+ public static AbstractCloudStorageService build(){
+ //获取云存储配置信息
+ CloudStorageConfig config = sysParamsService.getValueObject(Constant.CLOUD_STORAGE_CONFIG_KEY, CloudStorageConfig.class);
+
+ if(config.getType() == Constant.CloudService.QINIU.getValue()){
+ return new QiniuCloudStorageService(config);
+ }else if(config.getType() == Constant.CloudService.ALIYUN.getValue()){
+ return new AliyunCloudStorageService(config);
+ }else if(config.getType() == Constant.CloudService.QCLOUD.getValue()){
+ return new QcloudCloudStorageService(config);
+ }
+
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/QcloudCloudStorageService.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/QcloudCloudStorageService.java
new file mode 100644
index 0000000..916943e
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/QcloudCloudStorageService.java
@@ -0,0 +1,80 @@
+
+
+package com.weather.modules.oss.cloud;
+
+import com.qcloud.cos.COSClient;
+import com.qcloud.cos.ClientConfig;
+import com.qcloud.cos.auth.BasicCOSCredentials;
+import com.qcloud.cos.auth.COSCredentials;
+import com.qcloud.cos.model.ObjectMetadata;
+import com.qcloud.cos.model.PutObjectRequest;
+import com.qcloud.cos.model.PutObjectResult;
+import com.qcloud.cos.region.Region;
+import com.weather.common.exception.ErrorCode;
+import com.weather.common.exception.CommonException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 腾讯云存储
+ *
+ * @author 123
+ */
+public class QcloudCloudStorageService extends AbstractCloudStorageService {
+ private COSCredentials credentials;
+ private ClientConfig clientConfig;
+
+ public QcloudCloudStorageService(CloudStorageConfig config){
+ this.config = config;
+
+ //初始化
+ init();
+ }
+
+ private void init(){
+ //1、初始化用户身份信息(secretId, secretKey)
+ credentials = new BasicCOSCredentials(config.getQcloudSecretId(), config.getQcloudSecretKey());
+
+ //2、设置bucket的区域, COS地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
+ clientConfig = new ClientConfig(new Region(config.getQcloudRegion()));
+ }
+
+ @Override
+ public String upload(byte[] data, String path) {
+ return upload(new ByteArrayInputStream(data), path);
+ }
+
+ @Override
+ public String upload(InputStream inputStream, String path) {
+ try {
+ COSClient client = new COSClient(credentials, clientConfig);
+
+ ObjectMetadata metadata = new ObjectMetadata();
+ metadata.setContentLength(inputStream.available());
+ String bucketName = config.getQcloudBucketName() +"-"+ config.getQcloudAppId();
+ PutObjectRequest request = new PutObjectRequest(bucketName, path, inputStream, metadata);
+ PutObjectResult result = client.putObject(request);
+
+ client.shutdown();
+ if(result.getETag() == null){
+ throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, "");
+ }
+ } catch (IOException e) {
+ throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, "");
+ }
+
+ return config.getQcloudDomain() + "/" + path;
+ }
+
+ @Override
+ public String uploadSuffix(byte[] data, String suffix) {
+ return upload(data, getPath(config.getQcloudPrefix(), suffix));
+ }
+
+ @Override
+ public String uploadSuffix(InputStream inputStream, String suffix) {
+ return upload(inputStream, getPath(config.getQcloudPrefix(), suffix));
+ }
+}
\ No newline at end of file
diff --git a/system-admin/src/main/java/com/weather/modules/oss/cloud/QiniuCloudStorageService.java b/system-admin/src/main/java/com/weather/modules/oss/cloud/QiniuCloudStorageService.java
new file mode 100644
index 0000000..21e1e6e
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/oss/cloud/QiniuCloudStorageService.java
@@ -0,0 +1,72 @@
+
+package com.weather.modules.oss.cloud;
+
+import com.qiniu.http.Response;
+import com.qiniu.storage.Configuration;
+import com.qiniu.storage.Region;
+import com.qiniu.storage.UploadManager;
+import com.qiniu.util.Auth;
+import com.qiniu.util.IOUtils;
+import com.weather.common.exception.ErrorCode;
+import com.weather.common.exception.CommonException;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * 七牛云存储
+ *
+ * @author 123
+ */
+public class QiniuCloudStorageService extends AbstractCloudStorageService {
+ private UploadManager uploadManager;
+ private String token;
+
+ public QiniuCloudStorageService(CloudStorageConfig config) {
+ this.config = config;
+
+ //初始化
+ init();
+ }
+
+ private void init() {
+ uploadManager = new UploadManager(new Configuration(Region.autoRegion()));
+ token = Auth.create(config.getQiniuAccessKey(), config.getQiniuSecretKey()).
+ uploadToken(config.getQiniuBucketName());
+
+ }
+
+ @Override
+ public String upload(byte[] data, String path) {
+ try {
+ Response res = uploadManager.put(data, path, token);
+ if (!res.isOK()) {
+ throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, res.toString());
+ }
+ } catch (Exception e) {
+ throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, "");
+ }
+
+ return config.getQiniuDomain() + "/" + path;
+ }
+
+ @Override
+ public String upload(InputStream inputStream, String path) {
+ try {
+ byte[] data = IOUtils.toByteArray(inputStream);
+ return this.upload(data, path);
+ } catch (IOException e) {
+ throw new CommonException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, "");
+ }
+ }
+
+ @Override
+ public String uploadSuffix(byte[] data, String suffix) {
+ return upload(data, getPath(config.getQiniuPrefix(), suffix));
+ }
+
+ @Override
+ public String uploadSuffix(InputStream inputStream, String suffix) {
+ return upload(inputStream, getPath(config.getQiniuPrefix(), suffix));
+ }
+}
diff --git a/system-admin/src/main/java/com/weather/modules/oss/controller/SysOssController.java b/system-admin/src/main/java/com/weather/modules/oss/controller/SysOssController.java
new file mode 100644
index 0000000..a5a165e
--- /dev/null
+++ b/system-admin/src/main/java/com/weather/modules/oss/controller/SysOssController.java
@@ -0,0 +1,124 @@
+
+package com.weather.modules.oss.controller;
+
+import cn.hutool.core.io.file.FileNameUtil;
+import com.weather.common.annotation.LogOperation;
+import com.weather.common.constant.Constant;
+import com.weather.common.exception.ErrorCode;
+import com.weather.common.page.PageData;
+import com.weather.common.utils.JsonUtils;
+import com.weather.common.utils.Result;
+import com.weather.common.validator.ValidatorUtils;
+import com.weather.common.validator.group.AliyunGroup;
+import com.weather.common.validator.group.QcloudGroup;
+import com.weather.common.validator.group.QiniuGroup;
+import com.weather.modules.oss.cloud.CloudStorageConfig;
+import com.weather.modules.oss.cloud.OSSFactory;
+import com.weather.modules.oss.entity.SysOssEntity;
+import com.weather.modules.oss.service.SysOssService;
+import com.weather.modules.sys.service.SysParamsService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.AllArgsConstructor;
+import org.apache.shiro.authz.annotation.RequiresPermissions;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 文件上传
+ *
+ * @author 123
+ */
+@RestController
+@RequestMapping("sys/oss")
+@Tag(name = "文件上传")
+@AllArgsConstructor
+public class SysOssController {
+ private final SysOssService sysOssService;
+ private final SysParamsService sysParamsService;
+
+ private final static String KEY = Constant.CLOUD_STORAGE_CONFIG_KEY;
+
+ @GetMapping("page")
+ @Operation(summary = "分页")
+ @RequiresPermissions("sys:oss:all")
+ public Result> page(@Parameter(hidden = true) @RequestParam Map params) {
+ PageData page = sysOssService.page(params);
+
+ return new Result>().ok(page);
+ }
+
+ @GetMapping("info")
+ @Operation(summary = "云存储配置信息")
+ @RequiresPermissions("sys:oss:all")
+ public Result info() {
+ CloudStorageConfig config = sysParamsService.getValueObject(KEY, CloudStorageConfig.class);
+
+ return new Result().ok(config);
+ }
+
+ @PostMapping
+ @Operation(summary = "保存云存储配置信息")
+ @LogOperation("保存云存储配置信息")
+ @RequiresPermissions("sys:oss:all")
+ public Result saveConfig(@RequestBody CloudStorageConfig config) {
+ //校验类型
+ ValidatorUtils.validateEntity(config);
+
+ if (config.getType() == Constant.CloudService.QINIU.getValue()) {
+ //校验七牛数据
+ ValidatorUtils.validateEntity(config, QiniuGroup.class);
+ } else if (config.getType() == Constant.CloudService.ALIYUN.getValue()) {
+ //校验阿里云数据
+ ValidatorUtils.validateEntity(config, AliyunGroup.class);
+ } else if (config.getType() == Constant.CloudService.QCLOUD.getValue()) {
+ //校验腾讯云数据
+ ValidatorUtils.validateEntity(config, QcloudGroup.class);
+ }
+
+ sysParamsService.updateValueByCode(KEY, JsonUtils.toJsonString(config));
+
+ return new Result();
+ }
+
+ @PostMapping("upload")
+ @Operation(summary = "上传文件")
+ @RequiresPermissions("sys:oss:all")
+ public Result