This commit is contained in:
jiangyong27 2023-04-06 13:46:19 +08:00
commit 6888a8a813
11 changed files with 527 additions and 0 deletions

12
.gitignore vendored Normal file
View File

@ -0,0 +1,12 @@
*.iml
.idea/
output/
dockerfiles/
log
.DS_Store
web/node_modules
ad-api.exe
doc
cmd/test
go.sum
pkg

20
Dockerfile Normal file
View File

@ -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"]

62
cmd/film.go Normal file
View File

@ -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 {}
}

16
conf/server.conf.dev Normal file
View File

@ -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"

12
conf/server.conf.prod Normal file
View File

@ -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"

93
config/config.go Normal file
View File

@ -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))
}

39
go.mod Normal file
View File

@ -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
)

81
model/model.go Normal file
View File

@ -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
}

60
model/orders.go Normal file
View File

@ -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
}

16
start.sh Normal file
View File

@ -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

116
worker/worker.go Normal file
View File

@ -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)
}
}
}