fix: 统一API响应格式并修复前端测试
- 所有Handler方法使用标准{code:0,message:"success",data:...}响应格式
- 修复Cursor分页响应包装(GetAllDevices,GetLoginLogs,ListUsers等)
- 修复AuthHandler和SMSHandler认证方法响应格式
- 修复operation_log.go admin用户operation_type前缀问题
- 修复DashboardPage嵌套stats结构
- 修复LoginLogsPage reset功能stale closure问题
- 修复UsersPage批量操作API调用
- 修复多个前端测试(mock格式、按钮选择、断言逻辑)
- 添加OAuth测试域名白名单
- 新增代码审查流程文档
This commit is contained in:
@@ -58,7 +58,11 @@ func (h *AuthHandler) Register(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, userInfo)
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": userInfo,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Login(c *gin.Context) {
|
||||
@@ -98,7 +102,11 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": resp,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Logout(c *gin.Context) {
|
||||
@@ -144,7 +152,11 @@ func (h *AuthHandler) RefreshToken(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": resp,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) GetUserInfo(c *gin.Context) {
|
||||
@@ -160,7 +172,11 @@ func (h *AuthHandler) GetUserInfo(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, userInfo)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": userInfo,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) GetCSRFToken(c *gin.Context) {
|
||||
@@ -283,7 +299,11 @@ func (h *AuthHandler) LoginByEmailCode(c *gin.Context) {
|
||||
}()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": resp,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) BootstrapAdmin(c *gin.Context) {
|
||||
@@ -330,7 +350,11 @@ func (h *AuthHandler) BootstrapAdmin(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, resp)
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": resp,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) SendEmailBindCode(c *gin.Context) {
|
||||
|
||||
@@ -33,7 +33,11 @@ func (h *CustomFieldHandler) CreateField(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, field)
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": field,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateField 更新自定义字段
|
||||
@@ -56,7 +60,11 @@ func (h *CustomFieldHandler) UpdateField(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, field)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": field,
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteField 删除自定义字段
|
||||
@@ -72,7 +80,10 @@ func (h *CustomFieldHandler) DeleteField(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "field deleted"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "field deleted",
|
||||
})
|
||||
}
|
||||
|
||||
// GetField 获取自定义字段
|
||||
@@ -89,7 +100,11 @@ func (h *CustomFieldHandler) GetField(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, field)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": field,
|
||||
})
|
||||
}
|
||||
|
||||
// ListFields 获取所有自定义字段
|
||||
@@ -100,7 +115,11 @@ func (h *CustomFieldHandler) ListFields(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"fields": fields})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": fields,
|
||||
})
|
||||
}
|
||||
|
||||
// SetUserFieldValues 设置用户自定义字段值
|
||||
@@ -125,7 +144,10 @@ func (h *CustomFieldHandler) SetUserFieldValues(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "field values set"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "field values set",
|
||||
})
|
||||
}
|
||||
|
||||
// GetUserFieldValues 获取用户自定义字段值
|
||||
@@ -142,5 +164,9 @@ func (h *CustomFieldHandler) GetUserFieldValues(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"fields": values})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": values,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -41,7 +41,11 @@ func (h *DeviceHandler) CreateDevice(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, device)
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": device,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *DeviceHandler) GetMyDevices(c *gin.Context) {
|
||||
@@ -61,10 +65,14 @@ func (h *DeviceHandler) GetMyDevices(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"devices": devices,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": gin.H{
|
||||
"items": devices,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -81,7 +89,11 @@ func (h *DeviceHandler) GetDevice(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, device)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": device,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *DeviceHandler) UpdateDevice(c *gin.Context) {
|
||||
@@ -103,7 +115,11 @@ func (h *DeviceHandler) UpdateDevice(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, device)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": device,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *DeviceHandler) DeleteDevice(c *gin.Context) {
|
||||
@@ -118,7 +134,10 @@ func (h *DeviceHandler) DeleteDevice(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "device deleted"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "device deleted",
|
||||
})
|
||||
}
|
||||
|
||||
func (h *DeviceHandler) UpdateDeviceStatus(c *gin.Context) {
|
||||
@@ -153,7 +172,10 @@ func (h *DeviceHandler) UpdateDeviceStatus(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "status updated"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "status updated",
|
||||
})
|
||||
}
|
||||
|
||||
func (h *DeviceHandler) GetUserDevices(c *gin.Context) {
|
||||
@@ -199,10 +221,14 @@ func (h *DeviceHandler) GetUserDevices(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"devices": devices,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": gin.H{
|
||||
"items": devices,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -221,7 +247,11 @@ func (h *DeviceHandler) GetAllDevices(c *gin.Context) {
|
||||
handleError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, result)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": result,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -233,10 +263,14 @@ func (h *DeviceHandler) GetAllDevices(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"devices": devices,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": gin.H{
|
||||
"items": devices,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -267,7 +301,10 @@ func (h *DeviceHandler) TrustDevice(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "device trusted"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "device trusted",
|
||||
})
|
||||
}
|
||||
|
||||
// TrustDeviceByDeviceID 根据设备标识字符串设置设备为信任状态
|
||||
@@ -298,7 +335,10 @@ func (h *DeviceHandler) TrustDeviceByDeviceID(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "device trusted"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "device trusted",
|
||||
})
|
||||
}
|
||||
|
||||
// UntrustDevice 取消设备信任状态
|
||||
@@ -314,7 +354,10 @@ func (h *DeviceHandler) UntrustDevice(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "device untrusted"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "device untrusted",
|
||||
})
|
||||
}
|
||||
|
||||
// GetMyTrustedDevices 获取我的信任设备列表
|
||||
@@ -331,7 +374,11 @@ func (h *DeviceHandler) GetMyTrustedDevices(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"devices": devices})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": devices,
|
||||
})
|
||||
}
|
||||
|
||||
// LogoutAllOtherDevices 登出所有其他设备
|
||||
@@ -355,7 +402,10 @@ func (h *DeviceHandler) LogoutAllOtherDevices(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "all other devices logged out"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "all other devices logged out",
|
||||
})
|
||||
}
|
||||
|
||||
// parseDuration 解析duration字符串,如 "30d" -> 30天的time.Duration
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -19,13 +21,89 @@ func NewExportHandler(exportService *service.ExportService) *ExportHandler {
|
||||
}
|
||||
|
||||
func (h *ExportHandler) ExportUsers(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "user export not implemented"})
|
||||
format := c.DefaultQuery("format", "csv")
|
||||
fieldsStr := c.Query("fields")
|
||||
keyword := c.Query("keyword")
|
||||
statusStr := c.Query("status")
|
||||
|
||||
var fields []string
|
||||
if fieldsStr != "" {
|
||||
fields = strings.Split(fieldsStr, ",")
|
||||
}
|
||||
|
||||
var status *int
|
||||
if statusStr != "" {
|
||||
s, err := strconvAtoi(statusStr)
|
||||
if err == nil {
|
||||
status = &s
|
||||
}
|
||||
}
|
||||
|
||||
req := &service.ExportUsersRequest{
|
||||
Format: format,
|
||||
Fields: fields,
|
||||
Keyword: keyword,
|
||||
Status: status,
|
||||
}
|
||||
|
||||
data, filename, contentType, err := h.exportService.ExportUsers(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "导出失败: " + err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", contentType)
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Data(http.StatusOK, contentType, data)
|
||||
}
|
||||
|
||||
func (h *ExportHandler) ImportUsers(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "user import not implemented"})
|
||||
file, _, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "请上传文件"})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
data, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "读取文件失败"})
|
||||
return
|
||||
}
|
||||
|
||||
format := c.DefaultQuery("format", "csv")
|
||||
successCount, failCount, errs := h.exportService.ImportUsers(c.Request.Context(), data, format)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"data": gin.H{
|
||||
"success_count": successCount,
|
||||
"fail_count": failCount,
|
||||
"errors": errs,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *ExportHandler) GetImportTemplate(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"template": "id,username,email,nickname"})
|
||||
format := c.DefaultQuery("format", "csv")
|
||||
data, filename, contentType, err := h.exportService.GetImportTemplateByFormat(format)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "获取模板失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", contentType)
|
||||
c.Header("Content-Disposition", "attachment; filename="+filename)
|
||||
c.Data(http.StatusOK, contentType, data)
|
||||
}
|
||||
|
||||
func strconvAtoi(s string) (int, error) {
|
||||
var n int
|
||||
for _, c := range s {
|
||||
if c < '0' || c > '9' {
|
||||
return 0, nil
|
||||
}
|
||||
n = n*10 + int(c-'0')
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
@@ -41,15 +41,35 @@ func (h *LogHandler) GetMyLoginLogs(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"logs": logs,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"list": logs,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *LogHandler) GetMyOperationLogs(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"logs": []interface{}{}})
|
||||
userID, ok := getUserIDFromContext(c)
|
||||
if !ok {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
|
||||
logs, total, err := h.operationLogService.GetMyOperationLogs(c.Request.Context(), userID, page, pageSize)
|
||||
if err != nil {
|
||||
handleError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"list": logs,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *LogHandler) GetLoginLogs(c *gin.Context) {
|
||||
@@ -66,7 +86,11 @@ func (h *LogHandler) GetLoginLogs(c *gin.Context) {
|
||||
handleError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, result)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": result,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -78,8 +102,10 @@ func (h *LogHandler) GetLoginLogs(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"logs": logs,
|
||||
"total": total,
|
||||
"list": logs,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -97,7 +123,11 @@ func (h *LogHandler) GetOperationLogs(c *gin.Context) {
|
||||
handleError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, result)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": result,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -109,8 +139,10 @@ func (h *LogHandler) GetOperationLogs(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"logs": logs,
|
||||
"total": total,
|
||||
"list": logs,
|
||||
"total": total,
|
||||
"page": req.Page,
|
||||
"page_size": req.PageSize,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,11 @@ func (h *PermissionHandler) CreatePermission(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, perm)
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": perm,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *PermissionHandler) ListPermissions(c *gin.Context) {
|
||||
@@ -43,15 +47,16 @@ func (h *PermissionHandler) ListPermissions(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
perms, total, err := h.permissionService.ListPermissions(c.Request.Context(), &req)
|
||||
perms, _, err := h.permissionService.ListPermissions(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
handleError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"permissions": perms,
|
||||
"total": total,
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": perms,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,7 +73,11 @@ func (h *PermissionHandler) GetPermission(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, perm)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": perm,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *PermissionHandler) UpdatePermission(c *gin.Context) {
|
||||
@@ -90,7 +99,11 @@ func (h *PermissionHandler) UpdatePermission(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, perm)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": perm,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *PermissionHandler) DeletePermission(c *gin.Context) {
|
||||
@@ -105,7 +118,10 @@ func (h *PermissionHandler) DeletePermission(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "permission deleted"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "permission deleted",
|
||||
})
|
||||
}
|
||||
|
||||
func (h *PermissionHandler) UpdatePermissionStatus(c *gin.Context) {
|
||||
@@ -140,7 +156,10 @@ func (h *PermissionHandler) UpdatePermissionStatus(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "status updated"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "status updated",
|
||||
})
|
||||
}
|
||||
|
||||
func (h *PermissionHandler) GetPermissionTree(c *gin.Context) {
|
||||
@@ -150,5 +169,9 @@ func (h *PermissionHandler) GetPermissionTree(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"permissions": tree})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": tree,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -33,7 +33,11 @@ func (h *RoleHandler) CreateRole(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, role)
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": role,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RoleHandler) ListRoles(c *gin.Context) {
|
||||
@@ -50,8 +54,12 @@ func (h *RoleHandler) ListRoles(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"roles": roles,
|
||||
"total": total,
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": gin.H{
|
||||
"items": roles,
|
||||
"total": total,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -68,7 +76,11 @@ func (h *RoleHandler) GetRole(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, role)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": role,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RoleHandler) UpdateRole(c *gin.Context) {
|
||||
@@ -90,7 +102,11 @@ func (h *RoleHandler) UpdateRole(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, role)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": role,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RoleHandler) DeleteRole(c *gin.Context) {
|
||||
@@ -105,7 +121,10 @@ func (h *RoleHandler) DeleteRole(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "role deleted"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "role deleted",
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RoleHandler) UpdateRoleStatus(c *gin.Context) {
|
||||
@@ -141,7 +160,10 @@ func (h *RoleHandler) UpdateRoleStatus(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "status updated"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "status updated",
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RoleHandler) GetRolePermissions(c *gin.Context) {
|
||||
@@ -157,7 +179,11 @@ func (h *RoleHandler) GetRolePermissions(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"permissions": perms})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": perms,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RoleHandler) AssignPermissions(c *gin.Context) {
|
||||
@@ -182,5 +208,8 @@ func (h *RoleHandler) AssignPermissions(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "permissions assigned"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "permissions assigned",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -46,7 +46,11 @@ func (h *SMSHandler) SendCode(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": resp,
|
||||
})
|
||||
}
|
||||
|
||||
// LoginByCode 短信验证码登录(带设备信息以支持设备信任链路)
|
||||
@@ -94,5 +98,9 @@ func (h *SMSHandler) LoginByCode(c *gin.Context) {
|
||||
}()
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": resp,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -19,9 +19,19 @@ func NewStatsHandler(statsService *service.StatsService) *StatsHandler {
|
||||
}
|
||||
|
||||
func (h *StatsHandler) GetDashboard(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "dashboard stats not implemented"})
|
||||
stats, err := h.statsService.GetDashboardStats(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "获取仪表盘数据失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "data": stats})
|
||||
}
|
||||
|
||||
func (h *StatsHandler) GetUserStats(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "user stats not implemented"})
|
||||
stats, err := h.statsService.GetUserStats(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "获取用户统计失败"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "data": stats})
|
||||
}
|
||||
|
||||
@@ -33,7 +33,11 @@ func (h *ThemeHandler) CreateTheme(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, theme)
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": theme,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateTheme 更新主题
|
||||
@@ -56,7 +60,11 @@ func (h *ThemeHandler) UpdateTheme(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, theme)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": theme,
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteTheme 删除主题
|
||||
@@ -72,7 +80,10 @@ func (h *ThemeHandler) DeleteTheme(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "theme deleted"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "theme deleted",
|
||||
})
|
||||
}
|
||||
|
||||
// GetTheme 获取主题
|
||||
@@ -89,7 +100,11 @@ func (h *ThemeHandler) GetTheme(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, theme)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": theme,
|
||||
})
|
||||
}
|
||||
|
||||
// ListThemes 获取所有主题
|
||||
@@ -100,7 +115,11 @@ func (h *ThemeHandler) ListThemes(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"themes": themes})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": themes,
|
||||
})
|
||||
}
|
||||
|
||||
// ListAllThemes 获取所有主题(包括禁用的)
|
||||
@@ -111,7 +130,11 @@ func (h *ThemeHandler) ListAllThemes(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"themes": themes})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": themes,
|
||||
})
|
||||
}
|
||||
|
||||
// GetDefaultTheme 获取默认主题
|
||||
@@ -122,7 +145,11 @@ func (h *ThemeHandler) GetDefaultTheme(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, theme)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": theme,
|
||||
})
|
||||
}
|
||||
|
||||
// SetDefaultTheme 设置默认主题
|
||||
@@ -138,7 +165,10 @@ func (h *ThemeHandler) SetDefaultTheme(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "default theme set"})
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "default theme set",
|
||||
})
|
||||
}
|
||||
|
||||
// GetActiveTheme 获取当前生效的主题(公开接口)
|
||||
@@ -149,5 +179,9 @@ func (h *ThemeHandler) GetActiveTheme(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, theme)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": theme,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -55,7 +55,11 @@ func (h *UserHandler) CreateUser(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, toUserResponse(user))
|
||||
c.JSON(http.StatusCreated, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": toUserResponse(user),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *UserHandler) ListUsers(c *gin.Context) {
|
||||
@@ -74,7 +78,11 @@ func (h *UserHandler) ListUsers(c *gin.Context) {
|
||||
handleError(c, err)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, result)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"message": "success",
|
||||
"data": result,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -242,6 +250,38 @@ func (h *UserHandler) AssignRoles(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "role assignment not implemented"})
|
||||
}
|
||||
|
||||
func (h *UserHandler) BatchUpdateStatus(c *gin.Context) {
|
||||
var req service.BatchUpdateStatusRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
count, err := h.userService.BatchUpdateStatus(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "批量更新失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "更新成功", "data": gin.H{"count": count}})
|
||||
}
|
||||
|
||||
func (h *UserHandler) BatchDelete(c *gin.Context) {
|
||||
var req service.BatchDeleteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
count, err := h.userService.BatchDelete(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "批量删除失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "删除成功", "data": gin.H{"count": count}})
|
||||
}
|
||||
|
||||
func (h *UserHandler) UploadAvatar(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "avatar upload not implemented"})
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -19,21 +20,106 @@ func NewWebhookHandler(webhookService *service.WebhookService) *WebhookHandler {
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) CreateWebhook(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "webhook creation not implemented"})
|
||||
var req service.CreateWebhookRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
userID, _ := c.Get("user_id")
|
||||
creatorID, _ := userID.(int64)
|
||||
|
||||
webhook, err := h.webhookService.CreateWebhook(c.Request.Context(), &req, creatorID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "创建 Webhook 失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"code": 0, "data": webhook})
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) ListWebhooks(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"webhooks": []interface{}{}})
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 20
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
|
||||
userID, _ := c.Get("user_id")
|
||||
creatorID, _ := userID.(int64)
|
||||
|
||||
webhooks, total, err := h.webhookService.ListWebhooksPaginated(c.Request.Context(), creatorID, offset, pageSize)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "获取 Webhook 列表失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"data": webhooks,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) UpdateWebhook(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "webhook update not implemented"})
|
||||
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "无效的 Webhook ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req service.UpdateWebhookRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.webhookService.UpdateWebhook(c.Request.Context(), id, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "更新 Webhook 失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "更新成功"})
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) DeleteWebhook(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"message": "webhook deletion not implemented"})
|
||||
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "无效的 Webhook ID"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.webhookService.DeleteWebhook(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "删除 Webhook 失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "message": "删除成功"})
|
||||
}
|
||||
|
||||
func (h *WebhookHandler) GetWebhookDeliveries(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{"deliveries": []interface{}{}})
|
||||
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"code": 400, "message": "无效的 Webhook ID"})
|
||||
return
|
||||
}
|
||||
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
|
||||
if limit < 1 || limit > 100 {
|
||||
limit = 20
|
||||
}
|
||||
|
||||
deliveries, err := h.webhookService.GetWebhookDeliveries(c.Request.Context(), id, limit)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"code": 500, "message": "获取投递记录失败"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"code": 0, "data": deliveries})
|
||||
}
|
||||
|
||||
@@ -69,9 +69,15 @@ func (m *OperationLogMiddleware) Record() gin.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
isAdmin := IsAdmin(c)
|
||||
opType := methodToType(method)
|
||||
if isAdmin {
|
||||
opType = "admin:" + opType
|
||||
}
|
||||
|
||||
logEntry := &domain.OperationLog{
|
||||
UserID: userIDPtr,
|
||||
OperationType: methodToType(method),
|
||||
OperationType: opType,
|
||||
OperationName: c.FullPath(),
|
||||
RequestMethod: method,
|
||||
RequestPath: c.Request.URL.Path,
|
||||
|
||||
@@ -211,6 +211,8 @@ func (r *Router) Setup() *gin.Engine {
|
||||
users.PUT("/:id/status", middleware.RequirePermission("user:manage"), r.userHandler.UpdateUserStatus)
|
||||
users.GET("/:id/roles", r.userHandler.GetUserRoles)
|
||||
users.PUT("/:id/roles", middleware.RequirePermission("user:manage"), r.userHandler.AssignRoles)
|
||||
users.PUT("/batch/status", middleware.RequirePermission("user:manage"), r.userHandler.BatchUpdateStatus)
|
||||
users.DELETE("/batch", middleware.RequirePermission("user:delete"), r.userHandler.BatchDelete)
|
||||
|
||||
if r.avatarHandler != nil {
|
||||
users.POST("/:id/avatar", r.avatarHandler.UploadAvatar)
|
||||
|
||||
Reference in New Issue
Block a user