go gin web综合教程,包括 mysql redis log 路由

news2024/12/23 18:52:38

前言

在学习go许久,没看到网上有比较综合的gin web教程,很多都是最基础的教程,完全就是启动个服务返回参数,没有过多的结合实际开发。下面我结合一下我的经验,来写一篇深入的综合教程,包括数据库Mysql、redis结合。

项目结构

在这里插入图片描述
初学者一开始很迷茫,究竟项目结构应该是这么样的呢,我的结构仅仅供参考

包名描述
app应用相关的内容,比如request,response的定义
config配置文件存的地方
const常量
controller控制器,需要路由结合
file控制文件的操作,和日志相关
gredisredis的启动操作
logging日志相关
model数据库启动操作,和数据库相关的实体类
router路由
service服务逻辑内容
setting配置相关代码
util工具类

main文件

package main

import (
	"github.com/astaxie/beego/logs"
	"github.com/gin-gonic/gin"
	_ "github.com/go-sql-driver/mysql"
	"gotest/src/gredis"
	"gotest/src/logging"
	"gotest/src/model"
	"gotest/src/router"
	"gotest/src/setting"
	"net/http"
)

func init() {
	logs.Info("配置文件设置 初始化配置")
	setting.Setup()
	logs.Info("数据库 初始化配置")
	model.SetUp()
	logs.Info("日志文件 初始化配置")
	logging.Setup()
	logs.Info("redis 初始化配置")
	gredis.Setup()
}

func main() {
	//debug,release,test
	gin.SetMode("debug")
	logs.Info("路由 初始化配置")
	routersInit := router.InitRouter()
	server := &http.Server{
		//访问端口
		Addr: ":8080",
		//路由处理器
		Handler: routersInit,
	}
	//开启服务
	server.ListenAndServe()
}

启动程序描述了这几样东西

  1. 他默认会先执行init()方法,这里面设置了几个启动初始化配置,配置文件、数据库、日志文件、redis,会按顺序启动初始化
  2. 在main方法中,初始路由配置
  3. 配置server,输入路由实例和配置的端口
  4. 启动服务

我下面按顺序来看看这些内容

配置文件初始化

在这里插入图片描述
他调用的是setting.Setup(),也就是setting包中的Setup方法,也就是下面这个

package setting

import (
	"github.com/go-ini/ini"
	"log"
	"time"
)

type App struct {
	JwtSecret string
	PageSize  int
	PrefixUrl string

	RuntimeRootPath string

	ImageSavePath  string
	ImageMaxSize   int
	ImageAllowExts []string

	ExportSavePath string
	QrCodeSavePath string
	FontSavePath   string

	LogSavePath string
	LogSaveName string
	LogFileExt  string
	TimeFormat  string
}

var AppSetting = &App{}

type Server struct {
	RunMode      string
	HttpPort     int
	ReadTimeout  time.Duration
	WriteTimeout time.Duration
}

var ServerSetting = &Server{}

type Database struct {
	Type        string
	User        string
	Password    string
	Host        string
	Name        string
	TablePrefix string
}

var DatabaseSetting = &Database{}

type Redis struct {
	Host        string
	Password    string
	MaxIdle     int
	MaxActive   int
	IdleTimeout time.Duration
}

var RedisSetting = &Redis{}

var cfg *ini.File

// Setup initialize the configuration instance
func Setup() {
	var err error
	cfg, err = ini.Load("src/config/app.ini")
	if err != nil {
		log.Fatalf("setting.Setup, fail to parse 'config/app.ini': %v", err)
	}

	mapTo("app", AppSetting)
	mapTo("server", ServerSetting)
	mapTo("database", DatabaseSetting)
	mapTo("redis", RedisSetting)

	AppSetting.ImageMaxSize = AppSetting.ImageMaxSize * 1024 * 1024
	ServerSetting.ReadTimeout = ServerSetting.ReadTimeout * time.Second
	ServerSetting.WriteTimeout = ServerSetting.WriteTimeout * time.Second
	RedisSetting.IdleTimeout = RedisSetting.IdleTimeout * time.Second
}

// mapTo map section
func mapTo(section string, v interface{}) {
	err := cfg.Section(section).MapTo(v)
	if err != nil {
		log.Fatalf("Cfg.MapTo %s err: %v", section, err)
	}
}

配置文件放在config包中
在这里插入图片描述

[app]
PageSize = 10
JwtSecret = 233
PrefixUrl = http://127.0.0.1:8000

RuntimeRootPath = runtime/

ImageSavePath = upload/images/
# MB
ImageMaxSize = 5
ImageAllowExts = .jpg,.jpeg,.png

ExportSavePath = export/
QrCodeSavePath = qrcode/
FontSavePath = fonts/

LogSavePath = logs/
LogSaveName = log
LogFileExt = log
TimeFormat = 20060102

[server]
#debug or release
RunMode = debug
HttpPort = 8000
ReadTimeout = 60
WriteTimeout = 60

[database]
Type = mysql
User = root
Password = root
Host = 127.0.0.1:3306
Name = blog
TablePrefix = blog_

[redis]
Host = 127.0.0.1:6379
Password = 
MaxIdle = 30
MaxActive = 30
IdleTimeout = 200

Setup方法的步骤如下

  1. 读取src/config/app.ini文件
  2. 解析出AppSetting
  3. 解析出ServerSetting
  4. 解析出DatabaseSetting
  5. 解析出RedisSetting
  6. 分别赋值到全局变量,以供外部访问

配置文件初始化结束

数据库初始化配置

这里使用的是mysql,main方法调用的是model.SetUp()
在这里插入图片描述
内容如下:

package model

import (
	"fmt"
	"github.com/go-xorm/xorm"
	"gotest/src/setting"
)

type Model struct {
	ID         int `gorm:"primary_key" json:"id"`
	CreatedOn  int `json:"created_on"`
	ModifiedOn int `json:"modified_on"`
	DeletedOn  int `json:"deleted_on"`
}

var db *xorm.Engine

func SetUp() {
	var err error
	db, err = xorm.NewEngine("mysql", fmt.Sprintf("%s:%s@(%s)/%s?charset=utf8",
		setting.DatabaseSetting.User,
		setting.DatabaseSetting.Password,
		setting.DatabaseSetting.Host,
		setting.DatabaseSetting.Name))
	if err != nil {
		panic(err.Error())
	}
	db.DB().SetMaxIdleConns(10)
	db.DB().SetMaxOpenConns(100)
}

日志文件初始化

相关的文件如下:
在这里插入图片描述
logging/log.go文件

package logging

import (
	"fmt"
	"gotest/src/file"
	"log"
	"os"
	"path/filepath"
	"runtime"
)

type Level int

var (
	F *os.File

	DefaultPrefix      = ""
	DefaultCallerDepth = 2

	logger     *log.Logger
	logPrefix  = ""
	levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
)

const (
	DEBUG Level = iota
	INFO
	WARNING
	ERROR
	FATAL
)

// Setup initialize the log instance
func Setup() {
	var err error
	filePath := getLogFilePath()
	fileName := getLogFileName()
	F, err = file.MustOpen(fileName, filePath)
	if err != nil {
		log.Fatalf("logging.Setup err: %v", err)
	}

	logger = log.New(F, DefaultPrefix, log.LstdFlags)
}

// Debug output logs at debug level
func Debug(v ...interface{}) {
	setPrefix(DEBUG)
	logger.Println(v)
}

// Info output logs at info level
func Info(v ...interface{}) {
	setPrefix(INFO)
	logger.Println(v)
}

// Warn output logs at warn level
func Warn(v ...interface{}) {
	setPrefix(WARNING)
	logger.Println(v)
}

// Error output logs at error level
func Error(v ...interface{}) {
	setPrefix(ERROR)
	logger.Println(v)
}

// Fatal output logs at fatal level
func Fatal(v ...interface{}) {
	setPrefix(FATAL)
	logger.Fatalln(v)
}

// setPrefix set the prefix of the log output
func setPrefix(level Level) {
	_, file, line, ok := runtime.Caller(DefaultCallerDepth)
	if ok {
		logPrefix = fmt.Sprintf("[%s][%s:%d]", levelFlags[level], filepath.Base(file), line)
	} else {
		logPrefix = fmt.Sprintf("[%s]", levelFlags[level])
	}

	logger.SetPrefix(logPrefix)
}

logging/file.go文件:主要是定义日志文件的输出位置

package logging

import (
	"fmt"
	"gotest/src/setting"
	"time"
)

// getLogFilePath get the log file save path
func getLogFilePath() string {
	return fmt.Sprintf("%s%s", setting.AppSetting.RuntimeRootPath, setting.AppSetting.LogSavePath)
}

// getLogFileName get the save name of the log file
func getLogFileName() string {
	return fmt.Sprintf("%s%s.%s",
		setting.AppSetting.LogSaveName,
		time.Now().Format(setting.AppSetting.TimeFormat),
		setting.AppSetting.LogFileExt,
	)
}

文件相关的操作
file/file.go

package file

import (
	"fmt"
	"io/ioutil"
	"mime/multipart"
	"os"
	"path"
)

// GetSize get the file size
func GetSize(f multipart.File) (int, error) {
	content, err := ioutil.ReadAll(f)

	return len(content), err
}

// GetExt get the file ext
func GetExt(fileName string) string {
	return path.Ext(fileName)
}

// CheckNotExist check if the file exists
func CheckNotExist(src string) bool {
	_, err := os.Stat(src)

	return os.IsNotExist(err)
}

// CheckPermission check if the file has permission
func CheckPermission(src string) bool {
	_, err := os.Stat(src)

	return os.IsPermission(err)
}

// IsNotExistMkDir create a directory if it does not exist
func IsNotExistMkDir(src string) error {
	if notExist := CheckNotExist(src); notExist == true {
		if err := MkDir(src); err != nil {
			return err
		}
	}

	return nil
}

// MkDir create a directory
func MkDir(src string) error {
	err := os.MkdirAll(src, os.ModePerm)
	if err != nil {
		return err
	}

	return nil
}

// Open a file according to a specific mode
func Open(name string, flag int, perm os.FileMode) (*os.File, error) {
	f, err := os.OpenFile(name, flag, perm)
	if err != nil {
		return nil, err
	}

	return f, nil
}

// MustOpen maximize trying to open the file
func MustOpen(fileName, filePath string) (*os.File, error) {
	dir, err := os.Getwd()
	if err != nil {
		return nil, fmt.Errorf("os.Getwd err: %v", err)
	}

	src := dir + "/" + filePath
	perm := CheckPermission(src)
	if perm == true {
		return nil, fmt.Errorf("file.CheckPermission Permission denied src: %s", src)
	}

	err = IsNotExistMkDir(src)
	if err != nil {
		return nil, fmt.Errorf("file.IsNotExistMkDir src: %s, err: %v", src, err)
	}

	f, err := Open(src+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
	if err != nil {
		return nil, fmt.Errorf("Fail to OpenFile :%v", err)
	}

	return f, nil
}

redis 初始化

执行的是main方法中的gredis.Setup()
目录中的这里:
在这里插入图片描述
gredis/redis.go

package gredis

import (
	"encoding/json"
	"gotest/src/setting"
	"time"

	"github.com/gomodule/redigo/redis"
)

var RedisConn *redis.Pool

// Setup Initialize the Redis instance
func Setup() error {
	RedisConn = &redis.Pool{
		MaxIdle:     setting.RedisSetting.MaxIdle,
		MaxActive:   setting.RedisSetting.MaxActive,
		IdleTimeout: setting.RedisSetting.IdleTimeout,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", setting.RedisSetting.Host)
			if err != nil {
				return nil, err
			}
			if setting.RedisSetting.Password != "" {
				if _, err := c.Do("AUTH", setting.RedisSetting.Password); err != nil {
					c.Close()
					return nil, err
				}
			}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}

	return nil
}

// Set a key/value
func Set(key string, data interface{}, time int) error {
	conn := RedisConn.Get()
	defer conn.Close()

	value, err := json.Marshal(data)
	if err != nil {
		return err
	}

	_, err = conn.Do("SET", key, value)
	if err != nil {
		return err
	}

	_, err = conn.Do("EXPIRE", key, time)
	if err != nil {
		return err
	}

	return nil
}

// Exists check a key
func Exists(key string) bool {
	conn := RedisConn.Get()
	defer conn.Close()

	exists, err := redis.Bool(conn.Do("EXISTS", key))
	if err != nil {
		return false
	}

	return exists
}

// Get get a key
func Get(key string) ([]byte, error) {
	conn := RedisConn.Get()
	defer conn.Close()

	reply, err := redis.Bytes(conn.Do("GET", key))
	if err != nil {
		return nil, err
	}

	return reply, nil
}

// Delete delete a kye
func Delete(key string) (bool, error) {
	conn := RedisConn.Get()
	defer conn.Close()

	return redis.Bool(conn.Do("DEL", key))
}

// LikeDeletes batch delete
func LikeDeletes(key string) error {
	conn := RedisConn.Get()
	defer conn.Close()

	keys, err := redis.Strings(conn.Do("KEYS", "*"+key+"*"))
	if err != nil {
		return err
	}

	for _, key := range keys {
		_, err = Delete(key)
		if err != nil {
			return err
		}
	}

	return nil
}

路由初始化

执行的方法是main中的router.InitRouter()
在目录的位置如下:
在这里插入图片描述
router/router.go

package router

import (
	"github.com/gin-gonic/gin"
	"gotest/src/controller"
)

// InitRouter initialize routing information
func InitRouter() *gin.Engine {
	r := gin.New()
	r.Use(gin.Logger())
	r.Use(gin.Recovery())
	r.GET("/queryByDatabase", controller.TestQueryByDatabase)
	r.GET("/setRedis", controller.TestSetRedis)
	return r
}

编写表的实体类,用于类似dao层的处理

编写实体类以及操作

package model

type PersonTable struct {
	Id         int64  `xorm:"pk autoincr"`
	PersonName string `xorm:"varchar(24)"`
	PersonAge  int    `xorm:"int default 0"`
	PersonSex  int    `xorm:"notnull"`
}

func (a *PersonTable) GetAll() ([]PersonTable, error) {
	var persons []PersonTable
	db.Find(&persons)
	return persons, nil
}

对应的sql

CREATE TABLE `person_table` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `person_name` varchar(255) DEFAULT NULL,
  `person_age` int(11) DEFAULT NULL,
  `person_sex` int(11) DEFAULT NULL
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4;

编写接口控制器controller

目录位置:
在这里插入图片描述
controller/testControlelr.go

package controller

import (
	"encoding/json"
	"fmt"
	"github.com/astaxie/beego/logs"
	"github.com/gin-gonic/gin"
	"gotest/src/app"
	"gotest/src/const"
	"gotest/src/gredis"
	"gotest/src/logging"
	"gotest/src/model"
	"net/http"
)

func TestQueryByDatabase(c *gin.Context) {
	appG := app.Gin{C: c}
	personService := model.PersonTable{}
	persons, _ := personService.GetAll()
	fmt.Println(persons)
	appG.Response(http.StatusOK, e.SUCCESS, persons)

}

func TestSetRedis(c *gin.Context) {
	appG := app.Gin{C: c}
	key := c.Query("key")
	value := c.Query("value")
	logging.Info("key:", key, " value:", value)
	err := gredis.Set(key, value, 1000)
	if err != nil {
		logs.Error(err)
	}
	getValue, _ := gredis.Get(key)
	var sss string
	json.Unmarshal(getValue, &sss)
	logs.Info(sss)
	appG.Response(http.StatusOK, e.SUCCESS, sss)

}

里面还调用了2个包的内容
在这里插入图片描述
如下:
app/response.go

package app

import (
	"github.com/gin-gonic/gin"
	"gotest/src/const"
)

type Gin struct {
	C *gin.Context
}

type Response struct {
	Code int         `json:"code"`
	Msg  string      `json:"msg"`
	Data interface{} `json:"data"`
}

// Response setting gin.JSON
func (g *Gin) Response(httpCode, errCode int, data interface{}) {
	g.C.JSON(httpCode, Response{
		Code: errCode,
		Msg:  e.GetMsg(errCode),
		Data: data,
	})
	return
}

const/code.go

package e

const (
	SUCCESS        = 200
	ERROR          = 500
	INVALID_PARAMS = 400

	ERROR_EXIST_TAG       = 10001
	ERROR_EXIST_TAG_FAIL  = 10002
	ERROR_NOT_EXIST_TAG   = 10003
	ERROR_GET_TAGS_FAIL   = 10004
	ERROR_COUNT_TAG_FAIL  = 10005
	ERROR_ADD_TAG_FAIL    = 10006
	ERROR_EDIT_TAG_FAIL   = 10007
	ERROR_DELETE_TAG_FAIL = 10008
	ERROR_EXPORT_TAG_FAIL = 10009
	ERROR_IMPORT_TAG_FAIL = 10010

	ERROR_NOT_EXIST_ARTICLE        = 10011
	ERROR_CHECK_EXIST_ARTICLE_FAIL = 10012
	ERROR_ADD_ARTICLE_FAIL         = 10013
	ERROR_DELETE_ARTICLE_FAIL      = 10014
	ERROR_EDIT_ARTICLE_FAIL        = 10015
	ERROR_COUNT_ARTICLE_FAIL       = 10016
	ERROR_GET_ARTICLES_FAIL        = 10017
	ERROR_GET_ARTICLE_FAIL         = 10018
	ERROR_GEN_ARTICLE_POSTER_FAIL  = 10019

	ERROR_AUTH_CHECK_TOKEN_FAIL    = 20001
	ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 20002
	ERROR_AUTH_TOKEN               = 20003
	ERROR_AUTH                     = 20004

	ERROR_UPLOAD_SAVE_IMAGE_FAIL    = 30001
	ERROR_UPLOAD_CHECK_IMAGE_FAIL   = 30002
	ERROR_UPLOAD_CHECK_IMAGE_FORMAT = 30003
)

const/msg.go

package e

var MsgFlags = map[int]string{
	SUCCESS:                         "ok",
	ERROR:                           "fail",
	INVALID_PARAMS:                  "请求参数错误",
	ERROR_EXIST_TAG:                 "已存在该标签名称",
	ERROR_EXIST_TAG_FAIL:            "获取已存在标签失败",
	ERROR_NOT_EXIST_TAG:             "该标签不存在",
	ERROR_GET_TAGS_FAIL:             "获取所有标签失败",
	ERROR_COUNT_TAG_FAIL:            "统计标签失败",
	ERROR_ADD_TAG_FAIL:              "新增标签失败",
	ERROR_EDIT_TAG_FAIL:             "修改标签失败",
	ERROR_DELETE_TAG_FAIL:           "删除标签失败",
	ERROR_EXPORT_TAG_FAIL:           "导出标签失败",
	ERROR_IMPORT_TAG_FAIL:           "导入标签失败",
	ERROR_NOT_EXIST_ARTICLE:         "该文章不存在",
	ERROR_ADD_ARTICLE_FAIL:          "新增文章失败",
	ERROR_DELETE_ARTICLE_FAIL:       "删除文章失败",
	ERROR_CHECK_EXIST_ARTICLE_FAIL:  "检查文章是否存在失败",
	ERROR_EDIT_ARTICLE_FAIL:         "修改文章失败",
	ERROR_COUNT_ARTICLE_FAIL:        "统计文章失败",
	ERROR_GET_ARTICLES_FAIL:         "获取多个文章失败",
	ERROR_GET_ARTICLE_FAIL:          "获取单个文章失败",
	ERROR_GEN_ARTICLE_POSTER_FAIL:   "生成文章海报失败",
	ERROR_AUTH_CHECK_TOKEN_FAIL:     "Token鉴权失败",
	ERROR_AUTH_CHECK_TOKEN_TIMEOUT:  "Token已超时",
	ERROR_AUTH_TOKEN:                "Token生成失败",
	ERROR_AUTH:                      "Token错误",
	ERROR_UPLOAD_SAVE_IMAGE_FAIL:    "保存图片失败",
	ERROR_UPLOAD_CHECK_IMAGE_FAIL:   "检查图片失败",
	ERROR_UPLOAD_CHECK_IMAGE_FORMAT: "校验图片错误,图片格式或大小有问题",
}

// GetMsg get error information based on Code
func GetMsg(code int) string {
	msg, ok := MsgFlags[code]
	if ok {
		return msg
	}

	return MsgFlags[ERROR]
}

启动

到这里,你可以启动你的go程序,如果你是idea可以直接按main方法的图标启动
在这里插入图片描述

验证结果

在这里插入图片描述

在这里插入图片描述

成功返回正确的数据,从mysql返回正确,从redis设置并返回值,返回正确

总结

我写的这个教程,应该可以满足普通的开发了,本教程结合mysql,redis,log,配置文件,项目结构,都贴合实际开发的场景,如果你想用go-gin来写一个服务,那应该没啥问题,大多服务不就是结合数据库的curd,然后处理后返回数据。如果你正处于迷茫阶段,希望这个教程可以帮助到你,去开启你的go-gin web旅程

年轻人,放手干吧,未来属于你们
just do it

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

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

相关文章

JMeter启动时常见的错误

很多小伙伴在学工具这一块时,安装也是很吃力的一个问题,之前记得有说过怎么安装jmeter这个工具。那么你要启动jmeter的时候,一些粉丝就会碰到如下几个问题。 1.解压下载好的jmeter安装,Windows 平台,双击 jmeter/bin …

基于python实现的SGM半全局立体匹配算法

文章目录前言一、SGM是什么?1.代价计算2.代价聚合3.视察计算4.视察优化二、基于python实现SGM算法?总结前言 开始正是入门立体匹配算法啦,会不断更新立体匹配的算法和代码。   水平有限,旨在先了解和读懂别人的代码的实现方式&a…

3D立体匹配入门 - 视差计算

经典假设 1、左右视图成功匹配的窗口,具有相同的像素 这个是最经典的假设,几乎所有视差图计算都用上了他,通过匹配左右窗口像素,得到最佳匹配对应的x轴坐标差,就是视差 2、像素P的视差只与其领域有关 这个是基于马尔…

外汇天眼:即使与世界第一的差价合约提供商交易也会被骗!

你能想象,当你与世界第一的差价合约提供商进行交易时,也可能会被骗吗? 在投资理财多元化的今天,外汇投资理财也备受大家的关注,而与此同时,骗子的诈骗渠道也与时俱进,各类外汇投资骗局也层出不穷…

VMware Workstation 17.0 Pro SLIC Unlocker for Linux

VMware_Dell_2.6_BIOS-EFI64_Mod;macOS Unlocker,支持 macOS Ventura 请访问原文链接:VMware Workstation 17.0 Pro SLIC & Unlocker for Windows & Linux,查看最新版。原创作品,转载请保留出处。 作者主页&a…

多模式直方图的视网膜图像增强

论文题目:Retinal Image Enhancement in Multi-Mode Histogram 1 摘要 视网膜图像的评估被广泛用于帮助医生诊断许多疾病,如糖尿病或高血压。从采集过程来看,视网膜图像往往具有较低的灰度对比度和动态范围。本文提出了一种基于直方图分析的…

MySQL回表

1.索引结构 1.1.B-Tree(B树)和BTree(B树) 前面是B-Tree,后面是BTree,两者的区别在于: B-Tree中,所有的节点都会带有指向具体记录的指针;BTree中只有叶子节点才会带有指向具体记录的指针;B-Tree中,不同的叶子之间没有连在一起;BTree中所有的叶子节点通过指针连接在一起;B-Tree中…

java版商城之 Spring Cloud+SpringBoot+mybatis+uniapp b2b2c o2o 多商家入驻商城 直播带货商城 电子商务

一个好的SpringCloudSpringBoot b2b2c 电子商务平台涉及哪些技术、运营方案?以下是我结合公司的产品做的总结,希望可以帮助到大家! 搜索体验小程序:海哇 1. 涉及平台 平台管理、商家端(PC端、手机端)、买…

浴室预约小程序毕业设计,洗澡预约澡堂预约系统设计与实现,微信小程序毕业设计论文怎么写毕设源码开题报告需求分析怎么做

项目背景和意义 目的:本课题主要目标是设计并能够实现一个基于微信小程序浴室预约系统,前台用户使用小程序,后台管理使用JavaMysql开发,后台使用了springboot框架;通过后台添加设定浴室类型、录入浴室和管理浴室、管理…

CTPN+CRNN算法端到端实现文字识别的实战开发

本文分享自华为云社区《CTPNCRNN 算法端到端实现文字识别》,作者:HWCloudAI。 OCR介绍 光学字符识别(英语:Optical Character Recognition,OCR)是指对文本资料的图像文件进行分析识别处理,获取…

Java规则引擎Drools急速入门

文章目录1.Drools规则引擎简介2.Drools API开发步骤3.SpringBoot整合Drools案例4.Drools基础语法5.Drools条件语法部分6.Drools结果操作部分7.Drools内置属性部分8.Drools高级语法部分1.Drools规则引擎简介 (1)什么是规则引擎 ​ 全称为业务规则管理系…

类与对象(上篇)

类与对象面向过程和面向对象类的引入类的定义类的访问限定符及封装访问限定符封装类的作用域类的实例化类对象类对象的存储方式类成员函数的this指针this指针的引出this指针的特性面向过程和面向对象 C语言是面向过程,注重的是过程,先分析求解问题的步骤…

【计算机视觉】目标检测中Faster R-CNN、R-FCN、YOLO、SSD等算法的讲解(图文解释 超详细必看)

觉得有帮助请点赞关注收藏~~~ 一、基于候选区域的目标检测算法 基于候选区域的深度卷积神经网络(Region-based Convolutional Neural Networks)是一种将深度卷积神经网络和区域推荐相结合的物体检测方法,也可以叫做两阶段目标检测算法。第一…

Web大学生网页作业成品——环保垃圾分类网站设计与实现(HTML+CSS+JavaScript) web前端开发技术 web课程设计 网页规划与设计

🎀 精彩专栏推荐👇🏻👇🏻👇🏻 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业…

RFID标签让企业海量固定资产实现科学化管理

近年来,随着物联网、IoT、移动技术、云计算技术的成熟,越来越多的企业开始使用RFID标签管理企业海量的固定资产。优化固定资产标准化管理流程,有效管理和库存固定资产,进一步提高企业实物资产管理和库存效率。 包括资产申购、验收…

【操作系统】计算机大脑CPU

1.CPU组成机构和存储器层级 (1)CPU是计算机硬件系统的核心部件-大脑 结构:运算器控制器(两个部件里面有寄存器组)通过CPU内部的总线进行通信 (2)单核CPU架构 控制器Control Unit简称【CU】 …

Python使用Opencv图像处理方法完成手势识别(二)

Opencv完成手势识别根据坐标识别寻找最低点计算其他点与最低点的距离通过距离阈值判断手指根数和手势效果展现完整代码当我们把手近似出来后会得到一组轮廓的点坐标,我自己手势识别的思路就是根据点坐标来判断手势。根据坐标识别 寻找最低点 所谓寻找最低点&#…

浅谈Nacos注册中心集群分布式架构设计

前言 Nacos的压测性能是非常好的,这里是Nacos官方的压测报告。3节点(CPU 16核,内存32G)规模集群,压测容量服务数可达60W,实例注册数达110W,集群运行持续稳定,达到预期;注册/查询实例TPS达到 13…

ModStartBlog v6.3.0 任务调度重构,UEditor 升级

系统介绍 ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用,支持后台一键快速安装,让开发者能快的实现业务功能开发。 系统完全开源,基于 Apache 2.0 开源协议。 功能特性 丰富的模块市场,后台一键…

跨设备链路聚合 M-LAG

M-LAG(Multichassis Link Aggregation Group)即跨设备链路聚合组,是一种实现跨设备链路聚合的机制,如下图所示,将SwitchA和SwitchB通过peer-link链路连接并以同一个状态和Switch进行链路聚合协商,从而把链路…