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

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
}