2026-05-05 23:17:57 +08:00
|
|
|
|
package com.xuqm.push.controller;
|
|
|
|
|
|
|
|
|
|
|
|
import com.xuqm.common.model.ApiResponse;
|
|
|
|
|
|
import com.xuqm.push.entity.DeviceLoginLogEntity;
|
2026-05-14 23:40:35 +08:00
|
|
|
|
import com.xuqm.push.entity.PushUserEntity;
|
|
|
|
|
|
import com.xuqm.push.service.PushAccountService;
|
2026-05-05 23:17:57 +08:00
|
|
|
|
import com.xuqm.push.service.PushDiagnosticsService;
|
2026-05-27 13:36:16 +08:00
|
|
|
|
import com.xuqm.push.service.PushOperationLogService;
|
|
|
|
|
|
import java.util.LinkedHashMap;
|
2026-05-05 23:17:57 +08:00
|
|
|
|
import org.springframework.data.domain.Page;
|
|
|
|
|
|
import org.springframework.http.ResponseEntity;
|
2026-05-09 14:53:42 +08:00
|
|
|
|
import org.springframework.security.access.prepost.PreAuthorize;
|
2026-05-14 23:40:35 +08:00
|
|
|
|
import org.springframework.web.bind.annotation.DeleteMapping;
|
2026-05-05 23:17:57 +08:00
|
|
|
|
import org.springframework.web.bind.annotation.GetMapping;
|
2026-05-14 23:40:35 +08:00
|
|
|
|
import org.springframework.web.bind.annotation.PathVariable;
|
2026-05-05 23:17:57 +08:00
|
|
|
|
import org.springframework.web.bind.annotation.PostMapping;
|
2026-05-14 23:40:35 +08:00
|
|
|
|
import org.springframework.web.bind.annotation.PutMapping;
|
2026-05-05 23:17:57 +08:00
|
|
|
|
import org.springframework.web.bind.annotation.RequestBody;
|
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestMapping;
|
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestParam;
|
|
|
|
|
|
import org.springframework.web.bind.annotation.RestController;
|
|
|
|
|
|
|
2026-05-14 23:40:35 +08:00
|
|
|
|
import java.util.List;
|
2026-05-05 23:17:57 +08:00
|
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
|
|
@RestController
|
|
|
|
|
|
@RequestMapping("/api/push/admin")
|
2026-05-09 14:53:42 +08:00
|
|
|
|
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT', 'ROLE_ADMIN')")
|
2026-05-05 23:17:57 +08:00
|
|
|
|
public class PushManagementController {
|
|
|
|
|
|
|
|
|
|
|
|
private final PushDiagnosticsService diagnosticsService;
|
2026-05-14 23:40:35 +08:00
|
|
|
|
private final PushAccountService accountService;
|
2026-05-27 13:36:16 +08:00
|
|
|
|
private final PushOperationLogService opLogService;
|
2026-05-05 23:17:57 +08:00
|
|
|
|
|
2026-05-14 23:40:35 +08:00
|
|
|
|
public PushManagementController(PushDiagnosticsService diagnosticsService,
|
2026-05-27 13:36:16 +08:00
|
|
|
|
PushAccountService accountService,
|
|
|
|
|
|
PushOperationLogService opLogService) {
|
2026-05-05 23:17:57 +08:00
|
|
|
|
this.diagnosticsService = diagnosticsService;
|
2026-05-14 23:40:35 +08:00
|
|
|
|
this.accountService = accountService;
|
2026-05-27 13:36:16 +08:00
|
|
|
|
this.opLogService = opLogService;
|
2026-05-05 23:17:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-14 23:40:35 +08:00
|
|
|
|
// ---- user account management ----
|
|
|
|
|
|
|
|
|
|
|
|
@GetMapping("/users")
|
|
|
|
|
|
public ResponseEntity<ApiResponse<Map<String, Object>>> listUsers(
|
|
|
|
|
|
@RequestParam String appKey,
|
|
|
|
|
|
@RequestParam(required = false, defaultValue = "") String keyword,
|
|
|
|
|
|
@RequestParam(defaultValue = "0") int page,
|
|
|
|
|
|
@RequestParam(defaultValue = "20") int size) {
|
|
|
|
|
|
Page<PushUserEntity> result = accountService.listUsers(appKey, keyword, page, size);
|
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
|
|
|
|
|
"content", result.getContent(),
|
|
|
|
|
|
"total", result.getTotalElements(),
|
|
|
|
|
|
"totalPages", result.getTotalPages()
|
|
|
|
|
|
)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@GetMapping("/users/{userId}")
|
|
|
|
|
|
public ResponseEntity<ApiResponse<PushUserEntity>> getUser(
|
|
|
|
|
|
@PathVariable String userId,
|
|
|
|
|
|
@RequestParam String appKey) {
|
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(accountService.getAccount(appKey, userId)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@PutMapping("/users/{userId}")
|
|
|
|
|
|
public ResponseEntity<ApiResponse<PushUserEntity>> updateUser(
|
|
|
|
|
|
@PathVariable String userId,
|
|
|
|
|
|
@RequestBody UpdateUserRequest request) {
|
|
|
|
|
|
PushUserEntity.Gender gender = null;
|
|
|
|
|
|
if (request.gender() != null && !request.gender().isBlank()) {
|
|
|
|
|
|
try { gender = PushUserEntity.Gender.valueOf(request.gender().toUpperCase()); } catch (Exception ignored) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
PushUserEntity updated = accountService.updateAccount(request.appKey(), userId,
|
|
|
|
|
|
request.nickname(), request.avatar(), gender);
|
2026-05-27 13:36:16 +08:00
|
|
|
|
opLogService.record(request.appKey(), "UPDATE_USER", "ACCOUNT", userId,
|
|
|
|
|
|
"编辑用户 " + userId + " 的信息", null);
|
2026-05-14 23:40:35 +08:00
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(updated));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@PutMapping("/users/{userId}/status")
|
|
|
|
|
|
public ResponseEntity<ApiResponse<PushUserEntity>> setUserStatus(
|
|
|
|
|
|
@PathVariable String userId,
|
|
|
|
|
|
@RequestBody UserStatusRequest request) {
|
|
|
|
|
|
PushUserEntity.Status status;
|
|
|
|
|
|
try {
|
|
|
|
|
|
status = PushUserEntity.Status.valueOf(request.status().toUpperCase());
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
|
return ResponseEntity.badRequest().body(ApiResponse.error(400, "无效的状态值,可选:ACTIVE, BANNED"));
|
|
|
|
|
|
}
|
2026-05-27 13:36:16 +08:00
|
|
|
|
PushUserEntity result = accountService.setUserStatus(request.appKey(), userId, status);
|
|
|
|
|
|
String statusLabel = status == PushUserEntity.Status.BANNED ? "禁用" : "启用";
|
|
|
|
|
|
opLogService.record(request.appKey(), "UPDATE_USER_STATUS", "ACCOUNT", userId,
|
|
|
|
|
|
statusLabel + "用户 " + userId, null);
|
|
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(result));
|
2026-05-14 23:40:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@DeleteMapping("/users/{userId}")
|
|
|
|
|
|
public ResponseEntity<ApiResponse<Void>> deleteUser(
|
|
|
|
|
|
@PathVariable String userId,
|
|
|
|
|
|
@RequestParam String appKey) {
|
|
|
|
|
|
accountService.deleteAccount(appKey, userId);
|
2026-05-27 13:36:16 +08:00
|
|
|
|
opLogService.record(appKey, "DELETE_USER", "ACCOUNT", userId,
|
|
|
|
|
|
"删除用户 " + userId, null);
|
2026-05-14 23:40:35 +08:00
|
|
|
|
return ResponseEntity.ok(ApiResponse.ok());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@PostMapping("/users/import")
|
|
|
|
|
|
public ResponseEntity<ApiResponse<PushUserEntity>> importUser(@RequestBody ImportUserRequest request) {
|
|
|
|
|
|
PushUserEntity.Gender gender = null;
|
|
|
|
|
|
if (request.gender() != null && !request.gender().isBlank()) {
|
|
|
|
|
|
try { gender = PushUserEntity.Gender.valueOf(request.gender().toUpperCase()); } catch (Exception ignored) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
PushUserEntity.Status status = null;
|
|
|
|
|
|
if (request.status() != null && !request.status().isBlank()) {
|
|
|
|
|
|
try { status = PushUserEntity.Status.valueOf(request.status().toUpperCase()); } catch (Exception ignored) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
PushUserEntity user = accountService.importAccount(request.appKey(), request.userId(),
|
|
|
|
|
|
request.nickname(), request.avatar(), gender, status);
|
2026-05-27 13:36:16 +08:00
|
|
|
|
opLogService.record(request.appKey(), "IMPORT_USER", "ACCOUNT", request.userId(),
|
|
|
|
|
|
"导入用户 " + request.userId(), null);
|
2026-05-14 23:40:35 +08:00
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(user));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ---- diagnostics ----
|
|
|
|
|
|
|
2026-05-05 23:17:57 +08:00
|
|
|
|
@GetMapping("/user-status")
|
|
|
|
|
|
public ResponseEntity<ApiResponse<PushDiagnosticsService.PushTokenDiagnostics>> userStatus(
|
2026-05-07 19:39:42 +08:00
|
|
|
|
@RequestParam String appKey,
|
2026-05-05 23:17:57 +08:00
|
|
|
|
@RequestParam String userId) {
|
2026-05-07 19:39:42 +08:00
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(diagnosticsService.searchByUserId(appKey, userId)));
|
2026-05-05 23:17:57 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@GetMapping("/device-logs")
|
|
|
|
|
|
public ResponseEntity<ApiResponse<Map<String, Object>>> deviceLogs(
|
2026-05-07 19:39:42 +08:00
|
|
|
|
@RequestParam String appKey,
|
2026-05-05 23:17:57 +08:00
|
|
|
|
@RequestParam String userId,
|
|
|
|
|
|
@RequestParam(defaultValue = "0") int page,
|
|
|
|
|
|
@RequestParam(defaultValue = "20") int size) {
|
2026-05-07 19:39:42 +08:00
|
|
|
|
Page<DeviceLoginLogEntity> result = diagnosticsService.deviceLogs(appKey, userId, page, size);
|
2026-05-05 23:17:57 +08:00
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(Map.of(
|
|
|
|
|
|
"content", result.getContent(),
|
|
|
|
|
|
"total", result.getTotalElements(),
|
|
|
|
|
|
"totalPages", result.getTotalPages()
|
|
|
|
|
|
)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@PostMapping("/test-offline")
|
|
|
|
|
|
public ResponseEntity<ApiResponse<PushDiagnosticsService.TestPushResult>> testOffline(
|
|
|
|
|
|
@RequestBody TestOfflineRequest request) {
|
|
|
|
|
|
PushDiagnosticsService.TestPushResult result = diagnosticsService.sendTestOfflineMessage(
|
2026-05-07 19:39:42 +08:00
|
|
|
|
request.appKey(),
|
2026-05-05 23:17:57 +08:00
|
|
|
|
request.userId(),
|
|
|
|
|
|
request.title(),
|
|
|
|
|
|
request.body(),
|
|
|
|
|
|
request.payload());
|
2026-05-27 13:36:16 +08:00
|
|
|
|
opLogService.record(request.appKey(), "TEST_PUSH", "PUSH", request.userId(),
|
|
|
|
|
|
"向 " + request.userId() + " 发送测试推送", null);
|
2026-05-05 23:17:57 +08:00
|
|
|
|
return ResponseEntity.ok(ApiResponse.success(result));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-14 23:40:35 +08:00
|
|
|
|
public record UpdateUserRequest(String appKey, String nickname, String avatar, String gender) {}
|
|
|
|
|
|
public record UserStatusRequest(String appKey, String status) {}
|
|
|
|
|
|
public record ImportUserRequest(String appKey, String userId, String nickname,
|
|
|
|
|
|
String avatar, String gender, String status) {}
|
|
|
|
|
|
public record TestOfflineRequest(String appKey, String userId,
|
|
|
|
|
|
String title, String body, String payload) {}
|
2026-05-05 23:17:57 +08:00
|
|
|
|
}
|