package agent import ( "context" "database/sql" "fmt" "time" "ycc-server/app/main/model" "ycc-server/common/ctxdata" "ycc-server/common/globalkey" "ycc-server/common/tool" "ycc-server/common/xerr" "github.com/pkg/errors" "ycc-server/app/main/api/internal/svc" "ycc-server/app/main/api/internal/types" "github.com/zeromicro/go-zero/core/logx" ) type GetInviteLinkLogic struct { logx.Logger ctx context.Context svcCtx *svc.ServiceContext } func NewGetInviteLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetInviteLinkLogic { return &GetInviteLinkLogic{ Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx, } } func (l *GetInviteLinkLogic) GetInviteLink(req *types.GetInviteLinkReq) (resp *types.GetInviteLinkResp, err error) { // 1. 获取当前代理信息 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) } // 2. 验证邀请码参数 if req.InviteCode == "" { 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("邀请码不存在"), "") } 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("邀请码已使用"), "") } 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) } // 8. 生成短链(类型:2=邀请好友) shortLink, err := l.createInviteShortLink(inviteCodeRecord.Id, req.InviteCode, targetPath) if err != nil { return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成短链失败, %v", err) } return &types.GetInviteLinkResp{ InviteLink: shortLink, }, nil } // createInviteShortLink 创建邀请好友短链 func (l *GetInviteLinkLogic) createInviteShortLink(inviteCodeId int64, inviteCode, targetPath string) (string, error) { promotionConfig := l.svcCtx.Config.Promotion // 如果没有配置推广域名,返回空字符串(保持向后兼容) if promotionConfig.PromotionDomain == "" { l.Errorf("推广域名未配置,返回空链接") return "", nil } // 先查询是否已存在短链 existingShortLink, err := l.svcCtx.AgentShortLinkModel.FindOneByInviteCodeIdTypeDelState(l.ctx, sql.NullInt64{Int64: inviteCodeId, Valid: true}, 2, globalkey.DelStateNo) if err == nil && existingShortLink != nil { // 已存在短链,直接返回 return fmt.Sprintf("%s/s/%s", promotionConfig.PromotionDomain, existingShortLink.ShortCode), nil } if err != nil && !errors.Is(err, model.ErrNotFound) { return "", errors.Wrapf(err, "查询短链失败") } // 生成短链标识(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("生成短链失败,请重试"), "") } } // 创建短链记录(类型:2=邀请好友) shortLink := &model.AgentShortLink{ Type: 2, // 邀请好友 InviteCodeId: sql.NullInt64{Int64: inviteCodeId, Valid: inviteCodeId > 0}, InviteCode: sql.NullString{String: inviteCode, Valid: inviteCode != ""}, ShortCode: shortCode, TargetPath: targetPath, PromotionDomain: promotionConfig.PromotionDomain, } _, err = l.svcCtx.AgentShortLinkModel.Insert(l.ctx, nil, shortLink) if err != nil { return "", errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "保存短链失败, %v", err) } return fmt.Sprintf("%s/s/%s", promotionConfig.PromotionDomain, shortCode), nil }