feat(notify): 작업 배정·댓글·PM 지정 시 대상 유저에게 쪽지함 알림(+모바일 푸시)
All checks were successful
build-and-push / build (push) Successful in 32s
All checks were successful
build-and-push / build (push) Successful in 32s
- 작업 생성/수정으로 담당자 배정 시 그 구성원에게 알림(type=task), 본인배정 제외 - 내 작업에 새 댓글 달리면 담당자에게 알림(본인 댓글 제외) - 프로젝트 PM 지정/변경 시 PM에게 알림 - 모두 기존 s.notify() 사용 → Notification 생성 + 등록기기로 FCM 푸시(자격증명 시 자동) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c865baccd2
commit
dce6bb215e
@ -212,6 +212,9 @@ func (s *Server) handleCreateProject(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
s.db.Create(&p)
|
||||
s.audit(r, "create", "project", p.ID, p.Name)
|
||||
if p.PMEmail != "" && !s.owns(r, p.PMEmail) {
|
||||
s.notify(p.PMEmail, "project", "프로젝트 PM으로 지정되었습니다", p.Name, "/projects/"+p.ID)
|
||||
}
|
||||
writeJSON(w, http.StatusCreated, p)
|
||||
}
|
||||
|
||||
@ -224,6 +227,7 @@ func (s *Server) handlePatchProject(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusNotFound, "프로젝트를 찾을 수 없습니다")
|
||||
return
|
||||
}
|
||||
oldPM := p.PMEmail
|
||||
var patch map[string]interface{}
|
||||
if err := decodeJSON(r, &patch); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
@ -238,6 +242,9 @@ func (s *Server) handlePatchProject(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
s.db.First(&p, "id = ?", p.ID)
|
||||
if p.PMEmail != "" && !strings.EqualFold(p.PMEmail, oldPM) && !s.owns(r, p.PMEmail) {
|
||||
s.notify(p.PMEmail, "project", "프로젝트 PM으로 지정되었습니다", p.Name, "/projects/"+p.ID)
|
||||
}
|
||||
writeJSON(w, http.StatusOK, p)
|
||||
}
|
||||
|
||||
@ -383,9 +390,21 @@ func (s *Server) handleCreateTask(w http.ResponseWriter, r *http.Request) {
|
||||
t.Lane = "todo"
|
||||
}
|
||||
s.db.Create(&t)
|
||||
// 담당자로 배정되면 본인에게 알림(+모바일 푸시). 본인이 본인에게 단 경우는 제외.
|
||||
if t.Assignee != "" && !s.owns(r, t.Assignee) {
|
||||
s.notify(t.Assignee, "task", "작업에 배정되었습니다",
|
||||
fmt.Sprintf("[%s] %s", s.projectName(id), t.Title), "/projects/"+id)
|
||||
}
|
||||
writeJSON(w, http.StatusCreated, t)
|
||||
}
|
||||
|
||||
// projectName returns a project's name (best-effort) for notification text.
|
||||
func (s *Server) projectName(id string) string {
|
||||
var p models.Project
|
||||
s.db.Select("name").First(&p, "id = ?", id)
|
||||
return p.Name
|
||||
}
|
||||
|
||||
func (s *Server) handlePatchTask(w http.ResponseWriter, r *http.Request) {
|
||||
var t models.ProjectTask
|
||||
if err := s.db.First(&t, "id = ?", chi.URLParam(r, "tId")).Error; err != nil {
|
||||
@ -396,6 +415,7 @@ func (s *Server) handlePatchTask(w http.ResponseWriter, r *http.Request) {
|
||||
writeError(w, http.StatusForbidden, "권한이 없습니다")
|
||||
return
|
||||
}
|
||||
oldAssignee := t.Assignee
|
||||
var patch map[string]interface{}
|
||||
if err := decodeJSON(r, &patch); err != nil {
|
||||
writeError(w, http.StatusBadRequest, err.Error())
|
||||
@ -414,6 +434,11 @@ func (s *Server) handlePatchTask(w http.ResponseWriter, r *http.Request) {
|
||||
patch = snakeKeys(patch)
|
||||
s.db.Model(&t).Updates(patch)
|
||||
s.db.First(&t, "id = ?", t.ID)
|
||||
// 담당자가 새 사람으로 바뀌면 그 사람에게 알림(+푸시).
|
||||
if t.Assignee != "" && !strings.EqualFold(t.Assignee, oldAssignee) && !s.owns(r, t.Assignee) {
|
||||
s.notify(t.Assignee, "task", "작업에 배정되었습니다",
|
||||
fmt.Sprintf("[%s] %s", s.projectName(t.ProjectID), t.Title), "/projects/"+t.ProjectID)
|
||||
}
|
||||
writeJSON(w, http.StatusOK, t)
|
||||
}
|
||||
|
||||
@ -459,6 +484,18 @@ func (s *Server) handleCreateTaskComment(w http.ResponseWriter, r *http.Request)
|
||||
c.TaskID = tid
|
||||
c.AuthorEmail = s.email(r)
|
||||
s.db.Create(&c)
|
||||
// 댓글이 달리면 작업 담당자에게 알림(본인 댓글 제외).
|
||||
var t models.ProjectTask
|
||||
if s.db.Select("assignee", "title", "project_id").First(&t, "id = ?", tid).Error == nil {
|
||||
if t.Assignee != "" && !s.owns(r, t.Assignee) {
|
||||
body := c.Body
|
||||
if len(body) > 50 {
|
||||
body = body[:50] + "…"
|
||||
}
|
||||
s.notify(t.Assignee, "task", "내 작업에 새 댓글",
|
||||
fmt.Sprintf("%s — %s", t.Title, body), "/projects/"+t.ProjectID)
|
||||
}
|
||||
}
|
||||
writeJSON(w, http.StatusCreated, c)
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user