Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
package imctrl
import (
"strings"
"91porn-server/app/service/imadser"
"91porn-server/common"
"91porn-server/common/enum/imad"
"91porn-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
}
+435
View File
@@ -0,0 +1,435 @@
package imctrl
import (
"fmt"
"91porn-server/app/service/customerser"
"net/http"
"91porn-server/app/middleware/authuser"
"91porn-server/app/service/imser"
"91porn-server/app/service/messageser"
"91porn-server/common"
"91porn-server/common/imclient"
"91porn-server/common/log"
"91porn-server/common/stderr"
"91porn-server/common/truthutil"
"91porn-server/models/v/messagemod"
"91porn-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签名
// @Tags IM
// @Accept mpfd,json
// @Produce json,html
// @Success 200 {string} json "{"msg": "操作成功"}"
// @Failure 400 {string} json "{"msg": "操作失败"}"
// @Router /api/im/sign [get]
func GetImSign(ctx *gin.Context) {
token := ctx.Request.Header.Get("Authorization") //为兼容H5带APP token
if token == "" {
ctx.AbortWithStatusJSON(http.StatusOK, stderr.ErrNoToken.Struct())
return
}
claims, err := authuser.ParseToken(token)
if err != nil {
log.Error("GetImSign ParseToken error", log.Any("token", fmt.Sprintf("%+v", token)), log.E(err))
ctx.JSON(http.StatusBadRequest, stderr.InvalidToken.Struct())
return
}
ua, _ := common.GetUA(ctx)
if !sourcemod.GetCustomerStat() {
common.ServeJSON(ctx, stderr.ErrCustomerBanned, nil)
return
}
sign := imser.GetSign(claims.UID, ua)
common.ServeJSON(ctx, stderr.Success, sign)
}
// 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)
}
// ImSign 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/newSign [get]
func ImSign(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))
} else {
uid = claims.UID
}
}
if uid != 0 {
sign = imser.GetSignNew(uid, ua)
} else if sign == "" {
sign = imser.GetWhiteSignNew()
}
common.ServeJSON(ctx, stderr.Success, gin.H{
"sign": sign,
"faq": faqURL,
"check": checkURL,
"isVoiceActive": true,
})
}
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)
}