This commit is contained in:
2026-08-17 02:12:17 +08:00
parent 560ae46b82
commit 6a05803e08
96 changed files with 2888 additions and 685 deletions
+3 -31
View File
@@ -1,7 +1,6 @@
package handler
import (
"strconv"
"time"
"kra/internal/server/httpx"
@@ -35,16 +34,7 @@ func (h *Announcement) Create(c *gin.Context) {
}
func (h *Announcement) Delete(c *gin.Context) {
id, present, err := queryUintValue(c, "ID")
if err != nil {
httpx.Fail(c, err.Error())
return
}
if !present {
// Preserve the legacy endpoint behavior for an omitted ID.
id = 0
}
if err := h.service.Delete(c.Request.Context(), id); err != nil {
if err := h.service.Delete(c.Request.Context(), c.Query("ID")); err != nil {
httpx.Fail(c, "删除失败")
return
}
@@ -52,20 +42,7 @@ func (h *Announcement) Delete(c *gin.Context) {
}
func (h *Announcement) DeleteByIDs(c *gin.Context) {
values := c.QueryArray("IDs[]")
if len(values) == 0 {
values = c.QueryArray("IDs")
}
ids := make([]uint, 0, len(values))
for _, value := range values {
id, err := strconv.ParseUint(value, 10, 64)
if err != nil || id == 0 {
httpx.Fail(c, "参数错误")
return
}
ids = append(ids, uint(id))
}
if err := h.service.DeleteByIDs(c.Request.Context(), ids); err != nil {
if err := h.service.DeleteByIDs(c.Request.Context(), c.QueryArray("IDs[]")); err != nil {
httpx.Fail(c, "批量删除失败")
return
}
@@ -86,12 +63,7 @@ func (h *Announcement) Update(c *gin.Context) {
}
func (h *Announcement) Find(c *gin.Context) {
id, _, err := queryUintValue(c, "ID")
if err != nil {
httpx.Fail(c, err.Error())
return
}
item, err := h.service.Find(c.Request.Context(), id)
item, err := h.service.Find(c.Request.Context(), c.Query("ID"))
if err != nil {
httpx.Fail(c, "查询失败")
return
+37 -68
View File
@@ -2,7 +2,6 @@ package handler
import (
"errors"
"io"
"log/slog"
"strconv"
"time"
@@ -26,10 +25,23 @@ func NewAudit(service *service.AuditService, recorder *service.AuditRecorder, lo
return &Audit{service: service, recorder: recorder, logs: logs, logger: logger}
}
func page(c *gin.Context) (int, int) {
value, _ := strconv.Atoi(c.Query("page"))
size, _ := strconv.Atoi(c.Query("pageSize"))
return value, size
func page(c *gin.Context) (int, int, error) {
var value, size int
if raw, exists := c.GetQuery("page"); exists && raw != "" {
parsed, err := strconv.Atoi(raw)
if err != nil {
return 0, 0, err
}
value = parsed
}
if raw, exists := c.GetQuery("pageSize"); exists && raw != "" {
parsed, err := strconv.Atoi(raw)
if err != nil {
return 0, 0, err
}
size = parsed
}
return value, size, nil
}
func queryUintValue(c *gin.Context, key string) (uint, bool, error) {
@@ -42,9 +54,6 @@ func queryUintValue(c *gin.Context, key string) (uint, bool, error) {
}
func IDsFromQuery(c *gin.Context) []uint {
values := c.QueryArray("IDs[]")
if len(values) == 0 {
values = c.QueryArray("ids[]")
}
ids := make([]uint, 0, len(values))
for _, value := range values {
id, _ := strconv.ParseUint(value, 10, 64)
@@ -55,33 +64,6 @@ func IDsFromQuery(c *gin.Context) []uint {
return ids
}
// auditID accepts both DELETE encodings supported by the administration API:
// the identifier may be carried in the query string or in a JSON body.
func auditID(c *gin.Context) (uint, error) {
if raw := c.Query("ID"); raw != "" {
value, err := strconv.ParseUint(raw, 10, 64)
return uint(value), err
}
var request dto.IDRequest
if err := c.ShouldBindJSON(&request); err != nil && !errors.Is(err, io.EOF) {
return 0, err
}
return request.ID, nil
}
// auditIDs mirrors auditID for bulk deletes and supports both query-array and
// JSON-body encodings used by compatible administration pages.
func auditIDs(c *gin.Context) ([]uint, error) {
if ids := IDsFromQuery(c); len(ids) > 0 {
return ids, nil
}
var request dto.IDsRequest
if err := c.ShouldBindJSON(&request); err != nil && !errors.Is(err, io.EOF) {
return nil, err
}
return request.IDs, nil
}
func (h *Audit) Operations(c *gin.Context) {
var req dto.OperationRecordSearchRequest
if err := c.ShouldBindQuery(&req); err != nil {
@@ -113,28 +95,24 @@ func (h *Audit) Operation(c *gin.Context) {
httpx.Write(c, httpx.CodeSuccess, gin.H{"reSysOperationRecord": item}, "查询成功")
}
func (h *Audit) DeleteOperation(c *gin.Context) {
id, err := auditID(c)
if err != nil {
var req dto.IDRequest
if err := c.ShouldBindJSON(&req); err != nil {
httpx.Fail(c, err.Error())
return
}
if id == 0 {
httpx.Fail(c, "删除失败")
return
}
if err := h.service.DeleteOperations(c.Request.Context(), []uint{id}); err != nil {
if err := h.service.DeleteOperations(c.Request.Context(), []uint{req.ID}); err != nil {
httpx.Fail(c, "删除失败")
return
}
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteOperations(c *gin.Context) {
ids, err := auditIDs(c)
if err != nil {
var req dto.IDsRequest
if err := c.ShouldBindJSON(&req); err != nil {
httpx.Fail(c, err.Error())
return
}
if err := h.service.DeleteOperations(c.Request.Context(), ids); err != nil {
if err := h.service.DeleteOperations(c.Request.Context(), req.IDs); err != nil {
httpx.Fail(c, "批量删除失败")
return
}
@@ -168,28 +146,24 @@ func (h *Audit) Login(c *gin.Context) {
httpx.Write(c, httpx.CodeSuccess, item, "查询成功")
}
func (h *Audit) DeleteLogin(c *gin.Context) {
id, err := auditID(c)
if err != nil {
var req dto.IDRequest
if err := c.ShouldBindJSON(&req); err != nil {
httpx.Fail(c, err.Error())
return
}
if id == 0 {
httpx.Fail(c, "删除失败")
return
}
if err := h.service.DeleteLogins(c.Request.Context(), []uint{id}); err != nil {
if err := h.service.DeleteLogins(c.Request.Context(), []uint{req.ID}); err != nil {
httpx.Fail(c, "删除失败")
return
}
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteLogins(c *gin.Context) {
ids, err := auditIDs(c)
if err != nil {
var req dto.IDsRequest
if err := c.ShouldBindJSON(&req); err != nil {
httpx.Fail(c, err.Error())
return
}
if err := h.service.DeleteLogins(c.Request.Context(), ids); err != nil {
if err := h.service.DeleteLogins(c.Request.Context(), req.IDs); err != nil {
httpx.Fail(c, "批量删除失败")
return
}
@@ -298,24 +272,15 @@ func failLogViewer(c *gin.Context, err error, logger *slog.Logger) {
}
func (h *Audit) DeleteError(c *gin.Context) {
id, err := auditID(c)
if err != nil {
httpx.Fail(c, err.Error())
return
}
if err := h.service.DeleteErrors(c.Request.Context(), []uint{id}); err != nil {
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
if err := h.service.DeleteErrors(c.Request.Context(), []uint{uint(id)}); err != nil {
httpx.Fail(c, "删除失败:"+err.Error())
return
}
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteErrors(c *gin.Context) {
ids, err := auditIDs(c)
if err != nil {
httpx.Fail(c, err.Error())
return
}
if err := h.service.DeleteErrors(c.Request.Context(), ids); err != nil {
if err := h.service.DeleteErrors(c.Request.Context(), IDsFromQuery(c)); err != nil {
httpx.Fail(c, "批量删除失败:"+err.Error())
return
}
@@ -343,7 +308,11 @@ func (h *Audit) Error(c *gin.Context) {
httpx.OKWithData(c, item)
}
func (h *Audit) Errors(c *gin.Context) {
p, size := page(c)
p, size, err := page(c)
if err != nil {
httpx.Fail(c, err.Error())
return
}
createdAtRange := make([]time.Time, 0, 2)
for _, raw := range c.QueryArray("createdAtRange[]") {
value, parseErr := time.Parse(time.RFC3339, raw)
+16 -4
View File
@@ -161,8 +161,20 @@ func (h *Dictionary) FindDetail(c *gin.Context) {
httpx.Write(c, httpx.CodeSuccess, gin.H{"reSysDictionaryDetail": item}, "查询成功")
}
func (h *Dictionary) Details(c *gin.Context) {
p, size := page(c)
dictionaryID, _ := strconv.ParseUint(c.Query("sysDictionaryID"), 10, 64)
p, size, err := page(c)
if err != nil {
httpx.Fail(c, err.Error())
return
}
var dictionaryID uint64
if raw, exists := c.GetQuery("sysDictionaryID"); exists && raw != "" {
var parseErr error
dictionaryID, parseErr = strconv.ParseUint(raw, 10, 0)
if parseErr != nil {
httpx.Fail(c, parseErr.Error())
return
}
}
filter := biz.DictionaryDetailFilter{DictionaryID: uint(dictionaryID), Label: c.Query("label"), Value: c.Query("value")}
if raw, exists := c.GetQuery("status"); exists {
value, parseErr := strconv.ParseBool(raw)
@@ -199,7 +211,7 @@ func (h *Dictionary) Details(c *gin.Context) {
func (h *Dictionary) Tree(byType bool) gin.HandlerFunc {
return func(c *gin.Context) {
rawID := c.Query("sysDictionaryID")
id, parseErr := strconv.ParseUint(rawID, 10, 64)
id, parseErr := strconv.ParseUint(rawID, 10, 32)
typ := ""
if byType {
typ = c.Query("type")
@@ -241,7 +253,7 @@ func (h *Dictionary) Path(c *gin.Context) {
httpx.Fail(c, "字典详情ID不能为空")
return
}
id, parseErr := strconv.ParseUint(rawID, 10, 64)
id, parseErr := strconv.ParseUint(rawID, 10, 32)
if parseErr != nil {
httpx.Fail(c, "字典详情ID格式错误")
return
+11 -1
View File
@@ -71,6 +71,12 @@ func (h *Export) Delete(c *gin.Context) {
httpx.Fail(c, err.Error())
return
}
// Deleting an empty model is rejected by GORM's global-update guard in the
// reference endpoint. Preserve the same externally visible failure.
if req.ID == 0 {
httpx.Fail(c, "删除失败")
return
}
if err := h.service.Delete(c.Request.Context(), []uint{req.ID}); err != nil {
httpx.Fail(c, "删除失败")
return
@@ -103,7 +109,11 @@ func (h *Export) Find(c *gin.Context) {
httpx.OKWithData(c, gin.H{"resysExportTemplate": item})
}
func (h *Export) List(c *gin.Context) {
p, size := page(c)
p, size, err := page(c)
if err != nil {
httpx.Fail(c, err.Error())
return
}
start, startErr := parseTime(c.Query("startCreatedAt"))
end, endErr := parseTime(c.Query("endCreatedAt"))
if startErr != nil || endErr != nil {
+1 -1
View File
@@ -67,7 +67,7 @@ func (h *Media) Delete(c *gin.Context) {
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
}
func (h *Media) DeleteMany(c *gin.Context) {
var req dto.IDsRequest
var req dto.MediaBatchDeleteRequest
if err := c.ShouldBindJSON(&req); err != nil {
httpx.Fail(c, err.Error())
return
+2 -2
View File
@@ -106,7 +106,7 @@ func (h *Menu) Delete(c *gin.Context) {
httpx.Fail(c, "ID值不能为空")
return
}
if err := h.service.Delete(c.Request.Context(), req.ID); err != nil {
if err := h.service.Delete(c.Request.Context(), uint(req.ID)); err != nil {
httpx.Fail(c, "删除失败:"+err.Error())
return
}
@@ -123,7 +123,7 @@ func (h *Menu) Find(c *gin.Context) {
httpx.Fail(c, "ID值不能为空")
return
}
item, err := h.service.Find(c.Request.Context(), req.ID)
item, err := h.service.Find(c.Request.Context(), uint(req.ID))
if err != nil {
httpx.Fail(c, "获取失败")
return
+4 -8
View File
@@ -1,8 +1,6 @@
package handler
import (
"strconv"
"kra/internal/server/httpx"
"kra/internal/service"
"kra/internal/service/dto"
@@ -45,8 +43,7 @@ func (h *Parameter) Update(c *gin.Context) {
}
func (h *Parameter) Delete(c *gin.Context) {
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
if err := h.service.DeleteParameters(c.Request.Context(), []uint{uint(id)}); err != nil {
if err := h.service.DeleteParameters(c.Request.Context(), []string{c.Query("ID")}); err != nil {
httpx.Fail(c, "删除失败:"+err.Error())
return
}
@@ -54,7 +51,7 @@ func (h *Parameter) Delete(c *gin.Context) {
}
func (h *Parameter) DeleteMany(c *gin.Context) {
ids := IDsFromQuery(c)
ids := c.QueryArray("IDs[]")
if err := h.service.DeleteParameters(c.Request.Context(), ids); err != nil {
httpx.Fail(c, "批量删除失败:"+err.Error())
return
@@ -63,8 +60,7 @@ func (h *Parameter) DeleteMany(c *gin.Context) {
}
func (h *Parameter) Find(c *gin.Context) {
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
item, err := h.service.Parameter(c.Request.Context(), uint(id), "")
item, err := h.service.ParameterByID(c.Request.Context(), c.Query("ID"))
if err != nil {
httpx.Fail(c, "查询失败:"+err.Error())
return
@@ -73,7 +69,7 @@ func (h *Parameter) Find(c *gin.Context) {
}
func (h *Parameter) Get(c *gin.Context) {
item, err := h.service.Parameter(c.Request.Context(), 0, c.Query("key"))
item, err := h.service.ParameterByKey(c.Request.Context(), c.Query("key"))
if err != nil {
httpx.Fail(c, "获取失败:"+err.Error())
return
+1 -4
View File
@@ -1,8 +1,6 @@
package handler
import (
"strconv"
"kra/internal/server/httpx"
"kra/internal/service"
"kra/internal/service/dto"
@@ -44,8 +42,7 @@ func (h *Permission) SetButtons(c *gin.Context) {
}
func (h *Permission) CanRemoveButton(c *gin.Context) {
id, _ := strconv.ParseUint(c.Query("id"), 10, 64)
allowed, err := h.service.CanRemoveButton(c.Request.Context(), uint(id))
allowed, err := h.service.CanRemoveButton(c.Request.Context(), c.Query("id"))
if err != nil {
httpx.Fail(c, err.Error())
return
+1 -1
View File
@@ -91,7 +91,7 @@ func (h *Public) Login(c *gin.Context) {
}
return
}
maxAge := int(time.Until(time.UnixMilli(result.ExpiresAt)).Seconds())
maxAge := int(time.UnixMilli(result.ExpiresAt).Unix() - time.Now().Unix())
httpx.SetTokenCookie(c, result.Token, maxAge)
httpx.Write(c, httpx.CodeSuccess, result, "登录成功")
}
+4 -2
View File
@@ -125,7 +125,7 @@ func (h *Task) AlertStream(c *gin.Context) {
httpx.Fail(c, "未获取到用户身份")
return
}
c.Header("Content-Type", "text/event-stream")
c.Header("Content-Type", "text/event-stream; charset=utf-8")
c.Header("Cache-Control", "no-cache, no-transform")
c.Header("Connection", "keep-alive")
c.Header("X-Accel-Buffering", "no")
@@ -135,7 +135,9 @@ func (h *Task) AlertStream(c *gin.Context) {
return
}
// Remove the normal server deadline for long-lived SSE connections.
_ = http.NewResponseController(c.Writer).SetWriteDeadline(time.Time{})
controller := http.NewResponseController(c.Writer)
_ = controller.SetWriteDeadline(time.Time{})
_ = controller.SetReadDeadline(time.Time{})
events := h.service.Subscribe(claims.ID)
defer h.service.Unsubscribe(claims.ID, events)
ticker := time.NewTicker(30 * time.Second)
+7 -4
View File
@@ -71,7 +71,10 @@ func (h *User) Create(c *gin.Context) {
httpx.Fail(c, err.Error())
return
}
httpx.Write(c, httpx.CodeError, gin.H{"user": dto.UserResponse{}}, "注册失败")
if result == nil {
result = &dto.UserResponse{}
}
httpx.Write(c, httpx.CodeError, gin.H{"user": result}, "注册失败")
return
}
httpx.Write(c, httpx.CodeSuccess, gin.H{"user": result}, "注册成功")
@@ -115,7 +118,7 @@ func (h *User) UpdateSelf(c *gin.Context) {
func (h *User) Delete(c *gin.Context) {
var req dto.IDRequest
if err := c.ShouldBind(&req); err != nil {
if err := c.ShouldBindJSON(&req); err != nil {
httpx.Fail(c, err.Error())
return
}
@@ -235,7 +238,7 @@ func (h *User) SwitchAuthority(c *gin.Context) {
}
c.Header("new-token", login.Token)
c.Header("new-expires-at", strconv.FormatInt(login.ExpiresAt/1000, 10))
maxAge := int(time.Until(time.UnixMilli(login.ExpiresAt)).Seconds())
maxAge := int(time.UnixMilli(login.ExpiresAt).Unix() - time.Now().Unix())
httpx.SetTokenCookie(c, login.Token, maxAge)
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "修改成功")
}
@@ -246,7 +249,7 @@ func (h *User) Get(c *gin.Context) {
httpx.NoAuth(c, "未登录或非法访问")
return
}
result, err := h.service.User(c.Request.Context(), claims.ID)
result, err := h.service.UserByUUID(c.Request.Context(), claims.UUID)
if err != nil {
httpx.Fail(c, "获取失败")
return
+2 -1
View File
@@ -81,6 +81,7 @@ func (h *Version) List(c *gin.Context) {
}
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Version) Export(c *gin.Context) {
var req dto.ExportVersionRequest
if err := c.ShouldBindJSON(&req); err != nil {
@@ -122,7 +123,7 @@ func (h *Version) Download(c *gin.Context) {
c.Header("Content-Type", "application/json")
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", name))
c.Header("Content-Length", strconv.Itoa(len(raw)))
c.Data(http.StatusOK, "application/json; charset=utf-8", []byte(raw))
c.Data(http.StatusOK, "application/json", []byte(raw))
}
func (h *Version) Import(c *gin.Context) {
var req dto.ImportVersionRequest