From 85f6b7e42bf62612e598f6f4b5bf7bd3d770e3d2 Mon Sep 17 00:00:00 2001 From: aaron <> Date: Sat, 11 Apr 2026 21:15:42 +0800 Subject: [PATCH] update --- .claude/settings.local.json | 5 ++- backend/.env.example | 2 +- backend/app/api/resources.py | 10 +++--- backend/app/services/announcement_service.py | 9 +++-- backend/app/services/email_service.py | 36 +++++++++++++++++++ backend/app/services/notification_service.py | 37 ++++++++++++++++---- backend/app/services/schedule_service.py | 8 ++++- backend/app/services/timeline_service.py | 13 +++++++ 8 files changed, 104 insertions(+), 16 deletions(-) diff --git a/.claude/settings.local.json b/.claude/settings.local.json index bc4d8bc..1be01db 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -10,7 +10,10 @@ "Bash(npx shadcn@latest add:*)", "Bash(npm --prefix /Users/aaron/source_code/hku-icb-class/frontend run build)", "Bash(/Users/aaron/source_code/hku-icb-class/frontend/node_modules/.bin/tsc --noEmit --project /Users/aaron/source_code/hku-icb-class/frontend/tsconfig.json)", - "Bash(test:*)" + "Bash(test:*)", + "Bash(/Users/aaron/source_code/hku-icb-class/backend/.venv/bin/python:*)", + "Bash(.venv/bin/python:*)", + "Bash(backend/.venv/bin/pip show:*)" ] } } diff --git a/backend/.env.example b/backend/.env.example index c3a1fde..c3da64b 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -20,7 +20,7 @@ CH_COS_BASE_URL=https://hku-icb-1311994147.cos.ap-guangzhou.myqcloud.com # SMTP Email CH_SMTP_HOST=gz-smtp.qcloudmail.com CH_SMTP_PORT=465 -CH_SMTP_USER=noreply +CH_SMTP_USER=noreply@hkuicb.info CH_SMTP_PASSWORD=hX2gqEPjaRhULXF69 CH_SMTP_FROM_EMAIL=noreply@hkuicb.info CH_SMTP_FROM_NAME=HKU ICB Class Hub diff --git a/backend/app/api/resources.py b/backend/app/api/resources.py index e3f231f..6d8f83d 100644 --- a/backend/app/api/resources.py +++ b/backend/app/api/resources.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, Depends, HTTPException, UploadFile, File +from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form from sqlalchemy.ext.asyncio import AsyncSession from app.core.deps import require_role @@ -76,11 +76,11 @@ async def get_resources( @router.post("/", response_model=ResourceOut) async def upload_new_resource( - title: str, - category: str, file: UploadFile = File(...), - description: str | None = None, - class_id: int | None = None, + title: str = Form(...), + category: str = Form(...), + description: str | None = Form(None), + class_id: int | None = Form(None), user: User = Depends(require_role("super_admin", "class_admin")), db: AsyncSession = Depends(get_db), ): diff --git a/backend/app/services/announcement_service.py b/backend/app/services/announcement_service.py index 424a849..2e831d2 100644 --- a/backend/app/services/announcement_service.py +++ b/backend/app/services/announcement_service.py @@ -21,10 +21,15 @@ async def create_announcement( await db.commit() await db.refresh(announcement) - # Send notifications to class members + # Send notifications + email to class members + content_preview = (data.content[:100] + "...") if data.content and len(data.content) > 100 else (data.content or "") await create_notifications_for_class( db, class_id, "announcement", f"新公告: {data.title}", - related_id=announcement.id, exclude_user_id=author_id, + content=content_preview, + related_id=announcement.id, + email_subject=f"HKU ICB - 新公告: {data.title}", + email_body=f"
{content_preview}
" if content_preview else None, + email_action_path="/announcements", ) return announcement diff --git a/backend/app/services/email_service.py b/backend/app/services/email_service.py index 43b4cb4..28f0b20 100644 --- a/backend/app/services/email_service.py +++ b/backend/app/services/email_service.py @@ -57,3 +57,39 @@ async def send_approval_notification(student_email: str, approved: bool): await send_email( student_email, f"HKU ICB: Registration {status_text.capitalize()}", html ) + + +async def send_class_notification_email( + emails: list[str], + subject: str, + title: str, + body: str, + action_url: str | None = None, +): + """Send a styled notification email to class members.""" + action_html = "" + if action_url: + action_html = f""" +{data.title}
时间: {time_str}{location_info}
", + email_action_path="/schedule", ) return item diff --git a/backend/app/services/timeline_service.py b/backend/app/services/timeline_service.py index 0aef37d..c092e3b 100644 --- a/backend/app/services/timeline_service.py +++ b/backend/app/services/timeline_service.py @@ -7,6 +7,7 @@ from sqlalchemy.orm import selectinload from app.db.models import Timeline, User from app.schemas.timeline import TimelineCreate, TimelineUpdate +from app.services.notification_service import create_notifications_for_class async def create_timeline( @@ -21,6 +22,18 @@ async def create_timeline( db.add(post) await db.commit() await db.refresh(post) + + # Send notifications + email to class members + content_preview = (data.content[:100] + "...") if data.content and len(data.content) > 100 else (data.content or "") + await create_notifications_for_class( + db, class_id, "timeline", f"新大事件: {data.title}", + content=content_preview, + related_id=post.id, + email_subject=f"HKU ICB - 新大事件: {data.title}", + email_body=f"{content_preview}
" if content_preview else None, + email_action_path="/timeline", + ) + return post