Initial commit: 商品售卖网站

This commit is contained in:
2026-04-13 07:20:09 +08:00
commit c6154273f2
865 changed files with 26573 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
package utils
import (
"fmt"
"log"
"sale/internal/config"
"sale/internal/models"
sqlite "github.com/glebarez/sqlite"
"gorm.io/driver/mysql"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
var DB *gorm.DB
func InitDB() {
var err error
cfg := config.AppConfig.Database
switch cfg.Driver {
case "mysql":
dsn := fmt.Sprintf(
"%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DBName,
)
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
case "postgres":
dsn := fmt.Sprintf(
"host=%s port=%s user=%s password=%s dbname=%s sslmode=%s",
cfg.Host, cfg.Port, cfg.User, cfg.Password, cfg.DBName, cfg.SSLMode,
)
DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
default:
DB, err = gorm.Open(sqlite.Open(cfg.FilePath), &gorm.Config{
Logger: logger.Default.LogMode(logger.Info),
})
}
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
log.Printf("Database connected successfully (driver: %s)", cfg.Driver)
}
func AutoMigrate() {
err := DB.AutoMigrate(
&models.User{},
&models.Category{},
&models.Brand{},
&models.Product{},
&models.ProductCustomField{},
&models.Inventory{},
&models.Cart{},
&models.Order{},
&models.OrderItem{},
&models.Address{},
&models.Lottery{},
&models.LotteryPrize{},
&models.LotteryParticipant{},
&models.LotteryWinner{},
&models.Ticket{},
&models.SystemSetting{},
&models.SupplierAuthorization{},
&models.Article{},
)
if err != nil {
log.Fatalf("Failed to migrate database: %v", err)
}
log.Println("Database migrated successfully")
}
+50
View File
@@ -0,0 +1,50 @@
package utils
import (
"fmt"
"net/smtp"
"sale/internal/config"
)
func SendEmail(to, subject, body string) error {
cfg := config.AppConfig.SMTP
if cfg.Host == "" {
return fmt.Errorf("SMTP not configured")
}
auth := smtp.PlainAuth("", cfg.User, cfg.Password, cfg.Host)
msg := fmt.Sprintf(
"From: %s\r\nTo: %s\r\nSubject: %s\r\nContent-Type: text/html; charset=UTF-8\r\n\r\n%s",
cfg.From, to, subject, body,
)
return smtp.SendMail(
fmt.Sprintf("%s:%s", cfg.Host, cfg.Port),
auth,
cfg.From,
[]string{to},
[]byte(msg),
)
}
func SendVerifyEmail(to, code string) error {
subject := "邮箱验证码"
body := fmt.Sprintf(`
<h2>邮箱验证</h2>
<p>您的验证码是:<strong style="font-size:24px;color:#1890ff;">%s</strong></p>
<p>验证码有效期为30分钟,请尽快使用。</p>
`, code)
return SendEmail(to, subject, body)
}
func SendResetPasswordEmail(to, code string) error {
subject := "重置密码验证码"
body := fmt.Sprintf(`
<h2>重置密码</h2>
<p>您的验证码是:<strong style="font-size:24px;color:#1890ff;">%s</strong></p>
<p>验证码有效期为30分钟,请尽快使用。</p>
`, code)
return SendEmail(to, subject, body)
}
+46
View File
@@ -0,0 +1,46 @@
package utils
import (
"errors"
"time"
"sale/internal/config"
"github.com/golang-jwt/jwt/v5"
)
type Claims struct {
UserID uint `json:"user_id"`
Role string `json:"role"`
jwt.RegisteredClaims
}
func GenerateToken(userID uint, role string) (string, error) {
claims := Claims{
UserID: userID,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(config.AppConfig.JWT.ExpireHour) * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(config.AppConfig.JWT.Secret))
}
func ParseToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(config.AppConfig.JWT.Secret), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("invalid token")
}
+15
View File
@@ -0,0 +1,15 @@
package utils
import (
"golang.org/x/crypto/bcrypt"
)
func HashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
func CheckPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
+26
View File
@@ -0,0 +1,26 @@
package utils
import (
"crypto/rand"
"math/big"
)
func GenerateInviteCode() string {
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
code := make([]byte, 8)
for i := range code {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
code[i] = charset[n.Int64()]
}
return string(code)
}
func GenerateVerifyCode() string {
const charset = "0123456789"
code := make([]byte, 6)
for i := range code {
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(charset))))
code[i] = charset[n.Int64()]
}
return string(code)
}