Skip to content

Commit

Permalink
improvement: product recommendation with ai (#103)
Browse files Browse the repository at this point in the history
improvement: product recommendation with ai
  • Loading branch information
masnann authored Dec 14, 2023
2 parents b0cdc68 + 9c5aa72 commit c7d36a8
Show file tree
Hide file tree
Showing 28 changed files with 548 additions and 671 deletions.
25 changes: 13 additions & 12 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import (
hArticle "github.com/capstone-kelompok-7/backend-disappear/module/feature/article/handler"
rArticle "github.com/capstone-kelompok-7/backend-disappear/module/feature/article/repository"
sArticle "github.com/capstone-kelompok-7/backend-disappear/module/feature/article/service"
hChatbot "github.com/capstone-kelompok-7/backend-disappear/module/feature/assistant/handler"
rChatbot "github.com/capstone-kelompok-7/backend-disappear/module/feature/assistant/repository"
sChatbot "github.com/capstone-kelompok-7/backend-disappear/module/feature/assistant/service"
hAuth "github.com/capstone-kelompok-7/backend-disappear/module/feature/auth/handler"
rAuth "github.com/capstone-kelompok-7/backend-disappear/module/feature/auth/repository"
sAuth "github.com/capstone-kelompok-7/backend-disappear/module/feature/auth/service"
Expand All @@ -25,9 +28,6 @@ import (
hChallenge "github.com/capstone-kelompok-7/backend-disappear/module/feature/challenge/handler"
rChallenge "github.com/capstone-kelompok-7/backend-disappear/module/feature/challenge/repository"
sChallenge "github.com/capstone-kelompok-7/backend-disappear/module/feature/challenge/service"
hChatbot "github.com/capstone-kelompok-7/backend-disappear/module/feature/chatbot/handler"
rChatbot "github.com/capstone-kelompok-7/backend-disappear/module/feature/chatbot/repository"
sChatbot "github.com/capstone-kelompok-7/backend-disappear/module/feature/chatbot/service"
hDashboard "github.com/capstone-kelompok-7/backend-disappear/module/feature/dashboard/handler"
rDashboard "github.com/capstone-kelompok-7/backend-disappear/module/feature/dashboard/repository"
sDashboard "github.com/capstone-kelompok-7/backend-disappear/module/feature/dashboard/service"
Expand Down Expand Up @@ -90,8 +90,14 @@ func main() {
voucherService := sVoucher.NewVoucherService(voucherRepo, userService)
voucherHandler := hVoucher.NewVoucherHandler(voucherService)

mgodb := database.InitMongoDB(*initConfig)
var client = openai.NewClient(initConfig.OpenAiApiKey)
chatbotRepo := rChatbot.NewAssistantRepository(mgodb, db)
chatbotService := sChatbot.NewAssistantService(chatbotRepo, client, *initConfig)
chatbotHandler := hChatbot.NewAssistantHandler(chatbotService)

productRepo := repository.NewProductRepository(db)
productService := service.NewProductService(productRepo)
productService := service.NewProductService(productRepo, chatbotService)
productHandler := handler.NewProductHandler(productService)

categoryRepo := rCategory.NewCategoryRepository(db)
Expand Down Expand Up @@ -131,12 +137,6 @@ func main() {
voucherService, addressService, userService, cartService, fcmService)
orderHandler := hOrder.NewOrderHandler(orderService)

mgodb := database.InitMongoDB(*initConfig)
var client = openai.NewClient(initConfig.OpenAiApiKey)
chatbotRepo := rChatbot.NewChatbotRepository(mgodb)
chatbotService := sChatbot.NewChatbotService(chatbotRepo, client, *initConfig)
chatbotHandler := hChatbot.NewChatbotHandler(chatbotService)

dashboardRepo := rDashboard.NewDashboardRepository(db)
dashboardService := sDashboard.NewDashboardService(dashboardRepo, rdb)
dashboardHandler := hDashboard.NewDashboardHandler(dashboardService)
Expand All @@ -153,8 +153,9 @@ func main() {
e.Use(middlewares.ConfigureLogging())

e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, Disappear!")
return c.String(http.StatusOK, "Hello, Disappear! 🦄✨🍩")
})

routes.RouteUser(e, userHandler, jwtService, userService)
routes.RouteAuth(e, authHandler, jwtService, userService)
routes.RouteVoucher(e, voucherHandler, jwtService, userService)
Expand All @@ -167,7 +168,7 @@ func main() {
routes.RouteReview(e, reviewHandler, jwtService, userService)
routes.RouteCart(e, cartHandler, jwtService, userService)
routes.RouteOrder(e, orderHandler, jwtService, userService)
routes.RouteChatbot(e, chatbotHandler, jwtService, userService)
routes.RouteAssistant(e, chatbotHandler, jwtService, userService)
routes.RouteDashboard(e, dashboardHandler, jwtService, userService)
routes.RouteHomepage(e, homeHandler, jwtService, userService)
routes.RouteFcm(e, fcmHandler, jwtService, userService)
Expand Down
8 changes: 0 additions & 8 deletions module/entities/chatbot_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@ import (
"go.mongodb.org/mongo-driver/bson/primitive"
)

// type ChatModel struct {
// ID primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
// IdUser string `json:"id_user" form:"id_user"`
// Role string
// Text string `json:"text" form:"text"`
// CreatedAt time.Time
// }

type ChatModel struct {
ID primitive.ObjectID `json:"id,omitempty" bson:"_id,omitempty"`
UserID uint64 `json:"user_id" form:"user_id"`
Expand Down
43 changes: 21 additions & 22 deletions module/entities/users_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,27 @@ package entities
import "time"

type UserModels struct {
ID uint64 `gorm:"column:id;type:BIGINT UNSIGNED;primaryKey" json:"id"`
SocialID string `gorm:"column:social_id;type:VARCHAR(255)" json:"social_id"`
Provider string `gorm:"column:provider;type:VARCHAR(255)" json:"provider"`
Email string `gorm:"column:email;type:VARCHAR(255)" json:"email"`
Password string `gorm:"column:password;type:VARCHAR(255)" json:"password"`
Phone string `gorm:"column:phone;type:VARCHAR(255)" json:"phone"`
Role string `gorm:"column:role;type:VARCHAR(255)" json:"role"`
Name string `gorm:"column:name;type:VARCHAR(255)" json:"name"`
PhotoProfile string `gorm:"column:photo_profile;type:VARCHAR(255)" json:"photo_profile"`
TotalGram uint64 `gorm:"column:total_gram;type:BIGINT UNSIGNED" json:"total_gram"`
TotalChallenge uint64 `gorm:"column:total_challenge;type:BIGINT UNSIGNED" json:"total_challenge"`
Level string `gorm:"column:level;type:VARCHAR(255)" json:"level"`
Exp uint64 `gorm:"column:exp;type:BIGINT UNSIGNED" json:"exp"`
IsVerified bool `gorm:"column:is_verified;default:false" json:"is_verified"`
LastLogin time.Time `gorm:"column:last_login;type:timestamp;default:CURRENT_TIMESTAMP" json:"last_login"`
PreferredTopics string `gorm:"column:preferred_topics;type:TEXT" json:"preferred_topics"`
DeviceToken string `gorm:"column:device_token;type:VARCHAR(255)" json:"device_token"`
CreatedAt time.Time `gorm:"column:created_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt *time.Time `gorm:"column:deleted_at;type:TIMESTAMP NULL;index" json:"deleted_at"`
Address []AddressModels `gorm:"foreignKey:UserID" json:"addresses"`
Reviews []ReviewModels `gorm:"foreignKey:UserID" json:"reviews"`
ID uint64 `gorm:"column:id;type:BIGINT UNSIGNED;primaryKey" json:"id"`
SocialID string `gorm:"column:social_id;type:VARCHAR(255)" json:"social_id"`
Provider string `gorm:"column:provider;type:VARCHAR(255)" json:"provider"`
Email string `gorm:"column:email;type:VARCHAR(255)" json:"email"`
Password string `gorm:"column:password;type:VARCHAR(255)" json:"password"`
Phone string `gorm:"column:phone;type:VARCHAR(255)" json:"phone"`
Role string `gorm:"column:role;type:VARCHAR(255)" json:"role"`
Name string `gorm:"column:name;type:VARCHAR(255)" json:"name"`
PhotoProfile string `gorm:"column:photo_profile;type:VARCHAR(255)" json:"photo_profile"`
TotalGram uint64 `gorm:"column:total_gram;type:BIGINT UNSIGNED" json:"total_gram"`
TotalChallenge uint64 `gorm:"column:total_challenge;type:BIGINT UNSIGNED" json:"total_challenge"`
Level string `gorm:"column:level;type:VARCHAR(255)" json:"level"`
Exp uint64 `gorm:"column:exp;type:BIGINT UNSIGNED" json:"exp"`
IsVerified bool `gorm:"column:is_verified;default:false" json:"is_verified"`
LastLogin time.Time `gorm:"column:last_login;type:timestamp;default:CURRENT_TIMESTAMP" json:"last_login"`
DeviceToken string `gorm:"column:device_token;type:VARCHAR(255)" json:"device_token"`
CreatedAt time.Time `gorm:"column:created_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;type:timestamp DEFAULT CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt *time.Time `gorm:"column:deleted_at;type:TIMESTAMP NULL;index" json:"deleted_at"`
Address []AddressModels `gorm:"foreignKey:UserID" json:"addresses"`
Reviews []ReviewModels `gorm:"foreignKey:UserID" json:"reviews"`
}

type AddressModels struct {
Expand Down
4 changes: 2 additions & 2 deletions module/feature/article/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ func (h *ArticleHandler) GetUsersBookmark() echo.HandlerFunc {
}
}

func (h *ArticleHandler) GetArticlePreferences() echo.HandlerFunc {
func (h *ArticleHandler) GetAllArticleUser() echo.HandlerFunc {
return func(c echo.Context) error {
currentUser := c.Get("CurrentUser").(*entities.UserModels)
if currentUser.Role != "customer" {
Expand All @@ -282,7 +282,7 @@ func (h *ArticleHandler) GetArticlePreferences() echo.HandlerFunc {
case "terbanyak":
articles, totalItems, err = h.service.GetArticleMostViews(page, perPage)
default:
articles, totalItems, err = h.service.GetArticlePreferences(currentUser.ID, page, perPage)
articles, totalItems, err = h.service.GetAllArticleUser(page, perPage)
}
if err != nil {
return response.SendStatusInternalServerResponse(c, "Gagal mendapatkan artikel berdasarkan preferensi pengguna: "+err.Error())
Expand Down
6 changes: 3 additions & 3 deletions module/feature/article/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ type RepositoryArticleInterface interface {
GetUserBookmarkArticle(userID uint64) ([]*entities.ArticleBookmarkModels, error)
GetLatestArticle() ([]*entities.ArticleModels, error)
GetOldestArticle(page, perPage int) ([]*entities.ArticleModels, error)
FindAllByUserPreference(userID uint64, page, perPage int) ([]*entities.ArticleModels, error)
GetTotalArticleCount() (int64, error)
GetArticleAlphabet(page, perPage int) ([]*entities.ArticleModels, error)
GetArticleMostViews(page, perPage int) ([]*entities.ArticleModels, error)
GetOtherArticle() ([]*entities.ArticleModels, error)
SearchArticlesWithDateFilter(searchText string, startDate, endDate time.Time) ([]*entities.ArticleModels, error)
FindAllArticle(page, perPage int) ([]*entities.ArticleModels, error)
}

type ServiceArticleInterface interface {
Expand All @@ -43,14 +43,14 @@ type ServiceArticleInterface interface {
GetUserBookmarkArticle(userID uint64) ([]*entities.ArticleBookmarkModels, error)
GetLatestArticles() ([]*entities.ArticleModels, error)
GetOldestArticle(page, perPage int) ([]*entities.ArticleModels, int64, error)
GetArticlePreferences(userID uint64, page, perPage int) ([]*entities.ArticleModels, int64, error)
GetNextPage(currentPage int, totalPages int) int
GetPrevPage(currentPage int) int
CalculatePaginationValues(page int, totalItems int, perPage int) (int, int)
GetArticlesAlphabet(page, perPage int) ([]*entities.ArticleModels, int64, error)
GetArticleMostViews(page, perPage int) ([]*entities.ArticleModels, int64, error)
GetOtherArticle() ([]*entities.ArticleModels, error)
GetArticleSearchByDateRange(filterType, searchText string) ([]*entities.ArticleModels, error)
GetAllArticleUser(page, perPage int) ([]*entities.ArticleModels, int64, error)
}

type HandlerArticleInterface interface {
Expand All @@ -62,7 +62,7 @@ type HandlerArticleInterface interface {
BookmarkArticle() echo.HandlerFunc
DeleteBookmarkedArticle() echo.HandlerFunc
GetUsersBookmark() echo.HandlerFunc
GetArticlePreferences() echo.HandlerFunc
GetOtherArticle() echo.HandlerFunc
GetLatestArticle() echo.HandlerFunc
GetAllArticleUser() echo.HandlerFunc
}
51 changes: 12 additions & 39 deletions module/feature/article/repository/repository.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package repository

import (
"strings"
"time"

"github.com/capstone-kelompok-7/backend-disappear/module/entities"
Expand Down Expand Up @@ -138,44 +137,6 @@ func (r *ArticleRepository) GetLatestArticle() ([]*entities.ArticleModels, error
return articles, nil
}

func (r *ArticleRepository) FindAllByUserPreference(userID uint64, page, perPage int) ([]*entities.ArticleModels, error) {
var matchingArticles []*entities.ArticleModels
var nonMatchingArticles []*entities.ArticleModels

userPreferences, err := r.getUserPreferences(userID)
if err != nil {
return nil, err
}

searchPattern := "%" + strings.Join(userPreferences, "%") + "%"
offset := (page - 1) * perPage

err = r.db.Where("deleted_at IS NULL").Where("title LIKE ?", searchPattern).Offset(offset).Limit(perPage).Find(&matchingArticles).Error
if err != nil {
return nil, err
}

err = r.db.Where("deleted_at IS NULL").Where("title NOT LIKE ?", searchPattern).Offset(offset).Limit(perPage).Find(&nonMatchingArticles).Error
if err != nil {
return nil, err
}

articles := append(matchingArticles, nonMatchingArticles...)

return articles, nil
}

func (r *ArticleRepository) getUserPreferences(userID uint64) ([]string, error) {
var userPreferences []string

err := r.db.Model(&entities.UserModels{}).Select("preferred_topics").Where("id = ?", userID).Pluck("preferred_topics", &userPreferences).Error
if err != nil {
return nil, err
}

return userPreferences, nil
}

func (r *ArticleRepository) GetOldestArticle(page, perPage int) ([]*entities.ArticleModels, error) {
var articles []*entities.ArticleModels
offset := (page - 1) * perPage
Expand Down Expand Up @@ -249,3 +210,15 @@ func (r *ArticleRepository) SearchArticlesWithDateFilter(searchText string, star

return articles, nil
}

func (r *ArticleRepository) FindAllArticle(page, perPage int) ([]*entities.ArticleModels, error) {
var articles []*entities.ArticleModels
offset := (page - 1) * perPage

err := r.db.Where("deleted_at IS NULL").Offset(offset).Limit(perPage).Find(&articles).Error
if err != nil {
return nil, err
}

return articles, nil
}
4 changes: 2 additions & 2 deletions module/feature/article/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,8 @@ func (s *ArticleService) GetOldestArticle(page, perPage int) ([]*entities.Articl
return result, totalItems, nil
}

func (s *ArticleService) GetArticlePreferences(userID uint64, page, perPage int) ([]*entities.ArticleModels, int64, error) {
result, err := s.repo.FindAllByUserPreference(userID, page, perPage)
func (s *ArticleService) GetAllArticleUser(page, perPage int) ([]*entities.ArticleModels, int64, error) {
result, err := s.repo.FindAllArticle(page, perPage)
if err != nil {
return nil, 0, err
}
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,23 @@ package handler

import (
"github.com/capstone-kelompok-7/backend-disappear/module/entities"
"github.com/capstone-kelompok-7/backend-disappear/module/feature/chatbot"
"github.com/capstone-kelompok-7/backend-disappear/module/feature/chatbot/dto"
"github.com/capstone-kelompok-7/backend-disappear/module/feature/assistant"
"github.com/capstone-kelompok-7/backend-disappear/module/feature/assistant/dto"
"github.com/capstone-kelompok-7/backend-disappear/utils/response"
"github.com/labstack/echo/v4"
)

type ChatbotHandler struct {
service chatbot.ServicChatbotInterface
type AssistantHandler struct {
service assistant.ServiceAssistantInterface
}

func NewChatbotHandler(service chatbot.ServicChatbotInterface) chatbot.HandlerChatbotInterface {
return &ChatbotHandler{
func NewAssistantHandler(service assistant.ServiceAssistantInterface) assistant.HandlerAssistantInterface {
return &AssistantHandler{
service: service,
}
}

func (h *ChatbotHandler) CreateQuestion() echo.HandlerFunc {
func (h *AssistantHandler) CreateQuestion() echo.HandlerFunc {
return func(c echo.Context) error {
currentUser := c.Get("CurrentUser").(*entities.UserModels)
if currentUser.Role != "customer" {
Expand All @@ -43,7 +43,7 @@ func (h *ChatbotHandler) CreateQuestion() echo.HandlerFunc {
}
}

func (h *ChatbotHandler) CreateAnswer() echo.HandlerFunc {
func (h *AssistantHandler) CreateAnswer() echo.HandlerFunc {
return func(c echo.Context) error {
currentUser := c.Get("CurrentUser").(*entities.UserModels)
if currentUser.Role != "customer" {
Expand All @@ -68,7 +68,7 @@ func (h *ChatbotHandler) CreateAnswer() echo.HandlerFunc {
}
}

func (h *ChatbotHandler) GetChatByIdUser() echo.HandlerFunc {
func (h *AssistantHandler) GetChatByIdUser() echo.HandlerFunc {
return func(c echo.Context) error {
currentUser := c.Get("CurrentUser").(*entities.UserModels)
if currentUser.Role != "customer" {
Expand All @@ -84,18 +84,34 @@ func (h *ChatbotHandler) GetChatByIdUser() echo.HandlerFunc {
}
}

func (h *ChatbotHandler) GenerateArtikelAi() echo.HandlerFunc {
func (h *AssistantHandler) GenerateArticle() echo.HandlerFunc {
return func(c echo.Context) error {
judulRequest := new(dto.GenerateArticleAiRequest)
if err := c.Bind(judulRequest); err != nil {
request := new(dto.GenerateArticleAiRequest)
if err := c.Bind(request); err != nil {
return response.SendBadRequestResponse(c, "Format input yang Anda masukkan tidak sesuai.")
}

chat, err := h.service.GenerateArtikelAi(judulRequest.Text)
chat, err := h.service.GenerateArticle(request.Text)
if err != nil {
return response.SendStatusInternalServerResponse(c, "Gagal generate artikel: "+err.Error())
}

return response.SendSuccessResponse(c, "Berhasil mendapatkan jawaban", chat)
}
}

func (h *AssistantHandler) GetProductByIdUser() echo.HandlerFunc {
return func(c echo.Context) error {
currentUser := c.Get("CurrentUser").(*entities.UserModels)
if currentUser.Role != "customer" {
return response.SendStatusForbiddenResponse(c, "Tidak diizinkan: Anda tidak memiliki izin")
}

chat, err := h.service.GenerateRecommendationProduct(currentUser.ID)
if err != nil {
return response.SendStatusInternalServerResponse(c, "Gagal rekomendasi "+err.Error())
}

return response.SendSuccessResponse(c, "Berhasil rekomendasi", chat)
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package chatbot
package assistant

import (
"context"
Expand All @@ -8,23 +8,28 @@ import (
"github.com/sashabaranov/go-openai"
)

type RepositoryChatbotInterface interface {
type RepositoryAssistantInterface interface {
GetChatByIdUser(id uint64) ([]entities.ChatModel, error)
CreateQuestion(chat entities.ChatModel) error
CreateAnswer(chat entities.ChatModel) error
GetLastOrdersByUserID(userID uint64) ([]*entities.OrderModels, error)
GetTopSellingProducts() ([]string, error)
GetTopRatedProducts() ([]*entities.ProductModels, error)
}

type ServicChatbotInterface interface {
type ServiceAssistantInterface interface {
GetChatByIdUser(id uint64) ([]entities.ChatModel, error)
CreateQuestion(userID uint64, newData entities.ChatModel) error
CreateAnswer(userID uint64, newData entities.ChatModel) (string, error)
GetAnswerFromAi(chat []openai.ChatCompletionMessage, ctx context.Context) (openai.ChatCompletionResponse, error)
GenerateArtikelAi(judul string) (string, error)
GenerateArticle(title string) (string, error)
GenerateRecommendationProduct(userID uint64) ([]string, error)
}

type HandlerChatbotInterface interface {
type HandlerAssistantInterface interface {
GetChatByIdUser() echo.HandlerFunc
CreateQuestion() echo.HandlerFunc
CreateAnswer() echo.HandlerFunc
GenerateArtikelAi() echo.HandlerFunc
GenerateArticle() echo.HandlerFunc
GetProductByIdUser() echo.HandlerFunc
}
Loading

0 comments on commit c7d36a8

Please sign in to comment.