Add Company Feedback Statistics Endpoint and Update API Documentation
- Introduced a new endpoint to retrieve comprehensive feedback statistics for the authenticated user's company, including total likes, dislikes, and percentage calculations. - Implemented the GetCompanyFeedbackStats method in the feedback handler to handle requests, ensuring proper authentication and error handling. - Updated Swagger JSON, YAML, and Go documentation to accurately reflect the new endpoint, including detailed descriptions, parameters, and response formats for the CompanyFeedbackStatsResponse. - Enhanced the feedback repository and service layers to support the new statistics functionality, improving the overall data handling capabilities of the tender management system. - These changes enhance the usability and functionality of the API, providing valuable insights into company feedback statistics.
This commit is contained in:
+133
-28
@@ -5418,6 +5418,109 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/feedback/stats/company": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Retrieve comprehensive feedback statistics for the authenticated user's company including total likes, dislikes, and percentage calculations.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Feedback"
|
||||||
|
],
|
||||||
|
"summary": "Get company feedback statistics",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Company feedback statistics retrieved successfully",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/feedback.CompanyFeedbackStatsResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad request - Missing company association",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized - User not authenticated",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal server error",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/feedback/tender/{tender_id}": {
|
"/api/v1/feedback/tender/{tender_id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -7592,6 +7695,27 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"feedback.CompanyFeedbackStatsResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"company_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"last_updated": {
|
||||||
|
"description": "Unix timestamp",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_dislikes": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_feedback": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_likes": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"feedback.FeedbackListResponse": {
|
"feedback.FeedbackListResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -7759,6 +7883,9 @@ const docTemplate = `{
|
|||||||
"tender.TenderResponse": {
|
"tender.TenderResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"application_deadline": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"buyer_organization": {
|
"buyer_organization": {
|
||||||
"$ref": "#/definitions/tender.OrganizationResponse"
|
"$ref": "#/definitions/tender.OrganizationResponse"
|
||||||
},
|
},
|
||||||
@@ -7801,9 +7928,15 @@ const docTemplate = `{
|
|||||||
"submission_deadline": {
|
"submission_deadline": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"submission_url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"tender_deadline": {
|
"tender_deadline": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"tender_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@@ -7875,20 +8008,6 @@ const docTemplate = `{
|
|||||||
"tender_approval.CompanyTenderApprovalStatsResponse": {
|
"tender_approval.CompanyTenderApprovalStatsResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"approvals_by_status": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"approvals_by_submission": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"approved_tenders": {
|
"approved_tenders": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@@ -8002,20 +8121,6 @@ const docTemplate = `{
|
|||||||
"tender_approval.TenderApprovalStatsResponse": {
|
"tender_approval.TenderApprovalStatsResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"approvals_by_status": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"approvals_by_submission": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"approved_tenders": {
|
"approved_tenders": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|||||||
+133
-28
@@ -5412,6 +5412,109 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/v1/feedback/stats/company": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Retrieve comprehensive feedback statistics for the authenticated user's company including total likes, dislikes, and percentage calculations.",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Feedback"
|
||||||
|
],
|
||||||
|
"summary": "Get company feedback statistics",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Company feedback statistics retrieved successfully",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/definitions/feedback.CompanyFeedbackStatsResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Bad request - Missing company association",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized - User not authenticated",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal server error",
|
||||||
|
"schema": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/definitions/response.APIResponse"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"error": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/feedback/tender/{tender_id}": {
|
"/api/v1/feedback/tender/{tender_id}": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
@@ -7586,6 +7689,27 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"feedback.CompanyFeedbackStatsResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"company_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"last_updated": {
|
||||||
|
"description": "Unix timestamp",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_dislikes": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_feedback": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"total_likes": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"feedback.FeedbackListResponse": {
|
"feedback.FeedbackListResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -7753,6 +7877,9 @@
|
|||||||
"tender.TenderResponse": {
|
"tender.TenderResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"application_deadline": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"buyer_organization": {
|
"buyer_organization": {
|
||||||
"$ref": "#/definitions/tender.OrganizationResponse"
|
"$ref": "#/definitions/tender.OrganizationResponse"
|
||||||
},
|
},
|
||||||
@@ -7795,9 +7922,15 @@
|
|||||||
"submission_deadline": {
|
"submission_deadline": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"submission_url": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"tender_deadline": {
|
"tender_deadline": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"tender_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"title": {
|
"title": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
@@ -7869,20 +8002,6 @@
|
|||||||
"tender_approval.CompanyTenderApprovalStatsResponse": {
|
"tender_approval.CompanyTenderApprovalStatsResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"approvals_by_status": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"approvals_by_submission": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"approved_tenders": {
|
"approved_tenders": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
@@ -7996,20 +8115,6 @@
|
|||||||
"tender_approval.TenderApprovalStatsResponse": {
|
"tender_approval.TenderApprovalStatsResponse": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"approvals_by_status": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"approvals_by_submission": {
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "integer",
|
|
||||||
"format": "int64"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"approved_tenders": {
|
"approved_tenders": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|||||||
+76
-20
@@ -748,6 +748,20 @@ definitions:
|
|||||||
is_verified:
|
is_verified:
|
||||||
type: boolean
|
type: boolean
|
||||||
type: object
|
type: object
|
||||||
|
feedback.CompanyFeedbackStatsResponse:
|
||||||
|
properties:
|
||||||
|
company_id:
|
||||||
|
type: string
|
||||||
|
last_updated:
|
||||||
|
description: Unix timestamp
|
||||||
|
type: integer
|
||||||
|
total_dislikes:
|
||||||
|
type: integer
|
||||||
|
total_feedback:
|
||||||
|
type: integer
|
||||||
|
total_likes:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
feedback.FeedbackListResponse:
|
feedback.FeedbackListResponse:
|
||||||
properties:
|
properties:
|
||||||
feedback:
|
feedback:
|
||||||
@@ -860,6 +874,8 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
tender.TenderResponse:
|
tender.TenderResponse:
|
||||||
properties:
|
properties:
|
||||||
|
application_deadline:
|
||||||
|
type: integer
|
||||||
buyer_organization:
|
buyer_organization:
|
||||||
$ref: '#/definitions/tender.OrganizationResponse'
|
$ref: '#/definitions/tender.OrganizationResponse'
|
||||||
country_code:
|
country_code:
|
||||||
@@ -888,8 +904,12 @@ definitions:
|
|||||||
$ref: '#/definitions/tender.TenderStatus'
|
$ref: '#/definitions/tender.TenderStatus'
|
||||||
submission_deadline:
|
submission_deadline:
|
||||||
type: integer
|
type: integer
|
||||||
|
submission_url:
|
||||||
|
type: string
|
||||||
tender_deadline:
|
tender_deadline:
|
||||||
type: integer
|
type: integer
|
||||||
|
tender_id:
|
||||||
|
type: string
|
||||||
title:
|
title:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
@@ -941,16 +961,6 @@ definitions:
|
|||||||
- ApprovalStatusRejected
|
- ApprovalStatusRejected
|
||||||
tender_approval.CompanyTenderApprovalStatsResponse:
|
tender_approval.CompanyTenderApprovalStatsResponse:
|
||||||
properties:
|
properties:
|
||||||
approvals_by_status:
|
|
||||||
additionalProperties:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
approvals_by_submission:
|
|
||||||
additionalProperties:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
approved_tenders:
|
approved_tenders:
|
||||||
type: integer
|
type: integer
|
||||||
company_id:
|
company_id:
|
||||||
@@ -1026,16 +1036,6 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
tender_approval.TenderApprovalStatsResponse:
|
tender_approval.TenderApprovalStatsResponse:
|
||||||
properties:
|
properties:
|
||||||
approvals_by_status:
|
|
||||||
additionalProperties:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
approvals_by_submission:
|
|
||||||
additionalProperties:
|
|
||||||
format: int64
|
|
||||||
type: integer
|
|
||||||
type: object
|
|
||||||
approved_tenders:
|
approved_tenders:
|
||||||
type: integer
|
type: integer
|
||||||
last_updated:
|
last_updated:
|
||||||
@@ -4786,6 +4786,62 @@ paths:
|
|||||||
summary: Get feedback by ID
|
summary: Get feedback by ID
|
||||||
tags:
|
tags:
|
||||||
- Feedback
|
- Feedback
|
||||||
|
/api/v1/feedback/stats/company:
|
||||||
|
get:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Retrieve comprehensive feedback statistics for the authenticated
|
||||||
|
user's company including total likes, dislikes, and percentage calculations.
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Company feedback statistics retrieved successfully
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.APIResponse'
|
||||||
|
- properties:
|
||||||
|
data:
|
||||||
|
$ref: '#/definitions/feedback.CompanyFeedbackStatsResponse'
|
||||||
|
type: object
|
||||||
|
"400":
|
||||||
|
description: Bad request - Missing company association
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.APIResponse'
|
||||||
|
- properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
"401":
|
||||||
|
description: Unauthorized - User not authenticated
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.APIResponse'
|
||||||
|
- properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal server error
|
||||||
|
schema:
|
||||||
|
allOf:
|
||||||
|
- $ref: '#/definitions/response.APIResponse'
|
||||||
|
- properties:
|
||||||
|
error:
|
||||||
|
type: string
|
||||||
|
message:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
summary: Get company feedback statistics
|
||||||
|
tags:
|
||||||
|
- Feedback
|
||||||
/api/v1/feedback/tender/{tender_id}:
|
/api/v1/feedback/tender/{tender_id}:
|
||||||
get:
|
get:
|
||||||
consumes:
|
consumes:
|
||||||
|
|||||||
@@ -152,6 +152,7 @@ func RegisterPublicRoutes(e *echo.Echo, customerHandler *customer.Handler, tende
|
|||||||
feedbackGP.GET("", feedbackHandler.PublicListFeedback)
|
feedbackGP.GET("", feedbackHandler.PublicListFeedback)
|
||||||
feedbackGP.GET("/tender/:tender_id", feedbackHandler.PublicGetFeedbackByTenderID)
|
feedbackGP.GET("/tender/:tender_id", feedbackHandler.PublicGetFeedbackByTenderID)
|
||||||
feedbackGP.GET("/:id", feedbackHandler.PublicGetFeedback)
|
feedbackGP.GET("/:id", feedbackHandler.PublicGetFeedback)
|
||||||
|
feedbackGP.GET("/stats/company", feedbackHandler.GetCompanyFeedbackStats)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public Tender Approvals Routes
|
// Public Tender Approvals Routes
|
||||||
|
|||||||
@@ -30,6 +30,15 @@ type FeedbackResponse struct {
|
|||||||
CompanyId *string `json:"company_id"`
|
CompanyId *string `json:"company_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompanyFeedbackStatsResponse represents the response for company feedback statistics
|
||||||
|
type CompanyFeedbackStatsResponse struct {
|
||||||
|
CompanyID string `json:"company_id"`
|
||||||
|
TotalLikes int64 `json:"total_likes"`
|
||||||
|
TotalDislikes int64 `json:"total_dislikes"`
|
||||||
|
TotalFeedback int64 `json:"total_feedback"`
|
||||||
|
LastUpdated int64 `json:"last_updated"` // Unix timestamp
|
||||||
|
}
|
||||||
|
|
||||||
// GetDateFromTime returns the date_from as time.Time
|
// GetDateFromTime returns the date_from as time.Time
|
||||||
func (f *ListFeedbackForm) GetDateFromTime() *time.Time {
|
func (f *ListFeedbackForm) GetDateFromTime() *time.Time {
|
||||||
if f.DateFrom == nil {
|
if f.DateFrom == nil {
|
||||||
|
|||||||
@@ -378,3 +378,29 @@ func (h *Handler) PublicGetFeedbackByTenderID(c echo.Context) error {
|
|||||||
|
|
||||||
return response.Success(c, feedback, "Feedback retrieved successfully")
|
return response.Success(c, feedback, "Feedback retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCompanyFeedbackStats retrieves feedback statistics for the authenticated user's company
|
||||||
|
// @Summary Get company feedback statistics
|
||||||
|
// @Description Retrieve comprehensive feedback statistics for the authenticated user's company including total likes, dislikes, and percentage calculations.
|
||||||
|
// @Tags Feedback
|
||||||
|
// @Accept json
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} response.APIResponse{data=CompanyFeedbackStatsResponse} "Company feedback statistics retrieved successfully"
|
||||||
|
// @Failure 400 {object} response.APIResponse{error=string,message=string} "Bad request - Missing company association"
|
||||||
|
// @Failure 401 {object} response.APIResponse{error=string,message=string} "Unauthorized - User not authenticated"
|
||||||
|
// @Failure 500 {object} response.APIResponse{error=string,message=string} "Internal server error"
|
||||||
|
// @Security BearerAuth
|
||||||
|
// @Router /api/v1/feedback/stats/company [get]
|
||||||
|
func (h *Handler) GetCompanyFeedbackStats(c echo.Context) error {
|
||||||
|
companyID, err := user.GetCompanyIDFromContext(c)
|
||||||
|
if err != nil {
|
||||||
|
return response.BadRequest(c, "Company ID required", "User must be associated with a company")
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := h.service.CompanyFeedbackStats(c.Request().Context(), companyID)
|
||||||
|
if err != nil {
|
||||||
|
return response.InternalServerError(c, "Failed to retrieve company feedback statistics")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.Success(c, stats, "Company feedback statistics retrieved successfully")
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type Repository interface {
|
|||||||
GetFeedbackSummary(ctx context.Context, tenderID string) (*FeedbackSummary, error)
|
GetFeedbackSummary(ctx context.Context, tenderID string) (*FeedbackSummary, error)
|
||||||
GetFeedbackCountByType(ctx context.Context) (map[FeedbackType]int64, error)
|
GetFeedbackCountByType(ctx context.Context) (map[FeedbackType]int64, error)
|
||||||
GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error)
|
GetByTenderID(ctx context.Context, tenderID, companyID string) (*Feedback, error)
|
||||||
|
GetCompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// feedbackRepository implements FeedbackRepository interface using MongoDB ORM
|
// feedbackRepository implements FeedbackRepository interface using MongoDB ORM
|
||||||
@@ -412,6 +413,79 @@ func (r *feedbackRepository) GetByTenderID(ctx context.Context, tenderID, compan
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCompanyFeedbackStats calculates feedback statistics for a company
|
||||||
|
func (r *feedbackRepository) GetCompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error) {
|
||||||
|
pipeline := driver.Pipeline{
|
||||||
|
{
|
||||||
|
{Key: "$match", Value: bson.M{
|
||||||
|
"company_id": companyID,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
{Key: "$group", Value: bson.M{
|
||||||
|
"_id": nil,
|
||||||
|
"total_feedback": bson.M{
|
||||||
|
"$sum": 1,
|
||||||
|
},
|
||||||
|
"total_likes": bson.M{
|
||||||
|
"$sum": bson.M{
|
||||||
|
"$cond": []interface{}{
|
||||||
|
bson.M{"$eq": []string{"$feedback_type", string(FeedbackTypeLike)}},
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"total_dislikes": bson.M{
|
||||||
|
"$sum": bson.M{
|
||||||
|
"$cond": []interface{}{
|
||||||
|
bson.M{"$eq": []string{"$feedback_type", string(FeedbackTypeDislike)}},
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"last_updated": bson.M{"$max": "$updated_at"},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := r.repo.Aggregate(ctx, pipeline)
|
||||||
|
if err != nil {
|
||||||
|
r.logger.Error("Failed to get company feedback stats", map[string]interface{}{
|
||||||
|
"company_id": companyID,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stats := &CompanyFeedbackStatsResponse{
|
||||||
|
CompanyID: companyID,
|
||||||
|
TotalFeedback: 0,
|
||||||
|
TotalLikes: 0,
|
||||||
|
TotalDislikes: 0,
|
||||||
|
LastUpdated: time.Now().Unix(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(results) > 0 {
|
||||||
|
result := results[0]
|
||||||
|
if totalFeedback, ok := result["total_feedback"].(int32); ok {
|
||||||
|
stats.TotalFeedback = int64(totalFeedback)
|
||||||
|
}
|
||||||
|
if totalLikes, ok := result["total_likes"].(int32); ok {
|
||||||
|
stats.TotalLikes = int64(totalLikes)
|
||||||
|
}
|
||||||
|
if totalDislikes, ok := result["total_dislikes"].(int32); ok {
|
||||||
|
stats.TotalDislikes = int64(totalDislikes)
|
||||||
|
}
|
||||||
|
if lastUpdated, ok := result["last_updated"].(int64); ok {
|
||||||
|
stats.LastUpdated = lastUpdated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|
||||||
// buildSearchFilter builds MongoDB filter from search criteria
|
// buildSearchFilter builds MongoDB filter from search criteria
|
||||||
func (r *feedbackRepository) buildSearchFilter(criteria FeedbackSearchCriteria) bson.M {
|
func (r *feedbackRepository) buildSearchFilter(criteria FeedbackSearchCriteria) bson.M {
|
||||||
filter := bson.M{}
|
filter := bson.M{}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ type FeedbackService interface {
|
|||||||
ListFeedback(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) (*FeedbackListResponse, error)
|
ListFeedback(ctx context.Context, criteria FeedbackSearchCriteria, limit, offset int) (*FeedbackListResponse, error)
|
||||||
DeleteFeedback(ctx context.Context, id string) error
|
DeleteFeedback(ctx context.Context, id string) error
|
||||||
GetFeedbackByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error)
|
GetFeedbackByTenderID(ctx context.Context, tenderID, companyID string) (*FeedbackResponse, error)
|
||||||
|
CompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// feedbackService implements FeedbackService interface
|
// feedbackService implements FeedbackService interface
|
||||||
@@ -152,3 +153,27 @@ func (s *feedbackService) GetFeedbackByTenderID(ctx context.Context, tenderID, c
|
|||||||
}
|
}
|
||||||
return feedback.ToResponse(), nil
|
return feedback.ToResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *feedbackService) CompanyFeedbackStats(ctx context.Context, companyID string) (*CompanyFeedbackStatsResponse, error) {
|
||||||
|
s.logger.Info("Getting company feedback statistics", map[string]interface{}{
|
||||||
|
"company_id": companyID,
|
||||||
|
})
|
||||||
|
|
||||||
|
stats, err := s.feedbackRepo.GetCompanyFeedbackStats(ctx, companyID)
|
||||||
|
if err != nil {
|
||||||
|
s.logger.Error("Failed to get company feedback statistics", map[string]interface{}{
|
||||||
|
"company_id": companyID,
|
||||||
|
"error": err.Error(),
|
||||||
|
})
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.logger.Info("Company feedback statistics retrieved successfully", map[string]interface{}{
|
||||||
|
"company_id": companyID,
|
||||||
|
"total_feedback": stats.TotalFeedback,
|
||||||
|
"total_likes": stats.TotalLikes,
|
||||||
|
"total_dislikes": stats.TotalDislikes,
|
||||||
|
})
|
||||||
|
|
||||||
|
return stats, nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user