This commit is contained in:
Владимир Фёдоров 2026-03-07 19:23:00 +07:00
parent 6ad47cbc38
commit 856f12f2e3
12 changed files with 116 additions and 177 deletions

View File

@ -2,7 +2,7 @@ package services
import ( import (
"evening_detective/internal/models" "evening_detective/internal/models"
"evening_detective/internal/services/story_service" story_service_models "evening_detective/internal/services/story_service/models"
"evening_detective/proto" "evening_detective/proto"
"strings" "strings"
) )
@ -29,19 +29,19 @@ func mapProtoTeamsToTeam(team *proto.Team) *models.Team {
} }
} }
func mapPlaceToProtoAction(place *story_service.Place) *proto.Action { func mapPlaceToProtoAction(place *story_service_models.Place) *proto.Action {
return &proto.Action{ return &proto.Action{
Place: place.Code, Place: place.Code,
} }
} }
func mapStoryApplicationToProtoApplication(application *story_service.Application) *proto.Application { func mapStoryApplicationToProtoApplication(application *story_service_models.Application) *proto.Application {
return &proto.Application{ return &proto.Application{
Name: application.Name, Name: application.Name,
} }
} }
func mapStoryApplicationsToApplications(applications []*story_service.Application) []*models.Application { func mapStoryApplicationsToApplications(applications []*story_service_models.Application) []*models.Application {
res := make([]*models.Application, 0, len(applications)) res := make([]*models.Application, 0, len(applications))
for _, application := range applications { for _, application := range applications {
res = append(res, mapStoryApplicationToApplication(application)) res = append(res, mapStoryApplicationToApplication(application))
@ -49,7 +49,7 @@ func mapStoryApplicationsToApplications(applications []*story_service.Applicatio
return res return res
} }
func mapStoryApplicationToApplication(application *story_service.Application) *models.Application { func mapStoryApplicationToApplication(application *story_service_models.Application) *models.Application {
return &models.Application{ return &models.Application{
Name: application.Name, Name: application.Name,
State: "NEW", State: "NEW",

View File

@ -2,9 +2,10 @@ package story_service
import ( import (
"context" "context"
"evening_detective/internal/services/story_service/models"
) )
type IStoryStorage interface { type IStoryStorage interface {
Load(ctx context.Context) (*Story, error) Load(ctx context.Context) (*models.Story, error)
Save(ctx context.Context, story *Story) error Save(ctx context.Context, story *models.Story) error
} }

View File

@ -1,4 +1,4 @@
package story_service package models
type Application struct { type Application struct {
Name string `json:"name"` Name string `json:"name"`

View File

@ -1,4 +1,4 @@
package story_service package models
type Door struct { type Door struct {
Code string `json:"code"` Code string `json:"code"`

View File

@ -1,4 +1,4 @@
package story_service package models
type Place struct { type Place struct {
Code string `json:"code"` Code string `json:"code"`

View File

@ -1,4 +1,4 @@
package story_service package models
type Story struct { type Story struct {
Places []*Place `json:"places"` Places []*Place `json:"places"`

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"evening_detective/internal/modules/cleaner" "evening_detective/internal/modules/cleaner"
"evening_detective/internal/modules/formatter" "evening_detective/internal/modules/formatter"
"evening_detective/internal/services/story_service/models"
"regexp" "regexp"
"strings" "strings"
) )
@ -11,7 +12,7 @@ import (
type StoryService struct { type StoryService struct {
cleaner cleaner.ICleaner cleaner cleaner.ICleaner
formatter formatter.IFormatter formatter formatter.IFormatter
story *Story story *models.Story
storyStorage IStoryStorage storyStorage IStoryStorage
} }
@ -45,44 +46,44 @@ func (s *StoryService) Update(ctx context.Context) error {
return nil return nil
} }
func (s *StoryService) GetPlace(code string) *Place { func (s *StoryService) GetPlace(code string) *models.Place {
if strings.HasPrefix(code, "[") || strings.HasSuffix(code, "]") { if strings.HasPrefix(code, "[") || strings.HasSuffix(code, "]") {
return NewClientErrorPlace(code) return models.NewClientErrorPlace(code)
} }
clearCode := s.cleaner.ClearCode(code) clearCode := s.cleaner.ClearCode(code)
for _, place := range s.story.Places { for _, place := range s.story.Places {
if s.cleaner.ClearCode(place.Code) == clearCode { if s.cleaner.ClearCode(place.Code) == clearCode {
applications := make([]*Application, 0, len(place.Applications)) applications := make([]*models.Application, 0, len(place.Applications))
for _, application := range place.Applications { for _, application := range place.Applications {
name := s.cleaner.ClearText(application.Name) name := s.cleaner.ClearText(application.Name)
applications = append( applications = append(
applications, applications,
&Application{ &models.Application{
Name: name, Name: name,
}, },
) )
} }
return NewPlace( return models.NewPlace(
place.Code, place.Code,
place.Name, place.Name,
s.cleaner.ClearText(place.Text), s.cleaner.ClearText(place.Text),
WithPlaceApplication(applications...), models.WithPlaceApplication(applications...),
WithPlaceHidden(place.Hidden), models.WithPlaceHidden(place.Hidden),
WithPlaceDoors(place.Doors...), models.WithPlaceDoors(place.Doors...),
) )
} }
} }
return NewNotFoundPlace(code) return models.NewNotFoundPlace(code)
} }
func (s *StoryService) GetPlaces(codes []string) []*Place { func (s *StoryService) GetPlaces(codes []string) []*models.Place {
places := make([]*Place, 0, 100) places := make([]*models.Place, 0, 100)
m := map[string]any{} m := map[string]any{}
for _, code := range codes { for _, code := range codes {
place := s.GetPlace(code) place := s.GetPlace(code)
_, ok := m[place.Code] _, ok := m[place.Code]
if place.Hidden && !ok { if place.Hidden && !ok {
place = NewNotFoundPlace(place.Code) place = models.NewNotFoundPlace(place.Code)
} }
places = append(places, place) places = append(places, place)
for _, door := range place.Doors { for _, door := range place.Doors {
@ -118,11 +119,11 @@ func (s *StoryService) deletePlace(ctx context.Context, code string) error {
func (s *StoryService) addPlace(ctx context.Context, node *GraphNode) error { func (s *StoryService) addPlace(ctx context.Context, node *GraphNode) error {
s.story.Places = append( s.story.Places = append(
s.story.Places, s.story.Places,
NewPlace( models.NewPlace(
node.Code, node.Code,
node.Name, node.Name,
s.formatter.FormatText(node.Text), s.formatter.FormatText(node.Text),
WithPlaceApplication(s.getApplications(node)...), models.WithPlaceApplication(s.getApplications(node)...),
), ),
) )
return s.Update(ctx) return s.Update(ctx)
@ -131,22 +132,22 @@ func (s *StoryService) addPlace(ctx context.Context, node *GraphNode) error {
func (s *StoryService) updatePlace(ctx context.Context, code string, node *GraphNode) error { func (s *StoryService) updatePlace(ctx context.Context, code string, node *GraphNode) error {
for i := range s.story.Places { for i := range s.story.Places {
if s.story.Places[i].Code == code { if s.story.Places[i].Code == code {
s.story.Places[i] = NewPlace( s.story.Places[i] = models.NewPlace(
node.Code, node.Code,
node.Name, node.Name,
s.formatter.FormatText(node.Text), s.formatter.FormatText(node.Text),
WithPlaceApplication(s.getApplications(node)...), models.WithPlaceApplication(s.getApplications(node)...),
) )
return s.Update(ctx) return s.Update(ctx)
} }
} }
for i := range s.story.Places { for i := range s.story.Places {
if s.story.Places[i].Code == node.Code { if s.story.Places[i].Code == node.Code {
s.story.Places[i] = NewPlace( s.story.Places[i] = models.NewPlace(
code, code,
node.Name, node.Name,
s.formatter.FormatText(node.Text), s.formatter.FormatText(node.Text),
WithPlaceApplication(s.getApplications(node)...), models.WithPlaceApplication(s.getApplications(node)...),
) )
break break
} }
@ -154,12 +155,12 @@ func (s *StoryService) updatePlace(ctx context.Context, code string, node *Graph
return s.Update(ctx) return s.Update(ctx)
} }
func (s *StoryService) getApplications(node *GraphNode) []*Application { func (s *StoryService) getApplications(node *GraphNode) []*models.Application {
nodeApplications := make([]*Application, 0, len(node.Applications)) nodeApplications := make([]*models.Application, 0, len(node.Applications))
for _, application := range node.Applications { for _, application := range node.Applications {
nodeApplications = append( nodeApplications = append(
nodeApplications, nodeApplications,
&Application{ &models.Application{
Name: application.Name, Name: application.Name,
}, },
) )

View File

@ -4,6 +4,7 @@ import (
"evening_detective/internal/modules/cleaner" "evening_detective/internal/modules/cleaner"
"evening_detective/internal/modules/formatter" "evening_detective/internal/modules/formatter"
"evening_detective/internal/services/story_service" "evening_detective/internal/services/story_service"
"evening_detective/internal/services/story_service/models"
"evening_detective/internal/services/story_storage" "evening_detective/internal/services/story_storage"
"testing" "testing"
@ -13,128 +14,128 @@ import (
func TestStoryService_GetPlace(t *testing.T) { func TestStoryService_GetPlace(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
story *story_service.Story story *models.Story
code string code string
want *story_service.Place want *models.Place
}{ }{
{ {
name: "не корректный ввода", name: "не корректный ввода",
story: &story_service.Story{}, story: &models.Story{},
code: "[Ы]", code: "[Ы]",
want: story_service.NewClientErrorPlace("[Ы]"), want: models.NewClientErrorPlace("[Ы]"),
}, },
{ {
name: "точка не найдена", name: "точка не найдена",
story: &story_service.Story{}, story: &models.Story{},
code: "Ы", code: "Ы",
want: story_service.NewNotFoundPlace("Ы"), want: models.NewNotFoundPlace("Ы"),
}, },
{ {
name: "получение точки", name: "получение точки",
story: &story_service.Story{ story: &models.Story{
Places: []*story_service.Place{ Places: []*models.Place{
story_service.NewPlace("Ы", "Название", "Текст"), models.NewPlace("Ы", "Название", "Текст"),
}, },
}, },
code: "Ы", code: "Ы",
want: story_service.NewPlace("Ы", "Название", "Текст"), want: models.NewPlace("Ы", "Название", "Текст"),
}, },
{ {
name: "получение скрытой точки", name: "получение скрытой точки",
story: &story_service.Story{ story: &models.Story{
Places: []*story_service.Place{ Places: []*models.Place{
story_service.NewPlace( models.NewPlace(
"Ы", "Ы",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceHidden(true), models.WithPlaceHidden(true),
), ),
}, },
}, },
code: "Ы", code: "Ы",
want: story_service.NewPlace( want: models.NewPlace(
"Ы", "Ы",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceHidden(true), models.WithPlaceHidden(true),
), ),
}, },
{ {
name: "получение точки с приложением", name: "получение точки с приложением",
story: &story_service.Story{ story: &models.Story{
Places: []*story_service.Place{ Places: []*models.Place{
story_service.NewPlace( models.NewPlace(
"Ы", "Ы",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceApplication( models.WithPlaceApplication(
story_service.NewApplication("Приложение"), models.NewApplication("Приложение"),
), ),
), ),
}, },
}, },
code: "Ы", code: "Ы",
want: story_service.NewPlace( want: models.NewPlace(
"Ы", "Ы",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceApplication( models.WithPlaceApplication(
story_service.NewApplication("Приложение"), models.NewApplication("Приложение"),
), ),
), ),
}, },
{ {
name: "получение точки с проходом", name: "получение точки с проходом",
story: &story_service.Story{ story: &models.Story{
Places: []*story_service.Place{ Places: []*models.Place{
story_service.NewPlace( models.NewPlace(
"Ы", "Ы",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceDoors( models.WithPlaceDoors(
story_service.NewDoor("Й", "Приложение"), models.NewDoor("Й", "Приложение"),
), ),
), ),
}, },
}, },
code: "Ы", code: "Ы",
want: story_service.NewPlace( want: models.NewPlace(
"Ы", "Ы",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceDoors( models.WithPlaceDoors(
story_service.NewDoor("Й", "Приложение"), models.NewDoor("Й", "Приложение"),
), ),
), ),
}, },
{ {
name: "получение точки с диалогом", name: "получение точки с диалогом",
story: &story_service.Story{ story: &models.Story{
Places: []*story_service.Place{ Places: []*models.Place{
story_service.NewPlace( models.NewPlace(
"Ы", "Ы",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceDoors( models.WithPlaceDoors(
story_service.NewDoor( models.NewDoor(
"Й", "Й",
"Приложение", "Приложение",
story_service.WithDoorShow(true), models.WithDoorShow(true),
), ),
), ),
), ),
}, },
}, },
code: "Ы", code: "Ы",
want: story_service.NewPlace( want: models.NewPlace(
"Ы", "Ы",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceDoors( models.WithPlaceDoors(
story_service.NewDoor( models.NewDoor(
"Й", "Й",
"Приложение", "Приложение",
story_service.WithDoorShow(true), models.WithDoorShow(true),
), ),
), ),
), ),
@ -159,74 +160,74 @@ func TestStoryService_GetPlace(t *testing.T) {
func TestStoryService_GetPlaces(t *testing.T) { func TestStoryService_GetPlaces(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
story *story_service.Story story *models.Story
codes []string codes []string
want []*story_service.Place want []*models.Place
}{ }{
{ {
name: "Можно сходить в открытую точку", name: "Можно сходить в открытую точку",
story: &story_service.Story{ story: &models.Story{
Places: []*story_service.Place{ Places: []*models.Place{
story_service.NewPlace("Ы", "Название", "Текст"), models.NewPlace("Ы", "Название", "Текст"),
}, },
}, },
codes: []string{"Ы"}, codes: []string{"Ы"},
want: []*story_service.Place{ want: []*models.Place{
story_service.NewPlace("Ы", "Название", "Текст"), models.NewPlace("Ы", "Название", "Текст"),
}, },
}, },
{ {
name: "Нельзя открыть скрытую точку", name: "Нельзя открыть скрытую точку",
story: &story_service.Story{ story: &models.Story{
Places: []*story_service.Place{ Places: []*models.Place{
story_service.NewPlace( models.NewPlace(
"Ы", "Ы",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceHidden(true), models.WithPlaceHidden(true),
), ),
}, },
}, },
codes: []string{"Ы"}, codes: []string{"Ы"},
want: []*story_service.Place{ want: []*models.Place{
story_service.NewNotFoundPlace("Ы"), models.NewNotFoundPlace("Ы"),
}, },
}, },
{ {
name: "Открываем скрытую точку", name: "Открываем скрытую точку",
story: &story_service.Story{ story: &models.Story{
Places: []*story_service.Place{ Places: []*models.Place{
story_service.NewPlace( models.NewPlace(
"Ы-1", "Ы-1",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceDoors( models.WithPlaceDoors(
story_service.NewDoor("Ы-2", "Название"), models.NewDoor("Ы-2", "Название"),
), ),
), ),
story_service.NewPlace( models.NewPlace(
"Ы-2", "Ы-2",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceHidden(true), models.WithPlaceHidden(true),
), ),
}, },
}, },
codes: []string{"Ы-1", "Ы-2"}, codes: []string{"Ы-1", "Ы-2"},
want: []*story_service.Place{ want: []*models.Place{
story_service.NewPlace( models.NewPlace(
"Ы-1", "Ы-1",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceDoors( models.WithPlaceDoors(
story_service.NewDoor("Ы-2", "Название"), models.NewDoor("Ы-2", "Название"),
), ),
), ),
story_service.NewPlace( models.NewPlace(
"Ы-2", "Ы-2",
"Название", "Название",
"Текст", "Текст",
story_service.WithPlaceHidden(true), models.WithPlaceHidden(true),
), ),
}, },
}, },

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"evening_detective/internal/services/story_service" "evening_detective/internal/services/story_service"
"evening_detective/internal/services/story_service/models"
"fmt" "fmt"
"log" "log"
"os" "os"
@ -19,20 +20,20 @@ func NewFileStoryStorage(filepath string) story_service.IStoryStorage {
} }
} }
func (s *fileService) Load(ctx context.Context) (*story_service.Story, error) { func (s *fileService) Load(ctx context.Context) (*models.Story, error) {
data, err := os.ReadFile(s.filepath) data, err := os.ReadFile(s.filepath)
if err != nil { if err != nil {
return nil, fmt.Errorf("story file %s not found", s.filepath) return nil, fmt.Errorf("story file %s not found", s.filepath)
} }
log.Printf("load story from: %s", s.filepath) log.Printf("load story from: %s", s.filepath)
story := &story_service.Story{} story := &models.Story{}
if err := json.Unmarshal(data, story); err != nil { if err := json.Unmarshal(data, story); err != nil {
return nil, err return nil, err
} }
return story, nil return story, nil
} }
func (s *fileService) Save(ctx context.Context, story *story_service.Story) error { func (s *fileService) Save(ctx context.Context, story *models.Story) error {
data, err := json.Marshal(story) data, err := json.Marshal(story)
if err != nil { if err != nil {
return err return err

View File

@ -1,12 +1,11 @@
//go:generate mockgen -source=interface.go -destination=mocks/mock.go -package=mocks
package story_storage package story_storage
import ( import (
"context" "context"
"evening_detective/internal/services/story_service" "evening_detective/internal/services/story_service/models"
) )
type IStoryStorage interface { type IStoryStorage interface {
Load(ctx context.Context) (*story_service.Story, error) Load(ctx context.Context) (*models.Story, error)
Save(ctx context.Context, story *story_service.Story) error Save(ctx context.Context, story *models.Story) error
} }

View File

@ -1,65 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: interface.go
// Package mocks is a generated GoMock package.
package mocks
import (
context "context"
story_service "evening_detective/internal/services/story_service"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockIStoryStorage is a mock of IStoryStorage interface.
type MockIStoryStorage struct {
ctrl *gomock.Controller
recorder *MockIStoryStorageMockRecorder
}
// MockIStoryStorageMockRecorder is the mock recorder for MockIStoryStorage.
type MockIStoryStorageMockRecorder struct {
mock *MockIStoryStorage
}
// NewMockIStoryStorage creates a new mock instance.
func NewMockIStoryStorage(ctrl *gomock.Controller) *MockIStoryStorage {
mock := &MockIStoryStorage{ctrl: ctrl}
mock.recorder = &MockIStoryStorageMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockIStoryStorage) EXPECT() *MockIStoryStorageMockRecorder {
return m.recorder
}
// Load mocks base method.
func (m *MockIStoryStorage) Load(ctx context.Context) (*story_service.Story, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Load", ctx)
ret0, _ := ret[0].(*story_service.Story)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Load indicates an expected call of Load.
func (mr *MockIStoryStorageMockRecorder) Load(ctx interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Load", reflect.TypeOf((*MockIStoryStorage)(nil).Load), ctx)
}
// Save mocks base method.
func (m *MockIStoryStorage) Save(ctx context.Context, story *story_service.Story) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Save", ctx, story)
ret0, _ := ret[0].(error)
return ret0
}
// Save indicates an expected call of Save.
func (mr *MockIStoryStorageMockRecorder) Save(ctx, story interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Save", reflect.TypeOf((*MockIStoryStorage)(nil).Save), ctx, story)
}

View File

@ -3,23 +3,24 @@ package story_storage
import ( import (
"context" "context"
"evening_detective/internal/services/story_service" "evening_detective/internal/services/story_service"
"evening_detective/internal/services/story_service/models"
) )
type varService struct { type varService struct {
story *story_service.Story story *models.Story
} }
func NewVarStoryStorage(story *story_service.Story) story_service.IStoryStorage { func NewVarStoryStorage(story *models.Story) story_service.IStoryStorage {
return &varService{ return &varService{
story: story, story: story,
} }
} }
func (s *varService) Load(ctx context.Context) (*story_service.Story, error) { func (s *varService) Load(ctx context.Context) (*models.Story, error) {
return s.story, nil return s.story, nil
} }
func (s *varService) Save(ctx context.Context, story *story_service.Story) error { func (s *varService) Save(ctx context.Context, story *models.Story) error {
s.story = story s.story = story
return nil return nil
} }