This commit is contained in:
2025-07-28 01:46:39 +08:00
parent b03129667a
commit 357639462a
219 changed files with 21634 additions and 8138 deletions

View File

@@ -0,0 +1,322 @@
package handlers
import (
"encoding/json"
"strconv"
"time"
"tyapi-server/internal/application/api"
"tyapi-server/internal/application/api/commands"
"tyapi-server/internal/application/api/dto"
"tyapi-server/internal/shared/crypto"
"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)
response := dto.NewErrorResponse(errorCode, err.Error(), 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 := c.ShouldBindJSON(&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
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
}
// 获取用户的SecretKey
apiKeys, err := h.appService.GetUserApiKeys(c.Request.Context(), userID)
if err != nil {
h.logger.Error("获取用户API密钥失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "获取API密钥失败")
return
}
// 将JSON对象转换为字节数组
jsonData, err := json.Marshal(cmd.Data)
if err != nil {
h.logger.Error("序列化参数失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "参数序列化失败")
return
}
// 加密参数
encryptedData, err := crypto.AesEncrypt(jsonData, apiKeys.SecretKey)
if err != nil {
h.logger.Error("加密参数失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "加密参数失败")
return
}
response := dto.EncryptResponse{
EncryptedData: encryptedData,
}
h.responseBuilder.Success(c, response, "加密成功")
}
// 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调用记录成功")
}
// 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
}

View File

@@ -1,6 +1,11 @@
package handlers
import (
"bytes"
"encoding/json"
"io"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
@@ -38,38 +43,6 @@ func NewCertificationHandler(
}
// ================ 认证申请管理 ================
// CreateCertification 创建认证申请
// @Summary 创建认证申请
// @Description 为用户创建企业认证申请
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.CreateCertificationCommand true "创建认证申请请求"
// @Success 201 {object} responses.CertificationResponse "认证申请创建成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications [post]
func (h *CertificationHandler) CreateCertification(c *gin.Context) {
var cmd commands.CreateCertificationCommand
cmd.UserID = h.getCurrentUserID(c)
if cmd.UserID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
result, err := h.appService.CreateCertification(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("创建认证申请失败", zap.Error(err), zap.String("user_id", cmd.UserID))
h.response.BadRequest(c, err.Error())
return
}
h.response.Created(c, result, "认证申请创建成功")
}
// GetCertification 获取认证详情
// @Summary 获取认证详情
// @Description 根据认证ID获取认证详情
@@ -77,13 +50,12 @@ func (h *CertificationHandler) CreateCertification(c *gin.Context) {
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "认证ID"
// @Success 200 {object} responses.CertificationResponse "获取认证详情成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "认证记录不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/{id} [get]
// @Router /api/v1/certifications/details [get]
func (h *CertificationHandler) GetCertification(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
@@ -91,21 +63,14 @@ func (h *CertificationHandler) GetCertification(c *gin.Context) {
return
}
certificationID := c.Param("id")
if certificationID == "" {
h.response.BadRequest(c, "认证ID不能为空")
return
}
query := &queries.GetCertificationQuery{
CertificationID: certificationID,
UserID: userID,
UserID: userID,
}
result, err := h.appService.GetCertification(c.Request.Context(), query)
if err != nil {
h.logger.Error("获取认证详情失败", zap.Error(err), zap.String("certification_id", certificationID))
h.response.NotFound(c, "认证记录不存在")
h.logger.Error("获取认证详情失败", zap.Error(err), zap.String("user_id", userID))
h.response.BadRequest(c, err.Error())
return
}
@@ -121,14 +86,13 @@ func (h *CertificationHandler) GetCertification(c *gin.Context) {
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "认证ID"
// @Param request body commands.SubmitEnterpriseInfoCommand true "提交企业信息请求"
// @Success 200 {object} responses.CertificationResponse "企业信息提交成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "认证记录不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/{id}/enterprise-info [post]
// @Router /api/v1/certifications/enterprise-info [post]
func (h *CertificationHandler) SubmitEnterpriseInfo(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
@@ -136,22 +100,15 @@ func (h *CertificationHandler) SubmitEnterpriseInfo(c *gin.Context) {
return
}
certificationID := c.Param("id")
if certificationID == "" {
h.response.BadRequest(c, "认证ID不能为空")
return
}
var cmd commands.SubmitEnterpriseInfoCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
cmd.CertificationID = certificationID
cmd.UserID = userID
result, err := h.appService.SubmitEnterpriseInfo(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("提交企业信息失败", zap.Error(err), zap.String("certification_id", certificationID))
h.logger.Error("提交企业信息失败", zap.Error(err), zap.String("user_id", userID))
h.response.BadRequest(c, err.Error())
return
}
@@ -159,6 +116,69 @@ func (h *CertificationHandler) SubmitEnterpriseInfo(c *gin.Context) {
h.response.Success(c, result, "企业信息提交成功")
}
// ConfirmAuth 前端确认是否完成认证
// @Summary 前端确认认证状态
// @Description 前端轮询确认企业认证是否完成
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.ConfirmAuthCommand true "确认状态请求"
// @Success 200 {object} responses.ConfirmStatusResponse "状态确认成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "认证记录不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/confirm-auth [post]
func (h *CertificationHandler) ConfirmAuth(c *gin.Context) {
var cmd queries.ConfirmAuthCommand
cmd.UserID = h.getCurrentUserID(c)
if cmd.UserID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
result, err := h.appService.ConfirmAuth(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("确认认证/签署状态失败", zap.Error(err), zap.String("user_id", cmd.UserID))
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, result, "状态确认成功")
}
// ConfirmSign 前端确认是否完成签署
// @Summary 前端确认签署状态
// @Description 前端轮询确认合同签署是否完成
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.ConfirmSignCommand true "确认状态请求"
// @Success 200 {object} responses.ConfirmStatusResponse "状态确认成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "认证记录不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/confirm-sign [post]
func (h *CertificationHandler) ConfirmSign(c *gin.Context) {
var cmd queries.ConfirmSignCommand
cmd.UserID = h.getCurrentUserID(c)
if cmd.UserID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
result, err := h.appService.ConfirmSign(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("确认认证/签署状态失败", zap.Error(err), zap.String("user_id", cmd.UserID))
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, result, "状态确认成功")
}
// ================ 合同管理 ================
// ApplyContract 申请合同签署
@@ -176,21 +196,16 @@ func (h *CertificationHandler) SubmitEnterpriseInfo(c *gin.Context) {
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/apply-contract [post]
func (h *CertificationHandler) ApplyContract(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
var cmd commands.ApplyContractCommand
cmd.UserID = h.getCurrentUserID(c)
if cmd.UserID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
var cmd commands.ApplyContractCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
cmd.UserID = userID
result, err := h.appService.ApplyContract(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("申请合同失败", zap.Error(err), zap.String("certification_id", cmd.CertificationID))
h.logger.Error("申请合同失败", zap.Error(err), zap.String("user_id", cmd.UserID))
h.response.BadRequest(c, err.Error())
return
}
@@ -198,86 +213,6 @@ func (h *CertificationHandler) ApplyContract(c *gin.Context) {
h.response.Success(c, result, "合同申请成功")
}
// ================ 重试操作 ================
// RetryOperation 重试操作
// @Summary 重试操作
// @Description 重试失败的企业认证或合同申请操作
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.RetryOperationCommand true "重试操作请求"
// @Success 200 {object} responses.CertificationResponse "重试操作成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "认证记录不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/retry [post]
func (h *CertificationHandler) RetryOperation(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
var cmd commands.RetryOperationCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
cmd.UserID = userID
result, err := h.appService.RetryOperation(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("重试操作失败", zap.Error(err), zap.String("certification_id", cmd.CertificationID))
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, result, "重试操作成功")
}
// ================ 查询操作 ================
// GetUserCertifications 获取用户认证列表
// @Summary 获取用户认证列表
// @Description 获取当前用户的认证申请列表
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param status query string false "认证状态"
// @Param include_completed query bool false "是否包含已完成"
// @Param include_failed query bool false "是否包含失败"
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Success 200 {object} responses.CertificationListResponse "获取用户认证列表成功"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/user [get]
func (h *CertificationHandler) GetUserCertifications(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
var query queries.GetUserCertificationsQuery
if err := h.validator.BindAndValidate(c, &query); err != nil {
return
}
query.UserID = userID
result, err := h.appService.GetUserCertifications(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取用户认证列表失败", zap.Error(err), zap.String("user_id", userID))
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, result, "获取用户认证列表成功")
}
// ListCertifications 获取认证列表(管理员)
// @Summary 获取认证列表
// @Description 管理员获取认证申请列表
@@ -321,46 +256,6 @@ func (h *CertificationHandler) ListCertifications(c *gin.Context) {
h.response.Success(c, result, "获取认证列表成功")
}
// GetCertificationStatistics 获取认证统计
// @Summary 获取认证统计
// @Description 获取认证相关的统计数据
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param start_date query string true "开始日期" format(date)
// @Param end_date query string true "结束日期" format(date)
// @Param period query string false "统计周期" Enums(daily, weekly, monthly, yearly) default(daily)
// @Param group_by query []string false "分组字段"
// @Param user_ids query []string false "用户ID列表"
// @Param statuses query []string false "状态列表"
// @Success 200 {object} responses.CertificationStatisticsResponse "获取认证统计成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/statistics [get]
func (h *CertificationHandler) GetCertificationStatistics(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
var query queries.GetCertificationStatisticsQuery
if err := h.validator.BindAndValidate(c, &query); err != nil {
return
}
result, err := h.appService.GetCertificationStatistics(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取认证统计失败", zap.Error(err))
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, result, "获取认证统计成功")
}
// ================ 回调处理 ================
// HandleEsignCallback 处理e签宝回调
@@ -373,97 +268,118 @@ func (h *CertificationHandler) GetCertificationStatistics(c *gin.Context) {
// @Success 200 {object} responses.CallbackResponse "回调处理成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/callbacks [post]
// @Router /api/v1/certifications/callbacks/esign [post]
func (h *CertificationHandler) HandleEsignCallback(c *gin.Context) {
var cmd commands.EsignCallbackCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
// 记录请求基本信息
h.logger.Info("收到e签宝回调请求",
zap.String("method", c.Request.Method),
zap.String("url", c.Request.URL.String()),
zap.String("remote_addr", c.ClientIP()),
zap.String("user_agent", c.GetHeader("User-Agent")),
)
// 记录所有请求头
headers := make(map[string]string)
for key, values := range c.Request.Header {
if len(values) > 0 {
headers[key] = values[0]
}
}
h.logger.Info("回调请求头信息", zap.Any("headers", headers))
// 记录URL查询参数
queryParams := make(map[string]string)
for key, values := range c.Request.URL.Query() {
if len(values) > 0 {
queryParams[key] = values[0]
}
}
if len(queryParams) > 0 {
h.logger.Info("回调URL查询参数", zap.Any("query_params", queryParams))
}
result, err := h.appService.HandleEsignCallback(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("处理e签宝回调失败", zap.Error(err), zap.String("certification_id", cmd.CertificationID))
h.response.BadRequest(c, err.Error())
return
// 读取并记录请求体
var callbackData *commands.EsignCallbackData
if c.Request.Body != nil {
bodyBytes, err := c.GetRawData()
if err != nil {
h.logger.Error("读取回调请求体失败", zap.Error(err))
h.response.BadRequest(c, "读取请求体失败")
return
}
if err := json.Unmarshal(bodyBytes, &callbackData); err != nil {
h.logger.Error("回调请求体不是有效的JSON格式", zap.Error(err))
h.response.BadRequest(c, "请求体格式错误")
return
}
h.logger.Info("回调请求体内容", zap.Any("body", callbackData))
// 如果后续还需要用 c.Request.Body
c.Request.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
}
h.response.Success(c, result, "回调处理成功")
}
// 记录Content-Type
contentType := c.GetHeader("Content-Type")
h.logger.Info("回调请求Content-Type", zap.String("content_type", contentType))
// ================ 管理员操作 ================
// ForceTransitionStatus 强制状态转换(管理员)
// @Summary 强制状态转换
// @Description 管理员强制转换认证状态
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.ForceTransitionStatusCommand true "强制状态转换请求"
// @Success 200 {object} responses.CertificationResponse "状态转换成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 403 {object} map[string]interface{} "权限不足"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/force-transition [post]
func (h *CertificationHandler) ForceTransitionStatus(c *gin.Context) {
adminID := h.getCurrentUserID(c)
if adminID == "" {
h.response.Unauthorized(c, "用户未登录")
return
// 记录Content-Length
contentLength := c.GetHeader("Content-Length")
if contentLength != "" {
h.logger.Info("回调请求Content-Length", zap.String("content_length", contentLength))
}
var cmd commands.ForceTransitionStatusCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
cmd.AdminID = adminID
// 记录时间戳
h.logger.Info("回调请求时间",
zap.Time("request_time", time.Now()),
zap.String("request_id", c.GetHeader("X-Request-ID")),
)
result, err := h.appService.ForceTransitionStatus(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("强制状态转换失败", zap.Error(err), zap.String("certification_id", cmd.CertificationID))
h.response.BadRequest(c, err.Error())
return
// 记录完整的请求信息摘要
h.logger.Info("e签宝回调完整信息摘要",
zap.String("method", c.Request.Method),
zap.String("url", c.Request.URL.String()),
zap.String("client_ip", c.ClientIP()),
zap.String("content_type", contentType),
zap.Any("headers", headers),
zap.Any("query_params", queryParams),
zap.Any("body", callbackData),
)
// 处理回调数据
if callbackData != nil {
// 构建请求头映射
headers := make(map[string]string)
for key, values := range c.Request.Header {
if len(values) > 0 {
headers[key] = values[0]
}
}
// 构建查询参数映射
queryParams := make(map[string]string)
for key, values := range c.Request.URL.Query() {
if len(values) > 0 {
queryParams[key] = values[0]
}
}
if err := h.appService.HandleEsignCallback(c.Request.Context(), &commands.EsignCallbackCommand{
Data: callbackData,
Headers: headers,
QueryParams: queryParams,
}); err != nil {
h.logger.Error("处理e签宝回调失败", zap.Error(err))
h.response.BadRequest(c, "回调处理失败: "+err.Error())
return
}
}
h.response.Success(c, result, "状态转换成功")
}
// GetSystemMonitoring 获取系统监控数据
// @Summary 获取系统监控数据
// @Description 获取认证系统的监控数据
// @Tags 认证管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param time_range query string false "时间范围" Enums(1h, 6h, 24h, 7d, 30d) default(24h)
// @Param metrics query []string false "监控指标"
// @Success 200 {object} responses.SystemMonitoringResponse "获取系统监控数据成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 403 {object} map[string]interface{} "权限不足"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/certifications/monitoring [get]
func (h *CertificationHandler) GetSystemMonitoring(c *gin.Context) {
userID := h.getCurrentUserID(c)
if userID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
var query queries.GetSystemMonitoringQuery
if err := h.validator.BindAndValidate(c, &query); err != nil {
return
}
result, err := h.appService.GetSystemMonitoring(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取系统监控数据失败", zap.Error(err))
h.response.BadRequest(c, err.Error())
return
}
h.response.Success(c, result, "获取系统监控数据成功")
// 返回成功响应
c.JSON(200, map[string]interface{}{
"code": "200",
"msg": "success",
})
}
// ================ 辅助方法 ================

View File

@@ -1,6 +1,11 @@
package handlers
import (
"fmt"
"net/http"
"strconv"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
@@ -33,34 +38,6 @@ func NewFinanceHandler(
}
}
// CreateWallet 创建钱包
// @Summary 创建钱包
// @Description 为用户创建新的钱包账户
// @Tags 钱包管理
// @Accept json
// @Produce json
// @Param request body commands.CreateWalletCommand true "创建钱包请求"
// @Success 201 {object} responses.WalletResponse "钱包创建成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 409 {object} map[string]interface{} "钱包已存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/wallet [post]
func (h *FinanceHandler) CreateWallet(c *gin.Context) {
var cmd commands.CreateWalletCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
response, err := h.appService.CreateWallet(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("创建钱包失败", zap.Error(err))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Created(c, response, "钱包创建成功")
}
// GetWallet 获取钱包信息
// @Summary 获取钱包信息
// @Description 获取当前用户的钱包详细信息
@@ -94,333 +71,486 @@ func (h *FinanceHandler) GetWallet(c *gin.Context) {
h.responseBuilder.Success(c, result, "获取钱包信息成功")
}
// UpdateWallet 更新钱包
// @Summary 更新钱包信息
// @Description 更新当前用户的钱包基本信息
// GetUserWalletTransactions 获取用户钱包交易记录
// @Summary 获取用户钱包交易记录
// @Description 获取当前用户的钱包交易记录列表,支持分页和筛选
// @Tags 钱包管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.UpdateWalletCommand true "更新钱包请求"
// @Success 200 {object} map[string]interface{} "钱包更新成功"
// @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 min_amount query string false "最小金额"
// @Param max_amount query string false "最大金额"
// @Success 200 {object} responses.WalletTransactionListResponse "获取成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/wallet [put]
func (h *FinanceHandler) UpdateWallet(c *gin.Context) {
// @Router /api/v1/finance/wallet/transactions [get]
func (h *FinanceHandler) GetUserWalletTransactions(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
var cmd commands.UpdateWalletCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
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
}
}
cmd.UserID = userID
// 交易ID筛选
if transactionId := c.Query("transaction_id"); transactionId != "" {
filters["transaction_id"] = transactionId
}
err := h.appService.UpdateWallet(c.Request.Context(), &cmd)
// 产品名称筛选
if productName := c.Query("product_name"); productName != "" {
filters["product_name"] = productName
}
// 金额范围筛选
if minAmount := c.Query("min_amount"); minAmount != "" {
filters["min_amount"] = minAmount
}
if maxAmount := c.Query("max_amount"); maxAmount != "" {
filters["max_amount"] = maxAmount
}
// 构建分页选项
options := interfaces.ListOptions{
Page: page,
PageSize: pageSize,
Sort: "created_at",
Order: "desc",
}
result, err := h.appService.GetUserWalletTransactions(c.Request.Context(), userID, filters, options)
if err != nil {
h.logger.Error("更新钱包失败",
zap.String("user_id", userID),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, err.Error())
h.logger.Error("获取用户钱包交易记录失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "获取钱包交易记录失败")
return
}
h.responseBuilder.Success(c, nil, "钱包更新成功")
h.responseBuilder.Success(c, result, "获取钱包交易记录成功")
}
// Recharge 充值
// @Summary 钱包充值
// @Description 为钱包进行充值操作
// getIntQuery 获取整数查询参数
func (h *FinanceHandler) 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
}
// HandleAlipayCallback 处理支付宝支付回调
// @Summary 支付宝支付回调
// @Description 处理支付宝异步支付通知
// @Tags 支付管理
// @Accept application/x-www-form-urlencoded
// @Produce text/plain
// @Success 200 {string} string "success"
// @Failure 400 {string} string "fail"
// @Router /api/v1/finance/alipay/callback [post]
func (h *FinanceHandler) HandleAlipayCallback(c *gin.Context) {
// 记录回调请求信息
h.logger.Info("收到支付宝回调请求",
zap.String("method", c.Request.Method),
zap.String("url", c.Request.URL.String()),
zap.String("remote_addr", c.ClientIP()),
zap.String("user_agent", c.GetHeader("User-Agent")),
)
// 通过应用服务处理支付宝回调
err := h.appService.HandleAlipayCallback(c.Request.Context(), c.Request)
if err != nil {
h.logger.Error("支付宝回调处理失败", zap.Error(err))
c.String(400, "fail")
return
}
// 返回成功响应支付宝要求返回success
c.String(200, "success")
}
// HandleAlipayReturn 处理支付宝同步回调
// @Summary 支付宝同步回调
// @Description 处理支付宝同步支付通知,跳转到前端成功页面
// @Tags 支付管理
// @Accept application/x-www-form-urlencoded
// @Produce text/html
// @Success 200 {string} string "支付成功页面"
// @Failure 400 {string} string "支付失败页面"
// @Router /api/v1/finance/alipay/return [get]
func (h *FinanceHandler) HandleAlipayReturn(c *gin.Context) {
// 记录同步回调请求信息
h.logger.Info("收到支付宝同步回调请求",
zap.String("method", c.Request.Method),
zap.String("url", c.Request.URL.String()),
zap.String("remote_addr", c.ClientIP()),
zap.String("user_agent", c.GetHeader("User-Agent")),
)
// 获取查询参数
outTradeNo := c.Query("out_trade_no")
tradeNo := c.Query("trade_no")
totalAmount := c.Query("total_amount")
h.logger.Info("支付宝同步回调参数",
zap.String("out_trade_no", outTradeNo),
zap.String("trade_no", tradeNo),
zap.String("total_amount", totalAmount),
)
// 验证必要参数
if outTradeNo == "" {
h.logger.Error("支付宝同步回调缺少商户订单号")
h.redirectToFailPage(c, "", "缺少商户订单号")
return
}
// 通过应用服务处理同步回调,查询订单状态
orderStatus, err := h.appService.HandleAlipayReturn(c.Request.Context(), outTradeNo)
if err != nil {
h.logger.Error("支付宝同步回调处理失败",
zap.String("out_trade_no", outTradeNo),
zap.Error(err))
h.redirectToFailPage(c, outTradeNo, "订单处理失败")
return
}
// 根据环境确定前端域名
frontendDomain := "https://www.tianyuanapi.com"
if gin.Mode() == gin.DebugMode {
frontendDomain = "http://localhost:5173"
}
// 根据订单状态跳转到相应页面
switch orderStatus {
case "TRADE_SUCCESS":
// 支付成功,跳转到前端成功页面
successURL := fmt.Sprintf("%s/finance/wallet/success?out_trade_no=%s&trade_no=%s&amount=%s",
frontendDomain, outTradeNo, tradeNo, totalAmount)
c.Redirect(http.StatusFound, successURL)
case "WAIT_BUYER_PAY":
// 支付处理中,跳转到处理中页面
h.redirectToProcessingPage(c, outTradeNo, totalAmount)
default:
// 支付失败或取消,跳转到前端失败页面
h.redirectToFailPage(c, outTradeNo, orderStatus)
}
}
// redirectToFailPage 跳转到失败页面
func (h *FinanceHandler) redirectToFailPage(c *gin.Context, outTradeNo, reason string) {
frontendDomain := "https://www.tianyuanapi.com"
if gin.Mode() == gin.DebugMode {
frontendDomain = "http://localhost:5173"
}
failURL := fmt.Sprintf("%s/finance/wallet/fail?out_trade_no=%s&reason=%s",
frontendDomain, outTradeNo, reason)
c.Redirect(http.StatusFound, failURL)
}
// redirectToProcessingPage 跳转到处理中页面
func (h *FinanceHandler) redirectToProcessingPage(c *gin.Context, outTradeNo, amount string) {
frontendDomain := "https://www.tianyuanapi.com"
if gin.Mode() == gin.DebugMode {
frontendDomain = "http://localhost:5173"
}
processingURL := fmt.Sprintf("%s/finance/wallet/processing?out_trade_no=%s&amount=%s",
frontendDomain, outTradeNo, amount)
c.Redirect(http.StatusFound, processingURL)
}
// CreateAlipayRecharge 创建支付宝充值订单
// @Summary 创建支付宝充值订单
// @Description 创建支付宝充值订单并返回支付链接
// @Tags 钱包管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.RechargeWalletCommand true "充值请求"
// @Success 200 {object} responses.TransactionResponse "充值成功"
// @Param request body commands.CreateAlipayRechargeCommand true "充值请求"
// @Success 200 {object} responses.AlipayRechargeOrderResponse "创建充值订单成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/wallet/recharge [post]
func (h *FinanceHandler) Recharge(c *gin.Context) {
// @Router /api/v1/finance/wallet/alipay-recharge [post]
func (h *FinanceHandler) CreateAlipayRecharge(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
var cmd commands.RechargeWalletCommand
var cmd commands.CreateAlipayRechargeCommand
cmd.UserID = userID
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
cmd.UserID = userID
result, err := h.appService.Recharge(c.Request.Context(), &cmd)
// 调用应用服务进行完整的业务流程编排
result, err := h.appService.CreateAlipayRechargeOrder(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("充值失败",
h.logger.Error("创建支付宝充值订单失败",
zap.String("user_id", userID),
zap.String("amount", cmd.Amount),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, "创建支付宝充值订单失败: "+err.Error())
return
}
h.logger.Info("支付宝充值订单创建成功",
zap.String("user_id", userID),
zap.String("out_trade_no", result.OutTradeNo),
zap.String("amount", cmd.Amount),
zap.String("platform", cmd.Platform),
)
// 返回支付链接和订单信息
h.responseBuilder.Success(c, result, "支付宝充值订单创建成功")
}
// TransferRecharge 管理员对公转账充值
func (h *FinanceHandler) TransferRecharge(c *gin.Context) {
var cmd commands.TransferRechargeCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
if cmd.UserID == "" {
h.responseBuilder.BadRequest(c, "缺少用户ID")
return
}
result, err := h.appService.TransferRecharge(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("对公转账充值失败",
zap.String("user_id", cmd.UserID),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "充值成功")
h.responseBuilder.Success(c, result, "对公转账充值成功")
}
// Withdraw 提现
// @Summary 钱包提现
// @Description 从钱包进行提现操作
// GiftRecharge 管理员赠送充值
func (h *FinanceHandler) GiftRecharge(c *gin.Context) {
var cmd commands.GiftRechargeCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
if cmd.UserID == "" {
h.responseBuilder.BadRequest(c, "缺少用户ID")
return
}
result, err := h.appService.GiftRecharge(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("赠送充值失败",
zap.String("user_id", cmd.UserID),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "赠送充值成功")
}
// GetUserRechargeRecords 用户获取自己充值记录分页
func (h *FinanceHandler) GetUserRechargeRecords(c *gin.Context) {
userID := c.GetString("user_id")
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
}
}
// 充值类型筛选
if rechargeType := c.Query("recharge_type"); rechargeType != "" {
filters["recharge_type"] = rechargeType
}
// 状态筛选
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.GetUserRechargeRecords(c.Request.Context(), userID, filters, options)
if err != nil {
h.logger.Error("获取用户充值记录失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "获取充值记录失败")
return
}
h.responseBuilder.Success(c, result, "获取充值记录成功")
}
// GetAdminRechargeRecords 管理员获取充值记录分页
func (h *FinanceHandler) GetAdminRechargeRecords(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
}
}
// 充值类型筛选
if rechargeType := c.Query("recharge_type"); rechargeType != "" {
filters["recharge_type"] = rechargeType
}
// 状态筛选
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.GetAdminRechargeRecords(c.Request.Context(), filters, options)
if err != nil {
h.logger.Error("获取充值记录失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "获取充值记录失败")
return
}
h.responseBuilder.Success(c, result, "获取充值记录成功")
}
// GetRechargeConfig 获取充值配置
// @Summary 获取充值配置
// @Description 获取当前环境的充值配置信息(最低充值金额、最高充值金额等)
// @Tags 钱包管理
// @Accept json
// @Produce json
// @Success 200 {object} responses.RechargeConfigResponse "获取充值配置成功"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/wallet/recharge-config [get]
func (h *FinanceHandler) GetRechargeConfig(c *gin.Context) {
result, err := h.appService.GetRechargeConfig(c.Request.Context())
if err != nil {
h.logger.Error("获取充值配置失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "获取充值配置失败")
return
}
h.responseBuilder.Success(c, result, "获取充值配置成功")
}
// GetAlipayOrderStatus 获取支付宝订单状态
// @Summary 获取支付宝订单状态
// @Description 获取支付宝订单的当前状态,用于轮询查询
// @Tags 钱包管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.WithdrawWalletCommand true "提现请求"
// @Success 200 {object} responses.TransactionResponse "提现申请已提交"
// @Param out_trade_no query string true "商户订单号"
// @Success 200 {object} responses.AlipayOrderStatusResponse "获取订单状态成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "订单不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/wallet/withdraw [post]
func (h *FinanceHandler) Withdraw(c *gin.Context) {
// @Router /api/v1/finance/wallet/alipay-order-status [get]
func (h *FinanceHandler) GetAlipayOrderStatus(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
var cmd commands.WithdrawWalletCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
outTradeNo := c.Query("out_trade_no")
if outTradeNo == "" {
h.responseBuilder.BadRequest(c, "缺少商户订单号")
return
}
cmd.UserID = userID
result, err := h.appService.Withdraw(c.Request.Context(), &cmd)
result, err := h.appService.GetAlipayOrderStatus(c.Request.Context(), outTradeNo)
if err != nil {
h.logger.Error("提现失败",
h.logger.Error("获取支付宝订单状态失败",
zap.String("user_id", userID),
zap.String("out_trade_no", outTradeNo),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, err.Error())
h.responseBuilder.BadRequest(c, "获取订单状态失败: "+err.Error())
return
}
h.responseBuilder.Success(c, result, "提现申请已提交")
}
// WalletTransaction 钱包交易
// @Summary 钱包交易
// @Description 执行钱包内部交易操作
// @Tags 钱包管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.WalletTransactionCommand true "交易请求"
// @Success 200 {object} responses.TransactionResponse "交易成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/wallet/transaction [post]
func (h *FinanceHandler) WalletTransaction(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
var cmd commands.WalletTransactionCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
cmd.UserID = userID
result, err := h.appService.WalletTransaction(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("钱包交易失败",
zap.String("user_id", userID),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "交易成功")
}
// GetWalletStats 获取钱包统计
// @Summary 获取钱包统计
// @Description 获取钱包相关的统计数据
// @Tags 钱包管理
// @Accept json
// @Produce json
// @Security Bearer
// @Success 200 {object} responses.WalletStatsResponse "获取钱包统计成功"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/wallet/stats [get]
func (h *FinanceHandler) GetWalletStats(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
result, err := h.appService.GetWalletStats(c.Request.Context())
if err != nil {
h.logger.Error("获取钱包统计失败",
zap.String("user_id", userID),
zap.Error(err),
)
h.responseBuilder.InternalError(c, "获取钱包统计失败")
return
}
h.responseBuilder.Success(c, result, "获取钱包统计成功")
}
// CreateUserSecrets 创建用户密钥
// @Summary 创建用户密钥
// @Description 为用户创建API访问密钥
// @Tags 用户密钥管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param request body commands.CreateUserSecretsCommand true "创建密钥请求"
// @Success 201 {object} responses.UserSecretsResponse "用户密钥创建成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 409 {object} map[string]interface{} "密钥已存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/secrets [post]
func (h *FinanceHandler) CreateUserSecrets(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
var cmd commands.CreateUserSecretsCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
cmd.UserID = userID
result, err := h.appService.CreateUserSecrets(c.Request.Context(), &cmd)
if err != nil {
h.logger.Error("创建用户密钥失败",
zap.String("user_id", userID),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Created(c, result, "用户密钥创建成功")
}
// GetUserSecrets 获取用户密钥
// @Summary 获取用户密钥
// @Description 获取当前用户的API访问密钥信息
// @Tags 用户密钥管理
// @Accept json
// @Produce json
// @Security Bearer
// @Success 200 {object} responses.UserSecretsResponse "获取用户密钥成功"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "密钥不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/secrets [get]
func (h *FinanceHandler) GetUserSecrets(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
query := &queries.GetUserSecretsQuery{UserID: userID}
result, err := h.appService.GetUserSecrets(c.Request.Context(), query)
if err != nil {
h.logger.Error("获取用户密钥失败",
zap.String("user_id", userID),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "获取用户密钥成功")
}
// RegenerateAccessKey 重新生成访问密钥
// @Summary 重新生成访问密钥
// @Description 重新生成用户的API访问密钥
// @Tags 用户密钥管理
// @Accept json
// @Produce json
// @Security Bearer
// @Success 200 {object} responses.UserSecretsResponse "访问密钥重新生成成功"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "密钥不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/secrets/regenerate [post]
func (h *FinanceHandler) RegenerateAccessKey(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
cmd := &commands.RegenerateAccessKeyCommand{UserID: userID}
result, err := h.appService.RegenerateAccessKey(c.Request.Context(), cmd)
if err != nil {
h.logger.Error("重新生成访问密钥失败",
zap.String("user_id", userID),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "访问密钥重新生成成功")
}
// DeactivateUserSecrets 停用用户密钥
// @Summary 停用用户密钥
// @Description 停用用户的API访问密钥
// @Tags 用户密钥管理
// @Accept json
// @Produce json
// @Security Bearer
// @Success 200 {object} map[string]interface{} "用户密钥停用成功"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "密钥不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/finance/secrets/deactivate [post]
func (h *FinanceHandler) DeactivateUserSecrets(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
cmd := &commands.DeactivateUserSecretsCommand{UserID: userID}
err := h.appService.DeactivateUserSecrets(c.Request.Context(), cmd)
if err != nil {
h.logger.Error("停用用户密钥失败",
zap.String("user_id", userID),
zap.Error(err),
)
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "用户密钥停用成功")
h.responseBuilder.Success(c, result, "获取订单状态成功")
}

View File

@@ -1,9 +1,11 @@
package handlers
import (
"strconv"
"tyapi-server/internal/application/product"
"tyapi-server/internal/application/product/dto/commands"
"tyapi-server/internal/application/product/dto/queries"
"tyapi-server/internal/application/product/dto/responses"
"tyapi-server/internal/shared/interfaces"
"github.com/gin-gonic/gin"
@@ -84,7 +86,9 @@ func (h *ProductAdminHandler) CreateProduct(c *gin.Context) {
// @Router /api/v1/admin/products/{id} [put]
func (h *ProductAdminHandler) UpdateProduct(c *gin.Context) {
var cmd commands.UpdateProductCommand
if err := h.validator.ValidateParam(c, &cmd); err != nil {
cmd.ID = c.Param("id")
if cmd.ID == "" {
h.responseBuilder.BadRequest(c, "产品ID不能为空")
return
}
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
@@ -254,7 +258,7 @@ func (h *ProductAdminHandler) UpdateSubscriptionPrice(c *gin.Context) {
// ListProducts 获取产品列表(管理员)
// @Summary 获取产品列表
// @Description 管理员获取产品列表,支持筛选
// @Description 管理员获取产品列表,支持筛选和分页
// @Tags 产品管理
// @Accept json
// @Produce json
@@ -263,30 +267,76 @@ func (h *ProductAdminHandler) UpdateSubscriptionPrice(c *gin.Context) {
// @Param page_size query int false "每页数量" default(10)
// @Param keyword query string false "搜索关键词"
// @Param category_id query string false "分类ID"
// @Param status query string false "产品状态"
// @Param is_enabled query bool false "是否启用"
// @Param is_visible query bool false "是否可见"
// @Param is_package query bool false "是否组合包"
// @Param sort_by query string false "排序字段"
// @Param sort_order query string false "排序方向" Enums(asc, desc)
// @Success 200 {object} responses.ProductListResponse "获取产品列表成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products [get]
func (h *ProductAdminHandler) ListProducts(c *gin.Context) {
var query queries.ListProductsQuery
if err := h.validator.ValidateQuery(c, &query); err != nil {
return
// 解析查询参数
page := h.getIntQuery(c, "page", 1)
pageSize := h.getIntQuery(c, "page_size", 10)
// 构建筛选条件
filters := make(map[string]interface{})
// 搜索关键词筛选
if keyword := c.Query("keyword"); keyword != "" {
filters["keyword"] = keyword
}
// 设置默认值
if query.Page <= 0 {
query.Page = 1
}
if query.PageSize <= 0 {
query.PageSize = 10
}
if query.PageSize > 100 {
query.PageSize = 100
// 分类ID筛选
if categoryID := c.Query("category_id"); categoryID != "" {
filters["category_id"] = categoryID
}
result, err := h.productAppService.ListProducts(c.Request.Context(), &query)
// 启用状态筛选
if isEnabled := c.Query("is_enabled"); isEnabled != "" {
if enabled, err := strconv.ParseBool(isEnabled); err == nil {
filters["is_enabled"] = enabled
}
}
// 可见状态筛选
if isVisible := c.Query("is_visible"); isVisible != "" {
if visible, err := strconv.ParseBool(isVisible); err == nil {
filters["is_visible"] = visible
}
}
// 产品类型筛选
if isPackage := c.Query("is_package"); isPackage != "" {
if pkg, err := strconv.ParseBool(isPackage); err == nil {
filters["is_package"] = pkg
}
}
// 排序字段
sortBy := c.Query("sort_by")
if sortBy == "" {
sortBy = "created_at"
}
// 排序方向
sortOrder := c.Query("sort_order")
if sortOrder == "" {
sortOrder = "desc"
}
// 构建分页选项
options := interfaces.ListOptions{
Page: page,
PageSize: pageSize,
Sort: sortBy,
Order: sortOrder,
}
result, err := h.productAppService.ListProducts(c.Request.Context(), filters, options)
if err != nil {
h.logger.Error("获取产品列表失败", zap.Error(err))
h.responseBuilder.InternalError(c, "获取产品列表失败")
@@ -296,6 +346,16 @@ func (h *ProductAdminHandler) ListProducts(c *gin.Context) {
h.responseBuilder.Success(c, result, "获取产品列表成功")
}
// getIntQuery 获取整数查询参数
func (h *ProductAdminHandler) 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
}
// GetProductDetail 获取产品详情(管理员)
// @Summary 获取产品详情
// @Description 管理员获取产品详细信息
@@ -329,6 +389,233 @@ func (h *ProductAdminHandler) GetProductDetail(c *gin.Context) {
h.responseBuilder.Success(c, result, "获取产品详情成功")
}
// GetAvailableProducts 获取可选子产品列表
// @Summary 获取可选子产品列表
// @Description 管理员获取可选作组合包子产品的产品列表
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param exclude_package_id query string false "排除的组合包ID"
// @Param keyword query string false "搜索关键词"
// @Param category_id query string false "分类ID"
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(20)
// @Success 200 {object} responses.ProductListResponse "获取可选产品列表成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/available [get]
func (h *ProductAdminHandler) GetAvailableProducts(c *gin.Context) {
var query queries.GetAvailableProductsQuery
if err := h.validator.ValidateQuery(c, &query); err != nil {
return
}
// 设置默认值
if query.Page <= 0 {
query.Page = 1
}
if query.PageSize <= 0 {
query.PageSize = 20
}
if query.PageSize > 100 {
query.PageSize = 100
}
result, err := h.productAppService.GetAvailableProducts(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取可选产品列表失败", zap.Error(err))
h.responseBuilder.InternalError(c, "获取可选产品列表失败")
return
}
h.responseBuilder.Success(c, result, "获取可选产品列表成功")
}
// AddPackageItem 添加组合包子产品
// @Summary 添加组合包子产品
// @Description 管理员向组合包添加子产品
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "组合包ID"
// @Param command body commands.AddPackageItemCommand true "添加子产品命令"
// @Success 200 {object} map[string]interface{} "添加成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "产品不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/{id}/package-items [post]
func (h *ProductAdminHandler) AddPackageItem(c *gin.Context) {
packageID := c.Param("id")
if packageID == "" {
h.responseBuilder.BadRequest(c, "组合包ID不能为空")
return
}
var cmd commands.AddPackageItemCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
err := h.productAppService.AddPackageItem(c.Request.Context(), packageID, &cmd)
if err != nil {
h.logger.Error("添加组合包子产品失败", zap.Error(err), zap.String("package_id", packageID))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "添加组合包子产品成功")
}
// UpdatePackageItem 更新组合包子产品
// @Summary 更新组合包子产品
// @Description 管理员更新组合包子产品信息
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "组合包ID"
// @Param item_id path string true "子产品项目ID"
// @Param command body commands.UpdatePackageItemCommand true "更新子产品命令"
// @Success 200 {object} map[string]interface{} "更新成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "产品不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/{id}/package-items/{item_id} [put]
func (h *ProductAdminHandler) UpdatePackageItem(c *gin.Context) {
packageID := c.Param("id")
itemID := c.Param("item_id")
if packageID == "" || itemID == "" {
h.responseBuilder.BadRequest(c, "参数不能为空")
return
}
var cmd commands.UpdatePackageItemCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
err := h.productAppService.UpdatePackageItem(c.Request.Context(), packageID, itemID, &cmd)
if err != nil {
h.logger.Error("更新组合包子产品失败", zap.Error(err), zap.String("package_id", packageID), zap.String("item_id", itemID))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "更新组合包子产品成功")
}
// RemovePackageItem 移除组合包子产品
// @Summary 移除组合包子产品
// @Description 管理员从组合包移除子产品
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "组合包ID"
// @Param item_id path string true "子产品项目ID"
// @Success 200 {object} map[string]interface{} "移除成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "产品不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/{id}/package-items/{item_id} [delete]
func (h *ProductAdminHandler) RemovePackageItem(c *gin.Context) {
packageID := c.Param("id")
itemID := c.Param("item_id")
if packageID == "" || itemID == "" {
h.responseBuilder.BadRequest(c, "参数不能为空")
return
}
err := h.productAppService.RemovePackageItem(c.Request.Context(), packageID, itemID)
if err != nil {
h.logger.Error("移除组合包子产品失败", zap.Error(err), zap.String("package_id", packageID), zap.String("item_id", itemID))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "移除组合包子产品成功")
}
// ReorderPackageItems 重新排序组合包子产品
// @Summary 重新排序组合包子产品
// @Description 管理员重新排序组合包子产品
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "组合包ID"
// @Param command body commands.ReorderPackageItemsCommand true "重新排序命令"
// @Success 200 {object} map[string]interface{} "排序成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "产品不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/{id}/package-items/reorder [put]
func (h *ProductAdminHandler) ReorderPackageItems(c *gin.Context) {
packageID := c.Param("id")
if packageID == "" {
h.responseBuilder.BadRequest(c, "组合包ID不能为空")
return
}
var cmd commands.ReorderPackageItemsCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
err := h.productAppService.ReorderPackageItems(c.Request.Context(), packageID, &cmd)
if err != nil {
h.logger.Error("重新排序组合包子产品失败", zap.Error(err), zap.String("package_id", packageID))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "重新排序组合包子产品成功")
}
// UpdatePackageItems 批量更新组合包子产品
// @Summary 批量更新组合包子产品
// @Description 管理员批量更新组合包子产品配置
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "组合包ID"
// @Param command body commands.UpdatePackageItemsCommand true "批量更新命令"
// @Success 200 {object} map[string]interface{} "更新成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "产品不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/{id}/package-items/batch [put]
func (h *ProductAdminHandler) UpdatePackageItems(c *gin.Context) {
packageID := c.Param("id")
if packageID == "" {
h.responseBuilder.BadRequest(c, "组合包ID不能为空")
return
}
var cmd commands.UpdatePackageItemsCommand
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
return
}
err := h.productAppService.UpdatePackageItems(c.Request.Context(), packageID, &cmd)
if err != nil {
h.logger.Error("批量更新组合包子产品失败", zap.Error(err), zap.String("package_id", packageID))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "批量更新组合包子产品成功")
}
// ListCategories 获取分类列表(管理员)
// @Summary 获取分类列表
// @Description 管理员获取产品分类列表
@@ -467,3 +754,166 @@ func (h *ProductAdminHandler) GetSubscriptionStats(c *gin.Context) {
h.responseBuilder.Success(c, result, "获取订阅统计成功")
}
// GetProductApiConfig 获取产品API配置
// @Summary 获取产品API配置
// @Description 管理员获取产品的API配置信息如果不存在则返回空配置
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "产品ID"
// @Success 200 {object} responses.ProductApiConfigResponse "获取API配置成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "产品不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/{id}/api-config [get]
func (h *ProductAdminHandler) GetProductApiConfig(c *gin.Context) {
productID := c.Param("id")
if productID == "" {
h.responseBuilder.BadRequest(c, "产品ID不能为空")
return
}
result, err := h.productAppService.GetProductApiConfig(c.Request.Context(), productID)
if err != nil {
// 如果是配置不存在的错误,返回空配置而不是错误
if err.Error() == "record not found" || err.Error() == "产品API配置不存在" {
// 返回空的配置结构,让前端可以创建新配置
emptyConfig := &responses.ProductApiConfigResponse{
ID: "",
ProductID: productID,
RequestParams: []responses.RequestParamResponse{},
ResponseFields: []responses.ResponseFieldResponse{},
ResponseExample: map[string]interface{}{},
}
h.responseBuilder.Success(c, emptyConfig, "获取API配置成功")
return
}
h.logger.Error("获取产品API配置失败", zap.Error(err), zap.String("product_id", productID))
h.responseBuilder.NotFound(c, "产品不存在")
return
}
h.responseBuilder.Success(c, result, "获取API配置成功")
}
// CreateProductApiConfig 创建产品API配置
// @Summary 创建产品API配置
// @Description 管理员为产品创建API配置
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "产品ID"
// @Param request body responses.ProductApiConfigResponse true "API配置信息"
// @Success 201 {object} map[string]interface{} "API配置创建成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 409 {object} map[string]interface{} "API配置已存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/{id}/api-config [post]
func (h *ProductAdminHandler) CreateProductApiConfig(c *gin.Context) {
productID := c.Param("id")
if productID == "" {
h.responseBuilder.BadRequest(c, "产品ID不能为空")
return
}
var configResponse responses.ProductApiConfigResponse
if err := h.validator.BindAndValidate(c, &configResponse); err != nil {
return
}
if err := h.productAppService.CreateProductApiConfig(c.Request.Context(), productID, &configResponse); err != nil {
h.logger.Error("创建产品API配置失败", zap.Error(err), zap.String("product_id", productID))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Created(c, nil, "API配置创建成功")
}
// UpdateProductApiConfig 更新产品API配置
// @Summary 更新产品API配置
// @Description 管理员更新产品的API配置
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "产品ID"
// @Param request body responses.ProductApiConfigResponse true "API配置信息"
// @Success 200 {object} map[string]interface{} "API配置更新成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "产品或配置不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/{id}/api-config [put]
func (h *ProductAdminHandler) UpdateProductApiConfig(c *gin.Context) {
productID := c.Param("id")
if productID == "" {
h.responseBuilder.BadRequest(c, "产品ID不能为空")
return
}
var configResponse responses.ProductApiConfigResponse
if err := h.validator.BindAndValidate(c, &configResponse); err != nil {
return
}
// 先获取现有配置以获取配置ID
existingConfig, err := h.productAppService.GetProductApiConfig(c.Request.Context(), productID)
if err != nil {
h.logger.Error("获取现有API配置失败", zap.Error(err), zap.String("product_id", productID))
h.responseBuilder.NotFound(c, "产品API配置不存在")
return
}
if err := h.productAppService.UpdateProductApiConfig(c.Request.Context(), existingConfig.ID, &configResponse); err != nil {
h.logger.Error("更新产品API配置失败", zap.Error(err), zap.String("product_id", productID))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "API配置更新成功")
}
// DeleteProductApiConfig 删除产品API配置
// @Summary 删除产品API配置
// @Description 管理员删除产品的API配置
// @Tags 产品管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param id path string true "产品ID"
// @Success 200 {object} map[string]interface{} "API配置删除成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 404 {object} map[string]interface{} "产品或配置不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/products/{id}/api-config [delete]
func (h *ProductAdminHandler) DeleteProductApiConfig(c *gin.Context) {
productID := c.Param("id")
if productID == "" {
h.responseBuilder.BadRequest(c, "产品ID不能为空")
return
}
// 先获取现有配置以获取配置ID
existingConfig, err := h.productAppService.GetProductApiConfig(c.Request.Context(), productID)
if err != nil {
h.logger.Error("获取现有API配置失败", zap.Error(err), zap.String("product_id", productID))
h.responseBuilder.NotFound(c, "产品API配置不存在")
return
}
if err := h.productAppService.DeleteProductApiConfig(c.Request.Context(), existingConfig.ID); err != nil {
h.logger.Error("删除产品API配置失败", zap.Error(err), zap.String("product_id", productID))
h.responseBuilder.BadRequest(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "API配置删除成功")
}

View File

@@ -1,6 +1,7 @@
package handlers
import (
"strconv"
"tyapi-server/internal/application/product"
"tyapi-server/internal/application/product/dto/commands"
"tyapi-server/internal/application/product/dto/queries"
@@ -13,6 +14,7 @@ import (
// ProductHandler 产品相关HTTP处理器
type ProductHandler struct {
appService product.ProductApplicationService
apiConfigService product.ProductApiConfigApplicationService
categoryService product.CategoryApplicationService
subAppService product.SubscriptionApplicationService
responseBuilder interfaces.ResponseBuilder
@@ -23,6 +25,7 @@ type ProductHandler struct {
// NewProductHandler 创建产品HTTP处理器
func NewProductHandler(
appService product.ProductApplicationService,
apiConfigService product.ProductApiConfigApplicationService,
categoryService product.CategoryApplicationService,
subAppService product.SubscriptionApplicationService,
responseBuilder interfaces.ResponseBuilder,
@@ -31,6 +34,7 @@ func NewProductHandler(
) *ProductHandler {
return &ProductHandler{
appService: appService,
apiConfigService: apiConfigService,
categoryService: categoryService,
subAppService: subAppService,
responseBuilder: responseBuilder,
@@ -49,8 +53,6 @@ func NewProductHandler(
// @Param page_size query int false "每页数量" default(10)
// @Param keyword query string false "搜索关键词"
// @Param category_id query string false "分类ID"
// @Param min_price query number false "最低价格"
// @Param max_price query number false "最高价格"
// @Param is_enabled query bool false "是否启用"
// @Param is_visible query bool false "是否可见"
// @Param is_package query bool false "是否组合包"
@@ -61,23 +63,65 @@ func NewProductHandler(
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/products [get]
func (h *ProductHandler) ListProducts(c *gin.Context) {
var query queries.ListProductsQuery
if err := h.validator.ValidateQuery(c, &query); err != nil {
return
// 解析查询参数
page := h.getIntQuery(c, "page", 1)
pageSize := h.getIntQuery(c, "page_size", 10)
// 构建筛选条件
filters := make(map[string]interface{})
// 搜索关键词筛选
if keyword := c.Query("keyword"); keyword != "" {
filters["keyword"] = keyword
}
// 设置默认值
if query.Page <= 0 {
query.Page = 1
}
if query.PageSize <= 0 {
query.PageSize = 10
}
if query.PageSize > 100 {
query.PageSize = 100
// 分类ID筛选
if categoryID := c.Query("category_id"); categoryID != "" {
filters["category_id"] = categoryID
}
result, err := h.appService.ListProducts(c.Request.Context(), &query)
// 启用状态筛选
if isEnabled := c.Query("is_enabled"); isEnabled != "" {
if enabled, err := strconv.ParseBool(isEnabled); err == nil {
filters["is_enabled"] = enabled
}
}
// 可见状态筛选
if isVisible := c.Query("is_visible"); isVisible != "" {
if visible, err := strconv.ParseBool(isVisible); err == nil {
filters["is_visible"] = visible
}
}
// 产品类型筛选
if isPackage := c.Query("is_package"); isPackage != "" {
if pkg, err := strconv.ParseBool(isPackage); err == nil {
filters["is_package"] = pkg
}
}
// 排序字段
sortBy := c.Query("sort_by")
if sortBy == "" {
sortBy = "created_at"
}
// 排序方向
sortOrder := c.Query("sort_order")
if sortOrder == "" {
sortOrder = "desc"
}
// 构建分页选项
options := interfaces.ListOptions{
Page: page,
PageSize: pageSize,
Sort: sortBy,
Order: sortOrder,
}
result, err := h.appService.ListProducts(c.Request.Context(), filters, options)
if err != nil {
h.logger.Error("获取产品列表失败", zap.Error(err))
h.responseBuilder.InternalError(c, "获取产品列表失败")
@@ -87,6 +131,16 @@ func (h *ProductHandler) ListProducts(c *gin.Context) {
h.responseBuilder.Success(c, result, "获取产品列表成功")
}
// getIntQuery 获取整数查询参数
func (h *ProductHandler) 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
}
// GetProductDetail 获取产品详情
// @Summary 获取产品详情
// @Description 根据产品ID获取产品详细信息
@@ -176,6 +230,62 @@ func (h *ProductHandler) GetProductStats(c *gin.Context) {
h.responseBuilder.Success(c, result, "获取产品统计成功")
}
// GetProductApiConfig 获取产品API配置
// @Summary 获取产品API配置
// @Description 根据产品ID获取API配置信息
// @Tags 产品API配置
// @Accept json
// @Produce json
// @Param id path string true "产品ID"
// @Success 200 {object} responses.ProductApiConfigResponse "获取成功"
// @Failure 400 {object} interfaces.APIResponse "请求参数错误"
// @Failure 404 {object} interfaces.APIResponse "配置不存在"
// @Router /api/v1/products/{id}/api-config [get]
func (h *ProductHandler) GetProductApiConfig(c *gin.Context) {
productID := c.Param("id")
if productID == "" {
h.responseBuilder.BadRequest(c, "产品ID不能为空")
return
}
config, err := h.apiConfigService.GetProductApiConfig(c.Request.Context(), productID)
if err != nil {
h.logger.Error("获取产品API配置失败", zap.Error(err), zap.String("product_id", productID))
h.responseBuilder.NotFound(c, "产品API配置不存在")
return
}
h.responseBuilder.Success(c, config, "获取产品API配置成功")
}
// GetProductApiConfigByCode 根据产品代码获取API配置
// @Summary 根据产品代码获取API配置
// @Description 根据产品代码获取API配置信息
// @Tags 产品API配置
// @Accept json
// @Produce json
// @Param product_code path string true "产品代码"
// @Success 200 {object} responses.ProductApiConfigResponse "获取成功"
// @Failure 400 {object} interfaces.APIResponse "请求参数错误"
// @Failure 404 {object} interfaces.APIResponse "配置不存在"
// @Router /api/v1/products/code/{product_code}/api-config [get]
func (h *ProductHandler) GetProductApiConfigByCode(c *gin.Context) {
productCode := c.Param("product_code")
if productCode == "" {
h.responseBuilder.BadRequest(c, "产品代码不能为空")
return
}
config, err := h.apiConfigService.GetProductApiConfigByCode(c.Request.Context(), productCode)
if err != nil {
h.logger.Error("根据产品代码获取API配置失败", zap.Error(err), zap.String("product_code", productCode))
h.responseBuilder.NotFound(c, "产品API配置不存在")
return
}
h.responseBuilder.Success(c, config, "获取产品API配置成功")
}
// ================ 分类相关方法 ================
// ListCategories 获取分类列表

View File

@@ -1,11 +1,14 @@
package handlers
import (
"strconv"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"tyapi-server/internal/application/user"
"tyapi-server/internal/application/user/dto/commands"
"tyapi-server/internal/application/user/dto/queries"
"tyapi-server/internal/shared/interfaces"
"tyapi-server/internal/shared/middleware"
)
@@ -240,6 +243,109 @@ func (h *UserHandler) ResetPassword(c *gin.Context) {
h.response.Success(c, nil, "密码重置成功")
}
// ListUsers 管理员查看用户列表
// @Summary 管理员查看用户列表
// @Description 管理员查看用户列表,支持分页和筛选
// @Tags 用户管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param phone query string false "手机号筛选"
// @Param user_type query string false "用户类型筛选" Enums(user,admin)
// @Param is_active query bool false "是否激活筛选"
// @Param is_certified query bool false "是否已认证筛选"
// @Param company_name query string false "企业名称筛选"
// @Param start_date query string false "开始日期" format(date)
// @Param end_date query string false "结束日期" format(date)
// @Success 200 {object} responses.UserListResponse "用户列表"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 403 {object} map[string]interface{} "权限不足"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/users/admin/list [get]
func (h *UserHandler) ListUsers(c *gin.Context) {
// 检查管理员权限
userID := h.getCurrentUserID(c)
if userID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
// 构建查询参数
query := &queries.ListUsersQuery{
Page: 1,
PageSize: 10,
}
// 从查询参数中获取筛选条件
if page := c.Query("page"); page != "" {
if pageNum, err := strconv.Atoi(page); err == nil && pageNum > 0 {
query.Page = pageNum
}
}
if pageSize := c.Query("page_size"); pageSize != "" {
if size, err := strconv.Atoi(pageSize); err == nil && size > 0 && size <= 100 {
query.PageSize = size
}
}
query.Phone = c.Query("phone")
query.UserType = c.Query("user_type")
query.CompanyName = c.Query("company_name")
query.StartDate = c.Query("start_date")
query.EndDate = c.Query("end_date")
// 处理布尔值参数
if isActive := c.Query("is_active"); isActive != "" {
if active, err := strconv.ParseBool(isActive); err == nil {
query.IsActive = &active
}
}
if isCertified := c.Query("is_certified"); isCertified != "" {
if certified, err := strconv.ParseBool(isCertified); err == nil {
query.IsCertified = &certified
}
}
// 调用应用服务
resp, err := h.appService.ListUsers(c.Request.Context(), query)
if err != nil {
h.logger.Error("获取用户列表失败", zap.Error(err))
h.response.BadRequest(c, "获取用户列表失败")
return
}
h.response.Success(c, resp, "获取用户列表成功")
}
// GetUserStats 管理员获取用户统计信息
// @Summary 管理员获取用户统计信息
// @Description 管理员获取用户统计信息,包括总用户数、活跃用户数、已认证用户数
// @Tags 用户管理
// @Accept json
// @Produce json
// @Security Bearer
// @Success 200 {object} responses.UserStatsResponse "用户统计信息"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 403 {object} map[string]interface{} "权限不足"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/users/admin/stats [get]
func (h *UserHandler) GetUserStats(c *gin.Context) {
// 调用应用服务
resp, err := h.appService.GetUserStats(c.Request.Context())
if err != nil {
h.logger.Error("获取用户统计信息失败", zap.Error(err))
h.response.BadRequest(c, "获取用户统计信息失败")
return
}
h.response.Success(c, resp, "获取用户统计信息成功")
}
// getCurrentUserID 获取当前用户ID
func (h *UserHandler) getCurrentUserID(c *gin.Context) string {
if userID, exists := c.Get("user_id"); exists {

View File

@@ -0,0 +1,59 @@
package routes
import (
"tyapi-server/internal/infrastructure/http/handlers"
sharedhttp "tyapi-server/internal/shared/http"
"tyapi-server/internal/shared/middleware"
"go.uber.org/zap"
)
// ApiRoutes API路由注册器
type ApiRoutes struct {
apiHandler *handlers.ApiHandler
authMiddleware *middleware.JWTAuthMiddleware
domainAuthMiddleware *middleware.DomainAuthMiddleware
logger *zap.Logger
}
// NewApiRoutes 创建API路由注册器
func NewApiRoutes(
apiHandler *handlers.ApiHandler,
authMiddleware *middleware.JWTAuthMiddleware,
domainAuthMiddleware *middleware.DomainAuthMiddleware,
logger *zap.Logger,
) *ApiRoutes {
return &ApiRoutes{
apiHandler: apiHandler,
authMiddleware: authMiddleware,
domainAuthMiddleware: domainAuthMiddleware,
logger: logger,
}
}
// Register 注册相关路由
func (r *ApiRoutes) Register(router *sharedhttp.GinRouter) {
// API路由组需要用户认证
engine := router.GetEngine()
apiGroup := engine.Group("/api/v1")
{
apiGroup.POST("/:api_name", r.domainAuthMiddleware.Handle(""), r.apiHandler.HandleApiCall)
// 加密接口(用于前端调试)
apiGroup.POST("/encrypt", r.authMiddleware.Handle(), r.apiHandler.EncryptParams)
// API密钥管理接口
apiGroup.GET("/api-keys", r.authMiddleware.Handle(), r.apiHandler.GetUserApiKeys)
// 白名单管理接口
apiGroup.GET("/white-list", r.authMiddleware.Handle(), r.apiHandler.GetUserWhiteList)
apiGroup.POST("/white-list", r.authMiddleware.Handle(), r.apiHandler.AddWhiteListIP)
apiGroup.DELETE("/white-list/:ip", r.authMiddleware.Handle(), r.apiHandler.DeleteWhiteListIP)
// API调用记录接口
apiGroup.GET("/my/api-calls", r.authMiddleware.Handle(), r.apiHandler.GetUserApiCalls)
}
r.logger.Info("API路由注册完成")
}

View File

@@ -43,31 +43,29 @@ func (r *CertificationRoutes) Register(router *http.GinRouter) {
authGroup := certificationGroup.Group("")
authGroup.Use(r.auth.Handle())
{
authGroup.GET("/user", r.handler.GetUserCertifications) // 获取用户认证列表
authGroup.GET("", r.handler.ListCertifications) // 查询认证列表(管理员)
authGroup.GET("/statistics", r.handler.GetCertificationStatistics) // 获取认证统计
authGroup.GET("", r.handler.ListCertifications) // 查询认证列表(管理员)
// 1. 获取认证详情
authGroup.GET("/:id", r.handler.GetCertification)
authGroup.GET("/details", r.handler.GetCertification)
// 2. 提交企业信息
authGroup.POST("/:id/enterprise-info", r.handler.SubmitEnterpriseInfo)
authGroup.POST("/enterprise-info", r.handler.SubmitEnterpriseInfo)
// 合同管理
authGroup.POST("/apply-contract", r.handler.ApplyContract) // 申请合同签署
// 3. 申请合同签署
authGroup.POST("/apply-contract", r.handler.ApplyContract)
// 重试操作
authGroup.POST("/retry", r.handler.RetryOperation) // 重试操作
// 前端确认是否完成认证
authGroup.POST("/confirm-auth", r.handler.ConfirmAuth)
// 前端确认是否完成签署
authGroup.POST("/confirm-sign", r.handler.ConfirmSign)
// 管理员操作
authGroup.POST("/force-transition", r.handler.ForceTransitionStatus) // 强制状态转换
authGroup.GET("/monitoring", r.handler.GetSystemMonitoring) // 获取系统监控数据
}
// 回调路由(不需要认证,但需要验证签名)
callbackGroup := certificationGroup.Group("/callbacks")
{
callbackGroup.POST("", r.handler.HandleEsignCallback) // e签宝回调统一处理企业认证和合同签署回调
callbackGroup.POST("/esign", r.handler.HandleEsignCallback) // e签宝回调统一处理企业认证和合同签署回调
}
}
@@ -87,7 +85,7 @@ func (r *CertificationRoutes) GetRoutes() []RouteInfo {
{Method: "POST", Path: "/api/v1/certifications/retry", Handler: "RetryOperation", Auth: true},
{Method: "POST", Path: "/api/v1/certifications/force-transition", Handler: "ForceTransitionStatus", Auth: true},
{Method: "GET", Path: "/api/v1/certifications/monitoring", Handler: "GetSystemMonitoring", Auth: true},
{Method: "POST", Path: "/api/v1/certifications/callbacks", Handler: "HandleEsignCallback", Auth: false},
{Method: "POST", Path: "/api/v1/certifications/callbacks/esign", Handler: "HandleEsignCallback", Auth: false},
}
}

View File

@@ -10,51 +10,61 @@ import (
// FinanceRoutes 财务路由注册器
type FinanceRoutes struct {
financeHandler *handlers.FinanceHandler
authMiddleware *middleware.JWTAuthMiddleware
logger *zap.Logger
financeHandler *handlers.FinanceHandler
authMiddleware *middleware.JWTAuthMiddleware
adminAuthMiddleware *middleware.AdminAuthMiddleware
logger *zap.Logger
}
// NewFinanceRoutes 创建财务路由注册器
func NewFinanceRoutes(
financeHandler *handlers.FinanceHandler,
authMiddleware *middleware.JWTAuthMiddleware,
adminAuthMiddleware *middleware.AdminAuthMiddleware,
logger *zap.Logger,
) *FinanceRoutes {
return &FinanceRoutes{
financeHandler: financeHandler,
authMiddleware: authMiddleware,
logger: logger,
financeHandler: financeHandler,
authMiddleware: authMiddleware,
adminAuthMiddleware: adminAuthMiddleware,
logger: logger,
}
}
// Register 注册财务相关路由
func (r *FinanceRoutes) Register(router *sharedhttp.GinRouter) {
// 财务路由组,需要用户认证
engine := router.GetEngine()
// 支付宝回调路由(不需要认证)
alipayGroup := engine.Group("/api/v1/finance/alipay")
{
alipayGroup.POST("/callback", r.financeHandler.HandleAlipayCallback) // 支付宝异步回调
alipayGroup.GET("/return", r.financeHandler.HandleAlipayReturn) // 支付宝同步回调
}
// 财务路由组,需要用户认证
financeGroup := engine.Group("/api/v1/finance")
financeGroup.Use(r.authMiddleware.Handle())
{
// 钱包相关路由
walletGroup := financeGroup.Group("/wallet")
{
walletGroup.POST("", r.financeHandler.CreateWallet) // 创建钱包
walletGroup.GET("", r.financeHandler.GetWallet) // 获取钱包信息
walletGroup.PUT("", r.financeHandler.UpdateWallet) // 更新钱包
walletGroup.POST("/recharge", r.financeHandler.Recharge) // 充值
walletGroup.POST("/withdraw", r.financeHandler.Withdraw) // 提现
walletGroup.POST("/transaction", r.financeHandler.WalletTransaction) // 钱包交易
walletGroup.GET("/stats", r.financeHandler.GetWalletStats) // 获取钱包统计
walletGroup.GET("", r.financeHandler.GetWallet) // 获取钱包信息
walletGroup.GET("/transactions", r.financeHandler.GetUserWalletTransactions) // 获取钱包交易记录
walletGroup.GET("/recharge-config", r.financeHandler.GetRechargeConfig) // 获取充值配置
walletGroup.POST("/alipay-recharge", r.financeHandler.CreateAlipayRecharge) // 创建支付宝充值订单
walletGroup.GET("/recharge-records", r.financeHandler.GetUserRechargeRecords) // 用户充值记录分页
walletGroup.GET("/alipay-order-status", r.financeHandler.GetAlipayOrderStatus) // 获取支付宝订单状态
}
}
// 用户密钥相关路由
secretsGroup := financeGroup.Group("/secrets")
{
secretsGroup.POST("", r.financeHandler.CreateUserSecrets) // 创建用户密钥
secretsGroup.GET("", r.financeHandler.GetUserSecrets) // 获取用户密钥
secretsGroup.POST("/regenerate", r.financeHandler.RegenerateAccessKey) // 重新生成访问密钥
secretsGroup.POST("/deactivate", r.financeHandler.DeactivateUserSecrets) // 停用用户密钥
}
// 管理员财务路由
adminFinanceGroup := engine.Group("/api/v1/admin/finance")
adminFinanceGroup.Use(r.adminAuthMiddleware.Handle())
{
adminFinanceGroup.POST("/transfer-recharge", r.financeHandler.TransferRecharge) // 对公转账充值
adminFinanceGroup.POST("/gift-recharge", r.financeHandler.GiftRecharge) // 赠送充值
adminFinanceGroup.GET("/recharge-records", r.financeHandler.GetAdminRechargeRecords) // 管理员充值记录分页
}
r.logger.Info("财务路由注册完成")

View File

@@ -1,5 +1,5 @@
package routes
import (
"tyapi-server/internal/infrastructure/http/handlers"
sharedhttp "tyapi-server/internal/shared/http"
@@ -31,19 +31,33 @@ func (r *ProductAdminRoutes) Register(router *sharedhttp.GinRouter) {
// 管理员路由组
engine := router.GetEngine()
adminGroup := engine.Group("/api/v1/admin")
adminGroup.Use(r.auth.Handle()) // JWT认证
adminGroup.Use(r.auth.Handle()) // JWT认证
adminGroup.Use(r.admin.Handle()) // 管理员权限验证
{
// 产品管理
products := adminGroup.Group("/products")
{
products.GET("", r.handler.ListProducts)
products.GET("/available", r.handler.GetAvailableProducts)
products.GET("/:id", r.handler.GetProductDetail)
products.POST("", r.handler.CreateProduct)
products.PUT("/:id", r.handler.UpdateProduct)
products.DELETE("/:id", r.handler.DeleteProduct)
// 组合包管理
products.POST("/:id/package-items", r.handler.AddPackageItem)
products.PUT("/:id/package-items/:item_id", r.handler.UpdatePackageItem)
products.DELETE("/:id/package-items/:item_id", r.handler.RemovePackageItem)
products.PUT("/:id/package-items/reorder", r.handler.ReorderPackageItems)
products.PUT("/:id/package-items/batch", r.handler.UpdatePackageItems)
// API配置管理
products.GET("/:id/api-config", r.handler.GetProductApiConfig)
products.POST("/:id/api-config", r.handler.CreateProductApiConfig)
products.PUT("/:id/api-config", r.handler.UpdateProductApiConfig)
products.DELETE("/:id/api-config", r.handler.DeleteProductApiConfig)
}
// 分类管理
categories := adminGroup.Group("/product-categories")
{
@@ -53,7 +67,7 @@ func (r *ProductAdminRoutes) Register(router *sharedhttp.GinRouter) {
categories.PUT("/:id", r.handler.UpdateCategory)
categories.DELETE("/:id", r.handler.DeleteCategory)
}
// 订阅管理
subscriptions := adminGroup.Group("/subscriptions")
{
@@ -62,4 +76,4 @@ func (r *ProductAdminRoutes) Register(router *sharedhttp.GinRouter) {
subscriptions.PUT("/:id/price", r.handler.UpdateSubscriptionPrice)
}
}
}
}

View File

@@ -38,12 +38,16 @@ func (r *ProductRoutes) Register(router *sharedhttp.GinRouter) {
// 获取产品列表(分页+筛选)
products.GET("", r.productHandler.ListProducts)
// 获取产品详情
products.GET("/:id", r.productHandler.GetProductDetail)
// 获取产品统计
products.GET("/stats", r.productHandler.GetProductStats)
// 根据产品代码获取API配置
products.GET("/code/:product_code/api-config", r.productHandler.GetProductApiConfigByCode)
// 产品详情和API配置 - 使用具体路径避免冲突
products.GET("/:id", r.productHandler.GetProductDetail)
products.GET("/:id/api-config", r.productHandler.GetProductApiConfig)
// 订阅产品(需要认证)
products.POST("/:id/subscribe", r.auth.Handle(), r.productHandler.SubscribeProduct)
}

View File

@@ -12,6 +12,7 @@ import (
type UserRoutes struct {
handler *handlers.UserHandler
authMiddleware *middleware.JWTAuthMiddleware
adminAuthMiddleware *middleware.AdminAuthMiddleware
logger *zap.Logger
}
@@ -19,11 +20,13 @@ type UserRoutes struct {
func NewUserRoutes(
handler *handlers.UserHandler,
authMiddleware *middleware.JWTAuthMiddleware,
adminAuthMiddleware *middleware.AdminAuthMiddleware,
logger *zap.Logger,
) *UserRoutes {
return &UserRoutes{
handler: handler,
authMiddleware: authMiddleware,
adminAuthMiddleware: adminAuthMiddleware,
logger: logger,
}
}
@@ -48,6 +51,14 @@ func (r *UserRoutes) Register(router *sharedhttp.GinRouter) {
authenticated.GET("/me", r.handler.GetProfile) // 获取当前用户信息
authenticated.PUT("/me/password", r.handler.ChangePassword) // 修改密码
}
// 管理员路由
adminGroup := usersGroup.Group("/admin")
adminGroup.Use(r.adminAuthMiddleware.Handle())
{
adminGroup.GET("/list", r.handler.ListUsers) // 管理员查看用户列表
adminGroup.GET("/stats", r.handler.GetUserStats) // 管理员获取用户统计信息
}
}
r.logger.Info("用户路由注册完成")