package service import ( "errors" "fmt" "gorm.io/gorm" "psi/constant" "psi/database" "psi/models" systemReq "psi/models/request" systemRes "psi/models/response" "strconv" "time" ) type CarService struct{} func (s *CarService) GetCarList(req systemReq.QueryCarRequest, db ...*gorm.DB) ([]systemRes.CarResponse, int64, error) { databaseConn := database.OptionalDB(db...) if req.Page < 1 { req.Page = 1 } if req.PageSize < 1 || req.PageSize > 100 { req.PageSize = 20 } query := databaseConn.Model(&models.Car{}).Where("is_del = ?", 0) if req.Keyword != "" { query = query.Where("code like ? OR name like ?", "%"+req.Keyword+"%", "%"+req.Keyword+"%") } var total int64 if err := query.Count(&total).Error; err != nil { return nil, 0, errors.New("查询小车总数失败") } var cars []models.Car offset := (req.Page - 1) * req.PageSize if err := query.Order("id DESC").Offset(offset).Limit(req.PageSize).Find(&cars).Error; err != nil { return nil, 0, errors.New("查询小车列表失败") } responses := make([]systemRes.CarResponse, 0, len(cars)) for _, car := range cars { resp := systemRes.ConvertCarToResponse(car) var carShops []models.CarShop if err := databaseConn.Where("car_id = ? AND is_del = ?", car.ID, 0).Find(&carShops).Error; err == nil { shops := make([]systemRes.CarShopInfo, 0, len(carShops)) for _, cs := range carShops { shops = append(shops, systemRes.CarShopInfo{ ShopID: strconv.FormatInt(cs.ShopID, 10), ShopName: cs.ShopName, ShopType: cs.ShopType, ShopTypeText: getShopTypeText(cs.ShopType), }) } resp.Shops = shops } responses = append(responses, resp) } return responses, total, nil } func (s *CarService) GetCarByID(id int64, db ...*gorm.DB) (*systemRes.CarResponse, error) { databaseConn := database.OptionalDB(db...) var car models.Car if err := databaseConn.Where("id = ? AND is_del = ?", id, 0).First(&car).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("小车不存在") } return nil, errors.New("查询小车失败") } resp := systemRes.ConvertCarToResponse(car) var carShops []models.CarShop if err := databaseConn.Where("car_id = ? AND is_del = ?", car.ID, 0).Find(&carShops).Error; err == nil { shops := make([]systemRes.CarShopInfo, 0, len(carShops)) for _, cs := range carShops { shops = append(shops, systemRes.CarShopInfo{ ShopID: strconv.FormatInt(cs.ShopID, 10), ShopName: cs.ShopName, ShopType: cs.ShopType, ShopTypeText: getShopTypeText(cs.ShopType), }) } resp.Shops = shops } return &resp, nil } func (s *CarService) CreateCar(req systemReq.CreateCarRequest, id int64, db ...*gorm.DB) (int64, error) { databaseConn := database.OptionalDB(db...) var count int64 databaseConn.Model(&models.Car{}). Where("code = ? AND is_del = ?", req.Code, 0). Count(&count) if count > 0 { return 0, errors.New("小车编号已存在") } var warehouse models.Warehouse if err := databaseConn.Where("id = ? AND is_del = ?", req.WarehouseID, 0).First(&warehouse).Error; err != nil { return 0, errors.New("仓库不存在") } car := models.Car{ WarehouseID: req.WarehouseID, PushType: req.PushType, ReleaseType: req.ReleaseType, Code: req.Code, Name: req.Name, Capacity: req.Capacity, Appearance: req.Appearance, CreatedAt: time.Now().Unix(), UpdatedAt: time.Now().Unix(), IsDel: 0, } if err := databaseConn.Create(&car).Error; err != nil { return 0, errors.New("创建小车失败") } // 处理店铺绑定 if len(req.ShopInfo) > 0 { if err := s.bindShopsToCar(car.ID, id, req.ShopInfo, databaseConn); err != nil { return 0, err } } return car.ID, nil } func (s *CarService) UpdateCar(req systemReq.UpdateCarRequest, id int64, db ...*gorm.DB) error { databaseConn := database.OptionalDB(db...) var car models.Car if err := databaseConn.Where("id = ? AND is_del = ?", req.ID, 0).First(&car).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.New("小车不存在") } return errors.New("查询小车失败") } updates := make(map[string]interface{}) updates["updated_at"] = time.Now().Unix() if req.Name != "" { updates["name"] = req.Name } if req.Capacity != nil { updates["capacity"] = *req.Capacity } if req.Appearance != nil { updates["appearance"] = *req.Appearance } if req.PushType != nil { updates["push_type"] = *req.PushType } if req.ReleaseType != nil { updates["release_type"] = *req.ReleaseType } if err := databaseConn.Model(&car).Updates(updates).Error; err != nil { return errors.New("更新小车失败") } // 处理店铺绑定 if len(req.ShopInfo) > 0 { if err := s.bindShopsToCar(car.ID, id, req.ShopInfo, databaseConn); err != nil { return err } } return nil } func (s *CarService) DeleteCar(id int64, db ...*gorm.DB) error { databaseConn := database.OptionalDB(db...) if id == 0 { return errors.New("小车ID不能为空") } var car models.Car if err := databaseConn.Where("id = ? AND is_del = ?", id, 0).First(&car).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.New("小车不存在") } return errors.New("查询小车失败") } var taskCount int64 if err := databaseConn.Model(&models.WaveTask{}).Where("car_id = ? AND is_del = ? AND status NOT IN (?, ?)", id, 0, constant.WaveStatusCompleted, constant.WaveStatusCancelled).Count(&taskCount).Error; err != nil { return errors.New("查询任务状态失败") } if taskCount > 0 { return errors.New("该小车存在未完成的任务,无法删除") } now := time.Now().Unix() tx := databaseConn.Begin() if tx.Error != nil { return errors.New("开启事务失败") } if err := tx.Model(&car).Updates(map[string]interface{}{ "is_del": 1, "updated_at": now, }).Error; err != nil { tx.Rollback() return errors.New("删除小车失败") } if err := tx.Model(&models.CarShop{}).Where("car_id = ? AND is_del = ?", id, 0).Updates(map[string]interface{}{ "is_del": 1, "updated_at": now, }).Error; err != nil { tx.Rollback() return errors.New("删除小车店铺绑定关系失败") } if err := tx.Commit().Error; err != nil { return errors.New("提交事务失败") } return nil } // bindShopsToCar 处理小车与店铺的绑定关系(新增/更新时调用) func (s *CarService) bindShopsToCar(carID, id int64, info []map[string]interface{}, db *gorm.DB) error { // 获取当前已存在的绑定关系 var existingBindings []models.CarShop if err := db.Where("car_id = ? AND is_del = ?", carID, 0).Find(&existingBindings).Error; err != nil { return errors.New("查询现有绑定关系失败") } // 构建已存在店铺ID的集合 existingShopIDs := make(map[int64]bool) for _, binding := range existingBindings { existingShopIDs[binding.ShopID] = true } // 构建传入的店铺ID集合 newShopIDs := make(map[int64]bool) currentTime := time.Now().Unix() for _, shopInfo := range info { shopIDStr := getStringFromInfo(shopInfo, "id") if shopIDStr == "" { continue } var shopID int64 fmt.Sscanf(shopIDStr, "%d", &shopID) if shopID == 0 { continue } newShopIDs[shopID] = true } // 判断是否需要更新:如果两个集合完全相同,则不需要操作 if len(existingShopIDs) == len(newShopIDs) { sameCount := 0 for shopID := range existingShopIDs { if newShopIDs[shopID] { sameCount++ } } if sameCount == len(existingShopIDs) { return nil } } // 找出需要删除的店铺(在旧列表中但不在新列表中) var toDeleteShopIDs []int64 for shopID := range existingShopIDs { if !newShopIDs[shopID] { toDeleteShopIDs = append(toDeleteShopIDs, shopID) } } // 删除不在新列表中的绑定关系 if len(toDeleteShopIDs) > 0 { if err := db.Model(&models.CarShop{}). Where("car_id = ? AND shop_id IN ? AND is_del = ?", carID, toDeleteShopIDs, 0). Update("is_del", 1).Error; err != nil { return errors.New("删除店铺绑定关系失败") } } // 找出需要新增的店铺绑定(在新列表中但不在旧列表中) var newBindings []models.CarShop for _, shopInfo := range info { shopIDStr := getStringFromInfo(shopInfo, "id") if shopIDStr == "" { continue } var shopID int64 fmt.Sscanf(shopIDStr, "%d", &shopID) if shopID == 0 { continue } // 如果已经存在,则跳过 if existingShopIDs[shopID] { continue } // 获取店铺信息 var shop models.Shop err := db.Where("id = ? AND del_flag = ?", shopID, 0).First(&shop).Error if err != nil { // 使用新创建的店铺数据创建绑定关系 shopTypeStr := getStringFromInfo(shopInfo, "shop_type") var shopType int8 = 1 if shopTypeStr != "" { fmt.Sscanf(shopTypeStr, "%d", &shopType) } shopStatusStr := getStringFromInfo(shopInfo, "status") var shopStatus int8 = 1 if shopTypeStr != "" { fmt.Sscanf(shopStatusStr, "%d", &shopStatus) } shopMallIdStr := getStringFromInfo(shopInfo, "mall_id") var shopMallId int64 = 1 if shopMallIdStr != "" { fmt.Sscanf(shopMallIdStr, "%d", &shopMallId) } // 店铺不存在,先同步创建店铺 newShop := models.Shop{ ID: shopID, MallID: shopMallId, ShopType: shopType, ShopAliasName: getStringFromInfo(shopInfo, "shop_alias_name"), Status: shopStatus, DelFlag: 0, CreateTime: currentTime, UpdateTime: currentTime, CreateBy: id, UpdateBy: 0, } if err := db.Create(&newShop).Error; err != nil { return errors.New("同步创建店铺失败") } newBinding := models.CarShop{ CarID: carID, ShopID: shopID, ShopName: getStringFromInfo(shopInfo, "shop_alias_name"), ShopType: shopType, CreatedAt: currentTime, UpdatedAt: currentTime, IsDel: 0, } newBindings = append(newBindings, newBinding) } else { // 使用店铺表中的数据创建绑定关系 newBinding := models.CarShop{ CarID: carID, ShopID: shopID, ShopName: shop.ShopAliasName, ShopType: shop.ShopType, CreatedAt: currentTime, UpdatedAt: currentTime, IsDel: 0, } newBindings = append(newBindings, newBinding) } } // 批量创建新的绑定关系 if len(newBindings) > 0 { if err := db.Create(&newBindings).Error; err != nil { return errors.New("创建店铺绑定关系失败") } } return nil } // GetCarCapacityByID 根据小车ID查询容量 func (s *CarService) GetCarCapacityByID(carID int64, db ...*gorm.DB) (int64, error) { databaseConn := database.OptionalDB(db...) if carID <= 0 { return 0, errors.New("小车ID无效") } var car models.Car if err := databaseConn.Where("id = ? AND is_del = ?", carID, 0).Select("capacity").First(&car).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return 0, errors.New("小车不存在或已删除") } return 0, errors.New("查询小车容量失败") } if car.Capacity <= 0 { return 0, errors.New("小车容量配置不正确") } return car.Capacity, nil } // 辅助函数:从 info map 中安全获取 string 类型值 func getStringFromInfo(info map[string]interface{}, key string) string { if info == nil { return "" } if val, ok := info[key]; ok { if str, ok := val.(string); ok { return str } } return "" } // 辅助函数:从 info map 中安全获取 int64 类型值 func getInt64FromInfo(info map[string]interface{}, key string) int64 { if info == nil { return 0 } if val, ok := info[key]; ok { switch v := val.(type) { case float64: return int64(v) case int64: return v case int: return int64(v) } } return 0 } // 辅助函数:从 info map 中安全获取 int8 类型值 func getInt8FromInfo(info map[string]interface{}, key string, defaultVal int8) int8 { if info == nil { return defaultVal } if val, ok := info[key]; ok { switch v := val.(type) { case float64: return int8(v) case int64: return int8(v) case int: return int8(v) case int8: return v } } return defaultVal } // getShopTypeText 获取店铺类型文本描述 func getShopTypeText(shopType int8) string { switch shopType { case 1: return "拼多多" case 2: return "孔夫子" case 5: return "闲鱼" default: return "未知" } }