promote
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/app/main/model"
|
||||
"tyc-server/common/ctxdata"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type CreatePromotionLinkLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewCreatePromotionLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreatePromotionLinkLogic {
|
||||
return &CreatePromotionLinkLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 生成6位随机字符串(大小写字母和数字)
|
||||
func generateRandomString() (string, error) {
|
||||
const (
|
||||
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
length = 6
|
||||
)
|
||||
|
||||
result := make([]byte, length)
|
||||
for i := 0; i < length; i++ {
|
||||
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(chars))))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result[i] = chars[num.Int64()]
|
||||
}
|
||||
return string(result), nil
|
||||
}
|
||||
|
||||
func (l *CreatePromotionLinkLogic) CreatePromotionLink(req *types.CreatePromotionLinkReq) (resp *types.CreatePromotionLinkResp, err error) {
|
||||
// 获取当前用户ID
|
||||
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建推广链接, 获取用户信息失败, %+v", getUidErr)
|
||||
}
|
||||
|
||||
// 生成唯一URL
|
||||
var url string
|
||||
maxRetries := 5 // 最大重试次数
|
||||
for i := 0; i < maxRetries; i++ {
|
||||
// 生成6位随机字符串
|
||||
randomStr, err := generateRandomString()
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建推广链接, 生成随机字符串失败, %+v", err)
|
||||
}
|
||||
|
||||
// 检查URL是否已存在
|
||||
existLink, err := l.svcCtx.AdminPromotionLinkModel.FindOneByUrl(l.ctx, randomStr)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建推广链接, 检查URL是否存在失败, %+v", err)
|
||||
}
|
||||
|
||||
if existLink != nil {
|
||||
continue // URL已存在,继续尝试
|
||||
}
|
||||
|
||||
// URL可用
|
||||
url = randomStr
|
||||
break
|
||||
}
|
||||
|
||||
if url == "" {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建推广链接失败, 多次尝试生成唯一URL均失败")
|
||||
}
|
||||
url = fmt.Sprintf("%s/%s", l.svcCtx.Config.AdminPromotion.URLDomain, url)
|
||||
// 创建推广链接
|
||||
link := &model.AdminPromotionLink{
|
||||
Name: req.Name,
|
||||
Url: url,
|
||||
AdminUserId: adminUserId,
|
||||
}
|
||||
|
||||
var linkId int64
|
||||
err = l.svcCtx.AdminPromotionLinkModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
result, err := l.svcCtx.AdminPromotionLinkModel.Insert(l.ctx, session, link)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建推广链接失败, %+v", err)
|
||||
}
|
||||
|
||||
linkId, err = result.LastInsertId()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取推广链接ID失败, %+v", err)
|
||||
}
|
||||
|
||||
// 创建总统计记录
|
||||
totalStats := &model.AdminPromotionLinkStatsTotal{
|
||||
LinkId: linkId,
|
||||
}
|
||||
_, err = l.svcCtx.AdminPromotionLinkStatsTotalModel.Insert(l.ctx, session, totalStats)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建推广链接总统计记录失败, %+v", err)
|
||||
}
|
||||
|
||||
// 创建统计历史记录
|
||||
historyStats := &model.AdminPromotionLinkStatsHistory{
|
||||
LinkId: linkId,
|
||||
StatsDate: time.Now().Truncate(24 * time.Hour),
|
||||
}
|
||||
_, err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.Insert(l.ctx, session, historyStats)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建推广链接统计历史记录失败, %+v", err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "创建推广链接失败, %+v", err)
|
||||
}
|
||||
|
||||
return &types.CreatePromotionLinkResp{
|
||||
Id: linkId,
|
||||
Url: url,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/app/main/model"
|
||||
"tyc-server/common/ctxdata"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type DeletePromotionLinkLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewDeletePromotionLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeletePromotionLinkLogic {
|
||||
return &DeletePromotionLinkLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DeletePromotionLinkLogic) DeletePromotionLink(req *types.DeletePromotionLinkReq) error {
|
||||
// 获取当前用户ID
|
||||
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "删除推广链接, 获取用户信息失败, %+v", getUidErr)
|
||||
}
|
||||
|
||||
// 获取链接信息
|
||||
link, err := l.svcCtx.AdminPromotionLinkModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "删除推广链接, 获取链接信息失败, %+v", err)
|
||||
}
|
||||
|
||||
// 验证用户权限
|
||||
if link.AdminUserId != adminUserId {
|
||||
return errors.Wrapf(xerr.NewErrMsg("无权限删除此链接"), "删除推广链接, 无权限删除此链接, %+v", link)
|
||||
}
|
||||
|
||||
// 在事务中执行所有删除操作
|
||||
err = l.svcCtx.AdminPromotionLinkModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 软删除链接
|
||||
err = l.svcCtx.AdminPromotionLinkModel.DeleteSoft(l.ctx, session, link)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "删除推广链接, 软删除链接失败, %+v", err)
|
||||
}
|
||||
|
||||
// 软删除总统计记录
|
||||
totalStats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(l.ctx, link.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return errors.Wrapf(err, "删除推广链接, 获取总统计记录失败, %+v", err)
|
||||
}
|
||||
if totalStats != nil {
|
||||
err = l.svcCtx.AdminPromotionLinkStatsTotalModel.DeleteSoft(l.ctx, session, totalStats)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "删除推广链接, 软删除总统计记录失败, %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 软删除历史统计记录
|
||||
builder := l.svcCtx.AdminPromotionLinkStatsHistoryModel.SelectBuilder()
|
||||
builder = builder.Where("link_id = ?", link.Id)
|
||||
historyStats, err := l.svcCtx.AdminPromotionLinkStatsHistoryModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "删除推广链接, 获取历史统计记录失败, %+v", err)
|
||||
}
|
||||
for _, stat := range historyStats {
|
||||
err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.DeleteSoft(l.ctx, session, stat)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "删除推广链接, 软删除历史统计记录失败, %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "删除推广链接失败, %+v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/app/main/model"
|
||||
"tyc-server/common/ctxdata"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetPromotionLinkDetailLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetPromotionLinkDetailLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPromotionLinkDetailLogic {
|
||||
return &GetPromotionLinkDetailLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetPromotionLinkDetailLogic) GetPromotionLinkDetail(req *types.GetPromotionLinkDetailReq) (resp *types.GetPromotionLinkDetailResp, err error) {
|
||||
// 获取当前用户ID
|
||||
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取当前用户ID失败, %+v", getUidErr)
|
||||
}
|
||||
|
||||
// 获取链接信息
|
||||
link, err := l.svcCtx.AdminPromotionLinkModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "获取链接信息失败, %+v", err)
|
||||
}
|
||||
|
||||
// 验证用户权限
|
||||
if link.AdminUserId != adminUserId {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("无权限访问此链接"), "获取链接信息失败, 无权限访问此链接, %+v", link)
|
||||
}
|
||||
|
||||
// 获取总统计
|
||||
totalStats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOne(l.ctx, link.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, errors.Wrapf(err, "获取总统计失败, %+v", err)
|
||||
}
|
||||
return &types.GetPromotionLinkDetailResp{
|
||||
Name: link.Name,
|
||||
Url: link.Url,
|
||||
ClickCount: totalStats.ClickCount,
|
||||
PayCount: totalStats.PayCount,
|
||||
PayAmount: fmt.Sprintf("%.2f", totalStats.PayAmount),
|
||||
CreateTime: link.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
UpdateTime: link.UpdateTime.Format("2006-01-02 15:04:05"),
|
||||
LastClickTime: totalStats.LastClickTime.Time.Format("2006-01-02 15:04:05"),
|
||||
LastPayTime: totalStats.LastPayTime.Time.Format("2006-01-02 15:04:05"),
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/app/main/model"
|
||||
"tyc-server/common/ctxdata"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
)
|
||||
|
||||
type GetPromotionLinkListLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetPromotionLinkListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPromotionLinkListLogic {
|
||||
return &GetPromotionLinkListLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetPromotionLinkListLogic) GetPromotionLinkList(req *types.GetPromotionLinkListReq) (resp *types.GetPromotionLinkListResp, err error) {
|
||||
// 获取当前用户ID
|
||||
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取当前用户ID失败, %+v", getUidErr)
|
||||
}
|
||||
|
||||
// 构建查询条件
|
||||
builder := l.svcCtx.AdminPromotionLinkModel.SelectBuilder()
|
||||
builder = builder.Where("admin_user_id = ?", adminUserId)
|
||||
if req.Name != "" {
|
||||
builder = builder.Where("name LIKE ?", "%"+req.Name+"%")
|
||||
}
|
||||
if req.Url != "" {
|
||||
builder = builder.Where("url LIKE ?", "%"+req.Url+"%")
|
||||
}
|
||||
|
||||
// 获取列表和总数
|
||||
links, total, err := l.svcCtx.AdminPromotionLinkModel.FindPageListByPageWithTotal(l.ctx, builder, req.Page, req.PageSize, "create_time DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "获取推广链接列表失败, %+v", err)
|
||||
}
|
||||
|
||||
// 使用MapReduce并发获取统计数据
|
||||
items := make([]types.PromotionLinkItem, len(links))
|
||||
err = mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for _, link := range links {
|
||||
source <- link
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[types.PromotionLinkItem], cancel func(error)) {
|
||||
link := item.(*model.AdminPromotionLink)
|
||||
// 获取总统计
|
||||
totalStats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(l.ctx, link.Id)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
cancel(errors.Wrapf(err, "获取总统计失败, linkId: %d, %+v", link.Id, err))
|
||||
return
|
||||
}
|
||||
writer.Write(types.PromotionLinkItem{
|
||||
Id: link.Id,
|
||||
Name: link.Name,
|
||||
Url: link.Url,
|
||||
ClickCount: totalStats.ClickCount,
|
||||
PayCount: totalStats.PayCount,
|
||||
PayAmount: fmt.Sprintf("%.2f", totalStats.PayAmount),
|
||||
CreateTime: link.CreateTime.Format("2006-01-02 15:04:05"),
|
||||
LastClickTime: func() string {
|
||||
if totalStats.LastClickTime.Valid {
|
||||
return totalStats.LastClickTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return ""
|
||||
}(),
|
||||
LastPayTime: func() string {
|
||||
if totalStats.LastPayTime.Valid {
|
||||
return totalStats.LastPayTime.Time.Format("2006-01-02 15:04:05")
|
||||
}
|
||||
return ""
|
||||
}(),
|
||||
})
|
||||
}, func(pipe <-chan types.PromotionLinkItem, cancel func(error)) {
|
||||
for i := 0; i < len(links); i++ {
|
||||
item := <-pipe
|
||||
items[i] = item
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "获取推广链接统计数据失败, %+v", err)
|
||||
}
|
||||
|
||||
return &types.GetPromotionLinkListResp{
|
||||
Total: total,
|
||||
Items: items,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/common/ctxdata"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetPromotionStatsHistoryLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetPromotionStatsHistoryLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPromotionStatsHistoryLogic {
|
||||
return &GetPromotionStatsHistoryLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetPromotionStatsHistoryLogic) GetPromotionStatsHistory(req *types.GetPromotionStatsHistoryReq) (resp []types.PromotionStatsHistoryItem, err error) {
|
||||
// 获取当前用户ID
|
||||
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取当前用户ID失败, %+v", getUidErr)
|
||||
}
|
||||
// 构建查询条件
|
||||
builder := l.svcCtx.AdminPromotionLinkStatsHistoryModel.SelectBuilder()
|
||||
|
||||
// 如果有日期范围,添加日期过滤
|
||||
if req.StartDate != "" && req.EndDate != "" {
|
||||
startDate, err := time.Parse("2006-01-02", req.StartDate)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "开始日期格式错误")
|
||||
}
|
||||
endDate, err := time.Parse("2006-01-02", req.EndDate)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "结束日期格式错误")
|
||||
}
|
||||
// 将结束日期设置为当天的最后一刻
|
||||
endDate = endDate.Add(24*time.Hour - time.Second)
|
||||
builder = builder.Where("stats_date BETWEEN ? AND ?", startDate, endDate)
|
||||
}
|
||||
|
||||
// 获取历史统计数据
|
||||
historyStats, err := l.svcCtx.AdminPromotionLinkStatsHistoryModel.FindAll(l.ctx, builder, "stats_date DESC")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取历史统计数据失败")
|
||||
}
|
||||
|
||||
// 转换为响应格式
|
||||
resp = make([]types.PromotionStatsHistoryItem, 0, len(historyStats))
|
||||
for _, stat := range historyStats {
|
||||
// 验证链接是否属于当前用户
|
||||
link, err := l.svcCtx.AdminPromotionLinkModel.FindOne(l.ctx, stat.LinkId)
|
||||
if err != nil {
|
||||
continue // 如果链接不存在,跳过该记录
|
||||
}
|
||||
if link.AdminUserId != adminUserId {
|
||||
continue // 如果链接不属于当前用户,跳过该记录
|
||||
}
|
||||
|
||||
resp = append(resp, types.PromotionStatsHistoryItem{
|
||||
Id: stat.Id,
|
||||
LinkId: stat.LinkId,
|
||||
PayAmount: stat.PayAmount,
|
||||
ClickCount: stat.ClickCount,
|
||||
PayCount: stat.PayCount,
|
||||
StatsDate: stat.StatsDate.Format("2006-01-02"),
|
||||
})
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/app/main/model"
|
||||
"tyc-server/common/ctxdata"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/mr"
|
||||
)
|
||||
|
||||
type GetPromotionStatsTotalLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetPromotionStatsTotalLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetPromotionStatsTotalLogic {
|
||||
return &GetPromotionStatsTotalLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *GetPromotionStatsTotalLogic) GetPromotionStatsTotal(req *types.GetPromotionStatsTotalReq) (resp *types.GetPromotionStatsTotalResp, err error) {
|
||||
// 获取当前用户ID
|
||||
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "获取当前用户ID失败, %+v", getUidErr)
|
||||
}
|
||||
|
||||
// 获取用户的所有推广链接
|
||||
linkBuilder := l.svcCtx.AdminPromotionLinkModel.SelectBuilder()
|
||||
linkBuilder = linkBuilder.Where("admin_user_id = ?", adminUserId)
|
||||
links, err := l.svcCtx.AdminPromotionLinkModel.FindAll(l.ctx, linkBuilder, "")
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取推广链接列表失败, %+v", err)
|
||||
}
|
||||
|
||||
// 如果没有推广链接,返回空统计
|
||||
if len(links) == 0 {
|
||||
return &types.GetPromotionStatsTotalResp{}, nil
|
||||
}
|
||||
|
||||
// 构建链接ID列表
|
||||
linkIds := make([]int64, len(links))
|
||||
for i, link := range links {
|
||||
linkIds[i] = link.Id
|
||||
}
|
||||
|
||||
// 获取并计算总统计数据
|
||||
var totalClickCount, totalPayCount int64
|
||||
var totalPayAmount float64
|
||||
err = mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for _, linkId := range linkIds {
|
||||
source <- linkId
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[struct {
|
||||
ClickCount int64
|
||||
PayCount int64
|
||||
PayAmount float64
|
||||
}], cancel func(error)) {
|
||||
linkId := item.(int64)
|
||||
stats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(l.ctx, linkId)
|
||||
if err != nil && !errors.Is(err, model.ErrNotFound) {
|
||||
cancel(errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取总统计数据失败, linkId: %d, %+v", linkId, err))
|
||||
return
|
||||
}
|
||||
if stats != nil {
|
||||
writer.Write(struct {
|
||||
ClickCount int64
|
||||
PayCount int64
|
||||
PayAmount float64
|
||||
}{
|
||||
ClickCount: stats.ClickCount,
|
||||
PayCount: stats.PayCount,
|
||||
PayAmount: stats.PayAmount,
|
||||
})
|
||||
}
|
||||
}, func(pipe <-chan struct {
|
||||
ClickCount int64
|
||||
PayCount int64
|
||||
PayAmount float64
|
||||
}, cancel func(error)) {
|
||||
for stats := range pipe {
|
||||
totalClickCount += stats.ClickCount
|
||||
totalPayCount += stats.PayCount
|
||||
totalPayAmount += stats.PayAmount
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取总统计数据失败, %+v", err)
|
||||
}
|
||||
|
||||
// 获取今日统计数据
|
||||
today := time.Now().Truncate(24 * time.Hour)
|
||||
var todayClickCount, todayPayCount int64
|
||||
var todayPayAmount float64
|
||||
|
||||
err = mr.MapReduceVoid(func(source chan<- interface{}) {
|
||||
for _, linkId := range linkIds {
|
||||
source <- linkId
|
||||
}
|
||||
}, func(item interface{}, writer mr.Writer[struct {
|
||||
ClickCount int64
|
||||
PayCount int64
|
||||
PayAmount float64
|
||||
}], cancel func(error)) {
|
||||
linkId := item.(int64)
|
||||
builder := l.svcCtx.AdminPromotionLinkStatsHistoryModel.SelectBuilder()
|
||||
builder = builder.Where("link_id = ? AND DATE(stats_date) = DATE(?)", linkId, today)
|
||||
histories, err := l.svcCtx.AdminPromotionLinkStatsHistoryModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
cancel(errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取今日统计数据失败, linkId: %d, %+v", linkId, err))
|
||||
return
|
||||
}
|
||||
|
||||
var clickCount, payCount int64
|
||||
var payAmount float64
|
||||
for _, history := range histories {
|
||||
clickCount += history.ClickCount
|
||||
payCount += history.PayCount
|
||||
payAmount += history.PayAmount
|
||||
}
|
||||
|
||||
writer.Write(struct {
|
||||
ClickCount int64
|
||||
PayCount int64
|
||||
PayAmount float64
|
||||
}{
|
||||
ClickCount: clickCount,
|
||||
PayCount: payCount,
|
||||
PayAmount: payAmount,
|
||||
})
|
||||
}, func(pipe <-chan struct {
|
||||
ClickCount int64
|
||||
PayCount int64
|
||||
PayAmount float64
|
||||
}, cancel func(error)) {
|
||||
for stats := range pipe {
|
||||
todayClickCount += stats.ClickCount
|
||||
todayPayCount += stats.PayCount
|
||||
todayPayAmount += stats.PayAmount
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "获取今日统计数据失败, %+v", err)
|
||||
}
|
||||
|
||||
return &types.GetPromotionStatsTotalResp{
|
||||
TodayClickCount: int64(todayClickCount),
|
||||
TodayPayCount: int64(todayPayCount),
|
||||
TodayPayAmount: todayPayAmount,
|
||||
TotalClickCount: int64(totalClickCount),
|
||||
TotalPayCount: int64(totalPayCount),
|
||||
TotalPayAmount: totalPayAmount,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/app/main/model"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
type RecordLinkClickLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewRecordLinkClickLogic(ctx context.Context, svcCtx *svc.ServiceContext) *RecordLinkClickLogic {
|
||||
return &RecordLinkClickLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *RecordLinkClickLogic) RecordLinkClick(req *types.RecordLinkClickReq) (resp *types.RecordLinkClickResp, err error) {
|
||||
// 校验路径格式
|
||||
if len(req.Path) != 6 {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "无效的推广链接路径")
|
||||
}
|
||||
|
||||
// 检查是否只包含大小写字母和数字
|
||||
for _, char := range req.Path {
|
||||
if !((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || (char >= '0' && char <= '9')) {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "无效的推广链接路径")
|
||||
}
|
||||
}
|
||||
url := fmt.Sprintf("%s/%s", l.svcCtx.Config.AdminPromotion.URLDomain, req.Path)
|
||||
|
||||
link, err := l.svcCtx.AdminPromotionLinkModel.FindOneByUrl(l.ctx, url)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "无效的推广链接路径")
|
||||
}
|
||||
|
||||
err = l.svcCtx.AdminPromotionLinkModel.Trans(l.ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
// 更新总统计
|
||||
totalStats, err := l.svcCtx.AdminPromotionLinkStatsTotalModel.FindOneByLinkId(l.ctx, link.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
totalStats.ClickCount = totalStats.ClickCount + 1
|
||||
err = l.svcCtx.AdminPromotionLinkStatsTotalModel.UpdateWithVersion(l.ctx, session, totalStats)
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新总统计失败%+v", err)
|
||||
}
|
||||
// 更新历史统计
|
||||
today := time.Now().Truncate(24 * time.Hour)
|
||||
builder := l.svcCtx.AdminPromotionLinkStatsHistoryModel.SelectBuilder()
|
||||
builder = builder.Where("link_id = ? AND DATE(stats_date) = DATE(?)", link.Id, today)
|
||||
historyStats, err := l.svcCtx.AdminPromotionLinkStatsHistoryModel.FindAll(l.ctx, builder, "")
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询今日统计记录失败%+v", err)
|
||||
}
|
||||
|
||||
if len(historyStats) == 0 {
|
||||
// 创建今天的记录
|
||||
newStats := &model.AdminPromotionLinkStatsHistory{
|
||||
LinkId: link.Id,
|
||||
StatsDate: today,
|
||||
ClickCount: 1,
|
||||
}
|
||||
_, err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.Insert(l.ctx, session, newStats)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建今日统计记录失败%+v", err)
|
||||
}
|
||||
} else {
|
||||
// 更新今日记录
|
||||
historyStats[0].ClickCount++
|
||||
err = l.svcCtx.AdminPromotionLinkStatsHistoryModel.UpdateWithVersion(l.ctx, session, historyStats[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新历史统计失败%+v", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新总统计失败,%+v", err)
|
||||
}
|
||||
return &types.RecordLinkClickResp{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package admin_promotion
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/common/ctxdata"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type UpdatePromotionLinkLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUpdatePromotionLinkLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdatePromotionLinkLogic {
|
||||
return &UpdatePromotionLinkLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UpdatePromotionLinkLogic) UpdatePromotionLink(req *types.UpdatePromotionLinkReq) error {
|
||||
// 获取当前用户ID
|
||||
adminUserId, getUidErr := ctxdata.GetUidFromCtx(l.ctx)
|
||||
if getUidErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "更新推广链接, 获取用户信息失败, %+v", getUidErr)
|
||||
}
|
||||
|
||||
// 获取链接信息
|
||||
link, err := l.svcCtx.AdminPromotionLinkModel.FindOne(l.ctx, req.Id)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "更新推广链接, 获取链接信息失败, %+v", err)
|
||||
}
|
||||
|
||||
// 验证用户权限
|
||||
if link.AdminUserId != adminUserId {
|
||||
return errors.Wrapf(xerr.NewErrMsg("无权限修改此链接"), "更新推广链接, 无权限修改此链接, %+v", link)
|
||||
}
|
||||
|
||||
// 更新链接信息
|
||||
link.Name = *req.Name
|
||||
link.UpdateTime = time.Now()
|
||||
|
||||
_, err = l.svcCtx.AdminPromotionLinkModel.Update(l.ctx, nil, link)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新推广链接, 更新链接信息失败, %+v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user