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 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`). 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). 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{}). 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"). Order("retention_month"). Find(&retentionList).Error if err != nil { logger.Errorf("DailyData query err: %#v", err) response.Error(c, http.StatusInternalServerError, err, "查询失败") return } 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, "") } // 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("", "删除成功") //}