golang -- CLI 命令行实用程序开发实战 - Agenda

一、环境配置

Cobra既是用于创建强大的现代CLI应用程序的库,也是用于生成应用程序和命令文件的程序。程序选择Cobra进行命令行的解析,以下配置基于Ubuntu16.04

Cobra安装

直接使用命令 go get -v github.com/spf13/cobra/cobra 下载会出现错误,首先需要安装golang的项目依赖test和sys

首先cd到$GOPATH/src/golang.org/x文件夹下,采用git clone命令安装两个依赖包如下

golang -- CLI 命令行实用程序开发实战 - Agenda_第1张图片

安装完成后重新执行go get命令即可完成安装 golang -- CLI 命令行实用程序开发实战 - Agenda_第2张图片

执行go install github.com/spf13/cobra/cobra命令即可在$GOBIN下生成对应的可执行文件

golang -- CLI 命令行实用程序开发实战 - Agenda_第3张图片

 

Cobra程序初始化

在新建的项目文件夹下使用cobra init --pkg-name [name]命令可以初始化一个新的项目,成功后初始化的项目结构如下

golang -- CLI 命令行实用程序开发实战 - Agenda_第4张图片

 

Cobra程序添加命令

在项目文件夹下使用Cobra add命令可以为你的程序添加新的命令,首先先添加一个login命令为例

可以看到命令执行完成后cmd文件夹下出现了一个login.go文件,只需要修改相应代码就可以执行相关命令了

golang -- CLI 命令行实用程序开发实战 - Agenda_第5张图片

 

二、代码实现

由于要求简化,此次我只实现了用户相关的命令,没有实现会议相关的命令

实现的操作包括

用户注册

  1. 注册新用户时,用户需设置一个唯一的用户名和一个密码。另外,还需登记邮箱及电话信息。
  2. 如果注册时提供的用户名已由其他用户使用,应反馈一个适当的出错信息;成功注册后,亦应反馈一个成功注册的信息。

用户注册的代码实现逻辑是首先读取命令接收到的用户信息,接下来需要查找要注册的用户名是否已经注册,如果注册需要返回出错信息,用户信息我使用了一个json文件进行存取,使用go语言的"encoding/json"包对其进行编码,这里我在entity包编写了两个辅助函数用于对用户信息的保存和读取,代码实现如下

func GetUsers() []User {
	jsondata, err := ioutil.ReadFile("./data/userinfo.json")
	if err != nil{
		fmt.Println(err)
	}
	var users []User
	json.Unmarshal(jsondata, &users)
	return users
}


func SetUsers(users []User) {
	//源文件清除
	os.Truncate("./data/userinfo.json", 0)

	//转为json串
	jsondata, err := json.Marshal(users)
	if err != nil{
		fmt.Println(err)
	}
	ioutil.WriteFile("./data/userinfo.json", jsondata, os.ModeAppend)
	os.Chmod("./data/userinfo.json", 0777)//开放文件写权限
}

其中用户信息结构为一个结构体,包含4个字段

//用户信息
type User struct {
	UserName    string
	Password    string
	Email   string
	Phone   string
}

在用户注册过程中对各项信息的格式内容有一定的限制,这里我采取了正则表达式匹配的方式,如果注册成功将其写入json文件并给用户返回一个注册成功的信息

var registerCmd = &cobra.Command{
	Use:   "register",
	Short: "user register",
	Long: `use register -u [username], -p [password], -e [email], -c [cellphone] to register a user`,
	Run: func(cmd *cobra.Command, args []string) {
		//fmt.Println("register called")
		//获取参数
		user, _ := cmd.Flags().GetString("username")
		pass, _ := cmd.Flags().GetString("password")
		email, _ := cmd.Flags().GetString("email")
		phone, _ := cmd.Flags().GetString("cellphone")
		//fmt.Println(user,pass,email,phone)
		//未输入输出帮助信息
		if user == "" || pass == "" || email == "" || phone == "" {
			fmt.Println("User message can't be empty")
			printRegisterUsage()
			return
		}
		if !isNameVaild(user) || !isPasswordVaild(pass) || !isEmailVaild(email) || !isPhoneVaild(phone){
			fmt.Println("Infomation error")
			return
		}
		users := entity.GetUsers()
		for i := 0; i < len(users); i ++{
			if user == users[i].UserName{
				fmt.Println("User name already exist")
				return
			}
		}
		newuser := entity.User{UserName: user, Password: pass, Email: email, Phone: phone}
		users = append(users,newuser)
		entity.SetUsers(users)//保存新用户
		fmt.Println("Register success")
	},
}

用户登录

  1. 用户使用用户名和密码登录 Agenda 系统。
  2. 用户名和密码同时正确则登录成功并反馈一个成功登录的信息。否则,登录失败并反馈一个失败登录的信息。

用户登陆需要用户名存在于已注册用户列表中且密码需要和设置时候匹配,由于用户查询和用户删除操作需要在登陆的状态下才能操作,所以同样需要一个json文件保存当前登陆的用户,仿照用户注册需要两个辅助函数用于对当前登陆用户的读取和写入

func SetLoginUser(user *User){
	//初始化清空
	os.Truncate("./data/userLogin.json", 0)

	if user != nil {
		jsondata, err := json.Marshal(user)
		if err != nil{
			fmt.Println(err)
		}
		ioutil.WriteFile("./data/userLogin.json", jsondata, os.ModeAppend)
		os.Chmod("./data/userLogin.json", 0777)//开放文件写权限
	}
}

func GetLoginUser() *User{
	var user User
	jsondata, err := ioutil.ReadFile("./data/userLogin.json")
	if err != nil{
		fmt.Println(err)
	}
	json.Unmarshal(jsondata, &user)
	return &user
}

用户登陆只需要判断用户名密码后将登陆用户写入文件即可

var loginCmd = &cobra.Command{
	Use:   "login",
	Short: "user login",
	Long: `use login -u [username], -p [password] to login`,
	Run: func(cmd *cobra.Command, args []string) {
		//fmt.Println("login called")
		user, _ := cmd.Flags().GetString("username")
		pass, _ := cmd.Flags().GetString("password")
		//未输入输出帮助信息
		if user == "" || pass == "" {
			fmt.Println("User message can't be empty")
			printRegisterUsage()
			return
		}
		users := entity.GetUsers()
		for i := 0; i < len(users); i ++{
			//用户存在
			if user == users[i].UserName{
				if pass == users[i].Password{
					entity.SetLoginUser(&users[i])
					fmt.Println(user + " login")
					fmt.Println("Hello " + user)
					return
				}else{
					fmt.Println("Password Error")
					return
				}
			}
		}
		fmt.Println("User not exist")
	},
}

用户登出

  1. 已登录的用户登出系统后,只能使用用户注册和用户登录功能。

用户登出即首先判断是否登陆后将当前登陆的用户json文件设为空即可

var logoutCmd = &cobra.Command{
	Use:   "logout",
	Short: "user logout",
	Long: `use logout to logout your user`,
	Run: func(cmd *cobra.Command, args []string) {
		//fmt.Println("logout called")
		//获取当前登录用户
		loginUser := entity.GetLoginUser()
		if loginUser == nil{
			fmt.Println("User not login")
		}else{
			fmt.Println(loginUser.UserName + " want to logout")
			entity.SetLoginUser(nil)//保存当前登录用户为nil
			fmt.Println(loginUser.UserName + " logout success")
			
		}
	},
}

用户查询

  1. 已登录的用户可以查看已注册的所有用户的用户名、邮箱及电话信息。

查询操作提供了一个可选参数-u,如果提供该参数则表示对单个用户查询,否则输出所有用户的信息,只需要获取所有用户后遍历输出结果即可

var queryuserCmd = &cobra.Command{
	Use:   "queryuser",
	Short: "query single user or all users",
	Long: `user queryuser to list all users
	queryuser [-u name] to query single user`,
	Run: func(cmd *cobra.Command, args []string) {
		//fmt.Println("queryuser called")
		loginUser := entity.GetLoginUser()
		users := entity.GetUsers()
		user, _ := cmd.Flags().GetString("queryname")
		if loginUser == nil || loginUser.UserName == ""{
			fmt.Println("User not login")
		}else{
			fmt.Println(loginUser.UserName + " want to query user")
			if user == ""{
				for i := 0; i < len(users); i ++{
					fmt.Print("Username: " + users[i].UserName + "\t")
					fmt.Print("Email: " + users[i].Email + "\t")
					fmt.Print("Phone: " + users[i].Phone + "\t")
					fmt.Println("")
				}
                return
			}else{
				for i := 0; i < len(users); i ++{
					if user == users[i].UserName{
						
						fmt.Print("Username: " + users[i].UserName + "\t")
						fmt.Print("Email: " + users[i].Email + "\t")
						fmt.Println("Phone: " + users[i].Phone)
						fmt.Println("")
                        return
					}
				}
                fmt.Println("User you want to query not exist")
			}
		}
	},
}

用户删除

  1. 已登录的用户可以删除本用户账户(即销号)。
  2. 操作成功,需反馈一个成功注销的信息;否则,反馈一个失败注销的信息。
  3. 删除成功则退出系统登录状态。删除后,该用户账户不再存在。

用户删除只需要判断用户是否在登录状态,是的话调用辅助函数将当前用户设置为空,即实现用户登出并在所有用户的json文件中删除当前用户即可,这里使用切片后拼接的形式进行删除

var deleteuserCmd = &cobra.Command{
	Use:   "deleteuser",
	Short: "delete user",
	Long: `user deleteuser to delete user login`,
	Run: func(cmd *cobra.Command, args []string) {
		//fmt.Println("deleteuser called")
		loginUser := entity.GetLoginUser()
		users := entity.GetUsers()
		if loginUser == nil{
			fmt.Println("User not login")
		}else{
			fmt.Println(loginUser.UserName + " want to delete user")
			for i := 0; i < len(users); i ++{
				if users[i].UserName == loginUser.UserName{
					tmpName := loginUser.UserName
					var tmpusers = users[:i]
					tmpusers = append(tmpusers,users[i+1:]...)
					entity.SetUsers(tmpusers)//重置users
					entity.SetLoginUser(nil) //登录用户删除
					fmt.Println("User " + tmpName + " is delete success")
					return
				}
			}
		}
	},
}

三、程序测试

本次程序测试在GoOnline上完成

 环境配置

由于GoOnline上的go语言包搜索路径和linux本地不一致,首先需要修改引用的entity和main.go下的cmd包路径为当前建立项目的路径,当前路径可以通过pwd命令查看

import(
    "git.go-online.org.cn/agent0/agenda/entity"
    "git.go-online.org.cn/agent0/agenda/cmd"
)

接下来输入命令su进入root用户模式,将将/home文件夹递归777权限

完成配置后就可以通过go run命令运行程序了

功能测试

agenda -help

golang -- CLI 命令行实用程序开发实战 - Agenda_第6张图片

agenda register

如果有参数没有输入会输出错误信息和正确的提示,信息格式错误也会提示

golang -- CLI 命令行实用程序开发实战 - Agenda_第7张图片

agenda login 

golang -- CLI 命令行实用程序开发实战 - Agenda_第8张图片

agenda queryuser

golang -- CLI 命令行实用程序开发实战 - Agenda_第9张图片

agenda deleteuser

删除后使用另一个用户登录并查询该用户发现查询不到

golang -- CLI 命令行实用程序开发实战 - Agenda_第10张图片

agenda logout

登出后无法执行查询和删除用户功能

golang -- CLI 命令行实用程序开发实战 - Agenda_第11张图片

-h参数输出帮助信息

golang -- CLI 命令行实用程序开发实战 - Agenda_第12张图片

 

完整代码见GoOnline

你可能感兴趣的:(golang -- CLI 命令行实用程序开发实战 - Agenda)