XuqmGroup-Server/im-service/src/main/java/com/xuqm/im/service/ImGroupService.java

283 行
12 KiB
Java

2026-04-21 22:07:29 +08:00
package com.xuqm.im.service;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.xuqm.common.exception.BusinessException;
import com.xuqm.im.entity.ImGroupEntity;
import com.xuqm.im.entity.ImGroupJoinRequestEntity;
import com.xuqm.im.entity.ImGroupMuteEntity;
import com.xuqm.im.repository.ImGroupJoinRequestRepository;
2026-04-21 22:07:29 +08:00
import com.xuqm.im.repository.ImGroupRepository;
import com.xuqm.im.repository.ImGroupMuteRepository;
2026-04-21 22:07:29 +08:00
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
2026-04-21 22:07:29 +08:00
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
2026-04-21 22:07:29 +08:00
import java.util.UUID;
@Service
public class ImGroupService {
private final ImGroupRepository groupRepository;
private final ImGroupMuteRepository muteRepository;
private final ImGroupJoinRequestRepository joinRequestRepository;
2026-04-21 22:07:29 +08:00
private final ObjectMapper objectMapper;
public ImGroupService(ImGroupRepository groupRepository,
ImGroupMuteRepository muteRepository,
ImGroupJoinRequestRepository joinRequestRepository,
ObjectMapper objectMapper) {
2026-04-21 22:07:29 +08:00
this.groupRepository = groupRepository;
this.muteRepository = muteRepository;
this.joinRequestRepository = joinRequestRepository;
2026-04-21 22:07:29 +08:00
this.objectMapper = objectMapper;
}
@Transactional
public ImGroupEntity create(String appId, String name, String creatorId, List<String> memberIds, String groupType) {
2026-04-21 22:07:29 +08:00
List<String> members = new ArrayList<>(memberIds);
if (!members.contains(creatorId)) members.add(creatorId);
ImGroupEntity group = new ImGroupEntity();
group.setId(UUID.randomUUID().toString());
group.setAppId(appId);
group.setName(name);
group.setGroupType(normalizeGroupType(groupType));
2026-04-21 22:07:29 +08:00
group.setCreatorId(creatorId);
group.setMemberIds(toJson(members));
group.setAdminIds(toJson(List.of(creatorId)));
group.setAnnouncement(null);
2026-04-21 22:07:29 +08:00
group.setCreatedAt(LocalDateTime.now());
return groupRepository.save(group);
}
public ImGroupEntity get(String groupId) {
return groupRepository.findById(groupId)
2026-04-21 22:07:29 +08:00
.orElseThrow(() -> new BusinessException(404, "群组不存在"));
}
public ImGroupEntity get(String groupId, String requesterId) {
ImGroupEntity group = get(groupId);
if (!memberIds(group).contains(requesterId) && !group.getCreatorId().equals(requesterId)) {
throw new BusinessException(403, "不在群内");
}
return group;
}
@Transactional
public ImGroupEntity addMember(String groupId, String userId, String operatorId) {
ImGroupEntity group = get(groupId);
ensureCanManage(group, operatorId);
2026-04-21 22:07:29 +08:00
List<String> members = fromJson(group.getMemberIds());
if (!members.contains(userId)) {
members.add(userId);
group.setMemberIds(toJson(members));
groupRepository.save(group);
}
return group;
}
@Transactional
2026-04-21 22:07:29 +08:00
public ImGroupEntity removeMember(String groupId, String userId, String operatorId) {
ImGroupEntity group = get(groupId);
2026-04-21 22:07:29 +08:00
List<String> admins = fromJson(group.getAdminIds());
if (!admins.contains(operatorId) && !group.getCreatorId().equals(operatorId)) {
throw new BusinessException(403, "无权操作");
}
List<String> members = new ArrayList<>(fromJson(group.getMemberIds()));
members.remove(userId);
group.setMemberIds(toJson(members));
return groupRepository.save(group);
}
@Transactional
public ImGroupEntity update(String groupId, String operatorId, String name, String announcement) {
ImGroupEntity group = get(groupId);
ensureCanManage(group, operatorId);
if (name != null && !name.isBlank()) {
group.setName(name);
}
if (announcement != null) {
group.setAnnouncement(announcement);
}
return groupRepository.save(group);
}
@Transactional
public ImGroupEntity setRole(String groupId, String operatorId, String userId, String role) {
ImGroupEntity group = get(groupId);
ensureCanManage(group, operatorId);
List<String> admins = new ArrayList<>(fromJson(group.getAdminIds()));
if ("ADMIN".equalsIgnoreCase(role)) {
if (!admins.contains(userId)) admins.add(userId);
} else {
admins.remove(userId);
if (userId.equals(group.getCreatorId())) {
throw new BusinessException(403, "群主不能降级");
}
}
group.setAdminIds(toJson(admins));
return groupRepository.save(group);
}
@Transactional
public ImGroupEntity muteMember(String groupId, String operatorId, String userId, long minutes) {
ImGroupEntity group = get(groupId);
ensureCanManage(group, operatorId);
ImGroupMuteEntity mute = muteRepository
.findByGroupIdAndUserIdAndMutedUntilAfter(groupId, userId, LocalDateTime.now())
.orElseGet(() -> {
ImGroupMuteEntity entity = new ImGroupMuteEntity();
entity.setId(UUID.randomUUID().toString());
entity.setGroupId(groupId);
entity.setUserId(userId);
entity.setCreatedAt(LocalDateTime.now());
return entity;
});
mute.setMutedUntil(LocalDateTime.now().plusMinutes(Math.max(minutes, 0)));
mute.setUpdatedAt(LocalDateTime.now());
muteRepository.save(mute);
return group;
}
@Transactional
public void dismiss(String groupId, String operatorId) {
ImGroupEntity group = get(groupId);
if (!group.getCreatorId().equals(operatorId)) {
throw new BusinessException(403, "只有群主可以解散群");
}
muteRepository.deleteByGroupId(groupId);
groupRepository.delete(group);
}
@Transactional
public void adminDismiss(String groupId) {
muteRepository.deleteByGroupId(groupId);
groupRepository.deleteById(groupId);
}
public boolean isMemberMuted(String groupId, String userId) {
return muteRepository.findByGroupIdAndUserIdAndMutedUntilAfter(groupId, userId, LocalDateTime.now()).isPresent();
}
public List<String> memberIds(ImGroupEntity group) {
return fromJson(group.getMemberIds());
}
public List<String> adminIds(ImGroupEntity group) {
return fromJson(group.getAdminIds());
}
2026-04-21 22:07:29 +08:00
public List<ImGroupEntity> listByApp(String appId) {
return groupRepository.findByAppId(appId);
}
public List<ImGroupEntity> listUserGroups(String appId, String userId) {
return groupRepository.findUserGroups(appId, userId);
}
public List<ImGroupEntity> listPublicGroups(String appId, String keyword) {
String normalizedKeyword = keyword == null ? "" : keyword.trim().toLowerCase();
return groupRepository.findByAppId(appId).stream()
.filter(group -> "PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType())))
.filter(group -> normalizedKeyword.isBlank()
|| group.getName().toLowerCase().contains(normalizedKeyword)
|| group.getId().toLowerCase().contains(normalizedKeyword))
.toList();
}
@Transactional
public ImGroupJoinRequestEntity sendJoinRequest(String appId, String groupId, String requesterId, String remark) {
ImGroupEntity group = get(groupId);
if (!group.getAppId().equals(appId)) {
throw new BusinessException(403, "无权操作");
}
if (!"PUBLIC".equalsIgnoreCase(normalizeGroupType(group.getGroupType()))) {
throw new BusinessException(400, "该群不支持申请加入");
}
if (memberIds(group).contains(requesterId)) {
throw new BusinessException(400, "已经在群内");
}
return joinRequestRepository.findByAppIdAndGroupIdAndRequesterId(appId, groupId, requesterId)
.orElseGet(() -> {
ImGroupJoinRequestEntity entity = new ImGroupJoinRequestEntity();
entity.setId(UUID.randomUUID().toString());
entity.setAppId(appId);
entity.setGroupId(groupId);
entity.setRequesterId(requesterId);
entity.setRemark(remark);
entity.setStatus(ImGroupJoinRequestEntity.Status.PENDING.name());
entity.setCreatedAt(LocalDateTime.now());
return joinRequestRepository.save(entity);
});
}
public List<ImGroupJoinRequestEntity> listJoinRequests(String appId, String groupId, String operatorId) {
ImGroupEntity group = get(groupId);
ensureCanManage(group, operatorId);
return joinRequestRepository.findByAppIdAndGroupId(appId, groupId);
}
@Transactional
public ImGroupJoinRequestEntity acceptJoinRequest(String appId, String requestId, String operatorId) {
ImGroupJoinRequestEntity request = getJoinRequest(appId, requestId);
ImGroupEntity group = get(request.getGroupId());
ensureCanManage(group, operatorId);
request.setStatus(ImGroupJoinRequestEntity.Status.ACCEPTED.name());
request.setReviewedAt(LocalDateTime.now());
joinRequestRepository.save(request);
addMemberInternal(group, request.getRequesterId());
return request;
}
@Transactional
public ImGroupJoinRequestEntity rejectJoinRequest(String appId, String requestId, String operatorId) {
ImGroupJoinRequestEntity request = getJoinRequest(appId, requestId);
ImGroupEntity group = get(request.getGroupId());
ensureCanManage(group, operatorId);
request.setStatus(ImGroupJoinRequestEntity.Status.REJECTED.name());
request.setReviewedAt(LocalDateTime.now());
return joinRequestRepository.save(request);
}
2026-04-21 22:07:29 +08:00
private String toJson(List<String> list) {
try { return objectMapper.writeValueAsString(list); } catch (Exception e) { return "[]"; }
}
private List<String> fromJson(String json) {
try { return objectMapper.readValue(json, new TypeReference<>() {}); } catch (Exception e) { return new ArrayList<>(); }
}
private void ensureCanManage(ImGroupEntity group, String operatorId) {
List<String> admins = fromJson(group.getAdminIds());
if (!admins.contains(operatorId) && !group.getCreatorId().equals(operatorId)) {
throw new BusinessException(403, "无权操作");
}
}
private void addMemberInternal(ImGroupEntity group, String userId) {
List<String> members = fromJson(group.getMemberIds());
if (!members.contains(userId)) {
members.add(userId);
group.setMemberIds(toJson(members));
groupRepository.save(group);
}
}
private ImGroupJoinRequestEntity getJoinRequest(String appId, String requestId) {
ImGroupJoinRequestEntity request = joinRequestRepository.findById(requestId)
.orElseThrow(() -> new BusinessException(404, "加群申请不存在"));
if (!request.getAppId().equals(appId)) {
throw new BusinessException(403, "无权操作");
}
return request;
}
private String normalizeGroupType(String groupType) {
return (groupType == null || groupType.isBlank()) ? "WORK" : groupType.trim().toUpperCase();
}
2026-04-21 22:07:29 +08:00
}