feat():learning后台管理项目初始化
This commit is contained in:
179
internal/api/README.md
Normal file
179
internal/api/README.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# API 层
|
||||
|
||||
本模块负责HTTP API的实现,提供RESTful接口。
|
||||
|
||||
## 功能特性
|
||||
|
||||
- RESTful API设计
|
||||
- JWT认证中间件
|
||||
- 跨域支持
|
||||
- 请求日志记录
|
||||
- 统一错误处理
|
||||
- 参数验证
|
||||
|
||||
## 模块结构
|
||||
|
||||
```
|
||||
api/
|
||||
├── middlewares/ # 中间件
|
||||
│ ├── auth.go # 认证中间件
|
||||
│ ├── cors.go # 跨域中间件
|
||||
│ └── logging.go # 日志中间件
|
||||
├── handlers/ # 请求处理器
|
||||
│ ├── auth_handler.go # 认证处理器
|
||||
│ ├── user_handler.go # 用户处理器
|
||||
│ ├── role_handler.go # 角色处理器
|
||||
│ └── menu_handler.go # 菜单处理器
|
||||
├── routes/ # 路由配置
|
||||
│ └── routes.go # 路由设置
|
||||
└── README.md # 说明文档
|
||||
```
|
||||
|
||||
## API 接口
|
||||
|
||||
### 认证接口
|
||||
|
||||
```
|
||||
POST /api/auth/login # 用户登录
|
||||
POST /api/auth/logout # 用户登出
|
||||
GET /api/profile # 获取用户信息
|
||||
PUT /api/profile # 更新用户信息
|
||||
PUT /api/change-password # 修改密码
|
||||
```
|
||||
|
||||
### 用户管理接口(管理员)
|
||||
|
||||
```
|
||||
POST /api/admin/users # 创建用户
|
||||
GET /api/admin/users # 获取用户列表
|
||||
GET /api/admin/users/:id # 获取用户详情
|
||||
PUT /api/admin/users/:id # 更新用户
|
||||
DELETE /api/admin/users/:id # 删除用户
|
||||
PUT /api/admin/users/:id/status # 更新用户状态
|
||||
```
|
||||
|
||||
### 角色管理接口(管理员)
|
||||
|
||||
```
|
||||
POST /api/admin/roles # 创建角色
|
||||
GET /api/admin/roles # 获取角色列表
|
||||
GET /api/admin/roles/:id # 获取角色详情
|
||||
PUT /api/admin/roles/:id # 更新角色
|
||||
DELETE /api/admin/roles/:id # 删除角色
|
||||
PUT /api/admin/roles/:id/status # 更新角色状态
|
||||
```
|
||||
|
||||
### 菜单管理接口(管理员)
|
||||
|
||||
```
|
||||
POST /api/admin/menus # 创建菜单
|
||||
GET /api/admin/menus # 获取菜单列表
|
||||
GET /api/admin/menus/tree # 获取菜单树
|
||||
GET /api/admin/menus/:id # 获取菜单详情
|
||||
PUT /api/admin/menus/:id # 更新菜单
|
||||
DELETE /api/admin/menus/:id # 删除菜单
|
||||
PUT /api/admin/menus/:id/status # 更新菜单状态
|
||||
PUT /api/admin/menus/:id/sort # 更新菜单排序
|
||||
```
|
||||
|
||||
## 请求示例
|
||||
|
||||
### 用户登录
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/api/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"username": "admin",
|
||||
"password": "password"
|
||||
}'
|
||||
```
|
||||
|
||||
### 获取用户列表
|
||||
|
||||
```bash
|
||||
curl -X GET "http://localhost:8080/api/admin/users?page=1&size=10&username=admin" \
|
||||
-H "Authorization: Bearer your-jwt-token"
|
||||
```
|
||||
|
||||
### 创建用户
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:8080/api/admin/users \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "Authorization: Bearer your-jwt-token" \
|
||||
-d '{
|
||||
"username": "newuser",
|
||||
"email": "newuser@example.com",
|
||||
"password": "password",
|
||||
"nickname": "新用户",
|
||||
"role": "user"
|
||||
}'
|
||||
```
|
||||
|
||||
## 响应格式
|
||||
|
||||
### 成功响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "操作成功",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"username": "admin",
|
||||
"email": "admin@example.com"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 分页响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "操作成功",
|
||||
"data": [...],
|
||||
"total": 100,
|
||||
"page": 1,
|
||||
"size": 10
|
||||
}
|
||||
```
|
||||
|
||||
### 错误响应
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 400,
|
||||
"message": "参数错误"
|
||||
}
|
||||
```
|
||||
|
||||
## 中间件
|
||||
|
||||
### 认证中间件
|
||||
|
||||
验证JWT token,将用户信息存储到上下文中。
|
||||
|
||||
### 管理员中间件
|
||||
|
||||
验证用户是否具有管理员权限。
|
||||
|
||||
### 跨域中间件
|
||||
|
||||
处理跨域请求,支持预检请求。
|
||||
|
||||
### 日志中间件
|
||||
|
||||
记录HTTP请求日志,包括请求方法、路径、状态码、响应时间等。
|
||||
|
||||
## 错误处理
|
||||
|
||||
所有API都遵循统一的错误处理模式:
|
||||
|
||||
- 参数验证错误:400 Bad Request
|
||||
- 认证失败:401 Unauthorized
|
||||
- 权限不足:403 Forbidden
|
||||
- 资源不存在:404 Not Found
|
||||
- 服务器错误:500 Internal Server Error
|
||||
|
||||
73
internal/api/handlers/audit_log_handler.go
Normal file
73
internal/api/handlers/audit_log_handler.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AuditLogHandler 审计日志处理器
|
||||
type AuditLogHandler struct {
|
||||
service services.AuditLogService
|
||||
response *utils.Response
|
||||
}
|
||||
|
||||
// NewAuditLogHandler 创建审计日志处理器
|
||||
func NewAuditLogHandler(service services.AuditLogService) *AuditLogHandler {
|
||||
return &AuditLogHandler{
|
||||
service: service,
|
||||
response: utils.NewResponse(),
|
||||
}
|
||||
}
|
||||
|
||||
// List 获取审计日志列表
|
||||
func (h *AuditLogHandler) List(c *gin.Context) {
|
||||
var req models.AuditLogListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if req.Page < 1 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.Size < 1 {
|
||||
req.Size = 20
|
||||
}
|
||||
if req.SortBy == "" {
|
||||
req.SortBy = "operation_time"
|
||||
}
|
||||
if req.SortOrder == "" {
|
||||
req.SortOrder = "desc"
|
||||
}
|
||||
|
||||
result, err := h.service.List(&req)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, result)
|
||||
}
|
||||
|
||||
// GetByID 获取审计日志详情
|
||||
func (h *AuditLogHandler) GetByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
log, err := h.service.GetByID(uint(id))
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, log)
|
||||
}
|
||||
633
internal/api/handlers/finance_handler.go
Normal file
633
internal/api/handlers/finance_handler.go
Normal file
@@ -0,0 +1,633 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
)
|
||||
|
||||
type FinanceHandler struct {
|
||||
response *utils.Response
|
||||
}
|
||||
|
||||
func NewFinanceHandler() *FinanceHandler {
|
||||
return &FinanceHandler{response: utils.NewResponse()}
|
||||
}
|
||||
|
||||
func getPageParams(c *gin.Context) (int, int) {
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if size < 1 {
|
||||
size = 20
|
||||
}
|
||||
offset := (page - 1) * size
|
||||
return offset, size
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) ListSandboxRecords(c *gin.Context) {
|
||||
offset, size := getPageParams(c)
|
||||
// 兼容多种参数命名:user/user_id,project/project_id
|
||||
userID := c.DefaultQuery("user_id", c.Query("user"))
|
||||
project := c.DefaultQuery("project", c.Query("project_id"))
|
||||
start := c.Query("start")
|
||||
end := c.Query("end")
|
||||
res, err := services.ListSandboxRecords(offset, size, userID, project, start, end)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Page(c, res.List, res.Total, offset/size+1, size)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) ListTokenUsages(c *gin.Context) {
|
||||
offset, size := getPageParams(c)
|
||||
userID := c.DefaultQuery("user_id", c.Query("user"))
|
||||
project := c.DefaultQuery("project", c.Query("project_id"))
|
||||
start := c.Query("start") // 对于token使用,按day进行过滤
|
||||
end := c.Query("end")
|
||||
res, err := services.ListTokenUsages(offset, size, userID, project, start, end)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Page(c, res.List, res.Total, offset/size+1, size)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) ListMCPUsages(c *gin.Context) {
|
||||
offset, size := getPageParams(c)
|
||||
userID := c.DefaultQuery("user_id", c.Query("user"))
|
||||
project := c.DefaultQuery("project", c.Query("project_id"))
|
||||
start := c.Query("start")
|
||||
end := c.Query("end")
|
||||
res, err := services.ListMCPUsages(offset, size, userID, project, start, end)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Page(c, res.List, res.Total, offset/size+1, size)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) ListTransactionLogs(c *gin.Context) {
|
||||
offset, size := getPageParams(c)
|
||||
userID := c.Query("user_id")
|
||||
orderID := c.Query("order_id")
|
||||
txType := c.Query("type")
|
||||
status := c.Query("status")
|
||||
start := c.Query("start")
|
||||
end := c.Query("end")
|
||||
res, err := services.ListTransactionLogs(offset, size, userID, orderID, txType, status, start, end)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Page(c, res.List, res.Total, offset/size+1, size)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) ListPaymentRecords(c *gin.Context) {
|
||||
offset, size := getPageParams(c)
|
||||
userID := c.Query("user_id")
|
||||
orderID := c.Query("order_id")
|
||||
paypalOrderID := c.Query("paypal_order_id")
|
||||
status := c.Query("status")
|
||||
refundStatus := c.Query("refund_status")
|
||||
payerEmail := c.Query("payer_email")
|
||||
start := c.Query("start")
|
||||
end := c.Query("end")
|
||||
res, err := services.ListPaymentRecords(offset, size, userID, orderID, paypalOrderID, status, refundStatus, payerEmail, start, end)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Page(c, res.List, res.Total, offset/size+1, size)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) RefundPaymentRecord(c *gin.Context) {
|
||||
var req struct {
|
||||
OrderID string `json:"order_id"`
|
||||
PayPalCaptureID string `json:"paypal_capture_id"`
|
||||
Amount *int64 `json:"amount"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := services.RefundPaymentRecord(req.OrderID, req.PayPalCaptureID, req.Amount)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "退款请求已提交"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) ListMcpAccountRechargeRecords(c *gin.Context) {
|
||||
offset, size := getPageParams(c)
|
||||
provider := c.Query("provider")
|
||||
account := c.Query("account")
|
||||
start := c.Query("start")
|
||||
end := c.Query("end")
|
||||
res, err := services.ListMcpAccountRechargeRecords(offset, size, provider, account, start, end)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Page(c, res.List, res.Total, offset/size+1, size)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) CreateMcpAccountRechargeRecord(c *gin.Context) {
|
||||
var req struct {
|
||||
ProviderID string `json:"provider_id" binding:"required"`
|
||||
Amount float64 `json:"amount" binding:"required,gt=0"`
|
||||
RechargeDate string `json:"recharge_date" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前操作人信息
|
||||
var operatorID interface{}
|
||||
operatorName := "系统管理员"
|
||||
if user, exists := c.Get("user"); exists {
|
||||
if userInfo, ok := user.(map[string]interface{}); ok {
|
||||
// 尝试从userInfo中获取sub字段(可能是UUID格式)
|
||||
if sub, ok := userInfo["sub"].(string); ok {
|
||||
operatorID = sub
|
||||
}
|
||||
if name, ok := userInfo["name"].(string); ok {
|
||||
operatorName = name
|
||||
} else if email, ok := userInfo["email"].(string); ok {
|
||||
operatorName = email
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果user中没有sub,尝试从user_id获取
|
||||
if operatorID == nil {
|
||||
operatorID, _ = c.Get("user_id")
|
||||
}
|
||||
|
||||
err := services.CreateMcpAccountRechargeRecord(
|
||||
req.ProviderID,
|
||||
req.Amount,
|
||||
req.RechargeDate,
|
||||
operatorID,
|
||||
operatorName,
|
||||
req.Remark,
|
||||
)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "创建成功"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) UpdateMcpAccountRechargeRecord(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var req struct {
|
||||
Amount *float64 `json:"amount"`
|
||||
RechargeDate *string `json:"recharge_date"`
|
||||
Remark *string `json:"remark"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := services.UpdateMcpAccountRechargeRecord(id, req.Amount, req.RechargeDate, req.Remark)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "更新成功"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) DeleteMcpAccountRechargeRecord(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
err := services.DeleteMcpAccountRechargeRecord(id)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "删除成功"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) GetMcpProviderAccounts(c *gin.Context) {
|
||||
status := c.Query("status")
|
||||
var isUsed *bool
|
||||
if v := c.Query("is_used"); v != "" {
|
||||
if v == "true" {
|
||||
val := true
|
||||
isUsed = &val
|
||||
} else if v == "false" {
|
||||
val := false
|
||||
isUsed = &val
|
||||
}
|
||||
}
|
||||
|
||||
list, err := services.GetMcpProviderAccounts(status, isUsed)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, list)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) GetMcpAccountBalances(c *gin.Context) {
|
||||
list, err := services.GetMcpAccountBalances()
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, list)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) GetMcpAccountBalanceHistory(c *gin.Context) {
|
||||
providerID := c.Param("provider_id")
|
||||
start := c.Query("start")
|
||||
end := c.Query("end")
|
||||
|
||||
list, err := services.GetMcpAccountBalanceHistory(providerID, start, end)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, list)
|
||||
}
|
||||
|
||||
// ========== 模型账号充值记录和余额管理 ==========
|
||||
|
||||
func (h *FinanceHandler) ListModelAccountRechargeRecords(c *gin.Context) {
|
||||
offset, size := getPageParams(c)
|
||||
provider := c.Query("provider")
|
||||
modelName := c.Query("model_name")
|
||||
start := c.Query("start")
|
||||
end := c.Query("end")
|
||||
res, err := services.ListModelAccountRechargeRecords(offset, size, provider, modelName, start, end)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Page(c, res.List, res.Total, offset/size+1, size)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) CreateModelAccountRechargeRecord(c *gin.Context) {
|
||||
var req struct {
|
||||
Account string `json:"account" binding:"required"`
|
||||
Amount float64 `json:"amount" binding:"required,gt=0"`
|
||||
RechargeDate string `json:"recharge_date" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前操作人信息
|
||||
var operatorID interface{}
|
||||
operatorName := "系统管理员"
|
||||
|
||||
// 先尝试从 user_id 获取
|
||||
if userID, exists := c.Get("user_id"); exists {
|
||||
operatorID = userID
|
||||
}
|
||||
|
||||
// 从 user 信息中获取名称
|
||||
if user, exists := c.Get("user"); exists {
|
||||
// user 可能是 *models.UserInfo 类型
|
||||
if userInfo, ok := user.(map[string]interface{}); ok {
|
||||
if name, ok := userInfo["name"].(string); ok && name != "" {
|
||||
operatorName = name
|
||||
} else if email, ok := userInfo["email"].(string); ok && email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
} else {
|
||||
// 尝试使用反射获取结构体字段
|
||||
userVal := reflect.ValueOf(user)
|
||||
if userVal.Kind() == reflect.Ptr {
|
||||
userVal = userVal.Elem()
|
||||
}
|
||||
if userVal.Kind() == reflect.Struct {
|
||||
if nameField := userVal.FieldByName("Name"); nameField.IsValid() && nameField.Kind() == reflect.String {
|
||||
if name := nameField.String(); name != "" {
|
||||
operatorName = name
|
||||
}
|
||||
}
|
||||
if operatorName == "系统管理员" {
|
||||
if emailField := userVal.FieldByName("Email"); emailField.IsValid() && emailField.Kind() == reflect.String {
|
||||
if email := emailField.String(); email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := services.CreateModelAccountRechargeRecord(
|
||||
req.Account,
|
||||
req.Amount,
|
||||
req.RechargeDate,
|
||||
operatorID,
|
||||
operatorName,
|
||||
req.Remark,
|
||||
)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "创建成功"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) UpdateModelAccountRechargeRecord(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var req struct {
|
||||
Amount *float64 `json:"amount"`
|
||||
RechargeDate *string `json:"recharge_date"`
|
||||
Remark *string `json:"remark"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := services.UpdateModelAccountRechargeRecord(id, req.Amount, req.RechargeDate, req.Remark)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "更新成功"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) DeleteModelAccountRechargeRecord(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
err := services.DeleteModelAccountRechargeRecord(id)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "删除成功"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) GetModelConfigAccounts(c *gin.Context) {
|
||||
var enabled *bool
|
||||
if v := c.Query("enabled"); v != "" {
|
||||
if v == "true" {
|
||||
val := true
|
||||
enabled = &val
|
||||
} else if v == "false" {
|
||||
val := false
|
||||
enabled = &val
|
||||
}
|
||||
}
|
||||
|
||||
list, err := services.GetModelConfigAccounts(enabled)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, list)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) GetModelAccountBalances(c *gin.Context) {
|
||||
list, err := services.GetModelAccountBalances()
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, list)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) GetModelAccountBalanceHistory(c *gin.Context) {
|
||||
account := c.Param("account")
|
||||
start := c.Query("start")
|
||||
end := c.Query("end")
|
||||
|
||||
list, err := services.GetModelAccountBalanceHistory(account, start, end)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, list)
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) AdjustMcpAccountBalance(c *gin.Context) {
|
||||
providerID := c.Param("provider_id")
|
||||
var req struct {
|
||||
Balance float64 `json:"balance" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
operatorName := "系统管理员"
|
||||
if user, exists := c.Get("user"); exists {
|
||||
if userInfo, ok := user.(map[string]interface{}); ok {
|
||||
if name, ok := userInfo["name"].(string); ok && name != "" {
|
||||
operatorName = name
|
||||
} else if email, ok := userInfo["email"].(string); ok && email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
} else {
|
||||
userVal := reflect.ValueOf(user)
|
||||
if userVal.Kind() == reflect.Ptr {
|
||||
userVal = userVal.Elem()
|
||||
}
|
||||
if userVal.Kind() == reflect.Struct {
|
||||
if nameField := userVal.FieldByName("Name"); nameField.IsValid() && nameField.Kind() == reflect.String {
|
||||
if name := nameField.String(); name != "" {
|
||||
operatorName = name
|
||||
}
|
||||
}
|
||||
if operatorName == "系统管理员" {
|
||||
if emailField := userVal.FieldByName("Email"); emailField.IsValid() && emailField.Kind() == reflect.String {
|
||||
if email := emailField.String(); email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := services.AdjustMcpAccountBalance(providerID, req.Balance, operatorName, req.Remark); err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, gin.H{"message": "更新成功"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) AdjustModelAccountBalance(c *gin.Context) {
|
||||
account := c.Param("account")
|
||||
var req struct {
|
||||
Balance float64 `json:"balance" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
operatorName := "系统管理员"
|
||||
if user, exists := c.Get("user"); exists {
|
||||
if userInfo, ok := user.(map[string]interface{}); ok {
|
||||
if name, ok := userInfo["name"].(string); ok && name != "" {
|
||||
operatorName = name
|
||||
} else if email, ok := userInfo["email"].(string); ok && email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
} else {
|
||||
userVal := reflect.ValueOf(user)
|
||||
if userVal.Kind() == reflect.Ptr {
|
||||
userVal = userVal.Elem()
|
||||
}
|
||||
if userVal.Kind() == reflect.Struct {
|
||||
if nameField := userVal.FieldByName("Name"); nameField.IsValid() && nameField.Kind() == reflect.String {
|
||||
if name := nameField.String(); name != "" {
|
||||
operatorName = name
|
||||
}
|
||||
}
|
||||
if operatorName == "系统管理员" {
|
||||
if emailField := userVal.FieldByName("Email"); emailField.IsValid() && emailField.Kind() == reflect.String {
|
||||
if email := emailField.String(); email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := services.AdjustModelAccountBalance(account, req.Balance, operatorName, req.Remark); err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, gin.H{"message": "更新成功"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) CreateModelAccountBalance(c *gin.Context) {
|
||||
var req struct {
|
||||
Account string `json:"account" binding:"required"`
|
||||
Balance float64 `json:"balance" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
operatorName := "系统管理员"
|
||||
if user, exists := c.Get("user"); exists {
|
||||
if userInfo, ok := user.(map[string]interface{}); ok {
|
||||
if name, ok := userInfo["name"].(string); ok && name != "" {
|
||||
operatorName = name
|
||||
} else if email, ok := userInfo["email"].(string); ok && email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
} else {
|
||||
userVal := reflect.ValueOf(user)
|
||||
if userVal.Kind() == reflect.Ptr {
|
||||
userVal = userVal.Elem()
|
||||
}
|
||||
if userVal.Kind() == reflect.Struct {
|
||||
if nameField := userVal.FieldByName("Name"); nameField.IsValid() && nameField.Kind() == reflect.String {
|
||||
if name := nameField.String(); name != "" {
|
||||
operatorName = name
|
||||
}
|
||||
}
|
||||
if operatorName == "系统管理员" {
|
||||
if emailField := userVal.FieldByName("Email"); emailField.IsValid() && emailField.Kind() == reflect.String {
|
||||
if email := emailField.String(); email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := services.CreateModelAccountBalanceRecord(req.Account, req.Balance, operatorName, req.Remark); err != nil {
|
||||
if strings.Contains(err.Error(), "已存在") {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
} else {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
h.response.Success(c, gin.H{"message": "创建成功"})
|
||||
}
|
||||
|
||||
func (h *FinanceHandler) CreateMcpAccountBalance(c *gin.Context) {
|
||||
var req struct {
|
||||
ProviderID string `json:"provider_id" binding:"required"`
|
||||
Balance float64 `json:"balance" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
operatorName := "系统管理员"
|
||||
if user, exists := c.Get("user"); exists {
|
||||
if userInfo, ok := user.(map[string]interface{}); ok {
|
||||
if name, ok := userInfo["name"].(string); ok && name != "" {
|
||||
operatorName = name
|
||||
} else if email, ok := userInfo["email"].(string); ok && email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
} else {
|
||||
userVal := reflect.ValueOf(user)
|
||||
if userVal.Kind() == reflect.Ptr {
|
||||
userVal = userVal.Elem()
|
||||
}
|
||||
if userVal.Kind() == reflect.Struct {
|
||||
if nameField := userVal.FieldByName("Name"); nameField.IsValid() && nameField.Kind() == reflect.String {
|
||||
if name := nameField.String(); name != "" {
|
||||
operatorName = name
|
||||
}
|
||||
}
|
||||
if operatorName == "系统管理员" {
|
||||
if emailField := userVal.FieldByName("Email"); emailField.IsValid() && emailField.Kind() == reflect.String {
|
||||
if email := emailField.String(); email != "" {
|
||||
operatorName = email
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := services.CreateMcpAccountBalanceRecord(req.ProviderID, req.Balance, operatorName, req.Remark); err != nil {
|
||||
if strings.Contains(err.Error(), "已存在") {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
} else {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "创建成功"})
|
||||
}
|
||||
245
internal/api/handlers/goalfymax_user_handler.go
Normal file
245
internal/api/handlers/goalfymax_user_handler.go
Normal file
@@ -0,0 +1,245 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type GoalfyMaxUserHandler struct {
|
||||
service services.GoalfyMaxUserService
|
||||
response *utils.Response
|
||||
}
|
||||
|
||||
func NewGoalfyMaxUserHandler(s services.GoalfyMaxUserService) *GoalfyMaxUserHandler {
|
||||
return &GoalfyMaxUserHandler{service: s, response: utils.NewResponse()}
|
||||
}
|
||||
|
||||
func (h *GoalfyMaxUserHandler) List(c *gin.Context) {
|
||||
var req models.GoalfyMaxUserListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
users, total, err := h.service.List(&req)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, gin.H{"users": users, "total": total, "page": req.Page, "size": req.Size})
|
||||
}
|
||||
|
||||
func (h *GoalfyMaxUserHandler) GetByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id64, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
user, err := h.service.GetByID(uint(id64))
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, user)
|
||||
}
|
||||
|
||||
func (h *GoalfyMaxUserHandler) Create(c *gin.Context) {
|
||||
var req models.GoalfyMaxUserCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
user, err := h.service.Create(&req)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, user)
|
||||
}
|
||||
|
||||
func (h *GoalfyMaxUserHandler) Update(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id64, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
var req models.GoalfyMaxUserUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
user, err := h.service.Update(uint(id64), &req)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, user)
|
||||
}
|
||||
|
||||
func (h *GoalfyMaxUserHandler) Delete(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id64, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
if err := h.service.Delete(uint(id64)); err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, gin.H{"message": "删除成功"})
|
||||
}
|
||||
|
||||
func (h *GoalfyMaxUserHandler) Ban(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id64, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
var req models.GoalfyMaxUserBanRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 从上下文获取当前管理员ID
|
||||
adminID := 0
|
||||
if userID, exists := c.Get("user_id"); exists {
|
||||
switch v := userID.(type) {
|
||||
case int:
|
||||
adminID = v
|
||||
case uint:
|
||||
adminID = int(v)
|
||||
case string:
|
||||
if parsedID, err := strconv.Atoi(v); err == nil {
|
||||
adminID = parsedID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := h.service.Ban(uint(id64), &req, adminID); err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, gin.H{"message": "封禁成功"})
|
||||
}
|
||||
|
||||
func (h *GoalfyMaxUserHandler) Unban(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id64, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
if err := h.service.Unban(uint(id64)); err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, gin.H{"message": "解封成功"})
|
||||
}
|
||||
|
||||
func (h *GoalfyMaxUserHandler) AddBalance(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id64, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
var req models.GoalfyMaxUserAddBalanceRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 从上下文获取当前管理员信息
|
||||
operatorID := 0
|
||||
operatorEmail := "system@goalfy.com"
|
||||
if userID, exists := c.Get("user_id"); exists {
|
||||
switch v := userID.(type) {
|
||||
case int:
|
||||
operatorID = v
|
||||
case uint:
|
||||
operatorID = int(v)
|
||||
case string:
|
||||
if parsedID, err := strconv.Atoi(v); err == nil {
|
||||
operatorID = parsedID
|
||||
}
|
||||
}
|
||||
}
|
||||
// 尝试获取用户邮箱(从userInfo中获取)
|
||||
if userInfo, exists := c.Get("user"); exists {
|
||||
if user, ok := userInfo.(*models.UserInfo); ok && user != nil {
|
||||
if user.Email != "" {
|
||||
operatorEmail = user.Email
|
||||
} else if user.PreferredUsername != "" {
|
||||
operatorEmail = user.PreferredUsername + "@goalfy.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取IP地址和UserAgent
|
||||
ipAddress := c.ClientIP()
|
||||
userAgent := c.Request.UserAgent()
|
||||
|
||||
if err := h.service.AddBalance(uint(id64), &req, operatorID, operatorEmail, ipAddress, userAgent); err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, gin.H{"message": "增加余额成功"})
|
||||
}
|
||||
|
||||
func (h *GoalfyMaxUserHandler) DeductBalance(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id64, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
var req models.GoalfyMaxUserAddBalanceRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 从上下文获取当前管理员信息
|
||||
operatorID := 0
|
||||
operatorEmail := "system@goalfy.com"
|
||||
if userID, exists := c.Get("user_id"); exists {
|
||||
switch v := userID.(type) {
|
||||
case int:
|
||||
operatorID = v
|
||||
case uint:
|
||||
operatorID = int(v)
|
||||
case string:
|
||||
if parsedID, err := strconv.Atoi(v); err == nil {
|
||||
operatorID = parsedID
|
||||
}
|
||||
}
|
||||
}
|
||||
// 尝试获取用户邮箱(从userInfo中获取)
|
||||
if userInfo, exists := c.Get("user"); exists {
|
||||
if user, ok := userInfo.(*models.UserInfo); ok && user != nil {
|
||||
if user.Email != "" {
|
||||
operatorEmail = user.Email
|
||||
} else if user.PreferredUsername != "" {
|
||||
operatorEmail = user.PreferredUsername + "@goalfy.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取IP地址和UserAgent
|
||||
ipAddress := c.ClientIP()
|
||||
userAgent := c.Request.UserAgent()
|
||||
|
||||
if err := h.service.DeductBalance(uint(id64), &req, operatorID, operatorEmail, ipAddress, userAgent); err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, gin.H{"message": "减少余额成功"})
|
||||
}
|
||||
254
internal/api/handlers/invite_code_application_handler.go
Normal file
254
internal/api/handlers/invite_code_application_handler.go
Normal file
@@ -0,0 +1,254 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
)
|
||||
|
||||
type InviteCodeApplicationHandler struct {
|
||||
service *services.InviteCodeApplicationService
|
||||
}
|
||||
|
||||
// NewInviteCodeApplicationHandler 创建邀请码申请处理器
|
||||
func NewInviteCodeApplicationHandler(db *gorm.DB) *InviteCodeApplicationHandler {
|
||||
return &InviteCodeApplicationHandler{
|
||||
service: services.NewInviteCodeApplicationService(db),
|
||||
}
|
||||
}
|
||||
|
||||
// SubmitApplication 提交邀请码申请(公开接口,官网使用)
|
||||
func (h *InviteCodeApplicationHandler) SubmitApplication(c *gin.Context) {
|
||||
var req models.InviteCodeApplicationCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "请求参数无效",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
application, err := h.service.SubmitApplication(&req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "申请已提交,我们将在1-2个工作日内处理您的申请",
|
||||
"data": application,
|
||||
})
|
||||
}
|
||||
|
||||
// GetApplicationList 获取申请列表(后台管理接口,需要权限)
|
||||
func (h *InviteCodeApplicationHandler) GetApplicationList(c *gin.Context) {
|
||||
var req models.InviteCodeApplicationListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "请求参数无效",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.Size <= 0 {
|
||||
req.Size = 20
|
||||
}
|
||||
|
||||
response, err := h.service.GetApplicationList(&req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "获取申请列表失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": response,
|
||||
})
|
||||
}
|
||||
|
||||
// GetStatistics 获取申请统计(后台管理接口,需要权限)
|
||||
func (h *InviteCodeApplicationHandler) GetStatistics(c *gin.Context) {
|
||||
stats, err := h.service.GetStatistics()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "获取统计信息失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": stats,
|
||||
})
|
||||
}
|
||||
|
||||
// ApproveApplication 审批通过申请(后台管理接口,需要权限)
|
||||
func (h *InviteCodeApplicationHandler) ApproveApplication(c *gin.Context) {
|
||||
var req models.InviteCodeApplicationApproveRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "请求参数无效",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 从上下文获取操作人信息(需要在中间件中设置)
|
||||
approvedBy := c.GetString("username")
|
||||
if approvedBy == "" {
|
||||
approvedBy = "admin"
|
||||
}
|
||||
|
||||
if err := h.service.ApproveApplication(&req, approvedBy); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "审批通过成功",
|
||||
})
|
||||
}
|
||||
|
||||
// RejectApplication 审批拒绝申请(后台管理接口,需要权限)
|
||||
func (h *InviteCodeApplicationHandler) RejectApplication(c *gin.Context) {
|
||||
var req models.InviteCodeApplicationRejectRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "请求参数无效",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 从上下文获取操作人信息(需要在中间件中设置)
|
||||
approvedBy := c.GetString("username")
|
||||
if approvedBy == "" {
|
||||
approvedBy = "admin"
|
||||
}
|
||||
|
||||
if err := h.service.RejectApplication(&req, approvedBy); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "审批拒绝成功",
|
||||
})
|
||||
}
|
||||
|
||||
// BatchApproveApplications 批量审批通过(后台管理接口,需要权限)
|
||||
func (h *InviteCodeApplicationHandler) BatchApproveApplications(c *gin.Context) {
|
||||
var req models.InviteCodeApplicationBatchApproveRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "请求参数无效",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 从上下文获取操作人信息(需要在中间件中设置)
|
||||
approvedBy := c.GetString("username")
|
||||
if approvedBy == "" {
|
||||
approvedBy = "admin"
|
||||
}
|
||||
|
||||
if err := h.service.BatchApproveApplications(&req, approvedBy); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "批量审批通过成功",
|
||||
})
|
||||
}
|
||||
|
||||
// BatchRejectApplications 批量审批拒绝(后台管理接口,需要权限)
|
||||
func (h *InviteCodeApplicationHandler) BatchRejectApplications(c *gin.Context) {
|
||||
var req models.InviteCodeApplicationBatchRejectRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"error": "请求参数无效",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 从上下文获取操作人信息(需要在中间件中设置)
|
||||
approvedBy := c.GetString("username")
|
||||
if approvedBy == "" {
|
||||
approvedBy = "admin"
|
||||
}
|
||||
|
||||
if err := h.service.BatchRejectApplications(&req, approvedBy); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "批量审批拒绝成功",
|
||||
})
|
||||
}
|
||||
|
||||
// GetPendingCount 获取待处理申请数量(后台管理接口,需要权限)
|
||||
func (h *InviteCodeApplicationHandler) GetPendingCount(c *gin.Context) {
|
||||
count, err := h.service.GetPendingApplicationsCount()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"error": "获取待处理数量失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": gin.H{
|
||||
"count": count,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterRoutes 注册路由
|
||||
func (h *InviteCodeApplicationHandler) RegisterRoutes(router *gin.RouterGroup, authMiddleware gin.HandlerFunc) {
|
||||
// 公开接口(官网提交申请)
|
||||
public := router.Group("/public")
|
||||
{
|
||||
public.POST("/invite-code/apply", h.SubmitApplication)
|
||||
}
|
||||
|
||||
// 需要认证的接口(后台管理)
|
||||
protected := router.Group("/invite-code/applications")
|
||||
protected.Use(authMiddleware)
|
||||
{
|
||||
protected.GET("", h.GetApplicationList)
|
||||
protected.GET("/statistics", h.GetStatistics)
|
||||
protected.GET("/pending-count", h.GetPendingCount)
|
||||
protected.POST("/approve", h.ApproveApplication)
|
||||
protected.POST("/reject", h.RejectApplication)
|
||||
protected.POST("/batch-approve", h.BatchApproveApplications)
|
||||
protected.POST("/batch-reject", h.BatchRejectApplications)
|
||||
}
|
||||
}
|
||||
458
internal/api/handlers/invite_code_handler.go
Normal file
458
internal/api/handlers/invite_code_handler.go
Normal file
@@ -0,0 +1,458 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"goalfymax-admin/internal/config"
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type InviteCodeHandler struct {
|
||||
service services.InviteCodeService
|
||||
userLevelService services.UserLevelConfigService
|
||||
}
|
||||
|
||||
func NewInviteCodeHandler(service services.InviteCodeService, userLevelService services.UserLevelConfigService) *InviteCodeHandler {
|
||||
return &InviteCodeHandler{
|
||||
service: service,
|
||||
userLevelService: userLevelService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetInviteCodeList 获取邀请码列表
|
||||
func (h *InviteCodeHandler) GetInviteCodeList(c *gin.Context) {
|
||||
var req models.InviteCodeListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 设置默认分页参数
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.Size <= 0 {
|
||||
req.Size = 20
|
||||
}
|
||||
|
||||
response, err := h.service.List(&req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"message": err.Error(),
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// enrich invite_url
|
||||
base := config.GetConfig().SSO.SSOServerURL
|
||||
base = strings.TrimRight(base, "/")
|
||||
enriched := make([]gin.H, 0, len(response.List))
|
||||
for _, item := range response.List {
|
||||
// 获取用户等级信息
|
||||
var userLevelName string
|
||||
if item.UserLevelID != nil {
|
||||
if level, err := h.userLevelService.GetByID(*item.UserLevelID); err == nil {
|
||||
userLevelName = level.LevelName
|
||||
}
|
||||
}
|
||||
|
||||
enriched = append(enriched, gin.H{
|
||||
"id": item.ID,
|
||||
"code": item.Code,
|
||||
"is_used": item.IsUsed,
|
||||
"client_id": item.ClientID,
|
||||
"client_label": config.GetClientValue(item.ClientID),
|
||||
"email": item.Email,
|
||||
"user_level_id": item.UserLevelID,
|
||||
"user_level_name": userLevelName,
|
||||
"expires_at": item.ExpiresAt,
|
||||
"created_at": item.CreatedAt,
|
||||
"invite_url": base + "/invite/" + item.Code,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "获取成功",
|
||||
"data": gin.H{
|
||||
"list": enriched,
|
||||
"total": response.Total,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetInviteCodeDetail 获取邀请码详情
|
||||
func (h *InviteCodeHandler) GetInviteCodeDetail(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "ID格式错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
inviteCode, err := h.service.GetByID(uint(id))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"code": 404,
|
||||
"message": "邀请码不存在",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
base := config.GetConfig().SSO.SSOServerURL
|
||||
base = strings.TrimRight(base, "/")
|
||||
|
||||
// 获取用户等级信息
|
||||
var userLevelName string
|
||||
if inviteCode.UserLevelID != nil {
|
||||
if level, err := h.userLevelService.GetByID(*inviteCode.UserLevelID); err == nil {
|
||||
userLevelName = level.LevelName
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "获取成功",
|
||||
"data": gin.H{
|
||||
"id": inviteCode.ID,
|
||||
"code": inviteCode.Code,
|
||||
"is_used": inviteCode.IsUsed,
|
||||
"client_id": inviteCode.ClientID,
|
||||
"client_label": config.GetClientValue(inviteCode.ClientID),
|
||||
"email": inviteCode.Email,
|
||||
"user_level_id": inviteCode.UserLevelID,
|
||||
"user_level_name": userLevelName,
|
||||
"expires_at": inviteCode.ExpiresAt,
|
||||
"created_at": inviteCode.CreatedAt,
|
||||
"invite_url": base + "/invite/" + inviteCode.Code,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// validateEmail 验证邮箱格式
|
||||
func validateEmail(email string) bool {
|
||||
emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`)
|
||||
return emailRegex.MatchString(email)
|
||||
}
|
||||
|
||||
// CreateInviteCode 创建邀请码(支持设置过期时间和邮箱列表)
|
||||
func (h *InviteCodeHandler) CreateInviteCode(c *gin.Context) {
|
||||
var req models.InviteCodeCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "参数错误:" + err.Error(),
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 验证邮箱格式
|
||||
if len(req.Emails) > 0 {
|
||||
for _, email := range req.Emails {
|
||||
if email != "" && !validateEmail(email) {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "邮箱格式不正确: " + email,
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 验证用户等级ID是否存在且启用
|
||||
if req.UserLevelID != nil {
|
||||
userLevel, err := h.userLevelService.GetByID(*req.UserLevelID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "用户等级不存在",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
if userLevel.Status != 1 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "用户等级已禁用",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
inviteCodes, err := h.service.Create(&req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"message": err.Error(),
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
base := config.GetConfig().SSO.SSOServerURL
|
||||
base = strings.TrimRight(base, "/")
|
||||
|
||||
// 如果只创建了一个邀请码,返回单个对象(向后兼容)
|
||||
if len(inviteCodes) == 1 {
|
||||
inviteCode := inviteCodes[0]
|
||||
var userLevelName string
|
||||
if inviteCode.UserLevelID != nil {
|
||||
if level, err := h.userLevelService.GetByID(*inviteCode.UserLevelID); err == nil {
|
||||
userLevelName = level.LevelName
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "创建成功",
|
||||
"data": gin.H{
|
||||
"id": inviteCode.ID,
|
||||
"code": inviteCode.Code,
|
||||
"is_used": inviteCode.IsUsed,
|
||||
"client_id": inviteCode.ClientID,
|
||||
"client_label": config.GetClientValue(inviteCode.ClientID),
|
||||
"email": inviteCode.Email,
|
||||
"user_level_id": inviteCode.UserLevelID,
|
||||
"user_level_name": userLevelName,
|
||||
"expires_at": inviteCode.ExpiresAt,
|
||||
"created_at": inviteCode.CreatedAt,
|
||||
"invite_url": base + "/invite/" + inviteCode.Code,
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 如果创建了多个邀请码,返回数组
|
||||
enriched := make([]gin.H, 0, len(inviteCodes))
|
||||
for _, inviteCode := range inviteCodes {
|
||||
var userLevelName string
|
||||
if inviteCode.UserLevelID != nil {
|
||||
if level, err := h.userLevelService.GetByID(*inviteCode.UserLevelID); err == nil {
|
||||
userLevelName = level.LevelName
|
||||
}
|
||||
}
|
||||
|
||||
enriched = append(enriched, gin.H{
|
||||
"id": inviteCode.ID,
|
||||
"code": inviteCode.Code,
|
||||
"is_used": inviteCode.IsUsed,
|
||||
"client_id": inviteCode.ClientID,
|
||||
"client_label": config.GetClientValue(inviteCode.ClientID),
|
||||
"email": inviteCode.Email,
|
||||
"user_level_id": inviteCode.UserLevelID,
|
||||
"user_level_name": userLevelName,
|
||||
"expires_at": inviteCode.ExpiresAt,
|
||||
"created_at": inviteCode.CreatedAt,
|
||||
"invite_url": base + "/invite/" + inviteCode.Code,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "创建成功",
|
||||
"data": enriched,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateInviteCode 更新邀请码(支持更新过期时间)
|
||||
func (h *InviteCodeHandler) UpdateInviteCode(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "ID格式错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req models.InviteCodeUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
inviteCode, err := h.service.Update(uint(id), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": err.Error(),
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
base := config.GetConfig().SSO.SSOServerURL
|
||||
base = strings.TrimRight(base, "/")
|
||||
|
||||
// 获取用户等级信息
|
||||
var userLevelName string
|
||||
if inviteCode.UserLevelID != nil {
|
||||
if level, err := h.userLevelService.GetByID(*inviteCode.UserLevelID); err == nil {
|
||||
userLevelName = level.LevelName
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "更新成功",
|
||||
"data": gin.H{
|
||||
"id": inviteCode.ID,
|
||||
"code": inviteCode.Code,
|
||||
"is_used": inviteCode.IsUsed,
|
||||
"client_id": inviteCode.ClientID,
|
||||
"client_label": config.GetClientValue(inviteCode.ClientID),
|
||||
"email": inviteCode.Email,
|
||||
"user_level_id": inviteCode.UserLevelID,
|
||||
"user_level_name": userLevelName,
|
||||
"expires_at": inviteCode.ExpiresAt,
|
||||
"created_at": inviteCode.CreatedAt,
|
||||
"invite_url": base + "/invite/" + inviteCode.Code,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteInviteCode 删除邀请码
|
||||
func (h *InviteCodeHandler) DeleteInviteCode(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "ID格式错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err = h.service.Delete(uint(id))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"message": err.Error(),
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "删除成功",
|
||||
"data": nil,
|
||||
})
|
||||
}
|
||||
|
||||
// GetInviteCodeStatistics 获取统计信息
|
||||
func (h *InviteCodeHandler) GetInviteCodeStatistics(c *gin.Context) {
|
||||
stats, err := h.service.GetStatistics()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"message": err.Error(),
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "获取成功",
|
||||
"data": stats,
|
||||
})
|
||||
}
|
||||
|
||||
// MarkInviteCodeAsUsed 标记邀请码为已使用
|
||||
func (h *InviteCodeHandler) MarkInviteCodeAsUsed(c *gin.Context) {
|
||||
var req struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err := h.service.MarkAsUsed(req.Code)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": err.Error(),
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "标记成功",
|
||||
"data": nil,
|
||||
})
|
||||
}
|
||||
|
||||
// ValidateInviteCode 验证邀请码是否有效
|
||||
func (h *InviteCodeHandler) ValidateInviteCode(c *gin.Context) {
|
||||
var req struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": "参数错误",
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
err := h.service.ValidateInviteCode(req.Code)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"message": err.Error(),
|
||||
"data": nil,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "邀请码有效",
|
||||
"data": nil,
|
||||
})
|
||||
}
|
||||
|
||||
// GetClientOptions 获取客户端选项列表
|
||||
func (h *InviteCodeHandler) GetClientOptions(c *gin.Context) {
|
||||
options := config.GetClientOptions()
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 200,
|
||||
"message": "获取成功",
|
||||
"data": options,
|
||||
})
|
||||
}
|
||||
186
internal/api/handlers/mcp_provider_handler.go
Normal file
186
internal/api/handlers/mcp_provider_handler.go
Normal file
@@ -0,0 +1,186 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"goalfymax-admin/internal/storage"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type MCPProvider struct {
|
||||
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey"`
|
||||
Provider string `json:"provider"`
|
||||
BaseURL string `json:"base_url"`
|
||||
Auth *string `json:"auth"`
|
||||
Account *string `json:"account"`
|
||||
PriceType string `json:"price_type"`
|
||||
Price float64 `json:"price"`
|
||||
FloatingRatio float64 `json:"floating_ratio"`
|
||||
IsUsed bool `json:"is_used"`
|
||||
Status string `json:"status"`
|
||||
Description *string `json:"description"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
type MCPProviderHandler struct {
|
||||
response *utils.Response
|
||||
}
|
||||
|
||||
func NewMCPProviderHandler() *MCPProviderHandler {
|
||||
return &MCPProviderHandler{response: utils.NewResponse()}
|
||||
}
|
||||
|
||||
func (h *MCPProviderHandler) List(c *gin.Context) {
|
||||
db := storage.GetPG()
|
||||
if db == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"message": "PostgreSQL未初始化"})
|
||||
return
|
||||
}
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
if size <= 0 {
|
||||
size = 10
|
||||
}
|
||||
offset := (page - 1) * size
|
||||
|
||||
q := db.Table("mcp_providers")
|
||||
if v := c.Query("provider"); v != "" {
|
||||
q = q.Where("provider ILIKE ?", "%"+v+"%")
|
||||
}
|
||||
if v := c.Query("status"); v != "" {
|
||||
q = q.Where("status = ?", v)
|
||||
}
|
||||
if v := c.Query("is_used"); v != "" {
|
||||
if v == "true" {
|
||||
q = q.Where("is_used = ?", true)
|
||||
} else if v == "false" {
|
||||
q = q.Where("is_used = ?", false)
|
||||
}
|
||||
}
|
||||
|
||||
var total int64
|
||||
if err := q.Count(&total).Error; err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
var list []MCPProvider
|
||||
if err := q.Order("created_at DESC").Offset(offset).Limit(size).Find(&list).Error; err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Page(c, list, total, page, size)
|
||||
}
|
||||
|
||||
func (h *MCPProviderHandler) GetByID(c *gin.Context) {
|
||||
db := storage.GetPG()
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效ID")
|
||||
return
|
||||
}
|
||||
var m MCPProvider
|
||||
if err := db.Table("mcp_providers").Where("id = ?", id).First(&m).Error; err != nil {
|
||||
h.response.NotFound(c, "记录不存在")
|
||||
return
|
||||
}
|
||||
h.response.Success(c, m)
|
||||
}
|
||||
|
||||
func (h *MCPProviderHandler) Create(c *gin.Context) {
|
||||
db := storage.GetPG()
|
||||
var req map[string]any
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := db.Table("mcp_providers").Create(req).Error; err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, "创建成功")
|
||||
}
|
||||
|
||||
func (h *MCPProviderHandler) Update(c *gin.Context) {
|
||||
db := storage.GetPG()
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效ID")
|
||||
return
|
||||
}
|
||||
var req map[string]any
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
delete(req, "id")
|
||||
if err := db.Table("mcp_providers").Where("id = ?", id).Updates(req).Error; err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, "更新成功")
|
||||
}
|
||||
|
||||
func (h *MCPProviderHandler) Delete(c *gin.Context) {
|
||||
db := storage.GetPG()
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效ID")
|
||||
return
|
||||
}
|
||||
if err := db.Table("mcp_providers").Where("id = ?", id).Delete(nil).Error; err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, "删除成功")
|
||||
}
|
||||
|
||||
func (h *MCPProviderHandler) UpdateStatus(c *gin.Context) {
|
||||
db := storage.GetPG()
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效ID")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
Status string `json:"status" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := db.Table("mcp_providers").Where("id = ?", id).Update("status", body.Status).Error; err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, "状态更新成功")
|
||||
}
|
||||
|
||||
func (h *MCPProviderHandler) UpdateIsUsed(c *gin.Context) {
|
||||
db := storage.GetPG()
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效ID")
|
||||
return
|
||||
}
|
||||
var body struct {
|
||||
IsUsed bool `json:"is_used" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := db.Table("mcp_providers").Where("id = ?", id).Update("is_used", body.IsUsed).Error; err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.Success(c, "使用状态更新成功")
|
||||
}
|
||||
134
internal/api/handlers/message_push_handler.go
Normal file
134
internal/api/handlers/message_push_handler.go
Normal file
@@ -0,0 +1,134 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// MessagePushHandler 消息推送处理器
|
||||
type MessagePushHandler struct {
|
||||
service services.MessagePushService
|
||||
response *utils.Response
|
||||
}
|
||||
|
||||
// NewMessagePushHandler 创建消息推送处理器
|
||||
func NewMessagePushHandler(service services.MessagePushService) *MessagePushHandler {
|
||||
return &MessagePushHandler{
|
||||
service: service,
|
||||
response: utils.NewResponse(),
|
||||
}
|
||||
}
|
||||
|
||||
// SendMessage 发送消息
|
||||
func (h *MessagePushHandler) SendMessage(c *gin.Context) {
|
||||
var req models.MessagePushRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前用户信息
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
h.response.Unauthorized(c, "无法获取用户信息")
|
||||
return
|
||||
}
|
||||
|
||||
// 处理用户ID类型
|
||||
var senderID int
|
||||
switch v := userID.(type) {
|
||||
case int:
|
||||
senderID = v
|
||||
case uint:
|
||||
senderID = int(v)
|
||||
case string:
|
||||
var err error
|
||||
senderID, err = strconv.Atoi(v)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, "用户ID格式错误")
|
||||
return
|
||||
}
|
||||
default:
|
||||
h.response.InternalServerError(c, "用户ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取发送人姓名(从用户信息中获取)
|
||||
senderName := "系统管理员"
|
||||
if user, exists := c.Get("user"); exists {
|
||||
if userInfo, ok := user.(map[string]interface{}); ok {
|
||||
if name, ok := userInfo["name"].(string); ok {
|
||||
senderName = name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result, err := h.service.SendMessage(c.Request.Context(), &req, senderID, senderName)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, result)
|
||||
}
|
||||
|
||||
// GetPushLogs 获取推送记录列表
|
||||
func (h *MessagePushHandler) GetPushLogs(c *gin.Context) {
|
||||
var req models.MessagePushListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.service.GetPushLogs(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, result)
|
||||
}
|
||||
|
||||
// GetPushLogByID 根据ID获取推送记录
|
||||
func (h *MessagePushHandler) GetPushLogByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的推送记录ID")
|
||||
return
|
||||
}
|
||||
|
||||
log, err := h.service.GetPushLogByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err.Error() == "推送记录不存在" {
|
||||
h.response.NotFound(c, "推送记录不存在")
|
||||
return
|
||||
}
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, log)
|
||||
}
|
||||
|
||||
// SearchUsers 搜索用户
|
||||
func (h *MessagePushHandler) SearchUsers(c *gin.Context) {
|
||||
var req models.UserSearchRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.service.SearchUsers(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, result)
|
||||
}
|
||||
144
internal/api/handlers/page_handler.go
Normal file
144
internal/api/handlers/page_handler.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// PageHandler 页面处理器
|
||||
type PageHandler struct {
|
||||
pageService services.PageService
|
||||
response *utils.Response
|
||||
logger *utils.Logger
|
||||
}
|
||||
|
||||
// NewPageHandler 创建页面处理器
|
||||
func NewPageHandler(pageService services.PageService, logger *utils.Logger) *PageHandler {
|
||||
return &PageHandler{
|
||||
pageService: pageService,
|
||||
response: utils.NewResponse(),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Create 创建页面
|
||||
func (h *PageHandler) Create(c *gin.Context) {
|
||||
var req models.PageCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("参数绑定失败", zap.Error(err))
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
page, err := h.pageService.Create(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("创建页面失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, page)
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取页面
|
||||
func (h *PageHandler) GetByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.logger.Error("页面ID格式错误", zap.Error(err))
|
||||
h.response.BadRequest(c, "无效的页面ID")
|
||||
return
|
||||
}
|
||||
|
||||
page, err := h.pageService.GetByID(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("获取页面失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, page)
|
||||
}
|
||||
|
||||
// Update 更新页面
|
||||
func (h *PageHandler) Update(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.logger.Error("页面ID格式错误", zap.Error(err))
|
||||
h.response.BadRequest(c, "无效的页面ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.PageUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("参数绑定失败", zap.Error(err))
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
page, err := h.pageService.Update(uint(id), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("更新页面失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, page)
|
||||
}
|
||||
|
||||
// Delete 删除页面
|
||||
func (h *PageHandler) Delete(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.logger.Error("页面ID格式错误", zap.Error(err))
|
||||
h.response.BadRequest(c, "无效的页面ID")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.pageService.Delete(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("删除页面失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "页面删除成功"})
|
||||
}
|
||||
|
||||
// List 获取页面列表
|
||||
func (h *PageHandler) List(c *gin.Context) {
|
||||
var req models.PageListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.logger.Error("参数绑定失败", zap.Error(err))
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
pages, total, err := h.pageService.List(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("获取页面列表失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 如果没有分页参数,直接返回页面列表
|
||||
if req.Page <= 0 || req.Size <= 0 {
|
||||
h.response.Success(c, pages)
|
||||
return
|
||||
}
|
||||
|
||||
// 有分页参数时返回完整的分页信息
|
||||
h.response.Success(c, gin.H{
|
||||
"pages": pages,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"size": req.Size,
|
||||
})
|
||||
}
|
||||
117
internal/api/handlers/quota_handler.go
Normal file
117
internal/api/handlers/quota_handler.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
)
|
||||
|
||||
// QuotaHandler 配额处理器
|
||||
type QuotaHandler struct {
|
||||
quotaService services.QuotaService
|
||||
response *utils.Response
|
||||
}
|
||||
|
||||
// NewQuotaHandler 创建配额处理器
|
||||
func NewQuotaHandler(quotaService services.QuotaService) *QuotaHandler {
|
||||
return &QuotaHandler{
|
||||
quotaService: quotaService,
|
||||
response: utils.NewResponse(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetQuotaHistory 获取配额历史数据
|
||||
func (h *QuotaHandler) GetQuotaHistory(c *gin.Context) {
|
||||
var req models.QuotaHistoryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务层
|
||||
response, err := h.quotaService.GetQuotaHistory(&req)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 返回响应
|
||||
if response.Success {
|
||||
h.response.Success(c, response.Data)
|
||||
} else {
|
||||
h.response.BadRequest(c, response.Message)
|
||||
}
|
||||
}
|
||||
|
||||
// HealthCheck 健康检查
|
||||
func (h *QuotaHandler) HealthCheck(c *gin.Context) {
|
||||
err := h.quotaService.HealthCheck()
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, "配额服务健康检查失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"status": "ok", "service": "quota"})
|
||||
}
|
||||
|
||||
// GetQuotaRules 获取配额规则列表(透传网关)
|
||||
func (h *QuotaHandler) GetQuotaRules(c *gin.Context) {
|
||||
resp, err := h.quotaService.GetQuotaRules()
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
if resp.Success {
|
||||
// 透传 data
|
||||
c.JSON(200, gin.H{
|
||||
"code": 200,
|
||||
"message": "操作成功",
|
||||
"data": resp.Data,
|
||||
})
|
||||
return
|
||||
}
|
||||
h.response.BadRequest(c, resp.Message)
|
||||
}
|
||||
|
||||
// CreateQuotaRule 创建配额规则(代理网关)
|
||||
func (h *QuotaHandler) CreateQuotaRule(c *gin.Context) {
|
||||
var body map[string]any
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
resp, err := h.quotaService.CreateQuotaRule(body)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"code": 200, "message": "操作成功", "data": resp.Data})
|
||||
}
|
||||
|
||||
// UpdateQuotaRule 更新配额规则(代理网关)
|
||||
func (h *QuotaHandler) UpdateQuotaRule(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
var body map[string]any
|
||||
if err := c.ShouldBindJSON(&body); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
resp, err := h.quotaService.UpdateQuotaRule(id, body)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"code": 200, "message": "操作成功", "data": resp.Data})
|
||||
}
|
||||
|
||||
// DeleteQuotaRule 删除配额规则(代理网关)
|
||||
func (h *QuotaHandler) DeleteQuotaRule(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
resp, err := h.quotaService.DeleteQuotaRule(id)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(200, gin.H{"code": 200, "message": "操作成功", "data": resp.Data})
|
||||
}
|
||||
182
internal/api/handlers/rbac_handler.go
Normal file
182
internal/api/handlers/rbac_handler.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// RBACHandler 简化的RBAC处理器
|
||||
type RBACHandler struct {
|
||||
rbacService services.RBACService
|
||||
response *utils.Response
|
||||
logger *utils.Logger
|
||||
}
|
||||
|
||||
// NewRBACHandler 创建RBAC处理器
|
||||
func NewRBACHandler(rbacService services.RBACService, logger *utils.Logger) *RBACHandler {
|
||||
return &RBACHandler{
|
||||
rbacService: rbacService,
|
||||
response: utils.NewResponse(),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// AssignRolePagePermissions 分配角色页面权限
|
||||
func (h *RBACHandler) AssignRolePagePermissions(c *gin.Context) {
|
||||
var req models.RolePagePermissionAssignRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("参数绑定失败", zap.Error(err))
|
||||
h.response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
err := h.rbacService.AssignRolePagePermissions(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("分配角色页面权限失败", zap.Error(err))
|
||||
h.response.Error(c, 500, "分配角色页面权限失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "分配角色页面权限成功")
|
||||
}
|
||||
|
||||
// RemoveRolePagePermissions 移除角色页面权限
|
||||
func (h *RBACHandler) RemoveRolePagePermissions(c *gin.Context) {
|
||||
roleIDStr := c.Param("id")
|
||||
roleID, err := strconv.ParseUint(roleIDStr, 10, 32)
|
||||
if err != nil {
|
||||
h.logger.Error("角色ID格式错误", zap.Error(err))
|
||||
h.response.Error(c, 400, "角色ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
PageIDs []uint `json:"pageIds" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.logger.Error("参数绑定失败", zap.Error(err))
|
||||
h.response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.rbacService.RemoveRolePagePermissions(uint(roleID), req.PageIDs)
|
||||
if err != nil {
|
||||
h.logger.Error("移除角色页面权限失败", zap.Error(err))
|
||||
h.response.Error(c, 500, "移除角色页面权限失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "移除角色页面权限成功")
|
||||
}
|
||||
|
||||
// GetRolePagePermissions 获取角色页面权限
|
||||
func (h *RBACHandler) GetRolePagePermissions(c *gin.Context) {
|
||||
roleIDStr := c.Param("id")
|
||||
roleID, err := strconv.ParseUint(roleIDStr, 10, 32)
|
||||
if err != nil {
|
||||
h.logger.Error("角色ID格式错误", zap.Error(err))
|
||||
h.response.Error(c, 400, "角色ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
pages, err := h.rbacService.GetRolePagePermissions(uint(roleID))
|
||||
if err != nil {
|
||||
h.logger.Error("获取角色页面权限失败", zap.Error(err))
|
||||
h.response.Error(c, 500, "获取角色页面权限失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, pages)
|
||||
}
|
||||
|
||||
// GetUserPermissions 获取用户权限
|
||||
func (h *RBACHandler) GetUserPermissions(c *gin.Context) {
|
||||
userIDStr := c.Param("id")
|
||||
userID, err := strconv.ParseUint(userIDStr, 10, 32)
|
||||
if err != nil {
|
||||
h.logger.Error("用户ID格式错误", zap.Error(err))
|
||||
h.response.Error(c, 400, "用户ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
permissions, err := h.rbacService.GetUserPermissionsResponse(uint(userID))
|
||||
if err != nil {
|
||||
h.logger.Error("获取用户权限失败", zap.Error(err))
|
||||
h.response.Error(c, 500, "获取用户权限失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, permissions)
|
||||
}
|
||||
|
||||
// GetRolePermissions 获取角色权限
|
||||
func (h *RBACHandler) GetRolePermissions(c *gin.Context) {
|
||||
roleIDStr := c.Param("id")
|
||||
roleID, err := strconv.ParseUint(roleIDStr, 10, 32)
|
||||
if err != nil {
|
||||
h.logger.Error("角色ID格式错误", zap.Error(err))
|
||||
h.response.Error(c, 400, "角色ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
permissions, err := h.rbacService.GetRolePagePermissionsResponse(uint(roleID))
|
||||
if err != nil {
|
||||
h.logger.Error("获取角色权限失败", zap.Error(err))
|
||||
h.response.Error(c, 500, "获取角色权限失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, permissions)
|
||||
}
|
||||
|
||||
// CheckPagePermission 检查页面权限
|
||||
func (h *RBACHandler) CheckPagePermission(c *gin.Context) {
|
||||
userIDStr := c.Query("user_id")
|
||||
pagePath := c.Query("page_path")
|
||||
|
||||
userID, err := strconv.ParseUint(userIDStr, 10, 32)
|
||||
if err != nil {
|
||||
h.logger.Error("用户ID格式错误", zap.Error(err))
|
||||
h.response.Error(c, 400, "用户ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
hasPermission, err := h.rbacService.CheckUserPagePermission(uint(userID), pagePath)
|
||||
if err != nil {
|
||||
h.logger.Error("检查页面权限失败", zap.Error(err))
|
||||
h.response.Error(c, 500, "检查页面权限失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{
|
||||
"hasPermission": hasPermission,
|
||||
"pagePath": pagePath,
|
||||
})
|
||||
}
|
||||
|
||||
// GetUserAccessiblePages 获取用户可访问页面
|
||||
func (h *RBACHandler) GetUserAccessiblePages(c *gin.Context) {
|
||||
userIDStr := c.Param("id")
|
||||
userID, err := strconv.ParseUint(userIDStr, 10, 32)
|
||||
if err != nil {
|
||||
h.logger.Error("用户ID格式错误", zap.Error(err))
|
||||
h.response.Error(c, 400, "用户ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
pages, err := h.rbacService.GetUserAccessiblePages(uint(userID))
|
||||
if err != nil {
|
||||
h.logger.Error("获取用户可访问页面失败", zap.Error(err))
|
||||
h.response.Error(c, 500, "获取用户可访问页面失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{
|
||||
"pages": pages,
|
||||
})
|
||||
}
|
||||
228
internal/api/handlers/role_handler.go
Normal file
228
internal/api/handlers/role_handler.go
Normal file
@@ -0,0 +1,228 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// RoleHandler 角色处理器
|
||||
type RoleHandler struct {
|
||||
roleService services.RoleService
|
||||
rbacService services.RBACService
|
||||
response *utils.Response
|
||||
logger *utils.Logger
|
||||
}
|
||||
|
||||
// NewRoleHandler 创建角色处理器
|
||||
func NewRoleHandler(roleService services.RoleService, rbacService services.RBACService, logger *utils.Logger) *RoleHandler {
|
||||
return &RoleHandler{
|
||||
roleService: roleService,
|
||||
rbacService: rbacService,
|
||||
response: utils.NewResponse(),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Create 创建角色
|
||||
func (h *RoleHandler) Create(c *gin.Context) {
|
||||
var req models.RoleCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
role, err := h.roleService.Create(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("创建角色失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, role)
|
||||
}
|
||||
|
||||
// GetByID 获取角色详情
|
||||
func (h *RoleHandler) GetByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的角色ID")
|
||||
return
|
||||
}
|
||||
|
||||
role, err := h.roleService.GetByID(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("获取角色失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, role)
|
||||
}
|
||||
|
||||
// Update 更新角色
|
||||
func (h *RoleHandler) Update(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的角色ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.RoleUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
role, err := h.roleService.Update(uint(id), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("更新角色失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, role)
|
||||
}
|
||||
|
||||
// Delete 删除角色
|
||||
func (h *RoleHandler) Delete(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的角色ID")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.roleService.Delete(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("删除角色失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "角色删除成功"})
|
||||
}
|
||||
|
||||
// List 获取角色列表
|
||||
func (h *RoleHandler) List(c *gin.Context) {
|
||||
var req models.RoleListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
roles, total, err := h.roleService.List(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("获取角色列表失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 为每个角色获取页面权限
|
||||
rolesWithPermissions := make([]gin.H, len(roles))
|
||||
for i, role := range roles {
|
||||
// 获取角色的页面权限
|
||||
pages, err := h.rbacService.GetRolePagePermissions(role.ID)
|
||||
if err != nil {
|
||||
h.logger.Warn("获取角色页面权限失败", zap.Uint("roleId", role.ID), zap.Error(err))
|
||||
pages = []models.Page{} // 如果获取失败,返回空数组
|
||||
}
|
||||
|
||||
rolesWithPermissions[i] = gin.H{
|
||||
"id": role.ID,
|
||||
"name": role.Name,
|
||||
"level": role.Level,
|
||||
"description": role.Description,
|
||||
"isDefault": role.IsDefault,
|
||||
"createdAt": role.CreatedAt,
|
||||
"updatedAt": role.UpdatedAt,
|
||||
"deletedAt": role.DeletedAt,
|
||||
"pages": pages, // 添加页面权限信息
|
||||
}
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{
|
||||
"roles": rolesWithPermissions,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"size": req.Size,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateStatus 更新角色状态
|
||||
func (h *RoleHandler) UpdateStatus(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的角色ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Status int `json:"status" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.roleService.UpdateStatus(uint(id), req.Status)
|
||||
if err != nil {
|
||||
h.logger.Error("更新角色状态失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "角色状态更新成功"})
|
||||
}
|
||||
|
||||
// UpdatePermissions 更新角色权限
|
||||
func (h *RoleHandler) UpdatePermissions(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的角色ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.RolePagePermissionAssignRequest
|
||||
req.RoleID = uint(id)
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.rbacService.AssignRolePagePermissions(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("更新角色权限失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "角色权限更新成功"})
|
||||
}
|
||||
|
||||
// GetRolePermissions 获取角色权限
|
||||
func (h *RoleHandler) GetRolePermissions(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的角色ID")
|
||||
return
|
||||
}
|
||||
|
||||
permissions, err := h.rbacService.GetRolePagePermissions(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("获取角色权限失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"permissions": permissions})
|
||||
}
|
||||
267
internal/api/handlers/sso_handler.go
Normal file
267
internal/api/handlers/sso_handler.go
Normal file
@@ -0,0 +1,267 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// SSOHandler SSO处理器
|
||||
type SSOHandler struct {
|
||||
ssoService services.SSOService
|
||||
response *utils.Response
|
||||
logger *utils.Logger
|
||||
}
|
||||
|
||||
// NewSSOHandler 创建SSO处理器
|
||||
func NewSSOHandler(ssoService services.SSOService, logger *utils.Logger) *SSOHandler {
|
||||
return &SSOHandler{
|
||||
ssoService: ssoService,
|
||||
response: utils.NewResponse(),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// HandleSSOLogin 处理SSO登录请求(合并登录和回调逻辑)
|
||||
func (h *SSOHandler) HandleSSOLogin(c *gin.Context) {
|
||||
if c.Request.Method != http.MethodPost {
|
||||
h.response.BadRequest(c, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.SSOCallbackRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
// 解析失败时,走登录逻辑
|
||||
h.handleLoginLogic(c, models.SSOLoginRequest{})
|
||||
return
|
||||
}
|
||||
|
||||
// 如果code为空,走登录逻辑
|
||||
if req.Code == "" {
|
||||
h.handleLoginLogic(c, models.SSOLoginRequest{})
|
||||
return
|
||||
}
|
||||
|
||||
// 如果code不为空,走回调逻辑
|
||||
h.handleCallbackLogic(c, req)
|
||||
}
|
||||
|
||||
// HandleSSOCallback 处理SSO回调
|
||||
func (h *SSOHandler) HandleSSOCallback(c *gin.Context) {
|
||||
if c.Request.Method != http.MethodPost {
|
||||
h.response.BadRequest(c, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.SSOCallbackRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 验证参数
|
||||
if req.Code == "" || req.State == "" {
|
||||
h.response.BadRequest(c, "Code and state are required")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务层处理回调
|
||||
response, err := h.ssoService.HandleCallback(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to handle SSO callback", zap.Error(err))
|
||||
// 返回更具体的错误信息,避免前端重复尝试
|
||||
if strings.Contains(err.Error(), "password") {
|
||||
h.response.BadRequest(c, "数据库表结构错误,请联系管理员")
|
||||
} else {
|
||||
h.response.InternalServerError(c, "SSO登录处理失败,请稍后重试")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, response)
|
||||
}
|
||||
|
||||
// HandleRefreshToken 处理令牌刷新
|
||||
func (h *SSOHandler) HandleRefreshToken(c *gin.Context) {
|
||||
if c.Request.Method != http.MethodPost {
|
||||
h.response.BadRequest(c, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.RefreshTokenRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
if req.RefreshToken == "" {
|
||||
h.response.BadRequest(c, "Refresh token is required")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务层刷新令牌
|
||||
response, err := h.ssoService.RefreshToken(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to refresh token", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, response)
|
||||
}
|
||||
|
||||
// HandleLogout 处理登出请求
|
||||
func (h *SSOHandler) HandleLogout(c *gin.Context) {
|
||||
if c.Request.Method != http.MethodPost {
|
||||
h.response.BadRequest(c, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// 从Authorization头获取访问令牌
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
h.response.Unauthorized(c, "Authorization header is required")
|
||||
return
|
||||
}
|
||||
|
||||
// 提取Bearer令牌
|
||||
token := ""
|
||||
if len(authHeader) > 7 && authHeader[:7] == "Bearer " {
|
||||
token = authHeader[7:]
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
h.response.Unauthorized(c, "Invalid authorization header")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务层登出
|
||||
response, err := h.ssoService.Logout(c.Request.Context(), token)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to logout", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, response)
|
||||
}
|
||||
|
||||
// HandleUserInfo 处理用户信息请求
|
||||
func (h *SSOHandler) HandleUserInfo(c *gin.Context) {
|
||||
if c.Request.Method != http.MethodGet {
|
||||
h.response.BadRequest(c, "Method not allowed")
|
||||
return
|
||||
}
|
||||
|
||||
// 从Authorization头获取访问令牌
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
h.response.Unauthorized(c, "Authorization header is required")
|
||||
return
|
||||
}
|
||||
|
||||
// 提取Bearer令牌
|
||||
token := ""
|
||||
if len(authHeader) > 7 && authHeader[:7] == "Bearer " {
|
||||
token = authHeader[7:]
|
||||
}
|
||||
|
||||
if token == "" {
|
||||
h.response.Unauthorized(c, "Invalid authorization header")
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务层获取用户信息
|
||||
response, err := h.ssoService.GetUserInfo(c.Request.Context(), token)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to get user info", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, response)
|
||||
}
|
||||
|
||||
// handleLoginLogic 处理登录逻辑
|
||||
func (h *SSOHandler) handleLoginLogic(c *gin.Context, req models.SSOLoginRequest) {
|
||||
// 调用服务层初始化登录
|
||||
response, err := h.ssoService.InitiateLogin(c.Request.Context())
|
||||
if err != nil {
|
||||
h.logger.Error("failed to initiate SSO login", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, response)
|
||||
}
|
||||
|
||||
// handleCallbackLogic 处理回调逻辑
|
||||
func (h *SSOHandler) handleCallbackLogic(c *gin.Context, req models.SSOCallbackRequest) {
|
||||
// 验证参数
|
||||
if req.State == "" {
|
||||
// 参数缺失时,走登录逻辑
|
||||
h.handleLoginLogic(c, models.SSOLoginRequest{})
|
||||
return
|
||||
}
|
||||
|
||||
// 调用服务层处理回调
|
||||
response, err := h.ssoService.HandleCallback(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to handle SSO callback", zap.Error(err))
|
||||
// 回调失败时,走登录逻辑
|
||||
h.handleLoginLogic(c, models.SSOLoginRequest{})
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, response)
|
||||
}
|
||||
|
||||
// GetOnlineUsers 获取在线用户列表
|
||||
func (h *SSOHandler) GetOnlineUsers(c *gin.Context) {
|
||||
users, err := h.ssoService.GetOnlineUsers(c.Request.Context())
|
||||
if err != nil {
|
||||
h.logger.Error("failed to get online users", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, users)
|
||||
}
|
||||
|
||||
// GetOnlineUserCount 获取在线用户数量
|
||||
func (h *SSOHandler) GetOnlineUserCount(c *gin.Context) {
|
||||
count, err := h.ssoService.GetOnlineUserCount(c.Request.Context())
|
||||
if err != nil {
|
||||
h.logger.Error("failed to get online user count", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"count": count})
|
||||
}
|
||||
|
||||
// BatchLogout 批量登出用户
|
||||
func (h *SSOHandler) BatchLogout(c *gin.Context) {
|
||||
var req struct {
|
||||
UserIDs []int `json:"user_ids" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.ssoService.BatchUserLogout(c.Request.Context(), req.UserIDs)
|
||||
if err != nil {
|
||||
h.logger.Error("failed to batch logout users", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "批量登出成功")
|
||||
}
|
||||
189
internal/api/handlers/system_config_handler.go
Normal file
189
internal/api/handlers/system_config_handler.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// SystemConfigHandler 系统配置处理器
|
||||
type SystemConfigHandler struct {
|
||||
service services.SystemConfigService
|
||||
response *utils.Response
|
||||
logger *utils.Logger
|
||||
}
|
||||
|
||||
// NewSystemConfigHandler 创建系统配置处理器
|
||||
func NewSystemConfigHandler(service services.SystemConfigService, logger *utils.Logger) *SystemConfigHandler {
|
||||
return &SystemConfigHandler{
|
||||
service: service,
|
||||
response: utils.NewResponse(),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Create 创建系统配置
|
||||
func (h *SystemConfigHandler) Create(c *gin.Context) {
|
||||
var req models.SystemConfigRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.service.Create(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("创建系统配置失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, config)
|
||||
}
|
||||
|
||||
// GetByID 获取系统配置详情
|
||||
func (h *SystemConfigHandler) GetByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.service.GetByID(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("获取系统配置失败", zap.Error(err))
|
||||
h.response.NotFound(c, "配置不存在")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, config)
|
||||
}
|
||||
|
||||
// GetByKey 根据配置标识获取系统配置
|
||||
func (h *SystemConfigHandler) GetByKey(c *gin.Context) {
|
||||
key := c.Param("key")
|
||||
if key == "" {
|
||||
h.response.BadRequest(c, "配置标识不能为空")
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.service.GetByKey(key)
|
||||
if err != nil {
|
||||
h.logger.Error("获取系统配置失败", zap.Error(err))
|
||||
h.response.NotFound(c, "配置不存在")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, config)
|
||||
}
|
||||
|
||||
// List 获取系统配置列表
|
||||
func (h *SystemConfigHandler) List(c *gin.Context) {
|
||||
var req models.SystemConfigListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.Size <= 0 {
|
||||
req.Size = 20
|
||||
}
|
||||
|
||||
configs, total, err := h.service.List(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("获取系统配置列表失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, "获取列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Page(c, configs, total, req.Page, req.Size)
|
||||
}
|
||||
|
||||
// Update 更新系统配置
|
||||
func (h *SystemConfigHandler) Update(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.SystemConfigUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.service.Update(uint(id), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("更新系统配置失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, config)
|
||||
}
|
||||
|
||||
// Delete 删除系统配置
|
||||
func (h *SystemConfigHandler) Delete(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.service.Delete(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("删除系统配置失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, nil)
|
||||
}
|
||||
|
||||
// UpdateStatus 更新状态
|
||||
func (h *SystemConfigHandler) UpdateStatus(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.SystemConfigUpdateStatusRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.service.UpdateStatus(uint(id), req.Status)
|
||||
if err != nil {
|
||||
h.logger.Error("更新系统配置状态失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, nil)
|
||||
}
|
||||
|
||||
// GetAll 获取所有系统配置
|
||||
func (h *SystemConfigHandler) GetAll(c *gin.Context) {
|
||||
configs, err := h.service.GetAll()
|
||||
if err != nil {
|
||||
h.logger.Error("获取所有系统配置失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, "获取配置失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, configs)
|
||||
}
|
||||
195
internal/api/handlers/user_feedback_handler.go
Normal file
195
internal/api/handlers/user_feedback_handler.go
Normal file
@@ -0,0 +1,195 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// UserFeedbackHandler 用户反馈处理器
|
||||
type UserFeedbackHandler struct {
|
||||
service *services.UserFeedbackService
|
||||
response *utils.Response
|
||||
}
|
||||
|
||||
// NewUserFeedbackHandler 创建用户反馈处理器
|
||||
func NewUserFeedbackHandler(service *services.UserFeedbackService) *UserFeedbackHandler {
|
||||
return &UserFeedbackHandler{
|
||||
service: service,
|
||||
response: utils.NewResponse(),
|
||||
}
|
||||
}
|
||||
|
||||
// List 获取用户反馈列表
|
||||
func (h *UserFeedbackHandler) List(c *gin.Context) {
|
||||
var req models.UserFeedbackListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.service.List(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, result)
|
||||
}
|
||||
|
||||
// GetByID 根据ID获取用户反馈
|
||||
func (h *UserFeedbackHandler) GetByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的反馈ID")
|
||||
return
|
||||
}
|
||||
|
||||
feedbackItem, err := h.service.GetByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err.Error() == "反馈不存在" {
|
||||
h.response.NotFound(c, "反馈不存在")
|
||||
return
|
||||
}
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, feedbackItem)
|
||||
}
|
||||
|
||||
// MarkHandled 标记为已处理
|
||||
func (h *UserFeedbackHandler) MarkHandled(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的反馈ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UserFeedbackMarkRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前用户ID(从JWT token中解析)
|
||||
userID, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
h.response.Unauthorized(c, "无法获取用户信息")
|
||||
return
|
||||
}
|
||||
|
||||
// 处理用户ID,支持多种类型
|
||||
var handledBy int
|
||||
switch v := userID.(type) {
|
||||
case int:
|
||||
handledBy = v
|
||||
case uint:
|
||||
handledBy = int(v)
|
||||
case string:
|
||||
var err error
|
||||
handledBy, err = strconv.Atoi(v)
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, "用户ID格式错误")
|
||||
return
|
||||
}
|
||||
default:
|
||||
h.response.InternalServerError(c, "用户ID格式错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前状态以确定切换后的状态
|
||||
feedback, err := h.service.GetByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err.Error() == "反馈不存在" {
|
||||
h.response.NotFound(c, "反馈不存在")
|
||||
return
|
||||
}
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
newStatus := 0
|
||||
message := "标记为未处理成功"
|
||||
if feedback.Status == 0 {
|
||||
// 当前是未处理,切换为已处理
|
||||
newStatus = 1
|
||||
message = "标记为已处理成功"
|
||||
}
|
||||
|
||||
err = h.service.MarkHandled(c.Request.Context(), id, handledBy, req.Note)
|
||||
if err != nil {
|
||||
if err.Error() == "反馈不存在" {
|
||||
h.response.NotFound(c, "反馈不存在")
|
||||
return
|
||||
}
|
||||
if err.Error() == "处理人ID无效" {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 返回处理结果
|
||||
result := map[string]interface{}{
|
||||
"id": id,
|
||||
"status": newStatus,
|
||||
"message": message,
|
||||
}
|
||||
if newStatus == 1 {
|
||||
result["handled_by"] = handledBy
|
||||
}
|
||||
|
||||
h.response.Success(c, result)
|
||||
}
|
||||
|
||||
// Delete 删除用户反馈
|
||||
func (h *UserFeedbackHandler) Delete(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseInt(idStr, 10, 64)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的反馈ID")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.service.Delete(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err.Error() == "反馈不存在" {
|
||||
h.response.NotFound(c, "反馈不存在")
|
||||
return
|
||||
}
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "删除成功"})
|
||||
}
|
||||
|
||||
// GetStatistics 获取反馈统计信息
|
||||
func (h *UserFeedbackHandler) GetStatistics(c *gin.Context) {
|
||||
stats, err := h.service.GetStatistics(c.Request.Context())
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, stats)
|
||||
}
|
||||
|
||||
// hasHTTPPrefix 判断字符串是否为 http/https URL
|
||||
func hasHTTPPrefix(s string) bool {
|
||||
if len(s) < 7 {
|
||||
return false
|
||||
}
|
||||
if len(s) >= 8 && (s[:8] == "https://") {
|
||||
return true
|
||||
}
|
||||
return len(s) >= 7 && (s[:7] == "http://")
|
||||
}
|
||||
285
internal/api/handlers/user_handler.go
Normal file
285
internal/api/handlers/user_handler.go
Normal file
@@ -0,0 +1,285 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// UserHandler 用户处理器
|
||||
type UserHandler struct {
|
||||
userService services.UserService
|
||||
rbacService services.RBACService
|
||||
response *utils.Response
|
||||
logger *utils.Logger
|
||||
}
|
||||
|
||||
// NewUserHandler 创建用户处理器
|
||||
func NewUserHandler(userService services.UserService, rbacService services.RBACService, logger *utils.Logger) *UserHandler {
|
||||
return &UserHandler{
|
||||
userService: userService,
|
||||
rbacService: rbacService,
|
||||
response: utils.NewResponse(),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Create 创建用户
|
||||
func (h *UserHandler) Create(c *gin.Context) {
|
||||
var req models.UserCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userService.Create(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("创建用户失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, user)
|
||||
}
|
||||
|
||||
// GetByID 获取用户详情
|
||||
func (h *UserHandler) GetByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的用户ID")
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userService.GetByID(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("获取用户失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, user)
|
||||
}
|
||||
|
||||
// Update 更新用户
|
||||
func (h *UserHandler) Update(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的用户ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UserUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.userService.Update(uint(id), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("更新用户失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, user)
|
||||
}
|
||||
|
||||
// Delete 删除用户
|
||||
func (h *UserHandler) Delete(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的用户ID")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.userService.Delete(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("删除用户失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "用户删除成功"})
|
||||
}
|
||||
|
||||
// List 获取用户列表
|
||||
func (h *UserHandler) List(c *gin.Context) {
|
||||
var req models.UserListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
users, total, err := h.userService.ListWithRoles(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("获取用户列表失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{
|
||||
"users": users,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"size": req.Size,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateStatus 更新用户状态
|
||||
func (h *UserHandler) UpdateStatus(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的用户ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Status int `json:"status" binding:"required"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.userService.UpdateStatus(uint(id), req.Status)
|
||||
if err != nil {
|
||||
h.logger.Error("更新用户状态失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "用户状态更新成功"})
|
||||
}
|
||||
|
||||
// UpdateRoles 更新用户角色
|
||||
func (h *UserHandler) UpdateRoles(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的用户ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UserRoleAssignRequest
|
||||
req.UserID = uint(id)
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 现在用户只有一个角色,直接更新用户的role_id
|
||||
err = h.userService.UpdateRole(uint(id), req.RoleIDs[0])
|
||||
if err != nil {
|
||||
h.logger.Error("更新用户角色失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "用户角色更新成功"})
|
||||
}
|
||||
|
||||
// GetUserRoles 获取用户角色
|
||||
func (h *UserHandler) GetUserRoles(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的用户ID")
|
||||
return
|
||||
}
|
||||
|
||||
// 现在用户只有一个角色,直接获取用户信息
|
||||
user, err := h.userService.GetByID(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("获取用户信息失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 获取角色信息
|
||||
role, err := h.rbacService.GetRoleByID(user.RoleID)
|
||||
if err != nil {
|
||||
h.logger.Error("获取角色信息失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"roles": []models.Role{*role}})
|
||||
}
|
||||
|
||||
// GetUserPermissions 获取用户权限
|
||||
func (h *UserHandler) GetUserPermissions(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的用户ID")
|
||||
return
|
||||
}
|
||||
|
||||
permissions, err := h.rbacService.GetUserPermissionsResponse(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("获取用户权限失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"permissions": permissions})
|
||||
}
|
||||
|
||||
// CheckUserRole 检查用户当前系统角色
|
||||
// GET /admin/users/check-role/:user_id
|
||||
func (h *UserHandler) CheckUserRole(c *gin.Context) {
|
||||
userIDStr := c.Param("user_id")
|
||||
userID, err := strconv.Atoi(userIDStr)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的用户ID")
|
||||
return
|
||||
}
|
||||
|
||||
isSystemAdmin, err := h.userService.CheckUserSystemRole(userID)
|
||||
if err != nil {
|
||||
h.logger.Error("检查用户系统角色失败", zap.Int("user_id", userID), zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
currentRole := "custom"
|
||||
if isSystemAdmin {
|
||||
currentRole = "sys_admin"
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{
|
||||
"is_system_admin": isSystemAdmin,
|
||||
"current_role": currentRole,
|
||||
})
|
||||
}
|
||||
|
||||
// ChangeUserSystemRole 变更用户系统角色
|
||||
// POST /admin/users/change-system-role
|
||||
func (h *UserHandler) ChangeUserSystemRole(c *gin.Context) {
|
||||
var req struct {
|
||||
UserID int `json:"user_id" binding:"required"`
|
||||
SystemRole string `json:"system_role" binding:"required,oneof=sys_admin custom"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.userService.ChangeUserSystemRole(req.UserID, req.SystemRole)
|
||||
if err != nil {
|
||||
h.logger.Error("变更用户系统角色失败", zap.Int("user_id", req.UserID), zap.String("system_role", req.SystemRole), zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, gin.H{"message": "角色变更成功"})
|
||||
}
|
||||
171
internal/api/handlers/user_level_config_handler.go
Normal file
171
internal/api/handlers/user_level_config_handler.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// UserLevelConfigHandler 用户等级配置处理器
|
||||
type UserLevelConfigHandler struct {
|
||||
service services.UserLevelConfigService
|
||||
response *utils.Response
|
||||
logger *utils.Logger
|
||||
}
|
||||
|
||||
// NewUserLevelConfigHandler 创建用户等级配置处理器
|
||||
func NewUserLevelConfigHandler(service services.UserLevelConfigService, logger *utils.Logger) *UserLevelConfigHandler {
|
||||
return &UserLevelConfigHandler{
|
||||
service: service,
|
||||
response: utils.NewResponse(),
|
||||
logger: logger,
|
||||
}
|
||||
}
|
||||
|
||||
// Create 创建用户等级配置
|
||||
func (h *UserLevelConfigHandler) Create(c *gin.Context) {
|
||||
var req models.UserLevelConfigCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.service.Create(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("创建用户等级配置失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, config)
|
||||
}
|
||||
|
||||
// GetByID 获取用户等级配置详情
|
||||
func (h *UserLevelConfigHandler) GetByID(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.service.GetByID(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("获取用户等级配置失败", zap.Error(err))
|
||||
h.response.NotFound(c, "配置不存在")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, config)
|
||||
}
|
||||
|
||||
// List 获取用户等级配置列表
|
||||
func (h *UserLevelConfigHandler) List(c *gin.Context) {
|
||||
var req models.UserLevelConfigListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.Size <= 0 {
|
||||
req.Size = 20
|
||||
}
|
||||
|
||||
configs, total, err := h.service.List(&req)
|
||||
if err != nil {
|
||||
h.logger.Error("获取用户等级配置列表失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, "获取列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Page(c, configs, total, req.Page, req.Size)
|
||||
}
|
||||
|
||||
// Update 更新用户等级配置
|
||||
func (h *UserLevelConfigHandler) Update(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UserLevelConfigUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
config, err := h.service.Update(uint(id), &req)
|
||||
if err != nil {
|
||||
h.logger.Error("更新用户等级配置失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, config)
|
||||
}
|
||||
|
||||
// Delete 删除用户等级配置
|
||||
func (h *UserLevelConfigHandler) Delete(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.service.Delete(uint(id))
|
||||
if err != nil {
|
||||
h.logger.Error("删除用户等级配置失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, nil)
|
||||
}
|
||||
|
||||
// UpdateStatus 更新状态
|
||||
func (h *UserLevelConfigHandler) UpdateStatus(c *gin.Context) {
|
||||
idStr := c.Param("id")
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
h.response.BadRequest(c, "无效的ID")
|
||||
return
|
||||
}
|
||||
|
||||
var req models.UserLevelConfigUpdateStatusRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.service.UpdateStatus(uint(id), req.Status)
|
||||
if err != nil {
|
||||
h.logger.Error("更新用户等级配置状态失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, nil)
|
||||
}
|
||||
|
||||
// GetAll 获取所有用户等级配置
|
||||
func (h *UserLevelConfigHandler) GetAll(c *gin.Context) {
|
||||
configs, err := h.service.GetAll()
|
||||
if err != nil {
|
||||
h.logger.Error("获取所有用户等级配置失败", zap.Error(err))
|
||||
h.response.InternalServerError(c, "获取配置失败")
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, configs)
|
||||
}
|
||||
97
internal/api/handlers/user_project_quota_handler.go
Normal file
97
internal/api/handlers/user_project_quota_handler.go
Normal file
@@ -0,0 +1,97 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/internal/storage"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type UserProjectQuotaHandler struct {
|
||||
svc services.UserProjectQuotaService
|
||||
resp *utils.Response
|
||||
}
|
||||
|
||||
func NewUserProjectQuotaHandler(s services.UserProjectQuotaService) *UserProjectQuotaHandler {
|
||||
return &UserProjectQuotaHandler{svc: s, resp: utils.NewResponse()}
|
||||
}
|
||||
|
||||
func (h *UserProjectQuotaHandler) Create(c *gin.Context) {
|
||||
var req models.UserProjectQuota
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.resp.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
out, err := h.svc.Create(&req)
|
||||
if err != nil {
|
||||
h.resp.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.resp.Success(c, out)
|
||||
}
|
||||
|
||||
func (h *UserProjectQuotaHandler) Update(c *gin.Context) {
|
||||
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
var req models.UserProjectQuota
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.resp.ValidateError(c, err)
|
||||
return
|
||||
}
|
||||
out, err := h.svc.Update(uint(id64), &req)
|
||||
if err != nil {
|
||||
h.resp.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.resp.Success(c, out)
|
||||
}
|
||||
|
||||
func (h *UserProjectQuotaHandler) Delete(c *gin.Context) {
|
||||
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
if err := h.svc.Delete(uint(id64)); err != nil {
|
||||
h.resp.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.resp.Success(c, gin.H{"message": "deleted"})
|
||||
}
|
||||
|
||||
func (h *UserProjectQuotaHandler) GetByID(c *gin.Context) {
|
||||
id64, _ := strconv.ParseUint(c.Param("id"), 10, 64)
|
||||
out, err := h.svc.GetByID(uint(id64))
|
||||
if err != nil {
|
||||
h.resp.NotFound(c, "not found")
|
||||
return
|
||||
}
|
||||
h.resp.Success(c, out)
|
||||
}
|
||||
|
||||
func (h *UserProjectQuotaHandler) List(c *gin.Context) {
|
||||
var filter storage.UserProjectQuotaFilter
|
||||
filter.UserID = c.Query("user_id")
|
||||
if v := c.Query("enabled"); v != "" {
|
||||
if v == "true" {
|
||||
b := true
|
||||
filter.Enabled = &b
|
||||
} else if v == "false" {
|
||||
b := false
|
||||
filter.Enabled = &b
|
||||
}
|
||||
}
|
||||
if p := c.Query("page"); p != "" {
|
||||
if v, err := strconv.Atoi(p); err == nil {
|
||||
filter.Page = v
|
||||
}
|
||||
}
|
||||
if s := c.Query("size"); s != "" {
|
||||
if v, err := strconv.Atoi(s); err == nil {
|
||||
filter.Size = v
|
||||
}
|
||||
}
|
||||
items, total, err := h.svc.List(filter)
|
||||
if err != nil {
|
||||
h.resp.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
h.resp.Page(c, items, total, filter.Page, filter.Size)
|
||||
}
|
||||
170
internal/api/handlers/vendor_model_pricing_handler.go
Normal file
170
internal/api/handlers/vendor_model_pricing_handler.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// VendorModelPricingHandler 供应商模型价格配置处理器
|
||||
type VendorModelPricingHandler struct {
|
||||
db *gorm.DB
|
||||
response *utils.Response
|
||||
}
|
||||
|
||||
// NewVendorModelPricingHandler 创建处理器
|
||||
func NewVendorModelPricingHandler(db *gorm.DB) *VendorModelPricingHandler {
|
||||
return &VendorModelPricingHandler{
|
||||
db: db,
|
||||
response: utils.NewResponse(),
|
||||
}
|
||||
}
|
||||
|
||||
// VendorModelPricingResponse 供应商模型价格配置响应
|
||||
type VendorModelPricingResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Provider string `json:"provider"`
|
||||
Account string `json:"account"`
|
||||
ModelName string `json:"model_name"`
|
||||
InputPrice float64 `json:"input_price"`
|
||||
OutputPrice float64 `json:"output_price"`
|
||||
CacheReadPrice float64 `json:"cache_read_price"`
|
||||
CacheCreatePrice float64 `json:"cache_create_price"`
|
||||
PriceRatio float64 `json:"price_ratio"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// PriceUpdateRequest 价格更新请求
|
||||
type PriceUpdateRequest struct {
|
||||
InputPrice *float64 `json:"input_price"`
|
||||
OutputPrice *float64 `json:"output_price"`
|
||||
CacheReadPrice *float64 `json:"cache_read_price"`
|
||||
CacheCreatePrice *float64 `json:"cache_create_price"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
PriceRatio *float64 `json:"price_ratio"`
|
||||
}
|
||||
|
||||
// GetVendorModelPricing 获取供应商模型价格配置列表
|
||||
func (h *VendorModelPricingHandler) GetVendorModelPricing(c *gin.Context) {
|
||||
var models []VendorModelPricingResponse
|
||||
|
||||
// 构建查询条件
|
||||
// 使用 v2 表:gw_model_config_v2,直接返回数据,无需联查
|
||||
// 为兼容前端字段类型,account 字段返回空串
|
||||
query := h.db.Table("gw_model_config_v2 mc").
|
||||
Select("mc.id, mc.provider, '' as account, mc.model_name, " +
|
||||
"mc.prompt_price as input_price, " +
|
||||
"mc.output_price as output_price, " +
|
||||
"mc.cache_read_price, mc.cache_create_price, " +
|
||||
"mc.price_ratio, " +
|
||||
"mc.enabled, mc.created_at, mc.updated_at")
|
||||
|
||||
// 添加筛选条件
|
||||
if provider := c.Query("provider"); provider != "" {
|
||||
query = query.Where("mc.provider = ?", provider)
|
||||
}
|
||||
if model := c.Query("model"); model != "" {
|
||||
query = query.Where("mc.model_name LIKE ?", "%"+model+"%")
|
||||
}
|
||||
if status := c.Query("status"); status != "" {
|
||||
if status == "enabled" {
|
||||
query = query.Where("mc.enabled = ?", true)
|
||||
} else if status == "disabled" {
|
||||
query = query.Where("mc.enabled = ?", false)
|
||||
}
|
||||
}
|
||||
|
||||
// 分页
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
size, _ := strconv.Atoi(c.DefaultQuery("size", "20"))
|
||||
offset := (page - 1) * size
|
||||
|
||||
// 先获取总数(在应用分页之前)
|
||||
var total int64
|
||||
err := query.Count(&total).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 然后获取分页数据
|
||||
err = query.Offset(offset).Limit(size).Find(&models).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Page(c, models, total, page, size)
|
||||
}
|
||||
|
||||
// UpdateModelPricing 更新单个模型价格
|
||||
func (h *VendorModelPricingHandler) UpdateModelPricing(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
var req PriceUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 构建更新字段(v2 列)
|
||||
updates := make(map[string]interface{})
|
||||
if req.InputPrice != nil {
|
||||
updates["prompt_price"] = *req.InputPrice
|
||||
}
|
||||
if req.OutputPrice != nil {
|
||||
updates["output_price"] = *req.OutputPrice
|
||||
}
|
||||
if req.CacheReadPrice != nil {
|
||||
updates["cache_read_price"] = *req.CacheReadPrice
|
||||
}
|
||||
if req.CacheCreatePrice != nil {
|
||||
updates["cache_create_price"] = *req.CacheCreatePrice
|
||||
}
|
||||
if req.Enabled != nil {
|
||||
updates["enabled"] = *req.Enabled
|
||||
}
|
||||
if req.PriceRatio != nil {
|
||||
updates["price_ratio"] = *req.PriceRatio
|
||||
}
|
||||
|
||||
// 检查模型是否存在(v2 表)
|
||||
var count int64
|
||||
err := h.db.Table("gw_model_config_v2").Where("id = ?", id).Count(&count).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
if count == 0 {
|
||||
h.response.NotFound(c, "模型不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 更新模型价格(v2 表)
|
||||
err = h.db.Table("gw_model_config_v2").Where("id = ?", id).Updates(updates).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "价格更新成功")
|
||||
}
|
||||
|
||||
// GetProviders 获取供应商列表(用于筛选)
|
||||
func (h *VendorModelPricingHandler) GetProviders(c *gin.Context) {
|
||||
var providers []string
|
||||
err := h.db.Table("gw_providers").
|
||||
Select("DISTINCT name").
|
||||
Where("status = ?", "active").
|
||||
Find(&providers).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, providers)
|
||||
}
|
||||
403
internal/api/handlers/vm_pricing_handler.go
Normal file
403
internal/api/handlers/vm_pricing_handler.go
Normal file
@@ -0,0 +1,403 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// VmPricingHandler 虚拟机价格配置处理器
|
||||
type VmPricingHandler struct {
|
||||
db *gorm.DB
|
||||
response *utils.Response
|
||||
}
|
||||
|
||||
// NewVmPricingHandler 创建处理器
|
||||
func NewVmPricingHandler(db *gorm.DB) *VmPricingHandler {
|
||||
return &VmPricingHandler{
|
||||
db: db,
|
||||
response: utils.NewResponse(),
|
||||
}
|
||||
}
|
||||
|
||||
// VmSpecResponse 虚拟机规格响应
|
||||
type VmSpecResponse struct {
|
||||
ID uint `json:"id"`
|
||||
SpecType string `json:"spec_type"`
|
||||
CPUCores int `json:"cpu_cores"`
|
||||
MemoryGB int `json:"memory_gb"`
|
||||
Description *string `json:"description"`
|
||||
CostPricePerMinute float64 `json:"cost_price_per_minute"`
|
||||
MarkupRate float64 `json:"markup_rate"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// VmTemplateResponse 虚拟机模板响应
|
||||
type VmTemplateResponse struct {
|
||||
ID uint `json:"id"`
|
||||
SpecType string `json:"spec_type"`
|
||||
TemplateID string `json:"template_id"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
}
|
||||
|
||||
// VmSpecCreateRequest 规格创建请求
|
||||
type VmSpecCreateRequest struct {
|
||||
SpecType string `json:"spec_type" binding:"required"`
|
||||
CPUCores int `json:"cpu_cores" binding:"required"`
|
||||
MemoryGB int `json:"memory_gb" binding:"required"`
|
||||
Description *string `json:"description"`
|
||||
CostPricePerMinute float64 `json:"cost_price_per_minute" binding:"required"`
|
||||
MarkupRate *float64 `json:"markup_rate"`
|
||||
IsActive *bool `json:"is_active"`
|
||||
}
|
||||
|
||||
// VmSpecUpdateRequest 规格更新请求
|
||||
type VmSpecUpdateRequest struct {
|
||||
CostPricePerMinute *float64 `json:"cost_price_per_minute"`
|
||||
MarkupRate *float64 `json:"markup_rate"`
|
||||
IsActive *bool `json:"is_active"`
|
||||
}
|
||||
|
||||
// VmTemplateCreateRequest 模板创建请求
|
||||
type VmTemplateCreateRequest struct {
|
||||
SpecType string `json:"spec_type" binding:"required"`
|
||||
TemplateID string `json:"template_id" binding:"required"`
|
||||
IsDefault bool `json:"is_default"`
|
||||
}
|
||||
|
||||
// VmTemplateUpdateRequest 模板更新请求
|
||||
type VmTemplateUpdateRequest struct {
|
||||
IsDefault *bool `json:"is_default"`
|
||||
}
|
||||
|
||||
// GetVmSpecs 获取虚拟机规格列表
|
||||
func (h *VmPricingHandler) GetVmSpecs(c *gin.Context) {
|
||||
var specs []VmSpecResponse
|
||||
|
||||
// 构建查询条件
|
||||
query := h.db.Table("sb_sandbox_specs")
|
||||
|
||||
// 添加筛选条件
|
||||
if specType := c.Query("spec_type"); specType != "" {
|
||||
query = query.Where("spec_type LIKE ?", "%"+specType+"%")
|
||||
}
|
||||
if status := c.Query("status"); status != "" {
|
||||
if status == "active" {
|
||||
query = query.Where("is_active = ?", true)
|
||||
} else if status == "inactive" {
|
||||
query = query.Where("is_active = ?", false)
|
||||
}
|
||||
}
|
||||
|
||||
// 分页
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
|
||||
offset := (page - 1) * size
|
||||
|
||||
// 先获取总数
|
||||
var total int64
|
||||
err := query.Count(&total).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 然后获取分页数据
|
||||
err = query.Order("created_at DESC").Offset(offset).Limit(size).Find(&specs).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Page(c, specs, total, page, size)
|
||||
}
|
||||
|
||||
// UpdateVmSpec 更新虚拟机规格
|
||||
func (h *VmPricingHandler) UpdateVmSpec(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
var req VmSpecUpdateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 构建更新字段
|
||||
updates := make(map[string]interface{})
|
||||
if req.CostPricePerMinute != nil {
|
||||
updates["cost_price_per_minute"] = *req.CostPricePerMinute
|
||||
}
|
||||
if req.MarkupRate != nil {
|
||||
updates["markup_rate"] = *req.MarkupRate
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
updates["is_active"] = *req.IsActive
|
||||
}
|
||||
|
||||
if len(updates) == 0 {
|
||||
h.response.BadRequest(c, "没有需要更新的字段")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查规格是否存在
|
||||
var count int64
|
||||
err := h.db.Table("sb_sandbox_specs").Where("id = ?", id).Count(&count).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
if count == 0 {
|
||||
h.response.NotFound(c, "规格不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 更新规格
|
||||
err = h.db.Table("sb_sandbox_specs").Where("id = ?", id).Updates(updates).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "规格更新成功")
|
||||
}
|
||||
|
||||
// DeleteVmSpec 删除虚拟机规格
|
||||
func (h *VmPricingHandler) DeleteVmSpec(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
// 检查规格是否存在
|
||||
var count int64
|
||||
err := h.db.Table("sb_sandbox_specs").Where("id = ?", id).Count(&count).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
if count == 0 {
|
||||
h.response.NotFound(c, "规格不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 删除规格
|
||||
err = h.db.Table("sb_sandbox_specs").Where("id = ?", id).Delete(nil).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "规格删除成功")
|
||||
}
|
||||
|
||||
// CreateVmSpec 创建虚拟机规格
|
||||
func (h *VmPricingHandler) CreateVmSpec(c *gin.Context) {
|
||||
var req VmSpecCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 检查规格类型是否已存在
|
||||
var count int64
|
||||
err := h.db.Table("sb_sandbox_specs").Where("spec_type = ?", req.SpecType).Count(&count).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
if count > 0 {
|
||||
h.response.BadRequest(c, "该配置类型已存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 设置默认值
|
||||
markupRate := 0.3000 // 默认30%
|
||||
if req.MarkupRate != nil {
|
||||
markupRate = *req.MarkupRate
|
||||
}
|
||||
isActive := true
|
||||
if req.IsActive != nil {
|
||||
isActive = *req.IsActive
|
||||
}
|
||||
|
||||
// 创建规格
|
||||
insertData := map[string]interface{}{
|
||||
"spec_type": req.SpecType,
|
||||
"cpu_cores": req.CPUCores,
|
||||
"memory_gb": req.MemoryGB,
|
||||
"cost_price_per_minute": req.CostPricePerMinute,
|
||||
"markup_rate": markupRate,
|
||||
"is_active": isActive,
|
||||
}
|
||||
if req.Description != nil {
|
||||
insertData["description"] = *req.Description
|
||||
}
|
||||
|
||||
err = h.db.Table("sb_sandbox_specs").Create(insertData).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "规格创建成功")
|
||||
}
|
||||
|
||||
// GetVmTemplates 获取虚拟机模板列表
|
||||
func (h *VmPricingHandler) GetVmTemplates(c *gin.Context) {
|
||||
var templates []VmTemplateResponse
|
||||
|
||||
// 构建查询条件
|
||||
query := h.db.Table("sb_sandbox_templates")
|
||||
|
||||
// 添加筛选条件
|
||||
if specType := c.Query("spec_type"); specType != "" {
|
||||
query = query.Where("spec_type = ?", specType)
|
||||
}
|
||||
|
||||
// 分页
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
size, _ := strconv.Atoi(c.DefaultQuery("size", "10"))
|
||||
offset := (page - 1) * size
|
||||
|
||||
// 先获取总数
|
||||
var total int64
|
||||
err := query.Count(&total).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 然后获取分页数据
|
||||
err = query.Order("spec_type ASC, created_at DESC").Offset(offset).Limit(size).Find(&templates).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Page(c, templates, total, page, size)
|
||||
}
|
||||
|
||||
// CreateVmTemplate 创建虚拟机模板
|
||||
func (h *VmPricingHandler) CreateVmTemplate(c *gin.Context) {
|
||||
var req VmTemplateCreateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.response.BadRequest(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 检查规格是否存在
|
||||
var specCount int64
|
||||
err := h.db.Table("sb_sandbox_specs").Where("spec_type = ?", req.SpecType).Count(&specCount).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
if specCount == 0 {
|
||||
h.response.BadRequest(c, "规格类型不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 如果设置为默认模板,需要先取消同规格的其他默认模板
|
||||
if req.IsDefault {
|
||||
err = h.db.Table("sb_sandbox_templates").
|
||||
Where("spec_type = ? AND is_default = ?", req.SpecType, true).
|
||||
Update("is_default", false).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否已存在相同的规格-模板组合
|
||||
var existingCount int64
|
||||
err = h.db.Table("sb_sandbox_templates").
|
||||
Where("spec_type = ? AND template_id = ?", req.SpecType, req.TemplateID).
|
||||
Count(&existingCount).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
if existingCount > 0 {
|
||||
h.response.BadRequest(c, "该规格和模板的组合已存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 创建模板
|
||||
insertData := map[string]interface{}{
|
||||
"spec_type": req.SpecType,
|
||||
"template_id": req.TemplateID,
|
||||
"is_default": req.IsDefault,
|
||||
}
|
||||
err = h.db.Table("sb_sandbox_templates").Create(insertData).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "模板创建成功")
|
||||
}
|
||||
|
||||
// DeleteVmTemplate 删除虚拟机模板
|
||||
func (h *VmPricingHandler) DeleteVmTemplate(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
// 检查模板是否存在
|
||||
var count int64
|
||||
err := h.db.Table("sb_sandbox_templates").Where("id = ?", id).Count(&count).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
if count == 0 {
|
||||
h.response.NotFound(c, "模板不存在")
|
||||
return
|
||||
}
|
||||
|
||||
// 删除模板
|
||||
err = h.db.Table("sb_sandbox_templates").Where("id = ?", id).Delete(nil).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "模板删除成功")
|
||||
}
|
||||
|
||||
// SetDefaultVmTemplate 设置默认模板
|
||||
func (h *VmPricingHandler) SetDefaultVmTemplate(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
|
||||
// 获取模板信息
|
||||
var template VmTemplateResponse
|
||||
err := h.db.Table("sb_sandbox_templates").Where("id = ?", id).First(&template).Error
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
h.response.NotFound(c, "模板不存在")
|
||||
return
|
||||
}
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 取消同规格的其他默认模板
|
||||
err = h.db.Table("sb_sandbox_templates").
|
||||
Where("spec_type = ? AND is_default = ? AND id != ?", template.SpecType, true, id).
|
||||
Update("is_default", false).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 设置当前模板为默认
|
||||
err = h.db.Table("sb_sandbox_templates").Where("id = ?", id).Update("is_default", true).Error
|
||||
if err != nil {
|
||||
h.response.InternalServerError(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.response.Success(c, "默认模板设置成功")
|
||||
}
|
||||
141
internal/api/middlewares/api_log_middleware.go
Normal file
141
internal/api/middlewares/api_log_middleware.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
)
|
||||
|
||||
// API日志中间件 - 记录所有接口的调用信息
|
||||
func APILogMiddleware(logger *utils.Logger) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 排除健康检查接口
|
||||
if c.Request.URL.Path == "/health" {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 记录开始时间
|
||||
startTime := time.Now()
|
||||
|
||||
// 读取请求体(需要保存以便后续使用)
|
||||
var requestBody []byte
|
||||
if c.Request.Body != nil {
|
||||
requestBody, _ = io.ReadAll(c.Request.Body)
|
||||
c.Request.Body = io.NopCloser(bytes.NewBuffer(requestBody))
|
||||
}
|
||||
|
||||
// 获取用户信息
|
||||
userID := 0
|
||||
userEmail := "unknown"
|
||||
if userInfo, exists := c.Get("user"); exists {
|
||||
if user, ok := userInfo.(*models.UserInfo); ok && user != nil {
|
||||
if user.Email != "" {
|
||||
userEmail = user.Email
|
||||
} else if user.PreferredUsername != "" {
|
||||
userEmail = user.PreferredUsername + "@goalfy.com"
|
||||
}
|
||||
// 尝试获取用户ID
|
||||
if id, exists := c.Get("user_id"); exists {
|
||||
switch v := id.(type) {
|
||||
case int:
|
||||
userID = v
|
||||
case uint:
|
||||
userID = int(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建响应写入器包装器以捕获响应
|
||||
responseWriter := &responseBodyWriter{
|
||||
ResponseWriter: c.Writer,
|
||||
body: &bytes.Buffer{},
|
||||
}
|
||||
c.Writer = responseWriter
|
||||
|
||||
// 处理请求
|
||||
c.Next()
|
||||
|
||||
// 计算耗时
|
||||
duration := time.Since(startTime)
|
||||
|
||||
// 获取响应信息
|
||||
responseCode := c.Writer.Status()
|
||||
responseBody := responseWriter.body.String()
|
||||
|
||||
// 限制响应体大小(避免打印过大的响应)
|
||||
if len(responseBody) > 1000 {
|
||||
responseBody = responseBody[:1000] + "...(truncated)"
|
||||
}
|
||||
|
||||
// 限制请求体大小
|
||||
requestBodyStr := string(requestBody)
|
||||
if len(requestBodyStr) > 1000 {
|
||||
requestBodyStr = requestBodyStr[:1000] + "...(truncated)"
|
||||
}
|
||||
|
||||
// 获取请求信息
|
||||
method := c.Request.Method
|
||||
path := c.Request.URL.Path
|
||||
|
||||
// 提取模块名称
|
||||
module := extractModule(path)
|
||||
|
||||
// 打印日志
|
||||
logger.Info("📝 [API日志] 接口调用记录",
|
||||
zap.String("method", method),
|
||||
zap.String("path", path),
|
||||
zap.String("full_path", c.Request.URL.String()),
|
||||
zap.String("module", module),
|
||||
zap.Int("user_id", userID),
|
||||
zap.String("user_email", userEmail),
|
||||
zap.String("ip_address", c.ClientIP()),
|
||||
zap.String("user_agent", c.Request.UserAgent()),
|
||||
zap.String("request_body", requestBodyStr),
|
||||
zap.Int("response_code", responseCode),
|
||||
zap.String("response_body", responseBody),
|
||||
zap.Duration("duration", duration),
|
||||
zap.Int64("duration_ms", duration.Milliseconds()),
|
||||
zap.String("status", getStatus(responseCode)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// responseBodyWriter 用于捕获响应体
|
||||
type responseBodyWriter struct {
|
||||
gin.ResponseWriter
|
||||
body *bytes.Buffer
|
||||
}
|
||||
|
||||
func (w *responseBodyWriter) Write(b []byte) (int, error) {
|
||||
w.body.Write(b)
|
||||
return w.ResponseWriter.Write(b)
|
||||
}
|
||||
|
||||
// extractModule 从路径提取模块名称
|
||||
func extractModule(path string) string {
|
||||
parts := strings.Split(strings.Trim(path, "/"), "/")
|
||||
if len(parts) >= 3 {
|
||||
return parts[2] // /api/admin/{module}
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
// getStatus 根据响应码获取状态
|
||||
func getStatus(code int) string {
|
||||
if code >= 200 && code < 300 {
|
||||
return "success"
|
||||
} else if code >= 400 && code < 500 {
|
||||
return "client_error"
|
||||
} else if code >= 500 {
|
||||
return "server_error"
|
||||
}
|
||||
return "unknown"
|
||||
}
|
||||
47
internal/api/middlewares/logging.go
Normal file
47
internal/api/middlewares/logging.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package middlewares
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
)
|
||||
|
||||
// LoggingMiddleware 日志中间件
|
||||
func LoggingMiddleware(logger *utils.Logger) gin.HandlerFunc {
|
||||
return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
|
||||
// 记录请求日志
|
||||
logger.Info("HTTP请求",
|
||||
zap.String("method", param.Method),
|
||||
zap.String("path", param.Path),
|
||||
zap.Int("status", param.StatusCode),
|
||||
zap.Duration("latency", param.Latency),
|
||||
zap.String("client_ip", param.ClientIP),
|
||||
zap.String("user_agent", param.Request.UserAgent()),
|
||||
)
|
||||
return ""
|
||||
})
|
||||
}
|
||||
|
||||
// RequestLogMiddleware 请求日志中间件
|
||||
func RequestLogMiddleware(logger *utils.Logger) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
start := time.Now()
|
||||
|
||||
// 处理请求
|
||||
c.Next()
|
||||
|
||||
// 记录请求信息
|
||||
latency := time.Since(start)
|
||||
|
||||
logger.Info("请求处理完成",
|
||||
zap.String("method", c.Request.Method),
|
||||
zap.String("path", c.Request.URL.Path),
|
||||
zap.Int("status", c.Writer.Status()),
|
||||
zap.Duration("latency", latency),
|
||||
zap.String("client_ip", c.ClientIP()),
|
||||
zap.String("user_agent", c.Request.UserAgent()),
|
||||
)
|
||||
}
|
||||
}
|
||||
370
internal/api/routes/routes.go
Normal file
370
internal/api/routes/routes.go
Normal file
@@ -0,0 +1,370 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"goalfymax-admin/internal/api/handlers"
|
||||
"goalfymax-admin/internal/api/middlewares"
|
||||
"goalfymax-admin/internal/config"
|
||||
"goalfymax-admin/internal/models"
|
||||
"goalfymax-admin/internal/services"
|
||||
"goalfymax-admin/internal/storage"
|
||||
"goalfymax-admin/pkg/middleware"
|
||||
"goalfymax-admin/pkg/redis"
|
||||
"goalfymax-admin/pkg/utils"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SetupRoutes 设置路由
|
||||
func SetupRoutes(
|
||||
userService services.UserService,
|
||||
roleService services.RoleService,
|
||||
pageService services.PageService,
|
||||
quotaService services.QuotaService,
|
||||
ssoService services.SSOService,
|
||||
rbacService services.RBACService,
|
||||
userLevelConfigService services.UserLevelConfigService,
|
||||
systemConfigService services.SystemConfigService,
|
||||
redisClient *redis.Client,
|
||||
logger *utils.Logger,
|
||||
appConfig *config.Config,
|
||||
) *gin.Engine {
|
||||
// 创建Gin引擎
|
||||
r := gin.New()
|
||||
|
||||
// 添加CORS中间件
|
||||
r.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"http://localhost:5173", "http://localhost:5174", "http://localhost:3000", "http://localhost:3003", "http://localhost:3004"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "PATCH"},
|
||||
AllowHeaders: []string{"Origin", "Content-Type", "Accept", "Authorization", "X-Requested-With", "Cookie"},
|
||||
ExposeHeaders: []string{"Content-Length", "Content-Type"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
}))
|
||||
|
||||
// 添加中间件
|
||||
r.Use(middlewares.RequestLogMiddleware(logger))
|
||||
r.Use(middlewares.APILogMiddleware(logger)) // API日志中间件(记录数据修改接口)
|
||||
r.Use(gin.Recovery())
|
||||
|
||||
// 创建SSO客户端和认证中间件
|
||||
ssoConfig := &models.SSOConfig{
|
||||
SSOServerURL: appConfig.SSO.SSOServerURL,
|
||||
ClientID: appConfig.SSO.ClientID,
|
||||
ClientSecret: appConfig.SSO.ClientSecret,
|
||||
RedirectURI: appConfig.SSO.RedirectURI,
|
||||
Scope: appConfig.SSO.Scope,
|
||||
ResourceAud: appConfig.SSO.ResourceAud,
|
||||
Timeout: appConfig.SSO.Timeout,
|
||||
}
|
||||
ssoClient := middleware.NewSSOClient(ssoConfig, logger)
|
||||
sessionManager := middleware.NewMemorySessionManager()
|
||||
authMiddleware := middleware.NewAuthMiddleware(ssoClient, sessionManager, "/login")
|
||||
|
||||
// RBAC中间件已简化,不再需要全局实例
|
||||
|
||||
// 创建处理器
|
||||
quotaHandler := handlers.NewQuotaHandler(quotaService)
|
||||
userProjectQuotaHandler := handlers.NewUserProjectQuotaHandler(
|
||||
services.NewUserProjectQuotaService(
|
||||
storage.NewUserProjectQuotaStorage(),
|
||||
),
|
||||
)
|
||||
ssoHandler := handlers.NewSSOHandler(ssoService, logger)
|
||||
messagePushService := services.NewMessagePushService()
|
||||
ssoAdminService := services.NewSSOAdminService()
|
||||
userHandler := handlers.NewUserHandler(userService, rbacService, logger)
|
||||
auditLogService := services.NewAuditLogService(storage.NewAuditLogStorage())
|
||||
goalfyUserHandler := handlers.NewGoalfyMaxUserHandler(
|
||||
services.NewGoalfyMaxUserService(storage.NewGoalfyMaxUserStorage(), messagePushService, ssoAdminService, redisClient, storage.NewBalanceOperationLogStorage(), auditLogService, logger),
|
||||
)
|
||||
auditLogHandler := handlers.NewAuditLogHandler(auditLogService)
|
||||
userFeedbackHandler := handlers.NewUserFeedbackHandler(
|
||||
services.NewUserFeedbackService(storage.NewUserFeedbackStorage()),
|
||||
)
|
||||
messagePushHandler := handlers.NewMessagePushHandler(
|
||||
messagePushService,
|
||||
)
|
||||
roleHandler := handlers.NewRoleHandler(roleService, rbacService, logger)
|
||||
pageHandler := handlers.NewPageHandler(pageService, logger)
|
||||
rbacHandler := handlers.NewRBACHandler(rbacService, logger)
|
||||
vendorPricingHandler := handlers.NewVendorModelPricingHandler(storage.GetDB())
|
||||
vmPricingHandler := handlers.NewVmPricingHandler(storage.GetDB())
|
||||
mcpProviderHandler := handlers.NewMCPProviderHandler()
|
||||
financeHandler := handlers.NewFinanceHandler()
|
||||
userLevelConfigHandler := handlers.NewUserLevelConfigHandler(userLevelConfigService, logger)
|
||||
systemConfigHandler := handlers.NewSystemConfigHandler(systemConfigService, logger)
|
||||
inviteCodeHandler := handlers.NewInviteCodeHandler(
|
||||
services.NewInviteCodeService(storage.NewInviteCodeStorage()),
|
||||
userLevelConfigService,
|
||||
)
|
||||
inviteCodeApplicationHandler := handlers.NewInviteCodeApplicationHandler(storage.GetDB())
|
||||
|
||||
// 健康检查
|
||||
r.GET("/health", func(c *gin.Context) {
|
||||
c.JSON(200, gin.H{"status": "ok"})
|
||||
})
|
||||
|
||||
// API路由组
|
||||
api := r.Group("/api")
|
||||
{
|
||||
// 公开接口(不需要认证)
|
||||
public := api.Group("/public")
|
||||
{
|
||||
// 官网提交邀请码申请
|
||||
public.POST("/invite-code/apply", inviteCodeApplicationHandler.SubmitApplication)
|
||||
}
|
||||
|
||||
// SSO相关路由
|
||||
sso := api.Group("/sso")
|
||||
{
|
||||
sso.POST("/login", ssoHandler.HandleSSOLogin) // SSO登录
|
||||
sso.POST("/callback", ssoHandler.HandleSSOCallback) // SSO回调
|
||||
sso.POST("/refresh", ssoHandler.HandleRefreshToken) // 刷新令牌
|
||||
sso.POST("/logout", ssoHandler.HandleLogout) // 登出
|
||||
sso.GET("/userinfo", ssoHandler.HandleUserInfo) // 获取用户信息
|
||||
sso.GET("/online-users", ssoHandler.GetOnlineUsers) // 获取在线用户列表
|
||||
sso.GET("/online-count", ssoHandler.GetOnlineUserCount) // 获取在线用户数量
|
||||
sso.POST("/batch-logout", ssoHandler.BatchLogout) // 批量登出
|
||||
}
|
||||
|
||||
// 管理员路由组(需要认证和动态权限检查)
|
||||
admin := api.Group("/admin")
|
||||
admin.Use(authMiddleware.RequireAuth())
|
||||
// 使用简化的页面权限检查
|
||||
{
|
||||
// 用户管理 - 所有路由通过动态权限检查
|
||||
users := admin.Group("/users")
|
||||
{
|
||||
users.GET("", userHandler.List) // 获取用户列表
|
||||
users.POST("", userHandler.Create) // 创建用户
|
||||
users.GET("/:id", userHandler.GetByID) // 获取用户详情
|
||||
users.PUT("/:id", userHandler.Update) // 更新用户
|
||||
users.DELETE("/:id", userHandler.Delete) // 删除用户
|
||||
users.PUT("/:id/status", userHandler.UpdateStatus) // 更新用户状态
|
||||
users.PUT("/:id/roles", userHandler.UpdateRoles) // 更新用户角色
|
||||
users.GET("/:id/roles", userHandler.GetUserRoles) // 获取用户角色
|
||||
users.GET("/:id/permissions", userHandler.GetUserPermissions) // 获取用户权限
|
||||
users.GET("/check-role/:user_id", userHandler.CheckUserRole) // 检查用户系统角色
|
||||
users.POST("/change-system-role", userHandler.ChangeUserSystemRole) // 变更用户系统角色
|
||||
}
|
||||
|
||||
// GoalfyMax 用户管理
|
||||
goalfyUsers := admin.Group("/goalfymax-users")
|
||||
{
|
||||
goalfyUsers.GET("", goalfyUserHandler.List) // 列表
|
||||
goalfyUsers.POST("", goalfyUserHandler.Create) // 新增
|
||||
goalfyUsers.GET(":id", goalfyUserHandler.GetByID) // 详情
|
||||
goalfyUsers.PUT(":id", goalfyUserHandler.Update) // 编辑
|
||||
goalfyUsers.DELETE(":id", goalfyUserHandler.Delete) // 删除
|
||||
goalfyUsers.POST(":id/ban", goalfyUserHandler.Ban) // 封禁
|
||||
goalfyUsers.POST(":id/unban", goalfyUserHandler.Unban) // 解封
|
||||
goalfyUsers.POST(":id/add-balance", goalfyUserHandler.AddBalance) // 增加余额
|
||||
goalfyUsers.POST(":id/deduct-balance", goalfyUserHandler.DeductBalance) // 减少余额
|
||||
}
|
||||
|
||||
// 用户反馈管理
|
||||
userFeedbacks := admin.Group("/user-feedback")
|
||||
{
|
||||
userFeedbacks.GET("", userFeedbackHandler.List) // 获取反馈列表
|
||||
userFeedbacks.GET("/:id", userFeedbackHandler.GetByID) // 获取反馈详情
|
||||
userFeedbacks.POST("/:id/mark-handled", userFeedbackHandler.MarkHandled) // 标记为已处理
|
||||
userFeedbacks.DELETE("/:id", userFeedbackHandler.Delete) // 删除反馈
|
||||
userFeedbacks.GET("/statistics", userFeedbackHandler.GetStatistics) // 获取统计信息
|
||||
}
|
||||
|
||||
// 消息推送管理
|
||||
messagePush := admin.Group("/message-push")
|
||||
{
|
||||
messagePush.POST("/send", messagePushHandler.SendMessage) // 发送消息
|
||||
messagePush.GET("/logs", messagePushHandler.GetPushLogs) // 获取推送记录
|
||||
messagePush.GET("/logs/:id", messagePushHandler.GetPushLogByID) // 获取推送记录详情
|
||||
messagePush.GET("/users/search", messagePushHandler.SearchUsers) // 搜索用户
|
||||
}
|
||||
|
||||
// 角色管理 - 所有路由通过动态权限检查
|
||||
roles := admin.Group("/roles")
|
||||
{
|
||||
roles.GET("", roleHandler.List) // 获取角色列表
|
||||
roles.POST("", roleHandler.Create) // 创建角色
|
||||
roles.GET("/:id", roleHandler.GetByID) // 获取角色详情
|
||||
roles.PUT("/:id", roleHandler.Update) // 更新角色
|
||||
roles.DELETE("/:id", roleHandler.Delete) // 删除角色
|
||||
roles.PUT("/:id/status", roleHandler.UpdateStatus) // 更新角色状态
|
||||
roles.PUT("/:id/permissions", roleHandler.UpdatePermissions) // 更新角色权限
|
||||
roles.GET("/:id/permissions", roleHandler.GetRolePermissions) // 获取角色权限
|
||||
}
|
||||
|
||||
// 页面管理 - 所有路由通过页面权限检查
|
||||
pages := admin.Group("/pages")
|
||||
{
|
||||
pages.GET("", pageHandler.List) // 获取页面列表
|
||||
pages.POST("", pageHandler.Create) // 创建页面
|
||||
pages.GET("/:id", pageHandler.GetByID) // 获取页面详情
|
||||
pages.PUT("/:id", pageHandler.Update) // 更新页面
|
||||
pages.DELETE("/:id", pageHandler.Delete) // 删除页面
|
||||
}
|
||||
|
||||
// RBAC管理 - 所有路由通过动态权限检查
|
||||
rbac := admin.Group("/rbac")
|
||||
{
|
||||
rbac.POST("/role-page-permissions", rbacHandler.AssignRolePagePermissions) // 分配角色页面权限
|
||||
rbac.DELETE("/roles/:id/page-permissions", rbacHandler.RemoveRolePagePermissions) // 移除角色页面权限
|
||||
rbac.GET("/roles/:id/page-permissions", rbacHandler.GetRolePagePermissions) // 获取角色页面权限
|
||||
rbac.GET("/users/:id/permissions", rbacHandler.GetUserPermissions) // 获取用户权限
|
||||
rbac.GET("/roles/:id/permissions", rbacHandler.GetRolePermissions) // 获取角色权限
|
||||
rbac.GET("/check-page-permission", rbacHandler.CheckPagePermission) // 检查页面权限
|
||||
rbac.GET("/users/:id/accessible-pages", rbacHandler.GetUserAccessiblePages) // 获取用户可访问页面
|
||||
}
|
||||
|
||||
// 供应商模型价格配置
|
||||
vendorPricing := admin.Group("/vendor-model-pricing")
|
||||
{
|
||||
vendorPricing.GET("", vendorPricingHandler.GetVendorModelPricing) // 获取价格配置列表
|
||||
vendorPricing.PUT("/:id", vendorPricingHandler.UpdateModelPricing) // 更新模型价格
|
||||
vendorPricing.GET("/providers", vendorPricingHandler.GetProviders) // 获取供应商列表
|
||||
}
|
||||
|
||||
// 虚拟机价格配置
|
||||
vmPricing := admin.Group("/vm-pricing")
|
||||
{
|
||||
vmPricing.GET("/specs", vmPricingHandler.GetVmSpecs) // 获取规格列表
|
||||
vmPricing.POST("/specs", vmPricingHandler.CreateVmSpec) // 创建规格
|
||||
vmPricing.PUT("/specs/:id", vmPricingHandler.UpdateVmSpec) // 更新规格价格
|
||||
vmPricing.DELETE("/specs/:id", vmPricingHandler.DeleteVmSpec) // 删除规格
|
||||
vmPricing.GET("/templates", vmPricingHandler.GetVmTemplates) // 获取模板列表
|
||||
vmPricing.POST("/templates", vmPricingHandler.CreateVmTemplate) // 创建模板
|
||||
vmPricing.DELETE("/templates/:id", vmPricingHandler.DeleteVmTemplate) // 删除模板
|
||||
vmPricing.PUT("/templates/:id/default", vmPricingHandler.SetDefaultVmTemplate) // 设置默认模板
|
||||
}
|
||||
|
||||
// MCP 价格配置(PostgreSQL)
|
||||
mcpProviders := admin.Group("/mcp-providers")
|
||||
{
|
||||
mcpProviders.GET("", mcpProviderHandler.List)
|
||||
mcpProviders.POST("", mcpProviderHandler.Create)
|
||||
mcpProviders.GET(":id", mcpProviderHandler.GetByID)
|
||||
mcpProviders.PUT(":id", mcpProviderHandler.Update)
|
||||
mcpProviders.DELETE(":id", mcpProviderHandler.Delete)
|
||||
mcpProviders.PATCH(":id/status", mcpProviderHandler.UpdateStatus)
|
||||
mcpProviders.PATCH(":id/is-used", mcpProviderHandler.UpdateIsUsed)
|
||||
}
|
||||
|
||||
// 用户等级配置管理
|
||||
userLevelConfigs := admin.Group("/user-level-configs")
|
||||
{
|
||||
userLevelConfigs.GET("", userLevelConfigHandler.List) // 获取列表
|
||||
userLevelConfigs.GET("/all", userLevelConfigHandler.GetAll) // 获取所有(不分页)
|
||||
userLevelConfigs.POST("", userLevelConfigHandler.Create) // 创建
|
||||
userLevelConfigs.GET("/:id", userLevelConfigHandler.GetByID) // 获取详情
|
||||
userLevelConfigs.PUT("/:id", userLevelConfigHandler.Update) // 更新
|
||||
userLevelConfigs.DELETE("/:id", userLevelConfigHandler.Delete) // 删除
|
||||
userLevelConfigs.PUT("/:id/status", userLevelConfigHandler.UpdateStatus) // 更新状态
|
||||
}
|
||||
|
||||
// 系统通用配置管理
|
||||
systemConfigs := admin.Group("/system-configs")
|
||||
{
|
||||
systemConfigs.GET("", systemConfigHandler.List) // 获取列表
|
||||
systemConfigs.GET("/all", systemConfigHandler.GetAll) // 获取所有(不分页)
|
||||
systemConfigs.POST("", systemConfigHandler.Create) // 创建
|
||||
systemConfigs.GET("/key/:key", systemConfigHandler.GetByKey) // 根据Key获取
|
||||
systemConfigs.GET("/:id", systemConfigHandler.GetByID) // 获取详情
|
||||
systemConfigs.PUT("/:id", systemConfigHandler.Update) // 更新
|
||||
systemConfigs.DELETE("/:id", systemConfigHandler.Delete) // 删除
|
||||
systemConfigs.PUT("/:id/status", systemConfigHandler.UpdateStatus) // 更新状态
|
||||
}
|
||||
|
||||
// 邀请码管理(简化版)
|
||||
inviteCodes := admin.Group("/invite-codes")
|
||||
{
|
||||
inviteCodes.GET("", inviteCodeHandler.GetInviteCodeList) // 获取邀请码列表
|
||||
inviteCodes.POST("", inviteCodeHandler.CreateInviteCode) // 创建邀请码(支持设置过期时间)
|
||||
inviteCodes.GET("/client-options", inviteCodeHandler.GetClientOptions) // 获取客户端选项
|
||||
inviteCodes.GET("/statistics", inviteCodeHandler.GetInviteCodeStatistics) // 获取统计信息
|
||||
inviteCodes.GET("/:id", inviteCodeHandler.GetInviteCodeDetail) // 获取邀请码详情
|
||||
inviteCodes.PUT("/:id", inviteCodeHandler.UpdateInviteCode) // 更新邀请码(支持更新过期时间)
|
||||
inviteCodes.DELETE("/:id", inviteCodeHandler.DeleteInviteCode) // 删除邀请码
|
||||
inviteCodes.POST("/mark-used", inviteCodeHandler.MarkInviteCodeAsUsed) // 标记邀请码为已使用
|
||||
inviteCodes.POST("/validate", inviteCodeHandler.ValidateInviteCode) // 验证邀请码是否有效
|
||||
}
|
||||
|
||||
// 邀请码申请管理
|
||||
inviteApplications := admin.Group("/invite-applications")
|
||||
{
|
||||
inviteApplications.GET("", inviteCodeApplicationHandler.GetApplicationList) // 获取申请列表
|
||||
inviteApplications.GET("/statistics", inviteCodeApplicationHandler.GetStatistics) // 获取统计信息
|
||||
inviteApplications.GET("/pending-count", inviteCodeApplicationHandler.GetPendingCount) // 获取待处理数量
|
||||
inviteApplications.POST("/approve", inviteCodeApplicationHandler.ApproveApplication) // 审批通过申请
|
||||
inviteApplications.POST("/reject", inviteCodeApplicationHandler.RejectApplication) // 审批拒绝申请
|
||||
inviteApplications.POST("/batch-approve", inviteCodeApplicationHandler.BatchApproveApplications) // 批量审批通过
|
||||
inviteApplications.POST("/batch-reject", inviteCodeApplicationHandler.BatchRejectApplications) // 批量审批拒绝
|
||||
}
|
||||
|
||||
// 审计日志管理
|
||||
auditLogs := admin.Group("/audit-logs")
|
||||
{
|
||||
auditLogs.GET("", auditLogHandler.List) // 获取审计日志列表
|
||||
auditLogs.GET("/:id", auditLogHandler.GetByID) // 获取审计日志详情
|
||||
}
|
||||
}
|
||||
|
||||
// 财务数据(需要认证)
|
||||
finance := api.Group("/finance")
|
||||
finance.Use(authMiddleware.RequireAuth())
|
||||
{
|
||||
finance.GET("/sandbox-records", financeHandler.ListSandboxRecords)
|
||||
finance.GET("/token-usages", financeHandler.ListTokenUsages)
|
||||
finance.GET("/mcp-usages", financeHandler.ListMCPUsages)
|
||||
finance.GET("/transaction-logs", financeHandler.ListTransactionLogs)
|
||||
finance.GET("/payment-records", financeHandler.ListPaymentRecords)
|
||||
finance.POST("/payment-records/refund", financeHandler.RefundPaymentRecord)
|
||||
finance.GET("/mcp-account-recharge-records", financeHandler.ListMcpAccountRechargeRecords)
|
||||
finance.POST("/mcp-account-recharge-records", financeHandler.CreateMcpAccountRechargeRecord)
|
||||
finance.PUT("/mcp-account-recharge-records/:id", financeHandler.UpdateMcpAccountRechargeRecord)
|
||||
finance.DELETE("/mcp-account-recharge-records/:id", financeHandler.DeleteMcpAccountRechargeRecord)
|
||||
finance.GET("/mcp-provider-accounts", financeHandler.GetMcpProviderAccounts)
|
||||
finance.GET("/mcp-account-balances", financeHandler.GetMcpAccountBalances)
|
||||
finance.POST("/mcp-account-balances", financeHandler.CreateMcpAccountBalance)
|
||||
finance.PUT("/mcp-account-balances/:provider_id", financeHandler.AdjustMcpAccountBalance)
|
||||
finance.GET("/mcp-account-balances/:provider_id/history", financeHandler.GetMcpAccountBalanceHistory)
|
||||
|
||||
// 模型账号管理
|
||||
finance.GET("/model-account-recharge-records", financeHandler.ListModelAccountRechargeRecords)
|
||||
finance.POST("/model-account-recharge-records", financeHandler.CreateModelAccountRechargeRecord)
|
||||
finance.PUT("/model-account-recharge-records/:id", financeHandler.UpdateModelAccountRechargeRecord)
|
||||
finance.DELETE("/model-account-recharge-records/:id", financeHandler.DeleteModelAccountRechargeRecord)
|
||||
finance.GET("/model-config-accounts", financeHandler.GetModelConfigAccounts)
|
||||
finance.GET("/model-account-balances", financeHandler.GetModelAccountBalances)
|
||||
finance.POST("/model-account-balances", financeHandler.CreateModelAccountBalance)
|
||||
finance.PUT("/model-account-balances/:account", financeHandler.AdjustModelAccountBalance)
|
||||
finance.GET("/model-account-balances/:account/history", financeHandler.GetModelAccountBalanceHistory)
|
||||
}
|
||||
|
||||
// 配额相关路由(需要认证和动态权限检查)
|
||||
quotas := api.Group("/quotas")
|
||||
quotas.Use(authMiddleware.RequireAuth())
|
||||
// 使用简化的页面权限检查
|
||||
{
|
||||
quotas.POST("/history", quotaHandler.GetQuotaHistory) // 获取配额历史
|
||||
quotas.GET("/health", quotaHandler.HealthCheck) // 配额服务健康检查
|
||||
quotas.GET("/rules", quotaHandler.GetQuotaRules) // 获取配额规则列表(转发网关)
|
||||
quotas.POST("/rules", quotaHandler.CreateQuotaRule) // 创建规则(转发网关)
|
||||
quotas.PUT("/rules/:id", quotaHandler.UpdateQuotaRule) // 更新规则(转发网关)
|
||||
quotas.DELETE("/rules/:id", quotaHandler.DeleteQuotaRule) // 删除规则(转发网关)
|
||||
|
||||
// 用户项目配额 CRUD
|
||||
userProject := quotas.Group("/user-project")
|
||||
{
|
||||
userProject.GET("", userProjectQuotaHandler.List)
|
||||
userProject.POST("", userProjectQuotaHandler.Create)
|
||||
userProject.GET(":id", userProjectQuotaHandler.GetByID)
|
||||
userProject.PUT(":id", userProjectQuotaHandler.Update)
|
||||
userProject.DELETE(":id", userProjectQuotaHandler.Delete)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
Reference in New Issue
Block a user