Add group announcement edit permissions
This commit is contained in:
+1
-1
@@ -1500,7 +1500,7 @@ X-MeetingIM-APIKey: <预置的普通 API Key>
|
||||
**状态:新增。**
|
||||
|
||||
- 查询最新公告:`GET /v1/im/groups/{topic}/announcements/latest`;发布/更新:`PUT /v1/im/groups/{topic}/announcements/current`,Body `{"content":"...","version":1}`。
|
||||
- 返回 `announcement_id,content,author,version,published_at`。仅有权限角色可写,使用乐观锁防覆盖。
|
||||
- 返回 `announcement_id,topic,content,author,version,published_at,permissions.can_edit`;即使暂无公告也返回空公告和编辑权限。仅有权限角色可写,使用乐观锁防覆盖。
|
||||
- 发布成功后服务端生成 `group.announcement.updated` 群业务事件;顶部摘要使用接口返回数据,不解析聊天文本猜测。
|
||||
|
||||
### 6.8 群公告状态/邀请进群申请同时存在.png
|
||||
|
||||
+16
-3
@@ -5224,7 +5224,7 @@ func serveIMGroupAnnouncements(wrt http.ResponseWriter, req *http.Request, topic
|
||||
logs.Warn.Println("im group announcement latest: returning empty after store error", principal.TenantID, topic, err)
|
||||
ann = nil
|
||||
}
|
||||
writeIMSuccess(wrt, req, groupAnnouncementPayload(ann))
|
||||
writeIMSuccess(wrt, req, groupAnnouncementPayloadWithPermissions(topic, ann, selfSub))
|
||||
case tail == "announcements/current" && req.Method == http.MethodPut:
|
||||
if !(selfSub.ModeGiven & selfSub.ModeWant).IsAdmin() {
|
||||
writeIMError(wrt, req, http.StatusForbidden, 30005, imReasonPermissionDenied, "permission denied", nil)
|
||||
@@ -5257,7 +5257,7 @@ func serveIMGroupAnnouncements(wrt http.ResponseWriter, req *http.Request, topic
|
||||
return
|
||||
}
|
||||
dispatchIMGroupEvents(principal.TenantID)
|
||||
writeIMSuccess(wrt, req, groupAnnouncementPayload(announcement))
|
||||
writeIMSuccess(wrt, req, groupAnnouncementPayloadWithPermissions(topic, announcement, selfSub))
|
||||
case strings.HasPrefix(tail, "announcements/") && strings.HasSuffix(tail, "/read") && req.Method == http.MethodPost:
|
||||
announcementID := strings.TrimSuffix(strings.TrimPrefix(tail, "announcements/"), "/read")
|
||||
now := time.Now().UTC().Round(time.Millisecond)
|
||||
@@ -5272,7 +5272,7 @@ func serveIMGroupAnnouncements(wrt http.ResponseWriter, req *http.Request, topic
|
||||
logs.Warn.Println("im group announcement get: returning empty after store error", principal.TenantID, topic, err)
|
||||
ann = nil
|
||||
}
|
||||
writeIMSuccess(wrt, req, groupAnnouncementPayload(ann))
|
||||
writeIMSuccess(wrt, req, groupAnnouncementPayloadWithPermissions(topic, ann, selfSub))
|
||||
default:
|
||||
writeIMError(wrt, req, http.StatusMethodNotAllowed, 30002, imReasonMethodNotAllowed, "method not allowed", nil)
|
||||
}
|
||||
@@ -7097,6 +7097,19 @@ func groupAnnouncementPayload(ann *types.GroupAnnouncement) map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
func groupAnnouncementPayloadWithPermissions(topic string, ann *types.GroupAnnouncement, selfSub *types.Subscription) map[string]any {
|
||||
data := groupAnnouncementPayload(ann)
|
||||
data["topic"] = topic
|
||||
access := types.ModeNone
|
||||
if selfSub != nil {
|
||||
access = selfSub.ModeGiven & selfSub.ModeWant
|
||||
}
|
||||
data["permissions"] = map[string]any{
|
||||
"can_edit": access.IsAdmin(),
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func groupMutePayload(mute types.GroupMute) map[string]any {
|
||||
data := map[string]any{
|
||||
"user_id": mute.UserID.UserId(),
|
||||
|
||||
@@ -4350,6 +4350,39 @@ func TestServeIMGroupAnnouncementGovernanceJoinRequest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMGroupAnnouncementLatestIncludesEditPermission(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
oldStore := store.Store
|
||||
oldGroupBusiness := store.GroupBusiness
|
||||
oldSubs := store.Subs
|
||||
mockStore := expectIMBearer(t, ctrl, "token-1", 7, 42)
|
||||
store.Store = mockStore
|
||||
subs := mock_store.NewMockSubsPersistenceInterface(ctrl)
|
||||
store.Subs = subs
|
||||
store.GroupBusiness = &testGroupBusinessStore{}
|
||||
defer func() {
|
||||
store.Store = oldStore
|
||||
store.Subs = oldSubs
|
||||
store.GroupBusiness = oldGroupBusiness
|
||||
}()
|
||||
selfSub := &types.Subscription{TenantID: 7, User: types.Uid(42).String(), Topic: "grpAlpha", ModeWant: types.ModeCFull, ModeGiven: types.ModeCFull}
|
||||
subs.EXPECT().Get(types.TenantID(7), "grpAlpha", types.Uid(42), false).Return(selfSub, nil)
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/v1/im/groups/grpAlpha/announcements/latest", nil)
|
||||
req.Header.Set("Authorization", "Bearer token-1")
|
||||
rec := httptest.NewRecorder()
|
||||
serveIMGroupResource(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Fatalf("latest announcement status: got %d body=%s", rec.Code, rec.Body.String())
|
||||
}
|
||||
data := decodeIMHTTPResponse(t, rec).Data.(map[string]any)
|
||||
permissions := data["permissions"].(map[string]any)
|
||||
if data["topic"] != "grpAlpha" || data["announcement_id"] != "" || permissions["can_edit"] != true {
|
||||
t.Fatalf("unexpected latest announcement response: %#v", data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServeIMGroupAdminsMutesAndOwnerTransfer(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
Reference in New Issue
Block a user