后台面板

This commit is contained in:
2025-06-09 12:34:52 +08:00
parent 89bade9232
commit 19c82817ba
375 changed files with 20628 additions and 11903 deletions

View File

@@ -0,0 +1,210 @@
package service
import (
"context"
"database/sql"
"qnc-server/app/main/model"
"qnc-server/common/xerr"
"time"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type AdminPromotionLinkStatsService struct {
logx.Logger
AdminPromotionLinkModel model.AdminPromotionLinkModel
AdminPromotionLinkStatsTotalModel model.AdminPromotionLinkStatsTotalModel
AdminPromotionLinkStatsHistoryModel model.AdminPromotionLinkStatsHistoryModel
}
func NewAdminPromotionLinkStatsService(
AdminPromotionLinkModel model.AdminPromotionLinkModel,
AdminPromotionLinkStatsTotalModel model.AdminPromotionLinkStatsTotalModel,
AdminPromotionLinkStatsHistoryModel model.AdminPromotionLinkStatsHistoryModel,
) *AdminPromotionLinkStatsService {
return &AdminPromotionLinkStatsService{
Logger: logx.WithContext(context.Background()),
AdminPromotionLinkModel: AdminPromotionLinkModel,
AdminPromotionLinkStatsTotalModel: AdminPromotionLinkStatsTotalModel,
AdminPromotionLinkStatsHistoryModel: AdminPromotionLinkStatsHistoryModel,
}
}
// ensureTotalStats 确保总统计记录存在,如果不存在则创建
func (s *AdminPromotionLinkStatsService) ensureTotalStats(ctx context.Context, session sqlx.Session, linkId int64) (*model.AdminPromotionLinkStatsTotal, error) {
totalStats, err := s.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(ctx, linkId)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 如果记录不存在,创建新记录
totalStats = &model.AdminPromotionLinkStatsTotal{
LinkId: linkId,
ClickCount: 0,
PayCount: 0,
PayAmount: 0,
}
_, err = s.AdminPromotionLinkStatsTotalModel.Insert(ctx, session, totalStats)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建总统计记录失败: %+v", err)
}
// 重新获取创建后的记录
totalStats, err = s.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(ctx, linkId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取新创建的总统计记录失败: %+v", err)
}
} else {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询总统计失败: %+v", err)
}
}
return totalStats, nil
}
// ensureHistoryStats 确保历史统计记录存在,如果不存在则创建
func (s *AdminPromotionLinkStatsService) ensureHistoryStats(ctx context.Context, session sqlx.Session, linkId int64, today time.Time) (*model.AdminPromotionLinkStatsHistory, error) {
historyStats, err := s.AdminPromotionLinkStatsHistoryModel.FindOneByLinkIdStatsDate(ctx, linkId, today)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
// 如果记录不存在,创建新记录
historyStats = &model.AdminPromotionLinkStatsHistory{
LinkId: linkId,
StatsDate: today,
ClickCount: 0,
PayCount: 0,
PayAmount: 0,
}
_, err = s.AdminPromotionLinkStatsHistoryModel.Insert(ctx, session, historyStats)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建今日统计记录失败: %+v", err)
}
// 重新获取创建后的记录
historyStats, err = s.AdminPromotionLinkStatsHistoryModel.FindOneByLinkIdStatsDate(ctx, linkId, today)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取新创建的今日统计记录失败: %+v", err)
}
} else {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询今日统计记录失败: %+v", err)
}
}
return historyStats, nil
}
// UpdateLinkStats 更新推广链接统计
func (s *AdminPromotionLinkStatsService) UpdateLinkStats(ctx context.Context, linkId int64) error {
return s.AdminPromotionLinkStatsTotalModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
// 确保总统计记录存在
totalStats, err := s.ensureTotalStats(ctx, session, linkId)
if err != nil {
return err
}
// 更新总统计
totalStats.ClickCount++
totalStats.LastClickTime = sql.NullTime{Time: time.Now(), Valid: true}
err = s.AdminPromotionLinkStatsTotalModel.UpdateWithVersion(ctx, session, totalStats)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新总统计失败: %+v", err)
}
// 确保历史统计记录存在
now := time.Now()
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
historyStats, err := s.ensureHistoryStats(ctx, session, linkId, today)
if err != nil {
return err
}
// 更新历史统计
historyStats.ClickCount++
historyStats.LastClickTime = sql.NullTime{Time: time.Now(), Valid: true}
err = s.AdminPromotionLinkStatsHistoryModel.UpdateWithVersion(ctx, session, historyStats)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新历史统计失败: %+v", err)
}
return nil
})
}
// UpdatePaymentStats 更新付费统计
func (s *AdminPromotionLinkStatsService) UpdatePaymentStats(ctx context.Context, linkId int64, amount float64) error {
return s.AdminPromotionLinkStatsTotalModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
// 确保总统计记录存在
totalStats, err := s.ensureTotalStats(ctx, session, linkId)
if err != nil {
return err
}
// 更新总统计
totalStats.PayCount++
totalStats.PayAmount += amount
totalStats.LastPayTime = sql.NullTime{Time: time.Now(), Valid: true}
err = s.AdminPromotionLinkStatsTotalModel.UpdateWithVersion(ctx, session, totalStats)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新总统计失败: %+v", err)
}
// 确保历史统计记录存在
now := time.Now()
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
historyStats, err := s.ensureHistoryStats(ctx, session, linkId, today)
if err != nil {
return err
}
// 更新历史统计
historyStats.PayCount++
historyStats.PayAmount += amount
historyStats.LastPayTime = sql.NullTime{Time: time.Now(), Valid: true}
err = s.AdminPromotionLinkStatsHistoryModel.UpdateWithVersion(ctx, session, historyStats)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新历史统计失败: %+v", err)
}
return nil
})
}
// CreateLinkStats 创建新的推广链接统计记录
func (s *AdminPromotionLinkStatsService) CreateLinkStats(ctx context.Context, linkId int64) error {
return s.AdminPromotionLinkStatsTotalModel.Trans(ctx, func(ctx context.Context, session sqlx.Session) error {
// 检查总统计记录是否已存在
_, err := s.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(ctx, linkId)
if err == nil {
// 记录已存在,不需要创建
return nil
}
if err != model.ErrNotFound {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询总统计记录失败: %+v", err)
}
// 创建总统计记录
totalStats := &model.AdminPromotionLinkStatsTotal{
LinkId: linkId,
ClickCount: 0,
PayCount: 0,
PayAmount: 0,
}
_, err = s.AdminPromotionLinkStatsTotalModel.Insert(ctx, session, totalStats)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建总统计记录失败: %+v", err)
}
// 创建今日历史统计记录
now := time.Now()
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
historyStats := &model.AdminPromotionLinkStatsHistory{
LinkId: linkId,
StatsDate: today,
ClickCount: 0,
PayCount: 0,
PayAmount: 0,
}
_, err = s.AdminPromotionLinkStatsHistoryModel.Insert(ctx, session, historyStats)
if err != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建历史统计记录失败: %+v", err)
}
return nil
})
}

View File

@@ -2,11 +2,13 @@ package service
import (
"context"
"qnc-server/app/user/cmd/api/internal/config"
"qnc-server/app/user/model"
"qnc-server/app/main/api/internal/config"
"qnc-server/app/main/api/internal/types"
"qnc-server/app/main/model"
"qnc-server/pkg/lzkit/lzUtils"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
@@ -28,6 +30,7 @@ type AgentService struct {
AgentPlatformDeductionModel model.AgentPlatformDeductionModel
AgentActiveStatModel model.AgentActiveStatModel
AgentWithdrawalModel model.AgentWithdrawalModel
AsynqService *AsynqService
}
func NewAgentService(c config.Config, agentModel model.AgentModel, agentAuditModel model.AgentAuditModel,
@@ -37,7 +40,7 @@ func NewAgentService(c config.Config, agentModel model.AgentModel, agentAuditMod
agentMembershipRechargeOrderModel model.AgentMembershipRechargeOrderModel,
agentMembershipUserConfigModel model.AgentMembershipUserConfigModel,
agentProductConfigModel model.AgentProductConfigModel, agentPlatformDeductionModel model.AgentPlatformDeductionModel,
agentActiveStatModel model.AgentActiveStatModel, agentWithdrawalModel model.AgentWithdrawalModel) *AgentService {
agentActiveStatModel model.AgentActiveStatModel, asynqService *AsynqService, agentWithdrawalModel model.AgentWithdrawalModel) *AgentService {
return &AgentService{
config: c,
@@ -57,6 +60,7 @@ func NewAgentService(c config.Config, agentModel model.AgentModel, agentAuditMod
AgentPlatformDeductionModel: agentPlatformDeductionModel,
AgentActiveStatModel: agentActiveStatModel,
AgentWithdrawalModel: agentWithdrawalModel,
AsynqService: asynqService,
}
}
@@ -134,12 +138,18 @@ func (l *AgentService) AgentProcess(ctx context.Context, order *model.Order) err
return findAgentWalletModelErr
}
ancestorWallet.Balance += ancestorCommissionAmount
ancestorWallet.FrozenBalance += ancestorCommissionAmount
ancestorWallet.TotalEarnings += ancestorCommissionAmount
updateErr := l.AgentWalletModel.UpdateWithVersion(transCtx, session, ancestorWallet)
if updateErr != nil {
return updateErr
}
// 发送解冻任务
err := l.AsynqService.SendUnfreezeAgentBalanceTask(AncestorId, order.Id, ancestorCommissionAmount)
if err != nil {
logx.Errorf("创建解冻任务失败: %v, 代理ID: %d, 订单号: %d", err, AncestorId, order.Id)
return err
}
}
}
@@ -167,7 +177,9 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order
}
// 推广人最终获得代理佣金
finalCommission := order.Amount - deductedAmount
agentWalletModel.Balance += finalCommission
// 更新冻结余额而不是直接更新余额
agentWalletModel.FrozenBalance += finalCommission
agentWalletModel.TotalEarnings += finalCommission
agentCommission := model.AgentCommission{
@@ -185,6 +197,19 @@ func (l *AgentService) AgentCommission(ctx context.Context, agentID int64, order
if updateAgentWalletErr != nil {
return updateAgentWalletErr
}
// 创建24小时后解冻任务
unfreezePayload := types.UnfreezeAgentBalancePayload{
AgentID: agentID,
OrderID: order.Id,
Amount: finalCommission,
}
// 发送解冻任务
err := l.AsynqService.SendUnfreezeAgentBalanceTask(unfreezePayload.AgentID, unfreezePayload.OrderID, unfreezePayload.Amount)
if err != nil {
logx.Errorf("创建解冻任务失败: %v, 代理ID: %d, 订单号: %d", err, agentID, order.Id)
return err
}
return nil
}

View File

@@ -6,7 +6,7 @@ import (
"encoding/hex"
"fmt"
"net/http"
"qnc-server/app/user/cmd/api/internal/config"
"qnc-server/app/main/api/internal/config"
"qnc-server/pkg/lzkit/lzUtils"
"strconv"
"sync/atomic"

View File

@@ -8,8 +8,8 @@ import (
"io"
"net/http"
"net/url"
"qnc-server/app/user/cmd/api/internal/config"
"qnc-server/app/user/model"
"qnc-server/app/main/api/internal/config"
"qnc-server/app/main/model"
"qnc-server/pkg/lzkit/crypto"
"qnc-server/pkg/lzkit/lzUtils"
"sort"

View File

@@ -9,7 +9,7 @@ import (
"fmt"
"io/ioutil"
"net/http"
"qnc-server/app/user/cmd/api/internal/config"
"qnc-server/app/main/api/internal/config"
"strconv"
"time"

View File

@@ -4,8 +4,9 @@ package service
import (
"encoding/json"
"qnc-server/app/user/cmd/api/internal/config"
"qnc-server/app/user/cmd/api/internal/types"
"qnc-server/app/main/api/internal/config"
"qnc-server/app/main/api/internal/types"
"time"
"github.com/hibiken/asynq"
"github.com/zeromicro/go-zero/core/logx"
@@ -58,3 +59,43 @@ func (s *AsynqService) SendQueryTask(orderID int64) error {
logx.Infof("发送异步任务成功任务ID: %s, 队列: %s, 订单号: %d", info.ID, info.Queue, orderID)
return nil
}
// SendUnfreezeAgentBalanceTask 发送解冻代理金额的延迟任务
func (s *AsynqService) SendUnfreezeAgentBalanceTask(agentID, orderID int64, amount float64) error {
// 准备任务的 payload
payload := types.UnfreezeAgentBalancePayload{
AgentID: agentID,
OrderID: orderID,
Amount: amount,
FreezeTime: time.Now().Unix(),
}
payloadBytes, err := json.Marshal(payload)
if err != nil {
logx.Errorf("发送解冻代理金额任务失败 (无法编码 payload): %v, 代理ID: %d, 订单号: %d", err, agentID, orderID)
return err
}
// 设置任务选项
options := []asynq.Option{
asynq.MaxRetry(3), // 最大重试3次
asynq.ProcessIn(24 * time.Hour), // 24小时后执行
asynq.Timeout(5 * time.Minute), // 任务超时时间
asynq.Retention(24 * time.Hour), // 任务结果保留时间
asynq.Queue("agent_balance"), // 使用专门的队列
}
// 创建任务
task := asynq.NewTask(types.MsgDelayedTask, payloadBytes, options...)
// 将任务加入队列
info, err := s.client.Enqueue(task)
if err != nil {
logx.Errorf("发送解冻代理金额任务失败 (加入队列失败): %v, 代理ID: %d, 订单号: %d", err, agentID, orderID)
return err
}
logx.Infof("发送解冻代理金额任务成功任务ID: %s, 队列: %s, 代理ID: %d, 订单号: %d, 金额: %.2f",
info.ID, info.Queue, agentID, orderID, amount)
return nil
}

View File

@@ -0,0 +1,47 @@
package service
import (
"context"
"errors"
"qnc-server/app/main/model"
)
type DictService struct {
adminDictTypeModel model.AdminDictTypeModel
adminDictDataModel model.AdminDictDataModel
}
func NewDictService(adminDictTypeModel model.AdminDictTypeModel, adminDictDataModel model.AdminDictDataModel) *DictService {
return &DictService{adminDictTypeModel: adminDictTypeModel, adminDictDataModel: adminDictDataModel}
}
func (s *DictService) GetDictLabel(ctx context.Context, dictType string, dictValue int64) (string, error) {
dictTypeModel, err := s.adminDictTypeModel.FindOneByDictType(ctx, dictType)
if err != nil {
return "", err
}
if dictTypeModel.Status != 1 {
return "", errors.New("字典类型未启用")
}
dictData, err := s.adminDictDataModel.FindOneByDictTypeDictValue(ctx, dictTypeModel.DictType, dictValue)
if err != nil {
return "", err
}
if dictData.Status != 1 {
return "", errors.New("字典数据未启用")
}
return dictData.DictLabel, nil
}
func (s *DictService) GetDictValue(ctx context.Context, dictType string, dictLabel string) (int64, error) {
dictTypeModel, err := s.adminDictTypeModel.FindOneByDictType(ctx, dictType)
if err != nil {
return 0, err
}
if dictTypeModel.Status != 1 {
return 0, errors.New("字典类型未启用")
}
dictData, err := s.adminDictDataModel.FindOneByDictTypeDictLabel(ctx, dictTypeModel.DictType, dictLabel)
if err != nil {
return 0, err
}
return dictData.DictValue, nil
}

View File

@@ -2,7 +2,7 @@ package service
import (
"context"
"qnc-server/app/user/model"
"qnc-server/app/main/model"
"github.com/google/uuid"
"github.com/zeromicro/go-zero/core/stores/sqlx"

View File

@@ -6,7 +6,7 @@ import (
"io"
"net/http"
"net/url"
"qnc-server/app/user/cmd/api/internal/config"
"qnc-server/app/main/api/internal/config"
"qnc-server/pkg/lzkit/crypto"
"strings"

View File

@@ -4,8 +4,8 @@ import (
"context"
"fmt"
"net/http"
"qnc-server/app/user/cmd/api/internal/config"
"qnc-server/app/user/model"
"qnc-server/app/main/api/internal/config"
"qnc-server/app/main/model"
"qnc-server/common/ctxdata"
"qnc-server/pkg/lzkit/lzUtils"
"strconv"

View File

@@ -6,7 +6,7 @@ import (
"fmt"
"io"
"net/http"
"qnc-server/app/user/cmd/api/internal/config"
"qnc-server/app/main/api/internal/config"
"qnc-server/pkg/lzkit/crypto"
"strconv"
"time"

View File

@@ -11,7 +11,7 @@ import (
"fmt"
"io"
"net/http"
"qnc-server/app/user/cmd/api/internal/config"
"qnc-server/app/main/api/internal/config"
"strings"
"time"