Automatic Notification Sender
Introduce:
This is a backend program that periodically pushes academic information to teachers. The scheduled push functionality is implemented using Go's cron library, which regularly invokes a self-written teacher_noti method. It also provides external interfaces for adding, deleting, modifying, and querying push content.
New Techniques Learned:
- This was my first exposure to
crontaband thecronlibrary, where I learned how to use crontab's scheduling expressions:crontab.AddFunc("* * * * *", test)// The five asterisks represent minute, hour, day, month, and weekday, respectively. - The
AddFuncmethod has limitations; the method passed in cannot have parameters. If parameters are necessary, theAddJobmethod can be used:crontab.AddJob("* * * * *", Job{})// Job is a class that implements the Run method. - When obtaining
inttype data such asidorstate, it's best to encapsulate it externally:state := ParseIntDefault(c.PostForm("state"), 0)
func ParseIntDefault(s string, d int) int {
if a, err := strconv.Atoi(s); err == nil {
return a
}
return d
} -
When returning data to the frontend, pay attention to format: convert
inttypes tostring, formattimetypes, and handle possible null data. It's recommended to write anAssemblemethod (using interfaces to implement polymorphism) that accepts pointer data types:// Assembler assemble model into map
type Assembler interface {
Assemble() map[string]interface{}
}
// StructToMap returns a map
func StructToMap(a Assembler) map[string]interface{} {
return a.Assemble()
}
func (item *NotiForm) Assemble() map[string]interface{} {
data := map[string]interface{}{}
data["noti_id"] = strconv.Itoa(item.ID)
//...
// After using the ParseIntDefault method, invalid input will automatically default to 0
if item.End.IsZero() {
data["end"] = ""
} else {
data["end"] = item.End.In(cstlocGlobal).Format("2006-01-02 15:04:05")
}
return data
}
Low-Level Errors:
- Did not write
errnamewhen using reflection methods for error correction:errStrings := []string{}
t := reflect.TypeOf(notiForm)
for i := 0; i < t.NumField(); i++ {
if strings.Contains(err.Error(), t.Field(i).Name) {
errname := t.Field(i).Tag.Get("errname")
errStrings = append(errStrings, errname+"errname+" not filled in or incorrect"")
} - In many parts of the project, errors were simply logged without returning messages to the frontend or returning promptly.
- Repeatedly connecting to the database; this can be avoided by setting a global variable and connecting only once in the main method.
- Use American English for color names, e.g.,
color.
Misunderstandings:
- Created redundant database tables due to insufficient understanding of table functions; next time, think more clearly (and ask the leader when in doubt) before creating tables.
- When counting the number of successful sends, initially passed an
intpointer to the sending method and incremented it with each successful send. Later, I switched to adding aboolreturn value to the method and using aforloop outside to increment.
Understanding Go Language Interface for Polymorphism:
- Go language uses
structandinterfaceto implement polymorphism:type Cat struct{
Name string
}
type Dog struct{
Name string
}
// 1. Create an interface specifying the methods to implement
type animaler interface {
eat()
sleep()
}
// 2. Implement the methods in the interface (all methods in the interface must be implemented)
func (cat Cat) eat() {
fmt.Println(cat.Name,"eat")
}
func (cat Cat) sleep() {
fmt.Println(cat.Name,"sleep")
}
func (dog Dog) eat() {
fmt.Println(dog.Name,"eat")
}
func (dog Dog) sleep() {
fmt.Println(dog.Name,"sleep")
}
// 3. Create a method that takes the interface as a parameter
func TestInterface(a animaler) {
a.eat()
a.sleep()
}
// 4. When using, call the TestInterface() method
var cat Cat
cat.Name = "mimi"
var dog Dog
dog.Name = "wangwang"
TestInterface(cat) // The parameter type is `animaler`, but the actual argument is an instance of a struct that implements the interface methods TestInterface(dog)
TestInterface(dog) - An interface contains some methods; as long as a struct implements all the methods in the interface, the struct's instance can be used as an argument for functions that take the interface type as a parameter.
简介:
是一个定时给教师推送教务信息的后台程序,定时推送功能由Go的cron库来实现,定时调用自己写的teacher_noti方法,并且对外开放接口,可以在外面实现推送内容的增删改查。
学到的新技术:
-
第一次了解到crontab和接触到cron库,掌握了crontab的定时表达式
crontab.AddFunc("* * * * *", test) //五个*号分别表示分,时,天,月,星期AddFunc方法具有局限性,传入的方法不能带有参数,如果一定要带参数可以用AddJob方法
crontab.AddJob("* * * * *", Job{}) //Job是一个实现了Run方法的类 -
获取id、状态等int类型的数据时,最好在外面封装一层
state := ParseIntDefault(c.PostForm("state"), 0)
func ParseIntDefault(s string, d int) int {
if a, err := strconv.Atoi(s); err == nil {
return a
}
return d
} -
返回数据给前端时要注意格式,int类型要转string,time类型要format,还要做好数据可能为空的处理。这里最好写个Assemble方法(用接口实现多态),要传入指针数据类型
// Assembler 将 model 组装成 map
type Assembler interface {
Assemble() map[string]interface{}
}
// StructToMap 返回 map
func StructToMap(a Assembler) map[string]interface{} {
return a.Assemble()
}
func (item *NotiForm) Assemble() map[string]interface{} {
data := map[string]interface{}{}
data["noti_id"] = strconv.Itoa(item.ID)
//...
//使用ParseIntDefault方法后,若传入数据无效会自动校正为0
if item.End.IsZero() {
data["end"] = ""
} else {
data["end"] = item.End.In(cstlocGlobal).Format("2006-01-02 15:04:05")
}
return data
}
-
在使用反射方法纠错时没有写errname
errStrings := []string{}
t := reflect.TypeOf(notiForm)
for i := 0; i < t.NumField(); i++ {
if strings.Contains(err.Error(), t.Field(i).Name) {
errname := t.Field(i).Tag.Get("errname")
errStrings = append(errStrings, errname+"未填写或有误")
}
} -
项目中很多地方纠错就用了log打印一下,没有给前端返回消息或者及时return
-
重复连接数据,只要设置全局变量,然后在main方法中连接一次即可
-
颜色要用美式英语color
理解错误:
-
数据库表建多余了,对表功能的理解不够,下次建表前要想想清楚(及时问leader)
-
在统计成功发送数量时,想到的是往发送方法中传入一个int遍历的指针,每次成功发送后都++。后改为给该方法添加一个bool返回值,外层使用for循坏判断++
对Go语言接口实现多态的理解:
go语言使用struct和interface实现多态
type Cat struct{
Name string
}
type Dog struct{
Name string
}
//1. 创建一个接口,指定要实现的方法
type animaler interface {
eat()
sleep()
}
//2. 实现接口中的方法 (要将接口中的方法全部实现)
func (cat Cat) eat() {
fmt.Println(cat.Name,"eat")
}
func (cat Cat) sleep() {
fmt.Println(cat.Name,"sleep")
}
func (dog Dog) eat() {
fmt.Println(dog.Name,"eat")
}
func (dog Dog) sleep() {
fmt.Println(dog.Name,"sleep")
}
//3. 创建一个方法传入接口
func TestInterface(a animaler) {
a.eat()
a.sleep()
}
//4. 使用时调用方法TestInterface()
var cat Cat
cat.Name = "mimi"
var dog Dog
dog.Name = "wangwang"
TestInterface(cat) //定义时入参时为animaler类型,调用时入参为实现了接口方法的实体类类型
TestInterface(dog)
接口中有一些方法,只要某结构体实现了该接口中的所有方法,该结构体的实体就能作为已该接口为入参

浙公网安备 33010602011771号