Skip to content

Commit

Permalink
Workflow sla management and remove spring state machine implementatio…
Browse files Browse the repository at this point in the history
…ns (#22)
  • Loading branch information
haiphucnguyen authored Dec 2, 2024
1 parent a5ee0eb commit 4cefab0
Show file tree
Hide file tree
Showing 30 changed files with 351 additions and 601 deletions.
5 changes: 0 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
[versions]
mapstructVersion = "1.6.2"
archunitJunit5Version = "1.3.0"
springStateMachineVersion = "4.0.0"
lombokVersion = "1.18.34"
liquibaseVersion = "4.30.0"
assertJVersion = "3.26.3"
Expand All @@ -22,9 +21,6 @@ shedlockVersion="6.0.1"
[libraries]
jhipster-framework = { module = "tech.jhipster:jhipster-framework", version.ref = "jhisterVersion" }
lombok = { module = "org.projectlombok:lombok", version.ref = "lombokVersion" }
spring-statemachine-starter = { module = "org.springframework.statemachine:spring-statemachine-starter", version.ref = "springStateMachineVersion" }
spring-statemachine-jpa = { module = "org.springframework.statemachine:spring-statemachine-data-jpa", version.ref = "springStateMachineVersion" }
spring-statemachine-test = { module = "org.springframework.statemachine:spring-statemachine-test", version.ref = "springStateMachineVersion" }
liquibase = { module = "org.liquibase:liquibase-core", version.ref = "liquibaseVersion" }
assertJ = { module = "org.assertj:assertj-core", version.ref = "assertJVersion" }
mapstruct = { module = "org.mapstruct:mapstruct", version.ref = "mapstructVersion" }
Expand All @@ -48,7 +44,6 @@ spring-boot = { id = "org.springframework.boot", version.ref = "springbootVersio
spring-dependency-management = { id = "io.spring.dependency-management", version.ref="springDependencyManagementVersion" }

[bundles]
spring-statemachine = ["spring-statemachine-starter", "spring-statemachine-jpa"]
logback = ["logback-classic"]
junit = ["junit-jupiter-api", "junit-jupiter-engine"]
mockito = ["mockito", "mockito-junit"]
Expand Down
2 changes: 0 additions & 2 deletions server/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ dependencies {

implementation("org.hibernate.orm:hibernate-core")
implementation("org.hibernate.validator:hibernate-validator")
implementation(libs.bundles.spring.statemachine)
implementation(libs.liquibase)
implementation(libs.bundles.shedlock)

Expand All @@ -135,7 +134,6 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.boot:spring-boot-test")
testImplementation("org.springframework.security:spring-security-test")
testImplementation(libs.spring.statemachine.test)
testCompileOnly(libs.lombok)
testAnnotationProcessor(libs.lombok)
testImplementation("org.jetbrains.kotlin:kotlin-test")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
Expand Down Expand Up @@ -52,8 +50,8 @@ public class TeamRequest extends AbstractAuditingEntity<Long> {

private String requestDescription;

@Column(nullable = false, length = 50)
@Enumerated(EnumType.STRING)
@Column(nullable = false)
@Convert(converter = TeamRequestPriorityConverter.class)
private TeamRequestPriority priority;

@Column(name = "is_deleted", nullable = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
package io.flexwork.modules.teams.domain;

public enum TeamRequestPriority {
Critical,
High,
Medium,
Low,
Trivial
Critical(0),
High(1),
Medium(2),
Low(3),
Trivial(4);

private final int code;

TeamRequestPriority(int code) {
this.code = code;
}

public int getCode() {
return code;
}

public static TeamRequestPriority fromCode(int code) {
for (TeamRequestPriority priority : TeamRequestPriority.values()) {
if (priority.code == code) {
return priority;
}
}
throw new IllegalArgumentException("Invalid code for TeamRequestPriority: " + code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.flexwork.modules.teams.domain;

import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;

@Converter(autoApply = true)
public class TeamRequestPriorityConverter
implements AttributeConverter<TeamRequestPriority, Integer> {

@Override
public Integer convertToDatabaseColumn(TeamRequestPriority priority) {
return (priority != null) ? priority.getCode() : null;
}

@Override
public TeamRequestPriority convertToEntityAttribute(Integer code) {
return (code != null) ? TeamRequestPriority.fromCode(code) : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
Expand Down Expand Up @@ -48,5 +50,6 @@ public class WorkflowTransitionHistory {
private ZonedDateTime slaDueDate;

@Column(name = "status")
private String status;
@Enumerated(EnumType.STRING)
private WorkflowTransitionHistoryStatus status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.flexwork.modules.teams.domain;

public enum WorkflowTransitionHistoryStatus {
In_Progress,
Overdue,
Completed
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package io.flexwork.modules.teams.repository;

import io.flexwork.modules.teams.domain.TeamRequest;
import io.flexwork.modules.teams.domain.WorkflowTransitionHistoryStatus;
import io.flexwork.modules.teams.service.dto.PriorityDistributionDTO;
import io.flexwork.modules.teams.service.dto.TicketActionCountByDateDTO;
import io.flexwork.modules.teams.service.dto.TicketDistributionDTO;
import io.flexwork.modules.usermanagement.service.dto.TicketStatisticsDTO;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -80,29 +84,8 @@ public interface TeamRequestRepository
@Query(
"SELECT r FROM TeamRequest r "
+ "WHERE r.team.id = :teamId AND r.isCompleted = false AND r.isDeleted = false "
+ "AND r.assignUser IS NULL "
+ "ORDER BY CASE r.priority "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.Trivial THEN 1 "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.Low THEN 2 "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.Medium THEN 3 "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.High THEN 4 "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.Critical THEN 5 "
+ "END ASC")
Page<TeamRequest> findUnassignedTicketsByTeamIdAsc(
@Param("teamId") Long teamId, Pageable pageable);

@Query(
"SELECT r FROM TeamRequest r "
+ "WHERE r.team.id = :teamId AND r.isCompleted = false AND r.isDeleted = false "
+ "AND r.assignUser IS NULL "
+ "ORDER BY CASE r.priority "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.Trivial THEN 1 "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.Low THEN 2 "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.Medium THEN 3 "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.High THEN 4 "
+ " WHEN io.flexwork.modules.teams.domain.TeamRequestPriority.Critical THEN 5 "
+ "END DESC")
Page<TeamRequest> findUnassignedTicketsByTeamIdDesc(
+ "AND r.assignUser IS NULL")
Page<TeamRequest> findUnassignedTicketsByTeamId(
@Param("teamId") Long teamId, Pageable pageable);

// Query to count tickets by priority for a specific team
Expand All @@ -114,4 +97,64 @@ Page<TeamRequest> findUnassignedTicketsByTeamIdDesc(
+ "GROUP BY r.priority")
List<PriorityDistributionDTO> findTicketPriorityDistributionByTeamId(
@Param("teamId") Long teamId);

@Query(
"SELECT new io.flexwork.modules.usermanagement.service.dto.TicketStatisticsDTO("
+ "COUNT(tr), "
+ "SUM(CASE WHEN tr.isCompleted = false THEN 1 ELSE 0 END), "
+ "SUM(CASE WHEN tr.isCompleted = true THEN 1 ELSE 0 END)) "
+ "FROM TeamRequest tr "
+ "WHERE tr.isDeleted = false AND tr.team.id = :teamId")
TicketStatisticsDTO getTicketStatisticsByTeamId(@Param("teamId") Long teamId);

@Query(
"SELECT r "
+ "FROM TeamRequest r "
+ "JOIN WorkflowTransitionHistory h ON h.teamRequest.id = r.id "
+ "WHERE r.isDeleted = false "
+ "AND r.isCompleted = false "
+ "AND h.slaDueDate IS NOT NULL "
+ "AND h.slaDueDate < CURRENT_TIMESTAMP "
+ "AND h.status <> :status "
+ "AND r.team.id = :teamId")
Page<TeamRequest> findOverdueTicketsByTeamId(
@Param("teamId") Long teamId,
@Param("status") WorkflowTransitionHistoryStatus completedStatus,
Pageable pageable);

@Query(
"SELECT COUNT(r.id) "
+ "FROM TeamRequest r "
+ "JOIN WorkflowTransitionHistory h ON h.teamRequest.id = r.id "
+ "WHERE r.isDeleted = false "
+ "AND r.isCompleted = false "
+ "AND h.slaDueDate IS NOT NULL "
+ "AND h.slaDueDate < CURRENT_TIMESTAMP "
+ "AND h.status <> :status "
+ "AND r.team.id = :teamId")
Long countOverdueTicketsByTeamId(
@Param("teamId") Long teamId,
@Param("status") WorkflowTransitionHistoryStatus completedStatus);

@Query(
"SELECT new io.flexwork.modules.teams.service.dto.TicketActionCountByDateDTO("
+ "CAST(r.createdAt AS date), "
+ "COUNT(r.id), "
+ "COALESCE(closedTicketCounts.closedCount, 0)) "
+ "FROM TeamRequest r "
+ "LEFT JOIN ("
+ " SELECT CAST(c.actualCompletionDate AS date) AS completionDate, COUNT(c.id) AS closedCount "
+ " FROM TeamRequest c "
+ " WHERE c.isDeleted = false "
+ " AND c.team.id = :teamId "
+ " AND c.actualCompletionDate IS NOT NULL "
+ " GROUP BY CAST(c.actualCompletionDate AS date)"
+ ") closedTicketCounts ON CAST(r.createdAt AS date) = closedTicketCounts.completionDate "
+ "WHERE r.isDeleted = false "
+ "AND r.team.id = :teamId "
+ "AND r.createdAt >= :startDate "
+ "GROUP BY CAST(r.createdAt AS date), closedTicketCounts.closedCount "
+ "ORDER BY CAST(r.createdAt AS date) ASC")
List<TicketActionCountByDateDTO> findTicketActionByDaySeries(
@Param("teamId") Long teamId, @Param("startDate") Instant startDate);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ public interface WorkflowTransitionRepository extends JpaRepository<WorkflowTran
List<WorkflowState> findValidTargetStates(
@Param("workflowId") Long workflowId, @Param("sourceStateId") Long sourceStateId);

/**
* @param workflowId
* @param sourceStateId
* @return
*/
@Query(
"SELECT wt "
+ "FROM WorkflowTransition wt "
+ "WHERE wt.sourceState.id = :sourceStateId "
+ "AND wt.workflow.id = :workflowId")
List<WorkflowTransition> findTransitionsBySourceState(
@Param("workflowId") Long workflowId, @Param("sourceStateId") Long sourceStateId);

/**
* @param workflowId
* @param sourceStateId
* @param targetStateId
* @return
*/
Optional<WorkflowTransition> findByWorkflowIdAndSourceStateIdAndTargetStateId(
Long workflowId, Long sourceStateId, Long targetStateId);
}
Loading

0 comments on commit 4cefab0

Please sign in to comment.