新增语言学习后台服务
- 分类管理:支持分类的增删改查 - 单词管理:支持单词的增删改查,包含音标、释义、例句等字段 - 更新 README 项目说明 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
|
||||
"github.com/fish/language/backend/internal/models"
|
||||
"github.com/fish/language/backend/internal/repository/queries"
|
||||
)
|
||||
|
||||
const defaultPageSize = 20
|
||||
const maxPageSize = 100
|
||||
|
||||
// WordService handles business logic for words.
|
||||
type WordService struct {
|
||||
q *queries.Queries
|
||||
}
|
||||
|
||||
// NewWordService creates a new WordService.
|
||||
func NewWordService(q *queries.Queries) *WordService {
|
||||
return &WordService{q: q}
|
||||
}
|
||||
|
||||
// List returns a paginated list of words with optional filters.
|
||||
func (s *WordService) List(ctx context.Context, req models.ListWordsRequest) (models.ListWordsResponse, error) {
|
||||
page := req.Page
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize < 1 {
|
||||
pageSize = defaultPageSize
|
||||
}
|
||||
if pageSize > maxPageSize {
|
||||
pageSize = maxPageSize
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
listParams := queries.ListWordsParams{
|
||||
Limit: pageSize,
|
||||
Offset: offset,
|
||||
CategoryID: 0,
|
||||
Keyword: "",
|
||||
}
|
||||
countParams := queries.CountWordsParams{
|
||||
CategoryID: 0,
|
||||
Keyword: "",
|
||||
}
|
||||
if req.CategoryID != nil && *req.CategoryID > 0 {
|
||||
listParams.CategoryID = *req.CategoryID
|
||||
countParams.CategoryID = *req.CategoryID
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
listParams.Keyword = req.Keyword
|
||||
countParams.Keyword = req.Keyword
|
||||
}
|
||||
|
||||
rows, err := s.q.ListWords(ctx, listParams)
|
||||
if err != nil {
|
||||
return models.ListWordsResponse{}, err
|
||||
}
|
||||
total, err := s.q.CountWords(ctx, countParams)
|
||||
if err != nil {
|
||||
return models.ListWordsResponse{}, err
|
||||
}
|
||||
|
||||
words := make([]models.Word, len(rows))
|
||||
for i, r := range rows {
|
||||
words[i] = toWord(r)
|
||||
}
|
||||
|
||||
totalPages := int32(math.Ceil(float64(total) / float64(pageSize)))
|
||||
return models.ListWordsResponse{
|
||||
Words: words,
|
||||
Total: total,
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
TotalPages: totalPages,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Get returns a single word by id.
|
||||
func (s *WordService) Get(ctx context.Context, id int32) (models.Word, error) {
|
||||
row, err := s.q.GetWord(ctx, id)
|
||||
if err != nil {
|
||||
return models.Word{}, err
|
||||
}
|
||||
return toWord(row), nil
|
||||
}
|
||||
|
||||
// Create creates a new word.
|
||||
func (s *WordService) Create(ctx context.Context, req models.CreateWordRequest) (models.Word, error) {
|
||||
row, err := s.q.CreateWord(ctx, queries.CreateWordParams{
|
||||
CategoryID: req.CategoryID,
|
||||
Word: req.Word,
|
||||
Translation: req.Translation,
|
||||
Phonetic: req.Phonetic,
|
||||
ExampleSentence: req.ExampleSentence,
|
||||
AudioUrl: req.AudioURL,
|
||||
ImageUrl: req.ImageURL,
|
||||
SortOrder: req.SortOrder,
|
||||
})
|
||||
if err != nil {
|
||||
return models.Word{}, err
|
||||
}
|
||||
return toWord(row), nil
|
||||
}
|
||||
|
||||
// Update updates an existing word.
|
||||
func (s *WordService) Update(ctx context.Context, id int32, req models.UpdateWordRequest) (models.Word, error) {
|
||||
row, err := s.q.UpdateWord(ctx, queries.UpdateWordParams{
|
||||
ID: id,
|
||||
CategoryID: req.CategoryID,
|
||||
Word: req.Word,
|
||||
Translation: req.Translation,
|
||||
Phonetic: req.Phonetic,
|
||||
ExampleSentence: req.ExampleSentence,
|
||||
AudioUrl: req.AudioURL,
|
||||
ImageUrl: req.ImageURL,
|
||||
SortOrder: req.SortOrder,
|
||||
})
|
||||
if err != nil {
|
||||
return models.Word{}, err
|
||||
}
|
||||
return toWord(row), nil
|
||||
}
|
||||
|
||||
// Delete removes a word by id.
|
||||
func (s *WordService) Delete(ctx context.Context, id int32) error {
|
||||
return s.q.DeleteWord(ctx, id)
|
||||
}
|
||||
|
||||
func toWord(w queries.Word) models.Word {
|
||||
return models.Word{
|
||||
ID: w.ID,
|
||||
CategoryID: w.CategoryID,
|
||||
Word: w.Word,
|
||||
Translation: w.Translation,
|
||||
Phonetic: nullString(w.Phonetic),
|
||||
ExampleSentence: nullString(w.ExampleSentence),
|
||||
AudioURL: nullString(w.AudioUrl),
|
||||
ImageURL: nullString(w.ImageUrl),
|
||||
SortOrder: w.SortOrder,
|
||||
CreatedAt: w.CreatedAt,
|
||||
UpdatedAt: w.UpdatedAt,
|
||||
}
|
||||
}
|
||||
|
||||
func nullString(s *string) *string {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
v := *s
|
||||
return &v
|
||||
}
|
||||
Reference in New Issue
Block a user