Golang学习日志 ━━ gin-vue-admin插件开发记录

news2024/11/17 16:23:38

gin-vue-admin是一套国人用golang开发的后台管理系统,本文记录插件开发内容。
官网:https://www.gin-vue-admin.com/
学习视频:https://www.bilibili.com/video/BV1kv4y1g7nT/

在这里插入图片描述

插件目录

后端位置:\server\plugin\
前端位置:\web\src\plugin\

建议插件目录采用:家族标识\插件名\
例如test插件目录:abc123\test\

这么安排目录的原因是尽量不要把同一个项目打碎后分配到各个目录去,而是集中在一起,方便未来备份、转移等。
gin-vue-admin的plugin相当于一个五脏俱全的app,所以可以实现上面所建议的插件式目录分配方案。

现在正式开始

后端

\server\plugin\abc123\test\api\
\server\plugin\abc123\test\api\api.go
\server\plugin\abc123\test\api\enter.go
\server\plugin\abc123\test\config\
\server\plugin\abc123\test\config\config.go
\server\plugin\abc123\test\global\
\server\plugin\abc123\test\global\global.go
\server\plugin\abc123\test\model\
\server\plugin\abc123\test\model\request
\server\plugin\abc123\test\model\request\cms.go
\server\plugin\abc123\test\model\model.go
\server\plugin\abc123\test\router\
\server\plugin\abc123\test\router\enter.go
\server\plugin\abc123\test\router\router.go
\server\plugin\abc123\test\service\
\server\plugin\abc123\test\router\enter.go
\server\plugin\abc123\test\router\service.go
\server\plugin\abc123\test\main.go

在这里插入图片描述
看得出来后端基本就是一个完整的应用~~

前端

\web\src\plugin\abc123\test\api\
\web\src\plugin\abc123\test\api\cms.js
\web\src\plugin\abc123\test\view\cms\
\web\src\plugin\abc123\test\view\cms\cms.vue
\web\src\plugin\abc123\test\view\setting\
\web\src\plugin\abc123\test\view\setting\a.vue
\web\src\plugin\abc123\test\view\setting\b.vue

在这里插入图片描述

本例中前端实际就涉及:

  • view中的cms.vue用于界面设置;
  • api中的cms.js,用于和后端通讯;

代码示例

详细开发可以看官方文档,也可以用自动化代码管理器生成后拷贝其代码至插件目录。

Golang学习日志 ━━ Gin-Vue-Admin按步骤手动创建api及router、service

后端

后端文件比较多,只举例一部分。

  • \server\plugin\abc123\test\api\api.go
package api

import (
	"github.com/flipped-aurora/gin-vue-admin/server/global"
	"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
	"github.com/flipped-aurora/gin-vue-admin/server/model/common/response"
	"github.com/flipped-aurora/gin-vue-admin/server/plugin/abc123/test/model"
	appReq "github.com/flipped-aurora/gin-vue-admin/server/plugin/abc123/test/model/request"
	"github.com/flipped-aurora/gin-vue-admin/server/plugin/abc123/test/service"
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

type ABC123AppApi struct{}

var appService = service.ServiceGroupApp.ABC123AppService

// @Tags ABC123App
// @Summary 请手动填写接口功能
// @Produce  application/json
// @Success 200 {string} string "{"success":true,"data":{},"msg":"发送成功"}"
// @Router /abc123/app/routerName [post]
func (p *ABC123AppApi) ApiName(c *gin.Context) {

	var plug model.Request
	_ = c.ShouldBindJSON(&plug)

	if res, err := service.ServiceGroupApp.PlugService(plug); err != nil {
		global.GVA_LOG.Error("失败!", zap.Error(err))
		response.FailWithMessage("失败", c)
	} else {

		response.OkWithDetailed(res, "成功", c)
	}
}

// CreateTest 创建Test
// @Tags Test
// @Summary 创建Test
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.Test true "创建Test"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /abc123/app/createTest [post]
func (appApi *ABC123AppApi) CreateTest(c *gin.Context) {
	var data model.Test
	err := c.ShouldBindJSON(&data)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}

	if err := appService.CreateTest(data); err != nil {
		global.GVA_LOG.Error("创建失败!", zap.Error(err))
		response.FailWithMessage("创建失败", c)
	} else {
		response.OkWithMessage("创建成功", c)
	}
}

// DeleteTest 删除Test
// @Tags Test
// @Summary 删除Test
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.Test true "删除Test"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /abc123/app/deleteTest [delete]
func (appApi *ABC123AppApi) DeleteTest(c *gin.Context) {
	var data model.Test
	err := c.ShouldBindJSON(&data)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}

	if err := appService.DeleteTest(data); err != nil {
		global.GVA_LOG.Error("删除失败!", zap.Error(err))
		response.FailWithMessage("删除失败", c)
	} else {
		response.OkWithMessage("删除成功", c)
	}
}

// DeleteTestByIds 批量删除Test
// @Tags Test
// @Summary 批量删除Test
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "批量删除Test"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"批量删除成功"}"
// @Router /abc123/app/deleteTestByIds [delete]
func (appApi *ABC123AppApi) DeleteTestByIds(c *gin.Context) {
	var IDS request.IdsReq
	err := c.ShouldBindJSON(&IDS)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}

	if err := appService.DeleteTestByIds(IDS); err != nil {
		global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
		response.FailWithMessage("批量删除失败", c)
	} else {
		response.OkWithMessage("批量删除成功", c)
	}
}

// UpdateTest 更新Test
// @Tags Test
// @Summary 更新Test
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.Test true "更新Test"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /abc123/app/updateTest [put]
func (appApi *ABC123AppApi) UpdateTest(c *gin.Context) {
	var data model.Test
	err := c.ShouldBindJSON(&data)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}

	if err := appService.UpdateTest(data); err != nil {
		global.GVA_LOG.Error("更新失败!", zap.Error(err))
		response.FailWithMessage("更新失败", c)
	} else {
		response.OkWithMessage("更新成功", c)
	}
}

// FindTest 用id查询Test
// @Tags Test
// @Summary 用id查询Test
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query model.Test true "用id查询Test"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /abc123/app/findTest [get]
func (appApi *ABC123AppApi) FindTest(c *gin.Context) {
	var data model.Test
	err := c.ShouldBindQuery(&data)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}

	if redata, err := appService.GetTest(data.ID); err != nil {
		global.GVA_LOG.Error("查询失败!", zap.Error(err))
		response.FailWithMessage("查询失败", c)
	} else {
		response.OkWithData(gin.H{"redata": redata}, c)
	}
}

// GetTestList 分页获取Test列表
// @Tags Test
// @Summary 分页获取Test列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query appReq.TestSearch true "分页获取Test列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /abc123/app/getTestList [get]
func (appApi *ABC123AppApi) GetTestList(c *gin.Context) {
	var pageInfo appReq.TestSearch
	err := c.ShouldBindQuery(&pageInfo)
	if err != nil {
		response.FailWithMessage(err.Error(), c)
		return
	}

	if list, total, err := appService.GetTestInfoList(pageInfo); err != nil {
		global.GVA_LOG.Error("获取失败!", zap.Error(err))
		response.FailWithMessage("获取失败", c)
	} else {
		response.OkWithDetailed(response.PageResult{
			List:     list,
			Total:    total,
			Page:     pageInfo.Page,
			PageSize: pageInfo.PageSize,
		}, "获取成功", c)
	}
}

  • \server\plugin\abc123\test\router\router.go
package router

import (
	"github.com/flipped-aurora/gin-vue-admin/server/middleware"
	"github.com/flipped-aurora/gin-vue-admin/server/plugin/abc123/test/api"
	"github.com/gin-gonic/gin"
)

type ABC123AppRouter struct {
}

func (s *ABC123AppRouter) InitABC123AppRouter(Router *gin.RouterGroup) {
	plugRouter := Router.Use(middleware.OperationRecord()) // 行为记录 middleware.OperationRecord()
	plugRouterWithoutRecord := Router

	plugApi := api.ApiGroupApp.ABC123AppApi
	{
		plugRouter.POST("routerName", plugApi.ApiName)
	}
	{
		plugRouter.POST("createTest", plugApi.CreateTest)             // 新建Test
		plugRouter.DELETE("deleteTest", plugApi.DeleteTest)           // 删除Test
		plugRouter.DELETE("deleteTestByIds", plugApi.DeleteTestByIds) // 批量删除Test
		plugRouter.PUT("updateTest", plugApi.UpdateTest)              // 更新Test
	}
	{
		plugRouterWithoutRecord.GET("findTest", plugApi.FindTest)       // 根据ID获取Test
		plugRouterWithoutRecord.GET("getTestList", plugApi.GetTestList) // 获取Test列表
	}
}

前端

  • \web\src\plugin\abc123\test\api\cms.vue
<template>
  <div>
    <div class="gva-search-box">
      <el-form :inline="true" :model="searchInfo" class="demo-form-inline">
      <el-form-item label="创建时间">
      <el-date-picker v-model="searchInfo.startCreatedAt" type="datetime" placeholder="开始时间"></el-date-picker><el-date-picker v-model="searchInfo.endCreatedAt" type="datetime" placeholder="结束时间"></el-date-picker>
      </el-form-item>
        <el-form-item>
          <el-button size="small" type="primary" icon="search" @click="onSubmit">查询</el-button>
          <el-button size="small" icon="refresh" @click="onReset">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="gva-table-box">
        <div class="gva-btn-list">
            <el-button size="small" type="primary" icon="plus" @click="openDialog">新增</el-button>
            <el-popover v-model:visible="deleteVisible" placement="top" width="160">
            <p>确定要删除吗?</p>
            <div style="text-align: right; margin-top: 8px;">
                <el-button size="small" type="primary" link @click="deleteVisible = false">取消</el-button>
                <el-button size="small" type="primary" @click="onDelete">确定</el-button>
            </div>
            <template #reference>
                <el-button icon="delete" size="small" style="margin-left: 10px;" :disabled="!multipleSelection.length" @click="deleteVisible = true">删除</el-button>
            </template>
            </el-popover>
        </div>
        <el-table
        ref="multipleTable"
        style="width: 100%"
        tooltip-effect="dark"
        :data="tableData"
        row-key="ID"
        @selection-change="handleSelectionChange"
        >
        <el-table-column type="selection" width="55" />
        <el-table-column align="left" label="日期" width="180">
            <template #default="scope">{{ formatDate(scope.row.CreatedAt) }}</template>
        </el-table-column>
        <el-table-column align="left" label="title字段" prop="title" width="120" />
        <el-table-column align="left" label="content字段" prop="content" width="120" />
         <el-table-column align="left" label="showtime字段" width="180">
            <template #default="scope">{{ formatDate(scope.row.showtime) }}</template>
         </el-table-column>
        <el-table-column align="left" label="按钮组">
            <template #default="scope">
            <el-button type="primary" link icon="edit" size="small" class="table-button" @click="updateCmsFunc(scope.row)">变更</el-button>
            <el-button type="primary" link icon="delete" size="small" @click="deleteRow(scope.row)">删除</el-button>
            </template>
        </el-table-column>
        </el-table>
        <div class="gva-pagination">
            <el-pagination
            layout="total, sizes, prev, pager, next, jumper"
            :current-page="page"
            :page-size="pageSize"
            :page-sizes="[10, 30, 50, 100]"
            :total="total"
            @current-change="handleCurrentChange"
            @size-change="handleSizeChange"
            />
        </div>
    </div>
    <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" title="弹窗操作" :destroy-on-close="true">
      <el-form :model="formData" label-position="right" ref="elFormRef" :rules="rule" label-width="80px">
        <el-form-item label="标题:"  prop="title" >
          <el-input v-model="formData.title" :clearable="true"  placeholder="请输入" />
        </el-form-item>
        <el-form-item label="内容:"  prop="content" >
          <!--el-input id="editor" v-model="formData.content" :clearable="true"  placeholder="请输入" /-->
          <Editor
            :api-key="tinymceApiKey" 
            :init="tinymceInit" 
            v-model="formData.content" 
          />
        </el-form-item>
        <el-form-item label="显示时间:"  prop="showtime" >
          <el-date-picker v-model="formData.showtime" type="date" style="width:100%" placeholder="选择日期" :clearable="true"  />
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button size="small" @click="closeDialog">取 消</el-button>
          <el-button size="small" type="primary" @click="enterDialog">确 定</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script>
export default {
  name: 'Cms'
}
</script>

<script setup>
import {
  createCms,
  deleteCms,
  deleteCmsByIds,
  updateCms,
  findCms,
  getCmsList
} from '@/plugin/abc123/test/api/cms'

// 全量引入格式化工具 请按需保留
import { getDictFunc, formatDate, formatBoolean, filterDict } from '@/utils/format'
import { ElMessage, ElMessageBox } from 'element-plus'
import { ref, reactive } from 'vue'

const elFormRef = ref()


// =========== 表格控制部分 ===========
const page = ref(1)
const total = ref(0)
const pageSize = ref(10)
const tableData = ref([])
const searchInfo = ref({})

// 重置
const onReset = () => {
  searchInfo.value = {}
  getTableData()
}

// 搜索
const onSubmit = () => {
  page.value = 1
  pageSize.value = 10
  getTableData()
}

// 分页
const handleSizeChange = (val) => {
  pageSize.value = val
  getTableData()
}

// 修改页面容量
const handleCurrentChange = (val) => {
  page.value = val
  getTableData()
}

// 查询
const getTableData = async() => {
  const table = await getCmsList({ page: page.value, pageSize: pageSize.value, ...searchInfo.value })
  if (table.code === 0) {
    tableData.value = table.data.list
    total.value = table.data.total
    page.value = table.data.page
    pageSize.value = table.data.pageSize
  }
}

getTableData()

// ============== 表格控制部分结束 ===============

// 获取需要的字典 可能为空 按需保留
const setOptions = async () =>{
}

// 获取需要的字典 可能为空 按需保留
setOptions()


// 多选数据
const multipleSelection = ref([])
// 多选
const handleSelectionChange = (val) => {
    multipleSelection.value = val
}

// 删除行
const deleteRow = (row) => {
    ElMessageBox.confirm('确定要删除吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
    }).then(() => {
            deleteCmsFunc(row)
        })
    }


// 批量删除控制标记
const deleteVisible = ref(false)

// 多选删除
const onDelete = async() => {
      const ids = []
      if (multipleSelection.value.length === 0) {
        ElMessage({
          type: 'warning',
          message: '请选择要删除的数据'
        })
        return
      }
      multipleSelection.value &&
        multipleSelection.value.map(item => {
          ids.push(item.ID)
        })
      const res = await deleteCmsByIds({ ids })
      if (res.code === 0) {
        ElMessage({
          type: 'success',
          message: '删除成功'
        })
        if (tableData.value.length === ids.length && page.value > 1) {
          page.value--
        }
        deleteVisible.value = false
        getTableData()
      }
    }

// 行为控制标记(弹窗内部需要增还是改)
const type = ref('')

// 更新行
const updateCmsFunc = async(row) => {
    const res = await findCms({ ID: row.ID })
    type.value = 'update'
    if (res.code === 0) {
        formData.value = res.data.redata
        dialogFormVisible.value = true
    }
}


// 删除行
const deleteCmsFunc = async (row) => {
    const res = await deleteCms({ ID: row.ID })
    if (res.code === 0) {
        ElMessage({
                type: 'success',
                message: '删除成功'
            })
            if (tableData.value.length === 1 && page.value > 1) {
            page.value--
        }
        getTableData()
    }
}

// 弹窗控制标记
const dialogFormVisible = ref(false)

// 打开弹窗
const openDialog = () => {
    type.value = 'create'
    dialogFormVisible.value = true
}

// 关闭弹窗
const closeDialog = () => {
    dialogFormVisible.value = false
    formData.value = {
        title: '',
        content: '',
        showtime: new Date(),
        }
}
// 弹窗确定
const enterDialog = async () => {
     elFormRef.value?.validate( async (valid) => {
             if (!valid) return
              let res
              switch (type.value) {
                case 'create':
                  res = await createCms(formData.value)
                  break
                case 'update':
                  res = await updateCms(formData.value)
                  break
                default:
                  res = await createCms(formData.value)
                  break
              }
              if (res.code === 0) {
                ElMessage({
                  type: 'success',
                  message: '创建/更改成功'
                })
                closeDialog()
                getTableData()
              }
      })
}
</script>

<style>
/* 在el-dialog中tinymce z-index 被太小而被遮挡时要加这两句 */
.tox-tinymce-aux{z-index:99999 !important;}
.tinymce.ui.FloatPanel{z-Index: 99;}
</style>

<style scoped>
@media (min-width: 1024px) {
  #sample {
    display: flex;
    flex-direction: column;
    place-items: center;
    width: 1000px;
  }
}
</style>
  • \web\src\plugin\abc123\test\api\cms.js
import service from '@/utils/request'

// @Tags Cms
// @Summary 创建Cms
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.Cms true "创建Cms"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /abc123/test/createCms [post]
export const createCms = (data) => {
  return service({
    url: '/abc123/test/createTest',
    method: 'post',
    data
  })
}

// @Tags Cms
// @Summary 删除Cms
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.Cms true "删除Cms"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /abc123/test/deleteCms [delete]
export const deleteCms = (data) => {
  return service({
    url: '/abc123/test/deleteTest',
    method: 'delete',
    data
  })
}

// @Tags Cms
// @Summary 删除Cms
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body request.IdsReq true "批量删除Cms"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
// @Router /abc123/test/deleteCms [delete]
export const deleteCmsByIds = (data) => {
  return service({
    url: '/abc123/test/deleteTestByIds',
    method: 'delete',
    data
  })
}

// @Tags Cms
// @Summary 更新Cms
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data body model.Cms true "更新Cms"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
// @Router /abc123/test/updateCms [put]
export const updateCms = (data) => {
  return service({
    url: '/abc123/test/updateTest',
    method: 'put',
    data
  })
}

// @Tags Cms
// @Summary 用id查询Cms
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query model.Cms true "用id查询Cms"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"查询成功"}"
// @Router /abc123/test/findCms [get]
export const findCms = (params) => {
  return service({
    url: '/abc123/test/findTest',
    method: 'get',
    params
  })
}

// @Tags Cms
// @Summary 分页获取Cms列表
// @Security ApiKeyAuth
// @accept application/json
// @Produce application/json
// @Param data query request.PageInfo true "分页获取Cms列表"
// @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
// @Router /abc123/test/getCmsList [get]
export const getCmsList = (params) => {
  return service({
    url: '/abc123/test/getTestList',
    method: 'get',
    params
  })
}

前后端通讯

现在前后端都开发好了,那么如何实现前后端沟通呢?

后端

注册插件即可,因为路由等已经在插件内部设置完成。

\server\initialize\plugin.go

package initialize

import (
	"fmt"

	"github.com/flipped-aurora/gin-vue-admin/server/global"
	"github.com/flipped-aurora/gin-vue-admin/server/middleware"
	...
	...
	...
	"github.com/flipped-aurora/gin-vue-admin/server/plugin/abc123/test" // 调用测试插件

	"github.com/flipped-aurora/gin-vue-admin/server/utils/plugin"
	"github.com/gin-gonic/gin"
)

func PluginInit(group *gin.RouterGroup, Plugin ...plugin.Plugin) {
	for i := range Plugin {
		PluginGroup := group.Group(Plugin[i].RouterPath())
		Plugin[i].Register(PluginGroup)
	}
}

func InstallPlugin(Router *gin.Engine) {
	PublicGroup := Router.Group("")
	fmt.Println("无鉴权插件安装==》", PublicGroup)
	PrivateGroup := Router.Group("")
	fmt.Println("鉴权插件安装==》", PrivateGroup)
	PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
	//  添加跟角色挂钩权限的插件 示例 本地示例模式于在线仓库模式注意上方的import 可以自行切换 效果相同
	...
	...
	...
	PluginInit(PrivateGroup, abc123_test.CreateABC123AppPlug("")) // 注册测试插件
}

前端

代码已经不需要修改什么了,只需要在后台中设置好api、页面,再对角色进行授权就行。

这部分了解gin-vue-admin的同学可以直接跳过了,因为这和非插件形式的操作是一致的。现在插件已经注册,路由也已经确定,基本没有新的内容了~~哈哈

  • api
    在这里插入图片描述

  • 菜单

在这里插入图片描述

  • 角色
  1. 菜单授权
    在这里插入图片描述
  2. api授权
    在这里插入图片描述

运行

测试环境运行如下命令:

后端

go run main.go

前端

npm run serve

界面

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/692378.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

信号与系统复习笔记——采样与通讯系统

信号与系统复习笔记——采样与通讯系统 采样定理 冲激串采样函数可表示为&#xff1a; p ( t ) ∑ n − ∞ ∞ δ ( t − n T ) p(t) \sum_{n-\infty}^{\infty} \delta(t - nT) p(t)n−∞∑∞​δ(t−nT) 周期 T T T 称为采样周期&#xff0c;而 ω s 1 T \omega_s …

7月29-31日·相约上海丨上海国际智能遮阳与建筑节能展览会即将举办

上海国际智能遮阳与建筑节能展览会&#xff08;ISSE&#xff09;即将盛大召开。这个七月&#xff0c;期待您的参与&#xff0c;一同共聚盛会&#xff01; 1、关于展会 国内建筑遮阳市场尚在快速发展期&#xff0c;随着社会经济的发展以及建筑节能环保概念的不断深入&#xff…

开发的功能不都是经过上线测试,为什么上线后还会那么多 Bug ?

你是否也经过这样的灵魂拷问&#xff1a;「开发的功能不都是经过上线测试的吗&#xff1f;为什么上线后还会那么多 Bug &#xff1f;」。 大家明明都很努力&#xff0c;为什么「输出」的结果没有更进一步&#xff1f;今天我们就水一水这个「狗血」话题&#xff0c;究竟是谁个锅…

一半以上的年轻人存款不足10万元,能带给我们什么思考?

目录 年轻人存款现状现在的年轻人真的没有存款意愿吗&#xff1f;为什么年轻人存款少&#xff1f;收入低&#xff0c;臣妾做不到啊生活成本高消费观念不同超前消费、过度负债存款意识弱 依据自身情况聊聊你的目前的存款在哪一个区间&#xff1f;你觉得存款难吗&#xff1f;谈谈…

Open62541 NodeSet loader 编译与使用

大多数的OPC UA 建模工具通常是将NodeSet 编译成为C 或者C# 的源代码&#xff0c;然后和Server 程序一起编译。比如uaModeler&#xff0c;Opc foundation 的UA ModelCompiler 以及Open62541 提供的nodeset_Compiler 都是如此&#xff0c;这种方式在载入配套规范的Nodeset 无疑是…

使用Jenkins构建发布一个简单的maven项目

上一章&#xff0c;完成了jenkins在ubuntu上的安装&#xff0c;这一章将使用单个Jenkins服务完成一个maven项目的打包和发布。 1、在Jenkins的管理页面中安装相关插件 用到的插件有&#xff1a;Maven Integration、Git、Publish Over SSH三个&#xff0c;在Dashboard -> M…

全国水系与流域(五级)矢量数据下载

1. 全国水系与流域&#xff08;五级&#xff09;矢量数据 全国共计3040条水系&#xff0c;4015个流域 全国水系与流域&#xff08;五级&#xff09;矢量数据 链接 解压密码&#xff1a;rserforum.com 说明&#xff1a;“流域等级划分包括1-5级&#xff0c;其中1-2级为一级支流…

从零开始理解Linux中断架构(12)--硬中断之中断控制器(GICV3)

前面我们搞清楚了elx_irq的外围部分,了解清楚了kernel_enter/kernel_exit大致的作用。本节我们进入到硬中断处理部分。硬中断处理程序的位置见下图: 硬中断处理主要的工作就是判断中断发起源,应答中断控制器,根据中断源查询出并调用该中断源的设备级处理函数。 前面…

pcl matlab 计算平面与空间三角形的交线

一、生成一个平面 过程&#xff1a; 单有法向量不能确定一个平面,至少还要有平面上的一个点的坐标才行 假如知道法向量n(A,B,C) 而平面过某点M(x0,y0,z0) 那么平面的方程为 A(x-x0)B(y-y0)C(z-z0)0 要在图中画出来&#xff0c;那么先要给x&#xff0c;y一个范围 举个离子&#…

Appuploader:常见错误及解决方法指南

目录 前言 一.登录apple官网&#xff0c;注册账号 二.下载Appuploader和登录 三.bundle ID 四.设备管理 五.证书管理 六.描述文件管理 七.打包 八.安装测试 &#xff08;PS&#xff1a;第一次安装成功会显示不受信任&#xff0c;进入设置-通 安全性”界面&#xff0c…

Interactive Natural Language Processing

本文是对《Interactive Natural Language Processing》的翻译。 交互式自然语言处理 摘要1 引言2 交互式对象2.1 人在环2.2 KB在环2.3 模型或工具在环2.4 环境在环 3 交互界面3.1 自然语言3.2 形式语言3.3 编辑3.4 机器语言3.5 共享内存 4 交互方法4.1 预训练语言模型4.2 提示…

Broken Gallery 靶场

主机发现 sudo nmap -sn 192.168.28.0/24 sudo nmap -sT --min-rate 10000 -p- 192.168.28.32 -oA nmapscan/ports sudo nmap -sT -sV -sC -O -p22,80 192.168.28.32 -oA nmapscan/detial sudo nmap --scriptvuln 192.168.28.32 -oA nmapsccan/vuln 访问80 图片下载下来 exift…

如何在 ZBrush 和 UE5 中创建精灵吟游诗人(P2)

小伙伴们大家好&#xff0c;下篇咱们精彩继续&#xff0c;主要是讲解角色建模完成后的重新拓扑、UV、灯光设置和渲染。 纹理的重新拓扑和UV 一切都在 Maya 中完成&#xff0c;重新拓扑是一个漫长的过程&#xff0c;因为角色有很多元素&#xff0c;值得庆幸的是&#xff0c;在…

模型训练-3D并行

1. 数据并行&#xff08;Data Parallel&#xff09; 1.1常规数据并行 有一张显卡&#xff08;例如rank 0显卡&#xff09;专门用于存储模型参数、梯度信息和更新模型参数。将训练数据分成多份&#xff08;份数等于显卡数量&#xff09;&#xff0c;每张卡上的模型参数相同&…

CSS编写的瓶颈在哪里?CSS预处理器登场解决你的燃眉之急!

文章目录 I. 引言解释CSS预处理器是什么提及CSS预处理器的好处 II. 为什么要使用CSS预处理器减少代码量变量Mixin函数 & 条件语句 提升CSS的可读性和维护性嵌套规则继承 方便维护加入Source Map方便调试自动前缀 III. 简介几个流行的CSS预处理器Sass介绍Sass的特性解释Sass…

69、基于51单片机智能交流电表蓝牙远程抄表OLED屏系统设计(程序+原理图+PCB源文件+参考论文+参考PPT+元器件清单等)

方案选择 单片机的选择 方案一&#xff1a;AT89C52是美国ATMEL公司生产的低电压&#xff0c;高性能CMOS型8位单片机&#xff0c;器件采用ATMEL公司的高密度、非易失性存储技术生产&#xff0c;兼容标准MCS-51指令系统&#xff0c;片内置通用8位中央处理器(CPU)和Flash存储单元…

404系列之404界面

先看效果&#xff1a; 再看代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>404</title><style>* {border: 0;box-sizing: border-box;margin: 0;padding: 0;}:root {-…

PLC数据采集如何实现远程监控和远程维护上下载?

随着PLC在工业自动化控制领域得到越来越广泛的应用&#xff0c;PLC数据采集是也逐渐成为现代工业自动化的重要组成部分。通过采集PLC内的数据&#xff0c;可以实时监测控制生产过程及工艺参数&#xff0c;提高生产效率和产品质量&#xff0c;能够使企业能够获取关键生产数据&am…

面试中经常问到的线程安全的集合类,这样回答会显得很优雅

前言&#xff1a; 本篇文章主要讲解面试中经常问到的线程安全的集合类的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出&#xff0c;对大佬有帮助希望可以支持下哦~ 小威在此先感谢各位小伙伴儿了&#x1f601; 以…