update letter outgoing final attachment
This commit is contained in:
parent
7b9db24c83
commit
e83eefc614
@ -142,6 +142,7 @@ type repositories struct {
|
||||
// letter outgoing repos
|
||||
letterOutgoingRepo *repository.LetterOutgoingRepository
|
||||
letterOutgoingAttachmentRepo *repository.LetterOutgoingAttachmentRepository
|
||||
letterOutgoingFinalAttachmentRepo *repository.LetterOutgoingFinalAttachmentRepository
|
||||
letterOutgoingRecipientRepo *repository.LetterOutgoingRecipientRepository
|
||||
letterOutgoingDiscussionRepo *repository.LetterOutgoingDiscussionRepository
|
||||
letterOutgoingDiscussionAttachRepo *repository.LetterOutgoingDiscussionAttachmentRepository
|
||||
@ -177,6 +178,7 @@ func (a *App) initRepositories() *repositories {
|
||||
userDeptRepo: repository.NewUserDepartmentRepository(a.db),
|
||||
letterOutgoingRepo: repository.NewLetterOutgoingRepository(a.db),
|
||||
letterOutgoingAttachmentRepo: repository.NewLetterOutgoingAttachmentRepository(a.db),
|
||||
letterOutgoingFinalAttachmentRepo: repository.NewLetterOutgoingFinalAttachmentRepository(a.db),
|
||||
letterOutgoingRecipientRepo: repository.NewLetterOutgoingRecipientRepository(a.db),
|
||||
letterOutgoingDiscussionRepo: repository.NewLetterOutgoingDiscussionRepository(a.db),
|
||||
letterOutgoingDiscussionAttachRepo: repository.NewLetterOutgoingDiscussionAttachmentRepository(a.db),
|
||||
@ -260,6 +262,7 @@ func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processor
|
||||
a.db,
|
||||
repos.letterOutgoingRepo,
|
||||
repos.letterOutgoingAttachmentRepo,
|
||||
repos.letterOutgoingFinalAttachmentRepo,
|
||||
repos.letterOutgoingRecipientRepo,
|
||||
repos.letterOutgoingDiscussionRepo,
|
||||
repos.letterOutgoingDiscussionAttachRepo,
|
||||
@ -385,7 +388,7 @@ func (a *App) initServices(processors *processors, repos *repositories, cfg *con
|
||||
|
||||
fileCfg := cfg.S3Config
|
||||
s3Client := client.NewFileClient(fileCfg)
|
||||
fileSvc := service.NewFileService(s3Client, processors.userProcessor, "profile", "documents")
|
||||
fileSvc := service.NewFileService(s3Client, processors.userProcessor, "profile", "documents", "finals")
|
||||
|
||||
rbacSvc := service.NewRBACService(repos.rbacRepo)
|
||||
|
||||
|
||||
@ -43,7 +43,6 @@ type CreateOutgoingLetterAttachment struct {
|
||||
FileURL string `json:"file_url" validate:"required"`
|
||||
FileName string `json:"file_name" validate:"required"`
|
||||
FileType string `json:"file_type" validate:"required"`
|
||||
IsFinal bool `json:"is_final" validate:"omitempty"`
|
||||
}
|
||||
|
||||
type CreateOutgoingLetterRequest struct {
|
||||
@ -80,7 +79,6 @@ type OutgoingLetterAttachmentResponse struct {
|
||||
FileName string `json:"file_name"`
|
||||
FileType string `json:"file_type"`
|
||||
UploadedAt time.Time `json:"uploaded_at"`
|
||||
IsFinal bool `json:"is_final"`
|
||||
}
|
||||
|
||||
type OutgoingLetterApprovalResponse struct {
|
||||
|
||||
@ -45,7 +45,7 @@ type LetterOutgoing struct {
|
||||
Attachments []LetterOutgoingAttachment `gorm:"foreignKey:LetterID" json:"attachments,omitempty"`
|
||||
Approvals []LetterOutgoingApproval `gorm:"foreignKey:LetterID" json:"approvals,omitempty"`
|
||||
Discussions []LetterOutgoingDiscussion `gorm:"foreignKey:LetterID" json:"discussions,omitempty"`
|
||||
ActivityLogs []LetterOutgoingActivityLog `gorm:"foreignKey:LetterID" json:"activity_logs,omitempty"`
|
||||
FinalAttachments []LetterOutgoingFinalAttachment `gorm:"foreignKey:LetterID" json:"final_attachments,omitempty"`
|
||||
}
|
||||
|
||||
func (LetterOutgoing) TableName() string { return "letters_outgoing" }
|
||||
@ -76,11 +76,22 @@ type LetterOutgoingAttachment struct {
|
||||
FileType string `gorm:"not null" json:"file_type"`
|
||||
UploadedBy *uuid.UUID `json:"uploaded_by,omitempty"`
|
||||
UploadedAt time.Time `gorm:"autoCreateTime" json:"uploaded_at"`
|
||||
IsFinal bool `gorm:"default:false" json:"is_final"`
|
||||
}
|
||||
|
||||
func (LetterOutgoingAttachment) TableName() string { return "letter_outgoing_attachments" }
|
||||
|
||||
type LetterOutgoingFinalAttachment struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
LetterID uuid.UUID `gorm:"type:uuid;not null" json:"letter_id"`
|
||||
FileURL string `gorm:"not null" json:"file_url"`
|
||||
FileName string `gorm:"not null" json:"file_name"`
|
||||
FileType string `gorm:"not null" json:"file_type"`
|
||||
UploadedBy *uuid.UUID `json:"uploaded_by,omitempty"`
|
||||
UploadedAt time.Time `gorm:"autoCreateTime" json:"uploaded_at"`
|
||||
}
|
||||
|
||||
func (LetterOutgoingFinalAttachment) TableName() string { return "letter_outgoing_final_attachments" }
|
||||
|
||||
type LetterOutgoingDiscussion struct {
|
||||
ID uuid.UUID `gorm:"type:uuid;primary_key;default:gen_random_uuid()" json:"id"`
|
||||
LetterID uuid.UUID `gorm:"type:uuid;not null" json:"letter_id"`
|
||||
|
||||
@ -15,6 +15,7 @@ import (
|
||||
type FileService interface {
|
||||
UploadProfileAvatar(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, error)
|
||||
UploadDocument(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, string, error)
|
||||
UploadDocumentFinal(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, string, error)
|
||||
}
|
||||
|
||||
type FileHandler struct {
|
||||
@ -76,3 +77,29 @@ func (h *FileHandler) UploadDocument(c *gin.Context) {
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{"url": url, "key": key}))
|
||||
}
|
||||
|
||||
func (h *FileHandler) UploadDocumentFinal(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
if appCtx.UserID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, &contract.ErrorResponse{Error: "Unauthorized", Code: http.StatusUnauthorized})
|
||||
return
|
||||
}
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "file is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
content, err := io.ReadAll(io.LimitReader(file, 20<<20))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "failed to read file", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
ct := header.Header.Get("Content-Type")
|
||||
url, key, err := h.service.UploadDocumentFinal(c.Request.Context(), appCtx.UserID, header.Filename, content, ct)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{"url": url, "key": key}))
|
||||
}
|
||||
|
||||
@ -35,6 +35,9 @@ type LetterOutgoingService interface {
|
||||
AddAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error
|
||||
RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error
|
||||
|
||||
AddFinalAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error
|
||||
RemoveFinalAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error
|
||||
|
||||
CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateDiscussionRequest) (*contract.DiscussionResponse, error)
|
||||
UpdateDiscussion(ctx context.Context, discussionID uuid.UUID, req *contract.UpdateDiscussionRequest) error
|
||||
DeleteDiscussion(ctx context.Context, discussionID uuid.UUID) error
|
||||
@ -407,6 +410,48 @@ func (h *LetterOutgoingHandler) RemoveAttachment(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "attachment removed"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) AddFinalAttachments(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.AddAttachmentsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.AddFinalAttachments(c.Request.Context(), id, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{Message: "attachment added"}))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) RemoveFinalAttachment(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
attachmentID, err := uuid.Parse(c.Param("attachment_id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid attachment id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.RemoveFinalAttachment(c.Request.Context(), id, attachmentID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "attachment removed"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) CreateDiscussion(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
|
||||
@ -40,6 +40,9 @@ type LetterOutgoingProcessor interface {
|
||||
AddAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingAttachment, userID uuid.UUID) error
|
||||
RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID, userID uuid.UUID) error
|
||||
|
||||
AddFinalAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingFinalAttachment, userID uuid.UUID) error
|
||||
RemoveFinalAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID, userID uuid.UUID) error
|
||||
|
||||
CreateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion, attachments []entities.LetterOutgoingDiscussionAttachment, userID uuid.UUID) error
|
||||
GetDiscussionByID(ctx context.Context, id uuid.UUID) (*entities.LetterOutgoingDiscussion, error)
|
||||
UpdateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion) error
|
||||
@ -68,6 +71,7 @@ type LetterOutgoingProcessorImpl struct {
|
||||
db *gorm.DB
|
||||
letterRepo *repository.LetterOutgoingRepository
|
||||
attachmentRepo *repository.LetterOutgoingAttachmentRepository
|
||||
finalAttachmentRepo *repository.LetterOutgoingFinalAttachmentRepository
|
||||
recipientRepo *repository.LetterOutgoingRecipientRepository
|
||||
discussionRepo *repository.LetterOutgoingDiscussionRepository
|
||||
discussionAttachmentRepo *repository.LetterOutgoingDiscussionAttachmentRepository
|
||||
@ -84,6 +88,7 @@ func NewLetterOutgoingProcessor(
|
||||
db *gorm.DB,
|
||||
letterRepo *repository.LetterOutgoingRepository,
|
||||
attachmentRepo *repository.LetterOutgoingAttachmentRepository,
|
||||
finalAttachmentRepo *repository.LetterOutgoingFinalAttachmentRepository,
|
||||
recipientRepo *repository.LetterOutgoingRecipientRepository,
|
||||
discussionRepo *repository.LetterOutgoingDiscussionRepository,
|
||||
discussionAttachmentRepo *repository.LetterOutgoingDiscussionAttachmentRepository,
|
||||
@ -99,6 +104,7 @@ func NewLetterOutgoingProcessor(
|
||||
db: db,
|
||||
letterRepo: letterRepo,
|
||||
attachmentRepo: attachmentRepo,
|
||||
finalAttachmentRepo: finalAttachmentRepo,
|
||||
recipientRepo: recipientRepo,
|
||||
discussionRepo: discussionRepo,
|
||||
discussionAttachmentRepo: discussionAttachmentRepo,
|
||||
@ -1128,6 +1134,51 @@ func (p *LetterOutgoingProcessorImpl) RemoveAttachment(ctx context.Context, lett
|
||||
})
|
||||
}
|
||||
|
||||
func (p *LetterOutgoingProcessorImpl) AddFinalAttachments(ctx context.Context, letterID uuid.UUID, attachments []entities.LetterOutgoingFinalAttachment, userID uuid.UUID) error {
|
||||
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
// Get the letter to get the current revision number
|
||||
_, err := p.letterRepo.Get(txCtx, letterID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.finalAttachmentRepo.CreateBulk(txCtx, attachments); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
activityLog := &entities.LetterOutgoingActivityLog{
|
||||
LetterID: letterID,
|
||||
ActionType: entities.LetterOutgoingActionAttachmentAdded,
|
||||
ActorUserID: &userID,
|
||||
}
|
||||
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (p *LetterOutgoingProcessorImpl) RemoveFinalAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID, userID uuid.UUID) error {
|
||||
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
if err := p.finalAttachmentRepo.Delete(txCtx, attachmentID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
activityLog := &entities.LetterOutgoingActivityLog{
|
||||
LetterID: letterID,
|
||||
ActionType: entities.LetterOutgoingActionAttachmentRemoved,
|
||||
ActorUserID: &userID,
|
||||
TargetID: &attachmentID,
|
||||
}
|
||||
if err := p.activityLogRepo.Create(txCtx, activityLog); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (p *LetterOutgoingProcessorImpl) CreateDiscussion(ctx context.Context, discussion *entities.LetterOutgoingDiscussion, attachments []entities.LetterOutgoingDiscussionAttachment, userID uuid.UUID) error {
|
||||
return p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
if err := p.discussionRepo.Create(txCtx, discussion); err != nil {
|
||||
|
||||
@ -534,6 +534,60 @@ func (r *LetterOutgoingAttachmentRepository) ListByLetterIDs(ctx context.Context
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type LetterOutgoingFinalAttachmentRepository struct{ db *gorm.DB }
|
||||
|
||||
func NewLetterOutgoingFinalAttachmentRepository(db *gorm.DB) *LetterOutgoingFinalAttachmentRepository {
|
||||
return &LetterOutgoingFinalAttachmentRepository{db: db}
|
||||
}
|
||||
|
||||
func (r *LetterOutgoingFinalAttachmentRepository) Create(ctx context.Context, e *entities.LetterOutgoingFinalAttachment) error {
|
||||
db := DBFromContext(ctx, r.db)
|
||||
return db.WithContext(ctx).Create(e).Error
|
||||
}
|
||||
|
||||
func (r *LetterOutgoingFinalAttachmentRepository) CreateBulk(ctx context.Context, list []entities.LetterOutgoingFinalAttachment) error {
|
||||
db := DBFromContext(ctx, r.db)
|
||||
if len(list) == 0 {
|
||||
return nil
|
||||
}
|
||||
return db.WithContext(ctx).Create(&list).Error
|
||||
}
|
||||
|
||||
func (r *LetterOutgoingFinalAttachmentRepository) ListByLetter(ctx context.Context, letterID uuid.UUID) ([]entities.LetterOutgoingFinalAttachment, error) {
|
||||
db := DBFromContext(ctx, r.db)
|
||||
var list []entities.LetterOutgoingFinalAttachment
|
||||
if err := db.WithContext(ctx).Where("letter_id = ?", letterID).Order("uploaded_at ASC").Find(&list).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (r *LetterOutgoingFinalAttachmentRepository) Delete(ctx context.Context, id uuid.UUID) error {
|
||||
db := DBFromContext(ctx, r.db)
|
||||
return db.WithContext(ctx).Where("id = ?", id).Delete(&entities.LetterOutgoingAttachment{}).Error
|
||||
}
|
||||
|
||||
// ListByLetterIDs fetches attachments for multiple letters in a single query
|
||||
func (r *LetterOutgoingFinalAttachmentRepository) ListByLetterIDs(ctx context.Context, letterIDs []uuid.UUID) (map[uuid.UUID][]entities.LetterOutgoingFinalAttachment, error) {
|
||||
if len(letterIDs) == 0 {
|
||||
return make(map[uuid.UUID][]entities.LetterOutgoingFinalAttachment), nil
|
||||
}
|
||||
|
||||
db := DBFromContext(ctx, r.db)
|
||||
var attachments []entities.LetterOutgoingFinalAttachment
|
||||
if err := db.WithContext(ctx).Where("letter_id IN ?", letterIDs).Order("uploaded_at ASC").Find(&attachments).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Group attachments by letter ID
|
||||
result := make(map[uuid.UUID][]entities.LetterOutgoingFinalAttachment)
|
||||
for _, att := range attachments {
|
||||
result[att.LetterID] = append(result[att.LetterID], att)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
type LetterOutgoingRecipientRepository struct{ db *gorm.DB }
|
||||
|
||||
func NewLetterOutgoingRecipientRepository(db *gorm.DB) *LetterOutgoingRecipientRepository {
|
||||
|
||||
@ -22,6 +22,7 @@ type UserHandler interface {
|
||||
type FileHandler interface {
|
||||
UploadProfileAvatar(c *gin.Context)
|
||||
UploadDocument(c *gin.Context)
|
||||
UploadDocumentFinal(c *gin.Context)
|
||||
}
|
||||
|
||||
type RBACHandler interface {
|
||||
@ -121,6 +122,9 @@ type LetterOutgoingHandler interface {
|
||||
AddAttachments(c *gin.Context)
|
||||
RemoveAttachment(c *gin.Context)
|
||||
|
||||
AddFinalAttachments(c *gin.Context)
|
||||
RemoveFinalAttachment(c *gin.Context)
|
||||
|
||||
CreateDiscussion(c *gin.Context)
|
||||
UpdateDiscussion(c *gin.Context)
|
||||
DeleteDiscussion(c *gin.Context)
|
||||
|
||||
@ -112,6 +112,7 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
||||
files.Use(r.authMiddleware.RequireAuth())
|
||||
{
|
||||
files.POST("/documents", r.fileHandler.UploadDocument)
|
||||
files.POST("/documents/final", r.fileHandler.UploadDocumentFinal)
|
||||
}
|
||||
|
||||
rbac := v1.Group("/rbac")
|
||||
@ -212,6 +213,9 @@ func (r *Router) addAppRoutes(rg *gin.Engine) {
|
||||
lettersch.POST("/outgoing/:id/attachments", r.letterOutgoingHandler.AddAttachments)
|
||||
lettersch.DELETE("/outgoing/:id/attachments/:attachment_id", r.letterOutgoingHandler.RemoveAttachment)
|
||||
|
||||
lettersch.POST("/outgoing/:id/attachments/final", r.letterOutgoingHandler.AddFinalAttachments)
|
||||
lettersch.DELETE("/outgoing/:id/attachments/final/:attachment_id", r.letterOutgoingHandler.RemoveFinalAttachment)
|
||||
|
||||
lettersch.POST("/outgoing/:id/discussions", r.letterOutgoingHandler.CreateDiscussion)
|
||||
lettersch.PUT("/outgoing/discussions/:discussion_id", r.letterOutgoingHandler.UpdateDiscussion)
|
||||
lettersch.DELETE("/outgoing/discussions/:discussion_id", r.letterOutgoingHandler.DeleteDiscussion)
|
||||
|
||||
@ -21,10 +21,11 @@ type FileServiceImpl struct {
|
||||
userProcessor UserProcessor
|
||||
profileBucket string
|
||||
docBucket string
|
||||
finalBucket string
|
||||
}
|
||||
|
||||
func NewFileService(storage FileStorage, userProcessor UserProcessor, profileBucket, docBucket string) *FileServiceImpl {
|
||||
return &FileServiceImpl{storage: storage, userProcessor: userProcessor, profileBucket: profileBucket, docBucket: docBucket}
|
||||
func NewFileService(storage FileStorage, userProcessor UserProcessor, profileBucket, docBucket string, finalBucket string) *FileServiceImpl {
|
||||
return &FileServiceImpl{storage: storage, userProcessor: userProcessor, profileBucket: profileBucket, docBucket: docBucket, finalBucket: finalBucket}
|
||||
}
|
||||
|
||||
func (s *FileServiceImpl) UploadProfileAvatar(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, error) {
|
||||
@ -61,6 +62,22 @@ func (s *FileServiceImpl) UploadDocument(ctx context.Context, userID uuid.UUID,
|
||||
return url, key, nil
|
||||
}
|
||||
|
||||
func (s *FileServiceImpl) UploadDocumentFinal(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, string, error) {
|
||||
if err := s.storage.EnsureBucket(ctx, s.docBucket); err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
ext := strings.ToLower(strings.TrimPrefix(filepath.Ext(filename), "."))
|
||||
if mimeExt := mimeExtFromContentType(contentType); mimeExt != "" {
|
||||
ext = mimeExt
|
||||
}
|
||||
key := buildObjectKey("finals", userID, ext)
|
||||
url, err := s.storage.Upload(ctx, s.docBucket, key, content, contentType)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return url, key, nil
|
||||
}
|
||||
|
||||
func buildObjectKey(prefix string, userID uuid.UUID, ext string) string {
|
||||
now := time.Now().UTC()
|
||||
parts := []string{
|
||||
|
||||
@ -39,6 +39,9 @@ type LetterOutgoingService interface {
|
||||
AddAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error
|
||||
RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error
|
||||
|
||||
AddFinalAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error
|
||||
RemoveFinalAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error
|
||||
|
||||
CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateDiscussionRequest) (*contract.DiscussionResponse, error)
|
||||
UpdateDiscussion(ctx context.Context, discussionID uuid.UUID, req *contract.UpdateDiscussionRequest) error
|
||||
DeleteDiscussion(ctx context.Context, discussionID uuid.UUID) error
|
||||
@ -915,7 +918,6 @@ func (s *LetterOutgoingServiceImpl) AddAttachments(ctx context.Context, letterID
|
||||
FileURL: a.FileURL,
|
||||
FileName: a.FileName,
|
||||
FileType: a.FileType,
|
||||
IsFinal: a.IsFinal,
|
||||
}
|
||||
}
|
||||
|
||||
@ -937,6 +939,46 @@ func (s *LetterOutgoingServiceImpl) RemoveAttachment(ctx context.Context, letter
|
||||
return s.processor.RemoveAttachment(ctx, letterID, attachmentID, userID)
|
||||
}
|
||||
|
||||
func (s *LetterOutgoingServiceImpl) AddFinalAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error {
|
||||
userID := getUserIDFromContext(ctx)
|
||||
|
||||
_, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//if letter.Status != entities.LetterOutgoingStatusDraft {
|
||||
// return gorm.ErrInvalidData
|
||||
//}
|
||||
|
||||
attachments := make([]entities.LetterOutgoingFinalAttachment, len(req.Attachments))
|
||||
for i, a := range req.Attachments {
|
||||
attachments[i] = entities.LetterOutgoingFinalAttachment{
|
||||
LetterID: letterID,
|
||||
FileURL: a.FileURL,
|
||||
FileName: a.FileName,
|
||||
FileType: a.FileType,
|
||||
}
|
||||
}
|
||||
|
||||
return s.processor.AddFinalAttachments(ctx, letterID, attachments, userID)
|
||||
}
|
||||
|
||||
func (s *LetterOutgoingServiceImpl) RemoveFinalAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error {
|
||||
userID := getUserIDFromContext(ctx)
|
||||
|
||||
letter, err := s.processor.GetOutgoingLetterByID(ctx, letterID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if letter.Status != entities.LetterOutgoingStatusDraft {
|
||||
return gorm.ErrInvalidData
|
||||
}
|
||||
|
||||
return s.processor.RemoveFinalAttachment(ctx, letterID, attachmentID, userID)
|
||||
}
|
||||
|
||||
func (s *LetterOutgoingServiceImpl) CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateDiscussionRequest) (*contract.DiscussionResponse, error) {
|
||||
userID := getUserIDFromContext(ctx)
|
||||
|
||||
@ -1589,7 +1631,6 @@ func transformLetterToResponse(letter *entities.LetterOutgoing) *contract.Outgoi
|
||||
FileName: attachment.FileName,
|
||||
FileType: attachment.FileType,
|
||||
UploadedAt: attachment.UploadedAt,
|
||||
IsFinal: attachment.IsFinal,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
migrations/000046_letter_outgoing_final_attachaments.up.sql
Normal file
12
migrations/000046_letter_outgoing_final_attachaments.up.sql
Normal file
@ -0,0 +1,12 @@
|
||||
ALTER TABLE letter_outgoing_attachments
|
||||
DROP COLUMN IF EXISTS is_final;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS letter_outgoing_final_attachments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
letter_id UUID NOT NULL REFERENCES letters_outgoing(id) ON DELETE CASCADE,
|
||||
file_url TEXT NOT NULL,
|
||||
file_name TEXT NOT NULL,
|
||||
file_type TEXT NOT NULL,
|
||||
uploaded_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
||||
uploaded_at TIMESTAMP WITHOUT TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
Loading…
x
Reference in New Issue
Block a user