package controller import ( "context" "crypto/md5" "fmt" "getErpSendPublishing/utils" "getErpSendPublishing/utils/dbConnectUtil" "log" "net/http" "strconv" "strings" "github.com/gin-gonic/gin" ) // GoodsFormRequest 定义接收的表单数据结构 type GoodsFormRequest struct { ShopID int64 `form:"shopid" binding:"required,min=1"` // 店铺ID,必须大于0 GoodsID int64 `form:"goodsid" binding:"required,min=1"` // 商品ID,必须大于0 ISBN string `form:"isbn" binding:"required"` // ISBN,必须提供 } // CenterBookBatchItem 批量提交的单个商品数据结构 type CenterBookBatchItem struct { ISBN string `json:"isbn"` TotalPrice string `json:"totalPrice"` ImgBigUrl string `json:"imgBigUrl"` } // GoodsController 控制器结构 type GoodsController struct { // 可以在这里注入服务层依赖 } // BatchProcessCenterBooks 批量处理中心图书数据 func BatchProcessCenterBooks(ctx *gin.Context) { // 1. 直接获取参数 shopIDStr := ctx.PostForm("shopid") // 2. 空值检查 if shopIDStr == "" { log.Printf("[WARN] shopid参数为空 (路径: %s, IP: %s)", ctx.FullPath(), ctx.ClientIP()) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "shopid不能为空", "success": false, }) return } // 3. 转换并验证shopid格式 shopID, err := strconv.ParseInt(shopIDStr, 10, 64) if err != nil || shopID <= 0 { log.Printf("[WARN] 无效的shopid: %s (错误: %v)", shopIDStr, err) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "shopid必须为正整数", "success": false, }) return } // 4. 解析JSON请求体中的图书列表 var bookItems []CenterBookBatchItem if err := ctx.BindJSON(&bookItems); err != nil { log.Printf("[WARN] JSON解析失败: %v (路径: %s, IP: %s)", err, ctx.FullPath(), ctx.ClientIP()) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "JSON格式错误", "success": false, }) return } // 5. 检查图书列表是否为空 if len(bookItems) == 0 { log.Printf("[WARN] 图书列表为空 (shopid: %d)", shopID) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "图书列表不能为空", "success": false, }) return } // 6. 使用全局的Redis连接池(避免每次创建新连接) redisClient := utils.RedisTwoForParseFormData if redisClient == nil { log.Printf("[ERROR] Redis连接未初始化") ctx.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "message": "系统内部错误", "success": false, }) return } // 7. 构建Redis key redisKey := fmt.Sprintf("%d", shopID) // 8. 去重处理:遍历图书列表,移除重复的ISBN var deduplicatedItems []CenterBookBatchItem var skippedItems []CenterBookBatchItem for _, item := range bookItems { // 检查ISBN是否为空 if item.ISBN == "" { log.Printf("[WARN] 发现空的ISBN,跳过处理 (shopid: %d)", shopID) continue } // 生成MD5加密的ISBN hash := md5.Sum([]byte(item.ISBN)) encryptedIsbn := fmt.Sprintf("%x", hash) // 检查是否存在重复数据 exists, err := redisClient.HGet(redisKey, encryptedIsbn) if err == nil && exists != "" { // 存在重复数据,记录并跳过 log.Printf("[INFO] 检测到重复数据: shopid=%d, isbn=%s", shopID, item.ISBN) skippedItems = append(skippedItems, item) continue } // 非重复数据,添加到去重后列表 deduplicatedItems = append(deduplicatedItems, item) } // 9. 如果没有有效数据,返回错误 if len(deduplicatedItems) == 0 { log.Printf("[INFO] 所有数据均为重复数据,无有效处理项 (shopid: %d)", shopID) ctx.JSON(http.StatusOK, gin.H{ "code": 1, "message": "所有数据均为重复数据", "success": false, "data": gin.H{ "total": len(bookItems), "skipped": len(skippedItems), "processed": 0, "original_items": bookItems, "deduplicated_items": deduplicatedItems, "skipped_items": skippedItems, }, }) return } // 10. 关键成功日志 log.Printf("[INFO] 批量处理图书数据: shopid=%d, 总数=%d, 去重后=%d, 跳过=%d", shopID, len(bookItems), len(deduplicatedItems), len(skippedItems)) // 11. 返回结果 ctx.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "success": true, "data": gin.H{ "shopid": shopID, "total": len(bookItems), "deduplicated_count": len(deduplicatedItems), "skipped_count": len(skippedItems), "original_items": bookItems, "deduplicated_items": deduplicatedItems, "skipped_items": skippedItems, }, }) } // validateGoodsRequest 业务层面的验证 func (c *GoodsController) validateGoodsRequest(ctx context.Context, req *GoodsFormRequest) error { // 基础验证 if req.ShopID <= 0 { return fmt.Errorf("店铺ID必须大于0") } if req.GoodsID <= 0 { return fmt.Errorf("商品ID必须大于0") } if req.ISBN == "" { return fmt.Errorf("ISBN不能为空") } // 业务限制(根据实际情况调整) if req.ShopID > 1000000000 { return fmt.Errorf("店铺ID超出系统限制") } if req.GoodsID > 1000000000 { return fmt.Errorf("商品ID超出系统限制") } // ISBN长度限制 if len(req.ISBN) > 20 { return fmt.Errorf("ISBN长度超出限制") } return nil } // ParseFormData 表单解析 func ParseFormData(ctx *gin.Context) { // 1. 直接获取参数 shopIDStr := ctx.PostForm("shopid") isbn := ctx.PostForm("isbn") // 2. 空值检查(移除了 goodsid) if shopIDStr == "" || isbn == "" { log.Printf("[WARN] 参数为空 (路径: %s, IP: %s)", ctx.FullPath(), ctx.ClientIP()) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "shopid和isbn不能为空", "success": false, }) return } // 3. 转换并验证数字格式(只保留 shopid 验证) shopID, err := strconv.ParseInt(shopIDStr, 10, 64) if err != nil || shopID <= 0 { log.Printf("[WARN] 无效的shopid: %s (错误: %v)", shopIDStr, err) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "shopid必须为正整数", "success": false, }) return } // 4. 使用全局的Redis连接池(避免每次创建新连接) redisClient := utils.RedisTwoForParseFormData if redisClient == nil { log.Printf("[ERROR] Redis连接未初始化") ctx.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "message": "系统内部错误", "success": false, }) return } // 5. 生成MD5加密的ISBN hash := md5.Sum([]byte(isbn)) encryptedIsbn := fmt.Sprintf("%x", hash) //fmt.Println("encryptedIsbn:", encryptedIsbn) // 6. 构建Redis key redisKey := fmt.Sprintf("%d", shopID) // 7. 检查是否存在重复数据 exists, err := redisClient.HGet(redisKey, encryptedIsbn) if err == nil && exists != "" { // 存在重复数据 log.Printf("[INFO] 检测到重复数据: shopid=%d, isbn=%s", shopID, isbn) ctx.JSON(http.StatusOK, gin.H{ "code": 1, "message": "已有重复数据", "success": false, }) return } // 8. 关键成功日志 log.Printf("[INFO] 表单数据解析成功: shopid=%d, isbn=%s", shopID, isbn) // 9. 返回结果 ctx.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "shopid": shopID, "isbn": isbn, }, "success": true, }) } // NewParseFormData 处理包含更多参数的表单数据 func NewParseFormData(ctx *gin.Context) { // 1. 直接获取参数 shopIDStr := ctx.PostForm("shopId") isbn := ctx.PostForm("isbn") shopType := ctx.PostForm("shopType") price := ctx.PostForm("price") condition := ctx.PostForm("condition") // 2. 空值检查 if shopIDStr == "" || isbn == "" { log.Printf("[WARN] 参数为空 (路径: %s, IP: %s)", ctx.FullPath(), ctx.ClientIP()) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "shopId和isbn不能为空", "success": false, }) return } // 3. 转换并验证数字格式 shopID, err := strconv.ParseInt(shopIDStr, 10, 64) if err != nil || shopID <= 0 { log.Printf("[WARN] 无效的shopId: %s (错误: %v)", shopIDStr, err) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "shopId必须为正整数", "success": false, }) return } // 4. 使用全局的Redis连接池(避免每次创建新连接) redisClient := utils.RedisTwoForParseFormData if redisClient == nil { log.Printf("[ERROR] Redis连接未初始化") ctx.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "message": "系统内部错误", "success": false, }) return } // 5. 构建Redis key redisKey := fmt.Sprintf("%d", shopID) // 6. 根据shopType生成不同的加密key var encryptedKey string switch shopType { case "1", "5": // shopType为1或5时,使用ISBN的MD5 hash := md5.Sum([]byte(isbn)) encryptedKey = fmt.Sprintf("%x", hash) case "2": // shopType为2时,拼接isbn:price:condition后MD5 combinedKey := fmt.Sprintf("%s:%s:%s", isbn, price, condition) hash := md5.Sum([]byte(combinedKey)) encryptedKey = fmt.Sprintf("%x", hash) default: // 其他情况默认使用ISBN的MD5 hash := md5.Sum([]byte(isbn)) encryptedKey = fmt.Sprintf("%x", hash) } // 7. 检查是否存在重复数据 exists, err := redisClient.HGet(redisKey, encryptedKey) if err == nil && exists != "" { // 存在重复数据 log.Printf("[INFO] 检测到重复数据: shopId=%d, isbn=%s, shopType=%s", shopID, isbn, shopType) ctx.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "success": true, "data": gin.H{ "deduplicated": false, "isRepeat": true, "shopId": shopID, "isbn": isbn, "shopType": shopType, "price": price, "condition": condition, }, }) return } // 8. 关键成功日志 log.Printf("[INFO] 数据检查完成: shopId=%d, isbn=%s, shopType=%s, price=%s, condition=%s", shopID, isbn, shopType, price, condition) // 9. 返回结果 ctx.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "success": true, "data": gin.H{ "deduplicated": true, "isRepeat": false, "shopId": shopID, "isbn": isbn, "shopType": shopType, "price": price, "condition": condition, }, }) } // MultipleStores 闲鱼多点店铺去重 func MultipleStores(ctx *gin.Context) { // 1. 直接获取参数 shopIDsStr := ctx.PostForm("shopIds") isbn := ctx.PostForm("isbn") // 2. 空值检查 if shopIDsStr == "" || isbn == "" { log.Printf("[WARN] 参数为空 (路径: %s, IP: %s)", ctx.FullPath(), ctx.ClientIP()) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "shopIds和isbn不能为空", "success": false, }) return } // 3. 使用全局的Redis连接池(避免每次创建新连接) redisClient := utils.RedisTwoForParseFormData if redisClient == nil { log.Printf("[ERROR] Redis连接未初始化") ctx.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "message": "系统内部错误", "success": false, }) return } // 4. 生成MD5加密的ISBN hash := md5.Sum([]byte(isbn)) encryptedIsbn := fmt.Sprintf("%x", hash) // 5. 解析shopIds数组(支持逗号分隔,兼容JSON数组格式如 [id1,id2,id3]) // 去除可能的首尾方括号 shopIDsStr = strings.TrimSpace(shopIDsStr) shopIDsStr = strings.TrimPrefix(shopIDsStr, "[") shopIDsStr = strings.TrimSuffix(shopIDsStr, "]") shopIDStrs := strings.Split(shopIDsStr, ",") // 6. 遍历每个shopId,逐个检查是否存在重复数据 for _, idStr := range shopIDStrs { idStr = strings.TrimSpace(idStr) // 跳过空字符串 if idStr == "" { continue } // 转换并验证shopId格式 shopID, err := strconv.ParseInt(idStr, 10, 64) if err != nil || shopID <= 0 { log.Printf("[WARN] 无效的shopId: %s (错误: %v)", idStr, err) continue } // 构建Redis key redisKey := fmt.Sprintf("%d", shopID) // 检查是否存在重复数据 exists, err := redisClient.HGet(redisKey, encryptedIsbn) if err == nil && exists != "" { // 存在重复数据,立即返回不继续查询 log.Printf("[INFO] 检测到重复数据: shopId=%d, isbn=%s", shopID, isbn) ctx.JSON(http.StatusOK, gin.H{ "code": 1, "message": "已有重复数据", "success": false, "data": gin.H{ "shopId": shopID, "isbn": isbn, }, }) return } } // 7. 关键成功日志 log.Printf("[INFO] 表单数据解析成功: shopIds=%s, isbn=%s", shopIDsStr, isbn) // 8. 返回结果 ctx.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "shopIds": shopIDsStr, "isbn": isbn, }, "success": true, }) } func SingleShopMultipleStores(ctx *gin.Context) { // 1. 直接获取参数 shopIDStr := ctx.PostForm("shopId") isbn := ctx.PostForm("isbn") // 2. 空值检查(移除了 goodsid) if shopIDStr == "" || isbn == "" { log.Printf("[WARN] 参数为空 (路径: %s, IP: %s)", ctx.FullPath(), ctx.ClientIP()) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "shopid和isbn不能为空", "success": false, }) return } // 3. 转换并验证数字格式(只保留 shopid 验证) shopID, err := strconv.ParseInt(shopIDStr, 10, 64) if err != nil || shopID <= 0 { log.Printf("[WARN] 无效的shopid: %s (错误: %v)", shopIDStr, err) ctx.JSON(http.StatusBadRequest, gin.H{ "code": 400, "message": "shopid必须为正整数", "success": false, }) return } // 4. 使用全局的Redis连接池(避免每次创建新连接) redisClient := utils.RedisTwoForParseFormData if redisClient == nil { log.Printf("[ERROR] Redis连接未初始化") ctx.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "message": "系统内部错误", "success": false, }) return } // 5. 使用全局MySQL连接池(单例,在main.go启动时初始化) db := dbConnectUtil.DB if db == nil { log.Printf("[ERROR] 全局MySQL连接池未初始化") ctx.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "message": "系统内部错误", "success": false, }) return } // 6. 生成MD5加密的ISBN hash := md5.Sum([]byte(isbn)) encryptedIsbn := fmt.Sprintf("%x", hash) // 7. 查询当前店铺的mall_id var mallID string err = db.QueryRow(` SELECT mall_id FROM t_shop WHERE shop_type LIKE '%5%' AND del_flag LIKE '%0%' AND id = ?`, shopID).Scan(&mallID) if err != nil { // mall_id未找到,不做去重处理,直接返回成功 log.Printf("[INFO] 未找到店铺mall_id: shopid=%d, err=%v", shopID, err) ctx.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "shopid": shopID, "isbn": isbn, }, "success": true, }) return } // 8. 根据mall_id查询所有关联店铺id rows, err := db.Query(` SELECT id FROM t_shop WHERE shop_type LIKE '%5%' AND del_flag LIKE '%0%' AND mall_id = ?`, mallID) if err != nil { log.Printf("[ERROR] 查询关联店铺失败: mall_id=%s, err=%v", mallID, err) ctx.JSON(http.StatusInternalServerError, gin.H{ "code": 500, "message": "系统内部错误", "success": false, }) return } defer rows.Close() // 9. 遍历所有关联店铺id,参考ParseFormData第5步之后的逻辑检查重复 var relatedShopIDs []int64 for rows.Next() { var relatedShopID int64 if err := rows.Scan(&relatedShopID); err != nil { log.Printf("[WARN] 扫描关联店铺id失败: %v", err) continue } relatedShopIDs = append(relatedShopIDs, relatedShopID) } for _, relatedShopID := range relatedShopIDs { // 构建Redis key redisKey := fmt.Sprintf("%d", relatedShopID) // 检查是否存在重复数据 exists, err := redisClient.HGet(redisKey, encryptedIsbn) if err == nil && exists != "" { // 存在重复数据,立即返回不继续查询 log.Printf("[INFO] 检测到重复数据: shopId=%d, isbn=%s (关联mall_id=%s)", relatedShopID, isbn, mallID) ctx.JSON(http.StatusOK, gin.H{ "code": 1, "message": "已有重复数据", "success": false, "data": gin.H{ "shopId": relatedShopID, "isbn": isbn, "mallId": mallID, "relatedShopCount": len(relatedShopIDs), }, }) return } } // 10. 关键成功日志 log.Printf("[INFO] 多点店铺去重检查完成: shopid=%d, isbn=%s, mall_id=%s, 关联店铺数=%d", shopID, isbn, mallID, len(relatedShopIDs)) // 11. 返回结果 ctx.JSON(http.StatusOK, gin.H{ "code": 0, "message": "success", "data": gin.H{ "shopid": shopID, "isbn": isbn, "mallId": mallID, "relatedShopCount": len(relatedShopIDs), "relatedShopIds": relatedShopIDs, }, "success": true, }) }