From 43fd114804c3782728c075e18b3dac51c663d7c6 Mon Sep 17 00:00:00 2001 From: vipg Date: Fri, 14 Nov 2025 17:45:21 +0800 Subject: [PATCH] add --- .../futures_trade_record/src/logic/variety.go | 357 +++++++++++++++++- .../src/service/variety.go | 1 + 2 files changed, 357 insertions(+), 1 deletion(-) create mode 100644 backend/futures_trade_record/src/service/variety.go diff --git a/backend/futures_trade_record/src/logic/variety.go b/backend/futures_trade_record/src/logic/variety.go index 697a16f..f04d9fb 100644 --- a/backend/futures_trade_record/src/logic/variety.go +++ b/backend/futures_trade_record/src/logic/variety.go @@ -1 +1,356 @@ -package logic \ No newline at end of file +package logic + +import ( + "database/sql" + "errors" + "fmt" + "time" + + "github.com/google/uuid" + "go.uber.org/zap" +) + +// Variety 主表结构体 +type Variety struct { + ID uuid.UUID + Deleted bool + CreatedAt time.Time + UpdatedAt time.Time +} + +// VarietyName 子表结构体(名称) +type VarietyName struct { + ID uuid.UUID + VarietyID uuid.UUID + Name string + Deleted bool + CreatedAt time.Time + UpdatedAt time.Time +} + +// VarietyCode 子表结构体(代码) +type VarietyCode struct { + ID uuid.UUID + VarietyID uuid.UUID + Code string + Deleted bool + CreatedAt time.Time + UpdatedAt time.Time +} + +// VarietyTick 子表结构体(跳点) +type VarietyTick struct { + ID uuid.UUID + VarietyID uuid.UUID + Tick float64 + Deleted bool + CreatedAt time.Time + UpdatedAt time.Time +} + +// VarietyTickPrice 子表结构体(跳点价格) +type VarietyTickPrice struct { + ID uuid.UUID + VarietyID uuid.UUID + Price float64 + Deleted bool + CreatedAt time.Time + UpdatedAt time.Time +} + +// VarietyInfo 联合查询视图结构体 +type VarietyInfo struct { + VarietyID uuid.UUID + Name string + Code string + Tick float64 + TickPrice float64 +} + +// CreateVariety 创建品种主记录及关联信息 +func CreateVariety(name, code string, tick, tickPrice float64) (uuid.UUID, error) { + tx, err := DB.Begin() + if err != nil { + zap.L().Error("创建事务失败", zap.Error(err)) + return uuid.Nil, err + } + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // 1. 创建主表记录 + var varietyID uuid.UUID + err = tx.QueryRow(` + INSERT INTO variety (deleted) + VALUES ($1) + RETURNING id + `, false).Scan(&varietyID) + if err != nil { + tx.Rollback() + zap.L().Error("创建品种主表失败", zap.Error(err)) + return uuid.Nil, err + } + + // 2. 创建名称记录 + _, err = tx.Exec(` + INSERT INTO variety_name (variety_id, name, deleted) + VALUES ($1, $2, $3) + `, varietyID, name, false) + if err != nil { + tx.Rollback() + zap.L().Error("创建品种名称记录失败", zap.Error(err)) + return uuid.Nil, err + } + + // 3. 创建代码记录 + _, err = tx.Exec(` + INSERT INTO variety_code (variety_id, code, deleted) + VALUES ($1, $2, $3) + `, varietyID, code, false) + if err != nil { + tx.Rollback() + zap.L().Error("创建品种代码记录失败", zap.Error(err)) + return uuid.Nil, err + } + + // 4. 创建跳点记录 + _, err = tx.Exec(` + INSERT INTO variety_tick (variety_id, tick, deleted) + VALUES ($1, $2, $3) + `, varietyID, tick, false) + if err != nil { + tx.Rollback() + zap.L().Error("创建跳点记录失败", zap.Error(err)) + return uuid.Nil, err + } + + // 5. 创建跳点价格记录 + _, err = tx.Exec(` + INSERT INTO variety_tick_price (variety_id, price, deleted) + VALUES ($1, $2, $3) + `, varietyID, tickPrice, false) + if err != nil { + tx.Rollback() + zap.L().Error("创建跳点价格记录失败", zap.Error(err)) + return uuid.Nil, err + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + zap.L().Error("提交事务失败", zap.Error(err)) + return uuid.Nil, err + } + + return varietyID, nil +} + +// GetVarietyByID 查询品种详情(通过ID) +func GetVarietyByID(id uuid.UUID) (*VarietyInfo, error) { + var info VarietyInfo + err := DB.QueryRow(` + SELECT + variety_id, name, code, tick, tick_price + FROM variety_info_view + WHERE variety_id = $1 + `, id).Scan( + &info.VarietyID, + &info.Name, + &info.Code, + &info.Tick, + &info.TickPrice, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, fmt.Errorf("品种不存在: %s", id) + } + zap.L().Error("查询品种失败", zap.String("id", id.String()), zap.Error(err)) + return nil, err + } + return &info, nil +} + +// GetVarietyByName 按名称查询品种 +func GetVarietyByName(name string) (*VarietyInfo, error) { + var info VarietyInfo + err := DB.QueryRow(` + SELECT + variety_id, name, code, tick, tick_price + FROM variety_info_view + WHERE name = $1 AND name IS NOT NULL + `, name).Scan( + &info.VarietyID, + &info.Name, + &info.Code, + &info.Tick, + &info.TickPrice, + ) + if err != nil { + if errors.Is(err, sql.ErrNoRows) { + return nil, fmt.Errorf("未找到名称为 %s 的品种", name) + } + zap.L().Error("按名称查询品种失败", zap.String("name", name), zap.Error(err)) + return nil, err + } + return &info, nil +} + +// UpdateVarietyName 更新品种名称 +func UpdateVarietyName(varietyID uuid.UUID, newName string) error { + result, err := DB.Exec(` + UPDATE variety_name + SET name = $1 + WHERE variety_id = $2 AND deleted = false + `, newName, varietyID) + if err != nil { + zap.L().Error("更新品种名称失败", zap.String("id", varietyID.String()), zap.Error(err)) + return err + } + + rowsAffected, err := result.RowsAffected() + if err != nil { + return err + } + if rowsAffected == 0 { + return fmt.Errorf("未找到可更新的品种名称记录: %s", varietyID) + } + return nil +} + +// UpdateVarietyTick 更新跳点值 +func UpdateVarietyTick(varietyID uuid.UUID, newTick float64) error { + result, err := DB.Exec(` + UPDATE variety_tick + SET tick = $1 + WHERE variety_id = $2 AND deleted = false + `, newTick, varietyID) + if err != nil { + zap.L().Error("更新跳点值失败", zap.String("id", varietyID.String()), zap.Error(err)) + return err + } + + rowsAffected, err := result.RowsAffected() + if err != nil { + return err + } + if rowsAffected == 0 { + return fmt.Errorf("未找到可更新的跳点记录: %s", varietyID) + } + return nil +} + +// DeleteVariety 逻辑删除品种(软删除) +func DeleteVariety(varietyID uuid.UUID) error { + tx, err := DB.Begin() + if err != nil { + zap.L().Error("创建事务失败", zap.Error(err)) + return err + } + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + + // 1. 软删除主表 + _, err = tx.Exec(` + UPDATE variety + SET deleted = true + WHERE id = $1 + `, varietyID) + if err != nil { + tx.Rollback() + zap.L().Error("删除品种主表失败", zap.String("id", varietyID.String()), zap.Error(err)) + return err + } + + // 2. 软删除关联子表(实际可依赖外键级联删除,这里显式处理确保一致性) + tables := []string{ + "variety_name", + "variety_code", + "variety_tick", + "variety_tick_price", + } + for _, table := range tables { + _, err = tx.Exec(fmt.Sprintf(` + UPDATE %s + SET deleted = true + WHERE variety_id = $1 + `, table), varietyID) + if err != nil { + tx.Rollback() + zap.L().Error("删除子表记录失败", + zap.String("table", table), + zap.String("id", varietyID.String()), + zap.Error(err)) + return err + } + } + + if err := tx.Commit(); err != nil { + tx.Rollback() + zap.L().Error("提交删除事务失败", zap.Error(err)) + return err + } + return nil +} + +// ListVarieties 分页查询品种列表 +func ListVarieties(page, pageSize int) ([]VarietyInfo, int, error) { + if page < 1 { + page = 1 + } + offset := (page - 1) * pageSize + + // 查询总数 + var total int + err := DB.QueryRow(` + SELECT COUNT(DISTINCT variety_id) + FROM variety_info_view + WHERE variety_id IS NOT NULL + `).Scan(&total) + if err != nil { + zap.L().Error("查询品种总数失败", zap.Error(err)) + return nil, 0, err + } + + // 查询分页数据 + rows, err := DB.Query(` + SELECT + variety_id, name, code, tick, tick_price + FROM variety_info_view + WHERE variety_id IS NOT NULL + ORDER BY variety_id DESC + LIMIT $1 OFFSET $2 + `, pageSize, offset) + if err != nil { + zap.L().Error("查询品种列表失败", zap.Error(err)) + return nil, 0, err + } + defer rows.Close() + + var varieties []VarietyInfo + for rows.Next() { + var info VarietyInfo + err := rows.Scan( + &info.VarietyID, + &info.Name, + &info.Code, + &info.Tick, + &info.TickPrice, + ) + if err != nil { + zap.L().Error("解析品种记录失败", zap.Error(err)) + return nil, 0, err + } + varieties = append(varieties, info) + } + + if err = rows.Err(); err != nil { + zap.L().Error("遍历品种记录失败", zap.Error(err)) + return nil, 0, err + } + + return varieties, total, nil +} \ No newline at end of file diff --git a/backend/futures_trade_record/src/service/variety.go b/backend/futures_trade_record/src/service/variety.go new file mode 100644 index 0000000..2e5a6c3 --- /dev/null +++ b/backend/futures_trade_record/src/service/variety.go @@ -0,0 +1 @@ +package service \ No newline at end of file