github.com/easysoft/zendata@v0.0.0-20240513203326-705bd5a7fd67/internal/pkg/service/excel.go (about) 1 package service 2 3 import ( 4 "fmt" 5 "log" 6 "os" 7 "strconv" 8 "strings" 9 "time" 10 11 "github.com/easysoft/zendata/internal/pkg/domain" 12 "github.com/easysoft/zendata/internal/pkg/helper" 13 14 "github.com/360EntSecGroup-Skylar/excelize/v2" 15 consts "github.com/easysoft/zendata/internal/pkg/const" 16 fileUtils "github.com/easysoft/zendata/pkg/utils/file" 17 i118Utils "github.com/easysoft/zendata/pkg/utils/i118" 18 logUtils "github.com/easysoft/zendata/pkg/utils/log" 19 "github.com/easysoft/zendata/pkg/utils/vari" 20 "github.com/fatih/color" 21 "gorm.io/gorm" 22 ) 23 24 type ExcelService struct { 25 ExpressionService *ExpressionService `inject:""` 26 } 27 28 func (s *ExcelService) generateFieldValuesFromExcel(filePath, sheet string, field *domain.DefField, total int) ( 29 values map[string][]interface{}) { 30 values = map[string][]interface{}{} 31 32 // sql has variable expr 33 if filePath == "" || helper.IsSelectExcelWithExpr(*field) { 34 return 35 } 36 37 dbName := s.getDbName(filePath) 38 39 if !fileUtils.IsDir(filePath) { // file 40 firstSheet := s.ConvertSingleExcelToSQLiteIfNeeded(dbName, filePath) 41 if sheet == "" { 42 sheet = firstSheet 43 } 44 } else { // dir, for article generation only 45 s.ConvertWordExcelsToSQLiteIfNeeded(dbName, filePath) 46 } 47 48 list, fieldSelect := s.ReadDataFromSQLite(*field, dbName, sheet, total, filePath) 49 // get index list for data retrieve 50 numbs := helper.GenerateItems(int64(0), int64(len(list)-1), int64(1), 0, false, 1, "", 0) 51 52 // get data by index 53 index := 0 54 for _, numb := range numbs { 55 item := list[numb.(int64)%(int64(len(list)))] 56 57 if index >= consts.MaxNumb { 58 break 59 } 60 61 values[fieldSelect] = append(values[fieldSelect], item) 62 index = index + 1 63 } 64 65 return 66 } 67 68 func (s *ExcelService) getDbName(path string) (dbName string) { 69 dbName = strings.Replace(path, vari.WorkDir+consts.ResDirData+consts.PthSep, "", -1) 70 dbName = strings.Replace(dbName, consts.PthSep, "_", -1) 71 dbName = strings.Replace(dbName, ".", "_", -1) 72 73 return 74 } 75 76 func (s *ExcelService) ConvertSingleExcelToSQLiteIfNeeded(dbName string, path string) (firstSheet string) { 77 excel, err := excelize.OpenFile(path) 78 if err != nil { 79 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_read_file", path)) 80 return 81 } 82 83 firstSheet = excel.GetSheetList()[0] 84 changed, sqlBeforeCompleted := s.isExcelChanged(path) 85 if !changed { 86 return 87 } 88 89 for _, sheet := range excel.GetSheetList() { 90 rows, err := excel.GetRows(sheet) 91 92 if len(rows) == 0 { 93 continue 94 } 95 96 dropTemplate := `DROP TABLE IF EXISTS %s;` 97 ddlTemplate := `CREATE TABLE %s ( 98 %s 99 );` 100 insertTemplate := "INSERT INTO %s (%s) VALUES %s" 101 102 colDefine := "" 103 colList := "" 104 colCount := 0 105 index := 0 106 for _, col := range rows[0] { 107 colCount++ 108 109 val := strings.TrimSpace(col) 110 if index > 0 { 111 colDefine = colDefine + ",\n" 112 colList = colList + ", " 113 } 114 115 colProp := "" 116 if val == "seq" { 117 colProp = "CHAR (5) PRIMARY KEY ASC UNIQUE" 118 } else { 119 colProp = "VARCHAR" 120 } 121 colDefine = " " + colDefine + "`" + val + "` " + colProp 122 123 colList = colList + "`" + val + "`" 124 index++ 125 } 126 127 valList := "" 128 for rowIndex, row := range rows { 129 if rowIndex == 0 { 130 continue 131 } 132 133 if rowIndex > 1 { 134 valList = valList + ", " 135 } 136 valList = valList + "(" 137 138 dataColCount := 0 139 for colIndex, colCell := range row { 140 if colIndex >= colCount { 141 break 142 } 143 144 if colIndex > 0 { 145 valList = valList + ", " 146 } 147 colCell = strings.Replace(colCell, "'", "''", -1) 148 valList = valList + "'" + colCell + "'" 149 150 dataColCount++ 151 } 152 153 for dataColCount < colCount { 154 valList = valList + ", ''" 155 dataColCount++ 156 } 157 158 valList = valList + ")" 159 } 160 161 tableName := dbName + "_" + sheet 162 dropSql := fmt.Sprintf(dropTemplate, tableName) 163 ddl := fmt.Sprintf(ddlTemplate, tableName, colDefine) 164 insertSql := fmt.Sprintf(insertTemplate, tableName, colList, valList) 165 166 err = vari.DB.Exec(dropSql).Error 167 if err != nil { 168 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_drop_table", tableName, err.Error())) 169 return 170 } 171 172 err = vari.DB.Exec(ddl).Error 173 if err != nil { 174 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_create_table", tableName, err.Error())) 175 return 176 } 177 178 err = vari.DB.Exec(insertSql).Error 179 if err != nil { 180 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_exec_query", insertSql, err.Error())) 181 return 182 } 183 184 if changed { 185 err = vari.DB.Exec(sqlBeforeCompleted).Error 186 if err != nil { 187 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_exec_query", sqlBeforeCompleted, err.Error())) 188 } 189 } 190 } 191 192 return 193 } 194 195 func (s *ExcelService) ConvertWordExcelsToSQLiteIfNeeded(tableName string, dir string) { 196 changed, sqlBeforeCompleted := s.isExcelChanged(dir) 197 if !changed { 198 return 199 } 200 201 files := make([]string, 0) 202 fileUtils.GetFilesByExtInDir(dir, ".xlsx", &files) 203 204 seq := 1 205 insertSqls := make([]string, 0) 206 ddlFields := make([]string, 0) 207 ddlFields = append(ddlFields, " `词语` VARCHAR DEFAULT ''") 208 209 colMap := map[string]bool{} 210 for _, file := range files { 211 s.genImportExcelSqls(file, tableName, &seq, &ddlFields, &insertSqls, &colMap) 212 } 213 214 dropSql := `DROP TABLE IF EXISTS ` + tableName + `;` 215 err := vari.DB.Exec(dropSql).Error 216 if err != nil { 217 log.Println(i118Utils.I118Prt.Sprintf("fail_to_drop_table", tableName, err.Error())) 218 return 219 } 220 221 ddlTemplate := "CREATE TABLE " + tableName + "(\n" + 222 "\t`seq` CHAR (5) PRIMARY KEY ASC UNIQUE,\n" + 223 "%s" + 224 "\n);" 225 ddlSql := fmt.Sprintf(ddlTemplate, strings.Join(ddlFields, ", \n")) 226 err = vari.DB.Exec(ddlSql).Error 227 if err != nil { 228 log.Println(i118Utils.I118Prt.Sprintf("fail_to_create_table", tableName, err.Error())) 229 return 230 } 231 232 insertSql := strings.Join(insertSqls, "\n") 233 err = vari.DB.Exec(insertSql).Error 234 if err != nil { 235 log.Println(i118Utils.I118Prt.Sprintf("fail_to_exec_query", insertSql, err.Error())) 236 return 237 } 238 239 if changed { 240 err = vari.DB.Exec(sqlBeforeCompleted).Error 241 if err != nil { 242 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_exec_query", sqlBeforeCompleted, err.Error())) 243 } 244 } 245 246 return 247 } 248 249 func (s *ExcelService) ReadDataFromSQLite(field domain.DefField, dbName string, tableName string, total int, filePath string) ( 250 []string, string) { 251 list := make([]string, 0) 252 253 fieldSelect := field.Select 254 from := dbName 255 if tableName != "" { 256 from += "_" + tableName 257 } 258 259 where := strings.TrimSpace(field.Where) 260 if vari.GlobalVars.DefData.Type == consts.DefTypeArticle { 261 if where == "" { 262 where = "y" 263 } 264 265 cols := strings.Split(fieldSelect, "-") 266 wheres := "" 267 for index, col := range cols { 268 if index == 0 { 269 wheres += fmt.Sprintf("`%s` = '%s'", col, "y") 270 } else { 271 wheres += " AND " 272 wheres += fmt.Sprintf("`%s` = '%s'", col, where) 273 } 274 } 275 276 where = wheres 277 278 } else { 279 if where == "" { 280 where = "1=1" 281 } 282 } 283 284 if field.Rand { 285 where += " ORDER BY RANDOM() " 286 } 287 288 if !strings.Contains(where, "LIMIT") { 289 if total > consts.MaxNumb { 290 total = consts.MaxNumb 291 } 292 if field.Limit > 0 && total > field.Limit { 293 total = field.Limit 294 } 295 296 where = where + fmt.Sprintf(" LIMIT %d", total) 297 } 298 299 colStr := fieldSelect 300 if vari.GlobalVars.DefData.Type == consts.DefTypeArticle { 301 colStr = "`词语` AS `" + fieldSelect + "`" 302 } 303 304 sqlStr := fmt.Sprintf("SELECT %s FROM `%s` WHERE %s", colStr, from, where) 305 rows, err := vari.DB.Raw(sqlStr).Rows() 306 307 if err != nil { 308 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_exec_query", "", err.Error())) 309 logUtils.PrintToWithColor(i118Utils.I118Prt.Sprintf("pls_check_excel", filePath), color.FgRed) 310 311 return list, "" 312 } 313 314 defer rows.Close() 315 valMapArr := make([]map[string]string, 0) 316 columns, err := rows.Columns() 317 colNum := len(columns) 318 319 colIndexToName := map[int]string{} 320 for index, col := range columns { 321 colIndexToName[index] = col 322 } 323 324 var values = make([]interface{}, colNum) 325 for i := range values { 326 var itf string 327 values[i] = &itf 328 } 329 330 for rows.Next() { 331 err = rows.Scan(values...) 332 if err != nil { 333 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_parse_row", err.Error())) 334 return list, "" 335 } 336 337 rowMap := map[string]string{} 338 for index, v := range values { 339 item := v.(*string) 340 341 rowMap[colIndexToName[index]] = *item 342 } 343 344 valMapArr = append(valMapArr, rowMap) 345 } 346 347 for _, item := range valMapArr { 348 idx := 0 349 for _, val := range item { 350 if idx > 0 { 351 break 352 } 353 list = append(list, val) 354 idx++ 355 } 356 } 357 358 return list, fieldSelect 359 } 360 361 func (s *ExcelService) genExcelValuesWithExpr(field *domain.DefField, fieldNameToValuesMap map[string][]interface{}) ( 362 values []interface{}) { 363 selects := s.ExpressionService.ReplaceVariableValues(field.Select, fieldNameToValuesMap) 364 wheres := s.ExpressionService.ReplaceVariableValues(field.Where, fieldNameToValuesMap) 365 366 childMapValues := make([][]interface{}, 0) 367 for index, slct := range selects { 368 temp := *field 369 temp.Select = slct 370 temp.Where = wheres[index%len(wheres)] 371 372 resFile, _, sheet := fileUtils.GetResProp(temp.From, temp.FileDir) 373 374 selectCount := vari.GlobalVars.Total/len(selects) + 1 375 mp := s.generateFieldValuesFromExcel(resFile, sheet, &temp, selectCount) // re-generate values 376 for _, items := range mp { 377 childMapValues = append(childMapValues, items) 378 } 379 } 380 for index := 0; len(values) < vari.GlobalVars.Total; { 381 for i := range selects { 382 values = append(values, childMapValues[i][index%len(childMapValues[i])]) 383 } 384 index++ 385 } 386 387 return 388 } 389 390 type ExcelChangedResult struct { 391 Id uint 392 Path string 393 ChangeTime int64 `gorm:"column:changeTime"` 394 } 395 396 func (s *ExcelService) isExcelChanged(path string) (changed bool, sqlBeforeCompleted string) { 397 if !fileUtils.FileExist(path) { 398 return 399 } 400 401 fileChangeTime := time.Time{}.Unix() 402 if !fileUtils.IsDir(path) { 403 fileChangeTime = s.getFileModTime(path).Unix() 404 } else { 405 fileChangeTime = s.getDirModTime(path).Unix() 406 } 407 408 sqlStr := fmt.Sprintf("SELECT id, path, changeTime FROM %s "+ 409 "WHERE path = '%s' "+ 410 "ORDER BY changeTime DESC "+ 411 "LIMIT 1;", 412 consts.SqliteTrackTable, path) 413 414 record := ExcelChangedResult{} 415 err := vari.DB.Raw(sqlStr).Scan(&record).Error 416 417 if err != nil && err != gorm.ErrRecordNotFound { 418 logUtils.PrintTo(i118Utils.I118Prt.Sprintf("fail_to_exec_query", sqlStr, err.Error())) 419 420 changed = true 421 return 422 } 423 424 found := false 425 426 if record.Id > 0 { // found 427 found = true 428 429 if path == record.Path && record.ChangeTime < fileChangeTime { // update exist record 430 changed = true 431 } 432 } else { // not found, to add a record 433 changed = true 434 } 435 436 if changed { 437 if !found { 438 sqlBeforeCompleted = fmt.Sprintf("INSERT INTO %s(path, changeTime) VALUES('%s', %d)", 439 consts.SqliteTrackTable, path, fileChangeTime) 440 } else { 441 sqlBeforeCompleted = fmt.Sprintf("UPDATE %s SET changeTime = %d WHERE path = '%s'", 442 consts.SqliteTrackTable, fileChangeTime, path) 443 } 444 } 445 446 return 447 } 448 449 func (s *ExcelService) getDirModTime(path string) (dirChangeTime time.Time) { 450 files := make([]string, 0) 451 fileUtils.GetFilesByExtInDir(path, "", &files) 452 453 for _, file := range files { 454 time := s.getFileModTime(file) 455 if dirChangeTime.Unix() < time.Unix() { 456 dirChangeTime = time 457 } 458 } 459 460 return 461 } 462 463 func (s *ExcelService) getFileModTime(path string) time.Time { 464 f, err := os.Open(path) 465 if err != nil { 466 log.Println("open file error:" + path) 467 return time.Now() 468 } 469 defer f.Close() 470 471 fi, err := f.Stat() 472 if err != nil { 473 log.Println("stat fileinfo error") 474 return time.Now() 475 } 476 477 fileChangeTime := fi.ModTime() 478 479 //timeStr := fileChangeTime.Format("2006-01-02 15:04:05") 480 //logUtils.Screen(i118Utils.I118Prt.Sprintf("file_change_time", timeStr)) 481 482 return fileChangeTime 483 } 484 485 func (s *ExcelService) genImportExcelSqls(filePath, tableName string, seq *int, ddlFields, insertSqls *[]string, colMap *map[string]bool) { 486 excel, err := excelize.OpenFile(filePath) 487 if err != nil { 488 log.Println("fail to read file " + filePath + ", error: " + err.Error()) 489 return 490 } 491 492 fileName := fileUtils.GetFileName(filePath) 493 fileName = strings.TrimSuffix(fileName, "词库") 494 495 colPrefix := fileName // stringUtils.GetPinyin(fileName) 496 *ddlFields = append(*ddlFields, " `"+colPrefix+"` VARCHAR DEFAULT ''") 497 498 for sheetIndex, sheet := range excel.GetSheetList() { 499 rows, _ := excel.GetRows(sheet) 500 if len(rows) == 0 { 501 continue 502 } 503 504 colDefine := "" 505 colList := make([]string, 0) 506 507 colCount := 0 508 index := 0 509 // gen col list for ddl and insert cols 510 for colIndex, col := range rows[0] { 511 val := strings.TrimSpace(col) 512 513 if sheetIndex == 0 && val == "" { 514 break 515 } 516 colCount++ 517 518 colList = append(colList, val) 519 520 colNames := val 521 colNameArr := strings.Split(colNames, "-") 522 for _, colName := range colNameArr { 523 if (*colMap)[colName] == false { 524 colType := "VARCHAR" 525 colDefine = " " + "`" + colName + "` " + colType + " DEFAULT ''" 526 527 if colIndex == 0 { 528 colName = "词语" 529 } else { // first already added 530 *ddlFields = append(*ddlFields, colDefine) 531 } 532 533 (*colMap)[colName] = true 534 } 535 } 536 537 index++ 538 } 539 540 insertTemplate := "INSERT INTO `" + tableName + "` (%s) VALUES (%s);" 541 // gen values for insert 542 for rowIndex, row := range rows { 543 if rowIndex == 0 { // ignore title line 544 continue 545 } 546 547 record := map[string]interface{}{} 548 record[colPrefix] = "y" 549 record["seq"] = *seq 550 *seq += 1 551 552 for colIndex, col := range row { 553 if colIndex >= len(colList) { 554 break 555 } 556 557 colNames := colList[colIndex] 558 559 val := strings.ToLower(strings.TrimSpace(col)) 560 if val == "" { 561 continue 562 } 563 564 if colIndex == 0 { // word 565 record["词语"] = val 566 } else { 567 if val == "y" || val == "b" || val == "f" || val == "m" { 568 val = "y" 569 } else { 570 val = "" 571 } 572 573 colNameArr := strings.Split(colNames, "-") 574 for _, colName := range colNameArr { 575 record[colName] = val 576 } 577 } 578 } 579 580 cols := make([]string, 0) 581 vals := make([]string, 0) 582 583 for key, val := range record { 584 cols = append(cols, "`"+key+"`") 585 586 valStr := "" 587 switch val.(type) { 588 case int: 589 valStr = strconv.Itoa(val.(int)) 590 default: 591 valStr = "'" + val.(string) + "'" 592 } 593 594 vals = append(vals, valStr) 595 } 596 597 insertSql := fmt.Sprintf(insertTemplate, strings.Join(cols, ","), strings.Join(vals, ",")) 598 599 *insertSqls = append(*insertSqls, insertSql) 600 } 601 } 602 }