1.0
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"os"
|
||||
)
|
||||
|
||||
var MineConfig *Config
|
||||
|
||||
// Config 应用配置结构
|
||||
type Config struct {
|
||||
App AppConfig `yaml:"app"`
|
||||
Database DatabaseConfig `yaml:"database"`
|
||||
JWT JWTConfig `yaml:"jwt"`
|
||||
Server ServerConfig `yaml:"server"`
|
||||
Log LogConfig `yaml:"log"`
|
||||
}
|
||||
|
||||
// AppConfig 应用基础配置
|
||||
type AppConfig struct {
|
||||
Name string `yaml:"name"`
|
||||
Version string `yaml:"version"`
|
||||
Port int `yaml:"port"`
|
||||
Env string `yaml:"env"`
|
||||
}
|
||||
|
||||
// DatabaseConfig 数据库配置
|
||||
type DatabaseConfig struct {
|
||||
Driver string `yaml:"driver"`
|
||||
Host string `yaml:"host"`
|
||||
Port int `yaml:"port"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
DBName string `yaml:"dbname"`
|
||||
Charset string `yaml:"charset"`
|
||||
SQLitePath string `yaml:"sqlite_path"`
|
||||
MaxIdleConns int `yaml:"max_idle_conns"`
|
||||
MaxOpenConns int `yaml:"max_open_conns"`
|
||||
ConnMaxLifetime int `yaml:"conn_max_lifetime"`
|
||||
}
|
||||
|
||||
// JWTConfig JWT配置
|
||||
type JWTConfig struct {
|
||||
SecretKey string `yaml:"secret_key"`
|
||||
ExpireHours int `yaml:"expire_hours"`
|
||||
}
|
||||
|
||||
// ServerConfig 服务器配置
|
||||
type ServerConfig struct {
|
||||
ReadTimeout int `yaml:"read_timeout"`
|
||||
WriteTimeout int `yaml:"write_timeout"`
|
||||
StaticPath string `yaml:"static_path"`
|
||||
UploadMaxSize int `yaml:"upload_max_size"`
|
||||
}
|
||||
|
||||
// LogConfig 日志配置
|
||||
type LogConfig struct {
|
||||
Level string `yaml:"level"`
|
||||
FilePath string `yaml:"file_path"`
|
||||
MaxSize int `yaml:"max_size"`
|
||||
MaxAge int `yaml:"max_age"`
|
||||
MaxBackups int `yaml:"max_backups"`
|
||||
}
|
||||
|
||||
// LoadConfig 加载配置文件
|
||||
func LoadConfig(configPath string) (*Config, error) {
|
||||
config := &Config{}
|
||||
|
||||
// 读取配置文件
|
||||
file, err := os.ReadFile(configPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 解析YAML
|
||||
err = yaml.Unmarshal(file, config)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("解析配置文件失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置全局配置
|
||||
MineConfig = config
|
||||
|
||||
return config, nil
|
||||
}
|
||||
|
||||
// GetDSN 获取数据库连接字符串
|
||||
func (db *DatabaseConfig) GetDSN() string {
|
||||
switch db.Driver {
|
||||
case "mysql":
|
||||
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local",
|
||||
db.Username, db.Password, db.Host, db.Port, db.DBName, db.Charset)
|
||||
case "postgres":
|
||||
return fmt.Sprintf("host=%s user=%s password=%s dbname=%s port=%d sslmode=disable TimeZone=Asia/Shanghai",
|
||||
db.Host, db.Username, db.Password, db.DBName, db.Port)
|
||||
case "sqlite":
|
||||
return db.SQLitePath
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
// InitDatabase 初始化数据库连接
|
||||
func InitDatabase(config *DatabaseConfig) error {
|
||||
var err error
|
||||
var dialector gorm.Dialector
|
||||
|
||||
// 根据驱动类型选择相应的方言
|
||||
switch config.Driver {
|
||||
case "mysql":
|
||||
dialector = mysql.Open(config.GetDSN())
|
||||
case "postgres":
|
||||
dialector = postgres.Open(config.GetDSN())
|
||||
case "sqlite":
|
||||
dialector = sqlite.Open(config.GetDSN())
|
||||
default:
|
||||
return fmt.Errorf("不支持的数据库驱动: %s", config.Driver)
|
||||
}
|
||||
|
||||
// GORM 配置
|
||||
gormConfig := &gorm.Config{
|
||||
Logger: logger.Default.LogMode(logger.Info),
|
||||
}
|
||||
|
||||
// 连接数据库
|
||||
DB, err = gorm.Open(dialector, gormConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("连接数据库失败: %v", err)
|
||||
}
|
||||
|
||||
// 获取底层的 *sql.DB
|
||||
sqlDB, err := DB.DB()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取数据库连接失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置连接池参数
|
||||
sqlDB.SetMaxIdleConns(config.MaxIdleConns)
|
||||
sqlDB.SetMaxOpenConns(config.MaxOpenConns)
|
||||
sqlDB.SetConnMaxLifetime(time.Duration(config.ConnMaxLifetime) * time.Second)
|
||||
|
||||
// 测试连接
|
||||
if err := sqlDB.Ping(); err != nil {
|
||||
return fmt.Errorf("数据库连接测试失败: %v", err)
|
||||
}
|
||||
|
||||
log.Println("数据库连接成功")
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDB 获取数据库实例
|
||||
func GetDB() *gorm.DB {
|
||||
return DB
|
||||
}
|
||||
|
||||
// CloseDatabase 关闭数据库连接
|
||||
func CloseDatabase() error {
|
||||
sqlDB, err := DB.DB()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return sqlDB.Close()
|
||||
}
|
||||
|
||||
// AutoMigrate 自动迁移数据表
|
||||
func AutoMigrate(models ...interface{}) error {
|
||||
return DB.AutoMigrate(models...)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package common
|
||||
|
||||
type Result struct {
|
||||
Code int `json:"code"`
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
func Success(data interface{}) Result {
|
||||
|
||||
return Result{200, true, "", data}
|
||||
}
|
||||
|
||||
func SuccessWithMessage(data interface{}, message string) Result {
|
||||
return Result{200, true, message, data}
|
||||
}
|
||||
|
||||
func Error(code int, message string) Result {
|
||||
return Result{code, false, message, nil}
|
||||
}
|
||||
func ErrorWithData(data interface{}, code int, message string) Result {
|
||||
return Result{code, true, message, data}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package config
|
||||
|
||||
// 配置键常量定义
|
||||
const (
|
||||
// 系统基础配置
|
||||
ConfigSiteName = "site_name" // 网站名称
|
||||
ConfigSiteUrl = "site_url" // 网站地址
|
||||
ConfigSiteLogo = "site_logo" // 网站Logo
|
||||
ConfigSiteDescription = "site_description" // 网站描述
|
||||
|
||||
// 商品相关配置
|
||||
ConfigPriceIncreaseRate = "warehouse_price_rate" // 商品入库价格增长率
|
||||
ConfigMaxStockQuantity = "max_stock_quantity" // 最大库存数量
|
||||
ConfigMinSalePrice = "min_sale_price" // 最低销售价格
|
||||
|
||||
// 用户相关配置
|
||||
ConfigMaxUserLevel = "max_user_level" // 用户最大等级
|
||||
ConfigDefaultUserScore = "default_user_score" // 新用户默认积分
|
||||
ConfigReferralBonus = "referral_bonus" // 推荐奖励积分
|
||||
|
||||
// 订单相关配置
|
||||
ConfigOrderTimeout = "order_timeout" // 订单超时时间(分钟)
|
||||
ConfigMaxOrderQuantity = "max_order_quantity" // 单笔订单最大数量
|
||||
ConfigMinOrderAmount = "min_order_amount" // 最小订单金额
|
||||
|
||||
// 系统功能开关
|
||||
ConfigEnableRegistration = "enable_registration" // 是否开放注册
|
||||
ConfigEnableUpgrade = "enable_upgrade" // 是否开启升级功能
|
||||
ConfigMaintenanceMode = "maintenance_mode" // 维护模式
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
package config
|
||||
|
||||
// ConfigEvent 配置变更事件
|
||||
type ConfigEvent struct {
|
||||
ConfigKey string `json:"config_key"` // 配置键
|
||||
OldValue interface{} `json:"old_value"` // 旧值
|
||||
NewValue interface{} `json:"new_value"` // 新值
|
||||
ChangeType string `json:"change_type"` // 变更类型:create, update, delete
|
||||
ChangedBy uint `json:"changed_by"` // 修改人ID
|
||||
ChangedAt int64 `json:"changed_at"` // 修改时间戳
|
||||
}
|
||||
|
||||
// ConfigEventHandler 配置事件处理器接口
|
||||
type ConfigEventHandler interface {
|
||||
Handle(event ConfigEvent) error
|
||||
GetConfigKeys() []string // 返回该处理器关心的配置键列表
|
||||
}
|
||||
|
||||
// ConfigChange 配置变更记录(用于审计)
|
||||
type ConfigChange struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
ConfigKey string `gorm:"type:varchar(100);not null;comment:配置键" json:"config_key"`
|
||||
OldValue string `gorm:"type:text;comment:旧值" json:"old_value"`
|
||||
NewValue string `gorm:"type:text;comment:新值" json:"new_value"`
|
||||
ChangeType string `gorm:"type:varchar(20);not null;comment:变更类型" json:"change_type"`
|
||||
ChangedBy uint `gorm:"type:int;not null;comment:修改人ID" json:"changed_by"`
|
||||
ChangedAt int64 `gorm:"type:bigint;not null;comment:修改时间戳" json:"changed_at"`
|
||||
Extra map[string]interface{} `gorm:"type:json;comment:额外信息" json:"extra"`
|
||||
}
|
||||
|
||||
func (ConfigChange) TableName() string {
|
||||
return "config_changes"
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/config"
|
||||
"log"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ConfigEventManager 配置事件管理器
|
||||
type ConfigEventManager struct {
|
||||
handlers map[string][]config.ConfigEventHandler // key: config_key, value: handlers
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
var (
|
||||
configEventManager *ConfigEventManager
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// GetConfigEventManager 获取配置事件管理器单例
|
||||
func GetConfigEventManager() *ConfigEventManager {
|
||||
once.Do(func() {
|
||||
configEventManager = &ConfigEventManager{
|
||||
handlers: make(map[string][]config.ConfigEventHandler),
|
||||
}
|
||||
})
|
||||
return configEventManager
|
||||
}
|
||||
|
||||
// RegisterHandler 注册配置事件处理器
|
||||
func (cem *ConfigEventManager) RegisterHandler(handler config.ConfigEventHandler) {
|
||||
cem.mutex.Lock()
|
||||
defer cem.mutex.Unlock()
|
||||
|
||||
configKeys := handler.GetConfigKeys()
|
||||
for _, key := range configKeys {
|
||||
if cem.handlers[key] == nil {
|
||||
cem.handlers[key] = []config.ConfigEventHandler{}
|
||||
}
|
||||
cem.handlers[key] = append(cem.handlers[key], handler)
|
||||
}
|
||||
|
||||
log.Printf("注册配置事件处理器,监听配置: %v", configKeys)
|
||||
}
|
||||
|
||||
// TriggerEvent 触发配置变更事件
|
||||
func (cem *ConfigEventManager) TriggerEvent(event config.ConfigEvent) {
|
||||
cem.mutex.RLock()
|
||||
handlers := cem.handlers[event.ConfigKey]
|
||||
cem.mutex.RUnlock()
|
||||
|
||||
if len(handlers) == 0 {
|
||||
log.Printf("配置 %s 无对应的事件处理器", event.ConfigKey)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("触发配置变更事件: %s, 旧值: %v, 新值: %v", event.ConfigKey, event.OldValue, event.NewValue)
|
||||
|
||||
// 并发处理事件,避免阻塞
|
||||
for _, handler := range handlers {
|
||||
go func(h config.ConfigEventHandler) {
|
||||
if err := h.Handle(event); err != nil {
|
||||
log.Printf("处理配置事件失败: %s, 错误: %v", event.ConfigKey, err)
|
||||
}
|
||||
}(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// ListRegisteredHandlers 列出已注册的处理器(用于调试)
|
||||
func (cem *ConfigEventManager) ListRegisteredHandlers() map[string]int {
|
||||
cem.mutex.RLock()
|
||||
defer cem.mutex.RUnlock()
|
||||
|
||||
result := make(map[string]int)
|
||||
for key, handlers := range cem.handlers {
|
||||
result[key] = len(handlers)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/config"
|
||||
"awesomeProject/internal/models"
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
"log"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// PriceConfigHandler 价格配置变更处理器
|
||||
type PriceConfigHandler struct{}
|
||||
|
||||
// GetConfigKeys 返回该处理器关心的配置键
|
||||
func (h *PriceConfigHandler) GetConfigKeys() []string {
|
||||
return []string{
|
||||
config.ConfigPriceIncreaseRate,
|
||||
config.ConfigMinSalePrice,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 处理配置变更事件
|
||||
func (h *PriceConfigHandler) Handle(event config.ConfigEvent) error {
|
||||
switch event.ConfigKey {
|
||||
case config.ConfigPriceIncreaseRate:
|
||||
return h.handlePriceIncreaseRateChange(event)
|
||||
case config.ConfigMinSalePrice:
|
||||
return h.handleMinSalePriceChange(event)
|
||||
default:
|
||||
return fmt.Errorf("未知的配置键: %s", event.ConfigKey)
|
||||
}
|
||||
}
|
||||
|
||||
// handlePriceIncreaseRateChange 处理价格增长率变更
|
||||
func (h *PriceConfigHandler) handlePriceIncreaseRateChange(event config.ConfigEvent) error {
|
||||
log.Printf("价格增长率配置变更: %v -> %v", event.OldValue, event.NewValue)
|
||||
|
||||
// 解析新的增长率
|
||||
newRateStr, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("价格增长率配置值类型错误")
|
||||
}
|
||||
|
||||
newRate, err := strconv.ParseFloat(newRateStr, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("价格增长率配置值格式错误: %v", err)
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
|
||||
var secondList []models.SecondaryProduct
|
||||
result := db.Where("status = ? AND quantity > ?", 1, 0).Find(&secondList) // 修正:传递切片指针而不是值
|
||||
if result.Error != nil {
|
||||
log.Printf("查询失败: %v", result.Error)
|
||||
return result.Error
|
||||
}
|
||||
|
||||
// 如果没有找到符合条件的记录,直接返回
|
||||
if len(secondList) == 0 {
|
||||
log.Printf("没有找到需要更新的二级商品")
|
||||
return nil
|
||||
}
|
||||
tx := db.Begin()
|
||||
|
||||
for _, product := range secondList {
|
||||
// 检查商品是否有关联的仓库记录
|
||||
if product.UserWarehouseID == 0 {
|
||||
log.Printf("二级商品 ID: %d 没有关联的仓库记录,跳过", product.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
var warehouse models.UserWarehouse
|
||||
if err := tx.Where("id = ?", product.UserWarehouseID).First(&warehouse).Error; err != nil {
|
||||
tx.Rollback()
|
||||
log.Printf("查询仓库记录失败,ID: %d, 错误: %v", product.UserWarehouseID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
newWarehousePrice := warehouse.PurchasePrice + newRate
|
||||
if err := tx.Model(&models.UserWarehouse{}).Where("id = ?", product.UserWarehouseID).
|
||||
Update("quantity", product.Quantity+warehouse.Quantity).Error; err != nil {
|
||||
tx.Rollback()
|
||||
log.Printf("更新仓库价格失败,ID: %d, 错误: %v", product.UserWarehouseID, err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("已更新仓库商品 ID: %d 的价格,新价格: %.2f", product.UserWarehouseID, newWarehousePrice)
|
||||
|
||||
tx.Model(&models.SecondaryProduct{}).Where("id = ?", product.ID).Update("status", 0)
|
||||
}
|
||||
|
||||
tx.Model(&models.UserWarehouse{}).Where("quantity>?", 0).Update("status", 1).
|
||||
Update("warehouse_price", gorm.Expr("purchase_price + ?", newRate))
|
||||
tx.Commit()
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("价格增长率已更新为: %.2f%%", newRate)
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleMinSalePriceChange 处理最低销售价格变更
|
||||
func (h *PriceConfigHandler) handleMinSalePriceChange(event config.ConfigEvent) error {
|
||||
log.Printf("最低销售价格配置变更: %v -> %v", event.OldValue, event.NewValue)
|
||||
|
||||
// 解析新的最低价格
|
||||
newPriceStr, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("最低销售价格配置值类型错误")
|
||||
}
|
||||
|
||||
newPrice, err := strconv.ParseFloat(newPriceStr, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("最低销售价格配置值格式错误: %v", err)
|
||||
}
|
||||
|
||||
// 验证价格范围
|
||||
if newPrice < 0 {
|
||||
return fmt.Errorf("最低销售价格不能为负数")
|
||||
}
|
||||
|
||||
// 业务逻辑:检查并更新现有的低于最低价格的商品
|
||||
if err := h.updateLowPriceProducts(newPrice); err != nil {
|
||||
log.Printf("更新低价商品失败: %v", err)
|
||||
// 不返回错误,避免阻塞其他处理器
|
||||
}
|
||||
|
||||
log.Printf("最低销售价格已更新为: %.2f", newPrice)
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateLowPriceProducts 更新低于最低价格的商品
|
||||
func (h *PriceConfigHandler) updateLowPriceProducts(minPrice float64) error {
|
||||
db := common.GetDB()
|
||||
|
||||
// 查找低于最低价格的二级商品
|
||||
var lowPriceProducts []models.SecondaryProduct
|
||||
err := db.Where("sale_price < ? AND status = 1", minPrice).Find(&lowPriceProducts).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询低价商品失败: %v", err)
|
||||
}
|
||||
|
||||
if len(lowPriceProducts) == 0 {
|
||||
log.Printf("没有发现低于最低价格(%.2f)的商品", minPrice)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 批量更新这些商品的状态为下架
|
||||
result := db.Model(&models.SecondaryProduct{}).
|
||||
Where("sale_price < ? AND status = 1", minPrice).
|
||||
Update("status", 0)
|
||||
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("批量下架低价商品失败: %v", result.Error)
|
||||
}
|
||||
|
||||
log.Printf("已下架 %d 个低于最低价格的商品", result.RowsAffected)
|
||||
|
||||
// 这里可以添加更多逻辑,比如:
|
||||
// 1. 发送通知给商品卖家
|
||||
// 2. 记录下架日志
|
||||
// 3. 更新相关统计数据
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/config"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// SystemConfigHandler 系统配置变更处理器
|
||||
type SystemConfigHandler struct{}
|
||||
|
||||
// GetConfigKeys 返回该处理器关心的配置键
|
||||
func (h *SystemConfigHandler) GetConfigKeys() []string {
|
||||
return []string{
|
||||
config.ConfigEnableRegistration,
|
||||
config.ConfigMaintenanceMode,
|
||||
config.ConfigSiteName,
|
||||
config.ConfigSiteUrl,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 处理配置变更事件
|
||||
func (h *SystemConfigHandler) Handle(event config.ConfigEvent) error {
|
||||
switch event.ConfigKey {
|
||||
case config.ConfigEnableRegistration:
|
||||
return h.handleRegistrationToggle(event)
|
||||
case config.ConfigMaintenanceMode:
|
||||
return h.handleMaintenanceModeToggle(event)
|
||||
case config.ConfigSiteName:
|
||||
return h.handleSiteNameChange(event)
|
||||
case config.ConfigSiteUrl:
|
||||
return h.handleSiteUrlChange(event)
|
||||
default:
|
||||
return fmt.Errorf("未知的配置键: %s", event.ConfigKey)
|
||||
}
|
||||
}
|
||||
|
||||
// handleRegistrationToggle 处理注册开关变更
|
||||
func (h *SystemConfigHandler) handleRegistrationToggle(event config.ConfigEvent) error {
|
||||
newValueStr, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("注册开关配置值类型错误")
|
||||
}
|
||||
|
||||
enabled, err := strconv.ParseBool(newValueStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("注册开关配置值格式错误: %v", err)
|
||||
}
|
||||
|
||||
if enabled {
|
||||
log.Printf("用户注册已开启")
|
||||
// 这里可以添加逻辑,比如:
|
||||
// 1. 清理注册限制缓存
|
||||
// 2. 发送系统通知
|
||||
// 3. 更新前端配置缓存
|
||||
} else {
|
||||
log.Printf("用户注册已关闭")
|
||||
// 这里可以添加逻辑,比如:
|
||||
// 1. 设置注册限制
|
||||
// 2. 通知管理员
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleMaintenanceModeToggle 处理维护模式开关
|
||||
func (h *SystemConfigHandler) handleMaintenanceModeToggle(event config.ConfigEvent) error {
|
||||
newValueStr, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("维护模式配置值类型错误")
|
||||
}
|
||||
|
||||
enabled, err := strconv.ParseBool(newValueStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("维护模式配置值格式错误: %v", err)
|
||||
}
|
||||
|
||||
if enabled {
|
||||
log.Printf("系统进入维护模式")
|
||||
// 这里可以添加逻辑,比如:
|
||||
// 1. 断开非管理员用户连接
|
||||
// 2. 停止定时任务
|
||||
// 3. 发送维护通知
|
||||
// 4. 设置维护页面
|
||||
} else {
|
||||
log.Printf("系统退出维护模式")
|
||||
// 这里可以添加逻辑,比如:
|
||||
// 1. 恢复正常服务
|
||||
// 2. 重启定时任务
|
||||
// 3. 发送恢复通知
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleSiteNameChange 处理网站名称变更
|
||||
func (h *SystemConfigHandler) handleSiteNameChange(event config.ConfigEvent) error {
|
||||
newName, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("网站名称配置值类型错误")
|
||||
}
|
||||
|
||||
if len(newName) == 0 {
|
||||
return fmt.Errorf("网站名称不能为空")
|
||||
}
|
||||
|
||||
log.Printf("网站名称已更新: %s -> %s", event.OldValue, newName)
|
||||
|
||||
// 这里可以添加逻辑,比如:
|
||||
// 1. 更新邮件模板中的网站名称
|
||||
// 2. 刷新前端缓存
|
||||
// 3. 更新SEO相关配置
|
||||
// 4. 发送更新通知
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleSiteUrlChange 处理网站地址变更
|
||||
func (h *SystemConfigHandler) handleSiteUrlChange(event config.ConfigEvent) error {
|
||||
newUrl, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("网站地址配置值类型错误")
|
||||
}
|
||||
|
||||
if len(newUrl) == 0 {
|
||||
return fmt.Errorf("网站地址不能为空")
|
||||
}
|
||||
|
||||
// 简单的URL格式验证
|
||||
if len(newUrl) < 7 || (newUrl[:7] != "http://" && newUrl[:8] != "https://") {
|
||||
return fmt.Errorf("网站地址格式不正确,必须以http://或https://开头")
|
||||
}
|
||||
|
||||
log.Printf("网站地址已更新: %s -> %s", event.OldValue, newUrl)
|
||||
|
||||
// 这里可以添加逻辑,比如:
|
||||
// 1. 更新CORS配置
|
||||
// 2. 更新回调URL
|
||||
// 3. 刷新API文档
|
||||
// 4. 更新第三方服务配置
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/config"
|
||||
"awesomeProject/internal/models"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// UserConfigHandler 用户配置变更处理器
|
||||
type UserConfigHandler struct{}
|
||||
|
||||
// GetConfigKeys 返回该处理器关心的配置键
|
||||
func (h *UserConfigHandler) GetConfigKeys() []string {
|
||||
return []string{
|
||||
config.ConfigDefaultUserScore,
|
||||
config.ConfigReferralBonus,
|
||||
config.ConfigMaxUserLevel,
|
||||
config.ConfigEnableRegistration,
|
||||
}
|
||||
}
|
||||
|
||||
// Handle 处理配置变更事件
|
||||
func (h *UserConfigHandler) Handle(event config.ConfigEvent) error {
|
||||
switch event.ConfigKey {
|
||||
case config.ConfigDefaultUserScore:
|
||||
return h.handleDefaultUserScoreChange(event)
|
||||
case config.ConfigReferralBonus:
|
||||
return h.handleReferralBonusChange(event)
|
||||
case config.ConfigMaxUserLevel:
|
||||
return h.handleMaxUserLevelChange(event)
|
||||
case config.ConfigEnableRegistration:
|
||||
return h.handleRegistrationToggle(event)
|
||||
default:
|
||||
return fmt.Errorf("未知的配置键: %s", event.ConfigKey)
|
||||
}
|
||||
}
|
||||
|
||||
// handleDefaultUserScoreChange 处理默认用户积分变更
|
||||
func (h *UserConfigHandler) handleDefaultUserScoreChange(event config.ConfigEvent) error {
|
||||
newScoreStr, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("默认用户积分配置值类型错误")
|
||||
}
|
||||
|
||||
newScore, err := strconv.ParseFloat(newScoreStr, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("默认用户积分配置值格式错误: %v", err)
|
||||
}
|
||||
|
||||
if newScore < 0 {
|
||||
return fmt.Errorf("默认用户积分不能为负数")
|
||||
}
|
||||
|
||||
log.Printf("默认用户积分已更新: %v -> %.2f", event.OldValue, newScore)
|
||||
|
||||
// 这里可以添加业务逻辑,比如:
|
||||
// 1. 更新新注册用户的积分计算逻辑
|
||||
// 2. 发送通知给管理员
|
||||
// 3. 更新缓存中的默认积分值
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleReferralBonusChange 处理推荐奖励变更
|
||||
func (h *UserConfigHandler) handleReferralBonusChange(event config.ConfigEvent) error {
|
||||
newBonusStr, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("推荐奖励配置值类型错误")
|
||||
}
|
||||
|
||||
newBonus, err := strconv.ParseFloat(newBonusStr, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("推荐奖励配置值格式错误: %v", err)
|
||||
}
|
||||
|
||||
if newBonus < 0 {
|
||||
return fmt.Errorf("推荐奖励不能为负数")
|
||||
}
|
||||
|
||||
log.Printf("推荐奖励已更新: %v -> %.2f", event.OldValue, newBonus)
|
||||
|
||||
// 这里可以添加业务逻辑,比如:
|
||||
// 1. 重新计算推荐奖励策略
|
||||
// 2. 发送通知给有推荐关系的用户
|
||||
// 3. 更新推荐系统的计算规则
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleMaxUserLevelChange 处理最大用户等级变更
|
||||
func (h *UserConfigHandler) handleMaxUserLevelChange(event config.ConfigEvent) error {
|
||||
newLevelStr, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("最大用户等级配置值类型错误")
|
||||
}
|
||||
|
||||
newLevel, err := strconv.Atoi(newLevelStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("最大用户等级配置值格式错误: %v", err)
|
||||
}
|
||||
|
||||
if newLevel < 1 {
|
||||
return fmt.Errorf("最大用户等级不能小于1")
|
||||
}
|
||||
|
||||
log.Printf("最大用户等级已更新: %v -> %d", event.OldValue, newLevel)
|
||||
|
||||
// 检查是否有用户等级超过新的最大等级
|
||||
if err := h.checkAndAdjustUserLevels(newLevel); err != nil {
|
||||
log.Printf("调整用户等级失败: %v", err)
|
||||
// 不返回错误,避免阻塞其他处理器
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// handleRegistrationToggle 处理注册开关变更
|
||||
func (h *UserConfigHandler) handleRegistrationToggle(event config.ConfigEvent) error {
|
||||
newValueStr, ok := event.NewValue.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("注册开关配置值类型错误")
|
||||
}
|
||||
|
||||
enabled, err := strconv.ParseBool(newValueStr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("注册开关配置值格式错误: %v", err)
|
||||
}
|
||||
|
||||
if enabled {
|
||||
log.Printf("用户注册已开启")
|
||||
// 这里可以添加逻辑,比如:
|
||||
// 1. 清理注册限制缓存
|
||||
// 2. 发送开放注册通知
|
||||
// 3. 更新前端注册页面状态
|
||||
} else {
|
||||
log.Printf("用户注册已关闭")
|
||||
// 这里可以添加逻辑,比如:
|
||||
// 1. 设置注册限制
|
||||
// 2. 显示注册关闭页面
|
||||
// 3. 通知相关管理员
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkAndAdjustUserLevels 检查并调整超过最大等级的用户
|
||||
func (h *UserConfigHandler) checkAndAdjustUserLevels(maxLevel int) error {
|
||||
db := common.GetDB()
|
||||
|
||||
// 查找等级超过最大等级的用户
|
||||
var highLevelUsers []models.User
|
||||
err := db.Where("user_level > ?", maxLevel).Find(&highLevelUsers).Error
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询高等级用户失败: %v", err)
|
||||
}
|
||||
|
||||
if len(highLevelUsers) == 0 {
|
||||
log.Printf("没有发现超过最大等级(%d)的用户", maxLevel)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 批量调整这些用户的等级
|
||||
result := db.Model(&models.User{}).
|
||||
Where("user_level > ?", maxLevel).
|
||||
Update("user_level", maxLevel)
|
||||
|
||||
if result.Error != nil {
|
||||
return fmt.Errorf("批量调整用户等级失败: %v", result.Error)
|
||||
}
|
||||
|
||||
log.Printf("已调整 %d 个用户的等级到最大等级: %d", result.RowsAffected, maxLevel)
|
||||
|
||||
// 这里可以添加更多逻辑,比如:
|
||||
// 1. 发送等级调整通知给用户
|
||||
// 2. 记录等级变更日志
|
||||
// 3. 重新计算相关权益
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package events
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/events/handlers"
|
||||
"log"
|
||||
)
|
||||
|
||||
// InitEventSystem 初始化事件系统
|
||||
func InitEventSystem() {
|
||||
log.Println("初始化配置事件系统...")
|
||||
|
||||
eventManager := GetConfigEventManager()
|
||||
|
||||
// 注册价格配置处理器
|
||||
priceHandler := &handlers.PriceConfigHandler{}
|
||||
eventManager.RegisterHandler(priceHandler)
|
||||
|
||||
// 注册系统配置处理器
|
||||
systemHandler := &handlers.SystemConfigHandler{}
|
||||
eventManager.RegisterHandler(systemHandler)
|
||||
|
||||
// 注册用户配置处理器
|
||||
userHandler := &handlers.UserConfigHandler{}
|
||||
eventManager.RegisterHandler(userHandler)
|
||||
|
||||
// 可以继续注册更多处理器...
|
||||
// 例如:
|
||||
// orderHandler := &handlers.OrderConfigHandler{}
|
||||
// eventManager.RegisterHandler(orderHandler)
|
||||
|
||||
log.Printf("配置事件系统初始化完成,已注册处理器数量: %v", eventManager.ListRegisteredHandlers())
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AdminBannerHandler 管理端轮播图处理器
|
||||
type AdminBannerHandler struct{}
|
||||
|
||||
// CreateBannerRequest 创建轮播图请求结构体
|
||||
type CreateBannerRequest struct {
|
||||
Title string `json:"title" binding:"required"`
|
||||
ImageUrl string `json:"image_url" binding:"required"`
|
||||
LinkUrl string `json:"link_url"`
|
||||
Description string `json:"description"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
Status uint8 `json:"status"`
|
||||
}
|
||||
|
||||
// UpdateBannerRequest 更新轮播图请求结构体
|
||||
type UpdateBannerRequest struct {
|
||||
Title string `json:"title"`
|
||||
ImageUrl string `json:"image_url"`
|
||||
LinkUrl string `json:"link_url"`
|
||||
Description string `json:"description"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
Status uint8 `json:"status"`
|
||||
}
|
||||
|
||||
// GetBanners 获取轮播图列表
|
||||
func (h *AdminBannerHandler) GetBanners(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以查看轮播图管理
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var banners []models.Banner
|
||||
var total int64
|
||||
|
||||
// 获取总数
|
||||
db.Model(&models.Banner{}).Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := db.Offset(offset).Limit(pageSize).Order("sort_order ASC, created_at DESC").Find(&banners).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询轮播图失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": banners,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetBanner 获取单个轮播图
|
||||
func (h *AdminBannerHandler) GetBanner(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以查看轮播图详情
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
bannerID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "轮播图ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var banner models.Banner
|
||||
if err := common.GetDB().First(&banner, uint(bannerID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "轮播图不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(banner))
|
||||
}
|
||||
|
||||
// CreateBanner 创建轮播图
|
||||
func (h *AdminBannerHandler) CreateBanner(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以创建轮播图
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
var req CreateBannerRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建轮播图对象
|
||||
newBanner := models.Banner{
|
||||
Title: req.Title,
|
||||
ImageUrl: req.ImageUrl,
|
||||
LinkUrl: req.LinkUrl,
|
||||
Description: req.Description,
|
||||
SortOrder: req.SortOrder,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
// 保存轮播图
|
||||
if err := common.GetDB().Create(&newBanner).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建轮播图失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(newBanner))
|
||||
}
|
||||
|
||||
// UpdateBanner 更新轮播图
|
||||
func (h *AdminBannerHandler) UpdateBanner(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以更新轮播图
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
bannerID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "轮播图ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateBannerRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查轮播图是否存在
|
||||
var existBanner models.Banner
|
||||
if err := common.GetDB().First(&existBanner, uint(bannerID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "轮播图不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 准备更新数据
|
||||
updateData := models.Banner{
|
||||
Title: req.Title,
|
||||
ImageUrl: req.ImageUrl,
|
||||
LinkUrl: req.LinkUrl,
|
||||
Description: req.Description,
|
||||
SortOrder: req.SortOrder,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
// 更新轮播图信息
|
||||
updateData.ID = uint(bannerID)
|
||||
if err := common.GetDB().Model(&existBanner).Updates(&updateData).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新轮播图失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 返回更新后的轮播图信息
|
||||
var updatedBanner models.Banner
|
||||
common.GetDB().First(&updatedBanner, uint(bannerID))
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(updatedBanner))
|
||||
}
|
||||
|
||||
// DeleteBanner 删除轮播图
|
||||
func (h *AdminBannerHandler) DeleteBanner(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以删除轮播图
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
bannerID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "轮播图ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查轮播图是否存在
|
||||
var banner models.Banner
|
||||
if err := common.GetDB().First(&banner, uint(bannerID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "轮播图不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 删除轮播图
|
||||
if err := common.GetDB().Delete(&banner).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "删除轮播图失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success("轮播图删除成功"))
|
||||
}
|
||||
@@ -0,0 +1,390 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/config"
|
||||
"awesomeProject/internal/events"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AdminConfigHandler 管理端系统配置处理器
|
||||
type AdminConfigHandler struct{}
|
||||
|
||||
// GetSystemConfigs 获取系统配置列表
|
||||
func (h *AdminConfigHandler) GetSystemConfigs(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以查看系统配置
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
configKey := c.Query("config_key")
|
||||
configType := c.Query("config_type")
|
||||
isSystem := c.Query("is_system")
|
||||
|
||||
db := common.GetDB()
|
||||
var configs []models.SystemConfig
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.SystemConfig{})
|
||||
|
||||
// 添加搜索条件
|
||||
if configKey != "" {
|
||||
query = query.Where("config_key LIKE ?", "%"+configKey+"%")
|
||||
}
|
||||
if configType != "" {
|
||||
query = query.Where("config_type = ?", configType)
|
||||
}
|
||||
if isSystem != "" {
|
||||
query = query.Where("is_system = ?", isSystem)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Order("is_system DESC, created_at DESC").Find(&configs).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询系统配置失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": configs,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetSystemConfig 获取单个系统配置
|
||||
func (h *AdminConfigHandler) GetSystemConfig(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以查看系统配置
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
configID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "配置ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var config models.SystemConfig
|
||||
err = common.GetDB().First(&config, uint(configID)).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "系统配置不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询系统配置失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(config))
|
||||
}
|
||||
|
||||
// CreateSystemConfig 创建系统配置
|
||||
func (h *AdminConfigHandler) CreateSystemConfig(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以创建系统配置
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
var newConfig models.SystemConfig
|
||||
if err := c.ShouldBindJSON(&newConfig); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查配置键是否已存在
|
||||
var existConfig models.SystemConfig
|
||||
if err := common.GetDB().Where("config_key = ?", newConfig.ConfigKey).First(&existConfig).Error; err == nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "配置键已存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
if err := common.GetDB().Create(&newConfig).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建系统配置失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(newConfig))
|
||||
}
|
||||
|
||||
// UpdateSystemConfig 更新系统配置
|
||||
func (h *AdminConfigHandler) UpdateSystemConfig(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以更新系统配置
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
configID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "配置ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var updateData models.SystemConfig
|
||||
if err := c.ShouldBindJSON(&updateData); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查配置是否存在
|
||||
var existConfig models.SystemConfig
|
||||
if err := common.GetDB().First(&existConfig, uint(configID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "系统配置不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是系统配置,不允许修改配置键
|
||||
if existConfig.IsSystem == 1 && updateData.ConfigKey != existConfig.ConfigKey {
|
||||
c.JSON(http.StatusOK, common.Error(400, "系统配置不允许修改配置键"))
|
||||
return
|
||||
}
|
||||
|
||||
// 如果修改了配置键,检查是否已存在
|
||||
if updateData.ConfigKey != existConfig.ConfigKey {
|
||||
var duplicateConfig models.SystemConfig
|
||||
if err := common.GetDB().Where("config_key = ? AND id != ?", updateData.ConfigKey, configID).First(&duplicateConfig).Error; err == nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "配置键已存在"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 记录旧值,用于事件触发
|
||||
oldValue := existConfig.ConfigValue
|
||||
|
||||
// 更新配置信息
|
||||
updateData.ID = uint(configID)
|
||||
if err := common.GetDB().Model(&existConfig).Updates(&updateData).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新系统配置失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 返回更新后的配置信息
|
||||
var updatedConfig models.SystemConfig
|
||||
common.GetDB().First(&updatedConfig, uint(configID))
|
||||
|
||||
// 如果配置值发生变化,触发配置变更事件
|
||||
if oldValue != updatedConfig.ConfigValue {
|
||||
configEvent := config.ConfigEvent{
|
||||
ConfigKey: updatedConfig.ConfigKey,
|
||||
OldValue: oldValue,
|
||||
NewValue: updatedConfig.ConfigValue,
|
||||
ChangeType: "update",
|
||||
ChangedBy: user.ID,
|
||||
ChangedAt: time.Now().Unix(),
|
||||
}
|
||||
|
||||
// 异步触发事件,避免阻塞响应
|
||||
go func() {
|
||||
events.GetConfigEventManager().TriggerEvent(configEvent)
|
||||
}()
|
||||
|
||||
// 记录配置变更日志(可选)
|
||||
go h.logConfigChange(configEvent)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(updatedConfig))
|
||||
}
|
||||
|
||||
// DeleteSystemConfig 删除系统配置
|
||||
func (h *AdminConfigHandler) DeleteSystemConfig(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以删除系统配置
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
configID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "配置ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查配置是否存在
|
||||
var existConfig models.SystemConfig
|
||||
if err := common.GetDB().First(&existConfig, uint(configID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "系统配置不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 系统配置不允许删除
|
||||
if existConfig.IsSystem == 1 {
|
||||
c.JSON(http.StatusOK, common.Error(400, "系统配置不允许删除"))
|
||||
return
|
||||
}
|
||||
|
||||
// 删除配置
|
||||
if err := common.GetDB().Delete(&existConfig).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "删除系统配置失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(nil))
|
||||
}
|
||||
|
||||
// GetConfigByKey 根据配置键获取配置值(公共方法)
|
||||
func (h *AdminConfigHandler) GetConfigByKey(c *gin.Context) {
|
||||
configKey := c.Param("key")
|
||||
if configKey == "" {
|
||||
c.JSON(http.StatusOK, common.Error(400, "配置键不能为空"))
|
||||
return
|
||||
}
|
||||
|
||||
var config models.SystemConfig
|
||||
err := common.GetDB().Where("config_key = ?", configKey).First(&config).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "配置不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询配置失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(config))
|
||||
}
|
||||
|
||||
// InitSystemConfigs 初始化系统配置
|
||||
func (h *AdminConfigHandler) InitSystemConfigs() error {
|
||||
db := common.GetDB()
|
||||
|
||||
// 检查是否已有系统配置
|
||||
var count int64
|
||||
db.Model(&models.SystemConfig{}).Where("is_system = 1").Count(&count)
|
||||
if count > 0 {
|
||||
return nil // 已有系统配置,不重复创建
|
||||
}
|
||||
|
||||
// 创建默认系统配置
|
||||
defaultConfigs := []models.SystemConfig{
|
||||
{
|
||||
ConfigKey: "warehouse_price_rate",
|
||||
ConfigValue: "1.10",
|
||||
ConfigName: "入库价格增值率",
|
||||
ConfigDescription: "商品进入用户仓库时的价格增值比例",
|
||||
ConfigType: "number",
|
||||
IsSystem: 1,
|
||||
},
|
||||
{
|
||||
ConfigKey: "shipping_fee_rate",
|
||||
ConfigValue: "0.05",
|
||||
ConfigName: "运费比例",
|
||||
ConfigDescription: "基于商品价格计算的运费比例",
|
||||
ConfigType: "number",
|
||||
IsSystem: 1,
|
||||
},
|
||||
{
|
||||
ConfigKey: "platform_commission_rate",
|
||||
ConfigValue: "0.03",
|
||||
ConfigName: "平台佣金比例",
|
||||
ConfigDescription: "平台从交易中收取的佣金比例",
|
||||
ConfigType: "number",
|
||||
IsSystem: 1,
|
||||
},
|
||||
{
|
||||
ConfigKey: "min_withdraw_amount",
|
||||
ConfigValue: "100.00",
|
||||
ConfigName: "最小提现金额",
|
||||
ConfigDescription: "用户最小提现金额限制",
|
||||
ConfigType: "number",
|
||||
IsSystem: 1,
|
||||
},
|
||||
{
|
||||
ConfigKey: "max_product_images",
|
||||
ConfigValue: "9",
|
||||
ConfigName: "商品最大图片数量",
|
||||
ConfigDescription: "单个商品最多可上传的图片数量",
|
||||
ConfigType: "number",
|
||||
IsSystem: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, config := range defaultConfigs {
|
||||
if err := db.Create(&config).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// logConfigChange 记录配置变更日志
|
||||
func (h *AdminConfigHandler) logConfigChange(event config.ConfigEvent) {
|
||||
db := common.GetDB()
|
||||
|
||||
// 创建配置变更记录
|
||||
configChange := config.ConfigChange{
|
||||
ConfigKey: event.ConfigKey,
|
||||
OldValue: convertToString(event.OldValue),
|
||||
NewValue: convertToString(event.NewValue),
|
||||
ChangeType: event.ChangeType,
|
||||
ChangedBy: event.ChangedBy,
|
||||
ChangedAt: event.ChangedAt,
|
||||
Extra: map[string]interface{}{
|
||||
"ip": "", // 可以从context中获取
|
||||
"user_agent": "", // 可以从context中获取
|
||||
},
|
||||
}
|
||||
|
||||
// 保存到数据库
|
||||
if err := db.Create(&configChange).Error; err != nil {
|
||||
// 记录日志失败不应该影响主流程,只记录错误日志
|
||||
// common.Logger.Printf("记录配置变更日志失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// convertToString 将interface{}转换为字符串
|
||||
func convertToString(value interface{}) string {
|
||||
if value == nil {
|
||||
return ""
|
||||
}
|
||||
if str, ok := value.(string); ok {
|
||||
return str
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,631 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// AdminOrderHandler 管理端订单处理器
|
||||
type AdminOrderHandler struct{}
|
||||
|
||||
// GetPurchaseOrders 获取买单列表
|
||||
func (h *AdminOrderHandler) GetPurchaseOrders(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
orderNo := c.Query("order_no")
|
||||
orderStatus := c.Query("order_status")
|
||||
productType := c.Query("product_type")
|
||||
sellerName := c.Query("seller_name")
|
||||
buyerName := c.Query("buyer_name")
|
||||
|
||||
db := common.GetDB()
|
||||
var orders []models.PurchaseOrder
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.PurchaseOrder{}).Preload("Buyer").Preload("Seller").Preload("Address")
|
||||
|
||||
// 如果是代理商,只能查看自己邀请的用户的订单
|
||||
if user.SystemRole == 1 {
|
||||
// 子查询:获取代理商邀请的用户ID列表
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
query = query.Where("buyer_id IN (?)", subQuery)
|
||||
}
|
||||
|
||||
// 添加搜索条件
|
||||
if orderNo != "" {
|
||||
query = query.Where("order_no LIKE ?", "%"+orderNo+"%")
|
||||
}
|
||||
if orderStatus != "" {
|
||||
query = query.Where("order_status = ?", orderStatus)
|
||||
}
|
||||
if productType != "" {
|
||||
query = query.Where("product_type = ?", productType)
|
||||
}
|
||||
if sellerName != "" {
|
||||
query = query.Joins("LEFT JOIN users AS sellers ON purchase_orders.seller_id = sellers.id").
|
||||
Where("sellers.real_name LIKE ? OR sellers.phone LIKE ?", "%"+sellerName+"%", "%"+sellerName+"%")
|
||||
}
|
||||
if buyerName != "" {
|
||||
query = query.Joins("LEFT JOIN users AS buyers ON purchase_orders.buyer_id = buyers.id").
|
||||
Where("buyers.real_name LIKE ? OR buyers.phone LIKE ?", "%"+buyerName+"%", "%"+buyerName+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&orders).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询买单列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": orders,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetSalesOrders 获取卖单列表
|
||||
func (h *AdminOrderHandler) GetSalesOrders(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
orderNo := c.Query("order_no")
|
||||
orderStatus := c.Query("order_status")
|
||||
sellerName := c.Query("seller_name")
|
||||
buyerName := c.Query("buyer_name")
|
||||
|
||||
db := common.GetDB()
|
||||
var orders []models.SalesOrder
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.SalesOrder{}).Preload("Seller").Preload("Buyer").Preload("SecondaryProduct").Preload("Address")
|
||||
|
||||
// 如果是代理商,只能查看自己邀请的用户相关的订单
|
||||
if user.SystemRole == 1 {
|
||||
// 子查询:获取代理商邀请的用户ID列表
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
query = query.Where("buyer_id IN (?) OR seller_id IN (?)", subQuery, subQuery)
|
||||
}
|
||||
|
||||
// 添加搜索条件
|
||||
if orderNo != "" {
|
||||
query = query.Where("order_no LIKE ?", "%"+orderNo+"%")
|
||||
}
|
||||
if orderStatus != "" {
|
||||
query = query.Where("order_status = ?", orderStatus)
|
||||
}
|
||||
if sellerName != "" {
|
||||
query = query.Joins("LEFT JOIN users AS sellers ON sales_orders.seller_id = sellers.id").
|
||||
Where("sellers.real_name LIKE ? OR sellers.phone LIKE ?", "%"+sellerName+"%", "%"+sellerName+"%")
|
||||
}
|
||||
if buyerName != "" {
|
||||
query = query.Joins("LEFT JOIN users AS buyers ON sales_orders.buyer_id = buyers.id").
|
||||
Where("buyers.real_name LIKE ? OR buyers.phone LIKE ?", "%"+buyerName+"%", "%"+buyerName+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&orders).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询卖单列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": orders,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetPurchaseOrder 获取单个买单详情
|
||||
func (h *AdminOrderHandler) GetPurchaseOrder(c *gin.Context) {
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
var order models.PurchaseOrder
|
||||
query := common.GetDB().Preload("Buyer").Preload("Seller").Preload("Address")
|
||||
|
||||
err = query.First(&order, uint(orderID)).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询订单详情失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是代理商,检查权限
|
||||
if user.SystemRole == 1 {
|
||||
// 检查买家是否为代理商邀请的用户
|
||||
if order.Buyer.ReferrerIdentityCode != user.IdentityCode {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(order))
|
||||
}
|
||||
|
||||
// GetSalesOrder 获取单个卖单详情
|
||||
func (h *AdminOrderHandler) GetSalesOrder(c *gin.Context) {
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
var order models.SalesOrder
|
||||
query := common.GetDB().Preload("Seller").Preload("Buyer").Preload("SecondaryProduct").Preload("Address")
|
||||
|
||||
err = query.First(&order, uint(orderID)).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询订单详情失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是代理商,检查权限
|
||||
if user.SystemRole == 1 {
|
||||
// 检查买家或卖家是否为代理商邀请的用户
|
||||
if order.Buyer.ReferrerIdentityCode != user.IdentityCode && order.Seller.ReferrerIdentityCode != user.IdentityCode {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(order))
|
||||
}
|
||||
|
||||
// UpdatePurchaseOrderStatus 更新买单状态(仅管理员)
|
||||
func (h *AdminOrderHandler) UpdatePurchaseOrderStatus(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以更新订单状态
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var updateData struct {
|
||||
OrderStatus uint8 `json:"order_status" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&updateData); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
tx := db.Begin()
|
||||
|
||||
// 检查订单是否存在
|
||||
var order models.PurchaseOrder
|
||||
if err := tx.First(&order, uint(orderID)).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
updateFields := map[string]interface{}{
|
||||
"order_status": updateData.OrderStatus,
|
||||
"remark": updateData.Remark,
|
||||
}
|
||||
|
||||
// 如果状态为已完成,设置确认时间并将商品入库
|
||||
if updateData.OrderStatus == 2 {
|
||||
now := time.Now()
|
||||
updateFields["confirm_time"] = &now
|
||||
|
||||
// 商品入库到买家仓库
|
||||
if err := h.addToWarehouse(tx, order.BuyerID, &order); err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "商品入库失败"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Model(&order).Updates(updateFields).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新订单状态失败"))
|
||||
return
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
result := map[string]interface{}{
|
||||
"message": "订单状态更新成功",
|
||||
}
|
||||
if updateData.OrderStatus == 2 {
|
||||
result["message"] = "订单确认成功,商品已入库"
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// UpdateSalesOrderStatus 更新卖单状态(仅管理员)
|
||||
func (h *AdminOrderHandler) UpdateSalesOrderStatus(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以更新订单状态
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var updateData struct {
|
||||
OrderStatus uint8 `json:"order_status" binding:"required"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&updateData); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查订单是否存在
|
||||
var order models.SalesOrder
|
||||
if err := common.GetDB().First(&order, uint(orderID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新订单状态
|
||||
updateFields := map[string]interface{}{
|
||||
"order_status": updateData.OrderStatus,
|
||||
"remark": updateData.Remark,
|
||||
}
|
||||
|
||||
// 如果状态为确认完成,设置完成时间
|
||||
if updateData.OrderStatus == 2 {
|
||||
updateFields["complete_time"] = time.Now()
|
||||
db := common.GetDB()
|
||||
var purcharseOrder models.PurchaseOrder
|
||||
db.Where("id=?", order.PurchaseOrderID).First(&purcharseOrder)
|
||||
err := h.addToWarehouse(db, purcharseOrder.BuyerID, &purcharseOrder)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "确认失败"))
|
||||
}
|
||||
}
|
||||
if updateData.OrderStatus == 3 {
|
||||
canel(order.PurchaseOrderID, order.BuyerID)
|
||||
}
|
||||
if err := common.GetDB().Model(&order).Updates(updateFields).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新订单状态失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(nil))
|
||||
}
|
||||
|
||||
// GetOrderStats 获取订单统计信息
|
||||
func (h *AdminOrderHandler) GetOrderStats(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
db := common.GetDB()
|
||||
stats := make(map[string]interface{})
|
||||
|
||||
// 买单统计
|
||||
purchaseQuery := db.Model(&models.PurchaseOrder{}).Where("order_status = ?", 2)
|
||||
|
||||
if user.SystemRole == 1 {
|
||||
// 代理商只统计自己邀请的用户的订单
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
purchaseQuery = purchaseQuery.Where("buyer_id IN (?)", subQuery)
|
||||
}
|
||||
|
||||
var totalPurchaseOrders int64
|
||||
var totalPurchaseAmount float64
|
||||
purchaseQuery.Count(&totalPurchaseOrders)
|
||||
purchaseQuery.Select("COALESCE(SUM(total_amount), 0)").Scan(&totalPurchaseAmount)
|
||||
|
||||
stats["total_purchase_orders"] = totalPurchaseOrders
|
||||
stats["total_purchase_amount"] = totalPurchaseAmount
|
||||
|
||||
// 卖单统计
|
||||
salesQuery := db.Model(&models.SalesOrder{})
|
||||
if user.SystemRole == 1 {
|
||||
// 代理商只统计自己邀请的用户相关的订单
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
salesQuery = salesQuery.Where("buyer_id IN (?) OR seller_id IN (?)", subQuery, subQuery)
|
||||
}
|
||||
|
||||
var totalSalesOrders int64
|
||||
var totalSalesAmount float64
|
||||
salesQuery.Count(&totalSalesOrders)
|
||||
salesQuery.Select("COALESCE(SUM(total_amount), 0)").Scan(&totalSalesAmount)
|
||||
|
||||
stats["total_sales_orders"] = totalSalesOrders
|
||||
stats["total_sales_amount"] = totalSalesAmount
|
||||
|
||||
// 今日订单统计
|
||||
todayPurchaseQuery := purchaseQuery
|
||||
if user.SystemRole == 1 {
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
todayPurchaseQuery = todayPurchaseQuery.Where("buyer_id IN (?)", subQuery)
|
||||
}
|
||||
var todayPurchaseOrders int64
|
||||
todayPurchaseQuery.Where("DATE(created_at) = CURDATE()").Count(&todayPurchaseOrders)
|
||||
|
||||
todaySalesQuery := salesQuery
|
||||
if user.SystemRole == 1 {
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
todaySalesQuery = todaySalesQuery.Where("buyer_id IN (?) OR seller_id IN (?)", subQuery, subQuery)
|
||||
}
|
||||
var todaySalesOrders int64
|
||||
todaySalesQuery.Where("DATE(created_at) = CURDATE()").Count(&todaySalesOrders)
|
||||
|
||||
stats["today_purchase_orders"] = todayPurchaseOrders
|
||||
stats["today_sales_orders"] = todaySalesOrders
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(stats))
|
||||
}
|
||||
|
||||
func canel(OrderID, UserID uint) {
|
||||
db := common.GetDB()
|
||||
tx := db.Begin()
|
||||
|
||||
// 查询订单
|
||||
var order models.PurchaseOrder
|
||||
if err := tx.Where("id = ? AND buyer_id = ?", OrderID, UserID).First(&order).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return
|
||||
}
|
||||
|
||||
// 检查订单状态(只有待付款和待确认的订单可以取消)
|
||||
if order.OrderStatus != 0 && order.OrderStatus != 1 {
|
||||
tx.Rollback()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 更新订单状态为已取消
|
||||
if err := tx.Model(&order).Update("order_status", 3).Error; err != nil {
|
||||
tx.Rollback()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// 恢复商品库存
|
||||
if order.ProductType == 1 { // 一级商品
|
||||
if err := tx.Model(&models.PrimaryProduct{}).Where("id = ?", order.ProductID).
|
||||
Update("stock_quantity", gorm.Expr("stock_quantity + ?", order.Quantity)).Error; err != nil {
|
||||
tx.Rollback()
|
||||
|
||||
return
|
||||
}
|
||||
} else { // 二级商品
|
||||
if err := tx.Model(&models.SecondaryProduct{}).Where("id = ?", order.ProductID).
|
||||
Update("quantity", gorm.Expr("quantity + ?", order.Quantity)).Error; err != nil {
|
||||
tx.Rollback()
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
}
|
||||
|
||||
// addToWarehouse 添加商品到买家仓库
|
||||
func (h *AdminOrderHandler) addToWarehouse(tx *gorm.DB, buyerID uint, order *models.PurchaseOrder) error {
|
||||
|
||||
var config models.SystemConfig
|
||||
// 计算入库价格(根据系统配置增值)
|
||||
|
||||
// 检查该订单是否已经入库(防止重复入库)
|
||||
var existingByOrder models.UserWarehouse
|
||||
err := tx.Where("user_id = ? AND source_order_id = ? and product_type=? and product_id=?",
|
||||
buyerID, order.ID, order.ProductType, order.ProductID).First(&existingByOrder).Error
|
||||
|
||||
if err == nil {
|
||||
// 该订单已经入库,直接返回
|
||||
return nil
|
||||
} else if err != gorm.ErrRecordNotFound {
|
||||
// 查询出错
|
||||
return err
|
||||
}
|
||||
|
||||
// 检查仓库中是否已有相同商品(不同订单的相同商品)
|
||||
var existingWarehouse models.UserWarehouse
|
||||
//err = tx.Where("user_id = ? AND product_id = ? AND product_type = ?",
|
||||
// buyerID, order.ProductID, order.ProductType).First(&existingWarehouse).Error
|
||||
|
||||
_ = tx.Where("config_key= ?", "warehouse_price_rate").First(&config).Error
|
||||
value, _ := strconv.ParseFloat(config.ConfigValue, 64)
|
||||
warehousePrice := order.UnitPrice + value
|
||||
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
// 创建新的仓库记录
|
||||
warehouse := models.UserWarehouse{
|
||||
UserID: buyerID,
|
||||
ProductID: order.ProductID,
|
||||
ProductType: order.ProductType,
|
||||
ProductName: order.ProductName,
|
||||
ProductImages: order.ProductImages,
|
||||
PurchasePrice: order.UnitPrice,
|
||||
WarehousePrice: warehousePrice,
|
||||
Quantity: order.Quantity,
|
||||
SourceOrderID: &order.ID,
|
||||
Status: 1, // 库存中
|
||||
}
|
||||
|
||||
return tx.Create(&warehouse).Error
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
// 更新现有仓库记录的数量(累加不同订单的相同商品)
|
||||
return tx.Model(&existingWarehouse).Update("quantity", existingWarehouse.Quantity+order.Quantity).Error
|
||||
}
|
||||
}
|
||||
|
||||
func (h *AdminOrderHandler) GetOrderTrend(c *gin.Context) {
|
||||
db := common.GetDB()
|
||||
|
||||
type DailyStats struct {
|
||||
Date string `json:"date"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
result := make([]DailyStats, 0, 7)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
for i := 6; i >= 0; i-- {
|
||||
// 当天 00:00:00
|
||||
startOfDay := time.Date(now.Year(), now.Month(), now.Day()-i, 0, 0, 0, 0, now.Location())
|
||||
// 次日 00:00:00
|
||||
endOfDay := startOfDay.AddDate(0, 0, 1)
|
||||
|
||||
// 格式化显示用的 "01-02"
|
||||
displayDateStr := startOfDay.Format("01-02")
|
||||
|
||||
var count int64
|
||||
db.Model(&models.PurchaseOrder{}).
|
||||
Where("created_at >= ? AND created_at < ?", startOfDay, endOfDay).
|
||||
Count(&count)
|
||||
|
||||
result = append(result, DailyStats{
|
||||
Date: displayDateStr,
|
||||
Count: int(count),
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(map[string]interface{}{
|
||||
"trend": result,
|
||||
}))
|
||||
}
|
||||
|
||||
// GetRecentActivities 获取最近活动
|
||||
func (h *AdminOrderHandler) GetRecentActivities(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
db := common.GetDB()
|
||||
|
||||
// 准备返回数据结构
|
||||
type Activity struct {
|
||||
ID uint `json:"id"`
|
||||
Description string `json:"description"`
|
||||
Time time.Time `json:"time"`
|
||||
}
|
||||
|
||||
var activities []Activity
|
||||
|
||||
// 获取最近的订单活动(最多10条)
|
||||
var recentOrders []models.PurchaseOrder
|
||||
orderQuery := db.Model(&models.PurchaseOrder{}).Preload("Buyer")
|
||||
|
||||
if user.SystemRole == 1 {
|
||||
// 代理商只能看到自己邀请的用户的活动
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
orderQuery = orderQuery.Where("buyer_id IN (?)", subQuery)
|
||||
}
|
||||
|
||||
err := orderQuery.Order("created_at DESC").Limit(10).Find(&recentOrders).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "获取最近活动失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 将订单转换为活动
|
||||
for _, order := range recentOrders {
|
||||
var description string
|
||||
switch order.OrderStatus {
|
||||
case 0:
|
||||
description = fmt.Sprintf("用户 %s 创建了新订单 %s", order.Buyer.Phone, order.OrderNo)
|
||||
case 1:
|
||||
description = fmt.Sprintf("用户 %s 提交了订单 %s 的付款凭证", order.Buyer.Phone, order.OrderNo)
|
||||
case 2:
|
||||
description = fmt.Sprintf("订单 %s 已完成", order.OrderNo)
|
||||
case 3:
|
||||
description = fmt.Sprintf("订单 %s 已取消", order.OrderNo)
|
||||
}
|
||||
|
||||
activities = append(activities, Activity{
|
||||
ID: order.ID,
|
||||
Description: description,
|
||||
Time: order.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(map[string]interface{}{
|
||||
"activities": activities,
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AdminOrderMessageHandler 管理端订单留言处理器
|
||||
type AdminOrderMessageHandler struct{}
|
||||
|
||||
// GetOrderMessages 获取订单留言列表
|
||||
func (h *AdminOrderMessageHandler) GetOrderMessages(c *gin.Context) {
|
||||
orderID, err := strconv.ParseUint(c.Param("order_id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
orderType := c.Query("type") // purchase 或 sales
|
||||
if orderType == "" {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单类型无效"))
|
||||
return
|
||||
}
|
||||
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 验证管理员权限
|
||||
if user.SystemRole != 0 && user.SystemRole != 1 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 50 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var messages []models.OrderMessage
|
||||
var total int64
|
||||
|
||||
// 如果是买单,需要同时查询买单和对应卖单的留言
|
||||
var query *gorm.DB
|
||||
if orderType == "purchase" {
|
||||
// 查找是否有对应的卖单
|
||||
var salesOrder models.SalesOrder
|
||||
err := db.Where("purchase_order_id = ?", orderID).First(&salesOrder).Error
|
||||
if err == nil {
|
||||
// 有对应卖单,查询两个订单的留言
|
||||
query = db.Model(&models.OrderMessage{}).
|
||||
Where("(order_id = ? AND order_type = ?) OR (order_id = ? AND order_type = ?)",
|
||||
orderID, "purchase", salesOrder.ID, "sales")
|
||||
} else {
|
||||
// 没有对应卖单,只查询买单留言
|
||||
query = db.Model(&models.OrderMessage{}).
|
||||
Where("order_id = ? AND order_type = ?", orderID, "purchase")
|
||||
}
|
||||
} else {
|
||||
// 卖单,查询卖单和对应买单的留言
|
||||
var salesOrder models.SalesOrder
|
||||
err := db.Where("id = ?", orderID).First(&salesOrder).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
query = db.Model(&models.OrderMessage{}).
|
||||
Where("(order_id = ? AND order_type = ?) OR (order_id = ? AND order_type = ?)",
|
||||
salesOrder.ID, "sales", salesOrder.PurchaseOrderID, "purchase")
|
||||
}
|
||||
|
||||
query = query.Preload("User", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Select("id, customer_name, real_name, phone, system_role")
|
||||
})
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = query.Order("created_at ASC").Offset(offset).Limit(pageSize).Find(&messages).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询留言列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": messages,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// CreateOrderMessage 创建订单留言
|
||||
func (h *AdminOrderMessageHandler) CreateOrderMessage(c *gin.Context) {
|
||||
orderID, err := strconv.ParseUint(c.Param("order_id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
orderType := c.Query("type") // purchase 或 sales
|
||||
if orderType == "" {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单类型无效"))
|
||||
return
|
||||
}
|
||||
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 验证管理员权限
|
||||
if user.SystemRole != 0 && user.SystemRole != 1 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Content string `json:"content" binding:"required"`
|
||||
Images string `json:"images"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证留言内容
|
||||
if len(req.Content) > 500 {
|
||||
c.JSON(http.StatusOK, common.Error(400, "留言内容不能超过500字"))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证订单是否存在
|
||||
db := common.GetDB()
|
||||
if orderType == "purchase" {
|
||||
var order models.PurchaseOrder
|
||||
if err := db.First(&order, uint(orderID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
var order models.SalesOrder
|
||||
if err := db.First(&order, uint(orderID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 创建留言记录
|
||||
message := models.OrderMessage{
|
||||
OrderID: uint(orderID),
|
||||
OrderType: orderType,
|
||||
UserID: user.ID,
|
||||
Content: req.Content,
|
||||
Images: req.Images,
|
||||
}
|
||||
|
||||
if err := db.Create(&message).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建留言失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 预加载用户信息
|
||||
db.Preload("User", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Select("id, customer_name, real_name, phone, system_role")
|
||||
}).First(&message, message.ID)
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(message))
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AdminPaymentHandler 管理员付款方式处理器
|
||||
type AdminPaymentHandler struct{}
|
||||
|
||||
// GetPaymentInfo 获取管理员付款方式信息
|
||||
func (h *AdminPaymentHandler) GetPaymentInfo(c *gin.Context) {
|
||||
db := common.GetDB()
|
||||
|
||||
// 查询系统角色为管理员的用户的付款信息
|
||||
var admin models.User
|
||||
err := db.Where("system_role = ?", 0).Select("main_payment_qr_image, sub_payment_qr_image").First(&admin).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询管理员付款信息失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 返回管理员付款方式信息
|
||||
paymentInfo := map[string]interface{}{
|
||||
"main_payment_qr_image": admin.MainPaymentQrImage,
|
||||
"sub_payment_qr_image": admin.SubPaymentQrImage,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(paymentInfo))
|
||||
}
|
||||
|
||||
// UpdatePaymentInfo 更新管理员付款方式信息
|
||||
func (h *AdminPaymentHandler) UpdatePaymentInfo(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 验证是否为管理员
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 解析请求数据
|
||||
var req struct {
|
||||
MainPaymentQrImage string `json:"main_payment_qr_image"`
|
||||
SubPaymentQrImage string `json:"sub_payment_qr_image"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新管理员付款方式信息
|
||||
updates := map[string]interface{}{}
|
||||
if req.MainPaymentQrImage != "" {
|
||||
updates["main_payment_qr_image"] = req.MainPaymentQrImage
|
||||
}
|
||||
if req.SubPaymentQrImage != "" {
|
||||
updates["sub_payment_qr_image"] = req.SubPaymentQrImage
|
||||
}
|
||||
|
||||
if len(updates) == 0 {
|
||||
c.JSON(http.StatusOK, common.Error(400, "没有要更新的信息"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := common.GetDB().Model(&user).Updates(updates).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新付款方式失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 返回更新后的信息
|
||||
var updatedUser models.User
|
||||
common.GetDB().First(&updatedUser, user.ID)
|
||||
|
||||
paymentInfo := map[string]interface{}{
|
||||
"main_payment_qr_image": updatedUser.MainPaymentQrImage,
|
||||
"sub_payment_qr_image": updatedUser.SubPaymentQrImage,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(paymentInfo))
|
||||
}
|
||||
@@ -0,0 +1,511 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AdminProductHandler 管理端商品处理器
|
||||
type AdminProductHandler struct{}
|
||||
|
||||
// CreateProductCategoryRequest 创建商品分类请求结构体
|
||||
type CreateProductCategoryRequest struct {
|
||||
CategoryName string `json:"category_name" binding:"required"`
|
||||
CategoryIcon string `json:"category_icon"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
Status uint8 `json:"status"`
|
||||
}
|
||||
|
||||
// UpdateProductCategoryRequest 更新商品分类请求结构体
|
||||
type UpdateProductCategoryRequest struct {
|
||||
CategoryName string `json:"category_name"`
|
||||
CategoryIcon string `json:"category_icon"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
Status uint8 `json:"status"`
|
||||
}
|
||||
|
||||
// GetProductCategories 获取商品分类列表
|
||||
func (h *AdminProductHandler) GetProductCategories(c *gin.Context) {
|
||||
var categories []models.ProductCategory
|
||||
|
||||
err := common.GetDB().Order("sort_order ASC, created_at DESC").Find(&categories).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询商品分类失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(categories))
|
||||
}
|
||||
|
||||
// CreateProductCategory 创建商品分类(仅管理员)
|
||||
func (h *AdminProductHandler) CreateProductCategory(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以创建分类
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
var req CreateProductCategoryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建分类对象
|
||||
newCategory := models.ProductCategory{
|
||||
CategoryName: req.CategoryName,
|
||||
CategoryIcon: req.CategoryIcon,
|
||||
SortOrder: req.SortOrder,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
// 保存分类
|
||||
if err := common.GetDB().Create(&newCategory).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建商品分类失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(newCategory))
|
||||
}
|
||||
|
||||
// UpdateProductCategory 更新商品分类(仅管理员)
|
||||
func (h *AdminProductHandler) UpdateProductCategory(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以更新分类
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
categoryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "分类ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdateProductCategoryRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查分类是否存在
|
||||
var existCategory models.ProductCategory
|
||||
if err := common.GetDB().First(&existCategory, uint(categoryID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品分类不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 准备更新数据
|
||||
updateData := models.ProductCategory{
|
||||
CategoryName: req.CategoryName,
|
||||
CategoryIcon: req.CategoryIcon,
|
||||
SortOrder: req.SortOrder,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
// 更新分类信息
|
||||
updateData.ID = uint(categoryID)
|
||||
if err := common.GetDB().Model(&existCategory).Updates(&updateData).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新商品分类失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 返回更新后的分类信息
|
||||
var updatedCategory models.ProductCategory
|
||||
common.GetDB().First(&updatedCategory, uint(categoryID))
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(updatedCategory))
|
||||
}
|
||||
|
||||
// DeleteProductCategory 删除商品分类(仅管理员)
|
||||
func (h *AdminProductHandler) DeleteProductCategory(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以删除分类
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
categoryID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "分类ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查分类下是否有商品
|
||||
var productCount int64
|
||||
common.GetDB().Model(&models.PrimaryProduct{}).Where("category_id = ?", categoryID).Count(&productCount)
|
||||
if productCount > 0 {
|
||||
c.JSON(http.StatusOK, common.Error(400, "该分类下还有商品,无法删除"))
|
||||
return
|
||||
}
|
||||
|
||||
// 删除分类
|
||||
if err := common.GetDB().Delete(&models.ProductCategory{}, uint(categoryID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "删除商品分类失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(nil))
|
||||
}
|
||||
|
||||
// GetPrimaryProducts 获取一级商品列表
|
||||
func (h *AdminProductHandler) GetPrimaryProducts(c *gin.Context) {
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
categoryID := c.Query("category_id")
|
||||
productName := c.Query("product_name")
|
||||
status := c.Query("status")
|
||||
|
||||
db := common.GetDB()
|
||||
var products []models.PrimaryProduct
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.PrimaryProduct{}).Preload("Category")
|
||||
|
||||
// 添加搜索条件
|
||||
if categoryID != "" {
|
||||
query = query.Where("category_id = ?", categoryID)
|
||||
}
|
||||
if productName != "" {
|
||||
query = query.Where("product_name LIKE ?", "%"+productName+"%")
|
||||
}
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Order("sort_order ASC, created_at DESC").Find(&products).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询商品列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": products,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetPrimaryProduct 获取单个一级商品详情
|
||||
func (h *AdminProductHandler) GetPrimaryProduct(c *gin.Context) {
|
||||
productID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var product models.PrimaryProduct
|
||||
err = common.GetDB().Preload("Category").First(&product, uint(productID)).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询商品详情失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(product))
|
||||
}
|
||||
|
||||
// CreatePrimaryProductRequest 创建一级商品请求结构体
|
||||
type CreatePrimaryProductRequest struct {
|
||||
CategoryID uint `json:"category_id" binding:"required"`
|
||||
ProductName string `json:"product_name" binding:"required"`
|
||||
ProductDescription string `json:"product_description"`
|
||||
ProductImages string `json:"product_images"`
|
||||
OriginalPrice float64 `json:"original_price" binding:"required"`
|
||||
CurrentPrice float64 `json:"current_price" binding:"required"`
|
||||
StockQuantity int `json:"stock_quantity"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
Status uint8 `json:"status"`
|
||||
IsHot int8 `json:"is_hot"`
|
||||
}
|
||||
|
||||
// CreatePrimaryProduct 创建一级商品(仅管理员)
|
||||
func (h *AdminProductHandler) CreatePrimaryProduct(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以创建商品
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
var req CreatePrimaryProductRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查分类是否存在
|
||||
var category models.ProductCategory
|
||||
if err := common.GetDB().First(&category, req.CategoryID).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品分类不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建商品对象
|
||||
newProduct := models.PrimaryProduct{
|
||||
CategoryID: req.CategoryID,
|
||||
ProductName: req.ProductName,
|
||||
ProductDescription: req.ProductDescription,
|
||||
ProductImages: req.ProductImages,
|
||||
OriginalPrice: req.OriginalPrice,
|
||||
CurrentPrice: req.CurrentPrice,
|
||||
StockQuantity: req.StockQuantity,
|
||||
SortOrder: req.SortOrder,
|
||||
Status: req.Status,
|
||||
IsHot: req.IsHot,
|
||||
}
|
||||
|
||||
// 保存商品
|
||||
if err := common.GetDB().Create(&newProduct).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建商品失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 返回创建的商品(包含分类信息)
|
||||
var createdProduct models.PrimaryProduct
|
||||
common.GetDB().Preload("Category").First(&createdProduct, newProduct.ID)
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(createdProduct))
|
||||
}
|
||||
|
||||
// UpdatePrimaryProductRequest 更新一级商品请求结构体
|
||||
type UpdatePrimaryProductRequest struct {
|
||||
CategoryID uint `json:"category_id"`
|
||||
ProductName string `json:"product_name"`
|
||||
ProductDescription string `json:"product_description"`
|
||||
ProductImages string `json:"product_images"`
|
||||
OriginalPrice float64 `json:"original_price"`
|
||||
CurrentPrice float64 `json:"current_price"`
|
||||
StockQuantity int `json:"stock_quantity"`
|
||||
SortOrder int `json:"sort_order"`
|
||||
Status uint8 `json:"status"`
|
||||
IsHot int8 `json:"is_hot"`
|
||||
}
|
||||
|
||||
// UpdatePrimaryProduct 更新一级商品(仅管理员)
|
||||
func (h *AdminProductHandler) UpdatePrimaryProduct(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以更新商品
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
productID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var req UpdatePrimaryProductRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查商品是否存在
|
||||
var existProduct models.PrimaryProduct
|
||||
if err := common.GetDB().First(&existProduct, uint(productID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 如果更新了分类,检查分类是否存在
|
||||
if req.CategoryID != 0 {
|
||||
var category models.ProductCategory
|
||||
if err := common.GetDB().First(&category, req.CategoryID).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品分类不存在"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 准备更新数据
|
||||
updateData := models.PrimaryProduct{
|
||||
CategoryID: req.CategoryID,
|
||||
ProductName: req.ProductName,
|
||||
ProductDescription: req.ProductDescription,
|
||||
ProductImages: req.ProductImages,
|
||||
OriginalPrice: req.OriginalPrice,
|
||||
CurrentPrice: req.CurrentPrice,
|
||||
StockQuantity: req.StockQuantity,
|
||||
SortOrder: req.SortOrder,
|
||||
Status: req.Status,
|
||||
IsHot: req.IsHot,
|
||||
}
|
||||
fmt.Println(updateData.IsHot)
|
||||
// 更新商品信息
|
||||
updateData.ID = uint(productID)
|
||||
if err := common.GetDB().Model(&existProduct).Updates(&updateData).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新商品失败"))
|
||||
return
|
||||
}
|
||||
common.GetDB().Model(&existProduct).Select("status", "is_hot").
|
||||
Updates(models.PrimaryProduct{IsHot: req.IsHot, Status: req.Status})
|
||||
// 返回更新后的商品信息
|
||||
var updatedProduct models.PrimaryProduct
|
||||
common.GetDB().Preload("Category").First(&updatedProduct, uint(productID))
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(updatedProduct))
|
||||
}
|
||||
|
||||
// DeletePrimaryProduct 删除一级商品(仅管理员)
|
||||
func (h *AdminProductHandler) DeletePrimaryProduct(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以删除商品
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
productID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查商品是否存在
|
||||
var existProduct models.PrimaryProduct
|
||||
if err := common.GetDB().First(&existProduct, uint(productID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 删除商品
|
||||
if err := common.GetDB().Delete(&existProduct).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "删除商品失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(nil))
|
||||
}
|
||||
|
||||
// GetSecondaryProducts 获取二级商品列表
|
||||
func (h *AdminProductHandler) GetSecondaryProducts(c *gin.Context) {
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
sellerID := c.Query("seller_id")
|
||||
productName := c.Query("product_name")
|
||||
status := c.Query("status")
|
||||
|
||||
db := common.GetDB()
|
||||
var products []models.SecondaryProduct
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.SecondaryProduct{}).Preload("Seller")
|
||||
|
||||
// 添加搜索条件
|
||||
if sellerID != "" {
|
||||
query = query.Where("seller_id = ?", sellerID)
|
||||
}
|
||||
if productName != "" {
|
||||
query = query.Where("product_name LIKE ?", "%"+productName+"%")
|
||||
}
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&products).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询二级商品列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": products,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetProductStats 获取商品统计信息
|
||||
func (h *AdminProductHandler) GetProductStats(c *gin.Context) {
|
||||
db := common.GetDB()
|
||||
stats := make(map[string]interface{})
|
||||
|
||||
// 一级商品统计
|
||||
var totalPrimaryProducts int64
|
||||
var onSalePrimaryProducts int64
|
||||
db.Model(&models.PrimaryProduct{}).Count(&totalPrimaryProducts)
|
||||
db.Model(&models.PrimaryProduct{}).Where("status = 1").Count(&onSalePrimaryProducts)
|
||||
|
||||
stats["total_primary_products"] = totalPrimaryProducts
|
||||
stats["on_sale_primary_products"] = onSalePrimaryProducts
|
||||
|
||||
// 二级商品统计
|
||||
var totalSecondaryProducts int64
|
||||
var onSaleSecondaryProducts int64
|
||||
db.Model(&models.SecondaryProduct{}).Count(&totalSecondaryProducts)
|
||||
db.Model(&models.SecondaryProduct{}).Where("status = 1").Count(&onSaleSecondaryProducts)
|
||||
|
||||
stats["total_secondary_products"] = totalSecondaryProducts
|
||||
stats["on_sale_secondary_products"] = onSaleSecondaryProducts
|
||||
|
||||
// 商品分类数量
|
||||
var totalCategories int64
|
||||
db.Model(&models.ProductCategory{}).Count(&totalCategories)
|
||||
stats["total_categories"] = totalCategories
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(stats))
|
||||
}
|
||||
@@ -0,0 +1,368 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ScoreRecordCreateRequest 用于接收创建积分记录的请求
|
||||
type ScoreRecordCreateRequest struct {
|
||||
UserID uint `json:"user_id" binding:"required"`
|
||||
ChangeNumber int `json:"change_number" binding:"required"`
|
||||
Note string `json:"note" binding:"required"`
|
||||
}
|
||||
|
||||
// AdminScoreHandler 管理端积分处理器
|
||||
type AdminScoreHandler struct{}
|
||||
|
||||
// GetScoreRecords 获取积分记录列表
|
||||
func (h *AdminScoreHandler) GetScoreRecords(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
userID := c.Query("user_id")
|
||||
phone := c.Query("phone")
|
||||
changeType := c.Query("change_type") // positive: 正数, negative: 负数
|
||||
|
||||
db := common.GetDB()
|
||||
var records []models.ScoreRecord
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.ScoreRecord{}).Preload("User")
|
||||
|
||||
// 如果是代理商,只能查看自己邀请的用户的积分记录
|
||||
if user.SystemRole == 1 {
|
||||
// 子查询:获取代理商邀请的用户ID列表
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
query = query.Where("user_id IN (?)", subQuery)
|
||||
}
|
||||
|
||||
// 添加搜索条件
|
||||
if userID != "" {
|
||||
query = query.Where("user_id = ?", userID)
|
||||
}
|
||||
if phone != "" {
|
||||
// 通过关联用户表搜索手机号
|
||||
query = query.Joins("JOIN users ON users.id = score_records.user_id").Where("users.phone LIKE ?", "%"+phone+"%")
|
||||
}
|
||||
if changeType == "positive" {
|
||||
query = query.Where("change_number > 0")
|
||||
} else if changeType == "negative" {
|
||||
query = query.Where("change_number < 0")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&records).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询积分记录失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": records,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetScoreRecord 获取单个积分记录详情
|
||||
func (h *AdminScoreHandler) GetScoreRecord(c *gin.Context) {
|
||||
recordID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "记录ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
var record models.ScoreRecord
|
||||
query := common.GetDB().Preload("User")
|
||||
|
||||
err = query.First(&record, uint(recordID)).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "积分记录不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询积分记录失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是代理商,检查权限
|
||||
if user.SystemRole == 1 {
|
||||
// 检查用户是否为代理商邀请的用户
|
||||
if record.User.ReferrerIdentityCode != user.IdentityCode {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(record))
|
||||
}
|
||||
|
||||
// CreateScoreRecord 创建积分记录(仅管理员)
|
||||
func (h *AdminScoreHandler) CreateScoreRecord(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以创建积分记录
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
var newRecord1 ScoreRecordCreateRequest
|
||||
if err := c.ShouldBindJSON(&newRecord1); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户是否存在
|
||||
var targetUser models.User
|
||||
if err := common.GetDB().First(&targetUser, newRecord1.UserID).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "用户不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
newRecord := models.ScoreRecord{
|
||||
UserID: newRecord1.UserID,
|
||||
ChangeNumber: newRecord1.ChangeNumber,
|
||||
Note: newRecord1.Note,
|
||||
}
|
||||
// 开启事务
|
||||
tx := common.GetDB().Begin()
|
||||
|
||||
// 创建积分记录
|
||||
if err := tx.Create(&newRecord).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建积分记录失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新用户积分
|
||||
newPoints := targetUser.CurrentPoints + newRecord.ChangeNumber
|
||||
if newPoints < 0 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "用户积分不足"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Model(&targetUser).Update("current_points", newPoints).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新用户积分失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
tx.Commit()
|
||||
|
||||
// 返回创建的记录(包含用户信息)
|
||||
var createdRecord models.ScoreRecord
|
||||
common.GetDB().Preload("User").First(&createdRecord, newRecord.ID)
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(createdRecord))
|
||||
}
|
||||
|
||||
// UpdateScoreRecord 更新积分记录(仅管理员)
|
||||
func (h *AdminScoreHandler) UpdateScoreRecord(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以更新积分记录
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
recordID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "记录ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var updateData struct {
|
||||
ChangeNumber int `json:"change_number" binding:"required"`
|
||||
Note string `json:"note" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&updateData); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查积分记录是否存在
|
||||
var existRecord models.ScoreRecord
|
||||
if err := common.GetDB().Preload("User").First(&existRecord, uint(recordID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "积分记录不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
tx := common.GetDB().Begin()
|
||||
|
||||
// 计算积分差值
|
||||
pointsDiff := updateData.ChangeNumber - existRecord.ChangeNumber
|
||||
newUserPoints := existRecord.User.CurrentPoints + pointsDiff
|
||||
|
||||
if newUserPoints < 0 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "用户积分不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新积分记录
|
||||
if err := tx.Model(&existRecord).Updates(map[string]interface{}{
|
||||
"change_number": updateData.ChangeNumber,
|
||||
"note": updateData.Note,
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新积分记录失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新用户积分
|
||||
if err := tx.Model(&existRecord.User).Update("current_points", newUserPoints).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新用户积分失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
tx.Commit()
|
||||
|
||||
// 返回更新后的记录
|
||||
var updatedRecord models.ScoreRecord
|
||||
common.GetDB().Preload("User").First(&updatedRecord, uint(recordID))
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(updatedRecord))
|
||||
}
|
||||
|
||||
// DeleteScoreRecord 删除积分记录(仅管理员)
|
||||
func (h *AdminScoreHandler) DeleteScoreRecord(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以删除积分记录
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
recordID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "记录ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查积分记录是否存在
|
||||
var existRecord models.ScoreRecord
|
||||
if err := common.GetDB().Preload("User").First(&existRecord, uint(recordID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "积分记录不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
tx := common.GetDB().Begin()
|
||||
|
||||
// 恢复用户积分(减去这条记录的变化值)
|
||||
newUserPoints := existRecord.User.CurrentPoints - existRecord.ChangeNumber
|
||||
if newUserPoints < 0 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "删除此记录会导致用户积分为负数"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新用户积分
|
||||
if err := tx.Model(&existRecord.User).Update("current_points", newUserPoints).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新用户积分失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 删除积分记录
|
||||
if err := tx.Delete(&existRecord).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "删除积分记录失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
tx.Commit()
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(nil))
|
||||
}
|
||||
|
||||
// GetScoreStats 获取积分统计信息
|
||||
func (h *AdminScoreHandler) GetScoreStats(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
db := common.GetDB()
|
||||
stats := make(map[string]interface{})
|
||||
|
||||
// 构建基础查询
|
||||
baseQuery := db.Model(&models.ScoreRecord{})
|
||||
if user.SystemRole == 1 {
|
||||
// 代理商只统计自己邀请的用户的积分记录
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
baseQuery = baseQuery.Where("user_id IN (?)", subQuery)
|
||||
}
|
||||
|
||||
// 总积分变化记录数
|
||||
var totalRecords int64
|
||||
baseQuery.Count(&totalRecords)
|
||||
stats["total_records"] = totalRecords
|
||||
|
||||
// 总积分增加
|
||||
var totalIncrease int64
|
||||
baseQuery.Where("change_number > 0").Select("COALESCE(SUM(change_number), 0)").Scan(&totalIncrease)
|
||||
stats["total_increase"] = totalIncrease
|
||||
|
||||
// 总积分减少
|
||||
var totalDecrease int64
|
||||
baseQuery.Where("change_number < 0").Select("COALESCE(SUM(ABS(change_number)), 0)").Scan(&totalDecrease)
|
||||
stats["total_decrease"] = totalDecrease
|
||||
|
||||
// 今日积分变化
|
||||
todayQuery := baseQuery
|
||||
if user.SystemRole == 1 {
|
||||
subQuery := db.Model(&models.User{}).Select("id").Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
todayQuery = todayQuery.Where("user_id IN (?)", subQuery)
|
||||
}
|
||||
|
||||
var todayIncrease int64
|
||||
todayQuery.Where("change_number > 0 AND DATE(created_at) = CURDATE()").Select("COALESCE(SUM(change_number), 0)").Scan(&todayIncrease)
|
||||
stats["today_increase"] = todayIncrease
|
||||
|
||||
var todayDecrease int64
|
||||
todayQuery.Where("change_number < 0 AND DATE(created_at) = CURDATE()").Select("COALESCE(SUM(ABS(change_number)), 0)").Scan(&todayDecrease)
|
||||
stats["today_decrease"] = todayDecrease
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(stats))
|
||||
}
|
||||
@@ -0,0 +1,633 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/middleware"
|
||||
"awesomeProject/internal/models"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AdminUserHandler 管理端用户处理器
|
||||
type AdminUserHandler struct{}
|
||||
|
||||
// GetUsers 获取用户列表
|
||||
func (h *AdminUserHandler) GetUsers(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
phone := c.Query("phone")
|
||||
realName := c.Query("real_name")
|
||||
systemRole := c.Query("system_role")
|
||||
identityCode := c.Query("identity_code")
|
||||
|
||||
db := common.GetDB()
|
||||
var users []models.User
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.User{})
|
||||
|
||||
// 如果是代理商,只能查看自己邀请的用户
|
||||
if user.SystemRole == 1 {
|
||||
query = query.Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
}
|
||||
|
||||
// 添加搜索条件
|
||||
if phone != "" {
|
||||
query = query.Where("phone LIKE ?", "%"+phone+"%")
|
||||
}
|
||||
if realName != "" {
|
||||
query = query.Where("real_name LIKE ?", "%"+realName+"%")
|
||||
}
|
||||
if systemRole != "" {
|
||||
query = query.Where("system_role = ?", systemRole)
|
||||
} else {
|
||||
query = query.Where("system_role > 0")
|
||||
}
|
||||
if identityCode != "" {
|
||||
query = query.Where("identity_code LIKE ? OR referrer_identity_code LIKE ?", "%"+identityCode+"%", "%"+identityCode+"%")
|
||||
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&users).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询用户列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": users,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetUser 获取单个用户信息
|
||||
func (h *AdminUserHandler) GetUser(c *gin.Context) {
|
||||
userID, err := middleware.GetUserIDFromParam(c, "id")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
var user models.User
|
||||
err = common.GetDB().First(&user, userID).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "用户不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询用户信息失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(user))
|
||||
}
|
||||
|
||||
// CreateUser 创建用户(仅管理员)
|
||||
func (h *AdminUserHandler) CreateUser(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以创建用户
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
var newUser models.User
|
||||
if err := c.ShouldBindJSON(&newUser); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查手机号是否已存在
|
||||
var existUser models.User
|
||||
if err := common.GetDB().Where("phone = ?", newUser.Phone).First(&existUser).Error; err == nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "手机号已存在"))
|
||||
return
|
||||
}
|
||||
if newUser.SystemRole == 1 {
|
||||
newUser.IdentityCode = "T" + uuid.New().String()[:8]
|
||||
} else {
|
||||
newUser.IdentityCode = "U" + uuid.New().String()[:8]
|
||||
}
|
||||
if newUser.Password != "" {
|
||||
newUser.Password = fmt.Sprintf("%x", md5.Sum([]byte(newUser.Password)))
|
||||
}
|
||||
// 保存用户
|
||||
if err := common.GetDB().Create(&newUser).Error; err != nil {
|
||||
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建用户失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(newUser))
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户信息(仅管理员)
|
||||
func (h *AdminUserHandler) UpdateUser(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以更新用户
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
userID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "用户ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var updateData models.User
|
||||
if err := c.ShouldBindJSON(&updateData); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
fmt.Println(updateData)
|
||||
// 检查用户是否存在
|
||||
var existUser models.User
|
||||
if err := common.GetDB().First(&existUser, uint(userID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "用户不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
updateData.ID = uint(userID)
|
||||
if updateData.Password != "" {
|
||||
updateData.Password = fmt.Sprintf("%x", md5.Sum([]byte(updateData.Password)))
|
||||
}
|
||||
if err := common.GetDB().Model(&existUser).Updates(&updateData).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新用户信息失败"))
|
||||
return
|
||||
}
|
||||
if err := common.GetDB().Model(&existUser).Updates(map[string]interface{}{
|
||||
"status": updateData.Status,
|
||||
}).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新用户信息失败"))
|
||||
return
|
||||
}
|
||||
// 返回更新后的用户信息
|
||||
var updatedUser models.User
|
||||
common.GetDB().First(&updatedUser, uint(userID))
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(updatedUser))
|
||||
}
|
||||
|
||||
// DeleteUser 删除用户(仅管理员)
|
||||
func (h *AdminUserHandler) DeleteUser(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以删除用户
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
userID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "用户ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户是否存在
|
||||
var existUser models.User
|
||||
if err := common.GetDB().First(&existUser, uint(userID)).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "用户不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 删除用户
|
||||
if err := common.GetDB().Delete(&existUser).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "删除用户失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(nil))
|
||||
}
|
||||
|
||||
// GetUserStats 获取用户统计信息
|
||||
func (h *AdminUserHandler) GetUserStats(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
db := common.GetDB()
|
||||
stats := make(map[string]interface{})
|
||||
|
||||
// 如果是代理商,只统计自己邀请的用户
|
||||
baseQuery := db.Model(&models.User{})
|
||||
if user.SystemRole == 1 {
|
||||
baseQuery = baseQuery.Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
}
|
||||
|
||||
// 总用户数
|
||||
var totalUsers int64
|
||||
baseQuery.Count(&totalUsers)
|
||||
stats["total_users"] = totalUsers
|
||||
|
||||
// 按角色统计
|
||||
var roleCounts []struct {
|
||||
SystemRole uint8 `json:"system_role"`
|
||||
Count int64 `json:"count"`
|
||||
}
|
||||
|
||||
roleQuery := baseQuery
|
||||
if user.SystemRole == 1 {
|
||||
roleQuery = roleQuery.Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
}
|
||||
|
||||
roleQuery.Select("system_role, count(*) as count").Group("system_role").Scan(&roleCounts)
|
||||
stats["role_counts"] = roleCounts
|
||||
|
||||
// 今日新增用户
|
||||
var todayUsers int64
|
||||
todayQuery := baseQuery
|
||||
if user.SystemRole == 1 {
|
||||
todayQuery = todayQuery.Where("referrer_identity_code = ?", user.IdentityCode)
|
||||
}
|
||||
todayQuery.Where("DATE(created_at) = CURDATE()").Count(&todayUsers)
|
||||
stats["today_users"] = todayUsers
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(stats))
|
||||
}
|
||||
|
||||
// SetUserExpiryDate 设置用户过期时间(仅管理员)
|
||||
func (h *AdminUserHandler) SetUserExpiryDate(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 只有管理员可以设置用户过期时间
|
||||
if user.SystemRole != 0 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
userID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "用户ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ExpiryDate *string `json:"expiry_date"` // ISO 8601 格式,如 "2024-12-31T23:59:59Z",null表示永不过期
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
|
||||
// 检查用户是否存在
|
||||
var targetUser models.User
|
||||
if err := db.First(&targetUser, uint(userID)).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "用户不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询用户失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 防止设置管理员的过期时间
|
||||
if targetUser.SystemRole == 0 {
|
||||
c.JSON(http.StatusOK, common.Error(400, "不能设置管理员的过期时间"))
|
||||
return
|
||||
}
|
||||
|
||||
// 解析过期时间
|
||||
var expiryDate *time.Time
|
||||
if req.ExpiryDate != nil && *req.ExpiryDate != "" {
|
||||
parsedTime, err := time.Parse(time.RFC3339, *req.ExpiryDate)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "过期时间格式错误,请使用ISO 8601格式"))
|
||||
return
|
||||
}
|
||||
expiryDate = &parsedTime
|
||||
}
|
||||
|
||||
// 更新用户过期时间
|
||||
if err := db.Model(&targetUser).Update("expiry_date", expiryDate).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "设置用户过期时间失败"))
|
||||
return
|
||||
}
|
||||
|
||||
var message string
|
||||
if expiryDate == nil {
|
||||
message = "用户已设置为永不过期"
|
||||
} else {
|
||||
message = fmt.Sprintf("用户过期时间已设置为:%s", expiryDate.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(map[string]interface{}{
|
||||
"message": message,
|
||||
"expiry_date": expiryDate,
|
||||
}))
|
||||
}
|
||||
|
||||
// SearchUserStats 搜索用户统计信息
|
||||
func (h *AdminUserHandler) SearchUserStats(c *gin.Context) {
|
||||
// 获取当前用户信息
|
||||
currentUser, _ := c.Get("current_user")
|
||||
currentUseruser := currentUser.(models.User)
|
||||
|
||||
// 仅管理员可访问
|
||||
if currentUseruser.SystemRole > 1 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
query := c.Query("query")
|
||||
if query == "" {
|
||||
c.JSON(http.StatusOK, common.Error(400, "搜索条件不能为空"))
|
||||
return
|
||||
}
|
||||
|
||||
// 获取时间范围参数
|
||||
startDate := c.Query("start_date")
|
||||
endDate := c.Query("end_date")
|
||||
|
||||
db := common.GetDB()
|
||||
var user models.User
|
||||
//err2 := db.Where("id=?", currentUseruser.ID).First(¤tUseruser).Error
|
||||
//if err2 != nil {
|
||||
// c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
// return
|
||||
//}
|
||||
// 按手机号或姓名搜索用户
|
||||
err := db.Where("phone = ? OR customer_name LIKE ? OR real_name LIKE ?",
|
||||
query, "%"+query+"%", "%"+query+"%").First(&user).Error
|
||||
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "未找到匹配的用户"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询用户失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
if currentUseruser.SystemRole == 1 && user.ReferrerIdentityCode != currentUseruser.IdentityCode {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
// 统计该用户的数据
|
||||
stats := make(map[string]interface{})
|
||||
|
||||
// 构建采购订单查询
|
||||
purchaseQuery := db.Model(&models.PurchaseOrder{}).Where("buyer_id = ? and order_status=?", user.ID, 2)
|
||||
if startDate != "" && endDate != "" {
|
||||
purchaseQuery = purchaseQuery.Where("created_at >= ? AND created_at <= ?", startDate+" 00:00:00", endDate+" 23:59:59")
|
||||
}
|
||||
|
||||
// 采购订单统计
|
||||
var purchaseOrdersCount int64
|
||||
var totalPurchaseAmount float64
|
||||
purchaseQuery.Count(&purchaseOrdersCount)
|
||||
purchaseQuery.Select("COALESCE(SUM(total_amount), 0)").Scan(&totalPurchaseAmount)
|
||||
|
||||
// 构建销售订单查询
|
||||
salesQuery := db.Model(&models.SalesOrder{}).Where("seller_id = ? and order_status=?", user.ID, 2)
|
||||
if startDate != "" && endDate != "" {
|
||||
salesQuery = salesQuery.Where("created_at >= ? AND created_at <= ?", startDate+" 00:00:00", endDate+" 23:59:59")
|
||||
}
|
||||
|
||||
// 销售订单统计
|
||||
var salesOrdersCount int64
|
||||
var totalSalesAmount float64
|
||||
salesQuery.Count(&salesOrdersCount)
|
||||
salesQuery.Select("COALESCE(SUM(total_amount), 0)").Scan(&totalSalesAmount)
|
||||
|
||||
// 库存统计
|
||||
var warehouseItemsCount int64
|
||||
var totalWarehouseQuantity int64
|
||||
if startDate != "" && endDate != "" {
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? and created_at >= ? AND created_at <= ?", user.ID, startDate+" 00:00:00", endDate+" 23:59:59").Count(&warehouseItemsCount)
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? and created_at >= ? AND created_at <= ?", user.ID, startDate+" 00:00:00", endDate+" 23:59:59").Select("COALESCE(SUM(quantity), 0)").Scan(&totalWarehouseQuantity)
|
||||
|
||||
} else {
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? ", user.ID).Count(&warehouseItemsCount)
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? ", user.ID).Select("COALESCE(SUM(quantity), 0)").Scan(&totalWarehouseQuantity)
|
||||
|
||||
}
|
||||
|
||||
// 积分余额
|
||||
var scoreBalance int
|
||||
//db.Model(&models.ScoreRecord{}).Where("user_id = ?", user.ID).Select("COALESCE(SUM(score_change), 0)").Scan(&scoreBalance)
|
||||
scoreBalance = user.CurrentPoints
|
||||
// 最后活动时间(最近的订单时间)
|
||||
var lastActivityTime *time.Time
|
||||
var lastPurchaseTime, lastSalesTime time.Time
|
||||
|
||||
// 查找最近的采购订单时间
|
||||
db.Model(&models.PurchaseOrder{}).Where("user_id = ?", user.ID).
|
||||
Select("created_at").Order("created_at DESC").Limit(1).Scan(&lastPurchaseTime)
|
||||
|
||||
// 查找最近的销售订单时间
|
||||
db.Model(&models.SalesOrder{}).Where("user_id = ?", user.ID).
|
||||
Select("created_at").Order("created_at DESC").Limit(1).Scan(&lastSalesTime)
|
||||
|
||||
// 取两个时间中的最大值
|
||||
if !lastPurchaseTime.IsZero() && !lastSalesTime.IsZero() {
|
||||
if lastPurchaseTime.After(lastSalesTime) {
|
||||
lastActivityTime = &lastPurchaseTime
|
||||
} else {
|
||||
lastActivityTime = &lastSalesTime
|
||||
}
|
||||
} else if !lastPurchaseTime.IsZero() {
|
||||
lastActivityTime = &lastPurchaseTime
|
||||
} else if !lastSalesTime.IsZero() {
|
||||
lastActivityTime = &lastSalesTime
|
||||
}
|
||||
|
||||
stats["purchase_orders_count"] = purchaseOrdersCount
|
||||
stats["total_purchase_amount"] = totalPurchaseAmount
|
||||
stats["sales_orders_count"] = salesOrdersCount
|
||||
stats["total_sales_amount"] = totalSalesAmount
|
||||
stats["warehouse_items_count"] = warehouseItemsCount
|
||||
stats["total_warehouse_quantity"] = totalWarehouseQuantity
|
||||
stats["score_balance"] = scoreBalance
|
||||
stats["last_activity_time"] = lastActivityTime
|
||||
|
||||
result := map[string]interface{}{
|
||||
"user": user,
|
||||
"stats": stats,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetAllUserWarehouses 获取所有用户库存汇总(管理员专用)
|
||||
func (h *AdminUserHandler) GetAllUserWarehouses(c *gin.Context) {
|
||||
// 获取当前用户信息
|
||||
currentUser, _ := c.Get("current_user")
|
||||
currentUseruser := currentUser.(models.User)
|
||||
|
||||
// 仅管理员可访问
|
||||
if currentUseruser.SystemRole > 1 {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 100 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
searchQuery := c.Query("search") // 搜索手机号或姓名
|
||||
productType := c.Query("product_type") // 商品类型:1一级商品,2二级商品
|
||||
status := c.Query("status") // 状态:0已出售,1库存中
|
||||
|
||||
db := common.GetDB()
|
||||
|
||||
// 构建库存记录查询(带用户信息)
|
||||
warehouseQuery := db.Model(&models.UserWarehouse{}).
|
||||
Select("user_warehouse.*, users.phone, users.customer_name, users.real_name").
|
||||
Joins("JOIN users ON users.id = user_warehouse.user_id")
|
||||
|
||||
// 搜索用户(基于手机号或姓名)
|
||||
if searchQuery != "" {
|
||||
warehouseQuery = warehouseQuery.Where("users.phone LIKE ? OR users.customer_name LIKE ? OR users.real_name LIKE ?",
|
||||
"%"+searchQuery+"%", "%"+searchQuery+"%", "%"+searchQuery+"%")
|
||||
}
|
||||
|
||||
// 应用商品类型筛选
|
||||
if productType != "" {
|
||||
warehouseQuery = warehouseQuery.Where("user_warehouse.product_type = ?", productType)
|
||||
}
|
||||
|
||||
// 应用状态筛选
|
||||
if status != "" {
|
||||
warehouseQuery = warehouseQuery.Where("user_warehouse.status = ?", status)
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
var total int64
|
||||
warehouseQuery.Count(&total)
|
||||
|
||||
// 分页查询库存记录
|
||||
var userWarehouses []models.UserWarehouse
|
||||
offset := (page - 1) * pageSize
|
||||
err := warehouseQuery.Offset(offset).Limit(pageSize).Order("user_warehouse.created_at DESC").Find(&userWarehouses).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询用户库存列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 转换数据结构以包含用户信息,并按用户ID、商品类型、商品名称、状态聚合
|
||||
var resultList []struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Phone string `json:"phone"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
RealName string `json:"real_name"`
|
||||
ProductType uint8 `json:"product_type"`
|
||||
ProductName string `json:"product_name"`
|
||||
TotalQuantity int `json:"total_quantity"`
|
||||
TotalValue float64 `json:"total_value"`
|
||||
Status uint8 `json:"status"`
|
||||
}
|
||||
|
||||
// 按用户ID、商品类型、商品名称、状态聚合库存
|
||||
userWarehouseMap := make(map[string]*struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Phone string `json:"phone"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
RealName string `json:"real_name"`
|
||||
ProductType uint8 `json:"product_type"`
|
||||
ProductName string `json:"product_name"`
|
||||
TotalQuantity int `json:"total_quantity"`
|
||||
TotalValue float64 `json:"total_value"`
|
||||
Status uint8 `json:"status"`
|
||||
})
|
||||
|
||||
for _, warehouse := range userWarehouses {
|
||||
// 生成唯一键:用户ID_商品类型_商品名称_状态
|
||||
key := fmt.Sprintf("%d_%d_%s_%d", warehouse.UserID, warehouse.ProductType, warehouse.ProductName, warehouse.Status)
|
||||
|
||||
// 获取用户信息(第一次出现时)
|
||||
var user models.User
|
||||
db.First(&user, warehouse.UserID)
|
||||
|
||||
if item, exists := userWarehouseMap[key]; exists {
|
||||
// 累加数量和价值
|
||||
item.TotalQuantity += warehouse.Quantity
|
||||
item.TotalValue += float64(warehouse.Quantity) * warehouse.WarehousePrice
|
||||
} else {
|
||||
// 创建新的聚合记录
|
||||
userWarehouseMap[key] = &struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Phone string `json:"phone"`
|
||||
CustomerName string `json:"customer_name"`
|
||||
RealName string `json:"real_name"`
|
||||
ProductType uint8 `json:"product_type"`
|
||||
ProductName string `json:"product_name"`
|
||||
TotalQuantity int `json:"total_quantity"`
|
||||
TotalValue float64 `json:"total_value"`
|
||||
Status uint8 `json:"status"`
|
||||
}{
|
||||
UserID: warehouse.UserID,
|
||||
Phone: user.Phone,
|
||||
CustomerName: user.CustomerName,
|
||||
RealName: user.RealName,
|
||||
ProductType: warehouse.ProductType,
|
||||
ProductName: warehouse.ProductName,
|
||||
TotalQuantity: warehouse.Quantity,
|
||||
TotalValue: float64(warehouse.Quantity) * warehouse.WarehousePrice,
|
||||
Status: warehouse.Status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 转换为切片
|
||||
for _, item := range userWarehouseMap {
|
||||
resultList = append(resultList, *item)
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": resultList,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"awesomeProject/internal/services"
|
||||
jwtutil "awesomeProject/pkg/utils"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AuthHandler 认证处理器
|
||||
type AuthHandler struct{}
|
||||
|
||||
// LoginRequest 登录请求结构
|
||||
type LoginRequest struct {
|
||||
Phone string `json:"phone" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// LoginResponse 登录响应结构
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
User models.User `json:"user"`
|
||||
}
|
||||
type TokenGen struct {
|
||||
UserID uint `json:"user_id"`
|
||||
Phone string `json:"phone"`
|
||||
SystemRole int8 `json:"system_role"`
|
||||
}
|
||||
|
||||
// Login 用户登录
|
||||
func (h *AuthHandler) Login(c *gin.Context) {
|
||||
var req LoginRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 查询用户
|
||||
var user models.User
|
||||
err := common.GetDB().Where("phone = ?", req.Phone).First(&user).Error
|
||||
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(401, "手机号或密码错误"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "登录失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 验证密码(这里简单使用MD5,实际应该使用bcrypt)
|
||||
hashedPassword := fmt.Sprintf("%x", md5.Sum([]byte(req.Password)))
|
||||
fmt.Println(hashedPassword)
|
||||
if user.Password != hashedPassword {
|
||||
c.JSON(http.StatusOK, common.Error(404, "手机号或密码错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户状态
|
||||
if user.Status != 1 {
|
||||
c.JSON(http.StatusOK, common.Error(404, "用户已被禁用,请联系管理员"))
|
||||
return
|
||||
}
|
||||
|
||||
// 生成JWT token
|
||||
claims := TokenGen{
|
||||
UserID: user.ID,
|
||||
Phone: user.Phone,
|
||||
SystemRole: int8(user.SystemRole),
|
||||
}
|
||||
|
||||
token, err := jwtutil.GenerateToken(claims)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "生成token失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 返回登录结果
|
||||
response := LoginResponse{
|
||||
Token: token,
|
||||
User: user,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(response))
|
||||
}
|
||||
|
||||
// RegisterRequest 注册请求结构体
|
||||
type RegisterRequest struct {
|
||||
models.User
|
||||
SMSCode string `json:"sms_code"` // 短信验证码
|
||||
}
|
||||
|
||||
// Register 用户注册
|
||||
func (h *AuthHandler) Register(c *gin.Context) {
|
||||
var req RegisterRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证短信验证码(必填)
|
||||
if req.SMSCode == "" {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请输入短信验证码"))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证短信验证码
|
||||
smsService := services.GetSMSService()
|
||||
if !smsService.VerifyCode(req.Phone, req.SMSCode) {
|
||||
c.JSON(http.StatusOK, common.Error(400, "短信验证码错误或已过期"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查手机号是否已存在
|
||||
var existUser models.User
|
||||
if err := common.GetDB().Where("phone = ?", req.Phone).First(&existUser).Error; err == nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "手机号已被注册"))
|
||||
return
|
||||
}
|
||||
|
||||
user := req.User
|
||||
|
||||
// 生成身份码(简单实现,实际应该使用更复杂的算法)
|
||||
user.IdentityCode = fmt.Sprintf("U%v", uuid.New().String()[:8])
|
||||
|
||||
// 密码加密(这里简单使用MD5,实际应该使用bcrypt)
|
||||
hashedPassword := fmt.Sprintf("%x", md5.Sum([]byte(user.Password)))
|
||||
user.Password = hashedPassword
|
||||
|
||||
// 默认为普通用户
|
||||
if user.SystemRole == 0 {
|
||||
user.SystemRole = 2
|
||||
|
||||
}
|
||||
user.Status = 0
|
||||
// 保存用户
|
||||
if err := common.GetDB().Create(&user).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "注册失败"))
|
||||
return
|
||||
}
|
||||
|
||||
common.GetDB().Model(&user).Update("status", 0)
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(user))
|
||||
}
|
||||
|
||||
// GetCurrentUser 获取当前用户信息
|
||||
func (h *AuthHandler) GetCurrentUser(c *gin.Context) {
|
||||
// 从token中获取用户信息
|
||||
tokenMap, exists := c.Get("tokenMap")
|
||||
if !exists {
|
||||
c.JSON(http.StatusOK, common.Error(401, "未授权访问"))
|
||||
return
|
||||
}
|
||||
|
||||
claims := tokenMap.(map[string]interface{})
|
||||
userIDFloat, ok := claims["user_id"].(float64)
|
||||
if !ok {
|
||||
c.JSON(http.StatusOK, common.Error(401, "用户信息无效"))
|
||||
return
|
||||
}
|
||||
|
||||
userID := uint(userIDFloat)
|
||||
|
||||
// 查询用户信息
|
||||
var user models.User
|
||||
if err := common.GetDB().First(&user, userID).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "用户不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(user))
|
||||
}
|
||||
|
||||
// UpdateProfileRequest 更新个人信息请求结构体
|
||||
type UpdateProfileRequest struct {
|
||||
CustomerName string `json:"customer_name" binding:"required"`
|
||||
RealName string `json:"real_name" binding:"required"`
|
||||
Avatar string `json:"avatar"`
|
||||
CompanyName string `json:"company_name"`
|
||||
PersonalIntro string `json:"personal_intro"`
|
||||
BusinessLicense string `json:"business_license"`
|
||||
IDCardFront string `json:"id_card_front"`
|
||||
IDCardBack string `json:"id_card_back"`
|
||||
MainPaymentCode string `json:"main_payment_code"`
|
||||
BackupPaymentCode string `json:"backup_payment_code"`
|
||||
Phone string `json:"phone"`
|
||||
}
|
||||
|
||||
// UpdateProfile 更新个人信息
|
||||
func (h *AuthHandler) UpdateProfile(c *gin.Context) {
|
||||
// 从token中获取用户信息
|
||||
tokenMap, exists := c.Get("tokenMap")
|
||||
if !exists {
|
||||
c.JSON(http.StatusOK, common.Error(401, "未授权访问"))
|
||||
return
|
||||
}
|
||||
|
||||
claims := tokenMap.(map[string]interface{})
|
||||
userIDFloat, ok := claims["user_id"].(float64)
|
||||
if !ok {
|
||||
c.JSON(http.StatusOK, common.Error(401, "用户信息无效"))
|
||||
return
|
||||
}
|
||||
|
||||
userID := uint(userIDFloat)
|
||||
|
||||
var req UpdateProfileRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 查询当前用户
|
||||
var user models.User
|
||||
if err := common.GetDB().First(&user, userID).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "用户不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新用户信息
|
||||
updateData := models.User{
|
||||
CustomerName: req.CustomerName,
|
||||
RealName: req.RealName,
|
||||
Avatar: req.Avatar,
|
||||
CompanyName: req.CompanyName,
|
||||
PersonalIntro: req.PersonalIntro,
|
||||
BusinessLicenseImage: req.BusinessLicense,
|
||||
IdCardFrontImage: req.IDCardFront,
|
||||
IdCardBackImage: req.IDCardBack,
|
||||
MainPaymentQrImage: &req.MainPaymentCode,
|
||||
SubPaymentQrImage: &req.BackupPaymentCode,
|
||||
Phone: req.Phone,
|
||||
}
|
||||
|
||||
updateData.ID = userID
|
||||
if err := common.GetDB().Model(&user).Updates(&updateData).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新个人信息失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 返回更新后的用户信息
|
||||
var updatedUser models.User
|
||||
common.GetDB().First(&updatedUser, userID)
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(updatedUser))
|
||||
}
|
||||
|
||||
// ChangePasswordRequest 修改密码请求结构体
|
||||
type ChangePasswordRequest struct {
|
||||
CurrentPassword string `json:"current_password" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required"`
|
||||
}
|
||||
|
||||
// ChangePassword 修改密码
|
||||
func (h *AuthHandler) ChangePassword(c *gin.Context) {
|
||||
// 从token中获取用户信息
|
||||
tokenMap, exists := c.Get("tokenMap")
|
||||
if !exists {
|
||||
c.JSON(http.StatusOK, common.Error(401, "未授权访问"))
|
||||
return
|
||||
}
|
||||
|
||||
claims := tokenMap.(map[string]interface{})
|
||||
userIDFloat, ok := claims["user_id"].(float64)
|
||||
if !ok {
|
||||
c.JSON(http.StatusOK, common.Error(401, "用户信息无效"))
|
||||
return
|
||||
}
|
||||
|
||||
userID := uint(userIDFloat)
|
||||
|
||||
var req ChangePasswordRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 查询当前用户
|
||||
var user models.User
|
||||
if err := common.GetDB().First(&user, userID).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "用户不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证当前密码
|
||||
hashedCurrentPassword := fmt.Sprintf("%x", md5.Sum([]byte(req.CurrentPassword)))
|
||||
if hashedCurrentPassword != user.Password {
|
||||
c.JSON(http.StatusOK, common.Error(400, "当前密码错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 加密新密码
|
||||
hashedNewPassword := fmt.Sprintf("%x", md5.Sum([]byte(req.NewPassword)))
|
||||
|
||||
// 更新密码
|
||||
if err := common.GetDB().Model(&user).Update("password", hashedNewPassword).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "修改密码失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(nil))
|
||||
}
|
||||
|
||||
// SendSMSRequest 发送短信验证码请求结构体
|
||||
type SendSMSRequest struct {
|
||||
Phone string `json:"phone" binding:"required"`
|
||||
}
|
||||
|
||||
// SendSMS 发送短信验证码
|
||||
func (h *AuthHandler) SendSMS(c *gin.Context) {
|
||||
var req SendSMSRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "参数错误: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 获取客户端IP地址
|
||||
clientIP := getClientIP(c)
|
||||
|
||||
// 调用短信服务发送验证码
|
||||
smsService := services.GetSMSService()
|
||||
code, err := smsService.SendSMS(req.Phone, clientIP)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 根据环境决定是否返回验证码(开发环境返回,生产环境不返回)
|
||||
config := common.MineConfig
|
||||
response := map[string]interface{}{
|
||||
"message": "验证码已发送",
|
||||
}
|
||||
|
||||
// 仅在开发环境返回验证码,方便测试
|
||||
if config != nil && config.App.Env == "development" {
|
||||
response["code"] = code
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(response))
|
||||
}
|
||||
|
||||
// getClientIP 获取客户端真实IP地址
|
||||
func getClientIP(c *gin.Context) string {
|
||||
// 优先从 X-Forwarded-For 获取(经过代理时)
|
||||
ip := c.GetHeader("X-Forwarded-For")
|
||||
if ip != "" {
|
||||
// X-Forwarded-For 可能包含多个IP,取第一个
|
||||
ips := strings.Split(ip, ",")
|
||||
if len(ips) > 0 {
|
||||
return strings.TrimSpace(ips[0])
|
||||
}
|
||||
}
|
||||
|
||||
// 从 X-Real-IP 获取
|
||||
ip = c.GetHeader("X-Real-IP")
|
||||
if ip != "" {
|
||||
return strings.TrimSpace(ip)
|
||||
}
|
||||
|
||||
// 最后从 RemoteAddr 获取
|
||||
ip = c.ClientIP()
|
||||
return ip
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// InitHandler 初始化处理器
|
||||
type InitHandler struct{}
|
||||
|
||||
// InitDefaultAdmin 初始化默认管理员
|
||||
func (h *InitHandler) InitDefaultAdmin() error {
|
||||
db := common.GetDB()
|
||||
|
||||
// 检查是否已有管理员用户
|
||||
var adminCount int64
|
||||
db.Model(&models.User{}).Where("system_role = 0").Count(&adminCount)
|
||||
if adminCount > 0 {
|
||||
return nil // 已有管理员,不重复创建
|
||||
}
|
||||
|
||||
// 创建默认管理员
|
||||
hashedPassword := fmt.Sprintf("%x", md5.Sum([]byte("admin123")))
|
||||
admin := models.User{
|
||||
Phone: "13800138000",
|
||||
SystemRole: 0, // 管理员
|
||||
CustomerName: "系统管理员",
|
||||
RealName: "Administrator",
|
||||
Password: hashedPassword,
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
if err := db.Create(&admin).Error; err != nil {
|
||||
return fmt.Errorf("创建默认管理员失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新身份码
|
||||
admin.IdentityCode = fmt.Sprintf("A%08d", admin.ID)
|
||||
if err := db.Model(&admin).Update("identity_code", admin.IdentityCode).Error; err != nil {
|
||||
return fmt.Errorf("更新管理员身份码失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("默认管理员创建成功: 手机号: %s, 密码: admin123", admin.Phone)
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitDefaultAgent 初始化默认代理商(可选)
|
||||
func (h *InitHandler) InitDefaultAgent() error {
|
||||
db := common.GetDB()
|
||||
|
||||
// 检查是否已有代理商用户
|
||||
var agent models.User
|
||||
err := db.Where("phone = ?", "13800138001").First(&agent).Error
|
||||
if err == nil {
|
||||
return nil // 代理商已存在
|
||||
} else if err != gorm.ErrRecordNotFound {
|
||||
return fmt.Errorf("查询代理商失败: %v", err)
|
||||
}
|
||||
|
||||
// 创建默认代理商
|
||||
hashedPassword := fmt.Sprintf("%x", md5.Sum([]byte("agent123")))
|
||||
agentUser := models.User{
|
||||
Phone: "13800138001",
|
||||
SystemRole: 1, // 代理商
|
||||
CustomerName: "测试代理商",
|
||||
RealName: "Test Agent",
|
||||
Password: hashedPassword,
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
if err := db.Create(&agentUser).Error; err != nil {
|
||||
return fmt.Errorf("创建默认代理商失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新身份码
|
||||
agentUser.IdentityCode = fmt.Sprintf("T%08d", agentUser.ID)
|
||||
if err := db.Model(&agentUser).Update("identity_code", agentUser.IdentityCode).Error; err != nil {
|
||||
return fmt.Errorf("更新代理商身份码失败: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("默认代理商创建成功: 手机号: %s, 密码: agent123", agentUser.Phone)
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitTestUsers 初始化测试用户
|
||||
func (h *InitHandler) InitTestUsers() error {
|
||||
db := common.GetDB()
|
||||
|
||||
// 获取代理商身份码
|
||||
var agent models.User
|
||||
if err := db.Where("system_role = 1").First(&agent).Error; err != nil {
|
||||
return nil // 没有代理商,跳过创建测试用户
|
||||
}
|
||||
|
||||
// 创建测试用户
|
||||
testUsers := []models.User{
|
||||
{
|
||||
Phone: "13800138002",
|
||||
SystemRole: 2, // 普通用户
|
||||
CustomerName: "测试用户1",
|
||||
RealName: "Test User 1",
|
||||
Password: fmt.Sprintf("%x", md5.Sum([]byte("user123"))),
|
||||
ReferrerIdentityCode: agent.IdentityCode,
|
||||
Status: 1,
|
||||
},
|
||||
{
|
||||
Phone: "13800138003",
|
||||
SystemRole: 2, // 普通用户
|
||||
CustomerName: "测试用户2",
|
||||
RealName: "Test User 2",
|
||||
Password: fmt.Sprintf("%x", md5.Sum([]byte("user123"))),
|
||||
ReferrerIdentityCode: agent.IdentityCode,
|
||||
Status: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for i, user := range testUsers {
|
||||
// 检查用户是否已存在
|
||||
var existUser models.User
|
||||
if err := db.Where("phone = ?", user.Phone).First(&existUser).Error; err == nil {
|
||||
continue // 用户已存在,跳过
|
||||
}
|
||||
|
||||
if err := db.Create(&user).Error; err != nil {
|
||||
return fmt.Errorf("创建测试用户失败: %v", err)
|
||||
}
|
||||
|
||||
// 更新身份码
|
||||
user.IdentityCode = fmt.Sprintf("U%08d", user.ID)
|
||||
if err := db.Model(&user).Update("identity_code", user.IdentityCode).Error; err != nil {
|
||||
return fmt.Errorf("更新用户身份码失败: %v", err)
|
||||
}
|
||||
|
||||
testUsers[i] = user
|
||||
log.Printf("测试用户创建成功: 手机号: %s, 密码: user123", user.Phone)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// InitAll 初始化所有数据
|
||||
func (h *InitHandler) InitAll() error {
|
||||
// 初始化默认管理员
|
||||
if err := h.InitDefaultAdmin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 初始化默认代理商
|
||||
if err := h.InitDefaultAgent(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 初始化测试用户
|
||||
if err := h.InitTestUsers(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// UploadHandler 文件上传处理器
|
||||
type UploadHandler struct{}
|
||||
|
||||
// NewUploadHandler 创建文件上传处理器实例
|
||||
func NewUploadHandler() *UploadHandler {
|
||||
return &UploadHandler{}
|
||||
}
|
||||
|
||||
// UploadResponse 上传响应
|
||||
type UploadResponse struct {
|
||||
FileName string `json:"fileName"` // 原文件名
|
||||
NewFileName string `json:"newFileName"` // 新文件名
|
||||
URL string `json:"url"` // 访问URL
|
||||
Size int64 `json:"size"` // 文件大小(字节)
|
||||
Type string `json:"type"` // 文件类型
|
||||
}
|
||||
|
||||
// UploadFile 上传文件
|
||||
func (h *UploadHandler) UploadFile(c *gin.Context) {
|
||||
// 获取上传的文件
|
||||
file, header, err := c.Request.FormFile("file")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "获取上传文件失败: "+err.Error()))
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 检查文件大小限制 (10MB)
|
||||
maxSize := int64(10 * 1024 * 1024) // 10MB
|
||||
if header.Size > maxSize {
|
||||
c.JSON(http.StatusOK, common.Error(500, "文件大小超过限制(最大10MB)"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
if !isAllowedFileType(header.Filename) {
|
||||
c.JSON(http.StatusOK, common.Error(500, "不支持的文件类型"))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建保存目录
|
||||
saveDir := "./public/file"
|
||||
if err := os.MkdirAll(saveDir, os.ModePerm); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建保存目录失败: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 生成新的文件名
|
||||
newFileName, err := generateFileName(header.Filename, file)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "生成文件名失败: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 完整的保存路径
|
||||
savePath := filepath.Join(saveDir, newFileName)
|
||||
|
||||
// 保存文件
|
||||
if err := saveUploadedFile(file, savePath); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "保存文件失败: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建响应
|
||||
response := UploadResponse{
|
||||
FileName: header.Filename,
|
||||
NewFileName: newFileName,
|
||||
URL: "/back/file/" + newFileName,
|
||||
Size: header.Size,
|
||||
Type: getFileType(header.Filename),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(response))
|
||||
}
|
||||
|
||||
// generateFileName 生成唯一的文件名
|
||||
func generateFileName(originalName string, file io.Reader) (string, error) {
|
||||
// 获取文件扩展名
|
||||
ext := filepath.Ext(originalName)
|
||||
|
||||
// 重置文件指针到开头
|
||||
if seeker, ok := file.(io.Seeker); ok {
|
||||
seeker.Seek(0, 0)
|
||||
}
|
||||
|
||||
// 读取文件内容用于生成哈希
|
||||
hasher := md5.New()
|
||||
if _, err := io.Copy(hasher, file); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 重置文件指针到开头
|
||||
if seeker, ok := file.(io.Seeker); ok {
|
||||
seeker.Seek(0, 0)
|
||||
}
|
||||
|
||||
// 生成文件名:时间戳 + MD5哈希前8位 + 扩展名
|
||||
timestamp := time.Now().Format("20060102150405")
|
||||
hash := fmt.Sprintf("%x", hasher.Sum(nil))[:8]
|
||||
newName := fmt.Sprintf("%s_%s%s", timestamp, hash, ext)
|
||||
|
||||
return newName, nil
|
||||
}
|
||||
|
||||
// saveUploadedFile 保存上传的文件
|
||||
func saveUploadedFile(src io.Reader, dst string) error {
|
||||
out, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
_, err = io.Copy(out, src)
|
||||
return err
|
||||
}
|
||||
|
||||
// isAllowedFileType 检查是否为允许的文件类型
|
||||
func isAllowedFileType(filename string) bool {
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
|
||||
// 允许的文件类型
|
||||
allowedTypes := map[string]bool{
|
||||
// 图片类型
|
||||
".jpg": true,
|
||||
".jpeg": true,
|
||||
".png": true,
|
||||
".gif": true,
|
||||
".bmp": true,
|
||||
".webp": true,
|
||||
".svg": true,
|
||||
// 文档类型
|
||||
".pdf": true,
|
||||
".doc": true,
|
||||
".docx": true,
|
||||
".xls": true,
|
||||
".xlsx": true,
|
||||
".ppt": true,
|
||||
".pptx": true,
|
||||
".txt": true,
|
||||
".rtf": true,
|
||||
// 压缩包类型
|
||||
".zip": true,
|
||||
".rar": true,
|
||||
".7z": true,
|
||||
".tar": true,
|
||||
".gz": true,
|
||||
// 视频类型
|
||||
".mp4": true,
|
||||
".avi": true,
|
||||
".mov": true,
|
||||
".wmv": true,
|
||||
".flv": true,
|
||||
".webm": true,
|
||||
// 音频类型
|
||||
".mp3": true,
|
||||
".wav": true,
|
||||
".flac": true,
|
||||
".aac": true,
|
||||
".ogg": true,
|
||||
}
|
||||
|
||||
return allowedTypes[ext]
|
||||
}
|
||||
|
||||
// getFileType 获取文件类型
|
||||
func getFileType(filename string) string {
|
||||
ext := strings.ToLower(filepath.Ext(filename))
|
||||
|
||||
imageTypes := map[string]bool{
|
||||
".jpg": true, ".jpeg": true, ".png": true, ".gif": true,
|
||||
".bmp": true, ".webp": true, ".svg": true,
|
||||
}
|
||||
|
||||
documentTypes := map[string]bool{
|
||||
".pdf": true, ".doc": true, ".docx": true, ".xls": true,
|
||||
".xlsx": true, ".ppt": true, ".pptx": true, ".txt": true, ".rtf": true,
|
||||
}
|
||||
|
||||
videoTypes := map[string]bool{
|
||||
".mp4": true, ".avi": true, ".mov": true, ".wmv": true,
|
||||
".flv": true, ".webm": true,
|
||||
}
|
||||
|
||||
audioTypes := map[string]bool{
|
||||
".mp3": true, ".wav": true, ".flac": true, ".aac": true, ".ogg": true,
|
||||
}
|
||||
|
||||
archiveTypes := map[string]bool{
|
||||
".zip": true, ".rar": true, ".7z": true, ".tar": true, ".gz": true,
|
||||
}
|
||||
|
||||
if imageTypes[ext] {
|
||||
return "image"
|
||||
} else if documentTypes[ext] {
|
||||
return "document"
|
||||
} else if videoTypes[ext] {
|
||||
return "video"
|
||||
} else if audioTypes[ext] {
|
||||
return "audio"
|
||||
} else if archiveTypes[ext] {
|
||||
return "archive"
|
||||
}
|
||||
|
||||
return "other"
|
||||
}
|
||||
|
||||
// BatchUploadFiles 批量上传文件
|
||||
func (h *UploadHandler) BatchUploadFiles(c *gin.Context) {
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "获取上传文件失败: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
files := form.File["files"]
|
||||
if len(files) == 0 {
|
||||
c.JSON(http.StatusOK, common.Error(500, "没有选择文件"))
|
||||
return
|
||||
}
|
||||
|
||||
// 限制批量上传数量
|
||||
if len(files) > 10 {
|
||||
c.JSON(http.StatusOK, common.Error(500, "一次最多上传10个文件"))
|
||||
return
|
||||
}
|
||||
|
||||
var responses []UploadResponse
|
||||
var failedFiles []string
|
||||
|
||||
// 创建保存目录
|
||||
saveDir := "./public/file"
|
||||
if err := os.MkdirAll(saveDir, os.ModePerm); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建保存目录失败: "+err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
for _, header := range files {
|
||||
// 检查文件大小
|
||||
if header.Size > int64(10*1024*1024) {
|
||||
failedFiles = append(failedFiles, header.Filename+" (文件过大)")
|
||||
continue
|
||||
}
|
||||
|
||||
// 检查文件类型
|
||||
if !isAllowedFileType(header.Filename) {
|
||||
failedFiles = append(failedFiles, header.Filename+" (不支持的类型)")
|
||||
continue
|
||||
}
|
||||
|
||||
// 打开文件
|
||||
file, err := header.Open()
|
||||
if err != nil {
|
||||
failedFiles = append(failedFiles, header.Filename+" (打开失败)")
|
||||
continue
|
||||
}
|
||||
|
||||
// 生成新文件名
|
||||
newFileName, err := generateFileName(header.Filename, file)
|
||||
if err != nil {
|
||||
file.Close()
|
||||
failedFiles = append(failedFiles, header.Filename+" (生成文件名失败)")
|
||||
continue
|
||||
}
|
||||
|
||||
// 保存文件
|
||||
savePath := filepath.Join(saveDir, newFileName)
|
||||
if err := saveUploadedFile(file, savePath); err != nil {
|
||||
file.Close()
|
||||
failedFiles = append(failedFiles, header.Filename+" (保存失败)")
|
||||
continue
|
||||
}
|
||||
|
||||
file.Close()
|
||||
|
||||
// 添加到成功列表
|
||||
responses = append(responses, UploadResponse{
|
||||
FileName: header.Filename,
|
||||
NewFileName: newFileName,
|
||||
URL: "/back/file/" + newFileName,
|
||||
Size: header.Size,
|
||||
Type: getFileType(header.Filename),
|
||||
})
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"success": responses,
|
||||
"failed": failedFiles,
|
||||
"totalCount": len(files),
|
||||
"successCount": len(responses),
|
||||
"failedCount": len(failedFiles),
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserAddressHandler 用户收货地址处理器
|
||||
type UserAddressHandler struct{}
|
||||
|
||||
// GetAddressList 获取用户收货地址列表
|
||||
func (h *UserAddressHandler) GetAddressList(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
var addresses []models.UserAddress
|
||||
err := common.GetDB().Where("user_id = ?", user.ID).Order("is_default DESC, created_at DESC").Find(&addresses).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "获取收货地址失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(addresses))
|
||||
}
|
||||
|
||||
// GetAddress 获取单个收货地址
|
||||
func (h *UserAddressHandler) GetAddress(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
addressID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "地址ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var address models.UserAddress
|
||||
err = common.GetDB().Where("id = ? AND user_id = ?", addressID, user.ID).First(&address).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "收货地址不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "获取收货地址失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(address))
|
||||
}
|
||||
|
||||
// CreateAddress 创建收货地址
|
||||
func (h *UserAddressHandler) CreateAddress(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
var address models.UserAddress
|
||||
if err := c.ShouldBindJSON(&address); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 设置用户ID
|
||||
address.UserID = user.ID
|
||||
|
||||
// 开启事务
|
||||
tx := common.GetDB().Begin()
|
||||
|
||||
// 如果设置为默认地址,先取消其他地址的默认状态
|
||||
if address.IsDefault == 1 {
|
||||
if err := tx.Model(&models.UserAddress{}).Where("user_id = ?", user.ID).Update("is_default", 0).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新默认地址状态失败"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 创建地址
|
||||
if err := tx.Create(&address).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建收货地址失败"))
|
||||
return
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(address))
|
||||
}
|
||||
|
||||
// UpdateAddress 更新收货地址
|
||||
func (h *UserAddressHandler) UpdateAddress(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
addressID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "地址ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查地址是否存在且属于当前用户
|
||||
var existAddress models.UserAddress
|
||||
err = common.GetDB().Where("id = ? AND user_id = ?", addressID, user.ID).First(&existAddress).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "收货地址不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "获取收货地址失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var updateData models.UserAddress
|
||||
if err := c.ShouldBindJSON(&updateData); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
tx := common.GetDB().Begin()
|
||||
|
||||
// 如果设置为默认地址,先取消其他地址的默认状态
|
||||
if updateData.IsDefault == 1 {
|
||||
if err := tx.Model(&models.UserAddress{}).Where("user_id = ? AND id != ?", user.ID, addressID).Update("is_default", 0).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新默认地址状态失败"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 更新地址信息
|
||||
updateData.UserID = user.ID // 确保不会被修改
|
||||
if err := tx.Model(&existAddress).Updates(updateData).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新收货地址失败"))
|
||||
return
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
// 返回更新后的地址
|
||||
var updatedAddress models.UserAddress
|
||||
common.GetDB().Where("id = ?", addressID).First(&updatedAddress)
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(updatedAddress))
|
||||
}
|
||||
|
||||
// DeleteAddress 删除收货地址
|
||||
func (h *UserAddressHandler) DeleteAddress(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
addressID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "地址ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查地址是否存在且属于当前用户
|
||||
var address models.UserAddress
|
||||
err = common.GetDB().Where("id = ? AND user_id = ?", addressID, user.ID).First(&address).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "收货地址不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "获取收货地址失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 删除地址
|
||||
if err := common.GetDB().Delete(&address).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "删除收货地址失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(nil))
|
||||
}
|
||||
|
||||
// SetDefaultAddress 设置默认收货地址
|
||||
func (h *UserAddressHandler) SetDefaultAddress(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
addressID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "地址ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查地址是否存在且属于当前用户
|
||||
var address models.UserAddress
|
||||
err = common.GetDB().Where("id = ? AND user_id = ?", addressID, user.ID).First(&address).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "收货地址不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "获取收货地址失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 开启事务
|
||||
tx := common.GetDB().Begin()
|
||||
|
||||
// 取消其他地址的默认状态
|
||||
if err := tx.Model(&models.UserAddress{}).Where("user_id = ?", user.ID).Update("is_default", 0).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新默认地址状态失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 设置当前地址为默认
|
||||
if err := tx.Model(&address).Update("is_default", 1).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "设置默认地址失败"))
|
||||
return
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(nil))
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// UserBannerHandler 用户端轮播图处理器
|
||||
type UserBannerHandler struct{}
|
||||
|
||||
// GetActiveBanners 获取启用的轮播图列表
|
||||
func (h *UserBannerHandler) GetActiveBanners(c *gin.Context) {
|
||||
var banners []models.Banner
|
||||
|
||||
// 查询启用的轮播图,按排序顺序排列
|
||||
err := common.GetDB().Where("status = ?", 1).Order("sort_order ASC, created_at DESC").Find(&banners).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询轮播图失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(banners))
|
||||
}
|
||||
@@ -0,0 +1,863 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserOrderHandler 用户订单处理器
|
||||
type UserOrderHandler struct{}
|
||||
|
||||
// CreatePurchaseOrder 创建买单
|
||||
func (h *UserOrderHandler) CreatePurchaseOrder(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
var req struct {
|
||||
ProductID uint `json:"product_id" binding:"required"`
|
||||
ProductType uint8 `json:"product_type" binding:"required"` // 1一级商品,2二级商品
|
||||
ProductName string `json:"product_name" binding:"required"`
|
||||
ProductImages string `json:"product_images"`
|
||||
UnitPrice float64 `json:"unit_price" binding:"required"`
|
||||
Quantity int `json:"quantity" binding:"required"`
|
||||
TotalAmount float64 `json:"total_amount" binding:"required"`
|
||||
AddressID uint `json:"address_id" binding:"required"`
|
||||
PaymentProofImage string `json:"payment_proof_image"` // 不再强制要求
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证商品类型
|
||||
if req.ProductType != 1 && req.ProductType != 2 {
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品类型无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证数量和金额
|
||||
if req.Quantity <= 0 {
|
||||
c.JSON(http.StatusOK, common.Error(400, "购买数量必须大于0"))
|
||||
return
|
||||
}
|
||||
|
||||
if req.TotalAmount != req.UnitPrice*float64(req.Quantity) {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单金额计算错误"))
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
|
||||
// 开启事务
|
||||
tx := db.Begin()
|
||||
|
||||
// 验证收货地址是否属于当前用户
|
||||
var address models.UserAddress
|
||||
if err := tx.Where("id = ? AND user_id = ?", req.AddressID, user.ID).First(&address).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(404, "收货地址不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
var sellerID *uint
|
||||
//var productInfo interface{}
|
||||
|
||||
// 根据商品类型进行不同的处理
|
||||
if req.ProductType == 1 { // 一级商品
|
||||
var primaryProduct models.PrimaryProduct
|
||||
if err := tx.First(&primaryProduct, req.ProductID).Error; err != nil {
|
||||
tx.Rollback()
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询商品失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 检查商品状态和库存
|
||||
if primaryProduct.Status != 1 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品已下架"))
|
||||
return
|
||||
}
|
||||
|
||||
if primaryProduct.StockQuantity < req.Quantity {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "库存不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 减少库存,增加销量
|
||||
if err := tx.Model(&primaryProduct).Updates(map[string]interface{}{
|
||||
"stock_quantity": primaryProduct.StockQuantity - req.Quantity,
|
||||
"sales_count": primaryProduct.SalesCount + req.Quantity,
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新商品库存失败"))
|
||||
return
|
||||
}
|
||||
|
||||
//productInfo = primaryProduct
|
||||
|
||||
} else { // 二级商品
|
||||
var secondaryProduct models.SecondaryProduct
|
||||
if err := tx.Preload("Seller").First(&secondaryProduct, req.ProductID).Error; err != nil {
|
||||
tx.Rollback()
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询商品失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 检查商品状态和库存
|
||||
if secondaryProduct.Status != 1 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品已下架"))
|
||||
return
|
||||
}
|
||||
|
||||
if secondaryProduct.Quantity < req.Quantity {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "库存不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否是自己的商品
|
||||
if secondaryProduct.SellerID == user.ID {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "不能购买自己的商品"))
|
||||
return
|
||||
}
|
||||
var config models.SystemConfig
|
||||
tx.Where("config_key=?", "limit_days").First(&config)
|
||||
value, _ := strconv.Atoi(config.ConfigValue)
|
||||
|
||||
if value > 0 {
|
||||
fiveDaysAgo := time.Now().AddDate(0, 0, -value)
|
||||
var existingOrderCount int64
|
||||
if err := tx.Model(&models.PurchaseOrder{}).Where(
|
||||
"buyer_id = ? AND seller_id = ? AND product_type = 2 AND created_at > ? AND order_status != 3",
|
||||
user.ID, secondaryProduct.SellerID, fiveDaysAgo,
|
||||
).Count(&existingOrderCount).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "检查购买记录失败"))
|
||||
return
|
||||
}
|
||||
|
||||
if existingOrderCount > 0 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, fmt.Sprintf("%v天内只能向同一个卖家购买一次商品", value)))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 减少库存,增加浏览量
|
||||
if err := tx.Model(&secondaryProduct).Updates(map[string]interface{}{
|
||||
"quantity": secondaryProduct.Quantity - req.Quantity,
|
||||
"view_count": secondaryProduct.ViewCount + 1,
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新商品库存失败"))
|
||||
return
|
||||
}
|
||||
|
||||
sellerID = &secondaryProduct.SellerID
|
||||
//productInfo = secondaryProduct
|
||||
}
|
||||
|
||||
// 生成订单编号
|
||||
orderNo := fmt.Sprintf("PO%d%d", time.Now().Unix(), user.ID)
|
||||
|
||||
// 创建买单
|
||||
orderStatus := 0 // 默认待付款
|
||||
if req.PaymentProofImage != "" {
|
||||
orderStatus = 1 // 如果有付款凭证,设为待确认
|
||||
}
|
||||
|
||||
purchaseOrder := models.PurchaseOrder{
|
||||
OrderNo: orderNo,
|
||||
BuyerID: user.ID,
|
||||
SellerID: sellerID,
|
||||
ProductID: req.ProductID,
|
||||
ProductType: req.ProductType,
|
||||
ProductName: req.ProductName,
|
||||
ProductImages: req.ProductImages,
|
||||
UnitPrice: req.UnitPrice,
|
||||
Quantity: req.Quantity,
|
||||
TotalAmount: req.TotalAmount,
|
||||
AddressID: &req.AddressID,
|
||||
OrderStatus: uint8(orderStatus),
|
||||
PaymentProofImage: req.PaymentProofImage,
|
||||
Remark: req.Remark,
|
||||
}
|
||||
|
||||
if err := tx.Create(&purchaseOrder).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建订单失败"))
|
||||
return
|
||||
}
|
||||
|
||||
//如果是二级商品,创建对应的卖单
|
||||
if req.ProductType == 2 {
|
||||
salesOrder := models.SalesOrder{
|
||||
OrderNo: fmt.Sprintf("SO%d%d", time.Now().Unix(), *sellerID),
|
||||
SellerID: *sellerID,
|
||||
BuyerID: user.ID,
|
||||
SecondaryProductID: req.ProductID,
|
||||
ProductName: req.ProductName,
|
||||
ProductImages: req.ProductImages,
|
||||
SellingPrice: req.UnitPrice,
|
||||
Quantity: req.Quantity,
|
||||
TotalAmount: req.TotalAmount,
|
||||
AddressID: &req.AddressID,
|
||||
OrderStatus: 0, // 待付款
|
||||
PaymentProofImage: req.PaymentProofImage,
|
||||
PurchaseOrderID: purchaseOrder.ID,
|
||||
}
|
||||
|
||||
if err := tx.Create(&salesOrder).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建卖单失败"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
tx.Commit()
|
||||
|
||||
// 返回订单信息
|
||||
result := map[string]interface{}{
|
||||
"order_id": purchaseOrder.ID,
|
||||
"order_no": purchaseOrder.OrderNo,
|
||||
"status": "订单已提交,等待确认",
|
||||
"message": "订单创建成功",
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetPurchaseOrders 获取买单列表
|
||||
func (h *UserOrderHandler) GetPurchaseOrders(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 50 {
|
||||
pageSize = 10
|
||||
}
|
||||
order_id, _ := strconv.Atoi(c.DefaultQuery("order_id", "0"))
|
||||
// 状态过滤
|
||||
status := c.Query("status")
|
||||
startDate := c.Query("start_date")
|
||||
endDate := c.Query("end_date")
|
||||
|
||||
db := common.GetDB()
|
||||
var orders []models.PurchaseOrder
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.PurchaseOrder{}).Where("buyer_id = ?", user.ID).
|
||||
Preload("Address").Preload("Seller")
|
||||
|
||||
// 添加状态过滤
|
||||
if status != "" && status != "all" {
|
||||
switch status {
|
||||
case "pending":
|
||||
query = query.Where("order_status = 0")
|
||||
case "confirming":
|
||||
query = query.Where("order_status = 1")
|
||||
case "completed":
|
||||
query = query.Where("order_status = 2")
|
||||
case "cancelled":
|
||||
query = query.Where("order_status = 3")
|
||||
}
|
||||
}
|
||||
|
||||
// 添加日期过滤
|
||||
if startDate != "" {
|
||||
query = query.Where("created_at >= ?", startDate+" 00:00:00")
|
||||
}
|
||||
if endDate != "" {
|
||||
query = query.Where("created_at <= ?", endDate+" 23:59:59")
|
||||
}
|
||||
if order_id > 0 {
|
||||
query = query.Where("id = ?", order_id)
|
||||
}
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&orders).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询订单列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 计算总金额
|
||||
var totalAmount float64
|
||||
db.Model(&models.PurchaseOrder{}).Where("buyer_id = ? AND order_status = 2", user.ID).Select("COALESCE(SUM(total_amount), 0)").Scan(&totalAmount)
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": orders,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"total_amount": totalAmount,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetSalesOrders 获取卖单列表
|
||||
func (h *UserOrderHandler) GetSalesOrders(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 50 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 状态过滤
|
||||
status := c.Query("status")
|
||||
startDate := c.Query("start_date")
|
||||
endDate := c.Query("end_date")
|
||||
|
||||
db := common.GetDB()
|
||||
var orders []models.SalesOrder
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.SalesOrder{}).Where("seller_id = ?", user.ID).Preload("Buyer").Preload("Address")
|
||||
|
||||
// 添加状态过滤
|
||||
if status != "" && status != "all" {
|
||||
switch status {
|
||||
case "pending":
|
||||
query = query.Where("order_status = 0")
|
||||
case "confirming":
|
||||
query = query.Where("order_status = 1")
|
||||
case "completed":
|
||||
query = query.Where("order_status = 2")
|
||||
case "cancelled":
|
||||
query = query.Where("order_status = 3")
|
||||
}
|
||||
}
|
||||
|
||||
// 添加日期过滤
|
||||
if startDate != "" {
|
||||
query = query.Where("created_at >= ?", startDate+" 00:00:00")
|
||||
}
|
||||
if endDate != "" {
|
||||
query = query.Where("created_at <= ?", endDate+" 23:59:59")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&orders).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询卖单列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 计算总金额
|
||||
var totalAmount float64
|
||||
db.Model(&models.SalesOrder{}).Where("seller_id = ? AND order_status = 2", user.ID).Select("COALESCE(SUM(total_amount), 0)").Scan(&totalAmount)
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": orders,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"total_amount": totalAmount,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetOrderStats 获取订单统计信息(待付款和待确认数量)
|
||||
func (h *UserOrderHandler) GetOrderStats(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
db := common.GetDB()
|
||||
stats := make(map[string]interface{})
|
||||
|
||||
// 买单统计
|
||||
var purchasePendingCount int64 // 待付款
|
||||
var purchaseConfirmingCount int64 // 待确认
|
||||
db.Model(&models.PurchaseOrder{}).Where("buyer_id = ? AND order_status = ?", user.ID, 0).Count(&purchasePendingCount)
|
||||
db.Model(&models.PurchaseOrder{}).Where("buyer_id = ? AND order_status = ?", user.ID, 1).Count(&purchaseConfirmingCount)
|
||||
|
||||
// 卖单统计
|
||||
var salesPendingCount int64 // 待付款
|
||||
var salesConfirmingCount int64 // 待确认
|
||||
db.Model(&models.SalesOrder{}).Where("seller_id = ? AND order_status = ?", user.ID, 0).Count(&salesPendingCount)
|
||||
db.Model(&models.SalesOrder{}).Where("seller_id = ? AND order_status = ?", user.ID, 1).Count(&salesConfirmingCount)
|
||||
|
||||
stats["purchase_pending_count"] = purchasePendingCount
|
||||
stats["purchase_confirming_count"] = purchaseConfirmingCount
|
||||
stats["sales_pending_count"] = salesPendingCount
|
||||
stats["sales_confirming_count"] = salesConfirmingCount
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(stats))
|
||||
}
|
||||
|
||||
// ConfirmPurchaseOrder 确认买单(买家确认收货)
|
||||
func (h *UserOrderHandler) ConfirmPurchaseOrder(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
tx := db.Begin()
|
||||
|
||||
// 买家确认买单(确认收货)
|
||||
var order models.PurchaseOrder
|
||||
if err := tx.Where("id = ? AND buyer_id = ?", orderID, user.ID).First(&order).Error; err != nil {
|
||||
tx.Rollback()
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询订单失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if order.OrderStatus != 1 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单状态不允许确认"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新订单状态为已完成
|
||||
now := time.Now()
|
||||
if err := tx.Model(&order).Updates(map[string]interface{}{
|
||||
"order_status": 2,
|
||||
"confirm_time": &now,
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "确认订单失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 商品入库
|
||||
if err := h.addToWarehouse(tx, user.ID, &order); err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "商品入库失败"))
|
||||
return
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
result := map[string]interface{}{
|
||||
"message": "订单确认成功,商品已入库",
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// ConfirmSalesOrder 确认卖单(卖家确认发货)
|
||||
func (h *UserOrderHandler) ConfirmSalesOrder(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 卖家确认卖单
|
||||
var order models.SalesOrder
|
||||
tx := common.GetDB().Begin()
|
||||
if err := tx.Where("id = ? AND seller_id = ?", orderID, user.ID).First(&order).Error; err != nil {
|
||||
tx.Rollback()
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询订单失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if order.OrderStatus != 1 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单状态不允许确认"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新卖单状态为已完成
|
||||
now := time.Now()
|
||||
if err := tx.Model(&order).Updates(models.SalesOrder{
|
||||
OrderStatus: 2,
|
||||
CompleteTime: &now,
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "确认订单失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 同时更新对应的买单状态
|
||||
if err := tx.Model(&models.PurchaseOrder{}).Where(
|
||||
"id=?",
|
||||
order.PurchaseOrderID,
|
||||
).Updates(models.PurchaseOrder{
|
||||
OrderStatus: 2,
|
||||
ConfirmTime: &now,
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新买单状态失败"))
|
||||
return
|
||||
}
|
||||
|
||||
var config models.SystemConfig
|
||||
tx.Where("config_key=?", "warehouse_price_rate").First(&config)
|
||||
vaule, _ := strconv.Atoi(config.ConfigValue)
|
||||
newRecord := models.ScoreRecord{
|
||||
UserID: user.ID,
|
||||
Note: "系统策略",
|
||||
ChangeNumber: -vaule,
|
||||
}
|
||||
|
||||
if err := tx.Create(&newRecord).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建积分记录失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新用户积分
|
||||
newPoints := user.CurrentPoints + newRecord.ChangeNumber
|
||||
if newPoints < 0 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "用户积分不足"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Model(&user).Update("current_points", newPoints).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新用户积分失败"))
|
||||
return
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
var purcharseOrder models.PurchaseOrder
|
||||
db := common.GetDB()
|
||||
db.Where("id=?", order.PurchaseOrderID).First(&purcharseOrder)
|
||||
err = h.addToWarehouse(db, order.BuyerID, &purcharseOrder)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "添加用户仓库失败"))
|
||||
}
|
||||
result := map[string]interface{}{
|
||||
"message": "订单确认成功",
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// addToWarehouse 添加商品到买家仓库
|
||||
func (h *UserOrderHandler) addToWarehouse(tx *gorm.DB, buyerID uint, order *models.PurchaseOrder) error {
|
||||
|
||||
fmt.Println("order::", order)
|
||||
// 检查该订单是否已经入库(防止重复入库)
|
||||
var existingByOrder models.UserWarehouse
|
||||
err := tx.Where("user_id = ? AND source_order_id = ? and product_type=? and product_id=?",
|
||||
buyerID, order.ID, order.ProductType, order.ProductID).First(&existingByOrder).Error
|
||||
|
||||
if err == nil {
|
||||
// 该订单已经入库,直接返回
|
||||
return nil
|
||||
} else if err != gorm.ErrRecordNotFound {
|
||||
// 查询出错
|
||||
return err
|
||||
}
|
||||
|
||||
var config models.SystemConfig
|
||||
tx.Where("config_key=?", "warehouse_price_rate").First(&config)
|
||||
value, _ := strconv.Atoi(config.ConfigValue)
|
||||
// 计算入库价格(根据系统配置增值)
|
||||
warehousePrice := order.UnitPrice + float64(value)
|
||||
|
||||
// 检查仓库中是否已有相同商品(不同订单的相同商品)
|
||||
var existingWarehouse models.UserWarehouse
|
||||
//err = tx.Where("user_id = ? AND product_id = ? AND product_type = ?",
|
||||
// buyerID, order.ProductID, order.ProductType).First(&existingWarehouse).Error
|
||||
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
// 创建新的仓库记录
|
||||
warehouse := models.UserWarehouse{
|
||||
UserID: buyerID,
|
||||
ProductID: order.ProductID,
|
||||
ProductType: order.ProductType,
|
||||
ProductName: order.ProductName,
|
||||
ProductImages: order.ProductImages,
|
||||
PurchasePrice: order.UnitPrice,
|
||||
WarehousePrice: warehousePrice,
|
||||
Quantity: order.Quantity,
|
||||
SourceOrderID: &order.ID,
|
||||
Status: 1, // 库存中
|
||||
}
|
||||
|
||||
return tx.Create(&warehouse).Error
|
||||
} else if err != nil {
|
||||
return err
|
||||
} else {
|
||||
// 更新现有仓库记录的数量(累加不同订单的相同商品)
|
||||
return tx.Model(&existingWarehouse).Update("quantity", existingWarehouse.Quantity+order.Quantity).Error
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// UploadPaymentProof 上传付款凭证
|
||||
func (h *UserOrderHandler) UploadPaymentProof(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
PaymentProofImage string `json:"payment_proof_image" binding:"required"`
|
||||
ProductType uint `json:"product_type" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
tx := db.Begin()
|
||||
|
||||
// 查询订单
|
||||
var order models.PurchaseOrder
|
||||
if err := tx.Where("id = ? AND buyer_id = ?", orderID, user.ID).First(&order).Error; err != nil {
|
||||
tx.Rollback()
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询订单失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 检查订单状态
|
||||
if order.OrderStatus != 0 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单状态不允许上传付款凭证"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新订单状态为待确认,并保存付款凭证
|
||||
if err := tx.Model(&order).Updates(map[string]interface{}{
|
||||
"payment_proof_image": req.PaymentProofImage,
|
||||
"order_status": 1, // 待确认
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新订单失败"))
|
||||
return
|
||||
}
|
||||
|
||||
var saleOrder models.SalesOrder
|
||||
if req.ProductType == 2 {
|
||||
|
||||
if err := tx.Where("purchase_order_id = ?", order.ID).First(&saleOrder).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(200, common.Error(400, "查找不到有效订单"))
|
||||
}
|
||||
|
||||
// 再更新,这里可以使用 Model 指定要更新的对象
|
||||
// 注意:这仍然会基于 saleOrder 的主键 ID 更新,但因为我们刚查出来,所以是安全的。
|
||||
// 但更清晰的方式是直接用 Where。
|
||||
|
||||
// 更好的方式是:直接基于原始条件更新
|
||||
if err := tx.Where("purchase_order_id = ?", order.ID).Updates(models.SalesOrder{
|
||||
OrderStatus: 1,
|
||||
PaymentProofImage: req.PaymentProofImage,
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(200, common.Error(400, "更新错误"))
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
result := map[string]interface{}{
|
||||
"message": "付款凭证上传成功,订单已提交等待确认",
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// CancelOrder 取消订单
|
||||
func (h *UserOrderHandler) CancelOrder(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
tx := db.Begin()
|
||||
|
||||
// 查询订单
|
||||
var order models.PurchaseOrder
|
||||
if err := tx.Where("id = ? AND buyer_id = ?", orderID, user.ID).First(&order).Error; err != nil {
|
||||
tx.Rollback()
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询订单失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 检查订单状态(只有待付款和待确认的订单可以取消)
|
||||
if order.OrderStatus != 0 && order.OrderStatus != 1 {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单状态不允许取消"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新订单状态为已取消
|
||||
if err := tx.Model(&order).Update("order_status", 3).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "取消订单失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 恢复商品库存
|
||||
if order.ProductType == 1 { // 一级商品
|
||||
if err := tx.Model(&models.PrimaryProduct{}).Where("id = ?", order.ProductID).
|
||||
Update("stock_quantity", gorm.Expr("stock_quantity + ?", order.Quantity)).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "恢复库存失败"))
|
||||
return
|
||||
}
|
||||
} else { // 二级商品
|
||||
if err := tx.Model(&models.SecondaryProduct{}).Where("id = ?", order.ProductID).
|
||||
Update("quantity", gorm.Expr("quantity + ?", order.Quantity)).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "恢复库存失败"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
result := map[string]interface{}{
|
||||
"message": "订单已取消",
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetPurchaseOrderDetail 获取买单详情
|
||||
func (h *UserOrderHandler) GetPurchaseOrderDetail(c *gin.Context) {
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
var order models.PurchaseOrder
|
||||
query := common.GetDB().Where("buyer_id = ?", user.ID).
|
||||
Preload("Buyer").
|
||||
Preload("Seller").
|
||||
Preload("Address")
|
||||
|
||||
err = query.First(&order, uint(orderID)).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询订单详情失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
adminID := uint(1)
|
||||
if order.ProductType == 1 {
|
||||
if order.SellerID == nil {
|
||||
order.SellerID = &adminID
|
||||
var seller models.User
|
||||
|
||||
common.GetDB().Where("id=?", order.SellerID).First(&seller)
|
||||
order.Seller = &seller
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(order))
|
||||
}
|
||||
|
||||
// GetSalesOrderDetail 获取卖单详情
|
||||
func (h *UserOrderHandler) GetSalesOrderDetail(c *gin.Context) {
|
||||
orderID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
var order models.SalesOrder
|
||||
query := common.GetDB().Where("seller_id = ?", user.ID).
|
||||
Preload("Seller").
|
||||
Preload("Buyer").
|
||||
Preload("Address")
|
||||
|
||||
err = query.First(&order, uint(orderID)).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询订单详情失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(order))
|
||||
}
|
||||
@@ -0,0 +1,220 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserOrderMessageHandler 用户端订单留言处理器
|
||||
type UserOrderMessageHandler struct{}
|
||||
|
||||
// GetOrderMessages 获取订单留言列表
|
||||
func (h *UserOrderMessageHandler) GetOrderMessages(c *gin.Context) {
|
||||
|
||||
orderID, err := strconv.ParseUint(c.Param("order_id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
orderType := c.Query("type") // purchase 或 sales
|
||||
if orderType == "" {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单类型无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 50 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
var messages []models.OrderMessage
|
||||
var total int64
|
||||
|
||||
// 如果是买单,需要同时查询买单和对应卖单的留言
|
||||
var query *gorm.DB
|
||||
if orderType == "purchase" {
|
||||
// 查找是否有对应的卖单
|
||||
var salesOrder models.SalesOrder
|
||||
err := db.Where("purchase_order_id = ?", orderID).First(&salesOrder).Error
|
||||
if err == nil {
|
||||
// 有对应卖单,查询两个订单的留言
|
||||
query = db.Model(&models.OrderMessage{}).
|
||||
Where("(order_id = ? AND order_type = ?) OR (order_id = ? AND order_type = ?)",
|
||||
orderID, "purchase", salesOrder.ID, "sales")
|
||||
} else {
|
||||
// 没有对应卖单,只查询买单留言
|
||||
query = db.Model(&models.OrderMessage{}).
|
||||
Where("order_id = ? AND order_type = ?", orderID, "purchase")
|
||||
}
|
||||
} else {
|
||||
// 卖单,查询卖单和对应买单的留言
|
||||
var salesOrder models.SalesOrder
|
||||
err := db.Where("id = ?", orderID).First(&salesOrder).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "订单不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
query = db.Model(&models.OrderMessage{}).
|
||||
Where("(order_id = ? AND order_type = ?) OR (order_id = ? AND order_type = ?)",
|
||||
salesOrder.ID, "sales", salesOrder.PurchaseOrderID, "purchase")
|
||||
}
|
||||
|
||||
query = query.Preload("User", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Select("id, customer_name, real_name, phone, system_role")
|
||||
})
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err = query.Order("created_at ASC").Offset(offset).Limit(pageSize).Find(&messages).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询留言列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": messages,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// CreateOrderMessage 创建订单留言
|
||||
func (h *UserOrderMessageHandler) CreateOrderMessage(c *gin.Context) {
|
||||
orderID, err := strconv.ParseUint(c.Param("order_id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "订单ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 验证用户是否有权限给此订单留言
|
||||
orderType := h.getUserOrderType(uint(orderID), user.ID)
|
||||
if orderType == "" {
|
||||
c.JSON(http.StatusOK, common.Error(403, "权限不足"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Content string `json:"content" binding:"required"`
|
||||
Images string `json:"images"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 验证留言内容
|
||||
if len(req.Content) > 500 {
|
||||
c.JSON(http.StatusOK, common.Error(400, "留言内容不能超过500字"))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建留言记录
|
||||
message := models.OrderMessage{
|
||||
OrderID: uint(orderID),
|
||||
OrderType: orderType,
|
||||
UserID: user.ID,
|
||||
Content: req.Content,
|
||||
Images: req.Images,
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
if err := db.Create(&message).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建留言失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 预加载用户信息
|
||||
db.Preload("User", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Select("id, customer_name, real_name, phone")
|
||||
}).First(&message, message.ID)
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(message))
|
||||
}
|
||||
|
||||
// checkOrderPermission 检查用户是否有权限访问订单留言
|
||||
func (h *UserOrderMessageHandler) checkOrderPermission(orderID uint, userID uint) bool {
|
||||
db := common.GetDB()
|
||||
|
||||
// 检查买单权限
|
||||
var purchaseCount int64
|
||||
db.Model(&models.PurchaseOrder{}).Where("id = ? AND buyer_id = ?", orderID, userID).Count(&purchaseCount)
|
||||
if purchaseCount > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查卖单权限
|
||||
var salesCount int64
|
||||
db.Model(&models.SalesOrder{}).Where("id = ? AND seller_id = ?", orderID, userID).Count(&salesCount)
|
||||
if salesCount > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查是否为买单的卖家(二级商品订单)
|
||||
var purchaseOrder models.PurchaseOrder
|
||||
if err := db.Where("id = ? AND product_type = 2", orderID).First(&purchaseOrder).Error; err == nil {
|
||||
// 查找对应的二级商品是否属于当前用户
|
||||
var secondaryCount int64
|
||||
db.Model(&models.SecondaryProduct{}).Where("id = ? AND seller_id = ?", purchaseOrder.ProductID, userID).Count(&secondaryCount)
|
||||
if secondaryCount > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// getUserOrderType 获取用户在订单中的角色类型
|
||||
func (h *UserOrderMessageHandler) getUserOrderType(orderID uint, userID uint) string {
|
||||
db := common.GetDB()
|
||||
|
||||
// 检查是否为买单的买家
|
||||
var purchaseCount int64
|
||||
db.Model(&models.PurchaseOrder{}).Where("id = ? AND buyer_id = ?", orderID, userID).Count(&purchaseCount)
|
||||
if purchaseCount > 0 {
|
||||
return "purchase"
|
||||
}
|
||||
|
||||
// 检查是否为卖单的卖家
|
||||
var salesCount int64
|
||||
db.Model(&models.SalesOrder{}).Where("id = ? AND (seller_id = ?)", orderID, userID).Count(&salesCount)
|
||||
if salesCount > 0 || userID == 1 {
|
||||
return "sales"
|
||||
}
|
||||
|
||||
// 检查是否为买单的卖家(二级商品订单)
|
||||
var purchaseOrder models.PurchaseOrder
|
||||
if err := db.Where("id = ? AND product_type = 2", orderID).First(&purchaseOrder).Error; err == nil {
|
||||
// 查找对应的二级商品是否属于当前用户
|
||||
var secondaryCount int64
|
||||
db.Model(&models.SecondaryProduct{}).Where("id = ? AND seller_id = ?", purchaseOrder.ProductID, userID).Count(&secondaryCount)
|
||||
if secondaryCount > 0 {
|
||||
return "purchase" // 对于买单的卖家,也归类为purchase类型
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserPaymentHandler 用户收款方式处理器
|
||||
type UserPaymentHandler struct{}
|
||||
|
||||
// GetPaymentInfo 获取用户收款方式信息
|
||||
func (h *UserPaymentHandler) GetPaymentInfo(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 返回收款方式信息
|
||||
paymentInfo := map[string]interface{}{
|
||||
"main_payment_qr_image": user.MainPaymentQrImage,
|
||||
"sub_payment_qr_image": user.SubPaymentQrImage,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(paymentInfo))
|
||||
}
|
||||
|
||||
// UpdatePaymentInfo 更新用户收款方式信息
|
||||
func (h *UserPaymentHandler) UpdatePaymentInfo(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 解析请求数据
|
||||
var req struct {
|
||||
MainPaymentQrImage string `json:"main_payment_qr_image"`
|
||||
SubPaymentQrImage string `json:"sub_payment_qr_image"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新用户收款方式信息
|
||||
updates := map[string]interface{}{}
|
||||
if req.MainPaymentQrImage != "" {
|
||||
updates["main_payment_qr_image"] = req.MainPaymentQrImage
|
||||
}
|
||||
if req.SubPaymentQrImage != "" {
|
||||
updates["sub_payment_qr_image"] = req.SubPaymentQrImage
|
||||
}
|
||||
|
||||
if len(updates) == 0 {
|
||||
c.JSON(http.StatusOK, common.Error(400, "没有要更新的信息"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := common.GetDB().Model(&user).Updates(updates).Error; err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新收款方式失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 返回更新后的信息
|
||||
var updatedUser models.User
|
||||
common.GetDB().First(&updatedUser, user.ID)
|
||||
|
||||
paymentInfo := map[string]interface{}{
|
||||
"main_payment_qr_image": updatedUser.MainPaymentQrImage,
|
||||
"sub_payment_qr_image": updatedUser.SubPaymentQrImage,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(paymentInfo))
|
||||
}
|
||||
|
||||
// GetSellerPaymentInfo 获取卖家收款方式信息
|
||||
func (h *UserPaymentHandler) GetSellerPaymentInfo(c *gin.Context) {
|
||||
sellerID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "卖家ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var seller models.User
|
||||
err = common.GetDB().Select("main_payment_qr_image, sub_payment_qr_image").First(&seller, sellerID).Error
|
||||
if err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
c.JSON(http.StatusOK, common.Error(404, "卖家不存在"))
|
||||
} else {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询卖家信息失败"))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 返回卖家收款方式信息
|
||||
paymentInfo := map[string]interface{}{
|
||||
"main_payment_qr_image": seller.MainPaymentQrImage,
|
||||
"sub_payment_qr_image": seller.SubPaymentQrImage,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(paymentInfo))
|
||||
}
|
||||
|
||||
func (h *UserPaymentHandler) GetSellerInfo(c *gin.Context) {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// UserProductHandler 用户端商品处理器
|
||||
type UserProductHandler struct{}
|
||||
|
||||
// GetHomeData 获取首页数据(轮播图、商品分类和一级商品)
|
||||
func (h *UserProductHandler) GetHomeData(c *gin.Context) {
|
||||
db := common.GetDB()
|
||||
|
||||
// 获取启用的轮播图,按排序排列
|
||||
var banners []models.Banner
|
||||
err := db.Where("status = ?", 1).Order("sort_order ASC, created_at DESC").Find(&banners).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询轮播图失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 获取启用的商品分类,按排序排列
|
||||
var categories []models.ProductCategory
|
||||
err = db.Where("status = ?", 1).Order("sort_order ASC, created_at DESC").Find(&categories).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询商品分类失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 获取上架的一级商品,按排序排列,限制数量
|
||||
var primaryProducts []models.PrimaryProduct
|
||||
err = db.Where("status = ?", 1).
|
||||
Preload("Category").
|
||||
Order("is_hot desc, sort_order ASC, created_at DESC").
|
||||
Find(&primaryProducts).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询一级商品失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"banners": banners,
|
||||
"categories": categories,
|
||||
"primary_products": primaryProducts,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetProductCategories 获取商品分类列表
|
||||
func (h *UserProductHandler) GetProductCategories(c *gin.Context) {
|
||||
var categories []models.ProductCategory
|
||||
|
||||
err := common.GetDB().Where("status = ?", 1).Order("sort_order ASC, created_at DESC").Find(&categories).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询商品分类失败"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(categories))
|
||||
}
|
||||
|
||||
// GetPrimaryProducts 获取一级商品列表
|
||||
func (h *UserProductHandler) GetPrimaryProducts(c *gin.Context) {
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 50 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
categoryID := c.Query("category_id")
|
||||
productName := c.Query("product_name")
|
||||
sortType := c.DefaultQuery("sort", "default") // default, price_asc, price_desc, sales, views
|
||||
|
||||
db := common.GetDB()
|
||||
var products []models.PrimaryProduct
|
||||
var total int64
|
||||
|
||||
// 构建查询条件 - 只查询上架商品
|
||||
query := db.Model(&models.PrimaryProduct{}).Where("status = ?", 1).Preload("Category")
|
||||
|
||||
// 添加搜索条件
|
||||
if categoryID != "" {
|
||||
query = query.Where("category_id = ?", categoryID)
|
||||
}
|
||||
if productName != "" {
|
||||
query = query.Where("product_name LIKE ?", "%"+productName+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 排序
|
||||
switch sortType {
|
||||
case "price_asc":
|
||||
query = query.Order("current_price ASC")
|
||||
case "price_desc":
|
||||
query = query.Order("current_price DESC")
|
||||
case "sales":
|
||||
query = query.Order("sales_count DESC")
|
||||
case "views":
|
||||
query = query.Order("view_count DESC")
|
||||
default:
|
||||
query = query.Order("sort_order ASC, created_at DESC")
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Find(&products).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询商品列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": products,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetPrimaryProduct 获取一级商品详情
|
||||
func (h *UserProductHandler) GetPrimaryProduct(c *gin.Context) {
|
||||
productID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var product models.PrimaryProduct
|
||||
err = common.GetDB().Where("status = ?", 1).Preload("Category").First(&product, uint(productID)).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品不存在或已下架"))
|
||||
return
|
||||
}
|
||||
|
||||
// 增加浏览量
|
||||
common.GetDB().Model(&product).UpdateColumn("view_count", product.ViewCount+1)
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(product))
|
||||
}
|
||||
|
||||
// GetSecondaryProducts 获取二级商品列表(用户挂售的商品)
|
||||
func (h *UserProductHandler) GetSecondaryProducts(c *gin.Context) {
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 50 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
productName := c.Query("product_name")
|
||||
sortType := c.DefaultQuery("sort", "default") // default, price_asc, price_desc, views
|
||||
categoryId, _ := strconv.Atoi(c.DefaultQuery("category_id", "0"))
|
||||
|
||||
// 地址筛选参数
|
||||
province := c.Query("province")
|
||||
city := c.Query("city")
|
||||
district := c.Query("district")
|
||||
|
||||
db := common.GetDB()
|
||||
var products []models.SecondaryProduct
|
||||
var total int64
|
||||
|
||||
// 构建查询条件 - 只查询上架商品,并预加载卖家信息
|
||||
query := db.Model(&models.SecondaryProduct{}).Where("status = ? and quantity>0", 1).
|
||||
Preload("Seller")
|
||||
|
||||
// 添加商品名称搜索条件
|
||||
if productName != "" {
|
||||
query = query.Where("product_name LIKE ?", "%"+productName+"%")
|
||||
}
|
||||
|
||||
// 添加分类筛选条件
|
||||
if categoryId > 0 {
|
||||
query = query.Where("category_id = ?", categoryId)
|
||||
}
|
||||
|
||||
// 添加地址筛选条件 - 根据卖家的默认地址筛选
|
||||
if province != "" || city != "" || district != "" {
|
||||
// 子查询获取符合地址条件的用户ID
|
||||
subQuery := db.Model(&models.UserAddress{}).
|
||||
Select("user_id").
|
||||
Where("is_default = ?", 1)
|
||||
|
||||
if province != "" {
|
||||
subQuery = subQuery.Where("province = ?", province)
|
||||
}
|
||||
if city != "" {
|
||||
subQuery = subQuery.Where("city = ?", city)
|
||||
}
|
||||
if district != "" {
|
||||
subQuery = subQuery.Where("district = ?", district)
|
||||
}
|
||||
|
||||
query = query.Where("seller_id IN (?)", subQuery)
|
||||
}
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 排序
|
||||
switch sortType {
|
||||
case "price":
|
||||
query = query.Order("selling_price ASC")
|
||||
case "price_desc":
|
||||
query = query.Order("selling_price DESC")
|
||||
case "view":
|
||||
query = query.Order("view_count DESC")
|
||||
case "sales":
|
||||
query = query.Order("sales_count DESC")
|
||||
case "quantity":
|
||||
query = query.Order("quantity DESC")
|
||||
default:
|
||||
query = query.Order("created_at DESC")
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Find(&products).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询商品列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 获取当前用户信息
|
||||
currentUser, exists := c.Get("current_user")
|
||||
var userID uint
|
||||
if exists {
|
||||
user := currentUser.(models.User)
|
||||
userID = user.ID
|
||||
}
|
||||
|
||||
// 获取系统配置limit_days
|
||||
var config models.SystemConfig
|
||||
db.Where("config_key=?", "limit_days").First(&config)
|
||||
limitDays, _ := strconv.Atoi(config.ConfigValue)
|
||||
|
||||
// 为每个商品添加购买限制检查
|
||||
type ProductWithPurchaseLimit struct {
|
||||
models.SecondaryProduct
|
||||
CanPurchase bool `json:"can_purchase"`
|
||||
PurchaseLimitMessage string `json:"purchase_limit_message"`
|
||||
}
|
||||
|
||||
productList := make([]ProductWithPurchaseLimit, 0, len(products))
|
||||
for _, product := range products {
|
||||
productWithLimit := ProductWithPurchaseLimit{
|
||||
SecondaryProduct: product,
|
||||
CanPurchase: true,
|
||||
PurchaseLimitMessage: "",
|
||||
}
|
||||
|
||||
// 如果用户已登录,检查购买限制
|
||||
if exists && userID > 0 {
|
||||
// 检查是否是自己的商品
|
||||
if product.SellerID == userID {
|
||||
productWithLimit.CanPurchase = false
|
||||
productWithLimit.PurchaseLimitMessage = "不能购买自己的商品"
|
||||
} else if limitDays > 0 {
|
||||
// 检查是否在限制天数内已经购买过该卖家的商品
|
||||
limitDaysAgo := time.Now().AddDate(0, 0, -limitDays)
|
||||
var existingOrderCount int64
|
||||
db.Model(&models.PurchaseOrder{}).Where(
|
||||
"buyer_id = ? AND seller_id = ? AND product_type = 2 AND created_at > ? AND order_status != 3",
|
||||
userID, product.SellerID, limitDaysAgo,
|
||||
).Count(&existingOrderCount)
|
||||
|
||||
if existingOrderCount > 0 {
|
||||
productWithLimit.CanPurchase = false
|
||||
productWithLimit.PurchaseLimitMessage = fmt.Sprintf("%v天内只能向同一个卖家购买一次商品", limitDays)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
productList = append(productList, productWithLimit)
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": productList,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetSecondaryProduct 获取二级商品详情
|
||||
func (h *UserProductHandler) GetSecondaryProduct(c *gin.Context) {
|
||||
productID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var product models.SecondaryProduct
|
||||
err = common.GetDB().Where("status = ?", 1).Preload("Seller").First(&product, uint(productID)).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "商品不存在或已下架"))
|
||||
return
|
||||
}
|
||||
|
||||
// 增加浏览量
|
||||
common.GetDB().Model(&product).UpdateColumn("view_count", product.ViewCount+1)
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(product))
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// UserReconciliationHandler 用户端对账管理处理器
|
||||
type UserReconciliationHandler struct{}
|
||||
|
||||
// GetReconciliationStats 获取对账统计数据
|
||||
func (h *UserReconciliationHandler) GetReconciliationStats(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 获取日期参数
|
||||
startDate := c.Query("start_date")
|
||||
endDate := c.Query("end_date")
|
||||
|
||||
// 如果没有传入日期,默认为今天
|
||||
if startDate == "" || endDate == "" {
|
||||
today := time.Now().Format("2006-01-02")
|
||||
startDate = today
|
||||
endDate = today
|
||||
}
|
||||
|
||||
// 解析日期
|
||||
startTime, err := time.Parse("2006-01-02", startDate)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "开始日期格式错误"))
|
||||
return
|
||||
}
|
||||
endTime, err := time.Parse("2006-01-02", endDate)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "结束日期格式错误"))
|
||||
return
|
||||
}
|
||||
|
||||
// 设置时间范围
|
||||
startTime = time.Date(startTime.Year(), startTime.Month(), startTime.Day(), 0, 0, 0, 0, startTime.Location())
|
||||
endTime = time.Date(endTime.Year(), endTime.Month(), endTime.Day(), 23, 59, 59, 999999999, endTime.Location())
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"primary_confirmed": h.getPrimaryMarketStats(user.ID, startTime, endTime, 2), // 已确认
|
||||
"primary_pending": h.getPrimaryMarketStats(user.ID, startTime, endTime, 1), // 待确认
|
||||
"secondary_buy_confirmed": h.getSecondaryBuyStats(user.ID, startTime, endTime, 2), // 二级市场买单已确认
|
||||
"secondary_buy_pending": h.getSecondaryBuyStats(user.ID, startTime, endTime, 1), // 二级市场买单待确认
|
||||
"secondary_sell_confirmed": h.getSecondarySellStats(user.ID, startTime, endTime, 2), // 二级市场卖单已确认
|
||||
"secondary_sell_pending": h.getSecondarySellStats(user.ID, startTime, endTime, 1), // 二级市场卖单待确认
|
||||
"warehouse_active": h.getWarehouseStats(user.ID, 1), // 寄售中
|
||||
"warehouse_inactive": h.getWarehouseStats(user.ID, 0), // 未寄售
|
||||
"score_change": h.getScoreChangeStats(user.ID, startTime, endTime), // 积分变更
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// getPrimaryMarketStats 获取一级市场统计数据
|
||||
func (h *UserReconciliationHandler) getPrimaryMarketStats(userID uint, startTime, endTime time.Time, status int) map[string]interface{} {
|
||||
var totalAmount float64
|
||||
var totalQuantity int64
|
||||
db := common.GetDB()
|
||||
// 查询一级商品订单统计
|
||||
db.Model(&models.PurchaseOrder{}).
|
||||
Where("buyer_id = ? AND product_type = ? AND order_status = ? AND created_at BETWEEN ? AND ?",
|
||||
userID, 1, status, startTime, endTime).
|
||||
Select("COALESCE(SUM(total_amount), 0) as total_amount, COALESCE(SUM(quantity), 0) as total_quantity").
|
||||
Row().Scan(&totalAmount, &totalQuantity)
|
||||
|
||||
return map[string]interface{}{
|
||||
"amount": totalAmount,
|
||||
"quantity": totalQuantity,
|
||||
}
|
||||
}
|
||||
|
||||
// getSecondaryBuyStats 获取二级市场买单统计数据
|
||||
func (h *UserReconciliationHandler) getSecondaryBuyStats(userID uint, startTime, endTime time.Time, status int) map[string]interface{} {
|
||||
var totalAmount float64
|
||||
var totalQuantity int64
|
||||
db := common.GetDB()
|
||||
// 查询二级商品买单统计
|
||||
db.Model(&models.PurchaseOrder{}).
|
||||
Where("buyer_id = ? AND product_type = ? AND order_status = ? AND created_at BETWEEN ? AND ?",
|
||||
userID, 2, status, startTime, endTime).
|
||||
Select("COALESCE(SUM(total_amount), 0) as total_amount, COALESCE(SUM(quantity), 0) as total_quantity").
|
||||
Row().Scan(&totalAmount, &totalQuantity)
|
||||
|
||||
return map[string]interface{}{
|
||||
"amount": totalAmount,
|
||||
"quantity": totalQuantity,
|
||||
}
|
||||
}
|
||||
|
||||
// getSecondarySellStats 获取二级市场卖单统计数据
|
||||
func (h *UserReconciliationHandler) getSecondarySellStats(userID uint, startTime, endTime time.Time, status int) map[string]interface{} {
|
||||
var totalAmount float64
|
||||
var totalQuantity int64
|
||||
db := common.GetDB()
|
||||
// 查询二级商品卖单统计
|
||||
db.Model(&models.SalesOrder{}).
|
||||
Where("seller_id = ? AND order_status = ? AND created_at BETWEEN ? AND ?",
|
||||
userID, status, startTime, endTime).
|
||||
Select("COALESCE(SUM(total_amount), 0) as total_amount, COALESCE(SUM(quantity), 0) as total_quantity").
|
||||
Row().Scan(&totalAmount, &totalQuantity)
|
||||
|
||||
return map[string]interface{}{
|
||||
"amount": totalAmount,
|
||||
"quantity": totalQuantity,
|
||||
}
|
||||
}
|
||||
|
||||
// getWarehouseStats 获取仓库商品统计数据
|
||||
func (h *UserReconciliationHandler) getWarehouseStats(userID uint, status int) map[string]interface{} {
|
||||
var totalAmount float64
|
||||
var totalQuantity int64
|
||||
db := common.GetDB()
|
||||
// 查询仓库商品统计
|
||||
db.Model(&models.UserWarehouse{}).
|
||||
Where("user_id = ? AND status = ?", userID, status).
|
||||
Select("COALESCE(SUM(warehouse_price * quantity), 0) as total_amount, COALESCE(SUM(quantity), 0) as total_quantity").
|
||||
Row().Scan(&totalAmount, &totalQuantity)
|
||||
|
||||
return map[string]interface{}{
|
||||
"amount": totalAmount,
|
||||
"quantity": totalQuantity,
|
||||
}
|
||||
}
|
||||
|
||||
// getScoreChangeStats 获取积分变更统计数据
|
||||
func (h *UserReconciliationHandler) getScoreChangeStats(userID uint, startTime, endTime time.Time) map[string]interface{} {
|
||||
var increaseScore int64
|
||||
var decreaseScore int64
|
||||
db := common.GetDB()
|
||||
// 查询增加的积分
|
||||
db.Model(&models.ScoreRecord{}).
|
||||
Where("user_id = ? AND change_number > 0 AND created_at BETWEEN ? AND ?",
|
||||
userID, startTime, endTime).
|
||||
Select("COALESCE(SUM(change_number), 0)").
|
||||
Row().Scan(&increaseScore)
|
||||
|
||||
// 查询减少的积分(取绝对值)
|
||||
db.Model(&models.ScoreRecord{}).
|
||||
Where("user_id = ? AND change_number < 0 AND created_at BETWEEN ? AND ?",
|
||||
userID, startTime, endTime).
|
||||
Select("COALESCE(ABS(SUM(change_number)), 0)").
|
||||
Row().Scan(&decreaseScore)
|
||||
|
||||
return map[string]interface{}{
|
||||
"increase": increaseScore,
|
||||
"decrease": decreaseScore,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// UserScoreHandler 用户端积分处理器
|
||||
type UserScoreHandler struct{}
|
||||
|
||||
// GetMyScoreRecords 获取我的积分记录列表
|
||||
func (h *UserScoreHandler) GetMyScoreRecords(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 50 {
|
||||
pageSize = 20
|
||||
}
|
||||
|
||||
// 搜索参数
|
||||
changeType := c.Query("change_type") // all, positive, negative
|
||||
|
||||
db := common.GetDB()
|
||||
var records []models.ScoreRecord
|
||||
var total int64
|
||||
|
||||
// 构建查询条件 - 只查询当前用户的积分记录
|
||||
query := db.Model(&models.ScoreRecord{}).Where("user_id = ?", user.ID)
|
||||
|
||||
// 添加积分变化类型筛选
|
||||
if changeType == "positive" {
|
||||
query = query.Where("change_number > 0")
|
||||
} else if changeType == "negative" {
|
||||
query = query.Where("change_number < 0")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&records).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询积分记录失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 计算统计信息
|
||||
var totalIncome int // 总收入积分
|
||||
var totalExpense int // 总支出积分
|
||||
var currentPoints int // 当前积分
|
||||
|
||||
currentPoints = user.CurrentPoints
|
||||
|
||||
// 统计总收入和总支出
|
||||
db.Model(&models.ScoreRecord{}).Where("user_id = ? AND change_number > 0", user.ID).
|
||||
Select("COALESCE(SUM(change_number), 0)").Scan(&totalIncome)
|
||||
db.Model(&models.ScoreRecord{}).Where("user_id = ? AND change_number < 0", user.ID).
|
||||
Select("COALESCE(SUM(ABS(change_number)), 0)").Scan(&totalExpense)
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": records,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"current_points": currentPoints,
|
||||
"total_income": totalIncome,
|
||||
"total_expense": totalExpense,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetMyScoreStats 获取我的积分统计信息
|
||||
func (h *UserScoreHandler) GetMyScoreStats(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
db := common.GetDB()
|
||||
|
||||
// 当前积分
|
||||
currentPoints := user.CurrentPoints
|
||||
|
||||
// 总收入积分
|
||||
var totalIncome int
|
||||
db.Model(&models.ScoreRecord{}).Where("user_id = ? AND change_number > 0", user.ID).
|
||||
Select("COALESCE(SUM(change_number), 0)").Scan(&totalIncome)
|
||||
|
||||
// 总支出积分
|
||||
var totalExpense int
|
||||
db.Model(&models.ScoreRecord{}).Where("user_id = ? AND change_number < 0", user.ID).
|
||||
Select("COALESCE(SUM(ABS(change_number)), 0)").Scan(&totalExpense)
|
||||
|
||||
// 今日获得积分
|
||||
var todayIncome int
|
||||
db.Model(&models.ScoreRecord{}).Where("user_id = ? AND change_number > 0 AND DATE(created_at) = CURDATE()", user.ID).
|
||||
Select("COALESCE(SUM(change_number), 0)").Scan(&todayIncome)
|
||||
|
||||
// 本月获得积分
|
||||
var monthIncome int
|
||||
db.Model(&models.ScoreRecord{}).Where("user_id = ? AND change_number > 0 AND YEAR(created_at) = YEAR(NOW()) AND MONTH(created_at) = MONTH(NOW())", user.ID).
|
||||
Select("COALESCE(SUM(change_number), 0)").Scan(&monthIncome)
|
||||
|
||||
// 最近的积分记录(5条)
|
||||
var recentRecords []models.ScoreRecord
|
||||
db.Model(&models.ScoreRecord{}).Where("user_id = ?", user.ID).
|
||||
Order("created_at DESC").Limit(5).Find(&recentRecords)
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"current_points": currentPoints,
|
||||
"total_income": totalIncome,
|
||||
"total_expense": totalExpense,
|
||||
"today_income": todayIncome,
|
||||
"month_income": monthIncome,
|
||||
"recent_records": recentRecords,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// UserWarehouseHandler 用户仓库处理器
|
||||
type UserWarehouseHandler struct{}
|
||||
|
||||
// GetWarehouseList 获取用户仓库商品列表
|
||||
func (h *UserWarehouseHandler) GetWarehouseList(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 分页参数
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 || pageSize > 50 {
|
||||
pageSize = 10
|
||||
}
|
||||
|
||||
// 筛选参数
|
||||
productType := c.Query("product_type")
|
||||
status := c.Query("status")
|
||||
productName := c.Query("product_name")
|
||||
|
||||
db := common.GetDB()
|
||||
var warehouses []models.UserWarehouse
|
||||
var total int64
|
||||
|
||||
// 构建查询条件
|
||||
query := db.Model(&models.UserWarehouse{}).Where("user_id = ? and quantity>0", user.ID)
|
||||
|
||||
// 添加筛选条件
|
||||
if productType != "" {
|
||||
query = query.Where("product_type = ?", productType)
|
||||
}
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
if productName != "" {
|
||||
query = query.Where("product_name LIKE ?", "%"+productName+"%")
|
||||
}
|
||||
|
||||
// 获取总数
|
||||
query.Count(&total)
|
||||
|
||||
// 分页查询
|
||||
offset := (page - 1) * pageSize
|
||||
err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&warehouses).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(500, "查询仓库商品列表失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 计算总价值
|
||||
var totalValue float64
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? AND status = 1", user.ID).
|
||||
Select("COALESCE(SUM(warehouse_price * quantity), 0)").Scan(&totalValue)
|
||||
|
||||
// 构建返回数据
|
||||
result := map[string]interface{}{
|
||||
"list": warehouses,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"total_value": totalValue,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetWarehouseItem 获取单个仓库商品详情
|
||||
func (h *UserWarehouseHandler) GetWarehouseItem(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
itemID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var warehouse models.UserWarehouse
|
||||
err = common.GetDB().Where("id = ? AND user_id = ?", itemID, user.ID).First(&warehouse).Error
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(404, "仓库商品不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(warehouse))
|
||||
}
|
||||
|
||||
// SellWarehouseItem 出售仓库商品
|
||||
func (h *UserWarehouseHandler) SellWarehouseItem(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
itemID, err := strconv.ParseUint(c.Param("id"), 10, 32)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "商品ID无效"))
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Quantity int `json:"quantity" binding:"required,min=1"`
|
||||
Price float64 `json:"price" binding:"required,gt=0"`
|
||||
Description string `json:"description" binding:"required"`
|
||||
Condition string `json:"condition" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusOK, common.Error(400, "请求参数错误"))
|
||||
return
|
||||
}
|
||||
|
||||
db := common.GetDB()
|
||||
tx := db.Begin()
|
||||
|
||||
// 查询仓库商品
|
||||
var warehouse models.UserWarehouse
|
||||
if err := tx.Where("id = ? AND user_id = ? AND status = 1", itemID, user.ID).First(&warehouse).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(404, "仓库商品不存在或已出售"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查库存数量
|
||||
if warehouse.Quantity < req.Quantity {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(400, "库存数量不足"))
|
||||
return
|
||||
}
|
||||
|
||||
// 创建二级商品上架
|
||||
secondaryProduct := models.SecondaryProduct{
|
||||
SellerID: user.ID,
|
||||
OriginalProductID: &warehouse.ProductID,
|
||||
OriginalProductType: warehouse.ProductType,
|
||||
ProductName: warehouse.ProductName,
|
||||
ProductDescription: req.Description,
|
||||
ProductImages: warehouse.ProductImages,
|
||||
CostPrice: warehouse.PurchasePrice,
|
||||
SellingPrice: req.Price,
|
||||
Quantity: req.Quantity,
|
||||
Status: 1, // 上架状态
|
||||
ViewCount: 0,
|
||||
CategoryID: 1, // 默认分类,可以后续优化
|
||||
ProductCondition: req.Condition,
|
||||
UserWarehouseID: uint(itemID),
|
||||
}
|
||||
|
||||
if secondaryProduct.OriginalProductType == 1 {
|
||||
var primaryProduct models.PrimaryProduct
|
||||
err = tx.Where("id=?", secondaryProduct.OriginalProductID).First(&primaryProduct).Error
|
||||
if err == nil {
|
||||
secondaryProduct.CategoryID = primaryProduct.CategoryID
|
||||
}
|
||||
} else {
|
||||
var sec models.SecondaryProduct
|
||||
err = tx.Where("id=?", secondaryProduct.OriginalProductID).First(&sec).Error
|
||||
if err == nil {
|
||||
secondaryProduct.CategoryID = sec.CategoryID
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Create(&secondaryProduct).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "创建出售商品失败"))
|
||||
return
|
||||
}
|
||||
|
||||
// 更新仓库商品数量
|
||||
if warehouse.Quantity == req.Quantity {
|
||||
// 全部出售,更新状态为已出售
|
||||
if err := tx.Model(&warehouse).Updates(map[string]interface{}{
|
||||
"quantity": 0,
|
||||
"status": 0, // 已出售
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新仓库商品状态失败"))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// 部分出售,减少数量
|
||||
if err := tx.Model(&warehouse).Update("quantity", warehouse.Quantity-req.Quantity).Error; err != nil {
|
||||
tx.Rollback()
|
||||
c.JSON(http.StatusOK, common.Error(500, "更新仓库商品数量失败"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
tx.Commit()
|
||||
|
||||
result := map[string]interface{}{
|
||||
"message": "商品挂售成功",
|
||||
"secondary_product": secondaryProduct,
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(result))
|
||||
}
|
||||
|
||||
// GetWarehouseStats 获取仓库统计信息
|
||||
func (h *UserWarehouseHandler) GetWarehouseStats(c *gin.Context) {
|
||||
currentUser, _ := c.Get("current_user")
|
||||
user := currentUser.(models.User)
|
||||
|
||||
db := common.GetDB()
|
||||
stats := make(map[string]interface{})
|
||||
|
||||
// 库存中商品数量
|
||||
var stockCount int64
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? AND status = 1", user.ID).Count(&stockCount)
|
||||
|
||||
// 已出售商品数量
|
||||
var soldCount int64
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? AND status = 0", user.ID).Count(&soldCount)
|
||||
|
||||
// 库存总价值
|
||||
var totalValue float64
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? AND status = 1", user.ID).
|
||||
Select("COALESCE(SUM(warehouse_price * quantity), 0)").Scan(&totalValue)
|
||||
|
||||
// 一级商品数量
|
||||
var primaryCount int64
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? AND product_type = 1 AND status = 1", user.ID).Count(&primaryCount)
|
||||
|
||||
// 二级商品数量
|
||||
var secondaryCount int64
|
||||
db.Model(&models.UserWarehouse{}).Where("user_id = ? AND product_type = 2 AND status = 1", user.ID).Count(&secondaryCount)
|
||||
|
||||
stats["stock_count"] = stockCount
|
||||
stats["sold_count"] = soldCount
|
||||
stats["total_value"] = totalValue
|
||||
stats["primary_count"] = primaryCount
|
||||
stats["secondary_count"] = secondaryCount
|
||||
|
||||
c.JSON(http.StatusOK, common.Success(stats))
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
jwtutil "awesomeProject/pkg/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Whitelist 白名单路径(支持通配符)
|
||||
var Whitelist = []string{
|
||||
"/back/file/**",
|
||||
"/back/login",
|
||||
}
|
||||
|
||||
func CORSMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Methods", "*")
|
||||
c.Header("Access-Control-Allow-Headers", "*")
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func JWTMiddleware(c *gin.Context) {
|
||||
// ✅ 1. 获取请求路径
|
||||
path := c.Request.URL.Path
|
||||
if !strings.HasPrefix(path, "/back") {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
// ✅ 2. 检查是否在白名单中
|
||||
for _, rule := range Whitelist {
|
||||
if PathMatch(rule, path) {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 3. 从 Header 中提取 Token
|
||||
tokenString := c.GetHeader("token")
|
||||
if tokenString == "" {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "token无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ 4. 解析并验证 Token
|
||||
claims, err := jwtutil.ParseTokenToMap(tokenString)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "token无效"))
|
||||
return
|
||||
}
|
||||
|
||||
// ✅ 5. Token 有效,继续处理
|
||||
c.Set("tokenMap", claims)
|
||||
c.Next()
|
||||
}
|
||||
|
||||
// PathMatch 判断请求路径是否匹配白名单中的通配符规则
|
||||
func PathMatch(pattern, path string) bool {
|
||||
if pattern == path {
|
||||
return true
|
||||
}
|
||||
|
||||
if len(pattern) > 0 && pattern[0] == '/' {
|
||||
pattern = pattern[1:]
|
||||
}
|
||||
if len(path) > 0 && path[0] == '/' {
|
||||
path = path[1:]
|
||||
}
|
||||
|
||||
patternParts := strings.Split(pattern, "/")
|
||||
pathParts := strings.Split(path, "/")
|
||||
|
||||
if len(patternParts) > len(pathParts) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := 0; i < len(patternParts); i++ {
|
||||
p := patternParts[i]
|
||||
if p == "**" {
|
||||
// 匹配任意多级路径
|
||||
return true
|
||||
} else if p == "*" {
|
||||
// 匹配单级路径
|
||||
if i == len(patternParts)-1 {
|
||||
return true
|
||||
}
|
||||
continue
|
||||
} else if p != pathParts[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 如果模式完全匹配路径的前缀
|
||||
return len(patternParts) == len(pathParts)
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// AdminRoleMiddleware 管理员权限中间件
|
||||
func AdminRoleMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 获取token中的用户信息
|
||||
tokenMap, exists := c.Get("tokenMap")
|
||||
if !exists {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "未授权访问"))
|
||||
return
|
||||
}
|
||||
|
||||
claims := tokenMap.(map[string]interface{})
|
||||
userIDFloat, ok := claims["user_id"].(float64)
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "用户信息无效"))
|
||||
return
|
||||
}
|
||||
|
||||
userID := uint(userIDFloat)
|
||||
|
||||
// 查询用户信息
|
||||
var user models.User
|
||||
if err := common.GetDB().First(&user, userID).Error; err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "用户不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户角色:0管理员,1代理商,2普通用户
|
||||
if user.SystemRole != 0 {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(403, "权限不足,仅管理员可访问"))
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("current_user", user)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// AgentRoleMiddleware 代理商权限中间件
|
||||
func AgentRoleMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// 获取token中的用户信息
|
||||
tokenMap, exists := c.Get("tokenMap")
|
||||
if !exists {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "未授权访问"))
|
||||
return
|
||||
}
|
||||
|
||||
claims := tokenMap.(map[string]interface{})
|
||||
userIDFloat, ok := claims["user_id"].(float64)
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "用户信息无效"))
|
||||
return
|
||||
}
|
||||
|
||||
userID := uint(userIDFloat)
|
||||
|
||||
// 查询用户信息
|
||||
var user models.User
|
||||
if err := common.GetDB().First(&user, userID).Error; err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "用户不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
// 检查用户角色:0管理员,1代理商
|
||||
if user.SystemRole != 0 && user.SystemRole != 1 {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(403, "权限不足,仅管理员和代理商可访问"))
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("current_user", user)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
func UserRoleMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
|
||||
// 获取token中的用户信息
|
||||
tokenMap, exists := c.Get("tokenMap")
|
||||
if !exists {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "未授权访问"))
|
||||
return
|
||||
}
|
||||
|
||||
claims := tokenMap.(map[string]interface{})
|
||||
userIDFloat, ok := claims["user_id"].(float64)
|
||||
if !ok {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "用户信息无效"))
|
||||
return
|
||||
}
|
||||
|
||||
userID := uint(userIDFloat)
|
||||
|
||||
// 查询用户信息
|
||||
var user models.User
|
||||
if err := common.GetDB().First(&user, userID).Error; err != nil {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "用户不存在"))
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("current_user", user)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// CheckUserPermission 检查用户权限(代理商只能查看自己邀请的用户)
|
||||
func CheckUserPermission() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
currentUser, exists := c.Get("current_user")
|
||||
if !exists {
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(401, "用户信息获取失败"))
|
||||
return
|
||||
}
|
||||
|
||||
user := currentUser.(models.User)
|
||||
|
||||
// 如果是管理员,拥有所有权限
|
||||
if user.SystemRole == 0 {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是代理商,需要检查权限
|
||||
if user.SystemRole == 1 {
|
||||
// 获取请求路径
|
||||
//path := c.Request.URL.Path
|
||||
method := c.Request.Method
|
||||
|
||||
//// 代理商权限限制
|
||||
//allowedPaths := []string{
|
||||
// "/back/admin/users", // 查看用户列表
|
||||
// "/back/admin/purchase-orders", // 查看买单
|
||||
// "/back/admin/sales-orders", // 查看卖单
|
||||
// "/back/admin/score-records", // 查看积分记录
|
||||
//}
|
||||
//
|
||||
//// 检查是否为允许的路径
|
||||
//pathAllowed := false
|
||||
//for _, allowedPath := range allowedPaths {
|
||||
// if strings.HasPrefix(path, allowedPath) {
|
||||
// pathAllowed = true
|
||||
// break
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//if !pathAllowed {
|
||||
// c.AbortWithStatusJSON(http.StatusOK, common.Error(403, "代理商权限不足"))
|
||||
// return
|
||||
//}
|
||||
|
||||
// 对于GET请求,添加查询过滤条件
|
||||
if method == "GET" {
|
||||
// 设置代理商身份码,用于数据过滤
|
||||
c.Set("agent_identity_code", user.IdentityCode)
|
||||
} else {
|
||||
// 非GET请求,代理商不允许
|
||||
c.AbortWithStatusJSON(http.StatusOK, common.Error(403, "代理商只能查看数据,不能进行修改操作"))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserIDFromParam 从URL参数中获取用户ID并检查权限
|
||||
func GetUserIDFromParam(c *gin.Context, paramName string) (uint, error) {
|
||||
currentUser, exists := c.Get("current_user")
|
||||
if !exists {
|
||||
return 0, fmt.Errorf("用户信息获取失败")
|
||||
}
|
||||
|
||||
user := currentUser.(models.User)
|
||||
userIDStr := c.Param(paramName)
|
||||
userID, err := strconv.ParseUint(userIDStr, 10, 32)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("用户ID无效")
|
||||
}
|
||||
|
||||
targetUserID := uint(userID)
|
||||
|
||||
// 如果是管理员,可以查看所有用户
|
||||
if user.SystemRole == 0 {
|
||||
return targetUserID, nil
|
||||
}
|
||||
|
||||
// 如果是代理商,只能查看自己邀请的用户
|
||||
if user.SystemRole == 1 {
|
||||
var targetUser models.User
|
||||
if err := common.GetDB().First(&targetUser, targetUserID).Error; err != nil {
|
||||
return 0, fmt.Errorf("目标用户不存在")
|
||||
}
|
||||
|
||||
// 检查是否为代理商邀请的用户
|
||||
if targetUser.ReferrerIdentityCode != user.IdentityCode {
|
||||
return 0, fmt.Errorf("权限不足,只能查看您邀请的用户")
|
||||
}
|
||||
|
||||
return targetUserID, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("权限不足")
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Banner 轮播图表
|
||||
type Banner struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
Title string `gorm:"type:varchar(200);not null;comment:轮播图标题" json:"title" binding:"required"`
|
||||
ImageUrl string `gorm:"type:varchar(500);not null;comment:轮播图图片URL" json:"image_url" binding:"required"`
|
||||
LinkUrl string `gorm:"type:varchar(500);comment:点击跳转链接" json:"link_url"`
|
||||
Description string `gorm:"type:text;comment:描述信息" json:"description"`
|
||||
SortOrder int `gorm:"type:int;not null;default:0;comment:排序序号" json:"sort_order"`
|
||||
Status uint8 `gorm:"type:tinyint;not null;default:1;comment:状态:0禁用,1启用" json:"status"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (Banner) TableName() string {
|
||||
return "banners"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/config"
|
||||
)
|
||||
|
||||
// AutoMigrateAll 自动迁移所有数据表
|
||||
func AutoMigrateAll() error {
|
||||
db := common.GetDB()
|
||||
|
||||
// 按依赖关系迁移表
|
||||
return db.AutoMigrate(
|
||||
&User{},
|
||||
&UserAddress{},
|
||||
&ProductCategory{},
|
||||
&PrimaryProduct{},
|
||||
&SecondaryProduct{},
|
||||
&UserWarehouse{},
|
||||
&PurchaseOrder{},
|
||||
&SalesOrder{},
|
||||
&OrderMessage{}, // 订单留言表
|
||||
&SystemConfig{},
|
||||
&ScoreRecord{},
|
||||
&Banner{}, // 轮播图表
|
||||
&config.ConfigChange{}, // 配置变更记录表
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// OrderMessage 订单留言模型
|
||||
type OrderMessage struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
OrderID uint `gorm:"not null;index" json:"order_id"` // 订单ID
|
||||
OrderType string `gorm:"type:varchar(20);not null;default:'purchase'" json:"order_type"` // 订单类型:purchase(买单), sales(卖单)
|
||||
UserID uint `gorm:"not null;index" json:"user_id"` // 留言用户ID
|
||||
Content string `gorm:"type:text;not null" json:"content"` // 留言内容
|
||||
Images string `gorm:"type:text" json:"images"` // 留言图片,多个图片用逗号分隔
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
|
||||
// 关联关系
|
||||
User *User `gorm:"foreignKey:UserID" json:"user,omitempty"`
|
||||
}
|
||||
|
||||
// TableName 指定表名
|
||||
func (OrderMessage) TableName() string {
|
||||
return "order_messages"
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// PrimaryProduct 一级商品表(管理员发布的官方商品)
|
||||
type PrimaryProduct struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
CategoryID uint `gorm:"type:int;not null;comment:分类ID" json:"category_id" binding:"required"`
|
||||
ProductName string `gorm:"type:varchar(200);not null;comment:商品名称" json:"product_name" binding:"required"`
|
||||
ProductDescription string `gorm:"type:text;comment:商品描述" json:"product_description"`
|
||||
ProductImages string `gorm:"type:text;comment:商品图片URLs,逗号分割" json:"product_images"`
|
||||
OriginalPrice float64 `gorm:"type:decimal(10,2);not null;comment:原价" json:"original_price" binding:"required"`
|
||||
CurrentPrice float64 `gorm:"type:decimal(10,2);not null;comment:现价" json:"current_price" binding:"required"`
|
||||
StockQuantity int `gorm:"type:int;not null;default:0;comment:库存数量" json:"stock_quantity"`
|
||||
SalesCount int `gorm:"type:int;not null;default:0;comment:销量" json:"sales_count"`
|
||||
ViewCount int `gorm:"type:int;not null;default:0;comment:浏览量" json:"view_count"`
|
||||
Status uint8 `gorm:"type:tinyint;not null;default:1;comment:状态:0下架,1上架" json:"status"`
|
||||
IsHot int8 `gorm:"type:tinyint;not null;default:0;comment:是否为热门商品:0否,1是" json:"is_hot"`
|
||||
SortOrder int `gorm:"type:int;not null;default:0;comment:排序序号" json:"sort_order"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
|
||||
// 关联关系 - 注意:不要在关联字段上加验证标签
|
||||
Category ProductCategory `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
|
||||
}
|
||||
|
||||
func (PrimaryProduct) TableName() string {
|
||||
return "primary_products"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ProductCategory 商品分类表
|
||||
type ProductCategory struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
CategoryName string `gorm:"type:varchar(100);not null;comment:分类名称" json:"category_name" binding:"required"`
|
||||
CategoryIcon string `gorm:"type:varchar(255);comment:分类图标URL" json:"category_icon"`
|
||||
SortOrder int `gorm:"type:int;not null;default:0;comment:排序序号" json:"sort_order"`
|
||||
Status uint8 `gorm:"type:tinyint;not null;default:1;comment:状态:0禁用,1启用" json:"status"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (ProductCategory) TableName() string {
|
||||
return "product_categories"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// PurchaseOrder 买单表
|
||||
type PurchaseOrder struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
OrderNo string `gorm:"type:varchar(50);not null;uniqueIndex;comment:订单编号" json:"order_no"`
|
||||
BuyerID uint `gorm:"type:int;not null;comment:买家用户ID" json:"buyer_id" binding:"required"`
|
||||
SellerID *uint `gorm:"type:int;comment:卖家用户ID(二级商品交易时使用)" json:"seller_id"`
|
||||
ProductID uint `gorm:"type:int;not null;comment:商品ID" json:"product_id" binding:"required"`
|
||||
ProductType uint8 `gorm:"type:tinyint;not null;comment:商品类型:1一级商品,2二级商品" json:"product_type" binding:"required"`
|
||||
ProductName string `gorm:"type:varchar(200);not null;comment:商品名称" json:"product_name" binding:"required"`
|
||||
ProductImages string `gorm:"type:text;comment:商品图片URLs,逗号分割" json:"product_images"`
|
||||
UnitPrice float64 `gorm:"type:decimal(10,2);not null;comment:单价" json:"unit_price" binding:"required"`
|
||||
Quantity int `gorm:"type:int;not null;default:1;comment:数量" json:"quantity"`
|
||||
TotalAmount float64 `gorm:"type:decimal(10,2);not null;comment:总金额" json:"total_amount" binding:"required"`
|
||||
AddressID *uint `gorm:"type:int;comment:收货地址ID" json:"address_id"`
|
||||
OrderStatus uint8 `gorm:"type:tinyint;not null;default:0;comment:订单状态:0待付款,1待确认,2已完成,3已取消" json:"order_status"`
|
||||
ConfirmTime *time.Time `gorm:"type:timestamp;null;comment:确认时间" json:"confirm_time"`
|
||||
Remark string `gorm:"type:text;comment:备注" json:"remark"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
PaymentProofImage string `gorm:"type:varchar(255)" json:"payment_proof_image"`
|
||||
// 关联关系
|
||||
Buyer User `gorm:"foreignKey:BuyerID" json:"buyer,omitempty"`
|
||||
Seller *User `gorm:"foreignKey:SellerID" json:"seller,omitempty"`
|
||||
Address *UserAddress `gorm:"foreignKey:AddressID" json:"address,omitempty"`
|
||||
}
|
||||
|
||||
func (PurchaseOrder) TableName() string {
|
||||
return "purchase_orders"
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SalesOrder 卖单表
|
||||
type SalesOrder struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
OrderNo string `gorm:"type:varchar(50);not null;uniqueIndex;comment:订单编号" json:"order_no"`
|
||||
SellerID uint `gorm:"type:int;not null;comment:卖家用户ID" json:"seller_id" binding:"required"`
|
||||
BuyerID uint `gorm:"type:int;not null;comment:买家用户ID" json:"buyer_id" binding:"required"`
|
||||
SecondaryProductID uint `gorm:"type:int;not null;comment:二级商品ID" json:"secondary_product_id" binding:"required"`
|
||||
ProductName string `gorm:"type:varchar(200);not null;comment:商品名称" json:"product_name" binding:"required"`
|
||||
ProductImages string `gorm:"type:text;comment:商品图片URLs,逗号分割" json:"product_images"`
|
||||
SellingPrice float64 `gorm:"type:decimal(10,2);not null;comment:出售价格" json:"selling_price" binding:"required"`
|
||||
Quantity int `gorm:"type:int;not null;default:1;comment:数量" json:"quantity"`
|
||||
TotalAmount float64 `gorm:"type:decimal(10,2);not null;comment:总金额" json:"total_amount" binding:"required"`
|
||||
AddressID *uint `gorm:"type:int;comment:收货地址ID" json:"address_id"`
|
||||
OrderStatus uint8 `gorm:"type:tinyint;not null;default:0;comment:订单状态:0待付款,1待发货,2确认完成,3已取消" json:"order_status"`
|
||||
CompleteTime *time.Time `gorm:"type:timestamp;null;comment:完成时间" json:"complete_time"`
|
||||
Remark string `gorm:"type:text;comment:备注" json:"remark"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
PaymentProofImage string `json:"payment_proof_image" gorm:"type:varchar(255)"`
|
||||
PurchaseOrderID uint `gorm:"type:int;" json:"purchase_order_id"`
|
||||
// 关联关系
|
||||
Seller *User `gorm:"foreignKey:SellerID" json:"seller,omitempty"`
|
||||
Buyer User `gorm:"foreignKey:BuyerID" json:"buyer,omitempty"`
|
||||
SecondaryProduct SecondaryProduct `gorm:"foreignKey:SecondaryProductID" json:"secondary_product,omitempty"`
|
||||
Address *UserAddress `gorm:"foreignKey:AddressID" json:"address,omitempty"`
|
||||
PurchaseOrder *PurchaseOrder `gorm:"foreignKey:PurchaseOrderID" json:"purchase_order,omitempty"`
|
||||
}
|
||||
|
||||
func (SalesOrder) TableName() string {
|
||||
return "sales_orders"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// ScoreRecord 积分记录表
|
||||
type ScoreRecord struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
UserID uint `gorm:"type:int;not null;comment:用户编号" json:"user_id" binding:"required"`
|
||||
ChangeNumber int `gorm:"type:int;not null;comment:变化数量" json:"change_number" binding:"required"`
|
||||
Note string `gorm:"type:varchar(200);not null;comment:说明" json:"note" binding:"required"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
|
||||
// 关联关系
|
||||
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
|
||||
}
|
||||
|
||||
func (ScoreRecord) TableName() string {
|
||||
return "score_records"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SecondaryProduct 二级商品表(用户上架的商品)
|
||||
type SecondaryProduct struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
SellerID uint `gorm:"type:int;not null;comment:卖家用户ID" json:"seller_id" binding:"required"`
|
||||
OriginalProductID *uint `gorm:"type:int;comment:原始商品ID(一级商品ID或二级商品ID)" json:"original_product_id"`
|
||||
OriginalProductType uint8 `gorm:"type:tinyint;not null;comment:原始商品类型:1一级商品,2二级商品" json:"original_product_type" binding:"required"`
|
||||
ProductName string `gorm:"type:varchar(200);not null;comment:商品名称" json:"product_name" binding:"required"`
|
||||
ProductDescription string `gorm:"type:text;comment:商品描述" json:"product_description"`
|
||||
ProductImages string `gorm:"type:text;comment:商品图片URLs,JSON格式存储" json:"product_images"`
|
||||
CostPrice float64 `gorm:"type:decimal(10,2);not null;comment:成本价(用户购买时的价格)" json:"cost_price" binding:"required"`
|
||||
SellingPrice float64 `gorm:"type:decimal(10,2);not null;comment:出售价格" json:"selling_price" binding:"required"`
|
||||
Quantity int `gorm:"type:int;not null;default:1;comment:出售数量" json:"quantity"`
|
||||
Status uint8 `gorm:"type:tinyint;not null;default:1;comment:状态:0下架,1上架,2已售出" json:"status"`
|
||||
ViewCount int `gorm:"type:int;not null;default:0;comment:浏览量" json:"view_count"`
|
||||
SalesCount int `gorm:"type:int;not null;default:0;" json:"sales_count"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
CategoryID uint `gorm:"type:int;not null;default:0;comment:商品分类ID" json:"category_id"`
|
||||
ProductCondition string `gorm:"type varchar(255)" json:"product_condition"`
|
||||
UserWarehouseID uint `gorm:"type:int;not null" json:"user_warehouse_id"`
|
||||
// 关联关系
|
||||
Seller User `gorm:"foreignKey:SellerID" json:"seller,omitempty"`
|
||||
Category *ProductCategory `gorm:"foreignKey:CategoryID" json:"category,omitempty"`
|
||||
UserWarehouse *UserWarehouse `gorm:"foreignKey:UserWarehouseID" json:"user_warehouse,omitempty"`
|
||||
}
|
||||
|
||||
func (SecondaryProduct) TableName() string {
|
||||
return "secondary_products"
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// SystemConfig 系统常量设置表
|
||||
type SystemConfig struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
ConfigKey string `gorm:"type:varchar(100);not null;uniqueIndex;comment:配置键" json:"config_key" binding:"required"`
|
||||
ConfigValue string `gorm:"type:text;not null;comment:配置值" json:"config_value" binding:"required"`
|
||||
ConfigName string `gorm:"type:varchar(200);not null;comment:配置名称" json:"config_name" binding:"required"`
|
||||
ConfigDescription string `gorm:"type:text;comment:配置描述" json:"config_description"`
|
||||
ConfigType string `gorm:"type:varchar(20);not null;default:'string';comment:配置类型:string、number、boolean、json" json:"config_type"`
|
||||
IsSystem uint8 `gorm:"type:tinyint;not null;default:0;comment:是否系统配置:0否,1是" json:"is_system"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (SystemConfig) TableName() string {
|
||||
return "system_configs"
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// User 用户表
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
Phone string `gorm:"type:varchar(20);not null;uniqueIndex" json:"phone" binding:"required"`
|
||||
SystemRole uint8 `gorm:"type:tinyint;not null;default:2;comment:系统角色:0管理员,1代理商,2普通用户" json:"system_role"`
|
||||
CustomerName string `gorm:"type:varchar(50)" json:"customer_name"`
|
||||
RealName string `gorm:"type:varchar(50)" json:"real_name"`
|
||||
CurrentPoints int `gorm:"type:int;not null;default:0;comment:当前积分" json:"current_points"`
|
||||
Avatar string `gorm:"type:varchar(255)" json:"avatar"`
|
||||
CompanyName string `gorm:"type:varchar(100)" json:"company_name"`
|
||||
PersonalIntro string `gorm:"type:text" json:"personal_intro"`
|
||||
IdentityCode string `gorm:"type:varchar(50);uniqueIndex" json:"identity_code"`
|
||||
ReferrerIdentityCode string `gorm:"type:varchar(50)" json:"referrer_identity_code"`
|
||||
BusinessLicenseImage string `gorm:"type:varchar(255)" json:"business_license_image"`
|
||||
IdCardFrontImage string `gorm:"type:varchar(255)" json:"id_card_front_image"`
|
||||
IdCardBackImage string `gorm:"type:varchar(255)" json:"id_card_back_image"`
|
||||
MainPaymentQrImage *string `gorm:"type:varchar(255)" json:"main_payment_qr_image"`
|
||||
SubPaymentQrImage *string `gorm:"type:varchar(255)" json:"sub_payment_qr_image"`
|
||||
Password string `gorm:"type:varchar(255)" json:"password"`
|
||||
Status uint8 `gorm:"type:tinyint;not null;default:1;comment:状态:0禁用,1启用" json:"status"`
|
||||
ExpiryDate *time.Time `gorm:"type:timestamp;null;comment:过期时间,null表示永不过期" json:"expiry_date"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
}
|
||||
|
||||
func (User) TableName() string {
|
||||
return "users"
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// UserAddress 收货地址表
|
||||
type UserAddress struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
UserID uint `gorm:"type:int;not null;comment:用户ID" json:"user_id"`
|
||||
ConsigneeName string `gorm:"type:varchar(50);not null;comment:收件人姓名" json:"consignee_name" binding:"required"`
|
||||
ConsigneePhone string `gorm:"type:varchar(20);not null;comment:收件人电话" json:"consignee_phone" binding:"required"`
|
||||
Province string `gorm:"type:varchar(50);not null;comment:省份" json:"province" binding:"required"`
|
||||
City string `gorm:"type:varchar(50);not null;comment:城市" json:"city" binding:"required"`
|
||||
District string `gorm:"type:varchar(50);not null;comment:区县" json:"district" binding:"required"`
|
||||
DetailAddress string `gorm:"type:varchar(200);not null;comment:详细地址" json:"detail_address" binding:"required"`
|
||||
PostalCode string `gorm:"type:varchar(10);comment:邮政编码" json:"postal_code"`
|
||||
IsDefault uint8 `gorm:"type:tinyint;not null;default:0;comment:是否默认地址:0否,1是" json:"is_default"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
|
||||
// 关联关系
|
||||
User *User `gorm:"foreignKey:UserID;constraint:OnDelete:CASCADE" json:"-"`
|
||||
}
|
||||
|
||||
func (UserAddress) TableName() string {
|
||||
return "user_addresses"
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// UserWarehouse 用户仓库表
|
||||
type UserWarehouse struct {
|
||||
ID uint `gorm:"primaryKey;autoIncrement" json:"id"`
|
||||
UserID uint `gorm:"type:int;not null;comment:用户ID" json:"user_id" binding:"required"`
|
||||
ProductID uint `gorm:"type:int;not null;comment:商品ID" json:"product_id" binding:"required"`
|
||||
ProductType uint8 `gorm:"type:tinyint;not null;comment:商品类型:1一级商品,2二级商品" json:"product_type" binding:"required"`
|
||||
ProductName string `gorm:"type:varchar(200);not null;comment:商品名称" json:"product_name" binding:"required"`
|
||||
ProductImages string `gorm:"type:text;comment:商品图片URLs,JSON格式存储" json:"product_images"`
|
||||
PurchasePrice float64 `gorm:"type:decimal(10,2);not null;comment:购买价格" json:"purchase_price" binding:"required"`
|
||||
WarehousePrice float64 `gorm:"type:decimal(10,2);not null;comment:入库价格(根据系统常量增值后)" json:"warehouse_price" binding:"required"`
|
||||
Quantity int `gorm:"type:int;not null;default:1;comment:数量" json:"quantity"`
|
||||
SourceOrderID *uint `gorm:"type:int;comment:来源订单ID" json:"source_order_id"`
|
||||
Status uint8 `gorm:"type:tinyint;not null;default:1;comment:状态:0已出售,1库存中" json:"status"`
|
||||
CreatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"type:timestamp;default:CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" json:"updated_at"`
|
||||
|
||||
// 关联关系
|
||||
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
|
||||
}
|
||||
|
||||
func (UserWarehouse) TableName() string {
|
||||
return "user_warehouse"
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/handlers"
|
||||
"awesomeProject/internal/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SetupAdminRoutes 设置管理端路由
|
||||
func SetupAdminRoutes(router *gin.Engine) {
|
||||
// 管理端路由组
|
||||
adminGroup := router.Group("/back/admin")
|
||||
|
||||
// 应用JWT中间件
|
||||
adminGroup.Use(middleware.JWTMiddleware)
|
||||
|
||||
// 创建handler实例
|
||||
userHandler := &handlers.AdminUserHandler{}
|
||||
orderHandler := &handlers.AdminOrderHandler{}
|
||||
scoreHandler := &handlers.AdminScoreHandler{}
|
||||
productHandler := &handlers.AdminProductHandler{}
|
||||
configHandler := &handlers.AdminConfigHandler{}
|
||||
paymentHandler := &handlers.AdminPaymentHandler{}
|
||||
orderMessageHandler := &handlers.AdminOrderMessageHandler{}
|
||||
bannerHandler := &handlers.AdminBannerHandler{}
|
||||
|
||||
// 用户管理路由(管理员和代理商都可访问,但代理商有限制)
|
||||
userRoutes := adminGroup.Group("/users")
|
||||
|
||||
userRoutes.Use(middleware.AgentRoleMiddleware(), middleware.CheckUserPermission())
|
||||
{
|
||||
userRoutes.GET("", userHandler.GetUsers) // 获取用户列表
|
||||
|
||||
userRoutes.GET("/:id", userHandler.GetUser) // 获取单个用户信息
|
||||
userRoutes.GET("/stats", userHandler.GetUserStats) // 获取用户统计
|
||||
userRoutes.GET("/search-stats", userHandler.SearchUserStats) // 搜索用户统计
|
||||
// 以下接口仅管理员可访问
|
||||
userRoutes.Use(middleware.AdminRoleMiddleware())
|
||||
|
||||
userRoutes.POST("", userHandler.CreateUser) // 创建用户
|
||||
userRoutes.PUT("/:id", userHandler.UpdateUser) // 更新用户
|
||||
userRoutes.PUT("/:id/expiry", userHandler.SetUserExpiryDate) // 设置用户过期时间
|
||||
userRoutes.DELETE("/:id", userHandler.DeleteUser) // 删除用户
|
||||
}
|
||||
|
||||
// 用户库存管理路由(仅管理员可访问)
|
||||
warehouseRoutes := adminGroup.Group("/warehouse")
|
||||
warehouseRoutes.Use(middleware.AdminRoleMiddleware())
|
||||
{
|
||||
warehouseRoutes.GET("/users", userHandler.GetAllUserWarehouses) // 获取所有用户库存汇总
|
||||
}
|
||||
|
||||
// 订单管理路由(管理员和代理商都可访问,但代理商有限制)
|
||||
orderRoutes := adminGroup.Group("/orders")
|
||||
orderRoutes.Use(middleware.AgentRoleMiddleware(), middleware.CheckUserPermission())
|
||||
{
|
||||
// 买单管理
|
||||
purchaseGroup := orderRoutes.Group("/purchase")
|
||||
{
|
||||
purchaseGroup.GET("", orderHandler.GetPurchaseOrders) // 获取买单列表
|
||||
purchaseGroup.GET("/stats", orderHandler.GetOrderStats) // 获取订单统计
|
||||
purchaseGroup.GET("/trend", orderHandler.GetOrderTrend) // 获取订单趋势
|
||||
purchaseGroup.GET("/activities", orderHandler.GetRecentActivities) // 获取最近活动
|
||||
purchaseGroup.GET("/:id", orderHandler.GetPurchaseOrder) // 获取单个买单详情
|
||||
|
||||
// 以下接口仅管理员可访问
|
||||
purchaseGroup.Use(middleware.AdminRoleMiddleware())
|
||||
purchaseGroup.PUT("/:id/status", orderHandler.UpdatePurchaseOrderStatus) // 更新买单状态
|
||||
}
|
||||
|
||||
// 卖单管理
|
||||
salesGroup := orderRoutes.Group("/sales")
|
||||
{
|
||||
salesGroup.GET("", orderHandler.GetSalesOrders) // 获取卖单列表
|
||||
salesGroup.GET("/:id", orderHandler.GetSalesOrder) // 获取单个卖单详情
|
||||
|
||||
// 以下接口仅管理员可访问
|
||||
salesGroup.Use(middleware.AdminRoleMiddleware())
|
||||
salesGroup.PUT("/:id/status", orderHandler.UpdateSalesOrderStatus) // 更新卖单状态
|
||||
}
|
||||
|
||||
// 订单留言管理(管理员和代理商都可访问)
|
||||
orderRoutes.GET("/:order_id/messages", orderMessageHandler.GetOrderMessages) // 获取订单留言列表
|
||||
orderRoutes.POST("/:order_id/messages", orderMessageHandler.CreateOrderMessage) // 创建订单留言
|
||||
}
|
||||
|
||||
// 积分管理路由(管理员和代理商都可访问,但代理商有限制)
|
||||
scoreRoutes := adminGroup.Group("/scores")
|
||||
scoreRoutes.Use(middleware.AgentRoleMiddleware(), middleware.CheckUserPermission())
|
||||
{
|
||||
scoreRoutes.GET("", scoreHandler.GetScoreRecords) // 获取积分记录列表
|
||||
scoreRoutes.GET("/stats", scoreHandler.GetScoreStats) // 获取积分统计
|
||||
scoreRoutes.GET("/:id", scoreHandler.GetScoreRecord) // 获取单个积分记录详情
|
||||
|
||||
// 以下接口仅管理员可访问
|
||||
scoreRoutes.Use(middleware.AdminRoleMiddleware())
|
||||
scoreRoutes.POST("", scoreHandler.CreateScoreRecord) // 创建积分记录
|
||||
scoreRoutes.PUT("/:id", scoreHandler.UpdateScoreRecord) // 更新积分记录
|
||||
scoreRoutes.DELETE("/:id", scoreHandler.DeleteScoreRecord) // 删除积分记录
|
||||
}
|
||||
|
||||
// 商品管理路由(仅管理员可访问)
|
||||
productRoutes := adminGroup.Group("/products")
|
||||
productRoutes.Use(middleware.AdminRoleMiddleware())
|
||||
{
|
||||
// 商品分类管理
|
||||
categoryGroup := productRoutes.Group("/categories")
|
||||
{
|
||||
categoryGroup.GET("", productHandler.GetProductCategories) // 获取分类列表
|
||||
categoryGroup.POST("", productHandler.CreateProductCategory) // 创建分类
|
||||
categoryGroup.PUT("/:id", productHandler.UpdateProductCategory) // 更新分类
|
||||
categoryGroup.DELETE("/:id", productHandler.DeleteProductCategory) // 删除分类
|
||||
}
|
||||
|
||||
// 一级商品管理
|
||||
primaryGroup := productRoutes.Group("/primary")
|
||||
{
|
||||
primaryGroup.GET("", productHandler.GetPrimaryProducts) // 获取一级商品列表
|
||||
primaryGroup.GET("/stats", productHandler.GetProductStats) // 获取商品统计
|
||||
primaryGroup.GET("/:id", productHandler.GetPrimaryProduct) // 获取单个一级商品详情
|
||||
primaryGroup.POST("", productHandler.CreatePrimaryProduct) // 创建一级商品
|
||||
primaryGroup.PUT("/:id", productHandler.UpdatePrimaryProduct) // 更新一级商品
|
||||
primaryGroup.DELETE("/:id", productHandler.DeletePrimaryProduct) // 删除一级商品
|
||||
}
|
||||
|
||||
// 二级商品管理(仅查看)
|
||||
secondaryGroup := productRoutes.Group("/secondary")
|
||||
{
|
||||
secondaryGroup.GET("", productHandler.GetSecondaryProducts) // 获取二级商品列表
|
||||
}
|
||||
}
|
||||
|
||||
// 系统配置管理路由(仅管理员可访问)
|
||||
configRoutes := adminGroup.Group("/configs")
|
||||
configRoutes.Use(middleware.AdminRoleMiddleware())
|
||||
{
|
||||
configRoutes.GET("", configHandler.GetSystemConfigs) // 获取系统配置列表
|
||||
configRoutes.GET("/:id", configHandler.GetSystemConfig) // 获取单个系统配置
|
||||
configRoutes.POST("", configHandler.CreateSystemConfig) // 创建系统配置
|
||||
configRoutes.PUT("/:id", configHandler.UpdateSystemConfig) // 更新系统配置
|
||||
configRoutes.DELETE("/:id", configHandler.DeleteSystemConfig) // 删除系统配置
|
||||
}
|
||||
|
||||
// 管理员付款方式路由
|
||||
paymentRoutes := adminGroup.Group("/payment")
|
||||
{
|
||||
paymentRoutes.GET("/info", paymentHandler.GetPaymentInfo) // 获取管理员付款方式信息(无需权限验证,用户购买时需要查看)
|
||||
|
||||
// 以下接口仅管理员可访问
|
||||
paymentRoutes.Use(middleware.AdminRoleMiddleware())
|
||||
paymentRoutes.PUT("/info", paymentHandler.UpdatePaymentInfo) // 更新管理员付款方式信息
|
||||
}
|
||||
|
||||
// 轮播图管理路由(仅管理员可访问)
|
||||
bannerRoutes := adminGroup.Group("/banners")
|
||||
bannerRoutes.Use(middleware.AdminRoleMiddleware())
|
||||
{
|
||||
bannerRoutes.GET("", bannerHandler.GetBanners) // 获取轮播图列表
|
||||
bannerRoutes.GET("/:id", bannerHandler.GetBanner) // 获取单个轮播图
|
||||
bannerRoutes.POST("", bannerHandler.CreateBanner) // 创建轮播图
|
||||
bannerRoutes.PUT("/:id", bannerHandler.UpdateBanner) // 更新轮播图
|
||||
bannerRoutes.DELETE("/:id", bannerHandler.DeleteBanner) // 删除轮播图
|
||||
}
|
||||
|
||||
// 公共配置查询路由(根据key获取配置,无需权限验证)
|
||||
adminGroup.GET("/config/:key", configHandler.GetConfigByKey)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/handlers"
|
||||
"awesomeProject/internal/middleware"
|
||||
"awesomeProject/internal/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SetupPublicRoutes 设置公共路由
|
||||
func SetupPublicRoutes(router *gin.Engine) {
|
||||
// 创建handler实例
|
||||
authHandler := &handlers.AuthHandler{}
|
||||
uploadHandler := handlers.NewUploadHandler()
|
||||
|
||||
// 公共路由组
|
||||
publicGroup := router.Group("/back")
|
||||
publicGroup.POST("/upload", uploadHandler.UploadFile)
|
||||
publicGroup.GET("/config", func(context *gin.Context) {
|
||||
|
||||
db := common.GetDB()
|
||||
key := context.DefaultQuery("key", "")
|
||||
if key == "" {
|
||||
context.JSON(200, common.Error(500, "键值对为空"))
|
||||
return
|
||||
}
|
||||
var config models.SystemConfig
|
||||
db.Where("config_key=?", key).First(&config)
|
||||
context.JSON(200, common.Success(config.ConfigValue))
|
||||
})
|
||||
// 认证相关路由(无需token验证)
|
||||
authGroup := publicGroup.Group("/auth")
|
||||
{
|
||||
authGroup.POST("/login", authHandler.Login) // 用户登录
|
||||
authGroup.POST("/register", authHandler.Register) // 用户注册
|
||||
authGroup.POST("/send-sms", authHandler.SendSMS) // 发送短信验证码
|
||||
}
|
||||
|
||||
// 需要token验证的公共路由
|
||||
protectedGroup := publicGroup.Group("")
|
||||
|
||||
protectedGroup.Use(middleware.JWTMiddleware)
|
||||
{
|
||||
protectedGroup.GET("/current-user", authHandler.GetCurrentUser) // 获取当前用户信息
|
||||
protectedGroup.PUT("/update-profile", authHandler.UpdateProfile) // 更新个人信息
|
||||
protectedGroup.PUT("/change-password", authHandler.ChangePassword) // 修改密码
|
||||
|
||||
// 文件上传路由
|
||||
// 单文件上传
|
||||
protectedGroup.POST("/upload/batch", uploadHandler.BatchUploadFiles) // 批量文件上传
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SetupRouter 设置路由
|
||||
func SetupRouter() *gin.Engine {
|
||||
// 创建gin引擎
|
||||
router := gin.Default()
|
||||
|
||||
// 添加全局中间件
|
||||
router.Use(middleware.CORSMiddleware())
|
||||
|
||||
// 设置公共路由(如登录、注册等)
|
||||
SetupPublicRoutes(router)
|
||||
|
||||
// 设置管理端路由
|
||||
SetupAdminRoutes(router)
|
||||
|
||||
// 设置用户端路由
|
||||
SetupUserRoutes(router)
|
||||
|
||||
return router
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/handlers"
|
||||
"awesomeProject/internal/middleware"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// SetupUserRoutes 设置用户端路由
|
||||
func SetupUserRoutes(router *gin.Engine) {
|
||||
// 用户端路由组
|
||||
userGroup := router.Group("/back/user")
|
||||
|
||||
// 应用JWT中间件(用户端需要登录)
|
||||
userGroup.Use(middleware.JWTMiddleware)
|
||||
userGroup.Use(middleware.UserRoleMiddleware())
|
||||
// 创建handler实例
|
||||
productHandler := &handlers.UserProductHandler{}
|
||||
paymentHandler := &handlers.UserPaymentHandler{}
|
||||
addressHandler := &handlers.UserAddressHandler{}
|
||||
orderHandler := &handlers.UserOrderHandler{}
|
||||
warehouseHandler := &handlers.UserWarehouseHandler{}
|
||||
scoreHandler := &handlers.UserScoreHandler{}
|
||||
orderMessageHandler := &handlers.UserOrderMessageHandler{}
|
||||
reconciliationHandler := &handlers.UserReconciliationHandler{}
|
||||
bannerHandler := &handlers.UserBannerHandler{}
|
||||
|
||||
// 首页数据路由
|
||||
userGroup.GET("/home/data", productHandler.GetHomeData) // 获取首页数据(分类和一级商品)
|
||||
|
||||
// 轮播图相关路由
|
||||
userGroup.GET("/banners", bannerHandler.GetActiveBanners) // 获取启用的轮播图列表
|
||||
|
||||
// 商品相关路由
|
||||
productRoutes := userGroup.Group("/products")
|
||||
{
|
||||
// 商品分类
|
||||
productRoutes.GET("/categories", productHandler.GetProductCategories) // 获取商品分类列表
|
||||
|
||||
// 一级商品(官方商品)
|
||||
primaryGroup := productRoutes.Group("/primary")
|
||||
{
|
||||
primaryGroup.GET("", productHandler.GetPrimaryProducts) // 获取一级商品列表
|
||||
primaryGroup.GET("/:id", productHandler.GetPrimaryProduct) // 获取一级商品详情
|
||||
}
|
||||
|
||||
// 二级商品(用户挂售商品)
|
||||
secondaryGroup := productRoutes.Group("/secondary")
|
||||
{
|
||||
secondaryGroup.GET("", productHandler.GetSecondaryProducts) // 获取二级商品列表
|
||||
secondaryGroup.GET("/:id", productHandler.GetSecondaryProduct) // 获取二级商品详情
|
||||
}
|
||||
}
|
||||
|
||||
// 收款方式相关路由
|
||||
paymentRoutes := userGroup.Group("/payment")
|
||||
{
|
||||
paymentRoutes.GET("/info", paymentHandler.GetPaymentInfo) // 获取收款方式信息
|
||||
paymentRoutes.PUT("/info", paymentHandler.UpdatePaymentInfo) // 更新收款方式信息
|
||||
}
|
||||
|
||||
// 获取卖家收款信息
|
||||
userGroup.GET("/seller/:id/info", paymentHandler.GetSellerInfo) // 获取卖家基本信息
|
||||
userGroup.GET("/seller/:id/payment", paymentHandler.GetSellerPaymentInfo) // 获取卖家收款方式信息
|
||||
|
||||
// 收货地址相关路由
|
||||
addressRoutes := userGroup.Group("/addresses")
|
||||
{
|
||||
addressRoutes.GET("", addressHandler.GetAddressList) // 获取收货地址列表
|
||||
addressRoutes.GET("/:id", addressHandler.GetAddress) // 获取单个收货地址
|
||||
addressRoutes.POST("", addressHandler.CreateAddress) // 创建收货地址
|
||||
addressRoutes.PUT("/:id", addressHandler.UpdateAddress) // 更新收货地址
|
||||
addressRoutes.DELETE("/:id", addressHandler.DeleteAddress) // 删除收货地址
|
||||
addressRoutes.PUT("/:id/default", addressHandler.SetDefaultAddress) // 设置默认收货地址
|
||||
}
|
||||
|
||||
// 订单相关路由
|
||||
orderRoutes := userGroup.Group("/orders")
|
||||
{
|
||||
// 买单相关
|
||||
orderRoutes.POST("/purchase", orderHandler.CreatePurchaseOrder) // 创建买单
|
||||
orderRoutes.GET("/purchase", orderHandler.GetPurchaseOrders) // 获取买单列表
|
||||
orderRoutes.GET("/purchase/:id", orderHandler.GetPurchaseOrderDetail) // 获取买单详情
|
||||
orderRoutes.PUT("/purchase/:id/confirm", orderHandler.ConfirmPurchaseOrder) // 确认买单(买家确认收货)
|
||||
orderRoutes.PUT("/purchase/:id/payment", orderHandler.UploadPaymentProof) // 上传付款凭证
|
||||
orderRoutes.PUT("/purchase/:id/cancel", orderHandler.CancelOrder) // 取消订单
|
||||
|
||||
// 卖单相关
|
||||
orderRoutes.GET("/sales", orderHandler.GetSalesOrders) // 获取卖单列表
|
||||
orderRoutes.GET("/sales/:id", orderHandler.GetSalesOrderDetail) // 获取卖单详情
|
||||
orderRoutes.PUT("/sales/:id/confirm", orderHandler.ConfirmSalesOrder) // 确认卖单(卖家确认发货)
|
||||
|
||||
// 订单留言相关
|
||||
orderRoutes.GET("/:order_id/messages", orderMessageHandler.GetOrderMessages) // 获取订单留言列表
|
||||
orderRoutes.POST("/:order_id/messages", orderMessageHandler.CreateOrderMessage) // 创建订单留言
|
||||
|
||||
// 订单统计
|
||||
orderRoutes.GET("/stats", orderHandler.GetOrderStats) // 获取订单统计信息
|
||||
}
|
||||
|
||||
// 仓库相关路由
|
||||
warehouseRoutes := userGroup.Group("/warehouse")
|
||||
{
|
||||
warehouseRoutes.GET("", warehouseHandler.GetWarehouseList) // 获取仓库商品列表
|
||||
warehouseRoutes.GET("/:id", warehouseHandler.GetWarehouseItem) // 获取单个仓库商品详情
|
||||
warehouseRoutes.POST("/:id/sell", warehouseHandler.SellWarehouseItem) // 出售仓库商品
|
||||
warehouseRoutes.GET("/stats", warehouseHandler.GetWarehouseStats) // 获取仓库统计信息
|
||||
}
|
||||
|
||||
// 积分相关路由
|
||||
scoreRoutes := userGroup.Group("/scores")
|
||||
{
|
||||
scoreRoutes.GET("/records", scoreHandler.GetMyScoreRecords) // 获取我的积分记录列表
|
||||
scoreRoutes.GET("/stats", scoreHandler.GetMyScoreStats) // 获取我的积分统计信息
|
||||
}
|
||||
|
||||
// 对账管理相关路由
|
||||
reconciliationRoutes := userGroup.Group("/reconciliation")
|
||||
{
|
||||
reconciliationRoutes.GET("/stats", reconciliationHandler.GetReconciliationStats) // 获取对账统计数据
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SMSCodeInfo 短信验证码信息
|
||||
type SMSCodeInfo struct {
|
||||
Code string // 验证码
|
||||
Phone string // 手机号
|
||||
IP string // IP地址
|
||||
CreatedAt time.Time // 创建时间
|
||||
ExpiresAt time.Time // 过期时间
|
||||
}
|
||||
|
||||
// RateLimitInfo 频率限制信息
|
||||
type RateLimitInfo struct {
|
||||
Count int // 请求次数
|
||||
FirstTime time.Time // 首次请求时间
|
||||
LastTime time.Time // 最后请求时间
|
||||
}
|
||||
|
||||
// SMSService 短信验证码服务
|
||||
type SMSService struct {
|
||||
// 验证码存储:key为手机号,value为验证码信息
|
||||
codes sync.Map // map[string]*SMSCodeInfo
|
||||
|
||||
// 手机号频率限制:key为手机号,value为限制信息
|
||||
phoneLimits sync.Map // map[string]*RateLimitInfo
|
||||
|
||||
// IP频率限制:key为IP地址,value为限制信息
|
||||
ipLimits sync.Map // map[string]*RateLimitInfo
|
||||
}
|
||||
|
||||
var (
|
||||
smsServiceInstance *SMSService
|
||||
smsServiceOnce sync.Once
|
||||
)
|
||||
|
||||
// GetSMSService 获取短信服务单例
|
||||
func GetSMSService() *SMSService {
|
||||
smsServiceOnce.Do(func() {
|
||||
smsServiceInstance = &SMSService{}
|
||||
// 启动清理协程,定期清理过期数据
|
||||
go smsServiceInstance.cleanup()
|
||||
})
|
||||
return smsServiceInstance
|
||||
}
|
||||
|
||||
// GenerateCode 生成6位数字验证码
|
||||
func (s *SMSService) GenerateCode() string {
|
||||
// 使用时间戳作为随机种子,确保每次生成的验证码都不同
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
return fmt.Sprintf("%06d", r.Intn(1000000))
|
||||
}
|
||||
|
||||
// SendSMS 发送短信验证码
|
||||
// phone: 手机号
|
||||
// ip: 客户端IP地址
|
||||
// 返回:验证码、错误信息
|
||||
func (s *SMSService) SendSMS(phone, ip string) (string, error) {
|
||||
// 1. 验证手机号格式
|
||||
if !s.validatePhone(phone) {
|
||||
return "", fmt.Errorf("手机号格式不正确")
|
||||
}
|
||||
|
||||
// 2. 检查手机号频率限制(同一手机号1分钟内只能发送1次)
|
||||
if err := s.checkPhoneLimit(phone); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 3. 检查IP频率限制(同一IP1分钟内最多发送5次)
|
||||
if err := s.checkIPLimit(ip); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// 4. 生成验证码
|
||||
code := s.GenerateCode()
|
||||
|
||||
// 5. 存储验证码信息(有效期5分钟)
|
||||
codeInfo := &SMSCodeInfo{
|
||||
Code: code,
|
||||
Phone: phone,
|
||||
IP: ip,
|
||||
CreatedAt: time.Now(),
|
||||
ExpiresAt: time.Now().Add(5 * time.Minute),
|
||||
}
|
||||
s.codes.Store(phone, codeInfo)
|
||||
|
||||
// 6. 更新频率限制记录
|
||||
s.updatePhoneLimit(phone)
|
||||
s.updateIPLimit(ip)
|
||||
|
||||
// TODO: 这里应该调用真实的短信服务API发送验证码
|
||||
// 目前只返回验证码,实际生产环境需要调用短信服务商API
|
||||
fmt.Printf("[SMS] 发送验证码到 %s: %s (IP: %s)\n", phone, code, ip)
|
||||
|
||||
return code, nil
|
||||
}
|
||||
|
||||
// VerifyCode 验证验证码
|
||||
func (s *SMSService) VerifyCode(phone, code string) bool {
|
||||
value, ok := s.codes.Load(phone)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
codeInfo := value.(*SMSCodeInfo)
|
||||
|
||||
// 检查是否过期
|
||||
if time.Now().After(codeInfo.ExpiresAt) {
|
||||
s.codes.Delete(phone)
|
||||
return false
|
||||
}
|
||||
|
||||
// 验证码匹配
|
||||
if codeInfo.Code == code {
|
||||
// 验证成功后删除验证码(一次性使用)
|
||||
s.codes.Delete(phone)
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// validatePhone 验证手机号格式
|
||||
func (s *SMSService) validatePhone(phone string) bool {
|
||||
if len(phone) != 11 {
|
||||
return false
|
||||
}
|
||||
// 简单验证:1开头,第二位3-9
|
||||
if phone[0] != '1' || phone[1] < '3' || phone[1] > '9' {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// checkPhoneLimit 检查手机号频率限制
|
||||
// 同一手机号1分钟内只能发送1次
|
||||
func (s *SMSService) checkPhoneLimit(phone string) error {
|
||||
value, ok := s.phoneLimits.Load(phone)
|
||||
if !ok {
|
||||
return nil // 没有限制记录,可以发送
|
||||
}
|
||||
|
||||
limitInfo := value.(*RateLimitInfo)
|
||||
// 检查是否在限制时间内(1分钟)
|
||||
if time.Since(limitInfo.LastTime) < 1*time.Minute {
|
||||
return fmt.Errorf("操作过于频繁,请1分钟后再试")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkIPLimit 检查IP频率限制
|
||||
// 同一IP1分钟内最多发送5次
|
||||
func (s *SMSService) checkIPLimit(ip string) error {
|
||||
value, ok := s.ipLimits.Load(ip)
|
||||
if !ok {
|
||||
return nil // 没有限制记录,可以发送
|
||||
}
|
||||
|
||||
limitInfo := value.(*RateLimitInfo)
|
||||
|
||||
// 如果超过1分钟,重置计数
|
||||
if time.Since(limitInfo.FirstTime) >= 1*time.Minute {
|
||||
limitInfo.Count = 0
|
||||
limitInfo.FirstTime = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检查是否超过限制(1分钟内最多5次)
|
||||
if limitInfo.Count >= 5 {
|
||||
return fmt.Errorf("请求过于频繁,请稍后再试")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// updatePhoneLimit 更新手机号限制记录
|
||||
func (s *SMSService) updatePhoneLimit(phone string) {
|
||||
value, ok := s.phoneLimits.Load(phone)
|
||||
if !ok {
|
||||
// 创建新记录
|
||||
s.phoneLimits.Store(phone, &RateLimitInfo{
|
||||
Count: 1,
|
||||
FirstTime: time.Now(),
|
||||
LastTime: time.Now(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
limitInfo := value.(*RateLimitInfo)
|
||||
limitInfo.Count++
|
||||
limitInfo.LastTime = time.Now()
|
||||
}
|
||||
|
||||
// updateIPLimit 更新IP限制记录
|
||||
func (s *SMSService) updateIPLimit(ip string) {
|
||||
value, ok := s.ipLimits.Load(ip)
|
||||
if !ok {
|
||||
// 创建新记录
|
||||
s.ipLimits.Store(ip, &RateLimitInfo{
|
||||
Count: 1,
|
||||
FirstTime: time.Now(),
|
||||
LastTime: time.Now(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
limitInfo := value.(*RateLimitInfo)
|
||||
|
||||
// 如果超过1分钟,重置计数
|
||||
if time.Since(limitInfo.FirstTime) >= 1*time.Minute {
|
||||
limitInfo.Count = 1
|
||||
limitInfo.FirstTime = time.Now()
|
||||
limitInfo.LastTime = time.Now()
|
||||
} else {
|
||||
limitInfo.Count++
|
||||
limitInfo.LastTime = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup 定期清理过期数据
|
||||
func (s *SMSService) cleanup() {
|
||||
ticker := time.NewTicker(2 * time.Minute) // 每10分钟清理一次
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
now := time.Now()
|
||||
|
||||
// 清理过期的验证码
|
||||
s.codes.Range(func(key, value interface{}) bool {
|
||||
codeInfo := value.(*SMSCodeInfo)
|
||||
if now.After(codeInfo.ExpiresAt) {
|
||||
s.codes.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// 清理过期的频率限制记录(超过1小时未使用的记录)
|
||||
s.phoneLimits.Range(func(key, value interface{}) bool {
|
||||
limitInfo := value.(*RateLimitInfo)
|
||||
if now.Sub(limitInfo.LastTime) > 1*time.Hour {
|
||||
s.phoneLimits.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
s.ipLimits.Range(func(key, value interface{}) bool {
|
||||
limitInfo := value.(*RateLimitInfo)
|
||||
if now.Sub(limitInfo.LastTime) > 1*time.Hour {
|
||||
s.ipLimits.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"fmt"
|
||||
"log"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AutoCancelUnpaidOrders 自动取消超过30分钟未付款的订单
|
||||
func AutoCancelUnpaidOrders() {
|
||||
log.Println("开始执行自动取消未付款订单任务...")
|
||||
|
||||
db := common.GetDB()
|
||||
var cancelOrders []models.PurchaseOrder
|
||||
if err := db.Model(&models.PurchaseOrder{}).Where("order_status = ? and product_type=?", 3, 2).Find(&cancelOrders).Error; err != nil {
|
||||
log.Printf("查询未付款订单失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
var Ids []uint
|
||||
for _, order := range cancelOrders {
|
||||
Ids = append(Ids, order.ID)
|
||||
}
|
||||
fmt.Println(cancelOrders)
|
||||
db.Model(&models.SalesOrder{}).Where("purchase_order_id in ?", Ids).Update("order_status", 3)
|
||||
|
||||
var config models.SystemConfig
|
||||
db.Where("config_key=?", "order_over_time").First(&config)
|
||||
value, _ := strconv.Atoi(config.ConfigValue)
|
||||
thirtyMinutesAgo := time.Now().Add(-time.Duration(value) * time.Minute)
|
||||
// 查找所有超过30分钟未付款的订单
|
||||
var unpaidOrders []models.PurchaseOrder
|
||||
if err := db.Where("order_status = ? AND created_at < ?", 0, thirtyMinutesAgo).Find(&unpaidOrders).Error; err != nil {
|
||||
log.Printf("查询未付款订单失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("找到 %d 个需要自动取消的未付款订单", len(unpaidOrders))
|
||||
|
||||
// 处理每个未付款订单
|
||||
for _, order := range unpaidOrders {
|
||||
// 开启事务
|
||||
tx := db.Begin()
|
||||
|
||||
// 更新订单状态为已取消
|
||||
if err := tx.Model(&order).Update("order_status", 3).Error; err != nil {
|
||||
tx.Rollback()
|
||||
log.Printf("取消订单 %d 失败: %v", order.ID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 恢复商品库存
|
||||
if order.ProductType == 1 { // 一级商品
|
||||
if err := tx.Model(&models.PrimaryProduct{}).Where("id = ?", order.ProductID).
|
||||
Update("stock_quantity", gorm.Expr("stock_quantity + ?", order.Quantity)).Error; err != nil {
|
||||
tx.Rollback()
|
||||
log.Printf("恢复一级商品 %d 库存失败: %v", order.ProductID, err)
|
||||
continue
|
||||
}
|
||||
} else { // 二级商品
|
||||
if err := tx.Model(&models.SecondaryProduct{}).Where("id = ?", order.ProductID).
|
||||
Update("quantity", gorm.Expr("quantity + ?", order.Quantity)).Error; err != nil {
|
||||
tx.Rollback()
|
||||
log.Printf("恢复二级商品 %d 库存失败: %v", order.ProductID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// 如果是二级商品,还需要取消对应的卖家订单
|
||||
var salesOrder models.SalesOrder
|
||||
if err := tx.Where("purchase_order_id = ?", order.ID).First(&salesOrder).Error; err != nil {
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
tx.Rollback()
|
||||
log.Printf("查询订单 %d 对应的卖家订单失败: %v", order.ID, err)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// 更新卖家订单状态为已取消
|
||||
if err := tx.Model(&salesOrder).Update("order_status", 3).Error; err != nil {
|
||||
tx.Rollback()
|
||||
log.Printf("取消卖家订单 %d 失败: %v", salesOrder.ID, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提交事务
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
log.Printf("提交事务失败: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("成功自动取消订单 ID: %d, 订单号: %s", order.ID, order.OrderNo)
|
||||
|
||||
}
|
||||
|
||||
log.Println("自动取消未付款订单任务执行完成")
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"awesomeProject/internal/common"
|
||||
"awesomeProject/internal/models"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CheckUserExpiry 检查用户过期状态并禁用过期用户
|
||||
func CheckUserExpiry() {
|
||||
log.Println("开始执行用户过期检查任务...")
|
||||
|
||||
db := common.GetDB()
|
||||
now := time.Now()
|
||||
|
||||
// 查找所有已过期但仍然启用的用户
|
||||
var expiredUsers []models.User
|
||||
if err := db.Where("expiry_date IS NOT NULL AND expiry_date < ? AND status = ?", now, 1).Find(&expiredUsers).Error; err != nil {
|
||||
log.Printf("查询过期用户失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(expiredUsers) == 0 {
|
||||
log.Println("没有发现过期用户")
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("发现 %d 个过期用户需要禁用", len(expiredUsers))
|
||||
|
||||
// 批量禁用过期用户
|
||||
var userIDs []uint
|
||||
for _, user := range expiredUsers {
|
||||
userIDs = append(userIDs, user.ID)
|
||||
log.Printf("用户 ID: %d, 手机号: %s, 过期时间: %v",
|
||||
user.ID, user.Phone, user.ExpiryDate.Format("2006-01-02 15:04:05"))
|
||||
}
|
||||
|
||||
// 使用事务批量更新状态
|
||||
tx := db.Begin()
|
||||
if err := tx.Model(&models.User{}).Where("id IN ?", userIDs).Update("status", 0).Error; err != nil {
|
||||
tx.Rollback()
|
||||
log.Printf("批量禁用过期用户失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
log.Printf("提交事务失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
log.Printf("成功禁用 %d 个过期用户", len(userIDs))
|
||||
log.Println("用户过期检查任务执行完成")
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package tasks
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
)
|
||||
|
||||
var CronTask *cron.Cron
|
||||
|
||||
// InitCronTasks 初始化所有定时任务
|
||||
func InitCronTasks() {
|
||||
log.Println("初始化定时任务...")
|
||||
CronTask = cron.New()
|
||||
|
||||
// 添加定时任务:每15分钟检查一次未付款订单
|
||||
_, err := CronTask.AddFunc("*/15 * * * *", AutoCancelUnpaidOrders)
|
||||
if err != nil {
|
||||
log.Printf("添加自动取消订单任务失败: %v", err)
|
||||
} else {
|
||||
log.Println("已添加自动取消订单任务,每15分钟执行一次")
|
||||
}
|
||||
go AutoCancelUnpaidOrders()
|
||||
|
||||
// 添加定时任务:每小时检查一次用户过期状态
|
||||
_, err = CronTask.AddFunc("0 * * * *", CheckUserExpiry)
|
||||
if err != nil {
|
||||
log.Printf("添加用户过期检查任务失败: %v", err)
|
||||
} else {
|
||||
log.Println("已添加用户过期检查任务,每小时执行一次")
|
||||
}
|
||||
// 立即执行一次用户过期检查
|
||||
go CheckUserExpiry()
|
||||
// 启动所有定时任务
|
||||
CronTask.Start()
|
||||
log.Println("所有定时任务已启动")
|
||||
}
|
||||
|
||||
// 添加单个定时任务(保留原有函数以兼容旧代码)
|
||||
func addTask(cronn string, work func()) {
|
||||
_, err := CronTask.AddFunc(cronn, work)
|
||||
if err != nil {
|
||||
log.Printf("添加定时任务失败: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user