147 lines
5.0 KiB
Go
147 lines
5.0 KiB
Go
package storage
|
||
|
||
import (
|
||
"encoding/json"
|
||
|
||
"github.com/kamelion/ratatoskr-go/internal/model"
|
||
)
|
||
|
||
// Status — алиас доменного статуса задачи.
|
||
//
|
||
// Совместимый мост: весь внешний код продолжает использовать storage.Status
|
||
// (например «storage.StatusRunning»), но единый источник истины — model.Status.
|
||
type Status = model.Status
|
||
|
||
// Статусы задачи — re-export из model.
|
||
const (
|
||
StatusDraft = model.StatusDraft
|
||
StatusCollecting = model.StatusCollecting
|
||
StatusReady = model.StatusReady
|
||
StatusApproved = model.StatusApproved
|
||
StatusRunning = model.StatusRunning
|
||
StatusSuccess = model.StatusSuccess
|
||
StatusFailed = model.StatusFailed
|
||
StatusTimeout = model.StatusTimeout
|
||
StatusCancelled = model.StatusCancelled
|
||
StatusAborted = model.StatusAborted
|
||
StatusClosed = model.StatusClosed
|
||
)
|
||
|
||
// AllStatuses — все возможные статусы для валидации.
|
||
var AllStatuses = model.AllStatuses
|
||
|
||
// IsValidTransition проверяет, допустим ли переход from → to.
|
||
func IsValidTransition(from, to Status) bool {
|
||
return model.IsValidTransition(from, to)
|
||
}
|
||
|
||
// IsTerminal возвращает true, если статус терминальный.
|
||
func IsTerminal(s Status) bool {
|
||
return model.IsTerminal(s)
|
||
}
|
||
|
||
// Step — этап задачи с собственным критерием готовности.
|
||
type Step struct {
|
||
Title string `json:"title"`
|
||
AC string `json:"ac"` // acceptance criterion этапа
|
||
}
|
||
|
||
// Task — запись задачи в БД.
|
||
type Task struct {
|
||
ID int64 `json:"id"`
|
||
ChatID string `json:"chat_id"` // tg://<id>
|
||
Title string `json:"title"`
|
||
Goal string `json:"goal"`
|
||
Repo string `json:"repo"` // обратная совместимость: одиночный репозиторий
|
||
Repos []string `json:"repos"` // список репозиториев (основной)
|
||
Why string `json:"why"`
|
||
AC string `json:"ac"` // acceptance criteria
|
||
Steps []Step `json:"steps"` // этапы задачи (опционально)
|
||
TaskTag string `json:"task_tag"` // UUID, стабильный на всю жизнь
|
||
Status Status `json:"status"`
|
||
CreatedAt SQLiteTime `json:"created_at"`
|
||
UpdatedAt SQLiteTime `json:"updated_at"`
|
||
}
|
||
|
||
// ReposJoined возвращает repos как одну строку (JSON-массив) для хранения в БД.
|
||
// Пустой список → пустая строка.
|
||
func (t *Task) ReposJoined() string {
|
||
if len(t.Repos) == 0 {
|
||
return ""
|
||
}
|
||
b, _ := json.Marshal(t.Repos)
|
||
return string(b)
|
||
}
|
||
|
||
// EffectiveRepos возвращает сам репо: если Repos пуст, но Repo задан —
|
||
// подтягивает одиночный (обратная совместимость).
|
||
func (t *Task) EffectiveRepos() []string {
|
||
if len(t.Repos) > 0 {
|
||
return t.Repos
|
||
}
|
||
if t.Repo != "" {
|
||
return []string{t.Repo}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// SetReposFromDB заполняет Repos из сохранённой строки (JSON), либо
|
||
// из одиночного repo (обратная совместимость).
|
||
func (t *Task) SetReposFromDB(repos string) {
|
||
if repos == "" {
|
||
t.Repos = nil
|
||
return
|
||
}
|
||
_ = json.Unmarshal([]byte(repos), &t.Repos)
|
||
}
|
||
|
||
// StepsJoined возвращает steps как одну строку (JSON-массив) для хранения в БД.
|
||
// Пустой список → пустая строка.
|
||
func (t *Task) StepsJoined() string {
|
||
if len(t.Steps) == 0 {
|
||
return ""
|
||
}
|
||
b, _ := json.Marshal(t.Steps)
|
||
return string(b)
|
||
}
|
||
|
||
// SetStepsFromDB заполняет Steps из сохранённой строки (JSON).
|
||
func (t *Task) SetStepsFromDB(steps string) {
|
||
if steps == "" {
|
||
t.Steps = nil
|
||
return
|
||
}
|
||
_ = json.Unmarshal([]byte(steps), &t.Steps)
|
||
}
|
||
|
||
// TraceStatus — алиас доменного статуса трассировки.
|
||
type TraceStatus = model.TraceStatus
|
||
|
||
const (
|
||
TraceRunning TraceStatus = model.TraceRunning
|
||
TraceSuccess TraceStatus = model.TraceSuccess
|
||
TraceFailed TraceStatus = model.TraceFailed
|
||
TraceTimeout TraceStatus = model.TraceTimeout
|
||
)
|
||
|
||
// Trace — лог одного субагента.
|
||
type Trace struct {
|
||
ID int64 `json:"id"`
|
||
TaskID int64 `json:"task_id"`
|
||
Agent string `json:"agent"` // analyst | researcher | dev | reviewer
|
||
SessionID string `json:"session_id"` // opencode session_id
|
||
Prompt string `json:"prompt"`
|
||
Output string `json:"output"` // полный NDJSON или summary
|
||
Status TraceStatus `json:"status"`
|
||
StartedAt SQLiteTime `json:"started_at"`
|
||
FinishedAt NullSQLiteTime `json:"finished_at,omitempty"`
|
||
}
|
||
|
||
// TaskFilter — параметры фильтрации списка задач.
|
||
type TaskFilter struct {
|
||
ChatID string
|
||
Status Status
|
||
Limit int
|
||
Offset int
|
||
}
|