feat:notification api

This commit is contained in:
Rama Priyanto 2026-01-26 04:23:17 +07:00
parent 3b31156856
commit 50d298a48a
13 changed files with 1201 additions and 0 deletions

View File

@ -0,0 +1,22 @@
package entity
import "time"
type Notifications struct {
ID uint64 `gorm:"primaryKey;autoIncrement;column:id" json:"id"`
SentTo int `gorm:"column:sent_to;not null;default:0" json:"sentTo"`
SendBy int `gorm:"column:send_by;not null;default:0" json:"sendBy"`
SendByName string `gorm:"column:send_by_name;type:varchar(150);not null;default:''" json:"sendByName"`
Message string `gorm:"column:message;type:text;not null;default:''" json:"message"`
IsRead bool `gorm:"column:is_read;not null;default:false" json:"isRead"`
IsActive bool `gorm:"column:is_active;not null;default:true" json:"isActive"`
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"createdAt"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updatedAt"`
}
func (Notifications) TableName() string {
return "notifications"
}

View File

@ -0,0 +1,15 @@
package controller
import "narasi-ahli-be/app/module/notifications/service"
type Controller struct {
Notifications NotificationsController
}
func NewController(
NotificationsService service.NotificationsService,
) *Controller {
return &Controller{
Notifications: NewNotificationsController(NotificationsService),
}
}

View File

@ -0,0 +1,163 @@
package controller
import (
"narasi-ahli-be/app/module/notifications/request"
"narasi-ahli-be/app/module/notifications/service"
"strconv"
"github.com/gofiber/fiber/v2"
utilRes "narasi-ahli-be/utils/response"
utilVal "narasi-ahli-be/utils/validator"
)
type notificationsController struct {
notificationsService service.NotificationsService
}
type NotificationsController interface {
Create(c *fiber.Ctx) error
ListBySentTo(c *fiber.Ctx) error
MarkRead(c *fiber.Ctx) error
Delete(c *fiber.Ctx) error
}
func NewNotificationsController(notificationsService service.NotificationsService) NotificationsController {
return &notificationsController{
notificationsService: notificationsService,
}
}
// Create Notification
// @Summary Create Notification
// @Description API untuk membuat notifikasi dan mengirim ke user berdasarkan sentTo
// @Tags Notifications
// @Security Bearer
// @Accept json
// @Produce json
// @Param payload body request.CreateNotificationRequest true "Create Notification Payload"
// @Success 200 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 404 {object} response.Response
// @Failure 422 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /notifications [post]
func (_i *notificationsController) Create(c *fiber.Ctx) error {
req := new(request.CreateNotificationRequest)
if err := utilVal.ParseAndValidate(c, req); err != nil {
return err
}
data, err := _i.notificationsService.Create(req.SentTo, req.SendBy, req.SendByName, req.Message)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Messages: utilRes.Messages{"Notification successfully created"},
Data: data,
})
}
// List Notification By SentTo
// @Summary List Notification By SentTo
// @Description API untuk mengambil list notifikasi berdasarkan penerima (sentTo)
// @Tags Notifications
// @Security Bearer
// @Param sentTo path int true "SentTo (Receiver User ID)"
// @Param page query int false "Page" default(1)
// @Param limit query int false "Limit" default(10)
// @Param isRead query bool false "Filter isRead (true/false)"
// @Success 200 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 404 {object} response.Response
// @Failure 422 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /notifications/{sentTo} [get]
func (_i *notificationsController) ListBySentTo(c *fiber.Ctx) error {
sentTo, err := strconv.Atoi(c.Params("sentTo"))
if err != nil {
return err
}
page, _ := strconv.Atoi(c.Query("page", "1"))
limit, _ := strconv.Atoi(c.Query("limit", "10"))
// optional filter isRead
var isRead *bool
if c.Query("isRead") != "" {
val := c.QueryBool("isRead", false)
isRead = &val
}
data, total, err := _i.notificationsService.ListBySentTo(sentTo, page, limit, isRead)
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Messages: utilRes.Messages{"Notifications list successfully retrieved"},
Data: fiber.Map{
"total": total,
"page": page,
"limit": limit,
"data": data,
},
})
}
// Mark Read Notification
// @Summary Mark Read Notification
// @Description API untuk mengubah status notifikasi menjadi sudah dibaca
// @Tags Notifications
// @Security Bearer
// @Param id path int true "Notification ID"
// @Success 200 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 404 {object} response.Response
// @Failure 422 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /notifications/{id}/read [put]
func (_i *notificationsController) MarkRead(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
if err != nil {
return err
}
err = _i.notificationsService.MarkRead(uint64(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Messages: utilRes.Messages{"Notification successfully marked as read"},
})
}
// Soft Delete Notification
// @Summary Soft Delete Notification
// @Description API untuk soft delete notifikasi (isActive=false)
// @Tags Notifications
// @Security Bearer
// @Param id path int true "Notification ID"
// @Success 200 {object} response.Response
// @Failure 401 {object} response.Response
// @Failure 404 {object} response.Response
// @Failure 422 {object} response.Response
// @Failure 500 {object} response.Response
// @Router /notifications/{id} [delete]
func (_i *notificationsController) Delete(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 0)
if err != nil {
return err
}
err = _i.notificationsService.Delete(uint64(id))
if err != nil {
return err
}
return utilRes.Resp(c, utilRes.Response{
Messages: utilRes.Messages{"Notification successfully deleted"},
})
}

View File

@ -0,0 +1,51 @@
package notifications
import (
"narasi-ahli-be/app/module/notifications/controller"
"narasi-ahli-be/app/module/notifications/repository"
"narasi-ahli-be/app/module/notifications/service"
"github.com/gofiber/fiber/v2"
"go.uber.org/fx"
)
// struct of NotificationRouter
type NotificationRouter struct {
App fiber.Router
Controller *controller.Controller
}
// register bulky of Notifications module
var NewNotificationsModule = fx.Options(
// register repository
fx.Provide(repository.NewNotificationsRepository),
// register service
fx.Provide(service.NewNotificationsService),
// register controller
fx.Provide(controller.NewController),
// register router
fx.Provide(NewNotificationRouter),
)
// init NotificationRouter
func NewNotificationRouter(fiber *fiber.App, controller *controller.Controller) *NotificationRouter {
return &NotificationRouter{
App: fiber,
Controller: controller,
}
}
// register routes of Notifications module
func (_i *NotificationRouter) RegisterNotificationRoutes() {
notificationsController := _i.Controller.Notifications
_i.App.Route("/notifications", func(router fiber.Router) {
router.Post("/", notificationsController.Create)
router.Get("/:sentTo", notificationsController.ListBySentTo)
router.Put("/:id/read", notificationsController.MarkRead)
router.Delete("/:id", notificationsController.Delete)
})
}

View File

@ -0,0 +1,85 @@
package repository
import (
"narasi-ahli-be/app/database"
"narasi-ahli-be/app/database/entity"
)
type notificationsRepository struct {
DB *database.Database
}
type NotificationsRepository interface {
Create(data *entity.Notifications) error
FindById(id uint64) (*entity.Notifications, error)
ListBySentTo(sentTo int, page int, limit int, isRead *bool) ([]entity.Notifications, int64, error)
MarkRead(id uint64) error
Delete(id uint64) error
}
func NewNotificationsRepository(db *database.Database) NotificationsRepository {
return &notificationsRepository{
DB: db,
}
}
func (_i *notificationsRepository) Create(data *entity.Notifications) error {
return _i.DB.DB.Create(data).Error
}
func (_i *notificationsRepository) FindById(id uint64) (*entity.Notifications, error) {
var notif entity.Notifications
err := _i.DB.DB.Where("id = ?", id).First(&notif).Error
if err != nil {
return nil, err
}
return &notif, nil
}
func (_i *notificationsRepository) ListBySentTo(sentTo int, page int, limit int, isRead *bool) ([]entity.Notifications, int64, error) {
var data []entity.Notifications
var total int64
if page <= 0 {
page = 1
}
if limit <= 0 {
limit = 10
}
q := _i.DB.DB.Model(&entity.Notifications{}).
Where("sent_to = ?", sentTo).
Where("is_active = ?", true)
if isRead != nil {
q = q.Where("is_read = ?", *isRead)
}
// count
if err := q.Count(&total).Error; err != nil {
return nil, 0, err
}
// pagination
offset := (page - 1) * limit
if err := q.Order("created_at DESC").
Limit(limit).
Offset(offset).
Find(&data).Error; err != nil {
return nil, 0, err
}
return data, total, nil
}
func (_i *notificationsRepository) MarkRead(id uint64) error {
return _i.DB.DB.Model(&entity.Notifications{}).
Where("id = ? AND is_active = true", id).
Update("is_read", true).Error
}
func (_i *notificationsRepository) Delete(id uint64) error {
return _i.DB.DB.Model(&entity.Notifications{}).
Where("id = ?", id).
Update("is_active", false).Error
}

View File

@ -0,0 +1,15 @@
package request
type CreateNotificationRequest struct {
SentTo int `json:"sentTo" validate:"required"`
SendBy int `json:"sendBy" validate:"required"`
SendByName string `json:"sendByName" validate:"required"`
Message string `json:"message" validate:"required"`
}
type ListNotificationQuery struct {
Page int `query:"page"`
Limit int `query:"limit"`
IsRead *bool `query:"isRead"`
IsActive *bool `query:"isActive"`
}

View File

@ -0,0 +1,15 @@
package response
import "time"
type NotificationResponse struct {
ID uint64 `json:"id"`
SentTo int `json:"sentTo"`
SendBy int `json:"sendBy"`
SendByName string `json:"sendByName"`
Message string `json:"message"`
IsRead bool `json:"isRead"`
IsActive bool `json:"isActive"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}

View File

@ -0,0 +1,107 @@
package service
import (
"errors"
"narasi-ahli-be/app/database/entity"
"narasi-ahli-be/app/module/notifications/repository"
"narasi-ahli-be/app/module/notifications/response"
)
type notificationsService struct {
Repo repository.NotificationsRepository
}
func NewNotificationsService(repo repository.NotificationsRepository) NotificationsService {
return &notificationsService{Repo: repo}
}
type NotificationsService interface {
Create(sentTo int, sendBy int, sendByName string, message string) (*response.NotificationResponse, error)
ListBySentTo(sentTo int, page int, limit int, isRead *bool) ([]response.NotificationResponse, int64, error)
MarkRead(id uint64) error
Delete(id uint64) error
}
func (s *notificationsService) Create(sentTo int, sendBy int, sendByName string, message string) (*response.NotificationResponse, error) {
if sentTo <= 0 {
return nil, errors.New("sentTo is required")
}
if sendBy <= 0 {
return nil, errors.New("sendBy is required")
}
if sendByName == "" {
return nil, errors.New("sendByName is required")
}
if message == "" {
return nil, errors.New("message is required")
}
data := entity.Notifications{
SentTo: sentTo,
SendBy: sendBy,
SendByName: sendByName,
Message: message,
IsRead: false,
IsActive: true,
}
if err := s.Repo.Create(&data); err != nil {
return nil, err
}
return &response.NotificationResponse{
ID: data.ID,
SentTo: data.SentTo,
SendBy: data.SendBy,
SendByName: data.SendByName,
Message: data.Message,
IsRead: data.IsRead,
IsActive: data.IsActive,
CreatedAt: data.CreatedAt,
UpdatedAt: data.UpdatedAt,
}, nil
}
func (s *notificationsService) ListBySentTo(sentTo int, page int, limit int, isRead *bool) ([]response.NotificationResponse, int64, error) {
if sentTo <= 0 {
return nil, 0, errors.New("sentTo is required")
}
rows, total, err := s.Repo.ListBySentTo(sentTo, page, limit, isRead)
if err != nil {
return nil, 0, err
}
result := make([]response.NotificationResponse, 0)
for _, item := range rows {
result = append(result, response.NotificationResponse{
ID: item.ID,
SentTo: item.SentTo,
SendBy: item.SendBy,
SendByName: item.SendByName,
Message: item.Message,
IsRead: item.IsRead,
IsActive: item.IsActive,
CreatedAt: item.CreatedAt,
UpdatedAt: item.UpdatedAt,
})
}
return result, total, nil
}
func (s *notificationsService) MarkRead(id uint64) error {
old, err := s.Repo.FindById(id)
if err != nil {
return err
}
if old.IsActive == false {
return errors.New("notification already deleted")
}
return s.Repo.MarkRead(id)
}
func (s *notificationsService) Delete(id uint64) error {
return s.Repo.Delete(id)
}

View File

@ -23,6 +23,7 @@ import (
"narasi-ahli-be/app/module/magazines" "narasi-ahli-be/app/module/magazines"
"narasi-ahli-be/app/module/master_menus" "narasi-ahli-be/app/module/master_menus"
"narasi-ahli-be/app/module/master_modules" "narasi-ahli-be/app/module/master_modules"
"narasi-ahli-be/app/module/notifications"
"narasi-ahli-be/app/module/provinces" "narasi-ahli-be/app/module/provinces"
"narasi-ahli-be/app/module/research_journals" "narasi-ahli-be/app/module/research_journals"
"narasi-ahli-be/app/module/subscription" "narasi-ahli-be/app/module/subscription"
@ -72,6 +73,7 @@ type Router struct {
WorkHistoryRouter *work_history.WorkHistoryRouter WorkHistoryRouter *work_history.WorkHistoryRouter
ResearchJournalsRouter *research_journals.ResearchJournalsRouter ResearchJournalsRouter *research_journals.ResearchJournalsRouter
AIChatFilesRouter *ai_chat_files.AiChatFilesRouter AIChatFilesRouter *ai_chat_files.AiChatFilesRouter
NotificationRouter *notifications.NotificationRouter
} }
@ -109,6 +111,8 @@ func NewRouter(
workHistoryRouter *work_history.WorkHistoryRouter, workHistoryRouter *work_history.WorkHistoryRouter,
researchJournalsRouter *research_journals.ResearchJournalsRouter, researchJournalsRouter *research_journals.ResearchJournalsRouter,
aiChatFilesRouter *ai_chat_files.AiChatFilesRouter, aiChatFilesRouter *ai_chat_files.AiChatFilesRouter,
notificationRouter *notifications.NotificationRouter,
) *Router { ) *Router {
return &Router{ return &Router{
App: fiber, App: fiber,
@ -143,6 +147,8 @@ func NewRouter(
WorkHistoryRouter: workHistoryRouter, WorkHistoryRouter: workHistoryRouter,
ResearchJournalsRouter: researchJournalsRouter, ResearchJournalsRouter: researchJournalsRouter,
AIChatFilesRouter: aiChatFilesRouter, AIChatFilesRouter: aiChatFilesRouter,
NotificationRouter: notificationRouter,
} }
} }
@ -187,5 +193,7 @@ func (r *Router) Register() {
r.WorkHistoryRouter.RegisterWorkHistoryRoutes() r.WorkHistoryRouter.RegisterWorkHistoryRoutes()
r.ResearchJournalsRouter.RegisterResearchJournalsRoutes() r.ResearchJournalsRouter.RegisterResearchJournalsRoutes()
r.AIChatFilesRouter.RegisterAiChatFilesRoutes() r.AIChatFilesRouter.RegisterAiChatFilesRoutes()
r.NotificationRouter.RegisterNotificationRoutes()
} }

View File

@ -12354,6 +12354,254 @@ const docTemplate = `{
} }
} }
}, },
"/notifications": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "API untuk membuat notifikasi dan mengirim ke user berdasarkan sentTo",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Notifications"
],
"summary": "Create Notification",
"parameters": [
{
"description": "Create Notification Payload",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.CreateNotificationRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/notifications/{id}": {
"delete": {
"security": [
{
"Bearer": []
}
],
"description": "API untuk soft delete notifikasi (isActive=false)",
"tags": [
"Notifications"
],
"summary": "Soft Delete Notification",
"parameters": [
{
"type": "integer",
"description": "Notification ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/notifications/{id}/read": {
"put": {
"security": [
{
"Bearer": []
}
],
"description": "API untuk mengubah status notifikasi menjadi sudah dibaca",
"tags": [
"Notifications"
],
"summary": "Mark Read Notification",
"parameters": [
{
"type": "integer",
"description": "Notification ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/notifications/{sentTo}": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "API untuk mengambil list notifikasi berdasarkan penerima (sentTo)",
"tags": [
"Notifications"
],
"summary": "List Notification By SentTo",
"parameters": [
{
"type": "integer",
"description": "SentTo (Receiver User ID)",
"name": "sentTo",
"in": "path",
"required": true
},
{
"type": "integer",
"default": 1,
"description": "Page",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Limit",
"name": "limit",
"in": "query"
},
{
"type": "boolean",
"description": "Filter isRead (true/false)",
"name": "isRead",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/provinces": { "/provinces": {
"get": { "get": {
"security": [ "security": [
@ -16725,6 +16973,29 @@ const docTemplate = `{
} }
} }
}, },
"request.CreateNotificationRequest": {
"type": "object",
"required": [
"message",
"sendBy",
"sendByName",
"sentTo"
],
"properties": {
"message": {
"type": "string"
},
"sendBy": {
"type": "integer"
},
"sendByName": {
"type": "string"
},
"sentTo": {
"type": "integer"
}
}
},
"request.CustomStaticPagesCreateRequest": { "request.CustomStaticPagesCreateRequest": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -12343,6 +12343,254 @@
} }
} }
}, },
"/notifications": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "API untuk membuat notifikasi dan mengirim ke user berdasarkan sentTo",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Notifications"
],
"summary": "Create Notification",
"parameters": [
{
"description": "Create Notification Payload",
"name": "payload",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/request.CreateNotificationRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/notifications/{id}": {
"delete": {
"security": [
{
"Bearer": []
}
],
"description": "API untuk soft delete notifikasi (isActive=false)",
"tags": [
"Notifications"
],
"summary": "Soft Delete Notification",
"parameters": [
{
"type": "integer",
"description": "Notification ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/notifications/{id}/read": {
"put": {
"security": [
{
"Bearer": []
}
],
"description": "API untuk mengubah status notifikasi menjadi sudah dibaca",
"tags": [
"Notifications"
],
"summary": "Mark Read Notification",
"parameters": [
{
"type": "integer",
"description": "Notification ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/notifications/{sentTo}": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "API untuk mengambil list notifikasi berdasarkan penerima (sentTo)",
"tags": [
"Notifications"
],
"summary": "List Notification By SentTo",
"parameters": [
{
"type": "integer",
"description": "SentTo (Receiver User ID)",
"name": "sentTo",
"in": "path",
"required": true
},
{
"type": "integer",
"default": 1,
"description": "Page",
"name": "page",
"in": "query"
},
{
"type": "integer",
"default": 10,
"description": "Limit",
"name": "limit",
"in": "query"
},
{
"type": "boolean",
"description": "Filter isRead (true/false)",
"name": "isRead",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"422": {
"description": "Unprocessable Entity",
"schema": {
"$ref": "#/definitions/response.Response"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.Response"
}
}
}
}
},
"/provinces": { "/provinces": {
"get": { "get": {
"security": [ "security": [
@ -16714,6 +16962,29 @@
} }
} }
}, },
"request.CreateNotificationRequest": {
"type": "object",
"required": [
"message",
"sendBy",
"sendByName",
"sentTo"
],
"properties": {
"message": {
"type": "string"
},
"sendBy": {
"type": "integer"
},
"sendByName": {
"type": "string"
},
"sentTo": {
"type": "integer"
}
}
},
"request.CustomStaticPagesCreateRequest": { "request.CustomStaticPagesCreateRequest": {
"type": "object", "type": "object",
"required": [ "required": [

View File

@ -588,6 +588,22 @@ definitions:
- id - id
- provId - provId
type: object type: object
request.CreateNotificationRequest:
properties:
message:
type: string
sendBy:
type: integer
sendByName:
type: string
sentTo:
type: integer
required:
- message
- sendBy
- sendByName
- sentTo
type: object
request.CustomStaticPagesCreateRequest: request.CustomStaticPagesCreateRequest:
properties: properties:
description: description:
@ -9308,6 +9324,165 @@ paths:
summary: Update MasterStatuses summary: Update MasterStatuses
tags: tags:
- Untags - Untags
/notifications:
post:
consumes:
- application/json
description: API untuk membuat notifikasi dan mengirim ke user berdasarkan sentTo
parameters:
- description: Create Notification Payload
in: body
name: payload
required: true
schema:
$ref: '#/definitions/request.CreateNotificationRequest'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.Response'
"404":
description: Not Found
schema:
$ref: '#/definitions/response.Response'
"422":
description: Unprocessable Entity
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- Bearer: []
summary: Create Notification
tags:
- Notifications
/notifications/{id}:
delete:
description: API untuk soft delete notifikasi (isActive=false)
parameters:
- description: Notification ID
in: path
name: id
required: true
type: integer
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.Response'
"404":
description: Not Found
schema:
$ref: '#/definitions/response.Response'
"422":
description: Unprocessable Entity
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- Bearer: []
summary: Soft Delete Notification
tags:
- Notifications
/notifications/{id}/read:
put:
description: API untuk mengubah status notifikasi menjadi sudah dibaca
parameters:
- description: Notification ID
in: path
name: id
required: true
type: integer
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.Response'
"404":
description: Not Found
schema:
$ref: '#/definitions/response.Response'
"422":
description: Unprocessable Entity
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- Bearer: []
summary: Mark Read Notification
tags:
- Notifications
/notifications/{sentTo}:
get:
description: API untuk mengambil list notifikasi berdasarkan penerima (sentTo)
parameters:
- description: SentTo (Receiver User ID)
in: path
name: sentTo
required: true
type: integer
- default: 1
description: Page
in: query
name: page
type: integer
- default: 10
description: Limit
in: query
name: limit
type: integer
- description: Filter isRead (true/false)
in: query
name: isRead
type: boolean
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.Response'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.Response'
"404":
description: Not Found
schema:
$ref: '#/definitions/response.Response'
"422":
description: Unprocessable Entity
schema:
$ref: '#/definitions/response.Response'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.Response'
security:
- Bearer: []
summary: List Notification By SentTo
tags:
- Notifications
/provinces: /provinces:
get: get:
description: API for getting all Provinces description: API for getting all Provinces

View File

@ -25,6 +25,7 @@ import (
"narasi-ahli-be/app/module/magazines" "narasi-ahli-be/app/module/magazines"
"narasi-ahli-be/app/module/master_menus" "narasi-ahli-be/app/module/master_menus"
"narasi-ahli-be/app/module/master_modules" "narasi-ahli-be/app/module/master_modules"
"narasi-ahli-be/app/module/notifications"
"narasi-ahli-be/app/module/provinces" "narasi-ahli-be/app/module/provinces"
"narasi-ahli-be/app/module/research_journals" "narasi-ahli-be/app/module/research_journals"
"narasi-ahli-be/app/module/subscription" "narasi-ahli-be/app/module/subscription"
@ -99,6 +100,8 @@ func main() {
work_history.NewWorkHistoryModule, work_history.NewWorkHistoryModule,
research_journals.NewResearchJournalsModule, research_journals.NewResearchJournalsModule,
ai_chat_files.NewAiChatFilesModule, ai_chat_files.NewAiChatFilesModule,
notifications.NewNotificationsModule,
// start aplication // start aplication
fx.Invoke(webserver.Start), fx.Invoke(webserver.Start),