package system import ( "context" "encoding/json" "errors" "kra/internal/biz/system" "strings" "time" "kra/internal/service/dto" "github.com/google/uuid" ) var ( ErrExportTokenInvalid = errors.New("export token invalid") ErrExportTokenMalformed = errors.New("export token malformed") ErrExportTokenType = errors.New("export token type mismatch") ) type ExportService struct { uc *system.ExportUsecase cache system.Cache } type ExportToken struct { TemplateID string Params map[string]string Blank bool } type exportTokenCache struct { TemplateID string `json:"templateID"` Params map[string]string `json:"params"` Blank bool `json:"blank"` } func NewExportService(uc *system.ExportUsecase, cache system.Cache) *ExportService { return &ExportService{uc: uc, cache: cache} } func (s *ExportService) IssueToken(ctx context.Context, templateID string, params map[string]string, blank bool) (string, error) { token := strings.ReplaceAll(uuid.NewString(), "-", "") raw, err := json.Marshal(exportTokenCache{TemplateID: templateID, Params: params, Blank: blank}) if err != nil { return "", err } if err = s.cache.Set(ctx, "export:"+token, string(raw), 30*time.Minute); err != nil { return "", err } return token, nil } func (s *ExportService) ConsumeToken(ctx context.Context, token string, expectBlank bool) (*ExportToken, error) { key := "export:" + token raw, ok, err := s.cache.Get(ctx, key) if atomic, supported := s.cache.(interface { GetAndDelete(context.Context, string) (string, bool, error) }); supported { raw, ok, err = atomic.GetAndDelete(ctx, key) } if err != nil || !ok { return nil, ErrExportTokenInvalid } var value exportTokenCache if json.Unmarshal([]byte(raw), &value) != nil { return nil, ErrExportTokenMalformed } if value.Blank != expectBlank { return nil, ErrExportTokenType } if _, supported := s.cache.(interface { GetAndDelete(context.Context, string) (string, bool, error) }); !supported { _ = s.cache.Delete(ctx, key) } return &ExportToken{TemplateID: value.TemplateID, Params: value.Params, Blank: value.Blank}, nil } func exportTemplateDomain(value *dto.ExportTemplateRequest) *system.ExportTemplate { out := &system.ExportTemplate{ID: value.ID, DBName: value.DBName, Name: value.Name, TableName: value.TableName, TemplateID: value.TemplateID, TemplateInfo: value.TemplateInfo, SQL: value.SQL, ImportSQL: value.ImportSQL, Limit: value.Limit, Order: value.Order} for _, item := range value.Conditions { out.Conditions = append(out.Conditions, system.ExportCondition{ID: item.ID, TemplateID: item.TemplateID, From: item.From, Column: item.Column, Operator: item.Operator}) } for _, item := range value.Joins { out.Joins = append(out.Joins, system.ExportJoin{ID: item.ID, TemplateID: item.TemplateID, Join: item.Join, Table: item.Table, On: item.On}) } return out } func (s *ExportService) CreateRequest(ctx context.Context, req *dto.ExportTemplateRequest) error { return s.Create(ctx, exportTemplateDomain(req)) } func (s *ExportService) UpdateRequest(ctx context.Context, req *dto.ExportTemplateRequest) error { return s.Update(ctx, exportTemplateDomain(req)) } func (s *ExportService) TemplatesFilter(ctx context.Context, page, size int, name, tableName, templateID string, start, end *time.Time) ([]*dto.ExportTemplateResponse, int64, error) { return s.Templates(ctx, page, size, &system.ExportTemplateFilter{Name: name, TableName: tableName, TemplateID: templateID, StartCreatedAt: start, EndCreatedAt: end}) } func exportDTO(v *system.ExportTemplate) *dto.ExportTemplateResponse { var conditions []dto.ExportConditionResponse if v.Conditions != nil { conditions = make([]dto.ExportConditionResponse, 0, len(v.Conditions)) for _, x := range v.Conditions { conditions = append(conditions, dto.ExportConditionResponse{ID: x.ID, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt, TemplateID: x.TemplateID, From: x.From, Column: x.Column, Operator: x.Operator}) } } var joins []dto.ExportJoinResponse if v.Joins != nil { joins = make([]dto.ExportJoinResponse, 0, len(v.Joins)) for _, x := range v.Joins { joins = append(joins, dto.ExportJoinResponse{ID: x.ID, CreatedAt: x.CreatedAt, UpdatedAt: x.UpdatedAt, TemplateID: x.TemplateID, Join: x.Join, Table: x.Table, On: x.On}) } } return &dto.ExportTemplateResponse{ID: v.ID, CreatedAt: v.CreatedAt, UpdatedAt: v.UpdatedAt, DeletedAt: nil, DBName: v.DBName, Name: v.Name, TableName: v.TableName, TemplateID: v.TemplateID, TemplateInfo: v.TemplateInfo, SQL: v.SQL, ImportSQL: v.ImportSQL, Limit: v.Limit, Order: v.Order, Conditions: conditions, Joins: joins} } func (s *ExportService) Create(ctx context.Context, v *system.ExportTemplate) error { return s.uc.CreateExportTemplate(ctx, v) } func (s *ExportService) Update(ctx context.Context, v *system.ExportTemplate) error { return s.uc.UpdateExportTemplate(ctx, v) } func (s *ExportService) Delete(ctx context.Context, ids []uint) error { return s.uc.DeleteExportTemplates(ctx, ids) } func (s *ExportService) Template(ctx context.Context, id uint, tid string) (*dto.ExportTemplateResponse, error) { v, err := s.uc.FindExportTemplate(ctx, id, tid) if err != nil { return nil, err } return exportDTO(v), nil } func (s *ExportService) Templates(ctx context.Context, page, size int, q *system.ExportTemplateFilter) ([]*dto.ExportTemplateResponse, int64, error) { items, total, err := s.uc.ListExportTemplates(ctx, page, size, q) if err != nil { return nil, 0, err } out := make([]*dto.ExportTemplateResponse, 0, len(items)) for _, v := range items { out = append(out, exportDTO(v)) } return out, total, nil }