332 lines
9.3 KiB
Go
332 lines
9.3 KiB
Go
package controller
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"log"
|
||
"strconv"
|
||
"sync"
|
||
"time"
|
||
_type "xianyv/type"
|
||
"xianyv/utils/checkUtil"
|
||
"xianyv/utils/creatGoodsUtil"
|
||
|
||
"github.com/xuri/excelize/v2"
|
||
)
|
||
|
||
type GoodsController struct {
|
||
ExcelPath string
|
||
TxtPath string
|
||
SheetName string
|
||
}
|
||
|
||
// 简化缓存结构,避免复杂锁嵌套
|
||
type FileCache struct {
|
||
categoryMap map[string]string
|
||
categoryTime time.Time
|
||
categoryMutex sync.RWMutex
|
||
|
||
// Excel数据内存缓存,key为sheetName,value为map[col][int32]bool
|
||
excelCache map[string]map[string]map[int32]bool
|
||
excelCacheTime map[string]time.Time
|
||
excelCacheMutex sync.RWMutex
|
||
excelCacheDuration time.Duration
|
||
|
||
// Excel操作全局互斥锁(兼容老逻辑,后续可移除)
|
||
excelMutex sync.Mutex
|
||
cacheDuration time.Duration
|
||
}
|
||
|
||
var (
|
||
fileCacheInstance *FileCache
|
||
once sync.Once
|
||
)
|
||
|
||
func GetFileCache() *FileCache {
|
||
once.Do(func() {
|
||
fileCacheInstance = &FileCache{
|
||
cacheDuration: 5 * time.Minute,
|
||
excelCache: make(map[string]map[string]map[int32]bool),
|
||
excelCacheTime: make(map[string]time.Time),
|
||
excelCacheDuration: 5 * time.Minute,
|
||
}
|
||
})
|
||
return fileCacheInstance
|
||
}
|
||
|
||
// 预加载Excel校验数据到内存
|
||
func (fc *FileCache) PreloadExcelCache(filename, sheetName string, columns []string) error {
|
||
fc.excelCacheMutex.Lock()
|
||
defer fc.excelCacheMutex.Unlock()
|
||
|
||
// 只加载指定sheet和列
|
||
cache := make(map[string]map[int32]bool) // col -> set of int32
|
||
|
||
f, err := excelize.OpenFile(filename)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer f.Close()
|
||
|
||
rows, err := f.GetRows(sheetName)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
for _, col := range columns {
|
||
cache[col] = make(map[int32]bool)
|
||
}
|
||
for rowNum := 1; rowNum <= len(rows); rowNum++ {
|
||
for _, col := range columns {
|
||
cellAddress := fmt.Sprintf("%s%d", col, rowNum)
|
||
cellValue, err := f.GetCellValue(sheetName, cellAddress)
|
||
if err != nil || cellValue == "" {
|
||
continue
|
||
}
|
||
cellInt, err := strconv.ParseInt(cellValue, 10, 32)
|
||
if err != nil {
|
||
continue
|
||
}
|
||
cache[col][int32(cellInt)] = true
|
||
}
|
||
}
|
||
if fc.excelCache == nil {
|
||
fc.excelCache = make(map[string]map[string]map[int32]bool)
|
||
}
|
||
fc.excelCache[sheetName] = cache
|
||
if fc.excelCacheTime == nil {
|
||
fc.excelCacheTime = make(map[string]time.Time)
|
||
}
|
||
fc.excelCacheTime[sheetName] = time.Now()
|
||
return nil
|
||
}
|
||
|
||
// GetCategoryMap 获取类目映射(简化版本)
|
||
func (fc *FileCache) GetCategoryMap(txtPath string) (map[string]string, error) {
|
||
fc.categoryMutex.RLock()
|
||
if fc.categoryMap != nil && time.Since(fc.categoryTime) < fc.cacheDuration {
|
||
defer fc.categoryMutex.RUnlock()
|
||
return fc.categoryMap, nil
|
||
}
|
||
fc.categoryMutex.RUnlock()
|
||
|
||
fc.categoryMutex.Lock()
|
||
defer fc.categoryMutex.Unlock()
|
||
|
||
// 双重检查
|
||
if fc.categoryMap != nil && time.Since(fc.categoryTime) < fc.cacheDuration {
|
||
return fc.categoryMap, nil
|
||
}
|
||
|
||
log.Printf("更新类目缓存...")
|
||
categoryMap, err := checkUtil.CheckValuesInTxt(txtPath)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
fc.categoryMap = categoryMap
|
||
fc.categoryTime = time.Now()
|
||
log.Printf("类目缓存更新完成,共 %d 个类目", len(categoryMap))
|
||
return categoryMap, nil
|
||
}
|
||
|
||
// CheckValuesInExcelWithMutex 优先用内存缓存校验,若无则自动预加载
|
||
func (fc *FileCache) CheckValuesInExcelWithMutex(filename, sheetName string, values map[string]int32) (map[string]bool, error) {
|
||
// 先尝试用内存缓存
|
||
fc.excelCacheMutex.RLock()
|
||
cache, ok := fc.excelCache[sheetName]
|
||
fc.excelCacheMutex.RUnlock()
|
||
columns := make([]string, 0, len(values))
|
||
for col := range values {
|
||
columns = append(columns, col)
|
||
}
|
||
if !ok {
|
||
// 缓存不存在,自动预加载
|
||
if err := fc.PreloadExcelCache(filename, sheetName, columns); err != nil {
|
||
return nil, err
|
||
}
|
||
fc.excelCacheMutex.RLock()
|
||
cache, ok = fc.excelCache[sheetName]
|
||
fc.excelCacheMutex.RUnlock()
|
||
if !ok {
|
||
return nil, fmt.Errorf("Excel缓存加载失败: %s", sheetName)
|
||
}
|
||
}
|
||
// 校验
|
||
result := make(map[string]bool)
|
||
for col, target := range values {
|
||
if colMap, ok := cache[col]; ok {
|
||
result[col] = colMap[target]
|
||
} else {
|
||
result[col] = false
|
||
}
|
||
}
|
||
return result, nil
|
||
}
|
||
|
||
// createErrorResponse 创建错误响应
|
||
func createErrorResponse(format string, args ...interface{}) ([]byte, error) {
|
||
resp := _type.ErrResponse{
|
||
Code: 0,
|
||
Msg: fmt.Sprintf(format, args...),
|
||
Data: struct{}{},
|
||
}
|
||
response, err := json.Marshal(resp)
|
||
return response, err
|
||
}
|
||
|
||
// GoodsCreatController 修复死锁问题的版本
|
||
func (c *GoodsController) GoodsCreatController(body _type.Body, batchCreatRequest, domain string, flag bool) ([]byte, error) {
|
||
startTime := time.Now()
|
||
defer func() {
|
||
log.Printf("GoodsCreatController 执行时间: %v", time.Since(startTime))
|
||
}()
|
||
|
||
fileCache := GetFileCache()
|
||
|
||
// 类目ID基础校验
|
||
catStart := time.Now()
|
||
categoryMap, err := fileCache.GetCategoryMap(c.TxtPath)
|
||
log.Printf("GetCategoryMap elapsed=%v", time.Since(catStart))
|
||
if err != nil {
|
||
log.Printf("读取文件失败: %v", err)
|
||
return createErrorResponse("读取文件失败: %v", err)
|
||
}
|
||
|
||
if _, exists := categoryMap[body.CatIds]; !exists {
|
||
log.Printf("错误的类目ID: %s", body.CatIds)
|
||
return createErrorResponse("错误的类目ID: %s", body.CatIds)
|
||
}
|
||
|
||
// 省市区基础校验
|
||
valuesToCheck := map[string]int32{
|
||
"A": body.Province,
|
||
"C": body.City,
|
||
"E": body.District,
|
||
}
|
||
|
||
checkStart := time.Now()
|
||
checkResults, err := fileCache.CheckValuesInExcelWithMutex(c.ExcelPath, c.SheetName, valuesToCheck)
|
||
log.Printf("CheckValuesInExcelWithMutex elapsed=%v", time.Since(checkStart))
|
||
if err != nil {
|
||
log.Printf("Excel检查失败: %v", err)
|
||
} else {
|
||
log.Printf("检查结果: %v", checkResults)
|
||
}
|
||
|
||
// 业务逻辑处理
|
||
switch body.TypePlatform {
|
||
case 4:
|
||
createStart := time.Now()
|
||
createResponse, err := creatGoodsUtil.XianYvCreat(body, batchCreatRequest, body.AppId, body.AppSecret, domain, flag)
|
||
log.Printf("XianYvCreat elapsed=%v", time.Since(createStart))
|
||
if err != nil {
|
||
log.Printf("创建商品失败: %v", err)
|
||
return createErrorResponse("创建商品失败: %v", err)
|
||
}
|
||
return createResponse, nil
|
||
default:
|
||
return createErrorResponse("平台编号有误: %d", body.TypePlatform)
|
||
}
|
||
}
|
||
|
||
// GoodsCreatController 分离BookData的全局变量
|
||
func (c *GoodsController) GoodsCreatControllerNew(body _type.BodyNew, batchCreatRequest, domain string, flag bool) ([]byte, error) {
|
||
startTime := time.Now()
|
||
defer func() {
|
||
log.Printf("GoodsCreatController 执行时间: %v", time.Since(startTime))
|
||
}()
|
||
|
||
fileCache := GetFileCache()
|
||
|
||
// 类目ID基础校验
|
||
catStart := time.Now()
|
||
categoryMap, err := fileCache.GetCategoryMap(c.TxtPath)
|
||
log.Printf("GetCategoryMap elapsed=%v", time.Since(catStart))
|
||
if err != nil {
|
||
log.Printf("读取文件失败: %v", err)
|
||
return createErrorResponse("读取文件失败: %v", err)
|
||
}
|
||
|
||
if _, exists := categoryMap[body.CatIds]; !exists {
|
||
log.Printf("错误的类目ID: %s", body.CatIds)
|
||
return createErrorResponse("错误的类目ID: %s", body.CatIds)
|
||
}
|
||
|
||
// 省市区基础校验
|
||
valuesToCheck := map[string]int32{
|
||
"A": body.Province,
|
||
"C": body.City,
|
||
"E": body.District,
|
||
}
|
||
|
||
checkStart := time.Now()
|
||
checkResults, err := fileCache.CheckValuesInExcelWithMutex(c.ExcelPath, c.SheetName, valuesToCheck)
|
||
log.Printf("CheckValuesInExcelWithMutex elapsed=%v", time.Since(checkStart))
|
||
if err != nil {
|
||
log.Printf("Excel检查失败: %v", err)
|
||
} else {
|
||
log.Printf("检查结果: %v", checkResults)
|
||
}
|
||
|
||
// 业务逻辑处理
|
||
switch body.TypePlatform {
|
||
case 4:
|
||
createStart := time.Now()
|
||
if len(body.BookData) > 0 {
|
||
createResponse, err := creatGoodsUtil.XianYvCreatNew(body, batchCreatRequest, body.AppId, body.AppSecret, domain, flag)
|
||
log.Printf("XianYvCreat elapsed=%v", time.Since(createStart))
|
||
if err != nil {
|
||
log.Printf("创建商品失败: %v", err)
|
||
return createErrorResponse("创建商品失败: %v", err)
|
||
}
|
||
return createResponse, nil
|
||
} else {
|
||
createResponse, err := creatGoodsUtil.XianYvCreatNoIsbn(body, batchCreatRequest, body.AppId, body.AppSecret, domain, flag)
|
||
log.Printf("XianYvCreat elapsed=%v", time.Since(createStart))
|
||
if err != nil {
|
||
log.Printf("创建商品失败: %v", err)
|
||
return createErrorResponse("创建商品失败: %v", err)
|
||
}
|
||
return createResponse, nil
|
||
}
|
||
|
||
default:
|
||
return createErrorResponse("平台编号有误: %d", body.TypePlatform)
|
||
}
|
||
}
|
||
|
||
// 刷新指定sheet的Excel缓存(重新加载)
|
||
func (fc *FileCache) RefreshExcelCache(filename, sheetName string, columns []string) error {
|
||
return fc.PreloadExcelCache(filename, sheetName, columns)
|
||
}
|
||
|
||
// 清空所有Excel缓存(下次校验时会自动重新加载)
|
||
func (fc *FileCache) ClearAllExcelCache() {
|
||
fc.excelCacheMutex.Lock()
|
||
defer fc.excelCacheMutex.Unlock()
|
||
fc.excelCache = make(map[string]map[string]map[int32]bool)
|
||
fc.excelCacheTime = make(map[string]time.Time)
|
||
}
|
||
|
||
// 直接在内存缓存校验Excel值
|
||
// values: map[col]目标值
|
||
// 返回: map[col]是否存在
|
||
func (fc *FileCache) CheckValuesInExcelCache(sheetName string, values map[string]int32) (map[string]bool, error) {
|
||
fc.excelCacheMutex.RLock()
|
||
defer fc.excelCacheMutex.RUnlock()
|
||
result := make(map[string]bool)
|
||
cache, ok := fc.excelCache[sheetName]
|
||
if !ok {
|
||
return nil, fmt.Errorf("Excel缓存未加载: %s", sheetName)
|
||
}
|
||
for col, target := range values {
|
||
if colMap, ok := cache[col]; ok {
|
||
result[col] = colMap[target]
|
||
} else {
|
||
result[col] = false
|
||
}
|
||
}
|
||
return result, nil
|
||
}
|