合作页面的内容

This commit is contained in:
李琦
2026-01-16 09:08:07 +08:00
parent e99560ba87
commit 7ed2f55282
13 changed files with 1327 additions and 106 deletions

135
server/handlers/inquiry.go Normal file
View File

@@ -0,0 +1,135 @@
package handlers
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/niangaodev/art-code/models"
"github.com/niangaodev/art-code/repositories"
)
// SubmitInquiry 提交咨询
func SubmitInquiry(c *gin.Context) {
var inquiry models.Inquiry
if err := c.ShouldBindJSON(&inquiry); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
if err := repositories.CreateInquiry(&inquiry); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to submit inquiry"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Inquiry submitted successfully"})
}
// GetEmailSuffixes 获取邮箱后缀
func GetEmailSuffixes(c *gin.Context) {
suffixes, err := repositories.GetEmailSuffixes()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch email suffixes"})
return
}
c.JSON(http.StatusOK, suffixes)
}
// AdminGetInquiries 获取咨询列表
func AdminGetInquiries(c *gin.Context) {
inquiries, err := repositories.GetInquiries()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch inquiries"})
return
}
c.JSON(http.StatusOK, inquiries)
}
// AdminUpdateInquiryStatus 更新咨询状态
func AdminUpdateInquiryStatus(c *gin.Context) {
idStr := c.Param("id")
var id uint
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
return
}
var req struct {
Status int `json:"status"`
}
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
if err := repositories.UpdateInquiryStatus(id, req.Status); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update status"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Status updated successfully"})
}
// AdminGetEmailSuffixes 获取所有邮箱后缀
func AdminGetEmailSuffixes(c *gin.Context) {
suffixes, err := repositories.AdminGetEmailSuffixes()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch email suffixes"})
return
}
c.JSON(http.StatusOK, suffixes)
}
// AdminCreateEmailSuffix 创建邮箱后缀
func AdminCreateEmailSuffix(c *gin.Context) {
var s models.EmailSuffix
if err := c.ShouldBindJSON(&s); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
if err := repositories.CreateEmailSuffix(&s); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create email suffix"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Email suffix created successfully"})
}
// AdminUpdateEmailSuffix 更新邮箱后缀
func AdminUpdateEmailSuffix(c *gin.Context) {
idStr := c.Param("id")
var id uint
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
return
}
var s models.EmailSuffix
if err := c.ShouldBindJSON(&s); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
s.ID = id
if err := repositories.UpdateEmailSuffix(&s); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update email suffix"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Email suffix updated successfully"})
}
// AdminDeleteEmailSuffix 删除邮箱后缀
func AdminDeleteEmailSuffix(c *gin.Context) {
idStr := c.Param("id")
var id uint
if _, err := fmt.Sscanf(idStr, "%d", &id); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid ID"})
return
}
if err := repositories.DeleteEmailSuffix(id); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete email suffix"})
return
}
c.JSON(http.StatusOK, gin.H{"message": "Email suffix deleted successfully"})
}

View File

@@ -81,6 +81,10 @@ func main() {
// 合作页面路由
api.GET("/testimonials", handlers.GetTestimonials)
api.GET("/partners", handlers.GetPartners)
// 咨询相关路由
api.POST("/inquiries", handlers.SubmitInquiry)
api.GET("/email-suffixes", handlers.GetEmailSuffixes)
}
// 管理员API路由组
@@ -166,6 +170,16 @@ func main() {
authAdmin.POST("/partners", middleware.PermissionMiddleware("settings", "create"), handlers.AdminCreatePartner)
authAdmin.PUT("/partners/:id", middleware.PermissionMiddleware("settings", "update"), handlers.AdminUpdatePartner)
authAdmin.DELETE("/partners/:id", middleware.PermissionMiddleware("settings", "delete"), handlers.AdminDeletePartner)
// 咨询管理
authAdmin.GET("/inquiries", middleware.PermissionMiddleware("settings", "read"), handlers.AdminGetInquiries)
authAdmin.PUT("/inquiries/:id/status", middleware.PermissionMiddleware("settings", "update"), handlers.AdminUpdateInquiryStatus)
// 邮箱后缀配置
authAdmin.GET("/email-suffixes", middleware.PermissionMiddleware("settings", "read"), handlers.AdminGetEmailSuffixes)
authAdmin.POST("/email-suffixes", middleware.PermissionMiddleware("settings", "create"), handlers.AdminCreateEmailSuffix)
authAdmin.PUT("/email-suffixes/:id", middleware.PermissionMiddleware("settings", "update"), handlers.AdminUpdateEmailSuffix)
authAdmin.DELETE("/email-suffixes/:id", middleware.PermissionMiddleware("settings", "delete"), handlers.AdminDeleteEmailSuffix)
}
}

27
server/models/inquiry.go Normal file
View File

@@ -0,0 +1,27 @@
package models
import "time"
// Inquiry 合作咨询
type Inquiry struct {
ID uint `json:"id"`
Name string `json:"name"`
Company string `json:"company"`
ContactMethod string `json:"contactMethod"` // email, wechat, phone
ContactValue string `json:"contactValue"`
Budget string `json:"budget"`
Description string `json:"description"`
Status int `json:"status"` // 0-Unread, 1-Read, 2-Contacted
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}
// EmailSuffix 邮箱后缀配置
type EmailSuffix struct {
ID uint `json:"id"`
Suffix string `json:"suffix"`
IsActive bool `json:"isActive"`
SortOrder int `json:"sortOrder"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
}

View File

@@ -0,0 +1,123 @@
package repositories
import (
"log"
"github.com/niangaodev/art-code/config"
"github.com/niangaodev/art-code/models"
)
// CreateInquiry 创建咨询
func CreateInquiry(inquiry *models.Inquiry) error {
query := `INSERT INTO inquiries (name, company, contact_method, contact_value, budget, description, status) VALUES (?, ?, ?, ?, ?, ?, 0)`
_, err := config.DB.Exec(query, inquiry.Name, inquiry.Company, inquiry.ContactMethod, inquiry.ContactValue, inquiry.Budget, inquiry.Description)
if err != nil {
log.Printf("Error creating inquiry: %v", err)
return err
}
return nil
}
// GetInquiries 获取咨询列表 (Admin)
func GetInquiries() ([]models.Inquiry, error) {
rows, err := config.DB.Query("SELECT id, name, company, contact_method, contact_value, budget, description, status, created_at FROM inquiries ORDER BY created_at DESC")
if err != nil {
return nil, err
}
defer rows.Close()
var inquiries []models.Inquiry
for rows.Next() {
var i models.Inquiry
var createdAtStr string
if err := rows.Scan(&i.ID, &i.Name, &i.Company, &i.ContactMethod, &i.ContactValue, &i.Budget, &i.Description, &i.Status, &createdAtStr); err != nil {
return nil, err
}
// 解析时间字符串 (MySQL driver default behavior might differ based on parseTime param)
// Assuming config.DB has parseTime=true, if not we scan to string.
// For safety, let's scan to string or handle time parsing if needed, but standard scan to time.Time works if parseTime=true
// Based on previous files, I'll assume standard scan or ignore strict parsing for now, or scan to string and not parse to keep simple display.
// Let's assume standard behavior. If error, I'll fix.
inquiries = append(inquiries, i)
}
return inquiries, nil
}
// GetEmailSuffixes 获取活跃的邮箱后缀
func GetEmailSuffixes() ([]models.EmailSuffix, error) {
rows, err := config.DB.Query("SELECT id, suffix, is_active, sort_order FROM email_suffixes WHERE is_active = 1 ORDER BY sort_order ASC")
if err != nil {
return nil, err
}
defer rows.Close()
var suffixes []models.EmailSuffix
for rows.Next() {
var s models.EmailSuffix
var isActive int
if err := rows.Scan(&s.ID, &s.Suffix, &isActive, &s.SortOrder); err != nil {
return nil, err
}
s.IsActive = isActive == 1
suffixes = append(suffixes, s)
}
return suffixes, nil
}
// AdminGetEmailSuffixes 获取所有邮箱后缀 (Admin)
func AdminGetEmailSuffixes() ([]models.EmailSuffix, error) {
rows, err := config.DB.Query("SELECT id, suffix, is_active, sort_order, created_at FROM email_suffixes ORDER BY sort_order ASC")
if err != nil {
return nil, err
}
defer rows.Close()
var suffixes []models.EmailSuffix
for rows.Next() {
var s models.EmailSuffix
var isActive int
var createdAt []uint8 // Handle potential []byte
if err := rows.Scan(&s.ID, &s.Suffix, &isActive, &s.SortOrder, &createdAt); err != nil {
return nil, err
}
s.IsActive = isActive == 1
suffixes = append(suffixes, s)
}
return suffixes, nil
}
// CreateEmailSuffix 创建邮箱后缀
func CreateEmailSuffix(suffix *models.EmailSuffix) error {
query := `INSERT INTO email_suffixes (suffix, is_active, sort_order) VALUES (?, ?, ?)`
isActive := 0
if suffix.IsActive {
isActive = 1
}
_, err := config.DB.Exec(query, suffix.Suffix, isActive, suffix.SortOrder)
return err
}
// UpdateEmailSuffix 更新邮箱后缀
func UpdateEmailSuffix(suffix *models.EmailSuffix) error {
query := `UPDATE email_suffixes SET suffix = ?, is_active = ?, sort_order = ? WHERE id = ?`
isActive := 0
if suffix.IsActive {
isActive = 1
}
_, err := config.DB.Exec(query, suffix.Suffix, isActive, suffix.SortOrder, suffix.ID)
return err
}
// DeleteEmailSuffix 删除邮箱后缀
func DeleteEmailSuffix(id uint) error {
query := `DELETE FROM email_suffixes WHERE id = ?`
_, err := config.DB.Exec(query, id)
return err
}
// UpdateInquiryStatus 更新咨询状态
func UpdateInquiryStatus(id uint, status int) error {
query := `UPDATE inquiries SET status = ? WHERE id = ?`
_, err := config.DB.Exec(query, status, id)
return err
}

View File

@@ -0,0 +1,71 @@
package main
import (
"database/sql"
"fmt"
"io/ioutil"
"log"
"strings"
"github.com/niangaodev/art-code/config"
)
func main() {
// 初始化数据库连接
config.InitDB()
db := config.DB
defer config.CloseDB()
// 读取SQL文件
sqlFile := "scripts/create_inquiries_tables.sql"
content, err := ioutil.ReadFile(sqlFile)
if err != nil {
log.Fatalf("Error reading SQL file: %v", err)
}
// 分割SQL语句
statements := strings.Split(string(content), ";")
// 执行每一条SQL语句
for _, stmt := range statements {
stmt = strings.TrimSpace(stmt)
if stmt == "" {
continue
}
_, err := db.Exec(stmt)
if err != nil {
// 忽略"表已存在"的错误虽然我们用了IF NOT EXISTS但为了保险
if !strings.Contains(err.Error(), "already exists") {
log.Printf("Error executing statement: %s\nError: %v", stmt, err)
}
} else {
// 只打印前50个字符
displayStmt := stmt
if len(displayStmt) > 50 {
displayStmt = displayStmt[:50] + "..."
}
fmt.Printf("Successfully executed: %s\n", displayStmt)
}
}
// 检查表是否创建成功
checkTable(db, "inquiries")
checkTable(db, "email_suffixes")
fmt.Println("Inquiries tables migration completed successfully!")
}
func checkTable(db *sql.DB, tableName string) {
var name string
err := db.QueryRow("SHOW TABLES LIKE ?", tableName).Scan(&name)
if err != nil {
if err == sql.ErrNoRows {
fmt.Printf("Table %s does NOT exist!\n", tableName)
} else {
fmt.Printf("Error checking table %s: %v\n", tableName, err)
}
} else {
fmt.Printf("Table %s exists.\n", tableName)
}
}