【Go】学生教务管理系统【无界面 / 单机版】(一)

Ⅰ. 程序说明

一、博客日期

本文第一次发布
2022年12月29日17点27分
Authors
THDMI

二、引言

本作品旨在提供一个学生教务管理系统,主要包括学科管理班级管理考试管理成绩管理邮件箱 等功能。

作品使用 Golang 语言进行开发,结构上不依赖第三方库,但使用了少量第三方库作为美化(可以取消使用)。一般而言,是前后端分开但在本工程仅仅作为一个共体,后续确实有计划分开设计并使用网络传输,但在那之前先实现一个简单的管理系统。

本文计划分为两个部分

第一部分是界面逻辑设计,学过数据结构的同学可以简单理解为就是创建了一个树形结构,每一个功能即一个结点,这是第一部分要实现的内容,这一部分在2022年2月开始写,但中途因为复习缘故,到了2022年9月才完成。

第二部分是逻辑功能实现,利用一定的数据结构和算法实现一个基本的文件型数据库作为存储,逐步实现和整合各个逻辑功能
,这一部分自2022年10月开始断续写,在2022年11月完成了数据存储的验证,后续功能实现从2022年12月28日开始。


2023年03月20日补充: 截至目前,基本数据存储已经弄完,但数据之间的关联有点难弄,所以预计会晚一些,目前的框架其实很好用(就是已经上传的代码),如果有需要,只需要自己实现对应结点的功能即可,后续如果我再推送代码,会把目前的代码给覆盖,到时候有可能显得凌乱。

2023年03月28日补充: 截至目前,Github 仓库代码已更新,但功能未就绪,请勿直接覆盖替换:Github 仓库地址
Gitee 仓库代码保留原先的树形模板,便于二次开发:Gitee 仓库地址


下面开始第一部分内容。


Ⅱ. 版权声明

本工程作品均为本文作者撰写,作品遵循MPLv2.0开源协议。转载本文请标注出处。


Ⅲ. 开发环境

一、开发配置

操作系统
Microsoft Windows10 家庭版 20H2
安装内存 / 远端内存
16GB 2400MHz / 无

二、开发工具

软件依赖版本类型版本号
GoLand2022.2.3
Golang1.19

Ⅳ. 效果演示

一、工程结构

(一)目录结构

工程结构图

(二)目录说明

1. data目录
该目录下主要存放使用过程中的各种存储数据,包括classexamsubjectuser,可以根据目录名称推测出大概存放的内容,具体存放格式在每个目录下会有一个独特的说明文件(也可能会放在README.md,以后期为准)。
2. log目录
该目录下主要存放的是程序运行中出现的各种操作或异常的日志记录,该日志系统尚且不够完善的地方在于它未能解决跨日问题,这个在目前不是重点,因此保留下来,如果有同学补充了,可以issue一下。
3. main目录
该目录存放的是程序运行中最关键最核心的代码,目录下主要有三个部分,一个是日志系统,一个是主程序,一个是菜单骨架。其中,日志系统采用协程独立运行。主函数主要只包含了一个操作,读取菜单骨架并提供进一步操作的能力菜单骨架包括了菜单各结点的构成,每个结点包含的子结点,抑或是每个结点执行功能的路由,基本囊括在这一部分中。
4. public目录
该目录下主要存放着功能性的代码,包括各种基础操作和数据存储处理等,以及公有变量、结构等共享和菜单显示内容的定义,屏幕的输入输出函数即在此。


二、登陆效果

登陆界面效果

三、菜单效果

菜单效果演示

四、日志记录

日志记录效果


Ⅴ. 设计流程

一、总方案

(一)架构设计

1. 总体架构
总体架构

2. 日志处理架构
日志处理架构

3. 菜单显示架构
菜单显示架构

(二)设计思路

1. 总体思路
以主函数为例,开局先检测各种配置,如果正常则继续。绑定端口使用UDP开启日志处理功能,接着进入一个无限循环∞。在该循环中,主要就是不断读取某个结点并通过提示用户输入,根据用户输入进入下一步,即子结点或者是执行对应的功能,类似一棵树,其中日志的传递通过发送UDP消息进行,最初打算使用协程,使用通道传输消息,但是考虑到了其他因素的影响,使用UDP通信更为合适。

2. 日志处理思路
日志处理主要是遵循两个原则,一个是独立,一个是靠谱。独立日志处理指的是能够在处理其他事物的同时完成日志的记录,这一通信过程依赖UDP,关于日志靠谱实际上就是应该确保每一个日志都能准确记录下来,不能因为有错误即丢弃。

3. 菜单架构思路
菜单架构主要依赖于两个部分,一个是结点的预定义,包括这个结点:谁能访问、显示什么内容、下一个能访问哪个、执行什么,这同时也包含了结点的上一级的返回处理,学过数据结构的同学应该都知道怎么做。

不过最初的时候没处理好,最开始没打算使用双向链表,导致后来莫名其妙实现了单链表往上一级走的功能,没错,后来就是突然加上了双向功能,但却不是彻底的双向链表,只能说是用了辅助双向链表来保存结点。

总之,在结点预定义完成后,无限循环只需要一步访问一个结点就能执行对应的操作,并保存上一结点指针,如果操作函数为空则检查有没有下一级结点,当然,需要一定权限才能访问,这些都需要考虑到。


二、逻辑实现

这里指的是能够呈现出菜单样式的逻辑实现,而不是菜单具体功能的逻辑实现。

(一)主要函数

1. 预定义

	// 包含个人信息相关的结构
	profile := public.StructProfile{IsLogin: false, Permit: public.PermitGuest}
	// 传递全局的共享变量,通过参数传递
	share := public.StructShareBase{Profile: &profile}
	// 构造一棵树 - 菜单,本质是根节点
	menu := InitTreeMenu(&share)
	// 链式列表,保存上一级、下一级和当前结点
	nodeList := &public.StructMenuLink{Node: menu}
	// 初始化打印菜单
	FuncMenuPrint(menu, &share)

2. 引导用户输入【无限循环内】

		option := public.SelectOption(len(menu.MenuNode))
		if option < 0 {
			continue
		}

3. 返回上一级操作【无限循环内】

		if option == 0 {
			// 若上一级不为空则将上一级的链点赋给当前链点
			// 并传递菜单属性,否则退出并结束程序
			if nodeList.NodeLast != nil {
				nodeList = nodeList.NodeLast
				menu = nodeList.Node
			} else {
				public.FuncPrintLog(public.LogInfo, "程序正在退出...")
				public.Clear()
				public.TipWait("正在退出")
				public.Clear()
				return
			}

4. 进入下一级/执行操作【无限循环内】

		} else {
			// 判断下一链点是否为空,如果为空则创建链点
			// 并将下一链点的`NodeLast`指向为当前链点
			// 同时`nodeList`指针指向下一链点
			if nodeList.NodeNext == nil {
				node := &public.StructMenuLink{NodeLast: nodeList}
				nodeList = node
			}
			// 对当前菜单指向用户所选菜单选项
			// 并将指向后的菜单属性赋予`nodeList`链点的菜单属性`Node`
			menu = menu.MenuNode[option-1]
			nodeList.Node = menu
			// 执行当前菜单附带的操作函数,若需要验证则传入验证操作量
			// 通过后允许进入菜单,否则返回上一级菜单,或者保持当前菜单
			if menu.Func != nil {
				if menu.HasVerifier {
					vfResult := false
					menu.Func(&share, &vfResult)
					if !vfResult {
						nodeList = nodeList.NodeLast
						menu = nodeList.Node
					}
				} else if menu.IsKeepMenu {
					menu.Func(&share)
					nodeList = nodeList.NodeLast
					menu = nodeList.Node
				} else {
					menu.Func(&share)
				}
			}
		}

(二)日志处理

1. 端口监听

func ServerRun() {
	log = getLogFile("./log")
	server, err := net.ResolveUDPAddr("udp4", "127.0.0.1:32111")
	if err != nil {
		public.TipWait("开启日志系统时出现错误,即将关闭系统")
		os.Exit(0)
	}
	conn, err := net.ListenUDP("udp", server)
	for {
		disposeSocket(conn)
	}
}

2. 消息处理

func disposeSocket(conn *net.UDPConn) {
	var dataByte [1024]byte
	count, _, err := conn.ReadFromUDP(dataByte[:])
	if err != nil {
		public.TipWait("接收日志操作请求时出现错误")
		return
	}
	go logWrite(dataByte[:count])
}

3. 日志写入

func logWrite(data []byte) {
	// TODO 尚未解决跨日问题,可以通过在此调用查询文件名是否匹配当天日期而解决
	if log == nil {
		return
	}
	mutex.Lock()
	timeStr := time.Now().Format("[2006-01-02 15:04:05] ")
	_, err := log.WriteString(timeStr)
	_, err = log.Write(data)
	_, err = log.Write([]byte{'\r', '\n'})
	err = log.Sync()
	if err != nil {
		mutex.Unlock()
		return
	}
	mutex.Unlock()
}

4. 日志文件打开方式

func getLogFile(path string) *os.File {
	var logFile *os.File
	fileName := time.Now().Format("2006-01-02") + ".log"
	filePath := path + "/" + fileName
	flag := isFileExist(filePath)
	if flag {
		// 当天日志已存在就追加
		logFileTemp, err := os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			return nil
		}
		logFile = logFileTemp
	} else {
		// 当天日志不存在就创建
		logFileTemp, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
		if err != nil {
			return nil
		}
		logFile = logFileTemp
	}

	return logFile
}

(三)结构定义

1. 用户信息

type StructProfile struct {
	IsLogin   bool
	Name      string
	UserId    string
	ClassId   string
	SubjectId string
	Permit    int
}

2. 菜单结构

type StructMenu struct {
	Title       string
	Profile     *StructProfile
	PermitMode  int
	PermitList  []int
	Text        string
	HasVerifier bool
	IsKeepMenu  bool
	MenuNode    []*StructMenu
	Func        func(*StructShareBase, ...any)
}

3. 结点相互结构(双向链表)

type StructMenuLink struct {
	NodeLast *StructMenuLink
	NodeNext *StructMenuLink
	Node     *StructMenu
}

4. 共享变量

type StructShareBase struct {
	Profile *StructProfile
}

(四)菜单构造

1. 根节点

func InitTreeMenu(share *public.StructShareBase) *public.StructMenu {
	return &public.StructMenu{
		Profile:    share.Profile,
		PermitList: []int{public.PermitGuest},
		Text:       public.Menu00Login,
		MenuNode: []*public.StructMenu{
			&nodeLogin,
			&nodeRegister,
			&nodeChangeAccount,
		},
	}
}

2. 一级菜单

var nodeLogin = public.StructMenu{
	PermitMode:  public.PermitModeEqualGreater,
	PermitList:  []int{public.PermitGuest},
	Text:        public.Menu00Login,
	HasVerifier: true,
	MenuNode: []*public.StructMenu{
		&nodeProfile,
		&nodeSubject,
		&nodeClass,
		&nodeExam,
		&nodeReport,
		&nodeMailbox,
		&nodeToolbox,
	},
	Func: funcLogin,
}

var nodeRegister = public.StructMenu{
	PermitMode: public.PermitModeEqualGreater,
	PermitList: []int{public.PermitGuest},
	Text:       public.Menu00Register,
	Func:       funcRegister,
}

var nodeChangeAccount = public.StructMenu{
	PermitMode:  public.PermitModeEqualGreater,
	PermitList:  []int{public.PermitGuest},
	HasVerifier: true,
	IsKeepMenu:  true,
	Text:        public.Menu00ChangeAccount,
	Func:        funcChangeAccount,
}

3. 二级菜单

var nodeProfile = public.StructMenu{
	PermitMode: public.PermitModeGreater,
	PermitList: []int{public.PermitGuest},
	Text:       public.Menu01Profile,
	MenuNode: []*public.StructMenu{
		&nodeProfileSelfCheck,
		&nodeProfileSelfModify,
		&nodeProfileOtherCheck,
		&nodeProfileOtherEdit,
	},
}

var nodeSubject = public.StructMenu{
	PermitMode: public.PermitModeGreater,
	PermitList: []int{public.PermitUser},
	Text:       public.Menu01Subject,
	MenuNode: []*public.StructMenu{
		&nodeSubjectAdd,
		&nodeSubjectDel,
		&nodeSubjectCheck,
		&nodeSubjectEdit,
	},
}

var nodeClass = public.StructMenu{
	PermitMode: public.PermitModeGreater,
	PermitList: []int{public.PermitGuest},
	Text:       public.Menu01Class,
	MenuNode: []*public.StructMenu{
		&nodeClassAdd,
		&nodeClassDel,
		&nodeClassCheck,
		&nodeClassEdit,
		&nodeClassMemberAdd,
		&nodeClassMemberDel,
	},
}

var nodeExam = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager},
	Text:       public.Menu01Exam,
	MenuNode: []*public.StructMenu{
		&nodeExamAdd,
		&nodeExamDel,
		&nodeExamCheck,
		&nodeExamEdit,
	},
}

var nodeReport = public.StructMenu{
	PermitMode: public.PermitModeGreater,
	PermitList: []int{public.PermitGuest},
	Text:       public.Menu01Report,
	MenuNode: []*public.StructMenu{
		&nodeReportAdd,
		&nodeReportEdit,
		&nodeReportCheckDate,
		&nodeReportCheckId,
	},
}

var nodeMailbox = public.StructMenu{
	PermitMode: public.PermitModeGreater,
	PermitList: []int{public.PermitGuest},
	Text:       public.Menu01Mailbox,
	MenuNode: []*public.StructMenu{
		&nodeMailboxSend,
		&nodeMailboxReceive,
	},
}

var nodeToolbox = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu01Toolbox,
	MenuNode: []*public.StructMenu{
		&nodeToolboxAddActiveCode,
		&nodeToolboxDelAccount,
	},
}

4. 三级菜单

var nodeProfileSelfCheck = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager, public.PermitUser},
	Text:       public.Menu02ProfileSelfCheck,
	Func:       funcProfileSelfCheck,
}

var nodeProfileSelfModify = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager, public.PermitUser},
	Text:       public.Menu02ProfileSelfModify,
	Func:       funcProfileSelfModify,
}

var nodeProfileOtherCheck = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ProfileOtherCheck,
	Func:       funcProfileOtherCheck,
}

var nodeProfileOtherEdit = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ProfileOtherEdit,
	Func:       funcProfileOtherEdit,
}

var nodeSubjectAdd = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02SubjectAdd,
	Func:       funcSubjectAdd,
}

var nodeSubjectDel = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02SubjectDel,
	Func:       funcSubjectDel,
}

var nodeSubjectCheck = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager, public.PermitUser},
	Text:       public.Menu02SubjectCheck,
	Func:       funcSubjectCheck,
}

var nodeSubjectEdit = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02SubjectEdit,
	Func:       funcSubjectEdit,
}

var nodeClassAdd = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ClassAdd,
	Func:       funcClassAdd,
}

var nodeClassDel = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ClassDel,
	Func:       funcClassDel,
}

var nodeClassCheck = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager, public.PermitUser},
	Text:       public.Menu02ClassCheck,
	Func:       funcClassCheck,
}

var nodeClassEdit = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ClassEdit,
	Func:       funcClassEdit,
}

var nodeClassMemberAdd = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ClassMemberAdd,
	Func:       funcClassMemberAdd,
}

var nodeClassMemberDel = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ClassMemberDel,
	Func:       funcClassMemberDel,
}

var nodeExamAdd = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager},
	Text:       public.Menu02ExamAdd,
	Func:       funcExamAdd,
}

var nodeExamDel = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager},
	Text:       public.Menu02ExamDel,
	Func:       funcExamDel,
}

var nodeExamCheck = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager},
	Text:       public.Menu02ExamCheck,
	Func:       funcExamCheck,
}

var nodeExamEdit = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager},
	Text:       public.Menu02ExamEdit,
	Func:       funcExamEdit,
}

var nodeReportAdd = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager},
	Text:       public.Menu02ReportAdd,
	Func:       funcReportAdd,
}

var nodeReportEdit = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ReportEdit,
	Func:       funcReportEdit,
}

var nodeReportCheckDate = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager},
	Text:       public.Menu02ReportCheckDate,
	Func:       funcReportCheckDate,
}

var nodeReportCheckId = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager},
	Text:       public.Menu02ReportCheckId,
	Func:       funcReportCheckId,
}

var nodeMailboxSend = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager, public.PermitUser},
	Text:       public.Menu02MailboxSend,
	Func:       funcMailboxSend,
}

var nodeMailboxReceive = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator, public.PermitManager, public.PermitUser},
	Text:       public.Menu02MailboxReceive,
	Func:       funcMailboxReceive,
}

var nodeToolboxAddActiveCode = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ToolboxAddActiveCode,
	Func:       funcToolboxAddActiveCode,
}

var nodeToolboxDelAccount = public.StructMenu{
	PermitMode: public.PermitModeEqual,
	PermitList: []int{public.PermitAdministrator},
	Text:       public.Menu02ToolboxDelAccount,
	Func:       funcToolboxDelAccount,
}

(五)菜单功能

这一部分在下一节讲述。

func checkArgs(option []any) bool                                      {}
func funcLogin(share *public.StructShareBase, option ...any)           {}
func funcRegister(share *public.StructShareBase, _ ...any)             {}
func funcChangeAccount(share *public.StructShareBase, _ ...any)        {}
func funcProfileSelfCheck(share *public.StructShareBase, _ ...any)     {}
func funcProfileSelfModify(share *public.StructShareBase, _ ...any)    {}
func funcProfileOtherCheck(share *public.StructShareBase, _ ...any)    {}
func funcProfileOtherEdit(share *public.StructShareBase, _ ...any)     {}
func funcSubjectAdd(share *public.StructShareBase, _ ...any)           {}
func funcSubjectDel(share *public.StructShareBase, _ ...any)           {}
func funcSubjectCheck(share *public.StructShareBase, _ ...any)         {}
func funcSubjectEdit(share *public.StructShareBase, _ ...any)          {}
func funcClassAdd(share *public.StructShareBase, _ ...any)             {}
func funcClassDel(share *public.StructShareBase, _ ...any)             {}
func funcClassCheck(share *public.StructShareBase, _ ...any)           {}
func funcClassEdit(share *public.StructShareBase, _ ...any)            {}
func funcClassMemberAdd(share *public.StructShareBase, _ ...any)       {}
func funcClassMemberDel(share *public.StructShareBase, _ ...any)       {}
func funcExamAdd(share *public.StructShareBase, _ ...any)              {}
func funcExamDel(share *public.StructShareBase, _ ...any)              {}
func funcExamCheck(share *public.StructShareBase, _ ...any)            {}
func funcExamEdit(share *public.StructShareBase, _ ...any)             {}
func funcReportAdd(share *public.StructShareBase, _ ...any)            {}
func funcReportEdit(share *public.StructShareBase, _ ...any)           {}
func funcReportCheckDate(share *public.StructShareBase, _ ...any)      {}
func funcReportCheckId(share *public.StructShareBase, _ ...any)        {}
func funcMailboxSend(share *public.StructShareBase, _ ...any)          {}
func funcMailboxReceive(share *public.StructShareBase, _ ...any)       {}
func funcToolboxAddActiveCode(share *public.StructShareBase, _ ...any) {}
func funcToolboxDelAccount(share *public.StructShareBase, _ ...any)    {}

(五)其他工具

1. 初始化UDP客户端

func InitUdpClient() {
	conn, err := net.ListenPacket("udp", ":0")
	if err != nil {
		log.Fatal(err)
	}
	dst, err := net.ResolveUDPAddr("udp", "127.0.0.1:32111")
	if err != nil {
		log.Fatal(err)
	}
	socketDst = dst
	socket = conn
}

2. 字符串转Byte

func FuncStringToByteSlice(s string) []byte {
	tmp1 := (*[2]uintptr)(unsafe.Pointer(&s))
	tmp2 := [3]uintptr{tmp1[0], tmp1[1], tmp1[1]}
	return *(*[]byte)(unsafe.Pointer(&tmp2))

}

3. 日志打印

func FuncPrintLog(level byte, info string, args ...error) {
	var stringErr = "Undefined EOF..."
	if 0 < len(args) && args[0] != nil {
		stringErr = fmt.Sprintf("%v", args[0])
	}
	if openDebug {
		if 0 == len(args) {
			fmt.Printf("[DEBUG] [INFO] %s\n", info)
		} else {
			fmt.Printf("[DEBUG] [ERRS] %s --> %s\n", info, stringErr)
		}
		time.Sleep(3 * time.Second)
	}
	if socket == nil || socketDst == nil {
		fmt.Printf("\n[ERROR] 发送数据时建立链接失败,套接字对象为空,数据内容:%s\n", info)
		time.Sleep(10 * time.Second)
		return
	}
	switch level {
	case LogErrs:
		data := "[ERROR] " + info
		if 0 != len(args) {
			data = data + args[0].Error()
		}
		_, err := socket.WriteTo(FuncStringToByteSlice(data), socketDst)
		if err != nil {
			fmt.Printf("\n发送数据失败,原始错误信息:%s,日志无法被记录,err:%v\n", stringErr, err)
			time.Sleep(10 * time.Second)
			return
		}
		break
	case LogWarn:
		data := "[WARN] " + info
		_, err := socket.WriteTo(FuncStringToByteSlice(data), socketDst)
		if err != nil {
			fmt.Printf("\n发送数据失败,日志无法被记录,err:%v\n", err)
			time.Sleep(10 * time.Second)
			return
		}
		break
	case LogInfo:
		data := "[INFO] " + info
		_, err := socket.WriteTo(FuncStringToByteSlice(data), socketDst)
		if err != nil {
			fmt.Printf("\n发送数据失败,日志无法被记录,err:%v\n", err)
			time.Sleep(10 * time.Second)
			return
		}
		break
	default:
	}

}

4. 打印并等待若干时间

func TipWait(str string, timeSleep ...int) {
	fmt.Print(str)
	timer := 3
	if len(timeSleep) != 0 && 0 < timeSleep[0] {
		timer = timeSleep[0]
	}
	for i := 0; i < timer; i++ {
		fmt.Print(".")
		time.Sleep(1 * time.Second)
	}
	fmt.Println()
}

5. 清屏

func Clear() {
	var cmd *exec.Cmd
	switch SysType {
	case "linux":
		cmd = exec.Command("clear")
		break
	case "windows":
		cmd = exec.Command("cmd", "/c", "cls")
		break
	default:
		FuncPrintLog(LogWarn, "没有对应的系统类型,无法清屏")
		return
	}
	cmd.Stdout = os.Stdout
	err := cmd.Run()
	if err != nil {
		FuncPrintLog(LogWarn, "操作系统无法完全匹配,无法清屏")
	}
}

6. 引导选择选项

func SelectOption(rangeMax int) int {
	var option int
	Tip("请选择菜单选项:")
	_, err := fmt.Scanln(&option)
	if err != nil {
		Tip(Format(TipOpFail, "输入内容非法!请重新输入:"))
		return -1
	}
	if rangeMax < option || option < 0 {
		Tip(Format(TipOpFail, "请选择范围内的选项:"))
		return -1
	}
	return option
}

7. 引导输入

func TipInput(tip string, verifier ...func(string) (bool, string)) (string, error) {
	var input string
	for {
		Clear()
		fmt.Println(tip)
		_, err := fmt.Scanln(&input)
		if err != nil {
			TipWait(Format(TipOpFail, "输入内容非法!请重新输入!"))
			return "", err
		}
		if len(verifier) == 0 || verifier[0] == nil {
			return input, nil
		}
		vf := verifier[0]
		vfFlag, tips := vf(input)
		if vfFlag {
			break
		}
		TipWait(tips)
	}
	return input, nil
}

8. 格式化输入
提供类似Sprintf()的操作。

func Format(format string, strList ...string) string {
	var builder strings.Builder
	countLengthStrList := 0
	lenList := len(strList)
	for _, str := range strList {
		countLengthStrList += len(str)
	}
	// 计算容量并预分配
	builder.Grow(countLengthStrList + len(format) - lenList*2)
	count := 0
	start := 0
	lastByte := int32(format[0])
	for i, bt := range format {
		if i == 0 {
			continue
		}
		if count == lenList {
			builder.WriteString(format[i:])
			break
		}
		if lastByte == '%' && bt == 's' {
			for j := start; j < i-1; j++ {
				builder.WriteByte(format[j])
			}
			//builder.WriteString(format[start : i-1])
			builder.WriteString(strList[count])
			count++
			start = i + 1
		}
		lastByte = bt
	}
	return builder.String()
}

Ⅵ. 结语

本文只是第一篇,关于结构上的实现。下一篇是逻辑功能的具体实现,希望尽早能弄完。但是!(划重点)功能的实现已经弄得很复杂了,目前在考虑要怎么加上,有些东西比如数据库,我没有用现成的,因此做了个简单版的数据存储,还得增删查改,因此有点麻烦。


提前祝大家元旦快乐!!


Ⅶ. 项目位置

Go-NoUiStudentManage

Ⅷ. 参考文献

[1] Go 并发 | 菜鸟教程
[2] CLI Color | Github


END

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值