This commit is contained in:
2025-12-09 18:55:28 +08:00
parent 8d00d67540
commit c23ab8338b
209 changed files with 5445 additions and 3963 deletions

View File

@@ -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
}