v1.0
This commit is contained in:
@@ -4,13 +4,14 @@ 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"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
|
||||
"ycc-server/app/main/api/internal/svc"
|
||||
"ycc-server/app/main/api/internal/types"
|
||||
@@ -32,7 +33,7 @@ func NewGetInviteLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Get
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetInviteLinkLogic) GetInviteLink() (resp *types.GetInviteLinkResp, err error) {
|
||||
func (l *GetInviteLinkLogic) GetInviteLink(req *types.GetInviteLinkReq) (resp *types.GetInviteLinkResp, err error) {
|
||||
// 1. 获取当前代理信息
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
@@ -47,63 +48,109 @@ func (l *GetInviteLinkLogic) GetInviteLink() (resp *types.GetInviteLinkResp, err
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 为邀请链接生成一个邀请码(每次调用都生成新的,不过期)
|
||||
// 这样代理可以分享这个链接,用户通过链接注册时会使用这个邀请码
|
||||
var inviteCode string
|
||||
err = l.svcCtx.AgentInviteCodeModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 生成8位随机邀请码
|
||||
code := tool.Krand(8, tool.KC_RAND_KIND_ALL)
|
||||
maxRetries := 10
|
||||
for retry := 0; retry < maxRetries; retry++ {
|
||||
_, err := l.svcCtx.AgentInviteCodeModel.FindOneByCode(transCtx, code)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
break
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "检查邀请码失败, %v", err)
|
||||
}
|
||||
code = tool.Krand(8, tool.KC_RAND_KIND_ALL)
|
||||
if retry == maxRetries-1 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("生成邀请码失败,请重试"), "")
|
||||
}
|
||||
}
|
||||
|
||||
// 创建邀请码记录(用于链接,不过期)
|
||||
inviteCodeRecord := &model.AgentInviteCode{
|
||||
Code: code,
|
||||
AgentId: sql.NullInt64{Int64: agent.Id, Valid: true},
|
||||
TargetLevel: 1, // 代理发放的邀请码,目标等级为普通代理
|
||||
Status: 0, // 未使用
|
||||
ExpireTime: sql.NullTime{Valid: false}, // 不过期
|
||||
Remark: sql.NullString{String: "邀请链接生成", Valid: true},
|
||||
}
|
||||
|
||||
_, err := l.svcCtx.AgentInviteCodeModel.Insert(transCtx, session, inviteCodeRecord)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建邀请码失败, %v", err)
|
||||
}
|
||||
|
||||
inviteCode = code
|
||||
return nil
|
||||
})
|
||||
// 2. 验证邀请码参数
|
||||
if req.InviteCode == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码不能为空"), "")
|
||||
}
|
||||
|
||||
// 3. 查询邀请码是否存在且属于当前代理
|
||||
inviteCodeRecord, err := l.svcCtx.AgentInviteCodeModel.FindOneByCode(l.ctx, req.InviteCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("邀请码不存在"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询邀请码失败, %v", err)
|
||||
}
|
||||
|
||||
// 3. 生成邀请链接
|
||||
// 使用配置中的前端域名,如果没有则使用默认值
|
||||
frontendDomain := l.svcCtx.Config.AdminPromotion.URLDomain
|
||||
if frontendDomain == "" {
|
||||
frontendDomain = "https://example.com" // 默认值,需要配置
|
||||
// 4. 验证邀请码是否属于当前代理
|
||||
if !inviteCodeRecord.AgentId.Valid || inviteCodeRecord.AgentId.Int64 != agent.Id {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("无权使用此邀请码"), "")
|
||||
}
|
||||
inviteLink := fmt.Sprintf("%s/register?invite_code=%s", frontendDomain, inviteCode)
|
||||
|
||||
// 4. 生成二维码URL(使用ImageService)
|
||||
qrCodeUrl := fmt.Sprintf("%s/api/v1/image/qrcode?type=invitation&content=%s", frontendDomain, inviteLink)
|
||||
// 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: inviteLink,
|
||||
QrCodeUrl: qrCodeUrl,
|
||||
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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user