2025-08-14 23:41:29 +00:00
|
|
|
|
package blockchainservicelogic
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"blockchain/internal/domain/usecase"
|
|
|
|
|
"context"
|
2025-08-20 09:34:27 +00:00
|
|
|
|
"fmt"
|
2025-08-14 23:41:29 +00:00
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"blockchain/gen_result/pb/code.30cm.net/digimon/app-cloudep-blockchain"
|
|
|
|
|
"blockchain/internal/svc"
|
|
|
|
|
|
|
|
|
|
"github.com/zeromicro/go-zero/core/logx"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type GetHistoryKlineDataLogic struct {
|
2025-08-23 10:09:41 +00:00
|
|
|
|
ctx context.Context
|
|
|
|
|
|
2025-08-14 23:41:29 +00:00
|
|
|
|
svcCtx *svc.ServiceContext
|
|
|
|
|
logx.Logger
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewGetHistoryKlineDataLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetHistoryKlineDataLogic {
|
|
|
|
|
return &GetHistoryKlineDataLogic{
|
|
|
|
|
ctx: ctx,
|
|
|
|
|
svcCtx: svcCtx,
|
|
|
|
|
Logger: logx.WithContext(ctx),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *GetHistoryKlineDataLogic) GetHistoryKlineData(in *app_cloudep_blockchain.HistoryReq) (*app_cloudep_blockchain.OKResp, error) {
|
2025-08-20 09:34:27 +00:00
|
|
|
|
// 1) 先驗證輸入(避免把錯誤時間丟進背景 goroutine)
|
|
|
|
|
st, err := time.Parse(time.RFC3339, in.GetStartTime())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("invalid startTime (RFC3339): %w", err)
|
|
|
|
|
}
|
|
|
|
|
et, err := time.Parse(time.RFC3339, in.GetEndTime())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, fmt.Errorf("invalid endTime (RFC3339): %w", err)
|
|
|
|
|
}
|
|
|
|
|
if !st.Before(et) {
|
|
|
|
|
return nil, fmt.Errorf("startTime must be before endTime")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chunks := splitByWeeksUTC(st, et)
|
|
|
|
|
if len(chunks) == 0 {
|
|
|
|
|
return &app_cloudep_blockchain.OKResp{}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for _, ch := range chunks {
|
|
|
|
|
timeBucket := ch
|
|
|
|
|
err := l.svcCtx.WorkerPools.Submit(func() {
|
|
|
|
|
_ = l.svcCtx.BinanceDataSource.UpsertKline(context.Background(), usecase.QueryKline{
|
|
|
|
|
Symbol: in.GetSymbol(),
|
|
|
|
|
Interval: in.GetInterval(),
|
|
|
|
|
StartTime: timeBucket.start.UnixNano(),
|
|
|
|
|
EndTime: timeBucket.end.UnixNano(),
|
|
|
|
|
})
|
2025-08-14 23:41:29 +00:00
|
|
|
|
})
|
2025-08-20 09:34:27 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-14 23:41:29 +00:00
|
|
|
|
|
|
|
|
|
return &app_cloudep_blockchain.OKResp{}, nil
|
|
|
|
|
}
|
2025-08-20 09:34:27 +00:00
|
|
|
|
|
|
|
|
|
// --------- 小工具:按「週一起」切段(UTC) ---------
|
|
|
|
|
|
|
|
|
|
type timeRange struct {
|
|
|
|
|
start time.Time // 含
|
|
|
|
|
end time.Time // 不含
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 取得該時間所在「週一 00:00:00」(UTC)
|
|
|
|
|
func weekStartMondayUTC(t time.Time) time.Time {
|
|
|
|
|
t = t.UTC()
|
|
|
|
|
// Go: Sunday=0 ... Saturday=6;我們要週一=起始
|
|
|
|
|
wd := int(t.Weekday())
|
|
|
|
|
if wd == 0 { // Sunday
|
|
|
|
|
wd = 7
|
|
|
|
|
}
|
|
|
|
|
// 將時間歸零到當天 00:00
|
|
|
|
|
d0 := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, time.UTC)
|
|
|
|
|
// 回推到週一
|
|
|
|
|
return d0.AddDate(0, 0, -(wd - 1))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 將 [start,end) 切成多個「週粒度」的區間
|
|
|
|
|
func splitByWeeksUTC(start, end time.Time) []timeRange {
|
|
|
|
|
var chunks []timeRange
|
|
|
|
|
if !start.Before(end) {
|
|
|
|
|
return chunks
|
|
|
|
|
}
|
|
|
|
|
cur := start.UTC()
|
|
|
|
|
end = end.UTC()
|
|
|
|
|
|
|
|
|
|
for cur.Before(end) {
|
|
|
|
|
// 下一個週一 00:00
|
|
|
|
|
nextWeekStart := weekStartMondayUTC(cur).AddDate(0, 0, 7)
|
|
|
|
|
chunkEnd := end
|
|
|
|
|
if nextWeekStart.Before(end) {
|
|
|
|
|
chunkEnd = nextWeekStart
|
|
|
|
|
}
|
|
|
|
|
chunks = append(chunks, timeRange{start: cur, end: chunkEnd})
|
|
|
|
|
cur = chunkEnd
|
|
|
|
|
}
|
|
|
|
|
return chunks
|
|
|
|
|
}
|