Add group announcement edit permissions

This commit is contained in:
richjohnrambo
2026-09-04 11:09:29 +08:00
parent 62af772260
commit a191ca0c93
3 changed files with 50 additions and 4 deletions
+1 -1
View File
@@ -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
View File
@@ -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(),
+33
View File
@@ -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()