go语言学习-应付作业版Agenda

文章目录

    • CLI 命令行实用程序开发实战 - Agenda
      • 前言
      • 前期工作
      • 程序架构以及关键代码
        • entity
        • Service
        • cmd
      • 未来工作

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

前言

该实验为课程作业,内容就是实现一个对Agenda进行管理的CLI程序。由于该次作业没有组队,时间紧迫,要求降低为实现其中两个命令。也为了降低实验的难度,我首先实现了与用户有关的命令,如用户注册、用户登陆、用户登出、用户查询以及用户删除。而且本程序的框架我大概以及构建出来,只要在相应的package中增加需要的函数即可,为我有空时候进行该程序的完善打下基础。

前期工作

实现该程序,需要用到提供了简单的接口来创建命令行程序的 Golang 包——cobra。

使用以下命令安装:
go get -v github.com/spf13/cobra/cobra

如果提示有些包无法下载下来,则在$GOPATH/src下的相应目录使用git clone下载Github上的项目文件。我们常见的golang.org/x/...包,一般在 GitHub 上都有官方的镜像仓库对应。比如golang.org/x/text对应 github.com/golang/text。下载了相应的包后,再次执行上述命令即可。

安装好cobra后,在自己创建的某个项目文件下,如我的项目文件在一个叫agneda的目录下,所以我在该目录下使用cobra init --pkg-name agenda初始化一个项目。初始化后目录结构如下:
在这里插入图片描述

然后需要什么命令,则用cobra add添加,如我的首个命令为register,则cobra add register添加,添加成功会在cmd目录下生成register.go文件。

至此为止,项目的前期工作已经完成。

程序架构以及关键代码

在添加一系列需要文件后,项目的目录结构如下:
go语言学习-应付作业版Agenda_第1张图片
cmd目录下是实现的命令行,entity是与数据打交道的包,service提供了一个cmd包与entity交互的接口。实际上是service控制着程序的逻辑,cmd内的相应命令行程序只需调用service提供的服务接口即可,service会做一系列逻辑判断并调用entity包进行数据操控。

在service包下,service提供给外界接口,而myError提供了给service接口返回某些错误的功能。

entity则负责与数据打交道,在此包下我使用一个struct表示User实体,如果需要实现Meeting的功能,只需添加一个Meeting实体即可。data目录下则包含以json格式保存的用户数据或者是会议数据等。

entity

根据这样的框架,我们可以开始进行编程了。首先,需要实现底层的数据操作,即entity包的工作。

首先,需要定义一个用户实体:

//User is a user entity.
type User struct {
	Name     string
	Password string
	Email    string
	Phone    string
}

再用一个Slice管理注册的用户:

var users []User
var currentUser User

上面就是entity需要使用到的数据结构,然后开始函数设计。由于每次保存上次调用程序的状态,所以我们使用json格式以txt保存数据,每次调用该程序都需要读文件:

//Init reads the infomation in the .txt file.
func Init() {
	//读取注册数据
	readUsersFile()
	//读取当前登陆用户数据
	readcurrentUserFile()
}

剩下的就是一些提供给service进行逻辑判断以及数据操作的接口,实现无非就是写文件以及遍历User数组,这里就不展示了。

Service

首先仍然是初始化工作,这是一个登陆的系统,所以需要记录登陆状态,我在这里使用一个string记录用户的名字,如果无人登陆则为空(字符串的空为"")。则每次初始化还需要调用entity读取当前登陆用户。

var currentUser string

//Init inits all the thing.
func Init() {
	//初始化
	entity.Init()
	currentUser = entity.CurrentUser()
}

接着是对应于命令的函数,可以看到每个函数都会进行一些逻辑判断,有异常则返回一个error,让cmd处理。无误则进行相应的数据操作,如读取某些数据,或更新数据库里数据等。

//Register judges the input whether is valid and persist register information.
func Register(name string, password string, email string, phone string) error {
	//需要判断输入信息是否合法
	if currentUser != "" {
		return &MyError{"Empty user name."}
	}
	if name == "" {
		return &MyError{"User name already exists."}
	}
	if entity.CheckUser(name) {
		return &MyError{"User name already exists."}
	}
	if len(password) < 6 {
		return &MyError{"Password length cannot be less than 6."}
	}
	if matchE, _ := regexp.MatchString("^[a-z0-9A-Z]+[- | a-z0-9A-Z . _]+@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-z]{2,}$", email); !matchE {
		return &MyError{"Email is invalid."}
	}
	if matchP, _ := regexp.MatchString("^1\\d{10}$", phone); !matchP {
		return &MyError{"Phone is invalid."}
	}
	//persist register information
	entity.Register(name, password, email, phone)

	return nil
}

//LogIn judges the input whether is valid and persist current user information.
func LogIn(name string, password string) error {
	//需要判断输入信息是否合法
	if currentUser == name {
		if name == "" {
			return &MyError{"Empty user name."}
		}
		return &MyError{"You have logged in."}
	}
	if currentUser != "" {
		return &MyError{"Please log out first."}
	}
	if !entity.CheckPassword(name, password) {
		return &MyError{"The user name does not exist or the password is incorrect."}
	}

	entity.LogIn(name)
	currentUser = name

	return nil
}

//LogOut deletes current user information.
func LogOut() error {
	//需要判断是否为登陆状态
	if currentUser == "" {
		return &MyError{"You haven't logged in yet."}
	}

	entity.LogOut()
	currentUser = ""

	return nil
}

//QueryUser shows all the registered user information(except password).
func QueryUser() error {
	//需要判断是否为登陆状态
	if currentUser == "" {
		return &MyError{"You haven't logged in yet."}
	}

	var users []entity.User
	users = entity.QueryUser()
	fmt.Printf("%-10s %-18s %-16s\n", "Uesrname", "Email", "Phone")
	for _, v := range users {
		fmt.Printf("%-10s %-18s %-16s\n", v.Name, v.Email, v.Phone)
	}

	return nil
}

//DeleteUser deletes current user in user file and log out
func DeleteUser() error {
	//需要判断是否为登陆状态
	if currentUser == "" {
		return &MyError{"You haven't logged in yet."}
	}

	entity.DeleteUser()
	entity.LogOut()

	return nil
}

cmd

cmd包则进行命令参数进行处理,并调用service的相应函数将参数交给service进行逻辑判断,并返回相应的错误来以反馈给用户信息。所以在cmd包内主要是处理参数,而cobra已经建立好相应的框架,我们根据命令行的特点编辑即可。

如register:

// registerCmd represents the register command
var registerCmd = &cobra.Command{
	Use:   "register -u [username] -p [password] -e [email] -t [phone]",
	Short: "Register new users",
	Long:  "Register new users, for example: agenda register -u username –p password –e [email protected] -t 12345678911",
	Run: func(cmd *cobra.Command, args []string) {
		username, _ := cmd.Flags().GetString("username")
		password, _ := cmd.Flags().GetString("password")
		email, _ := cmd.Flags().GetString("email")
		phone, _ := cmd.Flags().GetString("phone")
		err := service.Register(username, password, email, phone)

		if err != nil {
			fmt.Println(err)
		} else {
			fmt.Println("Successfully registered!")
		}
	},
}

func init() {
	rootCmd.AddCommand(registerCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// registerCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// registerCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
	registerCmd.Flags().StringP("username", "u", "", "user name")
	registerCmd.Flags().StringP("password", "p", "", "password")
	registerCmd.Flags().StringP("email", "e", "", "email")
	registerCmd.Flags().StringP("phone", "t", "", "phone")
}

为了完成程序的初始化,则需要在root.go调用service的Init()。

// initConfig reads in config file and ENV variables if set.
func initConfig() {
	...
	
	//Init, used to read data.
	service.Init()
}

需要查看完整代码可以通过Go Online查看和实际运行,暂时还未将项目放到Github上。

未来工作

剩下的工作主要是该项目的主体部分,即管理Meeting,实际上大概流程与User的管理类似,将逻辑操作都放在service包上,在entity提供一些底层数据操作的接口,不过逻辑上可能更复杂,而且还需要选择适合的数据结构。

除此之外,还需要添加 log 服务,记录用户的操作过程,以及关键的输出,并记录在log文件上。

你可能感兴趣的:(golang)