liyong
2 小时以前 7a23c450f3ac85de7dca1b908de273ff636ce218
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
package cn.iocoder.yudao.module.im.controller.admin.group;
 
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.im.controller.admin.group.vo.request.ImGroupRequestApplyReqVO;
import cn.iocoder.yudao.module.im.controller.admin.group.vo.request.ImGroupRequestRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupRequestDO;
import cn.iocoder.yudao.module.im.enums.group.ImGroupMemberRoleEnum;
import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService;
import cn.iocoder.yudao.module.im.service.group.ImGroupRequestService;
import cn.iocoder.yudao.module.im.service.group.ImGroupService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
 
import java.util.*;
import java.util.stream.Stream;
 
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
 
/**
 * IM 加群申请 Controller
 *
 * @author 芋道源码
 */
@Tag(name = "管理后台 - IM 加群申请")
@RestController
@RequestMapping("/im/group-request")
@Validated
public class ImGroupRequestController {
 
    @Resource
    private ImGroupRequestService groupRequestService;
    @Resource
    private ImGroupService groupService;
    @Resource
    private ImGroupMemberService groupMemberService;
 
    @Resource
    private AdminUserApi adminUserApi;
 
    @PostMapping("/apply")
    @Operation(summary = "申请加群")
    public CommonResult<Long> applyJoinGroup(@Valid @RequestBody ImGroupRequestApplyReqVO reqVO) {
        ImGroupRequestDO request = groupRequestService.applyJoinGroup(getLoginUserId(), reqVO);
        return success(request != null ? request.getId() : null);
    }
 
    @PutMapping("/agree")
    @Operation(summary = "同意加群申请(群主或管理员)")
    @Parameter(name = "id", description = "申请编号", required = true, example = "1024")
    public CommonResult<Boolean> agreeGroupRequest(
            @RequestParam("id") @NotNull(message = "申请编号不能为空") Long id) {
        groupRequestService.agreeGroupRequest(getLoginUserId(), id);
        return success(true);
    }
 
    @PutMapping("/refuse")
    @Operation(summary = "拒绝加群申请(群主或管理员)")
    public CommonResult<Boolean> refuseGroupRequest(
            @RequestParam("id") @NotNull(message = "申请编号不能为空") Long id,
            @RequestParam(value = "handleContent", required = false)
            @Size(max = 255, message = "处理理由最多 255 个字符") String handleContent) {
        groupRequestService.refuseGroupRequest(getLoginUserId(), id, handleContent);
        return success(true);
    }
 
    @GetMapping("/unhandled-list")
    @Operation(summary = "查询「我管理的所有群」下的未处理加群申请列表(不分页);前端 store 据此派生横幅红点 + Drawer 列表")
    public CommonResult<List<ImGroupRequestRespVO>> getUnhandledRequestList() {
        List<ImGroupRequestDO> list = groupRequestService.getUnhandledRequestListByOwnerOrAdmin(getLoginUserId());
        return success(buildVOList(list));
    }
 
    @GetMapping("/pull")
    @Operation(summary = "增量拉取「我管理的群」下的加群申请(重连 / 离线补偿)")
    @Parameters({
            @Parameter(name = "lastUpdateTime", description = "上次拉取到的最新更新时间(毫秒时间戳);首次拉取不传"),
            @Parameter(name = "lastId", description = "上次拉取到的最后一条记录 id;首次拉取不传"),
            @Parameter(name = "limit", description = "单次拉取条数", required = true)
    })
    public CommonResult<List<ImGroupRequestRespVO>> pullMyGroupRequestList(
            @RequestParam(value = "lastUpdateTime", required = false) Long lastUpdateTime,
            @RequestParam(value = "lastId", required = false) Long lastId,
            @RequestParam("limit") @Min(1) @Max(200) Integer limit) {
        List<ImGroupRequestDO> list = groupRequestService.pullGroupRequestList(
                getLoginUserId(), lastUpdateTime, lastId, limit);
        return success(buildVOList(list));
    }
 
    @GetMapping("/list-by-group")
    @Operation(summary = "查询指定群下的全部加群申请(含已处理);仅群主 / 管理员可查")
    @Parameter(name = "groupId", description = "群编号", required = true, example = "1024")
    public CommonResult<List<ImGroupRequestRespVO>> getGroupRequestListByGroupId(
            @RequestParam("groupId") @NotNull(message = "群编号不能为空") Long groupId) {
        List<ImGroupRequestDO> list = groupRequestService.getGroupRequestListByGroupId(getLoginUserId(), groupId);
        return success(buildVOList(list));
    }
 
    @GetMapping("/get")
    @Operation(summary = "按 id 单查申请记录(带越权过滤;WebSocket 通知到达后用)")
    @Parameter(name = "id", description = "申请记录编号", required = true)
    public CommonResult<ImGroupRequestRespVO> getGroupRequest(@RequestParam("id") Long id) {
        ImGroupRequestDO request = groupRequestService.getGroupRequest(id);
        if (request == null) {
            return success(null);
        }
        // 越权过滤:申请人 / 邀请人 / 群主 / 管理员之外,当不存在返回 null
        Long currentUserId = getLoginUserId();
        boolean canSee = ObjUtil.equal(request.getUserId(), currentUserId)
                || ObjUtil.equal(request.getInviterUserId(), currentUserId)
                || isGroupOwnerOrAdmin(request.getGroupId(), currentUserId);
        if (!canSee) {
            return success(null);
        }
 
        // 转换并返回
        return success(CollUtil.getFirst(buildVOList(Collections.singletonList(request))));
    }
 
    /**
     * 当前用户是否该群的有效群主 / 管理员
     */
    private boolean isGroupOwnerOrAdmin(Long groupId, Long userId) {
        ImGroupMemberDO member = groupMemberService.getGroupMember(groupId, userId);
        return member != null
                && !CommonStatusEnum.DISABLE.getStatus().equals(member.getStatus())
                && ImGroupMemberRoleEnum.isOwnerOrAdmin(member.getRole());
    }
 
    /** 申请记录列表批量转 VO + 关联回填用户 / 群信息 */
    private List<ImGroupRequestRespVO> buildVOList(List<ImGroupRequestDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 1. 聚合 user / inviter 用户信息;convertSetByFlatMap 内部已过滤 null
        Set<Long> userIds = convertSetByFlatMap(list,
                request -> Stream.of(request.getUserId(), request.getInviterUserId()));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
        // 2. 聚合群信息(封禁 / 解散群也要回填,便于前端展示历史)
        Set<Long> groupIds = convertSet(list, ImGroupRequestDO::getGroupId);
        Map<Long, ImGroupDO> groupMap = groupService.getGroupMap(groupIds);
        return convertList(list, request -> {
            ImGroupRequestRespVO vo = BeanUtils.toBean(request, ImGroupRequestRespVO.class);
            MapUtils.findAndThen(userMap, request.getUserId(), user ->
                    vo.setUserNickname(user.getNickname()).setUserAvatar(user.getAvatar()));
            MapUtils.findAndThen(userMap, request.getInviterUserId(), user ->
                    vo.setInviterNickname(user.getNickname()).setInviterAvatar(user.getAvatar()));
            MapUtils.findAndThen(groupMap, request.getGroupId(), group ->
                    vo.setGroupName(group.getName()).setGroupAvatar(group.getAvatar()));
            return vo;
        });
    }
 
}