程序完整代码请看github,该程序为个人完成。
Go Online平台的分享链接
sys
和text
项目git clone https://github.com/golang/text
git clone https://github.com/golang/sys
go install github.com/spf13/cobra/cobra
安装cobra
包,在此之前请确认已经安装spf13
包。此时,若安装成功,在bin
文件夹下有conbra.exe
可执行文件。$GOPATH/src
路径下输入..\bin\cobra.exe init agenda
指令会调用cobra
包生成子样例文件夹agenda
,在该目录下执行go run main.go
指令,输出以下内容。..\..\bin cobra.exe add test
指令,创建main.go
的子程序test.go
,更改test.go
中init
添加新参数user
即输入代码testCmd.Flags().StringP("user", "u", "", "test")
后改动变量var testCmd
中的匿名函数Run
,添加代码parameter, _ := cmd.Flags().GetString("user")
获取标签信息并输出,输出结果如下,其中可以通过-u Simon
指定参数user
值。Agenda
的目录路径如下agenda
│ main.go
│ agenda.log
| LICENSE
│
└───service
│ │ Data_process.go
│ │ Date.go
│ │ Log.go
│ │ Meeting.go
│ │ Service.go
│ │ User.go
│ │
│ └───data
│ │ User.txt
│ │ Metting.txt
│
└───cmd
│ agenda.go
│ root.go
Agenda
指令,需要记录用户User
和会议Meeting
信息,其中分别保存在data/User.txt
和/data/Meeting.txt
中Meeting
信息为例。json
格式操作,因此需要对Meeting
成员进行encode、decode
处理。其中分别调用json.Marshal
和json.Unmarshal
函数func meeting_decode(json_info []byte) Meeting{
var res Meeting
err := json.Unmarshal(json_info, &res)
if err != nil {
defer log.Println("[Error] Get meeting error!")
defer fmt.Println("[Error] Get meeting error!")
}
return res
}
func meeting_encode(meeting Meeting)[]byte{
json_info, err := json.Marshal(meeting)
if err != nil{
defer log.Println("[Error] Write meeting error!")
defer fmt.Println("[Error] Write meeting error!")
}
return json_info
}
bufio.NewReader
函数打开相应文件并调用ReadString('\n')
函数每次读取一行数据或调用WriteString
函数每次写入一行数据并写入'\n'
数据,并进行相应的编码、解码操作。以read_meetings
为例,返回读取的所有Meeting
信息。func read_meetings() []Meeting{
file, err := os.Open("./service/data/Meeting.txt")
if err != nil {
panic(err)
}
defer file.Close()
var res []Meeting
meetings := bufio.NewReader(file)
for {
meeting_json, err := meetings.ReadString('\n')
if err != nil || io.EOF == err {
break
}
res = append(res, meeting_decode([]byte(meeting_json)))
}
return res
}
type User struct{
User_name string
User_password string
User_email string
User_phone int
User_meeting []Meeting
}
create_user
函数创建用户,其中需要确认传入的参数是否符合需求,包括user_phone
是否满足11位要求,user_email
是否满足对应的规则。调用regexp
库使用正则表达式处理func isPhone(phone int) bool{
res, _ := regexp.MatchString("^1[0-9]{10}$", strconv.Itoa(phone))
if !res{
defer log.Println("[Error] Phone's format is not correct!")
defer fmt.Println("[Error] Phone's format is not correct!")
}
return res
}
func isEmail(email string) bool{
res, _ := regexp.MatchString("^([a-z0-9_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$", email)
if !res{
defer log.Println("[Error] Email's format is not correct!")
defer fmt.Println("[Error] Email's format is not correct!")
}
return res
}
User.txt
中读取的users
信息查重即可。for _,user := range users{
if user.User_name == name {
defer log.Println("[Error] User's name has exist!")
defer fmt.Println("[Error] User's name has exist!")
return false
}
}
user
对象并添加到users
中user := User{name, password, email, phone, empty}
users = append(users, user)
func isValid(date Date) bool{
if date.Year < 1000 || date.Year > 9999 || date.Month > 12 || date.Month < 1 || date.Hour > 24 || date.Hour < 0 || date.Minute > 60 || date.Minute < 0 || date.Day < 1{
return false
}
switch date.Day {
case 1, 3, 5, 7, 8, 10, 12:
if date.Day > 31 {
return false
}
case 2:
if date.Year % 4 == 0{
if date.Day > 29{
return false
}
}else{
if date.Day > 28{
return false
}
}
default:
if date.Day > 30{
return false
}
}
return true
}
compare_date
函数判断日期大小,具体代码此处不贴出。string
需要进一步转化为data
对象,其中包含DateToString
和StringToDate
,此处设定的规则为yyyy-mm-dd-hh:mm
对应date
对象中的year month day hour minute
func DateToString(date Date) string{
res := strconv.Itoa(date.Year) + "-" + strconv.Itoa(date.Month) + "-" + strconv.Itoa(date.Day) + "-" + strconv.Itoa(date.Hour) + "-" + strconv.Itoa(date.Minute)
return res
}
Meeting
结构体如下type Meeting struct{
Participators []string //just record user's name
Sponsor,Title string
Start_time, End_time Date
}
crete_meeting
函数创建会议时,需要调用check_meeting
判断对应参数是否出错,其中包含调用isValid、compare_date
确认日期是否满足需求、判断Title
是否唯一、判断用户名是否有重复以及用户参加的会议是否有冲突或用户是否存在或重复,其中部分条件检测代码如下:func check_meeting(res Meeting, participators []string, sponsor, title string, start_time, end_time Date) bool{
if !isValid(start_time) {
defer log.Println("[Error] Error start time!")
defer fmt.Println("[Error] Error start time!")
return false
}
if !isValid(end_time) {
defer log.Println("[Error] Error end time!")
defer fmt.Println("[Error] Error end time!")
return false
}
if compare_date(start_time, end_time) { // check date
defer log.Println("[Error] start time should earily than end time!")
defer fmt.Println("[Error] start time should earily than end time!")
return false
}
for _, meeting := range meetings{ // check title
if meeting.Title == title {
defer log.Println("[Error] Meeting's title has exist!")
defer fmt.Println("[Error] Meeting's title has exist!")
return false
}
}
for _, par := range participators{
user_exist := false
for _, user := range users{
if user.User_name == par{ //check user existence
user_exist = true
for _, user_meeting := range user.User_meeting{ // check whether user meetings overlap
if isOverlap(res, user_meeting){
defer log.Println("[Error] Some users'meetings may overlap!")
defer fmt.Println("[Error] Some users'meetings may overlap!")
return false
}
}
}
}
if !user_exist{
defer log.Println("[Error] Some users may not exist")
defer fmt.Println("[Error] Some users may not exist")
return false
}
}
for i := 0; i < len(participators); i++{ // check whether users duplicate
for j := i + 1; j < len(participators); j++{
if participators[i] == participators[j]{
defer log.Println("[Error] Some users may not duplicate")
defer fmt.Println("[Error] Some users may not duplicate")
return false
}
}
}
return true
}
func isOverlap(meeting1, meeting2 Meeting) bool{ // false mean not overlap, input is one person's two meetings
if compare_date(meeting1.Start_time, meeting2.End_time) || compare_date(meeting2.Start_time, meeting1.End_time){
return false
}else{
return true
}
}
delete_user_meeting
删除掉该成员的该条会议信息。users
下标并调用append
函数删除用户名参加的会议Title
对应的会议。func delete_user_meeting(meeting Meeting, user_name string) bool{//delete meeting from users
user_idx := -1
for index, user_tmp := range users{
if user_tmp.User_name == user_name{
user_idx = index
break
}
}
if user_idx == -1{
defer log.Println("[Error] User does not partipant current meeting!")
defer fmt.Println("[Error] User does not partipant current meeting!")
return false
}
for index, meeting_tmp := range users[user_idx].User_meeting{
if meeting_tmp.Title == meeting.Title {
users[user_idx].User_meeting = append(users[user_idx].User_meeting[:index], users[user_idx].User_meeting[index + 1 :]...)
break
}
}
return true
}
delete_user_meeting
函数后再调用append
函数从meetings
中删除对应会议对象。而删除会议特定成员只需要找到对应的成员是否存在会议中并调用相关函数即可func delete_meeting(meeting_idx int) bool{
meetings[meeting_idx].Participators = append(meetings[meeting_idx].Participators, meetings[meeting_idx].Sponsor)
for _, user := range meetings[meeting_idx].Participators { //update all influenced user info
delete_user_meeting(meetings[meeting_idx], user)
}
meetings = append(meetings[:meeting_idx], meetings[meeting_idx + 1 : ]...)
defer log.Println("[Success] Delete meeting successful!")
defer fmt.Println("[Success] Delete meeting successful!")
return true
}
type LogFile struct{
file string
}
func (Log *LogFile) Write(p []byte) (int, error) {
f, err := os.OpenFile(Log.file, os.O_CREATE|os.O_APPEND, 0666)
defer f.Close()
if err != nil {
return -1, err
}
return f.Write(p)
}
fmt
和log
同时执行,如log.Println("[Error] Some info do not fit the standard!")
fmt.Println("[Error] Some info do not fit the standard!")
UI
代码Service.go
,此处包含对Agenda
的初始化以及提供agenda.go
使用的交互函数如Login_in()、Create_user()
,以大写字母开头表示public
允许函数在包外调用,此处需要注意的是,由于agenda.go
与Data_process.go
不在同级目录下,因此如果要在包外执行程序,对应的txt
文件路径要有相应的变化。Create_user
函数,其中直接调用create_user
即可,而当注册成功后会默认进入Login_in()
函数,需要验证用户名与密码信息是否存在与匹配。for index, user := range users{
if user.User_name == name && user.User_password == password{
user_index = index
break
}
}
process
函数即用户交互界面,其中提供了5个功能,分别为退出、创建会议、删除会议、删除会议成员、查询当前用户会议记录
。每个功能对应不同函数,并分别调用上述所讲的对应函数进行操作。当运行结束后,则写入信息。func process(){
var choice string
for {
fmt.Println("--q input q to quit agenda")
fmt.Println("--cm create a new meeting")
fmt.Println("--mr remove a meeting")
fmt.Println("--pr remove a particopator from a meeting")
fmt.Println("--qm query current user's meeting")
fmt.Scanln(&choice)
if(choice == "q"){
break
}else if(choice == "cm"){
cm_cmd()
}else if(choice == "mr"){
mr_cmd()
}else if(choice == "pr"){
pr_cmd()
}else if(choice == "qm"){
qm_cmd()
}
}
write_users()
write_meetings()
}
cm_cmd
函数为例,其中需要创建会议,因此需要用户输入会议信息后调用create_meeting
函数即可。func cm_cmd(){
var participators []string
//sponsor is default to be current user
var par, par_number, title, start_time, end_time string
fmt.Println("Input your meeting's participators number: ")
fmt.Scanln(&par_number)
par_num, _ := strconv.Atoi(par_number)
for i := 0; i < par_num ; i++{
fmt.Println("Input number ", i, "participator's name")
fmt.Scanln(&par)
participators = append(participators, par)
}
fmt.Println("Input your meeting's title: ")
fmt.Scanln(&title)
fmt.Println("Input your meeting's start time: ")
fmt.Scanln(&start_time)
fmt.Println("Input your meeting's end time: ")
fmt.Scanln(&end_time)
create_meeting(participators, users[user_index].User_name, title, StringToDate(start_time), StringToDate(end_time))
}
Cobra
包生成相应的文件便于执行程序,按照上述做法创建Agenda
后修改相应的匿名函数
和Init
,具体为通过命令行获取相应的初始化信息如用户登陆/注册前需要用到的信息而非直接进入process
函数进行用户交互。Init
函数如下:agendaCmd.Flags().StringP("user_name", "u", "", "user's name")
agendaCmd.Flags().StringP("user_password", "p", "", "user's password")
agendaCmd.Flags().StringP("user_email", "e", "", "user's email")
agendaCmd.Flags().IntP("user_telephone", "t", 0, "user's telephone number")
agendaCmd.Flags().BoolP("register", "r", false, "register a new user and login in")
agendaCmd.Flags().BoolP("login_in", "l", false, "login in with current user")
Run
对应添加的代码如下:Run: func(cmd *cobra.Command, args []string) {
fmt.Println("agenda called")
user_name, _ := cmd.Flags().GetString("user_name")
user_password, _ := cmd.Flags().GetString("user_password")
user_email, _ := cmd.Flags().GetString("user_email")
user_phone, _ := cmd.Flags().GetInt("user_telephone")
register, _ := cmd.Flags().GetBool("register")
login, _ := cmd.Flags().GetBool("login_in")
if(register && login){
log.Println("[Error] Do not set -l and -r true together!")
fmt.Println("[Error] Do not set -l and -r true together!")
return
}
if(register){
service.Init()
if service.Create_user(user_name, user_password, user_email, user_phone){
service.Login_in(user_name, user_password)
}else{
log.Println("[Error] Some info do not fit the standard!")
fmt.Println("[Error] Some info do not fit the standard!")
}
}
if(login){
service.Init()
service.Login_in(user_name, user_password)
}
},
agenda
目录下运行go run main.go agenda -h
指令获取对应命令行提示信息,如下:-r true -u d -p 123456 -t 11111111111 -e 111111@qq,com
输入必要信息以此注册用户,其中需要注意-l -r
指令默认为false
需要指定为true
且不能两个同时为true
否则会报错。若用户名不存在则创建成功,输出以下内容-l true -u a -p 123456
进入相应账号。cm
可以创建会议,根据相应指示输入参数,如下qm
可以查询当前用户的会议信息,如下pr
移除特定Title
的会议成员,如下:mr
则移除整个对应Title
会议,此时再次输入qm
指令输出为空,操作如下:User.txt
文件,发现其中users
信息以json
格式保存:Meeting.txt
如下agenda.log
,其中记录了操作过程中的相关信息,如下go online
上运行,而相关配置与visual studio code
不同,相关包的安装使用有问题,因此只能将文件压缩到main.go
中并修改了相应的UI界面,直接输入指令go run main.go
进入程序并按照相似指令操作即可,即txt
文件正常运行无误,Agenda
执行完毕。