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 crontab and the cron library, 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 AddFunc method has limitations; the method passed in cannot have parameters. If parameters are necessary, the AddJob method can be used:
    crontab.AddJob("* * * * *", Job{})
     // Job is a class that implements the Run method.
  • When obtaining int type data such as id or state, 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 int types to string, format time types, and handle possible null data. It's recommended to write an Assemble method (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 errname when 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 int pointer to the sending method and incremented it with each successful send. Later, I switched to adding a bool return value to the method and using a for loop outside to increment.

Understanding Go Language Interface for Polymorphism:

  • Go language uses struct and interface to 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方法,并且对外开放接口,可以在外面实现推送内容的增删改查。

 

学到的新技术:

  1. 第一次了解到crontab和接触到cron库,掌握了crontab的定时表达式

    crontab.AddFunc("* * * * *", test) //五个*号分别表示分,时,天,月,星期

    AddFunc方法具有局限性,传入的方法不能带有参数,如果一定要带参数可以用AddJob方法

    crontab.AddJob("* * * * *", Job{})  //Job是一个实现了Run方法的类
  2. 获取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
    }
  3. 返回数据给前端时要注意格式,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
    }

     

低级错误:

  1. 在使用反射方法纠错时没有写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+"未填写或有误")
    }
    }
  2. 项目中很多地方纠错就用了log打印一下,没有给前端返回消息或者及时return

  3. 重复连接数据,只要设置全局变量,然后在main方法中连接一次即可

  4. 颜色要用美式英语color

 

理解错误:

  1. 数据库表建多余了,对表功能的理解不够,下次建表前要想想清楚(及时问leader)

  2. 在统计成功发送数量时,想到的是往发送方法中传入一个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)

接口中有一些方法,只要某结构体实现了该接口中的所有方法,该结构体的实体就能作为已该接口为入参的入参

posted @ 2021-12-15 11:36  临卓  阅读(172)  评论(0)    收藏  举报