You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
599 lines
14 KiB
599 lines
14 KiB
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
|
|
}
|