v1.1
This commit is contained in:
@@ -1,19 +1,22 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/crypto"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/crypto"
|
||||
"strconv"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"ycc-server/app/main/api/internal/svc"
|
||||
"ycc-server/app/main/api/internal/types"
|
||||
@@ -47,10 +50,9 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
|
||||
}
|
||||
|
||||
// 1. 必须提供邀请码,用户不能自主成为代理
|
||||
if req.InviteCode == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("必须提供邀请码才能成为代理,请联系平台或代理获取邀请码"), "")
|
||||
}
|
||||
if req.Referrer == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("请填写邀请信息"), "")
|
||||
}
|
||||
|
||||
// 2. 校验验证码(开发环境下跳过验证码校验)
|
||||
if os.Getenv("ENV") != "development" {
|
||||
@@ -67,7 +69,7 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
}
|
||||
}
|
||||
|
||||
var userID int64
|
||||
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})
|
||||
@@ -88,15 +90,11 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
// 用户已存在
|
||||
if claims != nil && claims.UserType == model.UserTypeTemp {
|
||||
// 临时用户,检查手机号是否已绑定其他微信号
|
||||
userTemp, err := l.svcCtx.UserTempModel.FindOne(l.ctx, claims.UserId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询临时用户失败, %v", err)
|
||||
userAuth, findUserAuthErr := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, user.Id, claims.AuthType)
|
||||
if findUserAuthErr != nil && !errors.Is(findUserAuthErr, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户认证失败, %v", findUserAuthErr)
|
||||
}
|
||||
userAuth, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, user.Id, userTemp.AuthType)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户认证失败, %v", err)
|
||||
}
|
||||
if userAuth != nil && userAuth.AuthKey != userTemp.AuthKey {
|
||||
if userAuth != nil && userAuth.AuthKey != claims.AuthKey {
|
||||
return errors.Wrapf(xerr.NewErrMsg("该手机号已绑定其他微信号"), "")
|
||||
}
|
||||
// 临时用户,转为正式用户
|
||||
@@ -117,40 +115,53 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
return errors.Wrapf(xerr.NewErrMsg("您已经是代理"), "")
|
||||
}
|
||||
|
||||
// 4. 必须通过邀请码成为代理(没有其他途径)
|
||||
// 4.1 查询邀请码
|
||||
inviteCodeModel, err := l.svcCtx.AgentInviteCodeModel.FindOneByCode(transCtx, req.InviteCode)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrMsg("邀请码不存在"), "")
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询邀请码失败, %v", err)
|
||||
}
|
||||
var inviteCodeModel *model.AgentInviteCode
|
||||
var parentAgentId string
|
||||
var targetLevel int64
|
||||
|
||||
// 4.2 验证邀请码状态
|
||||
// 钻石级别的邀请码只能使用一次,使用后立即失效
|
||||
// 普通级别的邀请码可以无限使用
|
||||
if inviteCodeModel.Status != 0 {
|
||||
if inviteCodeModel.Status == 1 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("邀请码已使用"), "")
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrMsg("邀请码已失效"), "")
|
||||
}
|
||||
|
||||
// 4.3 验证邀请码是否过期
|
||||
if inviteCodeModel.ExpireTime.Valid && inviteCodeModel.ExpireTime.Time.Before(time.Now()) {
|
||||
return errors.Wrapf(xerr.NewErrMsg("邀请码已过期"), "")
|
||||
}
|
||||
|
||||
// 4.4 获取邀请码信息
|
||||
targetLevel := inviteCodeModel.TargetLevel
|
||||
var parentAgentId int64 = 0
|
||||
if inviteCodeModel.AgentId.Valid {
|
||||
parentAgentId = inviteCodeModel.AgentId.Int64
|
||||
}
|
||||
inviteCodeModel, err = l.svcCtx.AgentInviteCodeModel.FindOneByCode(transCtx, req.Referrer)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询邀请码失败, %v", err)
|
||||
}
|
||||
if inviteCodeModel != nil {
|
||||
if inviteCodeModel.Status != 0 {
|
||||
if inviteCodeModel.Status == 1 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("邀请码已使用"), "")
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrMsg("邀请码已失效"), "")
|
||||
}
|
||||
if inviteCodeModel.ExpireTime.Valid && inviteCodeModel.ExpireTime.Time.Before(time.Now()) {
|
||||
return errors.Wrapf(xerr.NewErrMsg("邀请码已过期"), "")
|
||||
}
|
||||
targetLevel = inviteCodeModel.TargetLevel
|
||||
if inviteCodeModel.AgentId.Valid {
|
||||
parentAgentId = inviteCodeModel.AgentId.String
|
||||
}
|
||||
} else {
|
||||
if codeVal, parseErr := strconv.ParseInt(req.Referrer, 10, 64); parseErr == nil && codeVal > 0 {
|
||||
parentAgent, err := l.findAgentByCode(transCtx, codeVal)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parentAgentId = parentAgent.Id
|
||||
targetLevel = 1
|
||||
} else {
|
||||
encRefMobile, _ := crypto.EncryptMobile(req.Referrer, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
agents, findErr := l.svcCtx.AgentModel.FindAll(transCtx, l.svcCtx.AgentModel.SelectBuilder().Where("mobile = ? AND del_state = ?", encRefMobile, globalkey.DelStateNo).Limit(1), "")
|
||||
if findErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上级代理失败, %v", findErr)
|
||||
}
|
||||
if len(agents) == 0 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("邀请信息无效"), "")
|
||||
}
|
||||
parentAgentId = agents[0].Id
|
||||
targetLevel = 1
|
||||
}
|
||||
}
|
||||
|
||||
// 4.5 创建代理记录
|
||||
newAgent := &model.Agent{
|
||||
Id: uuid.NewString(),
|
||||
UserId: userID,
|
||||
Level: targetLevel,
|
||||
Mobile: encryptedMobile,
|
||||
@@ -160,7 +171,7 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
}
|
||||
|
||||
// 4.6 处理上级关系
|
||||
if parentAgentId > 0 {
|
||||
if parentAgentId != "" {
|
||||
// 代理发放的邀请码,成为该代理的下级
|
||||
parentAgent, err := l.svcCtx.AgentModel.FindOne(transCtx, parentAgentId)
|
||||
if err != nil {
|
||||
@@ -177,23 +188,23 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "查找团队首领失败")
|
||||
}
|
||||
if teamLeaderId > 0 {
|
||||
newAgent.TeamLeaderId = sql.NullInt64{Int64: teamLeaderId, Valid: true}
|
||||
if teamLeaderId != "" {
|
||||
newAgent.TeamLeaderId = sql.NullString{String: teamLeaderId, Valid: true}
|
||||
}
|
||||
|
||||
// 先插入代理记录
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
newAgent.AgentCode = 0
|
||||
_, err = l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentId, _ := agentResult.LastInsertId()
|
||||
newAgent.Id = agentId
|
||||
// 已设置newAgent.Id为UUID
|
||||
|
||||
// 建立关系
|
||||
relation := &model.AgentRelation{
|
||||
Id: uuid.NewString(),
|
||||
ParentId: parentAgent.Id,
|
||||
ChildId: agentId,
|
||||
RelationType: 1, // 直接关系
|
||||
ChildId: newAgent.Id,
|
||||
RelationType: 1,
|
||||
}
|
||||
if _, err := l.svcCtx.AgentRelationModel.Insert(transCtx, session, relation); err != nil {
|
||||
return errors.Wrapf(err, "建立代理关系失败")
|
||||
@@ -201,29 +212,28 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
} else {
|
||||
// 平台发放的钻石邀请码,独立成团队
|
||||
if targetLevel == 3 {
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
newAgent.AgentCode = 0
|
||||
_, err = l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentId, _ := agentResult.LastInsertId()
|
||||
newAgent.Id = agentId
|
||||
|
||||
// 设置自己为团队首领
|
||||
newAgent.TeamLeaderId = sql.NullInt64{Int64: agentId, Valid: true}
|
||||
newAgent.TeamLeaderId = sql.NullString{String: newAgent.Id, Valid: true}
|
||||
if err := l.svcCtx.AgentModel.UpdateInTransaction(transCtx, session, newAgent); err != nil {
|
||||
return errors.Wrapf(err, "更新团队首领失败")
|
||||
}
|
||||
} else {
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
newAgent.AgentCode = 0
|
||||
_, err = l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
_, _ = agentResult.LastInsertId()
|
||||
}
|
||||
}
|
||||
|
||||
// 4.7 初始化钱包
|
||||
wallet := &model.AgentWallet{
|
||||
Id: uuid.NewString(),
|
||||
AgentId: newAgent.Id,
|
||||
}
|
||||
if _, err := l.svcCtx.AgentWalletModel.Insert(transCtx, session, wallet); err != nil {
|
||||
@@ -238,15 +248,18 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
inviteCodeModel.Status = 1 // 已使用(使用后立即失效)
|
||||
}
|
||||
// 记录使用信息(用于统计,普通邀请码可以多次使用)
|
||||
inviteCodeModel.UsedUserId = sql.NullInt64{Int64: userID, Valid: true}
|
||||
inviteCodeModel.UsedAgentId = sql.NullInt64{Int64: newAgent.Id, Valid: true}
|
||||
inviteCodeModel.UsedTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if err := l.svcCtx.AgentInviteCodeModel.UpdateWithVersion(transCtx, session, inviteCodeModel); err != nil {
|
||||
return errors.Wrapf(err, "更新邀请码状态失败")
|
||||
inviteCodeModel.UsedUserId = sql.NullString{String: userID, Valid: true}
|
||||
if inviteCodeModel != nil {
|
||||
inviteCodeModel.UsedAgentId = sql.NullString{String: newAgent.Id, Valid: true}
|
||||
inviteCodeModel.UsedTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if err := l.svcCtx.AgentInviteCodeModel.UpdateWithVersion(transCtx, session, inviteCodeModel); err != nil {
|
||||
return errors.Wrapf(err, "更新邀请码状态失败")
|
||||
}
|
||||
}
|
||||
|
||||
// 4.9 记录邀请码使用历史(用于统计和查询)
|
||||
usage := &model.AgentInviteCodeUsage{
|
||||
Id: uuid.NewString(),
|
||||
InviteCodeId: inviteCodeModel.Id,
|
||||
Code: inviteCodeModel.Code,
|
||||
UserId: userID,
|
||||
@@ -272,15 +285,46 @@ func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *type
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
func (l *ApplyForAgentLogic) findAgentByCode(ctx context.Context, code int64) (*model.Agent, error) {
|
||||
builder := l.svcCtx.AgentModel.SelectBuilder().Where("agent_code = ? AND del_state = ?", code, globalkey.DelStateNo).Limit(1)
|
||||
agents, err := l.svcCtx.AgentModel.FindAll(ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上级代理失败, %v", err)
|
||||
}
|
||||
if len(agents) == 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("上级邀请码不存在"), "")
|
||||
}
|
||||
return agents[0], nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// findTeamLeader 查找团队首领(钻石代理)
|
||||
func (l *ApplyForAgentLogic) findTeamLeader(ctx context.Context, agentId int64) (int64, error) {
|
||||
func (l *ApplyForAgentLogic) findTeamLeader(ctx context.Context, agentId string) (string, error) {
|
||||
currentId := agentId
|
||||
maxDepth := 100
|
||||
depth := 0
|
||||
@@ -290,22 +334,22 @@ func (l *ApplyForAgentLogic) findTeamLeader(ctx context.Context, agentId int64)
|
||||
Where("child_id = ? AND relation_type = ? AND del_state = ?", currentId, 1, 0)
|
||||
relations, err := l.svcCtx.AgentRelationModel.FindAll(ctx, builder, "")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
if len(relations) == 0 {
|
||||
agent, err := l.svcCtx.AgentModel.FindOne(ctx, currentId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
if agent.Level == 3 {
|
||||
return agent.Id, nil
|
||||
}
|
||||
return 0, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
parentAgent, err := l.svcCtx.AgentModel.FindOne(ctx, relations[0].ParentId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if parentAgent.Level == 3 {
|
||||
@@ -316,5 +360,5 @@ func (l *ApplyForAgentLogic) findTeamLeader(ctx context.Context, agentId int64)
|
||||
depth++
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/xerr"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
@@ -64,7 +65,7 @@ func (l *ApplyUpgradeLogic) ApplyUpgrade(req *types.ApplyUpgradeReq) (resp *type
|
||||
}
|
||||
|
||||
// 4. 查找原直接上级(用于返佣)
|
||||
var rebateAgentId int64
|
||||
var rebateAgentId string
|
||||
parent, err := l.svcCtx.AgentService.FindDirectParent(l.ctx, agent.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(err, "查找直接上级失败")
|
||||
@@ -74,10 +75,11 @@ func (l *ApplyUpgradeLogic) ApplyUpgrade(req *types.ApplyUpgradeReq) (resp *type
|
||||
}
|
||||
|
||||
// 5. 创建升级记录(待支付状态)
|
||||
var upgradeId int64
|
||||
var upgradeId string
|
||||
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 5.1 创建升级记录(状态为待支付)
|
||||
upgradeRecord := &model.AgentUpgrade{
|
||||
Id: uuid.NewString(),
|
||||
AgentId: agent.Id,
|
||||
FromLevel: fromLevel,
|
||||
ToLevel: toLevel,
|
||||
@@ -86,17 +88,16 @@ func (l *ApplyUpgradeLogic) ApplyUpgrade(req *types.ApplyUpgradeReq) (resp *type
|
||||
RebateAmount: rebateAmount,
|
||||
Status: 1, // 待支付(1=待支付,2=已支付,3=已完成,4=已取消)
|
||||
}
|
||||
if rebateAgentId > 0 {
|
||||
upgradeRecord.RebateAgentId = sql.NullInt64{Int64: rebateAgentId, Valid: true}
|
||||
if rebateAgentId != "" {
|
||||
upgradeRecord.RebateAgentId = sql.NullString{String: rebateAgentId, Valid: true}
|
||||
}
|
||||
|
||||
upgradeResult, err := l.svcCtx.AgentUpgradeModel.Insert(transCtx, session, upgradeRecord)
|
||||
_, err := l.svcCtx.AgentUpgradeModel.Insert(transCtx, session, upgradeRecord)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建升级记录失败")
|
||||
}
|
||||
upgradeId, _ = upgradeResult.LastInsertId()
|
||||
|
||||
// 注意:升级操作将在支付成功后通过支付回调完成
|
||||
upgradeId = upgradeRecord.Id
|
||||
return nil
|
||||
})
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
@@ -88,7 +89,7 @@ func (l *ApplyWithdrawalLogic) ApplyWithdrawal(req *types.ApplyWithdrawalReq) (r
|
||||
withdrawNo := fmt.Sprintf("WD%d%d", time.Now().Unix(), agent.Id)
|
||||
|
||||
// 8. 使用事务处理提现申请
|
||||
var withdrawalId int64
|
||||
var withdrawalId string
|
||||
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 8.1 冻结余额
|
||||
wallet.FrozenBalance += req.Amount
|
||||
@@ -99,6 +100,7 @@ func (l *ApplyWithdrawalLogic) ApplyWithdrawal(req *types.ApplyWithdrawalReq) (r
|
||||
|
||||
// 8.2 创建提现记录
|
||||
withdrawal := &model.AgentWithdrawal{
|
||||
Id: uuid.New().String(),
|
||||
AgentId: agent.Id,
|
||||
WithdrawNo: withdrawNo,
|
||||
PayeeAccount: req.PayeeAccount,
|
||||
@@ -109,11 +111,11 @@ func (l *ApplyWithdrawalLogic) ApplyWithdrawal(req *types.ApplyWithdrawalReq) (r
|
||||
Status: 1, // 处理中(待审核)
|
||||
}
|
||||
|
||||
withdrawalResult, err := l.svcCtx.AgentWithdrawalModel.Insert(transCtx, session, withdrawal)
|
||||
_, err := l.svcCtx.AgentWithdrawalModel.Insert(transCtx, session, withdrawal)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建提现记录失败")
|
||||
}
|
||||
withdrawalId, _ = withdrawalResult.LastInsertId()
|
||||
withdrawalId = withdrawal.Id
|
||||
|
||||
// 8.3 创建扣税记录
|
||||
taxRecord := &model.AgentWithdrawalTax{
|
||||
@@ -154,7 +156,7 @@ type TaxInfo struct {
|
||||
}
|
||||
|
||||
// calculateTax 计算税费
|
||||
func (l *ApplyWithdrawalLogic) calculateTax(ctx context.Context, agentId int64, amount float64, yearMonth int64) (*TaxInfo, error) {
|
||||
func (l *ApplyWithdrawalLogic) calculateTax(ctx context.Context, agentId string, amount float64, yearMonth int64) (*TaxInfo, error) {
|
||||
// 获取税率配置(默认6%)
|
||||
taxRate := 0.06
|
||||
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(ctx, "tax_rate")
|
||||
|
||||
@@ -53,7 +53,7 @@ func (l *DeleteInviteCodeLogic) DeleteInviteCode(req *types.DeleteInviteCodeReq)
|
||||
}
|
||||
|
||||
// 3. 验证邀请码是否属于当前代理
|
||||
if !inviteCode.AgentId.Valid || inviteCode.AgentId.Int64 != agent.Id {
|
||||
if !inviteCode.AgentId.Valid || inviteCode.AgentId.String != agent.Id {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("无权删除此邀请码"), "")
|
||||
}
|
||||
|
||||
|
||||
@@ -87,9 +87,9 @@ func (l *GenerateInviteCodeLogic) GenerateInviteCode(req *types.GenerateInviteCo
|
||||
// 创建邀请码记录
|
||||
inviteCode := &model.AgentInviteCode{
|
||||
Code: code,
|
||||
AgentId: sql.NullInt64{Int64: agent.Id, Valid: true},
|
||||
AgentId: sql.NullString{String: agent.Id, Valid: true},
|
||||
TargetLevel: 1, // 代理发放的邀请码,目标等级为普通代理
|
||||
Status: 0, // 未使用
|
||||
Status: 0, // 未使用
|
||||
ExpireTime: expireTime,
|
||||
Remark: sql.NullString{String: req.Remark, Valid: req.Remark != ""},
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"ycc-server/app/main/api/internal/svc"
|
||||
"ycc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
@@ -69,15 +70,17 @@ func (l *GeneratingLinkLogic) GeneratingLink(req *types.AgentGeneratingLinkReq)
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取等级加成配置失败, %v", err)
|
||||
}
|
||||
|
||||
// 4. 计算实际底价(产品基础底价 + 等级加成)
|
||||
basePrice := productConfig.BasePrice
|
||||
actualBasePrice := basePrice + float64(levelBonus)
|
||||
systemMaxPrice := productConfig.SystemMaxPrice
|
||||
|
||||
// 5. 验证设定价格范围
|
||||
if req.SetPrice < actualBasePrice || req.SetPrice > systemMaxPrice {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("设定价格必须在 %.2f 到 %.2f 之间"), "设定价格必须在 %.2f 到 %.2f 之间", actualBasePrice, systemMaxPrice)
|
||||
}
|
||||
basePrice := productConfig.BasePrice
|
||||
actualBasePrice := basePrice + float64(levelBonus)
|
||||
systemMaxPrice := productConfig.SystemMaxPrice
|
||||
upliftAmount, err := l.getLevelMaxUpliftAmount(agentModel.Level)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取等级上调金额失败, %v", err)
|
||||
}
|
||||
levelMaxPrice := systemMaxPrice + upliftAmount
|
||||
if req.SetPrice < actualBasePrice || req.SetPrice > levelMaxPrice {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("设定价格必须在 %.2f 到 %.2f 之间"), "设定价格必须在 %.2f 到 %.2f 之间", actualBasePrice, levelMaxPrice)
|
||||
}
|
||||
|
||||
// 6. 检查是否已存在相同的链接(同一代理、同一产品、同一价格)
|
||||
builder := l.svcCtx.AgentLinkModel.SelectBuilder().Where(squirrel.And{
|
||||
@@ -98,7 +101,7 @@ func (l *GeneratingLinkLogic) GeneratingLink(req *types.AgentGeneratingLinkReq)
|
||||
if targetPath == "" {
|
||||
targetPath = "/agent/promotionInquire/"
|
||||
}
|
||||
shortLink, err := l.getOrCreateShortLink(1, existingLinks[0].Id, 0, existingLinks[0].LinkIdentifier, "", targetPath)
|
||||
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)
|
||||
}
|
||||
@@ -132,6 +135,7 @@ func (l *GeneratingLinkLogic) GeneratingLink(req *types.AgentGeneratingLinkReq)
|
||||
|
||||
// 9. 保存推广链接
|
||||
agentLink := &model.AgentLink{
|
||||
// Id: uuid.NewString(),
|
||||
AgentId: agentModel.Id,
|
||||
UserId: userID,
|
||||
ProductId: req.ProductId,
|
||||
@@ -140,16 +144,12 @@ func (l *GeneratingLinkLogic) GeneratingLink(req *types.AgentGeneratingLinkReq)
|
||||
ActualBasePrice: actualBasePrice,
|
||||
}
|
||||
|
||||
result, err := l.svcCtx.AgentLinkModel.Insert(l.ctx, nil, agentLink)
|
||||
_, err = l.svcCtx.AgentLinkModel.Insert(l.ctx, nil, agentLink)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "保存推广链接失败, %v", err)
|
||||
}
|
||||
|
||||
// 获取插入的ID
|
||||
linkId, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取推广链接ID失败, %v", err)
|
||||
}
|
||||
linkId := agentLink.Id
|
||||
|
||||
// 使用默认target_path(如果未提供)
|
||||
targetPath := req.TargetPath
|
||||
@@ -158,7 +158,7 @@ func (l *GeneratingLinkLogic) GeneratingLink(req *types.AgentGeneratingLinkReq)
|
||||
}
|
||||
|
||||
// 生成短链(类型:1=推广报告)
|
||||
shortLink, err := l.createShortLink(1, linkId, 0, encrypted, "", targetPath)
|
||||
shortLink, err := l.createShortLink(1, linkId, "", encrypted, "", targetPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成短链失败, %v", err)
|
||||
}
|
||||
@@ -205,6 +205,30 @@ func (l *GeneratingLinkLogic) getLevelBonus(level int64) (int64, error) {
|
||||
return int64(value), nil
|
||||
}
|
||||
|
||||
func (l *GeneratingLinkLogic) getLevelMaxUpliftAmount(level int64) (float64, error) {
|
||||
var key string
|
||||
switch level {
|
||||
case 2:
|
||||
key = "gold_max_uplift_amount"
|
||||
case 3:
|
||||
key = "diamond_max_uplift_amount"
|
||||
default:
|
||||
return 0, nil
|
||||
}
|
||||
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, key)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
v, err := strconv.ParseFloat(config.ConfigValue, 64)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
if v < 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// getOrCreateShortLink 获取或创建短链
|
||||
// type: 1=推广报告(promotion), 2=邀请好友(invite)
|
||||
// linkId: 推广链接ID(仅推广报告使用)
|
||||
@@ -212,20 +236,20 @@ func (l *GeneratingLinkLogic) getLevelBonus(level int64) (int64, error) {
|
||||
// linkIdentifier: 推广链接标识(仅推广报告使用)
|
||||
// inviteCode: 邀请码(仅邀请好友使用)
|
||||
// targetPath: 目标地址(前端传入)
|
||||
func (l *GeneratingLinkLogic) getOrCreateShortLink(linkType int64, linkId, inviteCodeId int64, linkIdentifier, inviteCode, targetPath string) (string, error) {
|
||||
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 > 0 {
|
||||
existingShortLink, err = l.svcCtx.AgentShortLinkModel.FindOneByLinkIdTypeDelState(l.ctx, sql.NullInt64{Int64: linkId, Valid: true}, linkType, globalkey.DelStateNo)
|
||||
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 > 0 {
|
||||
existingShortLink, err = l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeIdTypeDelState(l.ctx, sql.NullInt64{Int64: inviteCodeId, Valid: true}, linkType, globalkey.DelStateNo)
|
||||
if inviteCodeId != "" {
|
||||
existingShortLink, err = l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeIdTypeDelState(l.ctx, sql.NullString{String: inviteCodeId, Valid: true}, linkType, globalkey.DelStateNo)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,7 +268,7 @@ func (l *GeneratingLinkLogic) getOrCreateShortLink(linkType int64, linkId, invit
|
||||
|
||||
// createShortLink 创建短链
|
||||
// type: 1=推广报告(promotion), 2=邀请好友(invite)
|
||||
func (l *GeneratingLinkLogic) createShortLink(linkType int64, linkId, inviteCodeId int64, linkIdentifier, inviteCode, targetPath string) (string, error) {
|
||||
func (l *GeneratingLinkLogic) createShortLink(linkType int64, linkId, inviteCodeId string, linkIdentifier, inviteCode, targetPath string) (string, error) {
|
||||
promotionConfig := l.svcCtx.Config.Promotion
|
||||
|
||||
// 如果没有配置推广域名,返回空字符串(保持向后兼容)
|
||||
@@ -291,6 +315,7 @@ func (l *GeneratingLinkLogic) createShortLink(linkType int64, linkId, inviteCode
|
||||
|
||||
// 创建短链记录
|
||||
shortLink := &model.AgentShortLink{
|
||||
Id: uuid.NewString(),
|
||||
Type: linkType,
|
||||
ShortCode: shortCode,
|
||||
TargetPath: targetPath,
|
||||
@@ -300,13 +325,13 @@ func (l *GeneratingLinkLogic) createShortLink(linkType int64, linkId, inviteCode
|
||||
// 根据类型设置对应字段
|
||||
if linkType == 1 {
|
||||
// 推广报告类型
|
||||
shortLink.LinkId = sql.NullInt64{Int64: linkId, Valid: linkId > 0}
|
||||
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.NullInt64{Int64: inviteCodeId, Valid: inviteCodeId > 0}
|
||||
shortLink.InviteCodeId = sql.NullString{String: inviteCodeId, Valid: inviteCodeId != ""}
|
||||
if inviteCode != "" {
|
||||
shortLink.InviteCode = sql.NullString{String: inviteCode, Valid: true}
|
||||
}
|
||||
|
||||
@@ -40,14 +40,14 @@ func (l *GetAgentInfoLogic) GetAgentInfo() (resp *types.AgentInfoResp, err error
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
// 不是代理,返回空信息
|
||||
return &types.AgentInfoResp{
|
||||
AgentId: 0,
|
||||
Level: 0,
|
||||
LevelName: "",
|
||||
Region: "",
|
||||
Mobile: "",
|
||||
WechatId: "",
|
||||
TeamLeaderId: 0,
|
||||
IsRealName: false,
|
||||
AgentId: "",
|
||||
Level: 0,
|
||||
LevelName: "",
|
||||
Region: "",
|
||||
Mobile: "",
|
||||
WechatId: "",
|
||||
TeamLeaderId: "",
|
||||
IsRealName: false,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
@@ -76,9 +76,9 @@ func (l *GetAgentInfoLogic) GetAgentInfo() (resp *types.AgentInfoResp, err error
|
||||
}
|
||||
|
||||
// 获取团队首领ID
|
||||
teamLeaderId := int64(0)
|
||||
teamLeaderId := ""
|
||||
if agent.TeamLeaderId.Valid {
|
||||
teamLeaderId = agent.TeamLeaderId.Int64
|
||||
teamLeaderId = agent.TeamLeaderId.String
|
||||
}
|
||||
|
||||
// 获取区域
|
||||
@@ -88,14 +88,15 @@ func (l *GetAgentInfoLogic) GetAgentInfo() (resp *types.AgentInfoResp, err error
|
||||
}
|
||||
|
||||
return &types.AgentInfoResp{
|
||||
AgentId: agent.Id,
|
||||
Level: agent.Level,
|
||||
LevelName: l.getLevelName(agent.Level),
|
||||
Region: region,
|
||||
Mobile: mobile,
|
||||
WechatId: wechatId,
|
||||
TeamLeaderId: teamLeaderId,
|
||||
IsRealName: isRealName,
|
||||
AgentId: agent.Id,
|
||||
Level: agent.Level,
|
||||
LevelName: l.getLevelName(agent.Level),
|
||||
Region: region,
|
||||
Mobile: mobile,
|
||||
WechatId: wechatId,
|
||||
TeamLeaderId: teamLeaderId,
|
||||
IsRealName: isRealName,
|
||||
AgentCode: agent.AgentCode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -78,9 +78,9 @@ func (l *GetAgentProductConfigLogic) GetAgentProductConfig() (resp *types.AgentP
|
||||
// 计算该产品的实际底价
|
||||
productActualBasePrice := productBasePrice + float64(levelBonus)
|
||||
|
||||
// 价格范围:实际底价 ≤ 设定价格 ≤ 产品配置的最高价格
|
||||
priceRangeMin := productActualBasePrice
|
||||
priceRangeMax := productConfig.SystemMaxPrice
|
||||
priceRangeMin := productActualBasePrice
|
||||
upliftAmount, _ := l.getLevelMaxUpliftAmount(agentModel.Level)
|
||||
priceRangeMax := productConfig.SystemMaxPrice + upliftAmount
|
||||
|
||||
// 使用产品配置的提价阈值和手续费比例,如果为NULL则使用0
|
||||
productPriceThreshold := 0.0
|
||||
@@ -144,3 +144,27 @@ func (l *GetAgentProductConfigLogic) getLevelBonus(level int64) (int64, error) {
|
||||
}
|
||||
return int64(value), nil
|
||||
}
|
||||
|
||||
func (l *GetAgentProductConfigLogic) getLevelMaxUpliftAmount(level int64) (float64, error) {
|
||||
var key string
|
||||
switch level {
|
||||
case 2:
|
||||
key = "gold_max_uplift_amount"
|
||||
case 3:
|
||||
key = "diamond_max_uplift_amount"
|
||||
default:
|
||||
return 0, nil
|
||||
}
|
||||
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, key)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
v, err := strconv.ParseFloat(config.ConfigValue, 64)
|
||||
if err != nil {
|
||||
return 0, nil
|
||||
}
|
||||
if v < 0 {
|
||||
return 0, nil
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ func (l *GetCommissionListLogic) GetCommissionList(req *types.GetCommissionListR
|
||||
for _, commission := range commissions {
|
||||
// 查询产品名称
|
||||
productName := ""
|
||||
if commission.ProductId > 0 {
|
||||
if commission.ProductId != "" {
|
||||
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, commission.ProductId)
|
||||
if err == nil {
|
||||
productName = product.ProductName
|
||||
@@ -87,7 +87,7 @@ func (l *GetCommissionListLogic) GetCommissionList(req *types.GetCommissionListR
|
||||
|
||||
// 查询订单号
|
||||
orderNo := ""
|
||||
if commission.OrderId > 0 {
|
||||
if commission.OrderId != "" {
|
||||
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, commission.OrderId)
|
||||
if err == nil {
|
||||
orderNo = order.OrderNo
|
||||
|
||||
@@ -59,7 +59,7 @@ func (l *GetConversionRateLogic) GetConversionRate() (resp *types.ConversionRate
|
||||
}
|
||||
|
||||
// calculateSubordinateConversionRate 计算下级转化率(考虑历史关系)
|
||||
func (l *GetConversionRateLogic) calculateSubordinateConversionRate(parentAgentId int64) types.ConversionRateData {
|
||||
func (l *GetConversionRateLogic) calculateSubordinateConversionRate(parentAgentId string) types.ConversionRateData {
|
||||
// 使用Asia/Shanghai时区,与数据库保持一致
|
||||
loc, _ := time.LoadLocation("Asia/Shanghai")
|
||||
now := time.Now().In(loc)
|
||||
@@ -120,7 +120,7 @@ func (l *GetConversionRateLogic) calculateSubordinateConversionRate(parentAgentI
|
||||
|
||||
// calculateConversionRate 计算转化率
|
||||
// agentId > 0 时统计该代理的转化率,否则统计 subordinateIds 列表的转化率
|
||||
func (l *GetConversionRateLogic) calculateConversionRate(agentId int64, subordinateIds []int64) types.ConversionRateData {
|
||||
func (l *GetConversionRateLogic) calculateConversionRate(agentId string, subordinateIds []string) types.ConversionRateData {
|
||||
// 使用Asia/Shanghai时区,与数据库保持一致
|
||||
loc, _ := time.LoadLocation("Asia/Shanghai")
|
||||
now := time.Now().In(loc)
|
||||
@@ -182,13 +182,13 @@ func (l *GetConversionRateLogic) calculateConversionRate(agentId int64, subordin
|
||||
}
|
||||
|
||||
// calculatePeriodConversion 计算指定时间段的转化率数据
|
||||
func (l *GetConversionRateLogic) calculatePeriodConversion(agentId int64, subordinateIds []int64, periodLabel string, startTime, endTime time.Time) types.PeriodConversionData {
|
||||
func (l *GetConversionRateLogic) calculatePeriodConversion(agentId string, subordinateIds []string, periodLabel string, startTime, endTime time.Time) types.PeriodConversionData {
|
||||
// 构建 agent_order 查询条件
|
||||
agentOrderBuilder := l.svcCtx.AgentOrderModel.SelectBuilder().
|
||||
Where("del_state = ?", globalkey.DelStateNo).
|
||||
Where("create_time >= ? AND create_time < ?", startTime, endTime)
|
||||
|
||||
if agentId > 0 {
|
||||
if agentId != "" {
|
||||
// 统计我的转化率
|
||||
agentOrderBuilder = agentOrderBuilder.Where("agent_id = ?", agentId)
|
||||
} else if len(subordinateIds) > 0 {
|
||||
@@ -208,7 +208,7 @@ func (l *GetConversionRateLogic) calculatePeriodConversion(agentId int64, subord
|
||||
}
|
||||
|
||||
// 添加调试日志
|
||||
if agentId == 0 && len(subordinateIds) > 0 {
|
||||
if agentId == "" && len(subordinateIds) > 0 {
|
||||
l.Infof("calculatePeriodConversion: 统计下级转化率,periodLabel=%s, startTime=%v, endTime=%v, subordinateIds数量=%d",
|
||||
periodLabel, startTime, endTime, len(subordinateIds))
|
||||
}
|
||||
@@ -228,7 +228,7 @@ func (l *GetConversionRateLogic) calculatePeriodConversion(agentId int64, subord
|
||||
}
|
||||
|
||||
if len(agentOrders) == 0 {
|
||||
if agentId == 0 && len(subordinateIds) > 0 {
|
||||
if agentId == "" && len(subordinateIds) > 0 {
|
||||
l.Infof("calculatePeriodConversion: 未找到代理订单,periodLabel=%s, startTime=%v, endTime=%v",
|
||||
periodLabel, startTime, endTime)
|
||||
}
|
||||
@@ -245,7 +245,7 @@ func (l *GetConversionRateLogic) calculatePeriodConversion(agentId int64, subord
|
||||
l.Infof("calculatePeriodConversion: 找到代理订单数量=%d, periodLabel=%s", len(agentOrders), periodLabel)
|
||||
|
||||
// 收集订单ID
|
||||
orderIds := make([]int64, 0, len(agentOrders))
|
||||
orderIds := make([]string, 0, len(agentOrders))
|
||||
for _, ao := range agentOrders {
|
||||
orderIds = append(orderIds, ao.OrderId)
|
||||
}
|
||||
@@ -271,8 +271,8 @@ func (l *GetConversionRateLogic) calculatePeriodConversion(agentId int64, subord
|
||||
// 统计查询订单数、付费订单数、用户数和总金额
|
||||
var totalAmount float64
|
||||
paidOrderCount := 0
|
||||
queryUserSet := make(map[int64]bool)
|
||||
paidUserSet := make(map[int64]bool)
|
||||
queryUserSet := make(map[string]bool)
|
||||
paidUserSet := make(map[string]bool)
|
||||
|
||||
for _, order := range orders {
|
||||
// 查询用户数(所有订单的用户,去重)
|
||||
@@ -303,7 +303,7 @@ func (l *GetConversionRateLogic) calculatePeriodConversion(agentId int64, subord
|
||||
// 结合使用agent_rebate表和agent_order表:
|
||||
// 1. 查询量:通过agent_order表统计所有查询(包括未付费的)
|
||||
// 2. 付费量和金额:通过agent_rebate表统计(只有付费的订单才会产生返佣)
|
||||
func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgentId int64, periodLabel string, startTime, endTime time.Time) types.PeriodConversionData {
|
||||
func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgentId string, periodLabel string, startTime, endTime time.Time) types.PeriodConversionData {
|
||||
// 1. 查询agent_rebate表:获取所有曾经给当前用户产生返佣的source_agent_id(这些代理在某个时间点是下级)
|
||||
// 不限制时间,获取所有历史返佣记录,用于确定哪些代理曾经是下级
|
||||
rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
|
||||
@@ -324,9 +324,9 @@ func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgen
|
||||
}
|
||||
|
||||
// 收集所有曾经产生返佣的source_agent_id(这些代理在某个时间点是下级)
|
||||
sourceAgentIdSet := make(map[int64]bool)
|
||||
paidOrderIdSet := make(map[int64]bool) // 已付费的订单ID(有返佣的订单)
|
||||
paidOrderIdToAmount := make(map[int64]float64) // 已付费订单的金额
|
||||
sourceAgentIdSet := make(map[string]bool)
|
||||
paidOrderIdSet := make(map[string]bool) // 已付费的订单ID(有返佣的订单)
|
||||
paidOrderIdToAmount := make(map[string]float64) // 已付费订单的金额
|
||||
|
||||
for _, rebate := range allRebates {
|
||||
sourceAgentIdSet[rebate.SourceAgentId] = true
|
||||
@@ -351,7 +351,7 @@ func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgen
|
||||
}
|
||||
}
|
||||
|
||||
sourceAgentIds := make([]int64, 0, len(sourceAgentIdSet))
|
||||
sourceAgentIds := make([]string, 0, len(sourceAgentIdSet))
|
||||
for agentId := range sourceAgentIdSet {
|
||||
sourceAgentIds = append(sourceAgentIds, agentId)
|
||||
}
|
||||
@@ -391,8 +391,8 @@ func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgen
|
||||
}
|
||||
|
||||
// 3. 通过order_id去重,获取所有订单ID(用于查询订单详情)
|
||||
orderIdSet := make(map[int64]bool)
|
||||
orderIdToAgentOrder := make(map[int64]*model.AgentOrder)
|
||||
orderIdSet := make(map[string]bool)
|
||||
orderIdToAgentOrder := make(map[string]*model.AgentOrder)
|
||||
|
||||
for _, ao := range agentOrders {
|
||||
orderIdSet[ao.OrderId] = true
|
||||
@@ -406,7 +406,7 @@ func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgen
|
||||
}
|
||||
}
|
||||
|
||||
orderIds := make([]int64, 0, len(orderIdSet))
|
||||
orderIds := make([]string, 0, len(orderIdSet))
|
||||
for orderId := range orderIdSet {
|
||||
orderIds = append(orderIds, orderId)
|
||||
}
|
||||
@@ -459,8 +459,8 @@ func (l *GetConversionRateLogic) calculateSubordinatePeriodConversion(parentAgen
|
||||
// 6. 统计查询订单数、付费订单数、用户数和总金额
|
||||
var totalAmount float64
|
||||
paidOrderCount := 0
|
||||
queryUserSet := make(map[int64]bool)
|
||||
paidUserSet := make(map[int64]bool)
|
||||
queryUserSet := make(map[string]bool)
|
||||
paidUserSet := make(map[string]bool)
|
||||
|
||||
for _, order := range orders {
|
||||
// 查询用户数(所有订单的用户,去重)
|
||||
|
||||
@@ -4,18 +4,22 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"ycc-server/common/tool"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"ycc-server/app/main/api/internal/svc"
|
||||
"ycc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
@@ -53,41 +57,51 @@ func (l *GetInviteLinkLogic) GetInviteLink(req *types.GetInviteLinkReq) (resp *t
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码不能为空"), "")
|
||||
}
|
||||
|
||||
// 3. 查询邀请码是否存在且属于当前代理
|
||||
inviteCodeRecord, err := l.svcCtx.AgentInviteCodeModel.FindOneByCode(l.ctx, req.InviteCode)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码不存在"), "")
|
||||
}
|
||||
ref := strings.TrimSpace(req.InviteCode)
|
||||
var inviteCodeRecord *model.AgentInviteCode
|
||||
inviteCodeRecord, err = l.svcCtx.AgentInviteCodeModel.FindOneByCode(l.ctx, ref)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询邀请码失败, %v", err)
|
||||
}
|
||||
|
||||
// 4. 验证邀请码是否属于当前代理
|
||||
if !inviteCodeRecord.AgentId.Valid || inviteCodeRecord.AgentId.Int64 != agent.Id {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("无权使用此邀请码"), "")
|
||||
}
|
||||
|
||||
// 5. 验证邀请码状态
|
||||
if inviteCodeRecord.Status != 0 {
|
||||
if inviteCodeRecord.Status == 1 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已使用"), "")
|
||||
if inviteCodeRecord != nil {
|
||||
if !inviteCodeRecord.AgentId.Valid || inviteCodeRecord.AgentId.String != agent.Id {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("无权使用此邀请码"), "")
|
||||
}
|
||||
if inviteCodeRecord.Status != 0 {
|
||||
if inviteCodeRecord.Status == 1 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已使用"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已失效"), "")
|
||||
}
|
||||
if inviteCodeRecord.ExpireTime.Valid && inviteCodeRecord.ExpireTime.Time.Before(time.Now()) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已过期"), "")
|
||||
}
|
||||
} else {
|
||||
if codeVal, parseErr := strconv.ParseInt(ref, 10, 64); parseErr == nil && codeVal > 0 {
|
||||
if agent.AgentCode != codeVal {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("无权使用此邀请码"), "")
|
||||
}
|
||||
} else {
|
||||
encMobile, _ := crypto.EncryptMobile(ref, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if encMobile != agent.Mobile {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请信息无效"), "")
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已失效"), "")
|
||||
}
|
||||
|
||||
// 6. 验证邀请码是否过期
|
||||
if inviteCodeRecord.ExpireTime.Valid && inviteCodeRecord.ExpireTime.Time.Before(time.Now()) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已过期"), "")
|
||||
}
|
||||
|
||||
// 7. 使用默认target_path(如果未提供)
|
||||
targetPath := req.TargetPath
|
||||
if targetPath == "" {
|
||||
targetPath = fmt.Sprintf("/register?invite_code=%s", req.InviteCode)
|
||||
targetPath = fmt.Sprintf("/register?invite_code=%s", ref)
|
||||
}
|
||||
|
||||
// 8. 生成短链(类型:2=邀请好友)
|
||||
shortLink, err := l.createInviteShortLink(inviteCodeRecord.Id, req.InviteCode, targetPath)
|
||||
shortLink, err := l.createInviteShortLink(func() string {
|
||||
if inviteCodeRecord != nil {
|
||||
return inviteCodeRecord.Id
|
||||
}
|
||||
return ""
|
||||
}(), ref, targetPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成短链失败, %v", err)
|
||||
}
|
||||
@@ -98,7 +112,7 @@ func (l *GetInviteLinkLogic) GetInviteLink(req *types.GetInviteLinkReq) (resp *t
|
||||
}
|
||||
|
||||
// createInviteShortLink 创建邀请好友短链
|
||||
func (l *GetInviteLinkLogic) createInviteShortLink(inviteCodeId int64, inviteCode, targetPath string) (string, error) {
|
||||
func (l *GetInviteLinkLogic) createInviteShortLink(inviteCodeId string, inviteCode, targetPath string) (string, error) {
|
||||
promotionConfig := l.svcCtx.Config.Promotion
|
||||
|
||||
// 如果没有配置推广域名,返回空字符串(保持向后兼容)
|
||||
@@ -108,7 +122,7 @@ func (l *GetInviteLinkLogic) createInviteShortLink(inviteCodeId int64, inviteCod
|
||||
}
|
||||
|
||||
// 先查询是否已存在短链
|
||||
existingShortLink, err := l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeIdTypeDelState(l.ctx, sql.NullInt64{Int64: inviteCodeId, Valid: true}, 2, globalkey.DelStateNo)
|
||||
existingShortLink, err := l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeIdTypeDelState(l.ctx, sql.NullString{String: inviteCodeId, Valid: inviteCodeId != ""}, 2, globalkey.DelStateNo)
|
||||
if err == nil && existingShortLink != nil {
|
||||
// 已存在短链,直接返回
|
||||
return fmt.Sprintf("%s/s/%s", promotionConfig.PromotionDomain, existingShortLink.ShortCode), nil
|
||||
@@ -118,6 +132,17 @@ func (l *GetInviteLinkLogic) createInviteShortLink(inviteCodeId int64, inviteCod
|
||||
return "", errors.Wrapf(err, "查询短链失败")
|
||||
}
|
||||
|
||||
// 如果没有邀请码ID(例如使用手机号或代理码),按邀请码字符串尝试查找以避免重复创建
|
||||
if inviteCodeId == "" && inviteCode != "" {
|
||||
existingByCode, err2 := l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeTypeDelState(l.ctx, sql.NullString{String: inviteCode, Valid: true}, 2, globalkey.DelStateNo)
|
||||
if err2 == nil && existingByCode != nil {
|
||||
return fmt.Sprintf("%s/s/%s", promotionConfig.PromotionDomain, existingByCode.ShortCode), nil
|
||||
}
|
||||
if err2 != nil && !errors.Is(err2, model.ErrNotFound) {
|
||||
return "", errors.Wrapf(err2, "查询短链失败")
|
||||
}
|
||||
}
|
||||
|
||||
// 生成短链标识(6位随机字符串,大小写字母+数字)
|
||||
var shortCode string
|
||||
maxRetries := 10 // 最大重试次数
|
||||
@@ -140,8 +165,9 @@ func (l *GetInviteLinkLogic) createInviteShortLink(inviteCodeId int64, inviteCod
|
||||
|
||||
// 创建短链记录(类型:2=邀请好友)
|
||||
shortLink := &model.AgentShortLink{
|
||||
Id: uuid.NewString(),
|
||||
Type: 2, // 邀请好友
|
||||
InviteCodeId: sql.NullInt64{Int64: inviteCodeId, Valid: inviteCodeId > 0},
|
||||
InviteCodeId: sql.NullString{String: inviteCodeId, Valid: inviteCodeId != ""},
|
||||
InviteCode: sql.NullString{String: inviteCode, Valid: inviteCode != ""},
|
||||
ShortCode: shortCode,
|
||||
TargetPath: targetPath,
|
||||
|
||||
@@ -76,9 +76,11 @@ func (l *GetLevelPrivilegeLogic) GetLevelPrivilege() (resp *types.GetLevelPrivil
|
||||
currentBonus = level1Bonus
|
||||
}
|
||||
|
||||
// 获取升级返佣配置
|
||||
// 获取升级返佣与费用配置
|
||||
upgradeToGoldRebate := getConfigFloat("upgrade_to_gold_rebate", 139.0)
|
||||
upgradeToDiamondRebate := getConfigFloat("upgrade_to_diamond_rebate", 680.0)
|
||||
upgradeToGoldFee := getConfigFloat("upgrade_to_gold_fee", 199.0)
|
||||
upgradeToDiamondFee := getConfigFloat("upgrade_to_diamond_fee", 980.0)
|
||||
|
||||
// 获取直接上级返佣配置
|
||||
directParentAmountDiamond := getConfigFloat("direct_parent_amount_diamond", 6.0)
|
||||
@@ -140,7 +142,11 @@ func (l *GetLevelPrivilegeLogic) GetLevelPrivilege() (resp *types.GetLevelPrivil
|
||||
}
|
||||
|
||||
return &types.GetLevelPrivilegeResp{
|
||||
Levels: levels,
|
||||
Levels: levels,
|
||||
UpgradeToGoldFee: upgradeToGoldFee,
|
||||
UpgradeToDiamondFee: upgradeToDiamondFee,
|
||||
UpgradeToGoldRebate: upgradeToGoldRebate,
|
||||
UpgradeToDiamondRebate: upgradeToDiamondRebate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ func (l *GetLinkDataLogic) GetLinkData(req *types.GetLinkDataReq) (resp *types.G
|
||||
}
|
||||
|
||||
// 创建featureId到sort的映射,用于后续排序
|
||||
featureSortMap := make(map[int64]int64)
|
||||
featureSortMap := make(map[string]int64)
|
||||
for _, productFeature := range productFeatureAll {
|
||||
featureSortMap[productFeature.FeatureId] = productFeature.Sort
|
||||
}
|
||||
@@ -62,18 +62,18 @@ func (l *GetLinkDataLogic) GetLinkData(req *types.GetLinkDataReq) (resp *types.G
|
||||
for _, productFeature := range productFeatureAll {
|
||||
source <- productFeature.FeatureId
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[*model.Feature], cancel func(error)) {
|
||||
id := item.(int64)
|
||||
}, 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("获取产品功能失败: %d, err:%v", id, findFeatureErr)
|
||||
return
|
||||
}
|
||||
if feature != nil && feature.Id > 0 {
|
||||
writer.Write(feature)
|
||||
}
|
||||
}, func(pipe <-chan *model.Feature, cancel func(error)) {
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"ycc-server/common/xerr"
|
||||
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
|
||||
"ycc-server/app/main/api/internal/svc"
|
||||
"ycc-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 del_state = ?", agent.Id, globalkey.DelStateNo)
|
||||
|
||||
orders, _, 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)
|
||||
}
|
||||
|
||||
// 组装查询报告列表(只展示已创建的查询)
|
||||
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)
|
||||
}
|
||||
|
||||
// 获取产品名称
|
||||
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.ProductName = product.ProductName
|
||||
item.CreateTime = q.CreateTime.Format("2006-01-02 15:04:05")
|
||||
item.QueryState = q.QueryState
|
||||
list = append(list, item)
|
||||
}
|
||||
|
||||
return &types.GetPromotionQueryListResp{
|
||||
Total: int64(len(list)), // 前端仅展示已创建查询条目
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
@@ -85,7 +85,7 @@ func (l *GetRebateListLogic) GetRebateList(req *types.GetRebateListReq) (resp *t
|
||||
for _, rebate := range rebates {
|
||||
// 查询订单号
|
||||
orderNo := ""
|
||||
if rebate.OrderId > 0 {
|
||||
if rebate.OrderId != "" {
|
||||
order, err := l.svcCtx.OrderModel.FindOne(l.ctx, rebate.OrderId)
|
||||
if err == nil {
|
||||
orderNo = order.OrderNo
|
||||
@@ -95,7 +95,7 @@ func (l *GetRebateListLogic) GetRebateList(req *types.GetRebateListReq) (resp *t
|
||||
// 查询来源代理手机号和等级
|
||||
sourceAgentMobile := ""
|
||||
sourceAgentLevel := int64(0)
|
||||
if rebate.SourceAgentId > 0 {
|
||||
if rebate.SourceAgentId != "" {
|
||||
sourceAgent, err := l.svcCtx.AgentModel.FindOne(l.ctx, rebate.SourceAgentId)
|
||||
if err == nil {
|
||||
if sourceAgent.Mobile != "" {
|
||||
|
||||
@@ -184,7 +184,7 @@ func (l *GetSubordinateContributionDetailLogic) GetSubordinateContributionDetail
|
||||
}
|
||||
|
||||
// isSubordinate 递归检查 targetId 是否是 parentId 的下级(直接或间接)
|
||||
func (l *GetSubordinateContributionDetailLogic) isSubordinate(parentId, targetId int64) bool {
|
||||
func (l *GetSubordinateContributionDetailLogic) isSubordinate(parentId, targetId string) bool {
|
||||
// 查询直接下级
|
||||
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
|
||||
Where("parent_id = ? AND relation_type = ? AND del_state = ?", parentId, 1, globalkey.DelStateNo)
|
||||
@@ -215,7 +215,7 @@ func (l *GetSubordinateContributionDetailLogic) countDistinctOrders(ctx context.
|
||||
return 0
|
||||
}
|
||||
|
||||
orderIdSet := make(map[int64]bool)
|
||||
orderIdSet := make(map[string]bool)
|
||||
for _, rebate := range rebates {
|
||||
orderIdSet[rebate.OrderId] = true
|
||||
}
|
||||
@@ -224,7 +224,7 @@ func (l *GetSubordinateContributionDetailLogic) countDistinctOrders(ctx context.
|
||||
}
|
||||
|
||||
// getOrderList 获取订单列表(仅显示有返佣的订单)
|
||||
func (l *GetSubordinateContributionDetailLogic) getOrderList(ctx context.Context, agentId, subordinateId int64, page, pageSize int64) ([]types.OrderItem, int64, error) {
|
||||
func (l *GetSubordinateContributionDetailLogic) getOrderList(ctx context.Context, agentId, subordinateId string, page, pageSize int64) ([]types.OrderItem, int64, error) {
|
||||
// 1. 查询所有返佣记录
|
||||
rebateBuilder := l.svcCtx.AgentRebateModel.SelectBuilder().
|
||||
Where("agent_id = ? AND source_agent_id = ? AND del_state = ?", agentId, subordinateId, globalkey.DelStateNo).
|
||||
@@ -247,14 +247,14 @@ func (l *GetSubordinateContributionDetailLogic) getOrderList(ctx context.Context
|
||||
|
||||
// 2. 在内存中去重订单ID,并按创建时间排序
|
||||
type OrderRebateInfo struct {
|
||||
OrderId int64
|
||||
RebateId int64
|
||||
ProductId int64
|
||||
OrderId string
|
||||
RebateId string
|
||||
ProductId string
|
||||
RebateAmount float64
|
||||
CreateTime time.Time
|
||||
}
|
||||
|
||||
orderMap := make(map[int64]*OrderRebateInfo) // orderId -> 最新的返佣信息
|
||||
orderMap := make(map[string]*OrderRebateInfo) // orderId -> 最新的返佣信息
|
||||
for _, rebate := range allRebates {
|
||||
if existing, ok := orderMap[rebate.OrderId]; ok {
|
||||
// 如果已存在,保留创建时间最新的
|
||||
@@ -303,7 +303,7 @@ func (l *GetSubordinateContributionDetailLogic) getOrderList(ctx context.Context
|
||||
|
||||
// 5. 组装订单列表
|
||||
var resultList []types.OrderItem
|
||||
productCache := make(map[int64]string) // 产品ID -> 产品名称缓存
|
||||
productCache := make(map[string]string) // 产品ID -> 产品名称缓存
|
||||
|
||||
for _, orderInfo := range pagedOrderList {
|
||||
// 查询订单信息
|
||||
@@ -344,7 +344,7 @@ func (l *GetSubordinateContributionDetailLogic) getOrderList(ctx context.Context
|
||||
}
|
||||
|
||||
// getInviteList 获取邀请列表
|
||||
func (l *GetSubordinateContributionDetailLogic) getInviteList(ctx context.Context, subordinateId int64, page, pageSize int64) ([]types.InviteItem, int64, error) {
|
||||
func (l *GetSubordinateContributionDetailLogic) getInviteList(ctx context.Context, subordinateId string, page, pageSize int64) ([]types.InviteItem, int64, error) {
|
||||
// 查询总数
|
||||
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
|
||||
Where("parent_id = ? AND relation_type = ? AND del_state = ?", subordinateId, 1, globalkey.DelStateNo)
|
||||
|
||||
@@ -50,12 +50,12 @@ func (l *GetTeamListLogic) GetTeamList(req *types.GetTeamListReq) (resp *types.G
|
||||
}
|
||||
|
||||
// 2. 递归查询所有下级(直接+间接)
|
||||
allSubordinateIds := make(map[int64]bool)
|
||||
directSubordinateIds := make(map[int64]bool)
|
||||
allSubordinateIds := make(map[string]bool)
|
||||
directSubordinateIds := make(map[string]bool)
|
||||
|
||||
// 递归函数:收集所有下级ID
|
||||
var collectSubordinates func(int64) error
|
||||
collectSubordinates = func(parentId int64) error {
|
||||
var collectSubordinates func(string) error
|
||||
collectSubordinates = func(parentId string) error {
|
||||
// 查询直接下级
|
||||
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
|
||||
Where("parent_id = ? AND relation_type = ? AND del_state = ?", parentId, 1, globalkey.DelStateNo)
|
||||
@@ -94,7 +94,7 @@ func (l *GetTeamListLogic) GetTeamList(req *types.GetTeamListReq) (resp *types.G
|
||||
}
|
||||
|
||||
// 4. 将下级ID转换为切片用于查询
|
||||
subordinateIds := make([]int64, 0, len(allSubordinateIds))
|
||||
subordinateIds := make([]string, 0, len(allSubordinateIds))
|
||||
for id := range allSubordinateIds {
|
||||
subordinateIds = append(subordinateIds, id)
|
||||
}
|
||||
@@ -183,7 +183,7 @@ func (l *GetTeamListLogic) GetTeamList(req *types.GetTeamListReq) (resp *types.G
|
||||
|
||||
// calculateTeamStatistics 计算团队统计数据
|
||||
// 注意:所有统计都基于 subordinateIds(下级ID列表),不包含自己的数据
|
||||
func (l *GetTeamListLogic) calculateTeamStatistics(agentId int64, subordinateIds []int64, todayStart, monthStart time.Time) types.TeamStatistics {
|
||||
func (l *GetTeamListLogic) calculateTeamStatistics(agentId string, subordinateIds []string, todayStart, monthStart time.Time) types.TeamStatistics {
|
||||
// 团队成员总数:只统计下级,不包括自己
|
||||
stats := types.TeamStatistics{
|
||||
TotalMembers: int64(len(subordinateIds)),
|
||||
@@ -213,7 +213,7 @@ func (l *GetTeamListLogic) calculateTeamStatistics(agentId int64, subordinateIds
|
||||
Where("agent_id = ? AND del_state = ?", agentId, globalkey.DelStateNo).
|
||||
Where("source_agent_id != ?", agentId). // 明确排除自己
|
||||
Where(squirrel.Eq{"source_agent_id": subordinateIds})
|
||||
|
||||
|
||||
// 统计去重的订单数量
|
||||
totalQueries := l.countDistinctOrders(l.ctx, rebateBuilder)
|
||||
stats.TotalQueries = totalQueries
|
||||
@@ -259,7 +259,7 @@ func (l *GetTeamListLogic) countDistinctOrders(ctx context.Context, builder squi
|
||||
return 0
|
||||
}
|
||||
|
||||
orderIdSet := make(map[int64]bool)
|
||||
orderIdSet := make(map[string]bool)
|
||||
for _, rebate := range rebates {
|
||||
orderIdSet[rebate.OrderId] = true
|
||||
}
|
||||
@@ -275,7 +275,7 @@ func (l *GetTeamListLogic) countDistinctOrdersForMember(ctx context.Context, bui
|
||||
return 0
|
||||
}
|
||||
|
||||
orderIdSet := make(map[int64]bool)
|
||||
orderIdSet := make(map[string]bool)
|
||||
for _, rebate := range rebates {
|
||||
orderIdSet[rebate.OrderId] = true
|
||||
}
|
||||
@@ -284,7 +284,7 @@ func (l *GetTeamListLogic) countDistinctOrdersForMember(ctx context.Context, bui
|
||||
}
|
||||
|
||||
// buildTeamMemberItem 构建团队成员项
|
||||
func (l *GetTeamListLogic) buildTeamMemberItem(agentId int64, member *model.Agent, directSubordinateIds map[int64]bool, todayStart time.Time) types.TeamMemberItem {
|
||||
func (l *GetTeamListLogic) buildTeamMemberItem(agentId string, member *model.Agent, directSubordinateIds map[string]bool, todayStart time.Time) types.TeamMemberItem {
|
||||
levelName := ""
|
||||
switch member.Level {
|
||||
case 1:
|
||||
|
||||
@@ -47,12 +47,12 @@ func (l *GetTeamStatisticsLogic) GetTeamStatistics() (resp *types.TeamStatistics
|
||||
}
|
||||
|
||||
// 2. 递归查询所有下级(直接+间接)
|
||||
allSubordinateIds := make(map[int64]bool)
|
||||
directSubordinateIds := make(map[int64]bool)
|
||||
allSubordinateIds := make(map[string]bool)
|
||||
directSubordinateIds := make(map[string]bool)
|
||||
|
||||
// 递归函数:收集所有下级ID
|
||||
var collectSubordinates func(int64) error
|
||||
collectSubordinates = func(parentId int64) error {
|
||||
var collectSubordinates func(string) error
|
||||
collectSubordinates = func(parentId string) error {
|
||||
// 查询直接下级
|
||||
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
|
||||
Where("parent_id = ? AND relation_type = ? AND del_state = ?", parentId, 1, globalkey.DelStateNo)
|
||||
@@ -63,23 +63,23 @@ func (l *GetTeamStatisticsLogic) GetTeamStatistics() (resp *types.TeamStatistics
|
||||
|
||||
for _, relation := range relations {
|
||||
// 如果是第一层,标记为直接下级
|
||||
if parentId == agent.Id {
|
||||
directSubordinateIds[relation.ChildId] = true
|
||||
}
|
||||
// 添加到所有下级集合
|
||||
allSubordinateIds[relation.ChildId] = true
|
||||
// 递归查询下级的下级
|
||||
if err := collectSubordinates(relation.ChildId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if parentId == agent.Id {
|
||||
directSubordinateIds[relation.ChildId] = true
|
||||
}
|
||||
// 添加到所有下级集合
|
||||
allSubordinateIds[relation.ChildId] = true
|
||||
// 递归查询下级的下级
|
||||
if err := collectSubordinates(relation.ChildId); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 开始递归收集所有下级
|
||||
if err := collectSubordinates(agent.Id); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询下级关系失败, %v", err)
|
||||
}
|
||||
if err := collectSubordinates(agent.Id); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询下级关系失败, %v", err)
|
||||
}
|
||||
|
||||
// 3. 获取当前时间用于统计今日和本月新增
|
||||
now := time.Now()
|
||||
@@ -100,10 +100,10 @@ func (l *GetTeamStatisticsLogic) GetTeamStatistics() (resp *types.TeamStatistics
|
||||
}
|
||||
|
||||
// 5. 将下级ID转换为切片用于查询
|
||||
subordinateIds := make([]int64, 0, len(allSubordinateIds))
|
||||
for id := range allSubordinateIds {
|
||||
subordinateIds = append(subordinateIds, id)
|
||||
}
|
||||
subordinateIds := make([]string, 0, len(allSubordinateIds))
|
||||
for id := range allSubordinateIds {
|
||||
subordinateIds = append(subordinateIds, id)
|
||||
}
|
||||
|
||||
// 6. 查询所有下级代理信息
|
||||
builder := l.svcCtx.AgentModel.SelectBuilder().
|
||||
|
||||
@@ -82,7 +82,7 @@ func (l *GetUpgradeRebateListLogic) GetUpgradeRebateList(req *types.GetUpgradeRe
|
||||
for _, upgrade := range upgrades {
|
||||
// 查询来源代理手机号(升级的代理)
|
||||
sourceAgentMobile := ""
|
||||
if upgrade.AgentId > 0 {
|
||||
if upgrade.AgentId != "" {
|
||||
sourceAgent, err := l.svcCtx.AgentModel.FindOne(l.ctx, upgrade.AgentId)
|
||||
if err == nil {
|
||||
if sourceAgent.Mobile != "" {
|
||||
|
||||
@@ -5,12 +5,15 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/redis"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
@@ -57,33 +60,26 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 查询邀请码
|
||||
inviteCodeModel, err := l.svcCtx.AgentInviteCodeModel.FindOneByCode(l.ctx, req.InviteCode)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码不存在"), "")
|
||||
}
|
||||
var inviteCodeModel *model.AgentInviteCode
|
||||
inviteCodeModel, err = l.svcCtx.AgentInviteCodeModel.FindOneByCode(l.ctx, req.Referrer)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询邀请码失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 验证邀请码状态
|
||||
// 钻石级别的邀请码只能使用一次,使用后立即失效
|
||||
// 普通级别的邀请码可以无限使用
|
||||
if inviteCodeModel.Status != 0 {
|
||||
if inviteCodeModel.Status == 1 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已使用"), "")
|
||||
if inviteCodeModel != nil {
|
||||
if inviteCodeModel.Status != 0 {
|
||||
if inviteCodeModel.Status == 1 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已使用"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已失效"), "")
|
||||
}
|
||||
if inviteCodeModel.ExpireTime.Valid && inviteCodeModel.ExpireTime.Time.Before(time.Now()) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已过期"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已失效"), "")
|
||||
}
|
||||
|
||||
// 3. 验证邀请码是否过期
|
||||
if inviteCodeModel.ExpireTime.Valid && inviteCodeModel.ExpireTime.Time.Before(time.Now()) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码已过期"), "")
|
||||
}
|
||||
|
||||
// 4. 使用事务处理注册
|
||||
var userID int64
|
||||
var agentID int64
|
||||
var userID string
|
||||
var agentID string
|
||||
var agentLevel int64
|
||||
|
||||
err = l.svcCtx.AgentInviteCodeModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
@@ -113,15 +109,11 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
// 注意:非微信环境下 claims 为 nil,此逻辑不会执行,直接使用已存在的 user.Id
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err == nil && claims != nil && claims.UserType == model.UserTypeTemp {
|
||||
userTemp, err := l.svcCtx.UserTempModel.FindOne(l.ctx, claims.UserId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询临时用户失败, %v", err)
|
||||
}
|
||||
userAuth, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, user.Id, userTemp.AuthType)
|
||||
userAuth, err := l.svcCtx.UserAuthModel.FindOneByUserIdAuthType(l.ctx, user.Id, claims.AuthType)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户认证失败, %v", err)
|
||||
}
|
||||
if userAuth != nil && userAuth.AuthKey != userTemp.AuthKey {
|
||||
if userAuth != nil && userAuth.AuthKey != claims.AuthKey {
|
||||
return errors.Wrapf(xerr.NewErrMsg("该手机号已绑定其他微信号"), "")
|
||||
}
|
||||
// 绑定临时用户到正式用户
|
||||
@@ -134,19 +126,37 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
userID = user.Id
|
||||
}
|
||||
|
||||
// 4.2 获取邀请码信息
|
||||
targetLevel := inviteCodeModel.TargetLevel
|
||||
var parentAgentId int64 = 0
|
||||
if inviteCodeModel.AgentId.Valid {
|
||||
parentAgentId = inviteCodeModel.AgentId.Int64
|
||||
var targetLevel int64
|
||||
var parentAgentId string
|
||||
if inviteCodeModel != nil {
|
||||
targetLevel = inviteCodeModel.TargetLevel
|
||||
if inviteCodeModel.AgentId.Valid {
|
||||
parentAgentId = inviteCodeModel.AgentId.String
|
||||
}
|
||||
} else {
|
||||
if codeVal, parseErr := strconv.ParseInt(req.Referrer, 10, 64); parseErr == nil && codeVal > 0 {
|
||||
parentAgent, err := l.findAgentByCode(transCtx, codeVal)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "")
|
||||
}
|
||||
parentAgentId = parentAgent.Id
|
||||
targetLevel = 1
|
||||
} else {
|
||||
encRefMobile, _ := crypto.EncryptMobile(req.Referrer, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
agents, findErr := l.svcCtx.AgentModel.FindAll(transCtx, l.svcCtx.AgentModel.SelectBuilder().Where("mobile = ? AND del_state = ?", encRefMobile, globalkey.DelStateNo).Limit(1), "")
|
||||
if findErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上级代理失败, %v", findErr)
|
||||
}
|
||||
if len(agents) == 0 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("邀请信息无效"), "")
|
||||
}
|
||||
parentAgentId = agents[0].Id
|
||||
targetLevel = 1
|
||||
}
|
||||
}
|
||||
|
||||
// 4.3 创建代理记录
|
||||
newAgent := &model.Agent{
|
||||
UserId: userID,
|
||||
Level: targetLevel,
|
||||
Mobile: encryptedMobile,
|
||||
}
|
||||
newAgent := &model.Agent{Id: uuid.NewString(), UserId: userID, Level: targetLevel, Mobile: encryptedMobile}
|
||||
if req.Region != "" {
|
||||
newAgent.Region = sql.NullString{String: req.Region, Valid: true}
|
||||
}
|
||||
@@ -155,7 +165,7 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
}
|
||||
|
||||
// 4.4 处理上级关系
|
||||
if parentAgentId > 0 {
|
||||
if parentAgentId != "" {
|
||||
// 查找上级代理
|
||||
parentAgent, err := l.svcCtx.AgentModel.FindOne(transCtx, parentAgentId)
|
||||
if err != nil {
|
||||
@@ -172,57 +182,50 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "查找团队首领失败")
|
||||
}
|
||||
if teamLeaderId > 0 {
|
||||
newAgent.TeamLeaderId = sql.NullInt64{Int64: teamLeaderId, Valid: true}
|
||||
if teamLeaderId != "" {
|
||||
newAgent.TeamLeaderId = sql.NullString{String: teamLeaderId, Valid: true}
|
||||
}
|
||||
|
||||
// 先插入代理记录
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
newAgent.AgentCode = 0
|
||||
_, err = l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentID, _ = agentResult.LastInsertId()
|
||||
newAgent.Id = agentID
|
||||
agentID = newAgent.Id
|
||||
|
||||
// 建立关系
|
||||
relation := &model.AgentRelation{
|
||||
ParentId: parentAgent.Id,
|
||||
ChildId: agentID,
|
||||
RelationType: 1, // 直接关系
|
||||
}
|
||||
relation := &model.AgentRelation{Id: uuid.NewString(), ParentId: parentAgent.Id, ChildId: agentID, RelationType: 1}
|
||||
if _, err := l.svcCtx.AgentRelationModel.Insert(transCtx, session, relation); err != nil {
|
||||
return errors.Wrapf(err, "建立代理关系失败")
|
||||
}
|
||||
} else {
|
||||
// 平台发放的钻石邀请码,独立成团队
|
||||
if targetLevel == 3 {
|
||||
// 先插入代理记录
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
newAgent.AgentCode = 0
|
||||
_, err = l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentID, _ = agentResult.LastInsertId()
|
||||
newAgent.Id = agentID
|
||||
agentID = newAgent.Id
|
||||
|
||||
// 设置自己为团队首领
|
||||
newAgent.TeamLeaderId = sql.NullInt64{Int64: agentID, Valid: true}
|
||||
newAgent.TeamLeaderId = sql.NullString{String: agentID, Valid: true}
|
||||
if err := l.svcCtx.AgentModel.UpdateInTransaction(transCtx, session, newAgent); err != nil {
|
||||
return errors.Wrapf(err, "更新团队首领失败")
|
||||
}
|
||||
} else {
|
||||
// 普通/黄金代理,但没有上级(异常情况)
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
newAgent.AgentCode = 0
|
||||
_, err = l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentID, _ = agentResult.LastInsertId()
|
||||
agentID = newAgent.Id
|
||||
}
|
||||
}
|
||||
|
||||
// 4.5 初始化钱包
|
||||
wallet := &model.AgentWallet{
|
||||
AgentId: agentID,
|
||||
}
|
||||
wallet := &model.AgentWallet{Id: uuid.NewString(), AgentId: agentID}
|
||||
if _, err := l.svcCtx.AgentWalletModel.Insert(transCtx, session, wallet); err != nil {
|
||||
return errors.Wrapf(err, "初始化钱包失败")
|
||||
}
|
||||
@@ -234,25 +237,21 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
// 钻石邀请码:使用后失效
|
||||
inviteCodeModel.Status = 1 // 已使用(使用后立即失效)
|
||||
}
|
||||
// 记录使用信息(用于统计,普通邀请码可以多次使用)
|
||||
inviteCodeModel.UsedUserId = sql.NullInt64{Int64: userID, Valid: true}
|
||||
inviteCodeModel.UsedAgentId = sql.NullInt64{Int64: agentID, Valid: true}
|
||||
inviteCodeModel.UsedTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if err := l.svcCtx.AgentInviteCodeModel.UpdateWithVersion(transCtx, session, inviteCodeModel); err != nil {
|
||||
return errors.Wrapf(err, "更新邀请码状态失败")
|
||||
if inviteCodeModel != nil {
|
||||
inviteCodeModel.UsedUserId = sql.NullString{String: userID, Valid: true}
|
||||
inviteCodeModel.UsedAgentId = sql.NullString{String: agentID, Valid: true}
|
||||
inviteCodeModel.UsedTime = sql.NullTime{Time: time.Now(), Valid: true}
|
||||
if err := l.svcCtx.AgentInviteCodeModel.UpdateWithVersion(transCtx, session, inviteCodeModel); err != nil {
|
||||
return errors.Wrapf(err, "更新邀请码状态失败")
|
||||
}
|
||||
}
|
||||
|
||||
// 4.7 记录邀请码使用历史(用于统计和查询)
|
||||
usage := &model.AgentInviteCodeUsage{
|
||||
InviteCodeId: inviteCodeModel.Id,
|
||||
Code: inviteCodeModel.Code,
|
||||
UserId: userID,
|
||||
AgentId: agentID,
|
||||
AgentLevel: targetLevel,
|
||||
UsedTime: time.Now(),
|
||||
}
|
||||
if _, err := l.svcCtx.AgentInviteCodeUsageModel.Insert(transCtx, session, usage); err != nil {
|
||||
return errors.Wrapf(err, "记录邀请码使用历史失败")
|
||||
if inviteCodeModel != nil {
|
||||
usage := &model.AgentInviteCodeUsage{Id: uuid.NewString(), InviteCodeId: inviteCodeModel.Id, Code: inviteCodeModel.Code, UserId: userID, AgentId: agentID, AgentLevel: targetLevel, UsedTime: time.Now()}
|
||||
if _, err := l.svcCtx.AgentInviteCodeUsageModel.Insert(transCtx, session, usage); err != nil {
|
||||
return errors.Wrapf(err, "记录邀请码使用历史失败")
|
||||
}
|
||||
}
|
||||
|
||||
agentLevel = targetLevel
|
||||
@@ -280,6 +279,7 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
case 3:
|
||||
levelName = "钻石"
|
||||
}
|
||||
agent, _ := l.svcCtx.AgentModel.FindOne(l.ctx, agentID)
|
||||
return &types.RegisterByInviteCodeResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
@@ -287,11 +287,17 @@ func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByIn
|
||||
AgentId: agentID,
|
||||
Level: agentLevel,
|
||||
LevelName: levelName,
|
||||
AgentCode: func() int64 {
|
||||
if agent != nil {
|
||||
return agent.AgentCode
|
||||
}
|
||||
return 0
|
||||
}(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// findTeamLeader 查找团队首领(钻石代理)
|
||||
func (l *RegisterByInviteCodeLogic) findTeamLeader(ctx context.Context, agentId int64) (int64, error) {
|
||||
func (l *RegisterByInviteCodeLogic) findTeamLeader(ctx context.Context, agentId string) (string, error) {
|
||||
currentId := agentId
|
||||
maxDepth := 100
|
||||
depth := 0
|
||||
@@ -301,22 +307,22 @@ func (l *RegisterByInviteCodeLogic) findTeamLeader(ctx context.Context, agentId
|
||||
Where("child_id = ? AND relation_type = ? AND del_state = ?", currentId, 1, 0)
|
||||
relations, err := l.svcCtx.AgentRelationModel.FindAll(ctx, builder, "")
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
if len(relations) == 0 {
|
||||
agent, err := l.svcCtx.AgentModel.FindOne(ctx, currentId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
if agent.Level == 3 {
|
||||
return agent.Id, nil
|
||||
}
|
||||
return 0, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
parentAgent, err := l.svcCtx.AgentModel.FindOne(ctx, relations[0].ParentId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
return "", err
|
||||
}
|
||||
|
||||
if parentAgent.Level == 3 {
|
||||
@@ -327,5 +333,30 @@ func (l *RegisterByInviteCodeLogic) findTeamLeader(ctx context.Context, agentId
|
||||
depth++
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (l *RegisterByInviteCodeLogic) findAgentByCode(ctx context.Context, code int64) (*model.Agent, error) {
|
||||
builder := l.svcCtx.AgentModel.SelectBuilder().Where("agent_code = ? AND del_state = ?", code, globalkey.DelStateNo).Limit(1)
|
||||
agents, err := l.svcCtx.AgentModel.FindAll(ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上级代理失败, %v", err)
|
||||
}
|
||||
if len(agents) == 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("上级邀请码不存在"), "")
|
||||
}
|
||||
return agents[0], nil
|
||||
}
|
||||
|
||||
func (l *RegisterByInviteCodeLogic) 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
|
||||
}
|
||||
|
||||
@@ -60,15 +60,10 @@ func (l *ShortLinkRedirectLogic) ShortLinkRedirect(shortCode string, r *http.Req
|
||||
}
|
||||
}
|
||||
} else if shortLink.Type == 2 {
|
||||
// 邀请好友类型:验证邀请码是否存在
|
||||
if shortLink.InviteCode.Valid && shortLink.InviteCode.String != "" {
|
||||
_, err = l.svcCtx.AgentInviteCodeModel.FindOneByCode(l.ctx, shortLink.InviteCode.String)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "邀请码不存在或已失效")
|
||||
}
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
l.Errorf("查询邀请码失败: %v", err)
|
||||
// 即使查询失败,也继续重定向,避免影响用户体验
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,4 +106,3 @@ func (l *ShortLinkRedirectLogic) ShortLinkRedirect(shortCode string, r *http.Req
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
@@ -79,21 +80,21 @@ func (l *UpgradeSubordinateLogic) UpgradeSubordinate(req *types.UpgradeSubordina
|
||||
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 7.1 创建升级记录
|
||||
upgradeRecord := &model.AgentUpgrade{
|
||||
Id: uuid.New().String(),
|
||||
AgentId: subordinateAgent.Id,
|
||||
FromLevel: 1, // 普通
|
||||
ToLevel: toLevel,
|
||||
UpgradeType: 2, // 钻石升级下级
|
||||
UpgradeFee: 0, // 免费
|
||||
RebateAmount: 0, // 无返佣
|
||||
OperatorAgentId: sql.NullInt64{Int64: operatorAgent.Id, Valid: true},
|
||||
OperatorAgentId: sql.NullString{String: operatorAgent.Id, Valid: true},
|
||||
Status: 1, // 待处理
|
||||
}
|
||||
|
||||
upgradeResult, err := l.svcCtx.AgentUpgradeModel.Insert(transCtx, session, upgradeRecord)
|
||||
_, err := l.svcCtx.AgentUpgradeModel.Insert(transCtx, session, upgradeRecord)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建升级记录失败")
|
||||
}
|
||||
upgradeId, _ := upgradeResult.LastInsertId()
|
||||
|
||||
// 7.2 执行升级操作
|
||||
if err := l.svcCtx.AgentService.ProcessUpgrade(transCtx, subordinateAgent.Id, toLevel, 2, 0, 0, "", operatorAgent.Id); err != nil {
|
||||
@@ -101,7 +102,6 @@ func (l *UpgradeSubordinateLogic) UpgradeSubordinate(req *types.UpgradeSubordina
|
||||
}
|
||||
|
||||
// 7.3 更新升级记录状态
|
||||
upgradeRecord.Id = upgradeId
|
||||
upgradeRecord.Status = 2 // 已完成
|
||||
upgradeRecord.Remark = lzUtils.StringToNullString("钻石代理升级下级成功")
|
||||
if err := l.svcCtx.AgentUpgradeModel.UpdateWithVersion(transCtx, session, upgradeRecord); err != nil {
|
||||
@@ -121,7 +121,7 @@ func (l *UpgradeSubordinateLogic) UpgradeSubordinate(req *types.UpgradeSubordina
|
||||
}
|
||||
|
||||
// isSubordinate 递归检查 targetId 是否是 parentId 的下级(直接或间接)
|
||||
func (l *UpgradeSubordinateLogic) isSubordinate(parentId, targetId int64) bool {
|
||||
func (l *UpgradeSubordinateLogic) isSubordinate(parentId, targetId string) bool {
|
||||
// 查询直接下级
|
||||
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
|
||||
Where("parent_id = ? AND relation_type = ? AND del_state = ?", parentId, 1, globalkey.DelStateNo)
|
||||
|
||||
Reference in New Issue
Block a user