调整UI
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const maxAccountPage = 10000
|
||||
|
||||
type accountPage struct {
|
||||
Current int
|
||||
Total int
|
||||
Count int64
|
||||
PrevURL string
|
||||
NextURL string
|
||||
}
|
||||
|
||||
func accountPageNum(c *gin.Context) (int, bool) {
|
||||
if c.Request.URL.Query().Has("page") {
|
||||
return 0, false
|
||||
}
|
||||
raw := strings.TrimSpace(c.Param("page"))
|
||||
if raw == "" {
|
||||
return 1, true
|
||||
}
|
||||
page, err := strconv.Atoi(raw)
|
||||
if err != nil || page < 1 || page > maxAccountPage {
|
||||
return 0, false
|
||||
}
|
||||
return page, true
|
||||
}
|
||||
|
||||
func makeAccountPage(path string, page, size int, count int64) accountPage {
|
||||
total := int((count + int64(size) - 1) / int64(size))
|
||||
p := accountPage{Current: page, Total: total, Count: count}
|
||||
link := func(n int) string {
|
||||
path = strings.TrimRight(path, "/")
|
||||
if n <= 1 {
|
||||
return path
|
||||
}
|
||||
return path + "/page/" + strconv.Itoa(n)
|
||||
}
|
||||
if page > 1 {
|
||||
p.PrevURL = link(page - 1)
|
||||
}
|
||||
if page < total {
|
||||
p.NextURL = link(page + 1)
|
||||
}
|
||||
return p
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"videoweb/app/service/authser"
|
||||
"videoweb/pub/common/log"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type registerBody struct {
|
||||
AccountNo string `json:"accountNo"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
AgentInviteCode string `json:"agentInviteCode"`
|
||||
DevID string `json:"devID"`
|
||||
Channel string `json:"channel"`
|
||||
}
|
||||
|
||||
type loginBody struct {
|
||||
Account string `json:"account"`
|
||||
Password string `json:"password"`
|
||||
DevID string `json:"devID"`
|
||||
Channel string `json:"channel"`
|
||||
}
|
||||
|
||||
// Register 代理上游注册(POST /api/app/auth/register),成功后写入登录 cookie。
|
||||
// 请求头带游客 Authorization 时升级该游客;邮箱缺省时用账号生成占位邮箱。
|
||||
func Register(c *gin.Context) {
|
||||
var body registerBody
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 1001,
|
||||
"msg": "参数错误",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
body.AccountNo = strings.TrimSpace(body.AccountNo)
|
||||
body.Email = strings.TrimSpace(body.Email)
|
||||
body.AgentInviteCode = strings.TrimSpace(body.AgentInviteCode)
|
||||
body.DevID = strings.TrimSpace(body.DevID)
|
||||
body.Channel = authser.ResolveChannel(c, body.Channel)
|
||||
|
||||
if body.AccountNo == "" || body.Password == "" {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 1001,
|
||||
"msg": "参数错误",
|
||||
"tip": "请填写账号和密码",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 上游 registerReq 要求 email;前端无邮箱时用账号生成唯一占位邮箱。
|
||||
if body.Email == "" {
|
||||
body.Email = body.AccountNo + "@hgdj.local"
|
||||
}
|
||||
|
||||
env, user, err := authser.Register(c, authser.RegisterReq{
|
||||
AccountNo: body.AccountNo,
|
||||
Email: body.Email,
|
||||
Password: body.Password,
|
||||
AgentInviteCode: body.AgentInviteCode,
|
||||
DevID: body.DevID,
|
||||
Channel: body.Channel,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("register upstream failed", log.E(err))
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "register failed",
|
||||
"tip": "注册失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if env == nil || env.Code != 200 || user == nil {
|
||||
tip := "注册失败"
|
||||
msg := ""
|
||||
code := 500
|
||||
if env != nil {
|
||||
code = env.Code
|
||||
msg = env.Msg
|
||||
if t := strings.TrimSpace(env.Tip); t != "" {
|
||||
tip = t
|
||||
} else if m := strings.TrimSpace(env.Msg); m != "" {
|
||||
tip = m
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"msg": msg,
|
||||
"tip": tip,
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
authser.SetAuthCookies(c, user)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": firstNonEmptyAuth(env.Msg, "success"),
|
||||
"tip": firstNonEmptyAuth(env.Tip, "注册成功"),
|
||||
"data": user,
|
||||
})
|
||||
}
|
||||
|
||||
// Login 代理上游登录(POST /api/app/auth/login),成功后写入登录 cookie。
|
||||
func Login(c *gin.Context) {
|
||||
var body loginBody
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 1001,
|
||||
"msg": "参数错误",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
body.Account = strings.TrimSpace(body.Account)
|
||||
body.DevID = strings.TrimSpace(body.DevID)
|
||||
body.Channel = authser.ResolveChannel(c, body.Channel)
|
||||
if body.Account == "" || body.Password == "" {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 1001,
|
||||
"msg": "参数错误",
|
||||
"tip": "请填写账号和密码",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
env, user, err := authser.Login(c, authser.LoginReq{
|
||||
Account: body.Account,
|
||||
Password: body.Password,
|
||||
DevID: body.DevID,
|
||||
Channel: body.Channel,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("login upstream failed", log.E(err))
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "login failed",
|
||||
"tip": "登录失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if env == nil || env.Code != 200 || user == nil {
|
||||
tip := "登录失败"
|
||||
msg := ""
|
||||
code := 500
|
||||
if env != nil {
|
||||
code = env.Code
|
||||
msg = env.Msg
|
||||
if t := strings.TrimSpace(env.Tip); t != "" {
|
||||
tip = t
|
||||
} else if m := strings.TrimSpace(env.Msg); m != "" {
|
||||
tip = m
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"msg": msg,
|
||||
"tip": tip,
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
authser.SetAuthCookies(c, user)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": firstNonEmptyAuth(env.Msg, "success"),
|
||||
"tip": firstNonEmptyAuth(env.Tip, "登录成功"),
|
||||
"data": user,
|
||||
})
|
||||
}
|
||||
|
||||
// Guest 代理上游游客登录,成功后写入登录 cookie。
|
||||
// 对应 POST /api/app/auth/guest;需传 devID,上游要求 X-User-Agent / X-Real-IP(由 BFF 组装)。
|
||||
func Guest(c *gin.Context) {
|
||||
var body struct {
|
||||
DevID string `json:"devID"`
|
||||
DeviceID string `json:"deviceId"`
|
||||
Mac string `json:"mac"`
|
||||
Channel string `json:"channel"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 1001,
|
||||
"msg": "参数错误",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
body.DevID = strings.TrimSpace(body.DevID)
|
||||
body.DeviceID = strings.TrimSpace(body.DeviceID)
|
||||
body.Mac = strings.TrimSpace(body.Mac)
|
||||
body.Channel = authser.ResolveChannel(c, body.Channel)
|
||||
if body.DevID == "" {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 1001,
|
||||
"msg": "参数错误",
|
||||
"tip": "缺少设备标识",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.DeviceID == "" {
|
||||
body.DeviceID = body.DevID
|
||||
}
|
||||
|
||||
env, user, err := authser.GuestLogin(c, authser.GuestLoginReq{
|
||||
DevID: body.DevID,
|
||||
DeviceID: body.DeviceID,
|
||||
Mac: body.Mac,
|
||||
Channel: body.Channel,
|
||||
})
|
||||
if err != nil {
|
||||
log.Error("guest login upstream failed", log.E(err))
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "guest login failed",
|
||||
"tip": "游客登录失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if env == nil || env.Code != 200 || user == nil {
|
||||
tip := "游客登录失败"
|
||||
msg := ""
|
||||
code := 500
|
||||
if env != nil {
|
||||
code = env.Code
|
||||
msg = env.Msg
|
||||
if t := strings.TrimSpace(env.Tip); t != "" {
|
||||
tip = t
|
||||
} else if m := strings.TrimSpace(env.Msg); m != "" {
|
||||
tip = m
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": code,
|
||||
"msg": msg,
|
||||
"tip": tip,
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
authser.SetAuthCookies(c, user)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": firstNonEmptyAuth(env.Msg, "success"),
|
||||
"tip": firstNonEmptyAuth(env.Tip, "游客登录成功"),
|
||||
"data": user,
|
||||
})
|
||||
}
|
||||
|
||||
// Logout 清除登录 cookie,并标记跳过紧接着的一次自动游客登录。
|
||||
func Logout(c *gin.Context) {
|
||||
authser.ClearAuthCookies(c)
|
||||
authser.MarkGuestSkipAuto(c)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"msg": "success",
|
||||
"tip": "已退出登录",
|
||||
"data": nil,
|
||||
})
|
||||
}
|
||||
|
||||
func firstNonEmptyAuth(values ...string) string {
|
||||
for _, v := range values {
|
||||
if strings.TrimSpace(v) != "" {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"videoweb/app/service/playletser"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ChapterLikeSet BFF:代理 POST /api/app/playlet/chapter/like/set。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/post_api_app_playlet_chapter_like_set
|
||||
func ChapterLikeSet(c *gin.Context) {
|
||||
var body struct {
|
||||
ChapterID int64 `json:"chapterId"`
|
||||
IsLike *bool `json:"isLike"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.ChapterID <= 0 || body.IsLike == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": "chapterId/isLike required",
|
||||
"tip": "缺少剧集 ID 或点赞状态",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
env, err := playletser.ChapterLikeSet(c, playletser.PlayletChapterLikeSetReq{
|
||||
ChapterID: body.ChapterID,
|
||||
IsLike: *body.IsLike,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": env.Tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// ChapterCollectSet BFF:代理 POST /api/app/playlet/chapter/collect/set。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/post_api_app_playlet_chapter_collect_set
|
||||
func ChapterCollectSet(c *gin.Context) {
|
||||
var body struct {
|
||||
ChapterID int64 `json:"chapterId"`
|
||||
IsCollect *bool `json:"isCollect"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.ChapterID <= 0 || body.IsCollect == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": "chapterId/isCollect required",
|
||||
"tip": "缺少剧集 ID 或收藏状态",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
env, err := playletser.ChapterCollectSet(c, playletser.PlayletChapterCollectSetReq{
|
||||
ChapterID: body.ChapterID,
|
||||
IsCollect: *body.IsCollect,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": env.Tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"videoweb/app/service/playletser"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ChapterUnlock BFF:代理 POST /api/app/playlet/chapter/unlock。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/post_api_app_playlet_chapter_unlock
|
||||
func ChapterUnlock(c *gin.Context) {
|
||||
var body struct {
|
||||
ChapterID int64 `json:"chapterId"`
|
||||
PlayletID int64 `json:"playletId"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.ChapterID <= 0 || body.PlayletID <= 0 {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": "chapterId/playletId required",
|
||||
"tip": "缺少剧集或短剧 ID",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
env, err := playletser.ChapterUnlock(c, playletser.PlayletChapterUnlockReq{
|
||||
ChapterID: body.ChapterID,
|
||||
PlayletID: body.PlayletID,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
"tip": "解锁失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "解锁失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": env.Tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"videoweb/app/service/playletser"
|
||||
"videoweb/pub/common/useragent"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// ChapterWatchReport BFF:代理 POST /api/app/playlet/chapter/watch/report。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/post_api_app_playlet_chapter_watch_report
|
||||
func ChapterWatchReport(c *gin.Context) {
|
||||
if useragent.TokenFromRequest(c.Request) == "" {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 401,
|
||||
"msg": "unauthorized",
|
||||
"tip": "请先登录",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var body struct {
|
||||
ChapterID int64 `json:"chapterId"`
|
||||
PlayletID int64 `json:"playletId"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.ChapterID <= 0 {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": "chapterId required",
|
||||
"tip": "缺少剧集 ID",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
env, err := playletser.ChapterWatchReport(c, playletser.PlayletChapterWatchReportReq{
|
||||
ChapterID: body.ChapterID,
|
||||
PlayletID: body.PlayletID,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
"tip": "上报失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "上报失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": env.Tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package web
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"videoweb/app/service/commentser"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -42,114 +41,3 @@ func CommentList(c *gin.Context) {
|
||||
}
|
||||
|
||||
// CommentSend BFF:代理 POST /api/app/comment/send。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/评论APP/post_api_app_comment_send
|
||||
func CommentSend(c *gin.Context) {
|
||||
var req commentser.CommentSendReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
env, err := commentser.Send(c, req)
|
||||
if err != nil {
|
||||
tip := "发布失败"
|
||||
msg := err.Error()
|
||||
switch {
|
||||
case strings.Contains(msg, "empty text"):
|
||||
tip = "请输入评论内容"
|
||||
case strings.Contains(msg, "too long"):
|
||||
tip = "评论不能超过 200 字"
|
||||
case strings.Contains(msg, "invalid objectId"):
|
||||
tip = "缺少评论对象"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": msg,
|
||||
"tip": tip,
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "发布失败",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
tip := env.Tip
|
||||
if tip == "" {
|
||||
tip = env.Msg
|
||||
}
|
||||
// 上游参数错误兜底提示(例如 objectType 不被支持)
|
||||
if env.Code == 1001 && (tip == "" || tip == "参数错误") {
|
||||
tip = "评论参数错误,请稍后重试"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
|
||||
// CommentLikeSet BFF:代理 POST /api/app/comment/like/set。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/评论APP/post_api_app_comment_like_set
|
||||
func CommentLikeSet(c *gin.Context) {
|
||||
var body struct {
|
||||
CommentID int64 `json:"commentId"`
|
||||
IsLike *bool `json:"isLike"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.CommentID <= 0 || body.IsLike == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": "commentId/isLike required",
|
||||
"tip": "缺少评论 ID 或点赞状态",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
env, err := commentser.LikeSet(c, commentser.CommentLikeSetReq{
|
||||
CommentID: body.CommentID,
|
||||
IsLike: *body.IsLike,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": env.Tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,338 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"videoweb/app/service/authser"
|
||||
"videoweb/app/service/playletser"
|
||||
"videoweb/app/service/videoser"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/common/useragent"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type favoriteWork struct {
|
||||
ID int64
|
||||
Title string
|
||||
Genre string
|
||||
DesktopMeta string
|
||||
Episodes string
|
||||
Badge string
|
||||
Image string
|
||||
Kind string
|
||||
URL string
|
||||
DramaID int64
|
||||
Episode int
|
||||
VideoID int64
|
||||
TotalEpisode int
|
||||
}
|
||||
|
||||
// Favorites 我的收藏:短剧 / 剧集(视频) / 章节收藏。
|
||||
// 文档:
|
||||
// http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/get_api_app_playlet_collect_list
|
||||
// http://172.104.181.99:25319/swagger/index.html#/视频相关接口/get_api_app_video_collect_list
|
||||
func Favorites(c *gin.Context) {
|
||||
filter, page, validRoute := favoritesRoute(c)
|
||||
if !validRoute {
|
||||
NotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
data := gin.H{"title": "我的收藏 - 黄果短剧", "navActive": "favorites", "accountTab": "favorites"}
|
||||
responses := gin.H{}
|
||||
loadAccountSummary(c, data, responses)
|
||||
|
||||
status := 0
|
||||
switch filter {
|
||||
case "short", "chapter", "series":
|
||||
case "following":
|
||||
status = 1
|
||||
case "finished":
|
||||
status = 2
|
||||
default:
|
||||
filter = "all"
|
||||
}
|
||||
data["filter"] = filter
|
||||
|
||||
pageSize := 12
|
||||
query := playletser.CollectListQuery{PageNum: page, PageSize: pageSize, WatchStatus: status}
|
||||
|
||||
if useragent.TokenFromRequest(c.Request) == "" {
|
||||
responses["playlet/collect/list"] = gin.H{"error": "暂无登录会话"}
|
||||
responses["video/collect/list"] = gin.H{"error": "暂无登录会话"}
|
||||
data["favoritesResponse"] = responses["playlet/collect/list"]
|
||||
data["playletCollectResponse"] = responses["playlet/collect/list"]
|
||||
data["videoCollectResponse"] = responses["video/collect/list"]
|
||||
data["favoritesDebug"] = responses
|
||||
renderFavorites(c, data)
|
||||
return
|
||||
}
|
||||
|
||||
if filter == "chapter" {
|
||||
list, code, err := playletser.FetchChapterCollectList(c, query)
|
||||
if retry, _ := authser.OnSessionExpired(c, code); retry {
|
||||
list, _, err = playletser.FetchChapterCollectList(c, query)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("fetch chapter favorites page failed", log.E(err))
|
||||
responses["playlet/chapter/collect/list"] = gin.H{"error": "加载失败", "msg": err.Error()}
|
||||
data["favoritesUnavailable"] = true
|
||||
} else if list != nil {
|
||||
responses["playlet/chapter/collect/list"] = gin.H{"code": 200, "data": list}
|
||||
data["paging"] = makeFavoritesPage(query.PageNum, query.PageSize, list.TotalCount, filter)
|
||||
data["favorites"] = chapterFavoriteWorks(list.List)
|
||||
} else {
|
||||
responses["playlet/chapter/collect/list"] = gin.H{"code": code, "data": list}
|
||||
}
|
||||
data["favoritesResponse"] = responses["playlet/chapter/collect/list"]
|
||||
data["playletCollectResponse"] = responses["playlet/chapter/collect/list"]
|
||||
data["favoritesDebug"] = responses
|
||||
renderFavorites(c, data)
|
||||
return
|
||||
}
|
||||
|
||||
if filter == "series" {
|
||||
loadVideoFavorites(c, data, responses, page, pageSize)
|
||||
data["favoritesResponse"] = responses["video/collect/list"]
|
||||
data["playletCollectResponse"] = gin.H{}
|
||||
data["videoCollectResponse"] = responses["video/collect/list"]
|
||||
data["favoritesDebug"] = responses
|
||||
renderFavorites(c, data)
|
||||
return
|
||||
}
|
||||
|
||||
var works []favoriteWork
|
||||
var totalCount int64
|
||||
playletFailed := false
|
||||
videoFailed := false
|
||||
|
||||
if filter == "all" || filter == "short" || filter == "following" || filter == "finished" {
|
||||
list, code, err := playletser.FetchCollectList(c, query)
|
||||
if retry, _ := authser.OnSessionExpired(c, code); retry {
|
||||
list, code, err = playletser.FetchCollectList(c, query)
|
||||
}
|
||||
if err == nil && list != nil {
|
||||
responses["playlet/collect/list"] = gin.H{"code": 200, "data": list}
|
||||
works = append(works, playletFavoriteWorks(list.List)...)
|
||||
totalCount += list.TotalCount
|
||||
} else if err != nil {
|
||||
responses["playlet/collect/list"] = gin.H{"error": "加载失败", "msg": err.Error()}
|
||||
log.Error("fetch favorites page failed", log.E(err))
|
||||
playletFailed = true
|
||||
} else {
|
||||
responses["playlet/collect/list"] = gin.H{"code": code, "data": list}
|
||||
}
|
||||
}
|
||||
|
||||
if filter == "all" {
|
||||
videoQuery := videoser.CollectListQuery{PageNum: page, PageSize: pageSize}
|
||||
videoList, videoCode, videoErr := videoser.FetchCollectList(c, videoQuery)
|
||||
if retry, _ := authser.OnSessionExpired(c, videoCode); retry {
|
||||
videoList, videoCode, videoErr = videoser.FetchCollectList(c, videoQuery)
|
||||
}
|
||||
if videoErr == nil && videoList != nil {
|
||||
responses["video/collect/list"] = gin.H{"code": 200, "data": videoList}
|
||||
works = append(works, videoFavoriteWorks(videoList.List)...)
|
||||
totalCount += videoList.TotalCount
|
||||
} else if videoErr != nil {
|
||||
responses["video/collect/list"] = gin.H{"error": "加载失败", "msg": videoErr.Error()}
|
||||
log.Error("fetch video favorites page failed", log.E(videoErr))
|
||||
videoFailed = true
|
||||
} else {
|
||||
responses["video/collect/list"] = gin.H{"code": videoCode, "data": videoList}
|
||||
}
|
||||
}
|
||||
|
||||
if playletFailed && (filter != "all" || videoFailed) {
|
||||
data["favoritesUnavailable"] = true
|
||||
}
|
||||
if filter == "all" && playletFailed && videoFailed {
|
||||
data["favoritesUnavailable"] = true
|
||||
}
|
||||
|
||||
data["paging"] = makeFavoritesPage(page, pageSize, totalCount, filter)
|
||||
data["favorites"] = works
|
||||
data["favoritesResponse"] = responses["playlet/collect/list"]
|
||||
data["playletCollectResponse"] = responses["playlet/collect/list"]
|
||||
data["videoCollectResponse"] = responses["video/collect/list"]
|
||||
data["favoritesDebug"] = responses
|
||||
renderFavorites(c, data)
|
||||
}
|
||||
|
||||
// renderFavorites 收藏为空时附带「大家都在看」后渲染。
|
||||
func renderFavorites(c *gin.Context, data gin.H) {
|
||||
attachFavoritesRecommended(c, data)
|
||||
HTMLOK(c, "favorites.html", data)
|
||||
}
|
||||
|
||||
// attachFavoritesRecommended 列表为空时拉取最热短剧(与搜索无结果一致)。
|
||||
// 上游:GET /api/app/playlet/all/list sortType=2
|
||||
func attachFavoritesRecommended(c *gin.Context, data gin.H) {
|
||||
if works, ok := data["favorites"].([]favoriteWork); ok && len(works) > 0 {
|
||||
return
|
||||
}
|
||||
watchingQ := playletser.AllListQuery{
|
||||
PageNum: 1,
|
||||
PageSize: 6,
|
||||
SortType: 2, // 最热
|
||||
UpdateStatus: 0,
|
||||
}
|
||||
watching := playletser.FetchAllListOrEmpty(c, watchingQ)
|
||||
if watching == nil || len(watching.List) == 0 {
|
||||
return
|
||||
}
|
||||
list := watching.List
|
||||
if len(list) > 6 {
|
||||
list = list[:6]
|
||||
}
|
||||
data["recommended"] = list
|
||||
}
|
||||
|
||||
func loadVideoFavorites(c *gin.Context, data gin.H, responses gin.H, page, pageSize int) {
|
||||
videoQuery := videoser.CollectListQuery{PageNum: page, PageSize: pageSize}
|
||||
list, code, err := videoser.FetchCollectList(c, videoQuery)
|
||||
if retry, _ := authser.OnSessionExpired(c, code); retry {
|
||||
list, code, err = videoser.FetchCollectList(c, videoQuery)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("fetch video favorites page failed", log.E(err))
|
||||
responses["video/collect/list"] = gin.H{"error": "加载失败", "msg": err.Error()}
|
||||
data["favoritesUnavailable"] = true
|
||||
return
|
||||
}
|
||||
if list == nil {
|
||||
responses["video/collect/list"] = gin.H{"code": code, "data": list}
|
||||
return
|
||||
}
|
||||
responses["video/collect/list"] = gin.H{"code": 200, "data": list}
|
||||
data["paging"] = makeFavoritesPage(page, pageSize, list.TotalCount, "series")
|
||||
data["favorites"] = videoFavoriteWorks(list.List)
|
||||
}
|
||||
|
||||
func favoritesRoute(c *gin.Context) (filter string, page int, valid bool) {
|
||||
if c.Request.URL.Query().Has("filter") {
|
||||
return "", 0, false
|
||||
}
|
||||
filter = strings.TrimSpace(c.Param("filter"))
|
||||
switch filter {
|
||||
case "":
|
||||
filter = "all"
|
||||
case "short", "chapter", "series", "following", "finished":
|
||||
default:
|
||||
return "", 0, false
|
||||
}
|
||||
|
||||
page, valid = accountPageNum(c)
|
||||
if !valid {
|
||||
return "", 0, false
|
||||
}
|
||||
return filter, page, true
|
||||
}
|
||||
|
||||
func favoritesPagePath(filter string, page int) string {
|
||||
path := "/favorites"
|
||||
if filter != "" && filter != "all" {
|
||||
path += "/" + filter
|
||||
}
|
||||
if page > 1 {
|
||||
path += "/page/" + strconv.Itoa(page)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func makeFavoritesPage(page, size int, count int64, filter string) accountPage {
|
||||
total := int((count + int64(size) - 1) / int64(size))
|
||||
pagination := accountPage{Current: page, Total: total, Count: count}
|
||||
if page > 1 {
|
||||
pagination.PrevURL = favoritesPagePath(filter, page-1)
|
||||
}
|
||||
if page < total {
|
||||
pagination.NextURL = favoritesPagePath(filter, page+1)
|
||||
}
|
||||
return pagination
|
||||
}
|
||||
|
||||
func playletFavoriteWorks(items []playletser.PlayletCollectData) []favoriteWork {
|
||||
works := make([]favoriteWork, 0, len(items))
|
||||
for _, item := range items {
|
||||
title := strings.TrimSpace(item.PlayName)
|
||||
if title == "" {
|
||||
title = strings.TrimSpace(item.Title)
|
||||
}
|
||||
if title == "" {
|
||||
continue
|
||||
}
|
||||
work := favoriteWork{
|
||||
ID: item.ID, Title: title, Genre: strings.Join(item.ThemeNames, " "),
|
||||
Image: profileCover(item.Cover), Kind: "short",
|
||||
URL: playletser.ChapterPlayPath(item.Slug, item.ID, item.LastChapterID),
|
||||
DramaID: item.ID, Episode: item.LastEpisode, VideoID: item.LastChapterID,
|
||||
TotalEpisode: item.TotalEpisode,
|
||||
}
|
||||
if work.Episode <= 0 {
|
||||
work.Episode = 1
|
||||
}
|
||||
if item.TotalEpisode > 0 {
|
||||
work.Episodes = fmt.Sprintf("全 %d 集", item.TotalEpisode)
|
||||
work.DesktopMeta = work.Episodes
|
||||
}
|
||||
if item.IsFeatured {
|
||||
work.Badge = "爆剧"
|
||||
}
|
||||
works = append(works, work)
|
||||
}
|
||||
return works
|
||||
}
|
||||
|
||||
func chapterFavoriteWorks(items []playletser.PlayletChapterCollectData) []favoriteWork {
|
||||
works := make([]favoriteWork, 0, len(items))
|
||||
for _, item := range items {
|
||||
title := strings.TrimSpace(item.PlayletTitle)
|
||||
if title == "" {
|
||||
continue
|
||||
}
|
||||
work := favoriteWork{
|
||||
ID: item.ChapterID, Title: title, Image: profileCover(item.PlayletCover), Kind: "chapter",
|
||||
URL: playletser.ChapterPlayPath(item.PlayletSlug, item.PlayletID, item.ChapterID), DesktopMeta: item.ChapterTitle,
|
||||
DramaID: item.PlayletID, Episode: item.CurrentEpisode, VideoID: item.ChapterID,
|
||||
TotalEpisode: item.TotalEpisode,
|
||||
}
|
||||
if work.Episode <= 0 {
|
||||
work.Episode = 1
|
||||
}
|
||||
if item.CurrentEpisode > 0 {
|
||||
work.Genre = fmt.Sprintf("第 %d 集", item.CurrentEpisode)
|
||||
}
|
||||
if item.TotalEpisode > 0 {
|
||||
work.Episodes = fmt.Sprintf("全 %d 集", item.TotalEpisode)
|
||||
}
|
||||
works = append(works, work)
|
||||
}
|
||||
return works
|
||||
}
|
||||
|
||||
func videoFavoriteWorks(items []videoser.VideoCollectData) []favoriteWork {
|
||||
works := make([]favoriteWork, 0, len(items))
|
||||
for _, item := range items {
|
||||
title := strings.TrimSpace(item.Title)
|
||||
if title == "" {
|
||||
continue
|
||||
}
|
||||
work := favoriteWork{
|
||||
ID: item.ID, Title: title, Genre: strings.Join(item.Keyworld, " "),
|
||||
Image: profileCover(item.CoverImg), Kind: "series",
|
||||
URL: videoser.VideoPlayURL(item.Slug, item.ID),
|
||||
}
|
||||
if item.VideoTime > 0 {
|
||||
work.Episodes = formatVideoDuration(item.VideoTime)
|
||||
work.DesktopMeta = work.Episodes
|
||||
} else {
|
||||
work.Episodes = "剧集"
|
||||
work.DesktopMeta = "剧集"
|
||||
}
|
||||
works = append(works, work)
|
||||
}
|
||||
return works
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"videoweb/app/service/authser"
|
||||
"videoweb/app/service/feedbackser"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -113,7 +112,7 @@ func UploadImage(c *gin.Context) {
|
||||
if name == "" || name == "." || name == "/" {
|
||||
name = "image.jpg"
|
||||
}
|
||||
resp, err := authser.UploadImage(c, name, f)
|
||||
resp, err := feedbackser.UploadImage(c, name, f)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
|
||||
@@ -1,375 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
"videoweb/app/service/authser"
|
||||
"videoweb/app/service/playletser"
|
||||
"videoweb/app/service/videoser"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/common/useragent"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type historyEntry struct {
|
||||
ID int64
|
||||
Title string
|
||||
Current int
|
||||
Total int
|
||||
EpisodeText string
|
||||
Genre string
|
||||
Time string
|
||||
Image string
|
||||
Progress string
|
||||
Kind string
|
||||
URL string
|
||||
sortAt time.Time
|
||||
}
|
||||
|
||||
type historyGroup struct {
|
||||
Title string
|
||||
Entries []historyEntry
|
||||
}
|
||||
|
||||
// History 观看历史页:短剧 GET /api/app/playlet/history/list + 剧集 GET /api/app/video/history/list。
|
||||
// 文档:
|
||||
// http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/get_api_app_playlet_history_list
|
||||
// http://172.104.181.99:25319/swagger/index.html#/视频相关接口/get_api_app_video_history_list
|
||||
func History(c *gin.Context) {
|
||||
page, validPage := accountPageNum(c)
|
||||
if !validPage {
|
||||
NotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
data := gin.H{"title": "观看历史 - 黄果短剧", "navActive": "history", "accountTab": "history"}
|
||||
responses := gin.H{}
|
||||
loadAccountSummary(c, data, responses)
|
||||
|
||||
pageSize := 20
|
||||
var entries []historyEntry
|
||||
var totalCount int64
|
||||
|
||||
if useragent.TokenFromRequest(c.Request) == "" {
|
||||
responses["playlet/history/list"] = gin.H{"error": "暂无登录会话"}
|
||||
responses["video/history/list"] = gin.H{"error": "暂无登录会话"}
|
||||
} else {
|
||||
playletQuery := playletser.HistoryListQuery{PageNum: page, PageSize: pageSize}
|
||||
playletList, playletCode, playletErr := playletser.FetchHistoryList(c, playletQuery)
|
||||
if retry, _ := authser.OnSessionExpired(c, playletCode); retry {
|
||||
playletList, playletCode, playletErr = playletser.FetchHistoryList(c, playletQuery)
|
||||
}
|
||||
if playletErr == nil && playletList != nil {
|
||||
responses["playlet/history/list"] = gin.H{"code": 200, "data": playletList}
|
||||
entries = append(entries, playletHistoryEntries(playletList.List)...)
|
||||
totalCount += playletList.TotalCount
|
||||
} else if playletErr != nil {
|
||||
responses["playlet/history/list"] = gin.H{"error": "加载失败", "msg": playletErr.Error()}
|
||||
log.Error("fetch playlet history page failed", log.E(playletErr))
|
||||
} else {
|
||||
responses["playlet/history/list"] = gin.H{"code": playletCode, "data": playletList}
|
||||
}
|
||||
|
||||
videoQuery := videoser.HistoryListQuery{PageNum: page, PageSize: pageSize}
|
||||
videoList, videoCode, videoErr := videoser.FetchHistoryList(c, videoQuery)
|
||||
if retry, _ := authser.OnSessionExpired(c, videoCode); retry {
|
||||
videoList, videoCode, videoErr = videoser.FetchHistoryList(c, videoQuery)
|
||||
}
|
||||
if videoErr == nil && videoList != nil {
|
||||
responses["video/history/list"] = gin.H{"code": 200, "data": videoList}
|
||||
entries = append(entries, videoHistoryEntries(videoList.List)...)
|
||||
totalCount += videoList.TotalCount
|
||||
} else if videoErr != nil {
|
||||
responses["video/history/list"] = gin.H{"error": "加载失败", "msg": videoErr.Error()}
|
||||
log.Error("fetch video history page failed", log.E(videoErr))
|
||||
} else {
|
||||
responses["video/history/list"] = gin.H{"code": videoCode, "data": videoList}
|
||||
}
|
||||
|
||||
if playletErr != nil && videoErr != nil {
|
||||
data["historyUnavailable"] = true
|
||||
}
|
||||
}
|
||||
|
||||
data["paging"] = makeAccountPage("/history", page, pageSize, totalCount)
|
||||
data["groups"] = groupHistoryEntries(entries)
|
||||
data["historyResponse"] = responses["playlet/history/list"]
|
||||
data["playletHistoryResponse"] = responses["playlet/history/list"]
|
||||
data["videoHistoryResponse"] = responses["video/history/list"]
|
||||
data["historyDebug"] = responses
|
||||
HTMLOK(c, "history.html", data)
|
||||
}
|
||||
|
||||
// HistoryClear BFF:代理 POST /api/app/playlet/history/clear。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/post_api_app_playlet_history_clear
|
||||
func HistoryClear(c *gin.Context) {
|
||||
if useragent.TokenFromRequest(c.Request) == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 401, "msg": "unauthorized", "tip": "请先登录", "data": nil})
|
||||
return
|
||||
}
|
||||
env, err := playletser.HistoryClear(c)
|
||||
if err != nil {
|
||||
log.Error("clear playlet history failed", log.E(err))
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error(), "tip": "清空失败,请稍后重试", "data": nil})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": "empty response", "tip": "清空失败,请稍后重试", "data": nil})
|
||||
return
|
||||
}
|
||||
if env.Code != 200 {
|
||||
tip := strings.TrimSpace(env.Tip)
|
||||
if tip == "" {
|
||||
tip = strings.TrimSpace(env.Msg)
|
||||
}
|
||||
if tip == "" {
|
||||
tip = "清空失败,请稍后重试"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": env.Code, "msg": env.Msg, "tip": tip, "data": env.Data})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "ok", "tip": "", "data": env.Data})
|
||||
}
|
||||
|
||||
// VideoHistoryClear BFF:代理 POST /api/app/video/history/clear。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/视频相关接口/post_api_app_video_history_clear
|
||||
func VideoHistoryClear(c *gin.Context) {
|
||||
if useragent.TokenFromRequest(c.Request) == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 401, "msg": "unauthorized", "tip": "请先登录", "data": nil})
|
||||
return
|
||||
}
|
||||
env, err := videoser.HistoryClear(c)
|
||||
if err != nil {
|
||||
log.Error("clear video history failed", log.E(err))
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error(), "tip": "清空失败,请稍后重试", "data": nil})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": "empty response", "tip": "清空失败,请稍后重试", "data": nil})
|
||||
return
|
||||
}
|
||||
if env.Code != 200 {
|
||||
tip := strings.TrimSpace(env.Tip)
|
||||
if tip == "" {
|
||||
tip = strings.TrimSpace(env.Msg)
|
||||
}
|
||||
if tip == "" {
|
||||
tip = "清空失败,请稍后重试"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": env.Code, "msg": env.Msg, "tip": tip, "data": env.Data})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "ok", "tip": "", "data": env.Data})
|
||||
}
|
||||
|
||||
// HistoryDelete BFF:代理 POST /api/app/playlet/history/delete。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/post_api_app_playlet_history_delete
|
||||
func HistoryDelete(c *gin.Context) {
|
||||
if useragent.TokenFromRequest(c.Request) == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 401, "msg": "unauthorized", "tip": "请先登录", "data": nil})
|
||||
return
|
||||
}
|
||||
var body playletser.PlayletHistoryDeleteReq
|
||||
if err := c.ShouldBindJSON(&body); err != nil || body.PlayletID <= 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 400, "msg": "playletId required", "tip": "参数错误", "data": nil})
|
||||
return
|
||||
}
|
||||
env, err := playletser.HistoryDelete(c, body)
|
||||
if err != nil {
|
||||
log.Error("delete playlet history failed", log.E(err))
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error(), "tip": "删除失败,请稍后重试", "data": nil})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": "empty response", "tip": "删除失败,请稍后重试", "data": nil})
|
||||
return
|
||||
}
|
||||
if env.Code != 200 {
|
||||
tip := strings.TrimSpace(env.Tip)
|
||||
if tip == "" {
|
||||
tip = strings.TrimSpace(env.Msg)
|
||||
}
|
||||
if tip == "" {
|
||||
tip = "删除失败,请稍后重试"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": env.Code, "msg": env.Msg, "tip": tip, "data": env.Data})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "ok", "tip": "", "data": env.Data})
|
||||
}
|
||||
|
||||
// VideoHistoryDelete BFF:代理 POST /api/app/video/history/delete。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/视频相关接口/post_api_app_video_history_delete
|
||||
func VideoHistoryDelete(c *gin.Context) {
|
||||
if useragent.TokenFromRequest(c.Request) == "" {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 401, "msg": "unauthorized", "tip": "请先登录", "data": nil})
|
||||
return
|
||||
}
|
||||
var body videoser.VideoHistoryDeleteReq
|
||||
if err := c.ShouldBindJSON(&body); err != nil || body.VideoID <= 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 400, "msg": "videoId required", "tip": "参数错误", "data": nil})
|
||||
return
|
||||
}
|
||||
env, err := videoser.HistoryDelete(c, body)
|
||||
if err != nil {
|
||||
log.Error("delete video history failed", log.E(err))
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": err.Error(), "tip": "删除失败,请稍后重试", "data": nil})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"code": 500, "msg": "empty response", "tip": "删除失败,请稍后重试", "data": nil})
|
||||
return
|
||||
}
|
||||
if env.Code != 200 {
|
||||
tip := strings.TrimSpace(env.Tip)
|
||||
if tip == "" {
|
||||
tip = strings.TrimSpace(env.Msg)
|
||||
}
|
||||
if tip == "" {
|
||||
tip = "删除失败,请稍后重试"
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": env.Code, "msg": env.Msg, "tip": tip, "data": env.Data})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "ok", "tip": "", "data": env.Data})
|
||||
}
|
||||
|
||||
func playletHistoryEntries(items []playletser.PlayletHistoryData) []historyEntry {
|
||||
out := make([]historyEntry, 0, len(items))
|
||||
for _, item := range items {
|
||||
title := strings.TrimSpace(item.PlayName)
|
||||
if title == "" {
|
||||
title = strings.TrimSpace(item.Title)
|
||||
}
|
||||
if title == "" {
|
||||
continue
|
||||
}
|
||||
progress := 0
|
||||
if item.TotalEpisode > 0 {
|
||||
progress = item.WatchedCount * 100 / item.TotalEpisode
|
||||
}
|
||||
if progress < 0 {
|
||||
progress = 0
|
||||
}
|
||||
if progress > 100 {
|
||||
progress = 100
|
||||
}
|
||||
out = append(out, historyEntry{
|
||||
ID: item.ID,
|
||||
Title: title,
|
||||
Current: item.LastEpisode,
|
||||
Total: item.TotalEpisode,
|
||||
Genre: strings.Join(item.ThemeNames, " "),
|
||||
Time: playletser.FormatHistoryListTime(item.HistoryAt),
|
||||
Image: profileCover(item.Cover),
|
||||
Progress: fmt.Sprintf("%d%%", progress),
|
||||
Kind: "short",
|
||||
URL: playletser.ChapterPlayPath(item.Slug, item.ID, item.LastChapterID),
|
||||
sortAt: historySortAt(item.HistoryAt),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func videoHistoryEntries(items []videoser.VideoHistoryData) []historyEntry {
|
||||
out := make([]historyEntry, 0, len(items))
|
||||
for _, item := range items {
|
||||
title := strings.TrimSpace(item.Title)
|
||||
if title == "" {
|
||||
continue
|
||||
}
|
||||
out = append(out, historyEntry{
|
||||
ID: item.ID,
|
||||
Title: title,
|
||||
EpisodeText: videoHistoryEpisodeText(item),
|
||||
Genre: strings.Join(item.Keyworld, " "),
|
||||
Time: playletser.FormatHistoryListTime(item.HistoryAt),
|
||||
Image: profileCover(item.CoverImg),
|
||||
Progress: "",
|
||||
Kind: "series",
|
||||
URL: videoser.VideoPlayURL(item.Slug, item.ID),
|
||||
sortAt: historySortAt(item.HistoryAt),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func videoHistoryEpisodeText(item videoser.VideoHistoryData) string {
|
||||
if item.VideoTime > 0 {
|
||||
return formatVideoDuration(item.VideoTime)
|
||||
}
|
||||
return "剧集"
|
||||
}
|
||||
|
||||
func formatVideoDuration(sec int) string {
|
||||
if sec <= 0 {
|
||||
return ""
|
||||
}
|
||||
h := sec / 3600
|
||||
m := (sec % 3600) / 60
|
||||
s := sec % 60
|
||||
if h > 0 {
|
||||
return fmt.Sprintf("%d:%02d:%02d", h, m, s)
|
||||
}
|
||||
return fmt.Sprintf("%d:%02d", m, s)
|
||||
}
|
||||
|
||||
func historySortAt(raw string) time.Time {
|
||||
t, ok := playletser.ParseHistoryTime(raw)
|
||||
if !ok {
|
||||
return time.Time{}
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func groupHistoryEntries(entries []historyEntry) []historyGroup {
|
||||
if len(entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
sort.SliceStable(entries, func(i, j int) bool {
|
||||
if entries[i].sortAt.Equal(entries[j].sortAt) {
|
||||
return entries[i].ID > entries[j].ID
|
||||
}
|
||||
return entries[i].sortAt.After(entries[j].sortAt)
|
||||
})
|
||||
order := []string{"今天", "昨天", "更早"}
|
||||
buckets := map[string][]historyEntry{
|
||||
"今天": {},
|
||||
"昨天": {},
|
||||
"更早": {},
|
||||
}
|
||||
for _, entry := range entries {
|
||||
bucket := historyBucketFromTime(entry.sortAt)
|
||||
buckets[bucket] = append(buckets[bucket], entry)
|
||||
}
|
||||
groups := make([]historyGroup, 0, 3)
|
||||
for _, title := range order {
|
||||
if len(buckets[title]) == 0 {
|
||||
continue
|
||||
}
|
||||
groups = append(groups, historyGroup{Title: title, Entries: buckets[title]})
|
||||
}
|
||||
return groups
|
||||
}
|
||||
|
||||
func historyBucketFromTime(t time.Time) string {
|
||||
if t.IsZero() {
|
||||
return "更早"
|
||||
}
|
||||
now := time.Now()
|
||||
startToday := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
startThat := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
|
||||
dayDiff := int(startToday.Sub(startThat).Hours() / 24)
|
||||
switch {
|
||||
case dayDiff <= 0:
|
||||
return "今天"
|
||||
case dayDiff == 1:
|
||||
return "昨天"
|
||||
default:
|
||||
return "更早"
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
// Index 首页 SSR:拉取 GET /api/app/playlet/home,按接口字段注入模板。
|
||||
// 接口文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/get_api_app_playlet_home
|
||||
// 继续观看:GET /api/app/playlet/history/list?pageNum=1&pageSize=6
|
||||
// 热播榜:短剧 hot/rank/list;剧集 video/all/list sortType=最热
|
||||
func Index(c *gin.Context) {
|
||||
raw := playletser.FetchHomeOrEmpty(c)
|
||||
@@ -17,12 +16,6 @@ func Index(c *gin.Context) {
|
||||
if raw != nil && raw.SeoInfo.Title != "" {
|
||||
title = raw.SeoInfo.Title
|
||||
}
|
||||
history := playletser.FetchHistoryListOrEmpty(c, playletser.HistoryListQuery{PageNum: 1, PageSize: 6})
|
||||
continueWatch := history.List
|
||||
if len(continueWatch) > 6 {
|
||||
continueWatch = continueWatch[:6]
|
||||
}
|
||||
|
||||
hotPlaylets := playletser.FetchHotRankListOrEmpty(c, playletser.HotRankQuery{
|
||||
PageNum: 1,
|
||||
PageSize: 10,
|
||||
@@ -52,7 +45,6 @@ func Index(c *gin.Context) {
|
||||
"homeConfigData": playletser.ActiveHomeConfigs(raw.HomeConfigData),
|
||||
// GET /api/app/playlet/tag/options → list(已上架标签)
|
||||
"tagOptions": playletser.FetchTagOptionsOrEmpty(c),
|
||||
"continueWatch": continueWatch,
|
||||
"hotRankPlaylets": hotPlaylets,
|
||||
"hotRankVideos": hotVideos,
|
||||
})
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package web
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// LoginPage H5 登录页:渲染 template/pages/login.html。
|
||||
// PC 端由页面内脚本回首页并打开 loginPop;H5 直接展示本页。
|
||||
func LoginPage(c *gin.Context) {
|
||||
HTMLOK(c, "login.html", gin.H{
|
||||
"title": "登录 - 黄果短剧",
|
||||
})
|
||||
}
|
||||
@@ -1,9 +1,7 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"videoweb/app/service/authser"
|
||||
"videoweb/app/service/commentser"
|
||||
"videoweb/app/service/playletser"
|
||||
"videoweb/app/service/videoser"
|
||||
@@ -92,13 +90,9 @@ func Play(c *gin.Context) {
|
||||
SortOrder: commentser.SortHot,
|
||||
})
|
||||
|
||||
unlockCost := int64(0)
|
||||
balance := int64(0)
|
||||
canWatch := false
|
||||
episode := 0
|
||||
if chapter != nil {
|
||||
unlockCost = chapter.UnlockCost()
|
||||
balance = chapter.Balance
|
||||
canWatch = chapter.AllowWatch()
|
||||
episode = chapter.CurrentEpisode
|
||||
}
|
||||
@@ -117,9 +111,6 @@ func Play(c *gin.Context) {
|
||||
if episode <= 0 {
|
||||
episode = 1
|
||||
}
|
||||
if unlockCost <= 0 {
|
||||
unlockCost = int64(playletser.FirstUnlockCost(playlet.PlayletChapterInfo))
|
||||
}
|
||||
|
||||
previewURL := strings.TrimSpace(playlet.Preview)
|
||||
mainURL := ""
|
||||
@@ -139,9 +130,6 @@ func Play(c *gin.Context) {
|
||||
"mainURL": mainURL,
|
||||
"previewURL": previewURL,
|
||||
"hasPreview": previewURL != "",
|
||||
"unlockCost": unlockCost,
|
||||
"balance": balance,
|
||||
"balanceText": authser.FormatRecharge(balance),
|
||||
"nextChapterID": playletser.NextChapterID(playlet, chapterID),
|
||||
"advitise": playlet.Advitise,
|
||||
"related": playlet.RelatedRecommendData,
|
||||
@@ -157,7 +145,6 @@ func Play(c *gin.Context) {
|
||||
"commentSize": comments.PageSize,
|
||||
"commentObjectID": commentObjectID,
|
||||
"commentObjectType": commentObjectType,
|
||||
"trackerDrama": dramaTrackingData(playlet, chapter, chapterID, episode),
|
||||
"playResponse": gin.H{
|
||||
"playlet": playlet,
|
||||
"chapter": chapter,
|
||||
@@ -174,76 +161,9 @@ func Play(c *gin.Context) {
|
||||
},
|
||||
}
|
||||
data["canonicalPath"] = playletser.PlayPath(playlet.Slug, playlet.ID)
|
||||
attachRechargeModal(c, data)
|
||||
HTMLOK(c, "play.html", data)
|
||||
}
|
||||
|
||||
func dramaTrackingData(playlet *playletser.PlayletInfoResp, chapter *playletser.PlayletChapterInfoResp, chapterID int64, episode int) gin.H {
|
||||
name := strings.TrimSpace(playlet.PlayName)
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(playlet.Title)
|
||||
}
|
||||
totalEpisode := playlet.TotalEpisode
|
||||
if totalEpisode <= 0 {
|
||||
totalEpisode = len(playlet.PlayletChapterInfo)
|
||||
}
|
||||
if totalEpisode <= 0 {
|
||||
totalEpisode = 1
|
||||
}
|
||||
data := gin.H{
|
||||
"MediaID": "",
|
||||
"DramaID": playlet.ID,
|
||||
"DramaTitle": name,
|
||||
"Episode": max(1, episode),
|
||||
"TotalEpisode": totalEpisode,
|
||||
"VideoID": chapterID,
|
||||
"DramaTypeID": "default",
|
||||
"DramaTypeName": "默认分类",
|
||||
"DramaTagKey": "default",
|
||||
"DramaTagName": "默认标签",
|
||||
"DramaDuration": 0,
|
||||
"RecommendTraceID": "",
|
||||
}
|
||||
if chapter != nil {
|
||||
if chapter.ID > 0 {
|
||||
data["VideoID"] = chapter.ID
|
||||
}
|
||||
if chapter.MediaTime > 0 {
|
||||
data["DramaDuration"] = chapter.MediaTime
|
||||
}
|
||||
}
|
||||
for i, id := range playlet.ThemeIDs {
|
||||
if id <= 0 || i >= len(playlet.ThemeNames) {
|
||||
continue
|
||||
}
|
||||
name := strings.TrimSpace(playlet.ThemeNames[i])
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
data["DramaTypeID"] = strconv.FormatInt(id, 10)
|
||||
data["DramaTypeName"] = name
|
||||
break
|
||||
}
|
||||
tagIDs := make([]string, 0, len(playlet.TagIDs))
|
||||
tagNames := make([]string, 0, len(playlet.TagIDs))
|
||||
for i, id := range playlet.TagIDs {
|
||||
if id <= 0 || i >= len(playlet.TagNames) {
|
||||
continue
|
||||
}
|
||||
name := strings.TrimSpace(playlet.TagNames[i])
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
tagIDs = append(tagIDs, strconv.FormatInt(id, 10))
|
||||
tagNames = append(tagNames, name)
|
||||
}
|
||||
if len(tagIDs) > 0 {
|
||||
data["DramaTagKey"] = strings.Join(tagIDs, ",")
|
||||
data["DramaTagName"] = strings.Join(tagNames, ",")
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// SeriesPlay 视频播放页:GET /api/app/video/detail。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/视频相关接口/get_api_app_video_detail
|
||||
func SeriesPlay(c *gin.Context) {
|
||||
@@ -369,8 +289,6 @@ func SeriesPlay(c *gin.Context) {
|
||||
"list": comments.List,
|
||||
"apis": []string{
|
||||
"GET /api/app/comment/list",
|
||||
"POST /api/app/comment/send",
|
||||
"POST /api/app/comment/like/set",
|
||||
},
|
||||
},
|
||||
"playDebug": gin.H{
|
||||
@@ -383,6 +301,5 @@ func SeriesPlay(c *gin.Context) {
|
||||
if strings.TrimSpace(c.Param("guessPage")) != "" {
|
||||
data["canonicalPath"] = videoser.VideoPlayURL(video.Slug, video.ID)
|
||||
}
|
||||
attachRechargeModal(c, data)
|
||||
HTMLOK(c, "seriesPlay.html", data)
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"videoweb/app/service/playletser"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// PlayletLikeSet BFF:代理 POST /api/app/playlet/like/set。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/post_api_app_playlet_like_set
|
||||
func PlayletLikeSet(c *gin.Context) {
|
||||
var body struct {
|
||||
ID int64 `json:"id"`
|
||||
IsLike *bool `json:"isLike"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.ID <= 0 || body.IsLike == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": "id/isLike required",
|
||||
"tip": "缺少短剧 ID 或点赞状态",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
env, err := playletser.LikeSet(c, playletser.PlayletLikeSetReq{
|
||||
ID: body.ID,
|
||||
IsLike: *body.IsLike,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": env.Tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"videoweb/app/service/playletser"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// PlayletCollectSet BFF:代理 POST /api/app/playlet/collect/set。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/post_api_app_playlet_collect_set
|
||||
func PlayletCollectSet(c *gin.Context) {
|
||||
var body struct {
|
||||
ID int64 `json:"id"`
|
||||
IsCollect *bool `json:"isCollect"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.ID <= 0 || body.IsCollect == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": "id/isCollect required",
|
||||
"tip": "缺少短剧 ID 或收藏状态",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
env, err := playletser.CollectSet(c, playletser.PlayletCollectSetReq{
|
||||
ID: body.ID,
|
||||
IsCollect: *body.IsCollect,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": env.Tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
@@ -1,229 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"videoweb/app/service/authser"
|
||||
"videoweb/app/service/playletser"
|
||||
"videoweb/app/service/usercenterser"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/common/useragent"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type profileWork struct {
|
||||
Title, Meta, Episodes, Badge, Image, Progress, Remaining, URL string
|
||||
}
|
||||
|
||||
// Profile 从当前会话拉取用户信息、观看历史和收藏,不展示演示账号数据。
|
||||
func Profile(c *gin.Context) {
|
||||
data := gin.H{
|
||||
"title": "个人中心 - 黄果短剧", "navActive": "profile", "accountTab": "profile",
|
||||
}
|
||||
responses := gin.H{}
|
||||
loadAccountSummary(c, data, responses)
|
||||
if useragent.TokenFromRequest(c.Request) != "" {
|
||||
fetchProfileLists(c, data, responses)
|
||||
}
|
||||
if pretty, err := json.MarshalIndent(responses, "", " "); err == nil {
|
||||
data["responseJSON"] = string(pretty)
|
||||
}
|
||||
HTMLOK(c, "profile.html", data)
|
||||
}
|
||||
|
||||
func loadAccountSummary(c *gin.Context, data, responses gin.H) {
|
||||
data["userName"] = "未登录"
|
||||
data["balance"] = "—"
|
||||
data["historyCount"] = "—"
|
||||
data["favoriteCount"] = "—"
|
||||
data["chasingCount"] = "—"
|
||||
data["boughtChapterCount"] = "—"
|
||||
user := authser.SyncUserOnEnter(c)
|
||||
if user == nil {
|
||||
if useragent.TokenFromRequest(c.Request) != "" {
|
||||
responses["auth/getInfo"] = gin.H{"error": "加载失败"}
|
||||
fetchProfileStats(c, data, responses)
|
||||
return
|
||||
}
|
||||
responses["auth/getInfo"] = gin.H{"error": "暂无登录会话"}
|
||||
return
|
||||
}
|
||||
applyProfileInfo(data, responses, user)
|
||||
if useragent.TokenFromRequest(c.Request) != "" {
|
||||
fetchProfileStats(c, data, responses)
|
||||
}
|
||||
}
|
||||
|
||||
func fetchProfileStats(c *gin.Context, data, responses gin.H) {
|
||||
stats, code, err := usercenterser.Fetch(c)
|
||||
if retry, _ := authser.OnSessionExpired(c, code); retry {
|
||||
stats, code, err = usercenterser.Fetch(c)
|
||||
}
|
||||
if err != nil || stats == nil {
|
||||
responses["user/center/data"] = gin.H{"error": "加载失败"}
|
||||
if err != nil {
|
||||
log.Error("fetch user center data failed", log.E(err))
|
||||
}
|
||||
return
|
||||
}
|
||||
responses["user/center/data"] = gin.H{"code": code, "data": stats}
|
||||
data["historyCount"] = fmt.Sprint(stats.WatchCount)
|
||||
data["favoriteCount"] = fmt.Sprint(stats.CollectCount)
|
||||
data["chasingCount"] = fmt.Sprint(stats.ChasingCount)
|
||||
data["boughtChapterCount"] = fmt.Sprint(stats.BoughtChapterCount)
|
||||
}
|
||||
|
||||
func fetchProfileLists(c *gin.Context, data, responses gin.H) {
|
||||
query := playletser.HistoryListQuery{PageNum: 1, PageSize: 6}
|
||||
history, code, err := playletser.FetchHistoryList(c, query)
|
||||
if retry, _ := authser.OnSessionExpired(c, code); retry {
|
||||
history, _, err = playletser.FetchHistoryList(c, query)
|
||||
}
|
||||
if err == nil && history != nil {
|
||||
responses["playlet/history/list"] = gin.H{"code": 200, "data": history}
|
||||
data["history"] = profileHistoryWorks(history.List)
|
||||
} else if err != nil {
|
||||
responses["playlet/history/list"] = gin.H{"error": "加载失败"}
|
||||
log.Error("fetch profile history failed", log.E(err))
|
||||
data["historyUnavailable"] = true
|
||||
}
|
||||
|
||||
collectQuery := playletser.CollectListQuery{PageNum: 1, PageSize: 6, WatchStatus: 0}
|
||||
favorites, code, err := playletser.FetchCollectList(c, collectQuery)
|
||||
if retry, _ := authser.OnSessionExpired(c, code); retry {
|
||||
favorites, _, err = playletser.FetchCollectList(c, collectQuery)
|
||||
}
|
||||
if err == nil && favorites != nil {
|
||||
responses["playlet/collect/list"] = gin.H{"code": 200, "data": favorites}
|
||||
data["favorites"] = profileFavoriteWorks(favorites.List)
|
||||
} else if err != nil {
|
||||
responses["playlet/collect/list"] = gin.H{"error": "加载失败"}
|
||||
log.Error("fetch profile favorites failed", log.E(err))
|
||||
data["favoritesUnavailable"] = true
|
||||
}
|
||||
}
|
||||
|
||||
func applyProfileInfo(data, responses gin.H, user *authser.LoginResp) {
|
||||
if user == nil {
|
||||
return
|
||||
}
|
||||
// 登录响应可能包含 token;调试区域只展示非凭证字段。
|
||||
rawUser, _ := json.Marshal(user)
|
||||
var safeUser map[string]any
|
||||
_ = json.Unmarshal(rawUser, &safeUser)
|
||||
delete(safeUser, "token")
|
||||
responses["auth/getInfo"] = gin.H{"code": 200, "data": safeUser}
|
||||
name := strings.TrimSpace(user.NickName)
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(user.AccountNo)
|
||||
}
|
||||
if name == "" {
|
||||
name = "用户"
|
||||
}
|
||||
data["userName"] = name
|
||||
data["setting"] = authser.BuildSettingVM(user)
|
||||
data["userID"] = user.UserID
|
||||
data["userId"] = fmt.Sprint(user.UserID)
|
||||
data["balance"] = fmt.Sprint(user.Recharge)
|
||||
data["recharge"] = user.Recharge
|
||||
data["rechargeText"] = authser.FormatRecharge(user.Recharge)
|
||||
if avatar := safeProfileImage(user.Avatar); avatar != "" {
|
||||
data["avatar"] = avatar
|
||||
}
|
||||
}
|
||||
|
||||
func safeProfileImage(raw string) string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if strings.HasPrefix(raw, "/static/") {
|
||||
return raw
|
||||
}
|
||||
u, err := url.Parse(raw)
|
||||
if err == nil && (u.Scheme == "https" || u.Scheme == "http") && u.Host != "" {
|
||||
return raw
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func profileHistoryWorks(items []playletser.PlayletHistoryData) []profileWork {
|
||||
works := make([]profileWork, 0, len(items))
|
||||
for _, item := range items {
|
||||
if len(works) == 6 {
|
||||
break
|
||||
}
|
||||
name := strings.TrimSpace(item.Title)
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(item.PlayName)
|
||||
}
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
progress := 0
|
||||
if item.TotalEpisode > 0 {
|
||||
progress = item.WatchedCount * 100 / item.TotalEpisode
|
||||
}
|
||||
if progress < 0 {
|
||||
progress = 0
|
||||
}
|
||||
if progress > 100 {
|
||||
progress = 100
|
||||
}
|
||||
meta := "观看记录"
|
||||
if item.LastEpisode > 0 {
|
||||
meta = fmt.Sprintf("看至第 %d 集", item.LastEpisode)
|
||||
}
|
||||
remaining := ""
|
||||
if item.UnwatchedCount > 0 {
|
||||
remaining = fmt.Sprintf("剩 %d 集", item.UnwatchedCount)
|
||||
}
|
||||
works = append(works, profileWork{Title: name, Meta: meta, Image: profileCover(item.Cover), Progress: fmt.Sprintf("%d%%", progress), Remaining: remaining, URL: profilePlayURL(item.Slug, item.ID, item.LastEpisode)})
|
||||
}
|
||||
return works
|
||||
}
|
||||
|
||||
func profileFavoriteWorks(items []playletser.PlayletCollectData) []profileWork {
|
||||
works := make([]profileWork, 0, len(items))
|
||||
for _, item := range items {
|
||||
if len(works) == 6 {
|
||||
break
|
||||
}
|
||||
name := strings.TrimSpace(item.Title)
|
||||
if name == "" {
|
||||
name = strings.TrimSpace(item.PlayName)
|
||||
}
|
||||
if name == "" {
|
||||
continue
|
||||
}
|
||||
work := profileWork{Title: name, Meta: strings.Join(item.ThemeNames, " "), Image: profileCover(item.Cover), URL: profilePlayURL(item.Slug, item.ID, 1)}
|
||||
if item.TotalEpisode > 0 {
|
||||
work.Episodes = fmt.Sprintf("全 %d 集", item.TotalEpisode)
|
||||
}
|
||||
if item.IsFeatured {
|
||||
work.Badge = "爆剧"
|
||||
}
|
||||
works = append(works, work)
|
||||
}
|
||||
return works
|
||||
}
|
||||
|
||||
func profileCover(raw string) string {
|
||||
if safe := safeProfileImage(raw); safe != "" {
|
||||
return safe
|
||||
}
|
||||
return "/static/images/placeholder.png?v=2"
|
||||
}
|
||||
|
||||
func profilePlayURL(slug string, id int64, episode int) string {
|
||||
if slug == "" {
|
||||
if id <= 0 {
|
||||
return ""
|
||||
}
|
||||
slug = fmt.Sprint(id)
|
||||
}
|
||||
if episode < 1 {
|
||||
episode = 1
|
||||
}
|
||||
return "/play/" + url.PathEscape(slug) + "/" + fmt.Sprint(episode)
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"videoweb/app/service/authser"
|
||||
"videoweb/app/service/playletser"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/common/useragent"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type purchasedWork struct {
|
||||
Title, Image, Info, Date, Progress, URL string
|
||||
}
|
||||
|
||||
// PurchasedSeries 已购剧集:GET /api/app/playlet/bought/list。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/app短剧相关接口/get_api_app_playlet_bought_list
|
||||
func PurchasedSeries(c *gin.Context) {
|
||||
page, validPage := accountPageNum(c)
|
||||
if !validPage {
|
||||
NotFound(c)
|
||||
return
|
||||
}
|
||||
|
||||
data := gin.H{"title": "已购剧集 - 黄果短剧", "navActive": "purchased", "accountTab": "purchased"}
|
||||
responses := gin.H{}
|
||||
loadAccountSummary(c, data, responses)
|
||||
|
||||
size := 20
|
||||
if useragent.TokenFromRequest(c.Request) == "" {
|
||||
responses["playlet/bought/list"] = gin.H{"error": "暂无登录会话"}
|
||||
data["purchasedResponse"] = responses["playlet/bought/list"]
|
||||
HTMLOK(c, "purchased.html", data)
|
||||
return
|
||||
}
|
||||
|
||||
list, code, err := playletser.FetchBoughtList(c, page, size)
|
||||
if retry, _ := authser.OnSessionExpired(c, code); retry {
|
||||
list, _, err = playletser.FetchBoughtList(c, page, size)
|
||||
}
|
||||
if err == nil && list != nil {
|
||||
responses["playlet/bought/list"] = gin.H{"code": 200, "data": list}
|
||||
data["paging"] = makeAccountPage("/purchased", page, size, list.TotalCount)
|
||||
works := make([]purchasedWork, 0, len(list.List))
|
||||
for _, item := range list.List {
|
||||
title := strings.TrimSpace(item.PlayName)
|
||||
if title == "" {
|
||||
title = strings.TrimSpace(item.Title)
|
||||
}
|
||||
if title == "" {
|
||||
continue
|
||||
}
|
||||
info := fmt.Sprintf("已购 %d 集", item.BoughtChapterCount)
|
||||
if item.TotalEpisode > 0 {
|
||||
info += fmt.Sprintf(" / 共 %d 集", item.TotalEpisode)
|
||||
}
|
||||
info += fmt.Sprintf(" · %d 金币", item.CostCoins)
|
||||
progress := 0
|
||||
if item.TotalEpisode > 0 {
|
||||
progress = item.WatchedCount * 100 / item.TotalEpisode
|
||||
}
|
||||
if progress < 0 {
|
||||
progress = 0
|
||||
}
|
||||
if progress > 100 {
|
||||
progress = 100
|
||||
}
|
||||
works = append(works, purchasedWork{
|
||||
Title: title, Image: profileCover(item.Cover), Info: info,
|
||||
Date: playletser.FormatHistoryAt(item.BoughtAt), Progress: fmt.Sprintf("%d%%", progress),
|
||||
URL: playletser.ChapterPlayPath(item.Slug, item.ID, item.LastChapterID),
|
||||
})
|
||||
}
|
||||
data["purchased"] = works
|
||||
} else if err != nil {
|
||||
responses["playlet/bought/list"] = gin.H{"error": "加载失败", "msg": err.Error()}
|
||||
log.Error("fetch bought playlets failed", log.E(err))
|
||||
data["purchasedUnavailable"] = true
|
||||
} else {
|
||||
responses["playlet/bought/list"] = gin.H{"code": code, "data": list}
|
||||
}
|
||||
|
||||
data["purchasedResponse"] = responses["playlet/bought/list"]
|
||||
HTMLOK(c, "purchased.html", data)
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
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
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package web
|
||||
|
||||
import "github.com/gin-gonic/gin"
|
||||
|
||||
// RegisterPage H5 注册页:渲染 template/pages/register.html。
|
||||
// PC 端由页面内脚本回首页并打开 registerPop;H5 直接展示本页。
|
||||
func RegisterPage(c *gin.Context) {
|
||||
HTMLOK(c, "register.html", gin.H{
|
||||
"title": "注册账号 - 黄果短剧",
|
||||
})
|
||||
}
|
||||
+1
-61
@@ -4,7 +4,6 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"videoweb/app/service/authser"
|
||||
"videoweb/app/service/configser"
|
||||
"videoweb/app/service/playletser"
|
||||
"videoweb/app/service/seoser"
|
||||
@@ -14,68 +13,12 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// HTML 渲染页面并注入登录态(以 cookie 为准)。
|
||||
// 无 token 时自动游客登录(POST /api/app/auth/guest),保证默认进入即有会话。
|
||||
// HTML 渲染页面并注入公共导航、配置与 SEO 数据。
|
||||
func HTML(c *gin.Context, code int, name string, data gin.H) {
|
||||
if data == nil {
|
||||
data = gin.H{}
|
||||
}
|
||||
|
||||
// 所有服务端页面先完成渠道首次归因;已有会话也不会漏记落地页渠道。
|
||||
if channel := authser.ResolveChannel(c, ""); channel != "" {
|
||||
data["channel"] = channel
|
||||
}
|
||||
authser.SyncUserOnEnter(c)
|
||||
|
||||
loggedIn, userName, avatar, email := authser.AuthFromCookies(c)
|
||||
registered := authser.IsRegisteredFromCookies(c)
|
||||
data["isLoggedIn"] = loggedIn
|
||||
data["isRegistered"] = registered
|
||||
// 已登录(含游客)展示用户菜单;未登录才展示「登录」按钮。
|
||||
if _, ok := data["guestHeader"]; !ok {
|
||||
data["guestHeader"] = !loggedIn
|
||||
}
|
||||
if _, ok := data["isVip"]; !ok {
|
||||
data["isVip"] = loggedIn && authser.IsActiveVipFromCookies(c)
|
||||
}
|
||||
isVip, _ := data["isVip"].(bool)
|
||||
data["isVip"] = isVip
|
||||
|
||||
if loggedIn {
|
||||
if _, provided := data["userName"]; !provided && userName != "" {
|
||||
data["userName"] = userName
|
||||
}
|
||||
if _, provided := data["avatar"]; !provided && avatar != "" {
|
||||
data["avatar"] = avatar
|
||||
}
|
||||
if email != "" {
|
||||
data["email"] = email
|
||||
}
|
||||
if _, ok := data["userId"]; !ok {
|
||||
if uid := authser.UserIDFromCookies(c); uid > 0 {
|
||||
data["userId"] = authser.FormatUserID(uid)
|
||||
}
|
||||
}
|
||||
if _, ok := data["recharge"]; !ok {
|
||||
recharge := authser.RechargeFromCookies(c)
|
||||
data["recharge"] = recharge
|
||||
data["rechargeText"] = authser.FormatRecharge(recharge)
|
||||
} else if _, ok := data["rechargeText"]; !ok {
|
||||
data["rechargeText"] = authser.FormatRecharge(toInt64(data["recharge"]))
|
||||
}
|
||||
if _, ok := data["vipType"]; !ok {
|
||||
data["vipType"] = authser.VipTypeFromCookies(c)
|
||||
}
|
||||
} else {
|
||||
delete(data, "userName")
|
||||
delete(data, "avatar")
|
||||
delete(data, "email")
|
||||
delete(data, "userId")
|
||||
delete(data, "recharge")
|
||||
delete(data, "rechargeText")
|
||||
delete(data, "vipType")
|
||||
}
|
||||
|
||||
// 导航分类:GET /api/app/translation/category/list → categoryList(包含分类 seoInfo)
|
||||
if _, ok := data["categoryList"]; !ok {
|
||||
data["categoryList"] = translationser.FetchHeaderCategoryListOrDefault(c)
|
||||
@@ -119,9 +62,6 @@ func HTML(c *gin.Context, code int, name string, data gin.H) {
|
||||
}
|
||||
}
|
||||
|
||||
data["trackerAppId"] = configser.ResolveTrackerAppID()
|
||||
attachTrackerPage(c.Request.URL.Path, code, data)
|
||||
|
||||
attachPageSEO(c, code, data)
|
||||
c.HTML(code, name, data)
|
||||
}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"videoweb/app/service/authser"
|
||||
"videoweb/pub/common/log"
|
||||
"videoweb/pub/common/useragent"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Setting 展示账号设置,字段来自最新的用户信息接口。
|
||||
func Setting(c *gin.Context) {
|
||||
data := gin.H{"title": "账号设置 - 黄果短剧", "navActive": "setting", "accountTab": "setting"}
|
||||
loadAccountSummary(c, data, gin.H{})
|
||||
HTMLOK(c, "setting.html", data)
|
||||
}
|
||||
|
||||
// UpdateProfile 代理账号资料修改。可选字段必须显式传入,避免覆盖未修改内容。
|
||||
func UpdateProfile(c *gin.Context) {
|
||||
if useragent.TokenFromRequest(c.Request) == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"code": 401, "tip": "请先登录"})
|
||||
return
|
||||
}
|
||||
var body authser.UpdateProfileReq
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "tip": "参数错误"})
|
||||
return
|
||||
}
|
||||
if body.NickName != nil {
|
||||
v := strings.TrimSpace(*body.NickName)
|
||||
if v == "" || len([]rune(v)) > 40 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "tip": "昵称须为 1 至 40 个字符"})
|
||||
return
|
||||
}
|
||||
body.NickName = &v
|
||||
}
|
||||
if body.Introduction != nil {
|
||||
v := strings.TrimSpace(*body.Introduction)
|
||||
if len([]rune(v)) > 300 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "tip": "简介不能超过 300 个字符"})
|
||||
return
|
||||
}
|
||||
body.Introduction = &v
|
||||
}
|
||||
if body.Avatar != nil {
|
||||
v := strings.TrimSpace(*body.Avatar)
|
||||
if safeProfileImage(v) == "" || strings.HasPrefix(v, "/static/") {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "tip": "头像地址无效"})
|
||||
return
|
||||
}
|
||||
body.Avatar = &v
|
||||
}
|
||||
if (body.OldPassword == nil) != (body.NewPassword == nil) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "tip": "请输入旧密码和新密码"})
|
||||
return
|
||||
}
|
||||
if body.OldPassword != nil && (*body.OldPassword == "" || *body.NewPassword == "") {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "tip": "密码不能为空"})
|
||||
return
|
||||
}
|
||||
if body.NickName == nil && body.Introduction == nil && body.Avatar == nil && body.OldPassword == nil && body.PrivacyMode == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "tip": "没有需要保存的修改"})
|
||||
return
|
||||
}
|
||||
env, err := authser.UpdateProfile(c, body)
|
||||
if err != nil {
|
||||
log.Error("update profile failed", log.E(err))
|
||||
c.JSON(http.StatusBadGateway, gin.H{"code": 500, "tip": "保存失败,请稍后重试"})
|
||||
return
|
||||
}
|
||||
if env == nil || env.Code != 200 {
|
||||
if env == nil {
|
||||
c.JSON(http.StatusBadGateway, gin.H{"code": 500, "tip": "保存失败,请稍后重试"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": env.Code, "tip": firstNonEmptyAuth(env.Tip, env.Msg, "保存失败")})
|
||||
return
|
||||
}
|
||||
if infoEnv, user, err := authser.GetInfo(c); err == nil && infoEnv != nil && infoEnv.Code == 200 && user != nil {
|
||||
authser.SetAuthCookies(c, user)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": 200, "tip": firstNonEmptyAuth(env.Tip, "保存成功")})
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// attachTrackerPage 为所有 HTML 页面补齐稳定的 pageKey/pageName,避免事件落入 unknown。
|
||||
func attachTrackerPage(path string, code int, data map[string]any) {
|
||||
if data == nil {
|
||||
return
|
||||
}
|
||||
key, name := trackerPageFromRequest(path, code)
|
||||
if current, _ := data["pageKey"].(string); strings.TrimSpace(current) == "" {
|
||||
data["pageKey"] = key
|
||||
}
|
||||
if current, _ := data["pageName"].(string); strings.TrimSpace(current) == "" {
|
||||
data["pageName"] = name
|
||||
}
|
||||
}
|
||||
|
||||
func trackerPageFromRequest(path string, code int) (string, string) {
|
||||
if code >= http.StatusInternalServerError {
|
||||
return "server_error", "服务异常页面"
|
||||
}
|
||||
if code >= http.StatusBadRequest {
|
||||
return "not_found", "404页面"
|
||||
}
|
||||
return trackerPageFromPath(path)
|
||||
}
|
||||
|
||||
func trackerPageFromPath(path string) (string, string) {
|
||||
p := strings.TrimSuffix(strings.TrimSpace(path), "/")
|
||||
if p == "" {
|
||||
return "home", "首页"
|
||||
}
|
||||
rules := []struct {
|
||||
prefix string
|
||||
key string
|
||||
name string
|
||||
}{
|
||||
{"/series-play", "series_play", "AI成人剧集播放页"},
|
||||
{"/series", "series", "AI成人剧集"},
|
||||
{"/play", "play", "短剧播放页"},
|
||||
{"/detail", "detail", "短剧详情"},
|
||||
{"/list", "list", "短剧列表"},
|
||||
{"/recently-added", "recently_added", "最新短剧"},
|
||||
{"/popular-recommend", "popular_recommend", "热门推荐"},
|
||||
{"/rank", "rank", "排行榜"},
|
||||
{"/topic-detail", "topic_detail", "专题详情"},
|
||||
{"/topic", "topic", "专题列表"},
|
||||
{"/tags", "tags", "标签详情"},
|
||||
{"/more-series-list", "more_series_list", "成人剧集列表"},
|
||||
{"/more-list", "more_list", "短剧扩展列表"},
|
||||
{"/search-mobile", "search", "搜索结果页"},
|
||||
{"/search", "search", "搜索结果页"},
|
||||
{"/history", "history", "观看历史"},
|
||||
{"/favorites", "favorites", "我的收藏"},
|
||||
{"/purchased", "purchased", "已购剧集"},
|
||||
{"/profile", "profile", "个人中心"},
|
||||
{"/setting", "setting", "设置"},
|
||||
{"/recharge", "recharge", "充值中心"},
|
||||
{"/download-app", "download_app", "下载APP"},
|
||||
{"/feedback", "feedback", "问题反馈"},
|
||||
{"/help", "help", "帮助中心"},
|
||||
{"/about", "about", "关于我们"},
|
||||
{"/terms", "terms", "用户协议"},
|
||||
{"/privacy", "privacy", "隐私政策"},
|
||||
{"/2257", "compliance_2257", "2257合规声明"},
|
||||
{"/dmca", "dmca", "DMCA版权声明"},
|
||||
{"/login", "login", "登录"},
|
||||
{"/register", "register", "注册"},
|
||||
}
|
||||
for _, rule := range rules {
|
||||
if p == rule.prefix || strings.HasPrefix(p, rule.prefix+"/") {
|
||||
return rule.key, rule.name
|
||||
}
|
||||
}
|
||||
return "internal_other", "站内其他页面"
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"videoweb/app/service/videoser"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// VideoCollectSet BFF:代理 POST /api/app/video/collect/set。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/视频相关接口/post_api_app_video_collect_set
|
||||
func VideoCollectSet(c *gin.Context) {
|
||||
var body struct {
|
||||
ID int64 `json:"id"`
|
||||
IsCollect *bool `json:"isCollect"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.ID <= 0 || body.IsCollect == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": "id/isCollect required",
|
||||
"tip": "缺少视频 ID 或收藏状态",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
env, err := videoser.CollectSet(c, videoser.VideoCollectSetReq{
|
||||
ID: body.ID,
|
||||
IsCollect: *body.IsCollect,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": env.Tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"videoweb/app/service/videoser"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// VideoLikeSet BFF:代理 POST /api/app/video/like/set。
|
||||
// 文档:http://172.104.181.99:25319/swagger/index.html#/视频相关接口/post_api_app_video_like_set
|
||||
func VideoLikeSet(c *gin.Context) {
|
||||
var body struct {
|
||||
ID int64 `json:"id"`
|
||||
IsLike *bool `json:"isLike"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "invalid body",
|
||||
"tip": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if body.ID <= 0 || body.IsLike == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 400,
|
||||
"msg": "id/isLike required",
|
||||
"tip": "缺少视频 ID 或点赞状态",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
env, err := videoser.LikeSet(c, videoser.VideoLikeSetReq{
|
||||
ID: body.ID,
|
||||
IsLike: *body.IsLike,
|
||||
})
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if env == nil {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 500,
|
||||
"msg": "empty response",
|
||||
"tip": "操作失败,请稍后重试",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": env.Code,
|
||||
"msg": env.Msg,
|
||||
"tip": env.Tip,
|
||||
"data": env.Data,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user