This commit is contained in:
2026-04-29 11:38:59 +08:00
parent e96e3f9583
commit 7d363f4e8a
30 changed files with 1135 additions and 215 deletions

View File

@@ -251,6 +251,7 @@ type (
AgentId *int64 `form:"agent_id,optional"` // 代理ID可选
RelationAgentId *int64 `form:"relation_agent_id,optional"` // 关联代理ID可选
Type *string `form:"type,optional"` // 奖励类型(可选)
Status *int64 `form:"status,optional"` // 状态(可选)
}
// 代理奖励列表项
@@ -260,6 +261,8 @@ type (
RelationAgentId int64 `json:"relation_agent_id"` // 关联代理ID
Amount float64 `json:"amount"` // 金额
Type string `json:"type"` // 奖励类型
Status int64 `json:"status"` // 状态0=正常, 1=已取消
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
}
@@ -319,6 +322,7 @@ type (
AgentId int64 `json:"agent_id"` // 代理ID
DeductedAgentId int64 `json:"deducted_agent_id"` // 被扣代理ID
Amount float64 `json:"amount"` // 金额
RefundedAmount float64 `json:"refunded_amount"` // 已退款金额
ProductName string `json:"product_name"` // 产品名
Type string `json:"type"` // 类型cost/pricing
Status int64 `json:"status"` // 状态

View File

@@ -61,7 +61,6 @@ Applepay:
LoadPrivateKeyPath: "etc/merchant/AuthKey_XXXXXXXXXXXX.p8"
SystemConfig:
ThreeVerify: true
CommissionSafeMode: false # 佣金安全防御模式true-冻结模式false-直接结算模式
WechatH5:
AppID: "xxxx"
AppSecret: "xxxx"

View File

@@ -100,8 +100,7 @@ type YushanConfig struct {
Url string
}
type SystemConfig struct {
ThreeVerify bool // 是否开启三级实名认证
CommissionSafeMode bool // 佣金安全防御模式true-冻结模式(status=1,进入frozen_balance)false-直接结算(status=0,进入balance)
ThreeVerify bool // 是否开启三级实名认证
}
type WechatH5Config struct {
AppID string

View File

@@ -36,6 +36,9 @@ func (l *AdminGetAgentRewardListLogic) AdminGetAgentRewardList(req *types.AdminG
if req.Type != nil && *req.Type != "" {
builder = builder.Where(squirrel.Eq{"type": *req.Type})
}
if req.Status != nil {
builder = builder.Where(squirrel.Eq{"status": *req.Status})
}
list, total, err := l.svcCtx.AgentRewardsModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
if err != nil {
return nil, err

View File

@@ -25,7 +25,7 @@ func NewAdminGetSystemConfigLogic(ctx context.Context, svcCtx *svc.ServiceContex
func (l *AdminGetSystemConfigLogic) AdminGetSystemConfig() (resp *types.AdminGetSystemConfigResp, err error) {
resp = &types.AdminGetSystemConfigResp{
CommissionSafeMode: l.svcCtx.Config.SystemConfig.CommissionSafeMode,
CommissionSafeMode: l.svcCtx.AgentConfigModel.IsCommissionSafeMode(l.ctx),
}
return
}

View File

@@ -24,9 +24,11 @@ func NewAdminUpdateSystemConfigLogic(ctx context.Context, svcCtx *svc.ServiceCon
}
func (l *AdminUpdateSystemConfigLogic) AdminUpdateSystemConfig(req *types.AdminUpdateSystemConfigReq) (resp *types.AdminUpdateSystemConfigResp, err error) {
// 更新佣金安全防御模式配置
if req.CommissionSafeMode != nil {
l.svcCtx.Config.SystemConfig.CommissionSafeMode = *req.CommissionSafeMode
if err := l.svcCtx.AgentConfigModel.SetCommissionSafeMode(l.ctx, *req.CommissionSafeMode); err != nil {
logx.Errorf("更新佣金安全防御模式失败: %v", err)
return nil, err
}
logx.Infof("更新系统配置:佣金安全防御模式设置为 %v", *req.CommissionSafeMode)
}

View File

@@ -6,7 +6,6 @@ import (
"fmt"
"time"
paylogic "bdrp-server/app/main/api/internal/logic/pay"
"bdrp-server/app/main/api/internal/svc"
"bdrp-server/app/main/api/internal/types"
"bdrp-server/app/main/model"
@@ -93,8 +92,8 @@ func (l *AdminRefundOrderLogic) handleAlipayRefund(order *model.Order, req *type
}
// 退款成功后,按本次退款金额更新代理佣金状态并扣除钱包金额
// 注意refundAmount 为本次实际退款金额,可以是部分退款
_ = paylogic.HandleCommissionAndWalletDeduction(l.ctx, l.svcCtx, nil, order, req.RefundAmount)
// 使用 AgentService 中的共用退款扣款逻辑
l.svcCtx.AgentService.HandleOrderRefundDeduction(l.ctx, nil, order, req.RefundAmount)
return &types.AdminRefundOrderResp{
Status: model.OrderStatusRefunded,

View File

@@ -1,11 +1,13 @@
package agent
import (
"context"
"bdrp-server/app/main/api/internal/service"
"bdrp-server/app/main/model"
"bdrp-server/common/ctxdata"
"bdrp-server/common/xerr"
"bdrp-server/pkg/lzkit/lzUtils"
"context"
"time"
"github.com/jinzhu/copier"
"github.com/pkg/errors"
@@ -40,6 +42,9 @@ func (l *GetAgentMembershipProductConfigLogic) GetAgentMembershipProductConfig(r
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取会员用户报告配置,获取代理信息失败: %v", err)
}
if service.IsMembershipExpired(agentModel, time.Now()) {
return nil, errors.Wrapf(xerr.NewErrMsg("会员已过期"), "获取会员用户报告配置,会员已过期: agent_id=%d", agentModel.Id)
}
if agentModel.LevelName == "" {
agentModel.LevelName = model.AgentLeveNameNormal
}

View File

@@ -1,11 +1,13 @@
package agent
import (
"context"
"bdrp-server/app/main/api/internal/service"
"bdrp-server/app/main/model"
"bdrp-server/common/ctxdata"
"bdrp-server/common/xerr"
"context"
"math"
"time"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/mr"
@@ -104,6 +106,10 @@ func (l *GetAgentProductConfigLogic) GetAgentProductConfig() (resp *types.AgentP
cancel(findAncestorAgentErr)
return
}
if service.IsMembershipExpired(ancestorAgentModel, time.Now()) {
writer.Write(&agentProductConfig)
return
}
if ancestorAgentModel.LevelName == "" {
ancestorAgentModel.LevelName = model.AgentLeveNameNormal
}

View File

@@ -175,6 +175,11 @@ func calculateActiveReward(rewards []*model.AgentRewards) types.ActiveReward {
last30dStart := now.AddDate(0, 0, -30) // 近30天
for _, r := range rewards {
// 跳过已取消的奖励(退款取消)
if r.Status == 1 {
continue
}
createTime := r.CreateTime
amount := r.Amount
@@ -219,11 +224,12 @@ func addToPeriods(res *types.ActiveReward, amount float64, today, last7d, last30
// 分类添加具体字段
func addToData(data *types.ActiveRewardData, amount float64, t string) {
// 所有类型都累加到总奖励
data.NewActiveReward += amount
switch t {
case "withdraw":
data.SubWithdrawReward += amount
case "new_active":
data.NewActiveReward += amount
case "upgrade":
data.SubUpgradeReward += amount
case "promotion":

View File

@@ -1,10 +1,12 @@
package agent
import (
"context"
"bdrp-server/app/main/api/internal/service"
"bdrp-server/app/main/model"
"bdrp-server/common/ctxdata"
"bdrp-server/common/xerr"
"context"
"time"
"github.com/pkg/errors"
@@ -37,6 +39,9 @@ func (l *SaveAgentMembershipUserConfigLogic) SaveAgentMembershipUserConfig(req *
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "保存会员代理报告配置: %v", err)
}
if service.IsMembershipExpired(agentModel, time.Now()) {
return errors.Wrapf(xerr.NewErrMsg("会员已过期"), "保存会员代理报告配置,会员已过期: agent_id=%d", agentModel.Id)
}
var agentMembershipUserConfigModel *model.AgentMembershipUserConfig
agentMembershipUserConfigModel, err = l.svcCtx.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(l.ctx, agentModel.Id, req.ProductID)

View File

@@ -11,7 +11,6 @@ import (
"strings"
"time"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
"github.com/zeromicro/go-zero/core/logx"
@@ -22,153 +21,6 @@ func roundMoney(v float64) float64 {
return math.Round(v*100) / 100
}
// HandleCommissionAndWalletDeduction 处理退款后的佣金状态更新和钱包金额扣除
// refundAmount 为本次实际退款金额(单位:元),从代理侧总共需要承担的金额
// 该函数会优先冲减当前订单相关的佣金(基于 RefundedAmount不足部分再从钱包余额/冻结余额中扣除
func HandleCommissionAndWalletDeduction(ctx context.Context, svcCtx *svc.ServiceContext, session sqlx.Session, order *model.Order, refundAmount float64) error {
refundAmount = roundMoney(refundAmount)
if refundAmount <= 0 {
return nil
}
// 查询当前订单关联的所有佣金记录(包括已结算和冻结),剔除已经完全退款的
commissionBuilder := svcCtx.AgentCommissionModel.SelectBuilder()
commissions, commissionsErr := svcCtx.AgentCommissionModel.FindAll(ctx, commissionBuilder.Where(squirrel.And{
squirrel.Eq{"order_id": order.Id},
squirrel.NotEq{"status": 2}, // 排除已全部退款的佣金
}), "")
if commissionsErr != nil {
logx.Errorf("查询代理佣金失败订单ID: %d, 错误: %v", order.Id, commissionsErr)
return nil // 返回 nil因为佣金更新失败不应影响退款流程
}
if len(commissions) == 0 {
return nil
}
// 剩余需要由佣金 + 钱包共同承担的退款金额
remainRefundAmount := refundAmount
// 记录每个代理本次需要从钱包扣除的金额,避免同一代理多条佣金时重复查钱包并产生多条流水
type walletAdjust struct {
agentId int64
amount float64 // 需要从该代理钱包扣除的金额(正数)
}
walletAdjustMap := make(map[int64]*walletAdjust)
// 1. 先在佣金记录上做冲减:增加 RefundedAmount必要时将状态置为已退款
for _, commission := range commissions {
available := roundMoney(commission.Amount - commission.RefundedAmount)
if available <= 0 {
continue
}
if remainRefundAmount <= 0 {
break
}
// 当前这条佣金最多可冲减 available本次实际冲减 currentRefund
currentRefund := available
if currentRefund > remainRefundAmount {
currentRefund = remainRefundAmount
}
currentRefund = roundMoney(currentRefund)
// 更新佣金的已退款金额
commission.RefundedAmount = roundMoney(commission.RefundedAmount + currentRefund)
// 如果这条佣金已经被完全冲减,则标记为已退款
if commission.RefundedAmount >= roundMoney(commission.Amount) {
commission.Status = 2
}
// 更新佣金状态到数据库
var updateCommissionErr error
if session != nil {
updateCommissionErr = svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, session, commission)
} else {
updateCommissionErr = svcCtx.AgentCommissionModel.UpdateWithVersion(ctx, nil, commission)
}
if updateCommissionErr != nil {
logx.Errorf("更新代理佣金状态失败佣金ID: %d, 订单ID: %d, 错误: %v", commission.Id, order.Id, updateCommissionErr)
continue // 如果佣金状态更新失败,就不继续计入本次冲减
}
// 记录该代理需要从钱包扣除的金额(可能后续还有其他佣金叠加)
wa, ok := walletAdjustMap[commission.AgentId]
if !ok {
wa = &walletAdjust{agentId: commission.AgentId}
walletAdjustMap[commission.AgentId] = wa
}
wa.amount = roundMoney(wa.amount + currentRefund)
remainRefundAmount = roundMoney(remainRefundAmount - currentRefund)
}
// 2. 再按代理维度,从钱包(冻结余额/可用余额)中扣除对应金额
for _, wa := range walletAdjustMap {
if wa.amount <= 0 {
continue
}
// 处理用户钱包的金额扣除
wallet, err := svcCtx.AgentWalletModel.FindOneByAgentId(ctx, wa.agentId)
if err != nil {
logx.Errorf("查询代理钱包失败代理ID: %d, 错误: %v", wa.agentId, err)
continue
}
// 记录变动前的余额
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
// 优先从冻结余额中扣除(与原先“冻结佣金优先使用冻结余额”的设计一致)
deduct := roundMoney(wa.amount)
if wallet.FrozenBalance >= deduct {
wallet.FrozenBalance = roundMoney(wallet.FrozenBalance - deduct)
} else {
remaining := roundMoney(deduct - wallet.FrozenBalance)
wallet.FrozenBalance = 0
// 可用余额可以为负数,由业务承担风险
wallet.Balance = roundMoney(wallet.Balance - remaining)
}
// 变动后余额和冻结余额
balanceAfter := roundMoney(wallet.Balance)
frozenBalanceAfter := roundMoney(wallet.FrozenBalance)
// 更新钱包
var updateWalletErr error
if session != nil {
updateWalletErr = svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
} else {
updateWalletErr = svcCtx.AgentWalletModel.UpdateWithVersion(ctx, nil, wallet)
}
if updateWalletErr != nil {
logx.Errorf("更新代理钱包失败代理ID: %d, 错误: %v", wa.agentId, updateWalletErr)
continue
}
// 创建钱包交易流水记录(退款)
transErr := svcCtx.AgentService.CreateWalletTransaction(
ctx,
session,
wa.agentId,
model.WalletTransactionTypeRefund,
roundMoney(-wa.amount*-1), // 钱包流水金额为负数
balanceBefore,
balanceAfter,
frozenBalanceBefore,
frozenBalanceAfter,
order.OrderNo,
0, // 这里不强绑到某一条具体佣金记录,按订单维度记录
"订单退款,佣金已扣除",
)
if transErr != nil {
logx.Errorf("创建代理钱包流水记录失败代理ID: %d, 错误: %v", wa.agentId, transErr)
continue
}
}
return nil
}
type WechatPayRefundCallbackLogic struct {
logx.Logger
ctx context.Context
@@ -227,8 +79,8 @@ func (l *WechatPayRefundCallbackLogic) handleQueryOrderRefund(orderNo string, st
return errors.Wrapf(err, "更新查询订单状态失败: %s", orderNo)
}
// 退款成功时,按本次实际退款金额更新代理佣金状态并扣除钱包金额
_ = HandleCommissionAndWalletDeduction(ctx, l.svcCtx, session, order, refundAmountYuan)
// 退款成功时,按本次实际退款金额执行共用扣款流程
l.svcCtx.AgentService.HandleOrderRefundDeduction(ctx, session, order, refundAmountYuan)
}
// 查找最新的pending状态的退款记录

View File

@@ -0,0 +1,67 @@
package middleware
import (
"net/http"
"time"
"bdrp-server/app/main/api/internal/service"
"bdrp-server/app/main/model"
jwtx "bdrp-server/common/jwt"
"github.com/zeromicro/go-zero/core/logx"
)
const (
HeaderMembershipExpired = "X-Membership-Expired"
)
// MembershipExpiredInterceptor 检测代理会员是否过期,过期则写入响应头
// 依赖 ctx 中的 claims由 AuthInterceptorMiddleware 注入)和 svcCtx 中的 AgentModel
type MembershipExpiredInterceptor struct {
AgentModel model.AgentModel
}
func NewMembershipExpiredInterceptor(agentModel model.AgentModel) *MembershipExpiredInterceptor {
return &MembershipExpiredInterceptor{
AgentModel: agentModel,
}
}
func (m *MembershipExpiredInterceptor) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 先执行业务逻辑
next(w, r)
// 业务完成后,尝试检测会员过期状态
claims, err := getClaimsFromRequest(r)
if err != nil || claims == nil {
return
}
// 只检查正式用户
if claims.UserType != model.UserTypeNormal {
return
}
agent, err := m.AgentModel.FindOneByUserId(r.Context(), claims.UserId)
if err != nil {
return
}
if service.IsMembershipExpired(agent, time.Now()) {
w.Header().Set(HeaderMembershipExpired, "true")
logx.Infof("检测到代理会员已过期写入响应头代理ID: %d, 用户ID: %d", agent.Id, claims.UserId)
}
}
}
func getClaimsFromRequest(r *http.Request) (*jwtx.JwtClaims, error) {
value := r.Context().Value(jwtx.ExtraKey)
if value == nil {
return nil, nil
}
if claims, ok := value.(*jwtx.JwtClaims); ok {
return claims, nil
}
return nil, nil
}

View File

@@ -0,0 +1,49 @@
package queue
import (
"context"
"encoding/json"
"fmt"
"time"
"bdrp-server/app/main/api/internal/service"
"bdrp-server/app/main/api/internal/svc"
"bdrp-server/app/main/api/internal/types"
"github.com/hibiken/asynq"
"github.com/zeromicro/go-zero/core/logx"
)
type AgentMembershipExpireHandleHandler struct {
svcCtx *svc.ServiceContext
}
func NewAgentMembershipExpireHandleHandler(svcCtx *svc.ServiceContext) *AgentMembershipExpireHandleHandler {
return &AgentMembershipExpireHandleHandler{
svcCtx: svcCtx,
}
}
func (l *AgentMembershipExpireHandleHandler) ProcessTask(ctx context.Context, t *asynq.Task) error {
var payload types.MsgAgentMembershipExpireHandlePayload
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
logx.Errorf("解析会员到期处理任务payload失败: %v", err)
return err
}
if payload.AgentID <= 0 {
return fmt.Errorf("无效代理ID: %d", payload.AgentID)
}
now := time.Now()
downgraded, err := service.DowngradeExpiredMembership(ctx, l.svcCtx.AgentModel, payload.AgentID, now)
if err != nil {
logx.Errorf("会员到期处理失败代理ID: %d, 错误: %v", payload.AgentID, err)
return err
}
if downgraded {
logx.Infof("会员到期处理成功代理ID: %d 已降级为普通代理", payload.AgentID)
} else {
logx.Infof("会员到期处理跳过代理ID: %d 当前无需降级", payload.AgentID)
}
return nil
}

View File

@@ -0,0 +1,68 @@
package queue
import (
"context"
"time"
"bdrp-server/app/main/api/internal/service"
"bdrp-server/app/main/api/internal/svc"
"github.com/hibiken/asynq"
"github.com/zeromicro/go-zero/core/logx"
)
type AgentMembershipExpireScanHandler struct {
svcCtx *svc.ServiceContext
}
func NewAgentMembershipExpireScanHandler(svcCtx *svc.ServiceContext) *AgentMembershipExpireScanHandler {
return &AgentMembershipExpireScanHandler{
svcCtx: svcCtx,
}
}
func (l *AgentMembershipExpireScanHandler) ProcessTask(ctx context.Context, t *asynq.Task) error {
now := time.Now()
dayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
dayEnd := dayStart.Add(24*time.Hour - time.Second)
// 1. 扫描当天将到期会员,并为每个会员安排准点处理任务
todayAgents, err := service.ListTodayWillExpireAgents(ctx, l.svcCtx.AgentModel, dayStart, dayEnd)
if err != nil {
logx.Errorf("扫描当天到期会员失败: %v", err)
return err
}
for _, agent := range todayAgents {
processAt := agent.MembershipExpiryTime.Time
if processAt.Before(now) {
processAt = now
}
if l.svcCtx.AsynqService != nil {
if sendErr := l.svcCtx.AsynqService.SendAgentMembershipExpireHandleTask(agent.Id, processAt); sendErr != nil {
logx.Errorf("安排会员到期处理任务失败代理ID: %d, 错误: %v", agent.Id, sendErr)
}
}
}
// 2. 补偿处理:扫描已过期但尚未降级的会员并立即降级
expiredAgents, err := service.ListExpiredUnprocessedAgents(ctx, l.svcCtx.AgentModel, now)
if err != nil {
logx.Errorf("扫描已过期未处理会员失败: %v", err)
return err
}
downgradedCount := 0
for _, agent := range expiredAgents {
downgraded, degradeErr := service.DowngradeExpiredMembership(ctx, l.svcCtx.AgentModel, agent.Id, now)
if degradeErr != nil {
logx.Errorf("补偿降级失败代理ID: %d, 错误: %v", agent.Id, degradeErr)
continue
}
if downgraded {
downgradedCount++
}
}
logx.Infof("会员到期扫描完成,当天到期会员: %d, 补偿降级数量: %d", len(todayAgents), downgradedCount)
return nil
}

View File

@@ -10,7 +10,6 @@ import (
"path"
"regexp"
"strings"
paylogic "bdrp-server/app/main/api/internal/logic/pay"
"bdrp-server/app/main/api/internal/service"
"bdrp-server/app/main/api/internal/svc"
"bdrp-server/app/main/api/internal/types"
@@ -283,8 +282,8 @@ func (l *PaySuccessNotifyUserHandler) handleError(ctx context.Context, err error
return fmt.Errorf("更新订单状态失败: %v", updateOrderErr)
}
// 使用公共函数按本次退款金额处理佣金和钱包扣除
_ = paylogic.HandleCommissionAndWalletDeduction(ctx, l.svcCtx, nil, order, order.Amount)
// 使用 AgentService 中的共用退款扣款逻辑
l.svcCtx.AgentService.HandleOrderRefundDeduction(ctx, nil, order, order.Amount)
return asynq.SkipRetry
} else {

View File

@@ -1,12 +1,14 @@
package queue
import (
"context"
"fmt"
"bdrp-server/app/main/api/internal/svc"
"bdrp-server/app/main/api/internal/types"
"context"
"fmt"
"time"
"github.com/hibiken/asynq"
"github.com/zeromicro/go-zero/core/logx"
)
type CronJob struct {
@@ -14,6 +16,8 @@ type CronJob struct {
svcCtx *svc.ServiceContext
}
const AgentMembershipExpireScanTaskTime = "5 0 * * *"
func NewCronJob(ctx context.Context, svcCtx *svc.ServiceContext) *CronJob {
return &CronJob{
ctx: ctx,
@@ -31,6 +35,29 @@ func (l *CronJob) Register() *asynq.ServeMux {
if err != nil {
panic(fmt.Sprintf("定时任务注册失败:%v", err))
}
// 注册会员到期扫描任务(每天凌晨执行)
expireScanTask := asynq.NewTask(types.MsgAgentMembershipExpireScan, nil, nil)
_, err = scheduler.Register(AgentMembershipExpireScanTaskTime, expireScanTask)
if err != nil {
panic(fmt.Sprintf("会员到期扫描任务注册失败:%v", err))
}
// 启动补偿:服务启动后立即触发一次扫描任务(按日期去重)
client := asynq.NewClient(redisClientOpt)
startupTaskID := fmt.Sprintf("agent_membership_expire_scan_startup_%s", time.Now().Format("20060102"))
_, enqueueErr := client.Enqueue(
asynq.NewTask(types.MsgAgentMembershipExpireScan, nil),
asynq.MaxRetry(1),
asynq.TaskID(startupTaskID),
)
if enqueueErr != nil {
logx.Errorf("启动补偿扫描任务入队失败: %v", enqueueErr)
}
if closeErr := client.Close(); closeErr != nil {
logx.Errorf("关闭启动补偿任务客户端失败: %v", closeErr)
}
scheduler.Start()
fmt.Println("定时任务启动!!!")
@@ -38,6 +65,8 @@ func (l *CronJob) Register() *asynq.ServeMux {
mux.Handle(types.MsgPaySuccessQuery, NewPaySuccessNotifyUserHandler(l.svcCtx))
mux.Handle(types.MsgCleanQueryData, NewCleanQueryDataHandler(l.svcCtx))
mux.Handle(types.MsgUnfreezeCommission, NewUnfreezeCommissionHandler(l.svcCtx))
mux.Handle(types.MsgAgentMembershipExpireScan, NewAgentMembershipExpireScanHandler(l.svcCtx))
mux.Handle(types.MsgAgentMembershipExpireHandle, NewAgentMembershipExpireHandleHandler(l.svcCtx))
return mux
}

View File

@@ -0,0 +1,70 @@
package service
import (
"context"
"database/sql"
"time"
"bdrp-server/app/main/model"
"bdrp-server/common/globalkey"
)
// IsMembershipExpired 判断代理会员是否已过期仅VIP/SVIP会被判定
func IsMembershipExpired(agent *model.Agent, now time.Time) bool {
if agent == nil {
return false
}
if agent.LevelName != model.AgentLeveNameVIP && agent.LevelName != model.AgentLeveNameSVIP {
return false
}
if !agent.MembershipExpiryTime.Valid {
return true
}
return !agent.MembershipExpiryTime.Time.After(now)
}
// DowngradeExpiredMembership 将已过期会员降级为普通代理,返回是否发生了降级
func DowngradeExpiredMembership(ctx context.Context, agentModel model.AgentModel, agentID int64, now time.Time) (bool, error) {
agent, err := agentModel.FindOne(ctx, agentID)
if err != nil {
return false, err
}
if !IsMembershipExpired(agent, now) {
return false, nil
}
agent.LevelName = model.AgentLeveNameNormal
ClearMembershipOnDowngrade(agent)
agent.UpdateTime = now
if err = agentModel.UpdateWithVersion(ctx, nil, agent); err != nil {
return false, err
}
return true, nil
}
// ListTodayWillExpireAgents 查询当天将到期的VIP/SVIP代理
func ListTodayWillExpireAgents(ctx context.Context, agentModel model.AgentModel, start, end time.Time) ([]*model.Agent, error) {
builder := agentModel.SelectBuilder().
Where("level_name IN (?, ?)", model.AgentLeveNameVIP, model.AgentLeveNameSVIP).
Where("membership_expiry_time IS NOT NULL").
Where("membership_expiry_time >= ?", start).
Where("membership_expiry_time <= ?", end).
Where("del_state = ?", globalkey.DelStateNo)
return agentModel.FindAll(ctx, builder, "membership_expiry_time ASC")
}
// ListExpiredUnprocessedAgents 查询已过期但仍为VIP/SVIP的代理
func ListExpiredUnprocessedAgents(ctx context.Context, agentModel model.AgentModel, now time.Time) ([]*model.Agent, error) {
builder := agentModel.SelectBuilder().
Where("level_name IN (?, ?)", model.AgentLeveNameVIP, model.AgentLeveNameSVIP).
Where("membership_expiry_time IS NOT NULL").
Where("membership_expiry_time <= ?", now).
Where("del_state = ?", globalkey.DelStateNo)
return agentModel.FindAll(ctx, builder, "membership_expiry_time ASC")
}
// ClearMembershipOnDowngrade 降级时清理会员有效期,避免脏状态
func ClearMembershipOnDowngrade(agent *model.Agent) {
agent.MembershipExpiryTime = sql.NullTime{}
}

View File

@@ -1,14 +1,17 @@
package service
import (
"context"
"database/sql"
"fmt"
"bdrp-server/app/main/api/internal/config"
"bdrp-server/app/main/model"
"bdrp-server/common/globalkey"
"bdrp-server/pkg/lzkit/lzUtils"
"context"
"database/sql"
"fmt"
"math"
"time"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
@@ -34,6 +37,7 @@ type AgentService struct {
AgentActiveStatModel model.AgentActiveStatModel
AgentWithdrawalModel model.AgentWithdrawalModel
AgentWalletTransactionModel model.AgentWalletTransactionModel
AgentConfigModel model.AgentConfigModel
AsynqService *AsynqService
}
@@ -44,7 +48,7 @@ func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel mo
agentMembershipRechargeOrderModel model.AgentMembershipRechargeOrderModel,
agentMembershipUserConfigModel model.AgentMembershipUserConfigModel,
agentProductConfigModel model.AgentProductConfigModel, agentPlatformDeductionModel model.AgentPlatformDeductionModel,
agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel, agentWalletTransactionModel model.AgentWalletTransactionModel, asynqService *AsynqService) *AgentService {
agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel, agentWalletTransactionModel model.AgentWalletTransactionModel, agentConfigModel model.AgentConfigModel, asynqService *AsynqService) *AgentService {
return &AgentService{
config: c,
@@ -66,6 +70,7 @@ func NewAgentService(c config.Config, orderModel model.OrderModel, agentModel mo
AgentActiveStatModel: agentActiveStatModel,
AgentWithdrawalModel: agentWithdrawalModel,
AgentWalletTransactionModel: agentWalletTransactionModel,
AgentConfigModel: agentConfigModel,
AsynqService: asynqService,
}
}
@@ -113,6 +118,9 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
return findAgentModelErr
}
if AncestorModel != nil {
if IsMembershipExpired(AncestorModel, time.Now()) {
AncestorModel.LevelName = model.AgentLeveNameNormal
}
if AncestorModel.LevelName == "" {
AncestorModel.LevelName = model.AgentLeveNameNormal
}
@@ -155,7 +163,7 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
// 根据安全防御模式配置决定佣金处理方式
var commissionStatus int64
if l.config.SystemConfig.CommissionSafeMode {
if l.AgentConfigModel.IsCommissionSafeMode(ctx) {
// 安全防御模式佣金冻结在frozen_balance中
ancestorWallet.FrozenBalance += ancestorCommissionAmount
commissionStatus = 1 // 冻结状态
@@ -227,7 +235,7 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
// 在事务提交后,仅在安全防御模式下触发解冻任务
// 注意:这里发送的是任务,实际解冻将在指定时间后由队列处理
if l.AsynqService != nil && l.config.SystemConfig.CommissionSafeMode {
if l.AsynqService != nil && l.AgentConfigModel.IsCommissionSafeMode(ctx) {
// 仅在安全防御模式下,才需要发送解冻任务
// 获取刚创建的佣金记录ID
// 由于我们需要佣金记录ID来触发解冻任务但事务中无法获取我们可以在事务后查询
@@ -275,7 +283,7 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order
frozenBalanceBefore := agentWalletModel.FrozenBalance
// 根据安全防御模式配置决定佣金状态和钱包操作
if l.config.SystemConfig.CommissionSafeMode {
if l.AgentConfigModel.IsCommissionSafeMode(ctx) {
// 安全防御模式佣金冻结在frozen_balance中
agentWalletModel.FrozenBalance += finalCommission
} else {
@@ -286,7 +294,7 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order
// 根据安全防御模式配置决定佣金状态
commissionStatus := int64(1) // 默认为冻结状态
if !l.config.SystemConfig.CommissionSafeMode {
if !l.AgentConfigModel.IsCommissionSafeMode(ctx) {
commissionStatus = 0 // 非安全模式直接设置为已结算
}
@@ -343,6 +351,9 @@ func (l *AgentService) AncestorCommission(ctx context.Context, descendantId int6
if err != nil {
return 0, err
}
if IsMembershipExpired(agentModel, time.Now()) {
agentModel.LevelName = model.AgentLeveNameNormal
}
if agentModel.LevelName == "" {
agentModel.LevelName = model.AgentLeveNameNormal
}
@@ -415,6 +426,17 @@ func (l *AgentService) PlatformPricing(ctx context.Context, agentID int64, order
// CommissionCost 上级底价成本
func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, orderId int64, session sqlx.Session) (float64, error) {
ancestorModel, findAncestorErr := l.AgentModel.FindOne(ctx, AncestorId)
if findAncestorErr != nil {
if errors.Is(findAncestorErr, model.ErrNotFound) {
return 0, nil
}
return 0, findAncestorErr
}
if IsMembershipExpired(ancestorModel, time.Now()) {
return 0, nil
}
if agentMembershipConfigModel.PriceIncreaseAmount.Valid {
// 拥有则查看该上级设定的成本
agentMembershipUserConfigModel, findAgentMembershipUserConfigModelErr := l.AgentMembershipUserConfigModel.FindOneByAgentIdProductId(ctx, AncestorId, productID)
@@ -449,6 +471,17 @@ func (l *AgentService) CommissionCost(ctx context.Context, descendantId int64, A
// CommissionPricing 上级提价成本
func (l *AgentService) CommissionPricing(ctx context.Context, descendantId int64, AncestorId int64, agentMembershipConfigModel *model.AgentMembershipConfig, productID int64, pricing float64, orderId int64, session sqlx.Session) (float64, error) {
ancestorModel, findAncestorErr := l.AgentModel.FindOne(ctx, AncestorId)
if findAncestorErr != nil {
if errors.Is(findAncestorErr, model.ErrNotFound) {
return 0, nil
}
return 0, findAncestorErr
}
if IsMembershipExpired(ancestorModel, time.Now()) {
return 0, nil
}
//看上级代理等级否有拥有定价标准收益功能
if agentMembershipConfigModel.PriceIncreaseMax.Valid && agentMembershipConfigModel.PriceRatio.Valid {
// 拥有则查看该上级设定的成本
@@ -517,6 +550,9 @@ func (l *AgentService) GiveUpgradeReward(ctx context.Context, agentID int64, old
}
// 获取上级代理的等级配置
if IsMembershipExpired(ancestorModel, time.Now()) {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
if ancestorModel.LevelName == "" {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
@@ -661,6 +697,9 @@ func (l *AgentService) GiveWithdrawReward(ctx context.Context, agentID int64, wi
}
// 获取上级代理的等级配置
if IsMembershipExpired(ancestorModel, time.Now()) {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
if ancestorModel.LevelName == "" {
ancestorModel.LevelName = model.AgentLeveNameNormal
}
@@ -848,3 +887,211 @@ func (l *AgentService) CreateWalletTransaction(ctx context.Context, session sqlx
}
return nil
}
// HandleOrderRefundDeduction 处理订单退款后的佣金扣款流程
// refundAmount: 本次实际退款金额
// 扣款顺序:
// 1. 扣减推广代理佣金(先扣冻结,再扣余额)
// 2. 扣减上级抽佣
// 3. 取消上级推广奖励(无论退多少都取消)
// 4. 不足部分继续从推广代理钱包扣
// 平台抽佣不受影响
func (l *AgentService) HandleOrderRefundDeduction(ctx context.Context, session sqlx.Session, order *model.Order, refundAmount float64) {
refundAmount = roundRefundMoney(refundAmount)
if refundAmount <= 0 {
return
}
remainRefundAmount := refundAmount
// 查找订单关联的代理订单获取推广人代理ID
agentOrder, err := l.AgentOrderModel.FindOneByOrderId(ctx, order.Id)
if err != nil || agentOrder == nil {
logx.Errorf("退款扣款查询代理订单失败订单ID: %d, 错误: %v", order.Id, err)
return
}
promoterAgentId := agentOrder.AgentId
// 查找上级代理ID
var ancestorAgentId int64 = 0
agentClosure, closureErr := l.AgentClosureModel.FindOneByDescendantIdDepth(ctx, promoterAgentId, 1)
if closureErr == nil && agentClosure != nil {
ancestorAgentId = agentClosure.AncestorId
}
// 第1步扣减推广代理佣金
promoterCommissions, _ := l.AgentCommissionModel.FindAll(ctx,
l.AgentCommissionModel.SelectBuilder().Where(squirrel.And{
squirrel.Eq{"order_id": order.Id},
squirrel.Eq{"agent_id": promoterAgentId},
squirrel.NotEq{"status": 2},
}), "")
for _, commission := range promoterCommissions {
if remainRefundAmount <= 0 {
break
}
available := roundRefundMoney(commission.Amount - commission.RefundedAmount)
if available <= 0 {
continue
}
currentRefund := available
if currentRefund > remainRefundAmount {
currentRefund = remainRefundAmount
}
currentRefund = roundRefundMoney(currentRefund)
commission.RefundedAmount = roundRefundMoney(commission.RefundedAmount + currentRefund)
if commission.RefundedAmount >= roundRefundMoney(commission.Amount) {
commission.Status = 2
}
if err := l.AgentCommissionModel.UpdateWithVersion(ctx, session, commission); err != nil {
logx.Errorf("退款扣款更新推广佣金失败佣金ID: %d, 错误: %v", commission.Id, err)
continue
}
deductFromAgentWallet(ctx, l, session, promoterAgentId, currentRefund,
fmt.Sprintf("订单退款,推广佣金扣除,订单号: %s", order.OrderNo))
remainRefundAmount = roundRefundMoney(remainRefundAmount - currentRefund)
}
// 第2步扣减上级抽佣
if ancestorAgentId > 0 && remainRefundAmount > 0 {
deductions, _ := l.AgentCommissionDeductionModel.FindAll(ctx,
l.AgentCommissionDeductionModel.SelectBuilder().Where(map[string]interface{}{
"order_id": order.Id,
"agent_id": ancestorAgentId,
}), "")
for _, deduction := range deductions {
if deduction.Status == 2 {
continue
}
if remainRefundAmount <= 0 {
break
}
available := roundRefundMoney(deduction.Amount - deduction.RefundedAmount)
if available <= 0 {
continue
}
currentRefund := available
if currentRefund > remainRefundAmount {
currentRefund = remainRefundAmount
}
currentRefund = roundRefundMoney(currentRefund)
deduction.RefundedAmount = roundRefundMoney(deduction.RefundedAmount + currentRefund)
if deduction.RefundedAmount >= roundRefundMoney(deduction.Amount) {
deduction.Status = 2
}
if err := l.AgentCommissionDeductionModel.UpdateWithVersion(ctx, session, deduction); err != nil {
logx.Errorf("退款扣款更新上级抽佣失败ID: %d, 错误: %v", deduction.Id, err)
continue
}
deductFromAgentWallet(ctx, l, session, ancestorAgentId, currentRefund,
fmt.Sprintf("订单退款,上级抽佣扣除,订单号: %s", order.OrderNo))
remainRefundAmount = roundRefundMoney(remainRefundAmount - currentRefund)
}
}
// 第3步取消上级推广奖励无论退多少都取消
if ancestorAgentId > 0 {
rewards, _ := l.AgentRewardsModel.FindAll(ctx,
l.AgentRewardsModel.SelectBuilder().Where(map[string]interface{}{
"agent_id": ancestorAgentId,
"relation_agent_id": promoterAgentId,
"type": model.AgentRewardsTypeDescendantPromotion,
"status": 0,
}), "id DESC")
if len(rewards) > 0 {
reward := rewards[0]
rewardAmount := roundRefundMoney(reward.Amount)
reward.Status = 1
reward.Remark = fmt.Sprintf("订单退款取消奖励,订单号: %s", order.OrderNo)
if err := l.AgentRewardsModel.UpdateWithVersion(ctx, session, reward); err != nil {
logx.Errorf("退款扣款取消推广奖励失败奖励ID: %d, 错误: %v", reward.Id, err)
} else {
deductFromAgentWallet(ctx, l, session, ancestorAgentId, rewardAmount,
fmt.Sprintf("订单退款,取消推广奖励,订单号: %s", order.OrderNo))
}
}
}
// 第4步不足部分从推广代理钱包扣
if remainRefundAmount > 0 {
deductFromAgentWallet(ctx, l, session, promoterAgentId, remainRefundAmount,
fmt.Sprintf("订单退款,不足部分从钱包扣除,订单号: %s", order.OrderNo))
}
}
// roundRefundMoney 四舍五入到分
func roundRefundMoney(v float64) float64 {
return math.Round(v*100) / 100
}
// deductFromAgentWallet 从代理钱包扣除金额(先冻结后余额)
func deductFromAgentWallet(ctx context.Context, l *AgentService, session sqlx.Session, agentId int64, amount float64, remark string) {
amount = roundRefundMoney(amount)
if amount <= 0 {
return
}
wallet, err := l.AgentWalletModel.FindOneByAgentId(ctx, agentId)
if err != nil {
logx.Errorf("退款扣款查询代理钱包失败代理ID: %d, 错误: %v", agentId, err)
return
}
balanceBefore := wallet.Balance
frozenBalanceBefore := wallet.FrozenBalance
if wallet.FrozenBalance >= amount {
wallet.FrozenBalance = roundRefundMoney(wallet.FrozenBalance - amount)
} else {
remaining := roundRefundMoney(amount - wallet.FrozenBalance)
wallet.FrozenBalance = 0
wallet.Balance = roundRefundMoney(wallet.Balance - remaining)
}
balanceAfter := roundRefundMoney(wallet.Balance)
frozenBalanceAfter := roundRefundMoney(wallet.FrozenBalance)
var updateErr error
if session != nil {
updateErr = l.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
} else {
updateErr = l.AgentWalletModel.UpdateWithVersion(ctx, nil, wallet)
}
if updateErr != nil {
logx.Errorf("退款扣款更新代理钱包失败代理ID: %d, 错误: %v", agentId, updateErr)
return
}
transErr := l.CreateWalletTransaction(
ctx,
session,
agentId,
model.WalletTransactionTypeRefund,
roundRefundMoney(-amount),
balanceBefore,
balanceAfter,
frozenBalanceBefore,
frozenBalanceAfter,
"",
0,
remark,
)
if transErr != nil {
logx.Errorf("退款扣款创建钱包流水失败代理ID: %d, 错误: %v", agentId, transErr)
}
}

View File

@@ -4,6 +4,7 @@ package service
import (
"encoding/json"
"fmt"
"time"
"bdrp-server/app/main/api/internal/config"
@@ -90,3 +91,32 @@ func (s *AsynqService) SendUnfreezeCommissionTask(commissionID int64) error {
logx.Infof("发送佣金解冻任务成功任务ID: %s, 队列: %s, 佣金ID: %d", info.ID, info.Queue, commissionID)
return nil
}
// SendAgentMembershipExpireHandleTask 发送代理会员到期处理任务
func (s *AsynqService) SendAgentMembershipExpireHandleTask(agentID int64, processAt time.Time) error {
payload := types.MsgAgentMembershipExpireHandlePayload{
AgentID: agentID,
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
logx.Errorf("发送会员到期处理任务失败 (无法编码 payload): %v, 代理ID: %d", err, agentID)
return err
}
taskID := fmt.Sprintf("agent_membership_expire_handle_%d", agentID)
options := []asynq.Option{
asynq.ProcessAt(processAt),
asynq.MaxRetry(5),
asynq.TaskID(taskID),
}
task := asynq.NewTask(types.MsgAgentMembershipExpireHandle, payloadBytes, options...)
info, err := s.client.Enqueue(task)
if err != nil {
logx.Errorf("发送会员到期处理任务失败 (加入队列失败): %+v, 代理ID: %d, TaskID: %s", err, agentID, taskID)
return err
}
logx.Infof("发送会员到期处理任务成功任务ID: %s, 队列: %s, 代理ID: %d, 执行时间: %s", info.ID, info.Queue, agentID, processAt.Format("2006-01-02 15:04:05"))
return nil
}

View File

@@ -65,6 +65,7 @@ type ServiceContext struct {
AgentWithdrawalTaxModel model.AgentWithdrawalTaxModel
AgentWithdrawalTaxExemptionModel model.AgentWithdrawalTaxExemptionModel
AgentWalletTransactionModel model.AgentWalletTransactionModel
AgentConfigModel model.AgentConfigModel
// 管理后台相关模型
AdminApiModel model.AdminApiModel
@@ -155,6 +156,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
agentWithdrawalTaxModel := model.NewAgentWithdrawalTaxModel(db, cacheConf)
agentWithdrawalTaxExemptionModel := model.NewAgentWithdrawalTaxExemptionModel(db, cacheConf)
agentWalletTransactionModel := model.NewAgentWalletTransactionModel(db, cacheConf)
agentConfigModel := model.NewAgentConfigModel(db, cacheConf)
// ============================== 管理后台相关模型 ==============================
adminApiModel := model.NewAdminApiModel(db, cacheConf)
adminMenuModel := model.NewAdminMenuModel(db, cacheConf)
@@ -196,7 +198,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
agentCommissionModel, agentCommissionDeductionModel, agentWalletModel, agentLinkModel,
agentOrderModel, agentRewardsModel, agentMembershipConfigModel, agentMembershipRechargeOrderModel,
agentMembershipUserConfigModel, agentProductConfigModel, agentPlatformDeductionModel,
agentActiveStatModel, agentWithdrawalModel, agentWalletTransactionModel, asynqService)
agentActiveStatModel, agentWithdrawalModel, agentWalletTransactionModel, agentConfigModel, asynqService)
userService := service.NewUserService(&c, userModel, userAuthModel, userTempModel, agentModel)
dictService := service.NewDictService(adminDictTypeModel, adminDictDataModel)
adminPromotionLinkStatsService := service.NewAdminPromotionLinkStatsService(adminPromotionLinkModel,
@@ -265,6 +267,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
AgentWithdrawalTaxModel: agentWithdrawalTaxModel,
AgentWithdrawalTaxExemptionModel: agentWithdrawalTaxExemptionModel,
AgentWalletTransactionModel: agentWalletTransactionModel,
AgentConfigModel: agentConfigModel,
// 管理后台相关模型
AdminApiModel: adminApiModel,

View File

@@ -7,3 +7,7 @@ type MsgPaySuccessQueryPayload struct {
type MsgUnfreezeCommissionPayload struct {
CommissionID int64 `json:"commission_id"`
}
type MsgAgentMembershipExpireHandlePayload struct {
AgentID int64 `json:"agent_id"`
}

View File

@@ -3,3 +3,5 @@ package types
const MsgPaySuccessQuery = "msg:pay_success:query"
const MsgCleanQueryData = "msg:clean_query_data"
const MsgUnfreezeCommission = "msg:unfreeze_commission"
const MsgAgentMembershipExpireScan = "msg:agent_membership_expire_scan"
const MsgAgentMembershipExpireHandle = "msg:agent_membership_expire_handle"

View File

@@ -339,6 +339,7 @@ type AdminGetAgentRewardListReq struct {
AgentId *int64 `form:"agent_id,optional"` // 代理ID可选
RelationAgentId *int64 `form:"relation_agent_id,optional"` // 关联代理ID可选
Type *string `form:"type,optional"` // 奖励类型(可选)
Status *int64 `form:"status,optional"` // 状态(可选)
}
type AdminGetAgentRewardListResp struct {
@@ -1080,6 +1081,7 @@ type AgentCommissionDeductionListItem struct {
AgentId int64 `json:"agent_id"` // 代理ID
DeductedAgentId int64 `json:"deducted_agent_id"` // 被扣代理ID
Amount float64 `json:"amount"` // 金额
RefundedAmount float64 `json:"refunded_amount"` // 已退款金额
ProductName string `json:"product_name"` // 产品名
Type string `json:"type"` // 类型cost/pricing
Status int64 `json:"status"` // 状态
@@ -1255,6 +1257,8 @@ type AgentRewardListItem struct {
RelationAgentId int64 `json:"relation_agent_id"` // 关联代理ID
Amount float64 `json:"amount"` // 金额
Type string `json:"type"` // 奖励类型
Status int64 `json:"status"` // 状态0=正常, 1=已取消
Remark string `json:"remark"` // 备注
CreateTime string `json:"create_time"` // 创建时间
}

View File

@@ -1,16 +1,16 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"bdrp-server/app/main/api/internal/config"
"bdrp-server/app/main/api/internal/handler"
"bdrp-server/app/main/api/internal/middleware"
"bdrp-server/app/main/api/internal/queue"
"bdrp-server/app/main/api/internal/service"
"bdrp-server/app/main/api/internal/svc"
"context"
"flag"
"fmt"
"os"
"github.com/zeromicro/go-zero/core/logx"
@@ -58,6 +58,7 @@ func main() {
server := rest.MustNewServer(c.RestConf)
server.Use(middleware.GlobalSourceInterceptor)
server.Use(middleware.NewMembershipExpiredInterceptor(svcContext.AgentModel).Handle)
defer server.Stop()
handler.RegisterHandlers(server, svcContext)