XuqmGroup-Server/xuqm-bugcollect-service/src/main/java/com/xuqm/bugcollect/controller/LogController.java
XuqmGroup fbc9d02e2a feat(bugcollect): 新增 PUT /issues/{id}/reopen 接口
- LogService.reopenIssue() 将状态设回 open
- LogController 暴露 /issues/{id}/reopen 端点

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 10:47:51 +08:00

221 行
10 KiB
Java

package com.xuqm.bugcollect.controller;
import com.xuqm.common.model.ApiResponse;
import com.xuqm.common.model.PageResult;
import com.xuqm.bugcollect.dto.*;
import com.xuqm.bugcollect.service.LogService;
import com.xuqm.bugcollect.service.SourcemapService;
import com.xuqm.bugcollect.service.WebhookService;
import jakarta.validation.Valid;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
@RestController
@RequestMapping("/bugcollect/v1")
public class LogController {
private final LogService logService;
private final SourcemapService sourcemapService;
private final WebhookService webhookService;
public LogController(LogService logService, SourcemapService sourcemapService, WebhookService webhookService) {
this.logService = logService;
this.sourcemapService = sourcemapService;
this.webhookService = webhookService;
}
// ── Ingestion ──────────────────────────────────────────────────────────────
@PostMapping("/issues/batch")
public ApiResponse<Void> ingestIssues(@Valid @RequestBody IssueBatchRequest request) {
logService.processIssueBatch(request);
return ApiResponse.ok();
}
@PostMapping("/events/batch")
public ApiResponse<Void> ingestEvents(@Valid @RequestBody EventBatchRequest request) {
logService.processEventBatch(request);
return ApiResponse.ok();
}
// ── Issues ─────────────────────────────────────────────────────────────────
@GetMapping("/issues")
public ApiResponse<PageResult<IssueResponse>> queryIssues(
@RequestParam String appKey,
@RequestParam(required = false) String level,
@RequestParam(required = false) String platform,
@RequestParam(required = false) String status,
@RequestParam(required = false) String q,
@RequestParam(required = false) String assignee,
@RequestParam(required = false) String environment,
@RequestParam(required = false) String from,
@RequestParam(required = false) String to,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
Page<IssueResponse> result = logService.queryIssues(
appKey, level, platform, status, q, assignee, environment, from, to, page - 1, size);
return ApiResponse.success(PageResult.of(result.getContent(), result.getTotalElements(), page, size));
}
@GetMapping("/issues/{id}")
public ApiResponse<IssueResponse> getIssueDetail(@PathVariable Long id) {
return ApiResponse.success(logService.getIssueDetail(id));
}
@GetMapping("/issues/{id}/events")
public ApiResponse<PageResult<IssueEventResponse>> getIssueEvents(
@PathVariable Long id,
@RequestParam(required = false) String from,
@RequestParam(required = false) String to,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
Page<IssueEventResponse> result = logService.getIssueEvents(id, from, to, page - 1, size);
return ApiResponse.success(PageResult.of(result.getContent(), result.getTotalElements(), page, size));
}
@GetMapping("/issues/{id}/trend")
public ApiResponse<IssueTrendResponse> getIssueTrend(
@PathVariable Long id,
@RequestParam(required = false) String from,
@RequestParam(required = false) String to) {
return ApiResponse.success(logService.getIssueTrend(id, from, to));
}
@DeleteMapping("/issues/{id}")
public ApiResponse<Void> deleteIssue(@PathVariable Long id) {
logService.deleteIssueAndEvents(id);
return ApiResponse.ok();
}
// ── Issue management ───────────────────────────────────────────────────────
@PutMapping("/issues/{id}/resolve")
public ApiResponse<Void> resolveIssue(@PathVariable Long id) {
logService.resolveIssue(id);
return ApiResponse.ok();
}
@PutMapping("/issues/{id}/ignore")
public ApiResponse<Void> ignoreIssue(@PathVariable Long id) {
logService.ignoreIssue(id);
return ApiResponse.ok();
}
@PutMapping("/issues/{id}/reopen")
public ApiResponse<Void> reopenIssue(@PathVariable Long id) {
logService.reopenIssue(id);
return ApiResponse.ok();
}
@PutMapping("/issues/{id}/assign")
public ApiResponse<Void> assignIssue(
@PathVariable Long id,
@RequestBody java.util.Map<String, String> body) {
logService.assignIssue(id, body.get("assignee"));
return ApiResponse.ok();
}
@PostMapping("/issues/bulk")
public ApiResponse<Void> bulkUpdateIssues(
@RequestParam String appKey,
@Valid @RequestBody IssueActionRequest request) {
logService.bulkUpdateIssues(appKey, request.ids(), request.action());
return ApiResponse.ok();
}
// ── Rankings ───────────────────────────────────────────────────────────────
@GetMapping("/issues/rankings/frequency")
public ApiResponse<List<IssueResponse>> getFrequencyRankings(
@RequestParam String appKey,
@RequestParam(required = false) String from,
@RequestParam(required = false) String to,
@RequestParam(defaultValue = "20") int limit) {
return ApiResponse.success(logService.getFrequencyRankings(appKey, from, to, limit));
}
@GetMapping("/issues/rankings/risk")
public ApiResponse<List<IssueResponse>> getRiskRankings(
@RequestParam String appKey,
@RequestParam(required = false) String from,
@RequestParam(required = false) String to,
@RequestParam(defaultValue = "20") int limit) {
return ApiResponse.success(logService.getRiskRankings(appKey, from, to, limit));
}
// ── Analytics events ───────────────────────────────────────────────────────
@GetMapping("/events")
public ApiResponse<PageResult<IssueEventResponse>> queryEvents(
@RequestParam String appKey,
@RequestParam(required = false) String name,
@RequestParam(required = false) String userId,
@RequestParam(required = false) String from,
@RequestParam(required = false) String to,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "20") int size) {
Page<IssueEventResponse> result = logService.queryEvents(appKey, name, userId, from, to, page - 1, size);
return ApiResponse.success(PageResult.of(result.getContent(), result.getTotalElements(), page, size));
}
@GetMapping("/events/funnel")
public ApiResponse<FunnelResponse> queryFunnel(
@RequestParam String appKey,
@RequestParam String steps,
@RequestParam(required = false) String from,
@RequestParam(required = false) String to) {
return ApiResponse.success(logService.queryFunnel(appKey, List.of(steps.split(",")), from, to));
}
// ── Overview ───────────────────────────────────────────────────────────────
@GetMapping("/overview")
public ApiResponse<OverviewResponse> getOverview(
@RequestParam String appKey,
@RequestParam(required = false) String from,
@RequestParam(required = false) String to) {
return ApiResponse.success(logService.getOverview(appKey, from, to));
}
// ── Sourcemaps ─────────────────────────────────────────────────────────────
@PostMapping("/sourcemaps/upload")
public ApiResponse<SourcemapUploadResponse> uploadSourcemap(
@RequestParam String appKey,
@RequestParam String platform,
@RequestParam String appVersion,
@RequestParam(required = false) String bundleVersion,
@RequestParam(required = false, defaultValue = "index") String bundleName,
@RequestParam("file") MultipartFile file) throws IOException {
return ApiResponse.success(sourcemapService.upload(appKey, platform, appVersion, bundleVersion, bundleName, file));
}
// ── Webhooks ───────────────────────────────────────────────────────────────
@GetMapping("/webhooks")
public ApiResponse<List<WebhookResponse>> listWebhooks(@RequestParam String appKey) {
return ApiResponse.success(webhookService.listWebhooks(appKey));
}
@PostMapping("/webhooks")
public ApiResponse<WebhookResponse> createWebhook(@Valid @RequestBody WebhookRequest request) {
return ApiResponse.success(webhookService.createWebhook(request));
}
@PutMapping("/webhooks/{id}")
public ApiResponse<WebhookResponse> updateWebhook(@PathVariable Long id, @Valid @RequestBody WebhookRequest request) {
return ApiResponse.success(webhookService.updateWebhook(id, request));
}
@DeleteMapping("/webhooks/{id}")
public ApiResponse<Void> deleteWebhook(@PathVariable Long id) {
webhookService.deleteWebhook(id);
return ApiResponse.ok();
}
}