Add Tender Approval Management Functionality and API Enhancements

- Introduced a new tender approval management system, including the creation of tender approval entities, services, and repositories.
- Implemented CRUD operations for tender approvals, allowing companies to approve or reject tenders.
- Developed administrative and public API endpoints for listing, retrieving, and updating tender approvals with comprehensive filtering options.
- Enhanced Swagger documentation to accurately reflect the new endpoints, including detailed descriptions, parameters, and response formats.
- Updated existing routes to include tender approval management, ensuring adherence to Clean Architecture principles and maintaining a clear separation of concerns in the handler layer.
This commit is contained in:
n.nakhostin
2025-08-18 19:00:41 +03:30
parent a8ef631e43
commit 94d0fbef38
14 changed files with 4615 additions and 16 deletions
+176
View File
@@ -0,0 +1,176 @@
package tender_approval
import (
"regexp"
"strings"
)
// Validation functions for tender approval domain
// IsValidTenderID validates if the tender ID format is correct
func IsValidTenderID(tenderID string) bool {
if tenderID == "" {
return false
}
// Tender ID should be a non-empty string
// You can add more specific validation rules here based on your tender ID format
return len(tenderID) > 0 && len(tenderID) <= 100
}
// IsValidCompanyID validates if the company ID format is correct
func IsValidCompanyID(companyID string) bool {
if companyID == "" {
return false
}
// Company ID should be a non-empty string
// You can add more specific validation rules here based on your company ID format
return len(companyID) > 0 && len(companyID) <= 100
}
// IsValidSubmissionMode validates if the submission mode is valid
func IsValidSubmissionMode(mode string) bool {
validModes := []string{
string(SubmissionModeSelfApply),
string(SubmissionModePartnership),
}
for _, validMode := range validModes {
if strings.EqualFold(mode, validMode) {
return true
}
}
return false
}
// IsValidStatus validates if the approval status is valid
func IsValidStatus(status string) bool {
validStatuses := []string{
string(ApprovalStatusApproved),
string(ApprovalStatusRejected),
}
for _, validStatus := range validStatuses {
if strings.EqualFold(status, validStatus) {
return true
}
}
return false
}
// IsValidNotes validates if the notes field is valid
func IsValidNotes(notes *string) bool {
if notes == nil {
return true // Notes are optional
}
// Notes should be between 0 and 1000 characters
return len(*notes) <= 1000
}
// IsValidReason validates if the rejection reason is valid
func IsValidReason(reason string) bool {
// Reason should be between 10 and 500 characters
if len(reason) < 10 || len(reason) > 500 {
return false
}
// Reason should not contain only whitespace
if strings.TrimSpace(reason) == "" {
return false
}
return true
}
// IsValidTimestamp validates if the timestamp is within reasonable range
func IsValidTimestamp(timestamp int64) bool {
// Timestamp should be positive and not too far in the future
// You can adjust these limits based on your requirements
if timestamp <= 0 {
return false
}
// Check if timestamp is not too far in the future (e.g., 10 years from now)
maxFutureTimestamp := int64(10 * 365 * 24 * 60 * 60) // 10 years in seconds
return timestamp <= maxFutureTimestamp
}
// IsValidLimit validates if the limit parameter is valid
func IsValidLimit(limit int) bool {
return limit > 0 && limit <= 100
}
// IsValidOffset validates if the offset parameter is valid
func IsValidOffset(offset int) bool {
return offset >= 0
}
// IsValidSortBy validates if the sort by parameter is valid
func IsValidSortBy(sortBy string) bool {
validSortFields := []string{
"created_at",
"updated_at",
"status",
"submission_mode",
}
for _, validField := range validSortFields {
if strings.EqualFold(sortBy, validField) {
return true
}
}
return false
}
// IsValidSortOrder validates if the sort order parameter is valid
func IsValidSortOrder(sortOrder string) bool {
validOrders := []string{"asc", "desc"}
for _, validOrder := range validOrders {
if strings.EqualFold(sortOrder, validOrder) {
return true
}
}
return false
}
// SanitizeString removes potentially dangerous characters from input strings
func SanitizeString(input string) string {
// Remove HTML tags
htmlTagRegex := regexp.MustCompile(`<[^>]*>`)
sanitized := htmlTagRegex.ReplaceAllString(input, "")
// Remove script tags
scriptRegex := regexp.MustCompile(`(?i)<script[^>]*>.*?</script>`)
sanitized = scriptRegex.ReplaceAllString(sanitized, "")
// Trim whitespace
sanitized = strings.TrimSpace(sanitized)
return sanitized
}
// ValidateSearchQuery validates if the search query is valid
func ValidateSearchQuery(query string) bool {
if query == "" {
return true // Empty query is valid
}
// Query should not be too long
if len(query) > 200 {
return false
}
// Query should not contain only special characters
if regexp.MustCompile(`^[^a-zA-Z0-9\s]+$`).MatchString(query) {
return false
}
return true
}