XuqmGroup-Server/push-service/src/main/java/com/xuqm/push/controller/PushManagementController.java
XuqmGroup 1a18925034 feat: 厂商应用商店提交功能完善及push用户管理
update-service:
- 修复华为 appId 解析 NPE(支持直接从响应体顶层读取 value 字段)
- 修复 OPPO 更新描述不足5字符时自动补空格
- 修复 VIVO 签名中文字符需 URL 编码
- 修复 RestTemplate 无超时(30s连接/5min读取)
- AppVersionEntity 添加 grayCallbackUrl 字段

tenant-service:
- FeatureServiceController switch 添加 FILE 分支(修复编译错误)
- FeatureServiceManager 添加 buildFileConfig 方法
- AppController 添加应用用户列表代理端点
- AppUserClient 新增 IM/Push 用户列表客户端

push-service:
- 新增 PushUserEntity/PushUserRepository/PushAccountService
- 新增 PushAuthController(内部鉴权接口)
- PushManagementController 添加用户管理接口
- PushAppSecretClient 对接 tenant-service 鉴权

im-service:
- ImAccountRepository/ImAccountService 添加用户搜索接口
- ImAdminController 添加管理端用户列表
- InternalPresenceController 完善在线状态接口

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 23:40:35 +08:00

152 行
6.9 KiB
Java

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

package com.xuqm.push.controller;
import com.xuqm.common.model.ApiResponse;
import com.xuqm.push.entity.DeviceLoginLogEntity;
import com.xuqm.push.entity.PushUserEntity;
import com.xuqm.push.service.PushAccountService;
import com.xuqm.push.service.PushDiagnosticsService;
import org.springframework.data.domain.Page;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/push/admin")
@PreAuthorize("hasAnyAuthority('ROLE_OPS', 'ROLE_TENANT', 'ROLE_ADMIN')")
public class PushManagementController {
private final PushDiagnosticsService diagnosticsService;
private final PushAccountService accountService;
public PushManagementController(PushDiagnosticsService diagnosticsService,
PushAccountService accountService) {
this.diagnosticsService = diagnosticsService;
this.accountService = accountService;
}
// ---- 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);
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"));
}
return ResponseEntity.ok(ApiResponse.success(accountService.setUserStatus(request.appKey(), userId, status)));
}
@DeleteMapping("/users/{userId}")
public ResponseEntity<ApiResponse<Void>> deleteUser(
@PathVariable String userId,
@RequestParam String appKey) {
accountService.deleteAccount(appKey, userId);
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);
return ResponseEntity.ok(ApiResponse.success(user));
}
// ---- diagnostics ----
@GetMapping("/user-status")
public ResponseEntity<ApiResponse<PushDiagnosticsService.PushTokenDiagnostics>> userStatus(
@RequestParam String appKey,
@RequestParam String userId) {
return ResponseEntity.ok(ApiResponse.success(diagnosticsService.searchByUserId(appKey, userId)));
}
@GetMapping("/device-logs")
public ResponseEntity<ApiResponse<Map<String, Object>>> deviceLogs(
@RequestParam String appKey,
@RequestParam String userId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size) {
Page<DeviceLoginLogEntity> result = diagnosticsService.deviceLogs(appKey, userId, page, size);
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(
request.appKey(),
request.userId(),
request.title(),
request.body(),
request.payload());
return ResponseEntity.ok(ApiResponse.success(result));
}
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) {}
}