master
jager 3 years ago
parent 0df3603ef2
commit b8d1391bde

@ -24,6 +24,8 @@ var (
QywxRobotUrl = ""
DingTalkSecret = ""
DingTalkRobotUrl = ""
WxgzhAppid = ""
WxgzhSecret = ""
TianApiKey = ""
UpdateDuration = time.Second * 60 // 默认一分钟
ThresholdValue float64 = 1 // 默认1%
@ -35,6 +37,8 @@ func load(v *viper.Viper) {
QywxRobotUrl = v.GetString("qywxRobotUrl")
DingTalkSecret = v.GetString("dingTalkSecret")
DingTalkRobotUrl = v.GetString("dingTalkRobotUrl")
WxgzhAppid = v.GetString("wxgzhAppid")
WxgzhSecret = v.GetString("wxgzhSecret")
TianApiKey = v.GetString("tianApiKey")
updates := v.GetInt("updateSecond")
if updates > 0 {

@ -2,6 +2,7 @@ package main
import (
"github.com/gin-gonic/gin"
"github.com/jageros/hawox/attribute"
"github.com/jageros/hawox/contextx"
"github.com/jageros/hawox/httpx"
"github.com/jageros/hawox/logx"
@ -10,8 +11,11 @@ import (
"net/http"
"stock/cfg"
"stock/fund"
"stock/module"
"stock/msg"
"stock/stock"
"stock/user"
"stock/wxgzh"
"time"
)
@ -32,6 +36,19 @@ func main() {
timer.Initialize(ctx) // 定时器初始化
// 初始化MongoDB
attribute.Initialize(ctx, func(opt *attribute.Option) {
opt.DBName = "stock-fund"
})
err = user.LoadAllUserIntoCache()
if err != nil {
logx.Fatal(err)
}
stockCodes := user.Codes(false)
fundCodes := user.Codes(true)
timer.RunEveryDay(9, 25, 0, func() {
if !stock.IsSellDay(time.Now()) {
logx.Info("今天不是交易日!")
@ -63,7 +80,7 @@ func main() {
logx.Info("今天不是交易日!")
return
}
text := fund.FundsMsg(cfg.FundCodes...)
text := fund.FundsMsg(fundCodes...)
if text == "" {
logx.Errorf("收集基金数据为空!")
return
@ -76,12 +93,13 @@ func main() {
}
})
ctx.Go(func(ctx contextx.Context) error {
ss, err := stock.NewStocks(cfg.StockCodes...)
err = stock.Init(stockCodes...)
if err != nil {
return err
logx.Fatal(err)
}
ctx.Go(func(ctx contextx.Context) error {
var count int
var opened, closed bool
tk := time.NewTicker(cfg.UpdateDuration)
@ -95,7 +113,7 @@ func main() {
if !closed {
if stock.HasClose(time.Now()) {
logx.Info("--- 已闭市 ---")
ss.Clear()
stock.Clear()
} else {
logx.Info("--- 未开市 ---")
}
@ -111,7 +129,7 @@ func main() {
logx.Infof("--- 交易中 ---")
}
err = ss.Update()
err = stock.Update()
if err != nil {
count++
logx.Errorf("Update ErrCount=%d err=%v", count, err)
@ -121,19 +139,33 @@ func main() {
continue
}
text := ss.Msg()
if text == "" {
logx.Info("已更新数据,未超过阈值,无警告!")
continue
}
err = msg.Send(text)
user.ForEachUser(func(u module.IUser) bool {
codes := u.Codes(false)
stk, err := stock.GetStocks(codes...)
if err != nil {
count++
logx.Errorf("SendMsg ErrCount=%d err=%v", count, err)
if count > 10 {
return err
logx.Error(err)
} else {
err = wxgzh.Send(u.OpenID(), stk)
if err != nil {
logx.Error(err)
}
}
return true
})
//text := ss.Msg()
//if text == "" {
// logx.Info("已更新数据,未超过阈值,无警告!")
// continue
//}
//err = msg.Send(text)
//if err != nil {
// count++
// logx.Errorf("SendMsg ErrCount=%d err=%v", count, err)
// if count > 10 {
// return err
// }
//}
}
}
})
@ -143,21 +175,12 @@ func main() {
r.GET("/sayhello", func(c *gin.Context) {
httpx.PkgMsgWrite(c, map[string]interface{}{"say": "hello world!"})
})
r.POST("/receive", func(c *gin.Context) {
msgs := &msg.RData{}
err := c.BindXML(msgs)
if err != nil {
logx.Error(err)
} else {
logx.Info(msgs)
}
c.String(http.StatusOK, "success")
})
r.POST("/receive", wxgzh.Handle)
r.GET("/receive", func(c *gin.Context) {
echostr := c.Query("echostr")
logx.Infof("=== %s ===", echostr)
logx.Infof("配置接口 %s", echostr)
c.String(http.StatusOK, echostr)
})
}, func(s *httpx.Server) {

@ -3,10 +3,14 @@ qywxRobotUrl: ""
# 钉钉群机器人的secret
dingTalkSecret: ""
# 钉钉群机器人发消息额链接
# 钉钉群机器人发消息的链接
dingTalkRobotUrl: ""
# 微信公众号appid
wxgzhAppid: ""
# 微信公众号secret
wxgzhSecret: ""
# 天行数据获取节假日的api的key https://www.tianapi.com/apiview/139
tianApiKey: ""

@ -50,11 +50,22 @@ func (f *fund) Msg() string {
return msg
}
type funds struct {
codes []string
}
func NewFunds(codes ...string) *funds {
return &funds{
codes: codes,
}
}
func FundsMsg(codes ...string) string {
if len(codes) <= 0 {
return ""
}
var msg = "基金定投估值 >>>\n"
//var msg = "基金定投估值 >>>\n"
msg := ""
for _, code := range codes {
fd, err := newFund(code)
if err != nil {
@ -87,3 +98,17 @@ const msgTemplate = `%s
(%s) %s%%
`
func (fs *funds) Arg(openid string) map[string]interface{} {
arg := map[string]interface{}{
"touser": openid,
"template_id": "SQXWp3RiYySb2GYG-vMYDsSIm-KZNM9szVpFOryUQGQ",
"data": map[string]interface{}{
"keyword": map[string]interface{}{
"value": FundsMsg(fs.codes...),
"color": "#173177",
},
},
}
return arg
}

@ -13,11 +13,14 @@
package fund
import (
"fmt"
"stock/wxgzh"
"testing"
)
func Test_Fund(t *testing.T) {
msg := FundsMsg()
fmt.Println(msg)
f := NewFunds("006229", "162412")
err := wxgzh.Send("o-KDV6NbRaanYz55fJuSgyR0qxxU", f)
if err != nil {
t.Error(err)
}
}

@ -35,6 +35,7 @@ require (
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tal-tech/go-zero v1.2.1 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
github.com/xiaonanln/go-xnsyncutil v0.0.5 // indirect
go.opentelemetry.io/otel v1.0.0-RC2 // indirect
go.opentelemetry.io/otel/trace v1.0.0-RC2 // indirect
go.uber.org/atomic v1.7.0 // indirect
@ -47,7 +48,9 @@ require (
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
google.golang.org/protobuf v1.27.1 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/eapache/queue.v1 v1.1.0 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

@ -422,6 +422,8 @@ github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVM
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/xiaonanln/go-xnsyncutil v0.0.5 h1:1kan2cg95e0quhKEBafu1lNG3UVI44BF0ThlJGa+lJQ=
github.com/xiaonanln/go-xnsyncutil v0.0.5/go.mod h1:PbwFumxH1s5Zc5mPk3A9GFaS/FdIP5WHobaRwQLS8xY=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@ -821,6 +823,8 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/cheggaaa/pb.v1 v1.0.28/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
gopkg.in/eapache/queue.v1 v1.1.0 h1:EldqoJEGtXYiVCMRo2C9mePO2UUGnYn2+qLmlQSqPdc=
gopkg.in/eapache/queue.v1 v1.1.0/go.mod h1:wNtmx1/O7kZSR9zNT1TTOJ7GLpm3Vn7srzlfylFbQwU=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
@ -829,6 +833,8 @@ gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdOD
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=

@ -0,0 +1,21 @@
/**
* @Author: jager
* @Email: lhj168os@gmail.com
* @File: user
* @Date: 2021/12/21 3:17
* @package: module
* @Version: v1.0.0
*
* @Description:
*
*/
package module
type IUser interface {
OpenID() string
Codes(isFund bool) []string
HasSubscribed(isFund bool, code string) bool
Subscribe(isFund bool, codes ...string)
UnSubscribe(isFund bool, codes ...string)
}

@ -9,10 +9,13 @@ import (
"stock/cfg"
"strconv"
"strings"
"sync"
)
var (
baseUrl = "https://hq.sinajs.cn/"
fds *stocks
mx sync.RWMutex
)
type IStock interface {
@ -28,6 +31,14 @@ type stock struct {
nowRise float64
}
func (s *stock) Code() string {
return s.code
}
func (s *stock) Name() string {
return s.values[0]
}
func (s *stock) update(values []string) {
s.values = values
}
@ -130,41 +141,84 @@ func (s *stock) Msg() string {
return msg
}
// ==========================
func Init(codes ...string) error {
fds = &stocks{}
return fds.AddCodes(codes...)
}
type stocks struct {
codes []string
ss []*stock
//codes []string
//ss []*stock
stkMap map[string]*stock
mx sync.RWMutex
}
func (sk *stocks) getStocks(codes ...string) *stocks {
var stks = &stocks{}
for _, code := range codes {
stks.stkMap[code] = sk.stkMap[code]
}
return stks
}
func (sk *stocks) Codes() []string {
var codes []string
for code := range sk.stkMap {
codes = append(codes, code)
}
return codes
}
func (sk *stocks) AddCodes(codes ...string) error {
sk.mx.Lock()
defer sk.mx.Unlock()
var cds []string
for _, code := range codes {
if _, ok := sk.stkMap[code]; ok {
continue
}
cds = append(cds, code)
}
stks, err := newStocks(cds...)
if err != nil {
return err
}
for _, stk := range stks {
sk.stkMap[stk.Code()] = stk
}
return nil
}
func (sk *stocks) Clear() {
if len(sk.ss) <= 0 {
return
sk.mx.Lock()
defer sk.mx.Unlock()
for _, fd := range fds.stkMap {
fd.lastRise = 0
}
sk.ss = []*stock{}
}
func (sk *stocks) Update() error {
str, err := getStockStr(sk.codes)
sk.mx.Lock()
sk.mx.Unlock()
str, err := getStockStr(sk.Codes())
if err != nil {
return err
}
strs := strings.Split(str, ";\n")
if len(sk.ss) == 0 {
sk.ss = []*stock{}
for _, s := range strs {
ss := strings.Split(s, "\"")
if len(ss) >= 2 {
v := &stock{
code: ss[0][13:19],
values: strings.Split(ss[1], ","),
}
sk.ss = append(sk.ss, v)
}
}
if skk, ok := sk.stkMap[v.code]; ok {
skk.update(v.values)
} else {
l := len(sk.ss)
for i, s := range strs {
ss := strings.Split(s, "\"")
if len(ss) >= 2 && i < l {
sk.ss[i].update(strings.Split(ss[1], ","))
sk.stkMap[v.code] = v
}
}
}
@ -173,8 +227,10 @@ func (sk *stocks) Update() error {
}
func (sk *stocks) Msg() string {
sk.mx.RLock()
defer sk.mx.RUnlock()
var resp string
for _, s := range sk.ss {
for _, s := range sk.stkMap {
if s.notify() {
msg := s.Msg()
resp = resp + msg + "\n"
@ -183,33 +239,65 @@ func (sk *stocks) Msg() string {
return resp
}
type IArg interface {
Arg(openId string) map[string]interface{}
func (sk *stocks) Arg(openid string) map[string]interface{} {
arg := map[string]interface{}{
"touser": openid,
"template_id": "yWuLbhAy7TTuqdeB9-VS6CR_t2rZQ8MHkJ62MF3VlS8",
"data": map[string]interface{}{
"keyword": map[string]interface{}{
"value": sk.Msg(),
"color": "#173177",
},
},
}
return arg
}
func (sk *stocks) ForEachStock(f func(stk IArg) error) error {
for _, k := range sk.ss {
err := f(k)
if err != nil {
return err
func GetStocks(codes ...string) (*stocks, error) {
if len(codes) <= 0 {
return nil, errcode.New(1, "股票代码为空")
}
mx.Lock()
defer mx.Unlock()
if fds == nil {
fds = &stocks{}
}
return nil
err := fds.AddCodes(codes...)
if err != nil {
return nil, err
}
return fds.getStocks(codes...), nil
}
func NewStocks(codes ...string) (*stocks, error) {
func Update() error {
return fds.Update()
}
func Clear() {
fds.Clear()
}
func newStocks(codes ...string) ([]*stock, error) {
if len(codes) <= 0 {
return nil, errcode.New(1, "股票代码为空")
}
s := &stocks{
codes: codes,
}
err := s.Update()
str, err := getStockStr(codes)
if err != nil {
return nil, err
}
return s, nil
strs := strings.Split(str, ";\n")
var stks []*stock
for _, s := range strs {
ss := strings.Split(s, "\"")
if len(ss) >= 2 {
v := &stock{
values: strings.Split(ss[1], ","),
}
stks = append(stks, v)
}
}
return stks, nil
}
func addPrefix(code string) string {
@ -251,66 +339,67 @@ const msgTemplate = `%s
%s %s
`
func (s *stock) Arg(openId string) map[string]interface{} {
arg := map[string]interface{}{
"touser": openId,
"template_id": "L7fOGJURj-1HF4cIpFizCOOiAMqER3PG-pfgn37Dalw",
"data": map[string]interface{}{
"first": map[string]interface{}{
"value": s.values[0],
"color": "#173177",
},
"keyword1": map[string]interface{}{
"value": fmt.Sprintf("%s %s", s.values[30], s.values[31]),
"color": "#173177",
},
"keyword2": map[string]interface{}{
"value": s.values[2],
"color": "#173177",
},
"keyword3": map[string]interface{}{
"value": s.values[1],
"color": "#173177",
},
"keyword4": map[string]interface{}{
"value": s.values[3],
"color": "#173177",
},
"keyword5": map[string]interface{}{
"value": s.rise(),
"color": "#173177",
},
"keyword6": map[string]interface{}{
"value": s.values[4],
"color": "#173177",
},
"keyword7": map[string]interface{}{
"value": s.values[5],
"color": "#173177",
},
"keyword8": map[string]interface{}{
"value": s.tradingVolume(),
"color": "#173177",
},
"keyword9": map[string]interface{}{
"value": numFormat(s.values[9]),
"color": "#173177",
},
"keyword10": map[string]interface{}{
"value": s.buyCount(),
"color": "#173177",
},
"keyword11": map[string]interface{}{
"value": s.sellCount(),
"color": "#173177",
},
"remark": map[string]interface{}{
"value": "欢迎再次购买!",
"color": "#173177",
},
},
}
return arg
}
//
//func (s *stock) Arg(openId string) map[string]interface{} {
//
// arg := map[string]interface{}{
// "touser": openId,
// "template_id": "L7fOGJURj-1HF4cIpFizCOOiAMqER3PG-pfgn37Dalw",
//
// "data": map[string]interface{}{
// "first": map[string]interface{}{
// "value": s.values[0],
// "color": "#173177",
// },
// "keyword1": map[string]interface{}{
// "value": fmt.Sprintf("%s %s", s.values[30], s.values[31]),
// "color": "#173177",
// },
// "keyword2": map[string]interface{}{
// "value": s.values[2],
// "color": "#173177",
// },
// "keyword3": map[string]interface{}{
// "value": s.values[1],
// "color": "#173177",
// },
// "keyword4": map[string]interface{}{
// "value": s.values[3],
// "color": "#173177",
// },
// "keyword5": map[string]interface{}{
// "value": s.rise(),
// "color": "#173177",
// },
// "keyword6": map[string]interface{}{
// "value": s.values[4],
// "color": "#173177",
// },
// "keyword7": map[string]interface{}{
// "value": s.values[5],
// "color": "#173177",
// },
// "keyword8": map[string]interface{}{
// "value": s.tradingVolume(),
// "color": "#173177",
// },
// "keyword9": map[string]interface{}{
// "value": numFormat(s.values[9]),
// "color": "#173177",
// },
// "keyword10": map[string]interface{}{
// "value": s.buyCount(),
// "color": "#173177",
// },
// "keyword11": map[string]interface{}{
// "value": s.sellCount(),
// "color": "#173177",
// },
// "remark": map[string]interface{}{
// "value": "欢迎再次购买!",
// "color": "#173177",
// },
// },
// }
// return arg
//}

@ -14,32 +14,71 @@ package user
import (
"github.com/jageros/hawox/attribute"
"stock/module"
"sync"
)
var users sync.Map
var (
users = map[string]*User{}
mx sync.RWMutex
)
func LoadAllUserIntoCache() error {
attrs, err := attribute.LoadAll("user")
if err != nil {
return err
}
mx.Lock()
for _, attr := range attrs {
u := &User{attr: attr}
users.Store(attr.GetAttrID(), u)
users[u.OpenID()] = u
}
mx.Unlock()
return nil
}
func GetUser(openId string) (*User, error) {
u, ok := users.Load(openId)
mx.RLock()
u, ok := users[openId]
mx.RUnlock()
if ok {
return u.(*User), nil
return u, nil
}
us, err := newUser(openId)
if err != nil {
return nil, err
}
users.Store(openId, us)
mx.Lock()
users[openId] = us
mx.Unlock()
return us, nil
}
func ForEachUser(f func(u module.IUser) bool) {
mx.Lock()
defer mx.Unlock()
for _, u := range users {
if !f(u) {
break
}
}
}
func Codes(isFund bool) []string {
var codes = map[string]struct{}{}
ForEachUser(func(u module.IUser) bool {
cds := u.Codes(isFund)
for _, cd := range cds {
if _, ok := codes[cd]; ok {
continue
}
codes[cd] = struct{}{}
}
return true
})
var cods []string
for code := range codes {
cods = append(cods, code)
}
return cods
}

@ -14,11 +14,13 @@ package user
import (
"github.com/jageros/hawox/attribute"
"sync"
"time"
)
type User struct {
attr *attribute.AttrMgr
mx sync.RWMutex
}
func newUser(openId string) (*User, error) {
@ -38,6 +40,8 @@ func (u *User) OpenID() string {
}
func (u *User) Codes(isFund bool) []string {
u.mx.RLock()
defer u.mx.RUnlock()
key := "stock"
if isFund {
key = "fund"
@ -56,6 +60,8 @@ func (u *User) Codes(isFund bool) []string {
// HasSubscribed 查询用户是否订阅此票
func (u *User) HasSubscribed(isFund bool, code string) bool {
u.mx.RLock()
defer u.mx.RUnlock()
key := "stock"
if isFund {
key = "fund"
@ -73,6 +79,8 @@ func (u *User) HasSubscribed(isFund bool, code string) bool {
// Subscribe 订阅股票或基金
func (u *User) Subscribe(isFund bool, codes ...string) {
u.mx.Lock()
defer u.mx.Unlock()
key := "stock"
if isFund {
key = "fund"
@ -95,6 +103,8 @@ func (u *User) Subscribe(isFund bool, codes ...string) {
// UnSubscribe 取消订阅股票或基金
func (u *User) UnSubscribe(isFund bool, codes ...string) {
u.mx.Lock()
defer u.mx.Unlock()
key := "stock"
if isFund {
key = "fund"

@ -14,9 +14,14 @@ package wxgzh
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jageros/hawox/errcode"
"github.com/jageros/hawox/httpc"
"stock/stock"
"github.com/jageros/hawox/logx"
"net/http"
"stock/module"
"stock/user"
"strings"
"time"
)
@ -43,21 +48,8 @@ type AccessToken struct {
Errmsg string `json:"errmsg"`
}
type RData struct {
ToUserName string `xml:"ToUserName"`
FromUserName string `xml:"FromUserName"`
CreateTime int `xml:"CreateTime"`
MsgType string `xml:"MsgType"`
Content string `xml:"Content"`
MsgID int64 `xml:"MsgId"`
}
type xml struct {
ToUserName string `xml:"ToUserName"`
FromUserName string `xml:"FromUserName"`
CreateTime int `xml:"CreateTime"`
MsgType string `xml:"MsgType"`
Content string `xml:"Content"`
type IArg interface {
Arg(openid string) map[string]interface{}
}
func getAccessToken(update bool) (string, error) {
@ -77,16 +69,16 @@ func getAccessToken(update bool) (string, error) {
return resp.AccessToken, nil
}
func send(openID string, stk stock.IArg, recall bool) error {
func send(openID string, arg IArg, recall bool) error {
token, err := getAccessToken(false)
if err != nil {
return err
}
url := fmt.Sprintf(sendUrl, token)
arg := stk.Arg(openID)
msg := arg.Arg(openID)
resp := &Resp{}
err = httpc.RequestWithInterface(httpc.POST, url, httpc.JSON, arg, nil, resp)
err = httpc.RequestWithInterface(httpc.POST, url, httpc.JSON, msg, nil, resp)
if err != nil {
return err
}
@ -97,7 +89,7 @@ func send(openID string, stk stock.IArg, recall bool) error {
return err
}
if recall {
return send(openID, stk, false)
return send(openID, arg, false)
}
}
return errcode.New(int32(resp.Errcode), resp.Errmsg)
@ -105,6 +97,76 @@ func send(openID string, stk stock.IArg, recall bool) error {
return nil
}
func Send(openId string, stk stock.IArg) error {
func Send(openId string, stk IArg) error {
return send(openId, stk, true)
}
func SendAll(stk IArg) error {
user.ForEachUser(func(u module.IUser) bool {
if u.HasSubscribed(false, "") {
err := Send(u.OpenID(), stk)
if err != nil {
logx.Error(err)
return false
}
}
return true
})
return nil
}
type rData struct {
ToUserName string `xml:"ToUserName"`
FromUserName string `xml:"FromUserName"`
CreateTime int64 `xml:"CreateTime"`
MsgType string `xml:"MsgType"`
Content string `xml:"Content"`
MsgID int64 `xml:"MsgId"`
}
type xml struct {
ToUserName string `xml:"ToUserName"`
FromUserName string `xml:"FromUserName"`
CreateTime int64 `xml:"CreateTime"`
MsgType string `xml:"MsgType"`
Content string `xml:"Content"`
}
func Handle(c *gin.Context) {
rMsg := &rData{}
err := c.BindXML(rMsg)
if err != nil {
c.String(http.StatusOK, err.Error())
c.Abort()
return
}
wMsg := &xml{
ToUserName: rMsg.FromUserName,
FromUserName: rMsg.ToUserName,
MsgType: "text",
CreateTime: time.Now().Unix(),
Content: "订阅股票:+st股票代码例如+st600905\n订阅基金+fd基金代码例如+fd161725\n" +
"取消订阅股票:-st股票代码例如-st600905\n取消订阅基金-fd基金代码例如-fd161725",
}
if rMsg.MsgType == "text" {
u, err := user.GetUser(rMsg.FromUserName)
if err != nil {
c.String(http.StatusOK, err.Error())
c.Abort()
return
}
switch {
case strings.HasPrefix(rMsg.Content, "+"):
u.Subscribe(rMsg.Content[1:3] == "fd", rMsg.Content[3:])
wMsg.Content = "订阅成功!"
case strings.HasPrefix(rMsg.Content, "-"):
u.UnSubscribe(rMsg.Content[1:3] == "fd", rMsg.Content[3:])
wMsg.Content = "成功取消订阅!"
}
}
c.XML(http.StatusOK, wMsg)
}

Loading…
Cancel
Save