migu_server/app/admin/apis/migumanage/migu_admin.go

1169 lines
33 KiB
Go
Raw Normal View History

2024-10-18 15:46:54 +00:00
package migumanage
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-admin-team/go-admin-core/logger"
"github.com/go-admin-team/go-admin-core/sdk/pkg/response"
"go-admin/app/admin/models"
"net/http"
"strings"
"time"
)
// 以下是后台部分接口
// TransactionList 查询交易流水记录
// @Summary 查询交易流水记录
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.TransactionListReq true "查询交易流水记录"
// @Success 200 {object} models.TransactionListResp
// @Router /api/v1/admin/transaction/list [post]
func (e MiGuDeployService) TransactionList(c *gin.Context) {
fmt.Println("TransactionList")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
fmt.Println("MakeContext err:", err)
e.Logger.Error(err)
return
}
req := &models.TransactionListReq{}
if c.ShouldBindJSON(req) != nil {
logger.Errorf("para err")
response.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
resp := models.TransactionListResp{
PageNum: req.PageNum,
}
pageNum := req.PageNum - 1
if pageNum < 0 {
pageNum = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
resp.PageSize = req.PageSize
if e.Orm == nil {
fmt.Println("Orm is nil")
}
var count int64
qs := e.Orm.Model(&models.MgTransactionLog{})
// 手机号码
if req.Phone != "" {
qs = qs.Where("phone_number = ?", req.Phone)
}
// 渠道号
if req.Channel != "" {
qs = qs.Where("channel_code = ?", req.Channel)
}
// 产品编号
if req.SkuCode != 0 {
qs = qs.Where("product_id = ?", req.SkuCode)
}
// 交易结果
if req.Result != "" {
if req.Result == "1" { // 成功
qs = qs.Where("result = ?", "00000")
} else if req.Result == "2" { // 失败
qs = qs.Where("result != ?", "00000")
}
}
// 开始时间和结束时间
if req.StartTime != "" && req.EndTime != "" {
qs = qs.Where("created_at BETWEEN ? AND ?", req.StartTime, req.EndTime)
}
err = qs.Count(&count).Error
if err != nil {
logger.Errorf("count err:", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
var transactionLogs []models.MgTransactionLog
err = qs.Order("created_at desc").Offset(pageNum * req.PageSize).Limit(req.PageSize).Find(&transactionLogs).Error
if err != nil {
logger.Errorf("TransactionList err:%#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
resp.List = transactionLogs
resp.Count = int(count)
e.OK(resp, "")
}
// ProductList 查询权益产品
// @Summary 查询权益产品
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.ProductListReq true "查询权益产品"
// @Success 200 {object} models.ProductListResp
// @Router /api/v1/admin/product/list [post]
func (e MiGuDeployService) ProductList(c *gin.Context) {
fmt.Println("ProductList")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
fmt.Println("MakeContext err:", err)
e.Logger.Error(err)
return
}
req := &models.ProductListReq{}
if c.ShouldBindJSON(req) != nil {
logger.Errorf("para err")
response.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
resp := models.ProductListResp{
PageNum: req.PageNum,
}
pageNum := req.PageNum - 1
if pageNum < 0 {
pageNum = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
resp.PageSize = req.PageSize
if e.Orm == nil {
fmt.Println("Orm is nil")
}
var count int64
qs := e.Orm.Model(&models.MgProduct{})
err = qs.Count(&count).Error
if err != nil {
logger.Errorf("count err:", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
var productList []models.MgProduct
err = qs.Order("id ").Offset(pageNum * req.PageSize).Limit(req.PageSize).Find(&productList).Error
if err != nil {
logger.Errorf("ProductList err:%#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
resp.List = productList
e.OK(resp, "")
}
// ChannelList 查询渠道列表
// @Summary 查询渠道列表
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.ChannelListReq true "查询渠道列表"
// @Success 200 {object} models.ChannelListResp
// @Router /api/v1/admin/channel/list [post]
func (e MiGuDeployService) ChannelList(c *gin.Context) {
fmt.Println("ProductList")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
fmt.Println("MakeContext err:", err)
e.Logger.Error(err)
return
}
req := &models.ChannelListReq{}
if c.ShouldBindJSON(req) != nil {
logger.Errorf("para err")
response.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
resp := models.ChannelListResp{
PageNum: req.PageNum,
}
pageNum := req.PageNum - 1
if pageNum < 0 {
pageNum = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
resp.PageSize = req.PageSize
if e.Orm == nil {
fmt.Println("Orm is nil")
}
var count int64
qs := e.Orm.Model(&models.MgChannel{})
err = qs.Count(&count).Error
if err != nil {
logger.Errorf("count err:", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
var channelList []models.MgChannel
err = qs.Order("id ").Offset(pageNum * req.PageSize).Limit(req.PageSize).Find(&channelList).Error
if err != nil {
logger.Errorf("ProductList err:%#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
resp.List = channelList
e.OK(resp, "")
}
// OrderList 查询订单列表
// @Summary 查询订单列表
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.OrderListReq true "查询订单列表"
// @Success 200 {object} models.OrderListResp
// @Router /api/v1/admin/order/list [post]
func (e MiGuDeployService) OrderList(c *gin.Context) {
fmt.Println("OrderList")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
fmt.Println("MakeContext err:", err)
e.Logger.Error(err)
return
}
req := &models.OrderListReq{}
if c.ShouldBindJSON(req) != nil {
logger.Errorf("para err")
response.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
resp := models.OrderListResp{
PageNum: req.PageNum,
}
pageNum := req.PageNum - 1
if pageNum < 0 {
pageNum = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
resp.PageSize = req.PageSize
if e.Orm == nil {
fmt.Println("Orm is nil")
}
qs := e.Orm.Model(&models.MgOrder{})
// 产品编号
if req.SkuCode != 0 {
qs = qs.Where("product_id = ?", req.SkuCode)
}
// 渠道号
if req.Channel != "" {
qs = qs.Where("channel_code = ?", req.Channel)
}
// 订单流水号
if req.OrderSerial != "" {
qs = qs.Where("order_serial = ?", req.OrderSerial)
}
// 外部订单号
if req.OutTradeNo != "" {
qs = qs.Where("external_order_id = ?", req.OutTradeNo)
}
// 渠道订单号
if req.ChannelTradeNo != "" {
qs = qs.Where("channel_trade_no = ?", req.ChannelTradeNo)
}
// 手机号码
if req.Phone != "" {
qs = qs.Where("phone_number = ?", req.Phone)
}
// SM4加密手机号
if req.SM4PhoneNumber != "" {
qs = qs.Where("sm4_phone_number = ?", req.SM4PhoneNumber)
}
// 退订状态
if req.State != 0 {
state := 1 // 订阅
if req.State == 1 {
state = 2 // 退订
}
qs = qs.Where("state = ?", state)
}
// 开始时间和结束时间
if req.StartTime != "" && req.EndTime != "" {
qs = qs.Where("subscribe_time BETWEEN ? AND ?", req.StartTime, req.EndTime)
}
var count int64
err = qs.Count(&count).Error
if err != nil {
logger.Errorf("count err:", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
var orderList []models.MgOrder
err = qs.Order("created_at desc").Offset(pageNum * req.PageSize).Limit(req.PageSize).Find(&orderList).Error
2024-10-18 15:46:54 +00:00
if err != nil {
logger.Errorf("OrderList err:%#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
resp.List = orderList
resp.Count = int(count)
e.OK(resp, "")
}
// HistoricalSummaryList 历史汇总查询
// @Summary 历史汇总查询
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.HistoricalSummaryListReq true "历史汇总查询"
// @Success 200 {object} models.HistoricalSummaryListResp
// @Router /api/v1/admin/historical_summary/list [post]
func (e MiGuDeployService) HistoricalSummaryList(c *gin.Context) {
fmt.Println("HistoricalSummaryList")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
fmt.Println("MakeContext err:", err)
e.Logger.Error(err)
return
}
// 请求参数绑定
req := &models.HistoricalSummaryListReq{}
if c.ShouldBindJSON(req) != nil {
logger.Errorf("para err")
response.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
resp := models.HistoricalSummaryListResp{
PageNum: req.PageNum,
}
// 分页处理
pageNum := req.PageNum - 1
if pageNum < 0 {
pageNum = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
resp.PageSize = req.PageSize
if e.Orm == nil {
fmt.Println("Orm is nil")
}
// 构建查询
var historicalSummaryList []models.MgHistoricalSummary
var count int64
// 设置开始时间和结束时间
startTime := req.StartTime
endTime := req.EndTime
if startTime == "" {
startTime = "1970-01-01 00:00:00"
}
if endTime == "" {
endTime = time.Now().Format("2006-01-02 15:04:05")
}
// 使用左连接查询
qs := e.Orm.Model(&models.MgOrder{}).
Select(`DATE_FORMAT(mg_order.subscribe_time, '%Y-%m-%d') AS date,
mg_order.product_id,
mg_order.channel_code,
(SELECT COUNT(*)
FROM mg_transaction_log
WHERE verification_code != ''
AND channel_code = mg_order.channel_code
AND DATE(created_at) = DATE(mg_order.subscribe_time)) AS submission_count,
COUNT(CASE WHEN mg_order.is_one_hour_cancel = 1 THEN 1 END) AS new_user_unsub_within_hour,
COUNT(CASE WHEN mg_order.state = 2 THEN 1 END) AS new_user_unsub_on_day,
COUNT(*) AS new_user_count,
CONCAT(ROUND(COUNT(CASE WHEN mg_order.is_one_hour_cancel = 1 THEN 1 END) * 100.0 /
NULLIF(COUNT(*), 0), 2), '%') AS new_user_unsub_within_hour_rate,
CONCAT(ROUND(COUNT(CASE WHEN mg_order.state = 2 THEN 1 END) * 100.0 /
NULLIF(COUNT(*), 0), 2), '%') AS new_user_unsub_on_day_rate`).
2024-10-18 15:46:54 +00:00
Where("mg_order.subscribe_time >= ? AND mg_order.subscribe_time <= ?", startTime, endTime).
Group("DATE(mg_order.subscribe_time), mg_order.product_id, mg_order.channel_code").
Order("mg_order.product_id, mg_order.channel_code, mg_order.subscribe_time").
Offset(pageNum * req.PageSize).
Limit(req.PageSize)
// 添加过滤条件
if req.SkuCode != 0 {
qs = qs.Where("product_id = ?", req.SkuCode)
}
if req.Channel != "" {
qs = qs.Where("channel_code = ?", req.Channel)
}
// 查询总记录数
err = qs.Count(&count).Error
if err != nil {
logger.Errorf("count err: %#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
// 执行查询
err = qs.Find(&historicalSummaryList).Error
if err != nil {
logger.Errorf("HistoricalSummaryList query err: %#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
// 返回结果
resp.List = historicalSummaryList
resp.Count = int(count)
e.OK(resp, "")
}
// RealtimeSummaryList 当日实时汇总
// @Summary 当日实时汇总
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.RealtimeSummaryListReq true "当日实时汇总"
// @Success 200 {object} models.RealtimeSummaryListResp
// @Router /api/v1/admin/realtime_summary/list [post]
func (e MiGuDeployService) RealtimeSummaryList(c *gin.Context) {
fmt.Println("RealtimeSummaryList")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
fmt.Println("MakeContext err:", err)
e.Logger.Error(err)
return
}
req := &models.RealtimeSummaryListReq{}
if c.ShouldBindJSON(req) != nil {
logger.Errorf("para err")
response.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
resp := models.RealtimeSummaryListResp{
PageNum: req.PageNum,
}
// 分页处理
pageNum := req.PageNum - 1
if pageNum < 0 {
pageNum = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
resp.PageSize = req.PageSize
if e.Orm == nil {
fmt.Println("Orm is nil")
}
// 获取当天的起始时间和结束时间
today := time.Now().Format("2006-01-02") // 获取当天日期字符串 "yyyy-MM-dd"
startDate := today + " 00:00:00"
endDate := today + " 23:59:59"
// 汇总查询,包括当日新增用户数
var count int64
var realtimeSummaryList []models.MgRealtimeSummary
qs := e.Orm.Model(&models.MgOrder{}).
Select(`product_id, channel_code,
(SELECT COUNT(*)
FROM mg_transaction_log
WHERE verification_code != ''
AND channel_code = mg_order.channel_code
AND created_at >= ?
AND created_at <= ?) AS submission_count,
COUNT(CASE WHEN mg_order.is_one_hour_cancel = 1 THEN 1 END) AS new_user_unsub_within_hour,
COUNT(CASE WHEN mg_order.state = 2 THEN 1 END) AS new_user_unsub_on_day,
COUNT(CASE WHEN mg_order.created_at >= ? AND mg_order.created_at <= ? THEN 1 END) AS new_user_count,
CONCAT(ROUND(COUNT(CASE WHEN mg_order.is_one_hour_cancel = 1 THEN 1 END) * 100.0 /
NULLIF(COUNT(CASE WHEN mg_order.created_at >= ? AND mg_order.created_at <= ? THEN 1 END), 0), 2), '%') AS new_user_unsub_within_hour_rate,
CONCAT(ROUND(COUNT(CASE WHEN mg_order.state = 2 THEN 1 END) * 100.0 /
NULLIF(COUNT(CASE WHEN mg_order.created_at >= ? AND mg_order.created_at <= ? THEN 1 END), 0), 2), '%') AS new_user_unsub_on_day_rate`,
startDate, endDate, startDate, endDate, startDate, endDate, startDate, endDate).
2024-10-18 15:46:54 +00:00
Where("mg_order.created_at >= ? AND mg_order.created_at <= ?", startDate, endDate).
Group("product_id, channel_code").
Order("product_id, channel_code").
Offset(pageNum * req.PageSize).Limit(req.PageSize)
// 产品编号
if req.SkuCode != 0 {
qs = qs.Where("product_id = ?", req.SkuCode)
}
// 渠道号
if req.Channel != "" {
qs = qs.Where("channel_code = ?", req.Channel)
}
// 获取数据
err = qs.Find(&realtimeSummaryList).Error
if err != nil {
logger.Errorf("RealtimeSummaryList query err: %#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
// 查询总记录数
err = e.Orm.Model(&models.MgOrder{}).Count(&count).Error
if err != nil {
logger.Errorf("count err: %#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
// 返回数据
resp.List = realtimeSummaryList
resp.Count = int(count)
e.OK(resp, "")
}
// UserRetentionList 用户留存记录
// @Summary 用户留存记录
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.UserRetentionListReq true "用户留存记录"
// @Success 200 {object} models.UserRetentionListResp
// @Router /api/v1/admin/user_retention/list [post]
func (e MiGuDeployService) UserRetentionList(c *gin.Context) {
// 创建上下文和 ORM
ctx := e.MakeContext(c)
if err := ctx.MakeOrm().Errors; err != nil {
e.Logger.Error(err)
response.Error(c, http.StatusInternalServerError, err, "数据库错误")
return
}
// 解析请求参数
req := &models.UserRetentionListReq{}
if err := c.ShouldBindJSON(req); err != nil {
response.Error(c, http.StatusBadRequest, err, "参数错误")
return
}
resp := models.UserRetentionListResp{
PageNum: req.PageNum,
}
// 分页处理
pageNum := req.PageNum - 1
if pageNum < 0 {
pageNum = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
resp.PageSize = req.PageSize
// 获取开始和结束时间
retentionMonth := req.RetentionMonth // 例如 "2024-10"
if retentionMonth == "" {
retentionMonth = time.Now().Format("2006-01")
}
// 解析出年和月
year, month, err := models.ParseYearMonth(retentionMonth)
if err != nil {
response.Error(c, http.StatusBadRequest, err, "日期格式错误")
return
}
// 计算该月份的第一天和最后一天
startDate := time.Date(year, month, 1, 0, 0, 0, 0, time.UTC) // 当月第一天
endDate := startDate.AddDate(0, 1, 0).Add(-time.Second) // 当月最后一天的最后一毫秒
// 定义返回的响应结构
var retentionList []models.MgUserRetention
// 查询用户留存数据
qs := e.Orm.Model(&models.MgOrder{}).
2024-10-18 15:46:54 +00:00
Select(`DATE_FORMAT(created_at, '%Y-%m') AS retention_month,
COUNT(DISTINCT phone_number) AS new_user_count,
COUNT(DISTINCT CASE WHEN state != 2 THEN phone_number END) AS retained_user_count,
channel_code, product_id,
IFNULL(FORMAT(COUNT(DISTINCT CASE WHEN state != 2 THEN phone_number END) / NULLIF(COUNT(DISTINCT phone_number), 0) * 100, 2), 0) AS retention_rate`).
Where("created_at >= ? AND created_at <= ?", startDate, endDate)
// 处理产品编号 (SkuCode) 过滤条件
if req.SkuCode != "" {
qs = qs.Where("product_id = ?", req.SkuCode)
}
// 处理渠道名称 (Channel) 过滤条件
if req.Channel != "" {
qs = qs.Where("channel_code = ?", req.Channel)
}
err = qs.Group("retention_month, channel_code, product_id").
2024-10-18 15:46:54 +00:00
Order("retention_month").
Find(&retentionList).Error
if err != nil {
logger.Errorf("DailyData query err: %#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
2024-10-18 15:46:54 +00:00
if err != nil {
e.Logger.Errorf("UserRetentionList query error: %#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
for i := range retentionList {
retentionList[i].RetentionRate += "%"
}
resp.List = retentionList
resp.Count = len(retentionList) // Count should reflect the actual number of entries
// 返回结果
response.OK(c, resp, "成功")
}
// SysChannelList 查询系统渠道编码
// @Summary 查询系统渠道编码
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.SysChannelListReq true "查询系统渠道编码"
// @Success 200 {object} models.SysChannelListResp
// @Router /api/v1/admin/sys_channel/list [post]
func (e MiGuDeployService) SysChannelList(c *gin.Context) {
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
e.Logger.Error(err)
response.Error(c, http.StatusInternalServerError, err, "数据库连接失败")
return
}
req := &models.SysChannelListReq{}
if c.ShouldBindJSON(req) != nil {
e.Logger.Error("参数解析失败")
response.Error(c, http.StatusBadRequest, errors.New("参数错误"), "参数错误")
return
}
resp := models.SysChannelListResp{
PageNum: req.PageNum,
PageSize: req.PageSize,
}
pageNum := req.PageNum - 1
if pageNum < 0 {
pageNum = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
var channels []models.ChannelData
// 1. 查询mg_product表的ChannelCode
var productChannelCodes []string
var mgOrders []models.MgProduct
err = e.Orm.Model(&models.MgProduct{}).
Select("channel_api_id").
Find(&mgOrders).Error
if err != nil {
e.Logger.Error("查询产品渠道编码失败:", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
// 2. 拆分逗号分隔的ChannelCode并去除空白
for _, order := range mgOrders {
channelCodes := strings.Split(order.ChannelApiID, ",")
for _, code := range channelCodes {
code = strings.TrimSpace(code) // 去掉前后空白
if code != "" {
productChannelCodes = append(productChannelCodes, code)
}
}
}
// 3. 查询mg_channel表的MainChannelCode和SubChannelCode
var mgChannels []models.MgChannel
err = e.Orm.Model(&models.MgChannel{}).
Select("main_channel_code, sub_channel_code").
Find(&mgChannels).Error
if err != nil {
e.Logger.Error("查询渠道列表失败:", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
// 4. 构建所有组合,并剔除重复的渠道编码
channelSet := make(map[string]struct{})
// 添加产品表中的渠道编码
for _, code := range productChannelCodes {
channelSet[code] = struct{}{}
}
// 添加渠道表中的主渠道和子渠道编码
for _, channel := range mgChannels {
channelSet[channel.MainChannelCode] = struct{}{}
channelSet[channel.SubChannelCode] = struct{}{}
}
// 5. 转换为ChannelData结构体并分页
for code := range channelSet {
if code != "" { // 跳过空的channel_code
channels = append(channels, models.ChannelData{
ChannelCode: code,
})
}
}
// 总数
resp.Count = len(channels)
// 分页
startIndex := pageNum * req.PageSize
endIndex := startIndex + req.PageSize
if startIndex > len(channels) {
startIndex = len(channels)
}
if endIndex > len(channels) {
endIndex = len(channels)
}
resp.List = channels[startIndex:endIndex]
e.OK(resp, "")
}
// HomepageDataSummary 查询首页汇总数据
// @Summary 查询首页汇总数据
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.HomepageDataSummaryReq true "查询首页汇总数据"
// @Success 200 {object} models.HomepageDataSummaryResp
// @Router /api/v1/admin/home/data [post]
func (e MiGuDeployService) HomepageDataSummary(c *gin.Context) {
fmt.Println("HomepageDataSummary")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
fmt.Println("MakeContext err:", err)
e.Logger.Error(err)
return
}
// 请求参数绑定
req := &models.HomepageDataSummaryReq{}
if c.ShouldBindJSON(req) != nil {
logger.Errorf("para err")
response.Error(c, http.StatusBadRequest, errors.New("para err"), "参数错误")
return
}
resp := models.HomepageDataSummaryResp{}
var summaryData models.SummaryData
// 设置开始时间和结束时间
startTime := req.StartTime
endTime := req.EndTime
if startTime == "" {
startTime = "1970-01-01 00:00:00"
}
if endTime == "" {
endTime = time.Now().Format("2006-01-02 15:04:05")
}
// 查询汇总数据
qs := e.Orm.Model(&models.MgOrder{}).
Select(`COUNT(*) AS total_user_count,
COUNT(CASE WHEN state = 2 THEN 1 END) AS unsubscribed_user_count,
COUNT(CASE WHEN is_one_hour_cancel = 1 THEN 1 END) AS one_hour_unsubscribed_user_count`).
Where("subscribe_time >= ? AND subscribe_time <= ?", startTime, endTime)
// 添加产品和渠道的过滤条件
if req.SkuCode != "" {
qs = qs.Where("product_id = ?", req.SkuCode)
}
if req.Channel != "" {
qs = qs.Where("channel_code = ?", req.Channel)
}
err = qs.Find(&summaryData).Error
if err != nil {
logger.Errorf("HomepageDataSummary query err: %#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
// 计算留存用户数
summaryData.RetainedUserCount = summaryData.TotalUserCount - summaryData.UnsubscribedUserCount
// 计算留存率
if summaryData.TotalUserCount > 0 {
summaryData.RetentionRate = fmt.Sprintf("%.2f%%", float64(summaryData.RetainedUserCount)*100.0/float64(summaryData.TotalUserCount))
} else {
summaryData.RetentionRate = "0.00%"
}
// 计算退订率
if summaryData.TotalUserCount > 0 {
summaryData.UnsubscribeRate = fmt.Sprintf("%.2f%%", float64(summaryData.UnsubscribedUserCount)*100.0/float64(summaryData.TotalUserCount))
summaryData.OneHourUnsubscribeRate = fmt.Sprintf("%.2f%%", float64(summaryData.OneHourUnsubscribedUserCount)*100.0/float64(summaryData.TotalUserCount))
} else {
summaryData.UnsubscribeRate = "0.00%"
summaryData.OneHourUnsubscribeRate = "0.00%"
}
resp.Summary = summaryData
// 查询每日数据
var dailyDataList []models.DailyData
dailyQuery := e.Orm.Model(&models.MgOrder{}).
Select(`DATE(subscribe_time) AS date,
COUNT(*) AS new_user_count,
COUNT(CASE WHEN state = 2 THEN 1 END) AS unsubscribed_user_count,
COUNT(CASE WHEN is_one_hour_cancel = 1 THEN 1 END) AS unsubscribed_within_one_hour`).
Where("subscribe_time >= ? AND subscribe_time <= ?", startTime, endTime)
// 处理产品编号 (SkuCode) 过滤条件
if req.SkuCode != "" {
dailyQuery = dailyQuery.Where("product_id = ?", req.SkuCode)
}
// 处理渠道名称 (Channel) 过滤条件
if req.Channel != "" {
dailyQuery = dailyQuery.Where("channel_code = ?", req.Channel)
}
err = dailyQuery.Group("DATE(subscribe_time)").
Order("DATE(subscribe_time)").
Find(&dailyDataList).Error
if err != nil {
logger.Errorf("DailyData query err: %#v", err)
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
// 计算每日退订率
for i := range dailyDataList {
if dailyDataList[i].NewUserCount > 0 {
dailyDataList[i].UnsubscribeRate = fmt.Sprintf("%.2f%%", float64(dailyDataList[i].UnsubscribedUserCount)*100.0/float64(dailyDataList[i].NewUserCount))
dailyDataList[i].UnsubscribeWithinOneHourRate = fmt.Sprintf("%.2f%%", float64(dailyDataList[i].UnsubscribedWithinOneHour)*100.0/float64(dailyDataList[i].NewUserCount))
} else {
dailyDataList[i].UnsubscribeRate = "0.00%"
dailyDataList[i].UnsubscribeWithinOneHourRate = "0.00%"
}
}
resp.DailyDataList = dailyDataList
// 返回结果
e.OK(resp, "")
}
2024-10-18 15:46:54 +00:00
// AddChannel 新增渠道
// @Summary 新增渠道
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.AddChannelReq true "新增渠道"
// @Success 200 {object} models.AddChannelResp
// @Router /api/v1/admin/channel/add [post]
func (e MiGuDeployService) AddChannel(c *gin.Context) {
fmt.Println("AddChannel called")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
e.Logger.Error(err)
response.Error(c, http.StatusInternalServerError, err, "创建上下文失败")
return
}
req := &models.AddChannelReq{}
if err := c.ShouldBindJSON(req); err != nil {
e.Logger.Errorf("para err: %v", err)
response.Error(c, http.StatusBadRequest, errors.New("参数错误"), "参数错误")
return
}
// Validate required fields
if req.ProductID == 0 || req.MainChannelCode == "" || req.SubscribeURL == "" {
response.Error(c, http.StatusBadRequest, errors.New("必填字段缺失"), "必填字段缺失")
return
}
channel := models.MgChannel{
ProductID: req.ProductID,
MainChannelCode: req.MainChannelCode,
SubChannelCode: req.SubChannelCode,
SubscribeURL: req.SubscribeURL,
UnsubscribeURL: req.UnsubscribeURL,
Status: req.Status,
Remarks: req.Remarks,
}
if err := e.Orm.Create(&channel).Error; err != nil {
e.Logger.Errorf("create channel err: %v", err)
response.Error(c, http.StatusInternalServerError, err, "创建失败")
return
}
resp := models.AddChannelResp{ChannelID: channel.ID}
e.OK(resp, "渠道创建成功")
}
//// UpdateChannel 更新渠道
//// @Summary 更新渠道
//// @Tags 2024-咪咕-管理后台
//// @Produce json
//// @Accept json
//// @Param request body models.UpdateChannelReq true "更新渠道请求"
//// @Success 200 {object} models.UpdateChannelResp
//// @Router /api/v1/admin/channel/update [post]
//func (e MiGuDeployService) UpdateChannel(c *gin.Context) {
// fmt.Println("UpdateChannel called")
// err := e.MakeContext(c).MakeOrm().Errors
// if err != nil {
// e.Logger.Error(err)
// response.Error(c, http.StatusInternalServerError, err, "创建上下文失败")
// return
// }
//
// req := &models.UpdateChannelReq{}
// if err := c.ShouldBindJSON(req); err != nil {
// e.Logger.Errorf("para err: %v", err)
// response.Error(c, http.StatusBadRequest, errors.New("参数错误"), "参数错误")
// return
// }
//
// // Validate required fields
// if req.ID == 0 {
// response.Error(c, http.StatusBadRequest, errors.New("必填字段缺失"), "必填字段缺失")
// return
// }
//
// channel := models.MgChannel{}
// if err := e.Orm.First(&channel, req.ID).Error; err != nil {
// e.Logger.Errorf("channel not found err: %v", err)
// response.Error(c, http.StatusNotFound, err, "渠道未找到")
// return
// }
//
// // Update fields
// if req.MainChannelCode != "" {
// channel.MainChannelCode = req.MainChannelCode
// }
// if req.SubChannelCode != "" {
// channel.SubChannelCode = req.SubChannelCode
// }
// if req.SubscribeURL != "" {
// channel.SubscribeURL = req.SubscribeURL
// }
// if req.UnsubscribeURL != "" {
// channel.UnsubscribeURL = req.UnsubscribeURL
// }
// if req.Status != 0 {
// channel.Status = req.Status
// }
// channel.Remarks = req.Remarks
//
// if err := e.Orm.Save(&channel).Error; err != nil {
// e.Logger.Errorf("update channel err: %v", err)
// response.Error(c, http.StatusInternalServerError, err, "更新失败")
// return
// }
//
// e.OK(nil, "渠道更新成功")
//}
//
//// DeleteChannel 删除渠道
//// @Summary 删除渠道
//// @Tags 2024-咪咕-管理后台
//// @Produce json
//// @Accept json
//// @Param request body models.DeleteChannelReq true "删除渠道请求"
//// @Success 200 {object} models.DeleteChannelResp
//// @Router /api/v1/admin/channel/delete [post]
//func (e MiGuDeployService) DeleteChannel(c *gin.Context) {
// fmt.Println("DeleteChannel called")
// err := e.MakeContext(c).MakeOrm().Errors
// if err != nil {
// e.Logger.Error(err)
// response.Error(c, http.StatusInternalServerError, err, "创建上下文失败")
// return
// }
//
// req := &models.DeleteChannelReq{}
// if err := c.ShouldBindJSON(req); err != nil {
// e.Logger.Errorf("para err: %v", err)
// response.Error(c, http.StatusBadRequest, errors.New("参数错误"), "参数错误")
// return
// }
//
// // Validate required fields
// if req.ID == 0 {
// response.Error(c, http.StatusBadRequest, errors.New("必填字段缺失"), "必填字段缺失")
// return
// }
//
// if err := e.Orm.Delete(&models.MgChannel{}, req.ID).Error; err != nil {
// e.Logger.Errorf("delete channel err: %v", err)
// response.Error(c, http.StatusInternalServerError, err, "删除失败")
// return
// }
//
// e.OK(nil, "渠道删除成功")
//}
// AddProduct 添加新产品
// @Summary 添加新产品
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.AddProductReq true "新增产品"
// @Success 200 {object} models.AddProductResp
// @Router /api/v1/admin/product/add [post]
func (e MiGuDeployService) AddProduct(c *gin.Context) {
fmt.Println("AddProduct")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
e.Logger.Error(err)
response.Error(c, http.StatusInternalServerError, err, "创建上下文失败")
return
}
req := &models.AddProductReq{}
if err := c.ShouldBindJSON(req); err != nil {
logger.Errorf("para err")
response.Error(c, http.StatusBadRequest, errors.New("参数错误"), "参数错误")
return
}
product := models.MgProduct{
Name: req.Name,
UniqueCode: req.UniqueCode,
SkuName: req.SkuName,
BillingPointID: req.BillingPointID,
ChannelCode: req.ChannelCode,
ProductApiID: req.ProductApiID,
ChannelApiID: req.ChannelApiID,
OfficialPage: req.OfficialPage,
}
if err := e.Orm.Create(&product).Error; err != nil {
logger.Errorf("AddProduct err:%#v", err)
response.Error(c, http.StatusInternalServerError, err, "创建失败")
return
}
e.OK(models.AddProductResp{ID: product.ID}, "创建成功")
}
//// UpdateProduct 修改产品
//// @Summary 修改产品
//// @Tags 2024-咪咕-管理后台
//// @Produce json
//// @Accept json
//// @Param request body models.UpdateProductReq true "修改产品"
//// @Success 200 {object} models.UpdateProductResp
//// @Router /api/v1/admin/product/update [post]
//func (e MiGuDeployService) UpdateProduct(c *gin.Context) {
// fmt.Println("UpdateProduct")
// err := e.MakeContext(c).MakeOrm().Errors
// if err != nil {
// e.Logger.Error(err)
// response.Error(c, http.StatusInternalServerError, err, "创建上下文失败")
// return
// }
//
// req := &models.UpdateProductReq{}
// if err := c.ShouldBindJSON(req); err != nil {
// logger.Errorf("para err")
// response.Error(c, http.StatusBadRequest, errors.New("参数错误"), "参数错误")
// return
// }
//
// product := models.MgProduct{}
// if err := e.Orm.First(&product, req.ID).Error; err != nil {
// logger.Errorf("Product not found: %#v", err)
// response.Error(c, http.StatusNotFound, err, "产品未找到")
// return
// }
//
// // 更新产品信息
// product.Name = req.Name
// product.UniqueCode = req.UniqueCode
// product.SkuName = req.SkuName
// product.BillingPointID = req.BillingPointID
// product.ChannelCode = req.ChannelCode
// product.ProductApiID = req.ProductApiID
// product.ChannelApiID = req.ChannelApiID
// product.OfficialPage = req.OfficialPage
//
// if err := e.Orm.Save(&product).Error; err != nil {
// logger.Errorf("UpdateProduct err:%#v", err)
// response.Error(c, http.StatusInternalServerError, err, "更新失败")
// return
// }
//
// e.OK(models.UpdateProductResp{ID: product.ID}, "更新成功")
//}
//
//// DeleteProduct 删除产品
//// @Summary 删除产品
//// @Tags 2024-咪咕-管理后台
//// @Produce json
//// @Accept json
//// @Param request body models.DeleteProductReq true "删除产品"
//// @Success 200 {object} nil
//// @Router /api/v1/admin/product/delete [post]
//func (e MiGuDeployService) DeleteProduct(c *gin.Context) {
// fmt.Println("DeleteProduct")
// err := e.MakeContext(c).MakeOrm().Errors
// if err != nil {
// e.Logger.Error(err)
// response.Error(c, http.StatusInternalServerError, err, "创建上下文失败")
// return
// }
//
// req := &models.DeleteProductReq{}
// if err := c.ShouldBindJSON(req); err != nil {
// logger.Errorf("para err")
// response.Error(c, http.StatusBadRequest, errors.New("参数错误"), "参数错误")
// return
// }
//
// if err := e.Orm.Delete(&models.MgProduct{}, req.ID).Error; err != nil {
// logger.Errorf("DeleteProduct err:%#v", err)
// response.Error(c, http.StatusInternalServerError, err, "删除失败")
// return
// }
//
// e.OK("", "删除成功")
//}