@@ -0,0 +1,573 @@
|
||||
package activityser
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"91porn-server/app/appg"
|
||||
"91porn-server/app/proto"
|
||||
"91porn-server/app/service/activityclient"
|
||||
"91porn-server/app/service/customerser"
|
||||
"91porn-server/app/service/productser"
|
||||
"91porn-server/app/service/rechargeser"
|
||||
"91porn-server/common"
|
||||
"91porn-server/common/constant"
|
||||
"91porn-server/common/crypt"
|
||||
"91porn-server/common/log"
|
||||
"91porn-server/common/rchgutil"
|
||||
"91porn-server/common/stderr"
|
||||
"91porn-server/middleware/ua"
|
||||
"91porn-server/models/commod"
|
||||
"91porn-server/models/v/advanceordermod"
|
||||
"91porn-server/models/v/currencymod"
|
||||
"91porn-server/models/v/productmod"
|
||||
"91porn-server/models/v/rchgamtmod"
|
||||
"91porn-server/models/v/sourcemod"
|
||||
"91porn-server/models/v/usermod"
|
||||
"91porn-server/models/v/walletmod"
|
||||
|
||||
"go.mongodb.org/mongo-driver/bson"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// PayChannel 支付渠道(精简字段)
|
||||
type PayChannel struct {
|
||||
Type string `json:"type"`
|
||||
TypeName string `json:"typeName"`
|
||||
}
|
||||
|
||||
// ActivityCurrencyItem 活动服金币充值列表项
|
||||
type ActivityCurrencyItem struct {
|
||||
ID primitive.ObjectID `json:"id"`
|
||||
Coin int64 `json:"coin"`
|
||||
Money int64 `json:"money"`
|
||||
Name string `json:"typeName"`
|
||||
GiveCoin int64 `json:"giveCoin"`
|
||||
RechargeType []PayChannel `json:"rechargeType"`
|
||||
}
|
||||
|
||||
func toPayChannels(src []rchgamtmod.PayChannelRes) []PayChannel {
|
||||
channels := make([]PayChannel, 0, len(src))
|
||||
for _, ch := range src {
|
||||
channels = append(channels, PayChannel{
|
||||
Type: ch.Type,
|
||||
TypeName: ch.TypeName,
|
||||
})
|
||||
}
|
||||
return channels
|
||||
}
|
||||
|
||||
// GetCurrencyList 获取金币充值列表
|
||||
func GetCurrencyList(ctx context.Context) ([]*ActivityCurrencyItem, stderr.Code) {
|
||||
currencys, err := currencymod.List(commod.Gold)
|
||||
if err != nil {
|
||||
log.ErrorX(ctx, "活动服-获取金币列表异常", log.E(err))
|
||||
return nil, stderr.ErrNetWorkBusy
|
||||
}
|
||||
|
||||
removeRepeat := make(map[int64]struct{})
|
||||
var moneys []string
|
||||
for _, cur := range currencys {
|
||||
if _, ok := removeRepeat[cur.Price]; ok {
|
||||
continue
|
||||
}
|
||||
removeRepeat[cur.Price] = struct{}{}
|
||||
moneys = append(moneys, rechargeser.FenToYuan(cur.Price))
|
||||
}
|
||||
|
||||
payReq := rchgutil.GainPayTypeReq{Money: moneys}
|
||||
bc, err := payReq.GetPayType()
|
||||
if err != nil {
|
||||
log.ErrorX(ctx, "活动服-获取支付通道列表异常", log.E(err))
|
||||
return nil, stderr.ErrNetWorkBusy
|
||||
}
|
||||
|
||||
data := make([]*ActivityCurrencyItem, 0, len(currencys))
|
||||
for _, v := range currencys {
|
||||
if v.Price < 100 {
|
||||
continue
|
||||
}
|
||||
rechargeType := rechargeser.GetPayChannelDetails_New(v.Price, bc, 0)
|
||||
if len(rechargeType) <= 0 {
|
||||
continue
|
||||
}
|
||||
data = append(data, &ActivityCurrencyItem{
|
||||
ID: v.ID,
|
||||
Coin: v.Coins,
|
||||
Money: v.Price,
|
||||
Name: v.Name,
|
||||
GiveCoin: v.GiveGold,
|
||||
RechargeType: toPayChannels(rechargeType),
|
||||
})
|
||||
}
|
||||
return data, stderr.Success
|
||||
}
|
||||
|
||||
// CardCategory 会员卡分类
|
||||
type CardCategory int
|
||||
|
||||
const (
|
||||
DefaultCard CardCategory = 0
|
||||
PreSaleCard CardCategory = 1
|
||||
NewbieCard CardCategory = 2
|
||||
)
|
||||
|
||||
// VipCardInfo 活动服会员卡信息
|
||||
type VipCardInfo struct {
|
||||
Id string `json:"id"`
|
||||
Category CardCategory `json:"category"`
|
||||
VipGrade string `json:"vipGrade"`
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
PreMoney int64 `json:"preMoney"`
|
||||
Money int64 `json:"money"`
|
||||
CanUseCoin bool `json:"canUseCoin"`
|
||||
DayCount int64 `json:"dayCount"`
|
||||
GiveCoin int64 `json:"giveCoin"`
|
||||
Sort int32 `json:"sort"`
|
||||
Image string `json:"image"`
|
||||
Rights []RightInfo `json:"rights"`
|
||||
RechargeType []PayChannel `json:"rechargeType"`
|
||||
ActivityStartAt time.Time `json:"activityStartAt"`
|
||||
ActivityEndAt time.Time `json:"activityEndAt"`
|
||||
CanUpgrade bool `json:"canUpgrade"`
|
||||
PreSaleStep int `json:"preSaleStep"` // 预售卡阶段 0-预付阶段 1-尾款阶段 2-尾款付款完成(结束)
|
||||
}
|
||||
|
||||
// RightInfo 权益信息
|
||||
type RightInfo struct {
|
||||
Name string `json:"name"`
|
||||
Desc string `json:"desc"`
|
||||
Image string `json:"image"`
|
||||
}
|
||||
|
||||
// VipDeduct 会员卡当前可用抵扣(vip/list 按抵扣后金额匹配支付通道用)
|
||||
type VipDeduct struct {
|
||||
ProductID string // 会员卡ID
|
||||
DeductAmount int64 // 券面额(分)
|
||||
}
|
||||
|
||||
// GetProductList 获取会员卡列表。
|
||||
// deducts 非空时,对携带抵扣的普通会员卡按"抵扣后有效金额"重新匹配支付通道(rechargeType),
|
||||
// money/preMoney 仍返回原价(避免与下单预览重复扣减)。
|
||||
func GetProductList(ctx context.Context, uid uint64, deducts []VipDeduct) ([]VipCardInfo, error) {
|
||||
u, err := usermod.FindUserByUID(uid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询用户异常: %w", err)
|
||||
}
|
||||
if u == nil {
|
||||
return nil, fmt.Errorf("用户不存在: %d", uid)
|
||||
}
|
||||
|
||||
productList, err := rechargeser.New_ProductList(uid, u.SysType, true, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 抵扣券:按抵扣后有效金额预匹配支付通道(命中的卡覆盖 rechargeType)
|
||||
deductedChans := buildDeductedRechargeTypes(productList, deducts)
|
||||
|
||||
var cards []VipCardInfo
|
||||
for _, group := range productList {
|
||||
for _, item := range group.List {
|
||||
p := item.Product
|
||||
card := VipCardInfo{
|
||||
Id: p.ID.Hex(),
|
||||
VipGrade: fmt.Sprintf("%d", p.VipLevel),
|
||||
Name: p.Name,
|
||||
Desc: p.Desc,
|
||||
PreMoney: p.OriginalPrice * 10,
|
||||
Money: p.DiscountedPrice * 10,
|
||||
CanUseCoin: p.IsAmountPay,
|
||||
DayCount: int64(p.Duration),
|
||||
GiveCoin: p.GiveCoin,
|
||||
Sort: int32(p.Sort),
|
||||
Image: p.BGImg,
|
||||
CanUpgrade: p.IsUpgrade,
|
||||
}
|
||||
|
||||
switch p.ProductType {
|
||||
case commod.AdvanceCard:
|
||||
card.Category = PreSaleCard
|
||||
advOrder, _ := advanceordermod.IsExist(bson.M{"productID": p.ID, "uid": uid})
|
||||
// advOrder.Status 取值(advanceordermod):
|
||||
// 0 DEFAULT / 1 AdvanceProcessing(预付中) / 2 AdvanceSUCCESS(预付成功)
|
||||
// 3 BalanceProcessing(尾款中) / 4 BalanceSUCCESS(尾款成功)
|
||||
if advOrder == nil || advOrder.Status == advanceordermod.DEFAULT || advOrder.Status == advanceordermod.AdvanceProcessing {
|
||||
// 预付阶段:尚未下单 / 预付中
|
||||
card.PreSaleStep = 0
|
||||
card.ActivityStartAt = p.ActivityTime
|
||||
card.ActivityEndAt = p.EndTime
|
||||
card.Money = p.AdvanceAmount * 10
|
||||
} else if advOrder.Status == advanceordermod.AdvanceSUCCESS || advOrder.Status == advanceordermod.BalanceProcessing {
|
||||
// 尾款阶段:预付成功 / 尾款支付中
|
||||
card.PreSaleStep = 1
|
||||
card.ActivityStartAt = p.StartTime
|
||||
card.ActivityEndAt = p.EndTime
|
||||
card.Money = p.BalanceAmount * 10
|
||||
card.Image = p.NewBgImg
|
||||
} else if advOrder.Status == advanceordermod.BalanceSUCCESS {
|
||||
// 尾款付款完成(结束)
|
||||
card.PreSaleStep = 2
|
||||
}
|
||||
case commod.NEWUSERCard:
|
||||
card.Category = NewbieCard
|
||||
card.ActivityStartAt = u.CreatedAt
|
||||
card.ActivityEndAt = card.ActivityStartAt.Add(time.Duration(p.ShowCountdownTime) * time.Hour)
|
||||
if time.Now().After(card.ActivityEndAt) {
|
||||
continue
|
||||
}
|
||||
default:
|
||||
card.Category = DefaultCard
|
||||
}
|
||||
|
||||
if card.Image == "" {
|
||||
card.Image = p.BGImg
|
||||
}
|
||||
|
||||
rights := make([]RightInfo, 0, len(p.NewPrivilege))
|
||||
for _, pr := range p.NewPrivilege {
|
||||
rights = append(rights, RightInfo{
|
||||
Name: pr.Name,
|
||||
Desc: pr.Desc,
|
||||
Image: pr.Image,
|
||||
})
|
||||
}
|
||||
card.Rights = rights
|
||||
card.RechargeType = toPayChannels(item.RechargeType)
|
||||
// 抵扣券:按抵扣后金额匹配的支付通道覆盖原价通道
|
||||
if chans, ok := deductedChans[p.ID.Hex()]; ok {
|
||||
card.RechargeType = chans
|
||||
}
|
||||
|
||||
cards = append(cards, card)
|
||||
}
|
||||
}
|
||||
return cards, nil
|
||||
}
|
||||
|
||||
// minDeductPayFen 抵扣后最低实付(分),与下单折价封顶保持一致
|
||||
const minDeductPayFen = 1
|
||||
|
||||
// buildDeductedRechargeTypes 依据 deducts 计算各会员卡"抵扣后有效金额"对应的支付通道。
|
||||
// 支付通道按支付金额匹配(如微信要求达标金额),券抵扣后实付变小可能改变可用通道,
|
||||
// 故对携带抵扣的普通会员卡(非预售卡)按 min(券面额, 原价-最低实付) 折后金额重新匹配 rechargeType。
|
||||
// 返回 productIdHex -> 支付通道;deducts 为空或无命中时返回 nil,调用方保留原价通道。
|
||||
func buildDeductedRechargeTypes(productList []proto.ProductList, deducts []VipDeduct) map[string][]PayChannel {
|
||||
deductMap := make(map[string]int64, len(deducts))
|
||||
for _, d := range deducts {
|
||||
if d.ProductID != "" && d.DeductAmount > 0 {
|
||||
deductMap[d.ProductID] = d.DeductAmount
|
||||
}
|
||||
}
|
||||
if len(deductMap) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
type effEntry struct {
|
||||
hex string
|
||||
effFen int64
|
||||
}
|
||||
var entries []effEntry
|
||||
moneySet := make(map[string]struct{})
|
||||
var moneys []string
|
||||
for _, group := range productList {
|
||||
for _, item := range group.List {
|
||||
p := item.Product
|
||||
if p.ProductType == commod.AdvanceCard {
|
||||
continue // 预售卡分阶段付款,不参与抵扣
|
||||
}
|
||||
deduct, ok := deductMap[p.ID.Hex()]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
originFen := p.DiscountedPrice * 10
|
||||
maxDeduct := originFen - minDeductPayFen
|
||||
if maxDeduct <= 0 {
|
||||
continue
|
||||
}
|
||||
if deduct > maxDeduct {
|
||||
deduct = maxDeduct
|
||||
}
|
||||
effFen := originFen - deduct
|
||||
entries = append(entries, effEntry{hex: p.ID.Hex(), effFen: effFen})
|
||||
y := rechargeser.FenToYuan(effFen)
|
||||
if _, exist := moneySet[y]; !exist {
|
||||
moneySet[y] = struct{}{}
|
||||
moneys = append(moneys, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
bc, err := (&rchgutil.GainPayTypeReq{Money: moneys}).GetPayTypeFromCache()
|
||||
if err != nil {
|
||||
log.Warn("活动服-抵扣后支付通道查询失败", log.Any("moneys", moneys), log.E(err))
|
||||
}
|
||||
out := make(map[string][]PayChannel, len(entries))
|
||||
for _, e := range entries {
|
||||
out[e.hex] = toPayChannels(rechargeser.GetPayChannelDetails_New(e.effFen, bc, 0))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// UserBalance 用户余额信息
|
||||
type UserBalance struct {
|
||||
Gold int64 `json:"gold"` // 金币余额
|
||||
Integral int64 `json:"integral"` // 积分余额
|
||||
LotteryFreeTimes int64 `json:"lotteryFreeTimes"` // 抽奖免费次数
|
||||
VipLevel int `json:"vipLevel,omitempty"` // VIP 等级;用户未开通或已过期时不下发
|
||||
VipExpireDate time.Time `json:"vipExpireDate,omitempty"` // VIP 截止时间;用户未开通或已过期时不下发
|
||||
PromotionCode string `json:"promotionCode,omitempty"` // 邀请码
|
||||
PromoteURL string `json:"promoteURL,omitempty"` // 邀请链接(已拼好 PromotionField + 邀请码)
|
||||
}
|
||||
|
||||
// GetUserBalance 获取用户金币/积分余额及 VIP 信息(VIP 已过期时不下发 VIP 字段)
|
||||
func GetUserBalance(uid uint64) (*UserBalance, error) {
|
||||
w, err := walletmod.GetWallet(uid)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询钱包异常: %w", err)
|
||||
}
|
||||
res := &UserBalance{}
|
||||
if w != nil {
|
||||
res.Gold = w.Amount
|
||||
res.Integral = w.Integral
|
||||
res.LotteryFreeTimes = w.LotteryTimes
|
||||
}
|
||||
// 用户信息独立取数,单点失败不影响余额下发
|
||||
u, uerr := usermod.FindUserByUID(uid)
|
||||
if uerr != nil {
|
||||
log.Warn("活动服-查询用户信息失败", log.Any("uid", uid), log.E(uerr))
|
||||
return res, nil
|
||||
}
|
||||
if u == nil {
|
||||
return res, nil
|
||||
}
|
||||
if u.IsVIP(time.Now()) {
|
||||
res.VipLevel = u.VipLevel
|
||||
res.VipExpireDate = u.VipExpireDate
|
||||
}
|
||||
// 邀请码 + 邀请链接(与 /api/app/mine/info 的 promoteURL 拼接方式一致)
|
||||
res.PromotionCode = u.PromCode
|
||||
if base := sourcemod.GetRandomPromotionURL(); base != "" && u.PromCode != "" {
|
||||
res.PromoteURL = common.BindUrl(base, constant.PromotionField+u.PromCode)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func convertBuyType(actBuyType int) commod.BuyType {
|
||||
switch actBuyType {
|
||||
case 1:
|
||||
return commod.BuyGold
|
||||
case 2:
|
||||
return commod.BuyProduct
|
||||
default:
|
||||
return commod.BuyType(actBuyType)
|
||||
}
|
||||
}
|
||||
|
||||
type RechargeAttribution struct {
|
||||
ActivityID string
|
||||
ExperimentID string
|
||||
ExperimentVariant string
|
||||
SessionID string
|
||||
}
|
||||
|
||||
// CreateRechargeOrder 创建充值订单。活动入口的 sourcePage 由服务端固定,
|
||||
// 避免调用方伪造其他来源;其余实验字段仍走统一下单校验。
|
||||
func CreateRechargeOrder(
|
||||
ctx context.Context,
|
||||
uid uint64,
|
||||
rechargeType, productID string,
|
||||
buyType int,
|
||||
ip string,
|
||||
attribution RechargeAttribution,
|
||||
couponID string,
|
||||
deductAmount int64,
|
||||
) (payUrl, mode string, err error) {
|
||||
pid, err := primitive.ObjectIDFromHex(productID)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("invalid productId: %w", err)
|
||||
}
|
||||
|
||||
u, err := usermod.FindUserByUID(uid)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("查询用户异常: %w", err)
|
||||
}
|
||||
if u == nil {
|
||||
return "", "", fmt.Errorf("用户不存在: %d", uid)
|
||||
}
|
||||
|
||||
realBuyType := convertBuyType(buyType)
|
||||
|
||||
in := &rechargeser.RechargeRequest{
|
||||
UID: uid,
|
||||
IP: ip,
|
||||
RechargeType: rechargeType,
|
||||
ProductID: pid,
|
||||
BuyType: realBuyType,
|
||||
SourcePage: rechargeser.OrderSourcePageH5Activity,
|
||||
SourceRef: attribution.ActivityID,
|
||||
ActivityID: attribution.ActivityID,
|
||||
ExperimentID: attribution.ExperimentID,
|
||||
ExperimentVariant: attribution.ExperimentVariant,
|
||||
SessionID: attribution.SessionID,
|
||||
}
|
||||
// 抵扣券作为活动服附加对象单独传入,不混入客户端下单请求体
|
||||
var deduct *rechargeser.ActivityDeduct
|
||||
if couponID != "" && deductAmount > 0 {
|
||||
deduct = &rechargeser.ActivityDeduct{CouponID: couponID, DeductAmount: deductAmount}
|
||||
}
|
||||
return rechargeser.Recharge(ctx, in, ua.UA{SysType: u.SysType}, deduct)
|
||||
}
|
||||
|
||||
// BuyCoinProduct 金币购买商品
|
||||
func BuyCoinProduct(ctx context.Context, uid uint64, productID string, sysType string) (stderr.Code, error) {
|
||||
pid, err := primitive.ObjectIDFromHex(productID)
|
||||
if err != nil {
|
||||
return stderr.ErrParamError, fmt.Errorf("invalid productId: %w", err)
|
||||
}
|
||||
|
||||
p, err := productmod.FindProduct(pid, sysType)
|
||||
if err != nil || p == nil {
|
||||
return stderr.ErrParamError, fmt.Errorf("商品不存在: %s", productID)
|
||||
}
|
||||
|
||||
if !p.IsAmountPay {
|
||||
return stderr.ErrParamError, fmt.Errorf("该商品不支持金币购买")
|
||||
}
|
||||
|
||||
code := productser.Buy(uid, p.ProductType, pid, primitive.NilObjectID, primitive.NilObjectID, 0, "", sysType, "", 0, false, ua.UA{SysType: sysType}, "", productser.VIPExperimentAttribution{})
|
||||
if code != stderr.Success {
|
||||
return code, fmt.Errorf("购买失败")
|
||||
}
|
||||
return stderr.Success, nil
|
||||
}
|
||||
|
||||
// GenerateSecretKey 生成 AES-256 密钥
|
||||
func GenerateSecretKey() (string, error) {
|
||||
key := make([]byte, 32)
|
||||
if _, err := rand.Read(key); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(key), nil
|
||||
}
|
||||
|
||||
// DecryptSign AES-CBC 解密签名载荷
|
||||
func DecryptSign(secretKeyBase64 string, signBase64 string) (*activityclient.SignPayload, error) {
|
||||
secretKey, err := base64.StdEncoding.DecodeString(secretKeyBase64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode secretKey failed: %w", err)
|
||||
}
|
||||
signBytes, err := base64.URLEncoding.DecodeString(signBase64)
|
||||
if err != nil {
|
||||
signBytes, err = base64.StdEncoding.DecodeString(signBase64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decode sign failed: %w", err)
|
||||
}
|
||||
}
|
||||
plaintext, err := crypt.CoreAesDecrypt(signBytes, string(secretKey))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("decrypt sign failed: %w", err)
|
||||
}
|
||||
var payload activityclient.SignPayload
|
||||
if err := json.Unmarshal([]byte(plaintext), &payload); err != nil {
|
||||
return nil, fmt.Errorf("parse sign payload failed: %w", err)
|
||||
}
|
||||
return &payload, nil
|
||||
}
|
||||
|
||||
// HmacSHA256 用于回调签名
|
||||
func HmacSHA256(secretKeyBase64 string, data []byte) (string, error) {
|
||||
keyBytes, err := base64.StdEncoding.DecodeString(secretKeyBase64)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("decode secretKey failed: %w", err)
|
||||
}
|
||||
h := hmac.New(sha256.New, keyBytes)
|
||||
h.Write(data)
|
||||
return hex.EncodeToString(h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// BuildSignedURL 生成带 sign 的活动服请求 URL
|
||||
func BuildSignedURL(userId, nickname, avatar string) (signedURL, sign string, err error) {
|
||||
conf := appg.Conf.ActivityServer
|
||||
baseURL := activityclient.GetActivityDomain()
|
||||
if baseURL == "" {
|
||||
return "", "", fmt.Errorf("活动服域名未获取,请检查配置")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
payload := &activityclient.SignPayload{
|
||||
AppId: conf.AppId,
|
||||
UserId: userId,
|
||||
Nickname: nickname,
|
||||
Avatar: avatar,
|
||||
Ts: now.Unix(),
|
||||
}
|
||||
|
||||
sign, err = activityclient.EncryptSign(conf.SecretKey, payload)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("encrypt sign failed: %w", err)
|
||||
}
|
||||
|
||||
signedURL = fmt.Sprintf("%s?appId=%s&sign=%s", baseURL, conf.AppId, sign)
|
||||
return signedURL, sign, nil
|
||||
}
|
||||
|
||||
// AppInfoResp 应用信息
|
||||
type AppInfoResp struct {
|
||||
CustomerServiceUrl string `json:"customerServiceUrl"`
|
||||
}
|
||||
|
||||
// GetAppInfo 获取应用信息
|
||||
func GetAppInfo(ctx context.Context, uid uint64) (*AppInfoResp, error) {
|
||||
resp, err := customerser.GetUrl(uid, ua.UA{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取客服地址失败: %w", err)
|
||||
}
|
||||
return &AppInfoResp{
|
||||
CustomerServiceUrl: resp.Data.Url,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// BuildLoginCurl 生成活动服登录接口的 curl 命令(调试用)
|
||||
func BuildLoginCurl(userId, nickname, avatar string) (string, error) {
|
||||
conf := appg.Conf.ActivityServer
|
||||
activityHost := activityclient.GetActivityDomain()
|
||||
if activityHost == "" {
|
||||
return "", fmt.Errorf("活动服域名未获取,请检查配置")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
payload := &activityclient.SignPayload{
|
||||
AppId: conf.AppId,
|
||||
UserId: userId,
|
||||
Nickname: nickname,
|
||||
Avatar: avatar,
|
||||
Ts: now.Unix(),
|
||||
}
|
||||
|
||||
sign, err := activityclient.EncryptSign(conf.SecretKey, payload)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("encrypt sign failed: %w", err)
|
||||
}
|
||||
|
||||
payloadJSON, _ := json.MarshalIndent(payload, "", " ")
|
||||
|
||||
result := fmt.Sprintf("AppId: %s\nSecretKey: %s\n\n", conf.AppId, conf.SecretKey)
|
||||
result += fmt.Sprintf("--- Generated Sign ---\nsign: %s\n\n", sign)
|
||||
result += fmt.Sprintf("payload: %s\n\n", string(payloadJSON))
|
||||
result += fmt.Sprintf("--- CURL ---\ncurl -X POST '%s/api/app/index/login' \\\n -H 'Content-Type: application/json' \\\n -d '{\"appId\":\"%s\",\"sign\":\"%s\"}'",
|
||||
activityHost, conf.AppId, sign)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
Reference in New Issue
Block a user