Skip to content
30 changes: 13 additions & 17 deletions cmd/common/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,26 +172,22 @@ func syncNoticeTask() error {
return fmt.Errorf("notice sync task: failed to create notice: %w", err)
}

channelProperties := map[string]string{
"channel_activity": "com.west2online.umeng.MfrMessageActivity",
"huawei_channel_importance": "NORMAL",
"xiaomi_channel_id": config.Vendors.Xiaomi.JwchNotice,
}
// 进行消息推送
err = umeng.SendAndroidGroupcastWithUrl(config.Umeng.Android.AppKey, config.Umeng.Android.AppMasterSecret,
"", "教务处通知", info.Title, constants.UmengJwchNoticeTag, info.URL, channelProperties)
if err != nil {
logger.Errorf("notice sync task: failed to send notice to Android: %v", err)
}
if ok := umeng.EnqueueAsync(func() error {
err = umeng.SendAndroidGroupcastWithUrl("教务处通知", info.Title, "", info.URL, constants.UmengJwchNoticeTag, "教务处")
if err != nil {
logger.Errorf("notice sync task: failed to send notice to Android: %v", err)
}

err = umeng.SendIOSGroupcast(config.Umeng.IOS.AppKey, config.Umeng.IOS.AppMasterSecret,
"教务处通知", "", info.Title, constants.UmengJwchNoticeTag)
if err != nil {
logger.Errorf("notice sync task: failed to send notice to IOS: %v", err)
err = umeng.SendIOSGroupcast("教务处通知", "", info.Title, constants.UmengJwchNoticeTag, "教务处")
if err != nil {
logger.Errorf("notice sync task: failed to send notice to IOS: %v", err)
}
logger.Infof("notice sync task: notice send success")
return nil
}); !ok {
logger.Errorf("umeng async queue full, drop notice notification")
}
logger.Infof("notice sync task: notice send success")

time.Sleep(constants.UmengRateLimitDelay)
}
return nil
}
Expand Down
29 changes: 23 additions & 6 deletions config/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,32 @@ type umeng struct {
IOS IOSUmeng `mapstructure:"ios"`
}

type vendor struct {
ExamNotifications string `mapstructure:"ExamNotifications"`
ExamResultsNotifications string `mapstructure:"ExamResultsNotifications"`
JwchNotice string `mapstructure:"JwchNotice"`
type oppo struct {
ChannelID string `mapstructure:"channel_id"`
Category string `mapstructure:"category"`
NotifyLevel string `mapstructure:"notify_level"`
PrivateMsgTemplate struct {
PrivateMsgTemplateID string `mapstructure:"private_msg_template_id"`
} `mapstructure:"private_msg_template"`
}

type huawei struct {
ChannelImportance string `mapstructure:"channel_importance"`
ChannelCategory string `mapstructure:"channel_category"`
}

type localProperties struct {
ChannelID string `mapstructure:"channel_id"`
ChannelName string `mapstructure:"channel_name"`
}

type vendors struct {
Xiaomi vendor `mapstructure:"xiaomi"`
Huawei vendor `mapstructure:"huawei"`
ChannelActivity string `mapstructure:"channel_activity"`
XiaoMiChannelID string `mapstructure:"xiaomi_channel_id"`
VivoCategory string `mapstructure:"vivo_category"`
Oppo oppo `mapstructure:"oppo"`
Huawei huawei `mapstructure:"huawei"`
LocalProperties localProperties `mapstructure:"local_properties"`
}

type mcp struct {
Expand Down
19 changes: 6 additions & 13 deletions internal/academic/service/get_scores.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,9 @@ package service
import (
"fmt"
"strings"
"time"

"github.com/bytedance/sonic"

"github.com/west2-online/fzuhelper-server/config"
loginmodel "github.com/west2-online/fzuhelper-server/kitex_gen/model"
"github.com/west2-online/fzuhelper-server/pkg/base"
"github.com/west2-online/fzuhelper-server/pkg/base/context"
Expand Down Expand Up @@ -179,9 +177,10 @@ func (s *AcademicService) handleScoreChange(stuID string, scores []*jwch.Mark) (
scores[i].Name, scores[i].Semester, scores[i].Teacher,
scores[i].ElectiveType,
}, "|"))
err = s.sendNotifications(scores[i].Name, tag)
if err != nil {
return err
if ok := umeng.EnqueueAsync(func() error {
return s.sendNotifications(scores[i].Name, tag)
}); !ok {
logger.Errorf("umeng async queue full, drop score notification, tag:%v", tag)
}
// 写入课程信息,代表发送过通知
_, err = s.db.Academic.CreateCourseOffering(s.ctx, &model.CourseOffering{
Expand All @@ -202,21 +201,15 @@ func (s *AcademicService) handleScoreChange(stuID string, scores []*jwch.Mark) (
}

func (s *AcademicService) sendNotifications(courseName, tag string) (err error) {
err = umeng.SendAndroidGroupcastWithGoApp(config.Umeng.Android.AppKey, config.Umeng.Android.AppMasterSecret,
"", fmt.Sprintf("%v成绩更新啦", courseName), "",
tag)
err = umeng.SendAndroidGroupcastWithGoApp(fmt.Sprintf("%v成绩更新啦", courseName), "", "", tag, fmt.Sprintf("成绩更新%v", tag[:12]))
if err != nil {
logger.Errorf("task queue: failed to send notice to Android: %v", err)
}
err = umeng.SendIOSGroupcast(config.Umeng.IOS.AppKey, config.Umeng.IOS.AppMasterSecret,
fmt.Sprintf("%v成绩更新啦", courseName), "", "",
tag)
err = umeng.SendIOSGroupcast(fmt.Sprintf("%v成绩更新啦", courseName), "", "", tag, fmt.Sprintf("成绩更新%v", tag[:12]))
if err != nil {
logger.Errorf("task queue: failed to send notice to IOS: %v", err)
}

logger.Infof("task queue: send notice to app, tag:%v", tag)
// 停止 30 秒防止 umeng 限流
time.Sleep(constants.UmengRateLimitDelay)
return nil
}
4 changes: 2 additions & 2 deletions internal/academic/service/get_scores_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,7 @@ func TestAcademicService_sendNotifications(t *testing.T) {
Convey("should send notifications to both Android and iOS", func() {
// Given: 准备发送推送的课程信息
courseName := "数据结构"
tag := "test_tag"
tag := "abcdefghijklmnopqrstuvwxyz123456"

// Mock umeng 推送成功
umengAndroidPatch := mockey.Mock(umeng.SendAndroidGroupcastWithGoApp).Return(nil).Build()
Expand All @@ -636,7 +636,7 @@ func TestAcademicService_sendNotifications(t *testing.T) {
Convey("should handle notification errors gracefully", func() {
// Given: 准备发送推送但可能出错
courseName := "数据结构"
tag := "test_tag"
tag := "abcdefghijklmnopqrstuvwxyz123456"

// Mock umeng 推送失败
umengAndroidPatch := mockey.Mock(umeng.SendAndroidGroupcastWithGoApp).Return(fmt.Errorf("android push failed")).Build()
Expand Down
64 changes: 0 additions & 64 deletions internal/course/service/get_course_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ import (
"slices"
"sort"
"strings"
"time"

"github.com/bytedance/sonic"

"github.com/west2-online/fzuhelper-server/config"
"github.com/west2-online/fzuhelper-server/internal/course/pack"
"github.com/west2-online/fzuhelper-server/kitex_gen/course"
kitexModel "github.com/west2-online/fzuhelper-server/kitex_gen/model"
Expand All @@ -36,9 +34,7 @@ import (
"github.com/west2-online/fzuhelper-server/pkg/constants"
"github.com/west2-online/fzuhelper-server/pkg/db/model"
"github.com/west2-online/fzuhelper-server/pkg/errno"
"github.com/west2-online/fzuhelper-server/pkg/logger"
"github.com/west2-online/fzuhelper-server/pkg/taskqueue"
"github.com/west2-online/fzuhelper-server/pkg/umeng"
"github.com/west2-online/fzuhelper-server/pkg/utils"
"github.com/west2-online/jwch"
"github.com/west2-online/yjsy"
Expand Down Expand Up @@ -109,7 +105,6 @@ func (s *CourseService) GetCourseList(req *course.CourseListRequest, loginData *
return s.removeDuplicateCourses(pack.BuildCourse(courses)), nil
}

// putCourseToDatabase 将课程表存入数据库,如果与数据库数据不同,进行 umeng 推送
func (s *CourseService) putCourseToDatabase(stuId string, term string, courses []*kitexModel.Course) error {
old, err := s.db.Course.GetUserTermCourseSha256ByStuIdAndTerm(s.ctx, stuId, term)
if err != nil {
Expand Down Expand Up @@ -148,70 +143,11 @@ func (s *CourseService) putCourseToDatabase(stuId string, term string, courses [
if err != nil {
return err
}
// 异步处理调课通知逻辑
s.taskQueue.Add(stuId, taskqueue.QueueTask{Execute: func() error {
return s.handleCourseUpdate(term, courses, old)
}})
}

return nil
}

// 当发现课程有调课时,对具体的字段进行一一对比,找出调课的课程
func (s *CourseService) handleCourseUpdate(term string, newCourses []*kitexModel.Course, oldCourses *model.UserCourse) (err error) {
// 将 old 的课程进行解析,变成同一个格式
olds := make([]*kitexModel.Course, 0)

if oldCourses.TermCourses != "" {
if err = sonic.Unmarshal([]byte(oldCourses.TermCourses), &olds); err != nil {
return fmt.Errorf("service.GetCourseList: Unmarshal old courses failed: %w", err)
}
}

// 构建 hash 映射表,方便对比
hashToAdjust := make(map[string]string)
for _, c := range olds {
hash := utils.GenerateCourseHash(c.Name, term, c.Teacher, c.ElectiveType, c.RawScheduleRules)
if c.ElectiveType != "" {
// 旧数据没有这个字段,防止错误发送通知
hashToAdjust[hash] = c.RawAdjust
}
}

// 对比新课程和旧课程的调课规则,有变化则发送通知
for _, c := range newCourses {
hash := utils.GenerateCourseHash(c.Name, term, c.Teacher, c.ElectiveType, c.RawScheduleRules)
if oldAdjust, exists := hashToAdjust[hash]; exists {
if oldAdjust != c.RawAdjust {
err = s.sendNotifications(c.Name, hash)
if err != nil {
return fmt.Errorf("service.GetCourseList: Send notifications failed: %w", err)
}
}
}
}

return nil
}

func (s *CourseService) sendNotifications(courseName, tag string) (err error) {
err = umeng.SendAndroidGroupcastWithGoApp(config.Umeng.Android.AppKey, config.Umeng.Android.AppMasterSecret,
"", fmt.Sprintf("[调课] %v", courseName), "", tag)
if err != nil {
logger.Errorf("service.sendNotifications: Send course updated message to Android failed: %v", err)
return err
}

err = umeng.SendIOSGroupcast(config.Umeng.Android.AppKey, config.Umeng.Android.AppMasterSecret,
"", fmt.Sprintf("[调课] %v", courseName), "", tag)
if err != nil {
logger.Errorf("service.sendNotifications: Send course updated message to IOS failed: %v", err)
return err
}
time.Sleep(constants.UmengRateLimitDelay)
return nil
}

func (s *CourseService) GetCourseListYjsy(req *course.CourseListRequest, loginData *kitexModel.LoginData) ([]*kitexModel.Course, error) {
var err error

Expand Down
Loading