Merge branch 'feature/zijie' of http://gitea.tashowz.com/tashow/tashow-platform into feature/zijie
# Conflicts: # tashow-dependencies/pom.xml # tashow-module/pom.xml # tashow-module/tashow-module-system/tashow-module-system-biz/src/main/resources/application.yaml
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<modules>
|
||||
<module>tashow-module-system</module>
|
||||
<module>tashow-module-infra</module>
|
||||
<module>tashow-module-sso</module>
|
||||
<module>tashow-module-app</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
||||
68
tashow-module/tashow-module-app/pom.xml
Normal file
68
tashow-module/tashow-module-app/pom.xml
Normal file
@@ -0,0 +1,68 @@
|
||||
<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>
|
||||
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.tashow.cloud.app;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* Hello world!
|
||||
*
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class AppServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AppServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package com.tashow.cloud.app.controller;
|
||||
|
||||
public class LoginController {
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.tashow.cloud.app.controller;
|
||||
|
||||
import com.tashow.cloud.app.mq.annotation.BuriedPoint;
|
||||
import com.tashow.cloud.app.mq.mapper.BuriedPointMapper;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
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.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 测试控制器
|
||||
*/
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
public class TestController {
|
||||
|
||||
private final BuriedPointProducer buriedPointProducer;
|
||||
private final BuriedPointMapper buriedPointMapper;
|
||||
|
||||
/**
|
||||
* 基础测试接口
|
||||
*/
|
||||
@GetMapping("/test")
|
||||
@PermitAll
|
||||
public String test() {
|
||||
return "test";
|
||||
}
|
||||
|
||||
/**
|
||||
* 测试埋点拦截器
|
||||
* 这个接口会被埋点拦截器自动记录请求信息
|
||||
*/
|
||||
@GetMapping("/test/buried-point")
|
||||
@PermitAll
|
||||
public Map<String, Object> testBuriedPoint() {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("message", "埋点拦截器测试成功");
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,114 @@
|
||||
package com.tashow.cloud.app.mq.config;
|
||||
import com.tashow.cloud.app.mq.interceptor.BuriedPointInterceptor;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import com.tashow.cloud.app.mq.producer.buriedPoint.BuriedPointProducer;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.amqp.core.Binding;
|
||||
import org.springframework.amqp.core.BindingBuilder;
|
||||
import org.springframework.amqp.core.DirectExchange;
|
||||
import org.springframework.amqp.core.Queue;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
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;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* 埋点功能配置类
|
||||
*/
|
||||
@Slf4j
|
||||
@Configuration
|
||||
@RequiredArgsConstructor
|
||||
public class BuriedPointConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final BuriedPointProducer buriedPointProducer;
|
||||
private final RabbitTemplate rabbitTemplate;
|
||||
|
||||
/**
|
||||
* RabbitTemplate初始化配置
|
||||
* 确保回调正确配置,以实现消息可靠性
|
||||
*/
|
||||
// @PostConstruct
|
||||
// public void initRabbitTemplate() {
|
||||
// log.info("[埋点配置] 初始化RabbitTemplate: {}", rabbitTemplate);
|
||||
// rabbitTemplate.setMandatory(true);
|
||||
// rabbitTemplate.setReturnsCallback(returned -> {
|
||||
// log.error("[埋点配置] 消息路由失败: exchange={}, routingKey={}, replyCode={}, replyText={}, message={}",
|
||||
// returned.getExchange(),
|
||||
// returned.getRoutingKey(),
|
||||
// returned.getReplyCode(),
|
||||
// returned.getReplyText(),
|
||||
// new String(returned.getMessage().getBody()));
|
||||
// });
|
||||
// rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
|
||||
// if (ack) {
|
||||
// log.debug("[埋点配置] 消息成功发送到交换机: {}", correlationData);
|
||||
// } else {
|
||||
// log.error("[埋点配置] 消息发送到交换机失败: cause={}, correlationData={}", cause, correlationData);
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// // 验证配置
|
||||
// if (rabbitTemplate.isConfirmListener()) {
|
||||
// log.info("[埋点配置] 确认回调已正确配置");
|
||||
// } else {
|
||||
// log.error("[埋点配置] 确认回调配置失败,请检查RabbitMQ配置!");
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 创建埋点队列
|
||||
*/
|
||||
@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/**",
|
||||
"/error"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
package com.tashow.cloud.app.mq.consumer.buriedPoint;
|
||||
import com.tashow.cloud.app.mq.mapper.BuriedPointMapper;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import com.tashow.cloud.app.mq.model.BuriedPoint;
|
||||
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 org.springframework.util.StringUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import com.rabbitmq.client.Channel;
|
||||
*/
|
||||
/**
|
||||
* 埋点消息消费者
|
||||
* 将埋点数据存储到数据库
|
||||
*//*
|
||||
|
||||
@Component
|
||||
@RabbitListener(queues = BuriedMessages.QUEUE)
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class BuriedPointConsumer {
|
||||
|
||||
private final BuriedPointMapper buriedPointMapper;
|
||||
|
||||
@Value("${spring.application.name:tashow-app}")
|
||||
private String applicationName;
|
||||
|
||||
*/
|
||||
/**
|
||||
* 处理埋点消息
|
||||
*//*
|
||||
|
||||
@RabbitHandler
|
||||
public void onMessage(BuriedMessages message,
|
||||
Channel channel,
|
||||
@Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
|
||||
try {
|
||||
log.info("[埋点消费者] 收到埋点消息: {}", message);
|
||||
|
||||
// 确保事件ID不为空
|
||||
if (message.getId() == null) {
|
||||
message.setId((int)(System.currentTimeMillis() % Integer.MAX_VALUE));
|
||||
log.warn("[埋点消费者] 消息中的事件ID为空,已自动生成: {}", message.getId());
|
||||
}
|
||||
saveToDatabase(message);
|
||||
channel.basicAck(deliveryTag, false);
|
||||
log.info("[埋点消费者] 消息处理成功,已确认");
|
||||
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
channel.basicNack(deliveryTag, false, true);
|
||||
} catch (IOException ex) {
|
||||
log.error("[埋点消费者] 拒绝消息失败", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
*/
|
||||
/**
|
||||
* 将埋点数据保存到数据库
|
||||
*//*
|
||||
|
||||
private void saveToDatabase(BuriedMessages message) {
|
||||
try {
|
||||
log.debug("[埋点消费者] 准备保存埋点数据,事件ID: {}", message.getId());
|
||||
|
||||
// 转换消息为实体
|
||||
BuriedPoint buriedPoint = new BuriedPoint();
|
||||
|
||||
// 设置必填字段,确保不为空
|
||||
buriedPoint.setEventId(message.getId());
|
||||
buriedPoint.setEventTime(message.getEventTime());
|
||||
|
||||
// 获取真实用户ID,避免使用默认anonymous
|
||||
String userId = message.getUserId();
|
||||
buriedPoint.setUserId(StringUtils.hasText(userId) && !"null".equals(userId) ? userId : "anonymous");
|
||||
|
||||
String eventType = message.getEventType();
|
||||
buriedPoint.setEventType(eventType);
|
||||
buriedPoint.setService(applicationName);
|
||||
|
||||
// 设置method字段,确保获取真实方法名
|
||||
buriedPoint.setMethod(message.getMethod());
|
||||
buriedPoint.setSessionId(message.getSessionId());
|
||||
buriedPoint.setClientIp(message.getClientIp());
|
||||
buriedPoint.setServerIp(message.getServerIp());
|
||||
|
||||
// 设置其他字段
|
||||
buriedPoint.setPagePath(message.getPagePath());
|
||||
buriedPoint.setElementId(message.getElementId());
|
||||
buriedPoint.setDuration(message.getDuration());
|
||||
|
||||
buriedPoint.setCreateTime(new Date());
|
||||
|
||||
log.debug("[埋点消费者] 埋点实体数据: eventId={}, eventType={}, userId={}, service={}, method={}",
|
||||
buriedPoint.getEventId(), buriedPoint.getEventType(),
|
||||
buriedPoint.getUserId(), buriedPoint.getService(), buriedPoint.getMethod());
|
||||
|
||||
int result = buriedPointMapper.insert(buriedPoint);
|
||||
|
||||
log.info("[埋点消费者] 埋点数据已保存到数据库, 事件ID: {}, 影响行数: {}", message.getId(), result);
|
||||
} catch (Exception e) {
|
||||
log.error("[埋点消费者] 保存埋点数据到数据库失败, 事件ID: {}, 错误: {}",
|
||||
message.getId(), e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
} */
|
||||
@@ -0,0 +1,103 @@
|
||||
package com.tashow.cloud.app.mq.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;
|
||||
|
||||
private static final String ATTRIBUTE_STOPWATCH = "BuriedPoint.StopWatch";
|
||||
private static final String ATTRIBUTE_REQUEST_ID = "BuriedPoint.RequestId";
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
|
||||
if (!(handler instanceof HandlerMethod)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// 开始计时
|
||||
StopWatch stopWatch = new StopWatch();
|
||||
stopWatch.start();
|
||||
request.setAttribute(ATTRIBUTE_STOPWATCH, stopWatch);
|
||||
|
||||
// 生成请求ID
|
||||
int requestId = (int)(Math.abs(IdUtil.getSnowflakeNextId()) % Integer.MAX_VALUE);
|
||||
request.setAttribute(ATTRIBUTE_REQUEST_ID, requestId);
|
||||
|
||||
// 收集埋点数据
|
||||
HandlerMethod handlerMethod = (HandlerMethod) handler;
|
||||
String method = request.getMethod() + " " + request.getRequestURI()+ JsonUtils.toJsonString(request.getParameterMap());
|
||||
String controllerName = handlerMethod.getBeanType().getSimpleName();
|
||||
String actionName = handlerMethod.getMethod().getName();
|
||||
|
||||
// 创建埋点消息
|
||||
BuriedMessages message = new BuriedMessages();
|
||||
message.setId(requestId);
|
||||
message.setEventTime(System.currentTimeMillis());
|
||||
message.setService(SpringUtils.getApplicationName());
|
||||
message.setMethod(method);
|
||||
message.setUserId(getUserId(request));
|
||||
message.setSessionId(request.getSession().getId());
|
||||
message.setClientIp(ServletUtils.getClientIP(request));
|
||||
message.setServerIp(getServerIp());
|
||||
message.setEventType("API_REQUEST_START");
|
||||
message.setPagePath(controllerName + "#" + actionName);
|
||||
message.setUserAgent(request.getHeader("User-Agent"));
|
||||
message.setStatusCode(BuriedMessages.STATUS_INIT);
|
||||
buriedPointProducer.asyncSendMessage(message);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("[埋点] 收集请求开始数据: {}", message);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("[埋点] 埋点数据收集异常", e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录用户ID
|
||||
* 如果未登录返回匿名标识
|
||||
*/
|
||||
private String getUserId(HttpServletRequest request) {
|
||||
Object userAttribute = request.getSession().getAttribute("USER_ID");
|
||||
if (userAttribute != null) {
|
||||
return userAttribute.toString();
|
||||
}
|
||||
// 返回匿名用户标识
|
||||
return "anonymous";
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取服务器IP
|
||||
*/
|
||||
private String getServerIp() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.tashow.cloud.app.mq.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.tashow.cloud.app.mq.model.BuriedPoint;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
|
||||
@Mapper
|
||||
public interface BuriedPointMapper extends BaseMapper<BuriedPoint> {
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.tashow.cloud.app.mq.message;
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Data
|
||||
public class BuriedMessages implements Serializable {
|
||||
|
||||
// 消息队列配置
|
||||
public static final String QUEUE = "BURIED_POINT_QUEUE";
|
||||
public static final String EXCHANGE = "BURIED_POINT_EXCHANGE";
|
||||
public static final String ROUTING_KEY = "BURIED_POINT_ROUTING_KEY";
|
||||
public static final String DEAD_LETTER_EXCHANGE = "DEAD_LETTER_EXCHANGE";
|
||||
public static final String DEAD_LETTER_ROUTING_KEY = "DEAD_LETTER_ROUTING";
|
||||
public static final String DEAD_LETTER_QUEUE = "DEAD_LETTER_QUEUE";
|
||||
|
||||
// 状态码定义
|
||||
public static final Integer STATUS_INIT = 10; // 初始状态
|
||||
public static final Integer STATUS_PROCESSING = 20; // 处理中
|
||||
public static final Integer STATUS_SUCCESS = 30; // 处理成功
|
||||
public static final Integer STATUS_WARNING = 40; // 处理警告
|
||||
public static final Integer STATUS_ERROR = 50; // 处理错误
|
||||
|
||||
// 通用字段
|
||||
private Integer id; // 事件唯一ID
|
||||
private Long eventTime; // 事件时间戳
|
||||
private String service; // 服务名称
|
||||
private String method; // 方法/接口
|
||||
private String userId; // 用户标识
|
||||
private String sessionId; // 会话标识
|
||||
private String clientIp; // 客户端IP
|
||||
private String serverIp; // 服务器IP
|
||||
|
||||
// 添加埋点特定字段
|
||||
private String eventType; // 事件类型: PAGE_VIEW, API_CALL, BUTTON_CLICK 等
|
||||
private String pagePath; // 页面路径/功能模块
|
||||
private String elementId; // 元素标识
|
||||
private Long duration; // 操作时长(毫秒)
|
||||
private String deviceInfo; // 设备信息
|
||||
private String userAgent; // 用户代理信息
|
||||
private Integer statusCode; // 响应状态码
|
||||
private String errorMessage; // 错误信息
|
||||
|
||||
// 扩展字段,用于存储特定事件的额外数据
|
||||
private Map<String, Object> extraData = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 快速创建消息的便捷方法
|
||||
*/
|
||||
public static BuriedMessages create(String userId, String eventType, String pagePath) {
|
||||
BuriedMessages msg = new BuriedMessages();
|
||||
msg.setUserId(userId);
|
||||
msg.setEventType(eventType);
|
||||
msg.setPagePath(pagePath);
|
||||
msg.setEventTime(System.currentTimeMillis());
|
||||
msg.setStatusCode(STATUS_INIT); // 默认初始状态
|
||||
return msg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加扩展数据
|
||||
*/
|
||||
public BuriedMessages addExtraData(String key, Object value) {
|
||||
if (this.extraData == null) {
|
||||
this.extraData = new HashMap<>();
|
||||
}
|
||||
this.extraData.put(key, value);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package com.tashow.cloud.app.mq.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 lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 埋点数据实体类
|
||||
*/
|
||||
@Data
|
||||
@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;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.tashow.cloud.app.mq.producer.buriedPoint;
|
||||
import com.tashow.cloud.app.mq.message.BuriedMessages;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.amqp.rabbit.connection.CorrelationData;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 埋点消息生产者
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class BuriedPointProducer {
|
||||
|
||||
@Autowired
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
/**
|
||||
* 异步发送完整的埋点消息,并确保消息已被broker接收
|
||||
*/
|
||||
@SneakyThrows
|
||||
public void asyncSendMessage(BuriedMessages message) {
|
||||
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
|
||||
// final CompletableFuture<Boolean> confirmFuture = new CompletableFuture<>();
|
||||
log.info("[埋点] 异步准备发送消息: {}", message);
|
||||
correlationData.getFuture().whenComplete((confirm, ex) -> {
|
||||
log.info("[埋点] 异步消息发送确认回调: {}", message);
|
||||
if (ex != null) {
|
||||
log.error("[埋点] 异步消息发送异常: {}", ex.getMessage(), ex);
|
||||
// confirmFuture.completeExceptionally(ex);
|
||||
} else if (confirm != null && confirm.isAck()) {
|
||||
log.info("[埋点] 异步消息发送成功: {}", message);
|
||||
// confirmFuture.complete(true);
|
||||
} else {
|
||||
log.warn("[埋点] 异步消息发送未被ACK");
|
||||
//confirmFuture.complete(false);
|
||||
}
|
||||
});
|
||||
rabbitTemplate.convertAndSend(BuriedMessages.EXCHANGE, BuriedMessages.ROUTING_KEY, message, correlationData);
|
||||
log.info("[埋点] 异步消息发送完成: {}", message);
|
||||
// return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
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) {
|
||||
// Swagger 接口文档
|
||||
registry.requestMatchers("/v3/api-docs/**").permitAll()
|
||||
.requestMatchers("/webjars/**").permitAll()
|
||||
.requestMatchers("/swagger-ui").permitAll()
|
||||
.requestMatchers("/swagger-ui/**").permitAll();
|
||||
// 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;
|
||||
@@ -7,10 +7,10 @@ spring:
|
||||
username: nacos # Nacos 账号
|
||||
password: nacos # Nacos 密码
|
||||
discovery: # 【配置中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
namespace: liwq # 命名空间。这里使用 dev 开发环境
|
||||
group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP
|
||||
metadata:
|
||||
version: 1.0.0 # 服务实例的版本号,可用于灰度发布
|
||||
config: # 【注册中心】配置项
|
||||
namespace: dev # 命名空间。这里使用 dev 开发环境
|
||||
namespace: liwq # 命名空间。这里使用 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】的配置
|
||||
@@ -8,18 +8,157 @@
|
||||
<artifactId>tashow-module</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modules>
|
||||
<module>tashow-module-infra-api</module>
|
||||
<module>tashow-module-infra-biz</module>
|
||||
</modules>
|
||||
<artifactId>tashow-module-infra</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
infra 模块,主要提供两块能力:
|
||||
1. 我们放基础设施的运维与管理,支撑上层的通用与核心业务。 例如说:定时任务的管理、服务器的信息等等
|
||||
2. 研发工具,提升研发效率与质量。 例如说:代码生成器、接口文档等等
|
||||
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.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>
|
||||
|
||||
<!-- 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>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.tashow.cloud.infra.api.websocket;
|
||||
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.tashow.cloud.common.pojo.CommonResult;
|
||||
import com.tashow.cloud.infraapi.api.websocket.WebSocketSenderApi;
|
||||
import com.tashow.cloud.infraapi.api.websocket.dto.WebSocketSendReqDTO;
|
||||
@@ -15,22 +14,22 @@ 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());
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user