This commit is contained in:
新亮
2021-04-18 19:54:31 +08:00
parent 3e1ef9c658
commit 1f85d8df61
64 changed files with 1356 additions and 2758 deletions

View File

@@ -41,10 +41,17 @@ const (
AdminDeleteError = 20303
AdminUpdateError = 20304
AdminResetPasswordError = 20305
AdminLoginError = 20307
AdminLogOutError = 20308
AdminModifyPasswordError = 20309
AdminModifyPersonalInfoError = 20310
AdminLoginError = 20306
AdminLogOutError = 20307
AdminModifyPasswordError = 20308
AdminModifyPersonalInfoError = 20309
// 配置
ConfigEmailError = 20401
ConfigSaveError = 20402
ConfigRedisConnectError = 20403
ConfigMySQLConnectError = 20404
ConfigMySQLInstallError = 20405
)
var codeText = map[int]string{
@@ -81,6 +88,12 @@ var codeText = map[int]string{
AdminLogOutError: "退出失败",
AdminModifyPasswordError: "修改密码失败",
AdminModifyPersonalInfoError: "修改个人信息失败",
ConfigEmailError: "修改邮箱配置失败",
ConfigSaveError: "写入配置文件失败",
ConfigRedisConnectError: "Redis 连接失败",
ConfigMySQLConnectError: "MySQL 连接失败",
ConfigMySQLInstallError: "MySQL 初始化数据失败",
}
func Text(code int) string {

View File

@@ -0,0 +1,98 @@
package config_handler
import (
"fmt"
"net/http"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/env"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/mail"
"github.com/spf13/cast"
"github.com/spf13/viper"
)
type emailRequest struct {
Host string `form:"host"` // 邮箱服务器
Port string `form:"port"` // 端口
User string `form:"user"` // 发件人邮箱
Pass string `form:"pass"` // 发件人密码
To string `form:"to"` // 收件人邮箱地址,多个用,分割
}
type emailResponse struct {
Email string `json:"email"` // 邮箱地址
}
// Email 修改邮件配置
// @Summary 修改邮件配置
// @Description 修改邮件配置
// @Tags API.config
// @Accept multipart/form-data
// @Produce json
// @Param host formData string true "邮箱服务器"
// @Param port formData string true "端口"
// @Param user formData string true "发件人邮箱"
// @Param pass formData string true "发件人密码"
// @Param to formData string true "收件人邮箱地址,多个用,分割"
// @Success 200 {object} emailResponse
// @Failure 400 {object} code.Failure
// @Router /api/config/email [patch]
func (h *handler) Email() core.HandlerFunc {
return func(c core.Context) {
req := new(emailRequest)
res := new(emailResponse)
if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
options := &mail.Options{
MailHost: req.Host,
MailPort: cast.ToInt(req.Port),
MailUser: req.User,
MailPass: req.Pass,
MailTo: req.To,
Subject: fmt.Sprintf("%s[%s] 邮箱告警人调整通知。", configs.ProjectName(), env.Active().Value()),
Body: fmt.Sprintf("%s[%s] 已添加您为系统告警通知人。", configs.ProjectName(), env.Active().Value()),
}
if err := mail.Send(options); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigEmailError,
"Mail Send error: "+err.Error()).WithErr(err),
)
return
}
viper.SetConfigName(env.Active().Value() + "_configs")
viper.SetConfigType("toml")
viper.AddConfigPath("./configs")
viper.Set("mail.host", req.Host)
viper.Set("mail.port", cast.ToInt(req.Port))
viper.Set("mail.user", req.User)
viper.Set("mail.pass", req.Pass)
viper.Set("mail.to", req.To)
err := viper.WriteConfig()
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigEmailError,
code.Text(code.ConfigEmailError)).WithErr(err),
)
return
}
res.Email = req.To
c.Payload(res)
}
}

View File

@@ -0,0 +1,34 @@
package config_handler
import (
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
"go.uber.org/zap"
)
var _ Handler = (*handler)(nil)
type Handler interface {
i()
// Email 修改邮件配置
// @Tags API.config
// @Router /api/config/email [patch]
Email() core.HandlerFunc
}
type handler struct {
logger *zap.Logger
cache cache.Repo
}
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
return &handler{
logger: logger,
cache: cache,
}
}
func (h *handler) i() {}

View File

@@ -1,47 +0,0 @@
package demo_handler
import (
"net/http"
"time"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/token"
)
type authResponse struct {
Authorization string `json:"authorization"` // 签名
ExpireTime int64 `json:"expire_time"` // 过期时间
}
// 获取授权信息
// @Summary 获取授权信息
// @Description 获取授权信息
// @Tags Demo
// @Accept json
// @Produce json
// @Success 200 {object} authResponse
// @Failure 400 {object} code.Failure
// @Router /auth/get [post]
func (h *handler) Auth() core.HandlerFunc {
return func(c core.Context) {
cfg := configs.Get().JWT
tokenString, err := token.New(cfg.Secret).JwtSign(1, "xinliangnote", time.Hour*cfg.ExpireDuration)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(err),
)
return
}
res := new(authResponse)
res.Authorization = tokenString
res.ExpireTime = time.Now().Add(time.Hour * cfg.ExpireDuration).Unix()
c.Payload(res)
}
}

View File

@@ -1,48 +0,0 @@
package demo_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/pkg/errors"
)
func (h *handler) Get() core.HandlerFunc {
type request struct {
Name string `uri:"name"`
}
type response struct {
Name string `json:"name"`
Job string `json:"job"`
}
return func(c core.Context) {
req := new(request)
if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
if req.Name != "Tom" {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.IllegalUserName,
code.Text(code.IllegalUserName)).WithErr(errors.New("req.Name != Tom")),
)
return
}
c.Payload(&response{
Name: "Tom",
Job: "Student",
})
}
}

View File

@@ -1,48 +0,0 @@
package demo_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/pkg/errors"
)
func (h *handler) Post() core.HandlerFunc {
type request struct {
Name string `form:"name"`
}
type response struct {
Name string `json:"name"`
Job string `json:"job"`
}
return func(c core.Context) {
req := new(request)
if err := c.ShouldBindPostForm(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
if req.Name != "Jack" {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.IllegalUserName,
code.Text(code.IllegalUserName)).WithErr(errors.New("req.Name != Jack")),
)
return
}
c.Payload(&response{
Name: "Jack",
Job: "Teacher",
})
}
}

View File

@@ -1,107 +0,0 @@
package demo_handler
import (
"net/http"
"time"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/api/third_party_request/go_gin_api"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/xinliangnote/go-gin-api/pkg/httpclient"
"github.com/xinliangnote/go-gin-api/pkg/p"
"go.uber.org/zap"
)
type traceResponse []struct {
Name string `json:"name"` //用户名
Job string `json:"job"` //工作
}
// Trace 示例
// @Summary Trace 示例
// @Description Trace 示例
// @Tags Demo
// @Accept json
// @Produce json
// @Param Authorization header string true "签名"
// @Success 200 {object} traceResponse
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /demo/trace [get]
func (h *handler) Trace() core.HandlerFunc {
return func(c core.Context) {
// 三方请求信息
res1, err := go_gin_api.DemoGet("Tom",
httpclient.WithTTL(time.Second*5),
httpclient.WithTrace(c.Trace()),
httpclient.WithLogger(c.Logger()),
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api.DemoGetRetryVerify),
)
if err != nil {
h.logger.Error("get [demo/get] err", zap.Error(err))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.CallHTTPError,
code.Text(code.CallHTTPError)).WithErr(err),
)
return
}
// 调试信息
p.Println("res1.Name", res1.Name, p.WithTrace(c.Trace()))
// 三方请求信息
res2, err := go_gin_api.DemoPost("Jack",
httpclient.WithTTL(time.Second*5),
httpclient.WithTrace(c.Trace()),
httpclient.WithLogger(c.Logger()),
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
httpclient.WithOnFailedRetry(3, time.Second*1, go_gin_api.DemoPostRetryVerify),
)
if err != nil {
h.logger.Error("post [demo/post] err", zap.Error(err))
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.CallHTTPError,
code.Text(code.CallHTTPError)).WithErr(err),
)
return
}
// 调试信息
p.Println("res2.Name",
res2.Name,
p.WithTrace(c.Trace()),
)
// 执行 SQL 信息
h.userService.GetUserByUserName(c, "test_user")
// 执行 Redis 信息
_ = h.cache.Set("name", "tom", time.Minute*10, cache.WithTrace(c.Trace()))
val, _ := h.cache.Get("name", cache.WithTrace(c.Trace()))
p.Println("redis-name", val, p.WithTrace(c.Trace()))
// 初始化客户端
// client := hello.NewHelloClient(d.grpConn.Conn())
// client.SayHello(grpc.ContextWithValueAndTimeout(c, time.Second*3), &hello.HelloRequest{Name: "Hello World"})
data := &traceResponse{
{
Name: res1.Name,
Job: res1.Job,
},
{
Name: res2.Name,
Job: res2.Job,
},
}
c.Payload(data)
}
}

View File

@@ -1,50 +0,0 @@
package demo_handler
import (
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
"github.com/xinliangnote/go-gin-api/internal/pkg/grpc"
"go.uber.org/zap"
)
var _ Handler = (*handler)(nil)
type Handler interface {
// i 为了避免被其他包实现
i()
// 示例:支持 get 请求的方法
Get() core.HandlerFunc
// 示例:支持 post 请求的方法
Post() core.HandlerFunc
// 获取授权信息
// @Tags Demo
// @Router /auth/get [post]
Auth() core.HandlerFunc
// Trace 示例
// @Tags Demo
// @Router /demo/trace [get]
Trace() core.HandlerFunc
}
type handler struct {
logger *zap.Logger
cache cache.Repo
grpConn grpc.ClientConn
userService user_service.UserService
}
func New(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.ClientConn) Handler {
return &handler{
logger: logger,
cache: cache,
grpConn: grpConn,
userService: user_service.NewUserService(db, cache),
}
}
func (h *handler) i() {}

View File

@@ -1,77 +0,0 @@
package user_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/pkg/errors"
)
type createRequest struct {
UserName string `json:"user_name"` // 用户名
NickName string `json:"nick_name"` // 昵称
Mobile string `json:"mobile"` // 手机号
}
type createResponse struct {
Id int32 `json:"id"` // 主键ID
}
// 创建用户
// @Summary 创建用户
// @Description 创建用户
// @Tags User
// @Accept json
// @Produce json
// @Param Request body createRequest true "请求信息"
// @Param Authorization header string true "签名"
// @Success 200 {object} createResponse
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /user/create [post]
func (h *handler) Create() core.HandlerFunc {
return func(c core.Context) {
req := new(createRequest)
res := new(createResponse)
if err := c.ShouldBindJSON(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
if req.UserName == "" {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.IllegalUserName,
code.Text(code.IllegalUserName)).WithErr(errors.New("req.UserName = ''")),
)
return
}
createUserData := new(user_service.CreateUserInfo)
createUserData.Mobile = req.Mobile
createUserData.NickName = req.NickName
createUserData.UserName = req.UserName
id, err := h.userService.Create(c, createUserData)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserCreateError,
code.Text(code.UserCreateError)).WithErr(err),
)
return
}
res.Id = id
c.Payload(res)
}
}

View File

@@ -1,57 +0,0 @@
package user_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
)
type deleteRequest struct {
Id int32 `uri:"id"` // 用户ID
}
type deleteResponse struct {
Id int32 `json:"id"` // 用户主键ID
}
// 删除用户
// @Summary 删除用户
// @Description 删除用户
// @Tags User
// @Accept json
// @Produce json
// @Param id path int true "用户ID"
// @Param Authorization header string true "签名"
// @Success 200 {object} deleteResponse
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /user/delete/{id} [patch]
func (h *handler) Delete() core.HandlerFunc {
return func(c core.Context) {
req := new(deleteRequest)
res := new(deleteResponse)
if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
err := h.userService.Delete(c, req.Id)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserUpdateError,
code.Text(code.UserUpdateError)).WithErr(err),
)
return
}
res.Id = req.Id
c.Payload(res)
}
}

View File

@@ -1,65 +0,0 @@
package user_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/ddm"
"github.com/xinliangnote/go-gin-api/pkg/errno"
)
type detailRequest struct {
UserName string `uri:"username"` // 用户名
}
type detailResponse struct {
Id int32 `json:"id"` // 用户主键ID
UserName string `json:"user_name"` // 用户名
NickName string `json:"nick_name"` // 昵称
Mobile ddm.Mobile `json:"mobile"` // 手机号(脱敏)
}
// 用户详情
// @Summary 用户详情
// @Description 用户详情
// @Tags User
// @Accept json
// @Produce json
// @Param username path string true "用户名"
// @Param Authorization header string true "签名"
// @Success 200 {object} detailResponse
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /user/info/{username} [get]
func (h *handler) Detail() core.HandlerFunc {
return func(c core.Context) {
req := new(detailRequest)
res := new(detailResponse)
if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
user, err := h.userService.GetUserByUserName(c, req.UserName)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserSearchError,
code.Text(code.UserSearchError)).WithErr(err),
)
return
}
res.Id = user.Id
res.UserName = user.UserName
res.NickName = user.NickName
res.Mobile = ddm.Mobile(user.Mobile)
c.Payload(res)
}
}

View File

@@ -1,59 +0,0 @@
package user_handler
import (
"net/http"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
)
type updateNickNameByIDRequest struct {
Id int32 `json:"id"` // 用户主键ID
NickName string `json:"nick_name"` // 昵称
}
type updateNickNameByIDResponse struct {
Id int32 `json:"id"` // 用户主键ID
}
// 编辑用户 - 通过用户主键ID更新用户昵称
// @Summary 编辑用户 - 通过用户主键ID更新用户昵称
// @Description 编辑用户 - 通过用户主键ID更新用户昵称
// @Tags User
// @Accept json
// @Produce json
// @Param Request body updateNickNameByIDRequest true "请求信息"
// @Param Authorization header string true "签名"
// @Success 200 {object} updateNickNameByIDResponse
// @Failure 400 {object} code.Failure
// @Failure 401 {object} code.Failure
// @Router /user/update [put]
func (h *handler) UpdateNickNameByID() core.HandlerFunc {
return func(c core.Context) {
req := new(updateNickNameByIDRequest)
res := new(updateNickNameByIDResponse)
if err := c.ShouldBindJSON(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
err := h.userService.UpdateNickNameByID(c, req.Id, req.NickName)
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.UserUpdateError,
code.Text(code.UserUpdateError)).WithErr(err),
)
return
}
res.Id = req.Id
c.Payload(res)
}
}

View File

@@ -1,53 +0,0 @@
package user_handler
import (
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
"go.uber.org/zap"
)
var _ Handler = (*handler)(nil)
type Handler interface {
// i 为了避免被其他包实现
i()
// Create 创建用户
// @Tags User
// @Router /user/create [post]
Create() core.HandlerFunc
// UpdateNickNameByID 编辑用户 - 通过主键ID更新用户昵称
// @Tags User
// @Router /user/update [put]
UpdateNickNameByID() core.HandlerFunc
// Delete 删除用户
// @Tags User
// @Router /user/delete/{id} [patch]
Delete() core.HandlerFunc
// Detail 用户详情
// @Tags User
// @Router /user/info/{username} [get]
Detail() core.HandlerFunc
}
type handler struct {
logger *zap.Logger
cache cache.Repo
userService user_service.UserService
}
func New(logger *zap.Logger, db db.Repo, cache cache.Repo) Handler {
return &handler{
logger: logger,
cache: cache,
userService: user_service.NewUserService(db, cache),
}
}
func (h *handler) i() {}

View File

@@ -1,15 +0,0 @@
package user_demo_repo
import "time"
// 用户Demo表
//go:generate gormgen -structs UserDemo -input .
type UserDemo struct {
Id int32 // 主键
UserName string // 用户名
NickName string // 昵称
Mobile string // 手机号
IsDeleted int32 // 是否删除 1:是 -1:否
CreatedAt time.Time `gorm:"time"` // 创建时间
UpdatedAt time.Time `gorm:"time"` // 更新时间
}

View File

@@ -1,12 +0,0 @@
#### go_gin_api.user_demo
用户Demo表
| 序号 | 名称 | 描述 | 类型 | 键 | 为空 | 额外 | 默认值 |
| :--: | :--: | :--: | :--: | :--: | :--: | :--: | :--: |
| 1 | id | 主键 | int(11) unsigned | PRI | NO | auto_increment | |
| 2 | user_name | 用户名 | varchar(32) | | NO | | |
| 3 | nick_name | 昵称 | varchar(100) | | NO | | |
| 4 | mobile | 手机号 | varchar(20) | | NO | | |
| 5 | is_deleted | 是否删除 1:是 -1:否 | tinyint(1) | | NO | | -1 |
| 6 | created_at | 创建时间 | timestamp | | NO | | CURRENT_TIMESTAMP |
| 7 | updated_at | 更新时间 | timestamp | | NO | on update CURRENT_TIMESTAMP | CURRENT_TIMESTAMP |

View File

@@ -1,411 +0,0 @@
///////////////////////////////////////////////////////////
// THIS FILE IS AUTO GENERATED by gormgen, DON'T EDIT IT //
// ANY CHANGES DONE HERE WILL BE LOST //
///////////////////////////////////////////////////////////
package user_demo_repo
import (
"fmt"
"time"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/pkg/errors"
"gorm.io/gorm"
)
func NewModel() *UserDemo {
return new(UserDemo)
}
func NewQueryBuilder() *userDemoRepoQueryBuilder {
return new(userDemoRepoQueryBuilder)
}
func (t *UserDemo) Create(db *gorm.DB) (id int32, err error) {
if err = db.Create(t).Error; err != nil {
return 0, errors.Wrap(err, "create err")
}
return t.Id, nil
}
func (t *UserDemo) Delete(db *gorm.DB) (err error) {
if err = db.Delete(t).Error; err != nil {
return errors.Wrap(err, "delete err")
}
return nil
}
func (t *UserDemo) Updates(db *gorm.DB, m map[string]interface{}) (err error) {
if err = db.Model(&UserDemo{}).Where("id = ?", t.Id).Updates(m).Error; err != nil {
return errors.Wrap(err, "updates err")
}
return nil
}
type userDemoRepoQueryBuilder struct {
order []string
where []struct {
prefix string
value interface{}
}
limit int
offset int
}
func (qb *userDemoRepoQueryBuilder) buildQuery(db *gorm.DB) *gorm.DB {
ret := db
for _, where := range qb.where {
ret = ret.Where(where.prefix, where.value)
}
for _, order := range qb.order {
ret = ret.Order(order)
}
ret = ret.Limit(qb.limit).Offset(qb.offset)
return ret
}
func (qb *userDemoRepoQueryBuilder) Count(db *gorm.DB) (int64, error) {
var c int64
res := qb.buildQuery(db).Model(&UserDemo{}).Count(&c)
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
c = 0
}
return c, res.Error
}
func (qb *userDemoRepoQueryBuilder) First(db *gorm.DB) (*UserDemo, error) {
ret := &UserDemo{}
res := qb.buildQuery(db).First(ret)
if res.Error != nil && res.Error == gorm.ErrRecordNotFound {
ret = nil
}
return ret, res.Error
}
func (qb *userDemoRepoQueryBuilder) QueryOne(db *gorm.DB) (*UserDemo, error) {
qb.limit = 1
ret, err := qb.QueryAll(db)
if len(ret) > 0 {
return ret[0], err
}
return nil, err
}
func (qb *userDemoRepoQueryBuilder) QueryAll(db *gorm.DB) ([]*UserDemo, error) {
var ret []*UserDemo
err := qb.buildQuery(db).Find(&ret).Error
return ret, err
}
func (qb *userDemoRepoQueryBuilder) Limit(limit int) *userDemoRepoQueryBuilder {
qb.limit = limit
return qb
}
func (qb *userDemoRepoQueryBuilder) Offset(offset int) *userDemoRepoQueryBuilder {
qb.offset = offset
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereId(p db_repo.Predicate, value int32) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "id", p),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereIdIn(value []int32) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "id", "IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereIdNotIn(value []int32) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "id", "NOT IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) OrderById(asc bool) *userDemoRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "id "+order)
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereUserName(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "user_name", p),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereUserNameIn(value []string) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "user_name", "IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereUserNameNotIn(value []string) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "user_name", "NOT IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) OrderByUserName(asc bool) *userDemoRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "user_name "+order)
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereNickName(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "nick_name", p),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereNickNameIn(value []string) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "nick_name", "IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereNickNameNotIn(value []string) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "nick_name", "NOT IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) OrderByNickName(asc bool) *userDemoRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "nick_name "+order)
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereMobile(p db_repo.Predicate, value string) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "mobile", p),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereMobileIn(value []string) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "mobile", "IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereMobileNotIn(value []string) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "mobile", "NOT IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) OrderByMobile(asc bool) *userDemoRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "mobile "+order)
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereIsDeleted(p db_repo.Predicate, value int32) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "is_deleted", p),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereIsDeletedIn(value []int32) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "is_deleted", "IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereIsDeletedNotIn(value []int32) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "is_deleted", "NOT IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) OrderByIsDeleted(asc bool) *userDemoRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "is_deleted "+order)
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereCreatedAt(p db_repo.Predicate, value time.Time) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "created_at", p),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereCreatedAtIn(value []time.Time) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "created_at", "IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereCreatedAtNotIn(value []time.Time) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "created_at", "NOT IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) OrderByCreatedAt(asc bool) *userDemoRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "created_at "+order)
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereUpdatedAt(p db_repo.Predicate, value time.Time) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "updated_at", p),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereUpdatedAtIn(value []time.Time) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "updated_at", "IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) WhereUpdatedAtNotIn(value []time.Time) *userDemoRepoQueryBuilder {
qb.where = append(qb.where, struct {
prefix string
value interface{}
}{
fmt.Sprintf("%v %v ?", "updated_at", "NOT IN"),
value,
})
return qb
}
func (qb *userDemoRepoQueryBuilder) OrderByUpdatedAt(asc bool) *userDemoRepoQueryBuilder {
order := "DESC"
if asc {
order = "ASC"
}
qb.order = append(qb.order, "updated_at "+order)
return qb
}

View File

@@ -1,34 +0,0 @@
package user_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/db"
)
var _ UserService = (*userSer)(nil)
type UserService interface {
// i 为了避免被其他包实现
i()
Create(ctx core.Context, user *CreateUserInfo) (id int32, err error)
UpdateNickNameByID(ctx core.Context, id int32, username string) (err error)
GetUserByUserName(ctx core.Context, username string) (user *user_demo_repo.UserDemo, err error)
Delete(ctx core.Context, id int32) (err error)
}
type userSer struct {
db db.Repo
cache cache.Repo
}
func NewUserService(db db.Repo, cache cache.Repo) UserService {
return &userSer{
db: db,
cache: cache,
}
}
func (u *userSer) i() {}

View File

@@ -1,25 +0,0 @@
package user_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
type CreateUserInfo struct {
UserName string `json:"user_name"` // 用户名
NickName string `json:"nick_name"` // 昵称
Mobile string `json:"mobile"` // 手机号
}
func (u *userSer) Create(ctx core.Context, user *CreateUserInfo) (id int32, err error) {
model := user_demo_repo.NewModel()
model.UserName = user.UserName
model.NickName = user.NickName
model.Mobile = user.Mobile
id, err = model.Create(u.db.GetDbW().WithContext(ctx.RequestContext()))
if err != nil {
return 0, err
}
return
}

View File

@@ -1,16 +0,0 @@
package user_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
func (u *userSer) Delete(ctx core.Context, id int32) (err error) {
model := user_demo_repo.NewModel()
model.Id = id
err = model.Delete(u.db.GetDbW().WithContext(ctx.RequestContext()))
if err != nil {
return nil
}
return nil
}

View File

@@ -1,23 +0,0 @@
package user_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo"
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
func (u *userSer) GetUserByUserName(ctx core.Context, username string) (user *user_demo_repo.UserDemo, err error) {
user, err = user_demo_repo.NewQueryBuilder().
WhereUserName(db_repo.EqualPredicate, username).
QueryOne(u.db.GetDbR().WithContext(ctx.RequestContext()))
if err != nil {
return user, err
}
if user == nil {
user = user_demo_repo.NewModel()
}
return user, nil
}

View File

@@ -1,22 +0,0 @@
package user_service
import (
"github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
func (u *userSer) UpdateNickNameByID(ctx core.Context, id int32, nickname string) (err error) {
model := user_demo_repo.NewModel()
model.Id = id
data := map[string]interface{}{
"nick_name": nickname,
}
err = model.Updates(u.db.GetDbW().WithContext(ctx.RequestContext()), data)
if err != nil {
return err
}
return nil
}

View File

@@ -3,7 +3,6 @@ package resolvers
import (
"context"
"github.com/xinliangnote/go-gin-api/internal/api/service/user_service"
"github.com/xinliangnote/go-gin-api/internal/graph/generated"
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
@@ -21,17 +20,17 @@ type mutationResolver struct{ *Resolver }
type queryResolver struct{ *Resolver }
type Resolver struct {
logger *zap.Logger
cache cache.Repo
userService user_service.UserService
logger *zap.Logger
cache cache.Repo
//userService user_service.UserService
}
func NewRootResolvers(logger *zap.Logger, db db.Repo, cache cache.Repo) generated.Config {
c := generated.Config{
Resolvers: &Resolver{
logger: logger,
cache: cache,
userService: user_service.NewUserService(db, cache),
logger: logger,
cache: cache,
//userService: user_service.NewUserService(db, cache),
},
}
return c

View File

@@ -24,18 +24,49 @@ type resource struct {
middles middleware.Middleware
}
func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.ClientConn) (core.Mux, error) {
var openBrowserUri = "http://127.0.0.1:9999"
_, ok := file.IsExists(configs.InitDBLockFile())
if !ok {
openBrowserUri = "http://127.0.0.1:9999/init?init=db"
}
type Server struct {
Mux core.Mux
Db db.Repo
Cache cache.Repo
GrpClient grpc.ClientConn
}
func NewHTTPServer(logger *zap.Logger) (*Server, error) {
if logger == nil {
return nil, errors.New("logger required")
}
r := new(resource)
r.logger = logger
openBrowserUri := "http://127.0.0.1" + configs.ProjectPort()
_, ok := file.IsExists(configs.ProjectInstallFile())
if !ok { // 未安装
openBrowserUri += "/install"
} else { // 已安装
// 初始化 DB
dbRepo, err := db.New()
if err != nil {
logger.Fatal("new db err", zap.Error(err))
}
r.db = dbRepo
// 初始化 Cache
cacheRepo, err := cache.New()
if err != nil {
logger.Fatal("new cache err", zap.Error(err))
}
r.cache = cacheRepo
// 初始化 gRPC client
gRPCRepo, err := grpc.New()
if err != nil {
logger.Fatal("new grpc err", zap.Error(err))
}
r.grpConn = gRPCRepo
}
mux, err := core.New(logger,
core.WithEnableOpenBrowser(openBrowserUri),
core.WithEnableCors(),
@@ -48,13 +79,8 @@ func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.C
panic(err)
}
r := new(resource)
r.mux = mux
r.logger = logger
r.db = db
r.cache = cache
r.grpConn = grpConn
r.middles = middleware.New(logger, cache, db)
r.middles = middleware.New(logger, r.cache, r.db)
// 设置 WEB 路由
setWebRouter(r)
@@ -65,5 +91,11 @@ func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo, grpConn grpc.C
// 设置 GraphQL 路由
setGraphQLRouter(r)
return mux, nil
s := new(Server)
s.Mux = mux
s.Db = r.db
s.Cache = r.cache
s.GrpClient = r.grpConn
return s, nil
}

View File

@@ -3,47 +3,17 @@ package router
import (
"github.com/xinliangnote/go-gin-api/internal/api/controller/admin_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/authorized_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/demo_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/config_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/tool_handler"
"github.com/xinliangnote/go-gin-api/internal/api/controller/user_handler"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
func setApiRouter(r *resource) {
// demo 控制器
demoHandler := demo_handler.New(r.logger, r.db, r.cache, r.grpConn)
demo := r.mux.Group("/demo", core.WrapAuthHandler(r.middles.Jwt)) // 使用 jwt 验证
{
// 为了演示 Trace ,增加了一些看起来无意义的调试信息和 SQL 信息。
demo.GET("/trace", demoHandler.Trace())
// 模拟数据
demo.GET("get/:name", core.AliasForRecordMetrics("/demo/get"), demoHandler.Get())
demo.POST("post", demoHandler.Post())
}
demoNoAuth := r.mux.Group("/auth") // 不使用 jwt 验证
{
demoNoAuth.POST("/get", demoHandler.Auth())
}
// user 控制器
userHandler := user_handler.New(r.logger, r.db, r.cache)
user := r.mux.Group("/user", core.WrapAuthHandler(r.middles.Jwt))
{
user.POST("/create", userHandler.Create())
user.PUT("/update", userHandler.UpdateNickNameByID())
user.PATCH("/delete/:id", userHandler.Delete())
user.GET("/info/:username", core.AliasForRecordMetrics("/user/info"), userHandler.Detail())
}
// authorized
authorizedHandler := authorized_handler.New(r.logger, r.db, r.cache)
// admin
adminHandler := admin_handler.New(r.logger, r.db, r.cache)
// 登录
// login
login := r.mux.Group("/login", r.middles.Signature())
{
login.POST("/web", adminHandler.Login())
@@ -52,14 +22,16 @@ func setApiRouter(r *resource) {
// api
api := r.mux.Group("/api", core.WrapAuthHandler(r.middles.Token), r.middles.Signature())
{
// authorized
authorizedHandler := authorized_handler.New(r.logger, r.db, r.cache)
api.POST("/authorized", authorizedHandler.Create())
api.GET("/authorized", authorizedHandler.List())
api.PATCH("/authorized/used", authorizedHandler.UpdateUsed())
api.DELETE("/authorized/:id", authorizedHandler.Delete())
api.DELETE("/authorized/:id", core.AliasForRecordMetrics("/api/authorized/info"), authorizedHandler.Delete())
api.POST("/authorized_api", authorizedHandler.CreateAPI())
api.GET("/authorized_api", authorizedHandler.ListAPI())
api.DELETE("/authorized_api/:id", authorizedHandler.DeleteAPI())
api.DELETE("/authorized_api/:id", core.AliasForRecordMetrics("/api/authorized_api/info"), authorizedHandler.DeleteAPI())
api.POST("/admin", adminHandler.Create())
api.GET("/admin", adminHandler.List())
@@ -75,5 +47,10 @@ func setApiRouter(r *resource) {
toolHandler := tool_handler.New(r.logger, r.db, r.cache)
api.GET("/tool/hashids/encode/:id", toolHandler.HashIdsEncode())
api.GET("/tool/hashids/decode/:id", toolHandler.HashIdsDecode())
// config
configHandler := config_handler.New(r.logger, r.db, r.cache)
api.PATCH("/config/email", configHandler.Email())
}
}

View File

@@ -3,38 +3,43 @@ package router
import (
"github.com/xinliangnote/go-gin-api/internal/web/controller/admin_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/authorized_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/configinfo_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/config_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/dashboard_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/gencode_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/index_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/install_handler"
"github.com/xinliangnote/go-gin-api/internal/web/controller/tool_handler"
)
func setWebRouter(r *resource) {
installHandler := install_handler.New(r.logger)
indexHandler := index_handler.New(r.logger, r.db, r.cache)
dashboardHandler := dashboard_handler.New(r.logger, r.db, r.cache)
genCodeHandler := gencode_handler.New(r.logger, r.db, r.cache)
configInfoHandler := configinfo_handler.New(r.logger, r.db, r.cache)
configInfoHandler := config_handler.New(r.logger, r.db, r.cache)
authorizedHandler := authorized_handler.New(r.logger, r.db, r.cache)
toolHandler := tool_handler.New(r.logger, r.db, r.cache)
adminHandler := admin_handler.New(r.logger, r.db, r.cache)
web := r.mux.Group("", r.middles.DisableLog())
{
// 首页侧边栏
// 首页
web.GET("", indexHandler.View())
// 安装
web.GET("/install", installHandler.View())
web.POST("/install/execute", installHandler.Execute())
web.POST("/install/restart", installHandler.Restart())
// 仪表盘
web.GET("/dashboard", dashboardHandler.View())
// 配置信息
web.GET("/configinfo", configInfoHandler.View())
// 代码生成
web.GET("/init", genCodeHandler.InitView())
web.POST("/init_exec", genCodeHandler.InitExecute())
web.GET("/config/email", configInfoHandler.EmailView())
web.GET("/config/code", configInfoHandler.CodeView())
// 代码生成工具
web.GET("/gormgen", genCodeHandler.GormView())
web.POST("/gormgen_exec", genCodeHandler.GormExecute())

View File

@@ -0,0 +1,56 @@
package config_handler
import (
"go/token"
"log"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/dave/dst"
"github.com/dave/dst/decorator"
"github.com/spf13/cast"
)
func (h *handler) CodeView() core.HandlerFunc {
return func(c core.Context) {
fs := token.NewFileSet()
filePath := "./internal/api/code/code.go"
parsedFile, err := decorator.ParseFile(fs, filePath, nil, 0)
if err != nil {
log.Fatalf("parsing package: %s: %s\n", filePath, err)
}
type codes struct {
Code int `json:"code"` // 错误码
Message string `json:"message"` // 错误码信息
}
var constCodes []codes
dst.Inspect(parsedFile, func(n dst.Node) bool {
decl, ok := n.(*dst.GenDecl)
if !ok || decl.Tok != token.CONST {
return true
}
for _, spec := range decl.Specs {
valueSpec, _ok := spec.(*dst.ValueSpec)
if !_ok {
continue
}
codeInt := cast.ToInt(valueSpec.Values[0].(*dst.BasicLit).Value)
constCodes = append(constCodes, codes{
Code: codeInt,
Message: code.Text(codeInt),
})
}
return true
})
c.HTML("config_code", constCodes)
}
}

View File

@@ -0,0 +1,12 @@
package config_handler
import (
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
func (h *handler) EmailView() core.HandlerFunc {
return func(c core.Context) {
c.HTML("config_email", configs.Get())
}
}

View File

@@ -1,4 +1,4 @@
package configinfo_handler
package config_handler
import (
"github.com/xinliangnote/go-gin-api/internal/pkg/cache"
@@ -13,7 +13,8 @@ var _ Handler = (*handler)(nil)
type Handler interface {
i()
View() core.HandlerFunc
EmailView() core.HandlerFunc
CodeView() core.HandlerFunc
}
type handler struct {

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"os/exec"
"runtime"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
@@ -25,10 +26,12 @@ func (h *handler) GormExecute() core.HandlerFunc {
mysqlConf := configs.Get().MySQL.Read
shellPath := fmt.Sprintf("./scripts/gormgen.sh %s %s %s %s %s", mysqlConf.Addr, mysqlConf.User, mysqlConf.Pass, mysqlConf.Name, req.Tables)
command := exec.Command("/bin/bash", "-c", shellPath) //初始化 Cmd
// runtime.GOOS = linux or darwin
command := exec.Command("/bin/bash", "-c", shellPath)
// windows 版本
//command := exec.Command("cmd", "/C", shellPath)
if runtime.GOOS == "windows" {
command = exec.Command("cmd", "/C", shellPath)
}
var stderr bytes.Buffer
command.Stderr = &stderr

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"fmt"
"os/exec"
"runtime"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
@@ -21,10 +22,13 @@ func (h *handler) HandlerExecute() core.HandlerFunc {
}
shellPath := fmt.Sprintf("./scripts/handlergen.sh %s", req.Name)
command := exec.Command("/bin/bash", "-c", shellPath) //初始化 Cmd
// windows 版本
//command := exec.Command("cmd", "/C", shellPath)
// runtime.GOOS = linux or darwin
command := exec.Command("/bin/bash", "-c", shellPath)
if runtime.GOOS == "windows" {
command = exec.Command("cmd", "/C", shellPath)
}
var stderr bytes.Buffer
command.Stderr = &stderr

View File

@@ -1,33 +0,0 @@
package gencode_handler
import (
"bytes"
"fmt"
"os/exec"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
func (h *handler) InitExecute() core.HandlerFunc {
return func(c core.Context) {
mysqlConf := configs.Get().MySQL.Read
shellPath := fmt.Sprintf("./scripts/init.sh %s %s %s %s", mysqlConf.Addr, mysqlConf.User, mysqlConf.Pass, mysqlConf.Name)
command := exec.Command("/bin/bash", "-c", shellPath) //初始化 Cmd
// windows 版本
//command := exec.Command("cmd", "/C", shellPath)
var stderr bytes.Buffer
command.Stderr = &stderr
output, err := command.Output()
if err != nil {
c.Payload(stderr.String())
return
}
c.Payload(string(output))
}
}

View File

@@ -1,9 +0,0 @@
package gencode_handler
import "github.com/xinliangnote/go-gin-api/internal/pkg/core"
func (h *handler) InitView() core.HandlerFunc {
return func(c core.Context) {
c.HTML("gencode_init", nil)
}
}

View File

@@ -13,9 +13,6 @@ var _ Handler = (*handler)(nil)
type Handler interface {
i()
InitView() core.HandlerFunc
InitExecute() core.HandlerFunc
HandlerView() core.HandlerFunc
HandlerExecute() core.HandlerFunc

View File

@@ -0,0 +1,204 @@
package install_handler
import (
"fmt"
"net/http"
"os"
"github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/api/code"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/web/controller/install_handler/mysql_table"
"github.com/xinliangnote/go-gin-api/pkg/env"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/go-redis/redis/v7"
"github.com/spf13/cast"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
type initExecuteRequest struct {
RedisAddr string `form:"redis_addr"` // 连接地址例如127.0.0.1:6379
RedisPass string `form:"redis_pass"` // 连接密码
RedisDb string `form:"redis_db"` // 连接 db
MySQLAddr string `form:"mysql_addr"`
MySQLUser string `form:"mysql_user"`
MySQLPass string `form:"mysql_pass"`
MySQLName string `form:"mysql_name"`
}
func (h *handler) Execute() core.HandlerFunc {
return func(c core.Context) {
req := new(initExecuteRequest)
if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err),
)
return
}
outPutString := ""
cfg := configs.Get()
redisClient := redis.NewClient(&redis.Options{
Addr: req.RedisAddr,
Password: req.RedisPass,
DB: cast.ToInt(req.RedisDb),
MaxRetries: cfg.Redis.MaxRetries,
PoolSize: cfg.Redis.PoolSize,
MinIdleConns: cfg.Redis.MinIdleConns,
})
if err := redisClient.Ping().Err(); err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigRedisConnectError,
code.Text(code.ConfigRedisConnectError)).WithErr(err),
)
return
}
defer redisClient.Close()
outPutString += "已检测 Redis 配置可用。\n"
dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=%t&loc=%s",
req.MySQLUser,
req.MySQLPass,
req.MySQLAddr,
req.MySQLName,
true,
"Local")
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
//Logger: logger.Default.LogMode(logger.Info), // 日志配置
})
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigMySQLConnectError,
code.Text(code.ConfigMySQLConnectError)).WithErr(err),
)
return
}
db.Set("gorm:table_options", "CHARSET=utf8mb4")
dbClient, _ := db.DB()
defer dbClient.Close()
outPutString += "已检测 MySQL 配置可用。\n"
viper.SetConfigName(env.Active().Value() + "_configs")
viper.SetConfigType("toml")
viper.AddConfigPath("./configs")
viper.Set("redis.addr", req.RedisAddr)
viper.Set("redis.pass", req.RedisPass)
viper.Set("redis.db", req.RedisDb)
viper.Set("mysql.read.addr", req.MySQLAddr)
viper.Set("mysql.read.user", req.MySQLUser)
viper.Set("mysql.read.pass", req.MySQLPass)
viper.Set("mysql.read.name", req.MySQLName)
viper.Set("mysql.write.addr", req.MySQLAddr)
viper.Set("mysql.write.user", req.MySQLUser)
viper.Set("mysql.write.pass", req.MySQLPass)
viper.Set("mysql.write.name", req.MySQLName)
if viper.WriteConfig() != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigSaveError,
code.Text(code.ConfigSaveError)).WithErr(err),
)
return
}
outPutString += "配置项 Redis、MySQL 配置成功。\n"
if err = db.Exec(mysql_table.CreateAuthorizedTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigMySQLInstallError,
"MySQL "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表authorized 成功。\n"
if err = db.Exec(mysql_table.CreateAuthorizedTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigMySQLInstallError,
"MySQL "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表authorized 默认数据成功。\n"
if err = db.Exec(mysql_table.CreateAuthorizedAPITableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigMySQLInstallError,
"MySQL "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表authorized_api 成功。\n"
if err = db.Exec(mysql_table.CreateAuthorizedAPITableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigMySQLInstallError,
"MySQL "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表authorized_api 默认数据成功。\n"
if err = db.Exec(mysql_table.CreateAdminTableSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigMySQLInstallError,
"MySQL "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表admin 成功。\n"
if err = db.Exec(mysql_table.CreateAdminTableDataSql()).Error; err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigMySQLInstallError,
"MySQL "+err.Error()).WithErr(err),
)
return
}
outPutString += "初始化 MySQL 数据表admin 默认数据成功。\n"
// 生成 install 完成标识
f, err := os.Create(configs.ProjectInstallFile())
if err != nil {
c.AbortWithError(errno.NewError(
http.StatusBadRequest,
code.ConfigMySQLInstallError,
"create lock file err: "+err.Error()).WithErr(err),
)
return
}
defer f.Close()
c.Payload(outPutString)
}
}

View File

@@ -0,0 +1,37 @@
package install_handler
import (
"bytes"
"os/exec"
"runtime"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
)
func (h *handler) Restart() core.HandlerFunc {
return func(c core.Context) {
shellPath := "./scripts/restart.sh"
// runtime.GOOS = linux or darwin
command := exec.Command("/bin/bash", "-c", shellPath)
if runtime.GOOS == "windows" {
command = exec.Command("cmd", "/C", shellPath)
}
var stderr bytes.Buffer
command.Stderr = &stderr
outPutString := ""
output, err := command.Output()
if err != nil {
outPutString += stderr.String()
c.Payload(outPutString)
return
}
outPutString += string(output)
c.Payload(outPutString)
}
}

View File

@@ -1,4 +1,4 @@
package configinfo_handler
package install_handler
import (
"github.com/xinliangnote/go-gin-api/configs"
@@ -7,6 +7,6 @@ import (
func (h *handler) View() core.HandlerFunc {
return func(c core.Context) {
c.HTML("configinfo", configs.Get())
c.HTML("install_view", configs.Get())
}
}

View File

@@ -0,0 +1,29 @@
package install_handler
import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"go.uber.org/zap"
)
var _ Handler = (*handler)(nil)
type Handler interface {
i()
View() core.HandlerFunc
Execute() core.HandlerFunc
Restart() core.HandlerFunc
}
type handler struct {
logger *zap.Logger
}
func New(logger *zap.Logger) Handler {
return &handler{
logger: logger,
}
}
func (h *handler) i() {}

View File

@@ -0,0 +1,44 @@
package mysql_table
//CREATE TABLE `admin` (
//`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
//`username` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',
//`password` varchar(100) NOT NULL DEFAULT '' COMMENT '密码',
//`nickname` varchar(60) NOT NULL DEFAULT '' COMMENT '昵称',
//`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
//`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否',
//`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',
//`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
//`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人',
//`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
//`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人',
//PRIMARY KEY (`id`),
//UNIQUE KEY `unique_username` (`username`)
//) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员表';
func CreateAdminTableSql() (sql string) {
sql = "CREATE TABLE `admin` ("
sql += "`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',"
sql += "`username` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',"
sql += "`password` varchar(100) NOT NULL DEFAULT '' COMMENT '密码',"
sql += "`nickname` varchar(60) NOT NULL DEFAULT '' COMMENT '昵称',"
sql += "`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',"
sql += "`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否',"
sql += "`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',"
sql += "`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',"
sql += "`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人',"
sql += "`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',"
sql += "`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人',"
sql += "PRIMARY KEY (`id`),"
sql += "UNIQUE KEY `unique_username` (`username`)"
sql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='管理员表';"
return
}
func CreateAdminTableDataSql() (sql string) {
sql = "INSERT INTO `admin` (`id`, `username`, `password`, `nickname`, `mobile`, `created_user`) VALUES"
sql += "(1, 'admin', 'f78382de80cf583cf854bbac0b6e796fbde36fe2739ca4ae072637010f179cb0', '管理员', '13888888888', 'init');"
return
}

View File

@@ -0,0 +1,43 @@
package mysql_table
//CREATE TABLE `authorized` (
//`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
//`business_key` varchar(32) NOT NULL DEFAULT '' COMMENT '调用方key',
//`business_secret` varchar(60) NOT NULL DEFAULT '' COMMENT '调用方secret',
//`business_developer` varchar(60) NOT NULL DEFAULT '' COMMENT '调用方对接人',
//`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',
//`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否',
//`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',
//`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
//`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人',
//`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
//`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人',
//PRIMARY KEY (`id`),
//UNIQUE KEY `unique_business_key` (`business_key`)
//) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='已授权的调用方表';
func CreateAuthorizedTableSql() (sql string) {
sql = "CREATE TABLE `authorized` ("
sql += "`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',"
sql += "`business_key` varchar(32) NOT NULL DEFAULT '' COMMENT '调用方key',"
sql += "`business_secret` varchar(60) NOT NULL DEFAULT '' COMMENT '调用方secret',"
sql += "`business_developer` varchar(60) NOT NULL DEFAULT '' COMMENT '调用方对接人',"
sql += "`remark` varchar(255) NOT NULL DEFAULT '' COMMENT '备注',"
sql += "`is_used` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用 1:是 -1:否',"
sql += "`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',"
sql += "`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',"
sql += "`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人',"
sql += "`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',"
sql += "`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人',"
sql += "PRIMARY KEY (`id`),"
sql += "UNIQUE KEY `unique_business_key` (`business_key`)"
sql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='已授权的调用方表';"
return
}
func CreateAuthorizedTableDataSql() (sql string) {
sql = "INSERT INTO `authorized` (`id`, `business_key`, `business_secret`, `business_developer`, `remark`, `created_user`) VALUES (1, 'admin', '12878dd962115106db6d', '管理员', '管理面板调用', 'init');"
return
}

View File

@@ -0,0 +1,42 @@
package mysql_table
//CREATE TABLE `authorized_api` (
//`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
//`business_key` varchar(30) NOT NULL DEFAULT '' COMMENT '调用方key',
//`method` varchar(30) NOT NULL DEFAULT '' COMMENT '请求方式',
//`api` varchar(100) NOT NULL DEFAULT '' COMMENT '请求地址',
//`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',
//`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
//`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人',
//`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
//`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人',
//PRIMARY KEY (`id`)
//) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='已授权接口地址表';
func CreateAuthorizedAPITableSql() (sql string) {
sql = "CREATE TABLE `authorized_api` ("
sql += "`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',"
sql += "`business_key` varchar(30) NOT NULL DEFAULT '' COMMENT '调用方key',"
sql += "`method` varchar(30) NOT NULL DEFAULT '' COMMENT '请求方式',"
sql += "`api` varchar(100) NOT NULL DEFAULT '' COMMENT '请求地址',"
sql += "`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',"
sql += "`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',"
sql += "`created_user` varchar(60) NOT NULL DEFAULT '' COMMENT '创建人',"
sql += "`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',"
sql += "`updated_user` varchar(60) NOT NULL DEFAULT '' COMMENT '更新人',"
sql += "PRIMARY KEY (`id`)"
sql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='已授权接口地址表';"
return
}
func CreateAuthorizedAPITableDataSql() (sql string) {
sql = "INSERT INTO `authorized_api` (`id`, `business_key`, `method`, `api`,`created_user`) VALUES"
sql += "(1, 'admin', 'GET', '/api/**', 'init'),"
sql += "(2, 'admin', 'POST', '/api/**', 'init'),"
sql += "(3, 'admin', 'PUT', '/api/**', 'init'),"
sql += "(4, 'admin', 'DELETE', '/api/**', 'init'),"
sql += "(5, 'admin', 'PATCH', '/api/**', 'init');"
return
}

View File

@@ -0,0 +1,27 @@
package mysql_table
//CREATE TABLE `user_demo` (
//`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
//`user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',
//`nick_name` varchar(100) NOT NULL DEFAULT '' COMMENT '昵称',
//`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',
//`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',
//`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
//`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
//PRIMARY KEY (`id`)
//) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户Demo表';
func CreateUserDemoTableSql() (sql string) {
sql = "CREATE TABLE `user_demo` ("
sql += "`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',"
sql += "`user_name` varchar(32) NOT NULL DEFAULT '' COMMENT '用户名',"
sql += "`nick_name` varchar(100) NOT NULL DEFAULT '' COMMENT '昵称',"
sql += "`mobile` varchar(20) NOT NULL DEFAULT '' COMMENT '手机号',"
sql += "`is_deleted` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否删除 1:是 -1:否',"
sql += "`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',"
sql += "`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',"
sql += "PRIMARY KEY (`id`)"
sql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户Demo表';"
return
}