mh_goadmin_server/app/admin/models/file.go
chenlin c160f02a41 1.收付款方式设置调整,银行名称和账号改为非必填;
2.收付款方式默认配置的type为0,新增的type为1;
3.商品分类调整,如果父类被隐藏或显示,其子类也做对应修改;
4.新增商品资料入参调整,串码类型改为非必填;
5.新增商品资料调整,相同名称商品不允许新建;
6.零售订单审核优化,记录审核时间;
7.零售订单付款接口优化;
8.新增零售订单付款状态查询接口;
9.日志部分init时进行设置,避免test报错;
10.QueryTimeFormat格式调整,跟前端保持一致;
11.新增河马付相关密钥文件;
2023-12-21 17:36:13 +08:00

1003 lines
30 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package models
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/codinl/go-logger"
"github.com/xuri/excelize/v2"
"go-admin/app/admin/models/tools"
orm "go-admin/common/global"
"gorm.io/gorm"
"reflect"
"strconv"
"strings"
"time"
"unicode"
)
type CategoryExcel struct {
FirstCategory string `json:"first_category" binding:"required"` // 一级分类
SecondCategory string `json:"second_category"` // 二级分类
ThreeCategory string `json:"three_category"` // 三级分类
}
type CommodityExcel struct {
Category string `json:"category" binding:"required"` // 商品所属分类
Name string `json:"name" binding:"required"` // 商品名称
IMEIType string `json:"imei_type" binding:"required"` // 是否串码 1-无串码 2-串码(系统生成) 3-串码(手动添加)
SysGenerate string `json:"sys_generate" binding:"required"` // 系统生成串码
SupplierName string `json:"supplier_name" binding:"required"` // 供应商名称
SellBrokerage string `json:"sell_brokerage" binding:"required"` // 销售毛利提成
StaffBrokerage string `json:"staff_brokerage" binding:"required"` // 员工毛利提成
MemberDiscount string `json:"member_discount" binding:"required"` // 会员优惠
RetailPrice string `json:"retail_price" binding:"required"` // 指导零售价
MinRetailPrice string `json:"min_retail_price" binding:"required"` // 最低零售价
WholesalePrice string `json:"wholesale_price" binding:"required"` // 指导采购价
StaffCostPrice string `json:"staff_cost_price" binding:"required"` // 员工成本价加价
Origin string `json:"origin"` // 产地
Remark string `json:"remark"` // 备注
}
type StockExcel struct {
Name string `json:"name"` // 商品名称
SerialNum string `json:"serial_num"` // 商品编号
StoreName string `json:"store_name" binding:"required"` // 所属门店
SysGenerate string `json:"sys_generate"` // 系统生成串码
WholesalePrice string `json:"wholesale_price" binding:"required"` // 指导采购价
StaffCostPrice string `json:"staff_cost_price" binding:"required"` // 员工成本价=员工成本价加价+指导采购价
SupplierName string `json:"supplier_name" binding:"required"` // 供应商名称
StockTime string `json:"stock_time"` // 入库时间
Count string `json:"count" binding:"required"` // 数量
}
// 获取struct的tag标签匹配excel导入数据
func getJSONTagNames(s interface{}) []string {
valueType := reflect.TypeOf(s)
if valueType.Kind() != reflect.Struct {
fmt.Println("传入的不是结构体")
return nil
}
var tagNames []string
for i := 0; i < valueType.NumField(); i++ {
field := valueType.Field(i)
tag := field.Tag.Get("json")
if tag != "" {
tagNames = append(tagNames, tag)
}
}
return tagNames
}
// FileExcelReader 预览excel数据不做必填项校验
func FileExcelReader(d []byte, cols []string) ([]byte, []map[string]interface{}, error) {
reader, err := excelize.OpenReader(bytes.NewReader(d))
if err != nil {
return nil, nil, fmt.Errorf("open reader error: %v", err)
}
sheetList := reader.GetSheetList()
if len(sheetList) == 0 {
return nil, nil, errors.New("sheet list is empty")
}
rows, err := reader.Rows(sheetList[0])
if err != nil {
return nil, nil, fmt.Errorf("reader rows error: %v", err)
}
sheetCols, err := reader.GetCols(sheetList[0])
if err != nil {
return nil, nil, fmt.Errorf("reader get cols error: %v", err)
}
if len(sheetCols) == 0 {
return nil, nil, errors.New("get cols is empty")
}
var colsMap []map[string]interface{}
if len(cols) == 0 {
switch sheetList[0] {
case "导分类":
cols = getJSONTagNames(CategoryExcel{})
case "导商品":
cols = getJSONTagNames(CommodityExcel{})
case "导库存":
cols = getJSONTagNames(StockExcel{})
default:
cols = make([]string, len(sheetCols))
for i := range sheetCols {
cols[i] = fmt.Sprintf("c%02d", i)
}
}
} else if len(cols) != len(sheetCols) {
return nil, nil, errors.New("cols length does not match the number of columns")
}
for rows.Next() {
columns, err := rows.Columns()
if err != nil {
return nil, nil, fmt.Errorf("rows columns error: %v", err)
}
columnMap := make(map[string]interface{}, len(cols))
for i, col := range cols {
if i < len(columns) {
columnMap[col] = columns[i]
} else {
columnMap[col] = ""
}
}
logger.Info("columnMap:", columnMap)
colsMap = append(colsMap, columnMap)
}
logger.Info("colsMap:", colsMap)
mCols, err := json.Marshal(colsMap)
if err != nil {
return mCols, nil, fmt.Errorf("marshal error: %v", err)
}
return mCols, colsMap, nil
}
// FileExcelImport 导入excel数据校验必填项
func FileExcelImport(d []byte, cols []string, nType int) ([]byte, []map[string]interface{}, error) {
reader, err := excelize.OpenReader(bytes.NewReader(d))
if err != nil {
return nil, nil, fmt.Errorf("open reader error: %v", err)
}
sheetList := reader.GetSheetList()
if len(sheetList) == 0 {
return nil, nil, errors.New("sheet list is empty")
}
rowData, err := reader.GetRows(sheetList[0])
if err != nil {
return nil, nil, fmt.Errorf("reader rows error: %v", err)
}
rows, err := reader.Rows(sheetList[0])
if err != nil {
return nil, nil, fmt.Errorf("reader rows err: %v", err)
}
sheetCols, err := reader.GetCols(sheetList[0])
if err != nil {
return nil, nil, fmt.Errorf("reader get cols err: %v", err)
}
if len(sheetCols) == 0 {
return nil, nil, errors.New("get cols is empty")
}
sheetCols = checkExcelFormat(rowData, sheetCols)
var colsMap []map[string]interface{}
if len(cols) == 0 {
switch nType { // 导入类型1 商品分类 2 商品资料 3 商品库存
case 1:
if sheetList[0] != "导分类" {
return nil, nil, errors.New("格式错误不是分类模版excel")
}
if err := checkCategoryExcel(sheetCols); err != nil {
return nil, nil, err
}
cols = getJSONTagNames(CategoryExcel{})
case 2:
if sheetList[0] != "导商品" {
return nil, nil, errors.New("格式错误不是商品模版excel")
}
if err := checkCommodityExcel(sheetCols); err != nil {
return nil, nil, err
}
cols = getJSONTagNames(CommodityExcel{})
case 3:
if sheetList[0] != "导库存" {
return nil, nil, errors.New("格式错误不是库存模版excel")
}
if err := checkStockExcel(sheetCols); err != nil {
return nil, nil, err
}
cols = getJSONTagNames(StockExcel{})
default:
return nil, nil, errors.New("格式错误不是商品分类或资料模版excel")
}
} else if len(cols) != len(sheetCols) {
return nil, nil, errors.New("cols length does not match the number of columns")
}
nCounter := len(rowData) //记录有数据的行数,空行则跳过循环
for rows.Next() {
if nCounter == 0 {
break
}
nCounter--
columns, err := rows.Columns()
if err != nil {
return nil, nil, fmt.Errorf("rows columns err: %v", err)
}
columnMap := make(map[string]interface{}, len(cols))
for i, col := range cols {
if i < len(columns) {
columnMap[col] = columns[i]
} else {
columnMap[col] = ""
}
}
logger.Info("columnMap:", columnMap)
colsMap = append(colsMap, columnMap)
}
logger.Info("colsMap:", colsMap)
mCols, err := json.Marshal(colsMap)
if err != nil {
return mCols, nil, fmt.Errorf("marshal err: %v", err)
}
return mCols, colsMap, nil
}
// checkExcelFormat校验excel表格格式剔除最后1行的空格
func checkExcelFormat(rowData, sheetCols [][]string) [][]string {
if findMaxLength(sheetCols) > len(rowData) { // 最后1行有空格踢除
newSheetCols := make([][]string, len(sheetCols))
for i, col := range sheetCols {
if len(col) > len(rowData) {
newSheetCols[i] = col[:len(rowData)]
} else {
newSheetCols[i] = col
}
}
return newSheetCols
}
return sheetCols
}
// 校验商品分类导入规则
// 导入商品分类校验报错: 1只有3级没有2级或1级 2有23级但没有1级
func checkCategoryExcel(sheetCols [][]string) error {
if len(sheetCols) != 3 {
return errors.New("模版错误,请检查文件")
}
if len(sheetCols[0]) < len(sheetCols[1]) || len(sheetCols[0]) < len(sheetCols[2]) {
return errors.New("格式错误,缺少一级分类")
} else if len(sheetCols[1]) < len(sheetCols[2]) {
return errors.New("格式错误,缺少二级分类")
}
for i := 1; i < len(sheetCols[2]); i++ {
if sheetCols[0][i] == "" && (sheetCols[1][i] != "" || sheetCols[2][i] != "") {
return errors.New("格式错误,缺少一级分类")
} else if sheetCols[2][i] != "" && sheetCols[1][i] == "" {
return errors.New("格式错误,缺少二级分类")
}
}
if duplicateName, nFlag := hasDuplicateNames(sheetCols); nFlag {
return fmt.Errorf("分类名称不允许重复,请检查:[%v]", duplicateName)
}
return nil
}
// 校验名称是否相同有则false
func hasDuplicateNames(sheetCols [][]string) (string, bool) {
nameMap := make(map[string]struct{})
for _, col := range sheetCols {
for _, name := range col {
if _, exists := nameMap[name]; exists && name != "" {
// 有重复的名称
return name, true
}
nameMap[name] = struct{}{}
}
}
// 没有重复的名称
return "", false
}
// 校验商品资料导入规则
// 导入商品资料校验报错:必填项缺少数据(除了产地、备注,其他都是必填项)
func checkCommodityExcel(sheetCols [][]string) error {
if len(sheetCols) != 14 {
return errors.New("模版错误,请检查文件")
}
for i := 0; i < len(sheetCols)-2; i++ {
if len(sheetCols[i]) < len(sheetCols[i+1]) {
return errors.New("格式错误,有必填项未录入")
}
for _, v := range sheetCols[i] {
if v == "" {
return errors.New("格式错误,有必填项未录入")
}
}
}
return nil
}
// 校验库存导入规则
// 导入规则:
// (1)商品名称100 字符内,商品名称和商品编号可只填一项,同时填写时优先按照商品名称导库存;
// 且需为已有商品名称,否则红框展示,下方红字提示“该商品不存在,请新建商品”
// (2)商品编号:限纯数字,商品名称和商品编号可只填一项,同时填写时优先按照商品名称导库存;
// 且需为已有商品编号,否则红框展示,下方红字提示“该商品编号不存在”
// (3)所属门店必填项100 字符内;且需为已有门店,否则红框展示,下方红字提示“该门店不存在,请新建门店”
// (4)商品串码100 字符内,限制仅可填数字或英文;串码类必填,非串码不填
// 如果是串码类商品没填串码或者填了中文,红框展示,下方红字提示“串码类商品,请填写正确地串码”
// 如果是非串码商品,填了串码,红框展示,下方红字提示“非串码商品,无需填写串码”
// 备注库存导入时如果是串码商品都需填写串码才能导入不区分是否自动生成串码自动生成是在后续采购导入时使用2023/12/19跟产品核实
// (5)采购价必填项入库时的采购价0 数字
// (6)员工成本价必填项入库时的员工成本价0 数字
// (7)供应商必填项100 字符内,且需为已有供应商,否则红框展示,下方红字提示“该供应商不存在,请新建供应商”
// (8)入库时间非必填项含年月日填写后入库时间会按照填写的日期0点展示不填写则按照实际导入成功时间展示
// (9)数量必填项限制≥1正整数
// 串码类商品需拆分成每个数量为1单独导入
// 非串码可大于1直接导入 * 串码类商品如数量填写1红框展示下方红字提示“串码类商品数量只能为1”
// 10入库时间必须早于/小于当前时间
func checkStockExcel(sheetCols [][]string) error {
if len(sheetCols) != 9 {
return errors.New("模版错误,请检查文件")
}
maxLength := findMaxLength(sheetCols)
nLow, nMax := determineLowAndMax(sheetCols)
if nMax < maxLength {
return errors.New("第" + strconv.Itoa(nMax+1) + "行商品名称和商品编号不能同时为空")
}
// 判断是否有重复串码
if duplicateName, nFlag := hasDuplicateIMEI(sheetCols[3]); nFlag {
return fmt.Errorf("商品串码不允许重复,请检查:[%v]", duplicateName)
}
//先遍历第1列
for i := 1; i < nLow; i++ {
if sheetCols[0][i] == "" && sheetCols[1][i] == "" { // 商品名称和编号不能都为空
return errors.New("第" + strconv.Itoa(i+1) + "行商品名称和商品编号不能同时为空")
}
}
for i := 1; i < nMax; i++ { // todo
if i < len(sheetCols[0]) {
if !IsExistingProduct(sheetCols[0][i]) {
return errors.New("第" + strconv.Itoa(i+1) + "行商品不存在,请新建商品")
}
}
if i < len(sheetCols[1]) {
// 商品编号必须为纯数字
if sheetCols[1][i] != "" {
if _, err := strconv.Atoi(sheetCols[1][i]); err != nil {
return errors.New("第" + strconv.Itoa(i+1) + "行商品编号必须为纯数字")
}
}
if !isExistingProductCode(sheetCols[1][i]) {
return errors.New("第" + strconv.Itoa(i+1) + "行商品编号不存在")
}
}
// 所属门店不能为空
if i < len(sheetCols[2]) {
if sheetCols[2][i] == "" {
return errors.New("第" + strconv.Itoa(i+1) + "行所属门店不能为空")
}
if !isExistingStore(sheetCols[2][i]) {
return errors.New("第" + strconv.Itoa(i+1) + "行门店不存在,请新建门店")
}
}
// 商品串码规则校验 当串码行数跟其他一致时正常遍历,如果小于其他行,则最后一行不遍历 todo
// 如串码在商品库存表已经存在,则报错提示
//if len(sheetCols[3]) <= nLow && i+1 < nLow {
if i < len(sheetCols[3]) {
productName := ""
if i < len(sheetCols[0]) {
productName = sheetCols[0][i]
}
productCode := ""
if i < len(sheetCols[1]) {
productCode = sheetCols[1][i]
}
count := ""
if i < len(sheetCols[8]) {
count = sheetCols[8][i]
}
if err := checkSerialCode(productName, productCode, sheetCols[3][i], i); err != nil {
return err
}
// 串码类商品数量只能为1
if sheetCols[3][i] != "" && count != "1" {
return errors.New("第" + strconv.Itoa(i+1) + "行串码类商品数量只能为1")
}
}
// 采购价、员工成本价必须大于0
if i < len(sheetCols[4]) {
if purchasePrice, err := strconv.Atoi(sheetCols[4][i]); err != nil || purchasePrice <= 0 {
return errors.New("第" + strconv.Itoa(i+1) + "行采购价必须是大于0的数字")
}
}
if i < len(sheetCols[5]) {
if employeeCost, err := strconv.Atoi(sheetCols[5][i]); err != nil || employeeCost <= 0 {
return errors.New("第" + strconv.Itoa(i+1) + "行员工成本价必须是大于0的数字")
}
}
// 供应商不能为空
if i < len(sheetCols[6]) {
if sheetCols[6][i] == "" {
return errors.New("第" + strconv.Itoa(i+1) + "行供应商不能为空")
}
if !isExistingSupplier(sheetCols[6][i]) {
return errors.New("第" + strconv.Itoa(i+1) + "行供应商不存在,请新建供应商")
}
}
// 入库时间格式校验
//if len(sheetCols[7]) < nLow && i+1 < nLow {
if i < len(sheetCols[7]) {
if sheetCols[7][i] != "" {
parsedTime, err := time.Parse("2006/1/2", sheetCols[7][i])
if err != nil {
return errors.New("第" + strconv.Itoa(i+1) + "行入库时间格式错误应为YYYY-MM-DD")
}
// 格式化时间为指定格式
formattedTime := parsedTime.Format(DateTimeFormat)
//sheetCols[7][i] = formattedTime + "00-00-00"
// 需小于当前时间
nFlag, _ := isInputTimeBeforeOrEqualNow(formattedTime)
if !nFlag {
return errors.New("第" + strconv.Itoa(i+1) + "行入库时间不能晚于当前时间")
}
}
}
// 数量必须为正整数
if i < len(sheetCols[8]) {
if quantity, err := strconv.Atoi(sheetCols[8][i]); err != nil || quantity < 1 {
return errors.New("第" + strconv.Itoa(i+1) + "行数量必须是大于等于1的整数")
}
}
}
return nil
}
// 判断是否早于等于当前时间
func isInputTimeBeforeOrEqualNow(inputTimeString string) (bool, error) {
// 将输入时间字符串解析为 time.Time 类型
inputTime, _ := time.Parse(time.RFC3339, inputTimeString)
// 获取当前时间
currentTime := time.Now()
// 比较时间
isBeforeOrEqual := inputTime.After(currentTime)
return !isBeforeOrEqual, nil
}
// 校验串码是否相同有则false
func hasDuplicateIMEI(sheetCols []string) (string, bool) {
nameMap := make(map[string]struct{})
for _, name := range sheetCols {
if _, exists := nameMap[name]; exists && name != "" {
// 有重复的名称
return name, true
}
nameMap[name] = struct{}{}
}
// 没有重复的名称
return "", false
}
func findMaxLength(sheetCols [][]string) int {
maxLength := 0
for _, col := range sheetCols {
if len(col) > maxLength {
maxLength = len(col)
}
}
return maxLength
}
func determineLowAndMax(sheetCols [][]string) (int, int) {
nLow := len(sheetCols[0])
nMax := len(sheetCols[1])
if len(sheetCols[0]) >= len(sheetCols[1]) {
nLow = len(sheetCols[1])
nMax = len(sheetCols[0])
}
return nLow, nMax
}
// IsExistingProduct 查询商品资料中某个名称的商品是否存在
func IsExistingProduct(productName string) bool {
if productName == "" {
return true
}
// 实现商品名称是否存在的逻辑
var count int64
orm.Eloquent.Debug().Model(&ErpCommodity{}).
Where("name = ?", productName).
Count(&count)
return count > 0
}
func isExistingProductCode(productCode string) bool {
if productCode == "" {
return true
}
// 实现商品编号是否存在的逻辑
var count int64
orm.Eloquent.Debug().Model(&ErpCommodity{}).
Where("serial_number = ?", productCode).
Count(&count)
return count > 0
}
func isExistingStore(storeName string) bool {
// 实现门店是否存在的逻辑
var count int64
orm.Eloquent.Debug().Model(&Store{}).
Where("name = ?", storeName).
Count(&count)
return count > 0
}
func isExistingSupplier(supplierName string) bool {
// 实现供应商是否存在的逻辑
var count int64
orm.Eloquent.Debug().Model(&ErpSupplier{}).
Where("name = ?", supplierName).
Count(&count)
return count > 0
}
func checkSerialCode(productName, productCode, serialCode string, row int) error {
var existingCommodity ErpCommodity
var existingStockCommodity ErpStockCommodity
var queryCondition string
if productCode != "" {
queryCondition = "serial_number= ?"
} else if productName != "" {
queryCondition = "name = ?"
} else {
return errors.New("商品名称和商品编码不能都为空")
}
result := orm.Eloquent.Where(queryCondition, productName).Or(queryCondition, productCode).First(&existingCommodity)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
return errors.New("未查询到商品数据")
}
fmt.Println("Error:", result.Error)
return result.Error
}
nIMEIType := int(existingCommodity.IMEIType)
if nIMEIType == 1 && serialCode != "" {
return errors.New("非串码商品,无需填写串码")
}
//if nIMEIType == 2 && serialCode != ""{
// return errors.New("第" + strconv.Itoa(row+1) + "行串码无需填写,该商品串码系统自动生成")
//}
if (nIMEIType == 2 || nIMEIType == 1) && serialCode == "" {
return errors.New("第" + strconv.Itoa(row+1) + "行是串码类商品,请填写正确地串码")
}
if nIMEIType != 1 && !isNumericOrAlpha(serialCode) {
return errors.New("串码类商品,请填写正确的串码")
}
result = orm.Eloquent.Where("imei = ?", serialCode).First(&existingStockCommodity)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) { //没找到对应串码
return nil
}
fmt.Println("Error:", result.Error)
return result.Error
}
return errors.New("第" + strconv.Itoa(row+1) + "行串码重复,请勿重复导入")
}
func isNumericOrAlpha(s string) bool {
for _, char := range s {
if !unicode.IsDigit(char) && !unicode.IsLetter(char) {
return false
}
}
return true
}
// 将读取的excel数据转换成CategoryExcel struct
func transCategoryData(colsMap []map[string]interface{}) []CategoryExcel {
var categories []CategoryExcel
// 遍历 colsMap 进行类型断言和转换
for _, col := range colsMap {
var category CategoryExcel
for key, value := range col {
switch key {
case "first_category":
if name, ok := value.(string); ok {
category.FirstCategory = name
}
case "second_category":
if name, ok := value.(string); ok {
category.SecondCategory = name
}
case "three_category":
if name, ok := value.(string); ok {
category.ThreeCategory = name
}
}
}
// 将处理后的数据追加到 categories 中
categories = append(categories, category)
}
return categories
}
// ImportCategoryData 导入商品分类到数据库
// 规则分类只有3级所有分类不允许名称完全相同
func ImportCategoryData(colsMap []map[string]interface{}, businessId uint32) error {
data := transCategoryData(colsMap)
if duplicateName, nFlag := hasDuplicateInDb(data); nFlag {
return fmt.Errorf("数据库已有此分类名称,不允许重复,请检查:[%v]", duplicateName)
}
for _, item := range data {
var firstCategory, secondCategory, threeCategory Category
if item.FirstCategory == "" { // 一级分类名称为空则跳过,继续遍历
continue
}
// 插入或获取一级分类
err := orm.Eloquent.Debug().FirstOrCreate(&firstCategory,
Category{
Name: item.FirstCategory,
Pid: 0,
Display: 1,
CooperativeBusinessId: businessId,
}).Error
if err != nil {
return err
}
// 插入或获取二级分类
if item.SecondCategory != "" {
err = orm.Eloquent.Debug().FirstOrCreate(&secondCategory,
Category{
Name: item.SecondCategory,
Pid: firstCategory.ID,
Display: 1,
CooperativeBusinessId: businessId,
}).Error
if err != nil {
return err
}
}
// 插入三级分类
if item.ThreeCategory != "" {
err = orm.Eloquent.Debug().FirstOrCreate(&threeCategory,
Category{
Name: item.ThreeCategory,
Pid: secondCategory.ID,
Display: 1,
CooperativeBusinessId: businessId,
}).Error
if err != nil {
return err
}
}
}
return nil
}
// 判断是否存在重复数据
func hasDuplicateInDb(data []CategoryExcel) (string, bool) {
for _, item := range data {
if exists := isCategoryExists(item.FirstCategory); exists {
return item.FirstCategory, true
}
if exists := isCategoryExists(item.SecondCategory); exists {
return item.SecondCategory, true
}
if exists := isCategoryExists(item.ThreeCategory); exists {
return item.ThreeCategory, true
}
}
return "", false
}
// 判断分类是否存在
func isCategoryExists(categoryName string) bool {
var count int64
orm.Eloquent.Debug().Model(&Category{}).
Where("name = ?", categoryName).
Count(&count)
return count > 0
}
// ImportCommodityData 导入商品资料
func ImportCommodityData(colsMap []map[string]interface{}, businessId uint32) error {
data, err := transCommodityData(colsMap)
if err != nil {
return err
}
var erpCommodities []ErpCommodity
erpCommodities, err = convertToErpCommodities(data)
if err != nil {
return err
}
err = updateOrInsertCommodities(erpCommodities)
if err != nil {
return err
}
return nil
}
// 将读取的excel数据转换成CommodityExcel struct
func transCommodityData(colsMap []map[string]interface{}) ([]CommodityExcel, error) {
var commodities []CommodityExcel
// 遍历 colsMap 进行类型断言和转换
for _, col := range colsMap {
var commodity CommodityExcel
// 将 col 转换为 JSON 字符串
jsonData, err := json.Marshal(col)
if err != nil {
return nil, fmt.Errorf("failed to marshal data to JSON: %v", err)
}
// 将 JSON 字符串反序列化为 CommodityExcel
if err := json.Unmarshal(jsonData, &commodity); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON to CommodityExcel: %v", err)
}
// 将处理后的数据追加到 commodities 中
commodities = append(commodities, commodity)
}
return commodities, nil
}
// 将读取的excel数据转换成CommodityExcel struct
func transStockData(colsMap []map[string]interface{}) ([]StockExcel, error) {
var stockInfos []StockExcel
// 遍历 colsMap 进行类型断言和转换
for _, col := range colsMap {
var stockInfo StockExcel
// 将 col 转换为 JSON 字符串
jsonData, err := json.Marshal(col)
if err != nil {
return nil, fmt.Errorf("failed to marshal data to JSON: %v", err)
}
// 将 JSON 字符串反序列化为 CommodityExcel
if err := json.Unmarshal(jsonData, &stockInfo); err != nil {
return nil, fmt.Errorf("failed to unmarshal JSON to StockExcel: %v", err)
}
// 将处理后的数据追加到 commodities 中
stockInfos = append(stockInfos, stockInfo)
}
return stockInfos, nil
}
func convertToErpCommodities(data []CommodityExcel) ([]ErpCommodity, error) {
var erpCommodities []ErpCommodity
productCounter := tools.NewProductCounter()
for _, item := range data {
category, err := getCategoryByName(item.Category)
if err != nil {
return nil, err
}
list, err := GetSupplier(GetSupplierRequest{Name: item.SupplierName})
if len(list) == 0 || errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("[%v]该供应商不存在,请新建供应商", item.SupplierName)
}
serialNumber := fmt.Sprintf("%s%04d", category.Number,
getNumberOnCategory(category.ID)+int64(productCounter.GetNextProductNumber(category.Number)))
fmt.Println("商品编号:", serialNumber)
erpCommodity, err := convertToErpCommodity(item, category, list[0], serialNumber)
if err != nil {
return nil, err
}
erpCommodities = append(erpCommodities, erpCommodity)
}
return erpCommodities, nil
}
// 查询商品分类
func getCategoryByName(name string) (*Category, error) {
var category Category
err := orm.Eloquent.Debug().Model(&Category{}).
Where("name = ?", name).First(&category).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("[%v]该分类不存在,请先新建分类", name)
}
return &category, err
}
// 转换格式匹配Commodity的model
func convertToErpCommodity(item CommodityExcel, category *Category, supplier *Supplier, serialNumber string) (ErpCommodity, error) {
brokerage1Float, err := strconv.ParseFloat(strings.TrimRight(item.SellBrokerage, "%"), 64) // 销售毛利
if err != nil {
return ErpCommodity{}, fmt.Errorf("销售毛利转换有误:[%v]", err)
}
brokerage2Float, err := strconv.ParseFloat(strings.TrimRight(item.StaffBrokerage, "%"), 64) // 员工毛利提成
if err != nil {
return ErpCommodity{}, fmt.Errorf("员工毛利提成转换有误:[%v]", err)
}
memberDiscountFloat, err := strconv.ParseFloat(item.MemberDiscount, 64) // 会员优惠
if err != nil {
return ErpCommodity{}, fmt.Errorf("会员优惠转换有误:[%v]", err)
}
nRetailPrice, err := strconv.ParseUint(item.RetailPrice, 10, 32) // 指导零售价
if err != nil {
return ErpCommodity{}, fmt.Errorf("指导零售价转换有误:[%v]", err)
}
nMinRetailPrice, err := strconv.ParseUint(item.MinRetailPrice, 10, 32) // 最低零售价
if err != nil {
return ErpCommodity{}, fmt.Errorf("最低零售价转换有误:[%v]", err)
}
nStaffCostPrice, err := strconv.ParseUint(item.StaffCostPrice, 10, 32) // 员工成本价加价
if err != nil {
return ErpCommodity{}, fmt.Errorf("员工成本价加价转换有误:[%v]", err)
}
nWholesalePrice, err := strconv.ParseUint(item.WholesalePrice, 10, 32) // 指导采购价
if err != nil {
return ErpCommodity{}, fmt.Errorf("指导采购价转换有误:[%v]", err)
}
//将是否串码转换为数字 1-无串码 2-串码(系统生成) 3-串码(手动添加)
var nIMEIType uint32
switch item.IMEIType {
case "串码类":
if item.SysGenerate == "是" {
nIMEIType = 2
} else if item.SysGenerate == "否" {
nIMEIType = 3
} else {
return ErpCommodity{}, errors.New("[系统生成串码]列有非法字段")
}
case "非串码":
nIMEIType = 1
default:
return ErpCommodity{}, errors.New("[是否串码]列有非法字段")
}
//serialNumber := fmt.Sprintf("%s%04d", category.Number, getNumberOnCategory(category.ID)+1)
//fmt.Println("商品编号:", serialNumber)
return ErpCommodity{
SerialNumber: serialNumber,
Name: item.Name,
ErpCategoryId: category.ID,
ErpCategoryName: item.Category,
IMEIType: nIMEIType,
ErpSupplierId: supplier.ID,
ErpSupplierName: item.SupplierName,
RetailPrice: uint32(nRetailPrice),
MinRetailPrice: uint32(nMinRetailPrice),
StaffCostPrice: uint32(nStaffCostPrice),
WholesalePrice: uint32(nWholesalePrice),
Brokerage1: brokerage1Float,
Brokerage2: brokerage2Float,
MemberDiscount: memberDiscountFloat,
Origin: item.Origin,
Remark: item.Remark,
}, nil
}
// 操作数据库,通过商品名称查找,没有则插入,有则更新
func updateOrInsertCommodities(erpCommodities []ErpCommodity) error {
for _, erpCommodity := range erpCommodities {
var existingCommodity ErpCommodity
result := orm.Eloquent.Where("name = ?", erpCommodity.Name).First(&existingCommodity)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
orm.Eloquent.Create(&erpCommodity)
fmt.Println("Inserted:", erpCommodity.Name)
} else {
fmt.Println("Error:", result.Error)
return result.Error
}
} else {
orm.Eloquent.Model(&existingCommodity).Updates(&erpCommodity)
fmt.Println("Updated:", erpCommodity.Name)
}
}
return nil
}
// 查询商品列表中同一类商品分类下商品的个数
func getNumberOnCategory(categoryId uint32) int64 {
var count int64
orm.Eloquent.Debug().Model(&ErpCommodity{}).
Where("erp_category_id = ?", categoryId).
Count(&count)
return count
}
// GenerateSerialNumber 生成商品编号
func GenerateSerialNumber(categoryId uint32) (string, error) {
var category Category
err := orm.Eloquent.Debug().Model(&Category{}).
Where("id = ?", categoryId).First(&category).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return "", errors.New("未查到商品分类")
}
var count int64
orm.Eloquent.Debug().Model(&ErpCommodity{}).
Where("erp_category_id = ?", category.ID).
Count(&count)
serialNumber := fmt.Sprintf("%s%04d", category.Number, count+1)
fmt.Println("商品编号:", serialNumber)
return serialNumber, nil
}