feat(schedule): add course table screens and navigation
Add complete schedule functionality including: - Schedule screen with weekly course table view - Course detail screen with transparent modal presentation - New ScheduleStack navigator integrated into main tab bar - Schedule service for API interactions - Type definitions for course entities Also includes bug fixes for group invite/request handlers to include required groupId parameter.
This commit is contained in:
@@ -73,9 +73,20 @@ func (s *PostService) Create(ctx context.Context, userID, title, content string,
|
||||
}
|
||||
|
||||
func (s *PostService) reviewPostAsync(postID, userID, title, content string, images []string) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("[ERROR] Panic in post moderation async flow, fallback publish post=%s panic=%v", postID, r)
|
||||
if err := s.updateModerationStatusWithRetry(postID, model.PostStatusPublished, "", "system"); err != nil {
|
||||
log.Printf("[WARN] Failed to publish post %s after panic recovery: %v", postID, err)
|
||||
return
|
||||
}
|
||||
s.invalidatePostCaches(postID)
|
||||
}
|
||||
}()
|
||||
|
||||
// 未启用AI时,直接发布
|
||||
if s.postAIService == nil || !s.postAIService.IsEnabled() {
|
||||
if err := s.postRepo.UpdateModerationStatus(postID, model.PostStatusPublished, "", "system"); err != nil {
|
||||
if err := s.updateModerationStatusWithRetry(postID, model.PostStatusPublished, "", "system"); err != nil {
|
||||
log.Printf("[WARN] Failed to publish post without AI moderation: %v", err)
|
||||
} else {
|
||||
s.invalidatePostCaches(postID)
|
||||
@@ -87,7 +98,7 @@ func (s *PostService) reviewPostAsync(postID, userID, title, content string, ima
|
||||
if err != nil {
|
||||
var rejectedErr *PostModerationRejectedError
|
||||
if errors.As(err, &rejectedErr) {
|
||||
if updateErr := s.postRepo.UpdateModerationStatus(postID, model.PostStatusRejected, rejectedErr.UserMessage(), "ai"); updateErr != nil {
|
||||
if updateErr := s.updateModerationStatusWithRetry(postID, model.PostStatusRejected, rejectedErr.UserMessage(), "ai"); updateErr != nil {
|
||||
log.Printf("[WARN] Failed to reject post %s: %v", postID, updateErr)
|
||||
} else {
|
||||
s.invalidatePostCaches(postID)
|
||||
@@ -97,7 +108,7 @@ func (s *PostService) reviewPostAsync(postID, userID, title, content string, ima
|
||||
}
|
||||
|
||||
// 规则审核不可用时,降级为发布,避免长时间pending
|
||||
if updateErr := s.postRepo.UpdateModerationStatus(postID, model.PostStatusPublished, "", "system"); updateErr != nil {
|
||||
if updateErr := s.updateModerationStatusWithRetry(postID, model.PostStatusPublished, "", "system"); updateErr != nil {
|
||||
log.Printf("[WARN] Failed to publish post %s after moderation error: %v", postID, updateErr)
|
||||
} else {
|
||||
s.invalidatePostCaches(postID)
|
||||
@@ -106,7 +117,7 @@ func (s *PostService) reviewPostAsync(postID, userID, title, content string, ima
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.postRepo.UpdateModerationStatus(postID, model.PostStatusPublished, "", "ai"); err != nil {
|
||||
if err := s.updateModerationStatusWithRetry(postID, model.PostStatusPublished, "", "ai"); err != nil {
|
||||
log.Printf("[WARN] Failed to publish post %s: %v", postID, err)
|
||||
return
|
||||
}
|
||||
@@ -127,6 +138,26 @@ func (s *PostService) reviewPostAsync(postID, userID, title, content string, ima
|
||||
}
|
||||
}
|
||||
|
||||
func (s *PostService) updateModerationStatusWithRetry(postID string, status model.PostStatus, rejectReason string, reviewedBy string) error {
|
||||
const maxAttempts = 3
|
||||
const retryDelay = 200 * time.Millisecond
|
||||
|
||||
var lastErr error
|
||||
for attempt := 1; attempt <= maxAttempts; attempt++ {
|
||||
if err := s.postRepo.UpdateModerationStatus(postID, status, rejectReason, reviewedBy); err != nil {
|
||||
lastErr = err
|
||||
if attempt < maxAttempts {
|
||||
log.Printf("[WARN] UpdateModerationStatus failed post=%s attempt=%d/%d err=%v", postID, attempt, maxAttempts, err)
|
||||
time.Sleep(time.Duration(attempt) * retryDelay)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return lastErr
|
||||
}
|
||||
|
||||
func (s *PostService) invalidatePostCaches(postID string) {
|
||||
cache.InvalidatePostDetail(s.cache, postID)
|
||||
cache.InvalidatePostList(s.cache)
|
||||
|
||||
Reference in New Issue
Block a user