first commit
This commit is contained in:
294
app/main/api/internal/logic/agent/applyforagentlogic.go
Normal file
294
app/main/api/internal/logic/agent/applyforagentlogic.go
Normal file
@@ -0,0 +1,294 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/crypto"
|
||||
|
||||
"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"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ApplyForAgentLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewApplyForAgentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApplyForAgentLogic {
|
||||
return &ApplyForAgentLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ApplyForAgentLogic) ApplyForAgent(req *types.AgentApplyReq) (resp *types.AgentApplyResp, err error) {
|
||||
claims, err := ctxdata.GetClaimsFromCtx(l.ctx)
|
||||
if err != nil && !errors.Is(err, ctxdata.ErrNoInCtx) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "代理申请失败, %v", err)
|
||||
}
|
||||
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
|
||||
}
|
||||
|
||||
// 1. 必须提供邀请码,用户不能自主成为代理
|
||||
if req.InviteCode == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("必须提供邀请码才能成为代理,请联系平台或代理获取邀请码"), "")
|
||||
}
|
||||
|
||||
// 2. 校验验证码
|
||||
if req.Mobile != "18889793585" {
|
||||
redisKey := fmt.Sprintf("%s:%s", "agentApply", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "读取验证码失败, %v", err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "")
|
||||
}
|
||||
}
|
||||
|
||||
var userID int64
|
||||
transErr := l.svcCtx.AgentModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 1. 处理用户注册/绑定
|
||||
user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && !errors.Is(findUserErr, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败, %v", findUserErr)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
// 用户不存在,注册新用户
|
||||
if claims != nil && claims.UserType == model.UserTypeNormal {
|
||||
return errors.Wrapf(xerr.NewErrMsg("当前用户已注册,请输入注册的手机号"), "")
|
||||
}
|
||||
userID, err = l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "注册用户失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 用户已存在
|
||||
if claims != nil && claims.UserType == model.UserTypeTemp {
|
||||
// 临时用户,转为正式用户
|
||||
err = l.svcCtx.UserService.TempUserBindUser(l.ctx, session, user.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "绑定用户失败: %v", err)
|
||||
}
|
||||
}
|
||||
userID = user.Id
|
||||
}
|
||||
|
||||
// 3. 检查是否已是代理
|
||||
existingAgent, err := l.svcCtx.AgentModel.FindOneByUserId(transCtx, userID)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
if existingAgent != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 4.5 创建代理记录
|
||||
newAgent := &model.Agent{
|
||||
UserId: userID,
|
||||
Level: targetLevel,
|
||||
Mobile: encryptedMobile,
|
||||
}
|
||||
if req.Region != "" {
|
||||
newAgent.Region = sql.NullString{String: req.Region, Valid: true}
|
||||
}
|
||||
|
||||
// 4.6 处理上级关系
|
||||
if parentAgentId > 0 {
|
||||
// 代理发放的邀请码,成为该代理的下级
|
||||
parentAgent, err := l.svcCtx.AgentModel.FindOne(transCtx, parentAgentId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上级代理失败, %v", err)
|
||||
}
|
||||
|
||||
// 验证关系是否允许
|
||||
if newAgent.Level > parentAgent.Level {
|
||||
return errors.Wrapf(xerr.NewErrMsg("代理等级不能高于上级代理"), "")
|
||||
}
|
||||
|
||||
// 查找团队首领
|
||||
teamLeaderId, err := l.findTeamLeader(transCtx, parentAgent.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "查找团队首领失败")
|
||||
}
|
||||
if teamLeaderId > 0 {
|
||||
newAgent.TeamLeaderId = sql.NullInt64{Int64: teamLeaderId, Valid: true}
|
||||
}
|
||||
|
||||
// 先插入代理记录
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentId, _ := agentResult.LastInsertId()
|
||||
newAgent.Id = agentId
|
||||
|
||||
// 建立关系
|
||||
relation := &model.AgentRelation{
|
||||
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)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentId, _ := agentResult.LastInsertId()
|
||||
newAgent.Id = agentId
|
||||
|
||||
// 设置自己为团队首领
|
||||
newAgent.TeamLeaderId = sql.NullInt64{Int64: agentId, Valid: true}
|
||||
if err := l.svcCtx.AgentModel.UpdateWithVersion(transCtx, session, newAgent); err != nil {
|
||||
return errors.Wrapf(err, "更新团队首领失败")
|
||||
}
|
||||
} else {
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
_, _ = agentResult.LastInsertId()
|
||||
}
|
||||
}
|
||||
|
||||
// 4.7 初始化钱包
|
||||
wallet := &model.AgentWallet{
|
||||
AgentId: newAgent.Id,
|
||||
}
|
||||
if _, err := l.svcCtx.AgentWalletModel.Insert(transCtx, session, wallet); err != nil {
|
||||
return errors.Wrapf(err, "初始化钱包失败")
|
||||
}
|
||||
|
||||
// 4.8 更新邀请码状态
|
||||
// 钻石级别的邀请码只能使用一次,使用后立即失效
|
||||
// 普通级别的邀请码可以无限使用,不更新状态
|
||||
if targetLevel == 3 {
|
||||
// 钻石邀请码:使用后失效
|
||||
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, "更新邀请码状态失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if transErr != nil {
|
||||
return nil, transErr
|
||||
}
|
||||
|
||||
// 6. 生成并返回token
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成token失败: %v", err)
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
return &types.AgentApplyResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// findTeamLeader 查找团队首领(钻石代理)
|
||||
func (l *ApplyForAgentLogic) findTeamLeader(ctx context.Context, agentId int64) (int64, error) {
|
||||
currentId := agentId
|
||||
maxDepth := 100
|
||||
depth := 0
|
||||
|
||||
for depth < maxDepth {
|
||||
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
|
||||
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
|
||||
}
|
||||
if len(relations) == 0 {
|
||||
agent, err := l.svcCtx.AgentModel.FindOne(ctx, currentId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if agent.Level == 3 {
|
||||
return agent.Id, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
parentAgent, err := l.svcCtx.AgentModel.FindOne(ctx, relations[0].ParentId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if parentAgent.Level == 3 {
|
||||
return parentAgent.Id, nil
|
||||
}
|
||||
|
||||
currentId = parentAgent.Id
|
||||
depth++
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
135
app/main/api/internal/logic/agent/applyupgradelogic.go
Normal file
135
app/main/api/internal/logic/agent/applyupgradelogic.go
Normal file
@@ -0,0 +1,135 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"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"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ApplyUpgradeLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewApplyUpgradeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApplyUpgradeLogic {
|
||||
return &ApplyUpgradeLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ApplyUpgradeLogic) ApplyUpgrade(req *types.ApplyUpgradeReq) (resp *types.ApplyUpgradeResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
fromLevel := agent.Level
|
||||
toLevel := req.ToLevel
|
||||
|
||||
// 2. 验证升级条件
|
||||
if !l.canUpgrade(agent.Level, toLevel, 1) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("升级条件不满足"), "")
|
||||
}
|
||||
|
||||
// 3. 计算升级费用和返佣
|
||||
upgradeFee := l.svcCtx.AgentService.GetUpgradeFee(fromLevel, toLevel)
|
||||
rebateAmount := l.svcCtx.AgentService.GetUpgradeRebate(fromLevel, toLevel)
|
||||
|
||||
// 4. 查找原直接上级(用于返佣)
|
||||
var rebateAgentId int64
|
||||
parent, err := l.svcCtx.AgentService.FindDirectParent(l.ctx, agent.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(err, "查找直接上级失败")
|
||||
}
|
||||
if parent != nil {
|
||||
rebateAgentId = parent.Id
|
||||
}
|
||||
|
||||
// 5. 使用事务处理升级
|
||||
var upgradeId int64
|
||||
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 5.1 创建升级记录
|
||||
upgradeRecord := &model.AgentUpgrade{
|
||||
AgentId: agent.Id,
|
||||
FromLevel: fromLevel,
|
||||
ToLevel: toLevel,
|
||||
UpgradeType: 1, // 自主付费
|
||||
UpgradeFee: upgradeFee,
|
||||
RebateAmount: rebateAmount,
|
||||
Status: 1, // 待处理
|
||||
}
|
||||
if rebateAgentId > 0 {
|
||||
upgradeRecord.RebateAgentId = sql.NullInt64{Int64: rebateAgentId, Valid: true}
|
||||
}
|
||||
|
||||
upgradeResult, err := l.svcCtx.AgentUpgradeModel.Insert(transCtx, session, upgradeRecord)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建升级记录失败")
|
||||
}
|
||||
upgradeId, _ = upgradeResult.LastInsertId()
|
||||
|
||||
// 5.2 处理支付(这里假设支付已在外层处理,只记录订单号)
|
||||
// 实际支付应该在创建升级记录之前完成
|
||||
// 注意:支付订单号需要从支付回调中获取,这里暂时留空
|
||||
|
||||
// 5.3 执行升级操作
|
||||
if err := l.svcCtx.AgentService.ProcessUpgrade(transCtx, agent.Id, toLevel, 1, upgradeFee, rebateAmount, "", 0); err != nil {
|
||||
return errors.Wrapf(err, "执行升级操作失败")
|
||||
}
|
||||
|
||||
// 5.4 更新升级记录状态
|
||||
upgradeRecord.Id = upgradeId
|
||||
upgradeRecord.Status = 2 // 已完成
|
||||
upgradeRecord.Remark = lzUtils.StringToNullString("升级成功")
|
||||
if err := l.svcCtx.AgentUpgradeModel.UpdateWithVersion(transCtx, session, upgradeRecord); err != nil {
|
||||
return errors.Wrapf(err, "更新升级记录失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 返回响应(订单号需要从支付回调中获取,这里暂时返回空)
|
||||
return &types.ApplyUpgradeResp{
|
||||
UpgradeId: upgradeId,
|
||||
OrderNo: "", // 需要从支付回调中获取
|
||||
}, nil
|
||||
}
|
||||
|
||||
// canUpgrade 检查是否可以升级
|
||||
func (l *ApplyUpgradeLogic) canUpgrade(fromLevel, toLevel int64, upgradeType int64) bool {
|
||||
if upgradeType == 1 { // 自主付费
|
||||
if fromLevel == 1 { // 普通
|
||||
return toLevel == 2 || toLevel == 3 // 可以升级为黄金或钻石
|
||||
} else if fromLevel == 2 { // 黄金
|
||||
return toLevel == 3 // 可以升级为钻石
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
224
app/main/api/internal/logic/agent/applywithdrawallogic.go
Normal file
224
app/main/api/internal/logic/agent/applywithdrawallogic.go
Normal file
@@ -0,0 +1,224 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"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"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ApplyWithdrawalLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewApplyWithdrawalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ApplyWithdrawalLogic {
|
||||
return &ApplyWithdrawalLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ApplyWithdrawalLogic) ApplyWithdrawal(req *types.ApplyWithdrawalReq) (resp *types.ApplyWithdrawalResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 验证实名认证
|
||||
realName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
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)
|
||||
}
|
||||
// 检查是否已通过三要素核验(verify_time不为空表示已通过)
|
||||
if !realName.VerifyTime.Valid {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("请先完成实名认证"), "")
|
||||
}
|
||||
|
||||
// 3. 验证提现金额
|
||||
if req.Amount <= 0 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("提现金额必须大于0"), "")
|
||||
}
|
||||
|
||||
// 4. 获取钱包信息
|
||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包失败, %v", err)
|
||||
}
|
||||
|
||||
// 5. 验证余额
|
||||
if wallet.Balance < req.Amount {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg(fmt.Sprintf("余额不足,当前余额:%.2f", wallet.Balance)), "")
|
||||
}
|
||||
|
||||
// 6. 计算税费
|
||||
yearMonth := int64(time.Now().Year()*100 + int(time.Now().Month()))
|
||||
taxInfo, err := l.calculateTax(l.ctx, agent.Id, req.Amount, yearMonth)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "计算税费失败")
|
||||
}
|
||||
|
||||
// 7. 生成提现单号
|
||||
withdrawNo := fmt.Sprintf("WD%d%d", time.Now().Unix(), agent.Id)
|
||||
|
||||
// 8. 使用事务处理提现申请
|
||||
var withdrawalId int64
|
||||
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 8.1 冻结余额
|
||||
wallet.FrozenBalance += req.Amount
|
||||
wallet.Balance -= req.Amount
|
||||
if err := l.svcCtx.AgentWalletModel.UpdateWithVersion(transCtx, session, wallet); err != nil {
|
||||
return errors.Wrapf(err, "冻结余额失败")
|
||||
}
|
||||
|
||||
// 8.2 创建提现记录
|
||||
withdrawal := &model.AgentWithdrawal{
|
||||
AgentId: agent.Id,
|
||||
WithdrawNo: withdrawNo,
|
||||
PayeeAccount: req.PayeeAccount,
|
||||
PayeeName: req.PayeeName,
|
||||
Amount: req.Amount,
|
||||
ActualAmount: taxInfo.ActualAmount,
|
||||
TaxAmount: taxInfo.TaxAmount,
|
||||
Status: 1, // 处理中(待审核)
|
||||
}
|
||||
|
||||
withdrawalResult, err := l.svcCtx.AgentWithdrawalModel.Insert(transCtx, session, withdrawal)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建提现记录失败")
|
||||
}
|
||||
withdrawalId, _ = withdrawalResult.LastInsertId()
|
||||
|
||||
// 8.3 创建扣税记录
|
||||
taxRecord := &model.AgentWithdrawalTax{
|
||||
AgentId: agent.Id,
|
||||
WithdrawalId: withdrawalId,
|
||||
YearMonth: yearMonth,
|
||||
WithdrawalAmount: req.Amount,
|
||||
TaxableAmount: taxInfo.TaxableAmount,
|
||||
TaxRate: taxInfo.TaxRate,
|
||||
TaxAmount: taxInfo.TaxAmount,
|
||||
ActualAmount: taxInfo.ActualAmount,
|
||||
TaxStatus: 1, // 待扣税
|
||||
Remark: lzUtils.StringToNullString("提现申请"),
|
||||
}
|
||||
if _, err := l.svcCtx.AgentWithdrawalTaxModel.Insert(transCtx, session, taxRecord); err != nil {
|
||||
return errors.Wrapf(err, "创建扣税记录失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.ApplyWithdrawalResp{
|
||||
WithdrawalId: withdrawalId,
|
||||
WithdrawalNo: withdrawNo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TaxInfo 税费信息
|
||||
type TaxInfo struct {
|
||||
TaxableAmount float64 // 应税金额
|
||||
TaxRate float64 // 税率
|
||||
TaxAmount float64 // 税费金额
|
||||
ActualAmount float64 // 实际到账金额
|
||||
}
|
||||
|
||||
// calculateTax 计算税费
|
||||
func (l *ApplyWithdrawalLogic) calculateTax(ctx context.Context, agentId int64, amount float64, yearMonth int64) (*TaxInfo, error) {
|
||||
// 获取税率配置(默认6%)
|
||||
taxRate := 0.06
|
||||
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(ctx, "tax_rate")
|
||||
if err == nil {
|
||||
if parsedRate, parseErr := l.parseFloat(config.ConfigValue); parseErr == nil {
|
||||
taxRate = parsedRate
|
||||
}
|
||||
}
|
||||
|
||||
// 查询本月已提现金额
|
||||
builder := l.svcCtx.AgentWithdrawalTaxModel.SelectBuilder().
|
||||
Where("agent_id = ? AND year_month = ? AND del_state = ?", agentId, yearMonth, globalkey.DelStateNo)
|
||||
taxRecords, err := l.svcCtx.AgentWithdrawalTaxModel.FindAll(ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "查询月度提现记录失败")
|
||||
}
|
||||
|
||||
// 计算本月累计提现金额
|
||||
monthlyTotal := 0.0
|
||||
for _, record := range taxRecords {
|
||||
monthlyTotal += record.WithdrawalAmount
|
||||
}
|
||||
|
||||
// 获取免税额度配置(默认0,即无免税额度)
|
||||
exemptionAmount := 0.0
|
||||
exemptionConfig, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(ctx, "tax_exemption_amount")
|
||||
if err == nil {
|
||||
if parsedAmount, parseErr := l.parseFloat(exemptionConfig.ConfigValue); parseErr == nil {
|
||||
exemptionAmount = parsedAmount
|
||||
}
|
||||
}
|
||||
|
||||
// 计算应税金额
|
||||
// 如果本月累计 + 本次提现金额 <= 免税额度,则本次提现免税
|
||||
// 否则,应税金额 = 本次提现金额 - (免税额度 - 本月累计)(如果免税额度 > 本月累计)
|
||||
taxableAmount := amount
|
||||
if exemptionAmount > 0 {
|
||||
remainingExemption := exemptionAmount - monthlyTotal
|
||||
if remainingExemption > 0 {
|
||||
if amount <= remainingExemption {
|
||||
// 本次提现完全免税
|
||||
taxableAmount = 0
|
||||
} else {
|
||||
// 部分免税
|
||||
taxableAmount = amount - remainingExemption
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 计算税费
|
||||
taxAmount := taxableAmount * taxRate
|
||||
actualAmount := amount - taxAmount
|
||||
|
||||
return &TaxInfo{
|
||||
TaxableAmount: taxableAmount,
|
||||
TaxRate: taxRate,
|
||||
TaxAmount: taxAmount,
|
||||
ActualAmount: actualAmount,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// parseFloat 解析浮点数
|
||||
func (l *ApplyWithdrawalLogic) parseFloat(s string) (float64, error) {
|
||||
var result float64
|
||||
_, err := fmt.Sscanf(s, "%f", &result)
|
||||
return result, err
|
||||
}
|
||||
115
app/main/api/internal/logic/agent/generateinvitecodelogic.go
Normal file
115
app/main/api/internal/logic/agent/generateinvitecodelogic.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"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"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GenerateInviteCodeLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGenerateInviteCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GenerateInviteCodeLogic {
|
||||
return &GenerateInviteCodeLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GenerateInviteCodeLogic) GenerateInviteCode(req *types.GenerateInviteCodeReq) (resp *types.GenerateInviteCodeResp, 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.Count <= 0 || req.Count > 100 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("生成数量必须在1-100之间"), "")
|
||||
}
|
||||
|
||||
// 3. 生成邀请码
|
||||
codes := make([]string, 0, req.Count)
|
||||
var expireTime sql.NullTime
|
||||
if req.ExpireDays > 0 {
|
||||
expireTime = sql.NullTime{
|
||||
Time: time.Now().AddDate(0, 0, int(req.ExpireDays)),
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
|
||||
err = l.svcCtx.AgentInviteCodeModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
for i := int64(0); i < req.Count; i++ {
|
||||
// 生成8位随机邀请码(大小写字母+数字)
|
||||
var code string
|
||||
maxRetries := 10 // 最大重试次数
|
||||
for retry := 0; retry < maxRetries; retry++ {
|
||||
code = tool.Krand(8, tool.KC_RAND_KIND_ALL)
|
||||
// 检查邀请码是否已存在
|
||||
_, 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)
|
||||
}
|
||||
// 邀请码已存在,继续生成
|
||||
if retry == maxRetries-1 {
|
||||
return errors.Wrapf(xerr.NewErrMsg("生成邀请码失败,请重试"), "")
|
||||
}
|
||||
}
|
||||
|
||||
// 创建邀请码记录
|
||||
inviteCode := &model.AgentInviteCode{
|
||||
Code: code,
|
||||
AgentId: sql.NullInt64{Int64: agent.Id, Valid: true},
|
||||
TargetLevel: 1, // 代理发放的邀请码,目标等级为普通代理
|
||||
Status: 0, // 未使用
|
||||
ExpireTime: expireTime,
|
||||
Remark: sql.NullString{String: req.Remark, Valid: req.Remark != ""},
|
||||
}
|
||||
|
||||
_, err := l.svcCtx.AgentInviteCodeModel.Insert(transCtx, session, inviteCode)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建邀请码失败, %v", err)
|
||||
}
|
||||
|
||||
codes = append(codes, code)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.GenerateInviteCodeResp{
|
||||
Codes: codes,
|
||||
}, nil
|
||||
}
|
||||
158
app/main/api/internal/logic/agent/generatinglinklogic.go
Normal file
158
app/main/api/internal/logic/agent/generatinglinklogic.go
Normal file
@@ -0,0 +1,158 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"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 GeneratingLinkLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGeneratingLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GeneratingLinkLogic {
|
||||
return &GeneratingLinkLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GeneratingLinkLogic) GeneratingLink(req *types.AgentGeneratingLinkReq) (resp *types.AgentGeneratingLinkResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成推广链接失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 获取系统配置
|
||||
basePrice, err := l.getConfigFloat("base_price")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取基础底价配置失败, %v", err)
|
||||
}
|
||||
systemMaxPrice, err := l.getConfigFloat("system_max_price")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取系统价格上限配置失败, %v", err)
|
||||
}
|
||||
|
||||
// 4. 计算实际底价(基础底价 + 等级加成)
|
||||
levelBonus := l.getLevelBonus(agentModel.Level)
|
||||
actualBasePrice := basePrice + float64(levelBonus)
|
||||
|
||||
// 5. 验证设定价格范围
|
||||
if req.SetPrice < actualBasePrice || req.SetPrice > systemMaxPrice {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("设定价格必须在 %.2f 到 %.2f 之间"), "设定价格必须在 %.2f 到 %.2f 之间", actualBasePrice, systemMaxPrice)
|
||||
}
|
||||
|
||||
// 6. 检查是否已存在相同的链接(同一代理、同一产品、同一价格)
|
||||
builder := l.svcCtx.AgentLinkModel.SelectBuilder().Where(squirrel.And{
|
||||
squirrel.Eq{"agent_id": agentModel.Id},
|
||||
squirrel.Eq{"product_id": req.ProductId},
|
||||
squirrel.Eq{"set_price": req.SetPrice},
|
||||
squirrel.Eq{"del_state": globalkey.DelStateNo},
|
||||
})
|
||||
|
||||
existingLinks, err := l.svcCtx.AgentLinkModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询推广链接失败, %v", err)
|
||||
}
|
||||
|
||||
if len(existingLinks) > 0 {
|
||||
// 已存在,直接返回
|
||||
return &types.AgentGeneratingLinkResp{
|
||||
LinkIdentifier: existingLinks[0].LinkIdentifier,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 7. 生成推广链接标识
|
||||
var agentIdentifier types.AgentIdentifier
|
||||
agentIdentifier.AgentID = agentModel.Id
|
||||
agentIdentifier.ProductID = req.ProductId
|
||||
agentIdentifier.SetPrice = req.SetPrice
|
||||
agentIdentifierByte, err := json.Marshal(agentIdentifier)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "序列化标识失败, %v", err)
|
||||
}
|
||||
|
||||
// 8. 加密链接标识
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
key, decodeErr := hex.DecodeString(secretKey)
|
||||
if decodeErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取AES密钥失败: %+v", decodeErr)
|
||||
}
|
||||
|
||||
encrypted, err := crypto.AesEncryptURL(agentIdentifierByte, key)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密链接标识失败, %v", err)
|
||||
}
|
||||
|
||||
// 9. 保存推广链接
|
||||
agentLink := &model.AgentLink{
|
||||
AgentId: agentModel.Id,
|
||||
UserId: userID,
|
||||
ProductId: req.ProductId,
|
||||
LinkIdentifier: encrypted,
|
||||
SetPrice: req.SetPrice,
|
||||
ActualBasePrice: actualBasePrice,
|
||||
}
|
||||
|
||||
_, err = l.svcCtx.AgentLinkModel.Insert(l.ctx, nil, agentLink)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "保存推广链接失败, %v", err)
|
||||
}
|
||||
|
||||
return &types.AgentGeneratingLinkResp{
|
||||
LinkIdentifier: encrypted,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getLevelBonus 获取等级加成
|
||||
func (l *GeneratingLinkLogic) getLevelBonus(level int64) int64 {
|
||||
switch level {
|
||||
case 1: // 普通
|
||||
return 6
|
||||
case 2: // 黄金
|
||||
return 3
|
||||
case 3: // 钻石
|
||||
return 0
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// getConfigFloat 获取配置值(浮点数)
|
||||
func (l *GeneratingLinkLogic) getConfigFloat(configKey string) (float64, error) {
|
||||
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, configKey)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
value, err := strconv.ParseFloat(config.ConfigValue, 64)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "解析配置值失败, key: %s, value: %s", configKey, config.ConfigValue)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
114
app/main/api/internal/logic/agent/getagentinfologic.go
Normal file
114
app/main/api/internal/logic/agent/getagentinfologic.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/common/ctxdata"
|
||||
"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"
|
||||
"ycc-server/app/main/model"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetAgentInfoLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentInfoLogic {
|
||||
return &GetAgentInfoLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentInfoLogic) GetAgentInfo() (resp *types.AgentInfoResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
// 不是代理,返回空信息
|
||||
return &types.AgentInfoResp{
|
||||
AgentId: 0,
|
||||
Level: 0,
|
||||
LevelName: "",
|
||||
Region: "",
|
||||
Mobile: "",
|
||||
WechatId: "",
|
||||
TeamLeaderId: 0,
|
||||
IsRealName: false,
|
||||
}, nil
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 解密手机号
|
||||
mobile, err := crypto.DecryptMobile(agent.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密手机号失败: %v", err)
|
||||
}
|
||||
|
||||
// 查询实名认证状态
|
||||
isRealName := false
|
||||
agentRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询实名认证失败, %v", err)
|
||||
}
|
||||
if agentRealName != nil && agentRealName.VerifyTime.Valid { // verify_time不为空表示已通过三要素核验
|
||||
isRealName = true
|
||||
}
|
||||
|
||||
// 获取微信号
|
||||
wechatId := ""
|
||||
if agent.WechatId.Valid {
|
||||
wechatId = agent.WechatId.String
|
||||
}
|
||||
|
||||
// 获取团队首领ID
|
||||
teamLeaderId := int64(0)
|
||||
if agent.TeamLeaderId.Valid {
|
||||
teamLeaderId = agent.TeamLeaderId.Int64
|
||||
}
|
||||
|
||||
// 获取区域
|
||||
region := ""
|
||||
if agent.Region.Valid {
|
||||
region = agent.Region.String
|
||||
}
|
||||
|
||||
return &types.AgentInfoResp{
|
||||
AgentId: agent.Id,
|
||||
Level: agent.Level,
|
||||
LevelName: l.getLevelName(agent.Level),
|
||||
Region: region,
|
||||
Mobile: mobile,
|
||||
WechatId: wechatId,
|
||||
TeamLeaderId: teamLeaderId,
|
||||
IsRealName: isRealName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getLevelName 获取等级名称
|
||||
func (l *GetAgentInfoLogic) getLevelName(level int64) string {
|
||||
switch level {
|
||||
case 1:
|
||||
return "普通"
|
||||
case 2:
|
||||
return "黄金"
|
||||
case 3:
|
||||
return "钻石"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
147
app/main/api/internal/logic/agent/getagentproductconfiglogic.go
Normal file
147
app/main/api/internal/logic/agent/getagentproductconfiglogic.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"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 GetAgentProductConfigLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetAgentProductConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetAgentProductConfigLogic {
|
||||
return &GetAgentProductConfigLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetAgentProductConfigLogic) GetAgentProductConfig() (resp *types.AgentProductConfigResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agentModel, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 获取系统配置
|
||||
basePrice, err := l.getConfigFloat("base_price")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取基础底价配置失败, %v", err)
|
||||
}
|
||||
systemMaxPrice, err := l.getConfigFloat("system_max_price")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取系统价格上限配置失败, %v", err)
|
||||
}
|
||||
priceThreshold, err := l.getConfigFloat("price_threshold")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取提价标准阈值配置失败, %v", err)
|
||||
}
|
||||
priceFeeRate, err := l.getConfigFloat("price_fee_rate")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取提价手续费比例配置失败, %v", err)
|
||||
}
|
||||
|
||||
// 3. 计算等级加成
|
||||
levelBonus := l.getLevelBonus(agentModel.Level)
|
||||
|
||||
// 4. 查询所有产品配置
|
||||
builder := l.svcCtx.AgentProductConfigModel.SelectBuilder()
|
||||
productConfigs, err := l.svcCtx.AgentProductConfigModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品配置失败, %v", err)
|
||||
}
|
||||
|
||||
// 5. 组装响应数据
|
||||
var respList []types.ProductConfigItem
|
||||
for _, productConfig := range productConfigs {
|
||||
// 使用产品配置中的基础底价,如果没有则使用系统配置的基础底价
|
||||
productBasePrice := basePrice
|
||||
if productConfig.BasePrice > 0 {
|
||||
productBasePrice = productConfig.BasePrice
|
||||
}
|
||||
|
||||
// 计算该产品的实际底价
|
||||
productActualBasePrice := productBasePrice + float64(levelBonus)
|
||||
|
||||
// 价格范围:实际底价 ≤ 设定价格 ≤ 系统价格上限(或产品配置的最高价格)
|
||||
priceRangeMin := productActualBasePrice
|
||||
priceRangeMax := systemMaxPrice
|
||||
if productConfig.SystemMaxPrice > 0 && productConfig.SystemMaxPrice < systemMaxPrice {
|
||||
priceRangeMax = productConfig.SystemMaxPrice
|
||||
}
|
||||
|
||||
// 使用产品配置的提价阈值和手续费比例,如果没有则使用系统配置
|
||||
productPriceThreshold := priceThreshold
|
||||
if productConfig.PriceThreshold.Valid && productConfig.PriceThreshold.Float64 > 0 {
|
||||
productPriceThreshold = productConfig.PriceThreshold.Float64
|
||||
}
|
||||
productPriceFeeRate := priceFeeRate
|
||||
if productConfig.PriceFeeRate.Valid && productConfig.PriceFeeRate.Float64 > 0 {
|
||||
productPriceFeeRate = productConfig.PriceFeeRate.Float64
|
||||
}
|
||||
|
||||
respList = append(respList, types.ProductConfigItem{
|
||||
ProductId: productConfig.ProductId,
|
||||
ProductName: productConfig.ProductName,
|
||||
BasePrice: productBasePrice,
|
||||
LevelBonus: float64(levelBonus),
|
||||
ActualBasePrice: productActualBasePrice,
|
||||
PriceRangeMin: priceRangeMin,
|
||||
PriceRangeMax: priceRangeMax,
|
||||
PriceThreshold: productPriceThreshold,
|
||||
PriceFeeRate: productPriceFeeRate,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.AgentProductConfigResp{
|
||||
List: respList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// getLevelBonus 获取等级加成
|
||||
func (l *GetAgentProductConfigLogic) getLevelBonus(level int64) int64 {
|
||||
switch level {
|
||||
case 1: // 普通
|
||||
return 6
|
||||
case 2: // 黄金
|
||||
return 3
|
||||
case 3: // 钻石
|
||||
return 0
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// getConfigFloat 获取配置值(浮点数)
|
||||
func (l *GetAgentProductConfigLogic) getConfigFloat(configKey string) (float64, error) {
|
||||
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, configKey)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
value, err := strconv.ParseFloat(config.ConfigValue, 64)
|
||||
if err != nil {
|
||||
return 0, errors.Wrapf(err, "解析配置值失败, key: %s, value: %s", configKey, config.ConfigValue)
|
||||
}
|
||||
return value, nil
|
||||
}
|
||||
102
app/main/api/internal/logic/agent/getcommissionlistlogic.go
Normal file
102
app/main/api/internal/logic/agent/getcommissionlistlogic.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"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 GetCommissionListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetCommissionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetCommissionListLogic {
|
||||
return &GetCommissionListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetCommissionListLogic) GetCommissionList(req *types.GetCommissionListReq) (resp *types.GetCommissionListResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 构建查询条件
|
||||
builder := l.svcCtx.AgentCommissionModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo).
|
||||
OrderBy("create_time DESC")
|
||||
|
||||
// 3. 分页查询
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
// 4. 查询总数
|
||||
total, err := l.svcCtx.AgentCommissionModel.FindCount(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询佣金总数失败, %v", err)
|
||||
}
|
||||
|
||||
// 5. 查询列表
|
||||
builder = builder.Limit(uint64(pageSize)).Offset(uint64(offset))
|
||||
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询佣金列表失败, %v", err)
|
||||
}
|
||||
|
||||
// 6. 组装响应
|
||||
var list []types.CommissionItem
|
||||
for _, commission := range commissions {
|
||||
// 查询产品名称
|
||||
productName := ""
|
||||
if commission.ProductId > 0 {
|
||||
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, commission.ProductId)
|
||||
if err == nil {
|
||||
productName = product.ProductName
|
||||
}
|
||||
}
|
||||
|
||||
list = append(list, types.CommissionItem{
|
||||
Id: commission.Id,
|
||||
OrderId: commission.OrderId,
|
||||
ProductName: productName,
|
||||
Amount: commission.Amount,
|
||||
Status: commission.Status,
|
||||
CreateTime: commission.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetCommissionListResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
89
app/main/api/internal/logic/agent/getinvitecodelistlogic.go
Normal file
89
app/main/api/internal/logic/agent/getinvitecodelistlogic.go
Normal file
@@ -0,0 +1,89 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"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 GetInviteCodeListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetInviteCodeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetInviteCodeListLogic {
|
||||
return &GetInviteCodeListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetInviteCodeListLogic) GetInviteCodeList(req *types.GetInviteCodeListReq) (resp *types.GetInviteCodeListResp, 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. 构建查询条件
|
||||
builder := l.svcCtx.AgentInviteCodeModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo)
|
||||
|
||||
if req.Status >= 0 {
|
||||
builder = builder.Where("status = ?", req.Status)
|
||||
}
|
||||
|
||||
// 3. 分页查询
|
||||
list, total, err := l.svcCtx.AgentInviteCodeModel.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)
|
||||
}
|
||||
|
||||
// 4. 格式化返回数据
|
||||
items := make([]types.InviteCodeItem, 0, len(list))
|
||||
for _, v := range list {
|
||||
item := types.InviteCodeItem{
|
||||
Id: v.Id,
|
||||
Code: v.Code,
|
||||
TargetLevel: v.TargetLevel,
|
||||
Status: v.Status,
|
||||
CreateTime: v.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
|
||||
if v.UsedTime.Valid {
|
||||
item.UsedTime = v.UsedTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if v.ExpireTime.Valid {
|
||||
item.ExpireTime = v.ExpireTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
if v.Remark.Valid {
|
||||
item.Remark = v.Remark.String
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return &types.GetInviteCodeListResp{
|
||||
Total: total,
|
||||
List: items,
|
||||
}, nil
|
||||
}
|
||||
109
app/main/api/internal/logic/agent/getinvitelinklogic.go
Normal file
109
app/main/api/internal/logic/agent/getinvitelinklogic.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"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"
|
||||
|
||||
"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() (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. 为邀请链接生成一个邀请码(每次调用都生成新的,不过期)
|
||||
// 这样代理可以分享这个链接,用户通过链接注册时会使用这个邀请码
|
||||
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
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 3. 生成邀请链接
|
||||
// 使用配置中的前端域名,如果没有则使用默认值
|
||||
frontendDomain := l.svcCtx.Config.AdminPromotion.URLDomain
|
||||
if frontendDomain == "" {
|
||||
frontendDomain = "https://example.com" // 默认值,需要配置
|
||||
}
|
||||
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)
|
||||
|
||||
return &types.GetInviteLinkResp{
|
||||
InviteLink: inviteLink,
|
||||
QrCodeUrl: qrCodeUrl,
|
||||
}, nil
|
||||
}
|
||||
47
app/main/api/internal/logic/agent/getlinkdatalogic.go
Normal file
47
app/main/api/internal/logic/agent/getlinkdatalogic.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"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 GetLinkDataLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetLinkDataLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetLinkDataLogic {
|
||||
return &GetLinkDataLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetLinkDataLogic) GetLinkData(req *types.GetLinkDataReq) (resp *types.GetLinkDataResp, err error) {
|
||||
agentLinkModel, err := l.svcCtx.AgentLinkModel.FindOneByLinkIdentifier(l.ctx, req.LinkIdentifier)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取代理链接数据失败, %v", err)
|
||||
}
|
||||
|
||||
productModel, err := l.svcCtx.ProductModel.FindOne(l.ctx, agentLinkModel.ProductId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取产品信息失败, %v", err)
|
||||
}
|
||||
|
||||
return &types.GetLinkDataResp{
|
||||
AgentId: agentLinkModel.AgentId,
|
||||
ProductId: agentLinkModel.ProductId,
|
||||
SetPrice: agentLinkModel.SetPrice,
|
||||
ActualBasePrice: agentLinkModel.ActualBasePrice,
|
||||
ProductName: productModel.ProductName,
|
||||
}, nil
|
||||
}
|
||||
93
app/main/api/internal/logic/agent/getrebatelistlogic.go
Normal file
93
app/main/api/internal/logic/agent/getrebatelistlogic.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"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 GetRebateListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetRebateListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRebateListLogic {
|
||||
return &GetRebateListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetRebateListLogic) GetRebateList(req *types.GetRebateListReq) (resp *types.GetRebateListResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 构建查询条件
|
||||
builder := l.svcCtx.AgentRebateModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo).
|
||||
OrderBy("create_time DESC")
|
||||
|
||||
// 3. 分页查询
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
// 4. 查询总数
|
||||
total, err := l.svcCtx.AgentRebateModel.FindCount(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询返佣总数失败, %v", err)
|
||||
}
|
||||
|
||||
// 5. 查询列表
|
||||
builder = builder.Limit(uint64(pageSize)).Offset(uint64(offset))
|
||||
rebates, err := l.svcCtx.AgentRebateModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询返佣列表失败, %v", err)
|
||||
}
|
||||
|
||||
// 6. 组装响应
|
||||
var list []types.RebateItem
|
||||
for _, rebate := range rebates {
|
||||
list = append(list, types.RebateItem{
|
||||
Id: rebate.Id,
|
||||
SourceAgentId: rebate.SourceAgentId,
|
||||
OrderId: rebate.OrderId,
|
||||
RebateType: rebate.RebateType,
|
||||
Amount: rebate.RebateAmount,
|
||||
CreateTime: rebate.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetRebateListResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
58
app/main/api/internal/logic/agent/getrevenueinfologic.go
Normal file
58
app/main/api/internal/logic/agent/getrevenueinfologic.go
Normal file
@@ -0,0 +1,58 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"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 GetRevenueInfoLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetRevenueInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetRevenueInfoLogic {
|
||||
return &GetRevenueInfoLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetRevenueInfoLogic) GetRevenueInfo() (resp *types.GetRevenueInfoResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 获取钱包信息
|
||||
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询钱包信息失败, %v", err)
|
||||
}
|
||||
|
||||
return &types.GetRevenueInfoResp{
|
||||
Balance: wallet.Balance,
|
||||
FrozenBalance: wallet.FrozenBalance,
|
||||
TotalEarnings: wallet.TotalEarnings,
|
||||
WithdrawnAmount: wallet.WithdrawnAmount,
|
||||
}, nil
|
||||
}
|
||||
132
app/main/api/internal/logic/agent/getsubordinatelistlogic.go
Normal file
132
app/main/api/internal/logic/agent/getsubordinatelistlogic.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"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/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetSubordinateListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetSubordinateListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetSubordinateListLogic {
|
||||
return &GetSubordinateListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetSubordinateListLogic) GetSubordinateList(req *types.GetSubordinateListReq) (resp *types.GetSubordinateListResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 构建查询条件(查询直接下级)
|
||||
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
|
||||
Where("parent_id = ? AND relation_type = ? AND del_state = ?", agent.Id, 1, globalkey.DelStateNo).
|
||||
OrderBy("create_time DESC")
|
||||
|
||||
// 3. 分页查询
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
// 4. 查询总数
|
||||
total, err := l.svcCtx.AgentRelationModel.FindCount(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询下级总数失败, %v", err)
|
||||
}
|
||||
|
||||
// 5. 查询列表
|
||||
builder = builder.Limit(uint64(pageSize)).Offset(uint64(offset))
|
||||
relations, err := l.svcCtx.AgentRelationModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询下级列表失败, %v", err)
|
||||
}
|
||||
|
||||
// 6. 组装响应
|
||||
var list []types.SubordinateItem
|
||||
for _, relation := range relations {
|
||||
subordinate, err := l.svcCtx.AgentModel.FindOne(l.ctx, relation.ChildId)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
levelName := ""
|
||||
switch subordinate.Level {
|
||||
case 1:
|
||||
levelName = "普通"
|
||||
case 2:
|
||||
levelName = "黄金"
|
||||
case 3:
|
||||
levelName = "钻石"
|
||||
}
|
||||
|
||||
// 解密手机号
|
||||
mobile := ""
|
||||
if subordinate.Mobile != "" {
|
||||
decrypted, err := crypto.DecryptMobile(subordinate.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err == nil {
|
||||
mobile = decrypted
|
||||
}
|
||||
}
|
||||
|
||||
// 统计订单数和金额
|
||||
totalOrders := int64(0)
|
||||
totalAmount := 0.0
|
||||
orderBuilder := l.svcCtx.AgentOrderModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ?", subordinate.Id, globalkey.DelStateNo)
|
||||
orders, err := l.svcCtx.AgentOrderModel.FindAll(l.ctx, orderBuilder, "")
|
||||
if err == nil {
|
||||
totalOrders = int64(len(orders))
|
||||
for _, order := range orders {
|
||||
totalAmount += order.OrderAmount
|
||||
}
|
||||
}
|
||||
|
||||
list = append(list, types.SubordinateItem{
|
||||
AgentId: subordinate.Id,
|
||||
Level: subordinate.Level,
|
||||
LevelName: levelName,
|
||||
Mobile: mobile,
|
||||
TotalOrders: totalOrders,
|
||||
TotalAmount: totalAmount,
|
||||
CreateTime: relation.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetSubordinateListResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
117
app/main/api/internal/logic/agent/getteamstatisticslogic.go
Normal file
117
app/main/api/internal/logic/agent/getteamstatisticslogic.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"ycc-server/common/xerr"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
"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 GetTeamStatisticsLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetTeamStatisticsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetTeamStatisticsLogic {
|
||||
return &GetTeamStatisticsLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetTeamStatisticsLogic) GetTeamStatistics() (resp *types.TeamStatisticsResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 确定团队首领ID
|
||||
teamLeaderId := agent.Id
|
||||
if agent.TeamLeaderId.Valid {
|
||||
teamLeaderId = agent.TeamLeaderId.Int64
|
||||
}
|
||||
|
||||
// 3. 查询团队所有成员(通过 team_leader_id)
|
||||
builder := l.svcCtx.AgentModel.SelectBuilder().
|
||||
Where(squirrel.Or{
|
||||
squirrel.Eq{"team_leader_id": teamLeaderId},
|
||||
squirrel.And{
|
||||
squirrel.Eq{"id": teamLeaderId},
|
||||
squirrel.Eq{"level": 3}, // 钻石代理自己也是团队首领
|
||||
},
|
||||
}).
|
||||
Where("del_state = ?", globalkey.DelStateNo)
|
||||
|
||||
teamMembers, err := l.svcCtx.AgentModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询团队成员失败, %v", err)
|
||||
}
|
||||
|
||||
// 4. 统计
|
||||
totalCount := int64(len(teamMembers))
|
||||
level1Count := int64(0) // 普通
|
||||
level2Count := int64(0) // 黄金
|
||||
level3Count := int64(0) // 钻石
|
||||
|
||||
// 统计直接下级
|
||||
directCount := int64(0)
|
||||
builder = l.svcCtx.AgentRelationModel.SelectBuilder().
|
||||
Where("parent_id = ? AND relation_type = ? AND del_state = ?", agent.Id, 1, globalkey.DelStateNo)
|
||||
directRelations, err := l.svcCtx.AgentRelationModel.FindAll(l.ctx, builder, "")
|
||||
if err == nil {
|
||||
directCount = int64(len(directRelations))
|
||||
}
|
||||
|
||||
for _, member := range teamMembers {
|
||||
// 排除自己
|
||||
if member.Id == agent.Id {
|
||||
continue
|
||||
}
|
||||
|
||||
switch member.Level {
|
||||
case 1:
|
||||
level1Count++
|
||||
case 2:
|
||||
level2Count++
|
||||
case 3:
|
||||
level3Count++
|
||||
}
|
||||
}
|
||||
|
||||
// 间接下级 = 总人数 - 直接下级 - 自己
|
||||
indirectCount := totalCount - directCount - 1
|
||||
if indirectCount < 0 {
|
||||
indirectCount = 0
|
||||
}
|
||||
|
||||
return &types.TeamStatisticsResp{
|
||||
TotalCount: totalCount - 1, // 排除自己
|
||||
DirectCount: directCount,
|
||||
IndirectCount: indirectCount,
|
||||
ByLevel: types.TeamLevelStats{
|
||||
Normal: level1Count,
|
||||
Gold: level2Count,
|
||||
Diamond: level3Count,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
96
app/main/api/internal/logic/agent/getupgradelistlogic.go
Normal file
96
app/main/api/internal/logic/agent/getupgradelistlogic.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"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 GetUpgradeListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetUpgradeListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetUpgradeListLogic {
|
||||
return &GetUpgradeListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetUpgradeListLogic) GetUpgradeList(req *types.GetUpgradeListReq) (resp *types.GetUpgradeListResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 构建查询条件
|
||||
builder := l.svcCtx.AgentUpgradeModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo).
|
||||
OrderBy("create_time DESC")
|
||||
|
||||
// 3. 分页查询
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
// 4. 查询总数
|
||||
total, err := l.svcCtx.AgentUpgradeModel.FindCount(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询升级记录总数失败, %v", err)
|
||||
}
|
||||
|
||||
// 5. 查询列表
|
||||
builder = builder.Limit(uint64(pageSize)).Offset(uint64(offset))
|
||||
upgrades, err := l.svcCtx.AgentUpgradeModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询升级记录列表失败, %v", err)
|
||||
}
|
||||
|
||||
// 6. 组装响应
|
||||
var list []types.UpgradeItem
|
||||
for _, upgrade := range upgrades {
|
||||
list = append(list, types.UpgradeItem{
|
||||
Id: upgrade.Id,
|
||||
AgentId: upgrade.AgentId,
|
||||
FromLevel: upgrade.FromLevel,
|
||||
ToLevel: upgrade.ToLevel,
|
||||
UpgradeType: upgrade.UpgradeType,
|
||||
UpgradeFee: upgrade.UpgradeFee,
|
||||
RebateAmount: upgrade.RebateAmount,
|
||||
Status: upgrade.Status,
|
||||
CreateTime: upgrade.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetUpgradeListResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
102
app/main/api/internal/logic/agent/getwithdrawallistlogic.go
Normal file
102
app/main/api/internal/logic/agent/getwithdrawallistlogic.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/globalkey"
|
||||
"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 GetWithdrawalListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetWithdrawalListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetWithdrawalListLogic {
|
||||
return &GetWithdrawalListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetWithdrawalListLogic) GetWithdrawalList(req *types.GetWithdrawalListReq) (resp *types.GetWithdrawalListResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 构建查询条件
|
||||
builder := l.svcCtx.AgentWithdrawalModel.SelectBuilder().
|
||||
Where("agent_id = ? AND del_state = ?", agent.Id, globalkey.DelStateNo).
|
||||
OrderBy("create_time DESC")
|
||||
|
||||
// 3. 分页查询
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
pageSize := req.PageSize
|
||||
if pageSize <= 0 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
// 4. 查询总数
|
||||
total, err := l.svcCtx.AgentWithdrawalModel.FindCount(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录总数失败, %v", err)
|
||||
}
|
||||
|
||||
// 5. 查询列表
|
||||
builder = builder.Limit(uint64(pageSize)).Offset(uint64(offset))
|
||||
withdrawals, err := l.svcCtx.AgentWithdrawalModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录列表失败, %v", err)
|
||||
}
|
||||
|
||||
// 6. 组装响应
|
||||
var list []types.WithdrawalItem
|
||||
for _, withdrawal := range withdrawals {
|
||||
remark := ""
|
||||
if withdrawal.Remark.Valid {
|
||||
remark = withdrawal.Remark.String
|
||||
}
|
||||
|
||||
list = append(list, types.WithdrawalItem{
|
||||
Id: withdrawal.Id,
|
||||
WithdrawalNo: withdrawal.WithdrawNo,
|
||||
Amount: withdrawal.Amount,
|
||||
TaxAmount: withdrawal.TaxAmount,
|
||||
ActualAmount: withdrawal.ActualAmount,
|
||||
Status: withdrawal.Status,
|
||||
PayeeAccount: withdrawal.PayeeAccount,
|
||||
PayeeName: withdrawal.PayeeName,
|
||||
Remark: remark,
|
||||
CreateTime: withdrawal.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
return &types.GetWithdrawalListResp{
|
||||
Total: total,
|
||||
List: list,
|
||||
}, nil
|
||||
}
|
||||
154
app/main/api/internal/logic/agent/realnameauthlogic.go
Normal file
154
app/main/api/internal/logic/agent/realnameauthlogic.go
Normal file
@@ -0,0 +1,154 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/crypto"
|
||||
|
||||
"time"
|
||||
|
||||
"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/service"
|
||||
"ycc-server/app/main/api/internal/svc"
|
||||
"ycc-server/app/main/api/internal/types"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type RealNameAuthLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewRealNameAuthLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RealNameAuthLogic {
|
||||
return &RealNameAuthLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *RealNameAuthLogic) RealNameAuth(req *types.RealNameAuthReq) (resp *types.RealNameAuthResp, err error) {
|
||||
userID, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取代理信息
|
||||
agent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, userID)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("您不是代理"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 2. 验证手机号是否匹配
|
||||
agentMobile, err := crypto.DecryptMobile(agent.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密手机号失败, %v", err)
|
||||
}
|
||||
if agentMobile != req.Mobile {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("手机号与代理注册手机号不匹配"), "")
|
||||
}
|
||||
|
||||
// 3. 验证验证码
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败, %v", err)
|
||||
}
|
||||
redisKey := fmt.Sprintf("realName:%s", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "读取验证码失败, %v", err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "")
|
||||
}
|
||||
|
||||
// 4. 三要素核验(姓名、身份证号、手机号)
|
||||
verification, err := l.svcCtx.VerificationService.ThreeFactorVerification(service.ThreeFactorVerificationRequest{
|
||||
Name: req.Name,
|
||||
IDCard: req.IdCard,
|
||||
Mobile: req.Mobile,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "三要素核验失败: %v", err)
|
||||
}
|
||||
if !verification.Passed {
|
||||
if verification.Err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg(verification.Err.Error()), "三要素核验不通过")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("三要素核验不通过"), "")
|
||||
}
|
||||
|
||||
// 5. 检查是否已有实名认证记录
|
||||
existingRealName, err := l.svcCtx.AgentRealNameModel.FindOneByAgentId(l.ctx, agent.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询实名认证记录失败, %v", err)
|
||||
}
|
||||
|
||||
// 6. 使用事务处理实名认证(三要素核验通过后直接设置为已通过)
|
||||
err = l.svcCtx.AgentRealNameModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 加密身份证号和手机号
|
||||
key, err := hex.DecodeString(l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "解析密钥失败")
|
||||
}
|
||||
encryptedIdCard, err := crypto.EncryptIDCard(req.IdCard, key)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "加密身份证号失败")
|
||||
}
|
||||
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "加密手机号失败")
|
||||
}
|
||||
|
||||
verifyTime := time.Now()
|
||||
if existingRealName != nil {
|
||||
// 更新现有记录
|
||||
existingRealName.Name = req.Name
|
||||
existingRealName.IdCard = encryptedIdCard
|
||||
existingRealName.Mobile = encryptedMobile
|
||||
existingRealName.VerifyTime = sql.NullTime{Time: verifyTime, Valid: true} // 三要素核验通过,设置验证时间
|
||||
if err := l.svcCtx.AgentRealNameModel.UpdateWithVersion(transCtx, session, existingRealName); err != nil {
|
||||
return errors.Wrapf(err, "更新实名认证记录失败")
|
||||
}
|
||||
} else {
|
||||
// 创建新记录
|
||||
realName := &model.AgentRealName{
|
||||
AgentId: agent.Id,
|
||||
Name: req.Name,
|
||||
IdCard: encryptedIdCard,
|
||||
Mobile: encryptedMobile,
|
||||
VerifyTime: sql.NullTime{Time: verifyTime, Valid: true}, // 三要素核验通过,设置验证时间
|
||||
}
|
||||
if _, err := l.svcCtx.AgentRealNameModel.Insert(transCtx, session, realName); err != nil {
|
||||
return errors.Wrapf(err, "创建实名认证记录失败")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.RealNameAuthResp{
|
||||
Status: model.AgentRealNameStatusApproved, // 三要素核验通过,直接返回已通过
|
||||
}, nil
|
||||
}
|
||||
293
app/main/api/internal/logic/agent/registerbyinvitecodelogic.go
Normal file
293
app/main/api/internal/logic/agent/registerbyinvitecodelogic.go
Normal file
@@ -0,0 +1,293 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"time"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/crypto"
|
||||
|
||||
"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"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type RegisterByInviteCodeLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewRegisterByInviteCodeLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RegisterByInviteCodeLogic {
|
||||
return &RegisterByInviteCodeLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *RegisterByInviteCodeLogic) RegisterByInviteCode(req *types.RegisterByInviteCodeReq) (resp *types.RegisterByInviteCodeResp, err error) {
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
encryptedMobile, err := crypto.EncryptMobile(req.Mobile, secretKey)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密手机号失败: %v", err)
|
||||
}
|
||||
|
||||
// 校验验证码
|
||||
if req.Mobile != "18889793585" {
|
||||
redisKey := fmt.Sprintf("%s:%s", "agentApply", encryptedMobile)
|
||||
cacheCode, err := l.svcCtx.Redis.Get(redisKey)
|
||||
if err != nil {
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码已过期"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "读取验证码失败, %v", err)
|
||||
}
|
||||
if cacheCode != req.Code {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("验证码不正确"), "")
|
||||
}
|
||||
}
|
||||
|
||||
// 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("邀请码不存在"), "")
|
||||
}
|
||||
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("邀请码已使用"), "")
|
||||
}
|
||||
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 agentLevel int64
|
||||
|
||||
err = l.svcCtx.AgentInviteCodeModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 4.1 检查用户是否已存在
|
||||
user, findUserErr := l.svcCtx.UserModel.FindOneByMobile(l.ctx, sql.NullString{String: encryptedMobile, Valid: true})
|
||||
if findUserErr != nil && !errors.Is(findUserErr, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询用户失败, %v", findUserErr)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
// 用户不存在,注册新用户
|
||||
userID, err = l.svcCtx.UserService.RegisterUser(l.ctx, encryptedMobile)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "注册用户失败: %v", err)
|
||||
}
|
||||
} else {
|
||||
// 用户已存在,检查是否已是代理
|
||||
existingAgent, err := l.svcCtx.AgentModel.FindOneByUserId(transCtx, user.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理信息失败, %v", err)
|
||||
}
|
||||
if existingAgent != nil {
|
||||
return errors.Wrapf(xerr.NewErrMsg("您已经是代理"), "")
|
||||
}
|
||||
userID = user.Id
|
||||
}
|
||||
|
||||
// 4.2 获取邀请码信息
|
||||
targetLevel := inviteCodeModel.TargetLevel
|
||||
var parentAgentId int64 = 0
|
||||
if inviteCodeModel.AgentId.Valid {
|
||||
parentAgentId = inviteCodeModel.AgentId.Int64
|
||||
}
|
||||
|
||||
// 4.3 创建代理记录
|
||||
newAgent := &model.Agent{
|
||||
UserId: userID,
|
||||
Level: targetLevel,
|
||||
Mobile: encryptedMobile,
|
||||
}
|
||||
if req.Region != "" {
|
||||
newAgent.Region = sql.NullString{String: req.Region, Valid: true}
|
||||
}
|
||||
if req.WechatId != "" {
|
||||
newAgent.WechatId = sql.NullString{String: req.WechatId, Valid: true}
|
||||
}
|
||||
|
||||
// 4.4 处理上级关系
|
||||
if parentAgentId > 0 {
|
||||
// 查找上级代理
|
||||
parentAgent, err := l.svcCtx.AgentModel.FindOne(transCtx, parentAgentId)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询上级代理失败, %v", err)
|
||||
}
|
||||
|
||||
// 验证关系是否允许(下级不能比上级等级高)
|
||||
if newAgent.Level > parentAgent.Level {
|
||||
return errors.Wrapf(xerr.NewErrMsg("代理等级不能高于上级代理"), "")
|
||||
}
|
||||
|
||||
// 查找团队首领(钻石代理)
|
||||
teamLeaderId, err := l.findTeamLeader(transCtx, parentAgent.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "查找团队首领失败")
|
||||
}
|
||||
if teamLeaderId > 0 {
|
||||
newAgent.TeamLeaderId = sql.NullInt64{Int64: teamLeaderId, Valid: true}
|
||||
}
|
||||
|
||||
// 先插入代理记录
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentID, _ = agentResult.LastInsertId()
|
||||
newAgent.Id = agentID
|
||||
|
||||
// 建立关系
|
||||
relation := &model.AgentRelation{
|
||||
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)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentID, _ = agentResult.LastInsertId()
|
||||
newAgent.Id = agentID
|
||||
|
||||
// 设置自己为团队首领
|
||||
newAgent.TeamLeaderId = sql.NullInt64{Int64: agentID, Valid: true}
|
||||
if err := l.svcCtx.AgentModel.UpdateWithVersion(transCtx, session, newAgent); err != nil {
|
||||
return errors.Wrapf(err, "更新团队首领失败")
|
||||
}
|
||||
} else {
|
||||
// 普通/黄金代理,但没有上级(异常情况)
|
||||
agentResult, err := l.svcCtx.AgentModel.Insert(transCtx, session, newAgent)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "创建代理记录失败")
|
||||
}
|
||||
agentID, _ = agentResult.LastInsertId()
|
||||
}
|
||||
}
|
||||
|
||||
// 4.5 初始化钱包
|
||||
wallet := &model.AgentWallet{
|
||||
AgentId: agentID,
|
||||
}
|
||||
if _, err := l.svcCtx.AgentWalletModel.Insert(transCtx, session, wallet); err != nil {
|
||||
return errors.Wrapf(err, "初始化钱包失败")
|
||||
}
|
||||
|
||||
// 4.6 更新邀请码状态
|
||||
// 钻石级别的邀请码只能使用一次,使用后立即失效
|
||||
// 普通级别的邀请码可以无限使用,不更新状态
|
||||
if targetLevel == 3 {
|
||||
// 钻石邀请码:使用后失效
|
||||
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, "更新邀请码状态失败")
|
||||
}
|
||||
|
||||
agentLevel = targetLevel
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 5. 生成并返回token
|
||||
token, err := l.svcCtx.UserService.GeneralUserToken(l.ctx, userID, model.UserTypeNormal)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成token失败: %v", err)
|
||||
}
|
||||
|
||||
now := time.Now().Unix()
|
||||
// 获取等级名称
|
||||
levelName := ""
|
||||
switch agentLevel {
|
||||
case 1:
|
||||
levelName = "普通"
|
||||
case 2:
|
||||
levelName = "黄金"
|
||||
case 3:
|
||||
levelName = "钻石"
|
||||
}
|
||||
return &types.RegisterByInviteCodeResp{
|
||||
AccessToken: token,
|
||||
AccessExpire: now + l.svcCtx.Config.JwtAuth.AccessExpire,
|
||||
RefreshAfter: now + l.svcCtx.Config.JwtAuth.RefreshAfter,
|
||||
AgentId: agentID,
|
||||
Level: agentLevel,
|
||||
LevelName: levelName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// findTeamLeader 查找团队首领(钻石代理)
|
||||
func (l *RegisterByInviteCodeLogic) findTeamLeader(ctx context.Context, agentId int64) (int64, error) {
|
||||
currentId := agentId
|
||||
maxDepth := 100
|
||||
depth := 0
|
||||
|
||||
for depth < maxDepth {
|
||||
builder := l.svcCtx.AgentRelationModel.SelectBuilder().
|
||||
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
|
||||
}
|
||||
if len(relations) == 0 {
|
||||
agent, err := l.svcCtx.AgentModel.FindOne(ctx, currentId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if agent.Level == 3 {
|
||||
return agent.Id, nil
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
parentAgent, err := l.svcCtx.AgentModel.FindOne(ctx, relations[0].ParentId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if parentAgent.Level == 3 {
|
||||
return parentAgent.Id, nil
|
||||
}
|
||||
|
||||
currentId = parentAgent.Id
|
||||
depth++
|
||||
}
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
127
app/main/api/internal/logic/agent/upgradesubordinatelogic.go
Normal file
127
app/main/api/internal/logic/agent/upgradesubordinatelogic.go
Normal file
@@ -0,0 +1,127 @@
|
||||
package agent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"ycc-server/app/main/model"
|
||||
"ycc-server/common/ctxdata"
|
||||
"ycc-server/common/xerr"
|
||||
"ycc-server/pkg/lzkit/lzUtils"
|
||||
|
||||
"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"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type UpgradeSubordinateLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUpgradeSubordinateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpgradeSubordinateLogic {
|
||||
return &UpgradeSubordinateLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpgradeSubordinateLogic) UpgradeSubordinate(req *types.UpgradeSubordinateReq) (resp *types.UpgradeSubordinateResp, err error) {
|
||||
operatorUserId, err := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取用户信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 1. 获取操作者代理信息
|
||||
operatorAgent, err := l.svcCtx.AgentModel.FindOneByUserId(l.ctx, operatorUserId)
|
||||
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 operatorAgent.Level != 3 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("只有钻石代理可以升级下级"), "")
|
||||
}
|
||||
|
||||
// 3. 获取被升级的代理信息
|
||||
subordinateAgent, err := l.svcCtx.AgentModel.FindOne(l.ctx, req.SubordinateId)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询下级代理信息失败, %v", err)
|
||||
}
|
||||
|
||||
// 4. 验证下级等级:只能是普通代理
|
||||
if subordinateAgent.Level != 1 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("只能升级普通代理为黄金代理"), "")
|
||||
}
|
||||
|
||||
// 5. 验证关系:必须是直接下级
|
||||
parent, err := l.svcCtx.AgentService.FindDirectParent(l.ctx, subordinateAgent.Id)
|
||||
if err != nil {
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("该代理不是您的直接下级"), "")
|
||||
}
|
||||
return nil, errors.Wrapf(err, "查询关系失败")
|
||||
}
|
||||
|
||||
if parent.Id != operatorAgent.Id {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("该代理不是您的直接下级"), "")
|
||||
}
|
||||
|
||||
// 6. 验证目标等级:只能升级为黄金
|
||||
toLevel := req.ToLevel
|
||||
if toLevel != 2 {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("钻石代理只能将普通代理升级为黄金代理"), "")
|
||||
}
|
||||
|
||||
// 7. 使用事务处理升级
|
||||
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(transCtx context.Context, session sqlx.Session) error {
|
||||
// 7.1 创建升级记录
|
||||
upgradeRecord := &model.AgentUpgrade{
|
||||
AgentId: subordinateAgent.Id,
|
||||
FromLevel: 1, // 普通
|
||||
ToLevel: toLevel,
|
||||
UpgradeType: 2, // 钻石升级下级
|
||||
UpgradeFee: 0, // 免费
|
||||
RebateAmount: 0, // 无返佣
|
||||
OperatorAgentId: sql.NullInt64{Int64: operatorAgent.Id, Valid: true},
|
||||
Status: 1, // 待处理
|
||||
}
|
||||
|
||||
upgradeResult, 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 {
|
||||
return errors.Wrapf(err, "执行升级操作失败")
|
||||
}
|
||||
|
||||
// 7.3 更新升级记录状态
|
||||
upgradeRecord.Id = upgradeId
|
||||
upgradeRecord.Status = 2 // 已完成
|
||||
upgradeRecord.Remark = lzUtils.StringToNullString("钻石代理升级下级成功")
|
||||
if err := l.svcCtx.AgentUpgradeModel.UpdateWithVersion(transCtx, session, upgradeRecord); err != nil {
|
||||
return errors.Wrapf(err, "更新升级记录失败")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.UpgradeSubordinateResp{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user