同步前后端联调与文档更新

This commit is contained in:
2026-04-02 09:25:05 +08:00
parent af43beadb0
commit 6964e26ec9
113 changed files with 4317 additions and 293 deletions

View File

@@ -0,0 +1,178 @@
package service
import (
"context"
"net/http"
"strings"
"cmr-backend/internal/apperr"
"cmr-backend/internal/store/postgres"
)
type AdminPipelineService struct {
store *postgres.Store
configService *ConfigService
}
type AdminReleaseView struct {
ID string `json:"id"`
ReleaseNo int `json:"releaseNo"`
ConfigLabel string `json:"configLabel"`
ManifestURL string `json:"manifestUrl"`
ManifestChecksumSha256 *string `json:"manifestChecksumSha256,omitempty"`
RouteCode *string `json:"routeCode,omitempty"`
BuildID *string `json:"buildId,omitempty"`
Status string `json:"status"`
PublishedAt string `json:"publishedAt"`
}
type AdminEventPipelineView struct {
EventID string `json:"eventId"`
CurrentRelease *AdminReleaseView `json:"currentRelease,omitempty"`
Sources []EventConfigSourceView `json:"sources"`
Builds []EventConfigBuildView `json:"builds"`
Releases []AdminReleaseView `json:"releases"`
}
type AdminRollbackReleaseInput struct {
ReleaseID string `json:"releaseId"`
}
func NewAdminPipelineService(store *postgres.Store, configService *ConfigService) *AdminPipelineService {
return &AdminPipelineService{
store: store,
configService: configService,
}
}
func (s *AdminPipelineService) GetEventPipeline(ctx context.Context, eventPublicID string, limit int) (*AdminEventPipelineView, error) {
event, err := s.store.GetEventByPublicID(ctx, strings.TrimSpace(eventPublicID))
if err != nil {
return nil, err
}
if event == nil {
return nil, apperr.New(http.StatusNotFound, "event_not_found", "event not found")
}
sources, err := s.configService.ListEventConfigSources(ctx, event.PublicID, limit)
if err != nil {
return nil, err
}
buildRecords, err := s.store.ListEventConfigBuildsByEventID(ctx, event.ID, limit)
if err != nil {
return nil, err
}
releaseRecords, err := s.store.ListEventReleasesByEventID(ctx, event.ID, limit)
if err != nil {
return nil, err
}
builds := make([]EventConfigBuildView, 0, len(buildRecords))
for i := range buildRecords {
item, err := buildEventConfigBuildView(&buildRecords[i])
if err != nil {
return nil, err
}
builds = append(builds, *item)
}
releases := make([]AdminReleaseView, 0, len(releaseRecords))
for _, item := range releaseRecords {
releases = append(releases, buildAdminReleaseView(item))
}
result := &AdminEventPipelineView{
EventID: event.PublicID,
Sources: sources,
Builds: builds,
Releases: releases,
}
if event.CurrentReleasePubID != nil {
result.CurrentRelease = &AdminReleaseView{
ID: *event.CurrentReleasePubID,
ConfigLabel: derefStringOrEmpty(event.ConfigLabel),
ManifestURL: derefStringOrEmpty(event.ManifestURL),
ManifestChecksumSha256: event.ManifestChecksum,
RouteCode: event.RouteCode,
Status: "published",
}
}
return result, nil
}
func (s *AdminPipelineService) BuildSource(ctx context.Context, sourceID string) (*EventConfigBuildView, error) {
return s.configService.BuildPreview(ctx, BuildPreviewInput{SourceID: sourceID})
}
func (s *AdminPipelineService) GetBuild(ctx context.Context, buildID string) (*EventConfigBuildView, error) {
return s.configService.GetEventConfigBuild(ctx, buildID)
}
func (s *AdminPipelineService) PublishBuild(ctx context.Context, buildID string) (*PublishedReleaseView, error) {
return s.configService.PublishBuild(ctx, PublishBuildInput{BuildID: buildID})
}
func (s *AdminPipelineService) RollbackRelease(ctx context.Context, eventPublicID string, input AdminRollbackReleaseInput) (*AdminReleaseView, error) {
event, err := s.store.GetEventByPublicID(ctx, strings.TrimSpace(eventPublicID))
if err != nil {
return nil, err
}
if event == nil {
return nil, apperr.New(http.StatusNotFound, "event_not_found", "event not found")
}
input.ReleaseID = strings.TrimSpace(input.ReleaseID)
if input.ReleaseID == "" {
return nil, apperr.New(http.StatusBadRequest, "invalid_params", "releaseId is required")
}
release, err := s.store.GetEventReleaseByPublicID(ctx, input.ReleaseID)
if err != nil {
return nil, err
}
if release == nil {
return nil, apperr.New(http.StatusNotFound, "release_not_found", "release not found")
}
if release.EventID != event.ID {
return nil, apperr.New(http.StatusConflict, "release_not_belong_to_event", "release does not belong to event")
}
if release.Status != "published" {
return nil, apperr.New(http.StatusConflict, "release_not_publishable", "release is not published")
}
tx, err := s.store.Begin(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback(ctx)
if err := s.store.SetCurrentEventRelease(ctx, tx, event.ID, release.ID); err != nil {
return nil, err
}
if err := tx.Commit(ctx); err != nil {
return nil, err
}
view := buildAdminReleaseView(*release)
return &view, nil
}
func buildAdminReleaseView(item postgres.EventRelease) AdminReleaseView {
return AdminReleaseView{
ID: item.PublicID,
ReleaseNo: item.ReleaseNo,
ConfigLabel: item.ConfigLabel,
ManifestURL: item.ManifestURL,
ManifestChecksumSha256: item.ManifestChecksum,
RouteCode: item.RouteCode,
BuildID: item.BuildID,
Status: item.Status,
PublishedAt: item.PublishedAt.Format(timeRFC3339),
}
}
func derefStringOrEmpty(value *string) string {
if value == nil {
return ""
}
return *value
}