feat(layout): 更新应用布局和UI组件样式

- 重构App.vue中的侧边栏布局,更新Logo设计为带有标识和副标题的新样式
- 调整顶部导航栏,增加标题区域显示当前路由标题和日期
- 修改菜单项配置,更新导航标签为更直观的中文描述
- 在Home.vue中替换原有的仪表板为新的Hero卡片和项目进展展示
- 更新Memory.vue中的学习界面,添加学习计划设置和多阶段学习模式
- 集成新的API端点路径,将baseURL从/api调整为/api/v1
- 调整整体视觉风格,包括颜色主题、字体家族和响应式布局
- 更新数据库模型以支持词库功能,添加相关的数据迁移和种子数据
- 调整认证系统的用户ID类型从整型到字符串的变更
- 更改前端构建工具从npm到pnpm,并更新相应的Dockerfile配置
This commit is contained in:
2026-02-27 16:16:57 +08:00
parent a62c2a3aa1
commit 613ce02e9a
47 changed files with 3164 additions and 2579 deletions

View File

@@ -63,9 +63,9 @@ func sign(data string) string {
return base64.RawURLEncoding.EncodeToString(mac.Sum(nil))
}
func makeToken(uid int64) string {
func makeToken(uid string) string {
exp := time.Now().Add(7 * 24 * time.Hour).Unix()
payload := fmt.Sprintf("%d:%d", uid, exp)
payload := fmt.Sprintf("%s:%d", uid, exp)
encPayload := base64.RawURLEncoding.EncodeToString([]byte(payload))
sig := sign(encPayload)
return encPayload + "." + sig
@@ -84,35 +84,35 @@ func (s *AuthService) Login(req request.LoginRequest) (string, *model.User, erro
return makeToken(user.ID), &user, nil
}
func ParseToken(tokenString string) (int64, error) {
func ParseToken(tokenString string) (string, error) {
parts := strings.Split(tokenString, ".")
if len(parts) != 2 {
return 0, errors.New("token无效")
return "", errors.New("token无效")
}
encPayload, gotSig := parts[0], parts[1]
if sign(encPayload) != gotSig {
return 0, errors.New("token无效")
return "", errors.New("token无效")
}
payloadBytes, err := base64.RawURLEncoding.DecodeString(encPayload)
if err != nil {
return 0, errors.New("token无效")
return "", errors.New("token无效")
}
payloadParts := strings.Split(string(payloadBytes), ":")
if len(payloadParts) != 2 {
return 0, errors.New("token无效")
return "", errors.New("token无效")
}
uid, err := strconv.ParseInt(payloadParts[0], 10, 64)
if err != nil {
return 0, errors.New("token无效")
uid := payloadParts[0]
if uid == "" {
return "", errors.New("token无效")
}
exp, err := strconv.ParseInt(payloadParts[1], 10, 64)
if err != nil {
return 0, errors.New("token无效")
return "", errors.New("token无效")
}
if time.Now().Unix() > exp {
return 0, errors.New("token已过期")
return "", errors.New("token已过期")
}
return uid, nil

View File

@@ -176,7 +176,7 @@ func (s *WordService) DownloadAudio(url, filePath string) error {
}
// 保存单词到数据库
func (s *WordService) SaveWord(userID int64, word string, youdaoResp *model.YoudaoResponse) (*model.Word, error) {
func (s *WordService) SaveWord(userID string, word string, youdaoResp *model.YoudaoResponse) (*model.Word, error) {
// 检查单词是否已存在
if w, err := s.wordRepo.FindByWord(word); err == nil {
// 单词已存在,更新记忆记录
@@ -262,6 +262,8 @@ func (s *WordService) SaveWord(userID int64, word string, youdaoResp *model.Youd
// 创建新单词
newWord := model.Word{
BookID: model.DefaultWordBookID,
HeadWord: word,
Word: word,
PhoneticUK: phoneticUK,
PhoneticUS: phoneticUS,
@@ -284,7 +286,7 @@ func (s *WordService) SaveWord(userID int64, word string, youdaoResp *model.Youd
}
// 创建记忆记录
func (s *WordService) createMemoryRecord(userID, wordID int64) error {
func (s *WordService) createMemoryRecord(userID, wordID string) error {
record := model.MemoryRecord{
WordID: wordID,
UserID: userID,
@@ -296,7 +298,7 @@ func (s *WordService) createMemoryRecord(userID, wordID int64) error {
}
// 更新记忆记录
func (s *WordService) updateMemoryRecord(userID, wordID int64, correct bool) error {
func (s *WordService) updateMemoryRecord(userID, wordID string, correct bool) error {
record, err := s.memoryRepo.FindByWord(userID, wordID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -333,7 +335,7 @@ func (s *WordService) updateMemoryRecord(userID, wordID int64, correct bool) err
}
// 获取待复习单词
func (s *WordService) GetReviewWords(userID int64, mode string, limit int) ([]model.MemoryRecord, error) {
func (s *WordService) GetReviewWords(userID string, mode string, limit int) ([]model.MemoryRecord, error) {
records, err := s.memoryRepo.Due(userID, limit)
if err != nil {
return nil, err
@@ -375,7 +377,7 @@ func containsAny(def string, ans string) bool {
}
// 提交复习答案
func (s *WordService) SubmitReviewAnswer(userID int64, req request.ReviewAnswerRequest) (*response.ReviewResult, error) {
func (s *WordService) SubmitReviewAnswer(userID string, req request.ReviewAnswerRequest) (*response.ReviewResult, error) {
record, err := s.memoryRepo.FindByID(userID, req.RecordID)
if err != nil {
return nil, err
@@ -416,8 +418,12 @@ func (s *WordService) GetAllWords(limit, offset int, query string) ([]model.Word
return s.wordRepo.List(limit, offset, strings.TrimSpace(query))
}
func (s *WordService) GetWordByID(id string) (*model.Word, error) {
return s.wordRepo.FindByID(id)
}
// 获取记忆统计
func (s *WordService) GetStatistics(userID int64) (map[string]interface{}, error) {
func (s *WordService) GetStatistics(userID string) (map[string]interface{}, error) {
totalWords, masteredWords, needReview, todayReviewed := s.memoryRepo.CountOverview(userID)
return map[string]interface{}{
@@ -428,7 +434,7 @@ func (s *WordService) GetStatistics(userID int64) (map[string]interface{}, error
}, nil
}
func (s *WordService) CreateStudySession(userID int64, limit int) ([]model.Word, error) {
func (s *WordService) CreateStudySession(userID string, limit int) ([]model.Word, error) {
if limit <= 0 || limit > 50 {
limit = 10
}
@@ -442,9 +448,9 @@ func (s *WordService) CreateStudySession(userID int64, limit int) ([]model.Word,
return words, nil
}
func (s *WordService) SubmitStudyAnswer(userID int64, req request.SubmitStudyAnswerRequest) (*response.ReviewResult, error) {
func (s *WordService) SubmitStudyAnswer(userID string, req request.SubmitStudyAnswerRequest) (*response.ReviewResult, error) {
reviewReq := request.ReviewAnswerRequest{
RecordID: 0,
RecordID: "",
Answer: req.Answer,
Mode: req.Mode,
}