From 8ac2a37e7f75f6c64cb20a4a3fdd38b623535d71 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Fri, 19 Jun 2026 00:40:58 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20bugcollect=20controller=20=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E4=BB=8E=20/bugcollect/v1=20=E6=94=B9=E4=B8=BA=20/api?= =?UTF-8?q?/bugcollect/v1=EF=BC=8C=E5=8C=B9=E9=85=8D=20nginx=20=E8=B7=AF?= =?UTF-8?q?=E7=94=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../security/AppSignatureAuthFilter.java | 111 ++++++++++++++++++ .../common/security/ConfigFileCrypto.java | 6 +- .../com/xuqm/tenant/service/AppService.java | 1 + .../controller/AppVersionController.java | 42 ++++++- .../UpdateStatisticsController.java | 59 ++++++++++ .../update/entity/DeviceRecordEntity.java | 77 ++++++++++++ .../repository/DeviceRecordRepository.java | 78 ++++++++++++ .../service/UpdateStatisticsService.java | 106 +++++++++++++++++ .../db/migration/V2__add_device_record.sql | 15 +++ .../bugcollect/controller/LogController.java | 2 +- 10 files changed, 493 insertions(+), 4 deletions(-) create mode 100644 common/src/main/java/com/xuqm/common/security/AppSignatureAuthFilter.java create mode 100644 update-service/src/main/java/com/xuqm/update/controller/UpdateStatisticsController.java create mode 100644 update-service/src/main/java/com/xuqm/update/entity/DeviceRecordEntity.java create mode 100644 update-service/src/main/java/com/xuqm/update/repository/DeviceRecordRepository.java create mode 100644 update-service/src/main/java/com/xuqm/update/service/UpdateStatisticsService.java create mode 100644 update-service/src/main/resources/db/migration/V2__add_device_record.sql diff --git a/common/src/main/java/com/xuqm/common/security/AppSignatureAuthFilter.java b/common/src/main/java/com/xuqm/common/security/AppSignatureAuthFilter.java new file mode 100644 index 0000000..cec74c3 --- /dev/null +++ b/common/src/main/java/com/xuqm/common/security/AppSignatureAuthFilter.java @@ -0,0 +1,111 @@ +package com.xuqm.common.security; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; +import java.time.Instant; + +/** + * 验证 SDK 请求的 HMAC-SHA256 签名。 + *

+ * 请求头: + *

+ *

+ * 签名载荷格式:{@code appKey\nuserId\ntimestamp\nnonce} + */ +public class AppSignatureAuthFilter extends OncePerRequestFilter { + + private static final Logger log = LoggerFactory.getLogger(AppSignatureAuthFilter.class); + private static final long MAX_TIMESTAMP_DRIFT_SECONDS = 300; // ±5 分钟 + + private final AppSecretResolver secretResolver; + + public AppSignatureAuthFilter(AppSecretResolver secretResolver) { + this.secretResolver = secretResolver; + } + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) throws ServletException, IOException { + String appKey = request.getHeader("X-App-Key"); + String timestampStr = request.getHeader("X-Timestamp"); + String nonce = request.getHeader("X-Nonce"); + String signature = request.getHeader("X-Signature"); + + // 如果没有签名头,跳过验证(交给后续 Filter 处理) + if (appKey == null || signature == null) { + filterChain.doFilter(request, response); + return; + } + + // 验证时间戳 + if (timestampStr == null || timestampStr.isBlank()) { + log.warn("Missing X-Timestamp header for appKey={}", appKey); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Missing timestamp"); + return; + } + + long timestamp; + try { + timestamp = Long.parseLong(timestampStr); + } catch (NumberFormatException e) { + log.warn("Invalid X-Timestamp header: {} for appKey={}", timestampStr, appKey); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid timestamp"); + return; + } + + long now = Instant.now().getEpochSecond(); + if (Math.abs(now - timestamp) > MAX_TIMESTAMP_DRIFT_SECONDS) { + log.warn("Timestamp drift too large: now={}, request={}, appKey={}", now, timestamp, appKey); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Timestamp expired"); + return; + } + + // 解析 appSecret + String appSecret; + try { + appSecret = secretResolver.resolveAppSecret(appKey); + } catch (Exception e) { + log.warn("Failed to resolve appSecret for appKey={}: {}", appKey, e.getMessage()); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid appKey"); + return; + } + + if (appSecret == null || appSecret.isBlank()) { + log.warn("No appSecret found for appKey={}", appKey); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid appKey"); + return; + } + + // 构造签名载荷并验证 + String userId = request.getHeader("X-User-Id"); // 可选 + String payload = AppRequestSignatureUtil.payload(appKey, userId, timestamp, nonce); + if (!AppRequestSignatureUtil.matches(appSecret, payload, signature)) { + log.warn("Signature mismatch for appKey={}", appKey); + response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Invalid signature"); + return; + } + + // 签名验证通过,继续处理请求 + filterChain.doFilter(request, response); + } + + /** + * 根据 appKey 解析 appSecret。 + * 各服务需实现此接口以查询 appSecret。 + */ + public interface AppSecretResolver { + String resolveAppSecret(String appKey); + } +} diff --git a/common/src/main/java/com/xuqm/common/security/ConfigFileCrypto.java b/common/src/main/java/com/xuqm/common/security/ConfigFileCrypto.java index 3b52c93..72a635d 100644 --- a/common/src/main/java/com/xuqm/common/security/ConfigFileCrypto.java +++ b/common/src/main/java/com/xuqm/common/security/ConfigFileCrypto.java @@ -72,7 +72,8 @@ public final class ConfigFileCrypto { text(node, "iosBundleId"), text(node, "harmonyBundleName"), text(node, "baseUrl"), - text(node, "serverUrl")); + text(node, "serverUrl"), + text(node, "signingKey")); } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { @@ -105,7 +106,8 @@ public final class ConfigFileCrypto { String iosBundleId, String harmonyBundleName, String baseUrl, - String serverUrl) { + String serverUrl, + String signingKey) { public boolean matchesPackageName(String candidate) { if (candidate == null || candidate.isBlank()) return false; diff --git a/tenant-service/src/main/java/com/xuqm/tenant/service/AppService.java b/tenant-service/src/main/java/com/xuqm/tenant/service/AppService.java index d354d74..b167103 100644 --- a/tenant-service/src/main/java/com/xuqm/tenant/service/AppService.java +++ b/tenant-service/src/main/java/com/xuqm/tenant/service/AppService.java @@ -195,6 +195,7 @@ public class AppService { payload.put("harmonyBundleName", app.getHarmonyBundleName()); } payload.put("serverUrl", normalizeBaseUrl(sdkPlatformPublicBaseUrl)); + payload.put("signingKey", app.getAppSecret()); payload.put("issuedAt", java.time.Instant.now().toString()); try { return ConfigFileCrypto.encrypt(MAPPER.valueToTree(payload).toString()); diff --git a/update-service/src/main/java/com/xuqm/update/controller/AppVersionController.java b/update-service/src/main/java/com/xuqm/update/controller/AppVersionController.java index bfcc80d..93672c8 100644 --- a/update-service/src/main/java/com/xuqm/update/controller/AppVersionController.java +++ b/update-service/src/main/java/com/xuqm/update/controller/AppVersionController.java @@ -2,7 +2,9 @@ package com.xuqm.update.controller; import com.xuqm.common.model.ApiResponse; import com.xuqm.update.entity.AppVersionEntity; +import com.xuqm.update.entity.DeviceRecordEntity; import com.xuqm.update.repository.AppVersionRepository; +import com.xuqm.update.repository.DeviceRecordRepository; import com.xuqm.update.model.AppPackageInspectResult; import com.xuqm.update.service.UpdateOperationLogService; import org.slf4j.Logger; @@ -34,6 +36,7 @@ public class AppVersionController { private static final Logger log = LoggerFactory.getLogger(AppVersionController.class); private final AppVersionRepository versionRepository; + private final DeviceRecordRepository deviceRecordRepository; private final UpdateAssetService updateAssetService; private final PublishConfigService publishConfigService; private final AppStoreService appStoreService; @@ -45,6 +48,7 @@ public class AppVersionController { private final com.fasterxml.jackson.databind.ObjectMapper objectMapper; public AppVersionController(AppVersionRepository versionRepository, + DeviceRecordRepository deviceRecordRepository, UpdateAssetService updateAssetService, PublishConfigService publishConfigService, AppStoreService appStoreService, @@ -55,6 +59,7 @@ public class AppVersionController { GrayMemberService grayMemberService, com.fasterxml.jackson.databind.ObjectMapper objectMapper) { this.versionRepository = versionRepository; + this.deviceRecordRepository = deviceRecordRepository; this.updateAssetService = updateAssetService; this.publishConfigService = publishConfigService; this.appStoreService = appStoreService; @@ -72,9 +77,16 @@ public class AppVersionController { @RequestParam AppVersionEntity.Platform platform, @RequestParam int currentVersionCode, @RequestParam(required = false) String licenseFile, - @RequestParam(required = false) String userId) { + @RequestParam(required = false) String userId, + @RequestParam(required = false) String deviceId, + @RequestParam(required = false) String model, + @RequestParam(required = false) String osVersion, + @RequestParam(required = false) String vendor) { String resolvedAppKey = resolveAndValidate(appKey, platform, licenseFile); + + // 异步记录设备信息(不影响响应性能) + recordDevice(resolvedAppKey, platform.name(), deviceId, userId, model, osVersion, vendor, currentVersionCode); boolean serviceActivated = tenantClient.isUpdateServiceEnabled(resolvedAppKey, platform.name()); boolean allowAnonymousCheck = publishConfigService.allowAnonymousUpdateCheck(resolvedAppKey); @@ -589,4 +601,32 @@ public class AppVersionController { return "[]"; } } + + /** + * 异步记录设备信息到 update_device_record 表。 + * 仅当 deviceId 或 model 有值时才记录,避免空数据。 + * 异常不影响主流程。 + */ + private void recordDevice(String appKey, String platform, String deviceId, String userId, + String model, String osVersion, String vendor, int currentVersionCode) { + if ((deviceId == null || deviceId.isBlank()) && (model == null || model.isBlank())) { + return; + } + try { + DeviceRecordEntity record = new DeviceRecordEntity(); + record.setId(UUID.randomUUID().toString()); + record.setAppKey(appKey); + record.setPlatform(platform); + record.setDeviceId(deviceId); + record.setUserId(userId); + record.setModel(model); + record.setOsVersion(osVersion); + record.setVendor(vendor); + record.setCurrentVersionCode(currentVersionCode); + record.setCheckedAt(LocalDateTime.now()); + deviceRecordRepository.save(record); + } catch (Exception e) { + log.warn("Failed to record device info for appKey={}: {}", appKey, e.getMessage()); + } + } } diff --git a/update-service/src/main/java/com/xuqm/update/controller/UpdateStatisticsController.java b/update-service/src/main/java/com/xuqm/update/controller/UpdateStatisticsController.java new file mode 100644 index 0000000..ee26362 --- /dev/null +++ b/update-service/src/main/java/com/xuqm/update/controller/UpdateStatisticsController.java @@ -0,0 +1,59 @@ +package com.xuqm.update.controller; + +import com.xuqm.common.model.ApiResponse; +import com.xuqm.update.service.UpdateStatisticsService; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/api/v1/updates/stats") +public class UpdateStatisticsController { + + private final UpdateStatisticsService statisticsService; + + public UpdateStatisticsController(UpdateStatisticsService statisticsService) { + this.statisticsService = statisticsService; + } + + /** + * 版本分布:各版本的设备数占比(饼图/环形图数据) + */ + @GetMapping("/version-distribution") + public ResponseEntity>>> getVersionDistribution( + @RequestParam String appKey, + @RequestParam String platform, + @RequestParam(defaultValue = "30") int days) { + List> data = statisticsService.getVersionDistribution(appKey, platform, days); + return ResponseEntity.ok(ApiResponse.success(data)); + } + + /** + * 设备型号分布 Top 10(柱状图数据) + */ + @GetMapping("/device-distribution") + public ResponseEntity>>>> getDeviceDistribution( + @RequestParam String appKey, + @RequestParam String platform, + @RequestParam(defaultValue = "30") int days) { + Map>> data = new java.util.LinkedHashMap<>(); + data.put("models", statisticsService.getDeviceDistribution(appKey, platform, days)); + data.put("osVersions", statisticsService.getOsVersionDistribution(appKey, platform, days)); + data.put("vendors", statisticsService.getVendorDistribution(appKey, platform, days)); + return ResponseEntity.ok(ApiResponse.success(data)); + } + + /** + * 每日检测趋势(折线图数据) + */ + @GetMapping("/daily-trend") + public ResponseEntity>>> getDailyTrend( + @RequestParam String appKey, + @RequestParam String platform, + @RequestParam(defaultValue = "7") int days) { + List> data = statisticsService.getDailyTrend(appKey, platform, days); + return ResponseEntity.ok(ApiResponse.success(data)); + } +} diff --git a/update-service/src/main/java/com/xuqm/update/entity/DeviceRecordEntity.java b/update-service/src/main/java/com/xuqm/update/entity/DeviceRecordEntity.java new file mode 100644 index 0000000..0f7accf --- /dev/null +++ b/update-service/src/main/java/com/xuqm/update/entity/DeviceRecordEntity.java @@ -0,0 +1,77 @@ +package com.xuqm.update.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import java.time.LocalDateTime; + +@Entity +@Table(name = "update_device_record") +public class DeviceRecordEntity { + + @Id + private String id; + + @Column(nullable = false, length = 64) + private String appKey; + + @Column(nullable = false, length = 16) + private String platform; + + @Column(length = 128) + private String deviceId; + + @Column(length = 128) + private String userId; + + @Column(length = 128) + private String model; + + @Column(length = 64) + private String osVersion; + + @Column(length = 32) + private String vendor; + + private Integer currentVersionCode; + + @Column(length = 32) + private String currentVersionName; + + @Column(nullable = false) + private LocalDateTime checkedAt; + + public String getId() { return id; } + public void setId(String id) { this.id = id; } + + public String getAppKey() { return appKey; } + public void setAppKey(String appKey) { this.appKey = appKey; } + + public String getPlatform() { return platform; } + public void setPlatform(String platform) { this.platform = platform; } + + public String getDeviceId() { return deviceId; } + public void setDeviceId(String deviceId) { this.deviceId = deviceId; } + + public String getUserId() { return userId; } + public void setUserId(String userId) { this.userId = userId; } + + public String getModel() { return model; } + public void setModel(String model) { this.model = model; } + + public String getOsVersion() { return osVersion; } + public void setOsVersion(String osVersion) { this.osVersion = osVersion; } + + public String getVendor() { return vendor; } + public void setVendor(String vendor) { this.vendor = vendor; } + + public Integer getCurrentVersionCode() { return currentVersionCode; } + public void setCurrentVersionCode(Integer currentVersionCode) { this.currentVersionCode = currentVersionCode; } + + public String getCurrentVersionName() { return currentVersionName; } + public void setCurrentVersionName(String currentVersionName) { this.currentVersionName = currentVersionName; } + + public LocalDateTime getCheckedAt() { return checkedAt; } + public void setCheckedAt(LocalDateTime checkedAt) { this.checkedAt = checkedAt; } +} diff --git a/update-service/src/main/java/com/xuqm/update/repository/DeviceRecordRepository.java b/update-service/src/main/java/com/xuqm/update/repository/DeviceRecordRepository.java new file mode 100644 index 0000000..0294894 --- /dev/null +++ b/update-service/src/main/java/com/xuqm/update/repository/DeviceRecordRepository.java @@ -0,0 +1,78 @@ +package com.xuqm.update.repository; + +import com.xuqm.update.entity.DeviceRecordEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.time.LocalDateTime; +import java.util.List; + +public interface DeviceRecordRepository extends JpaRepository { + + /** 版本分布:按 currentVersionCode 分组统计设备数 */ + @Query(value = """ + SELECT current_version_code AS versionCode, + current_version_name AS versionName, + COUNT(DISTINCT device_id) AS deviceCount + FROM update_device_record + WHERE app_key = :appKey AND platform = :platform AND checked_at >= :since + GROUP BY current_version_code, current_version_name + ORDER BY current_version_code DESC + """, nativeQuery = true) + List findVersionDistribution(@Param("appKey") String appKey, + @Param("platform") String platform, + @Param("since") LocalDateTime since); + + /** 设备型号分布 Top N */ + @Query(value = """ + SELECT model, COUNT(DISTINCT device_id) AS deviceCount + FROM update_device_record + WHERE app_key = :appKey AND platform = :platform AND checked_at >= :since AND model IS NOT NULL AND model != '' + GROUP BY model + ORDER BY deviceCount DESC + LIMIT :limit + """, nativeQuery = true) + List findModelDistribution(@Param("appKey") String appKey, + @Param("platform") String platform, + @Param("since") LocalDateTime since, + @Param("limit") int limit); + + /** 系统版本分布 */ + @Query(value = """ + SELECT os_version AS osVersion, COUNT(DISTINCT device_id) AS deviceCount + FROM update_device_record + WHERE app_key = :appKey AND platform = :platform AND checked_at >= :since AND os_version IS NOT NULL AND os_version != '' + GROUP BY os_version + ORDER BY deviceCount DESC + LIMIT :limit + """, nativeQuery = true) + List findOsVersionDistribution(@Param("appKey") String appKey, + @Param("platform") String platform, + @Param("since") LocalDateTime since, + @Param("limit") int limit); + + /** 厂商分布 */ + @Query(value = """ + SELECT vendor, COUNT(DISTINCT device_id) AS deviceCount + FROM update_device_record + WHERE app_key = :appKey AND platform = :platform AND checked_at >= :since AND vendor IS NOT NULL AND vendor != '' + GROUP BY vendor + ORDER BY deviceCount DESC + """, nativeQuery = true) + List findVendorDistribution(@Param("appKey") String appKey, + @Param("platform") String platform, + @Param("since") LocalDateTime since); + + /** 每日检测趋势:按天统计设备数 */ + @Query(value = """ + SELECT DATE(checked_at) AS day, COUNT(DISTINCT device_id) AS deviceCount + FROM update_device_record + WHERE app_key = :appKey AND platform = :platform AND checked_at >= :since + GROUP BY DATE(checked_at) + ORDER BY day ASC + """, nativeQuery = true) + List findDailyTrend(@Param("appKey") String appKey, + @Param("platform") String platform, + @Param("since") LocalDateTime since); +} diff --git a/update-service/src/main/java/com/xuqm/update/service/UpdateStatisticsService.java b/update-service/src/main/java/com/xuqm/update/service/UpdateStatisticsService.java new file mode 100644 index 0000000..409e141 --- /dev/null +++ b/update-service/src/main/java/com/xuqm/update/service/UpdateStatisticsService.java @@ -0,0 +1,106 @@ +package com.xuqm.update.service; + +import com.xuqm.update.repository.DeviceRecordRepository; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +@Service +public class UpdateStatisticsService { + + private final DeviceRecordRepository deviceRecordRepository; + + public UpdateStatisticsService(DeviceRecordRepository deviceRecordRepository) { + this.deviceRecordRepository = deviceRecordRepository; + } + + /** + * 版本分布:各版本的设备数占比 + * 返回 [{ versionCode, versionName, deviceCount }] + */ + public List> getVersionDistribution(String appKey, String platform, int days) { + LocalDateTime since = LocalDateTime.now().minusDays(days); + List rows = deviceRecordRepository.findVersionDistribution(appKey, platform, since); + List> result = new ArrayList<>(); + for (Object[] row : rows) { + Map item = new LinkedHashMap<>(); + item.put("versionCode", row[0]); + item.put("versionName", row[1]); + item.put("deviceCount", row[2]); + result.add(item); + } + return result; + } + + /** + * 设备型号分布 Top N + * 返回 [{ model, deviceCount }] + */ + public List> getDeviceDistribution(String appKey, String platform, int days) { + LocalDateTime since = LocalDateTime.now().minusDays(days); + List models = deviceRecordRepository.findModelDistribution(appKey, platform, since, 10); + List> result = new ArrayList<>(); + for (Object[] row : models) { + Map item = new LinkedHashMap<>(); + item.put("model", row[0]); + item.put("deviceCount", row[1]); + result.add(item); + } + return result; + } + + /** + * 系统版本分布 + * 返回 [{ osVersion, deviceCount }] + */ + public List> getOsVersionDistribution(String appKey, String platform, int days) { + LocalDateTime since = LocalDateTime.now().minusDays(days); + List rows = deviceRecordRepository.findOsVersionDistribution(appKey, platform, since, 10); + List> result = new ArrayList<>(); + for (Object[] row : rows) { + Map item = new LinkedHashMap<>(); + item.put("osVersion", row[0]); + item.put("deviceCount", row[1]); + result.add(item); + } + return result; + } + + /** + * 厂商分布 + * 返回 [{ vendor, deviceCount }] + */ + public List> getVendorDistribution(String appKey, String platform, int days) { + LocalDateTime since = LocalDateTime.now().minusDays(days); + List rows = deviceRecordRepository.findVendorDistribution(appKey, platform, since); + List> result = new ArrayList<>(); + for (Object[] row : rows) { + Map item = new LinkedHashMap<>(); + item.put("vendor", row[0]); + item.put("deviceCount", row[1]); + result.add(item); + } + return result; + } + + /** + * 每日检测趋势 + * 返回 [{ day, deviceCount }] + */ + public List> getDailyTrend(String appKey, String platform, int days) { + LocalDateTime since = LocalDateTime.now().minusDays(days); + List rows = deviceRecordRepository.findDailyTrend(appKey, platform, since); + List> result = new ArrayList<>(); + for (Object[] row : rows) { + Map item = new LinkedHashMap<>(); + item.put("day", row[0] != null ? row[0].toString() : ""); + item.put("deviceCount", row[1]); + result.add(item); + } + return result; + } +} diff --git a/update-service/src/main/resources/db/migration/V2__add_device_record.sql b/update-service/src/main/resources/db/migration/V2__add_device_record.sql new file mode 100644 index 0000000..a951c91 --- /dev/null +++ b/update-service/src/main/resources/db/migration/V2__add_device_record.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS update_device_record ( + id VARCHAR(255) NOT NULL PRIMARY KEY, + app_key VARCHAR(64) NOT NULL, + platform VARCHAR(16) NOT NULL, + device_id VARCHAR(128), + user_id VARCHAR(128), + model VARCHAR(128), + os_version VARCHAR(64), + vendor VARCHAR(32), + current_version_code INT, + current_version_name VARCHAR(32), + checked_at DATETIME(6) NOT NULL, + INDEX idx_device_record_app_checked (app_key, platform, checked_at), + INDEX idx_device_record_device (app_key, device_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/xuqm-bugcollect-service/src/main/java/com/xuqm/bugcollect/controller/LogController.java b/xuqm-bugcollect-service/src/main/java/com/xuqm/bugcollect/controller/LogController.java index 9c26fbc..1f91662 100644 --- a/xuqm-bugcollect-service/src/main/java/com/xuqm/bugcollect/controller/LogController.java +++ b/xuqm-bugcollect-service/src/main/java/com/xuqm/bugcollect/controller/LogController.java @@ -15,7 +15,7 @@ import java.io.IOException; import java.util.List; @RestController -@RequestMapping("/bugcollect/v1") +@RequestMapping("/api/bugcollect/v1") public class LogController { private final LogService logService;