package lottery import ( "bytes" "crypto/md5" "encoding/hex" "encoding/json" "errors" "fmt" "github.com/mitchellh/mapstructure" "golang.org/x/text/encoding/simplifiedchinese" "golang.org/x/text/transform" "io/ioutil" "net/http" url2 "net/url" "recook/configs" "strings" "sync" "time" ) const ( actionIssue = "102" actionPet = "103" actionQuery = "105" actionBonus = "106" ) var ( url = configs.Config_Lottery_Url username = configs.Config_Lottery_Username password = configs.Config_lottery_password client = &http.Client{} mu sync.Mutex timestamp int64 index = 0 orderIdTimestamp int64 orderIdIndex = 0 Lottery = &lottery{} LotteryError = errors.New("彩票格式错误") ) type lottery struct { } type IssueItem struct { LotID string `mapstructure:"@LotId" json:"lotId"` BonusCode string `mapstructure:"@bonusCode" json:"bonusCode"` Number string `mapstructure:"@number" json:"number"` OfficialStartTime string `mapstructure:"@officialStartTime" json:"officialStartTime"` OfficialStopTime string `mapstructure:"@officialStopTime" json:"officialStopTime"` StartTime string `mapstructure:"@startTime" json:"startTime"` Status string `mapstructure:"@status" json:"status"` StopTime string `mapstructure:"@stopTime" json:"stopTime"` } type IssueList []IssueItem func (s IssueList) Len() int { return len(s) } func (s IssueList) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s IssueList) Less(i, j int) bool { return s[i].Number < s[j].Number } // @Style 获取彩票期数信息 // @Param id string true "彩票编号" // @Param number string true "期数" func (l *lottery) Issue(id, number string) (result IssueItem, err error) { if len(number) == 0 { return result, errors.New("期数不能为空") } err = l.exec(actionIssue, &map[string]map[string]map[string]string{ "issueQuery": { "issue": { "@LotId": id, "@number": number, }, }, }, &result) return } // @Style 获取彩票未来期数列表 // @Param id string true "彩票编号" func (l *lottery) IssueList(id string) (result IssueList, err error) { err = l.exec(actionIssue, &map[string]map[string]map[string]string{ "issueQuery": { "issue": { "@LotId": id, "@number": "", }, }, }, &result) return } type BetItem struct { AnteCode []string `json:"anteCode" form:"anteCode"` PlayType int `json:"playType" form:"playType"` Money int `json:"money" form:"money"` } // @Style 彩票提交数据处理 func (l *lottery) BetList(id string, item *[]BetItem) error { result := []BetItem{} for _, betItem := range *item { count := l.GetCount(id, betItem.PlayType) lenCode := len(betItem.AnteCode) for i := 0; i < lenCode; i += count { endLen := i + count if endLen > lenCode { endLen = lenCode } anteCode := betItem.AnteCode[i:endLen] totalMoney := 0 for _, code := range anteCode { money, err := l.GetCodeMoney(id, betItem.PlayType, code) if err != nil { return err } totalMoney += money } result = append(result, BetItem{ AnteCode: anteCode, PlayType: betItem.PlayType, Money: totalMoney, }) } } return nil } // @Style 计算单注彩票金额 func (l *lottery) GetCodeMoney(id string, lotteryType int, code string) (int, error) { count, err := l.GetLotteryCount(id, code) if err != nil { return 0, err } money := l.GetMoney(id, lotteryType) return count * money, nil } // @Style 获取投注数量 func (l *lottery) GetLotteryCount(id, code string) (int, error) { lottery := strings.Split(code, "#") if len(lottery) != 2 { return 0, LotteryError } split := strings.Split(lottery[0], "$") redLen := 0 // 胆数 redDLen := 0 if len(split) == 1 { // 非胆拖 redLen = len(strings.Split(lottery[0], ",")) } else { // 胆拖 redLen = len(strings.Split(split[1], ",")) redDLen = len(strings.Split(split[0], ",")) } redCount := l.getRed(id) if redCount == 0 { return 0, LotteryError } redCountLen := l.combination(redCount, redLen-redDLen) if redCountLen == 0 { return 0, LotteryError } // 蓝球处理 split = strings.Split(lottery[1], "$") blueLen := 0 // 胆数 blueDLen := 0 if len(split) == 1 { // 非胆拖 blueLen = len(strings.Split(lottery[1], ",")) } else { // 胆拖 blueLen = len(strings.Split(split[1], ",")) blueDLen = len(strings.Split(split[0], ",")) } blueCount := l.getBlue(id) if blueCount == 0 { return 0, LotteryError } blueCountLen := l.combination(blueCount, blueLen-blueDLen) if blueCountLen == 0 { return 0, LotteryError } return redCountLen * blueCountLen, nil } // @Style 开奖红球数量 func (l *lottery) getRed(id string) int { switch id { case "ssq": return 6 case "dlt": return 5 } return 0 } // @Style 开奖蓝球数量 func (l *lottery) getBlue(id string) int { switch id { case "ssq": return 1 case "dlt": return 2 } return 0 } // @Style 排列组合 n组数据选k个 func (l *lottery) combination(k, n int) int { if k <= 0 || n <= 0 { return 0 } r := 1 if k > n { return 0 } for d := 1; d <= k; d++ { r *= n n-- r = r / d } return r } // @Style 彩票每票限制数量 func (l *lottery) GetCount(id string, lotteryType int) int { switch id { case "ssq": switch lotteryType { case 101: return 5 default: return 1 } case "dlt": switch lotteryType { case 101: return 5 case 103: return 5 default: return 1 } } return 0 } // @Style 获取单注金额 func (l *lottery) GetMoney(id string, lotteryType int) int { switch id { case "ssq": switch lotteryType { default: return 2 } case "dlt": switch lotteryType { case 101: return 2 case 102: return 2 case 106: return 2 default: return 3 } } return 0 } type Ticket struct { LotMoney string `json:"@LotMoney"` LotMulti string `json:"@LotMulti"` OrderID string `json:"@OrderID"` PlayType string `json:"@playType"` AnteCode []string `json:"anteCode"` UserProfile UserProfile `json:"userProfile"` } type UserProfile struct { BonusPhone string `json:"@bonusPhone"` CardNumber string `json:"@cardNumber"` CardType string `json:"@cardType"` Mail string `json:"@mail"` Mobile string `json:"@mobile"` RealName string `json:"@realName"` UserName string `json:"@userName"` } // @Style 投注 // @Param id string true "彩票编号" // @Param number string true "彩票期数" // @Param ticket string true "投注票列表" func (l *lottery) Bet(id, number string, ticket []Ticket) (err error) { for index, _ := range ticket { ticket[index].UserProfile.UserName = username } err = l.exec(actionPet, &map[string]map[string]interface{}{ "lotteryRequest": { "issue": map[string]string{ "@number": number, "@LotId": id, }, "ticket": ticket, }, }, nil) return } type OrderId struct { OrderId string `json:"@OrderID"` } // @Style 订单查询 func (l *lottery) Query(id string, orderIds []OrderId) (result []BetInfo, err error) { err = l.exec(actionQuery, &map[string]map[string]interface{}{ "ticketQuery": { "issue": map[string]string{ "@LotId": id, }, "ticket": orderIds, }, }, &result) return } type Bonus struct { BonusNumber string `mapstructure:"@bonusNumber" json:"bonusNumber"` TotalItems string `mapstructure:"@totalItems" json:"totalItems"` TotalMoney string `mapstructure:"@totalMoney" json:"totalMoney"` Issue Issue `mapstructure:"issue" json:"issue"` BonusItem []BonusItem `mapstructure:"bonusItem" json:"bonusItem"` } type BonusItem struct { PlayType string `mapstructure:"@playType" json:"playType"` Money string `mapstructure:"@money" json:"money"` WinMoney string `mapstructure:"@winMoney" json:"winMoney"` LevelBonusMoney string `mapstructure:"@levelBonusMoney" json:"levelBonusMoney"` IsBombBonus string `mapstructure:"@isBombBonus" json:"isBombBonus"` BonusLevel string `mapstructure:"@bonusLevel" json:"bonusLevel"` Size string `mapstructure:"@size" json:"size"` OrderID string `mapstructure:"@OrderID" json:"orderID"` } // @Style 获奖查询 func (l *lottery) Bonus(id, number string) (result Bonus, err error) { err = l.exec(actionBonus, &map[string]map[string]map[string]string{ "bonusQuery": { "issue": { "@LotId": id, "@number": number, }, }, }, &result) return } type Issue struct { LotId string `mapstructure:"@LotId"` Number string `mapstructure:"@number"` StartTime string `mapstructure:"@startTime"` StopTime string `mapstructure:"@stopTime"` OfficialStartTime string `mapstructure:"@officialStartTime"` OfficialStopTime string `mapstructure:"@officialStopTime"` Status string `mapstructure:"@status"` BonusCode string `mapstructure:"@bonusCode"` SalesMoney string `mapstructure:"@salesMoney"` BonusMoney string `mapstructure:"@bonusMoney"` } // @Style 奖期通知 func (l *lottery) CallbackIssue(data string) (result Issue, err error) { res := map[string]Issue{} err = l.FormatRequest(data, &res) result = res["issue"] return } type BetInfo struct { OrderId string `mapstructure:"@OrderId" json:"orderId"` DealTime string `mapstructure:"@dealTime" json:"dealTime"` Status string `mapstructure:"@status" json:"status"` Message string `mapstructure:"@message" json:"message"` TicketSerialNo string `mapstructure:"@ticketSerialNo" json:"ticketSerialNo"` PrintSerialNo string `mapstructure:"@printSerialNo" json:"printSerialNo"` PrintTime string `mapstructure:"@printTime" json:"printTime"` } // @Style 投注回调 func (l *lottery) CallbackBet(data string) (result []BetInfo, err error) { err = l.FormatRequest(data, &result) return } // @Style 通知返回成功 func (l *lottery) CallbackSuccess(action string) string { body := l.getBody(action, map[string]map[string]string{ "ActionResult": { "@wCode": "0000", "@wRltMsg": "成功,系统处理正常。", }, }) // 签名 body.Header.WSign = l.sign(body) bodyStr, _ := json.Marshal(body) return url2.Values{ "wAction": []string{action}, "wMessage": []string{string(bodyStr)}, }.Encode() } // @Style 反奖通知 func (l *lottery) CallbackBonus(data string) (result Bonus, err error) { err = l.FormatRequest(data, &result) return } type TicketHead struct { WAgentID string `json:"wAgentID"` Timestamp string `json:"timestamp"` WActionType string `json:"wActionType"` WSign string `json:"wSign"` } type requestBody struct { Version string `json:"@version"` WMsgID string `json:"@wMsgID"` Header TicketHead `json:"header"` Body interface{} `json:"body"` } type actionResult struct { ActionResult struct { WCode string `json:"@wCode" mapstructure:"@wCode"` WRltMsg string `json:"@wRltMsg" mapstructure:"@wRltMsg"` Data map[string]interface{} `mapstructure:",remain"` } } func (l *lottery) exec(action string, data interface{}, result interface{}) (err error) { body := l.getBody(action, data) // 签名 body.Header.WSign = l.sign(body) bodyStr, _ := json.Marshal(body) bytes, err := request("POST", url, url2.Values{ "wAction": []string{action}, "wMessage": []string{string(l.Utf8ToGbk(bodyStr))}, }.Encode(), map[string]string{"Content-Type": "application/x-www-form-urlencoded"}) query, err := url2.ParseQuery(string(bytes)) if err != nil { return } jsonData := query.Get("wMessage") resultBody := requestBody{} if err = json.Unmarshal([]byte(jsonData), &resultBody); err != nil { return } resultAction := actionResult{} if err = mapstructure.WeakDecode(resultBody.Body, &resultAction); err != nil { return } if resultAction.ActionResult.WCode != "0000" { return errors.New(resultAction.ActionResult.WRltMsg) } if result != nil { for _, value := range resultAction.ActionResult.Data { if err = mapstructure.WeakDecode(value, result); err != nil { return } } } return } // @Style 处理返回数据 func (l *lottery) FormatRequest(jsonData string, result interface{}) (err error) { resultBody := requestBody{} if err = json.Unmarshal([]byte(jsonData), &resultBody); err != nil { return } for _, value := range resultBody.Body.(map[string]interface{}) { if err = mapstructure.WeakDecode(value, result); err != nil { return } } return } // @Style 获取请求数据 func (l *lottery) getBody(action string, data interface{}) requestBody { now := time.Now() myWMsgID := username + now.Format("20060102") + fmt.Sprintf("%.8v", l.getSerialNum()) return requestBody{ Version: "2.0", WMsgID: myWMsgID, Header: TicketHead{ WAgentID: username, Timestamp: now.Format("20060102150405"), WActionType: action, WSign: "", }, Body: data, } } // @Style 获取流水号 func (l *lottery) getSerialNum() int { mu.Lock() defer mu.Unlock() now := time.Now().Unix() if now != timestamp { timestamp = now index = 0 } index++ return index } func (l *lottery) GetOrderId() string { mu.Lock() defer mu.Unlock() date := time.Now() now := date.Unix() if now != orderIdTimestamp { orderIdTimestamp = now orderIdIndex = 0 } orderIdIndex++ return fmt.Sprintf("%v%v%.8v", username, date.Format("20200102150405"), orderIdIndex) } // @Style 签名 func (l *lottery) sign(body requestBody) string { jsonData, _ := json.Marshal(body.Body) signStr := body.WMsgID + body.Header.Timestamp + password + string(jsonData) md5str := md5.New() md5str.Write(l.Utf8ToGbk([]byte(signStr))) return hex.EncodeToString(md5str.Sum(nil)) } func request(method, url, data string, headers ...map[string]string) ([]byte, error) { reqest, err := http.NewRequest(method, url, strings.NewReader(data)) if err != nil { return nil, err } if len(headers) > 0 { for key, value := range headers[0] { reqest.Header.Add(key, value) } } response, err := client.Do(reqest) if err != nil { return nil, err } defer response.Body.Close() utfReaderRes := transform.NewReader(response.Body, simplifiedchinese.GBK.NewDecoder()) result, err := ioutil.ReadAll(utfReaderRes) if err != nil { return nil, err } return result, nil } // @Style 转码 func (l *lottery) GbkToUtf8(str []byte) (b []byte) { r := transform.NewReader(bytes.NewReader(str), simplifiedchinese.GBK.NewDecoder()) b, err := ioutil.ReadAll(r) if err != nil { return } return } // @Style 转码 func (l *lottery) Utf8ToGbk(str []byte) (b []byte) { r := transform.NewReader(bytes.NewReader(str), simplifiedchinese.GBK.NewEncoder()) b, err := ioutil.ReadAll(r) if err != nil { return } return }