├── .gitignore ├── 12306_server ├── Gopkg.lock ├── Gopkg.toml ├── README.md ├── conf └── app.conf ├── controllers ├── base.go ├── passenger.go ├── schedule.go ├── station.go ├── task.go └── user.go ├── lastupdate.tmp ├── main.go ├── models ├── object.go └── user.go ├── routers ├── commentsRouter____src_12306_controllers.go ├── commentsRouter_controllers.go └── router.go ├── tests └── default_test.go └── utils ├── jwt.go ├── kyfw.go ├── request.go └── task.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | .idea 10 | .vscode 11 | 12 | # Architecture specific extensions/prefixes 13 | *.[568vq] 14 | [568vq].out 15 | 16 | *.cgo1.go 17 | *.cgo2.c 18 | _cgo_defun.c 19 | _cgo_gotypes.go 20 | _cgo_export.* 21 | 22 | _testmain.go 23 | 24 | *.exe 25 | *.test 26 | *.prof 27 | 28 | #Custom 29 | .DS_Store 30 | bee 31 | *.exe~ 32 | .goxc.local.json 33 | 34 | vendor/ 35 | -------------------------------------------------------------------------------- /12306_server: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panda843/12306_server/c57b5c58bca9ddf3fbd507c384ababafa63c9434/12306_server -------------------------------------------------------------------------------- /Gopkg.lock: -------------------------------------------------------------------------------- 1 | # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. 2 | 3 | 4 | [[projects]] 5 | name = "github.com/astaxie/beego" 6 | packages = [".","config","context","context/param","grace","logs","session","toolbox","utils"] 7 | revision = "bf5c5626ab429e66d88602e1ab1ab5fbf4629a01" 8 | version = "v1.9.2" 9 | 10 | [[projects]] 11 | branch = "master" 12 | name = "github.com/gopherjs/gopherjs" 13 | packages = ["js"] 14 | revision = "444abdf920945de5d4a977b572bcc6c674d1e4eb" 15 | 16 | [[projects]] 17 | name = "github.com/jtolds/gls" 18 | packages = ["."] 19 | revision = "77f18212c9c7edc9bd6a33d383a7b545ce62f064" 20 | version = "v4.2.1" 21 | 22 | [[projects]] 23 | name = "github.com/smartystreets/assertions" 24 | packages = [".","internal/go-render/render","internal/oglematchers"] 25 | revision = "0b37b35ec7434b77e77a4bb29b79677cced992ea" 26 | version = "1.8.1" 27 | 28 | [[projects]] 29 | name = "github.com/smartystreets/goconvey" 30 | packages = ["convey","convey/gotest","convey/reporting"] 31 | revision = "9e8dc3f972df6c8fcc0375ef492c24d0bb204857" 32 | version = "1.6.3" 33 | 34 | [solve-meta] 35 | analyzer-name = "dep" 36 | analyzer-version = 1 37 | inputs-digest = "1f33cb2d2cba43884c8c3d63eb0e5ddcf473e38558987cdbc5576e56bc435ba1" 38 | solver-name = "gps-cdcl" 39 | solver-version = 1 40 | -------------------------------------------------------------------------------- /Gopkg.toml: -------------------------------------------------------------------------------- 1 | 2 | # Gopkg.toml example 3 | # 4 | # Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md 5 | # for detailed Gopkg.toml documentation. 6 | # 7 | # required = ["github.com/user/thing/cmd/thing"] 8 | # ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] 9 | # 10 | # [[constraint]] 11 | # name = "github.com/user/project" 12 | # version = "1.0.0" 13 | # 14 | # [[constraint]] 15 | # name = "github.com/user/project2" 16 | # branch = "dev" 17 | # source = "github.com/myfork/project2" 18 | # 19 | # [[override]] 20 | # name = "github.com/x/y" 21 | # version = "2.4.0" 22 | 23 | 24 | [[constraint]] 25 | name = "github.com/astaxie/beego" 26 | version = "1.9.2" 27 | 28 | [[constraint]] 29 | name = "github.com/smartystreets/goconvey" 30 | version = "1.6.3" 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 12306API接口 2 | 3 | ```golang 4 | const ( 5 | //用户登录Init 6 | UserLoginInit = "https://kyfw.12306.cn/otn/login/init" 7 | //检测用户是否登录 8 | UserAuthUAMTK = "https://kyfw.12306.cn/passport/web/auth/uamtk" 9 | //获取登录验证码 10 | UserGetVerifyImg = "https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&%g" 11 | //检测登录验证码 12 | UserCheckVerify = "https://kyfw.12306.cn/passport/captcha/captcha-check" 13 | //登录12306 14 | UserLogin12306 = "https://kyfw.12306.cn/passport/web/login" 15 | //获取登录信息 16 | UserGetToken = "https://kyfw.12306.cn/otn/uamauthclient" 17 | //提交订单 18 | OrderSubmitOrderURL = "https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest" 19 | //初始化订单页面 20 | OrderInitOrderURL = "https://kyfw.12306.cn/otn/confirmPassenger/initDc" 21 | //检测订单 22 | OrderCheckedURL = "https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo" 23 | //获取车票数和排队人数 24 | OrderGetCountURL = "https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount" 25 | //加入购买队列 26 | OrderJoinBuyQueue = "https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue" 27 | //获取取票码 28 | OrderGetTicketCode = "https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random=1516252521719&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN=%s" 29 | //获取站台信息 30 | QueryGetStation = "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9042" 31 | //查询车次初始化 32 | QueryScheduleInit = "https://kyfw.12306.cn/otn/leftTicket/init" 33 | //车次查询日志 34 | QueryScheduleLog = "https://kyfw.12306.cn/otn/leftTicket/log?leftTicketDTO.train_date=%s&leftTicketDTO.from_station=%s&leftTicketDTO.to_station=%s&purpose_codes=ADULT" 35 | //查询车次信息 36 | QuerySchedule = "https://kyfw.12306.cn/otn/%s?leftTicketDTO.train_date=%s&leftTicketDTO.from_station=%s&leftTicketDTO.to_station=%s&purpose_codes=ADULT" 37 | //查询乘客信息 38 | QueryPassenger = "https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs" 39 | ) 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /conf/app.conf: -------------------------------------------------------------------------------- 1 | appname = 12306 2 | httpport = 9000 3 | runmode = dev 4 | autorender = false 5 | sessionon = true 6 | copyrequestbody = true 7 | EnableDocs = true 8 | JwtKey = 123456 9 | HostName = https://www.ganktools.com -------------------------------------------------------------------------------- /controllers/base.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "encoding/json" 7 | "strings" 8 | 9 | "github.com/astaxie/beego" 10 | "github.com/chuanshuo843/12306_server/utils" 11 | ) 12 | 13 | type _ResData struct { 14 | Status bool `json:"status"` 15 | Message string `json:"message"` 16 | Data interface{} `json:"data"` 17 | IsLogin bool `json:"login"` 18 | } 19 | 20 | // Operations about Users 21 | type BaseController struct { 22 | beego.Controller 23 | res _ResData 24 | Kyfw *utils.Kyfw 25 | AppID string 26 | UserID string 27 | } 28 | 29 | var ( 30 | kyfws *utils.KyfwList 31 | tasks *utils.TaskList 32 | ) 33 | 34 | func init() { 35 | kyfws = utils.InitKyfwList() 36 | tasks = utils.InitTaskList() 37 | } 38 | 39 | func (b *BaseController) Prepare() { 40 | //初始化返回数据 41 | b.res.Status = true 42 | b.res.Message = "success" 43 | b.res.Data = "" 44 | //获取用户对应的信息 45 | b.GetUserKyfw() 46 | if b.Kyfw == nil { 47 | b.Fail().SetMsg("登录信息失效,请重新登录").Send() 48 | return 49 | } 50 | hasher := md5.New() 51 | hasher.Write([]byte(b.UserID)) 52 | b.UserID = hex.EncodeToString(hasher.Sum(nil)) 53 | } 54 | 55 | // 获取用户数据 . 56 | func (b *BaseController) GetUserKyfw() { 57 | //Options的不获取 58 | if b.Ctx.Input.Is("OPTIONS") { 59 | return 60 | } 61 | //获取用户数据,没登录使用APPID,登录使用Token,同时存在则以Token为准 62 | b.AppID = b.GetString("app_id") 63 | b.UserID = b.AppID 64 | if b.AppID != "" { 65 | b.Kyfw = kyfws.Get(b.AppID) 66 | } 67 | authString := b.Ctx.Input.Header("Authorization") 68 | if authString != "" { 69 | kv := strings.Split(authString, " ") 70 | if len(kv) == 2 || kv[0] == "Bearer" { 71 | b.UserID = kv[1] 72 | b.Kyfw = kyfws.Get(kv[1]) 73 | } 74 | } 75 | } 76 | 77 | func (b *BaseController) Success() *BaseController { 78 | b.res.Status = true 79 | return b 80 | } 81 | 82 | func (b *BaseController) SetMsg(message string) *BaseController { 83 | b.res.Message = message 84 | return b 85 | } 86 | 87 | func (b *BaseController) Fail() *BaseController { 88 | b.res.Status = false 89 | return b 90 | } 91 | 92 | func (b *BaseController) SetData(data interface{}) *BaseController { 93 | b.res.Data = data 94 | return b 95 | } 96 | 97 | func (b *BaseController) Send() { 98 | if b.Kyfw != nil { 99 | b.res.IsLogin = b.Kyfw.IsLogin 100 | } 101 | json_data, _ := json.Marshal(b.res) 102 | b.Data["json"] = string(json_data) 103 | //初始化数据 104 | b.res.Status = true 105 | b.res.Message = "success" 106 | b.res.Data = "" 107 | b.ServeJSON() 108 | } 109 | -------------------------------------------------------------------------------- /controllers/passenger.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import "encoding/json" 4 | 5 | // "encoding/json" 6 | // "github.com/astaxie/beego" 7 | 8 | // PassengerController Operations about object 9 | type PassengerController struct { 10 | BaseController 11 | } 12 | 13 | // @Title Get 14 | // @Description 获取乘客列表 15 | // @Param uid path string true "The key for staticblock" 16 | // @Success 200 {object} models.User 17 | // @Failure 403 :uid is empty 18 | // @router / [get] 19 | func (p *PassengerController) Get() { 20 | data, err := p.Kyfw.GetPassenger() 21 | if err != nil { 22 | p.Fail().SetMsg(err.Error()).Send() 23 | return 24 | } 25 | var reData map[string]interface{} 26 | json.Unmarshal([]byte(data), &reData) 27 | p.Success().SetData(reData).Send() 28 | } 29 | -------------------------------------------------------------------------------- /controllers/schedule.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import "encoding/json" 4 | 5 | // "encoding/json" 6 | // "fmt" 7 | // "strings" 8 | // "github.com/astaxie/beego" 9 | 10 | // ScheduleController Operations about object 11 | type ScheduleController struct { 12 | BaseController 13 | } 14 | 15 | // @router /init [get] 16 | func (s *ScheduleController) InitQuery() { 17 | err := s.Kyfw.InitQuerySchedule() 18 | if err != nil { 19 | s.Fail().SetMsg(err.Error()).Send() 20 | return 21 | } 22 | s.Success().Send() 23 | } 24 | 25 | // @Title Get 26 | // @Description get user by uid 27 | // @Param uid path string true "The key for staticblock" 28 | // @Success 200 {object} models.User 29 | // @Failure 403 :uid is empty 30 | // @router / [get] 31 | func (s *ScheduleController) Get() { 32 | startStation := s.GetString("start_station") 33 | endStation := s.GetString("end_station") 34 | startCode := s.GetString("start_code") 35 | endCode := s.GetString("end_code") 36 | date := s.GetString("date") 37 | if startStation == "" || endStation == "" || date == "" { 38 | s.Fail().SetMsg("请选择正确的站台信息").Send() 39 | return 40 | } 41 | data, err := s.Kyfw.GetSchedule(startStation, endStation, startCode, endCode, date) 42 | if err != nil { 43 | s.Fail().SetMsg(err.Error()).Send() 44 | return 45 | } 46 | var reData map[string]interface{} 47 | json.Unmarshal([]byte(data), &reData) 48 | s.Success().SetData(reData).Send() 49 | } 50 | -------------------------------------------------------------------------------- /controllers/station.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | 4 | // StationController Operations about object 5 | type StationController struct { 6 | BaseController 7 | } 8 | 9 | // @Title Get 10 | // @Description get user by uid 11 | // @Param uid path string true "The key for staticblock" 12 | // @Success 200 {object} models.User 13 | // @Failure 403 :uid is empty 14 | // @router / [get] 15 | func (s *StationController) Get() { 16 | data,err := s.Kyfw.GetStations() 17 | if err != nil { 18 | s.Fail().SetMsg(err.Error()).Send() 19 | return 20 | } 21 | //缓存12个小时 22 | //s.Ctx.Output.Header("Cache-Control:", "public,max-age=43200") 23 | s.Success().SetData(data).Send() 24 | } 25 | -------------------------------------------------------------------------------- /controllers/task.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import "strconv" 4 | 5 | // PassengerController Operations about object 6 | type TaskController struct { 7 | BaseController 8 | } 9 | 10 | // @Title Post 11 | // @Description 添加任务 12 | // @Param uid path string true "The key for staticblock" 13 | // @Success 200 {object} models.User 14 | // @Failure 403 :uid is empty 15 | // @router / [post] 16 | func (t *TaskController) Post() { 17 | 18 | secretStr := t.GetString("secret_key") 19 | trainNo := t.GetString("train_no") 20 | trainCode := t.GetString("train_code") 21 | trainDate := t.GetString("train_date") 22 | formatDate := t.GetString("format_date") 23 | startStation := t.GetString("start_station") 24 | endStation := t.GetString("end_station") 25 | startCode := t.GetString("start_code") 26 | endCode := t.GetString("end_code") 27 | ticketStr := t.GetString("ticket_str") 28 | passengerStr := t.GetString("passenger_str") 29 | task := tasks.CreateTask(t.Kyfw, secretStr, trainNo, trainCode, startStation, startCode, endStation, endCode, trainDate, formatDate, ticketStr, passengerStr) 30 | tasks.Set(t.UserID, task) 31 | t.Success().SetMsg("任务添加成功").Send() 32 | } 33 | 34 | // @Title Get 35 | // @Description 获取任务 36 | // @Param uid path string true "The key for staticblock" 37 | // @Success 200 {object} models.User 38 | // @Failure 403 :uid is empty 39 | // @router / [get] 40 | func (t *TaskController) Get() { 41 | taskMaps := tasks.Get(t.UserID) 42 | t.Success().SetData(taskMaps).Send() 43 | } 44 | 45 | // @Title Get 46 | // @Description 获取任务日志 47 | // @Param uid path string true "The key for staticblock" 48 | // @Success 200 {object} models.User 49 | // @Failure 403 :uid is empty 50 | // @router /log/:id:int [get] 51 | func (t *TaskController) Log() { 52 | taskID, _ := strconv.ParseInt(t.Ctx.Input.Param(":id"), 10, 64) 53 | t.Success().SetData(taskID).Send() 54 | } 55 | -------------------------------------------------------------------------------- /controllers/user.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "time" 5 | 6 | "net/http" 7 | 8 | "github.com/chuanshuo843/12306_server/utils" 9 | ) 10 | 11 | // Operations about Users 12 | type UserController struct { 13 | BaseController 14 | } 15 | 16 | func (u *UserController) Prepare() { 17 | //初始化返回数据 18 | u.res.Status = true 19 | u.res.Message = "success" 20 | u.res.Data = "" 21 | //获取用户对应的信息 22 | u.GetUserKyfw() 23 | } 24 | 25 | func (u *UserController) InitLogin() { 26 | sid := u.Ctx.Input.CruSession.SessionID() 27 | u.Kyfw = kyfws.Create(sid) 28 | _, err := u.Kyfw.InitLogin() 29 | if err != nil { 30 | u.Fail().SetMsg(err.Error()).Send() 31 | return 32 | } 33 | u.Success().SetData(`{"app_id":"` + sid + `"}`).Send() 34 | } 35 | 36 | //登录12306 37 | func (u *UserController) Login() { 38 | if u.Kyfw == nil { 39 | u.Fail().SetMsg("获取用户数据失败").Send() 40 | return 41 | } 42 | verify := u.GetString("verify") 43 | username := u.GetString("username") 44 | password := u.GetString("password") 45 | // key := u.GetString("key") 46 | errLogin := u.Kyfw.Login(username, password, verify) 47 | if errLogin != nil { 48 | u.Fail().SetMsg(errLogin.Error()).Send() 49 | } 50 | //生成JWT 51 | jwt := utils.InitJwt() 52 | jwt.Payload.Jti = time.Now().Unix() 53 | jwt.Payload.Iat = time.Now().Unix() - 30 //减30秒以防请求过快 54 | jwt.Payload.Nbf = time.Now().Unix() - 30 //减30秒以防请求过快 55 | jwt.Payload.Exp = time.Now().Unix() + 43200 //有效期十二个小时 56 | jwt.Payload.Data = `{"username":"` + u.Kyfw.LoginName + `"}` 57 | token := jwt.Encode() 58 | kyfws.Move(u.AppID, token) 59 | u.res.IsLogin = u.Kyfw.IsLogin 60 | reJson := map[string]string{"access_token": token} 61 | u.Success().SetMsg("登录成功").SetData(reJson).Send() 62 | } 63 | 64 | //获取12306登录验证码 65 | func (u *UserController) VerifyCode() { 66 | if u.Kyfw == nil { 67 | u.Fail().SetMsg("获取用户数据失败").Send() 68 | return 69 | } 70 | //获取验证码 71 | data, errVer := u.Kyfw.GetVerifyImages() 72 | if errVer != nil { 73 | http.Error(u.Ctx.ResponseWriter, "Not Found", 404) 74 | return 75 | } 76 | u.Ctx.Output.ContentType("png") 77 | u.Ctx.Output.Body(data) 78 | } 79 | -------------------------------------------------------------------------------- /lastupdate.tmp: -------------------------------------------------------------------------------- 1 | {"/Users/tools/workspace/golang/src/github.com/chuanshuo843/12306_server/controllers":1516373508000000000,"E:\\WorkSpaces\\golang\\src\\12306\\controllers":1515130963641000000,"E:\\WorkSpaces\\golang\\src\\github.com\\chuanshuo843\\12306_server\\controllers":1516606923279745800} -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/astaxie/beego/plugins/cors" 5 | _ "github.com/chuanshuo843/12306_server/routers" 6 | "github.com/astaxie/beego" 7 | ) 8 | 9 | func main() { 10 | //开发模式 11 | if beego.BConfig.RunMode == "dev" { 12 | beego.BConfig.WebConfig.DirectoryIndex = true 13 | beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger" 14 | } 15 | //跨域处理 16 | beego.InsertFilter("*", beego.BeforeRouter, cors.Allow(&cors.Options{ 17 | AllowAllOrigins: true, 18 | AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, 19 | AllowHeaders: []string{"Origin", "Authorization", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"}, 20 | ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Content-Type"}, 21 | AllowCredentials: true, 22 | })) 23 | beego.Run() 24 | } 25 | -------------------------------------------------------------------------------- /models/object.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | var ( 10 | Objects map[string]*Object 11 | ) 12 | 13 | type Object struct { 14 | ObjectId string 15 | Score int64 16 | PlayerName string 17 | } 18 | 19 | func init() { 20 | Objects = make(map[string]*Object) 21 | Objects["hjkhsbnmn123"] = &Object{"hjkhsbnmn123", 100, "astaxie"} 22 | Objects["mjjkxsxsaa23"] = &Object{"mjjkxsxsaa23", 101, "someone"} 23 | } 24 | 25 | func AddOne(object Object) (ObjectId string) { 26 | object.ObjectId = "astaxie" + strconv.FormatInt(time.Now().UnixNano(), 10) 27 | Objects[object.ObjectId] = &object 28 | return object.ObjectId 29 | } 30 | 31 | func GetOne(ObjectId string) (object *Object, err error) { 32 | if v, ok := Objects[ObjectId]; ok { 33 | return v, nil 34 | } 35 | return nil, errors.New("ObjectId Not Exist") 36 | } 37 | 38 | func GetAll() map[string]*Object { 39 | return Objects 40 | } 41 | 42 | func Update(ObjectId string, Score int64) (err error) { 43 | if v, ok := Objects[ObjectId]; ok { 44 | v.Score = Score 45 | return nil 46 | } 47 | return errors.New("ObjectId Not Exist") 48 | } 49 | 50 | func Delete(ObjectId string) { 51 | delete(Objects, ObjectId) 52 | } 53 | 54 | -------------------------------------------------------------------------------- /models/user.go: -------------------------------------------------------------------------------- 1 | package models 2 | 3 | import ( 4 | "errors" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | var ( 10 | UserList map[string]*User 11 | ) 12 | 13 | func init() { 14 | UserList = make(map[string]*User) 15 | u := User{"user_11111", "astaxie", "11111", Profile{"male", 20, "Singapore", "astaxie@gmail.com"}} 16 | UserList["user_11111"] = &u 17 | } 18 | 19 | type User struct { 20 | Id string 21 | Username string 22 | Password string 23 | Profile Profile 24 | } 25 | 26 | type Profile struct { 27 | Gender string 28 | Age int 29 | Address string 30 | Email string 31 | } 32 | 33 | func AddUser(u User) string { 34 | u.Id = "user_" + strconv.FormatInt(time.Now().UnixNano(), 10) 35 | UserList[u.Id] = &u 36 | return u.Id 37 | } 38 | 39 | func GetUser(uid string) (u *User, err error) { 40 | if u, ok := UserList[uid]; ok { 41 | return u, nil 42 | } 43 | return nil, errors.New("User not exists") 44 | } 45 | 46 | func GetAllUsers() map[string]*User { 47 | return UserList 48 | } 49 | 50 | func UpdateUser(uid string, uu *User) (a *User, err error) { 51 | if u, ok := UserList[uid]; ok { 52 | if uu.Username != "" { 53 | u.Username = uu.Username 54 | } 55 | if uu.Password != "" { 56 | u.Password = uu.Password 57 | } 58 | if uu.Profile.Age != 0 { 59 | u.Profile.Age = uu.Profile.Age 60 | } 61 | if uu.Profile.Address != "" { 62 | u.Profile.Address = uu.Profile.Address 63 | } 64 | if uu.Profile.Gender != "" { 65 | u.Profile.Gender = uu.Profile.Gender 66 | } 67 | if uu.Profile.Email != "" { 68 | u.Profile.Email = uu.Profile.Email 69 | } 70 | return u, nil 71 | } 72 | return nil, errors.New("User Not Exist") 73 | } 74 | 75 | func Login(username, password string) bool { 76 | for _, u := range UserList { 77 | if u.Username == username && u.Password == password { 78 | return true 79 | } 80 | } 81 | return false 82 | } 83 | 84 | func DeleteUser(uid string) { 85 | delete(UserList, uid) 86 | } 87 | -------------------------------------------------------------------------------- /routers/commentsRouter____src_12306_controllers.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/astaxie/beego" 5 | "github.com/astaxie/beego/context/param" 6 | ) 7 | 8 | func init() { 9 | 10 | beego.GlobalControllerRouter["12306/controllers:ObjectController"] = append(beego.GlobalControllerRouter["12306/controllers:ObjectController"], 11 | beego.ControllerComments{ 12 | Method: "Post", 13 | Router: `/`, 14 | AllowHTTPMethods: []string{"post"}, 15 | MethodParams: param.Make(), 16 | Params: nil}) 17 | 18 | beego.GlobalControllerRouter["12306/controllers:ObjectController"] = append(beego.GlobalControllerRouter["12306/controllers:ObjectController"], 19 | beego.ControllerComments{ 20 | Method: "GetAll", 21 | Router: `/`, 22 | AllowHTTPMethods: []string{"get"}, 23 | MethodParams: param.Make(), 24 | Params: nil}) 25 | 26 | beego.GlobalControllerRouter["12306/controllers:ObjectController"] = append(beego.GlobalControllerRouter["12306/controllers:ObjectController"], 27 | beego.ControllerComments{ 28 | Method: "Get", 29 | Router: `/:objectId`, 30 | AllowHTTPMethods: []string{"get"}, 31 | MethodParams: param.Make(), 32 | Params: nil}) 33 | 34 | beego.GlobalControllerRouter["12306/controllers:ObjectController"] = append(beego.GlobalControllerRouter["12306/controllers:ObjectController"], 35 | beego.ControllerComments{ 36 | Method: "Put", 37 | Router: `/:objectId`, 38 | AllowHTTPMethods: []string{"put"}, 39 | MethodParams: param.Make(), 40 | Params: nil}) 41 | 42 | beego.GlobalControllerRouter["12306/controllers:ObjectController"] = append(beego.GlobalControllerRouter["12306/controllers:ObjectController"], 43 | beego.ControllerComments{ 44 | Method: "Delete", 45 | Router: `/:objectId`, 46 | AllowHTTPMethods: []string{"delete"}, 47 | MethodParams: param.Make(), 48 | Params: nil}) 49 | 50 | beego.GlobalControllerRouter["12306/controllers:UserController"] = append(beego.GlobalControllerRouter["12306/controllers:UserController"], 51 | beego.ControllerComments{ 52 | Method: "Post", 53 | Router: `/`, 54 | AllowHTTPMethods: []string{"post"}, 55 | MethodParams: param.Make(), 56 | Params: nil}) 57 | 58 | beego.GlobalControllerRouter["12306/controllers:UserController"] = append(beego.GlobalControllerRouter["12306/controllers:UserController"], 59 | beego.ControllerComments{ 60 | Method: "GetAll", 61 | Router: `/`, 62 | AllowHTTPMethods: []string{"get"}, 63 | MethodParams: param.Make(), 64 | Params: nil}) 65 | 66 | beego.GlobalControllerRouter["12306/controllers:UserController"] = append(beego.GlobalControllerRouter["12306/controllers:UserController"], 67 | beego.ControllerComments{ 68 | Method: "Get", 69 | Router: `/:uid`, 70 | AllowHTTPMethods: []string{"get"}, 71 | MethodParams: param.Make(), 72 | Params: nil}) 73 | 74 | beego.GlobalControllerRouter["12306/controllers:UserController"] = append(beego.GlobalControllerRouter["12306/controllers:UserController"], 75 | beego.ControllerComments{ 76 | Method: "Put", 77 | Router: `/:uid`, 78 | AllowHTTPMethods: []string{"put"}, 79 | MethodParams: param.Make(), 80 | Params: nil}) 81 | 82 | beego.GlobalControllerRouter["12306/controllers:UserController"] = append(beego.GlobalControllerRouter["12306/controllers:UserController"], 83 | beego.ControllerComments{ 84 | Method: "Delete", 85 | Router: `/:uid`, 86 | AllowHTTPMethods: []string{"delete"}, 87 | MethodParams: param.Make(), 88 | Params: nil}) 89 | 90 | beego.GlobalControllerRouter["12306/controllers:UserController"] = append(beego.GlobalControllerRouter["12306/controllers:UserController"], 91 | beego.ControllerComments{ 92 | Method: "Logout", 93 | Router: `/logout`, 94 | AllowHTTPMethods: []string{"get"}, 95 | MethodParams: param.Make(), 96 | Params: nil}) 97 | 98 | } 99 | -------------------------------------------------------------------------------- /routers/commentsRouter_controllers.go: -------------------------------------------------------------------------------- 1 | package routers 2 | 3 | import ( 4 | "github.com/astaxie/beego" 5 | "github.com/astaxie/beego/context/param" 6 | ) 7 | 8 | func init() { 9 | 10 | beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:PassengerController"] = append(beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:PassengerController"], 11 | beego.ControllerComments{ 12 | Method: "Get", 13 | Router: `/`, 14 | AllowHTTPMethods: []string{"get"}, 15 | MethodParams: param.Make(), 16 | Params: nil}) 17 | 18 | beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:ScheduleController"] = append(beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:ScheduleController"], 19 | beego.ControllerComments{ 20 | Method: "Get", 21 | Router: `/`, 22 | AllowHTTPMethods: []string{"get"}, 23 | MethodParams: param.Make(), 24 | Params: nil}) 25 | 26 | beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:ScheduleController"] = append(beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:ScheduleController"], 27 | beego.ControllerComments{ 28 | Method: "InitQuery", 29 | Router: `/init`, 30 | AllowHTTPMethods: []string{"get"}, 31 | MethodParams: param.Make(), 32 | Params: nil}) 33 | 34 | beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:StationController"] = append(beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:StationController"], 35 | beego.ControllerComments{ 36 | Method: "Get", 37 | Router: `/`, 38 | AllowHTTPMethods: []string{"get"}, 39 | MethodParams: param.Make(), 40 | Params: nil}) 41 | 42 | beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:TaskController"] = append(beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:TaskController"], 43 | beego.ControllerComments{ 44 | Method: "Post", 45 | Router: `/`, 46 | AllowHTTPMethods: []string{"post"}, 47 | MethodParams: param.Make(), 48 | Params: nil}) 49 | 50 | beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:TaskController"] = append(beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:TaskController"], 51 | beego.ControllerComments{ 52 | Method: "Get", 53 | Router: `/`, 54 | AllowHTTPMethods: []string{"get"}, 55 | MethodParams: param.Make(), 56 | Params: nil}) 57 | 58 | beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:TaskController"] = append(beego.GlobalControllerRouter["github.com/chuanshuo843/12306_server/controllers:TaskController"], 59 | beego.ControllerComments{ 60 | Method: "Log", 61 | Router: `/log/:id:int`, 62 | AllowHTTPMethods: []string{"get"}, 63 | MethodParams: param.Make(), 64 | Params: nil}) 65 | 66 | } 67 | -------------------------------------------------------------------------------- /routers/router.go: -------------------------------------------------------------------------------- 1 | // @APIVersion 1.0.0 2 | // @Title beego Test API 3 | // @Description beego has a very cool tools to autogenerate documents for your API 4 | // @Contact astaxie@gmail.com 5 | // @TermsOfServiceUrl http://beego.me/ 6 | // @License Apache 2.0 7 | // @LicenseUrl http://www.apache.org/licenses/LICENSE-2.0.html 8 | package routers 9 | 10 | import ( 11 | "net/http" 12 | "strings" 13 | 14 | "github.com/astaxie/beego" 15 | "github.com/astaxie/beego/context" 16 | "github.com/chuanshuo843/12306_server/controllers" 17 | "github.com/chuanshuo843/12306_server/utils" 18 | ) 19 | 20 | func init() { 21 | ns := beego.NewNamespace("/v1", 22 | //登录 23 | beego.NSRouter("/auth/login", &controllers.UserController{}, "Post:Login"), 24 | beego.NSRouter("/auth/verifyCode", &controllers.UserController{}, "Get:VerifyCode"), 25 | beego.NSRouter("/auth/init", &controllers.UserController{}, "Get:InitLogin"), 26 | //车次处理 27 | beego.NSNamespace("/schedule", 28 | beego.NSBefore(Auth), 29 | beego.NSInclude( 30 | &controllers.ScheduleController{}, 31 | ), 32 | ), 33 | //站台处理 34 | beego.NSNamespace("/station", 35 | beego.NSBefore(Auth), 36 | beego.NSInclude( 37 | &controllers.StationController{}, 38 | ), 39 | ), 40 | //乘客信息 41 | beego.NSNamespace("/passenger", 42 | beego.NSBefore(Auth), 43 | beego.NSInclude( 44 | &controllers.PassengerController{}, 45 | ), 46 | ), 47 | //任务管理 48 | beego.NSNamespace("/task", 49 | beego.NSBefore(Auth), 50 | beego.NSInclude( 51 | &controllers.TaskController{}, 52 | ), 53 | ), 54 | ) 55 | beego.AddNamespace(ns) 56 | } 57 | 58 | func Auth(ctx *context.Context) { 59 | //只检测OPTIONS以外的请求 60 | if !ctx.Input.Is("OPTIONS") { 61 | authString := ctx.Input.Header("Authorization") 62 | if authString == "" { 63 | AllowCross(ctx) 64 | return 65 | } 66 | kv := strings.Split(authString, " ") 67 | if len(kv) != 2 || kv[0] != "Bearer" { 68 | AllowCross(ctx) 69 | return 70 | } 71 | token := kv[1] 72 | jwt := utils.InitJwt() 73 | if !jwt.Checkd(token) { 74 | AllowCross(ctx) 75 | return 76 | } 77 | } 78 | } 79 | 80 | //错误返回 81 | func AllowCross(ctx *context.Context) { 82 | ctx.Output.Header("Cache-Control", "no-store") 83 | ctx.Output.Header("Access-Control-Allow-Origin", "*") 84 | ctx.Output.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE,OPTIONS") 85 | ctx.Output.Header("Access-Control-Allow-Headers", "Authorization") 86 | ctx.Output.Header("WWW-Authenticate", `Bearer realm="`+beego.AppConfig.String("HostName")+`" error="Authorization" error_description="invalid Authorization"`) 87 | http.Error(ctx.ResponseWriter, "Unauthorized", 401) 88 | } 89 | -------------------------------------------------------------------------------- /tests/default_test.go: -------------------------------------------------------------------------------- 1 | package test 2 | 3 | import ( 4 | "net/http" 5 | "net/http/httptest" 6 | "testing" 7 | "runtime" 8 | "path/filepath" 9 | _ "github.com/chuanshuo843/12306_server/routers" 10 | 11 | "github.com/astaxie/beego" 12 | . "github.com/smartystreets/goconvey/convey" 13 | ) 14 | 15 | func init() { 16 | _, file, _, _ := runtime.Caller(1) 17 | apppath, _ := filepath.Abs(filepath.Dir(filepath.Join(file, ".." + string(filepath.Separator)))) 18 | beego.TestBeegoInit(apppath) 19 | } 20 | 21 | // TestGet is a sample to run an endpoint test 22 | func TestGet(t *testing.T) { 23 | r, _ := http.NewRequest("GET", "/v1/object", nil) 24 | w := httptest.NewRecorder() 25 | beego.BeeApp.Handlers.ServeHTTP(w, r) 26 | 27 | beego.Trace("testing", "TestGet", "Code[%d]\n%s", w.Code, w.Body.String()) 28 | 29 | Convey("Subject: Test Station Endpoint\n", t, func() { 30 | Convey("Status Code Should Be 200", func() { 31 | So(w.Code, ShouldEqual, 200) 32 | }) 33 | Convey("The Result Should Not Be Empty", func() { 34 | So(w.Body.Len(), ShouldBeGreaterThan, 0) 35 | }) 36 | }) 37 | } 38 | 39 | -------------------------------------------------------------------------------- /utils/jwt.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/hmac" 5 | "crypto/sha256" 6 | "encoding/base64" 7 | "encoding/json" 8 | "strings" 9 | "time" 10 | ) 11 | 12 | type JwtHeader struct { 13 | JwtHead string `json:"type"` 14 | JwtAlg string `json:"alg"` 15 | } 16 | 17 | type JwtPayload struct { 18 | Iss string `json:"iss"` //Issuer,发行者 19 | Sub string `json:"sub"` //Subject,主题 20 | Aud string `json:"aud"` //Audience,观众 21 | Data string `json:"data"` //请求数据 22 | Exp int64 `json:"exp"` //Expiration time,过期时间 23 | Nbf int64 `json:"nbf"` //Not before 24 | Iat int64 `json:"iat"` //Issued at,发行时间 25 | Jti int64 `json:"jti"` //JWT ID 26 | } 27 | 28 | type JwtSecret struct { 29 | Key string `json:"key"` //秘钥 30 | } 31 | 32 | type Jwt struct { 33 | Header JwtHeader 34 | Payload JwtPayload 35 | Secret JwtSecret 36 | } 37 | 38 | //JWT 初始化 39 | func InitJwt() *Jwt{ 40 | jwt := &Jwt{} 41 | //设置Header 42 | jwt.Header.JwtAlg = "HS256" 43 | jwt.Header.JwtHead = "JWT" 44 | //设置Payload 45 | jwt.Payload.Iss = "https://www.ganktools.com" 46 | jwt.Payload.Sub = "https://www.ganktools.com" 47 | jwt.Payload.Aud = "https://www.ganktools.com" 48 | //设置加密秘钥 49 | jwt.Secret.Key = "jwt_key" 50 | return jwt 51 | } 52 | 53 | //编码JWT的Header头 54 | func (header *JwtHeader) Encode() string { 55 | json_data, _ := json.Marshal(header) 56 | return base64.StdEncoding.EncodeToString(json_data) 57 | } 58 | 59 | //解码JWT的Header头 60 | func (header *JwtHeader) Decode(data string) error { 61 | headerStr, err := base64.StdEncoding.DecodeString(data) 62 | if err != nil { 63 | return err 64 | } 65 | errJson := json.Unmarshal(headerStr, header) 66 | if errJson != nil { 67 | return errJson 68 | } 69 | return nil 70 | } 71 | 72 | //解码payload部分 73 | func (payload *JwtPayload) Decode(data string) error { 74 | payloadStr, err := base64.StdEncoding.DecodeString(data) 75 | if err != nil { 76 | return err 77 | } 78 | errJson := json.Unmarshal(payloadStr, payload) 79 | if errJson != nil { 80 | return errJson 81 | } 82 | return nil 83 | } 84 | 85 | //编码JWT的payload部分 86 | func (payload *JwtPayload) Encode() string { 87 | json_data, _ := json.Marshal(payload) 88 | return base64.StdEncoding.EncodeToString(json_data) 89 | } 90 | 91 | //JWT的secret部分加密 92 | func (secret *JwtSecret) Signature(header, payload string) string { 93 | encode_jwt := header + "." + payload 94 | h := hmac.New(sha256.New, []byte(secret.Key)) 95 | h.Write([]byte(encode_jwt)) 96 | return base64.StdEncoding.EncodeToString(h.Sum(nil)) 97 | } 98 | 99 | //JWT加密 100 | func (jwt *Jwt) Encode() string { 101 | headerStr := jwt.Header.Encode() 102 | payloadStr := jwt.Payload.Encode() 103 | return headerStr + "." + payloadStr + "." + jwt.Secret.Signature(headerStr, payloadStr) 104 | } 105 | 106 | //JWT解码 107 | func (jwt *Jwt) Decode(token string) error { 108 | data := strings.Split(token, ".") 109 | errHeader := jwt.Header.Decode(string(data[0])) 110 | if errHeader != nil { 111 | return errHeader 112 | } 113 | errPayload := jwt.Payload.Decode(string(data[1])) 114 | if errPayload != nil { 115 | return errPayload 116 | } 117 | return nil 118 | } 119 | 120 | //JWT检测 121 | func (jwt *Jwt) Checkd(token string) bool { 122 | data := strings.Split(token, ".") 123 | //检测长度 124 | if len(data) != 3 { 125 | return false 126 | } 127 | //解码Token 128 | errDeCode := jwt.Decode(token) 129 | if errDeCode != nil { 130 | return false 131 | } 132 | //检测Hash是否一致 133 | secret := jwt.Secret.Signature(string(data[0]), string(data[1])) 134 | if secret != string(data[2]) { 135 | return false 136 | } 137 | //检测JWT是否过期 138 | if jwt.Payload.Exp <= time.Now().Unix() { 139 | return false 140 | } 141 | //检测什么时间之后可用 142 | if jwt.Payload.Nbf >= time.Now().Unix() { 143 | return false 144 | } 145 | return true 146 | } 147 | -------------------------------------------------------------------------------- /utils/kyfw.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "math/rand" 10 | "net/http" 11 | "net/url" 12 | "regexp" 13 | "strings" 14 | "sync" 15 | "time" 16 | 17 | "github.com/astaxie/beego" 18 | ) 19 | 20 | const ( 21 | //用户登录Init 22 | UserLoginInit = "https://kyfw.12306.cn/otn/login/init" 23 | //检测用户是否登录 24 | UserAuthUAMTK = "https://kyfw.12306.cn/passport/web/auth/uamtk" 25 | //获取登录验证码 26 | UserGetVerifyImg = "https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&%g" 27 | //检测登录验证码 28 | UserCheckVerify = "https://kyfw.12306.cn/passport/captcha/captcha-check" 29 | //登录12306 30 | UserLogin12306 = "https://kyfw.12306.cn/passport/web/login" 31 | //获取登录信息 32 | UserGetToken = "https://kyfw.12306.cn/otn/uamauthclient" 33 | //提交订单 34 | OrderSubmitOrderURL = "https://kyfw.12306.cn/otn/leftTicket/submitOrderRequest" 35 | //初始化订单页面 36 | OrderInitOrderURL = "https://kyfw.12306.cn/otn/confirmPassenger/initDc" 37 | //检测订单 38 | OrderCheckedURL = "https://kyfw.12306.cn/otn/confirmPassenger/checkOrderInfo" 39 | //获取车票数和排队人数 40 | OrderGetCountURL = "https://kyfw.12306.cn/otn/confirmPassenger/getQueueCount" 41 | //加入购买队列 42 | OrderJoinBuyQueue = "https://kyfw.12306.cn/otn/confirmPassenger/confirmSingleForQueue" 43 | //获取取票码 44 | OrderGetTicketCode = "https://kyfw.12306.cn/otn/confirmPassenger/queryOrderWaitTime?random=1516252521719&tourFlag=dc&_json_att=&REPEAT_SUBMIT_TOKEN=%s" 45 | //获取站台信息 46 | QueryGetStation = "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9042" 47 | //查询车次初始化 48 | QueryScheduleInit = "https://kyfw.12306.cn/otn/leftTicket/init" 49 | //车次查询日志 50 | QueryScheduleLog = "https://kyfw.12306.cn/otn/leftTicket/log?leftTicketDTO.train_date=%s&leftTicketDTO.from_station=%s&leftTicketDTO.to_station=%s&purpose_codes=ADULT" 51 | //查询车次信息 52 | QuerySchedule = "https://kyfw.12306.cn/otn/%s?leftTicketDTO.train_date=%s&leftTicketDTO.from_station=%s&leftTicketDTO.to_station=%s&purpose_codes=ADULT" 53 | //查询乘客信息 54 | QueryPassenger = "https://kyfw.12306.cn/otn/confirmPassenger/getPassengerDTOs" 55 | ) 56 | 57 | type Kyfw struct { 58 | Request *Request 59 | IsLogin bool 60 | LoginName string 61 | LoginToken string 62 | QueryScheduleURL string 63 | OrderSubmitToken string 64 | OrderKeyCheckIsChange string 65 | OrderLeftTicketStr string 66 | OrderTrainLocation string 67 | OrderTicketCode string 68 | LoginNumber int 69 | CheckLoginNumber int 70 | QueryNumber int 71 | OrderSubmitNumber int 72 | OrderCheckdNumber int 73 | OrderGetCountNumber int 74 | OrderJoinBuyNumber int 75 | OrderQueryWaitNumber int 76 | MaxLoopNumber int 77 | } 78 | 79 | type KyfwList struct { 80 | UserKyfw *sync.Map 81 | } 82 | 83 | func InitKyfwList() *KyfwList { 84 | return &KyfwList{ 85 | UserKyfw: &sync.Map{}, 86 | } 87 | } 88 | 89 | func (kyfws *KyfwList) Create(key string) *Kyfw { 90 | kyf := &Kyfw{ 91 | Request: InitRequest(), 92 | MaxLoopNumber: 10, 93 | IsLogin: false, 94 | } 95 | kyfws.Set(key, kyf) 96 | return kyf 97 | } 98 | 99 | func (kyfws *KyfwList) Set(key string, ky *Kyfw) { 100 | kyfws.UserKyfw.Store(kyfws.GetKeyHash(key), ky) 101 | } 102 | 103 | func (kyfws *KyfwList) Get(key string) *Kyfw { 104 | v, ok := kyfws.UserKyfw.Load(kyfws.GetKeyHash(key)) 105 | if !ok { 106 | return nil 107 | } 108 | return v.(*Kyfw) 109 | } 110 | 111 | func (kyfws *KyfwList) GetKeyHash(key string) string { 112 | hasher := md5.New() 113 | hasher.Write([]byte(key)) 114 | return hex.EncodeToString(hasher.Sum(nil)) 115 | } 116 | 117 | func (kyfws *KyfwList) Move(forKey, toKey string) error { 118 | kyfws.Set(toKey, kyfws.Get(forKey)) 119 | kyfws.Delete(forKey) 120 | return nil 121 | } 122 | 123 | func (kyfws *KyfwList) Foreach() { 124 | kyfws.UserKyfw.Range(func(k, v interface{}) bool { 125 | fmt.Println(k.(string), v) 126 | return true 127 | }) 128 | } 129 | 130 | func (kyfws *KyfwList) Delete(key string) { 131 | kyfws.UserKyfw.Delete(kyfws.GetKeyHash(key)) 132 | } 133 | 134 | //登录页面初始化 135 | func (kyfw *Kyfw) InitLogin() ([]byte, error) { 136 | err := kyfw.Request.CreateHttpRequest(UserLoginInit, "GET", nil) 137 | if err != nil { 138 | return nil, err 139 | } 140 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/index/init") 141 | return kyfw.Request.Send() 142 | } 143 | 144 | //获取验证码 145 | func (kyfw *Kyfw) GetVerifyImages() ([]byte, error) { 146 | err := kyfw.Request.CreateHttpRequest(fmt.Sprintf(UserGetVerifyImg, rand.Float64()), "GET", nil) 147 | if err != nil { 148 | return nil, err 149 | } 150 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/login/init") 151 | return kyfw.Request.Send() 152 | } 153 | 154 | //获取12306登录token 155 | func (kyfw *Kyfw) Get12306Token(appToken string) error { 156 | err := kyfw.Request.CreateHttpRequest(UserGetToken, "POST", &url.Values{"tk": {appToken}}) 157 | if err != nil { 158 | return err 159 | } 160 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/passport?redirect=/otn/login/userLogin") 161 | data, errSend := kyfw.Request.Send() 162 | beego.Debug("Get12306Token:", string(data)) 163 | if errSend != nil { 164 | return errSend 165 | } 166 | //解析返回数据 167 | var tokenRes map[string]interface{} 168 | errJson := json.Unmarshal(data, &tokenRes) 169 | if errJson != nil { 170 | return errJson 171 | } 172 | //检测操作是否成功 173 | if tokenRes["result_code"].(float64) != 0 { 174 | kyfw.IsLogin = false 175 | return errors.New(tokenRes["result_message"].(string)) 176 | } 177 | kyfw.LoginName = tokenRes["username"].(string) 178 | kyfw.LoginToken = tokenRes["apptk"].(string) 179 | kyfw.IsLogin = true 180 | return nil 181 | } 182 | 183 | //检测用户是否登录 184 | func (kyfw *Kyfw) CheckIsLogin() (string, error) { 185 | err := kyfw.Request.CreateHttpRequest(UserAuthUAMTK, "POST", &url.Values{"appid": {"otn"}}) 186 | if err != nil { 187 | return "", err 188 | } 189 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/login/init") 190 | data, sendErr := kyfw.Request.Send() 191 | beego.Debug(string(data)) 192 | if sendErr != nil { 193 | return "", sendErr 194 | } 195 | //没有取到数据时递归调用直到取到数据为止 196 | if len(data) == 0 { 197 | if kyfw.CheckLoginNumber <= kyfw.MaxLoopNumber { 198 | return kyfw.CheckIsLogin() 199 | } 200 | return "", errors.New("获取检测登录数据失败") 201 | } 202 | kyfw.CheckLoginNumber = 0 203 | //解析返回数据 204 | var checkRes map[string]interface{} 205 | errJosn := json.Unmarshal(data, &checkRes) 206 | if errJosn != nil { 207 | return "", errJosn 208 | } 209 | // {"result_message":"验证通过","result_code":0,"apptk":null,"newapptk":"P5e8H_FPPq-Q6kfa9uUsKC0PUdOyqGtE6OSTPKvol9Qhuc1c0"} 210 | if checkRes["result_code"].(float64) != 0 { 211 | kyfw.IsLogin = false 212 | return "", errors.New(checkRes["result_message"].(string)) 213 | } 214 | kyfw.IsLogin = true 215 | return checkRes["newapptk"].(string), nil 216 | } 217 | 218 | //用户登录 219 | func (kyfw *Kyfw) Login(username, password, verify string) error { 220 | //检测验证码 221 | errVer := kyfw.CheckVerifyCode(verify) 222 | if errVer != nil { 223 | return errVer 224 | } 225 | //登录12306 226 | errLogin := kyfw.Login12306(username, password) 227 | if errLogin != nil { 228 | return errLogin 229 | } 230 | //检测用户是否登录 231 | appTk, errCheck := kyfw.CheckIsLogin() 232 | if errCheck != nil { 233 | return errCheck 234 | } 235 | //获取用户Token 236 | errTk := kyfw.Get12306Token(appTk) 237 | if errTk != nil { 238 | return errTk 239 | } 240 | return nil 241 | } 242 | 243 | //登录12306 244 | func (kyfw *Kyfw) Login12306(username, password string) error { 245 | kyfw.LoginNumber++ 246 | err := kyfw.Request.CreateHttpRequest(UserLogin12306, "POST", &url.Values{"username": {username}, "password": {password}, "appid": {"otn"}}) 247 | if err != nil { 248 | return err 249 | } 250 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/login/init") 251 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 252 | data, errSend := kyfw.Request.Send() 253 | if errSend != nil { 254 | return errSend 255 | } 256 | //没有取到数据时递归调用直到取到数据为止 257 | if len(data) == 0 { 258 | if kyfw.LoginNumber <= kyfw.MaxLoopNumber { 259 | return kyfw.Login12306(username, password) 260 | } 261 | return errors.New("获取登录数据失败") 262 | } 263 | kyfw.LoginNumber = 0 264 | var loginRes map[string]interface{} 265 | errJson := json.Unmarshal(data, &loginRes) 266 | if errJson != nil { 267 | return errJson 268 | } 269 | //{"result_message":"登录成功","result_code":0,"uamtk":"tnRPMlCjrDGm3k5IbzlRKQrbmnKToZC_8WN4ePn32Mkhuc1c0"} 270 | if loginRes["result_code"].(float64) != 0 { 271 | return errors.New(loginRes["result_message"].(string)) 272 | } 273 | return nil 274 | } 275 | 276 | //检测验证码 277 | func (kyfw *Kyfw) CheckVerifyCode(verifyCode string) error { 278 | err := kyfw.Request.CreateHttpRequest(UserCheckVerify, "POST", &url.Values{"answer": {verifyCode}, "login_site": {"E"}, "rand": {"sjrand"}}) 279 | if err != nil { 280 | return err 281 | } 282 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/login/init") 283 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 284 | data, errSend := kyfw.Request.Send() 285 | if errSend != nil { 286 | return errSend 287 | } 288 | var verRes map[string]interface{} 289 | errJson := json.Unmarshal(data, &verRes) 290 | if errJson != nil { 291 | return errJson 292 | } 293 | //{"result_message":"验证码校验成功","result_code":"4"} 294 | if verRes["result_code"].(string) != "4" { 295 | return errors.New(verRes["result_message"].(string)) 296 | } 297 | return nil 298 | } 299 | 300 | //获取站台信息 -------------------------------------------------------------------------------------Query 301 | func (kyfw *Kyfw) GetStations() (string, error) { 302 | err := kyfw.Request.CreateHttpRequest(QueryGetStation, "GET", nil) 303 | if err != nil { 304 | return "", err 305 | } 306 | data, errSend := kyfw.Request.Send() 307 | if errSend != nil { 308 | return "", errSend 309 | } 310 | stationList := strings.Split(string(data), "'") 311 | if len(stationList) != 3 { 312 | return "", errors.New("获取站台信息失败") 313 | } 314 | return string(stationList[1]), nil 315 | } 316 | 317 | //查询车次信息Init 318 | func (kyfw *Kyfw) InitQuerySchedule() error { 319 | //获取查询地址 320 | err := kyfw.Request.CreateHttpRequest(QueryScheduleInit, "GET", nil) 321 | if err != nil { 322 | return err 323 | } 324 | initData, _ := kyfw.Request.Send() 325 | splData := strings.Split(string(initData), "\n") 326 | kyfw.QueryScheduleURL = string([]byte(splData[13])[23:40]) 327 | return nil 328 | } 329 | 330 | //添加车次查询日志 331 | func (kyfw *Kyfw) AddQueryScheduleLog(startStation, endStation, startCode, endCode, date string) error { 332 | //设置Cookie 333 | cookies := []*http.Cookie{ 334 | &http.Cookie{Name: "_jc_save_fromDate", Value: date}, 335 | &http.Cookie{Name: "_jc_save_fromStation", Value: url.QueryEscape(startStation + "," + startCode)}, 336 | &http.Cookie{Name: "_jc_save_toDate", Value: date}, 337 | &http.Cookie{Name: "_jc_save_toStation", Value: url.QueryEscape(endStation + "," + endCode)}, 338 | &http.Cookie{Name: "_jc_save_wfdc_flag", Value: "dc"}, 339 | &http.Cookie{Name: "current_captcha_type", Value: "Z"}, 340 | &http.Cookie{Name: "RAIL_DEVICEID", Value: "P-3rV0RG9eaSEhKKfLTH7F5AAyUaGB84osHAAUosnqYAJA-izUyJkRbfB0Cw-UwpQjI_pmxe3quoWQz3tWxrwVOUPA0RBhiYKCqt4hc028fOVEOlM9NUScapq2xqoMQgLFrUmVJ5-Z1f_GVWrSS9MUdfrkvnvqgR"}, 341 | &http.Cookie{Name: "RAIL_EXPIRATION", Value: "1516096637926"}, 342 | } 343 | kyfw.Request.CreateHttpRequest(fmt.Sprintf(QueryScheduleLog, date, startCode, endCode), "GET", nil) 344 | kyfw.Request.SetCookie(cookies) 345 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 346 | _, err := kyfw.Request.Send() 347 | if err != nil { 348 | return errors.New("车次查询日志添加失败") 349 | } 350 | return nil 351 | } 352 | 353 | func (kyfw *Kyfw) QueryScheduleInfo(date, startCode, endCode string) (string, error) { 354 | kyfw.QueryNumber++ 355 | //查询车次信息 356 | errQ := kyfw.Request.CreateHttpRequest(fmt.Sprintf(QuerySchedule, kyfw.QueryScheduleURL, date, startCode, endCode), "GET", nil) 357 | if errQ != nil { 358 | return "", errQ 359 | } 360 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 361 | data, errQuery := kyfw.Request.Send() 362 | if errQuery != nil { 363 | return "", errQuery 364 | } 365 | //没有取到数据时递归调用直到取到数据为止 366 | if len(data) == 0 { 367 | if kyfw.QueryNumber <= kyfw.MaxLoopNumber { 368 | return kyfw.QueryScheduleInfo(date, startCode, endCode) 369 | } 370 | return "", errors.New("获取车次信息失败") 371 | } 372 | beego.Info("QuerySchedule:", string(data)) 373 | kyfw.QueryNumber = 0 374 | return string(data), nil 375 | } 376 | 377 | //查询车次信息 378 | func (kyfw *Kyfw) GetSchedule(startStation, endStation, startCode, endCode, date string) (string, error) { 379 | //检测查询URL 380 | if kyfw.QueryScheduleURL == "" { 381 | errQueryURL := kyfw.InitQuerySchedule() 382 | if errQueryURL != nil { 383 | return "", errQueryURL 384 | } 385 | } 386 | //添加查询日志 387 | errLog := kyfw.AddQueryScheduleLog(startStation, endStation, startCode, endCode, date) 388 | if errLog != nil { 389 | return "", errLog 390 | } 391 | //查询车次信息 392 | return kyfw.QueryScheduleInfo(date, startCode, endCode) 393 | } 394 | 395 | //查询乘客信息 396 | func (kyfw *Kyfw) GetPassenger() ([]byte, error) { 397 | kyfw.Request.CreateHttpRequest(QueryPassenger, "GET", nil) 398 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 399 | data, err := kyfw.Request.Send() 400 | if err != nil { 401 | return nil, err 402 | } 403 | beego.Info("GetPassenger:", string(data)) 404 | //解析返回信息 405 | var passRes map[string]interface{} 406 | errJson := json.Unmarshal(data, &passRes) 407 | if errJson != nil { 408 | return nil, errJson 409 | } 410 | if passRes["status"].(bool) != true { 411 | return nil, errors.New(passRes["message"].(string)) 412 | } 413 | passData := passRes["data"].(map[string]interface{}) 414 | if passData["isExist"].(bool) != true { 415 | kyfw.IsLogin = false 416 | return nil, errors.New(passData["exMsg"].(string)) 417 | } 418 | return data, nil 419 | } 420 | 421 | func (kyfw *Kyfw) GetSeatPrice(trainNo, startNo, endNo, seatType, date string) { 422 | //https://kyfw.12306.cn/otn/leftTicket/queryTicketPrice?train_no=5l0000G13061&from_station_no=01&to_station_no=12&seat_types=O9M&train_date=2017-02-05`] 423 | } 424 | 425 | //下单 ---------------------------------------------------------------------Order 426 | func (kyfw *Kyfw) PlaceAnOrder(secret, trainNo, trainCode, start, startCode, end, endCode, date, formatDate, ticketStr, passengerStr string) error { 427 | //提交订单 428 | errSub := kyfw.SubmitOrder(secret, start, end, date) 429 | if errSub != nil { 430 | return errSub 431 | } 432 | //初始化订单确认页面 433 | errInit := kyfw.InitConfirmOrder() 434 | if errInit != nil { 435 | return errInit 436 | } 437 | //检测订单 438 | _, errCheckd := kyfw.CheckConfirmOrder(ticketStr, passengerStr) 439 | if errCheckd != nil { 440 | return errCheckd 441 | } 442 | ticketArr := strings.Split(ticketStr, ",") 443 | //获取排队信息 444 | errQueue := kyfw.GetOrderTicketQueueInfo(formatDate, trainNo, trainCode, ticketArr[0], startCode, endCode) 445 | if errQueue != nil { 446 | return errQueue 447 | } 448 | //加入购买队列 449 | errJoin := kyfw.JoinBuyTicketQueue(ticketStr, passengerStr) 450 | if errJoin != nil { 451 | return errJoin 452 | } 453 | //获取取票码 454 | errTicket := kyfw.GetTicketCode() 455 | if errTicket != nil { 456 | return errTicket 457 | } 458 | return nil 459 | } 460 | 461 | //提交订单 462 | func (kyfw *Kyfw) SubmitOrder(secret, start, end, date string) error { 463 | kyfw.OrderSubmitNumber++ 464 | params := fmt.Sprintf("secretStr=%s&train_date=%s&back_train_date=%s&tour_flag=dc&purpose_codes=ADULT&"+ 465 | "query_from_station_name=%s&query_to_station_name=%s&undefined=", secret, date, 466 | date, start, end) 467 | err := kyfw.Request.CreateHttpRequest(OrderSubmitOrderURL, "POST", params) 468 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/leftTicket/init") 469 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 470 | if err != nil { 471 | return err 472 | } 473 | data, errSend := kyfw.Request.Send() 474 | if errSend != nil { 475 | return err 476 | } 477 | //没有取到数据时递归调用直到取到数据为止 478 | if len(data) == 0 { 479 | if kyfw.OrderSubmitNumber <= kyfw.MaxLoopNumber { 480 | return kyfw.SubmitOrder(secret, start, end, date) 481 | } 482 | return errors.New("提交订单失败") 483 | } 484 | beego.Info("SubmitOrder:", string(data)) 485 | kyfw.OrderSubmitNumber = 0 486 | var subRes map[string]interface{} 487 | errJson := json.Unmarshal(data, &subRes) 488 | if errJson != nil { 489 | return errJson 490 | } 491 | if subRes["status"].(bool) != true { 492 | msg := subRes["messages"].([]interface{}) 493 | return errors.New(string(msg[0].(string))) 494 | } 495 | return nil 496 | } 497 | 498 | //初始化确认订单页面 499 | func (kyfw *Kyfw) InitConfirmOrder() error { 500 | err := kyfw.Request.CreateHttpRequest(OrderInitOrderURL, "POST", &url.Values{"_json_att": {""}}) 501 | if err != nil { 502 | return err 503 | } 504 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/leftTicket/init") 505 | data, errSend := kyfw.Request.Send() 506 | if errSend != nil { 507 | return errSend 508 | } 509 | //获取KeyCheck 510 | var keyCheckRegexp = regexp.MustCompile(`'key_check_isChange':'[\S]+','left`) 511 | keyCacheSource := keyCheckRegexp.FindAllStringSubmatch(string(data), -1) 512 | if len(keyCacheSource) == 0 { 513 | return errors.New("获取key_check_isChange失败") 514 | } 515 | if len(keyCacheSource[0]) == 0 { 516 | return errors.New("获取key_check_isChange失败") 517 | } 518 | keyCacheStr := strings.Split(keyCacheSource[0][0], `'`)[3] 519 | kyfw.OrderKeyCheckIsChange = keyCacheStr 520 | //获取leftTicketStr 521 | var ticketRegexp = regexp.MustCompile(`'leftTicketStr':'[\S]+','limit`) 522 | ticketSource := ticketRegexp.FindAllStringSubmatch(string(data), -1) 523 | if len(ticketSource) == 0 { 524 | return errors.New("获取leftTicketStr失败") 525 | } 526 | if len(ticketSource[0]) == 0 { 527 | return errors.New("获取leftTicketStr失败") 528 | } 529 | ticketStr := strings.Split(ticketSource[0][0], `'`)[3] 530 | kyfw.OrderLeftTicketStr = ticketStr 531 | //获取trian_location 532 | var locationRegexp = regexp.MustCompile(`'train_location':'[\S]+'};`) 533 | locationSource := locationRegexp.FindAllStringSubmatch(string(data), -1) 534 | if len(locationSource) == 0 { 535 | return errors.New("获取trian_location失败") 536 | } 537 | if len(locationSource[0]) == 0 { 538 | return errors.New("获取trian_location失败") 539 | } 540 | locationStr := strings.Split(locationSource[0][0], `'`)[3] 541 | kyfw.OrderTrainLocation = locationStr 542 | //获取submitToken 543 | splData := strings.Split(string(data), "\n") 544 | if len(splData) > 64 { 545 | kyfw.OrderSubmitToken = string([]byte(splData[11])[32:64]) 546 | return nil 547 | } else { 548 | return errors.New("获取submitToken失败") 549 | } 550 | return nil 551 | } 552 | 553 | //检测订单 554 | func (kyfw *Kyfw) CheckConfirmOrder(ticketStr, passengerStr string) ([]byte, error) { 555 | kyfw.OrderCheckdNumber++ 556 | params := fmt.Sprintf("cancel_flag=2&bed_level_order_num=000000000000000000000000000000&passengerTicketStr=%s&oldPassengerStr=%s"+ 557 | "&tour_flag=dc&randCode=&_json_att=&REPEAT_SUBMIT_TOKEN=%s", ticketStr, passengerStr, kyfw.OrderSubmitToken) 558 | err := kyfw.Request.CreateHttpRequest(OrderCheckedURL, "POST", params) 559 | if err != nil { 560 | return nil, err 561 | } 562 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/confirmPassenger/initDc") 563 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 564 | data, errSend := kyfw.Request.Send() 565 | if errSend != nil { 566 | return nil, errSend 567 | } 568 | //没有取到数据时递归调用直到取到数据为止 569 | if len(data) == 0 { 570 | if kyfw.OrderCheckdNumber <= kyfw.MaxLoopNumber { 571 | return kyfw.CheckConfirmOrder(ticketStr, passengerStr) 572 | } 573 | return nil, errors.New("获取检测订单数据失败") 574 | } 575 | beego.Info("CheckOrder:", string(data)) 576 | kyfw.OrderCheckdNumber = 0 577 | var checkdRes map[string]interface{} 578 | errJson := json.Unmarshal(data, &checkdRes) 579 | if errJson != nil { 580 | return nil, errJson 581 | } 582 | if checkdRes["status"].(bool) != true { 583 | msg := checkdRes["messages"].([]interface{}) 584 | return nil, errors.New(msg[0].(string)) 585 | } 586 | checkdSubmit := checkdRes["data"].(map[string]interface{}) 587 | if checkdSubmit["submitStatus"].(bool) != true { 588 | return nil, errors.New(checkdSubmit["errMsg"].(string)) 589 | } 590 | return data, nil 591 | } 592 | 593 | //获取排队人数和Ticket信息 594 | func (kyfw *Kyfw) GetOrderTicketQueueInfo(trainDate, trainNo, trainCode, seatType, startCode, endCode string) error { 595 | kyfw.OrderGetCountNumber++ 596 | params := fmt.Sprintf("train_date=%s&train_no=%s&stationTrainCode=%s&seatType=%s&fromStationTelecode=%s"+ 597 | "&toStationTelecode=%s&leftTicket=%s&purpose_codes=00&train_location=%s&_json_att=&REPEAT_SUBMIT_TOKEN=%s", 598 | url.QueryEscape(trainDate), trainNo, trainCode, seatType, startCode, endCode, kyfw.OrderLeftTicketStr, kyfw.OrderTrainLocation, kyfw.OrderSubmitToken) 599 | //train_date=Thu+Jan+18+2018+00%3A00%3A00+GMT%2B0800+(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)&train_no=240000K4110T&stationTrainCode=K411&seatType=3&fromStationTelecode=BJP&toStationTelecode=TXP&leftTicket=%252BG1447KdMTkeq4e7Llen0%252BmM502nk7jzf4Re3fL57omLNhBAoYLby8tmSP8%253D&purpose_codes=00&train_location=PA&_json_att=&REPEAT_SUBMIT_TOKEN=f1bea73b1e94d07fab28b4fb63f12616 600 | err := kyfw.Request.CreateHttpRequest(OrderGetCountURL, "POST", params) 601 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/confirmPassenger/initDc") 602 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 603 | if err != nil { 604 | return err 605 | } 606 | data, errSend := kyfw.Request.Send() 607 | if errSend != nil { 608 | return errSend 609 | } 610 | //没有取到数据时递归调用直到取到数据为止 611 | if len(data) == 0 { 612 | if kyfw.OrderGetCountNumber <= kyfw.MaxLoopNumber { 613 | time.Sleep(1 * time.Second) 614 | return kyfw.GetOrderTicketQueueInfo(trainDate, trainNo, trainCode, seatType, startCode, endCode) 615 | } 616 | return errors.New("获取排队人数信息失败") 617 | } 618 | beego.Info("QueueCount:", string(data)) 619 | kyfw.OrderGetCountNumber = 0 620 | var queRes map[string]interface{} 621 | errJson := json.Unmarshal(data, &queRes) 622 | if errJson != nil { 623 | return errJson 624 | } 625 | if queRes["status"].(bool) != true { 626 | msg := queRes["messages"].([]interface{}) 627 | return errors.New(msg[0].(string)) 628 | } 629 | return nil 630 | } 631 | 632 | //加入购买队列 633 | func (kyfw *Kyfw) JoinBuyTicketQueue(ticketStr, passengerStr string) error { 634 | kyfw.OrderJoinBuyNumber++ 635 | params := fmt.Sprintf("passengerTicketStr=%s&oldPassengerStr=%s&randCode=&purpose_codes=00&key_check_isChange=%s&leftTicketStr=%s&train_location=%s&choose_seats=&seatDetailType=000&whatsSelect=1&roomType=00&dwAll=N&_json_att=&REPEAT_SUBMIT_TOKEN=%s", ticketStr, passengerStr, kyfw.OrderKeyCheckIsChange, kyfw.OrderLeftTicketStr, kyfw.OrderTrainLocation, kyfw.OrderSubmitToken) 636 | //passengerTicketStr=%s&oldPassengerStr=%s&randCode=&purpose_codes=00&key_check_isChange=%s&leftTicketStr=%s&train_location=%s&choose_seats=&seatDetailType=000&whatsSelect=1&roomType=00&dwAll=N&_json_att=&REPEAT_SUBMIT_TOKEN=%s 637 | err := kyfw.Request.CreateHttpRequest(OrderJoinBuyQueue, "POST", params) 638 | if err != nil { 639 | return err 640 | } 641 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/confirmPassenger/initDc") 642 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 643 | data, errSend := kyfw.Request.Send() 644 | if errSend != nil { 645 | return errSend 646 | } 647 | //没有取到数据时递归调用直到取到数据为止 648 | if len(data) == 0 { 649 | if kyfw.OrderJoinBuyNumber <= kyfw.MaxLoopNumber { 650 | return kyfw.JoinBuyTicketQueue(ticketStr, passengerStr) 651 | } 652 | return errors.New("加入购买队列失败") 653 | } 654 | beego.Info("BuyQueue:", string(data)) 655 | kyfw.OrderJoinBuyNumber = 0 656 | var joinRes map[string]interface{} 657 | errJosn := json.Unmarshal(data, &joinRes) 658 | if errJosn != nil { 659 | return errJosn 660 | } 661 | if joinRes["status"].(bool) != true { 662 | return errors.New(joinRes["data"].(string)) 663 | } 664 | joinData := joinRes["data"].(map[string]interface{}) 665 | if joinData["submitStatus"].(bool) != true { 666 | return errors.New("submit false") 667 | //return nil,errors.New(joinData["errMsg"].(string)) 668 | } 669 | return nil 670 | } 671 | 672 | //获取取票码 673 | func (kyfw *Kyfw) GetTicketCode() error { 674 | kyfw.OrderQueryWaitNumber++ 675 | err := kyfw.Request.CreateHttpRequest(fmt.Sprintf(OrderGetTicketCode, kyfw.OrderSubmitToken), "GET", nil) 676 | if err != nil { 677 | return err 678 | } 679 | kyfw.Request.SetHeader("Referer", "https://kyfw.12306.cn/otn/confirmPassenger/initDc") 680 | kyfw.Request.SetHeader("X-Requested-With", "XMLHttpRequest") 681 | data, errSend := kyfw.Request.Send() 682 | if errSend != nil { 683 | return errSend 684 | } 685 | //没有取到数据时递归调用直到取到数据为止 686 | if len(data) == 0 { 687 | if kyfw.OrderQueryWaitNumber <= kyfw.MaxLoopNumber { 688 | return kyfw.GetTicketCode() 689 | } 690 | return errors.New("下单成功,获取取票码数据失败") 691 | } 692 | beego.Info("Wiat:", string(data)) 693 | kyfw.OrderQueryWaitNumber = 0 694 | var tickRes map[string]interface{} 695 | errJosn := json.Unmarshal(data, &tickRes) 696 | if errJosn != nil { 697 | return errJosn 698 | } 699 | if tickRes["status"].(bool) != true { 700 | msg := tickRes["messages"].([]interface{}) 701 | return errors.New(msg[0].(string)) 702 | } 703 | tickData := tickRes["data"].(map[string]interface{}) 704 | //检测waitTime是否小于0不小于0时递归调用 705 | if tickData["waitTime"].(float64) < 0 { 706 | switch tickData["orderId"].(type) { 707 | case nil: 708 | return errors.New(tickData["msg"].(string)) 709 | case string: 710 | kyfw.OrderTicketCode = tickData["orderId"].(string) 711 | return nil 712 | } 713 | } else { 714 | return kyfw.GetTicketCode() 715 | } 716 | return nil 717 | } 718 | -------------------------------------------------------------------------------- /utils/request.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "crypto/tls" 6 | "errors" 7 | "io" 8 | "io/ioutil" 9 | "net" 10 | "net/http" 11 | "net/http/cookiejar" 12 | "net/url" 13 | "strings" 14 | "time" 15 | 16 | "golang.org/x/net/publicsuffix" 17 | ) 18 | 19 | //Request 20 | type Request struct { 21 | HttpClient *http.Client 22 | HttpRequest *http.Request 23 | HttpResponse *http.Response 24 | DisableDefaultHeader bool 25 | } 26 | 27 | //初始化Request 28 | func InitRequest() *Request { 29 | req := &Request{ 30 | HttpClient: &http.Client{}, 31 | HttpRequest: &http.Request{}, 32 | HttpResponse: &http.Response{}, 33 | } 34 | //初始化CookieJar 35 | req.HttpClient.Jar, _ = cookiejar.New(&cookiejar.Options{ 36 | PublicSuffixList: publicsuffix.List, 37 | }) 38 | //设置CheckRedirect 39 | req.HttpClient.CheckRedirect = func(req *http.Request, via []*http.Request) error { 40 | return http.ErrUseLastResponse 41 | } 42 | //设置Transport 43 | req.HttpClient.Transport = &http.Transport{ 44 | // 12306 https CA认证 45 | TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 46 | // TLSClientConfig: &tls.Config{RootCAs: pool}, 47 | DialContext: (&net.Dialer{ 48 | Timeout: 60 * time.Second, 49 | KeepAlive: 60 * time.Second, 50 | DualStack: true, 51 | }).DialContext, 52 | MaxIdleConns: 100, 53 | IdleConnTimeout: 90 * time.Second, 54 | TLSHandshakeTimeout: 60 * time.Second, 55 | } 56 | return req 57 | } 58 | 59 | //创建请求 60 | func (request *Request) CreateHttpRequest(requestUrl, method string, val interface{}) error { 61 | var nReq *http.Request 62 | var nErr error 63 | switch data := val.(type) { 64 | case string: 65 | nReq, nErr = http.NewRequest(method, requestUrl, io.Reader(bytes.NewBuffer([]byte(data)))) 66 | case []byte: 67 | nReq, nErr = http.NewRequest(method, requestUrl, io.Reader(bytes.NewBuffer(data))) 68 | case url.Values: 69 | nReq, nErr = http.NewRequest(method, requestUrl, strings.NewReader(data.Encode())) 70 | case *url.Values: 71 | nReq, nErr = http.NewRequest(method, requestUrl, strings.NewReader(data.Encode())) 72 | default: 73 | nReq, nErr = http.NewRequest(method, requestUrl, nil) 74 | } 75 | if nErr != nil { 76 | return errors.New("Request请求创建失败") 77 | } 78 | request.HttpRequest = nReq 79 | return nil 80 | } 81 | 82 | //设置Header头 83 | func (request *Request) SetHeader(key, val string) error { 84 | if request.HttpRequest != nil { 85 | request.HttpRequest.Header.Set(key, val) 86 | return nil 87 | } 88 | return errors.New("HttpRequest为空") 89 | } 90 | 91 | //设置Cookie 92 | func (request *Request) SetCookie(cookie []*http.Cookie) error { 93 | if request.HttpClient.Jar != nil && request.HttpRequest != nil { 94 | request.HttpClient.Jar.SetCookies(request.HttpRequest.URL, cookie) 95 | return nil 96 | } 97 | return errors.New("HttpClient为空") 98 | } 99 | 100 | //设置默认请求头 101 | func SetRequestDefaultHeader(request *Request) { 102 | //设置默认Header 103 | if !request.DisableDefaultHeader { 104 | request.SetHeader("Accept", "*/*") 105 | request.SetHeader("Accept-Encoding", "deflate, br") 106 | request.SetHeader("Accept-Language", "zh-CN,zh;q=0.9") 107 | request.SetHeader("Cache-Control", "no-cache") 108 | request.SetHeader("Connection", "keep-alive") 109 | request.SetHeader("Host", "kyfw.12306.cn") 110 | request.SetHeader("Origin", "https://kyfw.12306.cn") 111 | request.SetHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36") 112 | } 113 | } 114 | 115 | //读取返回数据 116 | func ReadHttpResponseData(request *Request) ([]byte, error) { 117 | //关闭Response body 118 | defer request.HttpResponse.Body.Close() 119 | //返回读取数据 120 | return ioutil.ReadAll(request.HttpResponse.Body) 121 | } 122 | 123 | //发送Request请求 124 | func SendHttpRequest(request *Request) error { 125 | //判断Post请求,设置Header头Content-Type 126 | if strings.ToUpper(request.HttpRequest.Method) == "POST" { 127 | request.SetHeader("Content-Type", "application/x-www-form-urlencoded; charset=utf-") 128 | } 129 | nRsp, errSend := request.HttpClient.Do(request.HttpRequest) 130 | if errSend != nil { 131 | return errors.New("发送Request请求失败") 132 | } 133 | request.HttpResponse = nRsp 134 | return nil 135 | } 136 | 137 | //发送请求 138 | func (request *Request) Send() ([]byte, error) { 139 | if request.HttpRequest != nil { 140 | //设置header 141 | SetRequestDefaultHeader(request) 142 | //发送请求 143 | err := SendHttpRequest(request) 144 | if err != nil { 145 | return nil, err 146 | } 147 | //读取数据 148 | return ReadHttpResponseData(request) 149 | } 150 | return nil, errors.New("HttpRequest为空") 151 | } 152 | -------------------------------------------------------------------------------- /utils/task.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/astaxie/beego" 7 | ) 8 | 9 | type Task struct { 10 | TaskID int64 `json:"task_id"` 11 | UserID string `json:"-"` 12 | StartTime int64 `json:"start_time"` 13 | EndTime int64 `json:"end_time"` 14 | IsRun bool `json:"task_status"` 15 | TicketCode string `json:"ticket_code"` 16 | UserKyfw *Kyfw `json:"-"` 17 | SecretKey string `json:"-"` 18 | TrainNo string `json:"-"` 19 | TrainCode string `json:"train_code"` 20 | TrainDate string `json:"date_time"` 21 | ForMatDate string `json:"-"` 22 | StartStation string `json:"start_name"` 23 | EndStation string `json:"end_name"` 24 | StartCode string `json:"-"` 25 | EndCode string `json:"-"` 26 | TicketStr string `json:"ticket_str"` 27 | PassengerStr string `json:"passenger_str"` 28 | } 29 | 30 | type TaskList struct { 31 | UserTask map[string]map[int64]interface{} 32 | } 33 | 34 | func InitTaskList() *TaskList { 35 | return &TaskList{ 36 | UserTask: make(map[string]map[int64]interface{}), 37 | } 38 | } 39 | 40 | func (tasks *TaskList) Set(key string, task *Task) { 41 | if tasks.UserTask[key] == nil { 42 | tasks.UserTask[key] = make(map[int64]interface{}) 43 | } 44 | task.TaskID = time.Now().Unix() 45 | task.UserID = key 46 | tasks.UserTask[key][task.TaskID] = task 47 | task.Start() 48 | } 49 | 50 | func (tasks *TaskList) Get(key string) map[int64]interface{} { 51 | v, ok := tasks.UserTask[key] 52 | if !ok { 53 | return nil 54 | } 55 | return v 56 | } 57 | 58 | func (tasks *TaskList) Del(key string) { 59 | delete(tasks.UserTask, key) 60 | } 61 | 62 | func (tasks *TaskList) CreateTask(kyfw *Kyfw, SecretKey, TrainNo, TrainCode, TrainDate, ForMatDate, StartStation, EndStation, StartCode, EndCode, TicketStr, PassengerStr string) *Task { 63 | return &Task{ 64 | UserKyfw: kyfw, 65 | SecretKey: SecretKey, 66 | TrainNo: TrainNo, 67 | TrainCode: TrainCode, 68 | TrainDate: TrainDate, 69 | ForMatDate: ForMatDate, 70 | StartStation: StartStation, 71 | EndStation: EndStation, 72 | StartCode: StartCode, 73 | EndCode: EndCode, 74 | TicketStr: TicketStr, 75 | PassengerStr: PassengerStr, 76 | } 77 | } 78 | 79 | func (tasks *TaskList) DeleteTask(key string, taskID int64) { 80 | delete(tasks.UserTask[key], taskID) 81 | } 82 | 83 | func (task *Task) Start() { 84 | //任务开始 85 | task.IsRun = true 86 | task.StartTime = time.Now().Unix() 87 | go func() { 88 | for task.TicketCode == "" && task.IsRun { 89 | time.Sleep(10 * time.Second) 90 | err := task.UserKyfw.PlaceAnOrder(task.SecretKey, task.TrainNo, task.TrainCode, task.StartStation, task.StartCode, task.EndStation, task.EndCode, task.TrainDate, task.ForMatDate, task.TicketStr, task.PassengerStr) 91 | //抢票失败,写日志 92 | if err != nil { 93 | beego.Debug("Task:", err.Error()) 94 | continue 95 | } 96 | beego.Debug("Task: success") 97 | //抢票成功 98 | task.TicketCode = task.UserKyfw.OrderTicketCode 99 | task.IsRun = false 100 | task.EndTime = time.Now().Unix() 101 | } 102 | }() 103 | } 104 | --------------------------------------------------------------------------------