Ship native iOS app, Wiki/TTS backend, and skeleton loading UX.

Replace the WebView shell with SwiftUI screens, add account-scoped Wiki and TTS APIs with adaptive review and photo scan support, and keep web/iOS pages usable while data loads asynchronously.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-06-08 15:13:59 +08:00
parent db5465c646
commit 05e173b293
115 changed files with 12709 additions and 1212 deletions
+60 -11
View File
@@ -6,6 +6,7 @@ from sqlalchemy.orm import Session
from models import QuizRecord, User, Word
from schemas import CoachSessionResponse, CoachTurnResponse, CoachWordBrief, MemoryToken
from services.adaptive_review_service import adaptive_review_service, retrievability
from services.memory_visual_service import (
memory_visual_service,
parse_iso,
@@ -15,6 +16,7 @@ from services.memory_visual_service import (
word_zh,
)
from services.quiz_service import quiz_service
from services.text_utils import en_answers_match
from services.word_service import word_service
@@ -225,12 +227,16 @@ class MemoryCoachService:
return CoachSessionResponse(words=[], total=0)
book_service.ensure_user_book_words(db, user, book)
practice = book_service.practice_settings_dict(db, user, book)
words = quiz_service.select_book_words(db, user, book, practice["daily_target"])
book_id = book.id
words = quiz_service.select_coach_words(
db, user, book_id, practice["daily_target"]
)
else:
settings = quiz_service.get_settings(db, user)
words = quiz_service.select_accumulation_words(db, user, settings.daily_target)
book_id = 0
words = quiz_service.select_coach_words(
db, user, book_id, settings.daily_target
)
if not words:
return CoachSessionResponse(words=[], total=0)
@@ -245,6 +251,8 @@ class MemoryCoachService:
retention_now=risk_map.get(w.id, _retention_now(w)),
mastery_score=w.mastery_score,
status=w.status,
retrievability=round(retrievability(w) * 100.0, 1),
next_review_at=w.next_review_at,
)
for w in words
]
@@ -272,6 +280,9 @@ class MemoryCoachService:
word_complete = False
next_stage = stage
input_hint = f"请输入「{zh}」的英文"
review_message: Optional[str] = None
review_retrievability: Optional[float] = None
next_review_at: Optional[str] = None
if stage == "intro":
next_stage = "derive"
@@ -283,8 +294,10 @@ class MemoryCoachService:
expect_input = True
next_stage = "derive"
else:
is_correct = answer.lower() == en.lower()
is_correct = en_answers_match(answer, en)
if is_correct:
attempts = hints_used + 1
before_r = retrievability(word)
tokens = self._reveal_all_v(tokens)
messages.append(f"正确:{en}")
quiz_service.submit_answer(
@@ -296,10 +309,35 @@ class MemoryCoachService:
en,
duration_seconds,
)
update = adaptive_review_service.update_word(
word,
is_correct=True,
attempts=attempts,
hints_used=hints_used,
duration_seconds=duration_seconds,
observed_retrievability=before_r,
)
db.commit()
if adaptive_review_service.is_plan_completed(word):
review_message = "本词已练会,已移出当前练习计划;可在学习设置中重置后继续复习"
else:
review_message = update.message
review_retrievability = update.retrievability
next_review_at = update.next_review_at
quiz_recorded = True
word_complete = True
next_stage = "done"
else:
before_r = retrievability(word)
quiz_service.record_attempt(
db,
user,
word.id,
"memory_coach",
answer,
en,
duration_seconds,
)
hints_used += 1
tokens = self.build_tokens(db, word, reveal_extra=hints_used)
tokens = self._reveal_q_tokens(tokens, 1)
@@ -308,15 +346,23 @@ class MemoryCoachService:
if hidden_left == 0:
tokens = self._reveal_all_v(tokens)
messages.append(f"答案:{en}")
quiz_service.submit_answer(
db,
user,
word.id,
"memory_coach",
answer,
en,
duration_seconds,
update = adaptive_review_service.update_word(
word,
is_correct=False,
attempts=hints_used,
hints_used=hints_used,
duration_seconds=duration_seconds,
observed_retrievability=before_r,
)
word.consecutive_correct_count = 0
word.status = "weak"
word.last_reviewed_at = datetime.now(timezone.utc).strftime(
"%Y-%m-%dT%H:%M:%SZ"
)
db.commit()
review_message = update.message
review_retrievability = update.retrievability
next_review_at = update.next_review_at
quiz_recorded = True
word_complete = True
next_stage = "done"
@@ -346,6 +392,9 @@ class MemoryCoachService:
hints_used=hints_used,
target_en=en if word_complete else None,
target_zh=zh if word_complete else None,
review_message=review_message,
retrievability=review_retrievability,
next_review_at=next_review_at,
)