Initial commit: web_hghl_server

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 16:28:14 +08:00
co-authored by Claude Opus 4.7
commit 65fca60622
653 changed files with 135901 additions and 0 deletions
+160
View File
@@ -0,0 +1,160 @@
package srv_contentexcel
import (
"bytes"
"fmt"
"strconv"
"strings"
"github.com/xuri/excelize/v2"
)
const (
colID = "ID"
colTitle = "标题"
colDesc = "描述"
colKeyworld = "关键词"
colRedLineRemark = "备注(红线-建议下架)"
sheetName = "Sheet1"
excelHeaders = 4
colRedLineRemarkIdx = 4
)
var headerAliases = map[string]int{
"id": 0, colID: 0,
"title": 1, colTitle: 1,
"desc": 2, "description": 2, colDesc: 2,
"keyworld": 3, "keywords": 3, "keyword": 3, colKeyworld: 3,
colRedLineRemark: colRedLineRemarkIdx,
}
func buildWorkbook(rows []ContentRow) ([]byte, error) {
f := excelize.NewFile()
defer f.Close()
if err := f.SetSheetRow(sheetName, "A1", &[]string{colID, colTitle, colDesc, colKeyworld}); err != nil {
return nil, err
}
for i, row := range rows {
cell, err := excelize.CoordinatesToCellName(1, i+2)
if err != nil {
return nil, err
}
if err := f.SetSheetRow(sheetName, cell, &[]any{
row.ID,
row.Title,
row.Desc,
strings.Join(row.Keyworld, ","),
}); err != nil {
return nil, err
}
}
var buf bytes.Buffer
if err := f.Write(&buf); err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func parseWorkbook(data []byte) ([]ContentRow, error) {
f, err := excelize.OpenReader(bytes.NewReader(data))
if err != nil {
return nil, fmt.Errorf("打开 Excel 失败: %w", err)
}
defer f.Close()
sheets := f.GetSheetList()
if len(sheets) == 0 {
return nil, fmt.Errorf("Excel 无工作表")
}
rows, err := f.GetRows(sheets[0])
if err != nil {
return nil, fmt.Errorf("读取 Excel 失败: %w", err)
}
if len(rows) <= 1 {
return nil, fmt.Errorf("Excel 无数据行")
}
colIndex := mapHeaderColumns(rows[0])
if len(colIndex) < excelHeaders {
return nil, fmt.Errorf("Excel 表头需包含: ID、标题、描述、关键词")
}
result := make([]ContentRow, 0, len(rows)-1)
for lineNo, row := range rows[1:] {
contentRow, ok, err := parseDataRow(row, colIndex, lineNo+2)
if err != nil {
return nil, err
}
if !ok {
continue
}
result = append(result, contentRow)
}
if len(result) == 0 {
return nil, fmt.Errorf("Excel 无有效数据行")
}
return result, nil
}
func mapHeaderColumns(header []string) map[int]int {
index := make(map[int]int, excelHeaders)
for i, name := range header {
key := strings.ToLower(strings.TrimSpace(name))
if col, ok := headerAliases[key]; ok {
index[col] = i
}
}
return index
}
func parseDataRow(row []string, colIndex map[int]int, lineNo int) (ContentRow, bool, error) {
if remarkIdx, ok := colIndex[colRedLineRemarkIdx]; ok {
if cellValue(row, remarkIdx) != "" {
return ContentRow{}, false, nil
}
}
idStr := cellValue(row, colIndex[0])
if idStr == "" {
return ContentRow{}, false, nil
}
id, err := strconv.ParseInt(idStr, 10, 64)
if err != nil || id <= 0 {
return ContentRow{}, false, fmt.Errorf("第 %d 行 ID 无效: %q", lineNo, idStr)
}
return ContentRow{
ID: id,
Title: cellValue(row, colIndex[1]),
Desc: cellValue(row, colIndex[2]),
Keyworld: parseKeywords(cellValue(row, colIndex[3])),
}, true, nil
}
func cellValue(row []string, index int) string {
if index < 0 || index >= len(row) {
return ""
}
return strings.TrimSpace(row[index])
}
func parseKeywords(raw string) []string {
raw = strings.TrimSpace(raw)
if raw == "" {
return nil
}
raw = strings.NewReplacer(",", ",", ";", ",", ";", ",").Replace(raw)
parts := strings.Split(raw, ",")
keywords := make([]string, 0, len(parts))
for _, part := range parts {
part = strings.TrimSpace(part)
if part != "" {
keywords = append(keywords, part)
}
}
return keywords
}