first commit
This commit is contained in:
190
app/main/api/internal/logic/agent/applyforagentlogic.go
Normal file
190
app/main/api/internal/logic/agent/applyforagentlogic.go
Normal file
@@ -0,0 +1,190 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
stderrors "errors"
|
||||
"fmt"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/xerr"
|
||||
"sim-server/pkg/lzkit/crypto"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ApplyForAgentLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewApplyForAgentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApplyForAgentLogic {
|
||||
return &ApplyForAgentLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *types.AgentApplyResp, err error) {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil && !stderrors.Is(err, ctxdata.ErrNoInCtx) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请失败, %v", err)
|
||||
}
|
||||
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
|
||||
}
|
||||
|
||||
// 系统简化:无需邀请码,直接申请成为代理
|
||||
if req.Referrer == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("请填写邀请信息"), "")
|
||||
}
|
||||
|
||||
// 2. 校验验证码(开发环境下跳过验证码校验)
|
||||
if os.Getenv("ENV") != "development" {
|
||||
redisKey := fmt.Sprintf("%s:%s", "agentApply", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if stderrors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "读取验证码失败, %v", err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "")
|
||||
}
|
||||
}
|
||||
|
||||
var userID string
|
||||
transErr := l.svcCtx.AgentModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 1. 处理用户注册/绑定
|
||||
user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && !stderrors.Is(findUserErr, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败, %v", findUserErr)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
// 用户不存在,注册新用户
|
||||
if claims != nil && claims.UserType == model.UserTypeNormal {
|
||||
return errors.Wrapf(xerr.NewErrMsg("当前用户已注册,请输入注册的手机号"), "")
|
||||
}
|
||||
userID, err = l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "注册用户失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 用户已存在
|
||||
if claims != nil && claims.UserType == model.UserTypeTemp {
|
||||
// 临时用户,检查手机号是否已绑定其他微信号
|
||||
userAuth, findUserAuthErr := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, user.Id, claims.AuthType)
|
||||
if findUserAuthErr != nil && !stderrors.Is(findUserAuthErr, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户认证失败, %v", findUserAuthErr)
|
||||
}
|
||||
if userAuth != nil && userAuth.AuthKey != claims.AuthKey {
|
||||
return errors.Wrapf(xerr.NewErrMsg("该手机号已绑定其他微信号"), "")
|
||||
}
|
||||
// 临时用户,转为正式用户
|
||||
err = l.svcCtx.UserService.TempUserBindUser(l.ctx, session, user.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定用户失败: %v", err)
|
||||
}
|
||||
}
|
||||
userID = user.Id
|
||||
}
|
||||
|
||||
// 3. 检查是否已是代理
|
||||
existingAgent, err := l.svcCtx.AgentModel.FindOneByUserId(transCtx, userID)
|
||||
if err != nil && !stderrors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
if existingAgent != nil {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您已经是代理"), "")
|
||||
}
|
||||
|
||||
// 系统简化:移除邀请码验证、等级设置、团队关系建立等复杂逻辑
|
||||
// 4. 分配代理编码
|
||||
agentCode, err := l.allocateAgentCode(transCtx, session)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "分配代理编码失败")
|
||||
}
|
||||
|
||||
// 5. 创建代理记录(简化版:无等级、无团队首领)
|
||||
newAgent := &model.Agent{
|
||||
Id: uuid.NewString(),
|
||||
UserId: userID,
|
||||
AgentCode: agentCode,
|
||||
Mobile: encryptedMobile,
|
||||
}
|
||||
if req.Region != "" {
|
||||
newAgent.Region = sql.NullString{String: req.Region, Valid: true}
|
||||
}
|
||||
|
||||
_, err = l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
|
||||
// 6. 初始化钱包(简化版:移除冻结余额、提现金额字段)
|
||||
wallet := &model.AgentWallet{
|
||||
Id: uuid.NewString(),
|
||||
AgentId: newAgent.Id,
|
||||
}
|
||||
if _, err := l.svcCtx.AgentWalletModel.Insert(transCtx, session, wallet); err != nil {
|
||||
return errors.Wrapf(err, "初始化钱包失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if transErr != nil {
|
||||
return nil, transErr
|
||||
}
|
||||
|
||||
// 7. 生成并返回token
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成token失败: %v", err)
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
agent, _ := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
var code int64
|
||||
if agent != nil {
|
||||
code = agent.AgentCode
|
||||
}
|
||||
return &types.AgentApplyResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||
AgentCode: code,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// allocateAgentCode 分配代理编码
|
||||
func (l *ApplyForAgentLogic) allocateAgentCode(ctx context.Context, session sqlx.Session) (int64, error) {
|
||||
// 系统简化:不需要事务,因为调用方已经在事务中
|
||||
builder := l.svcCtx.AgentModel.SelectBuilder().OrderBy("agent_code DESC").Limit(1)
|
||||
rows, err := l.svcCtx.AgentModel.FindAll(ctx, builder, "")
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理编码失败, %v", err)
|
||||
}
|
||||
var next int64 = 16800
|
||||
if len(rows) > 0 && rows[0].AgentCode > 0 {
|
||||
next = rows[0].AgentCode + 1
|
||||
}
|
||||
return next, nil
|
||||
}
|
||||
139
app/main/api/internal/logic/agent/createwithdrawlogic.go
Normal file
139
app/main/api/internal/logic/agent/createwithdrawlogic.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/xerr"
|
||||
"sim-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type CreateWithdrawLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewCreateWithdrawLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateWithdrawLogic {
|
||||
return &CreateWithdrawLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *CreateWithdrawLogic) CreateWithdraw(req *types.CreateWithdrawReq) (resp *types.CreateWithdrawResp, err error) {
|
||||
// 1. 获取当前用户ID(代理ID)
|
||||
userId, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 2. 查询代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userId)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrap(xerr.NewErrCode(xerr.CUSTOM_ERROR), "您还不是代理,无法申请提现")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 3. 查询钱包信息
|
||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询钱包信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 4. 验证提现金额
|
||||
if req.WithdrawAmount <= 0 {
|
||||
return nil, errors.Wrap(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "提现金额必须大于0")
|
||||
}
|
||||
|
||||
minWithdraw := 100.00 // 最低提现金额100元
|
||||
if req.WithdrawAmount < minWithdraw {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "最低提现金额为%.2f元", minWithdraw)
|
||||
}
|
||||
|
||||
if wallet.Balance < req.WithdrawAmount {
|
||||
return nil, errors.Wrap(xerr.NewErrCode(xerr.CUSTOM_ERROR), "可用余额不足")
|
||||
}
|
||||
|
||||
// 5. 加密银行卡号
|
||||
encryptedCardNumber, err := crypto.AesEncrypt([]byte(req.BankCardNumber), []byte(l.svcCtx.Config.Encrypt.SecretKey))
|
||||
if err != nil {
|
||||
logx.Errorf("加密银行卡号失败: %v", err)
|
||||
return nil, errors.Wrap(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密银行卡号失败")
|
||||
}
|
||||
|
||||
// 6. 生成提现记录ID
|
||||
withdrawId := uuid.NewString()
|
||||
|
||||
// 7. 解密代理手机号(用于冗余存储)
|
||||
agentMobile, err := crypto.DecryptMobile(agent.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
logx.Errorf("解密代理手机号失败: %v", err)
|
||||
agentMobile = ""
|
||||
}
|
||||
|
||||
// 8. 开启事务处理
|
||||
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 8.1 冻结提现金额
|
||||
wallet.Balance -= req.WithdrawAmount
|
||||
wallet.FrozenAmount += req.WithdrawAmount
|
||||
|
||||
// 使用版本号乐观锁更新
|
||||
err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "冻结金额失败")
|
||||
}
|
||||
|
||||
// 8.2 创建提现记录
|
||||
withdrawRecord := &model.AgentWithdraw{
|
||||
Id: withdrawId,
|
||||
AgentId: agent.Id,
|
||||
WithdrawAmount: req.WithdrawAmount,
|
||||
TaxAmount: 0.00,
|
||||
ActualAmount: 0.00,
|
||||
FrozenAmount: req.WithdrawAmount,
|
||||
AccountName: req.AccountName,
|
||||
BankCardNumber: req.BankCardNumber,
|
||||
BankCardNumberEncrypted: sql.NullString{String: string(encryptedCardNumber), Valid: true},
|
||||
BankBranch: sql.NullString{String: req.BankBranch, Valid: req.BankBranch != ""},
|
||||
Status: 0,
|
||||
}
|
||||
|
||||
// 冗余存储代理手机号和编码
|
||||
if agentMobile != "" {
|
||||
withdrawRecord.AgentMobile = sql.NullString{String: agentMobile, Valid: true}
|
||||
}
|
||||
if agent.AgentCode > 0 {
|
||||
withdrawRecord.AgentCode = sql.NullInt64{Int64: agent.AgentCode, Valid: true}
|
||||
}
|
||||
|
||||
_, err = l.svcCtx.AgentWithdrawModel.Insert(ctx, session, withdrawRecord)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建提现记录失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), err.Error())
|
||||
}
|
||||
|
||||
logx.Infof("代理 %s 申请提现成功,提现金额: %.2f", agent.Id, req.WithdrawAmount)
|
||||
|
||||
return &types.CreateWithdrawResp{
|
||||
WithdrawId: withdrawId,
|
||||
}, nil
|
||||
}
|
||||
279
app/main/api/internal/logic/agent/generatinglinklogic.go
Normal file
279
app/main/api/internal/logic/agent/generatinglinklogic.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/globalkey"
|
||||
"sim-server/common/tool"
|
||||
"sim-server/common/xerr"
|
||||
"sim-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GeneratingLinkLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGeneratingLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GeneratingLinkLogic {
|
||||
return &GeneratingLinkLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GeneratingLinkLogic) GeneratingLink(req *types.AgentGeneratingLinkReq) (resp *types.AgentGeneratingLinkResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成推广链接失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 获取产品配置(必须存在)
|
||||
productConfig, err := l.svcCtx.AgentProductConfigModel.FindOneByProductId(l.ctx, req.ProductId)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "产品配置不存在, productId: %d,请先在后台配置产品价格参数", req.ProductId)
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品配置失败, productId: %d, %v", req.ProductId, err)
|
||||
}
|
||||
|
||||
// 系统简化:移除等级加成和等级上调金额,直接使用底价和系统最高价
|
||||
actualBasePrice := productConfig.BasePrice
|
||||
systemMaxPrice := productConfig.SystemMaxPrice
|
||||
if req.SetPrice < actualBasePrice || req.SetPrice > systemMaxPrice {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("设定价格必须在 %.2f 到 %.2f 之间"), "设定价格必须在 %.2f 到 %.2f 之间", actualBasePrice, systemMaxPrice)
|
||||
}
|
||||
|
||||
// 检查是否已存在相同的链接(同一代理、同一产品、同一价格)
|
||||
builder := l.svcCtx.AgentLinkModel.SelectBuilder().Where(squirrel.And{
|
||||
squirrel.Eq{"agent_id": agentModel.Id},
|
||||
squirrel.Eq{"product_id": req.ProductId},
|
||||
squirrel.Eq{"set_price": req.SetPrice},
|
||||
squirrel.Eq{"del_state": globalkey.DelStateNo},
|
||||
})
|
||||
|
||||
existingLinks, err := l.svcCtx.AgentLinkModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询推广链接失败, %v", err)
|
||||
}
|
||||
|
||||
if len(existingLinks) > 0 {
|
||||
// 已存在,检查是否有短链,如果没有则生成
|
||||
targetPath := req.TargetPath
|
||||
if targetPath == "" {
|
||||
targetPath = "/agent/promotionInquire/"
|
||||
}
|
||||
shortLink, err := l.getOrCreateShortLink(1, existingLinks[0].Id, "", existingLinks[0].LinkIdentifier, "", targetPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取或创建短链失败, %v", err)
|
||||
}
|
||||
return &types.AgentGeneratingLinkResp{
|
||||
LinkIdentifier: existingLinks[0].LinkIdentifier,
|
||||
FullLink: shortLink,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 7. 生成推广链接标识
|
||||
var agentIdentifier types.AgentIdentifier
|
||||
agentIdentifier.AgentID = agentModel.Id
|
||||
agentIdentifier.ProductID = req.ProductId
|
||||
agentIdentifier.SetPrice = req.SetPrice
|
||||
agentIdentifierByte, err := json.Marshal(agentIdentifier)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "序列化标识失败, %v", err)
|
||||
}
|
||||
|
||||
// 8. 加密链接标识
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
key, decodeErr := hex.DecodeString(secretKey)
|
||||
if decodeErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取AES密钥失败: %+v", decodeErr)
|
||||
}
|
||||
|
||||
encrypted, err := crypto.AesEncryptURL(agentIdentifierByte, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密链接标识失败, %v", err)
|
||||
}
|
||||
|
||||
// 9. 保存推广链接
|
||||
agentLink := &model.AgentLink{
|
||||
// Id: uuid.NewString(),
|
||||
AgentId: agentModel.Id,
|
||||
UserId: userID,
|
||||
ProductId: req.ProductId,
|
||||
LinkIdentifier: encrypted,
|
||||
SetPrice: req.SetPrice,
|
||||
ActualBasePrice: actualBasePrice,
|
||||
}
|
||||
|
||||
_, err = l.svcCtx.AgentLinkModel.Insert(l.ctx, nil, agentLink)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "保存推广链接失败, %v", err)
|
||||
}
|
||||
|
||||
linkId := agentLink.Id
|
||||
|
||||
// 使用默认target_path(如果未提供)
|
||||
targetPath := req.TargetPath
|
||||
if targetPath == "" {
|
||||
targetPath = "/agent/promotionInquire/"
|
||||
}
|
||||
|
||||
// 生成短链(类型:1=推广报告)
|
||||
shortLink, err := l.createShortLink(1, linkId, "", encrypted, "", targetPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成短链失败, %v", err)
|
||||
}
|
||||
|
||||
return &types.AgentGeneratingLinkResp{
|
||||
LinkIdentifier: encrypted,
|
||||
FullLink: shortLink,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getOrCreateShortLink 获取或创建短链
|
||||
// type: 1=推广报告(promotion), 2=邀请好友(invite)
|
||||
// linkId: 推广链接ID(仅推广报告使用)
|
||||
// inviteCodeId: 邀请码ID(仅邀请好友使用)
|
||||
// linkIdentifier: 推广链接标识(仅推广报告使用)
|
||||
// inviteCode: 邀请码(仅邀请好友使用)
|
||||
// targetPath: 目标地址(前端传入)
|
||||
func (l *GeneratingLinkLogic) getOrCreateShortLink(linkType int64, linkId, inviteCodeId string, linkIdentifier, inviteCode, targetPath string) (string, error) {
|
||||
// 先查询是否已存在短链
|
||||
var existingShortLink *model.AgentShortLink
|
||||
var err error
|
||||
|
||||
if linkType == 1 {
|
||||
// 推广报告类型,使用link_id查询
|
||||
if linkId != "" {
|
||||
existingShortLink, err = l.svcCtx.AgentShortLinkModel.FindOneByLinkIdTypeDelState(l.ctx, sql.NullString{String: linkId, Valid: true}, linkType, globalkey.DelStateNo)
|
||||
}
|
||||
} else {
|
||||
// 邀请好友类型,使用invite_code_id查询
|
||||
if inviteCodeId != "" {
|
||||
existingShortLink, err = l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeIdTypeDelState(l.ctx, sql.NullString{String: inviteCodeId, Valid: true}, linkType, globalkey.DelStateNo)
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil && existingShortLink != nil {
|
||||
// 已存在短链,直接返回
|
||||
return l.buildShortLinkURL(existingShortLink.ShortCode), nil
|
||||
}
|
||||
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return "", errors.Wrapf(err, "查询短链失败")
|
||||
}
|
||||
|
||||
// 不存在,创建新的短链
|
||||
return l.createShortLink(linkType, linkId, inviteCodeId, linkIdentifier, inviteCode, targetPath)
|
||||
}
|
||||
|
||||
// createShortLink 创建短链
|
||||
// type: 1=推广报告(promotion), 2=邀请好友(invite)
|
||||
func (l *GeneratingLinkLogic) createShortLink(linkType int64, linkId, inviteCodeId string, linkIdentifier, inviteCode, targetPath string) (string, error) {
|
||||
promotionConfig := l.svcCtx.Config.Promotion
|
||||
|
||||
// 如果没有配置推广域名,返回空字符串(保持向后兼容)
|
||||
if promotionConfig.PromotionDomain == "" {
|
||||
l.Errorf("推广域名未配置,返回空链接")
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// 验证target_path
|
||||
if targetPath == "" {
|
||||
return "", errors.Wrapf(xerr.NewErrMsg("目标地址不能为空"), "")
|
||||
}
|
||||
|
||||
// 对于推广报告类型,将 linkIdentifier 拼接到 target_path
|
||||
if linkType == 1 && linkIdentifier != "" {
|
||||
// 如果 target_path 以 / 结尾,直接拼接 linkIdentifier
|
||||
if strings.HasSuffix(targetPath, "/") {
|
||||
targetPath = targetPath + url.QueryEscape(linkIdentifier)
|
||||
} else {
|
||||
// 否则在末尾添加 / 再拼接
|
||||
targetPath = targetPath + "/" + url.QueryEscape(linkIdentifier)
|
||||
}
|
||||
}
|
||||
|
||||
// 生成短链标识(6位随机字符串,大小写字母+数字)
|
||||
var shortCode string
|
||||
maxRetries := 10 // 最大重试次数
|
||||
for retry := 0; retry < maxRetries; retry++ {
|
||||
shortCode = tool.Krand(6, tool.KC_RAND_KIND_ALL)
|
||||
// 检查短链标识是否已存在
|
||||
_, err := l.svcCtx.AgentShortLinkModel.FindOneByShortCodeDelState(l.ctx, shortCode, globalkey.DelStateNo)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
// 短链标识不存在,可以使用
|
||||
break
|
||||
}
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "检查短链标识失败, %v", err)
|
||||
}
|
||||
// 短链标识已存在,继续生成
|
||||
if retry == maxRetries-1 {
|
||||
return "", errors.Wrapf(xerr.NewErrMsg("生成短链失败,请重试"), "")
|
||||
}
|
||||
}
|
||||
|
||||
// 创建短链记录
|
||||
shortLink := &model.AgentShortLink{
|
||||
Id: uuid.NewString(),
|
||||
Type: linkType,
|
||||
ShortCode: shortCode,
|
||||
TargetPath: targetPath,
|
||||
PromotionDomain: promotionConfig.PromotionDomain,
|
||||
}
|
||||
|
||||
// 根据类型设置对应字段
|
||||
if linkType == 1 {
|
||||
// 推广报告类型
|
||||
shortLink.LinkId = sql.NullString{String: linkId, Valid: linkId != ""}
|
||||
if linkIdentifier != "" {
|
||||
shortLink.LinkIdentifier = sql.NullString{String: linkIdentifier, Valid: true}
|
||||
}
|
||||
} else if linkType == 2 {
|
||||
// 邀请好友类型
|
||||
shortLink.InviteCodeId = sql.NullString{String: inviteCodeId, Valid: inviteCodeId != ""}
|
||||
if inviteCode != "" {
|
||||
shortLink.InviteCode = sql.NullString{String: inviteCode, Valid: true}
|
||||
}
|
||||
}
|
||||
_, err := l.svcCtx.AgentShortLinkModel.Insert(l.ctx, nil, shortLink)
|
||||
if err != nil {
|
||||
return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "保存短链失败, %v", err)
|
||||
}
|
||||
|
||||
return l.buildShortLinkURL(shortCode), nil
|
||||
}
|
||||
|
||||
// buildShortLinkURL 构建短链URL
|
||||
func (l *GeneratingLinkLogic) buildShortLinkURL(shortCode string) string {
|
||||
promotionConfig := l.svcCtx.Config.Promotion
|
||||
return fmt.Sprintf("%s/s/%s", promotionConfig.PromotionDomain, shortCode)
|
||||
}
|
||||
157
app/main/api/internal/logic/agent/getagentdashboardlogic.go
Normal file
157
app/main/api/internal/logic/agent/getagentdashboardlogic.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
"sim-server/app/main/model"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentDashboardLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentDashboardLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentDashboardLogic {
|
||||
return &GetAgentDashboardLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentDashboardLogic) GetAgentDashboard() (resp *types.AgentDashboardResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 查询代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您还不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 查询钱包信息
|
||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包信息失败, %v", err)
|
||||
}
|
||||
if wallet == nil {
|
||||
// 如果没有钱包,创建一个空的
|
||||
wallet = &model.AgentWallet{
|
||||
Balance: 0,
|
||||
TotalEarnings: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// 获取今日统计数据(使用佣金表)
|
||||
today := time.Now().Format("2006-01-02")
|
||||
todayStart, _ := time.Parse("2006-01-02", today)
|
||||
todayEnd := todayStart.Add(24 * time.Hour)
|
||||
|
||||
// 今日订单数和佣金
|
||||
todayOrders, todayEarnings, err := l.getCommissionStats(l.ctx, agent.Id, todayStart, todayEnd)
|
||||
if err != nil {
|
||||
logx.Errorf("获取今日佣金统计失败: %v", err)
|
||||
todayOrders = 0
|
||||
todayEarnings = 0
|
||||
}
|
||||
|
||||
// 本月订单数和佣金
|
||||
monthStart := time.Now().AddDate(0, 0, -time.Now().Day()+1) // 本月1号
|
||||
monthStart = time.Date(monthStart.Year(), monthStart.Month(), monthStart.Day(), 0, 0, 0, 0, monthStart.Location())
|
||||
_, monthEarnings, err := l.getCommissionStats(l.ctx, agent.Id, monthStart, time.Now())
|
||||
if err != nil {
|
||||
logx.Errorf("获取本月佣金统计失败: %v", err)
|
||||
monthEarnings = 0
|
||||
}
|
||||
|
||||
// 总订单数和佣金
|
||||
totalOrders, totalEarnings, err := l.getCommissionStats(l.ctx, agent.Id, time.Time{}, time.Now())
|
||||
if err != nil {
|
||||
logx.Errorf("获取总佣金统计失败: %v", err)
|
||||
totalOrders = 0
|
||||
totalEarnings = 0
|
||||
}
|
||||
|
||||
// 构建推广链接
|
||||
domain := l.svcCtx.Config.Promotion.OfficialDomain
|
||||
promotionLink := fmt.Sprintf("%s/pages/query/index?agentCode=%d", domain, agent.AgentCode)
|
||||
|
||||
// 获取佣金率(从配置中读取,默认为20%)
|
||||
commissionRate := l.getCommissionRate()
|
||||
|
||||
return &types.AgentDashboardResp{
|
||||
Statistics: types.DashboardStatistics{
|
||||
TodayOrders: todayOrders,
|
||||
TodayEarnings: fmt.Sprintf("%.2f", todayEarnings),
|
||||
MonthEarnings: fmt.Sprintf("%.2f", monthEarnings),
|
||||
TotalOrders: totalOrders,
|
||||
TotalEarnings: fmt.Sprintf("%.2f", totalEarnings),
|
||||
CommissionRate: commissionRate,
|
||||
CurrentBalance: fmt.Sprintf("%.2f", wallet.Balance),
|
||||
FrozenBalance: fmt.Sprintf("%.2f", wallet.FrozenAmount),
|
||||
},
|
||||
PromotionLink: promotionLink,
|
||||
Product: types.DashboardProduct{
|
||||
Name: "个人大数据",
|
||||
Price: "29.90",
|
||||
Description: "全面个人数据风险报告",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getCommissionStats 获取指定时间段的佣金统计
|
||||
func (l *GetAgentDashboardLogic) getCommissionStats(ctx context.Context, agentId string, startTime, endTime time.Time) (int64, float64, error) {
|
||||
builder := l.svcCtx.AgentCommissionModel.SelectBuilder()
|
||||
|
||||
// 添加代理ID条件
|
||||
builder = builder.Where("agent_id = ?", agentId)
|
||||
|
||||
// 添加时间范围条件
|
||||
if !startTime.IsZero() {
|
||||
builder = builder.Where("create_time >= ?", startTime)
|
||||
}
|
||||
if !endTime.IsZero() {
|
||||
builder = builder.Where("create_time < ?", endTime)
|
||||
}
|
||||
|
||||
// 只统计已发放的佣金
|
||||
builder = builder.Where("status = ?", 1)
|
||||
|
||||
// 获取数量
|
||||
count, err := l.svcCtx.AgentCommissionModel.FindCount(ctx, builder, "id")
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// 获取总额
|
||||
sum, err := l.svcCtx.AgentCommissionModel.FindSum(ctx, builder, "amount")
|
||||
if err != nil {
|
||||
return count, 0, err
|
||||
}
|
||||
|
||||
return count, sum, nil
|
||||
}
|
||||
|
||||
// getCommissionRate 获取佣金率
|
||||
func (l *GetAgentDashboardLogic) getCommissionRate() string {
|
||||
// TODO: 从配置或数据库中读取佣金率
|
||||
// 目前返回固定值20%
|
||||
return "20%"
|
||||
}
|
||||
79
app/main/api/internal/logic/agent/getagentinfologic.go
Normal file
79
app/main/api/internal/logic/agent/getagentinfologic.go
Normal file
@@ -0,0 +1,79 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/xerr"
|
||||
"sim-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
"sim-server/app/main/model"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentInfoLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentInfoLogic {
|
||||
return &GetAgentInfoLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentInfoLogic) GetAgentInfo() (resp *types.AgentInfoResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
// 不是代理,返回空信息
|
||||
return &types.AgentInfoResp{
|
||||
AgentId: "",
|
||||
Region: "",
|
||||
Mobile: "",
|
||||
WechatId: "",
|
||||
AgentCode: 0,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 解密手机号
|
||||
mobile, err := crypto.DecryptMobile(agent.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密手机号失败: %v", err)
|
||||
}
|
||||
|
||||
// 系统简化:移除实名认证查询
|
||||
// 获取微信号
|
||||
wechatId := ""
|
||||
if agent.WechatId.Valid {
|
||||
wechatId = agent.WechatId.String
|
||||
}
|
||||
|
||||
// 获取区域
|
||||
region := ""
|
||||
if agent.Region.Valid {
|
||||
region = agent.Region.String
|
||||
}
|
||||
|
||||
return &types.AgentInfoResp{
|
||||
AgentId: agent.Id,
|
||||
Region: region,
|
||||
Mobile: mobile,
|
||||
WechatId: wechatId,
|
||||
AgentCode: agent.AgentCode,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentProductConfigLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentProductConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentProductConfigLogic {
|
||||
return &GetAgentProductConfigLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentProductConfigLogic) GetAgentProductConfig() (resp *types.AgentProductConfigResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 验证用户是否为代理
|
||||
_, err = l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 系统简化:移除等级加成,直接使用基础配置
|
||||
// 2. 查询所有产品配置
|
||||
builder := l.svcCtx.AgentProductConfigModel.SelectBuilder()
|
||||
productConfigs, err := l.svcCtx.AgentProductConfigModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品配置失败, %v", err)
|
||||
}
|
||||
|
||||
// 3. 组装响应数据(通过 product_id 查询产品名称和英文标识)
|
||||
var respList []types.ProductConfigItem
|
||||
for _, productConfig := range productConfigs {
|
||||
// 通过 product_id 查询产品信息获取产品名称和英文标识
|
||||
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, productConfig.ProductId)
|
||||
productName := ""
|
||||
productEn := ""
|
||||
if err == nil {
|
||||
productName = product.ProductName
|
||||
productEn = product.ProductEn
|
||||
} else {
|
||||
// 如果产品不存在,记录日志但不影响主流程
|
||||
l.Infof("查询产品信息失败, productId: %d, err: %v", productConfig.ProductId, err)
|
||||
}
|
||||
|
||||
// 系统简化:直接使用基础底价和系统最高价
|
||||
actualBasePrice := productConfig.BasePrice
|
||||
priceRangeMin := actualBasePrice
|
||||
priceRangeMax := productConfig.SystemMaxPrice
|
||||
|
||||
// 使用产品配置的提价阈值和手续费比例,如果为NULL则使用0
|
||||
productPriceThreshold := 0.0
|
||||
if productConfig.PriceThreshold.Valid {
|
||||
productPriceThreshold = productConfig.PriceThreshold.Float64
|
||||
}
|
||||
productPriceFeeRate := 0.0
|
||||
if productConfig.PriceFeeRate.Valid {
|
||||
productPriceFeeRate = productConfig.PriceFeeRate.Float64
|
||||
}
|
||||
|
||||
respList = append(respList, types.ProductConfigItem{
|
||||
ProductId: productConfig.ProductId,
|
||||
ProductName: productName,
|
||||
ProductEn: productEn,
|
||||
ActualBasePrice: actualBasePrice,
|
||||
PriceRangeMin: priceRangeMin,
|
||||
PriceRangeMax: priceRangeMax,
|
||||
PriceThreshold: productPriceThreshold,
|
||||
PriceFeeRate: productPriceFeeRate,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.AgentProductConfigResp{
|
||||
List: respList,
|
||||
}, nil
|
||||
}
|
||||
112
app/main/api/internal/logic/agent/getcommissionlistlogic.go
Normal file
112
app/main/api/internal/logic/agent/getcommissionlistlogic.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/globalkey"
|
||||
"sim-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetCommissionListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetCommissionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCommissionListLogic {
|
||||
return &GetCommissionListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetCommissionListLogic) GetCommissionList(req *types.GetCommissionListReq) (resp *types.GetCommissionListResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 构建查询条件
|
||||
builder := l.svcCtx.AgentCommissionModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo).
|
||||
OrderBy("create_time DESC")
|
||||
|
||||
// 3. 分页查询
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
// 4. 查询总数
|
||||
total, err := l.svcCtx.AgentCommissionModel.FindCount(l.ctx, builder, "id")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询佣金总数失败, %v", err)
|
||||
}
|
||||
|
||||
// 5. 查询列表
|
||||
builder = builder.Limit(uint64(pageSize)).Offset(uint64(offset))
|
||||
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询佣金列表失败, %v", err)
|
||||
}
|
||||
|
||||
// 6. 组装响应
|
||||
var list []types.CommissionItem
|
||||
for _, commission := range commissions {
|
||||
// 查询产品名称
|
||||
productName := ""
|
||||
if commission.ProductId != "" {
|
||||
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, commission.ProductId)
|
||||
if err == nil {
|
||||
productName = product.ProductName
|
||||
}
|
||||
}
|
||||
|
||||
// 查询订单号
|
||||
orderNo := ""
|
||||
if commission.OrderId != "" {
|
||||
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, commission.OrderId)
|
||||
if err == nil {
|
||||
orderNo = order.OrderNo
|
||||
}
|
||||
}
|
||||
|
||||
list = append(list, types.CommissionItem{
|
||||
Id: commission.Id,
|
||||
OrderId: commission.OrderId,
|
||||
OrderNo: orderNo,
|
||||
ProductName: productName,
|
||||
Amount: commission.Amount,
|
||||
Status: commission.Status,
|
||||
CreateTime: commission.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetCommissionListResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
102
app/main/api/internal/logic/agent/getlinkdatalogic.go
Normal file
102
app/main/api/internal/logic/agent/getlinkdatalogic.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/xerr"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetLinkDataLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetLinkDataLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLinkDataLogic {
|
||||
return &GetLinkDataLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetLinkDataLogic) GetLinkData(req *types.GetLinkDataReq) (resp *types.GetLinkDataResp, err error) {
|
||||
agentLinkModel, err := l.svcCtx.AgentLinkModel.FindOneByLinkIdentifier(l.ctx, req.LinkIdentifier)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理链接数据失败, %v", err)
|
||||
}
|
||||
|
||||
productModel, err := l.svcCtx.ProductModel.FindOne(l.ctx, agentLinkModel.ProductId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取产品信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 获取产品功能列表
|
||||
build := l.svcCtx.ProductFeatureModel.SelectBuilder().Where(squirrel.Eq{
|
||||
"product_id": productModel.Id,
|
||||
})
|
||||
productFeatureAll, err := l.svcCtx.ProductFeatureModel.FindAll(l.ctx, build, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取产品功能列表失败, %v", err)
|
||||
}
|
||||
|
||||
// 创建featureId到sort的映射,用于后续排序
|
||||
featureSortMap := make(map[string]int64)
|
||||
for _, productFeature := range productFeatureAll {
|
||||
featureSortMap[productFeature.FeatureId] = productFeature.Sort
|
||||
}
|
||||
|
||||
var features []types.Feature
|
||||
mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for _, productFeature := range productFeatureAll {
|
||||
source <- productFeature.FeatureId
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[*model.Feature], cancel func(error)) {
|
||||
id := item.(string)
|
||||
|
||||
feature, findFeatureErr := l.svcCtx.FeatureModel.FindOne(l.ctx, id)
|
||||
if findFeatureErr != nil {
|
||||
logx.WithContext(l.ctx).Errorf("获取产品功能失败: %s, err:%v", id, findFeatureErr)
|
||||
return
|
||||
}
|
||||
if feature != nil && feature.Id != "" {
|
||||
writer.Write(feature)
|
||||
}
|
||||
}, func(pipe <-chan *model.Feature, cancel func(error)) {
|
||||
for item := range pipe {
|
||||
var feature types.Feature
|
||||
_ = copier.Copy(&feature, item)
|
||||
features = append(features, feature)
|
||||
}
|
||||
})
|
||||
|
||||
// 按照productFeature.Sort字段对features进行排序
|
||||
sort.Slice(features, func(i, j int) bool {
|
||||
sortI := featureSortMap[features[i].ID]
|
||||
sortJ := featureSortMap[features[j].ID]
|
||||
return sortI < sortJ
|
||||
})
|
||||
|
||||
return &types.GetLinkDataResp{
|
||||
AgentId: agentLinkModel.AgentId,
|
||||
ProductId: agentLinkModel.ProductId,
|
||||
SetPrice: agentLinkModel.SetPrice,
|
||||
ActualBasePrice: agentLinkModel.ActualBasePrice,
|
||||
ProductName: productModel.ProductName,
|
||||
ProductEn: productModel.ProductEn,
|
||||
SellPrice: agentLinkModel.SetPrice, // 使用代理设定价格作为销售价格
|
||||
Description: productModel.Description,
|
||||
Features: features,
|
||||
}, nil
|
||||
}
|
||||
124
app/main/api/internal/logic/agent/getpromotionquerylistlogic.go
Normal file
124
app/main/api/internal/logic/agent/getpromotionquerylistlogic.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/xerr"
|
||||
"sim-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
)
|
||||
|
||||
type GetPromotionQueryListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetPromotionQueryListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPromotionQueryListLogic {
|
||||
return &GetPromotionQueryListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetPromotionQueryListLogic) GetPromotionQueryList(req *types.GetPromotionQueryListReq) (resp *types.GetPromotionQueryListResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 查询当前代理的代理订单,按创建时间倒序分页
|
||||
builder := l.svcCtx.AgentOrderModel.SelectBuilder().
|
||||
Where("agent_id = ? AND process_status = 1", agent.Id)
|
||||
|
||||
orders, total, err := l.svcCtx.AgentOrderModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理订单失败, %v", err)
|
||||
}
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
key, decodeErr := hex.DecodeString(secretKey)
|
||||
if decodeErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR),
|
||||
"获取AES密钥失败: %v", decodeErr)
|
||||
}
|
||||
// 组装查询报告列表(只展示已创建的查询)
|
||||
list := make([]types.PromotionQueryItem, 0, len(orders))
|
||||
for _, ao := range orders {
|
||||
// 查询对应的报告
|
||||
q, qErr := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, ao.OrderId)
|
||||
if qErr != nil {
|
||||
if errors.Is(qErr, model.ErrNotFound) {
|
||||
// 订单对应的查询尚未创建,跳过展示
|
||||
continue
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询报告失败, %v", qErr)
|
||||
}
|
||||
|
||||
// 查询订单信息,获取order_no
|
||||
order, oErr := l.svcCtx.OrderModel.FindOne(l.ctx, ao.OrderId)
|
||||
if oErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单信息失败, %v", oErr)
|
||||
}
|
||||
|
||||
// 获取产品名称
|
||||
product, pErr := l.svcCtx.ProductModel.FindOne(l.ctx, ao.ProductId)
|
||||
if pErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品信息失败, %v", pErr)
|
||||
}
|
||||
|
||||
item := types.PromotionQueryItem{}
|
||||
_ = copier.Copy(&item, q)
|
||||
item.Id = q.Id
|
||||
item.OrderId = q.OrderId
|
||||
item.OrderNo = order.OrderNo
|
||||
item.ProductName = product.ProductName
|
||||
item.OrderAmount = order.Amount // 订单金额
|
||||
item.Amount = ao.AgentProfit // 推广收益
|
||||
item.CreateTime = q.CreateTime.Format("2006-01-02 15:04:05")
|
||||
item.QueryState = q.QueryState
|
||||
|
||||
// 解析query_params获取查询人信息
|
||||
if q.QueryParams != "" {
|
||||
queryParamsByte, err := crypto.AesDecrypt(q.QueryParams, key)
|
||||
var queryParams map[string]interface{}
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密查询参数失败, %v", err)
|
||||
}
|
||||
if err := json.Unmarshal(queryParamsByte, &queryParams); err == nil {
|
||||
// 获取姓名
|
||||
if name, ok := queryParams["name"].(string); ok {
|
||||
item.QueryName = name
|
||||
}
|
||||
// 获取手机号
|
||||
if mobile, ok := queryParams["mobile"].(string); ok {
|
||||
item.QueryMobile = mobile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
return &types.GetPromotionQueryListResp{
|
||||
Total: total, // 前端仅展示已创建查询条目
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
84
app/main/api/internal/logic/agent/getrevenueinfologic.go
Normal file
84
app/main/api/internal/logic/agent/getrevenueinfologic.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/globalkey"
|
||||
"sim-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetRevenueInfoLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetRevenueInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRevenueInfoLogic {
|
||||
return &GetRevenueInfoLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetRevenueInfoLogic) GetRevenueInfo() (resp *types.GetRevenueInfoResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 获取钱包信息
|
||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 获取当前时间
|
||||
now := time.Now()
|
||||
// 今日开始时间(00:00:00)
|
||||
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
|
||||
// 本月开始时间(1号 00:00:00)
|
||||
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
|
||||
|
||||
// 3. 统计佣金总额(从 agent_commission 表统计)
|
||||
commissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo)
|
||||
commissionTotal, _ := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, commissionBuilder, "amount")
|
||||
|
||||
// 3.1 统计佣金今日收益
|
||||
commissionTodayBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, todayStart)
|
||||
commissionToday, _ := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, commissionTodayBuilder, "amount")
|
||||
|
||||
// 3.2 统计佣金本月收益
|
||||
commissionMonthBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ? AND create_time >= ?", agent.Id, globalkey.DelStateNo, monthStart)
|
||||
commissionMonth, _ := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, commissionMonthBuilder, "amount")
|
||||
|
||||
// 系统简化:移除返佣功能(推广返佣和升级返佣)
|
||||
return &types.GetRevenueInfoResp{
|
||||
Balance: wallet.Balance,
|
||||
TotalEarnings: wallet.TotalEarnings,
|
||||
CommissionTotal: commissionTotal, // 佣金累计总收益
|
||||
CommissionToday: commissionToday, // 佣金今日收益
|
||||
CommissionMonth: commissionMonth, // 佣金本月收益
|
||||
}, nil
|
||||
}
|
||||
127
app/main/api/internal/logic/agent/getwithdrawlistlogic.go
Normal file
127
app/main/api/internal/logic/agent/getwithdrawlistlogic.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
"sim-server/app/main/api/internal/types"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/ctxdata"
|
||||
"sim-server/common/globalkey"
|
||||
"sim-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetWithdrawListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetWithdrawListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWithdrawListLogic {
|
||||
return &GetWithdrawListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetWithdrawListLogic) GetWithdrawList(req *types.GetWithdrawListReq) (resp *types.GetWithdrawListResp, err error) {
|
||||
// 1. 获取当前用户ID(代理ID)
|
||||
userId, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 2. 查询代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userId)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
// 不是代理,返回空列表
|
||||
return &types.GetWithdrawListResp{
|
||||
Total: 0,
|
||||
List: []types.WithdrawItem{},
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理信息失败: %v", err)
|
||||
}
|
||||
|
||||
// 3. 构建查询条件
|
||||
builder := l.svcCtx.AgentWithdrawModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo).
|
||||
OrderBy("create_time DESC")
|
||||
|
||||
// 4. 分页参数
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
// 5. 查询总数
|
||||
total, err := l.svcCtx.AgentWithdrawModel.FindCount(l.ctx, builder, "id")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录总数失败: %v", err)
|
||||
}
|
||||
|
||||
if total == 0 {
|
||||
return &types.GetWithdrawListResp{
|
||||
Total: 0,
|
||||
List: []types.WithdrawItem{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 6. 查询列表
|
||||
builder = builder.Limit(uint64(pageSize)).Offset(uint64(offset))
|
||||
withdrawals, err := l.svcCtx.AgentWithdrawModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录列表失败: %v", err)
|
||||
}
|
||||
|
||||
// 7. 组装响应
|
||||
var list []types.WithdrawItem
|
||||
for _, withdrawal := range withdrawals {
|
||||
// 处理银行卡号脱敏(只显示前4位和后4位)
|
||||
bankCardNumber := withdrawal.BankCardNumber
|
||||
if len(bankCardNumber) > 8 {
|
||||
bankCardNumber = fmt.Sprintf("%s****%s", bankCardNumber[:4], bankCardNumber[len(bankCardNumber)-4:])
|
||||
}
|
||||
|
||||
// 处理审核备注
|
||||
auditRemark := ""
|
||||
if withdrawal.AuditRemark.Valid {
|
||||
auditRemark = withdrawal.AuditRemark.String
|
||||
}
|
||||
|
||||
// 处理审核时间
|
||||
auditTime := ""
|
||||
if withdrawal.AuditTime.Valid {
|
||||
auditTime = withdrawal.AuditTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
list = append(list, types.WithdrawItem{
|
||||
Id: withdrawal.Id,
|
||||
WithdrawAmount: fmt.Sprintf("%.2f", withdrawal.WithdrawAmount),
|
||||
TaxAmount: fmt.Sprintf("%.2f", withdrawal.TaxAmount),
|
||||
ActualAmount: fmt.Sprintf("%.2f", withdrawal.ActualAmount),
|
||||
AccountName: withdrawal.AccountName,
|
||||
BankCardNumber: bankCardNumber,
|
||||
Status: withdrawal.Status,
|
||||
AuditRemark: auditRemark,
|
||||
CreateTime: withdrawal.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
AuditTime: auditTime,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetWithdrawListResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
102
app/main/api/internal/logic/agent/shortlinkredirectlogic.go
Normal file
102
app/main/api/internal/logic/agent/shortlinkredirectlogic.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sim-server/app/main/model"
|
||||
"sim-server/common/globalkey"
|
||||
"sim-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
|
||||
"sim-server/app/main/api/internal/svc"
|
||||
)
|
||||
|
||||
type ShortLinkRedirectLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewShortLinkRedirectLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShortLinkRedirectLogic {
|
||||
return &ShortLinkRedirectLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// ShortLinkRedirect 短链重定向
|
||||
// 从短链重定向到推广页面
|
||||
func (l *ShortLinkRedirectLogic) ShortLinkRedirect(shortCode string, r *http.Request, w http.ResponseWriter) error {
|
||||
// 1. 验证短链标识
|
||||
if shortCode == "" {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "缺少短链标识")
|
||||
}
|
||||
|
||||
// 2. 查询短链记录
|
||||
shortLink, err := l.svcCtx.AgentShortLinkModel.FindOneByShortCodeDelState(l.ctx, shortCode, globalkey.DelStateNo)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "短链不存在或已失效")
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询短链失败, %v", err)
|
||||
}
|
||||
|
||||
// 3. 根据类型验证链接有效性
|
||||
if shortLink.Type == 1 {
|
||||
// 推广报告类型:验证linkIdentifier是否存在
|
||||
if shortLink.LinkIdentifier.Valid && shortLink.LinkIdentifier.String != "" {
|
||||
_, err = l.svcCtx.AgentLinkModel.FindOneByLinkIdentifier(l.ctx, shortLink.LinkIdentifier.String)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "推广链接不存在或已失效")
|
||||
}
|
||||
l.Errorf("查询推广链接失败: %v", err)
|
||||
// 即使查询失败,也继续重定向,避免影响用户体验
|
||||
}
|
||||
}
|
||||
}
|
||||
// 系统简化:移除邀请码验证功能
|
||||
|
||||
// 4. 构建重定向URL
|
||||
redirectURL := shortLink.TargetPath
|
||||
if redirectURL == "" {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "短链目标地址为空")
|
||||
}
|
||||
|
||||
// 如果 target_path 是相对路径,需要拼接正式域名
|
||||
// 如果 target_path 已经是完整URL,则直接使用
|
||||
if !strings.HasPrefix(redirectURL, "http://") && !strings.HasPrefix(redirectURL, "https://") {
|
||||
// 相对路径,需要拼接正式域名
|
||||
officialDomain := l.svcCtx.Config.Promotion.OfficialDomain
|
||||
if officialDomain == "" {
|
||||
// 如果没有配置正式域名,使用当前请求的域名(向后兼容)
|
||||
scheme := "http"
|
||||
if r.TLS != nil {
|
||||
scheme = "https"
|
||||
}
|
||||
officialDomain = fmt.Sprintf("%s://%s", scheme, r.Host)
|
||||
}
|
||||
// 确保正式域名不以 / 结尾
|
||||
officialDomain = strings.TrimSuffix(officialDomain, "/")
|
||||
// 确保 target_path 以 / 开头
|
||||
if !strings.HasPrefix(redirectURL, "/") {
|
||||
redirectURL = "/" + redirectURL
|
||||
}
|
||||
redirectURL = officialDomain + redirectURL
|
||||
}
|
||||
|
||||
// 5. 执行重定向(302临时重定向)
|
||||
linkIdentifierStr := ""
|
||||
if shortLink.LinkIdentifier.Valid {
|
||||
linkIdentifierStr = shortLink.LinkIdentifier.String
|
||||
}
|
||||
l.Infof("短链重定向: shortCode=%s, type=%d, linkIdentifier=%s, redirectURL=%s", shortCode, shortLink.Type, linkIdentifierStr, redirectURL)
|
||||
http.Redirect(w, r, redirectURL, http.StatusFound)
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user