Init
This commit is contained in:
@@ -2,6 +2,7 @@ package processor
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"eslogad-be/internal/appcontext"
|
||||
@@ -24,16 +25,37 @@ type LetterProcessorImpl struct {
|
||||
dispositionNoteRepo *repository.DispositionNoteRepository
|
||||
// discussion repo
|
||||
discussionRepo *repository.LetterDiscussionRepository
|
||||
// settings and recipients
|
||||
settingRepo *repository.AppSettingRepository
|
||||
recipientRepo *repository.LetterIncomingRecipientRepository
|
||||
departmentRepo *repository.DepartmentRepository
|
||||
userDeptRepo *repository.UserDepartmentRepository
|
||||
}
|
||||
|
||||
func NewLetterProcessor(letterRepo *repository.LetterIncomingRepository, attachRepo *repository.LetterIncomingAttachmentRepository, txManager *repository.TxManager, activity *ActivityLogProcessorImpl, dispRepo *repository.LetterDispositionRepository, dispSelRepo *repository.LetterDispositionActionSelectionRepository, noteRepo *repository.DispositionNoteRepository, discussionRepo *repository.LetterDiscussionRepository) *LetterProcessorImpl {
|
||||
return &LetterProcessorImpl{letterRepo: letterRepo, attachRepo: attachRepo, txManager: txManager, activity: activity, dispositionRepo: dispRepo, dispositionActionSelRepo: dispSelRepo, dispositionNoteRepo: noteRepo, discussionRepo: discussionRepo}
|
||||
func NewLetterProcessor(letterRepo *repository.LetterIncomingRepository, attachRepo *repository.LetterIncomingAttachmentRepository, txManager *repository.TxManager, activity *ActivityLogProcessorImpl, dispRepo *repository.LetterDispositionRepository, dispSelRepo *repository.LetterDispositionActionSelectionRepository, noteRepo *repository.DispositionNoteRepository, discussionRepo *repository.LetterDiscussionRepository, settingRepo *repository.AppSettingRepository, recipientRepo *repository.LetterIncomingRecipientRepository, departmentRepo *repository.DepartmentRepository, userDeptRepo *repository.UserDepartmentRepository) *LetterProcessorImpl {
|
||||
return &LetterProcessorImpl{letterRepo: letterRepo, attachRepo: attachRepo, txManager: txManager, activity: activity, dispositionRepo: dispRepo, dispositionActionSelRepo: dispSelRepo, dispositionNoteRepo: noteRepo, discussionRepo: discussionRepo, settingRepo: settingRepo, recipientRepo: recipientRepo, departmentRepo: departmentRepo, userDeptRepo: userDeptRepo}
|
||||
}
|
||||
|
||||
func (p *LetterProcessorImpl) CreateIncomingLetter(ctx context.Context, req *contract.CreateIncomingLetterRequest) (*contract.IncomingLetterResponse, error) {
|
||||
var result *contract.IncomingLetterResponse
|
||||
err := p.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
|
||||
userID := appcontext.FromGinContext(txCtx).UserID
|
||||
|
||||
prefix := "ESLI"
|
||||
seq := 0
|
||||
if s, err := p.settingRepo.Get(txCtx, contract.SettingIncomingLetterPrefix); err == nil {
|
||||
if v, ok := s.Value["value"].(string); ok && v != "" {
|
||||
prefix = v
|
||||
}
|
||||
}
|
||||
if s, err := p.settingRepo.Get(txCtx, contract.SettingIncomingLetterSequence); err == nil {
|
||||
if v, ok := s.Value["value"].(float64); ok {
|
||||
seq = int(v)
|
||||
}
|
||||
}
|
||||
seq = seq + 1
|
||||
letterNumber := fmt.Sprintf("%s%04d", prefix, seq)
|
||||
|
||||
entity := &entities.LetterIncoming{
|
||||
ReferenceNumber: req.ReferenceNumber,
|
||||
Subject: req.Subject,
|
||||
@@ -45,26 +67,63 @@ func (p *LetterProcessorImpl) CreateIncomingLetter(ctx context.Context, req *con
|
||||
Status: entities.LetterIncomingStatusNew,
|
||||
CreatedBy: userID,
|
||||
}
|
||||
entity.LetterNumber = letterNumber
|
||||
if err := p.letterRepo.Create(txCtx, entity); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = p.settingRepo.Upsert(txCtx, contract.SettingIncomingLetterSequence, entities.JSONB{"value": seq})
|
||||
|
||||
defaultDeptCodes := []string{}
|
||||
if s, err := p.settingRepo.Get(txCtx, contract.SettingIncomingLetterRecipients); err == nil {
|
||||
if arr, ok := s.Value["department_codes"].([]interface{}); ok {
|
||||
for _, it := range arr {
|
||||
if str, ok := it.(string); ok {
|
||||
defaultDeptCodes = append(defaultDeptCodes, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// resolve department codes to ids using repository
|
||||
depIDs := make([]uuid.UUID, 0, len(defaultDeptCodes))
|
||||
for _, code := range defaultDeptCodes {
|
||||
dep, err := p.departmentRepo.GetByCode(txCtx, code)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
depIDs = append(depIDs, dep.ID)
|
||||
}
|
||||
// query user memberships for all departments at once
|
||||
userMemberships, _ := p.userDeptRepo.ListActiveByDepartmentIDs(txCtx, depIDs)
|
||||
// build recipients: one department recipient per department + one user recipient per membership
|
||||
recipients := make([]entities.LetterIncomingRecipient, 0, len(depIDs)+len(userMemberships))
|
||||
// department recipients
|
||||
for _, depID := range depIDs {
|
||||
id := depID
|
||||
recipients = append(recipients, entities.LetterIncomingRecipient{LetterID: entity.ID, RecipientDepartmentID: &id, Status: entities.RecipientStatusNew})
|
||||
}
|
||||
// user recipients
|
||||
for _, row := range userMemberships {
|
||||
uid := row.UserID
|
||||
recipients = append(recipients, entities.LetterIncomingRecipient{LetterID: entity.ID, RecipientUserID: &uid, Status: entities.RecipientStatusNew})
|
||||
}
|
||||
if len(recipients) > 0 {
|
||||
if err := p.recipientRepo.CreateBulk(txCtx, recipients); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if p.activity != nil {
|
||||
action := "letter.created"
|
||||
if err := p.activity.Log(txCtx, entity.ID, action, &userID, nil, nil, nil, nil, nil, map[string]interface{}{}); err != nil {
|
||||
if err := p.activity.Log(txCtx, entity.ID, action, &userID, nil, nil, nil, nil, nil, map[string]interface{}{"letter_number": letterNumber}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
attachments := make([]entities.LetterIncomingAttachment, 0, len(req.Attachments))
|
||||
for _, a := range req.Attachments {
|
||||
attachments = append(attachments, entities.LetterIncomingAttachment{
|
||||
LetterID: entity.ID,
|
||||
FileURL: a.FileURL,
|
||||
FileName: a.FileName,
|
||||
FileType: a.FileType,
|
||||
UploadedBy: &userID,
|
||||
})
|
||||
attachments = append(attachments, entities.LetterIncomingAttachment{LetterID: entity.ID, FileURL: a.FileURL, FileName: a.FileName, FileType: a.FileType, UploadedBy: &userID})
|
||||
}
|
||||
if len(attachments) > 0 {
|
||||
if err := p.attachRepo.CreateBulk(txCtx, attachments); err != nil {
|
||||
@@ -85,7 +144,6 @@ func (p *LetterProcessorImpl) CreateIncomingLetter(ctx context.Context, req *con
|
||||
result = transformer.LetterEntityToContract(entity, savedAttachments)
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -267,7 +325,7 @@ func (p *LetterProcessorImpl) CreateDiscussion(ctx context.Context, letterID uui
|
||||
return err
|
||||
}
|
||||
if p.activity != nil {
|
||||
action := "discussion.created"
|
||||
action := "reference_numberdiscussion.created"
|
||||
tgt := "discussion"
|
||||
ctxMap := map[string]interface{}{"message": req.Message, "parent_id": req.ParentID}
|
||||
if err := p.activity.Log(txCtx, letterID, action, &userID, nil, &tgt, &disc.ID, nil, nil, ctxMap); err != nil {
|
||||
|
||||
@@ -110,8 +110,13 @@ func (p *UserProcessorImpl) GetUserByID(ctx context.Context, id uuid.UUID) (*con
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("user not found: %w", err)
|
||||
}
|
||||
|
||||
return transformer.EntityToContract(user), nil
|
||||
resp := transformer.EntityToContract(user)
|
||||
if resp != nil {
|
||||
if roles, err := p.userRepo.GetRolesByUserID(ctx, resp.ID); err == nil {
|
||||
resp.Roles = transformer.RolesToContract(roles)
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) GetUserByEmail(ctx context.Context, email string) (*contract.UserResponse, error) {
|
||||
@@ -123,17 +128,35 @@ func (p *UserProcessorImpl) GetUserByEmail(ctx context.Context, email string) (*
|
||||
return transformer.EntityToContract(user), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) ListUsers(ctx context.Context, page, limit int) ([]contract.UserResponse, int, error) {
|
||||
func (p *UserProcessorImpl) ListUsersWithFilters(ctx context.Context, req *contract.ListUsersRequest) ([]contract.UserResponse, int, error) {
|
||||
page := req.Page
|
||||
if page <= 0 {
|
||||
page = 1
|
||||
}
|
||||
limit := req.Limit
|
||||
if limit <= 0 {
|
||||
limit = 10
|
||||
}
|
||||
offset := (page - 1) * limit
|
||||
|
||||
filters := map[string]interface{}{}
|
||||
|
||||
users, totalCount, err := p.userRepo.List(ctx, filters, limit, offset)
|
||||
users, totalCount, err := p.userRepo.ListWithFilters(ctx, req.Search, req.RoleCode, req.IsActive, limit, offset)
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("failed to get users: %w", err)
|
||||
}
|
||||
|
||||
responses := transformer.EntitiesToContracts(users)
|
||||
userIDs := make([]uuid.UUID, 0, len(responses))
|
||||
for i := range responses {
|
||||
userIDs = append(userIDs, responses[i].ID)
|
||||
}
|
||||
rolesMap, err := p.userRepo.GetRolesByUserIDs(ctx, userIDs)
|
||||
if err == nil {
|
||||
for i := range responses {
|
||||
if roles, ok := rolesMap[responses[i].ID]; ok {
|
||||
responses[i].Roles = transformer.RolesToContract(roles)
|
||||
}
|
||||
}
|
||||
}
|
||||
return responses, int(totalCount), nil
|
||||
}
|
||||
|
||||
@@ -219,12 +242,12 @@ func (p *UserProcessorImpl) GetUserPermissionCodes(ctx context.Context, userID u
|
||||
return codes, nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) GetUserPositions(ctx context.Context, userID uuid.UUID) ([]contract.PositionResponse, error) {
|
||||
positions, err := p.userRepo.GetPositionsByUserID(ctx, userID)
|
||||
func (p *UserProcessorImpl) GetUserDepartments(ctx context.Context, userID uuid.UUID) ([]contract.DepartmentResponse, error) {
|
||||
departments, err := p.userRepo.GetDepartmentsByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transformer.PositionsToContract(positions), nil
|
||||
return transformer.DepartmentsToContract(departments), nil
|
||||
}
|
||||
|
||||
func (p *UserProcessorImpl) GetUserProfile(ctx context.Context, userID uuid.UUID) (*contract.UserProfileResponse, error) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package processor
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/entities"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
@@ -21,5 +22,9 @@ type UserRepository interface {
|
||||
|
||||
GetRolesByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Role, error)
|
||||
GetPermissionsByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Permission, error)
|
||||
GetPositionsByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Position, error)
|
||||
GetDepartmentsByUserID(ctx context.Context, userID uuid.UUID) ([]entities.Department, error)
|
||||
|
||||
// New optimized helpers
|
||||
GetRolesByUserIDs(ctx context.Context, userIDs []uuid.UUID) (map[uuid.UUID][]entities.Role, error)
|
||||
ListWithFilters(ctx context.Context, search *string, roleCode *string, isActive *bool, limit, offset int) ([]*entities.User, int64, error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user