package com.xuqm.im.controller; import com.xuqm.common.model.ApiResponse; import com.xuqm.im.entity.ImAccountEntity; import com.xuqm.im.entity.ImGlobalMuteEntity; import com.xuqm.im.entity.ImGroupEntity; import com.xuqm.im.entity.ImMessageEntity; import com.xuqm.im.entity.KeywordFilterEntity; import com.xuqm.im.entity.WebhookConfigEntity; import com.xuqm.im.repository.ImAccountRepository; import com.xuqm.im.repository.ImGroupRepository; import com.xuqm.im.repository.ImMessageRepository; import com.xuqm.im.service.ImAccountService; import com.xuqm.im.service.ImGroupService; import com.xuqm.im.service.GlobalMuteService; import com.xuqm.im.service.KeywordFilterService; import com.xuqm.im.service.MessageService; import com.xuqm.im.service.WebhookConfigService; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.time.LocalDateTime; import java.util.List; import java.util.Map; @RestController @RequestMapping("/api/im/admin") public class ImAdminController { private final ImAccountRepository accountRepository; private final ImGroupRepository groupRepository; private final ImMessageRepository messageRepository; private final ImAccountService accountService; private final ImGroupService groupService; private final MessageService messageService; private final WebhookConfigService webhookConfigService; private final KeywordFilterService keywordFilterService; private final GlobalMuteService globalMuteService; public ImAdminController(ImAccountRepository accountRepository, ImGroupRepository groupRepository, ImMessageRepository messageRepository, ImAccountService accountService, ImGroupService groupService, MessageService messageService, WebhookConfigService webhookConfigService, KeywordFilterService keywordFilterService, GlobalMuteService globalMuteService) { this.accountRepository = accountRepository; this.groupRepository = groupRepository; this.messageRepository = messageRepository; this.accountService = accountService; this.groupService = groupService; this.messageService = messageService; this.webhookConfigService = webhookConfigService; this.keywordFilterService = keywordFilterService; this.globalMuteService = globalMuteService; } /** List all registered IM users for the given appId. */ @GetMapping("/users") public ResponseEntity>> listUsers( @RequestParam String appId, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { return ResponseEntity.ok(ApiResponse.success( accountRepository.findByAppId(appId, PageRequest.of(page, size)))); } /** Ban or unban a user. */ @PutMapping("/users/{userId}/status") public ResponseEntity> updateUserStatus( @RequestParam String appId, @PathVariable String userId, @RequestBody Map body) { ImAccountEntity account = accountRepository.findByAppIdAndUserId(appId, userId) .orElseThrow(() -> new RuntimeException("User not found")); account.setStatus(ImAccountEntity.Status.valueOf(body.get("status").toUpperCase())); return ResponseEntity.ok(ApiResponse.success(accountRepository.save(account))); } /** List all groups for the given appId. */ @GetMapping("/groups") public ResponseEntity>> listGroups(@RequestParam String appId) { return ResponseEntity.ok(ApiResponse.success(groupRepository.findByAppId(appId))); } /** Admin registers a new IM user (or returns existing). */ @PostMapping("/users") public ResponseEntity> registerUser( @RequestParam String appId, @RequestBody RegisterUserRequest req) { accountService.loginOrRegister(appId, req.userId(), req.nickname(), req.avatar()); ImAccountEntity account = accountRepository.findByAppIdAndUserId(appId, req.userId()) .orElseThrow(); return ResponseEntity.ok(ApiResponse.success(account)); } /** Admin creates a group. */ @PostMapping("/groups") public ResponseEntity> createGroup( @RequestParam String appId, @RequestBody CreateGroupRequest req) { return ResponseEntity.ok(ApiResponse.success( groupService.create(appId, req.name(), req.creatorId(), req.memberIds(), "WORK"))); } /** Fuzzy search users by userId or nickname. */ @GetMapping("/users/search") public ResponseEntity>> searchUsers( @RequestParam String appId, @RequestParam String keyword, @RequestParam(defaultValue = "20") int size) { List results = accountRepository.searchByKeyword(appId, keyword, PageRequest.of(0, size)); return ResponseEntity.ok(ApiResponse.success(results)); } /** Fuzzy search groups by id, name, creator or announcement. */ @GetMapping("/groups/search") public ResponseEntity>> searchGroups( @RequestParam String appId, @RequestParam String keyword, @RequestParam(defaultValue = "20") int size) { List results = groupRepository.searchByKeyword(appId, keyword, PageRequest.of(0, size)); return ResponseEntity.ok(ApiResponse.success(results)); } /** Search messages across the application. */ @GetMapping("/messages/search") public ResponseEntity>> searchMessages( @RequestParam String appId, @RequestParam(required = false) ImMessageEntity.ChatType chatType, @RequestParam(required = false) ImMessageEntity.MsgType msgType, @RequestParam(required = false) String keyword, @RequestParam(required = false) LocalDateTime startTime, @RequestParam(required = false) LocalDateTime endTime, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { return ResponseEntity.ok(ApiResponse.success( messageRepository.searchByKeyword( appId, chatType, msgType, keyword, startTime, endTime, PageRequest.of(page, size)))); } /** Message statistics for the given appId. */ @GetMapping("/stats") public ResponseEntity>> stats(@RequestParam String appId) { long totalMessages = messageRepository.countByAppId(appId); long totalUsers = accountRepository.countByAppId(appId); long totalGroups = groupRepository.countByAppId(appId); long todayMessages = messageRepository.countTodayByAppId(appId); return ResponseEntity.ok(ApiResponse.success(Map.of( "totalMessages", totalMessages, "totalUsers", totalUsers, "totalGroups", totalGroups, "todayMessages", todayMessages ))); } /** Admin queries conversation history between any two users. */ @GetMapping("/messages") public ResponseEntity>> history( @RequestParam String appId, @RequestParam String userA, @RequestParam String userB, @RequestParam(required = false) com.xuqm.im.entity.ImMessageEntity.MsgType msgType, @RequestParam(required = false) String keyword, @RequestParam(required = false) LocalDateTime startTime, @RequestParam(required = false) LocalDateTime endTime, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "20") int size) { return ResponseEntity.ok(ApiResponse.success( messageRepository.findSingleConversationFiltered( appId, userA, userB, msgType, keyword, startTime, endTime, PageRequest.of(page, size)))); } /** Admin revokes an arbitrary message. */ @PostMapping("/messages/{messageId}/revoke") public ResponseEntity> adminRevoke( @RequestParam String appId, @PathVariable String messageId) { return ResponseEntity.ok(ApiResponse.success(messageService.adminRevoke(appId, messageId))); } /** Admin force dismisses a group. */ @DeleteMapping("/groups/{groupId}") public ResponseEntity> adminDismissGroup(@PathVariable String groupId) { groupService.adminDismiss(groupId); return ResponseEntity.ok(ApiResponse.ok()); } @GetMapping("/webhooks") public ResponseEntity>> listWebhooks(@RequestParam String appId) { return ResponseEntity.ok(ApiResponse.success(webhookConfigService.list(appId))); } @PostMapping("/webhooks") public ResponseEntity> createWebhook( @RequestParam String appId, @RequestBody WebhookConfigRequest req) { return ResponseEntity.ok(ApiResponse.success( webhookConfigService.create(appId, req.url(), req.secret(), req.enabled()))); } @PutMapping("/webhooks/{id}") public ResponseEntity> updateWebhook( @RequestParam String appId, @PathVariable String id, @RequestBody WebhookConfigRequest req) { return ResponseEntity.ok(ApiResponse.success( webhookConfigService.update(appId, id, req.url(), req.secret(), req.enabled()))); } @DeleteMapping("/webhooks/{id}") public ResponseEntity> deleteWebhook( @RequestParam String appId, @PathVariable String id) { webhookConfigService.delete(appId, id); return ResponseEntity.ok(ApiResponse.ok()); } @GetMapping("/keyword-filters") public ResponseEntity>> listKeywordFilters(@RequestParam String appId) { return ResponseEntity.ok(ApiResponse.success(keywordFilterService.list(appId))); } @PostMapping("/keyword-filters") public ResponseEntity> createKeywordFilter( @RequestParam String appId, @RequestBody KeywordFilterRequest req) { return ResponseEntity.ok(ApiResponse.success( keywordFilterService.add(appId, req.pattern(), req.replacement(), req.action()))); } @PutMapping("/keyword-filters/{id}") public ResponseEntity> updateKeywordFilter( @RequestParam String appId, @PathVariable String id, @RequestBody KeywordFilterRequest req) { return ResponseEntity.ok(ApiResponse.success( keywordFilterService.update(appId, id, req.pattern(), req.replacement(), req.action(), req.enabled()))); } @DeleteMapping("/keyword-filters/{id}") public ResponseEntity> deleteKeywordFilter( @RequestParam String appId, @PathVariable String id) { keywordFilterService.delete(appId, id); return ResponseEntity.ok(ApiResponse.ok()); } @GetMapping("/global-mute") public ResponseEntity> getGlobalMute(@RequestParam String appId) { return ResponseEntity.ok(ApiResponse.success(globalMuteService.get(appId))); } @PutMapping("/global-mute") public ResponseEntity> setGlobalMute( @RequestParam String appId, @RequestParam boolean enabled) { return ResponseEntity.ok(ApiResponse.success(globalMuteService.setEnabled(appId, enabled))); } public record RegisterUserRequest(String userId, String nickname, String avatar) {} public record CreateGroupRequest(String name, String creatorId, List memberIds) {} public record WebhookConfigRequest(String url, String secret, Boolean enabled) {} public record KeywordFilterRequest(String pattern, String replacement, String action, Boolean enabled) {} }