Go-学生教务管理系统【无界面 / 离线版】(一)

news2024/11/23 19:47:24

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

  • Ⅰ. 程序说明
    • 一、博客日期
    • 二、引言
  • Ⅱ. 版权声明
  • Ⅲ. 开发环境
    • 一、开发配置
    • 二、开发工具
  • Ⅳ. 效果演示
    • 一、工程结构
      • (一)目录结构
      • (二)目录说明
    • 二、登陆效果
    • 三、菜单效果
    • 四、日志记录
  • Ⅴ. 设计流程
    • 一、总方案
      • (一)架构设计
      • (二)设计思路
    • 二、逻辑实现
      • (一)主要函数
      • (二)日志处理
      • (三)结构定义
      • (四)菜单构造
      • (五)菜单功能
      • (五)其他工具
  • Ⅵ. 结语
  • Ⅶ. 项目位置
  • Ⅷ. 参考文献

Ⅰ. 程序说明

一、博客日期

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

二、引言

前几天刚考完研,数学又一次挂掉了,是的,走出考场的瞬间就知道数学挂掉了,若是不设置单科最低或许还有机会。目前准备开始寻找工作,不过因为已经二战,白费了一年工作时间,不清楚还有哪个公司能招人,但这些已经是后续的事情,便想着在过年前把之前没做完的工程给补齐,于是便有了本文。


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

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

本文计划分为两个部分

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

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


下面开始第一部分内容。


Ⅱ. 版权声明

本工程作品均为本文作者撰写,作品遵循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

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

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

相关文章

【Ctfer训练计划】——(六)

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门 创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座…

Springboot启动之自定义run方法

前言 之前分析的Springboot启动过程的源码分析中给自己留了一个扩展作业&#xff1a;执行自定义的run方法&#xff0c;此方法在B.7、调用运行器简单分析过&#xff0c;今天咱们就自定义一个Run方法试试。 一、实现自定义的run方法 由于java中的接口可以多实现&#xff0c;所以…

IOS----TangramKit 布局框架

文章目录系统结构CocoaPods安装举例下面一个应用场景:布局线性布局TGLinearLayout相对布局TGRelativeLayout框架布局TGFrameLayout表格布局TGTableLayout流式布局TGFlowLayout浮动布局TGFloatLayout路径布局MyPathLayoutgithub: https://github.com/youngsoft/TangramKit/blob…

day32【代码随想录】回溯之N皇后、N皇后||、解数独、有效的数独

文章目录前言一、N皇后&#xff08;力扣51&#xff09;二、N皇后||&#xff08;力扣52&#xff09;三、解数独&#xff08;力扣37&#xff09;四、有效的数独&#xff08;力扣36&#xff09;前言 1、N皇后、 2、N皇后||、 3、解数独、 4、有效的数独 一、N皇后&#xff08;力扣…

材料表征仪器:慢正电子束谱仪最全知识讲解

1 引言 21世纪科学的发展将是微观与宏观的相互渗透与密切结合。凝聚态物理、材料科学等的研究&#xff0c;将由现在的宏观统计方法&#xff08;包括宏观量子统计&#xff09;深入发展到物质的原子层次物性研究&#xff0c;微观粒子&#xff08;颗粒、孔隙&#xff09;的量子效…

求最大公约数,求阶乘,求n个n相乘的末两位数(Python)

问题 AN: 41.求最大公约数 题目描述 对于求两个正整数m&#xff0c;n的最大公约数可以用do-while实现 输入 输入两个正整数m&#xff0c;n 输出 最大公约数 样例输入 1 2 样例输出 1 a,b map(int,input().split()) if a<b:a,b b,a#python很方便的交换操作 #适应判断不带…

MySQL复制技术方案——异步复制配置

为MySQL服务器配置复制非常简单。但由于场景不同&#xff0c;基本的步骤还是有所差异。最基本的场景是新安装的主库和备库&#xff0c;总的来说分为以下几步∶ 1. 配置复制 为MySQL服务器配置复制非常简单。但由于场景不同&#xff0c;基本的步骤还是有所差异。最基本的场景是…

HTML5 Canvas

文章目录HTML5 Canvas概述Canvas元素使用绘制直线画2条直线用直线画三角形用直线画矩形绘制矩形描边矩形填充矩形混合使用rect()清空矩形区域清空画布绘制多边形画箭头画正三角形绘制圆形HTML5 Canvas 概述 HTML5新增了一个Canvas元素&#xff0c;我们常说的Canvas技术&#…

【LeetCode】填充每个节点的下一个右侧节点指针 [M](二叉树遍历)

116. 填充每个节点的下一个右侧节点指针 - 力扣&#xff08;LeetCode&#xff09; 一、题目 给定一个 完美二叉树 &#xff0c;其所有叶子节点都在同一层&#xff0c;每个父节点都有两个子节点。二叉树定义如下&#xff1a; struct Node { int val; Node *left; Node *…

通过webpack解决浏览器兼容问题

前言 很多面试时都会问到关于浏览器兼容问题&#xff0c;正好最近在看webpack打包&#xff0c;那就在这里记录一下我们如何通过webpack来实现兼容。 需求 要知道我们到底需要兼容那些浏览器&#xff0c;在这里就需要用到browserlist来配置需要兼容的浏览器版本并告诉webpack…

Jenkins自动部署项目

目录 1.安装插件 2.配置 本文只讲解通过插件来自动部署项目&#xff0c;Jenkins的安装可以看博主的另一篇文章&#xff0c;绝对保姆级&#xff0c;简洁丝滑的安装教程&#xff1a; jenkins下载安装__BugMan的博客-CSDN博客 1.安装插件 目前业内常用的解决方法是使用publish…

程序员必备十大网站

窝窝整理了十大程序猿必备网站&#xff0c;涵盖了开源平台、搜索引擎、免费的精品课程&#xff0c;包括让你头疼的BUG、算法等。偷偷告诉你&#xff0c;还有帮你拿到心仪的 offer&#xff01; 一&#xff0c;海量的资源平台 十大网站榜首 &#xff1a;GitHub — 开发者极其重…

图查询语言 nGQL 简明教程 vol.01 快速入门

本文旨在让新手快速了解 nGQL&#xff0c;掌握方向&#xff0c;之后可以脚踩在地上借助文档写出任何心中的 NebulaGraph 图查询。 视频 本教程的视频版在B站这里。 准备工作 在正式开始 nGQL 实操之前&#xff0c;记得先看过文档「快速入门流程」&#xff0c;部署、连接过 …

《小强升职记》读后感

为什么平庸&#xff1f; 成功的人每天都在忙碌&#xff0c;平庸的人每天也在忙碌&#xff0c;而时间对每个人来说是绝对公平的&#xff0c;那么&#xff0c;两者之间的差距到底是如何产生的呢? 第一&#xff0c;在这个人生的关键时期&#xff0c;我们被迫完成角色的转变。 …

C语言 字符函数和字符串函数及模拟实现

上图注意内容 代码解释如下 int main() {if (my_strlen("abc") - my_strlen("abcdef") > 0){printf(">\n");}else{printf("<\n");}return 0; }my_strlen int my_strlen(const char* str) {int count 0;//计数器assert(str…

Springboot AOP切面

文章目录SpringBoot Aop切面(Aop)一、什么是切面二、切面的用途三、AOP切面常用注解四、详细内容1、切面&#xff08;Aspect&#xff09;2、连接点&#xff08;Joinpoint&#xff09;3、通知&#xff08;Advice&#xff09;4.切入点&#xff08;Pointcut&#xff09;五、代码操…

PMP是什么?PMP证书有什么用?(含PMP资料)

PMP介绍 PMP的英文全称是Project Management Professional&#xff0c;中文全称叫做项目管理专业人士资格认证。 它是由美国项目管理协会(PMI)在全球范围内推出的针对项目经理的资格认证体系&#xff0c;严格评估项目管理人员知识技能是否具有高品质的资格认证考试&#xff0…

学习:如何使用Axure制作网站、app结构图

​“老师&#xff0c;axure里面可以制作网站的结构图吗&#xff1f;” “结构图只能在脑图工具中才能画吗&#xff1f;能不能直接在axure里面直接画&#xff1f;” “结构图到底需不需要做&#xff1f;对我来说好像没什么用呢” 在同学们的学习当中&#xff0c;有部分同学对…

CIO你好,现在是时候我们来谈一下“去”中台的问题了

去中台是个什么鬼 去中台&#xff1f;要不我去XXXXXXX 首先&#xff0c;你看到这个标题有没有懵圈&#xff1f;是不是有一种。。。 的感觉&#xff1f; 不过我现在告诉你&#xff0c;你没有看错。这篇文章是正儿八经的来谈“去”中台的。只不过这个“去”字&#xff0c;打着…

数据结构(链表)

链表及其实现 链式结构 顺序表插入、删除时间代价的分析&#xff0c;可以看出其时间复杂度是线性阶的&#xff0c;而且会引起大量已存储元素的位置移动。 改进方法&#xff1a;链式结构 各个元素的物理存放位置在存储器中是任意的&#xff0c;不一定连续。每个元素放在一个独…