Initial commit
This commit is contained in:
263
user.go
Normal file
263
user.go
Normal file
@ -0,0 +1,263 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/awa/go-iap/appstore/api"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
"github.com/labstack/echo/v4"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type UserResponse struct {
|
||||
ID int `json:"id"`
|
||||
UserID string `json:"userid"`
|
||||
UserName string `json:"username"`
|
||||
VIP int `json:"vip"`
|
||||
}
|
||||
|
||||
// TODO: 以后引入 GORM mysql driver ,简化数据库操作。
|
||||
|
||||
func queryUserHandler(c echo.Context) error {
|
||||
// 从 context 中获取变量
|
||||
deviceID := c.Get(KEY_DEVICEID).(string)
|
||||
GID, _ := c.Get(KEY_GID).(int)
|
||||
|
||||
db, _ := GetDBManager()
|
||||
var response UserResponse
|
||||
|
||||
// 查询 user 表
|
||||
err := db.MySQL.QueryRow("SELECT ID, UserID, UserName FROM user WHERE DeviceID = ?", deviceID).Scan(&response.ID, &response.UserID, &response.UserName)
|
||||
if err == sql.ErrNoRows {
|
||||
// 用户不存在,创建新用户
|
||||
// TODO: 这里要不要自动分配userid,这个userid在内部基本不会用到
|
||||
res, err := db.MySQL.Exec("INSERT INTO user (DeviceID) VALUES (?)", deviceID)
|
||||
if err != nil {
|
||||
logger.Error("insert db error", zap.Error(err))
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create user")
|
||||
}
|
||||
lastID, err := res.LastInsertId()
|
||||
if err != nil {
|
||||
logger.Error("insert db error", zap.Error(err))
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to retrieve last insert ID")
|
||||
}
|
||||
response.ID = int(lastID)
|
||||
response.UserName = ""
|
||||
logger.Debug("insert user", zap.Int("ID", response.ID), zap.String("DeviceID", deviceID))
|
||||
|
||||
} else if err != nil {
|
||||
logger.Error("query db error", zap.Error(err))
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
// 看上传的 userID 跟数据库的是否一致 userID 转int ..
|
||||
if response.ID != GID {
|
||||
logger.Warn("userid not match", zap.Int("ID", response.ID), zap.Int("userGID", GID))
|
||||
//log.Printf("userid not match: %v != %v", numid, response.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// 查询 vip 表
|
||||
err = db.MySQL.QueryRow("SELECT IsVIP FROM vip WHERE ID = ?", response.ID).Scan(&response.VIP)
|
||||
if err == sql.ErrNoRows {
|
||||
response.VIP = 0 // 默认非VIP
|
||||
} else if err != nil {
|
||||
logger.Error("query db error", zap.Error(err))
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
|
||||
setResponse(c, response)
|
||||
return nil
|
||||
//return c.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// 查询用户的所有redis key,内部接口
|
||||
func UserRightsHandler(c echo.Context) error {
|
||||
// 获取 c 的 GET方法的参数
|
||||
ID, _ := strconv.Atoi(c.QueryParam("ID"))
|
||||
|
||||
db, _ := GetDBManager()
|
||||
ub := NewUserBenefits(db.Redis)
|
||||
|
||||
// 查询redis
|
||||
userData, err := ub.QueryUserBenefits(ID)
|
||||
if err != nil {
|
||||
logger.Error("QueryUserBenefits", zap.Error(err), zap.Int("ID", ID))
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
logger.Debug("QueryUserBenefits", zap.Any("userData", userData), zap.Int("ID", ID))
|
||||
}
|
||||
|
||||
setResponse(c, userData)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查询用户的所有redis key,内部接口
|
||||
func ResetUserRightsHandler(c echo.Context) error {
|
||||
// 获取 c 的 GET方法的参数
|
||||
ID, _ := strconv.Atoi(c.QueryParam("ID"))
|
||||
datastr := c.QueryParam("datestr")
|
||||
|
||||
db, _ := GetDBManager()
|
||||
ub := NewUserBenefits(db.Redis)
|
||||
|
||||
// 查询redis
|
||||
err := ub.ResetUserBenefits(ID, datastr)
|
||||
if err != nil {
|
||||
logger.Error("ResetUserRightsHandler", zap.Error(err), zap.Int("ID", ID))
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, err.Error())
|
||||
} else {
|
||||
logger.Debug("ResetUserRightsHandler", zap.Int("ID", ID))
|
||||
}
|
||||
|
||||
setResponse(c, nil)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 编写查询用户是否VIP的函数,输入是GID,输出是vip,并且给出是否有error ,不需要使用 echo context
|
||||
func queryUserVIP(ID int) (int, error) {
|
||||
db, _ := GetDBManager()
|
||||
var vip int
|
||||
err := db.MySQL.QueryRow("SELECT IsVIP FROM vip WHERE ID = ?", ID).Scan(&vip)
|
||||
if err == sql.ErrNoRows {
|
||||
return 0, nil // 默认非VIP
|
||||
} else if err != nil {
|
||||
logger.Error("query db error", zap.Error(err))
|
||||
return 0, err
|
||||
}
|
||||
return vip, nil
|
||||
}
|
||||
|
||||
// 编写查询用户是否有权限使用某功能,输入是用户ID,如果用户是VIP,则有权限;否则查询 QueryUserBenefits 看是否超过免费限制,输出 是否有权限,以及是否出错。
|
||||
func queryUserBenefits(c echo.Context) (bool, error) {
|
||||
// 获取参数
|
||||
ID, _ := c.Get(KEY_GID).(int)
|
||||
timeZone := c.Request().Header.Get(KEY_HEADER_TIMEZONE)
|
||||
secondsFromGMT, _ := strconv.Atoi(c.Request().Header.Get(KEY_HEADER_SECONDSFROMGMT))
|
||||
|
||||
db, _ := GetDBManager()
|
||||
var vip int
|
||||
err := db.MySQL.QueryRow("SELECT IsVIP FROM vip WHERE ID = ?", ID).Scan(&vip)
|
||||
if err == sql.ErrNoRows {
|
||||
// 非VIP,查询redis的免费次数
|
||||
db, _ := GetDBManager()
|
||||
ub := NewUserBenefits(db.Redis)
|
||||
status, err := ub.CheckAndDecrement(ID, timeZone, secondsFromGMT)
|
||||
if err != nil {
|
||||
logger.Error("CheckAndDecrement", zap.Error(err), zap.Int("ID", ID), zap.String("timeZone", timeZone), zap.Int("secondsFromGMT", secondsFromGMT))
|
||||
return false, err
|
||||
} else {
|
||||
logger.Debug("CheckAndDecrement", zap.Int("ID", ID), zap.String("timeZone", timeZone), zap.Int("secondsFromGMT", secondsFromGMT), zap.Int("status", status))
|
||||
return status == 0, nil
|
||||
}
|
||||
} else if err != nil {
|
||||
logger.Error("query db error", zap.Error(err))
|
||||
return false, err
|
||||
}
|
||||
|
||||
if vip == 1 {
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// 从苹果校验订单后,插入vip表中
|
||||
func UpdateOrderByVerify(ID int, AppAcountToken string, OriginTransID string, transantion *api.JWSTransaction) error {
|
||||
// 写入vip表,如果ID对应记录不存在,则插入,否则更新
|
||||
db, _ := GetDBManager()
|
||||
|
||||
var ProductType, Currency string
|
||||
var Price, Duration int
|
||||
// 先从 product 表中,根据 transantion.ProductID 获取到对应的 Duration,ProductName, Price, Currency
|
||||
err := db.MySQL.QueryRow("SELECT ProductType, Price, Currency, Duration from product where ProductID = ?", transantion.ProductID).Scan(&ProductType, &Price, &Currency, &Duration)
|
||||
if err == sql.ErrNoRows {
|
||||
logger.Error("query productID empty.", zap.Error(err), zap.Int("ID", ID), zap.String("AppAcountToken", AppAcountToken), zap.String("OriginTransID", OriginTransID))
|
||||
return err
|
||||
} else if err != nil {
|
||||
logger.Error("query productID error", zap.Error(err), zap.Int("ID", ID), zap.String("AppAcountToken", AppAcountToken), zap.String("OriginTransID", OriginTransID))
|
||||
return err
|
||||
}
|
||||
|
||||
// 取当前的时间戳
|
||||
//purchase_time := time.Now().Unix()
|
||||
//exp_time := purchase_time + int64(Duration)*3600*24
|
||||
|
||||
currentTime := time.Now()
|
||||
nextDay := time.Now().AddDate(0, 0, Duration)
|
||||
|
||||
// TODO: transaction.TransactionReason 有新购和续费,需要区分;同一个购买或者续费事件,可能有通知多次,需要排重
|
||||
var tmpID int
|
||||
errDup := db.MySQL.QueryRow("SELECT ID from vip where TransactionID = ? and OriginalTransactionID = ? and IsVip = 1 and ExpDate > ?", transantion.TransactionID, transantion.OriginalTransactionId, currentTime).Scan(&tmpID)
|
||||
if errDup != sql.ErrNoRows {
|
||||
// 表示重复了,可以直接返回
|
||||
logger.Info("duplicate request", zap.Int("ID", ID), zap.String("AppAcountToken", AppAcountToken), zap.String("OriginTransID", OriginTransID), zap.String("TransactionID", transantion.TransactionID))
|
||||
return nil
|
||||
} else if errDup != nil {
|
||||
logger.Error("query error", zap.Error(errDup), zap.Int("ID", ID), zap.String("AppAcountToken", AppAcountToken), zap.String("OriginTransID", OriginTransID))
|
||||
// 这里不返回,继续尝试更新。
|
||||
}
|
||||
|
||||
// 更新到DB
|
||||
sql := `INSERT INTO vip (ID, IsVip, AppStore, ProductID, ProductType, Environment, Price, Currency, Storefront, PurchaseDate, ExpDate, AutoRenew, OriginalTransactionID, TransactionID, AppAccountToken, TransactionReason)
|
||||
VALUES (?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
IsVip = 1, AppStore = ?, ProductID = ?, ProductType = ?, Environment = ?, Price = ?, Currency = ?, Storefront = ?, PurchaseDate = ?, ExpDate = ?, AutoRenew = ?, OriginalTransactionID = ? , TransactionID = ?, AppAccountToken = ?, TransactionReason = ? `
|
||||
|
||||
_, err2 := db.MySQL.Exec(sql,
|
||||
ID, APPSTORE, transantion.ProductID, ProductType, transantion.Environment, Price, Currency, transantion.Storefront, currentTime, nextDay, 1, OriginTransID, transantion.TransactionID, transantion.AppAccountToken, transantion.TransactionReason,
|
||||
APPSTORE, transantion.ProductID, ProductType, transantion.Environment, Price, Currency, transantion.Storefront, currentTime, nextDay, 1, OriginTransID, transantion.TransactionID, transantion.AppAccountToken, transantion.TransactionReason)
|
||||
|
||||
if err2 != nil {
|
||||
logger.Error("UpdateOrderByVerify", zap.Error(err), zap.Int("ID", ID), zap.String("AppAcountToken", AppAcountToken), zap.String("OriginTransID", OriginTransID))
|
||||
return err2
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 接收到appstore的回调,写入数据。因为不知道对应的用户账号,所以只能记录。
|
||||
func UpdateOrderByNotify(Notification *AppStoreServerNotification) error {
|
||||
db, _ := GetDBManager()
|
||||
|
||||
// 先写order_log表
|
||||
sql := `INSERT INTO order_log (AppStore, NotificationType, Subtype, Environment, AppAccountToken, TransactionInfo, RenewalInfo, Payload)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?) `
|
||||
|
||||
// 需要把 Notification.TransactionInfo 转成 字符串
|
||||
TransactionInfo, _ := json.Marshal(Notification.TransactionInfo)
|
||||
RenewalInfo, _ := json.Marshal(Notification.RenewalInfo)
|
||||
Payload, _ := json.Marshal(Notification.Payload)
|
||||
|
||||
_, err := db.MySQL.Exec(sql,
|
||||
APPSTORE, Notification.Payload.NotificationType, Notification.Payload.Subtype, Notification.Payload.Data.Environment, Notification.TransactionInfo.AppAccountToken, TransactionInfo, RenewalInfo, Payload)
|
||||
|
||||
if err != nil {
|
||||
logger.Error("UpdateOrderByNotify", zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 给定 appaccounttoken,在order_log中查询是否已经存在了,如果存在,则无需向苹果发起验证请求
|
||||
func CheckOrderByAppAcountToken(AppAccountToken string) (bool, error) {
|
||||
db, _ := GetDBManager()
|
||||
|
||||
// 根据AppAccountToken查询 order_log表,查看记录是否存在,如果存在,返回true,否则false
|
||||
var LogID int
|
||||
err := db.MySQL.QueryRow("SELECT LogID from order_log where AppAccountToken = ? and AppStore = ? ", AppAccountToken, APPSTORE).Scan(&LogID)
|
||||
if err == sql.ErrNoRows {
|
||||
logger.Info("query empty", zap.String("AppAccountToken", AppAccountToken))
|
||||
return false, nil
|
||||
} else if err != nil {
|
||||
logger.Error("query error.", zap.Error(err), zap.String("AppAccountToken", AppAccountToken))
|
||||
return false, err
|
||||
}
|
||||
|
||||
// TODO: 可以在这里更新 vip 表,这样 Verify 的过程中,如果查询到记录,就不需要再去appstore校验了。
|
||||
|
||||
return true, nil
|
||||
}
|
||||
Reference in New Issue
Block a user