feat: Refactor template handling and brand management

- Removed schema_json from article templates and related queries.
- Updated brand service to include website field in requests and responses.
- Simplified template wizard view by eliminating unused schema fields and related logic.
- Added tests for ark text format handling.
- Created migrations for dropping schema_json and adding website to brands.
This commit is contained in:
2026-04-02 11:38:08 +08:00
parent 7fa1809682
commit 111498a65f
25 changed files with 298 additions and 379 deletions
+7 -12
View File
@@ -140,12 +140,11 @@ func main() {
// Platform templates
templates := []struct {
key, name, schema, prompt, cardConfig string
key, name, prompt, cardConfig string
}{
{
"top_x_article",
"Top X 文章",
`{"fields":[{"name":"topic","type":"text","label":"话题","required":true},{"name":"count","type":"number","label":"数量","required":true,"default":10}]}`,
`你是一名经验丰富的内容编辑,请围绕「{{topic}}」撰写一篇完整的 Markdown 推荐类文章。
文章目标:
1. 帮读者快速理解这个主题下值得关注的 {{count}} 个方向、产品或对象。
@@ -245,7 +244,6 @@ func main() {
{
"product_review",
"产品评测文章",
`{"fields":[{"name":"product_name","type":"text","label":"产品名称","required":true},{"name":"category","type":"text","label":"品类","required":true}]}`,
`你是一名资深产品评测编辑,请围绕「{{product_name}}」这款{{category}}产品撰写一篇完整的 Markdown 评测文章。
文章目标:
1. 帮读者判断这款产品是否值得关注或购买。
@@ -346,7 +344,6 @@ func main() {
{
"research_report",
"研究报告文章",
`{"fields":[{"name":"subject","type":"text","label":"研究主题","required":true},{"name":"depth","type":"select","label":"深度","options":["overview","detailed"],"default":"overview"}]}`,
`你是一名研究分析师,请围绕「{{subject}}」撰写一篇完整的 Markdown 研究报告。
文章目标:
1. 提炼关键事实、趋势与结构化发现。
@@ -439,7 +436,6 @@ func main() {
{
"brand_search_expansion",
"品牌词搜索扩写",
`{"fields":[{"name":"brand","type":"text","label":"品牌名","required":true},{"name":"keyword","type":"text","label":"关键词","required":true}]}`,
`你是一名擅长搜索意图写作的内容编辑,请围绕「{{brand}} {{keyword}}」撰写一篇完整的 Markdown 品牌词搜索扩写文章。
文章目标:
1. 直接回答搜索用户最关心的问题。
@@ -536,10 +532,10 @@ func main() {
for _, t := range templates {
_, err = tx.Exec(ctx, `
INSERT INTO article_templates (scope, origin_type, template_key, template_name, schema_json, prompt_template, prompt_visibility, card_config_json, status)
VALUES ('platform', 'platform', $1, $2, $3::jsonb, $4, 'visible', $5::jsonb, 'active')
INSERT INTO article_templates (scope, origin_type, template_key, template_name, prompt_template, prompt_visibility, card_config_json, status)
VALUES ('platform', 'platform', $1, $2, $3, 'visible', $4::jsonb, 'active')
ON CONFLICT DO NOTHING
`, t.key, t.name, t.schema, t.prompt, t.cardConfig)
`, t.key, t.name, t.prompt, t.cardConfig)
if err != nil {
log.Fatalf("insert template %s: %v", t.key, err)
}
@@ -547,16 +543,15 @@ func main() {
_, err = tx.Exec(ctx, `
UPDATE article_templates
SET template_name = $2,
schema_json = $3::jsonb,
prompt_template = $4,
card_config_json = $5::jsonb,
prompt_template = $3,
card_config_json = $4::jsonb,
prompt_visibility = 'visible',
status = 'active',
updated_at = NOW()
WHERE scope = 'platform'
AND template_key = $1
AND deleted_at IS NULL
`, t.key, t.name, t.schema, t.prompt, t.cardConfig)
`, t.key, t.name, t.prompt, t.cardConfig)
if err != nil {
log.Fatalf("update template %s: %v", t.key, err)
}
+33 -25
View File
@@ -132,31 +132,7 @@ func (c *arkClient) Generate(ctx context.Context, req GenerateRequest, onDelta f
},
}
var textFormat *responses.ResponsesText
if req.ResponseFormat != nil {
format := &responses.TextFormat{
Name: strings.TrimSpace(req.ResponseFormat.Name),
}
switch req.ResponseFormat.Type {
case ResponseFormatTypeJSONSchema:
format.Type = responses.TextType_json_schema
case ResponseFormatTypeJSONObject:
format.Type = responses.TextType_json_object
default:
format.Type = responses.TextType_text
}
if description := strings.TrimSpace(req.ResponseFormat.Description); description != "" {
format.Description = &description
}
if len(req.ResponseFormat.SchemaJSON) > 0 {
format.Schema = &responses.Bytes{Value: req.ResponseFormat.SchemaJSON}
}
if req.ResponseFormat.Strict {
strict := true
format.Strict = &strict
}
textFormat = &responses.ResponsesText{Format: format}
}
textFormat := buildArkTextFormat(req.ResponseFormat)
var tools []*responses.ResponsesTool
if req.WebSearch != nil && req.WebSearch.Enabled {
@@ -260,6 +236,38 @@ func (c *arkClient) Generate(ctx context.Context, req GenerateRequest, onDelta f
}, nil
}
func buildArkTextFormat(format *ResponseFormat) *responses.ResponsesText {
if format == nil {
return nil
}
textFormat := &responses.TextFormat{}
switch format.Type {
case ResponseFormatTypeJSONSchema:
textFormat.Type = responses.TextType_json_schema
if name := strings.TrimSpace(format.Name); name != "" {
textFormat.Name = name
}
if description := strings.TrimSpace(format.Description); description != "" {
textFormat.Description = &description
}
if len(format.SchemaJSON) > 0 {
textFormat.Schema = &responses.Bytes{Value: format.SchemaJSON}
}
if format.Strict {
strict := true
textFormat.Strict = &strict
}
case ResponseFormatTypeJSONObject:
// Ark only accepts the type discriminator for json_object.
textFormat.Type = responses.TextType_json_object
default:
textFormat.Type = responses.TextType_text
}
return &responses.ResponsesText{Format: textFormat}
}
func resolveArkReasoning(value string) *responses.ResponsesReasoning {
switch strings.ToLower(strings.TrimSpace(value)) {
case "":
+65
View File
@@ -0,0 +1,65 @@
package llm
import (
"testing"
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model/responses"
)
func TestBuildArkTextFormatJSONObjectDropsSchemaFields(t *testing.T) {
text := buildArkTextFormat(&ResponseFormat{
Type: ResponseFormatTypeJSONObject,
Name: "article_outline",
Description: "outline object",
SchemaJSON: []byte(`{"type":"object"}`),
Strict: true,
})
if text == nil || text.Format == nil {
t.Fatal("buildArkTextFormat() returned nil")
}
if got := text.Format.Type; got != responses.TextType_json_object {
t.Fatalf("buildArkTextFormat() type = %v, want %v", got, responses.TextType_json_object)
}
if text.Format.Name != "" {
t.Fatalf("buildArkTextFormat() name = %q, want empty for json_object", text.Format.Name)
}
if text.Format.Description != nil {
t.Fatalf("buildArkTextFormat() description = %v, want nil for json_object", *text.Format.Description)
}
if text.Format.Schema != nil {
t.Fatalf("buildArkTextFormat() schema = %v, want nil for json_object", text.Format.Schema)
}
if text.Format.Strict != nil {
t.Fatalf("buildArkTextFormat() strict = %v, want nil for json_object", *text.Format.Strict)
}
}
func TestBuildArkTextFormatJSONSchemaKeepsSchemaFields(t *testing.T) {
text := buildArkTextFormat(&ResponseFormat{
Type: ResponseFormatTypeJSONSchema,
Name: "article_outline",
Description: "outline object",
SchemaJSON: []byte(`{"type":"object"}`),
Strict: true,
})
if text == nil || text.Format == nil {
t.Fatal("buildArkTextFormat() returned nil")
}
if got := text.Format.Type; got != responses.TextType_json_schema {
t.Fatalf("buildArkTextFormat() type = %v, want %v", got, responses.TextType_json_schema)
}
if text.Format.Name != "article_outline" {
t.Fatalf("buildArkTextFormat() name = %q, want article_outline", text.Format.Name)
}
if text.Format.Description == nil || *text.Format.Description != "outline object" {
t.Fatalf("buildArkTextFormat() description = %v, want outline object", text.Format.Description)
}
if text.Format.Schema == nil || string(text.Format.Schema.Value) != `{"type":"object"}` {
t.Fatalf("buildArkTextFormat() schema = %v, want schema payload", text.Format.Schema)
}
if text.Format.Strict == nil || !*text.Format.Strict {
t.Fatalf("buildArkTextFormat() strict = %v, want true", text.Format.Strict)
}
}
+42 -11
View File
@@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/json"
"fmt"
"strings"
"github.com/jackc/pgx/v5/pgxpool"
@@ -27,12 +28,14 @@ func NewBrandService(pool *pgxpool.Pool, auditLogs *auditlog.AsyncWriter) *Brand
type BrandRequest struct {
Name string `json:"name" binding:"required"`
Website *string `json:"website"`
Description *string `json:"description"`
}
type BrandResponse struct {
ID int64 `json:"id"`
Name string `json:"name"`
Website *string `json:"website"`
Description *string `json:"description"`
Status string `json:"status"`
CreatedAt string `json:"created_at"`
@@ -42,7 +45,7 @@ type BrandResponse struct {
func (s *BrandService) List(ctx context.Context) ([]BrandResponse, error) {
actor := auth.MustActor(ctx)
rows, err := s.pool.Query(ctx, `
SELECT id, name, description, status, created_at, updated_at
SELECT id, name, website, description, status, created_at, updated_at
FROM brands WHERE tenant_id = $1 AND deleted_at IS NULL ORDER BY created_at DESC
`, actor.TenantID)
if err != nil {
@@ -54,7 +57,7 @@ func (s *BrandService) List(ctx context.Context) ([]BrandResponse, error) {
for rows.Next() {
var b BrandResponse
var ca, ua interface{}
if err := rows.Scan(&b.ID, &b.Name, &b.Description, &b.Status, &ca, &ua); err != nil {
if err := rows.Scan(&b.ID, &b.Name, &b.Website, &b.Description, &b.Status, &ca, &ua); err != nil {
return nil, response.ErrInternal(50010, "scan_failed", err.Error())
}
b.CreatedAt = fmt.Sprintf("%v", ca)
@@ -69,12 +72,16 @@ func (s *BrandService) List(ctx context.Context) ([]BrandResponse, error) {
func (s *BrandService) Create(ctx context.Context, req BrandRequest) (*BrandResponse, error) {
actor := auth.MustActor(ctx)
normalizeBrandRequest(&req)
if req.Name == "" {
return nil, response.ErrBadRequest(40001, "invalid_params", "name is required")
}
var id int64
var ca interface{}
err := s.pool.QueryRow(ctx, `
INSERT INTO brands (tenant_id, name, description, status)
VALUES ($1, $2, $3, 'active') RETURNING id, created_at
`, actor.TenantID, req.Name, req.Description).Scan(&id, &ca)
INSERT INTO brands (tenant_id, name, website, description, status)
VALUES ($1, $2, $3, $4, 'active') RETURNING id, created_at
`, actor.TenantID, req.Name, req.Website, req.Description).Scan(&id, &ca)
if err != nil {
return nil, response.ErrConflict(40901, "brand_exists", "brand with this name already exists")
}
@@ -95,7 +102,14 @@ func (s *BrandService) Create(ctx context.Context, req BrandRequest) (*BrandResp
Result: &result,
})
return &BrandResponse{ID: id, Name: req.Name, Description: req.Description, Status: "active", CreatedAt: fmt.Sprintf("%v", ca)}, nil
return &BrandResponse{
ID: id,
Name: req.Name,
Website: req.Website,
Description: req.Description,
Status: "active",
CreatedAt: fmt.Sprintf("%v", ca),
}, nil
}
func (s *BrandService) Detail(ctx context.Context, id int64) (*BrandResponse, error) {
@@ -103,9 +117,9 @@ func (s *BrandService) Detail(ctx context.Context, id int64) (*BrandResponse, er
var b BrandResponse
var ca, ua interface{}
err := s.pool.QueryRow(ctx, `
SELECT id, name, description, status, created_at, updated_at
SELECT id, name, website, description, status, created_at, updated_at
FROM brands WHERE id = $1 AND tenant_id = $2 AND deleted_at IS NULL
`, id, actor.TenantID).Scan(&b.ID, &b.Name, &b.Description, &b.Status, &ca, &ua)
`, id, actor.TenantID).Scan(&b.ID, &b.Name, &b.Website, &b.Description, &b.Status, &ca, &ua)
if err != nil {
return nil, response.ErrNotFound(40420, "brand_not_found", "brand not found")
}
@@ -116,16 +130,33 @@ func (s *BrandService) Detail(ctx context.Context, id int64) (*BrandResponse, er
func (s *BrandService) Update(ctx context.Context, id int64, req BrandRequest) error {
actor := auth.MustActor(ctx)
normalizeBrandRequest(&req)
if req.Name == "" {
return response.ErrBadRequest(40001, "invalid_params", "name is required")
}
tag, err := s.pool.Exec(ctx, `
UPDATE brands SET name = $1, description = $2, updated_at = NOW()
WHERE id = $3 AND tenant_id = $4 AND deleted_at IS NULL
`, req.Name, req.Description, id, actor.TenantID)
UPDATE brands SET name = $1, website = $2, description = $3, updated_at = NOW()
WHERE id = $4 AND tenant_id = $5 AND deleted_at IS NULL
`, req.Name, req.Website, req.Description, id, actor.TenantID)
if err != nil || tag.RowsAffected() == 0 {
return response.ErrNotFound(40420, "brand_not_found", "brand not found")
}
return nil
}
func normalizeBrandRequest(req *BrandRequest) {
req.Name = strings.TrimSpace(req.Name)
req.Website = normalizeOptionalString(req.Website)
req.Description = normalizeOptionalString(req.Description)
}
func normalizeOptionalString(value *string) *string {
if value == nil {
return nil
}
return nilIfEmptyString(*value)
}
func (s *BrandService) Delete(ctx context.Context, id int64) error {
actor := auth.MustActor(ctx)
tx, err := s.pool.Begin(ctx)
+1 -16
View File
@@ -93,7 +93,6 @@ type TemplateListItem struct {
OriginType string `json:"origin_type"`
TemplateKey string `json:"template_key"`
TemplateName string `json:"template_name"`
SchemaJSON map[string]interface{} `json:"schema_json"`
PromptVisibility string `json:"prompt_visibility"`
CardConfigJSON map[string]interface{} `json:"card_config"`
Status string `json:"status"`
@@ -122,7 +121,6 @@ func (s *TemplateService) List(ctx context.Context) ([]TemplateListItem, error)
VersionNo: row.VersionNo,
CreatedAt: row.CreatedAt,
}
_ = json.Unmarshal(row.SchemaJSON, &item.SchemaJSON)
_ = json.Unmarshal(row.CardConfigJSON, &item.CardConfigJSON)
items = append(items, item)
}
@@ -142,7 +140,7 @@ func (s *TemplateService) Detail(ctx context.Context, id int64) (*TemplateDetail
return nil, response.ErrNotFound(40410, "template_not_found", "template not found")
}
detail := &TemplateDetail{
detail := &TemplateDetail{
TemplateListItem: TemplateListItem{
ID: record.ID,
Scope: record.Scope,
@@ -156,7 +154,6 @@ func (s *TemplateService) Detail(ctx context.Context, id int64) (*TemplateDetail
CreatedAt: record.CreatedAt,
},
}
_ = json.Unmarshal(record.SchemaJSON, &detail.SchemaJSON)
_ = json.Unmarshal(record.CardConfigJSON, &detail.CardConfigJSON)
if canViewPromptTemplate(record, actor.TenantID) {
@@ -166,18 +163,6 @@ func (s *TemplateService) Detail(ctx context.Context, id int64) (*TemplateDetail
return detail, nil
}
func (s *TemplateService) Schema(ctx context.Context, id int64) (map[string]interface{}, error) {
actor := auth.MustActor(ctx)
record, err := s.templates.GetTemplateByID(ctx, id, actor.TenantID)
if err != nil {
return nil, response.ErrNotFound(40410, "template_not_found", "template not found")
}
schema := map[string]interface{}{}
_ = json.Unmarshal(record.SchemaJSON, &schema)
return schema, nil
}
func canViewPromptTemplate(record *repository.TemplateRecord, actorTenantID int64) bool {
if record == nil || record.TenantID == nil {
return false
@@ -9,7 +9,6 @@ type ArticleTemplate struct {
OriginType string
TemplateKey string
TemplateName string
SchemaJSON map[string]interface{}
PromptTemplate *string
PromptVisibility string
ProtectedPromptAssetKey *string
+1
View File
@@ -6,6 +6,7 @@ type Brand struct {
ID int64
TenantID int64
Name string
Website *string
Description *string
Status string
CreatedAt time.Time
@@ -31,7 +31,6 @@ type ArticleTemplate struct {
ShareCodeID pgtype.Int8 `json:"share_code_id"`
TemplateKey string `json:"template_key"`
TemplateName string `json:"template_name"`
SchemaJson []byte `json:"schema_json"`
PromptTemplate pgtype.Text `json:"prompt_template"`
PromptVisibility string `json:"prompt_visibility"`
ProtectedPromptAssetKey pgtype.Text `json:"protected_prompt_asset_key"`
@@ -42,7 +42,6 @@ type Querier interface {
GetRecentArticles(ctx context.Context, tenantID int64) ([]GetRecentArticlesRow, error)
GetScheduleTaskByID(ctx context.Context, arg GetScheduleTaskByIDParams) (GetScheduleTaskByIDRow, error)
GetTemplateByID(ctx context.Context, arg GetTemplateByIDParams) (GetTemplateByIDRow, error)
GetTemplateSchema(ctx context.Context, id int64) (GetTemplateSchemaRow, error)
GetTenantMembership(ctx context.Context, userID int64) (GetTenantMembershipRow, error)
GetTenantMembershipByTenantAndUser(ctx context.Context, arg GetTenantMembershipByTenantAndUserParams) (GetTenantMembershipByTenantAndUserRow, error)
GetUserByEmail(ctx context.Context, email string) (GetUserByEmailRow, error)
@@ -13,7 +13,7 @@ import (
const getTemplateByID = `-- name: GetTemplateByID :one
SELECT id, scope, tenant_id, origin_type, template_key, template_name,
schema_json, prompt_template, prompt_visibility, protected_prompt_asset_key,
prompt_template, prompt_visibility, protected_prompt_asset_key,
card_config_json, status, version_no, created_at, updated_at
FROM article_templates
WHERE id = $1
@@ -33,7 +33,6 @@ type GetTemplateByIDRow struct {
OriginType string `json:"origin_type"`
TemplateKey string `json:"template_key"`
TemplateName string `json:"template_name"`
SchemaJson []byte `json:"schema_json"`
PromptTemplate pgtype.Text `json:"prompt_template"`
PromptVisibility string `json:"prompt_visibility"`
ProtectedPromptAssetKey pgtype.Text `json:"protected_prompt_asset_key"`
@@ -54,7 +53,6 @@ func (q *Queries) GetTemplateByID(ctx context.Context, arg GetTemplateByIDParams
&i.OriginType,
&i.TemplateKey,
&i.TemplateName,
&i.SchemaJson,
&i.PromptTemplate,
&i.PromptVisibility,
&i.ProtectedPromptAssetKey,
@@ -67,28 +65,9 @@ func (q *Queries) GetTemplateByID(ctx context.Context, arg GetTemplateByIDParams
return i, err
}
const getTemplateSchema = `-- name: GetTemplateSchema :one
SELECT id, template_name, schema_json
FROM article_templates
WHERE id = $1 AND deleted_at IS NULL
`
type GetTemplateSchemaRow struct {
ID int64 `json:"id"`
TemplateName string `json:"template_name"`
SchemaJson []byte `json:"schema_json"`
}
func (q *Queries) GetTemplateSchema(ctx context.Context, id int64) (GetTemplateSchemaRow, error) {
row := q.db.QueryRow(ctx, getTemplateSchema, id)
var i GetTemplateSchemaRow
err := row.Scan(&i.ID, &i.TemplateName, &i.SchemaJson)
return i, err
}
const listTemplates = `-- name: ListTemplates :many
SELECT id, scope, tenant_id, origin_type, template_key, template_name,
schema_json, prompt_template, prompt_visibility, card_config_json,
prompt_template, prompt_visibility, card_config_json,
status, version_no, created_at, updated_at
FROM article_templates
WHERE (scope = 'platform' OR tenant_id = $1::bigint)
@@ -103,7 +82,6 @@ type ListTemplatesRow struct {
OriginType string `json:"origin_type"`
TemplateKey string `json:"template_key"`
TemplateName string `json:"template_name"`
SchemaJson []byte `json:"schema_json"`
PromptTemplate pgtype.Text `json:"prompt_template"`
PromptVisibility string `json:"prompt_visibility"`
CardConfigJson []byte `json:"card_config_json"`
@@ -129,7 +107,6 @@ func (q *Queries) ListTemplates(ctx context.Context, tenantID int64) ([]ListTemp
&i.OriginType,
&i.TemplateKey,
&i.TemplateName,
&i.SchemaJson,
&i.PromptTemplate,
&i.PromptVisibility,
&i.CardConfigJson,
@@ -1,6 +1,6 @@
-- name: ListTemplates :many
SELECT id, scope, tenant_id, origin_type, template_key, template_name,
schema_json, prompt_template, prompt_visibility, card_config_json,
prompt_template, prompt_visibility, card_config_json,
status, version_no, created_at, updated_at
FROM article_templates
WHERE (scope = 'platform' OR tenant_id = sqlc.arg(tenant_id)::bigint)
@@ -9,14 +9,9 @@ ORDER BY created_at DESC;
-- name: GetTemplateByID :one
SELECT id, scope, tenant_id, origin_type, template_key, template_name,
schema_json, prompt_template, prompt_visibility, protected_prompt_asset_key,
prompt_template, prompt_visibility, protected_prompt_asset_key,
card_config_json, status, version_no, created_at, updated_at
FROM article_templates
WHERE id = sqlc.arg(id)
AND (scope = 'platform' OR tenant_id = sqlc.arg(tenant_id)::bigint)
AND deleted_at IS NULL;
-- name: GetTemplateSchema :one
SELECT id, template_name, schema_json
FROM article_templates
WHERE id = sqlc.arg(id) AND deleted_at IS NULL;
@@ -61,7 +61,3 @@ func (r *cachedTemplateRepository) GetTemplateByID(ctx context.Context, id, tena
}
return record, nil
}
func (r *cachedTemplateRepository) GetTemplateSchema(ctx context.Context, id int64) (*TemplateSchemaRecord, error) {
return r.inner.GetTemplateSchema(ctx, id)
}
@@ -14,7 +14,6 @@ type TemplateRecord struct {
OriginType string
TemplateKey string
TemplateName string
SchemaJSON []byte
PromptTemplate *string
PromptVisibility string
ProtectedPromptAssetKey *string
@@ -25,16 +24,9 @@ type TemplateRecord struct {
UpdatedAt time.Time
}
type TemplateSchemaRecord struct {
ID int64
TemplateName string
SchemaJSON []byte
}
type TemplateRepository interface {
ListTemplates(ctx context.Context, tenantID int64) ([]TemplateRecord, error)
GetTemplateByID(ctx context.Context, id, tenantID int64) (*TemplateRecord, error)
GetTemplateSchema(ctx context.Context, id int64) (*TemplateSchemaRecord, error)
}
type templateRepository struct {
@@ -60,7 +52,6 @@ func (r *templateRepository) ListTemplates(ctx context.Context, tenantID int64)
OriginType: row.OriginType,
TemplateKey: row.TemplateKey,
TemplateName: row.TemplateName,
SchemaJSON: row.SchemaJson,
PromptTemplate: nullableText(row.PromptTemplate),
PromptVisibility: row.PromptVisibility,
CardConfigJSON: row.CardConfigJson,
@@ -90,7 +81,6 @@ func (r *templateRepository) GetTemplateByID(ctx context.Context, id, tenantID i
OriginType: row.OriginType,
TemplateKey: row.TemplateKey,
TemplateName: row.TemplateName,
SchemaJSON: row.SchemaJson,
PromptTemplate: nullableText(row.PromptTemplate),
PromptVisibility: row.PromptVisibility,
ProtectedPromptAssetKey: nullableText(row.ProtectedPromptAssetKey),
@@ -101,16 +91,3 @@ func (r *templateRepository) GetTemplateByID(ctx context.Context, id, tenantID i
UpdatedAt: timeFromTimestamp(row.UpdatedAt),
}, nil
}
func (r *templateRepository) GetTemplateSchema(ctx context.Context, id int64) (*TemplateSchemaRecord, error) {
row, err := r.q.GetTemplateSchema(ctx, id)
if err != nil {
return nil, err
}
return &TemplateSchemaRecord{
ID: row.ID,
TemplateName: row.TemplateName,
SchemaJSON: row.SchemaJson,
}, nil
}
@@ -30,7 +30,6 @@ func RegisterRoutes(app *bootstrap.App) {
tplHandler := NewTemplateHandler(app)
templates.GET("", tplHandler.List)
templates.GET("/:id", tplHandler.Detail)
templates.GET("/:id/schema", tplHandler.Schema)
templates.POST("/:id/drafts", tplHandler.SaveDraft)
templates.POST("/:id/analyze-tasks", tplHandler.CreateAnalyzeTask)
templates.GET("/:id/analyze_task_result", tplHandler.GetAnalyzeTaskResult)
@@ -54,20 +54,6 @@ func (h *TemplateHandler) Detail(c *gin.Context) {
response.Success(c, data)
}
func (h *TemplateHandler) Schema(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
response.Error(c, response.ErrBadRequest(40001, "invalid_id", "template id must be a number"))
return
}
data, err := h.svc.Schema(c.Request.Context(), id)
if err != nil {
response.Error(c, err)
return
}
response.Success(c, data)
}
func (h *TemplateHandler) Generate(c *gin.Context) {
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
if err != nil {
@@ -0,0 +1,2 @@
ALTER TABLE article_templates
ADD COLUMN IF NOT EXISTS schema_json JSONB NOT NULL DEFAULT '{}'::jsonb;
@@ -0,0 +1,2 @@
ALTER TABLE article_templates
DROP COLUMN IF EXISTS schema_json;
@@ -0,0 +1,2 @@
ALTER TABLE brands
DROP COLUMN IF EXISTS website;
@@ -0,0 +1,2 @@
ALTER TABLE brands
ADD COLUMN website TEXT;