This commit is contained in:
solosw
2026-01-05 14:11:34 +08:00
parent 35ef825371
commit 99b11b04e4
658 changed files with 99266 additions and 0 deletions
+229
View File
@@ -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("轮播图删除成功"))
}
+390
View File
@@ -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 ""
}
+631
View File
@@ -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))
}
+511
View File
@@ -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))
}
+368
View File
@@ -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))
}
+633
View File
@@ -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(&currentUseruser).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))
}
+372
View File
@@ -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
}
+164
View File
@@ -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
}
+309
View File
@@ -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))
}
+227
View File
@@ -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))
}
+26
View File
@@ -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))
}
+863
View File
@@ -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 ""
}
+107
View File
@@ -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) {
}
+330
View File
@@ -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,
}
}
+131
View File
@@ -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))
}
+247
View File
@@ -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))
}