Compare commits

...

12 Commits

Author SHA1 Message Date
truthhun 55256b7ecd 调整页面和排序
11 months ago
truthhun 3a179457b0 移除调试代码
11 months ago
truthhun aa653542b5 完成账户处罚
11 months ago
truthhun 289bbc7d6c 显示用户组
11 months ago
truthhun 006a8f1827 处罚调整
11 months ago
truthhun b09629b117 用户处罚管理
11 months ago
truthhun 7bc1cb40ef 批量取消处罚
11 months ago
truthhun e4d8131357 检索和过滤用户
11 months ago
truthhun 10bdfada92 列表管理
11 months ago
truthhun 51e052d25d 惩处服务
11 months ago
truthhun 232c67c96d 接口封装
11 months ago
truthhun 38d508d42e 设置预览页最小高度
11 months ago

File diff suppressed because it is too large Load Diff

@ -0,0 +1,497 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: api/v1/punishment.proto
/*
Package v1 is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package v1
import (
"context"
"io"
"net/http"
"github.com/golang/protobuf/descriptor"
"github.com/golang/protobuf/proto"
"github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/grpc-ecosystem/grpc-gateway/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = descriptor.ForMessage
var _ = metadata.Join
func request_PunishmentAPI_CreatePunishment_0(ctx context.Context, marshaler runtime.Marshaler, client PunishmentAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq CreatePunishmentRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.CreatePunishment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_PunishmentAPI_CreatePunishment_0(ctx context.Context, marshaler runtime.Marshaler, server PunishmentAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq CreatePunishmentRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.CreatePunishment(ctx, &protoReq)
return msg, metadata, err
}
func request_PunishmentAPI_UpdatePunishment_0(ctx context.Context, marshaler runtime.Marshaler, client PunishmentAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq Punishment
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.UpdatePunishment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_PunishmentAPI_UpdatePunishment_0(ctx context.Context, marshaler runtime.Marshaler, server PunishmentAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq Punishment
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.UpdatePunishment(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_PunishmentAPI_GetPunishment_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_PunishmentAPI_GetPunishment_0(ctx context.Context, marshaler runtime.Marshaler, client PunishmentAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetPunishmentRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_PunishmentAPI_GetPunishment_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.GetPunishment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_PunishmentAPI_GetPunishment_0(ctx context.Context, marshaler runtime.Marshaler, server PunishmentAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq GetPunishmentRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_PunishmentAPI_GetPunishment_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.GetPunishment(ctx, &protoReq)
return msg, metadata, err
}
var (
filter_PunishmentAPI_ListPunishment_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
)
func request_PunishmentAPI_ListPunishment_0(ctx context.Context, marshaler runtime.Marshaler, client PunishmentAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListPunishmentRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_PunishmentAPI_ListPunishment_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ListPunishment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_PunishmentAPI_ListPunishment_0(ctx context.Context, marshaler runtime.Marshaler, server PunishmentAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ListPunishmentRequest
var metadata runtime.ServerMetadata
if err := req.ParseForm(); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_PunishmentAPI_ListPunishment_0); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.ListPunishment(ctx, &protoReq)
return msg, metadata, err
}
func request_PunishmentAPI_CancelPunishment_0(ctx context.Context, marshaler runtime.Marshaler, client PunishmentAPIClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq CancelPunishmentRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.CancelPunishment(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_PunishmentAPI_CancelPunishment_0(ctx context.Context, marshaler runtime.Marshaler, server PunishmentAPIServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq CancelPunishmentRequest
var metadata runtime.ServerMetadata
newReader, berr := utilities.IOReaderFactory(req.Body)
if berr != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
}
if err := marshaler.NewDecoder(newReader()).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := server.CancelPunishment(ctx, &protoReq)
return msg, metadata, err
}
// RegisterPunishmentAPIHandlerServer registers the http handlers for service PunishmentAPI to "mux".
// UnaryRPC :call PunishmentAPIServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterPunishmentAPIHandlerFromEndpoint instead.
func RegisterPunishmentAPIHandlerServer(ctx context.Context, mux *runtime.ServeMux, server PunishmentAPIServer) error {
mux.Handle("POST", pattern_PunishmentAPI_CreatePunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_PunishmentAPI_CreatePunishment_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_CreatePunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_PunishmentAPI_UpdatePunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_PunishmentAPI_UpdatePunishment_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_UpdatePunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_PunishmentAPI_GetPunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_PunishmentAPI_GetPunishment_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_GetPunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_PunishmentAPI_ListPunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_PunishmentAPI_ListPunishment_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_ListPunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_PunishmentAPI_CancelPunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_PunishmentAPI_CancelPunishment_0(rctx, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_CancelPunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterPunishmentAPIHandlerFromEndpoint is same as RegisterPunishmentAPIHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterPunishmentAPIHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterPunishmentAPIHandler(ctx, mux, conn)
}
// RegisterPunishmentAPIHandler registers the http handlers for service PunishmentAPI to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterPunishmentAPIHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterPunishmentAPIHandlerClient(ctx, mux, NewPunishmentAPIClient(conn))
}
// RegisterPunishmentAPIHandlerClient registers the http handlers for service PunishmentAPI
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "PunishmentAPIClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "PunishmentAPIClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "PunishmentAPIClient" to call the correct interceptors.
func RegisterPunishmentAPIHandlerClient(ctx context.Context, mux *runtime.ServeMux, client PunishmentAPIClient) error {
mux.Handle("POST", pattern_PunishmentAPI_CreatePunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_PunishmentAPI_CreatePunishment_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_CreatePunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_PunishmentAPI_UpdatePunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_PunishmentAPI_UpdatePunishment_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_UpdatePunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_PunishmentAPI_GetPunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_PunishmentAPI_GetPunishment_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_GetPunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_PunishmentAPI_ListPunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_PunishmentAPI_ListPunishment_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_ListPunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("PUT", pattern_PunishmentAPI_CancelPunishment_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_PunishmentAPI_CancelPunishment_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_PunishmentAPI_CancelPunishment_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_PunishmentAPI_CreatePunishment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "punishment"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_PunishmentAPI_UpdatePunishment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "punishment"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_PunishmentAPI_GetPunishment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "punishment"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_PunishmentAPI_ListPunishment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "punishment", "list"}, "", runtime.AssumeColonVerbOpt(true)))
pattern_PunishmentAPI_CancelPunishment_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"api", "v1", "punishment", "cancel"}, "", runtime.AssumeColonVerbOpt(true)))
)
var (
forward_PunishmentAPI_CreatePunishment_0 = runtime.ForwardResponseMessage
forward_PunishmentAPI_UpdatePunishment_0 = runtime.ForwardResponseMessage
forward_PunishmentAPI_GetPunishment_0 = runtime.ForwardResponseMessage
forward_PunishmentAPI_ListPunishment_0 = runtime.ForwardResponseMessage
forward_PunishmentAPI_CancelPunishment_0 = runtime.ForwardResponseMessage
)

@ -0,0 +1,96 @@
syntax = "proto3";
import "google/protobuf/timestamp.proto";
import "gogoproto/gogo.proto";
// import "validate/validate.proto";
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
package api.v1;
option go_package = "moredoc/api/v1;v1";
option java_multiple_files = true;
option java_package = "api.v1";
// proto
message Punishment {
google.protobuf.Timestamp end_time = 1 [ (gogoproto.stdtime) = true ];
google.protobuf.Timestamp created_at = 2 [ (gogoproto.stdtime) = true ];
google.protobuf.Timestamp updated_at = 3 [ (gogoproto.stdtime) = true ];
int64 id = 4;
int64 user_id = 5;
int32 type = 6;
bool enable = 7;
string operators = 8;
string remark = 9;
string reason = 10;
string username = 11;
}
message CancelPunishmentRequest { repeated int64 id = 1; }
message GetPunishmentRequest { int64 id = 1; }
message ListPunishmentRequest {
int64 page = 1;
int64 size = 2;
string wd = 3;
repeated string field = 4;
string order = 5;
repeated int32 type = 6;
repeated int32 enable = 7;
repeated int64 user_id = 8;
}
message CreatePunishmentRequest {
google.protobuf.Timestamp end_time = 1 [ (gogoproto.stdtime) = true ];
int64 id = 2;
repeated int64 user_id = 3;
repeated int32 type = 4;
bool enable = 5;
string remark = 6;
string reason = 7;
}
message ListPunishmentReply {
int64 total = 1;
repeated Punishment punishment = 2;
}
service PunishmentAPI {
rpc CreatePunishment(CreatePunishmentRequest)
returns (google.protobuf.Empty) {
option (google.api.http) = {
post : '/api/v1/punishment',
body : '*',
};
}
rpc UpdatePunishment(Punishment) returns (google.protobuf.Empty) {
option (google.api.http) = {
put : '/api/v1/punishment',
body : '*',
};
}
rpc GetPunishment(GetPunishmentRequest) returns (Punishment) {
option (google.api.http) = {
get : '/api/v1/punishment',
};
}
rpc ListPunishment(ListPunishmentRequest) returns (ListPunishmentReply) {
option (google.api.http) = {
get : '/api/v1/punishment/list',
};
}
//
rpc CancelPunishment(CancelPunishmentRequest)
returns (google.protobuf.Empty) {
option (google.api.http) = {
put : '/api/v1/punishment/cancel',
body : '*',
};
}
}

@ -590,14 +590,15 @@ func (m *FindPasswordRequest) GetCaptchaId() string {
// 用户列表请求
type ListUserRequest struct {
Page int64 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
Size_ int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
Wd string `protobuf:"bytes,3,opt,name=wd,proto3" json:"wd,omitempty"`
Sort string `protobuf:"bytes,4,opt,name=sort,proto3" json:"sort,omitempty"`
Id []int64 `protobuf:"varint,5,rep,packed,name=id,proto3" json:"id,omitempty"`
GroupId []int64 `protobuf:"varint,6,rep,packed,name=group_id,json=groupId,proto3" json:"group_id,omitempty"`
Status []int32 `protobuf:"varint,7,rep,packed,name=status,proto3" json:"status,omitempty"`
Limit int64 `protobuf:"varint,8,opt,name=limit,proto3" json:"limit,omitempty"`
Page int64 `protobuf:"varint,1,opt,name=page,proto3" json:"page,omitempty"`
Size_ int64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"`
Wd string `protobuf:"bytes,3,opt,name=wd,proto3" json:"wd,omitempty"`
Sort string `protobuf:"bytes,4,opt,name=sort,proto3" json:"sort,omitempty"`
Id []int64 `protobuf:"varint,5,rep,packed,name=id,proto3" json:"id,omitempty"`
GroupId []int64 `protobuf:"varint,6,rep,packed,name=group_id,json=groupId,proto3" json:"group_id,omitempty"`
Status []int32 `protobuf:"varint,7,rep,packed,name=status,proto3" json:"status,omitempty"`
Limit int64 `protobuf:"varint,8,opt,name=limit,proto3" json:"limit,omitempty"`
Field []string `protobuf:"bytes,9,rep,name=field,proto3" json:"field,omitempty"`
}
func (m *ListUserRequest) Reset() { *m = ListUserRequest{} }
@ -689,6 +690,13 @@ func (m *ListUserRequest) GetLimit() int64 {
return 0
}
func (m *ListUserRequest) GetField() []string {
if m != nil {
return m.Field
}
return nil
}
// 用户列表响应
type ListUserReply struct {
Total int64 `protobuf:"varint,1,opt,name=total,proto3" json:"total,omitempty"`
@ -1407,112 +1415,113 @@ func init() {
func init() { proto.RegisterFile("api/v1/user.proto", fileDescriptor_b74e2e33d57193df) }
var fileDescriptor_b74e2e33d57193df = []byte{
// 1674 bytes of a gzipped FileDescriptorProto
// 1687 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x58, 0x4f, 0x6f, 0x23, 0x49,
0x15, 0x9f, 0xf6, 0x7f, 0x3f, 0x27, 0x99, 0xa4, 0xe2, 0xd8, 0x4e, 0x67, 0x26, 0x76, 0x7a, 0x05,
0x84, 0x61, 0x14, 0x33, 0x19, 0xc1, 0x4a, 0xb3, 0x5a, 0xa1, 0xcc, 0x64, 0x77, 0x15, 0x14, 0x44,
0xe8, 0x4c, 0x84, 0xb4, 0x7b, 0x88, 0x2a, 0xee, 0x8a, 0x29, 0xb6, 0xbb, 0xab, 0xa7, 0xbb, 0x1c,
0x6f, 0x38, 0x72, 0xe1, 0xba, 0x12, 0x5f, 0x03, 0x89, 0x1b, 0x27, 0x3e, 0x00, 0x27, 0xb4, 0xd2,
0x5e, 0x38, 0x2d, 0x68, 0x86, 0x2b, 0x97, 0xfd, 0x00, 0x08, 0xd5, 0xbf, 0xb6, 0xbb, 0x9d, 0x5e,
0x92, 0x99, 0x5b, 0xbf, 0x57, 0xaf, 0x7e, 0x55, 0xf5, 0xde, 0xef, 0xfd, 0xb1, 0x61, 0x0d, 0x47,
0x74, 0x78, 0xf5, 0x64, 0x38, 0x49, 0x48, 0xbc, 0x17, 0xc5, 0x8c, 0x33, 0x54, 0xc3, 0x11, 0xdd,
0xbb, 0x7a, 0x62, 0xf7, 0xc7, 0x8c, 0x8d, 0x7d, 0x32, 0x94, 0xda, 0x8b, 0xc9, 0xe5, 0x90, 0xd3,
0x80, 0x24, 0x1c, 0x07, 0x91, 0x32, 0xb4, 0xdb, 0x63, 0x36, 0x66, 0xf2, 0x73, 0x28, 0xbe, 0xb4,
0xf6, 0x81, 0xde, 0x26, 0x80, 0x71, 0x18, 0x32, 0x8e, 0x39, 0x65, 0x61, 0xa2, 0x57, 0xb7, 0xf2,
0xa0, 0x24, 0x88, 0xf8, 0xb5, 0x5e, 0xec, 0xea, 0xcb, 0x44, 0x24, 0x0e, 0x68, 0x92, 0x50, 0x16,
0xea, 0x85, 0x0d, 0xbd, 0xe0, 0xb1, 0x69, 0xe8, 0x33, 0xec, 0x29, 0xb5, 0xf3, 0x9f, 0x2a, 0x54,
0xce, 0x12, 0x12, 0xa3, 0x0f, 0xa0, 0xe1, 0xb3, 0x31, 0x0d, 0xcf, 0x31, 0xef, 0xad, 0x0f, 0xac,
0xdd, 0xd6, 0xbe, 0xbd, 0xa7, 0x0e, 0xda, 0x33, 0x07, 0xed, 0xbd, 0x34, 0xb7, 0x7f, 0x5e, 0xf9,
0xf2, 0x9f, 0x7d, 0xcb, 0xad, 0xcb, 0x1d, 0x07, 0x1c, 0xfd, 0x0c, 0x60, 0x14, 0x13, 0xcc, 0x89,
0x27, 0xb6, 0xb7, 0x6f, 0xb9, 0xbd, 0xa9, 0xf7, 0x28, 0x80, 0x49, 0xe4, 0x19, 0x80, 0x8d, 0xdb,
0x02, 0xe8, 0x3d, 0x07, 0x1c, 0xad, 0x40, 0x89, 0x7a, 0x3d, 0x6b, 0x60, 0xed, 0x96, 0xdd, 0x12,
0xf5, 0x90, 0x0d, 0x0d, 0x11, 0x8f, 0x10, 0x07, 0xa4, 0x57, 0x1a, 0x58, 0xbb, 0x4d, 0x37, 0x95,
0x51, 0x07, 0x6a, 0x01, 0xbb, 0xa0, 0x3e, 0xe9, 0x95, 0xe5, 0x8a, 0x96, 0x50, 0x1b, 0xaa, 0x24,
0xc0, 0xd4, 0xef, 0x55, 0xa4, 0x5a, 0x09, 0xa8, 0x07, 0x75, 0xec, 0x79, 0x31, 0x49, 0x92, 0x5e,
0x55, 0xea, 0x8d, 0x88, 0x1e, 0x40, 0x33, 0xa1, 0xe3, 0x10, 0xf3, 0x49, 0x4c, 0x7a, 0x35, 0xb9,
0x36, 0x53, 0x20, 0x07, 0x96, 0x7d, 0x9c, 0xf0, 0x73, 0xe5, 0x55, 0x1a, 0xf5, 0xea, 0xd2, 0xa2,
0x25, 0x94, 0xc7, 0x42, 0x77, 0x14, 0xa1, 0x3e, 0xb4, 0x62, 0x32, 0xa6, 0x09, 0x27, 0xb1, 0xb0,
0x68, 0x48, 0x0b, 0x30, 0xaa, 0xa3, 0x08, 0x6d, 0x41, 0xd3, 0x63, 0xa3, 0xf3, 0x11, 0x9b, 0x84,
0xbc, 0xd7, 0x1c, 0x58, 0xbb, 0x55, 0xb7, 0xe1, 0xb1, 0xd1, 0x0b, 0x21, 0xa3, 0x1d, 0x58, 0xba,
0x64, 0xbe, 0xcf, 0xa6, 0x7a, 0x1d, 0xe4, 0x7a, 0x4b, 0xe9, 0x94, 0xc9, 0x43, 0x80, 0x4b, 0x1c,
0x26, 0xda, 0xa0, 0x25, 0x0d, 0x9a, 0x42, 0xa3, 0x96, 0xbf, 0x07, 0x2b, 0x97, 0xf8, 0x8a, 0xc5,
0x94, 0x13, 0x6d, 0xb2, 0x24, 0x4d, 0x96, 0x8d, 0x56, 0x99, 0xbd, 0x07, 0xcb, 0x23, 0x16, 0x04,
0x24, 0xe4, 0xda, 0x6a, 0x59, 0x5a, 0x2d, 0x69, 0xa5, 0x32, 0xea, 0x40, 0x2d, 0xe1, 0x98, 0x4f,
0x92, 0xde, 0x8a, 0x5c, 0xd5, 0x92, 0xd0, 0xe3, 0x2b, 0xcc, 0x71, 0xdc, 0xbb, 0xaf, 0xbc, 0xad,
0x24, 0x11, 0x21, 0xea, 0x91, 0x90, 0x53, 0x7e, 0xdd, 0x5b, 0x55, 0x11, 0x32, 0xb2, 0x58, 0x8b,
0x09, 0xf6, 0x65, 0xf4, 0xd6, 0xd4, 0x9a, 0x91, 0xd1, 0x26, 0x34, 0xc6, 0x31, 0x9b, 0x44, 0xe7,
0xd4, 0xeb, 0xa1, 0x41, 0x79, 0xb7, 0xec, 0xd6, 0xa5, 0x7c, 0xe4, 0x09, 0x87, 0x8c, 0x62, 0xe2,
0x51, 0x73, 0xcd, 0x8e, 0x72, 0x88, 0xd2, 0xc9, 0x5b, 0x3a, 0x5f, 0x5b, 0xd0, 0x75, 0xb5, 0x7f,
0x0f, 0x42, 0x4f, 0x06, 0xc2, 0x25, 0xaf, 0x26, 0x24, 0xe1, 0xe8, 0xfd, 0x39, 0xce, 0x08, 0x26,
0x35, 0x9f, 0x6f, 0x7d, 0xfb, 0x4d, 0xbf, 0x7b, 0x85, 0x7d, 0x2a, 0x68, 0xf6, 0xcc, 0x09, 0x68,
0xf8, 0xe1, 0xd3, 0xc7, 0x01, 0xfe, 0xe2, 0xc3, 0xa7, 0xfb, 0xce, 0x1c, 0xa1, 0x7e, 0x0c, 0x8d,
0x08, 0x27, 0xc9, 0x94, 0xc5, 0x9e, 0x22, 0xdb, 0xf3, 0xf6, 0xb7, 0xdf, 0xf4, 0x57, 0xb3, 0x1b,
0x7f, 0xea, 0xb8, 0xa9, 0x95, 0x20, 0xd5, 0x08, 0x47, 0x7c, 0xf4, 0x1b, 0xac, 0x39, 0x68, 0x44,
0x11, 0x31, 0xfd, 0x29, 0x1e, 0xa8, 0x98, 0xd8, 0xd4, 0x9a, 0x23, 0x6f, 0xc6, 0xd1, 0xea, 0x1c,
0x47, 0x9d, 0x1f, 0xc1, 0xc6, 0x27, 0x84, 0x8b, 0x3c, 0x7e, 0xa1, 0x2c, 0xcd, 0x93, 0x10, 0x54,
0xf8, 0x75, 0xa4, 0x9f, 0xe3, 0xca, 0x6f, 0xe7, 0x10, 0x40, 0x3f, 0x3b, 0xf2, 0xaf, 0x05, 0x20,
0x67, 0x9f, 0x93, 0x50, 0x9b, 0x28, 0x01, 0x0d, 0xa0, 0x22, 0x5e, 0x27, 0x5f, 0xd3, 0xda, 0x5f,
0xda, 0x53, 0xf5, 0x6c, 0x4f, 0x9c, 0xe0, 0xca, 0x15, 0xe7, 0x3d, 0x58, 0x3b, 0x24, 0x3e, 0xe1,
0x44, 0xea, 0xf4, 0x71, 0x26, 0x0b, 0xcb, 0x2a, 0x0b, 0x9d, 0x01, 0xac, 0xe8, 0x7b, 0xe5, 0x2d,
0x74, 0x9e, 0x3a, 0x7f, 0xb2, 0x60, 0xfd, 0x63, 0x1a, 0x7a, 0x27, 0xda, 0x33, 0xc6, 0x2e, 0x7d,
0xa7, 0x35, 0x9f, 0x8b, 0xe9, 0x65, 0x4b, 0xf3, 0x97, 0x9d, 0x77, 0x7f, 0xf9, 0xae, 0xee, 0xaf,
0x7c, 0x97, 0xfb, 0xab, 0x39, 0xf7, 0x3b, 0x7f, 0xb1, 0xe0, 0xfe, 0x31, 0x4d, 0x32, 0x4f, 0x42,
0x50, 0x89, 0xf0, 0x98, 0xe8, 0x47, 0xc9, 0x6f, 0xa1, 0x4b, 0xe8, 0xef, 0x54, 0xe9, 0x29, 0xbb,
0xf2, 0x5b, 0x3c, 0x7d, 0xaa, 0x2f, 0xe8, 0x96, 0xa6, 0x9e, 0xb4, 0x61, 0x31, 0xd7, 0x37, 0x90,
0xdf, 0xda, 0x3d, 0x55, 0xe3, 0xc0, 0x0c, 0xd9, 0x6b, 0x59, 0xb2, 0xcf, 0xf2, 0xad, 0x3e, 0x28,
0xcf, 0xe5, 0x5b, 0x1b, 0xaa, 0x3e, 0x0d, 0x28, 0x97, 0xd5, 0xa4, 0xec, 0x2a, 0xc1, 0xf9, 0x04,
0x96, 0x67, 0xf7, 0x4e, 0xe3, 0xce, 0xb1, 0xaf, 0xaf, 0xad, 0x84, 0xb9, 0xb8, 0x97, 0x0b, 0xe2,
0xfe, 0x39, 0xac, 0xe7, 0xa9, 0x26, 0xe0, 0x3a, 0x50, 0x23, 0x21, 0xbe, 0xf0, 0x95, 0x1b, 0x1a,
0xae, 0x96, 0xf4, 0x83, 0x54, 0xb8, 0xc4, 0x83, 0x8a, 0x89, 0x6f, 0xa8, 0x5a, 0x99, 0xa3, 0xea,
0x1f, 0x2c, 0xd8, 0x3c, 0x93, 0x35, 0x5e, 0x1c, 0x98, 0xe7, 0x48, 0xbe, 0xe6, 0xef, 0xc0, 0x12,
0xf3, 0xbd, 0xf3, 0x6c, 0x2a, 0xba, 0x2d, 0xe6, 0xa7, 0xec, 0x42, 0xef, 0xc3, 0x52, 0x48, 0xa6,
0xe7, 0xb7, 0xa2, 0x4b, 0x2b, 0x24, 0x53, 0xb3, 0xd1, 0xf9, 0x05, 0x74, 0xf5, 0xb3, 0x4f, 0xd2,
0xce, 0x9a, 0xa8, 0xa7, 0xef, 0x03, 0xcc, 0xba, 0xad, 0x24, 0x7f, 0x6b, 0x1f, 0x19, 0xcf, 0xcd,
0xac, 0xdd, 0x39, 0x2b, 0xe7, 0xaf, 0x16, 0xac, 0x9c, 0x7e, 0x67, 0x66, 0x64, 0xaa, 0x51, 0xe9,
0x6d, 0xab, 0xd1, 0xed, 0xd2, 0x61, 0x9e, 0x65, 0x95, 0x2c, 0xcb, 0x6e, 0xae, 0x37, 0xff, 0xb5,
0xa0, 0x7e, 0x78, 0x1d, 0xe2, 0x80, 0x8e, 0x16, 0xee, 0xdd, 0x85, 0xba, 0xb8, 0xca, 0xb9, 0x0e,
0x7b, 0xd9, 0xad, 0x09, 0xf1, 0x48, 0x85, 0x9e, 0x85, 0x9c, 0x84, 0x3c, 0x0d, 0xbd, 0x12, 0x33,
0xa1, 0xaf, 0xaa, 0xd0, 0x67, 0x1a, 0x78, 0x3d, 0xd7, 0xc0, 0xb3, 0xe3, 0x46, 0xf5, 0x5d, 0xc7,
0x8d, 0xda, 0x9d, 0xc7, 0x0d, 0xe7, 0x63, 0xe8, 0x98, 0x74, 0xd2, 0x7e, 0xb8, 0x63, 0x35, 0xf8,
0x79, 0xa5, 0x51, 0x5e, 0xad, 0x38, 0xbf, 0x86, 0xf6, 0x02, 0x4e, 0x71, 0x76, 0xfe, 0x10, 0xea,
0x9e, 0xb2, 0xd2, 0x09, 0x7a, 0xdf, 0xd0, 0xcc, 0x6c, 0x36, 0xeb, 0xce, 0x9f, 0x2d, 0xa8, 0x9c,
0xd2, 0x71, 0x78, 0xfb, 0xf0, 0x74, 0xa1, 0x2e, 0x86, 0x17, 0xe1, 0x90, 0xb2, 0x6e, 0xe0, 0x74,
0x1c, 0xea, 0xd1, 0x2a, 0xd2, 0x69, 0x59, 0xa2, 0xd1, 0xbb, 0x7b, 0xbf, 0x0d, 0x55, 0x3c, 0xc5,
0xb1, 0x27, 0x1d, 0x5f, 0x75, 0x95, 0xe0, 0x1c, 0x40, 0x37, 0x75, 0x85, 0x9e, 0x51, 0xef, 0xe8,
0x53, 0xe7, 0x33, 0xd8, 0x58, 0x84, 0x28, 0x76, 0xe7, 0x63, 0x68, 0x98, 0x69, 0x58, 0xfb, 0x73,
0x35, 0xf5, 0xa7, 0xd9, 0x9e, 0x5a, 0xec, 0xff, 0x7d, 0x05, 0xea, 0x02, 0xf9, 0xe0, 0xe4, 0x08,
0x61, 0x68, 0x98, 0x21, 0x02, 0xf5, 0xcd, 0x9e, 0x82, 0xb1, 0xc2, 0x4e, 0x6b, 0xc1, 0xac, 0xeb,
0x3a, 0x83, 0xdf, 0x7f, 0xfd, 0xef, 0x3f, 0x96, 0x6c, 0x67, 0x63, 0x38, 0xf7, 0xe3, 0x61, 0x68,
0x06, 0xbf, 0x67, 0xd6, 0x23, 0xf4, 0x19, 0x54, 0xa5, 0xfd, 0xdb, 0xe1, 0x3f, 0x94, 0xf8, 0x5d,
0x07, 0x65, 0xf0, 0xe5, 0x28, 0x2a, 0xc0, 0xcf, 0xa0, 0x76, 0xcc, 0xc6, 0x6c, 0xc2, 0x51, 0x67,
0x21, 0x70, 0x1f, 0x89, 0x5f, 0x13, 0x76, 0x81, 0xde, 0xd9, 0x92, 0xc0, 0x1b, 0x8f, 0xd6, 0xf3,
0xc0, 0x02, 0xec, 0x23, 0xa8, 0xeb, 0x22, 0x89, 0x3a, 0xe6, 0x52, 0xd9, 0xfe, 0x6f, 0x67, 0x5a,
0x8a, 0xd3, 0x96, 0x68, 0x2b, 0x68, 0x69, 0x1e, 0x0d, 0xbd, 0x02, 0xb4, 0x58, 0xf4, 0xd1, 0x4e,
0xba, 0xb3, 0xa8, 0x21, 0x14, 0x5e, 0x5a, 0x7b, 0xdb, 0xce, 0x7a, 0xdb, 0x94, 0x3f, 0xe1, 0x90,
0x4f, 0x61, 0x6d, 0x0e, 0x36, 0x66, 0x97, 0xe2, 0xf7, 0x40, 0xe6, 0xae, 0x85, 0xe0, 0x7d, 0x09,
0xbe, 0x69, 0xb7, 0xb3, 0xe0, 0x0a, 0x43, 0x39, 0x1b, 0x66, 0x93, 0x12, 0xda, 0x4c, 0x29, 0x96,
0x9f, 0x9e, 0x0a, 0x4f, 0xd0, 0x5e, 0x7a, 0x94, 0xf5, 0x92, 0x0b, 0xf5, 0x03, 0xcf, 0xcb, 0x3a,
0x3b, 0xdb, 0x52, 0x0a, 0x01, 0xbb, 0x12, 0x70, 0xcd, 0xc9, 0x00, 0x8a, 0xab, 0xba, 0x50, 0x3f,
0xcd, 0x07, 0xf0, 0x6e, 0x98, 0xf6, 0x02, 0xe6, 0x19, 0x34, 0x4c, 0x52, 0xa2, 0x6e, 0x4a, 0xd5,
0xec, 0x0c, 0x65, 0x6f, 0x2c, 0x2e, 0x08, 0x1a, 0x6f, 0x4a, 0xd0, 0x75, 0xb4, 0x96, 0x65, 0x1b,
0x4d, 0x38, 0xfa, 0x6d, 0x3a, 0x5a, 0xbe, 0x30, 0xa3, 0x5b, 0x8e, 0x72, 0xd9, 0x51, 0xd8, 0xde,
0x2a, 0x5a, 0x16, 0x07, 0x3d, 0x90, 0x07, 0x75, 0x50, 0x36, 0x88, 0x66, 0x34, 0x09, 0x01, 0x2d,
0x36, 0xff, 0xc2, 0xd4, 0xe9, 0xe7, 0x0e, 0xca, 0x0f, 0x0c, 0x86, 0x31, 0xa8, 0x9b, 0x65, 0x4c,
0x6a, 0x86, 0x02, 0x40, 0x2f, 0x70, 0x78, 0x74, 0x16, 0x89, 0xc2, 0x73, 0xc8, 0x46, 0x13, 0xf1,
0x2b, 0xeb, 0xce, 0xa9, 0xfa, 0x03, 0x79, 0xcc, 0x0e, 0xea, 0xe7, 0xde, 0x14, 0xd2, 0x89, 0x04,
0xf6, 0x0c, 0x70, 0x30, 0x9b, 0x69, 0x4d, 0x53, 0xdf, 0xce, 0xc7, 0x23, 0xdb, 0xe5, 0xec, 0x07,
0x85, 0xeb, 0xc5, 0xde, 0xd4, 0xad, 0x09, 0x1d, 0x43, 0x53, 0x74, 0xa6, 0x97, 0xcc, 0xc3, 0xd7,
0x85, 0x8f, 0x4a, 0x73, 0x4f, 0x98, 0x1a, 0x1e, 0xd8, 0x59, 0x1e, 0x88, 0xfe, 0x84, 0x7e, 0x25,
0x79, 0x20, 0xac, 0x88, 0xf7, 0x16, 0x90, 0xe8, 0x06, 0xc8, 0x57, 0xb0, 0x9a, 0x6f, 0x23, 0xb3,
0x2a, 0x5c, 0xd0, 0xa3, 0xec, 0x87, 0xc5, 0x06, 0x73, 0x05, 0x19, 0x65, 0x4b, 0x90, 0x69, 0x2e,
0xe8, 0x8b, 0xec, 0xaf, 0xa0, 0x53, 0x4e, 0xa2, 0x5f, 0x86, 0x04, 0xa5, 0x9c, 0xbd, 0xe1, 0x27,
0x52, 0x61, 0xdc, 0x1f, 0xcb, 0xa3, 0xbe, 0xef, 0xec, 0x64, 0x8e, 0xba, 0xa4, 0xa1, 0x67, 0x2a,
0xde, 0x30, 0xe1, 0x24, 0x62, 0xa1, 0xac, 0x4e, 0x37, 0x9c, 0xfc, 0x72, 0xca, 0xde, 0xe9, 0x64,
0xfb, 0xff, 0x9c, 0xcc, 0xa7, 0xec, 0x99, 0xf5, 0xe8, 0xf9, 0x4f, 0xfe, 0xf6, 0x7a, 0xdb, 0xfa,
0xea, 0xf5, 0xb6, 0xf5, 0xaf, 0xd7, 0xdb, 0xd6, 0x97, 0x6f, 0xb6, 0xef, 0x7d, 0xf5, 0x66, 0xfb,
0xde, 0x3f, 0xde, 0x6c, 0xdf, 0x03, 0xfd, 0xf7, 0xd9, 0x89, 0xf5, 0xe9, 0x5a, 0xc0, 0x62, 0xe2,
0xb1, 0x91, 0x06, 0xfc, 0xe0, 0xea, 0xc9, 0x45, 0x4d, 0x1e, 0xfa, 0xf4, 0x7f, 0x01, 0x00, 0x00,
0xff, 0xff, 0x46, 0x56, 0xfd, 0x90, 0x77, 0x13, 0x00, 0x00,
0x6f, 0x38, 0x72, 0xe1, 0xba, 0x12, 0x5f, 0x03, 0x89, 0x2f, 0xc0, 0x07, 0xe0, 0x80, 0xd0, 0x4a,
0x7b, 0xe1, 0xb4, 0xa0, 0x19, 0xae, 0x5c, 0xf6, 0x03, 0x20, 0x54, 0xff, 0xda, 0xee, 0x76, 0x7a,
0x49, 0x66, 0x6e, 0xfd, 0x5e, 0xbd, 0xfa, 0x55, 0xd5, 0x7b, 0xbf, 0xf7, 0xc7, 0x86, 0x35, 0x1c,
0xd1, 0xe1, 0xd5, 0x93, 0xe1, 0x24, 0x21, 0xf1, 0x5e, 0x14, 0x33, 0xce, 0x50, 0x0d, 0x47, 0x74,
0xef, 0xea, 0x89, 0xdd, 0x1f, 0x33, 0x36, 0xf6, 0xc9, 0x50, 0x6a, 0x2f, 0x26, 0x97, 0x43, 0x4e,
0x03, 0x92, 0x70, 0x1c, 0x44, 0xca, 0xd0, 0x6e, 0x8f, 0xd9, 0x98, 0xc9, 0xcf, 0xa1, 0xf8, 0xd2,
0xda, 0x07, 0x7a, 0x9b, 0x00, 0xc6, 0x61, 0xc8, 0x38, 0xe6, 0x94, 0x85, 0x89, 0x5e, 0xdd, 0xca,
0x83, 0x92, 0x20, 0xe2, 0xd7, 0x7a, 0xb1, 0xab, 0x2f, 0x13, 0x91, 0x38, 0xa0, 0x49, 0x42, 0x59,
0xa8, 0x17, 0x36, 0xf4, 0x82, 0xc7, 0xa6, 0xa1, 0xcf, 0xb0, 0xa7, 0xd4, 0xce, 0x7f, 0xaa, 0x50,
0x39, 0x4b, 0x48, 0x8c, 0x3e, 0x80, 0x86, 0xcf, 0xc6, 0x34, 0x3c, 0xc7, 0xbc, 0xb7, 0x3e, 0xb0,
0x76, 0x5b, 0xfb, 0xf6, 0x9e, 0x3a, 0x68, 0xcf, 0x1c, 0xb4, 0xf7, 0xd2, 0xdc, 0xfe, 0x79, 0xe5,
0xcb, 0x7f, 0xf6, 0x2d, 0xb7, 0x2e, 0x77, 0x1c, 0x70, 0xf4, 0x33, 0x80, 0x51, 0x4c, 0x30, 0x27,
0x9e, 0xd8, 0xde, 0xbe, 0xe5, 0xf6, 0xa6, 0xde, 0xa3, 0x00, 0x26, 0x91, 0x67, 0x00, 0x36, 0x6e,
0x0b, 0xa0, 0xf7, 0x1c, 0x70, 0xb4, 0x02, 0x25, 0xea, 0xf5, 0xac, 0x81, 0xb5, 0x5b, 0x76, 0x4b,
0xd4, 0x43, 0x36, 0x34, 0x44, 0x3c, 0x42, 0x1c, 0x90, 0x5e, 0x69, 0x60, 0xed, 0x36, 0xdd, 0x54,
0x46, 0x1d, 0xa8, 0x05, 0xec, 0x82, 0xfa, 0xa4, 0x57, 0x96, 0x2b, 0x5a, 0x42, 0x6d, 0xa8, 0x92,
0x00, 0x53, 0xbf, 0x57, 0x91, 0x6a, 0x25, 0xa0, 0x1e, 0xd4, 0xb1, 0xe7, 0xc5, 0x24, 0x49, 0x7a,
0x55, 0xa9, 0x37, 0x22, 0x7a, 0x00, 0xcd, 0x84, 0x8e, 0x43, 0xcc, 0x27, 0x31, 0xe9, 0xd5, 0xe4,
0xda, 0x4c, 0x81, 0x1c, 0x58, 0xf6, 0x71, 0xc2, 0xcf, 0x95, 0x57, 0x69, 0xd4, 0xab, 0x4b, 0x8b,
0x96, 0x50, 0x1e, 0x0b, 0xdd, 0x51, 0x84, 0xfa, 0xd0, 0x8a, 0xc9, 0x98, 0x26, 0x9c, 0xc4, 0xc2,
0xa2, 0x21, 0x2d, 0xc0, 0xa8, 0x8e, 0x22, 0xb4, 0x05, 0x4d, 0x8f, 0x8d, 0xce, 0x47, 0x6c, 0x12,
0xf2, 0x5e, 0x73, 0x60, 0xed, 0x56, 0xdd, 0x86, 0xc7, 0x46, 0x2f, 0x84, 0x8c, 0x76, 0x60, 0xe9,
0x92, 0xf9, 0x3e, 0x9b, 0xea, 0x75, 0x90, 0xeb, 0x2d, 0xa5, 0x53, 0x26, 0x0f, 0x01, 0x2e, 0x71,
0x98, 0x68, 0x83, 0x96, 0x34, 0x68, 0x0a, 0x8d, 0x5a, 0xfe, 0x1e, 0xac, 0x5c, 0xe2, 0x2b, 0x16,
0x53, 0x4e, 0xb4, 0xc9, 0x92, 0x34, 0x59, 0x36, 0x5a, 0x65, 0xf6, 0x1e, 0x2c, 0x8f, 0x58, 0x10,
0x90, 0x90, 0x6b, 0xab, 0x65, 0x69, 0xb5, 0xa4, 0x95, 0xca, 0xa8, 0x03, 0xb5, 0x84, 0x63, 0x3e,
0x49, 0x7a, 0x2b, 0x72, 0x55, 0x4b, 0x42, 0x8f, 0xaf, 0x30, 0xc7, 0x71, 0xef, 0xbe, 0xf2, 0xb6,
0x92, 0x44, 0x84, 0xa8, 0x47, 0x42, 0x4e, 0xf9, 0x75, 0x6f, 0x55, 0x45, 0xc8, 0xc8, 0x62, 0x2d,
0x26, 0xd8, 0x97, 0xd1, 0x5b, 0x53, 0x6b, 0x46, 0x46, 0x9b, 0xd0, 0x18, 0xc7, 0x6c, 0x12, 0x9d,
0x53, 0xaf, 0x87, 0x06, 0xe5, 0xdd, 0xb2, 0x5b, 0x97, 0xf2, 0x91, 0x27, 0x1c, 0x32, 0x8a, 0x89,
0x47, 0xcd, 0x35, 0x3b, 0xca, 0x21, 0x4a, 0x27, 0x6f, 0xe9, 0x7c, 0x6d, 0x41, 0xd7, 0xd5, 0xfe,
0x3d, 0x08, 0x3d, 0x19, 0x08, 0x97, 0xbc, 0x9a, 0x90, 0x84, 0xa3, 0xf7, 0xe7, 0x38, 0x23, 0x98,
0xd4, 0x7c, 0xbe, 0xf5, 0xed, 0x37, 0xfd, 0xee, 0x15, 0xf6, 0xa9, 0xa0, 0xd9, 0x33, 0x27, 0xa0,
0xe1, 0x87, 0x4f, 0x1f, 0x07, 0xf8, 0x8b, 0x0f, 0x9f, 0xee, 0x3b, 0x73, 0x84, 0xfa, 0x31, 0x34,
0x22, 0x9c, 0x24, 0x53, 0x16, 0x7b, 0x8a, 0x6c, 0xcf, 0xdb, 0xdf, 0x7e, 0xd3, 0x5f, 0xcd, 0x6e,
0xfc, 0xa9, 0xe3, 0xa6, 0x56, 0x82, 0x54, 0x23, 0x1c, 0xf1, 0xd1, 0x6f, 0xb0, 0xe6, 0xa0, 0x11,
0x45, 0xc4, 0xf4, 0xa7, 0x78, 0xa0, 0x62, 0x62, 0x53, 0x6b, 0x8e, 0xbc, 0x19, 0x47, 0xab, 0x73,
0x1c, 0x75, 0x7e, 0x04, 0x1b, 0x9f, 0x10, 0x2e, 0xf2, 0xf8, 0x85, 0xb2, 0x34, 0x4f, 0x42, 0x50,
0xe1, 0xd7, 0x91, 0x7e, 0x8e, 0x2b, 0xbf, 0x9d, 0x43, 0x00, 0xfd, 0xec, 0xc8, 0xbf, 0x16, 0x80,
0x9c, 0x7d, 0x4e, 0x42, 0x6d, 0xa2, 0x04, 0x34, 0x80, 0x8a, 0x78, 0x9d, 0x7c, 0x4d, 0x6b, 0x7f,
0x69, 0x4f, 0xd5, 0xb3, 0x3d, 0x71, 0x82, 0x2b, 0x57, 0x9c, 0xf7, 0x60, 0xed, 0x90, 0xf8, 0x84,
0x13, 0xa9, 0xd3, 0xc7, 0x99, 0x2c, 0x2c, 0xab, 0x2c, 0x74, 0x06, 0xb0, 0xa2, 0xef, 0x95, 0xb7,
0xd0, 0x79, 0xea, 0xfc, 0xc9, 0x82, 0xf5, 0x8f, 0x69, 0xe8, 0x9d, 0x68, 0xcf, 0x18, 0xbb, 0xf4,
0x9d, 0xd6, 0x7c, 0x2e, 0xa6, 0x97, 0x2d, 0xcd, 0x5f, 0x76, 0xde, 0xfd, 0xe5, 0xbb, 0xba, 0xbf,
0xf2, 0x5d, 0xee, 0xaf, 0xe6, 0xdc, 0xef, 0xfc, 0xcd, 0x82, 0xfb, 0xc7, 0x34, 0xc9, 0x3c, 0x09,
0x41, 0x25, 0xc2, 0x63, 0xa2, 0x1f, 0x25, 0xbf, 0x85, 0x2e, 0xa1, 0xbf, 0x53, 0xa5, 0xa7, 0xec,
0xca, 0x6f, 0xf1, 0xf4, 0xa9, 0xbe, 0xa0, 0x5b, 0x9a, 0x7a, 0xd2, 0x86, 0xc5, 0x5c, 0xdf, 0x40,
0x7e, 0x6b, 0xf7, 0x54, 0x8d, 0x03, 0x33, 0x64, 0xaf, 0x65, 0xc9, 0x3e, 0xcb, 0xb7, 0xfa, 0xa0,
0x3c, 0x97, 0x6f, 0x6d, 0xa8, 0xfa, 0x34, 0xa0, 0x5c, 0x56, 0x93, 0xb2, 0xab, 0x04, 0xa1, 0xbd,
0xa4, 0xc4, 0xf7, 0x7a, 0xcd, 0x41, 0x59, 0x78, 0x4e, 0x0a, 0xce, 0x27, 0xb0, 0x3c, 0x7b, 0x4d,
0xca, 0x06, 0x8e, 0x7d, 0xfd, 0x18, 0x25, 0xcc, 0xb1, 0xa1, 0x5c, 0xc0, 0x86, 0xcf, 0x61, 0x3d,
0x4f, 0x40, 0x01, 0xd7, 0x81, 0x1a, 0x09, 0xf1, 0x85, 0xaf, 0x9c, 0xd3, 0x70, 0xb5, 0xa4, 0x9f,
0xa9, 0x82, 0x28, 0x9e, 0x59, 0x9c, 0x0e, 0x86, 0xc0, 0x95, 0x39, 0x02, 0xff, 0xc1, 0x82, 0xcd,
0x33, 0x59, 0xf9, 0xc5, 0x81, 0x79, 0xe6, 0xe4, 0x3b, 0xc1, 0x0e, 0x2c, 0x31, 0xdf, 0x3b, 0xcf,
0x26, 0xa8, 0xdb, 0x62, 0x7e, 0xca, 0x39, 0xf4, 0x3e, 0x2c, 0x85, 0x64, 0x7a, 0x7e, 0x2b, 0x12,
0xb5, 0x42, 0x32, 0x35, 0x1b, 0x9d, 0x5f, 0x40, 0x57, 0x3f, 0xfb, 0x24, 0xed, 0xb7, 0x89, 0x7a,
0xfa, 0x3e, 0xc0, 0xac, 0x07, 0xcb, 0x94, 0x68, 0xed, 0x23, 0xe3, 0xb9, 0x99, 0xb5, 0x3b, 0x67,
0xe5, 0xfc, 0xc5, 0x82, 0x95, 0xd3, 0xef, 0xcc, 0x97, 0x4c, 0x8d, 0x2a, 0xbd, 0x6d, 0x8d, 0xba,
0x5d, 0x92, 0xcc, 0x73, 0xaf, 0x92, 0xe5, 0xde, 0xcd, 0x55, 0xe8, 0xbf, 0x16, 0xd4, 0x0f, 0xaf,
0x43, 0x1c, 0xd0, 0xd1, 0xc2, 0xbd, 0xbb, 0x50, 0x17, 0x57, 0x39, 0xd7, 0x61, 0x2f, 0xbb, 0x35,
0x21, 0x1e, 0xa9, 0xd0, 0xb3, 0x90, 0x93, 0x90, 0xa7, 0xa1, 0x57, 0x62, 0x26, 0xf4, 0x55, 0x15,
0xfa, 0x4c, 0x5b, 0xaf, 0xe7, 0xda, 0x7a, 0x76, 0x08, 0xa9, 0xbe, 0xeb, 0x10, 0x52, 0xbb, 0xf3,
0x10, 0xe2, 0x7c, 0x0c, 0x1d, 0x93, 0x4e, 0xda, 0x0f, 0x77, 0xac, 0x11, 0x3f, 0xaf, 0x34, 0xca,
0xab, 0x15, 0xe7, 0xd7, 0xd0, 0x5e, 0xc0, 0x29, 0xce, 0xce, 0x1f, 0x42, 0xdd, 0x53, 0x56, 0x3a,
0x41, 0xef, 0x1b, 0x9a, 0x99, 0xcd, 0x66, 0xdd, 0xf9, 0xb3, 0x05, 0x95, 0x53, 0x3a, 0x0e, 0x6f,
0x1f, 0x9e, 0x2e, 0xd4, 0xc5, 0x48, 0x23, 0x1c, 0x52, 0xd6, 0x6d, 0x9d, 0x8e, 0x43, 0x3d, 0x70,
0x45, 0x3a, 0x2d, 0x4b, 0x34, 0x7a, 0x77, 0xef, 0xb7, 0xa1, 0x8a, 0xa7, 0x38, 0xf6, 0xa4, 0xe3,
0xab, 0xae, 0x12, 0x9c, 0x03, 0xe8, 0xa6, 0xae, 0xd0, 0x93, 0xeb, 0x1d, 0x7d, 0xea, 0x7c, 0x06,
0x1b, 0x8b, 0x10, 0xc5, 0xee, 0x7c, 0x0c, 0x0d, 0x33, 0x23, 0x6b, 0x7f, 0xae, 0xa6, 0xfe, 0x34,
0xdb, 0x53, 0x8b, 0xfd, 0xbf, 0xaf, 0x40, 0x5d, 0x20, 0x1f, 0x9c, 0x1c, 0x21, 0x0c, 0x0d, 0x33,
0x5a, 0xa0, 0xbe, 0xd9, 0x53, 0x30, 0x6c, 0xd8, 0x69, 0x2d, 0x98, 0xf5, 0x62, 0x67, 0xf0, 0xfb,
0xaf, 0xff, 0xfd, 0xc7, 0x92, 0xed, 0x6c, 0x0c, 0xe7, 0x7e, 0x52, 0x0c, 0xcd, 0x38, 0xf8, 0xcc,
0x7a, 0x84, 0x3e, 0x83, 0xaa, 0xb4, 0x7f, 0x3b, 0xfc, 0x87, 0x12, 0xbf, 0xeb, 0xa0, 0x0c, 0xbe,
0x1c, 0x50, 0x05, 0xf8, 0x19, 0xd4, 0x8e, 0xd9, 0x98, 0x4d, 0x38, 0xea, 0x2c, 0x04, 0xee, 0x23,
0xf1, 0x1b, 0xc3, 0x2e, 0xd0, 0x3b, 0x5b, 0x12, 0x78, 0xe3, 0xd1, 0x7a, 0x1e, 0x58, 0x80, 0x7d,
0x04, 0x75, 0x5d, 0x24, 0x51, 0xc7, 0x5c, 0x2a, 0x3b, 0x15, 0xd8, 0x99, 0x96, 0xe2, 0xb4, 0x25,
0xda, 0x0a, 0x5a, 0x9a, 0x47, 0x43, 0xaf, 0x00, 0x2d, 0x16, 0x7d, 0xb4, 0x93, 0xee, 0x2c, 0x6a,
0x08, 0x85, 0x97, 0xd6, 0xde, 0xb6, 0xb3, 0xde, 0x36, 0xe5, 0x4f, 0x38, 0xe4, 0x53, 0x58, 0x9b,
0x83, 0x8d, 0xd9, 0xa5, 0xf8, 0x95, 0x90, 0xb9, 0x6b, 0x21, 0x78, 0x5f, 0x82, 0x6f, 0xda, 0xed,
0x2c, 0xb8, 0xc2, 0x50, 0xce, 0x86, 0xd9, 0xfc, 0x84, 0x36, 0x53, 0x8a, 0xe5, 0x67, 0xaa, 0xc2,
0x13, 0xb4, 0x97, 0x1e, 0x65, 0xbd, 0xe4, 0x42, 0xfd, 0xc0, 0xf3, 0xb2, 0xce, 0xce, 0xb6, 0x94,
0x42, 0xc0, 0xae, 0x04, 0x5c, 0x73, 0x32, 0x80, 0xe2, 0xaa, 0x2e, 0xd4, 0x4f, 0xf3, 0x01, 0xbc,
0x1b, 0xa6, 0xbd, 0x80, 0x79, 0x06, 0x0d, 0x93, 0x94, 0xa8, 0x9b, 0x52, 0x35, 0x3b, 0x59, 0xd9,
0x1b, 0x8b, 0x0b, 0x82, 0xc6, 0x9b, 0x12, 0x74, 0x1d, 0xad, 0x65, 0xd9, 0x46, 0x13, 0x8e, 0x7e,
0x9b, 0x0e, 0x9c, 0x2f, 0xcc, 0x40, 0x97, 0xa3, 0x5c, 0x76, 0x40, 0xb6, 0xb7, 0x8a, 0x96, 0xc5,
0x41, 0x0f, 0xe4, 0x41, 0x1d, 0x94, 0x0d, 0xa2, 0x19, 0x4d, 0x42, 0x40, 0x8b, 0xcd, 0xbf, 0x30,
0x75, 0xfa, 0xb9, 0x83, 0xf2, 0x03, 0x83, 0x61, 0x0c, 0xea, 0x66, 0x19, 0x93, 0x9a, 0xa1, 0x00,
0xd0, 0x0b, 0x1c, 0x1e, 0x9d, 0x45, 0xa2, 0xf0, 0x1c, 0xb2, 0xd1, 0x44, 0xfc, 0xf6, 0xba, 0x73,
0xaa, 0xfe, 0x40, 0x1e, 0xb3, 0x83, 0xfa, 0xb9, 0x37, 0x85, 0x74, 0x22, 0x81, 0x3d, 0x03, 0x1c,
0xcc, 0x26, 0x5d, 0xd3, 0xd4, 0xb7, 0xf3, 0xf1, 0xc8, 0x76, 0x39, 0xfb, 0x41, 0xe1, 0x7a, 0xb1,
0x37, 0x75, 0x6b, 0x42, 0xc7, 0xd0, 0x14, 0x9d, 0xe9, 0x25, 0xf3, 0xf0, 0x75, 0xe1, 0xa3, 0xd2,
0xdc, 0x13, 0xa6, 0x86, 0x07, 0x76, 0x96, 0x07, 0xa2, 0x3f, 0xa1, 0x5f, 0x49, 0x1e, 0x08, 0x2b,
0xe2, 0xbd, 0x05, 0x24, 0xba, 0x01, 0xf2, 0x15, 0xac, 0xe6, 0xdb, 0xc8, 0xac, 0x0a, 0x17, 0xf4,
0x28, 0xfb, 0x61, 0xb1, 0xc1, 0x5c, 0x41, 0x46, 0xd9, 0x12, 0x64, 0x9a, 0x0b, 0xfa, 0x22, 0xfb,
0xdb, 0xe8, 0x94, 0x93, 0xe8, 0x97, 0x21, 0x41, 0x29, 0x67, 0x6f, 0xf8, 0xe1, 0x54, 0x18, 0xf7,
0xc7, 0xf2, 0xa8, 0xef, 0x3b, 0x3b, 0x99, 0xa3, 0x2e, 0x69, 0xe8, 0x99, 0x8a, 0x37, 0x4c, 0x38,
0x89, 0x58, 0x28, 0xab, 0xd3, 0x0d, 0x27, 0xbf, 0x9c, 0xb2, 0x77, 0x3a, 0xd9, 0xfe, 0x3f, 0x27,
0xf3, 0x29, 0x7b, 0x66, 0x3d, 0x7a, 0xfe, 0x93, 0xbf, 0xbe, 0xde, 0xb6, 0xbe, 0x7a, 0xbd, 0x6d,
0xfd, 0xeb, 0xf5, 0xb6, 0xf5, 0xe5, 0x9b, 0xed, 0x7b, 0x5f, 0xbd, 0xd9, 0xbe, 0xf7, 0x8f, 0x37,
0xdb, 0xf7, 0x40, 0xff, 0xa9, 0x76, 0x62, 0x7d, 0xba, 0x16, 0xb0, 0x98, 0x78, 0x6c, 0xa4, 0x01,
0x3f, 0xb8, 0x7a, 0x72, 0x51, 0x93, 0x87, 0x3e, 0xfd, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x61,
0xbb, 0x15, 0x8a, 0x8d, 0x13, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@ -2761,6 +2770,15 @@ func (m *ListUserRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if len(m.Field) > 0 {
for iNdEx := len(m.Field) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Field[iNdEx])
copy(dAtA[i:], m.Field[iNdEx])
i = encodeVarintUser(dAtA, i, uint64(len(m.Field[iNdEx])))
i--
dAtA[i] = 0x4a
}
}
if m.Limit != 0 {
i = encodeVarintUser(dAtA, i, uint64(m.Limit))
i--
@ -3638,6 +3656,12 @@ func (m *ListUserRequest) Size() (n int) {
if m.Limit != 0 {
n += 1 + sovUser(uint64(m.Limit))
}
if len(m.Field) > 0 {
for _, s := range m.Field {
l = len(s)
n += 1 + l + sovUser(uint64(l))
}
}
return n
}
@ -5783,6 +5807,38 @@ func (m *ListUserRequest) Unmarshal(dAtA []byte) error {
break
}
}
case 9:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Field", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowUser
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthUser
}
postIndex := iNdEx + intStringLen
if postIndex < 0 {
return ErrInvalidLengthUser
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Field = append(m.Field, string(dAtA[iNdEx:postIndex]))
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipUser(dAtA[iNdEx:])

@ -90,6 +90,7 @@ message ListUserRequest {
repeated int64 group_id = 6; // ID
repeated int32 status = 7; //
int64 limit = 8; // 0pagesize
repeated string field = 9; //
}
//

@ -150,7 +150,7 @@ func (s *AttachmentAPIService) ListAttachment(ctx context.Context, req *pb.ListA
util.CopyStruct(&attachments, &pbAttachments)
var (
userIds []interface{}
userIds []int64
userIdIndexMap = make(map[int64][]int)
)
@ -186,7 +186,7 @@ func (s *AttachmentAPIService) UploadDocument(ctx *gin.Context) {
}
// 检查用户是否有权限上传文档
if !s.dbModel.CanIUploadDocument(userClaims.UserId) {
if !s.dbModel.CanIAccessUploadDocument(userClaims.UserId) {
ctx.JSON(http.StatusForbidden, ginResponse{Code: http.StatusForbidden, Message: "没有权限上传文档", Error: "没有权限上传文档"})
return
}

@ -47,6 +47,10 @@ func (s *CommentAPIService) CreateComment(ctx context.Context, req *pb.CreateCom
return nil, status.Errorf(codes.InvalidArgument, "验证码错误")
}
if yes, _ := s.dbModel.CanIAccessComment(userClaims.UserId); !yes {
return nil, status.Errorf(codes.PermissionDenied, "您已经被禁止发表评论")
}
comment := &model.Comment{}
err = util.CopyStruct(req, comment)
if err != nil {

@ -54,7 +54,7 @@ func (s *DocumentAPIService) CreateDocument(ctx context.Context, req *pb.CreateD
return nil, err
}
if !s.dbModel.CanIUploadDocument(userCliams.UserId) {
if !s.dbModel.CanIAccessUploadDocument(userCliams.UserId) {
return nil, status.Error(codes.PermissionDenied, "没有权限上传文档")
}
@ -673,6 +673,10 @@ func (s *DocumentAPIService) DownloadDocument(ctx context.Context, req *pb.Docum
userId = userClaims.UserId
}
if yes, _ := s.dbModel.CanIAccessDownload(userId); !yes {
return res, status.Errorf(codes.PermissionDenied, "您的账户已被禁止下载文档")
}
ip := ""
ips, _ := util.GetGRPCRemoteIP(ctx)
if len(ips) > 0 {

@ -34,6 +34,11 @@ func (s *FavoriteAPIService) CreateFavorite(ctx context.Context, req *pb.Favorit
return nil, err
}
yes, _ := s.dbModel.CanIAccessFavorite(userClaims.UserId)
if !yes {
return nil, status.Errorf(codes.PermissionDenied, "您已经被禁止收藏文档")
}
favorite := &model.Favorite{
UserId: userClaims.UserId,
DocumentId: req.DocumentId,

@ -0,0 +1,243 @@
package biz
import (
"context"
"strings"
pb "moredoc/api/v1"
"moredoc/middleware/auth"
"moredoc/model"
"moredoc/util"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
"gorm.io/gorm"
)
type PunishmentAPIService struct {
pb.UnimplementedPunishmentAPIServer
dbModel *model.DBModel
logger *zap.Logger
}
func NewPunishmentAPIService(dbModel *model.DBModel, logger *zap.Logger) (service *PunishmentAPIService) {
return &PunishmentAPIService{dbModel: dbModel, logger: logger.Named("PunishmentAPIService")}
}
func (s *PunishmentAPIService) checkPermission(ctx context.Context) (userClaims *auth.UserClaims, err error) {
return checkGRPCPermission(s.dbModel, ctx)
}
func (s *PunishmentAPIService) CreatePunishment(ctx context.Context, req *pb.CreatePunishmentRequest) (*emptypb.Empty, error) {
userClaims, err := s.checkPermission(ctx)
if err != nil {
return nil, err
}
if len(req.UserId) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "请选择用户")
}
if len(req.Type) == 0 {
return nil, status.Errorf(codes.InvalidArgument, "请选择处罚类型")
}
for _, userId := range req.UserId {
if userId == 1 {
continue
}
for _, typ := range req.Type {
punishment := &model.Punishment{
UserId: userId,
Type: int(typ),
Enable: req.Enable,
Reason: req.Reason,
Remark: req.Remark,
EndTime: req.EndTime,
}
s.logger.Debug("CreatePunishment", zap.Any("punishment", punishment), zap.Any("req", req))
punishment.Operators = s.dbModel.MakePunishmentOperators(userClaims.UserId, typ)
err = s.dbModel.CreatePunishment(punishment)
if err != nil {
s.logger.Error("CreatePunishment", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
}
}
return &emptypb.Empty{}, nil
}
func (s *PunishmentAPIService) UpdatePunishment(ctx context.Context, req *pb.Punishment) (*emptypb.Empty, error) {
userClaims, err := s.checkPermission(ctx)
if err != nil {
return nil, err
}
punishment := &model.Punishment{}
err = util.CopyStruct(req, punishment)
if err != nil {
s.logger.Error("CopyStruct", zap.Error(err))
return nil, status.Errorf(codes.InvalidArgument, err.Error())
}
s.logger.Debug("UpdatePunishment", zap.Any("punishment", punishment), zap.Any("req", req))
if existPunishment, _ := s.dbModel.GetPunishment(punishment.Id, "id", "operators"); existPunishment.Id > 0 {
punishment.Operators = s.dbModel.MakePunishmentOperators(userClaims.UserId, req.Type, existPunishment.Operators)
}
err = s.dbModel.UpdatePunishment(punishment)
if err != nil {
s.logger.Error("UpdatePunishment", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
return &emptypb.Empty{}, nil
}
func (s *PunishmentAPIService) GetPunishment(ctx context.Context, req *pb.GetPunishmentRequest) (*pb.Punishment, error) {
_, err := s.checkPermission(ctx)
if err != nil {
return nil, err
}
punishment, err := s.dbModel.GetPunishment(req.Id)
if err != nil {
s.logger.Error("GetPunishment", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
res := &pb.Punishment{}
err = util.CopyStruct(punishment, res)
if err != nil {
s.logger.Error("CopyStruct", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
if punishment.UserId > 0 {
user, _ := s.dbModel.GetUser(punishment.UserId, "id", "username")
if user.Id > 0 {
res.Username = user.Username
}
}
return res, nil
}
func (s *PunishmentAPIService) ListPunishment(ctx context.Context, req *pb.ListPunishmentRequest) (*pb.ListPunishmentReply, error) {
_, err := s.checkPermission(ctx)
if err != nil {
return nil, err
}
opt := &model.OptionGetPunishmentList{
Page: int(req.Page),
Size: int(req.Size_),
WithCount: true,
SelectFields: req.Field,
QueryLike: make(map[string][]interface{}),
QueryIn: make(map[string][]interface{}),
}
if len(req.UserId) > 0 {
opt.QueryIn["user_id"] = util.Slice2Interface(req.UserId)
}
if len(req.Type) > 0 {
opt.QueryIn["type"] = util.Slice2Interface(req.Type)
}
if len(req.Enable) > 0 {
opt.QueryIn["enable"] = util.Slice2Interface(req.Enable)
}
if req.Order != "" {
opt.Sort = strings.Split(req.Order, ",")
}
if req.Wd != "" {
wd := strings.TrimSpace(req.Wd)
opt.QueryLike["reason"] = []interface{}{wd}
opt.QueryLike["remark"] = []interface{}{wd}
}
data, total, err := s.dbModel.GetPunishmentList(opt)
if err != nil && err != gorm.ErrRecordNotFound {
s.logger.Error("GetPunishmentList", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
res := &pb.ListPunishmentReply{
Total: total,
}
err = util.CopyStruct(data, &res.Punishment)
if err != nil {
s.logger.Error("CopyStruct", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
if len(res.Punishment) > 0 {
var (
userIds []int64
userIdMapIndexes = make(map[int64][]int)
)
for i, v := range res.Punishment {
userIds = append(userIds, v.UserId)
userIdMapIndexes[v.UserId] = append(userIdMapIndexes[v.UserId], i)
}
users, _, _ := s.dbModel.GetUserList(&model.OptionGetUserList{
Ids: userIds,
WithCount: false,
SelectFields: []string{
"id",
"username",
},
})
for _, v := range users {
if indexes, ok := userIdMapIndexes[v.Id]; ok {
for _, index := range indexes {
res.Punishment[index].Username = v.Username
}
}
}
}
return res, nil
}
// 取消惩罚
func (s *PunishmentAPIService) CancelPunishment(ctx context.Context, req *pb.CancelPunishmentRequest) (*emptypb.Empty, error) {
userCliams, err := s.checkPermission(ctx)
if err != nil {
return nil, err
}
s.logger.Debug("CancelPunishment", zap.Any("req", req))
data, _, err := s.dbModel.GetPunishmentList(&model.OptionGetPunishmentList{
Ids: req.Id,
})
if err != nil && err != gorm.ErrRecordNotFound {
s.logger.Error("GetPunishmentList", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
for _, item := range data {
item.Enable = false
item.Operators = s.dbModel.MakePunishmentOperators(userCliams.UserId, 0, item.Operators)
err = s.dbModel.UpdatePunishment(&item, "enable", "operators")
if err != nil {
s.logger.Error("UpdatePunishment", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
}
return &emptypb.Empty{}, nil
}

@ -250,17 +250,18 @@ func (s *UserAPIService) UpdateUserProfile(ctx context.Context, req *pb.User) (*
Mobile: req.Mobile, Email: req.Email, Address: req.Address,
Signature: req.Signature, Avatar: req.Avatar,
Realname: req.Realname, Identity: req.Identity,
Status: int8(req.Status), Id: req.Id,
// Status: int8(req.Status),
Id: req.Id,
}
// 更改用户自己的资料
if req.Id <= 0 || req.Id == userClaims.UserId {
user.Id = userClaims.UserId
exist, _ := s.dbModel.GetUser(user.Id, "status")
if exist.Status != model.UserStatusNormal {
// 非正常的用户状态,禁止修改个人信息,以避免用户修改成非法信息等
return nil, status.Errorf(codes.InvalidArgument, "您的用户状态异常,禁止修改个人信息")
}
// exist, _ := s.dbModel.GetUser(user.Id, "status")
// if exist.Status != model.UserStatusNormal {
// // 非正常的用户状态,禁止修改个人信息,以避免用户修改成非法信息等
// return nil, status.Errorf(codes.InvalidArgument, "您的用户状态异常,禁止修改个人信息")
// }
if err := s.dbModel.UpdateUser(user, fields...); err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
@ -372,9 +373,10 @@ func (s *UserAPIService) ListUser(ctx context.Context, req *pb.ListUserRequest)
}
opt := &model.OptionGetUserList{
Page: int(req.Page),
Size: int(req.Size_),
WithCount: true,
Page: int(req.Page),
Size: int(req.Size_),
WithCount: true,
SelectFields: req.Field,
}
if req.Limit > 0 {
opt.Page = 1
@ -383,12 +385,8 @@ func (s *UserAPIService) ListUser(ctx context.Context, req *pb.ListUserRequest)
}
if len(req.Id) > 0 {
var ids []interface{}
for _, id := range req.Id {
ids = append(ids, id)
}
opt.WithCount = false
opt.Ids = ids
opt.Ids = req.Id
}
if len(req.GroupId) > 0 {
@ -543,7 +541,7 @@ func (s *UserAPIService) CanIUploadDocument(ctx context.Context, req *emptypb.Em
return nil, err
}
if !s.dbModel.CanIUploadDocument(userClaims.UserId) {
if !s.dbModel.CanIAccessUploadDocument(userClaims.UserId) {
return nil, status.Errorf(codes.PermissionDenied, "您没有上传文档的权限")
}

@ -137,7 +137,7 @@ func (m *DBModel) GetCategoryList(opt *OptionGetCategoryList) (categoryList []Ca
db = db.Offset((opt.Page - 1) * opt.Size).Limit(opt.Size)
err = db.Order("parent_id asc, sort desc, id asc").Find(&categoryList).Error
err = db.Order("parent_id asc, sort desc, title asc").Find(&categoryList).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetCategoryList", zap.Error(err))
}

@ -68,6 +68,7 @@ func getPermissions() (permissions []Permission) {
{Title: "更新头像", Description: "", Method: "POST", Path: "/api/v1/upload/avatar"},
{Title: "一键重转失败文档", Description: "", Method: "GRPC", Path: "/api.v1.DocumentAPI/SetDocumentReconvert"},
{Title: "批量修改文档分类", Description: "", Method: "GRPC", Path: "/api.v1.DocumentAPI/SetDocumentsCategory"},
{Title: "惩罚管理", Description: "", Method: "GRPC", Path: "/api.v1.PunishmentAPI/ListPunishment"},
}
return
}

@ -162,6 +162,7 @@ func (m *DBModel) SyncDB() (err error) {
&Sign{},
&Report{},
&Navigation{},
&Punishment{},
}
if err = m.db.AutoMigrate(tableModels...); err != nil {
m.logger.Fatal("SyncDB", zap.Error(err))

@ -0,0 +1,188 @@
package model
import (
"time"
jsoniter "github.com/json-iterator/go"
"go.uber.org/zap"
"gorm.io/gorm"
)
const (
PunishmentTypeDisabled = 1 // 禁用账户:禁止登录、禁止评论、禁止上传、禁止下载、禁止收藏
PunishmentTypeCommentLimited = 2 // 禁止评论
PunishmentTypeUploadLimited = 3 // 禁止上传
PunishmentTypeDownloadLimited = 4 // 禁止下载
PunishmentTypeFavoriteLimited = 5 // 禁止收藏
)
type Punishment struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:自增主键;"`
UserId int64 `form:"user_id" json:"user_id,omitempty" gorm:"column:user_id;type:bigint(20);size:20;default:0;index:idx_user_id;comment:用户ID;"`
Type int `form:"type" json:"type,omitempty" gorm:"column:type;type:int(11);size:11;default:0;comment:惩罚类型对应user表的status;"`
Enable bool `form:"enable" json:"enable,omitempty" gorm:"column:enable;type:tinyint(1);size:1;default:0;index:idx_enable;comment:0 关闭1启用;"`
Operators string `form:"operators" json:"operators,omitempty" gorm:"column:operators;type:text;comment:操作信息;"`
Reason string `form:"reason" json:"reason,omitempty" gorm:"column:reason;type:text;comment:惩罚原因;"`
Remark string `form:"remark" json:"remark,omitempty" gorm:"column:remark;type:text;comment:惩罚备注;"`
EndTime *time.Time `form:"end_time" json:"end_time,omitempty" gorm:"column:end_time;type:datetime;comment:惩罚结束时间,没有结束时间,则表示永久;"`
CreatedAt *time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;comment:创建时间;"`
UpdatedAt *time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;comment:更新时间;"`
}
type PunishmentOperator struct {
UserId int64 `json:"u"`
Type int32 `json:"t"`
Timestamp int64 `json:"ts"`
}
func (Punishment) TableName() string {
return tablePrefix + "punishment"
}
func (m *DBModel) MakePunishmentOperators(userId int64, punishmentType int32, operaterStr ...string) string {
var operators []PunishmentOperator
if len(operaterStr) > 0 && operaterStr[0] != "" {
err := jsoniter.Unmarshal([]byte(operaterStr[0]), &operators)
if err != nil {
m.logger.Error("FormatPunishmentOperators", zap.Error(err))
return operaterStr[0]
}
}
operators = append(operators, PunishmentOperator{
UserId: userId,
Type: punishmentType,
Timestamp: time.Now().Unix(),
})
operatersByte, err := jsoniter.Marshal(operators)
if err != nil {
m.logger.Error("FormatPunishmentOperators", zap.Error(err))
if len(operaterStr) > 0 {
return operaterStr[0]
}
return ""
}
return string(operatersByte)
}
// CreatePunishment 创建Punishment
func (m *DBModel) CreatePunishment(punishment *Punishment) (err error) {
err = m.db.Create(punishment).Error
if err != nil {
m.logger.Error("CreatePunishment", zap.Error(err))
return
}
return
}
// UpdatePunishment 更新Punishment如果需要更新指定字段则请指定updateFields参数
func (m *DBModel) UpdatePunishment(punishment *Punishment, updateFields ...string) (err error) {
db := m.db.Model(punishment)
tableName := Punishment{}.TableName()
updateFields = m.FilterValidFields(tableName, updateFields...)
if len(updateFields) > 0 { // 更新指定字段
db = db.Select(updateFields)
} else { // 更新全部字段,包括零值字段
db = db.Select(m.GetTableFields(tableName))
}
err = db.Where("id = ?", punishment.Id).Updates(punishment).Error
if err != nil {
m.logger.Error("UpdatePunishment", zap.Error(err))
}
return
}
// GetPunishment 根据id获取Punishment
func (m *DBModel) GetPunishment(id interface{}, fields ...string) (punishment Punishment, err error) {
db := m.db
fields = m.FilterValidFields(Punishment{}.TableName(), fields...)
if len(fields) > 0 {
db = db.Select(fields)
}
err = db.Where("id = ?", id).First(&punishment).Error
return
}
type OptionGetPunishmentList struct {
Page int
Size int
WithCount bool // 是否返回总数
Ids []int64 // id列表
SelectFields []string // 查询字段
QueryRange map[string][2]interface{} // map[field][]{min,max}
QueryIn map[string][]interface{} // map[field][]{value1,value2,...}
QueryLike map[string][]interface{} // map[field][]{value1,value2,...}
Sort []string
}
// GetPunishmentList 获取Punishment列表
func (m *DBModel) GetPunishmentList(opt *OptionGetPunishmentList) (punishmentList []Punishment, total int64, err error) {
tableName := Punishment{}.TableName()
db := m.db.Model(&Punishment{})
db = m.generateQueryRange(db, tableName, opt.QueryRange)
db = m.generateQueryIn(db, tableName, opt.QueryIn)
db = m.generateQueryLike(db, tableName, opt.QueryLike)
if len(opt.Ids) > 0 {
db = db.Where("id in (?)", opt.Ids)
}
if opt.WithCount {
err = db.Count(&total).Error
if err != nil {
m.logger.Error("GetPunishmentList", zap.Error(err))
return
}
}
opt.SelectFields = m.FilterValidFields(tableName, opt.SelectFields...)
if len(opt.SelectFields) > 0 {
db = db.Select(opt.SelectFields)
}
db = m.generateQuerySort(db, tableName, opt.Sort)
db = db.Offset((opt.Page - 1) * opt.Size).Limit(opt.Size)
err = db.Find(&punishmentList).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetPunishmentList", zap.Error(err))
}
return
}
// DeletePunishment 删除数据
func (m *DBModel) DeletePunishment(ids []int64) (err error) {
err = m.db.Where("id in (?)", ids).Delete(&Punishment{}).Error
if err != nil {
m.logger.Error("DeletePunishment", zap.Error(err))
}
return
}
func (m *DBModel) isInPunishing(userId int64, types []int) (yes bool, err error) {
if userId <= 1 {
return false, nil
}
punishment := &Punishment{}
err = m.db.Model(punishment).Select("id").
Where(
"user_id = ? and enable = ? and type in ? and (end_time IS NULL or end_time > ?)",
userId, true, types, time.Now(),
).Find(&punishment).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return false, nil
}
m.logger.Error("isInPunishing", zap.Error(err))
return
}
return punishment.Id > 0, nil
}

@ -12,11 +12,12 @@ import (
)
const (
UserStatusNormal = iota
UserStatusDisabled // 禁用
UserStatusPending // 审核中
UserStatusRejected // 拒绝
UserStatusIgnored // 忽略
UserStatusNormal = 0
UserStatusDisabled = 1 // 禁用账户:禁止登录、禁止评论、禁止上传、禁止下载、禁止收藏
UserStatusCommentLimited = 2 // 禁止评论
UserStatusUploadLimited = 3 // 禁止上传
UserStatusDownloadLimited = 4 // 禁止下载
UserStatusFavoriteLimited = 5 // 禁止收藏
)
// 用户的公开信息字段
@ -42,13 +43,13 @@ type User struct {
FavoriteCount int `form:"favorite_count" json:"favorite_count,omitempty" gorm:"column:favorite_count;type:int(10);default:0;comment:收藏数;"`
CommentCount int `form:"comment_count" json:"comment_count,omitempty" gorm:"column:comment_count;type:int(11);size:11;default:0;comment:评论数;"`
CreditCount int `form:"credit_count" json:"credit_count,omitempty" gorm:"column:credit_count;type:int(11);size:11;default:0;comment:积分数量;"`
Status int8 `form:"status" json:"status,omitempty" gorm:"column:status;type:tinyint(4);size:4;default:0;index:status;comment:用户状态0正常 1禁用 2审核中 3审核拒绝 4审核忽略;"`
Avatar string `form:"avatar" json:"avatar,omitempty" gorm:"column:avatar;type:varchar(255);size:255;comment:头像;"`
Identity string `form:"identity" json:"identity,omitempty" gorm:"column:identity;type:char(18);size:18;comment:身份证号码;"`
Realname string `form:"realname" json:"realname,omitempty" gorm:"column:realname;type:varchar(20);size:20;comment:身份证姓名;"`
LoginAt *time.Time `form:"login_at" json:"login_at,omitempty" gorm:"column:login_at;type:datetime;comment:最后登录时间;"`
CreatedAt *time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;comment:创建时间;"`
UpdatedAt *time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;comment:更新时间;"`
// Status int8 `form:"status" json:"status,omitempty" gorm:"column:status;type:tinyint(4);size:4;default:0;index:status;comment:用户状态0表示正常其他状态表示被惩罚;"`
}
func (User) TableName() string {
@ -191,7 +192,7 @@ type OptionGetUserList struct {
Page int
Size int
WithCount bool // 是否返回总数
Ids []interface{} // id列表
Ids []int64 // id列表
SelectFields []string // 查询字段
QueryRange map[string][2]interface{} // map[field][]{min,max}
QueryIn map[string][]interface{} // map[field][]{value1,value2,...}
@ -410,7 +411,13 @@ func (m *DBModel) SetUserGroupAndPassword(userId int64, groupId []int64, passwor
}
// CanIUploadDocument 判断用户是否有上传文档的权限
func (m *DBModel) CanIUploadDocument(userId int64) (yes bool) {
// 1. 用户是否被禁用或被处罚禁止上传文档
// 2. 用户所在的用户组是否允许上传文档
func (m *DBModel) CanIAccessUploadDocument(userId int64) (yes bool) {
if inPunishing, _ := m.isInPunishing(userId, []int{PunishmentTypeDisabled, PunishmentTypeUploadLimited}); inPunishing {
return false
}
var (
tableGroup = Group{}.TableName()
tableUserGroup = UserGroup{}.TableName()
@ -426,6 +433,39 @@ func (m *DBModel) CanIUploadDocument(userId int64) (yes bool) {
return group.Id > 0
}
// 用户是否可以下载文档:被禁用的账号或被禁止下载的账户不能下载
func (m *DBModel) CanIAccessDownload(userId int64) (yes bool, err error) {
yes, err = m.isInPunishing(userId, []int{PunishmentTypeDownloadLimited, PunishmentTypeDisabled})
yes = !yes
if err != nil {
m.logger.Error("CanIAccessDownload", zap.Error(err))
return
}
return
}
// 用户是否可以评论
func (m *DBModel) CanIAccessComment(userId int64) (yes bool, err error) {
yes, err = m.isInPunishing(userId, []int{PunishmentTypeCommentLimited, PunishmentTypeDisabled})
yes = !yes
if err != nil {
m.logger.Error("CanIAccessComment", zap.Error(err))
return
}
return
}
// 用户是否可以收藏文档
func (m *DBModel) CanIAccessFavorite(userId int64) (yes bool, err error) {
yes, err = m.isInPunishing(userId, []int{PunishmentTypeFavoriteLimited, PunishmentTypeDisabled})
yes = !yes
if err != nil {
m.logger.Error("CanIAccessFavorite", zap.Error(err))
return
}
return
}
// 用户是否发表评论
func (m *DBModel) CanIPublishComment(userId int64) (defaultCommentStatus int8, err error) {
if userId <= 0 {

@ -148,5 +148,14 @@ func RegisterGRPCService(dbModel *model.DBModel, logger *zap.Logger, endpoint st
return
}
// 惩罚服务
punishmentAPIService := biz.NewPunishmentAPIService(dbModel, logger)
v1.RegisterPunishmentAPIServer(grpcServer, punishmentAPIService)
err = v1.RegisterPunishmentAPIHandlerFromEndpoint(context.Background(), gwmux, endpoint, dialOpts)
if err != nil {
logger.Error("RegisterPunishmentAPIHandlerFromEndpoint", zap.Error(err))
return
}
return
}

@ -0,0 +1,42 @@
import service from '~/utils/request'
export const createPunishment = (data) => {
return service({
url: '/api/v1/punishment',
method: 'post',
data,
})
}
export const updatePunishment = (data) => {
return service({
url: '/api/v1/punishment',
method: 'put',
data,
})
}
export const getPunishment = (params) => {
return service({
url: '/api/v1/punishment',
method: 'get',
params,
})
}
export const listPunishment = (params) => {
return service({
url: '/api/v1/punishment/list',
method: 'get',
params,
})
}
// 取消惩罚
export const cancelPunishment = (data) => {
return service({
url: '/api/v1/punishment/cancel',
method: 'put',
data,
})
}

@ -0,0 +1,243 @@
<template>
<div class="com-form-punishment">
<el-form
ref="formPunishment"
label-position="top"
label-width="80px"
:model="punishment"
>
<el-form-item
label="用户"
prop="user_id"
:rules="
punishment.id === 0
? [{ required: true, trigger: 'blur', message: '' }]
: []
"
>
<el-select
v-if="punishment.id === 0"
v-model="punishment.user_id"
filterable
multiple
remote
reserve-keyword
placeholder="请输入和选择用户"
:remote-method="remoteSearchUser"
:loading="loading"
>
<el-option
v-for="user in users"
:key="'userid' + user.id"
:label="user.username"
:value="user.id"
>
</el-option>
</el-select>
<el-input v-else :disabled="true" v-model="punishment.username" />
</el-form-item>
<el-form-item
prop="type"
:rules="[
{ required: true, trigger: 'blur', message: '' },
]"
>
<template slot="label">
<ToolTip
content="禁止评论:不允许发表评论;禁止上传:不允许上传文档;禁止收藏:不允许收藏;禁止下载:不允许下载文档;禁用账户:包括上述全部禁用项"
/>
</template>
<el-checkbox-group v-if="punishment.id === 0" v-model="punishment.type">
<el-checkbox
v-for="item in punishmentTypeOptions"
:label="item.value"
:key="'checkbox-pt' + item.value"
>{{ item.label }}</el-checkbox
>
</el-checkbox-group>
<el-select v-else v-model="punishment.type" :disabled="true">
<el-option
v-for="item in punishmentTypeOptions"
:label="item.label"
:key="'select-pt-' + item.value"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-row :gutter="20">
<el-col :span="8">
<el-form-item label="是否启用处罚">
<el-switch
v-model="punishment.enable"
style="display: block"
active-color="#13ce66"
inactive-color="#ff4949"
active-text="是"
inactive-text="否"
>
</el-switch> </el-form-item
></el-col>
<el-col :span="16">
<el-form-item>
<template slot="label">
<ToolTip content="用户被处罚的截止时间,留空则为永久" />
</template>
<el-date-picker
v-model="punishment.end_time"
type="datetime"
placeholder="请选择处罚截止时间"
:picker-options="datetimePickerPunishmentOptions"
>
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="处罚原因">
<el-input
type="textarea"
v-model="punishment.reason"
:rows="3"
placeholder="请输入处罚原因,被处罚用户可见"
></el-input>
</el-form-item>
<el-form-item label="处罚备注">
<el-input
type="textarea"
v-model="punishment.remark"
:rows="3"
placeholder="请输入处罚备注,管理员可见"
></el-input>
</el-form-item>
<el-form-item>
<el-button
type="primary"
class="btn-block"
icon="el-icon-check"
:loading="loading"
@click="onSubmit"
></el-button
>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { createPunishment, updatePunishment } from '~/api/punishment'
import {
punishmentTypeOptions,
datetimePickerPunishmentOptions,
} from '~/utils/enum'
import { listUser } from '~/api/user'
export default {
name: 'FormPunishment',
props: {
initPunishment: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
punishmentTypeOptions,
datetimePickerPunishmentOptions,
loading: false,
punishment: {
id: 0,
user_id: '',
remark: '',
reason: '',
type: [],
enable: true,
},
users: [],
}
},
watch: {
initPunishment: {
handler(val) {
let enable = val.enable || false
this.punishment = {
id: 0,
user_id: '',
remark: '',
reason: '',
type: [],
...val,
enable: enable,
}
},
immediate: true,
},
},
// created() {
// this.punishment = { ...this.initPunishment }
// },
methods: {
onSubmit() {
this.$refs.formPunishment.validate(async (valid) => {
if (!valid) {
return
}
this.loading = true
const punishment = { ...this.punishment }
if (this.punishment.id > 0) {
delete punishment.operators
const res = await updatePunishment(punishment)
if (res.status === 200) {
this.$message.success('')
this.$emit('success', res.data)
} else {
this.$message.error(res.data.message)
}
} else {
const res = await createPunishment(punishment)
if (res.status === 200) {
this.$message.success('')
this.$emit('success', res.data)
} else {
this.$message.error(res.data.message)
}
}
this.loading = false
})
},
async remoteSearchUser(wd) {
this.searchUser(wd)
},
async searchUser(wd, userId = []) {
const res = await listUser({
page: 1,
size: 10,
wd: wd,
id: userId || [],
field: ['id', 'username'],
})
if (res.status === 200) {
this.users = res.data.user || []
}
},
clearValidate() {
this.$refs.formPunishment.clearValidate()
},
resetFields() {
this.punishment = {
id: 0,
title: '',
link: '',
sort: 0,
enable: true,
description: '',
}
},
reset() {
this.resetFields()
this.clearValidate()
},
},
}
</script>

@ -46,6 +46,7 @@
:placeholder="field.placeholder"
></el-cascader>
</el-form-item>
<slot name="inputs"></slot>
<el-form-item>
<el-button
type="primary"

@ -0,0 +1,16 @@
<template>
<el-tooltip class="item" effect="dark" :content="content" placement="top">
<el-button size="small" type="text" icon="el-icon-info"></el-button>
</el-tooltip>
</template>
<script>
export default {
name: 'ToolTip',
props: {
content: {
type: String,
default: '',
},
},
}
</script>

@ -160,6 +160,11 @@ export default {
title: '',
icon: 'el-icon-circle-check',
},
{
page: '/admin/user/punishment',
title: '',
icon: 'el-icon-warning-outline',
},
],
},
{

@ -6,19 +6,11 @@ export default function ({ store, route, redirect, from }) {
(route.name === 'login' || route.name === 'register') &&
!(from.name === 'login' || from.name === 'register')
) {
console.log(
'checkFront.js: route.name === login or register 1',
route.query
)
if (!route.query.redirect) {
route.query.redirect = from.fullPath
redirect(route)
return
}
console.log(
'checkFront.js: route.name === login or register 2',
route.query
)
}
store.dispatch('user/checkAndRefreshUser')

21404
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -20,7 +20,7 @@
"@wangeditor/plugin-md": "^1.0.0",
"core-js": "^3.19.3",
"echarts": "^5.4.2",
"element-ui": "^2.15.10",
"element-ui": "^2.15.13",
"hotkeys-js": "^3.10.0",
"nuxt": "^2.15.8",
"qrcodejs2": "0.0.2",

@ -175,7 +175,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listArticle()
} else {

@ -148,7 +148,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listAttachment()
} else {

@ -147,7 +147,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listBanner()
} else {

@ -179,7 +179,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listComment()
} else {

@ -134,7 +134,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listCategory()
} else {

@ -302,7 +302,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listDocument()
} else {

@ -218,7 +218,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listDocument()
} else {

@ -154,7 +154,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.$router.push({
query: this.search,

@ -164,7 +164,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listNavigation()
} else {

@ -159,7 +159,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listReport()
} else {

@ -179,7 +179,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listGroup()
} else {

@ -113,7 +113,7 @@ export default {
groups: [],
users: [],
user: { id: 0 },
total: 100,
total: 0,
searchFormFields: [],
listFields: [],
selectedRows: [],
@ -138,7 +138,9 @@ export default {
size: parseInt(this.$route.query.size) || 10,
...parseQueryIntArray(this.$route.query, ['group_id', 'status']),
}
if (this.groups.length === 0) {
await this.listGroup()
}
// 这里要执行下初始化,避免数据请求回来了,但是表格字段还没初始化,导致列表布局错乱
await this.initTableListFields()
this.listUser()
@ -148,7 +150,6 @@ export default {
async created() {
await this.initSearchForm()
await this.initTableListFields()
await this.listGroup()
await this.initSearchForm() // 请求完成用户组数据之后再初始化下搜索表单,因为下拉枚举需要用到用户组数据
},
methods: {
@ -159,6 +160,11 @@ export default {
let users = res.data.user || []
users.map((item) => {
item.username_html = genLinkHTML(item.username, `/user/${item.id}`)
let groups = (item.group_id || []).map((id) => {
let group = this.groups.find((group) => group.id === id)
return group ? group.title : ''
})
item.group = groups.join(', ')
})
this.users = users
this.total = res.data.total
@ -194,7 +200,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listUser()
} else {
@ -302,7 +308,6 @@ export default {
}
}),
},
// 暂时屏蔽
// {
// type: 'select',
// label: '状态',
@ -331,6 +336,11 @@ export default {
fixed: 'left',
type: 'html',
},
{
prop: 'group',
label: '',
width: 150,
},
{ prop: 'doc_count', label: '', width: 80, type: 'number' },
{ prop: 'credit_count', label: '', width: 100, type: 'number' },
{ prop: 'follow_count', label: '', width: 80, type: 'number' },

@ -137,7 +137,7 @@ export default {
this.$router.resolve({
query: this.search,
}).href
) > -1
) === 0
) {
this.listPermission()
} else {

@ -0,0 +1,331 @@
<template>
<div>
<el-card shadow="never" class="search-card">
<FormSearch
:fields="searchFormFields"
:loading="loading"
:show-create="true"
:show-delete="false"
:disabled-delete="selectedRow.length === 0"
:default-search="search"
@onSearch="onSearch"
@onCreate="onCreate"
>
<template slot="inputs">
<el-form-item label="用户">
<el-select
v-model="search.user_id"
filterable
multiple
remote
reserve-keyword
placeholder="请输入用户名"
:remote-method="remoteSearchUser"
:loading="loading"
>
<el-option
v-for="user in users"
:key="'userid' + user.id"
:label="user.username"
:value="user.id"
>
</el-option>
</el-select>
</el-form-item>
</template>
<template slot="buttons">
<el-form-item>
<el-tooltip
class="item"
effect="dark"
content="批量取消处罚"
placement="top"
>
<el-button
type="warning"
@click="batchCancelPunishment"
:disabled="selectedRow.length === 0"
icon="el-icon-edit"
></el-button
>
</el-tooltip>
</el-form-item>
</template>
</FormSearch>
</el-card>
<el-card shadow="never" class="mgt-20px">
<TableList
:loading="loading"
:table-data="punishments"
:fields="tableListFields"
:show-actions="true"
:show-view="false"
:show-edit="true"
:show-delete="false"
:show-select="true"
:actions-min-width="80"
@selectRow="selectRow"
@editRow="editRow"
/>
</el-card>
<el-card shadow="never" class="mgt-20px">
<div class="text-right">
<el-pagination
background
:current-page="search.page"
:page-sizes="[10, 20, 50, 100]"
:page-size="search.size"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
@size-change="handleSizeChange"
@current-change="handlePageChange"
>
</el-pagination>
</div>
</el-card>
<el-dialog
:close-on-click-modal="false"
:title="punishment.id ? '编辑惩罚' : '新增惩罚'"
:visible.sync="formPunishmentVisible"
width="640px"
>
<FormPunishment
ref="punishmentForm"
:init-punishment="punishment"
@success="formPunishmentSuccess"
/>
</el-dialog>
</div>
</template>
<script>
import {
listPunishment,
getPunishment,
cancelPunishment,
} from '~/api/punishment'
import { genLinkHTML, parseQueryIntArray } from '~/utils/utils'
import TableList from '~/components/TableList.vue'
import FormSearch from '~/components/FormSearch.vue'
import FormPunishment from '~/components/FormPunishment.vue'
import { punishmentTypeOptions } from '~/utils/enum'
import { listUser } from '~/api/user'
import { mapGetters } from 'vuex'
export default {
components: { TableList, FormSearch, FormPunishment },
layout: 'admin',
data() {
return {
punishmentTypeOptions,
loading: false,
formPunishmentVisible: false,
search: {
wd: '',
page: 1,
enable: [],
size: 10,
user_id: [],
},
punishments: [],
total: 0,
searchFormFields: [],
tableListFields: [],
selectedRow: [],
punishment: { id: 0 },
users: [],
}
},
head() {
return {
title: `处罚管理 - ${this.settings.system.sitename}`,
}
},
computed: {
...mapGetters('setting', ['settings']),
},
watch: {
'$route.query': {
immediate: true,
handler() {
this.search = {
...this.search,
...this.$route.query,
page: parseInt(this.$route.query.page) || 1,
size: parseInt(this.$route.query.size) || 10,
...parseQueryIntArray(this.$route.query, ['enable']),
...parseQueryIntArray(this.$route.query, ['user_id']),
}
this.listPunishment()
},
},
},
async created() {
this.initSearchForm()
this.initTableListFields()
// await this.listPunishment()
if ((this.search.user_id || []).length > 0) {
this.searchUser('', this.search.user_id)
}
},
methods: {
async remoteSearchUser(wd) {
this.searchUser(wd)
},
async searchUser(wd, userId = []) {
const res = await listUser({
page: 1,
size: 10,
wd: wd,
id: userId || [],
field: ['id', 'username'],
})
if (res.status === 200) {
this.users = res.data.user || []
}
},
async batchCancelPunishment() {
let res = await this.$confirm(
`您确定要取消选中的${this.selectedRow.length}条处罚吗?`,
'',
{
confirmButtonText: '',
cancelButtonText: '',
type: 'warning',
}
)
if (res) {
const ids = this.selectedRow.map((item) => item.id)
const res = await cancelPunishment({ id: ids })
if (res.status === 200) {
this.$message.success('')
this.listPunishment()
} else {
this.$message.error(res.data.message)
}
}
},
async listPunishment() {
this.loading = true
const res = await listPunishment(this.search)
if (res.status === 200) {
let punishments = res.data.punishment || []
punishments.map((item) => {
item.user_html = genLinkHTML(item.username, `/user/${item.id}`)
})
this.punishments = punishments
this.total = res.data.total
} else {
this.$message.error(res.data.message)
}
this.loading = false
},
handleSizeChange(val) {
this.search.size = val
this.$router.push({
query: this.search,
})
},
handlePageChange(val) {
this.search.page = val
this.$router.push({
query: this.search,
})
},
onSearch(search) {
this.search = {
...this.search,
...search,
user_id: this.search.user_id,
page: 1,
}
if (
location.href.lastIndexOf(
this.$router.resolve({
query: this.search,
}).href
) === 0
) {
this.listPunishment()
} else {
this.$router.push({
query: this.search,
})
}
},
onCreate() {
this.punishment = { id: 0, type: [], enable: true }
this.formPunishmentVisible = true
},
async editRow(row) {
const res = await getPunishment({ id: row.id })
if (res.status === 200) {
this.punishment = res.data
this.formPunishmentVisible = true
} else {
this.$message.error(res.data.message)
}
},
formPunishmentSuccess() {
this.formPunishmentVisible = false
this.listPunishment()
},
selectRow(rows) {
this.selectedRow = rows
},
initSearchForm() {
this.searchFormFields = [
{
type: 'select',
label: '',
name: 'type',
placeholder: '',
multiple: true,
options: this.punishmentTypeOptions,
},
{
type: 'text',
label: '',
name: 'wd',
placeholder: '',
},
]
},
initTableListFields() {
const enumOptions = {}
this.punishmentTypeOptions.map((item) => {
enumOptions[item.value] = item
})
this.tableListFields = [
{ prop: 'id', label: 'ID', width: 80, type: 'number', fixed: 'left' },
{
prop: 'enable',
label: '',
width: 80,
type: 'bool',
},
{
prop: 'type',
label: '',
minWidth: 120,
type: 'enum',
enum: enumOptions,
},
{
prop: 'user_html',
label: '',
minWidth: 150,
type: 'html',
},
{ prop: 'end_time', label: '', width: 160, type: 'datetime' },
{ prop: 'reason', label: '', minWidth: 250 },
{ prop: 'remark', label: '', minWidth: 250 },
{ prop: 'created_at', label: '', width: 160, type: 'datetime' },
{ prop: 'updated_at', label: '', width: 160, type: 'datetime' },
]
},
},
}
</script>
<style></style>

@ -1020,6 +1020,9 @@ export default {
width: 100%;
background-color: #fff;
}
.el-image__error {
min-height: 360px;
}
}
}
.doc-page-more {

@ -521,7 +521,13 @@ export default {
padding-bottom: 0;
}
.el-card__body {
padding: 15px 0;
padding: 15px 0 20px;
max-height: 77px;
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
text-overflow: ellipsis;
}
a {
display: inline-block;

@ -166,7 +166,7 @@
class="btn-block"
disabled
>
<span v-if="user.id > 0"></span>
<span v-if="user.id > 0"></span>
<span v-else></span>
</el-button>
</el-form-item>

@ -124,3 +124,80 @@ export const datetimePickerOptions = {
},
],
}
export const datetimePickerPunishmentOptions = {
shortcuts: [
{
text: '1',
onClick(picker) {
const end = new Date()
end.setTime(end.getTime() + 3600 * 1000)
picker.$emit('pick', end)
},
},
{
text: '12',
onClick(picker) {
const end = new Date()
end.setTime(end.getTime() + 3600 * 1000 * 12)
picker.$emit('pick', end)
},
},
{
text: '1',
onClick(picker) {
const end = new Date()
end.setTime(end.getTime() + 3600 * 1000 * 24)
picker.$emit('pick', end)
},
},
{
text: '2',
onClick(picker) {
const end = new Date()
end.setTime(end.getTime() + 3600 * 1000 * 24 * 2)
picker.$emit('pick', end)
},
},
{
text: '1',
onClick(picker) {
const end = new Date()
end.setTime(end.getTime() + 3600 * 1000 * 24 * 7)
picker.$emit('pick', end)
},
},
{
text: '1',
onClick(picker) {
const end = new Date()
end.setTime(end.getTime() + 3600 * 1000 * 24 * 30)
picker.$emit('pick', end)
},
},
{
text: '',
onClick(picker) {
const end = new Date()
end.setTime(end.getTime() + 3600 * 1000 * 24 * 183)
picker.$emit('pick', [start, end])
},
},
{
text: '1',
onClick(picker) {
const end = new Date()
end.setTime(end.getTime() + 3600 * 1000 * 24 * 365)
picker.$emit('pick', [start, end])
},
},
],
}
export const punishmentTypeOptions = [
{ label: '', value: 1, type: 'danger' },
{ label: '', value: 2, type: 'warning' },
{ label: '', value: 3, type: 'warning' },
{ label: '', value: 4, type: 'warning' },
{ label: '', value: 5, type: 'warning' },
]

@ -79,6 +79,12 @@ const cumstomPermissionMap = {
children: [],
pages: ['/admin/comment'],
},
'api.v1.PunishmentAPI': {
label: '',
path: 'ListPunishment',
children: [],
pages: ['/admin/user/punishment'],
},
upload: {
id: 0,
label: '',

Loading…
Cancel
Save