dukcapil/internal/service/approval_flow_service.go
2025-10-10 19:02:52 +07:00

263 lines
6.9 KiB
Go

package service
import (
"context"
"eslogad-be/internal/contract"
"eslogad-be/internal/entities"
"eslogad-be/internal/repository"
"github.com/google/uuid"
"gorm.io/gorm"
)
type ApprovalFlowService interface {
CreateApprovalFlow(ctx context.Context, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error)
GetApprovalFlow(ctx context.Context, id uuid.UUID) (*contract.ApprovalFlowResponse, error)
GetApprovalFlowByDepartment(ctx context.Context, departmentID uuid.UUID) (*contract.ApprovalFlowResponse, error)
UpdateApprovalFlow(ctx context.Context, id uuid.UUID, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error)
DeleteApprovalFlow(ctx context.Context, id uuid.UUID) error
ListApprovalFlows(ctx context.Context, req *contract.ListApprovalFlowsRequest) (*contract.ListApprovalFlowsResponse, error)
}
type ApprovalFlowServiceImpl struct {
db *gorm.DB
flowRepo *repository.ApprovalFlowRepository
stepRepo *repository.ApprovalFlowStepRepository
txManager *repository.TxManager
}
func NewApprovalFlowService(
db *gorm.DB,
flowRepo *repository.ApprovalFlowRepository,
stepRepo *repository.ApprovalFlowStepRepository,
txManager *repository.TxManager,
) *ApprovalFlowServiceImpl {
return &ApprovalFlowServiceImpl{
db: db,
flowRepo: flowRepo,
stepRepo: stepRepo,
txManager: txManager,
}
}
func (s *ApprovalFlowServiceImpl) CreateApprovalFlow(ctx context.Context, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error) {
flow := &entities.ApprovalFlow{
DepartmentID: req.DepartmentID,
Name: req.Name,
Description: req.Description,
IsActive: req.IsActive,
}
err := s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
if err := s.flowRepo.Create(txCtx, flow); err != nil {
return err
}
if len(req.Steps) > 0 {
steps := make([]entities.ApprovalFlowStep, len(req.Steps))
for i, stepReq := range req.Steps {
if stepReq.ApproverRoleID == nil && stepReq.ApproverUserID == nil {
return gorm.ErrInvalidData
}
steps[i] = entities.ApprovalFlowStep{
FlowID: flow.ID,
StepOrder: stepReq.StepOrder,
ParallelGroup: stepReq.ParallelGroup,
ApproverRoleID: stepReq.ApproverRoleID,
ApproverUserID: stepReq.ApproverUserID,
Required: stepReq.Required,
}
}
if err := s.stepRepo.CreateBulk(txCtx, steps); err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
result, err := s.flowRepo.Get(ctx, flow.ID)
if err != nil {
return nil, err
}
return transformApprovalFlowToResponse(result), nil
}
func (s *ApprovalFlowServiceImpl) GetApprovalFlow(ctx context.Context, id uuid.UUID) (*contract.ApprovalFlowResponse, error) {
flow, err := s.flowRepo.Get(ctx, id)
if err != nil {
return nil, err
}
return transformApprovalFlowToResponse(flow), nil
}
func (s *ApprovalFlowServiceImpl) GetApprovalFlowByDepartment(ctx context.Context, departmentID uuid.UUID) (*contract.ApprovalFlowResponse, error) {
flow, err := s.flowRepo.GetByDepartment(ctx, departmentID)
if err != nil {
return nil, err
}
return transformApprovalFlowToResponse(flow), nil
}
func (s *ApprovalFlowServiceImpl) UpdateApprovalFlow(ctx context.Context, id uuid.UUID, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error) {
flow, err := s.flowRepo.Get(ctx, id)
if err != nil {
return nil, err
}
flow.DepartmentID = req.DepartmentID
flow.Name = req.Name
flow.Description = req.Description
flow.IsActive = req.IsActive
err = s.txManager.WithTransaction(ctx, func(txCtx context.Context) error {
if err := s.flowRepo.Update(txCtx, flow); err != nil {
return err
}
if err := s.stepRepo.DeleteByFlow(txCtx, id); err != nil {
return err
}
if len(req.Steps) > 0 {
steps := make([]entities.ApprovalFlowStep, len(req.Steps))
for i, stepReq := range req.Steps {
if stepReq.ApproverRoleID == nil && stepReq.ApproverUserID == nil {
return gorm.ErrInvalidData
}
steps[i] = entities.ApprovalFlowStep{
FlowID: flow.ID,
StepOrder: stepReq.StepOrder,
ParallelGroup: stepReq.ParallelGroup,
ApproverRoleID: stepReq.ApproverRoleID,
ApproverUserID: stepReq.ApproverUserID,
Required: stepReq.Required,
}
}
if err := s.stepRepo.CreateBulk(txCtx, steps); err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, err
}
result, err := s.flowRepo.Get(ctx, id)
if err != nil {
return nil, err
}
return transformApprovalFlowToResponse(result), nil
}
func (s *ApprovalFlowServiceImpl) DeleteApprovalFlow(ctx context.Context, id uuid.UUID) error {
return s.flowRepo.Delete(ctx, id)
}
func (s *ApprovalFlowServiceImpl) ListApprovalFlows(ctx context.Context, req *contract.ListApprovalFlowsRequest) (*contract.ListApprovalFlowsResponse, error) {
filter := repository.ListApprovalFlowsFilter{
DepartmentID: req.DepartmentID,
Search: req.Search,
IsActive: req.IsActive,
}
page := req.Page
if page <= 0 {
page = 1
}
limit := req.Limit
if limit <= 0 {
limit = 10
}
if limit > 100 {
limit = 100 // Max limit to prevent performance issues
}
offset := (page - 1) * limit
flows, total, err := s.flowRepo.List(ctx, filter, limit, offset)
if err != nil {
return nil, err
}
items := make([]*contract.ApprovalFlowResponse, len(flows))
for i, flow := range flows {
items[i] = transformApprovalFlowToResponse(&flow)
}
return &contract.ListApprovalFlowsResponse{
Items: items,
Total: total,
}, nil
}
func transformApprovalFlowToResponse(flow *entities.ApprovalFlow) *contract.ApprovalFlowResponse {
resp := &contract.ApprovalFlowResponse{
ID: flow.ID,
DepartmentID: flow.DepartmentID,
Name: flow.Name,
Description: flow.Description,
IsActive: flow.IsActive,
CreatedAt: flow.CreatedAt,
UpdatedAt: flow.UpdatedAt,
}
if flow.Department != nil {
resp.Department = &contract.DepartmentResponse{
ID: flow.Department.ID,
Name: flow.Department.Name,
}
}
if len(flow.Steps) > 0 {
resp.Steps = make([]contract.ApprovalFlowStepResponse, len(flow.Steps))
for i, step := range flow.Steps {
stepResp := contract.ApprovalFlowStepResponse{
ID: step.ID,
StepOrder: step.StepOrder,
ParallelGroup: step.ParallelGroup,
ApproverRoleID: step.ApproverRoleID,
ApproverUserID: step.ApproverUserID,
Required: step.Required,
CreatedAt: step.CreatedAt,
UpdatedAt: step.UpdatedAt,
}
if step.ApproverRole != nil {
stepResp.ApproverRole = &contract.RoleResponse{
ID: step.ApproverRole.ID,
Name: step.ApproverRole.Name,
}
}
if step.ApproverUser != nil {
stepResp.ApproverUser = &contract.UserResponse{
ID: step.ApproverUser.ID,
Name: step.ApproverUser.Name,
Email: step.ApproverUser.Email,
}
}
resp.Steps[i] = stepResp
}
}
return resp
}