577 lines
21 KiB
Go
577 lines
21 KiB
Go
package run
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
|
|
knowledgecontent "git.iwork-ai.com/xdc/iqudo-top1/internal/knowledge"
|
|
"git.iwork-ai.com/xdc/iqudo-top1/internal/model"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
var knowledgePlaceholderPattern = regexp.MustCompile(`\{\{\s*(?:input|derived|form)\.([A-Za-z][A-Za-z0-9_]*)\s*\}\}`)
|
|
|
|
type KnowledgeView struct {
|
|
CardID uint64 `json:"card_id"`
|
|
CardVersionID uint64 `json:"card_version_id"`
|
|
Version int `json:"version"`
|
|
Title string `json:"title"`
|
|
StandardCopy string `json:"standard_copy"`
|
|
ForbiddenCopy string `json:"forbidden_copy"`
|
|
RiskNote string `json:"risk_note"`
|
|
Copies []KnowledgeCopy `json:"copies"`
|
|
}
|
|
|
|
type KnowledgeCopy struct {
|
|
ID string `json:"id"`
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
}
|
|
|
|
type NodeView struct {
|
|
NodeKey string `json:"node_key"`
|
|
Type string `json:"type"`
|
|
Title string `json:"title"`
|
|
Content string `json:"content"`
|
|
Config json.RawMessage `json:"config,omitempty"`
|
|
Fields []PublicFieldView `json:"fields,omitempty"`
|
|
Presentation *NodePresentation `json:"presentation,omitempty"`
|
|
Knowledge *KnowledgeView `json:"knowledge,omitempty"`
|
|
Outputs []KnowledgeGroup `json:"outputs"`
|
|
Collection *KnowledgeCollectionView `json:"collection,omitempty"`
|
|
}
|
|
|
|
type PublicFieldView struct {
|
|
Key string `json:"key"`
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Required bool `json:"required"`
|
|
Options json.RawMessage `json:"options"`
|
|
Validation json.RawMessage `json:"validation"`
|
|
}
|
|
|
|
type KnowledgeGroup struct {
|
|
Key string `json:"key"`
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Content json.RawMessage `json:"content"`
|
|
Relations map[string][]KnowledgeItemView `json:"relations"`
|
|
Suggested bool `json:"suggested,omitempty"`
|
|
}
|
|
|
|
type KnowledgeItemView struct {
|
|
Key string `json:"key"`
|
|
Name string `json:"name"`
|
|
Type string `json:"type"`
|
|
Content json.RawMessage `json:"content"`
|
|
}
|
|
|
|
type knowledgeNodeConfig struct {
|
|
KnowledgeCardID uint64 `json:"knowledge_card_id"`
|
|
KnowledgeCardVersionID uint64 `json:"knowledge_card_version_id"`
|
|
KnowledgeSelector *knowledgeSelector `json:"knowledge_selector"`
|
|
KnowledgeCollection *knowledgeCollectionConfig `json:"knowledge_collection"`
|
|
}
|
|
|
|
type knowledgeCollectionConfig struct {
|
|
ContextFieldKeys []string `json:"context_field_keys"`
|
|
ContextTitle string `json:"context_title"`
|
|
ContextHint string `json:"context_hint"`
|
|
SelectionTitle string `json:"selection_title"`
|
|
SelectionHint string `json:"selection_hint"`
|
|
Steps []knowledgeCollectionStep `json:"steps"`
|
|
}
|
|
|
|
type knowledgeCollectionStep struct {
|
|
FieldKey string `json:"field_key"`
|
|
Name string `json:"name"`
|
|
Root bool `json:"root"`
|
|
CandidateScope string `json:"candidate_scope"`
|
|
KnowledgeTypes []string `json:"knowledge_types"`
|
|
FromField string `json:"from_field"`
|
|
RelationType string `json:"relation_type"`
|
|
Required bool `json:"required"`
|
|
Multiple bool `json:"multiple"`
|
|
}
|
|
|
|
type KnowledgeCollectionView struct {
|
|
ContextFields []PublicFieldView `json:"context_fields"`
|
|
ContextTitle string `json:"context_title"`
|
|
ContextHint string `json:"context_hint"`
|
|
SelectionTitle string `json:"selection_title"`
|
|
SelectionHint string `json:"selection_hint"`
|
|
Steps []KnowledgeCollectionStepView `json:"steps"`
|
|
}
|
|
|
|
type KnowledgeCollectionStepView struct {
|
|
FieldKey string `json:"field_key"`
|
|
Name string `json:"name"`
|
|
Required bool `json:"required"`
|
|
Multiple bool `json:"multiple"`
|
|
FromField string `json:"from_field,omitempty"`
|
|
Options []KnowledgeCollectionOption `json:"options"`
|
|
}
|
|
|
|
type KnowledgeCollectionOption struct {
|
|
Value string `json:"value"`
|
|
Label string `json:"label"`
|
|
Parents []string `json:"parents,omitempty"`
|
|
Suggested bool `json:"suggested,omitempty"`
|
|
}
|
|
|
|
type knowledgeSelector struct {
|
|
DerivedField string `json:"derived_field"`
|
|
AnswerField string `json:"answer_field"`
|
|
CandidateScope string `json:"candidate_scope"`
|
|
KnowledgeTypes []string `json:"knowledge_types"`
|
|
KnowledgeKeys []string `json:"knowledge_keys"`
|
|
RelationTypes []string `json:"relation_types"`
|
|
RelationLabels map[string]string `json:"relation_labels"`
|
|
}
|
|
|
|
func (h *Handler) nodeView(db *gorm.DB, node model.SOPNode, tenantID uint64, answers map[string]interface{}) (NodeView, error) {
|
|
view := NodeView{NodeKey: node.NodeKey, Type: node.Type, Title: node.Title, Content: node.Content, Config: json.RawMessage(node.Config)}
|
|
if node.Type == "start" {
|
|
presentation, err := loadStartPresentation(db, node, tenantID, answers)
|
|
if err != nil {
|
|
return view, err
|
|
}
|
|
view.Presentation = presentation
|
|
}
|
|
if node.Type == "question" || node.Type == "choice" || node.Type == "form" {
|
|
fields, err := loadPublicNodeFields(db, node, tenantID)
|
|
if err != nil {
|
|
return view, err
|
|
}
|
|
view.Fields = fields
|
|
}
|
|
if node.Type != "knowledge" {
|
|
return view, nil
|
|
}
|
|
var config knowledgeNodeConfig
|
|
if err := json.Unmarshal(node.Config, &config); err != nil {
|
|
return view, err
|
|
}
|
|
if config.KnowledgeSelector != nil {
|
|
outputs, err := loadKnowledgeOutputs(db, node, tenantID, answers, *config.KnowledgeSelector)
|
|
view.Outputs = outputs
|
|
if err != nil {
|
|
return view, err
|
|
}
|
|
if config.KnowledgeCollection != nil {
|
|
collection, err := loadKnowledgeCollection(db, node, tenantID, *config.KnowledgeCollection, outputs, answers)
|
|
view.Collection = &collection
|
|
return view, err
|
|
}
|
|
return view, nil
|
|
}
|
|
knowledge, err := loadKnowledgeView(db, node, tenantID, answers)
|
|
if err != nil {
|
|
return view, err
|
|
}
|
|
view.Knowledge = &knowledge
|
|
return view, nil
|
|
}
|
|
|
|
func loadKnowledgeCollection(db *gorm.DB, node model.SOPNode, tenantID uint64, config knowledgeCollectionConfig, roots []KnowledgeGroup, context map[string]interface{}) (KnowledgeCollectionView, error) {
|
|
fields, err := loadPublicFieldsByKeys(db, node, tenantID, config.ContextFieldKeys)
|
|
if err != nil {
|
|
return KnowledgeCollectionView{}, err
|
|
}
|
|
view := KnowledgeCollectionView{ContextFields: fields, ContextTitle: config.ContextTitle, ContextHint: config.ContextHint, SelectionTitle: config.SelectionTitle, SelectionHint: config.SelectionHint, Steps: make([]KnowledgeCollectionStepView, 0, len(config.Steps))}
|
|
childrenByParent := map[string]map[string][]string{}
|
|
allItems := make([]model.KnowledgeItem, 0)
|
|
if raw, ok := context["__knowledge_snapshot"].(json.RawMessage); ok && len(raw) > 0 {
|
|
snapshot, parseErr := parseKnowledgeSnapshot(raw)
|
|
if parseErr != nil {
|
|
return KnowledgeCollectionView{}, parseErr
|
|
}
|
|
byID := map[uint64]model.KnowledgeItem{}
|
|
for _, item := range snapshot.Items {
|
|
byID[item.ID] = item
|
|
if item.Status == "active" {
|
|
allItems = append(allItems, item)
|
|
}
|
|
}
|
|
for _, relation := range snapshot.Relations {
|
|
from, fromOK := byID[relation.FromKnowledgeID]
|
|
to, toOK := byID[relation.ToKnowledgeID]
|
|
if !fromOK || !toOK || from.Status != "active" || to.Status != "active" {
|
|
continue
|
|
}
|
|
if childrenByParent[from.Name] == nil {
|
|
childrenByParent[from.Name] = map[string][]string{}
|
|
}
|
|
childrenByParent[from.Name][relation.RelationType] = appendUnique(childrenByParent[from.Name][relation.RelationType], to.Name)
|
|
}
|
|
}
|
|
suggestedRoots := make(map[string]bool, len(roots))
|
|
rootOptions := make([]KnowledgeCollectionOption, 0, len(roots))
|
|
for _, root := range roots {
|
|
suggestedRoots[root.Name] = root.Suggested
|
|
rootOptions = append(rootOptions, KnowledgeCollectionOption{Value: root.Name, Label: root.Name, Suggested: root.Suggested})
|
|
}
|
|
for _, step := range config.Steps {
|
|
item := KnowledgeCollectionStepView{FieldKey: step.FieldKey, Name: step.Name, Required: step.Required, Multiple: step.Multiple, FromField: step.FromField, Options: []KnowledgeCollectionOption{}}
|
|
if step.Root {
|
|
item.Options = append(item.Options, rootOptions...)
|
|
if step.CandidateScope == "all" {
|
|
seen := map[string]bool{}
|
|
for _, option := range item.Options {
|
|
seen[option.Value] = true
|
|
}
|
|
for _, knowledgeItem := range allItems {
|
|
if seen[knowledgeItem.Name] || (len(step.KnowledgeTypes) > 0 && !containsString(step.KnowledgeTypes, knowledgeItem.Type)) {
|
|
continue
|
|
}
|
|
item.Options = append(item.Options, KnowledgeCollectionOption{Value: knowledgeItem.Name, Label: knowledgeItem.Name, Suggested: suggestedRoots[knowledgeItem.Name]})
|
|
}
|
|
sort.SliceStable(item.Options, func(i, j int) bool {
|
|
if item.Options[i].Suggested != item.Options[j].Suggested {
|
|
return item.Options[i].Suggested
|
|
}
|
|
return item.Options[i].Label < item.Options[j].Label
|
|
})
|
|
}
|
|
} else {
|
|
seen := map[string]*KnowledgeCollectionOption{}
|
|
var parentStep *KnowledgeCollectionStepView
|
|
for index := range view.Steps {
|
|
if view.Steps[index].FieldKey == step.FromField {
|
|
parentStep = &view.Steps[index]
|
|
break
|
|
}
|
|
}
|
|
if parentStep != nil {
|
|
for _, parent := range parentStep.Options {
|
|
for _, childName := range childrenByParent[parent.Value][step.RelationType] {
|
|
option := seen[childName]
|
|
if option == nil {
|
|
option = &KnowledgeCollectionOption{Value: childName, Label: childName}
|
|
seen[childName] = option
|
|
}
|
|
option.Parents = appendUnique(option.Parents, parent.Value)
|
|
}
|
|
}
|
|
}
|
|
for _, option := range seen {
|
|
item.Options = append(item.Options, *option)
|
|
}
|
|
sort.Slice(item.Options, func(i, j int) bool { return item.Options[i].Label < item.Options[j].Label })
|
|
}
|
|
view.Steps = append(view.Steps, item)
|
|
}
|
|
return view, nil
|
|
}
|
|
|
|
func loadPublicFieldsByKeys(db *gorm.DB, node model.SOPNode, tenantID uint64, keys []string) ([]PublicFieldView, error) {
|
|
if len(keys) == 0 {
|
|
return []PublicFieldView{}, nil
|
|
}
|
|
var fields []model.ScenarioField
|
|
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 := map[string]model.ScenarioField{}
|
|
for _, field := range fields {
|
|
byKey[field.FieldKey] = field
|
|
}
|
|
result := make([]PublicFieldView, 0, len(keys))
|
|
for _, key := range keys {
|
|
if field, ok := byKey[key]; ok {
|
|
result = append(result, PublicFieldView{Key: field.FieldKey, Name: field.FieldName, Type: field.FieldType, Required: field.Required, Options: json.RawMessage(field.Options), Validation: json.RawMessage(field.Validation)})
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func appendUnique(values []string, value string) []string {
|
|
for _, existing := range values {
|
|
if existing == value {
|
|
return values
|
|
}
|
|
}
|
|
return append(values, value)
|
|
}
|
|
|
|
func loadPublicNodeFields(db *gorm.DB, node model.SOPNode, tenantID uint64) ([]PublicFieldView, error) {
|
|
var config answerNodeConfig
|
|
if err := json.Unmarshal(node.Config, &config); err != nil {
|
|
return nil, err
|
|
}
|
|
keys := config.FieldKeys
|
|
if config.FieldKey != "" {
|
|
keys = []string{config.FieldKey}
|
|
}
|
|
if len(keys) == 0 {
|
|
return nil, nil
|
|
}
|
|
var fields []model.ScenarioField
|
|
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
|
|
}
|
|
views := make([]PublicFieldView, 0, len(keys))
|
|
for _, key := range keys {
|
|
field, ok := byKey[key]
|
|
if !ok {
|
|
continue
|
|
}
|
|
views = append(views, PublicFieldView{Key: field.FieldKey, Name: field.FieldName, Type: field.FieldType, Required: field.Required || config.Required || containsString(config.RequiredFieldKeys, key), Options: json.RawMessage(field.Options), Validation: json.RawMessage(field.Validation)})
|
|
}
|
|
return views, nil
|
|
}
|
|
|
|
func loadKnowledgeOutputs(db *gorm.DB, node model.SOPNode, tenantID uint64, context map[string]interface{}, selector knowledgeSelector) ([]KnowledgeGroup, error) {
|
|
var sopRow struct{ ScenarioID uint64 }
|
|
if err := db.Table("sop_nodes n").Select("s.scenario_id").Joins("JOIN sop_versions sv ON sv.id = n.sop_version_id").Joins("JOIN sops s ON s.id = sv.sop_id").Where("n.id = ? AND n.tenant_id = ?", node.ID, tenantID).Scan(&sopRow).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
return loadKnowledgeOutputsForScenario(db, sopRow.ScenarioID, tenantID, context, selector)
|
|
}
|
|
|
|
func loadKnowledgeOutputsForScenario(db *gorm.DB, scenarioID, tenantID uint64, context map[string]interface{}, selector knowledgeSelector) ([]KnowledgeGroup, error) {
|
|
keys := append([]string{}, selector.KnowledgeKeys...)
|
|
if selector.DerivedField != "" {
|
|
value, _ := lookupContextValue(context, "derived."+selector.DerivedField)
|
|
keys = append(keys, stringSlice(value)...)
|
|
}
|
|
if selector.AnswerField != "" {
|
|
value, _ := lookupContextValue(context, "form."+selector.AnswerField)
|
|
keys = append(keys, stringSlice(value)...)
|
|
}
|
|
if len(keys) == 0 && selector.CandidateScope != "all" {
|
|
return []KnowledgeGroup{}, nil
|
|
}
|
|
keySet := make(map[string]bool, len(keys))
|
|
for _, key := range keys {
|
|
keySet[key] = true
|
|
}
|
|
items := make([]model.KnowledgeItem, 0)
|
|
relations := make([]model.KnowledgeRelation, 0)
|
|
snapshotTargets := map[uint64]model.KnowledgeItem{}
|
|
if raw, ok := context["__knowledge_snapshot"].(json.RawMessage); ok && len(raw) > 0 {
|
|
snapshot, err := parseKnowledgeSnapshot(raw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, item := range snapshot.Items {
|
|
snapshotTargets[item.ID] = item
|
|
if item.Status != "active" {
|
|
continue
|
|
}
|
|
if selector.CandidateScope != "all" && len(keys) > 0 && !knowledgeCandidateMatches(keySet, item) {
|
|
continue
|
|
}
|
|
if len(selector.KnowledgeTypes) > 0 && !containsString(selector.KnowledgeTypes, item.Type) {
|
|
continue
|
|
}
|
|
items = append(items, item)
|
|
}
|
|
for _, relation := range snapshot.Relations {
|
|
if len(selector.RelationTypes) > 0 && !containsString(selector.RelationTypes, relation.RelationType) {
|
|
continue
|
|
}
|
|
relations = append(relations, relation)
|
|
}
|
|
} else {
|
|
query := db.Where("tenant_id = ? AND scenario_id = ? AND status = ?", tenantID, scenarioID, "active")
|
|
if selector.CandidateScope != "all" && len(keys) > 0 {
|
|
query = query.Where("item_key IN ? OR name IN ?", keys, keys)
|
|
}
|
|
if len(selector.KnowledgeTypes) > 0 {
|
|
query = query.Where("type IN ?", selector.KnowledgeTypes)
|
|
}
|
|
if err := query.Order("sort_order, id").Find(&items).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
ids := make([]uint64, 0, len(items))
|
|
for _, item := range items {
|
|
if selector.CandidateScope == "all" || len(keySet) == 0 || knowledgeCandidateMatches(keySet, item) {
|
|
ids = append(ids, item.ID)
|
|
}
|
|
}
|
|
if len(relations) == 0 && len(ids) > 0 {
|
|
rq := db.Where("tenant_id = ? AND scenario_id = ? AND from_knowledge_id IN ?", tenantID, scenarioID, ids)
|
|
if len(selector.RelationTypes) > 0 {
|
|
rq = rq.Where("relation_type IN ?", selector.RelationTypes)
|
|
}
|
|
if err := rq.Order("sort_order,id").Find(&relations).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
targetIDs := make([]uint64, 0, len(relations))
|
|
for _, rel := range relations {
|
|
targetIDs = append(targetIDs, rel.ToKnowledgeID)
|
|
}
|
|
targets := make([]model.KnowledgeItem, 0)
|
|
if len(snapshotTargets) > 0 {
|
|
for _, id := range targetIDs {
|
|
if target, ok := snapshotTargets[id]; ok && target.Status == "active" {
|
|
targets = append(targets, target)
|
|
}
|
|
}
|
|
} else if len(targetIDs) > 0 {
|
|
if err := db.Where("tenant_id = ? AND id IN ? AND status = ?", tenantID, targetIDs, "active").Find(&targets).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
targetMap := map[uint64]model.KnowledgeItem{}
|
|
for _, target := range targets {
|
|
targetMap[target.ID] = target
|
|
}
|
|
relationMap := map[uint64]map[string][]KnowledgeItemView{}
|
|
for _, rel := range relations {
|
|
matched, err := matchCondition(json.RawMessage(rel.Condition), context)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("知识关系 %d 条件不正确: %w", rel.ID, err)
|
|
}
|
|
if !matched {
|
|
continue
|
|
}
|
|
target, ok := targetMap[rel.ToKnowledgeID]
|
|
if !ok {
|
|
continue
|
|
}
|
|
if relationMap[rel.FromKnowledgeID] == nil {
|
|
relationMap[rel.FromKnowledgeID] = map[string][]KnowledgeItemView{}
|
|
}
|
|
relationMap[rel.FromKnowledgeID][rel.RelationType] = append(relationMap[rel.FromKnowledgeID][rel.RelationType], KnowledgeItemView{Key: target.ItemKey, Name: target.Name, Type: target.Type, Content: renderKnowledgeContent(target.Content, context)})
|
|
}
|
|
outputs := make([]KnowledgeGroup, 0, len(items))
|
|
for _, item := range items {
|
|
outputs = append(outputs, KnowledgeGroup{Key: item.ItemKey, Name: item.Name, Type: item.Type, Content: renderKnowledgeContent(item.Content, context), Relations: relationMap[item.ID], Suggested: knowledgeCandidateMatches(keySet, item)})
|
|
}
|
|
return outputs, nil
|
|
}
|
|
|
|
func knowledgeCandidateMatches(candidates map[string]bool, item model.KnowledgeItem) bool {
|
|
return candidates[item.ItemKey] || candidates[item.Name]
|
|
}
|
|
|
|
func containsString(values []string, target string) bool {
|
|
for _, value := range values {
|
|
if value == target {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func renderKnowledgeContent(raw []byte, context map[string]interface{}) json.RawMessage {
|
|
var value interface{}
|
|
if json.Unmarshal(raw, &value) != nil {
|
|
return json.RawMessage(raw)
|
|
}
|
|
value = renderKnowledgeValue(value, context)
|
|
rendered, _ := json.Marshal(value)
|
|
return rendered
|
|
}
|
|
func renderKnowledgeValue(value interface{}, context map[string]interface{}) interface{} {
|
|
switch typed := value.(type) {
|
|
case string:
|
|
return knowledgePlaceholderPattern.ReplaceAllStringFunc(typed, func(token string) string {
|
|
match := knowledgePlaceholderPattern.FindStringSubmatch(token)
|
|
if len(match) != 2 {
|
|
return token
|
|
}
|
|
resolved, ok := lookupContextValue(context, tokenNamespaceKey(token, match[1]))
|
|
if !ok || resolved == nil {
|
|
return "未提供"
|
|
}
|
|
if values, ok := resolved.([]interface{}); ok {
|
|
parts := make([]string, 0, len(values))
|
|
for _, item := range values {
|
|
parts = append(parts, fmt.Sprint(item))
|
|
}
|
|
return strings.Join(parts, "、")
|
|
}
|
|
return fmt.Sprint(resolved)
|
|
})
|
|
case []interface{}:
|
|
for index, item := range typed {
|
|
typed[index] = renderKnowledgeValue(item, context)
|
|
}
|
|
return typed
|
|
case map[string]interface{}:
|
|
for key, item := range typed {
|
|
typed[key] = renderKnowledgeValue(item, context)
|
|
}
|
|
return typed
|
|
default:
|
|
return value
|
|
}
|
|
}
|
|
|
|
func tokenNamespaceKey(token, key string) string {
|
|
trimmed := strings.TrimSpace(strings.TrimSuffix(strings.TrimPrefix(token, "{{"), "}}"))
|
|
if strings.Contains(trimmed, ".") {
|
|
return trimmed
|
|
}
|
|
return key
|
|
}
|
|
|
|
func stringSlice(value interface{}) []string {
|
|
result := []string{}
|
|
switch values := value.(type) {
|
|
case []interface{}:
|
|
for _, item := range values {
|
|
if text, ok := item.(string); ok {
|
|
result = append(result, text)
|
|
}
|
|
}
|
|
case []string:
|
|
return values
|
|
case string:
|
|
return []string{values}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func loadKnowledgeView(db *gorm.DB, node model.SOPNode, tenantID uint64, answers map[string]interface{}) (KnowledgeView, error) {
|
|
var config knowledgeNodeConfig
|
|
if err := json.Unmarshal(node.Config, &config); err != nil {
|
|
return KnowledgeView{}, err
|
|
}
|
|
type row struct {
|
|
model.KnowledgeCardVersion
|
|
Title string `gorm:"column:title"`
|
|
}
|
|
var version row
|
|
query := db.Table("knowledge_card_versions kv").Select("kv.*, kc.title").
|
|
Joins("JOIN knowledge_cards kc ON kc.id = kv.knowledge_card_id").
|
|
Where("kv.tenant_id = ? AND kc.tenant_id = ? AND kv.knowledge_card_id = ?", tenantID, tenantID, config.KnowledgeCardID)
|
|
if config.KnowledgeCardVersionID != 0 {
|
|
query = query.Where("kv.id = ?", config.KnowledgeCardVersionID)
|
|
} else {
|
|
query = query.Where("kv.status = ?", "published").Order("kv.version DESC")
|
|
}
|
|
if err := query.First(&version).Error; err != nil {
|
|
return KnowledgeView{}, err
|
|
}
|
|
content, err := knowledgecontent.ParseContent(version.Content)
|
|
if err != nil {
|
|
return KnowledgeView{}, err
|
|
}
|
|
rendered := knowledgecontent.Render(content, answers)
|
|
copies := make([]KnowledgeCopy, 0, len(rendered))
|
|
for _, copy := range rendered {
|
|
copies = append(copies, KnowledgeCopy{ID: copy.ID, Title: copy.Title, Content: copy.Content})
|
|
}
|
|
standardCopy := ""
|
|
if len(copies) > 0 {
|
|
standardCopy = copies[0].Content
|
|
}
|
|
return KnowledgeView{
|
|
CardID: version.KnowledgeCardID, CardVersionID: version.ID, Version: version.Version, Title: version.Title,
|
|
StandardCopy: standardCopy, ForbiddenCopy: content.ForbiddenCopy, RiskNote: content.RiskNote, Copies: copies,
|
|
}, nil
|
|
}
|