优化结构

This commit is contained in:
Yvan
2026-08-21 10:29:14 +08:00
parent 56980c8087
commit 055f6811ca
340 changed files with 382 additions and 382 deletions
+107
View File
@@ -0,0 +1,107 @@
package handler
import (
"kra/internal/service"
"time"
"github.com/gin-gonic/gin"
)
type Announcement struct{ service *service.AnnouncementService }
func NewAnnouncement(service *service.AnnouncementService) *Announcement {
return &Announcement{service: service}
}
func announcementInput(req service.AnnouncementRequest) service.AnnouncementInput {
return service.AnnouncementInput{ID: req.ID, Title: req.Title, Content: req.Content, UserID: req.UserID, Attachments: req.Attachments}
}
func (h *Announcement) Create(c *gin.Context) {
var req service.AnnouncementRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Create(c.Request.Context(), announcementInput(req)); err != nil {
Fail(c, "创建失败")
return
}
Write(c, CodeSuccess, gin.H{}, "创建成功")
}
func (h *Announcement) Delete(c *gin.Context) {
if err := h.service.Delete(c.Request.Context(), c.Query("ID")); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Announcement) DeleteByIDs(c *gin.Context) {
if err := h.service.DeleteByIDs(c.Request.Context(), c.QueryArray("IDs[]")); err != nil {
Fail(c, "批量删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}
func (h *Announcement) Update(c *gin.Context) {
var req service.AnnouncementRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Update(c.Request.Context(), announcementInput(req)); err != nil {
Fail(c, "更新失败")
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Announcement) Find(c *gin.Context) {
item, err := h.service.Find(c.Request.Context(), c.Query("ID"))
if err != nil {
Fail(c, "查询失败")
return
}
OKWithData(c, item)
}
func (h *Announcement) List(c *gin.Context) {
var req service.AnnouncementSearchRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.service.List(c.Request.Context(), req.Page, req.PageSize, req.StartCreatedAt, req.EndCreatedAt)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Announcement) DataSource(c *gin.Context) {
users, err := h.service.UserOptions(c.Request.Context())
if err != nil {
Fail(c, "查询失败")
return
}
OKWithData(c, gin.H{"userID": users})
}
func (h *Announcement) Public(c *gin.Context) {
Write(c, CodeSuccess, gin.H{"info": "不需要鉴权的公告接口信息"}, "获取成功")
}
func parseTime(value string) (*time.Time, error) {
if value == "" {
return nil, nil
}
parsed, err := time.Parse(time.RFC3339, value)
if err != nil {
return nil, err
}
return &parsed, nil
}
+254
View File
@@ -0,0 +1,254 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type API struct{ service *service.APIService }
func NewAPI(service *service.APIService) *API { return &API{service: service} }
func (h *API) List(c *gin.Context) {
var req service.APIListRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Page == 0 {
Fail(c, "Page值不能为空")
return
}
if req.PageSize == 0 {
Fail(c, "PageSize值不能为空")
return
}
items, total, err := h.service.ListAPI(c.Request.Context(), &req)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *API) All(c *gin.Context) {
items, err := h.service.AllAPI(c.Request.Context(), true)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"apis": items}, "获取成功")
}
func (h *API) Create(c *gin.Context) {
var req service.APIRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if message := validateAPIRequest(&req); message != "" {
Fail(c, message)
return
}
created, err := h.service.CreateAPIRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "创建失败")
return
}
Write(c, CodeSuccess, created, "创建成功")
}
func (h *API) Update(c *gin.Context) {
var req service.APIRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if message := validateAPIRequest(&req); message != "" {
Fail(c, message)
return
}
if err := h.service.UpdateAPIRequest(c.Request.Context(), &req); err != nil {
Fail(c, "修改失败")
return
}
Write(c, CodeSuccess, gin.H{}, "修改成功")
}
func (h *API) Delete(c *gin.Context) {
var req service.DeleteAPIRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "ID值不能为空")
return
}
if err := h.service.DeleteAPI(c.Request.Context(), req.ID); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *API) DeleteByIDs(c *gin.Context) {
var req service.DeleteAPIsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.DeleteAPIs(c.Request.Context(), req.IDs); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *API) Find(c *gin.Context) {
var req service.GetAPIRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "ID值不能为空")
return
}
item, err := h.service.FindAPIResponse(c.Request.Context(), req.ID)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"api": item}, "获取成功")
}
func (h *API) Groups(c *gin.Context) {
groups, groupAPIMap, err := h.service.Groups(c.Request.Context())
if err != nil {
Fail(c, "获取失败")
return
}
OKWithData(c, gin.H{"groups": groups, "apiGroupMap": groupAPIMap})
}
func (h *API) Roles(c *gin.Context) {
path, method := c.Query("path"), c.Query("method")
if path == "" || method == "" {
Fail(c, "API路径和请求方法不能为空")
return
}
ids, err := h.service.APIRoleIDs(c.Request.Context(), path, method)
if err != nil {
Fail(c, "获取失败"+err.Error())
return
}
if ids == nil {
ids = []uint{}
}
Write(c, CodeSuccess, ids, "获取成功")
}
func (h *API) SetRoles(c *gin.Context) {
var req service.SetAPIRolesRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Path == "" || req.Method == "" {
Fail(c, "API路径和请求方法不能为空")
return
}
if err := h.service.SetAPIRoles(c.Request.Context(), req.Path, req.Method, req.AuthorityIDs); err != nil {
Fail(c, "设置失败"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *API) Sync(engine *gin.Engine) gin.HandlerFunc {
return func(c *gin.Context) {
routes := engine.Routes()
values := make([]service.APIRequest, 0, len(routes))
for _, route := range routes {
values = append(values, service.APIRequest{Path: route.Path, Method: route.Method})
}
result, err := h.service.SyncAPIResponses(c.Request.Context(), values)
if err != nil {
Fail(c, "同步失败")
return
}
OKWithData(c, result)
}
}
func (h *API) Ignore(c *gin.Context) {
var req service.IgnoreAPIRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.SetAPIIgnored(c.Request.Context(), req.Path, req.Method, req.Flag); err != nil {
Fail(c, "忽略失败")
return
}
OK(c)
}
func (h *API) ApplySync(c *gin.Context) {
var req service.ApplyAPISyncRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.ApplyAPISyncRequest(c.Request.Context(), &req); err != nil {
Fail(c, "忽略失败")
return
}
OK(c)
}
func (h *API) FreshCasbin(c *gin.Context) {
if err := h.service.FreshCasbin(c.Request.Context()); err != nil {
Fail(c, "刷新失败")
return
}
Write(c, CodeSuccess, gin.H{}, "刷新成功")
}
func (h *API) SetPolicyPaths(c *gin.Context) {
var req service.SetPolicyPathsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
if err := h.service.SetPolicyPathsRequest(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败")
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *API) PolicyPaths(c *gin.Context) {
var req service.GetPolicyPathsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
paths, err := h.service.PolicyPathResponses(c.Request.Context(), req.AuthorityID)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"paths": paths}, "获取成功")
}
func validateAPIRequest(req *service.APIRequest) string {
switch {
case req.Path == "":
return "Path值不能为空"
case req.Description == "":
return "Description值不能为空"
case req.APIGroup == "":
return "ApiGroup值不能为空"
case req.Method == "":
return "Method值不能为空"
default:
return ""
}
}
+56
View File
@@ -0,0 +1,56 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type APIToken struct {
service *service.TokenService
}
func NewAPIToken(service *service.TokenService) *APIToken {
return &APIToken{service: service}
}
func (h *APIToken) Create(c *gin.Context) {
var req service.CreateAPITokenRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
token, err := h.service.CreateAPIToken(c.Request.Context(), req.UserID, req.AuthorityID, req.Days, req.Remark)
if err != nil {
Fail(c, "签发失败: "+err.Error())
return
}
Write(c, CodeSuccess, gin.H{"token": token}, "签发成功")
}
func (h *APIToken) List(c *gin.Context) {
var req service.APITokenListRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.service.APITokens(c.Request.Context(), req.Page, req.PageSize, req.UserID, req.Status)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *APIToken) Delete(c *gin.Context) {
var req service.IDRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.DisableAPIToken(c.Request.Context(), req.ID); err != nil {
Fail(c, "作废失败")
return
}
Write(c, CodeSuccess, gin.H{}, "作废成功")
}
+302
View File
@@ -0,0 +1,302 @@
package handler
import (
"errors"
"log/slog"
"strconv"
"time"
"kra/internal/biz"
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Audit struct {
service *service.AuditService
recorder *service.AuditRecorder
logs *service.LogViewerService
logger *slog.Logger
}
func NewAudit(service *service.AuditService, recorder *service.AuditRecorder, logs *service.LogViewerService, logger *slog.Logger) *Audit {
return &Audit{service: service, recorder: recorder, logs: logs, logger: logger}
}
func (h *Audit) Operations(c *gin.Context) {
var req service.OperationRecordSearchRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.service.OperationsFilter(c.Request.Context(), req.Page, req.PageSize, req.Path, req.Method, req.Status)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Audit) Operation(c *gin.Context) {
var req service.AuditIDQuery
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "ID值不能为空")
return
}
item, err := h.service.Operation(c.Request.Context(), req.ID)
if err != nil {
Fail(c, "查询失败")
return
}
Write(c, CodeSuccess, gin.H{"reSysOperationRecord": item}, "查询成功")
}
func (h *Audit) DeleteOperation(c *gin.Context) {
var req service.IDRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.DeleteOperations(c.Request.Context(), []int{int(req.ID)}); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteOperations(c *gin.Context) {
var req service.AuditIDsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.DeleteOperations(c.Request.Context(), req.IDs); err != nil {
Fail(c, "批量删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}
func (h *Audit) Logins(c *gin.Context) {
var req service.LoginLogSearchRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.service.LoginsFilter(c.Request.Context(), req.Page, req.PageSize, req.Username, req.Status)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Audit) Login(c *gin.Context) {
var req service.AuditIDQuery
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
item, err := h.service.Login(c.Request.Context(), req.ID)
if err != nil {
Fail(c, "查询失败")
return
}
Write(c, CodeSuccess, item, "查询成功")
}
func (h *Audit) DeleteLogin(c *gin.Context) {
var req service.IDRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.DeleteLogins(c.Request.Context(), []int{int(req.ID)}); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteLogins(c *gin.Context) {
var req service.AuditIDsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.DeleteLogins(c.Request.Context(), req.IDs); err != nil {
Fail(c, "批量删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}
func (h *Audit) DataAccess(c *gin.Context) {
var req service.DataAccessListRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.service.DataAccessRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Audit) DeleteDataAccess(c *gin.Context) {
var req service.IDsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.DeleteDataAccess(c.Request.Context(), req.IDs); err != nil {
Fail(c, "删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) LogDates(c *gin.Context) {
month := c.Query("month")
// Month uses `binding:"required"`; an omitted or empty query is a
// generic parameter error, not the service's format-specific error.
if month == "" {
Fail(c, "参数错误")
return
}
data, err := h.logs.LogDates(c.Request.Context(), month)
if err != nil {
failLogViewer(c, err, h.logger)
return
}
Write(c, CodeSuccess, data, "获取成功")
}
func (h *Audit) LogFiles(c *gin.Context) {
date := c.Query("date")
if date == "" {
Fail(c, "参数错误")
return
}
data, err := h.logs.LogFiles(c.Request.Context(), date)
if err != nil {
failLogViewer(c, err, h.logger)
return
}
Write(c, CodeSuccess, data, "获取成功")
}
func (h *Audit) LogContent(c *gin.Context) {
date, path := c.Query("date"), c.Query("path")
if date == "" || path == "" {
Fail(c, "参数错误")
return
}
var cursor *int64
if raw := c.Query("cursor"); raw != "" {
value, err := strconv.ParseInt(raw, 10, 64)
if err != nil {
Fail(c, "参数错误")
return
}
cursor = &value
}
// Preserve negative cursors for the usecase to classify with the compatible
// "日志文件路径不合法" error rather than rejecting them as binding errors.
data, err := h.logs.LogContent(c.Request.Context(), date, path, cursor)
if err != nil {
failLogViewer(c, err, h.logger)
return
}
Write(c, CodeSuccess, data, "获取成功")
}
func failLogViewer(c *gin.Context, err error, logger *slog.Logger) {
if logger != nil {
logger.ErrorContext(c.Request.Context(), "日志查看失败", "mod", "log-viewer", "error", err)
}
message := "读取日志失败"
switch {
case errors.Is(err, biz.ErrInvalidLogMonth):
message = "日志月份格式不正确"
case errors.Is(err, biz.ErrInvalidLogDate):
message = "日志日期格式不正确"
case errors.Is(err, biz.ErrInvalidLogPath):
message = "日志文件路径不合法"
case errors.Is(err, biz.ErrLogFileNotFound):
message = "日志文件不存在"
case errors.Is(err, biz.ErrLogFileUnreadable):
message = "日志文件不可读取"
case errors.Is(err, biz.ErrLogRootUnavailable):
message = "日志目录不可读取"
}
Fail(c, message)
}
func (h *Audit) DeleteError(c *gin.Context) {
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
if err := h.service.DeleteErrors(c.Request.Context(), []uint{uint(id)}); err != nil {
Fail(c, "删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Audit) DeleteErrors(c *gin.Context) {
if err := h.service.DeleteErrors(c.Request.Context(), IDsFromQuery(c)); err != nil {
Fail(c, "批量删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}
func (h *Audit) UpdateError(c *gin.Context) {
var req service.ErrorRecordMutationRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.UpdateErrorRequest(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Audit) Error(c *gin.Context) {
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
item, err := h.service.Error(c.Request.Context(), uint(id))
if err != nil {
Fail(c, "查询失败:"+err.Error())
return
}
OKWithData(c, item)
}
func (h *Audit) Errors(c *gin.Context) {
p, size, err := page(c)
if err != nil {
Fail(c, err.Error())
return
}
createdAtRange := make([]time.Time, 0, 2)
for _, raw := range c.QueryArray("createdAtRange[]") {
value, parseErr := time.Parse(time.RFC3339, raw)
if parseErr != nil {
Fail(c, parseErr.Error())
return
}
createdAtRange = append(createdAtRange, value)
}
items, total, err := h.service.ErrorsFilter(c.Request.Context(), p, size, c.Query("form"), c.Query("info"), createdAtRange)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: p, PageSize: size}, "获取成功")
}
func (h *Audit) CreateError(c *gin.Context) {
var req service.ErrorRecordMutationRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.recorder.CreateErrorMutationRequest(c.Request.Context(), &req); err != nil {
Fail(c, "创建失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "创建成功")
}
+167
View File
@@ -0,0 +1,167 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Authority struct{ service *service.AuthorityService }
func NewAuthority(service *service.AuthorityService) *Authority { return &Authority{service: service} }
func (h *Authority) List(c *gin.Context) {
items, err := h.service.Authorities(c.Request.Context())
if err != nil {
Fail(c, "获取失败"+err.Error())
return
}
Write(c, CodeSuccess, items, "获取成功")
}
func (h *Authority) Create(c *gin.Context) {
var req service.AuthorityRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
if req.AuthorityName == "" {
Fail(c, "AuthorityName值不能为空")
return
}
value, err := h.service.CreateAuthorityRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "创建失败"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{"authority": value}, "创建成功")
}
func (h *Authority) Copy(c *gin.Context) {
var req service.CopyAuthorityRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.OldAuthorityID == 0 {
Fail(c, "OldAuthorityId值不能为空")
return
}
if req.Authority.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
if req.Authority.AuthorityName == "" {
Fail(c, "AuthorityName值不能为空")
return
}
value, err := h.service.CopyAuthorityRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "拷贝失败"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{"authority": value}, "拷贝成功")
}
func (h *Authority) Update(c *gin.Context) {
var req service.AuthorityRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
if req.AuthorityName == "" {
Fail(c, "AuthorityName值不能为空")
return
}
value, err := h.service.UpdateAuthorityRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "更新失败"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{"authority": value}, "更新成功")
}
func (h *Authority) Delete(c *gin.Context) {
var req service.DeleteAuthorityRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
if err := h.service.DeleteAuthority(c.Request.Context(), req.AuthorityID); err != nil {
Fail(c, "删除失败"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Authority) SetUsers(c *gin.Context) {
var req service.SetRoleUsersRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "角色ID不能为空")
return
}
if err := h.service.SetAuthorityUsers(c.Request.Context(), req.AuthorityID, req.UserIDs); err != nil {
Fail(c, "设置失败"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *Authority) Users(c *gin.Context) {
var req service.SetRoleUsersRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
ids, err := h.service.AuthorityUserIDs(c.Request.Context(), req.AuthorityID)
if err != nil {
Fail(c, "获取失败"+err.Error())
return
}
if ids == nil {
ids = []uint{}
}
Write(c, CodeSuccess, ids, "获取成功")
}
func (h *Authority) SetDataScope(c *gin.Context) {
var req service.SetDataScopeRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "角色ID不能为空")
return
}
if err := h.service.SetDataScope(c.Request.Context(), req.AuthorityID, req.DataScope, req.DepartmentIDs); err != nil {
Fail(c, "设置失败"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *Authority) DataScopeDepartments(c *gin.Context) {
var req service.SetDataScopeRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
ids, err := h.service.DataScopeDepartmentIDs(c.Request.Context(), req.AuthorityID)
if err != nil {
Fail(c, "获取失败"+err.Error())
return
}
if ids == nil {
ids = []uint{}
}
Write(c, CodeSuccess, ids, "获取成功")
}
+263
View File
@@ -0,0 +1,263 @@
package handler
import (
"kra/internal/service"
"strconv"
"github.com/gin-gonic/gin"
)
type Dictionary struct{ service *service.DictionaryService }
func NewDictionary(service *service.DictionaryService) *Dictionary {
return &Dictionary{service: service}
}
func (h *Dictionary) Create(c *gin.Context) {
var req service.DictionaryRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
created, err := h.service.CreateDictionaryRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "创建失败")
return
}
Write(c, CodeSuccess, created, "创建成功")
}
func (h *Dictionary) Update(c *gin.Context) {
var req service.DictionaryRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.UpdateDictionaryRequest(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败")
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Dictionary) Delete(c *gin.Context) {
var req service.DictionaryRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.DeleteDictionary(c.Request.Context(), req.ID); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Dictionary) Find(c *gin.Context) {
var req service.DictionaryRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
item, err := h.service.Dictionary(c.Request.Context(), req.ID, req.Type, req.Status, true)
if err != nil {
Fail(c, "字典未创建或未开启")
return
}
Write(c, CodeSuccess, gin.H{"resysDictionary": item}, "查询成功")
}
func (h *Dictionary) List(details bool) gin.HandlerFunc {
return func(c *gin.Context) {
items, _, err := h.service.Dictionaries(c.Request.Context(), 0, 0, c.Query("name"), "", details)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, items, "获取成功")
}
}
func (h *Dictionary) Export(c *gin.Context) {
var req service.DictionaryRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "字典ID不能为空")
return
}
item, err := h.service.ExportDictionary(c.Request.Context(), req.ID)
if err != nil {
Fail(c, "导出失败")
return
}
Write(c, CodeSuccess, item, "导出成功")
}
func (h *Dictionary) Import(c *gin.Context) {
var req service.ImportDictionaryRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.ImportDictionaryJSON(c.Request.Context(), req.JSON); err != nil {
Fail(c, "导入失败: "+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "导入成功")
}
func (h *Dictionary) CreateDetail(c *gin.Context) {
var req service.DictionaryDetailRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.CreateDictionaryDetailRequest(c.Request.Context(), &req); err != nil {
Fail(c, "创建失败")
return
}
Write(c, CodeSuccess, gin.H{}, "创建成功")
}
func (h *Dictionary) UpdateDetail(c *gin.Context) {
var req service.DictionaryDetailRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.UpdateDictionaryDetailRequest(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败")
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Dictionary) DeleteDetail(c *gin.Context) {
var req service.DictionaryDetailRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.DeleteDictionaryDetail(c.Request.Context(), req.ID); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Dictionary) FindDetail(c *gin.Context) {
var req service.DictionaryDetailRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "ID值不能为空")
return
}
item, err := h.service.DictionaryDetail(c.Request.Context(), req.ID)
if err != nil {
Fail(c, "查询失败")
return
}
Write(c, CodeSuccess, gin.H{"reSysDictionaryDetail": item}, "查询成功")
}
func (h *Dictionary) Details(c *gin.Context) {
p, size, err := page(c)
if err != nil {
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 {
Fail(c, parseErr.Error())
return
}
}
filter := service.DictionaryDetailListRequest{DictionaryID: uint(dictionaryID), Label: c.Query("label"), Value: c.Query("value")}
if raw, exists := c.GetQuery("status"); exists {
value, parseErr := strconv.ParseBool(raw)
if parseErr != nil {
Fail(c, parseErr.Error())
return
}
filter.Status = &value
}
if raw, exists := c.GetQuery("parentID"); exists {
value, parseErr := strconv.ParseUint(raw, 10, 64)
if parseErr != nil {
Fail(c, parseErr.Error())
return
}
parentID := uint(value)
filter.ParentID = &parentID
}
if raw, exists := c.GetQuery("level"); exists {
value, parseErr := strconv.Atoi(raw)
if parseErr != nil {
Fail(c, parseErr.Error())
return
}
filter.Level = &value
}
items, total, err := h.service.DictionaryDetailsRequest(c.Request.Context(), p, size, filter)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: p, PageSize: size}, "获取成功")
}
func (h *Dictionary) Tree(byType bool) gin.HandlerFunc {
return func(c *gin.Context) {
rawID := c.Query("sysDictionaryID")
id, parseErr := strconv.ParseUint(rawID, 10, 32)
typ := ""
if byType {
typ = c.Query("type")
if typ == "" {
Fail(c, "字典类型不能为空")
return
}
} else if rawID == "" {
Fail(c, "字典ID不能为空")
return
} else if parseErr != nil {
Fail(c, "字典ID格式错误")
return
}
items, err := h.service.DictionaryTree(c.Request.Context(), uint(id), typ)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"list": items}, "获取成功")
}
}
func (h *Dictionary) DetailsByParent(c *gin.Context) {
var req service.DictionaryDetailsByParentRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
items, err := h.service.DictionaryDetailsByParent(c.Request.Context(), req.DictionaryID, req.ParentID, req.IncludeChildren)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"list": items}, "获取成功")
}
func (h *Dictionary) Path(c *gin.Context) {
rawID := c.Query("id")
if rawID == "" {
Fail(c, "字典详情ID不能为空")
return
}
id, parseErr := strconv.ParseUint(rawID, 10, 32)
if parseErr != nil {
Fail(c, "字典详情ID格式错误")
return
}
path, err := h.service.DictionaryPath(c.Request.Context(), uint(id))
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"path": path}, "获取成功")
}
+32
View File
@@ -0,0 +1,32 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Email struct{ service *service.EmailService }
func NewEmail(service *service.EmailService) *Email { return &Email{service: service} }
func (h *Email) Test(c *gin.Context) {
if err := h.service.Test(c.Request.Context()); err != nil {
Fail(c, "发送失败")
return
}
Write(c, CodeSuccess, gin.H{}, "发送成功")
}
func (h *Email) Send(c *gin.Context) {
var req service.SendEmailRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Send(c.Request.Context(), req.To, req.Subject, req.Body); err != nil {
Fail(c, "发送失败")
return
}
Write(c, CodeSuccess, gin.H{}, "发送成功")
}
+241
View File
@@ -0,0 +1,241 @@
package handler
import (
"errors"
"kra/internal/service"
"net/http"
"net/url"
"strings"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
)
type Export struct {
service *service.ExportService
}
func NewExport(service *service.ExportService) *Export {
return &Export{service: service}
}
func exportParams(values url.Values) (map[string]string, error) {
out := map[string]string{}
nested, err := url.ParseQuery(values.Get("params"))
if err != nil {
return nil, err
}
for key, items := range nested {
if len(items) > 0 {
out[key] = items[0]
}
}
return out, nil
}
func exportIssueParams(values url.Values, blank bool) (map[string]string, error) {
params, err := exportParams(values)
if err != nil && blank {
return map[string]string{}, nil
}
return params, err
}
func (h *Export) Create(c *gin.Context) {
var req service.ExportTemplateRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Name == "" {
Fail(c, "Name值不能为空")
return
}
if err := h.service.CreateRequest(c.Request.Context(), &req); err != nil {
Fail(c, "创建失败")
return
}
Write(c, CodeSuccess, gin.H{}, "创建成功")
}
func (h *Export) Update(c *gin.Context) {
var req service.ExportTemplateRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Name == "" {
Fail(c, "Name值不能为空")
return
}
if err := h.service.UpdateRequest(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败")
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Export) Delete(c *gin.Context) {
var req service.IDRequest
if err := c.ShouldBindJSON(&req); err != nil {
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 {
Fail(c, "删除失败")
return
}
if err := h.service.Delete(c.Request.Context(), []uint{req.ID}); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Export) DeleteMany(c *gin.Context) {
var req service.IDsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Delete(c.Request.Context(), req.IDs); err != nil {
Fail(c, "批量删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}
func (h *Export) Find(c *gin.Context) {
var req service.ExportTemplateRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
item, err := h.service.Template(c.Request.Context(), req.ID, "")
if err != nil {
Fail(c, "查询失败")
return
}
OKWithData(c, gin.H{"resysExportTemplate": item})
}
func (h *Export) List(c *gin.Context) {
p, size, err := page(c)
if err != nil {
Fail(c, err.Error())
return
}
start, startErr := parseTime(c.Query("startCreatedAt"))
end, endErr := parseTime(c.Query("endCreatedAt"))
if startErr != nil || endErr != nil {
Fail(c, "参数错误")
return
}
items, total, err := h.service.TemplatesFilter(c.Request.Context(), p, size, c.Query("name"), c.Query("tableName"), c.Query("templateID"), start, end)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: p, PageSize: size}, "获取成功")
}
func (h *Export) Preview(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
Fail(c, "模板ID不能为空")
return
}
params, _ := exportParams(c.Request.URL.Query())
sql, err := h.service.Preview(c.Request.Context(), templateID, params)
if err != nil {
Fail(c, "获取失败")
return
}
OKWithData(c, gin.H{"sql": sql})
}
func (h *Export) Issue(blank bool) gin.HandlerFunc {
return func(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
Fail(c, "模板ID不能为空")
return
}
params, err := exportIssueParams(c.Request.URL.Query(), blank)
if err != nil {
Fail(c, "解析 params 参数失败")
return
}
token, err := h.service.IssueToken(c.Request.Context(), templateID, params, blank)
if err != nil {
Fail(c, "导出令牌创建失败")
return
}
path := "/sysExportTemplate/exportExcelByToken?token=" + token
if blank {
path = "/sysExportTemplate/exportTemplateByToken?token=" + token
}
OKWithData(c, path)
}
}
func (h *Export) Import(c *gin.Context) {
templateID := c.Query("templateID")
if templateID == "" {
Fail(c, "模板ID不能为空")
return
}
file, err := c.FormFile("file")
if err != nil {
Fail(c, "文件获取失败")
return
}
opened, err := file.Open()
if err != nil {
Fail(c, "读取文件失败")
return
}
defer opened.Close()
if err = h.service.Import(c.Request.Context(), templateID, opened); err != nil {
Fail(c, err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "导入成功")
}
func (h *Export) Download(expectBlank bool) gin.HandlerFunc {
return func(c *gin.Context) {
token := c.Query("token")
if token == "" {
Fail(c, "导出token不能为空")
return
}
value, err := h.service.ConsumeToken(c.Request.Context(), token, expectBlank)
if err != nil {
switch {
case errors.Is(err, service.ErrExportTokenMalformed):
Fail(c, "解析导出参数失败")
case errors.Is(err, service.ErrExportTokenType):
Fail(c, "token类型错误")
default:
Fail(c, "导出token无效或已过期")
}
return
}
var data []byte
var name string
if expectBlank {
data, name, err = h.service.ExportBlankTemplate(c.Request.Context(), value.TemplateID)
} else {
data, name, err = h.service.Export(c.Request.Context(), value.TemplateID, value.Params)
}
if err != nil {
Fail(c, "获取失败")
return
}
if expectBlank {
name = strings.TrimSuffix(name, "_template.xlsx") + "模板.xlsx"
} else {
name = strings.TrimSuffix(name, ".xlsx") + strings.ReplaceAll(uuid.NewString(), "-", "")[:6] + ".xlsx"
}
c.Header("Content-Disposition", "attachment; filename="+name)
c.Header("success", "true")
c.Data(http.StatusOK, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", data)
}
}
+40
View File
@@ -0,0 +1,40 @@
package handler
import (
"net/url"
"testing"
)
func TestExportParamsRejectsMalformedNestedQuery(t *testing.T) {
_, err := exportParams(url.Values{"params": {"name=%zz"}})
if err == nil {
t.Fatal("exportParams accepted malformed nested query")
}
}
func TestExportParamsUsesFirstValue(t *testing.T) {
got, err := exportParams(url.Values{"params": {"name=first&name=second"}})
if err != nil {
t.Fatalf("exportParams returned error: %v", err)
}
if got["name"] != "first" {
t.Fatalf("exportParams name = %q, want first", got["name"])
}
}
func TestExportIssueParamsKeepsBlankTemplateCompatible(t *testing.T) {
got, err := exportIssueParams(url.Values{"params": {"name=%zz"}}, true)
if err != nil {
t.Fatalf("blank template params returned error: %v", err)
}
if len(got) != 0 {
t.Fatalf("blank template params = %#v, want empty", got)
}
}
func TestExportIssueParamsRejectsMalformedExcelParams(t *testing.T) {
_, err := exportIssueParams(url.Values{"params": {"name=%zz"}}, false)
if err == nil {
t.Fatal("Excel export accepted malformed nested query")
}
}
+25
View File
@@ -0,0 +1,25 @@
package handler
import (
"github.com/gin-gonic/gin"
"kra/internal/biz"
"kra/internal/server/httpx"
"kra/internal/server/middleware"
)
const (
CodeSuccess = httpx.CodeSuccess
CodeError = httpx.CodeError
CodePasswordChangeRequired = httpx.CodePasswordChangeRequired
)
type Response = httpx.Response
type PageResult = httpx.PageResult
func Write(c *gin.Context, code int, data any, message string) { httpx.Write(c, code, data, message) }
func OK(c *gin.Context) { httpx.OK(c) }
func OKWithData(c *gin.Context, data any) { httpx.OKWithData(c, data) }
func Fail(c *gin.Context, message string) { httpx.Fail(c, message) }
func NoAuth(c *gin.Context, message string) { httpx.NoAuth(c, message) }
func SetTokenCookie(c *gin.Context, value string, maxAge int) { httpx.SetTokenCookie(c, value, maxAge) }
func Claims(c *gin.Context) *biz.AuthClaims { return middleware.Claims(c) }
@@ -0,0 +1,54 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type IntegrationConfig struct {
service *service.IntegrationConfigService
}
func NewIntegrationConfig(service *service.IntegrationConfigService) *IntegrationConfig {
return &IntegrationConfig{service: service}
}
func (h *IntegrationConfig) List(c *gin.Context) {
configs, err := h.service.List(c.Request.Context(), c.Param("kind"))
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, configs)
}
func (h *IntegrationConfig) Find(c *gin.Context) {
config, err := h.service.Find(c.Request.Context(), c.Param("kind"), c.Param("provider"))
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, config)
}
func (h *IntegrationConfig) Save(c *gin.Context) {
var req service.IntegrationConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Save(c.Request.Context(), c.Param("kind"), c.Param("provider"), &req); err != nil {
Fail(c, err.Error())
return
}
OK(c)
}
func (h *IntegrationConfig) Delete(c *gin.Context) {
if err := h.service.Delete(c.Request.Context(), c.Param("kind"), c.Param("provider")); err != nil {
Fail(c, err.Error())
return
}
OK(c)
}
+288
View File
@@ -0,0 +1,288 @@
package handler
import (
"errors"
"kra/internal/service"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
)
type Media struct{ service *service.MediaService }
func NewMedia(service *service.MediaService) *Media { return &Media{service: service} }
func (h *Media) limitMultipartBody(c *gin.Context) int64 {
limit := h.service.MediaConfig().EffectiveMaxFileSize()
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, limit+(1<<20))
return limit
}
func rejectMediaTooLarge(c *gin.Context, err error, message string) bool {
var tooLarge *http.MaxBytesError
if !errors.As(err, &tooLarge) {
return false
}
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, Response{Code: CodeError, Data: gin.H{}, Msg: message})
return true
}
func (h *Media) Upload(c *gin.Context) {
claims := Claims(c)
if claims == nil {
Fail(c, "接收文件失败")
return
}
limit := h.limitMultipartBody(c)
header, err := c.FormFile("file")
if err != nil {
if rejectMediaTooLarge(c, err, "文件超过大小上限") {
return
}
Fail(c, "接收文件失败")
return
}
if header.Size > limit {
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, Response{Code: CodeError, Data: gin.H{}, Msg: "文件超过大小上限"})
return
}
opened, err := header.Open()
if err != nil {
// Let the upload service surface this as the generic upload
// failure; keep the endpoint message consistent.
Fail(c, "上传文件失败")
return
}
defer opened.Close()
category, _ := strconv.Atoi(c.DefaultPostForm("classId", "0"))
save := c.DefaultQuery("noSave", "0") == "0"
item, err := h.service.Upload(c.Request.Context(), claims.ID, header.Filename, header.Header.Get("Content-Type"), category, opened, save)
if err != nil {
Fail(c, "上传文件失败")
return
}
Write(c, CodeSuccess, gin.H{"file": item}, "上传成功")
}
func (h *Media) List(c *gin.Context) {
var req service.MediaListRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.service.MediaListRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Media) Delete(c *gin.Context) {
var req service.IDRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Delete(c.Request.Context(), req.ID); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Media) DeleteMany(c *gin.Context) {
var req service.MediaBatchDeleteRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
failed := []uint{}
for _, id := range req.IDs {
if h.service.Delete(c.Request.Context(), id) != nil {
failed = append(failed, id)
}
}
if len(failed) > 0 {
Write(c, CodeSuccess, gin.H{"failedIds": failed}, "部分文件删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}
func (h *Media) Find(c *gin.Context) {
id, parseErr := strconv.ParseUint(c.Query("id"), 10, 64)
if parseErr != nil {
Fail(c, "文件ID非法")
return
}
item, err := h.service.Media(c.Request.Context(), uint(id))
if err != nil {
Fail(c, "查询失败")
return
}
OKWithData(c, item)
}
func (h *Media) Rename(c *gin.Context) {
var req service.RenameMediaRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Rename(c.Request.Context(), req.ID, req.Name); err != nil {
Fail(c, "编辑失败")
return
}
Write(c, CodeSuccess, gin.H{}, "编辑成功")
}
func (h *Media) ImportURLs(c *gin.Context) {
var req []service.ImportMediaRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.ImportURLRequests(c.Request.Context(), req); err != nil {
Fail(c, "导入URL失败")
return
}
Write(c, CodeSuccess, gin.H{}, "导入URL成功")
}
func (h *Media) Storage(c *gin.Context) {
var req service.StorageListRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
items, next, more, err := h.service.Storage(c.Request.Context(), req.Prefix, req.Cursor, req.Limit)
if err != nil {
Fail(c, "列举存储桶文件失败")
return
}
Write(c, CodeSuccess, gin.H{"list": items, "nextCursor": next, "hasMore": more}, "获取成功")
}
func (h *Media) Categories(c *gin.Context) {
items, err := h.service.Categories(c.Request.Context())
if err != nil {
Fail(c, "获取分类列表失败")
return
}
OKWithData(c, items)
}
func (h *Media) SaveCategory(c *gin.Context) {
var req service.CategoryRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, "参数错误")
return
}
if err := h.service.SaveCategoryRequest(c.Request.Context(), &req); err != nil {
Fail(c, "创建/更新失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "创建/更新成功")
}
func (h *Media) DeleteCategory(c *gin.Context) {
var req service.IDRequest
if c.ShouldBindJSON(&req) != nil || req.ID == 0 {
Fail(c, "参数错误")
return
}
if err := h.service.DeleteCategory(c.Request.Context(), req.ID); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Media) InitUpload(c *gin.Context) {
claims := Claims(c)
var req service.InitUploadRequest
if claims == nil {
Fail(c, "参数错误")
return
}
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if config := h.service.MediaConfig(); config.MaxFileSize > 0 && req.FileSize > config.MaxFileSize {
Fail(c, "文件超过大小上限")
return
}
result, err := h.service.InitUpload(c.Request.Context(), claims.ID, req.FileName, req.FileHash, req.FileSize, req.ChunkSize, req.ChunkTotal)
if err != nil {
Fail(c, err.Error())
return
}
Write(c, CodeSuccess, result, "成功")
}
func (h *Media) SaveChunk(c *gin.Context) {
claims := Claims(c)
if claims == nil {
Fail(c, "接收分片失败")
return
}
limit := h.limitMultipartBody(c)
header, err := c.FormFile("chunk")
if err != nil {
if rejectMediaTooLarge(c, err, "分片超过大小上限") {
return
}
Fail(c, "接收分片失败")
return
}
if header.Size > limit {
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, Response{Code: CodeError, Data: gin.H{}, Msg: "分片超过大小上限"})
return
}
uploadID, parseErr := strconv.ParseUint(c.PostForm("uploadId"), 10, 64)
if parseErr != nil || uploadID == 0 {
Fail(c, "上传会话 ID 非法")
return
}
index, parseErr := strconv.Atoi(c.PostForm("chunkIndex"))
if parseErr != nil || index < 0 {
Fail(c, "分片序号非法")
return
}
opened, err := header.Open()
if err != nil {
Fail(c, "分片读取失败")
return
}
defer opened.Close()
if err = h.service.SaveChunk(c.Request.Context(), claims.ID, uint(uploadID), index, c.PostForm("chunkHash"), opened); err != nil {
Fail(c, err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "ok")
}
func (h *Media) CompleteUpload(c *gin.Context) {
claims := Claims(c)
var req service.CompleteUploadRequest
if claims == nil {
Fail(c, "参数错误")
return
}
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
item, err := h.service.CompleteUpload(c.Request.Context(), claims.ID, req.UploadID)
if err != nil {
Fail(c, err.Error())
return
}
Write(c, CodeSuccess, gin.H{"media": item}, "成功")
}
func (h *Media) CancelUpload(c *gin.Context) {
claims := Claims(c)
id, _ := strconv.ParseUint(c.Param("uploadId"), 10, 64)
if claims == nil {
Fail(c, "未登录")
return
}
if err := h.service.CancelUpload(c.Request.Context(), claims.ID, uint(id)); err != nil {
Fail(c, err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "已取消")
}
+211
View File
@@ -0,0 +1,211 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Menu struct{ service *service.MenuService }
func NewMenu(service *service.MenuService) *Menu { return &Menu{service: service} }
func (h *Menu) List(c *gin.Context) {
items, err := h.service.Tree(c.Request.Context())
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, items, "获取成功")
}
func (h *Menu) Tree(c *gin.Context) {
items, err := h.service.Tree(c.Request.Context())
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"menus": items}, "获取成功")
}
func (h *Menu) Create(c *gin.Context) {
var req service.MenuRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Path == "" {
Fail(c, "Path值不能为空")
return
}
if req.Name == "" {
Fail(c, "Name值不能为空")
return
}
if req.Component == "" {
Fail(c, "Component值不能为空")
return
}
if req.Sort < 0 {
Fail(c, "Sort长度或值不在合法范围,ge=0")
return
}
if req.Meta.Title == "" {
Fail(c, "Title值不能为空")
return
}
if err := h.service.Create(c.Request.Context(), &req); err != nil {
Fail(c, "添加失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "添加成功")
}
func (h *Menu) Update(c *gin.Context) {
var req service.MenuRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Path == "" {
Fail(c, "Path值不能为空")
return
}
if req.Name == "" {
Fail(c, "Name值不能为空")
return
}
if req.Component == "" {
Fail(c, "Component值不能为空")
return
}
if req.Sort < 0 {
Fail(c, "Sort长度或值不在合法范围,ge=0")
return
}
if req.Meta.Title == "" {
Fail(c, "Title值不能为空")
return
}
if err := h.service.Update(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败")
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Menu) Delete(c *gin.Context) {
var req service.DeleteMenuRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "ID值不能为空")
return
}
if err := h.service.Delete(c.Request.Context(), uint(req.ID)); err != nil {
Fail(c, "删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Menu) Find(c *gin.Context) {
var req service.GetMenuRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "ID值不能为空")
return
}
item, err := h.service.Find(c.Request.Context(), uint(req.ID))
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"menu": item}, "获取成功")
}
func (h *Menu) SetAuthorityMenus(c *gin.Context) {
var req service.SetAuthorityMenusRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
if err := h.service.SetAuthorityMenus(c.Request.Context(), &req); err != nil {
Fail(c, "添加失败")
return
}
Write(c, CodeSuccess, gin.H{}, "添加成功")
}
func (h *Menu) AuthorityMenus(c *gin.Context) {
var req service.GetAuthorityMenusRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
menus, err := h.service.AuthorityMenus(c.Request.Context(), req.AuthorityID)
if err != nil {
Write(c, CodeError, gin.H{"menus": menus}, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"menus": menus}, "获取成功")
}
func (h *Menu) RoleIDs(c *gin.Context) {
var req service.SetMenuRolesRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
if req.MenuID == 0 {
Fail(c, "菜单ID不能为空")
return
}
ids, err := h.service.RoleIDs(c.Request.Context(), req.MenuID)
if err != nil {
Fail(c, "获取失败"+err.Error())
return
}
if ids == nil {
ids = []uint{}
}
defaultIDs, err := h.service.DefaultRouterRoleIDs(c.Request.Context(), req.MenuID)
if err != nil {
Fail(c, "获取失败"+err.Error())
return
}
if defaultIDs == nil {
defaultIDs = []uint{}
}
Write(c, CodeSuccess, gin.H{"authorityIds": ids, "defaultRouterAuthorityIds": defaultIDs}, "获取成功")
}
func (h *Menu) SetRoles(c *gin.Context) {
var req service.SetMenuRolesRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.MenuID == 0 {
Fail(c, "菜单ID不能为空")
return
}
if err := h.service.SetRoles(c.Request.Context(), &req); err != nil {
Fail(c, "设置失败"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
+24
View File
@@ -0,0 +1,24 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Navigation struct{ service *service.UserService }
func NewNavigation(service *service.UserService) *Navigation { return &Navigation{service: service} }
func (h *Navigation) Menu(c *gin.Context) {
claims := Claims(c)
if claims == nil {
NoAuth(c, "未登录或非法访问")
return
}
menus, err := h.service.Menus(c.Request.Context(), claims.AuthorityID)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"menus": menus}, "获取成功")
}
+251
View File
@@ -0,0 +1,251 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Organization struct {
departments *service.DepartmentService
positions *service.PositionService
}
func NewOrganization(departments *service.DepartmentService, positions *service.PositionService) *Organization {
return &Organization{departments: departments, positions: positions}
}
func (h *Organization) ListDepartments(c *gin.Context) {
var req service.DepartmentListRequest
_ = c.ShouldBindJSON(&req)
items, err := h.departments.Departments(c.Request.Context(), req.Name)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
Write(c, CodeSuccess, items, "获取成功")
}
func (h *Organization) CreateDepartment(c *gin.Context) {
var req service.DepartmentRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Name == "" {
Fail(c, "部门名称不能为空")
return
}
if err := h.departments.Create(c.Request.Context(), &req); err != nil {
Fail(c, "创建失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "创建成功")
}
func (h *Organization) UpdateDepartment(c *gin.Context) {
var req service.DepartmentRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "部门ID不能为空")
return
}
if err := h.departments.Update(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Organization) DeleteDepartment(c *gin.Context) {
var req service.DeleteDepartmentRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.departments.Delete(c.Request.Context(), req.ID); err != nil {
Fail(c, "删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Organization) FindDepartment(c *gin.Context) {
var req service.DeleteDepartmentRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
item, err := h.departments.Department(c.Request.Context(), req.ID)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
Write(c, CodeSuccess, item, "获取成功")
}
func (h *Organization) DepartmentUsers(c *gin.Context) {
var req service.SetDepartmentUsersRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
ids, err := h.departments.UserIDs(c.Request.Context(), req.DepartmentID)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
if ids == nil {
ids = []uint{}
}
Write(c, CodeSuccess, ids, "获取成功")
}
func (h *Organization) SetDepartmentUsers(c *gin.Context) {
var req service.SetDepartmentUsersRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.DepartmentID == 0 {
Fail(c, "部门ID不能为空")
return
}
if err := h.departments.SetUsers(c.Request.Context(), &req); err != nil {
Fail(c, "设置失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *Organization) SetUserDepartments(c *gin.Context) {
var req service.SetUserDepartmentsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "用户ID不能为空")
return
}
if err := h.departments.SetUserDepartments(c.Request.Context(), &req); err != nil {
Fail(c, "设置失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *Organization) ListPositions(c *gin.Context) {
var req service.PositionListRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.positions.Positions(c.Request.Context(), &req)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Organization) CreatePosition(c *gin.Context) {
var req service.PositionRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Name == "" {
Fail(c, "岗位名称不能为空")
return
}
if err := h.positions.Create(c.Request.Context(), &req); err != nil {
Fail(c, "创建失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "创建成功")
}
func (h *Organization) UpdatePosition(c *gin.Context) {
var req service.PositionRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "岗位ID不能为空")
return
}
if err := h.positions.Update(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Organization) DeletePosition(c *gin.Context) {
var req service.DeletePositionRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.positions.Delete(c.Request.Context(), req.ID); err != nil {
Fail(c, "删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Organization) FindPosition(c *gin.Context) {
var req service.DeletePositionRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
item, err := h.positions.Position(c.Request.Context(), req.ID)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
Write(c, CodeSuccess, item, "获取成功")
}
func (h *Organization) PositionUsers(c *gin.Context) {
var req service.SetPositionUsersRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
ids, err := h.positions.UserIDs(c.Request.Context(), req.PositionID)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
if ids == nil {
ids = []uint{}
}
Write(c, CodeSuccess, ids, "获取成功")
}
func (h *Organization) SetPositionUsers(c *gin.Context) {
var req service.SetPositionUsersRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.PositionID == 0 {
Fail(c, "岗位ID不能为空")
return
}
if err := h.positions.SetUsers(c.Request.Context(), &req); err != nil {
Fail(c, "设置失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *Organization) SetUserPositions(c *gin.Context) {
var req service.SetUserPositionsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "用户ID不能为空")
return
}
if err := h.positions.SetUserPositions(c.Request.Context(), &req); err != nil {
Fail(c, "设置失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
+90
View File
@@ -0,0 +1,90 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Parameter struct {
service *service.ParameterService
}
func NewParameter(service *service.ParameterService) *Parameter {
return &Parameter{service: service}
}
func (h *Parameter) Create(c *gin.Context) {
var req service.SystemParameterRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.CreateParameterRequest(c.Request.Context(), &req); err != nil {
Fail(c, "创建失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "创建成功")
}
func (h *Parameter) Update(c *gin.Context) {
var req service.SystemParameterRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.UpdateParameterRequest(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Parameter) Delete(c *gin.Context) {
if err := h.service.DeleteParameters(c.Request.Context(), []string{c.Query("ID")}); err != nil {
Fail(c, "删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Parameter) DeleteMany(c *gin.Context) {
ids := c.QueryArray("IDs[]")
if err := h.service.DeleteParameters(c.Request.Context(), ids); err != nil {
Fail(c, "批量删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}
func (h *Parameter) Find(c *gin.Context) {
item, err := h.service.ParameterByID(c.Request.Context(), c.Query("ID"))
if err != nil {
Fail(c, "查询失败:"+err.Error())
return
}
OKWithData(c, item)
}
func (h *Parameter) Get(c *gin.Context) {
item, err := h.service.ParameterByKey(c.Request.Context(), c.Query("key"))
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
Write(c, CodeSuccess, item, "获取成功")
}
func (h *Parameter) List(c *gin.Context) {
var req service.SystemParameterSearchRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.service.ParametersFilter(c.Request.Context(), req.Page, req.PageSize, req.Name, req.Key, req.StartCreatedAt, req.EndCreatedAt)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
+168
View File
@@ -0,0 +1,168 @@
package handler
import (
"io"
"kra/internal/service"
"net/http"
"github.com/gin-gonic/gin"
)
type Payment struct{ service *service.PaymentService }
func NewPayment(service *service.PaymentService) *Payment { return &Payment{service: service} }
func (h *Payment) Order(c *gin.Context) {
var req service.PaymentQueryRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
order, err := h.service.Order(c.Request.Context(), req.Provider, req.TradeNo)
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, order)
}
func (h *Payment) OrderByPath(c *gin.Context) {
order, err := h.service.Order(c.Request.Context(), c.Param("provider"), c.Param("tradeNo"))
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, order)
}
func (h *Payment) RefundByPath(c *gin.Context) {
var req struct {
Amount int64 `json:"amount"`
}
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
result, err := h.service.Refund(c.Request.Context(), &service.PaymentRefundRequest{
Provider: c.Param("provider"), TradeNo: c.Param("tradeNo"), Amount: req.Amount,
})
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, result)
}
func (h *Payment) FulfillByPath(c *gin.Context) {
result, err := h.service.Fulfill(c.Request.Context(), &service.PaymentFulfillRequest{
Provider: c.Param("provider"), TradeNo: c.Param("tradeNo"),
})
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, result)
}
func (h *Payment) Orders(c *gin.Context) {
var req service.PaymentOrderListRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, page, pageSize, err := h.service.Orders(c.Request.Context(), &req)
if err != nil {
Fail(c, err.Error())
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: page, PageSize: pageSize}, "获取成功")
}
func (h *Payment) Create(c *gin.Context) {
var req service.PaymentRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
req.ClientIP = c.ClientIP()
result, err := h.service.Create(c.Request.Context(), &req)
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, result)
}
func (h *Payment) Query(c *gin.Context) {
var req service.PaymentQueryRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
result, err := h.service.Query(c.Request.Context(), &req)
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, result)
}
func (h *Payment) Refund(c *gin.Context) {
var req service.PaymentRefundRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
result, err := h.service.Refund(c.Request.Context(), &req)
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, result)
}
func (h *Payment) Fulfill(c *gin.Context) {
var req service.PaymentFulfillRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
result, err := h.service.Fulfill(c.Request.Context(), &req)
if err != nil {
Fail(c, err.Error())
return
}
OKWithData(c, result)
}
func (h *Payment) Callback(c *gin.Context) {
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 8<<20)
raw, err := io.ReadAll(c.Request.Body)
if err != nil {
writePaymentCallbackAck(c, service.DefaultPaymentCallbackAck(c.Param("provider"), false))
return
}
headers := map[string]string{}
for k, v := range c.Request.Header {
if len(v) > 0 {
headers[k] = v[0]
}
}
query := map[string]string{}
for k, v := range c.Request.URL.Query() {
if len(v) > 0 {
query[k] = v[0]
}
}
ack, err := h.service.Callback(c.Request.Context(), &service.PaymentCallbackRequest{Provider: c.Param("provider"), Headers: headers, Body: raw, Query: query})
writePaymentCallbackAck(c, ack)
_ = err
}
func writePaymentCallbackAck(c *gin.Context, ack service.PaymentCallbackAck) {
if ack.StatusCode == 0 {
ack.StatusCode = http.StatusInternalServerError
}
if ack.ContentType != "" {
c.Header("Content-Type", ack.ContentType)
}
c.Status(ack.StatusCode)
if len(ack.Body) > 0 {
_, _ = c.Writer.Write(ack.Body)
}
}
+29
View File
@@ -0,0 +1,29 @@
package handler
import (
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"kra/internal/service"
)
func TestWritePaymentCallbackAckDoesNotExposeError(t *testing.T) {
gin.SetMode(gin.TestMode)
recorder := httptest.NewRecorder()
context, _ := gin.CreateTestContext(recorder)
writePaymentCallbackAck(context, service.PaymentCallbackAck{
StatusCode: 500,
ContentType: "text/plain; charset=utf-8",
Body: []byte("failure"),
})
if recorder.Code != 500 {
t.Fatalf("status = %d, want 500", recorder.Code)
}
if recorder.Body.String() != "failure" {
t.Fatalf("body = %q, want fixed failure ack", recorder.Body.String())
}
if recorder.Body.String() == "internal order resolver details" {
t.Fatal("internal error was exposed in callback response")
}
}
+53
View File
@@ -0,0 +1,53 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Permission struct{ service *service.PermissionService }
func NewPermission(service *service.PermissionService) *Permission {
return &Permission{service: service}
}
func (h *Permission) Buttons(c *gin.Context) {
var req service.GetAuthorityButtonsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
selected, err := h.service.SelectedButtons(c.Request.Context(), req.AuthorityID, req.MenuID)
if err != nil {
Fail(c, "查询失败")
return
}
Write(c, CodeSuccess, gin.H{"selected": selected}, "查询成功")
}
func (h *Permission) SetButtons(c *gin.Context) {
var req service.SetAuthorityButtonsRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.SetSelectedButtons(c.Request.Context(), req.AuthorityID, req.MenuID, req.Selected); err != nil {
Fail(c, "分配失败")
return
}
Write(c, CodeSuccess, gin.H{}, "分配成功")
}
func (h *Permission) CanRemoveButton(c *gin.Context) {
allowed, err := h.service.CanRemoveButton(c.Request.Context(), c.Query("id"))
if err != nil {
Fail(c, err.Error())
return
}
if !allowed {
Fail(c, "此按钮正在被使用无法删除")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
+12
View File
@@ -0,0 +1,12 @@
package handler
import "github.com/google/wire"
// ProviderSet wires the system HTTP handlers.
var ProviderSet = wire.NewSet(
NewAuthority, NewMenu, NewAPI, NewPermission, NewOrganization,
NewAnnouncement, NewEmail, NewPayment, NewTask, NewMedia, NewAudit,
NewExport, NewVersion, NewDictionary, NewParameter, NewAPIToken,
NewSystemConfig, NewPublic, NewUser, NewNavigation, NewSession, NewSet,
NewIntegrationConfig,
)
+167
View File
@@ -0,0 +1,167 @@
package handler
import (
"context"
"errors"
"strconv"
"strings"
"time"
"kra/internal/biz"
"kra/internal/service"
"github.com/gin-gonic/gin"
"github.com/mojocn/base64Captcha"
)
type Public struct {
auth *service.AuthService
system *service.SystemConfigService
settings *service.SecurityService
store *captchaStore
}
func NewPublic(auth *service.AuthService, system *service.SystemConfigService, settings *service.SecurityService) *Public {
return &Public{auth: auth, system: system, settings: settings, store: &captchaStore{service: settings}}
}
func (h *Public) captchaConfig() (int, int, int) {
config := h.settings.CaptchaSettings()
return config.KeyLong, config.ImageWidth, config.ImageHeight
}
func (h *Public) Captcha(c *gin.Context) {
keyLong, width, height := h.captchaConfig()
security, err := h.settings.CurrentSecurity(c.Request.Context())
if err != nil || security == nil {
Fail(c, "安全服务暂不可用")
return
}
openCaptcha := true
if security != nil {
keyLong, width, height = security.KeyLong, security.ImgWidth, security.ImgHeight
ttl := time.Duration(security.CaptchaTimeout) * time.Second
failures, counterErr := h.settings.EnsureLoginIPCounter(c.Request.Context(), c.ClientIP(), ttl)
if counterErr != nil {
Fail(c, "安全服务暂不可用")
return
}
openCaptcha = security.CaptchaOpen == 0 || failures > security.CaptchaOpen
}
driver := base64Captcha.NewDriverDigit(height, width, keyLong, 0.7, 80)
id, picture, _, err := base64Captcha.NewCaptcha(driver, h.store).Generate()
if err != nil {
Fail(c, "验证码获取失败")
return
}
Write(c, CodeSuccess, gin.H{"captchaId": id, "picPath": picture, "captchaLength": keyLong, "openCaptcha": openCaptcha}, "验证码获取成功")
}
func (h *Public) Login(c *gin.Context) {
var req service.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Username == "" {
Fail(c, "Username值不能为空")
return
}
if req.Password == "" {
Fail(c, "Password值不能为空")
return
}
result, err := h.auth.Login(c.Request.Context(), &req, c.ClientIP(), c.Request.UserAgent())
if err != nil {
switch {
case errors.Is(err, biz.ErrAccountLocked):
var locked *biz.AccountLockedError
errors.As(err, &locked)
minutes := 0
if locked != nil {
minutes = locked.Minutes
}
Fail(c, "账号已锁定,请 "+strconv.Itoa(minutes)+" 分钟后再试")
case errors.Is(err, biz.ErrCaptchaInvalid):
Fail(c, "验证码错误")
case errors.Is(err, biz.ErrUserDisabled):
Fail(c, "用户被禁止登录")
case errors.Is(err, biz.ErrTokenIssue):
Fail(c, "获取token失败")
case errors.Is(err, biz.ErrLoginState):
Fail(c, "设置登录状态失败")
case errors.Is(err, biz.ErrTokenRevoke):
Fail(c, "jwt作废失败")
default:
Fail(c, "用户名不存在或者密码错误")
}
return
}
maxAge := int(time.UnixMilli(result.ExpiresAt).Unix() - time.Now().Unix())
SetTokenCookie(c, result.Token, maxAge)
Write(c, CodeSuccess, result, "登录成功")
}
func (h *Public) CheckDatabase(c *gin.Context) {
initialized, err := h.system.IsInitialized(c.Request.Context())
if err != nil {
Fail(c, "数据库状态检查失败")
return
}
message := "数据库无需初始化"
if !initialized {
message = "前往初始化数据库"
}
Write(c, CodeSuccess, gin.H{"needInit": !initialized}, message)
}
func (h *Public) InitializeDatabase(engine *gin.Engine) gin.HandlerFunc {
return func(c *gin.Context) {
initialized, err := h.system.IsInitialized(c.Request.Context())
if err != nil {
Fail(c, "数据库状态检查失败")
return
}
if initialized {
Fail(c, "已存在数据库配置")
return
}
var input service.DatabaseInitRequest
if c.ShouldBindJSON(&input) != nil {
Fail(c, "参数校验不通过")
return
}
routes := engine.Routes()
values := make([]service.Route, 0, len(routes))
for _, route := range routes {
values = append(values, service.Route{Path: route.Path, Method: route.Method})
}
if err := h.system.InitializeRoutes(c.Request.Context(), &input, values); err != nil {
Fail(c, "自动创建数据库失败,请查看后台日志,检查后在进行初始化")
return
}
Write(c, CodeSuccess, gin.H{}, "自动创建数据库成功")
}
}
type captchaStore struct {
service *service.SecurityService
}
func (s *captchaStore) Set(id, value string) error {
expiration := s.service.CaptchaSettings().StoreExpiration
return s.service.SetCaptcha(context.Background(), id, value, expiration)
}
func (s *captchaStore) Get(id string, clear bool) string {
value, ok, err := s.service.GetCaptcha(context.Background(), id)
if err != nil || !ok {
return ""
}
if clear {
_ = s.service.DeleteCaptcha(context.Background(), id)
}
return value
}
func (s *captchaStore) Verify(id, answer string, clear bool) bool {
return id != "" && answer != "" && strings.EqualFold(s.Get(id, clear), answer)
}
+38
View File
@@ -0,0 +1,38 @@
package handler
import (
"strconv"
"github.com/gin-gonic/gin"
)
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 IDsFromQuery(c *gin.Context) []uint {
values := c.QueryArray("IDs[]")
ids := make([]uint, 0, len(values))
for _, value := range values {
id, _ := strconv.ParseUint(value, 10, 64)
if id > 0 {
ids = append(ids, uint(id))
}
}
return ids
}
+23
View File
@@ -0,0 +1,23 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Session struct{ tokens *service.TokenService }
func NewSession(tokens *service.TokenService) *Session { return &Session{tokens: tokens} }
func (h *Session) Logout(c *gin.Context) {
token := c.GetHeader("x-token")
if token == "" {
token, _ = c.Cookie("x-token")
}
if err := h.tokens.BlacklistToken(c.Request.Context(), token); err != nil {
Fail(c, "jwt作废失败")
return
}
SetTokenCookie(c, "", -1)
Write(c, CodeSuccess, gin.H{}, "jwt作废成功")
}
+30
View File
@@ -0,0 +1,30 @@
package handler
type Set struct {
Authority *Authority
Menu *Menu
API *API
Permission *Permission
Organization *Organization
Announcement *Announcement
Email *Email
Payment *Payment
Task *Task
Media *Media
Audit *Audit
Export *Export
Version *Version
Dictionary *Dictionary
Parameter *Parameter
APIToken *APIToken
SystemConfig *SystemConfig
Public *Public
User *User
Navigation *Navigation
Session *Session
IntegrationConfig *IntegrationConfig
}
func NewSet(authority *Authority, menu *Menu, api *API, permission *Permission, organization *Organization, announcement *Announcement, email *Email, payment *Payment, task *Task, media *Media, audit *Audit, export *Export, version *Version, dictionary *Dictionary, parameter *Parameter, apiToken *APIToken, systemConfig *SystemConfig, public *Public, user *User, navigation *Navigation, session *Session, integrationConfig *IntegrationConfig) *Set {
return &Set{Authority: authority, Menu: menu, API: api, Permission: permission, Organization: organization, Announcement: announcement, Email: email, Payment: payment, Task: task, Media: media, Audit: audit, Export: export, Version: version, Dictionary: dictionary, Parameter: parameter, APIToken: apiToken, SystemConfig: systemConfig, Public: public, User: user, Navigation: navigation, Session: session, IntegrationConfig: integrationConfig}
}
+78
View File
@@ -0,0 +1,78 @@
package handler
import (
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type SystemConfig struct {
system *service.SystemConfigService
settings *service.SecurityService
}
func NewSystemConfig(system *service.SystemConfigService, settings *service.SecurityService) *SystemConfig {
return &SystemConfig{system: system, settings: settings}
}
func (h *SystemConfig) GetSecurity(c *gin.Context) {
value, err := h.settings.Security(c.Request.Context())
if err != nil {
Fail(c, "获取安全配置失败")
return
}
Write(c, CodeSuccess, value, "获取成功")
}
func (h *SystemConfig) SetSecurity(c *gin.Context) {
var req service.SecurityConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
result, err := h.settings.SaveSecurityRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "设置安全配置失败")
return
}
Write(c, CodeSuccess, result, "设置成功")
}
func (h *SystemConfig) Get(c *gin.Context) {
value, err := h.system.SystemConfig()
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, value, "获取成功")
}
func (h *SystemConfig) Set(c *gin.Context) {
var req service.SetSystemConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.system.SaveSystemConfig(c.Request.Context(), &req); err != nil {
Fail(c, "设置失败")
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *SystemConfig) Reload(c *gin.Context) {
if err := h.system.ReloadConfig(c.Request.Context()); err != nil {
Fail(c, "重载系统失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "重载系统成功")
}
func (h *SystemConfig) ServerInfo(c *gin.Context) {
server, err := h.system.ServerInfo()
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"server": server}, "获取成功")
}
+158
View File
@@ -0,0 +1,158 @@
package handler
import (
"fmt"
"kra/internal/service"
"net/http"
"time"
"github.com/gin-gonic/gin"
)
type Task struct {
service *service.TaskService
}
func NewTask(service *service.TaskService) *Task {
return &Task{service: service}
}
func (h *Task) Create(c *gin.Context) {
var req service.TaskRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
_, err := h.service.CreateRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "创建失败: "+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "创建成功")
}
func (h *Task) Update(c *gin.Context) {
var req service.TaskRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.UpdateRequest(c.Request.Context(), &req); err != nil {
Fail(c, "更新失败: "+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "更新成功")
}
func (h *Task) Delete(c *gin.Context) {
var req service.DeleteTaskRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Delete(c.Request.Context(), req.ID); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Task) Toggle(c *gin.Context) {
var req service.ToggleTaskRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Toggle(c.Request.Context(), req.ID, req.Enabled); err != nil {
Fail(c, "操作失败: "+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "操作成功")
}
func (h *Task) Trigger(c *gin.Context) {
var req service.DeleteTaskRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Trigger(c.Request.Context(), req.ID); err != nil {
Fail(c, "触发失败: "+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "已触发, 执行结果见执行日志")
}
func (h *Task) List(c *gin.Context) {
var req service.TaskListRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.service.ListRequest(c.Request.Context(), req.Page, req.PageSize, req.Name, req.ExecutorType, req.Enabled)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Task) Logs(c *gin.Context) {
var req service.TaskLogListRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
items, total, err := h.service.Logs(c.Request.Context(), req.Page, req.PageSize, req.TaskID, req.Status)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Task) Methods(c *gin.Context) {
Write(c, CodeSuccess, gin.H{"methods": h.service.RegisteredMethods()}, "获取成功")
}
func (h *Task) AlertStream(c *gin.Context) {
claims := Claims(c)
if claims == nil || claims.ID == 0 {
Fail(c, "未获取到用户身份")
return
}
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")
flusher, ok := c.Writer.(http.Flusher)
if !ok {
Fail(c, "SSE 不受支持")
return
}
// Remove the normal server deadline for long-lived SSE connections.
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)
defer ticker.Stop()
_, _ = fmt.Fprint(c.Writer, ": connected\n\n")
flusher.Flush()
for {
select {
case event, open := <-events:
if !open {
return
}
_, _ = fmt.Fprintf(c.Writer, "event: timedTask:alert\ndata: %s\n\n", event)
flusher.Flush()
case <-ticker.C:
_, _ = fmt.Fprint(c.Writer, ": ping\n\n")
flusher.Flush()
case <-c.Request.Context().Done():
return
}
}
}
+253
View File
@@ -0,0 +1,253 @@
package handler
import (
"kra/internal/service"
"strconv"
"time"
"github.com/gin-gonic/gin"
)
type User struct {
service *service.UserService
auth *service.AuthService
}
func NewUser(user *service.UserService, auth *service.AuthService) *User {
return &User{service: user, auth: auth}
}
func (h *User) List(c *gin.Context) {
var req service.UserListRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Page == 0 {
Fail(c, "Page值不能为空")
return
}
if req.PageSize == 0 {
Fail(c, "PageSize值不能为空")
return
}
items, total, err := h.service.ListUsersRequest(c.Request.Context(), &req)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *User) Create(c *gin.Context) {
var req service.UserRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Username == "" {
Fail(c, "Username值不能为空")
return
}
if req.Password == "" {
Fail(c, "Password值不能为空")
return
}
if req.NickName == "" {
Fail(c, "NickName值不能为空")
return
}
if req.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
result, err := h.service.CreateUserRequest(c.Request.Context(), &req)
if err != nil {
if service.IsPasswordPolicyError(err) {
Fail(c, err.Error())
return
}
if result == nil {
result = &service.UserResponse{}
}
Write(c, CodeError, gin.H{"user": result}, "注册失败")
return
}
Write(c, CodeSuccess, gin.H{"user": result}, "注册成功")
}
func (h *User) Update(c *gin.Context) {
var req service.UserRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "ID值不能为空")
return
}
if err := h.service.UpdateUserRequest(c.Request.Context(), &req); err != nil {
Fail(c, "修改失败")
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *User) UpdateSelf(c *gin.Context) {
claims := Claims(c)
if claims == nil {
NoAuth(c, "未登录或非法访问")
return
}
var req service.SelfUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.UpdateSelfUserRequest(c.Request.Context(), claims.ID, &req); err != nil {
Fail(c, "修改失败")
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *User) Delete(c *gin.Context) {
var req service.IDRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.ID == 0 {
Fail(c, "ID值不能为空")
return
}
claims := Claims(c)
if claims != nil && claims.ID == req.ID {
Fail(c, "删除失败, 无法删除自己。")
return
}
if err := h.service.DeleteUser(c.Request.Context(), req.ID); err != nil {
Fail(c, "删除失败")
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *User) ResetPassword(c *gin.Context) {
var req service.ResetPasswordRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.ResetPassword(c.Request.Context(), req.ID, req.Password); err != nil {
if service.IsPasswordPolicyError(err) {
Fail(c, err.Error())
return
}
Fail(c, "重置失败"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "重置成功")
}
func (h *User) ChangePassword(c *gin.Context) {
claims := Claims(c)
var req service.ChangePasswordRequest
if claims == nil {
NoAuth(c, "未登录或非法访问")
return
}
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.Password == "" {
Fail(c, "Password值不能为空")
return
}
if req.NewPassword == "" {
Fail(c, "NewPassword值不能为空")
return
}
if err := h.service.ChangePassword(c.Request.Context(), claims.ID, req.Password, req.NewPassword); err != nil {
if service.IsPasswordPolicyError(err) {
Fail(c, err.Error())
return
}
Fail(c, "修改失败,原密码与当前账户不符")
return
}
Write(c, CodeSuccess, gin.H{}, "修改成功")
}
func (h *User) SetSelfSetting(c *gin.Context) {
claims := Claims(c)
var setting map[string]any
if claims == nil {
NoAuth(c, "未登录或非法访问")
return
}
if err := c.ShouldBindJSON(&setting); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.SetUserSetting(c.Request.Context(), claims.ID, setting); err != nil {
Fail(c, "设置失败")
return
}
Write(c, CodeSuccess, gin.H{}, "设置成功")
}
func (h *User) SetAuthorities(c *gin.Context) {
var req service.UserAuthoritiesRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.SetUserAuthorities(c.Request.Context(), req.ID, req.AuthorityIDs); err != nil {
Fail(c, "修改失败")
return
}
Write(c, CodeSuccess, gin.H{}, "修改成功")
}
func (h *User) SwitchAuthority(c *gin.Context) {
claims := Claims(c)
var req service.SwitchAuthorityRequest
if claims == nil {
NoAuth(c, "未登录或非法访问")
return
}
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if req.AuthorityID == 0 {
Fail(c, "AuthorityId值不能为空")
return
}
login, err := h.auth.SwitchAuthority(c.Request.Context(), claims, req.AuthorityID)
if err != nil {
Fail(c, err.Error())
return
}
c.Header("new-token", login.Token)
c.Header("new-expires-at", strconv.FormatInt(login.ExpiresAt/1000, 10))
maxAge := int(time.UnixMilli(login.ExpiresAt).Unix() - time.Now().Unix())
SetTokenCookie(c, login.Token, maxAge)
Write(c, CodeSuccess, gin.H{}, "修改成功")
}
func (h *User) Get(c *gin.Context) {
claims := Claims(c)
if claims == nil {
NoAuth(c, "未登录或非法访问")
return
}
result, err := h.service.UserByUUID(c.Request.Context(), claims.UUID)
if err != nil {
Fail(c, "获取失败")
return
}
Write(c, CodeSuccess, gin.H{"userInfo": result}, "获取成功")
}
+150
View File
@@ -0,0 +1,150 @@
package handler
import (
"errors"
"fmt"
"net/http"
"strconv"
"time"
"kra/internal/biz"
"kra/internal/service"
"github.com/gin-gonic/gin"
)
type Version struct{ service *service.VersionService }
func NewVersion(service *service.VersionService) *Version { return &Version{service: service} }
func versionStage(err error) (biz.VersionStage, bool) {
var stageErr *biz.VersionStageError
if !errors.As(err, &stageErr) {
return "", false
}
return stageErr.Stage, true
}
func (h *Version) Delete(c *gin.Context) {
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
if err := h.service.DeleteVersions(c.Request.Context(), []uint{uint(id)}); err != nil {
Fail(c, "删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "删除成功")
}
func (h *Version) DeleteMany(c *gin.Context) {
raw := c.QueryArray("IDs[]")
ids := make([]uint, 0, len(raw))
for _, value := range raw {
id, _ := strconv.ParseUint(value, 10, 64)
ids = append(ids, uint(id))
}
if err := h.service.DeleteVersions(c.Request.Context(), ids); err != nil {
Fail(c, "批量删除失败:"+err.Error())
return
}
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
}
func (h *Version) Find(c *gin.Context) {
id, _ := strconv.ParseUint(c.Query("ID"), 10, 64)
item, err := h.service.Version(c.Request.Context(), uint(id))
if err != nil {
Fail(c, "查询失败:"+err.Error())
return
}
OKWithData(c, item)
}
func (h *Version) List(c *gin.Context) {
var req service.VersionSearchRequest
if err := c.ShouldBindQuery(&req); err != nil {
Fail(c, err.Error())
return
}
name, code := "", ""
if req.VersionName != nil {
name = *req.VersionName
}
if req.VersionCode != nil {
code = *req.VersionCode
}
createdAtRange := make([]*time.Time, 0, len(req.CreatedAtRange))
for i := range req.CreatedAtRange {
createdAtRange = append(createdAtRange, &req.CreatedAtRange[i])
}
items, total, err := h.service.Versions(c.Request.Context(), req.Page, req.PageSize, name, code, createdAtRange)
if err != nil {
Fail(c, "获取失败:"+err.Error())
return
}
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
}
func (h *Version) Export(c *gin.Context) {
var req service.ExportVersionRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, err.Error())
return
}
if err := h.service.Export(c.Request.Context(), req.VersionName, req.VersionCode, req.Description, req.MenuIDs, req.APIIDs, req.DictIDs); err != nil {
switch stage, _ := versionStage(err); stage {
case biz.VersionStageMenus:
Fail(c, "获取菜单数据失败:"+err.Error())
case biz.VersionStageAPIs:
Fail(c, "获取API数据失败:"+err.Error())
case biz.VersionStageDictionaries:
Fail(c, "获取字典数据失败:"+err.Error())
case biz.VersionStageJSON:
Fail(c, "JSON序列化失败:"+err.Error())
case biz.VersionStageSave:
Fail(c, "保存版本记录失败:"+err.Error())
default:
Fail(c, "创建发版失败:"+err.Error())
}
return
}
Write(c, CodeSuccess, gin.H{}, "创建发版成功")
}
func (h *Version) Download(c *gin.Context) {
rawID := c.Query("ID")
if rawID == "" {
Fail(c, "版本ID不能为空")
return
}
id, _ := strconv.ParseUint(rawID, 10, 64)
raw, code, err := h.service.VersionData(c.Request.Context(), uint(id))
if err != nil {
Fail(c, "获取版本记录失败:"+err.Error())
return
}
name := fmt.Sprintf("version_%s_%s.json", code, time.Now().Format("20060102150405"))
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", []byte(raw))
}
func (h *Version) Import(c *gin.Context) {
var req service.ImportVersionRequest
if err := c.ShouldBindJSON(&req); err != nil {
Fail(c, "解析JSON数据失败:"+err.Error())
return
}
if err := h.service.ImportRequest(c.Request.Context(), &req); err != nil {
if errors.Is(err, biz.ErrInvalidVersion) {
Fail(c, "版本信息格式错误")
return
}
switch stage, _ := versionStage(err); stage {
case biz.VersionStageMenus:
Fail(c, "导入菜单失败: "+err.Error())
case biz.VersionStageAPIs:
Fail(c, "导入API失败: "+err.Error())
case biz.VersionStageDictionaries:
Fail(c, "导入字典失败: "+err.Error())
default:
Fail(c, "导入失败:"+err.Error())
}
return
}
Write(c, CodeSuccess, gin.H{}, "导入成功")
}