feat(mail): 프로젝트 고객사 도메인 메일 연동(Google Workspace 도메인위임) + 공동 메모
build-and-push / build (push) Successful in 32s
build-and-push / build (push) Successful in 32s
- Project.ClientDomain 필드, MailNote(프로젝트 구성원 공동 메모) 모델
- internal/mailsync: 서비스계정+도메인위임으로 팀 메일함을 도메인 검색·집계(stdlib만, push 패턴 재사용)
· GOOGLE_SA_CREDENTIALS_FILE 미설정 시 비활성(graceful)
- GET /projects/{id}/mails (3분 캐시), GET/PUT /projects/{id}/mail-notes
- fix: handlePatchProject map-key Updates가 camelCase 멀티워드 필드(consultingType·
scopeText·pmEmail·clientDomain·날짜)를 컬럼에 못 맞춰 저장 실패하던 버그 → snakeKeys 변환
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
ce07aced0f
commit
751aa8ed97
@@ -28,7 +28,7 @@ func All() []interface{} {
|
||||
&Attendance{}, &LeaveRequest{}, &OvertimeRequest{}, &WorkPolicy{}, &LeaveBalance{},
|
||||
// slice 3 — projects
|
||||
&Company{}, &Product{}, &Version{}, &Project{}, &ProjectMember{},
|
||||
&ClientContact{}, &ProjectTask{}, &TaskComment{}, &Contract{}, &ContractFile{}, &PaymentSplit{},
|
||||
&ClientContact{}, &ProjectTask{}, &TaskComment{}, &MailNote{}, &Contract{}, &ContractFile{}, &PaymentSplit{},
|
||||
// slice 4 — incentive
|
||||
&IncentiveConfig{}, &PaymentStage{}, &UserIncentive{}, &QuarterlySettlement{},
|
||||
// slice 5 — accounting
|
||||
|
||||
@@ -56,6 +56,7 @@ type Project struct {
|
||||
ScopeText string `json:"scopeText"` // 글 계약 범위
|
||||
ScopeGraphic string `json:"scopeGraphic"` // 그림 계약 범위
|
||||
PMEmail string `json:"pmEmail"` // 프로젝트 PM
|
||||
ClientDomain string `json:"clientDomain"` // 고객사 메일 도메인(postfix) — 관련 메일 집계용
|
||||
Cautions string `json:"cautions"` // 주의사항 (구성원 공개)
|
||||
Status string `json:"status"` // planned | active | hold | done | dropped
|
||||
StartDate string `json:"startDate"`
|
||||
@@ -125,6 +126,20 @@ type TaskComment struct {
|
||||
|
||||
func (m *TaskComment) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
|
||||
|
||||
// MailNote is a project's SHARED memo on one client-domain email — any project
|
||||
// member can edit it (협업 메모). Keyed by (project, RFC822 Message-ID).
|
||||
type MailNote struct {
|
||||
Base
|
||||
ProjectID string `gorm:"index:idx_mailnote_pm,unique" json:"projectId"`
|
||||
MessageID string `gorm:"index:idx_mailnote_pm,unique" json:"messageId"`
|
||||
Body string `json:"body"`
|
||||
LastEditedBy string `json:"lastEditedBy"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (m *MailNote) BeforeCreate(*gorm.DB) error { m.ensureID(); return nil }
|
||||
|
||||
// Contract holds the [admin-only] commercial terms of a project. BE is the
|
||||
// break-even floor (손해가 안 나는 최소 금액). Exposed ONLY to admins.
|
||||
type Contract struct {
|
||||
|
||||
Reference in New Issue
Block a user