new
This commit is contained in:
@@ -468,6 +468,77 @@ func (h *ApiHandler) GetAdminApiCalls(c *gin.Context) {
|
||||
h.responseBuilder.Success(c, result, "获取API调用记录成功")
|
||||
}
|
||||
|
||||
// ExportAdminApiCalls 导出管理端API调用记录
|
||||
// @Summary 导出管理端API调用记录
|
||||
// @Description 管理员导出API调用记录,支持Excel和CSV格式
|
||||
// @Tags API调用管理
|
||||
// @Accept json
|
||||
// @Produce application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/csv
|
||||
// @Security Bearer
|
||||
// @Param user_ids query string false "用户ID列表,逗号分隔"
|
||||
// @Param product_ids query string false "产品ID列表,逗号分隔"
|
||||
// @Param start_time query string false "开始时间" format(date-time)
|
||||
// @Param end_time query string false "结束时间" format(date-time)
|
||||
// @Param format query string false "导出格式" Enums(excel, csv) default(excel)
|
||||
// @Success 200 {file} file "导出文件"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未认证"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/admin/api-calls/export [get]
|
||||
func (h *ApiHandler) ExportAdminApiCalls(c *gin.Context) {
|
||||
// 解析查询参数
|
||||
filters := make(map[string]interface{})
|
||||
|
||||
// 用户ID筛选
|
||||
if userIds := c.Query("user_ids"); userIds != "" {
|
||||
filters["user_ids"] = userIds
|
||||
}
|
||||
|
||||
// 产品ID筛选
|
||||
if productIds := c.Query("product_ids"); productIds != "" {
|
||||
filters["product_ids"] = productIds
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if startTime := c.Query("start_time"); startTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", startTime); err == nil {
|
||||
filters["start_time"] = t
|
||||
}
|
||||
}
|
||||
if endTime := c.Query("end_time"); endTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", endTime); err == nil {
|
||||
filters["end_time"] = t
|
||||
}
|
||||
}
|
||||
|
||||
// 获取导出格式,默认为excel
|
||||
format := c.DefaultQuery("format", "excel")
|
||||
if format != "excel" && format != "csv" {
|
||||
h.responseBuilder.BadRequest(c, "不支持的导出格式")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用应用服务导出数据
|
||||
fileData, err := h.appService.ExportAdminApiCalls(c.Request.Context(), filters, format)
|
||||
if err != nil {
|
||||
h.logger.Error("导出API调用记录失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "导出API调用记录失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
contentType := "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
filename := "API调用记录.xlsx"
|
||||
if format == "csv" {
|
||||
contentType = "text/csv;charset=utf-8"
|
||||
filename = "API调用记录.csv"
|
||||
}
|
||||
|
||||
c.Header("Content-Type", contentType)
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Data(200, contentType, fileData)
|
||||
}
|
||||
|
||||
// getIntQuery 获取整数查询参数
|
||||
func (h *ApiHandler) getIntQuery(c *gin.Context, key string, defaultValue int) int {
|
||||
if value := c.Query(key); value != "" {
|
||||
@@ -477,3 +548,116 @@ func (h *ApiHandler) getIntQuery(c *gin.Context, key string, defaultValue int) i
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
// GetUserBalanceAlertSettings 获取用户余额预警设置
|
||||
// @Summary 获取用户余额预警设置
|
||||
// @Description 获取当前用户的余额预警配置
|
||||
// @Tags 用户设置
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} map[string]interface{} "获取成功"
|
||||
// @Failure 401 {object} map[string]interface{} "未授权"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/user/balance-alert/settings [get]
|
||||
func (h *ApiHandler) GetUserBalanceAlertSettings(c *gin.Context) {
|
||||
userID := c.GetString("user_id")
|
||||
if userID == "" {
|
||||
h.responseBuilder.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
|
||||
settings, err := h.appService.GetUserBalanceAlertSettings(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
h.logger.Error("获取用户余额预警设置失败",
|
||||
zap.String("user_id", userID),
|
||||
zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "获取预警设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, settings, "获取成功")
|
||||
}
|
||||
|
||||
// UpdateUserBalanceAlertSettings 更新用户余额预警设置
|
||||
// @Summary 更新用户余额预警设置
|
||||
// @Description 更新当前用户的余额预警配置
|
||||
// @Tags 用户设置
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body map[string]interface{} true "预警设置"
|
||||
// @Success 200 {object} map[string]interface{} "更新成功"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未授权"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/user/balance-alert/settings [put]
|
||||
func (h *ApiHandler) UpdateUserBalanceAlertSettings(c *gin.Context) {
|
||||
userID := c.GetString("user_id")
|
||||
if userID == "" {
|
||||
h.responseBuilder.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
|
||||
var request struct {
|
||||
Enabled bool `json:"enabled" binding:"required"`
|
||||
Threshold float64 `json:"threshold" binding:"required,min=0"`
|
||||
AlertPhone string `json:"alert_phone" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
h.responseBuilder.BadRequest(c, "请求参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err := h.appService.UpdateUserBalanceAlertSettings(c.Request.Context(), userID, request.Enabled, request.Threshold, request.AlertPhone)
|
||||
if err != nil {
|
||||
h.logger.Error("更新用户余额预警设置失败",
|
||||
zap.String("user_id", userID),
|
||||
zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "更新预警设置失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, gin.H{}, "更新成功")
|
||||
}
|
||||
|
||||
// TestBalanceAlertSms 测试余额预警短信
|
||||
// @Summary 测试余额预警短信
|
||||
// @Description 发送测试预警短信到指定手机号
|
||||
// @Tags 用户设置
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body map[string]interface{} true "测试参数"
|
||||
// @Success 200 {object} map[string]interface{} "发送成功"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未授权"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/user/balance-alert/test-sms [post]
|
||||
func (h *ApiHandler) TestBalanceAlertSms(c *gin.Context) {
|
||||
userID := c.GetString("user_id")
|
||||
if userID == "" {
|
||||
h.responseBuilder.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
|
||||
var request struct {
|
||||
Phone string `json:"phone" binding:"required,len=11"`
|
||||
Balance float64 `json:"balance" binding:"required"`
|
||||
AlertType string `json:"alert_type" binding:"required,oneof=low_balance arrears"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
h.responseBuilder.BadRequest(c, "请求参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err := h.appService.TestBalanceAlertSms(c.Request.Context(), userID, request.Phone, request.Balance, request.AlertType)
|
||||
if err != nil {
|
||||
h.logger.Error("发送测试预警短信失败",
|
||||
zap.String("user_id", userID),
|
||||
zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "发送测试短信失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, gin.H{}, "测试短信发送成功")
|
||||
}
|
||||
|
||||
@@ -215,6 +215,86 @@ func (h *CertificationHandler) ApplyContract(c *gin.Context) {
|
||||
h.response.Success(c, result, "合同申请成功")
|
||||
}
|
||||
|
||||
// RecognizeBusinessLicense OCR识别营业执照
|
||||
// @Summary OCR识别营业执照
|
||||
// @Description 上传营业执照图片进行OCR识别,自动填充企业信息
|
||||
// @Tags 认证管理
|
||||
// @Accept multipart/form-data
|
||||
// @Produce json
|
||||
// @Security Bearer
|
||||
// @Param image formData file true "营业执照图片文件"
|
||||
// @Success 200 {object} responses.BusinessLicenseResult "营业执照识别成功"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未认证"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/certifications/ocr/business-license [post]
|
||||
func (h *CertificationHandler) RecognizeBusinessLicense(c *gin.Context) {
|
||||
userID := h.getCurrentUserID(c)
|
||||
if userID == "" {
|
||||
h.response.Unauthorized(c, "用户未登录")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取上传的文件
|
||||
file, err := c.FormFile("image")
|
||||
if err != nil {
|
||||
h.logger.Error("获取上传文件失败", zap.Error(err), zap.String("user_id", userID))
|
||||
h.response.BadRequest(c, "请选择要上传的营业执照图片")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文件类型
|
||||
allowedTypes := map[string]bool{
|
||||
"image/jpeg": true,
|
||||
"image/jpg": true,
|
||||
"image/png": true,
|
||||
"image/webp": true,
|
||||
}
|
||||
if !allowedTypes[file.Header.Get("Content-Type")] {
|
||||
h.response.BadRequest(c, "只支持JPG、PNG、WEBP格式的图片")
|
||||
return
|
||||
}
|
||||
|
||||
// 验证文件大小(限制为5MB)
|
||||
if file.Size > 5*1024*1024 {
|
||||
h.response.BadRequest(c, "图片大小不能超过5MB")
|
||||
return
|
||||
}
|
||||
|
||||
// 打开文件
|
||||
src, err := file.Open()
|
||||
if err != nil {
|
||||
h.logger.Error("打开上传文件失败", zap.Error(err), zap.String("user_id", userID))
|
||||
h.response.BadRequest(c, "文件读取失败")
|
||||
return
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
// 读取文件内容
|
||||
imageBytes, err := io.ReadAll(src)
|
||||
if err != nil {
|
||||
h.logger.Error("读取文件内容失败", zap.Error(err), zap.String("user_id", userID))
|
||||
h.response.BadRequest(c, "文件读取失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用OCR服务识别营业执照
|
||||
result, err := h.appService.RecognizeBusinessLicense(c.Request.Context(), imageBytes)
|
||||
if err != nil {
|
||||
h.logger.Error("营业执照OCR识别失败", zap.Error(err), zap.String("user_id", userID))
|
||||
h.response.BadRequest(c, "营业执照识别失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.logger.Info("营业执照OCR识别成功",
|
||||
zap.String("user_id", userID),
|
||||
zap.String("company_name", result.CompanyName),
|
||||
zap.Float64("confidence", result.Confidence),
|
||||
)
|
||||
|
||||
h.response.Success(c, result, "营业执照识别成功")
|
||||
}
|
||||
|
||||
// ListCertifications 获取认证列表(管理员)
|
||||
// @Summary 获取认证列表
|
||||
// @Description 管理员获取认证申请列表
|
||||
|
||||
@@ -1199,6 +1199,102 @@ func (h *ProductAdminHandler) GetAdminWalletTransactions(c *gin.Context) {
|
||||
h.responseBuilder.Success(c, result, "获取消费记录成功")
|
||||
}
|
||||
|
||||
// ExportAdminWalletTransactions 导出管理端消费记录
|
||||
// @Summary 导出管理端消费记录
|
||||
// @Description 管理员导出消费记录,支持Excel和CSV格式
|
||||
// @Tags 财务管理
|
||||
// @Accept json
|
||||
// @Produce application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/csv
|
||||
// @Security Bearer
|
||||
// @Param user_ids query string false "用户ID列表,逗号分隔"
|
||||
// @Param user_id query string false "单个用户ID"
|
||||
// @Param transaction_id query string false "交易ID"
|
||||
// @Param product_name query string false "产品名称"
|
||||
// @Param product_ids query string false "产品ID列表,逗号分隔"
|
||||
// @Param min_amount query string false "最小金额"
|
||||
// @Param max_amount query string false "最大金额"
|
||||
// @Param start_time query string false "开始时间" format(date-time)
|
||||
// @Param end_time query string false "结束时间" format(date-time)
|
||||
// @Param format query string false "导出格式" Enums(excel, csv) default(excel)
|
||||
// @Success 200 {file} file "导出文件"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未认证"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/admin/wallet-transactions/export [get]
|
||||
func (h *ProductAdminHandler) ExportAdminWalletTransactions(c *gin.Context) {
|
||||
// 构建筛选条件
|
||||
filters := make(map[string]interface{})
|
||||
|
||||
// 用户ID筛选
|
||||
if userIds := c.Query("user_ids"); userIds != "" {
|
||||
filters["user_ids"] = userIds
|
||||
} else if userId := c.Query("user_id"); userId != "" {
|
||||
filters["user_id"] = userId
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if startTime := c.Query("start_time"); startTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", startTime); err == nil {
|
||||
filters["start_time"] = t
|
||||
}
|
||||
}
|
||||
if endTime := c.Query("end_time"); endTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", endTime); err == nil {
|
||||
filters["end_time"] = t
|
||||
}
|
||||
}
|
||||
|
||||
// 交易ID筛选
|
||||
if transactionId := c.Query("transaction_id"); transactionId != "" {
|
||||
filters["transaction_id"] = transactionId
|
||||
}
|
||||
|
||||
// 产品名称筛选
|
||||
if productName := c.Query("product_name"); productName != "" {
|
||||
filters["product_name"] = productName
|
||||
}
|
||||
|
||||
// 产品ID列表筛选
|
||||
if productIds := c.Query("product_ids"); productIds != "" {
|
||||
filters["product_ids"] = productIds
|
||||
}
|
||||
|
||||
// 金额范围筛选
|
||||
if minAmount := c.Query("min_amount"); minAmount != "" {
|
||||
filters["min_amount"] = minAmount
|
||||
}
|
||||
if maxAmount := c.Query("max_amount"); maxAmount != "" {
|
||||
filters["max_amount"] = maxAmount
|
||||
}
|
||||
|
||||
// 获取导出格式
|
||||
format := c.DefaultQuery("format", "excel")
|
||||
if format != "excel" && format != "csv" {
|
||||
h.responseBuilder.BadRequest(c, "不支持的导出格式")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用导出服务
|
||||
fileData, err := h.financeAppService.ExportAdminWalletTransactions(c.Request.Context(), filters, format)
|
||||
if err != nil {
|
||||
h.logger.Error("导出消费记录失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "导出消费记录失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
contentType := "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
filename := "消费记录.xlsx"
|
||||
if format == "csv" {
|
||||
contentType = "text/csv;charset=utf-8"
|
||||
filename = "消费记录.csv"
|
||||
}
|
||||
|
||||
c.Header("Content-Type", contentType)
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Data(200, contentType, fileData)
|
||||
}
|
||||
|
||||
// GetAdminRechargeRecords 获取管理端充值记录
|
||||
// @Summary 获取管理端充值记录
|
||||
// @Description 管理员获取充值记录,支持筛选和分页
|
||||
@@ -1282,3 +1378,184 @@ func (h *ProductAdminHandler) GetAdminRechargeRecords(c *gin.Context) {
|
||||
|
||||
h.responseBuilder.Success(c, result, "获取充值记录成功")
|
||||
}
|
||||
|
||||
// ExportAdminRechargeRecords 导出管理端充值记录
|
||||
// @Summary 导出管理端充值记录
|
||||
// @Description 管理员导出充值记录,支持Excel和CSV格式
|
||||
// @Tags 财务管理
|
||||
// @Accept json
|
||||
// @Produce application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/csv
|
||||
// @Security Bearer
|
||||
// @Param user_ids query string false "用户ID列表,逗号分隔"
|
||||
// @Param recharge_type query string false "充值类型" Enums(alipay, transfer, gift)
|
||||
// @Param status query string false "状态" Enums(pending, success, failed)
|
||||
// @Param start_time query string false "开始时间" format(date-time)
|
||||
// @Param end_time query string false "结束时间" format(date-time)
|
||||
// @Param format query string false "导出格式" Enums(excel, csv) default(excel)
|
||||
// @Success 200 {file} file "导出文件"
|
||||
// @Failure 400 {object} map[string]interface{} "请求参数错误"
|
||||
// @Failure 401 {object} map[string]interface{} "未认证"
|
||||
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
|
||||
// @Router /api/v1/admin/recharge-records/export [get]
|
||||
func (h *ProductAdminHandler) ExportAdminRechargeRecords(c *gin.Context) {
|
||||
// 解析查询参数
|
||||
filters := make(map[string]interface{})
|
||||
|
||||
// 用户ID筛选
|
||||
if userIds := c.Query("user_ids"); userIds != "" {
|
||||
filters["user_ids"] = userIds
|
||||
}
|
||||
|
||||
// 充值类型筛选
|
||||
if rechargeType := c.Query("recharge_type"); rechargeType != "" {
|
||||
filters["recharge_type"] = rechargeType
|
||||
}
|
||||
|
||||
// 状态筛选
|
||||
if status := c.Query("status"); status != "" {
|
||||
filters["status"] = status
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if startTime := c.Query("start_time"); startTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", startTime); err == nil {
|
||||
filters["start_time"] = t
|
||||
}
|
||||
}
|
||||
if endTime := c.Query("end_time"); endTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", endTime); err == nil {
|
||||
filters["end_time"] = t
|
||||
}
|
||||
}
|
||||
|
||||
// 获取导出格式,默认为excel
|
||||
format := c.DefaultQuery("format", "excel")
|
||||
if format != "excel" && format != "csv" {
|
||||
h.responseBuilder.BadRequest(c, "不支持的导出格式")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用应用服务导出数据
|
||||
fileData, err := h.financeAppService.ExportAdminRechargeRecords(c.Request.Context(), filters, format)
|
||||
if err != nil {
|
||||
h.logger.Error("导出充值记录失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "导出充值记录失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
contentType := "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
filename := "充值记录.xlsx"
|
||||
if format == "csv" {
|
||||
contentType = "text/csv;charset=utf-8"
|
||||
filename = "充值记录.csv"
|
||||
}
|
||||
|
||||
c.Header("Content-Type", contentType)
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Data(200, contentType, fileData)
|
||||
}
|
||||
|
||||
// GetAdminApiCalls 获取管理端API调用记录
|
||||
func (h *ProductAdminHandler) GetAdminApiCalls(c *gin.Context) {
|
||||
// 解析查询参数
|
||||
page := h.getIntQuery(c, "page", 1)
|
||||
pageSize := h.getIntQuery(c, "page_size", 10)
|
||||
|
||||
// 构建筛选条件
|
||||
filters := make(map[string]interface{})
|
||||
|
||||
// 用户ID筛选
|
||||
if userIds := c.Query("user_ids"); userIds != "" {
|
||||
filters["user_ids"] = userIds
|
||||
}
|
||||
|
||||
// 产品ID筛选
|
||||
if productIds := c.Query("product_ids"); productIds != "" {
|
||||
filters["product_ids"] = productIds
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if startTime := c.Query("start_time"); startTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", startTime); err == nil {
|
||||
filters["start_time"] = t
|
||||
}
|
||||
}
|
||||
if endTime := c.Query("end_time"); endTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", endTime); err == nil {
|
||||
filters["end_time"] = t
|
||||
}
|
||||
}
|
||||
|
||||
// 构建分页选项
|
||||
options := interfaces.ListOptions{
|
||||
Page: page,
|
||||
PageSize: pageSize,
|
||||
Sort: "created_at",
|
||||
Order: "desc",
|
||||
}
|
||||
|
||||
result, err := h.apiAppService.GetAdminApiCalls(c.Request.Context(), filters, options)
|
||||
if err != nil {
|
||||
h.logger.Error("获取管理端API调用记录失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "获取API调用记录失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.responseBuilder.Success(c, result, "获取API调用记录成功")
|
||||
}
|
||||
|
||||
// ExportAdminApiCalls 导出管理端API调用记录
|
||||
func (h *ProductAdminHandler) ExportAdminApiCalls(c *gin.Context) {
|
||||
// 解析查询参数
|
||||
filters := make(map[string]interface{})
|
||||
|
||||
// 用户ID筛选
|
||||
if userIds := c.Query("user_ids"); userIds != "" {
|
||||
filters["user_ids"] = userIds
|
||||
}
|
||||
|
||||
// 产品ID筛选
|
||||
if productIds := c.Query("product_ids"); productIds != "" {
|
||||
filters["product_ids"] = productIds
|
||||
}
|
||||
|
||||
// 时间范围筛选
|
||||
if startTime := c.Query("start_time"); startTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", startTime); err == nil {
|
||||
filters["start_time"] = t
|
||||
}
|
||||
}
|
||||
if endTime := c.Query("end_time"); endTime != "" {
|
||||
if t, err := time.Parse("2006-01-02 15:04:05", endTime); err == nil {
|
||||
filters["end_time"] = t
|
||||
}
|
||||
}
|
||||
|
||||
// 获取导出格式,默认为excel
|
||||
format := c.DefaultQuery("format", "excel")
|
||||
if format != "excel" && format != "csv" {
|
||||
h.responseBuilder.BadRequest(c, "不支持的导出格式")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用应用服务导出数据
|
||||
fileData, err := h.apiAppService.ExportAdminApiCalls(c.Request.Context(), filters, format)
|
||||
if err != nil {
|
||||
h.logger.Error("导出API调用记录失败", zap.Error(err))
|
||||
h.responseBuilder.BadRequest(c, "导出API调用记录失败")
|
||||
return
|
||||
}
|
||||
|
||||
// 设置响应头
|
||||
contentType := "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
filename := "API调用记录.xlsx"
|
||||
if format == "csv" {
|
||||
contentType = "text/csv;charset=utf-8"
|
||||
filename = "API调用记录.csv"
|
||||
}
|
||||
|
||||
c.Header("Content-Type", contentType)
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Data(200, contentType, fileData)
|
||||
}
|
||||
|
||||
1502
internal/infrastructure/http/handlers/statistics_handler.go
Normal file
1502
internal/infrastructure/http/handlers/statistics_handler.go
Normal file
File diff suppressed because it is too large
Load Diff
@@ -274,7 +274,6 @@ func (h *UserHandler) ListUsers(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// 构建查询参数
|
||||
query := &queries.ListUsersQuery{
|
||||
Page: 1,
|
||||
@@ -289,7 +288,7 @@ func (h *UserHandler) ListUsers(c *gin.Context) {
|
||||
}
|
||||
|
||||
if pageSize := c.Query("page_size"); pageSize != "" {
|
||||
if size, err := strconv.Atoi(pageSize); err == nil && size > 0 && size <= 100 {
|
||||
if size, err := strconv.Atoi(pageSize); err == nil && size > 0 && size <= 1000 {
|
||||
query.PageSize = size
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ func (r *ApiRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
apiGroup := engine.Group("/api/v1")
|
||||
|
||||
{
|
||||
// API调用接口 - 不受频率限制(业务核心接口)
|
||||
apiGroup.POST("/:api_name", r.domainAuthMiddleware.Handle(""), r.apiHandler.HandleApiCall)
|
||||
|
||||
// Console专用接口 - 使用JWT认证,不需要域名认证
|
||||
@@ -62,6 +63,11 @@ func (r *ApiRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
|
||||
// API调用记录接口
|
||||
apiGroup.GET("/my/api-calls", r.authMiddleware.Handle(), r.apiHandler.GetUserApiCalls)
|
||||
|
||||
// 余额预警设置接口
|
||||
apiGroup.GET("/user/balance-alert/settings", r.authMiddleware.Handle(), r.apiHandler.GetUserBalanceAlertSettings)
|
||||
apiGroup.PUT("/user/balance-alert/settings", r.authMiddleware.Handle(), r.apiHandler.UpdateUserBalanceAlertSettings)
|
||||
apiGroup.POST("/user/balance-alert/test-sms", r.authMiddleware.Handle(), r.apiHandler.TestBalanceAlertSms)
|
||||
}
|
||||
|
||||
r.logger.Info("API路由注册完成")
|
||||
|
||||
@@ -54,6 +54,9 @@ func (r *CertificationRoutes) Register(router *http.GinRouter) {
|
||||
// 2. 提交企业信息(应用每日限流)
|
||||
authGroup.POST("/enterprise-info", r.dailyRateLimit.Handle(), r.handler.SubmitEnterpriseInfo)
|
||||
|
||||
// OCR营业执照识别接口
|
||||
authGroup.POST("/ocr/business-license", r.handler.RecognizeBusinessLicense)
|
||||
|
||||
// 3. 申请合同签署
|
||||
authGroup.POST("/apply-contract", r.handler.ApplyContract)
|
||||
|
||||
@@ -84,6 +87,7 @@ func (r *CertificationRoutes) GetRoutes() []RouteInfo {
|
||||
{Method: "GET", Path: "/api/v1/certifications", Handler: "ListCertifications", Auth: true},
|
||||
{Method: "GET", Path: "/api/v1/certifications/statistics", Handler: "GetCertificationStatistics", Auth: true},
|
||||
{Method: "POST", Path: "/api/v1/certifications/:id/enterprise-info", Handler: "SubmitEnterpriseInfo", Auth: true},
|
||||
{Method: "POST", Path: "/api/v1/certifications/ocr/business-license", Handler: "RecognizeBusinessLicense", Auth: true},
|
||||
{Method: "POST", Path: "/api/v1/certifications/apply-contract", Handler: "ApplyContract", Auth: true},
|
||||
{Method: "POST", Path: "/api/v1/certifications/retry", Handler: "RetryOperation", Auth: true},
|
||||
{Method: "POST", Path: "/api/v1/certifications/force-transition", Handler: "ForceTransitionStatus", Auth: true},
|
||||
|
||||
@@ -87,12 +87,21 @@ func (r *ProductAdminRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
walletTransactions := adminGroup.Group("/wallet-transactions")
|
||||
{
|
||||
walletTransactions.GET("", r.handler.GetAdminWalletTransactions)
|
||||
walletTransactions.GET("/export", r.handler.ExportAdminWalletTransactions)
|
||||
}
|
||||
|
||||
// API调用记录管理
|
||||
apiCalls := adminGroup.Group("/api-calls")
|
||||
{
|
||||
apiCalls.GET("", r.handler.GetAdminApiCalls)
|
||||
apiCalls.GET("/export", r.handler.ExportAdminApiCalls)
|
||||
}
|
||||
|
||||
// 充值记录管理
|
||||
rechargeRecords := adminGroup.Group("/recharge-records")
|
||||
{
|
||||
rechargeRecords.GET("", r.handler.GetAdminRechargeRecords)
|
||||
rechargeRecords.GET("/export", r.handler.ExportAdminRechargeRecords)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
165
internal/infrastructure/http/routes/statistics_routes.go
Normal file
165
internal/infrastructure/http/routes/statistics_routes.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"tyapi-server/internal/infrastructure/http/handlers"
|
||||
sharedhttp "tyapi-server/internal/shared/http"
|
||||
"tyapi-server/internal/shared/middleware"
|
||||
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// StatisticsRoutes 统计路由
|
||||
type StatisticsRoutes struct {
|
||||
statisticsHandler *handlers.StatisticsHandler
|
||||
auth *middleware.JWTAuthMiddleware
|
||||
optionalAuth *middleware.OptionalAuthMiddleware
|
||||
admin *middleware.AdminAuthMiddleware
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// NewStatisticsRoutes 创建统计路由
|
||||
func NewStatisticsRoutes(
|
||||
statisticsHandler *handlers.StatisticsHandler,
|
||||
auth *middleware.JWTAuthMiddleware,
|
||||
optionalAuth *middleware.OptionalAuthMiddleware,
|
||||
admin *middleware.AdminAuthMiddleware,
|
||||
logger *zap.Logger,
|
||||
) *StatisticsRoutes {
|
||||
return &StatisticsRoutes{
|
||||
statisticsHandler: statisticsHandler,
|
||||
auth: auth,
|
||||
optionalAuth: optionalAuth,
|
||||
admin: admin,
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Register 注册统计相关路由
|
||||
func (r *StatisticsRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
engine := router.GetEngine()
|
||||
|
||||
// ================ 用户端统计路由 ================
|
||||
|
||||
// 统计公开接口
|
||||
statistics := engine.Group("/api/v1/statistics")
|
||||
{
|
||||
// 获取公开统计信息
|
||||
statistics.GET("/public", r.statisticsHandler.GetPublicStatistics)
|
||||
}
|
||||
|
||||
// 用户统计接口 - 需要认证
|
||||
userStats := engine.Group("/api/v1/statistics", r.auth.Handle())
|
||||
{
|
||||
// 获取用户统计信息
|
||||
userStats.GET("/user", r.statisticsHandler.GetUserStatistics)
|
||||
|
||||
// 独立统计接口(用户只能查询自己的数据)
|
||||
userStats.GET("/api-calls", r.statisticsHandler.GetApiCallsStatistics)
|
||||
userStats.GET("/consumption", r.statisticsHandler.GetConsumptionStatistics)
|
||||
userStats.GET("/recharge", r.statisticsHandler.GetRechargeStatistics)
|
||||
|
||||
// 获取最新产品推荐
|
||||
userStats.GET("/latest-products", r.statisticsHandler.GetLatestProducts)
|
||||
|
||||
// 获取指标列表
|
||||
userStats.GET("/metrics", r.statisticsHandler.GetMetrics)
|
||||
|
||||
// 获取指标详情
|
||||
userStats.GET("/metrics/:id", r.statisticsHandler.GetMetricDetail)
|
||||
|
||||
// 获取仪表板列表
|
||||
userStats.GET("/dashboards", r.statisticsHandler.GetDashboards)
|
||||
|
||||
// 获取仪表板详情
|
||||
userStats.GET("/dashboards/:id", r.statisticsHandler.GetDashboardDetail)
|
||||
|
||||
// 获取仪表板数据
|
||||
userStats.GET("/dashboards/:id/data", r.statisticsHandler.GetDashboardData)
|
||||
|
||||
// 获取报告列表
|
||||
userStats.GET("/reports", r.statisticsHandler.GetReports)
|
||||
|
||||
// 获取报告详情
|
||||
userStats.GET("/reports/:id", r.statisticsHandler.GetReportDetail)
|
||||
|
||||
// 创建报告
|
||||
userStats.POST("/reports", r.statisticsHandler.CreateReport)
|
||||
}
|
||||
|
||||
// ================ 管理员统计路由 ================
|
||||
|
||||
// 管理员路由组
|
||||
adminGroup := engine.Group("/api/v1/admin")
|
||||
adminGroup.Use(r.admin.Handle()) // 管理员权限验证
|
||||
{
|
||||
// 统计指标管理
|
||||
metrics := adminGroup.Group("/statistics/metrics")
|
||||
{
|
||||
metrics.GET("", r.statisticsHandler.AdminGetMetrics)
|
||||
metrics.POST("", r.statisticsHandler.AdminCreateMetric)
|
||||
metrics.PUT("/:id", r.statisticsHandler.AdminUpdateMetric)
|
||||
metrics.DELETE("/:id", r.statisticsHandler.AdminDeleteMetric)
|
||||
}
|
||||
|
||||
// 仪表板管理
|
||||
dashboards := adminGroup.Group("/statistics/dashboards")
|
||||
{
|
||||
dashboards.GET("", r.statisticsHandler.AdminGetDashboards)
|
||||
dashboards.POST("", r.statisticsHandler.AdminCreateDashboard)
|
||||
dashboards.PUT("/:id", r.statisticsHandler.AdminUpdateDashboard)
|
||||
dashboards.DELETE("/:id", r.statisticsHandler.AdminDeleteDashboard)
|
||||
}
|
||||
|
||||
// 报告管理
|
||||
reports := adminGroup.Group("/statistics/reports")
|
||||
{
|
||||
reports.GET("", r.statisticsHandler.AdminGetReports)
|
||||
}
|
||||
|
||||
// 系统统计
|
||||
system := adminGroup.Group("/statistics/system")
|
||||
{
|
||||
system.GET("", r.statisticsHandler.AdminGetSystemStatistics)
|
||||
}
|
||||
|
||||
// 独立域统计接口
|
||||
domainStats := adminGroup.Group("/statistics")
|
||||
{
|
||||
domainStats.GET("/user-domain", r.statisticsHandler.AdminGetUserDomainStatistics)
|
||||
domainStats.GET("/api-domain", r.statisticsHandler.AdminGetApiDomainStatistics)
|
||||
domainStats.GET("/consumption-domain", r.statisticsHandler.AdminGetConsumptionDomainStatistics)
|
||||
domainStats.GET("/recharge-domain", r.statisticsHandler.AdminGetRechargeDomainStatistics)
|
||||
}
|
||||
|
||||
// 排行榜接口
|
||||
rankings := adminGroup.Group("/statistics")
|
||||
{
|
||||
rankings.GET("/user-call-ranking", r.statisticsHandler.AdminGetUserCallRanking)
|
||||
rankings.GET("/recharge-ranking", r.statisticsHandler.AdminGetRechargeRanking)
|
||||
rankings.GET("/api-popularity-ranking", r.statisticsHandler.AdminGetApiPopularityRanking)
|
||||
rankings.GET("/today-certified-enterprises", r.statisticsHandler.AdminGetTodayCertifiedEnterprises)
|
||||
}
|
||||
|
||||
// 用户统计
|
||||
userStats := adminGroup.Group("/statistics/users")
|
||||
{
|
||||
userStats.GET("/:user_id", r.statisticsHandler.AdminGetUserStatistics)
|
||||
}
|
||||
|
||||
// 独立统计接口(管理员可查询任意用户)
|
||||
independentStats := adminGroup.Group("/statistics")
|
||||
{
|
||||
independentStats.GET("/api-calls", r.statisticsHandler.GetApiCallsStatistics)
|
||||
independentStats.GET("/consumption", r.statisticsHandler.GetConsumptionStatistics)
|
||||
independentStats.GET("/recharge", r.statisticsHandler.GetRechargeStatistics)
|
||||
}
|
||||
|
||||
// 数据聚合
|
||||
aggregation := adminGroup.Group("/statistics/aggregation")
|
||||
{
|
||||
aggregation.POST("/trigger", r.statisticsHandler.AdminTriggerAggregation)
|
||||
}
|
||||
}
|
||||
|
||||
r.logger.Info("统计路由注册完成")
|
||||
}
|
||||
Reference in New Issue
Block a user