first commit

This commit is contained in:
2026-02-08 16:19:37 +08:00
commit 958df98745
569 changed files with 61311 additions and 0 deletions

View File

@@ -0,0 +1,252 @@
package admin_agent
import (
"context"
"sort"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentRankingLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentRankingLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentRankingLogic {
return &AdminGetAgentRankingLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentRankingLogic) AdminGetAgentRanking(req *types.AdminGetAgentRankingReq) (resp *types.AdminGetAgentRankingResp, err error) {
// Set default limit if not provided
limit := req.Limit
if limit <= 0 {
limit = 10
}
// Validate type parameter
if req.Type != "commission" && req.Type != "orders" {
return nil, errors.New("invalid type parameter, must be 'commission' or 'orders'")
}
var items []types.AgentRankingItem
if req.Type == "commission" {
// Rank by sum of commission amounts
items, err = l.queryCommissionRanking(int(limit))
if err != nil {
return nil, err
}
} else {
// Rank by count of orders
items, err = l.queryOrdersRanking(int(limit))
if err != nil {
return nil, err
}
}
resp = &types.AdminGetAgentRankingResp{
Items: items,
}
return resp, nil
}
// queryCommissionRanking queries top agents by total commission amount
func (l *AdminGetAgentRankingLogic) queryCommissionRanking(limit int) ([]types.AgentRankingItem, error) {
// 1. Query all commissions
builder := l.svcCtx.AgentCommissionModel.SelectBuilder()
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, err
}
// 2. Group by agent_id and sum amounts
agentCommissionMap := make(map[string]float64)
for _, commission := range commissions {
agentCommissionMap[commission.AgentId] += commission.Amount
}
// 3. Build agent stats
type AgentStat struct {
AgentID string
Amount float64
}
stats := make([]AgentStat, 0, len(agentCommissionMap))
for agentID, amount := range agentCommissionMap {
stats = append(stats, AgentStat{
AgentID: agentID,
Amount: amount,
})
}
// 4. Sort by amount descending
sort.Slice(stats, func(i, j int) bool {
return stats[i].Amount > stats[j].Amount
})
// 5. Apply limit
if limit > len(stats) {
limit = len(stats)
}
stats = stats[:limit]
// 6. Query agents and build response
agentIDs := make([]string, len(stats))
for i, stat := range stats {
agentIDs[i] = stat.AgentID
}
agents, err := l.svcCtx.AgentModel.FindAll(l.ctx,
l.svcCtx.AgentModel.SelectBuilder(), "")
if err != nil {
return nil, err
}
// Build agent map
agentMap := make(map[string]*struct {
Mobile string
Region string
})
for _, agent := range agents {
region := "未知"
if agent.Region.Valid && agent.Region.String != "" {
region = agent.Region.String
}
agentMap[agent.Id] = &struct {
Mobile string
Region string
}{
Mobile: agent.Mobile,
Region: region,
}
}
// 7. Build items
items := make([]types.AgentRankingItem, len(stats))
for i, stat := range stats {
agentInfo := agentMap[stat.AgentID]
// Decrypt mobile
decryptedMobile, err := crypto.DecryptMobile(agentInfo.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
if err != nil {
l.Errorf("Failed to decrypt mobile for agent %s: %v", stat.AgentID, err)
decryptedMobile = ""
}
items[i] = types.AgentRankingItem{
AgentId: stat.AgentID,
AgentMobile: decryptedMobile,
Region: agentInfo.Region,
Value: stat.Amount,
}
}
return items, nil
}
// queryOrdersRanking queries top agents by order count
func (l *AdminGetAgentRankingLogic) queryOrdersRanking(limit int) ([]types.AgentRankingItem, error) {
// 1. Query all orders
builder := l.svcCtx.AgentOrderModel.SelectBuilder()
orders, err := l.svcCtx.AgentOrderModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, err
}
// 2. Group by agent_id and count
agentOrderCountMap := make(map[string]int64)
for _, order := range orders {
agentOrderCountMap[order.AgentId]++
}
// 3. Build agent stats
type AgentStat struct {
AgentID string
Count int64
}
stats := make([]AgentStat, 0, len(agentOrderCountMap))
for agentID, count := range agentOrderCountMap {
stats = append(stats, AgentStat{
AgentID: agentID,
Count: count,
})
}
// 4. Sort by count descending
sort.Slice(stats, func(i, j int) bool {
return stats[i].Count > stats[j].Count
})
// 5. Apply limit
if limit > len(stats) {
limit = len(stats)
}
stats = stats[:limit]
// 6. Query agents
agentIDs := make([]string, len(stats))
for i, stat := range stats {
agentIDs[i] = stat.AgentID
}
agents, err := l.svcCtx.AgentModel.FindAll(l.ctx,
l.svcCtx.AgentModel.SelectBuilder(), "")
if err != nil {
return nil, err
}
// Build agent map
agentMap := make(map[string]*struct {
Mobile string
Region string
})
for _, agent := range agents {
region := "未知"
if agent.Region.Valid && agent.Region.String != "" {
region = agent.Region.String
}
agentMap[agent.Id] = &struct {
Mobile string
Region string
}{
Mobile: agent.Mobile,
Region: region,
}
}
// 7. Build items
items := make([]types.AgentRankingItem, len(stats))
for i, stat := range stats {
agentInfo := agentMap[stat.AgentID]
// Decrypt mobile
decryptedMobile, err := crypto.DecryptMobile(agentInfo.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
if err != nil {
l.Errorf("Failed to decrypt mobile for agent %s: %v", stat.AgentID, err)
decryptedMobile = ""
}
items[i] = types.AgentRankingItem{
AgentId: stat.AgentID,
AgentMobile: decryptedMobile,
Region: agentInfo.Region,
Value: float64(stat.Count),
}
}
return items, nil
}

View File

@@ -0,0 +1,99 @@
package admin_agent
import (
"context"
"fmt"
"sort"
"time"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/common/globalkey"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentTrendsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentTrendsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentTrendsLogic {
return &AdminGetAgentTrendsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentTrendsLogic) AdminGetAgentTrends(req *types.AdminGetAgentTrendsReq) (resp *types.AdminGetAgentTrendsResp, err error) {
// 1. Parse date parameters with default to last 30 days
var startDate, endDate time.Time
if req.StartDate != "" {
startDate, err = time.Parse("2006-01-02", req.StartDate)
if err != nil {
return nil, fmt.Errorf("invalid start_date format: %v", err)
}
} else {
// Default to 30 days ago
startDate = time.Now().AddDate(0, 0, -30)
}
if req.EndDate != "" {
endDate, err = time.Parse("2006-01-02", req.EndDate)
if err != nil {
return nil, fmt.Errorf("invalid end_date format: %v", err)
}
// Set end date to end of day
endDate = time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 23, 59, 59, 0, time.Local)
} else {
// Default to today
endDate = time.Now()
}
// Ensure start date is before end date
if startDate.After(endDate) {
return nil, fmt.Errorf("start_date must be before end_date")
}
// 2. Query all agents in date range
builder := l.svcCtx.AgentModel.SelectBuilder()
builder = builder.Where("del_state = ?", globalkey.DelStateNo)
builder = builder.Where("create_time >= ?", startDate)
builder = builder.Where("create_time <= ?", endDate)
agents, err := l.svcCtx.AgentModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, fmt.Errorf("failed to query agents: %v", err)
}
// 3. Group by date in Go
dateMap := make(map[string]int64)
for _, agent := range agents {
date := agent.CreateTime.Format("2006-01-02")
dateMap[date]++
}
// 4. Build sorted response
dates := make([]string, 0, len(dateMap))
counts := make([]int64, 0, len(dateMap))
for date := range dateMap {
dates = append(dates, date)
}
// Sort dates
sort.Strings(dates)
for _, date := range dates {
counts = append(counts, dateMap[date])
}
return &types.AdminGetAgentTrendsResp{
Dates: dates,
Counts: counts,
}, nil
}

View File

@@ -0,0 +1,120 @@
package admin_agent
import (
"context"
"fmt"
"time"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/common/globalkey"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetOrderTrendsLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetOrderTrendsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetOrderTrendsLogic {
return &AdminGetOrderTrendsLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetOrderTrendsLogic) AdminGetOrderTrends(req *types.AdminGetOrderTrendsReq) (resp *types.AdminGetOrderTrendsResp, err error) {
// 1. Parse date parameters with default to last 30 days
var startDate, endDate time.Time
if req.StartDate != "" {
startDate, err = time.Parse("2006-01-02", req.StartDate)
if err != nil {
return nil, fmt.Errorf("invalid start_date format: %v", err)
}
} else {
// Default to 30 days ago
startDate = time.Now().AddDate(0, 0, -30)
}
if req.EndDate != "" {
endDate, err = time.Parse("2006-01-02", req.EndDate)
if err != nil {
return nil, fmt.Errorf("invalid end_date format: %v", err)
}
// Set end date to end of day
endDate = time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 23, 59, 59, 0, time.Local)
} else {
// Default to today
endDate = time.Now()
}
// Ensure start date is before end date
if startDate.After(endDate) {
return nil, fmt.Errorf("start_date must be before end_date")
}
// 2. Query all commissions in date range
builder := l.svcCtx.AgentCommissionModel.SelectBuilder()
builder = builder.Where("del_state = ?", globalkey.DelStateNo)
builder = builder.Where("create_time >= ?", startDate)
builder = builder.Where("create_time <= ?", endDate)
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, fmt.Errorf("failed to query commissions: %v", err)
}
// 3. Group by date in Go
dateMap := make(map[string]*struct {
Amount float64
Count int64
})
for _, commission := range commissions {
date := commission.CreateTime.Format("2006-01-02")
if _, exists := dateMap[date]; !exists {
dateMap[date] = &struct {
Amount float64
Count int64
}{}
}
dateMap[date].Amount += commission.Amount
dateMap[date].Count++
}
// 4. Build sorted response
dates := make([]string, 0, len(dateMap))
amounts := make([]float64, 0, len(dateMap))
counts := make([]int64, 0, len(dateMap))
// Sort dates
sortedDates := make([]string, 0, len(dateMap))
for date := range dateMap {
sortedDates = append(sortedDates, date)
}
// Simple bubble sort (can be optimized with sort package)
for i := 0; i < len(sortedDates); i++ {
for j := i + 1; j < len(sortedDates); j++ {
if sortedDates[i] > sortedDates[j] {
sortedDates[i], sortedDates[j] = sortedDates[j], sortedDates[i]
}
}
}
for _, date := range sortedDates {
dates = append(dates, date)
amounts = append(amounts, dateMap[date].Amount)
counts = append(counts, dateMap[date].Count)
}
return &types.AdminGetOrderTrendsResp{
Dates: dates,
Amounts: amounts,
Counts: counts,
}, nil
}

View File

@@ -0,0 +1,121 @@
package admin_agent
import (
"context"
"sort"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/app/main/model"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetProductDistributionLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetProductDistributionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetProductDistributionLogic {
return &AdminGetProductDistributionLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetProductDistributionLogic) AdminGetProductDistribution() (resp *types.AdminGetProductDistributionResp, err error) {
// 1. Query all commissions
builder := l.svcCtx.AgentCommissionModel.SelectBuilder()
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, err
}
// 2. Collect all product IDs
productIds := make([]string, 0, len(commissions))
productIDSet := make(map[string]bool)
for _, commission := range commissions {
if !productIDSet[commission.ProductId] {
productIDSet[commission.ProductId] = true
productIds = append(productIds, commission.ProductId)
}
}
// 3. Query products
var products []*model.Product
if len(productIds) > 0 {
products, err = l.svcCtx.ProductModel.FindAll(l.ctx,
l.svcCtx.ProductModel.SelectBuilder(), "")
if err != nil {
return nil, err
}
}
// 4. Build product name map
productNameMap := make(map[string]string)
for _, product := range products {
productNameMap[product.Id] = product.ProductName
}
// 5. Group by product in Go
productStats := make(map[string]*struct {
Count int64
Amount float64
})
for _, commission := range commissions {
productName := productNameMap[commission.ProductId]
if productName == "" {
productName = "未知产品"
}
if _, exists := productStats[productName]; !exists {
productStats[productName] = &struct {
Count int64
Amount float64
}{}
}
productStats[productName].Count++
productStats[productName].Amount += commission.Amount
}
// 6. Sort by amount descending
type ProductStat struct {
Name string
Count int64
Amount float64
}
stats := make([]ProductStat, 0, len(productStats))
for name, stat := range productStats {
stats = append(stats, ProductStat{
Name: name,
Count: stat.Count,
Amount: stat.Amount,
})
}
sort.Slice(stats, func(i, j int) bool {
return stats[i].Amount > stats[j].Amount
})
// 7. Build response
productNames := make([]string, len(stats))
counts := make([]int64, len(stats))
amounts := make([]float64, len(stats))
for i, stat := range stats {
productNames[i] = stat.Name
counts[i] = stat.Count
amounts[i] = stat.Amount
}
return &types.AdminGetProductDistributionResp{
Products: productNames,
Counts: counts,
Amounts: amounts,
}, nil
}

View File

@@ -0,0 +1,80 @@
package admin_agent
import (
"context"
"sort"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/common/globalkey"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetRegionDistributionLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetRegionDistributionLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetRegionDistributionLogic {
return &AdminGetRegionDistributionLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetRegionDistributionLogic) AdminGetRegionDistribution() (resp *types.AdminGetRegionDistributionResp, err error) {
// 1. Query all agents
builder := l.svcCtx.AgentModel.SelectBuilder()
builder = builder.Where("del_state = ?", globalkey.DelStateNo)
agents, err := l.svcCtx.AgentModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, err
}
// 2. Group by region in Go
regionMap := make(map[string]int64)
for _, agent := range agents {
region := "未知"
if agent.Region.Valid && agent.Region.String != "" {
region = agent.Region.String
}
regionMap[region]++
}
// 3. Sort by count descending
type RegionStat struct {
Region string
Count int64
}
stats := make([]RegionStat, 0, len(regionMap))
for region, count := range regionMap {
stats = append(stats, RegionStat{
Region: region,
Count: count,
})
}
sort.Slice(stats, func(i, j int) bool {
return stats[i].Count > stats[j].Count
})
// 4. Build response
regions := make([]string, len(stats))
counts := make([]int64, len(stats))
for i, stat := range stats {
regions[i] = stat.Region
counts[i] = stat.Count
}
return &types.AdminGetRegionDistributionResp{
Regions: regions,
Counts: counts,
}, nil
}

View File

@@ -0,0 +1,132 @@
package admin_agent
import (
"context"
"time"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/Masterminds/squirrel"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetStatisticsOverviewLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetStatisticsOverviewLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetStatisticsOverviewLogic {
return &AdminGetStatisticsOverviewLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetStatisticsOverviewLogic) AdminGetStatisticsOverview() (resp *types.AdminGetStatisticsOverviewResp, err error) {
// Get current time for date filtering
now := time.Now()
todayStart := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location())
monthStart := time.Date(now.Year(), now.Month(), 1, 0, 0, 0, 0, now.Location())
// 1. Total agents - count from agent table
totalAgents, err := l.svcCtx.AgentModel.FindCount(l.ctx, l.svcCtx.AgentModel.SelectBuilder(), "id")
if err != nil {
l.Errorf("Failed to count total agents: %v", err)
return nil, err
}
// 2. Today new agents - count agents created today
todayAgentsBuilder := l.svcCtx.AgentModel.SelectBuilder().Where(squirrel.GtOrEq{"create_time": todayStart})
todayNewAgents, err := l.svcCtx.AgentModel.FindCount(l.ctx, todayAgentsBuilder, "id")
if err != nil {
l.Errorf("Failed to count today new agents: %v", err)
return nil, err
}
// 3. Total orders - count from agent_order table
totalOrders, err := l.svcCtx.AgentOrderModel.FindCount(l.ctx, l.svcCtx.AgentOrderModel.SelectBuilder(), "id")
if err != nil {
l.Errorf("Failed to count total orders: %v", err)
return nil, err
}
// 4. Today orders - count orders created today
todayOrdersBuilder := l.svcCtx.AgentOrderModel.SelectBuilder().Where(squirrel.GtOrEq{"create_time": todayStart})
todayOrders, err := l.svcCtx.AgentOrderModel.FindCount(l.ctx, todayOrdersBuilder, "id")
if err != nil {
l.Errorf("Failed to count today orders: %v", err)
return nil, err
}
// 5. Total order amount - sum from agent_order table
totalOrderAmount, err := l.svcCtx.AgentOrderModel.FindSum(l.ctx, l.svcCtx.AgentOrderModel.SelectBuilder(), "order_amount")
if err != nil {
l.Errorf("Failed to sum total order amount: %v", err)
return nil, err
}
// 6. Today order amount - sum orders created today
todayOrderAmount, err := l.svcCtx.AgentOrderModel.FindSum(l.ctx, todayOrdersBuilder, "order_amount")
if err != nil {
l.Errorf("Failed to sum today order amount: %v", err)
return nil, err
}
// 7. Total commission - sum of all commission amounts
totalCommission, err := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, l.svcCtx.AgentCommissionModel.SelectBuilder(), "amount")
if err != nil {
l.Errorf("Failed to sum total commission: %v", err)
return nil, err
}
// 8. Today commission - sum of today's commission
todayCommissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().Where(squirrel.GtOrEq{"create_time": todayStart})
todayCommission, err := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, todayCommissionBuilder, "amount")
if err != nil {
l.Errorf("Failed to sum today commission: %v", err)
return nil, err
}
// 9. Pending withdraw - sum of withdraw records with status=0
pendingWithdrawBuilder := l.svcCtx.AgentWithdrawModel.SelectBuilder().Where(squirrel.Eq{"status": 0})
pendingWithdraw, err := l.svcCtx.AgentWithdrawModel.FindSum(l.ctx, pendingWithdrawBuilder, "withdraw_amount")
if err != nil {
l.Errorf("Failed to sum pending withdraw: %v", err)
return nil, err
}
// 10. Month order amount - sum of orders in current month
monthOrderAmountBuilder := l.svcCtx.AgentOrderModel.SelectBuilder().Where(squirrel.GtOrEq{"create_time": monthStart})
monthOrderAmount, err := l.svcCtx.AgentOrderModel.FindSum(l.ctx, monthOrderAmountBuilder, "order_amount")
if err != nil {
l.Errorf("Failed to sum month order amount: %v", err)
return nil, err
}
// 11. Month commission - sum of commission in current month
monthCommissionBuilder := l.svcCtx.AgentCommissionModel.SelectBuilder().Where(squirrel.GtOrEq{"create_time": monthStart})
monthCommission, err := l.svcCtx.AgentCommissionModel.FindSum(l.ctx, monthCommissionBuilder, "amount")
if err != nil {
l.Errorf("Failed to sum month commission: %v", err)
return nil, err
}
resp = &types.AdminGetStatisticsOverviewResp{
TotalAgents: totalAgents,
TodayNewAgents: todayNewAgents,
TotalOrders: totalOrders,
TodayOrders: todayOrders,
TotalOrderAmount: totalOrderAmount,
TodayOrderAmount: todayOrderAmount,
TotalCommission: totalCommission,
TodayCommission: todayCommission,
PendingWithdraw: pendingWithdraw,
MonthOrderAmount: monthOrderAmount,
MonthCommission: monthCommission,
}
return resp, nil
}

View File

@@ -0,0 +1,94 @@
package admin_agent
import (
"context"
"fmt"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/common/xerr"
"sim-server/pkg/lzkit/crypto"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v3/client"
"github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminSendAgentComplaintNotifyLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminSendAgentComplaintNotifyLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminSendAgentComplaintNotifyLogic {
return &AdminSendAgentComplaintNotifyLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminSendAgentComplaintNotifyLogic) AdminSendAgentComplaintNotify(req *types.AdminSendAgentComplaintNotifyReq) (resp *types.AdminSendAgentComplaintNotifyResp, err error) {
// 1. 查询代理信息
agent, err := l.svcCtx.AgentModel.FindOne(l.ctx, req.AgentId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理信息失败: %v", err)
}
// 2. 解密手机号
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)
}
// 3. 发送短信
smsResp, err := l.sendSmsRequest(mobile, req.UserName)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "发送短信失败: %v", err)
}
// 4. 检查发送结果
if *smsResp.Body.Code != "OK" {
return &types.AdminSendAgentComplaintNotifyResp{
Success: false,
Message: fmt.Sprintf("短信发送失败: %s", *smsResp.Body.Message),
}, nil
}
resp = &types.AdminSendAgentComplaintNotifyResp{
Success: true,
Message: "投诉通知发送成功",
}
return
}
// CreateClient 创建阿里云短信客户端
func (l *AdminSendAgentComplaintNotifyLogic) CreateClient() (*dysmsapi.Client, error) {
config := &openapi.Config{
AccessKeyId: &l.svcCtx.Config.VerifyCode.AccessKeyID,
AccessKeySecret: &l.svcCtx.Config.VerifyCode.AccessKeySecret,
}
config.Endpoint = tea.String(l.svcCtx.Config.VerifyCode.EndpointURL)
return dysmsapi.NewClient(config)
}
// sendSmsRequest 发送投诉通知短信请求
func (l *AdminSendAgentComplaintNotifyLogic) sendSmsRequest(mobile, userName string) (*dysmsapi.SendSmsResponse, error) {
// 初始化阿里云短信客户端
cli, err := l.CreateClient()
if err != nil {
return nil, err
}
request := &dysmsapi.SendSmsRequest{
SignName: tea.String(l.svcCtx.Config.VerifyCode.SignName),
TemplateCode: tea.String(l.svcCtx.Config.VerifyCode.ComplaintTemplate),
PhoneNumbers: tea.String(mobile),
TemplateParam: tea.String(fmt.Sprintf("{\"name\":\"%s\"}", userName)),
}
runtime := &service.RuntimeOptions{}
return cli.SendSmsWithOptions(request, runtime)
}

View File

@@ -0,0 +1,30 @@
package admin_agent
import (
"context"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminAuditAgentLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminAuditAgentLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminAuditAgentLogic {
return &AdminAuditAgentLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminAuditAgentLogic) AdminAuditAgent(req *types.AdminAuditAgentReq) (resp *types.AdminAuditAgentResp, err error) {
// todo: add your logic here and delete this line
return
}

View File

@@ -0,0 +1,185 @@
package admin_agent
import (
"context"
"strconv"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/common/ctxdata"
"sim-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/stores/sqlx"
)
type AdminAuditAgentWithdrawLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminAuditAgentWithdrawLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminAuditAgentWithdrawLogic {
return &AdminAuditAgentWithdrawLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminAuditAgentWithdrawLogic) AdminAuditAgentWithdraw(req *types.AdminAuditAgentWithdrawReq) (resp *types.AdminAuditAgentWithdrawResp, err error) {
// 1. 获取当前审核人ID管理员ID
adminUserId, err := ctxdata.GetUidFromCtx(l.ctx)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取审核人信息失败: %v", err)
}
// 2. 验证审核状态
if req.Status != 1 && req.Status != 2 {
return nil, errors.Wrap(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "审核状态无效,只能通过(1)或拒绝(2)")
}
// 3. 如果是拒绝,必须填写审核原因
if req.Status == 2 && req.AuditRemark == "" {
return nil, errors.Wrap(xerr.NewErrCode(xerr.REUQEST_PARAM_ERROR), "拒绝提现时必须填写审核原因")
}
// 4. 查询提现记录
withdrawRecord, err := l.svcCtx.AgentWithdrawModel.FindOne(l.ctx, req.WithdrawId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录失败: %v", err)
}
// 5. 验证提现记录状态
if withdrawRecord.Status != 0 {
return nil, errors.Wrap(xerr.NewErrCode(xerr.CUSTOM_ERROR), "该提现记录已被审核,请勿重复操作")
}
// 6. 如果是审核通过,从配置表读取税率并计算税费
var taxAmount float64
var actualAmount float64
if req.Status == 1 {
// 从配置表读取税率
taxRateConfig, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, "tax_rate")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取税率配置失败: %v", err)
}
taxRate, _ := strconv.ParseFloat(taxRateConfig.ConfigValue, 64)
// 计算税费和实际到账金额
taxAmount = withdrawRecord.WithdrawAmount * taxRate
actualAmount = withdrawRecord.WithdrawAmount - taxAmount
logx.Infof("提现审核通过计算税费:提现金额=%.2f,税率=%.4f,税费=%.2f,实际到账=%.2f",
withdrawRecord.WithdrawAmount, taxRate, taxAmount, actualAmount)
}
// 7. 查询代理钱包
wallet, err := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, withdrawRecord.AgentId)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询代理钱包失败: %v", err)
}
// 8. 开启事务处理审核逻辑
err = l.svcCtx.AgentWalletModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
// 7.0 在事务中再次查询提现记录和钱包,确保数据是最新的且未被修改
withdrawRecord, err = l.svcCtx.AgentWithdrawModel.FindOne(ctx, req.WithdrawId)
if err != nil {
return errors.Wrapf(err, "查询提现记录失败")
}
// 验证状态未被修改
if withdrawRecord.Status != 0 {
return errors.New("该提现记录已被审核,请勿重复操作")
}
wallet, err = l.svcCtx.AgentWalletModel.FindOneByAgentId(ctx, withdrawRecord.AgentId)
if err != nil {
return errors.Wrapf(err, "查询代理钱包失败")
}
// 验证冻结金额是否匹配提现记录
if wallet.FrozenAmount < withdrawRecord.FrozenAmount {
return errors.Errorf("钱包冻结金额不足,当前冻结: %.2f,需要: %.2f", wallet.FrozenAmount, withdrawRecord.FrozenAmount)
}
if req.Status == 1 {
// ============ 审核通过 ============
// 8.1 更新钱包:扣除冻结金额,增加累计提现金额
wallet.FrozenAmount -= withdrawRecord.FrozenAmount
wallet.TotalWithdraw += actualAmount // 累计实际到账金额
err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
if err != nil {
return errors.Wrapf(err, "更新钱包信息失败")
}
// 8.2 更新提现记录状态
updateSQL := `
UPDATE agent_withdraw
SET status = 1,
tax_amount = ?,
actual_amount = ?,
audit_user_id = ?,
audit_time = NOW(),
audit_remark = ?,
version = version + 1
WHERE id = ? AND version = ?
`
_, err = session.Exec(updateSQL, taxAmount, actualAmount, adminUserId, req.AuditRemark, req.WithdrawId, withdrawRecord.Version)
if err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
logx.Infof("管理员 %s 审核通过提现记录 %s代理ID: %s提现金额: %.2f,实际到账: %.2f",
adminUserId, req.WithdrawId, withdrawRecord.AgentId, withdrawRecord.WithdrawAmount, actualAmount)
} else {
// ============ 审核拒绝 ============
// 7.1 更新钱包:解冻金额,返回余额
wallet.FrozenAmount -= withdrawRecord.FrozenAmount
wallet.Balance += withdrawRecord.FrozenAmount
err := l.svcCtx.AgentWalletModel.UpdateWithVersion(ctx, session, wallet)
if err != nil {
return errors.Wrapf(err, "更新钱包信息失败")
}
// 7.2 更新提现记录状态
updateSQL := `
UPDATE agent_withdraw
SET status = 2,
audit_user_id = ?,
audit_time = NOW(),
audit_remark = ?,
version = version + 1
WHERE id = ? AND version = ?
`
_, err = session.Exec(updateSQL, adminUserId, req.AuditRemark, req.WithdrawId, withdrawRecord.Version)
if err != nil {
return errors.Wrapf(err, "更新提现记录失败")
}
logx.Infof("管理员 %s 拒绝提现记录 %s代理ID: %s提现金额: %.2f,原因: %s",
adminUserId, req.WithdrawId, withdrawRecord.AgentId, withdrawRecord.WithdrawAmount, req.AuditRemark)
}
return nil
})
if err != nil {
return nil, errors.Wrap(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), err.Error())
}
// 8. 返回成功响应
message := "审核通过"
if req.Status == 2 {
message = "审核拒绝"
}
return &types.AdminAuditAgentWithdrawResp{
Success: true,
Message: message,
}, nil
}

View File

@@ -0,0 +1,74 @@
package admin_agent
import (
"context"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/Masterminds/squirrel"
"github.com/jinzhu/copier"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentCommissionListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentCommissionListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentCommissionListLogic {
return &AdminGetAgentCommissionListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentCommissionListLogic) AdminGetAgentCommissionList(req *types.AdminGetAgentCommissionListReq) (resp *types.AdminGetAgentCommissionListResp, err error) {
builder := l.svcCtx.AgentCommissionModel.SelectBuilder()
if req.AgentId != nil {
builder = builder.Where(squirrel.Eq{"agent_id": *req.AgentId})
}
if req.Status != nil {
builder = builder.Where(squirrel.Eq{"status": *req.Status})
}
// 产品名称筛选功能已移除如需可按product_id筛选
list, total, err := l.svcCtx.AgentCommissionModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
if err != nil {
return nil, err
}
// 批量查product_name
productIds := make(map[string]struct{})
for _, v := range list {
productIds[v.ProductId] = struct{}{}
}
productNameMap := make(map[string]string)
if len(productIds) > 0 {
ids := make([]string, 0, len(productIds))
for id := range productIds {
ids = append(ids, id)
}
builder := l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"id": ids})
products, _ := l.svcCtx.ProductModel.FindAll(l.ctx, builder, "")
for _, p := range products {
productNameMap[p.Id] = p.ProductName
}
}
items := make([]types.AgentCommissionListItem, 0, len(list))
for _, v := range list {
item := types.AgentCommissionListItem{}
_ = copier.Copy(&item, v)
item.ProductName = productNameMap[v.ProductId]
item.CreateTime = v.CreateTime.Format("2006-01-02 15:04:05")
items = append(items, item)
}
resp = &types.AdminGetAgentCommissionListResp{
Total: total,
Items: items,
}
return
}

View File

@@ -0,0 +1,62 @@
package admin_agent
import (
"context"
"strconv"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentConfigLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentConfigLogic {
return &AdminGetAgentConfigLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentConfigLogic) AdminGetAgentConfig() (resp *types.AdminGetAgentConfigResp, err error) {
// 获取配置值的辅助函数
getConfigFloat := func(key string) float64 {
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, key)
if err != nil {
return 0
}
value, _ := strconv.ParseFloat(config.ConfigValue, 64)
return value
}
getConfigInt := func(key string) int64 {
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, key)
if err != nil {
return 0
}
value, _ := strconv.ParseInt(config.ConfigValue, 10, 64)
return value
}
// 系统简化:移除等级加成、升级费用、升级返佣、直接上级返佣配置、免税额度
// 只保留佣金冻结配置和税率配置
// 获取佣金冻结配置
commissionFreezeRatio := getConfigFloat("commission_freeze_ratio")
commissionFreezeThreshold := getConfigFloat("commission_freeze_threshold")
commissionFreezeDays := getConfigInt("commission_freeze_days")
return &types.AdminGetAgentConfigResp{
CommissionFreeze: types.CommissionFreezeConfig{
Ratio: commissionFreezeRatio,
Threshold: commissionFreezeThreshold,
Days: commissionFreezeDays,
},
TaxRate: getConfigFloat("tax_rate"),
}, nil
}

View File

@@ -0,0 +1,100 @@
package admin_agent
import (
"context"
"fmt"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/Masterminds/squirrel"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentLinkListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentLinkListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentLinkListLogic {
return &AdminGetAgentLinkListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentLinkListLogic) AdminGetAgentLinkList(req *types.AdminGetAgentLinkListReq) (resp *types.AdminGetAgentLinkListResp, err error) {
builder := l.svcCtx.AgentLinkModel.SelectBuilder()
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)
}
if req.LinkIdentifier != nil && *req.LinkIdentifier != "" {
builder = builder.Where("link_identifier = ?", *req.LinkIdentifier)
}
// 如果传入ProductId添加筛选条件
if req.ProductId != nil {
builder = builder.Where("product_id = ?", *req.ProductId)
}
links, total, err := l.svcCtx.AgentLinkModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "id DESC")
if err != nil {
return nil, err
}
// 批量查product_id->name避免N+1
productIdSet := make(map[string]struct{})
linkIdList := make([]string, 0, len(links))
for _, link := range links {
productIdSet[link.ProductId] = struct{}{}
linkIdList = append(linkIdList, link.Id)
}
productIdList := make([]string, 0, len(productIdSet))
for id := range productIdSet {
productIdList = append(productIdList, id)
}
productNameMap := make(map[string]string)
if len(productIdList) > 0 {
products, _ := l.svcCtx.ProductModel.FindAll(l.ctx, l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"id": productIdList}), "")
for _, p := range products {
productNameMap[p.Id] = p.ProductName
}
}
// 批量查询短链信息
shortLinkMap := make(map[string]string) // link_id -> short_link
if len(linkIdList) > 0 {
shortLinks, _ := l.svcCtx.AgentShortLinkModel.FindAll(l.ctx, l.svcCtx.AgentShortLinkModel.SelectBuilder().
Where(squirrel.Eq{"link_id": linkIdList}).
Where(squirrel.Eq{"type": 1}). // 推广报告类型
Where(squirrel.Eq{"del_state": 0}), "")
for _, sl := range shortLinks {
if sl.LinkId.Valid && sl.ShortCode != "" && sl.PromotionDomain != "" {
// 构建短链链接: {promotion_domain}/s/{shortCode}
// promotion_domain 已经包含协议http:// 或 https://
shortLinkMap[sl.LinkId.String] = fmt.Sprintf("%s/s/%s", sl.PromotionDomain, sl.ShortCode)
}
}
}
items := make([]types.AgentLinkListItem, 0, len(links))
for _, link := range links {
items = append(items, types.AgentLinkListItem{
Id: link.Id,
AgentId: link.AgentId,
ProductId: link.ProductId,
ProductName: productNameMap[link.ProductId],
SetPrice: link.SetPrice,
ShortLink: shortLinkMap[link.Id], // 短链链接
CreateTime: link.CreateTime.Format("2006-01-02 15:04:05"),
})
}
resp = &types.AdminGetAgentLinkListResp{
Total: total,
Items: items,
}
return
}

View File

@@ -0,0 +1,105 @@
package admin_agent
import (
"context"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/common/xerr"
"sim-server/pkg/lzkit/crypto"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentListLogic {
return &AdminGetAgentListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentListLogic) AdminGetAgentList(req *types.AdminGetAgentListReq) (resp *types.AdminGetAgentListResp, err error) {
builder := l.svcCtx.AgentModel.SelectBuilder()
// 系统简化:移除 TeamLeaderId, Level 筛选条件
// 只保留 Mobile 和 Region 筛选
if req.Mobile != nil && *req.Mobile != "" {
// 加密手机号进行查询
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)
}
builder = builder.Where(squirrel.Eq{"mobile": encryptedMobile})
}
if req.Region != nil && *req.Region != "" {
// 注意region字段现在是可选的查询时需要处理NULL值
// 如果region字段是NULL使用IS NULL查询否则使用等值查询
// 这里简化处理直接使用等值查询如果数据库中有NULL值需要特殊处理
builder = builder.Where(squirrel.Eq{"region": *req.Region})
}
agents, total, err := l.svcCtx.AgentModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "id DESC")
if err != nil {
return nil, err
}
items := make([]types.AgentListItem, 0, len(agents))
for _, agent := range agents {
agent.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)
}
// 查询钱包信息
wallet, _ := l.svcCtx.AgentWalletModel.FindOneByAgentId(l.ctx, agent.Id)
wechatId := ""
if agent.WechatId.Valid {
wechatId = agent.WechatId.String
}
// 获取区域
region := ""
if agent.Region.Valid {
region = agent.Region.String
}
// 系统简化:移除 Level, LevelName, TeamLeaderId, WithdrawnAmount, IsRealName 字段
item := types.AgentListItem{
Id: agent.Id,
UserId: agent.UserId,
Region: region,
Mobile: agent.Mobile,
WechatId: wechatId,
AgentCode: agent.AgentCode,
Balance: 0,
FrozenAmount: 0,
TotalEarnings: 0,
CreateTime: agent.CreateTime.Format("2006-01-02 15:04:05"),
}
if wallet != nil {
item.Balance = wallet.Balance
item.FrozenAmount = wallet.FrozenAmount
item.TotalEarnings = wallet.TotalEarnings
}
items = append(items, item)
}
resp = &types.AdminGetAgentListResp{
Total: total,
Items: items,
}
return
}

View File

@@ -0,0 +1,98 @@
package admin_agent
import (
"context"
"sim-server/common/xerr"
"github.com/Masterminds/squirrel"
"github.com/pkg/errors"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentOrderListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentOrderListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentOrderListLogic {
return &AdminGetAgentOrderListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentOrderListLogic) AdminGetAgentOrderList(req *types.AdminGetAgentOrderListReq) (resp *types.AdminGetAgentOrderListResp, err error) {
builder := l.svcCtx.AgentOrderModel.SelectBuilder()
if req.AgentId != nil {
builder = builder.Where("agent_id = ?", *req.AgentId)
}
if req.OrderId != nil {
builder = builder.Where("order_id = ?", *req.OrderId)
}
if req.ProcessStatus != nil {
builder = builder.Where("process_status = ?", *req.ProcessStatus)
}
// 分页查询
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
orders, total, err := l.svcCtx.AgentOrderModel.FindPageListByPageWithTotal(l.ctx, builder, page, pageSize, "create_time DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询代理订单列表失败, %v", err)
}
// 批量查询产品名称
productIdSet := make(map[string]struct{})
for _, order := range orders {
productIdSet[order.ProductId] = struct{}{}
}
productIdList := make([]string, 0, len(productIdSet))
for id := range productIdSet {
productIdList = append(productIdList, id)
}
productNameMap := make(map[string]string)
if len(productIdList) > 0 {
products, _ := l.svcCtx.ProductModel.FindAll(l.ctx, l.svcCtx.ProductModel.SelectBuilder().Where(squirrel.Eq{"id": productIdList}), "")
for _, p := range products {
productNameMap[p.Id] = p.ProductName
}
}
// 组装响应
items := make([]types.AgentOrderListItem, 0, len(orders))
for _, order := range orders {
items = append(items, types.AgentOrderListItem{
Id: order.Id,
AgentId: order.AgentId,
OrderId: order.OrderId,
ProductId: order.ProductId,
ProductName: productNameMap[order.ProductId],
OrderAmount: order.OrderAmount,
SetPrice: order.SetPrice,
ActualBasePrice: order.ActualBasePrice,
PriceCost: order.PriceCost,
AgentProfit: order.AgentProfit,
ProcessStatus: order.ProcessStatus,
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
})
}
return &types.AdminGetAgentOrderListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,564 @@
package admin_agent
import (
"context"
"fmt"
"strings"
"time"
"sim-server/app/main/model"
"sim-server/common/xerr"
"sim-server/pkg/lzkit/crypto"
"github.com/pkg/errors"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentOrdersListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentOrdersListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentOrdersListLogic {
return &AdminGetAgentOrdersListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentOrdersListLogic) AdminGetAgentOrdersList(req *types.AdminGetAgentOrdersListReq) (resp *types.AdminGetAgentOrdersListResp, err error) {
// 1. 查询订单列表(基础查询)
orders, total, err := l.queryOrders(req)
if err != nil {
return nil, err
}
if len(orders) == 0 {
return &types.AdminGetAgentOrdersListResp{
Total: 0,
Items: []types.AgentOrdersListItem{},
}, nil
}
// 2. 收集所有需要的ID
orderIds := make([]string, len(orders))
userIds := make([]string, 0, len(orders))
productIds := make([]string, 0, len(orders))
for i, order := range orders {
orderIds[i] = order.Id
userIds = append(userIds, order.UserId)
productIds = append(productIds, order.ProductId)
}
// 3. 批量查询佣金信息
commissionsMap := l.queryCommissionsByOrderIds(orderIds)
// 4. 批量查询代理信息
agentIds := make([]string, 0)
for _, commission := range commissionsMap {
agentIds = append(agentIds, commission.AgentId)
}
agentsMap := l.queryAgentsByAgentIds(agentIds)
// 5. 批量查询用户信息
usersMap := l.queryUsersByUserIds(userIds)
// 6. 批量查询产品信息
productsMap := l.queryProductsByProductIds(productIds)
// 7. 批量查询查询记录(用于报告结果跳转)
queriesMap := l.queryQueriesByOrderIds(orderIds)
// 8. 组装数据
list := l.assembleOrderList(orders, commissionsMap, agentsMap, usersMap, productsMap, queriesMap)
// 8. 应用后端筛选条件(代理、用户、产品、佣金状态)
filteredList := l.applyFilters(list, req, agentsMap, usersMap, productsMap, commissionsMap)
// 9. 应用分页(如果使用了后端筛选)
if l.needsBackendFiltering(req) {
total = int64(len(filteredList))
// 手动分页
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
start := (page - 1) * pageSize
end := start + pageSize
if start > int64(len(filteredList)) {
return &types.AdminGetAgentOrdersListResp{
Total: total,
Items: []types.AgentOrdersListItem{},
}, nil
}
if end > int64(len(filteredList)) {
end = int64(len(filteredList))
}
return &types.AdminGetAgentOrdersListResp{
Total: total,
Items: filteredList[start:end],
}, nil
}
return &types.AdminGetAgentOrdersListResp{
Total: total,
Items: filteredList,
}, nil
}
// needsBackendFiltering 检查是否需要后端筛选
func (l *AdminGetAgentOrdersListLogic) needsBackendFiltering(req *types.AdminGetAgentOrdersListReq) bool {
return (req.AgentId != nil && *req.AgentId != "") ||
(req.AgentMobile != nil && *req.AgentMobile != "") ||
(req.UserMobile != nil && *req.UserMobile != "") ||
(req.ProductName != nil && *req.ProductName != "") ||
(req.CommissionStatus != nil)
}
// applyFilters 应用后端筛选条件
func (l *AdminGetAgentOrdersListLogic) applyFilters(
list []types.AgentOrdersListItem,
req *types.AdminGetAgentOrdersListReq,
agentsMap map[string]*model.Agent,
usersMap map[string]*model.User,
productsMap map[string]*model.Product,
commissionsMap map[string]*model.AgentCommission,
) []types.AgentOrdersListItem {
var result []types.AgentOrdersListItem
for _, item := range list {
// 代理ID筛选
if req.AgentId != nil && *req.AgentId != "" {
if item.AgentId != *req.AgentId {
continue
}
}
// 代理手机号筛选
if req.AgentMobile != nil && *req.AgentMobile != "" {
if !contains(item.AgentMobile, *req.AgentMobile) {
continue
}
}
// 用户手机号筛选
if req.UserMobile != nil && *req.UserMobile != "" {
if item.UserMobile == nil || !contains(*item.UserMobile, *req.UserMobile) {
continue
}
}
// 产品名称筛选
if req.ProductName != nil && *req.ProductName != "" {
if !contains(item.ProductName, *req.ProductName) {
continue
}
}
// 佣金状态筛选
if req.CommissionStatus != nil {
if item.CommissionStatus != *req.CommissionStatus {
continue
}
}
result = append(result, item)
}
return result
}
// contains 检查字符串是否包含子字符串(不区分大小写)
func contains(str, substr string) bool {
return strings.Contains(strings.ToLower(str), strings.ToLower(substr))
}
// queryOrders 查询订单列表
func (l *AdminGetAgentOrdersListLogic) queryOrders(req *types.AdminGetAgentOrdersListReq) ([]*model.Order, int64, error) {
builder := l.svcCtx.OrderModel.SelectBuilder()
// 订单筛选
if req.OrderNo != nil && *req.OrderNo != "" {
builder = builder.Where("`order_no` LIKE ?", "%"+*req.OrderNo+"%")
}
if req.PlatformOrderId != nil && *req.PlatformOrderId != "" {
builder = builder.Where("`platform_order_id` LIKE ?", "%"+*req.PlatformOrderId+"%")
}
if req.PaymentPlatform != nil && *req.PaymentPlatform != "" {
builder = builder.Where("`payment_platform` = ?", *req.PaymentPlatform)
}
if req.PaymentScene != nil && *req.PaymentScene != "" {
builder = builder.Where("`payment_scene` = ?", *req.PaymentScene)
}
if req.OrderStatus != nil && *req.OrderStatus != "" {
builder = builder.Where("`status` = ?", *req.OrderStatus)
}
// 时间筛选
if req.CreateTimeStart != nil && *req.CreateTimeStart != "" {
createTimeStart, err := time.Parse("2006-01-02 15:04:05", *req.CreateTimeStart)
if err == nil {
builder = builder.Where("`create_time` >= ?", createTimeStart)
}
}
if req.CreateTimeEnd != nil && *req.CreateTimeEnd != "" {
createTimeEnd, err := time.Parse("2006-01-02 15:04:05", *req.CreateTimeEnd)
if err == nil {
builder = builder.Where("`create_time` <= ?", createTimeEnd)
}
}
if req.PayTimeStart != nil && *req.PayTimeStart != "" {
payTimeStart, err := time.Parse("2006-01-02 15:04:05", *req.PayTimeStart)
if err == nil {
builder = builder.Where("`pay_time` >= ?", payTimeStart)
}
}
if req.PayTimeEnd != nil && *req.PayTimeEnd != "" {
payTimeEnd, err := time.Parse("2006-01-02 15:04:05", *req.PayTimeEnd)
if err == nil {
builder = builder.Where("`pay_time` <= ?", payTimeEnd)
}
}
// 排序
orderBy := "`create_time` DESC"
if req.OrderBy != nil && *req.OrderBy != "" {
orderType := "DESC"
if req.OrderType != nil && strings.ToUpper(*req.OrderType) == "ASC" {
orderType = "ASC"
}
orderBy = fmt.Sprintf("`%s` %s", *req.OrderBy, orderType)
}
// 分页
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
// 查询总数和列表
orders, total, err := l.svcCtx.OrderModel.FindPageListByPageWithTotal(l.ctx, builder, page, pageSize, orderBy)
if err != nil {
return nil, 0, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单列表失败, %v", err)
}
return orders, total, nil
}
// queryCommissionsByOrderIds 批量查询佣金信息
func (l *AdminGetAgentOrdersListLogic) queryCommissionsByOrderIds(orderIds []string) map[string]*model.AgentCommission {
if len(orderIds) == 0 {
return make(map[string]*model.AgentCommission)
}
// 构建IN查询
inClause := strings.Repeat("?,", len(orderIds))
inClause = inClause[:len(inClause)-1]
commissions, err := l.svcCtx.AgentCommissionModel.FindAll(l.ctx,
l.svcCtx.AgentCommissionModel.SelectBuilder().
Where(fmt.Sprintf("`order_id` IN (%s)", inClause), stringsToInterfaces(orderIds)...),
"")
if err != nil {
logx.Errorf("查询佣金信息失败: %v", err)
return make(map[string]*model.AgentCommission)
}
// 构建map: orderId -> commission
result := make(map[string]*model.AgentCommission)
for _, commission := range commissions {
result[commission.OrderId] = commission
}
return result
}
// queryAgentsByAgentIds 批量查询代理信息
func (l *AdminGetAgentOrdersListLogic) queryAgentsByAgentIds(agentIds []string) map[string]*model.Agent {
if len(agentIds) == 0 {
return make(map[string]*model.Agent)
}
// 去重
uniqueAgentIds := make(map[string]bool)
for _, id := range agentIds {
uniqueAgentIds[id] = true
}
ids := make([]string, 0, len(uniqueAgentIds))
for id := range uniqueAgentIds {
ids = append(ids, id)
}
if len(ids) == 0 {
return make(map[string]*model.Agent)
}
// 构建IN查询
inClause := strings.Repeat("?,", len(ids))
inClause = inClause[:len(inClause)-1]
agents, err := l.svcCtx.AgentModel.FindAll(l.ctx,
l.svcCtx.AgentModel.SelectBuilder().
Where(fmt.Sprintf("`id` IN (%s)", inClause), stringsToInterfaces(ids)...),
"")
if err != nil {
logx.Errorf("查询代理信息失败: %v", err)
return make(map[string]*model.Agent)
}
// 构建map: agentId -> agent
result := make(map[string]*model.Agent)
for _, agent := range agents {
result[agent.Id] = agent
}
return result
}
// queryUsersByUserIds 批量查询用户信息
func (l *AdminGetAgentOrdersListLogic) queryUsersByUserIds(userIds []string) map[string]*model.User {
if len(userIds) == 0 {
return make(map[string]*model.User)
}
// 去重
uniqueUserIds := make(map[string]bool)
for _, id := range userIds {
uniqueUserIds[id] = true
}
ids := make([]string, 0, len(uniqueUserIds))
for id := range uniqueUserIds {
ids = append(ids, id)
}
if len(ids) == 0 {
return make(map[string]*model.User)
}
// 构建IN查询
inClause := strings.Repeat("?,", len(ids))
inClause = inClause[:len(inClause)-1]
users, err := l.svcCtx.UserModel.FindAll(l.ctx,
l.svcCtx.UserModel.SelectBuilder().
Where(fmt.Sprintf("`id` IN (%s)", inClause), stringsToInterfaces(ids)...),
"")
if err != nil {
logx.Errorf("查询用户信息失败: %v", err)
return make(map[string]*model.User)
}
// 构建map: userId -> user
result := make(map[string]*model.User)
for _, user := range users {
result[user.Id] = user
}
return result
}
// queryProductsByProductIds 批量查询产品信息
func (l *AdminGetAgentOrdersListLogic) queryProductsByProductIds(productIds []string) map[string]*model.Product {
if len(productIds) == 0 {
return make(map[string]*model.Product)
}
// 去重
uniqueProductIds := make(map[string]bool)
for _, id := range productIds {
uniqueProductIds[id] = true
}
ids := make([]string, 0, len(uniqueProductIds))
for id := range uniqueProductIds {
ids = append(ids, id)
}
if len(ids) == 0 {
return make(map[string]*model.Product)
}
// 构建IN查询
inClause := strings.Repeat("?,", len(ids))
inClause = inClause[:len(inClause)-1]
products, err := l.svcCtx.ProductModel.FindAll(l.ctx,
l.svcCtx.ProductModel.SelectBuilder().
Where(fmt.Sprintf("`id` IN (%s)", inClause), stringsToInterfaces(ids)...),
"")
if err != nil {
logx.Errorf("查询产品信息失败: %v", err)
return make(map[string]*model.Product)
}
// 构建map: productId -> product
result := make(map[string]*model.Product)
for _, product := range products {
result[product.Id] = product
}
return result
}
// queryQueriesByOrderIds 批量查询查询记录信息
func (l *AdminGetAgentOrdersListLogic) queryQueriesByOrderIds(orderIds []string) map[string]*model.Query {
if len(orderIds) == 0 {
return make(map[string]*model.Query)
}
// 构建IN查询
inClause := strings.Repeat("?,", len(orderIds))
inClause = inClause[:len(inClause)-1]
queries, err := l.svcCtx.QueryModel.FindAll(l.ctx,
l.svcCtx.QueryModel.SelectBuilder().
Where(fmt.Sprintf("`order_id` IN (%s)", inClause), stringsToInterfaces(orderIds)...),
"")
if err != nil {
logx.Errorf("查询查询记录失败: %v", err)
return make(map[string]*model.Query)
}
// 构建map: orderId -> query
result := make(map[string]*model.Query)
for _, query := range queries {
result[query.OrderId] = query
}
return result
}
// assembleOrderList 组装订单列表数据
func (l *AdminGetAgentOrdersListLogic) assembleOrderList(
orders []*model.Order,
commissionsMap map[string]*model.AgentCommission,
agentsMap map[string]*model.Agent,
usersMap map[string]*model.User,
productsMap map[string]*model.Product,
queriesMap map[string]*model.Query,
) []types.AgentOrdersListItem {
var list []types.AgentOrdersListItem
for _, order := range orders {
// 获取佣金信息
var commissionAmount float64
var commissionStatus int64 = 2 // 默认已取消
var agentId string
var agentMobile string
if commission, ok := commissionsMap[order.Id]; ok {
commissionAmount = commission.Amount
commissionStatus = commission.Status
agentId = commission.AgentId
// 获取代理信息并解密手机号
if agent, ok := agentsMap[commission.AgentId]; ok {
if agent.Mobile != "" {
// 解密代理手机号
mobile, err := crypto.DecryptMobile(agent.Mobile, l.svcCtx.Config.Encrypt.SecretKey)
if err != nil {
logx.Errorf("解密代理手机号失败: %v", err)
agentMobile = ""
}
agentMobile = mobile
}
}
} else {
// 没有佣金记录使用订单ID作为代理ID
agentId = order.Id
}
// 获取用户手机号并解密
var userMobile *string
if user, ok := usersMap[order.UserId]; ok && user.Mobile.Valid {
// 解密用户手机号
mobile, err := crypto.DecryptMobile(user.Mobile.String, l.svcCtx.Config.Encrypt.SecretKey)
if err != nil {
logx.Errorf("解密用户手机号失败: %v", err)
userMobile = nil
} else {
userMobile = &mobile
}
}
// 获取产品名称
productName := "未知产品"
if product, ok := productsMap[order.ProductId]; ok {
productName = product.ProductName
}
// 获取平台订单号
platformOrderId := ""
if order.PlatformOrderId.Valid {
platformOrderId = order.PlatformOrderId.String
}
// 获取支付时间
var payTime *string
if order.PayTime.Valid {
pt := order.PayTime.Time.Format("2006-01-02 15:04:05")
payTime = &pt
}
// 获取查询记录ID用于报告结果跳转
var queryId *string
if query, ok := queriesMap[order.Id]; ok {
queryId = &query.Id
}
list = append(list, types.AgentOrdersListItem{
Id: order.Id,
OrderNo: order.OrderNo,
PlatformOrderId: platformOrderId,
AgentId: agentId,
AgentMobile: agentMobile,
UserId: order.UserId,
UserMobile: userMobile,
ProductId: order.ProductId,
ProductName: productName,
OrderAmount: order.Amount,
CommissionAmount: commissionAmount,
PaymentPlatform: order.PaymentPlatform,
PaymentScene: order.PaymentScene,
OrderStatus: order.Status,
CommissionStatus: commissionStatus,
CreateTime: order.CreateTime.Format("2006-01-02 15:04:05"),
PayTime: payTime,
QueryId: queryId,
})
}
return list
}
// stringsToInterfaces 将字符串数组转换为interface数组
func stringsToInterfaces(strs []string) []interface{} {
result := make([]interface{}, len(strs))
for i, str := range strs {
result[i] = str
}
return result
}

View File

@@ -0,0 +1,97 @@
package admin_agent
import (
"context"
"sim-server/common/xerr"
"github.com/pkg/errors"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentProductConfigListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentProductConfigListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentProductConfigListLogic {
return &AdminGetAgentProductConfigListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentProductConfigListLogic) AdminGetAgentProductConfigList(req *types.AdminGetAgentProductConfigListReq) (resp *types.AdminGetAgentProductConfigListResp, err error) {
builder := l.svcCtx.AgentProductConfigModel.SelectBuilder()
// 如果提供了产品ID直接过滤
if req.ProductId != nil {
builder = builder.Where("product_id = ?", *req.ProductId)
}
// 如果提供了产品名称,通过关联查询 product 表过滤
if req.ProductName != nil && *req.ProductName != "" {
builder = builder.Where("product_id IN (SELECT id FROM product WHERE product_name LIKE ?)", "%"+*req.ProductName+"%")
}
// 分页查询
page := req.Page
if page <= 0 {
page = 1
}
pageSize := req.PageSize
if pageSize <= 0 {
pageSize = 20
}
configs, total, err := l.svcCtx.AgentProductConfigModel.FindPageListByPageWithTotal(l.ctx, builder, page, pageSize, "id DESC")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品配置列表失败, %v", err)
}
// 组装响应(通过 product_id 查询产品名称)
items := make([]types.AgentProductConfigItem, 0, len(configs))
for _, config := range configs {
// 通过 product_id 查询产品信息获取产品名称
product, err := l.svcCtx.ProductModel.FindOne(l.ctx, config.ProductId)
productName := ""
if err == nil {
productName = product.ProductName
} else {
// 如果产品不存在,记录日志但不影响主流程
l.Infof("查询产品信息失败, productId: %d, err: %v", config.ProductId, err)
}
priceThreshold := 0.0
if config.PriceThreshold.Valid {
priceThreshold = config.PriceThreshold.Float64
}
priceFeeRate := 0.0
if config.PriceFeeRate.Valid {
priceFeeRate = config.PriceFeeRate.Float64
}
items = append(items, types.AgentProductConfigItem{
Id: config.Id,
ProductId: config.ProductId,
ProductName: productName,
BasePrice: config.BasePrice,
PriceRangeMin: config.BasePrice, // 最低定价等于基础底价
PriceRangeMax: config.SystemMaxPrice,
PriceThreshold: priceThreshold,
PriceFeeRate: priceFeeRate,
CreateTime: config.CreateTime.Format("2006-01-02 15:04:05"),
})
}
return &types.AdminGetAgentProductConfigListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,158 @@
package admin_agent
import (
"context"
"fmt"
"strings"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/common/globalkey"
"sim-server/common/xerr"
"github.com/pkg/errors"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminGetAgentWithdrawListLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminGetAgentWithdrawListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminGetAgentWithdrawListLogic {
return &AdminGetAgentWithdrawListLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminGetAgentWithdrawListLogic) AdminGetAgentWithdrawList(req *types.AdminGetAgentWithdrawListReq) (resp *types.AdminGetAgentWithdrawListResp, err error) {
// 1. 构建查询条件
whereConditions := []string{fmt.Sprintf("del_state = %d", globalkey.DelStateNo)}
args := []interface{}{}
// 可选过滤条件
if req.AgentId != nil && *req.AgentId != "" {
whereConditions = append(whereConditions, "agent_id = ?")
args = append(args, *req.AgentId)
}
if req.Status != nil {
whereConditions = append(whereConditions, "status = ?")
args = append(args, *req.Status)
}
whereClause := strings.Join(whereConditions, " AND ")
// 2. 构建查询builder
builder := l.svcCtx.AgentWithdrawModel.SelectBuilder().
Where(whereClause, args...).
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.AgentWithdrawModel.FindCount(l.ctx, builder, "id")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录总数失败: %v", err)
}
if total == 0 {
return &types.AdminGetAgentWithdrawListResp{
Total: 0,
Items: []types.AgentWithdrawListItem{},
}, nil
}
// 5. 查询列表
builder = builder.Limit(uint64(pageSize)).Offset(uint64(offset))
withdrawals, err := l.svcCtx.AgentWithdrawModel.FindAll(l.ctx, builder, "")
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询提现记录列表失败: %v", err)
}
// 6. 组装响应
var items []types.AgentWithdrawListItem
for _, withdrawal := range withdrawals {
// 处理代理ID
agentId := withdrawal.AgentId
// 处理代理手机号
agentMobile := ""
if withdrawal.AgentMobile.Valid {
agentMobile = withdrawal.AgentMobile.String
}
// 处理代理编码
var agentCode int64 = 0
if withdrawal.AgentCode.Valid {
agentCode = withdrawal.AgentCode.Int64
}
// 银行卡号脱敏只显示前4位和后4位
bankCardNumber := withdrawal.BankCardNumber
if len(bankCardNumber) > 8 {
bankCardNumber = fmt.Sprintf("%s****%s", bankCardNumber[:4], bankCardNumber[len(bankCardNumber)-4:])
}
// 处理开户支行
bankBranch := ""
if withdrawal.BankBranch.Valid {
bankBranch = withdrawal.BankBranch.String
}
// 处理审核人ID
auditUserId := ""
if withdrawal.AuditUserId.Valid {
auditUserId = withdrawal.AuditUserId.String
}
// 处理审核时间
auditTime := ""
if withdrawal.AuditTime.Valid {
auditTime = withdrawal.AuditTime.Time.Format("2006-01-02 15:04:05")
}
// 处理审核备注
auditRemark := ""
if withdrawal.AuditRemark.Valid {
auditRemark = withdrawal.AuditRemark.String
}
items = append(items, types.AgentWithdrawListItem{
Id: withdrawal.Id,
AgentId: agentId,
AgentMobile: agentMobile,
AgentCode: agentCode,
WithdrawAmount: withdrawal.WithdrawAmount,
TaxAmount: withdrawal.TaxAmount,
ActualAmount: withdrawal.ActualAmount,
FrozenAmount: withdrawal.FrozenAmount,
AccountName: withdrawal.AccountName,
BankCardNumber: bankCardNumber, // 脱敏显示
BankCardNumberFull: withdrawal.BankCardNumber, // 完整卡号用于审核
BankBranch: bankBranch,
Status: withdrawal.Status,
AuditUserId: auditUserId,
AuditTime: auditTime,
AuditRemark: auditRemark,
CreateTime: withdrawal.CreateTime.Format("2006-01-02 15:04:05"),
})
}
return &types.AdminGetAgentWithdrawListResp{
Total: total,
Items: items,
}, nil
}

View File

@@ -0,0 +1,30 @@
package admin_agent
import (
"context"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminRefundAgentOrderLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminRefundAgentOrderLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminRefundAgentOrderLogic {
return &AdminRefundAgentOrderLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminRefundAgentOrderLogic) AdminRefundAgentOrder(req *types.AdminRefundAgentOrderReq) (resp *types.AdminRefundAgentOrderResp, err error) {
// todo: add your logic here and delete this line
return
}

View File

@@ -0,0 +1,126 @@
package admin_agent
import (
"context"
"database/sql"
"strconv"
"sim-server/common/xerr"
"github.com/pkg/errors"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"sim-server/app/main/model"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUpdateAgentConfigLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateAgentConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateAgentConfigLogic {
return &AdminUpdateAgentConfigLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateAgentConfigLogic) AdminUpdateAgentConfig(req *types.AdminUpdateAgentConfigReq) (resp *types.AdminUpdateAgentConfigResp, err error) {
// 系统简化:移除等级加成、升级费用、升级返佣、直接上级返佣配置、免税额度
// 只保留佣金冻结配置和税率配置
configTypeForKey := func(key string) string {
switch key {
case "commission_freeze_ratio", "commission_freeze_threshold", "commission_freeze_days":
return "commission_freeze"
case "tax_rate":
return "tax"
default:
return "other"
}
}
updateConfig := func(key string, value *float64) error {
if value == nil {
return nil
}
valStr := strconv.FormatFloat(*value, 'f', -1, 64)
config, err := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, key)
if err != nil {
if errors.Is(err, model.ErrNotFound) {
cfg := &model.AgentConfig{
ConfigKey: key,
ConfigValue: valStr,
ConfigType: configTypeForKey(key),
Description: sql.NullString{},
DeleteTime: sql.NullTime{},
Version: 0,
}
_, insErr := l.svcCtx.AgentConfigModel.Insert(l.ctx, nil, cfg)
if insErr != nil {
return errors.Wrapf(insErr, "创建配置失败, key: %s", key)
}
return nil
}
return errors.Wrapf(err, "查询配置失败, key: %s", key)
}
config.ConfigValue = valStr
if uErr := l.svcCtx.AgentConfigModel.UpdateWithVersion(l.ctx, nil, config); uErr != nil {
if errors.Is(uErr, model.ErrNoRowsUpdate) {
latestByKey, reErr := l.svcCtx.AgentConfigModel.FindOneByConfigKey(l.ctx, key)
if reErr != nil {
if errors.Is(reErr, model.ErrNotFound) {
cfg := &model.AgentConfig{
ConfigKey: key,
ConfigValue: valStr,
ConfigType: configTypeForKey(key),
Description: sql.NullString{},
DeleteTime: sql.NullTime{},
Version: 0,
}
_, insErr := l.svcCtx.AgentConfigModel.Insert(l.ctx, nil, cfg)
if insErr != nil {
return errors.Wrapf(insErr, "创建配置失败, key: %s", key)
}
return nil
}
return errors.Wrapf(reErr, "查询最新配置失败, key: %s", key)
}
latestByKey.ConfigValue = valStr
return l.svcCtx.AgentConfigModel.UpdateWithVersion(l.ctx, nil, latestByKey)
}
return uErr
}
return nil
}
// 更新佣金冻结配置
if req.CommissionFreeze != nil {
if err := updateConfig("commission_freeze_ratio", &req.CommissionFreeze.Ratio); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新佣金冻结比例失败, %v", err)
}
if err := updateConfig("commission_freeze_threshold", &req.CommissionFreeze.Threshold); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新佣金冻结阈值失败, %v", err)
}
// 更新解冻天数(整数类型)
if req.CommissionFreeze.Days > 0 {
daysFloat := float64(req.CommissionFreeze.Days)
if err := updateConfig("commission_freeze_days", &daysFloat); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新佣金冻结解冻天数失败, %v", err)
}
}
}
// 更新税费配置
if err := updateConfig("tax_rate", req.TaxRate); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新税率失败, %v", err)
}
return &types.AdminUpdateAgentConfigResp{
Success: true,
}, nil
}

View File

@@ -0,0 +1,63 @@
package admin_agent
import (
"context"
"database/sql"
"sim-server/common/xerr"
"github.com/pkg/errors"
"sim-server/app/main/api/internal/svc"
"sim-server/app/main/api/internal/types"
"github.com/zeromicro/go-zero/core/logx"
)
type AdminUpdateAgentProductConfigLogic struct {
logx.Logger
ctx context.Context
svcCtx *svc.ServiceContext
}
func NewAdminUpdateAgentProductConfigLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AdminUpdateAgentProductConfigLogic {
return &AdminUpdateAgentProductConfigLogic{
Logger: logx.WithContext(ctx),
ctx: ctx,
svcCtx: svcCtx,
}
}
func (l *AdminUpdateAgentProductConfigLogic) AdminUpdateAgentProductConfig(req *types.AdminUpdateAgentProductConfigReq) (resp *types.AdminUpdateAgentProductConfigResp, err error) {
// 查询配置
config, err := l.svcCtx.AgentProductConfigModel.FindOne(l.ctx, req.Id)
if err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询产品配置失败, %v", err)
}
// 更新配置字段
config.BasePrice = req.BasePrice
config.SystemMaxPrice = req.PriceRangeMax
// 价格阈值(可选)
if req.PriceThreshold != nil {
config.PriceThreshold = sql.NullFloat64{Float64: *req.PriceThreshold, Valid: true}
} else {
config.PriceThreshold = sql.NullFloat64{Valid: false}
}
// 提价手续费比例(可选)
if req.PriceFeeRate != nil {
config.PriceFeeRate = sql.NullFloat64{Float64: *req.PriceFeeRate, Valid: true}
} else {
config.PriceFeeRate = sql.NullFloat64{Valid: false}
}
// 更新配置
if err := l.svcCtx.AgentProductConfigModel.UpdateWithVersion(l.ctx, nil, config); err != nil {
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新产品配置失败, %v", err)
}
return &types.AdminUpdateAgentProductConfigResp{
Success: true,
}, nil
}