Browse Source

feat: 1.新增进货商品管理-供货商品分页查询接口

zhangwl 1 month ago
parent
commit
dce20959d0

+ 30 - 0
src/main/java/zs/payment/controller/YunMallGoodsController.java

@@ -7,11 +7,14 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.bind.annotation.RestController;
 import zs.payment.req.yunmall.goods.GoodsSaveReq;
 import zs.payment.req.yunmall.goods.GoodsSaveReq;
 import zs.payment.service.yunmall.goods.GoodsService;
 import zs.payment.service.yunmall.goods.GoodsService;
 
 
 import javax.validation.Valid;
 import javax.validation.Valid;
+import java.util.Map;
 
 
 @Slf4j
 @Slf4j
 @RestController
 @RestController
@@ -31,4 +34,31 @@ public class YunMallGoodsController {
         return goodsService.saveGoods(req.getGdId(),req.getOp());
         return goodsService.saveGoods(req.getGdId(),req.getOp());
     }
     }
 
 
+    /**
+     * 供货商品分页查询(对应 ptmall qiyuesearchgoods)
+     * 按一级/二级分类、品类、商品名称分页查询云商城生态商品。
+     * 示例:/yunmall/goods/search?psize=10&pno=1&orderBy=1&shopId=13537&keyWord=整套&pts=[1000]&categorys=[1200]
+     *
+     * @param shopId    商家编号(排除自家店铺)
+     * @param keyWord   商品名称关键字
+     * @param categorys 一级/二级分类ID的JSON数组字符串
+     * @param pts       品类ID的JSON数组字符串(存在时直接覆盖分类解析结果)
+     * @param psize     每页条数,默认10
+     * @param pno       当前页,默认1
+     * @param orderBy   排序规则,默认1
+     */
+    @RequestMapping(value = "/search", method = {RequestMethod.GET, RequestMethod.POST})
+    public Map<String, Object> search(
+            @RequestParam(required = false) Long shopId,
+            @RequestParam(required = false) String keyWord,
+            @RequestParam(required = false) String categorys,
+            @RequestParam(required = false) String pts,
+            @RequestParam(required = false, defaultValue = "10") Integer psize,
+            @RequestParam(required = false, defaultValue = "1") Integer pno,
+            @RequestParam(required = false, defaultValue = "1") Integer orderBy) {
+        log.info("---/yunmall/goods/search shopId={},keyWord={},categorys={},pts={},psize={},pno={},orderBy={}---",
+                shopId, keyWord, categorys, pts, psize, pno, orderBy);
+        return goodsService.searchGoods(shopId, keyWord, categorys, pts, psize, pno, orderBy);
+    }
+
 }
 }

+ 82 - 0
src/main/java/zs/payment/dao/yunmall/GoodsDao.java

@@ -1,15 +1,27 @@
 package zs.payment.dao.yunmall;
 package zs.payment.dao.yunmall;
 
 
+import org.bson.Document;
+import org.springframework.data.domain.Sort;
 import org.springframework.data.mongodb.core.MongoTemplate;
 import org.springframework.data.mongodb.core.MongoTemplate;
+import org.springframework.data.mongodb.core.query.Criteria;
+import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.stereotype.Repository;
 import org.springframework.stereotype.Repository;
 import zs.payment.dao.MongoDBDao;
 import zs.payment.dao.MongoDBDao;
 import zs.payment.entity.mongodb.yunmall.Goods;
 import zs.payment.entity.mongodb.yunmall.Goods;
 
 
 import javax.annotation.Resource;
 import javax.annotation.Resource;
+import java.util.List;
+import java.util.regex.Pattern;
 
 
 @Repository
 @Repository
 public class GoodsDao extends MongoDBDao<Goods> {
 public class GoodsDao extends MongoDBDao<Goods> {
 
 
+    /** 生态商品集合名(对应 Goods 上的 @Document(collection = "goods")) */
+    private static final String COLLECTION = "goods";
+
+    /** 自主结算型店铺(对应 ptmall ShopTypeEnum.SHOP_TYPE_FROM_SELF_MALL),供货搜索需排除 */
+    private static final int SHOP_TYPE_FROM_SELF_MALL = 4;
+
     @Resource(name = "yunmallTemplate")
     @Resource(name = "yunmallTemplate")
     private MongoTemplate mongoTemplate;
     private MongoTemplate mongoTemplate;
 
 
@@ -22,4 +34,74 @@ public class GoodsDao extends MongoDBDao<Goods> {
     protected Class<Goods> getEntityClass() {
     protected Class<Goods> getEntityClass() {
         return Goods.class;
         return Goods.class;
     }
     }
+
+    /**
+     * 供货商品搜索计数(对应 ptmall GoodsMongoDao.qiyueGoodsSearchCount)
+     */
+    public long searchCount(String keyWord, List<Long> ptIds, Long shopId) {
+        return mongoTemplate.count(buildSearchQuery(keyWord, ptIds, shopId), COLLECTION);
+    }
+
+    /**
+     * 供货商品搜索(对应 ptmall GoodsMongoDao.qiyueGoodsSearch)
+     * 返回原始 Document,保留文档中已存的全部字段以便按既定结构透传。
+     */
+    public List<Document> searchRaw(String keyWord, List<Long> ptIds, Long shopId,
+                                    Integer psize, Integer pno, Integer orderBy) {
+        Query query = buildSearchQuery(keyWord, ptIds, shopId);
+        query.with(buildSort(orderBy));
+        int size = (psize == null || psize <= 0) ? 10 : psize;
+        int page = (pno == null || pno <= 0) ? 1 : pno;
+        query.skip((long) (page - 1) * size).limit(size);
+        return mongoTemplate.find(query, Document.class, COLLECTION);
+    }
+
+    /**
+     * 构造查询条件(对齐 qiyueGoodsSearch 的 QueryBuilder):
+     * gd_name 模糊、pt_id in、排除自家店、排除自主结算店、skus.sku_id>0
+     */
+    private Query buildSearchQuery(String keyWord, List<Long> ptIds, Long shopId) {
+        Query query = new Query();
+        if (keyWord != null && !keyWord.isEmpty()) {
+            String kw = keyWord.replace("*", "\\*").replace("(", "\\(")
+                    .replace(")", "\\)").replace("+", "\\+");
+            query.addCriteria(Criteria.where("gd_name")
+                    .regex(Pattern.compile(".*" + kw + ".*", Pattern.CASE_INSENSITIVE)));
+        }
+        if (ptIds != null && !ptIds.isEmpty()) {
+            query.addCriteria(Criteria.where("pt_id").in(ptIds));
+        }
+        if (shopId != null) {
+            query.addCriteria(Criteria.where("shop.shop_id").ne(shopId));
+        }
+        query.addCriteria(Criteria.where("shop.shop_type").ne(SHOP_TYPE_FROM_SELF_MALL));
+        query.addCriteria(Criteria.where("skus.sku_id").gt(0));
+        return query;
+    }
+
+    /**
+     * 排序映射(严格对齐 ptmall OrderBy 枚举,未知值回退默认 CREATE_DESC):
+     * 1 create_time desc(默认)、2 gd_price asc、3 gd_price desc、
+     * 4 sales_volume desc、5 comment_num desc、6 gd_id desc、7 gd_id asc
+     */
+    private Sort buildSort(Integer orderBy) {
+        int ob = orderBy == null ? 1 : orderBy;
+        switch (ob) {
+            case 2:
+                return Sort.by(Sort.Direction.ASC, "gd_price");
+            case 3:
+                return Sort.by(Sort.Direction.DESC, "gd_price");
+            case 4:
+                return Sort.by(Sort.Direction.DESC, "sales_volume");
+            case 5:
+                return Sort.by(Sort.Direction.DESC, "comment_num");
+            case 6:
+                return Sort.by(Sort.Direction.DESC, "gd_id");
+            case 7:
+                return Sort.by(Sort.Direction.ASC, "gd_id");
+            case 1:
+            default:
+                return Sort.by(Sort.Direction.DESC, "create_time");
+        }
+    }
 }
 }

+ 22 - 0
src/main/java/zs/payment/mapper/flymall/B2cGoodsSpuInOnlineMapper.java

@@ -7,6 +7,7 @@ import org.apache.ibatis.annotations.Select;
 import zs.payment.entity.mysql.flymall.B2cGoodsSpuInOnline;
 import zs.payment.entity.mysql.flymall.B2cGoodsSpuInOnline;
 
 
 import java.math.BigDecimal;
 import java.math.BigDecimal;
+import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
 @Mapper
 @Mapper
@@ -49,4 +50,25 @@ public interface B2cGoodsSpuInOnlineMapper extends BaseMapper<B2cGoodsSpuInOnlin
      */
      */
     @Select("SELECT COUNT(*) FROM b2c_goods_spu_in_online WHERE goodsid = #{goodsid} AND shop_id = #{shopId} AND is_del = 0")
     @Select("SELECT COUNT(*) FROM b2c_goods_spu_in_online WHERE goodsid = #{goodsid} AND shop_id = #{shopId} AND is_del = 0")
     int countSuppliedByGoodsid(@Param("goodsid") Integer goodsid, @Param("shopId") Integer shopId);
     int countSuppliedByGoodsid(@Param("goodsid") Integer goodsid, @Param("shopId") Integer shopId);
+
+    /**
+     * 根据cate_id列表查询分类(对应ptmall IProductCategoryDao.getCategoryByIds)
+     * cateIds 为逗号拼接的数值ID串
+     */
+    @Select("SELECT cate_id, level FROM b2c_product_category WHERE cate_id IN (${cateIds}) AND state = 0")
+    List<Map<String, Object>> findCategoriesByCateIds(@Param("cateIds") String cateIds);
+
+    /**
+     * 根据parent_id列表查询子分类(对应ptmall IProductCategoryDao.getCategorys)
+     * parentIds 为逗号拼接的数值ID串
+     */
+    @Select("SELECT cate_id, level FROM b2c_product_category WHERE parent_id IN (${parentIds}) AND state = 0")
+    List<Map<String, Object>> findCategoriesByParentIds(@Param("parentIds") String parentIds);
+
+    /**
+     * 根据cate_id列表查询品类 pt_id(对应ptmall IProductTypeDao.getCategoryType)
+     * cateIds 为逗号拼接的数值ID串
+     */
+    @Select("SELECT pt_id FROM b2c_product_type WHERE cate_id IN (${cateIds}) AND status = 0")
+    List<Long> findPtIdsByCateIds(@Param("cateIds") String cateIds);
 }
 }

+ 18 - 0
src/main/java/zs/payment/service/yunmall/goods/GoodsService.java

@@ -1,5 +1,7 @@
 package zs.payment.service.yunmall.goods;
 package zs.payment.service.yunmall.goods;
 
 
+import java.util.Map;
+
 public interface GoodsService {
 public interface GoodsService {
     /**
     /**
      * 根据gdId查询
      * 根据gdId查询
@@ -8,4 +10,20 @@ public interface GoodsService {
      * @return
      * @return
      */
      */
     Boolean saveGoods(String gdId,Integer op);
     Boolean saveGoods(String gdId,Integer op);
+
+    /**
+     * 供货商品分页查询(对应 ptmall qiyuesearchgoods)
+     * 按一级/二级分类、品类、商品名称分页查询云商城生态商品。
+     *
+     * @param shopId    商家编号(排除自家店铺)
+     * @param keyWord   商品名称关键字
+     * @param categorys 一级/二级分类ID的JSON数组字符串,如 [1200]
+     * @param pts       品类ID的JSON数组字符串,如 [1000](存在时直接覆盖分类解析结果)
+     * @param psize     每页条数,默认10
+     * @param pno       当前页,默认1
+     * @param orderBy   排序规则,默认1
+     * @return {head:{status}, body:[{totalcount, goodsList}]}
+     */
+    Map<String, Object> searchGoods(Long shopId, String keyWord, String categorys, String pts,
+                                    Integer psize, Integer pno, Integer orderBy);
 }
 }

+ 217 - 0
src/main/java/zs/payment/service/yunmall/goods/impl/GoodsServiceImpl.java

@@ -1,11 +1,14 @@
 package zs.payment.service.yunmall.goods.impl;
 package zs.payment.service.yunmall.goods.impl;
 
 
+import com.alibaba.fastjson.JSON;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.bson.Document;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.Criteria;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.data.mongodb.core.query.Query;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 import zs.payment.dao.yunmall.GoodsDao;
 import zs.payment.dao.yunmall.GoodsDao;
+import zs.payment.mapper.flymall.B2cGoodsSpuInOnlineMapper;
 import zs.payment.entity.mongodb.yunmall.Goods;
 import zs.payment.entity.mongodb.yunmall.Goods;
 import zs.payment.entity.mongodb.yunmall.Shop;
 import zs.payment.entity.mongodb.yunmall.Shop;
 import zs.payment.entity.mongodb.yunmall.Sku;
 import zs.payment.entity.mongodb.yunmall.Sku;
@@ -22,7 +25,10 @@ import zs.payment.service.yunmall.goods.GoodsService;
 import zs.payment.utils.TimeUtils;
 import zs.payment.utils.TimeUtils;
 
 
 import java.math.BigDecimal;
 import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Optional;
 import java.util.stream.Collectors;
 import java.util.stream.Collectors;
 
 
@@ -44,6 +50,9 @@ public class GoodsServiceImpl implements GoodsService {
     @Autowired
     @Autowired
     private GoodsDao goodsDao;
     private GoodsDao goodsDao;
 
 
+    @Autowired
+    private B2cGoodsSpuInOnlineMapper b2cGoodsSpuInOnlineMapper;
+
 
 
     @Override
     @Override
     public Boolean saveGoods(String gdId, Integer op) {
     public Boolean saveGoods(String gdId, Integer op) {
@@ -110,4 +119,212 @@ public class GoodsServiceImpl implements GoodsService {
         }
         }
         return false;
         return false;
     }
     }
+
+    @Override
+    public Map<String, Object> searchGoods(Long shopId, String keyWord, String categorys, String pts,
+                                           Integer psize, Integer pno, Integer orderBy) {
+        // 1. 三个查询条件全空 → 参数缺失(对齐 qiyuesearchgoods)
+        if (StringUtils.isBlank(keyWord) && StringUtils.isBlank(categorys) && StringUtils.isBlank(pts)) {
+            return errorResult("参数缺失");
+        }
+
+        List<Long> ptIds = new ArrayList<>();
+
+        // 2. 一级/二级分类 → 向下钻取至 level>=3 叶子分类 → 品类 pt_id
+        if (StringUtils.isNotBlank(categorys)) {
+            List<Long> categoryIds = parseLongArray(categorys);
+            if (!categoryIds.isEmpty()) {
+                List<Map<String, Object>> categoryList =
+                        b2cGoodsSpuInOnlineMapper.findCategoriesByCateIds(joinIds(categoryIds));
+                while (categoryList != null && !categoryList.isEmpty()
+                        && toInteger(categoryList.get(0).get("level")) < 3) {
+                    categoryIds = collectCateIds(categoryList);
+                    categoryList = b2cGoodsSpuInOnlineMapper.findCategoriesByParentIds(joinIds(categoryIds));
+                    if (categoryList != null && !categoryList.isEmpty()) {
+                        categoryIds = collectCateIds(categoryList);
+                    }
+                }
+                if (!categoryIds.isEmpty()) {
+                    List<Long> resolved = b2cGoodsSpuInOnlineMapper.findPtIdsByCateIds(joinIds(categoryIds));
+                    if (resolved != null) {
+                        ptIds = resolved;
+                    }
+                }
+            }
+        }
+
+        // 3. 传了 pts 时直接覆盖分类解析结果
+        if (StringUtils.isNotBlank(pts)) {
+            ptIds = parseLongArray(pts);
+        }
+
+        // 4. keyWord 为空且未解析出品类 → 查询参数缺失
+        if (StringUtils.isBlank(keyWord) && ptIds.isEmpty()) {
+            return errorResult("查询参数缺失");
+        }
+
+        // 5. 查询 + 逐条补全字段
+        long totalcount = goodsDao.searchCount(keyWord, ptIds, shopId);
+        List<Document> goodsList = goodsDao.searchRaw(keyWord, ptIds, shopId, psize, pno, orderBy);
+        for (Document doc : goodsList) {
+            enrichGoods(doc);
+        }
+
+        Map<String, Object> bodyItem = new LinkedHashMap<>();
+        bodyItem.put("totalcount", totalcount);
+        bodyItem.put("goodsList", goodsList);
+        List<Map<String, Object>> body = new ArrayList<>();
+        body.add(bodyItem);
+
+        Map<String, Object> head = new LinkedHashMap<>();
+        head.put("status", 200);
+        Map<String, Object> result = new LinkedHashMap<>();
+        result.put("head", head);
+        result.put("body", body);
+        return result;
+    }
+
+    /**
+     * 逐条商品字段补全(对齐 qiyueGoodsSearch):
+     * 去 _id、补 sku.gd_stock/attrs、算 totalstock/min_price,缺失时补 cost_price/statement_rate。
+     */
+    private void enrichGoods(Document doc) {
+        doc.remove("_id");
+        long totalstock = 0;
+        BigDecimal minPrice = BigDecimal.valueOf(-1);
+        boolean minPriceSet = false;
+        List<BigDecimal> costPrices = new ArrayList<>();
+
+        Object skusObj = doc.get("skus");
+        if (skusObj instanceof List) {
+            for (Object o : (List<?>) skusObj) {
+                if (!(o instanceof Document)) {
+                    continue;
+                }
+                Document sku = (Document) o;
+                int gdStock = 0;
+                Object skuIdObj = sku.get("sku_id");
+                if (skuIdObj != null) {
+                    B2cGoodsSkuInOnline dbSku = b2cGoodsSkuInOnlineService.getById(toInteger(skuIdObj));
+                    if (dbSku != null) {
+                        gdStock = dbSku.getGdStock() == null ? 0 : dbSku.getGdStock();
+                        if (dbSku.getGdPrice() != null && (!minPriceSet || dbSku.getGdPrice().compareTo(minPrice) < 0)) {
+                            minPrice = dbSku.getGdPrice();
+                            minPriceSet = true;
+                        }
+                        if (dbSku.getCostPrice() != null && !costPrices.contains(dbSku.getCostPrice())) {
+                            costPrices.add(dbSku.getCostPrice());
+                        }
+                    }
+                }
+                sku.put("gd_stock", gdStock);
+                if (!(sku.get("attrs") instanceof List)) {
+                    sku.put("attrs", new ArrayList<>());
+                }
+                totalstock += gdStock;
+            }
+        }
+
+        doc.put("totalstock", totalstock);
+        doc.put("min_price", minPriceSet ? minPrice : BigDecimal.valueOf(-1));
+        if (!(doc.get("cost_price") instanceof List)) {
+            doc.put("cost_price", costPrices);
+        }
+        if (doc.get("statement_rate") == null || doc.get("tg_statement_rate") == null) {
+            Object ptObj = doc.get("pt_id");
+            if (ptObj != null) {
+                Map<String, Object> commission = b2cGoodsSpuInOnlineMapper.findCommissionByPtId(toInteger(ptObj));
+                if (commission != null) {
+                    if (doc.get("statement_rate") == null) {
+                        doc.put("statement_rate", commission.get("ledger_ratio"));
+                    }
+                    if (doc.get("tg_statement_rate") == null) {
+                        doc.put("tg_statement_rate", commission.get("tg_ledger_ratio"));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 解析 ID 列表参数,兼容三种形式:
+     * 1) JSON 数组:[1200000] 或 [1200000,1200001](对齐 groovy jsonSlurper.parseText)
+     * 2) 逗号分隔:1200000,1200001
+     * 3) 单个数值:1200000
+     * 解析失败返回空列表。
+     */
+    private List<Long> parseLongArray(String value) {
+        if (StringUtils.isBlank(value)) {
+            return new ArrayList<>();
+        }
+        String trimmed = value.trim();
+        // JSON 数组形式
+        if (trimmed.startsWith("[")) {
+            try {
+                List<Long> list = JSON.parseArray(trimmed, Long.class);
+                return list == null ? new ArrayList<>() : list;
+            } catch (Exception e) {
+                return new ArrayList<>();
+            }
+        }
+        // 逗号分隔 / 单个数值形式
+        List<Long> list = new ArrayList<>();
+        for (String part : trimmed.split(",")) {
+            String p = part.trim();
+            if (p.isEmpty()) {
+                continue;
+            }
+            try {
+                list.add(Long.parseLong(p));
+            } catch (NumberFormatException ignore) {
+                // 跳过非法项
+            }
+        }
+        return list;
+    }
+
+    /** 从分类查询结果中收集 cate_id 列表 */
+    private List<Long> collectCateIds(List<Map<String, Object>> categoryList) {
+        List<Long> ids = new ArrayList<>();
+        for (Map<String, Object> row : categoryList) {
+            Object cateId = row.get("cate_id");
+            if (cateId != null) {
+                ids.add(((Number) cateId).longValue());
+            }
+        }
+        return ids;
+    }
+
+    /** 数值ID列表拼接为逗号串,用于 IN (${...}) */
+    private String joinIds(List<Long> ids) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < ids.size(); i++) {
+            if (i > 0) {
+                sb.append(",");
+            }
+            sb.append(ids.get(i));
+        }
+        return sb.toString();
+    }
+
+    /** 宽松转 Integer */
+    private Integer toInteger(Object value) {
+        if (value == null) {
+            return 0;
+        }
+        if (value instanceof Number) {
+            return ((Number) value).intValue();
+        }
+        return Integer.parseInt(value.toString().trim());
+    }
+
+    /** 错误响应:{head:{status:500,msg}} */
+    private Map<String, Object> errorResult(String msg) {
+        Map<String, Object> head = new LinkedHashMap<>();
+        head.put("status", 500);
+        head.put("msg", msg);
+        Map<String, Object> result = new LinkedHashMap<>();
+        result.put("head", head);
+        return result;
+    }
 }
 }

+ 34 - 0
src/main/java/zs/payment/utils/MallRedisUtils.java

@@ -0,0 +1,34 @@
+package zs.payment.utils;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Repository;
+
+/**
+ * 云商城 Redis 工具类
+ * 连接云商城专用 Redis(r-2zel7t7rnqxzhuk6yj.redis.rds.aliyuncs.com:6379 db3)
+ * 用于 SKU 库存缓存等业务
+ */
+@Repository
+@Slf4j
+public class MallRedisUtils {
+    @Value("${mall.redis.master}")
+    private String mallRedisHost;
+    @Value("${mall.redis.db:3}")
+    private int mallRedisDb;
+
+    public void set(String key, String value) {
+        Redis.withClusterDb(mallRedisHost, mallRedisDb, jedis -> {
+            jedis.set(key, value);
+            return null;
+        });
+    }
+
+    public String get(String key) {
+        return Redis.withClusterDb(mallRedisHost, mallRedisDb, jedis -> jedis.get(key));
+    }
+
+    public Long del(String key) {
+        return Redis.withClusterDb(mallRedisHost, mallRedisDb, jedis -> jedis.del(key));
+    }
+}

+ 4 - 0
src/main/resources/application.yml

@@ -4,6 +4,10 @@ server:
     port: 18816 #msPayment
     port: 18816 #msPayment
     servlet:
     servlet:
         context-path: /pay2
         context-path: /pay2
+    tomcat:
+        # 允许查询串中出现原始的 [ ] { } | 等字符(RFC 3986 默认拒绝),兼容 pts/categorys=[1200000] 形式
+        relaxed-query-chars: "[,],{,},|"
+        relaxed-path-chars: "[,],{,},|"
 
 
 # Spring Profile 配置
 # Spring Profile 配置
 spring:
 spring: