Compare commits
No commits in common. "dev-1" and "master" have entirely different histories.
43
.drone.yml
43
.drone.yml
|
|
@ -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,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-medols-be:dev 103.82.242.92:8900/medols/web-medols-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/medols/web-medols-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-medols-be/build?token=autodeploymedols
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/web-medols-be.iml" filepath="$PROJECT_DIR$/.idea/web-medols-be.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="WEB_MODULE" version="4">
|
|
||||||
<component name="Go" enabled="true" />
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
package entity
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ActivityLogs struct {
|
type ActivityLogs struct {
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
package entity
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ArticleApprovals struct {
|
type ArticleApprovals struct {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
package article_category_details
|
package article_category_details
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
"time"
|
"time"
|
||||||
entity "web-medols-be/app/database/entity"
|
entity "web-medols-be/app/database/entity"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ArticleCategoryDetails struct {
|
type ArticleCategoryDetails struct {
|
||||||
|
|
@ -16,7 +15,4 @@ type ArticleCategoryDetails struct {
|
||||||
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"`
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
package entity
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ArticleComments struct {
|
type ArticleComments struct {
|
||||||
|
|
@ -19,9 +18,6 @@ type ArticleComments struct {
|
||||||
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
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
package entity
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ArticleFiles struct {
|
type ArticleFiles struct {
|
||||||
|
|
@ -27,7 +26,4 @@ type ArticleFiles struct {
|
||||||
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"`
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,8 @@
|
||||||
package entity
|
package entity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Articles struct {
|
type Articles struct {
|
||||||
|
|
@ -27,19 +26,12 @@ type Articles struct {
|
||||||
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"`
|
|
||||||
CurrentApprovalStep *int `json:"current_approval_step" gorm:"type:int4;default:0"` // 0=not submitted, 1+=approval step
|
|
||||||
// New fields for no-approval support
|
|
||||||
BypassApproval *bool `json:"bypass_approval" gorm:"type:bool;default:false"` // true = skip approval process
|
|
||||||
ApprovalExempt *bool `json:"approval_exempt" gorm:"type:bool;default:false"` // true = permanently exempt from approval
|
|
||||||
IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"`
|
IsPublish *bool `json:"is_publish" gorm:"type:bool;default:false"`
|
||||||
IsBanner *bool `json:"is_banner" gorm:"type:bool;default:false"`
|
IsBanner *bool `json:"is_banner" gorm:"type:bool;default:false"`
|
||||||
PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"`
|
PublishedAt *time.Time `json:"published_at" gorm:"type:timestamp"`
|
||||||
IsDraft *bool `json:"is_draft" gorm:"type:bool;default:false"`
|
IsDraft *bool `json:"is_draft" gorm:"type:bool;default:false"`
|
||||||
DraftedAt *time.Time `json:"drafted_at" gorm:"type:timestamp"`
|
DraftedAt *time.Time `json:"drafted_at" gorm:"type:timestamp"`
|
||||||
PublishSchedule *string `json:"publish_schedule" gorm:"type:varchar"`
|
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"`
|
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()"`
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
@ -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()"`
|
|
||||||
}
|
|
||||||
|
|
@ -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()"`
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
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"
|
||||||
"gorm.io/driver/postgres"
|
"gorm.io/driver/postgres"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/logger"
|
"gorm.io/gorm/logger"
|
||||||
|
"web-medols-be/app/database/entity"
|
||||||
|
"web-medols-be/app/database/entity/article_category_details"
|
||||||
|
"web-medols-be/app/database/entity/users"
|
||||||
|
"web-medols-be/config/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Database setup database with gorm
|
// Database setup database with gorm
|
||||||
|
|
@ -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,8 @@ func Models() []interface{} {
|
||||||
entity.ArticleComments{},
|
entity.ArticleComments{},
|
||||||
entity.ArticleNulisAI{},
|
entity.ArticleNulisAI{},
|
||||||
entity.AuditTrails{},
|
entity.AuditTrails{},
|
||||||
entity.Bookmarks{},
|
|
||||||
entity.Cities{},
|
entity.Cities{},
|
||||||
entity.Clients{},
|
entity.Clients{},
|
||||||
entity.ClientApprovalSettings{},
|
|
||||||
entity.CsrfTokenRecords{},
|
entity.CsrfTokenRecords{},
|
||||||
entity.CustomStaticPages{},
|
entity.CustomStaticPages{},
|
||||||
entity.Districts{},
|
entity.Districts{},
|
||||||
|
|
@ -116,11 +110,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{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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{},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"strings"
|
||||||
|
"web-medols-be/app/database/entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -23,7 +22,6 @@ var excludedPaths = []string{
|
||||||
"/clients",
|
"/clients",
|
||||||
"/clients/*",
|
"/clients/*",
|
||||||
"*/viewer/*",
|
"*/viewer/*",
|
||||||
"/bookmarks/test-table",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isPathExcluded checks if the given path should be excluded from client key validation
|
// isPathExcluded checks if the given path should be excluded from client key validation
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
package middleware
|
package middleware
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/csrf"
|
||||||
|
"github.com/gofiber/fiber/v2/middleware/session"
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
"web-medols-be/app/database"
|
"web-medols-be/app/database"
|
||||||
"web-medols-be/config/config"
|
"web-medols-be/config/config"
|
||||||
utilsSvc "web-medols-be/utils"
|
utilsSvc "web-medols-be/utils"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/csrf"
|
|
||||||
"github.com/gofiber/fiber/v2/middleware/session"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/gofiber/fiber/v2/middleware/compress"
|
"github.com/gofiber/fiber/v2/middleware/compress"
|
||||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||||
|
|
@ -58,8 +57,8 @@ 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://dev.mikulnews.com, https://n8n.qudoco.com, https://narasiahli.com",
|
||||||
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, X-Client-Key",
|
||||||
ExposeHeaders: "Content-Length, Content-Type",
|
ExposeHeaders: "Content-Length, Content-Type",
|
||||||
AllowCredentials: true,
|
AllowCredentials: true,
|
||||||
|
|
@ -130,7 +129,7 @@ func (m *Middleware) Register(db *database.Database) {
|
||||||
m.App.Use(ClientMiddleware(db.DB))
|
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),
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -1,12 +1,11 @@
|
||||||
package activity_logs
|
package activity_logs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"go.uber.org/fx"
|
||||||
"web-medols-be/app/module/activity_logs/controller"
|
"web-medols-be/app/module/activity_logs/controller"
|
||||||
"web-medols-be/app/module/activity_logs/repository"
|
"web-medols-be/app/module/activity_logs/repository"
|
||||||
"web-medols-be/app/module/activity_logs/service"
|
"web-medols-be/app/module/activity_logs/service"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"go.uber.org/fx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// struct of ActivityLogsRouter
|
// struct of ActivityLogsRouter
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
|
|
@ -9,9 +11,6 @@ import (
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type activityLogsController struct {
|
type activityLogsController struct {
|
||||||
|
|
@ -116,7 +115,7 @@ func (_i *activityLogsController) Show(c *fiber.Ctx) error {
|
||||||
// @Tags ActivityLogs
|
// @Tags ActivityLogs
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 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
|
||||||
|
|
@ -159,7 +158,7 @@ func (_i *activityLogsController) Save(c *fiber.Ctx) error {
|
||||||
// @Tags ActivityLogs
|
// @Tags ActivityLogs
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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.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
|
||||||
|
|
@ -197,7 +196,7 @@ func (_i *activityLogsController) Update(c *fiber.Ctx) error {
|
||||||
// @Tags ActivityLogs
|
// @Tags ActivityLogs
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "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
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
"web-medols-be/app/module/advertisement/request"
|
"web-medols-be/app/module/advertisement/request"
|
||||||
"web-medols-be/app/module/advertisement/service"
|
"web-medols-be/app/module/advertisement/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -119,7 +118,7 @@ func (_i *advertisementController) Show(c *fiber.Ctx) error {
|
||||||
// @Tags Advertisement
|
// @Tags Advertisement
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 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.AdvertisementCreateRequest true "Required payload"
|
// @Param payload body request.AdvertisementCreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -154,7 +153,7 @@ func (_i *advertisementController) Save(c *fiber.Ctx) error {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 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
|
||||||
|
|
@ -187,7 +186,7 @@ func (_i *advertisementController) Upload(c *fiber.Ctx) error {
|
||||||
// @Tags Advertisement
|
// @Tags Advertisement
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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.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
|
||||||
|
|
@ -225,7 +224,7 @@ func (_i *advertisementController) Update(c *fiber.Ctx) error {
|
||||||
// @Tags Advertisement
|
// @Tags Advertisement
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 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 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"
|
||||||
|
|
@ -264,7 +263,7 @@ func (_i *advertisementController) UpdatePublish(c *fiber.Ctx) error {
|
||||||
// @Tags Advertisement
|
// @Tags Advertisement
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -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"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -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"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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"`
|
|
||||||
}
|
|
||||||
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
"web-medols-be/app/module/article_approvals/request"
|
"web-medols-be/app/module/article_approvals/request"
|
||||||
"web-medols-be/app/module/article_approvals/service"
|
"web-medols-be/app/module/article_approvals/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -110,7 +109,7 @@ func (_i *articleApprovalsController) Show(c *fiber.Ctx) error {
|
||||||
// @Tags ArticleApprovals
|
// @Tags ArticleApprovals
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 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.ArticleApprovalsCreateRequest true "Required payload"
|
// @Param payload body request.ArticleApprovalsCreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -146,7 +145,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 +180,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
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
"web-medols-be/app/module/article_categories/request"
|
"web-medols-be/app/module/article_categories/request"
|
||||||
"web-medols-be/app/module/article_categories/service"
|
"web-medols-be/app/module/article_categories/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -179,7 +178,7 @@ func (_i *articleCategoriesController) ShowBySlug(c *fiber.Ctx) error {
|
||||||
// @Tags Article Categories
|
// @Tags Article Categories
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 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.ArticleCategoriesCreateRequest true "Required payload"
|
// @Param payload body request.ArticleCategoriesCreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -215,7 +214,7 @@ func (_i *articleCategoriesController) Save(c *fiber.Ctx) error {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 files formData file true "Upload thumbnail"
|
// @Param files formData file true "Upload thumbnail"
|
||||||
// @Param id path int true "ArticleCategories ID"
|
// @Param id path int true "ArticleCategories ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -242,7 +241,7 @@ func (_i *articleCategoriesController) SaveThumbnail(c *fiber.Ctx) error {
|
||||||
// @Tags Article Categories
|
// @Tags Article Categories
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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.ArticleCategoriesUpdateRequest true "Required payload"
|
// @Param payload body request.ArticleCategoriesUpdateRequest true "Required payload"
|
||||||
// @Param id path int true "ArticleCategories ID"
|
// @Param id path int true "ArticleCategories ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -279,7 +278,7 @@ func (_i *articleCategoriesController) Update(c *fiber.Ctx) error {
|
||||||
// @Tags Article Categories
|
// @Tags Article Categories
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "ArticleCategories ID"
|
// @Param id path int true "ArticleCategories ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/module/article_category_details/request"
|
"web-medols-be/app/module/article_category_details/request"
|
||||||
"web-medols-be/app/module/article_category_details/service"
|
"web-medols-be/app/module/article_category_details/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -96,7 +95,7 @@ func (_i *articleCategoryDetailsController) Show(c *fiber.Ctx) error {
|
||||||
// @Description API for create ArticleCategoryDetails
|
// @Description API for create ArticleCategoryDetails
|
||||||
// @Tags Untags
|
// @Tags Untags
|
||||||
// @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"
|
||||||
// @Body request.ArticleCategoryDetailsCreateRequest
|
// @Body request.ArticleCategoryDetailsCreateRequest
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 401 {object} response.Response
|
// @Failure 401 {object} response.Response
|
||||||
|
|
@ -125,7 +124,7 @@ func (_i *articleCategoryDetailsController) Save(c *fiber.Ctx) error {
|
||||||
// @Description API for update ArticleCategoryDetails
|
// @Description API for update ArticleCategoryDetails
|
||||||
// @Tags Untags
|
// @Tags Untags
|
||||||
// @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"
|
||||||
// @Body request.ArticleCategoryDetailsUpdateRequest
|
// @Body request.ArticleCategoryDetailsUpdateRequest
|
||||||
// @Param id path int true "ArticleCategoryDetails ID"
|
// @Param id path int true "ArticleCategoryDetails ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -160,7 +159,7 @@ func (_i *articleCategoryDetailsController) Update(c *fiber.Ctx) error {
|
||||||
// @Description API for delete ArticleCategoryDetails
|
// @Description API for delete ArticleCategoryDetails
|
||||||
// @Tags Untags
|
// @Tags Untags
|
||||||
// @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 "ArticleCategoryDetails ID"
|
// @Param id path int true "ArticleCategoryDetails ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 401 {object} response.Response
|
// @Failure 401 {object} response.Response
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
"web-medols-be/app/module/article_comments/request"
|
"web-medols-be/app/module/article_comments/request"
|
||||||
|
|
@ -8,9 +10,6 @@ import (
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type articleCommentsController struct {
|
type articleCommentsController struct {
|
||||||
|
|
@ -118,7 +117,7 @@ func (_i *articleCommentsController) Show(c *fiber.Ctx) error {
|
||||||
// @Tags ArticleComments
|
// @Tags ArticleComments
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 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.ArticleCommentsCreateRequest true "Required payload"
|
// @Param payload body request.ArticleCommentsCreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -154,7 +153,7 @@ func (_i *articleCommentsController) Save(c *fiber.Ctx) error {
|
||||||
// @Tags ArticleComments
|
// @Tags ArticleComments
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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.ArticleCommentsUpdateRequest true "Required payload"
|
// @Param payload body request.ArticleCommentsUpdateRequest true "Required payload"
|
||||||
// @Param id path int true "ArticleComments ID"
|
// @Param id path int true "ArticleComments ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -193,7 +192,7 @@ func (_i *articleCommentsController) Update(c *fiber.Ctx) error {
|
||||||
// @Tags ArticleComments
|
// @Tags ArticleComments
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "ArticleComments ID"
|
// @Param id path int true "ArticleComments ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -226,7 +225,7 @@ func (_i *articleCommentsController) Delete(c *fiber.Ctx) error {
|
||||||
// @Tags ArticleComments
|
// @Tags ArticleComments
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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.ArticleCommentsApprovalRequest true "Required payload"
|
// @Param payload body request.ArticleCommentsApprovalRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
"web-medols-be/app/module/article_files/request"
|
"web-medols-be/app/module/article_files/request"
|
||||||
|
|
@ -9,8 +10,6 @@ import (
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type articleFilesController struct {
|
type articleFilesController struct {
|
||||||
|
|
@ -117,7 +116,7 @@ func (_i *articleFilesController) Show(c *fiber.Ctx) error {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 files formData file true "Upload file" multiple true
|
// @Param files formData file true "Upload file" multiple true
|
||||||
// @Param articleId path int true "Article ID"
|
// @Param articleId path int true "Article ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -151,7 +150,7 @@ func (_i *articleFilesController) Save(c *fiber.Ctx) error {
|
||||||
// @Tags Article Files
|
// @Tags Article Files
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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.ArticleFilesUpdateRequest true "Required payload"
|
// @Param payload body request.ArticleFilesUpdateRequest true "Required payload"
|
||||||
// @Param id path int true "ArticleFiles ID"
|
// @Param id path int true "ArticleFiles ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -190,7 +189,7 @@ func (_i *articleFilesController) Update(c *fiber.Ctx) error {
|
||||||
// @Tags Article Files
|
// @Tags Article Files
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "ArticleFiles ID"
|
// @Param id path int true "ArticleFiles ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,17 @@ package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
"web-medols-be/app/database"
|
"web-medols-be/app/database"
|
||||||
"web-medols-be/app/database/entity"
|
"web-medols-be/app/database/entity"
|
||||||
"web-medols-be/app/module/article_files/request"
|
"web-medols-be/app/module/article_files/request"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
utilSvc "web-medols-be/utils/service"
|
utilSvc "web-medols-be/utils/service"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type articleFilesRepository struct {
|
type articleFilesRepository struct {
|
||||||
DB *database.Database
|
DB *database.Database
|
||||||
Log zerolog.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArticleFilesRepository define interface of IArticleFilesRepository
|
// ArticleFilesRepository define interface of IArticleFilesRepository
|
||||||
|
|
@ -30,10 +26,9 @@ type ArticleFilesRepository interface {
|
||||||
Delete(clientId *uuid.UUID, id uint) (err error)
|
Delete(clientId *uuid.UUID, id uint) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewArticleFilesRepository(db *database.Database, log zerolog.Logger) ArticleFilesRepository {
|
func NewArticleFilesRepository(db *database.Database) ArticleFilesRepository {
|
||||||
return &articleFilesRepository{
|
return &articleFilesRepository{
|
||||||
DB: db,
|
DB: db,
|
||||||
Log: log,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,41 +136,10 @@ func (_i *articleFilesRepository) Update(clientId *uuid.UUID, id uint, articleFi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a copy without the relation field to avoid updating non-existent columns
|
articleFilesMap, err := utilSvc.StructToMap(articleFiles)
|
||||||
updateData := &entity.ArticleFiles{
|
|
||||||
ID: articleFiles.ID,
|
|
||||||
ArticleId: articleFiles.ArticleId,
|
|
||||||
UploadID: articleFiles.UploadID,
|
|
||||||
FilePath: articleFiles.FilePath,
|
|
||||||
FileUrl: articleFiles.FileUrl,
|
|
||||||
FileName: articleFiles.FileName,
|
|
||||||
FileThumbnail: articleFiles.FileThumbnail,
|
|
||||||
FileAlt: articleFiles.FileAlt,
|
|
||||||
WidthPixel: articleFiles.WidthPixel,
|
|
||||||
HeightPixel: articleFiles.HeightPixel,
|
|
||||||
Size: articleFiles.Size,
|
|
||||||
DownloadCount: articleFiles.DownloadCount,
|
|
||||||
CreatedById: articleFiles.CreatedById,
|
|
||||||
StatusId: articleFiles.StatusId,
|
|
||||||
IsPublish: articleFiles.IsPublish,
|
|
||||||
PublishedAt: articleFiles.PublishedAt,
|
|
||||||
ClientId: articleFiles.ClientId,
|
|
||||||
IsActive: articleFiles.IsActive,
|
|
||||||
CreatedAt: articleFiles.CreatedAt,
|
|
||||||
UpdatedAt: time.Time{},
|
|
||||||
// Exclude Article relation field
|
|
||||||
}
|
|
||||||
|
|
||||||
articleFilesMap, err := utilSvc.StructToMap(updateData)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove any relation fields that might have been included
|
|
||||||
delete(articleFilesMap, "article")
|
|
||||||
|
|
||||||
_i.Log.Info().Interface("articleFilesMap : ", articleFilesMap).Msg("")
|
|
||||||
|
|
||||||
return _i.DB.DB.Model(&entity.ArticleFiles{}).
|
return _i.DB.DB.Model(&entity.ArticleFiles{}).
|
||||||
Where(&entity.ArticleFiles{ID: id}).
|
Where(&entity.ArticleFiles{ID: id}).
|
||||||
Updates(articleFilesMap).Error
|
Updates(articleFilesMap).Error
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,21 @@ import "time"
|
||||||
|
|
||||||
type ArticleFilesResponse struct {
|
type ArticleFilesResponse struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
ArticleId uint `json:"articleId"`
|
ArticleId uint `json:"article_id"`
|
||||||
FilePath *string `json:"filePath"`
|
FilePath *string `json:"file_path"`
|
||||||
FileUrl *string `json:"fileUrl"`
|
FileUrl *string `json:"file_url"`
|
||||||
FileName *string `json:"fileName"`
|
FileName *string `json:"file_name"`
|
||||||
FileThumbnail *string `json:"fileThumbnail"`
|
FileThumbnail *string `json:"file_thumbnail"`
|
||||||
FileAlt *string `json:"fileAlt"`
|
FileAlt *string `json:"file_alt"`
|
||||||
WidthPixel *string `json:"widthPixel"`
|
WidthPixel *string `json:"width_pixel"`
|
||||||
HeightPixel *string `json:"heightPixel"`
|
HeightPixel *string `json:"height_pixel"`
|
||||||
Size *string `json:"size"`
|
Size *string `json:"size"`
|
||||||
DownloadCount *int `json:"downloadCount"`
|
DownloadCount *int `json:"download_count"`
|
||||||
CreatedById int `json:"createdById"`
|
CreatedById int `json:"created_by_id"`
|
||||||
StatusId int `json:"statusId"`
|
StatusId int `json:"status_id"`
|
||||||
IsPublish *bool `json:"isPublish"`
|
IsPublish *bool `json:"is_publish"`
|
||||||
PublishedAt *time.Time `json:"publishedAt"`
|
PublishedAt *time.Time `json:"published_at"`
|
||||||
IsActive bool `json:"isActive"`
|
IsActive bool `json:"is_active"`
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,10 @@ package service
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
@ -19,12 +23,8 @@ import (
|
||||||
"web-medols-be/app/module/article_files/request"
|
"web-medols-be/app/module/article_files/request"
|
||||||
"web-medols-be/app/module/article_files/response"
|
"web-medols-be/app/module/article_files/response"
|
||||||
config "web-medols-be/config/config"
|
config "web-medols-be/config/config"
|
||||||
|
minioStorage "web-medols-be/config/config"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/minio/minio-go/v7"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ArticleFilesService
|
// ArticleFilesService
|
||||||
|
|
@ -32,7 +32,7 @@ type articleFilesService struct {
|
||||||
Repo repository.ArticleFilesRepository
|
Repo repository.ArticleFilesRepository
|
||||||
Log zerolog.Logger
|
Log zerolog.Logger
|
||||||
Cfg *config.Config
|
Cfg *config.Config
|
||||||
MinioStorage *config.MinioStorage
|
MinioStorage *minioStorage.MinioStorage
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArticleFilesService define interface of IArticleFilesService
|
// ArticleFilesService define interface of IArticleFilesService
|
||||||
|
|
@ -48,7 +48,7 @@ type ArticleFilesService interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewArticleFilesService init ArticleFilesService
|
// NewArticleFilesService init ArticleFilesService
|
||||||
func NewArticleFilesService(repo repository.ArticleFilesRepository, log zerolog.Logger, cfg *config.Config, minioStorage *config.MinioStorage) ArticleFilesService {
|
func NewArticleFilesService(repo repository.ArticleFilesRepository, log zerolog.Logger, cfg *config.Config, minioStorage *minioStorage.MinioStorage) ArticleFilesService {
|
||||||
|
|
||||||
return &articleFilesService{
|
return &articleFilesService{
|
||||||
Repo: repo,
|
Repo: repo,
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/module/article_nulis_ai/request"
|
"web-medols-be/app/module/article_nulis_ai/request"
|
||||||
"web-medols-be/app/module/article_nulis_ai/service"
|
"web-medols-be/app/module/article_nulis_ai/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type articleNulisAIController struct {
|
type articleNulisAIController struct {
|
||||||
|
|
@ -114,7 +113,7 @@ func (_i *articleNulisAIController) Show(c *fiber.Ctx) error {
|
||||||
// @Description API for create ArticleNulisAI
|
// @Description API for create ArticleNulisAI
|
||||||
// @Tags ArticleNulisAI
|
// @Tags ArticleNulisAI
|
||||||
// @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.ArticleNulisAICreateRequest true "Required payload"
|
// @Param payload body request.ArticleNulisAICreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -145,7 +144,7 @@ func (_i *articleNulisAIController) Save(c *fiber.Ctx) error {
|
||||||
// @Description API for update ArticleNulisAI
|
// @Description API for update ArticleNulisAI
|
||||||
// @Tags ArticleNulisAI
|
// @Tags ArticleNulisAI
|
||||||
// @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.ArticleNulisAIUpdateRequest true "Required payload"
|
// @Param payload body request.ArticleNulisAIUpdateRequest true "Required payload"
|
||||||
// @Param id path int true "ArticleNulisAI ID"
|
// @Param id path int true "ArticleNulisAI ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -180,7 +179,7 @@ func (_i *articleNulisAIController) Update(c *fiber.Ctx) error {
|
||||||
// @Description API for publish ArticleNulisAI
|
// @Description API for publish ArticleNulisAI
|
||||||
// @Tags ArticleNulisAI
|
// @Tags ArticleNulisAI
|
||||||
// @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.ArticleNulisAIUpdateRequest true "Required payload"
|
// @Param payload body request.ArticleNulisAIUpdateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -210,7 +209,7 @@ func (_i *articleNulisAIController) Publish(c *fiber.Ctx) error {
|
||||||
// @Description API for delete ArticleNulisAI
|
// @Description API for delete ArticleNulisAI
|
||||||
// @Tags ArticleNulisAI
|
// @Tags ArticleNulisAI
|
||||||
// @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 "ArticleNulisAI ID"
|
// @Param id path int true "ArticleNulisAI ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,17 @@
|
||||||
package articles
|
package articles
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"web-medols-be/app/middleware"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"go.uber.org/fx"
|
||||||
"web-medols-be/app/module/articles/controller"
|
"web-medols-be/app/module/articles/controller"
|
||||||
"web-medols-be/app/module/articles/repository"
|
"web-medols-be/app/module/articles/repository"
|
||||||
"web-medols-be/app/module/articles/service"
|
"web-medols-be/app/module/articles/service"
|
||||||
usersRepo "web-medols-be/app/module/users/repository"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"go.uber.org/fx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ArticlesRouter struct of ArticlesRouter
|
// ArticlesRouter struct of ArticlesRouter
|
||||||
type ArticlesRouter struct {
|
type ArticlesRouter struct {
|
||||||
App fiber.Router
|
App fiber.Router
|
||||||
Controller *controller.Controller
|
Controller *controller.Controller
|
||||||
UsersRepo usersRepo.UsersRepository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewArticlesModule register bulky of Articles module
|
// NewArticlesModule register bulky of Articles module
|
||||||
|
|
@ -34,11 +30,10 @@ var NewArticlesModule = fx.Options(
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewArticlesRouter init ArticlesRouter
|
// NewArticlesRouter init ArticlesRouter
|
||||||
func NewArticlesRouter(fiber *fiber.App, controller *controller.Controller, usersRepo usersRepo.UsersRepository) *ArticlesRouter {
|
func NewArticlesRouter(fiber *fiber.App, controller *controller.Controller) *ArticlesRouter {
|
||||||
return &ArticlesRouter{
|
return &ArticlesRouter{
|
||||||
App: fiber,
|
App: fiber,
|
||||||
Controller: controller,
|
Controller: controller,
|
||||||
UsersRepo: usersRepo,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -49,22 +44,8 @@ func (_i *ArticlesRouter) RegisterArticlesRoutes() {
|
||||||
|
|
||||||
// define routes
|
// define routes
|
||||||
_i.App.Route("/articles", func(router fiber.Router) {
|
_i.App.Route("/articles", func(router fiber.Router) {
|
||||||
// Add user middleware to extract user level from JWT token
|
|
||||||
router.Use(middleware.UserMiddleware(_i.UsersRepo))
|
|
||||||
|
|
||||||
// Dynamic approval system routes
|
|
||||||
router.Post("/:id/submit-approval", articlesController.SubmitForApproval)
|
|
||||||
router.Get("/:id/approval-status", articlesController.GetApprovalStatus)
|
|
||||||
router.Get("/pending-approval", articlesController.GetPendingApprovals)
|
|
||||||
router.Get("/waiting-for-approval", articlesController.GetArticlesWaitingForApproval)
|
|
||||||
|
|
||||||
// Publish/Unpublish routes
|
|
||||||
router.Put("/:id/publish", articlesController.Publish)
|
|
||||||
router.Put("/:id/unpublish", articlesController.Unpublish)
|
|
||||||
|
|
||||||
router.Get("/", articlesController.All)
|
router.Get("/", articlesController.All)
|
||||||
router.Get("/old-id/:id", articlesController.ShowByOldId)
|
router.Get("/old-id/:id", articlesController.ShowByOldId)
|
||||||
router.Get("/slug/:slug", articlesController.ShowBySlug)
|
|
||||||
router.Get("/:id", articlesController.Show)
|
router.Get("/:id", articlesController.Show)
|
||||||
router.Post("/", articlesController.Save)
|
router.Post("/", articlesController.Save)
|
||||||
router.Put("/:id", articlesController.Update)
|
router.Put("/:id", articlesController.Update)
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
"web-medols-be/app/module/articles/request"
|
"web-medols-be/app/module/articles/request"
|
||||||
"web-medols-be/app/module/articles/service"
|
"web-medols-be/app/module/articles/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -23,7 +22,6 @@ type ArticlesController interface {
|
||||||
All(c *fiber.Ctx) error
|
All(c *fiber.Ctx) error
|
||||||
Show(c *fiber.Ctx) error
|
Show(c *fiber.Ctx) error
|
||||||
ShowByOldId(c *fiber.Ctx) error
|
ShowByOldId(c *fiber.Ctx) error
|
||||||
ShowBySlug(c *fiber.Ctx) error
|
|
||||||
Save(c *fiber.Ctx) error
|
Save(c *fiber.Ctx) error
|
||||||
SaveThumbnail(c *fiber.Ctx) error
|
SaveThumbnail(c *fiber.Ctx) error
|
||||||
Update(c *fiber.Ctx) error
|
Update(c *fiber.Ctx) error
|
||||||
|
|
@ -34,16 +32,6 @@ type ArticlesController interface {
|
||||||
ArticlePerUserLevelStats(c *fiber.Ctx) error
|
ArticlePerUserLevelStats(c *fiber.Ctx) error
|
||||||
ArticleMonthlyStats(c *fiber.Ctx) error
|
ArticleMonthlyStats(c *fiber.Ctx) error
|
||||||
PublishScheduling(c *fiber.Ctx) error
|
PublishScheduling(c *fiber.Ctx) error
|
||||||
|
|
||||||
// Dynamic approval system methods
|
|
||||||
SubmitForApproval(c *fiber.Ctx) error
|
|
||||||
GetApprovalStatus(c *fiber.Ctx) error
|
|
||||||
GetPendingApprovals(c *fiber.Ctx) error
|
|
||||||
GetArticlesWaitingForApproval(c *fiber.Ctx) error
|
|
||||||
|
|
||||||
// Publish/Unpublish methods
|
|
||||||
Publish(c *fiber.Ctx) error
|
|
||||||
Unpublish(c *fiber.Ctx) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewArticlesController(articlesService service.ArticlesService, log zerolog.Logger) ArticlesController {
|
func NewArticlesController(articlesService service.ArticlesService, log zerolog.Logger) ArticlesController {
|
||||||
|
|
@ -59,7 +47,6 @@ func NewArticlesController(articlesService service.ArticlesService, log zerolog.
|
||||||
// @Tags Articles
|
// @Tags Articles
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
// @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 request.ArticlesQueryRequest false "query parameters"
|
// @Param req query request.ArticlesQueryRequest 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
|
||||||
|
|
@ -84,10 +71,6 @@ func (_i *articlesController) All(c *fiber.Ctx) error {
|
||||||
IsPublish: c.Query("isPublish"),
|
IsPublish: c.Query("isPublish"),
|
||||||
IsDraft: c.Query("isDraft"),
|
IsDraft: c.Query("isDraft"),
|
||||||
IsBanner: c.Query("isBanner"),
|
IsBanner: c.Query("isBanner"),
|
||||||
CustomCreatorName: c.Query("customCreatorName"),
|
|
||||||
Source: c.Query("source"),
|
|
||||||
StartDate: c.Query("startDate"),
|
|
||||||
EndDate: c.Query("endDate"),
|
|
||||||
}
|
}
|
||||||
req := reqContext.ToParamRequest()
|
req := reqContext.ToParamRequest()
|
||||||
req.Pagination = paginate
|
req.Pagination = paginate
|
||||||
|
|
@ -95,12 +78,9 @@ func (_i *articlesController) All(c *fiber.Ctx) error {
|
||||||
// Get ClientId from context
|
// Get ClientId from context
|
||||||
clientId := middleware.GetClientID(c)
|
clientId := middleware.GetClientID(c)
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
||||||
_i.Log.Info().Str("authToken", authToken).Msg("")
|
|
||||||
|
|
||||||
articlesData, paging, err := _i.articlesService.All(clientId, authToken, req)
|
articlesData, paging, err := _i.articlesService.All(clientId, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +98,6 @@ func (_i *articlesController) All(c *fiber.Ctx) error {
|
||||||
// @Description API for getting one Articles
|
// @Description API for getting one Articles
|
||||||
// @Tags Articles
|
// @Tags Articles
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param id path int true "Articles ID"
|
// @Param id path int true "Articles ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -151,7 +130,6 @@ func (_i *articlesController) Show(c *fiber.Ctx) error {
|
||||||
// @Description API for getting one Articles
|
// @Description API for getting one Articles
|
||||||
// @Tags Articles
|
// @Tags Articles
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param id path int true "Articles Old ID"
|
// @Param id path int true "Articles Old ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -179,42 +157,6 @@ func (_i *articlesController) ShowByOldId(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShowBySlug Articles
|
|
||||||
// @Summary Get one Articles by Slug
|
|
||||||
// @Description API for getting one Articles by slug
|
|
||||||
// @Tags Articles
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param slug path string true "Articles Slug"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /articles/slug/{slug} [get]
|
|
||||||
func (_i *articlesController) ShowBySlug(c *fiber.Ctx) error {
|
|
||||||
slug := c.Params("slug")
|
|
||||||
if slug == "" {
|
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
|
||||||
"error": true,
|
|
||||||
"msg": "Slug parameter is required",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
articlesData, err := _i.articlesService.ShowBySlug(clientId, slug)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Articles successfully retrieved"},
|
|
||||||
Data: articlesData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save Articles
|
// Save Articles
|
||||||
// @Summary Create Articles
|
// @Summary Create Articles
|
||||||
// @Description API for create Articles
|
// @Description API for create Articles
|
||||||
|
|
@ -240,9 +182,6 @@ func (_i *articlesController) Save(c *fiber.Ctx) error {
|
||||||
// Get ClientId from context
|
// Get ClientId from context
|
||||||
clientId := middleware.GetClientID(c)
|
clientId := middleware.GetClientID(c)
|
||||||
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
_i.Log.Info().Interface("authToken", authToken).Msg("")
|
|
||||||
|
|
||||||
dataResult, err := _i.articlesService.Save(clientId, *req, authToken)
|
dataResult, err := _i.articlesService.Save(clientId, *req, authToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -262,7 +201,7 @@ func (_i *articlesController) Save(c *fiber.Ctx) error {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 files formData file true "Upload thumbnail"
|
// @Param files formData file true "Upload thumbnail"
|
||||||
// @Param id path int true "Articles ID"
|
// @Param id path int true "Articles ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -290,8 +229,7 @@ func (_i *articlesController) SaveThumbnail(c *fiber.Ctx) error {
|
||||||
// @Description API for update Articles
|
// @Description API for update Articles
|
||||||
// @Tags Articles
|
// @Tags Articles
|
||||||
// @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.ArticlesUpdateRequest true "Required payload"
|
// @Param payload body request.ArticlesUpdateRequest true "Required payload"
|
||||||
// @Param id path int true "Articles ID"
|
// @Param id path int true "Articles ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -329,8 +267,7 @@ func (_i *articlesController) Update(c *fiber.Ctx) error {
|
||||||
// @Description API for Update Banner Articles
|
// @Description API for Update Banner Articles
|
||||||
// @Tags Articles
|
// @Tags Articles
|
||||||
// @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 "Articles ID"
|
// @Param id path int true "Articles ID"
|
||||||
// @Param isBanner query bool true "Articles Banner Status"
|
// @Param isBanner query bool true "Articles Banner Status"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -368,8 +305,7 @@ func (_i *articlesController) UpdateBanner(c *fiber.Ctx) error {
|
||||||
// @Description API for delete Articles
|
// @Description API for delete Articles
|
||||||
// @Tags Articles
|
// @Tags Articles
|
||||||
// @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 "Articles ID"
|
// @Param id path int true "Articles ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -401,7 +337,6 @@ func (_i *articlesController) Delete(c *fiber.Ctx) error {
|
||||||
// @Description API for View Thumbnail of Article
|
// @Description API for View Thumbnail of Article
|
||||||
// @Tags Articles
|
// @Tags Articles
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
|
||||||
// @Param thumbnailName path string true "Articles Thumbnail Name"
|
// @Param thumbnailName path string true "Articles Thumbnail Name"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -514,14 +449,13 @@ func (_i *articlesController) ArticleMonthlyStats(c *fiber.Ctx) error {
|
||||||
|
|
||||||
// PublishScheduling Articles
|
// PublishScheduling Articles
|
||||||
// @Summary PublishScheduling Articles
|
// @Summary PublishScheduling Articles
|
||||||
// @Description API for Publish Schedule of Article. Supports both date-only format (2006-01-02) and datetime format (2006-01-02 15:04:05)
|
// @Description API for Publish Schedule of Article
|
||||||
// @Tags Articles
|
// @Tags Articles
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string true "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 id query int false "article id"
|
// @Param id query int false "article id"
|
||||||
// @Param date query string false "publish date/time (format: 2006-01-02 or 2006-01-02 15:04:05)"
|
// @Param date query string false "publish date"
|
||||||
// @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
|
||||||
|
|
@ -547,251 +481,3 @@ func (_i *articlesController) PublishScheduling(c *fiber.Ctx) error {
|
||||||
Messages: utilRes.Messages{"Publish Scheduling of Articles successfully saved"},
|
Messages: utilRes.Messages{"Publish Scheduling of Articles successfully saved"},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitForApproval Articles
|
|
||||||
// @Summary Submit Article for Approval
|
|
||||||
// @Description API for submitting article for approval workflow
|
|
||||||
// @Tags Articles
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @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 id path int true "article id"
|
|
||||||
// @Param req body request.SubmitForApprovalRequest false "approval request data"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /articles/{id}/submit-approval [post]
|
|
||||||
func (_i *articlesController) SubmitForApproval(c *fiber.Ctx) error {
|
|
||||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var req request.SubmitForApprovalRequest
|
|
||||||
if err := c.BodyParser(&req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
|
|
||||||
err = _i.articlesService.SubmitForApproval(clientId, uint(id), authToken, req.WorkflowId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Article successfully submitted for approval"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetApprovalStatus Articles
|
|
||||||
// @Summary Get Article Approval Status
|
|
||||||
// @Description API for getting article approval status and workflow progress
|
|
||||||
// @Tags Articles
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string false "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 "article id"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /articles/{id}/approval-status [get]
|
|
||||||
func (_i *articlesController) GetApprovalStatus(c *fiber.Ctx) error {
|
|
||||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
response, err := _i.articlesService.GetApprovalStatus(clientId, uint(id))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Article approval status successfully retrieved"},
|
|
||||||
Data: response,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPendingApprovals Articles
|
|
||||||
// @Summary Get Pending Approvals
|
|
||||||
// @Description API for getting articles pending approval for current user level
|
|
||||||
// @Tags Articles
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
|
||||||
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
|
||||||
// @Param page query int false "page number"
|
|
||||||
// @Param limit query int false "items per page"
|
|
||||||
// @Param typeId query int false "article type id"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /articles/pending-approval [get]
|
|
||||||
func (_i *articlesController) GetPendingApprovals(c *fiber.Ctx) error {
|
|
||||||
page, err := strconv.Atoi(c.Query("page", "1"))
|
|
||||||
if err != nil {
|
|
||||||
page = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
limit, err := strconv.Atoi(c.Query("limit", "10"))
|
|
||||||
if err != nil {
|
|
||||||
limit = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse typeId filter
|
|
||||||
var typeId *int
|
|
||||||
if typeIdStr := c.Query("typeId"); typeIdStr != "" {
|
|
||||||
if parsedTypeId, err := strconv.Atoi(typeIdStr); err == nil {
|
|
||||||
typeId = &parsedTypeId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
|
|
||||||
response, paging, err := _i.articlesService.GetPendingApprovals(clientId, authToken, page, limit, typeId) // Updated with typeId filter
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Pending approvals successfully retrieved"},
|
|
||||||
Data: response,
|
|
||||||
Meta: paging,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetArticlesWaitingForApproval
|
|
||||||
// @Summary Get articles waiting for approval by current user level
|
|
||||||
// @Description API for getting articles that are waiting for approval by the current user's level
|
|
||||||
// @Tags Articles
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Client Key"
|
|
||||||
// @Param Authorization header string false "Insert your access token" default(Bearer <Add access token here>)
|
|
||||||
// @Param page query int false "Page number" default(1)
|
|
||||||
// @Param limit query int false "Items per page" default(10)
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /articles/waiting-for-approval [get]
|
|
||||||
func (_i *articlesController) GetArticlesWaitingForApproval(c *fiber.Ctx) error {
|
|
||||||
page, err := strconv.Atoi(c.Query("page", "1"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
limit, err := strconv.Atoi(c.Query("limit", "10"))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
|
|
||||||
responses, paging, err := _i.articlesService.GetArticlesWaitingForApproval(clientId, authToken, page, limit)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Articles waiting for approval retrieved successfully"},
|
|
||||||
Data: responses,
|
|
||||||
Meta: paging,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish Articles
|
|
||||||
// @Summary Publish Article
|
|
||||||
// @Description API for publishing an article
|
|
||||||
// @Tags Articles
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @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 id path int true "article id"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /articles/{id}/publish [put]
|
|
||||||
func (_i *articlesController) Publish(c *fiber.Ctx) error {
|
|
||||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
|
|
||||||
err = _i.articlesService.Publish(clientId, uint(id), authToken)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Article successfully published"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unpublish Articles
|
|
||||||
// @Summary Unpublish Article
|
|
||||||
// @Description API for unpublishing an article
|
|
||||||
// @Tags Articles
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @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 id path int true "article id"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /articles/{id}/unpublish [put]
|
|
||||||
func (_i *articlesController) Unpublish(c *fiber.Ctx) error {
|
|
||||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
|
|
||||||
err = _i.articlesService.Unpublish(clientId, uint(id), authToken)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Article successfully unpublished"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package mapper
|
package mapper
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"web-medols-be/app/database/entity"
|
"web-medols-be/app/database/entity"
|
||||||
articleCategoriesMapper "web-medols-be/app/module/article_categories/mapper"
|
articleCategoriesMapper "web-medols-be/app/module/article_categories/mapper"
|
||||||
articleCategoriesRepository "web-medols-be/app/module/article_categories/repository"
|
articleCategoriesRepository "web-medols-be/app/module/article_categories/repository"
|
||||||
|
|
@ -11,9 +13,6 @@ import (
|
||||||
articleFilesResponse "web-medols-be/app/module/article_files/response"
|
articleFilesResponse "web-medols-be/app/module/article_files/response"
|
||||||
res "web-medols-be/app/module/articles/response"
|
res "web-medols-be/app/module/articles/response"
|
||||||
usersRepository "web-medols-be/app/module/users/repository"
|
usersRepository "web-medols-be/app/module/users/repository"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func ArticlesResponseMapper(
|
func ArticlesResponseMapper(
|
||||||
|
|
@ -53,23 +52,6 @@ func ArticlesResponseMapper(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate PublishStatus based on conditions
|
|
||||||
publishStatus := "Cancel" // Default status
|
|
||||||
|
|
||||||
if articlesReq.IsPublish != nil && *articlesReq.IsPublish {
|
|
||||||
// Published: isPublish = true
|
|
||||||
publishStatus = "Published"
|
|
||||||
} else if articlesReq.PublishSchedule != nil && *articlesReq.PublishSchedule != "" {
|
|
||||||
// On Schedule: has publishSchedule and isPublish = false
|
|
||||||
if articlesReq.IsPublish == nil || !*articlesReq.IsPublish {
|
|
||||||
publishStatus = "On Schedule"
|
|
||||||
}
|
|
||||||
} else if articlesReq.IsPublish != nil && !*articlesReq.IsPublish &&
|
|
||||||
articlesReq.IsDraft != nil && !*articlesReq.IsDraft {
|
|
||||||
// Cancel: isPublish = false and isDraft = false
|
|
||||||
publishStatus = "Cancel"
|
|
||||||
}
|
|
||||||
|
|
||||||
if articlesReq != nil {
|
if articlesReq != nil {
|
||||||
articlesRes = &res.ArticlesResponse{
|
articlesRes = &res.ArticlesResponse{
|
||||||
ID: articlesReq.ID,
|
ID: articlesReq.ID,
|
||||||
|
|
@ -90,16 +72,10 @@ func ArticlesResponseMapper(
|
||||||
CommentCount: articlesReq.CommentCount,
|
CommentCount: articlesReq.CommentCount,
|
||||||
OldId: articlesReq.OldId,
|
OldId: articlesReq.OldId,
|
||||||
StatusId: articlesReq.StatusId,
|
StatusId: articlesReq.StatusId,
|
||||||
IsDraft: articlesReq.IsDraft,
|
|
||||||
DraftedAt: articlesReq.DraftedAt,
|
|
||||||
IsBanner: articlesReq.IsBanner,
|
IsBanner: articlesReq.IsBanner,
|
||||||
IsPublish: articlesReq.IsPublish,
|
IsPublish: articlesReq.IsPublish,
|
||||||
PublishSchedule: articlesReq.PublishSchedule,
|
|
||||||
PublishedAt: articlesReq.PublishedAt,
|
PublishedAt: articlesReq.PublishedAt,
|
||||||
PublishStatus: publishStatus,
|
|
||||||
IsActive: articlesReq.IsActive,
|
IsActive: articlesReq.IsActive,
|
||||||
Source: articlesReq.Source,
|
|
||||||
CustomCreatorName: articlesReq.CustomCreatorName,
|
|
||||||
CreatedAt: articlesReq.CreatedAt,
|
CreatedAt: articlesReq.CreatedAt,
|
||||||
UpdatedAt: articlesReq.UpdatedAt,
|
UpdatedAt: articlesReq.UpdatedAt,
|
||||||
ArticleFiles: articleFilesArr,
|
ArticleFiles: articleFilesArr,
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ package repository
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"web-medols-be/app/database"
|
"web-medols-be/app/database"
|
||||||
|
|
@ -10,9 +12,6 @@ import (
|
||||||
"web-medols-be/app/module/articles/response"
|
"web-medols-be/app/module/articles/response"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
utilSvc "web-medols-be/utils/service"
|
utilSvc "web-medols-be/utils/service"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type articlesRepository struct {
|
type articlesRepository struct {
|
||||||
|
|
@ -22,12 +21,11 @@ type articlesRepository struct {
|
||||||
|
|
||||||
// ArticlesRepository define interface of IArticlesRepository
|
// ArticlesRepository define interface of IArticlesRepository
|
||||||
type ArticlesRepository interface {
|
type ArticlesRepository interface {
|
||||||
GetAll(clientId *uuid.UUID, userLevelId *uint, req request.ArticlesQueryRequest) (articless []*entity.Articles, paging paginator.Pagination, err error)
|
GetAll(clientId *uuid.UUID, req request.ArticlesQueryRequest) (articless []*entity.Articles, paging paginator.Pagination, err error)
|
||||||
GetAllPublishSchedule(clientId *uuid.UUID) (articless []*entity.Articles, err error)
|
GetAllPublishSchedule(clientId *uuid.UUID) (articless []*entity.Articles, err error)
|
||||||
FindOne(clientId *uuid.UUID, id uint) (articles *entity.Articles, err error)
|
FindOne(clientId *uuid.UUID, id uint) (articles *entity.Articles, err error)
|
||||||
FindByFilename(clientId *uuid.UUID, thumbnailName string) (articleReturn *entity.Articles, err error)
|
FindByFilename(clientId *uuid.UUID, thumbnailName string) (articleReturn *entity.Articles, err error)
|
||||||
FindByOldId(clientId *uuid.UUID, oldId uint) (articles *entity.Articles, err error)
|
FindByOldId(clientId *uuid.UUID, oldId uint) (articles *entity.Articles, err error)
|
||||||
FindBySlug(clientId *uuid.UUID, slug string) (articles *entity.Articles, err error)
|
|
||||||
Create(clientId *uuid.UUID, articles *entity.Articles) (articleReturn *entity.Articles, err error)
|
Create(clientId *uuid.UUID, articles *entity.Articles) (articleReturn *entity.Articles, err error)
|
||||||
Update(clientId *uuid.UUID, id uint, articles *entity.Articles) (err error)
|
Update(clientId *uuid.UUID, id uint, articles *entity.Articles) (err error)
|
||||||
UpdateSkipNull(clientId *uuid.UUID, id uint, articles *entity.Articles) (err error)
|
UpdateSkipNull(clientId *uuid.UUID, id uint, articles *entity.Articles) (err error)
|
||||||
|
|
@ -45,59 +43,14 @@ func NewArticlesRepository(db *database.Database, log zerolog.Logger) ArticlesRe
|
||||||
}
|
}
|
||||||
|
|
||||||
// implement interface of IArticlesRepository
|
// implement interface of IArticlesRepository
|
||||||
func (_i *articlesRepository) GetAll(clientId *uuid.UUID, userLevelId *uint, req request.ArticlesQueryRequest) (articless []*entity.Articles, paging paginator.Pagination, err error) {
|
func (_i *articlesRepository) GetAll(clientId *uuid.UUID, req request.ArticlesQueryRequest) (articless []*entity.Articles, paging paginator.Pagination, err error) {
|
||||||
var count int64
|
var count int64
|
||||||
|
|
||||||
query := _i.DB.DB.Model(&entity.Articles{})
|
query := _i.DB.DB.Model(&entity.Articles{})
|
||||||
|
|
||||||
_i.Log.Info().Interface("userLevelId", userLevelId).Msg("")
|
// Add client filter
|
||||||
// Add approval workflow filtering based on user level
|
if clientId != nil {
|
||||||
if userLevelId != nil {
|
query = query.Where("client_id = ?", clientId)
|
||||||
// Strict filtering logic for article visibility based on approval workflow
|
|
||||||
query = query.Where(`
|
|
||||||
(
|
|
||||||
-- Articles that don't require approval (bypass or exempt)
|
|
||||||
(bypass_approval = true OR approval_exempt = true)
|
|
||||||
OR
|
|
||||||
-- Articles that are published AND approved through workflow
|
|
||||||
(is_publish = true AND status_id = 2)
|
|
||||||
OR
|
|
||||||
-- Articles created by users at HIGHER hierarchy only (not same or lower)
|
|
||||||
EXISTS (
|
|
||||||
SELECT 1 FROM users u
|
|
||||||
JOIN user_levels ul ON u.user_level_id = ul.id
|
|
||||||
WHERE u.id = articles.created_by_id
|
|
||||||
AND ul.level_number < (
|
|
||||||
SELECT ul2.level_number FROM user_levels ul2 WHERE ul2.id = ?
|
|
||||||
)
|
|
||||||
)
|
|
||||||
OR
|
|
||||||
-- Articles where this user level is the CURRENT approver in the workflow
|
|
||||||
(
|
|
||||||
workflow_id IS NOT NULL
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1 FROM article_approval_flows aaf
|
|
||||||
JOIN approval_workflow_steps aws ON aaf.workflow_id = aws.workflow_id
|
|
||||||
WHERE aaf.article_id = articles.id
|
|
||||||
AND aaf.status_id = 1 -- Only in progress
|
|
||||||
AND aws.required_user_level_id = ?
|
|
||||||
AND aws.step_order = aaf.current_step -- Must be current step
|
|
||||||
)
|
|
||||||
)
|
|
||||||
OR
|
|
||||||
-- Articles that have been approved by this user level
|
|
||||||
(
|
|
||||||
workflow_id IS NOT NULL
|
|
||||||
AND EXISTS (
|
|
||||||
SELECT 1 FROM article_approval_flows aaf
|
|
||||||
JOIN article_approval_step_logs aasl ON aaf.id = aasl.approval_flow_id
|
|
||||||
WHERE aaf.article_id = articles.id
|
|
||||||
AND aasl.user_level_id = ?
|
|
||||||
AND aasl.action = 'approve'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
`, *userLevelId, *userLevelId, *userLevelId)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if req.CategoryId != nil {
|
if req.CategoryId != nil {
|
||||||
|
|
@ -106,11 +59,6 @@ func (_i *articlesRepository) GetAll(clientId *uuid.UUID, userLevelId *uint, req
|
||||||
}
|
}
|
||||||
query = query.Where("articles.is_active = ?", true)
|
query = query.Where("articles.is_active = ?", true)
|
||||||
|
|
||||||
// Add client filter
|
|
||||||
if clientId != nil {
|
|
||||||
query = query.Where("articles.client_id = ?", clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Title != nil && *req.Title != "" {
|
if req.Title != nil && *req.Title != "" {
|
||||||
title := strings.ToLower(*req.Title)
|
title := strings.ToLower(*req.Title)
|
||||||
query = query.Where("LOWER(articles.title) LIKE ?", "%"+strings.ToLower(title)+"%")
|
query = query.Where("LOWER(articles.title) LIKE ?", "%"+strings.ToLower(title)+"%")
|
||||||
|
|
@ -141,25 +89,8 @@ func (_i *articlesRepository) GetAll(clientId *uuid.UUID, userLevelId *uint, req
|
||||||
if req.CreatedById != nil {
|
if req.CreatedById != nil {
|
||||||
query = query.Where("articles.created_by_id = ?", req.CreatedById)
|
query = query.Where("articles.created_by_id = ?", req.CreatedById)
|
||||||
}
|
}
|
||||||
if req.Source != nil && *req.Source != "" {
|
|
||||||
source := strings.ToLower(*req.Source)
|
|
||||||
query = query.Where("LOWER(articles.source) = ?", strings.ToLower(source))
|
|
||||||
}
|
|
||||||
if req.CustomCreatorName != nil && *req.CustomCreatorName != "" {
|
|
||||||
customCreatorName := strings.ToLower(*req.CustomCreatorName)
|
|
||||||
query = query.Where("LOWER(articles.custom_creator_name) LIKE ?", "%"+strings.ToLower(customCreatorName)+"%")
|
|
||||||
}
|
|
||||||
if req.StartDate != nil {
|
|
||||||
query = query.Where("DATE(articles.created_at) >= ?", req.StartDate.Format("2006-01-02"))
|
|
||||||
}
|
|
||||||
if req.EndDate != nil {
|
|
||||||
query = query.Where("DATE(articles.created_at) <= ?", req.EndDate.Format("2006-01-02"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count total records
|
|
||||||
query.Count(&count)
|
query.Count(&count)
|
||||||
|
|
||||||
// Apply sorting
|
|
||||||
if req.Pagination.SortBy != "" {
|
if req.Pagination.SortBy != "" {
|
||||||
direction := "ASC"
|
direction := "ASC"
|
||||||
if req.Pagination.Sort == "desc" {
|
if req.Pagination.Sort == "desc" {
|
||||||
|
|
@ -172,35 +103,21 @@ func (_i *articlesRepository) GetAll(clientId *uuid.UUID, userLevelId *uint, req
|
||||||
query.Order(fmt.Sprintf("%s %s", sortBy, direction))
|
query.Order(fmt.Sprintf("%s %s", sortBy, direction))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply pagination (manual calculation for better performance)
|
req.Pagination.Count = count
|
||||||
page := req.Pagination.Page
|
req.Pagination = paginator.Paging(req.Pagination)
|
||||||
limit := req.Pagination.Limit
|
|
||||||
if page <= 0 {
|
|
||||||
page = 1
|
|
||||||
}
|
|
||||||
if limit <= 0 {
|
|
||||||
limit = 10
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := (page - 1) * limit
|
err = query.Offset(req.Pagination.Offset).Limit(req.Pagination.Limit).Find(&articless).Error
|
||||||
err = query.Offset(offset).Limit(limit).Find(&articless).Error
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create pagination response
|
paging = *req.Pagination
|
||||||
paging = paginator.Pagination{
|
|
||||||
Page: page,
|
|
||||||
Limit: limit,
|
|
||||||
Count: count,
|
|
||||||
TotalPage: int((count + int64(limit) - 1) / int64(limit)),
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_i *articlesRepository) GetAllPublishSchedule(clientId *uuid.UUID) (articles []*entity.Articles, err error) {
|
func (_i *articlesRepository) GetAllPublishSchedule(clientId *uuid.UUID) (articles []*entity.Articles, err error) {
|
||||||
query := _i.DB.DB.Where("publish_schedule IS NOT NULL and is_publish = false")
|
query := _i.DB.DB.Where("publish_schedule IS NOT NULL")
|
||||||
if clientId != nil {
|
if clientId != nil {
|
||||||
query = query.Where("client_id = ?", clientId)
|
query = query.Where("client_id = ?", clientId)
|
||||||
}
|
}
|
||||||
|
|
@ -247,18 +164,6 @@ func (_i *articlesRepository) FindByOldId(clientId *uuid.UUID, oldId uint) (arti
|
||||||
return articles, nil
|
return articles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_i *articlesRepository) FindBySlug(clientId *uuid.UUID, slug string) (articles *entity.Articles, err error) {
|
|
||||||
query := _i.DB.DB.Where("slug = ?", slug)
|
|
||||||
if clientId != nil {
|
|
||||||
query = query.Where("client_id = ?", clientId)
|
|
||||||
}
|
|
||||||
if err := query.First(&articles).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return articles, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *articlesRepository) Create(clientId *uuid.UUID, articles *entity.Articles) (articleReturn *entity.Articles, err error) {
|
func (_i *articlesRepository) Create(clientId *uuid.UUID, articles *entity.Articles) (articleReturn *entity.Articles, err error) {
|
||||||
// Set client ID
|
// Set client ID
|
||||||
if clientId != nil {
|
if clientId != nil {
|
||||||
|
|
@ -285,12 +190,6 @@ func (_i *articlesRepository) Update(clientId *uuid.UUID, id uint, articles *ent
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove fields that could cause foreign key constraint violations
|
|
||||||
// delete(articlesMap, "workflow_id")
|
|
||||||
// delete(articlesMap, "id")
|
|
||||||
// delete(articlesMap, "created_at")
|
|
||||||
|
|
||||||
return _i.DB.DB.Model(&entity.Articles{}).
|
return _i.DB.DB.Model(&entity.Articles{}).
|
||||||
Where(&entity.Articles{ID: id}).
|
Where(&entity.Articles{ID: id}).
|
||||||
Updates(articlesMap).Error
|
Updates(articlesMap).Error
|
||||||
|
|
@ -308,16 +207,9 @@ func (_i *articlesRepository) UpdateSkipNull(clientId *uuid.UUID, id uint, artic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a copy to avoid modifying the original struct
|
|
||||||
updateData := *articles
|
|
||||||
// Clear fields that could cause foreign key constraint violations
|
|
||||||
updateData.WorkflowId = nil
|
|
||||||
updateData.ID = 0
|
|
||||||
updateData.UpdatedAt = time.Time{}
|
|
||||||
|
|
||||||
return _i.DB.DB.Model(&entity.Articles{}).
|
return _i.DB.DB.Model(&entity.Articles{}).
|
||||||
Where(&entity.Articles{ID: id}).
|
Where(&entity.Articles{ID: id}).
|
||||||
Updates(&updateData).Error
|
Updates(articles).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_i *articlesRepository) Delete(clientId *uuid.UUID, id uint) error {
|
func (_i *articlesRepository) Delete(clientId *uuid.UUID, id uint) error {
|
||||||
|
|
@ -332,9 +224,7 @@ func (_i *articlesRepository) Delete(clientId *uuid.UUID, id uint) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use soft delete by setting is_active to false
|
return _i.DB.DB.Delete(&entity.Articles{}, id).Error
|
||||||
isActive := false
|
|
||||||
return _i.DB.DB.Model(&entity.Articles{}).Where("id = ?", id).Update("is_active", isActive).Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_i *articlesRepository) SummaryStats(clientId *uuid.UUID, userID uint) (articleSummaryStats *response.ArticleSummaryStats, err error) {
|
func (_i *articlesRepository) SummaryStats(clientId *uuid.UUID, userID uint) (articleSummaryStats *response.ArticleSummaryStats, err error) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package request
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
"web-medols-be/app/database/entity"
|
"web-medols-be/app/database/entity"
|
||||||
|
|
@ -24,10 +23,6 @@ type ArticlesQueryRequest struct {
|
||||||
IsBanner *bool `json:"isBanner"`
|
IsBanner *bool `json:"isBanner"`
|
||||||
IsPublish *bool `json:"isPublish"`
|
IsPublish *bool `json:"isPublish"`
|
||||||
IsDraft *bool `json:"isDraft"`
|
IsDraft *bool `json:"isDraft"`
|
||||||
Source *string `json:"source"`
|
|
||||||
CustomCreatorName *string `json:"customCreatorName"`
|
|
||||||
StartDate *time.Time `json:"startDate"`
|
|
||||||
EndDate *time.Time `json:"endDate"`
|
|
||||||
Pagination *paginator.Pagination `json:"pagination"`
|
Pagination *paginator.Pagination `json:"pagination"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,8 +40,6 @@ type ArticlesCreateRequest struct {
|
||||||
IsPublish *bool `json:"isPublish"`
|
IsPublish *bool `json:"isPublish"`
|
||||||
IsDraft *bool `json:"isDraft"`
|
IsDraft *bool `json:"isDraft"`
|
||||||
OldId *uint `json:"oldId"`
|
OldId *uint `json:"oldId"`
|
||||||
Source *string `json:"source"`
|
|
||||||
CustomCreatorName *string `json:"customCreatorName"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req ArticlesCreateRequest) ToEntity() *entity.Articles {
|
func (req ArticlesCreateRequest) ToEntity() *entity.Articles {
|
||||||
|
|
@ -61,8 +54,6 @@ func (req ArticlesCreateRequest) ToEntity() *entity.Articles {
|
||||||
IsPublish: req.IsPublish,
|
IsPublish: req.IsPublish,
|
||||||
IsDraft: req.IsDraft,
|
IsDraft: req.IsDraft,
|
||||||
OldId: req.OldId,
|
OldId: req.OldId,
|
||||||
Source: req.Source,
|
|
||||||
CustomCreatorName: req.CustomCreatorName,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,8 +71,6 @@ type ArticlesUpdateRequest struct {
|
||||||
IsPublish *bool `json:"isPublish"`
|
IsPublish *bool `json:"isPublish"`
|
||||||
IsDraft *bool `json:"isDraft"`
|
IsDraft *bool `json:"isDraft"`
|
||||||
StatusId *int `json:"statusId"`
|
StatusId *int `json:"statusId"`
|
||||||
Source *string `json:"source"`
|
|
||||||
CustomCreatorName *string `json:"customCreatorName"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req ArticlesUpdateRequest) ToEntity() *entity.Articles {
|
func (req ArticlesUpdateRequest) ToEntity() *entity.Articles {
|
||||||
|
|
@ -97,8 +86,6 @@ func (req ArticlesUpdateRequest) ToEntity() *entity.Articles {
|
||||||
AiArticleId: req.AiArticleId,
|
AiArticleId: req.AiArticleId,
|
||||||
IsPublish: req.IsPublish,
|
IsPublish: req.IsPublish,
|
||||||
IsDraft: req.IsDraft,
|
IsDraft: req.IsDraft,
|
||||||
Source: req.Source,
|
|
||||||
CustomCreatorName: req.CustomCreatorName,
|
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -114,8 +101,6 @@ func (req ArticlesUpdateRequest) ToEntity() *entity.Articles {
|
||||||
AiArticleId: req.AiArticleId,
|
AiArticleId: req.AiArticleId,
|
||||||
IsPublish: req.IsPublish,
|
IsPublish: req.IsPublish,
|
||||||
IsDraft: req.IsDraft,
|
IsDraft: req.IsDraft,
|
||||||
Source: req.Source,
|
|
||||||
CustomCreatorName: req.CustomCreatorName,
|
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -133,10 +118,6 @@ type ArticlesQueryRequestContext struct {
|
||||||
IsPublish string `json:"isPublish"`
|
IsPublish string `json:"isPublish"`
|
||||||
IsDraft string `json:"isDraft"`
|
IsDraft string `json:"isDraft"`
|
||||||
StatusId string `json:"statusId"`
|
StatusId string `json:"statusId"`
|
||||||
Source string `json:"source"`
|
|
||||||
CustomCreatorName string `json:"customCreatorName"`
|
|
||||||
StartDate string `json:"startDate"`
|
|
||||||
EndDate string `json:"endDate"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (req ArticlesQueryRequestContext) ToParamRequest() ArticlesQueryRequest {
|
func (req ArticlesQueryRequestContext) ToParamRequest() ArticlesQueryRequest {
|
||||||
|
|
@ -197,39 +178,6 @@ func (req ArticlesQueryRequestContext) ToParamRequest() ArticlesQueryRequest {
|
||||||
request.CreatedById = &createdById
|
request.CreatedById = &createdById
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if source := req.Source; source != "" {
|
|
||||||
request.Source = &source
|
|
||||||
}
|
|
||||||
if customCreatorName := req.CustomCreatorName; customCreatorName != "" {
|
|
||||||
request.CustomCreatorName = &customCreatorName
|
|
||||||
}
|
|
||||||
if startDateStr := req.StartDate; startDateStr != "" {
|
|
||||||
if startDate, err := time.Parse("2006-01-02", startDateStr); err == nil {
|
|
||||||
request.StartDate = &startDate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if endDateStr := req.EndDate; endDateStr != "" {
|
|
||||||
if endDate, err := time.Parse("2006-01-02", endDateStr); err == nil {
|
|
||||||
request.EndDate = &endDate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitForApprovalRequest represents the request for submitting an article for approval
|
|
||||||
type SubmitForApprovalRequest struct {
|
|
||||||
WorkflowId *uint `json:"workflow_id" validate:"omitempty,min=1"`
|
|
||||||
Message *string `json:"message" validate:"omitempty,max=500"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates the SubmitForApprovalRequest
|
|
||||||
func (req *SubmitForApprovalRequest) Validate() error {
|
|
||||||
if req.WorkflowId != nil && *req.WorkflowId == 0 {
|
|
||||||
return errors.New("workflow_id must be greater than 0")
|
|
||||||
}
|
|
||||||
if req.Message != nil && len(*req.Message) > 500 {
|
|
||||||
return errors.New("message must be less than 500 characters")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -26,16 +26,10 @@ type ArticlesResponse struct {
|
||||||
AiArticleId *int `json:"aiArticleId"`
|
AiArticleId *int `json:"aiArticleId"`
|
||||||
OldId *uint `json:"oldId"`
|
OldId *uint `json:"oldId"`
|
||||||
StatusId *int `json:"statusId"`
|
StatusId *int `json:"statusId"`
|
||||||
IsDraft *bool `json:"isDraft"`
|
|
||||||
DraftedAt *time.Time `json:"draftedAt"`
|
|
||||||
IsBanner *bool `json:"isBanner"`
|
IsBanner *bool `json:"isBanner"`
|
||||||
IsPublish *bool `json:"isPublish"`
|
IsPublish *bool `json:"isPublish"`
|
||||||
PublishedAt *time.Time `json:"publishedAt"`
|
PublishedAt *time.Time `json:"publishedAt"`
|
||||||
PublishSchedule *string `json:"publishSchedule"`
|
|
||||||
PublishStatus string `json:"publishStatus"` // "On Schedule", "Published", "Cancel"
|
|
||||||
IsActive *bool `json:"isActive"`
|
IsActive *bool `json:"isActive"`
|
||||||
Source *string `json:"source"`
|
|
||||||
CustomCreatorName *string `json:"customCreatorName"`
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
CreatedAt time.Time `json:"createdAt"`
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
UpdatedAt time.Time `json:"updatedAt"`
|
||||||
|
|
||||||
|
|
@ -65,54 +59,3 @@ type ArticleMonthlyStats struct {
|
||||||
Comment []int `json:"comment"`
|
Comment []int `json:"comment"`
|
||||||
Share []int `json:"share"`
|
Share []int `json:"share"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArticleApprovalStatusResponse represents the approval status of an article
|
|
||||||
type ArticleApprovalStatusResponse struct {
|
|
||||||
ArticleId uint `json:"articleId"`
|
|
||||||
WorkflowId *uint `json:"workflowId"`
|
|
||||||
WorkflowName *string `json:"workflowName"`
|
|
||||||
CurrentStep int `json:"currentStep"`
|
|
||||||
TotalSteps int `json:"totalSteps"`
|
|
||||||
Status string `json:"status"` // "pending", "in_progress", "approved", "rejected", "revision_requested"
|
|
||||||
CurrentApprover *string `json:"currentApprover"`
|
|
||||||
SubmittedAt *time.Time `json:"submittedAt"`
|
|
||||||
LastActionAt *time.Time `json:"lastActionAt"`
|
|
||||||
Progress float64 `json:"progress"` // percentage complete
|
|
||||||
CanApprove bool `json:"canApprove"` // whether current user can approve
|
|
||||||
NextStep *string `json:"nextStep"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ArticleApprovalQueueResponse represents an article in the approval queue
|
|
||||||
type ArticleApprovalQueueResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Slug string `json:"slug"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
CategoryName string `json:"categoryName"`
|
|
||||||
AuthorName string `json:"authorName"`
|
|
||||||
SubmittedAt time.Time `json:"submittedAt"`
|
|
||||||
CurrentStep int `json:"currentStep"`
|
|
||||||
TotalSteps int `json:"totalSteps"`
|
|
||||||
Priority string `json:"priority"` // "low", "medium", "high", "urgent"
|
|
||||||
DaysInQueue int `json:"daysInQueue"`
|
|
||||||
WorkflowName string `json:"workflowName"`
|
|
||||||
CanApprove bool `json:"canApprove"`
|
|
||||||
EstimatedTime string `json:"estimatedTime"` // estimated time to complete approval
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientApprovalSettingsResponse represents client-level approval settings
|
|
||||||
type ClientApprovalSettingsResponse struct {
|
|
||||||
ClientId string `json:"clientId"`
|
|
||||||
RequiresApproval bool `json:"requiresApproval"`
|
|
||||||
DefaultWorkflowId *uint `json:"defaultWorkflowId"`
|
|
||||||
DefaultWorkflowName *string `json:"defaultWorkflowName"`
|
|
||||||
AutoPublishArticles bool `json:"autoPublishArticles"`
|
|
||||||
ApprovalExemptUsers []uint `json:"approvalExemptUsers"`
|
|
||||||
ApprovalExemptRoles []uint `json:"approvalExemptRoles"`
|
|
||||||
ApprovalExemptCategories []uint `json:"approvalExemptCategories"`
|
|
||||||
RequireApprovalFor []string `json:"requireApprovalFor"`
|
|
||||||
SkipApprovalFor []string `json:"skipApprovalFor"`
|
|
||||||
IsActive bool `json:"isActive"`
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
"github.com/minio/minio-go/v7"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
@ -13,8 +17,6 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
"web-medols-be/app/database/entity"
|
"web-medols-be/app/database/entity"
|
||||||
approvalWorkflowsRepository "web-medols-be/app/module/approval_workflows/repository"
|
|
||||||
articleApprovalFlowsRepository "web-medols-be/app/module/article_approval_flows/repository"
|
|
||||||
articleApprovalsRepository "web-medols-be/app/module/article_approvals/repository"
|
articleApprovalsRepository "web-medols-be/app/module/article_approvals/repository"
|
||||||
articleCategoriesRepository "web-medols-be/app/module/article_categories/repository"
|
articleCategoriesRepository "web-medols-be/app/module/article_categories/repository"
|
||||||
articleCategoryDetailsRepository "web-medols-be/app/module/article_category_details/repository"
|
articleCategoryDetailsRepository "web-medols-be/app/module/article_category_details/repository"
|
||||||
|
|
@ -29,11 +31,6 @@ import (
|
||||||
minioStorage "web-medols-be/config/config"
|
minioStorage "web-medols-be/config/config"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
utilSvc "web-medols-be/utils/service"
|
utilSvc "web-medols-be/utils/service"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/minio/minio-go/v7"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ArticlesService
|
// ArticlesService
|
||||||
|
|
@ -47,18 +44,13 @@ type articlesService struct {
|
||||||
Cfg *config.Config
|
Cfg *config.Config
|
||||||
UsersRepo usersRepository.UsersRepository
|
UsersRepo usersRepository.UsersRepository
|
||||||
MinioStorage *minioStorage.MinioStorage
|
MinioStorage *minioStorage.MinioStorage
|
||||||
|
|
||||||
// Dynamic approval system dependencies
|
|
||||||
ArticleApprovalFlowsRepo articleApprovalFlowsRepository.ArticleApprovalFlowsRepository
|
|
||||||
ApprovalWorkflowsRepo approvalWorkflowsRepository.ApprovalWorkflowsRepository
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ArticlesService define interface of IArticlesService
|
// ArticlesService define interface of IArticlesService
|
||||||
type ArticlesService interface {
|
type ArticlesService interface {
|
||||||
All(clientId *uuid.UUID, authToken string, req request.ArticlesQueryRequest) (articles []*response.ArticlesResponse, paging paginator.Pagination, err error)
|
All(clientId *uuid.UUID, req request.ArticlesQueryRequest) (articles []*response.ArticlesResponse, paging paginator.Pagination, err error)
|
||||||
Show(clientId *uuid.UUID, id uint) (articles *response.ArticlesResponse, err error)
|
Show(clientId *uuid.UUID, id uint) (articles *response.ArticlesResponse, err error)
|
||||||
ShowByOldId(clientId *uuid.UUID, oldId uint) (articles *response.ArticlesResponse, err error)
|
ShowByOldId(clientId *uuid.UUID, oldId uint) (articles *response.ArticlesResponse, err error)
|
||||||
ShowBySlug(clientId *uuid.UUID, slug string) (articles *response.ArticlesResponse, err error)
|
|
||||||
Save(clientId *uuid.UUID, req request.ArticlesCreateRequest, authToken string) (articles *entity.Articles, err error)
|
Save(clientId *uuid.UUID, req request.ArticlesCreateRequest, authToken string) (articles *entity.Articles, err error)
|
||||||
SaveThumbnail(clientId *uuid.UUID, c *fiber.Ctx) (err error)
|
SaveThumbnail(clientId *uuid.UUID, c *fiber.Ctx) (err error)
|
||||||
Update(clientId *uuid.UUID, id uint, req request.ArticlesUpdateRequest) (err error)
|
Update(clientId *uuid.UUID, id uint, req request.ArticlesUpdateRequest) (err error)
|
||||||
|
|
@ -72,22 +64,6 @@ type ArticlesService interface {
|
||||||
ArticleMonthlyStats(clientId *uuid.UUID, authToken string, year *int) (articleMonthlyStats []*response.ArticleMonthlyStats, err error)
|
ArticleMonthlyStats(clientId *uuid.UUID, authToken string, year *int) (articleMonthlyStats []*response.ArticleMonthlyStats, err error)
|
||||||
PublishScheduling(clientId *uuid.UUID, id uint, publishSchedule string) error
|
PublishScheduling(clientId *uuid.UUID, id uint, publishSchedule string) error
|
||||||
ExecuteScheduling() error
|
ExecuteScheduling() error
|
||||||
|
|
||||||
// Dynamic approval system methods
|
|
||||||
SubmitForApproval(clientId *uuid.UUID, articleId uint, authToken string, workflowId *uint) error
|
|
||||||
GetApprovalStatus(clientId *uuid.UUID, articleId uint) (*response.ArticleApprovalStatusResponse, error)
|
|
||||||
GetArticlesWaitingForApproval(clientId *uuid.UUID, authToken string, page, limit int) ([]*response.ArticleApprovalQueueResponse, paginator.Pagination, error)
|
|
||||||
GetPendingApprovals(clientId *uuid.UUID, authToken string, page, limit int, typeId *int) ([]*response.ArticleApprovalQueueResponse, paginator.Pagination, error) // Updated with typeId filter
|
|
||||||
|
|
||||||
// No-approval system methods
|
|
||||||
CheckApprovalRequired(clientId *uuid.UUID, articleId uint, userId uint, userLevelId uint) (bool, error)
|
|
||||||
AutoApproveArticle(clientId *uuid.UUID, articleId uint, reason string) error
|
|
||||||
GetClientApprovalSettings(clientId *uuid.UUID) (*response.ClientApprovalSettingsResponse, error)
|
|
||||||
SetArticleApprovalExempt(clientId *uuid.UUID, articleId uint, exempt bool, reason string) error
|
|
||||||
|
|
||||||
// Publish/Unpublish methods
|
|
||||||
Publish(clientId *uuid.UUID, articleId uint, authToken string) error
|
|
||||||
Unpublish(clientId *uuid.UUID, articleId uint, authToken string) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewArticlesService init ArticlesService
|
// NewArticlesService init ArticlesService
|
||||||
|
|
@ -97,8 +73,6 @@ func NewArticlesService(
|
||||||
articleCategoryDetailsRepo articleCategoryDetailsRepository.ArticleCategoryDetailsRepository,
|
articleCategoryDetailsRepo articleCategoryDetailsRepository.ArticleCategoryDetailsRepository,
|
||||||
articleFilesRepo articleFilesRepository.ArticleFilesRepository,
|
articleFilesRepo articleFilesRepository.ArticleFilesRepository,
|
||||||
articleApprovalsRepo articleApprovalsRepository.ArticleApprovalsRepository,
|
articleApprovalsRepo articleApprovalsRepository.ArticleApprovalsRepository,
|
||||||
articleApprovalFlowsRepo articleApprovalFlowsRepository.ArticleApprovalFlowsRepository,
|
|
||||||
approvalWorkflowsRepo approvalWorkflowsRepository.ApprovalWorkflowsRepository,
|
|
||||||
log zerolog.Logger,
|
log zerolog.Logger,
|
||||||
cfg *config.Config,
|
cfg *config.Config,
|
||||||
usersRepo usersRepository.UsersRepository,
|
usersRepo usersRepository.UsersRepository,
|
||||||
|
|
@ -111,8 +85,6 @@ func NewArticlesService(
|
||||||
ArticleCategoryDetailsRepo: articleCategoryDetailsRepo,
|
ArticleCategoryDetailsRepo: articleCategoryDetailsRepo,
|
||||||
ArticleFilesRepo: articleFilesRepo,
|
ArticleFilesRepo: articleFilesRepo,
|
||||||
ArticleApprovalsRepo: articleApprovalsRepo,
|
ArticleApprovalsRepo: articleApprovalsRepo,
|
||||||
ArticleApprovalFlowsRepo: articleApprovalFlowsRepo,
|
|
||||||
ApprovalWorkflowsRepo: approvalWorkflowsRepo,
|
|
||||||
Log: log,
|
Log: log,
|
||||||
UsersRepo: usersRepo,
|
UsersRepo: usersRepo,
|
||||||
MinioStorage: minioStorage,
|
MinioStorage: minioStorage,
|
||||||
|
|
@ -121,17 +93,7 @@ func NewArticlesService(
|
||||||
}
|
}
|
||||||
|
|
||||||
// All implement interface of ArticlesService
|
// All implement interface of ArticlesService
|
||||||
func (_i *articlesService) All(clientId *uuid.UUID, authToken string, req request.ArticlesQueryRequest) (articless []*response.ArticlesResponse, paging paginator.Pagination, err error) {
|
func (_i *articlesService) All(clientId *uuid.UUID, req request.ArticlesQueryRequest) (articless []*response.ArticlesResponse, paging paginator.Pagination, err error) {
|
||||||
// Extract userLevelId from authToken
|
|
||||||
var userLevelId *uint
|
|
||||||
if authToken != "" {
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user != nil {
|
|
||||||
userLevelId = &user.UserLevelId
|
|
||||||
_i.Log.Info().Interface("userLevelId", userLevelId).Msg("Extracted userLevelId from auth token")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.Category != nil {
|
if req.Category != nil {
|
||||||
findCategory, err := _i.ArticleCategoriesRepo.FindOneBySlug(clientId, *req.Category)
|
findCategory, err := _i.ArticleCategoriesRepo.FindOneBySlug(clientId, *req.Category)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -140,7 +102,7 @@ func (_i *articlesService) All(clientId *uuid.UUID, authToken string, req reques
|
||||||
req.CategoryId = &findCategory.ID
|
req.CategoryId = &findCategory.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
results, paging, err := _i.Repo.GetAll(clientId, userLevelId, req)
|
results, paging, err := _i.Repo.GetAll(clientId, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -181,23 +143,12 @@ func (_i *articlesService) ShowByOldId(clientId *uuid.UUID, oldId uint) (article
|
||||||
return mapper.ArticlesResponseMapper(_i.Log, host, clientId, result, _i.ArticleCategoriesRepo, _i.ArticleCategoryDetailsRepo, _i.ArticleFilesRepo, _i.UsersRepo), nil
|
return mapper.ArticlesResponseMapper(_i.Log, host, clientId, result, _i.ArticleCategoriesRepo, _i.ArticleCategoryDetailsRepo, _i.ArticleFilesRepo, _i.UsersRepo), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_i *articlesService) ShowBySlug(clientId *uuid.UUID, slug string) (articles *response.ArticlesResponse, err error) {
|
|
||||||
result, err := _i.Repo.FindBySlug(clientId, slug)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
host := _i.Cfg.App.Domain
|
|
||||||
|
|
||||||
return mapper.ArticlesResponseMapper(_i.Log, host, clientId, result, _i.ArticleCategoriesRepo, _i.ArticleCategoryDetailsRepo, _i.ArticleFilesRepo, _i.UsersRepo), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *articlesService) Save(clientId *uuid.UUID, req request.ArticlesCreateRequest, authToken string) (articles *entity.Articles, err error) {
|
func (_i *articlesService) Save(clientId *uuid.UUID, req request.ArticlesCreateRequest, authToken string) (articles *entity.Articles, err error) {
|
||||||
_i.Log.Info().Interface("data", req).Msg("")
|
_i.Log.Info().Interface("data", req).Msg("")
|
||||||
newReq := req.ToEntity()
|
newReq := req.ToEntity()
|
||||||
|
|
||||||
var userLevelNumber int
|
var userLevelNumber int
|
||||||
var approvalLevelId int
|
var userParentLevelId int
|
||||||
if req.CreatedById != nil {
|
if req.CreatedById != nil {
|
||||||
createdBy, err := _i.UsersRepo.FindOne(clientId, *req.CreatedById)
|
createdBy, err := _i.UsersRepo.FindOne(clientId, *req.CreatedById)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -205,16 +156,16 @@ func (_i *articlesService) Save(clientId *uuid.UUID, req request.ArticlesCreateR
|
||||||
}
|
}
|
||||||
newReq.CreatedById = &createdBy.ID
|
newReq.CreatedById = &createdBy.ID
|
||||||
userLevelNumber = createdBy.UserLevel.LevelNumber
|
userLevelNumber = createdBy.UserLevel.LevelNumber
|
||||||
|
if createdBy.UserLevel.ParentLevelId != nil {
|
||||||
// Find the next higher level for approval (level_number should be smaller)
|
userParentLevelId = *createdBy.UserLevel.ParentLevelId
|
||||||
approvalLevelId = _i.findNextApprovalLevel(clientId, userLevelNumber)
|
}
|
||||||
} else {
|
} else {
|
||||||
createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
||||||
newReq.CreatedById = &createdBy.ID
|
newReq.CreatedById = &createdBy.ID
|
||||||
userLevelNumber = createdBy.UserLevel.LevelNumber
|
userLevelNumber = createdBy.UserLevel.LevelNumber
|
||||||
|
if createdBy.UserLevel.ParentLevelId != nil {
|
||||||
// Find the next higher level for approval (level_number should be smaller)
|
userParentLevelId = *createdBy.UserLevel.ParentLevelId
|
||||||
approvalLevelId = _i.findNextApprovalLevel(clientId, userLevelNumber)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isDraft := true
|
isDraft := true
|
||||||
|
|
@ -246,29 +197,19 @@ func (_i *articlesService) Save(clientId *uuid.UUID, req request.ArticlesCreateR
|
||||||
newReq.CreatedAt = parsedTime
|
newReq.CreatedAt = parsedTime
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic Approval Workflow System
|
// Approval
|
||||||
statusIdOne := 1
|
statusIdOne := 1
|
||||||
statusIdTwo := 2
|
statusIdTwo := 2
|
||||||
isPublishFalse := false
|
isPublishFalse := false
|
||||||
|
|
||||||
// Get user info for approval logic
|
|
||||||
createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
createdBy := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
||||||
|
|
||||||
// Check if user level requires approval
|
|
||||||
if createdBy != nil && *createdBy.UserLevel.IsApprovalActive == false {
|
if createdBy != nil && *createdBy.UserLevel.IsApprovalActive == false {
|
||||||
// User level doesn't require approval - auto publish
|
|
||||||
newReq.NeedApprovalFrom = nil
|
newReq.NeedApprovalFrom = nil
|
||||||
newReq.StatusId = &statusIdTwo
|
newReq.StatusId = &statusIdTwo
|
||||||
newReq.IsPublish = &isPublishFalse
|
|
||||||
newReq.PublishedAt = nil
|
|
||||||
newReq.BypassApproval = &[]bool{true}[0]
|
|
||||||
} else {
|
} else {
|
||||||
// User level requires approval - set to pending
|
newReq.NeedApprovalFrom = &userParentLevelId
|
||||||
newReq.NeedApprovalFrom = &approvalLevelId
|
|
||||||
newReq.StatusId = &statusIdOne
|
newReq.StatusId = &statusIdOne
|
||||||
newReq.IsPublish = &isPublishFalse
|
newReq.IsPublish = &isPublishFalse
|
||||||
newReq.PublishedAt = nil
|
newReq.PublishedAt = nil
|
||||||
newReq.BypassApproval = &[]bool{false}[0]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveArticleRes, err := _i.Repo.Create(clientId, newReq)
|
saveArticleRes, err := _i.Repo.Create(clientId, newReq)
|
||||||
|
|
@ -276,63 +217,30 @@ func (_i *articlesService) Save(clientId *uuid.UUID, req request.ArticlesCreateR
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dynamic Approval Workflow Assignment
|
// Approval
|
||||||
|
var articleApproval *entity.ArticleApprovals
|
||||||
|
|
||||||
if createdBy != nil && *createdBy.UserLevel.IsApprovalActive == true {
|
if createdBy != nil && *createdBy.UserLevel.IsApprovalActive == true {
|
||||||
// Get default workflow for the client
|
articleApproval = &entity.ArticleApprovals{
|
||||||
defaultWorkflow, err := _i.ApprovalWorkflowsRepo.GetDefault(clientId)
|
|
||||||
if err == nil && defaultWorkflow != nil {
|
|
||||||
// Assign workflow to article
|
|
||||||
saveArticleRes.WorkflowId = &defaultWorkflow.ID
|
|
||||||
saveArticleRes.CurrentApprovalStep = &[]int{1}[0] // Start at step 1
|
|
||||||
|
|
||||||
// Update article with workflow info
|
|
||||||
err = _i.Repo.Update(clientId, saveArticleRes.ID, saveArticleRes)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to update article with workflow")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create approval flow
|
|
||||||
approvalFlow := &entity.ArticleApprovalFlows{
|
|
||||||
ArticleId: saveArticleRes.ID,
|
|
||||||
WorkflowId: defaultWorkflow.ID,
|
|
||||||
CurrentStep: 1,
|
|
||||||
StatusId: 1, // In Progress
|
|
||||||
SubmittedById: *newReq.CreatedById,
|
|
||||||
SubmittedAt: time.Now(),
|
|
||||||
ClientId: clientId,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = _i.ArticleApprovalFlowsRepo.Create(clientId, approvalFlow)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to create approval flow")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create legacy approval record for backward compatibility
|
|
||||||
articleApproval := &entity.ArticleApprovals{
|
|
||||||
ArticleId: saveArticleRes.ID,
|
ArticleId: saveArticleRes.ID,
|
||||||
ApprovalBy: *newReq.CreatedById,
|
ApprovalBy: *newReq.CreatedById,
|
||||||
StatusId: statusIdOne,
|
StatusId: statusIdOne,
|
||||||
Message: "Need Approval",
|
Message: "Need Approval",
|
||||||
ApprovalAtLevel: &approvalLevelId,
|
ApprovalAtLevel: &userLevelNumber,
|
||||||
}
|
|
||||||
_, err = _i.ArticleApprovalsRepo.Create(articleApproval)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to create legacy approval record")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Auto-publish for users who don't require approval
|
articleApproval = &entity.ArticleApprovals{
|
||||||
articleApproval := &entity.ArticleApprovals{
|
|
||||||
ArticleId: saveArticleRes.ID,
|
ArticleId: saveArticleRes.ID,
|
||||||
ApprovalBy: *newReq.CreatedById,
|
ApprovalBy: *newReq.CreatedById,
|
||||||
StatusId: statusIdTwo,
|
StatusId: statusIdTwo,
|
||||||
Message: "Publish Otomatis",
|
Message: "Publish Otomatis",
|
||||||
ApprovalAtLevel: nil,
|
ApprovalAtLevel: nil,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_, err = _i.ArticleApprovalsRepo.Create(articleApproval)
|
_, err = _i.ArticleApprovalsRepo.Create(articleApproval)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_i.Log.Error().Err(err).Msg("Failed to create auto-approval record")
|
return nil, err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var categoryIds []string
|
var categoryIds []string
|
||||||
|
|
@ -465,7 +373,14 @@ func (_i *articlesService) Update(clientId *uuid.UUID, id uint, req request.Arti
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_i *articlesService) Delete(clientId *uuid.UUID, id uint) error {
|
func (_i *articlesService) Delete(clientId *uuid.UUID, id uint) error {
|
||||||
return _i.Repo.Delete(clientId, id)
|
result, err := _i.Repo.FindOne(clientId, id)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
isActive := false
|
||||||
|
result.IsActive = &isActive
|
||||||
|
return _i.Repo.Update(clientId, id, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (_i *articlesService) Viewer(clientId *uuid.UUID, c *fiber.Ctx) (err error) {
|
func (_i *articlesService) Viewer(clientId *uuid.UUID, c *fiber.Ctx) (err error) {
|
||||||
|
|
@ -679,21 +594,6 @@ func (_i *articlesService) PublishScheduling(clientId *uuid.UUID, id uint, publi
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate publish schedule format
|
|
||||||
dateLayout := "2006-01-02"
|
|
||||||
datetimeLayout := "2006-01-02 15:04:05"
|
|
||||||
|
|
||||||
// Try parsing as datetime first (with time)
|
|
||||||
_, parseErr := time.Parse(datetimeLayout, publishSchedule)
|
|
||||||
if parseErr != nil {
|
|
||||||
// If datetime parsing fails, try parsing as date only
|
|
||||||
_, parseErr = time.Parse(dateLayout, publishSchedule)
|
|
||||||
if parseErr != nil {
|
|
||||||
return fmt.Errorf("invalid publish schedule format. Supported formats: '2006-01-02' or '2006-01-02 15:04:05'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result.PublishSchedule = &publishSchedule
|
result.PublishSchedule = &publishSchedule
|
||||||
return _i.Repo.Update(clientId, id, result)
|
return _i.Repo.Update(clientId, id, result)
|
||||||
}
|
}
|
||||||
|
|
@ -714,52 +614,21 @@ func (_i *articlesService) ExecuteScheduling() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Support both date-only and datetime formats
|
layout := "2006-01-02"
|
||||||
dateLayout := "2006-01-02"
|
|
||||||
datetimeLayout := "2006-01-02 15:04:05"
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
today := now.Truncate(24 * time.Hour)
|
||||||
|
|
||||||
for _, article := range articles { // Looping setiap artikel
|
for _, article := range articles { // Looping setiap artikel
|
||||||
if article.PublishSchedule == nil {
|
if article.PublishSchedule == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
var scheduledTime time.Time
|
parsedDate, err := time.Parse(layout, *article.PublishSchedule)
|
||||||
var parseErr error
|
if err != nil {
|
||||||
|
|
||||||
// Try parsing as datetime first (with time)
|
|
||||||
scheduledTime, parseErr = time.Parse(datetimeLayout, *article.PublishSchedule)
|
|
||||||
if parseErr != nil {
|
|
||||||
// If datetime parsing fails, try parsing as date only
|
|
||||||
scheduledTime, parseErr = time.Parse(dateLayout, *article.PublishSchedule)
|
|
||||||
if parseErr != nil {
|
|
||||||
_i.Log.Warn().Str("timestamp", time.Now().
|
|
||||||
Format(time.RFC3339)).Str("Service:articlesService", "Methods:ExecuteScheduling").
|
|
||||||
Str("Invalid schedule format", *article.PublishSchedule).
|
|
||||||
Interface("Article ID", article.ID).Msg("")
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// If parsed as date only, set time to start of day (00:00:00) in local timezone
|
|
||||||
scheduledTime = time.Date(scheduledTime.Year(), scheduledTime.Month(), scheduledTime.Day(), 0, 0, 0, 0, now.Location())
|
|
||||||
} else {
|
|
||||||
// For datetime format, parse in local timezone
|
|
||||||
scheduledTime = time.Date(scheduledTime.Year(), scheduledTime.Month(), scheduledTime.Day(),
|
|
||||||
scheduledTime.Hour(), scheduledTime.Minute(), scheduledTime.Second(), 0, now.Location())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the scheduled time has passed (for datetime) or if it's today (for date only)
|
if parsedDate.Equal(today) {
|
||||||
shouldPublish := false
|
|
||||||
if len(*article.PublishSchedule) > 10 { // Contains time (datetime format)
|
|
||||||
// For datetime format, check if scheduled time has passed
|
|
||||||
shouldPublish = now.After(scheduledTime) || now.Equal(scheduledTime)
|
|
||||||
} else {
|
|
||||||
// For date-only format, check if it's today
|
|
||||||
today := now.Truncate(24 * time.Hour)
|
|
||||||
scheduledDate := scheduledTime.Truncate(24 * time.Hour)
|
|
||||||
shouldPublish = scheduledDate.Equal(today) || scheduledDate.Before(today)
|
|
||||||
}
|
|
||||||
|
|
||||||
if shouldPublish {
|
|
||||||
isPublish := true
|
isPublish := true
|
||||||
statusIdTwo := 2
|
statusIdTwo := 2
|
||||||
|
|
||||||
|
|
@ -773,10 +642,6 @@ func (_i *articlesService) ExecuteScheduling() error {
|
||||||
_i.Log.Info().Str("timestamp", time.Now().
|
_i.Log.Info().Str("timestamp", time.Now().
|
||||||
Format(time.RFC3339)).Str("Service:articlesService", "Methods:ExecuteScheduling").
|
Format(time.RFC3339)).Str("Service:articlesService", "Methods:ExecuteScheduling").
|
||||||
Interface("Failed to publish Article ID : ", article.ID).Msg("")
|
Interface("Failed to publish Article ID : ", article.ID).Msg("")
|
||||||
} else {
|
|
||||||
_i.Log.Info().Str("timestamp", time.Now().
|
|
||||||
Format(time.RFC3339)).Str("Service:articlesService", "Methods:ExecuteScheduling").
|
|
||||||
Interface("Successfully published Article ID : ", article.ID).Msg("")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -796,573 +661,3 @@ func getFileExtension(filename string) string {
|
||||||
// ambil ekstensi terakhir
|
// ambil ekstensi terakhir
|
||||||
return parts[len(parts)-1]
|
return parts[len(parts)-1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// SubmitForApproval submits an article for approval using the dynamic workflow system
|
|
||||||
func (_i *articlesService) SubmitForApproval(clientId *uuid.UUID, articleId uint, authToken string, workflowId *uint) error {
|
|
||||||
// Extract user info from auth token
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
return errors.New("user not found from auth token")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if article exists
|
|
||||||
article, err := _i.Repo.FindOne(clientId, articleId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no workflow specified, get the default workflow
|
|
||||||
if workflowId == nil {
|
|
||||||
defaultWorkflow, err := _i.ApprovalWorkflowsRepo.GetDefault(clientId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
workflowId = &defaultWorkflow.ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate workflow exists and is active
|
|
||||||
_, err = _i.ApprovalWorkflowsRepo.FindOne(clientId, *workflowId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create approval flow
|
|
||||||
approvalFlow := &entity.ArticleApprovalFlows{
|
|
||||||
ArticleId: articleId,
|
|
||||||
WorkflowId: *workflowId,
|
|
||||||
CurrentStep: 1,
|
|
||||||
StatusId: 1, // 1 = In Progress
|
|
||||||
ClientId: clientId,
|
|
||||||
SubmittedById: user.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = _i.ArticleApprovalFlowsRepo.Create(clientId, approvalFlow)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update article status to pending approval
|
|
||||||
statusId := 1 // Pending approval
|
|
||||||
article.StatusId = &statusId
|
|
||||||
article.WorkflowId = workflowId
|
|
||||||
|
|
||||||
err = _i.Repo.Update(clientId, articleId, article)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_i.Log.Info().Str("timestamp", time.Now().
|
|
||||||
Format(time.RFC3339)).Str("Service:articlesService", "Methods:SubmitForApproval").
|
|
||||||
Interface("Article submitted for approval", articleId).Msg("")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetApprovalStatus gets the current approval status of an article
|
|
||||||
func (_i *articlesService) GetApprovalStatus(clientId *uuid.UUID, articleId uint) (*response.ArticleApprovalStatusResponse, error) {
|
|
||||||
// Check if article exists
|
|
||||||
_, err := _i.Repo.FindOne(clientId, articleId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get approval flow
|
|
||||||
approvalFlow, err := _i.ArticleApprovalFlowsRepo.FindByArticleId(clientId, articleId)
|
|
||||||
if err != nil {
|
|
||||||
// Article might not be in approval process
|
|
||||||
return &response.ArticleApprovalStatusResponse{
|
|
||||||
ArticleId: articleId,
|
|
||||||
Status: "not_submitted",
|
|
||||||
CurrentStep: 0,
|
|
||||||
TotalSteps: 0,
|
|
||||||
Progress: 0,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get workflow details
|
|
||||||
workflow, err := _i.ApprovalWorkflowsRepo.FindOne(clientId, approvalFlow.WorkflowId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get workflow steps
|
|
||||||
workflowSteps, err := _i.ApprovalWorkflowsRepo.GetWorkflowSteps(clientId, approvalFlow.WorkflowId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
totalSteps := len(workflowSteps)
|
|
||||||
progress := 0.0
|
|
||||||
if totalSteps > 0 {
|
|
||||||
progress = float64(approvalFlow.CurrentStep-1) / float64(totalSteps) * 100
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine status
|
|
||||||
status := "in_progress"
|
|
||||||
if approvalFlow.StatusId == 2 {
|
|
||||||
status = "approved"
|
|
||||||
} else if approvalFlow.StatusId == 3 {
|
|
||||||
status = "rejected"
|
|
||||||
} else if approvalFlow.StatusId == 4 {
|
|
||||||
status = "revision_requested"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current approver info
|
|
||||||
var currentApprover *string
|
|
||||||
var nextStep *string
|
|
||||||
if approvalFlow.CurrentStep <= totalSteps && approvalFlow.StatusId == 1 {
|
|
||||||
if approvalFlow.CurrentStep < totalSteps {
|
|
||||||
// Array indexing starts from 0, so subtract 1 from CurrentStep
|
|
||||||
nextStepIndex := approvalFlow.CurrentStep - 1
|
|
||||||
if nextStepIndex >= 0 && nextStepIndex < len(workflowSteps) {
|
|
||||||
nextStepInfo := workflowSteps[nextStepIndex]
|
|
||||||
nextStep = &nextStepInfo.RequiredUserLevel.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &response.ArticleApprovalStatusResponse{
|
|
||||||
ArticleId: articleId,
|
|
||||||
WorkflowId: &workflow.ID,
|
|
||||||
WorkflowName: &workflow.Name,
|
|
||||||
CurrentStep: approvalFlow.CurrentStep,
|
|
||||||
TotalSteps: totalSteps,
|
|
||||||
Status: status,
|
|
||||||
CurrentApprover: currentApprover,
|
|
||||||
SubmittedAt: &approvalFlow.CreatedAt,
|
|
||||||
LastActionAt: &approvalFlow.UpdatedAt,
|
|
||||||
Progress: progress,
|
|
||||||
CanApprove: false, // TODO: Implement based on user permissions
|
|
||||||
NextStep: nextStep,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPendingApprovals gets articles pending approval for a specific user level
|
|
||||||
func (_i *articlesService) GetPendingApprovals(clientId *uuid.UUID, authToken string, page, limit int, typeId *int) ([]*response.ArticleApprovalQueueResponse, paginator.Pagination, error) {
|
|
||||||
// Extract user info from auth token
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
return nil, paginator.Pagination{}, errors.New("user not found from auth token")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare filters
|
|
||||||
filters := make(map[string]interface{})
|
|
||||||
if typeId != nil {
|
|
||||||
filters["type_id"] = *typeId
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get pending approvals for the user level
|
|
||||||
approvalFlows, paging, err := _i.ArticleApprovalFlowsRepo.GetPendingApprovals(clientId, user.UserLevelId, page, limit, filters)
|
|
||||||
if err != nil {
|
|
||||||
return nil, paging, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var responses []*response.ArticleApprovalQueueResponse
|
|
||||||
for _, flow := range approvalFlows {
|
|
||||||
// Get article details
|
|
||||||
article, err := _i.Repo.FindOne(clientId, flow.ArticleId)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get workflow details
|
|
||||||
workflow, err := _i.ApprovalWorkflowsRepo.FindOne(clientId, flow.WorkflowId)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get workflow steps
|
|
||||||
workflowSteps, err := _i.ApprovalWorkflowsRepo.GetWorkflowSteps(clientId, flow.WorkflowId)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate days in queue
|
|
||||||
daysInQueue := int(time.Since(flow.CreatedAt).Hours() / 24)
|
|
||||||
|
|
||||||
// Determine priority based on days in queue
|
|
||||||
priority := "low"
|
|
||||||
if daysInQueue > 7 {
|
|
||||||
priority = "urgent"
|
|
||||||
} else if daysInQueue > 3 {
|
|
||||||
priority = "high"
|
|
||||||
} else if daysInQueue > 1 {
|
|
||||||
priority = "medium"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get author name
|
|
||||||
var authorName string
|
|
||||||
if article.CreatedById != nil {
|
|
||||||
user, err := _i.UsersRepo.FindOne(clientId, *article.CreatedById)
|
|
||||||
if err == nil && user != nil {
|
|
||||||
authorName = user.Fullname
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get category name
|
|
||||||
var categoryName string
|
|
||||||
if article.CategoryId != 0 {
|
|
||||||
category, err := _i.ArticleCategoriesRepo.FindOne(clientId, uint(article.CategoryId))
|
|
||||||
if err == nil && category != nil {
|
|
||||||
categoryName = category.Title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
response := &response.ArticleApprovalQueueResponse{
|
|
||||||
ID: article.ID,
|
|
||||||
Title: article.Title,
|
|
||||||
Slug: article.Slug,
|
|
||||||
Description: article.Description,
|
|
||||||
CategoryName: categoryName,
|
|
||||||
AuthorName: authorName,
|
|
||||||
SubmittedAt: flow.CreatedAt,
|
|
||||||
CurrentStep: flow.CurrentStep,
|
|
||||||
TotalSteps: len(workflowSteps),
|
|
||||||
Priority: priority,
|
|
||||||
DaysInQueue: daysInQueue,
|
|
||||||
WorkflowName: workflow.Name,
|
|
||||||
CanApprove: true, // TODO: Implement based on user permissions
|
|
||||||
EstimatedTime: "2-3 days", // TODO: Calculate based on historical data
|
|
||||||
}
|
|
||||||
|
|
||||||
responses = append(responses, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
return responses, paging, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetArticlesWaitingForApproval gets articles that are waiting for approval by a specific user level
|
|
||||||
func (_i *articlesService) GetArticlesWaitingForApproval(clientId *uuid.UUID, authToken string, page, limit int) ([]*response.ArticleApprovalQueueResponse, paginator.Pagination, error) {
|
|
||||||
// Extract user info from auth token
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
return nil, paginator.Pagination{}, errors.New("user not found from auth token")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the existing repository method with proper filtering
|
|
||||||
pagination := paginator.Pagination{
|
|
||||||
Page: page,
|
|
||||||
Limit: limit,
|
|
||||||
}
|
|
||||||
req := request.ArticlesQueryRequest{
|
|
||||||
Pagination: &pagination,
|
|
||||||
}
|
|
||||||
|
|
||||||
articles, paging, err := _i.Repo.GetAll(clientId, &user.UserLevelId, req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, paging, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build response
|
|
||||||
var responses []*response.ArticleApprovalQueueResponse
|
|
||||||
for _, article := range articles {
|
|
||||||
response := &response.ArticleApprovalQueueResponse{
|
|
||||||
ID: article.ID,
|
|
||||||
Title: article.Title,
|
|
||||||
Slug: article.Slug,
|
|
||||||
Description: article.Description,
|
|
||||||
SubmittedAt: article.CreatedAt,
|
|
||||||
CurrentStep: 1, // Will be updated with actual step
|
|
||||||
CanApprove: true,
|
|
||||||
}
|
|
||||||
responses = append(responses, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
return responses, paging, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CheckApprovalRequired checks if an article requires approval based on client settings
|
|
||||||
func (_i *articlesService) CheckApprovalRequired(clientId *uuid.UUID, articleId uint, userId uint, userLevelId uint) (bool, error) {
|
|
||||||
// Get article to check category and other properties
|
|
||||||
article, err := _i.Repo.FindOne(clientId, articleId)
|
|
||||||
if err != nil {
|
|
||||||
return true, err // Default to requiring approval on error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if article is already exempt
|
|
||||||
if article.ApprovalExempt != nil && *article.ApprovalExempt {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if article should bypass approval
|
|
||||||
if article.BypassApproval != nil && *article.BypassApproval {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check client-level settings (this would require the client approval settings service)
|
|
||||||
// For now, we'll use a simple check
|
|
||||||
// TODO: Integrate with ClientApprovalSettingsService
|
|
||||||
|
|
||||||
// Check if workflow is set to no approval
|
|
||||||
if article.WorkflowId != nil {
|
|
||||||
workflow, err := _i.ApprovalWorkflowsRepo.FindOne(clientId, *article.WorkflowId)
|
|
||||||
if err == nil && workflow != nil {
|
|
||||||
if workflow.RequiresApproval != nil && !*workflow.RequiresApproval {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
if workflow.AutoPublish != nil && *workflow.AutoPublish {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to requiring approval
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AutoApproveArticle automatically approves an article (for no-approval scenarios)
|
|
||||||
func (_i *articlesService) AutoApproveArticle(clientId *uuid.UUID, articleId uint, reason string) error {
|
|
||||||
article, err := _i.Repo.FindOne(clientId, articleId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update article status to approved
|
|
||||||
updates := map[string]interface{}{
|
|
||||||
"status_id": 2, // Assuming 2 = approved
|
|
||||||
"is_publish": true,
|
|
||||||
"published_at": time.Now(),
|
|
||||||
"current_approval_step": 0, // Reset approval step
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert updates map to article entity
|
|
||||||
articleUpdate := &entity.Articles{}
|
|
||||||
if isPublish, ok := updates["is_publish"].(bool); ok {
|
|
||||||
articleUpdate.IsPublish = &isPublish
|
|
||||||
}
|
|
||||||
if publishedAt, ok := updates["published_at"].(time.Time); ok {
|
|
||||||
articleUpdate.PublishedAt = &publishedAt
|
|
||||||
}
|
|
||||||
if currentApprovalStep, ok := updates["current_approval_step"].(int); ok {
|
|
||||||
articleUpdate.CurrentApprovalStep = ¤tApprovalStep
|
|
||||||
}
|
|
||||||
|
|
||||||
err = _i.Repo.Update(clientId, articleId, articleUpdate)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create approval flow record for audit trail
|
|
||||||
approvalFlow := &entity.ArticleApprovalFlows{
|
|
||||||
ArticleId: articleId,
|
|
||||||
WorkflowId: *article.WorkflowId,
|
|
||||||
CurrentStep: 0,
|
|
||||||
StatusId: 2, // approved
|
|
||||||
SubmittedById: *article.CreatedById,
|
|
||||||
SubmittedAt: time.Now(),
|
|
||||||
CompletedAt: &[]time.Time{time.Now()}[0],
|
|
||||||
ClientId: clientId,
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = _i.ArticleApprovalFlowsRepo.Create(clientId, approvalFlow)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to create approval flow for auto-approved article")
|
|
||||||
// Don't return error as article was already updated
|
|
||||||
}
|
|
||||||
|
|
||||||
_i.Log.Info().
|
|
||||||
Str("article_id", fmt.Sprintf("%d", articleId)).
|
|
||||||
Str("client_id", clientId.String()).
|
|
||||||
Str("reason", reason).
|
|
||||||
Msg("Article auto-approved")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetClientApprovalSettings gets the approval settings for a client
|
|
||||||
func (_i *articlesService) GetClientApprovalSettings(clientId *uuid.UUID) (*response.ClientApprovalSettingsResponse, error) {
|
|
||||||
// This would require the ClientApprovalSettingsService
|
|
||||||
// For now, return default settings
|
|
||||||
return &response.ClientApprovalSettingsResponse{
|
|
||||||
ClientId: clientId.String(),
|
|
||||||
RequiresApproval: true, // Default to requiring approval
|
|
||||||
AutoPublishArticles: false,
|
|
||||||
IsActive: true,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetArticleApprovalExempt sets whether an article is exempt from approval
|
|
||||||
func (_i *articlesService) SetArticleApprovalExempt(clientId *uuid.UUID, articleId uint, exempt bool, reason string) error {
|
|
||||||
updates := map[string]interface{}{
|
|
||||||
"approval_exempt": &exempt,
|
|
||||||
}
|
|
||||||
|
|
||||||
if exempt {
|
|
||||||
// If exempt, also set bypass approval
|
|
||||||
bypass := true
|
|
||||||
updates["bypass_approval"] = &bypass
|
|
||||||
updates["current_approval_step"] = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert updates map to article entity
|
|
||||||
articleUpdate := &entity.Articles{}
|
|
||||||
if approvalExempt, ok := updates["approval_exempt"].(*bool); ok {
|
|
||||||
articleUpdate.ApprovalExempt = approvalExempt
|
|
||||||
}
|
|
||||||
if bypassApproval, ok := updates["bypass_approval"].(*bool); ok {
|
|
||||||
articleUpdate.BypassApproval = bypassApproval
|
|
||||||
}
|
|
||||||
if currentApprovalStep, ok := updates["current_approval_step"].(int); ok {
|
|
||||||
articleUpdate.CurrentApprovalStep = ¤tApprovalStep
|
|
||||||
}
|
|
||||||
|
|
||||||
err := _i.Repo.Update(clientId, articleId, articleUpdate)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
_i.Log.Info().
|
|
||||||
Str("article_id", fmt.Sprintf("%d", articleId)).
|
|
||||||
Str("client_id", clientId.String()).
|
|
||||||
Bool("exempt", exempt).
|
|
||||||
Str("reason", reason).
|
|
||||||
Msg("Article approval exemption updated")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Publish publishes an article
|
|
||||||
func (_i *articlesService) Publish(clientId *uuid.UUID, articleId uint, authToken string) error {
|
|
||||||
// Extract user info from auth token
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
return errors.New("user not found from auth token")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if article exists
|
|
||||||
article, err := _i.Repo.FindOne(clientId, articleId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if article is already published
|
|
||||||
if article.IsPublish != nil && *article.IsPublish {
|
|
||||||
return errors.New("article is already published")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user has permission to publish
|
|
||||||
// For now, we'll allow any authenticated user to publish
|
|
||||||
// You can add more sophisticated permission checks here
|
|
||||||
|
|
||||||
// Update article to published status
|
|
||||||
isPublish := true
|
|
||||||
publishedAt := time.Now()
|
|
||||||
isDraftFalse := false
|
|
||||||
statusIdTwo := 2 // Published status
|
|
||||||
|
|
||||||
article.IsPublish = &isPublish
|
|
||||||
article.PublishedAt = &publishedAt
|
|
||||||
article.IsDraft = &isDraftFalse
|
|
||||||
article.DraftedAt = nil
|
|
||||||
article.StatusId = &statusIdTwo
|
|
||||||
article.PublishSchedule = nil // Clear any scheduled publish time
|
|
||||||
|
|
||||||
err = _i.Repo.Update(clientId, articleId, article)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create approval record for audit trail
|
|
||||||
articleApproval := &entity.ArticleApprovals{
|
|
||||||
ArticleId: articleId,
|
|
||||||
ApprovalBy: user.ID,
|
|
||||||
StatusId: statusIdTwo,
|
|
||||||
Message: "Article published",
|
|
||||||
ApprovalAtLevel: nil,
|
|
||||||
}
|
|
||||||
_, err = _i.ArticleApprovalsRepo.Create(articleApproval)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to create approval record for published article")
|
|
||||||
// Don't return error as article was already updated
|
|
||||||
}
|
|
||||||
|
|
||||||
_i.Log.Info().
|
|
||||||
Str("article_id", fmt.Sprintf("%d", articleId)).
|
|
||||||
Str("client_id", clientId.String()).
|
|
||||||
Str("user_id", fmt.Sprintf("%d", user.ID)).
|
|
||||||
Msg("Article published successfully")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unpublish unpublishes an article
|
|
||||||
func (_i *articlesService) Unpublish(clientId *uuid.UUID, articleId uint, authToken string) error {
|
|
||||||
// Extract user info from auth token
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
return errors.New("user not found from auth token")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if article exists
|
|
||||||
article, err := _i.Repo.FindOne(clientId, articleId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if article is already unpublished
|
|
||||||
if article.IsPublish == nil || !*article.IsPublish {
|
|
||||||
return errors.New("article is already unpublished")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if user has permission to unpublish
|
|
||||||
// For now, we'll allow any authenticated user to unpublish
|
|
||||||
// You can add more sophisticated permission checks here
|
|
||||||
|
|
||||||
// Update article to unpublished status
|
|
||||||
isPublishFalse := false
|
|
||||||
isDraftTrue := true
|
|
||||||
draftedAt := time.Now()
|
|
||||||
statusIdOne := 1 // Draft status
|
|
||||||
|
|
||||||
article.IsPublish = &isPublishFalse
|
|
||||||
article.PublishedAt = nil
|
|
||||||
article.PublishSchedule = nil
|
|
||||||
article.IsDraft = &isDraftTrue
|
|
||||||
article.DraftedAt = &draftedAt
|
|
||||||
article.StatusId = &statusIdOne
|
|
||||||
|
|
||||||
err = _i.Repo.Update(clientId, articleId, article)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create approval record for audit trail
|
|
||||||
articleApproval := &entity.ArticleApprovals{
|
|
||||||
ArticleId: articleId,
|
|
||||||
ApprovalBy: user.ID,
|
|
||||||
StatusId: statusIdOne,
|
|
||||||
Message: "Article unpublished",
|
|
||||||
ApprovalAtLevel: nil,
|
|
||||||
}
|
|
||||||
_, err = _i.ArticleApprovalsRepo.Create(articleApproval)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to create approval record for unpublished article")
|
|
||||||
// Don't return error as article was already updated
|
|
||||||
}
|
|
||||||
|
|
||||||
_i.Log.Info().
|
|
||||||
Str("article_id", fmt.Sprintf("%d", articleId)).
|
|
||||||
Str("client_id", clientId.String()).
|
|
||||||
Str("user_id", fmt.Sprintf("%d", user.ID)).
|
|
||||||
Msg("Article unpublished successfully")
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// findNextApprovalLevel finds the next higher level for approval
|
|
||||||
func (_i *articlesService) findNextApprovalLevel(clientId *uuid.UUID, currentLevelNumber int) int {
|
|
||||||
// For now, we'll use a simple logic based on level numbers
|
|
||||||
// Level 3 (POLRES) -> Level 2 (POLDAS) -> Level 1 (POLDAS)
|
|
||||||
|
|
||||||
switch currentLevelNumber {
|
|
||||||
case 3: // POLRES
|
|
||||||
return 2 // Should be approved by POLDAS (Level 2)
|
|
||||||
case 2: // POLDAS
|
|
||||||
return 1 // Should be approved by Level 1
|
|
||||||
case 1: // Highest level
|
|
||||||
return 0 // No approval needed, can publish directly
|
|
||||||
default:
|
|
||||||
_i.Log.Warn().Int("currentLevel", currentLevelNumber).Msg("Unknown level, no approval needed")
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,66 +0,0 @@
|
||||||
package bookmarks
|
|
||||||
|
|
||||||
import (
|
|
||||||
"web-medols-be/app/middleware"
|
|
||||||
"web-medols-be/app/module/bookmarks/controller"
|
|
||||||
"web-medols-be/app/module/bookmarks/repository"
|
|
||||||
"web-medols-be/app/module/bookmarks/service"
|
|
||||||
usersRepo "web-medols-be/app/module/users/repository"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"go.uber.org/fx"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BookmarksRouter struct of BookmarksRouter
|
|
||||||
type BookmarksRouter struct {
|
|
||||||
App fiber.Router
|
|
||||||
Controller controller.BookmarksController
|
|
||||||
UsersRepo usersRepo.UsersRepository
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBookmarksModule register bulky of Bookmarks module
|
|
||||||
var NewBookmarksModule = fx.Options(
|
|
||||||
// register repository of Bookmarks module
|
|
||||||
fx.Provide(repository.NewBookmarksRepository),
|
|
||||||
|
|
||||||
// register service of Bookmarks module
|
|
||||||
fx.Provide(service.NewBookmarksService),
|
|
||||||
|
|
||||||
// register controller of Bookmarks module
|
|
||||||
fx.Provide(controller.NewBookmarksController),
|
|
||||||
|
|
||||||
// register router of Bookmarks module
|
|
||||||
fx.Provide(NewBookmarksRouter),
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewBookmarksRouter init BookmarksRouter
|
|
||||||
func NewBookmarksRouter(fiber *fiber.App, controller controller.BookmarksController, usersRepo usersRepo.UsersRepository) *BookmarksRouter {
|
|
||||||
return &BookmarksRouter{
|
|
||||||
App: fiber,
|
|
||||||
Controller: controller,
|
|
||||||
UsersRepo: usersRepo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterBookmarksRoutes register routes of Bookmarks module
|
|
||||||
func (_i *BookmarksRouter) RegisterBookmarksRoutes() {
|
|
||||||
// define controllers
|
|
||||||
bookmarksController := _i.Controller
|
|
||||||
|
|
||||||
// define routes
|
|
||||||
_i.App.Route("/bookmarks", func(router fiber.Router) {
|
|
||||||
// Add user middleware to extract user level from JWT token
|
|
||||||
router.Use(middleware.UserMiddleware(_i.UsersRepo))
|
|
||||||
|
|
||||||
// Public routes (require authentication)
|
|
||||||
router.Get("/", bookmarksController.All)
|
|
||||||
router.Get("/:id", bookmarksController.Show)
|
|
||||||
router.Post("/", bookmarksController.Save)
|
|
||||||
router.Delete("/:id", bookmarksController.Delete)
|
|
||||||
|
|
||||||
// User-specific routes
|
|
||||||
router.Get("/user", bookmarksController.GetByUserId)
|
|
||||||
router.Post("/toggle/:articleId", bookmarksController.ToggleBookmark)
|
|
||||||
router.Get("/summary", bookmarksController.GetBookmarkSummary)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,342 +0,0 @@
|
||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"web-medols-be/app/middleware"
|
|
||||||
"web-medols-be/app/module/bookmarks/request"
|
|
||||||
"web-medols-be/app/module/bookmarks/service"
|
|
||||||
"web-medols-be/utils/paginator"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
|
||||||
utilVal "web-medols-be/utils/validator"
|
|
||||||
)
|
|
||||||
|
|
||||||
type bookmarksController struct {
|
|
||||||
bookmarksService service.BookmarksService
|
|
||||||
Log zerolog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
type BookmarksController interface {
|
|
||||||
All(c *fiber.Ctx) error
|
|
||||||
Show(c *fiber.Ctx) error
|
|
||||||
Save(c *fiber.Ctx) error
|
|
||||||
Delete(c *fiber.Ctx) error
|
|
||||||
GetByUserId(c *fiber.Ctx) error
|
|
||||||
ToggleBookmark(c *fiber.Ctx) error
|
|
||||||
GetBookmarkSummary(c *fiber.Ctx) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBookmarksController(bookmarksService service.BookmarksService, log zerolog.Logger) BookmarksController {
|
|
||||||
return &bookmarksController{
|
|
||||||
bookmarksService: bookmarksService,
|
|
||||||
Log: log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All Bookmarks
|
|
||||||
// @Summary Get all Bookmarks
|
|
||||||
// @Description API for getting all Bookmarks
|
|
||||||
// @Tags Bookmarks
|
|
||||||
// @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 request.BookmarksQueryRequest 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 /bookmarks [get]
|
|
||||||
func (_i *bookmarksController) All(c *fiber.Ctx) error {
|
|
||||||
paginate, err := paginator.Paginate(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
reqContext := request.BookmarksQueryRequestContext{
|
|
||||||
ArticleId: c.Query("articleId"),
|
|
||||||
}
|
|
||||||
req := reqContext.ToParamRequest()
|
|
||||||
req.Pagination = paginate
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
|
|
||||||
bookmarksData, paging, err := _i.bookmarksService.All(clientId, authToken, req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Successfully retrieved bookmarks"},
|
|
||||||
Data: bookmarksData,
|
|
||||||
Meta: &paging,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show Bookmark
|
|
||||||
// @Summary Get Bookmark by ID
|
|
||||||
// @Description API for getting Bookmark by ID
|
|
||||||
// @Tags Bookmarks
|
|
||||||
// @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 "Bookmark ID"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /bookmarks/{id} [get]
|
|
||||||
func (_i *bookmarksController) Show(c *fiber.Ctx) error {
|
|
||||||
id, err := strconv.Atoi(c.Params("id"))
|
|
||||||
if err != nil {
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: false,
|
|
||||||
Messages: utilRes.Messages{"Invalid bookmark ID"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
|
|
||||||
bookmarkData, err := _i.bookmarksService.Show(clientId, uint(id))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Successfully retrieved bookmark"},
|
|
||||||
Data: bookmarkData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save Bookmark
|
|
||||||
// @Summary Create new Bookmark
|
|
||||||
// @Description API for creating new Bookmark
|
|
||||||
// @Tags Bookmarks
|
|
||||||
// @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.BookmarksCreateRequest true "Bookmark data"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /bookmarks [post]
|
|
||||||
func (_i *bookmarksController) Save(c *fiber.Ctx) error {
|
|
||||||
var req request.BookmarksCreateRequest
|
|
||||||
if err := c.BodyParser(&req); err != nil {
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: false,
|
|
||||||
Messages: utilRes.Messages{"Invalid request body"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate request
|
|
||||||
if err := utilVal.ValidateStruct(req); err != nil {
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: false,
|
|
||||||
Messages: utilRes.Messages{"Validation error"},
|
|
||||||
Data: err,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
_i.Log.Info().Str("authToken", authToken).Msg("")
|
|
||||||
|
|
||||||
bookmarkData, err := _i.bookmarksService.Save(clientId, req, authToken)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Successfully created bookmark"},
|
|
||||||
Data: bookmarkData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete Bookmark
|
|
||||||
// @Summary Delete Bookmark
|
|
||||||
// @Description API for deleting Bookmark
|
|
||||||
// @Tags Bookmarks
|
|
||||||
// @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 "Bookmark ID"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /bookmarks/{id} [delete]
|
|
||||||
func (_i *bookmarksController) Delete(c *fiber.Ctx) error {
|
|
||||||
id, err := strconv.Atoi(c.Params("id"))
|
|
||||||
if err != nil {
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: false,
|
|
||||||
Messages: utilRes.Messages{"Invalid bookmark ID"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
|
|
||||||
err = _i.bookmarksService.Delete(clientId, uint(id))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Successfully deleted bookmark"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Bookmarks by User ID
|
|
||||||
// @Summary Get Bookmarks by User ID
|
|
||||||
// @Description API for getting Bookmarks by User ID
|
|
||||||
// @Tags Bookmarks
|
|
||||||
// @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 request.BookmarksQueryRequest 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 /bookmarks/user [get]
|
|
||||||
func (_i *bookmarksController) GetByUserId(c *fiber.Ctx) error {
|
|
||||||
paginate, err := paginator.Paginate(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
reqContext := request.BookmarksQueryRequestContext{
|
|
||||||
ArticleId: c.Query("articleId"),
|
|
||||||
}
|
|
||||||
req := reqContext.ToParamRequest()
|
|
||||||
req.Pagination = paginate
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
_i.Log.Info().Str("authToken", authToken).Msg("")
|
|
||||||
|
|
||||||
bookmarksData, paging, err := _i.bookmarksService.GetByUserId(clientId, authToken, req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Successfully retrieved user bookmarks"},
|
|
||||||
Data: bookmarksData,
|
|
||||||
Meta: &paging,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Toggle Bookmark
|
|
||||||
// @Summary Toggle Bookmark (Add/Remove)
|
|
||||||
// @Description API for toggling bookmark status for an article
|
|
||||||
// @Tags Bookmarks
|
|
||||||
// @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 articleId path int true "Article ID"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /bookmarks/toggle/{articleId} [post]
|
|
||||||
func (_i *bookmarksController) ToggleBookmark(c *fiber.Ctx) error {
|
|
||||||
articleId, err := strconv.Atoi(c.Params("articleId"))
|
|
||||||
if err != nil {
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: false,
|
|
||||||
Messages: utilRes.Messages{"Invalid article ID"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
_i.Log.Info().Str("authToken", authToken).Msg("")
|
|
||||||
|
|
||||||
isBookmarked, err := _i.bookmarksService.ToggleBookmark(clientId, authToken, uint(articleId))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
message := "Bookmark removed"
|
|
||||||
if isBookmarked {
|
|
||||||
message = "Bookmark added"
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{message},
|
|
||||||
Data: map[string]interface{}{
|
|
||||||
"isBookmarked": isBookmarked,
|
|
||||||
"articleId": articleId,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Bookmark Summary
|
|
||||||
// @Summary Get Bookmark Summary for User
|
|
||||||
// @Description API for getting bookmark summary including total count and recent bookmarks
|
|
||||||
// @Tags Bookmarks
|
|
||||||
// @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 /bookmarks/summary [get]
|
|
||||||
func (_i *bookmarksController) GetBookmarkSummary(c *fiber.Ctx) error {
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
_i.Log.Info().Str("authToken", authToken).Msg("")
|
|
||||||
|
|
||||||
summaryData, err := _i.bookmarksService.GetBookmarkSummary(clientId, authToken)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Successfully retrieved bookmark summary"},
|
|
||||||
Data: summaryData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
||||||
package mapper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
"web-medols-be/app/module/bookmarks/response"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ToBookmarksResponse(bookmark *entity.Bookmarks) *response.BookmarksResponse {
|
|
||||||
return &response.BookmarksResponse{
|
|
||||||
ID: bookmark.ID,
|
|
||||||
UserId: bookmark.UserId,
|
|
||||||
ArticleId: bookmark.ArticleId,
|
|
||||||
IsActive: bookmark.IsActive,
|
|
||||||
CreatedAt: bookmark.CreatedAt,
|
|
||||||
UpdatedAt: bookmark.UpdatedAt,
|
|
||||||
Article: response.ArticleDetails{
|
|
||||||
ID: bookmark.Article.ID,
|
|
||||||
Title: bookmark.Article.Title,
|
|
||||||
Slug: bookmark.Article.Slug,
|
|
||||||
Description: bookmark.Article.Description,
|
|
||||||
HtmlDescription: bookmark.Article.HtmlDescription,
|
|
||||||
CategoryId: bookmark.Article.CategoryId,
|
|
||||||
TypeId: bookmark.Article.TypeId,
|
|
||||||
Tags: bookmark.Article.Tags,
|
|
||||||
ThumbnailUrl: getThumbnailUrl(bookmark.Article.ThumbnailPath),
|
|
||||||
PageUrl: bookmark.Article.PageUrl,
|
|
||||||
CreatedById: bookmark.Article.CreatedById,
|
|
||||||
ShareCount: bookmark.Article.ShareCount,
|
|
||||||
ViewCount: bookmark.Article.ViewCount,
|
|
||||||
CommentCount: bookmark.Article.CommentCount,
|
|
||||||
StatusId: bookmark.Article.StatusId,
|
|
||||||
IsBanner: bookmark.Article.IsBanner,
|
|
||||||
IsPublish: bookmark.Article.IsPublish,
|
|
||||||
PublishedAt: bookmark.Article.PublishedAt,
|
|
||||||
IsActive: bookmark.Article.IsActive,
|
|
||||||
CreatedAt: bookmark.Article.CreatedAt,
|
|
||||||
UpdatedAt: bookmark.Article.UpdatedAt,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToBookmarksResponseList(bookmarks []entity.Bookmarks) []*response.BookmarksResponse {
|
|
||||||
var responses []*response.BookmarksResponse
|
|
||||||
for _, bookmark := range bookmarks {
|
|
||||||
responses = append(responses, ToBookmarksResponse(&bookmark))
|
|
||||||
}
|
|
||||||
return responses
|
|
||||||
}
|
|
||||||
|
|
||||||
func getThumbnailUrl(thumbnailPath *string) string {
|
|
||||||
if thumbnailPath != nil && *thumbnailPath != "" {
|
|
||||||
return *thumbnailPath
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
@ -1,217 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"web-medols-be/app/database"
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
"web-medols-be/app/module/bookmarks/request"
|
|
||||||
"web-medols-be/utils/paginator"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type bookmarksRepository struct {
|
|
||||||
DB *database.Database
|
|
||||||
Log zerolog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// BookmarksRepository define interface of IBookmarksRepository
|
|
||||||
type BookmarksRepository interface {
|
|
||||||
GetAll(clientId *uuid.UUID, req request.BookmarksQueryRequest) (bookmarks []*entity.Bookmarks, paging paginator.Pagination, err error)
|
|
||||||
FindOne(clientId *uuid.UUID, id uint) (bookmark *entity.Bookmarks, err error)
|
|
||||||
FindByUserAndArticle(clientId *uuid.UUID, userId uint, articleId uint) (bookmark *entity.Bookmarks, err error)
|
|
||||||
Create(clientId *uuid.UUID, bookmark *entity.Bookmarks) (bookmarkReturn *entity.Bookmarks, err error)
|
|
||||||
Update(clientId *uuid.UUID, id uint, bookmark *entity.Bookmarks) (err error)
|
|
||||||
Delete(clientId *uuid.UUID, id uint) (err error)
|
|
||||||
GetByUserId(clientId *uuid.UUID, userId uint, req request.BookmarksQueryRequest) (bookmarks []*entity.Bookmarks, paging paginator.Pagination, err error)
|
|
||||||
CountByUserId(clientId *uuid.UUID, userId uint) (count int64, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewBookmarksRepository(db *database.Database, log zerolog.Logger) BookmarksRepository {
|
|
||||||
return &bookmarksRepository{
|
|
||||||
DB: db,
|
|
||||||
Log: log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// implement interface of IBookmarksRepository
|
|
||||||
func (_i *bookmarksRepository) GetAll(clientId *uuid.UUID, req request.BookmarksQueryRequest) (bookmarks []*entity.Bookmarks, paging paginator.Pagination, err error) {
|
|
||||||
var count int64
|
|
||||||
|
|
||||||
query := _i.DB.DB.Model(&entity.Bookmarks{}).Preload("User").Preload("Article")
|
|
||||||
|
|
||||||
// Apply client filter
|
|
||||||
if clientId != nil {
|
|
||||||
query = query.Where("client_id = ?", clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply filters
|
|
||||||
if req.UserId != nil {
|
|
||||||
query = query.Where("user_id = ?", *req.UserId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if req.ArticleId != nil {
|
|
||||||
query = query.Where("article_id = ?", *req.ArticleId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count total records
|
|
||||||
if err = query.Count(&count).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to count bookmarks")
|
|
||||||
return nil, paging, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply pagination
|
|
||||||
if req.Pagination != nil {
|
|
||||||
offset := (req.Pagination.Page - 1) * req.Pagination.Limit
|
|
||||||
query = query.Offset(offset).Limit(req.Pagination.Limit)
|
|
||||||
paging = *req.Pagination
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute query
|
|
||||||
if err = query.Find(&bookmarks).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to get bookmarks")
|
|
||||||
return nil, paging, err
|
|
||||||
}
|
|
||||||
|
|
||||||
paging.Count = count
|
|
||||||
paging = *paginator.Paging(&paging)
|
|
||||||
|
|
||||||
return bookmarks, paging, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksRepository) FindOne(clientId *uuid.UUID, id uint) (bookmark *entity.Bookmarks, err error) {
|
|
||||||
query := _i.DB.DB.Model(&entity.Bookmarks{}).Preload("User").Preload("Article")
|
|
||||||
|
|
||||||
// Apply client filter
|
|
||||||
if clientId != nil {
|
|
||||||
query = query.Where("client_id = ?", clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = query.Where("id = ?", id).First(&bookmark).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to find bookmark")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bookmark, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksRepository) FindByUserAndArticle(clientId *uuid.UUID, userId uint, articleId uint) (bookmark *entity.Bookmarks, err error) {
|
|
||||||
query := _i.DB.DB.Model(&entity.Bookmarks{})
|
|
||||||
|
|
||||||
// Apply client filter
|
|
||||||
if clientId != nil {
|
|
||||||
query = query.Where("client_id = ?", clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = query.Where("user_id = ? AND article_id = ?", userId, articleId).First(&bookmark).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to find bookmark by user and article")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bookmark, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksRepository) Create(clientId *uuid.UUID, bookmark *entity.Bookmarks) (bookmarkReturn *entity.Bookmarks, err error) {
|
|
||||||
bookmark.ClientId = clientId
|
|
||||||
|
|
||||||
if err = _i.DB.DB.Create(bookmark).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to create bookmark")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bookmark, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksRepository) Update(clientId *uuid.UUID, id uint, bookmark *entity.Bookmarks) (err error) {
|
|
||||||
query := _i.DB.DB.Model(&entity.Bookmarks{})
|
|
||||||
|
|
||||||
// Apply client filter
|
|
||||||
if clientId != nil {
|
|
||||||
query = query.Where("client_id = ?", clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = query.Where("id = ?", id).Updates(bookmark).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to update bookmark")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksRepository) Delete(clientId *uuid.UUID, id uint) (err error) {
|
|
||||||
query := _i.DB.DB.Model(&entity.Bookmarks{})
|
|
||||||
|
|
||||||
// Apply client filter
|
|
||||||
if clientId != nil {
|
|
||||||
query = query.Where("client_id = ?", clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = query.Where("id = ?", id).Delete(&entity.Bookmarks{}).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to delete bookmark")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksRepository) GetByUserId(clientId *uuid.UUID, userId uint, req request.BookmarksQueryRequest) (bookmarks []*entity.Bookmarks, paging paginator.Pagination, err error) {
|
|
||||||
var count int64
|
|
||||||
|
|
||||||
query := _i.DB.DB.Model(&entity.Bookmarks{}).Preload("User").Preload("Article")
|
|
||||||
|
|
||||||
// Apply client filter
|
|
||||||
if clientId != nil {
|
|
||||||
query = query.Where("client_id = ?", clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply user filter
|
|
||||||
query = query.Where("user_id = ?", userId)
|
|
||||||
|
|
||||||
// Apply additional filters
|
|
||||||
if req.ArticleId != nil {
|
|
||||||
query = query.Where("article_id = ?", *req.ArticleId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count total records
|
|
||||||
if err = query.Count(&count).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to count user bookmarks")
|
|
||||||
return nil, paging, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply pagination
|
|
||||||
if req.Pagination != nil {
|
|
||||||
offset := (req.Pagination.Page - 1) * req.Pagination.Limit
|
|
||||||
query = query.Offset(offset).Limit(req.Pagination.Limit)
|
|
||||||
paging = *req.Pagination
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute query
|
|
||||||
if err = query.Find(&bookmarks).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to get user bookmarks")
|
|
||||||
return nil, paging, err
|
|
||||||
}
|
|
||||||
|
|
||||||
paging.Count = count
|
|
||||||
paging = *paginator.Paging(&paging)
|
|
||||||
|
|
||||||
return bookmarks, paging, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksRepository) CountByUserId(clientId *uuid.UUID, userId uint) (count int64, err error) {
|
|
||||||
query := _i.DB.DB.Model(&entity.Bookmarks{})
|
|
||||||
|
|
||||||
// Apply client filter
|
|
||||||
if clientId != nil {
|
|
||||||
query = query.Where("client_id = ?", clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply user filter
|
|
||||||
query = query.Where("user_id = ?", userId)
|
|
||||||
|
|
||||||
if err = query.Count(&count).Error; err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to count user bookmarks")
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return count, nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,63 +0,0 @@
|
||||||
package request
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
"web-medols-be/utils/paginator"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BookmarksGeneric interface {
|
|
||||||
ToEntity()
|
|
||||||
}
|
|
||||||
|
|
||||||
type BookmarksQueryRequest struct {
|
|
||||||
UserId *uint `json:"userId"`
|
|
||||||
ArticleId *uint `json:"articleId"`
|
|
||||||
Pagination *paginator.Pagination `json:"pagination"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BookmarksCreateRequest struct {
|
|
||||||
ArticleId uint `json:"articleId" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (req BookmarksCreateRequest) ToEntity(userId uint) *entity.Bookmarks {
|
|
||||||
return &entity.Bookmarks{
|
|
||||||
UserId: userId,
|
|
||||||
ArticleId: req.ArticleId,
|
|
||||||
IsActive: boolPtr(true),
|
|
||||||
CreatedAt: time.Now(),
|
|
||||||
UpdatedAt: time.Now(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type BookmarksQueryRequestContext struct {
|
|
||||||
UserId string `json:"userId"`
|
|
||||||
ArticleId string `json:"articleId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (req BookmarksQueryRequestContext) ToParamRequest() BookmarksQueryRequest {
|
|
||||||
var request BookmarksQueryRequest
|
|
||||||
|
|
||||||
if userIdStr := req.UserId; userIdStr != "" {
|
|
||||||
userId, err := strconv.Atoi(userIdStr)
|
|
||||||
if err == nil {
|
|
||||||
userIdUint := uint(userId)
|
|
||||||
request.UserId = &userIdUint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if articleIdStr := req.ArticleId; articleIdStr != "" {
|
|
||||||
articleId, err := strconv.Atoi(articleIdStr)
|
|
||||||
if err == nil {
|
|
||||||
articleIdUint := uint(articleId)
|
|
||||||
request.ArticleId = &articleIdUint
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to create bool pointer
|
|
||||||
func boolPtr(b bool) *bool {
|
|
||||||
return &b
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
package response
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type BookmarksResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
UserId uint `json:"userId"`
|
|
||||||
ArticleId uint `json:"articleId"`
|
|
||||||
IsActive *bool `json:"isActive"`
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
|
||||||
|
|
||||||
// Article details
|
|
||||||
Article ArticleDetails `json:"article"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ArticleDetails struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
Title string `json:"title"`
|
|
||||||
Slug string `json:"slug"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
HtmlDescription string `json:"htmlDescription"`
|
|
||||||
CategoryId int `json:"categoryId"`
|
|
||||||
TypeId int `json:"typeId"`
|
|
||||||
Tags string `json:"tags"`
|
|
||||||
ThumbnailUrl string `json:"thumbnailUrl"`
|
|
||||||
PageUrl *string `json:"pageUrl"`
|
|
||||||
CreatedById *uint `json:"createdById"`
|
|
||||||
ShareCount *int `json:"shareCount"`
|
|
||||||
ViewCount *int `json:"viewCount"`
|
|
||||||
CommentCount *int `json:"commentCount"`
|
|
||||||
StatusId *int `json:"statusId"`
|
|
||||||
IsBanner *bool `json:"isBanner"`
|
|
||||||
IsPublish *bool `json:"isPublish"`
|
|
||||||
PublishedAt *time.Time `json:"publishedAt"`
|
|
||||||
IsActive *bool `json:"isActive"`
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type BookmarksSummaryResponse struct {
|
|
||||||
TotalBookmarks int `json:"totalBookmarks"`
|
|
||||||
RecentBookmarks []*BookmarksResponse `json:"recentBookmarks"`
|
|
||||||
}
|
|
||||||
|
|
@ -1,244 +0,0 @@
|
||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
articlesRepository "web-medols-be/app/module/articles/repository"
|
|
||||||
"web-medols-be/app/module/bookmarks/mapper"
|
|
||||||
"web-medols-be/app/module/bookmarks/repository"
|
|
||||||
"web-medols-be/app/module/bookmarks/request"
|
|
||||||
"web-medols-be/app/module/bookmarks/response"
|
|
||||||
usersRepository "web-medols-be/app/module/users/repository"
|
|
||||||
"web-medols-be/utils/paginator"
|
|
||||||
utilSvc "web-medols-be/utils/service"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
|
||||||
|
|
||||||
// BookmarksService
|
|
||||||
type bookmarksService struct {
|
|
||||||
Repo repository.BookmarksRepository
|
|
||||||
ArticlesRepo articlesRepository.ArticlesRepository
|
|
||||||
UsersRepo usersRepository.UsersRepository
|
|
||||||
Log zerolog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
// BookmarksService define interface of IBookmarksService
|
|
||||||
type BookmarksService interface {
|
|
||||||
All(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error)
|
|
||||||
Show(clientId *uuid.UUID, id uint) (bookmark *response.BookmarksResponse, err error)
|
|
||||||
Save(clientId *uuid.UUID, req request.BookmarksCreateRequest, authToken string) (bookmark *entity.Bookmarks, err error)
|
|
||||||
Delete(clientId *uuid.UUID, id uint) error
|
|
||||||
GetByUserId(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error)
|
|
||||||
ToggleBookmark(clientId *uuid.UUID, authToken string, articleId uint) (isBookmarked bool, err error)
|
|
||||||
GetBookmarkSummary(clientId *uuid.UUID, authToken string) (summary *response.BookmarksSummaryResponse, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBookmarksService init BookmarksService
|
|
||||||
func NewBookmarksService(
|
|
||||||
repo repository.BookmarksRepository,
|
|
||||||
articlesRepo articlesRepository.ArticlesRepository,
|
|
||||||
usersRepo usersRepository.UsersRepository,
|
|
||||||
log zerolog.Logger,
|
|
||||||
) BookmarksService {
|
|
||||||
return &bookmarksService{
|
|
||||||
Repo: repo,
|
|
||||||
ArticlesRepo: articlesRepo,
|
|
||||||
UsersRepo: usersRepo,
|
|
||||||
Log: log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// implement interface of IBookmarksService
|
|
||||||
func (_i *bookmarksService) All(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error) {
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
_i.Log.Error().Msg("User not found from auth token")
|
|
||||||
return nil, paging, errors.New("user not found")
|
|
||||||
}
|
|
||||||
req.UserId = &user.ID
|
|
||||||
|
|
||||||
bookmarksEntity, paging, err := _i.Repo.GetAll(clientId, req)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to get all bookmarks")
|
|
||||||
return nil, paging, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []*entity.Bookmarks to []entity.Bookmarks
|
|
||||||
var bookmarksSlice []entity.Bookmarks
|
|
||||||
for _, b := range bookmarksEntity {
|
|
||||||
bookmarksSlice = append(bookmarksSlice, *b)
|
|
||||||
}
|
|
||||||
bookmarks = mapper.ToBookmarksResponseList(bookmarksSlice)
|
|
||||||
return bookmarks, paging, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksService) Show(clientId *uuid.UUID, id uint) (bookmark *response.BookmarksResponse, err error) {
|
|
||||||
bookmarkEntity, err := _i.Repo.FindOne(clientId, id)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to show bookmark")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
bookmark = mapper.ToBookmarksResponse(bookmarkEntity)
|
|
||||||
return bookmark, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksService) Save(clientId *uuid.UUID, req request.BookmarksCreateRequest, authToken string) (bookmark *entity.Bookmarks, err error) {
|
|
||||||
// Extract user info from auth token
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
_i.Log.Error().Msg("User not found from auth token")
|
|
||||||
return nil, errors.New("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if article exists
|
|
||||||
_, err = _i.ArticlesRepo.FindOne(clientId, req.ArticleId)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Article not found")
|
|
||||||
return nil, errors.New("article not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if bookmark already exists
|
|
||||||
existingBookmark, err := _i.Repo.FindByUserAndArticle(clientId, user.ID, req.ArticleId)
|
|
||||||
if err == nil && existingBookmark != nil {
|
|
||||||
_i.Log.Error().Msg("Bookmark already exists")
|
|
||||||
return nil, errors.New("article already bookmarked")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new bookmark
|
|
||||||
bookmarkEntity := req.ToEntity(user.ID)
|
|
||||||
bookmark, err = _i.Repo.Create(clientId, bookmarkEntity)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to create bookmark")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bookmark, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksService) Delete(clientId *uuid.UUID, id uint) error {
|
|
||||||
err := _i.Repo.Delete(clientId, id)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to delete bookmark")
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksService) GetByUserId(clientId *uuid.UUID, authToken string, req request.BookmarksQueryRequest) (bookmarks []*response.BookmarksResponse, paging paginator.Pagination, err error) {
|
|
||||||
// Extract user info from auth token
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
_i.Log.Error().Msg("User not found from auth token")
|
|
||||||
return nil, paging, errors.New("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
bookmarksEntity, paging, err := _i.Repo.GetByUserId(clientId, user.ID, req)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to get bookmarks by user ID")
|
|
||||||
return nil, paging, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []*entity.Bookmarks to []entity.Bookmarks
|
|
||||||
var bookmarksSlice []entity.Bookmarks
|
|
||||||
for _, b := range bookmarksEntity {
|
|
||||||
bookmarksSlice = append(bookmarksSlice, *b)
|
|
||||||
}
|
|
||||||
bookmarks = mapper.ToBookmarksResponseList(bookmarksSlice)
|
|
||||||
return bookmarks, paging, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksService) ToggleBookmark(clientId *uuid.UUID, authToken string, articleId uint) (isBookmarked bool, err error) {
|
|
||||||
// Extract user info from auth token
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
_i.Log.Error().Msg("User not found from auth token")
|
|
||||||
return false, errors.New("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if article exists
|
|
||||||
_, err = _i.ArticlesRepo.FindOne(clientId, articleId)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Article not found")
|
|
||||||
return false, errors.New("article not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if bookmark already exists
|
|
||||||
existingBookmark, err := _i.Repo.FindByUserAndArticle(clientId, user.ID, articleId)
|
|
||||||
if err == nil && existingBookmark != nil {
|
|
||||||
// Bookmark exists, delete it
|
|
||||||
err = _i.Repo.Delete(clientId, existingBookmark.ID)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to delete existing bookmark")
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return false, nil // Bookmark removed
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bookmark doesn't exist, create it
|
|
||||||
bookmarkEntity := &entity.Bookmarks{
|
|
||||||
UserId: user.ID,
|
|
||||||
ArticleId: articleId,
|
|
||||||
IsActive: boolPtr(true),
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = _i.Repo.Create(clientId, bookmarkEntity)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to create bookmark")
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil // Bookmark added
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *bookmarksService) GetBookmarkSummary(clientId *uuid.UUID, authToken string) (summary *response.BookmarksSummaryResponse, err error) {
|
|
||||||
// Extract user info from auth token
|
|
||||||
user := utilSvc.GetUserInfo(_i.Log, _i.UsersRepo, authToken)
|
|
||||||
if user == nil {
|
|
||||||
_i.Log.Error().Msg("User not found from auth token")
|
|
||||||
return nil, errors.New("user not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get total count
|
|
||||||
totalCount, err := _i.Repo.CountByUserId(clientId, user.ID)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to count user bookmarks")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get recent bookmarks (last 5)
|
|
||||||
req := request.BookmarksQueryRequest{
|
|
||||||
Pagination: &paginator.Pagination{
|
|
||||||
Page: 1,
|
|
||||||
Limit: 5,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
recentBookmarksEntity, _, err := _i.Repo.GetByUserId(clientId, user.ID, req)
|
|
||||||
if err != nil {
|
|
||||||
_i.Log.Error().Err(err).Msg("Failed to get recent bookmarks")
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []*entity.Bookmarks to []entity.Bookmarks
|
|
||||||
var bookmarksSlice []entity.Bookmarks
|
|
||||||
for _, b := range recentBookmarksEntity {
|
|
||||||
bookmarksSlice = append(bookmarksSlice, *b)
|
|
||||||
}
|
|
||||||
recentBookmarks := mapper.ToBookmarksResponseList(bookmarksSlice)
|
|
||||||
|
|
||||||
summary = &response.BookmarksSummaryResponse{
|
|
||||||
TotalBookmarks: int(totalCount),
|
|
||||||
RecentBookmarks: recentBookmarks,
|
|
||||||
}
|
|
||||||
|
|
||||||
return summary, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to create bool pointer
|
|
||||||
func boolPtr(b bool) *bool {
|
|
||||||
return &b
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
package client_approval_settings
|
|
||||||
|
|
||||||
import (
|
|
||||||
"web-medols-be/app/module/client_approval_settings/controller"
|
|
||||||
"web-medols-be/app/module/client_approval_settings/repository"
|
|
||||||
"web-medols-be/app/module/client_approval_settings/service"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"go.uber.org/fx"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ClientApprovalSettingsRouter struct of ClientApprovalSettingsRouter
|
|
||||||
type ClientApprovalSettingsRouter struct {
|
|
||||||
App fiber.Router
|
|
||||||
Controller *controller.Controller
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClientApprovalSettingsModule register bulky of ClientApprovalSettings module
|
|
||||||
var NewClientApprovalSettingsModule = fx.Options(
|
|
||||||
// register repository of ClientApprovalSettings module
|
|
||||||
fx.Provide(repository.NewClientApprovalSettingsRepository),
|
|
||||||
|
|
||||||
// register service of ClientApprovalSettings module
|
|
||||||
fx.Provide(service.NewClientApprovalSettingsService),
|
|
||||||
|
|
||||||
// register controller of ClientApprovalSettings module
|
|
||||||
fx.Provide(controller.NewController),
|
|
||||||
|
|
||||||
// register router of ClientApprovalSettings module
|
|
||||||
fx.Provide(NewClientApprovalSettingsRouter),
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewClientApprovalSettingsRouter init ClientApprovalSettingsRouter
|
|
||||||
func NewClientApprovalSettingsRouter(fiber *fiber.App, controller *controller.Controller) *ClientApprovalSettingsRouter {
|
|
||||||
return &ClientApprovalSettingsRouter{
|
|
||||||
App: fiber,
|
|
||||||
Controller: controller,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegisterClientApprovalSettingsRoutes register routes of ClientApprovalSettings
|
|
||||||
func (_i *ClientApprovalSettingsRouter) RegisterClientApprovalSettingsRoutes() {
|
|
||||||
// define controllers
|
|
||||||
clientApprovalSettingsController := _i.Controller.ClientApprovalSettings
|
|
||||||
|
|
||||||
// define routes
|
|
||||||
_i.App.Route("/client-approval-settings", func(router fiber.Router) {
|
|
||||||
// Basic CRUD routes
|
|
||||||
router.Post("/", clientApprovalSettingsController.CreateSettings)
|
|
||||||
router.Get("/", clientApprovalSettingsController.GetSettings)
|
|
||||||
router.Put("/", clientApprovalSettingsController.UpdateSettings)
|
|
||||||
router.Delete("/", clientApprovalSettingsController.DeleteSettings)
|
|
||||||
|
|
||||||
// Approval management routes
|
|
||||||
router.Post("/toggle-approval", clientApprovalSettingsController.ToggleApproval)
|
|
||||||
router.Post("/enable-approval", clientApprovalSettingsController.EnableApproval)
|
|
||||||
router.Post("/disable-approval", clientApprovalSettingsController.DisableApproval)
|
|
||||||
router.Put("/default-workflow", clientApprovalSettingsController.SetDefaultWorkflow)
|
|
||||||
|
|
||||||
// Exemption management routes
|
|
||||||
router.Post("/exempt-users", clientApprovalSettingsController.ManageExemptUsers)
|
|
||||||
router.Post("/exempt-roles", clientApprovalSettingsController.ManageExemptRoles)
|
|
||||||
router.Post("/exempt-categories", clientApprovalSettingsController.ManageExemptCategories)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,430 +0,0 @@
|
||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strconv"
|
|
||||||
"web-medols-be/app/middleware"
|
|
||||||
"web-medols-be/app/module/client_approval_settings/request"
|
|
||||||
"web-medols-be/app/module/client_approval_settings/service"
|
|
||||||
utilRes "web-medols-be/utils/response"
|
|
||||||
utilVal "web-medols-be/utils/validator"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientApprovalSettingsController struct {
|
|
||||||
clientApprovalSettingsService service.ClientApprovalSettingsService
|
|
||||||
Log zerolog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientApprovalSettingsController interface {
|
|
||||||
CreateSettings(c *fiber.Ctx) error
|
|
||||||
GetSettings(c *fiber.Ctx) error
|
|
||||||
UpdateSettings(c *fiber.Ctx) error
|
|
||||||
DeleteSettings(c *fiber.Ctx) error
|
|
||||||
ToggleApproval(c *fiber.Ctx) error
|
|
||||||
EnableApproval(c *fiber.Ctx) error
|
|
||||||
DisableApproval(c *fiber.Ctx) error
|
|
||||||
SetDefaultWorkflow(c *fiber.Ctx) error
|
|
||||||
ManageExemptUsers(c *fiber.Ctx) error
|
|
||||||
ManageExemptRoles(c *fiber.Ctx) error
|
|
||||||
ManageExemptCategories(c *fiber.Ctx) error
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClientApprovalSettingsController(
|
|
||||||
clientApprovalSettingsService service.ClientApprovalSettingsService,
|
|
||||||
log zerolog.Logger,
|
|
||||||
) ClientApprovalSettingsController {
|
|
||||||
return &clientApprovalSettingsController{
|
|
||||||
clientApprovalSettingsService: clientApprovalSettingsService,
|
|
||||||
Log: log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateSettings ClientApprovalSettings
|
|
||||||
// @Summary Create Client Approval Settings
|
|
||||||
// @Description API for creating client approval settings
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param payload body request.CreateClientApprovalSettingsRequest true "Required payload"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /client-approval-settings [post]
|
|
||||||
func (_i *clientApprovalSettingsController) CreateSettings(c *fiber.Ctx) error {
|
|
||||||
req := new(request.CreateClientApprovalSettingsRequest)
|
|
||||||
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
settings, err := _i.clientApprovalSettingsService.Create(clientId, *req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Client approval settings created successfully"},
|
|
||||||
Data: settings,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSettings ClientApprovalSettings
|
|
||||||
// @Summary Get Client Approval Settings
|
|
||||||
// @Description API for getting client approval settings
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @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 /client-approval-settings [get]
|
|
||||||
func (_i *clientApprovalSettingsController) GetSettings(c *fiber.Ctx) error {
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
settings, err := _i.clientApprovalSettingsService.GetByClientId(clientId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Client approval settings successfully retrieved"},
|
|
||||||
Data: settings,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateSettings ClientApprovalSettings
|
|
||||||
// @Summary Update Client Approval Settings
|
|
||||||
// @Description API for updating client approval settings
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param payload body request.UpdateClientApprovalSettingsRequest true "Required payload"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /client-approval-settings [put]
|
|
||||||
func (_i *clientApprovalSettingsController) UpdateSettings(c *fiber.Ctx) error {
|
|
||||||
req := new(request.UpdateClientApprovalSettingsRequest)
|
|
||||||
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
settings, err := _i.clientApprovalSettingsService.Update(clientId, *req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Client approval settings successfully updated"},
|
|
||||||
Data: settings,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteSettings ClientApprovalSettings
|
|
||||||
// @Summary Delete Client Approval Settings
|
|
||||||
// @Description API for deleting client approval settings
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @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 /client-approval-settings [delete]
|
|
||||||
func (_i *clientApprovalSettingsController) DeleteSettings(c *fiber.Ctx) error {
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
err := _i.clientApprovalSettingsService.Delete(clientId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Client approval settings successfully deleted"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToggleApproval ClientApprovalSettings
|
|
||||||
// @Summary Toggle Approval Requirement
|
|
||||||
// @Description API for toggling approval requirement on/off
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param payload body request.ToggleApprovalRequest true "Required payload"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /client-approval-settings/toggle [post]
|
|
||||||
func (_i *clientApprovalSettingsController) ToggleApproval(c *fiber.Ctx) error {
|
|
||||||
req := new(request.ToggleApprovalRequest)
|
|
||||||
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
err := _i.clientApprovalSettingsService.ToggleApprovalRequirement(clientId, req.RequiresApproval)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
action := "enabled"
|
|
||||||
if !req.RequiresApproval {
|
|
||||||
action = "disabled with auto-publish"
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{fmt.Sprintf("Approval system successfully %s", action)},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableApproval ClientApprovalSettings
|
|
||||||
// @Summary Enable Approval System
|
|
||||||
// @Description API for enabling approval system with smooth transition
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param payload body request.EnableApprovalRequest true "Required payload"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /client-approval-settings/enable [post]
|
|
||||||
func (_i *clientApprovalSettingsController) EnableApproval(c *fiber.Ctx) error {
|
|
||||||
req := new(request.EnableApprovalRequest)
|
|
||||||
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
err := _i.clientApprovalSettingsService.EnableApprovalWithTransition(clientId, req.DefaultWorkflowId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Approval system successfully enabled with smooth transition"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableApproval ClientApprovalSettings
|
|
||||||
// @Summary Disable Approval System
|
|
||||||
// @Description API for disabling approval system and auto-publish pending articles
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param payload body request.DisableApprovalRequest true "Required payload"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /client-approval-settings/disable [post]
|
|
||||||
func (_i *clientApprovalSettingsController) DisableApproval(c *fiber.Ctx) error {
|
|
||||||
req := new(request.DisableApprovalRequest)
|
|
||||||
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
err := _i.clientApprovalSettingsService.DisableApprovalWithAutoPublish(clientId, req.Reason)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Approval system successfully disabled with auto-publish enabled"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDefaultWorkflow ClientApprovalSettings
|
|
||||||
// @Summary Set Default Workflow
|
|
||||||
// @Description API for setting default workflow for client
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param payload body request.SetDefaultWorkflowRequest true "Required payload"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /client-approval-settings/default-workflow [post]
|
|
||||||
func (_i *clientApprovalSettingsController) SetDefaultWorkflow(c *fiber.Ctx) error {
|
|
||||||
req := new(request.SetDefaultWorkflowRequest)
|
|
||||||
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
err := _i.clientApprovalSettingsService.SetDefaultWorkflow(clientId, req.WorkflowId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Default workflow successfully set"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ManageExemptUsers ClientApprovalSettings
|
|
||||||
// @Summary Manage Exempt Users
|
|
||||||
// @Description API for adding/removing users from approval exemption
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param action path string true "Action: add or remove"
|
|
||||||
// @Param user_id path int true "User ID"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /client-approval-settings/exempt-users/{action}/{user_id} [post]
|
|
||||||
func (_i *clientApprovalSettingsController) ManageExemptUsers(c *fiber.Ctx) error {
|
|
||||||
action := c.Params("action")
|
|
||||||
userIdStr := c.Params("user_id")
|
|
||||||
|
|
||||||
if action != "add" && action != "remove" {
|
|
||||||
return utilRes.ErrorBadRequest(c, "Invalid action. Use 'add' or 'remove'")
|
|
||||||
}
|
|
||||||
|
|
||||||
userId, err := strconv.Atoi(userIdStr)
|
|
||||||
if err != nil {
|
|
||||||
return utilRes.ErrorBadRequest(c, "Invalid user ID format")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
if action == "add" {
|
|
||||||
err = _i.clientApprovalSettingsService.AddExemptUser(clientId, uint(userId))
|
|
||||||
} else {
|
|
||||||
err = _i.clientApprovalSettingsService.RemoveExemptUser(clientId, uint(userId))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{fmt.Sprintf("User successfully %sd from approval exemption", action)},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ManageExemptRoles ClientApprovalSettings
|
|
||||||
// @Summary Manage Exempt Roles
|
|
||||||
// @Description API for adding/removing roles from approval exemption
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param action path string true "Action: add or remove"
|
|
||||||
// @Param role_id 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 /client-approval-settings/exempt-roles/{action}/{role_id} [post]
|
|
||||||
func (_i *clientApprovalSettingsController) ManageExemptRoles(c *fiber.Ctx) error {
|
|
||||||
action := c.Params("action")
|
|
||||||
roleIdStr := c.Params("role_id")
|
|
||||||
|
|
||||||
if action != "add" && action != "remove" {
|
|
||||||
return utilRes.ErrorBadRequest(c, "Invalid action. Use 'add' or 'remove'")
|
|
||||||
}
|
|
||||||
|
|
||||||
roleId, err := strconv.Atoi(roleIdStr)
|
|
||||||
if err != nil {
|
|
||||||
return utilRes.ErrorBadRequest(c, "Invalid role ID format")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
if action == "add" {
|
|
||||||
err = _i.clientApprovalSettingsService.AddExemptRole(clientId, uint(roleId))
|
|
||||||
} else {
|
|
||||||
err = _i.clientApprovalSettingsService.RemoveExemptRole(clientId, uint(roleId))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{fmt.Sprintf("Role successfully %sd from approval exemption", action)},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// ManageExemptCategories ClientApprovalSettings
|
|
||||||
// @Summary Manage Exempt Categories
|
|
||||||
// @Description API for adding/removing categories from approval exemption
|
|
||||||
// @Tags ClientApprovalSettings
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string true "Insert the X-Client-Key"
|
|
||||||
// @Param action path string true "Action: add or remove"
|
|
||||||
// @Param category_id path int true "Category ID"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /client-approval-settings/exempt-categories/{action}/{category_id} [post]
|
|
||||||
func (_i *clientApprovalSettingsController) ManageExemptCategories(c *fiber.Ctx) error {
|
|
||||||
action := c.Params("action")
|
|
||||||
categoryIdStr := c.Params("category_id")
|
|
||||||
|
|
||||||
if action != "add" && action != "remove" {
|
|
||||||
return utilRes.ErrorBadRequest(c, "Invalid action. Use 'add' or 'remove'")
|
|
||||||
}
|
|
||||||
|
|
||||||
categoryId, err := strconv.Atoi(categoryIdStr)
|
|
||||||
if err != nil {
|
|
||||||
return utilRes.ErrorBadRequest(c, "Invalid category ID format")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
if action == "add" {
|
|
||||||
err = _i.clientApprovalSettingsService.AddExemptCategory(clientId, uint(categoryId))
|
|
||||||
} else {
|
|
||||||
err = _i.clientApprovalSettingsService.RemoveExemptCategory(clientId, uint(categoryId))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{fmt.Sprintf("Category successfully %sd from approval exemption", action)},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"web-medols-be/app/module/client_approval_settings/service"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Controller struct {
|
|
||||||
ClientApprovalSettings ClientApprovalSettingsController
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewController(ClientApprovalSettingsService service.ClientApprovalSettingsService, log zerolog.Logger) *Controller {
|
|
||||||
return &Controller{
|
|
||||||
ClientApprovalSettings: NewClientApprovalSettingsController(ClientApprovalSettingsService, log),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,104 +0,0 @@
|
||||||
package mapper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
res "web-medols-be/app/module/client_approval_settings/response"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ClientApprovalSettingsResponseMapper(
|
|
||||||
log zerolog.Logger,
|
|
||||||
clientId *uuid.UUID,
|
|
||||||
settings *entity.ClientApprovalSettings,
|
|
||||||
) *res.ClientApprovalSettingsResponse {
|
|
||||||
if settings == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &res.ClientApprovalSettingsResponse{
|
|
||||||
ID: settings.ID,
|
|
||||||
ClientId: settings.ClientId.String(),
|
|
||||||
RequiresApproval: *settings.RequiresApproval,
|
|
||||||
DefaultWorkflowId: settings.DefaultWorkflowId,
|
|
||||||
AutoPublishArticles: *settings.AutoPublishArticles,
|
|
||||||
ApprovalExemptUsers: settings.ApprovalExemptUsers,
|
|
||||||
ApprovalExemptRoles: settings.ApprovalExemptRoles,
|
|
||||||
ApprovalExemptCategories: settings.ApprovalExemptCategories,
|
|
||||||
RequireApprovalFor: settings.RequireApprovalFor,
|
|
||||||
SkipApprovalFor: settings.SkipApprovalFor,
|
|
||||||
IsActive: *settings.IsActive,
|
|
||||||
CreatedAt: settings.CreatedAt,
|
|
||||||
UpdatedAt: settings.UpdatedAt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ClientApprovalSettingsDetailResponseMapper(
|
|
||||||
log zerolog.Logger,
|
|
||||||
clientId *uuid.UUID,
|
|
||||||
settings *entity.ClientApprovalSettings,
|
|
||||||
) *res.ClientApprovalSettingsDetailResponse {
|
|
||||||
if settings == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
response := &res.ClientApprovalSettingsDetailResponse{
|
|
||||||
ID: settings.ID,
|
|
||||||
ClientId: settings.ClientId.String(),
|
|
||||||
RequiresApproval: *settings.RequiresApproval,
|
|
||||||
DefaultWorkflowId: settings.DefaultWorkflowId,
|
|
||||||
AutoPublishArticles: *settings.AutoPublishArticles,
|
|
||||||
ApprovalExemptUsers: settings.ApprovalExemptUsers,
|
|
||||||
ApprovalExemptRoles: settings.ApprovalExemptRoles,
|
|
||||||
ApprovalExemptCategories: settings.ApprovalExemptCategories,
|
|
||||||
RequireApprovalFor: settings.RequireApprovalFor,
|
|
||||||
SkipApprovalFor: settings.SkipApprovalFor,
|
|
||||||
IsActive: *settings.IsActive,
|
|
||||||
CreatedAt: settings.CreatedAt,
|
|
||||||
UpdatedAt: settings.UpdatedAt,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add client relation if available
|
|
||||||
if settings.Client.ID != uuid.Nil {
|
|
||||||
response.Client = &res.ClientResponse{
|
|
||||||
ID: 1, // Placeholder - would need proper ID mapping
|
|
||||||
Name: settings.Client.Name,
|
|
||||||
IsActive: *settings.Client.IsActive,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add workflow relation if available
|
|
||||||
if settings.Workflow != nil && settings.Workflow.ID != 0 {
|
|
||||||
response.Workflow = &res.WorkflowResponse{
|
|
||||||
ID: settings.Workflow.ID,
|
|
||||||
Name: settings.Workflow.Name,
|
|
||||||
Description: settings.Workflow.Description,
|
|
||||||
IsActive: *settings.Workflow.IsActive,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
func ApprovalStatusResponseMapper(
|
|
||||||
log zerolog.Logger,
|
|
||||||
clientId *uuid.UUID,
|
|
||||||
settings *entity.ClientApprovalSettings,
|
|
||||||
) *res.ApprovalStatusResponse {
|
|
||||||
if settings == nil {
|
|
||||||
return &res.ApprovalStatusResponse{
|
|
||||||
RequiresApproval: true, // Default to requiring approval
|
|
||||||
AutoPublishArticles: false,
|
|
||||||
IsActive: false,
|
|
||||||
Message: "No approval settings found",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &res.ApprovalStatusResponse{
|
|
||||||
RequiresApproval: *settings.RequiresApproval,
|
|
||||||
AutoPublishArticles: *settings.AutoPublishArticles,
|
|
||||||
IsActive: *settings.IsActive,
|
|
||||||
Message: "Approval settings loaded successfully",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ClientApprovalSettingsRepository interface {
|
|
||||||
// Basic CRUD
|
|
||||||
Create(clientId *uuid.UUID, settings *entity.ClientApprovalSettings) (*entity.ClientApprovalSettings, error)
|
|
||||||
FindOne(clientId *uuid.UUID) (*entity.ClientApprovalSettings, error)
|
|
||||||
Update(clientId *uuid.UUID, settings *entity.ClientApprovalSettings) (*entity.ClientApprovalSettings, error)
|
|
||||||
Delete(clientId *uuid.UUID) error
|
|
||||||
|
|
||||||
// Specific queries
|
|
||||||
FindByClientId(clientId uuid.UUID) (*entity.ClientApprovalSettings, error)
|
|
||||||
FindActiveSettings(clientId *uuid.UUID) (*entity.ClientApprovalSettings, error)
|
|
||||||
FindByWorkflowId(workflowId uint) ([]*entity.ClientApprovalSettings, error)
|
|
||||||
|
|
||||||
// Exemption management
|
|
||||||
AddExemptUser(clientId *uuid.UUID, userId uint) error
|
|
||||||
RemoveExemptUser(clientId *uuid.UUID, userId uint) error
|
|
||||||
AddExemptRole(clientId *uuid.UUID, roleId uint) error
|
|
||||||
RemoveExemptRole(clientId *uuid.UUID, roleId uint) error
|
|
||||||
AddExemptCategory(clientId *uuid.UUID, categoryId uint) error
|
|
||||||
RemoveExemptCategory(clientId *uuid.UUID, categoryId uint) error
|
|
||||||
|
|
||||||
// Bulk operations
|
|
||||||
BulkUpdateExemptUsers(clientId *uuid.UUID, userIds []uint) error
|
|
||||||
BulkUpdateExemptRoles(clientId *uuid.UUID, roleIds []uint) error
|
|
||||||
BulkUpdateExemptCategories(clientId *uuid.UUID, categoryIds []uint) error
|
|
||||||
}
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"web-medols-be/app/database"
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientApprovalSettingsRepository struct {
|
|
||||||
DB *database.Database
|
|
||||||
Log zerolog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClientApprovalSettingsRepository(db *database.Database, log zerolog.Logger) ClientApprovalSettingsRepository {
|
|
||||||
return &clientApprovalSettingsRepository{
|
|
||||||
DB: db,
|
|
||||||
Log: log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) Create(clientId *uuid.UUID, settings *entity.ClientApprovalSettings) (*entity.ClientApprovalSettings, error) {
|
|
||||||
settings.ClientId = *clientId
|
|
||||||
if err := r.DB.DB.Create(settings).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) FindOne(clientId *uuid.UUID) (*entity.ClientApprovalSettings, error) {
|
|
||||||
var settings entity.ClientApprovalSettings
|
|
||||||
err := r.DB.DB.Where("client_id = ?", clientId).First(&settings).Error
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &settings, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) Update(clientId *uuid.UUID, settings *entity.ClientApprovalSettings) (*entity.ClientApprovalSettings, error) {
|
|
||||||
settings.ClientId = *clientId
|
|
||||||
if err := r.DB.DB.Where("client_id = ?", clientId).Save(settings).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return settings, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) Delete(clientId *uuid.UUID) error {
|
|
||||||
return r.DB.DB.Where("client_id = ?", clientId).Delete(&entity.ClientApprovalSettings{}).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) FindByClientId(clientId uuid.UUID) (*entity.ClientApprovalSettings, error) {
|
|
||||||
var settings entity.ClientApprovalSettings
|
|
||||||
err := r.DB.DB.Where("client_id = ?", clientId).First(&settings).Error
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &settings, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) FindActiveSettings(clientId *uuid.UUID) (*entity.ClientApprovalSettings, error) {
|
|
||||||
var settings entity.ClientApprovalSettings
|
|
||||||
err := r.DB.DB.Where("client_id = ? AND is_active = ?", clientId, true).First(&settings).Error
|
|
||||||
if err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &settings, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) FindByWorkflowId(workflowId uint) ([]*entity.ClientApprovalSettings, error) {
|
|
||||||
var settings []*entity.ClientApprovalSettings
|
|
||||||
err := r.DB.DB.Where("default_workflow_id = ?", workflowId).Find(&settings).Error
|
|
||||||
return settings, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) AddExemptUser(clientId *uuid.UUID, userId uint) error {
|
|
||||||
return r.DB.DB.Model(&entity.ClientApprovalSettings{}).
|
|
||||||
Where("client_id = ?", clientId).
|
|
||||||
Update("approval_exempt_users", gorm.Expr("array_append(approval_exempt_users, ?)", userId)).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) RemoveExemptUser(clientId *uuid.UUID, userId uint) error {
|
|
||||||
return r.DB.DB.Model(&entity.ClientApprovalSettings{}).
|
|
||||||
Where("client_id = ?", clientId).
|
|
||||||
Update("approval_exempt_users", gorm.Expr("array_remove(approval_exempt_users, ?)", userId)).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) AddExemptRole(clientId *uuid.UUID, roleId uint) error {
|
|
||||||
return r.DB.DB.Model(&entity.ClientApprovalSettings{}).
|
|
||||||
Where("client_id = ?", clientId).
|
|
||||||
Update("approval_exempt_roles", gorm.Expr("array_append(approval_exempt_roles, ?)", roleId)).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) RemoveExemptRole(clientId *uuid.UUID, roleId uint) error {
|
|
||||||
return r.DB.DB.Model(&entity.ClientApprovalSettings{}).
|
|
||||||
Where("client_id = ?", clientId).
|
|
||||||
Update("approval_exempt_roles", gorm.Expr("array_remove(approval_exempt_roles, ?)", roleId)).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) AddExemptCategory(clientId *uuid.UUID, categoryId uint) error {
|
|
||||||
return r.DB.DB.Model(&entity.ClientApprovalSettings{}).
|
|
||||||
Where("client_id = ?", clientId).
|
|
||||||
Update("approval_exempt_categories", gorm.Expr("array_append(approval_exempt_categories, ?)", categoryId)).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) RemoveExemptCategory(clientId *uuid.UUID, categoryId uint) error {
|
|
||||||
return r.DB.DB.Model(&entity.ClientApprovalSettings{}).
|
|
||||||
Where("client_id = ?", clientId).
|
|
||||||
Update("approval_exempt_categories", gorm.Expr("array_remove(approval_exempt_categories, ?)", categoryId)).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) BulkUpdateExemptUsers(clientId *uuid.UUID, userIds []uint) error {
|
|
||||||
return r.DB.DB.Model(&entity.ClientApprovalSettings{}).
|
|
||||||
Where("client_id = ?", clientId).
|
|
||||||
Update("approval_exempt_users", userIds).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) BulkUpdateExemptRoles(clientId *uuid.UUID, roleIds []uint) error {
|
|
||||||
return r.DB.DB.Model(&entity.ClientApprovalSettings{}).
|
|
||||||
Where("client_id = ?", clientId).
|
|
||||||
Update("approval_exempt_roles", roleIds).Error
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *clientApprovalSettingsRepository) BulkUpdateExemptCategories(clientId *uuid.UUID, categoryIds []uint) error {
|
|
||||||
return r.DB.DB.Model(&entity.ClientApprovalSettings{}).
|
|
||||||
Where("client_id = ?", clientId).
|
|
||||||
Update("approval_exempt_categories", categoryIds).Error
|
|
||||||
}
|
|
||||||
|
|
@ -1,85 +0,0 @@
|
||||||
package request
|
|
||||||
|
|
||||||
// CreateClientApprovalSettingsRequest represents request for creating client approval settings
|
|
||||||
type CreateClientApprovalSettingsRequest struct {
|
|
||||||
RequiresApproval bool `json:"requiresApproval"`
|
|
||||||
DefaultWorkflowId *uint `json:"defaultWorkflowId" validate:"omitempty,min=1"`
|
|
||||||
AutoPublishArticles bool `json:"autoPublishArticles"`
|
|
||||||
ApprovalExemptUsers []uint `json:"approvalExemptUsers" validate:"omitempty,dive,min=1"`
|
|
||||||
ApprovalExemptRoles []uint `json:"approvalExemptRoles" validate:"omitempty,dive,min=1"`
|
|
||||||
ApprovalExemptCategories []uint `json:"approvalExemptCategories" validate:"omitempty,dive,min=1"`
|
|
||||||
RequireApprovalFor []string `json:"requireApprovalFor" validate:"omitempty,dive,min=1"`
|
|
||||||
SkipApprovalFor []string `json:"skipApprovalFor" validate:"omitempty,dive,min=1"`
|
|
||||||
IsActive bool `json:"isActive"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateClientApprovalSettingsRequest represents request for updating client approval settings
|
|
||||||
type UpdateClientApprovalSettingsRequest struct {
|
|
||||||
RequiresApproval *bool `json:"requiresApproval"`
|
|
||||||
DefaultWorkflowId **uint `json:"defaultWorkflowId"` // double pointer to allow nil
|
|
||||||
AutoPublishArticles *bool `json:"autoPublishArticles"`
|
|
||||||
ApprovalExemptUsers []uint `json:"approvalExemptUsers" validate:"omitempty,dive,min=1"`
|
|
||||||
ApprovalExemptRoles []uint `json:"approvalExemptRoles" validate:"omitempty,dive,min=1"`
|
|
||||||
ApprovalExemptCategories []uint `json:"approvalExemptCategories" validate:"omitempty,dive,min=1"`
|
|
||||||
RequireApprovalFor []string `json:"requireApprovalFor" validate:"omitempty,dive,min=1"`
|
|
||||||
SkipApprovalFor []string `json:"skipApprovalFor" validate:"omitempty,dive,min=1"`
|
|
||||||
IsActive *bool `json:"isActive"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToggleApprovalRequest represents request for toggling approval requirement
|
|
||||||
type ToggleApprovalRequest struct {
|
|
||||||
RequiresApproval bool `json:"requiresApproval"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// EnableApprovalRequest represents request for enabling approval with smooth transition
|
|
||||||
type EnableApprovalRequest struct {
|
|
||||||
DefaultWorkflowId *uint `json:"defaultWorkflowId" validate:"omitempty,min=1"`
|
|
||||||
Reason string `json:"reason" validate:"omitempty,max=500"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DisableApprovalRequest represents request for disabling approval system
|
|
||||||
type DisableApprovalRequest struct {
|
|
||||||
Reason string `json:"reason" validate:"required,max=500"`
|
|
||||||
HandleAction string `json:"handleAction" validate:"required,oneof=auto_approve keep_pending reset_to_draft"` // How to handle pending articles
|
|
||||||
}
|
|
||||||
|
|
||||||
// SetDefaultWorkflowRequest represents request for setting default workflow
|
|
||||||
type SetDefaultWorkflowRequest struct {
|
|
||||||
WorkflowId *uint `json:"workflowId" validate:"omitempty,min=1"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddExemptUserRequest represents request for adding user to exemption
|
|
||||||
type AddExemptUserRequest struct {
|
|
||||||
UserId uint `json:"userId" validate:"required,min=1"`
|
|
||||||
Reason string `json:"reason" validate:"omitempty,max=500"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveExemptUserRequest represents request for removing user from exemption
|
|
||||||
type RemoveExemptUserRequest struct {
|
|
||||||
UserId uint `json:"userId" validate:"required,min=1"`
|
|
||||||
Reason string `json:"reason" validate:"omitempty,max=500"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddExemptRoleRequest represents request for adding role to exemption
|
|
||||||
type AddExemptRoleRequest struct {
|
|
||||||
RoleId uint `json:"roleId" validate:"required,min=1"`
|
|
||||||
Reason string `json:"reason" validate:"omitempty,max=500"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveExemptRoleRequest represents request for removing role from exemption
|
|
||||||
type RemoveExemptRoleRequest struct {
|
|
||||||
RoleId uint `json:"roleId" validate:"required,min=1"`
|
|
||||||
Reason string `json:"reason" validate:"omitempty,max=500"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddExemptCategoryRequest represents request for adding category to exemption
|
|
||||||
type AddExemptCategoryRequest struct {
|
|
||||||
CategoryId uint `json:"categoryId" validate:"required,min=1"`
|
|
||||||
Reason string `json:"reason" validate:"omitempty,max=500"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveExemptCategoryRequest represents request for removing category from exemption
|
|
||||||
type RemoveExemptCategoryRequest struct {
|
|
||||||
CategoryId uint `json:"categoryId" validate:"required,min=1"`
|
|
||||||
Reason string `json:"reason" validate:"omitempty,max=500"`
|
|
||||||
}
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
package response
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ClientApprovalSettingsResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
ClientId string `json:"clientId"`
|
|
||||||
RequiresApproval bool `json:"requiresApproval"`
|
|
||||||
DefaultWorkflowId *uint `json:"defaultWorkflowId,omitempty"`
|
|
||||||
AutoPublishArticles bool `json:"autoPublishArticles"`
|
|
||||||
ApprovalExemptUsers []uint `json:"approvalExemptUsers"`
|
|
||||||
ApprovalExemptRoles []uint `json:"approvalExemptRoles"`
|
|
||||||
ApprovalExemptCategories []uint `json:"approvalExemptCategories"`
|
|
||||||
RequireApprovalFor []string `json:"requireApprovalFor"`
|
|
||||||
SkipApprovalFor []string `json:"skipApprovalFor"`
|
|
||||||
IsActive bool `json:"isActive"`
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientApprovalSettingsDetailResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
ClientId string `json:"clientId"`
|
|
||||||
RequiresApproval bool `json:"requiresApproval"`
|
|
||||||
DefaultWorkflowId *uint `json:"defaultWorkflowId,omitempty"`
|
|
||||||
AutoPublishArticles bool `json:"autoPublishArticles"`
|
|
||||||
ApprovalExemptUsers []uint `json:"approvalExemptUsers"`
|
|
||||||
ApprovalExemptRoles []uint `json:"approvalExemptRoles"`
|
|
||||||
ApprovalExemptCategories []uint `json:"approvalExemptCategories"`
|
|
||||||
RequireApprovalFor []string `json:"requireApprovalFor"`
|
|
||||||
SkipApprovalFor []string `json:"skipApprovalFor"`
|
|
||||||
IsActive bool `json:"isActive"`
|
|
||||||
CreatedAt time.Time `json:"createdAt"`
|
|
||||||
UpdatedAt time.Time `json:"updatedAt"`
|
|
||||||
|
|
||||||
// Relations
|
|
||||||
Client *ClientResponse `json:"client,omitempty"`
|
|
||||||
Workflow *WorkflowResponse `json:"workflow,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
IsActive bool `json:"isActive"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type WorkflowResponse struct {
|
|
||||||
ID uint `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description *string `json:"description,omitempty"`
|
|
||||||
IsActive bool `json:"isActive"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApprovalStatusResponse struct {
|
|
||||||
RequiresApproval bool `json:"requiresApproval"`
|
|
||||||
AutoPublishArticles bool `json:"autoPublishArticles"`
|
|
||||||
IsActive bool `json:"isActive"`
|
|
||||||
Message string `json:"message,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExemptUserResponse struct {
|
|
||||||
UserId uint `json:"userId"`
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExemptRoleResponse struct {
|
|
||||||
RoleId uint `json:"roleId"`
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExemptCategoryResponse struct {
|
|
||||||
CategoryId uint `json:"categoryId"`
|
|
||||||
Reason string `json:"reason,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApprovalTransitionResponse struct {
|
|
||||||
Success bool `json:"success"`
|
|
||||||
Message string `json:"message"`
|
|
||||||
AffectedArticles int `json:"affectedArticles,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
@ -1,326 +0,0 @@
|
||||||
package service
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
"web-medols-be/app/module/client_approval_settings/mapper"
|
|
||||||
"web-medols-be/app/module/client_approval_settings/repository"
|
|
||||||
"web-medols-be/app/module/client_approval_settings/request"
|
|
||||||
"web-medols-be/app/module/client_approval_settings/response"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
|
||||||
|
|
||||||
type clientApprovalSettingsService struct {
|
|
||||||
clientApprovalSettingsRepo repository.ClientApprovalSettingsRepository
|
|
||||||
Log zerolog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientApprovalSettingsService interface {
|
|
||||||
GetByClientId(clientId *uuid.UUID) (*response.ClientApprovalSettingsResponse, error)
|
|
||||||
Create(clientId *uuid.UUID, req request.CreateClientApprovalSettingsRequest) (*response.ClientApprovalSettingsResponse, error)
|
|
||||||
Update(clientId *uuid.UUID, req request.UpdateClientApprovalSettingsRequest) (*response.ClientApprovalSettingsResponse, error)
|
|
||||||
Delete(clientId *uuid.UUID) error
|
|
||||||
ToggleApprovalRequirement(clientId *uuid.UUID, requiresApproval bool) error
|
|
||||||
SetDefaultWorkflow(clientId *uuid.UUID, workflowId *uint) error
|
|
||||||
AddExemptUser(clientId *uuid.UUID, userId uint) error
|
|
||||||
RemoveExemptUser(clientId *uuid.UUID, userId uint) error
|
|
||||||
AddExemptRole(clientId *uuid.UUID, roleId uint) error
|
|
||||||
RemoveExemptRole(clientId *uuid.UUID, roleId uint) error
|
|
||||||
AddExemptCategory(clientId *uuid.UUID, categoryId uint) error
|
|
||||||
RemoveExemptCategory(clientId *uuid.UUID, categoryId uint) error
|
|
||||||
CheckIfApprovalRequired(clientId *uuid.UUID, userId uint, userLevelId uint, categoryId uint, contentType string) (bool, error)
|
|
||||||
|
|
||||||
// Enhanced methods for dynamic approval management
|
|
||||||
EnableApprovalWithTransition(clientId *uuid.UUID, defaultWorkflowId *uint) error
|
|
||||||
DisableApprovalWithAutoPublish(clientId *uuid.UUID, reason string) error
|
|
||||||
HandlePendingApprovalsOnDisable(clientId *uuid.UUID, action string) error // "auto_approve", "keep_pending", "reset_to_draft"
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewClientApprovalSettingsService(
|
|
||||||
clientApprovalSettingsRepo repository.ClientApprovalSettingsRepository,
|
|
||||||
log zerolog.Logger,
|
|
||||||
) ClientApprovalSettingsService {
|
|
||||||
return &clientApprovalSettingsService{
|
|
||||||
clientApprovalSettingsRepo: clientApprovalSettingsRepo,
|
|
||||||
Log: log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) GetByClientId(clientId *uuid.UUID) (*response.ClientApprovalSettingsResponse, error) {
|
|
||||||
settings, err := _i.clientApprovalSettingsRepo.FindByClientId(*clientId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if settings == nil {
|
|
||||||
// Return default settings if none found
|
|
||||||
return &response.ClientApprovalSettingsResponse{
|
|
||||||
ClientId: clientId.String(),
|
|
||||||
RequiresApproval: true, // Default to requiring approval
|
|
||||||
AutoPublishArticles: false,
|
|
||||||
ApprovalExemptUsers: []uint{},
|
|
||||||
ApprovalExemptRoles: []uint{},
|
|
||||||
ApprovalExemptCategories: []uint{},
|
|
||||||
RequireApprovalFor: []string{},
|
|
||||||
SkipApprovalFor: []string{},
|
|
||||||
IsActive: true,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapper.ClientApprovalSettingsResponseMapper(_i.Log, clientId, settings), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) Create(clientId *uuid.UUID, req request.CreateClientApprovalSettingsRequest) (*response.ClientApprovalSettingsResponse, error) {
|
|
||||||
// Check if settings already exist
|
|
||||||
existing, err := _i.clientApprovalSettingsRepo.FindByClientId(*clientId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if existing != nil {
|
|
||||||
return nil, fmt.Errorf("approval settings already exist for this client")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new settings
|
|
||||||
settings := &entity.ClientApprovalSettings{
|
|
||||||
RequiresApproval: &req.RequiresApproval,
|
|
||||||
DefaultWorkflowId: req.DefaultWorkflowId,
|
|
||||||
AutoPublishArticles: &req.AutoPublishArticles,
|
|
||||||
ApprovalExemptUsers: req.ApprovalExemptUsers,
|
|
||||||
ApprovalExemptRoles: req.ApprovalExemptRoles,
|
|
||||||
ApprovalExemptCategories: req.ApprovalExemptCategories,
|
|
||||||
RequireApprovalFor: req.RequireApprovalFor,
|
|
||||||
SkipApprovalFor: req.SkipApprovalFor,
|
|
||||||
IsActive: &req.IsActive,
|
|
||||||
}
|
|
||||||
|
|
||||||
createdSettings, err := _i.clientApprovalSettingsRepo.Create(clientId, settings)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapper.ClientApprovalSettingsResponseMapper(_i.Log, clientId, createdSettings), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) Update(clientId *uuid.UUID, req request.UpdateClientApprovalSettingsRequest) (*response.ClientApprovalSettingsResponse, error) {
|
|
||||||
// Get existing settings
|
|
||||||
settings, err := _i.clientApprovalSettingsRepo.FindByClientId(*clientId)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if settings == nil {
|
|
||||||
return nil, fmt.Errorf("approval settings not found for this client")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update fields if provided
|
|
||||||
if req.RequiresApproval != nil {
|
|
||||||
settings.RequiresApproval = req.RequiresApproval
|
|
||||||
}
|
|
||||||
if req.DefaultWorkflowId != nil {
|
|
||||||
settings.DefaultWorkflowId = *req.DefaultWorkflowId
|
|
||||||
}
|
|
||||||
if req.AutoPublishArticles != nil {
|
|
||||||
settings.AutoPublishArticles = req.AutoPublishArticles
|
|
||||||
}
|
|
||||||
if req.ApprovalExemptUsers != nil {
|
|
||||||
settings.ApprovalExemptUsers = req.ApprovalExemptUsers
|
|
||||||
}
|
|
||||||
if req.ApprovalExemptRoles != nil {
|
|
||||||
settings.ApprovalExemptRoles = req.ApprovalExemptRoles
|
|
||||||
}
|
|
||||||
if req.ApprovalExemptCategories != nil {
|
|
||||||
settings.ApprovalExemptCategories = req.ApprovalExemptCategories
|
|
||||||
}
|
|
||||||
if req.RequireApprovalFor != nil {
|
|
||||||
settings.RequireApprovalFor = req.RequireApprovalFor
|
|
||||||
}
|
|
||||||
if req.SkipApprovalFor != nil {
|
|
||||||
settings.SkipApprovalFor = req.SkipApprovalFor
|
|
||||||
}
|
|
||||||
if req.IsActive != nil {
|
|
||||||
settings.IsActive = req.IsActive
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedSettings, err := _i.clientApprovalSettingsRepo.Update(clientId, settings)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return mapper.ClientApprovalSettingsResponseMapper(_i.Log, clientId, updatedSettings), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) Delete(clientId *uuid.UUID) error {
|
|
||||||
return _i.clientApprovalSettingsRepo.Delete(clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) ToggleApprovalRequirement(clientId *uuid.UUID, requiresApproval bool) error {
|
|
||||||
settings, err := _i.clientApprovalSettingsRepo.FindByClientId(*clientId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if settings == nil {
|
|
||||||
return fmt.Errorf("approval settings not found for this client")
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.RequiresApproval = &requiresApproval
|
|
||||||
_, err = _i.clientApprovalSettingsRepo.Update(clientId, settings)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) SetDefaultWorkflow(clientId *uuid.UUID, workflowId *uint) error {
|
|
||||||
settings, err := _i.clientApprovalSettingsRepo.FindByClientId(*clientId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if settings == nil {
|
|
||||||
return fmt.Errorf("approval settings not found for this client")
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.DefaultWorkflowId = workflowId
|
|
||||||
_, err = _i.clientApprovalSettingsRepo.Update(clientId, settings)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) AddExemptUser(clientId *uuid.UUID, userId uint) error {
|
|
||||||
return _i.clientApprovalSettingsRepo.AddExemptUser(clientId, userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) RemoveExemptUser(clientId *uuid.UUID, userId uint) error {
|
|
||||||
return _i.clientApprovalSettingsRepo.RemoveExemptUser(clientId, userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) AddExemptRole(clientId *uuid.UUID, roleId uint) error {
|
|
||||||
return _i.clientApprovalSettingsRepo.AddExemptRole(clientId, roleId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) RemoveExemptRole(clientId *uuid.UUID, roleId uint) error {
|
|
||||||
return _i.clientApprovalSettingsRepo.RemoveExemptRole(clientId, roleId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) AddExemptCategory(clientId *uuid.UUID, categoryId uint) error {
|
|
||||||
return _i.clientApprovalSettingsRepo.AddExemptCategory(clientId, categoryId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) RemoveExemptCategory(clientId *uuid.UUID, categoryId uint) error {
|
|
||||||
return _i.clientApprovalSettingsRepo.RemoveExemptCategory(clientId, categoryId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) CheckIfApprovalRequired(clientId *uuid.UUID, userId uint, userLevelId uint, categoryId uint, contentType string) (bool, error) {
|
|
||||||
settings, err := _i.clientApprovalSettingsRepo.FindActiveSettings(clientId)
|
|
||||||
if err != nil {
|
|
||||||
return true, err // Default to requiring approval on error
|
|
||||||
}
|
|
||||||
|
|
||||||
if settings == nil {
|
|
||||||
return true, nil // Default to requiring approval if no settings
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if approval is disabled
|
|
||||||
if settings.RequiresApproval != nil && !*settings.RequiresApproval {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check user exemption
|
|
||||||
for _, exemptUserId := range settings.ApprovalExemptUsers {
|
|
||||||
if exemptUserId == userId {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check role exemption
|
|
||||||
for _, exemptRoleId := range settings.ApprovalExemptRoles {
|
|
||||||
if exemptRoleId == userLevelId {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check category exemption
|
|
||||||
for _, exemptCategoryId := range settings.ApprovalExemptCategories {
|
|
||||||
if exemptCategoryId == categoryId {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check content type exemptions
|
|
||||||
for _, skipType := range settings.SkipApprovalFor {
|
|
||||||
if skipType == contentType {
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if content type requires approval
|
|
||||||
for _, requireType := range settings.RequireApprovalFor {
|
|
||||||
if requireType == contentType {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default to requiring approval
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) EnableApprovalWithTransition(clientId *uuid.UUID, defaultWorkflowId *uint) error {
|
|
||||||
settings, err := _i.clientApprovalSettingsRepo.FindByClientId(*clientId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if settings == nil {
|
|
||||||
// Create new settings
|
|
||||||
settings = &entity.ClientApprovalSettings{
|
|
||||||
RequiresApproval: &[]bool{true}[0],
|
|
||||||
DefaultWorkflowId: defaultWorkflowId,
|
|
||||||
AutoPublishArticles: &[]bool{false}[0],
|
|
||||||
IsActive: &[]bool{true}[0],
|
|
||||||
}
|
|
||||||
_, err = _i.clientApprovalSettingsRepo.Create(clientId, settings)
|
|
||||||
} else {
|
|
||||||
// Update existing settings
|
|
||||||
settings.RequiresApproval = &[]bool{true}[0]
|
|
||||||
settings.DefaultWorkflowId = defaultWorkflowId
|
|
||||||
settings.AutoPublishArticles = &[]bool{false}[0]
|
|
||||||
settings.IsActive = &[]bool{true}[0]
|
|
||||||
_, err = _i.clientApprovalSettingsRepo.Update(clientId, settings)
|
|
||||||
}
|
|
||||||
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) DisableApprovalWithAutoPublish(clientId *uuid.UUID, reason string) error {
|
|
||||||
settings, err := _i.clientApprovalSettingsRepo.FindByClientId(*clientId)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if settings == nil {
|
|
||||||
return fmt.Errorf("approval settings not found for this client")
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.RequiresApproval = &[]bool{false}[0]
|
|
||||||
settings.AutoPublishArticles = &[]bool{true}[0]
|
|
||||||
settings.IsActive = &[]bool{true}[0]
|
|
||||||
|
|
||||||
_, err = _i.clientApprovalSettingsRepo.Update(clientId, settings)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (_i *clientApprovalSettingsService) HandlePendingApprovalsOnDisable(clientId *uuid.UUID, action string) error {
|
|
||||||
// This would typically interact with article approval flows
|
|
||||||
// For now, just log the action
|
|
||||||
_i.Log.Info().
|
|
||||||
Str("client_id", clientId.String()).
|
|
||||||
Str("action", action).
|
|
||||||
Msg("Handling pending approvals on disable")
|
|
||||||
|
|
||||||
// TODO: Implement actual logic based on action:
|
|
||||||
// - "auto_approve": Auto approve all pending articles
|
|
||||||
// - "keep_pending": Keep articles in pending state
|
|
||||||
// - "reset_to_draft": Reset articles to draft state
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
"web-medols-be/app/module/custom_static_pages/request"
|
"web-medols-be/app/module/custom_static_pages/request"
|
||||||
"web-medols-be/app/module/custom_static_pages/service"
|
"web-medols-be/app/module/custom_static_pages/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -148,7 +147,7 @@ func (_i *customStaticPagesController) ShowBySlug(c *fiber.Ctx) error {
|
||||||
// @Tags CustomStaticPages
|
// @Tags CustomStaticPages
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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.CustomStaticPagesCreateRequest true "Required payload"
|
// @Param payload body request.CustomStaticPagesCreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -183,7 +182,7 @@ func (_i *customStaticPagesController) Save(c *fiber.Ctx) error {
|
||||||
// @Tags CustomStaticPages
|
// @Tags CustomStaticPages
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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.CustomStaticPagesUpdateRequest true "Required payload"
|
// @Param payload body request.CustomStaticPagesUpdateRequest true "Required payload"
|
||||||
// @Param id path int true "CustomStaticPages ID"
|
// @Param id path int true "CustomStaticPages ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -222,7 +221,7 @@ func (_i *customStaticPagesController) Update(c *fiber.Ctx) error {
|
||||||
// @Tags CustomStaticPages
|
// @Tags CustomStaticPages
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "CustomStaticPages ID"
|
// @Param id path int true "CustomStaticPages ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
"web-medols-be/app/module/feedbacks/request"
|
"web-medols-be/app/module/feedbacks/request"
|
||||||
|
|
@ -8,9 +10,6 @@ import (
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type feedbacksController struct {
|
type feedbacksController struct {
|
||||||
|
|
@ -117,7 +116,7 @@ func (_i *feedbacksController) Show(c *fiber.Ctx) error {
|
||||||
// @Tags Feedbacks
|
// @Tags Feedbacks
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 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.FeedbacksCreateRequest true "Required payload"
|
// @Param payload body request.FeedbacksCreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -154,7 +153,7 @@ func (_i *feedbacksController) Save(c *fiber.Ctx) error {
|
||||||
// @Tags Feedbacks
|
// @Tags Feedbacks
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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.FeedbacksUpdateRequest true "Required payload"
|
// @Param payload body request.FeedbacksUpdateRequest true "Required payload"
|
||||||
// @Param id path int true "Feedbacks ID"
|
// @Param id path int true "Feedbacks ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -193,7 +192,7 @@ func (_i *feedbacksController) Update(c *fiber.Ctx) error {
|
||||||
// @Tags Feedbacks
|
// @Tags Feedbacks
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "Feedbacks ID"
|
// @Param id path int true "Feedbacks ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/module/magazine_files/request"
|
"web-medols-be/app/module/magazine_files/request"
|
||||||
"web-medols-be/app/module/magazine_files/service"
|
"web-medols-be/app/module/magazine_files/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -31,7 +30,7 @@ func NewMagazineFilesController(magazineFilesService service.MagazineFilesServic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All MagazineFiles
|
||||||
// @Summary Get all MagazineFiles
|
// @Summary Get all MagazineFiles
|
||||||
// @Description API for getting all MagazineFiles
|
// @Description API for getting all MagazineFiles
|
||||||
// @Tags Magazine Files
|
// @Tags Magazine Files
|
||||||
|
|
@ -102,7 +101,7 @@ func (_i *magazineFilesController) Show(c *fiber.Ctx) error {
|
||||||
// @Description API for create MagazineFiles
|
// @Description API for create MagazineFiles
|
||||||
// @Tags Magazine Files
|
// @Tags Magazine Files
|
||||||
// @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 files formData file true "Upload file" multiple true
|
// @Param files formData file true "Upload file" multiple true
|
||||||
// @Param title formData string true "Magazine file title"
|
// @Param title formData string true "Magazine file title"
|
||||||
// @Param description formData string true "Magazine file description"
|
// @Param description formData string true "Magazine file description"
|
||||||
|
|
@ -136,7 +135,7 @@ func (_i *magazineFilesController) Save(c *fiber.Ctx) error {
|
||||||
// @Description API for update MagazineFiles
|
// @Description API for update MagazineFiles
|
||||||
// @Tags Magazine Files
|
// @Tags Magazine Files
|
||||||
// @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 "MagazineFiles ID"
|
// @Param id path int true "MagazineFiles ID"
|
||||||
// @Body request.MagazineFilesUpdateRequest
|
// @Body request.MagazineFilesUpdateRequest
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -170,7 +169,7 @@ func (_i *magazineFilesController) Update(c *fiber.Ctx) error {
|
||||||
// @Description API for delete MagazineFiles
|
// @Description API for delete MagazineFiles
|
||||||
// @Tags Magazine Files
|
// @Tags Magazine Files
|
||||||
// @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 "MagazineFiles ID"
|
// @Param id path int true "MagazineFiles ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,13 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/middleware"
|
"web-medols-be/app/middleware"
|
||||||
"web-medols-be/app/module/magazines/request"
|
"web-medols-be/app/module/magazines/request"
|
||||||
"web-medols-be/app/module/magazines/service"
|
"web-medols-be/app/module/magazines/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -33,7 +32,7 @@ func NewMagazinesController(magazinesService service.MagazinesService) Magazines
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All Magazines
|
||||||
// @Summary Get all Magazines
|
// @Summary Get all Magazines
|
||||||
// @Description API for getting all Magazines
|
// @Description API for getting all Magazines
|
||||||
// @Tags Magazines
|
// @Tags Magazines
|
||||||
|
|
@ -115,7 +114,7 @@ func (_i *magazinesController) Show(c *fiber.Ctx) error {
|
||||||
// @Tags Magazines
|
// @Tags Magazines
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 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.MagazinesCreateRequest true "Required payload"
|
// @Param payload body request.MagazinesCreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -152,7 +151,7 @@ func (_i *magazinesController) Save(c *fiber.Ctx) error {
|
||||||
// @Tags Magazines
|
// @Tags Magazines
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "Magazines ID"
|
// @Param id path int true "Magazines ID"
|
||||||
// @Param payload body request.MagazinesUpdateRequest true "Required payload"
|
// @Param payload body request.MagazinesUpdateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -191,7 +190,7 @@ func (_i *magazinesController) Update(c *fiber.Ctx) error {
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "Magazine ID"
|
// @Param id path int true "Magazine ID"
|
||||||
// @Param files formData file true "Upload thumbnail"
|
// @Param files formData file true "Upload thumbnail"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -239,7 +238,7 @@ func (_i *magazinesController) Viewer(c *fiber.Ctx) error {
|
||||||
// @Tags Magazines
|
// @Tags Magazines
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
||||||
// @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 "Magazines ID"
|
// @Param id path int true "Magazines ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/module/master_menus/request"
|
"web-medols-be/app/module/master_menus/request"
|
||||||
"web-medols-be/app/module/master_menus/service"
|
"web-medols-be/app/module/master_menus/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -105,7 +104,7 @@ func (_i *masterMenusController) Show(c *fiber.Ctx) error {
|
||||||
// @Description API for create MasterMenus
|
// @Description API for create MasterMenus
|
||||||
// @Tags MasterMenus
|
// @Tags MasterMenus
|
||||||
// @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.MasterMenusCreateRequest true "Required payload"
|
// @Param payload body request.MasterMenusCreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -134,7 +133,7 @@ func (_i *masterMenusController) Save(c *fiber.Ctx) error {
|
||||||
// @Description API for update MasterMenus
|
// @Description API for update MasterMenus
|
||||||
// @Tags MasterMenus
|
// @Tags MasterMenus
|
||||||
// @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"
|
||||||
// @Body request.MasterMenusUpdateRequest
|
// @Body request.MasterMenusUpdateRequest
|
||||||
// @Param id path int true "MasterMenus ID"
|
// @Param id path int true "MasterMenus ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -170,7 +169,7 @@ func (_i *masterMenusController) Update(c *fiber.Ctx) error {
|
||||||
// @Description API for delete MasterMenus
|
// @Description API for delete MasterMenus
|
||||||
// @Tags MasterMenus
|
// @Tags MasterMenus
|
||||||
// @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 "MasterMenus ID"
|
// @Param id path int true "MasterMenus ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,12 @@
|
||||||
package controller
|
package controller
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
"strconv"
|
||||||
"web-medols-be/app/module/master_modules/request"
|
"web-medols-be/app/module/master_modules/request"
|
||||||
"web-medols-be/app/module/master_modules/service"
|
"web-medols-be/app/module/master_modules/service"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
utilRes "web-medols-be/utils/response"
|
||||||
utilVal "web-medols-be/utils/validator"
|
utilVal "web-medols-be/utils/validator"
|
||||||
)
|
)
|
||||||
|
|
@ -103,7 +102,7 @@ func (_i *masterModulesController) Show(c *fiber.Ctx) error {
|
||||||
// @Description API for create MasterModules
|
// @Description API for create MasterModules
|
||||||
// @Tags MasterModules
|
// @Tags MasterModules
|
||||||
// @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.MasterModulesCreateRequest true "Required payload"
|
// @Param payload body request.MasterModulesCreateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
@ -132,7 +131,7 @@ func (_i *masterModulesController) Save(c *fiber.Ctx) error {
|
||||||
// @Description API for update MasterModules
|
// @Description API for update MasterModules
|
||||||
// @Tags MasterModules
|
// @Tags MasterModules
|
||||||
// @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 "MasterModules ID"
|
// @Param id path int true "MasterModules ID"
|
||||||
// @Param payload body request.MasterModulesUpdateRequest true "Required payload"
|
// @Param payload body request.MasterModulesUpdateRequest true "Required payload"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
|
|
@ -167,7 +166,7 @@ func (_i *masterModulesController) Update(c *fiber.Ctx) error {
|
||||||
// @Description API for delete MasterModules
|
// @Description API for delete MasterModules
|
||||||
// @Tags MasterModules
|
// @Tags MasterModules
|
||||||
// @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 "MasterModules ID"
|
// @Param id path int true "MasterModules ID"
|
||||||
// @Success 200 {object} response.Response
|
// @Success 200 {object} response.Response
|
||||||
// @Failure 400 {object} response.BadRequestError
|
// @Failure 400 {object} response.BadRequestError
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package entity
|
package entity
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type MasterStatuses struct {
|
type MasterStatuses 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"`
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package request
|
package request
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
"web-medols-be/app/database/entity"
|
"web-medols-be/app/database/entity"
|
||||||
"web-medols-be/utils/paginator"
|
"web-medols-be/utils/paginator"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package response
|
package response
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type MasterStatusesResponse struct {
|
type MasterStatusesResponse struct {
|
||||||
ID uint `json:"id"`
|
ID uint `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
|
||||||
|
|
@ -1,235 +0,0 @@
|
||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"web-medols-be/app/middleware"
|
|
||||||
"web-medols-be/app/module/schedules/request"
|
|
||||||
"web-medols-be/app/module/schedules/service"
|
|
||||||
"web-medols-be/utils/paginator"
|
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"github.com/rs/zerolog"
|
|
||||||
|
|
||||||
utilRes "web-medols-be/utils/response"
|
|
||||||
utilVal "web-medols-be/utils/validator"
|
|
||||||
)
|
|
||||||
|
|
||||||
type schedulesController struct {
|
|
||||||
schedulesService service.SchedulesService
|
|
||||||
Log zerolog.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
type SchedulesController 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
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSchedulesController(schedulesService service.SchedulesService, log zerolog.Logger) SchedulesController {
|
|
||||||
return &schedulesController{
|
|
||||||
schedulesService: schedulesService,
|
|
||||||
Log: log,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// All Schedules
|
|
||||||
// @Summary Get all Schedules
|
|
||||||
// @Description API for getting all Schedules
|
|
||||||
// @Tags Schedules
|
|
||||||
// @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 request.SchedulesQueryRequest 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 /schedules [get]
|
|
||||||
func (_i *schedulesController) All(c *fiber.Ctx) error {
|
|
||||||
paginate, err := paginator.Paginate(c)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
reqContext := request.SchedulesQueryRequestContext{
|
|
||||||
Title: c.Query("title"),
|
|
||||||
Description: c.Query("description"),
|
|
||||||
Location: c.Query("location"),
|
|
||||||
TypeId: c.Query("typeId"),
|
|
||||||
StartDate: c.Query("startDate"),
|
|
||||||
EndDate: c.Query("endDate"),
|
|
||||||
IsLiveStreaming: c.Query("isLiveStreaming"),
|
|
||||||
Speakers: c.Query("speakers"),
|
|
||||||
StatusId: c.Query("statusId"),
|
|
||||||
CreatedById: c.Query("createdById"),
|
|
||||||
}
|
|
||||||
req := reqContext.ToParamRequest()
|
|
||||||
req.Pagination = paginate
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
// Get Authorization token from header
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
_i.Log.Info().Str("authToken", authToken).Msg("")
|
|
||||||
|
|
||||||
schedulesData, paging, err := _i.schedulesService.All(clientId, req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Schedules list successfully retrieved"},
|
|
||||||
Data: schedulesData,
|
|
||||||
Meta: paging,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show Schedule
|
|
||||||
// @Summary Get one Schedule
|
|
||||||
// @Description API for getting one Schedule
|
|
||||||
// @Tags Schedules
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param id path int true "Schedule ID"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /schedules/{id} [get]
|
|
||||||
func (_i *schedulesController) Show(c *fiber.Ctx) error {
|
|
||||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
scheduleData, err := _i.schedulesService.Show(clientId, uint(id))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Schedule successfully retrieved"},
|
|
||||||
Data: scheduleData,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save Schedule
|
|
||||||
// @Summary Create Schedule
|
|
||||||
// @Description API for create Schedule
|
|
||||||
// @Tags Schedules
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
|
||||||
// @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 payload body request.SchedulesCreateRequest true "Required payload"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /schedules [post]
|
|
||||||
func (_i *schedulesController) Save(c *fiber.Ctx) error {
|
|
||||||
req := new(request.SchedulesCreateRequest)
|
|
||||||
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
authToken := c.Get("Authorization")
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
_i.Log.Info().Interface("clientId", clientId).Msg("")
|
|
||||||
_i.Log.Info().Interface("authToken", authToken).Msg("")
|
|
||||||
|
|
||||||
dataResult, err := _i.schedulesService.Save(clientId, *req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Schedule successfully created"},
|
|
||||||
Data: dataResult,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update Schedule
|
|
||||||
// @Summary Update Schedule
|
|
||||||
// @Description API for update Schedule
|
|
||||||
// @Tags Schedules
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
|
||||||
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token"
|
|
||||||
// @Param payload body request.SchedulesUpdateRequest true "Required payload"
|
|
||||||
// @Param id path int true "Schedule ID"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /schedules/{id} [put]
|
|
||||||
func (_i *schedulesController) Update(c *fiber.Ctx) error {
|
|
||||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
req := new(request.SchedulesUpdateRequest)
|
|
||||||
if err := utilVal.ParseAndValidate(c, req); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
err = _i.schedulesService.Update(clientId, uint(id), *req)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Schedule successfully updated"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete Schedule
|
|
||||||
// @Summary Delete Schedule
|
|
||||||
// @Description API for delete Schedule
|
|
||||||
// @Tags Schedules
|
|
||||||
// @Security Bearer
|
|
||||||
// @Param X-Client-Key header string false "Insert the X-Client-Key"
|
|
||||||
// @Param X-Csrf-Token header string false "Insert the X-Csrf-Token"
|
|
||||||
// @Param id path int true "Schedule ID"
|
|
||||||
// @Success 200 {object} response.Response
|
|
||||||
// @Failure 400 {object} response.BadRequestError
|
|
||||||
// @Failure 401 {object} response.UnauthorizedError
|
|
||||||
// @Failure 500 {object} response.InternalServerError
|
|
||||||
// @Router /schedules/{id} [delete]
|
|
||||||
func (_i *schedulesController) Delete(c *fiber.Ctx) error {
|
|
||||||
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get ClientId from context
|
|
||||||
clientId := middleware.GetClientID(c)
|
|
||||||
|
|
||||||
err = _i.schedulesService.Delete(clientId, uint(id))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return utilRes.Resp(c, utilRes.Response{
|
|
||||||
Success: true,
|
|
||||||
Messages: utilRes.Messages{"Schedule successfully deleted"},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
package mapper
|
|
||||||
|
|
||||||
import (
|
|
||||||
"web-medols-be/app/database/entity"
|
|
||||||
schedulesResponse "web-medols-be/app/module/schedules/response"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ToSchedulesResponse(schedule *entity.Schedules) *schedulesResponse.SchedulesResponse {
|
|
||||||
return &schedulesResponse.SchedulesResponse{
|
|
||||||
ID: schedule.ID,
|
|
||||||
Title: schedule.Title,
|
|
||||||
Description: schedule.Description,
|
|
||||||
Location: schedule.Location,
|
|
||||||
IsLiveStreaming: schedule.IsLiveStreaming,
|
|
||||||
LiveStreamingUrl: schedule.LiveStreamingUrl,
|
|
||||||
TypeId: schedule.TypeId,
|
|
||||||
StartDate: schedule.StartDate,
|
|
||||||
EndDate: schedule.EndDate,
|
|
||||||
StartTime: schedule.StartTime,
|
|
||||||
EndTime: schedule.EndTime,
|
|
||||||
Speakers: schedule.Speakers,
|
|
||||||
PosterImagePath: schedule.PosterImagePath,
|
|
||||||
CreatedById: schedule.CreatedById,
|
|
||||||
StatusId: schedule.StatusId,
|
|
||||||
IsActive: schedule.IsActive,
|
|
||||||
CreatedAt: schedule.CreatedAt,
|
|
||||||
UpdatedAt: schedule.UpdatedAt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToSchedulesResponseList(schedules []*entity.Schedules) []*schedulesResponse.SchedulesResponse {
|
|
||||||
var responses []*schedulesResponse.SchedulesResponse
|
|
||||||
for _, schedule := range schedules {
|
|
||||||
responses = append(responses, ToSchedulesResponse(schedule))
|
|
||||||
}
|
|
||||||
return responses
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue