diff --git a/modules/core/src/main/java/com/bytedesk/core/gray_release/GrayReleaseController.java b/modules/core/src/main/java/com/bytedesk/core/gray_release/GrayReleaseController.java new file mode 100644 index 0000000000..bcb3c34ef9 --- /dev/null +++ b/modules/core/src/main/java/com/bytedesk/core/gray_release/GrayReleaseController.java @@ -0,0 +1,140 @@ +/* + * @Author: jackning 270580156@qq.com + * @Date: 2025-03-07 11:40:35 + * @LastEditors: jackning 270580156@qq.com + * @LastEditTime: 2025-03-07 11:42:59 + * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk + * Please be aware of the BSL license restrictions before installing Bytedesk IM – + * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. + * Business Source License 1.1: https://github.com/Bytedesk/bytedesk/blob/main/LICENSE + * contact: 270580156@qq.com + * + * Copyright (c) 2025 by bytedesk.com, All Rights Reserved. + */ +package com.bytedesk.core.gray_release; + +import java.time.LocalDateTime; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * 灰度发布控制器 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/v1/gray-release") +public class GrayReleaseController { + + private final GrayReleaseService grayReleaseService; + private final GrayReleaseMetricsService metricsService; + + /** + * 初始化功能的灰度发布 + */ + @PostMapping("/features/{feature}/init") + public ResponseEntity initializeFeature(@PathVariable String feature) { + GrayReleaseFeature grayFeature = GrayReleaseFeature.getByCode(feature); + if (grayFeature == null) { + return ResponseEntity.badRequest().body("Invalid feature code"); + } + + grayReleaseService.initializeFeatureRollout(grayFeature); + return ResponseEntity.ok().build(); + } + + /** + * 调整灰度比例 + */ + @PutMapping("/features/{feature}/rollout") + public ResponseEntity adjustRollout( + @PathVariable String feature, + @RequestParam int percentage) { + + if (percentage < 0 || percentage > 100) { + return ResponseEntity.badRequest().body("Percentage must be between 0 and 100"); + } + + grayReleaseService.increaseRolloutPercentage(feature, percentage); + return ResponseEntity.ok().build(); + } + + /** + * 暂停灰度发布 + */ + @PostMapping("/features/{feature}/pause") + public ResponseEntity pauseRollout(@PathVariable String feature) { + grayReleaseService.pauseRollout(feature); + return ResponseEntity.ok().build(); + } + + /** + * 恢复灰度发布 + */ + @PostMapping("/features/{feature}/resume") + public ResponseEntity resumeRollout(@PathVariable String feature) { + grayReleaseService.resumeRollout(feature); + return ResponseEntity.ok().build(); + } + + /** + * 完成灰度发布 + */ + @PostMapping("/features/{feature}/complete") + public ResponseEntity completeRollout(@PathVariable String feature) { + grayReleaseService.completeRollout(feature); + return ResponseEntity.ok().build(); + } + + /** + * 添加白名单用户 + */ + @PostMapping("/features/{feature}/whitelist/{userUid}") + public ResponseEntity addToWhitelist( + @PathVariable String feature, + @PathVariable String userUid) { + grayReleaseService.addToWhitelist(feature, userUid); + return ResponseEntity.ok().build(); + } + + /** + * 获取功能状态 + */ + @GetMapping("/features/{feature}") + public ResponseEntity getFeatureStatus( + @PathVariable String feature) { + return ResponseEntity.ok(grayReleaseService.getFeatureStatus(feature)); + } + + /** + * 获取功能统计数据 + */ + @GetMapping("/features/{feature}/stats") + public ResponseEntity getStats( + @PathVariable String feature, + @RequestParam(required = false) Integer hours) { + + LocalDateTime end = LocalDateTime.now(); + LocalDateTime start = end.minusHours(hours != null ? hours : 24); + + GrayReleaseFeature grayFeature = GrayReleaseFeature.getByCode(feature); + if (grayFeature == null) { + return ResponseEntity.badRequest().body(null); + } + + GrayReleaseFeatureStatistics stats = metricsService.getFeatureStatistics( + grayFeature, start, end); + + return ResponseEntity.ok(stats); + } +} diff --git a/modules/core/src/main/java/com/bytedesk/core/gray_release/GrayReleaseService.java b/modules/core/src/main/java/com/bytedesk/core/gray_release/GrayReleaseService.java index 8e3aa9b448..6b1f6291bf 100644 --- a/modules/core/src/main/java/com/bytedesk/core/gray_release/GrayReleaseService.java +++ b/modules/core/src/main/java/com/bytedesk/core/gray_release/GrayReleaseService.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2025-03-07 11:07:19 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-03-07 11:11:41 + * @LastEditTime: 2025-03-07 11:41:29 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -25,9 +25,58 @@ import lombok.extern.slf4j.Slf4j; @RequiredArgsConstructor public class GrayReleaseService { - private final GrayReleaseRepository grayReleaseRepository; + // private final GrayReleaseRepository grayReleaseRepository; private final GrayReleaseMetricsService metricsService; + public void enableFeature(String userUid) { + // 检查用户是否在灰度范围内 + if (isUserInGrayRelease(userUid, + GrayReleaseFeature.PROACTIVE_TRIGGER.getCode())) { + // 启用功能 + // ... + } + } + + public void handleMessage(String userUid, String message) { + try { + // 处理消息 + // ... + + // 记录成功 + metricsService.recordFeatureUsage(userUid, + GrayReleaseFeature.PROACTIVE_TRIGGER, true); + } catch (Exception e) { + // 记录失败 + metricsService.recordFeatureUsage(userUid, + GrayReleaseFeature.PROACTIVE_TRIGGER, false); + throw e; + } + } + + + public void monitorFeature(GrayReleaseFeature feature) { + GrayReleaseFeatureStatistics stats = metricsService.getFeatureStatistics( + feature, + LocalDateTime.now().minusHours(24), + LocalDateTime.now() + ); + + if (stats.getFailureRate() > 0.1) { + // 发送告警 + // sendAlert("Feature " + feature.getCode() + + // " has high failure rate: " + stats.getFailureRate()); + } + } + + /** + * 检查用户是否在灰度范围内 + */ + public boolean isUserInGrayRelease(String userUid, String feature) { + // TODO: 实现检查逻辑 + return true; + } + + /** * 初始化功能的灰度发布 */ diff --git a/modules/service/src/main/java/com/bytedesk/service/utils/ThreadMessageUtil.java b/modules/service/src/main/java/com/bytedesk/service/utils/ThreadMessageUtil.java index e578d38824..2087fe51c5 100644 --- a/modules/service/src/main/java/com/bytedesk/service/utils/ThreadMessageUtil.java +++ b/modules/service/src/main/java/com/bytedesk/service/utils/ThreadMessageUtil.java @@ -2,7 +2,7 @@ * @Author: jackning 270580156@qq.com * @Date: 2024-08-29 22:22:38 * @LastEditors: jackning 270580156@qq.com - * @LastEditTime: 2025-03-07 11:08:02 + * @LastEditTime: 2025-03-07 11:36:07 * @Description: bytedesk.com https://github.com/Bytedesk/bytedesk * Please be aware of the BSL license restrictions before installing Bytedesk IM – * selling, reselling, or hosting Bytedesk IM as a service is a breach of the terms and automatically terminates your rights under the license. @@ -213,12 +213,20 @@ public class ThreadMessageUtil { return message; } + // 检查无响应触发 public static void checkNoResponse(String userUid, long lastActiveTime, ServiceSettings settings) { if (!settings.isEnableProactiveTrigger()) { return; } + // 检查用户是否在灰度范围内 + // if (grayReleaseService.isUserInGrayRelease(userUid, + // GrayReleaseFeature.PROACTIVE_TRIGGER.getCode())) { + // // 启用功能 + // // ... + // } + // 检查用户是否可以使用主动触发功能 if (!settings.getGrayReleaseConfig().isUserInGrayRelease(userUid, "proactive_trigger")) { return; @@ -238,6 +246,8 @@ public class ThreadMessageUtil { // TODO: 发送主动推送消息 // sendProactiveMessage(userId, condition.getMessage()); }); + + }