更新
This commit is contained in:
+17
-48
@@ -1,68 +1,37 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"level_go/internal/api"
|
||||
"level_go/internal/router"
|
||||
"level_go/internal/service"
|
||||
"level_go/pkg/config"
|
||||
"level_go/pkg/config" // 引入配置包
|
||||
"log"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 1. 加载配置
|
||||
// 1. 初始化配置
|
||||
config.InitConfig()
|
||||
dbConf := config.AppConfig.Database
|
||||
|
||||
// 2. 连接数据库
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
|
||||
dbConf.User, dbConf.Password, dbConf.Host, dbConf.Port, dbConf.Dbname, dbConf.Charset)
|
||||
|
||||
// 2. 使用配置中的 DSN 连接数据库
|
||||
dsn := config.GetDSN()
|
||||
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
panic("数据库连接失败: " + err.Error())
|
||||
log.Fatal("数据库连接失败:", err)
|
||||
}
|
||||
|
||||
// 3. 注入 Service
|
||||
stockSvc := &service.StockService{DB: db}
|
||||
// 3. 依赖注入
|
||||
commonSrv := &service.CommonService{DB: db}
|
||||
stockSrv := &service.StockService{DB: db}
|
||||
|
||||
// 4. 路由设置
|
||||
r := gin.Default()
|
||||
commonHandler := &api.CommonHandler{Service: commonSrv}
|
||||
stockHandler := &api.StockHandler{Service: stockSrv}
|
||||
|
||||
// 跨域中间件
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
})
|
||||
// 4. 启动路由
|
||||
r := router.NewRouter(commonHandler, stockHandler)
|
||||
|
||||
api := r.Group("/api/v1")
|
||||
{
|
||||
api.GET("/stock/analyze", func(c *gin.Context) {
|
||||
start := c.Query("start_date")
|
||||
end := c.Query("end_date")
|
||||
|
||||
if start == "" || end == "" {
|
||||
c.JSON(400, gin.H{"error": "参数缺失"})
|
||||
return
|
||||
}
|
||||
|
||||
data, err := stockSvc.AnalyzePerformance(start, end)
|
||||
if err != nil {
|
||||
c.JSON(500, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(200, data)
|
||||
})
|
||||
}
|
||||
|
||||
// 5. 启动
|
||||
port := fmt.Sprintf(":%d", config.AppConfig.Server.Port)
|
||||
fmt.Printf("服务已启动,监听端口 %s\n", port)
|
||||
port := config.GetServerPort()
|
||||
log.Printf("服务启动于端口 %s", port)
|
||||
r.Run(port)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"level_go/internal/service"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type CommonHandler struct {
|
||||
Service *service.CommonService
|
||||
}
|
||||
|
||||
func (h *CommonHandler) GetTradeCalendar(c *gin.Context) {
|
||||
resp, err := h.Service.GetTradeCalendar()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"level_go/internal/model"
|
||||
"level_go/internal/service"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type StockHandler struct {
|
||||
Service *service.StockService
|
||||
}
|
||||
|
||||
func NewStockHandler(s *service.StockService) *StockHandler {
|
||||
return &StockHandler{Service: s}
|
||||
}
|
||||
|
||||
// GetKlineData 获取指定股票从2024-09-24至今的K线
|
||||
func (h *StockHandler) GetKlineData(c *gin.Context) {
|
||||
code := c.Param("code")
|
||||
if code == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "股票代码不能为空"})
|
||||
return
|
||||
}
|
||||
|
||||
data, err := h.Service.GetHistoryKline(code)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, data)
|
||||
}
|
||||
|
||||
func (h *StockHandler) Analyze(c *gin.Context) {
|
||||
var req model.AnalyzeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "参数错误"})
|
||||
return
|
||||
}
|
||||
|
||||
results, err := h.Service.AnalyzePerformance(req.StartDate, req.EndDate)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, results)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package model
|
||||
|
||||
import "time"
|
||||
|
||||
type TradeCalendar struct {
|
||||
CalDate time.Time `gorm:"column:cal_date;primaryKey"`
|
||||
Exchange string `gorm:"column:exchange;primaryKey"`
|
||||
IsOpen int `gorm:"column:is_open"`
|
||||
PretradeDate time.Time `gorm:"column:pretrade_date"`
|
||||
}
|
||||
|
||||
func (TradeCalendar) TableName() string { return "trade_calendar" }
|
||||
|
||||
type CalendarResponse struct {
|
||||
OpenDates []string `json:"open_dates"`
|
||||
MaxDate string `json:"max_date"`
|
||||
}
|
||||
+26
-16
@@ -2,25 +2,35 @@ package model
|
||||
|
||||
import "time"
|
||||
|
||||
// StockDaily 对应数据库中的 stock_daily 表
|
||||
type StockDaily struct {
|
||||
TsCode string `gorm:"column:ts_code"`
|
||||
TradeDate time.Time `gorm:"column:trade_date"`
|
||||
Open float64 `gorm:"column:open"`
|
||||
High float64 `gorm:"column:high"`
|
||||
Low float64 `gorm:"column:low"`
|
||||
Close float64 `gorm:"column:close"`
|
||||
TsCode string `gorm:"column:ts_code" json:"ts_code"`
|
||||
TradeDate time.Time `gorm:"column:trade_date" json:"trade_date"`
|
||||
Open float64 `gorm:"column:open" json:"open"`
|
||||
High float64 `gorm:"column:high" json:"high"`
|
||||
Low float64 `gorm:"column:low" json:"low"`
|
||||
Close float64 `gorm:"column:close" json:"close"`
|
||||
PctChg float64 `gorm:"column:pct_chg" json:"pct_chg"`
|
||||
}
|
||||
|
||||
func (StockDaily) TableName() string { return "stock_daily" }
|
||||
// AnalyzeRequest 用于接收前端 POST 请求的参数
|
||||
type AnalyzeRequest struct {
|
||||
StartDate string `json:"start_date"`
|
||||
EndDate string `json:"end_date"`
|
||||
}
|
||||
|
||||
// AnalysisResponse 用于向前端返回分析结果
|
||||
type AnalysisResponse struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
DayOpen float64 `json:"day_open"`
|
||||
DayHigh float64 `json:"day_high"`
|
||||
MaxHigh float64 `json:"max_high"`
|
||||
MaxDate string `json:"max_date"`
|
||||
PostMinLow float64 `json:"post_min_low"`
|
||||
DropPct *float64 `json:"drop_pct"` // 改为指针以支持 null
|
||||
ReboundPct *float64 `json:"rebound_pct"` // 改为指针以支持 null
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
DayOpen float64 `json:"day_open"`
|
||||
DayHigh float64 `json:"day_high"`
|
||||
MaxHigh float64 `json:"max_high"` // 逻辑最高点(用于计算降幅)
|
||||
MaxDate string `json:"max_date"` // 逻辑最高点日期
|
||||
OverallMaxHigh float64 `json:"overall_max_high"` // 区间绝对最高点
|
||||
OverallMaxDate string `json:"overall_max_date"` // 区间绝对最高点日期
|
||||
PostMinLow float64 `json:"post_min_low"` // 涨停日后的最低点
|
||||
DropPct *float64 `json:"drop_pct"` // 降幅 (跌破涨停价才有)
|
||||
ReboundPct *float64 `json:"rebound_pct"` // 涨幅/反弹
|
||||
ClosePrices []float64 `json:"close_prices"` // 用于前端画图的收盘价序列
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"level_go/internal/api"
|
||||
)
|
||||
|
||||
func NewRouter(commonApi *api.CommonHandler, stockApi *api.StockHandler) *gin.Engine {
|
||||
r := gin.Default()
|
||||
|
||||
// 跨域中间件 (根据需要开启)
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type")
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(204)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
})
|
||||
|
||||
v1 := r.Group("/api/v1")
|
||||
{
|
||||
v1.GET("/common/trade-calendar", commonApi.GetTradeCalendar)
|
||||
v1.POST("/stock/analyze", stockApi.Analyze)
|
||||
v1.GET("/stock/kline/:code", stockApi.GetKlineData)
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"level_go/internal/model"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type StockService struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func roundTo4(val float64) float64 {
|
||||
return math.Round(val*10000) / 10000
|
||||
}
|
||||
|
||||
func (s *StockService) AnalyzePerformance(startDate, endDate string) ([]model.AnalysisResponse, error) {
|
||||
var results []model.AnalysisResponse
|
||||
var limits []struct {
|
||||
TsCode string
|
||||
Name string
|
||||
Open float64
|
||||
High float64
|
||||
}
|
||||
|
||||
// 1. 获取选定日期当天涨停的股票
|
||||
query := `
|
||||
SELECT l.ts_code, l.name, d.open, d.high
|
||||
FROM stock_limit_data l
|
||||
LEFT JOIN stock_daily d ON l.ts_code = d.ts_code AND l.trade_date = d.trade_date
|
||||
WHERE l.trade_date = ? AND l.status = 1`
|
||||
|
||||
if err := s.DB.Raw(query, startDate).Scan(&limits).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, stock := range limits {
|
||||
var dailies []model.StockDaily
|
||||
s.DB.Where("ts_code = ? AND trade_date BETWEEN ? AND ?", stock.TsCode, startDate, endDate).
|
||||
Order("trade_date ASC").Find(&dailies)
|
||||
|
||||
if len(dailies) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// --- 步骤 1:寻找全区间的最高点 (MaxHigh) ---
|
||||
maxPrice := -1.0
|
||||
maxIdx := 0
|
||||
for i, d := range dailies {
|
||||
if d.High > maxPrice {
|
||||
maxPrice = d.High
|
||||
maxIdx = i
|
||||
}
|
||||
}
|
||||
|
||||
var dropPct, reboundPct *float64
|
||||
var postMinLow float64
|
||||
maxDateStr := dailies[maxIdx].TradeDate.Format("2006-01-02")
|
||||
|
||||
// --- 步骤 2:寻找最高点之后的最低点 (PostMinLow) ---
|
||||
if maxIdx < len(dailies)-1 {
|
||||
postMaxData := dailies[maxIdx+1:]
|
||||
minLow := postMaxData[0].Low
|
||||
minLowIdx := 0
|
||||
|
||||
for i, d := range postMaxData {
|
||||
if d.Low < minLow {
|
||||
minLow = d.Low
|
||||
minLowIdx = i
|
||||
}
|
||||
}
|
||||
postMinLow = minLow
|
||||
|
||||
// 只要出现比 MaxHigh 低的价格,就计算回落幅度
|
||||
if minLow < maxPrice {
|
||||
dVal := roundTo4(((maxPrice - minLow) / maxPrice) * 100)
|
||||
dropPct = &dVal
|
||||
|
||||
// --- 步骤 3:寻找最低点之后的反弹 ---
|
||||
if minLowIdx < len(postMaxData)-1 {
|
||||
lastClose := postMaxData[len(postMaxData)-1].Close
|
||||
if lastClose > minLow {
|
||||
rVal := roundTo4(((lastClose - minLow) / minLow) * 100)
|
||||
reboundPct = &rVal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- 步骤 4:处理特殊形态(如持续上涨无回落) ---
|
||||
if dropPct == nil {
|
||||
lastClose := dailies[len(dailies)-1].Close
|
||||
firstDayHigh := dailies[0].High
|
||||
if lastClose > firstDayHigh {
|
||||
rVal := roundTo4(((lastClose - firstDayHigh) / firstDayHigh) * 100)
|
||||
reboundPct = &rVal
|
||||
}
|
||||
}
|
||||
|
||||
results = append(results, model.AnalysisResponse{
|
||||
Code: stock.TsCode,
|
||||
Name: stock.Name,
|
||||
DayOpen: stock.Open,
|
||||
DayHigh: stock.High,
|
||||
MaxHigh: maxPrice,
|
||||
MaxDate: maxDateStr,
|
||||
PostMinLow: postMinLow,
|
||||
DropPct: dropPct,
|
||||
ReboundPct: reboundPct,
|
||||
})
|
||||
}
|
||||
|
||||
// 5. 基础排序(仅按回落幅度降序,不处理 null 特殊逻辑)
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
valI, valJ := -1.0, -1.0
|
||||
if results[i].DropPct != nil {
|
||||
valI = *results[i].DropPct
|
||||
}
|
||||
if results[j].DropPct != nil {
|
||||
valJ = *results[j].DropPct
|
||||
}
|
||||
return valI > valJ
|
||||
})
|
||||
|
||||
return results, nil
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"level_go/internal/model"
|
||||
)
|
||||
|
||||
type CommonService struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func (s *CommonService) GetTradeCalendar() (model.CalendarResponse, error) {
|
||||
var results []struct {
|
||||
DateStr string `gorm:"column:ds"`
|
||||
}
|
||||
var resp model.CalendarResponse
|
||||
|
||||
err := s.DB.Model(&model.TradeCalendar{}).
|
||||
Select("DATE_FORMAT(cal_date, '%Y-%m-%d') as ds").
|
||||
Where("is_open = ? AND exchange = ?", 1, "SSE").
|
||||
Order("cal_date ASC").Scan(&results).Error
|
||||
|
||||
if err != nil {
|
||||
return resp, err
|
||||
}
|
||||
|
||||
for _, r := range results {
|
||||
resp.OpenDates = append(resp.OpenDates, r.DateStr)
|
||||
resp.MaxDate = r.DateStr
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
"level_go/internal/model"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
type StockService struct {
|
||||
DB *gorm.DB
|
||||
}
|
||||
|
||||
func (s *StockService) GetHistoryKline(code string) ([]model.StockDaily, error) {
|
||||
var dailies []model.StockDaily
|
||||
startDate := "2024-09-24"
|
||||
|
||||
err := s.DB.Table("stock_daily").
|
||||
Where("ts_code = ? AND trade_date >= ?", code, startDate).
|
||||
Order("trade_date ASC").
|
||||
Find(&dailies).Error
|
||||
|
||||
return dailies, err
|
||||
}
|
||||
|
||||
func roundTo2(val float64) float64 {
|
||||
return math.Round(val*100) / 100
|
||||
}
|
||||
|
||||
func (s *StockService) AnalyzePerformance(startDate, endDate string) ([]model.AnalysisResponse, error) {
|
||||
var results []model.AnalysisResponse
|
||||
var limits []struct {
|
||||
TsCode string
|
||||
Name string
|
||||
Open float64
|
||||
High float64
|
||||
}
|
||||
|
||||
// 查询涨停股
|
||||
query := `SELECT l.ts_code, l.name, d.open, d.high FROM stock_limit_data l
|
||||
LEFT JOIN stock_daily d ON l.ts_code = d.ts_code AND l.trade_date = d.trade_date
|
||||
WHERE l.trade_date = ? AND l.status = 1`
|
||||
|
||||
if err := s.DB.Raw(query, startDate).Scan(&limits).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(limits) == 0 {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
codes := make([]string, 0, len(limits))
|
||||
for _, stock := range limits {
|
||||
codes = append(codes, stock.TsCode)
|
||||
}
|
||||
|
||||
var allDailies []model.StockDaily
|
||||
if err := s.DB.Table("stock_daily").
|
||||
Select("ts_code", "trade_date", "open", "high", "low", "close").
|
||||
Where("ts_code IN ? AND trade_date BETWEEN ? AND ?", codes, startDate, endDate).
|
||||
Order("ts_code ASC, trade_date ASC").
|
||||
Find(&allDailies).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dailiesByCode := make(map[string][]model.StockDaily, len(codes))
|
||||
for _, daily := range allDailies {
|
||||
dailiesByCode[daily.TsCode] = append(dailiesByCode[daily.TsCode], daily)
|
||||
}
|
||||
|
||||
for _, stock := range limits {
|
||||
dailies := dailiesByCode[stock.TsCode]
|
||||
if len(dailies) <= 1 {
|
||||
continue
|
||||
}
|
||||
|
||||
// A. 计算区间绝对最高价
|
||||
overallMax := dailies[0].High
|
||||
overallMaxDate := dailies[0].TradeDate.Format("2006-01-02")
|
||||
for _, d := range dailies {
|
||||
if d.High > overallMax {
|
||||
overallMax = d.High
|
||||
overallMaxDate = d.TradeDate.Format("2006-01-02")
|
||||
}
|
||||
}
|
||||
|
||||
// B. 逻辑计算
|
||||
limitHigh := stock.High
|
||||
postDailies := dailies[1:]
|
||||
|
||||
// 找出涨停后的全局最低
|
||||
minLowAfterLimit := postDailies[0].Low
|
||||
for _, d := range postDailies {
|
||||
if d.Low < minLowAfterLimit {
|
||||
minLowAfterLimit = d.Low
|
||||
}
|
||||
}
|
||||
|
||||
var dropPct, reboundPct *float64
|
||||
var logicMaxHigh float64
|
||||
var logicMaxDate string
|
||||
|
||||
if minLowAfterLimit >= limitHigh {
|
||||
// 形态 1:强势。计算涨幅
|
||||
currMax := limitHigh
|
||||
maxIdx := -1
|
||||
for i, d := range postDailies {
|
||||
if d.High > currMax {
|
||||
currMax = d.High
|
||||
maxIdx = i
|
||||
}
|
||||
}
|
||||
logicMaxHigh = currMax
|
||||
logicMaxDate = startDate
|
||||
if maxIdx != -1 {
|
||||
logicMaxDate = postDailies[maxIdx].TradeDate.Format("2006-01-02")
|
||||
val := roundTo2(((logicMaxHigh - limitHigh) / limitHigh) * 100)
|
||||
reboundPct = &val
|
||||
}
|
||||
dropPct = nil
|
||||
} else {
|
||||
// 形态 2:破位。计算降幅和反弹
|
||||
absMinLow := postDailies[0].Low
|
||||
minIdx := 0
|
||||
for i, d := range postDailies {
|
||||
if d.Low < absMinLow {
|
||||
absMinLow = d.Low
|
||||
minIdx = i
|
||||
}
|
||||
}
|
||||
// 找到涨停到该最低点之间的最高
|
||||
rangeDailies := dailies[:minIdx+2]
|
||||
rangeMax := dailies[0].High
|
||||
maxD := startDate
|
||||
for _, d := range rangeDailies {
|
||||
if d.High > rangeMax {
|
||||
rangeMax = d.High
|
||||
maxD = d.TradeDate.Format("2006-01-02")
|
||||
}
|
||||
}
|
||||
logicMaxHigh = rangeMax
|
||||
logicMaxDate = maxD
|
||||
|
||||
dVal := roundTo2(((rangeMax - absMinLow) / rangeMax) * 100)
|
||||
dropPct = &dVal
|
||||
|
||||
lastClose := postDailies[len(postDailies)-1].Close
|
||||
rVal := roundTo2(((lastClose - absMinLow) / absMinLow) * 100)
|
||||
reboundPct = &rVal
|
||||
}
|
||||
|
||||
var closePrices []float64
|
||||
for _, d := range dailies {
|
||||
closePrices = append(closePrices, d.Close)
|
||||
}
|
||||
|
||||
results = append(results, model.AnalysisResponse{
|
||||
Code: stock.TsCode,
|
||||
Name: stock.Name,
|
||||
DayOpen: stock.Open,
|
||||
DayHigh: stock.High,
|
||||
MaxHigh: logicMaxHigh,
|
||||
MaxDate: logicMaxDate,
|
||||
OverallMaxHigh: overallMax,
|
||||
OverallMaxDate: overallMaxDate,
|
||||
PostMinLow: minLowAfterLimit,
|
||||
DropPct: dropPct,
|
||||
ReboundPct: reboundPct,
|
||||
ClosePrices: closePrices,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(results, func(i, j int) bool {
|
||||
if results[i].DropPct == nil && results[j].DropPct != nil {
|
||||
return false
|
||||
}
|
||||
if results[i].DropPct != nil && results[j].DropPct == nil {
|
||||
return true
|
||||
}
|
||||
if results[i].DropPct != nil && results[j].DropPct != nil {
|
||||
return *results[i].DropPct > *results[j].DropPct
|
||||
}
|
||||
return false
|
||||
})
|
||||
return results, nil
|
||||
}
|
||||
+14
-1
@@ -24,7 +24,8 @@ var AppConfig *Config
|
||||
func InitConfig() {
|
||||
viper.SetConfigName("config")
|
||||
viper.SetConfigType("yaml")
|
||||
viper.AddConfigPath(".") // 在当前目录查找 config.yaml
|
||||
viper.AddConfigPath(".") // 在当前目录查找 config.yaml
|
||||
viper.AddConfigPath("./config") // 兼容多一层目录的情况
|
||||
|
||||
if err := viper.ReadInConfig(); err != nil {
|
||||
panic(fmt.Errorf("无法读取配置文件: %w", err))
|
||||
@@ -34,3 +35,15 @@ func InitConfig() {
|
||||
panic(fmt.Errorf("无法解析配置结构体: %w", err))
|
||||
}
|
||||
}
|
||||
|
||||
// GetDSN 生成 MySQL 连接字符串
|
||||
func GetDSN() string {
|
||||
db := AppConfig.Database
|
||||
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
|
||||
db.User, db.Password, db.Host, db.Port, db.Dbname, db.Charset)
|
||||
}
|
||||
|
||||
// GetServerPort 获取服务器端口字符串
|
||||
func GetServerPort() string {
|
||||
return fmt.Sprintf(":%d", AppConfig.Server.Port)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user