初始化
This commit is contained in:
20
tashow-module/pom.xml
Normal file
20
tashow-module/pom.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-platform</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>tashow-module</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<modules>
|
||||
<module>tashow-module-system</module>
|
||||
<module>tashow-module-infra</module>
|
||||
<module>tashow-module-app</module>
|
||||
<module>tashow-module-product</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
79
tashow-module/tashow-module-app/pom.xml
Normal file
79
tashow-module/tashow-module-app/pom.xml
Normal file
@@ -0,0 +1,79 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-module</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>tashow-module-app</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!-- Registry 注册中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Config 配置中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 远程调用相关 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-rpc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-data-mybatis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-env</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-infra-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-data-redis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-security</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-feishu-sdk</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.amqp</groupId>
|
||||
<artifactId>spring-rabbit</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-data-redis</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.tashow.cloud.app;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* 应用服务启动类
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@ComponentScan(basePackages = {"com.tashow.cloud.app", "com.tashow.cloud.sdk.feishu"})
|
||||
public class AppServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AppServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.tashow.cloud.app.config;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* 应用配置类
|
||||
*/
|
||||
@Configuration
|
||||
public class AppConfig {
|
||||
|
||||
/**
|
||||
* 提供ObjectMapper bean用于JSON处理
|
||||
*/
|
||||
@Bean
|
||||
public ObjectMapper objectMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.tashow.cloud.app.controller;
|
||||
|
||||
import cn.hutool.json.JSONObject;
|
||||
import com.lark.oapi.core.utils.Decryptor;
|
||||
import com.tashow.cloud.app.service.feishu.FeiShuCardDataService;
|
||||
import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient;
|
||||
import com.tashow.cloud.sdk.feishu.config.LarkConfig;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
public class FeishuController {
|
||||
private static final Logger log = LoggerFactory.getLogger(FeishuController.class);
|
||||
private static final String ACTION_COMPLETE_ALARM = "complete_alarm";
|
||||
private final FeiShuAlertClient feiShuAlertClient;
|
||||
private final FeiShuCardDataService feiShuCardDataService;
|
||||
private final LarkConfig larkConfig;
|
||||
|
||||
@Autowired
|
||||
public FeishuController(FeiShuAlertClient feiShuAlertClient, FeiShuCardDataService feiShuCardDataService, LarkConfig larkConfig) {
|
||||
this.feiShuAlertClient = feiShuAlertClient;
|
||||
this.feiShuCardDataService = feiShuCardDataService;
|
||||
this.larkConfig = larkConfig;
|
||||
}
|
||||
|
||||
@RequestMapping("/card")
|
||||
@PermitAll
|
||||
public String card(@RequestBody JSONObject data) {
|
||||
try {
|
||||
if (data.containsKey("app_id") && data.containsKey("action")) {
|
||||
JSONObject action = data.getJSONObject("action");
|
||||
JSONObject value = action.getJSONObject("value");
|
||||
if (value != null && ACTION_COMPLETE_ALARM.equals(value.getStr("action"))) {
|
||||
String messageId = data.getStr("open_message_id");
|
||||
Map<String, Object> templateData = feiShuCardDataService.getCardData(messageId);
|
||||
templateData.put("open_id", data.getStr("open_id"));
|
||||
templateData.put("complete_time", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||
JSONObject fromValue = action.getJSONObject("form_value");
|
||||
templateData.put("notes", fromValue.getStr("notes_input"));
|
||||
return feiShuAlertClient.buildCardWithData(larkConfig.getSuccessCards(), templateData);
|
||||
}
|
||||
}
|
||||
if (data.containsKey("encrypt")) {
|
||||
Decryptor decryptor = new Decryptor(larkConfig.getEncryptKey());
|
||||
return decryptor.decrypt(data.getStr("encrypt"));
|
||||
}
|
||||
return "{}";
|
||||
} catch (Exception e) {
|
||||
log.error("卡片处理异常", e);
|
||||
return "{\"code\":1,\"msg\":\"处理异常: " + e.getMessage() + "\"}";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.tashow.cloud.app.controller;
|
||||
|
||||
|
||||
public class LoginController {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.tashow.cloud.app.controller;
|
||||
import com.tashow.cloud.app.mapper.BuriedPointMapper;
|
||||
import com.tashow.cloud.app.mq.producer.buriedPoint.BuriedPointProducer;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 测试控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class TestController {
|
||||
/**
|
||||
* 基础测试接口
|
||||
*/
|
||||
@GetMapping("/test")
|
||||
@PermitAll
|
||||
public String test() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
package com.tashow.cloud.app.dal.dataobject;
|
||||
// 数据库对象
|
||||
@@ -0,0 +1,2 @@
|
||||
package com.tashow.cloud.app.dal.dto;
|
||||
// 视图层与业务层传输对象
|
||||
@@ -0,0 +1 @@
|
||||
package com.tashow.cloud.app.dal;
|
||||
@@ -0,0 +1,2 @@
|
||||
package com.tashow.cloud.app.dal.vo;
|
||||
// 视图参数接收
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.tashow.cloud.app.interceptor;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import com.tashow.cloud.app.mq.producer.buriedPoint.BuriedPointProducer;
|
||||
import com.tashow.cloud.common.util.json.JsonUtils;
|
||||
import com.tashow.cloud.common.util.servlet.ServletUtils;
|
||||
import com.tashow.cloud.common.util.spring.SpringUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StopWatch;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
/**
|
||||
* 后端静默埋点拦截器
|
||||
* 用于收集API请求信息并异步发送到消息队列
|
||||
*/
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class BuriedPointInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final BuriedPointProducer buriedPointProducer;
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
if (!(handler instanceof HandlerMethod handlerMethod)) {
|
||||
return true;
|
||||
}
|
||||
BuriedMessages message = new BuriedMessages(
|
||||
request,
|
||||
handlerMethod
|
||||
);
|
||||
buriedPointProducer.asyncSendMessage(message);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.tashow.cloud.app.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.tashow.cloud.app.model.MqMessageRecord;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
/**
|
||||
* 埋点消息发送记录Mapper接口
|
||||
*/
|
||||
@Mapper
|
||||
public interface BuriedPointFailRecordMapper extends BaseMapper<MqMessageRecord> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.tashow.cloud.app.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.tashow.cloud.app.model.BuriedPoint;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
|
||||
@Mapper
|
||||
public interface BuriedPointMapper extends BaseMapper<BuriedPoint> {
|
||||
|
||||
@Select("SELECT * FROM app_burying WHERE event_id = #{eventId} LIMIT 1")
|
||||
BuriedPoint selectByEventId(@Param("eventId") Integer eventId);
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.tashow.cloud.app.model;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.Accessors;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 埋点数据实体类
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@Accessors(chain = true)
|
||||
@TableName(value = "app_burying")
|
||||
public class BuriedPoint {
|
||||
/**
|
||||
* 主键ID
|
||||
*/
|
||||
@TableId(value = "id", type = IdType.AUTO)
|
||||
private Integer id;
|
||||
/**
|
||||
* 事件唯一ID
|
||||
*/
|
||||
@TableField(value = "event_id")
|
||||
private Integer eventId;
|
||||
|
||||
/**
|
||||
* 事件时间戳
|
||||
*/
|
||||
@TableField(value = "event_time")
|
||||
private Long eventTime;
|
||||
|
||||
/**
|
||||
* 服务名称
|
||||
*/
|
||||
@TableField(value = "service")
|
||||
private String service;
|
||||
|
||||
/**
|
||||
* 方法/接口
|
||||
*/
|
||||
@TableField(value = "method")
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 用户标识
|
||||
*/
|
||||
@TableField(value = "user_id")
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 会话标识
|
||||
*/
|
||||
@TableField(value = "session_id")
|
||||
private String sessionId;
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
@TableField(value = "client_ip")
|
||||
private String clientIp;
|
||||
|
||||
/**
|
||||
* 服务器IP
|
||||
*/
|
||||
@TableField(value = "server_ip")
|
||||
private String serverIp;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
@TableField(value = "event_type")
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 页面路径/功能模块
|
||||
*/
|
||||
@TableField(value = "page_path")
|
||||
private String pagePath;
|
||||
|
||||
/**
|
||||
* 元素标识
|
||||
*/
|
||||
@TableField(value = "element_id")
|
||||
private String elementId;
|
||||
|
||||
/**
|
||||
* 操作时长(毫秒)
|
||||
*/
|
||||
@TableField(value = "duration")
|
||||
private Long duration;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
@TableField(value = "create_time")
|
||||
private Date createTime;
|
||||
|
||||
|
||||
@TableField(value = "update_time")
|
||||
private Date updateTime;
|
||||
|
||||
@TableField(value = "status")
|
||||
private Integer status;
|
||||
|
||||
|
||||
public BuriedPoint(BuriedMessages message) {
|
||||
this.eventId = message.getId();
|
||||
this.eventTime = System.currentTimeMillis();
|
||||
this.userId = message.getUserId();
|
||||
this.eventType = message.getEventType();
|
||||
this.service = message.getService();
|
||||
this.method = message.getMethod();
|
||||
this.sessionId = message.getSessionId();
|
||||
this.clientIp = message.getClientIp();
|
||||
this.serverIp = message.getServerIp();
|
||||
this.status = message.getStatusCode();
|
||||
this.pagePath = message.getPagePath();
|
||||
this.elementId = message.getElementId();
|
||||
this.createTime = new Date();
|
||||
this.updateTime = new Date();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.tashow.cloud.app.model;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 埋点消息发送失败记录实体类
|
||||
*/
|
||||
@Data
|
||||
@TableName("mq_message_record")
|
||||
public class MqMessageRecord {
|
||||
/**
|
||||
* 状态常量定义
|
||||
*/
|
||||
public static final int STATUS_UNPROCESSED = 10; // 未处理
|
||||
public static final int STATUS_SUCCESS = 20; // 处理成功
|
||||
public static final int STATUS_FAILED = 30; // 发送失败
|
||||
@TableId
|
||||
private Integer id;
|
||||
|
||||
|
||||
/**
|
||||
* 交换机名称
|
||||
*/
|
||||
private String exchange;
|
||||
|
||||
/**
|
||||
* 路由键
|
||||
*/
|
||||
private String routingKey;
|
||||
|
||||
/**
|
||||
* 失败原因
|
||||
*/
|
||||
private String cause;
|
||||
|
||||
/**
|
||||
* 消息内容
|
||||
*/
|
||||
private String messageContent;
|
||||
|
||||
/**
|
||||
* 重试次数
|
||||
*/
|
||||
private Integer retryCount;
|
||||
|
||||
/**
|
||||
* 状态:0-未处理,1-处理中,2-处理成功,3-处理失败
|
||||
*/
|
||||
private Integer status;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
private Date updateTime;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.tashow.cloud.app.mq.config;
|
||||
|
||||
import com.tashow.cloud.app.interceptor.BuriedPointInterceptor;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import com.tashow.cloud.app.mq.producer.buriedPoint.BuriedPointProducer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.core.*;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* 埋点功能配置类
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class BuriedPointConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final BuriedPointProducer buriedPointProducer;
|
||||
|
||||
/**
|
||||
* 创建埋点队列
|
||||
*/
|
||||
@Bean
|
||||
public Queue buriedPointQueue() {
|
||||
return new Queue(BuriedMessages.QUEUE, true, false, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建埋点交换机
|
||||
*/
|
||||
@Bean
|
||||
public DirectExchange buriedPointExchange() {
|
||||
return new DirectExchange(BuriedMessages.EXCHANGE, true, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建埋点绑定关系
|
||||
*/
|
||||
@Bean
|
||||
public Binding buriedPointBinding() {
|
||||
return BindingBuilder.bind(buriedPointQueue())
|
||||
.to(buriedPointExchange())
|
||||
.with(BuriedMessages.ROUTING_KEY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建埋点拦截器
|
||||
*/
|
||||
@Bean
|
||||
public BuriedPointInterceptor buriedPointInterceptor() {
|
||||
return new BuriedPointInterceptor(buriedPointProducer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册埋点拦截器
|
||||
*/
|
||||
@Override
|
||||
public void addInterceptors(InterceptorRegistry registry) {
|
||||
// 注册拦截器,拦截所有请求
|
||||
registry.addInterceptor(buriedPointInterceptor())
|
||||
// 可以根据需要添加或排除特定路径
|
||||
.addPathPatterns("/**")
|
||||
// 排除静态资源、Swagger等路径
|
||||
.excludePathPatterns(
|
||||
"/swagger-ui/**",
|
||||
"/swagger-resources/**",
|
||||
"/v3/api-docs/**",
|
||||
"/webjars/**",
|
||||
"/static/**",
|
||||
"/card",
|
||||
"/error"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.tashow.cloud.app.mq.consumer.buriedPoint;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.tashow.cloud.app.mapper.BuriedPointMapper;
|
||||
import com.tashow.cloud.app.mapper.BuriedPointFailRecordMapper;
|
||||
import com.tashow.cloud.app.model.BuriedPoint;
|
||||
import com.tashow.cloud.app.model.MqMessageRecord;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import com.tashow.cloud.app.service.feishu.BuriedPointMonitorService;
|
||||
import com.rabbitmq.client.Channel;
|
||||
import com.tashow.cloud.mq.rabbitmq.consumer.AbstractRabbitMQConsumer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
|
||||
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||
import org.springframework.amqp.support.AmqpHeaders;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.messaging.handler.annotation.Header;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.Date;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
/**
|
||||
* 埋点消息消费者
|
||||
*/
|
||||
@Component
|
||||
@RabbitListener(queues = BuriedMessages.QUEUE)
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class BuriedPointConsumer extends AbstractRabbitMQConsumer<BuriedMessages> {
|
||||
private final BuriedPointMapper buriedPointMapper;
|
||||
private final BuriedPointMonitorService buriedPointMonitorService;
|
||||
|
||||
@Value("${spring.application.name:tashow-app}")
|
||||
private String applicationName;
|
||||
|
||||
@RabbitHandler
|
||||
public void handleMessage(BuriedMessages message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
|
||||
onMessage(message, channel, deliveryTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理埋点消息
|
||||
* @param message 消息对象
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public boolean processMessage(BuriedMessages message) {
|
||||
try {
|
||||
BuriedPoint existingPoint = buriedPointMapper.selectByEventId(message.getId());
|
||||
if (existingPoint != null) {
|
||||
existingPoint.setStatus(message.getStatusCode());
|
||||
existingPoint.setUpdateTime(new Date());
|
||||
return buriedPointMapper.updateById(existingPoint) > 0;
|
||||
}
|
||||
BuriedPoint buriedPoint = new BuriedPoint(message);
|
||||
buriedPoint.setService(applicationName);
|
||||
buriedPointMapper.insert(buriedPoint);
|
||||
|
||||
if(buriedPoint.getStatus() == BuriedMessages.STATUS_ERROR){
|
||||
buriedPointMonitorService.checkFailRecordsAndAlert("埋点数据处理异常");
|
||||
}
|
||||
return true;
|
||||
} catch (DuplicateKeyException e) {
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error("[埋点消费者] 保存数据失败", e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.tashow.cloud.app.mq.handler;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.tashow.cloud.app.mapper.BuriedPointFailRecordMapper;
|
||||
import com.tashow.cloud.app.model.MqMessageRecord;
|
||||
import com.tashow.cloud.app.service.feishu.BuriedPointMonitorService;
|
||||
import com.tashow.cloud.mq.handler.FailRecordHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.Date;
|
||||
/**
|
||||
* MQ消息记录处理器
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class BuriedPointFailRecordHandler implements FailRecordHandler {
|
||||
private final BuriedPointFailRecordMapper buriedPointFailRecordMapper;
|
||||
private final BuriedPointMonitorService buriedPointMonitorService;
|
||||
/**
|
||||
* 保存消息记录=
|
||||
*/
|
||||
@Override
|
||||
public void saveMessageRecord(Integer id, String exchange, String routingKey, String cause, String messageContent, int status) {
|
||||
try {
|
||||
MqMessageRecord existingRecord = findExistingRecord(id);
|
||||
if (existingRecord != null) {
|
||||
existingRecord.setRetryCount(existingRecord.getRetryCount() + 1);
|
||||
existingRecord.setMessageContent(messageContent);
|
||||
existingRecord.setStatus(status);
|
||||
existingRecord.setCause(cause);
|
||||
existingRecord.setUpdateTime(new Date());
|
||||
buriedPointFailRecordMapper.updateById(existingRecord);
|
||||
} else {
|
||||
MqMessageRecord record = new MqMessageRecord();
|
||||
record.setId(id);
|
||||
record.setExchange(exchange);
|
||||
record.setRoutingKey(routingKey);
|
||||
record.setCause(cause);
|
||||
record.setMessageContent(messageContent);
|
||||
record.setRetryCount(0);
|
||||
record.setStatus(status);
|
||||
record.setCreateTime(new Date());
|
||||
record.setUpdateTime(new Date());
|
||||
buriedPointFailRecordMapper.insert(record);
|
||||
if (status == MqMessageRecord.STATUS_FAILED) {
|
||||
buriedPointMonitorService.checkFailRecordsAndAlert(cause);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[MQ消息处理器] 保存消息记录异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新消息状态
|
||||
*/
|
||||
@Override
|
||||
public void updateMessageStatus(Integer id) {
|
||||
try {
|
||||
MqMessageRecord record = findExistingRecord(id);
|
||||
if (record != null) {
|
||||
record.setStatus(MqMessageRecord.STATUS_SUCCESS);
|
||||
record.setUpdateTime(new Date());
|
||||
buriedPointFailRecordMapper.updateById(record);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[MQ消息处理器] 更新消息状态异常: {}", id, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新消息状态并设置失败原因
|
||||
*/
|
||||
@Override
|
||||
public void updateMessageStatusWithCause(Integer id, String cause) {
|
||||
try {
|
||||
MqMessageRecord record = findExistingRecord(id);
|
||||
if (record != null) {
|
||||
record.setStatus(MqMessageRecord.STATUS_FAILED);
|
||||
record.setCause(cause);
|
||||
record.setUpdateTime(new Date());
|
||||
buriedPointFailRecordMapper.updateById(record);
|
||||
buriedPointMonitorService.checkFailRecordsAndAlert(cause);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("[MQ消息处理器] 更新消息状态和原因异常: {}", id, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查找已存在的失败记录
|
||||
*/
|
||||
private MqMessageRecord findExistingRecord(Integer id) {
|
||||
LambdaQueryWrapper<MqMessageRecord> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(MqMessageRecord::getId, id);
|
||||
return buriedPointFailRecordMapper.selectOne(queryWrapper);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
package com.tashow.cloud.app.mq.message;
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.tashow.cloud.common.util.json.JsonUtils;
|
||||
import com.tashow.cloud.common.util.servlet.ServletUtils;
|
||||
import com.tashow.cloud.common.util.spring.SpringUtils;
|
||||
import com.tashow.cloud.mq.core.BaseMqMessage;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Date;
|
||||
|
||||
import static com.tashow.cloud.web.apilog.core.interceptor.ApiAccessLogInterceptor.ATTRIBUTE_HANDLER_METHOD;
|
||||
|
||||
/**
|
||||
* 埋点消息
|
||||
*/
|
||||
@Data
|
||||
public class BuriedMessages extends BaseMqMessage {
|
||||
private static final String ATTRIBUTE_REQUEST_ID = "BuriedPoint.RequestId";
|
||||
|
||||
|
||||
/**
|
||||
* 交换机名称
|
||||
*/
|
||||
public static final String EXCHANGE = "tashow.buried.point.exchange";
|
||||
|
||||
/**
|
||||
* 队列名称
|
||||
*/
|
||||
public static final String QUEUE = "tashow.buried.point.queue";
|
||||
|
||||
/**
|
||||
* 路由键
|
||||
*/
|
||||
public static final String ROUTING_KEY = "tashow.buried.point.routing.key";
|
||||
|
||||
/**
|
||||
* 消息状态:处理中
|
||||
*/
|
||||
public static final int STATUS_PROCESSING = 10;
|
||||
|
||||
/**
|
||||
* 消息状态:成功
|
||||
*/
|
||||
public static final int STATUS_SUCCESS = 20;
|
||||
|
||||
/**
|
||||
* 消息状态:失败
|
||||
*/
|
||||
public static final int STATUS_ERROR = 30;
|
||||
|
||||
/**
|
||||
* 事件时间
|
||||
*/
|
||||
private Date eventTime;
|
||||
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
private String userId;
|
||||
|
||||
/**
|
||||
* 事件类型
|
||||
*/
|
||||
private String eventType;
|
||||
|
||||
/**
|
||||
* 方法名称
|
||||
*/
|
||||
private String method;
|
||||
|
||||
/**
|
||||
* 会话ID
|
||||
*/
|
||||
private String sessionId;
|
||||
|
||||
/**
|
||||
* 客户端IP
|
||||
*/
|
||||
private String clientIp;
|
||||
|
||||
/**
|
||||
* 服务端IP
|
||||
*/
|
||||
private String serverIp;
|
||||
|
||||
/**
|
||||
* 页面路径
|
||||
*/
|
||||
private String pagePath;
|
||||
|
||||
/**
|
||||
* 元素ID
|
||||
*/
|
||||
private String elementId;
|
||||
|
||||
/**
|
||||
* 服务名称
|
||||
*/
|
||||
private String service;
|
||||
|
||||
public BuriedMessages() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 从请求创建埋点消息
|
||||
*
|
||||
* @param request HTTP请求
|
||||
* @param handlerMethod 处理方法
|
||||
*/
|
||||
public BuriedMessages(HttpServletRequest request, HandlerMethod handlerMethod) {
|
||||
try {
|
||||
int requestId = (int)(Math.abs(IdUtil.getSnowflakeNextId()) % Integer.MAX_VALUE);
|
||||
this.setId(requestId);
|
||||
this.eventTime = new Date();
|
||||
this.service = SpringUtils.getApplicationName();
|
||||
this.method = request.getMethod() + " " + request.getRequestURI() +
|
||||
JsonUtils.toJsonString(request.getParameterMap());
|
||||
Object userId = request.getSession().getAttribute("USER_ID");
|
||||
this.userId = userId != null ? userId.toString() : "anonymous";
|
||||
this.sessionId = request.getSession().getId();
|
||||
this.clientIp = ServletUtils.getClientIP(request);
|
||||
try {
|
||||
this.serverIp = InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
this.serverIp = "unknown";
|
||||
}
|
||||
String controllerName = handlerMethod.getBeanType().getSimpleName();
|
||||
String actionName = handlerMethod.getMethod().getName();
|
||||
this.pagePath = controllerName + "#" + actionName;
|
||||
this.eventType = "API_REQUEST_START";
|
||||
this.setStatusCode(STATUS_PROCESSING);
|
||||
request.setAttribute(ATTRIBUTE_REQUEST_ID, this.getId());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("创建埋点消息失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.tashow.cloud.app.mq.producer.buriedPoint;
|
||||
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import com.tashow.cloud.common.util.json.JsonUtils;
|
||||
import com.tashow.cloud.mq.rabbitmq.producer.AbstractRabbitMQProducer;
|
||||
import org.springframework.amqp.core.ReturnedMessage;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 埋点消息生产者
|
||||
*/
|
||||
@Component
|
||||
public class BuriedPointProducer extends AbstractRabbitMQProducer<BuriedMessages> {
|
||||
|
||||
@Override
|
||||
public void returnedMessage(ReturnedMessage returned) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getExchange() {
|
||||
return BuriedMessages.EXCHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRoutingKey() {
|
||||
return BuriedMessages.ROUTING_KEY;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.tashow.cloud.app.security.config;
|
||||
|
||||
import com.tashow.cloud.infraapi.enums.ApiConstants;
|
||||
import com.tashow.cloud.security.security.config.AuthorizeRequestsCustomizer;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer;
|
||||
|
||||
/**
|
||||
* Infra 模块的 Security 配置
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false, value = "infraSecurityConfiguration")
|
||||
public class SecurityConfiguration {
|
||||
|
||||
@Value("${spring.boot.admin.context-path:''}")
|
||||
private String adminSeverContextPath;
|
||||
|
||||
@Bean("infraAuthorizeRequestsCustomizer")
|
||||
public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() {
|
||||
return new AuthorizeRequestsCustomizer() {
|
||||
@Override
|
||||
public void customize(AuthorizeHttpRequestsConfigurer<HttpSecurity>.AuthorizationManagerRequestMatcherRegistry registry) {
|
||||
// Spring Boot Actuator 的安全配置
|
||||
registry.requestMatchers("/actuator").permitAll()
|
||||
.requestMatchers("/actuator/**").permitAll();
|
||||
// Druid 监控
|
||||
registry.requestMatchers("/druid/**").permitAll();
|
||||
// Spring Boot Admin Server 的安全配置
|
||||
registry.requestMatchers(adminSeverContextPath).permitAll()
|
||||
.requestMatchers(adminSeverContextPath + "/**").permitAll();
|
||||
// 文件读取
|
||||
registry.requestMatchers(buildAdminApi("/infra/file/*/get/**")).permitAll();
|
||||
|
||||
// TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案
|
||||
// RPC 服务的安全配置
|
||||
registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package com.tashow.cloud.app.security.core;
|
||||
@@ -0,0 +1,241 @@
|
||||
package com.tashow.cloud.app.service.feishu;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.tashow.cloud.app.mapper.BuriedPointFailRecordMapper;
|
||||
import com.tashow.cloud.app.mapper.BuriedPointMapper;
|
||||
import com.tashow.cloud.app.model.BuriedPoint;
|
||||
import com.tashow.cloud.app.model.MqMessageRecord;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import com.tashow.cloud.sdk.feishu.client.FeiShuAlertClient;
|
||||
import com.tashow.cloud.sdk.feishu.config.LarkConfig;
|
||||
import com.tashow.cloud.sdk.feishu.util.ChartImageGenerator;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 埋点监控服务
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class BuriedPointMonitorService {
|
||||
private static final int ALERT_THRESHOLD = 3;
|
||||
private static final int MONITORING_HOURS = 12;
|
||||
private final Map<String, Long> alertCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final BuriedPointFailRecordMapper buriedPointFailRecordMapper;
|
||||
private final BuriedPointMapper buriedPointMapper;
|
||||
private final FeiShuAlertClient feiShuAlertClient;
|
||||
private final FeiShuCardDataService feiShuCardDataService;
|
||||
private final LarkConfig larkConfig;
|
||||
|
||||
/**
|
||||
* 检查失败记录并发送告警
|
||||
*/
|
||||
public boolean checkFailRecordsAndAlert(String cause) {
|
||||
try {
|
||||
Date now = new Date();
|
||||
Date hoursAgo = getDateHoursAgo(now, MONITORING_HOURS);
|
||||
boolean sentAlert = false;
|
||||
List<Date[]> timeRanges = getHourRanges(hoursAgo, now);
|
||||
long mqFailCount = countFailures(buriedPointFailRecordMapper, MqMessageRecord.class, hoursAgo, now);
|
||||
long buriedFailCount = countFailures(buriedPointMapper, BuriedPoint.class, hoursAgo, now);
|
||||
if (mqFailCount > ALERT_THRESHOLD||buriedFailCount > ALERT_THRESHOLD) {
|
||||
if (!hasRecentlySentAlert(cause)) {
|
||||
sendAlert(mqFailCount, cause, getMqStats(timeRanges));
|
||||
alertCache.put(cause, System.currentTimeMillis());
|
||||
sentAlert = true;
|
||||
}
|
||||
}
|
||||
|
||||
return sentAlert;
|
||||
} catch (Exception e) {
|
||||
log.error("[埋点监控] 检查失败记录异常", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否最近已发送过相同类型的告警
|
||||
*/
|
||||
private boolean hasRecentlySentAlert(String alertType) {
|
||||
Long lastSentTime = alertCache.get(alertType);
|
||||
if (lastSentTime == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
long hourInMillis = MONITORING_HOURS * 60 * 60 * 1000L;
|
||||
return (System.currentTimeMillis() - lastSentTime) < hourInMillis;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取消息队列统计数据
|
||||
*/
|
||||
private List<ChartImageGenerator.MonitoringDataPoint> getMqStats(List<Date[]> timeRanges) {
|
||||
Map<Date, Integer> successData = batchQueryMqStatus(timeRanges, MqMessageRecord.STATUS_SUCCESS);
|
||||
Map<Date, Integer> failedData = batchQueryMqFailures(timeRanges);
|
||||
|
||||
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:00");
|
||||
return timeRanges.stream()
|
||||
.map(range -> new ChartImageGenerator.MonitoringDataPoint(
|
||||
timeFormat.format(range[0]),
|
||||
successData.getOrDefault(range[0], 0),
|
||||
failedData.getOrDefault(range[0], 0)
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取埋点表统计数据
|
||||
*/
|
||||
private List<ChartImageGenerator.MonitoringDataPoint> getBuriedStats(List<Date[]> timeRanges) {
|
||||
// 批量查询每个时间区间的数据
|
||||
Map<Date, Integer> successData = batchQueryBuriedStatus(timeRanges, BuriedMessages.STATUS_SUCCESS);
|
||||
Map<Date, Integer> failedData = batchQueryBuriedStatus(timeRanges, BuriedMessages.STATUS_ERROR);
|
||||
SimpleDateFormat timeFormat = new SimpleDateFormat("HH:00");
|
||||
return timeRanges.stream()
|
||||
.map(range -> new ChartImageGenerator.MonitoringDataPoint(
|
||||
timeFormat.format(range[0]),
|
||||
successData.getOrDefault(range[0], 0),
|
||||
failedData.getOrDefault(range[0], 0)
|
||||
))
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询MQ状态数据
|
||||
*/
|
||||
private Map<Date, Integer> batchQueryMqStatus(List<Date[]> timeRanges, int status) {
|
||||
Map<Date, Integer> result = new HashMap<>();
|
||||
for (Date[] range : timeRanges) {
|
||||
LambdaQueryWrapper<MqMessageRecord> query = new LambdaQueryWrapper<>();
|
||||
query.ge(MqMessageRecord::getCreateTime, range[0])
|
||||
.lt(MqMessageRecord::getCreateTime, range[1])
|
||||
.eq(MqMessageRecord::getStatus, status);
|
||||
result.put(range[0], buriedPointFailRecordMapper.selectCount(query).intValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询MQ失败数据
|
||||
*/
|
||||
private Map<Date, Integer> batchQueryMqFailures(List<Date[]> timeRanges) {
|
||||
Map<Date, Integer> result = new HashMap<>();
|
||||
for (Date[] range : timeRanges) {
|
||||
LambdaQueryWrapper<MqMessageRecord> query = new LambdaQueryWrapper<>();
|
||||
query.ge(MqMessageRecord::getCreateTime, range[0])
|
||||
.lt(MqMessageRecord::getCreateTime, range[1])
|
||||
.in(MqMessageRecord::getStatus, Arrays.asList(
|
||||
MqMessageRecord.STATUS_UNPROCESSED, MqMessageRecord.STATUS_FAILED));
|
||||
result.put(range[0], buriedPointFailRecordMapper.selectCount(query).intValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量查询埋点状态数据
|
||||
*/
|
||||
private Map<Date, Integer> batchQueryBuriedStatus(List<Date[]> timeRanges, int status) {
|
||||
Map<Date, Integer> result = new HashMap<>();
|
||||
for (Date[] range : timeRanges) {
|
||||
LambdaQueryWrapper<BuriedPoint> query = new LambdaQueryWrapper<>();
|
||||
query.ge(BuriedPoint::getCreateTime, range[0])
|
||||
.lt(BuriedPoint::getCreateTime, range[1])
|
||||
.eq(BuriedPoint::getStatus, status);
|
||||
result.put(range[0], buriedPointMapper.selectCount(query).intValue());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算失败数量
|
||||
*/
|
||||
private <T> long countFailures(Object mapper, Class<T> entityClass, Date startDate, Date endDate) {
|
||||
try {
|
||||
if (entityClass == BuriedPoint.class) {
|
||||
LambdaQueryWrapper<BuriedPoint> query = new LambdaQueryWrapper<>();
|
||||
query.ge(BuriedPoint::getCreateTime, startDate)
|
||||
.le(BuriedPoint::getCreateTime, endDate)
|
||||
.eq(BuriedPoint::getStatus, BuriedMessages.STATUS_ERROR);
|
||||
return ((BuriedPointMapper)mapper).selectCount(query);
|
||||
} else {
|
||||
LambdaQueryWrapper<MqMessageRecord> query = new LambdaQueryWrapper<>();
|
||||
query.ge(MqMessageRecord::getCreateTime, startDate)
|
||||
.le(MqMessageRecord::getCreateTime, endDate)
|
||||
.eq(MqMessageRecord::getStatus, MqMessageRecord.STATUS_FAILED);
|
||||
return ((BuriedPointFailRecordMapper)mapper).selectCount(query);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送告警
|
||||
*/
|
||||
private void sendAlert(long failCount, String alertMsg, List<ChartImageGenerator.MonitoringDataPoint> data) {
|
||||
try {
|
||||
String imageKey = feiShuAlertClient.uploadImage(data, alertMsg);
|
||||
String title = alertMsg.split(":")[0].trim();
|
||||
|
||||
Map<String, Object> templateData = Map.of(
|
||||
"alert_title", title,
|
||||
"image_key", Map.of("img_key", imageKey),
|
||||
"current_time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()),
|
||||
"fail_count", failCount
|
||||
);
|
||||
|
||||
String messageId = feiShuAlertClient.sendCardMessage(
|
||||
larkConfig.getChatId(),
|
||||
larkConfig.getExceptionCards(),
|
||||
new HashMap<>(templateData)
|
||||
);
|
||||
feiShuCardDataService.saveCardData(messageId, templateData);
|
||||
} catch (Exception e) {
|
||||
log.error("[埋点监控] 发送告警失败: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取小时范围列表
|
||||
*/
|
||||
private List<Date[]> getHourRanges(Date startDate, Date endDate) {
|
||||
List<Date[]> ranges = new ArrayList<>();
|
||||
Calendar cal = Calendar.getInstance();
|
||||
|
||||
cal.setTime(endDate);
|
||||
cal.set(Calendar.MINUTE, 0);
|
||||
cal.set(Calendar.SECOND, 0);
|
||||
cal.set(Calendar.MILLISECOND, 0);
|
||||
Date endHour = cal.getTime();
|
||||
|
||||
cal.add(Calendar.HOUR_OF_DAY, -(MONITORING_HOURS - 1));
|
||||
Date startHour = startDate.after(cal.getTime()) ? startDate : cal.getTime();
|
||||
|
||||
cal.setTime(startHour);
|
||||
while (!cal.getTime().after(endHour)) {
|
||||
Date hourStart = cal.getTime();
|
||||
cal.add(Calendar.HOUR_OF_DAY, 1);
|
||||
Date hourEnd = cal.getTime().after(endDate) ? endDate : cal.getTime();
|
||||
ranges.add(new Date[]{hourStart, hourEnd});
|
||||
|
||||
if (hourEnd.equals(endDate)) break;
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定时间前N小时的时间
|
||||
*/
|
||||
private Date getDateHoursAgo(Date date, int hours) {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(date);
|
||||
calendar.add(Calendar.HOUR_OF_DAY, -hours);
|
||||
return calendar.getTime();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package com.tashow.cloud.app.service.feishu;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* 飞书卡片数据处理服务
|
||||
*/
|
||||
@Service
|
||||
public class FeiShuCardDataService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(FeiShuCardDataService.class);
|
||||
private static final String REDIS_KEY_PREFIX = "feishu:card:";
|
||||
private static final int CARD_EXPIRATION_DAYS = 30;
|
||||
|
||||
private final StringRedisTemplate stringRedisTemplate;
|
||||
private final ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
public FeiShuCardDataService(StringRedisTemplate stringRedisTemplate, ObjectMapper objectMapper) {
|
||||
this.stringRedisTemplate = stringRedisTemplate;
|
||||
this.objectMapper = objectMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存卡片数据到Redis
|
||||
*/
|
||||
public boolean saveCardData(String messageId, Map<String, Object> data) {
|
||||
if (messageId == null || data == null) return false;
|
||||
|
||||
try {
|
||||
String jsonData = objectMapper.writeValueAsString(data);
|
||||
stringRedisTemplate.opsForValue().set(
|
||||
REDIS_KEY_PREFIX + messageId,
|
||||
jsonData,
|
||||
CARD_EXPIRATION_DAYS,
|
||||
TimeUnit.DAYS
|
||||
);
|
||||
return true;
|
||||
} catch (JsonProcessingException e) {
|
||||
log.error("保存卡片数据失败: {}", e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Redis获取卡片数据
|
||||
*/
|
||||
public Map<String, Object> getCardData(String messageId) {
|
||||
try {
|
||||
String redisKey = REDIS_KEY_PREFIX + messageId;
|
||||
String jsonData = stringRedisTemplate.opsForValue().get(redisKey);
|
||||
|
||||
if (jsonData == null) return new HashMap<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> result = objectMapper.readValue(jsonData, Map.class);
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.error("获取卡片数据失败: {}", e.getMessage());
|
||||
return new HashMap<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.tashow.cloud.app.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.tashow.cloud.app.mapper.BuriedPointFailRecordMapper;
|
||||
import com.tashow.cloud.app.model.MqMessageRecord;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import com.tashow.cloud.app.mq.producer.buriedPoint.BuriedPointProducer;
|
||||
import com.tashow.cloud.common.util.json.JsonUtils;
|
||||
import com.tashow.cloud.mq.retry.MessageRetryService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 埋点失败记录服务
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class BuriedPointFailRecordService implements MessageRetryService<MqMessageRecord> {
|
||||
|
||||
private final BuriedPointFailRecordMapper buriedPointFailRecordMapper;
|
||||
private final BuriedPointProducer buriedPointProducer;
|
||||
|
||||
/**
|
||||
* 获取未处理的失败记录
|
||||
*/
|
||||
@Override
|
||||
public List<MqMessageRecord> getUnprocessedRecords() {
|
||||
LambdaQueryWrapper<MqMessageRecord> queryWrapper = new LambdaQueryWrapper<>();
|
||||
queryWrapper.eq(MqMessageRecord::getStatus, MqMessageRecord.STATUS_FAILED)
|
||||
.orderByAsc(MqMessageRecord::getCreateTime);
|
||||
return buriedPointFailRecordMapper.selectList(queryWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试失败消息
|
||||
*/
|
||||
@Override
|
||||
public void retryFailedMessage(Integer recordId) {
|
||||
try {
|
||||
Long id = Long.valueOf(recordId);
|
||||
MqMessageRecord record = buriedPointFailRecordMapper.selectById(id);
|
||||
BuriedMessages message = JsonUtils.parseObject(record.getMessageContent(), BuriedMessages.class);
|
||||
buriedPointProducer.asyncSendMessage(message);
|
||||
} catch (Exception e) {
|
||||
log.error("[埋点重试] 重试失败", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.tashow.cloud.app.task;
|
||||
import com.tashow.cloud.app.model.MqMessageRecord;
|
||||
import com.tashow.cloud.app.service.impl.BuriedPointFailRecordService;
|
||||
import com.tashow.cloud.mq.retry.AbstractMessageRetryTask;
|
||||
import com.tashow.cloud.mq.retry.MessageRetryService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 埋点消息重试定时任务
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class BuriedPointRetryTask extends AbstractMessageRetryTask<MqMessageRecord> {
|
||||
|
||||
private final BuriedPointFailRecordService buriedPointFailRecordService;
|
||||
|
||||
/**
|
||||
* 定时重试失败消息
|
||||
* 每天凌晨执行一次
|
||||
*/
|
||||
@Scheduled(cron = "0 0 0 * * ?")
|
||||
// @Scheduled(cron = "0/10 * * * * ?")
|
||||
public void execute() {
|
||||
retryFailedMessages();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MessageRetryService<MqMessageRecord> getMessageRetryService() {
|
||||
return buriedPointFailRecordService;
|
||||
}
|
||||
@Override
|
||||
protected Integer getRecordId(MqMessageRecord record) {
|
||||
return record.getId();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
--- #################### 注册中心 + 配置中心相关配置 ####################
|
||||
|
||||
spring:
|
||||
cloud:
|
||||
nacos:
|
||||
server-addr: 43.139.42.137:8848 # Nacos 服务器地址
|
||||
username: nacos # Nacos 账号
|
||||
password: nacos # Nacos 密码
|
||||
discovery: # 【配置中心】配置项
|
||||
namespace: 5c8b8fe6-9a89-4ae3-975e-ef3bf560ff82 # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
metadata:
|
||||
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
|
||||
config: # 【注册中心】配置项
|
||||
namespace: 5c8b8fe6-9a89-4ae3-975e-ef3bf560ff82 # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
@@ -0,0 +1,12 @@
|
||||
server:
|
||||
port: 48083
|
||||
spring:
|
||||
application:
|
||||
name: app-server
|
||||
profiles:
|
||||
active: local
|
||||
config:
|
||||
import:
|
||||
- optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置
|
||||
- optional:nacos:application.yaml # 加载【Nacos】的配置
|
||||
- optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置
|
||||
19
tashow-module/tashow-module-infra/Dockerfile
Normal file
19
tashow-module/tashow-module-infra/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
|
||||
## 感谢复旦核博士的建议!灰子哥,牛皮!
|
||||
FROM eclipse-temurin:17-jre
|
||||
|
||||
## 创建目录,并使用它作为工作目录
|
||||
RUN mkdir -p /home/java-work/system-infra
|
||||
WORKDIR /home/java-work/system-infra
|
||||
## 将后端项目的 Jar 文件,复制到镜像中
|
||||
COPY ./target/tashow-module-infra.jar app.jar
|
||||
|
||||
## 设置 TZ 时区
|
||||
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
|
||||
|
||||
## 暴露后端项目的 48080 端口
|
||||
EXPOSE 48082
|
||||
|
||||
## 启动后端项目
|
||||
CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
|
||||
179
tashow-module/tashow-module-infra/pom.xml
Normal file
179
tashow-module/tashow-module-infra/pom.xml
Normal file
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-module</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<artifactId>tashow-module-infra</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
infra 模块,主要提供两块能力:
|
||||
1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
|
||||
2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
|
||||
</description>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Cloud 基础 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-env</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 依赖服务 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-system-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-infra-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.otter</groupId>
|
||||
<artifactId>canal.client</artifactId>
|
||||
<version>1.1.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- 业务组件 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-data-mybatis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-generator</artifactId> <!-- 代码生成器,使用它解析表结构 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-data-canal</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>-->
|
||||
|
||||
<!-- RPC 远程调用相关 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-rpc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Registry 注册中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Config 配置中心相关 -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Job 定时任务相关 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 消息队列相关 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-mq</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-data-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.velocity</groupId>
|
||||
<artifactId>velocity-engine-core</artifactId> <!-- 实现代码生成 -->
|
||||
</dependency>
|
||||
|
||||
<!-- 监控相关 -->
|
||||
<dependency>
|
||||
<groupId>com.tashow.cloud</groupId>
|
||||
<artifactId>tashow-framework-monitor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-server</artifactId> <!-- 实现 Spring Boot Admin Server 服务端 -->
|
||||
</dependency>
|
||||
|
||||
<!-- 三方云服务相关 -->
|
||||
<dependency>
|
||||
<groupId>commons-net</groupId>
|
||||
<artifactId>commons-net</artifactId> <!-- 文件客户端:解决 ftp 连接 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jcraft</groupId>
|
||||
<artifactId>jsch</artifactId> <!-- 文件客户端:解决 sftp 连接 -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-s3</artifactId><!-- 文件客户端:解决阿里云、腾讯云、minio 等 S3 连接 -->
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.tika</groupId>
|
||||
<artifactId>tika-core</artifactId> <!-- 文件客户端:文件类型的识别 -->
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
<build>
|
||||
<!-- 设置构建的 jar 包名 -->
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<plugins>
|
||||
<!-- 打包 -->
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.tashow.cloud.infra;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 项目的启动类
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class InfraServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(InfraServerApplication.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.tashow.cloud.infra.api.config;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.infra.service.config.ConfigService;
|
||||
import com.tashow.cloud.infraapi.api.config.ConfigApi;
|
||||
import com.tashow.cloud.infra.dal.dataobject.config.ConfigDO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class ConfigApiImpl implements ConfigApi {
|
||||
|
||||
@Resource
|
||||
private ConfigService configService;
|
||||
|
||||
@Override
|
||||
public CommonResult<String> getConfigValueByKey(String key) {
|
||||
ConfigDO config = configService.getConfigByKey(key);
|
||||
return success(config != null ? config.getValue() : null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.tashow.cloud.infra.api.file;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.infra.service.file.FileService;
|
||||
import com.tashow.cloud.infraapi.api.file.FileApi;
|
||||
import com.tashow.cloud.infraapi.api.file.dto.FileCreateReqDTO;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class FileApiImpl implements FileApi {
|
||||
|
||||
@Resource
|
||||
private FileService fileService;
|
||||
|
||||
@Override
|
||||
public CommonResult<String> createFile(FileCreateReqDTO createReqDTO) {
|
||||
return success(fileService.createFile(createReqDTO.getName(), createReqDTO.getPath(),
|
||||
createReqDTO.getContent()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.tashow.cloud.infra.api.logger;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.infra.service.logger.ApiAccessLogService;
|
||||
import com.tashow.cloud.infraapi.api.logger.ApiAccessLogApi;
|
||||
import com.tashow.cloud.infraapi.api.logger.dto.ApiAccessLogCreateReqDTO;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class ApiAccessLogApiImpl implements ApiAccessLogApi {
|
||||
|
||||
@Resource
|
||||
private ApiAccessLogService apiAccessLogService;
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> createApiAccessLog(ApiAccessLogCreateReqDTO createDTO) {
|
||||
apiAccessLogService.createApiAccessLog(createDTO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.tashow.cloud.infra.api.logger;
|
||||
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.infra.service.logger.ApiErrorLogService;
|
||||
import com.tashow.cloud.infraapi.api.logger.ApiErrorLogApi;
|
||||
import com.tashow.cloud.infraapi.api.logger.dto.ApiErrorLogCreateReqDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class ApiErrorLogApiImpl implements ApiErrorLogApi {
|
||||
|
||||
@Resource
|
||||
private ApiErrorLogService apiErrorLogService;
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> createApiErrorLog(ApiErrorLogCreateReqDTO createDTO) {
|
||||
apiErrorLogService.createApiErrorLog(createDTO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
package com.tashow.cloud.infra.api;
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.tashow.cloud.infra.api.websocket;
|
||||
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.infraapi.api.websocket.WebSocketSenderApi;
|
||||
import com.tashow.cloud.infraapi.api.websocket.dto.WebSocketSendReqDTO;
|
||||
import com.tashow.cloud.websocket.core.sender.WebSocketMessageSender;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
@RestController // 提供 RESTful API 接口,给 Feign 调用
|
||||
@Validated
|
||||
public class WebSocketSenderApiImpl implements WebSocketSenderApi {
|
||||
//
|
||||
@Resource
|
||||
private WebSocketMessageSender webSocketMessageSender;
|
||||
|
||||
@Override
|
||||
public CommonResult<Boolean> send(WebSocketSendReqDTO message) {
|
||||
// if (StrUtil.isNotEmpty(message.getSessionId())) {
|
||||
// webSocketMessageSender.send(message.getSessionId(),
|
||||
// message.getMessageType(), message.getMessageContent());
|
||||
// } else if (message.getUserType() != null && message.getUserId() != null) {
|
||||
// webSocketMessageSender.send(message.getUserType(), message.getUserId(),
|
||||
// message.getMessageType(), message.getMessageContent());
|
||||
// } else if (message.getUserType() != null) {
|
||||
// webSocketMessageSender.send(message.getUserType(),
|
||||
// message.getMessageType(), message.getMessageContent());
|
||||
// }
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
import static com.tashow.cloud.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
|
||||
import static com.tashow.cloud.security.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.ZipUtil;
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.common.util.object.BeanUtils;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenCreateListReqVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenUpdateReqVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTablePageReqVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.table.DatabaseTableRespVO;
|
||||
import com.tashow.cloud.infra.convert.codegen.CodegenConvert;
|
||||
import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import com.tashow.cloud.infra.service.codegen.CodegenService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/** 管理后台 - 代码生成器 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/codegen")
|
||||
@Validated
|
||||
public class CodegenController {
|
||||
|
||||
@Resource private CodegenService codegenService;
|
||||
|
||||
/** 获得数据库自带的表定义列表", description = "会过滤掉已经导入 Codegen 的表 */
|
||||
@GetMapping("/db/table/list")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
|
||||
public CommonResult<List<DatabaseTableRespVO>> getDatabaseTableList(
|
||||
@RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId,
|
||||
@RequestParam(value = "name", required = false) String name,
|
||||
@RequestParam(value = "comment", required = false) String comment) {
|
||||
return success(codegenService.getDatabaseTableList(dataSourceConfigId, name, comment));
|
||||
}
|
||||
|
||||
/** 获得表定义列表 */
|
||||
@GetMapping("/table/list")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
|
||||
public CommonResult<List<CodegenTableRespVO>> getCodegenTableList(
|
||||
@RequestParam(value = "dataSourceConfigId") Long dataSourceConfigId) {
|
||||
List<CodegenTableDO> list = codegenService.getCodegenTableList(dataSourceConfigId);
|
||||
return success(BeanUtils.toBean(list, CodegenTableRespVO.class));
|
||||
}
|
||||
|
||||
/** 获得表定义分页 */
|
||||
@GetMapping("/table/page")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
|
||||
public CommonResult<PageResult<CodegenTableRespVO>> getCodegenTablePage(
|
||||
@Valid CodegenTablePageReqVO pageReqVO) {
|
||||
PageResult<CodegenTableDO> pageResult = codegenService.getCodegenTablePage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, CodegenTableRespVO.class));
|
||||
}
|
||||
|
||||
/** 获得表和字段的明细 */
|
||||
@GetMapping("/detail")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:query')")
|
||||
public CommonResult<CodegenDetailRespVO> getCodegenDetail(@RequestParam("tableId") Long tableId) {
|
||||
CodegenTableDO table = codegenService.getCodegenTable(tableId);
|
||||
List<CodegenColumnDO> columns = codegenService.getCodegenColumnListByTableId(tableId);
|
||||
// 拼装返回
|
||||
return success(CodegenConvert.INSTANCE.convert(table, columns));
|
||||
}
|
||||
|
||||
/** 基于数据库的表结构,创建代码生成器的表和字段定义 */
|
||||
@PostMapping("/create-list")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:create')")
|
||||
public CommonResult<List<Long>> createCodegenList(
|
||||
@Valid @RequestBody CodegenCreateListReqVO reqVO) {
|
||||
return success(codegenService.createCodegenList(getLoginUserId(), reqVO));
|
||||
}
|
||||
|
||||
/** 更新数据库的表和字段定义 */
|
||||
@PutMapping("/update")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
|
||||
public CommonResult<Boolean> updateCodegen(@Valid @RequestBody CodegenUpdateReqVO updateReqVO) {
|
||||
codegenService.updateCodegen(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 基于数据库的表结构,同步数据库的表和字段定义 */
|
||||
@PutMapping("/sync-from-db")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:update')")
|
||||
public CommonResult<Boolean> syncCodegenFromDB(@RequestParam("tableId") Long tableId) {
|
||||
codegenService.syncCodegenFromDB(tableId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 删除数据库的表和字段定义 */
|
||||
@DeleteMapping("/delete")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:delete')")
|
||||
public CommonResult<Boolean> deleteCodegen(@RequestParam("tableId") Long tableId) {
|
||||
codegenService.deleteCodegen(tableId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 预览生成代码 */
|
||||
@GetMapping("/preview")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:preview')")
|
||||
public CommonResult<List<CodegenPreviewRespVO>> previewCodegen(
|
||||
@RequestParam("tableId") Long tableId) {
|
||||
Map<String, String> codes = codegenService.generationCodes(tableId);
|
||||
return success(CodegenConvert.INSTANCE.convert(codes));
|
||||
}
|
||||
|
||||
/** 下载生成代码 */
|
||||
@GetMapping("/download")
|
||||
@PreAuthorize("@ss.hasPermission('infra:codegen:download')")
|
||||
public void downloadCodegen(@RequestParam("tableId") Long tableId, HttpServletResponse response)
|
||||
throws IOException {
|
||||
// 生成代码
|
||||
Map<String, String> codes = codegenService.generationCodes(tableId);
|
||||
// 构建 zip 包
|
||||
String[] paths = codes.keySet().toArray(new String[0]);
|
||||
ByteArrayInputStream[] ins =
|
||||
codes.values().stream().map(IoUtil::toUtf8Stream).toArray(ByteArrayInputStream[]::new);
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
ZipUtil.zip(outputStream, paths, ins);
|
||||
// 输出
|
||||
writeAttachment(response, "codegen.zip", outputStream.toByteArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 基于数据库的表结构,创建代码生成器的表和字段定义 Request VO */
|
||||
@Data
|
||||
public class CodegenCreateListReqVO {
|
||||
|
||||
/** 数据源配置的编号" */
|
||||
@NotNull(message = "数据源配置的编号不能为空")
|
||||
private Long dataSourceConfigId;
|
||||
|
||||
/** 表名数组", example = "[1, 2, 3] */
|
||||
@NotNull(message = "表名数组不能为空")
|
||||
private List<String> tableNames;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo;
|
||||
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 代码生成表和字段的明细 Response VO */
|
||||
@Data
|
||||
public class CodegenDetailRespVO {
|
||||
|
||||
/** 表定义 */
|
||||
private CodegenTableRespVO table;
|
||||
|
||||
/** 字段定义 */
|
||||
private List<CodegenColumnRespVO> columns;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 代码生成预览 Response VO,注意,每个文件都是一个该对象 */
|
||||
@Data
|
||||
public class CodegenPreviewRespVO {
|
||||
|
||||
/** 文件路径" */
|
||||
private String filePath;
|
||||
|
||||
/** 代码" */
|
||||
private String code;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo;
|
||||
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.column.CodegenColumnSaveReqVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTableSaveReqVO;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 代码生成表和字段的修改 Request VO */
|
||||
@Data
|
||||
public class CodegenUpdateReqVO {
|
||||
|
||||
@Valid // 校验内嵌的字段
|
||||
@NotNull(message = "表定义不能为空")
|
||||
private CodegenTableSaveReqVO table;
|
||||
|
||||
@Valid // 校验内嵌的字段
|
||||
@NotNull(message = "字段定义不能为空")
|
||||
private List<CodegenColumnSaveReqVO> columns;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo.column;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 代码生成字段定义 Response VO */
|
||||
@Data
|
||||
public class CodegenColumnRespVO {
|
||||
|
||||
/** 编号" */
|
||||
private Long id;
|
||||
|
||||
/** 表编号" */
|
||||
private Long tableId;
|
||||
|
||||
/** 字段名" */
|
||||
private String columnName;
|
||||
|
||||
/** 字段类型" */
|
||||
private String dataType;
|
||||
|
||||
/** 字段描述", example = "年龄 */
|
||||
private String columnComment;
|
||||
|
||||
/** 是否允许为空" */
|
||||
private Boolean nullable;
|
||||
|
||||
/** 是否主键" */
|
||||
private Boolean primaryKey;
|
||||
|
||||
/** 排序" */
|
||||
private Integer ordinalPosition;
|
||||
|
||||
/** Java 属性类型" */
|
||||
private String javaType;
|
||||
|
||||
/** Java 属性名" */
|
||||
private String javaField;
|
||||
|
||||
/** 字典类型 */
|
||||
private String dictType;
|
||||
|
||||
/** 数据示例 */
|
||||
private String example;
|
||||
|
||||
/** 是否为 Create 创建操作的字段" */
|
||||
private Boolean createOperation;
|
||||
|
||||
/** 是否为 Update 更新操作的字段" */
|
||||
private Boolean updateOperation;
|
||||
|
||||
/** 是否为 List 查询操作的字段" */
|
||||
private Boolean listOperation;
|
||||
|
||||
/** List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举" */
|
||||
private String listOperationCondition;
|
||||
|
||||
/** 是否为 List 查询操作的返回字段" */
|
||||
private Boolean listOperationResult;
|
||||
|
||||
/** 显示类型" */
|
||||
private String htmlType;
|
||||
|
||||
/** 创建时间 */
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo.column;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 代码生成字段定义创建/修改 Request VO */
|
||||
@Data
|
||||
public class CodegenColumnSaveReqVO {
|
||||
|
||||
/** 编号" */
|
||||
private Long id;
|
||||
|
||||
/** 表编号" */
|
||||
@NotNull(message = "表编号不能为空")
|
||||
private Long tableId;
|
||||
|
||||
/** 字段名" */
|
||||
@NotNull(message = "字段名不能为空")
|
||||
private String columnName;
|
||||
|
||||
/** 字段类型" */
|
||||
@NotNull(message = "字段类型不能为空")
|
||||
private String dataType;
|
||||
|
||||
/** 字段描述", example = "年龄 */
|
||||
@NotNull(message = "字段描述不能为空")
|
||||
private String columnComment;
|
||||
|
||||
/** 是否允许为空" */
|
||||
@NotNull(message = "是否允许为空不能为空")
|
||||
private Boolean nullable;
|
||||
|
||||
/** 是否主键" */
|
||||
@NotNull(message = "是否主键不能为空")
|
||||
private Boolean primaryKey;
|
||||
|
||||
/** 排序" */
|
||||
@NotNull(message = "排序不能为空")
|
||||
private Integer ordinalPosition;
|
||||
|
||||
/** Java 属性类型" */
|
||||
@NotNull(message = "Java 属性类型不能为空")
|
||||
private String javaType;
|
||||
|
||||
/** Java 属性名" */
|
||||
@NotNull(message = "Java 属性名不能为空")
|
||||
private String javaField;
|
||||
|
||||
/** 字典类型 */
|
||||
private String dictType;
|
||||
|
||||
/** 数据示例 */
|
||||
private String example;
|
||||
|
||||
/** 是否为 Create 创建操作的字段" */
|
||||
@NotNull(message = "是否为 Create 创建操作的字段不能为空")
|
||||
private Boolean createOperation;
|
||||
|
||||
/** 是否为 Update 更新操作的字段" */
|
||||
@NotNull(message = "是否为 Update 更新操作的字段不能为空")
|
||||
private Boolean updateOperation;
|
||||
|
||||
/** 是否为 List 查询操作的字段" */
|
||||
@NotNull(message = "是否为 List 查询操作的字段不能为空")
|
||||
private Boolean listOperation;
|
||||
|
||||
/** List 查询操作的条件类型,参见 CodegenColumnListConditionEnum 枚举" */
|
||||
@NotNull(message = "List 查询操作的条件类型不能为空")
|
||||
private String listOperationCondition;
|
||||
|
||||
/** 是否为 List 查询操作的返回字段" */
|
||||
@NotNull(message = "是否为 List 查询操作的返回字段不能为空")
|
||||
private Boolean listOperationResult;
|
||||
|
||||
/** 显示类型" */
|
||||
@NotNull(message = "显示类型不能为空")
|
||||
private String htmlType;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
|
||||
|
||||
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/** 管理后台 - 表定义分页 Request VO */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class CodegenTablePageReqVO extends PageParam {
|
||||
|
||||
/** 表名称,模糊匹配 */
|
||||
private String tableName;
|
||||
|
||||
/** 表描述,模糊匹配 */
|
||||
private String tableComment;
|
||||
|
||||
/** 实体,模糊匹配 */
|
||||
private String className;
|
||||
|
||||
/** 创建时间 */
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 代码生成表定义 Response VO */
|
||||
@Data
|
||||
public class CodegenTableRespVO {
|
||||
|
||||
/** 编号" */
|
||||
private Long id;
|
||||
|
||||
/** 生成场景,参见 CodegenSceneEnum 枚举" */
|
||||
private Integer scene;
|
||||
|
||||
/** 表名称" */
|
||||
private String tableName;
|
||||
|
||||
/** 表描述", example = "芋道 */
|
||||
private String tableComment;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
/** 模块名" */
|
||||
private String moduleName;
|
||||
|
||||
/** 业务名" */
|
||||
private String businessName;
|
||||
|
||||
/** 类名称" */
|
||||
private String className;
|
||||
|
||||
/** 类描述", example = "代码生成器的表定义 */
|
||||
private String classComment;
|
||||
|
||||
/** 作者", example = "芋道源码 */
|
||||
private String author;
|
||||
|
||||
/** 模板类型,参见 CodegenTemplateTypeEnum 枚举" */
|
||||
private Integer templateType;
|
||||
|
||||
/** 前端类型,参见 CodegenFrontTypeEnum 枚举" */
|
||||
private Integer frontType;
|
||||
|
||||
/** 父菜单编号 */
|
||||
private Long parentMenuId;
|
||||
|
||||
/** 主表的编号 */
|
||||
private Long masterTableId;
|
||||
|
||||
/** 子表关联主表的字段编号 */
|
||||
private Long subJoinColumnId;
|
||||
|
||||
/** 主表与子表是否一对多 */
|
||||
private Boolean subJoinMany;
|
||||
|
||||
/** 树表的父字段编号 */
|
||||
private Long treeParentColumnId;
|
||||
|
||||
/** 树表的名字字段编号 */
|
||||
private Long treeNameColumnId;
|
||||
|
||||
/** 主键编号" */
|
||||
private Integer dataSourceConfigId;
|
||||
|
||||
/** 创建时间 */
|
||||
private LocalDateTime createTime;
|
||||
|
||||
/** 更新时间 */
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import jakarta.validation.constraints.AssertTrue;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 代码生成表定义创建/修改 Response VO */
|
||||
@Data
|
||||
public class CodegenTableSaveReqVO {
|
||||
|
||||
/** 编号" */
|
||||
private Long id;
|
||||
|
||||
/** 生成场景,参见 CodegenSceneEnum 枚举" */
|
||||
@NotNull(message = "导入类型不能为空")
|
||||
private Integer scene;
|
||||
|
||||
/** 表名称" */
|
||||
@NotNull(message = "表名称不能为空")
|
||||
private String tableName;
|
||||
|
||||
/** 表描述", example = "芋道 */
|
||||
@NotNull(message = "表描述不能为空")
|
||||
private String tableComment;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
/** 模块名" */
|
||||
@NotNull(message = "模块名不能为空")
|
||||
private String moduleName;
|
||||
|
||||
/** 业务名" */
|
||||
@NotNull(message = "业务名不能为空")
|
||||
private String businessName;
|
||||
|
||||
/** 类名称" */
|
||||
@NotNull(message = "类名称不能为空")
|
||||
private String className;
|
||||
|
||||
/** 类描述", example = "代码生成器的表定义 */
|
||||
@NotNull(message = "类描述不能为空")
|
||||
private String classComment;
|
||||
|
||||
/** 作者", example = "芋道源码 */
|
||||
@NotNull(message = "作者不能为空")
|
||||
private String author;
|
||||
|
||||
/** 模板类型,参见 CodegenTemplateTypeEnum 枚举" */
|
||||
@NotNull(message = "模板类型不能为空")
|
||||
private Integer templateType;
|
||||
|
||||
/** 前端类型,参见 CodegenFrontTypeEnum 枚举" */
|
||||
@NotNull(message = "前端类型不能为空")
|
||||
private Integer frontType;
|
||||
|
||||
/** 父菜单编号 */
|
||||
private Long parentMenuId;
|
||||
|
||||
/** 主表的编号 */
|
||||
private Long masterTableId;
|
||||
|
||||
/** 子表关联主表的字段编号 */
|
||||
private Long subJoinColumnId;
|
||||
|
||||
/** 主表与子表是否一对多 */
|
||||
private Boolean subJoinMany;
|
||||
|
||||
/** 树表的父字段编号 */
|
||||
private Long treeParentColumnId;
|
||||
|
||||
/** 树表的名字字段编号 */
|
||||
private Long treeNameColumnId;
|
||||
|
||||
@AssertTrue(message = "上级菜单不能为空,请前往 [修改生成配置 -> 生成信息] 界面,设置“上级菜单”字段")
|
||||
@JsonIgnore
|
||||
public boolean isParentMenuIdValid() {
|
||||
// 生成场景为管理后台时,必须设置上级菜单,不然生成的菜单 SQL 是无父级菜单的
|
||||
return ObjectUtil.notEqual(getScene(), CodegenSceneEnum.ADMIN.getScene())
|
||||
|| getParentMenuId() != null;
|
||||
}
|
||||
|
||||
@AssertTrue(message = "关联的父表信息不全")
|
||||
@JsonIgnore
|
||||
public boolean isSubValid() {
|
||||
return ObjectUtil.notEqual(getTemplateType(), CodegenTemplateTypeEnum.SUB)
|
||||
|| (ObjectUtil.isAllNotEmpty(masterTableId, subJoinColumnId, subJoinMany));
|
||||
}
|
||||
|
||||
@AssertTrue(message = "关联的树表信息不全")
|
||||
@JsonIgnore
|
||||
public boolean isTreeValid() {
|
||||
return ObjectUtil.notEqual(templateType, CodegenTemplateTypeEnum.TREE)
|
||||
|| (ObjectUtil.isAllNotEmpty(treeParentColumnId, treeNameColumnId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.tashow.cloud.infra.controller.admin.codegen.vo.table;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 数据库的表定义 Response VO */
|
||||
@Data
|
||||
public class DatabaseTableRespVO {
|
||||
|
||||
/** 表名称" */
|
||||
private String name;
|
||||
|
||||
/** 表描述", example = "芋道源码 */
|
||||
private String comment;
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.tashow.cloud.infra.controller.admin.config;
|
||||
|
||||
import static com.tashow.cloud.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
import static com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.excel.excel.core.util.ExcelUtils;
|
||||
import com.tashow.cloud.infra.controller.admin.config.vo.ConfigPageReqVO;
|
||||
import com.tashow.cloud.infra.controller.admin.config.vo.ConfigRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.config.vo.ConfigSaveReqVO;
|
||||
import com.tashow.cloud.infra.convert.config.ConfigConvert;
|
||||
import com.tashow.cloud.infra.dal.dataobject.config.ConfigDO;
|
||||
import com.tashow.cloud.infra.service.config.ConfigService;
|
||||
import com.tashow.cloud.infraapi.enums.ErrorCodeConstants;
|
||||
import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/** 管理后台 - 参数配置 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/config")
|
||||
@Validated
|
||||
public class ConfigController {
|
||||
|
||||
@Resource private ConfigService configService;
|
||||
|
||||
/** 创建参数配置 */
|
||||
@PostMapping("/create")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:create')")
|
||||
public CommonResult<Long> createConfig(@Valid @RequestBody ConfigSaveReqVO createReqVO) {
|
||||
return success(configService.createConfig(createReqVO));
|
||||
}
|
||||
|
||||
/** 修改参数配置 */
|
||||
@PutMapping("/update")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:update')")
|
||||
public CommonResult<Boolean> updateConfig(@Valid @RequestBody ConfigSaveReqVO updateReqVO) {
|
||||
configService.updateConfig(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 删除参数配置 */
|
||||
@DeleteMapping("/delete")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:delete')")
|
||||
public CommonResult<Boolean> deleteConfig(@RequestParam("id") Long id) {
|
||||
configService.deleteConfig(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 获得参数配置 */
|
||||
@GetMapping(value = "/get")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:query')")
|
||||
public CommonResult<ConfigRespVO> getConfig(@RequestParam("id") Long id) {
|
||||
return success(ConfigConvert.INSTANCE.convert(configService.getConfig(id)));
|
||||
}
|
||||
|
||||
/** 根据参数键名查询参数值", description = "不可见的配置,不允许返回给前端 */
|
||||
@GetMapping(value = "/get-value-by-key")
|
||||
public CommonResult<String> getConfigKey(@RequestParam("key") String key) {
|
||||
ConfigDO config = configService.getConfigByKey(key);
|
||||
if (config == null) {
|
||||
return success(null);
|
||||
}
|
||||
if (!config.getVisible()) {
|
||||
throw exception(ErrorCodeConstants.CONFIG_GET_VALUE_ERROR_IF_VISIBLE);
|
||||
}
|
||||
return success(config.getValue());
|
||||
}
|
||||
|
||||
/** 获取参数配置分页 */
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:query')")
|
||||
public CommonResult<PageResult<ConfigRespVO>> getConfigPage(@Valid ConfigPageReqVO pageReqVO) {
|
||||
PageResult<ConfigDO> page = configService.getConfigPage(pageReqVO);
|
||||
return success(ConfigConvert.INSTANCE.convertPage(page));
|
||||
}
|
||||
|
||||
/** 导出参数配置 */
|
||||
@GetMapping("/export")
|
||||
@PreAuthorize("@ss.hasPermission('infra:config:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportConfig(ConfigPageReqVO exportReqVO, HttpServletResponse response)
|
||||
throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<ConfigDO> list = configService.getConfigPage(exportReqVO).getList();
|
||||
// 输出
|
||||
ExcelUtils.write(
|
||||
response, "参数配置.xls", "数据", ConfigRespVO.class, ConfigConvert.INSTANCE.convertList(list));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.tashow.cloud.infra.controller.admin.config.vo;
|
||||
|
||||
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/** 管理后台 - 参数配置分页 Request VO */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ConfigPageReqVO extends PageParam {
|
||||
|
||||
/** 数据源名称,模糊匹配 */
|
||||
private String name;
|
||||
|
||||
/** 参数键名,模糊匹配 */
|
||||
private String key;
|
||||
|
||||
/** 参数类型,参见 SysConfigTypeEnum 枚举 */
|
||||
private Integer type;
|
||||
|
||||
/** 创建时间 */
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.tashow.cloud.infra.controller.admin.config.vo;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.tashow.cloud.excel.excel.core.annotations.DictFormat;
|
||||
import com.tashow.cloud.excel.excel.core.convert.DictConvert;
|
||||
import com.tashow.cloud.infraapi.enums.DictTypeConstants;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 参数配置信息 Response VO */
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class ConfigRespVO {
|
||||
|
||||
/** 参数配置序号" */
|
||||
@ExcelProperty("参数配置序号")
|
||||
private Long id;
|
||||
|
||||
/** 参数分类" */
|
||||
@ExcelProperty("参数分类")
|
||||
private String category;
|
||||
|
||||
/** 参数名称", example = "数据库名 */
|
||||
@ExcelProperty("参数名称")
|
||||
private String name;
|
||||
|
||||
/** 参数键名" */
|
||||
@ExcelProperty("参数键名")
|
||||
private String key;
|
||||
|
||||
/** 参数键值" */
|
||||
@ExcelProperty("参数键值")
|
||||
private String value;
|
||||
|
||||
/** 参数类型,参见 SysConfigTypeEnum 枚举" */
|
||||
@ExcelProperty(value = "参数类型", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.CONFIG_TYPE)
|
||||
private Integer type;
|
||||
|
||||
/** 是否可见" */
|
||||
@ExcelProperty(value = "是否可见", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.BOOLEAN_STRING)
|
||||
private Boolean visible;
|
||||
|
||||
/** 备注 */
|
||||
@ExcelProperty("备注")
|
||||
private String remark;
|
||||
|
||||
/** 创建时间", example = "时间戳格式 */
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.tashow.cloud.infra.controller.admin.config.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 参数配置创建/修改 Request VO */
|
||||
@Data
|
||||
public class ConfigSaveReqVO {
|
||||
|
||||
/** 参数配置序号 */
|
||||
private Long id;
|
||||
|
||||
/** 参数分组" */
|
||||
@NotEmpty(message = "参数分组不能为空")
|
||||
@Size(max = 50, message = "参数名称不能超过 50 个字符")
|
||||
private String category;
|
||||
|
||||
/** 参数名称", example = "数据库名 */
|
||||
@NotBlank(message = "参数名称不能为空")
|
||||
@Size(max = 100, message = "参数名称不能超过 100 个字符")
|
||||
private String name;
|
||||
|
||||
/** 参数键名" */
|
||||
@NotBlank(message = "参数键名长度不能为空")
|
||||
@Size(max = 100, message = "参数键名长度不能超过 100 个字符")
|
||||
private String key;
|
||||
|
||||
/** 参数键值" */
|
||||
@NotBlank(message = "参数键值不能为空")
|
||||
@Size(max = 500, message = "参数键值长度不能超过 500 个字符")
|
||||
private String value;
|
||||
|
||||
/** 是否可见" */
|
||||
@NotNull(message = "是否可见不能为空")
|
||||
private Boolean visible;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.tashow.cloud.infra.controller.admin.db;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.common.util.object.BeanUtils;
|
||||
import com.tashow.cloud.infra.controller.admin.db.vo.DataSourceConfigRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.db.vo.DataSourceConfigSaveReqVO;
|
||||
import com.tashow.cloud.infra.dal.dataobject.db.DataSourceConfigDO;
|
||||
import com.tashow.cloud.infra.service.db.DataSourceConfigService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/** 管理后台 - 数据源配置 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/data-source-config")
|
||||
@Validated
|
||||
public class DataSourceConfigController {
|
||||
|
||||
@Resource private DataSourceConfigService dataSourceConfigService;
|
||||
|
||||
/** 创建数据源配置 */
|
||||
@PostMapping("/create")
|
||||
@PreAuthorize("@ss.hasPermission('infra:data-source-config:create')")
|
||||
public CommonResult<Long> createDataSourceConfig(
|
||||
@Valid @RequestBody DataSourceConfigSaveReqVO createReqVO) {
|
||||
return success(dataSourceConfigService.createDataSourceConfig(createReqVO));
|
||||
}
|
||||
|
||||
/** 更新数据源配置 */
|
||||
@PutMapping("/update")
|
||||
@PreAuthorize("@ss.hasPermission('infra:data-source-config:update')")
|
||||
public CommonResult<Boolean> updateDataSourceConfig(
|
||||
@Valid @RequestBody DataSourceConfigSaveReqVO updateReqVO) {
|
||||
dataSourceConfigService.updateDataSourceConfig(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 删除数据源配置 */
|
||||
@DeleteMapping("/delete")
|
||||
@PreAuthorize("@ss.hasPermission('infra:data-source-config:delete')")
|
||||
public CommonResult<Boolean> deleteDataSourceConfig(@RequestParam("id") Long id) {
|
||||
dataSourceConfigService.deleteDataSourceConfig(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 获得数据源配置 */
|
||||
@GetMapping("/get")
|
||||
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
|
||||
public CommonResult<DataSourceConfigRespVO> getDataSourceConfig(@RequestParam("id") Long id) {
|
||||
DataSourceConfigDO config = dataSourceConfigService.getDataSourceConfig(id);
|
||||
return success(BeanUtils.toBean(config, DataSourceConfigRespVO.class));
|
||||
}
|
||||
|
||||
/** 获得数据源配置列表 */
|
||||
@GetMapping("/list")
|
||||
@PreAuthorize("@ss.hasPermission('infra:data-source-config:query')")
|
||||
public CommonResult<List<DataSourceConfigRespVO>> getDataSourceConfigList() {
|
||||
List<DataSourceConfigDO> list = dataSourceConfigService.getDataSourceConfigList();
|
||||
return success(BeanUtils.toBean(list, DataSourceConfigRespVO.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.tashow.cloud.infra.controller.admin.db.vo;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 数据源配置 Response VO */
|
||||
@Data
|
||||
public class DataSourceConfigRespVO {
|
||||
|
||||
/** 主键编号" */
|
||||
private Integer id;
|
||||
|
||||
/** 数据源名称" */
|
||||
private String name;
|
||||
|
||||
/** 数据源连接" */
|
||||
private String url;
|
||||
|
||||
/** 用户名" */
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.tashow.cloud.infra.controller.admin.db.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 数据源配置创建/修改 Request VO */
|
||||
@Data
|
||||
public class DataSourceConfigSaveReqVO {
|
||||
|
||||
/** 主键编号 */
|
||||
private Long id;
|
||||
|
||||
/** 数据源名称" */
|
||||
@NotNull(message = "数据源名称不能为空")
|
||||
private String name;
|
||||
|
||||
/** 数据源连接" */
|
||||
@NotNull(message = "数据源连接不能为空")
|
||||
private String url;
|
||||
|
||||
/** 用户名" */
|
||||
@NotNull(message = "用户名不能为空")
|
||||
private String username;
|
||||
|
||||
/** 密码" */
|
||||
@NotNull(message = "密码不能为空")
|
||||
private String password;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
### 请求 /infra/file-config/create 接口 => 成功
|
||||
POST {{baseUrl}}/infra/file-config/create
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenantId}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
{
|
||||
"name": "S3 - 七牛云",
|
||||
"remark": "",
|
||||
"storage": 20,
|
||||
"config": {
|
||||
"accessKey": "b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8",
|
||||
"accessSecret": "kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP",
|
||||
"bucket": "ruoyi-vue-pro",
|
||||
"endpoint": "s3-cn-south-1.qiniucs.com",
|
||||
"domain": "http://test.yudao.iocoder.cn",
|
||||
"region": "oss-cn-beijing"
|
||||
}
|
||||
}
|
||||
|
||||
### 请求 /infra/file-config/update 接口 => 成功
|
||||
PUT {{baseUrl}}/infra/file-config/update
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenantId}}
|
||||
Authorization: Bearer {{token}}
|
||||
|
||||
{
|
||||
"id": 2,
|
||||
"name": "S3 - 七牛云",
|
||||
"remark": "",
|
||||
"config": {
|
||||
"accessKey": "b7yvuhBSAGjmtPhMFcn9iMOxUOY_I06cA_p0ZUx8",
|
||||
"accessSecret": "kXM1l5ia1RvSX3QaOEcwI3RLz3Y2rmNszWonKZtP",
|
||||
"bucket": "ruoyi-vue-pro",
|
||||
"endpoint": "s3-cn-south-1.qiniucs.com",
|
||||
"domain": "http://test.yudao.iocoder.cn",
|
||||
"region": "oss-cn-beijing"
|
||||
}
|
||||
}
|
||||
|
||||
### 请求 /infra/file-config/test 接口 => 成功
|
||||
GET {{baseUrl}}/infra/file-config/test?id=2
|
||||
Content-Type: application/json
|
||||
tenant-id: {{adminTenantId}}
|
||||
Authorization: Bearer {{token}}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.common.util.object.BeanUtils;
|
||||
import com.tashow.cloud.infra.controller.admin.file.vo.config.FileConfigPageReqVO;
|
||||
import com.tashow.cloud.infra.controller.admin.file.vo.config.FileConfigRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;
|
||||
import com.tashow.cloud.infra.dal.dataobject.file.FileConfigDO;
|
||||
import com.tashow.cloud.infra.service.file.FileConfigService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/** 管理后台 - 文件配置 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/file-config")
|
||||
@Validated
|
||||
public class FileConfigController {
|
||||
|
||||
@Resource private FileConfigService fileConfigService;
|
||||
|
||||
/** 创建文件配置 */
|
||||
@PostMapping("/create")
|
||||
@PreAuthorize("@ss.hasPermission('infra:file-config:create')")
|
||||
public CommonResult<Long> createFileConfig(@Valid @RequestBody FileConfigSaveReqVO createReqVO) {
|
||||
return success(fileConfigService.createFileConfig(createReqVO));
|
||||
}
|
||||
|
||||
/** 更新文件配置 */
|
||||
@PutMapping("/update")
|
||||
@PreAuthorize("@ss.hasPermission('infra:file-config:update')")
|
||||
public CommonResult<Boolean> updateFileConfig(
|
||||
@Valid @RequestBody FileConfigSaveReqVO updateReqVO) {
|
||||
fileConfigService.updateFileConfig(updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 更新文件配置为 Master */
|
||||
@PutMapping("/update-master")
|
||||
@PreAuthorize("@ss.hasPermission('infra:file-config:update')")
|
||||
public CommonResult<Boolean> updateFileConfigMaster(@RequestParam("id") Long id) {
|
||||
fileConfigService.updateFileConfigMaster(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 删除文件配置 */
|
||||
@DeleteMapping("/delete")
|
||||
@PreAuthorize("@ss.hasPermission('infra:file-config:delete')")
|
||||
public CommonResult<Boolean> deleteFileConfig(@RequestParam("id") Long id) {
|
||||
fileConfigService.deleteFileConfig(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 获得文件配置 */
|
||||
@GetMapping("/get")
|
||||
@PreAuthorize("@ss.hasPermission('infra:file-config:query')")
|
||||
public CommonResult<FileConfigRespVO> getFileConfig(@RequestParam("id") Long id) {
|
||||
FileConfigDO config = fileConfigService.getFileConfig(id);
|
||||
return success(BeanUtils.toBean(config, FileConfigRespVO.class));
|
||||
}
|
||||
|
||||
/** 获得文件配置分页 */
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@ss.hasPermission('infra:file-config:query')")
|
||||
public CommonResult<PageResult<FileConfigRespVO>> getFileConfigPage(
|
||||
@Valid FileConfigPageReqVO pageVO) {
|
||||
PageResult<FileConfigDO> pageResult = fileConfigService.getFileConfigPage(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, FileConfigRespVO.class));
|
||||
}
|
||||
|
||||
/** 测试文件配置是否正确 */
|
||||
@GetMapping("/test")
|
||||
@PreAuthorize("@ss.hasPermission('infra:file-config:query')")
|
||||
public CommonResult<String> testFileConfig(@RequestParam("id") Long id) throws Exception {
|
||||
String url = fileConfigService.testFileConfig(id);
|
||||
return success(url);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
import static com.tashow.cloud.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.common.util.object.BeanUtils;
|
||||
import com.tashow.cloud.infra.controller.admin.file.vo.file.*;
|
||||
import com.tashow.cloud.infra.dal.dataobject.file.FileDO;
|
||||
import com.tashow.cloud.infra.service.file.FileService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/** 管理后台 - 文件存储 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/file")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class FileController {
|
||||
|
||||
@Resource private FileService fileService;
|
||||
|
||||
/** 上传文件", description = "模式一:后端上传文件 */
|
||||
@PostMapping("/upload")
|
||||
public CommonResult<String> uploadFile(FileUploadReqVO uploadReqVO) throws Exception {
|
||||
MultipartFile file = uploadReqVO.getFile();
|
||||
String path = uploadReqVO.getPath();
|
||||
return success(
|
||||
fileService.createFile(
|
||||
file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
|
||||
}
|
||||
|
||||
/** 获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器 */
|
||||
@GetMapping("/presigned-url")
|
||||
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(@RequestParam("path") String path)
|
||||
throws Exception {
|
||||
return success(fileService.getFilePresignedUrl(path));
|
||||
}
|
||||
|
||||
/** 创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件 */
|
||||
@PostMapping("/create")
|
||||
public CommonResult<Long> createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {
|
||||
return success(fileService.createFile(createReqVO));
|
||||
}
|
||||
|
||||
/** 删除文件 */
|
||||
@DeleteMapping("/delete")
|
||||
@PreAuthorize("@ss.hasPermission('infra:file:delete')")
|
||||
public CommonResult<Boolean> deleteFile(@RequestParam("id") Long id) throws Exception {
|
||||
fileService.deleteFile(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 下载文件 */
|
||||
@GetMapping("/{configId}/get/**")
|
||||
@PermitAll
|
||||
public void getFileContent(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@PathVariable("configId") Long configId)
|
||||
throws Exception {
|
||||
// 获取请求的路径
|
||||
String path = StrUtil.subAfter(request.getRequestURI(), "/get/", false);
|
||||
if (StrUtil.isEmpty(path)) {
|
||||
throw new IllegalArgumentException("结尾的 path 路径必须传递");
|
||||
}
|
||||
// 解码,解决中文路径的问题 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/807/
|
||||
path = URLUtil.decode(path);
|
||||
|
||||
// 读取内容
|
||||
byte[] content = fileService.getFileContent(configId, path);
|
||||
if (content == null) {
|
||||
log.warn("[getFileContent][configId({}) path({}) 文件不存在]", configId, path);
|
||||
response.setStatus(HttpStatus.NOT_FOUND.value());
|
||||
return;
|
||||
}
|
||||
writeAttachment(response, path, content);
|
||||
}
|
||||
|
||||
/** 获得文件分页 */
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@ss.hasPermission('infra:file:query')")
|
||||
public CommonResult<PageResult<FileRespVO>> getFilePage(@Valid FilePageReqVO pageVO) {
|
||||
PageResult<FileDO> pageResult = fileService.getFilePage(pageVO);
|
||||
return success(BeanUtils.toBean(pageResult, FileRespVO.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file.vo.config;
|
||||
|
||||
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/** 管理后台 - 文件配置分页 Request VO */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class FileConfigPageReqVO extends PageParam {
|
||||
|
||||
/** 配置名 */
|
||||
private String name;
|
||||
|
||||
/** 存储器 */
|
||||
private Integer storage;
|
||||
|
||||
/** 创建时间 */
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file.vo.config;
|
||||
|
||||
import com.tashow.cloud.infra.framework.file.core.client.FileClientConfig;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 文件配置 Response VO */
|
||||
@Data
|
||||
public class FileConfigRespVO {
|
||||
|
||||
/** 编号" */
|
||||
private Long id;
|
||||
|
||||
/** 配置名" */
|
||||
private String name;
|
||||
|
||||
/** 存储器,参见 FileStorageEnum 枚举类" */
|
||||
private Integer storage;
|
||||
|
||||
/** 是否为主配置" */
|
||||
private Boolean master;
|
||||
|
||||
/** 存储配置 */
|
||||
private FileClientConfig config;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
/** 创建时间 */
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file.vo.config;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Map;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 文件配置创建/修改 Request VO */
|
||||
@Data
|
||||
public class FileConfigSaveReqVO {
|
||||
|
||||
/** 编号 */
|
||||
private Long id;
|
||||
|
||||
/** 配置名" */
|
||||
@NotNull(message = "配置名不能为空")
|
||||
private String name;
|
||||
|
||||
/** 存储器,参见 FileStorageEnum 枚举类" */
|
||||
@NotNull(message = "存储器不能为空")
|
||||
private Integer storage;
|
||||
|
||||
/** 存储配置,配置是动态参数,所以使用 Map 接收 */
|
||||
@NotNull(message = "存储配置不能为空")
|
||||
private Map<String, Object> config;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file.vo.file;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 文件创建 Request VO */
|
||||
@Data
|
||||
public class FileCreateReqVO {
|
||||
|
||||
@NotNull(message = "文件配置编号不能为空")
|
||||
/** 文件配置编号" */
|
||||
private Long configId;
|
||||
|
||||
@NotNull(message = "文件路径不能为空")
|
||||
/** 文件路径" */
|
||||
private String path;
|
||||
|
||||
@NotNull(message = "原文件名不能为空")
|
||||
/** 原文件名" */
|
||||
private String name;
|
||||
|
||||
@NotNull(message = "文件 URL不能为空")
|
||||
/** 文件 URL" */
|
||||
private String url;
|
||||
|
||||
/** 文件 MIME 类型 */
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 文件大小", example = "2048
|
||||
*/
|
||||
private Integer size;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file.vo.file;
|
||||
|
||||
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/** 管理后台 - 文件分页 Request VO */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class FilePageReqVO extends PageParam {
|
||||
|
||||
/** 文件路径,模糊匹配 */
|
||||
private String path;
|
||||
|
||||
/** 文件类型,模糊匹配 */
|
||||
private String type;
|
||||
|
||||
/** 创建时间 */
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file.vo.file;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
/** 管理后台 - 文件预签名地址 Response VO */
|
||||
@Data
|
||||
public class FilePresignedUrlRespVO {
|
||||
|
||||
/** 配置编号" */
|
||||
private Long configId;
|
||||
|
||||
/** 文件上传 URL" */
|
||||
private String uploadUrl;
|
||||
|
||||
/**
|
||||
* 文件访问 URL
|
||||
*
|
||||
* <p>前端上传完文件后,需要使用该 URL 进行访问
|
||||
*/
|
||||
private String url;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file.vo.file;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - 文件 Response VO,不返回 content 字段,太大 */
|
||||
@Data
|
||||
public class FileRespVO {
|
||||
|
||||
/** 文件编号" */
|
||||
private Long id;
|
||||
|
||||
/** 配置编号" */
|
||||
private Long configId;
|
||||
|
||||
/** 文件路径" */
|
||||
private String path;
|
||||
|
||||
/** 原文件名" */
|
||||
private String name;
|
||||
|
||||
/** 文件 URL" */
|
||||
private String url;
|
||||
|
||||
/** 文件MIME类型 */
|
||||
private String type;
|
||||
|
||||
/** 文件大小", example = "2048 */
|
||||
private Integer size;
|
||||
|
||||
/** 创建时间 */
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.tashow.cloud.infra.controller.admin.file.vo.file;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/** 管理后台 - 上传文件 Request VO */
|
||||
@Data
|
||||
public class FileUploadReqVO {
|
||||
|
||||
/** 文件附件 */
|
||||
@NotNull(message = "文件附件不能为空")
|
||||
private MultipartFile file;
|
||||
|
||||
/** 文件附件 */
|
||||
private String path;
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.tashow.cloud.infra.controller.admin.job;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.error;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/** 管理后台 - 定时任务 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/job")
|
||||
@Validated
|
||||
public class JobController {
|
||||
|
||||
/** 获得定时任务分页 */
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@ss.hasPermission('infra:job:query')")
|
||||
public CommonResult<String> getJobPage() {
|
||||
return error(-1, "Cloud 版本使用 XXL-Job 作为定时任务!请参考 https://cloud.iocoder.cn/job/ 文档操作");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.tashow.cloud.infra.controller.admin.logger;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
import static com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.common.util.object.BeanUtils;
|
||||
import com.tashow.cloud.excel.excel.core.util.ExcelUtils;
|
||||
import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogPageReqVO;
|
||||
import com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog.ApiAccessLogRespVO;
|
||||
import com.tashow.cloud.infra.dal.dataobject.logger.ApiAccessLogDO;
|
||||
import com.tashow.cloud.infra.service.logger.ApiAccessLogService;
|
||||
import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/** 管理后台 - API 访问日志 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/api-access-log")
|
||||
@Validated
|
||||
public class ApiAccessLogController {
|
||||
|
||||
@Resource private ApiAccessLogService apiAccessLogService;
|
||||
|
||||
/** 获得API 访问日志分页 */
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@ss.hasPermission('infra:api-access-log:query')")
|
||||
public CommonResult<PageResult<ApiAccessLogRespVO>> getApiAccessLogPage(
|
||||
@Valid ApiAccessLogPageReqVO pageReqVO) {
|
||||
PageResult<ApiAccessLogDO> pageResult = apiAccessLogService.getApiAccessLogPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ApiAccessLogRespVO.class));
|
||||
}
|
||||
|
||||
/** 导出API 访问日志 Excel */
|
||||
@GetMapping("/export-excel")
|
||||
@PreAuthorize("@ss.hasPermission('infra:api-access-log:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportApiAccessLogExcel(
|
||||
@Valid ApiAccessLogPageReqVO exportReqVO, HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<ApiAccessLogDO> list = apiAccessLogService.getApiAccessLogPage(exportReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(
|
||||
response,
|
||||
"API 访问日志.xls",
|
||||
"数据",
|
||||
ApiAccessLogRespVO.class,
|
||||
BeanUtils.toBean(list, ApiAccessLogRespVO.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.tashow.cloud.infra.controller.admin.logger;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
import static com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.tashow.cloud.web.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.common.util.object.BeanUtils;
|
||||
import com.tashow.cloud.excel.excel.core.util.ExcelUtils;
|
||||
import com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogPageReqVO;
|
||||
import com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog.ApiErrorLogRespVO;
|
||||
import com.tashow.cloud.infra.dal.dataobject.logger.ApiErrorLogDO;
|
||||
import com.tashow.cloud.infra.service.logger.ApiErrorLogService;
|
||||
import com.tashow.cloud.web.apilog.core.annotation.ApiAccessLog;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
/** 管理后台 - API 错误日志 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/api-error-log")
|
||||
@Validated
|
||||
public class ApiErrorLogController {
|
||||
|
||||
@Resource private ApiErrorLogService apiErrorLogService;
|
||||
|
||||
/** 更新 API 错误日志的状态 */
|
||||
@PutMapping("/update-status")
|
||||
@PreAuthorize("@ss.hasPermission('infra:api-error-log:update-status')")
|
||||
public CommonResult<Boolean> updateApiErrorLogProcess(
|
||||
@RequestParam("id") Long id, @RequestParam("processStatus") Integer processStatus) {
|
||||
apiErrorLogService.updateApiErrorLogProcess(id, processStatus, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 获得 API 错误日志分页 */
|
||||
@GetMapping("/page")
|
||||
@PreAuthorize("@ss.hasPermission('infra:api-error-log:query')")
|
||||
public CommonResult<PageResult<ApiErrorLogRespVO>> getApiErrorLogPage(
|
||||
@Valid ApiErrorLogPageReqVO pageReqVO) {
|
||||
PageResult<ApiErrorLogDO> pageResult = apiErrorLogService.getApiErrorLogPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ApiErrorLogRespVO.class));
|
||||
}
|
||||
|
||||
/** 导出 API 错误日志 Excel */
|
||||
@GetMapping("/export-excel")
|
||||
@PreAuthorize("@ss.hasPermission('infra:api-error-log:export')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void exportApiErrorLogExcel(
|
||||
@Valid ApiErrorLogPageReqVO exportReqVO, HttpServletResponse response) throws IOException {
|
||||
exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
List<ApiErrorLogDO> list = apiErrorLogService.getApiErrorLogPage(exportReqVO).getList();
|
||||
// 导出 Excel
|
||||
ExcelUtils.write(
|
||||
response,
|
||||
"API 错误日志.xls",
|
||||
"数据",
|
||||
ApiErrorLogRespVO.class,
|
||||
BeanUtils.toBean(list, ApiErrorLogRespVO.class));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog;
|
||||
|
||||
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/** 管理后台 - API 访问日志分页 Request VO */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ApiAccessLogPageReqVO extends PageParam {
|
||||
|
||||
/** 用户编号 */
|
||||
private Long userId;
|
||||
|
||||
/** 用户类型 */
|
||||
private Integer userType;
|
||||
|
||||
/** 应用名 */
|
||||
private String applicationName;
|
||||
|
||||
/** 请求地址,模糊匹配 */
|
||||
private String requestUrl;
|
||||
|
||||
/** 开始时间 */
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] beginTime;
|
||||
|
||||
/** 执行时长,大于等于,单位:毫秒 */
|
||||
private Integer duration;
|
||||
|
||||
/** 结果码 */
|
||||
private Integer resultCode;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
package com.tashow.cloud.infra.controller.admin.logger.vo.apiaccesslog;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.tashow.cloud.excel.excel.core.annotations.DictFormat;
|
||||
import com.tashow.cloud.excel.excel.core.convert.DictConvert;
|
||||
import com.tashow.cloud.systemapi.enums.DictTypeConstants;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - API 访问日志 Response VO */
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class ApiAccessLogRespVO {
|
||||
|
||||
/** 日志主键" */
|
||||
@ExcelProperty("日志主键")
|
||||
private Long id;
|
||||
|
||||
/** 链路追踪编号" */
|
||||
@ExcelProperty("链路追踪编号")
|
||||
private String traceId;
|
||||
|
||||
/** 用户编号" */
|
||||
@ExcelProperty("用户编号")
|
||||
private Long userId;
|
||||
|
||||
/** 用户类型,参见 UserTypeEnum 枚举" */
|
||||
@ExcelProperty(value = "用户类型", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.USER_TYPE)
|
||||
private Integer userType;
|
||||
|
||||
/** 应用名" */
|
||||
@ExcelProperty("应用名")
|
||||
private String applicationName;
|
||||
|
||||
/** 请求方法名" */
|
||||
@ExcelProperty("请求方法名")
|
||||
private String requestMethod;
|
||||
|
||||
/** 请求地址", example = "/xxx/yyy */
|
||||
@ExcelProperty("请求地址")
|
||||
private String requestUrl;
|
||||
|
||||
/** 请求参数 */
|
||||
@ExcelProperty("请求参数")
|
||||
private String requestParams;
|
||||
|
||||
/** 响应结果 */
|
||||
@ExcelProperty("响应结果")
|
||||
private String responseBody;
|
||||
|
||||
/** 用户 IP" */
|
||||
@ExcelProperty("用户 IP")
|
||||
private String userIp;
|
||||
|
||||
/** 浏览器 UA" */
|
||||
@ExcelProperty("浏览器 UA")
|
||||
private String userAgent;
|
||||
|
||||
/** 操作模块", example = "商品模块 */
|
||||
@ExcelProperty("操作模块")
|
||||
private String operateModule;
|
||||
|
||||
/** 操作名", example = "创建商品 */
|
||||
@ExcelProperty("操作名")
|
||||
private String operateName;
|
||||
|
||||
/** 操作分类" */
|
||||
@ExcelProperty(value = "操作分类", converter = DictConvert.class)
|
||||
@DictFormat(com.tashow.cloud.infraapi.enums.DictTypeConstants.OPERATE_TYPE)
|
||||
private Integer operateType;
|
||||
|
||||
/** 开始请求时间 */
|
||||
@ExcelProperty("开始请求时间")
|
||||
private LocalDateTime beginTime;
|
||||
|
||||
/** 结束请求时间 */
|
||||
@ExcelProperty("结束请求时间")
|
||||
private LocalDateTime endTime;
|
||||
|
||||
/** 执行时长" */
|
||||
@ExcelProperty("执行时长")
|
||||
private Integer duration;
|
||||
|
||||
/** 结果码" */
|
||||
@ExcelProperty("结果码")
|
||||
private Integer resultCode;
|
||||
|
||||
/** 结果提示 */
|
||||
@ExcelProperty("结果提示")
|
||||
private String resultMsg;
|
||||
|
||||
/** 创建时间 */
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog;
|
||||
|
||||
import static com.tashow.cloud.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
import com.tashow.cloud.common.pojo.PageParam;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
/** 管理后台 - API 错误日志分页 Request VO */
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ApiErrorLogPageReqVO extends PageParam {
|
||||
|
||||
/** 用户编号 */
|
||||
private Long userId;
|
||||
|
||||
/** 用户类型 */
|
||||
private Integer userType;
|
||||
|
||||
/** 应用名 */
|
||||
private String applicationName;
|
||||
|
||||
/** 请求地址 */
|
||||
private String requestUrl;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
/** 异常发生时间 */
|
||||
private LocalDateTime[] exceptionTime;
|
||||
|
||||
/** 处理状态 */
|
||||
private Integer processStatus;
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.tashow.cloud.infra.controller.admin.logger.vo.apierrorlog;
|
||||
|
||||
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import com.alibaba.excel.annotation.ExcelProperty;
|
||||
import com.tashow.cloud.excel.excel.core.annotations.DictFormat;
|
||||
import com.tashow.cloud.excel.excel.core.convert.DictConvert;
|
||||
import com.tashow.cloud.infraapi.enums.DictTypeConstants;
|
||||
import java.time.LocalDateTime;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - API 错误日志 Response VO */
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class ApiErrorLogRespVO {
|
||||
|
||||
/** 编号" */
|
||||
@ExcelProperty("编号")
|
||||
private Long id;
|
||||
|
||||
/** 链路追踪编号" */
|
||||
@ExcelProperty("链路追踪编号")
|
||||
private String traceId;
|
||||
|
||||
/** 用户编号" */
|
||||
@ExcelProperty("用户编号")
|
||||
private Long userId;
|
||||
|
||||
/** 用户类型" */
|
||||
@ExcelProperty(value = "用户类型", converter = DictConvert.class)
|
||||
@DictFormat(com.tashow.cloud.systemapi.enums.DictTypeConstants.USER_TYPE)
|
||||
private Integer userType;
|
||||
|
||||
/** 应用名" */
|
||||
@ExcelProperty("应用名")
|
||||
private String applicationName;
|
||||
|
||||
/** 请求方法名" */
|
||||
@ExcelProperty("请求方法名")
|
||||
private String requestMethod;
|
||||
|
||||
/** 请求地址", example = "/xx/yy */
|
||||
@ExcelProperty("请求地址")
|
||||
private String requestUrl;
|
||||
|
||||
/** 请求参数 */
|
||||
@ExcelProperty("请求参数")
|
||||
private String requestParams;
|
||||
|
||||
/** 用户 IP" */
|
||||
@ExcelProperty("用户 IP")
|
||||
private String userIp;
|
||||
|
||||
/** 浏览器 UA" */
|
||||
@ExcelProperty("浏览器 UA")
|
||||
private String userAgent;
|
||||
|
||||
/** 异常发生时间 */
|
||||
@ExcelProperty("异常发生时间")
|
||||
private LocalDateTime exceptionTime;
|
||||
|
||||
/** 异常名 */
|
||||
@ExcelProperty("异常名")
|
||||
private String exceptionName;
|
||||
|
||||
/** 异常导致的消息 */
|
||||
@ExcelProperty("异常导致的消息")
|
||||
private String exceptionMessage;
|
||||
|
||||
/** 异常导致的根消息 */
|
||||
@ExcelProperty("异常导致的根消息")
|
||||
private String exceptionRootCauseMessage;
|
||||
|
||||
/** 异常的栈轨迹 */
|
||||
@ExcelProperty("异常的栈轨迹")
|
||||
private String exceptionStackTrace;
|
||||
|
||||
/** 异常发生的类全名 */
|
||||
@ExcelProperty("异常发生的类全名")
|
||||
private String exceptionClassName;
|
||||
|
||||
/** 异常发生的类文件 */
|
||||
@ExcelProperty("异常发生的类文件")
|
||||
private String exceptionFileName;
|
||||
|
||||
/** 异常发生的方法名 */
|
||||
@ExcelProperty("异常发生的方法名")
|
||||
private String exceptionMethodName;
|
||||
|
||||
/** 异常发生的方法所在行 */
|
||||
@ExcelProperty("异常发生的方法所在行")
|
||||
private Integer exceptionLineNumber;
|
||||
|
||||
/** 处理状态" */
|
||||
@ExcelProperty(value = "处理状态", converter = DictConvert.class)
|
||||
@DictFormat(DictTypeConstants.API_ERROR_LOG_PROCESS_STATUS)
|
||||
private Integer processStatus;
|
||||
|
||||
/** 处理时间 */
|
||||
@ExcelProperty("处理时间")
|
||||
private LocalDateTime processTime;
|
||||
|
||||
/** 处理用户编号 */
|
||||
@ExcelProperty("处理用户编号")
|
||||
private Integer processUserId;
|
||||
|
||||
/** 创建时间 */
|
||||
@ExcelProperty("创建时间")
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
### 请求 /infra/redis/get-monitor-info 接口 => 成功
|
||||
GET {{baseUrl}}/infra/redis/get-monitor-info
|
||||
Authorization: Bearer {{token}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.tashow.cloud.infra.controller.admin.redis;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.infra.controller.admin.redis.vo.RedisMonitorRespVO;
|
||||
import com.tashow.cloud.infra.convert.redis.RedisConvert;
|
||||
import jakarta.annotation.Resource;
|
||||
import java.util.Properties;
|
||||
import org.springframework.data.redis.connection.RedisServerCommands;
|
||||
import org.springframework.data.redis.core.RedisCallback;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/** 管理后台 - Redis 监控 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/redis")
|
||||
public class RedisController {
|
||||
|
||||
@Resource private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/** 获得 Redis 监控信息 */
|
||||
@GetMapping("/get-monitor-info")
|
||||
@PreAuthorize("@ss.hasPermission('infra:redis:get-monitor-info')")
|
||||
public CommonResult<RedisMonitorRespVO> getRedisMonitorInfo() {
|
||||
// 获得 Redis 统计信息
|
||||
Properties info =
|
||||
stringRedisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info);
|
||||
Long dbSize = stringRedisTemplate.execute(RedisServerCommands::dbSize);
|
||||
Properties commandStats =
|
||||
stringRedisTemplate.execute(
|
||||
(RedisCallback<Properties>)
|
||||
connection -> connection.serverCommands().info("commandstats"));
|
||||
assert commandStats != null; // 断言,避免警告
|
||||
// 拼接结果返回
|
||||
return success(RedisConvert.INSTANCE.build(info, dbSize, commandStats));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.tashow.cloud.infra.controller.admin.redis.vo;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
|
||||
/** 管理后台 - Redis 监控信息 Response VO */
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public class RedisMonitorRespVO {
|
||||
|
||||
private Properties info;
|
||||
|
||||
/** Redis key 数量" */
|
||||
private Long dbSize;
|
||||
|
||||
/** CommandStat 数组 */
|
||||
private List<CommandStat> commandStats;
|
||||
|
||||
/** Redis 命令统计结果 */
|
||||
@Data
|
||||
@Builder
|
||||
@AllArgsConstructor
|
||||
public static class CommandStat {
|
||||
|
||||
/** Redis 命令" */
|
||||
private String command;
|
||||
|
||||
/** 调用次数" */
|
||||
private Long calls;
|
||||
|
||||
/** 消耗 CPU 秒数" */
|
||||
private Long usec;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.tashow.cloud.infra.controller.app.file;
|
||||
|
||||
import static com.tashow.cloud.common.pojo.CommonResult.success;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.infra.controller.admin.file.vo.file.FileCreateReqVO;
|
||||
import com.tashow.cloud.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
|
||||
import com.tashow.cloud.infra.controller.app.file.vo.AppFileUploadReqVO;
|
||||
import com.tashow.cloud.infra.service.file.FileService;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/** 用户 App - 文件存储 */
|
||||
@RestController
|
||||
@RequestMapping("/infra/file")
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class AppFileController {
|
||||
|
||||
@Resource private FileService fileService;
|
||||
|
||||
/** 上传文件 */
|
||||
@PostMapping("/upload")
|
||||
@PermitAll
|
||||
public CommonResult<String> uploadFile(AppFileUploadReqVO uploadReqVO) throws Exception {
|
||||
MultipartFile file = uploadReqVO.getFile();
|
||||
String path = uploadReqVO.getPath();
|
||||
return success(
|
||||
fileService.createFile(
|
||||
file.getOriginalFilename(), path, IoUtil.readBytes(file.getInputStream())));
|
||||
}
|
||||
|
||||
/** 获取文件预签名地址", description = "模式二:前端上传文件:用于前端直接上传七牛、阿里云 OSS 等文件存储器 */
|
||||
@GetMapping("/presigned-url")
|
||||
@PermitAll
|
||||
public CommonResult<FilePresignedUrlRespVO> getFilePresignedUrl(@RequestParam("path") String path)
|
||||
throws Exception {
|
||||
return success(fileService.getFilePresignedUrl(path));
|
||||
}
|
||||
|
||||
/** 创建文件", description = "模式二:前端上传文件:配合 presigned-url 接口,记录上传了上传的文件 */
|
||||
@PostMapping("/create")
|
||||
@PermitAll
|
||||
public CommonResult<Long> createFile(@Valid @RequestBody FileCreateReqVO createReqVO) {
|
||||
return success(fileService.createFile(createReqVO));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.tashow.cloud.infra.controller.app.file.vo;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
/** 用户 App - 上传文件 Request VO */
|
||||
@Data
|
||||
public class AppFileUploadReqVO {
|
||||
|
||||
/** 文件附件 */
|
||||
@NotNull(message = "文件附件不能为空")
|
||||
private MultipartFile file;
|
||||
|
||||
/** 文件附件 */
|
||||
private String path;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 占位
|
||||
*/
|
||||
package com.tashow.cloud.infra.controller.app;
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 提供 RESTful API 给前端:
|
||||
* 1. admin 包:提供给管理后台 yudao-ui-admin 前端项目
|
||||
* 2. app 包:提供给用户 APP yudao-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分
|
||||
*/
|
||||
package com.tashow.cloud.infra.controller;
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.tashow.cloud.infra.convert.codegen;
|
||||
|
||||
import com.tashow.cloud.common.util.collection.CollectionUtils;
|
||||
import com.tashow.cloud.common.util.object.BeanUtils;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenDetailRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.CodegenPreviewRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.column.CodegenColumnRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.codegen.vo.table.CodegenTableRespVO;
|
||||
import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenColumnDO;
|
||||
import com.tashow.cloud.infra.dal.dataobject.codegen.CodegenTableDO;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableField;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
||||
import org.apache.ibatis.type.JdbcType;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.Named;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Mapper
|
||||
public interface CodegenConvert {
|
||||
|
||||
CodegenConvert INSTANCE = Mappers.getMapper(CodegenConvert.class);
|
||||
|
||||
// ========== TableInfo 相关 ==========
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "name", target = "tableName"),
|
||||
@Mapping(source = "comment", target = "tableComment"),
|
||||
})
|
||||
CodegenTableDO convert(TableInfo bean);
|
||||
|
||||
List<CodegenColumnDO> convertList(List<TableField> list);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "name", target = "columnName"),
|
||||
@Mapping(source = "metaInfo.jdbcType", target = "dataType", qualifiedByName = "getDataType"),
|
||||
@Mapping(source = "comment", target = "columnComment"),
|
||||
@Mapping(source = "metaInfo.nullable", target = "nullable"),
|
||||
@Mapping(source = "keyFlag", target = "primaryKey"),
|
||||
@Mapping(source = "columnType.type", target = "javaType"),
|
||||
@Mapping(source = "propertyName", target = "javaField"),
|
||||
})
|
||||
CodegenColumnDO convert(TableField bean);
|
||||
|
||||
@Named("getDataType")
|
||||
default String getDataType(JdbcType jdbcType) {
|
||||
return jdbcType.name();
|
||||
}
|
||||
|
||||
// ========== 其它 ==========
|
||||
|
||||
default CodegenDetailRespVO convert(CodegenTableDO table, List<CodegenColumnDO> columns) {
|
||||
CodegenDetailRespVO respVO = new CodegenDetailRespVO();
|
||||
respVO.setTable(BeanUtils.toBean(table, CodegenTableRespVO.class));
|
||||
respVO.setColumns(BeanUtils.toBean(columns, CodegenColumnRespVO.class));
|
||||
return respVO;
|
||||
}
|
||||
|
||||
default List<CodegenPreviewRespVO> convert(Map<String, String> codes) {
|
||||
return CollectionUtils.convertList(codes.entrySet(),
|
||||
entry -> new CodegenPreviewRespVO().setFilePath(entry.getKey()).setCode(entry.getValue()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.tashow.cloud.infra.convert.config;
|
||||
|
||||
import com.tashow.cloud.common.pojo.PageResult;
|
||||
import com.tashow.cloud.infra.controller.admin.config.vo.ConfigRespVO;
|
||||
import com.tashow.cloud.infra.controller.admin.config.vo.ConfigSaveReqVO;
|
||||
import com.tashow.cloud.infra.dal.dataobject.config.ConfigDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ConfigConvert {
|
||||
|
||||
ConfigConvert INSTANCE = Mappers.getMapper(ConfigConvert.class);
|
||||
|
||||
PageResult<ConfigRespVO> convertPage(PageResult<ConfigDO> page);
|
||||
|
||||
List<ConfigRespVO> convertList(List<ConfigDO> list);
|
||||
|
||||
@Mapping(source = "configKey", target = "key")
|
||||
ConfigRespVO convert(ConfigDO bean);
|
||||
|
||||
@Mapping(source = "key", target = "configKey")
|
||||
ConfigDO convert(ConfigSaveReqVO bean);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.tashow.cloud.infra.convert.file;
|
||||
|
||||
import com.tashow.cloud.infra.controller.admin.file.vo.config.FileConfigSaveReqVO;
|
||||
import com.tashow.cloud.infra.dal.dataobject.file.FileConfigDO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* 文件配置 Convert
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Mapper
|
||||
public interface FileConfigConvert {
|
||||
|
||||
FileConfigConvert INSTANCE = Mappers.getMapper(FileConfigConvert.class);
|
||||
|
||||
@Mapping(target = "config", ignore = true)
|
||||
FileConfigDO convert(FileConfigSaveReqVO bean);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 提供 POJO 类的实体转换
|
||||
*
|
||||
* 目前使用 MapStruct 框架
|
||||
*/
|
||||
package com.tashow.cloud.infra.convert;
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.tashow.cloud.infra.convert.redis;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.tashow.cloud.infra.controller.admin.redis.vo.RedisMonitorRespVO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Properties;
|
||||
|
||||
@Mapper
|
||||
public interface RedisConvert {
|
||||
|
||||
RedisConvert INSTANCE = Mappers.getMapper(RedisConvert.class);
|
||||
|
||||
default RedisMonitorRespVO build(Properties info, Long dbSize, Properties commandStats) {
|
||||
RedisMonitorRespVO respVO = RedisMonitorRespVO.builder().info(info).dbSize(dbSize)
|
||||
.commandStats(new ArrayList<>(commandStats.size())).build();
|
||||
commandStats.forEach((key, value) -> {
|
||||
respVO.getCommandStats().add(RedisMonitorRespVO.CommandStat.builder()
|
||||
.command(StrUtil.subAfter((String) key, "cmdstat_", false))
|
||||
.calls(Long.valueOf(StrUtil.subBetween((String) value, "calls=", ",")))
|
||||
.usec(Long.valueOf(StrUtil.subBetween((String) value, "usec=", ",")))
|
||||
.build());
|
||||
});
|
||||
return respVO;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao>
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.codegen;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenColumnListConditionEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenColumnListConditionEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableField;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenColumnHtmlTypeEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenColumnListConditionEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 代码生成 column 字段定义
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "infra_codegen_column", autoResultMap = true)
|
||||
@KeySequence("infra_codegen_column_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CodegenColumnDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* ID 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 表编号
|
||||
* <p>
|
||||
* 关联 {@link CodegenTableDO#getId()}
|
||||
*/
|
||||
private Long tableId;
|
||||
|
||||
// ========== 表相关字段 ==========
|
||||
|
||||
/**
|
||||
* 字段名
|
||||
*
|
||||
* 关联 {@link TableField#getName()}
|
||||
*/
|
||||
private String columnName;
|
||||
/**
|
||||
* 数据库字段类型
|
||||
*
|
||||
* 关联 {@link TableField.MetaInfo#getJdbcType()}
|
||||
*/
|
||||
private String dataType;
|
||||
/**
|
||||
* 字段描述
|
||||
*
|
||||
* 关联 {@link TableField#getComment()}
|
||||
*/
|
||||
private String columnComment;
|
||||
/**
|
||||
* 是否允许为空
|
||||
*
|
||||
* 关联 {@link TableField.MetaInfo#isNullable()}
|
||||
*/
|
||||
private Boolean nullable;
|
||||
/**
|
||||
* 是否主键
|
||||
*
|
||||
* 关联 {@link TableField#isKeyFlag()}
|
||||
*/
|
||||
private Boolean primaryKey;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Integer ordinalPosition;
|
||||
|
||||
// ========== Java 相关字段 ==========
|
||||
|
||||
/**
|
||||
* Java 属性类型
|
||||
*
|
||||
* 例如说 String、Boolean 等等
|
||||
*
|
||||
* 关联 {@link TableField#getColumnType()}
|
||||
*/
|
||||
private String javaType;
|
||||
/**
|
||||
* Java 属性名
|
||||
*
|
||||
* 关联 {@link TableField#getPropertyName()}
|
||||
*/
|
||||
private String javaField;
|
||||
/**
|
||||
* 字典类型
|
||||
* <p>
|
||||
* 关联 DictTypeDO 的 type 属性
|
||||
*/
|
||||
private String dictType;
|
||||
/**
|
||||
* 数据示例,主要用于生成 Swagger 注解的 example 字段
|
||||
*/
|
||||
private String example;
|
||||
|
||||
// ========== CRUD 相关字段 ==========
|
||||
|
||||
/**
|
||||
* 是否为 Create 创建操作的字段
|
||||
*/
|
||||
private Boolean createOperation;
|
||||
/**
|
||||
* 是否为 Update 更新操作的字段
|
||||
*/
|
||||
private Boolean updateOperation;
|
||||
/**
|
||||
* 是否为 List 查询操作的字段
|
||||
*/
|
||||
private Boolean listOperation;
|
||||
/**
|
||||
* List 查询操作的条件类型
|
||||
* <p>
|
||||
* 枚举 {@link CodegenColumnListConditionEnum}
|
||||
*/
|
||||
private String listOperationCondition;
|
||||
/**
|
||||
* 是否为 List 查询操作的返回字段
|
||||
*/
|
||||
private Boolean listOperationResult;
|
||||
|
||||
// ========== UI 相关字段 ==========
|
||||
|
||||
/**
|
||||
* 显示类型
|
||||
* <p>
|
||||
* 枚举 {@link CodegenColumnHtmlTypeEnum}
|
||||
*/
|
||||
private String htmlType;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.codegen;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenFrontTypeEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import com.tashow.cloud.infra.dal.dataobject.db.DataSourceConfigDO;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenFrontTypeEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenFrontTypeEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenSceneEnum;
|
||||
import com.tashow.cloud.infra.enums.codegen.CodegenTemplateTypeEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
/**
|
||||
* 代码生成 table 表定义
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "infra_codegen_table", autoResultMap = true)
|
||||
@KeySequence("infra_codegen_table_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class CodegenTableDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* ID 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 数据源编号
|
||||
*
|
||||
* 关联 {@link DataSourceConfigDO#getId()}
|
||||
*/
|
||||
private Long dataSourceConfigId;
|
||||
/**
|
||||
* 生成场景
|
||||
*
|
||||
* 枚举 {@link CodegenSceneEnum}
|
||||
*/
|
||||
private Integer scene;
|
||||
|
||||
// ========== 表相关字段 ==========
|
||||
|
||||
/**
|
||||
* 表名称
|
||||
*
|
||||
* 关联 {@link TableInfo#getName()}
|
||||
*/
|
||||
private String tableName;
|
||||
/**
|
||||
* 表描述
|
||||
*
|
||||
* 关联 {@link TableInfo#getComment()}
|
||||
*/
|
||||
private String tableComment;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
// ========== 类相关字段 ==========
|
||||
|
||||
/**
|
||||
* 模块名,即一级目录
|
||||
*
|
||||
* 例如说,system、infra、tool 等等
|
||||
*/
|
||||
private String moduleName;
|
||||
/**
|
||||
* 业务名,即二级目录
|
||||
*
|
||||
* 例如说,user、permission、dict 等等
|
||||
*/
|
||||
private String businessName;
|
||||
/**
|
||||
* 类名称(首字母大写)
|
||||
*
|
||||
* 例如说,SysUser、SysMenu、SysDictData 等等
|
||||
*/
|
||||
private String className;
|
||||
/**
|
||||
* 类描述
|
||||
*/
|
||||
private String classComment;
|
||||
/**
|
||||
* 作者
|
||||
*/
|
||||
private String author;
|
||||
|
||||
// ========== 生成相关字段 ==========
|
||||
|
||||
/**
|
||||
* 模板类型
|
||||
*
|
||||
* 枚举 {@link CodegenTemplateTypeEnum}
|
||||
*/
|
||||
private Integer templateType;
|
||||
/**
|
||||
* 代码生成的前端类型
|
||||
*
|
||||
* 枚举 {@link CodegenFrontTypeEnum}
|
||||
*/
|
||||
private Integer frontType;
|
||||
|
||||
// ========== 菜单相关字段 ==========
|
||||
|
||||
/**
|
||||
* 父菜单编号
|
||||
*
|
||||
* 关联 MenuDO 的 id 属性
|
||||
*/
|
||||
private Long parentMenuId;
|
||||
|
||||
// ========== 主子表相关字段 ==========
|
||||
|
||||
/**
|
||||
* 主表的编号
|
||||
*
|
||||
* 关联 {@link CodegenTableDO#getId()}
|
||||
*/
|
||||
private Long masterTableId;
|
||||
/**
|
||||
* 【自己】子表关联主表的字段编号
|
||||
*
|
||||
* 关联 {@link CodegenColumnDO#getId()}
|
||||
*/
|
||||
private Long subJoinColumnId;
|
||||
/**
|
||||
* 主表与子表是否一对多
|
||||
*
|
||||
* true:一对多
|
||||
* false:一对一
|
||||
*/
|
||||
private Boolean subJoinMany;
|
||||
|
||||
// ========== 树表相关字段 ==========
|
||||
|
||||
/**
|
||||
* 树表的父字段编号
|
||||
*
|
||||
* 关联 {@link CodegenColumnDO#getId()}
|
||||
*/
|
||||
private Long treeParentColumnId;
|
||||
/**
|
||||
* 树表的名字字段编号
|
||||
*
|
||||
* 名字的用途:新增或修改时,select 框展示的字段
|
||||
*
|
||||
* 关联 {@link CodegenColumnDO#getId()}
|
||||
*/
|
||||
private Long treeNameColumnId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.config;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.tashow.cloud.infra.enums.config.ConfigTypeEnum;
|
||||
import com.tashow.cloud.infra.enums.config.ConfigTypeEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.tashow.cloud.infra.enums.config.ConfigTypeEnum;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* 参数配置表
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_config")
|
||||
@KeySequence("infra_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class ConfigDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 参数主键
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 参数分类
|
||||
*/
|
||||
private String category;
|
||||
/**
|
||||
* 参数名称
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 参数键名
|
||||
*
|
||||
* 支持多 DB 类型时,无法直接使用 key + @TableField("config_key") 来实现转换,原因是 "config_key" AS key 而存在报错
|
||||
*/
|
||||
private String configKey;
|
||||
/**
|
||||
* 参数键值
|
||||
*/
|
||||
private String value;
|
||||
/**
|
||||
* 参数类型
|
||||
*
|
||||
* 枚举 {@link ConfigTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 是否可见
|
||||
*
|
||||
* 不可见的参数,一般是敏感参数,前端不可获取
|
||||
*/
|
||||
private Boolean visible;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.db;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.tashow.cloud.mybatis.mybatis.core.type.EncryptTypeHandler;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 数据源配置
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "infra_data_source_config", autoResultMap = true)
|
||||
@KeySequence("infra_data_source_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
public class DataSourceConfigDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 主键编号 - Master 数据源
|
||||
*/
|
||||
public static final Long ID_MASTER = 0L;
|
||||
|
||||
/**
|
||||
* 主键编号
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 连接名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 数据源连接
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
private String username;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
@TableField(typeHandler = EncryptTypeHandler.class)
|
||||
private String password;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.demo.demo01;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 示例联系人 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("yudao_demo01_contact")
|
||||
@KeySequence("yudao_demo01_contact_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Demo01ContactDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 出生年
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
private String avatar;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.demo.demo02;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 示例分类 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("yudao_demo02_category")
|
||||
@KeySequence("yudao_demo02_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Demo02CategoryDO extends BaseDO {
|
||||
|
||||
public static final Long PARENT_ID_ROOT = 0L;
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 父级编号
|
||||
*/
|
||||
private Long parentId;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.demo.demo03;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 学生课程 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("yudao_demo03_course")
|
||||
@KeySequence("yudao_demo03_course_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Demo03CourseDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 学生编号
|
||||
*/
|
||||
private Long studentId;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 分数
|
||||
*/
|
||||
private Integer score;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.demo.demo03;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 学生班级 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("yudao_demo03_grade")
|
||||
@KeySequence("yudao_demo03_grade_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Demo03GradeDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 学生编号
|
||||
*/
|
||||
private Long studentId;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 班主任
|
||||
*/
|
||||
private String teacher;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.demo.demo03;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 学生 DO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("yudao_demo03_student")
|
||||
@KeySequence("yudao_demo03_student_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Demo03StudentDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 名字
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 性别
|
||||
*
|
||||
* 枚举 {@link TODO system_user_sex 对应的类}
|
||||
*/
|
||||
private Integer sex;
|
||||
/**
|
||||
* 出生日期
|
||||
*/
|
||||
private LocalDateTime birthday;
|
||||
/**
|
||||
* 简介
|
||||
*/
|
||||
private String description;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.file;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.tashow.cloud.common.util.json.JsonUtils;
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.tashow.cloud.infra.framework.file.core.client.FileClientConfig;
|
||||
import com.tashow.cloud.infra.framework.file.core.client.db.DBFileClientConfig;
|
||||
import com.tashow.cloud.infra.framework.file.core.client.ftp.FtpFileClientConfig;
|
||||
import com.tashow.cloud.infra.framework.file.core.client.local.LocalFileClientConfig;
|
||||
import com.tashow.cloud.infra.framework.file.core.client.s3.S3FileClientConfig;
|
||||
import com.tashow.cloud.infra.framework.file.core.client.sftp.SftpFileClientConfig;
|
||||
import com.tashow.cloud.infra.framework.file.core.enums.FileStorageEnum;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.baomidou.mybatisplus.extension.handlers.AbstractJsonTypeHandler;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import lombok.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
/**
|
||||
* 文件配置表
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName(value = "infra_file_config", autoResultMap = true)
|
||||
@KeySequence("infra_file_config_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FileConfigDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 配置编号,数据库自增
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 配置名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 存储器
|
||||
*
|
||||
* 枚举 {@link FileStorageEnum}
|
||||
*/
|
||||
private Integer storage;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
private String remark;
|
||||
/**
|
||||
* 是否为主配置
|
||||
*
|
||||
* 由于我们可以配置多个文件配置,默认情况下,使用主配置进行文件的上传
|
||||
*/
|
||||
private Boolean master;
|
||||
|
||||
/**
|
||||
* 支付渠道配置
|
||||
*/
|
||||
@TableField(typeHandler = FileClientConfigTypeHandler.class)
|
||||
private FileClientConfig config;
|
||||
|
||||
public static class FileClientConfigTypeHandler extends AbstractJsonTypeHandler<Object> {
|
||||
|
||||
public FileClientConfigTypeHandler(Class<?> type) {
|
||||
super(type);
|
||||
}
|
||||
|
||||
public FileClientConfigTypeHandler(Class<?> type, Field field) {
|
||||
super(type, field);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object parse(String json) {
|
||||
FileClientConfig config = JsonUtils.parseObjectQuietly(json, new TypeReference<>() {});
|
||||
if (config != null) {
|
||||
return config;
|
||||
}
|
||||
|
||||
// 兼容老版本的包路径
|
||||
String className = JsonUtils.parseObject(json, "@class", String.class);
|
||||
className = StrUtil.subAfter(className, ".", true);
|
||||
switch (className) {
|
||||
case "DBFileClientConfig":
|
||||
return JsonUtils.parseObject2(json, DBFileClientConfig.class);
|
||||
case "FtpFileClientConfig":
|
||||
return JsonUtils.parseObject2(json, FtpFileClientConfig.class);
|
||||
case "LocalFileClientConfig":
|
||||
return JsonUtils.parseObject2(json, LocalFileClientConfig.class);
|
||||
case "SftpFileClientConfig":
|
||||
return JsonUtils.parseObject2(json, SftpFileClientConfig.class);
|
||||
case "S3FileClientConfig":
|
||||
return JsonUtils.parseObject2(json, S3FileClientConfig.class);
|
||||
default:
|
||||
throw new IllegalArgumentException("未知的 FileClientConfig 类型:" + json);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toJson(Object obj) {
|
||||
return JsonUtils.toJsonString(obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.file;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.tashow.cloud.infra.framework.file.core.client.db.DBFileClient;
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 文件内容表
|
||||
*
|
||||
* 专门用于存储 {@link DBFileClient} 的文件内容
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_file_content")
|
||||
@KeySequence("infra_file_content_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FileContentDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号,数据库自增
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 配置编号
|
||||
*
|
||||
* 关联 {@link FileConfigDO#getId()}
|
||||
*/
|
||||
private Long configId;
|
||||
/**
|
||||
* 路径,即文件名
|
||||
*/
|
||||
private String path;
|
||||
/**
|
||||
* 文件内容
|
||||
*/
|
||||
private byte[] content;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.file;
|
||||
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
|
||||
/**
|
||||
* 文件表
|
||||
* 每次文件上传,都会记录一条记录到该表中
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_file")
|
||||
@KeySequence("infra_file_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class FileDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* 编号,数据库自增
|
||||
*/
|
||||
private Long id;
|
||||
/**
|
||||
* 配置编号
|
||||
*
|
||||
* 关联 {@link FileConfigDO#getId()}
|
||||
*/
|
||||
private Long configId;
|
||||
/**
|
||||
* 原文件名
|
||||
*/
|
||||
private String name;
|
||||
/**
|
||||
* 路径,即文件名
|
||||
*/
|
||||
private String path;
|
||||
/**
|
||||
* 访问地址
|
||||
*/
|
||||
private String url;
|
||||
/**
|
||||
* 文件的 MIME 类型,例如 "application/octet-stream"
|
||||
*/
|
||||
private String type;
|
||||
/**
|
||||
* 文件大小
|
||||
*/
|
||||
private Integer size;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.tashow.cloud.infra.dal.dataobject.logger;
|
||||
|
||||
import com.tashow.cloud.common.enums.UserTypeEnum;
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.mybatis.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.tashow.cloud.web.apilog.core.enums.OperateTypeEnum;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* API 访问日志
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@TableName("infra_api_access_log")
|
||||
@KeySequence(value = "infra_api_access_log_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class ApiAccessLogDO extends BaseDO {
|
||||
|
||||
/**
|
||||
* {@link #requestParams} 的最大长度
|
||||
*/
|
||||
public static final Integer REQUEST_PARAMS_MAX_LENGTH = 8000;
|
||||
|
||||
/**
|
||||
* {@link #resultMsg} 的最大长度
|
||||
*/
|
||||
public static final Integer RESULT_MSG_MAX_LENGTH = 512;
|
||||
|
||||
/**
|
||||
* 编号
|
||||
*/
|
||||
@TableId
|
||||
private Long id;
|
||||
/**
|
||||
* 链路追踪编号
|
||||
*
|
||||
* 一般来说,通过链路追踪编号,可以将访问日志,错误日志,链路追踪日志,logger 打印日志等,结合在一起,从而进行排错。
|
||||
*/
|
||||
private String traceId;
|
||||
/**
|
||||
* 用户编号
|
||||
*/
|
||||
private Long userId;
|
||||
/**
|
||||
* 用户类型
|
||||
*
|
||||
* 枚举 {@link UserTypeEnum}
|
||||
*/
|
||||
private Integer userType;
|
||||
/**
|
||||
* 应用名
|
||||
*
|
||||
* 目前读取 `spring.application.name` 配置项
|
||||
*/
|
||||
private String applicationName;
|
||||
|
||||
// ========== 请求相关字段 ==========
|
||||
|
||||
/**
|
||||
* 请求方法名
|
||||
*/
|
||||
private String requestMethod;
|
||||
/**
|
||||
* 访问地址
|
||||
*/
|
||||
private String requestUrl;
|
||||
/**
|
||||
* 请求参数
|
||||
*
|
||||
* query: Query String
|
||||
* body: Quest Body
|
||||
*/
|
||||
private String requestParams;
|
||||
/**
|
||||
* 响应结果
|
||||
*/
|
||||
private String responseBody;
|
||||
/**
|
||||
* 用户 IP
|
||||
*/
|
||||
private String userIp;
|
||||
/**
|
||||
* 浏览器 UA
|
||||
*/
|
||||
private String userAgent;
|
||||
|
||||
// ========== 执行相关字段 ==========
|
||||
|
||||
/**
|
||||
* 操作模块
|
||||
*/
|
||||
private String operateModule;
|
||||
/**
|
||||
* 操作名
|
||||
*/
|
||||
private String operateName;
|
||||
/**
|
||||
* 操作分类
|
||||
*
|
||||
* 枚举 {@link OperateTypeEnum}
|
||||
*/
|
||||
private Integer operateType;
|
||||
|
||||
/**
|
||||
* 开始请求时间
|
||||
*/
|
||||
private LocalDateTime beginTime;
|
||||
/**
|
||||
* 结束请求时间
|
||||
*/
|
||||
private LocalDateTime endTime;
|
||||
/**
|
||||
* 执行时长,单位:毫秒
|
||||
*/
|
||||
private Integer duration;
|
||||
|
||||
/**
|
||||
* 结果码
|
||||
*
|
||||
* 目前使用的 {@link CommonResult#getCode()} 属性
|
||||
*/
|
||||
private Integer resultCode;
|
||||
/**
|
||||
* 结果提示
|
||||
*
|
||||
* 目前使用的 {@link CommonResult#getMsg()} 属性
|
||||
*/
|
||||
private String resultMsg;
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user