Преглед на файлове

平台商品类别管理

wwh преди 2 седмици
родител
ревизия
291da29b3e

+ 3 - 0
baqing-shop/src/main/java/com/ruoyi/web/modules/category/constant/CategoryConstants.java

@@ -28,4 +28,7 @@ public final class CategoryConstants
28 28
     public static final String HOT_NO = "0";
29 29
 
30 30
     public static final String HEADER_SHOP_ID = "X-Shop-Id";
31
+
32
+    /** 平台分类展示名(shop_id 为 NULL) */
33
+    public static final String PLATFORM_SCOPE_NAME = "平台分类";
31 34
 }

+ 100 - 0
baqing-shop/src/main/java/com/ruoyi/web/modules/category/controller/PlatformCategoryController.java

@@ -0,0 +1,100 @@
1
+package com.ruoyi.web.modules.category.controller;
2
+
3
+import java.util.HashMap;
4
+import java.util.Map;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.security.access.prepost.PreAuthorize;
7
+import org.springframework.validation.annotation.Validated;
8
+import org.springframework.web.bind.annotation.DeleteMapping;
9
+import org.springframework.web.bind.annotation.GetMapping;
10
+import org.springframework.web.bind.annotation.PathVariable;
11
+import org.springframework.web.bind.annotation.PostMapping;
12
+import org.springframework.web.bind.annotation.PutMapping;
13
+import org.springframework.web.bind.annotation.RequestBody;
14
+import org.springframework.web.bind.annotation.RequestMapping;
15
+import org.springframework.web.bind.annotation.RequestParam;
16
+import org.springframework.web.bind.annotation.RestController;
17
+import com.ruoyi.common.annotation.Log;
18
+import com.ruoyi.common.core.controller.BaseController;
19
+import com.ruoyi.common.core.domain.AjaxResult;
20
+import com.ruoyi.common.enums.BusinessType;
21
+import com.ruoyi.web.modules.category.domain.BizGoodsCategory;
22
+import com.ruoyi.web.modules.category.exception.CategoryBatchDeleteException;
23
+import com.ruoyi.web.modules.category.service.IPlatformCategoryService;
24
+
25
+/**
26
+ * 平台端商品分类(shop_id 为 NULL,最多两级)
27
+ */
28
+@RestController
29
+@RequestMapping("/agri/category")
30
+public class PlatformCategoryController extends BaseController
31
+{
32
+    @Autowired
33
+    private IPlatformCategoryService platformCategoryService;
34
+
35
+    @PreAuthorize("@ss.hasPermi('agri:category:list')")
36
+    @GetMapping("/list")
37
+    public AjaxResult list(@RequestParam(value = "keyword", required = false) String keyword)
38
+    {
39
+        return success(platformCategoryService.selectPlatformCategoryList(keyword));
40
+    }
41
+
42
+    @PreAuthorize("@ss.hasPermi('agri:category:list')")
43
+    @GetMapping("/tree")
44
+    public AjaxResult tree(@RequestParam(value = "keyword", required = false) String keyword)
45
+    {
46
+        return success(platformCategoryService.selectPlatformCategoryTree(keyword));
47
+    }
48
+
49
+    @PreAuthorize("@ss.hasPermi('agri:category:list')")
50
+    @GetMapping("/level1Options")
51
+    public AjaxResult level1Options()
52
+    {
53
+        return success(platformCategoryService.selectPlatformLevel1Options());
54
+    }
55
+
56
+    @PreAuthorize("@ss.hasPermi('agri:category:list')")
57
+    @GetMapping("/level2Options")
58
+    public AjaxResult level2Options()
59
+    {
60
+        return success(platformCategoryService.selectPlatformLevel2Options());
61
+    }
62
+
63
+    @PreAuthorize("@ss.hasPermi('agri:category:add')")
64
+    @Log(title = "平台商品分类", businessType = BusinessType.INSERT)
65
+    @PostMapping
66
+    public AjaxResult add(@Validated @RequestBody BizGoodsCategory category)
67
+    {
68
+        category.setCreateBy(getUsername());
69
+        return toAjax(platformCategoryService.insertPlatformCategory(category));
70
+    }
71
+
72
+    @PreAuthorize("@ss.hasPermi('agri:category:edit')")
73
+    @Log(title = "平台商品分类", businessType = BusinessType.UPDATE)
74
+    @PutMapping
75
+    public AjaxResult edit(@Validated @RequestBody BizGoodsCategory category)
76
+    {
77
+        category.setUpdateBy(getUsername());
78
+        return toAjax(platformCategoryService.updatePlatformCategory(category));
79
+    }
80
+
81
+    @PreAuthorize("@ss.hasPermi('agri:category:remove')")
82
+    @Log(title = "平台商品分类", businessType = BusinessType.DELETE)
83
+    @DeleteMapping("/{categoryIds}")
84
+    public AjaxResult remove(@PathVariable("categoryIds") Long[] categoryIds)
85
+    {
86
+        try
87
+        {
88
+            platformCategoryService.deletePlatformCategoryByIds(categoryIds, getUsername());
89
+            return success();
90
+        }
91
+        catch (CategoryBatchDeleteException e)
92
+        {
93
+            Map<String, Object> data = new HashMap<>();
94
+            data.put("reasons", e.getReasons());
95
+            AjaxResult result = AjaxResult.error(e.getMessage());
96
+            result.put(AjaxResult.DATA_TAG, data);
97
+            return result;
98
+        }
99
+    }
100
+}

+ 27 - 0
baqing-shop/src/main/java/com/ruoyi/web/modules/category/service/IPlatformCategoryService.java

@@ -0,0 +1,27 @@
1
+package com.ruoyi.web.modules.category.service;
2
+
3
+import java.util.List;
4
+import com.ruoyi.web.modules.category.domain.BizGoodsCategory;
5
+import com.ruoyi.web.modules.category.vo.CategoryLevel2OptionVO;
6
+import com.ruoyi.web.modules.category.vo.CategoryListVO;
7
+import com.ruoyi.web.modules.category.vo.CategoryTreeResultVO;
8
+
9
+/**
10
+ * 平台端商品分类 Service(shop_id 为 NULL)
11
+ */
12
+public interface IPlatformCategoryService
13
+{
14
+    CategoryListVO selectPlatformCategoryList(String keyword);
15
+
16
+    CategoryTreeResultVO selectPlatformCategoryTree(String keyword);
17
+
18
+    List<BizGoodsCategory> selectPlatformLevel1Options();
19
+
20
+    List<CategoryLevel2OptionVO> selectPlatformLevel2Options();
21
+
22
+    int insertPlatformCategory(BizGoodsCategory category);
23
+
24
+    int updatePlatformCategory(BizGoodsCategory category);
25
+
26
+    void deletePlatformCategoryByIds(Long[] categoryIds, String operator);
27
+}

+ 131 - 41
baqing-shop/src/main/java/com/ruoyi/web/modules/category/service/impl/CategoryServiceImpl.java

@@ -18,6 +18,7 @@ import com.ruoyi.web.modules.category.facade.IGoodsFacade;
18 18
 import com.ruoyi.web.modules.category.facade.IShopFacade;
19 19
 import com.ruoyi.web.modules.category.mapper.BizGoodsCategoryMapper;
20 20
 import com.ruoyi.web.modules.category.service.ICategoryService;
21
+import com.ruoyi.web.modules.category.service.IPlatformCategoryService;
21 22
 import com.ruoyi.web.modules.category.support.SellerShopContext;
22 23
 import com.ruoyi.web.modules.category.vo.CategoryLevel2OptionVO;
23 24
 import com.ruoyi.web.modules.category.vo.CategoryListVO;
@@ -26,10 +27,10 @@ import com.ruoyi.web.modules.category.vo.CategoryTreeResultVO;
26 27
 import com.ruoyi.web.modules.category.vo.CategoryTreeVO;
27 28
 
28 29
 /**
29
- * 商品分类业务实现
30
+ * 商品分类业务实现(商家端 shop_id 非空;平台端 shop_id 为 NULL)
30 31
  */
31 32
 @Service
32
-public class CategoryServiceImpl implements ICategoryService
33
+public class CategoryServiceImpl implements ICategoryService, IPlatformCategoryService
33 34
 {
34 35
     @Autowired
35 36
     private BizGoodsCategoryMapper categoryMapper;
@@ -43,41 +44,125 @@ public class CategoryServiceImpl implements ICategoryService
43 44
     @Override
44 45
     public CategoryListVO selectCategoryList(String keyword)
45 46
     {
46
-        Long shopId = requireShopId();
47
-        List<BizGoodsCategory> list = queryList(shopId, keyword);
47
+        return doSelectCategoryList(requireShopId(), keyword);
48
+    }
49
+
50
+    @Override
51
+    public CategoryTreeResultVO selectCategoryTree(String keyword)
52
+    {
53
+        return doSelectCategoryTree(requireShopId(), keyword);
54
+    }
55
+
56
+    @Override
57
+    public List<BizGoodsCategory> selectLevel1Options()
58
+    {
59
+        return doSelectLevel1Options(requireShopId());
60
+    }
61
+
62
+    @Override
63
+    public List<CategoryLevel2OptionVO> selectLevel2Options()
64
+    {
65
+        return doSelectLevel2Options(requireShopId());
66
+    }
67
+
68
+    @Override
69
+    @Transactional(rollbackFor = Exception.class)
70
+    public int insertCategory(BizGoodsCategory category)
71
+    {
72
+        return doInsertCategory(requireShopId(), category);
73
+    }
74
+
75
+    @Override
76
+    @Transactional(rollbackFor = Exception.class)
77
+    public int updateCategory(BizGoodsCategory category)
78
+    {
79
+        return doUpdateCategory(requireShopId(), category);
80
+    }
81
+
82
+    @Override
83
+    @Transactional(rollbackFor = Exception.class)
84
+    public void deleteCategoryByIds(Long[] categoryIds, String operator)
85
+    {
86
+        doDeleteCategoryByIds(requireShopId(), categoryIds, operator);
87
+    }
88
+
89
+    @Override
90
+    public CategoryListVO selectPlatformCategoryList(String keyword)
91
+    {
92
+        return doSelectCategoryList(null, keyword);
93
+    }
94
+
95
+    @Override
96
+    public CategoryTreeResultVO selectPlatformCategoryTree(String keyword)
97
+    {
98
+        return doSelectCategoryTree(null, keyword);
99
+    }
100
+
101
+    @Override
102
+    public List<BizGoodsCategory> selectPlatformLevel1Options()
103
+    {
104
+        return doSelectLevel1Options(null);
105
+    }
106
+
107
+    @Override
108
+    public List<CategoryLevel2OptionVO> selectPlatformLevel2Options()
109
+    {
110
+        return doSelectLevel2Options(null);
111
+    }
112
+
113
+    @Override
114
+    @Transactional(rollbackFor = Exception.class)
115
+    public int insertPlatformCategory(BizGoodsCategory category)
116
+    {
117
+        return doInsertCategory(null, category);
118
+    }
119
+
120
+    @Override
121
+    @Transactional(rollbackFor = Exception.class)
122
+    public int updatePlatformCategory(BizGoodsCategory category)
123
+    {
124
+        return doUpdateCategory(null, category);
125
+    }
126
+
127
+    @Override
128
+    @Transactional(rollbackFor = Exception.class)
129
+    public void deletePlatformCategoryByIds(Long[] categoryIds, String operator)
130
+    {
131
+        doDeleteCategoryByIds(null, categoryIds, operator);
132
+    }
133
+
134
+    private CategoryListVO doSelectCategoryList(Long shopId, String keyword)
135
+    {
136
+        assertScopeMaintable(shopId);
48 137
         CategoryListVO vo = new CategoryListVO();
49 138
         vo.setShopId(shopId);
50
-        vo.setShopName(shopFacade.getShopName(shopId));
51
-        vo.setRows(buildFlatRows(list));
139
+        vo.setShopName(resolveScopeName(shopId));
140
+        vo.setRows(buildFlatRows(queryList(shopId, keyword)));
52 141
         return vo;
53 142
     }
54 143
 
55
-    @Override
56
-    public CategoryTreeResultVO selectCategoryTree(String keyword)
144
+    private CategoryTreeResultVO doSelectCategoryTree(Long shopId, String keyword)
57 145
     {
58
-        Long shopId = requireShopId();
59
-        List<BizGoodsCategory> list = queryList(shopId, keyword);
146
+        assertScopeMaintable(shopId);
60 147
         CategoryTreeResultVO result = new CategoryTreeResultVO();
61 148
         result.setShopId(shopId);
62
-        result.setShopName(shopFacade.getShopName(shopId));
63
-        result.setTree(buildTree(list));
149
+        result.setShopName(resolveScopeName(shopId));
150
+        result.setTree(buildTree(queryList(shopId, keyword)));
64 151
         return result;
65 152
     }
66 153
 
67
-    @Override
68
-    public List<BizGoodsCategory> selectLevel1Options()
154
+    private List<BizGoodsCategory> doSelectLevel1Options(Long shopId)
69 155
     {
70
-        Long shopId = requireShopId();
156
+        assertScopeMaintable(shopId);
71 157
         BizGoodsCategory query = new BizGoodsCategory();
72 158
         query.setShopId(shopId);
73 159
         query.setCategoryLevel(CategoryConstants.LEVEL_ONE);
74 160
         return categoryMapper.selectList(query);
75 161
     }
76 162
 
77
-    @Override
78
-    public List<CategoryLevel2OptionVO> selectLevel2Options()
163
+    private List<CategoryLevel2OptionVO> doSelectLevel2Options(Long shopId)
79 164
     {
80
-        Long shopId = requireShopId();
165
+        assertScopeMaintable(shopId);
81 166
         BizGoodsCategory query = new BizGoodsCategory();
82 167
         query.setShopId(shopId);
83 168
         List<BizGoodsCategory> all = categoryMapper.selectList(query);
@@ -98,14 +183,11 @@ public class CategoryServiceImpl implements ICategoryService
98 183
         return options;
99 184
     }
100 185
 
101
-    @Override
102
-    @Transactional(rollbackFor = Exception.class)
103
-    public int insertCategory(BizGoodsCategory category)
186
+    private int doInsertCategory(Long shopId, BizGoodsCategory category)
104 187
     {
105
-        Long shopId = requireShopId();
106
-        shopFacade.assertShopMaintable(shopId);
188
+        assertScopeMaintable(shopId);
107 189
         validateFields(category);
108
-        normalizeLevelOnSave(category, shopId, true);
190
+        normalizeLevelOnSave(category, shopId);
109 191
         if (categoryMapper.countByName(shopId, category.getParentId(), category.getCategoryName(), null) > 0)
110 192
         {
111 193
             throw new ServiceException("同级分类名称已存在");
@@ -115,12 +197,9 @@ public class CategoryServiceImpl implements ICategoryService
115 197
         return categoryMapper.insert(category);
116 198
     }
117 199
 
118
-    @Override
119
-    @Transactional(rollbackFor = Exception.class)
120
-    public int updateCategory(BizGoodsCategory category)
200
+    private int doUpdateCategory(Long shopId, BizGoodsCategory category)
121 201
     {
122
-        Long shopId = requireShopId();
123
-        shopFacade.assertShopMaintable(shopId);
202
+        assertScopeMaintable(shopId);
124 203
         if (category.getCategoryId() == null)
125 204
         {
126 205
             throw new ServiceException("分类ID不能为空");
@@ -150,12 +229,9 @@ public class CategoryServiceImpl implements ICategoryService
150 229
         return categoryMapper.update(category);
151 230
     }
152 231
 
153
-    @Override
154
-    @Transactional(rollbackFor = Exception.class)
155
-    public void deleteCategoryByIds(Long[] categoryIds, String operator)
232
+    private void doDeleteCategoryByIds(Long shopId, Long[] categoryIds, String operator)
156 233
     {
157
-        Long shopId = requireShopId();
158
-        shopFacade.assertShopMaintable(shopId);
234
+        assertScopeMaintable(shopId);
159 235
         if (categoryIds == null || categoryIds.length == 0)
160 236
         {
161 237
             throw new ServiceException("请选择要删除的分类");
@@ -178,8 +254,7 @@ public class CategoryServiceImpl implements ICategoryService
178 254
         {
179 255
             throw new CategoryBatchDeleteException("部分分类不满足删除条件,整批操作已取消", reasons);
180 256
         }
181
-        List<Long> idList = new ArrayList<>(targetIds);
182
-        categoryMapper.logicDeleteByIds(idList, shopId, operator);
257
+        categoryMapper.logicDeleteByIds(new ArrayList<>(targetIds), shopId, operator);
183 258
     }
184 259
 
185 260
     private Long requireShopId()
@@ -189,10 +264,26 @@ public class CategoryServiceImpl implements ICategoryService
189 264
         {
190 265
             throw new ServiceException("请先选择当前店铺");
191 266
         }
192
-        shopFacade.assertShopMaintable(shopId);
193 267
         return shopId;
194 268
     }
195 269
 
270
+    private void assertScopeMaintable(Long shopId)
271
+    {
272
+        if (shopId != null)
273
+        {
274
+            shopFacade.assertShopMaintable(shopId);
275
+        }
276
+    }
277
+
278
+    private String resolveScopeName(Long shopId)
279
+    {
280
+        if (shopId == null)
281
+        {
282
+            return CategoryConstants.PLATFORM_SCOPE_NAME;
283
+        }
284
+        return shopFacade.getShopName(shopId);
285
+    }
286
+
196 287
     private List<BizGoodsCategory> queryList(Long shopId, String keyword)
197 288
     {
198 289
         BizGoodsCategory query = new BizGoodsCategory();
@@ -281,7 +372,7 @@ public class CategoryServiceImpl implements ICategoryService
281 372
         {
282 373
             throw new ServiceException("请上传分类图片");
283 374
         }
284
-        if (category.getSortNo() == null || category.getSortNo() < 0)
375
+        if (category.getSortNo() < 0)
285 376
         {
286 377
             throw new ServiceException("排序须为非负整数");
287 378
         }
@@ -297,7 +388,7 @@ public class CategoryServiceImpl implements ICategoryService
297 388
         }
298 389
     }
299 390
 
300
-    private void normalizeLevelOnSave(BizGoodsCategory category, Long shopId, boolean insert)
391
+    private void normalizeLevelOnSave(BizGoodsCategory category, Long shopId)
301 392
     {
302 393
         if (CategoryConstants.LEVEL_ONE.equals(category.getCategoryLevel()))
303 394
         {
@@ -343,8 +434,7 @@ public class CategoryServiceImpl implements ICategoryService
343 434
             }
344 435
             if (CategoryConstants.LEVEL_ONE.equals(cat.getCategoryLevel()))
345 436
             {
346
-                List<Long> children = categoryMapper.selectChildIds(shopId, id);
347
-                result.addAll(children);
437
+                result.addAll(categoryMapper.selectChildIds(shopId, id));
348 438
                 result.add(id);
349 439
             }
350 440
             else

+ 35 - 6
baqing-shop/src/main/resources/mapper/category/BizGoodsCategoryMapper.xml

@@ -26,11 +26,35 @@
26 26
                p.category_name as parent_name
27 27
         from biz_goods_category c
28 28
         left join biz_goods_category p on c.parent_id = p.category_id and p.del_flag = '0'
29
+            and ((c.shop_id is null and p.shop_id is null) or (c.shop_id = p.shop_id))
30
+    </sql>
31
+
32
+    <sql id="shopIdCondition">
33
+        <choose>
34
+            <when test="shopId != null">
35
+                and shop_id = #{shopId}
36
+            </when>
37
+            <otherwise>
38
+                and shop_id is null
39
+            </otherwise>
40
+        </choose>
41
+    </sql>
42
+
43
+    <sql id="cShopIdCondition">
44
+        <choose>
45
+            <when test="shopId != null">
46
+                and c.shop_id = #{shopId}
47
+            </when>
48
+            <otherwise>
49
+                and c.shop_id is null
50
+            </otherwise>
51
+        </choose>
29 52
     </sql>
30 53
 
31 54
     <select id="selectById" resultMap="BizGoodsCategoryResult">
32 55
         <include refid="selectVo"/>
33
-        where c.category_id = #{categoryId} and c.shop_id = #{shopId} and c.del_flag = '0'
56
+        where c.category_id = #{categoryId} and c.del_flag = '0'
57
+        <include refid="cShopIdCondition"/>
34 58
     </select>
35 59
 
36 60
     <select id="selectByIdIgnoreShop" resultMap="BizGoodsCategoryResult">
@@ -42,7 +66,8 @@
42 66
 
43 67
     <select id="selectList" resultMap="BizGoodsCategoryResult">
44 68
         <include refid="selectVo"/>
45
-        where c.shop_id = #{shopId} and c.del_flag = '0'
69
+        where c.del_flag = '0'
70
+        <include refid="cShopIdCondition"/>
46 71
         <if test="keyword != null and keyword != ''">
47 72
             and c.category_name like concat('%', #{keyword}, '%')
48 73
         </if>
@@ -54,8 +79,9 @@
54 79
 
55 80
     <select id="countByName" resultType="int">
56 81
         select count(1) from biz_goods_category
57
-        where shop_id = #{shopId} and parent_id = #{parentId} and category_name = #{categoryName}
82
+        where parent_id = #{parentId} and category_name = #{categoryName}
58 83
           and del_flag = '0'
84
+        <include refid="shopIdCondition"/>
59 85
         <if test="excludeId != null">
60 86
             and category_id != #{excludeId}
61 87
         </if>
@@ -84,13 +110,15 @@
84 110
             update_by = #{updateBy},
85 111
             update_time = sysdate()
86 112
         </set>
87
-        where category_id = #{categoryId} and shop_id = #{shopId} and del_flag = '0'
113
+        where category_id = #{categoryId} and del_flag = '0'
114
+        <include refid="shopIdCondition"/>
88 115
     </update>
89 116
 
90 117
     <update id="logicDeleteByIds">
91 118
         update biz_goods_category
92 119
         set del_flag = '2', update_by = #{updateBy}, update_time = sysdate()
93
-        where shop_id = #{shopId} and del_flag = '0'
120
+        where del_flag = '0'
121
+        <include refid="shopIdCondition"/>
94 122
           and category_id in
95 123
         <foreach collection="ids" item="id" open="(" separator="," close=")">
96 124
             #{id}
@@ -99,7 +127,8 @@
99 127
 
100 128
     <select id="selectChildIds" resultType="long">
101 129
         select category_id from biz_goods_category
102
-        where shop_id = #{shopId} and parent_id = #{parentId} and del_flag = '0'
130
+        where parent_id = #{parentId} and del_flag = '0'
131
+        <include refid="shopIdCondition"/>
103 132
     </select>
104 133
 
105 134
 </mapper>

+ 105 - 0
baqing-shop/src/test/java/com/ruoyi/web/modules/category/controller/PlatformCategoryControllerTest.java

@@ -0,0 +1,105 @@
1
+package com.ruoyi.web.modules.category.controller;
2
+
3
+import static org.mockito.ArgumentMatchers.any;
4
+import static org.mockito.Mockito.doThrow;
5
+import static org.mockito.Mockito.lenient;
6
+import static org.mockito.Mockito.when;
7
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
8
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
9
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
10
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
11
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
12
+import java.util.Collections;
13
+import org.junit.jupiter.api.BeforeEach;
14
+import org.junit.jupiter.api.Test;
15
+import org.junit.jupiter.api.extension.ExtendWith;
16
+import org.mockito.InjectMocks;
17
+import org.mockito.Mock;
18
+import org.mockito.Spy;
19
+import org.mockito.junit.jupiter.MockitoExtension;
20
+import org.springframework.http.MediaType;
21
+import org.springframework.test.web.servlet.MockMvc;
22
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
23
+import com.fasterxml.jackson.databind.ObjectMapper;
24
+import com.ruoyi.web.modules.category.constant.CategoryConstants;
25
+import com.ruoyi.web.modules.category.domain.BizGoodsCategory;
26
+import com.ruoyi.web.modules.category.exception.CategoryBatchDeleteException;
27
+import com.ruoyi.web.modules.category.service.IPlatformCategoryService;
28
+import com.ruoyi.web.modules.category.vo.CategoryTreeResultVO;
29
+import com.ruoyi.web.modules.category.vo.CategoryTreeVO;
30
+
31
+/**
32
+ * 平台端分类接口测试
33
+ */
34
+@ExtendWith(MockitoExtension.class)
35
+class PlatformCategoryControllerTest
36
+{
37
+    @Mock
38
+    private IPlatformCategoryService platformCategoryService;
39
+
40
+    @Spy
41
+    @InjectMocks
42
+    private PlatformCategoryController controller;
43
+
44
+    private MockMvc mockMvc;
45
+
46
+    private final ObjectMapper objectMapper = new ObjectMapper();
47
+
48
+    @BeforeEach
49
+    void setUp() throws Exception
50
+    {
51
+        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
52
+        lenient().doReturn("admin").when(controller).getUsername();
53
+    }
54
+
55
+    @Test
56
+    void tree_returns200() throws Exception
57
+    {
58
+        CategoryTreeResultVO vo = new CategoryTreeResultVO();
59
+        vo.setShopName(CategoryConstants.PLATFORM_SCOPE_NAME);
60
+        CategoryTreeVO node = new CategoryTreeVO();
61
+        node.setCategoryId(1L);
62
+        node.setCategoryName("肥料");
63
+        vo.setTree(Collections.singletonList(node));
64
+        when(platformCategoryService.selectPlatformCategoryTree(null)).thenReturn(vo);
65
+
66
+        mockMvc.perform(get("/agri/category/tree"))
67
+                .andExpect(status().isOk())
68
+                .andExpect(jsonPath("$.code").value(200))
69
+                .andExpect(jsonPath("$.data.shopName").value(CategoryConstants.PLATFORM_SCOPE_NAME))
70
+                .andExpect(jsonPath("$.data.tree[0].categoryName").value("肥料"));
71
+    }
72
+
73
+    @Test
74
+    void add_returns200() throws Exception
75
+    {
76
+        when(platformCategoryService.insertPlatformCategory(any())).thenReturn(1);
77
+
78
+        BizGoodsCategory body = new BizGoodsCategory();
79
+        body.setCategoryLevel("1");
80
+        body.setCategoryName("肥料");
81
+        body.setCategoryPic("/profile/upload/c.jpg");
82
+        body.setSortNo(0);
83
+        body.setShowFlag("1");
84
+        body.setHotFlag("0");
85
+
86
+        mockMvc.perform(post("/agri/category")
87
+                .contentType(MediaType.APPLICATION_JSON)
88
+                .content(objectMapper.writeValueAsString(body)))
89
+                .andExpect(status().isOk())
90
+                .andExpect(jsonPath("$.code").value(200));
91
+    }
92
+
93
+    @Test
94
+    void remove_batchFail_returnsReasons() throws Exception
95
+    {
96
+        doThrow(new CategoryBatchDeleteException("部分分类不满足删除条件,整批操作已取消",
97
+                Collections.singletonList("分类「复合肥」下存在商品,请先处理商品")))
98
+                .when(platformCategoryService).deletePlatformCategoryByIds(any(), any());
99
+
100
+        mockMvc.perform(delete("/agri/category/10"))
101
+                .andExpect(status().isOk())
102
+                .andExpect(jsonPath("$.code").value(500))
103
+                .andExpect(jsonPath("$.data.reasons[0]").exists());
104
+    }
105
+}

+ 17 - 2
baqing-shop/src/test/java/com/ruoyi/web/modules/category/service/CategoryServiceImplTest.java

@@ -5,9 +5,10 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
5 5
 import static org.mockito.ArgumentMatchers.any;
6 6
 import static org.mockito.ArgumentMatchers.eq;
7 7
 import static org.mockito.Mockito.doNothing;
8
+import static org.mockito.Mockito.lenient;
8 9
 import static org.mockito.Mockito.never;
9
-import static org.mockito.Mockito.verify;
10 10
 import static org.mockito.Mockito.when;
11
+import static org.mockito.Mockito.verify;
11 12
 import static org.mockito.ArgumentMatchers.anyLong;
12 13
 import java.util.Arrays;
13 14
 import java.util.Collections;
@@ -52,7 +53,7 @@ class CategoryServiceImplTest
52 53
     void setUp()
53 54
     {
54 55
         SellerShopContext.setShopId(SHOP_ID);
55
-        doNothing().when(shopFacade).assertShopMaintable(anyLong());
56
+        lenient().doNothing().when(shopFacade).assertShopMaintable(anyLong());
56 57
     }
57 58
 
58 59
     @AfterEach
@@ -167,6 +168,20 @@ class CategoryServiceImplTest
167 168
                 () -> categoryService.deleteCategoryByIds(new Long[] { 1L }, "tester"));
168 169
     }
169 170
 
171
+    @Test
172
+    void insertPlatformLevel1_shopIdNull()
173
+    {
174
+        BizGoodsCategory dto = baseDto();
175
+        dto.setCategoryLevel(CategoryConstants.LEVEL_ONE);
176
+        when(categoryMapper.countByName(eq(null), eq(CategoryConstants.PARENT_ROOT), eq("肥料"), eq(null))).thenReturn(0);
177
+        when(categoryMapper.insert(any())).thenReturn(1);
178
+
179
+        int rows = categoryService.insertPlatformCategory(dto);
180
+
181
+        assertEquals(1, rows);
182
+        verify(categoryMapper).insert(any());
183
+    }
184
+
170 185
     private BizGoodsCategory baseDto()
171 186
     {
172 187
         BizGoodsCategory c = new BizGoodsCategory();

+ 1 - 1
doc/农资商城web/商品管理/商品分类/商品分类功能需求-草稿.md

@@ -7,6 +7,6 @@
7 7
 3. 编辑商品分类:编辑一级和二级分类
8 8
 4. 批量/删除商品分类:支持批量删除和单独删除一级和二级分类,删除一级则也删除全部二级
9 9
 5. 添加子分类:一级分类下可添加子分类
10
-6. 店铺管理员可以维护自己的商品分
10
+6. 平台端、商家端的商品分类最多支持两级分
11 11
 7. 异常返回:统一格式 code、msg、data。
12 12
 8. 技术栈:RuoYi v3.9.2-springboot2 分支 + MySQL-5.7.39。

+ 3 - 1
sql/biz_goods_category.sql

@@ -1,7 +1,8 @@
1
+-- 商品分类表(平台 + 店铺维度,最多两级)
2
+-- shop_id 为 NULL:平台分类;非 NULL:对应店铺分类
1 3
 CREATE TABLE IF NOT EXISTS `biz_goods_category` (
2 4
   `category_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '分类ID',
3
-  `shop_id` bigint(20) NOT NULL COMMENT '店铺ID',
5
+  `shop_id` bigint(20) DEFAULT NULL COMMENT '店铺ID;NULL=平台分类',
4 6
   `parent_id` bigint(20) NOT NULL DEFAULT '0' COMMENT '0为一级',
5 7
   `category_level` char(1) NOT NULL COMMENT '1一级 2二级',
6 8
   `category_name` varchar(64) NOT NULL COMMENT '分类名称',