Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
9b8b96856c | |||
c315c4747c |
@ -1,7 +1,7 @@
|
|||||||
[app]
|
[app]
|
||||||
name = "devcloud"
|
name = "devcloud"
|
||||||
description = "app desc"
|
description = "app desc"
|
||||||
address = "localhost"
|
address = "http://127.0.0.1:8080"
|
||||||
encrypt_key = "defualt app encrypt key"
|
encrypt_key = "defualt app encrypt key"
|
||||||
|
|
||||||
[datasource]
|
[datasource]
|
||||||
@ -11,7 +11,7 @@
|
|||||||
database = "devcloud_go18"
|
database = "devcloud_go18"
|
||||||
username = "root"
|
username = "root"
|
||||||
password = "123456"
|
password = "123456"
|
||||||
auto_migrate = true
|
auto_migrate = false
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
[http]
|
[http]
|
||||||
|
@ -33,3 +33,28 @@ type Service interface {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## 接口的实现
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TestIssueToken(t *testing.T) {
|
||||||
|
req := token.NewIssueTokenRequest()
|
||||||
|
req.IssueByPassword("admin", "123456")
|
||||||
|
req.Source = token.SOURCE_WEB
|
||||||
|
set, err := svc.IssueToken(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(set)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestQueryToken(t *testing.T) {
|
||||||
|
req := token.NewQueryTokenRequest()
|
||||||
|
set, err := svc.QueryToken(ctx, req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Log(set)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
_ "embed"
|
||||||
|
|
||||||
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/token"
|
||||||
"github.com/infraboard/mcube/v2/ioc"
|
"github.com/infraboard/mcube/v2/ioc"
|
||||||
"github.com/infraboard/mcube/v2/ioc/config/gorestful"
|
"github.com/infraboard/mcube/v2/ioc/config/gorestful"
|
||||||
@ -9,27 +11,31 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
ioc.Api().Registry(&TokenRestulApiHandler{})
|
ioc.Api().Registry(&TokenRestfulApiHandler{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type TokenRestulApiHandler struct {
|
type TokenRestfulApiHandler struct {
|
||||||
ioc.ObjectImpl
|
ioc.ObjectImpl
|
||||||
|
|
||||||
// 依赖控制器
|
// 依赖控制器
|
||||||
svc token.Service
|
svc token.Service
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *TokenRestulApiHandler) Name() string {
|
func (h *TokenRestfulApiHandler) Name() string {
|
||||||
return token.APP_NAME
|
return token.APP_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *TokenRestulApiHandler) Init() error {
|
//go:embed docs/login.md
|
||||||
|
var loginApiDocNotes string
|
||||||
|
|
||||||
|
func (h *TokenRestfulApiHandler) Init() error {
|
||||||
h.svc = token.GetService()
|
h.svc = token.GetService()
|
||||||
|
|
||||||
tags := []string{"用户登录"}
|
tags := []string{"登录管理"}
|
||||||
ws := gorestful.ObjectRouter(h)
|
ws := gorestful.ObjectRouter(h)
|
||||||
ws.Route(ws.POST("").To(h.Login).
|
ws.Route(ws.POST("").To(h.Login).
|
||||||
Doc("颁发令牌(登录)").
|
Doc("颁发令牌(登录)").
|
||||||
|
Notes(loginApiDocNotes).
|
||||||
Metadata(restfulspec.KeyOpenAPITags, tags).
|
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||||
Reads(token.IssueTokenRequest{}).
|
Reads(token.IssueTokenRequest{}).
|
||||||
Writes(token.Token{}).
|
Writes(token.Token{}).
|
||||||
|
8
devcloud/mcenter/apps/token/api/docs/login.md
Normal file
8
devcloud/mcenter/apps/token/api/docs/login.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
登录接口
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "admin",
|
||||||
|
"password": "123456"
|
||||||
|
}
|
||||||
|
```
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/infraboard/mcube/v2/ioc/config/application"
|
"github.com/infraboard/mcube/v2/ioc/config/application"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *TokenRestulApiHandler) Login(r *restful.Request, w *restful.Response) {
|
func (h *TokenRestfulApiHandler) Login(r *restful.Request, w *restful.Response) {
|
||||||
// 1. 获取用户的请求参数, 参数在Body里面
|
// 1. 获取用户的请求参数, 参数在Body里面
|
||||||
req := token.NewIssueTokenRequest()
|
req := token.NewIssueTokenRequest()
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ func (h *TokenRestulApiHandler) Login(r *restful.Request, w *restful.Response) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// Logout HandleFunc
|
// Logout HandleFunc
|
||||||
func (h *TokenRestulApiHandler) Logout(r *restful.Request, w *restful.Response) {
|
func (h *TokenRestfulApiHandler) Logout(r *restful.Request, w *restful.Response) {
|
||||||
req := token.NewRevolkTokenRequest(
|
req := token.NewRevolkTokenRequest(
|
||||||
token.GetAccessTokenFromHTTP(r.Request),
|
token.GetAccessTokenFromHTTP(r.Request),
|
||||||
token.GetRefreshTokenFromHTTP(r.Request),
|
token.GetRefreshTokenFromHTTP(r.Request),
|
||||||
@ -106,7 +106,7 @@ func (h *TokenRestulApiHandler) Logout(r *restful.Request, w *restful.Response)
|
|||||||
response.Success(w, tk)
|
response.Success(w, tk)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *TokenRestulApiHandler) ValiateToken(r *restful.Request, w *restful.Response) {
|
func (h *TokenRestfulApiHandler) ValiateToken(r *restful.Request, w *restful.Response) {
|
||||||
// 1. 获取用户的请求参数, 参数在Body里面
|
// 1. 获取用户的请求参数, 参数在Body里面
|
||||||
req := token.NewValiateTokenRequest("")
|
req := token.NewValiateTokenRequest("")
|
||||||
err := r.ReadEntity(req)
|
err := r.ReadEntity(req)
|
||||||
|
@ -22,6 +22,7 @@ func GetAccessTokenFromHTTP(r *http.Request) string {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
// ?token=xxxx
|
||||||
tk, _ = url.QueryUnescape(cookie.Value)
|
tk, _ = url.QueryUnescape(cookie.Value)
|
||||||
} else {
|
} else {
|
||||||
// 处理 带格式: Bearer <Your API key>
|
// 处理 带格式: Bearer <Your API key>
|
||||||
|
@ -65,3 +65,27 @@ func (u *User) CheckPassword(password string) error {
|
|||||||
return bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
|
return bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(password))
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 接口设计
|
||||||
|
|
||||||
|
```go
|
||||||
|
ws.Route(ws.GET("").To(h.QueryUser).
|
||||||
|
Doc("用户列表查询").
|
||||||
|
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||||
|
Param(restful.QueryParameter("page_size", "分页大小").DataType("integer")).
|
||||||
|
Param(restful.QueryParameter("page_number", "页码").DataType("integer")).
|
||||||
|
Writes(Set{}).
|
||||||
|
Returns(200, "OK", Set{}))
|
||||||
|
```
|
||||||
|
|
||||||
|
其中 user字段里面的
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"password": "$2a$10$GoEjC.vFlgJ..BCvaMu6YurdVgyx4p6S4LFRXiqXESiVY4lokL496"
|
||||||
|
}
|
||||||
|
// 需要脱敏: "*"
|
||||||
|
{
|
||||||
|
"password": "****"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
@ -1 +1,48 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/user"
|
||||||
|
"github.com/infraboard/mcube/v2/ioc"
|
||||||
|
"github.com/infraboard/mcube/v2/ioc/config/gorestful"
|
||||||
|
|
||||||
|
restfulspec "github.com/emicklei/go-restful-openapi/v2"
|
||||||
|
"github.com/emicklei/go-restful/v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
ioc.Api().Registry(&UserRestfulApiHandler{})
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserRestfulApiHandler struct {
|
||||||
|
ioc.ObjectImpl
|
||||||
|
|
||||||
|
// 依赖控制器
|
||||||
|
svc user.Service
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *UserRestfulApiHandler) Name() string {
|
||||||
|
return "users"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *UserRestfulApiHandler) Init() error {
|
||||||
|
h.svc = user.GetService()
|
||||||
|
|
||||||
|
tags := []string{"用户登录"}
|
||||||
|
ws := gorestful.ObjectRouter(h)
|
||||||
|
ws.Route(ws.GET("").To(h.QueryUser).
|
||||||
|
Doc("用户列表查询").
|
||||||
|
Metadata(restfulspec.KeyOpenAPITags, tags).
|
||||||
|
Param(restful.QueryParameter("page_size", "分页大小").DataType("integer")).
|
||||||
|
Param(restful.QueryParameter("page_number", "页码").DataType("integer")).
|
||||||
|
Writes(Set{}).
|
||||||
|
Returns(200, "OK", Set{}))
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// *types.Set[*User]
|
||||||
|
// 返回的泛型, API Doc这个工具 不支持泛型
|
||||||
|
type Set struct {
|
||||||
|
Total int64 `json:"total"`
|
||||||
|
Items []user.User `json:"items"`
|
||||||
|
}
|
||||||
|
40
devcloud/mcenter/apps/user/api/user.go
Normal file
40
devcloud/mcenter/apps/user/api/user.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"122.51.31.227/go-course/go18/devcloud/mcenter/apps/user"
|
||||||
|
"github.com/emicklei/go-restful/v3"
|
||||||
|
"github.com/gin-gonic/gin/binding"
|
||||||
|
"github.com/infraboard/mcube/v2/http/restful/response"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (h *UserRestfulApiHandler) QueryUser(r *restful.Request, w *restful.Response) {
|
||||||
|
// 获取用户通过API传入的参数
|
||||||
|
req := user.NewQueryUserRequest()
|
||||||
|
|
||||||
|
// r.QueryParameter("page_size")
|
||||||
|
// r.QueryParameter("page_number")
|
||||||
|
// url bind, url parameter <---> obj form:"page_size" form:"page_number"
|
||||||
|
// url?
|
||||||
|
// gin bind 的具体实现:非简单结构: json user_ids = [] user_id=1&user_id=2
|
||||||
|
if err := binding.Query.Bind(r.Request, req); err != nil {
|
||||||
|
response.Failed(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
set, err := h.svc.QueryUser(r.Request.Context(), req)
|
||||||
|
if err != nil {
|
||||||
|
response.Failed(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 专门做脱敏处理
|
||||||
|
// for user.password = "" json: omitempty
|
||||||
|
// 每个接口 都需要定制化的写这些逻辑
|
||||||
|
// 为对象实现一个脱名方法: Densence, 断言这个对象实现了这个方法
|
||||||
|
// 定义一个接口,断言这个对象 满足这个接口, 这个能解决80%的问题
|
||||||
|
// 对象嵌套, 你需要自己 去调用嵌套对象的 Densence
|
||||||
|
|
||||||
|
// 能不能直接通过JSON标签 这样方式来完成脱敏: must:"3,4" (181*****4777)
|
||||||
|
// 不能每次都调用吧,因此这个脱敏逻辑 放到 Rsep函数内进行处理
|
||||||
|
response.Success(w, set)
|
||||||
|
}
|
52
devcloud/mcenter/apps/user/design.drawio
Normal file
52
devcloud/mcenter/apps/user/design.drawio
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<mxfile host="65bd71144e">
|
||||||
|
<diagram id="DRQy-UvMks4-KcdwQQVb" name="第 1 页">
|
||||||
|
<mxGraphModel dx="892" dy="370" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0"/>
|
||||||
|
<mxCell id="1" parent="0"/>
|
||||||
|
<mxCell id="2" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="130" y="230" width="410" height="120" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="3" value="user" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="200" y="260" width="120" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="15" style="edgeStyle=none;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="4" target="2">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="4" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="130" y="110" width="410" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="6" value="API" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="30" y="130" width="60" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="7" value="接口" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="40" y="270" width="60" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="9" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="8" target="3">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="8" value="token" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="400" y="260" width="120" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="13" style="edgeStyle=none;html=1;exitX=0;exitY=0.3333333333333333;exitDx=0;exitDy=0;exitPerimeter=0;" edge="1" parent="1" source="10" target="11">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="10" value="Actor" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="630" y="10" width="30" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="14" style="edgeStyle=none;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="11" target="4">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="11" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="130" y="10" width="410" height="70" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="12" value="UI" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="40" y="25" width="60" height="30" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="16" value="<h1 style="margin-top: 0px;">Heading</h1><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;" vertex="1" parent="1">
|
||||||
|
<mxGeometry x="580" y="100" width="180" height="120" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
@ -33,13 +33,13 @@ type Service interface {
|
|||||||
|
|
||||||
func NewQueryUserRequest() *QueryUserRequest {
|
func NewQueryUserRequest() *QueryUserRequest {
|
||||||
return &QueryUserRequest{
|
return &QueryUserRequest{
|
||||||
PageRequest: request.NewDefaultPageRequest(),
|
PageRequest: *request.NewDefaultPageRequest(),
|
||||||
UserIds: []uint64{},
|
UserIds: []uint64{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type QueryUserRequest struct {
|
type QueryUserRequest struct {
|
||||||
*request.PageRequest
|
request.PageRequest
|
||||||
UserIds []uint64 `form:"user" json:"user"`
|
UserIds []uint64 `form:"user" json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ type CreateUserRequest struct {
|
|||||||
// 用户名
|
// 用户名
|
||||||
UserName string `json:"user_name" gorm:"column:user_name;type:varchar(100);not null;uniqueIndex" description:"用户名"`
|
UserName string `json:"user_name" gorm:"column:user_name;type:varchar(100);not null;uniqueIndex" description:"用户名"`
|
||||||
// 密码(Hash过后的)
|
// 密码(Hash过后的)
|
||||||
Password string `json:"password" gorm:"column:password;type:varchar(200);not null" description:"用户密码"`
|
Password string `json:"password,omitempty" gorm:"column:password;type:varchar(200);not null" description:"用户密码" mask:",3,4"`
|
||||||
// 用户描述
|
// 用户描述
|
||||||
Description string `json:"description" gorm:"column:description;type:varchar(200);not null" description:"用户描述"`
|
Description string `json:"description" gorm:"column:description;type:varchar(200);not null" description:"用户描述"`
|
||||||
// 用户类型
|
// 用户类型
|
||||||
|
18
devcloud/mcenter/main.go
Normal file
18
devcloud/mcenter/main.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/infraboard/mcube/v2/ioc/server/cmd"
|
||||||
|
|
||||||
|
// 加载的业务对象
|
||||||
|
_ "122.51.31.227/go-course/go18/devcloud/mcenter/apps"
|
||||||
|
|
||||||
|
// 非功能性模块
|
||||||
|
_ "github.com/infraboard/mcube/v2/ioc/apps/apidoc/restful"
|
||||||
|
_ "github.com/infraboard/mcube/v2/ioc/apps/health/restful"
|
||||||
|
_ "github.com/infraboard/mcube/v2/ioc/apps/metric/restful"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 启动
|
||||||
|
cmd.Start()
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user