diff --git a/INSTALL.md b/INSTALL.md index 22ab8bd..08038af 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -256,6 +256,10 @@ docker exec -it redis redis-cli ![](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/a532e3221834466a85b6739871694957~tplv-k3u1fbpfcp-watermark.image?) +注:我的配置里更改过端口,所以我的程序`AustinApplication`写的端口为7000 + +![](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/1b3944f3a9e849219c60e673baa5b7ff~tplv-k3u1fbpfcp-watermark.image?) + **** **** diff --git a/austin-common/src/main/java/com/java3y/austin/common/constant/AustinConstant.java b/austin-common/src/main/java/com/java3y/austin/common/constant/AustinConstant.java index 3123d46..a36dd86 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/constant/AustinConstant.java +++ b/austin-common/src/main/java/com/java3y/austin/common/constant/AustinConstant.java @@ -15,12 +15,6 @@ public class AustinConstant { public final static Integer FALSE = 0; - - /** - * 时间格式 - */ - public final static String YYYY_MM_DD = "yyyyMMdd"; - /** * cron时间格式 */ diff --git a/austin-stream/src/main/java/com/java3y/austin/stream/domain/SimpleAnchorInfo.java b/austin-common/src/main/java/com/java3y/austin/common/domain/SimpleAnchorInfo.java similarity index 92% rename from austin-stream/src/main/java/com/java3y/austin/stream/domain/SimpleAnchorInfo.java rename to austin-common/src/main/java/com/java3y/austin/common/domain/SimpleAnchorInfo.java index ce51d4b..4faf775 100644 --- a/austin-stream/src/main/java/com/java3y/austin/stream/domain/SimpleAnchorInfo.java +++ b/austin-common/src/main/java/com/java3y/austin/common/domain/SimpleAnchorInfo.java @@ -1,4 +1,4 @@ -package com.java3y.austin.stream.domain; +package com.java3y.austin.common.domain; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/austin-common/src/main/java/com/java3y/austin/common/enums/AnchorState.java b/austin-common/src/main/java/com/java3y/austin/common/enums/AnchorState.java index 8722b34..0ef1e3e 100644 --- a/austin-common/src/main/java/com/java3y/austin/common/enums/AnchorState.java +++ b/austin-common/src/main/java/com/java3y/austin/common/enums/AnchorState.java @@ -15,7 +15,7 @@ import lombok.ToString; @AllArgsConstructor public enum AnchorState { - RECEIVE(10, "成功消费Kafka"), + RECEIVE(10, "消息接收成功"), DISCARD(20, "消费被丢弃"), CONTENT_DEDUPLICATION(30, "消息被内容去重"), RULE_DEDUPLICATION(40, "消息被频次去重"), @@ -30,4 +30,19 @@ public enum AnchorState { private Integer code; private String description; + /** + * 通过code获取描述 + * + * @param code + * @return + */ + public static String getDescriptionByCode(Integer code) { + for (AnchorState anchorState : AnchorState.values()) { + if (anchorState.getCode().equals(code)) { + return anchorState.getDescription(); + } + } + return "未知点位"; + } + } diff --git a/austin-handler/src/main/java/com/java3y/austin/handler/script/TencentSmsScript.java b/austin-handler/src/main/java/com/java3y/austin/handler/script/TencentSmsScript.java index 64e40cd..6d16c2d 100644 --- a/austin-handler/src/main/java/com/java3y/austin/handler/script/TencentSmsScript.java +++ b/austin-handler/src/main/java/com/java3y/austin/handler/script/TencentSmsScript.java @@ -1,9 +1,9 @@ package com.java3y.austin.handler.script; +import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.IdUtil; -import com.java3y.austin.common.constant.AustinConstant; import com.java3y.austin.common.enums.SmsStatus; import com.java3y.austin.handler.domain.SmsParam; import com.java3y.austin.handler.domain.TencentSmsParam; @@ -66,7 +66,7 @@ public class TencentSmsScript implements SmsScript { .reverse().substring(0, PHONE_NUM)).reverse().toString(); SmsRecord smsRecord = SmsRecord.builder() - .sendDate(Integer.valueOf(DateUtil.format(new Date(), AustinConstant.YYYY_MM_DD))) + .sendDate(Integer.valueOf(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN))) .messageTemplateId(smsParam.getMessageTemplateId()) .phone(Long.valueOf(phone)) .supplierId(tencentSmsParam.getSupplierId()) diff --git a/austin-stream/src/main/java/com/java3y/austin/stream/sink/AustinSink.java b/austin-stream/src/main/java/com/java3y/austin/stream/sink/AustinSink.java index ac19156..cd5bd8e 100644 --- a/austin-stream/src/main/java/com/java3y/austin/stream/sink/AustinSink.java +++ b/austin-stream/src/main/java/com/java3y/austin/stream/sink/AustinSink.java @@ -4,7 +4,7 @@ import cn.hutool.core.date.DateUtil; import com.alibaba.fastjson.JSON; import com.google.common.base.Throwables; import com.java3y.austin.common.domain.AnchorInfo; -import com.java3y.austin.stream.domain.SimpleAnchorInfo; +import com.java3y.austin.common.domain.SimpleAnchorInfo; import com.java3y.austin.stream.utils.LettuceRedisUtils; import io.lettuce.core.RedisFuture; import lombok.extern.slf4j.Slf4j; @@ -56,7 +56,7 @@ public class AustinSink implements SinkFunction { */ redisFutures.add(redisAsyncCommands.hincrby(String.valueOf(info.getBusinessId()).getBytes(), String.valueOf(info.getState()).getBytes(), info.getIds().size())); - redisFutures.add(redisAsyncCommands.expire(String.valueOf(info.getBusinessId()).getBytes(), DateUtil.offsetDay(new Date(), 30).getTime())); + redisFutures.add(redisAsyncCommands.expire(String.valueOf(info.getBusinessId()).getBytes(), (DateUtil.offsetDay(new Date(), 30).getTime())/ 1000)); return redisFutures; }); diff --git a/austin-support/src/main/java/com/java3y/austin/support/utils/TaskInfoUtils.java b/austin-support/src/main/java/com/java3y/austin/support/utils/TaskInfoUtils.java index 3a71e3f..d3d9272 100644 --- a/austin-support/src/main/java/com/java3y/austin/support/utils/TaskInfoUtils.java +++ b/austin-support/src/main/java/com/java3y/austin/support/utils/TaskInfoUtils.java @@ -1,7 +1,7 @@ package com.java3y.austin.support.utils; +import cn.hutool.core.date.DatePattern; import cn.hutool.core.date.DateUtil; -import com.java3y.austin.common.constant.AustinConstant; import java.util.Date; @@ -21,10 +21,24 @@ public class TaskInfoUtils { * (固定16位) */ public static Long generateBusinessId(Long templateId, Integer templateType) { - Integer today = Integer.valueOf(DateUtil.format(new Date(), AustinConstant.YYYY_MM_DD)); + Integer today = Integer.valueOf(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN)); return Long.valueOf(String.format("%d%s", templateType * TYPE_FLAG + templateId, today)); } + /** + * 第二到8位为MessageTemplateId 切割出模板ID + */ + public static Long getMessageTemplateIdFromBusinessId(Long businessId) { + return Long.valueOf(String.valueOf(businessId).substring(1, 8)); + } + /** + * 从businessId切割出日期 + */ + public static Long getDateFromBusinessId(Long businessId) { + return Long.valueOf(String.valueOf(businessId).substring(8)); + } + + /** * 对url添加平台参数(用于追踪数据) */ diff --git a/austin-web/src/main/java/com/java3y/austin/web/constants/AmisVoConstant.java b/austin-web/src/main/java/com/java3y/austin/web/constants/AmisVoConstant.java new file mode 100644 index 0000000..564d454 --- /dev/null +++ b/austin-web/src/main/java/com/java3y/austin/web/constants/AmisVoConstant.java @@ -0,0 +1,12 @@ +package com.java3y.austin.web.constants; + +/** + * amis常量信息 + * @author 3y + */ +public class AmisVoConstant { + + public static final String LEGEND_TITLE = "人数"; + public static final String TYPE = "bar"; + +} diff --git a/austin-web/src/main/java/com/java3y/austin/web/controller/DataController.java b/austin-web/src/main/java/com/java3y/austin/web/controller/DataController.java index 2e8b2f4..12503b7 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/controller/DataController.java +++ b/austin-web/src/main/java/com/java3y/austin/web/controller/DataController.java @@ -2,20 +2,17 @@ package com.java3y.austin.web.controller; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSON; +import com.java3y.austin.common.enums.RespStatusEnum; import com.java3y.austin.common.vo.BasicResultVO; -import com.java3y.austin.support.utils.RedisUtils; import com.java3y.austin.web.service.DataService; +import com.java3y.austin.web.vo.DataParam; import com.java3y.austin.web.vo.amis.EchartsVo; +import com.java3y.austin.web.vo.amis.UserTimeLineVo; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; +import org.springframework.web.bind.annotation.*; /** * 获取数据接口(全链路追踪) @@ -28,29 +25,25 @@ import java.util.List; @Api("获取数据接口(全链路追踪)") @CrossOrigin(origins = "http://localhost:3000", allowCredentials = "true", allowedHeaders = "*") public class DataController { - - @Autowired - private RedisUtils redisUtils; - @Autowired private DataService dataService; - @GetMapping("/user") + @PostMapping("/user") @ApiOperation("/获取【当天】用户接收消息的全链路数据") - public BasicResultVO getUserData(String receiver) { - List list = redisUtils.lRange(receiver, 0, -1); - // log.info("data:{}", JSON.toJSONString(objectObjectMap)); - return BasicResultVO.success(); + public BasicResultVO getUserData(@RequestBody DataParam dataParam) { + UserTimeLineVo traceUserInfo = dataService.getTraceUserInfo(dataParam.getReceiver()); + + return BasicResultVO.success(traceUserInfo); } - @GetMapping("/messageTemplate") + @PostMapping("/messageTemplate") @ApiOperation("/获取消息模板全链路数据") - public BasicResultVO getMessageTemplateData(String businessId) { + public BasicResultVO getMessageTemplateData(@RequestBody DataParam dataParam) { EchartsVo echartsVo = EchartsVo.builder().build(); - if (StrUtil.isNotBlank(businessId)) { - echartsVo = dataService.getTraceMessageTemplateInfo(businessId); + if (StrUtil.isNotBlank(dataParam.getBusinessId())) { + echartsVo = dataService.getTraceMessageTemplateInfo(dataParam.getBusinessId()); } - return BasicResultVO.success(echartsVo); + return new BasicResultVO<>(RespStatusEnum.SUCCESS, echartsVo); } public static void main(String[] args) { diff --git a/austin-web/src/main/java/com/java3y/austin/web/service/DataService.java b/austin-web/src/main/java/com/java3y/austin/web/service/DataService.java index d6f7784..86e1e8e 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/service/DataService.java +++ b/austin-web/src/main/java/com/java3y/austin/web/service/DataService.java @@ -1,7 +1,7 @@ package com.java3y.austin.web.service; import com.java3y.austin.web.vo.amis.EchartsVo; -import com.java3y.austin.web.vo.amis.TimeLineItemVo; +import com.java3y.austin.web.vo.amis.UserTimeLineVo; /** * 数据链路追踪获取接口 @@ -12,12 +12,18 @@ public interface DataService { /** * 获取全链路追踪 用户维度信息 + * + * @param receiver 接收者 + * @return */ - TimeLineItemVo getTraceUserInfo(String receiver); + UserTimeLineVo getTraceUserInfo(String receiver); /** * 获取全链路追踪 消息模板维度信息 + * + * @param businessId 业务ID(如果传入消息模板ID,则生成当天的业务ID) + * @return */ EchartsVo getTraceMessageTemplateInfo(String businessId); diff --git a/austin-web/src/main/java/com/java3y/austin/web/service/impl/DataServiceImpl.java b/austin-web/src/main/java/com/java3y/austin/web/service/impl/DataServiceImpl.java index e9fd11f..688a650 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/service/impl/DataServiceImpl.java +++ b/austin-web/src/main/java/com/java3y/austin/web/service/impl/DataServiceImpl.java @@ -1,19 +1,28 @@ package com.java3y.austin.web.service.impl; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.StrPool; +import cn.hutool.core.util.StrUtil; +import com.alibaba.fastjson.JSON; import com.java3y.austin.common.constant.AustinConstant; +import com.java3y.austin.common.domain.SimpleAnchorInfo; import com.java3y.austin.common.enums.AnchorState; +import com.java3y.austin.common.enums.ChannelType; import com.java3y.austin.support.dao.MessageTemplateDao; import com.java3y.austin.support.domain.MessageTemplate; import com.java3y.austin.support.utils.RedisUtils; import com.java3y.austin.support.utils.TaskInfoUtils; +import com.java3y.austin.web.constants.AmisVoConstant; import com.java3y.austin.web.service.DataService; import com.java3y.austin.web.vo.amis.EchartsVo; -import com.java3y.austin.web.vo.amis.TimeLineItemVo; +import com.java3y.austin.web.vo.amis.UserTimeLineVo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.stream.Collectors; /** @@ -31,23 +40,98 @@ public class DataServiceImpl implements DataService { private MessageTemplateDao messageTemplateDao; @Override - public TimeLineItemVo getTraceUserInfo(String receiver) { - return null; + public UserTimeLineVo getTraceUserInfo(String receiver) { + List userInfoList = redisUtils.lRange(receiver, 0, -1); + if (CollUtil.isEmpty(userInfoList)) { + return UserTimeLineVo.builder().items(new ArrayList<>()).build(); + } + + // 0. 按时间排序 + List sortAnchorList = userInfoList.stream().map(s -> JSON.parseObject(s, SimpleAnchorInfo.class)).sorted((o1, o2) -> Math.toIntExact(o1.getTimestamp() - o2.getTimestamp())).collect(Collectors.toList()); + + // 1. 对相同的businessId进行分类 {"businessId":[{businessId,state,timeStamp},{businessId,state,timeStamp}]} + Map> map = new HashMap<>(); + for (SimpleAnchorInfo simpleAnchorInfo : sortAnchorList) { + List simpleAnchorInfos = map.get(String.valueOf(simpleAnchorInfo.getBusinessId())); + if (CollUtil.isEmpty(simpleAnchorInfos)) { + simpleAnchorInfos = new ArrayList<>(); + } + simpleAnchorInfos.add(simpleAnchorInfo); + map.put(String.valueOf(simpleAnchorInfo.getBusinessId()), simpleAnchorInfos); + } + + // 2. 封装vo 给到前端渲染展示 + List items = new ArrayList<>(); + for (Map.Entry> entry : map.entrySet()) { + Long messageTemplateId = TaskInfoUtils.getMessageTemplateIdFromBusinessId(Long.valueOf(entry.getKey())); + MessageTemplate messageTemplate = messageTemplateDao.findById(messageTemplateId).get(); + + StringBuilder sb = new StringBuilder(); + for (SimpleAnchorInfo simpleAnchorInfo : entry.getValue()) { + if (AnchorState.RECEIVE.getCode().equals(simpleAnchorInfo.getState())) { + sb.append(StrPool.CRLF); + } + String startTime = DateUtil.format(new Date(simpleAnchorInfo.getTimestamp()), DatePattern.NORM_DATETIME_PATTERN); + String stateDescription = AnchorState.getDescriptionByCode(simpleAnchorInfo.getState()); + sb.append(startTime).append(StrPool.C_COLON).append(stateDescription).append("==>"); + } + + for (String detail : sb.toString().split(StrPool.CRLF)) { + if (StrUtil.isNotBlank(detail)) { + UserTimeLineVo.ItemsVO itemsVO = UserTimeLineVo.ItemsVO.builder() + .businessId(entry.getKey()) + .sendType(ChannelType.getEnumByCode(messageTemplate.getSendChannel()).getDescription()) + .creator(messageTemplate.getCreator()) + .title(messageTemplate.getName()) + .detail(detail) + .build(); + items.add(itemsVO); + } + } + } + return UserTimeLineVo.builder().items(items).build(); } @Override public EchartsVo getTraceMessageTemplateInfo(String businessId) { + + // 1. 获取businessId并获取模板信息 + businessId = getRealBusinessId(businessId); + Optional optional = messageTemplateDao.findById(TaskInfoUtils.getMessageTemplateIdFromBusinessId(Long.valueOf(businessId))); + if (!optional.isPresent()) { + return null; + } + MessageTemplate messageTemplate = optional.get(); + + List xAxisList = new ArrayList<>(); + List actualData = new ArrayList<>(); + /** * key:state * value:stateCount */ Map anchorResult = redisUtils.hGetAll(getRealBusinessId(businessId)); - List stateList = anchorResult.entrySet().stream().map(objectObjectEntry -> Integer.valueOf(String.valueOf(objectObjectEntry.getKey()))).collect(Collectors.toList()); - for (AnchorState value : AnchorState.values()) { - + if (CollUtil.isNotEmpty(anchorResult)) { + anchorResult = MapUtil.sort(anchorResult); + for (Map.Entry entry : anchorResult.entrySet()) { + String description = AnchorState.getDescriptionByCode(Integer.valueOf(String.valueOf(entry.getKey()))); + xAxisList.add(description); + actualData.add(Integer.valueOf(String.valueOf(entry.getValue()))); + } } - return null; + + String title = "【" + messageTemplate.getName() + "】在" + TaskInfoUtils.getDateFromBusinessId(Long.valueOf(businessId)) + "的下发情况:"; + + return EchartsVo.builder() + .title(EchartsVo.TitleVO.builder().text(title).build()) + .legend(EchartsVo.LegendVO.builder().data(Arrays.asList(AmisVoConstant.LEGEND_TITLE)).build()) + .xAxis(EchartsVo.XAxisVO.builder().data(xAxisList).build()) + .series(Arrays.asList(EchartsVo.SeriesVO.builder().name(AmisVoConstant.LEGEND_TITLE).type(AmisVoConstant.TYPE).data(actualData).build())) + .yAxis(EchartsVo.YAxisVO.builder().build()) + .tooltip(EchartsVo.TooltipVO.builder().build()) + .build(); + } /** @@ -65,5 +149,4 @@ public class DataServiceImpl implements DataService { } return businessId; } - } diff --git a/austin-web/src/main/java/com/java3y/austin/web/vo/DataParam.java b/austin-web/src/main/java/com/java3y/austin/web/vo/DataParam.java index ad316bb..1f3da93 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/vo/DataParam.java +++ b/austin-web/src/main/java/com/java3y/austin/web/vo/DataParam.java @@ -19,14 +19,15 @@ public class DataParam { /** * 传入userId查看用户的链路信息 */ - private String userId; + private String receiver; /** * 业务Id(数据追踪使用) * 生成逻辑参考 TaskInfoUtils + * 如果传入的是模板ID,则生成当天的业务ID */ - private Long businessId; + private String businessId; } diff --git a/austin-web/src/main/java/com/java3y/austin/web/vo/amis/EchartsVo.java b/austin-web/src/main/java/com/java3y/austin/web/vo/amis/EchartsVo.java index 4aaac0f..04b84ba 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/vo/amis/EchartsVo.java +++ b/austin-web/src/main/java/com/java3y/austin/web/vo/amis/EchartsVo.java @@ -1,5 +1,6 @@ package com.java3y.austin.web.vo.amis; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -21,28 +22,34 @@ public class EchartsVo { /** * title 标题 */ + @JsonProperty private TitleVO title; /** * tooltip 提示 */ + @JsonProperty private TooltipVO tooltip; /** * legend 图例 */ + @JsonProperty private LegendVO legend; /** * xAxis x轴 */ + @JsonProperty private XAxisVO xAxis; /** * yAxis y轴 */ + @JsonProperty private YAxisVO yAxis; /** * series 系列列表 *

* 每个系列通过 type 决定自己的图表类型 */ + @JsonProperty private List series; /** @@ -50,7 +57,6 @@ public class EchartsVo { */ @Data @Builder - public static class TitleVO { /** * text @@ -63,8 +69,8 @@ public class EchartsVo { */ @Data @Builder - public static class TooltipVO { + private String color; } /** @@ -72,7 +78,6 @@ public class EchartsVo { */ @Data @Builder - public static class LegendVO { /** * data @@ -98,8 +103,8 @@ public class EchartsVo { */ @Data @Builder - public static class YAxisVO { + private String type; } /** @@ -107,7 +112,6 @@ public class EchartsVo { */ @Data @Builder - public static class SeriesVO { /** * name diff --git a/austin-web/src/main/java/com/java3y/austin/web/vo/amis/TimeLineItemVo.java b/austin-web/src/main/java/com/java3y/austin/web/vo/amis/UserTimeLineVo.java similarity index 61% rename from austin-web/src/main/java/com/java3y/austin/web/vo/amis/TimeLineItemVo.java rename to austin-web/src/main/java/com/java3y/austin/web/vo/amis/UserTimeLineVo.java index 4c69122..219fc04 100644 --- a/austin-web/src/main/java/com/java3y/austin/web/vo/amis/TimeLineItemVo.java +++ b/austin-web/src/main/java/com/java3y/austin/web/vo/amis/UserTimeLineVo.java @@ -9,21 +9,18 @@ import lombok.NoArgsConstructor; import java.util.List; /** - * https://aisuda.bce.baidu.com/amis/zh-CN/components/timeline#timeline-item - * * @author 3y - * 时间线 Vo */ @Data @Builder @AllArgsConstructor @NoArgsConstructor -public class TimeLineItemVo { +public class UserTimeLineVo { /** * items */ - private List items; + private List items; /** * ItemsVO @@ -32,24 +29,27 @@ public class TimeLineItemVo { @Builder public static class ItemsVO { /** - * time + * 业务ID */ - private String time; + private String businessId; /** - * title + * title 模板名称 */ private String title; /** - * detail + * detail 发送细节 */ private String detail; + /** - * color + * 发送类型 */ - private String color; + private String sendType; + /** - * icon + * 模板创建者 */ - private String icon; + private String creator; + } }