当前位置: 首页 > news >正文

使用Go语言的gorm框架查询数据库并分页导出到Excel实例

文章目录

  • 基本配置
    • 配置文件管理
    • 命令行工具: Cobra
      • 快速入门
      • 基本用法
  • 生成mock数据
    • SQL准备
    • gorm自动生成结构体代码
    • 生成mock数据
  • 查询数据
  • 导出Excel
    • 使用 excelize
    • 实现思路
    • 完整代码参考
  • 入口文件
  • 效果演示
    • 分页导出多个Excel文件
    • 合并为一个完整的Excel文件
  • 完整代码

基本配置

配置文件管理

添加依赖 go get github.com/spf13/viper,支持 JSON, TOML, YAML, HCL 等格式的配置文件。

在项目根目录下面新建 conf 目录,然后新建 application.yml 文件,此文件需要忽略版本控制。每次修改后,记得同步修改 conf/application.yml.demo 文件,让别人也知道你添加或修改了哪些内容。

server:port: 8080
datasource:driverName: mysqlhost: "127.0.0.1"port: "3306"database: go-demo-2025username: rootpassword: "123456"charset: utf8loc: Asia/Shanghai

配置初始化: common/initialization.go

// 配置初始化
func InitConfig() {workDir, _ := os.Getwd()               //获取目录对应的路径viper.SetConfigName("application")     //配置文件名viper.SetConfigType("yml")             //配置文件类型(后缀名)viper.AddConfigPath(workDir + "/conf") //执行go run对应的路径配置fmt.Println(workDir)err := viper.ReadInConfig()if err != nil {panic(err)}
}

数据库配置: 使用 gorm 初始化数据库配置,参考 common/database.go 文件

var DB *gorm.DB// https://gorm.io/zh_CN/docs/index.html
func InitDB() *gorm.DB {//从配置文件中读取数据库配置信息host := viper.GetString("datasource.host")port := viper.Get("datasource.port")database := viper.GetString("datasource.database")username := viper.GetString("datasource.username")password := viper.GetString("datasource.password")charset := viper.GetString("datasource.charset")loc := viper.GetString("datasource.loc")args := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s&parseTime=true&loc=%s",username,password,host,port,database,charset,url.QueryEscape(loc))//fmt.Println(args)db, err := gorm.Open(mysql.Open(args), &gorm.Config{Logger: logger.Default.LogMode(logger.Info), //配置日志级别,打印出所有的sql})if err != nil {fmt.Println(err)panic("failed to connect database, err: " + err.Error())}DB = dbreturn db
}

命令行工具: Cobra

Cobra是Go的CLI框架。它包含一个用于创建强大的现代CLI应用程序的库和一个用于快速生成基于Cobra的应用程序和命令文件的工具。

简单理解, 类似于 thinkphp 封装的 php think xxx 的命令行工具.

Cobra 官网: https://cobra.dev

快速入门

  • 安装: go get github.com/spf13/cobra
  • 入口文件: command.go
  • 核心文件: cmd/cobra.go

基本用法

测试Demo: command/testCmd.go

执行: go run command.go testCmd --paramA 100 --paramB 200 hello your name

输出:

--- test 运行 ---
参数个数: 3
100
200
0=>hello
1=>your
2=>name

更多参考: https://www.cnblogs.com/niuben/p/13886555.html

生成mock数据

SQL准备

CREATE TABLE `user` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',`user_id` bigint(20) unsigned NOT NULL COMMENT '用户编号',`name` varchar(255) NOT NULL DEFAULT '' COMMENT '用户姓名',`age` tinyint(4) unsigned NOT NULL DEFAULT '0' COMMENT '用户年龄',`address` varchar(255) NOT NULL DEFAULT '' COMMENT '地址',`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间',`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',PRIMARY KEY (`id`),UNIQUE KEY `key_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

gorm自动生成结构体代码

需要引入gorm.io/gen扩展,参考代码:gorm_generate_db_struct.go

package mainimport ("gorm.io/driver/mysql""gorm.io/gen""gorm.io/gorm""strings"
)func main() {// 初始化配置common.InitConfig()// 连接数据库db := common.InitDB()// 生成实例g := gen.NewGenerator(gen.Config{// 相对执行`go run`时的路径, 会自动创建目录OutPath: "old_crm_models/query",// WithDefaultQuery 生成默认查询结构体(作为全局变量使用), 即`Q`结构体和其字段(各表模型)// WithoutContext 生成没有context调用限制的代码供查询// WithQueryInterface 生成interface形式的查询代码(可导出), 如`Where()`方法返回的就是一个可导出的接口类型Mode: gen.WithDefaultQuery | gen.WithQueryInterface,// 表字段可为 null 值时, 对应结体字段使用指针类型//FieldNullable: true, // generate pointer when field is nullable// 表字段默认值与模型结构体字段零值不一致的字段, 在插入数据时需要赋值该字段值为零值的, 结构体字段须是指针类型才能成功, 即`FieldCoverable:true`配置下生成的结构体字段.// 因为在插入时遇到字段为零值的会被GORM赋予默认值. 如字段`age`表默认值为10, 即使你显式设置为0最后也会被GORM设为10提交.// 如果该字段没有上面提到的插入时赋零值的特殊需要, 则字段为非指针类型使用起来会比较方便.FieldCoverable: false, // generate pointer when field has default value, to fix problem zero value cannot be assign: https://gorm.io/docs/create.html#Default-Values// 模型结构体字段的数字类型的符号表示是否与表字段的一致, `false`指示都用有符号类型FieldSignable: false, // detect integer field's unsigned type, adjust generated data type// 生成 gorm 标签的字段索引属性FieldWithIndexTag: false, // generate with gorm index tag// 生成 gorm 标签的字段类型属性FieldWithTypeTag: true, // generate with gorm column type tag})// 设置目标 dbg.UseDB(db)// 自定义字段的数据类型// 统一数字类型为int64,兼容protobufdataMap := map[string]func(detailType string) (dataType string){"tinyint":   func(detailType string) (dataType string) { return "int64" },"smallint":  func(detailType string) (dataType string) { return "int64" },"mediumint": func(detailType string) (dataType string) { return "int64" },"bigint":    func(detailType string) (dataType string) { return "int64" },"int":       func(detailType string) (dataType string) { return "int64" },}// 要先于`ApplyBasic`执行g.WithDataTypeMap(dataMap)// 自定义模型结体字段的标签// 将特定字段名的 json 标签加上`string`属性,即 MarshalJSON 时该字段由数字类型转成字符串类型jsonField := gen.FieldJSONTagWithNS(func(columnName string) (tagContent string) {//toStringField := `balance, `toStringField := ``if strings.Contains(toStringField, columnName) {return columnName + ",string"}return columnName})// 将非默认字段名的字段定义为自动时间戳和软删除字段;// 自动时间戳默认字段名为:`updated_at`、`created_at, 表字段数据类型为: INT 或 DATETIME// 软删除默认字段名为:`deleted_at`, 表字段数据类型为: DATETIME//autoUpdateTimeField := gen.FieldGORMTag("update_time", "column:update_time;type:int unsigned;autoUpdateTime")//autoCreateTimeField := gen.FieldGORMTag("create_time", "column:create_time;type:int unsigned;autoCreateTime")// 模型自定义选项组//fieldOpts := []gen.ModelOpt{jsonField, autoCreateTimeField, autoUpdateTimeField}fieldOpts := []gen.ModelOpt{jsonField}// 创建模型的结构体,生成文件在 model 目录; 先创建的结果会被后面创建的覆盖// 创建全部模型文件, 并覆盖前面创建的同名模型allModel := g.GenerateAllTable(fieldOpts...)// 创建模型的方法,生成文件在 query 目录; 先创建结果不会被后创建的覆盖g.ApplyBasic(allModel...)g.Execute()
}

参考: https://segmentfault.com/a/1190000042502370

生成的结构体代码如下:

type User struct {ID         int64     `gorm:"column:id;type:int(11) unsigned;primaryKey;autoIncrement:true;comment:ID" json:"id"`                  // IDUserID     int64     `gorm:"column:user_id;type:bigint(20) unsigned;not null;comment:用户编号" json:"user_id"`                        // 用户编号Name       string    `gorm:"column:name;type:varchar(255);not null;comment:用户姓名" json:"name"`                                     // 用户姓名Age        int64     `gorm:"column:age;type:tinyint(4) unsigned;not null;comment:用户年龄" json:"age"`                                // 用户年龄Address    string    `gorm:"column:address;type:varchar(255);not null;comment:地址" json:"address"`                                 // 地址CreateTime time.Time `gorm:"column:create_time;type:datetime;not null;default:CURRENT_TIMESTAMP;comment:添加时间" json:"create_time"` // 添加时间UpdateTime time.Time `gorm:"column:update_time;type:datetime;not null;default:CURRENT_TIMESTAMP;comment:更新时间" json:"update_time"` // 更新时间
}

生成mock数据

代码路径:service/users/userService.go

// 批量添加mock数据
func (ctx *UserService) BatchCreateMockData() {for i := 1; i <= 82; i++ { //循环操作82次var users []*model.Userfor j := 1; j <= 100; j++ { //一次添加100条数据userid := "1" + fmt.Sprintf("%03d", i) + fmt.Sprintf("%03d", j)useridInt, _ := strconv.Atoi(userid)users = append(users, &model.User{UserID:  int64(useridInt),Name:    funcUtils.GenerateRandomChineseName(),Age:     int64(common.GenerateRandomNumber()),Address: funcUtils.GenerateRandomChinaAddress(),})}err := ctx.GormDB.Create(users).Errorif err != nil {fmt.Println(fmt.Sprintf("第 %d 页添加失败,错误原因:%s", i, err))} else {fmt.Println(fmt.Sprintf("第 %d 页添加成功", i))}}
}

备注:以上代码中关于测试数据生成的工具:

  • 中国地址生成器 https://github.com/GoFinalPack/chinese-address-generator
  • 随机生成中国人姓名 https://www.jianshu.com/p/bab0994647b3

在这里插入图片描述
生成的测试数据效果如下:
在这里插入图片描述

查询数据

gorm 查询方法文档: https://gorm.io/zh_CN/docs/query.html

定义查询总数和查询列表的方法,代码路径:service/users/userService.go

// 查询总数
func (ctx *UserService) GetUserCount() int64 {var count int64err := ctx.GormDB.Model(&model.User{}).Where("1=1").Count(&count).Errorif err != nil {fmt.Println(fmt.Sprintf("查询总数错误:%s", err))return 0}//fmt.Println(fmt.Sprintf("总条数:%d", count))return count
}func (ctx *UserService) GetUserList(page int, pageSize int) []model.User {var dataList []model.Useroffset := (page - 1) * pageSize //偏移量err2 := ctx.GormDB.Select("*").Where("1=1").Order("id asc").Limit(pageSize).Offset(offset).Find(&dataList).Errorif err2 != nil {fmt.Println(fmt.Sprintf("查询列表错误:%s", err2))return nil}return dataList
}

导出Excel

使用 excelize

使用率高的几个扩展

  • excelize (github.com/xuri/excelize/v2)
  • excelize (github.com/360EntSecGroup-Skylar/excelize)
  • xlsx (github.com/tealeg/xlsx/v3)
  • xxhash (github.com/OneOfOne/xxhash)

相关 Excel 开源类库性能对比: https://xuri.me/excelize/zh-hans/performance.html

此处以 github.com/xuri/excelize/v2 为例演示常用的Excel操作方法. 官方中文文档: https://xuri.me/excelize/zh-hans/

安装excelize go get github.com/xuri/excelize/v2

导出 Excel 文档参考代码:

var filePath stringfunc init() {filePath = fmt.Sprintf("files/%s", time.Now().Format("2006/01/02/"))
}// WriteExcel 导出 Excel 文档
// data: 要导出的数据
// return: 文件名, error
func WriteExcel(data [][]string, relativePath string, fileName string) (string, error) {//创建存放目录relativeFilePath := filePath + relativePath + "/"_, err := os.ReadDir(relativeFilePath)if err != nil {// 不存在就创建err = os.MkdirAll(relativeFilePath, fs.ModePerm)if err != nil {fmt.Println(err)}}//创建表格file := excelize.NewFile()sheetName := "Sheet1"index, _ := file.NewSheet(sheetName)for i, row := range data {for j, val := range row {// 列和行 数字索引转excel坐标索引cellName, _ := excelize.CoordinatesToCellName(j+1, i+1)//fmt.Println("cellName:", cellName)// 写入sheetfile.SetCellValue(sheetName, cellName, val)}}file.SetActiveSheet(index)//导出文件filePathName := relativeFilePath + fileName + "_" + common.GetMicroTimestamp() + ".xlsx"err = file.SaveAs(filePathName)if err != nil {return "", err}return filePathName, nil
}

实现思路

导出数据部分,考虑到数据量可能较大,如果一次性查询全量数据,可能造成内存或CPU爆满,因此不建议一次性全部导出,而是采用分页导出到多个文件,然后再将多个文件合并为一个Excel表格文件。

这里需要注意一个细节,就是正常导出的表格数据一般都是按照id(或者添加时间)倒序排列,最新的在前面。但是由于使用了分页导出,如果我们采用 order by id desc limit xxx offset xxx 有可能在分页查询的过程中产生新的数据,那么分页的偏移量(offset)可能导致出现重复数据,就是第一页的某一条数据有可能在第二页重复出现(应该很好理解吧?)。所以查询数据的时候需要 order by id asc 按照 id 从小到大的顺序导出数据就可以避免这个问题。

分页导出后,需要对整体顺序再次反转,最后合并的表格数据才能是按照 由新到旧 的顺序的结果。

// 二维数组/切片 反转
func ReverseTwoDimSlice(slice [][]string) {// 按照子切片的第0个元素进行倒序排列sort.Slice(slice, func(i, j int) bool {return slice[i][0] > slice[j][0] // 返回true表示i在j之前})
}

导出的表头,可以考虑使用上面 gorm 生成的 struct 部分,通过反射可以获取,核心代码如下:

func GetStructTag(data any) []string {t := reflect.TypeOf(data)var result []stringfor i := 0; i < t.NumField(); i++ {field := t.Field(i)jsonTag := field.Tag.Get("json")//fmt.Printf("Field: %s, Tag: %s\n", field.Name, jsonTag)result = append(result, jsonTag)}return result
}

上面代码传入的 data参数为 model 结构体的 tag 的 json 部分:
在这里插入图片描述
如果需要指定导出的表头字段,可以如下定义:

dataKeySlice = [][]string{{"id", "ID"},{"user_id", "用户ID"},{"name", "用户姓名"},{"age", "年龄"},{"address", "地址"},{"create_time", "添加时间"},{"update_time", "修改时间"},
}
dataKeys, dataKeysTitle := excelUtils.GetDataKeyAndTitle(dataKeySlice)// 根据自定义的二维切片,封装导出的表头字段的key和title
func GetDataKeyAndTitle(dataKeySlice [][]string) ([]string, []string) {//自定义导出的字段var dataKeys []stringvar dataKeysTitle []stringif len(dataKeySlice) > 0 { //如果定义了key对应的字段值for _, v := range dataKeySlice {if len(v[1]) > 0 {dataKeysTitle = append(dataKeysTitle, v[1])} else if len(v[0]) > 0 {dataKeysTitle = append(dataKeysTitle, v[0])}if len(v[0]) > 0 {dataKeys = append(dataKeys, v[0])}}}return dataKeys, dataKeysTitle
}

导出多个Excel文件后,再对它们进行合并为一个Excel文件:

// 合并一个目录下的所有Excel文件
func MergeExcel(dirPath string, outputFileName string, isDeleteOriginFiles bool) string {dir, err := ioutil.ReadDir(dirPath)if err != nil {fmt.Printf("open dir failed: %s\n", err.Error())}//设置路径,文件夹放在main的同级目录下PathSeperator := string(os.PathSeparator)outputdir := dirPath + "/../" + outputFileName//合并后的文件var new_file *xlsx.Filevar new_sheet *xlsx.Sheetnew_file = xlsx.NewFile()var new_err errornew_sheet, new_err = new_file.AddSheet("Sheet1")for _, fi := range dir {//fmt.Printf("open success: %s\n", Pthdir + PthSep+fi.Name())if new_err != nil {fmt.Printf(new_err.Error())}//读取文件xlFile, err := xlsx.OpenFile(dirPath + PathSeperator + fi.Name())if err != nil {fmt.Printf("open failed: %s\n", err)}for _, sheet := range xlFile.Sheets {//fmt.Printf("Sheet Name: %s\n", sheet.Name)num := 0for _, row := range sheet.Rows {num++//跳过前5行,将后面的行写入新的文件//if(num > 5){new_row := new_sheet.AddRow()//new_row.SetHeightCM(1)for _, cell := range row.Cells {text := cell.String()//fmt.Printf("%s\n", text)new_cell := new_row.AddCell()new_cell.Value = text}//}}}}//写入文件new_err = new_file.Save(outputdir)if new_err != nil {fmt.Printf(new_err.Error())}//是否删除原文件if isDeleteOriginFiles {os.RemoveAll(dirPath)}outputFilePath, _ := filepath.Abs(outputdir)return outputFilePath
}

完整代码参考

代码路径: service/users/userDataExportService.go

package usersimport ("fmt""go-demo-2025/common""go-demo-2025/utils/excelUtils""go-demo-2025/utils/funcUtils""math""os""path/filepath""strconv"
)// 通过反射直接获取结构体中的所有数据字段,并转换为map,再根据key的顺序逐一映射到新的切片
func (ctx *UserService) ExportUserList() {requestKey := common.GetYmdHis() + "_" + common.RandomString(10)count := ctx.GetUserCount()count = 1300 //调试数据//分页查询列表pageSize := 1000                                           //每页查询多少条pageCount := math.Ceil(float64(count) / float64(pageSize)) //总页数//fmt.Println("总页数:", pageCount)//自定义导出的字段var dataKeySlice [][]string//如果需要导出数据表的所有字段,则注释下面的二维切片dataKeySlice = [][]string{{"id", "ID"},{"user_id", "用户ID"},{"name", "用户姓名"},{"age", "年龄"},{"address", "地址"},{"create_time", "添加时间"},{"update_time", "修改时间"},}dataKeys, dataKeysTitle := excelUtils.GetDataKeyAndTitle(dataKeySlice)for page := 1; page <= int(pageCount); page++ {dataList := ctx.GetUserList(page, pageSize)var excelData [][]stringfor key, item := range dataList {//如果没有定义指定要导出的字段,则获取数据表的所有字段if len(dataKeys) == 0 { //dataKeys切片(model结构体的tag的json部分)dataKeys = funcUtils.GetStructTag(item)}//结构体转为mapitemMap, _ := funcUtils.StructToMap(item, "json")//fmt.Println(itemMap)//os.Exit(1)//按照顺序将map中的数据填充到key的切片中var itemSlice []string//第一列使用key,下一步排序用itemSlice = append(itemSlice, fmt.Sprintf("%03d", key)) //key前面补两个0,要不然反转的时候会按照字符串顺序排序,导致"2>10".这样改后就是"10>02")for _, keys := range dataKeys {                         //按照dataKeys设定的字段,逐一插入到切片中itemSlice = append(itemSlice, itemMap[keys])}excelData = append(excelData, itemSlice)}funcUtils.ReverseTwoDimSlice(excelData)                     //倒序排列,必须保证第0个元素是 key 值excelData = funcUtils.DeleteTwoDimSliceFirstChar(excelData) //删除第0个元素(key值)pageDiff := int(pageCount) - page + 1//导出Excel文件_, err1 := excelUtils.WriteExcel(excelData, requestKey, fmt.Sprintf("用户数据导出_page_%s", fmt.Sprintf("%04d", pageDiff)))if err1 != nil {fmt.Println("Write excel error: ", err1)os.Exit(1)}//fmt.Println("Write excel success, file name is: ", fileName)}//导出表头var excelDataTitle [][]stringif len(dataKeysTitle) == 0 {dataKeysTitle = dataKeys}excelDataTitle = append(excelDataTitle, dataKeysTitle)fileName, _ := excelUtils.WriteExcel(excelDataTitle, requestKey, "用户数据导出_page_0000")//合并多个文件为一个absPath, _ := filepath.Abs(fileName) // 获取文件的绝对路径dirPath := filepath.Dir(absPath)     //获取文件所在目录的绝对路径//fmt.Println(dirPath)outputFileName := fmt.Sprintf("用户数据导出_%v.xlsx", requestKey)outputFilePath := excelUtils.MergeExcel(dirPath, outputFileName, true)fmt.Println("最终导出的文件:", outputFilePath)
}

入口文件

新增命令行脚本: command/userCmd.go

package commandimport ("fmt""github.com/spf13/cobra""go-demo-2025/service/users""os""time"
)// go run command.go userCmd --operate exportData
func init() {RootCmd.AddCommand(userCmd)
}var userCmd = &cobra.Command{Use:   "userCmd",Short: "关于用户相关的命令行操作",Long:  ``,Run: func(cmd *cobra.Command, args []string) {fmt.Println("--- userCmd 运行 ---")userCmdRun(args)},
}func userCmdRun(args []string) {if operate == "" {fmt.Println("缺少参数operate")os.Exit(1)}if operate == "createMockData" { //生成mock数据fmt.Println("执行createMockData...")service := users.NewUserService()service.BatchCreateMockData()} else if operate == "exportData" { //导出用户数据fmt.Println("执行exportData...")service := users.NewUserService()startTime := time.Now()service.ExportUserList()endTime := time.Now()timeCost := endTime.Sub(startTime)fmt.Println("总耗时: ", timeCost)} else {fmt.Println("暂未定义此operate的业务逻辑")os.Exit(1)}
}

效果演示

分页导出多个Excel文件

合并多个文件前的效果演示,在合并多个Excel部分的代码暂时终止一下,看看效果。
在这里插入图片描述

在项目根目录下执行命令 go run command.go userCmd --operate exportData
在这里插入图片描述
生成的文件在:项目根目录下面的 /files/年/月/日/xxx 下面:
在这里插入图片描述
其中 用户数据导出_page_0000_xxx 是表头数据:

在这里插入图片描述

合并为一个完整的Excel文件

接下来, 去掉刚才的 os.Exit(1) 再试一次直接导出一个完整的Excel文件,还是执行命令 go run command.go userCmd --operate exportData

在这里插入图片描述
这次直接合并为多个文件,并且删除之前的多个小文件。
在这里插入图片描述

完整代码

源代码:https://gitee.com/rxbook/go-demo-2025

下载后,解压到自定义目录,配置好 Go 环境,创建数据库go-demo-2025,导入 data/go-demo-2025.sql 的SQL语句,复制 conf/application.yml.democonf/application.yml 修改对应的数据为你自己的数据库连接信息。

  • 执行 go run command.go userCmd --operate createMockData 生成测试用的mock数据;
  • 执行 go run command.go userCmd --operate exportData 即可导出Excel文件;
  • 执行 go run quick_start_demo/gin_http_get.go 快速入门Gin框架http服务;
  • 执行 go run main.go 启动HTTP服务,进入 router/router.go 查看具体测试的路由信息。

相关文章:

使用Go语言的gorm框架查询数据库并分页导出到Excel实例

文章目录 基本配置配置文件管理命令行工具: Cobra快速入门基本用法 生成mock数据SQL准备gorm自动生成结构体代码生成mock数据 查询数据导出Excel使用 excelize实现思路完整代码参考 入口文件效果演示分页导出多个Excel文件合并为一个完整的Excel文件 完整代码 基本配置 配置文…...

Run the FPGA VI 选项的作用

Run the FPGA VI 选项的作用是决定当主机 VI 运行时&#xff0c;FPGA VI 是否会自动运行。 具体作用&#xff1a; 勾选 “Run the FPGA VI”&#xff1a; 当主机 VI 执行时&#xff0c;如果 FPGA VI 没有正在运行&#xff0c;系统将自动启动并运行该 FPGA VI。 这可以确保 FPG…...

新手入门怎么炒股,新手炒股入门需要做哪些准备?

炒股自动化&#xff1a;申请官方API接口&#xff0c;散户也可以 python炒股自动化&#xff08;0&#xff09;&#xff0c;申请券商API接口 python炒股自动化&#xff08;1&#xff09;&#xff0c;量化交易接口区别 Python炒股自动化&#xff08;2&#xff09;&#xff1a;获取…...

Fetch 与 Axios:JavaScript HTTP 请求库的详细比较

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐&#xff1a;「storm…...

记录一个Ajax发送JSON数据的坑,后端RequestBody接收参数小细节?JSON对象和JSON字符串的区别?

上半部分主要介绍我实际出现的问题&#xff0c;最终下面会有总结。 起因&#xff1a;我想发送post请求的data&#xff0c;但是在浏览器中竟然被搞成了地址栏编码 如图前端发送的ajax请求数据 如图发送的请求体&#xff1a; 很明显是keyvalue这种形式&#xff0c;根本就不是…...

【智能算法应用】长鼻浣熊优化算法求解二维路径规划问题

摘要 本文采用长鼻浣熊优化算法 (Coati Optimization Algorithm, COA) 求解二维路径规划问题。COA 是一种基于长鼻浣熊的觅食和社群行为的智能优化算法&#xff0c;具有快速收敛性和较强的全局搜索能力。通过仿真实验&#xff0c;本文验证了 COA 在复杂环境下的路径规划性能&a…...

微服务中的负载均衡算法与策略深度解析

在微服务架构中&#xff0c;负载均衡是保证系统高可用性和高性能的关键技术。通过合理地将请求分配给多个服务实例&#xff0c;负载均衡策略可以优化资源利用&#xff0c;实现请求的均衡处理。本文将深入探讨微服务中的负载均衡算法及其配置策略&#xff0c;帮助读者更好地理解…...

初知C++:AVL树

文章目录 初知C&#xff1a;AVL树1.AVL树的概念2.AVL树的是实现2.1.AVL树的结构2.2.AVL树的插入2.3.旋转2.4.AVL树的查找2.5.AVL树平衡检测 初知C&#xff1a;AVL树 1.AVL树的概念 • AVL树是最先发明的自平衡⼆叉查找树&#xff0c;AVL是⼀颗空树&#xff0c;或者具备下列性…...

[LeetCode] 67. 二进制求和

题目描述&#xff1a; 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 示例 1&#xff1a; 输入:a "11", b "1" 输出&#xff1a;"100" 示例 2&#xff1a; 输入&#xff1a;a "1010", b "…...

工业物联网关-ModbusTCP

Modbus-TCP模式把网关视作Modbus从端设备&#xff0c;主端设备可以通过Modbus-TCP协议访问网关上所有终端设备。用户可以自定义多条通道&#xff0c;每条通道可以配置为TCP Server或者TCP Slave。注意&#xff0c;该模式需要指定采集通道&#xff0c;采集通道可以是串口和网口通…...

子组件向父组件传值$emit

点击子组件的按钮&#xff0c;将子组件的值传递给父组件&#xff0c;并进行提示。 子组件 <template><div><button click"emitIndex">clickme</button></div> </template> <script> export default {methods: {emitInde…...

校车购票微信小程序的设计与实现(lw+演示+源码+运行)

摘 要 由于APP软件在开发以及运营上面所需成本较高&#xff0c;而用户手机需要安装各种APP软件&#xff0c;因此占用用户过多的手机存储空间&#xff0c;导致用户手机运行缓慢&#xff0c;体验度比较差&#xff0c;进而导致用户会卸载非必要的APP&#xff0c;倒逼管理者必须改…...

【Golang】关于Go语言中的定时器原理与实战应用

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…...

matlab不小心删除怎么撤回

预设项——>删除文件——>移动至临时文件夹 tem临时文件夹下...

云原生、云计算、虚拟化概念概述

&#xff08;带着批评阅读&#xff0c;不对的请评论区补充&#xff09; 1、出现年代前后顺序 虚拟化------>云计算------>云原生 2、虚拟化 虚拟化侧重描述实现&#xff0c;最开始的技术是模拟、hook指令执行软件程序&#xff0c;后续出现了半虚拟化、CPU硬件提供虚拟化…...

【Trulens框架】用TruLens 自动化 RAG 应用项目评估测试

前言&#xff1a; 什么是Trulens TruLens是面向神经网络应用的质量评估工具&#xff0c;它可以帮助你使用反馈函数来客观地评估你的基于LLM&#xff08;语言模型&#xff09;的应用的质量和效果。反馈函数可以帮助你以编程的方式评估输入、输出和中间结果的质量&#xff0c;从而…...

互联网线上融合上门洗衣洗鞋小程序,让洗衣洗鞋像点外卖一样简单

随着服务创新的风潮&#xff0c;众多商家已巧妙融入预约上门洗鞋新风尚&#xff0c;并携手洗鞋小程序&#xff0c;开辟线上蓝海。那么&#xff0c;这不仅仅是一个小程序&#xff0c;它究竟蕴含着哪些诱人好处呢&#xff1f; 1. 无缝融合&#xff0c;双线共赢&#xff1a;小程序…...

R语言绘制三维散点图

之前我们绘制的属于二维散点图&#xff0c;具有两个维度通常是 x 轴和 y 轴&#xff09;上展示数据点的分布。只能呈现两个变量之间的关系。而三维散点图则具有三个维度&#xff08;x 轴、y 轴和 z 轴&#xff09;上展示数据点的分布。可以同时呈现三个变量之间的关系&#xff…...

2014年国赛高教杯数学建模A题嫦娥三号软着陆轨道设计与控制策略解题全过程文档及程序

2014年国赛高教杯数学建模 A题 嫦娥三号软着陆轨道设计与控制策略 嫦娥三号于2013年12月2日1时30分成功发射&#xff0c;12月6日抵达月球轨道。嫦娥三号在着陆准备轨道上的运行质量为2.4t&#xff0c;其安装在下部的主减速发动机能够产生1500N到7500N的可调节推力&#xff0c;…...

QD1-P25 CSS 背景

本节学习&#xff1a;CSS 背景属性 本节视频 https://www.bilibili.com/video/BV1n64y1U7oj?p25 背景颜色 ​​ 背景图片 不重复 ​​ 横向重复 ​​ 纵向重复 ​​ 双向重复 ​​ 背景图片大小 400px ​​ 600px ​​ 原图大小 ​​ 显示器宽度不够时&…...

《Linux运维总结:基于ARM64+X86_64架构CPU使用docker-compose一键离线部署mongodb 7.0.14容器版分片集群》

总结&#xff1a;整理不易&#xff0c;如果对你有帮助&#xff0c;可否点赞关注一下&#xff1f; 更多详细内容请参考&#xff1a;《Linux运维篇&#xff1a;Linux系统运维指南》 一、部署背景 由于业务系统的特殊性&#xff0c;我们需要面向不通的客户安装我们的业务系统&…...

Java利用ChromeDriver插件网页截图(Wondows版+Linux版)

chromedriver是谷歌浏览器驱动,用来模拟谷歌运行操作的一个工具&#xff0c;此处主要讲解Java后端利用此插件进行网页截图&#xff0c;并且适配Linux部署。 环境准备 Wondows服务器或电脑 本机需安装Chrome谷歌浏览器&#xff0c;根据本机浏览器版本&#xff0c;下载对应的chr…...

无人机之交互系统篇

一、系统构成 无人机交互系统通常由多个子系统组成&#xff0c;包括但不限于&#xff1a; 多模式人机交互装置&#xff1a;这是人机交互系统的基础层&#xff0c;通常包括计算机、局域网、传感器等设备&#xff0c;用于实现操作员与无人机之间的数据交互和指令传递。例如&…...

MarsCode--找出数字比例超过n/2的【简单】

问题描述 给定一个长度为n的整型数组&#xff0c;已知其中一个数字的出现次数超过数组长度的一半&#xff0c;找出这个元素 输入格式 一个长度为n的数组&#xff0c;其中某个元素的出现次数大于n/2 输出格式 一个整数 输入样例 [1,3,8,2,3,1,3,3,3] 输出样例 3 数据范…...

Python网络爬虫快速入门指南

Python网络爬虫快速入门指南 网络爬虫&#xff0c;也称为网络蜘蛛&#xff0c;是一种自动访问互联网并提取信息的程序。Python因其简洁明了的语法和丰富的库支持&#xff0c;成为开发网络爬虫的理想选择。在这篇博客中&#xff0c;我们将探讨如何快速入门Python网络爬虫技术&a…...

C86 架构一键离线安装 docker 和 docker-compose 实战指南

C86 架构一键离线安装 docker 和 docker-compose 实战指南 文章目录 C86 架构一键离线安装 docker 和 docker-compose 实战指南一 磁盘挂载二 docker 部署1 上传安装包2 解压安装包3 安装包 docker 三 验证安装四 清除安装包五 安装包下载地址 本文提供了在 C86 架构环境下&…...

【LwIP源码学习2】调试输出相关宏

前言 本文对lwip中debug.h文件里的调试相关宏进行分析。 正文 debug.h中有3个重要的调试相关宏&#xff1a; LWIP_ASSERT(message, assertion) LWIP_ERROR(message, expression, handler) LWIP_DEBUGF(debug, message) 断言 LWIP_ASSERT(message, assertion) 源代码为&…...

Python 列表专题:删除元素

Python 是一种强大的编程语言,广泛应用于数据分析、Web 开发、人工智能等多个领域。而列表(List)作为 Python 中最基本的数据结构之一,扮演着非常重要的角色。列表不仅可以存储各种类型的数据,还提供了丰富的操作接口,方便我们进行数据的增删改查。本篇博文将深入探讨 Py…...

Spring Boot 快速入门与核心原理详解

引言 在上一篇文章中&#xff0c;我们详细探讨了 Spring 框架中的事件监听与发布机制。本文将转向 Spring Boot&#xff0c;介绍如何快速入门 Spring Boot&#xff0c;并深入探讨其核心原理。Spring Boot 是由 Pivotal 团队提供的全新框架&#xff0c;旨在简化 Spring 应用的初…...

UniApp 与微信小程序详细对比

UniApp 与微信小程序详细对比 1. 开发环境 微信小程序&#xff1a; 主要使用微信开发者工具提供模拟器、调试工具和性能监控只能开发微信小程序 UniApp&#xff1a; 主要使用 HBuilderX&#xff0c;但也支持 VS Code 等其他编辑器HBuilderX 提供可视化界面、代码提示、调试工…...