Browse Source

Merge pull request #532 from flipped-aurora/gva_gormv2_dev

Gva gormv2 dev
main
奇淼(piexlmax 3 years ago
committed by GitHub
parent
commit
09fe9f3cff
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      server/api/v1/sys_initdb.go
  2. 24
      server/api/v1/sys_user.go
  3. 1
      server/config.yaml
  4. 1
      server/model/sys_auto_code.go
  5. 3
      server/router/sys_base.go
  6. 4
      server/service/exa_excel_parse.go
  7. 3
      server/service/jwt_black_list.go
  8. 9
      server/service/sys_auto_code.go
  9. 33
      server/service/sys_initdb.go
  10. 2
      server/utils/upload/aliyun_oss.go
  11. 1
      server/utils/upload/qiniu.go
  12. 1
      server/utils/upload/tencent_cos.go
  13. 6
      web/src/App.vue
  14. 23
      web/src/utils/request.js
  15. 22
      web/src/view/init/index.vue
  16. 2
      web/src/view/layout/bottomInfo/bottomInfo.vue
  17. 25
      web/src/view/login/index.vue
  18. 157
      web/src/view/login/login.vue
  19. 4
      web/src/view/systemTools/autoCode/index.vue

7
server/api/v1/sys_initdb.go

@ -5,6 +5,7 @@ import (
"gin-vue-admin/model/request"
"gin-vue-admin/model/response"
"gin-vue-admin/service"
"go.uber.org/zap"
"github.com/gin-gonic/gin"
@ -18,8 +19,8 @@ import (
// @Router /init/initdb [post]
func InitDB(c *gin.Context) {
if global.GVA_DB != nil {
global.GVA_LOG.Error("非法访问!")
response.FailWithMessage("非法访问", c)
global.GVA_LOG.Error("已存在数据库配置!")
response.FailWithMessage("已存在数据库配置", c)
return
}
var dbInfo request.InitDB
@ -30,7 +31,7 @@ func InitDB(c *gin.Context) {
}
if err := service.InitDB(dbInfo); err != nil {
global.GVA_LOG.Error("自动创建数据库失败!", zap.Any("err", err))
response.FailWithMessage("自动创建数据库失败,请查看后台日志", c)
response.FailWithMessage("自动创建数据库失败,请查看后台日志,检查后在进行初始化", c)
return
}
response.OkWithData("自动创建数据库成功", c)

24
server/api/v1/sys_user.go

@ -23,15 +23,15 @@ import (
// @Success 200 {string} string "{"success":true,"data":{},"msg":"登陆成功"}"
// @Router /base/login [post]
func Login(c *gin.Context) {
var L request.Login
_ = c.ShouldBindJSON(&L)
if err := utils.Verify(L, utils.LoginVerify); err != nil {
var l request.Login
_ = c.ShouldBindJSON(&l)
if err := utils.Verify(l, utils.LoginVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if store.Verify(L.CaptchaId, L.Captcha, true) {
U := &model.SysUser{Username: L.Username, Password: L.Password}
if err, user := service.Login(U); err != nil {
if store.Verify(l.CaptchaId, l.Captcha, true) {
u := &model.SysUser{Username: l.Username, Password: l.Password}
if err, user := service.Login(u); err != nil {
global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Any("err", err))
response.FailWithMessage("用户名不存在或者密码错误", c)
} else {
@ -112,13 +112,13 @@ func tokenNext(c *gin.Context, user model.SysUser) {
// @Success 200 {string} string "{"success":true,"data":{},"msg":"注册成功"}"
// @Router /user/register [post]
func Register(c *gin.Context) {
var R request.Register
_ = c.ShouldBindJSON(&R)
if err := utils.Verify(R, utils.RegisterVerify); err != nil {
var r request.Register
_ = c.ShouldBindJSON(&r)
if err := utils.Verify(r, utils.RegisterVerify); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
user := &model.SysUser{Username: R.Username, NickName: R.NickName, Password: R.Password, HeaderImg: R.HeaderImg, AuthorityId: R.AuthorityId}
user := &model.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId}
err, userReturn := service.Register(*user)
if err != nil {
global.GVA_LOG.Error("注册失败!", zap.Any("err", err))
@ -142,8 +142,8 @@ func ChangePassword(c *gin.Context) {
response.FailWithMessage(err.Error(), c)
return
}
U := &model.SysUser{Username: user.Username, Password: user.Password}
if err, _ := service.ChangePassword(U, user.NewPassword); err != nil {
u := &model.SysUser{Username: user.Username, Password: user.Password}
if err, _ := service.ChangePassword(u, user.NewPassword); err != nil {
global.GVA_LOG.Error("修改失败!", zap.Any("err", err))
response.FailWithMessage("修改失败,原密码与当前账户不符", c)
} else {

1
server/config.yaml

@ -53,6 +53,7 @@ captcha:
img-height: 80
# mysql connect configuration
# 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://www.gin-vue-admin.com/docs/first)
mysql:
path: ''
config: ''

1
server/model/sys_auto_code.go

@ -7,6 +7,7 @@ type AutoCodeStruct struct {
StructName string `json:"structName"` // Struct名称
TableName string `json:"tableName"` // 表名
PackageName string `json:"packageName"` // 文件名称
HumpPackageName string `json:"humpPackageName"` // go文件名称
Abbreviation string `json:"abbreviation"` // Struct简称
Description string `json:"description"` // Struct中文名称
AutoCreateApiToSql bool `json:"autoCreateApiToSql"` // 是否自动创建api

3
server/router/sys_base.go

@ -2,12 +2,11 @@ package router
import (
"gin-vue-admin/api/v1"
"gin-vue-admin/middleware"
"github.com/gin-gonic/gin"
)
func InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) {
BaseRouter := Router.Group("base").Use(middleware.NeedInit())
BaseRouter := Router.Group("base")
{
BaseRouter.POST("login", v1.Login)
BaseRouter.POST("captcha", v1.Captcha)

4
server/service/exa_excel_parse.go

@ -24,8 +24,8 @@ func ParseInfoList2Excel(infoList []model.SysBaseMenu, filePath string) error {
menu.Component,
})
}
excel.SaveAs(filePath)
return nil
err := excel.SaveAs(filePath)
return err
}
func ParseExcel2InfoList() ([]model.SysBaseMenu, error) {

3
server/service/jwt_black_list.go

@ -26,7 +26,8 @@ func JsonInBlacklist(jwtList model.JwtBlacklist) (err error) {
//@return: bool
func IsBlacklist(jwt string) bool {
isNotFound := errors.Is(global.GVA_DB.Where("jwt = ?", jwt).First(&model.JwtBlacklist{}).Error, gorm.ErrRecordNotFound)
err := global.GVA_DB.Where("jwt = ?", jwt).First(&model.JwtBlacklist{}).Error
isNotFound := errors.Is(err, gorm.ErrRecordNotFound)
return !isNotFound
}

9
server/service/sys_auto_code.go

@ -360,8 +360,15 @@ func getNeedList(autoCode *model.AutoCodeStruct) (dataList []tplData, fileList [
origFileName := strings.TrimSuffix(trimBase[lastSeparator+1:], ".tpl")
firstDot := strings.Index(origFileName, ".")
if firstDot != -1 {
var fileName string
if origFileName[firstDot:] !=".go"{
fileName = autoCode.PackageName+origFileName[firstDot:]
}else{
fileName = autoCode.HumpPackageName+origFileName[firstDot:]
}
dataList[index].autoCodePath = filepath.Join(autoPath, trimBase[:lastSeparator], autoCode.PackageName,
origFileName[:firstDot], autoCode.PackageName+origFileName[firstDot:])
origFileName[:firstDot], fileName)
}
}

33
server/service/sys_initdb.go

@ -9,10 +9,11 @@ import (
"gin-vue-admin/model/request"
"gin-vue-admin/source"
"gin-vue-admin/utils"
"path/filepath"
"github.com/spf13/viper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"path/filepath"
)
//@author: [songzhibin97](https://github.com/songzhibin97)
@ -71,13 +72,6 @@ func initDB(InitDBFunctions ...model.InitDBFunc) (err error) {
//@return: error
func InitDB(conf request.InitDB) error {
BaseMysql := config.Mysql{
Path: "",
Dbname: "",
Username: "",
Password: "",
Config: "charset=utf8mb4&parseTime=True&loc=Local",
}
if conf.Host == "" {
conf.Host = "127.0.0.1"
@ -100,15 +94,11 @@ func InitDB(conf request.InitDB) error {
Config: "charset=utf8mb4&parseTime=True&loc=Local",
}
if err := writeConfig(global.GVA_VP, MysqlConfig); err != nil {
return err
}
m := global.GVA_CONFIG.Mysql
if m.Dbname == "" {
if MysqlConfig.Dbname == "" {
return nil
}
linkDns := m.Username + ":" + m.Password + "@tcp(" + m.Path + ")/" + m.Dbname + "?" + m.Config
linkDns := MysqlConfig.Username + ":" + MysqlConfig.Password + "@tcp(" + MysqlConfig.Path + ")/" + MysqlConfig.Dbname + "?" + MysqlConfig.Config
mysqlConfig := mysql.Config{
DSN: linkDns, // DSN data source name
DefaultStringSize: 191, // string 类型字段的默认长度
@ -118,15 +108,11 @@ func InitDB(conf request.InitDB) error {
SkipInitializeWithVersion: false, // 根据版本自动配置
}
if db, err := gorm.Open(mysql.New(mysqlConfig), &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}); err != nil {
//global.GVA_LOG.Error("MySQL启动异常!", zap.Any("err", err))
//os.Exit(0)
//return nil
_ = writeConfig(global.GVA_VP, BaseMysql)
return nil
} else {
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(m.MaxIdleConns)
sqlDB.SetMaxOpenConns(m.MaxOpenConns)
sqlDB.SetMaxIdleConns(MysqlConfig.MaxIdleConns)
sqlDB.SetMaxOpenConns(MysqlConfig.MaxOpenConns)
global.GVA_DB = db
}
@ -147,7 +133,7 @@ func InitDB(conf request.InitDB) error {
model.SysOperationRecord{},
)
if err != nil {
_ = writeConfig(global.GVA_VP, BaseMysql)
global.GVA_DB = nil
return err
}
err = initDB(
@ -163,7 +149,10 @@ func InitDB(conf request.InitDB) error {
source.File,
source.BaseMenu)
if err != nil {
_ = writeConfig(global.GVA_VP, BaseMysql)
global.GVA_DB = nil
return err
}
if err = writeConfig(global.GVA_VP, MysqlConfig); err != nil {
return err
}
global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")

2
server/utils/upload/aliyun_oss.go

@ -25,7 +25,7 @@ func (*AliyunOSS) UploadFile(file *multipart.FileHeader) (string, string, error)
global.GVA_LOG.Error("function file.Open() Failed", zap.Any("err", openError.Error()))
return "", "", errors.New("function file.Open() Failed, err:" + openError.Error())
}
defer f.Close() // 创建文件 defer 关闭
// 上传阿里云路径 文件名格式 自己可以改 建议保证唯一性
yunFileTmpPath := filepath.Join("uploads", time.Now().Format("2006-01-02")) + "/" + file.Filename

1
server/utils/upload/qiniu.go

@ -38,6 +38,7 @@ func (*Qiniu) UploadFile(file *multipart.FileHeader) (string, string, error) {
return "", "", errors.New("function file.Open() Filed, err:" + openError.Error())
}
defer f.Close() // 创建文件 defer 关闭
fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename) // 文件名格式 自己可以改 建议保证唯一性
putErr := formUploader.Put(context.Background(), &ret, upToken, fileKey, f, file.Size, &putExtra)
if putErr != nil {

1
server/utils/upload/tencent_cos.go

@ -24,6 +24,7 @@ func (*TencentCOS) UploadFile(file *multipart.FileHeader) (string, string, error
global.GVA_LOG.Error("function file.Open() Filed", zap.Any("err", openError.Error()))
return "", "", errors.New("function file.Open() Filed, err:" + openError.Error())
}
defer f.Close() // 创建文件 defer 关闭
fileKey := fmt.Sprintf("%d%s", time.Now().Unix(), file.Filename)
_, err := client.Object.Put(context.Background(), global.GVA_CONFIG.TencentCOS.PathPrefix+"/"+fileKey, f, nil)

6
web/src/App.vue

@ -5,12 +5,8 @@
</template>
<script>
import { checkDB } from '@/api/initdb'
export default {
name: 'App',
async created() {
await checkDB()
}
name: 'App'
}
</script>

23
web/src/utils/request.js

@ -2,7 +2,7 @@ import axios from 'axios' // 引入axios
import { Message } from 'element-ui'
import { store } from '@/store'
import context from '@/main'
import router from '@/router/index'
import { MessageBox } from 'element-ui'
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
@ -64,16 +64,6 @@ service.interceptors.response.use(
if (response.headers['new-token']) {
store.commit('user/setToken', response.headers['new-token'])
}
if (response.data.code === 0) {
if (response.data.data?.needInit) {
Message({
type: 'info',
message: '您是第一次使用,请初始化'
})
store.commit('user/NeedInit')
router.push({ name: 'Init' })
}
}
if (response.data.code === 0 || response.headers.success === 'true') {
return response.data
} else {
@ -90,10 +80,13 @@ service.interceptors.response.use(
},
error => {
closeLoading()
Message({
showClose: true,
message: error,
type: 'error'
MessageBox.confirm(`检测到接口错误${error},此类错误内容常见于后台panic,如果影响您正常使用可强制登出清理缓存`, '接口报错', {
distinguishCancelAndClose: true,
confirmButtonText: '清理缓存',
cancelButtonText: '取消'
})
.then(() => {
store.commit('user/LoginOut')
})
return error
}

22
web/src/view/init/index.vue

@ -1,11 +1,21 @@
<template>
<div class="init_page">
<div class="init_page_panle">
<div v-if="hello < 2" id="hello" :class="[hello < 1 ? 'slide-in-fwd-top' : 'slide-out-right']" class="hello " @click="showNext">
<div v-if="hello < 2" id="hello" :class="[hello < 1 ? 'slide-in-fwd-top' : 'slide-out-right']" class="hello ">
<div>
<div class="hello_title">GIN-VUE-ADMIN</div>
<p class="in-two a-fadeinT">您需要初始化您的数据库并且填充初始数据</p>
<p class="init_p">点击进入初始化</p>
<p class="in-two a-fadeinT">初始化须知</p>
<p class="init_p">1.您需有用一定的VUE和golang基础</p>
<p class="init_p">2.请您确认是否已经阅读过官方文档</p>
<p class="init_p">3.请您确认是否了解后续的配置流程</p>
<p class="init_btn">
<el-button type="primary" @click="goDoc">
阅读文档
</el-button>
<el-button type="primary" @click="showNext">
我已确认
</el-button>
</p>
</div>
</div>
<div v-if="hello > 0 " :class="[(hello > 0 && !out)? 'slide-in-left' : '' , out ? 'slide-out-right' : '']" class=" form">
@ -73,6 +83,9 @@ export default {
this.hello = this.hello + 1
console.log(this.hello)
},
goDoc() {
window.open('https://www.gin-vue-admin.com')
},
async onSubmit() {
const loading = this.$loading({
lock: true,
@ -141,6 +154,9 @@ export default {
margin-top: 20px;
color: #777777;
}
.init_btn{
margin-top: 20px;
}
}
.form{
position: absolute;

2
web/src/view/layout/bottomInfo/bottomInfo.vue

@ -3,7 +3,7 @@
<div>
<span>Powered by</span>
<span>
<a href="https://github.com/flipped-aurora/gin-vue-admin">{{$GIN_VUE_ADMIN.appName}}</a>
<a href="https://github.com/flipped-aurora/gin-vue-admin">{{ $GIN_VUE_ADMIN.appName }}</a>
</span>
<el-divider direction="vertical" />
<span>Copyright</span>

25
web/src/view/login/index.vue

@ -3,7 +3,7 @@
<div class="login_panle">
<div class="login_panle_form">
<div class="login_panle_form_title">
<img class="login_panle_form_title_logo" :src="$GIN_VUE_ADMIN.appLogo" alt=""><p class="login_panle_form_title_p">{{$GIN_VUE_ADMIN.appName}}</p>
<img class="login_panle_form_title_logo" :src="$GIN_VUE_ADMIN.appLogo" alt=""><p class="login_panle_form_title_p">{{ $GIN_VUE_ADMIN.appName }}</p>
</div>
<el-form
ref="loginForm"
@ -47,10 +47,16 @@
>
</div>
</el-form-item>
<div />
<el-form-item>
<el-button
type="primary"
style="width: 100%"
style="width: 46%"
@click="checkInit"
>前往初始化</el-button>
<el-button
type="primary"
style="width: 46%;margin-left:8%"
@click="submitForm"
> </el-button>
</el-form-item>
@ -73,6 +79,7 @@
<script>
import { mapActions } from 'vuex'
import { captcha } from '@/api/user'
import { checkDB } from '@/api/initdb'
export default {
name: 'Login',
data() {
@ -113,6 +120,20 @@ export default {
},
methods: {
...mapActions('user', ['LoginIn']),
async checkInit() {
const res = await checkDB()
if (res.code === 0) {
if (res.data?.needInit) {
this.$store.commit('user/NeedInit')
this.$router.push({ name: 'Init' })
} else {
this.$message({
type: 'info',
message: '已配置数据库信息,无法初始化'
})
}
}
},
async login() {
return await this.LoginIn(this.loginForm)
},

157
web/src/view/login/login.vue

@ -1,157 +0,0 @@
<template>
<div id="userLayout" class="user-layout-wrapper">
<div class="container">
<div class="top">
<div class="desc">
<img class="logo_login" src="@/assets/logo_login.png" alt="">
</div>
<div class="header">
<a href="/"><span class="title">Gin-Vue-Admin</span></a>
</div>
</div>
<div class="main">
<el-form
ref="loginForm"
:model="loginForm"
:rules="rules"
@keyup.enter.native="submitForm"
>
<el-form-item prop="username">
<el-input v-model="loginForm.username" placeholder="请输入用户名">
<i slot="suffix" class="el-input__icon el-icon-user" />
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input
v-model="loginForm.password"
:type="lock === 'lock' ? 'password' : 'text'"
placeholder="请输入密码"
>
<i
slot="suffix"
:class="'el-input__icon el-icon-' + lock"
@click="changeLock"
/>
</el-input>
</el-form-item>
<el-form-item style="position: relative">
<el-input
v-model="loginForm.captcha"
name="logVerify"
placeholder="请输入验证码"
style="width: 60%"
/>
<div class="vPic">
<img
v-if="picPath"
:src="picPath"
width="100%"
height="100%"
alt="请输入验证码"
@click="loginVerify()"
>
</div>
</el-form-item>
<el-form-item>
<el-button
type="primary"
style="width: 100%"
@click="submitForm"
> </el-button>
</el-form-item>
</el-form>
</div>
<div class="footer">
<div class="links">
<a href="http://doc.henrongyi.top/"><img src="@/assets/docs.png" class="link-icon"></a>
<a href="https://www.yuque.com/flipped-aurora/"><img src="@/assets/yuque.png" class="link-icon"></a>
<a href="https://github.com/flipped-aurora/gin-vue-admin"><img src="@/assets/github.png" class="link-icon"></a>
<a href="https://space.bilibili.com/322210472"><img src="@/assets/video.png" class="link-icon"></a>
</div>
<div class="copyright">Copyright &copy; {{ curYear }} 💖flipped-aurora</div>
</div>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex'
import { captcha } from '@/api/user'
export default {
name: 'Login',
data() {
const checkUsername = (rule, value, callback) => {
if (value.length < 5) {
return callback(new Error('请输入正确的用户名'))
} else {
callback()
}
}
const checkPassword = (rule, value, callback) => {
if (value.length < 6) {
return callback(new Error('请输入正确的密码'))
} else {
callback()
}
}
return {
curYear: 0,
lock: 'lock',
loginForm: {
username: 'admin',
password: '123456',
captcha: '',
captchaId: ''
},
rules: {
username: [{ validator: checkUsername, trigger: 'blur' }],
password: [{ validator: checkPassword, trigger: 'blur' }]
},
logVerify: '',
picPath: ''
}
},
created() {
this.loginVerify()
this.curYear = new Date().getFullYear()
},
methods: {
...mapActions('user', ['LoginIn']),
async login() {
return await this.LoginIn(this.loginForm)
},
async submitForm() {
this.$refs.loginForm.validate(async(v) => {
if (v) {
const flag = await this.login()
if (!flag) {
this.loginVerify()
}
} else {
this.$message({
type: 'error',
message: '请正确填写登录信息',
showClose: true
})
this.loginVerify()
return false
}
})
},
changeLock() {
this.lock = this.lock === 'lock' ? 'unlock' : 'lock'
},
loginVerify() {
captcha({}).then((ele) => {
this.picPath = ele.data.picPath
this.loginForm.captchaId = ele.data.captchaId
})
}
}
}
</script>
<style scoped lang="scss">
@import "@/style/login.scss";
</style>

4
web/src/view/systemTools/autoCode/index.vue

@ -154,7 +154,7 @@ const fieldTemplate = {
import FieldDialog from '@/view/systemTools/autoCode/component/fieldDialog.vue'
import PreviewCodeDialg from '@/view/systemTools/autoCode/component/previewCodeDialg.vue'
import { toUpperCase, toHump } from '@/utils/stringFun'
import { toUpperCase, toHump, toSQLLine } from '@/utils/stringFun'
import { createTemp, getDB, getTable, getColumn, preview } from '@/api/autoCode'
import { getDict } from '@/utils/dictionary'
@ -294,6 +294,8 @@ export default {
})
return false
}
this.form.humpPackageName = toSQLLine(this.form.packageName)
debugger
if (isPreview) {
const data = await preview(this.form)
this.preViewCode = data.data.autoCode

Loading…
Cancel
Save