Files
stock/backend/internal/handlers/data_sync.go
T

138 lines
3.5 KiB
Go

package handlers
import (
"net/http"
"strings"
"stock-user-system/internal/datasource"
"stock-user-system/internal/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)
var validExchanges = map[string]string{
"SSE": "上交所",
"SZSE": "深交所",
"BSE": "北交所",
}
func toModelStock(s datasource.StockBasic) models.Stock {
return models.Stock{
TsCode: s.TsCode,
Symbol: s.Symbol,
Name: s.Name,
Area: s.Area,
Industry: s.Industry,
Fullname: s.Fullname,
Enname: s.Enname,
Cnspell: s.Cnspell,
Market: s.Market,
Exchange: s.Exchange,
CurrType: s.CurrType,
ListStatus: s.ListStatus,
ListDate: s.ListDate,
DelistDate: s.DelistDate,
IsHs: s.IsHs,
ActName: s.ActName,
ActEntType: s.ActEntType,
}
}
// DataSyncHandler 处理数据同步相关接口。
type DataSyncHandler struct {
DB *gorm.DB
client *datasource.Client
}
// NewDataSyncHandler 创建数据同步处理器。
func NewDataSyncHandler(db *gorm.DB) *DataSyncHandler {
return &DataSyncHandler{
DB: db,
client: datasource.NewClient(),
}
}
type initStocksResponse struct {
Count int `json:"count"`
Message string `json:"message"`
}
// InitStocks 从 Tushare 拉取全部上市股票列表并写入数据库。
func (h *DataSyncHandler) InitStocks(c *gin.Context) {
stocks, err := h.client.ListStocks()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
return
}
records := make([]models.Stock, 0, len(stocks))
for _, s := range stocks {
records = append(records, toModelStock(s))
}
if err := h.DB.Exec("TRUNCATE TABLE stocks RESTART IDENTITY").Error; err != nil {
if !strings.Contains(err.Error(), "does not exist") {
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "清空旧数据失败: " + err.Error()})
return
}
}
if err := h.DB.CreateInBatches(records, 500).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "写入股票列表失败: " + err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": initStocksResponse{
Count: len(records),
Message: "股票列表初始化完成",
},
})
}
type syncStocksByExchangeResponse struct {
Exchange string `json:"exchange"`
Count int `json:"count"`
Message string `json:"message"`
}
// SyncStocksByExchange 按交易所从 Tushare 同步股票基础信息。
func (h *DataSyncHandler) SyncStocksByExchange(c *gin.Context) {
exchange := strings.ToUpper(strings.TrimSpace(c.Param("exchange")))
if _, ok := validExchanges[exchange]; !ok {
c.JSON(http.StatusBadRequest, gin.H{"success": false, "error": "无效的交易所代码,支持 SSE/SZSE/BSE"})
return
}
stocks, err := h.client.ListStocksByExchange(exchange)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": err.Error()})
return
}
records := make([]models.Stock, 0, len(stocks))
for _, s := range stocks {
records = append(records, toModelStock(s))
}
if err := h.DB.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "ts_code"}},
UpdateAll: true,
}).CreateInBatches(records, 500).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"success": false, "error": "写入股票列表失败: " + err.Error()})
return
}
c.JSON(http.StatusOK, gin.H{
"success": true,
"data": syncStocksByExchangeResponse{
Exchange: exchange,
Count: len(records),
Message: validExchanges[exchange] + "股票同步完成",
},
})
}