优化结构
This commit is contained in:
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,35 @@
|
||||
server:
|
||||
http:
|
||||
network: tcp
|
||||
addr: 127.0.0.1:18001
|
||||
timeout: 60s
|
||||
data:
|
||||
database:
|
||||
driver: sqlite
|
||||
path: .tmp/init-check-data
|
||||
name: kra-init
|
||||
source: .tmp\init-check-data\kra-init.db
|
||||
prefix: ""
|
||||
log_mode: silent
|
||||
config: ""
|
||||
host: ""
|
||||
password: ""
|
||||
port: ""
|
||||
user: ""
|
||||
redis:
|
||||
addr: ""
|
||||
admin:
|
||||
router_prefix: ""
|
||||
jwt:
|
||||
signing_key: 7bd3e9cb-e201-4982-9af9-2ea209a80179
|
||||
expires_time: 3600s
|
||||
buffer_time: 300s
|
||||
issuer: kra
|
||||
system:
|
||||
disable_auto_migrate: false
|
||||
local:
|
||||
store_path: .tmp/init-check-data/uploads
|
||||
path_prefix: uploads/file
|
||||
app:
|
||||
app_id: kra
|
||||
env: test
|
||||
Binary file not shown.
@@ -0,0 +1,30 @@
|
||||
server:
|
||||
http:
|
||||
network: tcp
|
||||
addr: 127.0.0.1:18000
|
||||
timeout: 60s
|
||||
data:
|
||||
database:
|
||||
driver: sqlite
|
||||
path: .tmp/init-smoke
|
||||
name: fresh
|
||||
source: ""
|
||||
prefix: ""
|
||||
log_mode: silent
|
||||
redis:
|
||||
addr: ""
|
||||
admin:
|
||||
router_prefix: ""
|
||||
jwt:
|
||||
signing_key: init-test-secret
|
||||
expires_time: 3600s
|
||||
buffer_time: 300s
|
||||
issuer: kra
|
||||
system:
|
||||
disable_auto_migrate: false
|
||||
local:
|
||||
store_path: .tmp/init-smoke/uploads
|
||||
path_prefix: uploads/file
|
||||
app:
|
||||
app_id: kra
|
||||
env: test
|
||||
+23
-9
@@ -1,21 +1,35 @@
|
||||
# System Module
|
||||
|
||||
系统模块承载当前管理后台的完整业务边界:
|
||||
系统模块承载当前管理后台的完整业务边界。`internal` 顶层只保留有明确
|
||||
生命周期或分层职责的包:
|
||||
|
||||
- `app`:组合根、system 模块定义和任务/路由运行时组合
|
||||
- `biz`:系统领域对象、用例和仓储接口
|
||||
- `conf`:配置 proto 与运行时配置解析
|
||||
- `data`:数据库生命周期、系统仓储、系统表和支付持久化
|
||||
- `service`:请求 DTO 与领域用例的转换
|
||||
- `transport`:Gin server、middleware、handler、router、HTTP 响应工具
|
||||
- `integration`:Redis、邮件、对象存储和支付渠道适配器
|
||||
- `initialize`:数据库首次初始化和系统种子数据编排
|
||||
- `worker`:定时任务执行与调度
|
||||
- `integration`:Redis、邮件、对象存储、支付、WebSocket 和 EMQX 适配器
|
||||
- `security`:后台 JWT 等安全实现
|
||||
- `utils`:只服务于系统模块的无状态工具
|
||||
- `server`:Gin server 组合与生命周期;横切 HTTP 代码按子包维护:
|
||||
`server/handler`、`server/middleware`、`server/router`、`server/httpx`
|
||||
- `service`:应用服务、DTO 与领域对象转换和路由元数据;DTO 集中在
|
||||
`service/dto`,根包中的 `dto_aliases.go` 只负责兼容旧调用方
|
||||
- `worker`:定时任务执行与调度
|
||||
|
||||
目录代表边界,模块文件按资源命名。DTO、handler、中间件、路由和 HTTP
|
||||
响应工具分别放在独立子包中,避免 `service`/`server` 根目录堆积几十个
|
||||
文件,同时不把只有一两个文件的业务逻辑再拆成新包。单文件的 module
|
||||
定义并入 `app`,JWT 实现集中在 `security`,protobuf JSON 统一使用
|
||||
`pkg/protoutil`。
|
||||
|
||||
`internal/app` 只有 `catalog.go` 和 `definition.go` 是有意保留的组合根:
|
||||
前者组装模块 catalog、任务注册和运行时,后者声明 system 的迁移、管理面
|
||||
和定时任务。它们不是可以下沉到 `service` 或 `data` 的业务文件。
|
||||
|
||||
系统表统一使用 `sys_` 前缀;业务表应由新业务模块自行命名和迁移,不要混入
|
||||
本目录。
|
||||
|
||||
system 通过 `Definition()` 提供自己的迁移、支付菜单/API 和默认任务,通过
|
||||
`worker.TaskMethods` 提供依赖系统用例的任务实现,通过 `transport/server.Routes`
|
||||
提供路由。应用组合根消费这些公共协议;新增业务不需要修改 system 的初始化、
|
||||
system 通过 `Definition()` 提供迁移、支付菜单/API 和默认任务,通过
|
||||
`worker.TaskMethods` 提供依赖系统用例的任务实现,通过 `server/router.Routes` 提供
|
||||
路由。应用组合根消费这些公共协议;新增业务不需要修改 system 的初始化、
|
||||
worker、路由或数据层。
|
||||
|
||||
@@ -10,10 +10,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"kra/app/system/internal/conf"
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/worker"
|
||||
"kra/pkg/logging"
|
||||
"kra/pkg/mq"
|
||||
|
||||
"github.com/go-kratos/kratos/v3"
|
||||
"github.com/go-kratos/kratos/v3/config"
|
||||
@@ -40,10 +40,10 @@ func init() {
|
||||
flag.StringVar(&flagconf, "conf", "../configs", "config path, eg: -conf config.yaml")
|
||||
}
|
||||
|
||||
func newApp(logger *slog.Logger, hs *kratoshttp.Server, scheduler *worker.TaskScheduler, audit *service.AuditRecorder, loggerControl *logging.ReloadableLogger) *kratos.App {
|
||||
func newApp(logger *slog.Logger, hs *kratoshttp.Server, scheduler *worker.TaskScheduler, audit *service.AuditRecorder, loggerControl *logging.ReloadableLogger, _ mq.Client) *kratos.App {
|
||||
if audit != nil && loggerControl != nil {
|
||||
loggerControl.SetErrorSink(logging.ErrorSinkFunc(func(ctx context.Context, entry logging.ErrorEntry) error {
|
||||
return audit.CreateErrorRequest(ctx, &dto.ErrorRecordRequest{Form: entry.Form, Info: entry.Info, Level: entry.Level, RequestID: entry.RequestID, TraceID: entry.TraceID})
|
||||
return audit.CreateErrorRequest(ctx, &service.ErrorRecordRequest{Form: entry.Form, Info: entry.Info, Level: entry.Level, RequestID: entry.RequestID, TraceID: entry.TraceID})
|
||||
}))
|
||||
}
|
||||
return kratos.New(
|
||||
|
||||
@@ -16,6 +16,8 @@ import (
|
||||
"kra/app/system/internal/integration"
|
||||
"kra/app/system/internal/integration/cache"
|
||||
"kra/app/system/internal/server"
|
||||
"kra/app/system/internal/server/handler"
|
||||
"kra/app/system/internal/server/router"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/worker"
|
||||
"kra/pkg/logging"
|
||||
@@ -29,6 +31,8 @@ import (
|
||||
func wireApp(*conf.Server, *conf.Runtime, *slog.Logger, *logging.ReloadableLogger, string) (*kratos.App, func(), error) {
|
||||
panic(wire.Build(
|
||||
server.ProviderSet,
|
||||
handler.ProviderSet,
|
||||
router.ProviderSet,
|
||||
worker.ProviderSet,
|
||||
app.Catalog,
|
||||
app.TaskRegistry,
|
||||
|
||||
Generated
+21
-5
@@ -17,10 +17,13 @@ import (
|
||||
"kra/app/system/internal/initialize"
|
||||
"kra/app/system/internal/integration/cache"
|
||||
"kra/app/system/internal/integration/email"
|
||||
"kra/app/system/internal/integration/mq"
|
||||
"kra/app/system/internal/integration/storage"
|
||||
"kra/app/system/internal/integration/websocket"
|
||||
"kra/app/system/internal/server"
|
||||
"kra/app/system/internal/server/handler"
|
||||
"kra/app/system/internal/server/router"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/handler"
|
||||
"kra/app/system/internal/worker"
|
||||
"kra/pkg/logging"
|
||||
"log/slog"
|
||||
@@ -143,14 +146,27 @@ func wireApp(confServer *conf.Server, runtime *conf.Runtime, logger *slog.Logger
|
||||
user := handler.NewUser(userService, authService)
|
||||
navigation := handler.NewNavigation(userService)
|
||||
session := handler.NewSession(tokenService)
|
||||
set := handler.NewSet(authority, menu, api, permission, organization, announcement, handlerEmail, handlerPayment, task, media, audit, export, version, dictionary, parameter, apiToken, systemConfig, public, user, navigation, session)
|
||||
routes := server.NewRoutes(set)
|
||||
v := handler.NewSet(authority, menu, api, permission, organization, announcement, handlerEmail, handlerPayment, task, media, audit, export, version, dictionary, parameter, apiToken, systemConfig, public, user, navigation, session)
|
||||
routes := router.NewRoutes(v)
|
||||
taskMethods := worker.NewTaskMethods(taskUsecase, mediaUsecase, runtime)
|
||||
moduleRuntime := app.Runtime(routes, taskMethods, registry)
|
||||
engine := server.NewGinEngineWithRuntime(runtime, accessControlService, authService, securityService, auditRecorder, logger, string2, moduleRuntime)
|
||||
websocketServer, cleanup2, err := websocket.New(runtime)
|
||||
if err != nil {
|
||||
cleanup()
|
||||
return nil, nil, err
|
||||
}
|
||||
engine := server.NewGinEngineWithRuntime(runtime, accessControlService, authService, securityService, auditRecorder, logger, string2, moduleRuntime, websocketServer)
|
||||
httpServer := server.NewGinServer(confServer, engine)
|
||||
kratosApp := newApp(logger, httpServer, taskScheduler, auditRecorder, reloadableLogger)
|
||||
mqReloadable, cleanup3, err := mq.New(runtime, logger)
|
||||
if err != nil {
|
||||
cleanup2()
|
||||
cleanup()
|
||||
return nil, nil, err
|
||||
}
|
||||
kratosApp := newApp(logger, httpServer, taskScheduler, auditRecorder, reloadableLogger, mqReloadable)
|
||||
return kratosApp, func() {
|
||||
cleanup3()
|
||||
cleanup2()
|
||||
cleanup()
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
systemmodule "kra/app/system/internal/module"
|
||||
systemserver "kra/app/system/internal/server"
|
||||
systemrouter "kra/app/system/internal/server/router"
|
||||
systemworker "kra/app/system/internal/worker"
|
||||
"kra/pkg/module"
|
||||
platformtask "kra/pkg/task"
|
||||
@@ -15,7 +14,7 @@ import (
|
||||
// module means adding one Definition here; system initialization and runtime
|
||||
// code consume the catalog without knowing that module's implementation.
|
||||
func Catalog() module.Catalog {
|
||||
return module.Catalog{Definitions: []module.Definition{systemmodule.Definition()}}
|
||||
return module.Catalog{Definitions: []module.Definition{Definition()}}
|
||||
}
|
||||
|
||||
// TaskRegistry builds the process-wide registry from dependency-free module
|
||||
@@ -28,7 +27,7 @@ func TaskRegistry(catalog module.Catalog) *platformtask.Registry {
|
||||
}
|
||||
|
||||
// Runtime composes HTTP route contributors from the enabled modules.
|
||||
func Runtime(systemRoutes *systemserver.Routes, systemTasks *systemworker.TaskMethods, registry *platformtask.Registry) *module.Runtime {
|
||||
func Runtime(systemRoutes *systemrouter.Routes, systemTasks *systemworker.TaskMethods, registry *platformtask.Registry) *module.Runtime {
|
||||
platformtask.Apply(registry, systemTasks)
|
||||
return module.NewRuntime(systemRoutes)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package system
|
||||
package app
|
||||
|
||||
import (
|
||||
datapayment "kra/app/system/internal/data/payment"
|
||||
@@ -493,6 +493,30 @@ func (uc *PaymentUsecase) Refund(ctx context.Context, provider, tradeNo string,
|
||||
return uc.refundWithOrder(ctx, provider, tradeNo, amount)
|
||||
}
|
||||
|
||||
// Fulfill retries delivery for an already-paid order. It is intentionally
|
||||
// idempotent: the repository lease prevents concurrent attempts and a
|
||||
// succeeded order is returned as a duplicate without invoking the handler.
|
||||
func (uc *PaymentUsecase) Fulfill(ctx context.Context, provider, tradeNo string) (*PaymentResult, error) {
|
||||
if !validPaymentText(provider, 64) || !validPaymentText(tradeNo, 128) {
|
||||
return nil, errors.New("发货参数不完整")
|
||||
}
|
||||
if uc.orders == nil {
|
||||
return nil, errors.New("支付订单仓储未接入")
|
||||
}
|
||||
order, err := uc.orders.FindPaymentOrder(ctx, provider, tradeNo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result := paymentResultFromOrder(order)
|
||||
if result == nil {
|
||||
return nil, ErrPaymentOrderNotFound
|
||||
}
|
||||
if order.PaymentStatus != PaymentStatusPaid {
|
||||
return nil, ErrPaymentOrderState
|
||||
}
|
||||
return uc.fulfillOrder(ctx, order, result)
|
||||
}
|
||||
|
||||
// Callback verifies the notification in the adapter, then always performs a
|
||||
// provider-side query before it can dispatch fulfillment. Callback payloads
|
||||
// are never trusted for amount, currency, or final payment state.
|
||||
@@ -671,10 +695,21 @@ func (uc *PaymentUsecase) reconcilePaymentResult(ctx context.Context, result, ca
|
||||
return result, nil
|
||||
}
|
||||
|
||||
fulfilled, err := uc.fulfillOrder(ctx, order, result)
|
||||
if err != nil {
|
||||
return fail(err)
|
||||
}
|
||||
return fulfilled, nil
|
||||
}
|
||||
|
||||
func (uc *PaymentUsecase) fulfillOrder(ctx context.Context, order *PaymentOrder, result *PaymentResult) (*PaymentResult, error) {
|
||||
if order == nil || result == nil {
|
||||
return nil, errors.New("支付发货订单为空")
|
||||
}
|
||||
order, token, duplicate, err := uc.orders.BeginPaymentFulfillment(ctx, result.Provider, result.TradeNo, 10*time.Minute)
|
||||
if err != nil {
|
||||
uc.logger.Error(ctx, "payment.order.fulfillment_begin_failed", "provider", result.Provider, "trade_no", result.TradeNo, "error", err)
|
||||
return fail(err)
|
||||
return nil, err
|
||||
}
|
||||
if duplicate {
|
||||
result.Duplicate = true
|
||||
@@ -685,29 +720,23 @@ func (uc *PaymentUsecase) reconcilePaymentResult(ctx context.Context, result, ca
|
||||
if handler == nil {
|
||||
err = fmt.Errorf("支付发货 handler 未注册: %s", order.BusinessType)
|
||||
_, _ = uc.orders.CompletePaymentFulfillment(ctx, result.Provider, result.TradeNo, token, false, err.Error())
|
||||
uc.logger.Error(ctx, "payment.order.fulfillment_handler_missing", "provider", result.Provider, "trade_no", result.TradeNo, "business_type", order.BusinessType, "error", err)
|
||||
return fail(err)
|
||||
return nil, err
|
||||
}
|
||||
confirmation := &PaymentConfirmation{
|
||||
ID: order.ConfirmationID, Provider: order.Provider, TradeNo: order.TradeNo,
|
||||
ProviderTradeNo: result.ProviderTradeNo, BusinessType: order.BusinessType,
|
||||
BusinessID: order.BusinessID, Subject: order.Subject, Amount: order.Amount,
|
||||
OriginalAmount: order.OriginalAmount, PayerPaidAmount: order.PayerPaidAmount,
|
||||
CashPaidAmount: order.CashPaidAmount, PointPaidAmount: order.PointPaidAmount,
|
||||
confirmation := &PaymentConfirmation{ID: order.ConfirmationID, Provider: order.Provider, TradeNo: order.TradeNo,
|
||||
ProviderTradeNo: result.ProviderTradeNo, BusinessType: order.BusinessType, BusinessID: order.BusinessID,
|
||||
Subject: order.Subject, Amount: order.Amount, OriginalAmount: order.OriginalAmount,
|
||||
PayerPaidAmount: order.PayerPaidAmount, CashPaidAmount: order.CashPaidAmount, PointPaidAmount: order.PointPaidAmount,
|
||||
DiscountAmount: order.DiscountAmount, ProviderDiscountAmount: order.ProviderDiscountAmount,
|
||||
MerchantDiscountAmount: order.MerchantDiscountAmount, SettlementAmount: order.SettlementAmount,
|
||||
Currency: order.Currency, PayerCurrency: order.PayerCurrency, AmountBreakdownKnown: order.AmountBreakdownKnown,
|
||||
Payload: result.Payload, CreatedAt: time.Now().UTC(),
|
||||
}
|
||||
Payload: result.Payload, CreatedAt: time.Now().UTC()}
|
||||
if err = handler.Fulfill(ctx, confirmation); err != nil {
|
||||
_, _ = uc.orders.CompletePaymentFulfillment(ctx, result.Provider, result.TradeNo, token, false, err.Error())
|
||||
uc.logger.Error(ctx, "payment.order.fulfillment_failed", "provider", result.Provider, "trade_no", result.TradeNo, "confirmation_id", confirmation.ID, "error", err)
|
||||
return fail(err)
|
||||
return nil, err
|
||||
}
|
||||
order, err = uc.orders.CompletePaymentFulfillment(ctx, result.Provider, result.TradeNo, token, true, "")
|
||||
if err != nil {
|
||||
uc.logger.Error(ctx, "payment.order.fulfillment_record_failed", "provider", result.Provider, "trade_no", result.TradeNo, "confirmation_id", confirmation.ID, "error", err)
|
||||
return fail(err)
|
||||
return nil, err
|
||||
}
|
||||
result.Status = "fulfilled"
|
||||
return attachOrderResult(result, order), nil
|
||||
|
||||
@@ -330,6 +330,24 @@ func TestPersistentPaymentCallbackFulfillsAndPersistsDuplicate(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaymentFulfillRetriesFailedOrder(t *testing.T) {
|
||||
orders := &paymentOrderRepoStub{order: &PaymentOrder{Provider: PaymentAlipay, TradeNo: "order-1", BusinessType: "game_item", BusinessID: "item-1", Subject: "item", Amount: 100, Currency: "CNY", PaymentStatus: PaymentStatusPaid, FulfillmentStatus: FulfillmentStatusFailed, RefundStatus: RefundStatusNone, ConfirmationID: "11111111-1111-1111-1111-111111111111"}}
|
||||
uc := NewPaymentUsecase(&paymentRepoStub{}, orders, slog.New(slog.NewTextHandler(io.Discard, nil)))
|
||||
handler := &paymentFulfillmentStub{}
|
||||
registry := NewPaymentFulfillmentRegistry()
|
||||
if err := registry.Register(handler); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
uc.SetFulfillmentRegistry(registry)
|
||||
result, err := uc.Fulfill(context.Background(), PaymentAlipay, "order-1")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if result.Status != "fulfilled" || handler.calls != 1 || orders.order.FulfillmentStatus != FulfillmentStatusSucceeded {
|
||||
t.Fatalf("fulfillment retry failed: result=%+v calls=%d status=%s", result, handler.calls, orders.order.FulfillmentStatus)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPaymentConfirmationAcceptsProviderDiscount(t *testing.T) {
|
||||
intent := &PaymentIntent{Provider: PaymentAlipay, TradeNo: "T1", Amount: 1000, Currency: "CNY", BusinessType: "item", BusinessID: "I1"}
|
||||
result := &PaymentResult{Provider: PaymentAlipay, TradeNo: "T1", ProviderTradeNo: "P1", Status: "success", Amount: 1000, PayerPaidAmount: 800, CashPaidAmount: 800, DiscountAmount: 200, ProviderDiscountAmount: 150, MerchantDiscountAmount: 50, SettlementAmount: 950, Currency: "CNY", AmountBreakdownKnown: true}
|
||||
|
||||
Generated
+296
-39
@@ -219,6 +219,8 @@ type AdminBackend struct {
|
||||
Zap *AdminBackend_Zap `protobuf:"bytes,11,opt,name=zap,proto3" json:"zap,omitempty"`
|
||||
Cors *AdminBackend_CORS `protobuf:"bytes,12,opt,name=cors,proto3" json:"cors,omitempty"`
|
||||
App *AdminBackend_App `protobuf:"bytes,13,opt,name=app,proto3" json:"app,omitempty"`
|
||||
Websocket *AdminBackend_WebSocket `protobuf:"bytes,14,opt,name=websocket,proto3" json:"websocket,omitempty"`
|
||||
Mq *AdminBackend_MQ `protobuf:"bytes,15,opt,name=mq,proto3" json:"mq,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
@@ -344,6 +346,20 @@ func (x *AdminBackend) GetApp() *AdminBackend_App {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend) GetWebsocket() *AdminBackend_WebSocket {
|
||||
if x != nil {
|
||||
return x.Websocket
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend) GetMq() *AdminBackend_MQ {
|
||||
if x != nil {
|
||||
return x.Mq
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Server_HTTP struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"`
|
||||
@@ -1704,6 +1720,216 @@ func (x *AdminBackend_App) GetEnv() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
type AdminBackend_WebSocket struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||
Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
|
||||
AllowOrigins []string `protobuf:"bytes,3,rep,name=allow_origins,json=allowOrigins,proto3" json:"allow_origins,omitempty"`
|
||||
MaxMessageSize int64 `protobuf:"varint,4,opt,name=max_message_size,json=maxMessageSize,proto3" json:"max_message_size,omitempty"`
|
||||
WriteWait *durationpb.Duration `protobuf:"bytes,5,opt,name=write_wait,json=writeWait,proto3" json:"write_wait,omitempty"`
|
||||
PongWait *durationpb.Duration `protobuf:"bytes,6,opt,name=pong_wait,json=pongWait,proto3" json:"pong_wait,omitempty"`
|
||||
PingPeriod *durationpb.Duration `protobuf:"bytes,7,opt,name=ping_period,json=pingPeriod,proto3" json:"ping_period,omitempty"`
|
||||
MessageBufferSize int32 `protobuf:"varint,8,opt,name=message_buffer_size,json=messageBufferSize,proto3" json:"message_buffer_size,omitempty"`
|
||||
ConcurrentMessageHandling bool `protobuf:"varint,9,opt,name=concurrent_message_handling,json=concurrentMessageHandling,proto3" json:"concurrent_message_handling,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) Reset() {
|
||||
*x = AdminBackend_WebSocket{}
|
||||
mi := &file_conf_conf_proto_msgTypes[20]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AdminBackend_WebSocket) ProtoMessage() {}
|
||||
|
||||
func (x *AdminBackend_WebSocket) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_conf_conf_proto_msgTypes[20]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AdminBackend_WebSocket.ProtoReflect.Descriptor instead.
|
||||
func (*AdminBackend_WebSocket) Descriptor() ([]byte, []int) {
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 11}
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) GetEnabled() bool {
|
||||
if x != nil {
|
||||
return x.Enabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) GetPath() string {
|
||||
if x != nil {
|
||||
return x.Path
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) GetAllowOrigins() []string {
|
||||
if x != nil {
|
||||
return x.AllowOrigins
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) GetMaxMessageSize() int64 {
|
||||
if x != nil {
|
||||
return x.MaxMessageSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) GetWriteWait() *durationpb.Duration {
|
||||
if x != nil {
|
||||
return x.WriteWait
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) GetPongWait() *durationpb.Duration {
|
||||
if x != nil {
|
||||
return x.PongWait
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) GetPingPeriod() *durationpb.Duration {
|
||||
if x != nil {
|
||||
return x.PingPeriod
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) GetMessageBufferSize() int32 {
|
||||
if x != nil {
|
||||
return x.MessageBufferSize
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *AdminBackend_WebSocket) GetConcurrentMessageHandling() bool {
|
||||
if x != nil {
|
||||
return x.ConcurrentMessageHandling
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// MQ config is persisted in sys_integration_configs (kind=mq/provider=emqx).
|
||||
// The bootstrap fields are retained as a one-time migration source.
|
||||
type AdminBackend_MQ struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
|
||||
Broker string `protobuf:"bytes,2,opt,name=broker,proto3" json:"broker,omitempty"`
|
||||
ClientId string `protobuf:"bytes,3,opt,name=client_id,json=clientId,proto3" json:"client_id,omitempty"`
|
||||
Username string `protobuf:"bytes,4,opt,name=username,proto3" json:"username,omitempty"`
|
||||
Password string `protobuf:"bytes,5,opt,name=password,proto3" json:"password,omitempty"`
|
||||
KeepAlive int32 `protobuf:"varint,6,opt,name=keep_alive,json=keepAlive,proto3" json:"keep_alive,omitempty"`
|
||||
CleanSession bool `protobuf:"varint,7,opt,name=clean_session,json=cleanSession,proto3" json:"clean_session,omitempty"`
|
||||
ConnectTimeout int32 `protobuf:"varint,8,opt,name=connect_timeout,json=connectTimeout,proto3" json:"connect_timeout,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) Reset() {
|
||||
*x = AdminBackend_MQ{}
|
||||
mi := &file_conf_conf_proto_msgTypes[21]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*AdminBackend_MQ) ProtoMessage() {}
|
||||
|
||||
func (x *AdminBackend_MQ) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_conf_conf_proto_msgTypes[21]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use AdminBackend_MQ.ProtoReflect.Descriptor instead.
|
||||
func (*AdminBackend_MQ) Descriptor() ([]byte, []int) {
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 12}
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) GetEnabled() bool {
|
||||
if x != nil {
|
||||
return x.Enabled
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) GetBroker() string {
|
||||
if x != nil {
|
||||
return x.Broker
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) GetClientId() string {
|
||||
if x != nil {
|
||||
return x.ClientId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) GetPassword() string {
|
||||
if x != nil {
|
||||
return x.Password
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) GetKeepAlive() int32 {
|
||||
if x != nil {
|
||||
return x.KeepAlive
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) GetCleanSession() bool {
|
||||
if x != nil {
|
||||
return x.CleanSession
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *AdminBackend_MQ) GetConnectTimeout() int32 {
|
||||
if x != nil {
|
||||
return x.ConnectTimeout
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
type AdminBackend_Storage struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Type string `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
|
||||
@@ -1720,7 +1946,7 @@ type AdminBackend_Storage struct {
|
||||
|
||||
func (x *AdminBackend_Storage) Reset() {
|
||||
*x = AdminBackend_Storage{}
|
||||
mi := &file_conf_conf_proto_msgTypes[20]
|
||||
mi := &file_conf_conf_proto_msgTypes[22]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1732,7 +1958,7 @@ func (x *AdminBackend_Storage) String() string {
|
||||
func (*AdminBackend_Storage) ProtoMessage() {}
|
||||
|
||||
func (x *AdminBackend_Storage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_conf_conf_proto_msgTypes[20]
|
||||
mi := &file_conf_conf_proto_msgTypes[22]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1745,7 +1971,7 @@ func (x *AdminBackend_Storage) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AdminBackend_Storage.ProtoReflect.Descriptor instead.
|
||||
func (*AdminBackend_Storage) Descriptor() ([]byte, []int) {
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 11}
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 13}
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Storage) GetType() string {
|
||||
@@ -1819,7 +2045,7 @@ type AdminBackend_Qiniu struct {
|
||||
|
||||
func (x *AdminBackend_Qiniu) Reset() {
|
||||
*x = AdminBackend_Qiniu{}
|
||||
mi := &file_conf_conf_proto_msgTypes[21]
|
||||
mi := &file_conf_conf_proto_msgTypes[23]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1831,7 +2057,7 @@ func (x *AdminBackend_Qiniu) String() string {
|
||||
func (*AdminBackend_Qiniu) ProtoMessage() {}
|
||||
|
||||
func (x *AdminBackend_Qiniu) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_conf_conf_proto_msgTypes[21]
|
||||
mi := &file_conf_conf_proto_msgTypes[23]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1844,7 +2070,7 @@ func (x *AdminBackend_Qiniu) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AdminBackend_Qiniu.ProtoReflect.Descriptor instead.
|
||||
func (*AdminBackend_Qiniu) Descriptor() ([]byte, []int) {
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 12}
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 14}
|
||||
}
|
||||
|
||||
func (x *AdminBackend_Qiniu) GetZone() string {
|
||||
@@ -1916,7 +2142,7 @@ type AdminBackend_ObjectStore struct {
|
||||
|
||||
func (x *AdminBackend_ObjectStore) Reset() {
|
||||
*x = AdminBackend_ObjectStore{}
|
||||
mi := &file_conf_conf_proto_msgTypes[22]
|
||||
mi := &file_conf_conf_proto_msgTypes[24]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -1928,7 +2154,7 @@ func (x *AdminBackend_ObjectStore) String() string {
|
||||
func (*AdminBackend_ObjectStore) ProtoMessage() {}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_conf_conf_proto_msgTypes[22]
|
||||
mi := &file_conf_conf_proto_msgTypes[24]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -1941,7 +2167,7 @@ func (x *AdminBackend_ObjectStore) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AdminBackend_ObjectStore.ProtoReflect.Descriptor instead.
|
||||
func (*AdminBackend_ObjectStore) Descriptor() ([]byte, []int) {
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 13}
|
||||
return file_conf_conf_proto_rawDescGZIP(), []int{3, 15}
|
||||
}
|
||||
|
||||
func (x *AdminBackend_ObjectStore) GetEndpoint() string {
|
||||
@@ -2086,7 +2312,7 @@ const file_conf_conf_proto_rawDesc = "" +
|
||||
"\x12connect_timeout_ms\x18\n" +
|
||||
" \x01(\x03R\x10connectTimeoutMs\x12\x15\n" +
|
||||
"\x06is_zap\x18\v \x01(\bR\x05isZap\x120\n" +
|
||||
"\x05hosts\x18\f \x03(\v2\x1a.kratos.api.Data.MongoHostR\x05hosts\"\xad\x1c\n" +
|
||||
"\x05hosts\x18\f \x03(\v2\x1a.kratos.api.Data.MongoHostR\x05hosts\"\xc0\"\n" +
|
||||
"\fAdminBackend\x12#\n" +
|
||||
"\rrouter_prefix\x18\x01 \x01(\tR\frouterPrefix\x12.\n" +
|
||||
"\x03jwt\x18\x02 \x01(\v2\x1c.kratos.api.AdminBackend.JWTR\x03jwt\x12:\n" +
|
||||
@@ -2102,7 +2328,9 @@ const file_conf_conf_proto_rawDesc = "" +
|
||||
" \x01(\v2\x1f.kratos.api.AdminBackend.SystemR\x06system\x12.\n" +
|
||||
"\x03zap\x18\v \x01(\v2\x1c.kratos.api.AdminBackend.ZapR\x03zap\x121\n" +
|
||||
"\x04cors\x18\f \x01(\v2\x1d.kratos.api.AdminBackend.CORSR\x04cors\x12.\n" +
|
||||
"\x03app\x18\r \x01(\v2\x1c.kratos.api.AdminBackend.AppR\x03app\x1a\xb8\x01\n" +
|
||||
"\x03app\x18\r \x01(\v2\x1c.kratos.api.AdminBackend.AppR\x03app\x12@\n" +
|
||||
"\twebsocket\x18\x0e \x01(\v2\".kratos.api.AdminBackend.WebSocketR\twebsocket\x12+\n" +
|
||||
"\x02mq\x18\x0f \x01(\v2\x1b.kratos.api.AdminBackend.MQR\x02mq\x1a\xb8\x01\n" +
|
||||
"\x03JWT\x12\x1f\n" +
|
||||
"\vsigning_key\x18\x01 \x01(\tR\n" +
|
||||
"signingKey\x12<\n" +
|
||||
@@ -2175,7 +2403,29 @@ const file_conf_conf_proto_rawDesc = "" +
|
||||
"\x03App\x12\x12\n" +
|
||||
"\x04node\x18\x01 \x01(\tR\x04node\x12\x15\n" +
|
||||
"\x06app_id\x18\x02 \x01(\tR\x05appId\x12\x10\n" +
|
||||
"\x03env\x18\x03 \x01(\tR\x03env\x1a\xe8\x03\n" +
|
||||
"\x03env\x18\x03 \x01(\tR\x03env\x1a\xa6\x03\n" +
|
||||
"\tWebSocket\x12\x18\n" +
|
||||
"\aenabled\x18\x01 \x01(\bR\aenabled\x12\x12\n" +
|
||||
"\x04path\x18\x02 \x01(\tR\x04path\x12#\n" +
|
||||
"\rallow_origins\x18\x03 \x03(\tR\fallowOrigins\x12(\n" +
|
||||
"\x10max_message_size\x18\x04 \x01(\x03R\x0emaxMessageSize\x128\n" +
|
||||
"\n" +
|
||||
"write_wait\x18\x05 \x01(\v2\x19.google.protobuf.DurationR\twriteWait\x126\n" +
|
||||
"\tpong_wait\x18\x06 \x01(\v2\x19.google.protobuf.DurationR\bpongWait\x12:\n" +
|
||||
"\vping_period\x18\a \x01(\v2\x19.google.protobuf.DurationR\n" +
|
||||
"pingPeriod\x12.\n" +
|
||||
"\x13message_buffer_size\x18\b \x01(\x05R\x11messageBufferSize\x12>\n" +
|
||||
"\x1bconcurrent_message_handling\x18\t \x01(\bR\x19concurrentMessageHandling\x1a\xf8\x01\n" +
|
||||
"\x02MQ\x12\x18\n" +
|
||||
"\aenabled\x18\x01 \x01(\bR\aenabled\x12\x16\n" +
|
||||
"\x06broker\x18\x02 \x01(\tR\x06broker\x12\x1b\n" +
|
||||
"\tclient_id\x18\x03 \x01(\tR\bclientId\x12\x1a\n" +
|
||||
"\busername\x18\x04 \x01(\tR\busername\x12\x1a\n" +
|
||||
"\bpassword\x18\x05 \x01(\tR\bpassword\x12\x1d\n" +
|
||||
"\n" +
|
||||
"keep_alive\x18\x06 \x01(\x05R\tkeepAlive\x12#\n" +
|
||||
"\rclean_session\x18\a \x01(\bR\fcleanSession\x12'\n" +
|
||||
"\x0fconnect_timeout\x18\b \x01(\x05R\x0econnectTimeout\x1a\xe8\x03\n" +
|
||||
"\aStorage\x12\x12\n" +
|
||||
"\x04type\x18\x01 \x01(\tR\x04type\x124\n" +
|
||||
"\x05qiniu\x18\x02 \x01(\v2\x1e.kratos.api.AdminBackend.QiniuR\x05qiniu\x12C\n" +
|
||||
@@ -2227,7 +2477,7 @@ func file_conf_conf_proto_rawDescGZIP() []byte {
|
||||
return file_conf_conf_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 23)
|
||||
var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 25)
|
||||
var file_conf_conf_proto_goTypes = []any{
|
||||
(*Bootstrap)(nil), // 0: kratos.api.Bootstrap
|
||||
(*Server)(nil), // 1: kratos.api.Server
|
||||
@@ -2249,10 +2499,12 @@ var file_conf_conf_proto_goTypes = []any{
|
||||
(*AdminBackend_CORS)(nil), // 17: kratos.api.AdminBackend.CORS
|
||||
(*AdminBackend_CORSRule)(nil), // 18: kratos.api.AdminBackend.CORSRule
|
||||
(*AdminBackend_App)(nil), // 19: kratos.api.AdminBackend.App
|
||||
(*AdminBackend_Storage)(nil), // 20: kratos.api.AdminBackend.Storage
|
||||
(*AdminBackend_Qiniu)(nil), // 21: kratos.api.AdminBackend.Qiniu
|
||||
(*AdminBackend_ObjectStore)(nil), // 22: kratos.api.AdminBackend.ObjectStore
|
||||
(*durationpb.Duration)(nil), // 23: google.protobuf.Duration
|
||||
(*AdminBackend_WebSocket)(nil), // 20: kratos.api.AdminBackend.WebSocket
|
||||
(*AdminBackend_MQ)(nil), // 21: kratos.api.AdminBackend.MQ
|
||||
(*AdminBackend_Storage)(nil), // 22: kratos.api.AdminBackend.Storage
|
||||
(*AdminBackend_Qiniu)(nil), // 23: kratos.api.AdminBackend.Qiniu
|
||||
(*AdminBackend_ObjectStore)(nil), // 24: kratos.api.AdminBackend.ObjectStore
|
||||
(*durationpb.Duration)(nil), // 25: google.protobuf.Duration
|
||||
}
|
||||
var file_conf_conf_proto_depIdxs = []int32{
|
||||
1, // 0: kratos.api.Bootstrap.server:type_name -> kratos.api.Server
|
||||
@@ -2268,33 +2520,38 @@ var file_conf_conf_proto_depIdxs = []int32{
|
||||
10, // 10: kratos.api.AdminBackend.captcha:type_name -> kratos.api.AdminBackend.Captcha
|
||||
11, // 11: kratos.api.AdminBackend.local:type_name -> kratos.api.AdminBackend.Local
|
||||
12, // 12: kratos.api.AdminBackend.email:type_name -> kratos.api.AdminBackend.Email
|
||||
20, // 13: kratos.api.AdminBackend.storage:type_name -> kratos.api.AdminBackend.Storage
|
||||
22, // 13: kratos.api.AdminBackend.storage:type_name -> kratos.api.AdminBackend.Storage
|
||||
13, // 14: kratos.api.AdminBackend.media:type_name -> kratos.api.AdminBackend.Media
|
||||
14, // 15: kratos.api.AdminBackend.disk_list:type_name -> kratos.api.AdminBackend.Disk
|
||||
15, // 16: kratos.api.AdminBackend.system:type_name -> kratos.api.AdminBackend.System
|
||||
16, // 17: kratos.api.AdminBackend.zap:type_name -> kratos.api.AdminBackend.Zap
|
||||
17, // 18: kratos.api.AdminBackend.cors:type_name -> kratos.api.AdminBackend.CORS
|
||||
19, // 19: kratos.api.AdminBackend.app:type_name -> kratos.api.AdminBackend.App
|
||||
23, // 20: kratos.api.Server.HTTP.timeout:type_name -> google.protobuf.Duration
|
||||
23, // 21: kratos.api.Data.Redis.read_timeout:type_name -> google.protobuf.Duration
|
||||
23, // 22: kratos.api.Data.Redis.write_timeout:type_name -> google.protobuf.Duration
|
||||
7, // 23: kratos.api.Data.Mongo.hosts:type_name -> kratos.api.Data.MongoHost
|
||||
23, // 24: kratos.api.AdminBackend.JWT.expires_time:type_name -> google.protobuf.Duration
|
||||
23, // 25: kratos.api.AdminBackend.JWT.buffer_time:type_name -> google.protobuf.Duration
|
||||
23, // 26: kratos.api.AdminBackend.Captcha.store_expiration:type_name -> google.protobuf.Duration
|
||||
18, // 27: kratos.api.AdminBackend.CORS.whitelist:type_name -> kratos.api.AdminBackend.CORSRule
|
||||
21, // 28: kratos.api.AdminBackend.Storage.qiniu:type_name -> kratos.api.AdminBackend.Qiniu
|
||||
22, // 29: kratos.api.AdminBackend.Storage.aliyun_oss:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
22, // 30: kratos.api.AdminBackend.Storage.huawei_obs:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
22, // 31: kratos.api.AdminBackend.Storage.tencent_cos:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
22, // 32: kratos.api.AdminBackend.Storage.aws_s3:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
22, // 33: kratos.api.AdminBackend.Storage.cloudflare_r2:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
22, // 34: kratos.api.AdminBackend.Storage.minio:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
35, // [35:35] is the sub-list for method output_type
|
||||
35, // [35:35] is the sub-list for method input_type
|
||||
35, // [35:35] is the sub-list for extension type_name
|
||||
35, // [35:35] is the sub-list for extension extendee
|
||||
0, // [0:35] is the sub-list for field type_name
|
||||
20, // 20: kratos.api.AdminBackend.websocket:type_name -> kratos.api.AdminBackend.WebSocket
|
||||
21, // 21: kratos.api.AdminBackend.mq:type_name -> kratos.api.AdminBackend.MQ
|
||||
25, // 22: kratos.api.Server.HTTP.timeout:type_name -> google.protobuf.Duration
|
||||
25, // 23: kratos.api.Data.Redis.read_timeout:type_name -> google.protobuf.Duration
|
||||
25, // 24: kratos.api.Data.Redis.write_timeout:type_name -> google.protobuf.Duration
|
||||
7, // 25: kratos.api.Data.Mongo.hosts:type_name -> kratos.api.Data.MongoHost
|
||||
25, // 26: kratos.api.AdminBackend.JWT.expires_time:type_name -> google.protobuf.Duration
|
||||
25, // 27: kratos.api.AdminBackend.JWT.buffer_time:type_name -> google.protobuf.Duration
|
||||
25, // 28: kratos.api.AdminBackend.Captcha.store_expiration:type_name -> google.protobuf.Duration
|
||||
18, // 29: kratos.api.AdminBackend.CORS.whitelist:type_name -> kratos.api.AdminBackend.CORSRule
|
||||
25, // 30: kratos.api.AdminBackend.WebSocket.write_wait:type_name -> google.protobuf.Duration
|
||||
25, // 31: kratos.api.AdminBackend.WebSocket.pong_wait:type_name -> google.protobuf.Duration
|
||||
25, // 32: kratos.api.AdminBackend.WebSocket.ping_period:type_name -> google.protobuf.Duration
|
||||
23, // 33: kratos.api.AdminBackend.Storage.qiniu:type_name -> kratos.api.AdminBackend.Qiniu
|
||||
24, // 34: kratos.api.AdminBackend.Storage.aliyun_oss:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
24, // 35: kratos.api.AdminBackend.Storage.huawei_obs:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
24, // 36: kratos.api.AdminBackend.Storage.tencent_cos:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
24, // 37: kratos.api.AdminBackend.Storage.aws_s3:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
24, // 38: kratos.api.AdminBackend.Storage.cloudflare_r2:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
24, // 39: kratos.api.AdminBackend.Storage.minio:type_name -> kratos.api.AdminBackend.ObjectStore
|
||||
40, // [40:40] is the sub-list for method output_type
|
||||
40, // [40:40] is the sub-list for method input_type
|
||||
40, // [40:40] is the sub-list for extension type_name
|
||||
40, // [40:40] is the sub-list for extension extendee
|
||||
0, // [0:40] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_conf_conf_proto_init() }
|
||||
@@ -2308,7 +2565,7 @@ func file_conf_conf_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_conf_conf_proto_rawDesc), len(file_conf_conf_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 23,
|
||||
NumMessages: 25,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
||||
@@ -93,6 +93,8 @@ message AdminBackend {
|
||||
Zap zap = 11;
|
||||
CORS cors = 12;
|
||||
App app = 13;
|
||||
WebSocket websocket = 14;
|
||||
MQ mq = 15;
|
||||
|
||||
message JWT {
|
||||
string signing_key = 1;
|
||||
@@ -179,6 +181,31 @@ message AdminBackend {
|
||||
string env = 3;
|
||||
}
|
||||
|
||||
message WebSocket {
|
||||
bool enabled = 1;
|
||||
string path = 2;
|
||||
repeated string allow_origins = 3;
|
||||
int64 max_message_size = 4;
|
||||
google.protobuf.Duration write_wait = 5;
|
||||
google.protobuf.Duration pong_wait = 6;
|
||||
google.protobuf.Duration ping_period = 7;
|
||||
int32 message_buffer_size = 8;
|
||||
bool concurrent_message_handling = 9;
|
||||
}
|
||||
|
||||
// MQ config is persisted in sys_integration_configs (kind=mq/provider=emqx).
|
||||
// The bootstrap fields are retained as a one-time migration source.
|
||||
message MQ {
|
||||
bool enabled = 1;
|
||||
string broker = 2;
|
||||
string client_id = 3;
|
||||
string username = 4;
|
||||
string password = 5;
|
||||
int32 keep_alive = 6;
|
||||
bool clean_session = 7;
|
||||
int32 connect_timeout = 8;
|
||||
}
|
||||
|
||||
message Storage {
|
||||
string type = 1;
|
||||
Qiniu qiniu = 2;
|
||||
|
||||
@@ -182,6 +182,8 @@ func (d *Data) persistConfigValuesLocked(dataConfig *conf.Data, adminConfig *con
|
||||
fileAdmin := cloneAdminConfig(adminConfig)
|
||||
fileAdmin.Storage = nil
|
||||
fileAdmin.Email = nil
|
||||
fileAdmin.Websocket = nil
|
||||
fileAdmin.Mq = nil
|
||||
adminValue, err := protoMap(fileAdmin)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -194,6 +196,8 @@ func (d *Data) persistConfigValuesLocked(dataConfig *conf.Data, adminConfig *con
|
||||
}
|
||||
deleteYAMLMapping(&document, "admin", "storage")
|
||||
deleteYAMLMapping(&document, "admin", "email")
|
||||
deleteYAMLMapping(&document, "admin", "websocket")
|
||||
deleteYAMLMapping(&document, "admin", "mq")
|
||||
if adminConfig.System != nil {
|
||||
if err = setServerHTTPPort(&document, adminConfig.System.Addr); err != nil {
|
||||
return err
|
||||
@@ -253,6 +257,8 @@ func (d *Data) persistDatabaseConfig(database *conf.Data_Database, signingKey st
|
||||
}
|
||||
deleteYAMLMapping(&document, "admin", "storage")
|
||||
deleteYAMLMapping(&document, "admin", "email")
|
||||
deleteYAMLMapping(&document, "admin", "websocket")
|
||||
deleteYAMLMapping(&document, "admin", "mq")
|
||||
return writeConfigDocument(configPath, &document)
|
||||
}
|
||||
|
||||
@@ -271,11 +277,13 @@ func (d *Data) removeIntegrationConfigFromFile() error {
|
||||
if err = yaml.Unmarshal(raw, &document); err != nil {
|
||||
return err
|
||||
}
|
||||
if yamlMappingValue(&document, "admin", "storage") == nil && yamlMappingValue(&document, "admin", "email") == nil {
|
||||
if yamlMappingValue(&document, "admin", "storage") == nil && yamlMappingValue(&document, "admin", "email") == nil && yamlMappingValue(&document, "admin", "websocket") == nil && yamlMappingValue(&document, "admin", "mq") == nil {
|
||||
return nil
|
||||
}
|
||||
deleteYAMLMapping(&document, "admin", "storage")
|
||||
deleteYAMLMapping(&document, "admin", "email")
|
||||
deleteYAMLMapping(&document, "admin", "websocket")
|
||||
deleteYAMLMapping(&document, "admin", "mq")
|
||||
return writeConfigDocument(configPath, &document)
|
||||
}
|
||||
|
||||
@@ -357,6 +365,8 @@ func (d *Data) reloadConfig(ctx context.Context) error {
|
||||
}
|
||||
legacyStorage := next.Admin.Storage
|
||||
legacyEmail := next.Admin.Email
|
||||
legacyWebSocket := next.Admin.Websocket
|
||||
legacyMQ := next.Admin.Mq
|
||||
currentAdmin := d.runtime.Admin()
|
||||
if legacyStorage == nil {
|
||||
if currentAdmin != nil {
|
||||
@@ -366,6 +376,12 @@ func (d *Data) reloadConfig(ctx context.Context) error {
|
||||
if legacyEmail == nil && currentAdmin != nil {
|
||||
legacyEmail = currentAdmin.Email
|
||||
}
|
||||
if legacyWebSocket == nil && currentAdmin != nil {
|
||||
legacyWebSocket = currentAdmin.Websocket
|
||||
}
|
||||
if legacyMQ == nil && currentAdmin != nil {
|
||||
legacyMQ = currentAdmin.Mq
|
||||
}
|
||||
storageConfig, err := resolveStorageIntegrationConfig(candidateDB.WithContext(ctx), legacyStorage)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reload storage configuration: %w", err)
|
||||
@@ -376,6 +392,16 @@ func (d *Data) reloadConfig(ctx context.Context) error {
|
||||
return fmt.Errorf("reload email configuration: %w", err)
|
||||
}
|
||||
next.Admin.Email = emailConfig
|
||||
websocketConfig, err := resolveWebSocketIntegrationConfig(candidateDB.WithContext(ctx), legacyWebSocket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reload websocket configuration: %w", err)
|
||||
}
|
||||
next.Admin.Websocket = websocketConfig
|
||||
mqConfig, err := resolveMQIntegrationConfig(candidateDB.WithContext(ctx), legacyMQ)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reload mq configuration: %w", err)
|
||||
}
|
||||
next.Admin.Mq = mqConfig
|
||||
candidateStorage, err := storage.New(next.Admin)
|
||||
if err != nil {
|
||||
return fmt.Errorf("reload storage: %w", err)
|
||||
|
||||
@@ -58,6 +58,8 @@ func (d *Data) watchConfig() func() {
|
||||
if current := d.runtime.Admin(); current != nil {
|
||||
next.Admin.Storage = current.Storage
|
||||
next.Admin.Email = current.Email
|
||||
next.Admin.Mq = current.Mq
|
||||
next.Admin.Websocket = current.Websocket
|
||||
}
|
||||
next.Admin.ConfigPath = absolute
|
||||
d.runtime.Replace(next.Data, next.Admin)
|
||||
|
||||
@@ -207,6 +207,16 @@ func NewData(runtime *conf.Runtime, appLogger *slog.Logger, storageManager *stor
|
||||
}
|
||||
admin.Storage = storageConfig
|
||||
admin.Email = emailConfig
|
||||
websocketConfig, websocketErr := resolveWebSocketIntegrationConfig(db, admin.Websocket)
|
||||
if websocketErr != nil {
|
||||
return nil, nil, fmt.Errorf("load websocket integration configuration: %w", websocketErr)
|
||||
}
|
||||
admin.Websocket = websocketConfig
|
||||
mqConfig, mqErr := resolveMQIntegrationConfig(db, admin.Mq)
|
||||
if mqErr != nil {
|
||||
return nil, nil, fmt.Errorf("load mq integration configuration: %w", mqErr)
|
||||
}
|
||||
admin.Mq = mqConfig
|
||||
runtime.Replace(c, admin)
|
||||
activeStorage, storageErr := storage.New(admin)
|
||||
if storageErr != nil {
|
||||
|
||||
@@ -69,6 +69,12 @@ func (d *Data) PersistAdminConfig(ctx context.Context, raw []byte) error {
|
||||
if next.Email == nil {
|
||||
next.Email = currentAdmin.Email
|
||||
}
|
||||
if next.Websocket == nil {
|
||||
next.Websocket = currentAdmin.Websocket
|
||||
}
|
||||
if next.Mq == nil {
|
||||
next.Mq = currentAdmin.Mq
|
||||
}
|
||||
next.ConfigPath = currentAdmin.ConfigPath
|
||||
candidateStorage, err := storage.New(next)
|
||||
if err != nil {
|
||||
@@ -80,6 +86,12 @@ func (d *Data) PersistAdminConfig(ctx context.Context, raw []byte) error {
|
||||
if err := d.persistEmailIntegrationConfig(ctx, next.Email); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.PersistWebSocketConfig(ctx, next.Websocket); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.persistMQIntegrationConfig(ctx, next.Mq); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.persistConfigValues(currentData, next); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -108,6 +120,12 @@ func (d *Data) PersistRuntimeConfig(ctx context.Context, dataRaw, adminRaw []byt
|
||||
if nextAdmin.Email == nil {
|
||||
nextAdmin.Email = currentAdmin.Email
|
||||
}
|
||||
if nextAdmin.Websocket == nil {
|
||||
nextAdmin.Websocket = currentAdmin.Websocket
|
||||
}
|
||||
if nextAdmin.Mq == nil {
|
||||
nextAdmin.Mq = currentAdmin.Mq
|
||||
}
|
||||
nextAdmin.ConfigPath = currentAdmin.ConfigPath
|
||||
candidateStorage, err := storage.New(nextAdmin)
|
||||
if err != nil {
|
||||
@@ -119,6 +137,12 @@ func (d *Data) PersistRuntimeConfig(ctx context.Context, dataRaw, adminRaw []byt
|
||||
if err := d.persistEmailIntegrationConfig(ctx, nextAdmin.Email); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.PersistWebSocketConfig(ctx, nextAdmin.Websocket); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.persistMQIntegrationConfig(ctx, nextAdmin.Mq); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := d.persistConfigValues(nextData, nextAdmin); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -205,6 +229,14 @@ func (d *Data) InitializeDatabase(ctx context.Context, input *biz.DatabaseConfig
|
||||
if err != nil {
|
||||
return fmt.Errorf("initialize email integration configuration: %w", err)
|
||||
}
|
||||
var legacyWebSocket *conf.AdminBackend_WebSocket
|
||||
if currentAdmin != nil {
|
||||
legacyWebSocket = currentAdmin.Websocket
|
||||
}
|
||||
websocketConfig, err := resolveWebSocketIntegrationConfig(candidate.WithContext(ctx), legacyWebSocket)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initialize websocket integration configuration: %w", err)
|
||||
}
|
||||
signingKey := uuid.NewString()
|
||||
if err := d.persistDatabaseConfig(config, signingKey); err != nil {
|
||||
return fmt.Errorf("persist database configuration: %w", err)
|
||||
@@ -219,7 +251,13 @@ func (d *Data) InitializeDatabase(ctx context.Context, input *biz.DatabaseConfig
|
||||
}
|
||||
currentAdmin.Jwt.SigningKey = signingKey
|
||||
currentAdmin.Storage = storageConfig
|
||||
currentAdmin.Websocket = websocketConfig
|
||||
currentAdmin.Email = emailConfig
|
||||
mqConfig, err := resolveMQIntegrationConfig(candidate.WithContext(ctx), currentAdmin.Mq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initialize mq integration configuration: %w", err)
|
||||
}
|
||||
currentAdmin.Mq = mqConfig
|
||||
d.runtime.Replace(currentData, currentAdmin)
|
||||
activated = true
|
||||
return nil
|
||||
|
||||
@@ -19,6 +19,7 @@ const (
|
||||
integrationKindStorage = "storage"
|
||||
integrationKindEmail = "email"
|
||||
integrationKindPayment = "payment"
|
||||
integrationKindMQ = "mq"
|
||||
)
|
||||
|
||||
// integrationConfigPO stores credentials and provider-specific options for
|
||||
@@ -34,6 +35,88 @@ type integrationConfigPO struct {
|
||||
Config string `gorm:"type:text;not null"`
|
||||
}
|
||||
|
||||
func defaultMQIntegrationConfig() *conf.AdminBackend_MQ {
|
||||
return &conf.AdminBackend_MQ{CleanSession: true, KeepAlive: 30, ConnectTimeout: 10}
|
||||
}
|
||||
|
||||
func saveMQIntegrationConfig(db *gorm.DB, config *conf.AdminBackend_MQ) error {
|
||||
if config == nil {
|
||||
config = defaultMQIntegrationConfig()
|
||||
}
|
||||
raw, err := protojson.MarshalOptions{UseProtoNames: true, EmitDefaultValues: true}.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode emqx integration configuration: %w", err)
|
||||
}
|
||||
enabled := config.Enabled && strings.TrimSpace(config.Broker) != ""
|
||||
clean := db.Session(&gorm.Session{NewDB: true})
|
||||
var row integrationConfigPO
|
||||
err = clean.Where("kind = ? AND provider = ?", integrationKindMQ, "emqx").First(&row).Error
|
||||
switch {
|
||||
case errors.Is(err, gorm.ErrRecordNotFound):
|
||||
return clean.Create(&integrationConfigPO{Kind: integrationKindMQ, Provider: "emqx", Enabled: enabled, Config: string(raw)}).Error
|
||||
case err != nil:
|
||||
return err
|
||||
default:
|
||||
return clean.Model(&row).Updates(map[string]any{"enabled": enabled, "config": string(raw)}).Error
|
||||
}
|
||||
}
|
||||
|
||||
func loadMQIntegrationConfig(db *gorm.DB) (*conf.AdminBackend_MQ, bool, error) {
|
||||
var row integrationConfigPO
|
||||
err := db.Session(&gorm.Session{NewDB: true}).Where("kind = ? AND provider = ?", integrationKindMQ, "emqx").First(&row).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if !json.Valid([]byte(row.Config)) {
|
||||
return nil, false, errors.New("invalid emqx integration configuration")
|
||||
}
|
||||
config := defaultMQIntegrationConfig()
|
||||
if err = (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal([]byte(row.Config), config); err != nil {
|
||||
return nil, false, fmt.Errorf("decode emqx integration configuration: %w", err)
|
||||
}
|
||||
config.Enabled = row.Enabled
|
||||
return config, true, nil
|
||||
}
|
||||
|
||||
func resolveMQIntegrationConfig(db *gorm.DB, legacy *conf.AdminBackend_MQ) (*conf.AdminBackend_MQ, error) {
|
||||
clean := db.Session(&gorm.Session{NewDB: true})
|
||||
if !clean.Migrator().HasTable(&integrationConfigPO{}) {
|
||||
if legacy == nil {
|
||||
return defaultMQIntegrationConfig(), nil
|
||||
}
|
||||
return proto.Clone(legacy).(*conf.AdminBackend_MQ), nil
|
||||
}
|
||||
loaded, found, err := loadMQIntegrationConfig(clean)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if found {
|
||||
return loaded, nil
|
||||
}
|
||||
if legacy == nil {
|
||||
legacy = defaultMQIntegrationConfig()
|
||||
}
|
||||
if err = saveMQIntegrationConfig(clean, legacy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
loaded, _, err = loadMQIntegrationConfig(clean)
|
||||
return loaded, err
|
||||
}
|
||||
|
||||
func (d *Data) persistMQIntegrationConfig(ctx context.Context, config *conf.AdminBackend_MQ) error {
|
||||
if !d.databaseReady.Load() {
|
||||
return errors.New("database is not initialized")
|
||||
}
|
||||
db := d.gormDB.WithContext(ctx)
|
||||
if !db.Migrator().HasTable(&integrationConfigPO{}) {
|
||||
return errors.New("integration configuration table does not exist")
|
||||
}
|
||||
return saveMQIntegrationConfig(db, config)
|
||||
}
|
||||
|
||||
func (integrationConfigPO) TableName() string { return "sys_integration_configs" }
|
||||
|
||||
var storageProviderNames = []string{
|
||||
|
||||
@@ -123,6 +123,25 @@ func TestStorageIntegrationConfigRoundTrip(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestMQIntegrationConfigRoundTrip(t *testing.T) {
|
||||
db := openIntegrationConfigTestDB(t)
|
||||
legacy := &conf.AdminBackend_MQ{Enabled: true, Broker: "mqtt://emqx.example.com:1883", ClientId: "system", Username: "app", Password: "secret", KeepAlive: 45, CleanSession: true, ConnectTimeout: 12}
|
||||
if err := saveMQIntegrationConfig(db, legacy); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
loaded, found, err := loadMQIntegrationConfig(db)
|
||||
if err != nil || !found {
|
||||
t.Fatalf("found=%v err=%v", found, err)
|
||||
}
|
||||
if loaded.Broker != legacy.Broker || loaded.Password != legacy.Password || loaded.KeepAlive != 45 {
|
||||
t.Fatalf("loaded mq = %#v", loaded)
|
||||
}
|
||||
loaded, err = resolveMQIntegrationConfig(db, &conf.AdminBackend_MQ{Broker: "must-not-replace"})
|
||||
if err != nil || loaded.Broker != legacy.Broker {
|
||||
t.Fatalf("database mq was replaced: %#v err=%v", loaded, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveStorageIntegrationConfigMigratesLegacyOnlyOnce(t *testing.T) {
|
||||
db := openIntegrationConfigTestDB(t)
|
||||
legacy := &conf.AdminBackend_Storage{
|
||||
@@ -186,7 +205,7 @@ func TestResolveEmailIntegrationConfigMigratesLegacyOnlyOnce(t *testing.T) {
|
||||
|
||||
func TestPersistConfigValuesRemovesStorageFromYAML(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "config.yaml")
|
||||
input := []byte("data: {}\nadmin:\n router_prefix: /old\n storage:\n type: qiniu\n qiniu:\n secret_key: legacy-secret\n email:\n host: smtp.legacy.example.com\n secret: legacy-email-secret\n extension_key: retained\n")
|
||||
input := []byte("data: {}\nadmin:\n router_prefix: /old\n storage:\n type: qiniu\n qiniu:\n secret_key: legacy-secret\n email:\n host: smtp.legacy.example.com\n secret: legacy-email-secret\n mq:\n enabled: true\n broker: tcp://localhost:1883\n password: legacy-mq-secret\n extension_key: retained\n")
|
||||
if err := os.WriteFile(path, input, 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -199,6 +218,7 @@ func TestPersistConfigValuesRemovesStorageFromYAML(t *testing.T) {
|
||||
Qiniu: &conf.AdminBackend_Qiniu{SecretKey: "database-only-secret"},
|
||||
},
|
||||
Email: &conf.AdminBackend_Email{Host: "smtp.database.example.com", Secret: "database-only-email-secret"},
|
||||
Mq: &conf.AdminBackend_MQ{Enabled: true, Broker: "tcp://emqx:1883", Password: "database-only-mq-secret"},
|
||||
}
|
||||
if err := d.persistConfigValues(&conf.Data{}, admin); err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -222,6 +242,9 @@ func TestPersistConfigValuesRemovesStorageFromYAML(t *testing.T) {
|
||||
if _, exists := adminValue["email"]; exists {
|
||||
t.Fatalf("email remained in YAML: %s", raw)
|
||||
}
|
||||
if _, exists := adminValue["mq"]; exists {
|
||||
t.Fatalf("mq remained in YAML: %s", raw)
|
||||
}
|
||||
if adminValue["extension_key"] != "retained" {
|
||||
t.Fatalf("extension key was not retained: %#v", adminValue)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
|
||||
"kra/app/system/internal/biz"
|
||||
"kra/app/system/internal/conf"
|
||||
"kra/app/system/internal/security/adminauth"
|
||||
"kra/app/system/internal/security"
|
||||
|
||||
jwt "github.com/golang-jwt/jwt/v5"
|
||||
)
|
||||
@@ -95,7 +95,7 @@ func (i *tokenIssuer) IssueToken(user *biz.User, authorityID uint, mustChangePas
|
||||
if user.PasswordUpdatedAt != nil {
|
||||
passwordVersion = user.PasswordUpdatedAt.UnixNano()
|
||||
}
|
||||
token, claims, err := adminauth.Generate(settings.SigningKey, settings.Issuer, expires, settings.Buffer, user.ID, authorityID, user.UUID, user.Username, user.NickName, mustChangePassword, passwordVersion)
|
||||
token, claims, err := security.Generate(settings.SigningKey, settings.Issuer, expires, settings.Buffer, user.ID, authorityID, user.UUID, user.Username, user.NickName, mustChangePassword, passwordVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -107,7 +107,7 @@ func (i *tokenIssuer) ReissueToken(source *biz.AuthClaims, authorityID uint) (*b
|
||||
return nil, errors.New("nil JWT claims")
|
||||
}
|
||||
settings := i.settings.JWTSettings()
|
||||
claims := &adminauth.Claims{
|
||||
claims := &security.Claims{
|
||||
UUID: source.UUID, ID: source.ID, Username: source.Username, NickName: source.NickName,
|
||||
AuthorityID: authorityID, BufferTime: int64(source.BufferTime / time.Second), UserType: source.UserType,
|
||||
MustChangePwd: source.MustChangePwd, PasswordVersion: source.PasswordVersion,
|
||||
@@ -116,7 +116,7 @@ func (i *tokenIssuer) ReissueToken(source *biz.AuthClaims, authorityID uint) (*b
|
||||
IssuedAt: jwt.NewNumericDate(source.IssuedAt), NotBefore: jwt.NewNumericDate(source.NotBefore), ExpiresAt: jwt.NewNumericDate(source.ExpiresAt),
|
||||
},
|
||||
}
|
||||
token, err := adminauth.Sign(settings.SigningKey, claims)
|
||||
token, err := security.Sign(settings.SigningKey, claims)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -124,16 +124,16 @@ func (i *tokenIssuer) ReissueToken(source *biz.AuthClaims, authorityID uint) (*b
|
||||
}
|
||||
|
||||
func (i *tokenIssuer) ParseToken(token string) (*biz.AuthClaims, error) {
|
||||
claims, err := adminauth.Parse(token, i.settings.JWTSettings().SigningKey)
|
||||
claims, err := security.Parse(token, i.settings.JWTSettings().SigningKey)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, adminauth.ErrTokenExpired):
|
||||
case errors.Is(err, security.ErrTokenExpired):
|
||||
return nil, biz.ErrTokenExpired
|
||||
case errors.Is(err, adminauth.ErrTokenMalformed):
|
||||
case errors.Is(err, security.ErrTokenMalformed):
|
||||
return nil, biz.ErrTokenMalformed
|
||||
case errors.Is(err, adminauth.ErrTokenSignatureInvalid):
|
||||
case errors.Is(err, security.ErrTokenSignatureInvalid):
|
||||
return nil, biz.ErrTokenSignatureInvalid
|
||||
case errors.Is(err, adminauth.ErrTokenNotValidYet):
|
||||
case errors.Is(err, security.ErrTokenNotValidYet):
|
||||
return nil, biz.ErrTokenNotValidYet
|
||||
default:
|
||||
return nil, biz.ErrTokenInvalid
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"kra/app/system/internal/conf"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const integrationKindWebSocket = "websocket"
|
||||
|
||||
// saveWebSocketIntegrationConfig persists the Melody settings in the shared
|
||||
// integration table. It intentionally lives separately from storage/payment
|
||||
// persistence so adding another transport does not expand their API surface.
|
||||
func saveWebSocketIntegrationConfig(db *gorm.DB, config *conf.AdminBackend_WebSocket) error {
|
||||
if config == nil {
|
||||
config = &conf.AdminBackend_WebSocket{}
|
||||
}
|
||||
raw, err := protojson.MarshalOptions{UseProtoNames: true, EmitDefaultValues: true}.Marshal(config)
|
||||
if err != nil {
|
||||
return fmt.Errorf("encode websocket integration configuration: %w", err)
|
||||
}
|
||||
clean := db.Session(&gorm.Session{NewDB: true})
|
||||
var current integrationConfigPO
|
||||
err = clean.Where("kind = ? AND provider = ?", integrationKindWebSocket, "melody").First(¤t).Error
|
||||
switch {
|
||||
case errors.Is(err, gorm.ErrRecordNotFound):
|
||||
return clean.Create(&integrationConfigPO{Kind: integrationKindWebSocket, Provider: "melody", Enabled: config.Enabled, Config: string(raw)}).Error
|
||||
case err != nil:
|
||||
return err
|
||||
default:
|
||||
return clean.Model(¤t).Updates(map[string]any{"enabled": config.Enabled, "config": string(raw)}).Error
|
||||
}
|
||||
}
|
||||
|
||||
func loadWebSocketIntegrationConfig(db *gorm.DB) (*conf.AdminBackend_WebSocket, bool, error) {
|
||||
var row integrationConfigPO
|
||||
err := db.Session(&gorm.Session{NewDB: true}).Where("kind = ? AND provider = ?", integrationKindWebSocket, "melody").First(&row).Error
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if !json.Valid([]byte(row.Config)) {
|
||||
return nil, false, errors.New("invalid websocket integration configuration")
|
||||
}
|
||||
config := &conf.AdminBackend_WebSocket{}
|
||||
if err = (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal([]byte(row.Config), config); err != nil {
|
||||
return nil, false, fmt.Errorf("decode websocket integration configuration: %w", err)
|
||||
}
|
||||
config.Enabled = row.Enabled
|
||||
return config, true, nil
|
||||
}
|
||||
|
||||
func resolveWebSocketIntegrationConfig(db *gorm.DB, legacy *conf.AdminBackend_WebSocket) (*conf.AdminBackend_WebSocket, error) {
|
||||
clean := db.Session(&gorm.Session{NewDB: true})
|
||||
if !clean.Migrator().HasTable(&integrationConfigPO{}) {
|
||||
if legacy == nil {
|
||||
return &conf.AdminBackend_WebSocket{}, nil
|
||||
}
|
||||
return proto.Clone(legacy).(*conf.AdminBackend_WebSocket), nil
|
||||
}
|
||||
config, found, err := loadWebSocketIntegrationConfig(clean)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if found {
|
||||
return config, nil
|
||||
}
|
||||
if legacy == nil {
|
||||
legacy = &conf.AdminBackend_WebSocket{}
|
||||
}
|
||||
if err = saveWebSocketIntegrationConfig(clean, legacy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config, _, err = loadWebSocketIntegrationConfig(clean)
|
||||
return config, err
|
||||
}
|
||||
|
||||
func (d *Data) PersistWebSocketConfig(ctx context.Context, config *conf.AdminBackend_WebSocket) error {
|
||||
if !d.databaseReady.Load() {
|
||||
return errors.New("database is not initialized")
|
||||
}
|
||||
db := d.gormDB.WithContext(ctx)
|
||||
if !db.Migrator().HasTable(&integrationConfigPO{}) {
|
||||
return errors.New("integration configuration table does not exist")
|
||||
}
|
||||
return saveWebSocketIntegrationConfig(db, config)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package data
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/conf"
|
||||
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
)
|
||||
|
||||
func TestWebSocketIntegrationConfigRoundTrip(t *testing.T) {
|
||||
db := openIntegrationConfigTestDB(t)
|
||||
want := &conf.AdminBackend_WebSocket{
|
||||
Enabled: true, Path: "/events", AllowOrigins: []string{"https://admin.example.com"},
|
||||
MaxMessageSize: 4096, WriteWait: durationpb.New(3 * time.Second),
|
||||
PongWait: durationpb.New(20 * time.Second), PingPeriod: durationpb.New(15 * time.Second),
|
||||
MessageBufferSize: 32, ConcurrentMessageHandling: true,
|
||||
}
|
||||
if err := saveWebSocketIntegrationConfig(db, want); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, found, err := loadWebSocketIntegrationConfig(db)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("websocket integration configuration was not found")
|
||||
}
|
||||
if !got.Enabled || got.Path != want.Path || got.MaxMessageSize != want.MaxMessageSize || got.MessageBufferSize != want.MessageBufferSize {
|
||||
t.Fatalf("loaded websocket config = %#v", got)
|
||||
}
|
||||
if len(got.AllowOrigins) != 1 || got.AllowOrigins[0] != want.AllowOrigins[0] {
|
||||
t.Fatalf("allow origins = %v", got.AllowOrigins)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveWebSocketIntegrationConfigPrefersDatabase(t *testing.T) {
|
||||
db := openIntegrationConfigTestDB(t)
|
||||
if err := saveWebSocketIntegrationConfig(db, &conf.AdminBackend_WebSocket{Enabled: true, Path: "/database"}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := resolveWebSocketIntegrationConfig(db, &conf.AdminBackend_WebSocket{Path: "/legacy"})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.Path != "/database" || !got.Enabled {
|
||||
t.Fatalf("resolved websocket config = %#v", got)
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"kra/app/system/internal/conf"
|
||||
"kra/app/system/internal/utils/configutil"
|
||||
"kra/pkg/protoutil"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/proto"
|
||||
@@ -89,6 +89,16 @@ func (r *Repo) ConfigurationJSON() (json.RawMessage, error) {
|
||||
if adminConfig.App != nil {
|
||||
admin["app"] = adminConfig.App
|
||||
}
|
||||
if adminConfig.Websocket != nil {
|
||||
admin["websocket"] = adminConfig.Websocket
|
||||
}
|
||||
if adminConfig.Mq != nil {
|
||||
mqConfig := proto.Clone(adminConfig.Mq).(*conf.AdminBackend_MQ)
|
||||
if mqConfig.Password != "" {
|
||||
mqConfig.Password = "******"
|
||||
}
|
||||
admin["mq"] = mqConfig
|
||||
}
|
||||
}
|
||||
// Never mask secrets on the live runtime object. ConfigurationJSON is a
|
||||
// read-only operation; mutating dataConfig here would replace the actual
|
||||
@@ -105,6 +115,9 @@ func (r *Repo) ConfigurationJSON() (json.RawMessage, error) {
|
||||
safeAdmin.Email.Secret = "******"
|
||||
}
|
||||
maskStorageSecrets(safeAdmin.Storage)
|
||||
if safeAdmin.Mq != nil && safeAdmin.Mq.Password != "" {
|
||||
safeAdmin.Mq.Password = "******"
|
||||
}
|
||||
}
|
||||
dataMap := map[string]any{}
|
||||
if safeData != nil {
|
||||
@@ -137,12 +150,12 @@ func (r *Repo) SaveConfigurationJSON(ctx context.Context, raw json.RawMessage) e
|
||||
}
|
||||
options := protojson.UnmarshalOptions{DiscardUnknown: true}
|
||||
if len(value.Data) > 0 && string(value.Data) != "null" {
|
||||
if err := configutil.MergeProtoJSON(nextData, value.Data, options); err != nil {
|
||||
if err := protoutil.MergeProtoJSON(nextData, value.Data, options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(value.Admin) > 0 && string(value.Admin) != "null" {
|
||||
if err := configutil.MergeProtoJSON(nextAdmin, value.Admin, options); err != nil {
|
||||
if err := protoutil.MergeProtoJSON(nextAdmin, value.Admin, options); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -307,6 +320,9 @@ func preserveAdminSecrets(next, current *conf.AdminBackend) {
|
||||
next.Email.Secret = current.Email.Secret
|
||||
}
|
||||
preserveStorageSecrets(next.Storage, current.Storage)
|
||||
if next.Mq != nil && current.Mq != nil && maskedSecret(next.Mq.Password) {
|
||||
next.Mq.Password = current.Mq.Password
|
||||
}
|
||||
}
|
||||
|
||||
func maskedSecret(value string) bool { return value == "" || value == "******" }
|
||||
|
||||
+3
-2
@@ -1,10 +1,11 @@
|
||||
package configutil
|
||||
package initialize
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"kra/app/system/internal/conf"
|
||||
"kra/pkg/protoutil"
|
||||
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
)
|
||||
@@ -16,7 +17,7 @@ func TestMergeProtoJSONPreservesOmittedFieldsAndNormalizesKeys(t *testing.T) {
|
||||
}
|
||||
patch := json.RawMessage(`{"router_prefix":"/admin","system":{"iplimit_count":9}}`)
|
||||
|
||||
if err := MergeProtoJSON(target, patch, protojson.UnmarshalOptions{DiscardUnknown: true}); err != nil {
|
||||
if err := protoutil.MergeProtoJSON(target, patch, protojson.UnmarshalOptions{DiscardUnknown: true}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if target.RouterPrefix != "/admin" || target.System.IplimitCount != 9 {
|
||||
@@ -8,8 +8,11 @@ state, or translate provider-specific protocols into `biz` interfaces.
|
||||
|
||||
- `cache`: Redis-backed cache with an in-memory fallback.
|
||||
- `email`: SMTP email repository.
|
||||
- `mq`: reloadable EMQX/MQTT client exposed through the shared `pkg/mq` interface.
|
||||
- `payment`: payment-channel SDKs and callback/signature handling.
|
||||
- `storage`: local and object-storage implementations of `biz.FileStorage`.
|
||||
- `websocket`: reloadable Melody endpoint exposed through the shared
|
||||
`pkg/websocket` wrapper.
|
||||
|
||||
These packages are not `utils`: they perform I/O and own external dependency
|
||||
lifecycles. Their constructors are exposed through `ProviderSet`; `cmd` binds
|
||||
@@ -18,3 +21,31 @@ the `cache.RedisProvider` implementation to the shared `data.Data` container.
|
||||
Stateless protocol helpers that do not own clients live in
|
||||
`pkg/paymentkit`. They are intentionally small and dependency
|
||||
light, while provider adapters remain here.
|
||||
|
||||
## Shared WebSocket and MQ APIs
|
||||
|
||||
Business modules depend on `websocket.Hub` and `mq.Client` from the shared
|
||||
`pkg/websocket` and `pkg/mq` packages; they do not construct
|
||||
Melody or Paho clients and do not read system configuration directly. The
|
||||
system integration packages own runtime refresh and shutdown. Registered
|
||||
WebSocket handlers and MQTT subscriptions are retained when database-backed
|
||||
configuration replaces a live client.
|
||||
|
||||
```go
|
||||
ws.OnMessage(func(session *websocket.Session, payload []byte) {
|
||||
_ = ws.Send(session, payload)
|
||||
})
|
||||
_ = ws.Broadcast([]byte(`{"type":"system.ready"}`))
|
||||
|
||||
_ = mq.PublishJSON(ctx, mqClient, "orders/paid", event, mq.AtLeastOnce, false)
|
||||
_ = mqClient.Subscribe(ctx, "orders/+/paid", mq.AtLeastOnce, handler)
|
||||
```
|
||||
|
||||
Integration configuration is stored in `sys_integration_configs`:
|
||||
|
||||
- WebSocket: `kind=websocket`, `provider=melody`
|
||||
- EMQX: `kind=mq`, `provider=emqx`
|
||||
|
||||
The YAML values are only migration/bootstrap inputs. After the integration
|
||||
table exists, the database is authoritative and runtime updates are applied
|
||||
without restarting the process. The WebSocket public path defaults to `/ws`.
|
||||
|
||||
@@ -0,0 +1,154 @@
|
||||
package mq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/conf"
|
||||
"kra/pkg/mq"
|
||||
)
|
||||
|
||||
// Reloadable follows the runtime snapshot and keeps a single shared EMQX
|
||||
// connection for all modules in this process.
|
||||
type Reloadable struct {
|
||||
mu sync.RWMutex
|
||||
opMu sync.Mutex
|
||||
current mq.Client
|
||||
subscriptions map[string]subscription
|
||||
stop func()
|
||||
logger *slog.Logger
|
||||
closed bool
|
||||
}
|
||||
|
||||
type subscription struct {
|
||||
qos byte
|
||||
handler mq.Handler
|
||||
}
|
||||
|
||||
func New(runtime *conf.Runtime, logger *slog.Logger) (*Reloadable, func(), error) {
|
||||
if logger == nil {
|
||||
logger = slog.Default()
|
||||
}
|
||||
r := &Reloadable{logger: logger, subscriptions: make(map[string]subscription)}
|
||||
if runtime != nil {
|
||||
var config *conf.AdminBackend_MQ
|
||||
if admin := runtime.Admin(); admin != nil {
|
||||
config = admin.GetMq()
|
||||
}
|
||||
r.replace(config)
|
||||
r.stop = runtime.Subscribe(func(_ *conf.Data, admin *conf.AdminBackend) {
|
||||
if admin != nil {
|
||||
r.replace(admin.GetMq())
|
||||
}
|
||||
})
|
||||
}
|
||||
cleanup := func() {
|
||||
if r.stop != nil {
|
||||
r.stop()
|
||||
}
|
||||
_ = r.Close()
|
||||
}
|
||||
return r, cleanup, nil
|
||||
}
|
||||
|
||||
func (r *Reloadable) replace(config *conf.AdminBackend_MQ) {
|
||||
r.opMu.Lock()
|
||||
defer r.opMu.Unlock()
|
||||
if r.closed {
|
||||
return
|
||||
}
|
||||
if config == nil {
|
||||
config = &conf.AdminBackend_MQ{}
|
||||
}
|
||||
cfg := mq.Config{Enabled: config.Enabled, Broker: config.Broker, ClientID: config.ClientId, Username: config.Username, Password: config.Password, CleanSession: config.CleanSession}
|
||||
if config.KeepAlive > 0 {
|
||||
cfg.KeepAlive = time.Duration(config.KeepAlive) * time.Second
|
||||
}
|
||||
if config.ConnectTimeout > 0 {
|
||||
cfg.ConnectTimeout = time.Duration(config.ConnectTimeout) * time.Second
|
||||
}
|
||||
client, err := mq.NewMQTT(cfg)
|
||||
if err != nil {
|
||||
r.logger.Warn("emqx unavailable", "mod", "mq", "error", err)
|
||||
return
|
||||
}
|
||||
if config.Enabled {
|
||||
if err = r.restoreSubscriptions(context.Background(), client); err != nil {
|
||||
_ = client.Close()
|
||||
r.logger.Warn("restore emqx subscriptions failed", "mod", "mq", "error", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
r.mu.Lock()
|
||||
old := r.current
|
||||
r.current = client
|
||||
r.mu.Unlock()
|
||||
if old != nil {
|
||||
_ = old.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reloadable) restoreSubscriptions(ctx context.Context, client mq.Client) error {
|
||||
for topic, item := range r.subscriptions {
|
||||
if err := client.Subscribe(ctx, topic, item.qos, item.handler); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Reloadable) client() mq.Client { r.mu.RLock(); defer r.mu.RUnlock(); return r.current }
|
||||
func (r *Reloadable) Publish(ctx context.Context, topic string, payload []byte, qos byte, retain bool) error {
|
||||
c := r.client()
|
||||
if c == nil {
|
||||
return mq.ErrUnavailable
|
||||
}
|
||||
return c.Publish(ctx, topic, payload, qos, retain)
|
||||
}
|
||||
func (r *Reloadable) Subscribe(ctx context.Context, topic string, qos byte, handler mq.Handler) error {
|
||||
r.opMu.Lock()
|
||||
defer r.opMu.Unlock()
|
||||
c := r.client()
|
||||
if c == nil {
|
||||
return mq.ErrUnavailable
|
||||
}
|
||||
if err := c.Subscribe(ctx, topic, qos, handler); err != nil {
|
||||
return err
|
||||
}
|
||||
r.subscriptions[topic] = subscription{qos: qos, handler: handler}
|
||||
return nil
|
||||
}
|
||||
func (r *Reloadable) Unsubscribe(ctx context.Context, topics ...string) error {
|
||||
r.opMu.Lock()
|
||||
defer r.opMu.Unlock()
|
||||
c := r.client()
|
||||
if c == nil {
|
||||
return mq.ErrUnavailable
|
||||
}
|
||||
if err := c.Unsubscribe(ctx, topics...); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, topic := range topics {
|
||||
delete(r.subscriptions, topic)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (r *Reloadable) Connected() bool { c := r.client(); return c != nil && c.Connected() }
|
||||
func (r *Reloadable) Close() error {
|
||||
r.opMu.Lock()
|
||||
defer r.opMu.Unlock()
|
||||
if r.closed {
|
||||
return nil
|
||||
}
|
||||
r.closed = true
|
||||
r.mu.Lock()
|
||||
old := r.current
|
||||
r.current = nil
|
||||
r.mu.Unlock()
|
||||
if old != nil {
|
||||
return old.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package mq
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
platformmq "kra/pkg/mq"
|
||||
)
|
||||
|
||||
type fakeClient struct {
|
||||
subscribed []string
|
||||
unsubscribed []string
|
||||
}
|
||||
|
||||
func (*fakeClient) Publish(context.Context, string, []byte, byte, bool) error { return nil }
|
||||
func (f *fakeClient) Subscribe(_ context.Context, topic string, _ byte, _ platformmq.Handler) error {
|
||||
f.subscribed = append(f.subscribed, topic)
|
||||
return nil
|
||||
}
|
||||
func (f *fakeClient) Unsubscribe(_ context.Context, topics ...string) error {
|
||||
f.unsubscribed = append(f.unsubscribed, topics...)
|
||||
return nil
|
||||
}
|
||||
func (*fakeClient) Connected() bool { return true }
|
||||
func (*fakeClient) Close() error { return nil }
|
||||
|
||||
func TestReloadableTracksSubscriptions(t *testing.T) {
|
||||
client := &fakeClient{}
|
||||
r := &Reloadable{current: client, subscriptions: make(map[string]subscription)}
|
||||
handler := func(context.Context, platformmq.Message) {}
|
||||
if err := r.Subscribe(context.Background(), "orders/+/paid", platformmq.AtLeastOnce, handler); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := r.subscriptions["orders/+/paid"]; !ok {
|
||||
t.Fatal("subscription was not retained for configuration reload")
|
||||
}
|
||||
if err := r.Unsubscribe(context.Background(), "orders/+/paid"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := r.subscriptions["orders/+/paid"]; ok {
|
||||
t.Fatal("unsubscribed topic remained in the reload registry")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReloadableRestoresSubscriptions(t *testing.T) {
|
||||
client := &fakeClient{}
|
||||
r := &Reloadable{subscriptions: map[string]subscription{
|
||||
"orders/+/paid": {qos: platformmq.AtLeastOnce, handler: func(context.Context, platformmq.Message) {}},
|
||||
}}
|
||||
if err := r.restoreSubscriptions(context.Background(), client); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(client.subscribed) != 1 || client.subscribed[0] != "orders/+/paid" {
|
||||
t.Fatalf("restored subscriptions = %v", client.subscribed)
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,11 @@ import (
|
||||
"kra/app/system/internal/biz"
|
||||
"kra/app/system/internal/integration/cache"
|
||||
"kra/app/system/internal/integration/email"
|
||||
mqintegration "kra/app/system/internal/integration/mq"
|
||||
"kra/app/system/internal/integration/storage"
|
||||
websocketintegration "kra/app/system/internal/integration/websocket"
|
||||
"kra/pkg/mq"
|
||||
platformws "kra/pkg/websocket"
|
||||
|
||||
"github.com/google/wire"
|
||||
)
|
||||
@@ -16,5 +20,9 @@ var ProviderSet = wire.NewSet(
|
||||
cache.New,
|
||||
email.NewEmailRepo,
|
||||
storage.NewFileStorage,
|
||||
mqintegration.New,
|
||||
wire.Bind(new(mq.Client), new(*mqintegration.Reloadable)),
|
||||
websocketintegration.New,
|
||||
wire.Bind(new(platformws.Hub), new(*websocketintegration.Server)),
|
||||
wire.Bind(new(biz.FileStorage), new(*storage.Reloadable)),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
melody "github.com/olahol/melody"
|
||||
"kra/app/system/internal/conf"
|
||||
platformws "kra/pkg/websocket"
|
||||
)
|
||||
|
||||
// Server is the system-owned WebSocket endpoint. Business modules can attach
|
||||
// handlers and publish messages without depending on Gin or Melody directly.
|
||||
type Server struct {
|
||||
mu sync.RWMutex
|
||||
current *platformws.Server
|
||||
path string
|
||||
messageHandlers []func(*melody.Session, []byte)
|
||||
binaryHandlers []func(*melody.Session, []byte)
|
||||
connectHandlers []func(*melody.Session)
|
||||
disconnectHandlers []func(*melody.Session)
|
||||
closed bool
|
||||
}
|
||||
|
||||
func New(runtime *conf.Runtime) (*Server, func(), error) {
|
||||
s := &Server{}
|
||||
s.Replace(runtime)
|
||||
var unsubscribe func()
|
||||
if runtime != nil {
|
||||
unsubscribe = runtime.Subscribe(func(_ *conf.Data, _ *conf.AdminBackend) { s.Replace(runtime) })
|
||||
}
|
||||
return s, func() {
|
||||
if unsubscribe != nil {
|
||||
unsubscribe()
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.closed = true
|
||||
current := s.current
|
||||
s.current = nil
|
||||
s.mu.Unlock()
|
||||
if current != nil {
|
||||
_ = current.Close()
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) Replace(runtime *conf.Runtime) {
|
||||
var config *conf.AdminBackend_WebSocket
|
||||
if runtime != nil && runtime.Admin() != nil {
|
||||
config = runtime.Admin().Websocket
|
||||
}
|
||||
if config == nil {
|
||||
config = &conf.AdminBackend_WebSocket{}
|
||||
}
|
||||
s.mu.Lock()
|
||||
if s.closed {
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
previous := s.current
|
||||
if !config.Enabled {
|
||||
s.current = nil
|
||||
s.path = ""
|
||||
s.mu.Unlock()
|
||||
if previous != nil {
|
||||
_ = previous.Close()
|
||||
}
|
||||
return
|
||||
}
|
||||
next := platformws.New(platformws.Config{
|
||||
WriteWait: duration(config.WriteWait, 10*time.Second),
|
||||
PongWait: duration(config.PongWait, 60*time.Second),
|
||||
PingPeriod: duration(config.PingPeriod, 54*time.Second),
|
||||
MaxMessageSize: config.MaxMessageSize,
|
||||
MessageBufferSize: int(config.MessageBufferSize),
|
||||
ConcurrentMessageHandling: config.ConcurrentMessageHandling,
|
||||
AllowOrigins: config.AllowOrigins,
|
||||
})
|
||||
for _, handler := range s.messageHandlers {
|
||||
next.OnMessage(handler)
|
||||
}
|
||||
for _, handler := range s.binaryHandlers {
|
||||
next.OnBinaryMessage(handler)
|
||||
}
|
||||
for _, handler := range s.connectHandlers {
|
||||
next.OnConnect(handler)
|
||||
}
|
||||
for _, handler := range s.disconnectHandlers {
|
||||
next.OnDisconnect(handler)
|
||||
}
|
||||
s.path = strings.TrimSpace(config.Path)
|
||||
if s.path == "" {
|
||||
s.path = "/ws"
|
||||
} else if !strings.HasPrefix(s.path, "/") {
|
||||
s.path = "/" + s.path
|
||||
}
|
||||
s.current = next
|
||||
s.mu.Unlock()
|
||||
if previous != nil {
|
||||
_ = previous.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func duration(value interface{ AsDuration() time.Duration }, fallback time.Duration) time.Duration {
|
||||
if value == nil {
|
||||
return fallback
|
||||
}
|
||||
if result := value.AsDuration(); result > 0 {
|
||||
return result
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
func (s *Server) Enabled() bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.current != nil
|
||||
}
|
||||
func (s *Server) Path() string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
return s.path
|
||||
}
|
||||
func (s *Server) HandleRequest(w http.ResponseWriter, r *http.Request) error {
|
||||
if s == nil {
|
||||
return errors.New("websocket server is disabled")
|
||||
}
|
||||
s.mu.RLock()
|
||||
current := s.current
|
||||
s.mu.RUnlock()
|
||||
if current == nil {
|
||||
return errors.New("websocket server is disabled")
|
||||
}
|
||||
return current.HandleRequest(w, r)
|
||||
}
|
||||
func (s *Server) HandleRequestWithKeys(w http.ResponseWriter, r *http.Request, keys map[string]any) error {
|
||||
current, err := s.active()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return current.HandleRequestWithKeys(w, r, keys)
|
||||
}
|
||||
func (s *Server) Broadcast(message []byte) error {
|
||||
if s == nil {
|
||||
return errors.New("websocket server is disabled")
|
||||
}
|
||||
s.mu.RLock()
|
||||
current := s.current
|
||||
s.mu.RUnlock()
|
||||
if current == nil {
|
||||
return errors.New("websocket server is disabled")
|
||||
}
|
||||
return current.Broadcast(message)
|
||||
}
|
||||
func (s *Server) BroadcastBinary(message []byte) error {
|
||||
if s == nil {
|
||||
return errors.New("websocket server is disabled")
|
||||
}
|
||||
s.mu.RLock()
|
||||
current := s.current
|
||||
s.mu.RUnlock()
|
||||
if current == nil {
|
||||
return errors.New("websocket server is disabled")
|
||||
}
|
||||
return current.BroadcastBinary(message)
|
||||
}
|
||||
func (s *Server) Sessions() ([]*melody.Session, error) {
|
||||
current, err := s.active()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return current.Sessions()
|
||||
}
|
||||
func (s *Server) Len() int {
|
||||
current, err := s.active()
|
||||
if err != nil {
|
||||
return 0
|
||||
}
|
||||
return current.Len()
|
||||
}
|
||||
func (s *Server) Send(session *melody.Session, message []byte) error {
|
||||
current, err := s.active()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return current.Send(session, message)
|
||||
}
|
||||
func (s *Server) SendBinary(session *melody.Session, message []byte) error {
|
||||
current, err := s.active()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return current.SendBinary(session, message)
|
||||
}
|
||||
func (s *Server) active() (*platformws.Server, error) {
|
||||
if s == nil {
|
||||
return nil, errors.New("websocket server is disabled")
|
||||
}
|
||||
s.mu.RLock()
|
||||
current := s.current
|
||||
s.mu.RUnlock()
|
||||
if current == nil {
|
||||
return nil, errors.New("websocket server is disabled")
|
||||
}
|
||||
return current, nil
|
||||
}
|
||||
func (s *Server) OnMessage(handler func(*melody.Session, []byte)) {
|
||||
if s == nil || handler == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.messageHandlers = append(s.messageHandlers, handler)
|
||||
current := s.current
|
||||
if current != nil {
|
||||
current.OnMessage(handler)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
func (s *Server) OnBinaryMessage(handler func(*melody.Session, []byte)) {
|
||||
if s == nil || handler == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.binaryHandlers = append(s.binaryHandlers, handler)
|
||||
if s.current != nil {
|
||||
s.current.OnBinaryMessage(handler)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
func (s *Server) OnConnect(handler func(*melody.Session)) {
|
||||
if s == nil || handler == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.connectHandlers = append(s.connectHandlers, handler)
|
||||
if s.current != nil {
|
||||
s.current.OnConnect(handler)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
func (s *Server) OnDisconnect(handler func(*melody.Session)) {
|
||||
if s == nil || handler == nil {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.disconnectHandlers = append(s.disconnectHandlers, handler)
|
||||
if s.current != nil {
|
||||
s.current.OnDisconnect(handler)
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package adminauth
|
||||
package security
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -11,10 +11,12 @@ import (
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/conf"
|
||||
websocket "kra/app/system/internal/integration/websocket"
|
||||
"kra/app/system/internal/server/handler"
|
||||
"kra/app/system/internal/server/httpx"
|
||||
"kra/app/system/internal/server/middleware"
|
||||
"kra/app/system/internal/server/router"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/handler"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
servermiddleware "kra/app/system/internal/transport/middleware"
|
||||
platformmodule "kra/pkg/module"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -22,16 +24,16 @@ import (
|
||||
)
|
||||
|
||||
func NewGinEngine(runtime *conf.Runtime, access *service.AccessControlService, handlers *handler.Set, auth *service.AuthService, security *service.SecurityService, audit *service.AuditRecorder, logger *slog.Logger, version string) *gin.Engine {
|
||||
return NewGinEngineWithRuntime(runtime, access, auth, security, audit, logger, version, platformmodule.NewRuntime(NewRoutes(handlers)))
|
||||
return NewGinEngineWithRuntime(runtime, access, auth, security, audit, logger, version, platformmodule.NewRuntime(router.NewRoutes(handlers)), nil)
|
||||
}
|
||||
|
||||
func NewGinEngineWithRuntime(runtime *conf.Runtime, access *service.AccessControlService, auth *service.AuthService, security *service.SecurityService, audit *service.AuditRecorder, logger *slog.Logger, version string, routes *platformmodule.Runtime) *gin.Engine {
|
||||
func NewGinEngineWithRuntime(runtime *conf.Runtime, access *service.AccessControlService, auth *service.AuthService, security *service.SecurityService, audit *service.AuditRecorder, logger *slog.Logger, version string, routes *platformmodule.Runtime, ws *websocket.Server) *gin.Engine {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
engine := gin.New()
|
||||
if err := engine.SetTrustedProxies(nil); err != nil && logger != nil {
|
||||
logger.Error("disable trusted proxies failed", "error", err)
|
||||
}
|
||||
engine.Use(servermiddleware.RequestMeta(), servermiddleware.Recovery(logger), servermiddleware.AccessLog(runtime, logger, version), servermiddleware.CORS(runtime), servermiddleware.ErrorAudit(logger), servermiddleware.SecurityRateLimit(security))
|
||||
engine.Use(middleware.RequestMeta(), middleware.Recovery(logger), middleware.AccessLog(runtime, logger, version), middleware.CORS(runtime), middleware.ErrorAudit(logger), middleware.SecurityRateLimit(security))
|
||||
|
||||
prefix := ""
|
||||
config := runtime.Admin()
|
||||
@@ -46,14 +48,32 @@ func NewGinEngineWithRuntime(runtime *conf.Runtime, access *service.AccessContro
|
||||
// password and Casbin/data-scope middleware. Keeping the recorder after the
|
||||
// equivalent Kra guards avoids persisting rejected unauthenticated or
|
||||
// unauthorized requests as successful business operations.
|
||||
private.Use(servermiddleware.Auth(auth), servermiddleware.MustChangePassword(), servermiddleware.AccessControl(runtime, access), servermiddleware.OperationAudit(runtime, audit))
|
||||
private.Use(middleware.Auth(auth), middleware.MustChangePassword(), middleware.AccessControl(runtime, access), middleware.OperationAudit(runtime, audit))
|
||||
if routes != nil {
|
||||
routes.RegisterRoutes(public, private, engine)
|
||||
}
|
||||
if ws != nil && ws.Enabled() {
|
||||
path := ws.Path()
|
||||
engine.GET(path, func(c *gin.Context) {
|
||||
if !ws.Enabled() || c.Request.URL.Path != ws.Path() {
|
||||
c.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
if err := ws.HandleRequest(c.Writer, c.Request); err != nil && logger != nil {
|
||||
logger.Warn("websocket request failed", "mod", "websocket", "error", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
registerSwagger(engine, prefix, version, logger)
|
||||
registerLocalStorage(engine, runtime)
|
||||
|
||||
engine.NoRoute(func(c *gin.Context) {
|
||||
if ws != nil && ws.Enabled() && c.Request.Method == http.MethodGet && c.Request.URL.Path == ws.Path() {
|
||||
if err := ws.HandleRequest(c.Writer, c.Request); err != nil && logger != nil {
|
||||
logger.Warn("websocket request failed", "mod", "websocket", "error", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if serveLocalStorage(c, runtime) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"kra/app/system/internal/conf"
|
||||
"kra/app/system/internal/transport/handler"
|
||||
"kra/app/system/internal/server/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -56,7 +56,7 @@ func TestGinStartupLogsEveryRegisteredRoute(t *testing.T) {
|
||||
if got, want := strings.Count(text, `"msg":"router registered"`), len(engine.Routes()); got != want {
|
||||
t.Fatalf("registered route log count = %d, want %d", got, want)
|
||||
}
|
||||
if !strings.Contains(text, `"msg":"router register success"`) || !strings.Contains(text, `"route_count":186`) {
|
||||
if !strings.Contains(text, `"msg":"router register success"`) || !strings.Contains(text, `"route_count":190`) {
|
||||
t.Fatalf("startup route summary is missing: %s", text)
|
||||
}
|
||||
}
|
||||
@@ -258,6 +258,7 @@ GET /logViewer/files
|
||||
GET /menu/getMenuRoles
|
||||
GET /payment/configs
|
||||
GET /payment/orders
|
||||
GET /payment/orders/:provider/:tradeNo
|
||||
GET /position/findPosition
|
||||
GET /position/getPositionUsers
|
||||
GET /securityConfig/getSecurityConfig
|
||||
@@ -353,7 +354,10 @@ POST /menu/updateBaseMenu
|
||||
POST /payment/callback/:provider
|
||||
POST /payment/config
|
||||
POST /payment/create
|
||||
POST /payment/fulfill
|
||||
POST /payment/order
|
||||
POST /payment/orders/:provider/:tradeNo/fulfill
|
||||
POST /payment/orders/:provider/:tradeNo/refund
|
||||
POST /payment/query
|
||||
POST /payment/refund
|
||||
POST /position/createPosition
|
||||
|
||||
+23
-26
@@ -1,11 +1,8 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -16,86 +13,86 @@ func NewAnnouncement(service *service.AnnouncementService) *Announcement {
|
||||
return &Announcement{service: service}
|
||||
}
|
||||
|
||||
func announcementInput(req dto.AnnouncementRequest) service.AnnouncementInput {
|
||||
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 dto.AnnouncementRequest
|
||||
var req service.AnnouncementRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.Create(c.Request.Context(), announcementInput(req)); err != nil {
|
||||
httpx.Fail(c, "创建失败")
|
||||
Fail(c, "创建失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建成功")
|
||||
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 {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
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 {
|
||||
httpx.Fail(c, "批量删除失败")
|
||||
Fail(c, "批量删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "批量删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
|
||||
}
|
||||
|
||||
func (h *Announcement) Update(c *gin.Context) {
|
||||
var req dto.AnnouncementRequest
|
||||
var req service.AnnouncementRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.Update(c.Request.Context(), announcementInput(req)); err != nil {
|
||||
httpx.Fail(c, "更新失败")
|
||||
Fail(c, "更新失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
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 {
|
||||
httpx.Fail(c, "查询失败")
|
||||
Fail(c, "查询失败")
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, item)
|
||||
OKWithData(c, item)
|
||||
}
|
||||
|
||||
func (h *Announcement) List(c *gin.Context) {
|
||||
var req dto.AnnouncementSearchRequest
|
||||
var req service.AnnouncementSearchRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
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 {
|
||||
httpx.Fail(c, "查询失败")
|
||||
Fail(c, "查询失败")
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, gin.H{"userID": users})
|
||||
OKWithData(c, gin.H{"userID": users})
|
||||
}
|
||||
|
||||
func (h *Announcement) Public(c *gin.Context) {
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"info": "不需要鉴权的公告接口信息"}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"info": "不需要鉴权的公告接口信息"}, "获取成功")
|
||||
}
|
||||
|
||||
func parseTime(value string) (*time.Time, error) {
|
||||
+67
-69
@@ -1,9 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -13,234 +11,234 @@ 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 dto.APIListRequest
|
||||
var req service.APIListRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Page == 0 {
|
||||
httpx.Fail(c, "Page值不能为空")
|
||||
Fail(c, "Page值不能为空")
|
||||
return
|
||||
}
|
||||
if req.PageSize == 0 {
|
||||
httpx.Fail(c, "PageSize值不能为空")
|
||||
Fail(c, "PageSize值不能为空")
|
||||
return
|
||||
}
|
||||
items, total, err := h.service.ListAPI(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"apis": items}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"apis": items}, "获取成功")
|
||||
}
|
||||
func (h *API) Create(c *gin.Context) {
|
||||
var req dto.APIRequest
|
||||
var req service.APIRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if message := validateAPIRequest(&req); message != "" {
|
||||
httpx.Fail(c, message)
|
||||
Fail(c, message)
|
||||
return
|
||||
}
|
||||
created, err := h.service.CreateAPIRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "创建失败")
|
||||
Fail(c, "创建失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, created, "创建成功")
|
||||
Write(c, CodeSuccess, created, "创建成功")
|
||||
}
|
||||
func (h *API) Update(c *gin.Context) {
|
||||
var req dto.APIRequest
|
||||
var req service.APIRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if message := validateAPIRequest(&req); message != "" {
|
||||
httpx.Fail(c, message)
|
||||
Fail(c, message)
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateAPIRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "修改失败")
|
||||
Fail(c, "修改失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "修改成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "修改成功")
|
||||
}
|
||||
func (h *API) Delete(c *gin.Context) {
|
||||
var req dto.DeleteAPIRequest
|
||||
var req service.DeleteAPIRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "ID值不能为空")
|
||||
Fail(c, "ID值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteAPI(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *API) DeleteByIDs(c *gin.Context) {
|
||||
var req dto.DeleteAPIsRequest
|
||||
var req service.DeleteAPIsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteAPIs(c.Request.Context(), req.IDs); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *API) Find(c *gin.Context) {
|
||||
var req dto.GetAPIRequest
|
||||
var req service.GetAPIRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "ID值不能为空")
|
||||
Fail(c, "ID值不能为空")
|
||||
return
|
||||
}
|
||||
item, err := h.service.FindAPIResponse(c.Request.Context(), req.ID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"api": item}, "获取成功")
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, gin.H{"groups": groups, "apiGroupMap": groupAPIMap})
|
||||
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 == "" {
|
||||
httpx.Fail(c, "API路径和请求方法不能为空")
|
||||
Fail(c, "API路径和请求方法不能为空")
|
||||
return
|
||||
}
|
||||
ids, err := h.service.APIRoleIDs(c.Request.Context(), path, method)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败"+err.Error())
|
||||
Fail(c, "获取失败"+err.Error())
|
||||
return
|
||||
}
|
||||
if ids == nil {
|
||||
ids = []uint{}
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, ids, "获取成功")
|
||||
Write(c, CodeSuccess, ids, "获取成功")
|
||||
}
|
||||
func (h *API) SetRoles(c *gin.Context) {
|
||||
var req dto.SetAPIRolesRequest
|
||||
var req service.SetAPIRolesRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Path == "" || req.Method == "" {
|
||||
httpx.Fail(c, "API路径和请求方法不能为空")
|
||||
Fail(c, "API路径和请求方法不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.SetAPIRoles(c.Request.Context(), req.Path, req.Method, req.AuthorityIDs); err != nil {
|
||||
httpx.Fail(c, "设置失败"+err.Error())
|
||||
Fail(c, "设置失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
func (h *API) Sync(engine *gin.Engine) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
routes := engine.Routes()
|
||||
values := make([]dto.APIRequest, 0, len(routes))
|
||||
values := make([]service.APIRequest, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
values = append(values, dto.APIRequest{Path: route.Path, Method: route.Method})
|
||||
values = append(values, service.APIRequest{Path: route.Path, Method: route.Method})
|
||||
}
|
||||
result, err := h.service.SyncAPIResponses(c.Request.Context(), values)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "同步失败")
|
||||
Fail(c, "同步失败")
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, result)
|
||||
OKWithData(c, result)
|
||||
}
|
||||
}
|
||||
func (h *API) Ignore(c *gin.Context) {
|
||||
var req dto.IgnoreAPIRequest
|
||||
var req service.IgnoreAPIRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.SetAPIIgnored(c.Request.Context(), req.Path, req.Method, req.Flag); err != nil {
|
||||
httpx.Fail(c, "忽略失败")
|
||||
Fail(c, "忽略失败")
|
||||
return
|
||||
}
|
||||
httpx.OK(c)
|
||||
OK(c)
|
||||
}
|
||||
func (h *API) ApplySync(c *gin.Context) {
|
||||
var req dto.ApplyAPISyncRequest
|
||||
var req service.ApplyAPISyncRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.ApplyAPISyncRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "忽略失败")
|
||||
Fail(c, "忽略失败")
|
||||
return
|
||||
}
|
||||
httpx.OK(c)
|
||||
OK(c)
|
||||
}
|
||||
func (h *API) FreshCasbin(c *gin.Context) {
|
||||
if err := h.service.FreshCasbin(c.Request.Context()); err != nil {
|
||||
httpx.Fail(c, "刷新失败")
|
||||
Fail(c, "刷新失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "刷新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "刷新成功")
|
||||
}
|
||||
|
||||
func (h *API) SetPolicyPaths(c *gin.Context) {
|
||||
var req dto.SetPolicyPathsRequest
|
||||
var req service.SetPolicyPathsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.SetPolicyPathsRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败")
|
||||
Fail(c, "更新失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "更新成功")
|
||||
}
|
||||
func (h *API) PolicyPaths(c *gin.Context) {
|
||||
var req dto.GetPolicyPathsRequest
|
||||
var req service.GetPolicyPathsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
paths, err := h.service.PolicyPathResponses(c.Request.Context(), req.AuthorityID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"paths": paths}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"paths": paths}, "获取成功")
|
||||
}
|
||||
|
||||
func validateAPIRequest(req *dto.APIRequest) string {
|
||||
func validateAPIRequest(req *service.APIRequest) string {
|
||||
switch {
|
||||
case req.Path == "":
|
||||
return "Path值不能为空"
|
||||
+12
-14
@@ -1,9 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -17,42 +15,42 @@ func NewAPIToken(service *service.TokenService) *APIToken {
|
||||
}
|
||||
|
||||
func (h *APIToken) Create(c *gin.Context) {
|
||||
var req dto.CreateAPITokenRequest
|
||||
var req service.CreateAPITokenRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
token, err := h.service.CreateAPIToken(c.Request.Context(), req.UserID, req.AuthorityID, req.Days, req.Remark)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "签发失败: "+err.Error())
|
||||
Fail(c, "签发失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"token": token}, "签发成功")
|
||||
Write(c, CodeSuccess, gin.H{"token": token}, "签发成功")
|
||||
}
|
||||
|
||||
func (h *APIToken) List(c *gin.Context) {
|
||||
var req dto.APITokenListRequest
|
||||
var req service.APITokenListRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *APIToken) Delete(c *gin.Context) {
|
||||
var req dto.IDRequest
|
||||
var req service.IDRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.DisableAPIToken(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "作废失败")
|
||||
Fail(c, "作废失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "作废成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "作废成功")
|
||||
}
|
||||
+67
-69
@@ -7,9 +7,7 @@ import (
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/biz"
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -26,135 +24,135 @@ func NewAudit(service *service.AuditService, recorder *service.AuditRecorder, lo
|
||||
}
|
||||
|
||||
func (h *Audit) Operations(c *gin.Context) {
|
||||
var req dto.OperationRecordSearchRequest
|
||||
var req service.OperationRecordSearchRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
func (h *Audit) Operation(c *gin.Context) {
|
||||
var req dto.AuditIDQuery
|
||||
var req service.AuditIDQuery
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "ID值不能为空")
|
||||
Fail(c, "ID值不能为空")
|
||||
return
|
||||
}
|
||||
item, err := h.service.Operation(c.Request.Context(), req.ID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "查询失败")
|
||||
Fail(c, "查询失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"reSysOperationRecord": item}, "查询成功")
|
||||
Write(c, CodeSuccess, gin.H{"reSysOperationRecord": item}, "查询成功")
|
||||
}
|
||||
func (h *Audit) DeleteOperation(c *gin.Context) {
|
||||
var req dto.IDRequest
|
||||
var req service.IDRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteOperations(c.Request.Context(), []int{int(req.ID)}); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Audit) DeleteOperations(c *gin.Context) {
|
||||
var req dto.AuditIDsRequest
|
||||
var req service.AuditIDsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteOperations(c.Request.Context(), req.IDs); err != nil {
|
||||
httpx.Fail(c, "批量删除失败")
|
||||
Fail(c, "批量删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "批量删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
|
||||
}
|
||||
|
||||
func (h *Audit) Logins(c *gin.Context) {
|
||||
var req dto.LoginLogSearchRequest
|
||||
var req service.LoginLogSearchRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
func (h *Audit) Login(c *gin.Context) {
|
||||
var req dto.AuditIDQuery
|
||||
var req service.AuditIDQuery
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
item, err := h.service.Login(c.Request.Context(), req.ID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "查询失败")
|
||||
Fail(c, "查询失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, item, "查询成功")
|
||||
Write(c, CodeSuccess, item, "查询成功")
|
||||
}
|
||||
func (h *Audit) DeleteLogin(c *gin.Context) {
|
||||
var req dto.IDRequest
|
||||
var req service.IDRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteLogins(c.Request.Context(), []int{int(req.ID)}); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Audit) DeleteLogins(c *gin.Context) {
|
||||
var req dto.AuditIDsRequest
|
||||
var req service.AuditIDsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteLogins(c.Request.Context(), req.IDs); err != nil {
|
||||
httpx.Fail(c, "批量删除失败")
|
||||
Fail(c, "批量删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "批量删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
|
||||
}
|
||||
|
||||
func (h *Audit) DataAccess(c *gin.Context) {
|
||||
var req dto.DataAccessListRequest
|
||||
var req service.DataAccessListRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
items, total, err := h.service.DataAccessRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
func (h *Audit) DeleteDataAccess(c *gin.Context) {
|
||||
var req dto.IDsRequest
|
||||
var req service.IDsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteDataAccess(c.Request.Context(), req.IDs); err != nil {
|
||||
httpx.Fail(c, "删除失败:"+err.Error())
|
||||
Fail(c, "删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
|
||||
func (h *Audit) LogDates(c *gin.Context) {
|
||||
@@ -162,7 +160,7 @@ func (h *Audit) LogDates(c *gin.Context) {
|
||||
// Month uses `binding:"required"`; an omitted or empty query is a
|
||||
// generic parameter error, not the service's format-specific error.
|
||||
if month == "" {
|
||||
httpx.Fail(c, "参数错误")
|
||||
Fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
data, err := h.logs.LogDates(c.Request.Context(), month)
|
||||
@@ -170,12 +168,12 @@ func (h *Audit) LogDates(c *gin.Context) {
|
||||
failLogViewer(c, err, h.logger)
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, data, "获取成功")
|
||||
Write(c, CodeSuccess, data, "获取成功")
|
||||
}
|
||||
func (h *Audit) LogFiles(c *gin.Context) {
|
||||
date := c.Query("date")
|
||||
if date == "" {
|
||||
httpx.Fail(c, "参数错误")
|
||||
Fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
data, err := h.logs.LogFiles(c.Request.Context(), date)
|
||||
@@ -183,19 +181,19 @@ func (h *Audit) LogFiles(c *gin.Context) {
|
||||
failLogViewer(c, err, h.logger)
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, data, "获取成功")
|
||||
Write(c, CodeSuccess, data, "获取成功")
|
||||
}
|
||||
func (h *Audit) LogContent(c *gin.Context) {
|
||||
date, path := c.Query("date"), c.Query("path")
|
||||
if date == "" || path == "" {
|
||||
httpx.Fail(c, "参数错误")
|
||||
Fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
var cursor *int64
|
||||
if raw := c.Query("cursor"); raw != "" {
|
||||
value, err := strconv.ParseInt(raw, 10, 64)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "参数错误")
|
||||
Fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
cursor = &value
|
||||
@@ -207,7 +205,7 @@ func (h *Audit) LogContent(c *gin.Context) {
|
||||
failLogViewer(c, err, h.logger)
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, data, "获取成功")
|
||||
Write(c, CodeSuccess, data, "获取成功")
|
||||
}
|
||||
|
||||
func failLogViewer(c *gin.Context, err error, logger *slog.Logger) {
|
||||
@@ -229,76 +227,76 @@ func failLogViewer(c *gin.Context, err error, logger *slog.Logger) {
|
||||
case errors.Is(err, biz.ErrLogRootUnavailable):
|
||||
message = "日志目录不可读取"
|
||||
}
|
||||
httpx.Fail(c, 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 {
|
||||
httpx.Fail(c, "删除失败:"+err.Error())
|
||||
Fail(c, "删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Audit) DeleteErrors(c *gin.Context) {
|
||||
if err := h.service.DeleteErrors(c.Request.Context(), IDsFromQuery(c)); err != nil {
|
||||
httpx.Fail(c, "批量删除失败:"+err.Error())
|
||||
Fail(c, "批量删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "批量删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
|
||||
}
|
||||
func (h *Audit) UpdateError(c *gin.Context) {
|
||||
var req dto.ErrorRecordMutationRequest
|
||||
var req service.ErrorRecordMutationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateErrorRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败:"+err.Error())
|
||||
Fail(c, "更新失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
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 {
|
||||
httpx.Fail(c, "查询失败:"+err.Error())
|
||||
Fail(c, "查询失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, item)
|
||||
OKWithData(c, item)
|
||||
}
|
||||
func (h *Audit) Errors(c *gin.Context) {
|
||||
p, size, err := page(c)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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 {
|
||||
httpx.Fail(c, parseErr.Error())
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: p, PageSize: size}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: p, PageSize: size}, "获取成功")
|
||||
}
|
||||
func (h *Audit) CreateError(c *gin.Context) {
|
||||
var req dto.ErrorRecordMutationRequest
|
||||
var req service.ErrorRecordMutationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.recorder.CreateErrorMutationRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "创建失败:"+err.Error())
|
||||
Fail(c, "创建失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "创建成功")
|
||||
}
|
||||
+44
-46
@@ -1,9 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -15,155 +13,155 @@ func NewAuthority(service *service.AuthorityService) *Authority { return &Author
|
||||
func (h *Authority) List(c *gin.Context) {
|
||||
items, err := h.service.Authorities(c.Request.Context())
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败"+err.Error())
|
||||
Fail(c, "获取失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, items, "获取成功")
|
||||
Write(c, CodeSuccess, items, "获取成功")
|
||||
}
|
||||
func (h *Authority) Create(c *gin.Context) {
|
||||
var req dto.AuthorityRequest
|
||||
var req service.AuthorityRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
if req.AuthorityName == "" {
|
||||
httpx.Fail(c, "AuthorityName值不能为空")
|
||||
Fail(c, "AuthorityName值不能为空")
|
||||
return
|
||||
}
|
||||
value, err := h.service.CreateAuthorityRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "创建失败"+err.Error())
|
||||
Fail(c, "创建失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"authority": value}, "创建成功")
|
||||
Write(c, CodeSuccess, gin.H{"authority": value}, "创建成功")
|
||||
}
|
||||
func (h *Authority) Copy(c *gin.Context) {
|
||||
var req dto.CopyAuthorityRequest
|
||||
var req service.CopyAuthorityRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.OldAuthorityID == 0 {
|
||||
httpx.Fail(c, "OldAuthorityId值不能为空")
|
||||
Fail(c, "OldAuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Authority.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Authority.AuthorityName == "" {
|
||||
httpx.Fail(c, "AuthorityName值不能为空")
|
||||
Fail(c, "AuthorityName值不能为空")
|
||||
return
|
||||
}
|
||||
value, err := h.service.CopyAuthorityRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "拷贝失败"+err.Error())
|
||||
Fail(c, "拷贝失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"authority": value}, "拷贝成功")
|
||||
Write(c, CodeSuccess, gin.H{"authority": value}, "拷贝成功")
|
||||
}
|
||||
func (h *Authority) Update(c *gin.Context) {
|
||||
var req dto.AuthorityRequest
|
||||
var req service.AuthorityRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
if req.AuthorityName == "" {
|
||||
httpx.Fail(c, "AuthorityName值不能为空")
|
||||
Fail(c, "AuthorityName值不能为空")
|
||||
return
|
||||
}
|
||||
value, err := h.service.UpdateAuthorityRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "更新失败"+err.Error())
|
||||
Fail(c, "更新失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"authority": value}, "更新成功")
|
||||
Write(c, CodeSuccess, gin.H{"authority": value}, "更新成功")
|
||||
}
|
||||
func (h *Authority) Delete(c *gin.Context) {
|
||||
var req dto.DeleteAuthorityRequest
|
||||
var req service.DeleteAuthorityRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteAuthority(c.Request.Context(), req.AuthorityID); err != nil {
|
||||
httpx.Fail(c, "删除失败"+err.Error())
|
||||
Fail(c, "删除失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Authority) SetUsers(c *gin.Context) {
|
||||
var req dto.SetRoleUsersRequest
|
||||
var req service.SetRoleUsersRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "角色ID不能为空")
|
||||
Fail(c, "角色ID不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.SetAuthorityUsers(c.Request.Context(), req.AuthorityID, req.UserIDs); err != nil {
|
||||
httpx.Fail(c, "设置失败"+err.Error())
|
||||
Fail(c, "设置失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
func (h *Authority) Users(c *gin.Context) {
|
||||
var req dto.SetRoleUsersRequest
|
||||
var req service.SetRoleUsersRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
ids, err := h.service.AuthorityUserIDs(c.Request.Context(), req.AuthorityID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败"+err.Error())
|
||||
Fail(c, "获取失败"+err.Error())
|
||||
return
|
||||
}
|
||||
if ids == nil {
|
||||
ids = []uint{}
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, ids, "获取成功")
|
||||
Write(c, CodeSuccess, ids, "获取成功")
|
||||
}
|
||||
func (h *Authority) SetDataScope(c *gin.Context) {
|
||||
var req dto.SetDataScopeRequest
|
||||
var req service.SetDataScopeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "角色ID不能为空")
|
||||
Fail(c, "角色ID不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.SetDataScope(c.Request.Context(), req.AuthorityID, req.DataScope, req.DepartmentIDs); err != nil {
|
||||
httpx.Fail(c, "设置失败"+err.Error())
|
||||
Fail(c, "设置失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
func (h *Authority) DataScopeDepartments(c *gin.Context) {
|
||||
var req dto.SetDataScopeRequest
|
||||
var req service.SetDataScopeRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
ids, err := h.service.DataScopeDepartmentIDs(c.Request.Context(), req.AuthorityID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败"+err.Error())
|
||||
Fail(c, "获取失败"+err.Error())
|
||||
return
|
||||
}
|
||||
if ids == nil {
|
||||
ids = []uint{}
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, ids, "获取成功")
|
||||
Write(c, CodeSuccess, ids, "获取成功")
|
||||
}
|
||||
+66
-69
@@ -1,11 +1,8 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -17,152 +14,152 @@ func NewDictionary(service *service.DictionaryService) *Dictionary {
|
||||
}
|
||||
|
||||
func (h *Dictionary) Create(c *gin.Context) {
|
||||
var req dto.DictionaryRequest
|
||||
var req service.DictionaryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
created, err := h.service.CreateDictionaryRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "创建失败")
|
||||
Fail(c, "创建失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, created, "创建成功")
|
||||
Write(c, CodeSuccess, created, "创建成功")
|
||||
}
|
||||
func (h *Dictionary) Update(c *gin.Context) {
|
||||
var req dto.DictionaryRequest
|
||||
var req service.DictionaryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateDictionaryRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败")
|
||||
Fail(c, "更新失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "更新成功")
|
||||
}
|
||||
func (h *Dictionary) Delete(c *gin.Context) {
|
||||
var req dto.DictionaryRequest
|
||||
var req service.DictionaryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteDictionary(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Dictionary) Find(c *gin.Context) {
|
||||
var req dto.DictionaryRequest
|
||||
var req service.DictionaryRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
item, err := h.service.Dictionary(c.Request.Context(), req.ID, req.Type, req.Status, true)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "字典未创建或未开启")
|
||||
Fail(c, "字典未创建或未开启")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"resysDictionary": item}, "查询成功")
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, items, "获取成功")
|
||||
Write(c, CodeSuccess, items, "获取成功")
|
||||
}
|
||||
}
|
||||
func (h *Dictionary) Export(c *gin.Context) {
|
||||
var req dto.DictionaryRequest
|
||||
var req service.DictionaryRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "字典ID不能为空")
|
||||
Fail(c, "字典ID不能为空")
|
||||
return
|
||||
}
|
||||
item, err := h.service.ExportDictionary(c.Request.Context(), req.ID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "导出失败")
|
||||
Fail(c, "导出失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, item, "导出成功")
|
||||
Write(c, CodeSuccess, item, "导出成功")
|
||||
}
|
||||
func (h *Dictionary) Import(c *gin.Context) {
|
||||
var req dto.ImportDictionaryRequest
|
||||
var req service.ImportDictionaryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.ImportDictionaryJSON(c.Request.Context(), req.JSON); err != nil {
|
||||
httpx.Fail(c, "导入失败: "+err.Error())
|
||||
Fail(c, "导入失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "导入成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "导入成功")
|
||||
}
|
||||
|
||||
func (h *Dictionary) CreateDetail(c *gin.Context) {
|
||||
var req dto.DictionaryDetailRequest
|
||||
var req service.DictionaryDetailRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.CreateDictionaryDetailRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "创建失败")
|
||||
Fail(c, "创建失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "创建成功")
|
||||
}
|
||||
func (h *Dictionary) UpdateDetail(c *gin.Context) {
|
||||
var req dto.DictionaryDetailRequest
|
||||
var req service.DictionaryDetailRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateDictionaryDetailRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败")
|
||||
Fail(c, "更新失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "更新成功")
|
||||
}
|
||||
func (h *Dictionary) DeleteDetail(c *gin.Context) {
|
||||
var req dto.DictionaryDetailRequest
|
||||
var req service.DictionaryDetailRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteDictionaryDetail(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Dictionary) FindDetail(c *gin.Context) {
|
||||
var req dto.DictionaryDetailRequest
|
||||
var req service.DictionaryDetailRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "ID值不能为空")
|
||||
Fail(c, "ID值不能为空")
|
||||
return
|
||||
}
|
||||
item, err := h.service.DictionaryDetail(c.Request.Context(), req.ID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "查询失败")
|
||||
Fail(c, "查询失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"reSysDictionaryDetail": item}, "查询成功")
|
||||
Write(c, CodeSuccess, gin.H{"reSysDictionaryDetail": item}, "查询成功")
|
||||
}
|
||||
func (h *Dictionary) Details(c *gin.Context) {
|
||||
p, size, err := page(c)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
var dictionaryID uint64
|
||||
@@ -170,15 +167,15 @@ func (h *Dictionary) Details(c *gin.Context) {
|
||||
var parseErr error
|
||||
dictionaryID, parseErr = strconv.ParseUint(raw, 10, 0)
|
||||
if parseErr != nil {
|
||||
httpx.Fail(c, parseErr.Error())
|
||||
Fail(c, parseErr.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
filter := dto.DictionaryDetailListRequest{DictionaryID: uint(dictionaryID), Label: c.Query("label"), Value: c.Query("value")}
|
||||
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 {
|
||||
httpx.Fail(c, parseErr.Error())
|
||||
Fail(c, parseErr.Error())
|
||||
return
|
||||
}
|
||||
filter.Status = &value
|
||||
@@ -186,7 +183,7 @@ func (h *Dictionary) Details(c *gin.Context) {
|
||||
if raw, exists := c.GetQuery("parentID"); exists {
|
||||
value, parseErr := strconv.ParseUint(raw, 10, 64)
|
||||
if parseErr != nil {
|
||||
httpx.Fail(c, parseErr.Error())
|
||||
Fail(c, parseErr.Error())
|
||||
return
|
||||
}
|
||||
parentID := uint(value)
|
||||
@@ -195,17 +192,17 @@ func (h *Dictionary) Details(c *gin.Context) {
|
||||
if raw, exists := c.GetQuery("level"); exists {
|
||||
value, parseErr := strconv.Atoi(raw)
|
||||
if parseErr != nil {
|
||||
httpx.Fail(c, parseErr.Error())
|
||||
Fail(c, parseErr.Error())
|
||||
return
|
||||
}
|
||||
filter.Level = &value
|
||||
}
|
||||
items, total, err := h.service.DictionaryDetailsRequest(c.Request.Context(), p, size, filter)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: p, PageSize: size}, "获取成功")
|
||||
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) {
|
||||
@@ -215,52 +212,52 @@ func (h *Dictionary) Tree(byType bool) gin.HandlerFunc {
|
||||
if byType {
|
||||
typ = c.Query("type")
|
||||
if typ == "" {
|
||||
httpx.Fail(c, "字典类型不能为空")
|
||||
Fail(c, "字典类型不能为空")
|
||||
return
|
||||
}
|
||||
} else if rawID == "" {
|
||||
httpx.Fail(c, "字典ID不能为空")
|
||||
Fail(c, "字典ID不能为空")
|
||||
return
|
||||
} else if parseErr != nil {
|
||||
httpx.Fail(c, "字典ID格式错误")
|
||||
Fail(c, "字典ID格式错误")
|
||||
return
|
||||
}
|
||||
items, err := h.service.DictionaryTree(c.Request.Context(), uint(id), typ)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"list": items}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"list": items}, "获取成功")
|
||||
}
|
||||
}
|
||||
func (h *Dictionary) DetailsByParent(c *gin.Context) {
|
||||
var req dto.DictionaryDetailsByParentRequest
|
||||
var req service.DictionaryDetailsByParentRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
items, err := h.service.DictionaryDetailsByParent(c.Request.Context(), req.DictionaryID, req.ParentID, req.IncludeChildren)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"list": items}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"list": items}, "获取成功")
|
||||
}
|
||||
func (h *Dictionary) Path(c *gin.Context) {
|
||||
rawID := c.Query("id")
|
||||
if rawID == "" {
|
||||
httpx.Fail(c, "字典详情ID不能为空")
|
||||
Fail(c, "字典详情ID不能为空")
|
||||
return
|
||||
}
|
||||
id, parseErr := strconv.ParseUint(rawID, 10, 32)
|
||||
if parseErr != nil {
|
||||
httpx.Fail(c, "字典详情ID格式错误")
|
||||
Fail(c, "字典详情ID格式错误")
|
||||
return
|
||||
}
|
||||
path, err := h.service.DictionaryPath(c.Request.Context(), uint(id))
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"path": path}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"path": path}, "获取成功")
|
||||
}
|
||||
+6
-8
@@ -1,9 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -14,21 +12,21 @@ func NewEmail(service *service.EmailService) *Email { return &Email{service: ser
|
||||
|
||||
func (h *Email) Test(c *gin.Context) {
|
||||
if err := h.service.Test(c.Request.Context()); err != nil {
|
||||
httpx.Fail(c, "发送失败")
|
||||
Fail(c, "发送失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "发送成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "发送成功")
|
||||
}
|
||||
|
||||
func (h *Email) Send(c *gin.Context) {
|
||||
var req dto.SendEmailRequest
|
||||
var req service.SendEmailRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.Send(c.Request.Context(), req.To, req.Subject, req.Body); err != nil {
|
||||
httpx.Fail(c, "发送失败")
|
||||
Fail(c, "发送失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "发送成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "发送成功")
|
||||
}
|
||||
+45
-48
@@ -2,14 +2,11 @@ package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"kra/app/system/internal/service"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@@ -45,179 +42,179 @@ func exportIssueParams(values url.Values, blank bool) (map[string]string, error)
|
||||
}
|
||||
|
||||
func (h *Export) Create(c *gin.Context) {
|
||||
var req dto.ExportTemplateRequest
|
||||
var req service.ExportTemplateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Name == "" {
|
||||
httpx.Fail(c, "Name值不能为空")
|
||||
Fail(c, "Name值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.CreateRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "创建失败")
|
||||
Fail(c, "创建失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "创建成功")
|
||||
}
|
||||
func (h *Export) Update(c *gin.Context) {
|
||||
var req dto.ExportTemplateRequest
|
||||
var req service.ExportTemplateRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Name == "" {
|
||||
httpx.Fail(c, "Name值不能为空")
|
||||
Fail(c, "Name值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败")
|
||||
Fail(c, "更新失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "更新成功")
|
||||
}
|
||||
func (h *Export) Delete(c *gin.Context) {
|
||||
var req dto.IDRequest
|
||||
var req service.IDRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
if err := h.service.Delete(c.Request.Context(), []uint{req.ID}); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Export) DeleteMany(c *gin.Context) {
|
||||
var req dto.IDsRequest
|
||||
var req service.IDsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.Delete(c.Request.Context(), req.IDs); err != nil {
|
||||
httpx.Fail(c, "批量删除失败")
|
||||
Fail(c, "批量删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "批量删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
|
||||
}
|
||||
func (h *Export) Find(c *gin.Context) {
|
||||
var req dto.ExportTemplateRequest
|
||||
var req service.ExportTemplateRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
item, err := h.service.Template(c.Request.Context(), req.ID, "")
|
||||
if err != nil {
|
||||
httpx.Fail(c, "查询失败")
|
||||
Fail(c, "查询失败")
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, gin.H{"resysExportTemplate": item})
|
||||
OKWithData(c, gin.H{"resysExportTemplate": item})
|
||||
}
|
||||
func (h *Export) List(c *gin.Context) {
|
||||
p, size, err := page(c)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
start, startErr := parseTime(c.Query("startCreatedAt"))
|
||||
end, endErr := parseTime(c.Query("endCreatedAt"))
|
||||
if startErr != nil || endErr != nil {
|
||||
httpx.Fail(c, "参数错误")
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: p, PageSize: size}, "获取成功")
|
||||
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 == "" {
|
||||
httpx.Fail(c, "模板ID不能为空")
|
||||
Fail(c, "模板ID不能为空")
|
||||
return
|
||||
}
|
||||
params, _ := exportParams(c.Request.URL.Query())
|
||||
sql, err := h.service.Preview(c.Request.Context(), templateID, params)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, gin.H{"sql": sql})
|
||||
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 == "" {
|
||||
httpx.Fail(c, "模板ID不能为空")
|
||||
Fail(c, "模板ID不能为空")
|
||||
return
|
||||
}
|
||||
params, err := exportIssueParams(c.Request.URL.Query(), blank)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "解析 params 参数失败")
|
||||
Fail(c, "解析 params 参数失败")
|
||||
return
|
||||
}
|
||||
token, err := h.service.IssueToken(c.Request.Context(), templateID, params, blank)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "导出令牌创建失败")
|
||||
Fail(c, "导出令牌创建失败")
|
||||
return
|
||||
}
|
||||
path := "/sysExportTemplate/exportExcelByToken?token=" + token
|
||||
if blank {
|
||||
path = "/sysExportTemplate/exportTemplateByToken?token=" + token
|
||||
}
|
||||
httpx.OKWithData(c, path)
|
||||
OKWithData(c, path)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Export) Import(c *gin.Context) {
|
||||
templateID := c.Query("templateID")
|
||||
if templateID == "" {
|
||||
httpx.Fail(c, "模板ID不能为空")
|
||||
Fail(c, "模板ID不能为空")
|
||||
return
|
||||
}
|
||||
file, err := c.FormFile("file")
|
||||
if err != nil {
|
||||
httpx.Fail(c, "文件获取失败")
|
||||
Fail(c, "文件获取失败")
|
||||
return
|
||||
}
|
||||
opened, err := file.Open()
|
||||
if err != nil {
|
||||
httpx.Fail(c, "读取文件失败")
|
||||
Fail(c, "读取文件失败")
|
||||
return
|
||||
}
|
||||
defer opened.Close()
|
||||
if err = h.service.Import(c.Request.Context(), templateID, opened); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "导入成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "导入成功")
|
||||
}
|
||||
|
||||
func (h *Export) Download(expectBlank bool) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := c.Query("token")
|
||||
if token == "" {
|
||||
httpx.Fail(c, "导出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):
|
||||
httpx.Fail(c, "解析导出参数失败")
|
||||
Fail(c, "解析导出参数失败")
|
||||
case errors.Is(err, service.ErrExportTokenType):
|
||||
httpx.Fail(c, "token类型错误")
|
||||
Fail(c, "token类型错误")
|
||||
default:
|
||||
httpx.Fail(c, "导出token无效或已过期")
|
||||
Fail(c, "导出token无效或已过期")
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -229,7 +226,7 @@ func (h *Export) Download(expectBlank bool) gin.HandlerFunc {
|
||||
data, name, err = h.service.Export(c.Request.Context(), value.TemplateID, value.Params)
|
||||
}
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
if expectBlank {
|
||||
@@ -0,0 +1,25 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"kra/app/system/internal/biz"
|
||||
"kra/app/system/internal/server/httpx"
|
||||
"kra/app/system/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) }
|
||||
+72
-76
@@ -2,14 +2,10 @@ package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"kra/app/system/internal/service"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
servermiddleware "kra/app/system/internal/transport/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -28,14 +24,14 @@ func rejectMediaTooLarge(c *gin.Context, err error, message string) bool {
|
||||
if !errors.As(err, &tooLarge) {
|
||||
return false
|
||||
}
|
||||
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, httpx.Response{Code: httpx.CodeError, Data: gin.H{}, Msg: message})
|
||||
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, Response{Code: CodeError, Data: gin.H{}, Msg: message})
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *Media) Upload(c *gin.Context) {
|
||||
claims := servermiddleware.Claims(c)
|
||||
claims := Claims(c)
|
||||
if claims == nil {
|
||||
httpx.Fail(c, "接收文件失败")
|
||||
Fail(c, "接收文件失败")
|
||||
return
|
||||
}
|
||||
limit := h.limitMultipartBody(c)
|
||||
@@ -44,18 +40,18 @@ func (h *Media) Upload(c *gin.Context) {
|
||||
if rejectMediaTooLarge(c, err, "文件超过大小上限") {
|
||||
return
|
||||
}
|
||||
httpx.Fail(c, "接收文件失败")
|
||||
Fail(c, "接收文件失败")
|
||||
return
|
||||
}
|
||||
if header.Size > limit {
|
||||
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, httpx.Response{Code: httpx.CodeError, Data: gin.H{}, Msg: "文件超过大小上限"})
|
||||
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.
|
||||
httpx.Fail(c, "上传文件失败")
|
||||
Fail(c, "上传文件失败")
|
||||
return
|
||||
}
|
||||
defer opened.Close()
|
||||
@@ -63,41 +59,41 @@ func (h *Media) Upload(c *gin.Context) {
|
||||
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 {
|
||||
httpx.Fail(c, "上传文件失败")
|
||||
Fail(c, "上传文件失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"file": item}, "上传成功")
|
||||
Write(c, CodeSuccess, gin.H{"file": item}, "上传成功")
|
||||
}
|
||||
|
||||
func (h *Media) List(c *gin.Context) {
|
||||
var req dto.MediaListRequest
|
||||
var req service.MediaListRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
items, total, err := h.service.MediaListRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
func (h *Media) Delete(c *gin.Context) {
|
||||
var req dto.IDRequest
|
||||
var req service.IDRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.Delete(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Media) DeleteMany(c *gin.Context) {
|
||||
var req dto.MediaBatchDeleteRequest
|
||||
var req service.MediaBatchDeleteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
failed := []uint{}
|
||||
@@ -107,121 +103,121 @@ func (h *Media) DeleteMany(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
if len(failed) > 0 {
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"failedIds": failed}, "部分文件删除失败")
|
||||
Write(c, CodeSuccess, gin.H{"failedIds": failed}, "部分文件删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "批量删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "批量删除成功")
|
||||
}
|
||||
func (h *Media) Find(c *gin.Context) {
|
||||
id, parseErr := strconv.ParseUint(c.Query("id"), 10, 64)
|
||||
if parseErr != nil {
|
||||
httpx.Fail(c, "文件ID非法")
|
||||
Fail(c, "文件ID非法")
|
||||
return
|
||||
}
|
||||
item, err := h.service.Media(c.Request.Context(), uint(id))
|
||||
if err != nil {
|
||||
httpx.Fail(c, "查询失败")
|
||||
Fail(c, "查询失败")
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, item)
|
||||
OKWithData(c, item)
|
||||
}
|
||||
func (h *Media) Rename(c *gin.Context) {
|
||||
var req dto.RenameMediaRequest
|
||||
var req service.RenameMediaRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.Rename(c.Request.Context(), req.ID, req.Name); err != nil {
|
||||
httpx.Fail(c, "编辑失败")
|
||||
Fail(c, "编辑失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "编辑成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "编辑成功")
|
||||
}
|
||||
func (h *Media) ImportURLs(c *gin.Context) {
|
||||
var req []dto.ImportMediaRequest
|
||||
var req []service.ImportMediaRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.ImportURLRequests(c.Request.Context(), req); err != nil {
|
||||
httpx.Fail(c, "导入URL失败")
|
||||
Fail(c, "导入URL失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "导入URL成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "导入URL成功")
|
||||
}
|
||||
func (h *Media) Storage(c *gin.Context) {
|
||||
var req dto.StorageListRequest
|
||||
var req service.StorageListRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
items, next, more, err := h.service.Storage(c.Request.Context(), req.Prefix, req.Cursor, req.Limit)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "列举存储桶文件失败")
|
||||
Fail(c, "列举存储桶文件失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"list": items, "nextCursor": next, "hasMore": more}, "获取成功")
|
||||
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 {
|
||||
httpx.Fail(c, "获取分类列表失败")
|
||||
Fail(c, "获取分类列表失败")
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, items)
|
||||
OKWithData(c, items)
|
||||
}
|
||||
func (h *Media) SaveCategory(c *gin.Context) {
|
||||
var req dto.CategoryRequest
|
||||
var req service.CategoryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, "参数错误")
|
||||
Fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.service.SaveCategoryRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "创建/更新失败:"+err.Error())
|
||||
Fail(c, "创建/更新失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建/更新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "创建/更新成功")
|
||||
}
|
||||
func (h *Media) DeleteCategory(c *gin.Context) {
|
||||
var req dto.IDRequest
|
||||
var req service.IDRequest
|
||||
if c.ShouldBindJSON(&req) != nil || req.ID == 0 {
|
||||
httpx.Fail(c, "参数错误")
|
||||
Fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteCategory(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
|
||||
func (h *Media) InitUpload(c *gin.Context) {
|
||||
claims := servermiddleware.Claims(c)
|
||||
var req dto.InitUploadRequest
|
||||
claims := Claims(c)
|
||||
var req service.InitUploadRequest
|
||||
if claims == nil {
|
||||
httpx.Fail(c, "参数错误")
|
||||
Fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if config := h.service.MediaConfig(); config.MaxFileSize > 0 && req.FileSize > config.MaxFileSize {
|
||||
httpx.Fail(c, "文件超过大小上限")
|
||||
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 {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, result, "成功")
|
||||
Write(c, CodeSuccess, result, "成功")
|
||||
}
|
||||
func (h *Media) SaveChunk(c *gin.Context) {
|
||||
claims := servermiddleware.Claims(c)
|
||||
claims := Claims(c)
|
||||
if claims == nil {
|
||||
httpx.Fail(c, "接收分片失败")
|
||||
Fail(c, "接收分片失败")
|
||||
return
|
||||
}
|
||||
limit := h.limitMultipartBody(c)
|
||||
@@ -230,63 +226,63 @@ func (h *Media) SaveChunk(c *gin.Context) {
|
||||
if rejectMediaTooLarge(c, err, "分片超过大小上限") {
|
||||
return
|
||||
}
|
||||
httpx.Fail(c, "接收分片失败")
|
||||
Fail(c, "接收分片失败")
|
||||
return
|
||||
}
|
||||
if header.Size > limit {
|
||||
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, httpx.Response{Code: httpx.CodeError, Data: gin.H{}, Msg: "分片超过大小上限"})
|
||||
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 {
|
||||
httpx.Fail(c, "上传会话 ID 非法")
|
||||
Fail(c, "上传会话 ID 非法")
|
||||
return
|
||||
}
|
||||
index, parseErr := strconv.Atoi(c.PostForm("chunkIndex"))
|
||||
if parseErr != nil || index < 0 {
|
||||
httpx.Fail(c, "分片序号非法")
|
||||
Fail(c, "分片序号非法")
|
||||
return
|
||||
}
|
||||
opened, err := header.Open()
|
||||
if err != nil {
|
||||
httpx.Fail(c, "分片读取失败")
|
||||
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 {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "ok")
|
||||
Write(c, CodeSuccess, gin.H{}, "ok")
|
||||
}
|
||||
func (h *Media) CompleteUpload(c *gin.Context) {
|
||||
claims := servermiddleware.Claims(c)
|
||||
var req dto.CompleteUploadRequest
|
||||
claims := Claims(c)
|
||||
var req service.CompleteUploadRequest
|
||||
if claims == nil {
|
||||
httpx.Fail(c, "参数错误")
|
||||
Fail(c, "参数错误")
|
||||
return
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
item, err := h.service.CompleteUpload(c.Request.Context(), claims.ID, req.UploadID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"media": item}, "成功")
|
||||
Write(c, CodeSuccess, gin.H{"media": item}, "成功")
|
||||
}
|
||||
func (h *Media) CancelUpload(c *gin.Context) {
|
||||
claims := servermiddleware.Claims(c)
|
||||
claims := Claims(c)
|
||||
id, _ := strconv.ParseUint(c.Param("uploadId"), 10, 64)
|
||||
if claims == nil {
|
||||
httpx.Fail(c, "未登录")
|
||||
Fail(c, "未登录")
|
||||
return
|
||||
}
|
||||
if err := h.service.CancelUpload(c.Request.Context(), claims.ID, uint(id)); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "已取消")
|
||||
Write(c, CodeSuccess, gin.H{}, "已取消")
|
||||
}
|
||||
+53
-55
@@ -1,9 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -15,170 +13,170 @@ 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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, items, "获取成功")
|
||||
Write(c, CodeSuccess, items, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Menu) Tree(c *gin.Context) {
|
||||
items, err := h.service.Tree(c.Request.Context())
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"menus": items}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"menus": items}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Menu) Create(c *gin.Context) {
|
||||
var req dto.MenuRequest
|
||||
var req service.MenuRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Path == "" {
|
||||
httpx.Fail(c, "Path值不能为空")
|
||||
Fail(c, "Path值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Name == "" {
|
||||
httpx.Fail(c, "Name值不能为空")
|
||||
Fail(c, "Name值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Component == "" {
|
||||
httpx.Fail(c, "Component值不能为空")
|
||||
Fail(c, "Component值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Sort < 0 {
|
||||
httpx.Fail(c, "Sort长度或值不在合法范围,ge=0")
|
||||
Fail(c, "Sort长度或值不在合法范围,ge=0")
|
||||
return
|
||||
}
|
||||
if req.Meta.Title == "" {
|
||||
httpx.Fail(c, "Title值不能为空")
|
||||
Fail(c, "Title值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.Create(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "添加失败:"+err.Error())
|
||||
Fail(c, "添加失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "添加成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "添加成功")
|
||||
}
|
||||
|
||||
func (h *Menu) Update(c *gin.Context) {
|
||||
var req dto.MenuRequest
|
||||
var req service.MenuRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Path == "" {
|
||||
httpx.Fail(c, "Path值不能为空")
|
||||
Fail(c, "Path值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Name == "" {
|
||||
httpx.Fail(c, "Name值不能为空")
|
||||
Fail(c, "Name值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Component == "" {
|
||||
httpx.Fail(c, "Component值不能为空")
|
||||
Fail(c, "Component值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Sort < 0 {
|
||||
httpx.Fail(c, "Sort长度或值不在合法范围,ge=0")
|
||||
Fail(c, "Sort长度或值不在合法范围,ge=0")
|
||||
return
|
||||
}
|
||||
if req.Meta.Title == "" {
|
||||
httpx.Fail(c, "Title值不能为空")
|
||||
Fail(c, "Title值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.Update(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败")
|
||||
Fail(c, "更新失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "更新成功")
|
||||
}
|
||||
|
||||
func (h *Menu) Delete(c *gin.Context) {
|
||||
var req dto.DeleteMenuRequest
|
||||
var req service.DeleteMenuRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "ID值不能为空")
|
||||
Fail(c, "ID值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.Delete(c.Request.Context(), uint(req.ID)); err != nil {
|
||||
httpx.Fail(c, "删除失败:"+err.Error())
|
||||
Fail(c, "删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
|
||||
func (h *Menu) Find(c *gin.Context) {
|
||||
var req dto.GetMenuRequest
|
||||
var req service.GetMenuRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "ID值不能为空")
|
||||
Fail(c, "ID值不能为空")
|
||||
return
|
||||
}
|
||||
item, err := h.service.Find(c.Request.Context(), uint(req.ID))
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"menu": item}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"menu": item}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Menu) SetAuthorityMenus(c *gin.Context) {
|
||||
var req dto.SetAuthorityMenusRequest
|
||||
var req service.SetAuthorityMenusRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.SetAuthorityMenus(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "添加失败")
|
||||
Fail(c, "添加失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "添加成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "添加成功")
|
||||
}
|
||||
|
||||
func (h *Menu) AuthorityMenus(c *gin.Context) {
|
||||
var req dto.GetAuthorityMenusRequest
|
||||
var req service.GetAuthorityMenusRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
menus, err := h.service.AuthorityMenus(c.Request.Context(), req.AuthorityID)
|
||||
if err != nil {
|
||||
httpx.Write(c, httpx.CodeError, gin.H{"menus": menus}, "获取失败")
|
||||
Write(c, CodeError, gin.H{"menus": menus}, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"menus": menus}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"menus": menus}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Menu) RoleIDs(c *gin.Context) {
|
||||
var req dto.SetMenuRolesRequest
|
||||
var req service.SetMenuRolesRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.MenuID == 0 {
|
||||
httpx.Fail(c, "菜单ID不能为空")
|
||||
Fail(c, "菜单ID不能为空")
|
||||
return
|
||||
}
|
||||
ids, err := h.service.RoleIDs(c.Request.Context(), req.MenuID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败"+err.Error())
|
||||
Fail(c, "获取失败"+err.Error())
|
||||
return
|
||||
}
|
||||
if ids == nil {
|
||||
@@ -186,28 +184,28 @@ func (h *Menu) RoleIDs(c *gin.Context) {
|
||||
}
|
||||
defaultIDs, err := h.service.DefaultRouterRoleIDs(c.Request.Context(), req.MenuID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败"+err.Error())
|
||||
Fail(c, "获取失败"+err.Error())
|
||||
return
|
||||
}
|
||||
if defaultIDs == nil {
|
||||
defaultIDs = []uint{}
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"authorityIds": ids, "defaultRouterAuthorityIds": defaultIDs}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"authorityIds": ids, "defaultRouterAuthorityIds": defaultIDs}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Menu) SetRoles(c *gin.Context) {
|
||||
var req dto.SetMenuRolesRequest
|
||||
var req service.SetMenuRolesRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.MenuID == 0 {
|
||||
httpx.Fail(c, "菜单ID不能为空")
|
||||
Fail(c, "菜单ID不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.SetRoles(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "设置失败"+err.Error())
|
||||
Fail(c, "设置失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
+4
-6
@@ -2,8 +2,6 @@ package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
"kra/app/system/internal/transport/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -12,15 +10,15 @@ type Navigation struct{ service *service.UserService }
|
||||
|
||||
func NewNavigation(service *service.UserService) *Navigation { return &Navigation{service: service} }
|
||||
func (h *Navigation) Menu(c *gin.Context) {
|
||||
claims := middleware.Claims(c)
|
||||
claims := Claims(c)
|
||||
if claims == nil {
|
||||
httpx.NoAuth(c, "未登录或非法访问")
|
||||
NoAuth(c, "未登录或非法访问")
|
||||
return
|
||||
}
|
||||
menus, err := h.service.Menus(c.Request.Context(), claims.AuthorityID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"menus": menus}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"menus": menus}, "获取成功")
|
||||
}
|
||||
+71
-73
@@ -1,9 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -18,236 +16,236 @@ func NewOrganization(departments *service.DepartmentService, positions *service.
|
||||
}
|
||||
|
||||
func (h *Organization) ListDepartments(c *gin.Context) {
|
||||
var req dto.DepartmentListRequest
|
||||
var req service.DepartmentListRequest
|
||||
_ = c.ShouldBindJSON(&req)
|
||||
items, err := h.departments.Departments(c.Request.Context(), req.Name)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, items, "获取成功")
|
||||
Write(c, CodeSuccess, items, "获取成功")
|
||||
}
|
||||
func (h *Organization) CreateDepartment(c *gin.Context) {
|
||||
var req dto.DepartmentRequest
|
||||
var req service.DepartmentRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Name == "" {
|
||||
httpx.Fail(c, "部门名称不能为空")
|
||||
Fail(c, "部门名称不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.departments.Create(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "创建失败:"+err.Error())
|
||||
Fail(c, "创建失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "创建成功")
|
||||
}
|
||||
func (h *Organization) UpdateDepartment(c *gin.Context) {
|
||||
var req dto.DepartmentRequest
|
||||
var req service.DepartmentRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "部门ID不能为空")
|
||||
Fail(c, "部门ID不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.departments.Update(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败:"+err.Error())
|
||||
Fail(c, "更新失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "更新成功")
|
||||
}
|
||||
func (h *Organization) DeleteDepartment(c *gin.Context) {
|
||||
var req dto.DeleteDepartmentRequest
|
||||
var req service.DeleteDepartmentRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.departments.Delete(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "删除失败:"+err.Error())
|
||||
Fail(c, "删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Organization) FindDepartment(c *gin.Context) {
|
||||
var req dto.DeleteDepartmentRequest
|
||||
var req service.DeleteDepartmentRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
item, err := h.departments.Department(c.Request.Context(), req.ID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, item, "获取成功")
|
||||
Write(c, CodeSuccess, item, "获取成功")
|
||||
}
|
||||
func (h *Organization) DepartmentUsers(c *gin.Context) {
|
||||
var req dto.SetDepartmentUsersRequest
|
||||
var req service.SetDepartmentUsersRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
ids, err := h.departments.UserIDs(c.Request.Context(), req.DepartmentID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
if ids == nil {
|
||||
ids = []uint{}
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, ids, "获取成功")
|
||||
Write(c, CodeSuccess, ids, "获取成功")
|
||||
}
|
||||
func (h *Organization) SetDepartmentUsers(c *gin.Context) {
|
||||
var req dto.SetDepartmentUsersRequest
|
||||
var req service.SetDepartmentUsersRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.DepartmentID == 0 {
|
||||
httpx.Fail(c, "部门ID不能为空")
|
||||
Fail(c, "部门ID不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.departments.SetUsers(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "设置失败:"+err.Error())
|
||||
Fail(c, "设置失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
func (h *Organization) SetUserDepartments(c *gin.Context) {
|
||||
var req dto.SetUserDepartmentsRequest
|
||||
var req service.SetUserDepartmentsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "用户ID不能为空")
|
||||
Fail(c, "用户ID不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.departments.SetUserDepartments(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "设置失败:"+err.Error())
|
||||
Fail(c, "设置失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
|
||||
func (h *Organization) ListPositions(c *gin.Context) {
|
||||
var req dto.PositionListRequest
|
||||
var req service.PositionListRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
items, total, err := h.positions.Positions(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
func (h *Organization) CreatePosition(c *gin.Context) {
|
||||
var req dto.PositionRequest
|
||||
var req service.PositionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Name == "" {
|
||||
httpx.Fail(c, "岗位名称不能为空")
|
||||
Fail(c, "岗位名称不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.positions.Create(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "创建失败:"+err.Error())
|
||||
Fail(c, "创建失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "创建成功")
|
||||
}
|
||||
func (h *Organization) UpdatePosition(c *gin.Context) {
|
||||
var req dto.PositionRequest
|
||||
var req service.PositionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "岗位ID不能为空")
|
||||
Fail(c, "岗位ID不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.positions.Update(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败:"+err.Error())
|
||||
Fail(c, "更新失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "更新成功")
|
||||
}
|
||||
func (h *Organization) DeletePosition(c *gin.Context) {
|
||||
var req dto.DeletePositionRequest
|
||||
var req service.DeletePositionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.positions.Delete(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "删除失败:"+err.Error())
|
||||
Fail(c, "删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Organization) FindPosition(c *gin.Context) {
|
||||
var req dto.DeletePositionRequest
|
||||
var req service.DeletePositionRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
item, err := h.positions.Position(c.Request.Context(), req.ID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, item, "获取成功")
|
||||
Write(c, CodeSuccess, item, "获取成功")
|
||||
}
|
||||
func (h *Organization) PositionUsers(c *gin.Context) {
|
||||
var req dto.SetPositionUsersRequest
|
||||
var req service.SetPositionUsersRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
ids, err := h.positions.UserIDs(c.Request.Context(), req.PositionID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
if ids == nil {
|
||||
ids = []uint{}
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, ids, "获取成功")
|
||||
Write(c, CodeSuccess, ids, "获取成功")
|
||||
}
|
||||
func (h *Organization) SetPositionUsers(c *gin.Context) {
|
||||
var req dto.SetPositionUsersRequest
|
||||
var req service.SetPositionUsersRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.PositionID == 0 {
|
||||
httpx.Fail(c, "岗位ID不能为空")
|
||||
Fail(c, "岗位ID不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.positions.SetUsers(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "设置失败:"+err.Error())
|
||||
Fail(c, "设置失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
func (h *Organization) SetUserPositions(c *gin.Context) {
|
||||
var req dto.SetUserPositionsRequest
|
||||
var req service.SetUserPositionsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "用户ID不能为空")
|
||||
Fail(c, "用户ID不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.positions.SetUserPositions(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "设置失败:"+err.Error())
|
||||
Fail(c, "设置失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
+20
-22
@@ -1,9 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -17,76 +15,76 @@ func NewParameter(service *service.ParameterService) *Parameter {
|
||||
}
|
||||
|
||||
func (h *Parameter) Create(c *gin.Context) {
|
||||
var req dto.SystemParameterRequest
|
||||
var req service.SystemParameterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.CreateParameterRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "创建失败:"+err.Error())
|
||||
Fail(c, "创建失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "创建成功")
|
||||
}
|
||||
|
||||
func (h *Parameter) Update(c *gin.Context) {
|
||||
var req dto.SystemParameterRequest
|
||||
var req service.SystemParameterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateParameterRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败:"+err.Error())
|
||||
Fail(c, "更新失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
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 {
|
||||
httpx.Fail(c, "删除失败:"+err.Error())
|
||||
Fail(c, "删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
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 {
|
||||
httpx.Fail(c, "批量删除失败:"+err.Error())
|
||||
Fail(c, "批量删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "批量删除成功")
|
||||
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 {
|
||||
httpx.Fail(c, "查询失败:"+err.Error())
|
||||
Fail(c, "查询失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, item)
|
||||
OKWithData(c, item)
|
||||
}
|
||||
|
||||
func (h *Parameter) Get(c *gin.Context) {
|
||||
item, err := h.service.ParameterByKey(c.Request.Context(), c.Query("key"))
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, item, "获取成功")
|
||||
Write(c, CodeSuccess, item, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Parameter) List(c *gin.Context) {
|
||||
var req dto.SystemParameterSearchRequest
|
||||
var req service.SystemParameterSearchRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
+81
-32
@@ -2,11 +2,8 @@ package handler
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -17,89 +14,141 @@ func NewPayment(service *service.PaymentService) *Payment { return &Payment{serv
|
||||
func (h *Payment) Configs(c *gin.Context) {
|
||||
v, err := h.service.Configs(c.Request.Context())
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, v)
|
||||
OKWithData(c, v)
|
||||
}
|
||||
func (h *Payment) SaveConfig(c *gin.Context) {
|
||||
var req dto.PaymentConfigRequest
|
||||
var req service.PaymentConfigRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.SaveConfig(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c)
|
||||
OK(c)
|
||||
}
|
||||
func (h *Payment) Order(c *gin.Context) {
|
||||
var req dto.PaymentQueryRequest
|
||||
var req service.PaymentQueryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
order, err := h.service.Order(c.Request.Context(), req.Provider, req.TradeNo)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, order)
|
||||
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 dto.PaymentOrderListRequest
|
||||
var req service.PaymentOrderListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
items, total, page, pageSize, err := h.service.Orders(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: page, PageSize: pageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: page, PageSize: pageSize}, "获取成功")
|
||||
}
|
||||
func (h *Payment) Create(c *gin.Context) {
|
||||
var req dto.PaymentRequest
|
||||
var req service.PaymentRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
req.ClientIP = c.ClientIP()
|
||||
result, err := h.service.Create(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, result)
|
||||
OKWithData(c, result)
|
||||
}
|
||||
func (h *Payment) Query(c *gin.Context) {
|
||||
var req dto.PaymentQueryRequest
|
||||
var req service.PaymentQueryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
result, err := h.service.Query(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, result)
|
||||
OKWithData(c, result)
|
||||
}
|
||||
func (h *Payment) Refund(c *gin.Context) {
|
||||
var req dto.PaymentRefundRequest
|
||||
var req service.PaymentRefundRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
result, err := h.service.Refund(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, result)
|
||||
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)
|
||||
@@ -120,12 +169,12 @@ func (h *Payment) Callback(c *gin.Context) {
|
||||
query[k] = v[0]
|
||||
}
|
||||
}
|
||||
ack, err := h.service.Callback(c.Request.Context(), &dto.PaymentCallbackRequest{Provider: c.Param("provider"), Headers: headers, Body: raw, Query: query})
|
||||
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 dto.PaymentCallbackAck) {
|
||||
func writePaymentCallbackAck(c *gin.Context, ack service.PaymentCallbackAck) {
|
||||
if ack.StatusCode == 0 {
|
||||
ack.StatusCode = http.StatusInternalServerError
|
||||
}
|
||||
+2
-2
@@ -5,14 +5,14 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
)
|
||||
|
||||
func TestWritePaymentCallbackAckDoesNotExposeError(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
recorder := httptest.NewRecorder()
|
||||
context, _ := gin.CreateTestContext(recorder)
|
||||
writePaymentCallbackAck(context, dto.PaymentCallbackAck{
|
||||
writePaymentCallbackAck(context, service.PaymentCallbackAck{
|
||||
StatusCode: 500,
|
||||
ContentType: "text/plain; charset=utf-8",
|
||||
Body: []byte("failure"),
|
||||
+11
-13
@@ -1,9 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -15,41 +13,41 @@ func NewPermission(service *service.PermissionService) *Permission {
|
||||
}
|
||||
|
||||
func (h *Permission) Buttons(c *gin.Context) {
|
||||
var req dto.GetAuthorityButtonsRequest
|
||||
var req service.GetAuthorityButtonsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
selected, err := h.service.SelectedButtons(c.Request.Context(), req.AuthorityID, req.MenuID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "查询失败")
|
||||
Fail(c, "查询失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"selected": selected}, "查询成功")
|
||||
Write(c, CodeSuccess, gin.H{"selected": selected}, "查询成功")
|
||||
}
|
||||
|
||||
func (h *Permission) SetButtons(c *gin.Context) {
|
||||
var req dto.SetAuthorityButtonsRequest
|
||||
var req service.SetAuthorityButtonsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.SetSelectedButtons(c.Request.Context(), req.AuthorityID, req.MenuID, req.Selected); err != nil {
|
||||
httpx.Fail(c, "分配失败")
|
||||
Fail(c, "分配失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "分配成功")
|
||||
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 {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if !allowed {
|
||||
httpx.Fail(c, "此按钮正在被使用无法删除")
|
||||
Fail(c, "此按钮正在被使用无法删除")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
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,
|
||||
)
|
||||
+27
-29
@@ -8,9 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/biz"
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mojocn/base64Captcha"
|
||||
@@ -36,7 +34,7 @@ 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 {
|
||||
httpx.Fail(c, "安全服务暂不可用")
|
||||
Fail(c, "安全服务暂不可用")
|
||||
return
|
||||
}
|
||||
openCaptcha := true
|
||||
@@ -45,7 +43,7 @@ func (h *Public) Captcha(c *gin.Context) {
|
||||
ttl := time.Duration(security.CaptchaTimeout) * time.Second
|
||||
failures, counterErr := h.settings.EnsureLoginIPCounter(c.Request.Context(), c.ClientIP(), ttl)
|
||||
if counterErr != nil {
|
||||
httpx.Fail(c, "安全服务暂不可用")
|
||||
Fail(c, "安全服务暂不可用")
|
||||
return
|
||||
}
|
||||
openCaptcha = security.CaptchaOpen == 0 || failures > security.CaptchaOpen
|
||||
@@ -53,24 +51,24 @@ func (h *Public) Captcha(c *gin.Context) {
|
||||
driver := base64Captcha.NewDriverDigit(height, width, keyLong, 0.7, 80)
|
||||
id, picture, _, err := base64Captcha.NewCaptcha(driver, h.store).Generate()
|
||||
if err != nil {
|
||||
httpx.Fail(c, "验证码获取失败")
|
||||
Fail(c, "验证码获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"captchaId": id, "picPath": picture, "captchaLength": keyLong, "openCaptcha": openCaptcha}, "验证码获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"captchaId": id, "picPath": picture, "captchaLength": keyLong, "openCaptcha": openCaptcha}, "验证码获取成功")
|
||||
}
|
||||
|
||||
func (h *Public) Login(c *gin.Context) {
|
||||
var req dto.LoginRequest
|
||||
var req service.LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Username == "" {
|
||||
httpx.Fail(c, "Username值不能为空")
|
||||
Fail(c, "Username值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Password == "" {
|
||||
httpx.Fail(c, "Password值不能为空")
|
||||
Fail(c, "Password值不能为空")
|
||||
return
|
||||
}
|
||||
result, err := h.auth.Login(c.Request.Context(), &req, c.ClientIP(), c.Request.UserAgent())
|
||||
@@ -83,66 +81,66 @@ func (h *Public) Login(c *gin.Context) {
|
||||
if locked != nil {
|
||||
minutes = locked.Minutes
|
||||
}
|
||||
httpx.Fail(c, "账号已锁定,请 "+strconv.Itoa(minutes)+" 分钟后再试")
|
||||
Fail(c, "账号已锁定,请 "+strconv.Itoa(minutes)+" 分钟后再试")
|
||||
case errors.Is(err, biz.ErrCaptchaInvalid):
|
||||
httpx.Fail(c, "验证码错误")
|
||||
Fail(c, "验证码错误")
|
||||
case errors.Is(err, biz.ErrUserDisabled):
|
||||
httpx.Fail(c, "用户被禁止登录")
|
||||
Fail(c, "用户被禁止登录")
|
||||
case errors.Is(err, biz.ErrTokenIssue):
|
||||
httpx.Fail(c, "获取token失败")
|
||||
Fail(c, "获取token失败")
|
||||
case errors.Is(err, biz.ErrLoginState):
|
||||
httpx.Fail(c, "设置登录状态失败")
|
||||
Fail(c, "设置登录状态失败")
|
||||
case errors.Is(err, biz.ErrTokenRevoke):
|
||||
httpx.Fail(c, "jwt作废失败")
|
||||
Fail(c, "jwt作废失败")
|
||||
default:
|
||||
httpx.Fail(c, "用户名不存在或者密码错误")
|
||||
Fail(c, "用户名不存在或者密码错误")
|
||||
}
|
||||
return
|
||||
}
|
||||
maxAge := int(time.UnixMilli(result.ExpiresAt).Unix() - time.Now().Unix())
|
||||
httpx.SetTokenCookie(c, result.Token, maxAge)
|
||||
httpx.Write(c, httpx.CodeSuccess, result, "登录成功")
|
||||
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 {
|
||||
httpx.Fail(c, "数据库状态检查失败")
|
||||
Fail(c, "数据库状态检查失败")
|
||||
return
|
||||
}
|
||||
message := "数据库无需初始化"
|
||||
if !initialized {
|
||||
message = "前往初始化数据库"
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"needInit": !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 {
|
||||
httpx.Fail(c, "数据库状态检查失败")
|
||||
Fail(c, "数据库状态检查失败")
|
||||
return
|
||||
}
|
||||
if initialized {
|
||||
httpx.Fail(c, "已存在数据库配置")
|
||||
Fail(c, "已存在数据库配置")
|
||||
return
|
||||
}
|
||||
var input dto.DatabaseInitRequest
|
||||
var input service.DatabaseInitRequest
|
||||
if c.ShouldBindJSON(&input) != nil {
|
||||
httpx.Fail(c, "参数校验不通过")
|
||||
Fail(c, "参数校验不通过")
|
||||
return
|
||||
}
|
||||
routes := engine.Routes()
|
||||
values := make([]dto.Route, 0, len(routes))
|
||||
values := make([]service.Route, 0, len(routes))
|
||||
for _, route := range routes {
|
||||
values = append(values, dto.Route{Path: route.Path, Method: route.Method})
|
||||
values = append(values, service.Route{Path: route.Path, Method: route.Method})
|
||||
}
|
||||
if err := h.system.InitializeRoutes(c.Request.Context(), &input, values); err != nil {
|
||||
httpx.Fail(c, "自动创建数据库失败,请查看后台日志,检查后在进行初始化")
|
||||
Fail(c, "自动创建数据库失败,请查看后台日志,检查后在进行初始化")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "自动创建数据库成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "自动创建数据库成功")
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -2,7 +2,6 @@ package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -16,9 +15,9 @@ func (h *Session) Logout(c *gin.Context) {
|
||||
token, _ = c.Cookie("x-token")
|
||||
}
|
||||
if err := h.tokens.BlacklistToken(c.Request.Context(), token); err != nil {
|
||||
httpx.Fail(c, "jwt作废失败")
|
||||
Fail(c, "jwt作废失败")
|
||||
return
|
||||
}
|
||||
httpx.SetTokenCookie(c, "", -1)
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "jwt作废成功")
|
||||
SetTokenCookie(c, "", -1)
|
||||
Write(c, CodeSuccess, gin.H{}, "jwt作废成功")
|
||||
}
|
||||
+16
-18
@@ -1,9 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -20,61 +18,61 @@ func NewSystemConfig(system *service.SystemConfigService, settings *service.Secu
|
||||
func (h *SystemConfig) GetSecurity(c *gin.Context) {
|
||||
value, err := h.settings.Security(c.Request.Context())
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取安全配置失败")
|
||||
Fail(c, "获取安全配置失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, value, "获取成功")
|
||||
Write(c, CodeSuccess, value, "获取成功")
|
||||
}
|
||||
|
||||
func (h *SystemConfig) SetSecurity(c *gin.Context) {
|
||||
var req dto.SecurityConfigRequest
|
||||
var req service.SecurityConfigRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
result, err := h.settings.SaveSecurityRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "设置安全配置失败")
|
||||
Fail(c, "设置安全配置失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, result, "设置成功")
|
||||
Write(c, CodeSuccess, result, "设置成功")
|
||||
}
|
||||
|
||||
func (h *SystemConfig) Get(c *gin.Context) {
|
||||
value, err := h.system.SystemConfig()
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, value, "获取成功")
|
||||
Write(c, CodeSuccess, value, "获取成功")
|
||||
}
|
||||
|
||||
func (h *SystemConfig) Set(c *gin.Context) {
|
||||
var req dto.SetSystemConfigRequest
|
||||
var req service.SetSystemConfigRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.system.SaveSystemConfig(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "设置失败")
|
||||
Fail(c, "设置失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
|
||||
func (h *SystemConfig) Reload(c *gin.Context) {
|
||||
if err := h.system.ReloadConfig(c.Request.Context()); err != nil {
|
||||
httpx.Fail(c, "重载系统失败:"+err.Error())
|
||||
Fail(c, "重载系统失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "重载系统成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "重载系统成功")
|
||||
}
|
||||
|
||||
func (h *SystemConfig) ServerInfo(c *gin.Context) {
|
||||
server, err := h.system.ServerInfo()
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"server": server}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"server": server}, "获取成功")
|
||||
}
|
||||
+33
-37
@@ -2,14 +2,10 @@ package handler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"kra/app/system/internal/service"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
"kra/app/system/internal/transport/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -22,107 +18,107 @@ func NewTask(service *service.TaskService) *Task {
|
||||
}
|
||||
|
||||
func (h *Task) Create(c *gin.Context) {
|
||||
var req dto.TaskRequest
|
||||
var req service.TaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
_, err := h.service.CreateRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "创建失败: "+err.Error())
|
||||
Fail(c, "创建失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "创建成功")
|
||||
}
|
||||
|
||||
func (h *Task) Update(c *gin.Context) {
|
||||
var req dto.TaskRequest
|
||||
var req service.TaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "更新失败: "+err.Error())
|
||||
Fail(c, "更新失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "更新成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "更新成功")
|
||||
}
|
||||
|
||||
func (h *Task) Delete(c *gin.Context) {
|
||||
var req dto.DeleteTaskRequest
|
||||
var req service.DeleteTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.Delete(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
|
||||
func (h *Task) Toggle(c *gin.Context) {
|
||||
var req dto.ToggleTaskRequest
|
||||
var req service.ToggleTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.Toggle(c.Request.Context(), req.ID, req.Enabled); err != nil {
|
||||
httpx.Fail(c, "操作失败: "+err.Error())
|
||||
Fail(c, "操作失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "操作成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "操作成功")
|
||||
}
|
||||
|
||||
func (h *Task) Trigger(c *gin.Context) {
|
||||
var req dto.DeleteTaskRequest
|
||||
var req service.DeleteTaskRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.Trigger(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "触发失败: "+err.Error())
|
||||
Fail(c, "触发失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "已触发, 执行结果见执行日志")
|
||||
Write(c, CodeSuccess, gin.H{}, "已触发, 执行结果见执行日志")
|
||||
}
|
||||
|
||||
func (h *Task) List(c *gin.Context) {
|
||||
var req dto.TaskListRequest
|
||||
var req service.TaskListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Task) Logs(c *gin.Context) {
|
||||
var req dto.TaskLogListRequest
|
||||
var req service.TaskLogListRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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 {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Task) Methods(c *gin.Context) {
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"methods": h.service.RegisteredMethods()}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"methods": h.service.RegisteredMethods()}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Task) AlertStream(c *gin.Context) {
|
||||
claims := middleware.Claims(c)
|
||||
claims := Claims(c)
|
||||
if claims == nil || claims.ID == 0 {
|
||||
httpx.Fail(c, "未获取到用户身份")
|
||||
Fail(c, "未获取到用户身份")
|
||||
return
|
||||
}
|
||||
c.Header("Content-Type", "text/event-stream; charset=utf-8")
|
||||
@@ -131,7 +127,7 @@ func (h *Task) AlertStream(c *gin.Context) {
|
||||
c.Header("X-Accel-Buffering", "no")
|
||||
flusher, ok := c.Writer.(http.Flusher)
|
||||
if !ok {
|
||||
httpx.Fail(c, "SSE 不受支持")
|
||||
Fail(c, "SSE 不受支持")
|
||||
return
|
||||
}
|
||||
// Remove the normal server deadline for long-lived SSE connections.
|
||||
+70
-74
@@ -1,14 +1,10 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/service"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
"kra/app/system/internal/transport/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -22,236 +18,236 @@ func NewUser(user *service.UserService, auth *service.AuthService) *User {
|
||||
}
|
||||
|
||||
func (h *User) List(c *gin.Context) {
|
||||
var req dto.UserListRequest
|
||||
var req service.UserListRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Page == 0 {
|
||||
httpx.Fail(c, "Page值不能为空")
|
||||
Fail(c, "Page值不能为空")
|
||||
return
|
||||
}
|
||||
if req.PageSize == 0 {
|
||||
httpx.Fail(c, "PageSize值不能为空")
|
||||
Fail(c, "PageSize值不能为空")
|
||||
return
|
||||
}
|
||||
items, total, err := h.service.ListUsersRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *User) Create(c *gin.Context) {
|
||||
var req dto.UserRequest
|
||||
var req service.UserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Username == "" {
|
||||
httpx.Fail(c, "Username值不能为空")
|
||||
Fail(c, "Username值不能为空")
|
||||
return
|
||||
}
|
||||
if req.Password == "" {
|
||||
httpx.Fail(c, "Password值不能为空")
|
||||
Fail(c, "Password值不能为空")
|
||||
return
|
||||
}
|
||||
if req.NickName == "" {
|
||||
httpx.Fail(c, "NickName值不能为空")
|
||||
Fail(c, "NickName值不能为空")
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
result, err := h.service.CreateUserRequest(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
if service.IsPasswordPolicyError(err) {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if result == nil {
|
||||
result = &dto.UserResponse{}
|
||||
result = &service.UserResponse{}
|
||||
}
|
||||
httpx.Write(c, httpx.CodeError, gin.H{"user": result}, "注册失败")
|
||||
Write(c, CodeError, gin.H{"user": result}, "注册失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"user": result}, "注册成功")
|
||||
Write(c, CodeSuccess, gin.H{"user": result}, "注册成功")
|
||||
}
|
||||
|
||||
func (h *User) Update(c *gin.Context) {
|
||||
var req dto.UserRequest
|
||||
var req service.UserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "ID值不能为空")
|
||||
Fail(c, "ID值不能为空")
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateUserRequest(c.Request.Context(), &req); err != nil {
|
||||
httpx.Fail(c, "修改失败")
|
||||
Fail(c, "修改失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
|
||||
func (h *User) UpdateSelf(c *gin.Context) {
|
||||
claims := middleware.Claims(c)
|
||||
claims := Claims(c)
|
||||
if claims == nil {
|
||||
httpx.NoAuth(c, "未登录或非法访问")
|
||||
NoAuth(c, "未登录或非法访问")
|
||||
return
|
||||
}
|
||||
var req dto.SelfUserRequest
|
||||
var req service.SelfUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.UpdateSelfUserRequest(c.Request.Context(), claims.ID, &req); err != nil {
|
||||
httpx.Fail(c, "修改失败")
|
||||
Fail(c, "修改失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
|
||||
func (h *User) Delete(c *gin.Context) {
|
||||
var req dto.IDRequest
|
||||
var req service.IDRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.ID == 0 {
|
||||
httpx.Fail(c, "ID值不能为空")
|
||||
Fail(c, "ID值不能为空")
|
||||
return
|
||||
}
|
||||
claims := middleware.Claims(c)
|
||||
claims := Claims(c)
|
||||
if claims != nil && claims.ID == req.ID {
|
||||
httpx.Fail(c, "删除失败, 无法删除自己。")
|
||||
Fail(c, "删除失败, 无法删除自己。")
|
||||
return
|
||||
}
|
||||
if err := h.service.DeleteUser(c.Request.Context(), req.ID); err != nil {
|
||||
httpx.Fail(c, "删除失败")
|
||||
Fail(c, "删除失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
|
||||
func (h *User) ResetPassword(c *gin.Context) {
|
||||
var req dto.ResetPasswordRequest
|
||||
var req service.ResetPasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.ResetPassword(c.Request.Context(), req.ID, req.Password); err != nil {
|
||||
if service.IsPasswordPolicyError(err) {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Fail(c, "重置失败"+err.Error())
|
||||
Fail(c, "重置失败"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "重置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "重置成功")
|
||||
}
|
||||
|
||||
func (h *User) ChangePassword(c *gin.Context) {
|
||||
claims := middleware.Claims(c)
|
||||
var req dto.ChangePasswordRequest
|
||||
claims := Claims(c)
|
||||
var req service.ChangePasswordRequest
|
||||
if claims == nil {
|
||||
httpx.NoAuth(c, "未登录或非法访问")
|
||||
NoAuth(c, "未登录或非法访问")
|
||||
return
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.Password == "" {
|
||||
httpx.Fail(c, "Password值不能为空")
|
||||
Fail(c, "Password值不能为空")
|
||||
return
|
||||
}
|
||||
if req.NewPassword == "" {
|
||||
httpx.Fail(c, "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) {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Fail(c, "修改失败,原密码与当前账户不符")
|
||||
Fail(c, "修改失败,原密码与当前账户不符")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "修改成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "修改成功")
|
||||
}
|
||||
|
||||
func (h *User) SetSelfSetting(c *gin.Context) {
|
||||
claims := middleware.Claims(c)
|
||||
claims := Claims(c)
|
||||
var setting map[string]any
|
||||
if claims == nil {
|
||||
httpx.NoAuth(c, "未登录或非法访问")
|
||||
NoAuth(c, "未登录或非法访问")
|
||||
return
|
||||
}
|
||||
if err := c.ShouldBindJSON(&setting); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.SetUserSetting(c.Request.Context(), claims.ID, setting); err != nil {
|
||||
httpx.Fail(c, "设置失败")
|
||||
Fail(c, "设置失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "设置成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "设置成功")
|
||||
}
|
||||
|
||||
func (h *User) SetAuthorities(c *gin.Context) {
|
||||
var req dto.UserAuthoritiesRequest
|
||||
var req service.UserAuthoritiesRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.SetUserAuthorities(c.Request.Context(), req.ID, req.AuthorityIDs); err != nil {
|
||||
httpx.Fail(c, "修改失败")
|
||||
Fail(c, "修改失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "修改成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "修改成功")
|
||||
}
|
||||
|
||||
func (h *User) SwitchAuthority(c *gin.Context) {
|
||||
claims := middleware.Claims(c)
|
||||
var req dto.SwitchAuthorityRequest
|
||||
claims := Claims(c)
|
||||
var req service.SwitchAuthorityRequest
|
||||
if claims == nil {
|
||||
httpx.NoAuth(c, "未登录或非法访问")
|
||||
NoAuth(c, "未登录或非法访问")
|
||||
return
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
if req.AuthorityID == 0 {
|
||||
httpx.Fail(c, "AuthorityId值不能为空")
|
||||
Fail(c, "AuthorityId值不能为空")
|
||||
return
|
||||
}
|
||||
login, err := h.auth.SwitchAuthority(c.Request.Context(), claims, req.AuthorityID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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())
|
||||
httpx.SetTokenCookie(c, login.Token, maxAge)
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "修改成功")
|
||||
SetTokenCookie(c, login.Token, maxAge)
|
||||
Write(c, CodeSuccess, gin.H{}, "修改成功")
|
||||
}
|
||||
|
||||
func (h *User) Get(c *gin.Context) {
|
||||
claims := middleware.Claims(c)
|
||||
claims := Claims(c)
|
||||
if claims == nil {
|
||||
httpx.NoAuth(c, "未登录或非法访问")
|
||||
NoAuth(c, "未登录或非法访问")
|
||||
return
|
||||
}
|
||||
result, err := h.service.UserByUUID(c.Request.Context(), claims.UUID)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败")
|
||||
Fail(c, "获取失败")
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{"userInfo": result}, "获取成功")
|
||||
Write(c, CodeSuccess, gin.H{"userInfo": result}, "获取成功")
|
||||
}
|
||||
+29
-31
@@ -8,9 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/biz"
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -30,10 +28,10 @@ func versionStage(err error) (biz.VersionStage, bool) {
|
||||
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 {
|
||||
httpx.Fail(c, "删除失败:"+err.Error())
|
||||
Fail(c, "删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "删除成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "删除成功")
|
||||
}
|
||||
func (h *Version) DeleteMany(c *gin.Context) {
|
||||
raw := c.QueryArray("IDs[]")
|
||||
@@ -43,24 +41,24 @@ func (h *Version) DeleteMany(c *gin.Context) {
|
||||
ids = append(ids, uint(id))
|
||||
}
|
||||
if err := h.service.DeleteVersions(c.Request.Context(), ids); err != nil {
|
||||
httpx.Fail(c, "批量删除失败:"+err.Error())
|
||||
Fail(c, "批量删除失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "批量删除成功")
|
||||
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 {
|
||||
httpx.Fail(c, "查询失败:"+err.Error())
|
||||
Fail(c, "查询失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OKWithData(c, item)
|
||||
OKWithData(c, item)
|
||||
}
|
||||
func (h *Version) List(c *gin.Context) {
|
||||
var req dto.VersionSearchRequest
|
||||
var req service.VersionSearchRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
Fail(c, err.Error())
|
||||
return
|
||||
}
|
||||
name, code := "", ""
|
||||
@@ -76,47 +74,47 @@ func (h *Version) List(c *gin.Context) {
|
||||
}
|
||||
items, total, err := h.service.Versions(c.Request.Context(), req.Page, req.PageSize, name, code, createdAtRange)
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取失败:"+err.Error())
|
||||
Fail(c, "获取失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, httpx.PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
Write(c, CodeSuccess, PageResult{List: items, Total: total, Page: req.Page, PageSize: req.PageSize}, "获取成功")
|
||||
}
|
||||
|
||||
func (h *Version) Export(c *gin.Context) {
|
||||
var req dto.ExportVersionRequest
|
||||
var req service.ExportVersionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, err.Error())
|
||||
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:
|
||||
httpx.Fail(c, "获取菜单数据失败:"+err.Error())
|
||||
Fail(c, "获取菜单数据失败:"+err.Error())
|
||||
case biz.VersionStageAPIs:
|
||||
httpx.Fail(c, "获取API数据失败:"+err.Error())
|
||||
Fail(c, "获取API数据失败:"+err.Error())
|
||||
case biz.VersionStageDictionaries:
|
||||
httpx.Fail(c, "获取字典数据失败:"+err.Error())
|
||||
Fail(c, "获取字典数据失败:"+err.Error())
|
||||
case biz.VersionStageJSON:
|
||||
httpx.Fail(c, "JSON序列化失败:"+err.Error())
|
||||
Fail(c, "JSON序列化失败:"+err.Error())
|
||||
case biz.VersionStageSave:
|
||||
httpx.Fail(c, "保存版本记录失败:"+err.Error())
|
||||
Fail(c, "保存版本记录失败:"+err.Error())
|
||||
default:
|
||||
httpx.Fail(c, "创建发版失败:"+err.Error())
|
||||
Fail(c, "创建发版失败:"+err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "创建发版成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "创建发版成功")
|
||||
}
|
||||
func (h *Version) Download(c *gin.Context) {
|
||||
rawID := c.Query("ID")
|
||||
if rawID == "" {
|
||||
httpx.Fail(c, "版本ID不能为空")
|
||||
Fail(c, "版本ID不能为空")
|
||||
return
|
||||
}
|
||||
id, _ := strconv.ParseUint(rawID, 10, 64)
|
||||
raw, code, err := h.service.VersionData(c.Request.Context(), uint(id))
|
||||
if err != nil {
|
||||
httpx.Fail(c, "获取版本记录失败:"+err.Error())
|
||||
Fail(c, "获取版本记录失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
name := fmt.Sprintf("version_%s_%s.json", code, time.Now().Format("20060102150405"))
|
||||
@@ -126,27 +124,27 @@ func (h *Version) Download(c *gin.Context) {
|
||||
c.Data(http.StatusOK, "application/json", []byte(raw))
|
||||
}
|
||||
func (h *Version) Import(c *gin.Context) {
|
||||
var req dto.ImportVersionRequest
|
||||
var req service.ImportVersionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Fail(c, "解析JSON数据失败:"+err.Error())
|
||||
Fail(c, "解析JSON数据失败:"+err.Error())
|
||||
return
|
||||
}
|
||||
if err := h.service.ImportRequest(c.Request.Context(), &req); err != nil {
|
||||
if errors.Is(err, biz.ErrInvalidVersion) {
|
||||
httpx.Fail(c, "版本信息格式错误")
|
||||
Fail(c, "版本信息格式错误")
|
||||
return
|
||||
}
|
||||
switch stage, _ := versionStage(err); stage {
|
||||
case biz.VersionStageMenus:
|
||||
httpx.Fail(c, "导入菜单失败: "+err.Error())
|
||||
Fail(c, "导入菜单失败: "+err.Error())
|
||||
case biz.VersionStageAPIs:
|
||||
httpx.Fail(c, "导入API失败: "+err.Error())
|
||||
Fail(c, "导入API失败: "+err.Error())
|
||||
case biz.VersionStageDictionaries:
|
||||
httpx.Fail(c, "导入字典失败: "+err.Error())
|
||||
Fail(c, "导入字典失败: "+err.Error())
|
||||
default:
|
||||
httpx.Fail(c, "导入失败:"+err.Error())
|
||||
Fail(c, "导入失败:"+err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
httpx.Write(c, httpx.CodeSuccess, gin.H{}, "导入成功")
|
||||
Write(c, CodeSuccess, gin.H{}, "导入成功")
|
||||
}
|
||||
+3
-4
@@ -6,7 +6,6 @@ import (
|
||||
"kra/app/system/internal/biz"
|
||||
"kra/app/system/internal/conf"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -20,7 +19,7 @@ func AccessControl(runtime *conf.Runtime, access accessController) gin.HandlerFu
|
||||
return func(c *gin.Context) {
|
||||
claims := Claims(c)
|
||||
if claims == nil {
|
||||
httpx.NoAuth(c, "未登录或非法访问")
|
||||
NoAuth(c, "未登录或非法访问")
|
||||
return
|
||||
}
|
||||
path := c.Request.URL.Path
|
||||
@@ -30,13 +29,13 @@ func AccessControl(runtime *conf.Runtime, access accessController) gin.HandlerFu
|
||||
}
|
||||
allowed, err := access.Authorize(c.Request.Context(), claims.AuthorityID, policyPath, c.Request.Method)
|
||||
if err != nil || !allowed {
|
||||
httpx.Write(c, httpx.CodeError, gin.H{}, "权限不足")
|
||||
Write(c, CodeError, gin.H{}, "权限不足")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
requestContext, err := access.ContextWithDataScope(c.Request.Context(), claims.AuthorityID, claims.ID)
|
||||
if err != nil {
|
||||
httpx.Write(c, httpx.CodeError, gin.H{}, "数据权限解析失败")
|
||||
Write(c, CodeError, gin.H{}, "数据权限解析失败")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
+3
-4
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"kra/app/system/internal/biz"
|
||||
"kra/app/system/internal/conf"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -41,7 +40,7 @@ func AccessLog(runtime *conf.Runtime, logger *slog.Logger, version string) gin.H
|
||||
c.Header("X-Kra-Version", version)
|
||||
requestReadFailed := c.Request.ContentLength > bodyLimit
|
||||
if requestReadFailed {
|
||||
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, gin.H{"code": httpx.CodeError, "msg": "请求体超过大小上限"})
|
||||
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, gin.H{"code": CodeError, "msg": "请求体超过大小上限"})
|
||||
} else if c.Request.Body != nil && !mediaUpload {
|
||||
limited := http.MaxBytesReader(c.Writer, c.Request.Body, bodyLimit)
|
||||
var err error
|
||||
@@ -50,9 +49,9 @@ func AccessLog(runtime *conf.Runtime, logger *slog.Logger, version string) gin.H
|
||||
requestReadFailed = true
|
||||
var tooLarge *http.MaxBytesError
|
||||
if errors.As(err, &tooLarge) {
|
||||
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, gin.H{"code": httpx.CodeError, "msg": "请求体超过大小上限"})
|
||||
c.AbortWithStatusJSON(http.StatusRequestEntityTooLarge, gin.H{"code": CodeError, "msg": "请求体超过大小上限"})
|
||||
} else {
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"code": httpx.CodeError, "msg": "请求体读取失败"})
|
||||
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"code": CodeError, "msg": "请求体读取失败"})
|
||||
}
|
||||
} else {
|
||||
c.Request.Body = io.NopCloser(bytes.NewReader(requestBody))
|
||||
+2
-3
@@ -13,7 +13,6 @@ import (
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/conf"
|
||||
"kra/app/system/internal/dto"
|
||||
"kra/app/system/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -21,7 +20,7 @@ import (
|
||||
|
||||
const ctxOperationAuditPersistFailedKey = "operation_audit_persist_failed"
|
||||
|
||||
func OperationAudit(runtime *conf.Runtime, service *service.AuditRecorder) gin.HandlerFunc {
|
||||
func OperationAudit(runtime *conf.Runtime, recorder *service.AuditRecorder) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
path := c.Request.URL.Path
|
||||
if !recordsOperation(c.Request.Method, path) {
|
||||
@@ -81,7 +80,7 @@ func OperationAudit(runtime *conf.Runtime, service *service.AuditRecorder) gin.H
|
||||
responseBody = "[超出记录长度]"
|
||||
}
|
||||
errorMessage := c.Errors.ByType(gin.ErrorTypePrivate).String()
|
||||
if err := service.RecordOperationRequest(c.Request.Context(), &dto.OperationRecordRequest{IP: c.ClientIP(), Method: c.Request.Method, Path: path, Status: status, LatencyMS: time.Since(started).Milliseconds(), Agent: c.Request.UserAgent(), ErrorMessage: errorMessage, Body: operationRequestBody(requestBody, c.GetHeader("Content-Type"), maxBytes), Response: responseBody, UserID: userID, RequestID: stringValue(requestID), TraceID: stringValueFromContext(c, "trace_id"), DeviceID: c.GetHeader("X-Device-Id")}); err != nil {
|
||||
if err := recorder.RecordOperationRequest(c.Request.Context(), &service.OperationRecordRequest{IP: c.ClientIP(), Method: c.Request.Method, Path: path, Status: status, LatencyMS: time.Since(started).Milliseconds(), Agent: c.Request.UserAgent(), ErrorMessage: errorMessage, Body: operationRequestBody(requestBody, c.GetHeader("Content-Type"), maxBytes), Response: responseBody, UserID: userID, RequestID: stringValue(requestID), TraceID: stringValueFromContext(c, "trace_id"), DeviceID: c.GetHeader("X-Device-Id")}); err != nil {
|
||||
// Preserve the business response, but expose audit persistence failures
|
||||
// to the global access/error logging pipeline.
|
||||
c.Set(ctxOperationAuditPersistFailedKey, true)
|
||||
+5
-6
@@ -8,7 +8,6 @@ import (
|
||||
|
||||
"kra/app/system/internal/biz"
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"golang.org/x/sync/singleflight"
|
||||
@@ -25,7 +24,7 @@ func Auth(auth *service.AuthService) gin.HandlerFunc {
|
||||
token, _ = c.Cookie("x-token")
|
||||
}
|
||||
if token == "" {
|
||||
httpx.NoAuth(c, "未登录或非法访问,请登录")
|
||||
NoAuth(c, "未登录或非法访问,请登录")
|
||||
return
|
||||
}
|
||||
value, err, _ := refreshTokens.Do(token, func() (any, error) {
|
||||
@@ -45,15 +44,15 @@ func Auth(auth *service.AuthService) gin.HandlerFunc {
|
||||
case errors.Is(err, biz.ErrTokenDisabled):
|
||||
message = "您的帐户异地登陆或令牌失效"
|
||||
}
|
||||
httpx.SetTokenCookie(c, "", -1)
|
||||
httpx.NoAuth(c, message)
|
||||
SetTokenCookie(c, "", -1)
|
||||
NoAuth(c, message)
|
||||
return
|
||||
}
|
||||
authentication := value.(*biz.TokenAuthentication)
|
||||
if authentication.Refreshed != nil {
|
||||
c.Header("new-token", authentication.Refreshed.Value)
|
||||
c.Header("new-expires-at", strconv.FormatInt(authentication.Refreshed.ExpiresAt.Unix(), 10))
|
||||
httpx.SetTokenCookie(c, authentication.Refreshed.Value, int(authentication.Refreshed.TTL.Seconds()))
|
||||
SetTokenCookie(c, authentication.Refreshed.Value, int(authentication.Refreshed.TTL.Seconds()))
|
||||
}
|
||||
c.Set(claimsKey, authentication.Claims)
|
||||
c.Next()
|
||||
@@ -78,6 +77,6 @@ func MustChangePassword() gin.HandlerFunc {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
c.AbortWithStatusJSON(http.StatusConflict, httpx.Response{Code: httpx.CodePasswordChangeRequired, Data: gin.H{"needChangePassword": true}, Msg: "密码已过期,请先修改密码"})
|
||||
c.AbortWithStatusJSON(http.StatusConflict, Response{Code: CodePasswordChangeRequired, Data: gin.H{"needChangePassword": true}, Msg: "密码已过期,请先修改密码"})
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -6,8 +6,6 @@ import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -26,7 +24,7 @@ func ErrorAudit(logger *slog.Logger) gin.HandlerFunc {
|
||||
if auditPersistFailed != true && (strings.Contains(c.Request.URL.Path, "/sysError/") || strings.Contains(c.Request.URL.Path, "/logViewer/") || c.Writer.Status() >= 300 && c.Writer.Status() < 500) {
|
||||
return
|
||||
}
|
||||
var response httpx.Response
|
||||
var response Response
|
||||
var body []byte
|
||||
if value, ok := c.Get(ctxRespBufferKey); ok {
|
||||
if buffer, valid := value.(*bytes.Buffer); valid {
|
||||
@@ -36,7 +34,7 @@ func ErrorAudit(logger *slog.Logger) gin.HandlerFunc {
|
||||
if json.Unmarshal(body, &response) != nil && privateErrors == "" {
|
||||
return
|
||||
}
|
||||
if response.Code == httpx.CodeSuccess && privateErrors == "" {
|
||||
if response.Code == CodeSuccess && privateErrors == "" {
|
||||
return
|
||||
}
|
||||
if privateErrors == "" && expectedClientFailure(response.Msg) {
|
||||
@@ -0,0 +1,18 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"kra/app/system/internal/server/httpx"
|
||||
)
|
||||
|
||||
const (
|
||||
CodeSuccess = httpx.CodeSuccess
|
||||
CodeError = httpx.CodeError
|
||||
CodePasswordChangeRequired = httpx.CodePasswordChangeRequired
|
||||
)
|
||||
|
||||
type Response = httpx.Response
|
||||
|
||||
func Write(c *gin.Context, code int, data any, message string) { httpx.Write(c, code, data, 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) }
|
||||
+4
-6
@@ -1,13 +1,11 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/service"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"kra/app/system/internal/service"
|
||||
"kra/app/system/internal/transport/httpx"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
@@ -20,7 +18,7 @@ func SecurityRateLimit(settings *service.SecurityService) gin.HandlerFunc {
|
||||
}
|
||||
config, err := settings.CurrentSecurity(c.Request.Context())
|
||||
if err != nil || config == nil {
|
||||
c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{"code": httpx.CodeError, "msg": "安全服务暂不可用"})
|
||||
c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{"code": CodeError, "msg": "安全服务暂不可用"})
|
||||
return
|
||||
}
|
||||
if !config.LimitEnable {
|
||||
@@ -34,11 +32,11 @@ func SecurityRateLimit(settings *service.SecurityService) gin.HandlerFunc {
|
||||
key := "KRA_SecLimit" + c.ClientIP() + c.FullPath()
|
||||
count, cacheErr := settings.IncrementRateLimit(c.Request.Context(), key, time.Duration(window)*time.Second)
|
||||
if cacheErr != nil {
|
||||
c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{"code": httpx.CodeError, "msg": "安全服务暂不可用"})
|
||||
c.AbortWithStatusJSON(http.StatusServiceUnavailable, gin.H{"code": CodeError, "msg": "安全服务暂不可用"})
|
||||
return
|
||||
}
|
||||
if int(count) > config.LimitCount {
|
||||
c.JSON(200, gin.H{"code": httpx.CodeError, "msg": "请求太过频繁,请稍后再试"})
|
||||
c.JSON(200, gin.H{"code": CodeError, "msg": "请求太过频繁,请稍后再试"})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
@@ -1,10 +1,8 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
// ProviderSet is server providers.
|
||||
var ProviderSet = wire.NewSet(NewGinEngineWithRuntime, NewGinServer, NewRoutes, handler.NewAuthority, handler.NewMenu, handler.NewAPI, handler.NewPermission, handler.NewOrganization, handler.NewAnnouncement, handler.NewEmail, handler.NewPayment, handler.NewTask, handler.NewMedia, handler.NewAudit, handler.NewExport, handler.NewVersion, handler.NewDictionary, handler.NewParameter, handler.NewAPIToken, handler.NewSystemConfig, handler.NewPublic, handler.NewUser, handler.NewNavigation, handler.NewSession, handler.NewSet)
|
||||
var ProviderSet = wire.NewSet(NewGinEngineWithRuntime, NewGinServer)
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package router
|
||||
|
||||
import "kra/app/system/internal/server/handler"
|
||||
|
||||
type Set = handler.Set
|
||||
type Announcement = handler.Announcement
|
||||
type API = handler.API
|
||||
type APIToken = handler.APIToken
|
||||
type Audit = handler.Audit
|
||||
type Authority = handler.Authority
|
||||
type Dictionary = handler.Dictionary
|
||||
type Email = handler.Email
|
||||
type Export = handler.Export
|
||||
type Media = handler.Media
|
||||
type Menu = handler.Menu
|
||||
type Navigation = handler.Navigation
|
||||
type Organization = handler.Organization
|
||||
type Parameter = handler.Parameter
|
||||
type Payment = handler.Payment
|
||||
type Permission = handler.Permission
|
||||
type Public = handler.Public
|
||||
type Session = handler.Session
|
||||
type SystemConfig = handler.SystemConfig
|
||||
type Task = handler.Task
|
||||
type User = handler.User
|
||||
type Version = handler.Version
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterAnnouncement(private, public *gin.RouterGroup, h *handler.Announcement) {
|
||||
func RegisterAnnouncement(private, public *gin.RouterGroup, h *Announcement) {
|
||||
privateInfo := private.Group("/info")
|
||||
privateInfo.POST("/createInfo", h.Create)
|
||||
privateInfo.DELETE("/deleteInfo", h.Delete)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterAPI(group, public *gin.RouterGroup, engine *gin.Engine, h *handler.API) {
|
||||
func RegisterAPI(group, public *gin.RouterGroup, engine *gin.Engine, h *API) {
|
||||
api := group.Group("/api")
|
||||
api.POST("/getApiList", h.List)
|
||||
api.POST("/getAllApis", h.All)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterAPIToken(group *gin.RouterGroup, handler *handler.APIToken) {
|
||||
func RegisterAPIToken(group *gin.RouterGroup, handler *APIToken) {
|
||||
router := group.Group("/sysApiToken")
|
||||
router.POST("/createApiToken", handler.Create)
|
||||
router.POST("/getApiTokenList", handler.List)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterAudit(group, public *gin.RouterGroup, h *handler.Audit) {
|
||||
func RegisterAudit(group, public *gin.RouterGroup, h *Audit) {
|
||||
operations := group.Group("/sysOperationRecord")
|
||||
operations.GET("/getSysOperationRecordList", h.Operations)
|
||||
operations.GET("/findSysOperationRecord", h.Operation)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterAuthority(group *gin.RouterGroup, h *handler.Authority) {
|
||||
func RegisterAuthority(group *gin.RouterGroup, h *Authority) {
|
||||
router := group.Group("/authority")
|
||||
router.POST("/getAuthorityList", h.List)
|
||||
router.POST("/createAuthority", h.Create)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterDictionary(group *gin.RouterGroup, h *handler.Dictionary) {
|
||||
func RegisterDictionary(group *gin.RouterGroup, h *Dictionary) {
|
||||
dictionaries := group.Group("/sysDictionary")
|
||||
dictionaries.POST("/createSysDictionary", h.Create)
|
||||
dictionaries.PUT("/updateSysDictionary", h.Update)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterEmail(group *gin.RouterGroup, h *handler.Email) {
|
||||
func RegisterEmail(group *gin.RouterGroup, h *Email) {
|
||||
email := group.Group("/email")
|
||||
email.POST("/emailTest", h.Test)
|
||||
email.POST("/sendEmail", h.Send)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterExport(group, public *gin.RouterGroup, h *handler.Export) {
|
||||
func RegisterExport(group, public *gin.RouterGroup, h *Export) {
|
||||
router := group.Group("/sysExportTemplate")
|
||||
router.POST("/createSysExportTemplate", h.Create)
|
||||
router.PUT("/updateSysExportTemplate", h.Update)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterMedia(group *gin.RouterGroup, h *handler.Media) {
|
||||
func RegisterMedia(group *gin.RouterGroup, h *Media) {
|
||||
files := group.Group("/fileUploadAndDownload")
|
||||
files.POST("/upload", h.Upload)
|
||||
files.POST("/getFileList", h.List)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterMenu(group *gin.RouterGroup, h *handler.Menu) {
|
||||
func RegisterMenu(group *gin.RouterGroup, h *Menu) {
|
||||
router := group.Group("/menu")
|
||||
router.POST("/getMenuList", h.List)
|
||||
router.POST("/getBaseMenuTree", h.Tree)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterOrganization(group *gin.RouterGroup, h *handler.Organization) {
|
||||
func RegisterOrganization(group *gin.RouterGroup, h *Organization) {
|
||||
departments := group.Group("/department")
|
||||
departments.POST("/getDepartmentList", h.ListDepartments)
|
||||
departments.POST("/createDepartment", h.CreateDepartment)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterParameter(group *gin.RouterGroup, handler *handler.Parameter) {
|
||||
func RegisterParameter(group *gin.RouterGroup, handler *Parameter) {
|
||||
router := group.Group("/sysParams")
|
||||
router.POST("/createSysParams", handler.Create)
|
||||
router.PUT("/updateSysParams", handler.Update)
|
||||
+5
-2
@@ -2,17 +2,20 @@ package router
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"kra/app/system/internal/transport/handler"
|
||||
)
|
||||
|
||||
func RegisterPayment(group, public *gin.RouterGroup, h *handler.Payment) {
|
||||
func RegisterPayment(group, public *gin.RouterGroup, h *Payment) {
|
||||
payment := group.Group("/payment")
|
||||
payment.GET("/configs", h.Configs)
|
||||
payment.POST("/config", h.SaveConfig)
|
||||
payment.GET("/orders", h.Orders)
|
||||
payment.POST("/order", h.Order)
|
||||
payment.GET("/orders/:provider/:tradeNo", h.OrderByPath)
|
||||
payment.POST("/create", h.Create)
|
||||
payment.POST("/query", h.Query)
|
||||
payment.POST("/refund", h.Refund)
|
||||
payment.POST("/orders/:provider/:tradeNo/refund", h.RefundByPath)
|
||||
payment.POST("/orders/:provider/:tradeNo/fulfill", h.FulfillByPath)
|
||||
payment.POST("/fulfill", h.Fulfill)
|
||||
public.POST("/payment/callback/:provider", h.Callback)
|
||||
}
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterPermission(group *gin.RouterGroup, h *handler.Permission) {
|
||||
func RegisterPermission(group *gin.RouterGroup, h *Permission) {
|
||||
buttons := group.Group("/authorityBtn")
|
||||
buttons.POST("/getAuthorityBtn", h.Buttons)
|
||||
buttons.POST("/setAuthorityBtn", h.SetButtons)
|
||||
@@ -0,0 +1,6 @@
|
||||
package router
|
||||
|
||||
import "github.com/google/wire"
|
||||
|
||||
// ProviderSet wires the system route registrar.
|
||||
var ProviderSet = wire.NewSet(NewRoutes)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterPublic(group *gin.RouterGroup, engine *gin.Engine, handler *handler.Public) {
|
||||
func RegisterPublic(group *gin.RouterGroup, engine *gin.Engine, handler *Public) {
|
||||
base := group.Group("/base")
|
||||
base.POST("/captcha", handler.Captcha)
|
||||
base.POST("/login", handler.Login)
|
||||
@@ -0,0 +1,42 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
platformmodule "kra/pkg/module"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type Routes struct {
|
||||
handlers *Set
|
||||
}
|
||||
|
||||
func NewRoutes(handlers *Set) *Routes { return &Routes{handlers: handlers} }
|
||||
|
||||
var _ platformmodule.RouteRegistrar = (*Routes)(nil)
|
||||
|
||||
func (r *Routes) RegisterRoutes(public, private *gin.RouterGroup, engine *gin.Engine) {
|
||||
if r == nil || r.handlers == nil {
|
||||
return
|
||||
}
|
||||
RegisterPublic(public, engine, r.handlers.Public)
|
||||
RegisterUser(private, r.handlers.User)
|
||||
RegisterNavigation(private, r.handlers.Navigation)
|
||||
RegisterSession(private, r.handlers.Session)
|
||||
RegisterAuthority(private, r.handlers.Authority)
|
||||
RegisterMenu(private, r.handlers.Menu)
|
||||
RegisterAPI(private, public, engine, r.handlers.API)
|
||||
RegisterPermission(private, r.handlers.Permission)
|
||||
RegisterOrganization(private, r.handlers.Organization)
|
||||
RegisterDictionary(private, r.handlers.Dictionary)
|
||||
RegisterParameter(private, r.handlers.Parameter)
|
||||
RegisterAPIToken(private, r.handlers.APIToken)
|
||||
RegisterSystemConfig(private, r.handlers.SystemConfig)
|
||||
RegisterVersion(private, r.handlers.Version)
|
||||
RegisterExport(private, public, r.handlers.Export)
|
||||
RegisterAudit(private, public, r.handlers.Audit)
|
||||
RegisterTask(private, r.handlers.Task)
|
||||
RegisterMedia(private, r.handlers.Media)
|
||||
RegisterAnnouncement(private, public, r.handlers.Announcement)
|
||||
RegisterEmail(private, r.handlers.Email)
|
||||
RegisterPayment(private, public, r.handlers.Payment)
|
||||
}
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterSystemConfig(group *gin.RouterGroup, handler *handler.SystemConfig) {
|
||||
func RegisterSystemConfig(group *gin.RouterGroup, handler *SystemConfig) {
|
||||
security := group.Group("/securityConfig")
|
||||
security.GET("/getSecurityConfig", handler.GetSecurity)
|
||||
security.POST("/setSecurityConfig", handler.SetSecurity)
|
||||
+1
-3
@@ -1,12 +1,10 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"kra/app/system/internal/transport/handler"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func RegisterTask(group *gin.RouterGroup, h *handler.Task) {
|
||||
func RegisterTask(group *gin.RouterGroup, h *Task) {
|
||||
router := group.Group("/timedTask")
|
||||
router.POST("/createTimedTask", h.Create)
|
||||
router.PUT("/updateTimedTask", h.Update)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user