This commit is contained in:
2025-08-02 02:54:21 +08:00
parent 934dce2776
commit 66845d3fe0
74 changed files with 8686 additions and 212 deletions

View File

@@ -130,7 +130,7 @@ func (h *ApiHandler) AddWhiteListIP(c *gin.Context) {
}
var req dto.WhiteListRequest
if err := c.ShouldBindJSON(&req); err != nil {
if err := h.validator.BindAndValidate(c, &req); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误")
return
}
@@ -311,6 +311,86 @@ func (h *ApiHandler) GetUserApiCalls(c *gin.Context) {
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调用记录成功")
}
// getIntQuery 获取整数查询参数
func (h *ApiHandler) getIntQuery(c *gin.Context, key string, defaultValue int) int {
if value := c.Query(key); value != "" {

View File

@@ -17,24 +17,30 @@ import (
// FinanceHandler 财务HTTP处理器
type FinanceHandler struct {
appService finance.FinanceApplicationService
responseBuilder interfaces.ResponseBuilder
validator interfaces.RequestValidator
logger *zap.Logger
appService finance.FinanceApplicationService
invoiceAppService finance.InvoiceApplicationService
adminInvoiceAppService finance.AdminInvoiceApplicationService
responseBuilder interfaces.ResponseBuilder
validator interfaces.RequestValidator
logger *zap.Logger
}
// NewFinanceHandler 创建财务HTTP处理器
func NewFinanceHandler(
appService finance.FinanceApplicationService,
invoiceAppService finance.InvoiceApplicationService,
adminInvoiceAppService finance.AdminInvoiceApplicationService,
responseBuilder interfaces.ResponseBuilder,
validator interfaces.RequestValidator,
logger *zap.Logger,
) *FinanceHandler {
return &FinanceHandler{
appService: appService,
responseBuilder: responseBuilder,
validator: validator,
logger: logger,
appService: appService,
invoiceAppService: invoiceAppService,
adminInvoiceAppService: adminInvoiceAppService,
responseBuilder: responseBuilder,
validator: validator,
logger: logger,
}
}
@@ -554,3 +560,381 @@ func (h *FinanceHandler) GetAlipayOrderStatus(c *gin.Context) {
h.responseBuilder.Success(c, result, "获取订单状态成功")
}
// ==================== 发票相关Handler方法 ====================
// ApplyInvoice 申请开票
// @Summary 申请开票
// @Description 用户申请开票
// @Tags 发票管理
// @Accept json
// @Produce json
// @Param request body finance.ApplyInvoiceRequest true "申请开票请求"
// @Success 200 {object} response.Response{data=finance.InvoiceApplicationResponse}
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/invoices/apply [post]
func (h *FinanceHandler) ApplyInvoice(c *gin.Context) {
var req finance.ApplyInvoiceRequest
if err := h.validator.BindAndValidate(c, &req); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误", err)
return
}
userID := c.GetString("user_id") // 从JWT中获取用户ID
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
result, err := h.invoiceAppService.ApplyInvoice(c.Request.Context(), userID, req)
if err != nil {
h.responseBuilder.InternalError(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "申请开票成功")
}
// GetUserInvoiceInfo 获取用户发票信息
// @Summary 获取用户发票信息
// @Description 获取用户的发票信息
// @Tags 发票管理
// @Produce json
// @Success 200 {object} response.Response{data=finance.InvoiceInfoResponse}
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/invoices/info [get]
func (h *FinanceHandler) GetUserInvoiceInfo(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
result, err := h.invoiceAppService.GetUserInvoiceInfo(c.Request.Context(), userID)
if err != nil {
h.responseBuilder.InternalError(c, "获取发票信息失败")
return
}
h.responseBuilder.Success(c, result, "获取发票信息成功")
}
// UpdateUserInvoiceInfo 更新用户发票信息
// @Summary 更新用户发票信息
// @Description 更新用户的发票信息
// @Tags 发票管理
// @Accept json
// @Produce json
// @Param request body finance.UpdateInvoiceInfoRequest true "更新发票信息请求"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/invoices/info [put]
func (h *FinanceHandler) UpdateUserInvoiceInfo(c *gin.Context) {
var req finance.UpdateInvoiceInfoRequest
if err := h.validator.BindAndValidate(c, &req); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误", err)
return
}
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
err := h.invoiceAppService.UpdateUserInvoiceInfo(c.Request.Context(), userID, req)
if err != nil {
h.responseBuilder.InternalError(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "更新发票信息成功")
}
// GetUserInvoiceRecords 获取用户开票记录
// @Summary 获取用户开票记录
// @Description 获取用户的开票记录列表
// @Tags 发票管理
// @Produce json
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param status query string false "状态筛选"
// @Success 200 {object} response.Response{data=finance.InvoiceRecordsResponse}
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/invoices/records [get]
func (h *FinanceHandler) GetUserInvoiceRecords(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
status := c.Query("status")
startTime := c.Query("start_time")
endTime := c.Query("end_time")
req := finance.GetInvoiceRecordsRequest{
Page: page,
PageSize: pageSize,
Status: status,
StartTime: startTime,
EndTime: endTime,
}
result, err := h.invoiceAppService.GetUserInvoiceRecords(c.Request.Context(), userID, req)
if err != nil {
h.responseBuilder.InternalError(c, "获取开票记录失败")
return
}
h.responseBuilder.Success(c, result, "获取开票记录成功")
}
// DownloadInvoiceFile 下载发票文件
// @Summary 下载发票文件
// @Description 下载指定发票的文件
// @Tags 发票管理
// @Produce application/octet-stream
// @Param application_id path string true "申请ID"
// @Success 200 {file} file
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/invoices/{application_id}/download [get]
func (h *FinanceHandler) DownloadInvoiceFile(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
applicationID := c.Param("application_id")
if applicationID == "" {
h.responseBuilder.BadRequest(c, "申请ID不能为空")
return
}
result, err := h.invoiceAppService.DownloadInvoiceFile(c.Request.Context(), userID, applicationID)
if err != nil {
h.responseBuilder.InternalError(c, "下载发票文件失败")
return
}
// 设置响应头
c.Header("Content-Type", "application/pdf")
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", result.FileName))
c.Header("Content-Length", fmt.Sprintf("%d", len(result.FileContent)))
// 直接返回文件内容
c.Data(http.StatusOK, "application/pdf", result.FileContent)
}
// GetAvailableAmount 获取可开票金额
// @Summary 获取可开票金额
// @Description 获取用户当前可开票的金额
// @Tags 发票管理
// @Produce json
// @Success 200 {object} response.Response{data=finance.AvailableAmountResponse}
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/invoices/available-amount [get]
func (h *FinanceHandler) GetAvailableAmount(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
result, err := h.invoiceAppService.GetAvailableAmount(c.Request.Context(), userID)
if err != nil {
h.responseBuilder.InternalError(c, "获取可开票金额失败")
return
}
h.responseBuilder.Success(c, result, "获取可开票金额成功")
}
// ==================== 管理员发票相关Handler方法 ====================
// GetPendingApplications 获取发票申请列表(支持筛选)
// @Summary 获取发票申请列表
// @Description 管理员获取发票申请列表,支持状态和时间范围筛选
// @Tags 管理员-发票管理
// @Produce json
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param status query string false "状态筛选pending/completed/rejected"
// @Param start_time query string false "开始时间 (格式: 2006-01-02 15:04:05)"
// @Param end_time query string false "结束时间 (格式: 2006-01-02 15:04:05)"
// @Success 200 {object} response.Response{data=finance.PendingApplicationsResponse}
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/admin/invoices/pending [get]
func (h *FinanceHandler) GetPendingApplications(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
status := c.Query("status")
startTime := c.Query("start_time")
endTime := c.Query("end_time")
req := finance.GetPendingApplicationsRequest{
Page: page,
PageSize: pageSize,
Status: status,
StartTime: startTime,
EndTime: endTime,
}
result, err := h.adminInvoiceAppService.GetPendingApplications(c.Request.Context(), req)
if err != nil {
h.responseBuilder.InternalError(c, err.Error())
return
}
h.responseBuilder.Success(c, result, "获取发票申请列表成功")
}
// ApproveInvoiceApplication 通过发票申请(上传发票)
// @Summary 通过发票申请
// @Description 管理员通过发票申请并上传发票文件
// @Tags 管理员-发票管理
// @Accept multipart/form-data
// @Produce json
// @Param application_id path string true "申请ID"
// @Param file formData file true "发票文件"
// @Param admin_notes formData string false "管理员备注"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/admin/invoices/{application_id}/approve [post]
func (h *FinanceHandler) ApproveInvoiceApplication(c *gin.Context) {
applicationID := c.Param("application_id")
if applicationID == "" {
h.responseBuilder.BadRequest(c, "申请ID不能为空")
return
}
// 获取上传的文件
file, err := c.FormFile("file")
if err != nil {
h.responseBuilder.BadRequest(c, "请选择要上传的发票文件")
return
}
// 打开文件
fileHandle, err := file.Open()
if err != nil {
h.responseBuilder.InternalError(c, "文件打开失败")
return
}
defer fileHandle.Close()
// 获取管理员备注
adminNotes := c.PostForm("admin_notes")
req := finance.ApproveInvoiceRequest{
AdminNotes: adminNotes,
}
err = h.adminInvoiceAppService.ApproveInvoiceApplication(c.Request.Context(), applicationID, fileHandle, req)
if err != nil {
h.responseBuilder.InternalError(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "通过发票申请成功")
}
// RejectInvoiceApplication 拒绝发票申请
// @Summary 拒绝发票申请
// @Description 管理员拒绝发票申请
// @Tags 管理员-发票管理
// @Accept json
// @Produce json
// @Param application_id path string true "申请ID"
// @Param request body finance.RejectInvoiceRequest true "拒绝申请请求"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/admin/invoices/{application_id}/reject [post]
func (h *FinanceHandler) RejectInvoiceApplication(c *gin.Context) {
applicationID := c.Param("application_id")
if applicationID == "" {
h.responseBuilder.BadRequest(c, "申请ID不能为空")
return
}
var req finance.RejectInvoiceRequest
if err := h.validator.BindAndValidate(c, &req); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误", err)
return
}
err := h.adminInvoiceAppService.RejectInvoiceApplication(c.Request.Context(), applicationID, req)
if err != nil {
h.responseBuilder.InternalError(c, err.Error())
return
}
h.responseBuilder.Success(c, nil, "拒绝发票申请成功")
}
// AdminDownloadInvoiceFile 管理员下载发票文件
// @Summary 管理员下载发票文件
// @Description 管理员下载指定发票的文件
// @Tags 管理员-发票管理
// @Produce application/octet-stream
// @Param application_id path string true "申请ID"
// @Success 200 {file} file
// @Failure 400 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /api/v1/admin/invoices/{application_id}/download [get]
func (h *FinanceHandler) AdminDownloadInvoiceFile(c *gin.Context) {
applicationID := c.Param("application_id")
if applicationID == "" {
h.responseBuilder.BadRequest(c, "申请ID不能为空")
return
}
result, err := h.adminInvoiceAppService.DownloadInvoiceFile(c.Request.Context(), applicationID)
if err != nil {
h.responseBuilder.InternalError(c, "下载发票文件失败")
return
}
// 设置响应头
c.Header("Content-Type", "application/pdf")
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", result.FileName))
c.Header("Content-Length", fmt.Sprintf("%d", len(result.FileContent)))
// 直接返回文件内容
c.Data(http.StatusOK, "application/pdf", result.FileContent)
}
// DebugEventSystem 调试事件系统状态
// @Summary 调试事件系统状态
// @Description 获取事件系统的调试信息
// @Tags 调试
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Router /api/v1/debug/events [get]
func (h *FinanceHandler) DebugEventSystem(c *gin.Context) {
h.logger.Info("🔍 请求事件系统调试信息")
// 这里可以添加事件系统的状态信息
// 暂时返回基本信息
debugInfo := map[string]interface{}{
"timestamp": time.Now().Format("2006-01-02 15:04:05"),
"message": "事件系统调试端点已启用",
"handler": "FinanceHandler",
}
h.responseBuilder.Success(c, debugInfo, "事件系统调试信息")
}

View File

@@ -2,6 +2,9 @@ package handlers
import (
"strconv"
"time"
"tyapi-server/internal/application/api"
"tyapi-server/internal/application/finance"
"tyapi-server/internal/application/product"
"tyapi-server/internal/application/product/dto/commands"
"tyapi-server/internal/application/product/dto/queries"
@@ -18,6 +21,8 @@ type ProductAdminHandler struct {
categoryAppService product.CategoryApplicationService
subscriptionAppService product.SubscriptionApplicationService
documentationAppService product.DocumentationApplicationServiceInterface
apiAppService api.ApiApplicationService
financeAppService finance.FinanceApplicationService
responseBuilder interfaces.ResponseBuilder
validator interfaces.RequestValidator
logger *zap.Logger
@@ -29,6 +34,8 @@ func NewProductAdminHandler(
categoryAppService product.CategoryApplicationService,
subscriptionAppService product.SubscriptionApplicationService,
documentationAppService product.DocumentationApplicationServiceInterface,
apiAppService api.ApiApplicationService,
financeAppService finance.FinanceApplicationService,
responseBuilder interfaces.ResponseBuilder,
validator interfaces.RequestValidator,
logger *zap.Logger,
@@ -38,6 +45,8 @@ func NewProductAdminHandler(
categoryAppService: categoryAppService,
subscriptionAppService: subscriptionAppService,
documentationAppService: documentationAppService,
apiAppService: apiAppService,
financeAppService: financeAppService,
responseBuilder: responseBuilder,
validator: validator,
logger: logger,
@@ -710,7 +719,13 @@ func (h *ProductAdminHandler) GetCategoryDetail(c *gin.Context) {
// @Security Bearer
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param status query string false "订阅状态"
// @Param keyword query string false "搜索关键词"
// @Param company_name query string false "企业名称"
// @Param product_name 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} responses.SubscriptionListResponse "获取订阅列表成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
@@ -719,7 +734,7 @@ func (h *ProductAdminHandler) GetCategoryDetail(c *gin.Context) {
func (h *ProductAdminHandler) ListSubscriptions(c *gin.Context) {
var query queries.ListSubscriptionsQuery
if err := c.ShouldBindQuery(&query); err != nil {
h.responseBuilder.BadRequest(c, "请求参数错误")
h.responseBuilder.BadRequest(c, err.Error())
return
}
@@ -734,6 +749,14 @@ func (h *ProductAdminHandler) ListSubscriptions(c *gin.Context) {
query.PageSize = 100
}
// 设置默认排序
if query.SortBy == "" {
query.SortBy = "created_at"
}
if query.SortOrder == "" {
query.SortOrder = "desc"
}
result, err := h.subscriptionAppService.ListSubscriptions(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取订阅列表失败", zap.Error(err))
@@ -1053,3 +1076,251 @@ func (h *ProductAdminHandler) DeleteProductDocumentation(c *gin.Context) {
h.responseBuilder.Success(c, nil, "文档删除成功")
}
// 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 *ProductAdminHandler) GetAdminApiCalls(c *gin.Context) {
// 解析查询参数
page := h.getIntQuery(c, "page", 1)
pageSize := h.getIntQuery(c, "page_size", 10)
// 构建筛选条件
filters := make(map[string]interface{})
// 用户ID筛选
if 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.apiAppService.GetAdminApiCalls(c.Request.Context(), filters, options)
if err != nil {
h.logger.Error("获取管理端API调用记录失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "获取API调用记录失败")
return
}
h.responseBuilder.Success(c, result, "获取API调用记录成功")
}
// GetAdminWalletTransactions 获取管理端消费记录
// @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 user_id query string false "用户ID"
// @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 "最大金额"
// @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.WalletTransactionListResponse "获取消费记录成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/wallet-transactions [get]
func (h *ProductAdminHandler) GetAdminWalletTransactions(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 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.financeAppService.GetAdminWalletTransactions(c.Request.Context(), filters, options)
if err != nil {
h.logger.Error("获取管理端消费记录失败", zap.Error(err))
h.responseBuilder.BadRequest(c, "获取消费记录失败")
return
}
h.responseBuilder.Success(c, result, "获取消费记录成功")
}
// GetAdminRechargeRecords 获取管理端充值记录
// @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 user_id query string false "用户ID"
// @Param recharge_type query string false "充值类型" Enums(alipay, transfer, gift)
// @Param status query string false "状态" Enums(pending, success, failed)
// @Param min_amount query string false "最小金额"
// @Param max_amount query string false "最大金额"
// @Param start_time query string false "开始时间" format(date-time)
// @Param end_time query string false "结束时间" format(date-time)
// @Param sort_by query string false "排序字段"
// @Param sort_order query string false "排序方向" Enums(asc, desc)
// @Success 200 {object} dto.RechargeRecordListResponse "获取充值记录成功"
// @Failure 400 {object} map[string]interface{} "请求参数错误"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/admin/recharge-records [get]
func (h *ProductAdminHandler) 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
}
// 金额范围筛选
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.financeAppService.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, "获取充值记录成功")
}

View File

@@ -415,7 +415,10 @@ func (h *ProductHandler) GetCategoryDetail(c *gin.Context) {
// @Security Bearer
// @Param page query int false "页码" default(1)
// @Param page_size query int false "每页数量" default(10)
// @Param status query string false "订阅状态"
// @Param keyword query string false "搜索关键词"
// @Param product_name 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} responses.SubscriptionListResponse "获取订阅列表成功"
@@ -432,7 +435,7 @@ func (h *ProductHandler) ListMySubscriptions(c *gin.Context) {
var query queries.ListSubscriptionsQuery
if err := h.validator.ValidateQuery(c, &query); err != nil {
return
return
}
// 设置默认值
@@ -446,6 +449,17 @@ func (h *ProductHandler) ListMySubscriptions(c *gin.Context) {
query.PageSize = 100
}
// 设置默认排序
if query.SortBy == "" {
query.SortBy = "created_at"
}
if query.SortOrder == "" {
query.SortOrder = "desc"
}
// 用户端不支持企业名称筛选,清空该字段
query.CompanyName = ""
result, err := h.subAppService.ListMySubscriptions(c.Request.Context(), userID, &query)
if err != nil {
h.logger.Error("获取我的订阅列表失败", zap.Error(err), zap.String("user_id", userID))
@@ -521,6 +535,13 @@ func (h *ProductHandler) GetMySubscriptionDetail(c *gin.Context) {
return
}
// 验证订阅是否属于当前用户
if result.UserID != userID {
h.logger.Error("用户尝试访问不属于自己的订阅", zap.String("user_id", userID), zap.String("subscription_user_id", result.UserID), zap.String("subscription_id", subscriptionID))
h.responseBuilder.Forbidden(c, "无权访问此订阅")
return
}
h.responseBuilder.Success(c, result, "获取我的订阅详情成功")
}
@@ -539,16 +560,33 @@ func (h *ProductHandler) GetMySubscriptionDetail(c *gin.Context) {
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/my/subscriptions/{id}/usage [get]
func (h *ProductHandler) GetMySubscriptionUsage(c *gin.Context) {
userID := c.GetString("user_id")
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未登录")
return
}
subscriptionID := c.Param("id")
if subscriptionID == "" {
h.responseBuilder.BadRequest(c, "订阅ID不能为空")
return
}
// 获取当前用户ID
userID := h.getCurrentUserID(c)
if userID == "" {
h.responseBuilder.Unauthorized(c, "用户未认证")
// 获取订阅信息以验证权限
var query queries.GetSubscriptionQuery
query.ID = subscriptionID
subscription, err := h.subAppService.GetSubscriptionByID(c.Request.Context(), &query)
if err != nil {
h.logger.Error("获取订阅信息失败", zap.Error(err), zap.String("user_id", userID), zap.String("subscription_id", subscriptionID))
h.responseBuilder.NotFound(c, "订阅不存在")
return
}
// 验证订阅是否属于当前用户
if subscription.UserID != userID {
h.logger.Error("用户尝试访问不属于自己的订阅使用情况", zap.String("user_id", userID), zap.String("subscription_user_id", subscription.UserID), zap.String("subscription_id", subscriptionID))
h.responseBuilder.Forbidden(c, "无权访问此订阅")
return
}

View File

@@ -322,6 +322,46 @@ func (h *UserHandler) ListUsers(c *gin.Context) {
h.response.Success(c, resp, "获取用户列表成功")
}
// GetUserDetail 管理员获取用户详情
// @Summary 管理员获取用户详情
// @Description 管理员获取指定用户的详细信息
// @Tags 用户管理
// @Accept json
// @Produce json
// @Security Bearer
// @Param user_id path string true "用户ID"
// @Success 200 {object} responses.UserDetailResponse "用户详情"
// @Failure 401 {object} map[string]interface{} "未认证"
// @Failure 403 {object} map[string]interface{} "权限不足"
// @Failure 404 {object} map[string]interface{} "用户不存在"
// @Failure 500 {object} map[string]interface{} "服务器内部错误"
// @Router /api/v1/users/admin/{user_id} [get]
func (h *UserHandler) GetUserDetail(c *gin.Context) {
// 检查管理员权限
userID := h.getCurrentUserID(c)
if userID == "" {
h.response.Unauthorized(c, "用户未登录")
return
}
// 获取路径参数中的用户ID
targetUserID := c.Param("user_id")
if targetUserID == "" {
h.response.BadRequest(c, "用户ID不能为空")
return
}
// 调用应用服务
resp, err := h.appService.GetUserDetail(c.Request.Context(), targetUserID)
if err != nil {
h.logger.Error("获取用户详情失败", zap.Error(err), zap.String("target_user_id", targetUserID))
h.response.BadRequest(c, "获取用户详情失败")
return
}
h.response.Success(c, resp, "获取用户详情成功")
}
// GetUserStats 管理员获取用户统计信息
// @Summary 管理员获取用户统计信息
// @Description 管理员获取用户统计信息,包括总用户数、活跃用户数、已认证用户数

View File

@@ -58,6 +58,18 @@ func (r *FinanceRoutes) Register(router *sharedhttp.GinRouter) {
}
}
// 发票相关路由,需要用户认证
invoiceGroup := engine.Group("/api/v1/invoices")
invoiceGroup.Use(r.authMiddleware.Handle())
{
invoiceGroup.POST("/apply", r.financeHandler.ApplyInvoice) // 申请开票
invoiceGroup.GET("/info", r.financeHandler.GetUserInvoiceInfo) // 获取用户发票信息
invoiceGroup.PUT("/info", r.financeHandler.UpdateUserInvoiceInfo) // 更新用户发票信息
invoiceGroup.GET("/records", r.financeHandler.GetUserInvoiceRecords) // 获取用户开票记录
invoiceGroup.GET("/available-amount", r.financeHandler.GetAvailableAmount) // 获取可开票金额
invoiceGroup.GET("/:application_id/download", r.financeHandler.DownloadInvoiceFile) // 下载发票文件
}
// 管理员财务路由组
adminFinanceGroup := engine.Group("/api/v1/admin/finance")
adminFinanceGroup.Use(r.adminAuthMiddleware.Handle())
@@ -67,5 +79,15 @@ func (r *FinanceRoutes) Register(router *sharedhttp.GinRouter) {
adminFinanceGroup.GET("/recharge-records", r.financeHandler.GetAdminRechargeRecords) // 管理员充值记录分页
}
// 管理员发票相关路由组
adminInvoiceGroup := engine.Group("/api/v1/admin/invoices")
adminInvoiceGroup.Use(r.adminAuthMiddleware.Handle())
{
adminInvoiceGroup.GET("/pending", r.financeHandler.GetPendingApplications) // 获取待处理申请列表
adminInvoiceGroup.POST("/:application_id/approve", r.financeHandler.ApproveInvoiceApplication) // 通过发票申请
adminInvoiceGroup.POST("/:application_id/reject", r.financeHandler.RejectInvoiceApplication) // 拒绝发票申请
adminInvoiceGroup.GET("/:application_id/download", r.financeHandler.AdminDownloadInvoiceFile) // 下载发票文件
}
r.logger.Info("财务路由注册完成")
}

View File

@@ -79,5 +79,23 @@ func (r *ProductAdminRoutes) Register(router *sharedhttp.GinRouter) {
subscriptions.GET("/stats", r.handler.GetSubscriptionStats)
subscriptions.PUT("/:id/price", r.handler.UpdateSubscriptionPrice)
}
// API调用记录管理
apiCalls := adminGroup.Group("/api-calls")
{
apiCalls.GET("", r.handler.GetAdminApiCalls)
}
// 消费记录管理
walletTransactions := adminGroup.Group("/wallet-transactions")
{
walletTransactions.GET("", r.handler.GetAdminWalletTransactions)
}
// 充值记录管理
rechargeRecords := adminGroup.Group("/recharge-records")
{
rechargeRecords.GET("", r.handler.GetAdminRechargeRecords)
}
}
}

View File

@@ -57,6 +57,7 @@ func (r *UserRoutes) Register(router *sharedhttp.GinRouter) {
adminGroup.Use(r.adminAuthMiddleware.Handle())
{
adminGroup.GET("/list", r.handler.ListUsers) // 管理员查看用户列表
adminGroup.GET("/:user_id", r.handler.GetUserDetail) // 管理员获取用户详情
adminGroup.GET("/stats", r.handler.GetUserStats) // 管理员获取用户统计信息
}
}