From 6888a8a813b6d9e3390f1bec67750d2dfcb8b957 Mon Sep 17 00:00:00 2001 From: jiangyong27 Date: Thu, 6 Apr 2023 13:46:19 +0800 Subject: [PATCH] init --- .gitignore | 12 +++++ Dockerfile | 20 ++++++++ cmd/film.go | 62 ++++++++++++++++++++++ conf/server.conf.dev | 16 ++++++ conf/server.conf.prod | 12 +++++ config/config.go | 93 +++++++++++++++++++++++++++++++++ go.mod | 39 ++++++++++++++ model/model.go | 81 +++++++++++++++++++++++++++++ model/orders.go | 60 ++++++++++++++++++++++ start.sh | 16 ++++++ worker/worker.go | 116 ++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 527 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile create mode 100644 cmd/film.go create mode 100644 conf/server.conf.dev create mode 100644 conf/server.conf.prod create mode 100644 config/config.go create mode 100644 go.mod create mode 100644 model/model.go create mode 100644 model/orders.go create mode 100644 start.sh create mode 100644 worker/worker.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..253ab51 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +*.iml +.idea/ +output/ +dockerfiles/ +log +.DS_Store +web/node_modules +ad-api.exe +doc +cmd/test +go.sum +pkg diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..31e37b5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,20 @@ +FROM registry.cn-shanghai.aliyuncs.com/devcon/godev:v1.0.14 +ADD . /app/src +WORKDIR /app/src + +RUN mkdir -p /app/bin /app/log + +RUN export GOROOT=/usr/local/go1.18.2 && \ + export GOPROXY=https://goproxy.cn && \ + export PATH=$PATH:$GOROOT/bin && \ + go mod tidy && \ + go build cmd/film.go && \ + mv film /app/bin && \ + cp -r conf /app && \ + cp start.sh /app && \ + chmod +x /app/start.sh + +WORKDIR /app +EXPOSE 9263 + +CMD ["/app/start.sh"] diff --git a/cmd/film.go b/cmd/film.go new file mode 100644 index 0000000..cadc977 --- /dev/null +++ b/cmd/film.go @@ -0,0 +1,62 @@ +package main + +import ( + "film/config" + "film/model" + "film/worker" + "fmt" + rotatelogs "github.com/lestrrat-go/file-rotatelogs" + "github.com/rifflock/lfshook" + log "github.com/sirupsen/logrus" + "github.com/smbrave/goutil" + "os" + "time" +) + +func initLog() { + cfg := config.GetConfig() + logfile := "log/server.log" + writer, err := rotatelogs.New( + logfile+".%Y%m%d", + rotatelogs.WithLinkName(logfile), + rotatelogs.WithMaxAge(time.Duration(86400*7)*time.Second), + rotatelogs.WithRotationTime(time.Duration(86400)*time.Second), + ) + if err != nil { + panic(err) + } + + pathMap := lfshook.WriterMap{ + log.TraceLevel: writer, + log.DebugLevel: writer, + log.InfoLevel: writer, + log.WarnLevel: writer, + log.ErrorLevel: writer, + log.FatalLevel: writer, + log.PanicLevel: writer, + } + + log.AddHook(lfshook.NewHook(pathMap, new(goutil.LogFile))) + + log.SetOutput(os.Stdout) + log.SetReportCaller(true) + log.SetFormatter(new(goutil.LogFile)) + log.SetLevel(log.Level(cfg.Server.LogLevel)) +} +func main() { + config.LoadServerConfig() + initLog() + cfg := config.GetConfig() + dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", cfg.Mysql.User, + cfg.Mysql.Pass, cfg.Mysql.Host, cfg.Mysql.Port, cfg.Mysql.Db) + _, err := model.Init(dsn) + if err != nil { + panic(err) + } + + w := &worker.Worker{ + Token: config.GetConfig().Film.Token, + } + w.Init() + select {} +} diff --git a/conf/server.conf.dev b/conf/server.conf.dev new file mode 100644 index 0000000..90361e0 --- /dev/null +++ b/conf/server.conf.dev @@ -0,0 +1,16 @@ +[server] +address = "0.0.0.0:9263" +#0:PAINC 1:FATAL 2:ERROR 3:WARNING 4:INFO 5:DEBUG 6:TRACE +log_level = 6 + + +[mysql] +host = "100.118.29.74" +port = 3307 +user = "film" +pass = "7VCfJx7H8MymUm" +db = "film" + + +[film] +token = "cc5f1d4c36f7e9544d641a174b298f94" \ No newline at end of file diff --git a/conf/server.conf.prod b/conf/server.conf.prod new file mode 100644 index 0000000..4630fba --- /dev/null +++ b/conf/server.conf.prod @@ -0,0 +1,12 @@ +[server] +address = "0.0.0.0:9263" +#0:PAINC 1:FATAL 2:ERROR 3:WARNING 4:INFO 5:DEBUG 6:TRACE +log_level = 6 + + +[mysql] +host = "192.168.13.213" +port = 3306 +user = "film" +pass = "7VCfJx7H8MymUm" +db = "film" diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..33fba49 --- /dev/null +++ b/config/config.go @@ -0,0 +1,93 @@ +package config + +import ( + "github.com/mitchellh/mapstructure" + log "github.com/sirupsen/logrus" + "github.com/smbrave/goutil" + "github.com/spf13/viper" + "os" + "strings" +) + +var ( + config *Config + configEnv string + BuildTime string + CommitId string +) + +type Mysql struct { + Host string `toml:"host"` + Port int `toml:"port"` + User string `toml:"user"` + Pass string `toml:"pass"` + Db string `toml:"db"` +} + +type Server struct { + Address string `toml:"address"` + LogLevel int `toml:"log_level"` + EnableDoc bool `toml:"enable_doc"` +} + +type Redis struct { + Addr string `toml:"addr"` + Db int `toml:"db"` + Password string `toml:"password"` +} +type Film struct { + Token string `toml:"token"` +} + +type Config struct { + Server *Server `toml:"server"` + Mysql *Mysql `toml:"mysql"` + Redis *Redis `toml:"redis"` + Film *Film `toml:"film"` +} + +func GetEnv() string { + return configEnv +} + +func IsProdEnv() bool { + return configEnv == "prod" +} + +func IsDevEnv() bool { + return configEnv == "dev" +} + +func IsTestEnv() bool { + return configEnv == "test" +} + +func GetConfig() *Config { + return config +} + +func LoadServerConfig() { + configEnv = os.Getenv("CONFIG_ENV") + if configEnv == "" { + configEnv = "dev" + } + + var envConfig Config + + viper.SetConfigFile("conf/server.conf." + configEnv) + viper.SetConfigType("toml") + viper.AutomaticEnv() + viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) + viper.SetEnvPrefix("conf") + if err := viper.ReadInConfig(); err != nil { + panic(err) + } + if err := viper.Unmarshal(&envConfig, func(decoderConfig *mapstructure.DecoderConfig) { + decoderConfig.TagName = "toml" + }); err != nil { + panic(err) + } + + config = &envConfig + log.Infof("load real config[%s] ", goutil.EncodeJSONIndent(config)) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..2f54575 --- /dev/null +++ b/go.mod @@ -0,0 +1,39 @@ +module film + +go 1.18 + +require ( + github.com/go-co-op/gocron v1.19.0 + github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible + github.com/mitchellh/mapstructure v1.5.0 + github.com/rifflock/lfshook v0.0.0-20180920164130-b9218ef580f5 + github.com/sirupsen/logrus v1.9.0 + github.com/smbrave/goutil v0.0.0-20230208141215-e3360c3bfd1b + github.com/spf13/cast v1.5.0 + github.com/spf13/viper v1.15.0 + gorm.io/driver/mysql v1.4.7 + gorm.io/gorm v1.24.6 +) + +require ( + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/jonboulle/clockwork v0.4.0 // indirect + github.com/lestrrat-go/strftime v1.0.6 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect + github.com/spf13/afero v1.9.3 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + golang.org/x/sync v0.1.0 // indirect + golang.org/x/sys v0.3.0 // indirect + golang.org/x/text v0.5.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/model/model.go b/model/model.go new file mode 100644 index 0000000..ddc772d --- /dev/null +++ b/model/model.go @@ -0,0 +1,81 @@ +package model + +import ( + "context" + log "github.com/sirupsen/logrus" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "time" +) + +var ( + db *gorm.DB = nil +) + +func SetDb(b *gorm.DB) { + db = b +} + +func GetOrm() *gorm.DB { + return db +} + +type DBLogger struct { + level logger.LogLevel +} + +func (d *DBLogger) LogMode(level logger.LogLevel) logger.Interface { + d.level = level + return d +} + +func (d *DBLogger) Info(context.Context, string, ...interface{}) { + +} + +func (d *DBLogger) Warn(context.Context, string, ...interface{}) { + +} + +func (d *DBLogger) Error(context.Context, string, ...interface{}) { + +} + +func (d *DBLogger) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) { + sql, affects := fc() + + if err != nil && err != gorm.ErrRecordNotFound { + log.Errorf("[SQL]sql=%s affect=%d cost=%dms error=%v", sql, affects, time.Since(begin).Milliseconds(), err) + } else { + if time.Since(begin).Milliseconds() > 200 { + log.Errorf("[SQL]sql=%s affect=%d cost=%dms", sql, affects, time.Since(begin).Milliseconds()) + } else { + log.Debugf("[SQL]sql=%s affect=%d cost=%dms", sql, affects, time.Since(begin).Milliseconds()) + } + } +} + +func Init(dsn string) (*gorm.DB, error) { + var err error + db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) + if err != nil { + log.Errorf("open dsn[%s] error[%s]", dsn, err) + return nil, err + } + + db.Logger = &DBLogger{} + return db, err +} + +func Ping() error { + d, err := db.DB() + if err != nil { + return err + } + + if err := d.Ping(); err != nil { + return err + } + return nil +} diff --git a/model/orders.go b/model/orders.go new file mode 100644 index 0000000..1e4109c --- /dev/null +++ b/model/orders.go @@ -0,0 +1,60 @@ +package model + +import ( + "gorm.io/gorm" + "time" +) + +var ( + tableNameOrder = "orders" +) + +type Order struct { + Id int64 + OrderId string + StartTime string + StartDate string + TotalPrice int64 + PayPrice int64 + MaoyanPrice int64 + SeatNum int + Seats string + ShowTime int64 + PayTime int64 + CityName string + ProvinceName string + Ting string + CinemaName string + MovieName string + Address string + CreateTime int64 + UpdateTime int64 +} + +func AddOrder(o *Order) (int64, error) { + o.CreateTime = time.Now().Unix() + res := db.Table(tableNameOrder).Create(o) + return o.Id, res.Error +} + +func GetOrder(id int64) (*Order, error) { + var u Order + tx := db.Table(tableNameOrder) + tx = tx.Where("id = ?", id) + res := tx.First(&u) + if res.Error == gorm.ErrRecordNotFound { + return nil, nil + } + + if res.Error != nil { + return nil, res.Error + } + return &u, nil +} + +func UpdateOrder(o *Order) error { + o.UpdateTime = time.Now().Unix() + tx := db.Table(tableNameOrder) + res := tx.Save(o) + return res.Error +} diff --git a/start.sh b/start.sh new file mode 100644 index 0000000..0fcbba1 --- /dev/null +++ b/start.sh @@ -0,0 +1,16 @@ +#!/bin/bash +set -x +ROOT=$(cd `dirname $0`; pwd) +cd $ROOT + +if [ "$BINARY" = "" ];then + BINARY="film" +fi + +while [ true ];do + $ROOT/bin/$BINARY $@ >> log/run.log 2>&1 + /usr/local/bin/sendproxy weixin jiangyong "$CONFIG_ENV $BINARY 服务崩溃[`date +"%Y-%m-%d %H:%M:%S"`]" + sleep 60 +done + + diff --git a/worker/worker.go b/worker/worker.go new file mode 100644 index 0000000..d12ba59 --- /dev/null +++ b/worker/worker.go @@ -0,0 +1,116 @@ +package worker + +import ( + "crypto/tls" + "encoding/json" + "film/model" + "fmt" + "github.com/go-co-op/gocron" + log "github.com/sirupsen/logrus" + "github.com/spf13/cast" + "io" + "net/http" + "net/url" + "time" +) + +type Worker struct { + Token string +} + +func (w *Worker) Init() error { + timezone, _ := time.LoadLocation("Asia/Shanghai") + cron := gocron.NewScheduler(timezone) + + cron.Every(30).Seconds().Do(func() { + w.syncOrder() + }) + + cron.StartAsync() + return nil +} + +func (w *Worker) httpPost(requestUrl string) ([]byte, error) { + client := &http.Client{Timeout: 20 * time.Second} + //忽略https的证书 + client.Transport = &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } + p := url.Values{} + u, _ := url.Parse(requestUrl) + + u.RawQuery = p.Encode() + req, err := http.NewRequest("POST", u.String(), nil) + if err != nil { + return nil, err + } + req.Header.Set("token", w.Token) + + resp, err := client.Do(req) + if err != nil { + return nil, err + } + defer resp.Body.Close() + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("%d:%s", resp.StatusCode, resp.Status) + } + return io.ReadAll(resp.Body) +} + +func (w *Worker) syncOrder() { + orderUrl := "https://hahapiao.cn/api/Synchro/toList" + body, err := w.httpPost(orderUrl) + if err != nil { + log.Errorf("syncOrder error : %s", err.Error()) + return + } + result := make(map[string]interface{}) + if err := json.Unmarshal(body, &result); err != nil { + log.Errorf("json.unmarshal [%s] error : %s", string(body), err.Error()) + return + } + if cast.ToInt(result["status"]) != 200 || cast.ToInt(result["code"]) != 200 { + log.Errorf("status[%d] code[%d] message[%s]", cast.ToInt(result["status"]), cast.ToInt(result["code"]), cast.ToString(result["msg"])) + return + } + + datas := cast.ToSlice(result["data"]) + + for _, d := range datas { + data := cast.ToStringMap(d) + id := cast.ToInt64(data["id"]) + order, err := model.GetOrder(id) + if err != nil { + log.Errorf("get order id : %d error :%s", id, err.Error()) + } + isAdd := false + + if order == nil { + order = new(model.Order) + order.Id = id + isAdd = true + } + order.OrderId = cast.ToString(data["order_id"]) + order.PayPrice = int64(cast.ToFloat64(data["payPrice"]) * 100) + order.TotalPrice = int64(cast.ToFloat64(data["total_price"]) * 100) + order.MaoyanPrice = int64(cast.ToFloat64(data["maoyan_price"]) * 100) + order.StartTime = cast.ToString(data["startTime"]) + order.StartDate = cast.ToString(data["invalidateDate"]) + order.SeatNum = cast.ToInt(data["seat_num"]) + order.Seats = cast.ToString(data["seats"]) + order.ShowTime = cast.ToInt64(data["show_time"]) + order.PayTime = cast.ToInt64(data["pay_time"]) + order.CityName = cast.ToString(data["cityName"]) + order.ProvinceName = cast.ToString(data["provinceName"]) + order.MovieName = cast.ToString(data["movieName"]) + order.Address = cast.ToString(data["address"]) + order.Ting = cast.ToString(data["ting"]) + + if isAdd { + model.AddOrder(order) + } else { + model.UpdateOrder(order) + } + } + +}