feat: add class share drafts
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import UTC, datetime
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.models.chat import ChatSession, TopicSession
|
||||
from app.models.growth import ShareDraft, TopicSummary
|
||||
from app.models.user import User
|
||||
from app.services.entitlement_service import EntitlementService
|
||||
from app.services.growth_profile_service import GrowthProfileService, topic_summary_dict
|
||||
|
||||
|
||||
class ShareDraftService:
|
||||
@staticmethod
|
||||
def generate_for_session(db: Session, *, user: User, session: ChatSession) -> ShareDraft:
|
||||
entitlement = EntitlementService.active_entitlement(db, user)
|
||||
if not entitlement.allow_share_draft:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="当前权益暂不支持生成班级分享稿")
|
||||
|
||||
topic = _latest_topic(db, user=user, session=session)
|
||||
if topic is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="当前会话还没有可生成分享稿的主题")
|
||||
|
||||
summary = GrowthProfileService.generate_topic_summary(db, user=user, topic=topic)
|
||||
draft = ShareDraft(
|
||||
user_id=user.id,
|
||||
topic_session_id=topic.id,
|
||||
summary_id=summary.id,
|
||||
content=_render_share_draft(topic=topic, summary=summary),
|
||||
source="topic_summary",
|
||||
)
|
||||
topic.share_draft_generated = 1
|
||||
db.add_all([topic, draft])
|
||||
db.commit()
|
||||
db.refresh(draft)
|
||||
return draft
|
||||
|
||||
@staticmethod
|
||||
def list_user_drafts(db: Session, *, user: User, limit: int = 20) -> list[ShareDraft]:
|
||||
return list(
|
||||
db.scalars(
|
||||
select(ShareDraft)
|
||||
.where(ShareDraft.user_id == user.id)
|
||||
.order_by(ShareDraft.created_at.desc(), ShareDraft.id.desc())
|
||||
.limit(limit)
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def mark_copied(db: Session, *, user: User, draft_id: int) -> ShareDraft:
|
||||
draft = db.scalar(select(ShareDraft).where(ShareDraft.id == draft_id, ShareDraft.user_id == user.id))
|
||||
if draft is None:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="分享稿不存在")
|
||||
draft.copied = 1
|
||||
draft.copied_at = _now()
|
||||
db.add(draft)
|
||||
db.commit()
|
||||
db.refresh(draft)
|
||||
return draft
|
||||
|
||||
|
||||
def share_draft_dict(draft: ShareDraft) -> dict:
|
||||
return {
|
||||
"id": draft.id,
|
||||
"userId": draft.user_id,
|
||||
"topicSessionId": draft.topic_session_id,
|
||||
"summaryId": draft.summary_id,
|
||||
"content": draft.content,
|
||||
"source": draft.source,
|
||||
"copied": bool(draft.copied),
|
||||
"copiedAt": draft.copied_at,
|
||||
"createdAt": draft.created_at,
|
||||
}
|
||||
|
||||
|
||||
def _latest_topic(db: Session, *, user: User, session: ChatSession) -> TopicSession | None:
|
||||
return db.scalar(
|
||||
select(TopicSession)
|
||||
.where(TopicSession.user_id == user.id, TopicSession.chat_session_id == session.id)
|
||||
.order_by(TopicSession.updated_at.desc(), TopicSession.id.desc())
|
||||
.limit(1)
|
||||
)
|
||||
|
||||
|
||||
def _render_share_draft(*, topic: TopicSession, summary: TopicSummary) -> str:
|
||||
data = topic_summary_dict(summary)
|
||||
return (
|
||||
"【实修分享稿草稿】\n"
|
||||
"说明:这是根据我本次对话整理出的分享草稿,系统不会自动发送到任何群,"
|
||||
"我会按真实情况删改后再决定是否发到班级群。\n\n"
|
||||
"大家好,我想分享一下这次实修里看到的一点东西。\n\n"
|
||||
"1. 我这次观察到的议题\n"
|
||||
f"{topic.core_question or topic.title}\n\n"
|
||||
"2. 我看见了什么\n"
|
||||
f"{data.get('summary') or '(请用自己的话补充真实看见)'}\n\n"
|
||||
"3. 我感受到的情绪和身体反应\n"
|
||||
f"情绪:{data.get('emotions') or '(请补充)'}\n"
|
||||
f"身体:{data.get('bodyFeelings') or '(请补充)'}\n\n"
|
||||
"4. 我做了什么功课 / 准备继续做什么\n"
|
||||
f"{data.get('recommendedHomework') or '(请补充)'}\n\n"
|
||||
"5. 当下的一点变化\n"
|
||||
f"{data.get('insights') or '(请补充真实变化,不需要夸大)'}\n\n"
|
||||
"6. 我还在继续观察的方向\n"
|
||||
f"{data.get('nextObservation') or '(请补充)'}\n\n"
|
||||
"备注:这只是我的阶段性观察,不代表已经彻底解决,也不是建议别人照搬。"
|
||||
)
|
||||
|
||||
|
||||
def _now() -> datetime:
|
||||
return datetime.now(UTC).replace(tzinfo=None)
|
||||
Reference in New Issue
Block a user