init: hgmj server initial commit

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 16:50:13 +08:00
co-authored by Claude Opus 4.7
commit 625e89c15d
1870 changed files with 254090 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
logs
.DS_Store
.idea
.vscode
default.etcd
/app/*.tar.gz
/app/91jd-app-*
/app/config/app.yaml
/app/main
/app/config/*
/web/91jd-web-*
.log
video-app-*
/skd/91jd-skd-*
/skd/*.tar.gz
/app/app
/web/web
/app/config/app.json
/web/config/web.json
/skd/skd
/skd/config/skd.json
/skd/once/once
/skd/once/91jd-once-server
/skd/once/91jd-once-server.tar.gz
/app/__debug_bin
/web/__debug_bin
/schedule/__debug_bin
/web/*.tar.gz
/docs
/web/docs
/app/docs
/app/*.txt
/web/*.txt
/skd/*.txt
/reco/*.txt
/reco/config/*.json
/reco/91jd-reco-*
/reco/*.tar.gz
/go-shell/video/main.go
/go-shell/channel/main.go
/go-shell/detail/main.go
/go-shell/channStat/main.go
/go-shell/userStat/main.go
/go-shell/loseUserStat/main.go
web_test.sh
/web/.air.toml
/app/.air.toml
/script/*.mongodb
/tmp
/web/tmp
/app/tmp
/vendor/
/test/
/skd/temp
/skd/config/
/web/config/
/app/config/
*.sh
*/router/router_test
*.test
*.pem
+5
View File
@@ -0,0 +1,5 @@
FROM alpine
ADD html /html
ADD web-web /web-web
WORKDIR /
ENTRYPOINT [ "/web-web" ]
+20
View File
@@ -0,0 +1,20 @@
GOPATH:=$(shell go env GOPATH)
.PHONY: build
build:
go clean
env GOOS=linux go install -i -v
mv $(GOPATH)/bin/linux_amd64/91jd-server .
rm -rf $(GOPATH)/bin/linux_amd64
zip -q -r 91jd-server.zip 91jd-server config/app.yaml config/app-release.yaml
rm 91jd-server
.PHONY: test
test:
go test -v ./... -cover
.PHONY: docker
docker:
docker build . -t web-web:latest
+105
View File
@@ -0,0 +1,105 @@
# 91jd-server
Go 语言后端服务项目,基于 Gin 框架
## 架构
单仓库多服务(Monorepo),包含 3 个独立可部署的服务:
| 服务 | 入口 | 端口 | 说明 |
|------|------|------|------|
| **app** | `app/main.go` | 8182 | 面向客户端的 API 服务(移动端/APP) |
| **web** | `web/main.go` | - | 后台管理系统 API |
| **skd** | `skd/main.go` | - | 定时任务/后台 Job 调度服务 |
根目录 `main.go` 仅用于 Swagger 文档生成(端口 20114)。
## 目录结构
```
├── app/ # APP 端服务
│ ├── api/ # 接口处理层
│ ├── router/ # 路由定义
│ ├── service/ # 业务逻辑层
│ ├── middleware/ # APP 专用中间件
│ ├── config/ # APP 配置
│ └── proto/ # 请求/响应结构体
├── web/ # 后台管理端服务
│ ├── api/ # 接口处理层
│ ├── router/ # 路由定义
│ ├── service/ # 业务逻辑层
│ ├── middleware/ # Web 专用中间件
│ ├── config/ # Web 配置
│ └── proto/ # 请求/响应结构体
├── skd/ # 调度任务服务
│ ├── job/ # 定时任务定义
│ ├── service/ # 任务业务逻辑
│ ├── once/ # 一次性脚本
│ └── config/ # 调度配置
├── models/ # 数据模型层(数据库表结构、缓存模型)
├── common/ # 公共模块
│ ├── db/ # 数据库连接
│ ├── redis/ # Redis 客户端
│ ├── cache/ cachev2/ # 缓存层
│ ├── kafka/ # Kafka 消息队列
│ ├── elastic/ # Elasticsearch
│ ├── log/ # 日志
│ ├── conf/ # 配置加载
│ ├── tg/ # Telegram 机器人通知
│ ├── email/ sms/ # 邮件/短信通知
│ ├── filter/ # 内容过滤
│ ├── dataReport/ # 数据上报
│ ├── datacenter/ # 数据中心
│ ├── aiMate/ aiService/# AI 功能
│ ├── laosiji/ # 老司机模块
│ ├── store/ # 对象存储
│ └── ... # 其他工具模块
├── middleware/ # 通用中间件(CORS、IP、UA、请求ID)
├── generate/ # 代码生成
├── script/ # 脚本
└── swagger/ # Swagger 文档
```
## 技术栈
- **Web 框架**:Gin
- **ORM**:GORM
- **数据库**:MySQL
- **搜索引擎**:Elasticsearch
- **缓存**:Redis
- **消息队列**:Kafka (Sarama)
- **对象存储**:AWS S3
- **API 文档**:Swagger (swaggo)
- **部署**:Docker + Shell 脚本
## 业务功能
- 用户体系(注册/登录/JWT 鉴权/VIP)
- 视频/媒体管理(上传/播放/标签/搜索)
- 支付系统(充值/提现/钱包/金币)
- 社区功能(评论/关注/点赞/排行榜)
- IM 即时通讯(群组/消息)
- AI 功能(换脸/脱衣/文生图/文生视频/文生小说)
- 运营工具(活动/公告/签到/任务/兑换码)
- 广告系统
- 裸聊/代充
- 后台管理(用户管理/内容审核/数据统计/配置管理)
## 构建与部署
```bash
# 构建
make build
# 测试
make test
# Docker 构建
make docker
```
生产部署脚本:
- `app_update_prod.sh` — 部署 APP 服务
- `web_update_prod.sh` — 部署 Web 后台服务
- `skd_update_prod.sh` — 部署调度服务
- `skd_update_test.sh` — 部署调度服务(测试环境)
+275
View File
@@ -0,0 +1,275 @@
package activityctrl
import (
"strconv"
"91jd-server/app/service/activityser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
type CurrencyListReq struct {
UserID string `json:"userId" binding:"required"`
}
func CurrencyList(c *gin.Context) {
var req CurrencyListReq
if err := c.ShouldBindJSON(&req); err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
return
}
data, code := activityser.GetCurrencyList(c)
if code != stderr.Success {
common.ServeJSONNoEncrypt(c, code, nil)
return
}
common.ServeJSONNoEncrypt(c, stderr.Success, gin.H{
"list": data,
})
}
// VipDeductReq 会员卡当前可用抵扣(按抵扣后金额匹配支付通道)
type VipDeductReq struct {
ProductID string `json:"productId"` // 会员卡ID
DeductAmount int64 `json:"deductAmount"` // 券面额(分)
}
type ProductListReq struct {
UserID string `json:"userId" binding:"required"`
Deducts []VipDeductReq `json:"deducts"` // 各会员卡可用抵扣;按抵扣后有效金额匹配支付通道,可选
}
func ProductList(c *gin.Context) {
var req ProductListReq
if err := c.ShouldBindJSON(&req); err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
return
}
uid, err := strconv.ParseUint(req.UserID, 10, 64)
if err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
return
}
deducts := make([]activityser.VipDeduct, 0, len(req.Deducts))
for _, d := range req.Deducts {
deducts = append(deducts, activityser.VipDeduct{ProductID: d.ProductID, DeductAmount: d.DeductAmount})
}
res, err := activityser.GetProductList(c, uid, deducts)
if err != nil {
log.ErrorX(c, "活动服-获取会员卡列表异常", log.Any("uid", req.UserID), log.E(err))
common.ServeJSONNoEncrypt(c, stderr.ErrNetWorkBusy, nil)
return
}
common.ServeJSONNoEncrypt(c, stderr.Success, gin.H{
"list": res,
})
}
type RechargeOrderReq struct {
UserID string `json:"userId" binding:"required"`
RechargeType string `json:"rechargeType" binding:"required"`
ProductID string `json:"productId" binding:"required"`
BuyType int `json:"buyType" binding:"required"`
ActivityID string `json:"activityId"`
ExperimentID string `json:"experimentId"`
ExperimentVariant string `json:"experimentVariant"`
SessionID string `json:"sessionId"`
CouponID string `json:"couponId"` // 会员抵扣券ID(可选,购买会员卡时自动抵扣)
DeductAmount int64 `json:"deductAmount"` // 活动服建议抵扣金额(分,可选),本服按券自行校验上限后折价
}
func RechargeOrder(c *gin.Context) {
var req RechargeOrderReq
if err := c.ShouldBindJSON(&req); err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
return
}
uid, err := strconv.ParseUint(req.UserID, 10, 64)
if err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
return
}
payUrl, mode, err := activityser.CreateRechargeOrder(
c,
uid,
req.RechargeType,
req.ProductID,
req.BuyType,
c.ClientIP(),
activityser.RechargeAttribution{
ActivityID: req.ActivityID,
ExperimentID: req.ExperimentID,
ExperimentVariant: req.ExperimentVariant,
SessionID: req.SessionID,
},
req.CouponID,
req.DeductAmount,
)
if err != nil {
log.ErrorX(c, "活动服-充值下单异常", log.Any("uid", req.UserID), log.E(err))
common.ServeJSONNoEncrypt(c, stderr.RechargeFaile, err.Error())
return
}
common.ServeJSONNoEncrypt(c, stderr.Success, gin.H{
"payUrl": payUrl,
"mode": mode,
})
}
type BuyCoinProductReq struct {
UserID string `json:"userId" binding:"required"`
ProductID string `json:"productId" binding:"required"`
}
func BuyCoinProduct(c *gin.Context) {
var req BuyCoinProductReq
if err := c.ShouldBindJSON(&req); err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
return
}
uid, err := strconv.ParseUint(req.UserID, 10, 64)
if err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
return
}
code, err := activityser.BuyCoinProduct(c, uid, req.ProductID, "")
if err != nil {
log.ErrorX(c, "活动服-金币购买异常", log.Any("uid", req.UserID), log.Any("productId", req.ProductID), log.E(err))
if code == stderr.InsufficientBalance {
common.ServeJSONNoEncrypt(c, 201, "余额不足")
return
}
common.ServeJSONNoEncrypt(c, code, err.Error())
return
}
common.ServeJSONNoEncrypt(c, stderr.Success, nil)
}
type UserBalanceReq struct {
UserID string `json:"userId" binding:"required"`
}
func UserBalance(c *gin.Context) {
var req UserBalanceReq
if err := c.ShouldBindJSON(&req); err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
return
}
uid, err := strconv.ParseUint(req.UserID, 10, 64)
if err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
return
}
balance, err := activityser.GetUserBalance(uid)
if err != nil {
log.ErrorX(c, "活动服-查询余额异常", log.Any("uid", req.UserID), log.E(err))
common.ServeJSONNoEncrypt(c, stderr.ErrNetWorkBusy, nil)
return
}
common.ServeJSONNoEncrypt(c, stderr.Success, balance)
}
type AppInfoReq struct {
UserID string `json:"userId" binding:"required"`
}
func AppInfo(c *gin.Context) {
var req AppInfoReq
if err := c.ShouldBindJSON(&req); err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
return
}
uid, err := strconv.ParseUint(req.UserID, 10, 64)
if err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, "invalid userId")
return
}
data, err := activityser.GetAppInfo(c, uid)
if err != nil {
log.ErrorX(c, "活动服-获取应用信息异常", log.Any("uid", req.UserID), log.E(err))
common.ServeJSONNoEncrypt(c, stderr.ErrNetWorkBusy, nil)
return
}
common.ServeJSONNoEncrypt(c, stderr.Success, data)
}
func Reward(c *gin.Context) {
var req activityser.RewardReq
if err := c.ShouldBindJSON(&req); err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
return
}
if err := activityser.GrantReward(c, &req); err != nil {
log.ErrorX(c, "活动服-发奖异常",
log.Any("uid", req.UserId),
log.Any("rewardType", req.RewardType),
log.Any("amount", req.Amount),
log.E(err))
common.ServeJSONNoEncrypt(c, stderr.Failure, err.Error())
return
}
common.ServeJSONNoEncrypt(c, stderr.Success, nil)
}
func BatchReward(c *gin.Context) {
var req activityser.BatchRewardReq
if err := c.ShouldBindJSON(&req); err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
return
}
if err := activityser.GrantBatchReward(c, &req); err != nil {
log.ErrorX(c, "活动服-批量发奖异常",
log.Any("uid", req.UserId),
log.Any("rewardCount", len(req.Rewards)),
log.E(err))
common.ServeJSONNoEncrypt(c, stderr.Failure, err.Error())
return
}
common.ServeJSONNoEncrypt(c, stderr.Success, nil)
}
func Deduct(c *gin.Context) {
var req activityser.DeductReq
if err := c.ShouldBindJSON(&req); err != nil {
common.ServeJSONNoEncrypt(c, stderr.ErrParamError, nil)
return
}
code, err := activityser.Deduct(c, &req)
if err != nil {
log.ErrorX(c, "活动服-扣款异常",
log.Any("uid", req.UserId),
log.Any("deductType", req.DeductType),
log.Any("amount", req.Amount),
log.E(err))
common.ServeJSONNoEncrypt(c, code, err.Error())
return
}
common.ServeJSONNoEncrypt(c, stderr.Success, nil)
}
+111
View File
@@ -0,0 +1,111 @@
package adsctrl
import (
"91jd-server/app/service/adser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/v/adsmod"
"91jd-server/models/v/usermod"
"context"
"fmt"
"time"
"github.com/gin-gonic/gin"
)
// AdsClick doc
// @Summary 广告点击日志
// @Description 广告点击日志
// @Tags annou
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ads/click [post]
func AdsClick(ctx *gin.Context) {
_, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrTokenIsNotExist, "")
return
}
var arg struct {
ID string `form:"id" json:"id" binding:"required"` //广告id
Type int `form:"type" json:"type"` // 0 普通广告; 1 金主楼凤广告
}
if err := ctx.ShouldBind(&arg); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
// list doc
// @Summary 广告列表
// @Description 广告点击日志
// @Tags annou
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ads/list [post]
func List(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
ads := adser.AdList(adsmod.SysAds, "")
common.ServeJSON(ctx, stderr.Success, gin.H{"ads": ads})
return
}
user, _ := usermod.FindUserByUID(uid)
if user == nil {
ads := adser.AdList(adsmod.SysAds, "")
common.ServeJSON(ctx, stderr.Success, gin.H{"ads": ads})
return
}
if user.DistrictCode == "" {
ads := adser.AdList(adsmod.SysAds, "")
common.ServeJSON(ctx, stderr.Success, gin.H{"ads": ads})
return
}
ads := adser.AdList(adsmod.DiscAds, user.DistrictCode)
common.ServeJSON(ctx, stderr.Success, gin.H{"ads": ads})
}
// AdsClickStat doc
// @Summary 广告点击统计
// @Description 广告点击统计
// @Tags annou
// @Accept mpfd,json
// @Produce json,html
// @Param type query int false "广告类型 0:应用 1:广告"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ads/click/stat [post]
func AdsClickStat(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrTokenIsNotExist, "")
return
}
var arg struct {
Type int32 `form:"type" json:"type"` // 广告类型 0:应用 1:广告
}
if err := ctx.ShouldBind(&arg); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
common.Go(func() {
userInfo, _ := usermod.FindUserByUID(uid)
if userInfo != nil {
log.Info(fmt.Sprintf("ApplicationAdClick-param-%s:", userInfo.AdGroup), log.Any("uid", uid), log.Any("Type", arg.Type), log.Any("ua", ua), log.Any("ip", ip))
if arg.Type == 1 {
_ = adser.UpsertAdStat(context.Background(), userInfo, time.Now(), 0, 1, 0)
} else {
_ = adser.UpsertAdStat(context.Background(), userInfo, time.Now(), 1, 0, 0)
}
}
})
common.ServeJSON(ctx, stderr.Success, nil)
}
@@ -0,0 +1,34 @@
package advance_config_ctrl
import (
"91jd-server/app/service/advance_config_ser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"fmt"
"github.com/gin-gonic/gin"
)
// GetAdvanceConfig doc
// @Summary 预售配置列表
// @Description 预售配置列表
// @Tags 预售配置信息
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} advanceconfigmod.AppAdvanceRes
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/advance_config/list [get]
func GetAdvanceConfig(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
data, err := advance_config_ser.GainAdvanceConfig(uid)
if err != nil {
log.Error(fmt.Sprintf("advance_config_ser GainAdvanceConfig error:%v,uid:%v", err, uid))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+247
View File
@@ -0,0 +1,247 @@
package ai_changeface_ctrl
import (
"91jd-server/app/service/ai_changeface_ser"
"91jd-server/app/service/m3u8ticket"
"91jd-server/common"
"91jd-server/common/constant"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/l/operatorlgmod"
"91jd-server/models/v/aichangefacemod"
"encoding/json"
"errors"
"fmt"
"strconv"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// List doc
// @Summary AI换脸列表
// @Description AI换脸列表
// @Tags AI视频换脸
// @Accept json
// @Produce json
// @Param status query int false "记录状态"
// @Param pageNumber query int true "第几页"
// @Param pageSize query int true "每页数量"
// @Success 200 object interface{} "成功后返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /ai/changeface/list [get]
func List(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
var req struct {
Status *aichangefacemod.AiChangeFaceStatus `form:"status" json:"status"` // 0 未完成; 1 已完成; -1 已退款
commod.Page
}
if err = c.ShouldBindQuery(&req); err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
list, hasNext, err := ai_changeface_ser.List(uid, req.Status, int(req.Skip()), int(req.Limit()))
if err != nil {
log.Error(fmt.Sprintf("ai_changeface_ser List error%v, uid%v", err.Error(), uid))
common.ServeJSON(c, stderr.ErrDbQueryError, nil)
return
}
for i := range list {
m3u8ticket.SignURL(c, uid, &list[i].ModVideo, true, false)
m3u8ticket.SignURL(c, uid, &list[i].Url, true, false)
}
common.ServeJSON(c, stderr.Success, gin.H{
"list": list,
"hasNext": hasNext,
})
}
// Generate doc
// @Summary 生成AI换脸记录
// @Description 生成AI换脸记录
// @Tags AI视频换脸
// @Accept json
// @Produce json
// @Param q body aichangefacemod.GenerateRequest false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /ai/changeface/generate [post]
func Generate(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var in aichangefacemod.GenerateRequest
if err = ctx.ShouldBind(&in); err != nil {
log.Error(fmt.Sprintf("undress Generate param err%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if len(in.Pic) == 0 || in.VidModID.IsZero() {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
code := ai_changeface_ser.Generate(uid, in.Pic, in.VidModID, in.Discount, in.ShareTitle, in.ShareStatus, ua, ip)
if code != stderr.Success {
log.Error(fmt.Sprintf("undress Generate err%v", code))
common.ServeJSON(ctx, code, code.Error())
return
}
updateLog, _ := json.Marshal(in)
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiChangeface, constant.Add, string(updateLog), ctx.Request.URL.RequestURI())
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// Hide doc
// @Summary 删除AI换脸记录
// @Description 删除AI换脸记录
// @Tags AI视频换脸
// @Accept json
// @Produce json
// @Param id formData string false "AI订单ID"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /ai/changeface/hide [post]
func Hide(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req struct {
ID primitive.ObjectID `json:"id"`
}
if err = ctx.ShouldBind(&req); err != nil {
log.Error(fmt.Sprintf("changeface Generate param err%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if req.ID.IsZero() {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
acf, err := aichangefacemod.FindByID(nil, req.ID)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
if acf.ID.IsZero() {
common.ServeJSON(ctx, stderr.Failure, errors.New("ai换脸订单未找到"))
return
}
if acf.Uid != uid {
common.ServeJSON(ctx, stderr.Failure, errors.New("只能删除自己的订单"))
return
}
if acf.Status == aichangefacemod.StatusGenning || acf.Status == aichangefacemod.StatusSubmit {
common.ServeJSON(ctx, stderr.AiGenningDelForbidden, errors.New("不能删除排队中的订单"))
return
}
if err = aichangefacemod.Hide(uid, req.ID); err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
updateLog, _ := json.Marshal(req)
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiChangeface, constant.Delete, string(updateLog), ctx.Request.URL.RequestURI())
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// ModList doc
// @Summary AI模版列表
// @Description AI模版列表
// @Tags AI模版
// @Accept json
// @Produce json
// @Success 200 object aichangefacevidmod.AppResponse "成功后返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/ai/mod/list [get]
func ModList(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
data, err := ai_changeface_ser.ModList(uid)
if err != nil {
log.Error(fmt.Sprintf("ai_changeface_ser ModList error%v,uid%v", err.Error(), uid))
common.ServeJSON(c, stderr.ErrDbQueryError, nil)
return
}
if data != nil {
for i := range data.AiChangeFaceVideoMod {
m3u8ticket.SignURL(c, uid, &data.AiChangeFaceVideoMod[i].SourceURL, true, false)
}
for i := range data.AiImgToVideoMod {
m3u8ticket.SignURL(c, uid, &data.AiImgToVideoMod[i].NewUrl, true, false)
}
}
common.ServeJSON(c, stderr.Success, data)
}
// ModListV2 doc
// @Summary AI模版列表
// @Description AI模版列表
// @Tags AI模版
// @Accept json
// @Produce json
// @Param q query ai_changeface_ser.ModListV2Req false "请求参数"
// @Success 200 object ai_changeface_ser.ModListV2Resp "成功后返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai/mod/v2/list [get]
func ModListV2(ctx *gin.Context) {
var req ai_changeface_ser.ModListV2Req
if err := ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := ai_changeface_ser.ModListV2(req)
if err != nil {
log.Error("ai_changeface_ser.ModListV2 fail", log.Any("req", req), log.E(err))
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
uid := common.TryGetUID(ctx)
for i := range data.TemplateList {
if data.TemplateList[i] == nil {
continue
}
m3u8ticket.SignURL(ctx, uid, &data.TemplateList[i].M3u8Url, true, false)
}
common.ServeJSON(ctx, stderr.Success, data)
}
// ModInfo doc
// @Summary AI模版详情
// @Description AI模版详情
// @Tags AI模版
// @Accept json
// @Produce json
// @Param q query ai_changeface_ser.ModInfoReq false "请求参数"
// @Success 200 object ai_changeface_ser.ModInfoResp "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/ai/mod/info [get]
func ModInfo(ctx *gin.Context) {
var req ai_changeface_ser.ModInfoReq
if err := ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := req.GetInfo()
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
uid := common.TryGetUID(ctx)
m3u8ticket.SignURL(ctx, uid, &data.AiChangeFaceMod.M3u8Url, true, false)
m3u8ticket.SignURL(ctx, uid, &data.AiImgToVideoMod.NewUrl, true, false)
common.ServeJSON(ctx, stderr.Success, data)
}
+114
View File
@@ -0,0 +1,114 @@
package ai_image_to_video_ctrl
import (
"91jd-server/app/service/ai_image_to_video_ser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"fmt"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取AI图生视频列表列表接口
// @Description 获取AI图生视频列表列表
// @Tags 移动端-AI图生视频列表
// @Accept mpfd,json
// @Produce json
// @Param q query ai_image_to_video_ser.AppQueryListReq false "请求参数"
// @Success 200 object ai_image_to_video_ser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai/imagetovideo/list [get]
func List(ctx *gin.Context) {
p := &ai_image_to_video_ser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
log.Error(fmt.Sprintf("imagetovideo param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
// 获取用户当前配置
var err error
p.UID, err = common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
list, err := p.GetList()
if err != nil {
log.Error(fmt.Sprintf("imagetovideo get list err:%v, uid:%v", err, p.UID))
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, list)
}
// Generate doc
// @Summary 生成AI换脸记录
// @Description 生成AI换脸记录
// @Tags 移动端-AI图生视频列表
// @Accept json
// @Produce json
// @Param q body ai_image_to_video_ser.GenerateReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai/imagetovideo/generate [post]
func Generate(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
in := ai_image_to_video_ser.GenerateReq{}
if err = ctx.ShouldBind(&in); err != nil {
log.Error(fmt.Sprintf("imagetovideo Generate param err:%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
in.UID = uid
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
code, err := in.Generate(ua, ip)
if err != nil {
log.Error(fmt.Sprintf("imagetovideo Generate err:%v\n", err))
common.ServeJSON(ctx, code, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// Hide doc
// @Summary 删除AI换脸记录
// @Description 删除AI换脸记录
// @Tags 移动端-AI图生视频列表
// @Accept json
// @Produce json
// @Param q body ai_image_to_video_ser.HideReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai/imagetovideo/hide [post]
func Hide(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
in := ai_image_to_video_ser.HideReq{}
if err = ctx.ShouldBind(&in); err != nil {
log.Error(fmt.Sprintf("imagetovideo hide param err:%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
in.UID = uid
err = in.Hide()
if err != nil {
log.Error(fmt.Sprintf("imagetovideo hide err:%v\n", err))
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
+151
View File
@@ -0,0 +1,151 @@
package ai_mate_ctrl
import (
"91jd-server/app/appg"
"91jd-server/app/service/ai_mate_ser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
// GetCurrencys doc
// @Summary AI伴侣
// @Description 获取AI伴侣币列表
// @Tags mine
// @Accept json
// @Produce json
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/aimate/currencys [get]
func GetCurrencys(ctx *gin.Context) {
code, data := ai_mate_ser.GetCurrencyList()
if code != stderr.Success {
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, code, gin.H{"list": data})
}
// Exchange doc
// @Summary AI伴侣
// @Description 兑换AI伴侣货币
// @Tags mine
// @Accept json
// @Produce json
// @Param q query ai_mate_ser.ExchangeReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/aimate/exchange [post]
func Exchange(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req ai_mate_ser.ExchangeReq
if err = ctx.ShouldBindJSON(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
code := ai_mate_ser.Exchange(uid, req, ua, ip)
if code != stderr.Success {
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, code, "操作成功")
}
// Login doc
// @Summary AI伴侣
// @Description 获取当前用户的AI女友登录地址
// @Tags mine
// @Accept json
// @Produce json
// @Success 200 {object} ai_mate_ser.LoginResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/aimate/login [get]
func Login(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
if appg.Conf.URL.AIMateH5 == "" {
common.ServeJSON(ctx, stderr.FunctionNotEnabled, nil)
return
}
ret, err := ai_mate_ser.Login(uid)
if err != nil {
log.Error("ai_mate_ser Login failed", log.Any("uid", uid), log.E(err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, ret)
}
// GetBalance doc
// @Summary AI伴侣
// @Description 获取用户AI伴侣余额
// @Tags mine
// @Accept json
// @Produce json
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/aimate/getBalance [get]
func GetBalance(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
if appg.Conf.URL.AIMateH5 == "" {
common.ServeJSON(ctx, stderr.FunctionNotEnabled, nil)
return
}
ret, err := ai_mate_ser.GetNewBalance(uid)
if err != nil {
log.Error(fmt.Sprintf("uid:%d, ai_mate_ser GetNewBalance err:%v", uid, err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, ret)
}
// GianBalance 保留历史拼写错误的接口,兼容旧客户端。
func GianBalance(ctx *gin.Context) {
GetBalance(ctx)
}
// SyncInfo doc
// @Summary 同步聊天信息
// @Description 同步聊天信息
// @Tags AI伴侣模块
// @Accept mpfd,json
// @Produce json,html
// @Param param body ai_mate_ser.SyncInfoRes true "参数列表"
// @Success 200 {string} string "成功"
// @Failure 400 {string} string "获取失败的返回结果"
// @Router /api/app/aimate/sync [post]
func SyncInfo(ctx *gin.Context) {
var in ai_mate_ser.SyncInfoRes
if err := ctx.ShouldBindJSON(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
err := in.Sync()
if err != nil {
log.Error(fmt.Sprintf("uid:%v,aimate sync err:%v", in.UID, err))
common.ServeJSON(ctx, stderr.Failure, err)
return
}
ctx.JSON(http.StatusOK, stderr.Success.Msg())
}
+45
View File
@@ -0,0 +1,45 @@
package ai_mate_v2_ctrl
import (
"91jd-server/app/service/aiser"
"91jd-server/common"
"91jd-server/common/laosiji_app"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/cache/sysconfdata"
"91jd-server/models/v/sysconfmod"
"github.com/gin-gonic/gin"
)
// URL doc
// @Summary AI女友V2
// @Description 主钱包金币上分并获取AI女友授权链接
// @Tags AI女友V2
// @Produce json
// @Success 200 {object} aiser.GetAuthURLResp
// @Router /api/app/aimatev2/url [post]
func URL(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
config, err := sysconfdata.GetAllFromCache()
if err != nil {
log.Warn("get AI girlfriend switch failed", log.Any("uid", uid), log.E(err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
if !config.GetBool(sysconfmod.VCodeAiGirlFriend) || !laosiji_app.Configured() {
common.ServeJSON(ctx, stderr.FunctionNotEnabled, nil)
return
}
resp, err := aiser.GetAuthURL(ctx.Request.Context(), uid)
if err != nil {
log.Error("get AI girlfriend V2 URL failed", log.Any("uid", uid), log.E(err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, resp)
}
+112
View File
@@ -0,0 +1,112 @@
package ai_text_to_image_ctrl
import (
"91jd-server/app/service/ai_text_to_image_ser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"fmt"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取AI绘图列表接口
// @Description 获取AI绘图列表
// @Tags 移动端-AI绘图列表
// @Accept mpfd,json
// @Produce json
// @Param q query ai_text_to_image_ser.AppQueryListReq false "请求参数"
// @Success 200 object ai_text_to_image_ser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai/text_to_image/list [get]
func List(ctx *gin.Context) {
p := &ai_text_to_image_ser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
var err error
p.UID, err = common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
list := p.GetList()
common.ServeJSON(ctx, stderr.Success, list)
}
// Generate doc
// @Summary 生成AI绘图订单记录
// @Description 生成AI绘图订单记录
// @Tags 移动端-AI绘图列表
// @Accept json
// @Produce json
// @Param q body ai_text_to_image_ser.GenerateReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai/text_to_image/generate [post]
func Generate(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
in := ai_text_to_image_ser.GenerateReq{}
if err = ctx.ShouldBind(&in); err != nil {
log.Error(fmt.Sprintf("ai_text_to_image Generate param err:%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
in.UID = uid
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
code, err := in.Generate(ua, ip)
if err != nil {
log.Error(fmt.Sprintf("ai_text_to_image Generate err:%v\n", err))
common.ServeJSON(ctx, code, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// Hide doc
// @Summary 删除AI绘图记录
// @Description 删除AI绘图记录
// @Tags 移动端-AI绘图列表
// @Accept json
// @Produce json
// @Param q body ai_text_to_image_ser.HideReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai/text_to_image/hide [post]
func Hide(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
in := ai_text_to_image_ser.HideReq{}
if err = ctx.ShouldBind(&in); err != nil {
log.Error(fmt.Sprintf("ai_text_to_image hide param err:%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
in.UID = uid
err = in.Hide()
if err != nil {
log.Error(fmt.Sprintf("ai_text_to_image hide err:%v\n", err))
if code, ok := err.(stderr.Code); ok {
common.ServeJSON(ctx, code, code.Error())
return
}
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
+138
View File
@@ -0,0 +1,138 @@
package ai_text_to_novel_ctrl
import (
"91jd-server/app/service/ai_text_to_novel_ser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"fmt"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取AI小说列表列表接口
// @Description 获取AI小说列表列表
// @Tags 移动端-AI小说列表
// @Accept mpfd,json
// @Produce json
// @Param q query ai_text_to_novel_ser.AppQueryListReq false "请求参数"
// @Success 200 object ai_text_to_novel_ser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai_text_to_novel/list [get]
func List(ctx *gin.Context) {
p := &ai_text_to_novel_ser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
var err error
p.UID, err = common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
list := p.GetList()
common.ServeJSON(ctx, stderr.Success, list)
}
// Info doc
// @Summary 获取AI小说列表详情接口
// @Description 获取AI小说列表详情
// @Tags 移动端-AI小说列表
// @Accept mpfd,json
// @Produce json
// @Param q query ai_text_to_novel_ser.AppQueryInfoReq false "请求参数"
// @Success 200 object ai_text_to_novel_ser.AppQueryInfoRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai_text_to_novel/info [get]
func Info(ctx *gin.Context) {
p := &ai_text_to_novel_ser.AppQueryInfoReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
data, err := p.GetInfo()
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// Generate doc
// @Summary 生成AI小说订单记录
// @Description 生成AI小说订单记录
// @Tags 移动端-AI小说列表
// @Accept json
// @Produce json
// @Param q body ai_text_to_novel_ser.GenerateReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai_text_to_novel/generate [post]
func Generate(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
in := ai_text_to_novel_ser.GenerateReq{}
if err = ctx.ShouldBind(&in); err != nil {
log.Error(fmt.Sprintf("ai_text_to_novel Generate param err:%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
in.UID = uid
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
code, err := in.Generate(ua, ip)
if err != nil {
log.Error(fmt.Sprintf("ai_text_to_novel Generate err:%v\n", err))
common.ServeJSON(ctx, code, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// Hide doc
// @Summary 删除AI小说记录
// @Description 删除AI小说记录
// @Tags 移动端-AI小说列表
// @Accept json
// @Produce json
// @Param q body ai_text_to_novel_ser.HideReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ai_text_to_novel/hide [post]
func Hide(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
in := ai_text_to_novel_ser.HideReq{}
if err = ctx.ShouldBind(&in); err != nil {
log.Error(fmt.Sprintf("ai_text_to_novel hide param err:%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
in.UID = uid
err = in.Hide()
if err != nil {
log.Error(fmt.Sprintf("ai_text_to_novel hide err:%v\n", err))
if code, ok := err.(stderr.Code); ok {
common.ServeJSON(ctx, code, code.Error())
return
}
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
+218
View File
@@ -0,0 +1,218 @@
package ai_undress_ctrl
import (
"91jd-server/app/service/ai_changeface_img_ser"
"91jd-server/app/service/ai_undress_server"
"91jd-server/common"
"91jd-server/common/constant"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/l/operatorlgmod"
"91jd-server/models/v/aiUnDressmod"
"91jd-server/models/v/aichangefaceimgmod"
"encoding/json"
"fmt"
"strconv"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary AI脱衣列表
// @Description AI脱衣列表
// @Tags AI脱衣
// @Accept json
// @Produce json
// @Param status query int false "记录状态"
// @Param pageNumber query int true "第几页"
// @Param pageSize query int true "每页数量"
// @Success 200 object interface{} "成功后返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /ai/undress/list [get]
func List(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
var req aiUnDressmod.ListRequest
if err = c.ShouldBindQuery(&req); err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
data, code := ai_undress_server.List(uid, &req)
if code != stderr.Success {
log.Error(fmt.Sprintf("ai_undress_service List error%v,uid%v", code.Error(), uid))
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, code, data)
}
// Generate doc
// @Summary 生成AI脱衣记录
// @Description 生成AI脱衣记录
// @Tags AI脱衣
// @Accept json
// @Produce json
// @Param q body aiUnDressmod.GenerateRequest false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /ai/undress/generate [post]
func Generate(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req aiUnDressmod.GenerateRequest
if err = ctx.ShouldBind(&req); err != nil {
log.Error(fmt.Sprintf("undress Generate param err%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
code := ai_undress_server.Generate(uid, &req, ua, ip)
if code != stderr.Success {
log.Error(fmt.Sprintf("undress Generate err%v", code))
common.ServeJSON(ctx, code, code.Error())
return
}
updateLog, _ := json.Marshal(req)
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiUndressList, constant.Add, string(updateLog), ctx.Request.URL.RequestURI())
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// AiImgList doc
// @Summary AI换脸列表
// @Description AI换脸列表
// @Tags AI图片换脸
// @Accept json
// @Produce json
// @Param status query int false "记录状态"
// @Param pageNumber query int true "第几页"
// @Param pageSize query int true "每页数量"
// @Success 200 object aichangefaceimgmod.AiChangeFaceImg "成功后返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/ai/img/list [get]
func AiImgList(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
var req aichangefaceimgmod.ListRequest
if err = c.ShouldBindQuery(&req); err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
data, code := ai_changeface_img_ser.List(uid, &req)
if code != stderr.Success {
log.Error(fmt.Sprintf("ai_change_face_service List error%v,uid%v", code.Error(), uid))
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, code, data)
}
// AiImgGenerate doc
// @Summary 生成AI换脸记录
// @Description 生成AI换脸记录
// @Tags AI图片换脸
// @Accept json
// @Produce json
// @Param q body aichangefaceimgmod.GenerateRequest false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/ai/img/generate [post]
func AiImgGenerate(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req aichangefaceimgmod.GenerateRequest
if err = ctx.ShouldBind(&req); err != nil {
log.Error(fmt.Sprintf("ai_change_face_img Generate param err%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
code := ai_changeface_img_ser.Generate(uid, &req, ua, ip)
if code != stderr.Success {
log.Error(fmt.Sprintf("ai_change_face_img Generate err%v", code))
common.ServeJSON(ctx, code, code.Error())
return
}
updateLog, _ := json.Marshal(req)
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiChangeFaceImgList, constant.Add, string(updateLog), ctx.Request.URL.RequestURI())
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// AiChangeFaceImgHide doc
// @Summary 删除AI换脸记录
// @Description 删除AI换脸记录
// @Tags AI图片换脸
// @Accept json
// @Produce json
// @Param id formData string false "AI订单ID"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/ai/img/hide [post]
func AiChangeFaceImgHide(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req aichangefaceimgmod.DelRequest
if err = ctx.ShouldBind(&req); err != nil {
log.Error(fmt.Sprintf("ai_change_face_img del param err%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code := ai_changeface_img_ser.AiChangeFaceImgHide(uid, &req)
if code != stderr.Success {
common.ServeJSON(ctx, code, code.Error())
return
}
updateLog, _ := json.Marshal(req)
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiChangeFaceImgList, constant.Delete, string(updateLog), ctx.Request.URL.RequestURI())
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// AiUndressHide doc
// @Summary 删除AI换脸记录
// @Description 删除AI换脸记录
// @Tags AI脱衣
// @Accept json
// @Produce json
// @Param id formData string false "AI订单ID"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/ai/undress/hide [post]
func AiUndressHide(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req aiUnDressmod.DelRequest
if err = ctx.ShouldBind(&req); err != nil {
log.Error(fmt.Sprintf("undress del param err%v\n", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code := ai_undress_server.AiUndressHide(uid, &req)
if code != stderr.Success {
common.ServeJSON(ctx, code, code.Error())
return
}
updateLog, _ := json.Marshal(req)
_ = operatorlgmod.RecordOperation(strconv.FormatUint(uid, 10), constant.AiUndressList, constant.Delete, string(updateLog), ctx.Request.URL.RequestURI())
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
+116
View File
@@ -0,0 +1,116 @@
package aiplazactrl
import (
"91jd-server/app/service/aiplazaser"
"91jd-server/app/service/m3u8ticket"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取ai广场帖子列表接口
// @Description 获取ai广场帖子列表
// @Tags 移动端-ai广场帖子
// @Accept mpfd,json
// @Produce json
// @Param q query aiplazaser.AppQueryListReq false "请求参数"
// @Success 200 object aiplazaser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/aiplaza/list [get]
func List(ctx *gin.Context) {
p := &aiplazaser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
// var err error
// p.Uid, err = common.GetUID(ctx)
// if err != nil {
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
// return
// }
list := p.GetList()
uid := common.TryGetUID(ctx)
for i := range list.List {
if list.List[i] == nil {
continue
}
m3u8ticket.SignURL(ctx, uid, &list.List[i].OriginalVideo, true, false)
m3u8ticket.SignURL(ctx, uid, &list.List[i].GenerateVideo, true, false)
}
common.ServeJSON(ctx, stderr.Success, list)
}
// Info doc
//
// @Summary 获取ai广场帖子详情接口
// @Description 获取ai广场帖子详情
// @Tags 移动端-ai广场帖子
// @Accept mpfd,json
// @Produce json
// @Param q query aiplazaser.AppQueryInfoReq false "请求参数"
// @Success 200 object aiplazaser.AppQueryInfoResp "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/aiplaza/info [get]
func Info(ctx *gin.Context) {
p := &aiplazaser.AppQueryInfoReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
var err error
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
data, err := p.GetInfo(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
if data.Detail != nil {
m3u8ticket.SignURL(ctx, uid, &data.Detail.OriginalVideo, true, false)
m3u8ticket.SignURL(ctx, uid, &data.Detail.GenerateVideo, true, false)
}
common.ServeJSON(ctx, stderr.Success, data)
}
// Share doc
// @Summary 分享ai记录到ai广场
// @Description 分享ai记录到ai广场
// @Tags 移动端-ai广场帖子
// @Accept mpfd,json
// @Produce json
// @Param q body aiplazaser.ShareReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/aiplaza/share [post]
func Share(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &aiplazaser.ShareReq{}
err = ctx.ShouldBindJSON(&p)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
// 创建
err = p.Create(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, "")
}
+30
View File
@@ -0,0 +1,30 @@
package analyticsctrl
import (
"time"
"91jd-server/app/service/vipcardexperimentser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
func Events(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
request := vipcardexperimentser.EventsRequest{}
if err = ctx.ShouldBindJSON(&request); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
response, err := vipcardexperimentser.RecordEvents(uid, request, time.Now())
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, response)
}
+38
View File
@@ -0,0 +1,38 @@
package annouctrl
import (
"91jd-server/app/service/annouser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/annoumod"
"github.com/gin-gonic/gin"
)
// GetAnnous doc
// @Summary 获取公告列表
// @Description 获取消息模块公告列表
// @Tags annou
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/annou/msg/list [get]
func GetAnnous(ctx *gin.Context) {
_, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
req := annoumod.MsgListReq{}
err1 := ctx.ShouldBind(&req)
if err1 != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
info, err := annouser.MsgAnnouList(req)
common.ServeJSON(ctx, stderr.Success, info)
}
+51
View File
@@ -0,0 +1,51 @@
package backpackctrl
import (
"91jd-server/app/service/backpackser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// GetCouponList doc
//
// @Summary 获取优惠券
// @Description 获取优惠券
// @Tags 移动端-优惠券
// @Accept mpfd,json
// @Produce json
//
// @Param status formData integer false "物品状态 1-已使用 2-未使用 3-过期"
// @Param page formData integer false "当前页"
// @Param limit formData integer false "每页条数"
// @Param type formData integer false "1-楼风解锁折扣卷 2-会员折扣卷 3-AI换脸折扣券"
//
// @Success 200 object backpackmod.Backpack "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/backpack [get]
func GetCouponList(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
var params struct {
Status int `form:"status" binding:"required,min=1,max=3"` // 物品状态
Page int64 `form:"page" binding:"required,min=1"` // 当前页
Limit int64 `form:"limit" binding:"required,min=10,max=50"` // 每页条数
Type int `form:"type" ` // 类型
}
if err := c.ShouldBindQuery(&params); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err.Error())
return
}
log.Info("GetJewelBoxPrize start", log.Any("uid", uid), log.Any("params", params))
data, code := backpackser.GetCouponList(uid, params.Type, params.Status, params.Limit, params.Page)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, data)
}
+61
View File
@@ -0,0 +1,61 @@
package checkinctrl
import (
"91jd-server/app/service/checkinser"
"91jd-server/app/service/m3u8ticket"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// UserCheckin 用户签到
func UserCheckin(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
resp, code := checkinser.AddCheckin(uid)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
if resp != nil {
m3u8ticket.SignURL(c, uid, &resp.PrizeVideo, true, false)
}
common.ServeJSON(c, stderr.Success, resp)
}
// GetCheckinPrize 获取签到奖品
func GetCheckinPrize(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
resp, code := checkinser.GetCheckinPrizes(uid)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, resp)
}
// ClaimVipCheckinPrize 补领VIP签到奖励
func ClaimVipCheckinPrize(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
resp, code := checkinser.ClaimVipCheckinPrizes(uid)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
if resp != nil {
m3u8ticket.SignURL(c, uid, &resp.PrizeVideo, true, false)
}
common.ServeJSON(c, stderr.Success, resp)
}
+222
View File
@@ -0,0 +1,222 @@
package commentctrl
import (
"91jd-server/app/service/commentser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/v/aiplazamod"
"91jd-server/models/v/cmtmod"
"91jd-server/models/v/mediamod"
"time"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// List doc
// @Summary 评论模块 - 获取评论列表
// @Description 用户评论
// @Tags Comment
// @Accept mpfd,json
// @Produce json,html
// @Param objID query string true "评论对象的ID"
// @Param curTime query string true "打开评论列表的时间"
// @Param objType query string true "评论对象类型 video/cartoon/drama/AiPlaza"
// @Param pageNumber query integer true "当前页" mininum(1)
// @Param pageSize query integer true "每页条数" mininum(1)
// @Success 200 {object} cmtmod.ParentRespList "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/comment/list [get]
func List(ctx *gin.Context) {
uid, _ := common.GetUID(ctx)
type Info struct {
ObjID string `form:"objID" json:"objID" binding:"required"` //评论对象的ID
CurTime time.Time `form:"curTime" json:"curTime" binding:"required"` //打开评论列表的时间
ObjType string `form:"objType" json:"objType"` // 评论对象类型 video:视频(默认) cartoon:动漫 AiPlaza:ai广场
commod.Page
}
param := Info{}
err := ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
objID, err := primitive.ObjectIDFromHex(param.ObjID)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
var (
data = make(map[string]interface{})
vCnt int64 = 0
fCnt int64 = 0
hasNext bool
code stderr.Code
)
// 获取第一层评论总数,在获取第一页评论时返回总评论数
if param.Page.PageNumber == 1 {
if param.ObjType == "" || param.ObjType == "video" {
code, vCnt, err := commentser.GetVideoTotalComments(objID)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
data["total"] = vCnt // 总评论条数
} else if param.ObjType == cmtmod.OTypeCartoon || param.ObjType == cmtmod.OTypeDrama {
media, _ := mediamod.GetInfo(objID)
data["total"] = media.CountComment // 总评论条数
} else if param.ObjType == "AiPlaza" {
// ai广场
aiplaza, _ := aiplazamod.GetInfo(objID)
data["total"] = aiplaza.CommentCount // 总评论条数
}
} else {
code, fCnt, err = commentser.GetTotalComments(objID, param.ObjType)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
}
code, list, hasNext, err := commentser.GetParentCmtList(uid, objID, param.ObjType, param.CurTime, param.Page)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
// 获取置顶快捷搜索
if param.PageNumber == 1 {
list = commentser.QuickSearch(objID.Hex(), list)
if len(list) == 1 && vCnt == 0 {
data["total"] = 1
fCnt = 1
}
}
data["lfCount"] = fCnt // 一级评论条数
data["hasNext"] = hasNext
data["list"] = list
common.ServeJSON(ctx, code, data)
}
// Send doc
// @Summary 评论模块 - 发表评论
// @Description 用户评论
// @Tags Comment
// @Accept mpfd,json
// @Produce json,html
// @Param objType formData string true "评论对象类型 video:视频(默认) cartoon:动漫 AiPlaza:ai广场"
// @Param objID formData string true "评论对象的ID 帖子的ID"
// @Param cid formData string false "此评论是对某条评论的评论或回复 一级评论的ID,如果为空,则为对该视频的评论"
// @Param rid formData string false "被回复的评论id"
// @Param level formData integer false "评论层级 1:一级评论 2:二级评论"
// @Param toUserID formData integer false "对某用户回复评论 用户ID"
// @Param content formData string true "评论内容"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/comment/send [post]
func Send(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
param := cmtmod.PublishReqInfo{}
err = ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if param.Level != 1 && param.Level != 2 {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if param.Content == "" && param.Image == "" {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
ua, err := common.GetUA(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
ip := common.GetIP(ctx)
code, data, err := commentser.PublishComment(uid, ua, ip, param)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
if code == stderr.CommentUserNotBind {
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, code, data)
}
// Info doc
// @Summary 评论模块 - 获取评论详情(获取二级评论)
// @Description 用户评论
// @Tags Comment
// @Accept mpfd,json
// @Produce json,html
// @Param objID query string true "评论对象的ID"
// @Param cmtId query string true "评论id"
// @Param curTime query string true "打开评论列表的时间"
// @Param fstID query string true "默认展示的第一条二级评论的id"
// @Param pageNumber query integer true "当前页" mininum(1)
// @Param pageSize query integer true "每页条数" mininum(1)
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /comment/info [get]
func Info(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
type Info struct {
ObjID string `form:"objID" json:"objID" binding:"required"` //评论对象的ID
CmtID string `form:"cmtId" json:"cmtId" binding:"required"` //某条评论id,用于获取该评论下的评论
FstID string `form:"fstID" json:"fstID" binding:"required"` //默认展示的第一条二级评论的id
CurTime time.Time `form:"curTime" json:"curTime" binding:"required"` //打开评论列表的时间
commod.Page
}
param := Info{}
err = ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
objID, err := primitive.ObjectIDFromHex(param.ObjID)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
cmtID, err := primitive.ObjectIDFromHex(param.CmtID)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
fstID, err := primitive.ObjectIDFromHex(param.FstID)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
var code stderr.Code
var data []cmtmod.ChildRespList
code, data, err = commentser.GetChildCmtList(uid, objID, cmtID, fstID, param.CurTime, param.Page)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
var hasNext bool
if len(data) > int(param.PageSize) {
hasNext = true
data = data[:param.PageSize]
}
common.ServeJSON(ctx, code, commod.ListResp{HasNext: hasNext, List: data})
}
+5
View File
@@ -0,0 +1,5 @@
package api
func GetHasNext(pageSize int, pageNumber int, total int64) bool {
return pageSize*pageNumber < int(total)
}
@@ -0,0 +1,34 @@
package contentmarkerctrl
import (
"time"
"91jd-server/app/appg"
"91jd-server/app/service/contentmarkerser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// Get doc
// @Summary 获取首页及亚模块内容更新时间
// @Description 客户端根据更新时间与本地已读时间判断是否展示红点
// @Tags 内容更新
// @Produce json
// @Success 200 {object} contentmarkerser.Response
// @Router /api/app/content/update-markers [get]
func Get(ctx *gin.Context) {
var cache contentmarkerser.MarkerCache
if appg.Redis != nil {
cache = appg.Redis
}
data, err := contentmarkerser.GetCached(time.Now(), cache)
if err != nil {
log.Error("get content update markers failed", log.E(err))
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+42
View File
@@ -0,0 +1,42 @@
package couponctl
import (
"91jd-server/app/service/couponser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/coupon_record_mod"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary 获取优惠券
// @Description 获取优惠券
// @Tags
// @Accept mpfd,json
// @Produce json,html
// @Param type query integer true "优惠券类型"
// @Param pageNumber query integer true "查询页码"
// @Param pageSize query integer true "页码大小"
// @Success 200 {object} coupon_record_mod.QueryAllRes
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/coupon/list [get]
func List(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
var req coupon_record_mod.AppListReq
err = c.ShouldBindQuery(&req)
if err != nil {
common.ServeJSON(c, stderr.ErrParamError, nil)
return
}
data, err := couponser.List(uid, &req)
if err != nil {
common.ServeJSON(c, stderr.Failure, nil)
return
}
common.ServeJSON(c, stderr.Success, data)
}
+119
View File
@@ -0,0 +1,119 @@
package customerCtrl
import (
"91jd-server/app/appg"
"91jd-server/app/service/customerser"
"91jd-server/common"
"91jd-server/common/crypt"
"91jd-server/common/log"
"91jd-server/common/stderr"
"encoding/hex"
"github.com/gin-gonic/gin"
"github.com/go-playground/form"
"net/url"
)
// Url doc
//
// @Summary 获取客服链接
// @Description 获取客服链接
// @Tags 移动端-客服
// @Accept mpfd,json
// @Produce json
// @Success 200 object string "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/customer/url [get]
func Url(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
ua, _ := common.GetUA(ctx)
resp, err := customerser.GetUrl(uid, ua)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, resp.Data.Url)
}
func parseRequest(ctx *gin.Context, data interface{}) (err error) {
// 获取请求参数sign
sign := ctx.Query("sign")
decodeByte, err := hex.DecodeString(sign)
if err != nil {
log.Warn("非法请求1", log.Any("sign", sign), log.E(err))
return
}
str, err := crypt.AesDecrypt(string(decodeByte), appg.Conf.Customer.Secret)
if err != nil {
log.Warn("非法请求2", log.Any("sign", sign), log.E(err))
return
}
values, err := url.ParseQuery(str)
if err != nil {
log.Warn("非法请求3", log.Any("sign", sign), log.Any("str", str), log.E(err))
return
}
// 解码到结构体
decoder := form.NewDecoder()
err = decoder.Decode(data, values)
if err != nil {
log.Warn("参数解析错误", log.Any("values", values), log.E(err))
return
}
return nil
}
// Backpack doc
//
// @Summary 获取背包
// @Description 获取背包
// @Tags 移动端-客服
// @Accept mpfd,json
// @Produce json
// @Param q query customerser.BackpackReq false "请求参数"
// @Success 200 object customerser.BackpackResp "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /customer/user/backpack [get]
func Backpack(ctx *gin.Context) {
p := &customerser.BackpackReq{}
if err := parseRequest(ctx, p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
resp, err := p.GetData(ctx, uint64(p.UserID), p.Account, p.Phone, p.InviteCode)
if err != nil {
common.ServeJSONNoEncrypt(ctx, stderr.Failure, err)
return
}
common.ServeJSONNoEncrypt(ctx, stderr.Success, resp)
}
// Recharge doc
//
// @Summary 获取充值订单
// @Description 获取充值订单
// @Tags 移动端-客服
// @Accept mpfd,json
// @Produce json
// @Param q query customerser.RechargeReq false "请求参数"
// @Success 200 object customerser.RechargeResp "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /customer/user/recharge [get]
func Recharge(ctx *gin.Context) {
p := &customerser.RechargeReq{}
if err := parseRequest(ctx, p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
resp, err := p.GetData(ctx, uint64(p.UserID))
if err != nil {
common.ServeJSONNoEncrypt(ctx, stderr.Failure, err)
return
}
common.ServeJSONNoEncrypt(ctx, stderr.Success, resp)
}
+179
View File
@@ -0,0 +1,179 @@
package daichongctrl
import (
"fmt"
"net/http"
"strconv"
"strings"
"91jd-server/app/appg"
"91jd-server/app/service/daichongser"
"91jd-server/common"
"91jd-server/common/daichong"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/e/daichongmod"
"91jd-server/models/v/rchgordmod"
"91jd-server/models/v/usermod"
"91jd-server/models/v/walletmod"
"github.com/gin-gonic/gin"
"github.com/shopspring/decimal"
)
// 向玩家追分 doc
// @Summary 向玩家追分
// @Description 向玩家追分
// @Tags WEB-daichong
// @Accept mpfd,json
// @Produce json,html
// @Param amount formData string true "追分的钱数(元)"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /3rd/third/api/noticeSeekScore [post]
func SeekScore(ctx *gin.Context) {
var args struct {
Amount string `from:"amount" json:"amount" binding:"required"`
Uid string `from:"uid" json:"uid" binding:"required"`
ProT int `form:"productType" json:"productType"`
}
if err := ctx.ShouldBind(&args); err != nil {
ctx.JSON(http.StatusOK, gin.H{"data": nil, "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
return
}
if args.ProT == 1 {
ctx.JSON(http.StatusOK, gin.H{"data": nil, "code": stderr.ErrParamError, "msg": "瓦力游戏已下架"})
return
}
uid, _ := strconv.ParseUint(args.Uid, 10, 64)
user, err := usermod.FindUserByUIDForNoCache(uid)
if err != nil || user == nil {
ctx.JSON(http.StatusOK, gin.H{"data": nil, "code": stderr.Failure, "msg": stderr.Failure.Msg()})
return
}
if err = daichongser.SeekScoreBaseOnAmount(uid, args.Amount); err != nil {
ctx.JSON(http.StatusOK, gin.H{"data": nil, "code": stderr.Failure, "msg": stderr.Failure.Msg()})
return
}
ctx.JSON(http.StatusOK, gin.H{"data": nil, "code": stderr.Success, "msg": stderr.Success.Msg()})
}
// 查询玩家信息 doc
// @Summary 查询玩家信息 通知订单生成
// @Description 通知订单生成
// @Tags WEB-daichong
// @Accept mpfd,json
// @Produce json,html
// @Param appId formData string true "代充平台提供的appId"
// @Param data formData string true "加密结果字符串"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /3rd/third/api/queryPlayerInfo [post]
func QueryPlayerInfo(ctx *gin.Context) {
var args struct {
Uid string `from:"uid" json:"uid" binding:"required"`
ProT int `form:"productType" json:"productType"`
}
if err := ctx.ShouldBind(&args); err != nil {
ctx.JSON(http.StatusOK, gin.H{"data": nil, "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
return
}
if args.ProT == 1 {
ctx.JSON(http.StatusOK, gin.H{"data": nil, "code": stderr.ErrParamError, "msg": "瓦力游戏已下架"})
return
}
uidstr := strings.Split(strings.TrimSpace(args.Uid), ",")
type userForThird struct {
Uid string `json:"uid"`
NickName string `json:"nickName"`
CoinNum string `json:"coinNum"`
BankCoin string `json:"bankCoin"`
Money string `json:"money"`
}
ut := make([]userForThird, 0, len(uidstr))
for _, v := range uidstr {
uid, _ := strconv.Atoi(strings.TrimSpace(v))
user, err := usermod.FindUserByUID(uint64(uid))
if err != nil || user == nil {
continue
}
w, err := walletmod.GetWallet(uint64(uid))
var coinNum int64
if err != nil || w == nil {
coinNum = 0
} else {
coinNum = w.Amount
}
//将金币转成人民币
money := decimal.NewFromInt(coinNum).Div(decimal.NewFromInt(10))
ut = append(ut, userForThird{
Uid: strings.TrimSpace(v) + "-" + appg.Conf.DaiChong.PlatName,
NickName: user.Name,
CoinNum: strconv.Itoa(int(coinNum)),
Money: money.String(),
BankCoin: "0",
})
}
ctx.JSON(http.StatusOK, gin.H{"data": ut, "code": stderr.Success, "msg": stderr.Success.Msg()})
}
// NewOrder 上分 通知订单生成 doc
// @Summary 上分 通知订单生成
// @Description 通知订单生成
// @Tags WEB-daichong
// @Accept mpfd,json
// @Produce json,html
// @Param appId formData string true "代充平台提供的appId"
// @Param data formData string true "加密结果字符串"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /daichong/api/chat/getOrder [post]
func NewOrder(ctx *gin.Context) {
newOrderSign := daichongmod.NewOrderSign{}
if err := ctx.ShouldBind(&newOrderSign); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"data": nil, "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
return
}
log.Debug(fmt.Sprintf("daichong callback param:%+v", newOrderSign))
if newOrderSign.OrderId == "" {
ctx.JSON(http.StatusBadRequest, gin.H{"data": nil, "code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
return
}
if newOrderSign.ProT == 1 {
ctx.JSON(http.StatusOK, gin.H{"data": nil, "code": stderr.ErrParamError, "msg": "瓦力游戏已下架"})
return
}
cid, _ := strconv.ParseUint(strings.TrimSpace(newOrderSign.ToId), 10, 64)
user, err := usermod.FindUserByUIDForNoCache(cid)
if user == nil || err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"code": stderr.UserIsNotExists, "msg": stderr.UserIsNotExists.Msg(), "data": nil})
return
}
order, err := rchgordmod.FindRechargeOrderByThirdOrderId(newOrderSign.OrderId)
if err == nil && order.Status == rchgordmod.SUCCESS {
ctx.JSON(http.StatusOK, gin.H{
"data": gin.H{
"status": daichong.Success,
"devID": user.DevID,
"userIP": common.GetIP(ctx),
"tel": user.Mobile,
"devType": user.DevType},
"code": stderr.Success,
"msg": stderr.Success.Msg()})
return
}
code, err := daichongser.HandlerNewOrder(ctx, newOrderSign, *user)
if err != nil {
log.ErrorX(ctx, "handle new order failed", log.Any("newOrderSign", newOrderSign), log.Any("uid", user.UID),
log.Any("code", code), log.E(err))
}
ctx.JSON(http.StatusOK, gin.H{
"data": gin.H{
"status": code,
"devID": user.DevID,
"userIP": common.GetIP(ctx),
"tel": user.Mobile,
"devType": user.DevType},
"code": stderr.Success,
"msg": stderr.Success.Msg()})
}
+78
View File
@@ -0,0 +1,78 @@
package daichongctrl
import (
"encoding/base64"
"io"
"net/http"
"91jd-server/app/service/daichongser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// GetChatRecordsByOrderId doc
// @Summary 根据订单ID获取聊天记录
// @Description 根据订单ID获取聊天记录
// @Tags 代充-Daichong
// @Accept mpfd,json
// @Produce json,html
// @Param orderId formData string true "订单号"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/daichong/getChatRecordsByOrderId [post]
func GetChatRecordsByOrderId(ctx *gin.Context) {
orderId := ctx.PostForm("orderId")
if orderId == "" {
ctx.JSON(http.StatusBadRequest, gin.H{"code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
return
}
data, code := daichongser.GetChatRecordsByOrderId(orderId)
ctx.JSON(http.StatusOK, gin.H{"data": data.Data, "code": code, "msg": code.Msg()})
}
// UploadFile doc
// @Summary 代充上传图片
// @Description 上传图片
// @Tags 代充-Daichong
// @Accept mpfd,json
// @Produce json,html
// @Param fileName formData string true "文件名称"
// @Param upload formData file true "文件"
// @Param sessionId formData string true "会话ID"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/daichong/file/xxxx.jpg [post]
func UploadFile(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
filename := ctx.Param("filename")
sessionId := ctx.PostForm("sessionId")
if err != nil || filename == "" || sessionId == "" {
ctx.JSON(http.StatusBadRequest, gin.H{"code": stderr.ErrParamError, "msg": stderr.ErrParamError.Msg()})
return
}
headers, err := ctx.FormFile("upload")
if err != nil {
log.Warn("headers Open file wrong ", log.E(err))
ctx.JSON(http.StatusOK, gin.H{"code": stderr.ErrUploadError, "msg": stderr.ErrUploadError.Msg()})
return
}
f, err := headers.Open()
if err != nil {
log.Warn("headers Open file wrong ", log.E(err))
ctx.JSON(http.StatusOK, gin.H{"code": stderr.ErrUploadError, "msg": stderr.ErrUploadError.Msg()})
return
}
byteData, err := io.ReadAll(f)
f.Close()
if err != nil {
log.Warn("request multipart wrong ", log.E(err))
ctx.JSON(http.StatusOK, gin.H{"code": stderr.ErrUploadError, "msg": stderr.ErrUploadError.Msg()})
return
}
fileData := base64.StdEncoding.EncodeToString(byteData)
data, code := daichongser.UploadFile(filename, sessionId, fileData, uid)
ctx.JSON(http.StatusOK, gin.H{"data": data.Data, "code": code, "msg": code.Msg()})
}
+203
View File
@@ -0,0 +1,203 @@
package dramactrl
import (
"errors"
"time"
"91jd-server/app/service/dramaser"
"91jd-server/app/service/m3u8ticket"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/walletmod"
"github.com/gin-gonic/gin"
)
// ChannelConfig doc
// @Summary 短剧频道配置
// @Tags 移动端-短剧
// @Success 200 {object} dramaser.ChannelConfig
// @Router /api/app/media/drama/channel/config [get]
func ChannelConfig(ctx *gin.Context) {
if _, err := common.GetUID(ctx); err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
data, err := dramaser.GetChannelConfig(time.Now())
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// Feed doc
// @Summary AI短剧沉浸式推荐Feed
// @Tags 移动端-短剧
// @Param q query dramaser.FeedRequest true "请求参数"
// @Success 200 {object} dramaser.FeedResponse
// @Router /api/app/media/drama/feed [get]
func Feed(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req dramaser.FeedRequest
if err = ctx.ShouldBindQuery(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := dramaser.GetFeed(ctx.Request.Context(), uid, req.PageSize, time.Now())
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
for i := range data.List {
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.VideoUrl, true, false)
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.H265Url, true, false)
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.AudioUrl, true, false)
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.PreviewVideoUrl, false, true)
m3u8ticket.SignURL(ctx, uid, &data.List[i].Content.PreviewH265Url, false, true)
}
common.ServeJSON(ctx, stderr.Success, data)
}
// List doc
// @Summary 热门短剧双列列表
// @Tags 移动端-短剧
// @Param q query dramaser.ListRequest true "请求参数"
// @Success 200 {object} dramaser.ListResponse
// @Router /api/app/media/drama/list [get]
func List(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req dramaser.ListRequest
if err = ctx.ShouldBindQuery(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := dramaser.GetList(uid, req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// Topics doc
// @Summary 短剧专题列表
// @Tags 移动端-短剧
// @Success 200 {object} dramaser.TopicListResponse
// @Router /api/app/media/drama/topics [get]
func Topics(ctx *gin.Context) {
if _, err := common.GetUID(ctx); err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
data, err := dramaser.GetTopics(time.Now())
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// TopicWorks doc
// @Summary 短剧专题作品
// @Tags 移动端-短剧
// @Param q query dramaser.TopicWorksRequest true "请求参数"
// @Success 200 {object} dramaser.TopicWorksResponse
// @Router /api/app/media/drama/topic/works [get]
func TopicWorks(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req dramaser.TopicWorksRequest
if err = ctx.ShouldBindQuery(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := dramaser.GetTopicWorks(uid, req, time.Now())
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// DownloadAuthorize doc
// @Summary 申请短剧单集下载并扣除下载次数
// @Tags 移动端-短剧
// @Param X-Request-ID header string true "幂等请求ID"
// @Param body body dramaser.DownloadAuthorizeRequest true "请求参数"
// @Success 200 {object} dramaser.DownloadAuthorizeResponse
// @Router /api/app/media/drama/download/authorize [post]
func DownloadAuthorize(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req dramaser.DownloadAuthorizeRequest
if err = ctx.ShouldBindJSON(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := dramaser.AuthorizeDownload(ctx.Request.Context(), uid, ctx.GetHeader("X-Request-ID"), req, time.Now())
if err != nil {
serveDownloadAuthorizeError(ctx, err)
return
}
m3u8ticket.SignURL(ctx, uid, &data.DownloadURL, true, false)
m3u8ticket.SignURL(ctx, uid, &data.H265DownloadURL, true, false)
common.ServeJSON(ctx, stderr.Success, data)
}
func serveDownloadAuthorizeError(ctx *gin.Context, err error) {
switch {
case errors.Is(err, dramaser.ErrDramaEntitlementRequired):
common.ServeJsonWithExtra(ctx, stderr.ErrAccessForbid,
gin.H{"reason": "DRAMA_ENTITLEMENT_REQUIRED"}, gin.H{"msg": "", "tip": ""})
case errors.Is(err, walletmod.ErrDownloadCountNotEnough):
common.ServeJsonWithExtra(ctx, stderr.DownloadCountIsNotEnough,
gin.H{"reason": "DOWNLOAD_COUNT_NOT_ENOUGH", "remainingDownloadCount": 0}, gin.H{"msg": "", "tip": ""})
case errors.Is(err, walletmod.ErrDownloadRequestConflict):
common.ServeJSON(ctx, stderr.ErrInvalidRequest, err)
case errors.Is(err, dramaser.ErrDownloadResourceInvalid):
common.ServeJSON(ctx, stderr.ErrParamError, err)
default:
common.ServeJSON(ctx, stderr.ErrDbUpdateError, err)
}
}
// SaveEvents doc
// @Summary 批量上报短剧一期埋点
// @Tags 移动端-短剧
// @Param X-Request-ID header string true "幂等请求ID"
// @Param body body dramaser.EventsRequest true "请求参数"
// @Success 200 {object} dramaser.EventsResponse
// @Router /api/app/media/drama/events [post]
func SaveEvents(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req dramaser.EventsRequest
if err = ctx.ShouldBindJSON(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := dramaser.SaveEvents(uid, ctx.GetHeader("X-Request-ID"), req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+73
View File
@@ -0,0 +1,73 @@
package exchcodectrl
import (
"91jd-server/app/service/exchcodeser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"github.com/gin-gonic/gin"
)
// CodeExchange doc
// @Summary 兑换码兑换
// @Description 兑换码
// @Tags ExchangeCode
// @Accept mpfd,json
// @Produce json,html
// @Param code formData string true "兑换码"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/code/exchange [post]
func CodeExchange(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
var param struct {
Code string `json:"code" binding:"required"`
}
if err = ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code, data, err := exchcodeser.CodeExchange(uid, param.Code)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
common.ServeJSON(ctx, code, data)
}
// UserRecord doc
// @Summary 查询用户兑换记录
// @Description 查询用户兑换记录
// @Tags UserRecord
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData int true "页码"
// @Param pageSize formData int true "每页数据量"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/code/userRecord [get]
func UserRecord(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
var req struct {
commod.Page
}
if err = ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code, data, err := exchcodeser.UserExchageRecord(uid, req.Page)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
common.ServeJSON(ctx, code, data)
}
+123
View File
@@ -0,0 +1,123 @@
package followctrl
import (
"91jd-server/app/service/followser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/v/followmod"
"91jd-server/models/v/noticerecdmod"
"github.com/gin-gonic/gin"
"time"
)
// GetFollowList doc
// @Summary 获取关注列表 - 获取自己关注的用户
// @Description 获取关注列表
// @Tags 关注
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Param newsType formData string true "类型: SHORT:短视频博主 其他为空字符"
// @Success 200 {object} followmod.ListResp "{"list": [],"hasNext":false}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/follow/list [get]
func GetFollowList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
param := followmod.ListReq{}
err1 := ctx.ShouldBind(&param)
if err1 != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if param.PageSize < 1 {
param.PageSize = 10
}
if param.UID != 0 {
code, data := followser.GetHisFollowList(uid, param.UID, param.PageNumber, param.PageSize)
common.ServeJSON(ctx, code, data)
return
}
// 固定给20个下去不翻页(运营已经确定)
param.PageSize = 20
param.PageNumber = 1
code, data := followser.GetFollowList(uid, param.PageNumber, param.PageSize, param.IsShort)
common.ServeJSON(ctx, code, data)
}
// GetFansList doc
// @Summary 获取粉丝列表 - 获取我的粉丝
// @Description 获取粉丝列表
// @Tags 关注
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Success 200 {object} followmod.ListResp "{"list": [],"hasNext":false}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/follow/fans/list [get]
func GetFansList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
param := followmod.ListReq{}
err1 := ctx.ShouldBind(&param)
if err1 != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if param.PageSize < 1 {
param.PageSize = 10
}
if param.UID != 0 {
code, data := followser.GetHisFansList(uid, param.UID, param.PageNumber, param.PageSize)
common.ServeJSON(ctx, code, data)
return
}
code, data := followser.GetFansList(uid, param.PageNumber, param.PageSize)
if code == stderr.Success {
err = noticerecdmod.UpdateTrendReadTime(noticerecdmod.Fans, uid, time.Now())
if err != nil {
log.Error("followctrl GetFansList UpdateTrendReadTime faild", log.E(err))
}
}
common.ServeJSON(ctx, code, data)
}
// DynamicsList doc
// @Summary 获取关注用户发布的视频
// @Description 获取关注用户发布的视频
// @Tags 关注
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Success 200 {object} followmod.DynamicsResp "{"list": [],"hasNext":false}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/follow/dynamics/list [get]
func DynamicsList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
param := followmod.AppDynamicsListReq{}
err1 := ctx.ShouldBind(&param)
if err1 != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if param.PageSize < 1 {
param.PageSize = 10
}
data, code := followser.GetDynamicsList(uid, param)
common.ServeJSON(ctx, code, data)
}
+58
View File
@@ -0,0 +1,58 @@
package goldextractrl
import (
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/v/goldextramod"
"github.com/gin-gonic/gin"
)
func UserGoldExtra(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
var goldExtraReq struct {
Type uint `form:"type" json:"type"`
commod.Page
}
if err := ctx.ShouldBind(&goldExtraReq); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
skip := goldExtraReq.Skip()
limit := goldExtraReq.Limit()
var userExtras []goldextramod.GoldExtra
switch goldExtraReq.Type {
case 0: // 返回所有
userExtras, err = goldextramod.GetUserGoldExtra(nil, uid, skip, limit+1)
case 1: // 只返回有效
userExtras, err = goldextramod.GetUserGoldExtraValid(nil, uid, skip, limit+1)
case 2: // 已过期
userExtras, err = goldextramod.GetUserGoldExtraExpired(nil, uid, skip, limit+1)
case 3: // 已使用
userExtras, err = goldextramod.GetUserGoldExtraUsed(nil, uid, skip, limit+1)
default:
common.ServeJSON(ctx, stderr.ErrParamError, "无效的type值")
return
}
if err != nil {
common.ServeJSON(ctx, stderr.ErrServerUnavailable, "")
return
}
hasNext := false
if uint64(len(userExtras)) > limit {
userExtras = userExtras[:limit]
hasNext = true
}
common.ServeJSON(ctx, stderr.Success, struct {
List []goldextramod.GoldExtra `json:"list"`
HasNext bool `json:"hasNext"`
}{
List: userExtras,
HasNext: hasNext,
})
}
+38
View File
@@ -0,0 +1,38 @@
package health_check_ctrl
import (
"91jd-server/app/service/health_check_ser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"fmt"
"github.com/gin-gonic/gin"
)
// Ping doc
// @Summary 查询服务健康检测记录
// @Description 查询服务健康检测记录
// @Tags 移动端-服务健康检测
// @Accept json
// @Produce json
// @Param q body health_check_ser.PingReq false "请求参数"
// @Success 200 object health_check_ser.SystemStatus "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/health/ping [post]
func Ping(ctx *gin.Context) {
p := &health_check_ser.PingReq{}
if err := ctx.ShouldBind(&p); err != nil {
log.Error(fmt.Sprintf("health ping param err:%v", err))
common.ServeJSONNoEncrypt(ctx, stderr.ErrParamError, nil)
return
}
list, err := p.Ping()
if err != nil {
log.Error(fmt.Sprintf("health ping err:%v", err))
common.ServeJSONNoEncrypt(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSONNoEncrypt(ctx, stderr.Success, list)
}
+71
View File
@@ -0,0 +1,71 @@
package imctrl
import (
"strings"
"91jd-server/app/service/imadser"
"91jd-server/common"
"91jd-server/common/enum/imad"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
type IMAdReq struct {
Position string `json:"position"`
Positions []string `json:"positions"`
}
type IMAdResp struct {
Groups []imadser.AdPositionGroup `json:"groups"`
}
func GetIMAd(ctx *gin.Context) {
if _, err := common.GetUID(ctx); err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req IMAdReq
if err := ctx.ShouldBindJSON(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
positions := normalizeIMAdPositions(req)
if len(positions) == 0 {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
for _, position := range positions {
if !imad.IsPositionCode(position) {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
}
common.ServeJSON(ctx, stderr.Success, &IMAdResp{
Groups: imadser.GetAdsByPositionCodes(positions),
})
}
func normalizeIMAdPositions(req IMAdReq) []string {
positions := make([]string, 0, len(req.Positions)+1)
if position := strings.TrimSpace(req.Position); position != "" {
positions = append(positions, position)
}
positions = append(positions, req.Positions...)
seen := make(map[string]struct{}, len(positions))
normalized := make([]string, 0, len(positions))
for _, position := range positions {
position = strings.TrimSpace(position)
if position == "" {
continue
}
if _, ok := seen[position]; ok {
continue
}
seen[position] = struct{}{}
normalized = append(normalized, position)
}
return normalized
}
+365
View File
@@ -0,0 +1,365 @@
package imctrl
import (
"91jd-server/app/service/customerser"
"fmt"
"91jd-server/app/middleware/authuser"
"91jd-server/app/service/imser"
"91jd-server/app/service/messageser"
"91jd-server/common"
"91jd-server/common/imclient"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/common/truthutil"
"91jd-server/models/v/messagemod"
"91jd-server/models/v/sourcemod"
"github.com/gin-gonic/gin"
)
var accepts = []int32{1000, 1001, 1002, 1003}
const (
live_default = "ys-01"
faqURL = "/kefu/api/faq/queryByAppId"
checkURL = "/kefu/api/play/unread"
)
// GetImSign doc
// @Summary 获取Im签名
// @Description 获取Im签名 b 不需要token校验
// @Tags IM
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/im/whiteSign [get]
func GetWhiteImSign(ctx *gin.Context) {
if !sourcemod.GetCustomerStat() {
common.ServeJSON(ctx, stderr.ErrCustomerBanned, nil)
return
}
var uid uint64
var sign string
ua, _ := common.GetUA(ctx)
token := ctx.Request.Header.Get("Authorization") //token
if token != "" {
claims, err := authuser.ParseToken(token)
if err != nil {
log.Error("GetImSign ParseToken error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
}
uid = claims.UID
}
if uid != 0 {
//sign = imser.GetSign(uid, ua)
res, err := customerser.GetUrl(uid, ua)
if err != nil {
log.Error("GetImSign GetUrl error", log.Any("uid", uid), log.E(err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
sign = "/app/newkefu?" + res.Data.Params
} else if sign == "" {
sign = imser.GetWhiteSign()
}
common.ServeJSON(ctx, stderr.Success, sign)
}
func Token(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
if !imser.SDKEnabled() {
common.ServeJSON(ctx, stderr.Success, &imser.SDKAuthInfo{
Enabled: false,
UserID: uid,
})
return
}
data, err := imser.GetSDKAuth(uid)
if err != nil {
log.Warn("Get IM SDK token error", log.Any("uid", uid), log.E(err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
if ua, uaErr := common.GetUA(ctx); uaErr == nil {
data.SysType = ua.SysType
}
common.ServeJSON(ctx, stderr.Success, data)
}
type UserIDReq struct {
UserID uint64 `json:"userId"`
ImUserID int64 `json:"imUserId"`
}
type UserIDResp struct {
Enabled bool `json:"enabled"`
UserID uint64 `json:"userId"`
ImUserID int64 `json:"imUserId"`
}
func UserID(ctx *gin.Context) {
if _, err := common.GetUID(ctx); err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req UserIDReq
if err := ctx.ShouldBindJSON(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if (req.UserID > 0) == (req.ImUserID > 0) {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if !imser.SDKEnabled() {
common.ServeJSON(ctx, stderr.Success, &UserIDResp{
Enabled: false,
UserID: req.UserID,
ImUserID: req.ImUserID,
})
return
}
resp := &UserIDResp{Enabled: true, UserID: req.UserID, ImUserID: req.ImUserID}
if req.UserID > 0 {
imUserID, err := imser.ResolveStoredIMUserID(req.UserID)
if err != nil {
log.Warn("resolve IM user id failed", log.Any("uid", req.UserID), log.E(err))
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
resp.ImUserID = imUserID
} else {
uid, err := imser.ResolveUIDByIMUserID(req.ImUserID)
if err != nil {
log.Warn("resolve user id failed", log.Any("imUserId", req.ImUserID), log.E(err))
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
resp.UserID = uid
}
common.ServeJSON(ctx, stderr.Success, resp)
}
type EnsureFriendReq struct {
PeerID uint64 `json:"peerId" binding:"required"`
}
type EnsureFriendResp struct {
Enabled bool `json:"enabled"`
UserID uint64 `json:"userId"`
ImUserID int64 `json:"imUserId"`
PeerID uint64 `json:"peerId"`
PeerImUserID int64 `json:"peerImUserId"`
FriendAdded bool `json:"friendAdded"`
}
func EnsureFriend(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req EnsureFriendReq
if err = ctx.ShouldBindJSON(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if req.PeerID == 0 || req.PeerID == uid {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if !imser.SDKEnabled() {
common.ServeJSON(ctx, stderr.Success, &EnsureFriendResp{
Enabled: false,
UserID: uid,
PeerID: req.PeerID,
})
return
}
selfIMUserID, peerIMUserID, added, err := imser.EnsureFriendsBidirectional(uid, req.PeerID)
if err != nil {
log.Warn("ensure IM friend failed", log.Any("uid", uid), log.Any("peerId", req.PeerID), log.E(err))
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, &EnsureFriendResp{
Enabled: true,
UserID: uid,
ImUserID: selfIMUserID,
PeerID: req.PeerID,
PeerImUserID: peerIMUserID,
FriendAdded: added,
})
}
type FriendListReq struct {
Now *int64 `json:"now,omitempty"`
}
type FriendListResp struct {
Enabled bool `json:"enabled"`
UserID uint64 `json:"userId"`
ImUserID int64 `json:"imUserId"`
Now int64 `json:"now"`
NextNow int64 `json:"nextNow"`
Friends []imser.IMFriendItem `json:"friends"`
}
func FriendList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req FriendListReq
_ = ctx.ShouldBindJSON(&req)
if !imser.SDKEnabled() {
common.ServeJSON(ctx, stderr.Success, &FriendListResp{
Enabled: false,
UserID: uid,
Friends: []imser.IMFriendItem{},
})
return
}
var now int64
if req.Now != nil {
now = *req.Now
}
imUserID, usedNow, nextNow, friends, err := imser.FriendList(uid, now)
if err != nil {
log.Warn("get IM friend list failed", log.Any("uid", uid), log.E(err))
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, &FriendListResp{
Enabled: true,
UserID: uid,
ImUserID: imUserID,
Now: usedNow,
NextNow: nextNow,
Friends: friends,
})
}
type MessageHistoryReq struct {
PeerID uint64 `json:"peerId" binding:"required"`
StartTime *int64 `json:"startTime,omitempty"`
EndTime *int64 `json:"endTime,omitempty"`
StartSeq *int64 `json:"startSeq,omitempty"`
EndSeq *int64 `json:"endSeq,omitempty"`
Direction string `json:"direction,omitempty"`
Size int `json:"size,omitempty"`
}
type MessageHistoryResp struct {
Enabled bool `json:"enabled"`
UserID uint64 `json:"userId"`
ImUserID int64 `json:"imUserId"`
PeerID uint64 `json:"peerId"`
PeerImUserID int64 `json:"peerImUserId"`
Messages []imser.IMHistoryMessage `json:"messages"`
}
func MessageHistory(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req MessageHistoryReq
if err = ctx.ShouldBindJSON(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if req.PeerID == 0 || req.PeerID == uid {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if !imser.SDKEnabled() {
common.ServeJSON(ctx, stderr.Success, &MessageHistoryResp{
Enabled: false,
UserID: uid,
PeerID: req.PeerID,
Messages: []imser.IMHistoryMessage{},
})
return
}
historyReq := imclient.HistoryMessageRequest{
Direction: req.Direction,
Size: req.Size,
}
if req.StartTime != nil {
historyReq.StartTime = *req.StartTime
}
if req.EndTime != nil {
historyReq.EndTime = *req.EndTime
}
if req.StartSeq != nil {
historyReq.StartSeq = *req.StartSeq
}
if req.EndSeq != nil {
historyReq.EndSeq = *req.EndSeq
}
selfIMUserID, peerIMUserID, messages, err := imser.HistoryMessages(uid, req.PeerID, historyReq)
if err != nil {
log.Warn("get IM message history failed", log.Any("uid", uid), log.Any("peerId", req.PeerID), log.E(err))
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, &MessageHistoryResp{
Enabled: true,
UserID: uid,
ImUserID: selfIMUserID,
PeerID: req.PeerID,
PeerImUserID: peerIMUserID,
Messages: messages,
})
}
// SendMessage doc
// @Summary IM 发送私信
// @Description 扣费 + 内容校验通过后,通过第三方 IM 平台投递;参数与 /app/message/priLetter/add 一致
// @Tags IM
// @Accept json
// @Produce json
// @Param body body messagemod.AddMsgReqInfo true "私信参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/im/message/send [post]
func SendMessage(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var req messagemod.AddMsgReqInfo
if err = ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if len(req.ImgUrl) <= 0 && req.Content == "" {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 内容合规校验:失败时静默成功(与 priLetter/add 行为一致,避免泄露规则)
if req.Content != "" && !truthutil.CheckIsValid(req.Content, 1) {
log.Error("IM 私信内容校验不通过", log.Any("uid", uid), log.Any("content", req.Content))
common.ServeJSON(ctx, stderr.Success, nil)
return
}
code := messageser.SendIMPrivateLetter(uid, req)
if code != stderr.Success {
log.Warn("imctrl SendMessage fail",
log.Any("uid", uid), log.Any("takeUid", req.TakeUid), log.Any("code", code))
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
+101
View File
@@ -0,0 +1,101 @@
package imgroupctrl
import (
"91jd-server/app/service/imgroupmemberser"
"91jd-server/app/service/imgroupser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取im群组列表接口
// @Description 获取im群组列表
// @Tags 移动端-im群组
// @Accept mpfd,json
// @Produce json
// @Param q query imgroupser.AppQueryListReq false "请求参数"
// @Success 200 object imgroupser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/imgroup/list [get]
func List(ctx *gin.Context) {
p := &imgroupser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
// var err error
// p.Uid, err = common.GetUID(ctx)
// if err != nil {
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
// return
// }
list := p.GetList()
common.ServeJSON(ctx, stderr.Success, list)
}
// Info doc
//
// @Summary 获取im群组详情接口
// @Description 获取im群组详情
// @Tags 移动端-im群组
// @Accept mpfd,json
// @Produce json
// @Param q query imgroupser.AppQueryInfoReq false "请求参数"
// @Success 200 object imgroupser.AppQueryInfoRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/imgroup/info [get]
func Info(ctx *gin.Context) {
p := &imgroupser.AppQueryInfoReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
data, err := p.GetInfo()
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// HasJoin doc
//
// @Summary 获取是否加入群组接口
// @Description 获取是否加入群组详情
// @Tags 移动端-im群组
// @Accept mpfd,json
// @Produce json
// @Param q query imgroupmemberser.HasJoinReq false "请求参数"
// @Success 200 object imgroupmemberser.HasJoinRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/imgroup/hasjoin [get]
func HasJoin(ctx *gin.Context) {
p := &imgroupmemberser.HasJoinReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
var err error
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
data, err := p.GetInfo(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+74
View File
@@ -0,0 +1,74 @@
package immessagectrl
import (
"91jd-server/app/service/immessageser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取im消息列表接口
// @Description 获取im消息列表
// @Tags 移动端-im消息
// @Accept mpfd,json
// @Produce json
// @Param q query immessageser.AppQueryListReq false "请求参数"
// @Success 200 object immessageser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/immessage/list [get]
func List(ctx *gin.Context) {
p := &immessageser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
// var err error
// p.Uid, err = common.GetUID(ctx)
// if err != nil {
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
// return
// }
list := p.GetList()
common.ServeJSON(ctx, stderr.Success, list)
}
// Send doc
// @Summary 发送im消息
// @Description 发送im消息
// @Tags 移动端-im消息
// @Accept mpfd,json
// @Produce json
// @Param q body immessageser.SendReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/immessage/send [post]
func Send(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &immessageser.SendReq{}
err = ctx.ShouldBindJSON(&p)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
if p.Content == "" && p.Image == "" {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 创建
err = p.Create(uid)
if err != nil {
common.ServeError(ctx, err)
return
}
common.ServeJSON(ctx, stderr.Success, "")
}
+75
View File
@@ -0,0 +1,75 @@
package integeralctrl
import (
"91jd-server/app/service/integral_config_ser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/integralconfigmod"
"github.com/gin-gonic/gin"
)
// ExchangeIntegral doc
// @Summary 积分兑换
// @Description 积分兑换
// @Tags 积分配置
// @Accept mpfd,json
// @Produce json,html
// @Param id formData string true "积分兑换配置ID"
// @Param name formData string false "兑换用户姓名"
// @Param tel formData string false "积分兑换用户电话"
// @Param address formData string false "兑换用户地址"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/integral/exchangeIntegral [post]
func ExchangeIntegral(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var in integralconfigmod.ExchangeIntegralReq
if err = ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code := integral_config_ser.ExchangeIntegral(uid, &in)
if code != stderr.Success {
common.ServeJSON(ctx, code, code.Error())
return
}
common.ServeJSON(ctx, code, stderr.Success.Msg())
}
// GetList doc
// @Summary 积分兑换列表
// @Description 积分兑换列表
// @Tags 积分配置
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/integral/list [get]
func GetList(c *gin.Context) {
data := integral_config_ser.GetAllConfig()
common.ServeJSON(c, stderr.Success, data)
}
// GetRecordList doc
// @Summary 积分兑换记录列表
// @Description 积分兑换记录列表
// @Tags 积分配置
// @Accept mpfd,json
// @Produce json,html
// @Success 200 object integralconfigmod.AppIntegralRecord "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/integral/record/list [get]
func GetRecordList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
data := integral_config_ser.GetRecordConfig(uid)
common.ServeJSON(ctx, stderr.Success, data)
}
+116
View File
@@ -0,0 +1,116 @@
package likectrl
import (
"time"
"91jd-server/app/service/likeser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/v/likemod"
"91jd-server/models/v/noticerecdmod"
"github.com/gin-gonic/gin"
)
// ThumbsUp doc
// @Summary 点赞 - 视频/评论点赞
// @Description 用户操作
// @Tags 点赞
// @Accept mpfd,json
// @Produce json,html
// @Param q body likemod.ReqInfo true "参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /thumbsUp [post]
func ThumbsUp(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
param := likemod.ReqInfo{}
err = ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code, err := likeser.ThumbsUp(ctx, uid, param.Type, param.ObjID, param.TagID)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
common.ServeJSON(ctx, code, nil)
}
// ThumbsDown doc
// @Summary 取消点赞 - 视频/评论取消点赞
// @Description 用户操作
// @Tags 点赞
// @Accept mpfd,json
// @Produce json,html
// @Param q query likemod.DesLikeReq true "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /thumbsDown [post]
func ThumbsDown(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
param := likemod.DesLikeReq{}
err = ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code, err := likeser.ThumbsDown(ctx, uid, param.Type, param.ObjIDs...)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
common.ServeJSON(ctx, code, nil)
}
// Record doc
// @Summary 被赞列表
// @Description 被赞列表
// @Tags 点赞
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber query integer true "当前页" mininum(1)
// @Param pageSize query integer true "每页条数" mininum(1)
// @Success 200 {object} likeser.RecordPage "{"hasNext": true,"list":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/like/record/list [get]
func RecordList(c *gin.Context) {
var arg struct {
commod.Page
}
if err := c.ShouldBind(&arg); err != nil {
common.ServeJSON(c, stderr.ErrParamError, "likectrl RecordList arg error "+err.Error())
return
}
// 检查uid是否存在
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.UserIsNotExists, "likectrl RecordList Context USER_ID is not exist ")
return
}
skip := (arg.PageNumber - 1) * arg.PageSize
limit := arg.PageSize
recordPage, err := likeser.RecordPages(uid, int64(skip), int64(limit))
if err != nil {
common.ServeJSON(c, stderr.Failure, err)
return
}
err = noticerecdmod.UpdateTrendReadTime(noticerecdmod.Like, uid, time.Now())
if err != nil {
log.Error("likectrl RecordList UpdateTrendReadTime faild", log.E(err))
}
common.ServeJSON(c, stderr.Success, recordPage)
}
+145
View File
@@ -0,0 +1,145 @@
package mediabookshelfctrl
import (
"91jd-server/app/service/mediabookshelfser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary 获取媒体书架列表接口
// @Description 获取媒体书架列表
// @Tags 移动端-媒体书架列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediabookshelfser.AppQueryListReq false "请求参数"
// @Success 200 object mediabookshelfser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media_bookshelf/list [get]
func List(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &mediabookshelfser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
list, err := p.GetList(uid)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
common.ServeJSON(ctx, stderr.Success, list)
}
// Add doc
// @Summary 添加媒体进入书架接口
// @Description 添加媒体进入书架
// @Tags 移动端-媒体书架列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediabookshelfser.AppAddBookshelfReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media_bookshelf/add [post]
func Add(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &mediabookshelfser.AppAddBookshelfReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
ua, err := common.GetUA(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
ip := common.GetIP(ctx)
err = p.Add(uid, ua, ip)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// Del doc
// @Summary 删除书架中媒体接口
// @Description 删除书架中媒体
// @Tags 移动端-媒体书架列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediabookshelfser.AppDelBookshelfReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media_bookshelf/del [post]
func Del(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &mediabookshelfser.AppDelBookshelfReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
ua, err := common.GetUA(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
ip := common.GetIP(ctx)
err = p.Del(uid, ua, ip)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// DelBatch doc
// @Summary 批量删除书架中媒体接口
// @Description 批量删除书架中媒体
// @Tags 移动端-媒体书架列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediabookshelfser.AppDelBatchBookshelfReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media_bookshelf/del/batch [post]
func DelBatch(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &mediabookshelfser.AppDelBatchBookshelfReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
err = p.Del(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
+94
View File
@@ -0,0 +1,94 @@
package mediacontentctrl
import (
"91jd-server/app/service/m3u8ticket"
"91jd-server/app/service/mediacontentser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary 获取动漫内容列表列表接口
// @Description 获取动漫内容列表列表
// @Tags 移动端-动漫内容列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediacontentser.AppQueryListReq false "请求参数"
// @Success 200 object mediacontentser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media_content/list [get]
func List(ctx *gin.Context) {
p := &mediacontentser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
// var err error
// p.Uid, err = common.GetUID(ctx)
// if err != nil {
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
// return
// }
// 获取用户当前配置
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
list, err := p.GetList(uid)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
// H5 防盗链:逐条为动漫/有声内容的 m3u8 地址签票(非 H5/未开启时零副作用)。
for i := range list.List {
m3u8ticket.SignURL(ctx, uid, &list.List[i].VideoUrl, true, false)
m3u8ticket.SignURL(ctx, uid, &list.List[i].H265Url, true, false)
m3u8ticket.SignURL(ctx, uid, &list.List[i].AudioUrl, true, false)
m3u8ticket.SignURL(ctx, uid, &list.List[i].PreviewVideoUrl, false, true)
m3u8ticket.SignURL(ctx, uid, &list.List[i].PreviewH265Url, false, true)
}
common.ServeJSON(ctx, stderr.Success, list)
}
// Info doc
//
// @Summary 获取动漫内容列表详情接口
// @Description 获取动漫内容列表详情
// @Tags 移动端-动漫内容列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediacontentser.AppQueryInfoReq false "请求参数"
// @Success 200 object mediacontentser.MediaContentInfo "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media_content/info [get]
func Info(ctx *gin.Context) {
p := &mediacontentser.AppQueryInfoReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
data, err := p.GetInfo(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
// H5 防盗链:为动漫/有声详情的 m3u8 地址签票(非 H5/未开启时零副作用)。
m3u8ticket.SignURL(ctx, uid, &data.VideoUrl, true, false)
m3u8ticket.SignURL(ctx, uid, &data.H265Url, true, false)
m3u8ticket.SignURL(ctx, uid, &data.AudioUrl, true, false)
m3u8ticket.SignURL(ctx, uid, &data.PreviewVideoUrl, false, true)
m3u8ticket.SignURL(ctx, uid, &data.PreviewH265Url, false, true)
common.ServeJSON(ctx, stderr.Success, data)
}
+295
View File
@@ -0,0 +1,295 @@
package mediactrl
import (
"91jd-server/app/service/mediaser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/v/mediamod"
"fmt"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取动漫列表列表接口
// @Description 获取动漫列表列表
// @Tags 移动端-动漫列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediaser.AppQueryListReq false "请求参数"
// @Success 200 object mediaser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/list [get]
func List(ctx *gin.Context) {
p := &mediaser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
// var err error
// p.Uid, err = common.GetUID(ctx)
// if err != nil {
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
// return
// }
list := p.GetList()
common.ServeJSON(ctx, stderr.Success, list)
}
// Info doc
// @Summary 获取动漫列表详情接口
// @Description 获取动漫列表详情
// @Tags 移动端-动漫列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediaser.AppQueryInfoReq false "请求参数"
// @Success 200 object mediamod.AppMediaBase "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/info [get]
func Info(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &mediaser.AppQueryInfoReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
data, err := p.GetInfo(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// Library doc
// @Summary 获取动漫片库接口
// @Description 获取动漫片库详情
// @Tags 移动端-动漫列表
// @Accept mpfd,json
// @Produce json
// @Success 200 object mediamod.AppLibrary "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/library [get]
func Library(ctx *gin.Context) {
data, err := mediaser.GetLibrary()
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// LibrarySearch doc
// @Summary 动漫片库搜索接口
// @Description 动漫片库搜索详情
// @Tags 移动端-动漫列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediamod.AppLibraryReq false "请求参数"
// @Success 200 object mediamod.AppElasticSearchLibraryResponse "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/library/search [post]
func LibrarySearch(ctx *gin.Context) {
req := mediamod.AppLibraryReq{}
if err := ctx.ShouldBind(&req); err != nil {
log.Error(fmt.Sprintf("media librarySearch param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := mediaser.LibrarySearch(req)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// Search doc
// @Summary 动漫片库搜索接口
// @Description 动漫片库搜索详情
// @Tags 移动端-动漫列表
// @Accept mpfd,json
// @Produce json
// @Param q query mediamod.AppSearchReq false "请求参数"
// @Success 200 object mediamod.AppElasticSearchResponse "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/search [get]
func Search(ctx *gin.Context) {
req := mediamod.AppSearchReq{}
if err := ctx.ShouldBind(&req); err != nil {
log.Error(fmt.Sprintf("media Search param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
uid, _ := common.GetUID(ctx)
data, err := mediaser.Search(uid, req)
if err != nil {
log.Error(fmt.Sprintf("media Search err:%v", err))
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// GetTopicList doc
// @Summary 更多-动漫专题列表
// @Description 动漫专题列表
// @Tags 移动端-动漫专题
// @Accept mpfd,json
// @Produce json
// @Param q query mediaser.AppGetTopicReq false "请求参数"
// @Success 200 object mediaser.AppGetTopicRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/topic [get]
func GetTopicList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &mediaser.AppGetTopicReq{}
if err := ctx.ShouldBind(&p); err != nil {
log.Error(fmt.Sprintf("GetTopicList param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
list, err := p.GetTopicList(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, list)
}
// Recommend doc
// @Summary 动漫详情-推荐列表
// @Description 动漫详情-推荐列表
// @Tags 移动端-动漫推荐
// @Accept mpfd,json
// @Produce json
// @Param q query mediaser.AppRecommendReq false "请求参数"
// @Success 200 object mediaser.AppRecommendRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/recommend [get]
func Recommend(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &mediaser.AppRecommendReq{}
if err := ctx.ShouldBind(&p); err != nil {
log.Error(fmt.Sprintf("Recommend param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
list, err := p.GetList(uid)
if err != nil {
log.Error(fmt.Sprintf("Recommend GetList err:%v", err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, list)
}
// Ranking doc
// @Summary 动漫排行榜
// @Description 动漫排行榜
// @Tags 移动端-动漫排行榜
// @Accept mpfd,json
// @Produce json
// @Param q query mediaser.RankingReq false "请求参数"
// @Success 200 object mediaser.RankingResp "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/ranking [get]
func Ranking(ctx *gin.Context) {
p := &mediaser.RankingReq{}
if err := ctx.ShouldBind(&p); err != nil {
log.Error(fmt.Sprintf("Rank param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
res, err := p.List()
if err != nil {
log.Error(fmt.Sprintf("Get Ranking List err:%v", err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, res)
}
// Hot doc
// @Summary 热门动漫
// @Description 热门动漫
// @Tags 移动端-动漫
// @Accept mpfd,json
// @Produce json
// @Param q query mediaser.HotReq false "请求参数"
// @Success 200 object mediaser.HotResp "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/hot [get]
func Hot(ctx *gin.Context) {
p := &mediaser.HotReq{}
if err := ctx.ShouldBind(&p); err != nil {
log.Error(fmt.Sprintf("Hot param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
resp, err := p.List()
if err != nil {
log.Error(fmt.Sprintf("Get Hot Media List err:%v", err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, resp)
}
// MyBuy doc
// @Summary 我的购买
// @Description 我的购买
// @Tags 移动端-我的购买
// @Accept mpfd,json
// @Produce json
// @Param q query mediaser.MyBuyReq false "请求参数"
// @Success 200 object mediaser.MyBuyResp "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media/my_buy [get]
func MyBuy(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
p := &mediaser.MyBuyReq{}
if err := ctx.ShouldBind(&p); err != nil {
log.Error(fmt.Sprintf("MyBuy param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
resp, err := p.List(uid)
if err != nil {
log.Error(fmt.Sprintf("Get MyBuy Media List err:%v", err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, resp)
}
+65
View File
@@ -0,0 +1,65 @@
package mediatagctrl
import (
"91jd-server/app/service/mediatagser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取动漫标签列表接口
// @Description 获取动漫标签列表
// @Tags 移动端-动漫标签
// @Accept mpfd,json
// @Produce json
// @Param q query mediatagser.AppQueryListReq false "请求参数"
// @Success 200 object mediatagser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media_tag/list [get]
func List(ctx *gin.Context) {
p := &mediatagser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
// var err error
// p.Uid, err = common.GetUID(ctx)
// if err != nil {
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
// return
// }
list := p.GetList()
common.ServeJSON(ctx, stderr.Success, list)
}
// Info doc
//
// @Summary 获取动漫标签详情接口
// @Description 获取动漫标签详情
// @Tags 移动端-动漫标签
// @Accept mpfd,json
// @Produce json
// @Param q query mediatagser.AppQueryInfoReq false "请求参数"
// @Success 200 object mediatagmod.MediaTag "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/media_tag/info [get]
func Info(ctx *gin.Context) {
p := &mediatagser.AppQueryInfoReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
data, err := p.GetInfo()
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+250
View File
@@ -0,0 +1,250 @@
package messagectrl
import (
"91jd-server/app/service/messageser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/common/truthutil"
"91jd-server/models/v/messagemod"
"91jd-server/models/v/sessionmod"
"fmt"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary 消息
// @Description 动态列表
// @Tags 私信模块
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Param msgType formData string true "消息类型:comment_msg 评论"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/msg/dynamic/list [get]
func List(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var in *messagemod.QueryCond
if err := ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, has, code := messageser.QueryDynamics(uid, in)
if code != stderr.Success {
common.ServeJSON(ctx, code, nil)
return
}
resp := make(map[string]interface{})
resp["list"] = data
resp["hasNext"] = has
common.ServeJSON(ctx, stderr.Success, resp)
}
// NoRedNum doc
// @Summary 消息
// @Description 动态列表
// @Tags 私信模块
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/msg/dynamic/noRedNum [get]
func NoRedNum(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
data, code := messageser.QueryNoRedDynamicNum(uid)
if code != stderr.Success {
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// SessionList doc
// @Summary 消息
// @Description 会话列表
// @Tags 私信模块
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/msg/session/list [get]
func SessionList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var in *sessionmod.QueryCond
if err := ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, has, code := messageser.QueryPrivateLetterSession(uid, in)
if code != stderr.Success {
common.ServeJSON(ctx, code, nil)
return
}
resp := make(map[string]interface{})
resp["list"] = data
resp["hasNext"] = has
common.ServeJSON(ctx, stderr.Success, resp)
}
// DelSession doc
// @Summary 消息
// @Description 删除会话
// @Tags 私信模块
// @Accept mpfd,json
// @Produce json,html
// @Param sessionId formData string true "会话ID"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/msg/session/del [post]
func DelSession(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
type req struct {
SessionId string `json:"sessionId" form:"sessionId"` // 会话id
}
var in req
if err := ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code := messageser.DelSession(uid, in.SessionId)
if code != stderr.Success {
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
// GetSessionId doc
// @Summary 消息
// @Description 获取sessionId
// @Tags 私信模块
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/msg/session/get [get]
func GetSessionId(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var in *sessionmod.QuerySessionIdCond
if err := ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if in.TakeUid <= 0 {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
sessionId := messageser.GetSessionId(uid, in.TakeUid)
if sessionId == "" {
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, sessionId)
}
// MessageList doc
// @Summary 消息
// @Description 会话消息列表
// @Tags 私信模块
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/msg/message/list [get]
func MessageList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var in *messagemod.QueryMsgCond
if err := ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, has, code := messageser.QueryPrivateLetterMsg(uid, in)
if code != stderr.Success {
common.ServeJSON(ctx, code, nil)
return
}
resp := make(map[string]interface{})
resp["list"] = data
resp["hasNext"] = has
common.ServeJSON(ctx, stderr.Success, resp)
}
// PrivateLetter doc
// @Summary 消息
// @Description 发消息(私信)
// @Tags 私信模块
// @Accept mpfd,json
// @Produce json,html
// @Param takeUid formData string true "接受用户uid"
// @Param imgUrl formData []string false "图片内容"
// @Param content formData integer false "消息内容"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/message/priLetter/add [post]
func PrivateLetter(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
param := messagemod.AddMsgReqInfo{}
err = ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if len(param.ImgUrl) <= 0 && param.Content == "" {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
// 检查发送内容的合法性
if param.Content != "" && !truthutil.CheckIsValid(param.Content, 1) {
log.Error("私信检查不通过", log.Any("uid", uid), log.Any("content", param.Content))
common.ServeJSON(ctx, stderr.Success, nil)
return
}
code := messageser.AddPrivateLetter(uid, param)
if code != stderr.Success {
log.Warn(fmt.Sprintf("messagectrl Add messageser.AddPrivateLetter error:%+v:", code.Msg()), log.Any("uid", uid))
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
+34
View File
@@ -0,0 +1,34 @@
package minectrl
import (
"91jd-server/app/service/feedbackser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// FeedBack doc
// @Summary 用户 - 用户反馈
// @Description 用户反馈
// @Tags mine
// @Accept mpfd,json
// @Produce json,html
// @Param q body feedbackser.FeedbackReq false "请求参数"
// @Success 200 {string} json "{"msg": "成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/feedback [post]
func FeedBack(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
var req = feedbackser.FeedbackReq{}
if err = ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
ua, _ := common.GetUA(ctx)
code, err := req.Submit(ua, uid)
common.ServeJSON(ctx, code, err)
}
+176
View File
@@ -0,0 +1,176 @@
package minectrl
import (
"91jd-server/app/service/collectser"
"91jd-server/app/service/mineser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/v/collectmod"
"91jd-server/models/v/followmod"
"91jd-server/models/v/userResourcemod"
"fmt"
"github.com/gin-gonic/gin"
)
// Follow doc
// @Summary 关注或者取消关注
// @Description 关注或者取消关注
// @Tags 关注
// @Accept mpfd,json
// @Produce json,html
// @Param followUID formData number true "关注的用户uid"
// @Param isShort formData bool true "是否是短视频用户"
// @Param isFollow formData bool true "true则是关注,false则是取消关注"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/mine/follow [post]
func Follow(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
param := followmod.UserFollowReq{}
err = ctx.ShouldBind(&param)
if err != nil {
log.Error(fmt.Sprintf("mine follow param error:%v,uid:%v", err, uid))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if param.FollowUID <= 0 {
log.Error(fmt.Sprintf("mine follow param followUid err param:%+v,uid:%v", param, uid))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
code, data := mineser.Follow(uid, param.FollowUID, param.IsFollow, param.IsShort)
if code != stderr.Success {
log.Error(fmt.Sprintf("mine follow mineser Follow err:%+v,uid:%v", data, uid))
}
common.ServeJSON(ctx, code, data)
}
// UserDownload doc
// @Summary 使用下载次数
// @Description 使用下载次数
// @Tags 我的
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/mine/download/use [post]
func UserDownload(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
code, data := mineser.UseDownloadCount(uid)
if code != stderr.Success {
common.ServeJSON(ctx, code, data)
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
// Collect doc
// @Summary 用户模块 - 用户收藏信息
// @Description 保存用户一条收藏信息
// @Tags mine
// @Accept json
// @Produce json
// @Param q body collectmod.DoCollectReqInfo true "参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/mine/collect [post]
func Collect(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
param := collectmod.DoCollectReqInfo{}
err = ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
code, err := collectser.DoCollect(uid, param.Type, param.ObjID, param.IsCollect, ua, ip)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
common.ServeJSON(ctx, code, nil)
}
// InfoList doc
// @Summary 用户模块 - 用户收藏详情列表
// @Description 视频/地点/话题 列表
// @Tags mine
// @Accept json
// @Produce json
// @Param type formData string true "收藏类型 video:视频 tag:专题 location:地点"
// @Param pageNumber formData integer true "页数"
// @Param pageSize formData integer true "每页条数"
// @Param uid formData integer true "用户uid"
// @Success 200 {object} commod.ListResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/mine/collect/infoList [get]
func InfoList(ctx *gin.Context) {
type Info struct {
Type string `form:"type" json:"type" binding:"required"`
UID uint64 `form:"uid" json:"uid" binding:"required"`
Page commod.Page
}
param := Info{}
err := ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, "api mine collect InfoList ShouldBind err"+err.Error())
return
}
code, hasNext, data, err := collectser.GetInfoList(param.UID, param.Type, param.Page)
if err != nil {
common.ServeJSON(ctx, code, "api mine collect InfoList GetInfoList err"+err.Error())
return
}
common.ServeJSON(ctx, code, commod.ListResp{HasNext: hasNext, List: data})
}
// UserResourceList doc
// @Summary 用户资源列表
// @Description 用户资源列表
// @Tags mine
// @Accept json
// @Produce json
// @Param type formData string true "收藏类型 video:视频 tag:专题 location:地点"
// @Param pageNumber formData integer true "页数"
// @Param pageSize formData integer true "每页条数"
// @Param uid formData integer true "用户uid"
// @Success 200 {object} commod.ListResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/mine/userResource/list [get]
func UserResourceList(ctx *gin.Context) {
type Info struct {
Type string `form:"type" json:"type" binding:"required"`
Page commod.Page
}
param := Info{}
err := ctx.ShouldBind(&param)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, "api mine collect InfoList ShouldBind err"+err.Error())
return
}
data, total, err := userResourcemod.GetUserResource(int(param.Page.PageNumber), int(param.Page.PageSize), param.Type)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, gin.H{
"list": data,
"total": total,
})
}
+137
View File
@@ -0,0 +1,137 @@
package modulectrl
import (
"91jd-server/app/appg"
"91jd-server/app/service/moduleser"
"91jd-server/common"
"91jd-server/common/constant/redisconst"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/v/moduleconfmod"
"fmt"
"strings"
"time"
"github.com/vmihailenco/msgpack/v5"
"github.com/gin-gonic/gin"
)
// List ...
// @Summary 获取系统的所有后台配置模块
// @Description 获取系统的所有后台配置模块
// @Tags 模块配置
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {array} []moduleconfmod.AppModuleConf
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/modules/list [get]
func List(c *gin.Context) {
ip := c.ClientIP()
str, err := appg.Redis.Get(redisconst.ModulesCache)
if err != nil {
log.Warn(fmt.Sprintf("IP:%s;缓存获取广告列表信息异常:%v", ip, err))
}
var modules []moduleconfmod.ModuleConf
if str != nil {
if err = msgpack.Unmarshal([]byte(*str), &modules); err != nil {
log.Warn(fmt.Sprintf("IP:%s;解析缓存数据异常:%v", ip, err))
modules = nil
}
}
if modules == nil {
modules, err = moduleconfmod.GetAllModule()
if err != nil {
common.ServeJSON(c, stderr.ErrDbQueryError, err)
return
}
common.Go(func() {
bytes, _ := msgpack.Marshal(modules)
if setErr := appg.Redis.Set(redisconst.ModulesCache, bytes, 600*time.Second); setErr != nil {
log.Warn(fmt.Sprintf("IP:%s;保存缓存数据异常:%v", ip, setErr))
}
})
}
var data moduleconfmod.AppModuleConf
if len(modules) <= 0 {
common.ServeJSON(c, stderr.Success, data)
return
}
now := time.Now()
for _, m := range modules {
if !m.IsActiveAt(now) {
continue
}
m.HaiJiaoStyle.EnsureSortRules()
for k, v := range m.HaiJiaoStyle.SortRules {
v.Name = v.Val.Name()
m.HaiJiaoStyle.SortRules[k] = v
}
subConf := moduleconfmod.APPModuleConf{
ID: m.ID,
ModuleName: m.ModuleName,
Cover: m.Cover,
Type: m.Type,
ShowType: m.ShowType,
ShowJG: m.ShowJG,
HaiJiaoStyle: m.HaiJiaoStyle,
AiPlazaStyle: m.AiPlazaStyle,
PureVersion: m.PureVersion,
OnlineAt: m.OnlineAt,
OfflineAt: m.OfflineAt,
ExcludeLatest: m.ExcludeLatest,
ExcludeRecommend: m.ExcludeRecommend,
ExcludeSearch: m.ExcludeSearch,
SearchOnlyWhenInactive: m.SearchOnlyWhenInactive,
}
if !m.DefaultTagId.IsZero() {
subConf.DefaultTagId = m.DefaultTagId.Hex()
}
switch m.Type {
case moduleconfmod.HomePage, moduleconfmod.Cartoon, moduleconfmod.Comics:
data.HomePage = append(data.HomePage, subConf)
case moduleconfmod.Pics:
data.Pics = append(data.Pics, subConf)
case moduleconfmod.Novel:
data.Novel = append(data.Novel, subConf)
case moduleconfmod.Community:
data.Community = append(data.Community, subConf)
//case moduleconfmod.PrivateCircle:
//data.PrivateCircle = append(data.PrivateCircle, subConf)
case moduleconfmod.DeepWeb:
data.DeepWeb = append(data.DeepWeb, subConf)
case moduleconfmod.ShortPage:
data.ShortPage = append(data.ShortPage, subConf)
case moduleconfmod.Drama:
// 短剧模块同时下发到首页顶部模块。
data.DramaPage = append(data.DramaPage, subConf)
// 短剧模块名称不包含热门,AI的模块需要同步到首页
if !strings.Contains(subConf.ModuleName, "热门") && !strings.Contains(subConf.ModuleName, "AI") {
data.HomePage = append(data.HomePage, subConf)
}
}
}
common.ServeJSON(c, stderr.Success, data)
}
// Announcements ...
// @Summary 获取跑马灯
// @Description 获取跑马灯
// @Tags 模块配置
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} moduleser.AnnouncementResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/modules/announce [get]
func Announcements(c *gin.Context) {
resp, err := moduleser.GetModuleAnnouncements()
if err != nil {
common.ServeJSON(c, stderr.ErrDbQueryError, err)
return
}
common.ServeJSON(c, stderr.Success, resp)
}
+66
View File
@@ -0,0 +1,66 @@
package nakedchatctrl
import (
"91jd-server/app/service/nakedchatser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取裸聊列表接口
// @Description 获取裸聊列表
// @Tags 移动端-裸聊
// @Accept mpfd,json
// @Produce json
// @Param q query nakedchatser.AppQueryListReq false "请求参数"
// @Success 200 object nakedchatser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/nakedchat/list [get]
func List(ctx *gin.Context) {
p := &nakedchatser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
// var err error
// p.Uid, err = common.GetUID(ctx)
// if err != nil {
// common.ServeJSON(ctx, stderr.ErrNoToken, err)
// return
// }
list := p.GetList()
common.ServeJSON(ctx, stderr.Success, list)
}
// Info doc
//
// @Summary 获取裸聊详情接口
// @Description 获取裸聊详情
// @Tags 移动端-裸聊
// @Accept mpfd,json
// @Produce json
// @Param q query nakedchatser.AppQueryInfoReq false "请求参数"
// @Success 200 object nakedchatser.AppQueryInfoRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/nakedchat/info [get]
func Info(ctx *gin.Context) {
p := &nakedchatser.AppQueryInfoReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
data, err := p.GetInfo()
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+39
View File
@@ -0,0 +1,39 @@
package nakedchatorderctrl
import (
"91jd-server/app/service/nakedchatorderser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// List doc
//
// @Summary 获取裸聊订单列表接口
// @Description 获取裸聊订单列表
// @Tags 移动端-裸聊订单
// @Accept mpfd,json
// @Produce json
// @Param q query nakedchatorderser.AppQueryListReq false "请求参数"
// @Success 200 object nakedchatorderser.AppListRes "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/nakedchatorder/list [get]
func List(ctx *gin.Context) {
p := &nakedchatorderser.AppQueryListReq{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 获取用户当前配置
var err error
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
list := p.GetList(uid)
common.ServeJSON(ctx, stderr.Success, list)
}
+41
View File
@@ -0,0 +1,41 @@
package notictrl
import (
"91jd-server/app/service/notiser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// SendCaptcha doc
// @Summary 发送验证码
// @Description 发送用户注册、登录的验证码
// @Tags captcha
// @Accept json
// @Produce json
// @Param mobile formData string false "手机号码信息"
// @Param email formData string false "邮箱地址"
// @Param type formData integer false "发送验证码的用途 1-绑定手机号 2-手机号登陆 3-email"
// @Success 200 {string} string "操作成功"
// @Router /api/app/notification/captcha [post]
func SendCaptcha(ctx *gin.Context) {
var args struct {
Mobile string `form:"mobile" json:"mobile"`
Email string `form:"email" json:"email"`
Type int `form:"type" json:"type"`
}
if err := ctx.ShouldBind(&args); err != nil {
log.Warn("SendCaptcha bind args", log.E(err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if args.Email == "" && args.Mobile == "" {
log.ErrorX(ctx, "empty phone number and email", log.Any("args", args))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
errcode := notiser.SendCaptcha(ctx, args.Mobile, args.Email, args.Type)
common.ServeJSON(ctx, errcode, nil)
}
+48
View File
@@ -0,0 +1,48 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/cachev2"
"91jd-server/common/constant/redisconst"
"91jd-server/common/stderr"
"fmt"
"github.com/gin-gonic/gin"
)
func AlbumList(ctx *gin.Context) {
req := &officialWebsiteser.AlbumListReq{}
if err := ctx.ShouldBindQuery(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
var data officialWebsiteser.AlbumListResp
_, err := cachev2.Classes().
CacheTime(redisconst.OfficialWebsiteAlbumListCacheExpire).
AutoListKey(fmt.Sprintf(redisconst.OfficialWebsiteAlbumListCacheKey, req.PageNumber, req.PageSize)).
ResBind(&data).Cache(officialWebsiteser.AlbumList, req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, &data)
}
func AlbumDetail(ctx *gin.Context) {
var req = &officialWebsiteser.AlbumDetailReq{}
if err := ctx.ShouldBindUri(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
if err := ctx.ShouldBindQuery(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
data, err := officialWebsiteser.AlbumDetail(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+28
View File
@@ -0,0 +1,28 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/cachev2"
"91jd-server/common/constant/redisconst"
"91jd-server/common/stderr"
"91jd-server/models/v/sourcemod"
"github.com/gin-gonic/gin"
)
func GetBasicData(ctx *gin.Context) {
var data = officialWebsiteser.GetBasicDataResp{}
_, err := cachev2.Classes().
CacheTime(redisconst.OfficialWebsiteBasicDataCacheExpire).
AutoListKey(redisconst.OfficialWebsiteBasicDataCacheKey).
ResBind(&data).
Cache(officialWebsiteser.GetBasicData)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
// 域名信息实时获取,不随基础数据大缓存(12h),避免下发已失效的域名
data.Domain, data.SourceList = sourcemod.PingList()
common.ServeJSON(ctx, stderr.Success, &data)
}
+23
View File
@@ -0,0 +1,23 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
func BusinessList(ctx *gin.Context) {
req := &officialWebsiteser.BusinessListReq{}
if err := ctx.ShouldBindQuery(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
data, err := officialWebsiteser.BusinessList(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+44
View File
@@ -0,0 +1,44 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/cachev2"
"91jd-server/common/constant/redisconst"
"91jd-server/common/stderr"
"fmt"
"github.com/gin-gonic/gin"
)
func HeroList(ctx *gin.Context) {
req := &officialWebsiteser.HeroListReq{}
if err := ctx.ShouldBindQuery(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
var data officialWebsiteser.HeroListResp
_, err := cachev2.Classes().
CacheTime(redisconst.OfficialWebsiteHeroListCacheExpire).
AutoListKey(fmt.Sprintf(redisconst.OfficialWebsiteHeroListCacheKey, req.SortType, req.PageNumber, req.PageSize)).
ResBind(&data).Cache(officialWebsiteser.HeroList, req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
func HeroDetail(ctx *gin.Context) {
req := &officialWebsiteser.HeroDetailReq{}
if err := ctx.ShouldBindUri(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
data, err := officialWebsiteser.HeroDetail(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+23
View File
@@ -0,0 +1,23 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
func JobList(ctx *gin.Context) {
req := &officialWebsiteser.JobListReq{}
if err := ctx.ShouldBindQuery(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
data, err := officialWebsiteser.JobList(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+44
View File
@@ -0,0 +1,44 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/cachev2"
"91jd-server/common/constant/redisconst"
"91jd-server/common/stderr"
"fmt"
"github.com/gin-gonic/gin"
)
func NewsList(ctx *gin.Context) {
req := &officialWebsiteser.NewsListReq{}
if err := ctx.ShouldBindQuery(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
var data officialWebsiteser.NewsListResp
_, err := cachev2.Classes().
CacheTime(redisconst.OfficialWebsiteNewsListCacheExpire).
AutoListKey(fmt.Sprintf(redisconst.OfficialWebsiteNewsListCacheKey, req.PageNumber, req.PageSize)).
ResBind(&data).Cache(officialWebsiteser.NewsList, req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, gin.H{"hasNext": data.HasNext, "list": data.List})
}
func NewsDetail(ctx *gin.Context) {
req := &officialWebsiteser.NewsDetailReq{}
if err := ctx.ShouldBindUri(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
data, err := officialWebsiteser.NewsDetail(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+29
View File
@@ -0,0 +1,29 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/cachev2"
"91jd-server/common/constant/redisconst"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
func PartnerList(ctx *gin.Context) {
req := &officialWebsiteser.PartnerListReq{}
if err := ctx.ShouldBindQuery(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
var data officialWebsiteser.PartnerListResp
_, err := cachev2.Classes().
CacheTime(redisconst.OfficialWebsitePartnerListCacheExpire).
AutoListKey(redisconst.OfficialWebsitePartnerListCacheKey).
ResBind(&data).Cache(officialWebsiteser.PartnerList, req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+23
View File
@@ -0,0 +1,23 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
func RecruitForm(ctx *gin.Context) {
req := &officialWebsiteser.RecruitFormReq{}
if err := ctx.ShouldBindJSON(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
data, err := officialWebsiteser.RecruitForm(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+23
View File
@@ -0,0 +1,23 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
func TagList(ctx *gin.Context) {
req := &officialWebsiteser.TagListReq{}
if err := ctx.ShouldBindQuery(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
data, err := officialWebsiteser.TagList(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+70
View File
@@ -0,0 +1,70 @@
package officialWebsitectrl
import (
"91jd-server/app/service/officialWebsiteser"
"91jd-server/common"
"91jd-server/common/cachev2"
"91jd-server/common/constant/redisconst"
"91jd-server/common/stderr"
"fmt"
"strings"
"github.com/gin-gonic/gin"
)
func VideoList(ctx *gin.Context) {
req := &officialWebsiteser.VideoListReq{}
if err := ctx.ShouldBindQuery(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
var data officialWebsiteser.VideoListResp
_, err := cachev2.Classes().
CacheTime(redisconst.OfficialWebsiteVideoListCacheExpire).
AutoListKey(fmt.Sprintf(redisconst.OfficialWebsiteVideoListCacheKey, req.Type, req.ID, req.Sort, req.PageNumber, req.PageSize)).
ResBind(&data).Cache(officialWebsiteser.VideoList, req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
func VideoDetail(ctx *gin.Context) {
req := &officialWebsiteser.VideoDetailReq{}
if err := ctx.ShouldBindUri(req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
data, err := officialWebsiteser.VideoDetail(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
func VideoCheck(ctx *gin.Context) {
source := ctx.Param("source")
if source == "" {
common.ServeJSON(ctx, stderr.ErrParamError, "")
ctx.Abort()
}
id := ctx.Param("id")
var req = &officialWebsiteser.VideoDetailReq{ID: id}
data, err := officialWebsiteser.VideoDetail(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
ctx.Abort()
}
if data.ID == "" {
common.ServeJSON(ctx, stderr.ErrParamError, "")
ctx.Abort()
}
if !strings.Contains(source, data.Url) {
common.ServeJSON(ctx, stderr.ErrParamError, "")
ctx.Abort()
}
ctx.Next()
}
+34
View File
@@ -0,0 +1,34 @@
package officialctrl
import (
"91jd-server/app/service/officialser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/officialmod"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary 官方列表
// @Description APP-官方列表
// @Tags 官方列表
// @Accept mpfd,json
// @Produce json,html
// @Param type query integer true "类型,1:下载 2:社区"
// @Success 200 {string} json "{"msg": "操作成功", "data":[]}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/official/list [get]
func List(ctx *gin.Context) {
var in *officialmod.QueryCond
if err := ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
ua, _ := common.GetUA(ctx)
data, code := officialser.QueryAll(in, ua.Ver, ua.SysType)
if code != stderr.Success {
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
+50
View File
@@ -0,0 +1,50 @@
package paymentguidectrl
import (
"strings"
"91jd-server/app/service/paymentguideser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/paymentguidemod"
"github.com/gin-gonic/gin"
)
func Get(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
scene := strings.ToUpper(strings.TrimSpace(ctx.Query("scene")))
if !paymentguidemod.ValidScene(scene) {
common.ServeJSON(ctx, stderr.ErrParamError, "invalid scene")
return
}
resp, err := paymentguideser.GetGuide(uid, scene)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
common.ServeJSON(ctx, stderr.Success, resp)
}
func Impression(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
req := paymentguideser.ImpressionReq{}
if err = ctx.ShouldBindJSON(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
req.Scene = strings.ToUpper(strings.TrimSpace(req.Scene))
if err = paymentguideser.RecordImpression(uid, req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, "")
}
+919
View File
@@ -0,0 +1,919 @@
package pingctrl
import (
"91jd-server/app/service/activityclient"
"91jd-server/app/service/adser"
"91jd-server/app/service/advance_ser"
"91jd-server/app/service/ai_mate_ser"
"91jd-server/app/service/messageser"
"91jd-server/app/service/paymentguideser"
"91jd-server/app/service/reportser"
"91jd-server/app/service/sys_config"
"91jd-server/common/constant"
"91jd-server/common/services/message"
"91jd-server/common/store"
"91jd-server/models/cache/bannerjumpdata"
"91jd-server/models/cache/sysconfdata"
"91jd-server/models/v/bannerjumpmod"
"91jd-server/models/v/jingangmod"
"91jd-server/models/v/sysconfmod"
"91jd-server/models/v/walletmod"
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"sync"
"time"
"91jd-server/app/appg"
"91jd-server/app/middleware/requestEncrypt"
"91jd-server/app/proto"
"91jd-server/app/service/versionser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/common/version"
"91jd-server/models/commod"
"91jd-server/models/v/sourcemod"
"91jd-server/models/v/systemmod"
"91jd-server/models/v/usermod"
"91jd-server/models/v/versionmod"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// newUserAdFree 判断当前用户是否处于新人免广告期内。
// 当【新人广告开关】(VCodeNewUserAdFreeSwitch) 开启,且用户已登录、注册时间仍在
// 【新人免广告时限】(VCodeNewUserAdFreeHours) 内时返回 true,此时
// /ping/domain 与 /ping/domain/h5 不返回广告信息。
func newUserAdFree(configure sysconfmod.ConfMap, user *usermod.User) bool {
if !configure.GetBool(sysconfmod.VCodeNewUserAdFreeSwitch) {
return false
}
if user == nil {
return false
}
hours := configure.GetInt(sysconfmod.VCodeNewUserAdFreeHours)
if hours <= 0 {
return false
}
return user.CreatedAt.Add(time.Duration(hours) * time.Hour).After(time.Now())
}
func shortDramaEntryPopupEnabled(configure sysconfmod.ConfMap) bool {
if _, exists := configure[string(sysconfmod.VCodeShortDramaEntryPopup)]; !exists {
return true
}
return configure.GetBool(sysconfmod.VCodeShortDramaEntryPopup)
}
const (
defaultEntryPageHome = "home"
defaultEntryPageDrama = "drama"
defaultEntryAudienceNew = "new_user"
defaultEntryAudienceAll = "all_users"
)
func normalizeDefaultEntryPage(page string) string {
switch strings.TrimSpace(page) {
case defaultEntryPageDrama:
return defaultEntryPageDrama
case defaultEntryPageHome:
return defaultEntryPageHome
default:
return defaultEntryPageHome
}
}
// defaultEntryAudienceMatch 判断当前用户是否属于默认入口配置的生效对象。
// 注册未满24小时视为新用户;已经处理过旧版本的老用户升级后也命中一次。
// 历史用户首次接入版本标记时,以最近登录版本兼容判断是否刚升级。
func defaultEntryAudienceMatch(audience, currentVer string, user *usermod.User, now time.Time) bool {
switch strings.TrimSpace(audience) {
case defaultEntryAudienceAll:
return true
case defaultEntryAudienceNew:
if user == nil || currentVer == "" {
return false
}
if user.IsNewUser(now) {
return true
}
if user.DefaultEntryHandledVer == currentVer {
return false
}
if user.DefaultEntryHandledVer != "" {
return true
}
return user.LastVer != "" && user.LastVer != currentVer
default:
return false
}
}
// defaultEntryClaimAccepted 判断版本标记的原子抢占结果是否允许本次进入配置页。
// 注册未满24小时的用户持续命中新用户规则,不受版本标记及其缓存状态影响;
// 超过24小时的升级老用户仍只允许首次抢占成功的请求命中。
func defaultEntryClaimAccepted(audience string, user *usermod.User, now time.Time, claimed bool, claimErr error) bool {
if strings.TrimSpace(audience) != defaultEntryAudienceNew || user.IsNewUser(now) {
return true
}
return claimErr == nil && claimed
}
// resolveDefaultEntryPage 返回客户端本次应直接进入的最终页面。
// defaultEntryAudience 仅作为后台规则保留,客户端无需再次组合判断。
func resolveDefaultEntryPage(configure sysconfmod.ConfMap, user *usermod.User, currentVer string) string {
page := normalizeDefaultEntryPage(configure.GetString(sysconfmod.VCodeDefaultEntryPage))
audience := strings.TrimSpace(configure.GetString(sysconfmod.VCodeDefaultEntryAudience))
now := time.Now()
matched := defaultEntryAudienceMatch(audience, currentVer, user, now)
if user != nil && currentVer != "" && user.DefaultEntryHandledVer != currentVer {
claimed, err := usermod.ClaimDefaultEntryVersion(user.UID, currentVer)
if !defaultEntryClaimAccepted(audience, user, now, claimed, err) {
return defaultEntryPageHome
}
}
if matched {
return page
}
return defaultEntryPageHome
}
// newUserAdFreePosSet 返回【新人免广告-广告位列表】(VCodeNewUserAdFreePositions) 配置的广告位集合。
// 命中新人免广告的用户,集合内的广告位(pos)不返回广告。
func newUserAdFreePosSet(configure sysconfmod.ConfMap) map[int]struct{} {
codes := configure.GetStrSlice(sysconfmod.VCodeNewUserAdFreePositions)
set := make(map[int]struct{}, len(codes))
for _, c := range codes {
pos, err := strconv.Atoi(c)
if err != nil {
log.Error("新人免广告广告位配置错误,必须为数字", log.Any("code", c))
continue
}
set[pos] = struct{}{}
}
return set
}
// DomainList doc
// @Summary 获取资源信息
// @Description 获取域名/广告/版本等信息
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} proto.SysInfo "{"msg": "操作成功"}"
// @Router /api/app/ping/domain [get]
func DomainList(ctx *gin.Context) {
wg := sync.WaitGroup{}
ua, _ := common.GetUA(ctx)
configure, _ := sysconfdata.GetAllFromCache()
uid, _ := common.GetUID(ctx)
var user *usermod.User
var wallet *walletmod.Wallet
if uid > 0 {
user, _ = usermod.FindUserByUID(uid)
if user != nil {
wallet, _ = walletmod.GetWallet(user.UID)
}
}
adFree := newUserAdFree(configure, user)
wg.Add(8)
sysInfo := new(proto.SysInfo)
var ads proto.AdsRes
common.Go(func() {
defer wg.Done()
advSource := proto.AdvanceSource{
PageBackground: configure.GetString(sysconfmod.VCodeAdvancePageBackground),
PageVidBackground: configure.GetString(sysconfmod.VCodeAdvancePageVidBackground),
ButtonBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonBackground),
ButtonWaitBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonWaitBackground),
ButtonProcBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonProcBackground),
EnterBgWait: configure.GetString(sysconfmod.VCodeAdvanceEnterBgWait),
EnterBgProc: configure.GetString(sysconfmod.VCodeAdvanceEnterBgProc),
PopBgWait: configure.GetString(sysconfmod.VCodeAdvancePopBgWait),
PopBgProc: configure.GetString(sysconfmod.VCodeAdvancePopBgProc),
Banner: configure.GetString(sysconfmod.VCodeAdvanceBanner),
BannerWait: configure.GetString(sysconfmod.VCodeAdvanceBannerWait),
BannerProc: configure.GetString(sysconfmod.VCodeAdvanceBannerProc),
}
sysInfo.AdvancePage = advSource
// 获取banner活动
banner := make(map[int]bannerjumpmod.BannerJumpInfo)
bj, err := bannerjumpdata.GetAllFromCache()
if err != nil {
log.Error("获取banner活动发生错误", log.E(err))
return
}
for _, b := range bj {
item, ok := buildBannerJumpInfo(&b, user, wallet)
if !ok {
continue
}
sysInfo.BannerJumpList = append(sysInfo.BannerJumpList, item)
if _, exists := banner[b.Position]; exists {
continue
}
banner[b.Position] = item
}
sysInfo.BannerJump = banner
})
common.Go(func() {
defer wg.Done()
sysInfo.AiBubble = configure.GetStrSlice(sysconfmod.VCodeAiBubble)
sysInfo.AiCharacterImg = configure.GetString(sysconfmod.VCodeAiCharacterImg)
})
common.Go(func() {
defer wg.Done()
sysInfo.Domain, sysInfo.SourceList = sourcemod.PingList()
sysInfo.JGArea, _ = jingangmod.GetJGListValid(nil)
for _, v := range sysInfo.JGArea {
v.LinkUrl = activityclient.ReplaceActivityDomain(v.LinkUrl, user, wallet)
}
})
common.Go(func() {
defer wg.Done()
if user != nil && !user.ID.IsZero() {
sysInfo.SendMsgPrice = messageser.CheckChatPrice(user)
}
sysInfo.AdvanceStatus = advance_ser.GainAdvanceStatus(uid)
})
common.Go(func() {
defer wg.Done()
//版本、广告、公告
verResp, _, annouResp, _, _, _, err := versionser.AdvVersionAnnounThreeServer(ua.Ver, ua.SysType)
if err != nil {
log.Error("VersionThreeServer", log.E(err))
}
for k, v := range annouResp {
v.Href = activityclient.ReplaceActivityDomain(v.Href, user, wallet)
annouResp[k] = v
}
//版本业务
//安卓不做限制;iOS 端版本 <= 1.11.2 不下发版本信息
skipVer := false
if ua.SysType == constant.SysTypeIOS {
if cur, err := version.New(ua.Ver); err == nil && cur.LTE(version.MustNew("1.11.2")) {
skipVer = true
}
}
if len(verResp.DownloadLink) > 0 && !skipVer {
versionBody := []*versionmod.VersionBody{
&versionmod.VersionBody{
VersionName: verResp.ServerVersion,
Platform: ua.SysType,
Description: verResp.Description,
ForcedUpdate: verResp.IsForceUpdate,
URL: verResp.DownloadLink[0],
IosUrl: verResp.DownloadLink[0],
}}
sysInfo.Ver = versionBody
}
//公告
sysInfo.Ads.AnnounList = annouResp
})
common.Go(func() {
defer wg.Done()
jtAds, err := adser.JtAdvertiseThreeServer()
if err != nil {
log.Error("JtAdvertiseThreeServer error occur", log.E(err))
return
}
// 新人免广告:命中开关时,构建需要屏蔽的广告位集合
freePosSet := make(map[int]struct{})
if adFree {
freePosSet = newUserAdFreePosSet(configure)
}
// 默认空列表,避免序列化为 null
adsList := []proto.AdsInfo{}
for _, loc := range jtAds {
pos, err := strconv.Atoi(loc.AdvertiseLocationCode)
if err != nil {
log.Error("广告位置代码错误,必须为数字", log.Any("code", loc.AdvertiseLocationCode))
continue
}
// 100000 以上保留为娱乐广告
if pos > 100000 {
continue
}
// 新人免广告:命中配置的广告位则跳过,不返回该广告位的广告
if _, ok := freePosSet[pos]; ok {
continue
}
for _, ad := range loc.AdDetailInfoList {
extra := ad.GetExtraData()
adsinfo := proto.AdsInfo{
ID: ad.AdvertiseCode,
Title: ad.AdvertiseName,
Cover: ad.GetCoverLsj(),
Href: ad.GetRealLink(user, wallet),
Position: pos,
PositionName: loc.AdvertiseLocationName,
SortCode: ad.Sort,
CoverImgSize: extra.CoverImgSize,
WatchTime: extra.WatchTime,
}
adsList = append(adsList, adsinfo)
}
}
sysInfo.Ads.AdsList = adsList
})
common.Go(func() {
defer wg.Done()
var e error
sysInfo.PaymentStatusPopupConfig, sysInfo.PaymentStatusPopup, e = sys_config.SysConfUserPaymentStatusPopup(user)
if e != nil {
// 分层弹窗配置失败不阻断整个接口,降级为空配置
log.Error("SysConfUserPaymentStatusPopup error", log.E(e))
}
})
common.Go(func() {
defer wg.Done()
var e error
sysInfo.PaymentGuide, e = paymentguideser.GetPingGuide(user)
if e != nil {
// 新版付费引导失败不阻断 Ping,降级为不展示。
log.Error("GetPingGuide error", log.E(e))
}
})
wg.Wait()
item, _ := json.Marshal(ads)
log.Info(fmt.Sprintf("home:%s", string(item)))
sysInfo.SystemConfigList = []*systemmod.Config{}
sysInfo.TotalWatch = sys_config.GetTotalWatchCount()
sysInfo.LouFengH5 = appg.Conf.URL.LouFengH5
sysInfo.DataBuriedPoint = appg.Conf.URL.DataBuriedPoint
sysInfo.RandomBanner = appg.Conf.RandomBanner
//sysInfo.Active2023URL = appg.Conf.URL.Active2023 + "?appId=" + strconv.FormatInt(int64(commod.KFK_APPID), 10)
sysInfo.AdsTimeLongVideo = commod.AdsTimeLongVideo
sysInfo.HlH5URL = appg.Conf.URL.HlH5Url
if configure.GetBool(sysconfmod.VCodeLotteryEnable) {
sysInfo.LuckyDrawIcon = configure.GetString(sysconfmod.VCodeLotteryIcon)
sysInfo.LuckyDrawH5 = appg.Conf.URL.LuckyDrawH5
luckyDrawUrl := configure.GetString(sysconfmod.VCodeLotteryUrl)
if luckyDrawUrl != "" {
sysInfo.LuckyDrawH5 = activityclient.ReplaceActivityDomain(luckyDrawUrl, user, wallet)
}
}
sysInfo.AiUndressPrice = configure.GetInt(sysconfmod.VCodeAiUndressPrice)
sysInfo.AiImageToVideoPrice = configure.GetInt(sysconfmod.VCodeAiImageToVideoPrice)
sysInfo.AiTextToImagePrice = configure.GetInt(sysconfmod.VCodeAiTextToImagePrice)
sysInfo.Broadcast = configure.GetBool(sysconfmod.VCodeBroadcast)
sysInfo.StoreIsOpen = configure.GetBool(sysconfmod.VCodeStoreOpen)
sysInfo.BackgroundTheme = constant.ThemeDefault
sysInfo.HotSearchTerms = configure.GetStrSlice(sysconfmod.VCodeHotSearchTerms)
sysInfo.SearchHintWord = configure.GetStrSlice(sysconfmod.VCodeSearchHintWord)
sysInfo.FestivalUi = configure.GetString(sysconfmod.VCodeFestivalUi)
sysInfo.AiGirlFriend = configure.GetBool(sysconfmod.VCodeAiGirlFriend)
sysInfo.AiUndress = configure.GetBool(sysconfmod.VCodeAiUndress)
sysInfo.AiImageChangeFace = configure.GetBool(sysconfmod.VCodeAiImageChangeFace)
sysInfo.AiVideoChangeFace = configure.GetBool(sysconfmod.VCodeAiVideoChangeFace)
sysInfo.AiTextToNovelPrice = configure.GetInt(sysconfmod.VCodeAiTextToNovelPrice)
sysInfo.QmdlUrl = configure.GetString(sysconfmod.VCodeQMDL)
sysInfo.DarkWebVipName = configure.GetString(sysconfmod.VCodeDarkWebVipName)
sysInfo.DarkWebVipId = configure.GetString(sysconfmod.VCodeDarkWebVipId)
sysInfo.RecommendVipIds = configure.GetStrSlice(sysconfmod.VCodeRecommendVipId)
sysInfo.ShortDramaCardID = configure.GetString(sysconfmod.VCodeShortDramaCardID)
sysInfo.ShortDramaEntryPopupEnabled = shortDramaEntryPopupEnabled(configure)
sysInfo.DefaultEntryPage = resolveDefaultEntryPage(configure, user, ua.Ver)
sysInfo.DefaultEntryAudience = configure.GetString(sysconfmod.VCodeDefaultEntryAudience)
sysInfo.NewbieSaleTime = configure.GetInt(sysconfmod.VCodeNewbieSaleTime)
sysInfo.PrivateZoneVipName = configure.GetString(sysconfmod.VCodePrivateZoneVipName)
sysInfo.PrivateZoneVipId = configure.GetString(sysconfmod.VCodePrivateZoneVipId)
sysInfo.ReturnSaleVipIds = configure.GetStrSlice(sysconfmod.VCodeReturnSaleVipIds)
sysInfo.OldReturnSaleTime = configure.GetInt(sysconfmod.VCodeOldReturnSaleTime)
sysInfo.Video1 = configure.GetString(sysconfmod.VCodeVideo1)
sysInfo.Video2 = configure.GetString(sysconfmod.VCodeVideo2)
sysInfo.PersonalCenterBackground = configure.GetString(sysconfmod.VCodePersonalCenterBackground)
sysInfo.AIMateH5 = ai_mate_ser.GetApiUrl()
sysInfo.ReportUrl = appg.Conf.DataReport.AppUrl
sysInfo.ReportSDKConfig = reportser.GetSDKDomainConfig()
sysInfo.FreeMark = configure.GetBool(sysconfmod.VCodeFreeMark)
sysInfo.VipMark = configure.GetBool(sysconfmod.VCodeVipMark)
sysInfo.CoinMark = configure.GetBool(sysconfmod.VCodeCoinMark)
sysInfo.AiSwitchConf = doAiSwitchConf(configure.GetObject(sysconfmod.VCodeAiSort), configure.GetObject(sysconfmod.VCodeAiSwitch))
sysInfo.SignIcon = configure.GetString(sysconfmod.VCodeSignIcon)
sysInfo.DarkWebEnable = configure.GetBool(sysconfmod.VCodeDarkWebEnable)
sysInfo.DarkWebImg = configure.GetString(sysconfmod.VCodeDarkWebImg)
sysInfo.DarkWebIcon = configure.GetString(sysconfmod.VCodeDarkWebIcon)
sysInfo.DarkWebIconName = configure.GetString(sysconfmod.VCodeDarkWebIconName)
common.ServeJSON(ctx, stderr.Success, sysInfo)
}
// Ping doc
// @Summary 域名测试
// @Description 测试域名是否正常
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} proto.GinH{response=string} "response:返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/ping/check [get]
func Ping(ctx *gin.Context) {
common.ServeJSON(ctx, stderr.Success, gin.H{"response": "pong"})
}
// Ping doc
// @Summary 域名
// @Description 域名
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Param ver query string true "版本号"
// @Param buildId query string true "安装包ID"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /web/pass [get]
func Pass(ctx *gin.Context) {
ver := ctx.Param("ver")
buildId := ctx.Param("buildId")
if ver == "" || buildId == "" {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
pass, err := versionmod.CheckPass(ver, buildId)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, nil)
return
}
ctx.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "msg": "success", "data": gin.H{"pass": pass}})
}
// GetSysDate doc
// @Summary 获取服务器时间
// @Description 获取服务器时间
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /ping/sysDate [get]
func GetSysDate(ctx *gin.Context) {
common.ServeJSON(ctx, http.StatusOK, gin.H{"sysDate": time.Now()})
}
func M(ctx *gin.Context) {
ctx.String(200, "%d", 0)
}
// Domain doc
// @Summary 获取资源信息(web)
// @Description 获取域名/广告/版本等信息(web)
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} proto.SysInfos "{"msg": "操作成功"}"
// @Router /api/app/ping/domain/h5 [get]
func Domain(ctx *gin.Context) {
wg := sync.WaitGroup{}
ua, _ := common.GetUA(ctx)
configure, _ := sysconfdata.GetAllFromCache()
uid, _ := common.GetUID(ctx)
var user *usermod.User
var wallet *walletmod.Wallet
if uid > 0 {
user, _ = usermod.FindUserByUID(uid)
if user != nil {
wallet, _ = walletmod.GetWallet(user.UID)
}
}
adFree := newUserAdFree(configure, user)
wg.Add(8)
sysInfo := new(proto.SysInfos)
common.Go(func() {
defer wg.Done()
advSource := proto.AdvanceSource{
PageBackground: configure.GetString(sysconfmod.VCodeAdvancePageBackground),
PageVidBackground: configure.GetString(sysconfmod.VCodeAdvancePageVidBackground),
ButtonBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonBackground),
ButtonWaitBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonWaitBackground),
ButtonProcBackground: configure.GetString(sysconfmod.VCodeAdvanceButtonProcBackground),
EnterBgWait: configure.GetString(sysconfmod.VCodeAdvanceEnterBgWait),
EnterBgProc: configure.GetString(sysconfmod.VCodeAdvanceEnterBgProc),
PopBgWait: configure.GetString(sysconfmod.VCodeAdvancePopBgWait),
PopBgProc: configure.GetString(sysconfmod.VCodeAdvancePopBgProc),
Banner: configure.GetString(sysconfmod.VCodeAdvanceBanner),
BannerWait: configure.GetString(sysconfmod.VCodeAdvanceBannerWait),
BannerProc: configure.GetString(sysconfmod.VCodeAdvanceBannerProc),
}
sysInfo.AdvancePage = advSource
// 获取banner活动
banner := make(map[int]bannerjumpmod.BannerJumpInfo)
bj, err := bannerjumpdata.GetAllFromCache()
if err != nil {
log.Error("获取banner活动发生错误", log.E(err))
return
}
for _, b := range bj {
item, ok := buildBannerJumpInfo(&b, user, wallet)
if !ok {
continue
}
sysInfo.BannerJumpList = append(sysInfo.BannerJumpList, item)
if _, exists := banner[b.Position]; exists {
continue
}
banner[b.Position] = item
}
sysInfo.BannerJump = banner
})
common.Go(func() {
defer wg.Done()
sysInfo.AiBubble = configure.GetStrSlice(sysconfmod.VCodeAiBubble)
sysInfo.AiCharacterImg = configure.GetString(sysconfmod.VCodeAiCharacterImg)
})
common.Go(func() {
defer wg.Done()
sysInfo.Domain, sysInfo.SourceList = sourcemod.PingList()
sysInfo.JGArea, _ = jingangmod.GetJGListValid(nil)
for _, v := range sysInfo.JGArea {
v.LinkUrl = activityclient.ReplaceActivityDomain(v.LinkUrl, user, wallet)
}
})
common.Go(func() {
defer wg.Done()
if user != nil && !user.ID.IsZero() {
sysInfo.SendMsgPrice = messageser.CheckChatPrice(user)
}
sysInfo.AdvanceStatus = advance_ser.GainAdvanceStatus(uid)
})
common.Go(func() {
defer wg.Done()
//版本、广告、公告
verResp, _, annouResp, iosUrl, androidUrl, shopIosLink, err := versionser.AdvVersionAnnounThreeServer(ua.Ver, "ios")
log.Info(fmt.Sprintf("======> /api/app/ping/domain/h5 verResp:%+v", verResp))
if err != nil {
log.Error("VersionThreeServer", log.E(err))
return
}
//版本业务
if len(verResp.DownloadLink) > 0 {
versionBody := []*versionmod.VersionBody{
&versionmod.VersionBody{
VersionName: verResp.ServerVersion,
Platform: ua.SysType,
Description: verResp.Description,
ForcedUpdate: verResp.IsForceUpdate,
URL: verResp.DownloadLink[0],
}}
sysInfo.Ver = versionBody
}
for k, v := range annouResp {
v.Href = activityclient.ReplaceActivityDomain(v.Href, user, wallet)
annouResp[k] = v
}
//公告
sysInfo.AnnounList = annouResp
sysInfo.IosLink = iosUrl
sysInfo.AndLink = androidUrl
sysInfo.ShopIosLink = shopIosLink
})
common.Go(func() {
defer wg.Done()
jtAds, err := adser.JtAdvertiseThreeServer()
if err != nil {
log.Error("JtAdvertiseThreeServer error occur", log.E(err))
return
}
// 新人免广告:命中开关时,构建需要屏蔽的广告位集合
freePosSet := make(map[int]struct{})
if adFree {
freePosSet = newUserAdFreePosSet(configure)
}
// 默认空列表,避免序列化为 null
adsList := []proto.AdsInfo{}
for _, loc := range jtAds {
pos, err := strconv.Atoi(loc.AdvertiseLocationCode)
if err != nil {
log.Error("广告位置代码错误,必须为数字", log.Any("code", loc.AdvertiseLocationCode))
continue
}
// 100000 以上保留为娱乐广告
if pos > 100000 {
continue
}
// 新人免广告:命中配置的广告位则跳过,不返回该广告位的广告
if _, ok := freePosSet[pos]; ok {
continue
}
for _, ad := range loc.AdDetailInfoList {
extra := ad.GetExtraData()
adsinfo := proto.AdsInfo{
ID: ad.AdvertiseCode,
Title: ad.AdvertiseName,
Cover: ad.GetCoverLsj(),
Href: ad.GetRealLink(user, wallet),
Position: pos,
PositionName: loc.AdvertiseLocationName,
SortCode: ad.Sort,
CoverImgSize: extra.CoverImgSize,
WatchTime: extra.WatchTime,
}
adsList = append(adsList, adsinfo)
}
}
sysInfo.AdsList = adsList
})
common.Go(func() {
defer wg.Done()
var e error
sysInfo.PaymentStatusPopupConfig, sysInfo.PaymentStatusPopup, e = sys_config.SysConfUserPaymentStatusPopup(user)
if e != nil {
// 分层弹窗配置失败不阻断整个接口,降级为空配置
log.Error("SysConfUserPaymentStatusPopup error", log.E(e))
}
})
common.Go(func() {
defer wg.Done()
var e error
sysInfo.PaymentGuide, e = paymentguideser.GetPingGuide(user)
if e != nil {
// 新版付费引导失败不阻断 Ping,降级为不展示。
log.Error("GetPingGuide error", log.E(e))
}
})
wg.Wait()
sysInfo.SystemConfigList = []*systemmod.Config{}
sysInfo.TotalWatch = sys_config.GetTotalWatchCount()
sysInfo.EKey = requestEncrypt.PubKey
sysInfo.LouFengH5 = appg.Conf.URL.LouFengH5
sysInfo.DataBuriedPoint = appg.Conf.URL.DataBuriedPoint
sysInfo.RandomBanner = appg.Conf.RandomBanner
//sysInfo.Active2023URL = appg.Conf.URL.Active2023 + "?appId=" + strconv.FormatInt(int64(commod.KFK_APPID), 10)
sysInfo.AdsTimeLongVideo = commod.AdsTimeLongVideo
sysInfo.HlH5URL = appg.Conf.URL.HlH5Url
if configure.GetBool(sysconfmod.VCodeLotteryEnable) {
sysInfo.LuckyDrawIcon = configure.GetString(sysconfmod.VCodeLotteryIcon)
sysInfo.LuckyDrawH5 = appg.Conf.URL.LuckyDrawH5
luckyDrawUrl := configure.GetString(sysconfmod.VCodeLotteryUrl)
if luckyDrawUrl != "" {
sysInfo.LuckyDrawH5 = activityclient.ReplaceActivityDomain(luckyDrawUrl, user, wallet)
}
}
sysInfo.AiUndressPrice = configure.GetInt(sysconfmod.VCodeAiUndressPrice)
sysInfo.AiImageToVideoPrice = configure.GetInt(sysconfmod.VCodeAiImageToVideoPrice)
sysInfo.AiTextToImagePrice = configure.GetInt(sysconfmod.VCodeAiTextToImagePrice)
sysInfo.Broadcast = configure.GetBool(sysconfmod.VCodeBroadcast)
sysInfo.StoreIsOpen = configure.GetBool(sysconfmod.VCodeStoreOpen)
sysInfo.BackgroundTheme = constant.ThemeDefault
sysInfo.HotSearchTerms = configure.GetStrSlice(sysconfmod.VCodeHotSearchTerms)
sysInfo.SearchHintWord = configure.GetStrSlice(sysconfmod.VCodeSearchHintWord)
sysInfo.FestivalUi = configure.GetString(sysconfmod.VCodeFestivalUi)
sysInfo.AiGirlFriend = configure.GetBool(sysconfmod.VCodeAiGirlFriend)
sysInfo.AiUndress = configure.GetBool(sysconfmod.VCodeAiUndress)
sysInfo.AiImageChangeFace = configure.GetBool(sysconfmod.VCodeAiImageChangeFace)
sysInfo.AiVideoChangeFace = configure.GetBool(sysconfmod.VCodeAiVideoChangeFace)
sysInfo.AiTextToNovelPrice = configure.GetInt(sysconfmod.VCodeAiTextToNovelPrice)
sysInfo.QmdlUrl = configure.GetString(sysconfmod.VCodeQMDL)
sysInfo.DarkWebVipName = configure.GetString(sysconfmod.VCodeDarkWebVipName)
sysInfo.DarkWebVipId = configure.GetString(sysconfmod.VCodeDarkWebVipId)
sysInfo.RecommendVipIds = configure.GetStrSlice(sysconfmod.VCodeRecommendVipId)
sysInfo.ShortDramaCardID = configure.GetString(sysconfmod.VCodeShortDramaCardID)
sysInfo.ShortDramaEntryPopupEnabled = shortDramaEntryPopupEnabled(configure)
sysInfo.DefaultEntryPage = resolveDefaultEntryPage(configure, user, ua.Ver)
sysInfo.DefaultEntryAudience = configure.GetString(sysconfmod.VCodeDefaultEntryAudience)
sysInfo.PrivateZoneVipName = configure.GetString(sysconfmod.VCodePrivateZoneVipName)
sysInfo.PrivateZoneVipId = configure.GetString(sysconfmod.VCodePrivateZoneVipId)
sysInfo.ReturnSaleVipIds = configure.GetStrSlice(sysconfmod.VCodeReturnSaleVipIds)
sysInfo.OldReturnSaleTime = configure.GetInt(sysconfmod.VCodeOldReturnSaleTime)
sysInfo.NewbieSaleTime = configure.GetInt(sysconfmod.VCodeNewbieSaleTime)
sysInfo.AIMateH5 = ai_mate_ser.GetApiUrl()
sysInfo.Video1 = configure.GetString(sysconfmod.VCodeVideo1)
sysInfo.Video2 = configure.GetString(sysconfmod.VCodeVideo2)
sysInfo.PersonalCenterBackground = configure.GetString(sysconfmod.VCodePersonalCenterBackground)
sysInfo.ReportUrl = appg.Conf.DataReport.AppUrl
sysInfo.ReportSDKConfig = reportser.GetSDKDomainConfig()
sysInfo.FreeMark = configure.GetBool(sysconfmod.VCodeFreeMark)
sysInfo.VipMark = configure.GetBool(sysconfmod.VCodeVipMark)
sysInfo.CoinMark = configure.GetBool(sysconfmod.VCodeCoinMark)
sysInfo.AiSwitchConf = doAiSwitchConf(configure.GetObject(sysconfmod.VCodeAiSort), configure.GetObject(sysconfmod.VCodeAiSwitch))
sysInfo.SignIcon = configure.GetString(sysconfmod.VCodeSignIcon)
sysInfo.DarkWebEnable = configure.GetBool(sysconfmod.VCodeDarkWebEnable)
sysInfo.DarkWebImg = configure.GetString(sysconfmod.VCodeDarkWebImg)
sysInfo.DarkWebIcon = configure.GetString(sysconfmod.VCodeDarkWebIcon)
sysInfo.DarkWebIconName = configure.GetString(sysconfmod.VCodeDarkWebIconName)
common.ServeJSON(ctx, stderr.Success, sysInfo)
}
// CheckMessageTip doc
// @Summary 检查消息小红点
// @Description 检查消息小红点
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /ping/checkMessageTip [get]
func CheckMessageTip(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
tip := message.CheckTip(uid)
common.ServeJSON(ctx, http.StatusOK, gin.H{"newsTip": tip})
return
}
func StoreUrl(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil || uid == 0 {
common.ServeJSON(ctx, stderr.Success, nil)
return
}
user, err := usermod.FindUserByUID(uid)
if err != nil {
return
}
if user == nil {
return
}
var balance int64
w, _ := walletmod.GetWallet(uid)
if w != nil {
balance = w.Income + w.Amount
}
shopUrl := store.GetStoreLink(&store.UserData{
AppUid: user.UID,
AppId: int(commod.KFK_APPID),
Name: user.Name,
Portrait: user.Portrait,
ExpireTime: time.Now().Add(time.Hour * 24 * 2).Unix(),
Balance: balance,
})
common.ServeJSON(ctx, stderr.Success, shopUrl)
}
// buildBannerJumpInfo 根据 banner DB 数据构造下发 DTO。
// 倒计时类型按 Url 中的 type 参数推导(与 task/list 保持一致的判断方式)。
// 返回 ok=false 表示该 banner 当前不应下发:
// - Url 含 type=hongbaoRain 但活动服无可用红包雨场次
//
// countdownType=1 时,StartAt/EndAt 用场次起止时间覆盖;否则保留 banner 自身时间。
func buildBannerJumpInfo(b *bannerjumpmod.BannerJump, user *usermod.User, wallet *walletmod.Wallet) (bannerjumpmod.BannerJumpInfo, bool) {
cdStart, cdEnd, cdType, ok := activityclient.ResolveCountdownByLink(b.Url)
if !ok {
return bannerjumpmod.BannerJumpInfo{}, false
}
startAt, endAt := b.StartAt, b.EndAt
if cdType == 1 {
startAt, endAt = cdStart, cdEnd
}
return bannerjumpmod.BannerJumpInfo{
ID: b.ID,
Position: b.Position,
Banner: b.Banner,
Title: b.Title,
Url: activityclient.ReplaceActivityDomain(b.Url, user, wallet),
StartAt: startAt,
EndAt: endAt,
CountdownType: cdType,
}, true
}
// GetBannerJump doc
// @Summary 通过浮窗ID获取浮窗信息
// @Description 按浮窗ID返回单个浮窗的最新信息,等同于 /ping/domain 中对应 banner 的状态。countdownType=1 但当前无可用红包雨场次时返回空数据
// @Tags PING
// @Accept json
// @Produce json
// @Param id path string true "浮窗ID"
// @Success 200 {object} bannerjumpmod.BannerJumpInfo "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "参数错误"}"
// @Router /api/app/ping/banner/{id} [get]
func GetBannerJump(ctx *gin.Context) {
id, err := primitive.ObjectIDFromHex(ctx.Param("id"))
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
uid, _ := common.GetUID(ctx)
var user *usermod.User
var wallet *walletmod.Wallet
if uid != 0 {
user, _ = usermod.FindUserByUID(uid)
wallet, _ = walletmod.GetWallet(uid)
}
bj, err := bannerjumpdata.GetAllFromCache()
if err != nil {
log.Error("获取banner活动发生错误", log.E(err))
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, nil)
return
}
for i := range bj {
if bj[i].ID != id {
continue
}
info, ok := buildBannerJumpInfo(&bj[i], user, wallet)
if !ok {
common.ServeJSON(ctx, stderr.CodeEmptyData, nil)
return
}
common.ServeJSON(ctx, stderr.Success, info)
return
}
common.ServeJSON(ctx, stderr.CodeEmptyData, nil)
}
func doAiSwitchConf(aiSort map[string]string, aiSwitch map[string]string) []proto.AISwitchConf {
list := make([]proto.AISwitchConf, 0)
for i := 0; i < 7; i++ {
conf := proto.AISwitchConf{
Type: i + 1, // 类型从1(脱衣)开始
Sort: i + 7, // 默认排后面
IsOpen: true, // 默认开启状态
}
key := strconv.Itoa(conf.Type)
if _, ok := aiSort[key]; ok {
sortInt, _ := strconv.Atoi(aiSort[key])
conf.Sort = sortInt
}
if _, ok := aiSwitch[key]; ok {
isOpen, _ := strconv.Atoi(aiSwitch[key])
conf.IsOpen = isOpen == 1
}
list = append(list, conf)
}
return list
}
// DomainRefresh doc
// @Summary 按需刷新资源信息
// @Description 按 keys 增量刷新部分资源(当前支持 paymentPopup/paymentGuide)
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Param keys query []string true "刷新项,如 paymentPopup/paymentGuide"
// @Success 200 {object} proto.SysInfoRefresh "{"msg": "操作成功"}"
// @Router /api/app/ping/domain/refresh [get]
func DomainRefresh(ctx *gin.Context) {
var p = &struct {
Keys []string `json:"keys" form:"keys" binding:"required"`
}{}
if err := ctx.ShouldBind(p); err != nil {
log.Error(fmt.Sprintf("DomainRefresh param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
var user *usermod.User
if uid, err := common.GetUID(ctx); err == nil && uid > 0 {
user, _ = usermod.FindUserByUID(uid)
}
var resp = proto.SysInfoRefresh{}
for _, key := range p.Keys {
switch key {
case "paymentPopup":
if user == nil {
continue
}
paymentStatusPopupConfig, paymentStatusPopup, err := sys_config.SysConfUserPaymentStatusPopup(user)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "SysConfUserPaymentStatusPopup Error: "+err.Error())
return
}
resp.PaymentPopup = proto.SysInfoRefreshPaymentPopup{
PaymentStatusPopup: paymentStatusPopup,
Homepage: paymentStatusPopupConfig.Homepage,
HomepageFlot: paymentStatusPopupConfig.HomepageFlot,
PlayPage: paymentStatusPopupConfig.PlayPage,
MeTab: paymentStatusPopupConfig.MeTab,
VipCard: paymentStatusPopupConfig.VipCard,
LastDiscountTime: paymentStatusPopupConfig.LastDiscountTime,
}
case "paymentGuide":
paymentGuide, err := paymentguideser.GetPingGuide(user)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "GetPingGuide Error: "+err.Error())
return
}
resp.PaymentGuide = paymentGuide
default:
log.Error(fmt.Sprintf("DomainRefresh unknown key:%v", key))
}
}
common.ServeJSON(ctx, stderr.Success, resp)
}
+152
View File
@@ -0,0 +1,152 @@
package productctrl
import (
"91jd-server/app/service/advance_ser"
"91jd-server/app/service/integral_config_ser"
"91jd-server/models/v/integralconfigmod"
"91jd-server/app/service/productser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type BuyProductRequest struct {
ProductType commod.ProductType `form:"productType" json:"productType" bson:"productType"`
ProductID primitive.ObjectID `form:"productID" json:"productID" binding:"required" bson:"productID"`
ContentID primitive.ObjectID `form:"contentID" json:"contentID" bson:"contentID"`
CheckoutContextID string `form:"checkoutContextId" json:"checkoutContextId"`
ChapterID string `form:"chapterID" json:"chapterID"`
CouponID primitive.ObjectID `form:"couponId" json:"couponId"`
ServiceID primitive.ObjectID `form:"serviceId" json:"serviceId"`
GoldVideoCouponNum int `form:"goldVideoCouponNum" json:"goldVideoCouponNum"`
IsH5 bool `form:"isH5" json:"isH5"`
Num uint64 `json:"num" form:"num"`
UserContact string `json:"userContact" form:"userContact"`
ExperimentID string `json:"experimentId" form:"experimentId"`
ExperimentVariant string `json:"experimentVariant" form:"experimentVariant"`
SessionID string `json:"sessionId" form:"sessionId"`
}
// BuyProduct doc
// @Summary 商品购买
// @Description 商品购买
// @Tags product
// @Accept json,mpfd
// @Produce json,html
// @Param request body productctrl.BuyProductRequest true "购买参数;短剧单集解锁时productType=19且contentID、checkoutContextId必填"
// @Param X-Request-ID header string false "短剧单集购买幂等ID"
// @Success 200 {object} productser.BuyDramaEpisodeResponse "短剧单集购买成功时的数据结构"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/product/buy [post]
func BuyProduct(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
ua, err := common.GetUA(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
ip := common.GetIP(ctx)
args := BuyProductRequest{}
if err = ctx.ShouldBind(&args); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
if args.ProductType == commod.Media && !args.ContentID.IsZero() {
data, code := productser.BuyDramaEpisode(
uid, args.ProductID, args.ContentID, args.CheckoutContextID,
ctx.GetHeader("X-Request-ID"), ua, ip,
)
if code != stderr.Success {
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, code, data)
return
}
code := productser.Buy(uid, args.ProductType, args.ProductID, args.CouponID, args.ServiceID, args.Num, args.UserContact, ua.SysType,
args.ChapterID, args.GoldVideoCouponNum, args.IsH5, ua, ip, productser.VIPExperimentAttribution{
ExperimentID: args.ExperimentID,
ExperimentVariant: args.ExperimentVariant,
SessionID: args.SessionID,
})
common.ServeJSON(ctx, code, nil)
}
// GetAwVip doc
// @Summary 获取暗网VIP会员卡
// @Description 获取暗网VIP会员卡
// @Tags 产品配置
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/product/getAwVip [get]
func GetAwVip(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
data, code := productser.GetAwVipInfo(uid)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, code, data)
}
// ExchangeIntegral doc
// @Summary 积分兑换
// @Description 积分兑换
// @Tags 产品配置
// @Accept mpfd,json
// @Produce json,html
// @Param id formData string true "积分兑换配置ID"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/product/exchangeIntegral [post]
func ExchangeIntegral(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var in integralconfigmod.ExchangeIntegralReq
if err = ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code := integral_config_ser.ExchangeIntegral(uid, &in)
if code != stderr.Success {
common.ServeJSON(ctx, code, code.Error())
return
}
common.ServeJSON(ctx, code, stderr.Success.Msg())
}
// AdvanceStatus doc
// @Summary 获取预售状态
// @Description 获取预售状态
// @Tags 产品配置
// @Accept mpfd,json
// @Produce json,html
// @Success 200 object advanceordermod.AdvanceStatus "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/product/advanceStatus [get]
func AdvanceStatus(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
advanceStatus := advance_ser.GainAdvanceStatus(uid)
common.ServeJSON(c, stderr.Success, advanceStatus)
}
+122
View File
@@ -0,0 +1,122 @@
package publishctrl
import (
"sync"
"91jd-server/app/service/publishser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/v/vidmod"
"github.com/gin-gonic/gin"
)
// Details doc
// @Summary 创作视频
// @Description 数据详情
// @Tags 发布
// @Accept json
// @Produce json
// @Success 200 object publishser.DetailsResponse "成功后返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /publish/details [get]
func Details(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err.Error())
return
}
var (
res publishser.DetailsResponse
wg sync.WaitGroup
)
wg.Add(7)
common.Go(func() {
defer wg.Done()
res.IsFirst, err = vidmod.IsSubmitByPublisherID(uid)
})
common.Go(func() {
defer wg.Done()
var weekIncomeLeaderboard []publishser.User
weekIncomeLeaderboard, err = publishser.GetWeekIncomeLeaderboard(3)
if err != nil {
return
}
res.Leaderboards = append(res.Leaderboards, publishser.Leaderboard{
Type: publishser.WeekIncomeLeaderboard,
Members: weekIncomeLeaderboard,
})
})
common.Go(func() {
defer wg.Done()
var weekWorkLeaderboard []publishser.User
weekWorkLeaderboard, err = publishser.GetWeekWorkLeaderboard(3)
if err != nil {
return
}
res.Leaderboards = append(res.Leaderboards, publishser.Leaderboard{
Type: publishser.WeekWorkLeaderboard,
Members: weekWorkLeaderboard,
})
})
common.Go(func() {
defer wg.Done()
res.PendingReviewWorkCount, err = publishser.GetPendingReviewWorkCount(uid)
})
common.Go(func() {
defer wg.Done()
res.WorkTotal, err = publishser.GetWorkTotal(uid)
})
common.Go(func() {
defer wg.Done()
res.ActivityDetails, err = publishser.GetActivityDetails()
})
common.Go(func() {
defer wg.Done()
res.WorkCreateCount, err = publishser.GetCreatorNumber()
})
wg.Wait()
res.PassWorkCount = res.WorkTotal - res.PendingReviewWorkCount
if err != nil {
log.Error(err.Error())
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
return
}
common.ServeJSON(c, stderr.Success, res)
}
// WeekLeaderboard doc
// @Summary 周榜详情
// @Description 周榜-查看更多
// @Tags 发布
// @Accept json
// @Produce json
// @Param type query int true "榜单类型"
// @Success 200 object publishser.WeekLeaderboardResp "成功后返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /publish/leaderboard [get]
func WeekLeaderboard(c *gin.Context) {
var (
req publishser.WeekLeaderboardReq
leaderboard []publishser.User
err error
)
if err = c.ShouldBindQuery(&req); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err.Error())
return
}
switch publishser.ListType(req.Type) {
case publishser.WeekIncomeLeaderboard:
if leaderboard, err = publishser.GetWeekIncomeLeaderboard(10); err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
return
}
case publishser.WeekWorkLeaderboard:
if leaderboard, err = publishser.GetWeekWorkLeaderboard(10); err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
return
}
}
common.ServeJSON(c, stderr.Success, publishser.Leaderboard{Type: publishser.ListType(req.Type), Members: leaderboard})
}
+72
View File
@@ -0,0 +1,72 @@
package rechargectrl
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"time"
"91jd-server/app/service/rechargeser"
"91jd-server/common/log"
"91jd-server/common/rchgutil"
"github.com/gin-gonic/gin"
)
// DaBaiShaCallBack 金鱼结构回调函数
func DaBaiShaCallBack(ctx *gin.Context) {
if err := func() error {
rchg := rchgutil.DaBaiShaRes{}
g := rchgutil.DaBaiSha{}
if err := ctx.ShouldBindJSON(&rchg); err != nil {
log.Error(fmt.Sprintf("DaBaiSha callback parameter bind fail error:%+v:", err))
return err
}
bs, _ := json.Marshal(rchg)
log.Info(fmt.Sprintf("DaBaiSha callback parameter data:%+v:", string(bs)))
buf := bytes.Buffer{}
buf.WriteString(strconv.Itoa(rchg.Code))
buf.WriteString(rchg.MercID)
buf.WriteString(rchg.OID)
buf.WriteString(rchg.PayMoney)
buf.WriteString(rchg.TradeNo)
buf.WriteString(g.GetAppSecret())
if !rchgutil.VerifySign(rchg.Sign, buf.String()) {
log.Error("DaBaiSha callback sign verify fail")
return errors.New("check sign fail")
}
payMoneyf, err := strconv.ParseFloat(rchg.PayMoney, 64)
if err != nil {
log.Error(fmt.Sprintf("DaBaiSha callback ParseFloat fail error:%+v:", err))
return fmt.Errorf("invalid payMoney %s", rchg.PayMoney)
}
payMoneyf = payMoneyf * 100
payMoney := int64(payMoneyf)
g.TradeNo = rchg.TradeNo
msg, err := g.QueryOrder()
if err != nil {
return err
}
if msg.PayTime == "" {
return errors.New("querry order err,no payTime")
}
loc, _ := time.LoadLocation("Local")
paymentAt, err := time.ParseInLocation("2006-01-02T15:04:05Z07:00", msg.PayTime, loc)
if err != nil {
return err
}
rchg.TradeNo = rchgutil.RChgIDDisassemble(rchg.TradeNo)
if err = rechargeser.RechargeCallBack(ctx, rchg.OID, payMoney, rchg.TradeNo, rchg.Code, paymentAt, time.Now()); err != nil {
log.Error(fmt.Sprintf("DaBaiSha RechargeCallBack fail error:%+v:", err))
return err
}
return nil
}(); err != nil {
ctx.String(http.StatusBadRequest, "fail")
return
}
ctx.String(http.StatusOK, "success")
}
+122
View File
@@ -0,0 +1,122 @@
package rechargectrl
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"91jd-server/app/service/rechargeser"
"91jd-server/common/log"
"91jd-server/common/rchgutil"
)
// PayCenterCallBack 支付中心结构回调函数
func PayCenterCallBack(ctx *gin.Context) {
if err := func() error {
rchg := rchgutil.RechargeCallbackResp{}
g := rchgutil.Recharge{}
if err := ctx.ShouldBindJSON(&rchg); err != nil {
log.Error(fmt.Sprintf("PayCenterCallBack parameter bind fail error:%+v:", err))
return err
}
bs, _ := json.Marshal(rchg)
log.Info(fmt.Sprintf("PayCenterCallBack parameter data:%+v:", string(bs)))
buf := bytes.Buffer{}
buf.WriteString(strconv.Itoa(rchg.Code))
buf.WriteString(rchg.MercID)
buf.WriteString(rchg.OID)
buf.WriteString(rchg.PayMoney)
buf.WriteString(rchg.TradeNo)
buf.WriteString(g.GetAppSecret())
if !rchgutil.VerifySign(rchg.Sign, buf.String()) {
log.Error("PayCenterCallBack sign verify fail")
return errors.New("check sign fail")
}
payMoneyf, err := strconv.ParseFloat(rchg.PayMoney, 64)
if err != nil {
log.Error(fmt.Sprintf("PayCenterCallBack ParseFloat fail error:%+v:", err))
return fmt.Errorf("pay center invalid payMoney %s", rchg.PayMoney)
}
payMoneyf = payMoneyf * 100
payMoney := int64(payMoneyf)
g.TradeNo = rchg.TradeNo
msg, err := g.QueryOrder()
if err != nil {
log.Error(fmt.Sprintf("PayCenterCallBack QueryOrder error: %+v, data: %+v", err, msg))
return err
}
if msg.PayTime == "" {
log.Error(fmt.Sprintf("PayCenterCallBack PayTime error: %+v", msg))
return errors.New("query order err,no payTime")
}
loc, _ := time.LoadLocation("Local")
paymentAt, err := time.ParseInLocation("2006-01-02T15:04:05Z07:00", msg.PayTime, loc)
if err != nil {
log.Error(fmt.Sprintf("PayCenterCallBack paymentAt error: %+v, data: %+v", err, msg))
return err
}
rchg.TradeNo = rchgutil.RChgIDDisassemble(rchg.TradeNo)
if err = rechargeser.RechargeCallBack(ctx, rchg.OID, payMoney, rchg.TradeNo, rchg.Code, paymentAt, time.Now()); err != nil {
log.Error(fmt.Sprintf("PayCenterCallBack RechargeCallBack error:%+v:", err))
return err
}
return nil
}(); err != nil {
ctx.String(http.StatusBadRequest, "fail")
return
}
ctx.String(http.StatusOK, "success")
}
// RefundCallBack 支付中心结构退款函数
func RefundCallBack(ctx *gin.Context) {
if err := func() error {
rchg := rchgutil.RefundCallbackResp{}
g := rchgutil.Recharge{}
if err := ctx.ShouldBindJSON(&rchg); err != nil {
log.Error(fmt.Sprintf("payCenter RefundCallBack parameter bind fail error:%+v:", err))
return err
}
bs, _ := json.Marshal(rchg)
log.Info(fmt.Sprintf("payCenter RefundCallBack parameter data:%+v:", string(bs)))
buf := bytes.Buffer{}
buf.WriteString(strconv.Itoa(rchg.Code))
buf.WriteString(rchg.MercID)
buf.WriteString(rchg.OID)
buf.WriteString(rchg.TradeNo)
buf.WriteString(g.GetAppSecret())
if !rchgutil.VerifySign(rchg.Sign, buf.String()) {
log.Error("payCenter RefundCallBack sign verify fail")
return errors.New("check sign fail")
}
//g.TradeNo = rchg.TradeNo
//msg, err := g.QueryOrder()
//if err != nil {
// log.Error(fmt.Sprintf("payCenter RefundCallBack QueryOrder error: %+v, data: %+v", err, msg))
// return err
//}
//
//if msg.PayStatus != "未知" {
// log.Error(fmt.Sprintf("payCenter RefundCallBack PayTime error: %+v", msg))
// return errors.New("refund querry order payStatus err")
//}
rchg.TradeNo = rchgutil.RChgIDDisassemble(rchg.TradeNo)
if err := rechargeser.RefundCallBack(ctx, rchg.OID, rchg.TradeNo); err != nil {
log.Error(fmt.Sprintf("payCenter RefundCallBack RechargeCallBack error:%+v:", err))
return err
}
return nil
}(); err != nil {
ctx.String(http.StatusBadRequest, "fail")
return
}
ctx.String(http.StatusOK, "success")
}
+143
View File
@@ -0,0 +1,143 @@
package rechargectrl
import (
"sync"
"91jd-server/app/proto"
"91jd-server/app/service/rechargeser"
"91jd-server/common"
"91jd-server/common/daichong"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"github.com/gin-gonic/gin"
)
// NewRecharge doc
// @Summary 充值接口 充值完成后会产生一条充值流水
// @Description 充值接口 充值完成后会产生一条充值流水
// @Tags 钱包
// @Accept json
// @Produce json
// @Param request formData rechargeser.RechargeRequest true "request"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/topay [post]
func NewRecharge(c *gin.Context) {
var (
err error
in = new(rechargeser.RechargeRequest)
payUrl, mode string
)
in.UID, err = common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
in.IP = common.GetIP(c)
if err = c.ShouldBindJSON(&in); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err)
return
}
ua, _ := common.GetUA(c)
// 客户端下单不参与活动抵扣券(deduct=nil),couponId/deductAmount 仅活动服 HMAC 入口可注入
if payUrl, mode, err = rechargeser.Recharge(c, in, ua, nil); err != nil {
common.ServeJSON(c, stderr.RechargeFaile, err.Error())
return
}
common.ServeJSON(c, stderr.Success, gin.H{
"payUrl": payUrl,
"mode": mode,
})
}
// CurrencyList doc
// @Summary 获取充值金额列表
// @Description 获取充值金额列表
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Param request query rechargeser.CurrencyListRequest true "类型1-金币 2-游戏币 3-果币"
// @Success 200 {object} proto.CurryenyResp "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/currencys [get]
func CurrencyList(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrParamError, nil)
return
}
ua, err := common.GetUA(c)
if err != nil {
common.ServeJSON(c, stderr.ErrParamError, nil)
return
}
var (
args rechargeser.CurrencyListRequest
wg sync.WaitGroup
dcChat daichong.ChatResp
code stderr.Code
data []*proto.CurrencyListResponse
)
if err := c.ShouldBindQuery(&args); err != nil {
common.ServeJSON(c, stderr.ErrParamError, nil)
return
}
wg.Add(2)
common.Go(func() {
defer wg.Done()
data, code = rechargeser.New_CurrencyList(c, uid, ua.SysType, commod.CurrencyType(args.Type))
})
common.Go(func() {
defer wg.Done()
//var productType int
//if commod.CurrencyType(args.Type) == commod.GameCoin {
// productType = 1
//}
//dcChat, _ = daichongser.NewTakeChat(c, uid, productType)
dcChat = daichong.ChatResp{}
})
wg.Wait()
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, proto.CurryenyResp{
Chat: dcChat.Data,
List: data,
})
}
// GetUserTransactions doc
// @Summary 获取充值记录
// @Description 根据条件查询充值记录
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData int true "页码"
// @Param pageSize formData int true "条数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/mine/transaction [get]
func GetUserTransactions(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrParamError, nil)
return
}
var arg commod.Page
if err = c.ShouldBind(&arg); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err)
return
}
total, data, hasNext, err := rechargeser.GetUserTransactionDetails(uid, arg.PageNumber, arg.PageSize)
if err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
return
}
common.ServeJSON(c, stderr.Success, gin.H{
"total": total,
"list": data,
"hasNext": hasNext,
})
}
+64
View File
@@ -0,0 +1,64 @@
package rechargectrl
import (
"91jd-server/app/service/proxyser"
"91jd-server/common"
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"91jd-server/app/service/rechargeser"
"91jd-server/common/log"
"91jd-server/common/rchgutil"
"github.com/gin-gonic/gin"
)
// CallBack 支付回调回调函数
func CallBack(ctx *gin.Context) {
var transNo string = ""
var payMoney int64 = 0
success, err := func() (string, error) {
var err error
name := ctx.Param("name")
rchg := rchgutil.GetNotifyBack(name)
if rchg == nil {
log.Error(fmt.Sprintf("%s callback request path invalid.", name))
return "", errors.New(" request name invalid")
}
ata, _ := ctx.GetRawData()
ctx.Request.Body = io.NopCloser(bytes.NewBuffer(ata))
bodystr := string(ata)
log.Info(fmt.Sprintf("%s callback request url +%s, body:%s,contenttype:%s", name, ctx.Request.URL, bodystr, ctx.ContentType()))
if err = ctx.ShouldBind(rchg); err != nil {
log.Error(fmt.Sprintf("%s callback parameter bind fail error:%+v:", name, err))
return "", err
}
bs, _ := json.Marshal(rchg)
log.Info(fmt.Sprintf("%s callback parameter data:%+v:", name, string(bs)))
rb, err := rchg.Notify()
if err != nil {
return "", err
}
rb.TransNo = rchgutil.RChgIDDisassemble(rb.TransNo)
if err = rechargeser.RechargeCallBack(ctx, rb.OID, rb.PayMoney, rb.TransNo, rb.Code, rb.PaymentAt, rb.SuccessAt); err != nil {
log.Error(fmt.Sprintf("%s RechargeCallBack fail error:%+v:", name, err))
}
transNo = rb.TransNo
payMoney = rb.PayMoney
return rchg.Success(), err
}()
if err != nil {
ctx.String(http.StatusBadRequest, "fail")
return
} else {
//异步处理全民代理分成
common.Go(func() {
proxyser.HandelProxyRechargeCommission(nil, transNo, payMoney)
})
ctx.String(http.StatusOK, success)
}
}
+96
View File
@@ -0,0 +1,96 @@
package recommctrl
import (
"91jd-server/app/service/m3u8ticket"
"91jd-server/app/service/recommser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/middleware/requestid"
"91jd-server/models/v/recmdtag"
"github.com/gin-gonic/gin"
)
// GetVidList doc
// @Summary 获取短视频推荐列表
// @Description 获取推荐视频
// @Tags recommend
// @Accept mpfd,json
// @Produce json,html
// @Param pageSize formData integer true "页码大小"
// @Param X-Request-ID header string false "重试幂等ID;同一次请求重试保持不变"
// @Success 200 {object} recommod.VideoListResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/recommend/vid/list [get]
func GetVidList(ctx *gin.Context) {
uid, _ := common.GetUID(ctx)
type Param struct {
PageSize uint64 `json:"pageSize" form:"pageSize" binding:"required,min=1,max=100"` // 每页条数
}
param := Param{}
if err := ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if param.PageSize < 1 {
param.PageSize = 10
}
requestID, _ := requestid.FromClient(ctx)
code, data := recommser.GetVidListContext(
ctx.Request.Context(),
uid,
param.PageSize,
requestID,
)
// 推荐视频列表:对 m3u8 播放地址签票
m3u8ticket.Sign(ctx, data)
common.ServeJSON(ctx, code, data)
}
// GetVidAd doc
// @Summary 获取视频插播广告
// @Description 获取视频插播广告
// @Tags recommend
// @Accept mpfd,json
// @Produce json,html
// @Param pageSize formData integer true "页码大小"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/recommend/GetVidAd/list [get]
func GetVidAd(ctx *gin.Context) {
uid, _ := common.GetUID(ctx)
data := recommser.GetAd(uid)
common.ServeJSON(ctx, stderr.Success, data)
}
// GetShortDiscoverList doc
// @Summary 获取抖音短视频视频列表
// @Description 获取抖音短视频列表(第一页存在发现tag列表)
// @Tags recommend
// @Accept json
// @Produce json,html
// @Param type query recmdtag.AppGetShortDiscoverListReq true "请求参数"
// @Success 200 {object} recmdtag.AppGetShortDiscoverListRep "{}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/recommend/vid/list/discover [get]
func GetShortDiscoverList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
param := &recmdtag.AppGetShortDiscoverListReq{}
if err = ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
resp, code, err := recommser.GetShortDiscoverList(uid, param)
if err != nil {
log.Error("GetShortVideoList error:", log.E(err))
}
m3u8ticket.Sign(ctx, resp)
common.ServeJSON(ctx, code, resp)
}
+56
View File
@@ -0,0 +1,56 @@
package recreationctrl
import (
"91jd-server/app/service/adser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary 获取娱乐模块列表
// @Description 获取金主广告列表
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {array} adser.RecreationListRes
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/recreation/list [get]
func List(ctx *gin.Context) {
list, err := adser.RecreationFromJt()
if err != nil {
log.Error("RecreationFromJt", log.E(err))
common.ServeJSON(ctx, stderr.Success, nil) // 加载娱乐广告失败可忽略
return
}
common.ServeJSON(ctx, stderr.Success, list)
}
// Click doc
// @Summary 娱乐模块广告点击
// @Description 娱乐模块广告点击
// @Tags PING
// @Accept mpfd,json
// @Produce json,html
// @Param id formData string true "广告数据id"
// @Param type formData string true "用户点击数据类型,app or adv"
// @Param sysType formData string true "用户设备类型. ios or android"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/recreation/click [post]
func Click(ctx *gin.Context) {
var p adser.RecreationClickInfo
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
_, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrTokenIsNotExist, "")
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
+50
View File
@@ -0,0 +1,50 @@
package scenebannerctrl
import (
"strings"
"time"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/scenebannermod"
"github.com/gin-gonic/gin"
)
type listResp struct {
List []bannerItem `json:"list"`
}
type bannerItem struct {
ID string `json:"id"`
ImageURL string `json:"imageUrl"`
MediaType string `json:"mediaType"`
LinkType string `json:"linkType"`
LinkValue string `json:"linkValue"`
Sort int `json:"sort"`
}
func List(ctx *gin.Context) {
scene := strings.ToUpper(strings.TrimSpace(ctx.Query("scene")))
if !scenebannermod.ValidScene(scene) {
common.ServeJSON(ctx, stderr.ErrParamError, "invalid scene")
return
}
list, err := scenebannermod.FindActive(scene, time.Now())
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
items := make([]bannerItem, 0, len(list))
for _, banner := range list {
items = append(items, bannerItem{
ID: banner.ID.Hex(),
ImageURL: banner.ImageURL,
MediaType: banner.MediaType,
LinkType: banner.LinkType,
LinkValue: banner.LinkValue,
Sort: banner.Sort,
})
}
common.ServeJSON(ctx, stderr.Success, listResp{List: items})
}
+245
View File
@@ -0,0 +1,245 @@
package searchctrl
import (
"91jd-server/app/service/moduleser"
"91jd-server/app/service/search"
"91jd-server/app/service/vidser"
"91jd-server/common"
"91jd-server/common/filter"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/l/searchlogmod"
"fmt"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary 搜索模块 - 搜索keyWords和realm指定的相关资源
// @Description 获取FILE域 Token
// @Tags Search
// @Accept mpfd,json
// @Produce json,html
// @Param q body search.NewsKeywordSearchReq true "参数"
// @Success 200 object search.NewsKeywordSearchRep "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/search/list [post]
func List(c *gin.Context) {
var req search.NewsKeywordSearchReq
if err := c.ShouldBind(&req); err != nil {
common.ServeJSON(c, stderr.ErrParamError, "search List arg error "+err.Error())
return
}
// 检查uid是否存在
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrAccessForbid, "search List Context USER_ID is not exist ")
return
}
req.KeyWords, err = filterKeyWord(req.KeyWords)
if err != nil {
common.ServeJSON(c, stderr.TagAddTagNameInvalidErr, err)
return
}
//录入搜索日志
common.Go(func() {
_ = searchlogmod.InsertMany(uid, req.Realm, req.KeyWords)
})
data, err := req.Search(uid)
if err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
return
}
common.ServeJSON(c, stderr.Success, data)
//if arg.Realm == constant.Video {
// if items, ok := result.Data().([]searcher.VideoRes); ok {
// // 查询用户信息
// u, err := usermod.FindUserByUID(uid)
// if err != nil {
// common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
// return
// }
// if u.VipExpireDate.After(time.Now()) {
// for k, item := range items {
// if item.Coins != nil && *item.Coins < 10 {
// var zero int64 = 0
// item.Coins = &zero
// }
// items[k] = item
// }
// }
// common.ServeJSON(c, stderr.Success, gin.H{
// "list": items,
// "hasNext": result.HasNext(),
// })
// return
// }
//}
//
//if req.Realm == constant.SearchSP || req.Realm == constant.SearchShort {
// // 额外获取TAG相关信息
// tagId, err := tagmod.GetTagIDByName(req.KeyWords[0])
// if tagId.IsZero() || err != nil {
// common.ServeJSON(c, stderr.Success, gin.H{
// "list": result.Data(),
// "tagID": "",
// "tagVidList": nil,
// "hasNext": result.HasNext(),
// })
// return
// }
// // 6-最多收藏
// vmodList, err := vidser.GetVideosByTagID(tagId, req.Realm, 6, 0, 4)
// if err != nil {
// common.ServeJSON(c, stderr.Success, gin.H{
// "list": result.Data(),
// "tagID": "",
// "tagVidList": nil,
// "hasNext": result.HasNext(),
// })
// return
// }
// oids := make([]primitive.ObjectID, len(vmodList))
// for i, v := range vmodList {
// oids[i] = v.ID
// }
// if len(oids) > int(req.PageSize) {
// oids = oids[:req.PageSize]
// }
// vidList := vidhelpser.GetVideosByIDs(0, oids)
// common.ServeJSON(c, stderr.Success, gin.H{
// "list": result.Data(),
// "tagID": tagId.Hex(),
// "tagVidList": vidList,
// "hasNext": result.HasNext(),
// })
// return
//}
//common.ServeJSON(c, stderr.Success, gin.H{
// "list": result.Data(),
// "hasNext": result.HasNext(),
//})
}
// 如返回为空数组 则表示所输入关键字 全为违规词汇
func filterKeyWord(KeyWords []string) ([]string, error) {
//过滤掉违规词汇
pureKw := make([]string, 0, len(KeyWords))
for _, v := range KeyWords {
s, e := filter.TagFilter.Filter(v)
if len(s) == 0 && e == nil {
pureKw = append(pureKw, v)
}
}
if len(pureKw) == 0 {
return pureKw, fmt.Errorf("Invalid tag name: %v", KeyWords)
}
return pureKw, nil
}
// HotTagSearch doc
// @Summary 搜索
// @Description 猜你想要
// @Tags Search
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} proto.Tag
// @Failure 400 {string} json "{"msg": "操作失败" "data":{}}"
// @Router /search/hotTag [get]
func HotTagSearch(c *gin.Context) {
data, err := search.GetHotTag()
if err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, err.Error())
return
}
common.ServeJSON(c, stderr.Success, gin.H{
"list": data,
})
}
// HotVid doc
// @Summary 热门视频列表
// @Description 热门视频列表
// @Tags Search
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber query int true "当前页"
// @Param pageSize query int true "每页条数"
// @Param type query int true "0最新热播 1本月最热 2上月最热"
// @Success 200 {object} vidmod.VideoModel "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/search/hotVid/list [get]
func HotVid(c *gin.Context) {
var arg struct {
commod.Page
T int `json:"type" form:"type"` // 0 最新热播 1本月最热 2上月最热
}
if err := c.ShouldBind(&arg); err != nil {
common.ServeJSON(c, stderr.ErrParamError, "searchCtrl WonderTagList arg error "+err.Error())
return
}
res, err := vidser.GetHotVideo(int64(arg.PageNumber), int64(arg.PageSize), arg.T)
if err != nil {
common.ServeJSON(c, stderr.Failure, err)
return
}
common.ServeJSON(c, stderr.Success, res)
}
// HotPublisher doc
// @Summary 热门博主
// @Description 热门博主
// @Tags Search
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber query int true "当前页"
// @Param pageSize query int true "每页条数"
// @Success 200 {object} proto.HotPublisher "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /search/hotPublisher/list [get]
func HotPublisher(c *gin.Context) {
// 检查uid是否存在
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrAccessForbid, "search HotPublisher Context USER_ID is not exist ")
return
}
list, err := vidser.GetHotPublisher(uid)
if err != nil {
common.ServeJSON(c, stderr.Failure, err)
return
}
common.ServeJSON(c, stderr.Success, gin.H{
"list": list,
})
}
// PublisherList doc
// @Summary 热门板块
// @Description 热门板块
// @Tags 发布
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} tagmod.TagInfoRes "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/search/publisher/list [get]
func PublisherList(c *gin.Context) {
// 检查uid是否存在
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrAccessForbid, "search HotPublisher Context USER_ID is not exist ")
return
}
list, err := moduleser.GetPublishTag(uid)
if err != nil {
common.ServeJSON(c, stderr.Failure, err)
return
}
common.ServeJSON(c, stderr.Success, list)
}
+138
View File
@@ -0,0 +1,138 @@
package sharectrl
import (
"91jd-server/app/service/shareser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/e/sharemod"
"net/http"
"time"
"go.mongodb.org/mongo-driver/bson/primitive"
"github.com/gin-gonic/gin"
)
// GeneratorQrCode doc
// @Summary 获取视频的分享次数
// @Description 获取视频的分享次数
// @Tags share
// @Accept mpfd,json
// @Produce json,html
// @Param content formData string true "分享的url"
// @Param videoID formData string false "视频ID;同一用户、视频、自然日最多累计一次真实分享推荐分"
// @Param eventId formData string false "分享事件ID;同一次事件重试时保持不变,最长128字符"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/share/output [post]
func GeneratorQrCode(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
param := sharemod.VShareReq{}
if err = ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if len(param.Content) == 0 {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if param.ObjType == "drama" {
requestID := ctx.GetHeader("X-Request-ID")
if param.MediaID == "" || param.EventID == "" || requestID == "" || len(requestID) > 128 || len(param.EventID) > 128 {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
code, data := shareser.GeneratorDramaQrCodeContext(
ctx.Request.Context(), uid, param.Content, param.MediaID, param.ContentID, param.EventID,
)
common.ServeJSON(ctx, code, data)
return
}
code, data := shareser.GeneratorQrCodeContext(
ctx.Request.Context(),
uid,
param.Content,
param.VideoID,
param.EventID,
)
common.ServeJSON(ctx, code, data)
}
// GetShareCnt doc
// @Summary 获取视频的分享次数
// @Description 获取视频的分享次数
// @Tags share
// @Accept mpfd,json
// @Produce json,html
// @Param videoID formData string true "视频id"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/share/count [get]
func GetShareCnt(ctx *gin.Context) {
param := sharemod.VShareCntReq{}
if err := ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if len(param.VideoID) == 0 {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
code, data := shareser.GetShareCnt(param.VideoID)
common.ServeJSON(ctx, code, data)
}
// Info doc
// @Summary 获取分享信息
// @Description 获取分享信息
// @Tags share
// @Accept mpfd,json
// @Produce json,html
// @Param id formData string true "视频id"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/share/info [get]
func Info(ctx *gin.Context) {
var param struct {
ID primitive.ObjectID `json:"id"`
}
h := gin.H{
"hash": false,
"data": "",
"time": time.Now().UTC().Format("2006-01-02T15:04:05.000Z"),
}
if err := ctx.ShouldBind(&param); err != nil {
h["code"] = stderr.ErrParamError
h["msg"] = stderr.ErrParamError.Msg()
h["tip"] = stderr.ErrParamError.Tip()
ctx.JSON(http.StatusOK, h)
return
}
if param.ID.IsZero() {
h["code"] = stderr.ErrParamError
h["msg"] = stderr.ErrParamError.Msg()
h["tip"] = stderr.ErrParamError.Tip()
ctx.JSON(http.StatusOK, h)
return
}
ua, _ := common.GetUA(ctx)
data, sErr := shareser.Info(param.ID, ua.SysType)
if sErr != nil && sErr.Code != stderr.Success {
h["code"] = sErr.Code
h["msg"] = sErr.Msg
h["tip"] = sErr.Tips
ctx.JSON(http.StatusOK, h)
return
}
h["code"] = stderr.Success
h["msg"] = stderr.Success.Msg()
h["tip"] = stderr.Success.Tip()
h["data"] = data
ctx.JSON(http.StatusOK, h)
}
+44
View File
@@ -0,0 +1,44 @@
package signrecordctrl
import (
"91jd-server/app/service/signrecordser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"fmt"
"github.com/gin-gonic/gin"
)
// AgainSign doc
// @Summary 补签打卡接口
// @Description 补签打卡
// @Tags 移动端-补签打卡
// @Accept mpfd,json
// @Produce json
// @Param q query signrecordser.AppReSignReq false "请求参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/sign_record/resign [post]
func AgainSign(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
p := &signrecordser.AppReSignReq{}
if err := ctx.ShouldBind(&p); err != nil {
log.Error(fmt.Sprintf("uid:%v,resign param is err:%v", uid, err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
code, err := p.ReSign(uid)
if code != stderr.Success {
log.Error(fmt.Sprintf("uid:%v,resign is err:%v", uid, err))
common.ServeJSON(ctx, code, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, stderr.Success.Msg())
}
+107
View File
@@ -0,0 +1,107 @@
package statcenterctl
import (
"time"
"91jd-server/models/commod"
)
// 全民代理查询
type UserInviteUserListReq struct {
UserId uint64 `form:"userId" json:"userId"` // 用户ID
Appid int32 `form:"appId" json:"appId"` // APPID
commod.Page
}
type UserInviteUserListRes struct {
Total int64 `json:"total"`
HasNext bool `json:"hasNext"`
List []UserInviteUserInfo `json:"list"`
}
type UserInviteUserInfo struct {
UserId uint64 `json:"userId"` // 邀请用户ID
Name string `json:"name"` // 邀请用户名称
Portrait string `json:"portrait"` // 被邀请人头像
BindPhone string `json:"bindPhone"` // 绑定
CreateAt time.Time `json:"createAt"` // 注册时间
}
type UserInviteIncomeListReq struct {
UserId uint64 `form:"userId" json:"userId" ` // 用户ID
Appid int32 `form:"appId" json:"appId"` // APPID
commod.Page
}
type UserInviteIncomeListRes struct {
//总邀请数
TotalInvites int64 `json:"totalInvites"`
//今日邀请
TodayInvites int64 `json:"todayInvites"`
//总邀请充值
TotalInviteAmount int64 `json:"totalInviteAmount"`
//今日充值
TodayInviteAmount int64 `json:"todayInviteAmount"`
//列表总数
Total int64 `json:"total"`
//是否还有下一页
HasNext bool `json:"hasNext"`
//列表
List []UserInviteIncomeInfo `json:"list"`
}
type UserInviteIncomeInfo struct {
// 充值用户
UserId uint64 `json:"userId"`
// 充值用户
UserName string `json:"userName"`
// 收入金币
IncomeAmount int64 `json:"incomeAmount" bson:"incomeAmount"`
// 分成比例
IncomeRate float64 `json:"incomeRate" bson:"incomeRate"`
// 充值时间
RechargeAt time.Time `json:"rechargeAt"`
}
type VideoIncomeListReq struct {
commod.Page
}
type StatcenterSyncReq struct {
Job string `json:"job"` // user_access/user_register 大于用户Id:
UserId uint64 `json:"userId"` // 用户ID
PlatformId string `json:"platformId"` // 原始平台Id
MaxSize int64 `json:"maxSize"` // 最大条数
SuccessTime time.Time `json:"successTime"` // 成功时间
}
type StatcenterSyncResp struct {
Code int `json:"code"` // 200正常 其他异常
Msg string `json:"msg"` // 错误消息
Job string `json:"job" bson:"job" binding:"required"` // 类型
AccessList []commod.UserAccessMsg `json:"accessList"` // 日活记录
RegisterList []commod.UserRegisterMsg `json:"registerList"` // 提现记录
BindingList []commod.UserBindingMsg `json:"bindingList"` // 用户绑定记录
InviteList []commod.UserInviteBindMsg `json:"inviteList"` // 邀请记录
ConsumeList []commod.ConsumeRecordMsg `json:"consumeList"` // 消费流水
RechargeList []commod.UserRechargeMsg `json:"rechargeList"` // 充值流水
AllRechargeList []commod.UserRechargeMsg `json:"allRechargeList"` // 全部支付订单
CardSellList []commod.CardSellMsg `json:"cardSellList"` // 会员卡特权卡销售流水
AiSellList []commod.AiSellMsg `json:"aiSellList"` // AI销售流水
}
type UserInviteIncomeListResWaLi struct {
TotalInvites int64 `json:"totalInvites"` //总推广人数
TodayInvites int64 `json:"todayInvites"` //今日推广
TotalInviteAmount int64 `json:"totalInviteAmount"` //总收益
YesterdaylInviteAmount int64 `json:"yesterdaylInviteAmount"` //昨日收益
Total int64 `json:"total"` //总数
List []UserInviteIncomeInfoWaLi `json:"list"` //收益记录
HasNext bool `json:"hasNext"`
}
type UserInviteIncomeInfoWaLi struct {
Desc string `json:"desc"` // 收益描述名称
IncomeAmount int64 `json:"incomeAmount"` // 收入金币
SetDate time.Time `json:"setDate"` // 结算时间
}
+178
View File
@@ -0,0 +1,178 @@
package statcenterctl
import (
"91jd-server/app/service/proxyser"
"91jd-server/app/service/walletser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// UserInviteInfo
// @Tags 全名代理
// @Summary 获取账户信息
// @Description 获取账户信息
// @Accept json
// @Produce json
// @Success 200 {object} walletser.UserInviteAmountInfo
// @Success 400 {string} string "失败"
// @Router /userInvite/info [POST]
func UserInviteInfo(ctx *gin.Context) {
uid, err := common.GetUID(ctx) //当前用户uid
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
resp, err := walletser.GetUserAmount(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, resp)
}
// @Tags 全名代理
// @Summary 获取视频收益详情
// @Description
// @Accept json
// @Produce json
// @Param param body VideoIncomeListReq true "参数"
// @Success 200 {string} string "成功"
// @Success 400 {string} string "失败"
// @Router /userInvite/videolist [POST]
func UserVideoList(ctx *gin.Context) {
uid, err := common.GetUID(ctx) //当前用户uid
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
var request VideoIncomeListReq
if err = ctx.ShouldBind(&request); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
resp, err := walletser.GetVideoIncomelist(uid, request.PageNumber, request.PageSize)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, resp)
}
// UserInviteList
// @Tags 全名代理
// @Summary 邀请列表
// @Description 返回全民代理被邀请人列表
// @Accept json
// @Produce json
// @Param param body UserInviteUserListReq true "参数"
// @Success 200 {string} string "成功"
// @Success 400 {string} string "失败"
// @Router /userInvite/userlist [POST]
func UserInviteList(ctx *gin.Context) {
uid, err := common.GetUID(ctx) //当前用户uid
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
var request UserInviteUserListReq
if err = ctx.ShouldBind(&request); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
//查询
list, total, err := proxyser.GetInveUserList(uid, request.PageNumber, request.PageSize)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
var resp UserInviteUserListRes
if uint64(total) > request.PageNumber*request.PageSize {
resp.HasNext = true
}
resp.Total = total
resp.List = make([]UserInviteUserInfo, len(list))
for i, l := range list {
resp.List[i] = UserInviteUserInfo{
UserId: l.Invitee,
Name: l.InviteeName,
Portrait: l.InviteePortrait,
CreateAt: l.CreatedAt,
}
}
common.ServeJSON(ctx, stderr.Success, resp)
}
// NewUserInviteList
// @Tags 全名代理
// @Summary 新邀请列表
// @Description 返回全民代理被邀请人列表
// @Accept json
// @Produce json
// @Param param body UserInviteUserListReq true "参数"
// @Success 200 {string} string "成功"
// @Success 400 {string} string "失败"
// @Router /userInvite/userlist [POST]
func NewUserInviteList(ctx *gin.Context) {
uid, err := common.GetUID(ctx) //当前用户uid
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
var request UserInviteUserListReq
if err = ctx.ShouldBind(&request); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
//查询
list, total, err := proxyser.GetInveUserList(uid, request.PageNumber, request.PageSize)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
var resp UserInviteUserListRes
if uint64(total) > request.PageNumber*request.PageSize {
resp.HasNext = true
}
resp.Total = total
resp.List = make([]UserInviteUserInfo, len(list))
for i, l := range list {
resp.List[i] = UserInviteUserInfo{
UserId: l.Invitee,
Name: l.InviteeName,
Portrait: l.InviteePortrait,
CreateAt: l.CreatedAt,
}
}
common.ServeJSON(ctx, stderr.Success, resp)
}
// UserInviteIncomeList
// @Tags 全名代理
// @Summary 收益详情
// @Description 返回用户收益详情
// @Accept json
// @Produce json
// @Param param body UserInviteIncomeListReq true "参数"
// @Success 200 {object} UserInviteIncomeListRes "成功"
// @Success 400 {string} string "失败"
// @Router /api/app/userinvite/incomelist [POST]
func UserInviteIncomeList(ctx *gin.Context) {
uid, err := common.GetUID(ctx) //当前用户uid
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
var request UserInviteIncomeListReq
if err = ctx.ShouldBind(&request); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
resp, err := walletser.GetInviteIncomelist(uid, request.PageNumber, request.PageSize)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, resp)
}
+579
View File
@@ -0,0 +1,579 @@
package statcenterctl
import (
"fmt"
"math"
"net/http"
"strings"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/l/visitlogmod"
"91jd-server/models/v/prdcthsomod"
"91jd-server/models/v/productmod"
"91jd-server/models/v/productposimod"
"91jd-server/models/v/proxymod"
"91jd-server/models/v/rchgordmod"
"91jd-server/models/v/txnmod"
"91jd-server/models/v/usermod"
"github.com/gin-gonic/gin"
"github.com/shopspring/decimal"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo/options"
)
// @Tags 数据通过
// @Summary 拉去数据同步信息
// @Description 返回用户收益详情
// @Accept json
// @Produce json
// @Param param body StatcenterSyncReq true "参数"
// @Success 200 {string} string "成功"
// @Success 400 {string} string "失败"
// @Router /statcenter/sync [POST]
func StatcenterSyncList(ctx *gin.Context) {
var request StatcenterSyncReq
var err error
if err = ctx.ShouldBind(&request); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
var resp StatcenterSyncResp
resp.Job = request.Job
resp.Code = 200
// 根据记录查询数据库
switch request.Job {
case string(commod.USER_ACCE):
fmt.Println(request.Job)
// 查询
resp.AccessList, err = UserAccessList(request)
if err != nil {
resp.Code = 502
resp.Msg = err.Error()
}
case string(commod.USER_REG):
fmt.Println(request.Job)
resp.RegisterList, err = UserRegisterList(request)
if err != nil {
resp.Code = 502
resp.Msg = err.Error()
}
case string(commod.USER_BINDING):
fmt.Println(request.Job)
resp.BindingList, err = UserBindingList(request)
if err != nil {
resp.Code = 502
resp.Msg = err.Error()
}
case string(commod.USER_INVITE):
fmt.Println(request.Job)
resp.InviteList, err = UserInviterList(request)
if err != nil {
resp.Code = 502
resp.Msg = err.Error()
}
case string(commod.ConsumeRecordJob):
fmt.Println(request.Job)
resp.ConsumeList, err = ConsumeRecordList(request)
if err != nil {
resp.Code = 502
resp.Msg = err.Error()
}
case string(commod.USER_RECH):
fmt.Println(request.Job)
resp.RechargeList, err = UserRechargeList(request)
if err != nil {
resp.Code = 502
resp.Msg = err.Error()
}
case string(commod.USER_RECH_ALL):
fmt.Println(request.Job)
resp.AllRechargeList, err = UserAllOrderList(request)
if err != nil {
resp.Code = 502
resp.Msg = err.Error()
}
case string(commod.CardSellJob):
fmt.Println(request.Job)
resp.CardSellList, err = CardSellList(request)
if err != nil {
resp.Code = 502
resp.Msg = err.Error()
}
case string(commod.AiSellJob):
fmt.Println(request.Job)
resp.AiSellList, err = AiSellList(request)
if err != nil {
resp.Code = 502
resp.Msg = err.Error()
}
default:
resp.Code = 501
resp.Msg = "job is error"
}
ctx.JSON(http.StatusOK, resp)
}
// AiSellList AI销售流水同步
func AiSellList(req StatcenterSyncReq) (list []commod.AiSellMsg, err error) {
log.Debug("AiSellList job start running")
typeInts := []txnmod.TransType{txnmod.AiChangeFaceImgDebitGold, txnmod.AiImageToVideoDebitGold, txnmod.AiChangefaceDebitGold, txnmod.AiUndressDebitGold, txnmod.AiTextToImageDebitGold, txnmod.AiMateChat}
startID, err := primitive.ObjectIDFromHex(req.PlatformId)
if err != nil {
return
}
filter := bson.M{"tranTypeInt": bson.M{"$in": typeInts}, "_id": bson.M{"$gt": startID}}
opts := options.Find().SetLimit(req.MaxSize).SetSort(bson.D{{Key: "_id", Value: 1}})
_, logs, err := txnmod.FindTransactionLogs(filter, opts)
if err != nil {
return
}
list = make([]commod.AiSellMsg, len(logs))
if len(list) == 0 {
log.Debug("AiSellList job len(list) == 0")
return
}
for i, l := range logs {
amount := l.Amount
if amount < 0 {
amount = -amount
}
msg := commod.AiSellMsg{
AppID: commod.KFK_APPID,
UID: l.UID,
UniqID: l.ID.Hex(),
Amount: amount,
SysType: l.SysType,
CurrencyType: "pay",
TranCreatedAt: l.CreatedAt,
IsRepurchase: l.IsRepurchase,
}
switch txnmod.TransType(l.TranTypeInt) {
case txnmod.AiChangeFaceImgDebitGold:
msg.TranType = "img_faceswap"
case txnmod.AiImageToVideoDebitGold:
msg.TranType = "img_to_vid"
case txnmod.AiChangefaceDebitGold:
msg.TranType = "vid_faceswap"
case txnmod.AiUndressDebitGold:
msg.TranType = "strip"
case txnmod.AiTextToImageDebitGold:
msg.TranType = "text_to_img"
case txnmod.AiMateChat:
msg.TranType = "mate"
aiMatePoint := l.AiMatePoint
if aiMatePoint < 0 {
msg.Amount = int64(math.Round(math.Abs(aiMatePoint)))
}
}
list[i] = msg
}
log.Debug("AiSellList job start finished")
return
}
// UserAccessList 日活数据同步
func UserAccessList(request StatcenterSyncReq) ([]commod.UserAccessMsg, error) {
visitList, err := visitlogmod.AccessSyncById(request.PlatformId, request.MaxSize)
if err != nil {
log.Error("UserAccessList AccessSyncById ", log.E(err))
return nil, err
}
accessList := make([]commod.UserAccessMsg, len(visitList))
for i, visitInfo := range visitList {
// iOS 开头的 devType(如 "iOS:25.3.0")统一规范化为 "ios"
if strings.HasPrefix(strings.ToLower(visitInfo.DevType), "ios") {
visitInfo.DevType = "ios"
}
accessList[i] = commod.UserAccessMsg{
UserId: visitInfo.UID,
AppId: commod.KFK_APPID,
SysType: visitInfo.SysType,
DevType: visitInfo.DevType,
IP: visitInfo.IP,
Version: visitInfo.Ver,
DevID: visitInfo.DevID,
VisitAt: visitInfo.CreatedAt,
PlatformId: visitInfo.ID.Hex(),
IsDirect: visitInfo.IsDirect,
DistrictCode: visitInfo.DistrictCode,
RegisterTime: visitInfo.RegisterTime,
IsDeduction: visitInfo.IsDeduction,
}
}
return accessList, nil
}
// UserRegisterList 注册数据同步
func UserRegisterList(request StatcenterSyncReq) ([]commod.UserRegisterMsg, error) {
userList, err := usermod.StatcenterSyncList(request.UserId, request.MaxSize)
if err != nil {
log.Error("UserRegisterList AccessSyncById ", log.E(err))
return nil, err
}
registerList := make([]commod.UserRegisterMsg, len(userList))
for i, userInfo := range userList {
registerList[i] = commod.UserRegisterMsg{
UserId: userInfo.UID,
AppId: commod.KFK_APPID,
SysType: userInfo.SysType,
DevType: userInfo.DevType,
Mobile: userInfo.Mobile,
Name: userInfo.Name,
IP: userInfo.RegisterIP,
IsDirect: userInfo.IsDirect,
DistrictCode: userInfo.DistrictCode,
PromSeqe: userInfo.PromSeqe,
PUC: userInfo.PUC,
PromCode: userInfo.PromCode,
RegisterTime: userInfo.CreatedAt,
PlatformId: userInfo.ID.Hex(),
}
}
return registerList, nil
}
// UserRegisterList 注册数据同步
func UserInviterList(request StatcenterSyncReq) ([]commod.UserInviteBindMsg, error) {
data, err := proxymod.StatCenterSyncInviteList(request.SuccessTime, request.MaxSize)
if err != nil {
log.Error("UserInviteList InviteSyncByInviteTime ", log.E(err))
return nil, err
}
res := make([]commod.UserInviteBindMsg, len(data))
for i, v := range data {
res[i] = commod.UserInviteBindMsg{
UserId: v.UID,
AppId: commod.KFK_APPID,
ParentPromCode: v.InviteCode,
InviteTime: v.InviteTime,
}
}
return res, nil
}
// UserBindingList 绑定数据同步
func UserBindingList(request StatcenterSyncReq) ([]commod.UserBindingMsg, error) {
userList, err := usermod.StatcenterSyncBindUserList(request.SuccessTime, request.MaxSize)
if err != nil {
log.Error("UserBindingList AccessSyncById ", log.E(err))
return nil, err
}
bindingList := make([]commod.UserBindingMsg, len(userList))
for i, userInfo := range userList {
bindingList[i] = commod.UserBindingMsg{
UserId: userInfo.UID,
AppId: commod.KFK_APPID,
SysType: userInfo.SysType,
DevType: userInfo.DevType,
Mobile: userInfo.Mobile,
PlatformId: userInfo.ID.Hex(),
BindingTime: userInfo.MobileBindAt,
}
}
return bindingList, nil
}
// ConsumeRecordList 消费流水同步
func ConsumeRecordList(request StatcenterSyncReq) ([]commod.ConsumeRecordMsg, error) {
var typeInts = []txnmod.TransType{txnmod.PayVIP, txnmod.MeetingCard,
txnmod.BuyVIP, txnmod.VideoFreeCard, txnmod.VideoDiscount,
txnmod.Other, txnmod.LouFeng, txnmod.LouFengMianFei, txnmod.BookLoufeng, txnmod.CoinMonthCard}
objId, err := primitive.ObjectIDFromHex(request.PlatformId)
if err != nil {
return nil, err
}
f := bson.M{"tranTypeInt": bson.M{"$in": typeInts}, "_id": bson.M{"$gt": objId}}
opt := options.Find().SetLimit(request.MaxSize).SetSort(bson.D{{Key: "_id", Value: 1}})
_, txns, err := txnmod.FindTransactionLogs(f, opt)
if err != nil {
log.Error("UserBindingList AccessSyncById ", log.E(err))
return nil, err
}
list := make([]commod.ConsumeRecordMsg, len(txns))
for i, v := range txns {
temp := commod.ConsumeRecordMsg{
AppID: commod.KFK_APPID,
UID: v.UID,
CurrencyType: v.CurrencyType,
Amount: decimal.NewFromFloat(v.ActualAmount),
Uniq: v.ID.Hex(),
CreatedAt: v.CreatedAt,
}
switch txnmod.TransType(v.TranTypeInt) {
case txnmod.PayVIP:
temp.Type = commod.StatVipCard
case txnmod.LouFeng, txnmod.LouFengMianFei, txnmod.BookLoufeng:
temp.Type = commod.StatLouFeng
case txnmod.Other, txnmod.MeetingCard:
temp.Type = commod.StatValueAddSer
}
if v.CurrencyType == commod.CurrencyTypeCash {
temp.Money = decimal.NewFromFloat(v.ActualAmount).Shift(-1)
}
list[i] = temp
}
return list, nil
}
// UserRechargeList 充值数据同步
func UserRechargeList(request StatcenterSyncReq) ([]commod.UserRechargeMsg, error) {
rechargeOrders, err := rchgordmod.StatCenterSyncRecharge(request.SuccessTime, request.MaxSize)
if err != nil {
log.Error("UserRechargeList StatCenterSyncRecharge ", log.E(err))
return nil, err
}
data := make([]commod.UserRechargeMsg, len(rechargeOrders))
for i, v := range rechargeOrders {
data[i] = commod.UserRechargeMsg{
UserId: v.UID,
AppId: commod.KFK_APPID,
PlatformId: v.ID.Hex(),
SysType: v.DevType,
DevType: v.DevType,
ChannelName: v.Channel,
CID: v.Channel,
Type: v.RechargeType,
OrderId: v.ID.Hex(),
OID: v.OID,
Money: v.Money,
PayMoney: v.PayMoney,
Status: v.Status,
Rate: "12",
SuccessAt: v.SuccessAt,
ProductType: v.ProductType,
ChanShareMod: v.ChanShareMod,
}
}
return data, nil
}
// UserAllOrderList 用户订单同步
func UserAllOrderList(request StatcenterSyncReq) ([]commod.UserRechargeMsg, error) {
rechargeOrders, err := rchgordmod.StatCenterSyncOrder(request.SuccessTime, request.MaxSize)
if err != nil {
log.Error("UserRechargeList StatCenterSyncRecharge ", log.E(err))
return nil, err
}
data := make([]commod.UserRechargeMsg, len(rechargeOrders))
for i, v := range rechargeOrders {
data[i] = commod.UserRechargeMsg{
UserId: v.UID,
AppId: commod.KFK_APPID,
PlatformId: v.ID.Hex(),
SysType: v.DevType,
DevType: v.DevType,
ChannelName: v.Channel,
CID: v.Channel,
Type: v.RechargeType,
OrderId: v.ID.Hex(),
OID: v.OID,
Money: v.Money,
PayMoney: v.PayMoney,
Status: v.Status,
Rate: "12",
SuccessAt: v.CreatedAt,
ProductType: v.ProductType,
ChanShareMod: v.ChanShareMod,
}
}
return data, nil
}
// CardSellList 会员卡特权卡销售流水同步
func CardSellList(req StatcenterSyncReq) (list []commod.CardSellMsg, err error) {
log.Debug("CardSellList job start running")
typeInts := []txnmod.TransType{txnmod.MeetingCard, txnmod.LouFengDiscount, txnmod.LouFengMianFei,
txnmod.BuyVIP, txnmod.VideoDiscount, txnmod.VideoFreeCard, txnmod.PayVIP, txnmod.BuyAdvanceVIP,
txnmod.BuyBalanceVIP, txnmod.BuyGameAdvanceVIP, txnmod.BuyWhoringCard,
}
startID, err := primitive.ObjectIDFromHex(req.PlatformId)
if err != nil {
return
}
filter := bson.M{"tranTypeInt": bson.M{"$in": typeInts}, "_id": bson.M{"$gt": startID}}
opts := options.Find().SetLimit(req.MaxSize).SetSort(bson.D{{Key: "_id", Value: 1}})
_, logs, err := txnmod.FindTransactionLogs(filter, opts)
if err != nil {
return
}
logsLen := len(logs)
productIDs := make([]primitive.ObjectID, 0, logsLen)
historyIDs := make([]primitive.ObjectID, 0, logsLen)
for _, l := range logs {
if l.ProductID != nil && *l.ProductID != "" {
productID, err := primitive.ObjectIDFromHex(*l.ProductID)
if err != nil {
log.Error("primitive.ObjectIDFromHex", log.Any("productID", *l.ProductID), log.E(err))
continue
}
productIDs = append(productIDs, productID)
} else if !l.TransNo.IsZero() {
historyIDs = append(historyIDs, l.TransNo)
}
}
historyMap, err := getProductsByHistories(historyIDs)
if err != nil {
return
}
productMap, err := getProductPositions(productIDs)
if err != nil {
return
}
list = make([]commod.CardSellMsg, len(logs))
for i, l := range logs {
amount := l.Amount
if amount < 0 {
amount = -amount
}
msg := commod.CardSellMsg{
AppID: commod.KFK_APPID,
UID: l.UID,
UniqID: l.ID.Hex(),
Amount: amount,
TranTypeInt: l.TranTypeInt,
TranType: l.TranType,
SysType: l.SysType,
CurrencyType: l.CurrencyType,
TranCreatedAt: l.CreatedAt,
}
if !l.TransNo.IsZero() {
msg.Product = historyMap[l.TransNo]
} else if l.ProductID != nil && *l.ProductID != "" {
msg.Product = productMap[*l.ProductID]
} else {
msg.Product = commod.Product{
ProductType: txnmod.TranType2ProductType[txnmod.TransType(l.TranTypeInt)],
Position: detectProductPosition(l),
}
}
list[i] = msg
}
log.Debug("CardSellList job start finished")
return
}
func getProductsByHistories(historyIDs []primitive.ObjectID) (products map[primitive.ObjectID]commod.Product, err error) {
_, histories, err := prdcthsomod.FindProductHistorys(bson.M{"_id": bson.M{"$in": historyIDs}}, options.Find())
if err != nil {
return
}
historyMap := make(map[primitive.ObjectID]prdcthsomod.ProductHistory)
for _, history := range histories {
historyMap[history.ID] = *history
}
productIDs := make([]primitive.ObjectID, len(histories))
for i, h := range histories {
productIDs[i] = h.ProductID
}
productMap, err := productmod.ListByIDsMap(productIDs)
if err != nil {
return
}
posIDs := make([]primitive.ObjectID, 0, len(productMap))
for _, p := range productMap {
if p.Position != "" {
posID, err := primitive.ObjectIDFromHex(p.Position)
if err != nil {
log.Error("primitive.ObjectIDFromHex", log.Any("productID", p.ID.Hex()),
log.Any("position", p.Position), log.E(err))
continue
}
posIDs = append(posIDs, posID)
}
}
positions, err := productposimod.FindByIDs(posIDs)
if err != nil {
log.Error("productposimod.FindByIDs", log.Any("positionIDs", posIDs), log.E(err))
}
positionMap := make(map[string]productposimod.ProductPosition)
for _, p := range positions {
positionMap[p.ID.Hex()] = p
}
products = make(map[primitive.ObjectID]commod.Product)
for hID, h := range historyMap {
product := commod.Product{Position: commod.Position{}}
pro, ok := productMap[h.ProductID]
if ok {
product.ID = pro.ID.Hex()
product.Name = pro.Name
product.DiscountedPrice = pro.DiscountedPrice
product.ProductType = pro.ProductType
pos, ok := positionMap[pro.Position]
if ok {
product.Position.ID = pos.ID
product.Position.Name = pos.Name
}
}
products[hID] = product
}
return
}
func getProductPositions(productIDs []primitive.ObjectID) (products map[string]commod.Product, err error) {
productList, err := productmod.ListToIDs(productIDs, "")
if err != nil {
return
}
positionIDs := make([]primitive.ObjectID, 0, len(productList))
for _, p := range productList {
if p.Position != "" {
positionID, err := primitive.ObjectIDFromHex(p.Position)
if err != nil {
log.Error("primitive.ObjectIDFromHex", log.Any("position", p.Position), log.E(err))
continue
}
positionIDs = append(positionIDs, positionID)
}
}
positionList, err := productposimod.FindByIDs(positionIDs)
if err != nil {
return
}
positionMap := make(map[string]productposimod.ProductPosition)
for _, p := range positionList {
positionMap[p.ID.Hex()] = p
}
products = make(map[string]commod.Product)
for _, p := range productList {
pos := positionMap[p.Position]
position := commod.Position{
ID: pos.ID,
Name: pos.Name,
}
products[p.ID.Hex()] = commod.Product{
ID: p.ID.Hex(),
Name: p.Name,
DiscountedPrice: p.DiscountedPrice,
ProductType: p.ProductType,
Position: position,
}
}
return
}
func detectProductPosition(txnLog *txnmod.TransactionLog) (position commod.Position) {
positionMap, err := productposimod.FindAllNameMap()
if err != nil {
return
}
pos := productposimod.ProductPosition{}
if txnLog != nil {
switch txnLog.TranTypeInt {
case int64(txnmod.BuyVIP), int64(txnmod.PayVIP):
pos = positionMap["会员卡"]
case int64(txnmod.MeetingCard), int64(txnmod.LouFengDiscount), int64(txnmod.LouFengMianFei),
int64(txnmod.Other), int64(txnmod.VideoDiscount), int64(txnmod.VideoFreeCard):
pos = positionMap["特权卡"]
default: // 默认特权卡
pos = positionMap["特权卡"]
}
}
position.ID = pos.ID
position.Name = pos.Name
return
}
+24
View File
@@ -0,0 +1,24 @@
package statcenterctl
import (
"context"
"fmt"
"net/http"
"91jd-server/common/httputil"
"91jd-server/common/log"
)
// HttpRequest HttpRequest
func HttpRequest(ctx context.Context, url string, request interface{}, resp interface{}) error {
code, err := httputil.DefaultClientPostJsonWithResp(resp, url, nil, &request)
if err != nil {
log.Error("HttpRequest err", log.Any("Url", url), log.E(err))
return err
}
if code != http.StatusOK {
log.Error("HttpRequest err", log.Any("code", code))
return fmt.Errorf("code err, %d", code)
}
return nil
}
+174
View File
@@ -0,0 +1,174 @@
package tagctrl
import (
"91jd-server/app/service/mediaser"
"91jd-server/web/service/vidser"
"91jd-server/app/service/tagser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// VidList doc
// @Summary 专题 - 视频列表
// @Description 根据标签获取视频列表
// @Tags 圈子
// @Accept json
// @Produce json
// @Param q query vidser.GetVideoListByTagReq true "参数"
// @Success 200 {object} vidser.GetVideoListByTagReq "success"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/tag/vid/list [get]
func VidList(ctx *gin.Context) {
var req *vidser.GetVideoListByTagReq
if err := ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, "tagCtrl VidList arg error "+err.Error())
return
}
rep, err := req.GetVideoListByTag()
if err != nil {
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, rep)
}
// TagList 标签列表
// @Summary 专题模块 - 标签列表
// @Description 获取标签列表
// @Tags Special Topic
// @Accept json
// @Produce json
// @Param pageNumber query integer true "页码"
// @Param pageSize query integer true "每页条数"
// @Success 200 {string} json "{"msg": "操作成功", "data":tagser.Tag}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /tag/list [get]
func TagList(ctx *gin.Context) {
wordGroup := tagser.GetTagGroup(16)
common.ServeJSON(ctx, stderr.Success, wordGroup)
}
func V2TagList(ctx *gin.Context) {
var param struct {
Content string `form:"content" json:"content"`
commod.Page
}
if err := ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data := tagser.GetCommonUsedTagList(param.Content, param.PageNumber, param.PageSize)
common.ServeJSON(ctx, stderr.Success, data)
}
// TagListMostPlayed 标签列表-根据播放量由高到低排序
// @Summary 专题模块 - 标签列表
// @Description 获取标签列表,根据播放量由高到低排序
// @Tags Special Topic
// @Accept json
// @Produce json
// @Param pageNumber query integer true "页码"
// @Param pageSize query integer true "每页条数"
// @Success 200 {object} tagser.MostPlayedTagListResponse
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /tag/list/most-played [get]
func TagListMostPlayed(c *gin.Context) {
req := tagser.MostPlayedTagListRequest{}
if err := c.ShouldBind(&req); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err)
return
}
resp, err := tagser.GetMostPlayedTagList(req)
if err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
return
}
common.ServeJSON(c, stderr.Success, resp)
}
// TagConfList 标签列表
// @Summary 专题模块 - 标签列表,获取后台配置标签列表
// @Description 获取标签列表,由后台配置
// @Tags Special Topic
// @Accept json
// @Produce json
// @Success 200 {object} tagser.AllTagConfResponse
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /tag/conf/list [get]
func TagConfList(c *gin.Context) {
resp, err := tagser.GetCommonUsedRecmdTags()
if err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, err)
return
}
common.ServeJSON(c, stderr.Success, resp)
}
// GetTagInfo doc
// @Summary 用户模块 - 标签详情
// @Description 获取标签详细信息(标签对应的视频列表)
// @Tags Special Topic
// @Accept json
// @Produce json
// @Param tagID formData string true "标签id"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/tag/info [get]
func GetTagInfo(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
// 获取标签详情
type Info struct {
TagID string `form:"tagID" json:"tagID" binding:"required"`
}
param := Info{}
if err = ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
tagID, err := primitive.ObjectIDFromHex(param.TagID)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
code, data, err := tagser.GetTagInfo(uid, tagID)
if err != nil {
common.ServeJSON(ctx, code, err)
return
}
common.ServeJSON(ctx, code, data)
}
// MediaList doc
// @Summary 标签-ACG列表
// @Description 根据标签获取ACG列表
// @Tags 圈子
// @Accept json
// @Produce json
// @Param q query mediaser.TagMediaListReq true "请求参数"
// @Success 200 object mediaser.TagMediaListResp "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/api/tag/media/list [get]
func MediaList(ctx *gin.Context) {
var req mediaser.TagMediaListReq
if err := ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, "tagCtrl MediaList arg error "+err.Error())
return
}
resp, err := req.GetList()
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
common.ServeJSON(ctx, stderr.Success, resp)
}
+86
View File
@@ -0,0 +1,86 @@
package taskctrl
import (
"91jd-server/app/service/taskser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// 签到
func Sign(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
log.Info("Sign start", log.Any("uid", uid))
var params struct {
ID string `json:"id" binding:"required"`
}
if err = c.ShouldBindJSON(&params); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err.Error())
return
}
log.Info("Sign start", log.Any("uid", uid), log.Any("params", params))
if code := taskser.Sign(uid, params.ID); code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, "success")
}
// 补签
func ReSign(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
log.Info("ReSign start", log.Any("uid", uid))
var params struct {
ID string `json:"id" binding:"required"`
}
if err = c.ShouldBindJSON(&params); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err.Error())
return
}
log.Info("ReSign start", log.Any("uid", uid), log.Any("params", params))
if code := taskser.ReSign(uid, params.ID); code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, "success")
}
// 获取签到的额外奖励
func SignExtraPrizes(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
data, code := taskser.SignExtraPrizes(uid)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, data)
}
// 获取签到信息
func GetSignDetails(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
data, code := taskser.GetSignDetails(uid)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, data)
}
+238
View File
@@ -0,0 +1,238 @@
package taskctrl
import (
"91jd-server/models/v/taskmod"
"fmt"
"sync"
"91jd-server/app/service/activityclient"
"91jd-server/app/service/taskser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/v/usermod"
"91jd-server/models/v/walletmod"
"github.com/gin-gonic/gin"
)
/*
// 获取任务列表
func GetTaskList(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
data, code := taskser.GetTaskList(uid)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, data)
}
// 获取任务详情
func GetTaskDetails(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
log.Info("GetTaskDetails start", log.Any("uid", uid))
var params struct {
Type int `form:"type" binding:"required,min=3"`
}
if err = c.ShouldBindQuery(&params); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err.Error())
return
}
log.Info("GetTaskDetails start", log.Any("uid", uid), log.Any("params", params))
data, code := taskser.GetTaskDetails(uid, params.Type)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, data)
}
// 领取宝箱奖励
func GetBoon(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
log.InfoX(c, "GetJewelBoxPrize start", log.Any("uid", uid))
var params struct {
ID string `json:"id"`
Type int `json:"type"`
}
if err = c.ShouldBindJSON(&params); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err.Error())
return
}
log.InfoX(c, "GetJewelBoxPrize start", log.Any("uid", uid), log.Any("params", params))
if code := taskser.GetBoon(c, uid, params.ID, params.Type); code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, stderr.Success, "success")
}
*/
// GetNewTask doc
// @Summary 任务列表
// @Description 任务列表
// @Tags 福利任务
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} taskser.NewTaskResponse "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/task/list [post]
func GetNewTask(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
var user *usermod.User
var wallet *walletmod.Wallet
user, _ = usermod.FindUserByUID(uid)
if user != nil {
wallet, _ = walletmod.GetWallet(uid)
}
var dailyTasks []taskser.DailyTaskResponse
var onceTasks []taskser.OnceTaskResponse
var growthTasks []*taskser.GrowthTaskResponse
var dailyTaskCode, onceTaskCode, growthCode stderr.Code
var dailyTaskMsg, onceTaskMsg, growthTaskMsg string
wg := sync.WaitGroup{}
wg.Add(3)
common.Go(func() {
defer wg.Done()
dailyTasks, dailyTaskCode, dailyTaskMsg = taskser.GetDailyTask(uid)
})
common.Go(func() {
defer wg.Done()
onceTasks, onceTaskCode, onceTaskMsg = taskser.GetOnceTask(uid)
})
common.Go(func() {
defer wg.Done()
growthTasks, growthCode, growthTaskMsg = taskser.GetGrowthTask(uid)
})
wg.Wait()
if dailyTaskCode != stderr.Success {
common.ServeJSON(c, dailyTaskCode, dailyTaskMsg)
return
}
if onceTaskCode != stderr.Success {
common.ServeJSON(c, onceTaskCode, onceTaskMsg)
return
}
if growthCode != stderr.Success {
common.ServeJSON(c, growthCode, growthTaskMsg)
return
}
// 按 Link 中的 type 推导倒计时类型。CountdownType=1 (红包雨) 但活动服无可用场次时,过滤该任务。
filteredDailyTasks := make([]taskser.DailyTaskResponse, 0, len(dailyTasks))
for i := range dailyTasks {
start, end, ctype, ok := activityclient.ResolveCountdownByLink(dailyTasks[i].Link)
if !ok {
continue
}
dailyTasks[i].StartAt = start
dailyTasks[i].EndAt = end
dailyTasks[i].CountdownType = ctype
dailyTasks[i].Link = activityclient.ReplaceActivityDomain(dailyTasks[i].Link, user, wallet)
filteredDailyTasks = append(filteredDailyTasks, dailyTasks[i])
}
dailyTasks = filteredDailyTasks
filteredOnceTasks := make([]taskser.OnceTaskResponse, 0, len(onceTasks))
for i := range onceTasks {
start, end, ctype, ok := activityclient.ResolveCountdownByLink(onceTasks[i].Link)
if !ok {
continue
}
onceTasks[i].StartAt = start
onceTasks[i].EndAt = end
onceTasks[i].CountdownType = ctype
onceTasks[i].Link = activityclient.ReplaceActivityDomain(onceTasks[i].Link, user, wallet)
filteredOnceTasks = append(filteredOnceTasks, onceTasks[i])
}
onceTasks = filteredOnceTasks
common.ServeJSON(c, stderr.Success, taskser.NewTaskResponse{
DailyTasks: dailyTasks,
OnceTasks: onceTasks,
GrowthTasks: growthTasks,
})
}
// Receive doc
// @Summary 我的任务 - 领取积分
// @Description 领取积分
// @Tags 福利任务
// @Accept mpfd,json
// @Produce json,html
// @Param taskId formData string true "任务ID"
// @Param type formData int true "任务类型 1、每日任务 2、一次行任务"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/task/receive [post]
func Receive(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
var in taskmod.ReceiveTaskReq
err = c.ShouldBindJSON(&in)
if err != nil {
log.Error(fmt.Sprintf("task Receive task param err:%v", err))
common.ServeJSON(c, stderr.ErrParamError, err.Error())
return
}
code := taskser.ReceiveTask(uid, &in)
if code != stderr.Success {
common.ServeJSON(c, code, code.Error())
return
}
common.ServeJSON(c, stderr.Success, stderr.Success.Msg())
}
// Do doc
// @Summary 我的任务-做任务
// @Description 做任务
// @Tags 福利任务
// @Accept mpfd,json
// @Produce json,html
// @Param taskId formData string true "任务ID"
// @Param type formData int true "任务类型 1、每日任务 2、一次行任务"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/task/do [post]
func Do(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
var in taskmod.DoTaskReq
err = c.ShouldBindJSON(&in)
if err != nil {
log.Error(fmt.Sprintf("task do task param err:%v", err))
common.ServeJSON(c, stderr.ErrParamError, err.Error())
return
}
code := taskser.DoTask(uid, &in)
if code != stderr.Success {
common.ServeJSON(c, code, code.Error())
return
}
common.ServeJSON(c, stderr.Success, stderr.Success.Msg())
}
+106
View File
@@ -0,0 +1,106 @@
package txnactctr
import (
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/txnactmod"
"91jd-server/models/v/usermod"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// GetBanks doc
// @Summary 获取绑定的银行卡列表
// @Description 银行卡绑定
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/txnact/banks [get]
func GetBanks(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
data, err := txnactmod.FindManyByActType(uid, txnactmod.Bank)
if err != nil {
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, gin.H{
"list": data,
})
}
// AddBank doc
// @Summary 绑定银行卡
// @Description 银行卡绑定
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Param actName body string true "账户姓名"
// @Param act body string true "账户号"
// @Param bankCode body string true "银行代号"
// @Param cardType body string true "卡类型"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/txnact/bank [post]
func AddBank(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
t := txnactmod.TransactionAct{AType: txnactmod.Bank}
if err = ctx.ShouldBind(&t); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
t.UID = uid
user, err := usermod.FindUserByUID(uid)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err.Error())
return
}
if user.BankActName != "" && user.BankActName != t.ActName {
common.ServeJSON(ctx, stderr.DifferentBankActName, "")
return
}
if err = txnactmod.Insert(&t); err != nil {
if stderr.IsEqual(err, stderr.InsertExistError) {
common.ServeJSON(ctx, stderr.ErrWithDrawAccountHasBind, err.Error())
return
}
common.ServeJSON(ctx, stderr.ErrDbInsertError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
// DelAlipay doc
// @Summary 删除除提现账户
// @Description 删除除提现账户
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Param id body string true "id"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/txnact/del [delete]
func DelTxAccount(ctx *gin.Context) {
var p struct {
ID primitive.ObjectID `json:"id"`
}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
if _, err := txnactmod.DeleteByID(p.ID); err != nil {
common.ServeJSON(ctx, stderr.ErrDbDeleteError, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
+373
View File
@@ -0,0 +1,373 @@
package updownctrl
import (
"encoding/base64"
"fmt"
"io"
"net/http"
"net/url"
"path/filepath"
"strconv"
"strings"
"sync"
"time"
"91jd-server/app/appg"
"91jd-server/app/service/m3u8ticket"
"91jd-server/app/service/updownloadser"
"91jd-server/common"
"91jd-server/common/constant/redisconst"
"91jd-server/common/hevcpull"
"91jd-server/common/log"
"91jd-server/common/m3u8"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/v/sourcemod"
"91jd-server/models/v/usermod"
"github.com/gin-gonic/gin"
)
const (
Retries = 3 //重试3次
transcodeM3u8RoutePrefix = "/api/app/vid/transcode/m3u8/"
transcodeSigningOriginHost = "hevc-pull.invalid"
// ctxM3u8TicketRequired 标记当前 m3u8 路由需要做 H5 防盗链票据校验。
// 仅 App H5 播放路由挂载 RequireM3u8Ticket;官网/分享等自有鉴权路由不挂载,避免误伤。
ctxM3u8TicketRequired = "m3u8_ticket_required"
)
// RequireM3u8Ticket 是一个标记中间件:挂到某条 m3u8 路由后,DownloadM3u8H5 会对其启用票据校验。
// 未挂载的路由保持旧逻辑(不验票),从而把防盗链范围精确限定在 App H5 播放地址上。
func RequireM3u8Ticket(c *gin.Context) {
c.Set(ctxM3u8TicketRequired, true)
}
var interval = []int64{5, 5, 10, 15} //通知时间间隔
// UploadStatic doc
// @Summary 文件管理 - 表单上传文件
// @Description 表单上传文件,上传静态文件到AWS 上传独立文件
// @Tags uploaddown
// @Accept mpfd,json
// @Produce json,html
// @Param upload formData file true "文件"
// @Success 200 {string} json "{"msg": "success" "data":{"coverImg":"xxxxxxxxxx.ext"}}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /vid/uploadStatic [post]
func UploadStatic(c *gin.Context) {
headers, err := c.FormFile("upload")
if err != nil {
log.Warn("request multipart wrong ", log.E(err))
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
return
}
f, err := headers.Open()
if err != nil {
log.Warn("headers Open file wrong ", log.E(err))
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
return
}
byteData, err := io.ReadAll(f)
f.Close()
if err != nil {
log.Warn("request multipart wrong ", log.E(err))
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
return
}
fileData := base64.StdEncoding.EncodeToString(byteData)
resp, err := updownloadser.SendImageToFS(headers.Filename, fileData)
if err != nil {
log.Warn("file upload wrong ", log.E(err))
common.ServeJSON(c, stderr.ErrUploadError, err.Error())
return
}
if resp.Code == http.StatusOK {
common.ServeJSON(c, stderr.Success, gin.H{"coverImg": resp.Data.FileName})
return
}
common.ServeJSON(c, stderr.ErrUploadError, "")
}
// UploadDotJson doc
// @Summary 文件管理 - API断点续传文件
// @Description API断点续传
// @Tags uploaddown
// @Accept mpfd,json
// @Produce json,html
// @Param id formData string true "taskId 文件MD5做ID"
// @Param pos formData integer true "第几片视频"
// @Param totalPos formData integer true "视频总片数"
// @Param data formData string true "数据内容"
// @Success 200 {string} json "{"msg": "success" "data":{"id":"5d8a2af58747044ca077f358","videoUri":"xxxxxxx.m3u8"} }"
// @Failure 400 {string} json "{"msg": "fail"}"
// @Router /vid/uploadDotJson [post]
func UploadDotJson(c *gin.Context) {
uid, err := common.GetUID(c)
if err == nil && uid > 0 {
//判断该用户是否被禁止上传视频
user, err := usermod.FindUserByUID(uid)
if err != nil || (user != nil && user.ForbidUpload) {
common.ServeJSON(c, stderr.ForbidUploadVideo, "")
return
}
}
var cnt int
var resp commod.Resp
var httpErr error
var args struct {
ID string `form:"id" json:"id" binding:"required"` //taskId
POS int64 `form:"pos" json:"pos" binding:"required"` //分片序号
TotalPos int64 `form:"totalPos" json:"totalPos" binding:"required"` //总分片数
Data string `form:"data" json:"data" binding:"required"` //分片内容
}
if err = c.ShouldBind(&args); err != nil {
common.ServeJSON(c, stderr.ErrParamError, err)
return
}
for cnt < Retries {
resp, httpErr = updownloadser.SendFile2FS(args.ID, args.Data, args.POS, args.TotalPos)
if httpErr == nil {
break
}
time.Sleep(time.Duration(interval[cnt]) * time.Second)
log.Warn("retry to upload file to file-server", log.Any("重试次数", cnt), log.E(httpErr))
cnt++
}
if httpErr != nil {
log.Warn("file upload wrong ", log.E(err))
common.ServeJSON(c, stderr.ErrUploadError, nil)
return
}
if resp.Code == http.StatusOK {
common.ServeJSON(c, stderr.Success, resp.Data)
return
}
common.ServeJSON(c, stderr.ErrUploadError, nil)
}
// DownloadM3u8H5 doc
// @Summary 文件管理 - 下载文件接口H5
// @Description 下载文件
// @Tags 正式
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /vid//h5/m3u8/:source [get]
func DownloadM3u8H5(c *gin.Context) {
// 统计请求来源(Referer/Origin):进程内无锁累加,后台定时批量刷回 Redis ZSet 计数。
collectM3u8H5Referer(c)
source := c.Param("source")
if source == "" {
common.ServeJSON(c, stderr.ErrParamError, "")
return
}
//cdn有值,则代表前端选线使用
cdn := c.Query("c")
// 防盗链:开启票据后校验,校验失败改下发广告兜底 m3u8,阻断盗链。
// 带票地址是加密单段 token(无 .m3u8 后缀),故先验票解出真实 path,再判断后缀与取文件名。
source = verifyH5M3u8Ticket(c, source)
ext := filepath.Ext(source)
if ext != ".m3u8" {
common.ServeJSON(c, stderr.ErrMimeType, "")
return
}
fileName := filepath.Base(source)
c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileName))
cdns := sourcemod.GetCdnURL()
if len(cdns) <= 0 {
common.ServeJSON(c, stderr.Failure, "")
return
}
if cdn == "" {
//cdn为空时,则前端为老版本,没有选线
//切记,后台配置第一个域名为当前系统常用cdn域名(eg:松鼠云)
cdn = cdns[0].Url
//去掉首尾反斜杠(/)、空格
cdn = strings.Trim(cdn, "/ ")
}
byteBuff, err := m3u8.GetAPPM3u8(source, fileName, ext, cdn, updownloadser.FsIO)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"data": "", "msg": err.Error()})
return
}
if byteBuff == nil {
c.JSON(http.StatusBadRequest, gin.H{"data": "", "msg": stderr.CodeEmptyData.Msg()})
return
}
c.Writer.Header().Add("Content-Length", strconv.Itoa(byteBuff.Len()))
c.Data(200, "application/octet-stream", byteBuff.Bytes())
}
// m3u8 请求来源计数(按 origin):进程内用 map[origin]次数 聚合,再由后台定时批量刷回 Redis ZSet。
// 每个请求都计入(不去重,去重会丢失次数),把高频接口"每请求一次 Redis 写"降为"每周期每来源一次"。
// ZSet:member=origin,score=该来源累计请求次数 —— ZScore 查单个来源,ZRevRangeWithScores 看 Top。
var (
m3u8RefererMu sync.Mutex
m3u8RefererCounts = make(map[string]int64) // key=origin,value=该来源累计请求次数
m3u8RefererFlushOnce sync.Once
)
// m3u8RefererFlushInterval 为聚合计数刷回 Redis 的周期;越短则崩溃丢失窗口越小、Redis 写越频繁。
const m3u8RefererFlushInterval = 5 * time.Second
// m3u8RefererResetHour 为来源累计计数每日清零的整点(time.Local,已在启动时设为 Asia/Shanghai):
// 每天该点该 ZSet 过期失效、从零重新累计。改此值即可调整清零时刻。
const m3u8RefererResetHour = 5
// collectM3u8H5Referer 给当前请求来源(origin)的计数 +1:每个 origin 在 map 里各占一个计数。
// 加锁只护一次 map 自增,不碰 Redis;计数由后台定时批量刷回 Redis(见 startM3u8RefererFlusher)。
func collectM3u8H5Referer(c *gin.Context) {
origin := refererOrigin(c)
if origin == "" {
return
}
m3u8RefererFlushOnce.Do(startM3u8RefererFlusher) // 首个请求到来时惰性启动后台刷新协程
m3u8RefererMu.Lock()
m3u8RefererCounts[origin]++ // 每个 origin 各自累加
m3u8RefererMu.Unlock()
}
// startM3u8RefererFlusher 启动后台协程,按固定周期把聚合计数批量刷回 Redis。
func startM3u8RefererFlusher() {
common.Go(func() {
ticker := time.NewTicker(m3u8RefererFlushInterval)
defer ticker.Stop()
for range ticker.C {
FlushM3u8RefererStats()
}
})
}
// FlushM3u8RefererStats 换出当前按 origin 聚合的计数,逐个 ZIncrBy 刷回 Redis,供定时器与进程退出兜底调用。
// 锁内只换出快照(不含 Redis IO),换出后本地表即清空(空闲来源自然淘汰);刷回失败的计数并回本地表、下个周期重试。
func FlushM3u8RefererStats() {
if appg.Redis == nil {
return
}
m3u8RefererMu.Lock()
if len(m3u8RefererCounts) == 0 {
m3u8RefererMu.Unlock()
return
}
snapshot := m3u8RefererCounts
m3u8RefererCounts = make(map[string]int64)
m3u8RefererMu.Unlock()
var failed map[string]int64
for origin, cnt := range snapshot {
if _, err := appg.Redis.ZIncrBy(redisconst.M3u8H5RefererSet, float64(cnt), origin); err != nil {
if failed == nil {
failed = make(map[string]int64)
}
failed[origin] += cnt
}
}
// 每天凌晨 m3u8RefererResetHour 点整体清零:每次刷回都把过期续到下一个清零点,到点 Redis 删除该 key,
// 下次刷回自然重建、从零累计。用 EXPIREAT(绝对时间点)而非相对 TTL,故进程重启/无请求空窗期也照常按点失效。
_, _ = appg.Redis.ExpireKeAt(redisconst.M3u8H5RefererSet, nextM3u8RefererResetAt())
if len(failed) > 0 {
m3u8RefererMu.Lock()
for origin, cnt := range failed {
m3u8RefererCounts[origin] += cnt
}
m3u8RefererMu.Unlock()
log.Warn("flush m3u8 referer stats partially failed", log.Any("failedSources", len(failed)))
}
}
// nextM3u8RefererResetAt 返回下一个每日清零时刻(今天 m3u8RefererResetHour 点未过则用今天,已过则用明天),
// 供刷回时给累计 ZSet 设 EXPIREAT。基于 time.Now()(time.Local=Asia/Shanghai),即北京时间。
func nextM3u8RefererResetAt() time.Time {
now := time.Now()
reset := time.Date(now.Year(), now.Month(), now.Day(), m3u8RefererResetHour, 0, 0, 0, now.Location())
if !now.Before(reset) { // 已到/过今天清零点,则顺延到明天
reset = reset.AddDate(0, 0, 1)
}
return reset
}
// refererOrigin 提取请求来源站点并归一到 scheme://host:优先 Referer,缺省或解析失败时回退 Origin。
// 解析不出 host(非法 URL / Origin 为 "null" 等)返回空串丢弃,避免任意串塞进永不过期的 ZSet 撑爆内存。
func refererOrigin(c *gin.Context) string {
if o := normalizeOrigin(c.GetHeader("Referer")); o != "" {
return o
}
return normalizeOrigin(c.GetHeader("Origin"))
}
// normalizeOrigin 把来源头归一到 scheme://host;空串或解析不出 host 一律返回空串。
func normalizeOrigin(raw string) string {
if raw == "" {
return ""
}
if u, err := url.Parse(raw); err == nil && u.Host != "" {
return u.Scheme + "://" + u.Host
}
return ""
}
// verifyH5M3u8Ticket 校验 H5 m3u8 播放防盗链票据。
// 未配置密钥时原样返回 source(保持旧逻辑);开启后票据非法/过期/IP 不符则返回广告兜底 source,
// 并打上 no-store 避免中间层把兜底 playlist 当正片缓存。
func verifyH5M3u8Ticket(c *gin.Context, source string) string {
if !m3u8ticket.Enabled() {
return source
}
// 仅 h5/m3u8 这类显式标记 RequireM3u8Ticket 的路由做严格校验;其余路由(h5 light/官网/分享等)只把
// 带票地址解密还原成真实 path,不校验,保证新老链接都能播放。
if !c.GetBool(ctxM3u8TicketRequired) {
return m3u8ticket.StripTicket(source)
}
ip := common.GetIP(c)
ua := ""
if u, uaErr := common.GetUA(c); uaErr == nil {
ua = u.UserAgent
}
// 带票地址形如 /{version}/{token}.m3u8,真实 path 加密在 token 里。
realSource, info, ok := m3u8ticket.VerifyPath(source, ip, ua)
if ok {
// 若上游 Auth 已解析出登录用户,则要求与票据签发用户一致,进一步绑定到本人。
if uid := common.TryGetUID(c); uid > 0 && uid != info.UserID {
ok = false
}
}
if !ok {
log.Warn("DownloadM3u8H5 ticket invalid",
log.Any("source", source),
log.Any("ip", ip),
)
c.Header("Cache-Control", "private, no-store")
return m3u8ticket.FallbackPath
}
return realSource
}
func transcodePlaybackSource(normalizedSource string) string {
return "/" + strings.TrimLeft(normalizedSource, "/")
}
func signedTranscodeChildPlaylistURI(parentSource, childURI, secret string, expiresAt time.Time) (string, error) {
childSource, err := hevcpull.ResolveChildSource(parentSource, childURI)
if err != nil {
return "", err
}
unsigned := (&url.URL{
Scheme: "https",
Host: transcodeSigningOriginHost,
Path: transcodeM3u8RoutePrefix + childSource,
}).String()
signed, err := hevcpull.SignURL(unsigned, secret, expiresAt)
if err != nil {
return "", err
}
parsed, err := url.Parse(signed)
if err != nil {
return "", err
}
// Root-relative output keeps the trusted host of the originally signed
// master URL and cannot be influenced by a forwarded Host header.
return parsed.RequestURI(), nil
}
+58
View File
@@ -0,0 +1,58 @@
package updownctrl
import (
"net/url"
"strings"
"testing"
"time"
"91jd-server/common/hevcpull"
)
func TestSignedTranscodeChildPlaylistURI(t *testing.T) {
now := time.Date(2026, 7, 25, 12, 0, 0, 0, time.UTC)
expiresAt := now.Add(time.Hour)
secret := "test-pull-secret-strong-32-bytes!!"
childURI, err := signedTranscodeChildPlaylistURI(
"sp/movie/master.m3u8",
"720/index.m3u8",
secret,
expiresAt,
)
if err != nil {
t.Fatalf("signedTranscodeChildPlaylistURI failed: %v", err)
}
if !strings.HasPrefix(childURI, transcodeM3u8RoutePrefix+"sp/movie/720/index.m3u8?") {
t.Fatalf("unexpected child URI: %s", childURI)
}
parsed, err := url.Parse("https://app.example.com" + childURI)
if err != nil {
t.Fatalf("parse child URI: %v", err)
}
if err = hevcpull.VerifyURL(parsed, secret, now); err != nil {
t.Fatalf("child URI signature failed: %v", err)
}
if _, err = signedTranscodeChildPlaylistURI(
"sp/movie/master.m3u8",
"https://external.example.com/index.m3u8",
secret,
expiresAt,
); err == nil {
t.Fatal("absolute child playlist was accepted")
}
}
func TestTranscodePlaybackSourcePreservesExistingM3u8Semantics(t *testing.T) {
for _, source := range []string{
"sp/movie/index.m3u8",
"pms/movie/index.m3u8",
"laosiji/m3m/movie/index.m3u8",
} {
got := transcodePlaybackSource(source)
if got != "/"+source {
t.Fatalf("transcodePlaybackSource(%q) = %q", source, got)
}
}
}
+46
View File
@@ -0,0 +1,46 @@
package userctrl
import "91jd-server/models/commod"
// PageRule 通用分页验证结构体
type PageRule struct {
UID uint64 `json:"uid" form:"uid"`
//hot 热度值排序;watch 最多播放;like 最多点赞(收藏);new 最新视频
Type string `json:"type" form:"type"`
// 点赞类型: SP:长视频 SHORT:短视频 COVER:图文帖子 PIC:图集帖子 SEED_LINK:种子/黄油帖子 TAG:标签 COMMENT:评论 video:动漫 image:漫画 text:小说"
LikeType string `json:"likeType" form:"likeType"`
//0 默认全部 1 长视频 2 短视频 3-帖子 4-图集
PlayTimeType int `json:"playTimeType" form:"playTimeType"`
commod.Page
}
var portrait = []string{
"image/gy/8j/hw/la/aa283b9a2c6c443883da2c6dec05c19a.jpeg",
"image/e5/oq/hd/hi/2a120f6ebb44468b92a1f0c55b4a475c.jpeg",
"image/tl/gc/lk/5p/96586678db814f579e6eaf8ef55165bc.jpeg",
"image/hm/21/19/57/e5c625d1a903453e8fc8e3e3575c860b.jpeg",
"image/0v/h7/g1/tb/f3d02445d42e49b7915b843aecfb7c7c.jpeg",
"image/en/fl/su/8w/d7e168beed5c4742be13cf998613d112.jpeg",
"image/25/2q/gh/fd/a122e79c556c40c1a61e4ad55219134f.jpeg",
"image/6h/2k/gs/5x/6fe68e9d41ae4cbda2a5e34e64ba4af9.jpeg",
"image/kz/2y/xv/a9/ad29c0e2fb7e43c282a9c1d4d70e1185.jpeg",
"image/1s/h3/hi/ht/1de64b74f1c34545ae8013553b12c6c1.jpeg",
"image/6n/os/7a/24/7e5031e18874496bb7481fbe27a85d34.jpeg",
"image/vo/g9/nj/l2/f288707eeb9041b8b471f0ac5f25fcdd.jpeg",
"image/of/cg/cb/zm/90280c2e0c2c4198b677c0e0430c3166.jpeg",
"image/ex/yb/so/l0/5011e1a5415c400783ab5d29d1d68129.jpeg",
"image/0b/4n/ct/hs/9d84d100aba340558f68e638e45c6203.jpeg",
"image/2b/bz/6w/g5/bbb4d6d1c0064491a5131a75a0d23546.jpeg",
"image/qf/n1/oq/m6/a29791ca041541c192205e1c82073398.jpeg",
"image/oa/u8/w3/f6/d149a55b4e894fbba332fa06889a091a.jpeg",
"image/ar/8x/9u/xw/e69e7be1a7174e6d96049b9b84888989.jpeg",
"image/g0/uu/64/mu/3bdcdd6c86a64b589bfcd0c9fac2d7da.jpeg",
"image/xa/a2/gs/dz/406b619f79684992837334ec2c21cffb.jpeg",
"image/eq/wo/4y/8e/749252631f5c40bda89d19150bfc661c.jpeg",
"image/ox/z3/3u/tr/227819454ccf4851b0905c2c74331394.jpeg",
"image/75/fj/s5/b4/afeeaada5460465982cc8539d7c1ff03.jpeg",
"image/jr/8v/e2/84/4e75fdfcf8404700b3f1e3b352ed45e0.jpeg",
"image/pd/1s/ld/mp/779ce7c27d71446188b40cdf11774df0.jpeg",
"image/nb/92/0s/gc/f158a9166e3648c8be5de39259556caa.jpeg",
"image/kh/bj/aa/zp/a5088e99962049a398f92033838c97ad.jpeg",
}
+40
View File
@@ -0,0 +1,40 @@
package userctrl
import (
"91jd-server/app/service/userser"
"91jd-server/common"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// ConsumePrivilege doc
// @Summary 消耗预售卡预付权益
// @Description 消耗预售卡预付权益
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param q body userser.ConsumeReq false "请求参数"
// @Success 200 {string} string "操作成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/privilege/consume [post]
func ConsumePrivilege(ctx *gin.Context) {
// 获取用户当前配置
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var args userser.ConsumeReq
if err := ctx.ShouldBind(&args); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
err = userser.Consume(uid, args.PrivilegeType, args.Count)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, nil)
}
common.ServeJSON(ctx, stderr.Success, nil)
}
+807
View File
@@ -0,0 +1,807 @@
package userctrl
import (
"91jd-server/app/appg"
"91jd-server/app/middleware/authuser"
"91jd-server/app/service/commentser"
"91jd-server/app/service/filterser"
"91jd-server/app/service/proxyser"
"91jd-server/app/service/smsser"
"91jd-server/app/service/userser"
"91jd-server/common"
"91jd-server/common/constant"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/common/truthutil"
v10 "91jd-server/common/v10"
"91jd-server/common/ysphone"
"91jd-server/common/ysqr"
"91jd-server/models/commod"
"91jd-server/models/v/cmtmod"
"91jd-server/models/v/faqmod"
"91jd-server/models/v/usermod"
"91jd-server/models/v/vidmod"
"91jd-server/web/service/annouser"
"strconv"
"strings"
"github.com/gin-gonic/gin"
)
var devIDNilValue = "00000000-0000-0000-0000-000000000000"
var maxNameRune = 12
// MobileBind doc
// @Summary 用户模块 - 绑定手机号
// @Description 绑定手机号
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param mobile formData string true "电话号码"
// @Param code formData string true "验证码"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/mobileBind [post]
func MobileBind(ctx *gin.Context) {
var args struct {
Mobile string `json:"mobile" form:"mobile" binding:"required"`
Code string `json:"code" form:"code" binding:"required"`
PassWord string `json:"passWord"`
}
if err := ctx.ShouldBind(&args); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
args.Mobile = strings.TrimSpace(args.Mobile)
if !v10.IsGlobalizationPhoneNumber(args.Mobile) {
common.ServeJSON(ctx, stderr.ErrMobileInvalid, nil)
return
}
args.Mobile = ysphone.FormatPhoneNumber(args.Mobile)
verifyCode := smsser.VerifySmsCode(args.Mobile, args.Code)
if verifyCode != stderr.Success {
common.ServeJSON(ctx, verifyCode, nil)
return
}
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
code := userser.MobileBind(uid, args.Mobile, args.PassWord)
common.ServeJSON(ctx, code, nil)
}
// MobileLogin doc
// @Summary 用户模块 - 使用机号登陆
// @Description 使用手机号登陆
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param mobile formData string true "电话号码"
// @Param code formData string true "验证码"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/mobileLoginOnly [post]
func MobileLogin(ctx *gin.Context) {
var args struct {
Mobile string `json:"mobile" form:"mobile" binding:"required"`
Code string `json:"code" form:"code" binding:"required"`
}
if err := ctx.ShouldBind(&args); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
args.Mobile = ysphone.FormatPhoneNumber(strings.TrimSpace(args.Mobile))
fakeMobilePrefixLen := len(constant.FakeMobilePrefix)
if len(args.Mobile) < fakeMobilePrefixLen {
common.ServeJSON(ctx, stderr.ErrMobileInvalid, nil)
return
}
//非马甲手机号 需要校验
if args.Mobile[:fakeMobilePrefixLen] != constant.FakeMobilePrefix {
if !v10.IsGlobalizationPhoneNumber(args.Mobile) {
common.ServeJSON(ctx, stderr.ErrMobileInvalid, nil)
return
}
verifyCode := smsser.VerifySmsCode(args.Mobile, args.Code)
if verifyCode != stderr.Success {
common.ServeJSON(ctx, verifyCode, "")
return
}
} else {
//马甲账号验证1 验证码
if args.Code != appg.Static.VerifyCodeForFake {
common.ServeJSON(ctx, stderr.ErrCaptchaInvalid, "")
return
}
}
ip := common.GetIP(ctx)
ua, _ := common.GetUA(ctx)
userInfo, code := userser.MobileLoginOnly(ip, args.Mobile, ua)
if code == stderr.Success {
userInfo.LoginType = usermod.MobileLogin
common.ServeJSON(ctx, code, gin.H{"token": userInfo.Token, "userInfo": userInfo})
return
}
if code == stderr.ErrAccessForbid {
common.ServeJSON(ctx, code, gin.H{"uid": userInfo.UID, "reason": userInfo.LockReason})
return
}
common.ServeJSON(ctx, code, nil)
}
// Modify doc
// @Summary 用户模块 - 修改信息
// @Description 用户修改信息
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param gender formData string false "性别"
// @Param name formData string false "用户名称"
// @Param age formData integer false "年龄"
// @Param mobile formData string false "手机号"
// @Param password formData integer false "密码"
// @Param portrait formData string false "头像"
// @Param background formData string false "背景图"
// @Param status formData integer false "状态"
// @Param role formData string false "角色"
// @Param summary formData string false "简介"
// @Param region formData string false "地区"
// @Param promotionCode formData string false "推广码"
// @Param qRCode formData string false "二维码"
// @Param birthday formData string false "生日"
// @Param promotionCode formData string false "生日"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/info [post]
func Modify(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
set := usermod.UserModifyReq{}
if err = ctx.ShouldBind(&set); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
if set.AppLock != nil {
if len(*set.AppLock) != 4 {
common.ServeJSON(ctx, stderr.ErrParamError, "app_lock length invalid")
return
}
if _, err = strconv.ParseInt(*set.AppLock, 10, 64); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, "app_lock is not number string")
return
}
}
if set.Portrait != nil || set.Name != nil || set.Background != nil || set.Summary != nil {
u, err := usermod.FindUserByUID(uid)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err.Error())
return
}
if u == nil {
common.ServeJSON(ctx, stderr.UserIsNotExists, "")
return
}
//if set.Portrait != nil && u.CreatedAt.After(time.Now().Add(-7*24*time.Hour)) {
// common.ServeJSON(ctx, stderr.UserActiveNotEnough, "")
// return
//}
if !u.IsPaidVIP() {
common.ServeJSON(ctx, stderr.NoVipPrivilege, "")
return
}
if set.Name != nil {
if len([]rune(*set.Name)) > maxNameRune {
common.ServeJSON(ctx, stderr.ErrUserNameTooLong, nil)
return
}
newName, err := filterser.TextFilter(*set.Name)
if err == nil {
// 检查合法性
if !truthutil.CheckIsValid(newName, 0) {
msg := map[string]interface{}{"msg": "昵称设置错误"}
common.ServeJsonWithExtra(ctx, stderr.Failure, nil, msg)
return
}
set.Name = &newName
}
name := strings.TrimSpace(*set.Name)
set.Name = &name
}
if set.Summary != nil {
newSummary, err := filterser.TextFilter(*set.Summary)
if err == nil {
// 检查合法性
if !truthutil.CheckIsValid(newSummary, 0) {
msg := map[string]interface{}{"msg": "个性签名设置错误"}
common.ServeJsonWithExtra(ctx, stderr.Failure, nil, msg)
return
}
set.Summary = &newSummary
}
}
}
if _, err = usermod.UpdateSelf(uid, set); err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err)
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
// LoginWithDevice doc
// @Summary 用户模块 - 用户登陆
// @Description 登陆
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param devID formData string true "设备ID"
// @Param devType formData string true "设备型号"
// @Param sysType formData string true "系统类型" Enums(Android-10,IOS-13)
// @Param ver formData string true "app 应用版本号"
// @Param buildID formData string true "app 构建ID(包ID)"
// @Param channel formData string false "渠道"
// @Param promotionCode formData string false "推广码"
// @Param devToken formData string false "设备token"
// @Param cutInfos formData string false "剪切板信息"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/login [post]
func LoginWithDevice(ctx *gin.Context) {
token := ctx.Request.Header.Get("Authorization")
p := usermod.DevLoginP{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
if p.DevID == devIDNilValue {
common.ServeJSON(ctx, stderr.ErrParamError, "devID is nil")
return
}
if !common.IsNormalDevId(p.DevID) {
common.ServeJSON(ctx, stderr.ErrParamError, "devID is invalid")
return
}
ua, _ := common.GetUA(ctx)
ip := common.GetIP(ctx)
userInfo, code, loginType, err := userser.DeviceLogin(ip, p, token, ua)
if err != nil {
log.Warn("LoginWithDevice", log.E(err))
if code != 0 {
if code == stderr.ErrAccessForbid {
common.ServeJSON(ctx, code, gin.H{"uid": userInfo.UID, "reason": userInfo.LockReason})
return
}
common.ServeJSON(ctx, code, err.Error())
return
}
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err.Error())
return
}
userInfo.LoginType = loginType
common.ServeJSON(ctx, stderr.Success, userInfo)
}
// MyRights doc
// @Summary 用户模块 - 用户权益查询
// @Description 用户权益,包括观影券
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} usermod.UserRights
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/rights [get]
func MyRights(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
userRights, err := userser.MyRights(uid)
if err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, "")
return
}
log.Info("userRights info", log.Any("res", userRights))
common.ServeJSON(c, stderr.Success, userRights)
}
// Info doc
// @Summary 用户模块 - 基本信息查询
// @Description 用户基本信息查询
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param uid formData integer false "用户id(如果是查看自己页面则不传)"
// @Success 200 {object} usermod.UserRes "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/info [get]
func Info(ctx *gin.Context) {
uid, _ := common.GetUID(ctx)
var user struct {
UID uint64 `form:"uid" json:"uid"`
}
if err := ctx.ShouldBind(&user); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if user.UID != 0 {
info, err := userser.OtherInfo(uid, user.UID)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "")
return
}
common.ServeJSON(ctx, stderr.Success, info)
return
}
info, err := userser.Info(uid)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "")
return
}
log.Debug("info", log.Any("info", info))
common.ServeJSON(ctx, stderr.Success, info)
}
// Collection doc
// @Summary 我的模块 - 我的帖子
// @Description 作品,传uid是获取所传uid的作品,不传是获取自己的作品
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param uid formData integer false "用户id"
// @Param pageSize formData int true "每页显示条数"
// @Param pageNumber formData int true "当前第几页"
// @Param uid formData uint64 false "用户id(查询其他用户使用,非必传)"
// @Param type formData string false "hot 热度值排序;new 最新视频"
// @Success 200 {object} userser.VideoListResp 操作成功
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/collection [get]
func Collection(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
pgR := PageRule{}
if err = ctx.ShouldBind(&pgR); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
var collection []*vidmod.VideoInfoResp
var hasNext bool
var total int64
if pgR.UID == 0 {
collection, total, hasNext, err = userser.IVList(uid, pgR.Type, pgR.PageSize, pgR.PageNumber, pgR.PlayTimeType)
} else {
collection, total, hasNext, err = userser.HisVList(uid, pgR.UID, pgR.PageSize, pgR.PageNumber, pgR.Type, pgR.PlayTimeType)
}
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err)
return
}
resp := userser.VideoListResp{
List: collection,
Total: total,
HasNext: hasNext,
}
common.ServeJSON(ctx, stderr.Success, resp)
}
// PublishList doc
// @Summary 我的模块 - 用户发布的帖子列表
// @Description 作品-用户发布的帖子列表
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param q body userser.PublishWorkReq true "参数"
// @Success 200 {object} userser.PublishWorkRep 操作成功
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/publish [get]
func PublishList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
req := userser.PublishWorkReq{}
if err = ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
rep, err := req.GetUserPublishWorkList(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, rep)
}
// PublishWorkDelete doc
// @Summary 我的模块 - 删除我发布但未通过审核的作品
// @Description 作品-删除我发布但未通过审核的作品
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param q body userser.PublishWorkDeleteReq true "参数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/publish/delete [post]
func PublishWorkDelete(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
req := userser.PublishWorkDeleteReq{}
if err = ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
err = req.UserPublishWorkDelete(uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, nil)
}
// BuyVidList doc
// @Summary 我的模块 - 购买的视频
// @Description 我购买的视频
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param uid formData integer false "用户id"
// @Param pageSize formData int true "每页显示条数"
// @Param pageNumber formData int true "当前第几页"
// @Param newsType formData string true "购买类型 SP,SHORT,COVER,PIC,SEED_LINK"
// @Success 200 {object} userser.VideoListResp 操作成功
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/buyVid [get]
func BuyVidList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var p struct {
UID uint64 `form:"uid" json:"uid"`
PageNumber int64 `form:"pageNumber" json:"pageNumber" binding:"required"`
PageSize int64 `form:"pageSize" json:"pageSize" binding:"required"`
NewsType string `form:"newsType" json:"newsType"`
}
if err = ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
vList, total, hasNext, err := userser.BuyVidList(uid, p.UID, p.NewsType, p.PageSize, p.PageNumber)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err)
return
}
resp := userser.VideoListResp{
List: vList,
Total: total,
HasNext: hasNext,
}
common.ServeJSON(ctx, stderr.Success, resp)
}
// Like doc
// @Summary 我的模块 - 我的喜欢
// @Description 喜欢,传uid是获取所传uid的喜欢,不传是获取自己的喜欢
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param q query userser.GetUserLikeReq true "每页显示条数"
// @Success 200 {object} userser.ILikeListRep 操作成功
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/like [get]
func Like(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
req := userser.GetUserLikeReq{}
err = ctx.ShouldBind(&req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
vList, hasNext, err := userser.UserLikesVidList(uid, &req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, nil)
return
}
resp := userser.ILikeListRep{
List: vList,
HasNext: hasNext,
}
common.ServeJSON(ctx, stderr.Success, resp)
return
}
// Comment doc
// @Summary 我的模块 - 我的评论
// @Description 我的评论
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param pageSize formData int true "每页显示条数"
// @Param pageNumber formData int true "当前第几页"
// @Success 200 {object} userser.VideoListResp 操作成功
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/comment [get]
func Comment(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
pgR := PageRule{}
err = ctx.ShouldBind(&pgR)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
list, hasNext, err := commentser.GetMyCmtList(uid, pgR.Page)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
common.ServeJSON(ctx, stderr.Success, &cmtmod.MyCmtResp{
List: list,
HasNext: hasNext,
})
}
// FaqList doc
// @Summary 用户常见问题
// @Description 用户常见问题列表
// @Tags 用户管理
// @Accept mpfd,json
// @Produce json,html
// @Param category query string false "问题类型"
// @Param pageNumber query integer true "当前页"
// @Param pageSize query integer true "每页条数"
// @Success 200 {string} json "{"msg": "操作成功", "date": {}}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/faq/list [get]
func FaqList(c *gin.Context) {
var args struct {
Category string `form:"cate" json:"cate" binding:"required"`
commod.Page
}
if err := c.ShouldBind(&args); err != nil {
common.ServeJSON(c, stderr.ErrParamError, "web user FaqList arg error "+err.Error())
return
}
stdQuery := commod.StdQuery{Page: &commod.PageBy{Num: args.PageNumber, Size: args.PageSize}}
page, err := userser.FaqList(args.Category, stdQuery)
if err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, "web user PlayRecord error: "+err.Error())
return
}
common.ServeJSON(c, stderr.Success, page)
}
// FaqCateList doc
// @Summary 用户常见问题
// @Description 用户常见问题分类列表
// @Tags 用户管理
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功", "date": {}}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /web/user/faq/cate/list [get]
func FaqCateList(c *gin.Context) {
page, err := faqmod.FaqCateList()
if err != nil {
common.ServeJSON(c, stderr.ErrNetWorkBusy, "web user FaqCateList error: "+err.Error())
return
}
common.ServeJSON(c, stderr.Success, page)
}
// QrCode doc
// @Summary 二维码
// @Description 二维码
// @Tags 用户管理
// @Accept mpfd,json
// @Produce json,html
// @Param Authorization header string true "user token"
// @Success 200 {string} json "{"msg": "操作成功", "content": ""}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/certificate/qr [get]
func QrCode(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
user, _ := usermod.FindUserByUID(uid)
if user == nil {
common.ServeJSON(ctx, stderr.AdminIsNotExist, "")
return
}
t := ysqr.Login
if actType, b := ctx.Get("actType"); b {
at, ok := actType.(ysqr.ActType)
if ok {
t = at
}
}
content := ysqr.Content{
UID: user.UID,
T: t,
LoginClaims: ysqr.LoginClaims{
DevID: user.DevID,
},
}
common.ServeJSON(ctx, stderr.Success, gin.H{
"content": content.String(authuser.GetTokenSecret()),
})
}
// Invite doc
// @Summary 邀请
// @Description 邀请绑定
// @Tags 用户
// @Accept mpfd,json
// @Produce json,html
// @Param promotionCode formData string true "邀请码"
// @Success 200 {string} json "{"msg": "操作成功", "content": ""}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/inviteBind [post]
func Invite(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var p struct {
PromCode string `json:"promotionCode" bson:"promotionCode"` //推广码
}
if err = ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
code := proxyser.InviteBind(uid, p.PromCode, true)
common.ServeJSON(ctx, code, code.Msg())
}
// AnnounceList doc
// @Summary 跑马灯列表
// @Description 跑马灯列表
// @Tags 用户
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} []annoumod.Announce "success"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/announce/list [get]
func AnnounceList(ctx *gin.Context) {
code, data := annouser.GetAnnounceList()
common.ServeJSON(ctx, code, data)
}
// LoginWithH5 doc
// @Summary 用户模块 - 注册用户接口
// @Description 手机注册登陆
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param request usermod.LoginWithH5 body object true "request"
// @Success 200 {object} usermod.UserRes "success"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/login/h5 [post]
func LoginWithH5(ctx *gin.Context) {
p := usermod.LoginWithH5{}
if err := ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// 仅允许 h5/ios 端登录注册,其余 sysType 一律拒绝
if p.SysType != "h5" && p.SysType != "ios" {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
// devID 以 web 开头的为攻击流量,直接拒绝(忽略大小写)
if strings.HasPrefix(strings.ToLower(p.DevID), "web") {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
ip := common.GetIP(ctx)
ua, err := common.GetUA(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
log.Info("login info", log.Any("ip", ip), log.Any("p", p), log.Any("ua", ua))
user, code, err := userser.H5Login(ctx, ip, p, ua)
if err != nil || code != stderr.Success {
log.ErrorX(ctx, "login h5 failed", log.Any("req", p), log.Any("ip", ip), log.E(err))
common.ServeJSON(ctx, code, nil)
return
}
common.ServeJSON(ctx, code, user)
}
// LoginByQRCode doc
// @Summary 用户模块 - 登录
// @Description H5用户通过二维码登录
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param request body usermod.LoginH5ByQRCode true "request"
// @Success 200 {object} usermod.UserRes "success"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/login/h5/qr [post]
func LoginByQRCode(ctx *gin.Context) {
req := usermod.LoginH5ByQRCode{}
if err := ctx.ShouldBind(&req); err != nil {
log.Error("bind request parameter failed")
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
user, err := userser.H5LoginByQRCode(ctx, req.Content, common.GetIP(ctx))
if err != stderr.Success {
common.ServeJSON(ctx, err, nil)
return
}
common.ServeJSON(ctx, stderr.Success, user)
}
// ExtractQRCode doc
// @Summary 解析二维码
// @Description 解析二维码
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} string "success"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/qrcode/extract [post]
func ExtractQRCode(ctx *gin.Context) {
file, err := ctx.FormFile("qrcode")
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
f, err := file.Open()
if err != nil {
common.ServeJSON(ctx, stderr.ErrInterServerError, err)
return
}
content, err := common.DecodeQRCode(f)
f.Close()
if err != nil {
common.ServeJSON(ctx, stderr.ErrInterServerError, err)
return
}
common.ServeJSON(ctx, stderr.Success, content)
}
// Portrait 头像列表
//
// @Summary 头像列表
// @Description 头像列表
// @Tags user
// @Accept mpfd,json
// @Produce json,html
// @Param Authorization header string true "user token"
// @Success 200 {string} json "{"msg": "操作成功", "content": ""}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/portrait [get]
func Portrait(ctx *gin.Context) {
common.ServeJSON(ctx, stderr.Success, gin.H{"data": portrait})
}
+42
View File
@@ -0,0 +1,42 @@
package vidctrl
import (
"91jd-server/app/service/vidser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/vidmod"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary 获取帖子
// @Description 获取帖子
// @Tags
// @Accept mpfd,json
// @Produce json,html
// @Param type query integer true "类型"
// @Param model query integer false "模块"
// @Param time query time.Time false "请求页面时间"
// @Param tag query string false "标签ID"
// @Param paymentType query integer false "付费类型"
// @Param city query string false "城市"
// @Param pageNumber query integer true "查询页码"
// @Param pageSize query integer true "页码大小"
// @Success 200 {object} vidmod.NewsListResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/vid/list [get]
func List(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
var req vidmod.AppListReq
if err = c.ShouldBind(&req); err != nil {
common.ServeJSON(c, stderr.ErrParamError, nil)
return
}
data, code := vidser.List(uid, &req)
common.ServeJSON(c, code, data)
}
+825
View File
@@ -0,0 +1,825 @@
package vidctrl
import (
"91jd-server/models/v/discount_area_mod"
"errors"
"fmt"
"strings"
"time"
"91jd-server/app/service/m3u8ticket"
"91jd-server/app/service/vidser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/v/usermod"
"91jd-server/models/v/vidmod"
"github.com/gin-gonic/gin"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// GetLocationList doc
// @Summary 获取同城视频
// @Description 获取同城视频
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param ip formData string true "用户ip地址"
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "页码大小"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/vid/location/list [get]
func GetLocationList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
req := vidmod.LocVideoReq{}
if err = ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if req.PageSize < 1 {
req.PageSize = 10
}
code, data := vidser.GetLocationList(uid, ctx.ClientIP(), req.City, req.PageNumber, req.PageSize, vidmod.SP)
common.ServeJSON(ctx, code, data)
}
// GetVidInfo doc
// @Summary 获取视频详情
// @Description 获取视频详情
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param videoID formData string true "视频id"
// @Param searchAccessToken query string false "搜索结果返回的短时效访问凭证"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/vid/info [get]
func GetVidInfo(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
param := vidmod.VideoReq{}
if err = ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if len(param.VideoID) == 0 {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
code, data := vidser.GetInfoWithSearchAccess(uid, param.VideoID, param.SearchAccessToken)
// 视频详情:对 m3u8 播放地址签发 H5 防盗链票据
m3u8ticket.Sign(ctx, data)
common.ServeJSON(ctx, code, data)
}
// Submit doc
// @Summary 提交视频的基本信息
// @Description 视频基本信息提交
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param uid formData integer false "用户ID"
// @Param title formData string false "视频标题"
// @Param content formData string false "视频内容"
// @Param newsType formData string false "上传类型 COVER-图集 SP-视频"
// @Param tags formData array true "视频标签数组"
// @Param playTime formData integer false "视频时长"
// @Param cover formData string false "封面图"
// @Param coverThumb formData string false "小图"
// @Param seriesCover formData array false "图集图片地址"
// @Param via formData string false "视频来源"
// @Param coins formData integer false "观看金币"
// @Param size formData integer false "文件大小"
// @Param mimeType formData string false "影片类型"
// @Param actor formData string false "演员名字"
// @Param sourceURL formData string false "资源url 为SP时 必传"
// @Param sourceID formData string false "上传视频成功后 返回的ID 为SP时必传"
// @Param filename formData string false "文件名"
// @Param resolution formData string false "分辨率"
// @Param ratio formData number false "视频比列"
// @Param md5 formData string false "文件摘要"
// @Param freeTime formData integer false "免费观影时长"
// @Param location formData string false "地理位置信息"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /vid/submit [post]
func Submit(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
param := vidmod.SubmitReq{}
if err = ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
//判断该用户是否被禁止上传视频
user, err := usermod.FindUserByUID(uid)
if err != nil || (user != nil && user.ForbidUpload) {
common.ServeJSON(ctx, stderr.ForbidUploadVideo, err)
return
}
if param.NewsType == vidmod.COVER || param.NewsType == vidmod.PIC {
if user.CoverUploadCount > 3 && !user.IsPaidVIP() {
common.ServeJSON(ctx, stderr.NoVipNoUpload, err)
return
}
} else if param.NewsType == vidmod.SP || param.NewsType == vidmod.SHORT {
if user.VidUploadCount >= 3 && !user.IsPaidVIP() {
common.ServeJSON(ctx, stderr.NoVipNoUpload, err)
return
}
}
//默认newsType
if len(param.NewsType) == 0 {
param.NewsType = vidmod.SP
}
//图集的第一张放到封面中
if (param.NewsType == vidmod.COVER || param.NewsType == vidmod.PIC) && len(param.SeriesCover) != 0 {
param.Cover = param.SeriesCover[0]
param.CoverThumb = param.SeriesCover[0]
}
//免费时长设置,收费视频
if param.Coins > 0 {
param.FreeTime = vidmod.GetFreeTime(param.PlayTime)
}
ip := common.GetIP(ctx)
code, vid := vidser.SubmitBase(uid, param, ip)
if code != stderr.Success {
_, _ = vidser.DeleteManyByTTL(uid, vid)
common.ServeJSON(ctx, code, vid)
return
}
n := int64(1)
if param.NewsType == vidmod.COVER || param.NewsType == vidmod.PIC {
// code = vidser.SendCoverInfo2FS(param)
// if code != stderr.Success {
// vidser.DeleteManyByTTL(uid, vid)
// }
_ = usermod.IncUploadCount(uid, usermod.UploadCountInc{
TotalWorks: &n,
CoverUploadCount: &n,
})
common.ServeJSON(ctx, code, vid)
return
}
if param.NewsType == vidmod.SP || param.NewsType == vidmod.SHORT {
_ = usermod.IncUploadCount(uid, usermod.UploadCountInc{
TotalWorks: &n,
VidUploadCount: &n,
})
}
param.SourceID = vid.Hex()
code = vidser.SendBaseInfo2FS(param)
if code != stderr.Success {
_, _ = vidser.DeleteManyByTTL(uid, vid)
}
common.ServeJSON(ctx, code, vid)
}
// PlayRecord doc
// @Summary 今日热门视频列表
// @Description 今日热门视频列表
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param videoID formData string true "视频id"
// @Param beginTime formData string true "开始播放的时间"
// @Param endTime formData string true "退出或者跳转到下个视频的时间"
// @Param playWay formData integer true "通过什么途径获取视频的 0,免费 1付费 2.试看 "
// @Param via formData integer true "视频来源,1.推荐 2.附近 3专题 4搜索 5分享 6热搜 7收藏 8热搜"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/vid/play [post]
func PlayRecord(ctx *gin.Context) {
uid := common.TryGetUID(ctx)
param := vidmod.PlayReq{}
if err := ctx.ShouldBind(&param); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
code, data := vidser.PlayRecord(uid, param)
common.ServeJSON(ctx, code, data)
}
// GetWatchCount doc
// @Summary 根据id获取用户观看次数
// @Description 根据id获取用户观看次数
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param vid formData string false "视频ID"
// @Success 200 {object} vidmod.WatchCountResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/user/count [get]
func GetWatchCount(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
var p struct {
Vid string `form:"vid"`
First string `form:"first"`
}
if err = ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if p.Vid == "" {
u, err := usermod.FindUserByUID(uid)
if err != nil || u == nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, "FindUserByUID error")
return
}
watchCount := vidser.FreshCount(u.UID)
watch := watchCount > 0
totalWatchCount := vidser.TotalFreeWatchCount()
log.Info("GetWatchCount", log.Any("uid", uid), log.Any("isCan", watch), log.Any("watchCount", watchCount),
log.Any("totalWatchCount", totalWatchCount))
common.ServeJSON(ctx, stderr.Success, vidmod.WatchCountResp{
IsCan: watch,
WatchCount: watchCount,
TotalWatchCount: totalWatchCount,
})
return
}
oid, err := primitive.ObjectIDFromHex(p.Vid)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, "get watch count arg error "+err.Error())
return
}
watch, watchCount := vidser.GetWatchCount(uid, oid, p.First)
log.Info("GetWatchCount", log.Any("uid", uid), log.Any("vid", p.Vid), log.Any("isCan", watch))
common.ServeJSON(ctx, stderr.Success, vidmod.WatchCountResp{
IsCan: watch,
WatchCount: watchCount,
TotalWatchCount: vidser.TotalFreeWatchCount(),
})
}
// ModuleVideoList doc
// @Summary 获取模块下各个专题视频
// @Description 获取模块下各个专题所有视频
// @Tags 首页模块
// @Accept mpfd,json
// @Produce json,html
// @Param subModuleID path string true "亚模块"
// @Param tagid query string true "标签id"
// @Param pageNumber query integer true "模块下视频页码"
// @Param pageSize query integer true "模式下视频数量"
// @Param moduleSort query integer true " 视频排序 1、最新,2、最热/最多喜欢,3、最多播放,4、十分钟以上视频, 5、精华/精选,6、视频 7-最多收藏 8、解锁次数"
// @Success 200 {object} vidser.VideoUnderSubModuleResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/module/:subModuleID [get]
func ModuleVideoList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil && !errors.Is(err, common.ErrUserNotExist) {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
subModuleIDStr := ctx.Param("subModuleID")
if subModuleIDStr == "" {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
subModuleID, err := primitive.ObjectIDFromHex(subModuleIDStr)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
req := vidser.VideoUnderSubModuleReq{}
if err := ctx.ShouldBindQuery(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
res, err := vidser.GetVideosUnderSubModuleNew(subModuleID, uid, req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
// 首页模块下各专题视频:对 m3u8 播放地址签发 H5 防盗链票据
m3u8ticket.Sign(ctx, res)
common.ServeJSON(ctx, stderr.Success, res)
}
// RefreshModuleVideos 从亚模块热门候选池中随机返回视频。
func RefreshModuleVideos(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil && !errors.Is(err, common.ErrUserNotExist) {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
subModuleID, err := primitive.ObjectIDFromHex(ctx.Param("subModuleID"))
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
req := vidser.RefreshModuleVideosReq{}
if err = ctx.ShouldBindQuery(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
req.RefreshToken = strings.TrimSpace(req.RefreshToken)
if req.RefreshToken == "" {
common.ServeJSON(ctx, stderr.ErrParamError, "refreshToken is required")
return
}
res, err := vidser.RefreshModuleVideos(uid, subModuleID, req)
if err != nil {
if errors.Is(err, vidser.ErrRandomRefreshUnsupported) {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
// 随机刷新列表:对 sourceURL、h265Url 签发播放防盗链票据。
m3u8ticket.Sign(ctx, res.AllVideoInfo)
common.ServeJSON(ctx, stderr.Success, res)
}
// SectionVideoList doc
// @Summary 获取一个专题下的视频列表
// @Description 获取一个专题下的视频列表,如“撩吧--推荐”模块下“XX”专题下的视频列表
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param sectionID path string true "专题ID"
// @Param pageSize query integer true "页码大小"
// @Param pageNumber query integer true "专题页码"
// @Param sortType query string true "watch:最多播放,new:最新,collect: 收藏"
// @Success 200 {object} vidser.VideoUnderSectionResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/section/:sectionID [get]
func SectionVideoList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil && !errors.Is(err, common.ErrUserNotExist) {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
req := vidser.VideoUnderSectionReq{}
if err = ctx.ShouldBindUri(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if err = ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
var sectionID primitive.ObjectID
//var bloggerID int
sectionID, err = primitive.ObjectIDFromHex(req.SectionID)
//bloggerID, err2 := strconv.Atoi(req.SectionID)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
page := commod.Page{}
if err = ctx.ShouldBind(&page); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
resp := vidser.VideoUnderSectionResp{}
if !sectionID.IsZero() {
resp, err = vidser.GetSectionVideos(uid, sectionID, page, req.SortType, req.PlayTimeType)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
}
//if bloggerID != 0 {
// resp, err = vidser.GetBloggerVideos(uid, uint64(bloggerID), page)
// if err != nil {
// serveVid(ctx, stderr.ErrDbQueryError, nil)
// return
// }
//}
common.Go(func() {
if err := vidser.IncSectionHits(ctx, sectionID); err != nil {
log.ErrorX(ctx, "vidser.IncSectionHits failed", log.Any("sectionID", sectionID), log.E(err))
}
})
common.ServeJSON(ctx, stderr.Success, resp)
}
// AllVideosOfModule doc
// @Summary 获取模块下所有视频
// @Description 获取模块下所有视频
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param subModuleID path string true "亚模块"
// @Param pageSize query integer true "页码大小,一个专题下视频数量"
// @Param sectionSize query integer true "专题数量"
// @Param sectionPage query integer true "专题页码"
// @Success 200 {object} vidser.VideoUnderSubModuleResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/module/moduleVideo [get]
func AllVideosOfModule(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil && !errors.Is(err, common.ErrUserNotExist) {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
req := vidser.AllVideosOfModuleReq{}
if err := ctx.ShouldBindQuery(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
subModuleID, err := primitive.ObjectIDFromHex(req.SubModuleID)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
res, err := vidser.AllVideosOfModule(subModuleID, uid, req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
common.ServeJSON(ctx, stderr.Success, res)
}
// DiscountVidList doc
// @Summary 获取折扣专区下视频
// @Description 获取折扣专区下视频
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param discountId query string true "折扣专区id"
// @Param pageSize query integer true "页码大小,一个专题下视频数量"
// @Param pageNumber query integer true "页码数"
// @Param sortType query integer true "排序类型 0-默认 1-最新 2-最热"
// @Success 200 {object} vidser.DiscountVideoResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/discount/list [get]
func DiscountVidList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil && !errors.Is(err, common.ErrUserNotExist) {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
req := vidser.DiscountVideoReq{}
if err := ctx.ShouldBindQuery(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
resp, err := vidser.GetDiscountAreaVideos(uid, &req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
// 通过折扣专区id获取折扣视频
common.ServeJSON(ctx, stderr.Success, resp)
}
// DiscountArea doc
// @Summary 获取vip折扣专区
// @Description 获取vip折扣专区
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} vidser.DiscountAreaResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/discount/area [get]
func DiscountArea(ctx *gin.Context) {
list, err := vidser.GetDiscountArea()
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
if len(list) == 0 {
list = []discount_area_mod.DiscountArea{}
}
common.ServeJSON(ctx, stderr.Success, &vidser.DiscountAreaResp{
List: list,
})
}
// CommunityRecommend doc
// @Summary 获取社区下黄油和图集的推荐
// @Description 获取社区下黄油和图集的推荐
// @Tags 社区模块
// @Accept mpfd,json
// @Produce json,html
// @Param q query vidser.CommunityRecommendReq false "请求参数"
// @Success 200 {object} vidser.VideoUnderSubModuleResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/community/recommend [get]
func CommunityRecommend(ctx *gin.Context) {
req := vidser.CommunityRecommendReq{}
if err := ctx.ShouldBindQuery(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
res, err := vidser.GetCommunityRecommend(req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
common.ServeJSON(ctx, stderr.Success, res)
}
// Ranking doc
// @Summary 视频/帖子/图集排行榜
// @Description 视频/帖子/图集排行榜
// @Tags 移动端-视频/帖子/图集排行榜
// @Accept mpfd,json
// @Produce json
// @Param q query vidser.RankingReq false "请求参数"
// @Success 200 object vidser.RankingResp "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/ranking [get]
func Ranking(ctx *gin.Context) {
p := &vidser.RankingReq{}
if err := ctx.ShouldBind(&p); err != nil {
log.Error(fmt.Sprintf("Rank param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
p.UID, _ = common.GetUID(ctx)
res, err := p.List()
if err != nil {
log.Error(fmt.Sprintf("Get Ranking List err:%v", err))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, res)
}
// SectionList doc
// @Summary 获取一个模块下的专题
// @Description 获取一个模块下的专题
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param mid path string true "模块id"
// @Param pageSize query integer true "页码大小"
// @Param pageNumber query integer true "专题页码"
// @Success 200 object vidser.SectionListResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/section/list [get]
func SectionList(ctx *gin.Context) {
req := vidser.SectionListReq{}
if err := ctx.ShouldBind(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
resp, err := req.GetList()
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
common.ServeJSON(ctx, stderr.Success, resp)
}
// RecommendList doc
// @Summary 推荐视频列表
// @Description 推荐视频列表
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param tagId query string true "标签id"
// @Param newsType query string true "帖子类型 SP-视频 SHORT-短视频 COVER-帖子 PIC-图集 SEED_LINK-种子帖"
// @Param pageNumber query int true "当前页"
// @Param pageSize query int true "每页条数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/recommend/list [get]
func RecommendList(c *gin.Context) {
var arg struct {
commod.Page
TagId string `json:"tagId" form:"tagId"` // 标签
NewsType string `json:"newsType" form:"newsType"` // 视频/帖子类型
}
if err := c.ShouldBind(&arg); err != nil {
common.ServeJSON(c, stderr.ErrParamError, "vid HotList arg error "+err.Error())
return
}
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrAccessForbid, err)
return
}
list, hasNext, err := vidser.GetRecommendList(uid, arg.TagId, arg.NewsType, arg.Page)
if err != nil {
common.ServeJSON(c, stderr.Failure, err)
return
}
common.ServeJSON(c, stderr.Success, gin.H{
"list": list,
"hasNext": hasNext,
"updatedAt": time.Now().Format("2006-01-02 15:04"),
})
}
// SubModuleAllVideoList doc
// @Summary 获取模块下视频列表(猜你喜欢)
// @Description 获取模块下视频列表,不区分专题(猜你喜欢)
// @Tags 首页模块
// @Accept mpfd,json
// @Produce json,html
// @Param subModuleID path string true "亚模块"
// @Param q query vidser.SubModuleAllVideoListReq false "请求参数"
// @Success 200 {object} vidser.VideoUnderSubModuleResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/module/all/:subModuleID [get]
func SubModuleAllVideoList(ctx *gin.Context) {
uid, _ := common.GetUID(ctx)
subModuleIDStr := ctx.Param("subModuleID")
if subModuleIDStr == "" {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
subModuleID, err := primitive.ObjectIDFromHex(subModuleIDStr)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
req := vidser.SubModuleAllVideoListReq{}
if err := ctx.ShouldBindQuery(&req); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
res, err := vidser.SubModuleAllVideoList(uid, subModuleID, req)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, nil)
return
}
common.ServeJSON(ctx, stderr.Success, res)
}
// Library doc
//
// @Summary 获取首页片库接口
// @Description 获取首页片库详情
// @Tags 移动端-主页模块
// @Accept mpfd,json
// @Produce json
// @Success 200 object vidmod.LibraryData "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/library [get]
func Library(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, nil)
return
}
data, err := vidser.GetLibrary(uid)
if err != nil {
common.ServeJSON(c, stderr.Failure, nil)
return
}
common.ServeJSON(c, stderr.Success, data)
}
// LibrarySearch doc
//
// @Summary 片库-搜索视频
// @Description 片库-搜索视频
// @Tags 移动端-主页模块
// @Accept mpfd,json
// @Produce json,html
// @Param q query vidser.LibraryElasticSearchRequest false "请求参数"
// @Success 200 object vidser.AppElasticSearchLibraryResponse "成功"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/library/search [post]
func LibrarySearch(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
in := vidser.LibraryElasticSearchRequest{}
if err := ctx.ShouldBind(&in); err != nil {
log.Error(fmt.Sprintf("video librarySearch param err:%v", err))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
in.UID = uid
data, err := in.CheckSearchType()
if err != nil {
log.Error(fmt.Sprintf("video librarySearch err:%v", err))
common.ServeJSON(ctx, stderr.Failure, err)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// ChangeSectionVideoList doc
// @Summary 获取专题下的换一批视频列表
// @Description 专题下的视频列表-换一批
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param sectionID path string true "专题ID"
// @Success 200 {object} vidser.VideoUnderSectionResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/vid/section/changeVideo/:sectionID [get]
func ChangeSectionVideoList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil && !errors.Is(err, common.ErrUserNotExist) {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
in := vidmod.ChangeVideoSectionReq{}
if err = ctx.ShouldBindUri(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if err = ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
data, err := vidser.GetChangeVideos(&in, uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// ChangeSectionMediaList doc
// @Summary 获取专题下的换一批动漫/漫画列表
// @Description 专题下的acg列表-换一批
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param sectionID path string true "专题ID"
// @Success 200 {object} vidser.VideoUnderSectionResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/vid/section/changeMedia/:sectionID [get]
func ChangeSectionMediaList(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil && !errors.Is(err, common.ErrUserNotExist) {
common.ServeJSON(ctx, stderr.ErrNoToken, nil)
return
}
in := vidmod.ChangeVideoSectionReq{}
if err = ctx.ShouldBindUri(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
if err = ctx.ShouldBind(&in); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
data, err := vidser.GetChangeMedias(&in, uid)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// HomeMostNewModuleVideoList doc
// @Summary 首页固定的最新模块下视频数据列表
// @Description 首页固定的最新模块下视频数据列表
// @Tags vid
// @Accept mpfd,json
// @Produce json,html
// @Param q query vidmod.HomeMostNewModuleVideoListReq true "req"
// @Success 200 {object} vidser.VideoUnderSectionResp
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/vid/home/new/list [get]
func HomeMostNewModuleVideoList(ctx *gin.Context) {
in := vidmod.HomeMostNewModuleVideoListReq{}
err := ctx.ShouldBindQuery(&in)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
err = ctx.ShouldBind(&in)
if err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
ua, _ := common.GetUA(ctx)
// 兼容线上参数错误
if ua.SysType == "android" && in.SortType == 4 {
in.SortType = 3
} else if in.SortType == 5 {
in.SortType = 4
}
uid, _ := common.GetUID(ctx)
data, err := vidser.MostNewModuleVideoList(uid, in)
if err != nil {
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
@@ -0,0 +1,43 @@
package video_gold_coin_ctrl
import (
"91jd-server/app/service/video_gold_coin_ser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/v/videogoldcoinmod"
"fmt"
"github.com/gin-gonic/gin"
)
// GetVideoGoldCoin doc
// @Summary 金币视频列表
// @Description 金币视频列表
// @Tags 预售金币视频配置
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData integer true "查询页码"
// @Param pageSize formData integer true "每页条数"
// @Success 200 {object} videogoldcoinmod.QueryVideoGoldCoinRes
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/video_gold_coin/list [get]
func GetVideoGoldCoin(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var in videogoldcoinmod.QueryVideoGoldCoinCond
if err = ctx.ShouldBindQuery(&in); err != nil {
log.Error(fmt.Sprintf("video_gold_coin_ser param error:%v,uid:%v", err, uid))
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := video_gold_coin_ser.GainVideoGoldCoin(uid, &in)
if err != nil {
log.Error(fmt.Sprintf("video_gold_coin_ser GainVideoGoldCoin error:%v,uid:%v", err, uid))
common.ServeJSON(ctx, stderr.Failure, nil)
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
@@ -0,0 +1,42 @@
package videoactivityctrl
import (
"91jd-server/app/service/videoactivityserver"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/v/videoactivitymod"
"github.com/gin-gonic/gin"
)
// List doc
// @Summary 活动视频列表
// @Description 活动详情/作品列表
// @Tags 参赛作品
// @Accept json
// @Produce json
// @Param activityId query string false "活动ID"
// @Param type query int false "榜单类型"
// @Param pageNumber query int true "第几页"
// @Param pageSize query int true "每页数量"
// @Success 200 object videoactivitymod.ListResponse "成功后返回"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /video_activity/list [get]
func List(c *gin.Context) {
uid, err := common.GetUID(c)
if err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
var req videoactivitymod.ListRequest
if err = c.ShouldBindQuery(&req); err != nil {
common.ServeJSON(c, stderr.ErrNoToken, err)
return
}
data, code := videoactivityserver.List(uid, &req)
if code != stderr.Success {
common.ServeJSON(c, code, nil)
return
}
common.ServeJSON(c, code, data)
}
+111
View File
@@ -0,0 +1,111 @@
package vipctrl
import (
"91jd-server/app/service/integral_config_ser"
"91jd-server/common/daichong"
"sync"
"time"
"91jd-server/app/proto"
"91jd-server/app/service/productser"
"91jd-server/app/service/rechargeser"
"91jd-server/app/service/vipcardexperimentser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// Product doc
// @Summary vip种类 新
// @Description 可购买的vip种类
// @Tags vip
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} proto.ProductRes "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/vip/product [get]
func Product(ctx *gin.Context) {
uid, _ := common.GetUID(ctx)
ua, _ := common.GetUA(ctx)
var (
data = proto.ProductRes{
IsNewUser: false,
}
goldResError error
)
var arg struct {
IsNewUser bool `json:"isNewUser" form:"isNewUser"` // 是否新手
}
if err := ctx.ShouldBind(&arg); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, nil)
return
}
wg := sync.WaitGroup{}
wg.Add(3)
common.Go(func() {
defer wg.Done()
countdownSec, _ := productser.CountdownTiroCard(uid)
if countdownSec > 0 {
data.IsNewUser = true
}
if arg.IsNewUser == true {
data.IsNewUser = arg.IsNewUser
}
data.List, goldResError = rechargeser.New_ProductList(uid, ua.SysType, data.IsNewUser, 0)
})
common.Go(func() {
defer wg.Done()
//d, _ := daichongser.NewTakeChat(ctx, uid, 0)
d := daichong.ChatResp{}
data.Daichong = d.Data
})
common.Go(func() {
defer wg.Done()
data.IntegralList = integral_config_ser.GetAllConfig()
})
wg.Wait()
if goldResError != nil {
common.ServeJSON(ctx, stderr.PayBusy, goldResError.Error())
return
}
if err := vipcardexperimentser.ApplyToProductResponse(uid, &data, time.Now()); err != nil {
// 实验是增强能力,查询异常时降级到原套餐列表,不能阻断购买主链路。
log.Warn("vip card experiment fallback to legacy product response", log.Any("uid", uid), log.E(err))
}
common.ServeJSON(ctx, stderr.Success, data)
}
// 获取新手卡倒计时
func CountdownTiroCard(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
countdownSec, err := productser.CountdownTiroCard(uid)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
return
}
common.ServeJSON(ctx, stderr.Success, gin.H{"countdownSec": countdownSec})
}
//// VipRecommend doc
//// @Summary vip推荐展示
//// @Description vip推荐展示
//// @Tags vip
//// @Accept mpfd,json
//// @Produce json,html
//// @Success 200 {object} productmod.Product
//// @Failure 400 {string} json "{"msg": "操作失败"}"
//// @Router /api/app/vip/recommend [get]
//func VipRecommend(ctx *gin.Context) {
// resp, err := productser.GetRecommendVip()
// if err != nil {
// common.ServeJSON(ctx, stderr.ErrDbQueryError, err)
// return
// }
// common.ServeJSON(ctx, stderr.Success, resp)
//}
+115
View File
@@ -0,0 +1,115 @@
package walletctrl
import (
"91jd-server/app/service/walletser"
"91jd-server/common"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/v/txnmod"
"91jd-server/models/v/usermod"
"github.com/gin-gonic/gin"
)
// GetNewBill doc
// @Summary --我的账单新
// @Description 我的账单
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Param year query string true "年"
// @Param month query string true "月"
// @Param pageNumber query int true "页码"
// @Param pageSize query int true "条数"
// @Success 200 {object} txnmod.Bills1Res "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/app/mine/zhangdan [get]
func GetNewBill(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var arg struct {
Month int `form:"month"`
Year int `form:"year"`
commod.Page
}
if err = ctx.ShouldBind(&arg); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, err := walletser.GetBills1(uid, arg.Year, arg.Month, arg.PageNumber, arg.PageSize)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, "")
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// GetIncome doc
// @Summary --收益明细
// @Description 收益明细
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Param startTime query string false "开始时间"
// @Param endTime query string false "结束时间"
// @Param pageNumber formData int true "页码"
// @Param pageSize formData int true "条数"
// @Success 200 {object} txnmod.TransactionLog "{"list": [],"hasNext":false}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/mine/iIncomes [get]
func GetIncome(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err)
return
}
var arg struct {
commod.Page
}
if err = ctx.ShouldBind(&arg); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err)
return
}
data, hasNext, err := txnmod.FindIncome(uid, arg.PageNumber, arg.PageSize)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, "")
return
}
var rechargeIds []uint64
for _, d := range data {
rechargeIds = append(rechargeIds, d.RechargeId)
}
if len(rechargeIds) > 0 {
// 获取用户信息
users, err := usermod.FindUsersMapByUID(rechargeIds)
if err != nil {
common.ServeJSON(ctx, stderr.ErrDbQueryError, "")
return
}
mUser := make(map[uint64]*txnmod.RechargeUserInfo)
if len(users) > 0 {
for _, u := range users {
ru := txnmod.RechargeUserInfo{
UID: u.UID,
Name: u.Name,
Portrait: u.Portrait,
}
mUser[u.UID] = &ru
}
}
for _, d := range data {
if d.RechargeId != 0 {
d.RechargeUser = *mUser[d.RechargeId]
}
}
}
common.ServeJSON(ctx, stderr.Success, gin.H{
"list": data,
"hasNext": hasNext,
})
}
+22
View File
@@ -0,0 +1,22 @@
package walletctrl
import (
"91jd-server/app/appg"
"91jd-server/common/store"
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
)
func Check(ctx *gin.Context, param interface{}) (err error) {
b, _ := json.Marshal(param)
sign, err := store.Encrypt(b, appg.Conf.Base.StoreEncryptKey)
if err != nil {
return
}
headerSign := ctx.Request.Header.Get("sign")
if sign != headerSign {
return errors.New("校验失败")
}
return nil
}
+48
View File
@@ -0,0 +1,48 @@
package walletctrl
import (
"91jd-server/app/service/ai_undress_server"
"91jd-server/app/service/aiser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/v/walletmod"
"github.com/gin-gonic/gin"
)
// GetWalletAmount doc
// @Summary 获取钱包信息
// @Description 获取钱包信息
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {object} walletmod.Wallet "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /mine/wallet [get]
func GetWalletAmount(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
// 从AI女友页面返回后,钱包查询会先尝试把第三方剩余余额下分回主钱包。
// 下分失败只记录告警,钱包接口仍返回当前本地余额。
if err = aiser.TrySettleDown(ctx.Request.Context(), uid); err != nil {
log.Warn("wallet AI girlfriend settlement failed", log.Any("uid", uid), log.E(err))
}
w, err := walletmod.GetWallet(uid)
if err != nil {
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err.Error())
return
}
resp := &walletmod.WalletResp{}
// 当日剩余ai免费脱衣次数
if w != nil {
resp.Wallet = *w
resp.TodayAiFreeTimes = ai_undress_server.GetTodayRemainingAiFreeUndressTimes(*w)
}
common.ServeJSON(ctx, stderr.Success, resp)
}
+58
View File
@@ -0,0 +1,58 @@
package withdrawctrl
import (
"bytes"
"errors"
"fmt"
"net/http"
"strconv"
"91jd-server/app/service/withdrawser"
"91jd-server/common/log"
"91jd-server/common/rchgutil"
"github.com/gin-gonic/gin"
)
// GoldFishCallBack 金鱼结构回调函数
func GoldFishCallBack(ctx *gin.Context) {
if err := func() error {
exchg := rchgutil.GoldFishExchangeRes{}
if err := ctx.ShouldBindJSON(&exchg); err != nil {
log.Error(fmt.Sprintf("GoldFish callback parameter bind fail error:%+v:", err))
return err
}
e := rchgutil.GoldFishExchange{}
log.Info(fmt.Sprintf("GoldFish callback parameter data:%+v:", exchg))
buf := bytes.Buffer{}
buf.WriteString(strconv.Itoa(exchg.Code))
buf.WriteString(exchg.Mark)
buf.WriteString(exchg.MercID)
buf.WriteString(exchg.Money)
buf.WriteString(exchg.OID)
buf.WriteString(exchg.Status)
buf.WriteString(exchg.TradeNo)
buf.WriteString(e.GetAppSecret())
if !rchgutil.VerifySign(exchg.Sign, buf.String()) {
log.Error("GoldFish callback sign verify fail")
return errors.New("check sign fail")
}
moneyf, err := strconv.ParseFloat(exchg.Money, 64)
if err != nil {
log.Error(fmt.Sprintf("GoldFish callback ParseFloat fail error:%+v:", err))
return err
}
moneyf = moneyf * 100
money := int64(moneyf)
exchg.TradeNo = rchgutil.EChgIDDisassemble(exchg.TradeNo)
if err = withdrawser.ExchgCallBack(exchg.TradeNo, exchg.OID, money, exchg.Status, exchg.Mark); err != nil {
log.Error(fmt.Sprintf("GoldFish RechargeCallBack fail error:%+v:", err))
return err
}
return nil
}(); err != nil {
ctx.String(http.StatusBadRequest, "fail")
return
}
ctx.String(http.StatusOK, "success")
}
+201
View File
@@ -0,0 +1,201 @@
package withdrawctrl
import (
"91jd-server/app/service/withdrawser"
"91jd-server/common"
"91jd-server/common/log"
"91jd-server/common/stderr"
"91jd-server/models/commod"
"91jd-server/models/v/txnactmod"
"91jd-server/models/v/usermod"
"91jd-server/models/v/wdordmod"
"github.com/gin-gonic/gin"
)
// Withdraw doc
// @Summary 提现
// @Description 提现
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Param money formData int true "提现金额"
// @Param payType formData string true "提现方式(alipay,bankcard)"
// @Param withdrawType formData int true "提现类型(0代理提现/1金币提现)"
// @Param actName formData string true "交易账户持有人"
// @Param act formData string true "交易账户"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /withdraw [post]
func Withdraw(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
p := wdordmod.WithdrawReq{}
if err = ctx.ShouldBind(&p); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
//类型校验
if p.PayType != wdordmod.TypeBankCard && p.PayType != wdordmod.TypeAlipay && p.PayType != wdordmod.TypeUsdt {
common.ServeJSON(ctx, stderr.ExchangeCodeInvalid, err.Error())
return
}
w := wdordmod.WithdrawOrder{
UID: uid,
UserIP: common.GetIP(ctx),
Money: p.Money,
PayType: p.PayType,
ActName: p.ActName,
Act: p.Act,
ProductType: p.ProductType,
}
if p.PayType == wdordmod.TypeBankCard {
user, err := usermod.FindUserByUID(uid)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err)
return
}
if user.BankActName != "" && user.BankActName != p.ActName {
common.ServeJSON(ctx, stderr.DifferentBankActName, err)
return
}
typeAct := txnactmod.Bank
act, err := txnactmod.FindOne(txnactmod.Query{
Act: &(p.Act),
AType: &typeAct,
})
if err != nil {
log.Error("txnactmod.FindOne", log.Any("req", p), log.E(err))
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, err)
return
}
if act.ID.IsZero() {
log.Warn("zero act id", log.Any("req", p), log.Any("act", act))
common.ServeJSON(ctx, stderr.ErrNetWorkBusy, "")
return
}
w.BankCode = act.BankCode
}
if p.Name != nil {
w.Name = *p.Name
}
if p.DevID != nil {
w.DevID = *p.DevID
}
if p.WithdrawType != nil {
w.WithdrawType = *p.WithdrawType
} else {
w.WithdrawType = 1
}
if err = withdrawser.WithDraw(&w); err != nil {
switch err.Error() {
case "Insufficient balance":
log.Error("WithDraw Insufficient balance", log.Any("error==>", err.Error()))
common.ServeJSON(ctx, stderr.InsufficientBalance, err.Error())
return
case "PayType Insufficient balance":
log.Error("WithDraw PayType Insufficient balance", log.Any("error==>", err.Error()))
common.ServeJSON(ctx, stderr.InsufficientBalance, err.Error())
return
case "illegal withdraw amount":
log.Error("Withdraw illegal withdraw amount", log.Any("order", w), log.E(err))
common.ServeJSON(ctx, stderr.WithDrawIllegalAmount, err.Error())
return
default:
common.ServeJSON(ctx, stderr.ErrDbQueryError, err.Error())
return
}
}
common.ServeJSON(ctx, stderr.Success, nil)
}
// WithdrawOrder doc
// @Summary 查看提现订单
// @Description 查看提现订单
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Param pageNumber formData int true "页码"
// @Param pageSize formData int true "条数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /withdraw/order [get]
func WithdrawOrder(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
var arg struct {
commod.Page
}
if err = ctx.ShouldBind(&arg); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
total, data, hasNext, err := wdordmod.FindOrdersByUID(uid, arg.PageNumber, arg.PageSize)
if err != nil {
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, map[string]interface{}{
"total": total,
"result": data,
"list": data,
"hasNext": hasNext,
})
}
// GetType doc
// @Summary 提现方式获取
// @Description
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Param payType formData string true "提现方式(银行卡/支付宝)"
// @Param pageSize formData int true "条数"
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /app/withdraw/type [get]
func GetType(ctx *gin.Context) {
uid, err := common.GetUID(ctx)
if err != nil {
common.ServeJSON(ctx, stderr.ErrNoToken, err.Error())
return
}
var arg struct {
Type string `json:"payType" form:"payType"`
}
if err := ctx.ShouldBind(&arg); err != nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
u, err := usermod.FindUserByUID(uid)
if err != nil || u == nil {
common.ServeJSON(ctx, stderr.ErrParamError, err.Error())
return
}
data, err := withdrawser.WithdrawType(u.SnapVip, arg.Type)
if err != nil {
common.ServeJSON(ctx, stderr.ErrServerUnavailable, err.Error())
return
}
common.ServeJSON(ctx, stderr.Success, data)
}
// GetWithdrawCfg doc
// @Summary 提现配置获取
// @Description
// @Tags 钱包
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /withdraw/cfg [get]
func GetWithdrawCfg(ctx *gin.Context) {
data := withdrawser.GetWithdrawCfg()
common.ServeJSON(ctx, stderr.Success, data)
}
+40
View File
@@ -0,0 +1,40 @@
package withdrawctrl
import (
"fmt"
"net/http"
"91jd-server/app/service/withdrawser"
"91jd-server/common/log"
"91jd-server/common/rchgutil"
"91jd-server/common/stderr"
"github.com/gin-gonic/gin"
)
// YinseCallBack
func YinseCallBack(ctx *gin.Context) {
name := ctx.Param("name")
if name == "goldfish" {
GoldFishCallBack(ctx)
return
}
ctx.JSON(http.StatusOK, func() (msg rchgutil.ExchgBackMsg) {
exchg := rchgutil.ExchgBack{}
msg.Code = int(stderr.Failure)
if err := ctx.ShouldBindJSON(&exchg); err != nil {
msg.Err = fmt.Sprintf("GoldFish callback parameter bind fail error:%+v:", err)
log.Error(msg.Err)
return
}
log.Info(fmt.Sprintf("GoldFish callback parameter data:%+v:", exchg))
exchg.TradeNo = rchgutil.EChgIDDisassemble(exchg.TradeNo)
if err := withdrawser.ExchgCallBack(exchg.TradeNo, exchg.OID, exchg.Money, exchg.Status, exchg.Mark); err != nil {
msg.Err = fmt.Sprintf("GoldFish RechargeCallBack fail error:%+v:", err)
log.Error(msg.Err)
return
}
msg.Code = int(stderr.Success)
return
}())
}
+14
View File
@@ -0,0 +1,14 @@
package appg
import (
"errors"
"strings"
)
// AICallbackURL returns the externally reachable Web callback URL for AI jobs.
func AICallbackURL(path string) (string, error) {
if Conf == nil || strings.TrimSpace(Conf.URL.AiImageToVideoCallbackUrl) == "" {
return "", errors.New("ai callback url is not configured")
}
return strings.TrimRight(strings.TrimSpace(Conf.URL.AiImageToVideoCallbackUrl), "/") + "/" + strings.TrimLeft(path, "/"), nil
}
+231
View File
@@ -0,0 +1,231 @@
package appg
import (
"91jd-server/common/constant"
"91jd-server/common/db"
"91jd-server/common/elastic"
"91jd-server/common/email"
"91jd-server/common/httputil"
"91jd-server/common/laosiji_app"
"91jd-server/common/redis"
"91jd-server/common/sms"
"91jd-server/common/tg"
)
var (
Conf *GlobalConfig
Static *StaticSource
Name *NameSource
Redis *redis.Client
VideoDB *db.MongoDB
LogDB *db.MongoDB
StatDB *db.MongoDB
Bot *tg.Bot
VideoES *elastic.Client // ES搜索
ProxyCfg *httputil.ProxyCfg
ImAuth string //im token
)
// ShouldEnforceIPRateLimit reports whether IP-based request restrictions should
// be applied. The shared test environment intentionally skips these checks so
// NAT traffic from testers cannot block login, registration, SMS, or comments.
func ShouldEnforceIPRateLimit() bool {
return Conf == nil || Conf.Base.Env != constant.TestRunmod
}
// H265URLForApp 提供统一的 App H.265 熔断开关。
// 未配置或配置为 true 时返回原地址;显式为 false 时所有 App 接口隐藏地址。
func H265URLForApp(rawURL string) string {
if Conf != nil && Conf.Hevc.EnableApp != nil && !*Conf.Hevc.EnableApp {
return ""
}
return rawURL
}
type StaticSource struct {
Portrait []string `json:"portrait"`
SysUser []uint64 `json:"sysUser"`
TagFilter []string `json:"tagFilter"`
VerifyCodeForFake string `json:"verifyCodeForFake"` //马甲号验证码
}
type NameSource struct {
FamilyNames []string `json:"familyNames"`
MiddleNames []string `json:"middleNames"`
AfterNames []string `json:"afterNames"`
}
type ReplayAttackConfig struct {
Enable bool `json:"enable"`
Key string `json:"key"`
WindowDurationSeconds int64 `json:"windowDurationSeconds"`
}
type GlobalConfig struct {
Base struct {
Port int64 `json:"port"`
PProf bool `json:"pprof"`
Env string `json:"env"`
JwtKey string `json:"jwtKey"`
ResKey string `json:"resKey"`
ResKey360 string `json:"resKey360"`
DevIDSignKey string `json:"devIDSignKey"`
WebJwtKey string `json:"webJwtKey"`
ReplayAttack ReplayAttackConfig `json:"replayAttack"`
DemarcatAmout uint64 `json:"demarcatAmout"`
TotalWatch uint64 `json:"totalWatch"`
StoreEncryptKey string `json:"storeEncryptKey"`
// H5M3u8TicketKey H5 m3u8 播放防盗链票据的服务端密钥(建议 openssl rand -base64 32)。
// 留空时保持旧逻辑(不签票、不验票),便于先发布代码再由运维配置密钥开启严格模式。
H5M3u8TicketKey string `json:"h5M3u8TicketKey"`
// 媒体分片(TS)鉴权签名密钥对象(含 keyVersion/key);未配置时回退内置默认(版本 default + constant.MediaSourceAuthKey)
TsAuth constant.TsAuthKeyConfig `json:"tsAuth"`
} `json:"base"`
Log struct {
Level string `json:"level"`
DisableStacktrace bool `json:"disableStacktrace"`
} `json:"log"`
MonitorReport struct {
GroupId string `json:"groupId"`
SecretKey string `json:"secretKey"`
} `json:"monitorReport"`
Kefu struct {
AppID string `json:"appId"`
AppSecret string `json:"appSecret"`
PlatName string `json:"platName"`
Theme string `json:"theme"`
} `json:"kefu"`
DataReport struct {
Url string `json:"url"`
AppUrl string `json:"appUrl"`
} `json:"dataReport"`
DaiChong struct {
AppID string `json:"appId"`
AppSecret string `json:"appSecret"`
PlatName string `json:"platName"`
Domain string `json:"domain"`
UploadUrl string `json:"uploadUrl"`
H5domain string `json:"h5domain"`
H5domainQP string `json:"h5domainQP"`
} `json:"daichong"`
Customer struct {
Url string `json:"url"`
AppId string `json:"appId"` // 客服中心给的appid
Secret string `json:"secret"`
} `json:"customer"`
PayCenter struct {
AppName string `json:"appName"` // APP名称
CallbackUrl string `json:"callbackUrl"` // 回调地址(APP服务地址)
ApiUrl string `json:"apiUrl"` // 新支付中心地址
MercID string `json:"mercID"` // 商户ID
MercSecret string `json:"mercSecret"` // 商户密钥
} `json:"payCenter"`
Im struct {
AppID string `json:"appId"`
AppName string `json:"appName"`
AppSecret string `json:"appSecret"`
AppJwtKey string `json:"appJwtKey"`
} `json:"im"`
ImV2 struct {
BaseURL string `json:"baseUrl"`
DynamicConfigDomain string `json:"dynamicConfigDomain"`
SocketURL string `json:"socketUrl"`
MerchantCode string `json:"merchantCode"`
TenantCode string `json:"tenantCode"`
AppKey string `json:"appKey"`
ClientID string `json:"clientId"`
ClientSecret string `json:"clientSecret"`
} `json:"imv2"`
Elastic struct {
VideoUrl string `json:"videoUrl"`
IsActive bool `json:"isActive"`
UserName string `json:"userName"`
PassWord string `json:"passWord"`
} `json:"elastic"`
Mongo struct {
VideoDbUrl string `json:"videoDbUrl"` //主数据库 存放强业务相关的数据
StatDbUrl string `json:"statDbUrl"` //统计数据库 存放统计 日志 渠道相关的数据
LogDbUrl string `json:"logDbUrl"` //日志数据库
LogReadDBUrl string `json:"logDbUrlRead"` //读日志数据库
} `json:"mongo"`
Redis struct {
URL string `json:"url"`
} `json:"redis"`
ShortRecommend ShortRecommendConfig `json:"shortRecommend"`
Game struct {
URL string `json:"url"`
MercId int `json:"mercId"`
} `json:"game"`
URL struct {
ImCenterUrl string `json:"imCenterUrl"`
OriginUrl string `json:"originUrl"`
JH1BOriginUrl string `json:"jH1BOriginUrl"`
LaoSiJiOriginUrl string `json:"laoSiJiOriginUrl"`
LaoSiJiTSCdn string `json:"laoSiJiTSCdn"`
UploadUrl string `json:"uploadUrl"`
UploadImgUrl string `json:"uploadImgUrl"`
SendSingleFile string `json:"sendSingleFile"`
SendBatchFile string `json:"sendBatchFile"`
FileInfoUrl string `json:"fileInfoUrl"`
CoverInfoUrl string `json:"coverInfoUrl"`
LocalWebUrl string `json:"localWebUrl"`
CdnUrl string `json:"cdnUrl"`
HttpProxy string `json:"httpProxy"`
TransactionUrl string `json:"transactionUrl"` // 支付下单地址
DataCentreURL string `json:"dataCentreUrl"` // 数据中心
ApiUrls []string `json:"apiUrls"` // api域名
LouFengH5 string `json:"louFengH5"` // 楼凤H5(金主特制)
DataBuriedPoint string `json:"dataBuriedPoint"` // 数据埋点
ProductUrl string `json:"productUrl"` // 产品中心
//Active2023 string `json:"active2023"` // 2023新春抽奖地址
NavigateUrl string `json:"navigateUrl"` // 导航站地址
HlH5Url string `json:"hlH5Url"` // 黑料h5url
LuckyDrawH5 string `json:"luckyDrawH5"` // 抽奖H5链接
StoreUrl string `json:"storeUrl"` // 原味商城url
AiServer string `json:"aiServer"` // ai订单请求地址
AiUndressServer string `json:"aiUndressServer"` // AI图片脱衣服务地址
AiImageToVideoUrl string `json:"aiImageToVideoUrl"` // AI图生视频请求地址
AiImageToVideoCallbackUrl string `json:"aiImageToVideoCallbackUrl"` // AI图生视频回调地址
AIMateH5 string `json:"aiMateH5"` // AI伴侣链接
} `json:"url"`
Limit struct {
GlobalLimit int64 `json:"globalLimit"` // 全局接口访问频率限制
CommentLimit int64 `json:"commentLimit"` // 评论接口限制
RechargeLimit int64 `json:"rechargeLimit"` // 充值接口限制
LoginLimit int64 `json:"loginLimit"` // 登陆注册接口限制
SMSIPLimit int64 `json:"smsIPLimit"` // 短信验证码 IP 频率限制(次/自然日)
} `json:"limit"`
Hevc struct {
EnableApp *bool `json:"enableApp"` // false 时隐藏 App 响应里的 H265 播放地址;未配置时默认返回
PullSecret string `json:"pullSecret"` // SKD 与 App 共享的 H265 拉流签名密钥,至少 32 个随机字节
} `json:"hevc"`
CmtLimit struct {
CmtLimitExpire int64 `json:"cmtLimitExpire"` // 发表评论限制过期时间(秒)
CommentCount int64 `json:"commentCount"` // 发表评论次数限制
} `json:"cmtLimit"`
HttpOptions struct {
DcCtxTimeOut uint64 `json:"dcCtxTimeOut"` //代充http请求 超时ctx取消时间
} `json:"httpOptions"`
AWS struct {
SES email.Config `json:"ses"`
} `json:"aws"`
Kafka struct {
Url string `yaml:"url"`
Group string `yaml:"group"`
} `yaml:"kafka"`
AdCenter struct {
ApiDomain string `json:"apiDomain"` // api地址 如:https://adopenapi.adcs01.top
MerchantCode string `json:"merchantCode"` // 商户 code
AppCode string `json:"appCode"` // 应用 code
AesKey string `json:"aesKey"` // 数据解密KEY
} `json:"adCenter"`
RandomBanner string `json:"randomBanner"` // 随机banner
AiFreeUnDressDailyLimit int `json:"aiFreeUnDressDailyLimit"` // AI免费脱衣每日限额
SMS sms.Config `json:"sms"`
ActivityServer struct {
AppId string `json:"appId"` // 活动服应用ID
SecretKey string `json:"secretKey"` // 活动服回调签名密钥(Base64编码)
ApiUrl string `json:"apiUrl"` // 活动服API地址,用于拉取域名等
} `json:"activityServer"`
LaoSiJiAiMate laosiji_app.Config `json:"laoSiJiAiMate"` // 老司机AI女友V2第三方配置
}
+24
View File
@@ -0,0 +1,24 @@
package appg
import (
"os"
"91jd-server/common/elastic"
"91jd-server/common/log"
)
func InitElastic() {
opts := elastic.Options{
Address: []string{Conf.Elastic.VideoUrl},
MaxIdleConnsPerHost: 10,
IdleConnTimeout: 30,
UserName: Conf.Elastic.UserName,
PassWord: Conf.Elastic.PassWord,
}
c, err := elastic.InitElastic(opts)
if err != nil {
log.Error("startUp VideoES InitElastic error", log.E(err))
os.Exit(1)
}
VideoES = c
}

Some files were not shown because too many files have changed in this diff Show More