github.com/easysoft/zendata@v0.0.0-20240513203326-705bd5a7fd67/internal/server/service/def.go (about)

     1  package serverService
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"github.com/easysoft/zendata/internal/pkg/domain"
    11  	"github.com/easysoft/zendata/internal/pkg/service"
    12  
    13  	consts "github.com/easysoft/zendata/internal/pkg/const"
    14  	"github.com/easysoft/zendata/internal/pkg/helper"
    15  	"github.com/easysoft/zendata/internal/pkg/model"
    16  	serverRepo "github.com/easysoft/zendata/internal/server/repo"
    17  	serverUtils "github.com/easysoft/zendata/internal/server/utils"
    18  	fileUtils "github.com/easysoft/zendata/pkg/utils/file"
    19  	"github.com/easysoft/zendata/pkg/utils/vari"
    20  	"gopkg.in/yaml.v3"
    21  	"gorm.io/gorm"
    22  )
    23  
    24  type DefService struct {
    25  	DefRepo     *serverRepo.DefRepo     `inject:""`
    26  	FieldRepo   *serverRepo.FieldRepo   `inject:""`
    27  	ReferRepo   *serverRepo.ReferRepo   `inject:""`
    28  	SectionRepo *serverRepo.SectionRepo `inject:""`
    29  	MockRepo    *serverRepo.MockRepo    `inject:""`
    30  
    31  	ResService     *ResService     `inject:""`
    32  	SectionService *SectionService `inject:""`
    33  
    34  	RangeService *service.RangeService `inject:""`
    35  }
    36  
    37  func (s *DefService) List(keywords string, page int) (list []*model.ZdDef, total int) {
    38  	list, total, _ = s.DefRepo.List(strings.TrimSpace(keywords), page)
    39  	return
    40  }
    41  
    42  func (s *DefService) Get(id int) (def model.ZdDef, dirs []domain.Dir) {
    43  	if id > 0 {
    44  		def, _ = s.DefRepo.Get(uint(id))
    45  	} else {
    46  		def.Folder = "users" + consts.PthSep
    47  		def.Type = "text"
    48  	}
    49  
    50  	serverUtils.GetDirs(consts.ResDirUsers, &dirs)
    51  
    52  	return
    53  }
    54  
    55  func (s *DefService) Save(def *model.ZdDef) (err error) {
    56  	def.Folder = serverUtils.DealWithPathSepRight(def.Folder)
    57  
    58  	def.Path = vari.WorkDir + def.Folder +
    59  		serverUtils.AddExt(strings.TrimSuffix(def.FileName, ".yaml"), ".yaml")
    60  
    61  	if def.ID == 0 {
    62  		err = s.Create(def)
    63  	} else {
    64  		err = s.Update(def)
    65  	}
    66  	s.updateYaml(def.ID)
    67  
    68  	return
    69  }
    70  
    71  func (s *DefService) Create(def *model.ZdDef) (err error) {
    72  	def.ReferName = helper.PathToName(def.Path, consts.ResDirUsers, def.Type)
    73  	err = s.DefRepo.Create(def)
    74  
    75  	// add root field node
    76  	rootField, err := s.FieldRepo.CreateTreeNode(def.ID, 0, "字段", "root")
    77  	s.ReferRepo.CreateDefault(rootField.ID, consts.ResTypeDef)
    78  	err = s.DefRepo.Update(def)
    79  
    80  	return
    81  }
    82  
    83  func (s *DefService) Update(def *model.ZdDef) (err error) {
    84  	var old model.ZdDef
    85  	old, err = s.DefRepo.Get(def.ID)
    86  	if err == gorm.ErrRecordNotFound {
    87  		return
    88  	}
    89  	if def.Path != old.Path {
    90  		fileUtils.RemoveExist(old.Path)
    91  	}
    92  
    93  	def.ReferName = helper.PathToName(def.Path, consts.ResDirUsers, def.Type)
    94  	err = s.DefRepo.Update(def)
    95  
    96  	return
    97  }
    98  
    99  func (s *DefService) Remove(id int) (err error) {
   100  	var old model.ZdDef
   101  	old, err = s.DefRepo.Get(uint(id))
   102  	if err == gorm.ErrRecordNotFound {
   103  		return
   104  	}
   105  	fileUtils.RemoveExist(old.Path)
   106  
   107  	err = s.DefRepo.Remove(uint(id))
   108  	return
   109  }
   110  
   111  func (s *DefService) updateYamlByField(fieldId uint) (err error) {
   112  	field, _ := s.FieldRepo.Get(fieldId)
   113  	return s.updateYaml(field.DefID)
   114  }
   115  
   116  func (s *DefService) updateYaml(id uint) (err error) {
   117  	var po model.ZdDef
   118  	po, _ = s.DefRepo.Get(id)
   119  
   120  	s.genYaml(&po)
   121  	err = s.DefRepo.UpdateYaml(po)
   122  	//update mock content
   123  	mockData := s.MockRepo.GetByDefID(id)
   124  	if mockData.ID > 0 {
   125  		mockData.DataContent = po.Yaml
   126  		s.MockRepo.Save(&mockData)
   127  	}
   128  	fileUtils.WriteFile(po.Path, po.Yaml)
   129  
   130  	return
   131  }
   132  
   133  func (s *DefService) genYaml(def *model.ZdDef) (str string) {
   134  	root, err := s.FieldRepo.GetDefFieldTree(def.ID)
   135  	if err != nil {
   136  		return
   137  	}
   138  
   139  	yamlObj := domain.DefData{}
   140  	s.DefRepo.GenDef(*def, &yamlObj)
   141  
   142  	for _, child := range root.Fields { // ignore the root
   143  		defField := domain.DefField{}
   144  
   145  		refer, _ := s.ReferRepo.GetByOwnerIdAndType(child.ID, consts.ResTypeDef)
   146  		s.zdFieldToFieldForExport(*child, refer, &defField)
   147  
   148  		yamlObj.Fields = append(yamlObj.Fields, defField)
   149  	}
   150  
   151  	bytes, err := yaml.Marshal(yamlObj)
   152  	def.Yaml = helper.ConvertYamlStringToMapFormat(bytes)
   153  
   154  	return
   155  }
   156  
   157  func (s *DefService) zdFieldToFieldForExport(treeNode model.ZdField, refer model.ZdRefer, field *domain.DefField) {
   158  	genFieldFromZdField(treeNode, refer, field)
   159  
   160  	for _, child := range treeNode.Fields {
   161  		childField := domain.DefField{}
   162  
   163  		childRefer, _ := s.ReferRepo.GetByOwnerId(child.ID)
   164  		s.zdFieldToFieldForExport(*child, childRefer, &childField)
   165  
   166  		field.Fields = append(field.Fields, childField)
   167  	}
   168  
   169  	//for _, from := range treeNode.Froms { // only one level
   170  	//	childField := model.DefField{}
   171  	//	genFieldFromZdField(*from, &childField)
   172  	//
   173  	//	field.Froms = append(field.Froms, childField)
   174  	//}
   175  
   176  	if len(field.Fields) == 0 {
   177  		field.Fields = nil
   178  	}
   179  	if len(field.Froms) == 0 {
   180  		field.Froms = nil
   181  	}
   182  
   183  	return
   184  }
   185  
   186  func (s *DefService) Sync(files []domain.ResFile) (err error) {
   187  	list := s.DefRepo.ListAll()
   188  
   189  	mp := map[string]*model.ZdDef{}
   190  	for _, item := range list {
   191  		mp[item.Path] = item
   192  	}
   193  
   194  	for _, fi := range files {
   195  		// for yaml "res", "data" type should be default value text
   196  		if fi.ResType == "" || fi.ResType == consts.ResTypeYaml {
   197  			fi.ResType = consts.ResTypeText
   198  		}
   199  
   200  		_, found := mp[fi.Path]
   201  		if !found { // no record
   202  			s.SyncToDB(fi, false)
   203  		} else if fi.UpdatedAt.Unix() > mp[fi.Path].UpdatedAt.Unix() { // db is old
   204  			s.DefRepo.Remove(mp[fi.Path].ID)
   205  			s.SyncToDB(fi, false)
   206  		}
   207  	}
   208  
   209  	return
   210  }
   211  func (s *DefService) SyncToDB(fi domain.ResFile, isMock bool) (err error, id uint) {
   212  	content, _ := os.ReadFile(fi.Path)
   213  	yamlContent := helper.ReplaceSpecialChars(content)
   214  	po := model.ZdDef{}
   215  	err = yaml.Unmarshal(yamlContent, &po)
   216  	po.Title = fi.Title
   217  	po.Type = fi.ResType
   218  	po.Desc = fi.Desc
   219  	po.Path = fi.Path
   220  	po.IsMock = isMock
   221  	po.Folder = serverUtils.GetRelativePath(po.Path)
   222  
   223  	po.ReferName = helper.PathToName(po.Path, consts.ResDirUsers, po.Type)
   224  	po.FileName = fileUtils.GetFileName(po.Path)
   225  
   226  	po.Yaml = string(content)
   227  
   228  	s.DefRepo.Create(&po)
   229  	id = po.ID
   230  
   231  	rootField, _ := s.FieldRepo.CreateTreeNode(po.ID, 0, "字段", "root")
   232  	s.ReferRepo.CreateDefault(rootField.ID, consts.ResTypeDef)
   233  	fmt.Println(rootField.ID, po.Type, po, rootField)
   234  	for i, field := range po.Fields {
   235  		field.Ord = i + 1
   236  		s.saveFieldToDB(&field, po, fi.Path, rootField.ID, po.ID)
   237  	}
   238  
   239  	return
   240  }
   241  func (s *DefService) saveFieldToDB(field *model.ZdField, def model.ZdDef, currPath string, parentID, defID uint) {
   242  	if field.Froms != nil && len(field.Froms) > 0 {
   243  		for idx, from := range field.Froms {
   244  			if from.Field == "" {
   245  				from.Field = "from" + strconv.Itoa(idx+1)
   246  			}
   247  			s.saveFieldToDB(from, def, currPath, parentID, defID)
   248  		}
   249  
   250  		return
   251  	}
   252  
   253  	// update field
   254  	field.DefID = defID
   255  	field.ParentID = parentID
   256  	if field.From == "" && def.From != "" {
   257  		field.From = def.From
   258  	}
   259  	if field.Type == "" {
   260  		field.Type = consts.FieldTypeList
   261  	}
   262  	if field.Mode == "" {
   263  		field.Mode = consts.ModeParallel
   264  	}
   265  
   266  	field.Range = strings.TrimSpace(field.Range)
   267  
   268  	// save field
   269  	s.FieldRepo.Save(field)
   270  
   271  	// create refer
   272  	refer := model.ZdRefer{OwnerType: "def", OwnerID: field.ID}
   273  
   274  	needToCreateSections := false
   275  	if field.Select != "" { // refer to excel
   276  		refer.Type = consts.ResTypeExcel
   277  
   278  		refer.ColName = field.Select
   279  		refer.Condition = field.Where
   280  		refer.Rand = field.Rand
   281  
   282  		_, sheet := fileUtils.ConvertResExcelPath(field.From, currPath)
   283  		refer.File = field.From
   284  		refer.Sheet = sheet
   285  
   286  	} else if field.Use != "" { // refer to ranges or instances, need to read yaml to get the type
   287  		rangeSections := s.RangeService.ParseRangeProperty(field.Use)
   288  		if len(rangeSections) > 0 { // only get the first one
   289  			rangeSection := rangeSections[0]
   290  			desc, _, count, countTag := s.RangeService.ParseRangeSection(rangeSection) // medium{2!}
   291  			refer.ColName = desc
   292  			refer.Count = count
   293  			refer.CountTag = countTag
   294  		}
   295  
   296  		path := fileUtils.ConvertReferRangeToPath(field.From, currPath)
   297  		_, _, refer.Type = helper.ReadYamlInfo(path)
   298  		refer.File = field.From
   299  
   300  	} else if field.Config != "" { // refer to config
   301  		refer.Type = consts.ResTypeConfig
   302  
   303  		rangeSections := s.RangeService.ParseRangeProperty(field.Config) // dir/config.yaml
   304  		if len(rangeSections) > 0 {                                      // only get the first one
   305  			rangeSection := rangeSections[0]
   306  			desc, _, count, countTag := s.RangeService.ParseRangeSection(rangeSection)
   307  			refer.Count = count
   308  			refer.CountTag = countTag
   309  
   310  			path := fileUtils.ConvertReferRangeToPath(desc, currPath)
   311  			refer.File = GetRelatedPathWithResDir(path)
   312  		}
   313  
   314  	} else if field.Range != "" {
   315  		rangeSections := s.RangeService.ParseRangeProperty(field.Range)
   316  		if len(rangeSections) > 0 {
   317  			rangeSection := rangeSections[0]                                              // deal with yaml and text refer using range prop
   318  			desc, step, count, countTag := s.RangeService.ParseRangeSection(rangeSection) // dir/users.txt:R{3}
   319  			if filepath.Ext(desc) == ".txt" || filepath.Ext(desc) == ".yaml" {
   320  				if filepath.Ext(desc) == ".txt" { // dir/users.txt:2
   321  					refer.Type = consts.ResTypeText
   322  
   323  					if strings.ToLower(step) == "r" {
   324  						refer.Rand = true
   325  					} else {
   326  						refer.Step, _ = strconv.Atoi(step)
   327  					}
   328  
   329  				} else if filepath.Ext(desc) == ".yaml" { // dir/content.yaml{3}
   330  					refer.Type = consts.ResTypeYaml
   331  
   332  					refer.Count = count
   333  					refer.CountTag = countTag
   334  				}
   335  
   336  				path := fileUtils.ConvertReferRangeToPath(desc, currPath)
   337  				refer.File = GetRelatedPathWithResDir(path)
   338  			} else { // like 1-9,a-z
   339  				needToCreateSections = true
   340  			}
   341  		}
   342  	}
   343  
   344  	// save refer
   345  	refer.OwnerID = field.ID
   346  	s.ReferRepo.Save(&refer)
   347  
   348  	// gen sections if needed
   349  	if needToCreateSections {
   350  		rangeSections := s.RangeService.ParseRangeProperty(field.Range)
   351  
   352  		for i, rangeSection := range rangeSections {
   353  			s.SectionService.SaveFieldSectionToDB(rangeSection, i, field.ID, "def")
   354  		}
   355  	}
   356  
   357  	// deal with field's children
   358  	for _, child := range field.Fields {
   359  		s.saveFieldToDB(child, def, currPath, field.ID, defID)
   360  	}
   361  }