feat: 修复报错

This commit is contained in:
Daniel
2026-03-26 14:13:44 +08:00
commit b2223ec058
31 changed files with 17401 additions and 0 deletions

20
go-service/Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
ARG DOCKER_MIRROR_PREFIX=m.daocloud.io/docker.io/library
FROM ${DOCKER_MIRROR_PREFIX}/golang:1.22-bookworm
ENV GOPROXY=https://goproxy.cn,direct \
GOSUMDB=sum.golang.google.cn \
CGO_ENABLED=0
RUN sed -i 's|deb.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources \
&& sed -i 's|security.debian.org|mirrors.aliyun.com|g' /etc/apt/sources.list.d/debian.sources
WORKDIR /app
COPY go.mod ./
RUN go mod tidy || true
COPY . .
RUN go mod tidy
RUN go build -o /bin/go-service main.go
CMD ["/bin/go-service"]

3
go-service/go.mod Normal file
View File

@@ -0,0 +1,3 @@
module ai-trading/go-service
go 1.22

171
go-service/main.go Normal file
View File

@@ -0,0 +1,171 @@
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
"time"
binance "github.com/adshao/go-binance/v2"
"github.com/redis/go-redis/v9"
)
type KlineMessage struct {
Type string `json:"type"`
Source string `json:"source"`
Symbol string `json:"symbol"`
Interval string `json:"interval"`
EventTime int64 `json:"event_time"`
OpenTime int64 `json:"open_time"`
CloseTime int64 `json:"close_time"`
Open string `json:"open"`
High string `json:"high"`
Low string `json:"low"`
Close string `json:"close"`
Volume string `json:"volume"`
TradeNum int64 `json:"trade_num"`
Final bool `json:"final"`
}
type RedisPublisher interface {
Publish(ctx context.Context, channel string, payload []byte) error
Close() error
}
type NoopPublisher struct{}
func (p *NoopPublisher) Publish(_ context.Context, _ string, _ []byte) error {
return nil
}
func (p *NoopPublisher) Close() error {
return nil
}
type RedisChannelPublisher struct {
client *redis.Client
}
func (p *RedisChannelPublisher) Publish(ctx context.Context, channel string, payload []byte) error {
return p.client.Publish(ctx, channel, payload).Err()
}
func (p *RedisChannelPublisher) Close() error {
return p.client.Close()
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
symbol := getEnv("BINANCE_SYMBOL", "btcusdt")
interval := getEnv("BINANCE_INTERVAL", "1m")
channel := getEnv("REDIS_CHANNEL", "kline.stream")
publisher, err := initPublisherFromEnv(ctx)
if err != nil {
log.Fatalf("init publisher failed: %v", err)
}
defer func() {
if err := publisher.Close(); err != nil {
log.Printf("publisher close failed: %v", err)
}
}()
doneC, stopC, err := binance.WsKlineServe(symbol, interval,
func(event *binance.WsKlineEvent) {
msg := KlineMessage{
Type: "kline",
Source: "binance_ws",
Symbol: strings.ToLower(event.Symbol),
Interval: event.Kline.Interval,
EventTime: event.Time,
OpenTime: event.Kline.StartTime,
CloseTime: event.Kline.EndTime,
Open: event.Kline.Open,
High: event.Kline.High,
Low: event.Kline.Low,
Close: event.Kline.Close,
Volume: event.Kline.Volume,
TradeNum: int64(event.Kline.TradeNum),
Final: event.Kline.IsFinal,
}
payload, err := json.Marshal(msg)
if err != nil {
log.Printf("marshal failed: %v", err)
return
}
fmt.Println(string(payload))
if err := publisher.Publish(ctx, channel, payload); err != nil {
log.Printf("redis publish failed: %v", err)
}
},
func(err error) {
log.Printf("ws error: %v", err)
},
)
if err != nil {
log.Fatalf("start ws stream failed: %v", err)
}
log.Printf("subscribed binance kline stream: symbol=%s interval=%s channel=%s", symbol, interval, channel)
sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
select {
case <-sig:
log.Println("received shutdown signal")
close(stopC)
cancel()
case <-doneC:
log.Println("ws stream closed")
cancel()
}
}
func initPublisherFromEnv(ctx context.Context) (RedisPublisher, error) {
addr := strings.TrimSpace(os.Getenv("REDIS_ADDR"))
if addr == "" {
log.Println("REDIS_ADDR not configured, using NoopPublisher")
return &NoopPublisher{}, nil
}
db, err := strconv.Atoi(getEnv("REDIS_DB", "0"))
if err != nil {
return nil, fmt.Errorf("invalid REDIS_DB: %w", err)
}
client := redis.NewClient(&redis.Options{
Addr: addr,
Password: os.Getenv("REDIS_PASSWORD"),
DB: db,
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
})
if err := client.Ping(ctx).Err(); err != nil {
return nil, fmt.Errorf("redis ping failed: %w", err)
}
log.Printf("connected redis at %s db=%d", addr, db)
return &RedisChannelPublisher{client: client}, nil
}
func getEnv(key, fallback string) string {
v := strings.TrimSpace(os.Getenv(key))
if v == "" {
return fallback
}
return v
}