141 lines
3.5 KiB
Go
141 lines
3.5 KiB
Go
package service
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/xuri/excelize/v2"
|
||
"gorm.io/gorm"
|
||
|
||
"psi/database"
|
||
"psi/models"
|
||
"psi/models/request"
|
||
)
|
||
|
||
// LocationImportService 库位导入服务
|
||
type LocationImportService struct{}
|
||
|
||
// LOCATION_IMPORT_COLUMNS 库位导入表格列(仅1列)
|
||
var LOCATION_IMPORT_COLUMNS = []string{"库位编码"}
|
||
|
||
// LocationImportResult 导入结果
|
||
type LocationImportResult struct {
|
||
SuccessCount int
|
||
FailCount int
|
||
FailList []string
|
||
Message string
|
||
}
|
||
|
||
// AddFail 添加失败记录
|
||
func (r *LocationImportResult) AddFail(reason string) {
|
||
r.FailCount++
|
||
r.FailList = append(r.FailList, reason)
|
||
}
|
||
|
||
// ParseLocationImportExcel 解析Excel文件为库位行数据
|
||
func (s *LocationImportService) ParseLocationImportExcel(fileBytes []byte) ([]request.LocationImportRow, error) {
|
||
f, err := excelize.OpenReader(strings.NewReader(string(fileBytes)))
|
||
if err != nil {
|
||
return nil, fmt.Errorf("无法打开Excel文件: %v", err)
|
||
}
|
||
defer f.Close()
|
||
|
||
rows, err := f.GetRows(f.GetSheetName(0))
|
||
if err != nil {
|
||
return nil, fmt.Errorf("读取Sheet失败: %v", err)
|
||
}
|
||
|
||
if len(rows) < 2 {
|
||
return nil, fmt.Errorf("Excel至少需要表头+1行数据")
|
||
}
|
||
|
||
// 校验表头
|
||
header := rows[0]
|
||
if len(header) < len(LOCATION_IMPORT_COLUMNS) {
|
||
return nil, fmt.Errorf("表头列数不足,期望%d列(库位编码),实际%d列", len(LOCATION_IMPORT_COLUMNS), len(header))
|
||
}
|
||
|
||
var result []request.LocationImportRow
|
||
for i := 1; i < len(rows); i++ {
|
||
row := rows[i]
|
||
code := strings.TrimSpace(getCell(row, 0))
|
||
if code == "" {
|
||
continue
|
||
}
|
||
result = append(result, request.LocationImportRow{Code: code})
|
||
}
|
||
|
||
return result, nil
|
||
}
|
||
|
||
// getCell 安全获取单元格值
|
||
func getCell(row []string, idx int) string {
|
||
if idx < len(row) {
|
||
return row[idx]
|
||
}
|
||
return ""
|
||
}
|
||
|
||
// ImportLocationsFromExcel 从Excel导入库位(事务内批量创建)
|
||
func (s *LocationImportService) ImportLocationsFromExcel(userID, warehouseID int64, rows []request.LocationImportRow) (*LocationImportResult, error) {
|
||
databaseConn, err := database.GetTenantDB(userID)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("获取数据库连接失败: %v", err)
|
||
}
|
||
|
||
result := &LocationImportResult{}
|
||
|
||
err = databaseConn.Transaction(func(tx *gorm.DB) error {
|
||
now := time.Now().Unix()
|
||
|
||
for _, row := range rows {
|
||
code := strings.TrimSpace(row.Code)
|
||
if code == "" {
|
||
result.AddFail("库位编码为空")
|
||
continue
|
||
}
|
||
|
||
// 检查同仓库下是否已存在
|
||
var existing models.Location
|
||
if err := tx.Where("warehouse_id = ? AND code = ? AND is_del = ?", warehouseID, code, 0).First(&existing).Error; err == nil {
|
||
result.AddFail(fmt.Sprintf("%s: 库位已存在", code))
|
||
continue
|
||
}
|
||
|
||
loc := models.Location{
|
||
WarehouseID: warehouseID,
|
||
Code: code,
|
||
Type: 1, // 存储库位
|
||
Capacity: 255,
|
||
Sort: 0,
|
||
Status: 1, // 启用
|
||
CreatedAt: now,
|
||
UpdatedAt: now,
|
||
IsDel: 0,
|
||
}
|
||
if err := tx.Create(&loc).Error; err != nil {
|
||
result.AddFail(fmt.Sprintf("%s: 创建失败: %v", code, err))
|
||
continue
|
||
}
|
||
|
||
result.SuccessCount++
|
||
}
|
||
|
||
return nil
|
||
})
|
||
|
||
if err != nil {
|
||
return nil, fmt.Errorf("导入库位事务失败: %v", err)
|
||
}
|
||
|
||
if result.FailCount > 0 {
|
||
result.Message = fmt.Sprintf("成功 %d 条,失败 %d 条。失败明细: %s",
|
||
result.SuccessCount, result.FailCount, strings.Join(result.FailList, "; "))
|
||
} else {
|
||
result.Message = fmt.Sprintf("全部成功,共 %d 条", result.SuccessCount)
|
||
}
|
||
|
||
return result, nil
|
||
}
|