IM 通信系统 --- 项目介绍及环境搭建
1. 项目简介
1. 核心功能
1. 消息发送与接收
包括: 文字、表情、图片、音频、视频等
2. 聊天模式
- 访客模式
- 点对点私聊
- 群聊(广播)
- 机器人
3. 其他功能
- 心跳检测下线
- 快捷回复
- 撤回记录
- 拉黑等
2. 技术栈
1. 前端
- H5 ajax 获取音频
- websocket 发送消息
- js/vue 单页面APP
2. 后端
- wesocket 组件转发消息
- channel && goroutine 提高并发
- gin
- template
- swagger
- gorm
- logger
- govalidator
- SQL
- NoSQL
- MQ
3. 架构图
2. 环境搭建
1. 初始化
go mod init any_chat
2. 下载依赖
go get gorm.io/driver/mysql
go get gorm.io/gorm
go get github.com/gin-gonic/gin
go get gopkg.in/yaml.v2 # yaml文件读取
go get github.com/sirupsen/logrus # 日志
3. yaml 配置文件
0. 根配置
gin_any_chat/config/enter.go
package config
type Config struct {
}
1. 环境 配置
gin_any_chat/settings.go
system:
host: "0.0.0.0"
port: 8089
env: release
gin_any_chat/config/system_conf.go
package config
type System struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Env string `yaml:"env"`
}
gin_any_chat/config/enter.go
package config
type Config struct {
Mysql Mysql `yaml:"mysql"`
Logger Logger `yaml:"logger"`
System System `yaml:"system"`
}
2. MySQL 配置
gin_any_chat/settings.go
mysql:
host: 81.70.168.242
port: 33061
db: gin_any_chat
user: root
password: 123456
max-idle-conns: 10
max-open-conns: 100
log_level: dev
config: charset=utf8mb4&parse-time=True&loc=Local
gin_any_chat/config/mysql_conf.go
package config
type Mysql struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Config string `yaml:"config"` // 高级配置,如 charset 等
DB string `yaml:"db"`
User string `yaml:"user"`
Password string `yaml:"password"`
MaxIdleConns int `json:"max-idle-conns" yaml:"max-idle-conns"` // 空闲中的最大连接数
MaxOpenConns int `json:"max-open-conns" yaml:"max-open-conns"` // 最大建立连接数
Charset string `yaml:"charset"` // 编码格式
ParseTime string `yaml:"parse-time"` // 是否开启时间同步
Loc string `yaml:"loc"` // 时间同步为本地时间
LogLevel string `yaml:"log_level"` // 日志等级,debug就是输出全部SQL,dev,release(线上环境)
}
gin_any_chat/config/enter.go
package config
type Config struct {
Mysql Mysql `yaml:"mysql"`
}
3. 日志 配置
gin_any_chat/settings.go
logger:
level: info
prefix: '[blog]'
director: log
show_line: true
log_in_console: true
gin_any_chat/config/logger_conf.go
package config
type Logger struct {
Level string `yaml:"level"`
Prefix string `yaml:"prefix"`
Director string `yaml:"director"`
ShowLine bool `yaml:"show-line"` // 是否显示行号
LogInConsole bool `yaml:"log-in-console"` // 是否显示打印的路径
}
gin_any_chat/config/enter.go
package config
type Config struct {
Mysql Mysql `yaml:"mysql"`
Logger Logger `yaml:"logger"`
}
gin_any_chat/core/log.go
package core
import (
"bytes"
"fmt"
"gin_any_chat/global"
"github.com/sirupsen/logrus"
"os"
"path"
)
// 日志颜色
const (
red = 31
yellow = 33
blue = 36
gray = 37
)
type LogFormatter struct{}
func (t *LogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
// 根据不同的level 显示颜色
var levelColor int
switch entry.Level {
case logrus.DebugLevel, logrus.TraceLevel:
levelColor = gray
case logrus.WarnLevel:
levelColor = yellow
case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
levelColor = red
default:
levelColor = blue
}
var b *bytes.Buffer
if entry.Buffer != nil {
b = entry.Buffer
} else {
b = &bytes.Buffer{}
}
prefix := global.Config.Logger.Prefix
// 自定义日期格式
timestamp := entry.Time.Format("2006-01-02 15:04:05")
if entry.HasCaller() {
// 自定义文件路径
funcVal := entry.Caller.Function
fileVal := fmt.Sprintf("%s:%d", path.Base(entry.Caller.File), entry.Caller.Line)
// 自定义输出格式
fmt.Fprintf(b, "%s[%s] \x1b[%dm[%s]\x1b[0m %s %s %s\n", prefix, timestamp, levelColor, entry.Level, fileVal, funcVal, entry.Message)
} else {
fmt.Fprintf(b, "%s[%s] \x1b[%dm[%s]\x1b[0m %s\n", prefix, timestamp, levelColor, entry.Level, entry.Message)
}
return b.Bytes(), nil
}
func InitLogger() *logrus.Logger {
mLog := logrus.New() // 创建一个实例
mLog.SetOutput(os.Stdout) // 设置输出类型
mLog.SetReportCaller(global.Config.Logger.ShowLine) // 开启返回函数和行号
mLog.SetFormatter(&LogFormatter{}) // 设置自己定义的Formatter
level, err := logrus.ParseLevel(global.Config.Logger.Level)
if err != nil {
level = logrus.InfoLevel
}
mLog.SetLevel(level) // 设置最低的Level
InitDefaultLogger() // 修改 logrus 全局log样式
return mLog
}
func InitDefaultLogger() {
// 修改 logrus 全局log样式
logrus.SetOutput(os.Stdout) // 设置输出类型
logrus.SetReportCaller(global.Config.Logger.ShowLine) // 开启返回函数和行号
logrus.SetFormatter(&LogFormatter{}) // 设置自己定义的Formatter
level, err := logrus.ParseLevel(global.Config.Logger.Level)
if err != nil {
level = logrus.InfoLevel
}
logrus.SetLevel(level) // 设置最低的Level
}
gin_any_chat/global/global.go
package global
import (
"gin_any_chat/config"
)
var (
Config *config.Config
Logger *logrus.Logger
)
gin_any_chat/main.go
package main
import (
"gin_blog/core"
"gin_blog/global"
)
func main() {
// 1. 读取配置文件
core.InitConf()
// 2. 日志配置初始化, 并设置到全局变量
global.Logger = core.InitLogger()
}
4. Gorm 配置
gin_any_chat/config/mysql_conf.go
package config
import "strconv"
type Mysql struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Config string `yaml:"config"` // 高级配置,如 charset 等
DB string `yaml:"db"`
User string `yaml:"user"`
Password string `yaml:"password"`
MaxIdleConns int `json:"max-idle-conns" yaml:"max-idle-conns"` // 空闲中的最大连接数
MaxOpenConns int `json:"max-open-conns" yaml:"max-open-conns"` // 最大建立连接数
Charset string `yaml:"charset"` // 编码格式
ParseTime string `yaml:"parse-time"` // 是否开启时间同步
Loc string `yaml:"loc"` // 时间同步为本地时间
LogLevel string `yaml:"log_level"` // 日志等级,debug就是输出全部SQL,dev,release(线上环境)
}
func (m *Mysql) Dsn() string {
return m.User + ":" + m.Password + "@tcp(" + m.Host + ":" + strconv.Itoa(m.Port) + ")/" + m.DB + "?" + m.Config
}
gin_any_chat/core/gorm.go
package core
import (
"fmt"
"gin_any_chat/global"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"time"
)
func InitGorm() *gorm.DB {
if global.Config.Mysql.Host == "" {
global.Logger.Warn("未配置mysql,取消gorm连接")
return nil
}
// 1. 根据配置文件得到连接字符串
dsn := global.Config.Mysql.Dsn()
// 2. 设置 MySQL 日志级别和系统环境同步
var mySQLogger logger.Interface
if global.Config.System.Env == "debug" {
// 开发环境显示所有执行的 SQL 语句
mySQLogger = logger.Default.LogMode(logger.Info)
} else {
mySQLogger = logger.Default.LogMode(logger.Error)
}
// 3. 建立 MySQL 连接, 并配置
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: mySQLogger,
})
if err != nil {
global.Logger.Error(fmt.Sprintf("[%s] mysql 连接失败", dsn))
panic(err)
}
sqlDB, _ := db.DB()
sqlDB.SetMaxIdleConns(global.Config.Mysql.MaxIdleConns) // 最大空闲连接数
sqlDB.SetMaxOpenConns(global.Config.Mysql.MaxOpenConns) // 最多可容纳连接数
sqlDB.SetConnMaxLifetime(time.Hour * 4) // 连接最大服用时间,不能超过mysql的 wait_timeout
return db
}
gin_any_chat/global/global.go
package global
import (
"gin_any_chat/config"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
var (
Config *config.Config
Logger *logrus.Logger
DB *gorm.DB
)
main.go
package main
import (
"gin_any_chat/core"
"gin_any_chat/global"
"gin_any_chat/routers"
)
func main() {
// 1. 建立连接, 并配置 编码方式、时区
//db, err := gorm.Open(mysql.Open("root:123456@tcp(81.70.168.242:33061)/gin_any_chat?charset=utf8mb4&parseTime=True&loc=Local"), &gorm.Config{})
//if err != nil {
// panic("连接数据库发生错误")
//}
// 2. 自动迁移模型
//db.AutoMigrate(&models.UserInfo{})
// 1. 配置文件初始化
core.InitConf()
// 2. 日志配置初始化, 并设置到全局变量
global.Logger = core.InitLogger()
// 3. 建立 MySQL 连接, 并设置到全局变量
global.DB = core.InitGorm()
}
4. 统一响应 配置
1. 自定义状态码
gin_any_chat/models/response/error_code.go
package response
type ErrorCode int
const (
SETTINGS_ERROR ErrorCode = 5001
)
var (
ErrorMap = map[ErrorCode]string{
SETTINGS_ERROR: "系统错误",
}
)
2. 统一响应函数
gin_any_chat/models/response/response.go
package response
import (
"github.com/gin-gonic/gin"
"net/http"
)
const (
ERROR = iota
SUCCESS
)
const (
PANIC_ERROR = "未知错误"
)
type Response struct {
Code int `json:"code"`
Data any `json:"data"`
Message string `json:"message"`
}
type PageListResponse struct {
Code int `json:"code"`
Count int64 `json:"count"`
Data any `json:"data"`
Message string `json:"message"`
}
// Result 统一返回函数
func Result(code int, data any, msg string, c *gin.Context) {
c.JSON(http.StatusOK, &Response{
Code: code,
Data: data,
Message: msg,
})
}
// Ok 请求成功返回数据
func Ok(data any, msg string, c *gin.Context) {
Result(SUCCESS, data, msg, c)
}
// OkWithData 查询数据库成功返回数据
func OkWithData(data any, c *gin.Context) {
Result(SUCCESS, data, "查询成功", c)
}
// OkWithMessage 请求成功只显示提示信息
func OkWithMessage(msg string, c *gin.Context) {
Result(SUCCESS, map[string]any{}, msg, c)
}
// Fail 请求失败返回数据
func Fail(data any, msg string, c *gin.Context) {
Result(ERROR, data, msg, c)
}
// FailWithMessage 请求失败只显示提示信息
func FailWithMessage(msg string, c *gin.Context) {
Result(ERROR, map[string]any{}, msg, c)
}
// OkWithDataPageList 请求成功返回分页数据
func OkWithDataPageList(count int64, data any, c *gin.Context) {
c.JSON(http.StatusOK, &PageListResponse{
Code: SUCCESS,
Count: count,
Data: data,
Message: "请求成功",
})
}
// FailWithCode 请求失败,根据不同code码显示不同提示信息
func FailWithCode(code ErrorCode, c *gin.Context) {
msg, ok := ErrorMap[code]
if !ok {
Result(ERROR, map[string]any{}, PANIC_ERROR, c)
return
}
Result(int(code), map[string]any{}, msg, c)
}
3. 请求参数校验失败自动返回报错信息
gin_any_chat/utils/valid.go
package utils
import (
"github.com/go-playground/validator/v10"
"reflect"
)
// GetValidMsg 根据错误信息中的字段名, 返回结构体中的 msg 错误信息
func GetValidMsg(err error, obj any) string {
getObj := reflect.TypeOf(obj)
// 断言 err 是否是 validator.ValidationErrors 类型
if errs, ok := err.(validator.ValidationErrors); ok {
for _, e := range errs {
// 循环每一个错误信息, 根据报错字段名, 获取结构体的具体字段
if f, exits := getObj.Elem().FieldByName(e.Field()); exits {
msg := f.Tag.Get("msg")
return msg
}
}
}
return err.Error()
}
gin_any_chat/models/response/response.go
// .......
// FailWithError 请求参数校验失败自动返回错误信息
func FailWithError(err error, obj any, c *gin.Context) {
if err == io.EOF {
FailWithMessage("参数错误", c)
return
}
msg := utils.GetValidMsg(err, obj)
FailWithMessage(msg, c)
}
4. 加载错误码到内存
gin_any_chat/modesl/response/error_code.json
{
"5001": "系统错误"
}
gin_any_chat/core/error_code.go
package core
import (
"encoding/json"
"gin_any_chat/global"
"github.com/sirupsen/logrus"
"os"
)
const filePath = "models/response/error_code.json"
func ReadErrorCodeFromJson() {
byteData, err := os.ReadFile(filePath)
if err != nil {
logrus.Errorf("读取错误码Json文件错误: %s", err)
return
}
var errMap global.ErrorMap
err = json.Unmarshal(byteData, &errMap)
if err != nil {
logrus.Errorf("Json反序列化到Map错误: %s", err)
return
}
global.ErrorCodes = &errMap
}
global/global.go
package global
import (
"gin_any_chat/config"
"gin_any_chat/models/response"
"github.com/sirupsen/logrus"
"gorm.io/gorm"
)
type ErrorMap map[response.ErrorCode]string
var (
Config *config.Config
Logger *logrus.Logger
DB *gorm.DB
ErrorCodes ErrorMap
)
gin_any_chat/main.go
package main
import (
"gin_any_chat/core"
"gin_any_chat/global"
"gin_any_chat/routers"
)
func main() {
// 1. 配置文件初始化
core.InitConf()
// 2.读错误码 JSON 文件到内存中
core.ReadErrorCodeFromJson()
// 3. 日志配置初始化, 并设置到全局变量
global.Logger = core.InitLogger()
// 4. 建立 MySQL 连接, 并设置到全局变量
global.DB = core.InitGorm()
// 5. 根据配置文件, 启动服务器
router := routers.InitRouter()
addr := global.Config.System.Addr()
global.Logger.Infof("项目启动成功,运行在: %s", addr)
router.Run(addr)
}
5. 初始化配置文件
gin_any_chat/global/global.go
package global
import "gin_blog/config"
var (
Config *config.Config
)
gin_any_chat/core/conf.go
package core
import (
"fmt"
"gin_any_chat/config"
"gopkg.in/yaml.v3"
"log"
"os"
)
func InitConf() {
const ConfigFile = "settings.yaml"
c := &config.Config{}
// 1. 读取文件中的内容
yamlConf, err := os.ReadFile(ConfigFile)
if err != nil {
panic(fmt.Errorf("读取配置文件初失败: %s", err))
}
// 2. 将配置文件的内容赋值给 Config 对象
err = yaml.Unmarshal(yamlConf, c)
if err != nil {
log.Fatalf("配置文件初始化失败: %v", err)
}
log.Println("配置文件初始化成功!")
// 3. 将配置文件对象设置到全局变量, 供全局调用
global.Config = c
}
启动文件调用
gin_any_chat/main.go
package main
import (
"gin_any_chat/core"
)
func main() {
// 1. 配置文件初始化
core.InitConf()
}
6. 路由、API 分包
1. API 分包
gin_any_chat/api/user_api/views.go
package user_api
import (
"github.com/gin-gonic/gin"
"net/http"
)
// UserInfoView 响应函数
func (ua UserApi) UserInfoView(c *gin.Context) {
c.String(http.StatusOK, "ok")
}
gin_any_chat/api/user_api/enter.go
package user_api
type UserApi struct {
}
gin_any_chat/api/enter.go
package api
import "gin_any_chat/api/user_api"
type ApiGroup struct {
UserApis user_api.UserApi // 用户相关的所有 API
}
var ApiGroupApp = new(ApiGroup)
2. 路由 分包
gin_any_chat/routers/user_router.go
package routers
import "gin_any_chat/api"
// UserRouters 用户相关的所有路由映射
func (router *RouterGroup) UserRouters() {
router.GET("/index", api.ApiGroupApp.UserApis.UserInfoView)
}
gin_any_chat/routers/enter.go
package routers
import (
"github.com/gin-gonic/gin"
)
type RouterGroup struct {
*gin.RouterGroup
}
// 路由初始化
func InitRouter() *gin.Engine {
// 1. 设置
ginMode := global.Config.System.GinMode()
gin.SetMode(ginMode)
router := gin.Default()
apiRouterGroup := router.Group("api") // 添加 /api 路由前缀
routerGroupApp := &RouterGroup{apiRouterGroup} // 初始化路由分组对象
routerGroupApp.UserRouters() // 配置所有用户相关的路由映射关系
return router
}
3. 服务启动
gin_any_chat/config/system_conf.go
package config
import "fmt"
type System struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Env string `yaml:"env"`
}
func (s System) Addr() string {
return fmt.Sprintf("%s:%d", s.Host, s.Port)
}
func (s System) GinMode() string {
return fmt.Sprintf("%s", s.Env)
}
gin_any_chat/main.go
package main
import (
"gin_any_chat/models"
"gin_any_chat/routers"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 初始化路由
routers := routers.InitRouter()
// 服务启动
routers.Run("127.0.0.1:8848")
}
7. SQL 模型创建及迁移
1. 用户信息表
gin_any_chat/models/UserInfo.go
package models
import "gorm.io/gorm"
type UserInfoModel struct {
gorm.Model // 公共字段: ID, CreatedAt, UpdatedAt, DeletedAt
Name string
PassWord string
Phone string
Email string
Identity string // 用户唯一标识
ClientIp string // 用户 IP
ClientPort uint16 // 用户 端口
IsLogout bool // 是否下线
DeviceInfo string // 用户使用的设备信息
LoginAt uint64 // 最近一次上线的时间
LogoutAt uint64 // 最近一次离线时间
HeartbeatAt uint64 // 最近一次心跳时间
}
// TableName 结构体 UserInfo 在数据库中生成的表名
func (u *UserInfoModel) TableName() string {
return "user_info_model"
}
2. 命令行迁移
gin_any_chat/common/enter.go
package common
import (
sys_flag "flag"
)
type Option struct {
Version bool
DB bool
}
func Parse() Option {
db := sys_flag.Bool("db", false, "初始化数据库")
// 解析命令行参数写入注册的flag中
sys_flag.Parse()
return Option{
//Version: *version,
DB: *db,
}
}
// IsWebStop 判断是否需要停止web项目
func IsWebStop(option Option) bool {
if option.DB {
return true
}
//if option.Version {
// return false
//}
return false
}
// 根据不用命令执行不同函数
func SwitchOption(option Option) {
if option.DB {
MakeMigrations()
}
//if option.Version {
// Version()
//}
}
gin_any_chat/common/db.go
package common
import (
"gin_any_chat/global"
"gin_any_chat/models"
)
func MakeMigrations() {
// 1. 提前声明绑定的中间表
//global.DB.SetupJoinTable(&models.UserInfoModel{}, "CollectsModels", &models.UserCollectModel{})
// 2. 自动迁移模型, 生成表结构到 MySQL
err := global.DB.Set("gorm:table_options", "Engine=InnoDB").
AutoMigrate(
&models.UserInfoModel{},
)
if err != nil {
global.Logger.Errorf("[ error ] 数据库迁移失败,%s", err.Error())
return
}
global.Logger.Info("[ success ] 数据库迁移成功")
}
gin_any_chat/main.go
package main
import (
"gin_any_chat/common"
"gin_any_chat/core"
"gin_any_chat/global"
"gin_any_chat/routers"
)
func main() {
//fmt.Println("main")
// 1. 配置文件初始化
core.InitConf()
// 2.读错误码 JSON 文件到内存中
core.ReadErrorCodeFromJson()
//3. 日志配置初始化, 并设置到全局变量
global.Logger = core.InitLogger()
// 4. 建立 MySQL 连接, 并设置到全局变量
global.DB = core.InitGorm()
// 5. 命令行参数解析
option := common.Parse()
// 如果需要停止web项目,就不会执行下面的启动项目
if common.IsWebStop(option) {
common.SwitchOption(option)
return
}
common.SwitchOption(option)
// 6. 根据配置文件, 启动服务器
router := routers.InitRouter()
addr := global.Config.System.Addr()
global.Logger.Infof("项目启动成功,运行在: %s", addr)
router.Run(addr)
}
3. 执行命令
go run main.go -db
8. 自动生成 API 文档 ( ginSwagger )
1. 环境搭建
1. 安装模块
go get github.com/swaggo/swag/cmd/swag
go get github.com/swaggo/gin-swagger
go get github.com/swaggo/files
2. 配置注释
main.go 中的 mian 函数上方添加注释内容
package main
import (
"gin_any_chat/common"
"gin_any_chat/core"
"gin_any_chat/global"
"gin_any_chat/routers"
)
// @title IM通信系统 API文档
// @version 1.0
// @description IM通信系统 API文档
// @host 127.0.0.1:8848
// @BasePath /
func main() {
}
3. 初始化生成 API 文档
执行以下命令后, 会自动在项目根目录下生成 docs 文件夹, 其中是 API 文档的 HTML 文件
swag init
2. 使用
1. 引入 docs 目录
main.go
package main
import (
_ "gin_any_chat/docs" // 导入 swagger 生成文档目录
)
2. 添加文档访问路由
gin_any_chat/routers/init_router.go
package routers
import (
swaggerFiles "github.com/swaggo/files"
gs "github.com/swaggo/gin-swagger"
)
func (router *RouterGroup) InitRoters() {
router.GET("/swagger/*any", gs.WrapHandler(swaggerFiles.Handler))
}
gin_any_chat/routers/enter.go
package routers
import (
"gin_any_chat/global"
"github.com/gin-gonic/gin"
)
type RouterGroup struct {
*gin.RouterGroup
}
func InitRouter() *gin.Engine {
ginMode := global.Config.System.GinMode()
gin.SetMode(ginMode)
router := gin.Default()
apiRouterGroup := router.Group("api")
routerGroupApp := &RouterGroup{apiRouterGroup}
// 配置路由映射关系
routerGroupApp.UserRouters()
routerGroupApp.InitRoters()
return router
}
再次执行命令
swag init
3. 视图添加注释
gin_any_chat/api/user_api/views.go
package user_api
import (
"github.com/gin-gonic/gin"
"net/http"
)
// @Tags 首页
// @Summary 首页
// @Description 描述,可以有多个
// @Param limit query string false "如果有query参数,则加上,没有则不加,query 表示query查询参数,,false表示没有,true表示必填"
// @Param data query string false "如果有body参数,则加上,没有则不加,body表示请求体查询参数,false表示没有,true表示必填"
// @Router /api/index [get] "表示get请求"
// @Produce json
// @Success 200 {object} response.Response{}
func (ua UserApi) UserInfoView(c *gin.Context) {
c.JSON(http.StatusOK, &gin.H{"message": "OK"})
}
再次执行命令
swag init
**示例: **
1. 创建广告
// AdvertCreateView 创建广告
// @Tags 广告管理
// @Summary 创建广告
// @Description 描述,可以有多个
// @Param data body AdvertParams true "表示body的查询参数,false表示没有,true表示必填"
// @Router /api/adverts [post]
// @Produce json
// @Success 200 {object} response.Response{}
func (AdvertApi) AdvertCreateView(c *gin.Context) {}
2. 广告列表
// AdvertListView 广告列表
// @Tags 广告列表
// @Summary 广告列表
// @Description 描述,可以有多个
// @Param data query models.PageParams true "如果有body参数,则加上,没有则不加,body表示请求体查询参数,false表示没有,true表示必填"
// @Router /api/adverts [get] "表示get请求"
// @Produce json
// @Success 200 {object} response.PageListResponse{}
func (AdvertApi) AdvertListView(c *gin.Context) {}
3. 广告更新
// AdvertUpdateView 广告更新
// @Tags 广告管理
// @Summary 广告更新
// @Description 描述,可以有多个
// @Param data body AdvertParams true "广告参数"
// @Router /api/adverts/:id [put]
// @Produce json
// @Success 200 {object} response.Response{data=string}
func (AdvertApi) AdvertUpdateView(c *gin.Context) {}
4. 广告批量删除
// AdvertRemoveView 广告批量删除
// @Tags 广告管理
// @Summary 广告批量删除
// @Description 描述,可以有多个
// @Param data body models.RemoveParams true "广告ID列表"
// @Router /api/adverts [delete]
// @Produce json
// @Success 200 {object} response.Response{data=string}
func (AdvertApi) AdvertRemoveView(c *gin.Context) {}
3. 访问
http://127.0.0.1:8848/api/swagger/index.html

浙公网安备 33010602011771号