代码调整

dev
truthhun 2 years ago
parent fff770a0e1
commit cef49c086c

@ -0,0 +1,159 @@
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 User {
int64 id = 1;
string username = 2;
string password = 3;
string nickname = 4;
string mobile = 5;
string email = 6;
string address = 7;
string signature = 8;
string last_login_ip = 9;
string register_ip = 10;
int32 doc_count = 11;
int32 follow_count = 12;
int32 fans_count = 13;
int32 favorite_count = 14;
int32 comment_count = 15;
int32 status = 16;
string avatar = 17;
string identity = 18;
string realname = 19;
google.protobuf.Timestamp login_at = 20 [ (gogoproto.stdtime) = true ];
google.protobuf.Timestamp created_at = 21 [ (gogoproto.stdtime) = true ];
google.protobuf.Timestamp updated_at = 22 [ (gogoproto.stdtime) = true ];
}
message RegisterAndLoginRequest {
string username = 1 [(gogoproto.moretags) = "validate:\"min=4,max=32,alphanum\""];
string password = 2 [(gogoproto.moretags) = "validate:\"min=6\""];
string captcha = 3;
}
message LoginReply { string token = 1; }
message UpdateUserRequest {
int64 id = 1;
string name = 2;
}
message UpdateUserReply { User user = 1; }
message DeleteUserRequest { repeated int64 id = 1; }
message GetUserRequest { int64 id = 1; }
message ListUserRequest {
int64 page = 1;
int64 size = 2;
repeated int64 ids = 3;
}
message ListUserReply {
int64 total = 1;
repeated User user = 2;
}
//
message UpdateUserPasswordRequest {
int64 id = 1;
string old_password = 2;
string new_password = 3;
}
service UserAPI {
//
rpc Register(RegisterAndLoginRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
post : '/api/v1/user/register',
body : '*',
};
}
//
rpc Login(RegisterAndLoginRequest) returns (LoginReply) {
option (google.api.http) = {
post : '/api/v1/user/login',
body : '*',
};
}
// Id
rpc GetUser(GetUserRequest) returns (User) {
option (google.api.http) = {
get : '/api/v1/user',
};
}
// ID
// 穿ID
rpc UpdateUserPassword(UpdateUserRequest) returns (User) {
option (google.api.http) = {
put : '/api/v1/user/password',
body : '*',
};
}
//
rpc DeleteUser(DeleteUserRequest) returns (google.protobuf.Empty) {
option (google.api.http) = {
delete : '/api/v1/user',
};
}
//
//
rpc ListUser(ListUserRequest) returns (ListUserReply) {
option (google.api.http) = {
get : '/api/v1/user/list',
};
}
//
// rpc ListUserFans(ListUserFansRequest) returns (ListUserReply) {
// option (google.api.http) = {
// get : '/api/v1/user/fans',
// };
// }
// //
// rpc ListUserFollow(ListUserFollowRequest) returns (ListUserReply) {
// option (google.api.http) = {
// get : '/api/v1/user/follow',
// };
// }
// //
// rpc ListUserFavorite(ListUserFavoriteRequest) returns (ListUserReply) {
// option (google.api.http) = {
// get : '/api/v1/user/favorite',
// };
// }
// //
// rpc ListUserDocument(ListUserDocumentRequest) returns (ListUserReply) {
// option (google.api.http) = {
// get : '/api/v1/user/document',
// };
// }
// //
// rpc ListUserComment(ListUserCommentRequest) returns (ListUserReply) {
// option (google.api.http) = {
// get : '/api/v1/user/comment',
// };
// }
}

@ -0,0 +1,77 @@
package biz
import (
"context"
pb "moredoc/api/v1"
"moredoc/model"
"moredoc/util/validate"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/emptypb"
)
type UserAPIService struct {
pb.UnimplementedUserAPIServer
dbModel *model.DBModel
logger *zap.Logger
}
func NewUserAPIService(dbModel *model.DBModel, logger *zap.Logger) (service *UserAPIService) {
return &UserAPIService{dbModel: dbModel, logger: logger.Named("UserAPIService")}
}
func (s *UserAPIService) getValidFieldMap() map[string]string {
return map[string]string{"Username": "用户名", "Password": "密码"}
}
// Register 用户注册
// TODO: 1. 判断系统是否启用了注册
// TODO: 2. 如果系统启用了注册,判断是否需要管理员审核
// TODO: 3. 如果启用了验证码功能,则需要判断验证码是否正确
func (s *UserAPIService) Register(ctx context.Context, req *pb.RegisterAndLoginRequest) (*emptypb.Empty, error) {
err := validate.ValidateStruct(req, s.getValidFieldMap())
if err != nil {
return nil, status.Errorf(codes.InvalidArgument, err.Error())
}
exist, _ := s.dbModel.GetUserByUsername(req.Username, "id")
if exist.Id > 0 {
return nil, status.Errorf(codes.AlreadyExists, "用户名已存在")
}
user := &model.User{Username: req.Username, Password: req.Password}
if err = s.dbModel.CreateUser(user); err != nil {
s.logger.Error("CreateUser", zap.Error(err))
return nil, status.Errorf(codes.Internal, err.Error())
}
return &emptypb.Empty{}, nil
}
func (s *UserAPIService) Login(ctx context.Context, req *pb.RegisterAndLoginRequest) (*pb.LoginReply, error) {
errValidate := validate.ValidateStruct(req, s.getValidFieldMap())
if errValidate != nil {
return nil, status.Errorf(codes.InvalidArgument, errValidate.Error())
}
return &pb.LoginReply{}, nil
}
func (s *UserAPIService) GetUser(ctx context.Context, req *pb.GetUserRequest) (*pb.User, error) {
return &pb.User{}, nil
}
func (s *UserAPIService) UpdateUserPassword(ctx context.Context, req *pb.UpdateUserRequest) (*pb.User, error) {
return &pb.User{}, nil
}
func (s *UserAPIService) DeleteUser(ctx context.Context, req *pb.DeleteUserRequest) (*emptypb.Empty, error) {
return &emptypb.Empty{}, nil
}
func (s *UserAPIService) ListUser(ctx context.Context, req *pb.ListUserRequest) (*pb.ListUserReply, error) {
return &pb.ListUserReply{}, nil
}

@ -9,6 +9,21 @@
- [HealthAPI](#-HealthAPI)
- [api/v1/user.proto](#api_v1_user-proto)
- [DeleteUserRequest](#api-v1-DeleteUserRequest)
- [GetUserRequest](#api-v1-GetUserRequest)
- [ListUserReply](#api-v1-ListUserReply)
- [ListUserRequest](#api-v1-ListUserRequest)
- [LoginReply](#api-v1-LoginReply)
- [LoginRequest](#api-v1-LoginRequest)
- [RegisterRequest](#api-v1-RegisterRequest)
- [UpdateUserPasswordRequest](#api-v1-UpdateUserPasswordRequest)
- [UpdateUserReply](#api-v1-UpdateUserReply)
- [UpdateUserRequest](#api-v1-UpdateUserRequest)
- [User](#api-v1-User)
- [UserAPI](#api-v1-UserAPI)
- [Scalar Value Types](#scalar-value-types)
@ -71,6 +86,232 @@
<a name="api_v1_user-proto"></a>
<p align="right"><a href="#top">Top</a></p>
## api/v1/user.proto
<a name="api-v1-DeleteUserRequest"></a>
### DeleteUserRequest
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| id | [int64](#int64) | repeated | |
<a name="api-v1-GetUserRequest"></a>
### GetUserRequest
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| id | [int64](#int64) | | |
<a name="api-v1-ListUserReply"></a>
### ListUserReply
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| total | [int64](#int64) | | |
| user | [User](#api-v1-User) | repeated | |
<a name="api-v1-ListUserRequest"></a>
### ListUserRequest
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| page | [int64](#int64) | | |
| size | [int64](#int64) | | |
| ids | [int64](#int64) | repeated | |
<a name="api-v1-LoginReply"></a>
### LoginReply
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| token | [string](#string) | | |
<a name="api-v1-LoginRequest"></a>
### LoginRequest
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| username | [string](#string) | | |
| password | [string](#string) | | |
| captcha | [string](#string) | | |
<a name="api-v1-RegisterRequest"></a>
### RegisterRequest
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| username | [string](#string) | | |
| password | [string](#string) | | |
<a name="api-v1-UpdateUserPasswordRequest"></a>
### UpdateUserPasswordRequest
修改用户密码
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| id | [int64](#int64) | | |
| old_password | [string](#string) | | |
| new_password | [string](#string) | | |
<a name="api-v1-UpdateUserReply"></a>
### UpdateUserReply
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| user | [User](#api-v1-User) | | |
<a name="api-v1-UpdateUserRequest"></a>
### UpdateUserRequest
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| id | [int64](#int64) | | |
| name | [string](#string) | | |
<a name="api-v1-User"></a>
### User
| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| id | [int64](#int64) | | |
| username | [string](#string) | | |
| password | [string](#string) | | |
| nickname | [string](#string) | | |
| mobile | [string](#string) | | |
| email | [string](#string) | | |
| address | [string](#string) | | |
| signature | [string](#string) | | |
| last_login_ip | [string](#string) | | |
| register_ip | [string](#string) | | |
| doc_count | [int32](#int32) | | |
| follow_count | [int32](#int32) | | |
| fans_count | [int32](#int32) | | |
| favorite_count | [int32](#int32) | | |
| comment_count | [int32](#int32) | | |
| status | [int32](#int32) | | |
| 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) | | |
<a name="api-v1-UserAPI"></a>
### UserAPI
| 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) | 用户登录 |
| GetUser | [GetUserRequest](#api-v1-GetUserRequest) | [User](#api-v1-User) | 查询用户信息。如果传递了Id参数则表示查询用户的公开信息否则查询当前用户的私有信息 |
| UpdateUserPassword | [UpdateUserRequest](#api-v1-UpdateUserRequest) | [User](#api-v1-User) | 更新用户密码。如果不传用户ID则表示更新当前用户的密码 如果穿了用户ID则表示更新指定用户的密码这时需要验证当前用户的权限 |
| DeleteUser | [DeleteUserRequest](#api-v1-DeleteUserRequest) | [.google.protobuf.Empty](#google-protobuf-Empty) | 删除用户。需要验证用户权限 |
| ListUser | [ListUserRequest](#api-v1-ListUserRequest) | [ListUserReply](#api-v1-ListUserReply) | 查询用户列表。对于非管理员,返回相应用户的公开信息; 对于管理员,返回相应用户的绝大部分信息 |
## Scalar Value Types
| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby |

@ -3,7 +3,7 @@
openapi: 3.0.3
info:
title: HealthAPI API
title: ""
version: 0.0.1
paths:
/api/v1/ping:
@ -29,6 +29,167 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Status'
/api/v1/user:
get:
tags:
- UserAPI
description: 查询用户信息。如果传递了Id参数则表示查询用户的公开信息否则查询当前用户的私有信息
operationId: UserAPI_GetUser
parameters:
- name: id
in: query
schema:
type: integer
format: int64
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/User'
default:
description: Default error response
content:
application/json:
schema:
$ref: '#/components/schemas/Status'
delete:
tags:
- UserAPI
description: 删除用户。需要验证用户权限
operationId: UserAPI_DeleteUser
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/user/list:
get:
tags:
- UserAPI
description: |-
查询用户列表。对于非管理员,返回相应用户的公开信息;
对于管理员,返回相应用户的绝大部分信息
operationId: UserAPI_ListUser
parameters:
- name: page
in: query
schema:
type: integer
format: int64
- name: size
in: query
schema:
type: integer
format: int64
- name: ids
in: query
schema:
type: array
items:
type: integer
format: int64
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/ListUserReply'
default:
description: Default error response
content:
application/json:
schema:
$ref: '#/components/schemas/Status'
/api/v1/user/login:
post:
tags:
- UserAPI
description: 用户登录
operationId: UserAPI_Login
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
required: true
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/LoginReply'
default:
description: Default error response
content:
application/json:
schema:
$ref: '#/components/schemas/Status'
/api/v1/user/password:
put:
tags:
- UserAPI
description: |-
更新用户密码。如果不传用户ID则表示更新当前用户的密码
如果穿了用户ID则表示更新指定用户的密码这时需要验证当前用户的权限
operationId: UserAPI_UpdateUserPassword
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateUserRequest'
required: true
responses:
"200":
description: OK
content:
application/json:
schema:
$ref: '#/components/schemas/User'
default:
description: Default error response
content:
application/json:
schema:
$ref: '#/components/schemas/Status'
/api/v1/user/register:
post:
tags:
- UserAPI
description: 用户注册
operationId: UserAPI_Register
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/RegisterRequest'
required: true
responses:
"200":
description: OK
content: {}
default:
description: Default error response
content:
application/json:
schema:
$ref: '#/components/schemas/Status'
/health:
get:
tags:
@ -54,6 +215,30 @@ 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.
ListUserReply:
type: object
properties:
total:
type: integer
format: int64
user:
type: array
items:
$ref: '#/components/schemas/User'
LoginReply:
type: object
properties:
token:
type: string
LoginRequest:
type: object
properties:
username:
type: string
password:
type: string
captcha:
type: string
PongReply:
type: object
properties:
@ -62,6 +247,13 @@ components:
createdAt:
type: string
format: date-time
RegisterRequest:
type: object
properties:
username:
type: string
password:
type: string
Status:
type: object
properties:
@ -78,5 +270,71 @@ 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:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
User:
type: object
properties:
id:
type: integer
format: int64
username:
type: string
password:
type: string
nickname:
type: string
mobile:
type: string
email:
type: string
address:
type: string
signature:
type: string
lastLoginIp:
type: string
registerIp:
type: string
docCount:
type: integer
format: int32
followCount:
type: integer
format: int32
fansCount:
type: integer
format: int32
favoriteCount:
type: integer
format: int32
commentCount:
type: integer
format: int32
status:
type: integer
format: int32
avatar:
type: string
identity:
type: string
realname:
type: string
loginAt:
type: string
format: date-time
createdAt:
type: string
format: date-time
updatedAt:
type: string
format: date-time
tags:
- name: HealthAPI
- name: UserAPI

@ -22,7 +22,10 @@ require (
gorm.io/gorm v1.23.2
)
require google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 // indirect
require (
google.golang.org/genproto v0.0.0-20220228195345-15d65a4533f7 // indirect
gopkg.in/go-playground/validator.v9 v9.29.1 // indirect
)
require (
github.com/alexandrevicenzi/unchained v1.3.0
@ -31,6 +34,7 @@ require (
github.com/gin-contrib/static v0.0.1
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator v9.31.0+incompatible
github.com/go-playground/validator/v10 v10.10.1 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect

@ -10,7 +10,7 @@ import (
)
type Attachment struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"column:id;type:bigint(20) unsigned;default:0;primarykey;autoIncrement;comment:附件 id;"`
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:附件 id;"`
Hash string `form:"hash" json:"hash,omitempty" gorm:"column:hash;type:char(32);size:32;default:;index:hash;comment:文件MD5;"`
UserId int64 `form:"user_id" json:"user_id,omitempty" gorm:"column:user_id;type:bigint(20) unsigned;default:0;index:user_id;comment:用户 id;"`
TypeId int64 `form:"type_id" json:"type_id,omitempty" gorm:"column:type_id;type:bigint(20) unsigned;default:0;comment:类型数据ID对应与用户头像时则为用户id对应为文档时则为文档ID;"`
@ -27,6 +27,25 @@ type Attachment struct {
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message Attachment {
// int64 id = 1;
// string hash = 2;
// int64 user_id = 3;
// int64 type_id = 4;
// int32 type = 5;
// int32 is_approved = 6;
// string path = 7;
// string name = 8;
// int64 size = 9;
// int64 width = 10;
// int64 height = 11;
// string ext = 12;
// string ip = 13;
// google.protobuf.Timestamp created_at = 14 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 15 [ (gogoproto.stdtime) = true ];
//}
func (Attachment) TableName() string {
return tablePrefix + "attachment"
}

@ -10,7 +10,7 @@ import (
)
type Banner struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"column:id;type:bigint(20);size:20;default:0;primarykey;autoIncrement;comment:;"`
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
Title string `form:"title" json:"title,omitempty" gorm:"column:title;type:varchar(255);size:255;default:;comment:横幅名称;"`
Path string `form:"path" json:"path,omitempty" gorm:"column:path;type:varchar(255);size:255;default:;comment:横幅地址;"`
Sort int `form:"sort" json:"sort,omitempty" gorm:"column:sort;type:int(11);size:11;default:0;comment:排序,值越大越靠前;"`
@ -21,6 +21,19 @@ type Banner struct {
Url string `form:"url" json:"url,omitempty" gorm:"column:url;type:varchar(255);size:255;default:;comment:横幅跳转地址;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message Banner {
// int64 id = 1;
// string title = 2;
// string path = 3;
// int32 sort = 4;
// int32 status = 5;
// int32 category = 6;
// google.protobuf.Timestamp created_at = 7 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 8 [ (gogoproto.stdtime) = true ];
// string url = 9;
//}
func (Banner) TableName() string {
return tablePrefix + "banner"
}

@ -10,7 +10,7 @@ import (
)
type Category struct {
Id int `form:"id" json:"id,omitempty" gorm:"column:id;type:int(11);size:11;default:0;primarykey;comment:;"`
Id int `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
ParentId int `form:"parent_id" json:"parent_id,omitempty" gorm:"column:parent_id;type:int(11);size:11;default:0;index:parent_id_title,unique;index:parent_id;comment:上级ID;"`
Title string `form:"title" json:"title,omitempty" gorm:"column:title;type:varchar(64);size:64;default:;index:parent_id_title,unique;comment:分类名称;"`
Cover string `form:"cover" json:"cover,omitempty" gorm:"column:cover;type:varchar(255);size:255;default:;comment:分类封面;"`
@ -22,6 +22,20 @@ type Category struct {
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message Category {
// int32 id = 1;
// int32 parent_id = 2;
// string title = 3;
// string cover = 4;
// int32 doc_count = 5;
// int32 sort = 6;
// string alias = 7;
// int32 status = 8;
// google.protobuf.Timestamp created_at = 9 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 10 [ (gogoproto.stdtime) = true ];
//}
func (Category) TableName() string {
return tablePrefix + "category"
}

@ -1,171 +0,0 @@
package model
import (
"fmt"
"strings"
"time"
"go.uber.org/zap"
"gorm.io/gorm"
)
type Comment struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"column:id;type:bigint(20) unsigned;default:0;primarykey;autoIncrement;comment:回复 id;"`
UserId int64 `form:"user_id" json:"user_id,omitempty" gorm:"column:user_id;type:bigint(20) unsigned;default:0;comment:发表用户 id;"`
DocumentId int64 `form:"document_id" json:"document_id,omitempty" gorm:"column:document_id;type:bigint(20) unsigned;default:0;index:document_id;comment:文档ID;"`
ParentId int64 `form:"parent_id" json:"parent_id,omitempty" gorm:"column:parent_id;type:bigint(20) unsigned;default:0;comment:父级ID上一个评论ID;"`
Content string `form:"content" json:"content,omitempty" gorm:"column:content;type:text;default:;comment:内容;"`
Ip string `form:"ip" json:"ip,omitempty" gorm:"column:ip;type:varchar(16);size:16;default:;comment:ip 地址;"`
ReplyCount int `form:"reply_count" json:"reply_count,omitempty" gorm:"column:reply_count;type:int(10) unsigned;default:0;comment:关联回复数;"`
LikeCount int `form:"like_count" json:"like_count,omitempty" gorm:"column:like_count;type:int(10) unsigned;default:0;comment:喜欢数;"`
CreatedAt time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;default:;comment:创建时间;"`
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
DeletedAt gorm.DeletedAt
IsFirst int8 `form:"is_first" json:"is_first,omitempty" gorm:"column:is_first;type:tinyint(3) unsigned;default:0;comment:是否首个回复;"`
IsComment int8 `form:"is_comment" json:"is_comment,omitempty" gorm:"column:is_comment;type:tinyint(3) unsigned;default:0;comment:是否是回复回帖的内容;"`
IsApproved int8 `form:"is_approved" json:"is_approved,omitempty" gorm:"column:is_approved;type:tinyint(3) unsigned;default:1;comment:是否合法;"`
}
func (Comment) TableName() string {
return tablePrefix + "comment"
}
// CreateComment 创建Comment
// TODO: 创建成功之后,注意相关表统计字段数值的增减
func (m *DBModel) CreateComment(comment *Comment) (err error) {
err = m.db.Create(comment).Error
if err != nil {
m.logger.Error("CreateComment", zap.Error(err))
return
}
return
}
// UpdateComment 更新Comment如果需要更新指定字段则请指定updateFields参数
func (m *DBModel) UpdateComment(comment *Comment, updateFields ...string) (err error) {
db := m.db.Model(comment)
updateFields = m.FilterValidFields(Comment{}.TableName(), updateFields...)
if len(updateFields) > 0 { // 更新指定字段
db = db.Select(updateFields)
}
err = db.Where("id = ?", comment.Id).Updates(comment).Error
if err != nil {
m.logger.Error("UpdateComment", zap.Error(err))
}
return
}
// GetComment 根据id获取Comment
func (m *DBModel) GetComment(id interface{}, fields ...string) (comment Comment, err error) {
db := m.db
fields = m.FilterValidFields(Comment{}.TableName(), fields...)
if len(fields) > 0 {
db = db.Select(fields)
}
err = db.Where("id = ?", id).First(&comment).Error
return
}
type OptionGetCommentList 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,...}
Sort []string
}
// GetCommentList 获取Comment列表
func (m *DBModel) GetCommentList(opt OptionGetCommentList) (commentList []Comment, total int64, err error) {
db := m.db.Model(&Comment{})
for field, rangeValue := range opt.QueryRange {
fields := m.FilterValidFields(Comment{}.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(Comment{}.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(Comment{}.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...)
}
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("GetCommentList", zap.Error(err))
return
}
}
opt.SelectFields = m.FilterValidFields(Comment{}.TableName(), opt.SelectFields...)
if len(opt.SelectFields) > 0 {
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(Comment{}.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(&commentList).Error
if err != nil && err != gorm.ErrRecordNotFound {
m.logger.Error("GetCommentList", zap.Error(err))
}
return
}
// DeleteComment 删除数据
// TODO: 删除数据之后,存在 comment_id 的关联表,需要删除对应数据,同时相关表的统计数值,也要随着减少
func (m *DBModel) DeleteComment(ids []interface{}) (err error) {
err = m.db.Where("id in (?)", ids).Delete(&Comment{}).Error
if err != nil {
m.logger.Error("DeleteComment", zap.Error(err))
}
return
}

@ -10,7 +10,7 @@ import (
)
type Config struct {
Id int `form:"id" json:"id,omitempty" gorm:"column:id;type:int(11);size:11;default:0;primarykey;comment:;"`
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
Label string `form:"label" json:"label,omitempty" gorm:"column:label;type:varchar(64);size:64;default:;comment:标签名称;"`
Name string `form:"name" json:"name,omitempty" gorm:"column:name;type:varchar(64);size:64;default:;index:name_category,unique;comment:表单字段名称;"`
Value string `form:"value" json:"value,omitempty" gorm:"column:value;type:text;default:;comment:值;"`
@ -23,6 +23,21 @@ type Config struct {
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message Config {
// int64 id = 1;
// string label = 2;
// string name = 3;
// string value = 4;
// int32 placeholder = 5;
// int32 input_type = 6;
// string category = 7;
// int32 sort = 8;
// string options = 9;
// google.protobuf.Timestamp created_at = 10 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 11 [ (gogoproto.stdtime) = true ];
//}
func (Config) TableName() string {
return tablePrefix + "config"
}

@ -10,7 +10,7 @@ import (
)
type Document struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"column:id;type:bigint(20);size:20;default:0;primarykey;autoIncrement;comment:;"`
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
Title string `form:"title" json:"title,omitempty" gorm:"column:title;type:varchar(255);size:255;default:;comment:文档名称;"`
Keywords string `form:"keywords" json:"keywords,omitempty" gorm:"column:keywords;type:varchar(255);size:255;default:;comment:文档关键字;"`
Description string `form:"description" json:"description,omitempty" gorm:"column:description;type:varchar(512);size:512;default:;comment:文档描述;"`
@ -30,11 +30,38 @@ type Document struct {
Price int `form:"price" json:"price,omitempty" gorm:"column:price;type:int(11);size:11;default:0;comment:价格0表示免费;"`
Size int64 `form:"size" json:"size,omitempty" gorm:"column:size;type:bigint(20);size:20;default:0;comment:文件大小;"`
Status int `form:"status" json:"status,omitempty" gorm:"column:status;type:smallint(6);size:6;default:0;index:status;comment:文档状态0 待转换1 转换中2 转换完成3 转换失败4 禁用;"`
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:更新时间;"`
DeletedAt gorm.DeletedAt
CreatedAt time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;default:;comment:创建时间;"`
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
DeletedAt time.Time `form:"deleted_at" json:"deleted_at,omitempty" gorm:"column:deleted_at;type:datetime;default:;comment:;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message Document {
// int64 id = 1;
// string title = 2;
// string keywords = 3;
// string description = 4;
// int64 user_id = 5;
// string cover = 6;
// int32 width = 7;
// int32 height = 8;
// int32 preview = 9;
// int32 pages = 10;
// string uuid = 11;
// int32 download_count = 12;
// int32 view_count = 13;
// int32 favorite_count = 14;
// int32 comment_count = 15;
// int32 score = 16;
// int32 score_count = 17;
// int32 price = 18;
// int64 size = 19;
// int32 status = 20;
// google.protobuf.Timestamp created_at = 21 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 22 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp deleted_at = 23 [ (gogoproto.stdtime) = true ];
//}
func (Document) TableName() string {
return tablePrefix + "document"
}

@ -10,13 +10,22 @@ import (
)
type DocumentCategory struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"column:id;type:bigint(20);size:20;default:0;primarykey;autoIncrement;comment:;"`
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
DocumentId int64 `form:"document_id" json:"document_id,omitempty" gorm:"column:document_id;type:bigint(20);size:20;default:0;comment:文档ID;"`
CategoryId int64 `form:"category_id" json:"category_id,omitempty" gorm:"column:category_id;type:bigint(20);size:20;default:0;index:category_id;comment:分类ID;"`
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:更新时间;"`
CreatedAt time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;default:;comment:创建时间;"`
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message DocumentCategory {
// int64 id = 1;
// int64 document_id = 2;
// int64 category_id = 3;
// google.protobuf.Timestamp created_at = 4 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 5 [ (gogoproto.stdtime) = true ];
//}
func (DocumentCategory) TableName() string {
return tablePrefix + "document_category"
}

@ -10,14 +10,24 @@ import (
)
type DocumentScore struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"column:id;type:bigint(20);size:20;default:0;primarykey;autoIncrement;comment:;"`
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
DocumentId int64 `form:"document_id" json:"document_id,omitempty" gorm:"column:document_id;type:bigint(20);size:20;default:0;comment:文档ID;"`
UserId int64 `form:"user_id" json:"user_id,omitempty" gorm:"column:user_id;type:bigint(20);size:20;default:0;comment:用户ID;"`
Score int `form:"score" json:"score,omitempty" gorm:"column:score;type:int(11);size:11;default:0;comment:文档评分值3位数如500表示5分;"`
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:更新时间;"`
CreatedAt time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;default:;comment:创建时间;"`
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message DocumentScore {
// int64 id = 1;
// int64 document_id = 2;
// int64 user_id = 3;
// int32 score = 4;
// google.protobuf.Timestamp created_at = 5 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 6 [ (gogoproto.stdtime) = true ];
//}
func (DocumentScore) TableName() string {
return tablePrefix + "document_score"
}

@ -10,14 +10,24 @@ import (
)
type Download struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"column:id;type:bigint(20);size:20;default:0;primarykey;autoIncrement;comment:;"`
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:user_id;comment:下载文档的用户ID;"`
DocumentId int64 `form:"document_id" json:"document_id,omitempty" gorm:"column:document_id;type:bigint(20);size:20;default:0;comment:被下载的文档ID;"`
Ip string `form:"ip" json:"ip,omitempty" gorm:"column:ip;type:varchar(16);size:16;default:;comment:下载文档的用户IP;"`
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:更新时间;"`
CreatedAt time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;default:;comment:创建时间;"`
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message Download {
// int64 id = 1;
// int64 user_id = 2;
// int64 document_id = 3;
// string ip = 4;
// google.protobuf.Timestamp created_at = 5 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 6 [ (gogoproto.stdtime) = true ];
//}
func (Download) TableName() string {
return tablePrefix + "download"
}

@ -10,16 +10,28 @@ import (
)
type Friendlink struct {
Id int `form:"id" json:"id,omitempty" gorm:"column:id;type:int(11);size:11;default:0;comment:;"`
Id int `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:;"`
Title string `form:"title" json:"title,omitempty" gorm:"column:title;type:varchar(64);size:64;default:;comment:链接名称;"`
Link string `form:"link" json:"link,omitempty" gorm:"column:link;type:varchar(255);size:255;default:;comment:链接地址;"`
Note string `form:"note" json:"note,omitempty" gorm:"column:note;type:text;default:;comment:备注;"`
Sort int `form:"sort" json:"sort,omitempty" gorm:"column:sort;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;comment:状态0 正常1 禁用;"`
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:更新时间;"`
CreatedAt time.Time `form:"created_at" json:"created_at,omitempty" gorm:"column:created_at;type:datetime;default:;comment:创建时间;"`
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message Friendlink {
// int32 id = 1;
// string title = 2;
// string link = 3;
// string note = 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 ];
//}
func (Friendlink) TableName() string {
return tablePrefix + "friendlink"
}

@ -11,7 +11,7 @@ import (
)
type User struct {
Id int64 `form:"id" json:"id,omitempty" gorm:"column:id;type:bigint(20) unsigned;default:0;primarykey;comment:用户 id;"`
Id int64 `form:"id" json:"id,omitempty" gorm:"primaryKey;autoIncrement;column:id;comment:用户 id;"`
Username string `form:"username" json:"username,omitempty" gorm:"column:username;type:varchar(64);size:64;default:;index:username,unique;comment:用户名;"`
Password string `form:"password" json:"password,omitempty" gorm:"column:password;type:varchar(128);size:128;default:;comment:密码;"`
Nickname string `form:"nickname" json:"nickname,omitempty" gorm:"column:nickname;type:varchar(64);size:64;default:;comment:用户昵称;"`
@ -35,6 +35,32 @@ type User struct {
UpdatedAt time.Time `form:"updated_at" json:"updated_at,omitempty" gorm:"column:updated_at;type:datetime;default:;comment:更新时间;"`
}
// 这里是proto文件中的结构体可以根据需要删除或者调整
//message User {
// int64 id = 1;
// string username = 2;
// string password = 3;
// string nickname = 4;
// string mobile = 5;
// string email = 6;
// string address = 7;
// string signature = 8;
// string last_login_ip = 9;
// string register_ip = 10;
// int32 doc_count = 11;
// int32 follow_count = 12;
// int32 fans_count = 13;
// int32 favorite_count = 14;
// int32 comment_count = 15;
// int32 status = 16;
// string avatar = 17;
// string identity = 18;
// string realname = 19;
// google.protobuf.Timestamp login_at = 20 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp created_at = 21 [ (gogoproto.stdtime) = true ];
// google.protobuf.Timestamp updated_at = 22 [ (gogoproto.stdtime) = true ];
//}
func (User) TableName() string {
return tablePrefix + "user"
}

@ -60,18 +60,24 @@ func Run(cfg *conf.Config, logger *zap.Logger) {
// =========================================================================
endpoint := fmt.Sprintf("localhost:%v", cfg.Port)
healthAPIService := biz.NewHealthAPIService(dbModel, logger)
// 注册grpc服务可以理解为类似给Web服务注册路由
healthAPIService := biz.NewHealthAPIService(dbModel, logger)
v1.RegisterHealthAPIServer(grpcServer, healthAPIService)
// 注册 restful api 服务
err = v1.RegisterHealthAPIHandlerFromEndpoint(context.Background(), gwmux, endpoint, dialOpts)
if err != nil {
logger.Fatal("RegisterHealthAPIHandlerFromEndpoint", zap.Error(err))
return
}
// 用户API接口服务
userAPIService := biz.NewUserAPIService(dbModel, logger)
v1.RegisterUserAPIServer(grpcServer, userAPIService)
err = v1.RegisterUserAPIHandlerFromEndpoint(context.Background(), gwmux, endpoint, dialOpts)
if err != nil {
logger.Fatal("RegisterUserAPIHandlerFromEndpoint", zap.Error(err))
return
}
// =========================================================================
// 【end】 在这里注册您的API服务模块
// =========================================================================

@ -0,0 +1,44 @@
package validate
import (
"errors"
"strings"
localesZH "github.com/go-playground/locales/zh"
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
transZH "github.com/go-playground/validator/v10/translations/zh"
)
var (
zh = localesZH.New()
uni = ut.New(zh, zh)
trans, _ = uni.GetTranslator("zh")
validate = validator.New()
)
func init() {
transZH.RegisterDefaultTranslations(validate, trans)
}
// ValidateStruct 验证结构体
func ValidateStruct(s interface{}, fieldZH ...map[string]string) error {
errValidate := validate.Struct(s)
if errValidate != nil {
var (
errs []string
fieldReplaces []string
)
for _, field := range fieldZH {
for k, v := range field {
fieldReplaces = append(fieldReplaces, k, v)
}
}
replacer := strings.NewReplacer(fieldReplaces...)
for _, v := range errValidate.(validator.ValidationErrors).Translate(trans) {
errs = append(errs, replacer.Replace(v))
}
return errors.New(strings.Join(errs, ""))
}
return nil
}
Loading…
Cancel
Save