From 92162d4fa6feed155dd9102e1a89780a1e6ac9f1 Mon Sep 17 00:00:00 2001 From: truthhun Date: Thu, 20 Oct 2022 11:46:37 +0800 Subject: [PATCH] =?UTF-8?q?=E7=94=A8=E6=88=B7=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/user.proto | 43 +- biz/user.go | 87 +- docs/api.md | 898 +++++++++++++++++++-- docs/openapi.yaml | 1148 ++++++++++++++++++++++++++- model/user.go | 109 ++- model/userGroup.go | 66 +- web/api/user.js | 20 +- web/components/FormConfig.vue | 1 - web/components/FormProfile.vue | 4 +- web/components/FormUser.vue | 146 ++++ web/components/TableList.vue | 9 + web/pages/admin/banner.vue | 1 + web/pages/admin/user/group.vue | 1 + web/pages/admin/user/list.vue | 47 +- web/pages/admin/user/permission.vue | 2 +- web/store/module/user.js | 6 +- web/utils/permission.js | 12 +- 17 files changed, 2417 insertions(+), 183 deletions(-) create mode 100644 web/components/FormUser.vue diff --git a/api/v1/user.proto b/api/v1/user.proto index dd9c6ed..4cfdec5 100644 --- a/api/v1/user.proto +++ b/api/v1/user.proto @@ -38,14 +38,15 @@ message User { } message RegisterAndLoginRequest { - string username = 1 [ (gogoproto.moretags) = "validate:\"min=4,max=32\"" ]; + string username = 1 [ (gogoproto.moretags) = "validate:\"min=3,max=32\"" ]; string password = 2 [ (gogoproto.moretags) = "validate:\"min=6\"" ]; string captcha = 3; string captcha_id = 4; } message GetUserCaptchaRequest { - string type = 1; // 验证码类型:register、login、comment、find_password、upload + string type = + 1; // 验证码类型:register、login、comment、find_password、upload } message LoginReply { @@ -83,11 +84,17 @@ message GetUserCaptchaReply { message UpdateUserPasswordRequest { int64 id = 1; string old_password = 2; - string new_password = 3 [ (gogoproto.moretags) = "validate:\"min=6\"" ]; + string new_password = 3 [ (gogoproto.moretags) = "validate:\"min=6\"" ]; } -message GetUserPermissionsReply{ - repeated Permission permission = 1; +message GetUserPermissionsReply { repeated Permission permission = 1; } + +message SetUserRequest { + int64 id = 1; + string username = 2 [ (gogoproto.moretags) = "validate:\"min=3,max=32\"" ]; + string password = 3 [ (gogoproto.moretags) = "validate:\"min=6\"" ]; + ; + repeated int64 group_id = 4; } service UserAPI { @@ -123,7 +130,8 @@ service UserAPI { // 更新用户密码。如果不传用户ID,则表示更新当前用户的密码; // 如果穿了用户ID,则表示更新指定用户的密码,这时需要验证当前用户的权限 - rpc UpdateUserPassword(UpdateUserPasswordRequest) returns (google.protobuf.Empty) { + rpc UpdateUserPassword(UpdateUserPasswordRequest) + returns (google.protobuf.Empty) { option (google.api.http) = { put : '/api/v1/user/password', body : '*', @@ -132,9 +140,9 @@ service UserAPI { // 更新用户密码。如果不传用户ID,则表示更新当前用户的密码; // 如果穿了用户ID,则表示更新指定用户的密码,这时需要验证当前用户的权限 - rpc UpdateUser(User) returns (google.protobuf.Empty) { + rpc UpdateUserProfile(User) returns (google.protobuf.Empty) { option (google.api.http) = { - put : '/api/v1/user', + put : '/api/v1/user/profile', body : '*', }; } @@ -146,6 +154,22 @@ service UserAPI { }; } + // 新增用户 + rpc AddUser(SetUserRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + post : '/api/v1/user', + body : '*', + }; + } + + // 设置用户 + rpc SetUser(SetUserRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + put : '/api/v1/user', + body : '*', + }; + } + // 查询用户列表。对于非管理员,返回相应用户的公开信息; // 对于管理员,返回相应用户的绝大部分信息 rpc ListUser(ListUserRequest) returns (ListUserReply) { @@ -162,7 +186,8 @@ service UserAPI { } // GetUserCaptcha 获取用户验证码 - rpc GetUserPermissions(google.protobuf.Empty) returns (GetUserPermissionsReply) { + rpc GetUserPermissions(google.protobuf.Empty) + returns (GetUserPermissionsReply) { option (google.api.http) = { get : '/api/v1/user/permission', }; diff --git a/biz/user.go b/biz/user.go index e914798..bb2d973 100644 --- a/biz/user.go +++ b/biz/user.go @@ -45,6 +45,10 @@ func (s *UserAPIService) getValidFieldMap() map[string]string { return map[string]string{"Username": "用户名", "Password": "密码"} } +func (s *UserAPIService) checkPermission(ctx context.Context) (*auth.UserClaims, error) { + return checkGRPCPermission(s.dbModel, ctx) +} + // Register 用户注册 func (s *UserAPIService) Register(ctx context.Context, req *pb.RegisterAndLoginRequest) (*emptypb.Empty, error) { err := validate.ValidateStruct(req, s.getValidFieldMap()) @@ -151,10 +155,28 @@ func (s *UserAPIService) GetUser(ctx context.Context, req *pb.GetUserRequest) (* return &pb.User{}, nil } -// UpdateUser 更改用户信息 +func (s *UserAPIService) SetUser(ctx context.Context, req *pb.SetUserRequest) (*emptypb.Empty, error) { + _, err := s.checkPermission(ctx) + if err != nil { + return nil, err + } + + if len(req.GroupId) == 0 { + return nil, status.Errorf(codes.InvalidArgument, "用户组不能为空") + } + + err = s.dbModel.SetUserGroupAndPassword(req.Id, req.GroupId, req.Password) + if err != nil { + return nil, status.Errorf(codes.Internal, err.Error()) + } + + return &emptypb.Empty{}, nil +} + +// UpdateUserProfile 更改用户信息 // 1. 用户更改自身信息 // 2. 管理员更改用户信息 -func (s *UserAPIService) UpdateUser(ctx context.Context, req *pb.User) (*emptypb.Empty, error) { +func (s *UserAPIService) UpdateUserProfile(ctx context.Context, req *pb.User) (*emptypb.Empty, error) { userClaims, ok := ctx.Value(auth.CtxKeyUserClaims).(*auth.UserClaims) if !ok || s.dbModel.IsInvalidToken(userClaims.UUID) { return nil, status.Errorf(codes.Unauthenticated, ErrorMessageInvalidToken) @@ -308,16 +330,36 @@ func (s *UserAPIService) ListUser(ctx context.Context, req *pb.ListUserRequest) } opt.SelectFields = limitFileds - var pbUser []*pb.User + var pbUsers []*pb.User userList, total, err := s.dbModel.GetUserList(opt) if err == gorm.ErrRecordNotFound { err = nil return &pb.ListUserReply{}, nil } + util.CopyStruct(&userList, &pbUsers) + + // 查询用户ID + var ( + userIds []interface{} + userIndex = make(map[int64]int) + ) + + for index, pbUser := range pbUsers { + userIds = append(userIds, pbUser.Id) + userIndex[pbUser.Id] = index + } + + userGroups, _, _ := s.dbModel.GetUserGroupList(&model.OptionGetUserGroupList{ + QueryIn: map[string][]interface{}{"user_id": userIds}, + }) + + for _, userGroup := range userGroups { + index := userIndex[userGroup.UserId] + pbUsers[index].GroupId = append(pbUsers[index].GroupId, userGroup.GroupId) + } - util.CopyStruct(&userList, &pbUser) - s.logger.Debug("ListUser", zap.Any("userList", userList), zap.Any("pbUser", pbUser), zap.Int64("total", total)) - return &pb.ListUserReply{Total: total, User: pbUser}, nil + s.logger.Debug("ListUser", zap.Any("userList", userList), zap.Any("pbUser", pbUsers), zap.Int64("total", total)) + return &pb.ListUserReply{Total: total, User: pbUsers}, nil } // GetUserCaptcha 获取用户验证码 @@ -370,3 +412,36 @@ func (s *UserAPIService) GetUserPermissions(ctx context.Context, req *emptypb.Em return &pb.GetUserPermissionsReply{Permission: pbPermissions}, nil } + +// AddUser 新增用户 +func (s *UserAPIService) AddUser(ctx context.Context, req *pb.SetUserRequest) (*emptypb.Empty, error) { + s.logger.Debug("AddUser", zap.Any("req", req)) + + _, err := s.checkPermission(ctx) + if err != nil { + return nil, err + } + + err = validate.ValidateStruct(req, s.getValidFieldMap()) + if err != nil { + return nil, status.Errorf(codes.InvalidArgument, err.Error()) + } + + if len(req.GroupId) == 0 { + return nil, status.Errorf(codes.InvalidArgument, "用户组不能为空") + } + + existUser, _ := s.dbModel.GetUserByUsername(req.Username, "id") + if existUser.Id > 0 { + return nil, status.Errorf(codes.InvalidArgument, "用户名已存在") + } + + // 新增用户 + user := &model.User{Username: req.Username, Password: req.Password} + err = s.dbModel.CreateUser(user, req.GroupId...) + if err != nil { + return nil, status.Errorf(codes.Internal, err.Error()) + } + + return &emptypb.Empty{}, nil +} diff --git a/docs/api.md b/docs/api.md index f52b79c..e4da09e 100644 --- a/docs/api.md +++ b/docs/api.md @@ -3,23 +3,79 @@ ## Table of Contents +- [api/v1/attachment.proto](#api_v1_attachment-proto) + - [Attachment](#api-v1-Attachment) + - [DeleteAttachmentRequest](#api-v1-DeleteAttachmentRequest) + - [GetAttachmentRequest](#api-v1-GetAttachmentRequest) + - [ListAttachmentReply](#api-v1-ListAttachmentReply) + - [ListAttachmentRequest](#api-v1-ListAttachmentRequest) + + - [AttachmentAPI](#api-v1-AttachmentAPI) + +- [api/v1/banner.proto](#api_v1_banner-proto) + - [Banner](#api-v1-Banner) + - [DeleteBannerRequest](#api-v1-DeleteBannerRequest) + - [GetBannerRequest](#api-v1-GetBannerRequest) + - [ListBannerReply](#api-v1-ListBannerReply) + - [ListBannerRequest](#api-v1-ListBannerRequest) + + - [BannerAPI](#api-v1-BannerAPI) + +- [api/v1/config.proto](#api_v1_config-proto) + - [Config](#api-v1-Config) + - [Configs](#api-v1-Configs) + - [ListConfigRequest](#api-v1-ListConfigRequest) + + - [ConfigAPI](#api-v1-ConfigAPI) + +- [api/v1/friendlink.proto](#api_v1_friendlink-proto) + - [DeleteFriendlinkRequest](#api-v1-DeleteFriendlinkRequest) + - [Friendlink](#api-v1-Friendlink) + - [GetFriendlinkRequest](#api-v1-GetFriendlinkRequest) + - [ListFriendlinkReply](#api-v1-ListFriendlinkReply) + - [ListFriendlinkRequest](#api-v1-ListFriendlinkRequest) + + - [FriendlinkAPI](#api-v1-FriendlinkAPI) + +- [api/v1/group.proto](#api_v1_group-proto) + - [DeleteGroupRequest](#api-v1-DeleteGroupRequest) + - [GetGroupPermissionRequest](#api-v1-GetGroupPermissionRequest) + - [GetGroupRequest](#api-v1-GetGroupRequest) + - [Group](#api-v1-Group) + - [GroupPermissions](#api-v1-GroupPermissions) + - [ListGroupReply](#api-v1-ListGroupReply) + - [ListGroupRequest](#api-v1-ListGroupRequest) + - [UpdateGroupPermissionRequest](#api-v1-UpdateGroupPermissionRequest) + + - [GroupAPI](#api-v1-GroupAPI) + - [api/v1/health.proto](#api_v1_health-proto) - [PingRequest](#-PingRequest) - [PongReply](#-PongReply) - [HealthAPI](#-HealthAPI) +- [api/v1/permission.proto](#api_v1_permission-proto) + - [DeletePermissionRequest](#api-v1-DeletePermissionRequest) + - [GetPermissionReply](#api-v1-GetPermissionReply) + - [GetPermissionRequest](#api-v1-GetPermissionRequest) + - [ListPermissionReply](#api-v1-ListPermissionReply) + - [ListPermissionRequest](#api-v1-ListPermissionRequest) + - [Permission](#api-v1-Permission) + + - [PermissionAPI](#api-v1-PermissionAPI) + - [api/v1/user.proto](#api_v1_user-proto) - [DeleteUserRequest](#api-v1-DeleteUserRequest) + - [GetUserCaptchaReply](#api-v1-GetUserCaptchaReply) + - [GetUserCaptchaRequest](#api-v1-GetUserCaptchaRequest) + - [GetUserPermissionsReply](#api-v1-GetUserPermissionsReply) - [GetUserRequest](#api-v1-GetUserRequest) - [ListUserReply](#api-v1-ListUserReply) - [ListUserRequest](#api-v1-ListUserRequest) - [LoginReply](#api-v1-LoginReply) - - [LoginRequest](#api-v1-LoginRequest) - - [RegisterRequest](#api-v1-RegisterRequest) + - [RegisterAndLoginRequest](#api-v1-RegisterAndLoginRequest) - [UpdateUserPasswordRequest](#api-v1-UpdateUserPasswordRequest) - - [UpdateUserReply](#api-v1-UpdateUserReply) - - [UpdateUserRequest](#api-v1-UpdateUserRequest) - [User](#api-v1-User) - [UserAPI](#api-v1-UserAPI) @@ -28,6 +84,619 @@ + +

Top

+ +## api/v1/attachment.proto + + + + + +### Attachment + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | | | +| hash | [string](#string) | | | +| user_id | [int64](#int64) | | | +| type_id | [int64](#int64) | | | +| type | [int32](#int32) | | | +| enable | [bool](#bool) | | | +| path | [string](#string) | | | +| name | [string](#string) | | | +| size | [int64](#int64) | | | +| width | [int64](#int64) | | | +| height | [int64](#int64) | | | +| ext | [string](#string) | | | +| ip | [string](#string) | | | +| username | [string](#string) | | 用户名称 | +| type_name | [string](#string) | | 附件类型名称 | +| description | [string](#string) | | 附件描述、备注 | +| created_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | +| updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | + + + + + + + + +### DeleteAttachmentRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | repeated | | + + + + + + + + +### GetAttachmentRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | | | + + + + + + + + +### ListAttachmentReply + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| total | [int64](#int64) | | | +| attachment | [Attachment](#api-v1-Attachment) | repeated | | + + + + + + + + +### ListAttachmentRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| page | [int64](#int64) | | | +| size | [int64](#int64) | | | +| wd | [string](#string) | | 搜索关键字 | +| enable | [bool](#bool) | repeated | | +| user_id | [int64](#int64) | repeated | 用户ID | +| type | [int64](#int64) | repeated | 类型 | +| ext | [string](#string) | | 扩展名 | + + + + + + + + + + + + + + +### AttachmentAPI +附件服务。只有管理员才有权限操作 + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| UpdateAttachment | [Attachment](#api-v1-Attachment) | [.google.protobuf.Empty](#google-protobuf-Empty) | | +| DeleteAttachment | [DeleteAttachmentRequest](#api-v1-DeleteAttachmentRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | | +| GetAttachment | [GetAttachmentRequest](#api-v1-GetAttachmentRequest) | [Attachment](#api-v1-Attachment) | | +| ListAttachment | [ListAttachmentRequest](#api-v1-ListAttachmentRequest) | [ListAttachmentReply](#api-v1-ListAttachmentReply) | | + + + + + + +

Top

+ +## api/v1/banner.proto + + + + + +### Banner + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | | | +| title | [string](#string) | | | +| path | [string](#string) | | | +| sort | [int32](#int32) | | | +| enable | [bool](#bool) | | | +| type | [int32](#int32) | | | +| url | [string](#string) | | | +| description | [string](#string) | | | +| created_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | +| updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | + + + + + + + + +### DeleteBannerRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | repeated | | + + + + + + + + +### GetBannerRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | | | + + + + + + + + +### ListBannerReply + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| total | [int64](#int64) | | | +| banner | [Banner](#api-v1-Banner) | repeated | | + + + + + + + + +### ListBannerRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| page | [int64](#int64) | | | +| size | [int64](#int64) | | | +| type | [int32](#int32) | repeated | | +| enable | [bool](#bool) | repeated | | +| wd | [string](#string) | | | + + + + + + + + + + + + + + +### BannerAPI + + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| CreateBanner | [Banner](#api-v1-Banner) | [Banner](#api-v1-Banner) | | +| UpdateBanner | [Banner](#api-v1-Banner) | [.google.protobuf.Empty](#google-protobuf-Empty) | | +| DeleteBanner | [DeleteBannerRequest](#api-v1-DeleteBannerRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | | +| GetBanner | [GetBannerRequest](#api-v1-GetBannerRequest) | [Banner](#api-v1-Banner) | | +| ListBanner | [ListBannerRequest](#api-v1-ListBannerRequest) | [ListBannerReply](#api-v1-ListBannerReply) | | + + + + + + +

Top

+ +## api/v1/config.proto + + + + + +### Config + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | | | +| label | [string](#string) | | | +| name | [string](#string) | | | +| value | [string](#string) | | | +| placeholder | [string](#string) | | | +| input_type | [string](#string) | | | +| category | [string](#string) | | | +| sort | [int32](#int32) | | | +| options | [string](#string) | | | +| created_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | +| updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | + + + + + + + + +### Configs + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| config | [Config](#api-v1-Config) | repeated | | + + + + + + + + +### ListConfigRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| category | [string](#string) | repeated | | + + + + + + + + + + + + + + +### ConfigAPI + + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| UpdateConfig | [Configs](#api-v1-Configs) | [.google.protobuf.Empty](#google-protobuf-Empty) | UpdateConfig 更新配置 | +| ListConfig | [ListConfigRequest](#api-v1-ListConfigRequest) | [Configs](#api-v1-Configs) | ListConfig 查询配置项 | + + + + + + +

Top

+ +## api/v1/friendlink.proto + + + + + +### DeleteFriendlinkRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | repeated | | + + + + + + + + +### Friendlink + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int32](#int32) | | | +| title | [string](#string) | | | +| link | [string](#string) | | | +| description | [string](#string) | | | +| sort | [int32](#int32) | | | +| enable | [bool](#bool) | | | +| created_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | +| updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | + + + + + + + + +### GetFriendlinkRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | | | + + + + + + + + +### ListFriendlinkReply + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| friendlink | [Friendlink](#api-v1-Friendlink) | repeated | | +| total | [int64](#int64) | | | + + + + + + + + +### ListFriendlinkRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| page | [int32](#int32) | | | +| size | [int32](#int32) | | | +| wd | [string](#string) | | | +| enable | [bool](#bool) | repeated | | + + + + + + + + + + + + + + +### FriendlinkAPI + + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| CreateFriendlink | [Friendlink](#api-v1-Friendlink) | [Friendlink](#api-v1-Friendlink) | | +| UpdateFriendlink | [Friendlink](#api-v1-Friendlink) | [.google.protobuf.Empty](#google-protobuf-Empty) | | +| DeleteFriendlink | [DeleteFriendlinkRequest](#api-v1-DeleteFriendlinkRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | | +| GetFriendlink | [GetFriendlinkRequest](#api-v1-GetFriendlinkRequest) | [Friendlink](#api-v1-Friendlink) | | +| ListFriendlink | [ListFriendlinkRequest](#api-v1-ListFriendlinkRequest) | [ListFriendlinkReply](#api-v1-ListFriendlinkReply) | | + + + + + + +

Top

+ +## api/v1/group.proto + + + + + +### DeleteGroupRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | repeated | | + + + + + + + + +### GetGroupPermissionRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | | | + + + + + + + + +### GetGroupRequest +根据组名或者ID获取用户组 + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | | | +| title | [string](#string) | | | + + + + + + + + +### Group + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| id | [int64](#int64) | | | +| title | [string](#string) | | | +| color | [string](#string) | | | +| is_default | [bool](#bool) | | | +| is_display | [bool](#bool) | | | +| description | [string](#string) | | | +| user_count | [int32](#int32) | | | +| sort | [int32](#int32) | | | +| created_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | +| updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | + + + + + + + + +### GroupPermissions + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| permission_id | [int64](#int64) | repeated | | + + + + + + + + +### ListGroupReply + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| group | [Group](#api-v1-Group) | repeated | | +| total | [int64](#int64) | | | + + + + + + + + +### ListGroupRequest +查询用户组列表。不需要分页,直接返回全部用户组,只是可以指定查询哪些字段 + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| wd | [string](#string) | | | +| page | [int64](#int64) | | | +| size | [int64](#int64) | | | +| sort | [string](#string) | | | +| field | [string](#string) | repeated | | + + + + + + + + +### UpdateGroupPermissionRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| group_id | [int64](#int64) | | | +| permission_id | [int64](#int64) | repeated | | + + + + + + + + + + + + + + +### GroupAPI + + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| CreateGroup | [Group](#api-v1-Group) | [Group](#api-v1-Group) | 创建用户组 | +| UpdateGroup | [Group](#api-v1-Group) | [.google.protobuf.Empty](#google-protobuf-Empty) | 更新用户组 | +| DeleteGroup | [DeleteGroupRequest](#api-v1-DeleteGroupRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | 删除用户组 | +| GetGroup | [GetGroupRequest](#api-v1-GetGroupRequest) | [Group](#api-v1-Group) | 获取用户组列表 | +| ListGroup | [ListGroupRequest](#api-v1-ListGroupRequest) | [ListGroupReply](#api-v1-ListGroupReply) | | +| GetGroupPermission | [GetGroupPermissionRequest](#api-v1-GetGroupPermissionRequest) | [GroupPermissions](#api-v1-GroupPermissions) | 获取用户组权限列表 | +| UpdateGroupPermission | [UpdateGroupPermissionRequest](#api-v1-UpdateGroupPermissionRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | 更新用户组权限,给用户组设置权限 | + + + + +

Top

@@ -86,16 +755,16 @@ - +

Top

-## api/v1/user.proto +## api/v1/permission.proto - + -### DeleteUserRequest +### DeletePermissionRequest @@ -108,9 +777,24 @@ - + -### GetUserRequest +### GetPermissionReply + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| permission | [Permission](#api-v1-Permission) | | | + + + + + + + + +### GetPermissionRequest @@ -123,25 +807,25 @@ - + -### ListUserReply +### ListPermissionReply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | total | [int64](#int64) | | | -| user | [User](#api-v1-User) | repeated | | +| permission | [Permission](#api-v1-Permission) | repeated | | - + -### ListUserRequest +### ListPermissionRequest @@ -149,86 +833,188 @@ | ----- | ---- | ----- | ----------- | | page | [int64](#int64) | | | | size | [int64](#int64) | | | -| ids | [int64](#int64) | repeated | | +| wd | [string](#string) | | | +| method | [string](#string) | repeated | | +| path | [string](#string) | | | - + -### LoginReply +### Permission | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| token | [string](#string) | | | +| id | [int64](#int64) | | | +| method | [string](#string) | | | +| path | [string](#string) | | | +| title | [string](#string) | | | +| description | [string](#string) | | | +| created_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | +| updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | + + + + + + + + + + + + + + +### PermissionAPI +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| UpdatePermission | [Permission](#api-v1-Permission) | [.google.protobuf.Empty](#google-protobuf-Empty) | | +| GetPermission | [GetPermissionRequest](#api-v1-GetPermissionRequest) | [Permission](#api-v1-Permission) | | +| ListPermission | [ListPermissionRequest](#api-v1-ListPermissionRequest) | [ListPermissionReply](#api-v1-ListPermissionReply) | | + + + + + + +

Top

+## api/v1/user.proto - + -### LoginRequest +### DeleteUserRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| username | [string](#string) | | | -| password | [string](#string) | | | +| id | [int64](#int64) | repeated | | + + + + + + + + +### GetUserCaptchaReply + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| enable | [bool](#bool) | | | +| id | [string](#string) | | | | captcha | [string](#string) | | | +| type | [string](#string) | | | - + -### RegisterRequest +### GetUserCaptchaRequest | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| username | [string](#string) | | | -| password | [string](#string) | | | +| type | [string](#string) | | 验证码类型:register、login、comment、find_password、upload | - + + +### GetUserPermissionsReply + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| permission | [Permission](#api-v1-Permission) | repeated | | + + + + + + + + +### GetUserRequest -### UpdateUserPasswordRequest -修改用户密码 | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [int64](#int64) | | | -| old_password | [string](#string) | | | -| new_password | [string](#string) | | | - + + +### ListUserReply + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| total | [int64](#int64) | | | +| user | [User](#api-v1-User) | repeated | | + + + + + + + + +### ListUserRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| page | [int64](#int64) | | | +| size | [int64](#int64) | | | +| wd | [string](#string) | | | +| sort | [string](#string) | | | +| id | [int64](#int64) | repeated | | +| group_id | [int64](#int64) | repeated | | +| status | [int32](#int32) | repeated | | + + + + + + + -### UpdateUserReply +### LoginReply | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | +| token | [string](#string) | | | | user | [User](#api-v1-User) | | | @@ -236,16 +1022,35 @@ - + + +### RegisterAndLoginRequest + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| username | [string](#string) | | | +| password | [string](#string) | | | +| captcha | [string](#string) | | | +| captcha_id | [string](#string) | | | + + + + + -### UpdateUserRequest + +### UpdateUserPasswordRequest +修改用户密码 | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | id | [int64](#int64) | | | -| name | [string](#string) | | | +| old_password | [string](#string) | | | +| new_password | [string](#string) | | | @@ -260,10 +1065,11 @@ | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | +| login_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | +| created_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | +| updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | | id | [int64](#int64) | | | | username | [string](#string) | | | -| password | [string](#string) | | | -| nickname | [string](#string) | | | | mobile | [string](#string) | | | | email | [string](#string) | | | | address | [string](#string) | | | @@ -279,9 +1085,7 @@ | avatar | [string](#string) | | | | identity | [string](#string) | | | | realname | [string](#string) | | | -| login_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | -| created_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | -| updated_at | [google.protobuf.Timestamp](#google-protobuf-Timestamp) | | | +| group_id | [int64](#int64) | repeated | | @@ -301,12 +1105,16 @@ | Method Name | Request Type | Response Type | Description | | ----------- | ------------ | ------------- | ------------| -| Register | [RegisterRequest](#api-v1-RegisterRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | 用户注册 | -| Login | [LoginRequest](#api-v1-LoginRequest) | [LoginReply](#api-v1-LoginReply) | 用户登录 | +| Register | [RegisterAndLoginRequest](#api-v1-RegisterAndLoginRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | 用户注册 | +| Login | [RegisterAndLoginRequest](#api-v1-RegisterAndLoginRequest) | [LoginReply](#api-v1-LoginReply) | 用户登录 | +| Logout | [.google.protobuf.Empty](#google-protobuf-Empty) | [.google.protobuf.Empty](#google-protobuf-Empty) | 退出登录 | | GetUser | [GetUserRequest](#api-v1-GetUserRequest) | [User](#api-v1-User) | 查询用户信息。如果传递了Id参数,则表示查询用户的公开信息,否则查询当前用户的私有信息 | -| UpdateUserPassword | [UpdateUserRequest](#api-v1-UpdateUserRequest) | [User](#api-v1-User) | 更新用户密码。如果不传用户ID,则表示更新当前用户的密码; 如果穿了用户ID,则表示更新指定用户的密码,这时需要验证当前用户的权限 | +| UpdateUserPassword | [UpdateUserPasswordRequest](#api-v1-UpdateUserPasswordRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | 更新用户密码。如果不传用户ID,则表示更新当前用户的密码; 如果穿了用户ID,则表示更新指定用户的密码,这时需要验证当前用户的权限 | +| UpdateUser | [User](#api-v1-User) | [.google.protobuf.Empty](#google-protobuf-Empty) | 更新用户密码。如果不传用户ID,则表示更新当前用户的密码; 如果穿了用户ID,则表示更新指定用户的密码,这时需要验证当前用户的权限 | | DeleteUser | [DeleteUserRequest](#api-v1-DeleteUserRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | 删除用户。需要验证用户权限 | | ListUser | [ListUserRequest](#api-v1-ListUserRequest) | [ListUserReply](#api-v1-ListUserReply) | 查询用户列表。对于非管理员,返回相应用户的公开信息; 对于管理员,返回相应用户的绝大部分信息 | +| GetUserCaptcha | [GetUserCaptchaRequest](#api-v1-GetUserCaptchaRequest) | [GetUserCaptchaReply](#api-v1-GetUserCaptchaReply) | GetUserCaptcha 获取用户验证码 | +| GetUserPermissions | [.google.protobuf.Empty](#google-protobuf-Empty) | [GetUserPermissionsReply](#api-v1-GetUserPermissionsReply) | GetUserCaptcha 获取用户验证码 | diff --git a/docs/openapi.yaml b/docs/openapi.yaml index 5738653..e24ba1e 100644 --- a/docs/openapi.yaml +++ b/docs/openapi.yaml @@ -6,6 +6,713 @@ info: title: "" version: 0.0.1 paths: + /api/v1/attachment: + get: + tags: + - AttachmentAPI + operationId: AttachmentAPI_GetAttachment + parameters: + - name: id + in: query + schema: + type: integer + format: int64 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Attachment' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + put: + tags: + - AttachmentAPI + operationId: AttachmentAPI_UpdateAttachment + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Attachment' + required: true + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + delete: + tags: + - AttachmentAPI + operationId: AttachmentAPI_DeleteAttachment + parameters: + - name: id + in: query + schema: + type: array + items: + type: integer + format: int64 + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/attachment/list: + get: + tags: + - AttachmentAPI + operationId: AttachmentAPI_ListAttachment + parameters: + - name: page + in: query + schema: + type: integer + format: int64 + - name: size + in: query + schema: + type: integer + format: int64 + - name: wd + in: query + schema: + type: string + - name: enable + in: query + schema: + type: array + items: + type: boolean + - name: userId + in: query + schema: + type: array + items: + type: integer + format: int64 + - name: type + in: query + schema: + type: array + items: + type: integer + format: int64 + - name: ext + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListAttachmentReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/banner: + get: + tags: + - BannerAPI + operationId: BannerAPI_GetBanner + parameters: + - name: id + in: query + schema: + type: integer + format: int64 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Banner' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + put: + tags: + - BannerAPI + operationId: BannerAPI_UpdateBanner + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Banner' + required: true + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + post: + tags: + - BannerAPI + operationId: BannerAPI_CreateBanner + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Banner' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Banner' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + delete: + tags: + - BannerAPI + operationId: BannerAPI_DeleteBanner + parameters: + - name: id + in: query + schema: + type: array + items: + type: integer + format: int64 + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/banner/list: + get: + tags: + - BannerAPI + operationId: BannerAPI_ListBanner + parameters: + - name: page + in: query + schema: + type: integer + format: int64 + - name: size + in: query + schema: + type: integer + format: int64 + - name: type + in: query + schema: + type: array + items: + type: integer + format: int32 + - name: enable + in: query + schema: + type: array + items: + type: boolean + - name: wd + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListBannerReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/config: + put: + tags: + - ConfigAPI + description: UpdateConfig 更新配置 + operationId: ConfigAPI_UpdateConfig + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Configs' + required: true + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/config/list: + get: + tags: + - ConfigAPI + description: ListConfig 查询配置项 + operationId: ConfigAPI_ListConfig + parameters: + - name: category + in: query + schema: + type: array + items: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Configs' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/friendlink: + get: + tags: + - FriendlinkAPI + operationId: FriendlinkAPI_GetFriendlink + parameters: + - name: id + in: query + schema: + type: integer + format: int64 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Friendlink' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + put: + tags: + - FriendlinkAPI + operationId: FriendlinkAPI_UpdateFriendlink + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Friendlink' + required: true + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + post: + tags: + - FriendlinkAPI + operationId: FriendlinkAPI_CreateFriendlink + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Friendlink' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Friendlink' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + delete: + tags: + - FriendlinkAPI + operationId: FriendlinkAPI_DeleteFriendlink + parameters: + - name: id + in: query + schema: + type: array + items: + type: integer + format: int64 + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/friendlink/list: + get: + tags: + - FriendlinkAPI + operationId: FriendlinkAPI_ListFriendlink + parameters: + - name: page + in: query + schema: + type: integer + format: int32 + - name: size + in: query + schema: + type: integer + format: int32 + - name: wd + in: query + schema: + type: string + - name: enable + in: query + schema: + type: array + items: + type: boolean + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListFriendlinkReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/group: + get: + tags: + - GroupAPI + description: 获取用户组列表 + operationId: GroupAPI_GetGroup + parameters: + - name: id + in: query + schema: + type: integer + format: int64 + - name: title + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + put: + tags: + - GroupAPI + description: 更新用户组 + operationId: GroupAPI_UpdateGroup + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + required: true + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + post: + tags: + - GroupAPI + description: 创建用户组 + operationId: GroupAPI_CreateGroup + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + required: true + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Group' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + delete: + tags: + - GroupAPI + description: 删除用户组 + operationId: GroupAPI_DeleteGroup + parameters: + - name: id + in: query + schema: + type: array + items: + type: integer + format: int64 + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/group/list: + get: + tags: + - GroupAPI + operationId: GroupAPI_ListGroup + parameters: + - name: wd + in: query + schema: + type: string + - name: page + in: query + schema: + type: integer + format: int64 + - name: size + in: query + schema: + type: integer + format: int64 + - name: sort + in: query + schema: + type: string + - name: field + in: query + schema: + type: array + items: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListGroupReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/group/permission: + get: + tags: + - GroupAPI + description: 获取用户组权限列表 + operationId: GroupAPI_GetGroupPermission + parameters: + - name: id + in: query + schema: + type: integer + format: int64 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/GroupPermissions' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + put: + tags: + - GroupAPI + description: 更新用户组权限,给用户组设置权限 + operationId: GroupAPI_UpdateGroupPermission + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/UpdateGroupPermissionRequest' + required: true + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/permission: + get: + tags: + - PermissionAPI + operationId: PermissionAPI_GetPermission + parameters: + - name: id + in: query + schema: + type: integer + format: int64 + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Permission' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + put: + tags: + - PermissionAPI + operationId: PermissionAPI_UpdatePermission + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/Permission' + required: true + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' + /api/v1/permission/list: + get: + tags: + - PermissionAPI + operationId: PermissionAPI_ListPermission + parameters: + - name: page + in: query + schema: + type: integer + format: int64 + - name: size + in: query + schema: + type: integer + format: int64 + - name: wd + in: query + schema: + type: string + - name: method + in: query + schema: + type: array + items: + type: string + - name: path + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/ListPermissionReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /api/v1/ping: get: tags: @@ -54,6 +761,29 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + put: + tags: + - UserAPI + description: |- + 更新用户密码。如果不传用户ID,则表示更新当前用户的密码; + 如果穿了用户ID,则表示更新指定用户的密码,这时需要验证当前用户的权限 + operationId: UserAPI_UpdateUser + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/User' + required: true + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' delete: tags: - UserAPI @@ -77,6 +807,30 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /api/v1/user/captcha: + get: + tags: + - UserAPI + description: GetUserCaptcha 获取用户验证码 + operationId: UserAPI_GetUserCaptcha + parameters: + - name: type + in: query + schema: + type: string + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/GetUserCaptchaReply' + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /api/v1/user/list: get: tags: @@ -89,20 +843,42 @@ paths: - name: page in: query schema: - type: integer - format: int64 - - name: size + type: integer + format: int64 + - name: size + in: query + schema: + type: integer + format: int64 + - name: wd + in: query + schema: + type: string + - name: sort + in: query + schema: + type: string + - name: id + in: query + schema: + type: array + items: + type: integer + format: int64 + - name: groupId in: query schema: - type: integer - format: int64 - - name: ids + type: array + items: + type: integer + format: int64 + - name: status in: query schema: type: array items: type: integer - format: int64 + format: int32 responses: "200": description: OK @@ -126,7 +902,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/LoginRequest' + $ref: '#/components/schemas/RegisterAndLoginRequest' required: true responses: "200": @@ -141,6 +917,22 @@ paths: application/json: schema: $ref: '#/components/schemas/Status' + /api/v1/user/logout: + delete: + tags: + - UserAPI + description: 退出登录 + operationId: UserAPI_Logout + responses: + "200": + description: OK + content: {} + default: + description: Default error response + content: + application/json: + schema: + $ref: '#/components/schemas/Status' /api/v1/user/password: put: tags: @@ -153,15 +945,31 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateUserRequest' + $ref: '#/components/schemas/UpdateUserPasswordRequest' required: true responses: "200": description: OK + content: {} + default: + description: Default error response content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: '#/components/schemas/Status' + /api/v1/user/permission: + get: + tags: + - UserAPI + description: GetUserCaptcha 获取用户验证码 + operationId: UserAPI_GetUserPermissions + responses: + "200": + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/GetUserPermissionsReply' default: description: Default error response content: @@ -178,7 +986,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/RegisterRequest' + $ref: '#/components/schemas/RegisterAndLoginRequest' required: true responses: "200": @@ -207,6 +1015,159 @@ paths: $ref: '#/components/schemas/Status' components: schemas: + Attachment: + type: object + properties: + id: + type: integer + format: int64 + hash: + type: string + userId: + type: integer + format: int64 + typeId: + type: integer + format: int64 + type: + type: integer + format: int32 + enable: + type: boolean + path: + type: string + name: + type: string + size: + type: integer + format: int64 + width: + type: integer + format: int64 + height: + type: integer + format: int64 + ext: + type: string + ip: + type: string + username: + type: string + typeName: + type: string + description: + type: string + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + Banner: + type: object + properties: + id: + type: integer + format: int64 + title: + type: string + path: + type: string + sort: + type: integer + format: int32 + enable: + type: boolean + type: + type: integer + format: int32 + url: + type: string + description: + type: string + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + Config: + type: object + properties: + id: + type: integer + format: int64 + label: + type: string + name: + type: string + value: + type: string + placeholder: + type: string + inputType: + type: string + category: + type: string + sort: + type: integer + format: int32 + options: + type: string + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + Configs: + type: object + properties: + config: + type: array + items: + $ref: '#/components/schemas/Config' + Friendlink: + type: object + properties: + id: + type: integer + format: int32 + title: + type: string + link: + type: string + description: + type: string + sort: + type: integer + format: int32 + enable: + type: boolean + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + GetUserCaptchaReply: + type: object + properties: + enable: + type: boolean + id: + type: string + captcha: + type: string + type: + type: string + GetUserPermissionsReply: + type: object + properties: + permission: + type: array + items: + $ref: '#/components/schemas/Permission' GoogleProtobufAny: type: object properties: @@ -215,6 +1176,92 @@ components: description: The type of the serialized message. additionalProperties: true description: Contains an arbitrary serialized message along with a @type that describes the type of the serialized message. + Group: + type: object + properties: + id: + type: integer + format: int64 + title: + type: string + color: + type: string + isDefault: + type: boolean + isDisplay: + type: boolean + description: + type: string + userCount: + type: integer + format: int32 + sort: + type: integer + format: int32 + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time + GroupPermissions: + type: object + properties: + permissionId: + type: array + items: + type: integer + format: int64 + ListAttachmentReply: + type: object + properties: + total: + type: integer + format: int64 + attachment: + type: array + items: + $ref: '#/components/schemas/Attachment' + ListBannerReply: + type: object + properties: + total: + type: integer + format: int64 + banner: + type: array + items: + $ref: '#/components/schemas/Banner' + ListFriendlinkReply: + type: object + properties: + friendlink: + type: array + items: + $ref: '#/components/schemas/Friendlink' + total: + type: integer + format: int64 + ListGroupReply: + type: object + properties: + group: + type: array + items: + $ref: '#/components/schemas/Group' + total: + type: integer + format: int64 + ListPermissionReply: + type: object + properties: + total: + type: integer + format: int64 + permission: + type: array + items: + $ref: '#/components/schemas/Permission' ListUserReply: type: object properties: @@ -230,15 +1277,28 @@ components: properties: token: type: string - LoginRequest: + user: + $ref: '#/components/schemas/User' + Permission: type: object properties: - username: + id: + type: integer + format: int64 + method: type: string - password: + path: type: string - captcha: + title: + type: string + description: + type: string + createdAt: + type: string + format: date-time + updatedAt: type: string + format: date-time PongReply: type: object properties: @@ -247,13 +1307,17 @@ components: createdAt: type: string format: date-time - RegisterRequest: + RegisterAndLoginRequest: type: object properties: username: type: string password: type: string + captcha: + type: string + captchaId: + type: string Status: type: object properties: @@ -270,26 +1334,45 @@ components: $ref: '#/components/schemas/GoogleProtobufAny' description: A list of messages that carry the error details. There is a common set of message types for APIs to use. description: 'The `Status` type defines a logical error model that is suitable for different programming environments, including REST APIs and RPC APIs. It is used by [gRPC](https://github.com/grpc). Each `Status` message contains three pieces of data: error code, error message, and error details. You can find out more about this error model and how to work with it in the [API Design Guide](https://cloud.google.com/apis/design/errors).' - UpdateUserRequest: + UpdateGroupPermissionRequest: + type: object + properties: + groupId: + type: integer + format: int64 + permissionId: + type: array + items: + type: integer + format: int64 + UpdateUserPasswordRequest: type: object properties: id: type: integer format: int64 - name: + oldPassword: type: string + newPassword: + type: string + description: 修改用户密码 User: type: object properties: + loginAt: + type: string + format: date-time + createdAt: + type: string + format: date-time + updatedAt: + type: string + format: date-time id: type: integer format: int64 username: type: string - password: - type: string - nickname: - type: string mobile: type: string email: @@ -326,15 +1409,18 @@ components: type: string realname: type: string - loginAt: - type: string - format: date-time - createdAt: - type: string - format: date-time - updatedAt: - type: string - format: date-time + groupId: + type: array + items: + type: integer + format: int64 tags: + - name: AttachmentAPI + description: 附件服务。只有管理员才有权限操作 + - name: BannerAPI + - name: ConfigAPI + - name: FriendlinkAPI + - name: GroupAPI - name: HealthAPI + - name: PermissionAPI - name: UserAPI diff --git a/model/user.go b/model/user.go index 809c730..d9d0d2c 100644 --- a/model/user.go +++ b/model/user.go @@ -82,7 +82,7 @@ func (User) TableName() string { // CreateUser 创建User // TODO: 创建成功之后,注意相关表统计字段数值的增减 -func (m *DBModel) CreateUser(user *User, groupId int64) (err error) { +func (m *DBModel) CreateUser(user *User, groupIds ...int64) (err error) { user.Password, _ = unchained.MakePassword(user.Password, unchained.GetRandomString(4), "md5") sess := m.db.Begin() @@ -102,30 +102,38 @@ func (m *DBModel) CreateUser(user *User, groupId int64) (err error) { } // 2. 添加用户组 - group := &UserGroup{ - UserId: user.Id, - GroupId: groupId, - } - err = sess.Create(group).Error - if err != nil { - m.logger.Error("CreateUser", zap.Error(err)) - return - } + for _, groupId := range groupIds { + group := &UserGroup{ + UserId: user.Id, + GroupId: groupId, + } + err = sess.Create(group).Error + if err != nil { + m.logger.Error("CreateUser", zap.Error(err)) + return + } - // 3. 添加用户统计 - err = sess.Model(&Group{}).Where("id = ?", groupId).Update("user_count", gorm.Expr("user_count + ?", 1)).Error - if err != nil { - m.logger.Error("CreateUser", zap.Error(err)) - return + // 3. 添加用户统计 + err = sess.Model(&Group{}).Where("id = ?", groupId).Update("user_count", gorm.Expr("user_count + ?", 1)).Error + if err != nil { + m.logger.Error("CreateUser", zap.Error(err)) + return + } } return } // UpdateUserPassword 更新User密码 -func (m *DBModel) UpdateUserPassword(id interface{}, newPassword string) (err error) { +func (m *DBModel) UpdateUserPassword(id interface{}, newPassword string, tx ...*gorm.DB) (err error) { newPassword, _ = unchained.MakePassword(newPassword, unchained.GetRandomString(4), "md5") - err = m.db.Model(&User{}).Where("id = ?", id).Update("password", newPassword).Error + + user := &User{} + sess := m.db.Model(user) + if len(tx) > 0 { + sess = tx[0].Model(user) + } + err = sess.Where("id = ?", id).Update("password", newPassword).Error if err != nil { m.logger.Error("UpdateUserPassword", zap.Error(err)) } @@ -283,8 +291,8 @@ func (m *DBModel) initUser() (err error) { // 初始化一个用户 user := &User{Username: "admin", Password: "123456"} - groupId := 1 // ID==1的用户组为管理员组 - err = m.CreateUser(user, int64(groupId)) + var groupId int64 = 1 // ID==1的用户组为管理员组 + err = m.CreateUser(user, groupId) if err != nil { m.logger.Error("initUser", zap.Error(err)) } @@ -318,3 +326,66 @@ func (m *DBModel) GetUserPermissinsByUserId(userId int64) (permissions []*Permis err = nil return } + +// SetUserGroupAndPassword 设置用户组和密码 +func (m *DBModel) SetUserGroupAndPassword(userId int64, groupId []int64, password ...string) (err error) { + tx := m.db.Begin() + defer func() { + if err != nil { + tx.Rollback() + } else { + tx.Commit() + } + }() + + var ( + existUsersGroups []UserGroup + userGroups []UserGroup + ) + + tx.Where("user_id = ?", userId).Find(&existUsersGroups) + + // 删除旧的关联用户组 + err = tx.Where("user_id = ?", userId).Delete(&UserGroup{}).Error + if err != nil { + m.logger.Error("SetUserGroupAndPassword", zap.Error(err)) + return + } + + // 设置用户组统计数据 + for _, existUsersGroup := range existUsersGroups { + err = tx.Model(&Group{}).Where("id = ?", existUsersGroup.GroupId).Update("user_count", gorm.Expr("user_count - ?", 1)).Error + if err != nil { + m.logger.Error("SetUserGroupAndPassword", zap.Error(err)) + return + } + } + + // 设置用户组统计数据 + for _, groupId := range groupId { + userGroups = append(userGroups, UserGroup{UserId: userId, GroupId: groupId}) + err = tx.Model(&Group{}).Where("id = ?", groupId).Update("user_count", gorm.Expr("user_count + ?", 1)).Error + if err != nil { + m.logger.Error("SetUserGroupAndPassword", zap.Error(err)) + return + } + } + + if len(userGroups) > 0 { + err = tx.Create(&userGroups).Error + if err != nil { + m.logger.Error("SetUserGroupAndPassword", zap.Error(err)) + return + } + } + + if len(password) > 0 { + err = m.UpdateUserPassword(userId, password[0], tx) + if err != nil { + m.logger.Error("UpdateUserPassword", zap.Error(err)) + return + } + } + + return +} diff --git a/model/userGroup.go b/model/userGroup.go index c9851d0..463ef4a 100644 --- a/model/userGroup.go +++ b/model/userGroup.go @@ -1,8 +1,6 @@ package model import ( - "fmt" - "strings" "time" "go.uber.org/zap" @@ -73,47 +71,18 @@ func (m *DBModel) GetUserGroup(id interface{}, fields ...string) (userGroup User type OptionGetUserGroupList struct { Page int Size int - WithCount bool // 是否返回总数 - Ids []interface{} // 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,...} + WithCount bool // 是否返回总数 + Ids []interface{} // id列表 + SelectFields []string // 查询字段 + QueryIn map[string][]interface{} // map[field][]{value1,value2,...} Sort []string } // GetUserGroupList 获取UserGroup列表 -func (m *DBModel) GetUserGroupList(opt OptionGetUserGroupList) (userGroupList []UserGroup, total int64, err error) { +func (m *DBModel) GetUserGroupList(opt *OptionGetUserGroupList) (userGroupList []UserGroup, total int64, err error) { db := m.db.Model(&UserGroup{}) - - for field, rangeValue := range opt.QueryRange { - fields := m.FilterValidFields(UserGroup{}.TableName(), field) - if len(fields) == 0 { - continue - } - if rangeValue[0] != nil { - db = db.Where(fmt.Sprintf("%s >= ?", field), rangeValue[0]) - } - if rangeValue[1] != nil { - db = db.Where(fmt.Sprintf("%s <= ?", field), rangeValue[1]) - } - } - - for field, values := range opt.QueryIn { - fields := m.FilterValidFields(UserGroup{}.TableName(), field) - if len(fields) == 0 { - continue - } - db = db.Where(fmt.Sprintf("%s in (?)", field), values) - } - - for field, values := range opt.QueryLike { - fields := m.FilterValidFields(UserGroup{}.TableName(), field) - if len(fields) == 0 { - continue - } - db = db.Where(strings.TrimSuffix(fmt.Sprintf(strings.Join(make([]string, len(values)+1), "%s like ? or"), field), "or"), values...) - } + tableName := UserGroup{}.TableName() + db = m.generateQueryIn(db, tableName, opt.QueryIn) if len(opt.Ids) > 0 { db = db.Where("id in (?)", opt.Ids) @@ -132,27 +101,6 @@ func (m *DBModel) GetUserGroupList(opt OptionGetUserGroupList) (userGroupList [] db = db.Select(opt.SelectFields) } - if len(opt.Sort) > 0 { - var sorts []string - for _, sort := range opt.Sort { - slice := strings.Split(sort, " ") - if len(m.FilterValidFields(UserGroup{}.TableName(), slice[0])) == 0 { - continue - } - - if len(slice) == 2 { - sorts = append(sorts, fmt.Sprintf("%s %s", slice[0], slice[1])) - } else { - sorts = append(sorts, fmt.Sprintf("%s desc", slice[0])) - } - } - if len(sorts) > 0 { - db = db.Order(strings.Join(sorts, ",")) - } - } - - db = db.Offset((opt.Page - 1) * opt.Size).Limit(opt.Size) - err = db.Find(&userGroupList).Error if err != nil && err != gorm.ErrRecordNotFound { m.logger.Error("GetUserGroupList", zap.Error(err)) diff --git a/web/api/user.js b/web/api/user.js index 44e3b35..b28a4a3 100644 --- a/web/api/user.js +++ b/web/api/user.js @@ -40,9 +40,9 @@ export const updateUserPassword = (data) => { }) } -export const updateUser = (data) => { +export const updateUserProfile = (data) => { return service({ - url: '/api/v1/user', + url: '/api/v1/user/profile', method: 'put', data, }) @@ -56,6 +56,22 @@ export const deleteUser = (params) => { }) } +export const addUser = (data) => { + return service({ + url: '/api/v1/user', + method: 'post', + data, + }) +} + +export const setUser = (data) => { + return service({ + url: '/api/v1/user', + method: 'put', + data, + }) +} + export const listUser = (params) => { return service({ url: '/api/v1/user/list', diff --git a/web/components/FormConfig.vue b/web/components/FormConfig.vue index 3d6cfe8..96829f0 100644 --- a/web/components/FormConfig.vue +++ b/web/components/FormConfig.vue @@ -58,7 +58,6 @@ +
+ + + + + + + + + + + + + + 提交 + + +
+ + + diff --git a/web/components/TableList.vue b/web/components/TableList.vue index fd22868..669c74c 100644 --- a/web/components/TableList.vue +++ b/web/components/TableList.vue @@ -80,6 +80,7 @@ fixed="right" label="操作" :min-width="actionsMinWidth" + class="com-table-list-actions" > @@ -47,8 +71,9 @@ import { listGroup } from '~/api/group' import { userStatusOptions } from '~/utils/enum' import TableList from '~/components/TableList.vue' import FormSearch from '~/components/FormSearch.vue' +import FormUser from '~/components/FormUser.vue' export default { - components: { TableList, FormSearch }, + components: { TableList, FormSearch, FormUser }, layout: 'admin', data() { return { @@ -61,8 +86,10 @@ export default { group_id: [], size: 10, }, + formUserVisible: false, groups: [], users: [], + user: { id: 0 }, total: 100, searchFormFields: [], listFields: [], @@ -109,7 +136,21 @@ export default { this.listUser() }, onCreate() { - console.log('onCreate') + this.formUserVisible = true + this.$nextTick(() => { + this.$refs.formUser.reset() + }) + }, + setUser(row) { + this.formUserVisible = true + this.$nextTick(() => { + this.$refs.formUser.reset() + this.user = { ...row } + }) + }, + success() { + this.formUserVisible = false + this.listUser() }, batchDelete() { console.log('batchDelete') diff --git a/web/pages/admin/user/permission.vue b/web/pages/admin/user/permission.vue index 0baec3f..7461c27 100644 --- a/web/pages/admin/user/permission.vue +++ b/web/pages/admin/user/permission.vue @@ -19,7 +19,7 @@ :show-edit="true" :show-delete="false" :show-select="false" - :actions-min-width="70" + :actions-min-width="80" @editRow="editRow" /> diff --git a/web/store/module/user.js b/web/store/module/user.js index 9d9388f..4d9d484 100644 --- a/web/store/module/user.js +++ b/web/store/module/user.js @@ -1,5 +1,5 @@ import { Message } from 'element-ui' -import { login, getUser, updateUser, logout } from '~/api/user' +import { login, getUser, updateUserProfile, logout } from '~/api/user' export const user = { namespaced: true, state: { @@ -40,8 +40,8 @@ export const user = { } return res }, - async updateUser({ commit }, profile) { - const res = await updateUser(profile) + async updateUserProfile({ commit }, profile) { + const res = await updateUserProfile(profile) if (res.status === 200) { commit('mergeUser', profile) } else { diff --git a/web/utils/permission.js b/web/utils/permission.js index 2df77ee..417eab0 100644 --- a/web/utils/permission.js +++ b/web/utils/permission.js @@ -30,6 +30,11 @@ const cumstomPermissionMap = { path: 'ListPermission', children: [], }, + 'api.v1.ConfigAPI': { + label: '系统设置', + path: 'ListConfig', + children: [], + }, upload: { id: 0, label: '上传管理', @@ -55,7 +60,7 @@ export const permissionsToTree = (permissions) => { } permissionMap[slice[1]].children.push({ ...permission, - label: permission.title, + label: permission.title || permission.path, }) } else { if (!permissionMap[slice[3]]) { @@ -64,7 +69,10 @@ export const permissionsToTree = (permissions) => { label: slice[3], } } - permissionMap[slice[3]].children.push(permission) + permissionMap[slice[3]].children.push({ + ...permission, + label: permission.title || permission.path, + }) } }) Object.keys(permissionMap).forEach((key) => {