Files
proto_go_dart_demo/backend/service/note_service.go
2026-02-23 13:02:22 +03:00

148 lines
3.2 KiB
Go

package service
import (
"context"
"sync"
"time"
notesv1 "backend/gen/notes/v1"
"github.com/google/uuid"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"google.golang.org/protobuf/types/known/timestamppb"
)
// NoteService — реализация gRPC сервиса, сгенерированного из proto.
// Типы notesv1.Note, notesv1.CreateNoteRequest и т.д. —
// это всё автоматически сгенерированные из proto файла структуры.
// Если ты изменишь proto (добавишь поле, переименуешь) и
// перегенерируешь — этот код перестанет компилироваться,
// пока ты не обновишь реализацию. Вот в чём магия.
type NoteService struct {
notesv1.UnimplementedNoteServiceServer
mu sync.RWMutex
notes map[string]*notesv1.Note
}
func NewNoteService() *NoteService {
return &NoteService{
notes: make(map[string]*notesv1.Note),
}
}
func (s *NoteService) CreateNote(
ctx context.Context,
req *notesv1.CreateNoteRequest,
) (*notesv1.Note, error) {
if req.Title == "" {
return nil, status.Error(codes.InvalidArgument, "title is required")
}
now := timestamppb.New(time.Now())
note := &notesv1.Note{
Id: uuid.New().String(),
Title: req.Title,
Content: req.Content,
CreatedAt: now,
UpdatedAt: now,
}
s.mu.Lock()
s.notes[note.Id] = note
s.mu.Unlock()
return note, nil
}
func (s *NoteService) GetNote(
ctx context.Context,
req *notesv1.GetNoteRequest,
) (*notesv1.Note, error) {
s.mu.RLock()
note, ok := s.notes[req.Id]
s.mu.RUnlock()
if !ok {
return nil, status.Errorf(codes.NotFound, "note %s not found", req.Id)
}
return note, nil
}
func (s *NoteService) UpdateNote(
ctx context.Context,
req *notesv1.UpdateNoteRequest,
) (*notesv1.Note, error) {
s.mu.Lock()
defer s.mu.Unlock()
note, ok := s.notes[req.Id]
if !ok {
return nil, status.Errorf(codes.NotFound, "note %s not found", req.Id)
}
note.Title = req.Title
note.Content = req.Content
note.UpdatedAt = timestamppb.New(time.Now())
return note, nil
}
func (s *NoteService) DeleteNote(
ctx context.Context,
req *notesv1.DeleteNoteRequest,
) (*emptypb.Empty, error) {
s.mu.Lock()
defer s.mu.Unlock()
if _, ok := s.notes[req.Id]; !ok {
return nil, status.Errorf(codes.NotFound, "note %s not found", req.Id)
}
delete(s.notes, req.Id)
return &emptypb.Empty{}, nil
}
func (s *NoteService) ListNotes(
ctx context.Context,
req *notesv1.ListNotesRequest,
) (*notesv1.ListNotesResponse, error) {
s.mu.RLock()
defer s.mu.RUnlock()
all := make([]*notesv1.Note, 0, len(s.notes))
for _, n := range s.notes {
all = append(all, n)
}
// Простая пагинация
total := int32(len(all))
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
page := req.Page
if page <= 0 {
page = 1
}
start := (page - 1) * pageSize
if start >= total {
return &notesv1.ListNotesResponse{
Notes: []*notesv1.Note{},
TotalCount: total,
}, nil
}
end := start + pageSize
if end > total {
end = total
}
return &notesv1.ListNotesResponse{
Notes: all[start:end],
TotalCount: total,
}, nil
}