feat: simplify SOP to immediate-effect configuration
This commit is contained in:
160
internal/run/start_presentation.go
Normal file
160
internal/run/start_presentation.go
Normal file
@@ -0,0 +1,160 @@
|
||||
package run
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"git.iwork-ai.com/xdc/iqudo-top1/internal/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type NodePresentation struct {
|
||||
Summary []PresentationField `json:"summary"`
|
||||
Items []PresentationItem `json:"items"`
|
||||
Opening *PresentationCopy `json:"opening,omitempty"`
|
||||
}
|
||||
|
||||
type PresentationField struct {
|
||||
Key string `json:"key"`
|
||||
Label string `json:"label"`
|
||||
Kind string `json:"kind"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
type PresentationItem struct {
|
||||
Fields []PresentationField `json:"fields"`
|
||||
}
|
||||
|
||||
type PresentationCopy struct {
|
||||
Title string `json:"title"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type startNodeConfig struct {
|
||||
Presentation *startPresentationConfig `json:"presentation"`
|
||||
}
|
||||
|
||||
type startPresentationConfig struct {
|
||||
SummaryFieldKeys []string `json:"summary_field_keys"`
|
||||
ItemFieldKeys []string `json:"item_field_keys"`
|
||||
ImageFieldKeys []string `json:"image_field_keys"`
|
||||
OpeningTitle string `json:"opening_title"`
|
||||
OpeningTemplate string `json:"opening_template"`
|
||||
}
|
||||
|
||||
func loadStartPresentation(db *gorm.DB, node model.SOPNode, tenantID uint64, context map[string]interface{}) (*NodePresentation, error) {
|
||||
var config startNodeConfig
|
||||
if err := json.Unmarshal(node.Config, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if config.Presentation == nil {
|
||||
return nil, nil
|
||||
}
|
||||
keys := append([]string{}, config.Presentation.SummaryFieldKeys...)
|
||||
keys = append(keys, config.Presentation.ItemFieldKeys...)
|
||||
fields := make([]model.ScenarioField, 0)
|
||||
if len(keys) > 0 {
|
||||
if err := db.Table("scenario_fields sf").Select("sf.*").
|
||||
Joins("JOIN sops s ON s.scenario_id = sf.scenario_id").
|
||||
Joins("JOIN sop_versions sv ON sv.sop_id = s.id").
|
||||
Where("sv.id = ? AND sf.tenant_id = ? AND sf.field_key IN ?", node.SOPVersionID, tenantID, keys).
|
||||
Find(&fields).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
byKey := make(map[string]model.ScenarioField, len(fields))
|
||||
for _, field := range fields {
|
||||
byKey[field.FieldKey] = field
|
||||
}
|
||||
return buildStartPresentation(*config.Presentation, byKey, context), nil
|
||||
}
|
||||
|
||||
func buildStartPresentation(config startPresentationConfig, fields map[string]model.ScenarioField, context map[string]interface{}) *NodePresentation {
|
||||
view := &NodePresentation{Summary: []PresentationField{}, Items: []PresentationItem{}}
|
||||
imageKeys := make(map[string]bool, len(config.ImageFieldKeys))
|
||||
for _, key := range config.ImageFieldKeys {
|
||||
imageKeys[key] = true
|
||||
}
|
||||
for _, key := range config.SummaryFieldKeys {
|
||||
value, ok := lookupContextValue(context, "input."+key)
|
||||
if !ok || presentationValueEmpty(value) {
|
||||
continue
|
||||
}
|
||||
view.Summary = append(view.Summary, presentationField(key, value, fields, imageKeys))
|
||||
}
|
||||
itemValues := make(map[string][]interface{}, len(config.ItemFieldKeys))
|
||||
itemCount := 0
|
||||
for _, key := range config.ItemFieldKeys {
|
||||
value, _ := lookupContextValue(context, "input."+key)
|
||||
values := presentationValues(value)
|
||||
itemValues[key] = values
|
||||
if len(values) > itemCount {
|
||||
itemCount = len(values)
|
||||
}
|
||||
}
|
||||
for index := 0; index < itemCount; index++ {
|
||||
item := PresentationItem{Fields: []PresentationField{}}
|
||||
for _, key := range config.ItemFieldKeys {
|
||||
values := itemValues[key]
|
||||
if index >= len(values) || presentationValueEmpty(values[index]) {
|
||||
continue
|
||||
}
|
||||
item.Fields = append(item.Fields, presentationField(key, values[index], fields, imageKeys))
|
||||
}
|
||||
if len(item.Fields) > 0 {
|
||||
view.Items = append(view.Items, item)
|
||||
}
|
||||
}
|
||||
if config.OpeningTemplate != "" {
|
||||
title := config.OpeningTitle
|
||||
if title == "" {
|
||||
title = "开场话术"
|
||||
}
|
||||
view.Opening = &PresentationCopy{Title: title, Content: fmt.Sprint(renderKnowledgeValue(config.OpeningTemplate, context))}
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
func presentationField(key string, value interface{}, fields map[string]model.ScenarioField, imageKeys map[string]bool) PresentationField {
|
||||
label := key
|
||||
if field, ok := fields[key]; ok && field.FieldName != "" {
|
||||
label = field.FieldName
|
||||
}
|
||||
kind := "text"
|
||||
if imageKeys[key] {
|
||||
kind = "image"
|
||||
}
|
||||
return PresentationField{Key: key, Label: label, Kind: kind, Value: value}
|
||||
}
|
||||
|
||||
func presentationValues(value interface{}) []interface{} {
|
||||
switch values := value.(type) {
|
||||
case []interface{}:
|
||||
return values
|
||||
case []string:
|
||||
result := make([]interface{}, 0, len(values))
|
||||
for _, item := range values {
|
||||
result = append(result, item)
|
||||
}
|
||||
return result
|
||||
case nil:
|
||||
return nil
|
||||
default:
|
||||
return []interface{}{value}
|
||||
}
|
||||
}
|
||||
|
||||
func presentationValueEmpty(value interface{}) bool {
|
||||
switch typed := value.(type) {
|
||||
case nil:
|
||||
return true
|
||||
case string:
|
||||
return typed == ""
|
||||
case []interface{}:
|
||||
return len(typed) == 0
|
||||
case []string:
|
||||
return len(typed) == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user