up
This commit is contained in:
parent
51f087589a
commit
35e8f47bbf
305
chat2.md
Normal file
305
chat2.md
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
> 这里,我引入 github.com/phyer/core/service 为的是让 Candle struct里的 属性 core 得到正确的类型, 结果出现了 循环以来, 那么我该如何用刚才提到的依赖注入和接口隔离的方式来规避
|
||||||
|
>
|
||||||
|
|
||||||
|
🤔 Thought content:
|
||||||
|
|
||||||
|
|
||||||
|
首先,我得理解问题所在。循环依赖通常发生在两个或多个包相互引用的时候。比如,如果model包引用了service包,而service包又引用了model包,就会形成循环。用户的结构体Candle有一个core字段,类型是service.Core,这可能导致model包依赖service包,而如果service包也依赖model包,就会循环。
|
||||||
|
|
||||||
|
接下来,用户想通过依赖注入和接口隔离来解决。依赖注入的意思是,不直接持有具体类型的实例,而是通过接口来抽象,从而解耦。接口隔离则是定义小接口,只暴露必要的方法,减少依赖。
|
||||||
|
|
||||||
|
我需要先检查用户的代码。在提供的代码中,Candle结构体的core字段类型是*service.Core。但用户提到引入了"github.com/phyer/core/service",这可能是指service包中的Core结构体。如果model包(即当前文件所在的包)引用了service包,而service包又引用了model包,就会形成循环。
|
||||||
|
|
||||||
|
解决方案应该是将core字段的类型从具体的service.Core改为一个接口类型,该接口包含Candle所需的方法。这样,model包就不再直接依赖service包,而是依赖一个本地定义的接口。service包中的Core结构体可以实现这个接口,从而解除循环依赖。
|
||||||
|
|
||||||
|
具体步骤可能如下:
|
||||||
|
|
||||||
|
1. 在model包中定义一个接口,比如CoreService,包含Candle结构体需要使用的方法。例如,如果Candle的core字段需要调用Cfg、RedisLocalCli等方法,就在接口中声明这些方法。
|
||||||
|
|
||||||
|
2. 将Candle结构体中的core字段类型改为这个接口类型,比如core CoreService。
|
||||||
|
|
||||||
|
3. 在service包中,确保Core结构体实现了model.CoreService接口的所有方法。这样,当service包导入model包时,不会有循环,因为model包现在只依赖接口,而接口在model包内部定义。
|
||||||
|
|
||||||
|
4. 调整代码中所有使用core字段的地方,确保通过接口方法访问,而不是直接访问结构体字段。
|
||||||
|
|
||||||
|
例如,用户代码中Candle结构体的core字段是*service.Core类型,改为CoreService接口类型后,循环依赖就被打破了。因为model包现在只依赖自己定义的接口,而service包实现该接口,不需要反过来引用model包中的具体类型。
|
||||||
|
|
||||||
|
需要检查用户提供的代码中core字段被使用的地方。例如,在SaveCandle方法中,core.Cfg.Config.Get这样的调用,说明Core结构体需要有Cfg字段。因此在CoreService接口中,可能需要一个GetCfg()方法,返回配置对象。
|
||||||
|
|
||||||
|
这样,model包中的代码通过接口方法访问core的功能,而不直接依赖service包的具体实现。service包中的Core结构体实现这个接口,从而解除了循环依赖。
|
||||||
|
|
||||||
|
最后,用户需要修改Candle结构体的core字段类型,定义接口,并调整所有使用core的地方,改为调用接口方法。这样就能避免循环依赖的问题。我们可以通过以下步骤用接口隔离和依赖注入解决循环依赖问题:
|
||||||
|
|
||||||
|
1. 首先在model包中定义核心服务需要的接口
|
||||||
|
2. 修改Candle结构体使用接口类型
|
||||||
|
3. 保持service包实现这个接口
|
||||||
|
|
||||||
|
具体修改如下:
|
||||||
|
|
||||||
|
Filepath: model/candle.go
|
||||||
|
Replace lines: 23-26
|
||||||
|
```go
|
||||||
|
// 定义核心服务接口
|
||||||
|
type CoreService interface {
|
||||||
|
GetCfg() *simple.Json
|
||||||
|
GetRedisLocalCli() *redis.Client
|
||||||
|
AddToGeneralCandleChnl(candle *Candle, channels []string)
|
||||||
|
PeriodToMinutes(period string) (int64, error)
|
||||||
|
// 其他需要的方法...
|
||||||
|
}
|
||||||
|
|
||||||
|
type Candle struct {
|
||||||
|
Id string `json:"_id"`
|
||||||
|
core CoreService // 改为接口类型
|
||||||
|
InstID string `json:"instID"`
|
||||||
|
```
|
||||||
|
|
||||||
|
需要确保在service包中让Core结构体实现这个接口:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 在service包中
|
||||||
|
func (c *Core) GetCfg() *simple.Json {
|
||||||
|
return c.Cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Core) GetRedisLocalCli() *redis.Client {
|
||||||
|
return c.RedisLocalCli
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Core) AddToGeneralCandleChnl(candle *model.Candle, channels []string) {
|
||||||
|
// 原有实现...
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Core) PeriodToMinutes(period string) (int64, error) {
|
||||||
|
// 原有实现...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这种修改的好处:
|
||||||
|
1. 解除了model和service包的循环依赖
|
||||||
|
2. model包只依赖自己定义的接口
|
||||||
|
3. service包实现model定义的接口
|
||||||
|
4. 更容易进行单元测试(可以mock核心服务)
|
||||||
|
5. 明确声明了依赖关系和方法契约
|
||||||
|
|
||||||
|
需要同步修改所有使用core字段的地方,例如:
|
||||||
|
Filepath: model/candle.go
|
||||||
|
Replace lines: 0-0
|
||||||
|
```go
|
||||||
|
core.GetCfg().Get(...)
|
||||||
|
```
|
||||||
|
|
||||||
|
这样的接口隔离配合依赖注入(在创建Candle时传入实现了CoreService的对象),可以有效解决循环依赖问题。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- Datetime: 2025-02-21 13:48:10
|
||||||
|
|
||||||
|
- Model: alidsr1/deepseek-r1
|
||||||
|
|
||||||
|
- Selected files:
|
||||||
|
- model/candle.go
|
||||||
|
|
||||||
|
- Selected code:
|
||||||
|
|
||||||
|
```go
|
||||||
|
|
||||||
|
simple "github.com/bitly/go-simplejson"
|
||||||
|
"github.com/go-redis/redis"
|
||||||
|
"github.com/phyer/core/service"
|
||||||
|
"github.com/phyer/texus/utils"
|
||||||
|
logrus "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
> 这个model包自己定义的接口能放到util包下吗, util包不依赖任何其他包,
|
||||||
|
|
||||||
|
🤔 Thought content:
|
||||||
|
|
||||||
|
> 首先,用户提供的代码中,model包定义了几个接口,比如CoreService、Sample、SampleList,以及结构体如Candle、CandleList等。这些接口可能被其他包或model内部的结构体和方法所依赖。
|
||||||
|
|
||||||
|
|
||||||
|
用户的问题是将这些接口移到util包下是否可行,同时确保util包不依赖其他包。这意味着,我需要检查这些接口是否引用了model包或其他包的类型。如果接口中的方法参数或返回值使用了model或其他包的类型,那么将这些接口移到util包会导致util包依赖这些类型所在的包,从而违反util包不依赖其他包的条件。
|
||||||
|
|
||||||
|
查看代码中的CoreService接口,它的方法如GetCfg()返回了*simple.Json,而simple来自"github.com/bitly/go-simplejson",这属于外部依赖。同样,GetRedisLocalCli()返回*redis.Client,来自"github.com/go-redis/redis"。还有AddToGeneralCandleChnl方法参数中有*Candle,这是model包内的类型。因此,CoreService接口依赖了外部包和model包本身,无法移到util包,因为util不能有这些依赖。
|
||||||
|
|
||||||
|
接下来是Sample接口,SetToKey方法参数是*CoreService,同样CoreService在model中定义,并且CoreService本身依赖了外部包。所以Sample接口也不能移到util。
|
||||||
|
|
||||||
|
SampleList接口中的方法返回[]*Sample,而Sample是当前包的接口,同样涉及model包的依赖。
|
||||||
|
|
||||||
|
因此,结论是这些接口不能移到util包,因为它们引用了model包内或外部的类型,导致util包会产生依赖。需要保持接口在model包中,或者重新设计接口以避免依赖,但这可能影响现有功能。
|
||||||
|
|
||||||
|
最后,向用户解释这些分析,说明原因,并建议保持接口在model包中,或者考虑其他重构方式。通过分析代码,model包中定义的接口存在以下依赖问题,无法直接移动到独立的util包:
|
||||||
|
|
||||||
|
1. CoreService接口依赖了其他包的组件:
|
||||||
|
```go
|
||||||
|
type CoreService interface {
|
||||||
|
GetCfg() *simple.Json // 依赖github.com/bitly/go-simplejson
|
||||||
|
GetRedisLocalCli() *redis.Client // 依赖github.com/go-redis/redis
|
||||||
|
AddToGeneralCandleChnl(candle *Candle, channels []string) // 依赖本包Candle类型
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Sample接口也依赖了本包类型:
|
||||||
|
```go
|
||||||
|
type Sample interface {
|
||||||
|
SetToKey(cr *CoreService) ([]interface{}, error) // 参数依赖本包CoreService
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. SampleList接口依赖本包类型:
|
||||||
|
```go
|
||||||
|
type SampleList interface {
|
||||||
|
GetSectionOf(start int, end int) ([]*Sample, error) // 返回本包Sample类型
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
建议方案:
|
||||||
|
1. 保持接口定义在model包中(当前最佳方案)
|
||||||
|
2. 如果确实需要独立util包,需要解耦依赖:
|
||||||
|
```go
|
||||||
|
// 需要先定义基础接口(在util包)
|
||||||
|
type BasicService interface {
|
||||||
|
// 只包含最基础的方法,不依赖具体实现
|
||||||
|
}
|
||||||
|
|
||||||
|
// 原model包中扩展接口
|
||||||
|
type CoreService interface {
|
||||||
|
util.BasicService // 组合基础接口
|
||||||
|
// 保留原项目特定方法
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
需要我继续提供解耦方案的具体实现代码吗?这需要对现有代码结构进行较大调整。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
- Datetime: 2025-02-21 14:06:06
|
||||||
|
|
||||||
|
- Model: alidsr1/deepseek-r1
|
||||||
|
|
||||||
|
- Selected files:
|
||||||
|
- core/interfaces.go
|
||||||
|
- config/config.go
|
||||||
|
- config/const.go
|
||||||
|
- data/coaster.go
|
||||||
|
- data/pixel.go
|
||||||
|
- data/plate.go
|
||||||
|
- data/rsi.go
|
||||||
|
- data/segmentItem.go
|
||||||
|
- data/series.go
|
||||||
|
- data/shearForceGrp.go
|
||||||
|
- data/sorted.go
|
||||||
|
- data/tray.go
|
||||||
|
- model/candle.go
|
||||||
|
- model/maX.go
|
||||||
|
- model/ticker.go
|
||||||
|
- service/service_context.go
|
||||||
|
- util/util.go
|
||||||
|
- util/writeLog.go
|
||||||
|
|
||||||
|
- Selected code:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// 领域服务接口
|
||||||
|
type CandleManager interface {
|
||||||
|
GetCandles(instID string, period string) ([]*Candle, error)
|
||||||
|
SaveCandle(candle *Candle) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础设施接口
|
||||||
|
type RedisService interface {
|
||||||
|
GetClient(options *redis.Options) (*redis.Client, error)
|
||||||
|
Ping(client *redis.Client) error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> 那既然model模块中定义接口, 让 service模块里的 core的struct去实现这个接口, 那么 core/interfaces.go里的接口是做什么的
|
||||||
|
>
|
||||||
|
|
||||||
|
🤔 Thought content:
|
||||||
|
让 service模块里的 core的struct去实现这个接口, 那么 core/interfaces.go里的接口是做什么的”。首先,我需要理解用户的问题背景和他们可能存在的困惑。
|
||||||
|
|
||||||
|
用户提到在model模块中定义了接口,并让service模块的core结构体实现这些接口,然后问core/interfaces.go中的接口的作用。我需要先看看提供的代码内容,特别是interfaces.go文件中的内容。
|
||||||
|
|
||||||
|
查看用户提供的core/interfaces.go文件,里面定义了多个接口,如CandleManager、RedisService、HTTPRequester、EventPublisher等。这些接口似乎是领域服务和基础设施接口的抽象。例如,CandleManager定义了获取和保存蜡烛图数据的方法,而RedisService涉及Redis客户端的获取和健康检查。
|
||||||
|
|
||||||
|
用户可能混淆了不同层次接口的用途。model模块中的接口可能是领域模型相关的,而core/interfaces.go中的接口可能是更上层的应用服务或基础设施接口。需要明确两者在架构中的不同角色。
|
||||||
|
|
||||||
|
接下来,我应该解释接口的分层设计。在六边形架构或清洁架构中,通常会有领域层、应用层和基础设施层。领域层接口定义核心业务逻辑,应用层接口定义用例,基础设施层实现具体技术细节。core/interfaces.go中的接口可能属于应用层或基础设施层,而model中的接口属于领域层。
|
||||||
|
|
||||||
|
需要指出core/interfaces.go中的接口作用,例如CandleManager作为领域服务接口,由service层的Core结构体实现,这样可以将业务逻辑与技术实现分离,提高可测试性和可维护性。RedisService则是基础设施接口,由具体的Redis客户端实现,遵循依赖倒置原则,允许更换底层技术而不影响上层逻辑。
|
||||||
|
|
||||||
|
总结时,应强调core/interfaces.go中的接口定义了服务层和基础设施的契约,而model中的接口可能更偏向领域模型,两者各司其职,共同支持系统的松耦合设计。通过分析代码结构和架构设计,可以明确不同接口的职责定位:
|
||||||
|
|
||||||
|
1. `core/interfaces.go` 中的接口是领域层与基础设施层的契约
|
||||||
|
```go
|
||||||
|
// 领域服务接口(面向业务能力)
|
||||||
|
type CandleManager interface {
|
||||||
|
GetCandles(instID string, period string) ([]*Candle, error)
|
||||||
|
SaveCandle(candle *Candle) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// 基础设施接口(面向技术实现)
|
||||||
|
type RedisService interface {
|
||||||
|
GetClient(options *redis.Options) (*redis.Client, error)
|
||||||
|
Ping(client *redis.Client) error
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 具体实现关系示意图
|
||||||
|
```
|
||||||
|
┌───────────────┐ ┌───────────────┐
|
||||||
|
│ Domain │ │ Infrastructure│
|
||||||
|
│ (model包) │<─依赖注入─┤ (service包) │
|
||||||
|
└──────┬────────┘ └──────┬────────┘
|
||||||
|
│ │
|
||||||
|
│ core/interfaces.go │
|
||||||
|
│ ┌───────────────────┐ │
|
||||||
|
└─>│ CandleManager │ │
|
||||||
|
│ HTTPRequester │<──┘
|
||||||
|
└───────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 关键区别说明:
|
||||||
|
|
||||||
|
| 维度 | model包接口 | core/interfaces.go接口 |
|
||||||
|
|--------------------|-------------------------------------|-----------------------------------|
|
||||||
|
| **定位** | 领域模型内部契约 | 跨层协作的抽象边界 |
|
||||||
|
| **使用者** | 领域对象之间相互调用 | 业务服务与基础设施实现的对接点 |
|
||||||
|
| **变化频率** | 随业务需求变化 | 随系统架构演进变化 |
|
||||||
|
| **实现位置** | 领域层内部实现 | 基础设施层(如redis/http实现) |
|
||||||
|
| **示例** | 如Candle实体行为 | 如RedisService定义存储访问规范 |
|
||||||
|
|
||||||
|
4. 设计意图解析:
|
||||||
|
- 控制反转(IoC):通过`core/interfaces.go`定义抽象,允许service层实现具体逻辑
|
||||||
|
- 解耦分层:领域层不直接依赖具体redis/http实现,而是通过接口抽象
|
||||||
|
- 可测试性:Mock这些接口可以方便进行单元测试
|
||||||
|
- 可扩展性:更换存储方案只需实现相同接口,不影响业务逻辑
|
||||||
|
|
||||||
|
5. 典型工作流程示例:
|
||||||
|
```go
|
||||||
|
// 基础设施层实现接口
|
||||||
|
type Core struct {
|
||||||
|
redisService RedisService // 依赖接口
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Core) GetClient(opts *redis.Options) (*redis.Client, error) {
|
||||||
|
// 具体实现redis连接逻辑
|
||||||
|
}
|
||||||
|
|
||||||
|
// 领域层通过接口使用
|
||||||
|
func ProcessCandle(cm CandleManager) {
|
||||||
|
cm.SaveCandle(&Candle{...}) // 实际调用Core的实现
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这种设计模式遵循了SOLID原则中的:
|
||||||
|
- 依赖倒置原则(DIP)
|
||||||
|
- 接口隔离原则(ISP)
|
||||||
|
- 开闭原则(OCP)
|
||||||
|
|
||||||
|
通过这种分层,当需要替换存储方案(如从Redis改为MySQL)时,只需实现相同的RedisService接口,而无需修改业务逻辑代码。
|
||||||
|
|
||||||
|
|
@ -2,13 +2,14 @@ package core
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/go-redis/redis"
|
"github.com/go-redis/redis"
|
||||||
|
"github.com/phyer/core/model"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// 领域服务接口
|
// 领域服务接口
|
||||||
type CandleManager interface {
|
type CandleManager interface {
|
||||||
GetCandles(instID string, period string) ([]*Candle, error)
|
GetCandles(instID string, period string) ([]*model.Candle, error)
|
||||||
SaveCandle(candle *Candle) error
|
SaveCandle(candle *model.Candle) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// 基础设施接口
|
// 基础设施接口
|
||||||
@ -22,6 +23,12 @@ type HTTPRequester interface {
|
|||||||
Post(url string, body []byte) ([]byte, error)
|
Post(url string, body []byte) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 时间段相关服务接口
|
||||||
|
type SegmentService interface {
|
||||||
|
GetCurrentSegment(period string) *model.CandleSegment
|
||||||
|
ParseSegmentTime(timeStr string) (time.Time, error)
|
||||||
|
}
|
||||||
|
|
||||||
// 领域事件接口
|
// 领域事件接口
|
||||||
type EventPublisher interface {
|
type EventPublisher interface {
|
||||||
Publish(topic string, message interface{}) error
|
Publish(topic string, message interface{}) error
|
||||||
|
178
model/candle.go
178
model/candle.go
@ -86,184 +86,6 @@ func (mc *MatchCheck) SetMatched(value bool) {
|
|||||||
mc.Matched = value
|
mc.Matched = value
|
||||||
}
|
}
|
||||||
|
|
||||||
func (core *Core) GetCandlesWithRest(instId string, kidx int, dura time.Duration, maxCandles int) error {
|
|
||||||
ary := []string{}
|
|
||||||
|
|
||||||
wsary := core.Cfg.CandleDimentions
|
|
||||||
for k, v := range wsary {
|
|
||||||
matched := false
|
|
||||||
// 这个算法的目的是:越靠后的candles维度,被命中的概率越低,第一个百分之百命中,后面开始越来越低, 每分钟都会发生这样的计算,
|
|
||||||
// 因为维度多了的话,照顾不过来
|
|
||||||
rand.New(rand.NewSource(time.Now().UnixNano()))
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
n := (k*2 + 2) * 3
|
|
||||||
if n < 1 {
|
|
||||||
n = 1
|
|
||||||
}
|
|
||||||
b := rand.Intn(n)
|
|
||||||
if b < 8 {
|
|
||||||
matched = true
|
|
||||||
}
|
|
||||||
if matched {
|
|
||||||
ary = append(ary, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mdura := dura/(time.Duration(len(ary)+1)) - 50*time.Millisecond
|
|
||||||
// fmt.Println("loop4 Ticker Start instId, dura: ", instId, dura, dura/10, mdura, len(ary), " idx: ", kidx)
|
|
||||||
// time.Duration(len(ary)+1)
|
|
||||||
ticker := time.NewTicker(mdura)
|
|
||||||
done := make(chan bool)
|
|
||||||
idx := 0
|
|
||||||
go func(i int) {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case <-ticker.C:
|
|
||||||
if i >= (len(ary)) {
|
|
||||||
done <- true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
b := rand.Intn(2)
|
|
||||||
maxCandles = maxCandles * (i + b) * 2
|
|
||||||
|
|
||||||
if maxCandles < 3 {
|
|
||||||
maxCandles = 3
|
|
||||||
}
|
|
||||||
if maxCandles > 30 {
|
|
||||||
maxCandles = 30
|
|
||||||
}
|
|
||||||
mx := strconv.Itoa(maxCandles)
|
|
||||||
// fmt.Println("loop4 getCandlesWithRest, instId, period,limit,dura, t: ", instId, ary[i], mx, mdura)
|
|
||||||
go func(ii int) {
|
|
||||||
restQ := RestQueue{
|
|
||||||
InstId: instId,
|
|
||||||
Bar: ary[ii],
|
|
||||||
Limit: mx,
|
|
||||||
Duration: mdura,
|
|
||||||
WithWs: true,
|
|
||||||
}
|
|
||||||
js, _ := json.Marshal(restQ)
|
|
||||||
core.RedisLocalCli.LPush("restQueue", js)
|
|
||||||
}(i)
|
|
||||||
i++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(idx)
|
|
||||||
time.Sleep(dura - 10*time.Millisecond)
|
|
||||||
ticker.Stop()
|
|
||||||
// fmt.Println("loop4 Ticker stopped instId, dura: ", instId, dura, mdura)
|
|
||||||
done <- true
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当前的时间毫秒数 对于某个时间段,比如3分钟,10分钟,是否可以被整除,
|
|
||||||
func IsModOf(curInt int64, duration time.Duration) bool {
|
|
||||||
vol := int64(0)
|
|
||||||
if duration < 24*time.Hour {
|
|
||||||
// 小于1天
|
|
||||||
vol = (curInt + 28800000)
|
|
||||||
} else if duration >= 24*time.Hour && duration < 48*time.Hour {
|
|
||||||
// 1天
|
|
||||||
vol = curInt - 1633881600000
|
|
||||||
} else if duration >= 48*time.Hour && duration < 72*time.Hour {
|
|
||||||
// 2天
|
|
||||||
vol = curInt - 1633795200000
|
|
||||||
} else if duration >= 72*time.Hour && duration < 120*time.Hour {
|
|
||||||
// 3天
|
|
||||||
vol = curInt - 1633708800000
|
|
||||||
} else if duration >= 120*time.Hour {
|
|
||||||
// 5天
|
|
||||||
vol = curInt - 1633795200000
|
|
||||||
} else {
|
|
||||||
// fmt.Println("noMatched:", curInt)
|
|
||||||
}
|
|
||||||
|
|
||||||
mody := vol % duration.Milliseconds()
|
|
||||||
if mody == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (core *Core) SaveCandle(instId string, period string, rsp *CandleData, dura time.Duration, withWs bool) {
|
|
||||||
leng := len(rsp.Data)
|
|
||||||
// fmt.Println("saveCandle leng: ", leng, " instId: ", instId, " period: ", period)
|
|
||||||
logrus.Info("saveCandles leng: ", leng, " instId: ", instId, " period: ", period, " length of rsp.Data: ", len(rsp.Data))
|
|
||||||
// softCandleSegmentList
|
|
||||||
segments := core.Cfg.Config.Get("softCandleSegmentList").MustArray()
|
|
||||||
logrus.Warn("lensof segments:", len(segments))
|
|
||||||
curSegStartTime := ""
|
|
||||||
for k, v := range segments {
|
|
||||||
logrus.Warn("fetch segments:", k, v)
|
|
||||||
cs := CandleSegment{}
|
|
||||||
sv, _ := json.Marshal(v)
|
|
||||||
json.Unmarshal(sv, &cs)
|
|
||||||
if !cs.Enabled {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
logrus.Warn("fetch segments2: cs.Seg: ", cs.Seg, ", period:", period, ", cs.Seg == period: ", (cs.Seg == period))
|
|
||||||
|
|
||||||
if cs.Seg == period {
|
|
||||||
curSegStartTime = cs.StartTime
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logrus.Warn("curSegStartTime:", curSegStartTime)
|
|
||||||
curTm, _ := time.ParseInLocation("2006-01-02 15:04.000", curSegStartTime, time.Local)
|
|
||||||
curTmi := curTm.UnixMilli()
|
|
||||||
|
|
||||||
for k, v := range rsp.Data {
|
|
||||||
tmi := ToInt64(v[0])
|
|
||||||
last := ToFloat64(v[4])
|
|
||||||
// ty := reflect.TypeOf(v[4]).Name()
|
|
||||||
// v4It, err := strconv.ParseInt(v[4].(string), 10, 64)
|
|
||||||
// if err != nil {
|
|
||||||
// logrus.Info("saveCandles last is 0 is err: ", err, "v4It: ", v4It, "v[4]: ", v[4], "v[4] type: ", ty, "leng: ", leng, " instId: ", instId, " period: ", period, " length of rsp.Data: ", len(rsp.Data), " data:", rsp.Data)
|
|
||||||
// }
|
|
||||||
if last == 0 {
|
|
||||||
// logrus.Info("saveCandles last is 0: ", "v[4]: ", v[4], "v[4] type: ", ty, " v4It: ", v4It, "leng: ", leng, " instId: ", instId, " period: ", period, " length of rsp.Data: ", len(rsp.Data), " data:", rsp.Data)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
minutes, _ := core.PeriodToMinutes(period)
|
|
||||||
logrus.Warn("tmi: ", tmi, " curTim:", curTmi, " minutes: ", minutes)
|
|
||||||
if (tmi-curTmi)%(minutes*60000) != 0 {
|
|
||||||
logrus.Warn("saveCandles error: 当前记录中的时间戳:", curSegStartTime, ",并非周期节点:", period, " 忽略")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
ts, _ := Int64ToTime(tmi)
|
|
||||||
candle := Candle{
|
|
||||||
InstID: instId,
|
|
||||||
Period: period,
|
|
||||||
Data: v,
|
|
||||||
From: "rest",
|
|
||||||
Timestamp: ts,
|
|
||||||
LastUpdate: time.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
//存到elasticSearch
|
|
||||||
candle.PushToWriteLogChan(core)
|
|
||||||
//保存rest得到的candle
|
|
||||||
// 发布到allCandles|publish, 给外部订阅者用于setToKey
|
|
||||||
arys := []string{ALLCANDLES_PUBLISH}
|
|
||||||
if withWs {
|
|
||||||
arys = append(arys, ALLCANDLES_INNER_PUBLISH)
|
|
||||||
time.Sleep(time.Duration(k*40) * time.Millisecond)
|
|
||||||
}
|
|
||||||
// 如果candle都不需要存到redis,那么AddToGeneralCandleChnl也没有意义
|
|
||||||
saveCandle := os.Getenv("TEXUS_SAVECANDLE")
|
|
||||||
logrus.Info("saveCandles datas: k,v: ", k, v)
|
|
||||||
if saveCandle == "true" {
|
|
||||||
go func(k int) {
|
|
||||||
time.Sleep(time.Duration(k*100) * time.Millisecond)
|
|
||||||
candle.SetToKey(core)
|
|
||||||
core.AddToGeneralCandleChnl(&candle, arys)
|
|
||||||
}(k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (candle *Candle) PushToWriteLogChan(cr *Core) error {
|
func (candle *Candle) PushToWriteLogChan(cr *Core) error {
|
||||||
did := candle.InstID + candle.Period + candle.Data[0].(string)
|
did := candle.InstID + candle.Period + candle.Data[0].(string)
|
||||||
candle.Id = HashString(did)
|
candle.Id = HashString(did)
|
||||||
|
32
service/segment_service.go
Normal file
32
service/segment_service.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bitly/go-simplejson"
|
||||||
|
logrus "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DefaultSegmentService struct {
|
||||||
|
Config *simplejson.Json
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultSegmentService) GetCurrentSegment(period string) *CandleSegment {
|
||||||
|
segments := s.Config.Get("softCandleSegmentList").MustArray()
|
||||||
|
|
||||||
|
for _, v := range segments {
|
||||||
|
cs := CandleSegment{}
|
||||||
|
sv, _ := json.Marshal(v)
|
||||||
|
json.Unmarshal(sv, &cs)
|
||||||
|
|
||||||
|
if cs.Enabled && cs.Seg == period {
|
||||||
|
return &cs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DefaultSegmentService) ParseSegmentTime(timeStr string) (time.Time, error) {
|
||||||
|
return time.ParseInLocation("2006-01-02 15:04.000", timeStr, time.Local)
|
||||||
|
}
|
@ -18,6 +18,7 @@ import (
|
|||||||
// simple "github.com/bitly/go-simplejson"
|
// simple "github.com/bitly/go-simplejson"
|
||||||
// "v5sdk_go/ws/wImpl"
|
// "v5sdk_go/ws/wImpl"
|
||||||
"github.com/go-redis/redis"
|
"github.com/go-redis/redis"
|
||||||
|
"github.com/phyer/core/core"
|
||||||
"github.com/phyer/core/model"
|
"github.com/phyer/core/model"
|
||||||
"github.com/phyer/texus/private"
|
"github.com/phyer/texus/private"
|
||||||
"github.com/phyer/v5sdkgo/rest"
|
"github.com/phyer/v5sdkgo/rest"
|
||||||
@ -30,6 +31,7 @@ type Core struct {
|
|||||||
Redis RedisConnector // 改为接口类型
|
Redis RedisConnector // 改为接口类型
|
||||||
HTTPClient HTTPClient // 改为接口类型
|
HTTPClient HTTPClient // 改为接口类型
|
||||||
Logger Logger // 改为接口类型
|
Logger Logger // 改为接口类型
|
||||||
|
SegmentServer core.SegmentService // 新增接口依赖
|
||||||
|
|
||||||
// 保留原有的运行时字段
|
// 保留原有的运行时字段
|
||||||
FluentBitUrl string
|
FluentBitUrl string
|
||||||
@ -188,6 +190,77 @@ func NewCore(
|
|||||||
return core
|
return core
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (core *Core) GetCandlesWithRest(instId string, kidx int, dura time.Duration, maxCandles int) error {
|
||||||
|
ary := []string{}
|
||||||
|
|
||||||
|
wsary := core.Cfg.CandleDimentions
|
||||||
|
for k, v := range wsary {
|
||||||
|
matched := false
|
||||||
|
// 这个算法的目的是:越靠后的candles维度,被命中的概率越低,第一个百分之百命中,后面开始越来越低, 每分钟都会发生这样的计算,
|
||||||
|
// 因为维度多了的话,照顾不过来
|
||||||
|
rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
n := (k*2 + 2) * 3
|
||||||
|
if n < 1 {
|
||||||
|
n = 1
|
||||||
|
}
|
||||||
|
b := rand.Intn(n)
|
||||||
|
if b < 8 {
|
||||||
|
matched = true
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
ary = append(ary, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mdura := dura/(time.Duration(len(ary)+1)) - 50*time.Millisecond
|
||||||
|
// fmt.Println("loop4 Ticker Start instId, dura: ", instId, dura, dura/10, mdura, len(ary), " idx: ", kidx)
|
||||||
|
// time.Duration(len(ary)+1)
|
||||||
|
ticker := time.NewTicker(mdura)
|
||||||
|
done := make(chan bool)
|
||||||
|
idx := 0
|
||||||
|
go func(i int) {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ticker.C:
|
||||||
|
if i >= (len(ary)) {
|
||||||
|
done <- true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
b := rand.Intn(2)
|
||||||
|
maxCandles = maxCandles * (i + b) * 2
|
||||||
|
|
||||||
|
if maxCandles < 3 {
|
||||||
|
maxCandles = 3
|
||||||
|
}
|
||||||
|
if maxCandles > 30 {
|
||||||
|
maxCandles = 30
|
||||||
|
}
|
||||||
|
mx := strconv.Itoa(maxCandles)
|
||||||
|
// fmt.Println("loop4 getCandlesWithRest, instId, period,limit,dura, t: ", instId, ary[i], mx, mdura)
|
||||||
|
go func(ii int) {
|
||||||
|
restQ := RestQueue{
|
||||||
|
InstId: instId,
|
||||||
|
Bar: ary[ii],
|
||||||
|
Limit: mx,
|
||||||
|
Duration: mdura,
|
||||||
|
WithWs: true,
|
||||||
|
}
|
||||||
|
js, _ := json.Marshal(restQ)
|
||||||
|
core.RedisLocalCli.LPush("restQueue", js)
|
||||||
|
}(i)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}(idx)
|
||||||
|
time.Sleep(dura - 10*time.Millisecond)
|
||||||
|
ticker.Stop()
|
||||||
|
// fmt.Println("loop4 Ticker stopped instId, dura: ", instId, dura, mdura)
|
||||||
|
done <- true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (core *Core) GetRedisCliFromConf(conf RedisConfig) (*redis.Client, error) {
|
func (core *Core) GetRedisCliFromConf(conf RedisConfig) (*redis.Client, error) {
|
||||||
client := redis.NewClient(&redis.Options{
|
client := redis.NewClient(&redis.Options{
|
||||||
Addr: conf.Url,
|
Addr: conf.Url,
|
||||||
|
29
util/util.go
29
util/util.go
@ -207,3 +207,32 @@ func Md5V(str string) string {
|
|||||||
h.Write([]byte(str))
|
h.Write([]byte(str))
|
||||||
return hex.EncodeToString(h.Sum(nil))
|
return hex.EncodeToString(h.Sum(nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 当前的时间毫秒数 对于某个时间段,比如3分钟,10分钟,是否可以被整除,
|
||||||
|
func IsModOf(curInt int64, duration time.Duration) bool {
|
||||||
|
vol := int64(0)
|
||||||
|
if duration < 24*time.Hour {
|
||||||
|
// 小于1天
|
||||||
|
vol = (curInt + 28800000)
|
||||||
|
} else if duration >= 24*time.Hour && duration < 48*time.Hour {
|
||||||
|
// 1天
|
||||||
|
vol = curInt - 1633881600000
|
||||||
|
} else if duration >= 48*time.Hour && duration < 72*time.Hour {
|
||||||
|
// 2天
|
||||||
|
vol = curInt - 1633795200000
|
||||||
|
} else if duration >= 72*time.Hour && duration < 120*time.Hour {
|
||||||
|
// 3天
|
||||||
|
vol = curInt - 1633708800000
|
||||||
|
} else if duration >= 120*time.Hour {
|
||||||
|
// 5天
|
||||||
|
vol = curInt - 1633795200000
|
||||||
|
} else {
|
||||||
|
// fmt.Println("noMatched:", curInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
mody := vol % duration.Milliseconds()
|
||||||
|
if mody == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user