Files
tyapi-server/internal/infrastructure/http/handlers/api_handler.go
2025-09-12 01:15:09 +08:00

664 lines
21 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package handlers
import (
"strconv"
"time"
"tyapi-server/internal/application/api"
"tyapi-server/internal/application/api/commands"
"tyapi-server/internal/application/api/dto"
"tyapi-server/internal/shared/interfaces"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
// ApiHandler API调用HTTP处理器
type ApiHandler struct {
appService api.ApiApplicationService
responseBuilder interfaces.ResponseBuilder
validator interfaces.RequestValidator
logger *zap.Logger
}
// NewApiHandler 创建API调用HTTP处理器
func NewApiHandler(
appService api.ApiApplicationService,
responseBuilder interfaces.ResponseBuilder,
validator interfaces.RequestValidator,
logger *zap.Logger,
) *ApiHandler {
return &ApiHandler{
appService: appService,
responseBuilder: responseBuilder,
validator: validator,
logger: logger,
}
}
// HandleApiCall 统一API调用入口
// @Summary API调用
// @Description 统一API调用入口参数加密传输
// @Tags API调用
// @Accept json
// @Produce json
// @Param request body commands.ApiCallCommand true "API调用请求"
// @Success 200 {object} dto.ApiCallResponse "调用成功"
// @Failure 400 {object} dto.ApiCallResponse "请求参数错误"
// @Failure 401 {object} dto.ApiCallResponse "未授权"
// @Failure 429 {object} dto.ApiCallResponse "请求过于频繁"
// @Failure 500 {object} dto.ApiCallResponse "服务器内部错误"
// @Router /api/v1/:api_name [post]
func (h *ApiHandler) HandleApiCall(c *gin.Context) {
// 1. 基础参数校验
accessId := c.GetHeader("Access-Id")
if accessId == "" {
response := dto.NewErrorResponse(1005, "缺少Access-Id", "")
c.JSON(200, response)
return
}
// 2. 绑定和校验请求参数
var cmd commands.ApiCallCommand
cmd.ClientIP = c.ClientIP()
cmd.AccessId = accessId
cmd.ApiName = c.Param("api_name")
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
response := dto.NewErrorResponse(1003, "请求参数结构不正确", "")
c.JSON(200, response)
return
}
// 3. 调用应用服务
transactionId, encryptedResp, err := h.appService.CallApi(c.Request.Context(), &cmd)
if err != nil {
// 根据错误类型返回对应的错误码和预定义错误消息
errorCode := api.GetErrorCode(err)
errorMessage := api.GetErrorMessage(err)
response := dto.NewErrorResponse(errorCode, errorMessage, transactionId)
c.JSON(200, response) // API调用接口统一返回200状态码
return
}
// 4. 返回成功响应
response := dto.NewSuccessResponse(transactionId, encryptedResp)
c.JSON(200, response)
}
// GetUserApiKeys 获取用户API密钥
func (h *ApiHandler) GetUserApiKeys(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
result, err := h.appService.GetUserApiKeys(c.Request.Context(), userID)
if err != nil {
h.logger.Error("获取用户API密钥失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "获取API密钥成功")
}
// GetUserWhiteList 获取用户白名单列表
func (h *ApiHandler) GetUserWhiteList(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
result, err := h.appService.GetUserWhiteList(c.Request.Context(), userID)
if err != nil {
h.logger.Error("获取用户白名单失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "获取白名单成功")
}
// AddWhiteListIP 添加白名单IP
func (h *ApiHandler) AddWhiteListIP(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
var req dto.WhiteListRequest
if err := h.validator.BindAndValidate(c, &req); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误")
return
}
err := h.appService.AddWhiteListIP(c.Request.Context(), userID, req.IPAddress)
if err != nil {
h.logger.Error("添加白名单IP失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "添加白名单IP成功")
}
// DeleteWhiteListIP 删除白名单IP
// @Summary 删除白名单IP
// @Description 从当前用户的白名单中删除指定IP地址
// @Tags API管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param ip path string true "IP地址"
// @Success 200 {object} map[string]interface{} "删除白名单IP成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/my/whitelist/{ip} [delete]
func (h *ApiHandler) DeleteWhiteListIP(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
ipAddress := c.Param("ip")
if ipAddress == "" {
h.responseBuilder.BadRequest(c, "IP地址不能为空")
return
}
err := h.appService.DeleteWhiteListIP(c.Request.Context(), userID, ipAddress)
if err != nil {
h.logger.Error("删除白名单IP失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "删除白名单IP成功")
}
// EncryptParams 加密参数接口(用于前端调试)
// @Summary 加密参数
// @Description 用于前端调试时加密API调用参数
// @Tags API调试
// @Accept json
// @Produce json
// @Param request body commands.EncryptCommand true "加密请求"
// @Success 200 {object} dto.EncryptResponse "加密成功"
// @Failure 400 {object} dto.EncryptResponse "请求参数错误"
// @Failure 401 {object} dto.EncryptResponse "未授权"
// @Router /api/v1/encrypt [post]
func (h *ApiHandler) EncryptParams(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
var cmd commands.EncryptCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误")
return
}
// 调用应用服务层进行加密
encryptedData, err := h.appService.EncryptParams(c.Request.Context(), userID, &cmd)
if err != nil {
h.logger.Error("加密参数失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "加密参数失败")
return
}
response := dto.EncryptResponse{
EncryptedData: encryptedData,
}
h.responseBuilder.Success(c, response, "加密成功")
}
// DecryptParams 解密参数
// @Summary 解密参数
// @Description 使用密钥解密加密的数据
// @Tags API调试
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.DecryptCommand 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/decrypt [post]
func (h *ApiHandler) DecryptParams(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
var cmd commands.DecryptCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误")
return
}
// 调用应用服务层进行解密
decryptedData, err := h.appService.DecryptParams(c.Request.Context(), userID, &cmd)
if err != nil {
h.logger.Error("解密参数失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "解密参数失败")
return
}
h.responseBuilder.Success(c, decryptedData, "解密成功")
}
// GetFormConfig 获取指定API的表单配置
// @Summary 获取表单配置
// @Description 获取指定API的表单配置用于前端动态生成表单
// @Tags API调试
// @Accept json
// @Produce json
// @Security Bearer
// @Param api_code path string true "API代码"
// @Success 200 {object} map[string]interface{} "获取成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未授权"
// @Failure 404 {object} map[string]interface{} "API接口不存在"
// @Router /api/v1/form-config/{api_code} [get]
func (h *ApiHandler) GetFormConfig(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
apiCode := c.Param("api_code")
if apiCode == "" {
h.responseBuilder.BadRequest(c, "API代码不能为空")
return
}
h.logger.Info("获取表单配置", zap.String("api_code", apiCode), zap.String("user_id", userID))
// 获取表单配置
config, err := h.appService.GetFormConfig(c.Request.Context(), apiCode)
if err != nil {
h.logger.Error("获取表单配置失败", zap.String("api_code", apiCode), zap.String("user_id", userID), zap.Error(err))
h.responseBuilder.BadRequest(c, "获取表单配置失败")
return
}
if config == nil {
h.responseBuilder.BadRequest(c, "API接口不存在")
return
}
h.logger.Info("获取表单配置成功", zap.String("api_code", apiCode), zap.String("user_id", userID), zap.Int("field_count", len(config.Fields)))
h.responseBuilder.Success(c, config, "获取表单配置成功")
}
// getCurrentUserID 获取当前用户ID
func (h *ApiHandler) getCurrentUserID(c *gin.Context) string {
if userID, exists := c.Get("user_id"); exists {
if id, ok := userID.(string); ok {
return id
}
}
return ""
}
// GetUserApiCalls 获取用户API调用记录
// @Summary 获取用户API调用记录
// @Description 获取当前用户的API调用记录列表支持分页和筛选
// @Tags API管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param start_time query string false "开始时间 (格式: 2006-01-02 15:04:05)"
// @Param end_time query string false "结束时间 (格式: 2006-01-02 15:04:05)"
// @Param transaction_id query string false "交易ID"
// @Param product_name query string false "产品名称"
// @Param status query string false "状态 (pending/success/failed)"
// @Success 200 {object} dto.ApiCallListResponse "获取成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/my/api-calls [get]
func (h *ApiHandler) GetUserApiCalls(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
// 解析查询参数
page := h.getIntQuery(c, "page", 1)
pageSize := h.getIntQuery(c, "page_size", 10)
// 构建筛选条件
filters := make(map[string]interface{})
// 时间范围筛选
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
}
// 状态筛选
if status := c.Query("status"); status != "" {
filters["status"] = status
}
// 构建分页选项
options := interfaces.ListOptions{
Page: page,
PageSize: pageSize,
Sort: "created_at",
Order: "desc",
}
result, err := h.appService.GetUserApiCalls(c.Request.Context(), userID, filters, options)
if err != nil {
h.logger.Error("获取用户API调用记录失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "获取API调用记录失败")
return
}
h.responseBuilder.Success(c, result, "获取API调用记录成功")
}
// GetAdminApiCalls 获取管理端API调用记录
// @Summary 获取管理端API调用记录
// @Description 管理员获取API调用记录支持筛选和分页
// @Tags API管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param user_id query string false "用户ID"
// @Param transaction_id query string false "交易ID"
// @Param product_name query string false "产品名称"
// @Param status query string false "状态"
// @Param start_time query string false "开始时间" format(date-time)
// @Param end_time query string false "结束时间" format(date-time)
// @Param sort_by query string false "排序字段"
// @Param sort_order query string false "排序方向" Enums(asc, desc)
// @Success 200 {object} dto.ApiCallListResponse "获取API调用记录成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/api-calls [get]
func (h *ApiHandler) GetAdminApiCalls(c *gin.Context) {
// 解析查询参数
page := h.getIntQuery(c, "page", 1)
pageSize := h.getIntQuery(c, "page_size", 10)
// 构建筛选条件
filters := make(map[string]interface{})
// 用户ID筛选
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
}
// 状态筛选
if status := c.Query("status"); status != "" {
filters["status"] = status
}
// 构建分页选项
options := interfaces.ListOptions{
Page: page,
PageSize: pageSize,
Sort: "created_at",
Order: "desc",
}
result, err := h.appService.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调用记录
// @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 != "" {
if intValue, err := strconv.Atoi(value); err == nil && intValue > 0 {
return intValue
}
}
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{}, "测试短信发送成功")
}