Fix bootstrap meeting unread counts
This commit is contained in:
@@ -75,6 +75,10 @@ func (s testSecurityStore) CountUnreadSystemNotifications(types.TenantID, types.
|
|||||||
return 0, s.err
|
return 0, s.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s testSecurityStore) CountUnreadSystemNotificationsByEventPrefix(types.TenantID, types.Uid, string) (int, error) {
|
||||||
|
return 0, s.err
|
||||||
|
}
|
||||||
|
|
||||||
func (s testSecurityStore) MarkSystemNotificationRead(types.TenantID, types.Uid, string, time.Time) error {
|
func (s testSecurityStore) MarkSystemNotificationRead(types.TenantID, types.Uid, string, time.Time) error {
|
||||||
return s.err
|
return s.err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -490,6 +490,7 @@ type TenantBusinessAdapter interface {
|
|||||||
TenantSystemNotificationCreate(t.TenantID, *t.SystemNotification) error
|
TenantSystemNotificationCreate(t.TenantID, *t.SystemNotification) error
|
||||||
TenantSystemNotificationList(t.TenantID, t.Uid, string, int) ([]t.SystemNotification, string, error)
|
TenantSystemNotificationList(t.TenantID, t.Uid, string, int) ([]t.SystemNotification, string, error)
|
||||||
TenantSystemNotificationUnreadCount(t.TenantID, t.Uid) (int, error)
|
TenantSystemNotificationUnreadCount(t.TenantID, t.Uid) (int, error)
|
||||||
|
TenantSystemNotificationUnreadCountByEventPrefix(t.TenantID, t.Uid, string) (int, error)
|
||||||
TenantSystemNotificationMarkRead(t.TenantID, t.Uid, string, time.Time) error
|
TenantSystemNotificationMarkRead(t.TenantID, t.Uid, string, time.Time) error
|
||||||
|
|
||||||
TenantFriendRequestCreate(t.TenantID, *t.FriendRequest) (*t.FriendRequest, error)
|
TenantFriendRequestCreate(t.TenantID, *t.FriendRequest) (*t.FriendRequest, error)
|
||||||
|
|||||||
@@ -8793,6 +8793,33 @@ func (a *adapter) TenantSystemNotificationUnreadCount(id t.TenantID, uid t.Uid)
|
|||||||
return count, nil
|
return count, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *adapter) TenantSystemNotificationUnreadCountByEventPrefix(id t.TenantID, uid t.Uid, eventPrefix string) (int, error) {
|
||||||
|
if !a.tenantScopedDB {
|
||||||
|
tenantAdp, err := a.routedTenantAdapter(id)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if tenantAdp != a {
|
||||||
|
return tenantAdp.TenantSystemNotificationUnreadCountByEventPrefix(id, uid, eventPrefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eventPrefix = strings.TrimSpace(eventPrefix)
|
||||||
|
if err := validTenant(id); err != nil || uid.IsZero() || eventPrefix == "" {
|
||||||
|
return 0, t.ErrMalformed
|
||||||
|
}
|
||||||
|
ctx, cancel := a.getContext()
|
||||||
|
if cancel != nil {
|
||||||
|
defer cancel()
|
||||||
|
}
|
||||||
|
var count int
|
||||||
|
if err := a.db.GetContext(ctx, &count,
|
||||||
|
`SELECT COUNT(*) FROM im_system_notifications WHERE user_id=? AND read_at IS NULL AND event_type LIKE ?`,
|
||||||
|
decodeUID(uid), eventPrefix+"%"); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *adapter) TenantSystemNotificationMarkRead(id t.TenantID, uid t.Uid, eventID string, readAt time.Time) error {
|
func (a *adapter) TenantSystemNotificationMarkRead(id t.TenantID, uid t.Uid, eventID string, readAt time.Time) error {
|
||||||
if !a.tenantScopedDB {
|
if !a.tenantScopedDB {
|
||||||
tenantAdp, err := a.routedTenantAdapter(id)
|
tenantAdp, err := a.routedTenantAdapter(id)
|
||||||
|
|||||||
@@ -1711,6 +1711,10 @@ func serveIMBootstrap(wrt http.ResponseWriter, req *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
modulePayloads := workbenchModulePayloads(modules)
|
modulePayloads := workbenchModulePayloads(modules)
|
||||||
|
if err := populateWorkbenchModuleUnreadCounts(principal.TenantID, principal.Uid, modulePayloads); err != nil {
|
||||||
|
writeIMRelationError(wrt, req, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
entryBadges, entries, err := bootstrapEntryPayloads(principal.TenantID, principal.Uid, modulePayloads)
|
entryBadges, entries, err := bootstrapEntryPayloads(principal.TenantID, principal.Uid, modulePayloads)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
writeIMRelationError(wrt, req, err)
|
writeIMRelationError(wrt, req, err)
|
||||||
@@ -7855,6 +7859,26 @@ func workbenchModulePayloads(modules []types.WorkbenchModule) []map[string]any {
|
|||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func populateWorkbenchModuleUnreadCounts(tenantID types.TenantID, uid types.Uid, modules []map[string]any) error {
|
||||||
|
if store.Security == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, module := range modules {
|
||||||
|
code, _ := module["code"].(string)
|
||||||
|
switch code {
|
||||||
|
case "meetings":
|
||||||
|
count, err := store.Security.CountUnreadSystemNotificationsByEventPrefix(tenantID, uid, "meeting.")
|
||||||
|
if err != nil && !errors.Is(err, types.ErrNotFound) && !errors.Is(err, types.ErrUnsupported) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
module["unread_count"] = count
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func bootstrapEntryPayloads(tenantID types.TenantID, uid types.Uid, modules []map[string]any) (map[string]any, map[string]any, error) {
|
func bootstrapEntryPayloads(tenantID types.TenantID, uid types.Uid, modules []map[string]any) (map[string]any, map[string]any, error) {
|
||||||
newFriendsPending := 0
|
newFriendsPending := 0
|
||||||
if store.Relations != nil {
|
if store.Relations != nil {
|
||||||
|
|||||||
+13
-3
@@ -2406,14 +2406,21 @@ func TestServeIMBootstrapIncludesHomeCapabilities(t *testing.T) {
|
|||||||
oldWorkbench := store.Workbench
|
oldWorkbench := store.Workbench
|
||||||
oldRelations := store.Relations
|
oldRelations := store.Relations
|
||||||
oldThirdParty := store.ThirdPartyConfig
|
oldThirdParty := store.ThirdPartyConfig
|
||||||
|
readAt := time.Now().UTC()
|
||||||
security := &testSecurityStore{
|
security := &testSecurityStore{
|
||||||
realName: &types.RealNameVerification{Status: "verified"},
|
realName: &types.RealNameVerification{Status: "verified"},
|
||||||
unreadNotifications: 4,
|
notifications: []types.SystemNotification{
|
||||||
|
{EventID: "evt-meeting-created", EventType: "meeting.created"},
|
||||||
|
{EventID: "evt-meeting-updated", EventType: "meeting.updated"},
|
||||||
|
{EventID: "evt-friend-request", EventType: "friend.request.created"},
|
||||||
|
{EventID: "evt-daily-report", EventType: "daily_report.reminded"},
|
||||||
|
{EventID: "evt-meeting-read", EventType: "meeting.created", ReadAt: &readAt},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
relations := &testRelationsStore{pendingCount: 2}
|
relations := &testRelationsStore{pendingCount: 2}
|
||||||
workbench := &testWorkbenchStore{version: 9, modules: []types.WorkbenchModule{
|
workbench := &testWorkbenchStore{version: 9, modules: []types.WorkbenchModule{
|
||||||
{Code: "organization", Name: "组织架构", Route: "/organization", UnreadCount: 3, Enabled: true, Permission: "enabled"},
|
{Code: "organization", Name: "组织架构", Route: "/organization", UnreadCount: 3, Enabled: true, Permission: "enabled"},
|
||||||
{Code: "meetings", Name: "会议通知", Route: "/meetings", UnreadCount: 5, Enabled: false, Permission: "enabled"},
|
{Code: "meetings", Name: "会议通知", Route: "/meetings", UnreadCount: 0, Enabled: true, Permission: "enabled"},
|
||||||
{Code: "external", Name: "外链", Route: "https://example.com", UnreadCount: 9, Enabled: true, Permission: "enabled"},
|
{Code: "external", Name: "外链", Route: "https://example.com", UnreadCount: 9, Enabled: true, Permission: "enabled"},
|
||||||
}}
|
}}
|
||||||
store.Security = security
|
store.Security = security
|
||||||
@@ -2473,11 +2480,14 @@ func TestServeIMBootstrapIncludesHomeCapabilities(t *testing.T) {
|
|||||||
if modules[0].(map[string]any)["unread_count"].(float64) != 3 {
|
if modules[0].(map[string]any)["unread_count"].(float64) != 3 {
|
||||||
t.Fatalf("unexpected bootstrap module unread: %#v", modules[0])
|
t.Fatalf("unexpected bootstrap module unread: %#v", modules[0])
|
||||||
}
|
}
|
||||||
|
if modules[1].(map[string]any)["unread_count"].(float64) != 2 {
|
||||||
|
t.Fatalf("unexpected bootstrap meetings unread: %#v", modules[1])
|
||||||
|
}
|
||||||
badges := data["entry_badges"].(map[string]any)
|
badges := data["entry_badges"].(map[string]any)
|
||||||
if badges["new_friends_pending_count"].(float64) != 2 ||
|
if badges["new_friends_pending_count"].(float64) != 2 ||
|
||||||
badges["contacts_total_unread"].(float64) != 2 ||
|
badges["contacts_total_unread"].(float64) != 2 ||
|
||||||
badges["system_unread_count"].(float64) != 4 ||
|
badges["system_unread_count"].(float64) != 4 ||
|
||||||
badges["workbench_total_unread"].(float64) != 3 {
|
badges["workbench_total_unread"].(float64) != 5 {
|
||||||
t.Fatalf("unexpected bootstrap badges: %#v", badges)
|
t.Fatalf("unexpected bootstrap badges: %#v", badges)
|
||||||
}
|
}
|
||||||
entries := data["entries"].(map[string]any)
|
entries := data["entries"].(map[string]any)
|
||||||
|
|||||||
@@ -4258,6 +4258,7 @@ type UserSecurityPersistenceInterface interface {
|
|||||||
CreateSystemNotification(tenantID types.TenantID, notification *types.SystemNotification) error
|
CreateSystemNotification(tenantID types.TenantID, notification *types.SystemNotification) error
|
||||||
ListSystemNotifications(tenantID types.TenantID, uid types.Uid, cursor string, limit int) ([]types.SystemNotification, string, error)
|
ListSystemNotifications(tenantID types.TenantID, uid types.Uid, cursor string, limit int) ([]types.SystemNotification, string, error)
|
||||||
CountUnreadSystemNotifications(tenantID types.TenantID, uid types.Uid) (int, error)
|
CountUnreadSystemNotifications(tenantID types.TenantID, uid types.Uid) (int, error)
|
||||||
|
CountUnreadSystemNotificationsByEventPrefix(tenantID types.TenantID, uid types.Uid, eventPrefix string) (int, error)
|
||||||
MarkSystemNotificationRead(tenantID types.TenantID, uid types.Uid, eventID string, readAt time.Time) error
|
MarkSystemNotificationRead(tenantID types.TenantID, uid types.Uid, eventID string, readAt time.Time) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4421,6 +4422,17 @@ func (userSecurityMapper) CountUnreadSystemNotifications(tenantID types.TenantID
|
|||||||
return tenantAdp.TenantSystemNotificationUnreadCount(tenantID, uid)
|
return tenantAdp.TenantSystemNotificationUnreadCount(tenantID, uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (userSecurityMapper) CountUnreadSystemNotificationsByEventPrefix(tenantID types.TenantID, uid types.Uid, eventPrefix string) (int, error) {
|
||||||
|
if uid.IsZero() || strings.TrimSpace(eventPrefix) == "" {
|
||||||
|
return 0, types.ErrMalformed
|
||||||
|
}
|
||||||
|
tenantAdp, err := tenantBusinessAdapter(tenantID)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return tenantAdp.TenantSystemNotificationUnreadCountByEventPrefix(tenantID, uid, eventPrefix)
|
||||||
|
}
|
||||||
|
|
||||||
func (userSecurityMapper) MarkSystemNotificationRead(tenantID types.TenantID, uid types.Uid, eventID string, readAt time.Time) error {
|
func (userSecurityMapper) MarkSystemNotificationRead(tenantID types.TenantID, uid types.Uid, eventID string, readAt time.Time) error {
|
||||||
tenantAdp, err := tenantBusinessAdapter(tenantID)
|
tenantAdp, err := tenantBusinessAdapter(tenantID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -241,6 +241,7 @@ type testSecurityStore struct {
|
|||||||
nextCursor string
|
nextCursor string
|
||||||
unreadTenant types.TenantID
|
unreadTenant types.TenantID
|
||||||
unreadUser types.Uid
|
unreadUser types.Uid
|
||||||
|
unreadEventPrefix string
|
||||||
readTenant types.TenantID
|
readTenant types.TenantID
|
||||||
readUser types.Uid
|
readUser types.Uid
|
||||||
}
|
}
|
||||||
@@ -402,6 +403,19 @@ func (s *testSecurityStore) CountUnreadSystemNotifications(tenantID types.Tenant
|
|||||||
return count, s.err
|
return count, s.err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *testSecurityStore) CountUnreadSystemNotificationsByEventPrefix(tenantID types.TenantID, uid types.Uid, eventPrefix string) (int, error) {
|
||||||
|
s.unreadTenant = tenantID
|
||||||
|
s.unreadUser = uid
|
||||||
|
s.unreadEventPrefix = eventPrefix
|
||||||
|
count := 0
|
||||||
|
for _, notification := range s.notifications {
|
||||||
|
if notification.ReadAt == nil && strings.HasPrefix(notification.EventType, eventPrefix) {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count, s.err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *testSecurityStore) MarkSystemNotificationRead(tenantID types.TenantID, uid types.Uid, eventID string, readAt time.Time) error {
|
func (s *testSecurityStore) MarkSystemNotificationRead(tenantID types.TenantID, uid types.Uid, eventID string, readAt time.Time) error {
|
||||||
s.readTenant = tenantID
|
s.readTenant = tenantID
|
||||||
s.readUser = uid
|
s.readUser = uid
|
||||||
|
|||||||
Reference in New Issue
Block a user