mirror of
https://github.com/hpd840321/craftlabs-authorization-sdk.git
synced 2026-06-09 10:00:30 +08:00
feat: add failure reason persistence and batch replay endpoints to callback inbox
Added failureReason field to CallbackInboxStatusPatchRequest so Ops can categorize failure causes. Added POST /batch-replay for mass reprocess and GET /stats/backlog for backlog monitoring. Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
+26
@@ -27,6 +27,8 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/api/v1/callback-inbox")
|
@RequestMapping("/api/v1/callback-inbox")
|
||||||
@@ -103,4 +105,28 @@ public class CallbackInboxController {
|
|||||||
public CallbackWebhookDeliveryStatusResponse getWebhookDelivery(@PathVariable("id") long id) {
|
public CallbackWebhookDeliveryStatusResponse getWebhookDelivery(@PathVariable("id") long id) {
|
||||||
return callbackInboxService.getWebhookDeliveryStatus(id);
|
return callbackInboxService.getWebhookDeliveryStatus(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** M5-F07:批量重处理 — 接收 ID 列表,逐条触发 DEAD 重放。 */
|
||||||
|
@PostMapping("/batch-replay")
|
||||||
|
public ResponseEntity<Map<String, Object>> batchReplay(@RequestBody List<Long> ids) {
|
||||||
|
int success = 0;
|
||||||
|
int failed = 0;
|
||||||
|
java.util.List<String> errors = new java.util.ArrayList<>();
|
||||||
|
for (Long id : ids) {
|
||||||
|
try {
|
||||||
|
callbackInboxService.replayWebhookDelivery(id);
|
||||||
|
success++;
|
||||||
|
} catch (Exception e) {
|
||||||
|
failed++;
|
||||||
|
errors.add("ID " + id + ": " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ResponseEntity.ok(java.util.Map.of("success", success, "failed", failed, "errors", errors));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** M5-F08:死信与积压监控摘要。 */
|
||||||
|
@GetMapping("/stats/backlog")
|
||||||
|
public ResponseEntity<Map<String, Object>> backlogStats() {
|
||||||
|
return ResponseEntity.ok(callbackInboxService.getBacklogStats());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
@@ -133,6 +133,9 @@ public class CallbackInboxService {
|
|||||||
row.setStatus(to.name());
|
row.setStatus(to.name());
|
||||||
row.setProcessedAt(now);
|
row.setProcessedAt(now);
|
||||||
row.setProcessedByUserId(currentActorId());
|
row.setProcessedByUserId(currentActorId());
|
||||||
|
if (request.getFailureReason() != null) {
|
||||||
|
row.setFailureReason(request.getFailureReason());
|
||||||
|
}
|
||||||
row.setUpdatedAt(now);
|
row.setUpdatedAt(now);
|
||||||
inboxMapper.updateById(row);
|
inboxMapper.updateById(row);
|
||||||
auditService.record(
|
auditService.record(
|
||||||
@@ -306,6 +309,33 @@ public class CallbackInboxService {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> getBacklogStats() {
|
||||||
|
long totalPending = inboxMapper.selectCount(
|
||||||
|
Wrappers.lambdaQuery(PlatformCallbackInbox.class)
|
||||||
|
.eq(PlatformCallbackInbox::getStatus, CallbackInboxStatus.PENDING));
|
||||||
|
|
||||||
|
long totalFailed = inboxMapper.selectCount(
|
||||||
|
Wrappers.lambdaQuery(PlatformCallbackInbox.class)
|
||||||
|
.eq(PlatformCallbackInbox::getStatus, CallbackInboxStatus.FAILED));
|
||||||
|
|
||||||
|
var oldestPending = inboxMapper.selectOne(
|
||||||
|
Wrappers.lambdaQuery(PlatformCallbackInbox.class)
|
||||||
|
.eq(PlatformCallbackInbox::getStatus, CallbackInboxStatus.PENDING)
|
||||||
|
.orderByAsc(PlatformCallbackInbox::getReceivedAt)
|
||||||
|
.last("LIMIT 1"));
|
||||||
|
|
||||||
|
double oldestHours = 0;
|
||||||
|
if (oldestPending != null && oldestPending.getReceivedAt() != null) {
|
||||||
|
oldestHours = java.time.Duration.between(
|
||||||
|
oldestPending.getReceivedAt(), OffsetDateTime.now()).toMinutes() / 60.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return java.util.Map.of(
|
||||||
|
"totalPending", totalPending,
|
||||||
|
"totalFailed", totalFailed,
|
||||||
|
"oldestPendingHours", Math.round(oldestHours * 10.0) / 10.0);
|
||||||
|
}
|
||||||
|
|
||||||
private static String currentActorId() {
|
private static String currentActorId() {
|
||||||
var a = org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication();
|
var a = org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication();
|
||||||
if (a == null || !a.isAuthenticated()) {
|
if (a == null || !a.isAuthenticated()) {
|
||||||
|
|||||||
+9
@@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotBlank;
|
|||||||
public class CallbackInboxStatusPatchRequest {
|
public class CallbackInboxStatusPatchRequest {
|
||||||
|
|
||||||
@NotBlank private String status;
|
@NotBlank private String status;
|
||||||
|
private String failureReason;
|
||||||
|
|
||||||
public String getStatus() {
|
public String getStatus() {
|
||||||
return status;
|
return status;
|
||||||
@@ -13,4 +14,12 @@ public class CallbackInboxStatusPatchRequest {
|
|||||||
public void setStatus(String status) {
|
public void setStatus(String status) {
|
||||||
this.status = status;
|
this.status = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getFailureReason() {
|
||||||
|
return failureReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFailureReason(String failureReason) {
|
||||||
|
this.failureReason = failureReason;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user