Compare commits

..

No commits in common. "dev-1" and "main" have entirely different histories.
dev-1 ... main

317 changed files with 647561 additions and 40731 deletions

View File

@ -1,43 +0,0 @@
kind: pipeline
type: ssh
name: medol-be-build-deploy
server:
host:
from_secret: ssh_host
user:
from_secret: ssh_user
ssh_key:
from_secret: ssh_key
steps:
- name: prepare repo
when:
branch:
- dev-1
commands:
- rm -rf /opt/build/medol-be
- mkdir -p /opt/build/medol-be
- cd /opt/build/medol-be
- git clone http://38.47.180.165:3000/medol/medol-be.git
- name: build image
when:
branch:
- dev-1
commands:
- docker login 38.47.180.165:3000 -u administrator -p HarborDockerImageRep0
- cd /opt/build/medol-be/medol-be
- docker build -t 38.47.180.165:3000/medol/medol-be:main .
- docker push 38.47.180.165:3000/medol/medol-be:main
- name: deploy
when:
branch:
- dev-1
commands:
- docker login 38.47.180.165:3000 -u administrator -p HarborDockerImageRep0
- docker pull 38.47.180.165:3000/medol/medol-be:main
- docker stop medol-be || true
- docker rm medol-be || true
- docker run -dt -p 8804:8800 --name medol-be 38.47.180.165:3000/medol/medol-be:main

2
.gitignore vendored
View File

@ -1,3 +1,3 @@
/vendor /vendor
debug.log debug.log
/.ideadebug.log /.idea

View File

@ -2,27 +2,32 @@ stages:
- build-image - build-image
- deploy - deploy
#build-1:
# stage: build-app
# image: golang:alpine
# script:
# - go build -o main .
# artifacts:
# paths:
# - main
build-2: build-2:
stage: build-image stage: build-image
image: docker:24 image: docker/compose:latest
services: services:
- name: docker:24-dind - name: docker:dind
command: ["--insecure-registry=38.47.185.86:8900"] command: [ "--insecure-registry=103.82.242.92:8900" ]
variables:
DOCKER_HOST: tcp://docker:2375
DOCKER_TLS_CERTDIR: ""
script: script:
- docker version - docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 103.82.242.92:8900
- docker login -u $DEPLOY_USERNAME -p $DEPLOY_TOKEN 38.47.185.86:8900 - docker-compose build
- docker build -t registry.gitlab.com/hanifsalafi/web-medols-be:dev . - docker tag registry.gitlab.com/hanifsalafi/web-humas-be:dev 103.82.242.92:8900/humas/web-humas-be:dev
- docker tag registry.gitlab.com/hanifsalafi/web-medols-be:dev 38.47.185.86:8900/medols/web-medols-be:dev - docker push 103.82.242.92:8900/humas/web-humas-be:dev
- docker push 38.47.185.86:8900/medols/web-medols-be:dev
deploy: deploy:
stage: deploy stage: deploy
when: on_success when: on_success
image: curlimages/curl:latest image: curlimages/curl:latest
services:
- docker:dind
script: script:
- curl --user $JENKINS_USER:$JENKINS_PWD "http://38.47.185.86:8080/job/autodeploy-medols-be/build?token=autodeploymedols" - curl --user $JENKINS_USER:$JENKINS_PWD http://38.47.180.165:8080/job/autodeploy-humas-be/build?token=autodeployhumas

8
.idea/.gitignore vendored
View File

@ -1,8 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

View File

@ -2,7 +2,7 @@
<project version="4"> <project version="4">
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/.idea/web-medols-be.iml" filepath="$PROJECT_DIR$/.idea/web-medols-be.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/go-humas-be.iml" filepath="$PROJECT_DIR$/.idea/go-humas-be.iml" />
</modules> </modules>
</component> </component>
</project> </project>

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

View File

@ -1,18 +1,13 @@
package entity package entity
import ( import "time"
"time"
"github.com/google/uuid"
)
type ActivityLogs struct { type ActivityLogs struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
ActivityTypeId int `json:"activity_type_id" gorm:"type:int4"` ActivityTypeId int `json:"activity_type_id" gorm:"type:int4"`
Url string `json:"url" gorm:"type:varchar"` Url string `json:"url" gorm:"type:varchar"`
VisitorIp *string `json:"visitor_ip" gorm:"type:varchar"` VisitorIp *string `json:"visitor_ip" gorm:"type:varchar"`
ArticleId *uint `json:"article_id" gorm:"type:int4"` ArticleId *uint `json:"article_id" gorm:"type:int4"`
UserId *uint `json:"user_id" gorm:"type:int4"` UserId *uint `json:"user_id" gorm:"type:int4"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
} }

View File

@ -1,22 +1,18 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type Advertisement struct { type Advertisement struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Title string `json:"title" gorm:"type:varchar"` Title string `json:"title" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"` Description string `json:"description" gorm:"type:varchar"`
RedirectLink string `json:"redirect_link" gorm:"type:varchar"` RedirectLink string `json:"redirect_link" gorm:"type:varchar"`
ContentFilePath *string `json:"content_file_path" gorm:"type:varchar"` ContentFilePath *string `json:"content_file_path" gorm:"type:varchar"`
ContentFileName *string `json:"content_file_name" gorm:"type:varchar"` ContentFileName *string `json:"content_file_name" gorm:"type:varchar"`
Placement string `json:"placement" gorm:"type:varchar"` Placement string `json:"placement" gorm:"type:varchar"`
StatusId int `json:"status_id" gorm:"type:int4"` StatusId int `json:"status_id" gorm:"type:int4"`
IsPublish bool `json:"is_publish" gorm:"type:bool"` IsPublish bool `json:"is_publish" gorm:"type:bool"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` IsActive bool `json:"is_active" gorm:"type:bool;default:true"`
IsActive bool `json:"is_active" gorm:"type:bool;default:true"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,24 +0,0 @@
package entity
import (
"github.com/google/uuid"
"time"
)
type ApprovalWorkflowSteps struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
WorkflowId uint `json:"workflow_id" gorm:"type:int4;not null"`
StepOrder int `json:"step_order" gorm:"type:int4;not null"`
StepName string `json:"step_name" gorm:"type:varchar;not null"`
RequiredUserLevelId uint `json:"required_user_level_id" gorm:"type:int4;not null"`
CanSkip *bool `json:"can_skip" gorm:"type:bool;default:false"`
AutoApproveAfterHours *int `json:"auto_approve_after_hours" gorm:"type:int4"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
// Relations
Workflow ApprovalWorkflows `json:"workflow" gorm:"foreignKey:WorkflowId;constraint:OnDelete:CASCADE"`
RequiredUserLevel UserLevels `json:"required_user_level" gorm:"foreignKey:RequiredUserLevelId"`
}

View File

@ -1,23 +0,0 @@
package entity
import (
"github.com/google/uuid"
"time"
)
type ApprovalWorkflows struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Name string `json:"name" gorm:"type:varchar;not null"`
Description *string `json:"description" gorm:"type:text"`
IsDefault *bool `json:"is_default" gorm:"type:bool;default:false"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
// New fields for no-approval support
RequiresApproval *bool `json:"requires_approval" gorm:"type:bool;default:true"` // false = no approval needed
AutoPublish *bool `json:"auto_publish" gorm:"type:bool;default:false"` // true = auto publish after creation
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
// Relations
Steps []ApprovalWorkflowSteps `json:"steps" gorm:"foreignKey:WorkflowId;constraint:OnDelete:CASCADE"`
}

View File

@ -1,29 +0,0 @@
package entity
import (
"github.com/google/uuid"
"time"
)
type ArticleApprovalFlows struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
ArticleId uint `json:"article_id" gorm:"type:int4;not null"`
WorkflowId uint `json:"workflow_id" gorm:"type:int4;not null"`
CurrentStep int `json:"current_step" gorm:"type:int4;default:1"`
StatusId int `json:"status_id" gorm:"type:int4;default:1"` // 1=pending, 2=approved, 3=rejected, 4=revision_requested
SubmittedById uint `json:"submitted_by_id" gorm:"type:int4;not null"`
SubmittedAt time.Time `json:"submitted_at" gorm:"default:now()"`
CompletedAt *time.Time `json:"completed_at" gorm:"type:timestamp"`
RejectionReason *string `json:"rejection_reason" gorm:"type:text"`
RevisionRequested *bool `json:"revision_requested" gorm:"type:bool;default:false"`
RevisionMessage *string `json:"revision_message" gorm:"type:text"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
// Relations
Article Articles `json:"article" gorm:"foreignKey:ArticleId;constraint:OnDelete:CASCADE"`
Workflow ApprovalWorkflows `json:"workflow" gorm:"foreignKey:WorkflowId"`
SubmittedBy *Users `json:"submitted_by" gorm:"foreignKey:SubmittedById"`
StepLogs []ArticleApprovalStepLogs `json:"step_logs" gorm:"foreignKey:ApprovalFlowId;constraint:OnDelete:CASCADE"`
}

View File

@ -1,25 +0,0 @@
package entity
import (
"github.com/google/uuid"
"time"
)
type ArticleApprovalStepLogs struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
ApprovalFlowId uint `json:"approval_flow_id" gorm:"type:int4;not null"`
StepOrder int `json:"step_order" gorm:"type:int4;not null"`
StepName string `json:"step_name" gorm:"type:varchar;not null"`
ApprovedById *uint `json:"approved_by_id" gorm:"type:int4"`
Action string `json:"action" gorm:"type:varchar;not null"` // approve, reject, request_revision
Message *string `json:"message" gorm:"type:text"`
ProcessedAt time.Time `json:"processed_at" gorm:"default:now()"`
UserLevelId uint `json:"user_level_id" gorm:"type:int4;not null"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
// Relations
ApprovalFlow ArticleApprovalFlows `json:"approval_flow" gorm:"foreignKey:ApprovalFlowId;constraint:OnDelete:CASCADE"`
ApprovedBy Users `json:"approved_by" gorm:"foreignKey:ApprovedById"`
UserLevel UserLevels `json:"user_level" gorm:"foreignKey:UserLevelId"`
}

View File

@ -1,18 +1,13 @@
package entity package entity
import ( import "time"
"time"
"github.com/google/uuid"
)
type ArticleApprovals struct { type ArticleApprovals struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
ArticleId uint `json:"article_id" gorm:"type:int4"` ArticleId uint `json:"article_id" gorm:"type:int4"`
ApprovalBy uint `json:"approval_by" gorm:"type:int4"` ApprovalBy uint `json:"approval_by" gorm:"type:int4"`
StatusId int `json:"status_id" gorm:"type:int4"` StatusId int `json:"status_id" gorm:"type:int4"`
Message string `json:"message" gorm:"type:varchar"` Message string `json:"message" gorm:"type:varchar"`
ApprovalAtLevel *int `json:"approval_at_level" gorm:"type:int4"` ApprovalAtLevel *int `json:"approval_at_level" gorm:"type:int4"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
} }

View File

@ -1,9 +1,6 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type ArticleCategories struct { type ArticleCategories struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
@ -19,7 +16,6 @@ type ArticleCategories struct {
StatusId int `json:"status_id" gorm:"type:int4;default:1"` StatusId int `json:"status_id" gorm:"type:int4;default:1"`
IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"` IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"`
PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`

View File

@ -1,10 +1,8 @@
package article_category_details package article_category_details
import ( import (
entity "go-humas-be/app/database/entity"
"time" "time"
entity "web-medols-be/app/database/entity"
"github.com/google/uuid"
) )
type ArticleCategoryDetails struct { type ArticleCategoryDetails struct {
@ -12,11 +10,7 @@ type ArticleCategoryDetails struct {
ArticleId uint `json:"article_id" gorm:"type:int4"` ArticleId uint `json:"article_id" gorm:"type:int4"`
CategoryId int `json:"category_id" gorm:"type:int4"` CategoryId int `json:"category_id" gorm:"type:int4"`
Category *entity.ArticleCategories `json:"category" gorm:"foreignKey:CategoryId;references:ID"` Category *entity.ArticleCategories `json:"category" gorm:"foreignKey:CategoryId;references:ID"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive bool `json:"is_active" gorm:"type:bool"` IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
// Relations
Article entity.Articles `json:"article" gorm:"foreignKey:ArticleId;constraint:OnDelete:CASCADE"`
} }

View File

@ -1,10 +1,6 @@
package entity package entity
import ( import "time"
"time"
"github.com/google/uuid"
)
type ArticleComments struct { type ArticleComments struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
@ -15,13 +11,9 @@ type ArticleComments struct {
IsPublic bool `json:"is_public" gorm:"type:bool;default:false"` IsPublic bool `json:"is_public" gorm:"type:bool;default:false"`
StatusId int `json:"status_id" gorm:"type:int4;default:0"` StatusId int `json:"status_id" gorm:"type:int4;default:0"`
ApprovedAt *time.Time `json:"approved_at" gorm:"type:timestamp"` ApprovedAt *time.Time `json:"approved_at" gorm:"type:timestamp"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive bool `json:"is_active" gorm:"type:bool"` IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
// Relations
Article Articles `json:"article" gorm:"foreignKey:ArticleId"`
} }
// statusId => 0: waiting, 1: accepted, 2: replied, 3: rejected // statusId => 0: waiting, 1: accepted, 2: replied, 3: rejected

View File

@ -1,10 +1,6 @@
package entity package entity
import ( import "time"
"time"
"github.com/google/uuid"
)
type ArticleFiles struct { type ArticleFiles struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
@ -23,11 +19,7 @@ type ArticleFiles struct {
StatusId int `json:"status_id" gorm:"type:int4"` StatusId int `json:"status_id" gorm:"type:int4"`
IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"` IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"`
PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive bool `json:"is_active" gorm:"type:bool;default:true"` IsActive bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
// Relations
Article Articles `json:"article" gorm:"foreignKey:ArticleId"`
} }

View File

@ -1,47 +1,35 @@
package entity package entity
import ( import "time"
"time"
"github.com/google/uuid"
)
type Articles struct { type Articles struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Title string `json:"title" gorm:"type:varchar"` Title string `json:"title" gorm:"type:varchar"`
Slug string `json:"slug" gorm:"type:varchar"` Slug string `json:"slug" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"` Description string `json:"description" gorm:"type:varchar"`
CategoryId int `json:"category_id" gorm:"type:int4"` CategoryId int `json:"category_id" gorm:"type:int4"`
HtmlDescription string `json:"html_description" gorm:"type:varchar"` HtmlDescription string `json:"html_description" gorm:"type:varchar"`
TypeId int `json:"type_id" gorm:"type:int4"` TypeId int `json:"type_id" gorm:"type:int4"`
Tags string `json:"tags" gorm:"type:varchar"` Tags string `json:"tags" gorm:"type:varchar"`
ThumbnailName *string `json:"thumbnail_name" gorm:"type:varchar"` ThumbnailName *string `json:"thumbnail_name" gorm:"type:varchar"`
ThumbnailPath *string `json:"thumbnail_path" gorm:"type:varchar"` ThumbnailPath *string `json:"thumbnail_path" gorm:"type:varchar"`
PageUrl *string `json:"page_url" gorm:"type:varchar"` PageUrl *string `json:"page_url" gorm:"type:varchar"`
CreatedById *uint `json:"created_by_id" gorm:"type:int4"` CreatedById *uint `json:"created_by_id" gorm:"type:int4"`
AiArticleId *int `json:"ai_article_id" gorm:"type:int4"` AiArticleId *int `json:"ai_article_id" gorm:"type:int4"`
CommentCount *int `json:"comment_count" gorm:"type:int4;default:0"` CommentCount *int `json:"comment_count" gorm:"type:int4;default:0"`
ShareCount *int `json:"share_count" gorm:"type:int4;default:0"` ShareCount *int `json:"share_count" gorm:"type:int4;default:0"`
ViewCount *int `json:"view_count" gorm:"type:int4;default:0"` ViewCount *int `json:"view_count" gorm:"type:int4;default:0"`
StatusId *int `json:"status_id" gorm:"type:int4"` StatusId *int `json:"status_id" gorm:"type:int4"`
OldId *uint `json:"old_id" gorm:"type:int4"` OldId *uint `json:"old_id" gorm:"type:int4"`
NeedApprovalFrom *int `json:"need_approval_from" gorm:"type:int4"` NeedApprovalFrom *int `json:"need_approval_from" gorm:"type:int4"`
HasApprovedBy *string `json:"has_approved_by" gorm:"type:varchar"` HasApprovedBy *string `json:"has_approved_by" gorm:"type:varchar"`
WorkflowId *uint `json:"workflow_id" gorm:"type:int4"` IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"`
CurrentApprovalStep *int `json:"current_approval_step" gorm:"type:int4;default:0"` // 0=not submitted, 1+=approval step IsBanner *bool `json:"is_banner" gorm:"type:bool;default:false"`
// New fields for no-approval support PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"`
BypassApproval *bool `json:"bypass_approval" gorm:"type:bool;default:false"` // true = skip approval process IsDraft *bool `json:"is_draft" gorm:"type:bool;default:false"`
ApprovalExempt *bool `json:"approval_exempt" gorm:"type:bool;default:false"` // true = permanently exempt from approval DraftedAt *time.Time `json:"drafted_at" gorm:"type:timestamp"`
IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"` PublishSchedule *string `json:"publish_schedule" gorm:"type:varchar"`
IsBanner *bool `json:"is_banner" gorm:"type:bool;default:false"` IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
IsDraft *bool `json:"is_draft" gorm:"type:bool;default:false"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
DraftedAt *time.Time `json:"drafted_at" gorm:"type:timestamp"`
PublishSchedule *string `json:"publish_schedule" gorm:"type:varchar"`
Source *string `json:"source" gorm:"type:varchar"`
CustomCreatorName *string `json:"custom_creator_name" gorm:"type:varchar"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,9 +1,6 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type AuditTrails struct { type AuditTrails struct {
ID uint `gorm:"primaryKey"` ID uint `gorm:"primaryKey"`
@ -16,6 +13,5 @@ type AuditTrails struct {
RequestBody string RequestBody string
ResponseBody string ResponseBody string
DurationMs int64 DurationMs int64
ClientId *uuid.UUID
CreatedAt time.Time CreatedAt time.Time
} }

View File

@ -1,20 +0,0 @@
package entity
import (
"time"
"github.com/google/uuid"
)
type Bookmarks struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
UserId uint `json:"user_id" gorm:"type:int4"`
ArticleId uint `json:"article_id" gorm:"type:int4"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
// Relations
Article Articles `json:"article" gorm:"foreignKey:ArticleId"`
}

View File

@ -1,57 +0,0 @@
package entity
import (
"database/sql/driver"
"encoding/json"
"time"
"github.com/google/uuid"
)
// StringArray is a custom type for handling string arrays with JSON serialization
type StringArray []string
// Scan implements the sql.Scanner interface
func (s *StringArray) Scan(value interface{}) error {
if value == nil {
*s = StringArray{}
return nil
}
switch v := value.(type) {
case []byte:
return json.Unmarshal(v, s)
case string:
return json.Unmarshal([]byte(v), s)
default:
return nil
}
}
// Value implements the driver.Valuer interface
func (s StringArray) Value() (driver.Value, error) {
if s == nil || len(s) == 0 {
return "[]", nil
}
return json.Marshal(s)
}
type ClientApprovalSettings struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
ClientId uuid.UUID `json:"client_id" gorm:"type:UUID;not null;uniqueIndex"`
RequiresApproval *bool `json:"requires_approval" gorm:"type:bool;default:true"` // false = no approval needed
DefaultWorkflowId *uint `json:"default_workflow_id" gorm:"type:int4"` // default workflow for this client
AutoPublishArticles *bool `json:"auto_publish_articles" gorm:"type:bool;default:false"` // auto publish after creation
ApprovalExemptUsers []uint `json:"approval_exempt_users" gorm:"type:int4[]"` // user IDs exempt from approval
ApprovalExemptRoles []uint `json:"approval_exempt_roles" gorm:"type:int4[]"` // role IDs exempt from approval
ApprovalExemptCategories []uint `json:"approval_exempt_categories" gorm:"type:int4[]"` // category IDs exempt from approval
RequireApprovalFor []string `json:"require_approval_for" gorm:"type:jsonb"` // specific content types that need approval
SkipApprovalFor []string `json:"skip_approval_for" gorm:"type:jsonb"` // specific content types that skip approval
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
// Relations
Client Clients `json:"client" gorm:"foreignKey:ClientId;constraint:OnDelete:CASCADE"`
Workflow *ApprovalWorkflows `json:"workflow" gorm:"foreignKey:DefaultWorkflowId"`
}

View File

@ -1,15 +0,0 @@
package entity
import (
"github.com/google/uuid"
"time"
)
type Clients struct {
ID uuid.UUID `json:"id" gorm:"primaryKey;type:UUID"`
Name string `json:"name" gorm:"type:varchar"`
CreatedById *uint `json:"created_by_id" gorm:"type:int4"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
}

View File

@ -1,15 +1,11 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type CsrfTokenRecords struct { type CsrfTokenRecords struct {
ID uint `gorm:"primaryKey"` ID uint `gorm:"primaryKey"`
Token string `gorm:"uniqueIndex;size:255"` Token string `gorm:"uniqueIndex;size:255"`
Value []byte `gorm:"value"` Value []byte `gorm:"value"`
ExpireAt time.Time `gorm:"index"` ExpireAt time.Time `gorm:"index"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
CreatedAt time.Time CreatedAt time.Time
} }

View File

@ -1,18 +1,14 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type CustomStaticPages struct { type CustomStaticPages struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Title string `json:"title" gorm:"type:varchar"` Title string `json:"title" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"` Description string `json:"description" gorm:"type:varchar"`
Slug string `json:"slug" gorm:"type:varchar"` Slug string `json:"slug" gorm:"type:varchar"`
HtmlBody string `json:"html_body" gorm:"type:text"` HtmlBody string `json:"html_body" gorm:"type:text"`
IsActive bool `json:"is_active" gorm:"type:bool"` IsActive bool `json:"is_active" gorm:"type:bool"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,9 +1,6 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type Feedbacks struct { type Feedbacks struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
@ -13,7 +10,6 @@ type Feedbacks struct {
StatusId int `json:"status_id" gorm:"type:int4;default:0"` StatusId int `json:"status_id" gorm:"type:int4;default:0"`
ApprovedAt *time.Time `json:"approved_at" gorm:"type:timestamp"` ApprovedAt *time.Time `json:"approved_at" gorm:"type:timestamp"`
ReplyMessage *string `json:"reply_message" gorm:"type:varchar"` ReplyMessage *string `json:"reply_message" gorm:"type:varchar"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive bool `json:"is_active" gorm:"type:bool;default:true"` IsActive bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`

View File

@ -1,16 +1,12 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type ForgotPasswords struct { type ForgotPasswords struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
KeycloakID string `json:"keycloak_id" gorm:"type:varchar"` KeycloakID string `json:"keycloak_id" gorm:"type:varchar"`
CodeRequest string `json:"code_request" gorm:"type:varchar"` CodeRequest string `json:"code_request" gorm:"type:varchar"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` IsActive bool `json:"is_active" gorm:"type:bool"`
IsActive bool `json:"is_active" gorm:"type:bool"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,9 +1,6 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type MagazineFiles struct { type MagazineFiles struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
@ -21,7 +18,6 @@ type MagazineFiles struct {
HeightPixel *string `json:"height_pixel" gorm:"type:varchar"` HeightPixel *string `json:"height_pixel" gorm:"type:varchar"`
Size *string `json:"size" gorm:"type:varchar"` Size *string `json:"size" gorm:"type:varchar"`
PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive bool `json:"is_active" gorm:"type:bool"` IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`

View File

@ -1,9 +1,6 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type Magazines struct { type Magazines struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
@ -17,7 +14,6 @@ type Magazines struct {
StatusId int `json:"status_id" gorm:"type:int4"` StatusId int `json:"status_id" gorm:"type:int4"`
IsPublish *bool `json:"is_publish" gorm:"type:bool"` IsPublish *bool `json:"is_publish" gorm:"type:bool"`
PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"` PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive bool `json:"is_active" gorm:"type:bool"` IsActive bool `json:"is_active" gorm:"type:bool"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`

View File

@ -1,22 +1,18 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type MasterMenus struct { type MasterMenus struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Name string `json:"name" gorm:"type:varchar"` Name string `json:"name" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"` Description string `json:"description" gorm:"type:varchar"`
ModuleId int `json:"module_id" gorm:"type:int4"` ModuleId int `json:"module_id" gorm:"type:int4"`
ParentMenuId *int `json:"parent_menu_id" gorm:"type:int4"` ParentMenuId *int `json:"parent_menu_id" gorm:"type:int4"`
Icon *string `json:"icon" gorm:"type:varchar"` Icon *string `json:"icon" gorm:"type:varchar"`
Group string `json:"group" gorm:"type:varchar"` Group string `json:"group" gorm:"type:varchar"`
Position *int `json:"position" gorm:"type:int4"` Position *int `json:"position" gorm:"type:int4"`
StatusId int `json:"status_id" gorm:"type:int4"` StatusId int `json:"status_id" gorm:"type:int4"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,18 +1,14 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type MasterModules struct { type MasterModules struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Name string `json:"name" gorm:"type:varchar"` Name string `json:"name" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"` Description string `json:"description" gorm:"type:varchar"`
PathUrl string `json:"path_url" gorm:"type:varchar"` PathUrl string `json:"path_url" gorm:"type:varchar"`
StatusId int `json:"status_id" gorm:"type:int4"` StatusId int `json:"status_id" gorm:"type:int4"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,18 +1,14 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type OneTimePasswords struct { type OneTimePasswords struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Email string `json:"email" gorm:"type:varchar"` Email string `json:"email" gorm:"type:varchar"`
Name *string `json:"name" gorm:"type:varchar"` Name *string `json:"name" gorm:"type:varchar"`
Identity *string `json:"identity" gorm:"type:varchar"` Identity *string `json:"identity" gorm:"type:varchar"`
OtpCode string `json:"otp_code" gorm:"type:varchar"` OtpCode string `json:"otp_code" gorm:"type:varchar"`
ValidUntil time.Time `json:"valid_until" gorm:"default:(NOW() + INTERVAL '10 minutes')"` ValidUntil time.Time `json:"valid_until" gorm:"default:(NOW() + INTERVAL '10 minutes')"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` IsActive bool `json:"is_active" gorm:"type:bool"`
IsActive bool `json:"is_active" gorm:"type:bool"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
} }

View File

@ -1,29 +0,0 @@
package entity
import (
"time"
"github.com/google/uuid"
)
type Schedules struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Title string `json:"title" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"`
Location string `json:"location" gorm:"type:varchar"`
IsLiveStreaming *bool `json:"is_live_streaming" gorm:"type:bool;default:false"`
LiveStreamingUrl *string `json:"live_streaming_url" gorm:"type:varchar"`
TypeId int `json:"type_id" gorm:"type:int4"`
StartDate *time.Time `json:"start_date" gorm:"type:date"`
EndDate *time.Time `json:"end_date" gorm:"type:date"`
StartTime *string `json:"start_time" gorm:"type:varchar"`
EndTime *string `json:"end_time" gorm:"type:varchar"`
Speakers string `json:"speakers" gorm:"type:varchar"`
PosterImagePath *string `json:"poster_image_path" gorm:"type:varchar"`
CreatedById *uint `json:"created_by_id" gorm:"type:int4"`
StatusId *int `json:"status_id" gorm:"type:int4"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
}

View File

@ -1,15 +1,11 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type Subscription struct { type Subscription struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Email string `json:"email" gorm:"type:varchar"` Email string `json:"email" gorm:"type:varchar"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` IsActive bool `json:"is_active" gorm:"type:bool;default:true"`
IsActive bool `json:"is_active" gorm:"type:bool;default:true"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,21 +1,17 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type UserLevels struct { type UserLevels struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Name string `json:"name" gorm:"type:varchar"` Name string `json:"name" gorm:"type:varchar"`
AliasName string `json:"alias_name" gorm:"type:varchar"` AliasName string `json:"alias_name" gorm:"type:varchar"`
LevelNumber int `json:"level_number" gorm:"type:int4"` LevelNumber int `json:"level_number" gorm:"type:int4"`
ParentLevelId *int `json:"parent_level_id" gorm:"type:int4"` ParentLevelId *int `json:"parent_level_id" gorm:"type:int4"`
ProvinceId *int `json:"province_id" gorm:"type:int4"` ProvinceId *int `json:"province_id" gorm:"type:int4"`
Group *string `json:"group" gorm:"type:varchar"` Group *string `json:"group" gorm:"type:varchar"`
IsApprovalActive *bool `json:"is_approval_active" gorm:"type:bool;default:false"` IsApprovalActive *bool `json:"is_approval_active" gorm:"type:bool;default:false"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` IsActive *bool `json:"is_active" gorm:"type:bool"`
IsActive *bool `json:"is_active" gorm:"type:bool"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,22 +1,18 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type UserRoleAccesses struct { type UserRoleAccesses struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
UserRoleId uint `json:"user_role_id" gorm:"type:int4"` UserRoleId uint `json:"user_role_id" gorm:"type:int4"`
MenuId int `json:"menu_id" gorm:"type:int4"` MenuId int `json:"menu_id" gorm:"type:int4"`
IsViewEnabled bool `json:"is_view_enabled" gorm:"type:bool"` IsViewEnabled bool `json:"is_view_enabled" gorm:"type:bool"`
IsInsertEnabled bool `json:"is_insert_enabled" gorm:"type:bool"` IsInsertEnabled bool `json:"is_insert_enabled" gorm:"type:bool"`
IsUpdateEnabled bool `json:"is_update_enabled" gorm:"type:bool"` IsUpdateEnabled bool `json:"is_update_enabled" gorm:"type:bool"`
IsDeleteEnabled bool `json:"is_delete_enabled" gorm:"type:bool"` IsDeleteEnabled bool `json:"is_delete_enabled" gorm:"type:bool"`
IsApprovalEnabled bool `json:"is_approval_enabled" gorm:"type:bool"` IsApprovalEnabled bool `json:"is_approval_enabled" gorm:"type:bool"`
IsAdminEnabled bool `json:"is_admin_enabled" gorm:"type:bool"` IsAdminEnabled bool `json:"is_admin_enabled" gorm:"type:bool"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,16 +1,12 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type UserRoleLevelDetails struct { type UserRoleLevelDetails struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
UserRoleId uint `json:"user_role_id" gorm:"type:int4"` UserRoleId uint `json:"user_role_id" gorm:"type:int4"`
UserLevelId uint `json:"user_level_id" gorm:"type:int4"` UserLevelId uint `json:"user_level_id" gorm:"type:int4"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,20 +1,16 @@
package entity package entity
import ( import "time"
"github.com/google/uuid"
"time"
)
type UserRoles struct { type UserRoles struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"` ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Name string `json:"name" gorm:"type:varchar"` Name string `json:"name" gorm:"type:varchar"`
Description string `json:"description" gorm:"type:varchar"` Description string `json:"description" gorm:"type:varchar"`
Code string `json:"code" gorm:"type:varchar"` Code string `json:"code" gorm:"type:varchar"`
StatusId int `json:"status_id" gorm:"type:int4;default:1"` StatusId int `json:"status_id" gorm:"type:int4;default:1"`
CreatedById *uint `json:"created_by_id" gorm:"type:int4"` CreatedById *uint `json:"created_by_id" gorm:"type:int4"`
UserLevelId uint `json:"user_level_id" gorm:"type:int4"` UserLevelId uint `json:"user_level_id" gorm:"type:int4"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"` IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
} }

View File

@ -1,36 +0,0 @@
package entity
import (
"github.com/google/uuid"
"time"
)
type Users struct {
ID uint `json:"id" gorm:"primaryKey;type:int4;autoIncrement"`
Username string `json:"username" gorm:"type:varchar"`
Email string `json:"email" gorm:"type:varchar"`
Fullname string `json:"fullname" gorm:"type:varchar"`
Address *string `json:"address" gorm:"type:varchar"`
PhoneNumber *string `json:"phone_number" gorm:"type:varchar"`
WorkType *string `json:"work_type" gorm:"type:varchar"`
GenderType *string `json:"gender_type" gorm:"type:varchar"`
IdentityType *string `json:"identity_type" gorm:"type:varchar"`
IdentityGroup *string `json:"identity_group" gorm:"type:varchar"`
IdentityGroupNumber *string `json:"identity_group_number" gorm:"type:varchar"`
IdentityNumber *string `json:"identity_number" gorm:"type:varchar"`
DateOfBirth *string `json:"date_of_birth" gorm:"type:varchar"`
LastEducation *string `json:"last_education" gorm:"type:varchar"`
UserRoleId uint `json:"user_role_id" gorm:"type:int4"`
UserLevelId uint `json:"user_level_id" gorm:"type:int4"`
UserLevel *UserLevels `json:"user_levels" gorm:"foreignKey:UserLevelId;references:ID"`
KeycloakId *string `json:"keycloak_id" gorm:"type:varchar"`
StatusId *int `json:"status_id" gorm:"type:int4;default:1"`
CreatedById *uint `json:"created_by_id" gorm:"type:int4"`
ProfilePicturePath *string `json:"profile_picture_path" gorm:"type:varchar"`
TempPassword *string `json:"temp_password" gorm:"type:varchar"`
IsEmailUpdated *bool `json:"is_email_updated" gorm:"type:bool;default:false"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`
}

View File

@ -1,9 +1,8 @@
package users package users
import ( import (
"github.com/google/uuid" "go-humas-be/app/database/entity"
"time" "time"
"web-medols-be/app/database/entity"
) )
type Users struct { type Users struct {
@ -30,7 +29,6 @@ type Users struct {
ProfilePicturePath *string `json:"profile_picture_path" gorm:"type:varchar"` ProfilePicturePath *string `json:"profile_picture_path" gorm:"type:varchar"`
TempPassword *string `json:"temp_password" gorm:"type:varchar"` TempPassword *string `json:"temp_password" gorm:"type:varchar"`
IsEmailUpdated *bool `json:"is_email_updated" gorm:"type:bool;default:false"` IsEmailUpdated *bool `json:"is_email_updated" gorm:"type:bool;default:false"`
ClientId *uuid.UUID `json:"client_id" gorm:"type:UUID"`
IsActive *bool `json:"is_active" gorm:"type:bool;default:true"` IsActive *bool `json:"is_active" gorm:"type:bool;default:true"`
CreatedAt time.Time `json:"created_at" gorm:"default:now()"` CreatedAt time.Time `json:"created_at" gorm:"default:now()"`
UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"` UpdatedAt time.Time `json:"updated_at" gorm:"default:now()"`

View File

@ -1,11 +1,11 @@
package database package database
import ( import (
"web-medols-be/app/database/entity"
"web-medols-be/app/database/entity/article_category_details"
"web-medols-be/config/config"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go-humas-be/app/database/entity"
"go-humas-be/app/database/entity/article_category_details"
"go-humas-be/app/database/entity/users"
"go-humas-be/config/config"
"gorm.io/driver/postgres" "gorm.io/driver/postgres"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
@ -86,11 +86,7 @@ func Models() []interface{} {
entity.ActivityLogs{}, entity.ActivityLogs{},
entity.ActivityLogTypes{}, entity.ActivityLogTypes{},
entity.Advertisement{}, entity.Advertisement{},
entity.ApprovalWorkflows{},
entity.ApprovalWorkflowSteps{},
entity.Articles{}, entity.Articles{},
entity.ArticleApprovalFlows{},
entity.ArticleApprovalStepLogs{},
entity.ArticleCategories{}, entity.ArticleCategories{},
entity.ArticleApprovals{}, entity.ArticleApprovals{},
article_category_details.ArticleCategoryDetails{}, article_category_details.ArticleCategoryDetails{},
@ -98,10 +94,7 @@ func Models() []interface{} {
entity.ArticleComments{}, entity.ArticleComments{},
entity.ArticleNulisAI{}, entity.ArticleNulisAI{},
entity.AuditTrails{}, entity.AuditTrails{},
entity.Bookmarks{},
entity.Cities{}, entity.Cities{},
entity.Clients{},
entity.ClientApprovalSettings{},
entity.CsrfTokenRecords{}, entity.CsrfTokenRecords{},
entity.CustomStaticPages{}, entity.CustomStaticPages{},
entity.Districts{}, entity.Districts{},
@ -116,11 +109,10 @@ func Models() []interface{} {
entity.Provinces{}, entity.Provinces{},
entity.OneTimePasswords{}, entity.OneTimePasswords{},
entity.Subscription{}, entity.Subscription{},
entity.Schedules{},
entity.UserLevels{}, entity.UserLevels{},
entity.UserRoles{}, entity.UserRoles{},
entity.UserRoleAccesses{}, entity.UserRoleAccesses{},
entity.Users{}, users.Users{},
entity.UserRoleLevelDetails{}, entity.UserRoleLevelDetails{},
} }
} }

View File

@ -1,8 +1,8 @@
package seeds package seeds
import ( import (
"go-humas-be/app/database/entity"
"gorm.io/gorm" "gorm.io/gorm"
"web-medols-be/app/database/entity"
) )
type ActivityLogsSeeder struct{} type ActivityLogsSeeder struct{}

View File

@ -1,112 +0,0 @@
package seeds
import (
"gorm.io/gorm"
"web-medols-be/app/database/entity"
)
type ApprovalWorkflowsSeeder struct{}
// Sample 3-level approval workflow
var approvalWorkflows = []entity.ApprovalWorkflows{
{
ID: 1,
Name: "3-Level Approval Workflow",
Description: &[]string{"Standard 3-level approval workflow for articles: Editor -> Senior Editor -> Chief Editor"}[0],
IsActive: &[]bool{true}[0],
},
{
ID: 2,
Name: "2-Level Approval Workflow",
Description: &[]string{"Simple 2-level approval workflow: Editor -> Chief Editor"}[0],
IsActive: &[]bool{true}[0],
},
}
// Sample approval workflow steps for 3-level workflow
var approvalWorkflowSteps = []entity.ApprovalWorkflowSteps{
// 3-Level Workflow Steps
{
ID: 1,
WorkflowId: 1,
StepOrder: 1,
StepName: "Editor Review",
RequiredUserLevelId: 3, // Assuming Editor user level ID is 3
CanSkip: &[]bool{false}[0],
AutoApproveAfterHours: &[]int{24}[0],
IsActive: &[]bool{true}[0],
},
{
ID: 2,
WorkflowId: 1,
StepOrder: 2,
StepName: "Senior Editor Review",
RequiredUserLevelId: 4, // Assuming Senior Editor user level ID is 4
CanSkip: &[]bool{false}[0],
AutoApproveAfterHours: &[]int{48}[0],
IsActive: &[]bool{true}[0],
},
{
ID: 3,
WorkflowId: 1,
StepOrder: 3,
StepName: "Chief Editor Final Approval",
RequiredUserLevelId: 5, // Assuming Chief Editor user level ID is 5
CanSkip: &[]bool{false}[0],
AutoApproveAfterHours: &[]int{72}[0],
IsActive: &[]bool{true}[0],
},
// 2-Level Workflow Steps
{
ID: 4,
WorkflowId: 2,
StepOrder: 1,
StepName: "Editor Review",
RequiredUserLevelId: 3, // Editor user level
CanSkip: &[]bool{false}[0],
AutoApproveAfterHours: &[]int{24}[0],
IsActive: &[]bool{true}[0],
},
{
ID: 5,
WorkflowId: 2,
StepOrder: 2,
StepName: "Chief Editor Approval",
RequiredUserLevelId: 5, // Chief Editor user level
CanSkip: &[]bool{false}[0],
AutoApproveAfterHours: &[]int{48}[0],
IsActive: &[]bool{true}[0],
},
}
func (ApprovalWorkflowsSeeder) Seed(conn *gorm.DB) error {
// Seed approval workflows
for _, workflow := range approvalWorkflows {
if err := conn.Create(&workflow).Error; err != nil {
return err
}
}
// Seed approval workflow steps
for _, step := range approvalWorkflowSteps {
if err := conn.Create(&step).Error; err != nil {
return err
}
}
return nil
}
func (ApprovalWorkflowsSeeder) Count(conn *gorm.DB) (int, error) {
var workflowCount int64
if err := conn.Model(&entity.ApprovalWorkflows{}).Count(&workflowCount).Error; err != nil {
return 0, err
}
var stepCount int64
if err := conn.Model(&entity.ApprovalWorkflowSteps{}).Count(&stepCount).Error; err != nil {
return 0, err
}
return int(workflowCount + stepCount), nil
}

View File

@ -1,8 +1,8 @@
package seeds package seeds
import ( import (
"go-humas-be/app/database/entity"
"gorm.io/gorm" "gorm.io/gorm"
"web-medols-be/app/database/entity"
) )
type MasterApprovalStatusesSeeder struct{} type MasterApprovalStatusesSeeder struct{}

View File

@ -1,8 +1,8 @@
package seeds package seeds
import ( import (
"go-humas-be/app/database/entity"
"gorm.io/gorm" "gorm.io/gorm"
"web-medols-be/app/database/entity"
) )
type MasterStatusesSeeder struct{} type MasterStatusesSeeder struct{}

View File

@ -1,36 +0,0 @@
package seeds
import (
"web-medols-be/app/database/entity"
"github.com/google/uuid"
)
// CreateNoApprovalWorkflow creates a special workflow that bypasses approval
func CreateNoApprovalWorkflow() *entity.ApprovalWorkflows {
return &entity.ApprovalWorkflows{
Name: "No Approval Required",
Description: &[]string{"Workflow for clients that don't require approval process"}[0],
IsDefault: &[]bool{false}[0],
IsActive: &[]bool{true}[0],
RequiresApproval: &[]bool{false}[0], // This is the key field
AutoPublish: &[]bool{true}[0], // Auto publish articles
Steps: []entity.ApprovalWorkflowSteps{}, // No steps needed
}
}
// CreateClientApprovalSettings creates default settings for a client
func CreateClientApprovalSettings(clientId string, requiresApproval bool) *entity.ClientApprovalSettings {
clientUUID, _ := uuid.Parse(clientId)
return &entity.ClientApprovalSettings{
ClientId: clientUUID,
RequiresApproval: &[]bool{requiresApproval}[0],
AutoPublishArticles: &[]bool{!requiresApproval}[0], // Auto publish if no approval needed
IsActive: &[]bool{true}[0],
ApprovalExemptUsers: []uint{},
ApprovalExemptRoles: []uint{},
ApprovalExemptCategories: []uint{},
RequireApprovalFor: []string{},
SkipApprovalFor: []string{},
}
}

View File

@ -3,12 +3,12 @@ package middleware
import ( import (
"encoding/json" "encoding/json"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"go-humas-be/app/database/entity"
utilSvc "go-humas-be/utils/service"
"gorm.io/gorm" "gorm.io/gorm"
"log" "log"
"strings" "strings"
"time" "time"
"web-medols-be/app/database/entity"
utilSvc "web-medols-be/utils/service"
) )
func AuditTrailsMiddleware(db *gorm.DB) fiber.Handler { func AuditTrailsMiddleware(db *gorm.DB) fiber.Handler {

View File

@ -1,128 +0,0 @@
package middleware
import (
"strings"
"web-medols-be/app/database/entity"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"gorm.io/gorm"
)
const (
ClientKeyHeader = "X-Client-Key"
ClientContextKey = "client_id"
)
// excludedPaths contains paths that don't require client key validation
var excludedPaths = []string{
"/swagger/*",
"/docs/*",
"/users/login",
"/health/*",
"/clients",
"/clients/*",
"*/viewer/*",
"/bookmarks/test-table",
}
// isPathExcluded checks if the given path should be excluded from client key validation
func isPathExcluded(path string) bool {
for _, excludedPath := range excludedPaths {
if strings.HasPrefix(excludedPath, "*") && strings.HasSuffix(excludedPath, "*") {
// Handle wildcard at both beginning and end (e.g., "*/viewer/*")
pattern := excludedPath[1 : len(excludedPath)-1] // Remove * from both ends
if strings.Contains(path, pattern) {
return true
}
} else if strings.HasPrefix(excludedPath, "*") {
// Handle wildcard at the beginning
if strings.HasSuffix(path, excludedPath[1:]) {
return true
}
} else if strings.HasSuffix(excludedPath, "*") {
// Handle wildcard at the end
prefix := excludedPath[:len(excludedPath)-1]
if strings.HasPrefix(path, prefix) {
return true
}
} else {
// Exact match
if path == excludedPath {
return true
}
}
}
return false
}
// ClientMiddleware extracts and validates the Client Key from request headers
func ClientMiddleware(db *gorm.DB) fiber.Handler {
return func(c *fiber.Ctx) error {
// Check if path should be excluded from client key validation
if isPathExcluded(c.Path()) {
return c.Next()
}
// Extract Client Key from header
clientKey := c.Get(ClientKeyHeader)
if clientKey == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"code": 400,
"messages": []string{"Client Key is required in header: " + ClientKeyHeader},
})
}
// Parse UUID
clientUUID, err := uuid.Parse(clientKey)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"success": false,
"code": 400,
"messages": []string{"Invalid Client Key format"},
})
}
// Validate client exists and is active
var client entity.Clients
if err := db.Where("id = ? AND is_active = ?", clientUUID, true).First(&client).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
"success": false,
"code": 401,
"messages": []string{"Invalid or inactive Client Key"},
})
}
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"success": false,
"code": 500,
"messages": []string{"Error validating Client Key"},
})
}
// Store client ID in context for use in handlers
c.Locals(ClientContextKey, clientUUID)
return c.Next()
}
}
// GetClientID retrieves the client ID from the context
func GetClientID(c *fiber.Ctx) *uuid.UUID {
if clientID, ok := c.Locals(ClientContextKey).(uuid.UUID); ok {
return &clientID
}
return nil
}
// AddExcludedPath adds a new path to the excluded paths list
func AddExcludedPath(path string) {
excludedPaths = append(excludedPaths, path)
}
// GetExcludedPaths returns the current list of excluded paths
func GetExcludedPaths() []string {
return excludedPaths
}

View File

@ -2,9 +2,9 @@ package middleware
import ( import (
"fmt" "fmt"
"go-humas-be/app/database/entity"
"gorm.io/gorm" "gorm.io/gorm"
"time" "time"
"web-medols-be/app/database/entity"
) )
type PostgresStorage struct { type PostgresStorage struct {

View File

@ -1,14 +1,13 @@
package middleware package middleware
import ( import (
"log"
"time"
"web-medols-be/app/database"
"web-medols-be/config/config"
utilsSvc "web-medols-be/utils"
"github.com/gofiber/fiber/v2/middleware/csrf" "github.com/gofiber/fiber/v2/middleware/csrf"
"github.com/gofiber/fiber/v2/middleware/session" "github.com/gofiber/fiber/v2/middleware/session"
"go-humas-be/app/database"
"go-humas-be/config/config"
utilsSvc "go-humas-be/utils"
"log"
"time"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/compress" "github.com/gofiber/fiber/v2/middleware/compress"
@ -58,9 +57,9 @@ func (m *Middleware) Register(db *database.Database) {
m.App.Use(cors.New(cors.Config{ m.App.Use(cors.New(cors.Config{
Next: utilsSvc.IsEnabled(m.Cfg.Middleware.Cors.Enable), Next: utilsSvc.IsEnabled(m.Cfg.Middleware.Cors.Enable),
AllowOrigins: "http://localhost:3000, http://localhost:4000, https://dev.mikulnews.com, https://n8n.qudoco.com, https://narasiahli.com, https://dev.asuransiaman.com, https://dev.beritabumn.com, https://dev.kabarharapan.com, https://dev.kebaikanindonesia.com, https://dev.isukini.com, https://dev.bhayangkarakita.com, https://dev.infokreasi.com, https://dev.milenialbersuara.com, https://dev2.fokusaja.com, https://dev.arahnegeri.com, https://dev.wargabicara.com", AllowOrigins: "http://localhost:3000, http://localhost:4000, https://kontenhumas.com, https://mediapool.id, https://polrinews-project.vercel.app, http://127.0.0.1:3000, http://10.200.202.141",
AllowMethods: "HEAD, GET, POST, PUT, DELETE, OPTIONS, PATCH", AllowMethods: "HEAD, GET, POST, PUT, DELETE, OPTION, PATCH",
AllowHeaders: "Origin, Content-Type, Accept, Accept-Language, Authorization, X-Requested-With, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Origin, Access-Control-Allow-Credentials, X-Csrf-Token, Cookie, Set-Cookie, X-Client-Key", AllowHeaders: "Origin, Content-Type, Accept, Accept-Language, Authorization, X-Requested-With, Access-Control-Request-Method, Access-Control-Request-Headers, Access-Control-Allow-Origin, Access-Control-Allow-Credentials, X-Csrf-Token, Cookie, Set-Cookie",
ExposeHeaders: "Content-Length, Content-Type", ExposeHeaders: "Content-Length, Content-Type",
AllowCredentials: true, AllowCredentials: true,
MaxAge: 12, MaxAge: 12,
@ -126,11 +125,8 @@ func (m *Middleware) Register(db *database.Database) {
//=============================== //===============================
// Client middleware - must be applied before other business logic
m.App.Use(ClientMiddleware(db.DB))
m.App.Use(AuditTrailsMiddleware(db.DB)) m.App.Use(AuditTrailsMiddleware(db.DB))
// StartAuditTrailCleanup(db.DB, m.Cfg.Middleware.AuditTrails.Retention) StartAuditTrailCleanup(db.DB, m.Cfg.Middleware.AuditTrails.Retention)
//m.App.Use(filesystem.New(filesystem.Config{ //m.App.Use(filesystem.New(filesystem.Config{
// Next: utils.IsEnabled(m.Cfg.Middleware.FileSystem.Enable), // Next: utils.IsEnabled(m.Cfg.Middleware.FileSystem.Enable),

View File

@ -1,57 +0,0 @@
package middleware
import (
"web-medols-be/app/database/entity/users"
"web-medols-be/app/module/users/repository"
utilSvc "web-medols-be/utils/service"
"github.com/gofiber/fiber/v2"
"github.com/rs/zerolog"
)
const (
UserContextKey = "user"
UserLevelContextKey = "user_level_id"
)
// UserMiddleware extracts user information from JWT token and stores in context
func UserMiddleware(usersRepo repository.UsersRepository) fiber.Handler {
return func(c *fiber.Ctx) error {
// Skip if no Authorization header
authHeader := c.Get("Authorization")
if authHeader == "" {
return c.Next()
}
// Get user info from token
// Create a default logger if not available in context
log := zerolog.Nop()
if logFromCtx, ok := c.Locals("log").(zerolog.Logger); ok {
log = logFromCtx
}
user := utilSvc.GetUserInfo(log, usersRepo, authHeader)
if user != nil {
// Store user in context
c.Locals(UserContextKey, user)
c.Locals(UserLevelContextKey, user.UserLevelId)
}
return c.Next()
}
}
// GetUser retrieves the user from the context
func GetUser(c *fiber.Ctx) *users.Users {
if user, ok := c.Locals(UserContextKey).(*users.Users); ok {
return user
}
return nil
}
// GetUserLevelID retrieves the user level ID from the context
func GetUserLevelID(c *fiber.Ctx) *uint {
if userLevelId, ok := c.Locals(UserLevelContextKey).(uint); ok {
return &userLevelId
}
return nil
}

View File

@ -1,11 +1,10 @@
package activity_logs package activity_logs
import ( import (
"web-medols-be/app/module/activity_logs/controller"
"web-medols-be/app/module/activity_logs/repository"
"web-medols-be/app/module/activity_logs/service"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"go-humas-be/app/module/activity_logs/controller"
"go-humas-be/app/module/activity_logs/repository"
"go-humas-be/app/module/activity_logs/service"
"go.uber.org/fx" "go.uber.org/fx"
) )

View File

@ -1,17 +1,15 @@
package controller package controller
import ( import (
"strconv"
"strings"
"web-medols-be/app/middleware"
"web-medols-be/app/module/activity_logs/request"
"web-medols-be/app/module/activity_logs/service"
"web-medols-be/utils/paginator"
utilRes "web-medols-be/utils/response"
utilVal "web-medols-be/utils/validator"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go-humas-be/app/module/activity_logs/request"
"go-humas-be/app/module/activity_logs/service"
"go-humas-be/utils/paginator"
utilRes "go-humas-be/utils/response"
utilVal "go-humas-be/utils/validator"
"strconv"
"strings"
) )
type activityLogsController struct { type activityLogsController struct {
@ -40,7 +38,6 @@ func NewActivityLogsController(activityLogsService service.ActivityLogsService,
// @Description API for getting all ActivityLogs // @Description API for getting all ActivityLogs
// @Tags ActivityLogs // @Tags ActivityLogs
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key"
// @Param req query request.ActivityLogsQueryRequest false "query parameters" // @Param req query request.ActivityLogsQueryRequest false "query parameters"
// @Param req query paginator.Pagination false "pagination parameters" // @Param req query paginator.Pagination false "pagination parameters"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
@ -54,8 +51,6 @@ func (_i *activityLogsController) All(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c)
reqContext := request.ActivityLogsQueryRequestContext{ reqContext := request.ActivityLogsQueryRequestContext{
ActivityTypeId: c.Query("activityTypeId"), ActivityTypeId: c.Query("activityTypeId"),
Url: c.Query("url"), Url: c.Query("url"),
@ -65,7 +60,7 @@ func (_i *activityLogsController) All(c *fiber.Ctx) error {
req := reqContext.ToParamRequest() req := reqContext.ToParamRequest()
req.Pagination = paginate req.Pagination = paginate
activityLogsData, paging, err := _i.activityLogsService.All(clientId, req) activityLogsData, paging, err := _i.activityLogsService.All(req)
if err != nil { if err != nil {
return err return err
} }
@ -83,7 +78,6 @@ func (_i *activityLogsController) All(c *fiber.Ctx) error {
// @Description API for getting one ActivityLogs // @Description API for getting one ActivityLogs
// @Tags ActivityLogs // @Tags ActivityLogs
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key"
// @Param id path int true "ActivityLogs ID" // @Param id path int true "ActivityLogs ID"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError // @Failure 400 {object} response.BadRequestError
@ -96,9 +90,7 @@ func (_i *activityLogsController) Show(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c) activityLogsData, err := _i.activityLogsService.Show(uint(id))
activityLogsData, err := _i.activityLogsService.Show(clientId, uint(id))
if err != nil { if err != nil {
return err return err
} }
@ -115,8 +107,7 @@ func (_i *activityLogsController) Show(c *fiber.Ctx) error {
// @Description API for create ActivityLogs // @Description API for create ActivityLogs
// @Tags ActivityLogs // @Tags ActivityLogs
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>) // @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param payload body request.ActivityLogsCreateRequest true "Required payload" // @Param payload body request.ActivityLogsCreateRequest true "Required payload"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
@ -130,8 +121,6 @@ func (_i *activityLogsController) Save(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c)
var authToken *string var authToken *string
getTokenFromHeader := c.Get("Authorization") getTokenFromHeader := c.Get("Authorization")
if getTokenFromHeader == "" { if getTokenFromHeader == "" {
@ -141,7 +130,7 @@ func (_i *activityLogsController) Save(c *fiber.Ctx) error {
} }
visitorIp := GetVisitorIP(c) visitorIp := GetVisitorIP(c)
req.VisitorIp = &visitorIp req.VisitorIp = &visitorIp
dataResult, err := _i.activityLogsService.Save(clientId, *req, authToken) dataResult, err := _i.activityLogsService.Save(*req, authToken)
if err != nil { if err != nil {
return err return err
} }
@ -158,8 +147,7 @@ func (_i *activityLogsController) Save(c *fiber.Ctx) error {
// @Description API for update ActivityLogs // @Description API for update ActivityLogs
// @Tags ActivityLogs // @Tags ActivityLogs
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token"
// @Param payload body request.ActivityLogsUpdateRequest true "Required payload" // @Param payload body request.ActivityLogsUpdateRequest true "Required payload"
// @Param id path int true "ActivityLogs ID" // @Param id path int true "ActivityLogs ID"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
@ -178,9 +166,7 @@ func (_i *activityLogsController) Update(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c) err = _i.activityLogsService.Update(uint(id), *req)
err = _i.activityLogsService.Update(clientId, uint(id), *req)
if err != nil { if err != nil {
return err return err
} }
@ -196,8 +182,7 @@ func (_i *activityLogsController) Update(c *fiber.Ctx) error {
// @Description API for delete ActivityLogs // @Description API for delete ActivityLogs
// @Tags ActivityLogs // @Tags ActivityLogs
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token"
// @Param id path int true "ActivityLogs ID" // @Param id path int true "ActivityLogs ID"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError // @Failure 400 {object} response.BadRequestError
@ -210,9 +195,7 @@ func (_i *activityLogsController) Delete(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c) err = _i.activityLogsService.Delete(uint(id))
err = _i.activityLogsService.Delete(clientId, uint(id))
if err != nil { if err != nil {
return err return err
} }
@ -228,7 +211,6 @@ func (_i *activityLogsController) Delete(c *fiber.Ctx) error {
// @Description API for get activity stats ActivityLogs // @Description API for get activity stats ActivityLogs
// @Tags ActivityLogs // @Tags ActivityLogs
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError // @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError // @Failure 401 {object} response.UnauthorizedError
@ -236,10 +218,7 @@ func (_i *activityLogsController) Delete(c *fiber.Ctx) error {
// @Router /activity-logs/statistics [get] // @Router /activity-logs/statistics [get]
func (_i *activityLogsController) GetActivityStats(c *fiber.Ctx) error { func (_i *activityLogsController) GetActivityStats(c *fiber.Ctx) error {
_i.Log.Info().Interface("GetActivityStats", "checker controller").Msg("") _i.Log.Info().Interface("GetActivityStats", "checker controller").Msg("")
activityStatsData, err := _i.activityLogsService.GetActivityStats()
clientId := middleware.GetClientID(c)
activityStatsData, err := _i.activityLogsService.GetActivityStats(clientId)
if err != nil { if err != nil {
return err return err
} }

View File

@ -2,7 +2,7 @@ package controller
import ( import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"web-medols-be/app/module/activity_logs/service" "go-humas-be/app/module/activity_logs/service"
) )
type Controller struct { type Controller struct {

View File

@ -1,8 +1,8 @@
package mapper package mapper
import ( import (
"web-medols-be/app/database/entity" "go-humas-be/app/database/entity"
res "web-medols-be/app/module/activity_logs/response" res "go-humas-be/app/module/activity_logs/response"
) )
func ActivityLogsResponseMapper(activityLogsReq *entity.ActivityLogs) (activityLogsRes *res.ActivityLogsResponse) { func ActivityLogsResponseMapper(activityLogsReq *entity.ActivityLogs) (activityLogsRes *res.ActivityLogsResponse) {

View File

@ -2,14 +2,13 @@ package repository
import ( import (
"fmt" "fmt"
"github.com/google/uuid"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go-humas-be/app/database"
"go-humas-be/app/database/entity"
"go-humas-be/app/module/activity_logs/request"
"go-humas-be/utils/paginator"
"strings" "strings"
"time" "time"
"web-medols-be/app/database"
"web-medols-be/app/database/entity"
"web-medols-be/app/module/activity_logs/request"
"web-medols-be/utils/paginator"
) )
type activityLogsRepository struct { type activityLogsRepository struct {
@ -19,14 +18,14 @@ type activityLogsRepository struct {
// ActivityLogsRepository define interface of IActivityLogsRepository // ActivityLogsRepository define interface of IActivityLogsRepository
type ActivityLogsRepository interface { type ActivityLogsRepository interface {
GetAll(clientId *uuid.UUID, req request.ActivityLogsQueryRequest) (activityLogss []*entity.ActivityLogs, paging paginator.Pagination, err error) GetAll(req request.ActivityLogsQueryRequest) (activityLogss []*entity.ActivityLogs, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (activityLogs *entity.ActivityLogs, err error) FindOne(id uint) (activityLogs *entity.ActivityLogs, err error)
Create(activityLogs *entity.ActivityLogs) (activityLogsReturn *entity.ActivityLogs, err error) Create(activityLogs *entity.ActivityLogs) (activityLogsReturn *entity.ActivityLogs, err error)
Update(clientId *uuid.UUID, id uint, activityLogs *entity.ActivityLogs) (err error) Update(id uint, activityLogs *entity.ActivityLogs) (err error)
Delete(clientId *uuid.UUID, id uint) (err error) Delete(id uint) (err error)
CountUniqueVisitorAllTime(clientId *uuid.UUID) (count int64, err error) CountUniqueVisitorAllTime() (count int64, err error)
CountUniqueVisitorToday(clientId *uuid.UUID) (count int64, err error) CountUniqueVisitorToday() (count int64, err error)
CountTotalViewAllTime(clientId *uuid.UUID) (count int64, err error) CountTotalViewAllTime() (count int64, err error)
} }
func NewActivityLogsRepository(db *database.Database, logger zerolog.Logger) ActivityLogsRepository { func NewActivityLogsRepository(db *database.Database, logger zerolog.Logger) ActivityLogsRepository {
@ -37,16 +36,11 @@ func NewActivityLogsRepository(db *database.Database, logger zerolog.Logger) Act
} }
// implement interface of IActivityLogsRepository // implement interface of IActivityLogsRepository
func (_i *activityLogsRepository) GetAll(clientId *uuid.UUID, req request.ActivityLogsQueryRequest) (activityLogss []*entity.ActivityLogs, paging paginator.Pagination, err error) { func (_i *activityLogsRepository) GetAll(req request.ActivityLogsQueryRequest) (activityLogss []*entity.ActivityLogs, paging paginator.Pagination, err error) {
var count int64 var count int64
query := _i.DB.DB.Model(&entity.ActivityLogs{}) query := _i.DB.DB.Model(&entity.ActivityLogs{})
// Add ClientId filter
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
if req.ActivityTypeId != nil { if req.ActivityTypeId != nil {
query = query.Where("activity_type_id = ?", req.ActivityTypeId) query = query.Where("activity_type_id = ?", req.ActivityTypeId)
} }
@ -83,15 +77,8 @@ func (_i *activityLogsRepository) GetAll(clientId *uuid.UUID, req request.Activi
return return
} }
func (_i *activityLogsRepository) FindOne(clientId *uuid.UUID, id uint) (activityLogs *entity.ActivityLogs, err error) { func (_i *activityLogsRepository) FindOne(id uint) (activityLogs *entity.ActivityLogs, err error) {
query := _i.DB.DB.Where("id = ?", id) if err := _i.DB.DB.First(&activityLogs, id).Error; err != nil {
// Add ClientId filter
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
if err := query.First(&activityLogs).Error; err != nil {
return nil, err return nil, err
} }
@ -103,47 +90,43 @@ func (_i *activityLogsRepository) Create(activityLogs *entity.ActivityLogs) (act
return activityLogs, result.Error return activityLogs, result.Error
} }
func (_i *activityLogsRepository) Update(clientId *uuid.UUID, id uint, activityLogs *entity.ActivityLogs) (err error) { func (_i *activityLogsRepository) Update(id uint, activityLogs *entity.ActivityLogs) (err error) {
query := _i.DB.DB.Model(&entity.ActivityLogs{}).Where(&entity.ActivityLogs{ID: id}) return _i.DB.DB.Model(&entity.ActivityLogs{}).
if clientId != nil { Where(&entity.ActivityLogs{ID: id}).
query = query.Where("client_id = ?", clientId) Updates(activityLogs).Error
}
return query.Updates(activityLogs).Error
} }
func (_i *activityLogsRepository) Delete(clientId *uuid.UUID, id uint) error { func (_i *activityLogsRepository) Delete(id uint) error {
query := _i.DB.DB.Model(&entity.ActivityLogs{}).Where("id = ?", id) return _i.DB.DB.Delete(&entity.ActivityLogs{}, id).Error
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
return query.Delete(&entity.ActivityLogs{}).Error
} }
func (_i *activityLogsRepository) CountUniqueVisitorAllTime(clientId *uuid.UUID) (count int64, err error) { func (_i *activityLogsRepository) CountUniqueVisitorAllTime() (count int64, err error) {
query := _i.DB.DB.Model(&entity.ActivityLogs{}) err = _i.DB.DB.
if clientId != nil { Model(&entity.ActivityLogs{}).
query = query.Where("client_id = ?", clientId) Distinct("visitor_ip").
} Count(&count).Error
err = query.Distinct("visitor_ip").Count(&count).Error
return return
} }
func (_i *activityLogsRepository) CountTotalViewAllTime(clientId *uuid.UUID) (count int64, err error) { func (_i *activityLogsRepository) CountTotalViewAllTime() (count int64, err error) {
query := _i.DB.DB.Model(&entity.ActivityLogs{}).Where("activity_type_id = ?", 2) err = _i.DB.DB.
if clientId != nil { Model(&entity.ActivityLogs{}).
query = query.Where("client_id = ?", clientId) Where("activity_type_id = ?", 2).
} Count(&count).Error
err = query.Count(&count).Error
return return
} }
func (_i *activityLogsRepository) CountUniqueVisitorToday(clientId *uuid.UUID) (count int64, err error) { func (_i *activityLogsRepository) CountUniqueVisitorToday() (count int64, err error) {
tenMinutesAgo := time.Now().Add(-10 * time.Minute) tenMinutesAgo := time.Now().Add(-10 * time.Minute)
query := _i.DB.DB.Model(&entity.AuditTrails{}).Where("created_at >= ?", tenMinutesAgo) err = _i.DB.DB.
if clientId != nil { Model(&entity.AuditTrails{}).
query = query.Where("client_id = ?", clientId) Where("created_at >= ?", tenMinutesAgo).
} Select("ip").
err = query.Select("ip").Group("ip").Count(&count).Error Group("ip").
Count(&count).Error
return return
} }

View File

@ -1,10 +1,10 @@
package request package request
import ( import (
"go-humas-be/app/database/entity"
"go-humas-be/utils/paginator"
"strconv" "strconv"
"time" "time"
"web-medols-be/app/database/entity"
"web-medols-be/utils/paginator"
) )
type ActivityLogsGeneric interface { type ActivityLogsGeneric interface {

View File

@ -1,17 +1,16 @@
package service package service
import ( import (
"github.com/google/uuid"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"web-medols-be/app/database/entity" "go-humas-be/app/database/entity"
"web-medols-be/app/module/activity_logs/mapper" "go-humas-be/app/module/activity_logs/mapper"
"web-medols-be/app/module/activity_logs/repository" "go-humas-be/app/module/activity_logs/repository"
"web-medols-be/app/module/activity_logs/request" "go-humas-be/app/module/activity_logs/request"
"web-medols-be/app/module/activity_logs/response" "go-humas-be/app/module/activity_logs/response"
"web-medols-be/app/module/articles/service" "go-humas-be/app/module/articles/service"
usersRepository "web-medols-be/app/module/users/repository" usersRepository "go-humas-be/app/module/users/repository"
"web-medols-be/utils/paginator" "go-humas-be/utils/paginator"
utilSvc "web-medols-be/utils/service" utilSvc "go-humas-be/utils/service"
) )
// ActivityLogsService // ActivityLogsService
@ -24,12 +23,12 @@ type activityLogsService struct {
// ActivityLogsService define interface of IActivityLogsService // ActivityLogsService define interface of IActivityLogsService
type ActivityLogsService interface { type ActivityLogsService interface {
All(clientId *uuid.UUID, req request.ActivityLogsQueryRequest) (activityLogs []*response.ActivityLogsResponse, paging paginator.Pagination, err error) All(req request.ActivityLogsQueryRequest) (activityLogs []*response.ActivityLogsResponse, paging paginator.Pagination, err error)
Show(clientId *uuid.UUID, id uint) (activityLogs *response.ActivityLogsResponse, err error) Show(id uint) (activityLogs *response.ActivityLogsResponse, err error)
Save(clientId *uuid.UUID, req request.ActivityLogsCreateRequest, authToken *string) (activityLogs *entity.ActivityLogs, err error) Save(req request.ActivityLogsCreateRequest, authToken *string) (activityLogs *entity.ActivityLogs, err error)
Update(clientId *uuid.UUID, id uint, req request.ActivityLogsUpdateRequest) (err error) Update(id uint, req request.ActivityLogsUpdateRequest) (err error)
Delete(clientId *uuid.UUID, id uint) error Delete(id uint) error
GetActivityStats(clientId *uuid.UUID) (activityStats *response.ActivityStatsResponse, err error) GetActivityStats() (activityStats *response.ActivityStatsResponse, err error)
} }
// NewActivityLogsService init ActivityLogsService // NewActivityLogsService init ActivityLogsService
@ -44,8 +43,8 @@ func NewActivityLogsService(repo repository.ActivityLogsRepository, log zerolog.
} }
// All implement interface of ActivityLogsService // All implement interface of ActivityLogsService
func (_i *activityLogsService) All(clientId *uuid.UUID, req request.ActivityLogsQueryRequest) (activityLogss []*response.ActivityLogsResponse, paging paginator.Pagination, err error) { func (_i *activityLogsService) All(req request.ActivityLogsQueryRequest) (activityLogss []*response.ActivityLogsResponse, paging paginator.Pagination, err error) {
results, paging, err := _i.Repo.GetAll(clientId, req) results, paging, err := _i.Repo.GetAll(req)
if err != nil { if err != nil {
return return
} }
@ -57,8 +56,8 @@ func (_i *activityLogsService) All(clientId *uuid.UUID, req request.ActivityLogs
return return
} }
func (_i *activityLogsService) Show(clientId *uuid.UUID, id uint) (activityLogs *response.ActivityLogsResponse, err error) { func (_i *activityLogsService) Show(id uint) (activityLogs *response.ActivityLogsResponse, err error) {
result, err := _i.Repo.FindOne(clientId, id) result, err := _i.Repo.FindOne(id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -66,15 +65,11 @@ func (_i *activityLogsService) Show(clientId *uuid.UUID, id uint) (activityLogs
return mapper.ActivityLogsResponseMapper(result), nil return mapper.ActivityLogsResponseMapper(result), nil
} }
func (_i *activityLogsService) Save(clientId *uuid.UUID, req request.ActivityLogsCreateRequest, authToken *string) (activityLogs *entity.ActivityLogs, err error) { func (_i *activityLogsService) Save(req request.ActivityLogsCreateRequest, authToken *string) (activityLogs *entity.ActivityLogs, err error) {
_i.Log.Info().Interface("data", req).Msg("") _i.Log.Info().Interface("data", req).Msg("")
newReq := req.ToEntity() newReq := req.ToEntity()
if clientId != nil {
newReq.ClientId = clientId
}
if authToken != nil { if authToken != nil {
createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, *authToken) createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, *authToken)
newReq.UserId = &createdBy.ID newReq.UserId = &createdBy.ID
@ -86,7 +81,7 @@ func (_i *activityLogsService) Save(clientId *uuid.UUID, req request.ActivityLog
} }
// update article // update article
err = _i.ArticleService.UpdateActivityCount(clientId, *req.ArticleId, req.ActivityTypeId) err = _i.ArticleService.UpdateActivityCount(*req.ArticleId, req.ActivityTypeId)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -94,30 +89,26 @@ func (_i *activityLogsService) Save(clientId *uuid.UUID, req request.ActivityLog
return result, nil return result, nil
} }
func (_i *activityLogsService) Update(clientId *uuid.UUID, id uint, req request.ActivityLogsUpdateRequest) (err error) { func (_i *activityLogsService) Update(id uint, req request.ActivityLogsUpdateRequest) (err error) {
_i.Log.Info().Interface("data", req).Msg("") _i.Log.Info().Interface("data", req).Msg("")
newReq := req.ToEntity() return _i.Repo.Update(id, req.ToEntity())
if clientId != nil {
newReq.ClientId = clientId
}
return _i.Repo.Update(clientId, id, newReq)
} }
func (_i *activityLogsService) Delete(clientId *uuid.UUID, id uint) error { func (_i *activityLogsService) Delete(id uint) error {
return _i.Repo.Delete(clientId, id) return _i.Repo.Delete(id)
} }
func (_i *activityLogsService) GetActivityStats(clientId *uuid.UUID) (activityStats *response.ActivityStatsResponse, err error) { func (_i *activityLogsService) GetActivityStats() (activityStats *response.ActivityStatsResponse, err error) {
_i.Log.Info().Interface("GetActivityStats", "checker").Msg("") _i.Log.Info().Interface("GetActivityStats", "checker").Msg("")
countUniqueVisitorAllTime, err := _i.Repo.CountUniqueVisitorAllTime(clientId) countUniqueVisitorAllTime, err := _i.Repo.CountUniqueVisitorAllTime()
if err != nil { if err != nil {
return nil, err return nil, err
} }
countUniqueVisitorToday, err := _i.Repo.CountUniqueVisitorToday(clientId) countUniqueVisitorToday, err := _i.Repo.CountUniqueVisitorToday()
if err != nil { if err != nil {
return nil, err return nil, err
} }
countTotalViewAllTime, err := _i.Repo.CountTotalViewAllTime(clientId) countTotalViewAllTime, err := _i.Repo.CountTotalViewAllTime()
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -2,10 +2,10 @@ package advertisement
import ( import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"go-humas-be/app/module/advertisement/controller"
"go-humas-be/app/module/advertisement/repository"
"go-humas-be/app/module/advertisement/service"
"go.uber.org/fx" "go.uber.org/fx"
"web-medols-be/app/module/advertisement/controller"
"web-medols-be/app/module/advertisement/repository"
"web-medols-be/app/module/advertisement/service"
) )
// struct of AdvertisementRouter // struct of AdvertisementRouter

View File

@ -1,17 +1,15 @@
package controller package controller
import ( import (
"strconv"
"web-medols-be/app/middleware"
"web-medols-be/app/module/advertisement/request"
"web-medols-be/app/module/advertisement/service"
"web-medols-be/utils/paginator"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go-humas-be/app/module/advertisement/request"
"go-humas-be/app/module/advertisement/service"
"go-humas-be/utils/paginator"
"strconv"
utilRes "web-medols-be/utils/response" utilRes "go-humas-be/utils/response"
utilVal "web-medols-be/utils/validator" utilVal "go-humas-be/utils/validator"
) )
type advertisementController struct { type advertisementController struct {
@ -42,7 +40,6 @@ func NewAdvertisementController(advertisementService service.AdvertisementServic
// @Description API for getting all Advertisement // @Description API for getting all Advertisement
// @Tags Advertisement // @Tags Advertisement
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key"
// @Param req query request.AdvertisementQueryRequest false "query parameters" // @Param req query request.AdvertisementQueryRequest false "query parameters"
// @Param req query paginator.Pagination false "pagination parameters" // @Param req query paginator.Pagination false "pagination parameters"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
@ -56,8 +53,6 @@ func (_i *advertisementController) All(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c)
reqContext := request.AdvertisementQueryRequestContext{ reqContext := request.AdvertisementQueryRequestContext{
Title: c.Query("title"), Title: c.Query("title"),
Description: c.Query("description"), Description: c.Query("description"),
@ -68,7 +63,7 @@ func (_i *advertisementController) All(c *fiber.Ctx) error {
req := reqContext.ToParamRequest() req := reqContext.ToParamRequest()
req.Pagination = paginate req.Pagination = paginate
advertisementData, paging, err := _i.advertisementService.All(clientId, req) advertisementData, paging, err := _i.advertisementService.All(req)
if err != nil { if err != nil {
return err return err
} }
@ -86,7 +81,6 @@ func (_i *advertisementController) All(c *fiber.Ctx) error {
// @Description API for getting one Advertisement // @Description API for getting one Advertisement
// @Tags Advertisement // @Tags Advertisement
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key"
// @Param id path int true "Advertisement ID" // @Param id path int true "Advertisement ID"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError // @Failure 400 {object} response.BadRequestError
@ -99,9 +93,7 @@ func (_i *advertisementController) Show(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c) advertisementData, err := _i.advertisementService.Show(uint(id))
advertisementData, err := _i.advertisementService.Show(clientId, uint(id))
if err != nil { if err != nil {
return err return err
} }
@ -118,9 +110,8 @@ func (_i *advertisementController) Show(c *fiber.Ctx) error {
// @Description API for create Advertisement // @Description API for create Advertisement
// @Tags Advertisement // @Tags Advertisement
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" // @Param Authorization header string true "Insert your access token" default(Bearer <Add access token here>)
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param payload body request.AdvertisementCreateRequest true "Required payload" // @Param payload body request.AdvertisementCreateRequest true "Required payload"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError // @Failure 400 {object} response.BadRequestError
@ -133,9 +124,7 @@ func (_i *advertisementController) Save(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c) dataResult, err := _i.advertisementService.Save(*req)
dataResult, err := _i.advertisementService.Save(clientId, *req)
if err != nil { if err != nil {
return err return err
} }
@ -153,8 +142,7 @@ func (_i *advertisementController) Save(c *fiber.Ctx) error {
// @Tags Advertisement // @Tags Advertisement
// @Security Bearer // @Security Bearer
// @Produce json // @Produce json
// @Param X-Client-Key header string false "Insert the X-Client-Key" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token"
// @Param file formData file true "Upload file" multiple false // @Param file formData file true "Upload file" multiple false
// @Param id path int true "Advertisement ID" // @Param id path int true "Advertisement ID"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
@ -168,9 +156,7 @@ func (_i *advertisementController) Upload(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c) err = _i.advertisementService.Upload(c, uint(id))
err = _i.advertisementService.Upload(clientId, c, uint(id))
if err != nil { if err != nil {
return err return err
} }
@ -186,8 +172,7 @@ func (_i *advertisementController) Upload(c *fiber.Ctx) error {
// @Description API for update Advertisement // @Description API for update Advertisement
// @Tags Advertisement // @Tags Advertisement
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token"
// @Param payload body request.AdvertisementUpdateRequest true "Required payload" // @Param payload body request.AdvertisementUpdateRequest true "Required payload"
// @Param id path int true "Advertisement ID" // @Param id path int true "Advertisement ID"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
@ -206,9 +191,7 @@ func (_i *advertisementController) Update(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c) err = _i.advertisementService.Update(uint(id), *req)
err = _i.advertisementService.Update(clientId, uint(id), *req)
if err != nil { if err != nil {
return err return err
} }
@ -224,9 +207,8 @@ func (_i *advertisementController) Update(c *fiber.Ctx) error {
// @Description API for Update Publish Advertisement // @Description API for Update Publish Advertisement
// @Tags Advertisement // @Tags Advertisement
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" // @Param Authorization header string true "Insert your access token" default(Bearer <Add access token here>)
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param id path int true "Advertisement ID" // @Param id path int true "Advertisement ID"
// @Param isPublish query bool true "Advertisement Publish Status" // @Param isPublish query bool true "Advertisement Publish Status"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
@ -245,9 +227,7 @@ func (_i *advertisementController) UpdatePublish(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c) err = _i.advertisementService.UpdatePublish(uint(id), isPublish)
err = _i.advertisementService.UpdatePublish(clientId, uint(id), isPublish)
if err != nil { if err != nil {
return err return err
} }
@ -263,8 +243,7 @@ func (_i *advertisementController) UpdatePublish(c *fiber.Ctx) error {
// @Description API for delete Advertisement // @Description API for delete Advertisement
// @Tags Advertisement // @Tags Advertisement
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token"
// @Param id path int true "Advertisement ID" // @Param id path int true "Advertisement ID"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError // @Failure 400 {object} response.BadRequestError
@ -277,9 +256,7 @@ func (_i *advertisementController) Delete(c *fiber.Ctx) error {
return err return err
} }
clientId := middleware.GetClientID(c) err = _i.advertisementService.Delete(uint(id))
err = _i.advertisementService.Delete(clientId, uint(id))
if err != nil { if err != nil {
return err return err
} }
@ -295,7 +272,6 @@ func (_i *advertisementController) Delete(c *fiber.Ctx) error {
// @Description API for Viewer Advertisement // @Description API for Viewer Advertisement
// @Tags Advertisement // @Tags Advertisement
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key"
// @Param filename path string true "Content File Name" // @Param filename path string true "Content File Name"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError // @Failure 400 {object} response.BadRequestError
@ -303,6 +279,5 @@ func (_i *advertisementController) Delete(c *fiber.Ctx) error {
// @Failure 500 {object} response.InternalServerError // @Failure 500 {object} response.InternalServerError
// @Router /advertisement/viewer/{filename} [get] // @Router /advertisement/viewer/{filename} [get]
func (_i *advertisementController) Viewer(c *fiber.Ctx) error { func (_i *advertisementController) Viewer(c *fiber.Ctx) error {
clientId := middleware.GetClientID(c) return _i.advertisementService.Viewer(c)
return _i.advertisementService.Viewer(clientId, c)
} }

View File

@ -2,7 +2,7 @@ package controller
import ( import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"web-medols-be/app/module/advertisement/service" "go-humas-be/app/module/advertisement/service"
) )
type Controller struct { type Controller struct {

View File

@ -1,8 +1,8 @@
package mapper package mapper
import ( import (
"web-medols-be/app/database/entity" "go-humas-be/app/database/entity"
res "web-medols-be/app/module/advertisement/response" res "go-humas-be/app/module/advertisement/response"
) )
func AdvertisementResponseMapper(advertisementReq *entity.Advertisement, host string) (advertisementRes *res.AdvertisementResponse) { func AdvertisementResponseMapper(advertisementReq *entity.Advertisement, host string) (advertisementRes *res.AdvertisementResponse) {

View File

@ -2,14 +2,13 @@ package repository
import ( import (
"fmt" "fmt"
"github.com/google/uuid"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go-humas-be/app/database"
"go-humas-be/app/database/entity"
"go-humas-be/app/module/advertisement/request"
"go-humas-be/utils/paginator"
utilSvc "go-humas-be/utils/service"
"strings" "strings"
"web-medols-be/app/database"
"web-medols-be/app/database/entity"
"web-medols-be/app/module/advertisement/request"
"web-medols-be/utils/paginator"
utilSvc "web-medols-be/utils/service"
) )
type advertisementRepository struct { type advertisementRepository struct {
@ -19,12 +18,12 @@ type advertisementRepository struct {
// AdvertisementRepository define interface of IAdvertisementRepository // AdvertisementRepository define interface of IAdvertisementRepository
type AdvertisementRepository interface { type AdvertisementRepository interface {
GetAll(clientId *uuid.UUID, req request.AdvertisementQueryRequest) (advertisements []*entity.Advertisement, paging paginator.Pagination, err error) GetAll(req request.AdvertisementQueryRequest) (advertisements []*entity.Advertisement, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (advertisement *entity.Advertisement, err error) FindOne(id uint) (advertisement *entity.Advertisement, err error)
FindByFilename(clientId *uuid.UUID, contentFilename string) (advertisement *entity.Advertisement, err error) FindByFilename(contentFilename string) (advertisement *entity.Advertisement, err error)
Create(advertisement *entity.Advertisement) (advertisementReturn *entity.Advertisement, err error) Create(advertisement *entity.Advertisement) (advertisementReturn *entity.Advertisement, err error)
Update(clientId *uuid.UUID, id uint, advertisement *entity.Advertisement) (err error) Update(id uint, advertisement *entity.Advertisement) (err error)
Delete(clientId *uuid.UUID, id uint) (err error) Delete(id uint) (err error)
} }
func NewAdvertisementRepository(db *database.Database, logger zerolog.Logger) AdvertisementRepository { func NewAdvertisementRepository(db *database.Database, logger zerolog.Logger) AdvertisementRepository {
@ -35,16 +34,10 @@ func NewAdvertisementRepository(db *database.Database, logger zerolog.Logger) Ad
} }
// implement interface of IAdvertisementRepository // implement interface of IAdvertisementRepository
func (_i *advertisementRepository) GetAll(clientId *uuid.UUID, req request.AdvertisementQueryRequest) (advertisements []*entity.Advertisement, paging paginator.Pagination, err error) { func (_i *advertisementRepository) GetAll(req request.AdvertisementQueryRequest) (advertisements []*entity.Advertisement, paging paginator.Pagination, err error) {
var count int64 var count int64
query := _i.DB.DB.Model(&entity.Advertisement{}) query := _i.DB.DB.Model(&entity.Advertisement{})
// Add ClientId filter
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("is_active = ?", true) query = query.Where("is_active = ?", true)
if req.Title != nil && *req.Title != "" { if req.Title != nil && *req.Title != "" {
@ -89,30 +82,17 @@ func (_i *advertisementRepository) GetAll(clientId *uuid.UUID, req request.Adver
return return
} }
func (_i *advertisementRepository) FindOne(clientId *uuid.UUID, id uint) (advertisement *entity.Advertisement, err error) { func (_i *advertisementRepository) FindOne(id uint) (advertisement *entity.Advertisement, err error) {
query := _i.DB.DB.Where("id = ?", id) if err := _i.DB.DB.First(&advertisement, id).Error; err != nil {
// Add ClientId filter
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
if err := query.First(&advertisement).Error; err != nil {
return nil, err return nil, err
} }
return advertisement, nil return advertisement, nil
} }
func (_i *advertisementRepository) FindByFilename(clientId *uuid.UUID, contentFilename string) (advertisement *entity.Advertisement, err error) { func (_i *advertisementRepository) FindByFilename(contentFilename string) (advertisement *entity.Advertisement, err error) {
query := _i.DB.DB.Where("content_file_name = ?", contentFilename)
// Add ClientId filter if err := _i.DB.DB.Where("content_file_name = ?", contentFilename).First(&advertisement).Error; err != nil {
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
if err := query.First(&advertisement).Error; err != nil {
return nil, err return nil, err
} }
@ -124,22 +104,16 @@ func (_i *advertisementRepository) Create(advertisement *entity.Advertisement) (
return advertisement, result.Error return advertisement, result.Error
} }
func (_i *advertisementRepository) Update(clientId *uuid.UUID, id uint, advertisement *entity.Advertisement) (err error) { func (_i *advertisementRepository) Update(id uint, advertisement *entity.Advertisement) (err error) {
advertisementMap, err := utilSvc.StructToMap(advertisement) advertisementMap, err := utilSvc.StructToMap(advertisement)
if err != nil { if err != nil {
return err return err
} }
query := _i.DB.DB.Model(&entity.Advertisement{}).Where(&entity.Advertisement{ID: id}) return _i.DB.DB.Model(&entity.Advertisement{}).
if clientId != nil { Where(&entity.Advertisement{ID: id}).
query = query.Where("client_id = ?", clientId) Updates(advertisementMap).Error
}
return query.Updates(advertisementMap).Error
} }
func (_i *advertisementRepository) Delete(clientId *uuid.UUID, id uint) error { func (_i *advertisementRepository) Delete(id uint) error {
query := _i.DB.DB.Model(&entity.Advertisement{}).Where("id = ?", id) return _i.DB.DB.Delete(&entity.Advertisement{}, id).Error
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
return query.Delete(&entity.Advertisement{}).Error
} }

View File

@ -1,10 +1,10 @@
package request package request
import ( import (
"go-humas-be/app/database/entity"
"go-humas-be/utils/paginator"
"strconv" "strconv"
"time" "time"
"web-medols-be/app/database/entity"
"web-medols-be/utils/paginator"
) )
type AdvertisementGeneric interface { type AdvertisementGeneric interface {

View File

@ -4,9 +4,17 @@ import (
"context" "context"
"fmt" "fmt"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/minio/minio-go/v7" "github.com/minio/minio-go/v7"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go-humas-be/app/database/entity"
"go-humas-be/app/module/advertisement/mapper"
"go-humas-be/app/module/advertisement/repository"
"go-humas-be/app/module/advertisement/request"
"go-humas-be/app/module/advertisement/response"
usersRepository "go-humas-be/app/module/users/repository"
config "go-humas-be/config/config"
minioStorage "go-humas-be/config/config"
"go-humas-be/utils/paginator"
"io" "io"
"log" "log"
"math/rand" "math/rand"
@ -15,15 +23,6 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"web-medols-be/app/database/entity"
"web-medols-be/app/module/advertisement/mapper"
"web-medols-be/app/module/advertisement/repository"
"web-medols-be/app/module/advertisement/request"
"web-medols-be/app/module/advertisement/response"
usersRepository "web-medols-be/app/module/users/repository"
config "web-medols-be/config/config"
minioStorage "web-medols-be/config/config"
"web-medols-be/utils/paginator"
) )
// AdvertisementService // AdvertisementService
@ -37,14 +36,14 @@ type advertisementService struct {
// AdvertisementService define interface of IAdvertisementService // AdvertisementService define interface of IAdvertisementService
type AdvertisementService interface { type AdvertisementService interface {
All(clientId *uuid.UUID, req request.AdvertisementQueryRequest) (advertisement []*response.AdvertisementResponse, paging paginator.Pagination, err error) All(req request.AdvertisementQueryRequest) (advertisement []*response.AdvertisementResponse, paging paginator.Pagination, err error)
Show(clientId *uuid.UUID, id uint) (advertisement *response.AdvertisementResponse, err error) Show(id uint) (advertisement *response.AdvertisementResponse, err error)
Save(clientId *uuid.UUID, req request.AdvertisementCreateRequest) (advertisement *entity.Advertisement, err error) Save(req request.AdvertisementCreateRequest) (advertisement *entity.Advertisement, err error)
Upload(clientId *uuid.UUID, c *fiber.Ctx, id uint) (err error) Upload(c *fiber.Ctx, id uint) (err error)
Update(clientId *uuid.UUID, id uint, req request.AdvertisementUpdateRequest) (err error) Update(id uint, req request.AdvertisementUpdateRequest) (err error)
UpdatePublish(clientId *uuid.UUID, id uint, isPublish bool) (err error) UpdatePublish(id uint, isPublish bool) (err error)
Delete(clientId *uuid.UUID, id uint) error Delete(id uint) error
Viewer(clientId *uuid.UUID, c *fiber.Ctx) (err error) Viewer(c *fiber.Ctx) (err error)
} }
// NewAdvertisementService init AdvertisementService // NewAdvertisementService init AdvertisementService
@ -60,8 +59,8 @@ func NewAdvertisementService(repo repository.AdvertisementRepository, minioStora
} }
// All implement interface of AdvertisementService // All implement interface of AdvertisementService
func (_i *advertisementService) All(clientId *uuid.UUID, req request.AdvertisementQueryRequest) (advertisements []*response.AdvertisementResponse, paging paginator.Pagination, err error) { func (_i *advertisementService) All(req request.AdvertisementQueryRequest) (advertisements []*response.AdvertisementResponse, paging paginator.Pagination, err error) {
results, paging, err := _i.Repo.GetAll(clientId, req) results, paging, err := _i.Repo.GetAll(req)
if err != nil { if err != nil {
return return
} }
@ -74,8 +73,8 @@ func (_i *advertisementService) All(clientId *uuid.UUID, req request.Advertiseme
return return
} }
func (_i *advertisementService) Show(clientId *uuid.UUID, id uint) (advertisement *response.AdvertisementResponse, err error) { func (_i *advertisementService) Show(id uint) (advertisement *response.AdvertisementResponse, err error) {
result, err := _i.Repo.FindOne(clientId, id) result, err := _i.Repo.FindOne(id)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -84,18 +83,13 @@ func (_i *advertisementService) Show(clientId *uuid.UUID, id uint) (advertisemen
return mapper.AdvertisementResponseMapper(result, host), nil return mapper.AdvertisementResponseMapper(result, host), nil
} }
func (_i *advertisementService) Save(clientId *uuid.UUID, req request.AdvertisementCreateRequest) (advertisement *entity.Advertisement, err error) { func (_i *advertisementService) Save(req request.AdvertisementCreateRequest) (advertisement *entity.Advertisement, err error) {
_i.Log.Info().Interface("data", req).Msg("") _i.Log.Info().Interface("data", req).Msg("")
newReq := req.ToEntity() newReq := req.ToEntity()
if clientId != nil {
newReq.ClientId = clientId
}
return _i.Repo.Create(newReq) return _i.Repo.Create(newReq)
} }
func (_i *advertisementService) Upload(clientId *uuid.UUID, c *fiber.Ctx, id uint) (err error) { func (_i *advertisementService) Upload(c *fiber.Ctx, id uint) (err error) {
bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName bucketName := _i.MinioStorage.Cfg.ObjectStorage.MinioStorage.BucketName
form, err := c.MultipartForm() form, err := c.MultipartForm()
@ -108,7 +102,7 @@ func (_i *advertisementService) Upload(clientId *uuid.UUID, c *fiber.Ctx, id uin
// Create minio connection. // Create minio connection.
minioClient, err := _i.MinioStorage.ConnectMinio() minioClient, err := _i.MinioStorage.ConnectMinio()
result, err := _i.Repo.FindOne(clientId, id) result, err := _i.Repo.FindOne(id)
if result == nil { if result == nil {
// Return status 400. Id not found. // Return status 400. Id not found.
@ -160,7 +154,7 @@ func (_i *advertisementService) Upload(clientId *uuid.UUID, c *fiber.Ctx, id uin
result.ContentFileName = &newFilename result.ContentFileName = &newFilename
result.ContentFilePath = &objectName result.ContentFilePath = &objectName
err = _i.Repo.Update(clientId, id, result) err = _i.Repo.Update(id, result)
if err != nil { if err != nil {
return err return err
} }
@ -176,16 +170,12 @@ func (_i *advertisementService) Upload(clientId *uuid.UUID, c *fiber.Ctx, id uin
return return
} }
func (_i *advertisementService) Update(clientId *uuid.UUID, id uint, req request.AdvertisementUpdateRequest) (err error) { func (_i *advertisementService) Update(id uint, req request.AdvertisementUpdateRequest) (err error) {
_i.Log.Info().Interface("data", req).Msg("") _i.Log.Info().Interface("data", req).Msg("")
newReq := req.ToEntity() return _i.Repo.Update(id, req.ToEntity())
if clientId != nil {
newReq.ClientId = clientId
}
return _i.Repo.Update(clientId, id, newReq)
} }
func (_i *advertisementService) UpdatePublish(clientId *uuid.UUID, id uint, isPublish bool) (err error) { func (_i *advertisementService) UpdatePublish(id uint, isPublish bool) (err error) {
_i.Log.Info().Str("timestamp", time.Now(). _i.Log.Info().Str("timestamp", time.Now().
Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish"). Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish").
Interface("ids", id).Msg("") Interface("ids", id).Msg("")
@ -193,7 +183,7 @@ func (_i *advertisementService) UpdatePublish(clientId *uuid.UUID, id uint, isPu
Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish"). Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish").
Interface("isPublish", isPublish).Msg("") Interface("isPublish", isPublish).Msg("")
result, err := _i.Repo.FindOne(clientId, id) result, err := _i.Repo.FindOne(id)
if err != nil { if err != nil {
return err return err
} }
@ -204,24 +194,21 @@ func (_i *advertisementService) UpdatePublish(clientId *uuid.UUID, id uint, isPu
Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish"). Format(time.RFC3339)).Str("Service:Resource", "UpdatePublish").
Interface("result", result).Msg("") Interface("result", result).Msg("")
return _i.Repo.Update(clientId, id, result) return _i.Repo.Update(id, result)
} }
func (_i *advertisementService) Delete(clientId *uuid.UUID, id uint) error { func (_i *advertisementService) Delete(id uint) error {
result, err := _i.Repo.FindOne(clientId, id) result, err := _i.Repo.FindOne(id)
if err != nil { if err != nil {
return err return err
} }
result.IsActive = false result.IsActive = false
if clientId != nil { return _i.Repo.Update(id, result)
result.ClientId = clientId
}
return _i.Repo.Update(clientId, id, result)
} }
func (_i *advertisementService) Viewer(clientId *uuid.UUID, c *fiber.Ctx) (err error) { func (_i *advertisementService) Viewer(c *fiber.Ctx) (err error) {
filename := c.Params("filename") filename := c.Params("filename")
result, err := _i.Repo.FindByFilename(clientId, filename) result, err := _i.Repo.FindByFilename(filename)
if err != nil { if err != nil {
return err return err
} }

View File

@ -1,57 +0,0 @@
package approval_workflow_steps
import (
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
"web-medols-be/app/module/approval_workflow_steps/controller"
"web-medols-be/app/module/approval_workflow_steps/repository"
"web-medols-be/app/module/approval_workflow_steps/service"
)
// ApprovalWorkflowStepsRouter struct of ApprovalWorkflowStepsRouter
type ApprovalWorkflowStepsRouter struct {
App fiber.Router
Controller controller.ApprovalWorkflowStepsController
}
// NewApprovalWorkflowStepsModule register bulky of ApprovalWorkflowSteps module
var NewApprovalWorkflowStepsModule = fx.Options(
// register repository of ApprovalWorkflowSteps module
fx.Provide(repository.NewApprovalWorkflowStepsRepository),
// register service of ApprovalWorkflowSteps module
fx.Provide(service.NewApprovalWorkflowStepsService),
// register controller of ApprovalWorkflowSteps module
fx.Provide(controller.NewApprovalWorkflowStepsController),
// register router of ApprovalWorkflowSteps module
fx.Provide(NewApprovalWorkflowStepsRouter),
)
// NewApprovalWorkflowStepsRouter init ApprovalWorkflowStepsRouter
func NewApprovalWorkflowStepsRouter(fiber *fiber.App, controller controller.ApprovalWorkflowStepsController) *ApprovalWorkflowStepsRouter {
return &ApprovalWorkflowStepsRouter{
App: fiber,
Controller: controller,
}
}
// RegisterApprovalWorkflowStepsRoutes register routes of ApprovalWorkflowSteps module
func (_i *ApprovalWorkflowStepsRouter) RegisterApprovalWorkflowStepsRoutes() {
// define controllers
approvalWorkflowStepsController := _i.Controller
// define routes
_i.App.Route("/approval-workflow-steps", func(router fiber.Router) {
router.Get("/", approvalWorkflowStepsController.All)
router.Get("/:id", approvalWorkflowStepsController.Show)
router.Post("/", approvalWorkflowStepsController.Save)
router.Put("/:id", approvalWorkflowStepsController.Update)
router.Delete("/:id", approvalWorkflowStepsController.Delete)
router.Get("/workflow/:workflowId", approvalWorkflowStepsController.GetByWorkflow)
router.Get("/role/:roleId", approvalWorkflowStepsController.GetByRole)
router.Post("/bulk", approvalWorkflowStepsController.BulkSave)
router.Put("/workflow/:workflowId/reorder", approvalWorkflowStepsController.Reorder)
})
}

View File

@ -1,443 +0,0 @@
package controller
import (
"strconv"
"web-medols-be/app/database/entity"
"web-medols-be/app/middleware"
"web-medols-be/app/module/approval_workflow_steps/request"
"web-medols-be/app/module/approval_workflow_steps/service"
"web-medols-be/utils/paginator"
utilRes "web-medols-be/utils/response"
utilVal "web-medols-be/utils/validator"
"github.com/gofiber/fiber/v2"
"github.com/rs/zerolog"
)
// Helper functions for parsing query parameters
func parseUintPointer(s string) *uint {
if s == "" {
return nil
}
if val, err := strconv.ParseUint(s, 10, 32); err == nil {
uval := uint(val)
return &uval
}
return nil
}
func parseIntPointer(s string) *int {
if s == "" {
return nil
}
if val, err := strconv.Atoi(s); err == nil {
return &val
}
return nil
}
func parseStringPointer(s string) *string {
if s == "" {
return nil
}
return &s
}
func parseBoolPointer(s string) *bool {
if s == "" {
return nil
}
if val, err := strconv.ParseBool(s); err == nil {
return &val
}
return nil
}
type approvalWorkflowStepsController struct {
approvalWorkflowStepsService service.ApprovalWorkflowStepsService
Log zerolog.Logger
}
type ApprovalWorkflowStepsController interface {
All(c *fiber.Ctx) error
Show(c *fiber.Ctx) error
Save(c *fiber.Ctx) error
Update(c *fiber.Ctx) error
Delete(c *fiber.Ctx) error
GetByWorkflow(c *fiber.Ctx) error
GetByRole(c *fiber.Ctx) error
BulkSave(c *fiber.Ctx) error
Reorder(c *fiber.Ctx) error
}
func NewApprovalWorkflowStepsController(approvalWorkflowStepsService service.ApprovalWorkflowStepsService, log zerolog.Logger) ApprovalWorkflowStepsController {
return &approvalWorkflowStepsController{
approvalWorkflowStepsService: approvalWorkflowStepsService,
Log: log,
}
}
// All ApprovalWorkflowSteps
// @Summary Get all ApprovalWorkflowSteps
// @Description API for getting all ApprovalWorkflowSteps
// @Tags ApprovalWorkflowSteps
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param workflowId query int false "Workflow ID filter"
// @Param stepOrder query int false "Step order filter"
// @Param stepName query string false "Step name filter"
// @Param userLevelId query int false "User level ID filter"
// @Param isOptional query bool false "Is optional filter"
// @Param isActive query bool false "Is active filter"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflow-steps [get]
func (_i *approvalWorkflowStepsController) All(c *fiber.Ctx) error {
_, err := paginator.Paginate(c)
if err != nil {
return err
}
req := request.GetApprovalWorkflowStepsRequest{
WorkflowID: parseUintPointer(c.Query("workflowId")),
StepOrder: parseIntPointer(c.Query("stepOrder")),
StepName: parseStringPointer(c.Query("stepName")),
UserLevelID: parseUintPointer(c.Query("userLevelId")),
IsOptional: parseBoolPointer(c.Query("isOptional")),
IsActive: parseBoolPointer(c.Query("isActive")),
Page: 1,
Limit: 10,
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
_i.Log.Info().Interface("clientId", clientId).Msg("")
approvalWorkflowStepsData, paging, err := _i.approvalWorkflowStepsService.GetAll(clientId, req)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflowSteps list successfully retrieved"},
Data: approvalWorkflowStepsData,
Meta: paging,
})
}
// Show ApprovalWorkflowSteps
// @Summary Get one ApprovalWorkflowSteps
// @Description API for getting one ApprovalWorkflowSteps
// @Tags ApprovalWorkflowSteps
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflowSteps ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflow-steps/{id} [get]
func (_i *approvalWorkflowStepsController) Show(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
approvalWorkflowStepsData, err := _i.approvalWorkflowStepsService.FindOne(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflowSteps successfully retrieved"},
Data: approvalWorkflowStepsData,
})
}
// Save ApprovalWorkflowSteps
// @Summary Save ApprovalWorkflowSteps
// @Description API for saving ApprovalWorkflowSteps
// @Tags ApprovalWorkflowSteps
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param payload body request.CreateApprovalWorkflowStepsRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflow-steps [post]
func (_i *approvalWorkflowStepsController) Save(c *fiber.Ctx) error {
req := new(request.CreateApprovalWorkflowStepsRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to entity
step := &entity.ApprovalWorkflowSteps{
WorkflowId: req.WorkflowID,
StepOrder: req.StepOrder,
StepName: req.StepName,
RequiredUserLevelId: req.ApproverRoleID,
CanSkip: &req.IsOptional,
}
approvalWorkflowStepsData, err := _i.approvalWorkflowStepsService.Create(clientId, step)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflowSteps successfully created"},
Data: approvalWorkflowStepsData,
})
}
// Update ApprovalWorkflowSteps
// @Summary Update ApprovalWorkflowSteps
// @Description API for updating ApprovalWorkflowSteps
// @Tags ApprovalWorkflowSteps
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflowSteps ID"
// @Param payload body request.UpdateApprovalWorkflowStepsRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflow-steps/{id} [put]
func (_i *approvalWorkflowStepsController) Update(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
req := new(request.UpdateApprovalWorkflowStepsRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to entity
step := &entity.ApprovalWorkflowSteps{}
if req.StepOrder != nil {
step.StepOrder = *req.StepOrder
}
if req.StepName != nil {
step.StepName = *req.StepName
}
if req.ApproverRoleID != nil {
step.RequiredUserLevelId = *req.ApproverRoleID
}
if req.IsOptional != nil {
step.CanSkip = req.IsOptional
}
err = _i.approvalWorkflowStepsService.Update(clientId, uint(id), step)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflowSteps successfully updated"},
})
}
// Delete ApprovalWorkflowSteps
// @Summary Delete ApprovalWorkflowSteps
// @Description API for deleting ApprovalWorkflowSteps
// @Tags ApprovalWorkflowSteps
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflowSteps ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflow-steps/{id} [delete]
func (_i *approvalWorkflowStepsController) Delete(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
err = _i.approvalWorkflowStepsService.Delete(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflowSteps successfully deleted"},
})
}
// GetByWorkflow ApprovalWorkflowSteps
// @Summary Get ApprovalWorkflowSteps by Workflow ID
// @Description API for getting ApprovalWorkflowSteps by Workflow ID
// @Tags ApprovalWorkflowSteps
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param workflowId path int true "Workflow ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflow-steps/workflow/{workflowId} [get]
func (_i *approvalWorkflowStepsController) GetByWorkflow(c *fiber.Ctx) error {
workflowId, err := strconv.Atoi(c.Params("workflowId"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid workflow ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
approvalWorkflowStepsData, err := _i.approvalWorkflowStepsService.GetByWorkflowID(clientId, uint(workflowId))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflowSteps by workflow successfully retrieved"},
Data: approvalWorkflowStepsData,
})
}
// GetByRole ApprovalWorkflowSteps
// @Summary Get ApprovalWorkflowSteps by Role ID
// @Description API for getting ApprovalWorkflowSteps by Role ID
// @Tags ApprovalWorkflowSteps
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param roleId path int true "Role ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflow-steps/role/{roleId} [get]
func (_i *approvalWorkflowStepsController) GetByRole(c *fiber.Ctx) error {
roleId, err := strconv.Atoi(c.Params("roleId"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid role ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
approvalWorkflowStepsData, err := _i.approvalWorkflowStepsService.GetByWorkflowID(clientId, uint(roleId))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflowSteps by role successfully retrieved"},
Data: approvalWorkflowStepsData,
})
}
// BulkSave ApprovalWorkflowSteps
// @Summary Bulk create ApprovalWorkflowSteps
// @Description API for bulk creating ApprovalWorkflowSteps
// @Tags ApprovalWorkflowSteps
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param payload body request.BulkCreateApprovalWorkflowStepsRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflow-steps/bulk [post]
func (_i *approvalWorkflowStepsController) BulkSave(c *fiber.Ctx) error {
req := new(request.BulkCreateApprovalWorkflowStepsRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to entities
var steps []*entity.ApprovalWorkflowSteps
for _, stepReq := range req.Steps {
step := &entity.ApprovalWorkflowSteps{
WorkflowId: stepReq.WorkflowID,
StepOrder: stepReq.StepOrder,
StepName: stepReq.StepName,
RequiredUserLevelId: stepReq.ApproverRoleID,
CanSkip: &stepReq.IsOptional,
}
steps = append(steps, step)
}
approvalWorkflowStepsData, err := _i.approvalWorkflowStepsService.BulkCreate(clientId, req.WorkflowID, steps)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflowSteps successfully bulk created"},
Data: approvalWorkflowStepsData,
})
}
// Reorder ApprovalWorkflowSteps
// @Summary Reorder ApprovalWorkflowSteps
// @Description API for reordering ApprovalWorkflowSteps
// @Tags ApprovalWorkflowSteps
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param workflowId path int true "Workflow ID"
// @Param payload body request.ReorderApprovalWorkflowStepsRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflow-steps/workflow/{workflowId}/reorder [put]
func (_i *approvalWorkflowStepsController) Reorder(c *fiber.Ctx) error {
workflowId, err := strconv.Atoi(c.Params("workflowId"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid workflow ID format")
}
req := new(request.ReorderApprovalWorkflowStepsRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to expected format
stepOrders := req.ToStepOrders()
err = _i.approvalWorkflowStepsService.ReorderSteps(clientId, uint(workflowId), stepOrders)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflowSteps successfully reordered"},
})
}

View File

@ -1,75 +0,0 @@
package mapper
import (
"github.com/google/uuid"
"github.com/rs/zerolog"
"web-medols-be/app/database/entity"
res "web-medols-be/app/module/approval_workflow_steps/response"
usersRepository "web-medols-be/app/module/users/repository"
)
func ApprovalWorkflowStepsResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
approvalWorkflowStepsReq *entity.ApprovalWorkflowSteps,
usersRepo usersRepository.UsersRepository,
) (approvalWorkflowStepsRes *res.ApprovalWorkflowStepsResponse) {
if approvalWorkflowStepsReq != nil {
// Convert boolean pointers to boolean values
isOptional := false
if approvalWorkflowStepsReq.CanSkip != nil {
isOptional = *approvalWorkflowStepsReq.CanSkip
}
autoApprove := false
if approvalWorkflowStepsReq.AutoApproveAfterHours != nil {
autoApprove = *approvalWorkflowStepsReq.AutoApproveAfterHours > 0
}
approvalWorkflowStepsRes = &res.ApprovalWorkflowStepsResponse{
ID: approvalWorkflowStepsReq.ID,
WorkflowID: approvalWorkflowStepsReq.WorkflowId,
StepName: approvalWorkflowStepsReq.StepName,
StepOrder: approvalWorkflowStepsReq.StepOrder,
ApproverRoleID: approvalWorkflowStepsReq.RequiredUserLevelId,
IsOptional: isOptional,
RequiresComment: false, // Default value
AutoApprove: autoApprove,
TimeoutHours: approvalWorkflowStepsReq.AutoApproveAfterHours,
CreatedAt: approvalWorkflowStepsReq.CreatedAt,
UpdatedAt: approvalWorkflowStepsReq.UpdatedAt,
}
}
return approvalWorkflowStepsRes
}
func ApprovalWorkflowStepsSummaryResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
approvalWorkflowStepsReq *entity.ApprovalWorkflowSteps,
usersRepo usersRepository.UsersRepository,
) (approvalWorkflowStepsRes *res.ApprovalWorkflowStepsSummaryResponse) {
if approvalWorkflowStepsReq != nil {
// Convert boolean pointers to boolean values
isOptional := false
if approvalWorkflowStepsReq.CanSkip != nil {
isOptional = *approvalWorkflowStepsReq.CanSkip
}
approvalWorkflowStepsRes = &res.ApprovalWorkflowStepsSummaryResponse{
ID: approvalWorkflowStepsReq.ID,
WorkflowID: approvalWorkflowStepsReq.WorkflowId,
StepName: approvalWorkflowStepsReq.StepName,
StepOrder: approvalWorkflowStepsReq.StepOrder,
ApproverRoleID: approvalWorkflowStepsReq.RequiredUserLevelId,
IsOptional: isOptional,
RequiresComment: false, // Default value
TimeoutHours: approvalWorkflowStepsReq.AutoApproveAfterHours,
}
}
return approvalWorkflowStepsRes
}

View File

@ -1,373 +0,0 @@
package repository
import (
"fmt"
"web-medols-be/app/database"
"web-medols-be/app/database/entity"
"web-medols-be/app/module/approval_workflow_steps/request"
"web-medols-be/utils/paginator"
"github.com/google/uuid"
"github.com/rs/zerolog"
)
type approvalWorkflowStepsRepository struct {
DB *database.Database
Log zerolog.Logger
}
// ApprovalWorkflowStepsRepository define interface of IApprovalWorkflowStepsRepository
type ApprovalWorkflowStepsRepository interface {
// Basic CRUD
GetAll(clientId *uuid.UUID, req request.GetApprovalWorkflowStepsRequest) (steps []*entity.ApprovalWorkflowSteps, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (step *entity.ApprovalWorkflowSteps, err error)
Create(clientId *uuid.UUID, step *entity.ApprovalWorkflowSteps) (stepReturn *entity.ApprovalWorkflowSteps, err error)
Update(id uint, step *entity.ApprovalWorkflowSteps) (err error)
Delete(clientId *uuid.UUID, id uint) (err error)
// Workflow-specific methods
GetByWorkflowId(clientId *uuid.UUID, workflowId uint) (steps []*entity.ApprovalWorkflowSteps, err error)
GetActiveByWorkflowId(clientId *uuid.UUID, workflowId uint) (steps []*entity.ApprovalWorkflowSteps, err error)
FindByWorkflowAndStep(clientId *uuid.UUID, workflowId uint, stepOrder int) (step *entity.ApprovalWorkflowSteps, err error)
GetNextStep(clientId *uuid.UUID, workflowId uint, currentStep int) (step *entity.ApprovalWorkflowSteps, err error)
GetPreviousStep(clientId *uuid.UUID, workflowId uint, currentStep int) (step *entity.ApprovalWorkflowSteps, err error)
// Step management methods
ReorderSteps(clientId *uuid.UUID, workflowId uint, stepOrders []map[string]interface{}) (err error)
GetMaxStepOrder(clientId *uuid.UUID, workflowId uint) (maxOrder int, err error)
GetStepsByUserLevel(clientId *uuid.UUID, userLevelId uint) (steps []*entity.ApprovalWorkflowSteps, err error)
// Validation methods
ValidateStepSequence(clientId *uuid.UUID, workflowId uint) (isValid bool, errors []string, err error)
CheckStepDependencies(clientId *uuid.UUID, stepId uint) (canDelete bool, dependencies []string, err error)
}
func NewApprovalWorkflowStepsRepository(db *database.Database, log zerolog.Logger) ApprovalWorkflowStepsRepository {
return &approvalWorkflowStepsRepository{
DB: db,
Log: log,
}
}
// Basic CRUD implementations
func (_i *approvalWorkflowStepsRepository) GetAll(clientId *uuid.UUID, req request.GetApprovalWorkflowStepsRequest) (steps []*entity.ApprovalWorkflowSteps, paging paginator.Pagination, err error) {
var count int64
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
// Apply filters from request
if req.WorkflowID != nil && *req.WorkflowID > 0 {
query = query.Where("workflow_id = ?", *req.WorkflowID)
}
if req.IsActive != nil {
query = query.Where("is_active = ?", *req.IsActive)
}
if req.UserLevelID != nil && *req.UserLevelID > 0 {
query = query.Where("required_user_level_id = ?", *req.UserLevelID)
}
if req.StepOrder != nil && *req.StepOrder > 0 {
query = query.Where("step_order = ?", *req.StepOrder)
}
if req.StepName != nil && *req.StepName != "" {
query = query.Where("step_name ILIKE ?", "%"+*req.StepName+"%")
}
query = query.Preload("Workflow").Preload("RequiredUserLevel")
query = query.Order("workflow_id ASC, step_order ASC")
err = query.Count(&count).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
// Apply pagination
page := req.Page
limit := req.Limit
if page <= 0 {
page = 1
}
if limit <= 0 {
limit = 10
}
offset := (page - 1) * limit
err = query.Offset(offset).Limit(limit).Find(&steps).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
paging = paginator.Pagination{
Page: page,
Limit: limit,
Count: count,
TotalPage: int((count + int64(limit) - 1) / int64(limit)),
}
return steps, paging, nil
}
func (_i *approvalWorkflowStepsRepository) FindOne(clientId *uuid.UUID, id uint) (step *entity.ApprovalWorkflowSteps, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Preload("Workflow").Preload("RequiredUserLevel")
err = query.First(&step, id).Error
return step, err
}
func (_i *approvalWorkflowStepsRepository) Create(clientId *uuid.UUID, step *entity.ApprovalWorkflowSteps) (stepReturn *entity.ApprovalWorkflowSteps, err error) {
step.ClientId = clientId
err = _i.DB.DB.Create(&step).Error
return step, err
}
func (_i *approvalWorkflowStepsRepository) Update(id uint, step *entity.ApprovalWorkflowSteps) (err error) {
err = _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{}).Where("id = ?", id).Updates(step).Error
return err
}
func (_i *approvalWorkflowStepsRepository) Delete(clientId *uuid.UUID, id uint) (err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
err = query.Delete(&entity.ApprovalWorkflowSteps{}, id).Error
return err
}
// Workflow-specific methods
func (_i *approvalWorkflowStepsRepository) GetByWorkflowId(clientId *uuid.UUID, workflowId uint) (steps []*entity.ApprovalWorkflowSteps, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("workflow_id = ?", workflowId)
query = query.Preload("Workflow").Preload("RequiredUserLevel")
query = query.Order("step_order ASC")
err = query.Find(&steps).Error
return steps, err
}
func (_i *approvalWorkflowStepsRepository) GetActiveByWorkflowId(clientId *uuid.UUID, workflowId uint) (steps []*entity.ApprovalWorkflowSteps, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("workflow_id = ? AND is_active = ?", workflowId, true)
query = query.Preload("Workflow").Preload("RequiredUserLevel")
query = query.Order("step_order ASC")
err = query.Find(&steps).Error
return steps, err
}
func (_i *approvalWorkflowStepsRepository) FindByWorkflowAndStep(clientId *uuid.UUID, workflowId uint, stepOrder int) (step *entity.ApprovalWorkflowSteps, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("workflow_id = ? AND step_order = ?", workflowId, stepOrder)
query = query.Preload("Workflow").Preload("RequiredUserLevel")
err = query.First(&step).Error
return step, err
}
func (_i *approvalWorkflowStepsRepository) GetNextStep(clientId *uuid.UUID, workflowId uint, currentStep int) (step *entity.ApprovalWorkflowSteps, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("workflow_id = ? AND step_order > ? AND is_active = ?", workflowId, currentStep, true)
query = query.Preload("Workflow").Preload("RequiredUserLevel")
query = query.Order("step_order ASC")
err = query.First(&step).Error
return step, err
}
func (_i *approvalWorkflowStepsRepository) GetPreviousStep(clientId *uuid.UUID, workflowId uint, currentStep int) (step *entity.ApprovalWorkflowSteps, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("workflow_id = ? AND step_order < ? AND is_active = ?", workflowId, currentStep, true)
query = query.Preload("Workflow").Preload("RequiredUserLevel")
query = query.Order("step_order DESC")
err = query.First(&step).Error
return step, err
}
// Step management methods
func (_i *approvalWorkflowStepsRepository) ReorderSteps(clientId *uuid.UUID, workflowId uint, stepOrders []map[string]interface{}) (err error) {
// Start transaction
tx := _i.DB.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
if err := tx.Error; err != nil {
return err
}
// Update each step order
for _, stepOrder := range stepOrders {
stepId := stepOrder["id"]
newOrder := stepOrder["step_order"]
query := tx.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
err = query.Where("id = ? AND workflow_id = ?", stepId, workflowId).Update("step_order", newOrder).Error
if err != nil {
tx.Rollback()
return err
}
}
return tx.Commit().Error
}
func (_i *approvalWorkflowStepsRepository) GetMaxStepOrder(clientId *uuid.UUID, workflowId uint) (maxOrder int, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("workflow_id = ?")
query = query.Select("COALESCE(MAX(step_order), 0) as max_order")
err = query.Scan(&maxOrder).Error
return maxOrder, err
}
func (_i *approvalWorkflowStepsRepository) GetStepsByUserLevel(clientId *uuid.UUID, userLevelId uint) (steps []*entity.ApprovalWorkflowSteps, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflowSteps{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("required_user_level_id = ? AND is_active = ?", userLevelId, true)
query = query.Preload("Workflow").Preload("RequiredUserLevel")
query = query.Order("workflow_id ASC, step_order ASC")
err = query.Find(&steps).Error
return steps, err
}
// Validation methods
func (_i *approvalWorkflowStepsRepository) ValidateStepSequence(clientId *uuid.UUID, workflowId uint) (isValid bool, errors []string, err error) {
errors = make([]string, 0)
// Get all steps for the workflow
steps, err := _i.GetActiveByWorkflowId(clientId, workflowId)
if err != nil {
return false, errors, err
}
if len(steps) == 0 {
errors = append(errors, "Workflow must have at least one step")
return false, errors, nil
}
// Check for sequential step orders starting from 1
expectedOrder := 1
for _, step := range steps {
if step.StepOrder != expectedOrder {
errors = append(errors, fmt.Sprintf("Step order %d is missing or out of sequence", expectedOrder))
}
expectedOrder++
}
// Check for duplicate step orders
stepOrderMap := make(map[int]bool)
for _, step := range steps {
if stepOrderMap[step.StepOrder] {
errors = append(errors, fmt.Sprintf("Duplicate step order found: %d", step.StepOrder))
}
stepOrderMap[step.StepOrder] = true
}
isValid = len(errors) == 0
return isValid, errors, nil
}
func (_i *approvalWorkflowStepsRepository) CheckStepDependencies(clientId *uuid.UUID, stepId uint) (canDelete bool, dependencies []string, err error) {
dependencies = make([]string, 0)
// Check if step is referenced in any active approval flows
var activeFlowCount int64
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
// Join with workflow steps to check current step
query = query.Joins("JOIN approval_workflow_steps ON article_approval_flows.workflow_id = approval_workflow_steps.workflow_id AND article_approval_flows.current_step = approval_workflow_steps.step_order")
query = query.Where("approval_workflow_steps.id = ? AND article_approval_flows.status_id IN (1, 4)", stepId) // pending or revision_requested
err = query.Count(&activeFlowCount).Error
if err != nil {
return false, dependencies, err
}
if activeFlowCount > 0 {
dependencies = append(dependencies, fmt.Sprintf("%d active approval flows are currently at this step", activeFlowCount))
}
// Check if step is referenced in approval step logs
var logCount int64
logQuery := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
logQuery = logQuery.Where("client_id = ?", clientId)
}
// This is a simplified check - in reality, you might want to join with the step to check step_order
logQuery = logQuery.Where("step_order IN (SELECT step_order FROM approval_workflow_steps WHERE id = ?)", stepId)
err = logQuery.Count(&logCount).Error
if err != nil {
return false, dependencies, err
}
if logCount > 0 {
dependencies = append(dependencies, fmt.Sprintf("%d approval step logs reference this step", logCount))
}
canDelete = len(dependencies) == 0
return canDelete, dependencies, nil
}

View File

@ -1,72 +0,0 @@
package request
type CreateApprovalWorkflowStepsRequest struct {
WorkflowID uint `json:"workflowId" validate:"required"`
StepOrder int `json:"stepOrder" validate:"required,min=1"`
StepName string `json:"stepName" validate:"required,min=3,max=100"`
Description *string `json:"description" validate:"omitempty,max=500"`
ApproverRoleID uint `json:"approverRoleId" validate:"required"`
IsOptional bool `json:"isOptional"`
RequiresComment bool `json:"requiresComment"`
AutoApprove bool `json:"autoApprove"`
TimeoutHours *int `json:"timeoutHours" validate:"omitempty,min=1,max=720"`
}
type UpdateApprovalWorkflowStepsRequest struct {
StepOrder *int `json:"stepOrder" validate:"omitempty,min=1"`
StepName *string `json:"stepName" validate:"omitempty,min=3,max=100"`
Description *string `json:"description" validate:"omitempty,max=500"`
ApproverRoleID *uint `json:"approverRoleId" validate:"omitempty"`
IsOptional *bool `json:"isOptional"`
RequiresComment *bool `json:"requiresComment"`
AutoApprove *bool `json:"autoApprove"`
TimeoutHours *int `json:"timeoutHours" validate:"omitempty,min=1,max=720"`
}
type GetApprovalWorkflowStepsRequest struct {
WorkflowID *uint `json:"workflowId" form:"workflowId"`
RoleID *uint `json:"roleId" form:"roleId"`
UserLevelID *uint `json:"userLevelId" form:"userLevelId"`
StepOrder *int `json:"stepOrder" form:"stepOrder"`
StepName *string `json:"stepName" form:"stepName"`
IsOptional *bool `json:"isOptional" form:"isOptional"`
IsActive *bool `json:"isActive" form:"isActive"`
Page int `json:"page" form:"page" validate:"min=1"`
Limit int `json:"limit" form:"limit" validate:"min=1,max=100"`
SortBy *string `json:"sortBy" form:"sortBy"`
SortOrder *string `json:"sortOrder" form:"sortOrder" validate:"omitempty,oneof=asc desc"`
}
type BulkCreateApprovalWorkflowStepsRequest struct {
WorkflowID uint `json:"workflowId" validate:"required"`
Steps []CreateApprovalWorkflowStepsRequest `json:"steps" validate:"required,min=1,max=20,dive"`
}
type ReorderApprovalWorkflowStepsRequest struct {
StepOrders []struct {
ID uint `json:"id" validate:"required"`
StepOrder int `json:"stepOrder" validate:"required,min=1"`
} `json:"stepOrders" validate:"required,min=1,dive"`
}
func (r *ReorderApprovalWorkflowStepsRequest) ToStepOrders() []struct {
ID uint
StepOrder int
} {
result := make([]struct {
ID uint
StepOrder int
}, len(r.StepOrders))
for i, step := range r.StepOrders {
result[i] = struct {
ID uint
StepOrder int
}{
ID: step.ID,
StepOrder: step.StepOrder,
}
}
return result
}

View File

@ -1,40 +0,0 @@
package response
import (
"time"
)
type ApprovalWorkflowStepsResponse struct {
ID uint `json:"id"`
WorkflowID uint `json:"workflowId"`
StepOrder int `json:"stepOrder"`
StepName string `json:"stepName"`
Description *string `json:"description"`
ApproverRoleID uint `json:"approverRoleId"`
ApproverRoleName *string `json:"approverRoleName,omitempty"`
IsOptional bool `json:"isOptional"`
RequiresComment bool `json:"requiresComment"`
AutoApprove bool `json:"autoApprove"`
TimeoutHours *int `json:"timeoutHours"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
type ApprovalWorkflowStepsSummaryResponse struct {
ID uint `json:"id"`
WorkflowID uint `json:"workflowId"`
StepOrder int `json:"stepOrder"`
StepName string `json:"stepName"`
ApproverRoleID uint `json:"approverRoleId"`
IsOptional bool `json:"isOptional"`
RequiresComment bool `json:"requiresComment"`
TimeoutHours *int `json:"timeoutHours"`
}
type ApprovalWorkflowStepsStatsResponse struct {
TotalSteps int `json:"totalSteps"`
OptionalSteps int `json:"optionalSteps"`
MandatorySteps int `json:"mandatorySteps"`
StepsWithTimeout int `json:"stepsWithTimeout"`
AverageTimeoutHours float64 `json:"averageTimeoutHours"`
}

View File

@ -1,319 +0,0 @@
package service
import (
"errors"
"fmt"
"web-medols-be/app/database/entity"
"web-medols-be/app/module/approval_workflow_steps/repository"
"web-medols-be/app/module/approval_workflow_steps/request"
workflowRepo "web-medols-be/app/module/approval_workflows/repository"
"web-medols-be/utils/paginator"
"github.com/google/uuid"
"github.com/rs/zerolog"
)
type approvalWorkflowStepsService struct {
ApprovalWorkflowStepsRepository repository.ApprovalWorkflowStepsRepository
ApprovalWorkflowsRepository workflowRepo.ApprovalWorkflowsRepository
Log zerolog.Logger
}
// ApprovalWorkflowStepsService define interface of IApprovalWorkflowStepsService
type ApprovalWorkflowStepsService interface {
// Basic CRUD
GetAll(clientId *uuid.UUID, req request.GetApprovalWorkflowStepsRequest) (steps []*entity.ApprovalWorkflowSteps, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (step *entity.ApprovalWorkflowSteps, err error)
Create(clientId *uuid.UUID, step *entity.ApprovalWorkflowSteps) (stepReturn *entity.ApprovalWorkflowSteps, err error)
Update(clientId *uuid.UUID, id uint, step *entity.ApprovalWorkflowSteps) (err error)
Delete(clientId *uuid.UUID, id uint) (err error)
// Workflow steps management
GetByWorkflowID(clientId *uuid.UUID, workflowID uint) (steps []*entity.ApprovalWorkflowSteps, err error)
// GetByRoleID(clientId *uuid.UUID, roleID uint) (steps []*entity.ApprovalWorkflowSteps, err error) // Not implemented yet
BulkCreate(clientId *uuid.UUID, workflowID uint, steps []*entity.ApprovalWorkflowSteps) (stepsReturn []*entity.ApprovalWorkflowSteps, err error)
ReorderSteps(clientId *uuid.UUID, workflowID uint, stepOrders []struct {
ID uint
StepOrder int
}) (err error)
// Validation
ValidateStep(clientId *uuid.UUID, step *entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error)
CanDeleteStep(clientId *uuid.UUID, id uint) (canDelete bool, reason string, err error)
ValidateStepOrder(clientId *uuid.UUID, workflowID uint, stepOrder int, excludeID *uint) (isValid bool, err error)
}
func NewApprovalWorkflowStepsService(
approvalWorkflowStepsRepository repository.ApprovalWorkflowStepsRepository,
approvalWorkflowsRepository workflowRepo.ApprovalWorkflowsRepository,
log zerolog.Logger,
) ApprovalWorkflowStepsService {
return &approvalWorkflowStepsService{
ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository,
ApprovalWorkflowsRepository: approvalWorkflowsRepository,
Log: log,
}
}
func (_i *approvalWorkflowStepsService) GetAll(clientId *uuid.UUID, req request.GetApprovalWorkflowStepsRequest) (steps []*entity.ApprovalWorkflowSteps, paging paginator.Pagination, err error) {
return _i.ApprovalWorkflowStepsRepository.GetAll(clientId, req)
}
func (_i *approvalWorkflowStepsService) FindOne(clientId *uuid.UUID, id uint) (step *entity.ApprovalWorkflowSteps, err error) {
return _i.ApprovalWorkflowStepsRepository.FindOne(clientId, id)
}
func (_i *approvalWorkflowStepsService) Create(clientId *uuid.UUID, step *entity.ApprovalWorkflowSteps) (stepReturn *entity.ApprovalWorkflowSteps, err error) {
// Validate workflow exists
workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, step.WorkflowId)
if err != nil {
return nil, fmt.Errorf("workflow not found: %w", err)
}
if workflow == nil {
return nil, errors.New("workflow not found")
}
// Validate step order is unique within workflow
isValid, err := _i.ValidateStepOrder(clientId, step.WorkflowId, step.StepOrder, nil)
if err != nil {
return nil, err
}
if !isValid {
return nil, errors.New("step order already exists in this workflow")
}
// Validate step data
isValid, validationErrors, err := _i.ValidateStep(clientId, step)
if err != nil {
return nil, err
}
if !isValid {
return nil, fmt.Errorf("validation failed: %v", validationErrors)
}
return _i.ApprovalWorkflowStepsRepository.Create(clientId, step)
}
func (_i *approvalWorkflowStepsService) Update(clientId *uuid.UUID, id uint, step *entity.ApprovalWorkflowSteps) (err error) {
// Check if step exists
existingStep, err := _i.ApprovalWorkflowStepsRepository.FindOne(clientId, id)
if err != nil {
return err
}
if existingStep == nil {
return errors.New("step not found")
}
// If step order is being changed, validate it's unique
if step.StepOrder != 0 && step.StepOrder != existingStep.StepOrder {
isValid, err := _i.ValidateStepOrder(clientId, existingStep.WorkflowId, step.StepOrder, &id)
if err != nil {
return err
}
if !isValid {
return errors.New("step order already exists in this workflow")
}
}
return _i.ApprovalWorkflowStepsRepository.Update(id, step)
}
func (_i *approvalWorkflowStepsService) Delete(clientId *uuid.UUID, id uint) (err error) {
// Check if step can be deleted
canDelete, reason, err := _i.CanDeleteStep(clientId, id)
if err != nil {
return err
}
if !canDelete {
return fmt.Errorf("cannot delete step: %s", reason)
}
return _i.ApprovalWorkflowStepsRepository.Delete(clientId, id)
}
func (_i *approvalWorkflowStepsService) GetByWorkflowID(clientId *uuid.UUID, workflowID uint) (steps []*entity.ApprovalWorkflowSteps, err error) {
return _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, workflowID)
}
// GetByRoleID method is not implemented in repository yet
// func (_i *approvalWorkflowStepsService) GetByRoleID(clientId *uuid.UUID, roleID uint) (steps []*entity.ApprovalWorkflowSteps, err error) {
// return _i.ApprovalWorkflowStepsRepository.GetByRoleID(clientId, roleID)
// }
func (_i *approvalWorkflowStepsService) BulkCreate(clientId *uuid.UUID, workflowID uint, steps []*entity.ApprovalWorkflowSteps) (stepsReturn []*entity.ApprovalWorkflowSteps, err error) {
// Validate workflow exists
workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, workflowID)
if err != nil {
return nil, fmt.Errorf("workflow not found: %w", err)
}
if workflow == nil {
return nil, errors.New("workflow not found")
}
// Validate all steps
stepOrders := make(map[int]bool)
for i, step := range steps {
step.WorkflowId = workflowID
// Check for duplicate step orders within the batch
if stepOrders[step.StepOrder] {
return nil, fmt.Errorf("duplicate step order %d in batch", step.StepOrder)
}
stepOrders[step.StepOrder] = true
// Validate step order is unique in database
isValid, err := _i.ValidateStepOrder(clientId, workflowID, step.StepOrder, nil)
if err != nil {
return nil, err
}
if !isValid {
return nil, fmt.Errorf("step order %d already exists in workflow", step.StepOrder)
}
// Validate step data
var errors []string
if step.RequiredUserLevelId == 0 {
errors = append(errors, fmt.Sprintf("Step %d: RequiredUserLevelId is required", i+1))
}
if step.AutoApproveAfterHours != nil && *step.AutoApproveAfterHours <= 0 {
errors = append(errors, fmt.Sprintf("Step %d: AutoApproveAfterHours must be positive", i+1))
}
if step.AutoApproveAfterHours != nil && *step.AutoApproveAfterHours > 720 {
errors = append(errors, fmt.Sprintf("Step %d: AutoApproveAfterHours cannot exceed 720 hours (30 days)", i+1))
}
if len(step.StepName) > 500 {
errors = append(errors, fmt.Sprintf("Step %d: StepName cannot exceed 500 characters", i+1))
}
if len(errors) > 0 {
return nil, fmt.Errorf("validation failed for step %d: %v", step.StepOrder, errors)
}
}
// BulkCreate method is not implemented in repository yet
// return _i.ApprovalWorkflowStepsRepository.BulkCreate(clientId, steps)
return nil, fmt.Errorf("BulkCreate method not implemented yet")
}
func (_i *approvalWorkflowStepsService) ReorderSteps(clientId *uuid.UUID, workflowID uint, stepOrders []struct {
ID uint
StepOrder int
}) (err error) {
// Validate workflow exists
workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, workflowID)
if err != nil {
return fmt.Errorf("workflow not found: %w", err)
}
if workflow == nil {
return errors.New("workflow not found")
}
// Validate all steps belong to the workflow and orders are unique
orders := make(map[int]bool)
for _, stepOrder := range stepOrders {
// Check step exists and belongs to workflow
step, err := _i.ApprovalWorkflowStepsRepository.FindOne(clientId, stepOrder.ID)
if err != nil {
return err
}
if step == nil {
return fmt.Errorf("step with ID %d not found", stepOrder.ID)
}
if step.WorkflowId != workflowID {
return fmt.Errorf("step with ID %d does not belong to workflow %d", stepOrder.ID, workflowID)
}
// Check for duplicate orders
if orders[stepOrder.StepOrder] {
return fmt.Errorf("duplicate step order %d", stepOrder.StepOrder)
}
orders[stepOrder.StepOrder] = true
}
// Convert stepOrders to the format expected by repository
stepOrderMaps := make([]map[string]interface{}, len(stepOrders))
for i, stepOrder := range stepOrders {
stepOrderMaps[i] = map[string]interface{}{
"id": stepOrder.ID,
"step_order": stepOrder.StepOrder,
}
}
return _i.ApprovalWorkflowStepsRepository.ReorderSteps(clientId, workflowID, stepOrderMaps)
}
func (_i *approvalWorkflowStepsService) ValidateStep(clientId *uuid.UUID, step *entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error) {
var validationErrors []string
// Validate step name
if step.StepName == "" {
validationErrors = append(validationErrors, "step name is required")
}
if len(step.StepName) < 3 {
validationErrors = append(validationErrors, "step name must be at least 3 characters")
}
if len(step.StepName) > 500 {
validationErrors = append(validationErrors, "step name must not exceed 500 characters")
}
// Validate step order
if step.StepOrder < 1 {
validationErrors = append(validationErrors, "step order must be at least 1")
}
// Validate required user level ID
if step.RequiredUserLevelId == 0 {
validationErrors = append(validationErrors, "required user level ID is required")
}
// Validate auto approve after hours if provided
if step.AutoApproveAfterHours != nil && *step.AutoApproveAfterHours < 1 {
validationErrors = append(validationErrors, "auto approve after hours must be at least 1")
}
if step.AutoApproveAfterHours != nil && *step.AutoApproveAfterHours > 720 {
validationErrors = append(validationErrors, "auto approve after hours must not exceed 720 (30 days)")
}
return len(validationErrors) == 0, validationErrors, nil
}
func (_i *approvalWorkflowStepsService) CanDeleteStep(clientId *uuid.UUID, id uint) (canDelete bool, reason string, err error) {
// Check if step exists
step, err := _i.ApprovalWorkflowStepsRepository.FindOne(clientId, id)
if err != nil {
return false, "", err
}
if step == nil {
return false, "step not found", nil
}
// Check if there are any active approval flows using this step
// This would require checking article_approval_step_logs table
// For now, we'll allow deletion but this should be implemented
// based on business requirements
return true, "", nil
}
func (_i *approvalWorkflowStepsService) ValidateStepOrder(clientId *uuid.UUID, workflowID uint, stepOrder int, excludeID *uint) (isValid bool, err error) {
existingStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, workflowID, stepOrder)
if err != nil {
return false, err
}
// If no existing step found, order is valid
if existingStep == nil {
return true, nil
}
// If excludeID is provided and matches existing step, order is valid (updating same step)
if excludeID != nil && existingStep.ID == *excludeID {
return true, nil
}
// Order already exists for different step
return false, nil
}

View File

@ -1,60 +0,0 @@
package approval_workflows
import (
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
"web-medols-be/app/module/approval_workflows/controller"
"web-medols-be/app/module/approval_workflows/repository"
"web-medols-be/app/module/approval_workflows/service"
)
// ApprovalWorkflowsRouter struct of ApprovalWorkflowsRouter
type ApprovalWorkflowsRouter struct {
App fiber.Router
Controller controller.ApprovalWorkflowsController
}
// NewApprovalWorkflowsModule register bulky of ApprovalWorkflows module
var NewApprovalWorkflowsModule = fx.Options(
// register repository of ApprovalWorkflows module
fx.Provide(repository.NewApprovalWorkflowsRepository),
// register service of ApprovalWorkflows module
fx.Provide(service.NewApprovalWorkflowsService),
// register controller of ApprovalWorkflows module
fx.Provide(controller.NewApprovalWorkflowsController),
// register router of ApprovalWorkflows module
fx.Provide(NewApprovalWorkflowsRouter),
)
// NewApprovalWorkflowsRouter init ApprovalWorkflowsRouter
func NewApprovalWorkflowsRouter(fiber *fiber.App, controller controller.ApprovalWorkflowsController) *ApprovalWorkflowsRouter {
return &ApprovalWorkflowsRouter{
App: fiber,
Controller: controller,
}
}
// RegisterApprovalWorkflowsRoutes register routes of ApprovalWorkflows module
func (_i *ApprovalWorkflowsRouter) RegisterApprovalWorkflowsRoutes() {
// define controllers
approvalWorkflowsController := _i.Controller
// define routes
_i.App.Route("/approval-workflows", func(router fiber.Router) {
router.Get("/", approvalWorkflowsController.All)
router.Get("/default", approvalWorkflowsController.GetDefault)
router.Get("/:id", approvalWorkflowsController.Show)
router.Get("/:id/with-steps", approvalWorkflowsController.GetWithSteps)
router.Post("/", approvalWorkflowsController.Save)
router.Post("/with-steps", approvalWorkflowsController.SaveWithSteps)
router.Put("/:id", approvalWorkflowsController.Update)
router.Put("/:id/with-steps", approvalWorkflowsController.UpdateWithSteps)
router.Put("/:id/set-default", approvalWorkflowsController.SetDefault)
router.Put("/:id/activate", approvalWorkflowsController.Activate)
router.Put("/:id/deactivate", approvalWorkflowsController.Deactivate)
router.Delete("/:id", approvalWorkflowsController.Delete)
})
}

View File

@ -1,481 +0,0 @@
package controller
import (
"github.com/gofiber/fiber/v2"
"github.com/rs/zerolog"
"strconv"
"web-medols-be/app/database/entity"
"web-medols-be/app/middleware"
"web-medols-be/app/module/approval_workflows/request"
"web-medols-be/app/module/approval_workflows/service"
"web-medols-be/utils/paginator"
utilRes "web-medols-be/utils/response"
utilVal "web-medols-be/utils/validator"
)
type approvalWorkflowsController struct {
approvalWorkflowsService service.ApprovalWorkflowsService
Log zerolog.Logger
}
type ApprovalWorkflowsController interface {
All(c *fiber.Ctx) error
Show(c *fiber.Ctx) error
Save(c *fiber.Ctx) error
Update(c *fiber.Ctx) error
Delete(c *fiber.Ctx) error
GetDefault(c *fiber.Ctx) error
SetDefault(c *fiber.Ctx) error
Activate(c *fiber.Ctx) error
Deactivate(c *fiber.Ctx) error
GetWithSteps(c *fiber.Ctx) error
SaveWithSteps(c *fiber.Ctx) error
UpdateWithSteps(c *fiber.Ctx) error
}
func NewApprovalWorkflowsController(approvalWorkflowsService service.ApprovalWorkflowsService, log zerolog.Logger) ApprovalWorkflowsController {
return &approvalWorkflowsController{
approvalWorkflowsService: approvalWorkflowsService,
Log: log,
}
}
// All ApprovalWorkflows
// @Summary Get all ApprovalWorkflows
// @Description API for getting all ApprovalWorkflows
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param req query request.ApprovalWorkflowsQueryRequest false "query parameters"
// @Param req query paginator.Pagination false "pagination parameters"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows [get]
func (_i *approvalWorkflowsController) All(c *fiber.Ctx) error {
paginate, err := paginator.Paginate(c)
if err != nil {
return err
}
reqContext := request.ApprovalWorkflowsQueryRequestContext{
Name: c.Query("name"),
Description: c.Query("description"),
IsActive: c.Query("isActive"),
IsDefault: c.Query("isDefault"),
}
req := reqContext.ToParamRequest()
req.Pagination = paginate
// Get ClientId from context
clientId := middleware.GetClientID(c)
_i.Log.Info().Interface("clientId", clientId).Msg("")
approvalWorkflowsData, paging, err := _i.approvalWorkflowsService.GetAll(clientId, req)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows list successfully retrieved"},
Data: approvalWorkflowsData,
Meta: paging,
})
}
// Show ApprovalWorkflows
// @Summary Get one ApprovalWorkflows
// @Description API for getting one ApprovalWorkflows
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflows ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/{id} [get]
func (_i *approvalWorkflowsController) Show(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
approvalWorkflowsData, err := _i.approvalWorkflowsService.FindOne(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows successfully retrieved"},
Data: approvalWorkflowsData,
})
}
// Save ApprovalWorkflows
// @Summary Save ApprovalWorkflows
// @Description API for saving ApprovalWorkflows
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param payload body request.ApprovalWorkflowsCreateRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows [post]
func (_i *approvalWorkflowsController) Save(c *fiber.Ctx) error {
req := new(request.ApprovalWorkflowsCreateRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to entity
workflow := req.ToEntity()
steps := req.ToStepsEntity()
approvalWorkflowsData, err := _i.approvalWorkflowsService.Create(clientId, workflow, steps)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows successfully created"},
Data: approvalWorkflowsData,
})
}
// Update ApprovalWorkflows
// @Summary Update ApprovalWorkflows
// @Description API for updating ApprovalWorkflows
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflows ID"
// @Param payload body request.ApprovalWorkflowsUpdateRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/{id} [put]
func (_i *approvalWorkflowsController) Update(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
req := new(request.ApprovalWorkflowsUpdateRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to entity
workflow := req.ToEntity()
err = _i.approvalWorkflowsService.Update(clientId, uint(id), workflow)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows successfully updated"},
})
}
// Delete ApprovalWorkflows
// @Summary Delete ApprovalWorkflows
// @Description API for deleting ApprovalWorkflows
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflows ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/{id} [delete]
func (_i *approvalWorkflowsController) Delete(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
err = _i.approvalWorkflowsService.Delete(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows successfully deleted"},
})
}
// GetDefault ApprovalWorkflows
// @Summary Get default ApprovalWorkflows
// @Description API for getting default ApprovalWorkflows
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/default [get]
func (_i *approvalWorkflowsController) GetDefault(c *fiber.Ctx) error {
// Get ClientId from context
clientId := middleware.GetClientID(c)
approvalWorkflowsData, err := _i.approvalWorkflowsService.GetDefault(clientId)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Default ApprovalWorkflows successfully retrieved"},
Data: approvalWorkflowsData,
})
}
// SetDefault ApprovalWorkflows
// @Summary Set default ApprovalWorkflows
// @Description API for setting default ApprovalWorkflows
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflows ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/{id}/set-default [put]
func (_i *approvalWorkflowsController) SetDefault(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
err = _i.approvalWorkflowsService.SetDefault(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows successfully set as default"},
})
}
// Activate ApprovalWorkflows
// @Summary Activate ApprovalWorkflows
// @Description API for activating ApprovalWorkflows
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflows ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/{id}/activate [put]
func (_i *approvalWorkflowsController) Activate(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
err = _i.approvalWorkflowsService.ActivateWorkflow(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows successfully activated"},
})
}
// Deactivate ApprovalWorkflows
// @Summary Deactivate ApprovalWorkflows
// @Description API for deactivating ApprovalWorkflows
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflows ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/{id}/deactivate [put]
func (_i *approvalWorkflowsController) Deactivate(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
err = _i.approvalWorkflowsService.DeactivateWorkflow(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows successfully deactivated"},
})
}
// GetWithSteps ApprovalWorkflows
// @Summary Get ApprovalWorkflows with steps
// @Description API for getting ApprovalWorkflows with steps
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflows ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/{id}/with-steps [get]
func (_i *approvalWorkflowsController) GetWithSteps(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
workflowData, stepsData, err := _i.approvalWorkflowsService.GetWorkflowWithSteps(clientId, uint(id))
if err != nil {
return err
}
// Combine workflow and steps data
responseData := map[string]interface{}{
"workflow": workflowData,
"steps": stepsData,
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows with steps successfully retrieved"},
Data: responseData,
})
}
// SaveWithSteps ApprovalWorkflows
// @Summary Create ApprovalWorkflows with steps
// @Description API for creating ApprovalWorkflows with steps
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param req body request.ApprovalWorkflowsWithStepsCreateRequest true "ApprovalWorkflows with steps data"
// @Success 201 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/with-steps [post]
func (_i *approvalWorkflowsController) SaveWithSteps(c *fiber.Ctx) error {
req := new(request.ApprovalWorkflowsWithStepsCreateRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to entities
workflow := req.ToEntity()
steps := req.ToStepsEntity()
approvalWorkflowsData, err := _i.approvalWorkflowsService.CreateWorkflowWithSteps(clientId, workflow, steps)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows with steps successfully created"},
Data: approvalWorkflowsData,
})
}
// UpdateWithSteps ApprovalWorkflows
// @Summary Update ApprovalWorkflows with steps
// @Description API for updating ApprovalWorkflows with steps
// @Tags ApprovalWorkflows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ApprovalWorkflows ID"
// @Param req body request.ApprovalWorkflowsWithStepsUpdateRequest true "ApprovalWorkflows with steps data"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /approval-workflows/{id}/with-steps [put]
func (_i *approvalWorkflowsController) UpdateWithSteps(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
req := new(request.ApprovalWorkflowsWithStepsUpdateRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to entities
workflow := &entity.ApprovalWorkflows{
Name: req.Name,
Description: &req.Description,
IsActive: req.IsActive,
IsDefault: req.IsDefault,
}
steps := make([]*entity.ApprovalWorkflowSteps, len(req.Steps))
for i, stepReq := range req.Steps {
steps[i] = stepReq.ToEntity(uint(id))
}
err = _i.approvalWorkflowsService.UpdateWorkflowWithSteps(clientId, uint(id), workflow, steps)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ApprovalWorkflows with steps successfully updated"},
})
}

View File

@ -1,126 +0,0 @@
package mapper
import (
"github.com/google/uuid"
"github.com/rs/zerolog"
"web-medols-be/app/database/entity"
approvalWorkflowStepsMapper "web-medols-be/app/module/approval_workflow_steps/mapper"
approvalWorkflowStepsRepository "web-medols-be/app/module/approval_workflow_steps/repository"
approvalWorkflowStepsResponse "web-medols-be/app/module/approval_workflow_steps/response"
res "web-medols-be/app/module/approval_workflows/response"
usersRepository "web-medols-be/app/module/users/repository"
)
func ApprovalWorkflowsResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
approvalWorkflowsReq *entity.ApprovalWorkflows,
approvalWorkflowStepsRepo approvalWorkflowStepsRepository.ApprovalWorkflowStepsRepository,
usersRepo usersRepository.UsersRepository,
) (approvalWorkflowsRes *res.ApprovalWorkflowsResponse) {
// Get workflow steps using GetAll with filter
var workflowStepsArr []*approvalWorkflowStepsResponse.ApprovalWorkflowStepsResponse
if len(approvalWorkflowsReq.Steps) > 0 {
for _, step := range approvalWorkflowsReq.Steps {
workflowStepsArr = append(workflowStepsArr, approvalWorkflowStepsMapper.ApprovalWorkflowStepsResponseMapper(
log,
clientId,
&step,
usersRepo,
))
}
}
if approvalWorkflowsReq != nil {
// Convert boolean pointer to boolean value
isActive := false
if approvalWorkflowsReq.IsActive != nil {
isActive = *approvalWorkflowsReq.IsActive
}
approvalWorkflowsRes = &res.ApprovalWorkflowsResponse{
ID: approvalWorkflowsReq.ID,
Name: approvalWorkflowsReq.Name,
Description: approvalWorkflowsReq.Description,
IsActive: isActive,
CreatedBy: 0, // Default value since entity doesn't have CreatedBy field
CreatedAt: approvalWorkflowsReq.CreatedAt,
UpdatedAt: approvalWorkflowsReq.UpdatedAt,
Steps: workflowStepsArr,
}
}
return approvalWorkflowsRes
}
func ApprovalWorkflowsWithStepsResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
approvalWorkflowsReq *entity.ApprovalWorkflows,
approvalWorkflowStepsRepo approvalWorkflowStepsRepository.ApprovalWorkflowStepsRepository,
usersRepo usersRepository.UsersRepository,
) (approvalWorkflowsRes *res.ApprovalWorkflowsWithStepsResponse) {
// Get workflow steps with detailed information
var workflowStepsArr []*approvalWorkflowStepsResponse.ApprovalWorkflowStepsResponse
if len(approvalWorkflowsReq.Steps) > 0 {
for _, step := range approvalWorkflowsReq.Steps {
workflowStepsArr = append(workflowStepsArr, approvalWorkflowStepsMapper.ApprovalWorkflowStepsResponseMapper(
log,
clientId,
&step,
usersRepo,
))
}
}
if approvalWorkflowsReq != nil {
// Convert boolean pointer to boolean value
isActive := false
if approvalWorkflowsReq.IsActive != nil {
isActive = *approvalWorkflowsReq.IsActive
}
approvalWorkflowsRes = &res.ApprovalWorkflowsWithStepsResponse{
ID: approvalWorkflowsReq.ID,
Name: approvalWorkflowsReq.Name,
Description: approvalWorkflowsReq.Description,
IsActive: isActive,
CreatedBy: 0, // Default value since entity doesn't have CreatedBy field
CreatedAt: approvalWorkflowsReq.CreatedAt,
UpdatedAt: approvalWorkflowsReq.UpdatedAt,
Steps: workflowStepsArr,
}
}
return approvalWorkflowsRes
}
func ApprovalWorkflowsSummaryResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
approvalWorkflowsReq *entity.ApprovalWorkflows,
usersRepo usersRepository.UsersRepository,
) (approvalWorkflowsRes *res.ApprovalWorkflowsSummaryResponse) {
if approvalWorkflowsReq != nil {
// Convert boolean pointer to boolean value
isActive := false
if approvalWorkflowsReq.IsActive != nil {
isActive = *approvalWorkflowsReq.IsActive
}
approvalWorkflowsRes = &res.ApprovalWorkflowsSummaryResponse{
ID: approvalWorkflowsReq.ID,
Name: approvalWorkflowsReq.Name,
Description: approvalWorkflowsReq.Description,
IsActive: isActive,
StepCount: 0, // Default value, should be calculated if needed
}
}
return approvalWorkflowsRes
}

View File

@ -1,196 +0,0 @@
package repository
import (
"fmt"
"strings"
"github.com/google/uuid"
"github.com/rs/zerolog"
"web-medols-be/app/database"
"web-medols-be/app/database/entity"
approvalWorkflowStepsEntity "web-medols-be/app/database/entity"
"web-medols-be/app/module/approval_workflows/request"
"web-medols-be/utils/paginator"
)
type approvalWorkflowsRepository struct {
DB *database.Database
Log zerolog.Logger
}
// ApprovalWorkflowsRepository define interface of IApprovalWorkflowsRepository
type ApprovalWorkflowsRepository interface {
GetAll(clientId *uuid.UUID, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, err error)
FindDefault(clientId *uuid.UUID) (workflow *entity.ApprovalWorkflows, err error)
GetDefault(clientId *uuid.UUID) (workflow *entity.ApprovalWorkflows, err error)
GetWorkflowSteps(clientId *uuid.UUID, workflowId uint) (steps []*entity.ApprovalWorkflowSteps, err error)
Create(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows) (workflowReturn *entity.ApprovalWorkflows, err error)
Update(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows) (err error)
Delete(clientId *uuid.UUID, id uint) (err error)
SetDefault(clientId *uuid.UUID, id uint) (err error)
}
func NewApprovalWorkflowsRepository(db *database.Database, log zerolog.Logger) ApprovalWorkflowsRepository {
return &approvalWorkflowsRepository{
DB: db,
Log: log,
}
}
// implement interface of IApprovalWorkflowsRepository
func (_i *approvalWorkflowsRepository) GetAll(clientId *uuid.UUID, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, paging paginator.Pagination, err error) {
var count int64
query := _i.DB.DB.Model(&entity.ApprovalWorkflows{})
// Add client filter
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("approval_workflows.is_active = ?", true)
if req.Name != nil && *req.Name != "" {
name := strings.ToLower(*req.Name)
query = query.Where("LOWER(approval_workflows.name) LIKE ?", "%"+strings.ToLower(name)+"%")
}
if req.Description != nil && *req.Description != "" {
description := strings.ToLower(*req.Description)
query = query.Where("LOWER(approval_workflows.description) LIKE ?", "%"+strings.ToLower(description)+"%")
}
if req.IsActive != nil {
query = query.Where("approval_workflows.is_active = ?", req.IsActive)
}
if req.IsDefault != nil {
query = query.Where("approval_workflows.is_default = ?", req.IsDefault)
}
query.Count(&count)
if req.Pagination.SortBy != "" {
direction := "ASC"
if req.Pagination.Sort == "desc" {
direction = "DESC"
}
query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction))
} else {
direction := "DESC"
sortBy := "approval_workflows.is_default DESC, approval_workflows.created_at"
query.Order(fmt.Sprintf("%s %s", sortBy, direction))
}
req.Pagination.Count = count
req.Pagination = paginator.Paging(req.Pagination)
err = query.Preload("Steps").Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&workflows).Error
if err != nil {
return
}
paging = *req.Pagination
return
}
func (_i *approvalWorkflowsRepository) FindOne(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("is_active = ?", true)
query = query.Preload("Steps")
err = query.First(&workflow, id).Error
return workflow, err
}
func (_i *approvalWorkflowsRepository) FindDefault(clientId *uuid.UUID) (workflow *entity.ApprovalWorkflows, err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("is_active = ? AND is_default = ?", true, true)
query = query.Preload("Steps")
err = query.First(&workflow).Error
return workflow, err
}
func (_i *approvalWorkflowsRepository) Create(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows) (workflowReturn *entity.ApprovalWorkflows, err error) {
workflow.ClientId = clientId
err = _i.DB.DB.Create(&workflow).Error
return workflow, err
}
func (_i *approvalWorkflowsRepository) Update(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows) (err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
err = query.Where("id = ?", id).Updates(workflow).Error
return err
}
func (_i *approvalWorkflowsRepository) Delete(clientId *uuid.UUID, id uint) (err error) {
query := _i.DB.DB.Model(&entity.ApprovalWorkflows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
isActive := false
err = query.Where("id = ?", id).Update("is_active", isActive).Error
return err
}
func (_i *approvalWorkflowsRepository) SetDefault(clientId *uuid.UUID, id uint) (err error) {
// First, unset all default workflows
query := _i.DB.DB.Model(&entity.ApprovalWorkflows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
err = query.Update("is_default", false).Error
if err != nil {
return err
}
// Then set the specified workflow as default
query = _i.DB.DB.Model(&entity.ApprovalWorkflows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
err = query.Where("id = ?", id).Update("is_default", true).Error
return err
}
// GetDefault gets the default workflow for a client
func (_i *approvalWorkflowsRepository) GetDefault(clientId *uuid.UUID) (workflow *entity.ApprovalWorkflows, err error) {
return _i.FindDefault(clientId)
}
// GetWorkflowSteps gets all steps for a specific workflow
func (_i *approvalWorkflowsRepository) GetWorkflowSteps(clientId *uuid.UUID, workflowId uint) (steps []*entity.ApprovalWorkflowSteps, err error) {
query := _i.DB.DB.Model(&approvalWorkflowStepsEntity.ApprovalWorkflowSteps{})
// Join with approval_workflows to check client_id
query = query.Joins("JOIN approval_workflows ON approval_workflow_steps.workflow_id = approval_workflows.id")
if clientId != nil {
query = query.Where("approval_workflows.client_id = ?", clientId)
}
query = query.Where("approval_workflow_steps.workflow_id = ?", workflowId)
query = query.Where("approval_workflows.is_active = ?", true)
query = query.Order("approval_workflow_steps.step_order ASC")
// Preload the RequiredUserLevel relation
query = query.Preload("RequiredUserLevel")
err = query.Find(&steps).Error
return steps, err
}

View File

@ -1,195 +0,0 @@
package request
import (
"strconv"
"time"
"web-medols-be/app/database/entity"
"web-medols-be/utils/paginator"
)
type ApprovalWorkflowsGeneric interface {
ToEntity()
}
type ApprovalWorkflowsQueryRequest struct {
Name *string `json:"name"`
Description *string `json:"description"`
IsActive *bool `json:"isActive"`
IsDefault *bool `json:"isDefault"`
Pagination *paginator.Pagination `json:"pagination"`
}
type ApprovalWorkflowsCreateRequest struct {
Name string `json:"name" validate:"required"`
Description string `json:"description" validate:"required"`
IsActive *bool `json:"isActive"`
IsDefault *bool `json:"isDefault"`
RequiresApproval *bool `json:"requiresApproval"`
AutoPublish *bool `json:"autoPublish"`
Steps []ApprovalWorkflowStepRequest `json:"steps"`
}
func (req ApprovalWorkflowsCreateRequest) ToEntity() *entity.ApprovalWorkflows {
return &entity.ApprovalWorkflows{
Name: req.Name,
Description: &req.Description,
IsActive: req.IsActive,
IsDefault: req.IsDefault,
RequiresApproval: req.RequiresApproval,
AutoPublish: req.AutoPublish,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
func (req ApprovalWorkflowsCreateRequest) ToStepsEntity() []*entity.ApprovalWorkflowSteps {
steps := make([]*entity.ApprovalWorkflowSteps, len(req.Steps))
for i, stepReq := range req.Steps {
steps[i] = &entity.ApprovalWorkflowSteps{
StepOrder: stepReq.StepOrder,
StepName: stepReq.StepName,
RequiredUserLevelId: stepReq.RequiredUserLevelId,
CanSkip: stepReq.CanSkip,
AutoApproveAfterHours: stepReq.AutoApproveAfterHours,
IsActive: stepReq.IsActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
return steps
}
type ApprovalWorkflowsUpdateRequest struct {
Name string `json:"name" validate:"required"`
Description string `json:"description" validate:"required"`
IsActive *bool `json:"isActive"`
IsDefault *bool `json:"isDefault"`
RequiresApproval *bool `json:"requiresApproval"`
AutoPublish *bool `json:"autoPublish"`
}
func (req ApprovalWorkflowsUpdateRequest) ToEntity() *entity.ApprovalWorkflows {
return &entity.ApprovalWorkflows{
Name: req.Name,
Description: &req.Description,
IsActive: req.IsActive,
IsDefault: req.IsDefault,
RequiresApproval: req.RequiresApproval,
AutoPublish: req.AutoPublish,
UpdatedAt: time.Now(),
}
}
type ApprovalWorkflowStepRequest struct {
StepOrder int `json:"stepOrder" validate:"required"`
StepName string `json:"stepName" validate:"required"`
RequiredUserLevelId uint `json:"requiredUserLevelId" validate:"required"`
CanSkip *bool `json:"canSkip"`
AutoApproveAfterHours *int `json:"autoApproveAfterHours"`
IsActive *bool `json:"isActive"`
}
func (req ApprovalWorkflowStepRequest) ToEntity(workflowId uint) *entity.ApprovalWorkflowSteps {
return &entity.ApprovalWorkflowSteps{
WorkflowId: workflowId,
StepOrder: req.StepOrder,
StepName: req.StepName,
RequiredUserLevelId: req.RequiredUserLevelId,
CanSkip: req.CanSkip,
AutoApproveAfterHours: req.AutoApproveAfterHours,
IsActive: req.IsActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
type ApprovalWorkflowsWithStepsCreateRequest struct {
Name string `json:"name" validate:"required"`
Description string `json:"description" validate:"required"`
IsActive *bool `json:"isActive"`
IsDefault *bool `json:"isDefault"`
RequiresApproval *bool `json:"requiresApproval"`
AutoPublish *bool `json:"autoPublish"`
Steps []ApprovalWorkflowStepRequest `json:"steps" validate:"required,min=1"`
}
func (req ApprovalWorkflowsWithStepsCreateRequest) ToEntity() *entity.ApprovalWorkflows {
return &entity.ApprovalWorkflows{
Name: req.Name,
Description: &req.Description,
IsActive: req.IsActive,
IsDefault: req.IsDefault,
RequiresApproval: req.RequiresApproval,
AutoPublish: req.AutoPublish,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
func (req ApprovalWorkflowsWithStepsCreateRequest) ToStepsEntity() []*entity.ApprovalWorkflowSteps {
steps := make([]*entity.ApprovalWorkflowSteps, len(req.Steps))
for i, stepReq := range req.Steps {
steps[i] = &entity.ApprovalWorkflowSteps{
StepOrder: stepReq.StepOrder,
StepName: stepReq.StepName,
RequiredUserLevelId: stepReq.RequiredUserLevelId,
CanSkip: stepReq.CanSkip,
AutoApproveAfterHours: stepReq.AutoApproveAfterHours,
IsActive: stepReq.IsActive,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
}
return steps
}
type ApprovalWorkflowsWithStepsUpdateRequest struct {
Name string `json:"name" validate:"required"`
Description string `json:"description" validate:"required"`
IsActive *bool `json:"isActive"`
IsDefault *bool `json:"isDefault"`
RequiresApproval *bool `json:"requiresApproval"`
AutoPublish *bool `json:"autoPublish"`
Steps []ApprovalWorkflowStepRequest `json:"steps" validate:"required,min=1"`
}
type ApprovalWorkflowsQueryRequestContext struct {
Name string `json:"name"`
Description string `json:"description"`
IsActive string `json:"isActive"`
IsDefault string `json:"isDefault"`
}
func (req ApprovalWorkflowsQueryRequestContext) ToParamRequest() ApprovalWorkflowsQueryRequest {
var name *string
var description *string
var isActive *bool
var isDefault *bool
if req.Name != "" {
name = &req.Name
}
if req.Description != "" {
description = &req.Description
}
if req.IsActive != "" {
if parsedIsActive, err := strconv.ParseBool(req.IsActive); err == nil {
isActive = &parsedIsActive
}
}
if req.IsDefault != "" {
if parsedIsDefault, err := strconv.ParseBool(req.IsDefault); err == nil {
isDefault = &parsedIsDefault
}
}
return ApprovalWorkflowsQueryRequest{
Name: name,
Description: description,
IsActive: isActive,
IsDefault: isDefault,
}
}

View File

@ -1,48 +0,0 @@
package response
import (
"time"
approvalWorkflowStepsResponse "web-medols-be/app/module/approval_workflow_steps/response"
)
type ApprovalWorkflowsResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Description *string `json:"description"`
IsActive bool `json:"isActive"`
CreatedBy uint `json:"createdBy"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
// Relations
Steps []*approvalWorkflowStepsResponse.ApprovalWorkflowStepsResponse `json:"steps,omitempty"`
}
type ApprovalWorkflowsWithStepsResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Description *string `json:"description"`
IsActive bool `json:"isActive"`
CreatedBy uint `json:"createdBy"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
// Relations
Steps []*approvalWorkflowStepsResponse.ApprovalWorkflowStepsResponse `json:"steps"`
}
type ApprovalWorkflowsSummaryResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
Description *string `json:"description"`
IsActive bool `json:"isActive"`
StepCount int `json:"stepCount"`
}
type ApprovalWorkflowsStatsResponse struct {
TotalWorkflows int `json:"totalWorkflows"`
ActiveWorkflows int `json:"activeWorkflows"`
InactiveWorkflows int `json:"inactiveWorkflows"`
TotalSteps int `json:"totalSteps"`
AverageStepsPerFlow int `json:"averageStepsPerFlow"`
}

View File

@ -1,319 +0,0 @@
package service
import (
"errors"
"fmt"
"github.com/google/uuid"
"github.com/rs/zerolog"
"web-medols-be/app/database/entity"
"web-medols-be/app/module/approval_workflows/repository"
"web-medols-be/app/module/approval_workflows/request"
stepRepo "web-medols-be/app/module/approval_workflow_steps/repository"
"web-medols-be/utils/paginator"
)
type approvalWorkflowsService struct {
ApprovalWorkflowsRepository repository.ApprovalWorkflowsRepository
ApprovalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository
Log zerolog.Logger
}
// ApprovalWorkflowsService define interface of IApprovalWorkflowsService
type ApprovalWorkflowsService interface {
// Basic CRUD
GetAll(clientId *uuid.UUID, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, err error)
Create(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error)
Update(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows) (err error)
Delete(clientId *uuid.UUID, id uint) (err error)
// Workflow management
GetDefault(clientId *uuid.UUID) (workflow *entity.ApprovalWorkflows, err error)
SetDefault(clientId *uuid.UUID, id uint) (err error)
ActivateWorkflow(clientId *uuid.UUID, id uint) (err error)
DeactivateWorkflow(clientId *uuid.UUID, id uint) (err error)
// Workflow with steps
GetWorkflowWithSteps(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps, err error)
CreateWorkflowWithSteps(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error)
UpdateWorkflowWithSteps(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (err error)
// Validation
ValidateWorkflow(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error)
CanDeleteWorkflow(clientId *uuid.UUID, id uint) (canDelete bool, reason string, err error)
}
func NewApprovalWorkflowsService(
approvalWorkflowsRepository repository.ApprovalWorkflowsRepository,
approvalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository,
log zerolog.Logger,
) ApprovalWorkflowsService {
return &approvalWorkflowsService{
ApprovalWorkflowsRepository: approvalWorkflowsRepository,
ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository,
Log: log,
}
}
// Basic CRUD implementations
func (_i *approvalWorkflowsService) GetAll(clientId *uuid.UUID, req request.ApprovalWorkflowsQueryRequest) (workflows []*entity.ApprovalWorkflows, paging paginator.Pagination, err error) {
return _i.ApprovalWorkflowsRepository.GetAll(clientId, req)
}
func (_i *approvalWorkflowsService) FindOne(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, err error) {
return _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
}
func (_i *approvalWorkflowsService) Create(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error) {
// Validate workflow and steps
isValid, validationErrors, err := _i.ValidateWorkflow(clientId, workflow, steps)
if err != nil {
return nil, err
}
if !isValid {
return nil, errors.New(fmt.Sprintf("Validation failed: %v", validationErrors))
}
// Create workflow
workflowReturn, err = _i.ApprovalWorkflowsRepository.Create(clientId, workflow)
if err != nil {
return nil, err
}
// Create steps
for i, step := range steps {
step.WorkflowId = workflowReturn.ID
step.StepOrder = i + 1
_, err = _i.ApprovalWorkflowStepsRepository.Create(clientId, step)
if err != nil {
// Rollback workflow creation if step creation fails
_i.ApprovalWorkflowsRepository.Delete(clientId, workflowReturn.ID)
return nil, err
}
}
return workflowReturn, nil
}
func (_i *approvalWorkflowsService) Update(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows) (err error) {
// Check if workflow exists
existingWorkflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
if err != nil {
return err
}
if existingWorkflow == nil {
return errors.New("workflow not found")
}
return _i.ApprovalWorkflowsRepository.Update(clientId, id, workflow)
}
func (_i *approvalWorkflowsService) Delete(clientId *uuid.UUID, id uint) (err error) {
// Check if workflow can be deleted
canDelete, reason, err := _i.CanDeleteWorkflow(clientId, id)
if err != nil {
return err
}
if !canDelete {
return errors.New(reason)
}
return _i.ApprovalWorkflowsRepository.Delete(clientId, id)
}
// Workflow management
func (_i *approvalWorkflowsService) GetDefault(clientId *uuid.UUID) (workflow *entity.ApprovalWorkflows, err error) {
return _i.ApprovalWorkflowsRepository.FindDefault(clientId)
}
func (_i *approvalWorkflowsService) SetDefault(clientId *uuid.UUID, id uint) (err error) {
// Check if workflow exists and is active
workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
if err != nil {
return err
}
if workflow == nil {
return errors.New("workflow not found")
}
if workflow.IsActive == nil || !*workflow.IsActive {
return errors.New("cannot set inactive workflow as default")
}
return _i.ApprovalWorkflowsRepository.SetDefault(clientId, id)
}
func (_i *approvalWorkflowsService) ActivateWorkflow(clientId *uuid.UUID, id uint) (err error) {
// Validate workflow before activation
workflow, err := _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
if err != nil {
return err
}
if workflow == nil {
return errors.New("workflow not found")
}
// Get workflow steps and validate
steps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id)
if err != nil {
return err
}
isValid, validationErrors, err := _i.ValidateWorkflow(clientId, workflow, steps)
if err != nil {
return err
}
if !isValid {
return errors.New(fmt.Sprintf("Cannot activate invalid workflow: %v", validationErrors))
}
// Activate workflow
isActive := true
updateData := &entity.ApprovalWorkflows{IsActive: &isActive}
return _i.ApprovalWorkflowsRepository.Update(clientId, id, updateData)
}
func (_i *approvalWorkflowsService) DeactivateWorkflow(clientId *uuid.UUID, id uint) (err error) {
// Check if this is the default workflow
defaultWorkflow, err := _i.ApprovalWorkflowsRepository.FindDefault(clientId)
if err != nil {
return err
}
if defaultWorkflow != nil && defaultWorkflow.ID == id {
return errors.New("cannot deactivate default workflow")
}
// Check if workflow is being used in active approval flows
canDelete, reason, err := _i.CanDeleteWorkflow(clientId, id)
if err != nil {
return err
}
if !canDelete {
return errors.New(fmt.Sprintf("Cannot deactivate workflow: %s", reason))
}
// Deactivate workflow
isActive := false
updateData := &entity.ApprovalWorkflows{IsActive: &isActive}
return _i.ApprovalWorkflowsRepository.Update(clientId, id, updateData)
}
// Workflow with steps
func (_i *approvalWorkflowsService) GetWorkflowWithSteps(clientId *uuid.UUID, id uint) (workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps, err error) {
workflow, err = _i.ApprovalWorkflowsRepository.FindOne(clientId, id)
if err != nil {
return nil, nil, err
}
steps, err = _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id)
if err != nil {
return nil, nil, err
}
return workflow, steps, nil
}
func (_i *approvalWorkflowsService) CreateWorkflowWithSteps(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (workflowReturn *entity.ApprovalWorkflows, err error) {
return _i.Create(clientId, workflow, steps)
}
func (_i *approvalWorkflowsService) UpdateWorkflowWithSteps(clientId *uuid.UUID, id uint, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (err error) {
// Update workflow
err = _i.Update(clientId, id, workflow)
if err != nil {
return err
}
// Get existing steps
existingSteps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, id)
if err != nil {
return err
}
// Delete existing steps (simplified approach - in production, you might want to update/merge)
for _, existingStep := range existingSteps {
err = _i.ApprovalWorkflowStepsRepository.Delete(clientId, existingStep.ID)
if err != nil {
return err
}
}
// Create new steps
for i, step := range steps {
step.WorkflowId = id
step.StepOrder = i + 1
_, err = _i.ApprovalWorkflowStepsRepository.Create(clientId, step)
if err != nil {
return err
}
}
return nil
}
// Validation
func (_i *approvalWorkflowsService) ValidateWorkflow(clientId *uuid.UUID, workflow *entity.ApprovalWorkflows, steps []*entity.ApprovalWorkflowSteps) (isValid bool, errors []string, err error) {
errors = make([]string, 0)
// Validate workflow
if workflow.Name == "" {
errors = append(errors, "Workflow name is required")
}
// Validate steps
if len(steps) == 0 {
errors = append(errors, "Workflow must have at least one step")
} else {
// Check for duplicate step orders
stepOrderMap := make(map[int]bool)
for i, step := range steps {
expectedOrder := i + 1
if step.StepOrder != 0 && step.StepOrder != expectedOrder {
errors = append(errors, fmt.Sprintf("Step %d has incorrect order %d, expected %d", i+1, step.StepOrder, expectedOrder))
}
if stepOrderMap[step.StepOrder] {
errors = append(errors, fmt.Sprintf("Duplicate step order: %d", step.StepOrder))
}
stepOrderMap[step.StepOrder] = true
if step.StepName == "" {
errors = append(errors, fmt.Sprintf("Step %d name is required", i+1))
}
if step.RequiredUserLevelId == 0 {
errors = append(errors, fmt.Sprintf("Step %d must have a required user level", i+1))
}
}
}
isValid = len(errors) == 0
return isValid, errors, nil
}
func (_i *approvalWorkflowsService) CanDeleteWorkflow(clientId *uuid.UUID, id uint) (canDelete bool, reason string, err error) {
// Check if workflow is default
defaultWorkflow, err := _i.ApprovalWorkflowsRepository.FindDefault(clientId)
if err != nil {
return false, "", err
}
if defaultWorkflow != nil && defaultWorkflow.ID == id {
return false, "Cannot delete default workflow", nil
}
// Check if workflow is being used in active approval flows
// This would require a method in ArticleApprovalFlowsRepository
// For now, we'll assume it can be deleted
// TODO: Implement check for active approval flows
return true, "", nil
}

View File

@ -1,61 +0,0 @@
package article_approval_flows
import (
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
"web-medols-be/app/module/article_approval_flows/controller"
"web-medols-be/app/module/article_approval_flows/repository"
"web-medols-be/app/module/article_approval_flows/service"
)
// ArticleApprovalFlowsRouter struct of ArticleApprovalFlowsRouter
type ArticleApprovalFlowsRouter struct {
App fiber.Router
Controller controller.ArticleApprovalFlowsController
}
// NewArticleApprovalFlowsModule register bulky of ArticleApprovalFlows module
var NewArticleApprovalFlowsModule = fx.Options(
// register repository of ArticleApprovalFlows module
fx.Provide(repository.NewArticleApprovalFlowsRepository),
// register service of ArticleApprovalFlows module
fx.Provide(service.NewArticleApprovalFlowsService),
// register controller of ArticleApprovalFlows module
fx.Provide(controller.NewArticleApprovalFlowsController),
// register router of ArticleApprovalFlows module
fx.Provide(NewArticleApprovalFlowsRouter),
)
// NewArticleApprovalFlowsRouter init ArticleApprovalFlowsRouter
func NewArticleApprovalFlowsRouter(fiber *fiber.App, controller controller.ArticleApprovalFlowsController) *ArticleApprovalFlowsRouter {
return &ArticleApprovalFlowsRouter{
App: fiber,
Controller: controller,
}
}
// RegisterArticleApprovalFlowsRoutes register routes of ArticleApprovalFlows module
func (_i *ArticleApprovalFlowsRouter) RegisterArticleApprovalFlowsRoutes() {
// define controllers
articleApprovalFlowsController := _i.Controller
// define routes
_i.App.Route("/article-approval-flows", func(router fiber.Router) {
router.Get("/", articleApprovalFlowsController.All)
router.Get("/my-queue", articleApprovalFlowsController.GetMyApprovalQueue)
router.Get("/pending", articleApprovalFlowsController.GetPendingApprovals)
router.Get("/history", articleApprovalFlowsController.GetApprovalHistory)
router.Get("/dashboard-stats", articleApprovalFlowsController.GetDashboardStats)
router.Get("/workload-stats", articleApprovalFlowsController.GetWorkloadStats)
router.Get("/analytics", articleApprovalFlowsController.GetApprovalAnalytics)
router.Get("/:id", articleApprovalFlowsController.Show)
router.Post("/submit", articleApprovalFlowsController.SubmitForApproval)
router.Put("/:id/approve", articleApprovalFlowsController.Approve)
router.Put("/:id/reject", articleApprovalFlowsController.Reject)
router.Put("/:id/request-revision", articleApprovalFlowsController.RequestRevision)
router.Put("/:id/resubmit", articleApprovalFlowsController.Resubmit)
})
}

View File

@ -1,664 +0,0 @@
package controller
import (
"strconv"
"web-medols-be/app/middleware"
"web-medols-be/app/module/article_approval_flows/request"
"web-medols-be/app/module/article_approval_flows/service"
usersRepository "web-medols-be/app/module/users/repository"
"web-medols-be/utils/paginator"
utilSvc "web-medols-be/utils/service"
"github.com/gofiber/fiber/v2"
"github.com/rs/zerolog"
utilRes "web-medols-be/utils/response"
utilVal "web-medols-be/utils/validator"
)
type articleApprovalFlowsController struct {
articleApprovalFlowsService service.ArticleApprovalFlowsService
UsersRepo usersRepository.UsersRepository
Log zerolog.Logger
}
type ArticleApprovalFlowsController interface {
All(c *fiber.Ctx) error
Show(c *fiber.Ctx) error
SubmitForApproval(c *fiber.Ctx) error
Approve(c *fiber.Ctx) error
Reject(c *fiber.Ctx) error
RequestRevision(c *fiber.Ctx) error
Resubmit(c *fiber.Ctx) error
GetMyApprovalQueue(c *fiber.Ctx) error
GetPendingApprovals(c *fiber.Ctx) error
GetApprovalHistory(c *fiber.Ctx) error
GetDashboardStats(c *fiber.Ctx) error
GetWorkloadStats(c *fiber.Ctx) error
GetApprovalAnalytics(c *fiber.Ctx) error
}
func NewArticleApprovalFlowsController(articleApprovalFlowsService service.ArticleApprovalFlowsService, usersRepo usersRepository.UsersRepository, log zerolog.Logger) ArticleApprovalFlowsController {
return &articleApprovalFlowsController{
articleApprovalFlowsService: articleApprovalFlowsService,
UsersRepo: usersRepo,
Log: log,
}
}
// All ArticleApprovalFlows
// @Summary Get all ArticleApprovalFlows
// @Description API for getting all ArticleApprovalFlows
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param req query request.ArticleApprovalFlowsQueryRequest false "query parameters"
// @Param req query paginator.Pagination false "pagination parameters"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows [get]
func (_i *articleApprovalFlowsController) All(c *fiber.Ctx) error {
paginate, err := paginator.Paginate(c)
if err != nil {
return err
}
reqContext := request.ArticleApprovalFlowsQueryRequestContext{
ArticleId: c.Query("articleId"),
WorkflowId: c.Query("workflowId"),
StatusId: c.Query("statusId"),
SubmittedBy: c.Query("submittedBy"),
CurrentStep: c.Query("currentStep"),
DateFrom: c.Query("dateFrom"),
DateTo: c.Query("dateTo"),
}
req := reqContext.ToParamRequest()
req.Pagination = paginate
// Get ClientId from context
clientId := middleware.GetClientID(c)
_i.Log.Info().Interface("clientId", clientId).Msg("")
articleApprovalFlowsData, paging, err := _i.articleApprovalFlowsService.GetAll(clientId, req)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalFlows list successfully retrieved"},
Data: articleApprovalFlowsData,
Meta: paging,
})
}
// Show ArticleApprovalFlows
// @Summary Get one ArticleApprovalFlows
// @Description API for getting one ArticleApprovalFlows
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ArticleApprovalFlows ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/{id} [get]
func (_i *articleApprovalFlowsController) Show(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
articleApprovalFlowsData, err := _i.articleApprovalFlowsService.FindOne(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalFlows successfully retrieved"},
Data: articleApprovalFlowsData,
})
}
// SubmitForApproval ArticleApprovalFlows
// @Summary Submit article for approval
// @Description API for submitting article for approval
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param req body request.SubmitForApprovalRequest true "Submit for approval data"
// @Success 201 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/submit [post]
func (_i *articleApprovalFlowsController) SubmitForApproval(c *fiber.Ctx) error {
req := new(request.SubmitForApprovalRequest)
if err := c.BodyParser(req); err != nil {
return utilRes.ErrorBadRequest(c, "Invalid request body")
}
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Get Authorization token from header and extract user ID
authToken := c.Get("Authorization")
if authToken == "" {
return utilRes.ErrorBadRequest(c, "Authorization token required")
}
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
if user == nil {
return utilRes.ErrorBadRequest(c, "Invalid authorization token")
}
var workflowId *uint
if req.WorkflowId != nil {
workflowIdVal := uint(*req.WorkflowId)
workflowId = &workflowIdVal
}
articleApprovalFlowsData, err := _i.articleApprovalFlowsService.SubmitArticleForApproval(clientId, uint(req.ArticleId), user.ID, workflowId)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Article successfully submitted for approval"},
Data: articleApprovalFlowsData,
})
}
// Approve ArticleApprovalFlows
// @Summary Approve article
// @Description API for approving article
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param id path int true "ArticleApprovalFlows ID"
// @Param req body request.ApprovalActionRequest true "Approval action data"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/{id}/approve [put]
func (_i *articleApprovalFlowsController) Approve(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
req := new(request.ApprovalActionRequest)
if err := c.BodyParser(req); err != nil {
return utilRes.ErrorBadRequest(c, "Invalid request body")
}
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Get Authorization token from header and extract user ID
authToken := c.Get("Authorization")
if authToken == "" {
return utilRes.ErrorBadRequest(c, "Authorization token required")
}
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
if user == nil {
return utilRes.ErrorBadRequest(c, "Invalid authorization token")
}
err = _i.articleApprovalFlowsService.ApproveStep(clientId, uint(id), user.ID, req.Message)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Article successfully approved"},
Data: nil,
})
}
// Reject ArticleApprovalFlows
// @Summary Reject article
// @Description API for rejecting article
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param id path int true "ArticleApprovalFlows ID"
// @Param req body request.RejectionRequest true "Rejection data"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/{id}/reject [put]
func (_i *articleApprovalFlowsController) Reject(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
req := new(request.RejectionRequest)
if err := c.BodyParser(req); err != nil {
return utilRes.ErrorBadRequest(c, "Invalid request body")
}
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Get Authorization token from header and extract user ID
authToken := c.Get("Authorization")
if authToken == "" {
return utilRes.ErrorBadRequest(c, "Authorization token required")
}
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
if user == nil {
return utilRes.ErrorBadRequest(c, "Invalid authorization token")
}
err = _i.articleApprovalFlowsService.RejectArticle(clientId, uint(id), user.ID, req.Reason)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Article successfully rejected"},
Data: nil,
})
}
// RequestRevision ArticleApprovalFlows
// @Summary Request revision for article
// @Description API for requesting revision for article
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param id path int true "ArticleApprovalFlows ID"
// @Param req body request.RevisionRequest true "Revision request data"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/{id}/request-revision [put]
func (_i *articleApprovalFlowsController) RequestRevision(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
req := new(request.RevisionRequest)
if err := c.BodyParser(req); err != nil {
return utilRes.ErrorBadRequest(c, "Invalid request body")
}
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Get Authorization token from header and extract user ID
authToken := c.Get("Authorization")
if authToken == "" {
return utilRes.ErrorBadRequest(c, "Authorization token required")
}
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
if user == nil {
return utilRes.ErrorBadRequest(c, "Invalid authorization token")
}
err = _i.articleApprovalFlowsService.RequestRevision(clientId, uint(id), user.ID, req.Message)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Revision successfully requested"},
Data: nil,
})
}
// Resubmit ArticleApprovalFlows
// @Summary Resubmit article after revision
// @Description API for resubmitting article after revision
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param id path int true "ArticleApprovalFlows ID"
// @Param req body request.ResubmitRequest true "Resubmit data"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/{id}/resubmit [put]
func (_i *articleApprovalFlowsController) Resubmit(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
req := new(request.ResubmitRequest)
if err := c.BodyParser(req); err != nil {
return utilRes.ErrorBadRequest(c, "Invalid request body")
}
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Get Authorization token from header and extract user ID
authToken := c.Get("Authorization")
if authToken == "" {
return utilRes.ErrorBadRequest(c, "Authorization token required")
}
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
if user == nil {
return utilRes.ErrorBadRequest(c, "Invalid authorization token")
}
err = _i.articleApprovalFlowsService.ResubmitAfterRevision(clientId, uint(id), user.ID)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Article successfully resubmitted"},
Data: nil,
})
}
// GetMyApprovalQueue ArticleApprovalFlows
// @Summary Get my approval queue
// @Description API for getting my approval queue
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param includePreview query bool false "Include article preview"
// @Param urgentOnly query bool false "Show only urgent articles"
// @Param req query paginator.Pagination false "pagination parameters"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/my-queue [get]
func (_i *articleApprovalFlowsController) GetMyApprovalQueue(c *fiber.Ctx) error {
paginate, err := paginator.Paginate(c)
if err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Get Authorization token from header and extract user level ID
authToken := c.Get("Authorization")
if authToken == "" {
return utilRes.ErrorBadRequest(c, "Authorization token required")
}
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
if user == nil {
return utilRes.ErrorBadRequest(c, "Invalid authorization token")
}
// Optional parameters
includePreview := c.QueryBool("includePreview", false)
urgentOnly := c.QueryBool("urgentOnly", false)
approvalQueueData, paging, err := _i.articleApprovalFlowsService.GetMyApprovalQueue(clientId, user.UserLevelId, paginate.Page, paginate.Limit, includePreview, urgentOnly)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"My approval queue successfully retrieved"},
Data: approvalQueueData,
Meta: paging,
})
}
// GetPendingApprovals ArticleApprovalFlows
// @Summary Get pending approvals
// @Description API for getting pending approvals
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param req query paginator.Pagination false "pagination parameters"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/pending [get]
func (_i *articleApprovalFlowsController) GetPendingApprovals(c *fiber.Ctx) error {
paginate, err := paginator.Paginate(c)
if err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Get Authorization token from header and extract user level ID
authToken := c.Get("Authorization")
if authToken == "" {
return utilRes.ErrorBadRequest(c, "Authorization token required")
}
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
if user == nil {
return utilRes.ErrorBadRequest(c, "Invalid authorization token")
}
filters := make(map[string]interface{})
pendingApprovalsData, paging, err := _i.articleApprovalFlowsService.GetPendingApprovals(clientId, user.UserLevelId, paginate.Page, paginate.Limit, filters)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Pending approvals successfully retrieved"},
Data: pendingApprovalsData,
Meta: paging,
})
}
// GetApprovalHistory ArticleApprovalFlows
// @Summary Get approval history
// @Description API for getting approval history
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param articleId query int false "Article ID filter"
// @Param userId query int false "User ID filter"
// @Param req query paginator.Pagination false "pagination parameters"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/history [get]
func (_i *articleApprovalFlowsController) GetApprovalHistory(c *fiber.Ctx) error {
paginate, err := paginator.Paginate(c)
if err != nil {
return err
}
articleId := 0
if articleIdStr := c.Query("articleId"); articleIdStr != "" {
articleId, err = strconv.Atoi(articleIdStr)
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid articleId format")
}
}
// userId parameter is not used in the current implementation
// userId := 0
// if userIdStr := c.Query("userId"); userIdStr != "" {
// userId, err := strconv.Atoi(userIdStr)
// if err != nil {
// return utilRes.ErrorBadRequest(c, "Invalid userId format")
// }
// }
// Get ClientId from context
clientId := middleware.GetClientID(c)
approvalHistoryData, paging, err := _i.articleApprovalFlowsService.GetApprovalHistory(clientId, uint(articleId), paginate.Page, paginate.Limit)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Approval history successfully retrieved"},
Data: approvalHistoryData,
Meta: paging,
})
}
// GetDashboardStats ArticleApprovalFlows
// @Summary Get dashboard statistics
// @Description API for getting dashboard statistics
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/dashboard-stats [get]
func (_i *articleApprovalFlowsController) GetDashboardStats(c *fiber.Ctx) error {
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Get Authorization token from header and extract user level ID
authToken := c.Get("Authorization")
if authToken == "" {
return utilRes.ErrorBadRequest(c, "Authorization token required")
}
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
if user == nil {
return utilRes.ErrorBadRequest(c, "Invalid authorization token")
}
// TODO: Implement GetDashboardStats method in service
_ = clientId // suppress unused variable warning
_ = user.UserLevelId // suppress unused variable warning
// dashboardStatsData, err := _i.articleApprovalFlowsService.GetDashboardStats(clientId, user.UserLevelId)
// if err != nil {
// return err
// }
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Dashboard statistics successfully retrieved"},
Data: nil,
})
}
// GetWorkloadStats ArticleApprovalFlows
// @Summary Get workload statistics
// @Description API for getting workload statistics
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/workload-stats [get]
func (_i *articleApprovalFlowsController) GetWorkloadStats(c *fiber.Ctx) error {
// Get ClientId from context
clientId := middleware.GetClientID(c)
// TODO: Implement GetWorkloadStats method in service
_ = clientId // suppress unused variable warning
// workloadStatsData, err := _i.articleApprovalFlowsService.GetWorkloadStats(clientId)
// if err != nil {
// return err
// }
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Workload statistics successfully retrieved"},
Data: nil,
})
}
// GetApprovalAnalytics ArticleApprovalFlows
// @Summary Get approval analytics
// @Description API for getting approval analytics
// @Tags ArticleApprovalFlows
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param period query string false "Period filter (daily, weekly, monthly)"
// @Param startDate query string false "Start date filter (YYYY-MM-DD)"
// @Param endDate query string false "End date filter (YYYY-MM-DD)"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-flows/analytics [get]
func (_i *articleApprovalFlowsController) GetApprovalAnalytics(c *fiber.Ctx) error {
// period := c.Query("period", "monthly")
// startDate := c.Query("startDate")
// endDate := c.Query("endDate")
// Get ClientId from context
clientId := middleware.GetClientID(c)
// TODO: Implement GetApprovalAnalytics method in service
_ = clientId // suppress unused variable warning
// analyticsData, err := _i.articleApprovalFlowsService.GetApprovalAnalytics(clientId, period, startDate, endDate)
// if err != nil {
// return err
// }
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Approval analytics successfully retrieved"},
Data: nil,
})
}

View File

@ -1,257 +0,0 @@
package mapper
import (
"time"
"github.com/google/uuid"
"github.com/rs/zerolog"
"web-medols-be/app/database/entity"
articleApprovalStepLogsResponse "web-medols-be/app/module/article_approval_step_logs/response"
articleApprovalStepLogsRepository "web-medols-be/app/module/article_approval_step_logs/repository"
approvalWorkflowsRepository "web-medols-be/app/module/approval_workflows/repository"
approvalWorkflowsResponse "web-medols-be/app/module/approval_workflows/response"
articlesRepository "web-medols-be/app/module/articles/repository"
articlesResponse "web-medols-be/app/module/articles/response"
usersRepository "web-medols-be/app/module/users/repository"
res "web-medols-be/app/module/article_approval_flows/response"
)
func ArticleApprovalFlowsResponseMapper(
log zerolog.Logger,
host string,
clientId *uuid.UUID,
articleApprovalFlowsReq *entity.ArticleApprovalFlows,
articlesRepo articlesRepository.ArticlesRepository,
approvalWorkflowsRepo approvalWorkflowsRepository.ApprovalWorkflowsRepository,
articleApprovalStepLogsRepo articleApprovalStepLogsRepository.ArticleApprovalStepLogsRepository,
usersRepo usersRepository.UsersRepository,
) (articleApprovalFlowsRes *res.ArticleApprovalFlowsResponse) {
// Get article information
var articleRes *articlesResponse.ArticlesResponse
if articleApprovalFlowsReq.ArticleId != 0 {
article, _ := articlesRepo.FindOne(clientId, articleApprovalFlowsReq.ArticleId)
if article != nil {
// Note: This would need additional repositories for full mapping
// For now, we'll create a basic response
articleRes = &articlesResponse.ArticlesResponse{
ID: article.ID,
Title: article.Title,
Slug: article.Slug,
Description: article.Description,
StatusId: article.StatusId,
IsActive: article.IsActive,
CreatedAt: article.CreatedAt,
UpdatedAt: article.UpdatedAt,
}
}
}
// Get approval workflow information
var approvalWorkflowRes *approvalWorkflowsResponse.ApprovalWorkflowsResponse
if articleApprovalFlowsReq.WorkflowId != 0 {
approvalWorkflow, _ := approvalWorkflowsRepo.FindOne(clientId, articleApprovalFlowsReq.WorkflowId)
if approvalWorkflow != nil {
// Note: This would need additional repositories for full mapping
// For now, we'll create a basic response
approvalWorkflowRes = &approvalWorkflowsResponse.ApprovalWorkflowsResponse{
ID: approvalWorkflow.ID,
Name: approvalWorkflow.Name,
Description: approvalWorkflow.Description,
IsActive: *approvalWorkflow.IsActive,
CreatedAt: approvalWorkflow.CreatedAt,
UpdatedAt: approvalWorkflow.UpdatedAt,
}
}
}
// Get step logs
var stepLogsArr []*articleApprovalStepLogsResponse.ArticleApprovalStepLogsResponse
if articleApprovalFlowsReq != nil {
// Map status ID to status string
statusStr := "pending"
switch articleApprovalFlowsReq.StatusId {
case 1:
statusStr = "pending"
case 2:
statusStr = "approved"
case 3:
statusStr = "rejected"
case 4:
statusStr = "revision_requested"
}
articleApprovalFlowsRes = &res.ArticleApprovalFlowsResponse{
ID: articleApprovalFlowsReq.ID,
ArticleID: articleApprovalFlowsReq.ArticleId,
WorkflowID: articleApprovalFlowsReq.WorkflowId,
CurrentStep: articleApprovalFlowsReq.CurrentStep,
Status: statusStr,
SubmittedBy: articleApprovalFlowsReq.SubmittedById,
SubmittedAt: articleApprovalFlowsReq.SubmittedAt,
CompletedAt: articleApprovalFlowsReq.CompletedAt,
RejectionReason: articleApprovalFlowsReq.RejectionReason,
RevisionReason: articleApprovalFlowsReq.RevisionMessage,
CreatedAt: articleApprovalFlowsReq.CreatedAt,
UpdatedAt: articleApprovalFlowsReq.UpdatedAt,
Article: articleRes,
Workflow: approvalWorkflowRes,
StepLogs: stepLogsArr,
}
}
return articleApprovalFlowsRes
}
func ArticleApprovalFlowsDetailResponseMapper(
log zerolog.Logger,
host string,
clientId *uuid.UUID,
articleApprovalFlowsReq *entity.ArticleApprovalFlows,
articlesRepo articlesRepository.ArticlesRepository,
approvalWorkflowsRepo approvalWorkflowsRepository.ApprovalWorkflowsRepository,
articleApprovalStepLogsRepo articleApprovalStepLogsRepository.ArticleApprovalStepLogsRepository,
usersRepo usersRepository.UsersRepository,
) (articleApprovalFlowsRes *res.ArticleApprovalFlowsDetailResponse) {
submittedByName := ""
if articleApprovalFlowsReq.SubmittedById != 0 {
findUser, _ := usersRepo.FindOne(clientId, articleApprovalFlowsReq.SubmittedById)
if findUser != nil {
submittedByName = findUser.Fullname
}
}
if articleApprovalFlowsReq.SubmittedById != 0 {
findUser, _ := usersRepo.FindOne(clientId, articleApprovalFlowsReq.SubmittedById)
if findUser != nil {
submittedByName = findUser.Fullname
}
}
// Get article information with full details
var articleRes *articlesResponse.ArticlesResponse
if articleApprovalFlowsReq.ArticleId != 0 {
article, _ := articlesRepo.FindOne(clientId, articleApprovalFlowsReq.ArticleId)
if article != nil {
// Note: This would need additional repositories for full mapping
// For now, we'll create a basic response
articleRes = &articlesResponse.ArticlesResponse{
ID: article.ID,
Title: article.Title,
Slug: article.Slug,
Description: article.Description,
StatusId: article.StatusId,
IsActive: article.IsActive,
CreatedAt: article.CreatedAt,
UpdatedAt: article.UpdatedAt,
}
}
}
// Get approval workflow information with full details
var approvalWorkflowRes *approvalWorkflowsResponse.ApprovalWorkflowsWithStepsResponse
if articleApprovalFlowsReq.WorkflowId != 0 {
approvalWorkflow, _ := approvalWorkflowsRepo.FindOne(clientId, articleApprovalFlowsReq.WorkflowId)
if approvalWorkflow != nil {
// Note: This would need additional repositories for full mapping
// For now, we'll create a basic response
approvalWorkflowRes = &approvalWorkflowsResponse.ApprovalWorkflowsWithStepsResponse{
ID: approvalWorkflow.ID,
Name: approvalWorkflow.Name,
Description: approvalWorkflow.Description,
IsActive: *approvalWorkflow.IsActive,
CreatedAt: approvalWorkflow.CreatedAt,
UpdatedAt: approvalWorkflow.UpdatedAt,
}
}
}
// Get step logs with detailed information
var stepLogsArr []*articleApprovalStepLogsResponse.ArticleApprovalStepLogsResponse
if articleApprovalFlowsReq != nil {
// Map status ID to status string
statusStr := "pending"
switch articleApprovalFlowsReq.StatusId {
case 1:
statusStr = "pending"
case 2:
statusStr = "approved"
case 3:
statusStr = "rejected"
case 4:
statusStr = "revision_requested"
}
articleApprovalFlowsRes = &res.ArticleApprovalFlowsDetailResponse{
ID: articleApprovalFlowsReq.ID,
ArticleID: articleApprovalFlowsReq.ArticleId,
WorkflowID: articleApprovalFlowsReq.WorkflowId,
CurrentStep: articleApprovalFlowsReq.CurrentStep,
Status: statusStr,
SubmittedBy: articleApprovalFlowsReq.SubmittedById,
SubmittedByName: submittedByName,
SubmittedAt: articleApprovalFlowsReq.SubmittedAt,
CompletedAt: articleApprovalFlowsReq.CompletedAt,
RejectionReason: articleApprovalFlowsReq.RejectionReason,
RevisionReason: articleApprovalFlowsReq.RevisionMessage,
CreatedAt: articleApprovalFlowsReq.CreatedAt,
UpdatedAt: articleApprovalFlowsReq.UpdatedAt,
Article: articleRes,
Workflow: approvalWorkflowRes,
StepLogs: stepLogsArr,
}
}
return articleApprovalFlowsRes
}
func ArticleApprovalFlowsSummaryResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
articleApprovalFlowsReq *entity.ArticleApprovalFlows,
usersRepo usersRepository.UsersRepository,
) (articleApprovalFlowsRes *res.ArticleApprovalFlowsSummaryResponse) {
submittedByName := ""
if articleApprovalFlowsReq.SubmittedById != 0 {
findUser, _ := usersRepo.FindOne(clientId, articleApprovalFlowsReq.SubmittedById)
if findUser != nil {
submittedByName = findUser.Fullname
}
}
if articleApprovalFlowsReq != nil {
// Map status ID to status string
statusStr := "pending"
switch articleApprovalFlowsReq.StatusId {
case 1:
statusStr = "pending"
case 2:
statusStr = "approved"
case 3:
statusStr = "rejected"
case 4:
statusStr = "revision_requested"
}
// Calculate days in approval
daysInApproval := int(time.Since(articleApprovalFlowsReq.SubmittedAt).Hours() / 24)
articleApprovalFlowsRes = &res.ArticleApprovalFlowsSummaryResponse{
ID: articleApprovalFlowsReq.ID,
ArticleID: articleApprovalFlowsReq.ArticleId,
CurrentStep: articleApprovalFlowsReq.CurrentStep,
Status: statusStr,
SubmittedBy: articleApprovalFlowsReq.SubmittedById,
SubmittedByName: submittedByName,
SubmittedAt: articleApprovalFlowsReq.SubmittedAt,
DaysInApproval: daysInApproval,
}
}
return articleApprovalFlowsRes
}

View File

@ -1,420 +0,0 @@
package repository
import (
"fmt"
"time"
"web-medols-be/app/database"
"web-medols-be/app/database/entity"
"web-medols-be/app/module/article_approval_flows/request"
"web-medols-be/utils/paginator"
"github.com/google/uuid"
"github.com/rs/zerolog"
)
type articleApprovalFlowsRepository struct {
DB *database.Database
Log zerolog.Logger
}
// ArticleApprovalFlowsRepository define interface of IArticleApprovalFlowsRepository
type ArticleApprovalFlowsRepository interface {
// Basic CRUD
GetAll(clientId *uuid.UUID, req request.ArticleApprovalFlowsQueryRequest) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (flow *entity.ArticleApprovalFlows, err error)
FindActiveByArticleId(articleId uint) (flow *entity.ArticleApprovalFlows, err error)
FindByArticleId(clientId *uuid.UUID, articleId uint) (flow *entity.ArticleApprovalFlows, err error)
Create(clientId *uuid.UUID, flow *entity.ArticleApprovalFlows) (flowReturn *entity.ArticleApprovalFlows, err error)
Update(id uint, flow *entity.ArticleApprovalFlows) (err error)
Delete(clientId *uuid.UUID, id uint) (err error)
// Approval Queue Methods
GetPendingApprovals(clientId *uuid.UUID, userLevelId uint, page, limit int, filters map[string]interface{}) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error)
GetPendingApprovalsByUserLevel(clientId *uuid.UUID, userLevelId uint, page, limit int) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error)
GetMyApprovalQueue(clientId *uuid.UUID, userLevelId uint, page, limit int, includePreview bool, urgentOnly bool) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error)
// Statistics Methods
GetPendingCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error)
GetOverdueCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error)
GetApprovedCountByPeriod(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (count int64, err error)
GetRejectedCountByPeriod(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (count int64, err error)
GetRevisionRequestCountByPeriod(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (count int64, err error)
// Workload Methods
GetLevelWorkload(clientId *uuid.UUID, userLevelId uint) (workload map[string]interface{}, err error)
GetTeamWorkloadComparison(clientId *uuid.UUID, userLevelId uint) (comparison []map[string]interface{}, err error)
GetWorkflowBottlenecks(clientId *uuid.UUID) (bottlenecks []map[string]interface{}, err error)
}
func NewArticleApprovalFlowsRepository(db *database.Database, log zerolog.Logger) ArticleApprovalFlowsRepository {
return &articleApprovalFlowsRepository{
DB: db,
Log: log,
}
}
// Basic CRUD implementations
func (_i *articleApprovalFlowsRepository) GetAll(clientId *uuid.UUID, req request.ArticleApprovalFlowsQueryRequest) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) {
var count int64
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
// Apply filters from request
if req.ArticleId != nil && *req.ArticleId > 0 {
query = query.Where("article_id = ?", *req.ArticleId)
}
if req.StatusId != nil && *req.StatusId > 0 {
query = query.Where("status_id = ?", *req.StatusId)
}
if req.SubmittedBy != nil && *req.SubmittedBy > 0 {
query = query.Where("submitted_by_id = ?", *req.SubmittedBy)
}
if req.CurrentStep != nil && *req.CurrentStep > 0 {
query = query.Where("current_step = ?", *req.CurrentStep)
}
if req.DateFrom != nil {
query = query.Where("submitted_at >= ?", *req.DateFrom)
}
if req.WorkflowId != nil {
query = query.Where("workflow_id = ?", *req.WorkflowId)
}
if req.DateTo != nil {
query = query.Where("submitted_at <= ?", *req.DateTo)
}
// Preload related data
query = query.Preload("Article").Preload("Workflow").Preload("SubmittedBy").Preload("StepLogs")
query = query.Order("submitted_at DESC")
err = query.Count(&count).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
// Apply pagination
page := req.Pagination.Page
limit := req.Pagination.Limit
if page <= 0 {
page = 1
}
if limit <= 0 {
limit = 10
}
offset := (page - 1) * limit
err = query.Offset(offset).Limit(limit).Find(&flows).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
paging = paginator.Pagination{
Page: page,
Limit: limit,
Count: int64(count),
TotalPage: int((count + int64(limit) - 1) / int64(limit)),
}
return flows, paging, nil
}
func (_i *articleApprovalFlowsRepository) FindOne(clientId *uuid.UUID, id uint) (flow *entity.ArticleApprovalFlows, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Preload("Article").Preload("Workflow").Preload("SubmittedBy").Preload("StepLogs")
err = query.First(&flow, id).Error
return flow, err
}
func (_i *articleApprovalFlowsRepository) FindActiveByArticleId(articleId uint) (flow *entity.ArticleApprovalFlows, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
query = query.Where("article_id = ? AND status_id IN (1, 4)", articleId) // pending or revision_requested
query = query.Preload("Article").Preload("Workflow").Preload("SubmittedBy").Preload("StepLogs")
err = query.First(&flow).Error
return flow, err
}
func (_i *articleApprovalFlowsRepository) Create(clientId *uuid.UUID, flow *entity.ArticleApprovalFlows) (flowReturn *entity.ArticleApprovalFlows, err error) {
flow.ClientId = clientId
err = _i.DB.DB.Create(&flow).Error
return flow, err
}
func (_i *articleApprovalFlowsRepository) Update(id uint, flow *entity.ArticleApprovalFlows) (err error) {
err = _i.DB.DB.Model(&entity.ArticleApprovalFlows{}).Where("id = ?", id).Updates(flow).Error
return err
}
func (_i *articleApprovalFlowsRepository) Delete(clientId *uuid.UUID, id uint) (err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
err = query.Delete(&entity.ArticleApprovalFlows{}, id).Error
return err
}
// Approval Queue Methods
func (_i *articleApprovalFlowsRepository) GetPendingApprovals(clientId *uuid.UUID, userLevelId uint, page, limit int, filters map[string]interface{}) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) {
var count int64
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
if clientId != nil {
query = query.Where("article_approval_flows.client_id = ?", clientId)
}
// Join with workflow steps to find current step requirements
query = query.Joins("JOIN approval_workflows ON article_approval_flows.workflow_id = approval_workflows.id")
query = query.Joins("JOIN approval_workflow_steps ON approval_workflows.id = approval_workflow_steps.workflow_id AND article_approval_flows.current_step = approval_workflow_steps.step_order")
query = query.Where("approval_workflow_steps.required_user_level_id = ?", userLevelId)
query = query.Where("article_approval_flows.status_id IN (1, 4)") // pending or revision_requested
query = query.Where("article_approval_flows.current_step > 0") // exclude completed flows (current_step = 0)
// Debug logging
_i.Log.Info().
Interface("clientId", clientId).
Uint("userLevelId", userLevelId).
Msg("Getting pending approvals for user level")
// Apply filters
if categoryId, ok := filters["category_id"]; ok {
query = query.Joins("JOIN articles ON article_approval_flows.article_id = articles.id")
query = query.Where("articles.category_id = ?", categoryId)
}
if typeId, ok := filters["type_id"]; ok {
query = query.Joins("JOIN articles ON article_approval_flows.article_id = articles.id")
query = query.Where("articles.type_id = ?", typeId)
}
if search, ok := filters["search"]; ok {
query = query.Joins("JOIN articles ON article_approval_flows.article_id = articles.id")
query = query.Where("articles.title ILIKE ? OR articles.description ILIKE ?", fmt.Sprintf("%%%s%%", search), fmt.Sprintf("%%%s%%", search))
}
query = query.Preload("Article").Preload("Workflow").Preload("SubmittedBy").Preload("StepLogs")
query = query.Order("article_approval_flows.submitted_at ASC")
err = query.Count(&count).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
// Debug logging
_i.Log.Info().
Int64("count", count).
Msg("Found pending approvals count")
offset := (page - 1) * limit
err = query.Offset(offset).Limit(limit).Find(&flows).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
// Debug logging
_i.Log.Info().
Int("flowsCount", len(flows)).
Msg("Retrieved flows from database")
paging = paginator.Pagination{
Page: page,
Limit: limit,
Count: int64(count),
TotalPage: int((count + int64(limit) - 1) / int64(limit)),
}
return flows, paging, nil
}
func (_i *articleApprovalFlowsRepository) GetMyApprovalQueue(clientId *uuid.UUID, userLevelId uint, page, limit int, includePreview bool, urgentOnly bool) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) {
filters := make(map[string]interface{})
if urgentOnly {
// Add logic for urgent articles (e.g., submitted more than 24 hours ago)
filters["urgent"] = true
}
return _i.GetPendingApprovals(clientId, userLevelId, page, limit, filters)
}
// Statistics Methods
func (_i *articleApprovalFlowsRepository) GetPendingCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
if clientId != nil {
query = query.Where("article_approval_flows.client_id = ?", clientId)
}
query = query.Joins("JOIN approval_workflows ON article_approval_flows.workflow_id = approval_workflows.id")
query = query.Joins("JOIN approval_workflow_steps ON approval_workflows.id = approval_workflow_steps.workflow_id AND article_approval_flows.current_step = approval_workflow_steps.step_order")
query = query.Where("approval_workflow_steps.required_user_level_id = ?", userLevelId)
query = query.Where("article_approval_flows.status_id IN (1, 4)") // pending or revision_requested
err = query.Count(&count).Error
return count, err
}
func (_i *articleApprovalFlowsRepository) GetOverdueCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
if clientId != nil {
query = query.Where("article_approval_flows.client_id = ?", clientId)
}
query = query.Joins("JOIN approval_workflows ON article_approval_flows.workflow_id = approval_workflows.id")
query = query.Joins("JOIN approval_workflow_steps ON approval_workflows.id = approval_workflow_steps.workflow_id AND article_approval_flows.current_step = approval_workflow_steps.step_order")
query = query.Where("approval_workflow_steps.required_user_level_id = ?", userLevelId)
query = query.Where("article_approval_flows.status_id IN (1, 4)") // pending or revision_requested
query = query.Where("article_approval_flows.submitted_at < ?", time.Now().Add(-24*time.Hour)) // overdue after 24 hours
err = query.Count(&count).Error
return count, err
}
func (_i *articleApprovalFlowsRepository) GetApprovedCountByPeriod(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (count int64, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("article_approval_step_logs.client_id = ?", clientId)
}
query = query.Where("user_level_id = ? AND action = 'approve' AND processed_at BETWEEN ? AND ?", userLevelId, startDate, endDate)
err = query.Count(&count).Error
return count, err
}
func (_i *articleApprovalFlowsRepository) GetRejectedCountByPeriod(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (count int64, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("article_approval_step_logs.client_id = ?", clientId)
}
query = query.Where("user_level_id = ? AND action = 'reject' AND processed_at BETWEEN ? AND ?", userLevelId, startDate, endDate)
err = query.Count(&count).Error
return count, err
}
func (_i *articleApprovalFlowsRepository) GetRevisionRequestCountByPeriod(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (count int64, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("article_approval_step_logs.client_id = ?", clientId)
}
query = query.Where("user_level_id = ? AND action = 'request_revision' AND processed_at BETWEEN ? AND ?", userLevelId, startDate, endDate)
err = query.Count(&count).Error
return count, err
}
// Workload Methods (simplified implementations)
func (_i *articleApprovalFlowsRepository) GetLevelWorkload(clientId *uuid.UUID, userLevelId uint) (workload map[string]interface{}, err error) {
workload = make(map[string]interface{})
// Get pending count
pendingCount, err := _i.GetPendingCountByLevel(clientId, userLevelId)
if err != nil {
return nil, err
}
workload["pending_count"] = pendingCount
workload["level_id"] = userLevelId
return workload, nil
}
func (_i *articleApprovalFlowsRepository) GetTeamWorkloadComparison(clientId *uuid.UUID, userLevelId uint) (comparison []map[string]interface{}, err error) {
// This would require more complex queries to get team members at the same level
// For now, return empty slice
comparison = make([]map[string]interface{}, 0)
return comparison, nil
}
func (_i *articleApprovalFlowsRepository) GetWorkflowBottlenecks(clientId *uuid.UUID) (bottlenecks []map[string]interface{}, err error) {
// This would require complex analysis of workflow steps
// For now, return empty slice
bottlenecks = make([]map[string]interface{}, 0)
return bottlenecks, nil
}
// FindByArticleId finds an approval flow by article ID for a specific client
func (_i *articleApprovalFlowsRepository) FindByArticleId(clientId *uuid.UUID, articleId uint) (flow *entity.ArticleApprovalFlows, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("article_id = ?", articleId)
query = query.Where("status_id IN (1, 2, 3, 4)") // active flows (pending, approved, rejected, revision_requested)
query = query.Order("created_at DESC")
err = query.First(&flow).Error
return flow, err
}
// GetPendingApprovalsByUserLevel gets pending approvals for a specific user level with pagination
func (_i *articleApprovalFlowsRepository) GetPendingApprovalsByUserLevel(clientId *uuid.UUID, userLevelId uint, page, limit int) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) {
var count int64
query := _i.DB.DB.Model(&entity.ArticleApprovalFlows{})
if clientId != nil {
query = query.Where("article_approval_flows.client_id = ?", clientId)
}
// Join with workflow steps to get approvals for this user level
query = query.Joins("JOIN approval_workflows ON article_approval_flows.workflow_id = approval_workflows.id")
query = query.Joins("JOIN approval_workflow_steps ON approval_workflows.id = approval_workflow_steps.workflow_id AND article_approval_flows.current_step = approval_workflow_steps.step_order")
query = query.Where("approval_workflow_steps.required_user_level_id = ?", userLevelId)
query = query.Where("article_approval_flows.status_id IN (1, 4)") // pending or revision_requested
query = query.Where("article_approval_flows.current_step > 0") // exclude completed flows (current_step = 0)
// Preload related data
query = query.Preload("Article").Preload("Workflow")
// Count total
err = query.Count(&count).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
// Apply pagination
offset := (page - 1) * limit
query = query.Offset(offset).Limit(limit)
query = query.Order("article_approval_flows.created_at ASC") // oldest first
err = query.Find(&flows).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
// Create pagination response
paging = paginator.Pagination{
Page: page,
Limit: limit,
Count: count,
Offset: offset,
}
return flows, paging, nil
}

View File

@ -1,115 +0,0 @@
package request
import (
"strconv"
"time"
"web-medols-be/utils/paginator"
)
type ArticleApprovalFlowsGeneric interface {
ToEntity()
}
type ArticleApprovalFlowsQueryRequest struct {
ArticleId *int `json:"articleId"`
WorkflowId *int `json:"workflowId"`
StatusId *int `json:"statusId"`
SubmittedBy *int `json:"submittedBy"`
CurrentStep *int `json:"currentStep"`
DateFrom *time.Time `json:"dateFrom"`
DateTo *time.Time `json:"dateTo"`
Pagination *paginator.Pagination `json:"pagination"`
}
type SubmitForApprovalRequest struct {
ArticleId int `json:"articleId" validate:"required"`
WorkflowId *int `json:"workflowId"`
}
type ApprovalActionRequest struct {
Message string `json:"message"`
}
type RejectionRequest struct {
Reason string `json:"reason" validate:"required"`
}
type RevisionRequest struct {
Message string `json:"message" validate:"required"`
}
type ResubmitRequest struct {
Message string `json:"message"`
}
type ArticleApprovalFlowsQueryRequestContext struct {
ArticleId string `json:"articleId"`
WorkflowId string `json:"workflowId"`
StatusId string `json:"statusId"`
SubmittedBy string `json:"submittedBy"`
CurrentStep string `json:"currentStep"`
DateFrom string `json:"dateFrom"`
DateTo string `json:"dateTo"`
}
func (req ArticleApprovalFlowsQueryRequestContext) ToParamRequest() ArticleApprovalFlowsQueryRequest {
var articleId *int
var workflowId *int
var statusId *int
var submittedBy *int
var currentStep *int
var dateFrom *time.Time
var dateTo *time.Time
if req.ArticleId != "" {
if parsedArticleId, err := strconv.Atoi(req.ArticleId); err == nil {
articleId = &parsedArticleId
}
}
if req.WorkflowId != "" {
if parsedWorkflowId, err := strconv.Atoi(req.WorkflowId); err == nil {
workflowId = &parsedWorkflowId
}
}
if req.StatusId != "" {
if parsedStatusId, err := strconv.Atoi(req.StatusId); err == nil {
statusId = &parsedStatusId
}
}
if req.SubmittedBy != "" {
if parsedSubmittedBy, err := strconv.Atoi(req.SubmittedBy); err == nil {
submittedBy = &parsedSubmittedBy
}
}
if req.CurrentStep != "" {
if parsedCurrentStep, err := strconv.Atoi(req.CurrentStep); err == nil {
currentStep = &parsedCurrentStep
}
}
if req.DateFrom != "" {
if parsedDateFrom, err := time.Parse("2006-01-02", req.DateFrom); err == nil {
dateFrom = &parsedDateFrom
}
}
if req.DateTo != "" {
if parsedDateTo, err := time.Parse("2006-01-02", req.DateTo); err == nil {
dateTo = &parsedDateTo
}
}
return ArticleApprovalFlowsQueryRequest{
ArticleId: articleId,
WorkflowId: workflowId,
StatusId: statusId,
SubmittedBy: submittedBy,
CurrentStep: currentStep,
DateFrom: dateFrom,
DateTo: dateTo,
}
}

View File

@ -1,111 +0,0 @@
package response
import (
"time"
articlesResponse "web-medols-be/app/module/articles/response"
approvalWorkflowsResponse "web-medols-be/app/module/approval_workflows/response"
articleApprovalStepLogsResponse "web-medols-be/app/module/article_approval_step_logs/response"
)
type ArticleApprovalFlowsResponse struct {
ID uint `json:"id"`
ArticleID uint `json:"articleId"`
WorkflowID uint `json:"workflowId"`
CurrentStep int `json:"currentStep"`
Status string `json:"status"`
SubmittedBy uint `json:"submittedBy"`
SubmittedAt time.Time `json:"submittedAt"`
CompletedAt *time.Time `json:"completedAt"`
RejectedAt *time.Time `json:"rejectedAt"`
RejectionReason *string `json:"rejectionReason"`
RevisionRequestedAt *time.Time `json:"revisionRequestedAt"`
RevisionReason *string `json:"revisionReason"`
ResubmittedAt *time.Time `json:"resubmittedAt"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
// Relations
Article *articlesResponse.ArticlesResponse `json:"article,omitempty"`
Workflow *approvalWorkflowsResponse.ApprovalWorkflowsResponse `json:"workflow,omitempty"`
StepLogs []*articleApprovalStepLogsResponse.ArticleApprovalStepLogsResponse `json:"stepLogs,omitempty"`
}
type ArticleApprovalFlowsDetailResponse struct {
ID uint `json:"id"`
ArticleID uint `json:"articleId"`
WorkflowID uint `json:"workflowId"`
CurrentStep int `json:"currentStep"`
Status string `json:"status"`
SubmittedBy uint `json:"submittedBy"`
SubmittedByName string `json:"submittedByName"`
SubmittedAt time.Time `json:"submittedAt"`
CompletedAt *time.Time `json:"completedAt"`
RejectedAt *time.Time `json:"rejectedAt"`
RejectionReason *string `json:"rejectionReason"`
RevisionRequestedAt *time.Time `json:"revisionRequestedAt"`
RevisionReason *string `json:"revisionReason"`
ResubmittedAt *time.Time `json:"resubmittedAt"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
// Relations with full details
Article *articlesResponse.ArticlesResponse `json:"article"`
Workflow *approvalWorkflowsResponse.ApprovalWorkflowsWithStepsResponse `json:"workflow"`
StepLogs []*articleApprovalStepLogsResponse.ArticleApprovalStepLogsResponse `json:"stepLogs"`
}
type ArticleApprovalFlowsSummaryResponse struct {
ID uint `json:"id"`
ArticleID uint `json:"articleId"`
ArticleTitle string `json:"articleTitle"`
WorkflowName string `json:"workflowName"`
CurrentStep int `json:"currentStep"`
TotalSteps int `json:"totalSteps"`
Status string `json:"status"`
SubmittedBy uint `json:"submittedBy"`
SubmittedByName string `json:"submittedByName"`
SubmittedAt time.Time `json:"submittedAt"`
DaysInApproval int `json:"daysInApproval"`
}
type ApprovalQueueResponse struct {
ID uint `json:"id"`
ArticleID uint `json:"articleId"`
ArticleTitle string `json:"articleTitle"`
ArticleAuthor string `json:"articleAuthor"`
WorkflowName string `json:"workflowName"`
CurrentStepName string `json:"currentStepName"`
SubmittedAt time.Time `json:"submittedAt"`
DaysWaiting int `json:"daysWaiting"`
Priority string `json:"priority"`
RequiresComment bool `json:"requiresComment"`
}
type ApprovalDashboardStatsResponse struct {
PendingApprovals int `json:"pendingApprovals"`
MyPendingApprovals int `json:"myPendingApprovals"`
ApprovedToday int `json:"approvedToday"`
RejectedToday int `json:"rejectedToday"`
RevisionRequested int `json:"revisionRequested"`
OverdueApprovals int `json:"overdueApprovals"`
AverageApprovalTime int `json:"averageApprovalTime"` // in hours
}
type ApprovalWorkloadStatsResponse struct {
ApproverID uint `json:"approverId"`
ApproverName string `json:"approverName"`
PendingCount int `json:"pendingCount"`
ApprovedThisWeek int `json:"approvedThisWeek"`
RejectedThisWeek int `json:"rejectedThisWeek"`
AverageResponseTime int `json:"averageResponseTime"` // in hours
}
type ApprovalAnalyticsResponse struct {
Period string `json:"period"`
TotalSubmissions int `json:"totalSubmissions"`
TotalApproved int `json:"totalApproved"`
TotalRejected int `json:"totalRejected"`
TotalRevisions int `json:"totalRevisions"`
ApprovalRate float64 `json:"approvalRate"`
AverageApprovalTime float64 `json:"averageApprovalTime"` // in hours
}

View File

@ -1,788 +0,0 @@
package service
import (
"errors"
"time"
"web-medols-be/app/database/entity"
approvalWorkflowStepsRepo "web-medols-be/app/module/approval_workflow_steps/repository"
approvalWorkflowsRepo "web-medols-be/app/module/approval_workflows/repository"
"web-medols-be/app/module/article_approval_flows/repository"
"web-medols-be/app/module/article_approval_flows/request"
approvalStepLogsRepo "web-medols-be/app/module/article_approval_step_logs/repository"
articlesRepo "web-medols-be/app/module/articles/repository"
usersRepo "web-medols-be/app/module/users/repository"
"web-medols-be/utils/paginator"
"github.com/google/uuid"
"github.com/rs/zerolog"
)
type articleApprovalFlowsService struct {
ArticleApprovalFlowsRepository repository.ArticleApprovalFlowsRepository
ApprovalWorkflowsRepository approvalWorkflowsRepo.ApprovalWorkflowsRepository
ApprovalWorkflowStepsRepository approvalWorkflowStepsRepo.ApprovalWorkflowStepsRepository
ArticleApprovalStepLogsRepository approvalStepLogsRepo.ArticleApprovalStepLogsRepository
ArticlesRepository articlesRepo.ArticlesRepository
UsersRepository usersRepo.UsersRepository
Log zerolog.Logger
}
// ArticleApprovalFlowsService define interface of IArticleApprovalFlowsService
type ArticleApprovalFlowsService interface {
// Basic CRUD
GetAll(clientId *uuid.UUID, req request.ArticleApprovalFlowsQueryRequest) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (flow *entity.ArticleApprovalFlows, err error)
Create(clientId *uuid.UUID, flow *entity.ArticleApprovalFlows) (flowReturn *entity.ArticleApprovalFlows, err error)
Update(id uint, flow *entity.ArticleApprovalFlows) (err error)
Delete(clientId *uuid.UUID, id uint) (err error)
// Article submission and approval workflow
SubmitArticleForApproval(clientId *uuid.UUID, articleId uint, submittedById uint, workflowId *uint) (flow *entity.ArticleApprovalFlows, err error)
ApproveStep(clientId *uuid.UUID, flowId uint, approvedById uint, message string) (err error)
RejectArticle(clientId *uuid.UUID, flowId uint, rejectedById uint, reason string) (err error)
RequestRevision(clientId *uuid.UUID, flowId uint, requestedById uint, revisionMessage string) (err error)
ResubmitAfterRevision(clientId *uuid.UUID, flowId uint, resubmittedById uint) (err error)
// Dashboard and queue methods
GetPendingApprovals(clientId *uuid.UUID, userLevelId uint, page, limit int, filters map[string]interface{}) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error)
GetMyApprovalQueue(clientId *uuid.UUID, userLevelId uint, page, limit int, includePreview bool, urgentOnly bool) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error)
GetApprovalHistory(clientId *uuid.UUID, articleId uint, page, limit int) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error)
// Statistics and analytics
GetPendingCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error)
GetOverdueCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error)
GetApprovalStatistics(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (stats map[string]interface{}, err error)
GetWorkloadAnalytics(clientId *uuid.UUID, userLevelId uint) (analytics map[string]interface{}, err error)
// Workflow management
CanUserApproveStep(clientId *uuid.UUID, flowId uint, userId uint, userLevelId uint) (canApprove bool, reason string, err error)
GetCurrentStepInfo(clientId *uuid.UUID, flowId uint) (stepInfo map[string]interface{}, err error)
GetNextStepPreview(clientId *uuid.UUID, flowId uint) (nextStep *entity.ApprovalWorkflowSteps, err error)
}
func NewArticleApprovalFlowsService(
articleApprovalFlowsRepository repository.ArticleApprovalFlowsRepository,
approvalWorkflowsRepository approvalWorkflowsRepo.ApprovalWorkflowsRepository,
approvalWorkflowStepsRepository approvalWorkflowStepsRepo.ApprovalWorkflowStepsRepository,
articleApprovalStepLogsRepository approvalStepLogsRepo.ArticleApprovalStepLogsRepository,
articlesRepository articlesRepo.ArticlesRepository,
usersRepository usersRepo.UsersRepository,
log zerolog.Logger,
) ArticleApprovalFlowsService {
return &articleApprovalFlowsService{
ArticleApprovalFlowsRepository: articleApprovalFlowsRepository,
ApprovalWorkflowsRepository: approvalWorkflowsRepository,
ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository,
ArticleApprovalStepLogsRepository: articleApprovalStepLogsRepository,
ArticlesRepository: articlesRepository,
UsersRepository: usersRepository,
Log: log,
}
}
// Basic CRUD implementations
func (_i *articleApprovalFlowsService) GetAll(clientId *uuid.UUID, req request.ArticleApprovalFlowsQueryRequest) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) {
return _i.ArticleApprovalFlowsRepository.GetAll(clientId, req)
}
func (_i *articleApprovalFlowsService) FindOne(clientId *uuid.UUID, id uint) (flow *entity.ArticleApprovalFlows, err error) {
return _i.ArticleApprovalFlowsRepository.FindOne(clientId, id)
}
func (_i *articleApprovalFlowsService) Create(clientId *uuid.UUID, flow *entity.ArticleApprovalFlows) (flowReturn *entity.ArticleApprovalFlows, err error) {
return _i.ArticleApprovalFlowsRepository.Create(clientId, flow)
}
func (_i *articleApprovalFlowsService) Update(id uint, flow *entity.ArticleApprovalFlows) (err error) {
return _i.ArticleApprovalFlowsRepository.Update(id, flow)
}
func (_i *articleApprovalFlowsService) Delete(clientId *uuid.UUID, id uint) (err error) {
return _i.ArticleApprovalFlowsRepository.Delete(clientId, id)
}
// Article submission and approval workflow
func (_i *articleApprovalFlowsService) SubmitArticleForApproval(clientId *uuid.UUID, articleId uint, submittedById uint, workflowId *uint) (flow *entity.ArticleApprovalFlows, err error) {
// Check if article already has an active approval flow
existingFlow, err := _i.ArticleApprovalFlowsRepository.FindActiveByArticleId(articleId)
if err == nil && existingFlow != nil {
return nil, errors.New("article already has an active approval flow")
}
// Get workflow (use default if not specified)
var workflow *entity.ApprovalWorkflows
if workflowId != nil {
workflow, err = _i.ApprovalWorkflowsRepository.FindOne(clientId, *workflowId)
} else {
workflow, err = _i.ApprovalWorkflowsRepository.FindDefault(clientId)
}
if err != nil {
return nil, err
}
if workflow == nil {
return nil, errors.New("no workflow found")
}
if workflow.IsActive != nil && !*workflow.IsActive {
return nil, errors.New("workflow is not active")
}
// Get first step of workflow
firstStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, workflow.ID, 1)
if err != nil {
return nil, err
}
if firstStep == nil {
return nil, errors.New("workflow has no steps")
}
// Create approval flow
flow = &entity.ArticleApprovalFlows{
ArticleId: articleId,
WorkflowId: workflow.ID,
CurrentStep: 1,
StatusId: 1, // pending
SubmittedById: submittedById,
SubmittedAt: time.Now(),
}
flow, err = _i.ArticleApprovalFlowsRepository.Create(clientId, flow)
if err != nil {
return nil, err
}
// Get current article data first
currentArticle, err := _i.ArticlesRepository.FindOne(clientId, articleId)
if err != nil {
return nil, err
}
// Update only the necessary fields
currentArticle.WorkflowId = &workflow.ID
currentArticle.CurrentApprovalStep = &flow.CurrentStep
currentArticle.StatusId = &[]int{1}[0] // pending approval
err = _i.ArticlesRepository.UpdateSkipNull(clientId, articleId, currentArticle)
if err != nil {
return nil, err
}
// Process auto-skip logic based on user level
err = _i.processAutoSkipSteps(clientId, flow, submittedById)
if err != nil {
return nil, err
}
return flow, nil
}
// processAutoSkipSteps handles automatic step skipping based on user level
func (_i *articleApprovalFlowsService) processAutoSkipSteps(clientId *uuid.UUID, flow *entity.ArticleApprovalFlows, submittedById uint) error {
// Get user level of the submitter
userLevelId, err := _i.getUserLevelId(clientId, submittedById)
if err != nil {
return err
}
// Get all workflow steps
steps, err := _i.ApprovalWorkflowStepsRepository.GetByWorkflowId(clientId, flow.WorkflowId)
if err != nil {
return err
}
// Sort steps by step order
sortStepsByOrder(steps)
// Process each step to determine if it should be auto-skipped
for _, step := range steps {
shouldSkip := _i.shouldSkipStep(userLevelId, step.RequiredUserLevelId)
if shouldSkip {
// Create skip log
stepLog := &entity.ArticleApprovalStepLogs{
ApprovalFlowId: flow.ID,
StepOrder: step.StepOrder,
StepName: step.StepName,
ApprovedById: &submittedById,
Action: "auto_skip",
Message: &[]string{"Step auto-skipped due to user level"}[0],
ProcessedAt: time.Now(),
UserLevelId: step.RequiredUserLevelId,
}
_, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog)
if err != nil {
return err
}
// Update flow to next step (handle step order starting from 0)
nextStepOrder := step.StepOrder + 1
flow.CurrentStep = nextStepOrder
} else {
// Stop at first step that cannot be skipped
break
}
}
// Update flow with final current step
err = _i.ArticleApprovalFlowsRepository.Update(flow.ID, flow)
if err != nil {
return err
}
// Get current article data first
currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId)
if err != nil {
return err
}
// Update only the necessary fields
currentArticle.CurrentApprovalStep = &flow.CurrentStep
err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle)
if err != nil {
return err
}
// Check if all steps were skipped (workflow complete)
// Find the highest step order
maxStepOrder := 0
for _, step := range steps {
if step.StepOrder > maxStepOrder {
maxStepOrder = step.StepOrder
}
}
if flow.CurrentStep > maxStepOrder {
// All steps completed, mark as approved
flow.StatusId = 2 // approved
flow.CurrentStep = 0 // Set to 0 to indicate completion
flow.CompletedAt = &[]time.Time{time.Now()}[0]
err = _i.ArticleApprovalFlowsRepository.Update(flow.ID, flow)
if err != nil {
return err
}
// Get current article data first
currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId)
if err != nil {
return err
}
// Update only the necessary fields
currentArticle.StatusId = &[]int{2}[0] // approved
currentArticle.CurrentApprovalStep = &[]int{0}[0] // Set to 0 to indicate completion
err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle)
if err != nil {
return err
}
}
return nil
}
// getUserLevelId gets the user level ID for a given user
func (_i *articleApprovalFlowsService) getUserLevelId(clientId *uuid.UUID, userId uint) (uint, error) {
// Get user from database to retrieve user level
user, err := _i.UsersRepository.FindOne(clientId, userId)
if err != nil {
_i.Log.Error().Err(err).Uint("userId", userId).Msg("Failed to find user")
return 0, err
}
if user.UserLevel == nil {
_i.Log.Error().Uint("userId", userId).Msg("User has no user level")
return 0, errors.New("user has no user level")
}
_i.Log.Info().
Uint("userId", userId).
Uint("userLevelId", user.UserLevel.ID).
Str("userLevelName", user.UserLevel.Name).
Msg("Retrieved user level from database")
return user.UserLevel.ID, nil
}
// shouldSkipStep determines if a step should be auto-skipped based on user level
func (_i *articleApprovalFlowsService) shouldSkipStep(userLevelId, requiredLevelId uint) bool {
// Get user level details to compare level numbers
// User level with lower level_number (higher authority) can skip steps requiring higher level_number
// For now, we'll use a simple comparison based on IDs
// In production, this should compare level_number fields
// Simple logic: if user level ID is less than required level ID, they can skip
// This assumes level 1 (ID=1) has higher authority than level 2 (ID=2), etc.
return userLevelId < requiredLevelId
}
// sortStepsByOrder sorts workflow steps by their step order
func sortStepsByOrder(steps []*entity.ApprovalWorkflowSteps) {
// Simple bubble sort for step order
n := len(steps)
for i := 0; i < n-1; i++ {
for j := 0; j < n-i-1; j++ {
if steps[j].StepOrder > steps[j+1].StepOrder {
steps[j], steps[j+1] = steps[j+1], steps[j]
}
}
}
}
func (_i *articleApprovalFlowsService) ApproveStep(clientId *uuid.UUID, flowId uint, approvedById uint, message string) (err error) {
// Get approval flow
flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId)
if err != nil {
return err
}
if flow == nil {
return errors.New("approval flow not found")
}
if flow.StatusId != 1 && flow.StatusId != 4 { // not pending or revision_requested
return errors.New("approval flow is not in pending state")
}
// Get current step
currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep)
if err != nil {
return err
}
if currentStep == nil {
return errors.New("current step not found")
}
// Create step log
stepLog := &entity.ArticleApprovalStepLogs{
ApprovalFlowId: flow.ID,
StepOrder: flow.CurrentStep,
StepName: currentStep.StepName,
ApprovedById: &approvedById,
Action: "approve",
Message: &message,
ProcessedAt: time.Now(),
UserLevelId: currentStep.RequiredUserLevelId,
}
_, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog)
if err != nil {
return err
}
// Check if there's a next step
nextStep, err := _i.ApprovalWorkflowStepsRepository.GetNextStep(clientId, flow.WorkflowId, flow.CurrentStep)
if err != nil && err.Error() != "record not found" {
return err
}
if nextStep == nil || nextStep.ID == 0 {
// No next step - approval complete
flowUpdate := &entity.ArticleApprovalFlows{
StatusId: 2, // approved
CurrentStep: 0, // Set to 0 to indicate completion
CompletedAt: &[]time.Time{time.Now()}[0],
}
// Debug logging
_i.Log.Info().
Interface("flowUpdate :: ", flowUpdate).
Msg("Retrieved next step from database")
err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate)
if err != nil {
return err
}
// Get current article data first
currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId)
if err != nil {
return err
}
// Update only the necessary fields
currentArticle.StatusId = &[]int{2}[0] // approved
currentArticle.CurrentApprovalStep = &[]int{0}[0] // Set to 0 to indicate completion
currentArticle.IsPublish = &[]bool{true}[0] // Set to true to indicate publication
currentArticle.IsDraft = &[]bool{false}[0] // Set to false to indicate publication
err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle)
if err != nil {
return err
}
} else {
// Move to next step
flowUpdate := &entity.ArticleApprovalFlows{
CurrentStep: nextStep.StepOrder,
StatusId: 1, // pending
}
err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate)
if err != nil {
return err
}
// Get current article data first
currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId)
if err != nil {
return err
}
// Update only the necessary fields
currentArticle.CurrentApprovalStep = &nextStep.StepOrder
err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle)
if err != nil {
return err
}
}
return nil
}
func (_i *articleApprovalFlowsService) RejectArticle(clientId *uuid.UUID, flowId uint, rejectedById uint, reason string) (err error) {
// Get approval flow
flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId)
if err != nil {
return err
}
if flow == nil {
return errors.New("approval flow not found")
}
if flow.StatusId != 1 && flow.StatusId != 4 { // not pending or revision_requested
return errors.New("approval flow is not in pending state")
}
// Get current step
currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep)
if err != nil {
return err
}
// Create step log
stepLog := &entity.ArticleApprovalStepLogs{
ApprovalFlowId: flow.ID,
StepOrder: flow.CurrentStep,
StepName: currentStep.StepName,
ApprovedById: &rejectedById,
Action: "reject",
Message: &reason,
ProcessedAt: time.Now(),
UserLevelId: currentStep.RequiredUserLevelId,
}
_, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog)
if err != nil {
return err
}
// Update approval flow status
flowUpdate := &entity.ArticleApprovalFlows{
StatusId: 3, // rejected
RejectionReason: &reason,
CompletedAt: &[]time.Time{time.Now()}[0],
}
err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate)
if err != nil {
return err
}
// Get current article data first
currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId)
if err != nil {
return err
}
// Update only the necessary fields
currentArticle.StatusId = &[]int{3}[0] // rejected
currentArticle.CurrentApprovalStep = nil
err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle)
if err != nil {
return err
}
return nil
}
func (_i *articleApprovalFlowsService) RequestRevision(clientId *uuid.UUID, flowId uint, requestedById uint, revisionMessage string) (err error) {
// Get approval flow
flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId)
if err != nil {
return err
}
if flow == nil {
return errors.New("approval flow not found")
}
if flow.StatusId != 1 { // not pending
return errors.New("approval flow is not in pending state")
}
// Get current step
currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep)
if err != nil {
return err
}
// Create step log
stepLog := &entity.ArticleApprovalStepLogs{
ApprovalFlowId: flow.ID,
StepOrder: flow.CurrentStep,
StepName: currentStep.StepName,
ApprovedById: &requestedById,
Action: "request_revision",
Message: &revisionMessage,
ProcessedAt: time.Now(),
UserLevelId: currentStep.RequiredUserLevelId,
}
_, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog)
if err != nil {
return err
}
// Update approval flow status
flowUpdate := &entity.ArticleApprovalFlows{
StatusId: 4, // revision_requested
RevisionRequested: &[]bool{true}[0],
RevisionMessage: &revisionMessage,
}
err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate)
if err != nil {
return err
}
// Get current article data first
currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId)
if err != nil {
return err
}
// Update only the necessary fields
currentArticle.StatusId = &[]int{4}[0] // revision_requested
err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle)
if err != nil {
return err
}
return nil
}
func (_i *articleApprovalFlowsService) ResubmitAfterRevision(clientId *uuid.UUID, flowId uint, resubmittedById uint) (err error) {
// Get approval flow
flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId)
if err != nil {
return err
}
if flow == nil {
return errors.New("approval flow not found")
}
if flow.StatusId != 4 { // not revision_requested
return errors.New("approval flow is not in revision requested state")
}
// Reset approval flow to pending
flowUpdate := &entity.ArticleApprovalFlows{
StatusId: 1, // pending
RevisionRequested: &[]bool{false}[0],
RevisionMessage: nil,
CurrentStep: 1, // restart from first step
}
err = _i.ArticleApprovalFlowsRepository.Update(flowId, flowUpdate)
if err != nil {
return err
}
// Get current article data first
currentArticle, err := _i.ArticlesRepository.FindOne(clientId, flow.ArticleId)
if err != nil {
return err
}
// Update only the necessary fields
currentArticle.StatusId = &[]int{1}[0] // pending approval
currentArticle.CurrentApprovalStep = &[]int{1}[0]
err = _i.ArticlesRepository.UpdateSkipNull(clientId, flow.ArticleId, currentArticle)
if err != nil {
return err
}
// Create resubmission log
stepLog := &entity.ArticleApprovalStepLogs{
ApprovalFlowId: flow.ID,
StepOrder: 1,
StepName: "Resubmission",
ApprovedById: &resubmittedById,
Action: "resubmit",
Message: &[]string{"Article resubmitted after revision"}[0],
ProcessedAt: time.Now(),
}
_, err = _i.ArticleApprovalStepLogsRepository.Create(clientId, stepLog)
if err != nil {
return err
}
return nil
}
// Dashboard and queue methods
func (_i *articleApprovalFlowsService) GetPendingApprovals(clientId *uuid.UUID, userLevelId uint, page, limit int, filters map[string]interface{}) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) {
return _i.ArticleApprovalFlowsRepository.GetPendingApprovals(clientId, userLevelId, page, limit, filters)
}
func (_i *articleApprovalFlowsService) GetMyApprovalQueue(clientId *uuid.UUID, userLevelId uint, page, limit int, includePreview bool, urgentOnly bool) (flows []*entity.ArticleApprovalFlows, paging paginator.Pagination, err error) {
return _i.ArticleApprovalFlowsRepository.GetMyApprovalQueue(clientId, userLevelId, page, limit, includePreview, urgentOnly)
}
func (_i *articleApprovalFlowsService) GetApprovalHistory(clientId *uuid.UUID, articleId uint, page, limit int) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error) {
return _i.ArticleApprovalStepLogsRepository.GetApprovalHistory(clientId, articleId, page, limit)
}
// Statistics and analytics
func (_i *articleApprovalFlowsService) GetPendingCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error) {
return _i.ArticleApprovalFlowsRepository.GetPendingCountByLevel(clientId, userLevelId)
}
func (_i *articleApprovalFlowsService) GetOverdueCountByLevel(clientId *uuid.UUID, userLevelId uint) (count int64, err error) {
return _i.ArticleApprovalFlowsRepository.GetOverdueCountByLevel(clientId, userLevelId)
}
func (_i *articleApprovalFlowsService) GetApprovalStatistics(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (stats map[string]interface{}, err error) {
stats = make(map[string]interface{})
// Get approved count
approvedCount, err := _i.ArticleApprovalFlowsRepository.GetApprovedCountByPeriod(clientId, userLevelId, startDate, endDate)
if err != nil {
return nil, err
}
// Get rejected count
rejectedCount, err := _i.ArticleApprovalFlowsRepository.GetRejectedCountByPeriod(clientId, userLevelId, startDate, endDate)
if err != nil {
return nil, err
}
// Get revision request count
revisionCount, err := _i.ArticleApprovalFlowsRepository.GetRevisionRequestCountByPeriod(clientId, userLevelId, startDate, endDate)
if err != nil {
return nil, err
}
stats["approved_count"] = approvedCount
stats["rejected_count"] = rejectedCount
stats["revision_requested_count"] = revisionCount
stats["total_processed"] = approvedCount + rejectedCount + revisionCount
stats["period_start"] = startDate
stats["period_end"] = endDate
return stats, nil
}
func (_i *articleApprovalFlowsService) GetWorkloadAnalytics(clientId *uuid.UUID, userLevelId uint) (analytics map[string]interface{}, err error) {
return _i.ArticleApprovalFlowsRepository.GetLevelWorkload(clientId, userLevelId)
}
// Workflow management
func (_i *articleApprovalFlowsService) CanUserApproveStep(clientId *uuid.UUID, flowId uint, userId uint, userLevelId uint) (canApprove bool, reason string, err error) {
// Get approval flow
flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId)
if err != nil {
return false, "", err
}
if flow == nil {
return false, "approval flow not found", nil
}
if flow.StatusId != 1 && flow.StatusId != 4 { // not pending or revision_requested
return false, "approval flow is not in pending state", nil
}
// Get current step
currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep)
if err != nil {
return false, "", err
}
if currentStep == nil {
return false, "current step not found", nil
}
// Check if user level matches required level
if currentStep.RequiredUserLevelId != userLevelId {
return false, "user level does not match required level for this step", nil
}
// Check if user submitted the article (cannot approve own submission)
if flow.SubmittedById == userId {
return false, "cannot approve own submission", nil
}
return true, "", nil
}
func (_i *articleApprovalFlowsService) GetCurrentStepInfo(clientId *uuid.UUID, flowId uint) (stepInfo map[string]interface{}, err error) {
stepInfo = make(map[string]interface{})
// Get approval flow
flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId)
if err != nil {
return nil, err
}
if flow == nil {
return nil, errors.New("approval flow not found")
}
// Get current step
currentStep, err := _i.ApprovalWorkflowStepsRepository.FindByWorkflowAndStep(clientId, flow.WorkflowId, flow.CurrentStep)
if err != nil {
return nil, err
}
stepInfo["current_step"] = flow.CurrentStep
stepInfo["step_name"] = currentStep.StepName
stepInfo["required_user_level_id"] = currentStep.RequiredUserLevelId
stepInfo["can_skip"] = currentStep.CanSkip
stepInfo["auto_approve_after_hours"] = currentStep.AutoApproveAfterHours
stepInfo["status"] = flow.StatusId
return stepInfo, nil
}
func (_i *articleApprovalFlowsService) GetNextStepPreview(clientId *uuid.UUID, flowId uint) (nextStep *entity.ApprovalWorkflowSteps, err error) {
// Get approval flow
flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, flowId)
if err != nil {
return nil, err
}
if flow == nil {
return nil, errors.New("approval flow not found")
}
// Get next step
nextStep, err = _i.ApprovalWorkflowStepsRepository.GetNextStep(clientId, flow.WorkflowId, flow.CurrentStep)
if err != nil {
return nil, err
}
return nextStep, nil
}

View File

@ -1,71 +0,0 @@
package article_approval_step_logs
import (
"web-medols-be/app/module/article_approval_step_logs/controller"
"web-medols-be/app/module/article_approval_step_logs/repository"
"web-medols-be/app/module/article_approval_step_logs/service"
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
)
// ArticleApprovalStepLogsRouter struct of ArticleApprovalStepLogsRouter
type ArticleApprovalStepLogsRouter struct {
App fiber.Router
Controller controller.ArticleApprovalStepLogsController
}
// NewArticleApprovalStepLogsModule register bulky of ArticleApprovalStepLogs module
var NewArticleApprovalStepLogsModule = fx.Options(
// register repository of ArticleApprovalStepLogs module
fx.Provide(repository.NewArticleApprovalStepLogsRepository),
// register service of ArticleApprovalStepLogs module
fx.Provide(service.NewArticleApprovalStepLogsService),
// register controller of ArticleApprovalStepLogs module
fx.Provide(controller.NewArticleApprovalStepLogsController),
// register router of ArticleApprovalStepLogs module
fx.Provide(NewArticleApprovalStepLogsRouter),
)
// NewArticleApprovalStepLogsRouter init ArticleApprovalStepLogsRouter
func NewArticleApprovalStepLogsRouter(fiber *fiber.App, controller controller.ArticleApprovalStepLogsController) *ArticleApprovalStepLogsRouter {
return &ArticleApprovalStepLogsRouter{
App: fiber,
Controller: controller,
}
}
// RegisterArticleApprovalStepLogsRoutes register routes of ArticleApprovalStepLogs module
func (_i *ArticleApprovalStepLogsRouter) RegisterArticleApprovalStepLogsRoutes() {
// define controllers
articleApprovalStepLogsController := _i.Controller
// define routes
_i.App.Route("/article-approval-step-logs", func(router fiber.Router) {
router.Get("/", articleApprovalStepLogsController.All)
router.Get("/:id", articleApprovalStepLogsController.Show)
router.Post("/", articleApprovalStepLogsController.Save)
router.Put("/:id", articleApprovalStepLogsController.Update)
router.Delete("/:id", articleApprovalStepLogsController.Delete)
// Approval process management routes
router.Get("/flow/:flow_id", articleApprovalStepLogsController.GetByApprovalFlow)
router.Get("/step/:step_id", articleApprovalStepLogsController.GetByWorkflowStep)
router.Get("/approver/:user_id", articleApprovalStepLogsController.GetByApprover)
router.Get("/pending", articleApprovalStepLogsController.GetPendingApprovals)
router.Get("/overdue", articleApprovalStepLogsController.GetOverdueApprovals)
// Approval action routes
router.Post("/:id/process", articleApprovalStepLogsController.ProcessApproval)
router.Post("/bulk-process", articleApprovalStepLogsController.BulkProcessApproval)
router.Post("/:id/auto-approve", articleApprovalStepLogsController.AutoApprove)
// History and analytics routes
router.Get("/history", articleApprovalStepLogsController.GetApprovalHistory)
router.Get("/stats", articleApprovalStepLogsController.GetApprovalStats)
router.Get("/user/:user_id/workload", articleApprovalStepLogsController.GetUserWorkload)
})
}

View File

@ -1,678 +0,0 @@
package controller
import (
"strconv"
"web-medols-be/app/middleware"
"web-medols-be/app/module/article_approval_step_logs/request"
"web-medols-be/app/module/article_approval_step_logs/service"
"web-medols-be/utils/paginator"
utilRes "web-medols-be/utils/response"
utilVal "web-medols-be/utils/validator"
"github.com/gofiber/fiber/v2"
"github.com/rs/zerolog"
)
type articleApprovalStepLogsController struct {
articleApprovalStepLogsService service.ArticleApprovalStepLogsService
Log zerolog.Logger
}
type ArticleApprovalStepLogsController interface {
// Basic CRUD
All(c *fiber.Ctx) error
Show(c *fiber.Ctx) error
Save(c *fiber.Ctx) error
Update(c *fiber.Ctx) error
Delete(c *fiber.Ctx) error
// Approval process management
GetByApprovalFlow(c *fiber.Ctx) error
GetByWorkflowStep(c *fiber.Ctx) error
GetByApprover(c *fiber.Ctx) error
GetPendingApprovals(c *fiber.Ctx) error
GetOverdueApprovals(c *fiber.Ctx) error
// Approval actions
ProcessApproval(c *fiber.Ctx) error
BulkProcessApproval(c *fiber.Ctx) error
AutoApprove(c *fiber.Ctx) error
// History and analytics
GetApprovalHistory(c *fiber.Ctx) error
GetApprovalStats(c *fiber.Ctx) error
GetUserWorkload(c *fiber.Ctx) error
}
func NewArticleApprovalStepLogsController(
articleApprovalStepLogsService service.ArticleApprovalStepLogsService,
log zerolog.Logger,
) ArticleApprovalStepLogsController {
return &articleApprovalStepLogsController{
articleApprovalStepLogsService: articleApprovalStepLogsService,
Log: log,
}
}
// All ArticleApprovalStepLogs
// @Summary Get all ArticleApprovalStepLogs
// @Description API for getting all ArticleApprovalStepLogs
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param req query request.ArticleApprovalStepLogsQueryRequest false "query parameters"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs [get]
func (_i *articleApprovalStepLogsController) All(c *fiber.Ctx) error {
paginate, err := paginator.Paginate(c)
if err != nil {
return err
}
reqContext := request.ArticleApprovalStepLogsQueryRequestContext{
ApprovalFlowId: c.Query("approvalFlowId"),
StepId: c.Query("stepId"),
ActionById: c.Query("actionById"),
ActionType: c.Query("actionType"),
StatusId: c.Query("statusId"),
ActionDateFrom: c.Query("actionDateFrom"),
ActionDateTo: c.Query("actionDateTo"),
UserLevelId: c.Query("userLevelId"),
IsUrgent: c.Query("isUrgent"),
Search: c.Query("search"),
OrderBy: c.Query("orderBy"),
OrderDirection: c.Query("orderDirection"),
}
req := reqContext.ToParamRequest()
req.Pagination = paginate
// Get ClientId from context
clientId := middleware.GetClientID(c)
_i.Log.Info().Interface("clientId", clientId).Msg("")
articleApprovalStepLogsData, paging, err := _i.articleApprovalStepLogsService.GetAll(clientId, *req)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalStepLogs list successfully retrieved"},
Data: articleApprovalStepLogsData,
Meta: paging,
})
}
// Show ArticleApprovalStepLogs
// @Summary Get one ArticleApprovalStepLogs
// @Description API for getting one ArticleApprovalStepLogs
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ArticleApprovalStepLogs ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/{id} [get]
func (_i *articleApprovalStepLogsController) Show(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.FindOne(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalStepLogs successfully retrieved"},
Data: articleApprovalStepLogsData,
})
}
// Save ArticleApprovalStepLogs
// @Summary Save ArticleApprovalStepLogs
// @Description API for saving ArticleApprovalStepLogs
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param payload body request.ArticleApprovalStepLogsCreateRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs [post]
func (_i *articleApprovalStepLogsController) Save(c *fiber.Ctx) error {
req := new(request.ArticleApprovalStepLogsCreateRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to entity
entity := req.ToEntity()
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.Create(clientId, entity)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalStepLogs successfully created"},
Data: articleApprovalStepLogsData,
})
}
// Update ArticleApprovalStepLogs
// @Summary Update ArticleApprovalStepLogs
// @Description API for updating ArticleApprovalStepLogs
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ArticleApprovalStepLogs ID"
// @Param payload body request.ArticleApprovalStepLogsUpdateRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/{id} [put]
func (_i *articleApprovalStepLogsController) Update(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
req := new(request.ArticleApprovalStepLogsUpdateRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
// Convert request to entity
entity := req.ToEntity()
err = _i.articleApprovalStepLogsService.Update(clientId, uint(id), entity)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalStepLogs successfully updated"},
})
}
// Delete ArticleApprovalStepLogs
// @Summary Delete ArticleApprovalStepLogs
// @Description API for deleting ArticleApprovalStepLogs
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "ArticleApprovalStepLogs ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/{id} [delete]
func (_i *articleApprovalStepLogsController) Delete(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
err = _i.articleApprovalStepLogsService.Delete(clientId, uint(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalStepLogs successfully deleted"},
})
}
// GetByApprovalFlow ArticleApprovalStepLogs
// @Summary Get ArticleApprovalStepLogs by Approval Flow ID
// @Description API for getting ArticleApprovalStepLogs by Approval Flow ID
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param flow_id path int true "Approval Flow ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/flow/{flow_id} [get]
func (_i *articleApprovalStepLogsController) GetByApprovalFlow(c *fiber.Ctx) error {
flowId, err := strconv.Atoi(c.Params("flow_id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid flow ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.GetByApprovalFlowID(clientId, uint(flowId))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalStepLogs by approval flow successfully retrieved"},
Data: articleApprovalStepLogsData,
})
}
// GetByWorkflowStep ArticleApprovalStepLogs
// @Summary Get ArticleApprovalStepLogs by Workflow Step ID
// @Description API for getting ArticleApprovalStepLogs by Workflow Step ID
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param step_id path int true "Workflow Step ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/step/{step_id} [get]
func (_i *articleApprovalStepLogsController) GetByWorkflowStep(c *fiber.Ctx) error {
stepId, err := strconv.Atoi(c.Params("step_id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid step ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.GetByWorkflowStepID(clientId, uint(stepId))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalStepLogs by workflow step successfully retrieved"},
Data: articleApprovalStepLogsData,
})
}
// GetByApprover ArticleApprovalStepLogs
// @Summary Get ArticleApprovalStepLogs by Approver User ID
// @Description API for getting ArticleApprovalStepLogs by Approver User ID
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param user_id path int true "Approver User ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/approver/{user_id} [get]
func (_i *articleApprovalStepLogsController) GetByApprover(c *fiber.Ctx) error {
userId, err := strconv.Atoi(c.Params("user_id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid user ID format")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.GetByApproverUserID(clientId, uint(userId))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"ArticleApprovalStepLogs by approver successfully retrieved"},
Data: articleApprovalStepLogsData,
})
}
// GetPendingApprovals ArticleApprovalStepLogs
// @Summary Get Pending Approvals
// @Description API for getting pending approvals
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param user_id query int false "Filter by user ID"
// @Param role_id query int false "Filter by role ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/pending [get]
func (_i *articleApprovalStepLogsController) GetPendingApprovals(c *fiber.Ctx) error {
// Get ClientId from context
clientId := middleware.GetClientID(c)
var userID *uint
var roleID *uint
if userIDStr := c.Query("user_id"); userIDStr != "" {
if id, err := strconv.Atoi(userIDStr); err == nil {
userIDVal := uint(id)
userID = &userIDVal
}
}
if roleIDStr := c.Query("role_id"); roleIDStr != "" {
if id, err := strconv.Atoi(roleIDStr); err == nil {
roleIDVal := uint(id)
roleID = &roleIDVal
}
}
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.GetPendingApprovals(clientId, userID, roleID)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Pending approvals successfully retrieved"},
Data: articleApprovalStepLogsData,
})
}
// GetOverdueApprovals ArticleApprovalStepLogs
// @Summary Get Overdue Approvals
// @Description API for getting overdue approvals
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param user_id query int false "Filter by user ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/overdue [get]
func (_i *articleApprovalStepLogsController) GetOverdueApprovals(c *fiber.Ctx) error {
// Get ClientId from context
clientId := middleware.GetClientID(c)
var userID *uint
if userIDStr := c.Query("user_id"); userIDStr != "" {
if id, err := strconv.Atoi(userIDStr); err == nil {
userIDVal := uint(id)
userID = &userIDVal
}
}
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.GetOverdueApprovals(clientId, userID)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Overdue approvals successfully retrieved"},
Data: articleApprovalStepLogsData,
})
}
// ProcessApproval ArticleApprovalStepLogs
// @Summary Process Approval
// @Description API for processing approval step
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "Step Log ID"
// @Param payload body request.ProcessApprovalRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/{id}/process [post]
func (_i *articleApprovalStepLogsController) ProcessApproval(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
req := new(request.ProcessApprovalRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
err = _i.articleApprovalStepLogsService.ProcessApproval(clientId, uint(id), req.UserID, req.StatusID, req.Comments)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Approval processed successfully"},
})
}
// BulkProcessApproval ArticleApprovalStepLogs
// @Summary Bulk Process Approvals
// @Description API for bulk processing approval steps
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param payload body request.BulkProcessApprovalRequest true "Required payload"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/bulk-process [post]
func (_i *articleApprovalStepLogsController) BulkProcessApproval(c *fiber.Ctx) error {
req := new(request.BulkProcessApprovalRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
err := _i.articleApprovalStepLogsService.BulkProcessApproval(clientId, req.LogIDs, req.UserID, req.StatusID, req.Comments)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Approvals processed successfully"},
})
}
// AutoApprove ArticleApprovalStepLogs
// @Summary Auto Approve Step
// @Description API for automatically approving a step
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param id path int true "Step Log ID"
// @Param reason query string true "Auto approval reason"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/{id}/auto-approve [post]
func (_i *articleApprovalStepLogsController) AutoApprove(c *fiber.Ctx) error {
id, err := strconv.Atoi(c.Params("id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid ID format")
}
reason := c.Query("reason")
if reason == "" {
return utilRes.ErrorBadRequest(c, "Reason is required")
}
// Get ClientId from context
clientId := middleware.GetClientID(c)
err = _i.articleApprovalStepLogsService.AutoApprove(clientId, uint(id), reason)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Step auto approved successfully"},
})
}
// GetApprovalHistory ArticleApprovalStepLogs
// @Summary Get Approval History
// @Description API for getting approval history
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param article_id query int false "Filter by article ID"
// @Param user_id query int false "Filter by user ID"
// @Param from_date query string false "Filter from date (YYYY-MM-DD)"
// @Param to_date query string false "Filter to date (YYYY-MM-DD)"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/history [get]
func (_i *articleApprovalStepLogsController) GetApprovalHistory(c *fiber.Ctx) error {
// Get ClientId from context
clientId := middleware.GetClientID(c)
var articleID *uint
var userID *uint
filters := make(map[string]interface{})
if articleIDStr := c.Query("article_id"); articleIDStr != "" {
if id, err := strconv.Atoi(articleIDStr); err == nil {
articleIDVal := uint(id)
articleID = &articleIDVal
}
}
if userIDStr := c.Query("user_id"); userIDStr != "" {
if id, err := strconv.Atoi(userIDStr); err == nil {
userIDVal := uint(id)
userID = &userIDVal
}
}
if fromDate := c.Query("from_date"); fromDate != "" {
filters["from_date"] = fromDate
}
if toDate := c.Query("to_date"); toDate != "" {
filters["to_date"] = toDate
}
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.GetApprovalHistory(clientId, articleID, userID, filters)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Approval history successfully retrieved"},
Data: articleApprovalStepLogsData,
})
}
// GetApprovalStats ArticleApprovalStepLogs
// @Summary Get Approval Statistics
// @Description API for getting approval statistics
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param from_date query string false "Filter from date (YYYY-MM-DD)"
// @Param to_date query string false "Filter to date (YYYY-MM-DD)"
// @Param workflow_id query int false "Filter by workflow ID"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/stats [get]
func (_i *articleApprovalStepLogsController) GetApprovalStats(c *fiber.Ctx) error {
// Get ClientId from context
clientId := middleware.GetClientID(c)
filters := make(map[string]interface{})
if fromDate := c.Query("from_date"); fromDate != "" {
filters["from_date"] = fromDate
}
if toDate := c.Query("to_date"); toDate != "" {
filters["to_date"] = toDate
}
if workflowID := c.Query("workflow_id"); workflowID != "" {
if id, err := strconv.Atoi(workflowID); err == nil {
filters["workflow_id"] = id
}
}
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.GetApprovalStats(clientId, filters)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"Approval statistics successfully retrieved"},
Data: articleApprovalStepLogsData,
})
}
// GetUserWorkload ArticleApprovalStepLogs
// @Summary Get User Workload
// @Description API for getting user workload statistics
// @Tags ArticleApprovalStepLogs
// @Security Bearer
// @Param X-Client-Key header string true "Insert the X-Client-Key"
// @Param user_id path int true "User ID"
// @Param include_stats query bool false "Include detailed statistics"
// @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError
// @Failure 401 {object} response.UnauthorizedError
// @Failure 500 {object} response.InternalServerError
// @Router /article-approval-step-logs/user/{user_id}/workload [get]
func (_i *articleApprovalStepLogsController) GetUserWorkload(c *fiber.Ctx) error {
userId, err := strconv.Atoi(c.Params("user_id"))
if err != nil {
return utilRes.ErrorBadRequest(c, "Invalid user ID format")
}
includeStats := c.Query("include_stats") == "true"
// Get ClientId from context
clientId := middleware.GetClientID(c)
articleApprovalStepLogsData, err := _i.articleApprovalStepLogsService.GetUserWorkload(clientId, uint(userId), includeStats)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Success: true,
Messages: utilRes.Messages{"User workload successfully retrieved"},
Data: articleApprovalStepLogsData,
})
}

View File

@ -1,148 +0,0 @@
package mapper
import (
"web-medols-be/app/database/entity"
res "web-medols-be/app/module/article_approval_step_logs/response"
usersRepository "web-medols-be/app/module/users/repository"
"github.com/google/uuid"
"github.com/rs/zerolog"
)
func ArticleApprovalStepLogsResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
articleApprovalStepLogsReq *entity.ArticleApprovalStepLogs,
usersRepo usersRepository.UsersRepository,
) (articleApprovalStepLogsRes *res.ArticleApprovalStepLogsResponse) {
if articleApprovalStepLogsReq != nil {
// Map entity fields to response fields
// approvalStatusID := uint(1) // Default status
// if articleApprovalStepLogsReq.Action == "approved" {
// approvalStatusID = 2
// } else if articleApprovalStepLogsReq.Action == "rejected" {
// approvalStatusID = 3
// }
articleApprovalStepLogsRes = &res.ArticleApprovalStepLogsResponse{
ID: articleApprovalStepLogsReq.ID,
ApprovalFlowId: articleApprovalStepLogsReq.ApprovalFlowId,
StepOrder: articleApprovalStepLogsReq.StepOrder,
StepName: articleApprovalStepLogsReq.StepName,
ApprovedById: articleApprovalStepLogsReq.ApprovedById,
Action: articleApprovalStepLogsReq.Action,
Message: articleApprovalStepLogsReq.Message,
ProcessedAt: articleApprovalStepLogsReq.ProcessedAt,
UserLevelId: articleApprovalStepLogsReq.UserLevelId,
ClientId: func() *string {
if articleApprovalStepLogsReq.ClientId != nil {
s := articleApprovalStepLogsReq.ClientId.String()
return &s
}
return nil
}(),
CreatedAt: articleApprovalStepLogsReq.CreatedAt,
}
}
return articleApprovalStepLogsRes
}
func ArticleApprovalStepLogsDetailResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
articleApprovalStepLogsReq *entity.ArticleApprovalStepLogs,
usersRepo usersRepository.UsersRepository,
) (articleApprovalStepLogsRes *res.ArticleApprovalStepLogsDetailResponse) {
if articleApprovalStepLogsReq != nil {
// Map entity fields to response fields
approvalStatusID := uint(1) // Default status
if articleApprovalStepLogsReq.Action == "approved" {
approvalStatusID = 2
} else if articleApprovalStepLogsReq.Action == "rejected" {
approvalStatusID = 3
}
articleApprovalStepLogsRes = &res.ArticleApprovalStepLogsDetailResponse{
ID: articleApprovalStepLogsReq.ID,
ArticleApprovalFlowID: articleApprovalStepLogsReq.ApprovalFlowId,
WorkflowStepID: uint(articleApprovalStepLogsReq.StepOrder),
ApproverUserID: articleApprovalStepLogsReq.ApprovedById,
ApprovalStatusID: approvalStatusID,
Comments: articleApprovalStepLogsReq.Message,
ApprovedAt: &articleApprovalStepLogsReq.ProcessedAt,
IsAutoApproved: false, // Default value
CreatedAt: articleApprovalStepLogsReq.CreatedAt,
UpdatedAt: articleApprovalStepLogsReq.CreatedAt, // Use CreatedAt as UpdatedAt
// Relations would be populated separately if needed
}
}
return articleApprovalStepLogsRes
}
func ArticleApprovalStepLogsSummaryResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
articleApprovalStepLogsReq *entity.ArticleApprovalStepLogs,
usersRepo usersRepository.UsersRepository,
) (articleApprovalStepLogsRes *res.ArticleApprovalStepLogsSummaryResponse) {
approverName := ""
if articleApprovalStepLogsReq.ApprovedById != nil {
findUser, _ := usersRepo.FindOne(clientId, *articleApprovalStepLogsReq.ApprovedById)
if findUser != nil {
approverName = findUser.Fullname
}
}
if articleApprovalStepLogsReq != nil {
// Map entity fields to response fields
approvalStatusName := "Pending"
if articleApprovalStepLogsReq.Action == "approved" {
approvalStatusName = "Approved"
} else if articleApprovalStepLogsReq.Action == "rejected" {
approvalStatusName = "Rejected"
}
articleApprovalStepLogsRes = &res.ArticleApprovalStepLogsSummaryResponse{
ID: articleApprovalStepLogsReq.ID,
WorkflowStepID: uint(articleApprovalStepLogsReq.StepOrder),
StepName: articleApprovalStepLogsReq.StepName,
ApproverUserID: articleApprovalStepLogsReq.ApprovedById,
ApproverUserName: &approverName,
ApprovalStatusID: uint(1), // Default status ID
ApprovalStatusName: approvalStatusName,
ApprovedAt: &articleApprovalStepLogsReq.ProcessedAt,
IsAutoApproved: false, // Default value
}
}
return articleApprovalStepLogsRes
}
func ApprovalHistoryResponseMapper(
log zerolog.Logger,
clientId *uuid.UUID,
articleApprovalStepLogsReq *entity.ArticleApprovalStepLogs,
usersRepo usersRepository.UsersRepository,
) (approvalHistoryRes *res.ApprovalHistoryResponse) {
if articleApprovalStepLogsReq != nil {
// Create a basic ApprovalHistoryResponse structure
// This would typically be built from multiple step logs, not a single one
approvalHistoryRes = &res.ApprovalHistoryResponse{
ArticleID: 0, // Would need article information
ArticleTitle: "", // Would need article information
WorkflowName: "", // Would need workflow information
StartedAt: articleApprovalStepLogsReq.CreatedAt,
CompletedAt: nil, // Would be set when workflow is complete
CurrentStep: &articleApprovalStepLogsReq.StepName,
Steps: []res.ArticleApprovalStepLogsSummaryResponse{}, // Would be populated with all steps
}
}
return approvalHistoryRes
}

View File

@ -1,438 +0,0 @@
package repository
import (
"fmt"
"time"
"web-medols-be/app/database"
"web-medols-be/app/database/entity"
"web-medols-be/app/module/article_approval_step_logs/request"
"web-medols-be/utils/paginator"
"github.com/google/uuid"
"github.com/rs/zerolog"
)
type articleApprovalStepLogsRepository struct {
DB *database.Database
Log zerolog.Logger
}
// ArticleApprovalStepLogsRepository define interface of IArticleApprovalStepLogsRepository
type ArticleApprovalStepLogsRepository interface {
// Basic CRUD
GetAll(clientId *uuid.UUID, req request.ArticleApprovalStepLogsQueryRequest) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (log *entity.ArticleApprovalStepLogs, err error)
GetByApprovalFlowId(clientId *uuid.UUID, approvalFlowId uint) (logs []*entity.ArticleApprovalStepLogs, err error)
Create(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (logReturn *entity.ArticleApprovalStepLogs, err error)
Update(id uint, log *entity.ArticleApprovalStepLogs) (err error)
Delete(clientId *uuid.UUID, id uint) (err error)
// Approval History Methods
GetApprovalHistory(clientId *uuid.UUID, articleId uint, page, limit int) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error)
GetUserApprovalHistory(clientId *uuid.UUID, userId uint, page, limit int, filters map[string]interface{}) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error)
GetLevelApprovalHistory(clientId *uuid.UUID, userLevelId uint, page, limit int, filters map[string]interface{}) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error)
// Analytics Methods
GetApprovalTimeAnalytics(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (analytics map[string]interface{}, err error)
GetUserPerformanceMetrics(clientId *uuid.UUID, userId uint, startDate, endDate time.Time) (metrics map[string]interface{}, err error)
GetWorkflowStepAnalytics(clientId *uuid.UUID, workflowId uint, stepOrder int) (analytics map[string]interface{}, err error)
// Audit Methods
GetAuditTrail(clientId *uuid.UUID, articleId uint) (trail []*entity.ArticleApprovalStepLogs, err error)
GetRecentActions(clientId *uuid.UUID, userId uint, limit int) (logs []*entity.ArticleApprovalStepLogs, err error)
}
func NewArticleApprovalStepLogsRepository(db *database.Database, log zerolog.Logger) ArticleApprovalStepLogsRepository {
return &articleApprovalStepLogsRepository{
DB: db,
Log: log,
}
}
// Basic CRUD implementations
func (_i *articleApprovalStepLogsRepository) GetAll(clientId *uuid.UUID, req request.ArticleApprovalStepLogsQueryRequest) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error) {
var count int64
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
// Apply client filter
if clientId != nil {
query = query.Where("client_id = ?", *clientId)
}
// Apply filters based on request
if req.ArticleApprovalFlowID != nil {
query = query.Where("article_approval_flow_id = ?", *req.ArticleApprovalFlowID)
}
if req.WorkflowStepID != nil {
query = query.Where("workflow_step_id = ?", *req.WorkflowStepID)
}
if req.ApproverUserID != nil {
query = query.Where("approver_user_id = ?", *req.ApproverUserID)
}
if req.ApprovalStatusID != nil {
query = query.Where("approval_status_id = ?", *req.ApprovalStatusID)
}
if req.DateFrom != nil {
query = query.Where("approved_at >= ?", *req.DateFrom)
}
if req.DateTo != nil {
query = query.Where("approved_at <= ?", *req.DateTo)
}
if req.IsAutoApproved != nil {
query = query.Where("is_auto_approved = ?", *req.IsAutoApproved)
}
// Count total records
query.Count(&count)
// Apply sorting
if req.Pagination.SortBy != "" {
direction := "ASC"
if req.Pagination.Sort == "desc" {
direction = "DESC"
}
query.Order(fmt.Sprintf("%s %s", req.Pagination.SortBy, direction))
} else {
direction := "DESC"
sortBy := "article_approval_step_logs.approved_at"
query.Order(fmt.Sprintf("%s %s", sortBy, direction))
}
// Apply pagination (manual calculation for better performance)
page := req.Pagination.Page
limit := req.Pagination.Limit
if page <= 0 {
page = 1
}
if limit <= 0 {
limit = 10
}
offset := (page - 1) * limit
err = query.Offset(offset).Limit(limit).Preload("ApprovalFlow").Preload("Step").Preload("ActionBy").Preload("Status").Find(&logs).Error
if err != nil {
return
}
// Create pagination response
paging = paginator.Pagination{
Page: page,
Limit: limit,
Count: count,
TotalPage: int((count + int64(limit) - 1) / int64(limit)),
}
return logs, paging, nil
}
func (_i *articleApprovalStepLogsRepository) FindOne(clientId *uuid.UUID, id uint) (log *entity.ArticleApprovalStepLogs, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Preload("ApprovalFlow").Preload("ApprovedBy").Preload("UserLevel")
err = query.First(&log, id).Error
return log, err
}
func (_i *articleApprovalStepLogsRepository) GetByApprovalFlowId(clientId *uuid.UUID, approvalFlowId uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("approval_flow_id = ?", approvalFlowId)
query = query.Preload("ApprovalFlow").Preload("ApprovedBy").Preload("UserLevel")
query = query.Order("step_order ASC, processed_at ASC")
err = query.Find(&logs).Error
return logs, err
}
func (_i *articleApprovalStepLogsRepository) Create(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (logReturn *entity.ArticleApprovalStepLogs, err error) {
log.ClientId = clientId
log.ProcessedAt = time.Now()
err = _i.DB.DB.Create(&log).Error
return log, err
}
func (_i *articleApprovalStepLogsRepository) Update(id uint, log *entity.ArticleApprovalStepLogs) (err error) {
err = _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{}).Where("id = ?", id).Updates(log).Error
return err
}
func (_i *articleApprovalStepLogsRepository) Delete(clientId *uuid.UUID, id uint) (err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
err = query.Delete(&entity.ArticleApprovalStepLogs{}, id).Error
return err
}
// Approval History Methods
func (_i *articleApprovalStepLogsRepository) GetApprovalHistory(clientId *uuid.UUID, articleId uint, page, limit int) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error) {
var count int64
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("article_approval_step_logs.client_id = ?", clientId)
}
// Join with approval flows to filter by article
query = query.Joins("JOIN article_approval_flows ON article_approval_step_logs.approval_flow_id = article_approval_flows.id")
query = query.Where("article_approval_flows.article_id = ?", articleId)
query = query.Preload("ApprovalFlow").Preload("ApprovedBy").Preload("UserLevel")
query = query.Order("article_approval_step_logs.processed_at DESC")
err = query.Count(&count).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
offset := (page - 1) * limit
err = query.Offset(offset).Limit(limit).Find(&logs).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
paging = paginator.Pagination{
Page: page,
Limit: limit,
Count: count,
TotalPage: int((count + int64(limit) - 1) / int64(limit)),
}
return logs, paging, nil
}
func (_i *articleApprovalStepLogsRepository) GetUserApprovalHistory(clientId *uuid.UUID, userId uint, page, limit int, filters map[string]interface{}) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error) {
var count int64
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("article_approval_step_logs.client_id = ?", clientId)
}
query = query.Where("approved_by_id = ?", userId)
// Apply filters
if action, ok := filters["action"]; ok {
query = query.Where("action = ?", action)
}
if startDate, ok := filters["start_date"]; ok {
query = query.Where("processed_at >= ?", startDate)
}
if endDate, ok := filters["end_date"]; ok {
query = query.Where("processed_at <= ?", endDate)
}
query = query.Preload("ApprovalFlow").Preload("ApprovedBy").Preload("UserLevel")
query = query.Order("processed_at DESC")
err = query.Count(&count).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
offset := (page - 1) * limit
err = query.Offset(offset).Limit(limit).Find(&logs).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
paging = paginator.Pagination{
Page: page,
Limit: limit,
Count: count,
TotalPage: int((count + int64(limit) - 1) / int64(limit)),
}
return logs, paging, nil
}
func (_i *articleApprovalStepLogsRepository) GetLevelApprovalHistory(clientId *uuid.UUID, userLevelId uint, page, limit int, filters map[string]interface{}) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error) {
var count int64
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("article_approval_step_logs.client_id = ?", clientId)
}
query = query.Where("user_level_id = ?", userLevelId)
// Apply filters
if action, ok := filters["action"]; ok {
query = query.Where("action = ?", action)
}
if startDate, ok := filters["start_date"]; ok {
query = query.Where("processed_at >= ?", startDate)
}
if endDate, ok := filters["end_date"]; ok {
query = query.Where("processed_at <= ?", endDate)
}
query = query.Preload("ApprovalFlow").Preload("ApprovedBy").Preload("UserLevel")
query = query.Order("processed_at DESC")
err = query.Count(&count).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
offset := (page - 1) * limit
err = query.Offset(offset).Limit(limit).Find(&logs).Error
if err != nil {
return nil, paginator.Pagination{}, err
}
paging = paginator.Pagination{
Page: page,
Limit: limit,
Count: count,
TotalPage: int((count + int64(limit) - 1) / int64(limit)),
}
return logs, paging, nil
}
// Analytics Methods
func (_i *articleApprovalStepLogsRepository) GetApprovalTimeAnalytics(clientId *uuid.UUID, userLevelId uint, startDate, endDate time.Time) (analytics map[string]interface{}, err error) {
analytics = make(map[string]interface{})
// Get average approval time for this level
var avgTime float64
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("user_level_id = ? AND action IN ('approve', 'reject') AND processed_at BETWEEN ? AND ?", userLevelId, startDate, endDate)
query = query.Select("AVG(EXTRACT(EPOCH FROM (processed_at - created_at))) as avg_time")
err = query.Scan(&avgTime).Error
if err != nil {
return nil, err
}
analytics["average_approval_time_seconds"] = avgTime
analytics["level_id"] = userLevelId
analytics["period_start"] = startDate
analytics["period_end"] = endDate
return analytics, nil
}
func (_i *articleApprovalStepLogsRepository) GetUserPerformanceMetrics(clientId *uuid.UUID, userId uint, startDate, endDate time.Time) (metrics map[string]interface{}, err error) {
metrics = make(map[string]interface{})
// Get counts by action
var approvedCount, rejectedCount, revisionCount int64
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("approved_by_id = ? AND processed_at BETWEEN ? AND ?", userId, startDate, endDate)
// Approved count
query.Where("action = 'approve'").Count(&approvedCount)
// Rejected count
query.Where("action = 'reject'").Count(&rejectedCount)
// Revision requested count
query.Where("action = 'request_revision'").Count(&revisionCount)
metrics["approved_count"] = approvedCount
metrics["rejected_count"] = rejectedCount
metrics["revision_requested_count"] = revisionCount
metrics["total_processed"] = approvedCount + rejectedCount + revisionCount
metrics["user_id"] = userId
metrics["period_start"] = startDate
metrics["period_end"] = endDate
return metrics, nil
}
func (_i *articleApprovalStepLogsRepository) GetWorkflowStepAnalytics(clientId *uuid.UUID, workflowId uint, stepOrder int) (analytics map[string]interface{}, err error) {
analytics = make(map[string]interface{})
// Get step performance metrics
var totalProcessed, approvedCount, rejectedCount int64
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
// Join with approval flows to filter by workflow
query = query.Joins("JOIN article_approval_flows ON article_approval_step_logs.approval_flow_id = article_approval_flows.id")
query = query.Where("article_approval_flows.workflow_id = ? AND article_approval_step_logs.step_order = ?", workflowId, stepOrder)
// Total processed
query.Count(&totalProcessed)
// Approved count
query.Where("article_approval_step_logs.action = 'approve'").Count(&approvedCount)
// Rejected count
query.Where("article_approval_step_logs.action = 'reject'").Count(&rejectedCount)
analytics["workflow_id"] = workflowId
analytics["step_order"] = stepOrder
analytics["total_processed"] = totalProcessed
analytics["approved_count"] = approvedCount
analytics["rejected_count"] = rejectedCount
analytics["approval_rate"] = float64(approvedCount) / float64(totalProcessed) * 100
return analytics, nil
}
// Audit Methods
func (_i *articleApprovalStepLogsRepository) GetAuditTrail(clientId *uuid.UUID, articleId uint) (trail []*entity.ArticleApprovalStepLogs, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("article_approval_step_logs.client_id = ?", clientId)
}
// Join with approval flows to filter by article
query = query.Joins("JOIN article_approval_flows ON article_approval_step_logs.approval_flow_id = article_approval_flows.id")
query = query.Where("article_approval_flows.article_id = ?", articleId)
query = query.Preload("ApprovalFlow").Preload("ApprovedBy").Preload("UserLevel")
query = query.Order("article_approval_step_logs.processed_at ASC")
err = query.Find(&trail).Error
return trail, err
}
func (_i *articleApprovalStepLogsRepository) GetRecentActions(clientId *uuid.UUID, userId uint, limit int) (logs []*entity.ArticleApprovalStepLogs, err error) {
query := _i.DB.DB.Model(&entity.ArticleApprovalStepLogs{})
if clientId != nil {
query = query.Where("client_id = ?", clientId)
}
query = query.Where("approved_by_id = ?", userId)
query = query.Preload("ApprovalFlow").Preload("ApprovedBy").Preload("UserLevel")
query = query.Order("processed_at DESC")
query = query.Limit(limit)
err = query.Find(&logs).Error
return logs, err
}

View File

@ -1,243 +0,0 @@
package request
import (
"strconv"
"time"
"web-medols-be/app/database/entity"
"web-medols-be/utils/paginator"
)
type CreateArticleApprovalStepLogsRequest struct {
ArticleApprovalFlowID uint `json:"articleApprovalFlowId" validate:"required"`
WorkflowStepID uint `json:"workflowStepId" validate:"required"`
ApproverUserID *uint `json:"approverUserId"`
ApprovalStatusID uint `json:"approvalStatusId" validate:"required"`
Comments *string `json:"comments" validate:"omitempty,max=1000"`
ApprovedAt *time.Time `json:"approvedAt"`
DueDate *time.Time `json:"dueDate"`
IsAutoApproved bool `json:"isAutoApproved"`
}
type UpdateArticleApprovalStepLogsRequest struct {
ApproverUserID *uint `json:"approverUserId"`
ApprovalStatusID *uint `json:"approvalStatusId"`
Comments *string `json:"comments" validate:"omitempty,max=1000"`
ApprovedAt *time.Time `json:"approvedAt"`
DueDate *time.Time `json:"dueDate"`
IsAutoApproved *bool `json:"isAutoApproved"`
}
type ArticleApprovalStepLogsQueryRequest struct {
ArticleApprovalFlowID *uint `json:"articleApprovalFlowId" form:"articleApprovalFlowId"`
WorkflowStepID *uint `json:"workflowStepId" form:"workflowStepId"`
ApproverUserID *uint `json:"approverUserId" form:"approverUserId"`
ApprovalStatusID *uint `json:"approvalStatusId" form:"approvalStatusId"`
IsAutoApproved *bool `json:"isAutoApproved" form:"isAutoApproved"`
DateFrom *time.Time `json:"dateFrom" form:"dateFrom"`
DateTo *time.Time `json:"dateTo" form:"dateTo"`
Pagination *paginator.Pagination `json:"pagination"`
}
type ProcessApprovalRequest struct {
UserID uint `json:"userId" validate:"required"`
StatusID uint `json:"statusId" validate:"required"`
ApprovalStatusID uint `json:"approvalStatusId" validate:"required"`
Comments *string `json:"comments" validate:"omitempty,max=1000"`
}
type BulkProcessApprovalRequest struct {
LogIDs []uint `json:"logIds" validate:"required,min=1,max=50,dive,required"`
UserID uint `json:"userId" validate:"required"`
StatusID uint `json:"statusId" validate:"required"`
StepLogIDs []uint `json:"stepLogIds" validate:"required,min=1,max=50,dive,required"`
ApprovalStatusID uint `json:"approvalStatusId" validate:"required"`
Comments *string `json:"comments" validate:"omitempty,max=1000"`
}
type GetApprovalHistoryRequest struct {
ArticleID *uint `json:"articleId" form:"articleId"`
UserID *uint `json:"userId" form:"userId"`
WorkflowID *uint `json:"workflowId" form:"workflowId"`
StatusID *uint `json:"statusId" form:"statusId"`
DateFrom *time.Time `json:"dateFrom" form:"dateFrom"`
DateTo *time.Time `json:"dateTo" form:"dateTo"`
Page int `json:"page" form:"page" validate:"min=1"`
Limit int `json:"limit" form:"limit" validate:"min=1,max=100"`
SortBy *string `json:"sortBy" form:"sortBy"`
SortOrder *string `json:"sortOrder" form:"sortOrder" validate:"omitempty,oneof=asc desc"`
}
type GetApprovalStatsRequest struct {
WorkflowID *uint `json:"workflowId" form:"workflowId"`
StepID *uint `json:"stepId" form:"stepId"`
UserID *uint `json:"userId" form:"userId"`
DateFrom *time.Time `json:"dateFrom" form:"dateFrom"`
DateTo *time.Time `json:"dateTo" form:"dateTo"`
GroupBy *string `json:"groupBy" form:"groupBy" validate:"omitempty,oneof=step user workflow status"`
}
type GetUserWorkloadRequest struct {
UserID *uint `json:"userId" form:"userId"`
RoleID *uint `json:"roleId" form:"roleId"`
StatusID *uint `json:"statusId" form:"statusId"`
DateFrom *time.Time `json:"dateFrom" form:"dateFrom"`
DateTo *time.Time `json:"dateTo" form:"dateTo"`
IncludeStats bool `json:"includeStats" form:"includeStats"`
Page int `json:"page" form:"page" validate:"min=1"`
Limit int `json:"limit" form:"limit" validate:"min=1,max=100"`
}
// Missing request types that are referenced in controller
type ArticleApprovalStepLogsQueryRequestContext struct {
ApprovalFlowId string `json:"approvalFlowId" form:"approvalFlowId"`
StepId string `json:"stepId" form:"stepId"`
ActionById string `json:"actionById" form:"actionById"`
ActionType string `json:"actionType" form:"actionType"`
StatusId string `json:"statusId" form:"statusId"`
ActionDateFrom string `json:"actionDateFrom" form:"actionDateFrom"`
ActionDateTo string `json:"actionDateTo" form:"actionDateTo"`
UserLevelId string `json:"userLevelId" form:"userLevelId"`
IsUrgent string `json:"isUrgent" form:"isUrgent"`
Search string `json:"search" form:"search"`
OrderBy string `json:"orderBy" form:"orderBy"`
OrderDirection string `json:"orderDirection" form:"orderDirection"`
}
func (r *ArticleApprovalStepLogsQueryRequestContext) ToParamRequest() *ArticleApprovalStepLogsQueryRequest {
// Convert string parameters to appropriate types using helper functions
approvalFlowId := parseStringToUintPtr(r.ApprovalFlowId)
workflowStepId := parseStringToUintPtr(r.StepId)
approverUserId := parseStringToUintPtr(r.ActionById)
approvalStatusId := parseStringToUintPtr(r.StatusId)
isAutoApproved := parseStringToBoolPtr(r.IsUrgent)
dateFrom := parseStringToTimePtr(r.ActionDateFrom)
dateTo := parseStringToTimePtr(r.ActionDateTo)
// Handle string parameters
var sortBy *string
var sortOrder *string
if r.OrderBy != "" {
sortBy = &r.OrderBy
}
if r.OrderDirection != "" {
sortOrder = &r.OrderDirection
}
// Set default pagination
page := 1
limit := 10
// Create pagination object
pagination := &paginator.Pagination{
Page: page,
Limit: limit,
}
// Set sorting if provided
if sortBy != nil {
pagination.SortBy = *sortBy
}
if sortOrder != nil {
pagination.Sort = *sortOrder
}
return &ArticleApprovalStepLogsQueryRequest{
ArticleApprovalFlowID: approvalFlowId,
WorkflowStepID: workflowStepId,
ApproverUserID: approverUserId,
ApprovalStatusID: approvalStatusId,
IsAutoApproved: isAutoApproved,
DateFrom: dateFrom,
DateTo: dateTo,
Pagination: pagination,
}
}
// Helper function to parse string to uint pointer
func parseStringToUintPtr(s string) *uint {
if s == "" {
return nil
}
if val, err := strconv.ParseUint(s, 10, 32); err == nil {
uintVal := uint(val)
return &uintVal
}
return nil
}
// Helper function to parse string to bool pointer
func parseStringToBoolPtr(s string) *bool {
if s == "" {
return nil
}
if val, err := strconv.ParseBool(s); err == nil {
return &val
}
return nil
}
// Helper function to parse string to time pointer
func parseStringToTimePtr(s string) *time.Time {
if s == "" {
return nil
}
// Try different date formats
formats := []string{
"2006-01-02",
"2006-01-02T15:04:05Z",
"2006-01-02T15:04:05Z07:00",
"2006-01-02 15:04:05",
}
for _, format := range formats {
if val, err := time.Parse(format, s); err == nil {
return &val
}
}
return nil
}
type ArticleApprovalStepLogsCreateRequest struct {
ArticleApprovalFlowID uint `json:"articleApprovalFlowId" validate:"required"`
WorkflowStepID uint `json:"workflowStepId" validate:"required"`
ApproverUserID *uint `json:"approverUserId"`
ApprovalStatusID uint `json:"approvalStatusId" validate:"required"`
Comments *string `json:"comments" validate:"omitempty,max=1000"`
ApprovedAt *time.Time `json:"approvedAt"`
DueDate *time.Time `json:"dueDate"`
IsAutoApproved bool `json:"isAutoApproved"`
}
func (r *ArticleApprovalStepLogsCreateRequest) ToEntity() *entity.ArticleApprovalStepLogs {
// Return the entity representation
return &entity.ArticleApprovalStepLogs{
ApprovalFlowId: r.ArticleApprovalFlowID,
StepOrder: int(r.WorkflowStepID),
ApprovedById: r.ApproverUserID,
Action: "pending", // Default action
Message: r.Comments,
ProcessedAt: time.Now(),
UserLevelId: 1, // Default user level
}
}
type ArticleApprovalStepLogsUpdateRequest struct {
ApproverUserID *uint `json:"approverUserId"`
ApprovalStatusID *uint `json:"approvalStatusId"`
Comments *string `json:"comments" validate:"omitempty,max=1000"`
ApprovedAt *time.Time `json:"approvedAt"`
DueDate *time.Time `json:"dueDate"`
IsAutoApproved *bool `json:"isAutoApproved"`
}
func (r *ArticleApprovalStepLogsUpdateRequest) ToEntity() *entity.ArticleApprovalStepLogs {
// Return the entity representation
return &entity.ArticleApprovalStepLogs{
ApprovedById: r.ApproverUserID,
Action: "updated", // Default action
Message: r.Comments,
ProcessedAt: time.Now(),
}
}

View File

@ -1,91 +0,0 @@
package response
import (
"time"
approvalWorkflowStepsResponse "web-medols-be/app/module/approval_workflow_steps/response"
articlesResponse "web-medols-be/app/module/articles/response"
usersResponse "web-medols-be/app/module/users/response"
)
type ArticleApprovalStepLogsResponse struct {
ID uint `json:"id"`
ApprovalFlowId uint `json:"approvalFlowId"`
StepOrder int `json:"stepOrder"`
StepName string `json:"stepName"`
ApprovedById *uint `json:"approvedById"`
Action string `json:"action"`
Message *string `json:"message"`
ProcessedAt time.Time `json:"processedAt"`
UserLevelId uint `json:"userLevelId"`
ClientId *string `json:"clientId"`
CreatedAt time.Time `json:"createdAt"`
// Relations
ApprovedBy *usersResponse.UsersResponse `json:"approvedBy,omitempty"`
}
type ArticleApprovalStepLogsDetailResponse struct {
ID uint `json:"id"`
ArticleApprovalFlowID uint `json:"articleApprovalFlowId"`
WorkflowStepID uint `json:"workflowStepId"`
ApproverUserID *uint `json:"approverUserId"`
ApprovalStatusID uint `json:"approvalStatusId"`
ApprovalStatusName *string `json:"approvalStatusName,omitempty"`
Comments *string `json:"comments"`
ApprovedAt *time.Time `json:"approvedAt"`
DueDate *time.Time `json:"dueDate"`
IsAutoApproved bool `json:"isAutoApproved"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
// Relations with full details
WorkflowStep *approvalWorkflowStepsResponse.ApprovalWorkflowStepsResponse `json:"workflowStep,omitempty"`
ApproverUser *usersResponse.UsersResponse `json:"approverUser,omitempty"`
Article *articlesResponse.ArticlesResponse `json:"article,omitempty"`
}
type ArticleApprovalStepLogsSummaryResponse struct {
ID uint `json:"id"`
WorkflowStepID uint `json:"workflowStepId"`
StepName string `json:"stepName"`
ApproverUserID *uint `json:"approverUserId"`
ApproverUserName *string `json:"approverUserName,omitempty"`
ApprovalStatusID uint `json:"approvalStatusId"`
ApprovalStatusName string `json:"approvalStatusName"`
ApprovedAt *time.Time `json:"approvedAt"`
DueDate *time.Time `json:"dueDate"`
IsAutoApproved bool `json:"isAutoApproved"`
}
type ApprovalHistoryResponse struct {
ArticleID uint `json:"articleId"`
ArticleTitle string `json:"articleTitle"`
WorkflowName string `json:"workflowName"`
StartedAt time.Time `json:"startedAt"`
CompletedAt *time.Time `json:"completedAt"`
CurrentStep *string `json:"currentStep"`
Steps []ArticleApprovalStepLogsSummaryResponse `json:"steps"`
}
type ApprovalStepStatsResponse struct {
StepName string `json:"stepName"`
TotalApprovals int `json:"totalApprovals"`
PendingApprovals int `json:"pendingApprovals"`
ApprovedCount int `json:"approvedCount"`
RejectedCount int `json:"rejectedCount"`
AutoApprovedCount int `json:"autoApprovedCount"`
AverageProcessingTime float64 `json:"averageProcessingTime"` // in hours
OverdueCount int `json:"overdueCount"`
}
type UserApprovalStatsResponse struct {
UserID uint `json:"userId"`
UserName string `json:"userName"`
TotalAssigned int `json:"totalAssigned"`
PendingApprovals int `json:"pendingApprovals"`
CompletedApprovals int `json:"completedApprovals"`
ApprovedCount int `json:"approvedCount"`
RejectedCount int `json:"rejectedCount"`
AverageProcessingTime float64 `json:"averageProcessingTime"` // in hours
OverdueCount int `json:"overdueCount"`
}

View File

@ -1,296 +0,0 @@
package service
import (
"errors"
"fmt"
"time"
"web-medols-be/app/database/entity"
stepRepo "web-medols-be/app/module/approval_workflow_steps/repository"
flowRepo "web-medols-be/app/module/article_approval_flows/repository"
"web-medols-be/app/module/article_approval_step_logs/repository"
"web-medols-be/app/module/article_approval_step_logs/request"
"web-medols-be/utils/paginator"
"github.com/google/uuid"
"github.com/rs/zerolog"
)
type articleApprovalStepLogsService struct {
ArticleApprovalStepLogsRepository repository.ArticleApprovalStepLogsRepository
ArticleApprovalFlowsRepository flowRepo.ArticleApprovalFlowsRepository
ApprovalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository
Log zerolog.Logger
}
// ArticleApprovalStepLogsService define interface of IArticleApprovalStepLogsService
type ArticleApprovalStepLogsService interface {
// Basic CRUD
GetAll(clientId *uuid.UUID, req request.ArticleApprovalStepLogsQueryRequest) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error)
FindOne(clientId *uuid.UUID, id uint) (log *entity.ArticleApprovalStepLogs, err error)
Create(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (logReturn *entity.ArticleApprovalStepLogs, err error)
Update(clientId *uuid.UUID, id uint, log *entity.ArticleApprovalStepLogs) (err error)
Delete(clientId *uuid.UUID, id uint) (err error)
// Approval process management
GetByApprovalFlowID(clientId *uuid.UUID, flowID uint) (logs []*entity.ArticleApprovalStepLogs, err error)
GetByWorkflowStepID(clientId *uuid.UUID, stepID uint) (logs []*entity.ArticleApprovalStepLogs, err error)
GetByApproverUserID(clientId *uuid.UUID, userID uint) (logs []*entity.ArticleApprovalStepLogs, err error)
GetPendingApprovals(clientId *uuid.UUID, userID *uint, roleID *uint) (logs []*entity.ArticleApprovalStepLogs, err error)
GetOverdueApprovals(clientId *uuid.UUID, userID *uint) (logs []*entity.ArticleApprovalStepLogs, err error)
// Approval actions
ProcessApproval(clientId *uuid.UUID, logID uint, userID uint, statusID uint, comments *string) (err error)
BulkProcessApproval(clientId *uuid.UUID, logIDs []uint, userID uint, statusID uint, comments *string) (err error)
AutoApprove(clientId *uuid.UUID, logID uint, reason string) (err error)
// History and analytics
GetApprovalHistory(clientId *uuid.UUID, articleID *uint, userID *uint, filters map[string]interface{}) (logs []*entity.ArticleApprovalStepLogs, err error)
GetApprovalStats(clientId *uuid.UUID, filters map[string]interface{}) (stats map[string]interface{}, err error)
GetUserWorkload(clientId *uuid.UUID, userID uint, includeStats bool) (workload map[string]interface{}, err error)
// Validation
ValidateStepLog(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (isValid bool, errors []string, err error)
CanProcessApproval(clientId *uuid.UUID, logID uint, userID uint) (canProcess bool, reason string, err error)
}
func NewArticleApprovalStepLogsService(
articleApprovalStepLogsRepository repository.ArticleApprovalStepLogsRepository,
articleApprovalFlowsRepository flowRepo.ArticleApprovalFlowsRepository,
approvalWorkflowStepsRepository stepRepo.ApprovalWorkflowStepsRepository,
log zerolog.Logger,
) ArticleApprovalStepLogsService {
return &articleApprovalStepLogsService{
ArticleApprovalStepLogsRepository: articleApprovalStepLogsRepository,
ArticleApprovalFlowsRepository: articleApprovalFlowsRepository,
ApprovalWorkflowStepsRepository: approvalWorkflowStepsRepository,
Log: log,
}
}
func (_i *articleApprovalStepLogsService) GetAll(clientId *uuid.UUID, req request.ArticleApprovalStepLogsQueryRequest) (logs []*entity.ArticleApprovalStepLogs, paging paginator.Pagination, err error) {
return _i.ArticleApprovalStepLogsRepository.GetAll(clientId, req)
}
func (_i *articleApprovalStepLogsService) FindOne(clientId *uuid.UUID, id uint) (log *entity.ArticleApprovalStepLogs, err error) {
return _i.ArticleApprovalStepLogsRepository.FindOne(clientId, id)
}
func (_i *articleApprovalStepLogsService) Create(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (logReturn *entity.ArticleApprovalStepLogs, err error) {
// Validate business rules
if log.ApprovalFlowId == 0 {
return nil, errors.New("approval flow ID is required")
}
// Validate approval flow exists
flow, err := _i.ArticleApprovalFlowsRepository.FindOne(clientId, log.ApprovalFlowId)
if err != nil {
return nil, fmt.Errorf("approval flow not found: %w", err)
}
if flow == nil {
return nil, errors.New("approval flow not found")
}
// Validate step log data
isValid, validationErrors, err := _i.ValidateStepLog(clientId, log)
if err != nil {
return nil, err
}
if !isValid {
return nil, fmt.Errorf("validation failed: %v", validationErrors)
}
return _i.ArticleApprovalStepLogsRepository.Create(clientId, log)
}
func (_i *articleApprovalStepLogsService) Update(clientId *uuid.UUID, id uint, log *entity.ArticleApprovalStepLogs) (err error) {
// Check if log exists
existingLog, err := _i.ArticleApprovalStepLogsRepository.FindOne(clientId, id)
if err != nil {
return err
}
if existingLog == nil {
return errors.New("step log not found")
}
return _i.ArticleApprovalStepLogsRepository.Update(id, log)
}
func (_i *articleApprovalStepLogsService) Delete(clientId *uuid.UUID, id uint) (err error) {
// Check if log exists
existingLog, err := _i.ArticleApprovalStepLogsRepository.FindOne(clientId, id)
if err != nil {
return err
}
if existingLog == nil {
return errors.New("step log not found")
}
return _i.ArticleApprovalStepLogsRepository.Delete(clientId, id)
}
func (_i *articleApprovalStepLogsService) GetByApprovalFlowID(clientId *uuid.UUID, flowID uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
return _i.ArticleApprovalStepLogsRepository.GetByApprovalFlowId(clientId, flowID)
}
func (_i *articleApprovalStepLogsService) GetByWorkflowStepID(clientId *uuid.UUID, stepID uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
// This method is not implemented in repository, return empty slice for now
return []*entity.ArticleApprovalStepLogs{}, nil
}
func (_i *articleApprovalStepLogsService) GetByApproverUserID(clientId *uuid.UUID, userID uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
// This method is not implemented in repository, return empty slice for now
return []*entity.ArticleApprovalStepLogs{}, nil
}
func (_i *articleApprovalStepLogsService) GetPendingApprovals(clientId *uuid.UUID, userID *uint, roleID *uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
// This method is not implemented in repository, return empty slice for now
return []*entity.ArticleApprovalStepLogs{}, nil
}
func (_i *articleApprovalStepLogsService) GetOverdueApprovals(clientId *uuid.UUID, userID *uint) (logs []*entity.ArticleApprovalStepLogs, err error) {
// This method is not implemented in repository, return empty slice for now
return []*entity.ArticleApprovalStepLogs{}, nil
}
func (_i *articleApprovalStepLogsService) ProcessApproval(clientId *uuid.UUID, logID uint, userID uint, statusID uint, comments *string) (err error) {
// Check if user can process this approval
canProcess, reason, err := _i.CanProcessApproval(clientId, logID, userID)
if err != nil {
return err
}
if !canProcess {
return fmt.Errorf("cannot process approval: %s", reason)
}
// Update the step log
now := time.Now()
updateLog := &entity.ArticleApprovalStepLogs{
ApprovedById: &userID,
Action: "approve", // This should be determined based on statusID
Message: comments,
ProcessedAt: now,
}
return _i.ArticleApprovalStepLogsRepository.Update(logID, updateLog)
}
func (_i *articleApprovalStepLogsService) BulkProcessApproval(clientId *uuid.UUID, logIDs []uint, userID uint, statusID uint, comments *string) (err error) {
// Validate all logs can be processed by this user
for _, logID := range logIDs {
canProcess, reason, err := _i.CanProcessApproval(clientId, logID, userID)
if err != nil {
return err
}
if !canProcess {
return fmt.Errorf("cannot process approval for log %d: %s", logID, reason)
}
}
// Note: BulkUpdate method is not available in repository
// This functionality would need to be implemented when repository is updated
// For now, we'll process each log individually
now := time.Now()
for _, logID := range logIDs {
updateLog := &entity.ArticleApprovalStepLogs{
ApprovedById: &userID,
Action: "approve",
Message: comments,
ProcessedAt: now,
}
err := _i.ArticleApprovalStepLogsRepository.Update(logID, updateLog)
if err != nil {
return err
}
}
return nil
}
func (_i *articleApprovalStepLogsService) AutoApprove(clientId *uuid.UUID, logID uint, reason string) (err error) {
// Get the step log
log, err := _i.ArticleApprovalStepLogsRepository.FindOne(clientId, logID)
if err != nil {
return err
}
if log == nil {
return errors.New("step log not found")
}
// Note: WorkflowStepID and AutoApprove fields are not available in current entity structure
// This functionality would need to be implemented when entity is updated
// Auto approve with current entity structure
now := time.Now()
updateLog := &entity.ArticleApprovalStepLogs{
Action: "approve",
Message: &reason,
ProcessedAt: now,
}
return _i.ArticleApprovalStepLogsRepository.Update(logID, updateLog)
}
func (_i *articleApprovalStepLogsService) GetApprovalHistory(clientId *uuid.UUID, articleID *uint, userID *uint, filters map[string]interface{}) (logs []*entity.ArticleApprovalStepLogs, err error) {
// This method signature doesn't match repository, return empty slice for now
return []*entity.ArticleApprovalStepLogs{}, nil
}
func (_i *articleApprovalStepLogsService) GetApprovalStats(clientId *uuid.UUID, filters map[string]interface{}) (stats map[string]interface{}, err error) {
// This method is not implemented in repository, return empty map for now
return make(map[string]interface{}), nil
}
func (_i *articleApprovalStepLogsService) GetUserWorkload(clientId *uuid.UUID, userID uint, includeStats bool) (workload map[string]interface{}, err error) {
// This method is not implemented in repository, return empty map for now
return make(map[string]interface{}), nil
}
func (_i *articleApprovalStepLogsService) ValidateStepLog(clientId *uuid.UUID, log *entity.ArticleApprovalStepLogs) (isValid bool, errors []string, err error) {
var validationErrors []string
// Validate required fields
if log.ApprovalFlowId == 0 {
validationErrors = append(validationErrors, "approval flow ID is required")
}
// Note: WorkflowStepID field is not available in current entity structure
// This validation would need to be implemented when entity is updated
// Note: ApprovalStatusID field is not available in current entity structure
// This validation would need to be implemented when entity is updated
// Validate message length if provided
if log.Message != nil && len(*log.Message) > 1000 {
validationErrors = append(validationErrors, "message must not exceed 1000 characters")
}
// Note: DueDate field is not available in current entity structure
// This validation would need to be implemented when entity is updated
return len(validationErrors) == 0, validationErrors, nil
}
func (_i *articleApprovalStepLogsService) CanProcessApproval(clientId *uuid.UUID, logID uint, userID uint) (canProcess bool, reason string, err error) {
// Get the step log
log, err := _i.ArticleApprovalStepLogsRepository.FindOne(clientId, logID)
if err != nil {
return false, "", err
}
if log == nil {
return false, "step log not found", nil
}
// Check if already processed (using ProcessedAt field)
if !log.ProcessedAt.IsZero() {
return false, "approval already processed", nil
}
// Check if user has permission to approve this step
// This would require checking user roles against step approver role
// For now, we'll allow any user to process
// TODO: Implement proper role-based authorization
// Note: DueDate field is not available in current entity structure
// This check would need to be implemented when entity is updated
return true, "", nil
}

View File

@ -2,10 +2,10 @@ package article_approvals
import ( import (
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"go-humas-be/app/module/article_approvals/controller"
"go-humas-be/app/module/article_approvals/repository"
"go-humas-be/app/module/article_approvals/service"
"go.uber.org/fx" "go.uber.org/fx"
"web-medols-be/app/module/article_approvals/controller"
"web-medols-be/app/module/article_approvals/repository"
"web-medols-be/app/module/article_approvals/service"
) )
// struct of ArticleApprovalsRouter // struct of ArticleApprovalsRouter

View File

@ -1,17 +1,15 @@
package controller package controller
import ( import (
"strconv"
"web-medols-be/app/middleware"
"web-medols-be/app/module/article_approvals/request"
"web-medols-be/app/module/article_approvals/service"
"web-medols-be/utils/paginator"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go-humas-be/app/module/article_approvals/request"
"go-humas-be/app/module/article_approvals/service"
"go-humas-be/utils/paginator"
"strconv"
utilRes "web-medols-be/utils/response" utilRes "go-humas-be/utils/response"
utilVal "web-medols-be/utils/validator" utilVal "go-humas-be/utils/validator"
) )
type articleApprovalsController struct { type articleApprovalsController struct {
@ -109,9 +107,8 @@ func (_i *articleApprovalsController) Show(c *fiber.Ctx) error {
// @Description API for create ArticleApprovals // @Description API for create ArticleApprovals
// @Tags ArticleApprovals // @Tags ArticleApprovals
// @Security Bearer // @Security Bearer
// @Param X-Client-Key header string false "Insert the X-Client-Key" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" // @Param Authorization header string true "Insert your access token" default(Bearer <Add access token here>)
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
// @Param payload body request.ArticleApprovalsCreateRequest true "Required payload" // @Param payload body request.ArticleApprovalsCreateRequest true "Required payload"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError // @Failure 400 {object} response.BadRequestError
@ -125,11 +122,7 @@ func (_i *articleApprovalsController) Save(c *fiber.Ctx) error {
} }
authToken := c.Get("Authorization") authToken := c.Get("Authorization")
dataResult, err := _i.articleApprovalsService.Save(*req, authToken)
// Get ClientId from context
clientId := middleware.GetClientID(c)
dataResult, err := _i.articleApprovalsService.Save(clientId, *req, authToken)
if err != nil { if err != nil {
return err return err
} }
@ -146,7 +139,7 @@ func (_i *articleApprovalsController) Save(c *fiber.Ctx) error {
// @Description API for update ArticleApprovals // @Description API for update ArticleApprovals
// @Tags ArticleApprovals // @Tags ArticleApprovals
// @Security Bearer // @Security Bearer
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param payload body request.ArticleApprovalsUpdateRequest true "Required payload" // @Param payload body request.ArticleApprovalsUpdateRequest true "Required payload"
// @Param id path int true "ArticleApprovals ID" // @Param id path int true "ArticleApprovals ID"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
@ -181,7 +174,7 @@ func (_i *articleApprovalsController) Update(c *fiber.Ctx) error {
// @Description API for delete ArticleApprovals // @Description API for delete ArticleApprovals
// @Tags ArticleApprovals // @Tags ArticleApprovals
// @Security Bearer // @Security Bearer
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token" // @Param X-Csrf-Token header string true "Insert the X-Csrf-Token"
// @Param id path int true "ArticleApprovals ID" // @Param id path int true "ArticleApprovals ID"
// @Success 200 {object} response.Response // @Success 200 {object} response.Response
// @Failure 400 {object} response.BadRequestError // @Failure 400 {object} response.BadRequestError

View File

@ -2,7 +2,7 @@ package controller
import ( import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
"web-medols-be/app/module/article_approvals/service" "go-humas-be/app/module/article_approvals/service"
) )
type Controller struct { type Controller struct {

View File

@ -1,8 +1,8 @@
package mapper package mapper
import ( import (
"web-medols-be/app/database/entity" "go-humas-be/app/database/entity"
res "web-medols-be/app/module/article_approvals/response" res "go-humas-be/app/module/article_approvals/response"
) )
func ArticleApprovalsResponseMapper(articleApprovalsReq *entity.ArticleApprovals) (articleApprovalsRes *res.ArticleApprovalsResponse) { func ArticleApprovalsResponseMapper(articleApprovalsReq *entity.ArticleApprovals) (articleApprovalsRes *res.ArticleApprovalsResponse) {

View File

@ -3,12 +3,12 @@ package repository
import ( import (
"fmt" "fmt"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"go-humas-be/app/database"
"go-humas-be/app/database/entity"
"go-humas-be/app/module/article_approvals/request"
"go-humas-be/utils/paginator"
utilSvc "go-humas-be/utils/service"
"strings" "strings"
"web-medols-be/app/database"
"web-medols-be/app/database/entity"
"web-medols-be/app/module/article_approvals/request"
"web-medols-be/utils/paginator"
utilSvc "web-medols-be/utils/service"
) )
type articleApprovalsRepository struct { type articleApprovalsRepository struct {

View File

@ -1,9 +1,9 @@
package request package request
import ( import (
"go-humas-be/app/database/entity"
"go-humas-be/utils/paginator"
"strconv" "strconv"
"web-medols-be/app/database/entity"
"web-medols-be/utils/paginator"
) )
type ArticleApprovalsGeneric interface { type ArticleApprovalsGeneric interface {

View File

@ -1,18 +1,17 @@
package service package service
import ( import (
"github.com/google/uuid"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"web-medols-be/app/database/entity" "go-humas-be/app/database/entity"
"web-medols-be/app/module/article_approvals/mapper" "go-humas-be/app/module/article_approvals/mapper"
"web-medols-be/app/module/article_approvals/repository" "go-humas-be/app/module/article_approvals/repository"
"web-medols-be/app/module/article_approvals/request" "go-humas-be/app/module/article_approvals/request"
"web-medols-be/app/module/article_approvals/response" "go-humas-be/app/module/article_approvals/response"
articlesService "web-medols-be/app/module/articles/service" articlesService "go-humas-be/app/module/articles/service"
usersRepository "web-medols-be/app/module/users/repository" usersRepository "go-humas-be/app/module/users/repository"
"web-medols-be/utils/paginator" "go-humas-be/utils/paginator"
utilSvc "web-medols-be/utils/service" utilSvc "go-humas-be/utils/service"
) )
// ArticleApprovalsService // ArticleApprovalsService
@ -27,7 +26,7 @@ type articleApprovalsService struct {
type ArticleApprovalsService interface { type ArticleApprovalsService interface {
All(req request.ArticleApprovalsQueryRequest) (articleApprovals []*response.ArticleApprovalsResponse, paging paginator.Pagination, err error) All(req request.ArticleApprovalsQueryRequest) (articleApprovals []*response.ArticleApprovalsResponse, paging paginator.Pagination, err error)
Show(id uint) (articleApprovals *response.ArticleApprovalsResponse, err error) Show(id uint) (articleApprovals *response.ArticleApprovalsResponse, err error)
Save(clientId *uuid.UUID, req request.ArticleApprovalsCreateRequest, authToken string) (articleApprovals *entity.ArticleApprovals, err error) Save(req request.ArticleApprovalsCreateRequest, authToken string) (articleApprovals *entity.ArticleApprovals, err error)
Update(id uint, req request.ArticleApprovalsUpdateRequest) (err error) Update(id uint, req request.ArticleApprovalsUpdateRequest) (err error)
Delete(id uint) error Delete(id uint) error
} }
@ -66,7 +65,7 @@ func (_i *articleApprovalsService) Show(id uint) (articleApprovals *response.Art
return mapper.ArticleApprovalsResponseMapper(result), nil return mapper.ArticleApprovalsResponseMapper(result), nil
} }
func (_i *articleApprovalsService) Save(clientId *uuid.UUID, req request.ArticleApprovalsCreateRequest, authToken string) (articleApprovals *entity.ArticleApprovals, err error) { func (_i *articleApprovalsService) Save(req request.ArticleApprovalsCreateRequest, authToken string) (articleApprovals *entity.ArticleApprovals, err error) {
_i.Log.Info().Interface("data", req).Msg("") _i.Log.Info().Interface("data", req).Msg("")
newReq := req.ToEntity() newReq := req.ToEntity()
@ -78,7 +77,7 @@ func (_i *articleApprovalsService) Save(clientId *uuid.UUID, req request.Article
approvalByUserLevelId := createdBy.UserLevelId approvalByUserLevelId := createdBy.UserLevelId
approvalParentLevelId := createdBy.UserLevel.ParentLevelId approvalParentLevelId := createdBy.UserLevel.ParentLevelId
err = _i.ArticlesService.UpdateApproval(clientId, newReq.ArticleId, newReq.StatusId, int(approvalByUserLevelId), *newReq.ApprovalAtLevel, *approvalParentLevelId) err = _i.ArticlesService.UpdateApproval(newReq.ArticleId, newReq.StatusId, int(approvalByUserLevelId), *newReq.ApprovalAtLevel, *approvalParentLevelId)
if err != nil { if err != nil {
return nil, err return nil, err
} }

Some files were not shown because too many files have changed in this diff Show More