1037 lines
31 KiB
Go
1037 lines
31 KiB
Go
package routes
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"log"
|
||
"net/http"
|
||
"os"
|
||
"runtime"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
"xianyv/controller"
|
||
_type "xianyv/type"
|
||
"xianyv/utils/requestUtil"
|
||
)
|
||
|
||
// 简单的全局令牌桶限速器(本地测试用,QPS = 10)
|
||
const qpsLimit = 10
|
||
|
||
var tokenBucket chan struct{}
|
||
|
||
func init() {
|
||
tokenBucket = make(chan struct{}, qpsLimit)
|
||
// 预先填充令牌
|
||
for i := 0; i < qpsLimit; i++ {
|
||
tokenBucket <- struct{}{}
|
||
}
|
||
|
||
// 以 QPS 速率补充令牌
|
||
go func() {
|
||
ticker := time.NewTicker(time.Second / time.Duration(qpsLimit))
|
||
defer ticker.Stop()
|
||
for range ticker.C {
|
||
select {
|
||
case tokenBucket <- struct{}{}:
|
||
default:
|
||
// bucket 已满,跳过
|
||
}
|
||
}
|
||
}()
|
||
}
|
||
|
||
type routeHandler struct {
|
||
cfg *_type.Config
|
||
}
|
||
|
||
// RegisterRoutes 注册所有路由
|
||
func RegisterRoutes(cfg *_type.Config) *http.ServeMux {
|
||
r := http.NewServeMux()
|
||
handler := &routeHandler{cfg: cfg}
|
||
|
||
// 注册健康检查路由
|
||
r.HandleFunc("GET /health", healthHandler)
|
||
|
||
// 请求类目Id处理器,留作备用
|
||
r.HandleFunc("POST /xianyv/getCategoryList", handler.getCategoryListHandler)
|
||
|
||
// 发布处理器
|
||
r.HandleFunc("POST /xianyv/creatGoodsBatch", handler.creatGoodsBatchHandler)
|
||
|
||
// 上架处理器
|
||
r.HandleFunc("POST /xianyv/publish", publishHandler)
|
||
|
||
// 下架处理器
|
||
r.HandleFunc("POST /xianyv/downShelf", downShelfHandler)
|
||
|
||
// 擦亮商品处理器
|
||
r.HandleFunc("POST /xianyv/flash", flashHandler)
|
||
|
||
// 改价处理器
|
||
r.HandleFunc("POST /xianyv/editPrices", editPricesHandler)
|
||
|
||
// 改库存处理器
|
||
r.HandleFunc("POST /xianyv/editStock", editStockHandler)
|
||
|
||
// 查商品处理器
|
||
r.HandleFunc("POST /xianyv/getGoodsList", getGoodsListHandler)
|
||
|
||
// 新增:跨页统计 outer_id 的处理器,返回完整统计信息
|
||
r.HandleFunc("POST /xianyv/getGoodsListAllStats", getGoodsListAllStatsHandler)
|
||
|
||
// 查店铺处理器
|
||
r.HandleFunc("POST /xianyv/getShopList", getShopListHandler)
|
||
|
||
// 查询订单列表处理器
|
||
r.HandleFunc("POST /xianyv/getOrderList", getOrderListHandler)
|
||
|
||
// 发布其他类测试(实际不使用)
|
||
r.HandleFunc("POST /xianyv/onlineTest", handler.onlineTestHandler)
|
||
|
||
// 获取商品详情处理器
|
||
r.HandleFunc("POST /xianyv/getGoodsDetail", getGoodsDetailHandler)
|
||
|
||
// 注册默认路由(处理未找到的路由)
|
||
r.HandleFunc("/", notFoundHandler)
|
||
|
||
return r
|
||
}
|
||
|
||
// 健康检查处理器
|
||
func healthHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
response := map[string]interface{}{
|
||
"status": "ok",
|
||
"timestamp": time.Now().Format(time.RFC3339),
|
||
"goroutines": runtime.NumGoroutine(),
|
||
"message": "服务运行正常",
|
||
}
|
||
|
||
w.WriteHeader(http.StatusOK)
|
||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||
log.Printf("错误的编码响应: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "错误的编码响应: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 发布处理器
|
||
func (h *routeHandler) creatGoodsBatchHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
var body _type.Body
|
||
// 请求参数处理(计时)
|
||
paramsStart := time.Now()
|
||
body, err := requestUtil.RequestParams(r)
|
||
if err != nil {
|
||
log.Printf("请求参数处理失败: %v (elapsed=%v)", err, time.Since(paramsStart))
|
||
errMsg := fmt.Sprintf(`{"error": "请求参数处理失败: %v"}`, err)
|
||
http.Error(w, errMsg, http.StatusBadRequest)
|
||
return
|
||
}
|
||
log.Printf("请求参数解析完成 (elapsed=%v)", time.Since(paramsStart))
|
||
|
||
// 类目处理
|
||
goodsController := &controller.GoodsController{
|
||
ExcelPath: h.cfg.File.ExcelPath,
|
||
TxtPath: h.cfg.File.TxtPath,
|
||
SheetName: h.cfg.File.SheetName,
|
||
}
|
||
|
||
// 调用业务逻辑(计时)
|
||
handlerStart := time.Now()
|
||
createResponse, err := goodsController.GoodsCreatController(
|
||
body,
|
||
h.cfg.BatchCreatRequest.Path,
|
||
h.cfg.App.Domain,
|
||
false,
|
||
)
|
||
log.Printf("GoodsCreatController 完成 (elapsed=%v)", time.Since(handlerStart))
|
||
|
||
if err != nil {
|
||
errorMsg := fmt.Sprintf(`{"error": "请求解析或验证失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
// 返回Response结果
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(createResponse)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 上架处理器
|
||
func publishHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
var body _type.ListedProducts
|
||
body.SpecifyPublishTime = r.FormValue("specify_publish_time")
|
||
body.NotifyUrl = r.FormValue("notify_url")
|
||
appIdStr := r.FormValue("appId")
|
||
body.AppSecret = r.FormValue("appSecret")
|
||
body.AppId, _ = strconv.Atoi(appIdStr)
|
||
fmt.Printf("appId: %d", body.AppId)
|
||
body.NotifyUrl = "http://146.56.192.164:19095/huidiao/goofish/goofishGoods"
|
||
|
||
// 从绝对路径读取txt文件
|
||
filePath := "D:/workSpace/batchWorkSpace/josn/product_ids.txt" // 请替换为实际的绝对路径
|
||
content, err := os.ReadFile(filePath)
|
||
if err != nil {
|
||
log.Printf("读取文件失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "读取文件失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 按行分割文件内容
|
||
lines := strings.Split(strings.TrimSpace(string(content)), "\n")
|
||
if len(lines) == 0 {
|
||
errorMsg := `{"error": "文件为空"}`
|
||
http.Error(w, errorMsg, http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
// 首先解析表单(只需要解析一次)
|
||
if err := r.ParseForm(); err != nil {
|
||
http.Error(w, "Failed to parse form", http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
var responses []string
|
||
var hasError bool
|
||
|
||
const qps = 9
|
||
const interval = time.Second / time.Duration(qps) // 100ms
|
||
|
||
// 循环处理每个productId
|
||
for i, line := range lines {
|
||
line = strings.TrimSpace(line)
|
||
if line == "" {
|
||
continue // 跳过空行
|
||
}
|
||
// 添加延时(从第二个请求开始)
|
||
if i > 0 {
|
||
time.Sleep(interval)
|
||
}
|
||
productIdStr := line
|
||
|
||
body.ProductId, _ = strconv.Atoi(productIdStr)
|
||
|
||
// 获取 user_name 数组
|
||
body.UserName = r.Form["user_name[]"]
|
||
log.Printf("上架测试:%v", body)
|
||
|
||
response, err := controller.Publish(body)
|
||
if err != nil {
|
||
log.Printf("上架商品失败 (productId: %s): %v", productIdStr, err)
|
||
errorResponse := fmt.Sprintf(`{"productId": "%s", "error": "上架商品失败: %v"}`, productIdStr, err)
|
||
responses = append(responses, errorResponse)
|
||
hasError = true
|
||
continue
|
||
}
|
||
|
||
successResponse := fmt.Sprintf(`{"productId": "%s", "response": %s}`, productIdStr, string(response))
|
||
fmt.Printf("successResponse: %v\n", successResponse)
|
||
}
|
||
|
||
// 构建最终响应
|
||
|
||
if hasError {
|
||
w.WriteHeader(http.StatusMultiStatus) // 207 Multi-Status
|
||
} else {
|
||
w.WriteHeader(http.StatusOK)
|
||
}
|
||
|
||
}
|
||
|
||
// 下架处理器
|
||
func downShelfHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
var body _type.DownAndFlash
|
||
productIdStr := r.FormValue("productId")
|
||
appIdStr := r.FormValue("appId")
|
||
body.AppSecret = r.FormValue("appSecret")
|
||
if i, err := strconv.ParseInt(productIdStr, 10, 64); err == nil {
|
||
body.ProductId = int64(i)
|
||
} else {
|
||
body.ProductId = 0
|
||
}
|
||
body.AppId, _ = strconv.Atoi(appIdStr)
|
||
|
||
response, err := controller.DownShelf(body)
|
||
if err != nil {
|
||
log.Printf("下架商品失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "下架商品失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 擦亮商品处理器
|
||
func flashHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
var body _type.DownAndFlash
|
||
productIdStr := r.FormValue("productId")
|
||
appIdStr := r.FormValue("appId")
|
||
body.AppSecret = r.FormValue("appSecret")
|
||
if i, err := strconv.ParseInt(productIdStr, 10, 64); err == nil {
|
||
body.ProductId = int64(i)
|
||
} else {
|
||
body.ProductId = 0
|
||
}
|
||
body.AppId, _ = strconv.Atoi(appIdStr)
|
||
|
||
response, err := controller.Flash(body)
|
||
if err != nil {
|
||
log.Printf("擦亮商品失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "擦亮商品失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 改价处理器
|
||
func editPricesHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
var edit _type.EditPrices
|
||
productIdStr := r.FormValue("productId")
|
||
pricesStr := r.FormValue("price")
|
||
originalPriceStr := r.FormValue("originalPrice")
|
||
appIdStr := r.FormValue("appId")
|
||
edit.AppSecret = r.FormValue("appSecret")
|
||
if i, err := strconv.ParseInt(productIdStr, 10, 64); err == nil {
|
||
edit.ProductId = int64(i)
|
||
} else {
|
||
edit.ProductId = 0
|
||
}
|
||
if i, err := strconv.ParseInt(pricesStr, 10, 64); err == nil {
|
||
edit.Price = int64(i)
|
||
} else {
|
||
edit.Price = 0
|
||
}
|
||
if i, err := strconv.ParseInt(originalPriceStr, 10, 64); err == nil {
|
||
edit.OriginalPrice = int64(i)
|
||
} else {
|
||
edit.OriginalPrice = 0
|
||
}
|
||
edit.AppId, _ = strconv.Atoi(appIdStr)
|
||
|
||
fmt.Printf("修改价格参数:%v", edit)
|
||
response, err := controller.EditPrices(edit)
|
||
if err != nil {
|
||
log.Printf("修改价格失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "修改价格失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 修改库存处理器
|
||
func editStockHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
var edit _type.EditStock
|
||
productIdStr := r.FormValue("productId")
|
||
stockStr := r.FormValue("stock")
|
||
appIdStr := r.FormValue("appId")
|
||
edit.AppSecret = r.FormValue("appSecret")
|
||
if i, err := strconv.ParseInt(productIdStr, 10, 64); err == nil {
|
||
edit.ProductId = int64(i)
|
||
} else {
|
||
edit.ProductId = 0
|
||
}
|
||
if i, err := strconv.ParseInt(stockStr, 10, 32); err == nil {
|
||
edit.Stock = int32(i)
|
||
} else {
|
||
edit.Stock = 0
|
||
}
|
||
edit.AppId, _ = strconv.Atoi(appIdStr)
|
||
|
||
fmt.Printf("修改库存参数:%v", edit)
|
||
response, err := controller.EditStock(edit)
|
||
if err != nil {
|
||
log.Printf("修改库存失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "修改库存失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 获取商品列表处理器
|
||
func getGoodsListHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
// 检查是否有online_time字段
|
||
onlineTimeStr := r.FormValue("online_time")
|
||
|
||
var response []byte
|
||
var err error
|
||
|
||
if onlineTimeStr != "" {
|
||
var selectGoodsList _type.SelectGoodsListWithTime
|
||
timeStrs := strings.Split(onlineTimeStr, ",")
|
||
var onlineTime []int64
|
||
for _, timeStr := range timeStrs {
|
||
if t, err := strconv.ParseInt(strings.TrimSpace(timeStr), 10, 64); err == nil {
|
||
onlineTime = append(onlineTime, t)
|
||
}
|
||
}
|
||
selectGoodsList.OfflineTime = onlineTime
|
||
|
||
productStatusStr := r.FormValue("productStatus")
|
||
appIdStr := r.FormValue("appId")
|
||
selectGoodsList.AppSecret = r.FormValue("appSecret")
|
||
pageNoStr := r.FormValue("pageNo")
|
||
pageSizeStr := r.FormValue("pageSize")
|
||
|
||
if i, err := strconv.ParseInt(productStatusStr, 10, 32); err == nil {
|
||
selectGoodsList.ProductStatus = int32(i)
|
||
}
|
||
if i, err := strconv.Atoi(appIdStr); err == nil {
|
||
selectGoodsList.AppId = i
|
||
}
|
||
if i, err := strconv.ParseInt(pageNoStr, 10, 32); err == nil {
|
||
selectGoodsList.PageNo = int32(i)
|
||
}
|
||
if i, err := strconv.ParseInt(pageSizeStr, 10, 32); err == nil {
|
||
selectGoodsList.PageSize = int32(i)
|
||
}
|
||
|
||
response, err = controller.SelectGoodsListWithTime(selectGoodsList)
|
||
} else {
|
||
var selectGoodsList _type.SelectGoodsListWithoutTime
|
||
productStatusStr := r.FormValue("productStatus")
|
||
appIdStr := r.FormValue("appId")
|
||
selectGoodsList.AppSecret = r.FormValue("appSecret")
|
||
pageNoStr := r.FormValue("pageNo")
|
||
pageSizeStr := r.FormValue("pageSize")
|
||
|
||
if i, err := strconv.ParseInt(productStatusStr, 10, 32); err == nil {
|
||
selectGoodsList.ProductStatus = int32(i)
|
||
}
|
||
if i, err := strconv.Atoi(appIdStr); err == nil {
|
||
selectGoodsList.AppId = i
|
||
}
|
||
if i, err := strconv.ParseInt(pageNoStr, 10, 32); err == nil {
|
||
selectGoodsList.PageNo = int32(i)
|
||
}
|
||
if i, err := strconv.ParseInt(pageSizeStr, 10, 32); err == nil {
|
||
selectGoodsList.PageSize = int32(i)
|
||
}
|
||
|
||
response, err = controller.SelectGoodsListWithoutTime(selectGoodsList)
|
||
}
|
||
|
||
if err != nil {
|
||
log.Printf("查询商品失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "查询商品失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
// 添加统计功能
|
||
responseWithStats, err := addOuterIDStatsToResponse(response)
|
||
if err != nil {
|
||
log.Printf("统计outer_id失败,返回原始数据: %v", err)
|
||
// 统计失败时返回原始数据
|
||
responseWithStats = response
|
||
} else {
|
||
log.Printf("成功添加outer_id统计信息")
|
||
}
|
||
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(responseWithStats)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// getGoodsListAllStatsHandler - 在 getGoodsList 的基础上做跨页拉取并统计所有页的 outer_id 分布,
|
||
// 返回原始分页响应并在 data.outer_id_stats 中附加总体统计信息
|
||
func getGoodsListAllStatsHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
|
||
// 检查是否有online_time字段
|
||
onlineTimeStr := r.FormValue("online_time")
|
||
|
||
var response []byte
|
||
var err error
|
||
|
||
// 通用变量
|
||
var initialPageNo int32 = 1
|
||
var pageSize int32 = 100
|
||
var combinedList []map[string]interface{}
|
||
|
||
if onlineTimeStr != "" {
|
||
var selectGoodsList _type.SelectGoodsListWithTime
|
||
timeStrs := strings.Split(onlineTimeStr, ",")
|
||
var onlineTime []int64
|
||
for _, timeStr := range timeStrs {
|
||
if t, err := strconv.ParseInt(strings.TrimSpace(timeStr), 10, 64); err == nil {
|
||
onlineTime = append(onlineTime, t)
|
||
}
|
||
}
|
||
selectGoodsList.OfflineTime = onlineTime
|
||
|
||
productStatusStr := r.FormValue("productStatus")
|
||
appIdStr := r.FormValue("appId")
|
||
selectGoodsList.AppSecret = r.FormValue("appSecret")
|
||
pageNoStr := r.FormValue("pageNo")
|
||
pageSizeStr := r.FormValue("pageSize")
|
||
|
||
if i, err := strconv.ParseInt(productStatusStr, 10, 32); err == nil {
|
||
selectGoodsList.ProductStatus = int32(i)
|
||
}
|
||
if i, err := strconv.Atoi(appIdStr); err == nil {
|
||
selectGoodsList.AppId = i
|
||
}
|
||
if i, err := strconv.ParseInt(pageNoStr, 10, 32); err == nil {
|
||
selectGoodsList.PageNo = int32(i)
|
||
}
|
||
if i, err := strconv.ParseInt(pageSizeStr, 10, 32); err == nil {
|
||
selectGoodsList.PageSize = int32(i)
|
||
}
|
||
|
||
initialPageNo = selectGoodsList.PageNo
|
||
if selectGoodsList.PageSize > 0 {
|
||
pageSize = selectGoodsList.PageSize
|
||
}
|
||
|
||
// 第一次请求
|
||
response, err = controller.SelectGoodsListWithTime(selectGoodsList)
|
||
if err != nil {
|
||
log.Printf("查询商品失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "查询商品失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
var resp struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data struct {
|
||
Count int `json:"count"`
|
||
List []map[string]interface{} `json:"list"`
|
||
PageNo int `json:"page_no"`
|
||
PageSize int `json:"page_size"`
|
||
} `json:"data"`
|
||
}
|
||
if err := json.Unmarshal(response, &resp); err != nil {
|
||
// 无法解析则返回原始响应
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
}
|
||
return
|
||
}
|
||
|
||
combinedList = append(combinedList, resp.Data.List...)
|
||
|
||
totalCount := resp.Data.Count
|
||
if resp.Data.PageSize > 0 {
|
||
pageSize = int32(resp.Data.PageSize)
|
||
}
|
||
|
||
totalPages := 1
|
||
if pageSize > 0 {
|
||
totalPages = (totalCount + int(pageSize) - 1) / int(pageSize)
|
||
}
|
||
|
||
for p := 1; p <= totalPages; p++ {
|
||
if int32(p) == initialPageNo {
|
||
continue
|
||
}
|
||
selectGoodsList.PageNo = int32(p)
|
||
pageResp, pageErr := controller.SelectGoodsListWithTime(selectGoodsList)
|
||
if pageErr != nil {
|
||
log.Printf("Warning: 获取第 %d 页失败: %v", p, pageErr)
|
||
continue
|
||
}
|
||
var pageParsed struct {
|
||
Data struct {
|
||
List []map[string]interface{} `json:"list"`
|
||
} `json:"data"`
|
||
}
|
||
if perr := json.Unmarshal(pageResp, &pageParsed); perr != nil {
|
||
log.Printf("Warning: 解析第 %d 页响应失败: %v", p, perr)
|
||
continue
|
||
}
|
||
combinedList = append(combinedList, pageParsed.Data.List...)
|
||
}
|
||
|
||
// 构造最终响应:在原始 response 的 data 中注入 outer_id_stats
|
||
finalRespMap := make(map[string]interface{})
|
||
if err := json.Unmarshal(response, &finalRespMap); err != nil {
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
}
|
||
return
|
||
}
|
||
|
||
// 计算统计
|
||
outerCount := make(map[string]int)
|
||
for _, item := range combinedList {
|
||
if v, ok := item["outer_id"].(string); ok {
|
||
if v == "" {
|
||
outerCount["(空)"]++
|
||
} else {
|
||
outerCount[v]++
|
||
}
|
||
}
|
||
}
|
||
stats := map[string]interface{}{
|
||
"total_count": len(combinedList),
|
||
"distribution": outerCount,
|
||
}
|
||
|
||
if dataMap, ok := finalRespMap["data"].(map[string]interface{}); ok {
|
||
dataMap["outer_id_stats"] = stats
|
||
finalRespMap["data"] = dataMap
|
||
}
|
||
|
||
bts, merr := json.Marshal(finalRespMap)
|
||
if merr != nil {
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
}
|
||
return
|
||
}
|
||
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(bts)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
return
|
||
|
||
} else {
|
||
// 无 time 字段的情况
|
||
var selectGoodsList _type.SelectGoodsListWithoutTime
|
||
productStatusStr := r.FormValue("productStatus")
|
||
appIdStr := r.FormValue("appId")
|
||
selectGoodsList.AppSecret = r.FormValue("appSecret")
|
||
pageNoStr := r.FormValue("pageNo")
|
||
pageSizeStr := r.FormValue("pageSize")
|
||
|
||
if i, err := strconv.ParseInt(productStatusStr, 10, 32); err == nil {
|
||
selectGoodsList.ProductStatus = int32(i)
|
||
}
|
||
if i, err := strconv.Atoi(appIdStr); err == nil {
|
||
selectGoodsList.AppId = i
|
||
}
|
||
if i, err := strconv.ParseInt(pageNoStr, 10, 32); err == nil {
|
||
selectGoodsList.PageNo = int32(i)
|
||
}
|
||
if i, err := strconv.ParseInt(pageSizeStr, 10, 32); err == nil {
|
||
selectGoodsList.PageSize = int32(i)
|
||
}
|
||
|
||
initialPageNo = selectGoodsList.PageNo
|
||
if selectGoodsList.PageSize > 0 {
|
||
pageSize = selectGoodsList.PageSize
|
||
}
|
||
|
||
response, err = controller.SelectGoodsListWithoutTime(selectGoodsList)
|
||
if err != nil {
|
||
log.Printf("查询商品失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "查询商品失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
|
||
var resp struct {
|
||
Code int `json:"code"`
|
||
Msg string `json:"msg"`
|
||
Data struct {
|
||
Count int `json:"count"`
|
||
List []map[string]interface{} `json:"list"`
|
||
PageNo int `json:"page_no"`
|
||
PageSize int `json:"page_size"`
|
||
} `json:"data"`
|
||
}
|
||
if err := json.Unmarshal(response, &resp); err != nil {
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
}
|
||
return
|
||
}
|
||
|
||
combinedList = append(combinedList, resp.Data.List...)
|
||
|
||
totalCount := resp.Data.Count
|
||
if resp.Data.PageSize > 0 {
|
||
pageSize = int32(resp.Data.PageSize)
|
||
}
|
||
|
||
totalPages := 1
|
||
if pageSize > 0 {
|
||
totalPages = (totalCount + int(pageSize) - 1) / int(pageSize)
|
||
}
|
||
|
||
for p := 1; p <= totalPages; p++ {
|
||
if int32(p) == initialPageNo {
|
||
continue
|
||
}
|
||
selectGoodsList.PageNo = int32(p)
|
||
pageResp, pageErr := controller.SelectGoodsListWithoutTime(selectGoodsList)
|
||
if pageErr != nil {
|
||
log.Printf("Warning: 获取第 %d 页失败: %v", p, pageErr)
|
||
continue
|
||
}
|
||
var pageParsed struct {
|
||
Data struct {
|
||
List []map[string]interface{} `json:"list"`
|
||
} `json:"data"`
|
||
}
|
||
if perr := json.Unmarshal(pageResp, &pageParsed); perr != nil {
|
||
log.Printf("Warning: 解析第 %d 页响应失败: %v", p, perr)
|
||
continue
|
||
}
|
||
combinedList = append(combinedList, pageParsed.Data.List...)
|
||
}
|
||
|
||
finalRespMap := make(map[string]interface{})
|
||
if err := json.Unmarshal(response, &finalRespMap); err != nil {
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
}
|
||
return
|
||
}
|
||
|
||
outerCount := make(map[string]int)
|
||
for _, item := range combinedList {
|
||
if v, ok := item["outer_id"].(string); ok {
|
||
if v == "" {
|
||
outerCount["(空)"]++
|
||
} else {
|
||
outerCount[v]++
|
||
}
|
||
}
|
||
}
|
||
stats := map[string]interface{}{
|
||
"total_count": len(combinedList),
|
||
"distribution": outerCount,
|
||
}
|
||
|
||
if dataMap, ok := finalRespMap["data"].(map[string]interface{}); ok {
|
||
dataMap["outer_id_stats"] = stats
|
||
finalRespMap["data"] = dataMap
|
||
}
|
||
|
||
bts, merr := json.Marshal(finalRespMap)
|
||
if merr != nil {
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
}
|
||
return
|
||
}
|
||
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(bts)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
return
|
||
}
|
||
}
|
||
|
||
// 获取店铺列表处理器
|
||
func getShopListHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
var body _type.GetShopList
|
||
appIdStr := r.FormValue("appId")
|
||
body.AppSecret = r.FormValue("appSecret")
|
||
body.AppId, _ = strconv.Atoi(appIdStr)
|
||
response, err := controller.GetShopList(body)
|
||
if err != nil {
|
||
log.Printf("获取店铺列表失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "获取店铺列表失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 获取订单列表处理器
|
||
func getOrderListHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
var body _type.GetShopList
|
||
appIdStr := r.FormValue("appId")
|
||
body.AppSecret = r.FormValue("appSecret")
|
||
body.AppId, _ = strconv.Atoi(appIdStr)
|
||
response, err := controller.GetOrderList(body)
|
||
if err != nil {
|
||
log.Printf("获取店铺列表失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "获取店铺列表失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 发布处理器
|
||
func (h *routeHandler) onlineTestHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
var body _type.Body
|
||
|
||
// 请求参数处理
|
||
body, err := requestUtil.RequestParams(r)
|
||
if err != nil {
|
||
log.Printf("请求参数处理失败: %v", err)
|
||
errMsg := fmt.Sprintf(`{"error": "请求参数处理失败: %v"}`, err)
|
||
http.Error(w, errMsg, http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
// 类目处理
|
||
goodsController := &controller.GoodsController{
|
||
ExcelPath: h.cfg.File.ExcelPath,
|
||
TxtPath: h.cfg.File.TxtPath,
|
||
SheetName: h.cfg.File.SheetName,
|
||
}
|
||
|
||
createResponse, err := goodsController.GoodsCreatController(
|
||
body,
|
||
h.cfg.BatchCreatRequest.Path,
|
||
h.cfg.App.Domain,
|
||
true,
|
||
)
|
||
|
||
if err != nil {
|
||
errorMsg := fmt.Sprintf(`{"error": "请求解析或验证失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusBadRequest)
|
||
return
|
||
}
|
||
|
||
// 返回Response结果
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(createResponse)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 获取商品详情处理器
|
||
func getGoodsDetailHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
var body _type.GetGoosDetail
|
||
productIdStr := r.FormValue("productId")
|
||
appIdStr := r.FormValue("appId")
|
||
body.AppSecret = r.FormValue("appSecret")
|
||
if i, err := strconv.ParseInt(productIdStr, 10, 64); err == nil {
|
||
body.ProductId = int64(i)
|
||
} else {
|
||
body.ProductId = 0
|
||
}
|
||
body.AppId, _ = strconv.Atoi(appIdStr)
|
||
|
||
response, err := controller.GetGoosDetail(body)
|
||
if err != nil {
|
||
log.Printf("获取商品详情失败: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "获取商品详情失败: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
w.WriteHeader(http.StatusOK)
|
||
if err = json.NewEncoder(w).Encode(json.RawMessage(response)); err != nil {
|
||
log.Printf("Failed to encode response: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "无法对响应进行编码: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 404 处理器
|
||
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
|
||
w.Header().Set("Content-Type", "application/json")
|
||
w.WriteHeader(http.StatusNotFound)
|
||
response := map[string]interface{}{
|
||
"error": "未找到路由",
|
||
"path": r.URL.Path,
|
||
"method": r.Method,
|
||
"code": 404,
|
||
"message": "请求的资源不存在",
|
||
}
|
||
|
||
if err := json.NewEncoder(w).Encode(response); err != nil {
|
||
log.Printf("错误的编码响应: %v", err)
|
||
errorMsg := fmt.Sprintf(`{"error": "错误的编码响应: %v"}`, err)
|
||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||
return
|
||
}
|
||
}
|
||
|
||
// 请求类目Id处理器
|
||
func (h *routeHandler) getCategoryListHandler(w http.ResponseWriter, r *http.Request) {
|
||
fmt.Println("开始请求商品分类列表")
|
||
|
||
// 创建或打开txt文件
|
||
file, err := os.OpenFile("category_list.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
|
||
if err != nil {
|
||
log.Printf("创建文件失败: %v", err)
|
||
return
|
||
}
|
||
defer file.Close()
|
||
|
||
// 请求商品分类列表
|
||
fmt.Println("正在获取商品分类列表...")
|
||
categoryPath := h.cfg.CategoryListRequest.Path
|
||
categoryRequest := _type.CategoryListRequest{
|
||
ItemBizType: h.cfg.CategoryListRequest.ItemBizType, // 普通商品类型
|
||
SpBizType: h.cfg.CategoryListRequest.SpBizType, // 潮品行业类型(可选)
|
||
}
|
||
|
||
categoryResponseBody, err := requestUtil.MakeAPIRequest(h.cfg.App.AppId, h.cfg.App.AppSecret, h.cfg.App.Domain, categoryPath, categoryRequest)
|
||
if err != nil {
|
||
log.Printf("获取商品分类列表失败: %v", err)
|
||
} else {
|
||
fmt.Println("商品分类列表响应:")
|
||
fmt.Println(string(categoryResponseBody))
|
||
|
||
// 解析响应以获取分类信息
|
||
var categoryResponse _type.CategoryListResponse
|
||
if err := json.Unmarshal(categoryResponseBody, &categoryResponse); err != nil {
|
||
log.Printf("解析分类响应失败: %v", err)
|
||
} else if categoryResponse.Code == 0 {
|
||
fmt.Println("成功获取商品分类:")
|
||
|
||
// 遍历分类列表,同时打印和写入文件
|
||
for _, item := range categoryResponse.Data.List {
|
||
// 控制台打印
|
||
fmt.Printf("行业: %s, 类目: %s (ID: %s)\n",
|
||
item.SpBizName, item.ChannelCatName, item.ChannelCatId)
|
||
|
||
// 写入文件
|
||
line := fmt.Sprintf("行业: %s, 类目: %s (ID: %s)\n",
|
||
item.SpBizName, item.ChannelCatName, item.ChannelCatId)
|
||
if _, err := file.WriteString(line); err != nil {
|
||
log.Printf("写入文件失败: %v", err)
|
||
}
|
||
}
|
||
|
||
fmt.Printf("共获取 %d 个分类,已保存到 category_list.txt\n", len(categoryResponse.Data.List))
|
||
} else {
|
||
errorMsg := fmt.Sprintf("获取分类失败: %s (代码: %d)\n", categoryResponse.Msg, categoryResponse.Code)
|
||
fmt.Printf(errorMsg)
|
||
file.WriteString(errorMsg)
|
||
}
|
||
}
|
||
|
||
if err != nil {
|
||
log.Printf("获取商品分类列表失败: %v", err)
|
||
}
|
||
|
||
fmt.Println("商品分类列表获取结束")
|
||
}
|
||
|
||
// 将统计结果添加到响应中的辅助函数
|
||
func addOuterIDStatsToResponse(originalResponse []byte) ([]byte, error) {
|
||
var responseMap map[string]interface{}
|
||
if err := json.Unmarshal(originalResponse, &responseMap); err != nil {
|
||
return nil, fmt.Errorf("解析响应JSON失败: %v", err)
|
||
}
|
||
|
||
// 获取统计结果
|
||
stats := getOuterIDStats(originalResponse)
|
||
|
||
// 将统计结果添加到response的data中
|
||
if data, exists := responseMap["data"].(map[string]interface{}); exists {
|
||
data["outer_id_stats"] = stats
|
||
responseMap["data"] = data
|
||
}
|
||
|
||
return json.Marshal(responseMap)
|
||
}
|
||
|
||
// 获取outer_id统计
|
||
func getOuterIDStats(responseData []byte) map[string]interface{} {
|
||
stats := make(map[string]interface{})
|
||
|
||
var data struct {
|
||
Data struct {
|
||
List []struct {
|
||
OuterID string `json:"outer_id"`
|
||
} `json:"list"`
|
||
} `json:"data"`
|
||
}
|
||
|
||
if err := json.Unmarshal(responseData, &data); err != nil {
|
||
return stats
|
||
}
|
||
|
||
outerIDCount := make(map[string]int)
|
||
totalCount := len(data.Data.List)
|
||
|
||
for _, item := range data.Data.List {
|
||
outerID := item.OuterID
|
||
if outerID == "" {
|
||
outerID = "(空)"
|
||
}
|
||
outerIDCount[outerID]++
|
||
}
|
||
|
||
stats["total_count"] = totalCount
|
||
stats["distribution"] = outerIDCount
|
||
|
||
return stats
|
||
}
|