Files
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

580 lines
17 KiB
Go

package statcenterctl
import (
"fmt"
"math"
"net/http"
"strings"
"91porn-server/common"
"91porn-server/common/log"
"91porn-server/common/stderr"
"91porn-server/models/commod"
"91porn-server/models/l/visitlogmod"
"91porn-server/models/v/prdcthsomod"
"91porn-server/models/v/productmod"
"91porn-server/models/v/productposimod"
"91porn-server/models/v/proxymod"
"91porn-server/models/v/rchgordmod"
"91porn-server/models/v/txnmod"
"91porn-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
}