[GO] 图书管理系统API

news2025/7/17 11:36:14

图书管理系统

1. 创建项目

请添加图片描述

2. 配置goproxy

GOPROXY=https://goproxy.cn

请添加图片描述

3. 添加格式化工具

请添加图片描述

4. 定义目录结构

|---- Readme.md //项目说明
|---- config // 配置文件(mysql配置,ip,端口,用户名,密码等)
|---- controller // CLD服务入口,负责处理路由,参数校验,请求转发
|---- service // CLD,逻辑(服务)层,负责业务逻辑处理
|---- dao
|---- mysql
|---- model // 模型(定义表结构)
|---- logging // 日志处理
|---- main.go // 项目启动入口
|---- middleware // 中间件
|---- pkg // 公共服务,所有模块都能访问的服务
|---- router // 路由

4.2 实际目录的结构

— d:\bookmanage
│ go.mod
│ go.sum
│ main.go

├─.idea
│ .gitignore
│ bookmanage.iml
│ modules.xml
│ watcherTasks.xml
│ workspace.xml

├─controller
│ book.go
│ users.go

├─dao
│ └─mysql
│ mysql.go

├─model
│ book.go
│ user.go
│ user_m2m_book.go

└─router
api_router.go
init_router.go
test_router.go

5. 创建数据库

mysql> create database books charset utf8;

6. 项目要实现的功能

  • 图书管理服务
    • 用户服务: 登录,注册
    • 书籍服务: 对书籍的增删改查

7. main.go

7.1 安装gin

go: D:\bookmanage\go.mod already exists
PS D:\bookmanage> go get github.com/gin-gonic/gin
go: added github.com/gin-contrib/sse v0.1.0
go: added github.com/gin-gonic/gin v1.8.1
go: added github.com/go-playground/locales v0.14.0
go: added github.com/go-playground/universal-translator v0.18.0
go: added github.com/go-playground/validator/v10 v10.10.0
go: added github.com/goccy/go-json v0.9.7
go: added github.com/json-iterator/go v1.1.12
go: added github.com/leodido/go-urn v1.2.1
go: added github.com/mattn/go-isatty v0.0.14
go: added github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421
go: added github.com/modern-go/reflect2 v1.0.2
go: added github.com/pelletier/go-toml/v2 v2.0.1
go: added github.com/ugorji/go/codec v1.2.7
go: added golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
go: added golang.org/x/net v0.0.0-20210226172049-e18ecbb05110
go: added golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069
go: added golang.org/x/text v0.3.6
go: added google.golang.org/protobuf v1.28.0
go: added gopkg.in/yaml.v2 v2.4.0

此时go.mod下确认有此条信息

github.com/gin-gonic/gin v1.8.1 // indirect

7.2 简单的测试下

package main

import (
	"fmt"

	"github.com/gin-gonic/gin"
)

func main() {
	// 1. 初始化
	r := gin.Default()
	// 2. 定义路由
	r.GET("/", func(c *gin.Context) {
		c.String(200, "Success")
	})
	fmt.Println("http://192.168.31.1:8080/")
	r.Run(":8080")
}

请添加图片描述

8. 路由分层

8.1 新建router目录

PS D:\bookmanage> mkdir router

8.2 总的路由入口

router/init_router.go

package router

import "github.com/gin-gonic/gin"

/*
加载其他路由文件中的路由
*/

func InitRouter() *gin.Engine {
	r := gin.Default()
	return r
}

修改main.go

将
r := gin.Default()
改为
r := router.InitRouter()

8.3 测试路由

router/test_router.go

package router

import "github.com/gin-gonic/gin"

func LoadTestRouter(r *gin.Engine) {
	r.GET("/test", func(c *gin.Context) {
		c.String(200, "test")
	})
}

修改router/init_router.go

package router

import "github.com/gin-gonic/gin"

/*
加载其他路由文件中的路由
*/

func InitRouter() *gin.Engine {
	r := gin.Default()
	LoadTestRouter(r)	// 加入这行,这样就能加载/test了
	return r
}

请添加图片描述

9. 数据库连接

9.1 安装2个Package

PS D:\bookmanage> go get gorm.io/driver/mysql
PS D:\bookmanage> go get gorm.io/gorm   

9.2 定义全局数据库连接

9.2.1 数据库连接

dao/mysql/mysql.go

定义全局变量DB为数据库连接

import (
	"fmt"

	gmysql "gorm.io/driver/mysql"
	"gorm.io/gorm"
)

// 全局定义,其他地方可以直接使用
var DB *gorm.DB

// 初始化mysql连接
func InitMysql() {
	dsn := "root:123456@tcp(192.168.31.24:3306)/books?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(gmysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Printf("数据库连接失败", err)
	}
	DB = db
}

这样就能在其他地方实现调用

9.2.2 main.go

func main() {
	// 初始化mysql
	mysql.InitMysql()		// <<-----这条就调用了初始化数据库
	fmt.Println(mysql.DB, 1111111111)
	// 1. 实例化router服务的方法拆分到router.InitRouter
	r := router.InitRouter()
	// 2. 定义路由
	r.GET("/", func(c *gin.Context) {
		c.String(200, "Success")
	})
	fmt.Println("http://192.168.31.1:8080/")
	r.Run(":8080")
}
结果: 获取到了数据库连接
&{0xc000130240 <nil> 0 0xc0003b2700 1} 1111111111

9.3 定义表结构

9.3.1 创建model目录

mkdir model

9.3.2 user表

model/user.go

参数含义
json:“username”json反向解析名字
gorm:not null字段在数据库内不能为空
binding:“required”请求时不能为空,shouldbind参数校验
package model

type User struct {
	Id       int64  `json:"id gorm:primaryKey"`
	Username string `json:"username" gorm:"not null" binding:"required"`
	Password string `json:"password" gorm:"not null" binding:"required"`
	Token    string `json:"token"`
}

// 表名默认会添加s,自定义表名
func (User) TableName() string {
	return "user"
}

9.3.3 book表

package model

type Book struct {
	Id    int64  `json:"id" gorm:"primaryKey"`
	Name  string `json:"name" gorm:"not null" binding:"required"`
	Desc  string `json:"desc"`
	Users []User `gorm:"many2many:book_users"`
}

func (Book) TableName() string {
	return "book"
}

9.3.4 book_users表

package model

// 用户与书籍关联关系表
type BookUser struct {
	UserID int64 `gorm:"primaryKey"`
	BookID int64 `gorm:"primaryKey"`
}

9.3.5 InitMysql

dao/mysql/mysql.go

// 初始化mysql连接
func InitMysql() {
	dsn := "root:123456@tcp(192.168.31.24:3306)/books?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(gmysql.Open(dsn), &gorm.Config{})
	if err != nil {
		fmt.Printf("数据库连接失败:", err)
	}
	DB = db
    // <<----------追加以下3行-------------->>
	if err:= DB.AutoMigrate(model.User{},model.Book{});err!=nil {
		fmt.Printf("数据库创建失败:",err)
	}
}

此时重新运行main.go,书库库和表都将被创建

mysql> use books
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-----------------+
| Tables_in_books |
+-----------------+
| user            |
| book            |
| book_users      |
+-----------------+
3 rows in set (0.00 sec)

mysql> desc user;
+----------+------------+------+-----+---------+----------------+
| Field    | Type       | Null | Key | Default | Extra          |
+----------+------------+------+-----+---------+----------------+
| id       | bigint(20) | NO   | PRI | NULL    | auto_increment |
| username | longtext   | YES  |     | NULL    |                |
| password | longtext   | YES  |     | NULL    |                |
| token    | longtext   | YES  |     | NULL    |                |
+----------+------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

mysql> desc book;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | varchar(191) | NO   | PRI | NULL    |       |
| name  | longtext     | YES  |     | NULL    |       |
| desc  | longtext     | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)

mysql> desc book_users;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| book_id | varchar(191) | NO   | PRI | NULL    |       |
| user_id | bigint(20)   | NO   | PRI | NULL    |       |
+---------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

10. 创建用户

router/api_router.go

package router

import (
	"bookmanage/dao/mysql"
	"bookmanage/model"

	"github.com/gin-gonic/gin"
)

func LoadApiRouter(r *gin.Engine) {
	r.POST("/register", RegisterHandler)
}

func RegisterHandler(c *gin.Context) {
	p := new(model.User)
	// 参数校验和绑定
	if err := c.BindJSON(p); err != nil {
		c.JSON(400, gin.H{"msg": err.Error()})
		return
	}
	// 入库
	mysql.DB.Create(p)
	c.JSON(200, gin.H{"msg": "success"})
}

router/init_router.go

func InitRouter() *gin.Engine {
	r := gin.Default()
	LoadTestRouter(r)
	LoadApiRouter(r)		// <<------追加
	return r
}

如果此时什么都不带请求就会有报错

请添加图片描述

加上username和password参数后提交成功

请添加图片描述

数据库下对应记录也被正常创建

mysql> select * from user;
+----+----------+----------+-------+
| id | username | password | token |
+----+----------+----------+-------+
|  1 | qiuqin   | root123  |       |
+----+----------+----------+-------+
1 row in set (0.00 sec)

11. 用户注册和登录

11.1 创建逻辑分层目录

mkdir controller

11.2 逻辑分层整理

controller/user.go

将原来api下面的RegisterHandler移动到这个文件下

package controller

import (
	"bookmanage/dao/mysql"
	"bookmanage/model"
	"github.com/gin-gonic/gin"
)
// 注册功能
func RegisterHandler(c *gin.Context) {
	p := new(model.User)
	// 参数校验和绑定
	if err := c.BindJSON(p); err != nil {
		c.JSON(400, gin.H{"msg": err.Error()})
		return
	}
	// 入库
	mysql.DB.Create(p)
	c.JSON(200, gin.H{"msg": "success"})
}

11.3 逻辑分层整理

router/api_router.go

package router

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

func LoadApiRouter(r *gin.Engine) {
	r.POST("/register", controller.RegisterHandler) //<<---这里将func指向controller下
}

11.4 安装uuid包

PS D:\bookmanage> go get github.com/google/uuid
go: downloading github.com/google/uuid v1.3.0
go: added github.com/google/uuid v1.3.0

11.5 追加登录功能

controller/user.go

// 登录功能
func LoginHandler(c *gin.Context) {
	p := new(model.User)
	// 参数校验和绑定
	if err := c.BindJSON(p); err != nil {
		c.JSON(400, gin.H{"msg": err.Error()})
		return
	}
	// 判断用户名密码是否正确
	u := model.User{Username: p.Username, Password: p.Password}
    // 判断rows是否有数据
	if rows := mysql.DB.Where(&u).First(&u).Row(); rows == nil {
		c.JSON(403, gin.H{"msg": "用户名密码错误"})
		return
	}
	//随机生成一个字符串作为Token
	token := uuid.New().String()
	mysql.DB.Model(&u).Update("token", token)
	c.JSON(200, gin.H{"msg": "success"})
}

11.6 Login路由追加

router/init_router.go

追加login路由

func LoadApiRouter(r *gin.Engine) {
	r.POST("/register", controller.RegisterHandler)
	r.POST("/login", controller.LoginHandler)
}

重启服务后访问测试.

没有输入用户名密码时无法登陆成功

请添加图片描述

如果密码错误

请添加图片描述

输入正确的值时,会返回token

请添加图片描述

同时Token被写入数据库对应用户记录下

ysql> select * from user;
+----+----------+----------+--------------------------------------+
| id | username | password | token                                |
+----+----------+----------+--------------------------------------+
|  1 | qiuqin   | root123  | f1bbf482-37e6-479d-9e15-a60dd2907f37 |
+----+----------+----------+--------------------------------------+
1 row in set (0.00 sec)

12. 书籍

12.1 创建书籍

12.1.1 创建书籍func

controller/book.go

// Create book ifno
func CreateBookHandler(c *gin.Context) {
	p := new(model.Book)
	if err := c.ShouldBind(p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	mysql.DB.Create(p)
	c.JSON(200, gin.H{"msg": "书籍记录新增成功!"})
}

12.1.2 书籍的路由

router/api_router.go

func LoadApiRouter(r *gin.Engine) {
	r.POST("/register", controller.RegisterHandler)
	r.POST("/login", controller.LoginHandler)

	v1 := r.Group("/api/v1")
	v1.POST("/book", controller.CreateBookHandler)
}

重启服务后

如果没有提交参数,则会报错,因为这里的name是必须的

请添加图片描述

正确填入书名后,返回:书籍记录新增成功!

请添加图片描述

数据库该记录也被追加成功

请添加图片描述

12.2 查看书籍清单

12.2.1 查看书籍清单func

controller/book.go

// Get Book list
func GetBookListHandler(c *gin.Context) {
	books := []model.Book{}
	mysql.DB.Find(&books)
	c.JSON(200, gin.H{"books": books})
}

12.2.2 路由配置

router/api_router.go

func LoadApiRouter(r *gin.Engine) {
	r.POST("/register", controller.RegisterHandler)
	r.POST("/login", controller.LoginHandler)

	v1 := r.Group("/api/v1")
	v1.POST("/book", controller.CreateBookHandler)
	v1.GET("/book", controller.GetBookListHandler)	//<<-----追加这条
}

这样用GET访问http://192.168.31.1:8080/api/v1/book 就可以获取到书籍信息

请添加图片描述

当有多条记录时,会通过json一起返回

请添加图片描述

12.3 单条数据查询

12.3.1 单条数据查询func

controller/book.go

// Get Book detail
func BookDetailHandler(c *gin.Context) {
	bookId, err := strconv.ParseInt(c.Param("id"), 10, 64)
	if err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	book := model.Book{Id: bookId}
	mysql.DB.Find(&book)
	c.JSON(200, gin.H{"book": book})
}

12.3.2 路由配置

router/api_router.go

func LoadApiRouter(r *gin.Engine) {
	r.POST("/register", controller.RegisterHandler)
	r.POST("/login", controller.LoginHandler)

	v1 := r.Group("/api/v1")
	v1.POST("/book", controller.CreateBookHandler)
	v1.GET("/book", controller.GetBookListHandler)
	v1.GET("/book/:id", controller.BookDetailHandler)
}

重启服务后可以访问到记录

请添加图片描述

12.4 更新记录

12.4.1 更新记录func

controller/book.go

// update Book detail
func UpdateBookHandler(c *gin.Context) {
	p := new(model.Book)
	if err := c.ShouldBind(p); err != nil {
		c.JSON(400, gin.H{"error": err.Error()})
		return
	}
	oldBookId := &model.Book{Id: p.Id}
	var newBook model.Book
	if p.Name != "" {
		newBook.Name = p.Name
	}
	if p.Desc != "" {
		newBook.Desc = p.Desc
	}
	mysql.DB.Model(&oldBookId).Updates(newBook)
	c.JSON(200, gin.H{"msg": newBook})
}

12.4.2 路由配置

router/api_router.go

func LoadApiRouter(r *gin.Engine) {
	r.POST("/register", controller.RegisterHandler)
	r.POST("/login", controller.LoginHandler)

	v1 := r.Group("/api/v1")
	v1.POST("/book", controller.CreateBookHandler)
	v1.GET("/book", controller.GetBookListHandler)
	v1.GET("/book/:id", controller.BookDetailHandler)
	v1.PUT("/book", controller.UpdateBookHandler)	// <-----添加这条
}

重启服务后

请添加图片描述

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

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

相关文章

缓存实现方式

为啥需要缓存&#xff1f; mysql关系型数据库&#xff0c;查询时需要磁盘IO&#xff0c;会消耗系统性能并且耗时&#xff0c;当数据变化量较小&#xff0c;并且响应要快的话&#xff0c;可以考虑使用缓存 服务端缓存方式有哪些&#xff1f; 服务端缓存方式&#xff1a; ①可以使…

git push/pull 超时问题解决

使用代理&#xff0c;发现git仓库浏览器可以访问到&#xff0c;但是本地 push/pull 时提示超时&#xff0c;这里提供一个方案供参考&#xff08;亲测有效&#xff09;&#xff1a; 修改系统的DNS为&#xff1a;114.114.114.114

基于java SSM框架的校园二手交易平台设计

一、项目介绍 游客&#xff1a;浏览商品&#xff0c;登录、注册 用户&#xff1a;浏览商品&#xff0c;发布&#xff0c;评论商品&#xff0c;我的订单&#xff0c;收藏&#xff0c;修改个人信息&#xff0c;搜索&#xff0c;回复评论 管理员&#xff1a;商品分类管理&#xff…

基于MATLAB开发AUTOSAR软件应用层模块-part13.AUTOSAR Dictionary-3 编辑AUTOSAR元素-SWC 和PORTS

配置SWC 此处可以配置SWC的名字和类型,类型包含: Application 应用组件 ComplexDiviceDriver 复杂驱动组件 EcuAbstraction ECU 抽象组件 SensorActuator 传感器执行器组件

学会python可以做哪些兼职?

前言 以我差不多四年的 Python 使用经验来看&#xff0c;大概可以按以下这些路子来赚到钱&#xff0c;但编程技能其实只是当中必不可少的一部分&#xff0c;搭配其它技能栈食用风味更佳。 1、爬虫 很多人入门 Python 的必修课之一一定是 Web 开发和爬虫&#xff0c;但这两项…

如何使用Github+picGo搭建图床???

最开始我使用iPic微博图床匿名上传&#xff0c;但是&#xff01;&#xff01;&#xff01;当我不小心上传了隐私文件后&#xff0c;删除特别麻烦(联系新浪客服,提供图片是自己上传的证据才给予处理)&#xff0c;因为白嫖图床&#xff0c;总担心挂掉&#xff0c;而且不利于备份。…

独立性能环境的重要性

独立性能环境的重要性 有些同学担心&#xff0c;由于性能测试环境和线上环境配置不一致&#xff0c;会导致线下环境得到的性能结果无法发现足够多的性能问题&#xff0c;无法对线上环境的容量评估/稳定性保障带来足够的参考。其中担心的重点有如下几点&#xff1a; 配置不一致&…

47 岁从华为退休,操作系统老兵转战 OpenHarmony 生态 | 近匠

【CSDN 编者按】开源生态建设作为开源发展的关键&#xff0c;在国内仍缺乏足够的多的成功先例&#xff0c;开源操作系统生态到底该如何建设&#xff1f;本期《近匠》邀请到专注于 OpenHarmony 操作系统生态共建的杨启彬与我们分享他的实践经验。 本期受访嘉宾&#xff1a; 杨启…

软件测试工程师面试如何描述自动化测试是怎么实现的?

软件测试工程师面试的时候&#xff0c;但凡简历中有透露一点点自己会自动化测试的技能点的描述&#xff0c;都会被面试官问&#xff0c;那你结合你的测试项目说说自动化测试是怎么实现的&#xff1f;一到这里&#xff0c;很多网友&#xff0c;包括我的学生&#xff0c;也都一脸…

【车载开发系列】UDS诊断---OBD基础概述

【车载开发系列】UDS诊断—OBD基础概述 UDS诊断---OBD基础概述【车载开发系列】UDS诊断---OBD基础概述一.什么是OBD二.OBD和UDS的区别三.OBD应用功能与应用领域四.OBD的特点有哪些五.OBD的服务概述六.OBD服务中的DTC七.OBD服务中0x03和0x07的区别八.Pending DTC有什么用九.OBD的…

昇腾携手OpenMMLab,支持海量算法仓库的昇腾AI推理部署

摘要&#xff1a;近日&#xff0c;昇腾AI联合浦江实验室&#xff0c;正式实现OpenMMLab算法仓库在昇腾的异构计算架构CANN上的推理部署&#xff0c;目前相关代码已推入MMDeploy 0.10.0版本&#xff0c;并在GitHub正式发布。本文分享自华为云社区《昇腾携手OpenMMLab&#xff0c…

标准库 - Object

一、Object 对象&#xff0c;同时也是一个构造函数。 Object 对象提供了很多操作对象的属性和方法&#xff0c;也是对应的原型。 1.Objcet() 作为函数使用 可以把其他数据类型转为一个包装对象 <script> var a Object("a");// 传入字符串就可以包装为字符串…

面试必备:分库分表经典15连问

前言 大家好&#xff0c;我是田螺。我们去面试的时候&#xff0c;几乎都会被问到分库分表。田螺哥整理了分库分表的15道经典分库分表面试题&#xff0c;大家看完肯定会有帮助的。 公众号&#xff1a;捡田螺的小男孩github地址&#xff0c;感谢每颗star&#xff1a;github 1. 我…

Jmeter随机参数各种搭配

目录&#xff1a;导读 前言 一、两个固定值之间随机生成一个值&#xff0c;应用场景没有限制 二、接下来使用CSV文件按提前写好的值作为参数&#xff0c;执行场景需要提前根据实际情况配置好执行次数 三、在固定的多个值中&#xff0c;随机选取一个值作为参数&#xff0c;执…

Smconf(分布式配置管理框架)概述

Smconf 专注于分布式环境下的配置的统一管理。采用 JavaZookeeperMongodbSpring Boot 开发。目前只支持 Java&#xff0c;其他的使用语言需要通过调用 REST API 来实现。 每个技术人都有一个开源的梦想&#xff0c;那就是自己也能开发出一个让很多人使用的框架。其实分享使用不…

8个巧用iOS备忘录的方法,别浪费了几千块的iPhone手机

你用过ios系统的备忘录吗&#xff1f;是不是只用来记录生活的一些事项呢&#xff1f;如果是这样那就太浪费啦&#xff01;这里分享8个小技巧&#xff0c;快学一学让你的备忘录能物尽其用吧&#xff01;1.笔记-横线与网格大家是不是觉得备忘录的背景光秃秃的&#xff1f;其实是有…

antv/g6基本使用教程

安装 & 引用 在项目中引入 G6 有以下两种方式&#xff1a;npm 引入&#xff0c;CDN 引入。 1 在项目中使用 npm 包引入 Step 1: 使用命令行在项目目录下执行以下命令&#xff1a; npm install --save antv/g6 Step 2: 在需要用的 G6 的 JS 文件中导入&#xff1a; imp…

JSP ssh免疫疫苗管理系统myeclipse开发mysql数据库MVC模式java编程计算机网页设计

一、源码特点 JSP ssh免疫疫苗管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S 模式开发。开发环境为TOMCAT7.0…

linux 终端分屏工具 tmux

引言&#xff1a;我们在用 MobaXterm 的时候会 经常遇到窗口不够用的时候&#xff0c;而且切换窗口也比较费时间&#xff0c;所以就需要一个工具&#xff0c;一个终端窗口可以打开多个终端。 sudo apt-get install tmuxtmux输入tmux进入。 上下分屏&#xff1a;ctrl b 再按 &…

【深度学习动手做】Dataset类,读取数据库中带标签的图片

用于将带标签的数据读取&#xff0c;比如我有一个分类“蚂蚁”和“蜜蜂”的任务&#xff0c;其工作目录下有以下&#xff1a; data文件夹、read_data.py ... 其中data文件夹下一级又分为“train”&#xff0c;"val”&#xff0c;然后按照标签分为两个文件夹“ants”&#…