161 lines
4.0 KiB
Go
161 lines
4.0 KiB
Go
|
|
package main
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"encoding/csv"
|
|||
|
|
"fmt"
|
|||
|
|
"os"
|
|||
|
|
"strconv"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"gorm.io/driver/postgres"
|
|||
|
|
"gorm.io/gorm"
|
|||
|
|
"gorm.io/gorm/logger"
|
|||
|
|
"gorm.io/gorm/schema"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
func main() {
|
|||
|
|
// 连接数据库
|
|||
|
|
db, err := connectDB()
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Fprintf(os.Stderr, "连接数据库失败: %v\n", err)
|
|||
|
|
os.Exit(1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 读取 CSV 文件
|
|||
|
|
csvFile, err := os.Open("成本价.csv")
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Fprintf(os.Stderr, "打开 CSV 文件失败: %v\n", err)
|
|||
|
|
os.Exit(1)
|
|||
|
|
}
|
|||
|
|
defer csvFile.Close()
|
|||
|
|
|
|||
|
|
reader := csv.NewReader(csvFile)
|
|||
|
|
reader.LazyQuotes = true
|
|||
|
|
reader.TrimLeadingSpace = true
|
|||
|
|
|
|||
|
|
// 读取所有记录
|
|||
|
|
records, err := reader.ReadAll()
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Fprintf(os.Stderr, "读取 CSV 文件失败: %v\n", err)
|
|||
|
|
os.Exit(1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if len(records) < 2 {
|
|||
|
|
fmt.Fprintf(os.Stderr, "CSV 文件数据不足(需要至少包含表头和数据行)\n")
|
|||
|
|
os.Exit(1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
ctx := context.Background()
|
|||
|
|
successCount := 0
|
|||
|
|
failCount := 0
|
|||
|
|
skipCount := 0
|
|||
|
|
|
|||
|
|
fmt.Printf("开始更新成本价...\n")
|
|||
|
|
fmt.Printf("共 %d 条记录(包含表头)\n\n", len(records))
|
|||
|
|
|
|||
|
|
// 从第二行开始处理(跳过表头)
|
|||
|
|
for i := 1; i < len(records); i++ {
|
|||
|
|
record := records[i]
|
|||
|
|
if len(record) < 7 {
|
|||
|
|
fmt.Printf("第 %d 行数据列数不足,跳过\n", i+1)
|
|||
|
|
skipCount++
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
productCode := strings.TrimSpace(record[0])
|
|||
|
|
costPriceStr := strings.TrimSpace(record[6]) // 成本价在第7列(索引6)
|
|||
|
|
|
|||
|
|
// 跳过产品编号为空的行
|
|||
|
|
if productCode == "" {
|
|||
|
|
fmt.Printf("第 %d 行产品编号为空,跳过\n", i+1)
|
|||
|
|
skipCount++
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果成本价为空,跳过(不更新)
|
|||
|
|
if costPriceStr == "" {
|
|||
|
|
fmt.Printf("产品 %s: 成本价为空,跳过\n", productCode)
|
|||
|
|
skipCount++
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 解析成本价为浮点数
|
|||
|
|
costPrice, err := strconv.ParseFloat(costPriceStr, 64)
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Printf("产品 %s: 成本价格式错误 (%s),跳过: %v\n", productCode, costPriceStr, err)
|
|||
|
|
skipCount++
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新数据库
|
|||
|
|
result := db.WithContext(ctx).
|
|||
|
|
Table("product").
|
|||
|
|
Where("code = ? AND deleted_at IS NULL", productCode).
|
|||
|
|
Update("cost_price", costPrice)
|
|||
|
|
|
|||
|
|
if result.Error != nil {
|
|||
|
|
// 如果单数表名失败,尝试复数表名
|
|||
|
|
if strings.Contains(result.Error.Error(), "does not exist") {
|
|||
|
|
result = db.WithContext(ctx).
|
|||
|
|
Table("products").
|
|||
|
|
Where("code = ? AND deleted_at IS NULL", productCode).
|
|||
|
|
Update("cost_price", costPrice)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if result.Error != nil {
|
|||
|
|
fmt.Printf("产品 %s: 更新失败 - %v\n", productCode, result.Error)
|
|||
|
|
failCount++
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if result.RowsAffected == 0 {
|
|||
|
|
fmt.Printf("产品 %s: 未找到匹配的记录\n", productCode)
|
|||
|
|
failCount++
|
|||
|
|
} else {
|
|||
|
|
fmt.Printf("产品 %s: 成功更新成本价为 %.2f (影响 %d 行)\n", productCode, costPrice, result.RowsAffected)
|
|||
|
|
successCount++
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Printf("\n=== 更新完成 ===\n")
|
|||
|
|
fmt.Printf("成功更新: %d 条\n", successCount)
|
|||
|
|
fmt.Printf("更新失败: %d 条\n", failCount)
|
|||
|
|
fmt.Printf("跳过记录: %d 条\n", skipCount)
|
|||
|
|
fmt.Printf("总计处理: %d 条\n", len(records)-1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// connectDB 连接数据库
|
|||
|
|
func connectDB() (*gorm.DB, error) {
|
|||
|
|
// 数据库连接配置
|
|||
|
|
dsn := "host=1.117.67.95 user=tyapi_user password=Pg9mX4kL8nW2rT5y dbname=tyapi port=25010 sslmode=disable TimeZone=Asia/Shanghai"
|
|||
|
|
|
|||
|
|
// 配置GORM,使用单数表名(与项目配置一致)
|
|||
|
|
gormConfig := &gorm.Config{
|
|||
|
|
NamingStrategy: schema.NamingStrategy{
|
|||
|
|
SingularTable: true, // 使用单数表名
|
|||
|
|
},
|
|||
|
|
Logger: logger.Default.LogMode(logger.Info), // 显示 SQL 日志
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
db, err := gorm.Open(postgres.Open(dsn), gormConfig)
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, fmt.Errorf("连接数据库失败: %w", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试连接
|
|||
|
|
sqlDB, err := db.DB()
|
|||
|
|
if err != nil {
|
|||
|
|
return nil, fmt.Errorf("获取数据库实例失败: %w", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if err := sqlDB.Ping(); err != nil {
|
|||
|
|
return nil, fmt.Errorf("数据库连接测试失败: %w", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fmt.Println("数据库连接成功")
|
|||
|
|
return db, nil
|
|||
|
|
}
|
|||
|
|
|