parent
0cd4a8102f
commit
a91d1606a6
@ -0,0 +1,78 @@
|
||||
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";
|
||||
|
||||
message Friendlink {
|
||||
int32 id = 1;
|
||||
string title = 2;
|
||||
string link = 3;
|
||||
string description = 4;
|
||||
int32 sort = 5;
|
||||
int32 status = 6;
|
||||
google.protobuf.Timestamp created_at = 7 [ (gogoproto.stdtime) = true ];
|
||||
google.protobuf.Timestamp updated_at = 8 [ (gogoproto.stdtime) = true ];
|
||||
}
|
||||
|
||||
message DeleteFriendlinkRequest {
|
||||
repeated int64 id = 1;
|
||||
}
|
||||
|
||||
message GetFriendlinkRequest {
|
||||
int64 id = 1;
|
||||
}
|
||||
|
||||
message ListFriendlinkRequest {
|
||||
int32 page = 1;
|
||||
int32 size = 2;
|
||||
string wd = 3;
|
||||
repeated int32 status = 4;
|
||||
}
|
||||
|
||||
message ListFriendlinkReply {
|
||||
repeated Friendlink friendlink = 1;
|
||||
int64 total = 2;
|
||||
}
|
||||
|
||||
service FriendlinkAPI{
|
||||
rpc CreateFriendlink (Friendlink) returns (Friendlink){
|
||||
option (google.api.http) = {
|
||||
post: '/api/v1/friendlink',
|
||||
body: '*',
|
||||
};
|
||||
}
|
||||
|
||||
rpc UpdateFriendlink (Friendlink) returns (Friendlink){
|
||||
option (google.api.http) = {
|
||||
put: '/api/v1/friendlink',
|
||||
body: '*',
|
||||
};
|
||||
}
|
||||
|
||||
rpc DeleteFriendlink (DeleteFriendlinkRequest) returns (google.protobuf.Empty){
|
||||
option (google.api.http) = {
|
||||
delete: '/api/v1/friendlink',
|
||||
};
|
||||
}
|
||||
|
||||
rpc GetFriendlink (GetFriendlinkRequest) returns (Friendlink){
|
||||
option (google.api.http) = {
|
||||
get: '/api/v1/friendlink',
|
||||
};
|
||||
}
|
||||
|
||||
rpc ListFriendlink (ListFriendlinkRequest) returns (ListFriendlinkReply){
|
||||
option (google.api.http) = {
|
||||
get: '/api/v1/friendlink/list',
|
||||
};
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package biz
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
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"
|
||||
)
|
||||
|
||||
type FriendlinkAPIService struct {
|
||||
pb.UnimplementedFriendlinkAPIServer
|
||||
dbModel *model.DBModel
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
func NewFriendlinkAPIService(dbModel *model.DBModel, logger *zap.Logger) (service *FriendlinkAPIService) {
|
||||
return &FriendlinkAPIService{dbModel: dbModel, logger: logger.Named("FriendlinkAPIService")}
|
||||
}
|
||||
|
||||
// checkPermission 检查用户权限
|
||||
func (s *FriendlinkAPIService) checkPermission(ctx context.Context) (userClaims *auth.UserClaims, err error) {
|
||||
var ok bool
|
||||
userClaims, ok = ctx.Value(auth.CtxKeyUserClaims).(*auth.UserClaims)
|
||||
if !ok {
|
||||
return nil, status.Errorf(codes.Unauthenticated, ErrorMessageInvalidToken)
|
||||
}
|
||||
|
||||
fullMethod, _ := ctx.Value(auth.CtxKeyFullMethod).(string)
|
||||
if yes := s.dbModel.CheckPermissionByUserId(userClaims.UserId, fullMethod); !yes {
|
||||
return nil, status.Errorf(codes.PermissionDenied, ErrorMessagePermissionDenied)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// CreateFriendlink 创建友情链接,需要鉴权
|
||||
func (s *FriendlinkAPIService) CreateFriendlink(ctx context.Context, req *pb.Friendlink) (*pb.Friendlink, error) {
|
||||
s.logger.Debug("CreateFriendlink", zap.Any("req", req))
|
||||
_, err := s.checkPermission(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
friendlink := &model.Friendlink{}
|
||||
util.CopyStruct(req, friendlink)
|
||||
err = s.dbModel.CreateFriendlink(friendlink)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
pbFriendlink := &pb.Friendlink{}
|
||||
util.CopyStruct(friendlink, pbFriendlink)
|
||||
return pbFriendlink, nil
|
||||
}
|
||||
|
||||
// UpdateFriendlink 更新友情链接,需要鉴权
|
||||
func (s *FriendlinkAPIService) UpdateFriendlink(ctx context.Context, req *pb.Friendlink) (*pb.Friendlink, error) {
|
||||
_, err := s.checkPermission(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if req.Id <= 0 {
|
||||
return nil, status.Errorf(codes.InvalidArgument, "参数不正确")
|
||||
}
|
||||
|
||||
friendlink := &model.Friendlink{}
|
||||
util.CopyStruct(req, friendlink)
|
||||
err = s.dbModel.UpdateFriendlink(friendlink)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
pbFriendlink := &pb.Friendlink{}
|
||||
util.CopyStruct(friendlink, pbFriendlink)
|
||||
return pbFriendlink, nil
|
||||
}
|
||||
|
||||
// DeleteFriendlink 删除友情链接,需要鉴权
|
||||
func (s *FriendlinkAPIService) DeleteFriendlink(ctx context.Context, req *pb.DeleteFriendlinkRequest) (*emptypb.Empty, error) {
|
||||
_, err := s.checkPermission(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = s.dbModel.DeleteFriendlink(req.Id)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
return &emptypb.Empty{}, nil
|
||||
}
|
||||
|
||||
// GetFriendlink 查询友情链接
|
||||
func (s *FriendlinkAPIService) GetFriendlink(ctx context.Context, req *pb.GetFriendlinkRequest) (*pb.Friendlink, error) {
|
||||
var fields []string
|
||||
_, err := s.checkPermission(ctx)
|
||||
if err != nil {
|
||||
fields = s.dbModel.GetFriendlinkPublicFields() // 非管理员可查询的字段
|
||||
}
|
||||
|
||||
friendlink, err := s.dbModel.GetFriendlink(req.Id, fields...)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
pbFriendlink := &pb.Friendlink{}
|
||||
util.CopyStruct(friendlink, pbFriendlink)
|
||||
return pbFriendlink, nil
|
||||
}
|
||||
|
||||
func (s *FriendlinkAPIService) ListFriendlink(ctx context.Context, req *pb.ListFriendlinkRequest) (*pb.ListFriendlinkReply, error) {
|
||||
opt := &model.OptionGetFriendlinkList{
|
||||
WithCount: true,
|
||||
}
|
||||
|
||||
_, err := s.checkPermission(ctx)
|
||||
if err == nil {
|
||||
// 管理员可使用like查询
|
||||
if req.Wd != "" {
|
||||
wd := "%" + req.Wd + "%"
|
||||
opt.QueryLike = map[string][]interface{}{
|
||||
"title": {wd},
|
||||
"description": {wd},
|
||||
}
|
||||
}
|
||||
// 管理员可查询指定状态的友链
|
||||
if len(req.Status) > 0 {
|
||||
opt.QueryIn = map[string][]interface{}{"status": {req.Status}}
|
||||
}
|
||||
} else {
|
||||
// 非管理员可查询的字段
|
||||
opt.SelectFields = s.dbModel.GetFriendlinkPublicFields()
|
||||
opt.QueryIn = map[string][]interface{}{"status": {model.FriendlinkStatusNormal}}
|
||||
}
|
||||
|
||||
friendlink, total, err := s.dbModel.GetFriendlinkList(opt)
|
||||
if err != nil {
|
||||
return nil, status.Errorf(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
var pbFriendlink []*pb.Friendlink
|
||||
util.CopyStruct(friendlink, &pbFriendlink)
|
||||
|
||||
return &pb.ListFriendlinkReply{Friendlink: pbFriendlink, Total: total}, nil
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
import service from '~/utils/request'
|
||||
|
||||
export const createFriendlink = (data) => {
|
||||
return service({
|
||||
url: '/api/v1/friendlink',
|
||||
method: 'post',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export const updateFriendlink = (data) => {
|
||||
return service({
|
||||
url: '/api/v1/friendlink',
|
||||
method: 'put',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteFriendlink = (params) => {
|
||||
return service({
|
||||
url: '/api/v1/friendlink',
|
||||
method: 'delete',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
export const getFriendlink = (params) => {
|
||||
return service({
|
||||
url: '/api/v1/friendlink',
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
export const listFriendlink = (params) => {
|
||||
return service({
|
||||
url: '/api/v1/friendlink/list',
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,16 +1,152 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>{{ $route.name }}</div>
|
||||
<el-card shadow="never" class="search-card">
|
||||
<FormSearch
|
||||
:fields="searchFormFields"
|
||||
:loading="loading"
|
||||
:show-create="true"
|
||||
:show-delete="true"
|
||||
:disabled-delete="selectedRow.length === 0"
|
||||
@onCreate="onCreate"
|
||||
/>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="mgt-20px">
|
||||
<TableList
|
||||
:table-data="groups"
|
||||
:fields="tableListFields"
|
||||
:show-actions="true"
|
||||
:show-view="false"
|
||||
:show-edit="true"
|
||||
:show-delete="true"
|
||||
:show-select="true"
|
||||
@selectRow="selectRow"
|
||||
/>
|
||||
</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, 200]"
|
||||
: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
|
||||
:title="group.id ? '编辑分组' : '新增分组'"
|
||||
:init-group="group"
|
||||
:visible.sync="formGroupVisible"
|
||||
>
|
||||
<FormGroup />
|
||||
</el-dialog> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listFriendlink } from '~/api/friendlink'
|
||||
import TableList from '~/components/TableList.vue'
|
||||
import FormSearch from '~/components/FormSearch.vue'
|
||||
export default {
|
||||
components: { TableList, FormSearch },
|
||||
layout: 'admin',
|
||||
head() {
|
||||
data() {
|
||||
return {
|
||||
title: `面板 - MOREDOC · 魔刀文库`,
|
||||
loading: false,
|
||||
formGroupVisible: false,
|
||||
search: {
|
||||
wd: '',
|
||||
page: 1,
|
||||
status: [],
|
||||
group_id: [],
|
||||
size: 10,
|
||||
},
|
||||
groups: [],
|
||||
total: 0,
|
||||
searchFormFields: [],
|
||||
tableListFields: [],
|
||||
selectedRow: [],
|
||||
group: {},
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.initSearchForm()
|
||||
this.initTableListFields()
|
||||
await this.listFriendlink()
|
||||
},
|
||||
methods: {
|
||||
async listFriendlink() {
|
||||
this.loading = true
|
||||
const res = await listFriendlink(this.search)
|
||||
if (res.status === 200) {
|
||||
this.groups = res.data.group
|
||||
this.total = res.data.total
|
||||
} else {
|
||||
this.$message.error(res.data.message)
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.search.size = val
|
||||
this.listFriendlink()
|
||||
},
|
||||
handlePageChange(val) {
|
||||
this.search.page = val
|
||||
this.listFriendlink()
|
||||
},
|
||||
onSearch() {
|
||||
this.search.page = 1
|
||||
this.listFriendlink()
|
||||
},
|
||||
onCreate() {
|
||||
this.formGroupVisible = true
|
||||
},
|
||||
setGroup() {
|
||||
this.formGroupVisible = false
|
||||
},
|
||||
batchDelete() {
|
||||
console.log('batchDelete')
|
||||
},
|
||||
selectRow(rows) {
|
||||
this.selectedRow = rows
|
||||
},
|
||||
initSearchForm() {
|
||||
this.searchFormFields = [
|
||||
{
|
||||
type: 'text',
|
||||
label: '关键字',
|
||||
name: 'wd',
|
||||
placeholder: '请输入关键字',
|
||||
},
|
||||
]
|
||||
},
|
||||
initTableListFields() {
|
||||
this.tableListFields = [
|
||||
{ prop: 'id', label: 'ID', width: 80, type: 'number', fixed: 'left' },
|
||||
{
|
||||
prop: 'icon',
|
||||
label: '图标',
|
||||
width: 80,
|
||||
type: 'avatar',
|
||||
fixed: 'left',
|
||||
},
|
||||
{ prop: 'title', label: '名称', width: 150, fixed: 'left' },
|
||||
{ prop: 'sort', label: '排序', width: 80, type: 'number' },
|
||||
{ prop: 'user_count', label: '用户数', width: 80, type: 'number' },
|
||||
{ prop: 'color', label: '颜色', width: 120, type: 'color' },
|
||||
{ prop: 'is_default', label: '是否默认', width: 80, type: 'bool' },
|
||||
{ prop: 'is_display', label: '是否展示', width: 80, type: 'bool' },
|
||||
{ prop: 'description', label: '描述', width: 250 },
|
||||
{ prop: 'created_at', label: '创建时间', width: 160, type: 'datetime' },
|
||||
{ prop: 'updated_at', label: '更新时间', width: 160, type: 'datetime' },
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style></style>
|
||||
|
Loading…
Reference in new issue