280 lines
9.1 KiB
Go
280 lines
9.1 KiB
Go
package web
|
||
|
||
import (
|
||
"net/http"
|
||
"net/url"
|
||
"strconv"
|
||
"strings"
|
||
"videoweb/app/service/authser"
|
||
"videoweb/app/service/rechargeser"
|
||
"videoweb/pub/common/log"
|
||
"videoweb/pub/common/useragent"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
const rechargePageSize = 5
|
||
|
||
// Recharge 金币充值页:套餐 GET /api/app/recharge/package/list,购买 POST /api/app/coin/buy,流水 GET /api/app/coin/trans/list。
|
||
func Recharge(c *gin.Context) {
|
||
data := gin.H{
|
||
"title": "金币充值 - 黄果短剧", "pageHeading": "金币充值",
|
||
"backHref": "/profile", "helpHref": "/help",
|
||
}
|
||
responses := gin.H{}
|
||
loadAccountSummary(c, data, responses)
|
||
|
||
packages, err := rechargeser.FetchPackageList(c)
|
||
if err != nil {
|
||
log.Error("fetch recharge packages failed", log.E(err))
|
||
data["packageUnavailable"] = true
|
||
packages = &rechargeser.RechargePackageListResp{}
|
||
responses["recharge/package/list"] = gin.H{"error": "加载失败", "msg": err.Error()}
|
||
} else {
|
||
responses["recharge/package/list"] = gin.H{"code": 200, "data": packages}
|
||
}
|
||
vm := rechargeser.BuildDepositPageVM(packages, "coin")
|
||
data["packages"] = vm.CoinList
|
||
data["paymentMethods"] = vm.PayMethods
|
||
data["rechargePackageResponse"] = packages
|
||
|
||
balance := authser.RechargeFromCookies(c)
|
||
if _, ok := data["recharge"]; ok {
|
||
balance = toInt64(data["recharge"])
|
||
}
|
||
data["balance"] = balance
|
||
data["balanceText"] = authser.FormatRecharge(balance)
|
||
|
||
page, _, err := fetchRechargeTransactions(c, 1, rechargePageSize, 0)
|
||
if err != nil {
|
||
log.Error("fetch recharge transactions failed", log.E(err))
|
||
data["transactionsUnavailable"] = true
|
||
page = &rechargeser.CoinTransPage{PageNum: 1, PageSize: rechargePageSize}
|
||
responses["coin/trans/list"] = gin.H{"error": "加载失败", "msg": err.Error()}
|
||
} else {
|
||
responses["coin/trans/list"] = gin.H{"code": 200, "data": page}
|
||
}
|
||
data["transactions"] = rechargeser.BuildTxListFromCoinTrans(page.List, 0)
|
||
data["transactionPage"] = page
|
||
data["transactionResponse"] = page
|
||
data["rechargeDebug"] = responses
|
||
HTMLOK(c, "recharge.html", data)
|
||
}
|
||
|
||
// RechargeCoinBuy BFF:代理 POST /api/app/coin/buy。
|
||
// 文档:http://172.104.181.99:25319/swagger/index.html#/金币/post_api_app_coin_buy
|
||
func RechargeCoinBuy(c *gin.Context) {
|
||
if useragent.TokenFromRequest(c.Request) == "" {
|
||
c.JSON(http.StatusOK, gin.H{"code": 401, "msg": "unauthorized", "tip": "请先登录", "data": nil})
|
||
return
|
||
}
|
||
var body rechargeser.BuyPayReq
|
||
if err := c.ShouldBindJSON(&body); err != nil || body.PackageID <= 0 {
|
||
c.JSON(http.StatusOK, gin.H{"code": 400, "msg": "packageId required", "tip": "请选择充值套餐", "data": nil})
|
||
return
|
||
}
|
||
body.PayMode = rechargeser.NormalizePayMode(body.PayMode)
|
||
if body.PayMode == "" {
|
||
c.JSON(http.StatusOK, gin.H{"code": 400, "msg": "payMode required", "tip": "请选择支付方式", "data": nil})
|
||
return
|
||
}
|
||
if !validCoinPackagePayment(c, body.PackageID, body.PayMode) {
|
||
c.JSON(http.StatusOK, gin.H{"code": 400, "msg": "invalid package/payMode", "tip": "套餐或支付方式不可用,请刷新后重试", "data": nil})
|
||
return
|
||
}
|
||
|
||
env, err := rechargeser.BuyCoin(c, body)
|
||
if err != nil {
|
||
log.Error("buy coin failed", log.E(err))
|
||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error(), "tip": "创建支付订单失败,请稍后重试", "data": nil})
|
||
return
|
||
}
|
||
if env != nil {
|
||
if retry, needLogin := authser.OnSessionExpired(c, env.Code); needLogin {
|
||
c.JSON(http.StatusOK, gin.H{"code": env.Code, "msg": env.Msg, "tip": "登录已过期,请重新登录", "data": nil})
|
||
return
|
||
} else if retry {
|
||
env, err = rechargeser.BuyCoin(c, body)
|
||
if err != nil {
|
||
log.Error("buy coin retry failed", log.E(err))
|
||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error(), "tip": "创建支付订单失败,请稍后重试", "data": nil})
|
||
return
|
||
}
|
||
}
|
||
}
|
||
if env == nil || env.Code != 200 {
|
||
code, tip, msg := 500, "创建支付订单失败", "buy failed"
|
||
if env != nil {
|
||
code = env.Code
|
||
msg = env.Msg
|
||
tip = firstNonEmptyAuth(env.Tip, env.Msg, tip)
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"code": code, "msg": msg, "tip": tip, "data": nil})
|
||
return
|
||
}
|
||
safeURL := safePaymentURL(env.Data.PayURL)
|
||
if safeURL == "" {
|
||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": "invalid payUrl", "tip": "支付地址无效,请稍后重试", "data": nil})
|
||
return
|
||
}
|
||
env.Data.PayURL = safeURL
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 200,
|
||
"msg": env.Msg,
|
||
"tip": firstNonEmptyAuth(env.Tip, "订单创建成功"),
|
||
"data": env.Data,
|
||
})
|
||
}
|
||
|
||
// RechargeTransactions BFF:代理 GET /api/app/coin/trans/list。
|
||
// 文档:http://172.104.181.99:25319/swagger/index.html#/金币/get_api_app_coin_trans_list
|
||
// query:page / pageSize / type(0全部 1充值 2消费 3奖励)
|
||
func RechargeTransactions(c *gin.Context) {
|
||
if useragent.TokenFromRequest(c.Request) == "" {
|
||
c.JSON(http.StatusOK, gin.H{"code": 401, "msg": "unauthorized", "tip": "请先登录", "data": nil})
|
||
return
|
||
}
|
||
page := queryInt(c, "page", 1)
|
||
pageSize := queryInt(c, "pageSize", rechargePageSize)
|
||
typ := queryIntAllowZero(c, "type", 0)
|
||
if typ < 0 || typ > 3 {
|
||
typ = 0
|
||
}
|
||
result, code, err := fetchRechargeTransactions(c, page, pageSize, typ)
|
||
if err != nil {
|
||
log.Error("fetch recharge transactions failed", log.E(err))
|
||
tip := "交易明细加载失败"
|
||
if code > 0 {
|
||
c.JSON(http.StatusOK, gin.H{"code": code, "msg": err.Error(), "tip": tip, "data": nil})
|
||
return
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error(), "tip": tip, "data": nil})
|
||
return
|
||
}
|
||
if result == nil {
|
||
result = &rechargeser.CoinTransPage{PageNum: page, PageSize: pageSize}
|
||
}
|
||
c.JSON(http.StatusOK, gin.H{
|
||
"code": 200,
|
||
"msg": "ok",
|
||
"tip": "",
|
||
"data": gin.H{
|
||
"list": rechargeser.BuildTxListFromCoinTrans(result.List, 0),
|
||
"rawList": result.List,
|
||
"pageNum": result.PageNum,
|
||
"pageSize": result.PageSize,
|
||
"totalCount": result.TotalCount,
|
||
},
|
||
})
|
||
}
|
||
|
||
// RechargeCoinTransExport BFF:代理 GET /api/app/coin/trans/export。
|
||
// 文档:http://172.104.181.99:25319/swagger/index.html#/金币/get_api_app_coin_trans_export
|
||
// query:type(0全部 1充值 2消费 3奖励),返回 CSV。
|
||
func RechargeCoinTransExport(c *gin.Context) {
|
||
if useragent.TokenFromRequest(c.Request) == "" {
|
||
c.JSON(http.StatusOK, gin.H{"code": 401, "msg": "unauthorized", "tip": "请先登录", "data": nil})
|
||
return
|
||
}
|
||
typ := queryIntAllowZero(c, "type", 0)
|
||
if typ < 0 || typ > 3 {
|
||
typ = 0
|
||
}
|
||
result, err := rechargeser.FetchCoinTransExport(c, typ)
|
||
if err != nil {
|
||
log.Error("export coin trans failed", log.E(err))
|
||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error(), "tip": "导出失败,请稍后重试", "data": nil})
|
||
return
|
||
}
|
||
ct := strings.TrimSpace(result.ContentType)
|
||
if ct == "" || strings.Contains(strings.ToLower(ct), "json") {
|
||
ct = "text/csv; charset=utf-8"
|
||
}
|
||
disposition := strings.TrimSpace(result.ContentDisposition)
|
||
if disposition == "" {
|
||
disposition = `attachment; filename="coin-trans.csv"`
|
||
}
|
||
c.Header("Content-Type", ct)
|
||
c.Header("Content-Disposition", disposition)
|
||
c.Header("Cache-Control", "no-store")
|
||
c.Data(http.StatusOK, ct, result.Body)
|
||
}
|
||
|
||
func fetchRechargeTransactions(c *gin.Context, page, pageSize, typ int) (*rechargeser.CoinTransPage, int, error) {
|
||
result, code, err := rechargeser.FetchCoinTransPage(c, page, pageSize, typ)
|
||
if retry, _ := authser.OnSessionExpired(c, code); retry {
|
||
result, code, err = rechargeser.FetchCoinTransPage(c, page, pageSize, typ)
|
||
}
|
||
return result, code, err
|
||
}
|
||
|
||
// attachRechargeModal 注入充值弹窗数据:GET /api/app/recharge/package/list → coinList。
|
||
func attachRechargeModal(c *gin.Context, data gin.H) {
|
||
if data == nil {
|
||
return
|
||
}
|
||
packages := rechargeser.FetchPackageListOrEmpty(c)
|
||
vm := rechargeser.BuildDepositPageVM(packages, "coin")
|
||
data["rechargePackages"] = vm.CoinList
|
||
data["rechargePayMethods"] = vm.PayMethods
|
||
data["rechargePackageResponse"] = packages
|
||
if _, ok := data["unlockCost"]; !ok {
|
||
data["unlockCost"] = int64(0)
|
||
}
|
||
if _, ok := data["balance"]; !ok {
|
||
data["balance"] = authser.RechargeFromCookies(c)
|
||
}
|
||
if _, ok := data["balanceText"]; !ok {
|
||
data["balanceText"] = authser.FormatRecharge(toInt64(data["balance"]))
|
||
}
|
||
}
|
||
|
||
func validCoinPackagePayment(c *gin.Context, packageID int64, payMode string) bool {
|
||
data, err := rechargeser.FetchPackageList(c)
|
||
if err != nil || data == nil {
|
||
return false
|
||
}
|
||
for _, item := range data.CoinList {
|
||
if item.ID != packageID {
|
||
continue
|
||
}
|
||
for _, method := range item.PayMethods {
|
||
if rechargeser.NormalizePayMode(method.Type) == payMode {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
return false
|
||
}
|
||
|
||
func safePaymentURL(raw string) string {
|
||
raw = strings.TrimSpace(raw)
|
||
u, err := url.Parse(raw)
|
||
if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" {
|
||
return ""
|
||
}
|
||
return raw
|
||
}
|
||
|
||
func queryInt(c *gin.Context, name string, fallback int) int {
|
||
v, err := strconv.Atoi(strings.TrimSpace(c.Query(name)))
|
||
if err != nil || v <= 0 {
|
||
return fallback
|
||
}
|
||
return v
|
||
}
|
||
|
||
// queryIntAllowZero 允许 0(如 type=0 全部)。
|
||
func queryIntAllowZero(c *gin.Context, name string, fallback int) int {
|
||
raw := strings.TrimSpace(c.Query(name))
|
||
if raw == "" {
|
||
return fallback
|
||
}
|
||
v, err := strconv.Atoi(raw)
|
||
if err != nil {
|
||
return fallback
|
||
}
|
||
return v
|
||
}
|