Implement Company Search and Update API Documentation
- Introduced a new search functionality for companies, allowing advanced filtering capabilities including tags, business criteria, and location. - Updated the API documentation to reflect changes in the search endpoint, enhancing clarity for API consumers. - Refactored existing company-related API endpoints for consistency, including renaming and restructuring routes. - Enhanced response structures to return company entities directly, simplifying the response handling in API endpoints. - Removed unused query parameters and handlers, streamlining the company management functionality.
This commit is contained in:
+86
-1318
File diff suppressed because it is too large
Load Diff
+86
-1318
File diff suppressed because it is too large
Load Diff
+81
-881
File diff suppressed because it is too large
Load Diff
@@ -47,24 +47,13 @@ func RegisterAdminRoutes(e *echo.Echo, userHandler *user.Handler, companyHandler
|
|||||||
companiesGP := adminV1.Group("/companies")
|
companiesGP := adminV1.Group("/companies")
|
||||||
{
|
{
|
||||||
companiesGP.Use(userHandler.AuthMiddleware())
|
companiesGP.Use(userHandler.AuthMiddleware())
|
||||||
companiesGP.POST("", companyHandler.CreateCompany)
|
companiesGP.POST("", companyHandler.Create)
|
||||||
companiesGP.GET("", companyHandler.ListCompanies)
|
companiesGP.GET("", companyHandler.Search)
|
||||||
companiesGP.GET("/:id", companyHandler.GetCompany)
|
companiesGP.GET("/:id", companyHandler.Get)
|
||||||
companiesGP.PUT("/:id", companyHandler.UpdateCompany)
|
companiesGP.PUT("/:id", companyHandler.Update)
|
||||||
companiesGP.DELETE("/:id", companyHandler.DeleteCompany)
|
companiesGP.DELETE("/:id", companyHandler.Delete)
|
||||||
companiesGP.GET("/search", companyHandler.SearchCompanies)
|
companiesGP.PATCH("/:id/status", companyHandler.UpdateStatus)
|
||||||
companiesGP.PATCH("/:id/status", companyHandler.UpdateCompanyStatus)
|
companiesGP.PATCH("/:id/verification", companyHandler.UpdateVerification)
|
||||||
companiesGP.PATCH("/:id/verification", companyHandler.UpdateCompanyVerification)
|
|
||||||
companiesGP.PUT("/:id/tags", companyHandler.UpdateCompanyTags)
|
|
||||||
companiesGP.POST("/:id/tags/add", companyHandler.AddTags)
|
|
||||||
companiesGP.POST("/:id/tags/remove", companyHandler.RemoveTags)
|
|
||||||
companiesGP.POST("/:id/verify", companyHandler.VerifyCompany)
|
|
||||||
companiesGP.POST("/:id/suspend", companyHandler.SuspendCompany)
|
|
||||||
companiesGP.POST("/:id/activate", companyHandler.ActivateCompany)
|
|
||||||
companiesGP.GET("/stats", companyHandler.GetCompanyStats)
|
|
||||||
companiesGP.GET("/type/:type", companyHandler.GetCompaniesByType)
|
|
||||||
companiesGP.GET("/status/:status", companyHandler.GetCompaniesByStatus)
|
|
||||||
companiesGP.GET("/industry/:industry", companyHandler.GetCompaniesByIndustry)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admin Customers Routes
|
// Admin Customers Routes
|
||||||
|
|||||||
+6
-109
@@ -13,7 +13,6 @@ const (
|
|||||||
CompanyStatusActive CompanyStatus = "active"
|
CompanyStatusActive CompanyStatus = "active"
|
||||||
CompanyStatusInactive CompanyStatus = "inactive"
|
CompanyStatusInactive CompanyStatus = "inactive"
|
||||||
CompanyStatusSuspended CompanyStatus = "suspended"
|
CompanyStatusSuspended CompanyStatus = "suspended"
|
||||||
CompanyStatusPending CompanyStatus = "pending"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// CompanyType represents the type of company
|
// CompanyType represents the type of company
|
||||||
@@ -48,9 +47,8 @@ type Company struct {
|
|||||||
Address *Address `bson:"address,omitempty" json:"address,omitempty"`
|
Address *Address `bson:"address,omitempty" json:"address,omitempty"`
|
||||||
|
|
||||||
// Contact information
|
// Contact information
|
||||||
ContactPerson *ContactPerson `bson:"contact_person,omitempty" json:"contact_person,omitempty"`
|
Phone *string `bson:"phone,omitempty" json:"phone,omitempty"`
|
||||||
Phone *string `bson:"phone,omitempty" json:"phone,omitempty"`
|
Email *string `bson:"email,omitempty" json:"email,omitempty"`
|
||||||
Email *string `bson:"email,omitempty" json:"email,omitempty"`
|
|
||||||
|
|
||||||
// Tags for categorization and search
|
// Tags for categorization and search
|
||||||
Tags *CompanyTags `bson:"tags,omitempty" json:"tags,omitempty"`
|
Tags *CompanyTags `bson:"tags,omitempty" json:"tags,omitempty"`
|
||||||
@@ -64,10 +62,6 @@ type Company struct {
|
|||||||
Language string `bson:"language" json:"language"` // Default: "en"
|
Language string `bson:"language" json:"language"` // Default: "en"
|
||||||
Currency string `bson:"currency" json:"currency"` // Default: "USD"
|
Currency string `bson:"currency" json:"currency"` // Default: "USD"
|
||||||
Timezone string `bson:"timezone" json:"timezone"` // Default: "UTC"
|
Timezone string `bson:"timezone" json:"timezone"` // Default: "UTC"
|
||||||
|
|
||||||
// Audit fields
|
|
||||||
CreatedBy *string `bson:"created_by,omitempty" json:"created_by,omitempty"`
|
|
||||||
UpdatedBy *string `bson:"updated_by,omitempty" json:"updated_by,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetID sets the company ID (implements IDSetter interface)
|
// SetID sets the company ID (implements IDSetter interface)
|
||||||
@@ -80,22 +74,22 @@ func (c *Company) GetID() string {
|
|||||||
return c.ID.Hex()
|
return c.ID.Hex()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetCreatedAt sets the created timestamp (implements Timestampable interface)
|
// SetCreatedAt sets the created timestamp (implements Timestamp interface)
|
||||||
func (c *Company) SetCreatedAt(timestamp int64) {
|
func (c *Company) SetCreatedAt(timestamp int64) {
|
||||||
c.CreatedAt = timestamp
|
c.CreatedAt = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetUpdatedAt sets the updated timestamp (implements Timestampable interface)
|
// SetUpdatedAt sets the updated timestamp (implements Timestamp interface)
|
||||||
func (c *Company) SetUpdatedAt(timestamp int64) {
|
func (c *Company) SetUpdatedAt(timestamp int64) {
|
||||||
c.UpdatedAt = timestamp
|
c.UpdatedAt = timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCreatedAt returns the created timestamp (implements Timestampable interface)
|
// GetCreatedAt returns the created timestamp (implements Timestamp interface)
|
||||||
func (c *Company) GetCreatedAt() int64 {
|
func (c *Company) GetCreatedAt() int64 {
|
||||||
return c.CreatedAt
|
return c.CreatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUpdatedAt returns the updated timestamp (implements Timestampable interface)
|
// GetUpdatedAt returns the updated timestamp (implements Timestamp interface)
|
||||||
func (c *Company) GetUpdatedAt() int64 {
|
func (c *Company) GetUpdatedAt() int64 {
|
||||||
return c.UpdatedAt
|
return c.UpdatedAt
|
||||||
}
|
}
|
||||||
@@ -109,18 +103,6 @@ type Address struct {
|
|||||||
Country string `bson:"country" json:"country"`
|
Country string `bson:"country" json:"country"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ContactPerson represents a contact person for the company
|
|
||||||
type ContactPerson struct {
|
|
||||||
FirstName string `bson:"first_name" json:"first_name"`
|
|
||||||
LastName string `bson:"last_name" json:"last_name"`
|
|
||||||
FullName string `bson:"full_name" json:"full_name"`
|
|
||||||
Position string `bson:"position" json:"position"`
|
|
||||||
Email string `bson:"email" json:"email"`
|
|
||||||
Phone string `bson:"phone" json:"phone"`
|
|
||||||
Mobile *string `bson:"mobile,omitempty" json:"mobile,omitempty"`
|
|
||||||
IsPrimary bool `bson:"is_primary" json:"is_primary"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompanyTags represents tags for company categorization and search
|
// CompanyTags represents tags for company categorization and search
|
||||||
type CompanyTags struct {
|
type CompanyTags struct {
|
||||||
// CPV codes (Common Procurement Vocabulary)
|
// CPV codes (Common Procurement Vocabulary)
|
||||||
@@ -138,88 +120,3 @@ type CompanyTags struct {
|
|||||||
// Certifications
|
// Certifications
|
||||||
Certifications []string `bson:"certifications,omitempty" json:"certifications,omitempty"`
|
Certifications []string `bson:"certifications,omitempty" json:"certifications,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompanyResponse represents the company data sent in API responses
|
|
||||||
type CompanyResponse struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Status string `json:"status"`
|
|
||||||
RegistrationNumber string `json:"registration_number"`
|
|
||||||
TaxID string `json:"tax_id"`
|
|
||||||
Industry string `json:"industry"`
|
|
||||||
Description *string `json:"description,omitempty"`
|
|
||||||
Website *string `json:"website,omitempty"`
|
|
||||||
EmployeeCount *int `json:"employee_count,omitempty"`
|
|
||||||
AnnualRevenue *float64 `json:"annual_revenue,omitempty"`
|
|
||||||
FoundedYear *int `json:"founded_year,omitempty"`
|
|
||||||
Address *Address `json:"address,omitempty"`
|
|
||||||
ContactPerson *ContactPerson `json:"contact_person,omitempty"`
|
|
||||||
Phone *string `json:"phone,omitempty"`
|
|
||||||
Email *string `json:"email,omitempty"`
|
|
||||||
Tags *CompanyTags `json:"tags,omitempty"`
|
|
||||||
IsVerified bool `json:"is_verified"`
|
|
||||||
IsCompliant bool `json:"is_compliant"`
|
|
||||||
ComplianceNotes *string `json:"compliance_notes,omitempty"`
|
|
||||||
Language string `json:"language"`
|
|
||||||
Currency string `json:"currency"`
|
|
||||||
Timezone string `json:"timezone"`
|
|
||||||
CreatedAt int64 `json:"created_at"`
|
|
||||||
UpdatedAt int64 `json:"updated_at"`
|
|
||||||
CreatedBy *string `json:"created_by,omitempty"`
|
|
||||||
UpdatedBy *string `json:"updated_by,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToResponse converts Company to CompanyResponse
|
|
||||||
func (c *Company) ToResponse() *CompanyResponse {
|
|
||||||
return &CompanyResponse{
|
|
||||||
ID: c.ID.Hex(),
|
|
||||||
Name: c.Name,
|
|
||||||
Type: string(c.Type),
|
|
||||||
Status: string(c.Status),
|
|
||||||
RegistrationNumber: c.RegistrationNumber,
|
|
||||||
TaxID: c.TaxID,
|
|
||||||
Industry: c.Industry,
|
|
||||||
Description: c.Description,
|
|
||||||
Website: c.Website,
|
|
||||||
EmployeeCount: c.EmployeeCount,
|
|
||||||
AnnualRevenue: c.AnnualRevenue,
|
|
||||||
FoundedYear: c.FoundedYear,
|
|
||||||
Address: c.Address,
|
|
||||||
ContactPerson: c.ContactPerson,
|
|
||||||
Phone: c.Phone,
|
|
||||||
Email: c.Email,
|
|
||||||
Tags: c.Tags,
|
|
||||||
IsVerified: c.IsVerified,
|
|
||||||
IsCompliant: c.IsCompliant,
|
|
||||||
ComplianceNotes: c.ComplianceNotes,
|
|
||||||
Language: c.Language,
|
|
||||||
Currency: c.Currency,
|
|
||||||
Timezone: c.Timezone,
|
|
||||||
CreatedAt: c.CreatedAt,
|
|
||||||
UpdatedAt: c.UpdatedAt,
|
|
||||||
CreatedBy: c.CreatedBy,
|
|
||||||
UpdatedBy: c.UpdatedBy,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type CompanyProfileResponse struct {
|
|
||||||
ID string `json:"id"`
|
|
||||||
Name string `json:"name"`
|
|
||||||
RegistrationNumber string `json:"registration_number"`
|
|
||||||
Industry string `json:"industry"`
|
|
||||||
FoundedYear *int `json:"founded_year,omitempty"`
|
|
||||||
CreatedAt int64 `json:"created_at"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToResponse converts Company to CompanyResponse
|
|
||||||
func (c *Company) ToProfileResponse() *CompanyProfileResponse {
|
|
||||||
return &CompanyProfileResponse{
|
|
||||||
ID: c.ID.Hex(),
|
|
||||||
Name: c.Name,
|
|
||||||
RegistrationNumber: c.RegistrationNumber,
|
|
||||||
Industry: c.Industry,
|
|
||||||
FoundedYear: c.FoundedYear,
|
|
||||||
CreatedAt: c.CreatedAt,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+127
-159
@@ -1,72 +1,61 @@
|
|||||||
package company
|
package company
|
||||||
|
|
||||||
// CreateCompanyForm represents the form for creating a new company
|
import "tm/pkg/response"
|
||||||
type CreateCompanyForm struct {
|
|
||||||
Name string `json:"name" valid:"required,length(2|200)"`
|
|
||||||
Type string `json:"type" valid:"required,in(private|public|government|ngo|startup)"`
|
|
||||||
RegistrationNumber string `json:"registration_number" valid:"required,length(5|50)"`
|
|
||||||
TaxID string `json:"tax_id" valid:"required,length(5|50)"`
|
|
||||||
Industry string `json:"industry" valid:"required,length(2|100)"`
|
|
||||||
Description *string `json:"description,omitempty" valid:"optional,length(10|1000)"`
|
|
||||||
Website *string `json:"website,omitempty" valid:"optional,url"`
|
|
||||||
|
|
||||||
// Business information
|
// CompanyForm represents the form for creating/updating a new company
|
||||||
EmployeeCount *int `json:"employee_count,omitempty" valid:"optional,range(1|100000)"`
|
type (
|
||||||
AnnualRevenue *float64 `json:"annual_revenue,omitempty" valid:"optional,range(0|999999999999)"`
|
CompanyForm struct {
|
||||||
FoundedYear *int `json:"founded_year,omitempty" valid:"optional,range(1800|2100)"`
|
Name string `json:"name" valid:"required,length(2|200)"`
|
||||||
|
Type string `json:"type" valid:"required,in(private|public|government|ngo|startup)"`
|
||||||
|
RegistrationNumber string `json:"registration_number" valid:"required,length(5|50)"`
|
||||||
|
TaxID string `json:"tax_id" valid:"required,length(5|50)"`
|
||||||
|
Industry string `json:"industry" valid:"required,length(2|100)"`
|
||||||
|
Description *string `json:"description,omitempty" valid:"optional,length(10|1000)"`
|
||||||
|
Website *string `json:"website,omitempty" valid:"optional,url"`
|
||||||
|
|
||||||
// Address information
|
// Business information
|
||||||
Address *AddressForm `json:"address,omitempty"`
|
EmployeeCount *int `json:"employee_count,omitempty" valid:"optional,range(1|100000)"`
|
||||||
|
AnnualRevenue *float64 `json:"annual_revenue,omitempty" valid:"optional,range(0|999999999999)"`
|
||||||
|
FoundedYear *int `json:"founded_year,omitempty" valid:"optional,range(1800|2100)"`
|
||||||
|
|
||||||
// Contact information
|
// Address information
|
||||||
ContactPerson *ContactPersonForm `json:"contact_person,omitempty"`
|
Address *AddressForm `json:"address,omitempty"`
|
||||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)"`
|
|
||||||
Email *string `json:"email,omitempty" valid:"optional,email"`
|
|
||||||
|
|
||||||
// Tags for categorization and search
|
// Contact information
|
||||||
Tags *CompanyTagsForm `json:"tags,omitempty"`
|
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)"`
|
||||||
|
Email *string `json:"email,omitempty" valid:"optional,email"`
|
||||||
|
|
||||||
// Settings
|
// Tags for categorization and search
|
||||||
Language *string `json:"language,omitempty" valid:"optional,in(en|ar|fr|es|de|zh|ja|ko)"`
|
Tags *CompanyTagsForm `json:"tags,omitempty"`
|
||||||
Currency *string `json:"currency,omitempty" valid:"optional,in(USD|EUR|GBP|JPY|CAD|AUD|CHF|CNY|INR|BRL)"`
|
|
||||||
Timezone *string `json:"timezone,omitempty" valid:"optional,length(3|50)"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UpdateCompanyForm represents the form for updating a company
|
// Settings
|
||||||
type UpdateCompanyForm struct {
|
Language *string `json:"language,omitempty" valid:"optional,in(en|ar|fr|es|de|zh|ja|ko)"`
|
||||||
Name *string `json:"name,omitempty" valid:"optional,length(2|200)"`
|
Currency *string `json:"currency,omitempty" valid:"optional,in(USD|EUR|GBP|JPY|CAD|AUD|CHF|CNY|INR|BRL)"`
|
||||||
Type *string `json:"type,omitempty" valid:"optional,in(private|public|government|ngo|startup)"`
|
Timezone *string `json:"timezone,omitempty" valid:"optional,length(3|50)"`
|
||||||
RegistrationNumber *string `json:"registration_number,omitempty" valid:"optional,length(5|50)"`
|
}
|
||||||
TaxID *string `json:"tax_id,omitempty" valid:"optional,length(5|50)"`
|
|
||||||
Industry *string `json:"industry,omitempty" valid:"optional,length(2|100)"`
|
|
||||||
Description *string `json:"description,omitempty" valid:"optional,length(10|1000)"`
|
|
||||||
Website *string `json:"website,omitempty" valid:"optional,url"`
|
|
||||||
|
|
||||||
// Business information
|
// AddressForm represents the form for company address
|
||||||
EmployeeCount *int `json:"employee_count,omitempty" valid:"optional,range(1|100000)"`
|
AddressForm struct {
|
||||||
AnnualRevenue *float64 `json:"annual_revenue,omitempty" valid:"optional,range(0|999999999999)"`
|
Street string `json:"street" valid:"required,length(5|200)"`
|
||||||
FoundedYear *int `json:"founded_year,omitempty" valid:"optional,range(1800|2100)"`
|
City string `json:"city" valid:"required,length(2|100)"`
|
||||||
|
State string `json:"state" valid:"required,length(2|100)"`
|
||||||
|
PostalCode string `json:"postal_code" valid:"required,length(3|20)"`
|
||||||
|
Country string `json:"country" valid:"required,length(2|100)"`
|
||||||
|
}
|
||||||
|
|
||||||
// Address information
|
// CompanyTagsForm represents the form for company tags
|
||||||
Address *AddressForm `json:"address,omitempty"`
|
CompanyTagsForm struct {
|
||||||
|
CPVCodes []string `json:"cpv_codes,omitempty" valid:"optional"`
|
||||||
|
Categories []string `json:"categories,omitempty" valid:"optional"`
|
||||||
|
Keywords []string `json:"keywords,omitempty" valid:"optional"`
|
||||||
|
Specializations []string `json:"specializations,omitempty" valid:"optional"`
|
||||||
|
Certifications []string `json:"certifications,omitempty" valid:"optional"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
// Contact information
|
// SearchForm represents the form for searching companies with filters
|
||||||
ContactPerson *ContactPersonForm `json:"contact_person,omitempty"`
|
type SearchForm struct {
|
||||||
Phone *string `json:"phone,omitempty" valid:"optional,length(10|20)"`
|
Search *string `query:"q" valid:"optional"`
|
||||||
Email *string `json:"email,omitempty" valid:"optional,email"`
|
|
||||||
|
|
||||||
// Tags for categorization and search
|
|
||||||
Tags *CompanyTagsForm `json:"tags,omitempty"`
|
|
||||||
|
|
||||||
// Settings
|
|
||||||
Language *string `json:"language,omitempty" valid:"optional,in(en|ar|fr|es|de|zh|ja|ko)"`
|
|
||||||
Currency *string `json:"currency,omitempty" valid:"optional,in(USD|EUR|GBP|JPY|CAD|AUD|CHF|CNY|INR|BRL)"`
|
|
||||||
Timezone *string `json:"timezone,omitempty" valid:"optional,length(3|50)"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListCompaniesForm represents the form for listing companies with filters
|
|
||||||
type ListCompaniesForm struct {
|
|
||||||
Search *string `query:"search" valid:"optional"`
|
|
||||||
Type *string `query:"type" valid:"optional,in(private|public|government|ngo|startup)"`
|
Type *string `query:"type" valid:"optional,in(private|public|government|ngo|startup)"`
|
||||||
Status *string `query:"status" valid:"optional,in(active|inactive|suspended|pending)"`
|
Status *string `query:"status" valid:"optional,in(active|inactive|suspended|pending)"`
|
||||||
Industry *string `query:"industry" valid:"optional"`
|
Industry *string `query:"industry" valid:"optional"`
|
||||||
@@ -90,122 +79,101 @@ type ListCompaniesForm struct {
|
|||||||
SortOrder *string `query:"sort_order" valid:"optional,in(asc|desc)"`
|
SortOrder *string `query:"sort_order" valid:"optional,in(asc|desc)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompanyStatusForm represents the form for updating company status
|
// StatusForm represents the form for updating company status
|
||||||
type UpdateCompanyStatusForm struct {
|
type StatusForm struct {
|
||||||
Status string `json:"status" valid:"required,in(active|inactive|suspended|pending)"`
|
Status string `json:"status" valid:"required,in(active|inactive|suspended)"`
|
||||||
|
Reason *string `json:"reason" valid:"optional,length(10|500)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompanyVerificationForm represents the form for updating company verification status
|
// VerificationForm represents the form for updating company verification status
|
||||||
type UpdateCompanyVerificationForm struct {
|
type VerificationForm struct {
|
||||||
IsVerified bool `json:"is_verified"`
|
IsVerified bool `json:"is_verified"`
|
||||||
IsCompliant bool `json:"is_compliant"`
|
IsCompliant bool `json:"is_compliant"`
|
||||||
ComplianceNotes *string `json:"compliance_notes,omitempty" valid:"optional,length(0|1000)"`
|
ComplianceNotes *string `json:"compliance_notes,omitempty" valid:"optional,length(0|1000)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompanyTagsForm represents the form for updating company tags
|
|
||||||
type UpdateCompanyTagsForm struct {
|
|
||||||
Tags *CompanyTagsForm `json:"tags" valid:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTagsForm represents the form for adding tags to a company
|
|
||||||
type AddTagsForm struct {
|
|
||||||
CPVCodes []string `json:"cpv_codes,omitempty" valid:"optional"`
|
|
||||||
Categories []string `json:"categories,omitempty" valid:"optional"`
|
|
||||||
Keywords []string `json:"keywords,omitempty" valid:"optional"`
|
|
||||||
Specializations []string `json:"specializations,omitempty" valid:"optional"`
|
|
||||||
Certifications []string `json:"certifications,omitempty" valid:"optional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveTagsForm represents the form for removing tags from a company
|
|
||||||
type RemoveTagsForm struct {
|
|
||||||
CPVCodes []string `json:"cpv_codes,omitempty" valid:"optional"`
|
|
||||||
Categories []string `json:"categories,omitempty" valid:"optional"`
|
|
||||||
Keywords []string `json:"keywords,omitempty" valid:"optional"`
|
|
||||||
Specializations []string `json:"specializations,omitempty" valid:"optional"`
|
|
||||||
Certifications []string `json:"certifications,omitempty" valid:"optional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SuspendCompanyForm represents the form for suspending a company
|
|
||||||
type SuspendCompanyForm struct {
|
|
||||||
Reason string `json:"reason" valid:"required,length(10|500)"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddressForm represents the form for company address
|
|
||||||
type AddressForm struct {
|
|
||||||
Street string `json:"street" valid:"required,length(5|200)"`
|
|
||||||
City string `json:"city" valid:"required,length(2|100)"`
|
|
||||||
State string `json:"state" valid:"required,length(2|100)"`
|
|
||||||
PostalCode string `json:"postal_code" valid:"required,length(3|20)"`
|
|
||||||
Country string `json:"country" valid:"required,length(2|100)"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// ContactPersonForm represents the form for contact person
|
|
||||||
type ContactPersonForm struct {
|
|
||||||
FirstName string `json:"first_name" valid:"required,length(2|50)"`
|
|
||||||
LastName string `json:"last_name" valid:"required,length(2|50)"`
|
|
||||||
FullName string `json:"full_name" valid:"required,length(2|100)"`
|
|
||||||
Position string `json:"position" valid:"required,length(2|100)"`
|
|
||||||
Email string `json:"email" valid:"required,email"`
|
|
||||||
Phone string `json:"phone" valid:"required,length(10|20)"`
|
|
||||||
Mobile *string `json:"mobile,omitempty" valid:"optional,length(10|20)"`
|
|
||||||
IsPrimary bool `json:"is_primary"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompanyTagsForm represents the form for company tags
|
|
||||||
type CompanyTagsForm struct {
|
|
||||||
CPVCodes []string `json:"cpv_codes,omitempty" valid:"optional"`
|
|
||||||
Categories []string `json:"categories,omitempty" valid:"optional"`
|
|
||||||
Keywords []string `json:"keywords,omitempty" valid:"optional"`
|
|
||||||
Specializations []string `json:"specializations,omitempty" valid:"optional"`
|
|
||||||
Certifications []string `json:"certifications,omitempty" valid:"optional"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// CompanyListResponse represents the response for listing companies
|
// CompanyListResponse represents the response for listing companies
|
||||||
type CompanyListResponse struct {
|
type CompanyListResponse struct {
|
||||||
Companies []*CompanyResponse `json:"companies"`
|
Companies []*CompanyResponse `json:"companies"`
|
||||||
Total int64 `json:"total"`
|
Meta *response.Meta `json:"-"`
|
||||||
Limit int `json:"limit"`
|
|
||||||
Offset int `json:"offset"`
|
|
||||||
TotalPages int `json:"total_pages"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Company search and matching DTOs
|
// CompanyResponse represents the company data sent in API responses
|
||||||
type CompanySearchForm struct {
|
type CompanyResponse struct {
|
||||||
Query *string `query:"q" valid:"optional"`
|
ID string `json:"id"`
|
||||||
CPVCodes []string `query:"cpv_codes" valid:"optional"`
|
Name string `json:"name"`
|
||||||
Categories []string `query:"categories" valid:"optional"`
|
Type string `json:"type"`
|
||||||
Keywords []string `query:"keywords" valid:"optional"`
|
Status string `json:"status"`
|
||||||
Specializations []string `query:"specializations" valid:"optional"`
|
RegistrationNumber string `json:"registration_number"`
|
||||||
Industry *string `query:"industry" valid:"optional"`
|
TaxID string `json:"tax_id"`
|
||||||
Location *string `query:"location" valid:"optional"`
|
Industry string `json:"industry"`
|
||||||
MinEmployees *int `query:"min_employees" valid:"optional,min(1)"`
|
Description *string `json:"description,omitempty"`
|
||||||
MaxEmployees *int `query:"max_employees" valid:"optional,min(1)"`
|
Website *string `json:"website,omitempty"`
|
||||||
MinRevenue *float64 `query:"min_revenue" valid:"optional,min(0)"`
|
EmployeeCount *int `json:"employee_count,omitempty"`
|
||||||
MaxRevenue *float64 `query:"max_revenue" valid:"optional,min(0)"`
|
AnnualRevenue *float64 `json:"annual_revenue,omitempty"`
|
||||||
IsVerified *bool `query:"is_verified" valid:"optional"`
|
FoundedYear *int `json:"founded_year,omitempty"`
|
||||||
IsCompliant *bool `query:"is_compliant" valid:"optional"`
|
Address *Address `json:"address,omitempty"`
|
||||||
Limit *int `query:"limit" valid:"optional,range(1|100)"`
|
Phone *string `json:"phone,omitempty"`
|
||||||
Offset *int `query:"offset" valid:"optional,min(0)"`
|
Email *string `json:"email,omitempty"`
|
||||||
|
Tags *CompanyTags `json:"tags,omitempty"`
|
||||||
|
IsVerified bool `json:"is_verified"`
|
||||||
|
IsCompliant bool `json:"is_compliant"`
|
||||||
|
ComplianceNotes *string `json:"compliance_notes,omitempty"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
Timezone string `json:"timezone"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
|
UpdatedAt int64 `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Company statistics DTOs
|
// ToResponse converts Company to CompanyResponse
|
||||||
type CompanyStatsResponse struct {
|
func (c *Company) ToResponse() *CompanyResponse {
|
||||||
TotalCompanies int64 `json:"total_companies"`
|
return &CompanyResponse{
|
||||||
ActiveCompanies int64 `json:"active_companies"`
|
ID: c.ID.Hex(),
|
||||||
VerifiedCompanies int64 `json:"verified_companies"`
|
Name: c.Name,
|
||||||
CompaniesByType map[string]int64 `json:"companies_by_type"`
|
Type: string(c.Type),
|
||||||
CompaniesByIndustry map[string]int64 `json:"companies_by_industry"`
|
Status: string(c.Status),
|
||||||
CompaniesByStatus map[string]int64 `json:"companies_by_status"`
|
RegistrationNumber: c.RegistrationNumber,
|
||||||
TopCategories []CategoryCount `json:"top_categories"`
|
TaxID: c.TaxID,
|
||||||
TopCPVCodes []CPVCodeCount `json:"top_cpv_codes"`
|
Industry: c.Industry,
|
||||||
|
Description: c.Description,
|
||||||
|
Website: c.Website,
|
||||||
|
EmployeeCount: c.EmployeeCount,
|
||||||
|
AnnualRevenue: c.AnnualRevenue,
|
||||||
|
FoundedYear: c.FoundedYear,
|
||||||
|
Address: c.Address,
|
||||||
|
Phone: c.Phone,
|
||||||
|
Email: c.Email,
|
||||||
|
Tags: c.Tags,
|
||||||
|
IsVerified: c.IsVerified,
|
||||||
|
IsCompliant: c.IsCompliant,
|
||||||
|
ComplianceNotes: c.ComplianceNotes,
|
||||||
|
Language: c.Language,
|
||||||
|
Currency: c.Currency,
|
||||||
|
Timezone: c.Timezone,
|
||||||
|
CreatedAt: c.CreatedAt,
|
||||||
|
UpdatedAt: c.UpdatedAt,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type CategoryCount struct {
|
// CompanyProfileResponse represents the company profile data sent in API responses
|
||||||
Category string `json:"category"`
|
type CompanyProfileResponse struct {
|
||||||
Count int64 `json:"count"`
|
ID string `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
RegistrationNumber string `json:"registration_number"`
|
||||||
|
Industry string `json:"industry"`
|
||||||
|
FoundedYear *int `json:"founded_year,omitempty"`
|
||||||
|
CreatedAt int64 `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type CPVCodeCount struct {
|
// ToResponse converts Company to CompanyResponse
|
||||||
CPVCode string `json:"cpv_code"`
|
func (c *Company) ToProfileResponse() *CompanyProfileResponse {
|
||||||
Count int64 `json:"count"`
|
return &CompanyProfileResponse{
|
||||||
|
ID: c.ID.Hex(),
|
||||||
|
Name: c.Name,
|
||||||
|
RegistrationNumber: c.RegistrationNumber,
|
||||||
|
Industry: c.Industry,
|
||||||
|
FoundedYear: c.FoundedYear,
|
||||||
|
CreatedAt: c.CreatedAt,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-532
@@ -1,7 +1,6 @@
|
|||||||
package company
|
package company
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
|
||||||
"tm/internal/user"
|
"tm/internal/user"
|
||||||
"tm/pkg/logger"
|
"tm/pkg/logger"
|
||||||
"tm/pkg/response"
|
"tm/pkg/response"
|
||||||
@@ -44,23 +43,23 @@ func (h *Handler) MyCompany(c echo.Context) error {
|
|||||||
return response.Unauthorized(c, "User not authenticated")
|
return response.Unauthorized(c, "User not authenticated")
|
||||||
}
|
}
|
||||||
|
|
||||||
company, err := h.service.GetCompanyByID(c.Request().Context(), companyID)
|
company, err := h.service.GetMyCompany(c.Request().Context(), companyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.InternalServerError(c, "Failed to get company")
|
return response.InternalServerError(c, "Failed to get company")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, company.ToProfileResponse(), "Company retrieved successfully")
|
return response.Success(c, company, "Company retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// **************************** ADMIN ENDPOINTS ****************************
|
// **************************** ADMIN ENDPOINTS ****************************
|
||||||
|
|
||||||
// CreateCompany creates a new company (Web Panel)
|
// Create creates a new company (Web Panel)
|
||||||
// @Summary Create a new company
|
// @Summary Create a new company
|
||||||
// @Description Create a new company with comprehensive information including business details, address, tags, and customer assignment. This endpoint is used by the web panel for full company registration.
|
// @Description Create a new company with comprehensive information including business details, address, tags, and customer assignment. This endpoint is used by the web panel for full company registration.
|
||||||
// @Tags Admin-Companies
|
// @Tags Admin-Companies
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param company body CreateCompanyForm true "Company information including type, business details, address, tags, and optional customer assignment"
|
// @Param company body CompanyForm true "Company information including type, business details, address, tags, and optional customer assignment"
|
||||||
// @Success 201 {object} response.APIResponse{data=CompanyResponse} "Company created successfully"
|
// @Success 201 {object} response.APIResponse{data=CompanyResponse} "Company created successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
||||||
// @Failure 409 {object} response.APIResponse "Conflict - Company with this name/registration/tax ID already exists"
|
// @Failure 409 {object} response.APIResponse "Conflict - Company with this name/registration/tax ID already exists"
|
||||||
@@ -68,19 +67,13 @@ func (h *Handler) MyCompany(c echo.Context) error {
|
|||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/companies [post]
|
// @Router /admin/v1/companies [post]
|
||||||
func (h *Handler) CreateCompany(c echo.Context) error {
|
func (h *Handler) Create(c echo.Context) error {
|
||||||
form, err := response.Parse[CreateCompanyForm](c)
|
form, err := response.Parse[CompanyForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
company, err := h.service.Create(c.Request().Context(), form)
|
||||||
createdBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
company, err := h.service.CreateCompany(c.Request().Context(), form, &createdBy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "company with this name already exists" ||
|
if err.Error() == "company with this name already exists" ||
|
||||||
err.Error() == "company with this registration number already exists" ||
|
err.Error() == "company with this registration number already exists" ||
|
||||||
@@ -90,7 +83,7 @@ func (h *Handler) CreateCompany(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to create company")
|
return response.InternalServerError(c, "Failed to create company")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Created(c, company.ToResponse(), "Company created successfully")
|
return response.Created(c, company, "Company created successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompany retrieves a company by ID (Web Panel)
|
// GetCompany retrieves a company by ID (Web Panel)
|
||||||
@@ -106,10 +99,10 @@ func (h *Handler) CreateCompany(c echo.Context) error {
|
|||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/companies/{id} [get]
|
// @Router /admin/v1/companies/{id} [get]
|
||||||
func (h *Handler) GetCompany(c echo.Context) error {
|
func (h *Handler) Get(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
company, err := h.service.GetCompanyByID(c.Request().Context(), id)
|
company, err := h.service.GetByID(c.Request().Context(), id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "company not found" {
|
if err.Error() == "company not found" {
|
||||||
return response.NotFound(c, "Company not found")
|
return response.NotFound(c, "Company not found")
|
||||||
@@ -117,17 +110,17 @@ func (h *Handler) GetCompany(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to get company")
|
return response.InternalServerError(c, "Failed to get company")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, company.ToResponse(), "Company retrieved successfully")
|
return response.Success(c, company, "Company retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompany updates a company (Web Panel)
|
// Update updates a company (Web Panel)
|
||||||
// @Summary Update company information
|
// @Summary Update company information
|
||||||
// @Description Update company information including business details, address, tags, and customer assignment
|
// @Description Update company information including business details, address, tags, and customer assignment
|
||||||
// @Tags Admin-Companies
|
// @Tags Admin-Companies
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Company ID"
|
// @Param id path string true "Company ID"
|
||||||
// @Param company body UpdateCompanyForm true "Company update information"
|
// @Param company body CompanyForm true "Company update information"
|
||||||
// @Success 200 {object} response.APIResponse{data=CompanyResponse} "Company updated successfully"
|
// @Success 200 {object} response.APIResponse{data=CompanyResponse} "Company updated successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
||||||
@@ -136,20 +129,14 @@ func (h *Handler) GetCompany(c echo.Context) error {
|
|||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/companies/{id} [put]
|
// @Router /admin/v1/companies/{id} [put]
|
||||||
func (h *Handler) UpdateCompany(c echo.Context) error {
|
func (h *Handler) Update(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
form, err := response.Parse[UpdateCompanyForm](c)
|
form, err := response.Parse[CompanyForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
company, err := h.service.Update(c.Request().Context(), id, form)
|
||||||
updatedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
company, err := h.service.UpdateCompany(c.Request().Context(), id, form, &updatedBy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "company not found" {
|
if err.Error() == "company not found" {
|
||||||
return response.NotFound(c, "Company not found")
|
return response.NotFound(c, "Company not found")
|
||||||
@@ -162,10 +149,10 @@ func (h *Handler) UpdateCompany(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to update company")
|
return response.InternalServerError(c, "Failed to update company")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, company.ToResponse(), "Company updated successfully")
|
return response.Success(c, company, "Company updated successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCompany deletes a company (Web Panel)
|
// Delete deletes a company (Web Panel)
|
||||||
// @Summary Delete company
|
// @Summary Delete company
|
||||||
// @Description Soft delete a company by setting status to inactive
|
// @Description Soft delete a company by setting status to inactive
|
||||||
// @Tags Admin-Companies
|
// @Tags Admin-Companies
|
||||||
@@ -178,16 +165,10 @@ func (h *Handler) UpdateCompany(c echo.Context) error {
|
|||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/companies/{id} [delete]
|
// @Router /admin/v1/companies/{id} [delete]
|
||||||
func (h *Handler) DeleteCompany(c echo.Context) error {
|
func (h *Handler) Delete(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
err := h.service.Delete(c.Request().Context(), id)
|
||||||
deletedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.service.DeleteCompany(c.Request().Context(), id, &deletedBy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "company not found" {
|
if err.Error() == "company not found" {
|
||||||
return response.NotFound(c, "Company not found")
|
return response.NotFound(c, "Company not found")
|
||||||
@@ -200,53 +181,7 @@ func (h *Handler) DeleteCompany(c echo.Context) error {
|
|||||||
}, "Company deleted successfully")
|
}, "Company deleted successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCompanies lists companies with filters and pagination (Web Panel)
|
// Search searches companies with advanced filters (Web Panel)
|
||||||
// @Summary List companies with filters and pagination
|
|
||||||
// @Description Retrieve a paginated list of companies with advanced filtering options including search, type, status, industry, verification status, tags, and business criteria. Supports sorting and pagination.
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param search query string false "Search term to filter companies by name, description, or industry"
|
|
||||||
// @Param type query string false "Filter by company type" Enums(private, public, government, ngo, startup)
|
|
||||||
// @Param status query string false "Filter by company status" Enums(active, inactive, suspended, pending)
|
|
||||||
// @Param industry query string false "Filter by industry"
|
|
||||||
// @Param is_verified query boolean false "Filter by verification status"
|
|
||||||
// @Param is_compliant query boolean false "Filter by compliance status"
|
|
||||||
// @Param cpv_codes query array false "Filter by CPV codes"
|
|
||||||
// @Param categories query array false "Filter by categories"
|
|
||||||
// @Param keywords query array false "Filter by keywords"
|
|
||||||
// @Param specializations query array false "Filter by specializations"
|
|
||||||
// @Param employee_count_min query integer false "Minimum employee count"
|
|
||||||
// @Param employee_count_max query integer false "Maximum employee count"
|
|
||||||
// @Param annual_revenue_min query number false "Minimum annual revenue"
|
|
||||||
// @Param annual_revenue_max query number false "Maximum annual revenue"
|
|
||||||
// @Param founded_year_min query integer false "Minimum founded year"
|
|
||||||
// @Param founded_year_max query integer false "Maximum founded year"
|
|
||||||
// @Param limit query integer false "Number of companies per page (1-100)" minimum(1) maximum(100) default(20)
|
|
||||||
// @Param offset query integer false "Number of companies to skip for pagination" minimum(0) default(0)
|
|
||||||
// @Param sort_by query string false "Field to sort by" Enums(name, type, industry, created_at, updated_at, status, employee_count, annual_revenue) default(created_at)
|
|
||||||
// @Param sort_order query string false "Sort order" Enums(asc, desc) default(desc)
|
|
||||||
// @Success 200 {object} response.APIResponse{data=CompanyListResponse} "Companies retrieved successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid query parameters"
|
|
||||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid query parameters"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies [get]
|
|
||||||
func (h *Handler) ListCompanies(c echo.Context) error {
|
|
||||||
form, err := response.Parse[ListCompaniesForm](c)
|
|
||||||
if err != nil {
|
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
result, err := h.service.ListCompanies(c.Request().Context(), form)
|
|
||||||
if err != nil {
|
|
||||||
return response.InternalServerError(c, "Failed to list companies")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, result, "Companies retrieved successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchCompanies searches companies with advanced filters (Web Panel)
|
|
||||||
// @Summary Advanced search for companies
|
// @Summary Advanced search for companies
|
||||||
// @Description Search companies with advanced filtering capabilities including tags, business criteria, and location
|
// @Description Search companies with advanced filtering capabilities including tags, business criteria, and location
|
||||||
// @Tags Admin-Companies
|
// @Tags Admin-Companies
|
||||||
@@ -272,29 +207,29 @@ func (h *Handler) ListCompanies(c echo.Context) error {
|
|||||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid query parameters"
|
// @Failure 422 {object} response.APIResponse "Validation error - Invalid query parameters"
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/companies/search [get]
|
// @Router /admin/v1/companies [get]
|
||||||
func (h *Handler) SearchCompanies(c echo.Context) error {
|
func (h *Handler) Search(c echo.Context) error {
|
||||||
form, err := response.Parse[CompanySearchForm](c)
|
form, err := response.Parse[SearchForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
result, err := h.service.SearchCompanies(c.Request().Context(), form)
|
result, err := h.service.SearchCompanies(c.Request().Context(), form, response.NewPagination(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.InternalServerError(c, "Failed to search companies")
|
return response.InternalServerError(c, "Failed to list companies")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, result, "Companies search completed")
|
return response.SuccessWithMeta(c, result, result.Meta, "Companies retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompanyStatus updates company status (Web Panel)
|
// UpdateStatus updates company status (Web Panel)
|
||||||
// @Summary Update company status
|
// @Summary Update company status
|
||||||
// @Description Update company account status (active, inactive, suspended, pending)
|
// @Description Update company account status (active, inactive, suspended, pending)
|
||||||
// @Tags Admin-Companies
|
// @Tags Admin-Companies
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Company ID"
|
// @Param id path string true "Company ID"
|
||||||
// @Param status body UpdateCompanyStatusForm true "New company status"
|
// @Param status body StatusForm true "New company status"
|
||||||
// @Success 200 {object} response.APIResponse "Company status updated successfully"
|
// @Success 200 {object} response.APIResponse "Company status updated successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
||||||
@@ -302,20 +237,14 @@ func (h *Handler) SearchCompanies(c echo.Context) error {
|
|||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/companies/{id}/status [patch]
|
// @Router /admin/v1/companies/{id}/status [patch]
|
||||||
func (h *Handler) UpdateCompanyStatus(c echo.Context) error {
|
func (h *Handler) UpdateStatus(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
form, err := response.Parse[UpdateCompanyStatusForm](c)
|
form, err := response.Parse[StatusForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
err = h.service.UpdateStatus(c.Request().Context(), id, form)
|
||||||
updatedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.service.UpdateCompanyStatus(c.Request().Context(), id, form, &updatedBy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "company not found" {
|
if err.Error() == "company not found" {
|
||||||
return response.NotFound(c, "Company not found")
|
return response.NotFound(c, "Company not found")
|
||||||
@@ -328,14 +257,14 @@ func (h *Handler) UpdateCompanyStatus(c echo.Context) error {
|
|||||||
}, "Company status updated successfully")
|
}, "Company status updated successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompanyVerification updates company verification status (Web Panel)
|
// UpdateVerification updates company verification status (Web Panel)
|
||||||
// @Summary Update company verification status
|
// @Summary Update company verification status
|
||||||
// @Description Update company verification and compliance status
|
// @Description Update company verification and compliance status
|
||||||
// @Tags Admin-Companies
|
// @Tags Admin-Companies
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param id path string true "Company ID"
|
// @Param id path string true "Company ID"
|
||||||
// @Param verification body UpdateCompanyVerificationForm true "Verification status update"
|
// @Param verification body VerificationForm true "Verification status update"
|
||||||
// @Success 200 {object} response.APIResponse "Company verification updated successfully"
|
// @Success 200 {object} response.APIResponse "Company verification updated successfully"
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
||||||
@@ -343,20 +272,14 @@ func (h *Handler) UpdateCompanyStatus(c echo.Context) error {
|
|||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
// @Failure 500 {object} response.APIResponse "Internal server error"
|
||||||
// @Security BearerAuth
|
// @Security BearerAuth
|
||||||
// @Router /admin/v1/companies/{id}/verification [patch]
|
// @Router /admin/v1/companies/{id}/verification [patch]
|
||||||
func (h *Handler) UpdateCompanyVerification(c echo.Context) error {
|
func (h *Handler) UpdateVerification(c echo.Context) error {
|
||||||
id := c.Param("id")
|
id := c.Param("id")
|
||||||
form, err := response.Parse[UpdateCompanyVerificationForm](c)
|
form, err := response.Parse[VerificationForm](c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
return response.ValidationError(c, "Invalid request data", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
err = h.service.UpdateVerification(c.Request().Context(), id, form)
|
||||||
updatedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.service.UpdateCompanyVerification(c.Request().Context(), id, form, &updatedBy)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "company not found" {
|
if err.Error() == "company not found" {
|
||||||
return response.NotFound(c, "Company not found")
|
return response.NotFound(c, "Company not found")
|
||||||
@@ -368,421 +291,3 @@ func (h *Handler) UpdateCompanyVerification(c echo.Context) error {
|
|||||||
"message": "Company verification updated successfully",
|
"message": "Company verification updated successfully",
|
||||||
}, "Company verification updated successfully")
|
}, "Company verification updated successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompanyTags updates company tags (Web Panel)
|
|
||||||
// @Summary Update company tags
|
|
||||||
// @Description Update company tags (CPV, categories, keywords, specializations, certifications)
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param id path string true "Company ID"
|
|
||||||
// @Param tags body UpdateCompanyTagsForm true "Tags update information"
|
|
||||||
// @Success 200 {object} response.APIResponse "Company tags updated successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
|
||||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/{id}/tags [put]
|
|
||||||
func (h *Handler) UpdateCompanyTags(c echo.Context) error {
|
|
||||||
id := c.Param("id")
|
|
||||||
form, err := response.Parse[UpdateCompanyTagsForm](c)
|
|
||||||
if err != nil {
|
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
|
||||||
updatedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.service.UpdateCompanyTags(c.Request().Context(), id, form, &updatedBy)
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "company not found" {
|
|
||||||
return response.NotFound(c, "Company not found")
|
|
||||||
}
|
|
||||||
return response.InternalServerError(c, "Failed to update company tags")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, map[string]interface{}{
|
|
||||||
"message": "Company tags updated successfully",
|
|
||||||
}, "Company tags updated successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTags adds tags to a company (Web Panel)
|
|
||||||
// @Summary Add tags to company
|
|
||||||
// @Description Add specific tags to a company
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param id path string true "Company ID"
|
|
||||||
// @Param tags body AddTagsForm true "Tags to add"
|
|
||||||
// @Success 200 {object} response.APIResponse "Tags added successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
|
||||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/{id}/tags/add [post]
|
|
||||||
func (h *Handler) AddTags(c echo.Context) error {
|
|
||||||
id := c.Param("id")
|
|
||||||
form, err := response.Parse[AddTagsForm](c)
|
|
||||||
if err != nil {
|
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
|
||||||
updatedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.service.AddTags(c.Request().Context(), id, form, &updatedBy)
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "company not found" {
|
|
||||||
return response.NotFound(c, "Company not found")
|
|
||||||
}
|
|
||||||
return response.InternalServerError(c, "Failed to add tags")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, map[string]interface{}{
|
|
||||||
"message": "Tags added successfully",
|
|
||||||
}, "Tags added successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveTags removes tags from a company (Web Panel)
|
|
||||||
// @Summary Remove tags from company
|
|
||||||
// @Description Remove specific tags from a company
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param id path string true "Company ID"
|
|
||||||
// @Param tags body RemoveTagsForm true "Tags to remove"
|
|
||||||
// @Success 200 {object} response.APIResponse "Tags removed successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
|
||||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/{id}/tags/remove [post]
|
|
||||||
func (h *Handler) RemoveTags(c echo.Context) error {
|
|
||||||
id := c.Param("id")
|
|
||||||
form, err := response.Parse[RemoveTagsForm](c)
|
|
||||||
if err != nil {
|
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
|
||||||
updatedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.service.RemoveTags(c.Request().Context(), id, form, &updatedBy)
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "company not found" {
|
|
||||||
return response.NotFound(c, "Company not found")
|
|
||||||
}
|
|
||||||
return response.InternalServerError(c, "Failed to remove tags")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, map[string]interface{}{
|
|
||||||
"message": "Tags removed successfully",
|
|
||||||
}, "Tags removed successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyCompany verifies a company (Web Panel)
|
|
||||||
// @Summary Verify company
|
|
||||||
// @Description Mark a company as verified
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param id path string true "Company ID"
|
|
||||||
// @Success 200 {object} response.APIResponse "Company verified successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid company ID"
|
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/{id}/verify [post]
|
|
||||||
func (h *Handler) VerifyCompany(c echo.Context) error {
|
|
||||||
id := c.Param("id")
|
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
|
||||||
verifiedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.service.VerifyCompany(c.Request().Context(), id, &verifiedBy)
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "company not found" {
|
|
||||||
return response.NotFound(c, "Company not found")
|
|
||||||
}
|
|
||||||
return response.InternalServerError(c, "Failed to verify company")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, map[string]interface{}{
|
|
||||||
"message": "Company verified successfully",
|
|
||||||
}, "Company verified successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SuspendCompany suspends a company (Web Panel)
|
|
||||||
// @Summary Suspend company
|
|
||||||
// @Description Suspend a company account with reason
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param id path string true "Company ID"
|
|
||||||
// @Param suspension body SuspendCompanyForm true "Suspension information"
|
|
||||||
// @Success 200 {object} response.APIResponse "Company suspended successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid input data"
|
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
|
||||||
// @Failure 422 {object} response.APIResponse "Validation error - Invalid request data"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/{id}/suspend [post]
|
|
||||||
func (h *Handler) SuspendCompany(c echo.Context) error {
|
|
||||||
id := c.Param("id")
|
|
||||||
form, err := response.Parse[SuspendCompanyForm](c)
|
|
||||||
if err != nil {
|
|
||||||
return response.ValidationError(c, "Invalid request data", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
|
||||||
suspendedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.service.SuspendCompany(c.Request().Context(), id, form.Reason, &suspendedBy)
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "company not found" {
|
|
||||||
return response.NotFound(c, "Company not found")
|
|
||||||
}
|
|
||||||
return response.InternalServerError(c, "Failed to suspend company")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, map[string]interface{}{
|
|
||||||
"message": "Company suspended successfully",
|
|
||||||
}, "Company suspended successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActivateCompany activates a company (Web Panel)
|
|
||||||
// @Summary Activate company
|
|
||||||
// @Description Activate a suspended company account
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param id path string true "Company ID"
|
|
||||||
// @Success 200 {object} response.APIResponse "Company activated successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid company ID"
|
|
||||||
// @Failure 404 {object} response.APIResponse "Not found - Company not found"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/{id}/activate [post]
|
|
||||||
func (h *Handler) ActivateCompany(c echo.Context) error {
|
|
||||||
id := c.Param("id")
|
|
||||||
|
|
||||||
// Get user ID from JWT token context
|
|
||||||
activatedBy, err := user.GetUserIDFromContext(c)
|
|
||||||
if err != nil {
|
|
||||||
return response.Unauthorized(c, "User not authenticated")
|
|
||||||
}
|
|
||||||
|
|
||||||
err = h.service.ActivateCompany(c.Request().Context(), id, &activatedBy)
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "company not found" {
|
|
||||||
return response.NotFound(c, "Company not found")
|
|
||||||
}
|
|
||||||
return response.InternalServerError(c, "Failed to activate company")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, map[string]interface{}{
|
|
||||||
"message": "Company activated successfully",
|
|
||||||
}, "Company activated successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompanyStats returns company statistics (Web Panel)
|
|
||||||
// @Summary Get company statistics
|
|
||||||
// @Description Get comprehensive company statistics for dashboard
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Success 200 {object} response.APIResponse{data=CompanyStatsResponse} "Company statistics retrieved successfully"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/stats [get]
|
|
||||||
func (h *Handler) GetCompanyStats(c echo.Context) error {
|
|
||||||
stats, err := h.service.GetCompanyStats(c.Request().Context())
|
|
||||||
if err != nil {
|
|
||||||
return response.InternalServerError(c, "Failed to get company statistics")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.Success(c, stats, "Company statistics retrieved successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompaniesByType retrieves companies by type (Web Panel)
|
|
||||||
// @Summary Get companies by type
|
|
||||||
// @Description Retrieve companies filtered by their type (private, public, government, ngo, startup)
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param type path string true "Company type" Enums(private, public, government, ngo, startup)
|
|
||||||
// @Param limit query integer false "Number of companies per page (1-100)" minimum(1) maximum(100) default(20)
|
|
||||||
// @Param offset query integer false "Number of companies to skip for pagination" minimum(0) default(0)
|
|
||||||
// @Success 200 {object} response.APIResponse{data=[]CompanyResponse,meta=response.Meta} "Companies retrieved successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid company type"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/type/{type} [get]
|
|
||||||
func (h *Handler) GetCompaniesByType(c echo.Context) error {
|
|
||||||
companyTypeStr := c.Param("type")
|
|
||||||
companyType := CompanyType(companyTypeStr)
|
|
||||||
|
|
||||||
// Validate company type
|
|
||||||
if companyType != CompanyTypePrivate && companyType != CompanyTypePublic &&
|
|
||||||
companyType != CompanyTypeGovernment && companyType != CompanyTypeNgo &&
|
|
||||||
companyType != CompanyTypeStartup {
|
|
||||||
return response.BadRequest(c, "Invalid company type", "Must be private, public, government, ngo, or startup")
|
|
||||||
}
|
|
||||||
|
|
||||||
limit := 20
|
|
||||||
if limitStr := c.QueryParam("limit"); limitStr != "" {
|
|
||||||
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 && parsedLimit <= 100 {
|
|
||||||
limit = parsedLimit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := 0
|
|
||||||
if offsetStr := c.QueryParam("offset"); offsetStr != "" {
|
|
||||||
if parsedOffset, err := strconv.Atoi(offsetStr); err == nil && parsedOffset >= 0 {
|
|
||||||
offset = parsedOffset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companies, total, err := h.service.GetCompaniesByType(c.Request().Context(), companyType, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
return response.InternalServerError(c, "Failed to retrieve companies by type")
|
|
||||||
}
|
|
||||||
|
|
||||||
var companyResponses []*CompanyResponse
|
|
||||||
for _, company := range companies {
|
|
||||||
companyResponses = append(companyResponses, company.ToResponse())
|
|
||||||
}
|
|
||||||
|
|
||||||
meta := &response.Meta{
|
|
||||||
Total: int(total),
|
|
||||||
Limit: limit,
|
|
||||||
Offset: offset,
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.SuccessWithMeta(c, companyResponses, meta, "Companies retrieved successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompaniesByStatus retrieves companies by status (Web Panel)
|
|
||||||
// @Summary Get companies by status
|
|
||||||
// @Description Retrieve companies filtered by their status (active, inactive, suspended, pending)
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param status path string true "Company status" Enums(active, inactive, suspended, pending)
|
|
||||||
// @Param limit query integer false "Number of companies per page (1-100)" minimum(1) maximum(100) default(20)
|
|
||||||
// @Param offset query integer false "Number of companies to skip for pagination" minimum(0) default(0)
|
|
||||||
// @Success 200 {object} response.APIResponse{data=[]CompanyResponse,meta=response.Meta} "Companies retrieved successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid company status"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/status/{status} [get]
|
|
||||||
func (h *Handler) GetCompaniesByStatus(c echo.Context) error {
|
|
||||||
statusStr := c.Param("status")
|
|
||||||
status := CompanyStatus(statusStr)
|
|
||||||
|
|
||||||
// Validate company status
|
|
||||||
if status != CompanyStatusActive && status != CompanyStatusInactive &&
|
|
||||||
status != CompanyStatusSuspended && status != CompanyStatusPending {
|
|
||||||
return response.BadRequest(c, "Invalid company status", "Must be active, inactive, suspended, or pending")
|
|
||||||
}
|
|
||||||
|
|
||||||
limit := 20
|
|
||||||
if limitStr := c.QueryParam("limit"); limitStr != "" {
|
|
||||||
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 && parsedLimit <= 100 {
|
|
||||||
limit = parsedLimit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := 0
|
|
||||||
if offsetStr := c.QueryParam("offset"); offsetStr != "" {
|
|
||||||
if parsedOffset, err := strconv.Atoi(offsetStr); err == nil && parsedOffset >= 0 {
|
|
||||||
offset = parsedOffset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companies, total, err := h.service.GetCompaniesByStatus(c.Request().Context(), status, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
return response.InternalServerError(c, "Failed to retrieve companies by status")
|
|
||||||
}
|
|
||||||
|
|
||||||
var companyResponses []*CompanyResponse
|
|
||||||
for _, company := range companies {
|
|
||||||
companyResponses = append(companyResponses, company.ToResponse())
|
|
||||||
}
|
|
||||||
|
|
||||||
meta := &response.Meta{
|
|
||||||
Total: int(total),
|
|
||||||
Limit: limit,
|
|
||||||
Offset: offset,
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.SuccessWithMeta(c, companyResponses, meta, "Companies retrieved successfully")
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompaniesByIndustry retrieves companies by industry (Web Panel)
|
|
||||||
// @Summary Get companies by industry
|
|
||||||
// @Description Retrieve companies filtered by their industry
|
|
||||||
// @Tags Admin-Companies
|
|
||||||
// @Accept json
|
|
||||||
// @Produce json
|
|
||||||
// @Param industry path string true "Industry"
|
|
||||||
// @Param limit query integer false "Number of companies per page (1-100)" minimum(1) maximum(100) default(20)
|
|
||||||
// @Param offset query integer false "Number of companies to skip for pagination" minimum(0) default(0)
|
|
||||||
// @Success 200 {object} response.APIResponse{data=[]CompanyResponse,meta=response.Meta} "Companies retrieved successfully"
|
|
||||||
// @Failure 400 {object} response.APIResponse "Bad request - Invalid industry"
|
|
||||||
// @Failure 500 {object} response.APIResponse "Internal server error"
|
|
||||||
// @Security BearerAuth
|
|
||||||
// @Router /admin/v1/companies/industry/{industry} [get]
|
|
||||||
func (h *Handler) GetCompaniesByIndustry(c echo.Context) error {
|
|
||||||
industry := c.Param("industry")
|
|
||||||
|
|
||||||
limit := 20
|
|
||||||
if limitStr := c.QueryParam("limit"); limitStr != "" {
|
|
||||||
if parsedLimit, err := strconv.Atoi(limitStr); err == nil && parsedLimit > 0 && parsedLimit <= 100 {
|
|
||||||
limit = parsedLimit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := 0
|
|
||||||
if offsetStr := c.QueryParam("offset"); offsetStr != "" {
|
|
||||||
if parsedOffset, err := strconv.Atoi(offsetStr); err == nil && parsedOffset >= 0 {
|
|
||||||
offset = parsedOffset
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companies, total, err := h.service.GetCompaniesByIndustry(c.Request().Context(), industry, limit, offset)
|
|
||||||
if err != nil {
|
|
||||||
return response.InternalServerError(c, "Failed to retrieve companies by industry")
|
|
||||||
}
|
|
||||||
|
|
||||||
var companyResponses []*CompanyResponse
|
|
||||||
for _, company := range companies {
|
|
||||||
companyResponses = append(companyResponses, company.ToResponse())
|
|
||||||
}
|
|
||||||
|
|
||||||
meta := &response.Meta{
|
|
||||||
Total: int(total),
|
|
||||||
Limit: limit,
|
|
||||||
Offset: offset,
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.SuccessWithMeta(c, companyResponses, meta, "Companies retrieved successfully")
|
|
||||||
}
|
|
||||||
|
|||||||
+89
-538
@@ -5,70 +5,38 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
"tm/pkg/logger"
|
"tm/pkg/logger"
|
||||||
"tm/pkg/mongo"
|
orm "tm/pkg/mongo"
|
||||||
|
"tm/pkg/response"
|
||||||
|
|
||||||
"go.mongodb.org/mongo-driver/bson"
|
"go.mongodb.org/mongo-driver/bson"
|
||||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Repository defines the interface for company data operations
|
// Repository defines the interface for company data operations
|
||||||
type Repository interface {
|
type Repository interface {
|
||||||
Create(ctx context.Context, company *Company) error
|
Create(ctx context.Context, company *Company) error
|
||||||
|
Update(ctx context.Context, company *Company) error
|
||||||
GetByID(ctx context.Context, id string) (*Company, error)
|
GetByID(ctx context.Context, id string) (*Company, error)
|
||||||
|
GetByIDs(ctx context.Context, ids []string) ([]Company, error)
|
||||||
GetByName(ctx context.Context, name string) (*Company, error)
|
GetByName(ctx context.Context, name string) (*Company, error)
|
||||||
GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Company, error)
|
GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Company, error)
|
||||||
GetByTaxID(ctx context.Context, taxID string) (*Company, error)
|
GetByTaxID(ctx context.Context, taxID string) (*Company, error)
|
||||||
GetByIDs(ctx context.Context, ids []string) ([]*Company, error)
|
Search(ctx context.Context, form *SearchForm, pagination *response.Pagination) ([]Company, int64, error)
|
||||||
Update(ctx context.Context, company *Company) error
|
|
||||||
Delete(ctx context.Context, id string) error
|
Delete(ctx context.Context, id string) error
|
||||||
List(ctx context.Context, limit, offset int) ([]*Company, error)
|
|
||||||
Search(ctx context.Context, search string, companyType *string, status *string, industry *string, isVerified *bool, isCompliant *bool, language *string, currency *string, cpvCodes []string, categories []string, keywords []string, specializations []string, employeeCountMin *int, employeeCountMax *int, annualRevenueMin *float64, annualRevenueMax *float64, foundedYearMin *int, foundedYearMax *int, limit, offset int, sortBy, sortOrder string) ([]*Company, error)
|
|
||||||
Count(ctx context.Context) (int64, error)
|
|
||||||
CountByType(ctx context.Context, companyType CompanyType) (int64, error)
|
|
||||||
CountByStatus(ctx context.Context, status CompanyStatus) (int64, error)
|
|
||||||
CountByIndustry(ctx context.Context, industry string) (int64, error)
|
|
||||||
CountVerified(ctx context.Context) (int64, error)
|
|
||||||
UpdateStatus(ctx context.Context, id string, status CompanyStatus) error
|
UpdateStatus(ctx context.Context, id string, status CompanyStatus) error
|
||||||
UpdateVerification(ctx context.Context, id string, isVerified, isCompliant bool, complianceNotes *string) error
|
UpdateVerification(ctx context.Context, id string, isVerified, isCompliant bool, complianceNotes *string) error
|
||||||
UpdateTags(ctx context.Context, id string, tags *CompanyTags) error
|
|
||||||
AddTags(ctx context.Context, id string, cpvCodes []string, categories []string, keywords []string, specializations []string, certifications []string) error
|
|
||||||
RemoveTags(ctx context.Context, id string, cpvCodes []string, categories []string, keywords []string, specializations []string, certifications []string) error
|
|
||||||
GetCompanyStats(ctx context.Context) (*CompanyStatsResponse, error)
|
|
||||||
SearchByTags(ctx context.Context, cpvCodes []string, categories []string, keywords []string, specializations []string, limit, offset int) ([]*Company, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// companyRepository implements the Repository interface using the MongoDB ORM
|
// companyRepository implements the Repository interface using the MongoDB ORM
|
||||||
type companyRepository struct {
|
type companyRepository struct {
|
||||||
ormRepo mongo.Repository[Company]
|
ormRepo orm.Repository[Company]
|
||||||
logger logger.Logger
|
logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCompanyRepository creates a new company repository
|
// NewCompanyRepository creates a new company repository
|
||||||
func NewCompanyRepository(mongoManager *mongo.ConnectionManager, logger logger.Logger) Repository {
|
func NewCompanyRepository(mongoManager *orm.ConnectionManager, logger logger.Logger) Repository {
|
||||||
// Create indexes using the ORM's index management
|
// Create indexes using the ORM's index management
|
||||||
indexes := []mongo.Index{
|
indexes := []orm.Index{
|
||||||
*mongo.CreateUniqueIndex("name_idx", bson.D{{Key: "name", Value: 1}}),
|
*orm.NewIndex("registration_number_idx", bson.D{{Key: "registration_number", Value: 1}}),
|
||||||
*mongo.CreateUniqueIndex("registration_number_idx", bson.D{{Key: "registration_number", Value: 1}}),
|
|
||||||
*mongo.CreateUniqueIndex("tax_id_idx", bson.D{{Key: "tax_id", Value: 1}}),
|
|
||||||
*mongo.NewIndex("type_idx", bson.D{{Key: "type", Value: 1}}),
|
|
||||||
*mongo.NewIndex("status_idx", bson.D{{Key: "status", Value: 1}}),
|
|
||||||
*mongo.NewIndex("industry_idx", bson.D{{Key: "industry", Value: 1}}),
|
|
||||||
*mongo.NewIndex("is_verified_idx", bson.D{{Key: "is_verified", Value: 1}}),
|
|
||||||
*mongo.NewIndex("is_compliant_idx", bson.D{{Key: "is_compliant", Value: 1}}),
|
|
||||||
*mongo.NewIndex("language_idx", bson.D{{Key: "language", Value: 1}}),
|
|
||||||
*mongo.NewIndex("currency_idx", bson.D{{Key: "currency", Value: 1}}),
|
|
||||||
*mongo.NewIndex("employee_count_idx", bson.D{{Key: "employee_count", Value: 1}}),
|
|
||||||
*mongo.NewIndex("annual_revenue_idx", bson.D{{Key: "annual_revenue", Value: 1}}),
|
|
||||||
*mongo.NewIndex("founded_year_idx", bson.D{{Key: "founded_year", Value: 1}}),
|
|
||||||
*mongo.NewIndex("created_at_idx", bson.D{{Key: "created_at", Value: -1}}),
|
|
||||||
// Tag indexes for efficient search
|
|
||||||
*mongo.NewIndex("cpv_codes_idx", bson.D{{Key: "tags.cpv_codes", Value: 1}}),
|
|
||||||
*mongo.NewIndex("categories_idx", bson.D{{Key: "tags.categories", Value: 1}}),
|
|
||||||
*mongo.NewIndex("keywords_idx", bson.D{{Key: "tags.keywords", Value: 1}}),
|
|
||||||
*mongo.NewIndex("specializations_idx", bson.D{{Key: "tags.specializations", Value: 1}}),
|
|
||||||
*mongo.NewIndex("certifications_idx", bson.D{{Key: "tags.certifications", Value: 1}}),
|
|
||||||
// Text index for search
|
|
||||||
*mongo.CreateTextIndex("search_idx", "name", "description", "industry"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create indexes
|
// Create indexes
|
||||||
@@ -80,7 +48,7 @@ func NewCompanyRepository(mongoManager *mongo.ConnectionManager, logger logger.L
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create ORM repository
|
// Create ORM repository
|
||||||
ormRepo := mongo.NewRepository[Company](mongoManager.GetCollection("companies"), logger)
|
ormRepo := orm.NewRepository[Company](mongoManager.GetCollection("companies"), logger)
|
||||||
|
|
||||||
return &companyRepository{
|
return &companyRepository{
|
||||||
ormRepo: ormRepo,
|
ormRepo: ormRepo,
|
||||||
@@ -93,7 +61,6 @@ func (r *companyRepository) Create(ctx context.Context, company *Company) error
|
|||||||
// Set created/updated timestamps using Unix timestamps
|
// Set created/updated timestamps using Unix timestamps
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
company.SetCreatedAt(now)
|
company.SetCreatedAt(now)
|
||||||
company.SetUpdatedAt(now)
|
|
||||||
|
|
||||||
// Use ORM to create company
|
// Use ORM to create company
|
||||||
err := r.ormRepo.Create(ctx, company)
|
err := r.ormRepo.Create(ctx, company)
|
||||||
@@ -119,7 +86,7 @@ func (r *companyRepository) Create(ctx context.Context, company *Company) error
|
|||||||
func (r *companyRepository) GetByID(ctx context.Context, id string) (*Company, error) {
|
func (r *companyRepository) GetByID(ctx context.Context, id string) (*Company, error) {
|
||||||
company, err := r.ormRepo.FindByID(ctx, id)
|
company, err := r.ormRepo.FindByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
if errors.Is(err, orm.ErrDocumentNotFound) {
|
||||||
return nil, errors.New("company not found")
|
return nil, errors.New("company not found")
|
||||||
}
|
}
|
||||||
r.logger.Error("Failed to get company by ID", map[string]interface{}{
|
r.logger.Error("Failed to get company by ID", map[string]interface{}{
|
||||||
@@ -132,108 +99,43 @@ func (r *companyRepository) GetByID(ctx context.Context, id string) (*Company, e
|
|||||||
return company, nil
|
return company, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByName retrieves a company by name
|
// GetByIDs retrieves companies by IDs
|
||||||
func (r *companyRepository) GetByName(ctx context.Context, name string) (*Company, error) {
|
func (r *companyRepository) GetByIDs(ctx context.Context, ids []string) ([]Company, error) {
|
||||||
filter := bson.M{"name": name}
|
companies, err := r.ormRepo.FindAll(ctx, bson.M{"_id": bson.M{"$in": ids}}, orm.NewPaginationBuilder().Build())
|
||||||
company, err := r.ormRepo.FindOne(ctx, filter)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
|
||||||
return nil, errors.New("company not found")
|
|
||||||
}
|
|
||||||
r.logger.Error("Failed to get company by name", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"name": name,
|
|
||||||
})
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return companies.Items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByName retrieves a company by name
|
||||||
|
func (r *companyRepository) GetByName(ctx context.Context, name string) (*Company, error) {
|
||||||
|
company, err := r.ormRepo.FindOne(ctx, bson.M{"name": name})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return company, nil
|
return company, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByRegistrationNumber retrieves a company by registration number
|
// GetByRegistrationNumber retrieves a company by registration number
|
||||||
func (r *companyRepository) GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Company, error) {
|
func (r *companyRepository) GetByRegistrationNumber(ctx context.Context, registrationNumber string) (*Company, error) {
|
||||||
filter := bson.M{"registration_number": registrationNumber}
|
company, err := r.ormRepo.FindOne(ctx, bson.M{"registration_number": registrationNumber})
|
||||||
company, err := r.ormRepo.FindOne(ctx, filter)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
|
||||||
return nil, errors.New("company not found")
|
|
||||||
}
|
|
||||||
r.logger.Error("Failed to get company by registration number", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"registration_number": registrationNumber,
|
|
||||||
})
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return company, nil
|
return company, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByTaxID retrieves a company by tax ID
|
// GetByTaxID retrieves a company by tax ID
|
||||||
func (r *companyRepository) GetByTaxID(ctx context.Context, taxID string) (*Company, error) {
|
func (r *companyRepository) GetByTaxID(ctx context.Context, taxID string) (*Company, error) {
|
||||||
filter := bson.M{"tax_id": taxID}
|
company, err := r.ormRepo.FindOne(ctx, bson.M{"tax_id": taxID})
|
||||||
company, err := r.ormRepo.FindOne(ctx, filter)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, mongo.ErrDocumentNotFound) {
|
|
||||||
return nil, errors.New("company not found")
|
|
||||||
}
|
|
||||||
r.logger.Error("Failed to get company by tax ID", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"tax_id": taxID,
|
|
||||||
})
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return company, nil
|
return company, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByIDs retrieves multiple companies by their IDs
|
|
||||||
func (r *companyRepository) GetByIDs(ctx context.Context, ids []string) ([]*Company, error) {
|
|
||||||
if len(ids) == 0 {
|
|
||||||
return []*Company{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert string IDs to ObjectIDs for MongoDB query
|
|
||||||
objectIDs := make([]interface{}, len(ids))
|
|
||||||
for i, id := range ids {
|
|
||||||
objectID, err := primitive.ObjectIDFromHex(id)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Warn("Invalid company ID format", map[string]interface{}{
|
|
||||||
"id": id,
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
objectIDs[i] = objectID
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(objectIDs) == 0 {
|
|
||||||
return []*Company{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
filter := bson.M{"_id": bson.M{"$in": objectIDs}}
|
|
||||||
companies, err := r.ormRepo.FindMany(ctx, filter, len(ids))
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to get companies by IDs", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"ids": ids,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to pointer slice
|
|
||||||
result := make([]*Company, len(companies))
|
|
||||||
for i := range companies {
|
|
||||||
result[i] = &companies[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.Info("Retrieved companies by IDs", map[string]interface{}{
|
|
||||||
"requested_count": len(ids),
|
|
||||||
"found_count": len(result),
|
|
||||||
})
|
|
||||||
|
|
||||||
return result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update updates a company
|
// Update updates a company
|
||||||
func (r *companyRepository) Update(ctx context.Context, company *Company) error {
|
func (r *companyRepository) Update(ctx context.Context, company *Company) error {
|
||||||
// Set updated timestamp using Unix timestamp
|
// Set updated timestamp using Unix timestamp
|
||||||
@@ -257,20 +159,16 @@ func (r *companyRepository) Update(ctx context.Context, company *Company) error
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete deletes a company (soft delete by setting status to inactive)
|
// Delete deletes a company
|
||||||
func (r *companyRepository) Delete(ctx context.Context, id string) error {
|
func (r *companyRepository) Delete(ctx context.Context, id string) error {
|
||||||
// Get company first
|
// Get company first
|
||||||
company, err := r.GetByID(ctx, id)
|
_, err := r.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update status to inactive
|
|
||||||
company.Status = CompanyStatusInactive
|
|
||||||
company.SetUpdatedAt(time.Now().Unix())
|
|
||||||
|
|
||||||
// Update in database
|
// Update in database
|
||||||
err = r.ormRepo.Update(ctx, company)
|
err = r.ormRepo.Delete(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Failed to delete company", map[string]interface{}{
|
r.logger.Error("Failed to delete company", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
@@ -286,246 +184,122 @@ func (r *companyRepository) Delete(ctx context.Context, id string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// List retrieves companies with pagination
|
|
||||||
func (r *companyRepository) List(ctx context.Context, limit, offset int) ([]*Company, error) {
|
|
||||||
// Build pagination
|
|
||||||
pagination := mongo.NewPaginationBuilder().
|
|
||||||
Limit(limit).
|
|
||||||
Skip(offset).
|
|
||||||
SortDesc("created_at").
|
|
||||||
Build()
|
|
||||||
|
|
||||||
// Only active companies by default
|
|
||||||
filter := bson.M{"status": bson.M{"$ne": CompanyStatusInactive}}
|
|
||||||
|
|
||||||
// Use ORM to find companies
|
|
||||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to list companies", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"limit": limit,
|
|
||||||
"offset": offset,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []Company to []*Company
|
|
||||||
companies := make([]*Company, len(result.Items))
|
|
||||||
for i := range result.Items {
|
|
||||||
companies[i] = &result.Items[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return companies, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search retrieves companies with search and filters
|
// Search retrieves companies with search and filters
|
||||||
func (r *companyRepository) Search(ctx context.Context, search string, companyType *string, status *string, industry *string, isVerified *bool, isCompliant *bool, language *string, currency *string, cpvCodes []string, categories []string, keywords []string, specializations []string, employeeCountMin *int, employeeCountMax *int, annualRevenueMin *float64, annualRevenueMax *float64, foundedYearMin *int, foundedYearMax *int, limit, offset int, sortBy, sortOrder string) ([]*Company, error) {
|
func (r *companyRepository) Search(ctx context.Context, form *SearchForm, pagination *response.Pagination) ([]Company, int64, error) {
|
||||||
// Build filter
|
// Build filter
|
||||||
filter := bson.M{}
|
filter := bson.M{}
|
||||||
|
|
||||||
if search != "" {
|
if form.Search != nil {
|
||||||
filter["$text"] = bson.M{"$search": search}
|
filter["$text"] = bson.M{"$search": *form.Search}
|
||||||
}
|
}
|
||||||
|
|
||||||
if companyType != nil {
|
if form.Type != nil {
|
||||||
filter["type"] = *companyType
|
filter["type"] = *form.Type
|
||||||
}
|
}
|
||||||
|
|
||||||
if status != nil {
|
if form.Status != nil {
|
||||||
filter["status"] = *status
|
filter["status"] = *form.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
if industry != nil {
|
if form.Industry != nil {
|
||||||
filter["industry"] = *industry
|
filter["industry"] = *form.Industry
|
||||||
}
|
}
|
||||||
|
|
||||||
if isVerified != nil {
|
if form.IsVerified != nil {
|
||||||
filter["is_verified"] = *isVerified
|
filter["is_verified"] = *form.IsVerified
|
||||||
}
|
}
|
||||||
|
|
||||||
if isCompliant != nil {
|
if form.IsCompliant != nil {
|
||||||
filter["is_compliant"] = *isCompliant
|
filter["is_compliant"] = *form.IsCompliant
|
||||||
}
|
}
|
||||||
|
|
||||||
if language != nil {
|
if form.Language != nil {
|
||||||
filter["language"] = *language
|
filter["language"] = *form.Language
|
||||||
}
|
}
|
||||||
|
|
||||||
if currency != nil {
|
if form.Currency != nil {
|
||||||
filter["currency"] = *currency
|
filter["currency"] = *form.Currency
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tag filters
|
// Tag filters
|
||||||
if len(cpvCodes) > 0 {
|
if len(form.CPVCodes) > 0 {
|
||||||
filter["tags.cpv_codes"] = bson.M{"$in": cpvCodes}
|
filter["tags.cpv_codes"] = bson.M{"$in": form.CPVCodes}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(categories) > 0 {
|
if len(form.Categories) > 0 {
|
||||||
filter["tags.categories"] = bson.M{"$in": categories}
|
filter["tags.categories"] = bson.M{"$in": form.Categories}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(keywords) > 0 {
|
if len(form.Keywords) > 0 {
|
||||||
filter["tags.keywords"] = bson.M{"$in": keywords}
|
filter["tags.keywords"] = bson.M{"$in": form.Keywords}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(specializations) > 0 {
|
if len(form.Specializations) > 0 {
|
||||||
filter["tags.specializations"] = bson.M{"$in": specializations}
|
filter["tags.specializations"] = bson.M{"$in": form.Specializations}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Range filters
|
// Range filters
|
||||||
if employeeCountMin != nil || employeeCountMax != nil {
|
if form.EmployeeCountMin != nil || form.EmployeeCountMax != nil {
|
||||||
rangeFilter := bson.M{}
|
rangeFilter := bson.M{}
|
||||||
if employeeCountMin != nil {
|
if form.EmployeeCountMin != nil {
|
||||||
rangeFilter["$gte"] = *employeeCountMin
|
rangeFilter["$gte"] = *form.EmployeeCountMin
|
||||||
}
|
}
|
||||||
if employeeCountMax != nil {
|
if form.EmployeeCountMax != nil {
|
||||||
rangeFilter["$lte"] = *employeeCountMax
|
rangeFilter["$lte"] = *form.EmployeeCountMax
|
||||||
}
|
}
|
||||||
filter["employee_count"] = rangeFilter
|
filter["employee_count"] = rangeFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
if annualRevenueMin != nil || annualRevenueMax != nil {
|
if form.AnnualRevenueMin != nil || form.AnnualRevenueMax != nil {
|
||||||
rangeFilter := bson.M{}
|
rangeFilter := bson.M{}
|
||||||
if annualRevenueMin != nil {
|
if form.AnnualRevenueMin != nil {
|
||||||
rangeFilter["$gte"] = *annualRevenueMin
|
rangeFilter["$gte"] = *form.AnnualRevenueMin
|
||||||
}
|
}
|
||||||
if annualRevenueMax != nil {
|
if form.AnnualRevenueMax != nil {
|
||||||
rangeFilter["$lte"] = *annualRevenueMax
|
rangeFilter["$lte"] = *form.AnnualRevenueMax
|
||||||
}
|
}
|
||||||
filter["annual_revenue"] = rangeFilter
|
filter["annual_revenue"] = rangeFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
if foundedYearMin != nil || foundedYearMax != nil {
|
if form.FoundedYearMin != nil || form.FoundedYearMax != nil {
|
||||||
rangeFilter := bson.M{}
|
rangeFilter := bson.M{}
|
||||||
if foundedYearMin != nil {
|
if form.FoundedYearMin != nil {
|
||||||
rangeFilter["$gte"] = *foundedYearMin
|
rangeFilter["$gte"] = *form.FoundedYearMin
|
||||||
}
|
}
|
||||||
if foundedYearMax != nil {
|
if form.FoundedYearMax != nil {
|
||||||
rangeFilter["$lte"] = *foundedYearMax
|
rangeFilter["$lte"] = *form.FoundedYearMax
|
||||||
}
|
}
|
||||||
filter["founded_year"] = rangeFilter
|
filter["founded_year"] = rangeFilter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Build sort
|
||||||
|
sort := "created_at"
|
||||||
|
if pagination.SortBy != "" {
|
||||||
|
sort = pagination.SortBy
|
||||||
|
}
|
||||||
|
|
||||||
|
sortOrder := "desc"
|
||||||
|
if pagination.SortOrder != "" {
|
||||||
|
sortOrder = pagination.SortOrder
|
||||||
|
}
|
||||||
|
|
||||||
// Build pagination
|
// Build pagination
|
||||||
pagination := mongo.NewPaginationBuilder().
|
paginationBuilder := orm.NewPaginationBuilder().
|
||||||
Limit(limit).
|
Limit(pagination.Limit).
|
||||||
Skip(offset)
|
Skip(pagination.Offset).
|
||||||
|
SortBy(sort, sortOrder).
|
||||||
|
Build()
|
||||||
|
|
||||||
// Set sort
|
// Use ORM to find users
|
||||||
if sortBy != "" {
|
result, err := r.ormRepo.FindAll(ctx, filter, paginationBuilder)
|
||||||
if sortOrder == "desc" {
|
|
||||||
pagination.SortDesc(sortBy)
|
|
||||||
} else {
|
|
||||||
pagination.SortAsc(sortBy)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pagination.SortDesc("created_at") // Default sort
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use ORM to find companies
|
|
||||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination.Build())
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Error("Failed to search companies", map[string]interface{}{
|
r.logger.Error("Failed to list users", map[string]interface{}{
|
||||||
"error": err.Error(),
|
|
||||||
"search": search,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []Company to []*Company
|
|
||||||
companies := make([]*Company, len(result.Items))
|
|
||||||
for i := range result.Items {
|
|
||||||
companies[i] = &result.Items[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return companies, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count counts all companies
|
|
||||||
func (r *companyRepository) Count(ctx context.Context) (int64, error) {
|
|
||||||
filter := bson.M{"status": bson.M{"$ne": CompanyStatusInactive}}
|
|
||||||
|
|
||||||
count, err := r.ormRepo.Count(ctx, filter)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to count companies", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
})
|
})
|
||||||
return 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return count, nil
|
return result.Items, result.TotalCount, nil
|
||||||
}
|
|
||||||
|
|
||||||
// CountByType counts companies by type
|
|
||||||
func (r *companyRepository) CountByType(ctx context.Context, companyType CompanyType) (int64, error) {
|
|
||||||
filter := bson.M{
|
|
||||||
"type": companyType,
|
|
||||||
"status": bson.M{"$ne": CompanyStatusInactive},
|
|
||||||
}
|
|
||||||
|
|
||||||
count, err := r.ormRepo.Count(ctx, filter)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to count companies by type", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_type": companyType,
|
|
||||||
})
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return count, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountByStatus counts companies by status
|
|
||||||
func (r *companyRepository) CountByStatus(ctx context.Context, status CompanyStatus) (int64, error) {
|
|
||||||
filter := bson.M{"status": status}
|
|
||||||
|
|
||||||
count, err := r.ormRepo.Count(ctx, filter)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to count companies by status", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"status": status,
|
|
||||||
})
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return count, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountByIndustry counts companies by industry
|
|
||||||
func (r *companyRepository) CountByIndustry(ctx context.Context, industry string) (int64, error) {
|
|
||||||
filter := bson.M{
|
|
||||||
"industry": industry,
|
|
||||||
"status": bson.M{"$ne": CompanyStatusInactive},
|
|
||||||
}
|
|
||||||
|
|
||||||
count, err := r.ormRepo.Count(ctx, filter)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to count companies by industry", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"industry": industry,
|
|
||||||
})
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return count, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CountVerified counts verified companies
|
|
||||||
func (r *companyRepository) CountVerified(ctx context.Context) (int64, error) {
|
|
||||||
filter := bson.M{
|
|
||||||
"is_verified": true,
|
|
||||||
"status": bson.M{"$ne": CompanyStatusInactive},
|
|
||||||
}
|
|
||||||
|
|
||||||
count, err := r.ormRepo.Count(ctx, filter)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to count verified companies", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return count, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateStatus updates company status
|
// UpdateStatus updates company status
|
||||||
@@ -591,226 +365,3 @@ func (r *companyRepository) UpdateVerification(ctx context.Context, id string, i
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateTags updates company tags
|
|
||||||
func (r *companyRepository) UpdateTags(ctx context.Context, id string, tags *CompanyTags) error {
|
|
||||||
// Get company first
|
|
||||||
company, err := r.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update tags
|
|
||||||
company.Tags = tags
|
|
||||||
company.SetUpdatedAt(time.Now().Unix())
|
|
||||||
|
|
||||||
// Update in database
|
|
||||||
err = r.ormRepo.Update(ctx, company)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to update company tags", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.Info("Company tags updated successfully", map[string]interface{}{
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTags adds tags to a company
|
|
||||||
func (r *companyRepository) AddTags(ctx context.Context, id string, cpvCodes []string, categories []string, keywords []string, specializations []string, certifications []string) error {
|
|
||||||
// Get company first
|
|
||||||
company, err := r.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize tags if nil
|
|
||||||
if company.Tags == nil {
|
|
||||||
company.Tags = &CompanyTags{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add tags (avoiding duplicates)
|
|
||||||
company.Tags.CPVCodes = r.addUniqueStrings(company.Tags.CPVCodes, cpvCodes)
|
|
||||||
company.Tags.Categories = r.addUniqueStrings(company.Tags.Categories, categories)
|
|
||||||
company.Tags.Keywords = r.addUniqueStrings(company.Tags.Keywords, keywords)
|
|
||||||
company.Tags.Specializations = r.addUniqueStrings(company.Tags.Specializations, specializations)
|
|
||||||
company.Tags.Certifications = r.addUniqueStrings(company.Tags.Certifications, certifications)
|
|
||||||
|
|
||||||
company.SetUpdatedAt(time.Now().Unix())
|
|
||||||
|
|
||||||
// Update in database
|
|
||||||
err = r.ormRepo.Update(ctx, company)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to add tags to company", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.Info("Tags added to company successfully", map[string]interface{}{
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveTags removes tags from a company
|
|
||||||
func (r *companyRepository) RemoveTags(ctx context.Context, id string, cpvCodes []string, categories []string, keywords []string, specializations []string, certifications []string) error {
|
|
||||||
// Get company first
|
|
||||||
company, err := r.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize tags if nil
|
|
||||||
if company.Tags == nil {
|
|
||||||
company.Tags = &CompanyTags{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove tags
|
|
||||||
company.Tags.CPVCodes = r.removeStrings(company.Tags.CPVCodes, cpvCodes)
|
|
||||||
company.Tags.Categories = r.removeStrings(company.Tags.Categories, categories)
|
|
||||||
company.Tags.Keywords = r.removeStrings(company.Tags.Keywords, keywords)
|
|
||||||
company.Tags.Specializations = r.removeStrings(company.Tags.Specializations, specializations)
|
|
||||||
company.Tags.Certifications = r.removeStrings(company.Tags.Certifications, certifications)
|
|
||||||
|
|
||||||
company.SetUpdatedAt(time.Now().Unix())
|
|
||||||
|
|
||||||
// Update in database
|
|
||||||
err = r.ormRepo.Update(ctx, company)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to remove tags from company", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
r.logger.Info("Tags removed from company successfully", map[string]interface{}{
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompanyStats returns company statistics
|
|
||||||
func (r *companyRepository) GetCompanyStats(ctx context.Context) (*CompanyStatsResponse, error) {
|
|
||||||
// This would typically use aggregation pipelines for better performance
|
|
||||||
// For now, we'll use basic counts
|
|
||||||
|
|
||||||
totalCompanies, _ := r.Count(ctx)
|
|
||||||
activeCompanies, _ := r.CountByStatus(ctx, CompanyStatusActive)
|
|
||||||
verifiedCompanies, _ := r.CountVerified(ctx)
|
|
||||||
|
|
||||||
// For this implementation, we'll return basic stats
|
|
||||||
// In a real implementation, you'd use MongoDB aggregation pipelines
|
|
||||||
stats := &CompanyStatsResponse{
|
|
||||||
TotalCompanies: totalCompanies,
|
|
||||||
ActiveCompanies: activeCompanies,
|
|
||||||
VerifiedCompanies: verifiedCompanies,
|
|
||||||
CompaniesByType: make(map[string]int64),
|
|
||||||
CompaniesByIndustry: make(map[string]int64),
|
|
||||||
CompaniesByStatus: make(map[string]int64),
|
|
||||||
TopCategories: []CategoryCount{},
|
|
||||||
TopCPVCodes: []CPVCodeCount{},
|
|
||||||
}
|
|
||||||
|
|
||||||
return stats, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchByTags searches companies by tags
|
|
||||||
func (r *companyRepository) SearchByTags(ctx context.Context, cpvCodes []string, categories []string, keywords []string, specializations []string, limit, offset int) ([]*Company, error) {
|
|
||||||
filter := bson.M{}
|
|
||||||
|
|
||||||
// Build tag filters
|
|
||||||
var tagFilters []bson.M
|
|
||||||
|
|
||||||
if len(cpvCodes) > 0 {
|
|
||||||
tagFilters = append(tagFilters, bson.M{"tags.cpv_codes": bson.M{"$in": cpvCodes}})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(categories) > 0 {
|
|
||||||
tagFilters = append(tagFilters, bson.M{"tags.categories": bson.M{"$in": categories}})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(keywords) > 0 {
|
|
||||||
tagFilters = append(tagFilters, bson.M{"tags.keywords": bson.M{"$in": keywords}})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(specializations) > 0 {
|
|
||||||
tagFilters = append(tagFilters, bson.M{"tags.specializations": bson.M{"$in": specializations}})
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(tagFilters) > 0 {
|
|
||||||
filter["$or"] = tagFilters
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only active companies
|
|
||||||
filter["status"] = bson.M{"$ne": CompanyStatusInactive}
|
|
||||||
|
|
||||||
// Build pagination
|
|
||||||
pagination := mongo.NewPaginationBuilder().
|
|
||||||
Limit(limit).
|
|
||||||
Skip(offset).
|
|
||||||
SortDesc("created_at").
|
|
||||||
Build()
|
|
||||||
|
|
||||||
// Use ORM to find companies
|
|
||||||
result, err := r.ormRepo.FindAll(ctx, filter, pagination)
|
|
||||||
if err != nil {
|
|
||||||
r.logger.Error("Failed to search companies by tags", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert []Company to []*Company
|
|
||||||
companies := make([]*Company, len(result.Items))
|
|
||||||
for i := range result.Items {
|
|
||||||
companies[i] = &result.Items[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return companies, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper functions for tag management
|
|
||||||
func (r *companyRepository) addUniqueStrings(existing []string, new []string) []string {
|
|
||||||
existingMap := make(map[string]bool)
|
|
||||||
for _, s := range existing {
|
|
||||||
existingMap[s] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
result := make([]string, len(existing))
|
|
||||||
copy(result, existing)
|
|
||||||
|
|
||||||
for _, s := range new {
|
|
||||||
if !existingMap[s] {
|
|
||||||
result = append(result, s)
|
|
||||||
existingMap[s] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *companyRepository) removeStrings(existing []string, toRemove []string) []string {
|
|
||||||
removeMap := make(map[string]bool)
|
|
||||||
for _, s := range toRemove {
|
|
||||||
removeMap[s] = true
|
|
||||||
}
|
|
||||||
|
|
||||||
var result []string
|
|
||||||
for _, s := range existing {
|
|
||||||
if !removeMap[s] {
|
|
||||||
result = append(result, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|||||||
+56
-453
@@ -3,46 +3,29 @@ package company
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"time"
|
|
||||||
|
|
||||||
"tm/pkg/logger"
|
"tm/pkg/logger"
|
||||||
|
"tm/pkg/response"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service defines business logic for company operations
|
// Service defines business logic for company operations
|
||||||
type Service interface {
|
type Service interface {
|
||||||
// Core company operations
|
// Core company operations
|
||||||
CreateCompany(ctx context.Context, form *CreateCompanyForm, createdBy *string) (*Company, error)
|
Create(ctx context.Context, form *CompanyForm) (*CompanyResponse, error)
|
||||||
GetCompanyByID(ctx context.Context, id string) (*Company, error)
|
Update(ctx context.Context, id string, form *CompanyForm) (*CompanyResponse, error)
|
||||||
GetCompaniesByIDs(ctx context.Context, ids []string) ([]*Company, error)
|
GetByID(ctx context.Context, id string) (*CompanyResponse, error)
|
||||||
UpdateCompany(ctx context.Context, id string, form *UpdateCompanyForm, updatedBy *string) (*Company, error)
|
GetByIDs(ctx context.Context, ids []string) ([]*CompanyResponse, error)
|
||||||
DeleteCompany(ctx context.Context, id string, deletedBy *string) error
|
Delete(ctx context.Context, id string) error
|
||||||
|
|
||||||
// Company listing and search
|
// Company listing and search
|
||||||
ListCompanies(ctx context.Context, form *ListCompaniesForm) (*CompanyListResponse, error)
|
SearchCompanies(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*CompanyListResponse, error)
|
||||||
SearchCompanies(ctx context.Context, form *CompanySearchForm) (*CompanyListResponse, error)
|
|
||||||
SearchCompaniesByTags(ctx context.Context, cpvCodes []string, categories []string, keywords []string, specializations []string, limit, offset int) ([]*Company, error)
|
|
||||||
|
|
||||||
// Company management
|
// Company management
|
||||||
UpdateCompanyStatus(ctx context.Context, id string, form *UpdateCompanyStatusForm, updatedBy *string) error
|
UpdateStatus(ctx context.Context, id string, form *StatusForm) error
|
||||||
UpdateCompanyVerification(ctx context.Context, id string, form *UpdateCompanyVerificationForm, updatedBy *string) error
|
UpdateVerification(ctx context.Context, id string, form *VerificationForm) error
|
||||||
|
|
||||||
// Tag management
|
// Get my company
|
||||||
UpdateCompanyTags(ctx context.Context, id string, form *UpdateCompanyTagsForm, updatedBy *string) error
|
GetMyCompany(ctx context.Context, id string) (*CompanyProfileResponse, error)
|
||||||
AddTags(ctx context.Context, id string, form *AddTagsForm, updatedBy *string) error
|
|
||||||
RemoveTags(ctx context.Context, id string, form *RemoveTagsForm, updatedBy *string) error
|
|
||||||
|
|
||||||
// Business operations
|
|
||||||
VerifyCompany(ctx context.Context, id string, verifiedBy *string) error
|
|
||||||
SuspendCompany(ctx context.Context, id string, reason string, suspendedBy *string) error
|
|
||||||
ActivateCompany(ctx context.Context, id string, activatedBy *string) error
|
|
||||||
|
|
||||||
// Statistics and analytics
|
|
||||||
GetCompanyStats(ctx context.Context) (*CompanyStatsResponse, error)
|
|
||||||
|
|
||||||
// Search and filtering helpers
|
|
||||||
GetCompaniesByType(ctx context.Context, companyType CompanyType, limit, offset int) ([]*Company, int64, error)
|
|
||||||
GetCompaniesByStatus(ctx context.Context, status CompanyStatus, limit, offset int) ([]*Company, int64, error)
|
|
||||||
GetCompaniesByIndustry(ctx context.Context, industry string, limit, offset int) ([]*Company, int64, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// companyService implements the Service interface
|
// companyService implements the Service interface
|
||||||
@@ -59,8 +42,8 @@ func NewCompanyService(repository Repository, logger logger.Logger) Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateCompany creates a new company
|
// Create creates a new company
|
||||||
func (s *companyService) CreateCompany(ctx context.Context, form *CreateCompanyForm, createdBy *string) (*Company, error) {
|
func (s *companyService) Create(ctx context.Context, form *CompanyForm) (*CompanyResponse, error) {
|
||||||
// Check if company name already exists
|
// Check if company name already exists
|
||||||
existingCompany, _ := s.repository.GetByName(ctx, form.Name)
|
existingCompany, _ := s.repository.GetByName(ctx, form.Name)
|
||||||
if existingCompany != nil {
|
if existingCompany != nil {
|
||||||
@@ -83,7 +66,7 @@ func (s *companyService) CreateCompany(ctx context.Context, form *CreateCompanyF
|
|||||||
company := &Company{
|
company := &Company{
|
||||||
Name: form.Name,
|
Name: form.Name,
|
||||||
Type: CompanyType(form.Type),
|
Type: CompanyType(form.Type),
|
||||||
Status: CompanyStatusPending,
|
Status: CompanyStatusActive,
|
||||||
RegistrationNumber: form.RegistrationNumber,
|
RegistrationNumber: form.RegistrationNumber,
|
||||||
TaxID: form.TaxID,
|
TaxID: form.TaxID,
|
||||||
Industry: form.Industry,
|
Industry: form.Industry,
|
||||||
@@ -93,7 +76,6 @@ func (s *companyService) CreateCompany(ctx context.Context, form *CreateCompanyF
|
|||||||
AnnualRevenue: form.AnnualRevenue,
|
AnnualRevenue: form.AnnualRevenue,
|
||||||
FoundedYear: form.FoundedYear,
|
FoundedYear: form.FoundedYear,
|
||||||
Address: s.convertAddressForm(form.Address),
|
Address: s.convertAddressForm(form.Address),
|
||||||
ContactPerson: s.convertContactPersonForm(form.ContactPerson),
|
|
||||||
Phone: form.Phone,
|
Phone: form.Phone,
|
||||||
Email: form.Email,
|
Email: form.Email,
|
||||||
Tags: s.convertCompanyTagsForm(form.Tags),
|
Tags: s.convertCompanyTagsForm(form.Tags),
|
||||||
@@ -102,7 +84,6 @@ func (s *companyService) CreateCompany(ctx context.Context, form *CreateCompanyF
|
|||||||
Language: s.getDefaultLanguage(form.Language),
|
Language: s.getDefaultLanguage(form.Language),
|
||||||
Currency: s.getDefaultCurrency(form.Currency),
|
Currency: s.getDefaultCurrency(form.Currency),
|
||||||
Timezone: s.getDefaultTimezone(form.Timezone),
|
Timezone: s.getDefaultTimezone(form.Timezone),
|
||||||
CreatedBy: createdBy,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save to database
|
// Save to database
|
||||||
@@ -120,14 +101,13 @@ func (s *companyService) CreateCompany(ctx context.Context, form *CreateCompanyF
|
|||||||
"company_id": company.ID,
|
"company_id": company.ID,
|
||||||
"name": company.Name,
|
"name": company.Name,
|
||||||
"type": company.Type,
|
"type": company.Type,
|
||||||
"created_by": createdBy,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return company, nil
|
return company.ToResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetCompanyByID retrieves a company by ID
|
// GetCompanyByID retrieves a company by ID
|
||||||
func (s *companyService) GetCompanyByID(ctx context.Context, id string) (*Company, error) {
|
func (s *companyService) GetByID(ctx context.Context, id string) (*CompanyResponse, error) {
|
||||||
company, err := s.repository.GetByID(ctx, id)
|
company, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get company by ID", map[string]interface{}{
|
s.logger.Error("Failed to get company by ID", map[string]interface{}{
|
||||||
@@ -142,34 +122,11 @@ func (s *companyService) GetCompanyByID(ctx context.Context, id string) (*Compan
|
|||||||
"name": company.Name,
|
"name": company.Name,
|
||||||
})
|
})
|
||||||
|
|
||||||
return company, nil
|
return company.ToResponse(), nil
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompaniesByIDs retrieves multiple companies by their IDs
|
|
||||||
func (s *companyService) GetCompaniesByIDs(ctx context.Context, ids []string) ([]*Company, error) {
|
|
||||||
if len(ids) == 0 {
|
|
||||||
return []*Company{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
companies, err := s.repository.GetByIDs(ctx, ids)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get companies by IDs", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"ids": ids,
|
|
||||||
})
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("Companies retrieved successfully", map[string]interface{}{
|
|
||||||
"requested_count": len(ids),
|
|
||||||
"found_count": len(companies),
|
|
||||||
})
|
|
||||||
|
|
||||||
return companies, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompany updates a company
|
// UpdateCompany updates a company
|
||||||
func (s *companyService) UpdateCompany(ctx context.Context, id string, form *UpdateCompanyForm, updatedBy *string) (*Company, error) {
|
func (s *companyService) Update(ctx context.Context, id string, form *CompanyForm) (*CompanyResponse, error) {
|
||||||
// Get existing company
|
// Get existing company
|
||||||
company, err := s.repository.GetByID(ctx, id)
|
company, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -181,39 +138,39 @@ func (s *companyService) UpdateCompany(ctx context.Context, id string, form *Upd
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if name already exists (if changing name)
|
// Check if name already exists (if changing name)
|
||||||
if form.Name != nil && *form.Name != company.Name {
|
if form.Name != "" && form.Name != company.Name {
|
||||||
existingCompany, _ := s.repository.GetByName(ctx, *form.Name)
|
existingCompany, _ := s.repository.GetByName(ctx, form.Name)
|
||||||
if existingCompany != nil {
|
if existingCompany != nil {
|
||||||
return nil, errors.New("company with this name already exists")
|
return nil, errors.New("company with this name already exists")
|
||||||
}
|
}
|
||||||
company.Name = *form.Name
|
company.Name = form.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if registration number already exists (if changing)
|
// Check if registration number already exists (if changing)
|
||||||
if form.RegistrationNumber != nil && *form.RegistrationNumber != company.RegistrationNumber {
|
if form.RegistrationNumber != "" && form.RegistrationNumber != company.RegistrationNumber {
|
||||||
existingCompany, _ := s.repository.GetByRegistrationNumber(ctx, *form.RegistrationNumber)
|
existingCompany, _ := s.repository.GetByRegistrationNumber(ctx, form.RegistrationNumber)
|
||||||
if existingCompany != nil {
|
if existingCompany != nil {
|
||||||
return nil, errors.New("company with this registration number already exists")
|
return nil, errors.New("company with this registration number already exists")
|
||||||
}
|
}
|
||||||
company.RegistrationNumber = *form.RegistrationNumber
|
company.RegistrationNumber = form.RegistrationNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if tax ID already exists (if changing)
|
// Check if tax ID already exists (if changing)
|
||||||
if form.TaxID != nil && *form.TaxID != company.TaxID {
|
if form.TaxID != "" && form.TaxID != company.TaxID {
|
||||||
existingCompany, _ := s.repository.GetByTaxID(ctx, *form.TaxID)
|
existingCompany, _ := s.repository.GetByTaxID(ctx, form.TaxID)
|
||||||
if existingCompany != nil {
|
if existingCompany != nil {
|
||||||
return nil, errors.New("company with this tax ID already exists")
|
return nil, errors.New("company with this tax ID already exists")
|
||||||
}
|
}
|
||||||
company.TaxID = *form.TaxID
|
company.TaxID = form.TaxID
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update fields if provided
|
// Update fields if provided
|
||||||
if form.Type != nil {
|
if form.Type != "" {
|
||||||
company.Type = CompanyType(*form.Type)
|
company.Type = CompanyType(form.Type)
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Industry != nil {
|
if form.Industry != "" {
|
||||||
company.Industry = *form.Industry
|
company.Industry = form.Industry
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Description != nil {
|
if form.Description != nil {
|
||||||
@@ -240,10 +197,6 @@ func (s *companyService) UpdateCompany(ctx context.Context, id string, form *Upd
|
|||||||
company.Address = s.convertAddressForm(form.Address)
|
company.Address = s.convertAddressForm(form.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.ContactPerson != nil {
|
|
||||||
company.ContactPerson = s.convertContactPersonForm(form.ContactPerson)
|
|
||||||
}
|
|
||||||
|
|
||||||
if form.Phone != nil {
|
if form.Phone != nil {
|
||||||
company.Phone = form.Phone
|
company.Phone = form.Phone
|
||||||
}
|
}
|
||||||
@@ -268,10 +221,6 @@ func (s *companyService) UpdateCompany(ctx context.Context, id string, form *Upd
|
|||||||
company.Timezone = *form.Timezone
|
company.Timezone = *form.Timezone
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set updated by
|
|
||||||
company.UpdatedBy = updatedBy
|
|
||||||
company.UpdatedAt = time.Now().Unix()
|
|
||||||
|
|
||||||
// Update in database
|
// Update in database
|
||||||
err = s.repository.Update(ctx, company)
|
err = s.repository.Update(ctx, company)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -285,25 +234,15 @@ func (s *companyService) UpdateCompany(ctx context.Context, id string, form *Upd
|
|||||||
s.logger.Info("Company updated successfully", map[string]interface{}{
|
s.logger.Info("Company updated successfully", map[string]interface{}{
|
||||||
"company_id": company.ID,
|
"company_id": company.ID,
|
||||||
"name": company.Name,
|
"name": company.Name,
|
||||||
"updated_by": updatedBy,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return company, nil
|
return company.ToResponse(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// DeleteCompany deletes a company (soft delete)
|
// DeleteCompany deletes a company (soft delete)
|
||||||
func (s *companyService) DeleteCompany(ctx context.Context, id string, deletedBy *string) error {
|
func (s *companyService) Delete(ctx context.Context, id string) error {
|
||||||
// Get company to check if exists
|
|
||||||
company, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("company not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set updated by before deletion
|
|
||||||
company.UpdatedBy = deletedBy
|
|
||||||
|
|
||||||
// Delete company (soft delete)
|
// Delete company (soft delete)
|
||||||
err = s.repository.Delete(ctx, id)
|
err := s.repository.Delete(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to delete company", map[string]interface{}{
|
s.logger.Error("Failed to delete company", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
@@ -314,92 +253,19 @@ func (s *companyService) DeleteCompany(ctx context.Context, id string, deletedBy
|
|||||||
|
|
||||||
s.logger.Info("Company deleted successfully", map[string]interface{}{
|
s.logger.Info("Company deleted successfully", map[string]interface{}{
|
||||||
"company_id": id,
|
"company_id": id,
|
||||||
"deleted_by": deletedBy,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListCompanies lists companies with search and filters
|
|
||||||
func (s *companyService) ListCompanies(ctx context.Context, form *ListCompaniesForm) (*CompanyListResponse, error) {
|
|
||||||
// Set defaults
|
|
||||||
limit := 20
|
|
||||||
if form.Limit != nil {
|
|
||||||
limit = *form.Limit
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := 0
|
|
||||||
if form.Offset != nil {
|
|
||||||
offset = *form.Offset
|
|
||||||
}
|
|
||||||
|
|
||||||
search := ""
|
|
||||||
if form.Search != nil {
|
|
||||||
search = *form.Search
|
|
||||||
}
|
|
||||||
|
|
||||||
sortBy := "created_at"
|
|
||||||
if form.SortBy != nil {
|
|
||||||
sortBy = *form.SortBy
|
|
||||||
}
|
|
||||||
|
|
||||||
sortOrder := "desc"
|
|
||||||
if form.SortOrder != nil {
|
|
||||||
sortOrder = *form.SortOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get companies
|
|
||||||
companies, err := s.repository.Search(ctx, search, form.Type, form.Status, form.Industry, form.IsVerified, form.IsCompliant, form.Language, form.Currency, form.CPVCodes, form.Categories, form.Keywords, form.Specializations, form.EmployeeCountMin, form.EmployeeCountMax, form.AnnualRevenueMin, form.AnnualRevenueMax, form.FoundedYearMin, form.FoundedYearMax, limit, offset, sortBy, sortOrder)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to list companies", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return nil, errors.New("failed to list companies")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert to responses
|
|
||||||
var companyResponses []*CompanyResponse
|
|
||||||
for _, company := range companies {
|
|
||||||
companyResponses = append(companyResponses, company.ToResponse())
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement proper count for pagination metadata
|
|
||||||
total := int64(len(companies)) // This should be a separate count query
|
|
||||||
totalPages := (total + int64(limit) - 1) / int64(limit)
|
|
||||||
|
|
||||||
return &CompanyListResponse{
|
|
||||||
Companies: companyResponses,
|
|
||||||
Total: total,
|
|
||||||
Limit: limit,
|
|
||||||
Offset: offset,
|
|
||||||
TotalPages: int(totalPages),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SearchCompanies searches companies with advanced filters
|
// SearchCompanies searches companies with advanced filters
|
||||||
func (s *companyService) SearchCompanies(ctx context.Context, form *CompanySearchForm) (*CompanyListResponse, error) {
|
func (s *companyService) SearchCompanies(ctx context.Context, form *SearchForm, pagination *response.Pagination) (*CompanyListResponse, error) {
|
||||||
// Set defaults
|
|
||||||
limit := 20
|
|
||||||
if form.Limit != nil {
|
|
||||||
limit = *form.Limit
|
|
||||||
}
|
|
||||||
|
|
||||||
offset := 0
|
|
||||||
if form.Offset != nil {
|
|
||||||
offset = *form.Offset
|
|
||||||
}
|
|
||||||
|
|
||||||
query := ""
|
|
||||||
if form.Query != nil {
|
|
||||||
query = *form.Query
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get companies using search
|
// Get companies using search
|
||||||
companies, err := s.repository.Search(ctx, query, nil, nil, form.Industry, form.IsVerified, form.IsCompliant, nil, nil, form.CPVCodes, form.Categories, form.Keywords, form.Specializations, form.MinEmployees, form.MaxEmployees, form.MinRevenue, form.MaxRevenue, nil, nil, limit, offset, "created_at", "desc")
|
companies, total, err := s.repository.Search(ctx, form, pagination)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to search companies", map[string]interface{}{
|
s.logger.Error("Failed to search companies", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"query": query,
|
|
||||||
})
|
})
|
||||||
return nil, errors.New("failed to search companies")
|
return nil, errors.New("failed to search companies")
|
||||||
}
|
}
|
||||||
@@ -410,34 +276,29 @@ func (s *companyService) SearchCompanies(ctx context.Context, form *CompanySearc
|
|||||||
companyResponses = append(companyResponses, company.ToResponse())
|
companyResponses = append(companyResponses, company.ToResponse())
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implement proper count for pagination metadata
|
|
||||||
total := int64(len(companies)) // This should be a separate count query
|
|
||||||
totalPages := (total + int64(limit) - 1) / int64(limit)
|
|
||||||
|
|
||||||
return &CompanyListResponse{
|
return &CompanyListResponse{
|
||||||
Companies: companyResponses,
|
Companies: companyResponses,
|
||||||
Total: total,
|
Meta: pagination.Response(total),
|
||||||
Limit: limit,
|
|
||||||
Offset: offset,
|
|
||||||
TotalPages: int(totalPages),
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SearchCompaniesByTags searches companies by tags
|
// GetByIDs retrieves companies by IDs
|
||||||
func (s *companyService) SearchCompaniesByTags(ctx context.Context, cpvCodes []string, categories []string, keywords []string, specializations []string, limit, offset int) ([]*Company, error) {
|
func (s *companyService) GetByIDs(ctx context.Context, ids []string) ([]*CompanyResponse, error) {
|
||||||
companies, err := s.repository.SearchByTags(ctx, cpvCodes, categories, keywords, specializations, limit, offset)
|
companies, err := s.repository.GetByIDs(ctx, ids)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to search companies by tags", map[string]interface{}{
|
return nil, errors.New("failed to get companies by IDs")
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return nil, errors.New("failed to search companies by tags")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return companies, nil
|
var companyResponses []*CompanyResponse
|
||||||
|
for _, company := range companies {
|
||||||
|
companyResponses = append(companyResponses, company.ToResponse())
|
||||||
|
}
|
||||||
|
|
||||||
|
return companyResponses, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompanyStatus updates company status
|
// UpdateCompanyStatus updates company status
|
||||||
func (s *companyService) UpdateCompanyStatus(ctx context.Context, id string, form *UpdateCompanyStatusForm, updatedBy *string) error {
|
func (s *companyService) UpdateStatus(ctx context.Context, id string, form *StatusForm) error {
|
||||||
// Get company to check if exists
|
// Get company to check if exists
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
_, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -445,13 +306,12 @@ func (s *companyService) UpdateCompanyStatus(ctx context.Context, id string, for
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update status
|
// Update status
|
||||||
status := CompanyStatus(form.Status)
|
err = s.repository.UpdateStatus(ctx, id, CompanyStatus(form.Status))
|
||||||
err = s.repository.UpdateStatus(ctx, id, status)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to update company status", map[string]interface{}{
|
s.logger.Error("Failed to update company status", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
"company_id": id,
|
"company_id": id,
|
||||||
"status": form.Status,
|
"status": CompanyStatus(form.Status),
|
||||||
})
|
})
|
||||||
return errors.New("failed to update company status")
|
return errors.New("failed to update company status")
|
||||||
}
|
}
|
||||||
@@ -459,14 +319,13 @@ func (s *companyService) UpdateCompanyStatus(ctx context.Context, id string, for
|
|||||||
s.logger.Info("Company status updated successfully", map[string]interface{}{
|
s.logger.Info("Company status updated successfully", map[string]interface{}{
|
||||||
"company_id": id,
|
"company_id": id,
|
||||||
"status": form.Status,
|
"status": form.Status,
|
||||||
"updated_by": updatedBy,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompanyVerification updates company verification status
|
// UpdateCompanyVerification updates company verification status
|
||||||
func (s *companyService) UpdateCompanyVerification(ctx context.Context, id string, form *UpdateCompanyVerificationForm, updatedBy *string) error {
|
func (s *companyService) UpdateVerification(ctx context.Context, id string, form *VerificationForm) error {
|
||||||
// Get company to check if exists
|
// Get company to check if exists
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
_, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -487,258 +346,19 @@ func (s *companyService) UpdateCompanyVerification(ctx context.Context, id strin
|
|||||||
"company_id": id,
|
"company_id": id,
|
||||||
"is_verified": form.IsVerified,
|
"is_verified": form.IsVerified,
|
||||||
"is_compliant": form.IsCompliant,
|
"is_compliant": form.IsCompliant,
|
||||||
"updated_by": updatedBy,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCompanyTags updates company tags
|
// GetMyCompany retrieves the company for the authenticated user
|
||||||
func (s *companyService) UpdateCompanyTags(ctx context.Context, id string, form *UpdateCompanyTagsForm, updatedBy *string) error {
|
func (s *companyService) GetMyCompany(ctx context.Context, id string) (*CompanyProfileResponse, error) {
|
||||||
// Get company to check if exists
|
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("company not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update tags
|
|
||||||
tags := s.convertCompanyTagsForm(form.Tags)
|
|
||||||
err = s.repository.UpdateTags(ctx, id, tags)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to update company tags", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
return errors.New("failed to update company tags")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("Company tags updated successfully", map[string]interface{}{
|
|
||||||
"company_id": id,
|
|
||||||
"updated_by": updatedBy,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddTags adds tags to a company
|
|
||||||
func (s *companyService) AddTags(ctx context.Context, id string, form *AddTagsForm, updatedBy *string) error {
|
|
||||||
// Get company to check if exists
|
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("company not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add tags
|
|
||||||
err = s.repository.AddTags(ctx, id, form.CPVCodes, form.Categories, form.Keywords, form.Specializations, form.Certifications)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to add tags to company", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
return errors.New("failed to add tags to company")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("Tags added to company successfully", map[string]interface{}{
|
|
||||||
"company_id": id,
|
|
||||||
"updated_by": updatedBy,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// RemoveTags removes tags from a company
|
|
||||||
func (s *companyService) RemoveTags(ctx context.Context, id string, form *RemoveTagsForm, updatedBy *string) error {
|
|
||||||
// Get company to check if exists
|
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("company not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove tags
|
|
||||||
err = s.repository.RemoveTags(ctx, id, form.CPVCodes, form.Categories, form.Keywords, form.Specializations, form.Certifications)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to remove tags from company", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
return errors.New("failed to remove tags from company")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("Tags removed from company successfully", map[string]interface{}{
|
|
||||||
"company_id": id,
|
|
||||||
"updated_by": updatedBy,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyCompany verifies a company
|
|
||||||
func (s *companyService) VerifyCompany(ctx context.Context, id string, verifiedBy *string) error {
|
|
||||||
// Get company to check if exists
|
|
||||||
company, err := s.repository.GetByID(ctx, id)
|
company, err := s.repository.GetByID(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("company not found")
|
return nil, errors.New("company not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update verification
|
return company.ToProfileResponse(), nil
|
||||||
err = s.repository.UpdateVerification(ctx, id, true, company.IsCompliant, company.ComplianceNotes)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to verify company", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
return errors.New("failed to verify company")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("Company verified successfully", map[string]interface{}{
|
|
||||||
"company_id": id,
|
|
||||||
"verified_by": verifiedBy,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// SuspendCompany suspends a company
|
|
||||||
func (s *companyService) SuspendCompany(ctx context.Context, id string, reason string, suspendedBy *string) error {
|
|
||||||
// Get company to check if exists
|
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("company not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update status to suspended
|
|
||||||
err = s.repository.UpdateStatus(ctx, id, CompanyStatusSuspended)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to suspend company", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
return errors.New("failed to suspend company")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("Company suspended successfully", map[string]interface{}{
|
|
||||||
"company_id": id,
|
|
||||||
"reason": reason,
|
|
||||||
"suspended_by": suspendedBy,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ActivateCompany activates a company
|
|
||||||
func (s *companyService) ActivateCompany(ctx context.Context, id string, activatedBy *string) error {
|
|
||||||
// Get company to check if exists
|
|
||||||
_, err := s.repository.GetByID(ctx, id)
|
|
||||||
if err != nil {
|
|
||||||
return errors.New("company not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update status to active
|
|
||||||
err = s.repository.UpdateStatus(ctx, id, CompanyStatusActive)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to activate company", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_id": id,
|
|
||||||
})
|
|
||||||
return errors.New("failed to activate company")
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Info("Company activated successfully", map[string]interface{}{
|
|
||||||
"company_id": id,
|
|
||||||
"activated_by": activatedBy,
|
|
||||||
})
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompanyStats returns company statistics
|
|
||||||
func (s *companyService) GetCompanyStats(ctx context.Context) (*CompanyStatsResponse, error) {
|
|
||||||
stats, err := s.repository.GetCompanyStats(ctx)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get company stats", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
return nil, errors.New("failed to get company statistics")
|
|
||||||
}
|
|
||||||
|
|
||||||
return stats, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompaniesByType retrieves companies by type with pagination
|
|
||||||
func (s *companyService) GetCompaniesByType(ctx context.Context, companyType CompanyType, limit, offset int) ([]*Company, int64, error) {
|
|
||||||
// Use search method with type filter
|
|
||||||
typeStr := string(companyType)
|
|
||||||
companies, err := s.repository.Search(ctx, "", &typeStr, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, limit, offset, "created_at", "desc")
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get companies by type", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_type": companyType,
|
|
||||||
})
|
|
||||||
return nil, 0, errors.New("failed to get companies by type")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get total count
|
|
||||||
total, err := s.repository.CountByType(ctx, companyType)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to count companies by type", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"company_type": companyType,
|
|
||||||
})
|
|
||||||
return companies, 0, nil // Return companies even if count fails
|
|
||||||
}
|
|
||||||
|
|
||||||
return companies, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompaniesByStatus retrieves companies by status with pagination
|
|
||||||
func (s *companyService) GetCompaniesByStatus(ctx context.Context, status CompanyStatus, limit, offset int) ([]*Company, int64, error) {
|
|
||||||
// Use search method with status filter
|
|
||||||
statusStr := string(status)
|
|
||||||
companies, err := s.repository.Search(ctx, "", nil, &statusStr, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, limit, offset, "created_at", "desc")
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get companies by status", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"status": status,
|
|
||||||
})
|
|
||||||
return nil, 0, errors.New("failed to get companies by status")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get total count
|
|
||||||
total, err := s.repository.CountByStatus(ctx, status)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to count companies by status", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"status": status,
|
|
||||||
})
|
|
||||||
return companies, 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return companies, total, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCompaniesByIndustry retrieves companies by industry with pagination
|
|
||||||
func (s *companyService) GetCompaniesByIndustry(ctx context.Context, industry string, limit, offset int) ([]*Company, int64, error) {
|
|
||||||
// Use search method with industry filter
|
|
||||||
companies, err := s.repository.Search(ctx, "", nil, nil, &industry, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, limit, offset, "created_at", "desc")
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to get companies by industry", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"industry": industry,
|
|
||||||
})
|
|
||||||
return nil, 0, errors.New("failed to get companies by industry")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get total count
|
|
||||||
total, err := s.repository.CountByIndustry(ctx, industry)
|
|
||||||
if err != nil {
|
|
||||||
s.logger.Error("Failed to count companies by industry", map[string]interface{}{
|
|
||||||
"error": err.Error(),
|
|
||||||
"industry": industry,
|
|
||||||
})
|
|
||||||
return companies, 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return companies, total, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper methods for converting forms to domain objects
|
// Helper methods for converting forms to domain objects
|
||||||
@@ -756,23 +376,6 @@ func (s *companyService) convertAddressForm(form *AddressForm) *Address {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *companyService) convertContactPersonForm(form *ContactPersonForm) *ContactPerson {
|
|
||||||
if form == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &ContactPerson{
|
|
||||||
FirstName: form.FirstName,
|
|
||||||
LastName: form.LastName,
|
|
||||||
FullName: form.FullName,
|
|
||||||
Position: form.Position,
|
|
||||||
Email: form.Email,
|
|
||||||
Phone: form.Phone,
|
|
||||||
Mobile: form.Mobile,
|
|
||||||
IsPrimary: form.IsPrimary,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *companyService) convertCompanyTagsForm(form *CompanyTagsForm) *CompanyTags {
|
func (s *companyService) convertCompanyTagsForm(form *CompanyTagsForm) *CompanyTags {
|
||||||
if form == nil {
|
if form == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ type SearchCustomersForm struct {
|
|||||||
// CustomerListResponse represents the response for listing customers
|
// CustomerListResponse represents the response for listing customers
|
||||||
type CustomerListResponse struct {
|
type CustomerListResponse struct {
|
||||||
Customers []*CustomerResponse `json:"customers"`
|
Customers []*CustomerResponse `json:"customers"`
|
||||||
Meta *response.Meta `json:"meta"`
|
Meta *response.Meta `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LoginForm struct {
|
type LoginForm struct {
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ func (h *Handler) Search(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to list customers")
|
return response.InternalServerError(c, "Failed to list customers")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, result, "Customers retrieved successfully")
|
return response.SuccessWithMeta(c, result, result.Meta, "Customers retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateCustomerStatus updates customer status (Web Panel)
|
// UpdateCustomerStatus updates customer status (Web Panel)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func (s *customerService) Register(ctx context.Context, form *CreateCustomerForm
|
|||||||
var companyIDs []string
|
var companyIDs []string
|
||||||
if len(form.CompanyIDs) > 0 {
|
if len(form.CompanyIDs) > 0 {
|
||||||
for _, companyID := range form.CompanyIDs {
|
for _, companyID := range form.CompanyIDs {
|
||||||
_, err := s.companyService.GetCompanyByID(ctx, companyID)
|
_, err := s.companyService.GetByID(ctx, companyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Invalid company ID provided", map[string]interface{}{
|
s.logger.Error("Invalid company ID provided", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
@@ -210,7 +210,7 @@ func (s *customerService) Update(ctx context.Context, id string, form *UpdateCus
|
|||||||
// Verify that all company IDs exist
|
// Verify that all company IDs exist
|
||||||
var validCompanyIDs []string
|
var validCompanyIDs []string
|
||||||
for _, companyID := range form.CompanyIDs {
|
for _, companyID := range form.CompanyIDs {
|
||||||
_, err := s.companyService.GetCompanyByID(ctx, companyID)
|
_, err := s.companyService.GetByID(ctx, companyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Invalid company ID provided in update", map[string]interface{}{
|
s.logger.Error("Invalid company ID provided in update", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
@@ -317,7 +317,7 @@ func (s *customerService) AssignCompanies(ctx context.Context, customerID string
|
|||||||
// Verify companies exist and update customer's company IDs
|
// Verify companies exist and update customer's company IDs
|
||||||
var validCompanyIDs []string
|
var validCompanyIDs []string
|
||||||
for _, companyID := range companyIDs {
|
for _, companyID := range companyIDs {
|
||||||
_, err = s.companyService.GetCompanyByID(ctx, companyID)
|
_, err = s.companyService.GetByID(ctx, companyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Company not found during assignment", map[string]interface{}{
|
s.logger.Error("Company not found during assignment", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
@@ -695,7 +695,7 @@ func (s *customerService) loadCompaniesForCustomer(ctx context.Context, customer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load companies in batch using the new GetCompaniesByIDs method
|
// Load companies in batch using the new GetCompaniesByIDs method
|
||||||
companies, err := s.companyService.GetCompaniesByIDs(ctx, customer.CompanyIDs)
|
companies, err := s.companyService.GetByIDs(ctx, customer.CompanyIDs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to load companies for customer", map[string]interface{}{
|
s.logger.Error("Failed to load companies for customer", map[string]interface{}{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
@@ -709,7 +709,7 @@ func (s *customerService) loadCompaniesForCustomer(ctx context.Context, customer
|
|||||||
var companySummaries []*CompanySummary
|
var companySummaries []*CompanySummary
|
||||||
for _, company := range companies {
|
for _, company := range companies {
|
||||||
companySummaries = append(companySummaries, &CompanySummary{
|
companySummaries = append(companySummaries, &CompanySummary{
|
||||||
ID: company.ID.Hex(),
|
ID: company.ID,
|
||||||
Name: company.Name,
|
Name: company.Name,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ func (s *tenderService) ListTenders(ctx context.Context, req ListTendersRequest)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get company CPV codes for match calculation
|
// Get company CPV codes for match calculation
|
||||||
comp, err := s.companyService.GetCompanyByID(ctx, *req.CompanyID)
|
comp, err := s.companyService.GetByID(ctx, *req.CompanyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get company for match calculation", map[string]interface{}{
|
s.logger.Error("Failed to get company for match calculation", map[string]interface{}{
|
||||||
"company_id": *req.CompanyID,
|
"company_id": *req.CompanyID,
|
||||||
@@ -234,7 +234,7 @@ func (s *tenderService) RecommendTenders(ctx context.Context, companyID *string,
|
|||||||
limit = 100
|
limit = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
company, err := s.companyService.GetCompanyByID(ctx, *companyID)
|
company, err := s.companyService.GetByID(ctx, *companyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("Failed to get company", map[string]interface{}{
|
s.logger.Error("Failed to get company", map[string]interface{}{
|
||||||
"company_id": *companyID,
|
"company_id": *companyID,
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ type AuthResponse struct {
|
|||||||
|
|
||||||
type UserListResponse struct {
|
type UserListResponse struct {
|
||||||
Users []*UserResponse `json:"users"`
|
Users []*UserResponse `json:"users"`
|
||||||
Meta *response.Meta `json:"meta"`
|
Meta *response.Meta `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SearchUsersForm struct {
|
type SearchUsersForm struct {
|
||||||
|
|||||||
@@ -316,7 +316,7 @@ func (h *Handler) ListUsers(c echo.Context) error {
|
|||||||
return response.InternalServerError(c, "Failed to list users")
|
return response.InternalServerError(c, "Failed to list users")
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.Success(c, users, "Users retrieved successfully")
|
return response.SuccessWithMeta(c, users, users.Meta, "Users retrieved successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByID gets a user by ID (admin only)
|
// GetUserByID gets a user by ID (admin only)
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import (
|
|||||||
type APIResponse struct {
|
type APIResponse struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
Message string `json:"message,omitempty"`
|
Message string `json:"message,omitempty"`
|
||||||
Data interface{} `json:",inline"`
|
Data interface{} `json:"data,omitempty"`
|
||||||
Error *APIError `json:"error,omitempty"`
|
Error *APIError `json:"error,omitempty"`
|
||||||
|
Meta *Meta `json:"meta,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// APIError represents error details in API response
|
// APIError represents error details in API response
|
||||||
@@ -54,6 +55,7 @@ func SuccessWithMeta(c echo.Context, data interface{}, meta *Meta, message strin
|
|||||||
Success: true,
|
Success: true,
|
||||||
Message: message,
|
Message: message,
|
||||||
Data: data,
|
Data: data,
|
||||||
|
Meta: meta,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user