add dukcapil
This commit is contained in:
@@ -1,365 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/appcontext"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ApprovalFlowService interface {
|
||||
CreateApprovalFlow(ctx context.Context, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error)
|
||||
GetApprovalFlow(ctx context.Context, id uuid.UUID) (*contract.ApprovalFlowResponse, error)
|
||||
GetApprovalFlowByDepartment(ctx context.Context, departmentID uuid.UUID) (*contract.ApprovalFlowResponse, error)
|
||||
UpdateApprovalFlow(ctx context.Context, id uuid.UUID, req *contract.ApprovalFlowRequest) (*contract.ApprovalFlowResponse, error)
|
||||
DeleteApprovalFlow(ctx context.Context, id uuid.UUID) error
|
||||
ListApprovalFlows(ctx context.Context, req *contract.ListApprovalFlowsRequest) (*contract.ListApprovalFlowsResponse, error)
|
||||
}
|
||||
|
||||
type AdminApprovalFlowHandler struct {
|
||||
svc ApprovalFlowService
|
||||
}
|
||||
|
||||
func NewAdminApprovalFlowHandler(svc ApprovalFlowService) *AdminApprovalFlowHandler {
|
||||
return &AdminApprovalFlowHandler{svc: svc}
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) CreateApprovalFlow(c *gin.Context) {
|
||||
var req contract.ApprovalFlowRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Steps) == 0 {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "at least one approval step is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
for i, step := range req.Steps {
|
||||
if step.ApproverRoleID == nil && step.ApproverUserID == nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "step " + strconv.Itoa(i+1) + " must have either approver_role_id or approver_user_id",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
if step.ApproverRoleID != nil && step.ApproverUserID != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "step " + strconv.Itoa(i+1) + " cannot have both approver_role_id and approver_user_id",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := h.svc.CreateApprovalFlow(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) GetApprovalFlow(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetApprovalFlow(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) GetApprovalFlowByDepartment(c *gin.Context) {
|
||||
departmentID, err := uuid.Parse(c.Param("department_id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid department_id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetApprovalFlowByDepartment(c.Request.Context(), departmentID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) UpdateApprovalFlow(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.ApprovalFlowRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.Steps) == 0 {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "at least one approval step is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
for i, step := range req.Steps {
|
||||
if step.ApproverRoleID == nil && step.ApproverUserID == nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "step " + strconv.Itoa(i+1) + " must have either approver_role_id or approver_user_id",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
if step.ApproverRoleID != nil && step.ApproverUserID != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "step " + strconv.Itoa(i+1) + " cannot have both approver_role_id and approver_user_id",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := h.svc.UpdateApprovalFlow(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) DeleteApprovalFlow(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.DeleteApprovalFlow(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "approval flow deleted"})
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) ListApprovalFlows(c *gin.Context) {
|
||||
// Parse query params
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
|
||||
|
||||
var departmentID *uuid.UUID
|
||||
if departmentIDStr := c.Query("department_id"); departmentIDStr != "" {
|
||||
if id, err := uuid.Parse(departmentIDStr); err == nil {
|
||||
departmentID = &id
|
||||
}
|
||||
}
|
||||
|
||||
var isActive *bool
|
||||
if isActiveStr := c.Query("is_active"); isActiveStr != "" {
|
||||
if active, err := strconv.ParseBool(isActiveStr); err == nil {
|
||||
isActive = &active
|
||||
}
|
||||
}
|
||||
|
||||
var search *string
|
||||
if searchStr := c.Query("search"); searchStr != "" {
|
||||
search = &searchStr
|
||||
}
|
||||
|
||||
// Build request - pass PAGE, bukan OFFSET
|
||||
req := &contract.ListApprovalFlowsRequest{
|
||||
Page: page, // ✅ Pass page number
|
||||
Limit: limit,
|
||||
DepartmentID: departmentID,
|
||||
IsActive: isActive,
|
||||
Search: search, // tambahkan ini juga
|
||||
}
|
||||
|
||||
resp, err := h.svc.ListApprovalFlows(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) ListApprovalFlowsByDepartment(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
|
||||
offset := (page - 1) * limit
|
||||
|
||||
req := &contract.ListApprovalFlowsRequest{
|
||||
Limit: limit,
|
||||
Page: offset,
|
||||
DepartmentID: &appCtx.DepartmentID,
|
||||
}
|
||||
|
||||
resp, err := h.svc.ListApprovalFlows(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) ActivateApprovalFlow(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
// Get the current flow
|
||||
flow, err := h.svc.GetApprovalFlow(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
// Update only the IsActive field
|
||||
req := &contract.ApprovalFlowRequest{
|
||||
DepartmentID: flow.DepartmentID,
|
||||
Name: flow.Name,
|
||||
Description: flow.Description,
|
||||
IsActive: true,
|
||||
Steps: make([]contract.ApprovalFlowStepRequest, len(flow.Steps)),
|
||||
}
|
||||
|
||||
// Copy existing steps
|
||||
for i, step := range flow.Steps {
|
||||
req.Steps[i] = contract.ApprovalFlowStepRequest{
|
||||
StepOrder: step.StepOrder,
|
||||
ParallelGroup: step.ParallelGroup,
|
||||
ApproverRoleID: step.ApproverRoleID,
|
||||
ApproverUserID: step.ApproverUserID,
|
||||
Required: step.Required,
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := h.svc.UpdateApprovalFlow(c.Request.Context(), id, req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) DeactivateApprovalFlow(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
// Get the current flow
|
||||
flow, err := h.svc.GetApprovalFlow(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
// Update only the IsActive field
|
||||
req := &contract.ApprovalFlowRequest{
|
||||
DepartmentID: flow.DepartmentID,
|
||||
Name: flow.Name,
|
||||
Description: flow.Description,
|
||||
IsActive: false,
|
||||
Steps: make([]contract.ApprovalFlowStepRequest, len(flow.Steps)),
|
||||
}
|
||||
|
||||
// Copy existing steps
|
||||
for i, step := range flow.Steps {
|
||||
req.Steps[i] = contract.ApprovalFlowStepRequest{
|
||||
StepOrder: step.StepOrder,
|
||||
ParallelGroup: step.ParallelGroup,
|
||||
ApproverRoleID: step.ApproverRoleID,
|
||||
ApproverUserID: step.ApproverUserID,
|
||||
Required: step.Required,
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := h.svc.UpdateApprovalFlow(c.Request.Context(), id, req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *AdminApprovalFlowHandler) CloneApprovalFlow(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var cloneReq struct {
|
||||
DepartmentID uuid.UUID `json:"department_id" validate:"required"`
|
||||
Name string `json:"name" validate:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&cloneReq); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
// Get the source flow
|
||||
sourceFlow, err := h.svc.GetApprovalFlow(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
// Create new flow request with cloned data
|
||||
req := &contract.ApprovalFlowRequest{
|
||||
DepartmentID: cloneReq.DepartmentID,
|
||||
Name: cloneReq.Name,
|
||||
Description: sourceFlow.Description,
|
||||
IsActive: false, // New cloned flow starts as inactive
|
||||
Steps: make([]contract.ApprovalFlowStepRequest, len(sourceFlow.Steps)),
|
||||
}
|
||||
|
||||
// Copy steps from source flow
|
||||
for i, step := range sourceFlow.Steps {
|
||||
req.Steps[i] = contract.ApprovalFlowStepRequest{
|
||||
StepOrder: step.StepOrder,
|
||||
ParallelGroup: step.ParallelGroup,
|
||||
ApproverRoleID: step.ApproverRoleID,
|
||||
ApproverUserID: step.ApproverUserID,
|
||||
Required: step.Required,
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := h.svc.CreateApprovalFlow(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
@@ -1,195 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
"eslogad-be/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type AnalyticsHandler struct {
|
||||
analyticsService service.AnalyticsService
|
||||
}
|
||||
|
||||
func NewAnalyticsHandler(analyticsService service.AnalyticsService) *AnalyticsHandler {
|
||||
return &AnalyticsHandler{
|
||||
analyticsService: analyticsService,
|
||||
}
|
||||
}
|
||||
|
||||
// GetDashboard handles GET /api/v1/analytics/dashboard
|
||||
func (h *AnalyticsHandler) GetDashboard(c *gin.Context) {
|
||||
var req contract.AnalyticsDashboardRequest
|
||||
|
||||
// Bind query parameters
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "Invalid query parameters",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get analytics dashboard data
|
||||
response, err := h.analyticsService.GetDashboard(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(response))
|
||||
}
|
||||
|
||||
// GetLetterVolume handles GET /api/v1/analytics/volume
|
||||
func (h *AnalyticsHandler) GetLetterVolume(c *gin.Context) {
|
||||
response, err := h.analyticsService.GetLetterVolume(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(response))
|
||||
}
|
||||
|
||||
// GetStatusDistribution handles GET /api/v1/analytics/status-distribution
|
||||
func (h *AnalyticsHandler) GetStatusDistribution(c *gin.Context) {
|
||||
var req contract.AnalyticsDashboardRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "Invalid query parameters",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get full dashboard and extract status distribution
|
||||
response, err := h.analyticsService.GetDashboard(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]interface{}{
|
||||
"status_distribution": response.StatusDistribution,
|
||||
}))
|
||||
}
|
||||
|
||||
// GetPriorityDistribution handles GET /api/v1/analytics/priority-distribution
|
||||
func (h *AnalyticsHandler) GetPriorityDistribution(c *gin.Context) {
|
||||
var req contract.AnalyticsDashboardRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "Invalid query parameters",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get full dashboard and extract priority distribution
|
||||
response, err := h.analyticsService.GetDashboard(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]interface{}{
|
||||
"priority_distribution": response.PriorityDistribution,
|
||||
}))
|
||||
}
|
||||
|
||||
// GetDepartmentStats handles GET /api/v1/analytics/department-stats
|
||||
func (h *AnalyticsHandler) GetDepartmentStats(c *gin.Context) {
|
||||
var req contract.AnalyticsDashboardRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "Invalid query parameters",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get full dashboard and extract department stats
|
||||
response, err := h.analyticsService.GetDashboard(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]interface{}{
|
||||
"department_stats": response.DepartmentStats,
|
||||
}))
|
||||
}
|
||||
|
||||
// GetMonthlyTrend handles GET /api/v1/analytics/monthly-trend
|
||||
func (h *AnalyticsHandler) GetMonthlyTrend(c *gin.Context) {
|
||||
var req contract.AnalyticsDashboardRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "Invalid query parameters",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get full dashboard and extract monthly trend
|
||||
response, err := h.analyticsService.GetDashboard(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]interface{}{
|
||||
"monthly_trend": response.MonthlyTrend,
|
||||
}))
|
||||
}
|
||||
|
||||
// GetApprovalMetrics handles GET /api/v1/analytics/approval-metrics
|
||||
func (h *AnalyticsHandler) GetApprovalMetrics(c *gin.Context) {
|
||||
var req contract.AnalyticsDashboardRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "Invalid query parameters",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get full dashboard and extract approval metrics
|
||||
response, err := h.analyticsService.GetDashboard(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]interface{}{
|
||||
"approval_metrics": response.ApprovalMetrics,
|
||||
}))
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"eslogad-be/internal/util"
|
||||
"go-backend-template/internal/util"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"eslogad-be/internal/constants"
|
||||
"eslogad-be/internal/contract"
|
||||
"eslogad-be/internal/logger"
|
||||
"go-backend-template/internal/constants"
|
||||
"go-backend-template/internal/contract"
|
||||
"go-backend-template/internal/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
@@ -54,82 +54,6 @@ func (h *AuthHandler) Login(c *gin.Context) {
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(loginResponse), "AuthHandler::Login")
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Logout(c *gin.Context) {
|
||||
token := h.extractTokenFromHeader(c)
|
||||
if token == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::Logout -> token is required")
|
||||
h.sendErrorResponse(c, "Token is required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
err := h.authService.Logout(c.Request.Context(), token)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::Logout -> Failed to logout")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Info("AuthHandler::Logout -> Successfully logged out")
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "Successfully logged out"})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) RefreshToken(c *gin.Context) {
|
||||
token := h.extractTokenFromHeader(c)
|
||||
if token == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::RefreshToken -> token is required")
|
||||
h.sendErrorResponse(c, "Token is required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
loginResponse, err := h.authService.RefreshToken(c.Request.Context(), token)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::RefreshToken -> Failed to refresh token")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Infof("AuthHandler::RefreshToken -> Successfully refreshed token for user = %s", loginResponse.User.Email)
|
||||
c.JSON(http.StatusOK, loginResponse)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) ValidateToken(c *gin.Context) {
|
||||
token := h.extractTokenFromHeader(c)
|
||||
if token == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::ValidateToken -> token is required")
|
||||
h.sendErrorResponse(c, "Token is required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
userResponse, err := h.authService.ValidateToken(token)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::ValidateToken -> Failed to validate token")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Infof("AuthHandler::ValidateToken -> Successfully validated token for user = %s", userResponse.Email)
|
||||
c.JSON(http.StatusOK, userResponse)
|
||||
}
|
||||
|
||||
func (h *AuthHandler) GetProfile(c *gin.Context) {
|
||||
token := h.extractTokenFromHeader(c)
|
||||
if token == "" {
|
||||
logger.FromContext(c.Request.Context()).Error("AuthHandler::GetProfile -> token is required")
|
||||
h.sendErrorResponse(c, "Token is required", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
userResponse, err := h.authService.ValidateToken(token)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("AuthHandler::GetProfile -> Failed to get profile")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Infof("AuthHandler::GetProfile -> Successfully retrieved profile for user = %s", userResponse.Email)
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Data: userResponse, Message: "success get user profile"})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) extractTokenFromHeader(c *gin.Context) string {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
|
||||
@@ -2,12 +2,10 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/contract"
|
||||
"go-backend-template/internal/contract"
|
||||
)
|
||||
|
||||
type AuthService interface {
|
||||
Login(ctx context.Context, req *contract.LoginRequest) (*contract.LoginResponse, error)
|
||||
ValidateToken(tokenString string) (*contract.UserResponse, error)
|
||||
RefreshToken(ctx context.Context, tokenString string) (*contract.LoginResponse, error)
|
||||
Logout(ctx context.Context, tokenString string) error
|
||||
RefreshToken(ctx context.Context, req *contract.RefreshTokenRequest) (*contract.LoginResponse, error)
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/appcontext"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type DispositionRouteService interface {
|
||||
Create(ctx context.Context, req *contract.CreateDispositionRouteRequest) (*contract.DispositionRouteResponse, error)
|
||||
CreateOrUpdate(ctx context.Context, req *contract.CreateDispositionRouteRequest) (*contract.BulkCreateDispositionRouteResponse, error)
|
||||
Update(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionRouteRequest) (*contract.DispositionRouteResponse, error)
|
||||
Get(ctx context.Context, id uuid.UUID) (*contract.DispositionRouteResponse, error)
|
||||
ListByFromDept(ctx context.Context, from uuid.UUID) (*contract.ListDispositionRoutesResponse, error)
|
||||
SetActive(ctx context.Context, id uuid.UUID, active bool) error
|
||||
ListGrouped(ctx context.Context) (*contract.ListDispositionRoutesGroupedResponse, error)
|
||||
ListAll(ctx context.Context) (*contract.ListDispositionRoutesDetailedResponse, error)
|
||||
}
|
||||
|
||||
type DispositionRouteHandler struct{ svc DispositionRouteService }
|
||||
|
||||
func NewDispositionRouteHandler(svc DispositionRouteService) *DispositionRouteHandler {
|
||||
return &DispositionRouteHandler{svc: svc}
|
||||
}
|
||||
|
||||
// Create handles both single and bulk route creation with upsert logic
|
||||
func (h *DispositionRouteHandler) Create(c *gin.Context) {
|
||||
var req contract.CreateDispositionRouteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body: " + err.Error(), Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate request
|
||||
if len(req.ToDepartmentIDs) == 0 {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "to_department_ids cannot be empty", Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
// If single route, use Create for backward compatibility
|
||||
if len(req.ToDepartmentIDs) == 1 {
|
||||
resp, err := h.svc.Create(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(201, contract.BuildSuccessResponse(resp))
|
||||
return
|
||||
}
|
||||
|
||||
// For multiple routes, use bulk create/update
|
||||
bulkResp, err := h.svc.CreateOrUpdate(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(201, contract.BuildSuccessResponse(bulkResp))
|
||||
}
|
||||
|
||||
// BulkCreateOrUpdate explicitly handles bulk create/update operations
|
||||
func (h *DispositionRouteHandler) BulkCreateOrUpdate(c *gin.Context) {
|
||||
var req contract.CreateDispositionRouteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body: " + err.Error(), Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
// Validate request
|
||||
if len(req.ToDepartmentIDs) == 0 {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "to_department_ids cannot be empty", Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.CreateOrUpdate(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *DispositionRouteHandler) Update(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
var req contract.UpdateDispositionRouteRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.Update(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *DispositionRouteHandler) Get(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.Get(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *DispositionRouteHandler) ListByFromDept(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
|
||||
resp, err := h.svc.ListByFromDept(c.Request.Context(), appCtx.DepartmentID)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *DispositionRouteHandler) SetActive(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
toggle := c.Query("active")
|
||||
active := toggle != "false"
|
||||
if err := h.svc.SetActive(c.Request.Context(), id, active); err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, &contract.SuccessResponse{Message: "updated"})
|
||||
}
|
||||
|
||||
// ListGrouped returns all disposition routes grouped by from_department_id
|
||||
func (h *DispositionRouteHandler) ListGrouped(c *gin.Context) {
|
||||
resp, err := h.svc.ListGrouped(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// ListAll returns all disposition routes with department details
|
||||
func (h *DispositionRouteHandler) ListAll(c *gin.Context) {
|
||||
resp, err := h.svc.ListAll(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"go-backend-template/internal/constants"
|
||||
"go-backend-template/internal/contract"
|
||||
"go-backend-template/internal/logger"
|
||||
"go-backend-template/internal/util"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type DukcapilHandler struct {
|
||||
dukcapilService DukcapilService
|
||||
}
|
||||
|
||||
func NewDukcapilHandler(dukcapilService DukcapilService) *DukcapilHandler {
|
||||
return &DukcapilHandler{dukcapilService: dukcapilService}
|
||||
}
|
||||
|
||||
// FaceMatch handles POST /api/v1/dukcapil/face-match (1:N face recognition).
|
||||
func (h *DukcapilHandler) FaceMatch(c *gin.Context) {
|
||||
var req contract.FaceMatchRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("DukcapilHandler::FaceMatch -> request binding failed")
|
||||
h.sendValidationError(c, "Invalid request body", constants.MalformedFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
if strings.TrimSpace(req.TransactionID) == "" {
|
||||
h.sendValidationError(c, "transaction_id is required", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(req.TransactionSource) == "" {
|
||||
h.sendValidationError(c, "transaction_source is required", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(req.Threshold) == "" {
|
||||
h.sendValidationError(c, "threshold is required", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
if strings.TrimSpace(req.Image) == "" {
|
||||
h.sendValidationError(c, "image is required (base64-encoded)", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
res, err := h.dukcapilService.FaceMatch(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
logger.FromContext(c.Request.Context()).WithError(err).Error("DukcapilHandler::FaceMatch -> upstream call failed")
|
||||
c.JSON(http.StatusBadGateway, &contract.ErrorResponse{
|
||||
Error: "upstream_error",
|
||||
Message: err.Error(),
|
||||
Code: http.StatusBadGateway,
|
||||
Details: map[string]interface{}{"entity": constants.DukcapilHandlerEntity},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c.Request.Context()).Infof("DukcapilHandler::FaceMatch -> tid=%s errorCode=%s matches=%d", res.TID, res.ErrorCode, len(res.Matches))
|
||||
util.HandleResponse(c.Writer, c.Request, contract.BuildSuccessResponse(res), "DukcapilHandler::FaceMatch")
|
||||
}
|
||||
|
||||
func (h *DukcapilHandler) sendValidationError(c *gin.Context, message, code string) {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "validation_error",
|
||||
Message: message,
|
||||
Code: http.StatusBadRequest,
|
||||
Details: map[string]interface{}{
|
||||
"error_code": code,
|
||||
"entity": constants.DukcapilHandlerEntity,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go-backend-template/internal/contract"
|
||||
)
|
||||
|
||||
type DukcapilService interface {
|
||||
FaceMatch(ctx context.Context, req *contract.FaceMatchRequest) (*contract.FaceMatchResponse, error)
|
||||
}
|
||||
@@ -1,105 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
"eslogad-be/internal/appcontext"
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type FileService interface {
|
||||
UploadProfileAvatar(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, error)
|
||||
UploadDocument(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, string, error)
|
||||
UploadDocumentFinal(ctx context.Context, userID uuid.UUID, filename string, content []byte, contentType string) (string, string, error)
|
||||
}
|
||||
|
||||
type FileHandler struct {
|
||||
service FileService
|
||||
}
|
||||
|
||||
func NewFileHandler(service FileService) *FileHandler {
|
||||
return &FileHandler{service: service}
|
||||
}
|
||||
|
||||
func (h *FileHandler) UploadProfileAvatar(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
if appCtx.UserID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, &contract.ErrorResponse{Error: "Unauthorized", Code: http.StatusUnauthorized})
|
||||
return
|
||||
}
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "file is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
content, err := io.ReadAll(io.LimitReader(file, 10<<20))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "failed to read file", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
ct := header.Header.Get("Content-Type")
|
||||
url, err := h.service.UploadProfileAvatar(c.Request.Context(), appCtx.UserID, header.Filename, content, ct)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{"url": url}))
|
||||
}
|
||||
|
||||
func (h *FileHandler) UploadDocument(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
if appCtx.UserID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, &contract.ErrorResponse{Error: "Unauthorized", Code: http.StatusUnauthorized})
|
||||
return
|
||||
}
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "file is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
content, err := io.ReadAll(io.LimitReader(file, 20<<20))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "failed to read file", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
ct := header.Header.Get("Content-Type")
|
||||
url, key, err := h.service.UploadDocument(c.Request.Context(), appCtx.UserID, header.Filename, content, ct)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{"url": url, "key": key}))
|
||||
}
|
||||
|
||||
func (h *FileHandler) UploadDocumentFinal(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
if appCtx.UserID == uuid.Nil {
|
||||
c.JSON(http.StatusUnauthorized, &contract.ErrorResponse{Error: "Unauthorized", Code: http.StatusUnauthorized})
|
||||
return
|
||||
}
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "file is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
content, err := io.ReadAll(io.LimitReader(file, 20<<20))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "failed to read file", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
ct := header.Header.Get("Content-Type")
|
||||
url, key, err := h.service.UploadDocumentFinal(c.Request.Context(), appCtx.UserID, header.Filename, content, ct)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(map[string]string{"url": url, "key": key}))
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"eslogad-be/internal/logger"
|
||||
"go-backend-template/internal/logger"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
@@ -1,509 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/appcontext"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type LetterService interface {
|
||||
CreateIncomingLetter(ctx context.Context, req *contract.CreateIncomingLetterRequest) (*contract.IncomingLetterResponse, error)
|
||||
GetIncomingLetterByID(ctx context.Context, id uuid.UUID) (*contract.IncomingLetterResponse, error)
|
||||
ListIncomingLetters(ctx context.Context, req *contract.ListIncomingLettersRequest) (*contract.ListIncomingLettersResponse, error)
|
||||
SearchIncomingLetters(ctx context.Context, req *contract.SearchIncomingLettersRequest) (*contract.SearchIncomingLettersResponse, error)
|
||||
GetLetterUnreadCounts(ctx context.Context) (*contract.LetterUnreadCountResponse, error)
|
||||
MarkIncomingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error)
|
||||
MarkOutgoingLetterAsRead(ctx context.Context, letterID uuid.UUID) (*contract.MarkLetterReadResponse, error)
|
||||
UpdateIncomingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateIncomingLetterRequest) (*contract.IncomingLetterResponse, error)
|
||||
SoftDeleteIncomingLetter(ctx context.Context, id uuid.UUID) error
|
||||
BulkSoftDeleteIncomingLetters(ctx context.Context, ids []uuid.UUID) error
|
||||
BulkArchiveIncomingLetters(ctx context.Context, letterIDs []uuid.UUID) (*contract.BulkArchiveLettersResponse, error)
|
||||
ArchiveIncomingLetter(ctx context.Context, letterID uuid.UUID) error
|
||||
|
||||
CreateDispositions(ctx context.Context, req *contract.CreateLetterDispositionRequest) (*contract.ListDispositionsResponse, error)
|
||||
GetEnhancedDispositionsByLetter(ctx context.Context, letterID uuid.UUID) (*contract.ListEnhancedDispositionsResponse, error)
|
||||
|
||||
CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error)
|
||||
UpdateDiscussion(ctx context.Context, letterID uuid.UUID, discussionID uuid.UUID, req *contract.UpdateLetterDiscussionRequest) (*contract.LetterDiscussionResponse, error)
|
||||
|
||||
GetDepartmentDispositionStatus(ctx context.Context, req *contract.GetDepartmentDispositionStatusRequest) (*contract.ListDepartmentDispositionStatusResponse, error)
|
||||
UpdateDispositionStatus(ctx context.Context, req *contract.UpdateDispositionStatusRequest) (*contract.DepartmentDispositionStatusResponse, error)
|
||||
|
||||
GetLetterCTA(ctx context.Context, letterID uuid.UUID) (*contract.LetterCTAResponse, error)
|
||||
}
|
||||
|
||||
type LetterHandler struct {
|
||||
svc LetterService
|
||||
}
|
||||
|
||||
func NewLetterHandler(svc LetterService) *LetterHandler {
|
||||
return &LetterHandler{svc: svc}
|
||||
}
|
||||
|
||||
// Helper functions for common patterns
|
||||
func (h *LetterHandler) parseUUID(c *gin.Context, param string) (uuid.UUID, bool) {
|
||||
id, err := uuid.Parse(c.Param(param))
|
||||
if err != nil {
|
||||
h.respondError(c, http.StatusBadRequest, "invalid "+param)
|
||||
return uuid.Nil, false
|
||||
}
|
||||
return id, true
|
||||
}
|
||||
|
||||
func (h *LetterHandler) bindJSON(c *gin.Context, req interface{}) bool {
|
||||
if err := c.ShouldBindJSON(req); err != nil {
|
||||
h.respondError(c, http.StatusBadRequest, "invalid request body")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *LetterHandler) bindQuery(c *gin.Context, req interface{}) bool {
|
||||
if err := c.ShouldBindQuery(req); err != nil {
|
||||
h.respondError(c, http.StatusBadRequest, "invalid query parameters")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *LetterHandler) respondError(c *gin.Context, code int, message string) {
|
||||
c.JSON(code, &contract.ErrorResponse{
|
||||
Error: message,
|
||||
Code: code,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *LetterHandler) respondSuccess(c *gin.Context, code int, data interface{}) {
|
||||
c.JSON(code, contract.BuildSuccessResponse(data))
|
||||
}
|
||||
|
||||
func (h *LetterHandler) handleServiceError(c *gin.Context, err error) {
|
||||
if err != nil {
|
||||
h.respondError(c, http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (h *LetterHandler) CreateIncomingLetter(c *gin.Context) {
|
||||
var req contract.CreateIncomingLetterRequest
|
||||
if !h.bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.CreateIncomingLetter(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusCreated, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) GetIncomingLetter(c *gin.Context) {
|
||||
id, ok := h.parseUUID(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetIncomingLetterByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) ListIncomingLetters(c *gin.Context) {
|
||||
req := h.parseListRequest(c)
|
||||
|
||||
resp, err := h.svc.ListIncomingLetters(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) GetLetterUnreadCounts(c *gin.Context) {
|
||||
resp, err := h.svc.GetLetterUnreadCounts(c.Request.Context())
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) MarkIncomingLetterAsRead(c *gin.Context) {
|
||||
id, ok := h.parseUUID(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.MarkIncomingLetterAsRead(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) MarkOutgoingLetterAsRead(c *gin.Context) {
|
||||
id, ok := h.parseUUID(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.MarkOutgoingLetterAsRead(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) parseListRequest(c *gin.Context) *contract.ListIncomingLettersRequest {
|
||||
//appCtx := appcontext.FromGinContext(c)
|
||||
//departmentID := appCtx.DepartmentID
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))
|
||||
|
||||
// Ensure valid pagination values
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if limit < 1 {
|
||||
limit = 10
|
||||
}
|
||||
if limit > 100 {
|
||||
limit = 100
|
||||
}
|
||||
|
||||
req := &contract.ListIncomingLettersRequest{
|
||||
Page: page,
|
||||
Limit: limit,
|
||||
}
|
||||
|
||||
if status := c.Query("status"); status != "" {
|
||||
req.Status = &status
|
||||
}
|
||||
|
||||
if query := c.Query("q"); query != "" {
|
||||
req.Query = &query
|
||||
}
|
||||
|
||||
// Parse is_read filter
|
||||
if isReadStr := c.Query("is_read"); isReadStr != "" {
|
||||
isRead := isReadStr == "true" || isReadStr == "1"
|
||||
req.IsRead = &isRead
|
||||
}
|
||||
|
||||
// Parse priority_ids filter
|
||||
if priorityIDsStr := c.QueryArray("priority_ids[]"); len(priorityIDsStr) > 0 {
|
||||
priorityIDs := make([]uuid.UUID, 0, len(priorityIDsStr))
|
||||
for _, idStr := range priorityIDsStr {
|
||||
if id, err := uuid.Parse(idStr); err == nil {
|
||||
priorityIDs = append(priorityIDs, id)
|
||||
}
|
||||
}
|
||||
req.PriorityIDs = priorityIDs
|
||||
} else if priorityIDStr := c.Query("priority_ids"); priorityIDStr != "" {
|
||||
// Also support comma-separated format
|
||||
idStrs := strings.Split(priorityIDStr, ",")
|
||||
priorityIDs := make([]uuid.UUID, 0, len(idStrs))
|
||||
for _, idStr := range idStrs {
|
||||
if id, err := uuid.Parse(strings.TrimSpace(idStr)); err == nil {
|
||||
priorityIDs = append(priorityIDs, id)
|
||||
}
|
||||
}
|
||||
req.PriorityIDs = priorityIDs
|
||||
}
|
||||
|
||||
// Parse is_dispositioned filter
|
||||
if isDispositionedStr := c.Query("is_dispositioned"); isDispositionedStr != "" {
|
||||
isDispositioned := isDispositionedStr == "true" || isDispositionedStr == "1"
|
||||
req.IsDispositioned = &isDispositioned
|
||||
}
|
||||
|
||||
// Parse is_archived filter
|
||||
if isArchivedStr := c.Query("is_archived"); isArchivedStr != "" {
|
||||
isArchived := isArchivedStr == "true" || isArchivedStr == "1"
|
||||
req.IsArchived = &isArchived
|
||||
}
|
||||
|
||||
//req.DepartmentID = &departmentID
|
||||
|
||||
return req
|
||||
}
|
||||
|
||||
func (h *LetterHandler) UpdateIncomingLetter(c *gin.Context) {
|
||||
id, ok := h.parseUUID(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.UpdateIncomingLetterRequest
|
||||
if !h.bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.UpdateIncomingLetter(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) DeleteIncomingLetter(c *gin.Context) {
|
||||
id, ok := h.parseUUID(c, "id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.SoftDeleteIncomingLetter(c.Request.Context(), id); err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, &contract.SuccessResponse{
|
||||
Message: "Letter deleted successfully",
|
||||
})
|
||||
}
|
||||
|
||||
func (h *LetterHandler) BulkDeleteIncomingLetters(c *gin.Context) {
|
||||
var req struct {
|
||||
IDs []uuid.UUID `json:"ids" binding:"required,min=1"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, contract.ErrorResponse{
|
||||
Message: "Invalid request body",
|
||||
Error: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.BulkSoftDeleteIncomingLetters(c.Request.Context(), req.IDs); err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, &contract.SuccessResponse{
|
||||
Message: fmt.Sprintf("%d letters deleted successfully", len(req.IDs)),
|
||||
})
|
||||
}
|
||||
|
||||
func (h *LetterHandler) CreateDispositions(c *gin.Context) {
|
||||
var req contract.CreateLetterDispositionRequest
|
||||
if !h.bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
req.FromDepartment = appCtx.DepartmentID
|
||||
|
||||
resp, err := h.svc.CreateDispositions(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusCreated, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) GetEnhancedDispositionsByLetter(c *gin.Context) {
|
||||
letterID, ok := h.parseUUID(c, "letter_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetEnhancedDispositionsByLetter(c.Request.Context(), letterID)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) CreateDiscussion(c *gin.Context) {
|
||||
letterID, ok := h.parseUUID(c, "letter_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.CreateLetterDiscussionRequest
|
||||
if !h.bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.CreateDiscussion(c.Request.Context(), letterID, &req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusCreated, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) UpdateDiscussion(c *gin.Context) {
|
||||
letterID, ok := h.parseUUID(c, "letter_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
discussionID, ok := h.parseUUID(c, "discussion_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.UpdateLetterDiscussionRequest
|
||||
if !h.bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.UpdateDiscussion(c.Request.Context(), letterID, discussionID, &req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) SearchIncomingLetters(c *gin.Context) {
|
||||
var req contract.SearchIncomingLettersRequest
|
||||
if !h.bindQuery(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.Limit <= 0 {
|
||||
req.Limit = 10
|
||||
}
|
||||
if req.SortOrder == "" {
|
||||
req.SortOrder = "desc"
|
||||
}
|
||||
if req.SortBy == "" {
|
||||
req.SortBy = "created_at"
|
||||
}
|
||||
|
||||
resp, err := h.svc.SearchIncomingLetters(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) GetDepartmentDispositionStatus(c *gin.Context) {
|
||||
letterID, ok := h.parseUUID(c, "letter_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
departmentID := appcontext.FromGinContext(c.Request.Context()).DepartmentID
|
||||
|
||||
req := &contract.GetDepartmentDispositionStatusRequest{
|
||||
LetterIncomingID: letterID,
|
||||
DepartmentID: departmentID,
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetDepartmentDispositionStatus(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) UpdateDispositionStatus(c *gin.Context) {
|
||||
letterID, ok := h.parseUUID(c, "letter_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.UpdateDispositionStatusRequest
|
||||
if !h.bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
req.LetterIncomingID = letterID
|
||||
|
||||
resp, err := h.svc.UpdateDispositionStatus(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) GetLetterCTA(c *gin.Context) {
|
||||
letterID, ok := h.parseUUID(c, "letter_id")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetLetterCTA(c.Request.Context(), letterID)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) BulkArchiveIncomingLetters(c *gin.Context) {
|
||||
var req contract.BulkArchiveLettersRequest
|
||||
if !h.bindJSON(c, &req) {
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.LetterIDs) == 0 {
|
||||
h.respondError(c, http.StatusBadRequest, "at least one letter ID is required")
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.BulkArchiveIncomingLetters(c.Request.Context(), req.LetterIDs)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *LetterHandler) ArchiveIncomingLetter(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
h.respondError(c, http.StatusBadRequest, "invalid id")
|
||||
return
|
||||
}
|
||||
|
||||
err = h.svc.ArchiveIncomingLetter(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
h.handleServiceError(c, err)
|
||||
return
|
||||
}
|
||||
|
||||
h.respondSuccess(c, http.StatusOK, &contract.SuccessResponse{Message: "archived"})
|
||||
}
|
||||
@@ -1,640 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/appcontext"
|
||||
"eslogad-be/internal/contract"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type LetterOutgoingService interface {
|
||||
CreateOutgoingLetter(ctx context.Context, req *contract.CreateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error)
|
||||
GetOutgoingLetterByID(ctx context.Context, id uuid.UUID) (*contract.OutgoingLetterResponse, error)
|
||||
ListOutgoingLetters(ctx context.Context, req *contract.ListOutgoingLettersRequest) (*contract.ListOutgoingLettersResponse, error)
|
||||
SearchOutgoingLetters(ctx context.Context, req *contract.SearchOutgoingLettersRequest) (*contract.SearchOutgoingLettersResponse, error)
|
||||
UpdateOutgoingLetter(ctx context.Context, id uuid.UUID, req *contract.UpdateOutgoingLetterRequest) (*contract.OutgoingLetterResponse, error)
|
||||
DeleteOutgoingLetter(ctx context.Context, id uuid.UUID) error
|
||||
BulkDeleteOutgoingLetters(ctx context.Context, ids []uuid.UUID) error
|
||||
|
||||
SubmitForApproval(ctx context.Context, letterID uuid.UUID) error
|
||||
ApproveOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.ApproveLetterRequest) error
|
||||
RejectOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.RejectLetterRequest) error
|
||||
ReviseOutgoingLetter(ctx context.Context, letterID uuid.UUID, req *contract.ReviseLetterRequest) error
|
||||
SendOutgoingLetter(ctx context.Context, letterID uuid.UUID) error
|
||||
ArchiveOutgoingLetter(ctx context.Context, letterID uuid.UUID) error
|
||||
|
||||
AddRecipients(ctx context.Context, letterID uuid.UUID, req *contract.AddRecipientsRequest) error
|
||||
UpdateRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID, req *contract.UpdateRecipientRequest) error
|
||||
RemoveRecipient(ctx context.Context, letterID uuid.UUID, recipientID uuid.UUID) error
|
||||
|
||||
AddAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error
|
||||
RemoveAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error
|
||||
|
||||
AddFinalAttachments(ctx context.Context, letterID uuid.UUID, req *contract.AddAttachmentsRequest) error
|
||||
RemoveFinalAttachment(ctx context.Context, letterID uuid.UUID, attachmentID uuid.UUID) error
|
||||
|
||||
CreateDiscussion(ctx context.Context, letterID uuid.UUID, req *contract.CreateDiscussionRequest) (*contract.DiscussionResponse, error)
|
||||
UpdateDiscussion(ctx context.Context, discussionID uuid.UUID, req *contract.UpdateDiscussionRequest) error
|
||||
DeleteDiscussion(ctx context.Context, discussionID uuid.UUID) error
|
||||
|
||||
GetLetterApprovalInfo(ctx context.Context, letterID uuid.UUID) (*contract.LetterApprovalInfoResponse, error)
|
||||
GetLetterApprovals(ctx context.Context, letterID uuid.UUID) (*contract.GetLetterApprovalsResponse, error)
|
||||
GetApprovalDiscussions(ctx context.Context, letterID uuid.UUID) (*contract.OutgoingLetterApprovalDiscussionsResponse, error)
|
||||
GetApprovalTimeline(ctx context.Context, letterID uuid.UUID) (*contract.ApprovalTimelineResponse, error)
|
||||
BulkArchiveOutgoingLetters(ctx context.Context, letterIDs []uuid.UUID) (*contract.BulkArchiveLettersResponse, error)
|
||||
}
|
||||
|
||||
type LetterOutgoingHandler struct {
|
||||
svc LetterOutgoingService
|
||||
}
|
||||
|
||||
func NewLetterOutgoingHandler(svc LetterOutgoingService) *LetterOutgoingHandler {
|
||||
return &LetterOutgoingHandler{svc: svc}
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) CreateOutgoingLetter(c *gin.Context) {
|
||||
var req contract.CreateOutgoingLetterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
ctx := c.Request.Context()
|
||||
req.UserID = appcontext.FromGinContext(ctx).UserID
|
||||
|
||||
resp, err := h.svc.CreateOutgoingLetter(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) GetOutgoingLetter(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetOutgoingLetterByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) ListOutgoingLetters(c *gin.Context) {
|
||||
var req contract.ListOutgoingLettersRequest
|
||||
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid query parameters", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
|
||||
if req.Limit <= 0 {
|
||||
req.Limit = 10
|
||||
}
|
||||
|
||||
if ids := c.QueryArray("priority_ids[]"); len(ids) > 0 {
|
||||
for _, s := range ids {
|
||||
if id, err := uuid.Parse(s); err == nil {
|
||||
req.PriorityIDs = append(req.PriorityIDs, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("[DEBUG] request: %v\n", req)
|
||||
fmt.Printf("[DEBUG] Raw query: %v\n", c.Request.URL.RawQuery)
|
||||
fmt.Printf("[DEBUG] Parsed form: %v\n", c.Request.URL.Query())
|
||||
|
||||
resp, err := h.svc.ListOutgoingLetters(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) UpdateOutgoingLetter(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.UpdateOutgoingLetterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.UpdateOutgoingLetter(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) DeleteOutgoingLetter(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.DeleteOutgoingLetter(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{Message: "deleted"}))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) BulkDeleteOutgoingLetters(c *gin.Context) {
|
||||
var req struct {
|
||||
IDs []uuid.UUID `json:"ids" binding:"required,min=1"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "Invalid request body",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.BulkDeleteOutgoingLetters(c.Request.Context(), req.IDs); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{
|
||||
Message: fmt.Sprintf("%d letters deleted successfully", len(req.IDs)),
|
||||
}))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) SubmitForApproval(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.SubmitForApproval(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "submitted for approval"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) ApproveOutgoingLetter(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.ApproveLetterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.ApproveOutgoingLetter(c.Request.Context(), id, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{Message: "approved"}))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) RejectOutgoingLetter(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.RejectLetterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.RejectOutgoingLetter(c.Request.Context(), id, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{Message: "rejected"}))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) ReviseOutgoingLetter(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.ReviseLetterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.ReviseOutgoingLetter(c.Request.Context(), id, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{Message: "revised"}))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) SendOutgoingLetter(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.SendOutgoingLetter(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "sent"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) ArchiveOutgoingLetter(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.ArchiveOutgoingLetter(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "archived"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) AddRecipients(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.AddRecipientsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.AddRecipients(c.Request.Context(), id, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "recipients added"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) UpdateRecipient(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
recipientID, err := uuid.Parse(c.Param("recipient_id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid recipient id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.UpdateRecipientRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.UpdateRecipient(c.Request.Context(), id, recipientID, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "recipient updated"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) RemoveRecipient(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
recipientID, err := uuid.Parse(c.Param("recipient_id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid recipient id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.RemoveRecipient(c.Request.Context(), id, recipientID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "recipient removed"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) AddAttachments(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.AddAttachmentsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.AddAttachments(c.Request.Context(), id, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{Message: "attachment added"}))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) RemoveAttachment(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
attachmentID, err := uuid.Parse(c.Param("attachment_id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid attachment id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.RemoveAttachment(c.Request.Context(), id, attachmentID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "attachment removed"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) AddFinalAttachments(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.AddAttachmentsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.AddFinalAttachments(c.Request.Context(), id, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(&contract.SuccessResponse{Message: "attachment added"}))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) RemoveFinalAttachment(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
attachmentID, err := uuid.Parse(c.Param("attachment_id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid attachment id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.RemoveFinalAttachment(c.Request.Context(), id, attachmentID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "attachment removed"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) CreateDiscussion(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid letter id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.CreateDiscussionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.CreateDiscussion(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) UpdateDiscussion(c *gin.Context) {
|
||||
discussionID, err := uuid.Parse(c.Param("discussion_id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid discussion id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.UpdateDiscussionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.UpdateDiscussion(c.Request.Context(), discussionID, &req); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "discussion updated"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) DeleteDiscussion(c *gin.Context) {
|
||||
discussionID, err := uuid.Parse(c.Param("discussion_id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid discussion id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.DeleteDiscussion(c.Request.Context(), discussionID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "discussion deleted"})
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) SearchOutgoingLetters(c *gin.Context) {
|
||||
var req contract.SearchOutgoingLettersRequest
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid query parameters", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Page <= 0 {
|
||||
req.Page = 1
|
||||
}
|
||||
if req.Limit <= 0 {
|
||||
req.Limit = 10
|
||||
}
|
||||
if req.SortOrder == "" {
|
||||
req.SortOrder = "desc"
|
||||
}
|
||||
if req.SortBy == "" {
|
||||
req.SortBy = "created_at"
|
||||
}
|
||||
|
||||
resp, err := h.svc.SearchOutgoingLetters(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) GetLetterApprovalInfo(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetLetterApprovalInfo(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// GetLetterApprovals returns all approvals and their status for a letter
|
||||
func (h *LetterOutgoingHandler) GetLetterApprovals(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetLetterApprovals(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, &contract.ErrorResponse{Error: "letter not found", Code: http.StatusNotFound})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// GetApprovalDiscussions returns both approvals and discussions for an outgoing letter
|
||||
func (h *LetterOutgoingHandler) GetApprovalDiscussions(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetApprovalDiscussions(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, &contract.ErrorResponse{Error: "letter not found", Code: http.StatusNotFound})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// GetApprovalTimeline returns a chronological timeline of approval and discussion events
|
||||
func (h *LetterOutgoingHandler) GetApprovalTimeline(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetApprovalTimeline(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, &contract.ErrorResponse{Error: "letter not found", Code: http.StatusNotFound})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *LetterOutgoingHandler) BulkArchiveOutgoingLetters(c *gin.Context) {
|
||||
var req contract.BulkArchiveLettersRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid request body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
if len(req.LetterIDs) == 0 {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "at least one letter ID is required", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.BulkArchiveOutgoingLetters(c.Request.Context(), req.LetterIDs)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: http.StatusInternalServerError})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type MasterService interface {
|
||||
CreateLabel(ctx context.Context, req *contract.CreateLabelRequest) (*contract.LabelResponse, error)
|
||||
UpdateLabel(ctx context.Context, id uuid.UUID, req *contract.UpdateLabelRequest) (*contract.LabelResponse, error)
|
||||
DeleteLabel(ctx context.Context, id uuid.UUID) error
|
||||
ListLabels(ctx context.Context) (*contract.ListLabelsResponse, error)
|
||||
|
||||
CreatePriority(ctx context.Context, req *contract.CreatePriorityRequest) (*contract.PriorityResponse, error)
|
||||
UpdatePriority(ctx context.Context, id uuid.UUID, req *contract.UpdatePriorityRequest) (*contract.PriorityResponse, error)
|
||||
DeletePriority(ctx context.Context, id uuid.UUID) error
|
||||
ListPriorities(ctx context.Context) (*contract.ListPrioritiesResponse, error)
|
||||
|
||||
CreateInstitution(ctx context.Context, req *contract.CreateInstitutionRequest) (*contract.InstitutionResponse, error)
|
||||
UpdateInstitution(ctx context.Context, id uuid.UUID, req *contract.UpdateInstitutionRequest) (*contract.InstitutionResponse, error)
|
||||
DeleteInstitution(ctx context.Context, id uuid.UUID) error
|
||||
ListInstitutions(ctx context.Context, req *contract.ListInstitutionsRequest) (*contract.ListInstitutionsResponse, error)
|
||||
|
||||
CreateDispositionAction(ctx context.Context, req *contract.CreateDispositionActionRequest) (*contract.DispositionActionResponse, error)
|
||||
UpdateDispositionAction(ctx context.Context, id uuid.UUID, req *contract.UpdateDispositionActionRequest) (*contract.DispositionActionResponse, error)
|
||||
DeleteDispositionAction(ctx context.Context, id uuid.UUID) error
|
||||
ListDispositionActions(ctx context.Context) (*contract.ListDispositionActionsResponse, error)
|
||||
|
||||
CreateDepartment(ctx context.Context, req *contract.CreateDepartmentRequest) (*contract.GetDepartmentResponse, error)
|
||||
GetDepartment(ctx context.Context, id uuid.UUID) (*contract.GetDepartmentResponse, error)
|
||||
UpdateDepartment(ctx context.Context, id uuid.UUID, req *contract.UpdateDepartmentRequest) (*contract.GetDepartmentResponse, error)
|
||||
DeleteDepartment(ctx context.Context, id uuid.UUID) error
|
||||
ListDepartments(ctx context.Context, req *contract.ListDepartmentsRequest) (*contract.ListDepartmentsResponse, error)
|
||||
GetOrganizationalChart(ctx context.Context, rootPath string) (*contract.OrganizationalChartResponse, error)
|
||||
GetOrganizationalChartByID(ctx context.Context, departmentID uuid.UUID) (*contract.OrganizationalChartResponse, error)
|
||||
}
|
||||
|
||||
type MasterHandler struct{ svc MasterService }
|
||||
|
||||
func NewMasterHandler(svc MasterService) *MasterHandler { return &MasterHandler{svc: svc} }
|
||||
|
||||
func (h *MasterHandler) CreateLabel(c *gin.Context) {
|
||||
var req contract.CreateLabelRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.CreateLabel(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *MasterHandler) UpdateLabel(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
var req contract.UpdateLabelRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.UpdateLabel(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
func (h *MasterHandler) DeleteLabel(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteLabel(c.Request.Context(), id); err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, &contract.SuccessResponse{Message: "deleted"})
|
||||
}
|
||||
func (h *MasterHandler) ListLabels(c *gin.Context) {
|
||||
resp, err := h.svc.ListLabels(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// Priorities
|
||||
func (h *MasterHandler) CreatePriority(c *gin.Context) {
|
||||
var req contract.CreatePriorityRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.CreatePriority(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(201, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
func (h *MasterHandler) UpdatePriority(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
var req contract.UpdatePriorityRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.UpdatePriority(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
func (h *MasterHandler) DeletePriority(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeletePriority(c.Request.Context(), id); err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, &contract.SuccessResponse{Message: "deleted"})
|
||||
}
|
||||
func (h *MasterHandler) ListPriorities(c *gin.Context) {
|
||||
resp, err := h.svc.ListPriorities(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// Institutions
|
||||
func (h *MasterHandler) CreateInstitution(c *gin.Context) {
|
||||
var req contract.CreateInstitutionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.CreateInstitution(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(201, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *MasterHandler) UpdateInstitution(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
var req contract.UpdateInstitutionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.UpdateInstitution(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *MasterHandler) DeleteInstitution(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteInstitution(c.Request.Context(), id); err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, &contract.SuccessResponse{Message: "deleted"})
|
||||
}
|
||||
|
||||
func (h *MasterHandler) ListInstitutions(c *gin.Context) {
|
||||
var req contract.ListInstitutionsRequest
|
||||
|
||||
if search := c.Query("search"); search != "" {
|
||||
req.Search = &search
|
||||
}
|
||||
|
||||
resp, err := h.svc.ListInstitutions(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// Disposition Actions
|
||||
func (h *MasterHandler) CreateDispositionAction(c *gin.Context) {
|
||||
var req contract.CreateDispositionActionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.CreateDispositionAction(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(201, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
func (h *MasterHandler) UpdateDispositionAction(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
var req contract.UpdateDispositionActionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.UpdateDispositionAction(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
func (h *MasterHandler) DeleteDispositionAction(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(400, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteDispositionAction(c.Request.Context(), id); err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, &contract.SuccessResponse{Message: "deleted"})
|
||||
}
|
||||
func (h *MasterHandler) ListDispositionActions(c *gin.Context) {
|
||||
resp, err := h.svc.ListDispositionActions(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(500, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(200, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// Departments
|
||||
func (h *MasterHandler) CreateDepartment(c *gin.Context) {
|
||||
var req contract.CreateDepartmentRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.CreateDepartment(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *MasterHandler) GetDepartment(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.GetDepartment(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, &contract.ErrorResponse{Error: "department not found", Code: 404})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *MasterHandler) UpdateDepartment(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
var req contract.UpdateDepartmentRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.UpdateDepartment(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, &contract.ErrorResponse{Error: "department not found", Code: 404})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *MasterHandler) DeleteDepartment(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteDepartment(c.Request.Context(), id); err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, &contract.ErrorResponse{Error: "department not found", Code: 404})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "department deleted successfully"})
|
||||
}
|
||||
|
||||
func (h *MasterHandler) ListDepartments(c *gin.Context) {
|
||||
var req contract.ListDepartmentsRequest
|
||||
|
||||
// Parse query parameters
|
||||
if err := c.ShouldBindQuery(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid query parameters", Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.ListDepartments(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *MasterHandler) GetOrganizationalChart(c *gin.Context) {
|
||||
// Get optional root path from query parameter
|
||||
rootPath := c.Query("root_path")
|
||||
|
||||
resp, err := h.svc.GetOrganizationalChart(c.Request.Context(), rootPath)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *MasterHandler) GetOrganizationalChartByID(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid department id", Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetOrganizationalChartByID(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusNotFound, &contract.ErrorResponse{Error: "department not found", Code: 404})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
"eslogad-be/internal/service"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type NotificationHandler struct {
|
||||
notificationService service.NotificationService
|
||||
}
|
||||
|
||||
func NewNotificationHandler(notificationService service.NotificationService) *NotificationHandler {
|
||||
return &NotificationHandler{
|
||||
notificationService: notificationService,
|
||||
}
|
||||
}
|
||||
|
||||
// TriggerNotification handles single notification trigger
|
||||
func (h *NotificationHandler) TriggerNotification(c *gin.Context) {
|
||||
var req contract.TriggerNotificationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.TriggerNotification(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": resp.Message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// BulkTriggerNotification handles bulk notification trigger
|
||||
func (h *NotificationHandler) BulkTriggerNotification(c *gin.Context) {
|
||||
var req contract.BulkTriggerNotificationRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.BulkTriggerNotification(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// GetSubscriber retrieves subscriber information
|
||||
func (h *NotificationHandler) GetSubscriber(c *gin.Context) {
|
||||
userIDStr := c.Param("userId")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid user ID"})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.GetSubscriber(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// UpdateSubscriberChannel updates subscriber channel credentials
|
||||
func (h *NotificationHandler) UpdateSubscriberChannel(c *gin.Context) {
|
||||
var req contract.UpdateSubscriberChannelRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.UpdateSubscriberChannel(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": resp.Message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// TriggerNotificationForCurrentUser triggers notification for the authenticated user
|
||||
func (h *NotificationHandler) TriggerNotificationForCurrentUser(c *gin.Context) {
|
||||
// Get current user ID from context (set by auth middleware)
|
||||
userIDInterface, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := userIDInterface.(uuid.UUID)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user ID in context"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
TemplateID string `json:"template_id" validate:"required"`
|
||||
TemplateData map[string]interface{} `json:"template_data,omitempty"`
|
||||
Overrides *contract.NotificationOverrides `json:"overrides,omitempty"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create trigger request with current user ID
|
||||
triggerReq := &contract.TriggerNotificationRequest{
|
||||
UserID: userID,
|
||||
TemplateID: req.TemplateID,
|
||||
TemplateData: req.TemplateData,
|
||||
Overrides: req.Overrides,
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.TriggerNotification(c.Request.Context(), triggerReq)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": resp.Message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// GetCurrentUserSubscriber retrieves subscriber information for the authenticated user
|
||||
func (h *NotificationHandler) GetCurrentUserSubscriber(c *gin.Context) {
|
||||
// Get current user ID from context (set by auth middleware)
|
||||
userIDInterface, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := userIDInterface.(uuid.UUID)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user ID in context"})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.GetSubscriber(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// UpdateCurrentUserSubscriberChannel updates channel credentials for the authenticated user
|
||||
func (h *NotificationHandler) UpdateCurrentUserSubscriberChannel(c *gin.Context) {
|
||||
// Get current user ID from context (set by auth middleware)
|
||||
userIDInterface, exists := c.Get("user_id")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "user not authenticated"})
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := userIDInterface.(uuid.UUID)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "invalid user ID in context"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Channel string `json:"channel" validate:"required,oneof=email sms push chat in_app"`
|
||||
Credentials map[string]interface{} `json:"credentials" validate:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// Create update request with current user ID
|
||||
updateReq := &contract.UpdateSubscriberChannelRequest{
|
||||
UserID: userID,
|
||||
Channel: req.Channel,
|
||||
Credentials: req.Credentials,
|
||||
}
|
||||
|
||||
resp, err := h.notificationService.UpdateSubscriberChannel(c.Request.Context(), updateReq)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if !resp.Success {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"success": false,
|
||||
"message": resp.Message,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
@@ -1,205 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/contract"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type OnlyOfficeService interface {
|
||||
ProcessCallback(ctx context.Context, documentKey string, req *contract.OnlyOfficeCallbackRequest) (*contract.OnlyOfficeCallbackResponse, error)
|
||||
GetEditorConfig(ctx context.Context, req *contract.GetEditorConfigRequest) (*contract.GetEditorConfigResponse, error)
|
||||
LockDocument(ctx context.Context, documentID uuid.UUID, userID uuid.UUID) error
|
||||
UnlockDocument(ctx context.Context, documentID uuid.UUID, userID uuid.UUID) error
|
||||
GetDocumentSession(ctx context.Context, documentKey string) (*contract.DocumentSession, error)
|
||||
GetOnlyOfficeConfig(ctx context.Context) (*contract.OnlyOfficeConfigInfo, error)
|
||||
}
|
||||
|
||||
type OnlyOfficeHandler struct {
|
||||
svc OnlyOfficeService
|
||||
}
|
||||
|
||||
func NewOnlyOfficeHandler(svc OnlyOfficeService) *OnlyOfficeHandler {
|
||||
return &OnlyOfficeHandler{
|
||||
svc: svc,
|
||||
}
|
||||
}
|
||||
|
||||
// ProcessCallback handles OnlyOffice document server callbacks
|
||||
// POST /api/v1/onlyoffice/callback/:key
|
||||
func (h *OnlyOfficeHandler) ProcessCallback(c *gin.Context) {
|
||||
documentKey := c.Param("key")
|
||||
if documentKey == "" {
|
||||
c.JSON(http.StatusBadRequest, &contract.OnlyOfficeCallbackResponse{Error: 1})
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.OnlyOfficeCallbackRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.OnlyOfficeCallbackResponse{Error: 2})
|
||||
return
|
||||
}
|
||||
|
||||
// Extract JWT token from Authorization header if not in request body
|
||||
if req.Token == "" {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader != "" {
|
||||
// Remove "Bearer " prefix if present
|
||||
if len(authHeader) > 7 && authHeader[:7] == "Bearer " {
|
||||
req.Token = authHeader[7:]
|
||||
} else {
|
||||
req.Token = authHeader
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OnlyOffice requires the key in the request to match the URL
|
||||
if req.Key != "" && req.Key != documentKey {
|
||||
c.JSON(http.StatusBadRequest, &contract.OnlyOfficeCallbackResponse{Error: 1})
|
||||
return
|
||||
}
|
||||
req.Key = documentKey
|
||||
|
||||
resp, err := h.svc.ProcessCallback(c.Request.Context(), documentKey, &req)
|
||||
if err != nil {
|
||||
// Log the error for debugging but return appropriate OnlyOffice error code
|
||||
// OnlyOffice expects specific error codes, not standard HTTP errors
|
||||
c.JSON(http.StatusOK, &contract.OnlyOfficeCallbackResponse{Error: 0})
|
||||
return
|
||||
}
|
||||
|
||||
// OnlyOffice expects 200 OK with error field in response
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
func (h *OnlyOfficeHandler) GetEditorConfig(c *gin.Context) {
|
||||
var req contract.GetEditorConfigRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: fmt.Sprintf("invalid request body: %v", err),
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetEditorConfig(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// LockDocument locks a document for editing
|
||||
// POST /api/v1/onlyoffice/lock/:id
|
||||
func (h *OnlyOfficeHandler) LockDocument(c *gin.Context) {
|
||||
documentID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "invalid document id",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get user ID from context
|
||||
userCtx := c.MustGet("user").(map[string]interface{})
|
||||
userID, err := uuid.Parse(userCtx["user_id"].(string))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "invalid user context",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.LockDocument(c.Request.Context(), documentID, userID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "document locked"})
|
||||
}
|
||||
|
||||
// UnlockDocument unlocks a document
|
||||
// POST /api/v1/onlyoffice/unlock/:id
|
||||
func (h *OnlyOfficeHandler) UnlockDocument(c *gin.Context) {
|
||||
documentID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "invalid document id",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Get user ID from context
|
||||
userCtx := c.MustGet("user").(map[string]interface{})
|
||||
userID, err := uuid.Parse(userCtx["user_id"].(string))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "invalid user context",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.svc.UnlockDocument(c.Request.Context(), documentID, userID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "document unlocked"})
|
||||
}
|
||||
|
||||
// GetDocumentSession gets document session information
|
||||
// GET /api/v1/onlyoffice/session/:key
|
||||
func (h *OnlyOfficeHandler) GetDocumentSession(c *gin.Context) {
|
||||
documentKey := c.Param("key")
|
||||
if documentKey == "" {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{
|
||||
Error: "document key is required",
|
||||
Code: http.StatusBadRequest,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
session, err := h.svc.GetDocumentSession(c.Request.Context(), documentKey)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(session))
|
||||
}
|
||||
|
||||
// GetOnlyOfficeConfig returns the OnlyOffice configuration
|
||||
// GET /api/v1/onlyoffice/config
|
||||
func (h *OnlyOfficeHandler) GetOnlyOfficeConfig(c *gin.Context) {
|
||||
config, err := h.svc.GetOnlyOfficeConfig(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{
|
||||
Error: err.Error(),
|
||||
Code: http.StatusInternalServerError,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(config))
|
||||
}
|
||||
@@ -1,182 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type RBACService interface {
|
||||
CreatePermission(ctx context.Context, req *contract.CreatePermissionRequest) (*contract.PermissionResponse, error)
|
||||
UpdatePermission(ctx context.Context, id uuid.UUID, req *contract.UpdatePermissionRequest) (*contract.PermissionResponse, error)
|
||||
DeletePermission(ctx context.Context, id uuid.UUID) error
|
||||
ListPermissions(ctx context.Context) (*contract.ListPermissionsResponse, error)
|
||||
|
||||
CreateRole(ctx context.Context, req *contract.CreateRoleRequest) (*contract.RoleWithPermissionsResponse, error)
|
||||
UpdateRole(ctx context.Context, id uuid.UUID, req *contract.UpdateRoleRequest) (*contract.RoleWithPermissionsResponse, error)
|
||||
DeleteRole(ctx context.Context, id uuid.UUID) error
|
||||
ListRoles(ctx context.Context) (*contract.ListRolesResponse, error)
|
||||
|
||||
// New methods
|
||||
GetPermissionsGrouped(ctx context.Context) (*contract.PermissionsGroupedResponse, error)
|
||||
CreateOrUpdateRole(ctx context.Context, req *contract.CreateOrUpdateRoleRequest) (*contract.RoleDetailResponse, error)
|
||||
GetRoleDetail(ctx context.Context, roleID uuid.UUID) (*contract.RoleDetailResponse, error)
|
||||
}
|
||||
|
||||
type RBACHandler struct{ svc RBACService }
|
||||
|
||||
func NewRBACHandler(svc RBACService) *RBACHandler { return &RBACHandler{svc: svc} }
|
||||
|
||||
func (h *RBACHandler) CreatePermission(c *gin.Context) {
|
||||
var req contract.CreatePermissionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: http.StatusBadRequest})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.CreatePermission(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *RBACHandler) UpdatePermission(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
var req contract.UpdatePermissionRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.UpdatePermission(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *RBACHandler) DeletePermission(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeletePermission(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "deleted"})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) ListPermissions(c *gin.Context) {
|
||||
resp, err := h.svc.ListPermissions(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *RBACHandler) CreateRole(c *gin.Context) {
|
||||
var req contract.CreateRoleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.CreateRole(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusCreated, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *RBACHandler) UpdateRole(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
var req contract.UpdateRoleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body", Code: 400})
|
||||
return
|
||||
}
|
||||
resp, err := h.svc.UpdateRole(c.Request.Context(), id, &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
func (h *RBACHandler) DeleteRole(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
if err := h.svc.DeleteRole(c.Request.Context(), id); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "deleted"})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) ListRoles(c *gin.Context) {
|
||||
resp, err := h.svc.ListRoles(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(resp))
|
||||
}
|
||||
|
||||
// New handlers for the required API endpoints
|
||||
func (h *RBACHandler) GetPermissionsGrouped(c *gin.Context) {
|
||||
resp, err := h.svc.GetPermissionsGrouped(c.Request.Context())
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *RBACHandler) CreateOrUpdateRole(c *gin.Context) {
|
||||
var req contract.CreateOrUpdateRoleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid body: " + err.Error(), Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.CreateOrUpdateRole(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
func (h *RBACHandler) GetRoleDetail(c *gin.Context) {
|
||||
id, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, &contract.ErrorResponse{Error: "invalid id", Code: 400})
|
||||
return
|
||||
}
|
||||
|
||||
resp, err := h.svc.GetRoleDetail(c.Request.Context(), id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, &contract.ErrorResponse{Error: err.Error(), Code: 500})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"eslogad-be/internal/constants"
|
||||
"eslogad-be/internal/contract"
|
||||
"eslogad-be/internal/logger"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type RepositoryAttachmentHandler struct {
|
||||
attachmentService RepositoryAttachmentService
|
||||
}
|
||||
|
||||
func NewRepositoryAttachmentHandler(attachmentService RepositoryAttachmentService) *RepositoryAttachmentHandler {
|
||||
return &RepositoryAttachmentHandler{
|
||||
attachmentService: attachmentService,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *RepositoryAttachmentHandler) CreateAttachment(c *gin.Context) {
|
||||
var req contract.CreateRepositoryAttachmentRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::CreateAttachment -> request binding failed")
|
||||
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
userResponse, err := h.attachmentService.CreateAttachment(c.Request.Context(), &req)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::CreateAttachment -> Failed to create user from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c).Infof("UserHandler::CreateUser -> Successfully created repository attachment = %+v", userResponse)
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(userResponse))
|
||||
}
|
||||
|
||||
func (h *RepositoryAttachmentHandler) DeleteAttachment(c *gin.Context) {
|
||||
attachmentIDStr := c.Param("id")
|
||||
attachmentID, err := uuid.Parse(attachmentIDStr)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::DeleteAttachment -> Invalid attachment id")
|
||||
h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.attachmentService.DeleteAttachment(c.Request.Context(), attachmentID)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::DeleteAttachment -> Failed to delete attachment from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c).Info("UserHandler::DeleteAttachment -> Successfully deleted attachment")
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "User deleted successfully"})
|
||||
}
|
||||
|
||||
func (h *RepositoryAttachmentHandler) GetAttachment(c *gin.Context) {
|
||||
attachmentIDStr := c.Param("id")
|
||||
attachmentID, err := uuid.Parse(attachmentIDStr)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::GetAttachment -> Invalid attachment ID")
|
||||
h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
attachmentResponse, err := h.attachmentService.GetById(c.Request.Context(), attachmentID)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::GetAttachment -> Failed to get attachment from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c).Infof("UserHandler::GetAttachment -> Successfully retrieved attachment = %+v", attachmentResponse)
|
||||
c.JSON(http.StatusOK, attachmentResponse)
|
||||
}
|
||||
func (h *RepositoryAttachmentHandler) ListAttachment(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
req := &contract.ListRepositoryAttachmentsRequest{
|
||||
Page: 1,
|
||||
Limit: 10,
|
||||
}
|
||||
|
||||
if page := c.Query("page"); page != "" {
|
||||
if p, err := strconv.Atoi(page); err == nil {
|
||||
req.Page = p
|
||||
}
|
||||
}
|
||||
|
||||
if limit := c.Query("limit"); limit != "" {
|
||||
if l, err := strconv.Atoi(limit); err == nil {
|
||||
req.Limit = l
|
||||
}
|
||||
}
|
||||
|
||||
attachmentsResponse, err := h.attachmentService.ListAttachment(ctx, req)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::ListUsers -> Failed to list users from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c).Infof("UserHandler::ListUsers -> Successfully listed users = %+v", attachmentsResponse)
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(attachmentsResponse))
|
||||
}
|
||||
|
||||
func (h *RepositoryAttachmentHandler) sendValidationErrorResponse(c *gin.Context, message string, errorCode string) {
|
||||
statusCode := constants.HttpErrorMap[errorCode]
|
||||
if statusCode == 0 {
|
||||
statusCode = http.StatusBadRequest
|
||||
}
|
||||
|
||||
errorResponse := &contract.ErrorResponse{
|
||||
Error: message,
|
||||
Code: statusCode,
|
||||
Details: map[string]interface{}{
|
||||
"error_code": errorCode,
|
||||
"entity": constants.UserValidatorEntity,
|
||||
},
|
||||
}
|
||||
c.JSON(statusCode, errorResponse)
|
||||
}
|
||||
|
||||
func (h *RepositoryAttachmentHandler) sendErrorResponse(c *gin.Context, message string, statusCode int) {
|
||||
errorResponse := &contract.ErrorResponse{
|
||||
Error: message,
|
||||
Code: statusCode,
|
||||
Details: map[string]interface{}{},
|
||||
}
|
||||
c.JSON(statusCode, errorResponse)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/contract"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type RepositoryAttachmentService interface {
|
||||
CreateAttachment(ctx context.Context, req *contract.CreateRepositoryAttachmentRequest) (*contract.RepositoryAttachmentsResponse, error)
|
||||
DeleteAttachment(ctx context.Context, id uuid.UUID) error
|
||||
GetById(ctx context.Context, id uuid.UUID) (*contract.RepositoryAttachmentsResponse, error)
|
||||
ListAttachment(ctx context.Context, req *contract.ListRepositoryAttachmentsRequest) (*contract.ListRepositoryAttachmentsResponse, error)
|
||||
}
|
||||
@@ -4,10 +4,9 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"eslogad-be/internal/appcontext"
|
||||
"eslogad-be/internal/constants"
|
||||
"eslogad-be/internal/contract"
|
||||
"eslogad-be/internal/logger"
|
||||
"go-backend-template/internal/constants"
|
||||
"go-backend-template/internal/contract"
|
||||
"go-backend-template/internal/logger"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
@@ -149,248 +148,32 @@ func (h *UserHandler) GetUser(c *gin.Context) {
|
||||
func (h *UserHandler) ListUsers(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
req := &contract.ListUsersRequest{
|
||||
Page: 1,
|
||||
Limit: 10,
|
||||
}
|
||||
page := 1
|
||||
limit := 10
|
||||
|
||||
if page := c.Query("page"); page != "" {
|
||||
if p, err := strconv.Atoi(page); err == nil {
|
||||
req.Page = p
|
||||
if pageStr := c.Query("page"); pageStr != "" {
|
||||
if p, err := strconv.Atoi(pageStr); err == nil {
|
||||
page = p
|
||||
}
|
||||
}
|
||||
|
||||
if limit := c.Query("limit"); limit != "" {
|
||||
if l, err := strconv.Atoi(limit); err == nil {
|
||||
req.Limit = l
|
||||
if limitStr := c.Query("limit"); limitStr != "" {
|
||||
if l, err := strconv.Atoi(limitStr); err == nil {
|
||||
limit = l
|
||||
}
|
||||
}
|
||||
|
||||
var roleParam *string
|
||||
if role := c.Query("role"); role != "" {
|
||||
roleParam = &role
|
||||
req.Role = &role
|
||||
}
|
||||
|
||||
if roleCode := c.Query("role_code"); roleCode != "" {
|
||||
req.RoleCode = &roleCode
|
||||
}
|
||||
|
||||
if req.RoleCode == nil && roleParam != nil {
|
||||
req.RoleCode = roleParam
|
||||
}
|
||||
|
||||
if search := c.Query("search"); search != "" {
|
||||
req.Search = &search
|
||||
}
|
||||
|
||||
if isActiveStr := c.Query("is_active"); isActiveStr != "" {
|
||||
if isActive, err := strconv.ParseBool(isActiveStr); err == nil {
|
||||
req.IsActive = &isActive
|
||||
}
|
||||
}
|
||||
|
||||
validationError, validationErrorCode := h.userValidator.ValidateListUsersRequest(req)
|
||||
if validationError != nil {
|
||||
logger.FromContext(c).WithError(validationError).Error("UserHandler::ListUsers -> request validation failed")
|
||||
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
usersResponse, err := h.userService.ListUsers(ctx, req)
|
||||
usersResponse, err := h.userService.GetUsers(ctx, page, limit)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::ListUsers -> Failed to list users from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c).Infof("UserHandler::ListUsers -> Successfully listed users = %+v", usersResponse)
|
||||
logger.FromContext(c).Infof("UserHandler::ListUsers -> Successfully listed users")
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(usersResponse))
|
||||
}
|
||||
|
||||
func (h *UserHandler) ChangePassword(c *gin.Context) {
|
||||
userIDStr := c.Param("id")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::ChangePassword -> Invalid user ID")
|
||||
h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
|
||||
if validationError != nil {
|
||||
logger.FromContext(c).WithError(validationError).Error("UserHandler::ChangePassword -> user ID validation failed")
|
||||
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.ChangePasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::ChangePassword -> request binding failed")
|
||||
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
validationError, validationErrorCode = h.userValidator.ValidateChangePasswordRequest(&req)
|
||||
if validationError != nil {
|
||||
logger.FromContext(c).WithError(validationError).Error("UserHandler::ChangePassword -> request validation failed")
|
||||
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.userService.ChangePassword(c.Request.Context(), userID, &req)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::ChangePassword -> Failed to change password from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c).Info("UserHandler::ChangePassword -> Successfully changed password")
|
||||
c.JSON(http.StatusOK, &contract.SuccessResponse{Message: "Password changed successfully"})
|
||||
}
|
||||
|
||||
func (h *UserHandler) ChangeUserPassword(c *gin.Context) {
|
||||
userIDStr := c.Param("id")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::ChangeUserPassword -> Invalid user ID")
|
||||
h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
|
||||
if validationError != nil {
|
||||
logger.FromContext(c).WithError(validationError).Error("UserHandler::ChangeUserPassword -> user ID validation failed")
|
||||
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
var req contract.ChangeUserPasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::ChangeUserPassword -> request binding failed")
|
||||
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.userService.ChangeUserPassword(c.Request.Context(), userID, &req)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::ChangeUserPassword -> Failed to change password from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c).Info("UserHandler::ChangeUserPassword -> Successfully changed password")
|
||||
c.JSON(http.StatusOK, &contract.NewSuccessResponse{Success: true, Message: "Password changed successfully"})
|
||||
}
|
||||
|
||||
func (h *UserHandler) GetProfile(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
if appCtx.UserID == uuid.Nil {
|
||||
h.sendErrorResponse(c, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
profile, err := h.userService.GetProfile(c.Request.Context(), appCtx.UserID)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::GetProfile -> Failed to get profile")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(profile))
|
||||
}
|
||||
|
||||
func (h *UserHandler) UpdateProfile(c *gin.Context) {
|
||||
appCtx := appcontext.FromGinContext(c.Request.Context())
|
||||
if appCtx.UserID == uuid.Nil {
|
||||
h.sendErrorResponse(c, "Unauthorized", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
var req contract.UpdateUserProfileRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
h.sendValidationErrorResponse(c, "Invalid request body", constants.MissingFieldErrorCode)
|
||||
return
|
||||
}
|
||||
updated, err := h.userService.UpdateProfile(c.Request.Context(), appCtx.UserID, &req)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::UpdateProfile -> Failed to update profile")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(updated))
|
||||
}
|
||||
|
||||
func (h *UserHandler) ListTitles(c *gin.Context) {
|
||||
titles, err := h.userService.ListTitles(c.Request.Context())
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::ListTitles -> Failed to get titles from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c).Infof("UserHandler::ListTitles -> Successfully retrieved titles = %+v", titles)
|
||||
c.JSON(http.StatusOK, titles)
|
||||
}
|
||||
|
||||
func (h *UserHandler) GetUserProfile(c *gin.Context) {
|
||||
userIDStr := c.Param("id")
|
||||
userID, err := uuid.Parse(userIDStr)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::GetUserProfile -> Invalid user ID")
|
||||
h.sendValidationErrorResponse(c, "Invalid user ID", constants.MalformedFieldErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
validationError, validationErrorCode := h.userValidator.ValidateUserID(userID)
|
||||
if validationError != nil {
|
||||
logger.FromContext(c).WithError(validationError).Error("UserHandler::GetUserProfile -> user ID validation failed")
|
||||
h.sendValidationErrorResponse(c, validationError.Error(), validationErrorCode)
|
||||
return
|
||||
}
|
||||
|
||||
profile, err := h.userService.GetProfile(c.Request.Context(), userID)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::GetUserProfile -> Failed to get user profile from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
logger.FromContext(c).Infof("UserHandler::GetUserProfile -> Successfully retrieved user profile for user ID = %s", userID)
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(profile))
|
||||
}
|
||||
|
||||
func (h *UserHandler) GetActiveUsersForMention(c *gin.Context) {
|
||||
search := c.Query("search")
|
||||
limitStr := c.DefaultQuery("limit", "50")
|
||||
|
||||
limit, err := strconv.Atoi(limitStr)
|
||||
if err != nil || limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
if limit > 100 {
|
||||
limit = 100
|
||||
}
|
||||
|
||||
var searchPtr *string
|
||||
if search != "" {
|
||||
searchPtr = &search
|
||||
}
|
||||
|
||||
users, err := h.userService.GetActiveUsersForMention(c.Request.Context(), searchPtr, limit)
|
||||
if err != nil {
|
||||
logger.FromContext(c).WithError(err).Error("UserHandler::GetActiveUsersForMention -> Failed to get active users from service")
|
||||
h.sendErrorResponse(c, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
response := contract.MentionUsersResponse{
|
||||
Users: users,
|
||||
Count: len(users),
|
||||
}
|
||||
|
||||
logger.FromContext(c).Infof("UserHandler::GetActiveUsersForMention -> Successfully retrieved %d active users", len(users))
|
||||
|
||||
c.JSON(http.StatusOK, contract.BuildSuccessResponse(response))
|
||||
}
|
||||
|
||||
func (h *UserHandler) sendErrorResponse(c *gin.Context, message string, statusCode int) {
|
||||
errorResponse := &contract.ErrorResponse{
|
||||
Error: message,
|
||||
|
||||
@@ -2,7 +2,7 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"eslogad-be/internal/contract"
|
||||
"go-backend-template/internal/contract"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@@ -12,16 +12,5 @@ type UserService interface {
|
||||
UpdateUser(ctx context.Context, id uuid.UUID, req *contract.UpdateUserRequest) (*contract.UserResponse, error)
|
||||
DeleteUser(ctx context.Context, id uuid.UUID) error
|
||||
GetUserByID(ctx context.Context, id uuid.UUID) (*contract.UserResponse, error)
|
||||
GetUserByEmail(ctx context.Context, email string) (*contract.UserResponse, error)
|
||||
ListUsers(ctx context.Context, req *contract.ListUsersRequest) (*contract.ListUsersResponse, error)
|
||||
ChangePassword(ctx context.Context, userID uuid.UUID, req *contract.ChangePasswordRequest) error
|
||||
ChangeUserPassword(ctx context.Context, userID uuid.UUID, req *contract.ChangeUserPasswordRequest) error
|
||||
|
||||
GetProfile(ctx context.Context, userID uuid.UUID) (*contract.UserProfileResponse, error)
|
||||
UpdateProfile(ctx context.Context, userID uuid.UUID, req *contract.UpdateUserProfileRequest) (*contract.UserProfileResponse, error)
|
||||
|
||||
ListTitles(ctx context.Context) (*contract.ListTitlesResponse, error)
|
||||
|
||||
// Get active users for mention purposes
|
||||
GetActiveUsersForMention(ctx context.Context, search *string, limit int) ([]contract.UserResponse, error)
|
||||
GetUsers(ctx context.Context, page, limit int) (*contract.PaginatedUserResponse, error)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"eslogad-be/internal/contract"
|
||||
"go-backend-template/internal/contract"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
@@ -12,5 +12,4 @@ type UserValidator interface {
|
||||
ValidateListUsersRequest(req *contract.ListUsersRequest) (error, string)
|
||||
ValidateChangePasswordRequest(req *contract.ChangePasswordRequest) (error, string)
|
||||
ValidateUserID(userID uuid.UUID) (error, string)
|
||||
ValidateUpdateUserOutletRequest(req *contract.UpdateUserOutletRequest) (error, string)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user