Compare commits

...

2 Commits

Author SHA1 Message Date
ff4b3a7b54 1.新增营收分析接口; 2024-10-23 13:51:56 +08:00
f8e1a6e92d 1.注释对外提供的接口,只保留后台相关接口;
2.添加定时任务,每晚01:30查询现有用户是否有退订情况;
2024-10-22 17:59:50 +08:00
8 changed files with 388 additions and 132 deletions

View File

@ -55,5 +55,5 @@ deploy:
dev: dev:
GOOS=linux GOARCH=amd64 go build -o temp_migu_server main.go GOOS=linux GOARCH=amd64 go build -o temp_migu_admin_server main.go

View File

@ -807,8 +807,8 @@ func (e MiGuDeployService) HomepageDataSummary(c *gin.Context) {
Where("subscribe_time >= ? AND subscribe_time <= ?", startTime, endTime) Where("subscribe_time >= ? AND subscribe_time <= ?", startTime, endTime)
// 添加产品和渠道的过滤条件 // 添加产品和渠道的过滤条件
if req.SkuCode != "" { if req.ProductID != 0 {
qs = qs.Where("product_id = ?", req.SkuCode) qs = qs.Where("product_id = ?", req.ProductID)
} }
if req.Channel != "" { if req.Channel != "" {
qs = qs.Where("channel_code = ?", req.Channel) qs = qs.Where("channel_code = ?", req.Channel)
@ -852,8 +852,8 @@ func (e MiGuDeployService) HomepageDataSummary(c *gin.Context) {
Where("subscribe_time >= ? AND subscribe_time <= ?", startTime, endTime) Where("subscribe_time >= ? AND subscribe_time <= ?", startTime, endTime)
// 处理产品编号 (SkuCode) 过滤条件 // 处理产品编号 (SkuCode) 过滤条件
if req.SkuCode != "" { if req.ProductID != 0 {
dailyQuery = dailyQuery.Where("product_id = ?", req.SkuCode) dailyQuery = dailyQuery.Where("product_id = ?", req.ProductID)
} }
// 处理渠道名称 (Channel) 过滤条件 // 处理渠道名称 (Channel) 过滤条件
@ -887,6 +887,77 @@ func (e MiGuDeployService) HomepageDataSummary(c *gin.Context) {
e.OK(resp, "") e.OK(resp, "")
} }
// CalculateRevenueAnalysis 营收分析
// @Summary 营收分析
// @Tags 2024-咪咕-管理后台
// @Produce json
// @Accept json
// @Param request body models.RetentionMonthsReq true "营收分析"
// @Success 200 {object} models.RetentionMonthsResp
// @Router /api/v1/admin/home/revenue_analysis [post]
func (e MiGuDeployService) CalculateRevenueAnalysis(c *gin.Context) {
fmt.Println("CalculateRevenueAnalysis")
err := e.MakeContext(c).MakeOrm().Errors
if err != nil {
fmt.Println("MakeContext err:", err)
e.Logger.Error(err)
return
}
// 绑定请求参数
req := &models.RetentionMonthsReq{}
if err := c.ShouldBindJSON(req); err != nil {
response.Error(c, http.StatusBadRequest, err, "参数错误")
return
}
// 构建查询
query := e.Orm.Model(&models.MgOrder{})
// 新用户数的查询条件:当月的所有数据(包含订购和退订,不用区分)
if req.StartTime != "" && req.EndTime != "" {
query = query.Where("subscribe_time >= ? AND subscribe_time <= ?", req.StartTime, req.EndTime)
}
if req.ProductID != 0 {
query = query.Where("product_id = ?", req.ProductID)
}
if req.Channel != "" {
query = query.Where("channel_code = ?", req.Channel)
}
// 统计每个月的新用户数和当月有效用户数
var result []models.MonthlyRetention
err = query.
Select(`DATE_FORMAT(subscribe_time, '%Y-%m') AS month,
COUNT(DISTINCT phone_number) AS new_user_count,
COUNT(DISTINCT CASE
WHEN unsubscribe_time IS NULL OR TIMESTAMPDIFF(HOUR, subscribe_time, unsubscribe_time) > 24
THEN phone_number END) AS valid_users_count`).
Group("month").
Order("month").
Find(&result).Error
if err != nil {
response.Error(c, http.StatusInternalServerError, err, "查询失败")
return
}
// 计算总的新用户数
totalNewUserCount := 0
totalValidUsers := 0
for _, data := range result {
totalNewUserCount += data.NewUserCount
totalValidUsers += data.ValidUsersCount
}
// 返回结果
resp := models.RetentionMonthsResp{
TotalNewUsers: totalNewUserCount, // 添加总新用户数
TotalValidUsers: totalValidUsers, // 添加有效用户数
MonthlyRetentionData: result,
}
response.OK(c, resp, "查询成功")
}
// AddChannel 新增渠道 // AddChannel 新增渠道
// @Summary 新增渠道 // @Summary 新增渠道
// @Tags 2024-咪咕-管理后台 // @Tags 2024-咪咕-管理后台

View File

@ -492,7 +492,7 @@ type DeleteChannelResp struct {
type HomepageDataSummaryReq struct { type HomepageDataSummaryReq struct {
StartTime string `json:"start_time"` // 查询开始时间 StartTime string `json:"start_time"` // 查询开始时间
EndTime string `json:"end_time"` // 查询结束时间 EndTime string `json:"end_time"` // 查询结束时间
SkuCode string `json:"sku_code"` // 产品编号 ProductID int `json:"product_id"` // 产品ID
Channel string `json:"channel"` // 渠道名称 Channel string `json:"channel"` // 渠道名称
} }
@ -521,6 +521,28 @@ type SummaryData struct {
RetentionRate string `json:"retention_rate"` // 留存率 (字符串,保留两位小数,如 "73.65%") RetentionRate string `json:"retention_rate"` // 留存率 (字符串,保留两位小数,如 "73.65%")
} }
// RetentionMonthsReq 查询用户留存月份的请求结构体
type RetentionMonthsReq struct {
StartTime string `json:"start_time"` // 查询开始时间
EndTime string `json:"end_time"` // 查询结束时间
ProductID int `json:"product_id"` // 产品ID
Channel string `json:"channel"` // 渠道名称
}
// MonthlyRetention 表示每月留存数据
type MonthlyRetention struct {
Month string `json:"month"` // 年月
NewUserCount int `json:"new_user_count"` // 新用户数
ValidUsersCount int `json:"valid_users_count"` // 当月有效用户数
}
// RetentionMonthsResp 表示响应参数结构体
type RetentionMonthsResp struct {
TotalNewUsers int `json:"total_new_users"` // 总新用户数
TotalValidUsers int `json:"total_valid_users"` // 总有效用户数
MonthlyRetentionData []MonthlyRetention `json:"monthly_retention_data"` // 每月留存数据
}
// MiGuCaptchaRequest 调用下单接口(森越转发) // MiGuCaptchaRequest 调用下单接口(森越转发)
func MiGuCaptchaRequest(r *MiGuSendCaptchaReq) (MiGuRsp, error) { func MiGuCaptchaRequest(r *MiGuSendCaptchaReq) (MiGuRsp, error) {
var miGuResp MiGuRsp var miGuResp MiGuRsp
@ -888,8 +910,8 @@ func GetChannelInfoByChannelCode(channel string, db *gorm.DB) (MgChannel, error)
return channelInfo, nil return channelInfo, nil
} }
// CheckOrderState 定时任务检查订单状态是否有1小时退订 // CheckAllOrderState 定时任务,检查历史订阅用户有无退订
func CheckOrderState() { func CheckAllOrderState() {
if database.Db == nil { if database.Db == nil {
log.Println("Database connection is nil") log.Println("Database connection is nil")
fmt.Println("Database connection is nil") fmt.Println("Database connection is nil")
@ -899,12 +921,7 @@ func CheckOrderState() {
// 查询订单列表中未退订的用户查询其是否退订如果退订则更新退订时间判断是否为1小时内退订 // 查询订单列表中未退订的用户查询其是否退订如果退订则更新退订时间判断是否为1小时内退订
var orderList []MgOrder var orderList []MgOrder
// 获取当前时间前2个小时 err := database.Db.Where("state = 1").Order("created_at desc").
oneHourAgo := time.Now().Add(-2 * time.Hour)
err := database.Db.Where("is_one_hour_cancel != 1").
Where("created_at >= ?", oneHourAgo).
Order("created_at desc").
Find(&orderList).Error Find(&orderList).Error
if err != nil { if err != nil {
@ -980,70 +997,3 @@ func CheckOrderState() {
} }
} }
} }
// CheckAllOrderState 定时任务检查所有非1小时退订订单
func CheckAllOrderState() {
if database.Db == nil {
log.Println("Database connection is nil")
fmt.Println("Database connection is nil")
return
}
// 查询订单列表中未退订的用户查询其是否退订如果退订则更新退订时间判断是否为1小时内退订
var orderList []MgOrder
err := database.Db.Where("is_one_hour_cancel != 1").Order("created_at desc").
Find(&orderList).Error
if err != nil {
fmt.Println("query mg_order err:", err.Error())
return
}
for i, _ := range orderList {
for j := 0; j < 3; j++ {
var req QueryRightsInfoReq
req.AppChannelList = append(req.AppChannelList, ChannelCode)
req.Mobile = orderList[i].PhoneNumber
resp, err := MiGuQueryRightsInfo(&req)
if err != nil {
fmt.Println("CheckOrderState MiGuQueryRightsInfo err:", err.Error())
logger.Errorf("CheckOrderState MiGuQueryRightsInfo err:", err.Error())
continue
}
// 有退订数据
if len(resp.ResultData) != 0 {
if resp.ResultData[0].IsUnsub == 1 {
var cancelFlag int
subscribeTime := orderList[i].SubscribeTime
// 检查 subscribeTime 是否为 nil
if subscribeTime != nil && IsWithinOneHourCancel(*subscribeTime, resp.ResultData[0].UnsubTime) {
cancelFlag = 1
}
err = database.Db.Table("mg_order").Where("order_serial = ?", orderList[i].OrderSerial).Updates(map[string]interface{}{
"state": UnsubscribeOK,
"is_one_hour_cancel": cancelFlag,
"unsubscribe_time": resp.ResultData[0].UnsubTime,
"updated_at": time.Now(),
}).Error
if err != nil {
fmt.Println("CheckOrderState update mg_order err:", err.Error())
logger.Errorf("CheckOrderState update mg_order err:", err.Error())
logger.Errorf("CheckOrderState order_serial:", orderList[i].OrderSerial)
continue
}
break
}
} else {
if j == 2 {
fmt.Println("resp.ResultData is null, phone_number is:", req.Mobile)
logger.Errorf("resp.ResultData is null, phone_number is:", req.Mobile)
}
continue
}
}
}
}

View File

@ -28,31 +28,32 @@ func registerMiGuControlManageRouter(v1 *gin.RouterGroup, authMiddleware *jwt.Gi
api.POST("user_retention/list", apiMiGu.UserRetentionList) // 用户留存记录 api.POST("user_retention/list", apiMiGu.UserRetentionList) // 用户留存记录
api.POST("sys_channel/list", apiMiGu.SysChannelList) // 查询系统所有渠道编码 api.POST("sys_channel/list", apiMiGu.SysChannelList) // 查询系统所有渠道编码
api.POST("home/data", apiMiGu.HomepageDataSummary) // 查询首页汇总数据 api.POST("home/data", apiMiGu.HomepageDataSummary) // 查询首页汇总数据
api.POST("home/revenue_analysis", apiMiGu.CalculateRevenueAnalysis) // 查询不同日期的留存月份
} }
} }
// registerMiGuControlManageUnAuthRouter 无需认证的路由代码 // registerMiGuControlManageUnAuthRouter 无需认证的路由代码
func registerMiGuControlManageUnAuthRouter(v1 *gin.RouterGroup) { func registerMiGuControlManageUnAuthRouter(v1 *gin.RouterGroup) {
apiMiGu := migumanage.MiGuDeployService{} //apiMiGu := migumanage.MiGuDeployService{}
api := v1.Group("/notice") //api := v1.Group("/notice")
{ //{
api.GET("subscribe", apiMiGu.SubscribeNotice) // 订购成功通知 // api.GET("subscribe", apiMiGu.SubscribeNotice) // 订购成功通知
api.GET("unsubscribe", apiMiGu.UnsubscribeNotice) // 退订通知 // api.GET("unsubscribe", apiMiGu.UnsubscribeNotice) // 退订通知
} //}
//
apiPost := v1.Group("/migu") //apiPost := v1.Group("/migu")
{ //{
apiPost.POST("send_captcha", apiMiGu.SendCaptcha) // 下单接口(下发验证码) // apiPost.POST("send_captcha", apiMiGu.SendCaptcha) // 下单接口(下发验证码)
apiPost.POST("submit_order", apiMiGu.SubmitOrder) // 提交接口(提交验证码) // apiPost.POST("submit_order", apiMiGu.SubmitOrder) // 提交接口(提交验证码)
apiPost.POST("order/check", apiMiGu.CheckOrder) // 查询是否已经退订接口 // apiPost.POST("order/check", apiMiGu.CheckOrder) // 查询是否已经退订接口
apiPost.POST("order/check_rights_info", apiMiGu.CheckRightsInfo) // 查询是否已经退订接口(咪咕) // apiPost.POST("order/check_rights_info", apiMiGu.CheckRightsInfo) // 查询是否已经退订接口(咪咕)
} //}
//
apiEx := v1.Group("/coupon_provider") //apiEx := v1.Group("/coupon_provider")
{ //{
apiEx.POST("send_captcha", apiMiGu.SendCaptchaEx) // 下单接口(下发验证码)-对外 // apiEx.POST("send_captcha", apiMiGu.SendCaptchaEx) // 下单接口(下发验证码)-对外
apiEx.POST("submit_order", apiMiGu.SubmitOrderEx) // 提交接口(提交验证码)-对外 // apiEx.POST("submit_order", apiMiGu.SubmitOrderEx) // 提交接口(提交验证码)-对外
apiEx.POST("order/check", apiMiGu.CheckOrderEx) // 查询是否已经退订接口-对外 // apiEx.POST("order/check", apiMiGu.CheckOrderEx) // 查询是否已经退订接口-对外
apiEx.POST("order/check_rights_info", apiMiGu.CheckRightsInfoEx) // 查询是否已经退订接口(咪咕)-对外 // apiEx.POST("order/check_rights_info", apiMiGu.CheckRightsInfoEx) // 查询是否已经退订接口(咪咕)-对外
} //}
} }

View File

@ -92,21 +92,11 @@ func run() error {
// TODO // TODO
s := gocron.NewScheduler() s := gocron.NewScheduler()
//err := s.Every(1).Day().At("18:01").Do(models.CheckOrderState) err := s.Every(1).Day().At("01:30").Do(models.CheckAllOrderState)
//if err != nil {
// fmt.Println("err:", err)
//}
err := s.Every(10).Minute().Do(models.CheckOrderState)
if err != nil { if err != nil {
fmt.Println("err:", err) fmt.Println("err:", err)
} }
//err = s.Every(1).Day().At("10:28").Do(models.CheckAllOrderState)
//if err != nil {
// fmt.Println("err:", err)
//}
<-s.Start() <-s.Start()
}() }()

View File

@ -151,6 +151,39 @@ const docTemplateadmin = `{
} }
} }
}, },
"/api/v1/admin/home/revenue_analysis": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"2024-咪咕-管理后台"
],
"summary": "营收分析",
"parameters": [
{
"description": "营收分析",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.RetentionMonthsReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.RetentionMonthsResp"
}
}
}
}
},
"/api/v1/admin/order/list": { "/api/v1/admin/order/list": {
"post": { "post": {
"consumes": [ "consumes": [
@ -5021,9 +5054,9 @@ const docTemplateadmin = `{
"description": "查询结束时间", "description": "查询结束时间",
"type": "string" "type": "string"
}, },
"sku_code": { "product_id": {
"description": "产品编号", "description": "产品ID",
"type": "string" "type": "integer"
}, },
"start_time": { "start_time": {
"description": "查询开始时间", "description": "查询开始时间",
@ -5350,6 +5383,23 @@ const docTemplateadmin = `{
} }
} }
}, },
"models.MonthlyRetention": {
"type": "object",
"properties": {
"month": {
"description": "年月",
"type": "string"
},
"new_user_count": {
"description": "新用户数",
"type": "integer"
},
"valid_users_count": {
"description": "当月有效用户数",
"type": "integer"
}
}
},
"models.NewSendDataReq": { "models.NewSendDataReq": {
"type": "object", "type": "object",
"required": [ "required": [
@ -5652,6 +5702,47 @@ const docTemplateadmin = `{
} }
} }
}, },
"models.RetentionMonthsReq": {
"type": "object",
"properties": {
"channel": {
"description": "渠道名称",
"type": "string"
},
"end_time": {
"description": "查询结束时间",
"type": "string"
},
"product_id": {
"description": "产品ID",
"type": "integer"
},
"start_time": {
"description": "查询开始时间",
"type": "string"
}
}
},
"models.RetentionMonthsResp": {
"type": "object",
"properties": {
"monthly_retention_data": {
"description": "每月留存数据",
"type": "array",
"items": {
"$ref": "#/definitions/models.MonthlyRetention"
}
},
"total_new_users": {
"description": "总新用户数",
"type": "integer"
},
"total_valid_users": {
"description": "总有效用户数",
"type": "integer"
}
}
},
"models.SendCaptchaReq": { "models.SendCaptchaReq": {
"type": "object", "type": "object",
"required": [ "required": [
@ -6296,8 +6387,8 @@ var SwaggerInfoadmin = &swag.Spec{
Description: "基于Gin + Vue + Element UI的前后端分离权限管理系统的接口文档\n添加qq群: 521386980 进入技术交流群 请先star谢谢", Description: "基于Gin + Vue + Element UI的前后端分离权限管理系统的接口文档\n添加qq群: 521386980 进入技术交流群 请先star谢谢",
InfoInstanceName: "admin", InfoInstanceName: "admin",
SwaggerTemplate: docTemplateadmin, SwaggerTemplate: docTemplateadmin,
LeftDelim: "{{", //LeftDelim: "{{",
RightDelim: "}}", //RightDelim: "}}",
} }
func init() { func init() {

View File

@ -143,6 +143,39 @@
} }
} }
}, },
"/api/v1/admin/home/revenue_analysis": {
"post": {
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"2024-咪咕-管理后台"
],
"summary": "营收分析",
"parameters": [
{
"description": "营收分析",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/models.RetentionMonthsReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.RetentionMonthsResp"
}
}
}
}
},
"/api/v1/admin/order/list": { "/api/v1/admin/order/list": {
"post": { "post": {
"consumes": [ "consumes": [
@ -5013,9 +5046,9 @@
"description": "查询结束时间", "description": "查询结束时间",
"type": "string" "type": "string"
}, },
"sku_code": { "product_id": {
"description": "产品编号", "description": "产品ID",
"type": "string" "type": "integer"
}, },
"start_time": { "start_time": {
"description": "查询开始时间", "description": "查询开始时间",
@ -5342,6 +5375,23 @@
} }
} }
}, },
"models.MonthlyRetention": {
"type": "object",
"properties": {
"month": {
"description": "年月",
"type": "string"
},
"new_user_count": {
"description": "新用户数",
"type": "integer"
},
"valid_users_count": {
"description": "当月有效用户数",
"type": "integer"
}
}
},
"models.NewSendDataReq": { "models.NewSendDataReq": {
"type": "object", "type": "object",
"required": [ "required": [
@ -5644,6 +5694,47 @@
} }
} }
}, },
"models.RetentionMonthsReq": {
"type": "object",
"properties": {
"channel": {
"description": "渠道名称",
"type": "string"
},
"end_time": {
"description": "查询结束时间",
"type": "string"
},
"product_id": {
"description": "产品ID",
"type": "integer"
},
"start_time": {
"description": "查询开始时间",
"type": "string"
}
}
},
"models.RetentionMonthsResp": {
"type": "object",
"properties": {
"monthly_retention_data": {
"description": "每月留存数据",
"type": "array",
"items": {
"$ref": "#/definitions/models.MonthlyRetention"
}
},
"total_new_users": {
"description": "总新用户数",
"type": "integer"
},
"total_valid_users": {
"description": "总有效用户数",
"type": "integer"
}
}
},
"models.SendCaptchaReq": { "models.SendCaptchaReq": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -1052,9 +1052,9 @@ definitions:
end_time: end_time:
description: 查询结束时间 description: 查询结束时间
type: string type: string
sku_code: product_id:
description: 产品编号 description: 产品ID
type: string type: integer
start_time: start_time:
description: 查询开始时间 description: 查询开始时间
type: string type: string
@ -1288,6 +1288,18 @@ definitions:
description: 留存率(以百分比形式存储) description: 留存率(以百分比形式存储)
type: string type: string
type: object type: object
models.MonthlyRetention:
properties:
month:
description: 年月
type: string
new_user_count:
description: 新用户数
type: integer
valid_users_count:
description: 当月有效用户数
type: integer
type: object
models.NewSendDataReq: models.NewSendDataReq:
properties: properties:
cert_list: cert_list:
@ -1498,6 +1510,35 @@ definitions:
page_size: page_size:
type: integer type: integer
type: object type: object
models.RetentionMonthsReq:
properties:
channel:
description: 渠道名称
type: string
end_time:
description: 查询结束时间
type: string
product_id:
description: 产品ID
type: integer
start_time:
description: 查询开始时间
type: string
type: object
models.RetentionMonthsResp:
properties:
monthly_retention_data:
description: 每月留存数据
items:
$ref: '#/definitions/models.MonthlyRetention'
type: array
total_new_users:
description: 总新用户数
type: integer
total_valid_users:
description: 总有效用户数
type: integer
type: object
models.SendCaptchaReq: models.SendCaptchaReq:
properties: properties:
channel: channel:
@ -2022,6 +2063,27 @@ paths:
summary: 查询首页汇总数据 summary: 查询首页汇总数据
tags: tags:
- 2024-咪咕-管理后台 - 2024-咪咕-管理后台
/api/v1/admin/home/revenue_analysis:
post:
consumes:
- application/json
parameters:
- description: 营收分析
in: body
name: request
required: true
schema:
$ref: '#/definitions/models.RetentionMonthsReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/models.RetentionMonthsResp'
summary: 营收分析
tags:
- 2024-咪咕-管理后台
/api/v1/admin/order/list: /api/v1/admin/order/list:
post: post:
consumes: consumes: