Bläddra i källkod

交易市场平台(供应商)

wwh 1 vecka sedan
förälder
incheckning
501980b089

+ 3 - 1
baqing-admin/src/main/java/com/ruoyi/web/modules/technology/mapper/BizOnlineTrainingCourseMapper.java

@@ -1,6 +1,7 @@
1
 package com.ruoyi.web.modules.technology.mapper;
1
 package com.ruoyi.web.modules.technology.mapper;
2
 
2
 
3
 import java.util.List;
3
 import java.util.List;
4
+import com.ruoyi.web.modules.category.domain.vo.InformationCategoryItemVo;
4
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseQuery;
5
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseQuery;
5
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseRow;
6
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseRow;
6
 
7
 
@@ -13,5 +14,6 @@ public interface BizOnlineTrainingCourseMapper
13
 
14
 
14
     OnlineTrainingCourseRow selectOnlineTrainingCourseById(Long id);
15
     OnlineTrainingCourseRow selectOnlineTrainingCourseById(Long id);
15
 
16
 
16
-    List<String> selectDistinctCourseTopics();
17
+    /** 课程主题选项:资讯类别 parentCode=005、categoryType=8 的下级编码与名称 */
18
+    List<InformationCategoryItemVo> selectDistinctCourseTopics();
17
 }
19
 }

+ 2 - 1
baqing-admin/src/main/java/com/ruoyi/web/modules/technology/service/IBizOnlineTrainingCourseService.java

@@ -1,6 +1,7 @@
1
 package com.ruoyi.web.modules.technology.service;
1
 package com.ruoyi.web.modules.technology.service;
2
 
2
 
3
 import java.util.List;
3
 import java.util.List;
4
+import com.ruoyi.web.modules.category.domain.vo.InformationCategoryItemVo;
4
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseDetailVo;
5
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseDetailVo;
5
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseListVo;
6
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseListVo;
6
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseQuery;
7
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseQuery;
@@ -14,5 +15,5 @@ public interface IBizOnlineTrainingCourseService
14
 
15
 
15
     OnlineTrainingCourseDetailVo selectOnlineTrainingCourseById(Long id);
16
     OnlineTrainingCourseDetailVo selectOnlineTrainingCourseById(Long id);
16
 
17
 
17
-    List<String> selectCourseTopics();
18
+    List<InformationCategoryItemVo> selectCourseTopics();
18
 }
19
 }

+ 9 - 26
baqing-admin/src/main/java/com/ruoyi/web/modules/technology/service/impl/BizOnlineTrainingCourseServiceImpl.java

@@ -2,9 +2,7 @@ package com.ruoyi.web.modules.technology.service.impl;
2
 
2
 
3
 import java.util.ArrayList;
3
 import java.util.ArrayList;
4
 import java.util.Collections;
4
 import java.util.Collections;
5
-import java.util.LinkedHashSet;
6
 import java.util.List;
5
 import java.util.List;
7
-import java.util.Set;
8
 import org.springframework.beans.factory.annotation.Autowired;
6
 import org.springframework.beans.factory.annotation.Autowired;
9
 import org.springframework.stereotype.Service;
7
 import org.springframework.stereotype.Service;
10
 import com.ruoyi.common.exception.ServiceException;
8
 import com.ruoyi.common.exception.ServiceException;
@@ -14,8 +12,6 @@ import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseListVo;
14
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseQuery;
12
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseQuery;
15
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseRow;
13
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseRow;
16
 import com.ruoyi.web.modules.category.domain.vo.InformationCategoryItemVo;
14
 import com.ruoyi.web.modules.category.domain.vo.InformationCategoryItemVo;
17
-import com.ruoyi.web.modules.category.service.IBizInformationCategoryService;
18
-import com.ruoyi.web.modules.farming.support.TechResourceCourseTopicValidator;
19
 import com.ruoyi.web.modules.technology.mapper.BizOnlineTrainingCourseMapper;
15
 import com.ruoyi.web.modules.technology.mapper.BizOnlineTrainingCourseMapper;
20
 import com.ruoyi.web.modules.technology.service.IBizOnlineTrainingCourseService;
16
 import com.ruoyi.web.modules.technology.service.IBizOnlineTrainingCourseService;
21
 import com.ruoyi.web.modules.technology.support.OnlineTrainingCourseRules;
17
 import com.ruoyi.web.modules.technology.support.OnlineTrainingCourseRules;
@@ -26,9 +22,6 @@ public class BizOnlineTrainingCourseServiceImpl implements IBizOnlineTrainingCou
26
     @Autowired
22
     @Autowired
27
     private BizOnlineTrainingCourseMapper onlineTrainingCourseMapper;
23
     private BizOnlineTrainingCourseMapper onlineTrainingCourseMapper;
28
 
24
 
29
-    @Autowired
30
-    private IBizInformationCategoryService bizInformationCategoryService;
31
-
32
     @Override
25
     @Override
33
     public List<OnlineTrainingCourseListVo> selectOnlineTrainingCourseList(OnlineTrainingCourseQuery query)
26
     public List<OnlineTrainingCourseListVo> selectOnlineTrainingCourseList(OnlineTrainingCourseQuery query)
34
     {
27
     {
@@ -62,32 +55,22 @@ public class BizOnlineTrainingCourseServiceImpl implements IBizOnlineTrainingCou
62
     }
55
     }
63
 
56
 
64
     @Override
57
     @Override
65
-    public List<String> selectCourseTopics()
58
+    public List<InformationCategoryItemVo> selectCourseTopics()
66
     {
59
     {
67
-        Set<String> merged = new LinkedHashSet<>();
68
-        for (InformationCategoryItemVo item : bizInformationCategoryService.selectChildren(
69
-                TechResourceCourseTopicValidator.CATEGORY_PARENT_CODE,
70
-                TechResourceCourseTopicValidator.CATEGORY_TYPE))
60
+        List<InformationCategoryItemVo> categories = onlineTrainingCourseMapper.selectDistinctCourseTopics();
61
+        if (categories == null || categories.isEmpty())
71
         {
62
         {
72
-            if (item != null && StringUtils.isNotEmpty(item.getCode()))
73
-            {
74
-                merged.add(item.getCode().trim());
75
-            }
63
+            return Collections.emptyList();
76
         }
64
         }
77
-        List<String> fromDb = onlineTrainingCourseMapper.selectDistinctCourseTopics();
78
-        if (fromDb != null)
65
+        List<InformationCategoryItemVo> result = new ArrayList<>(categories.size());
66
+        for (InformationCategoryItemVo item : categories)
79
         {
67
         {
80
-            for (String topic : fromDb)
68
+            if (item != null && StringUtils.isNotEmpty(item.getCode()))
81
             {
69
             {
82
-                if (StringUtils.isNotEmpty(topic))
83
-                {
84
-                    merged.add(topic.trim());
85
-                }
70
+                result.add(item);
86
             }
71
             }
87
         }
72
         }
88
-        List<String> sorted = new ArrayList<>(merged);
89
-        Collections.sort(sorted);
90
-        return sorted;
73
+        return result;
91
     }
74
     }
92
 
75
 
93
     private static OnlineTrainingCourseQuery normalizeQuery(OnlineTrainingCourseQuery query)
76
     private static OnlineTrainingCourseQuery normalizeQuery(OnlineTrainingCourseQuery query)

+ 14 - 7
baqing-admin/src/main/resources/mapper/technology/BizOnlineTrainingCourseMapper.xml

@@ -47,12 +47,19 @@
47
           and <include refid="publishedVideoWhere"/>
47
           and <include refid="publishedVideoWhere"/>
48
     </select>
48
     </select>
49
 
49
 
50
-    <select id="selectDistinctCourseTopics" resultType="string">
51
-        select distinct course_topic
52
-        from biz_tech_resource
53
-        where <include refid="publishedVideoWhere"/>
54
-          and course_topic is not null
55
-          and course_topic != ''
56
-        order by course_topic asc
50
+    <resultMap type="com.ruoyi.web.modules.category.domain.vo.InformationCategoryItemVo" id="CourseTopicCategoryResult">
51
+        <result property="code" column="category_code"/>
52
+        <result property="name" column="category_name"/>
53
+    </resultMap>
54
+
55
+    <select id="selectDistinctCourseTopics" resultMap="CourseTopicCategoryResult">
56
+        select category_code, category_name
57
+        from biz_information_category
58
+        where del_flag = '0'
59
+          and parent_code = '005'
60
+          and category_type = '8'
61
+          and category_code is not null
62
+          and category_code != ''
63
+        order by category_code asc
57
     </select>
64
     </select>
58
 </mapper>
65
 </mapper>

+ 6 - 2
baqing-admin/src/test/java/com/ruoyi/web/modules/technology/controller/BizOnlineTrainingCourseControllerApiTest.java

@@ -31,6 +31,7 @@ import com.ruoyi.common.core.domain.entity.SysUser;
31
 import com.ruoyi.common.core.domain.model.LoginUser;
31
 import com.ruoyi.common.core.domain.model.LoginUser;
32
 import com.ruoyi.common.exception.ServiceException;
32
 import com.ruoyi.common.exception.ServiceException;
33
 import com.ruoyi.framework.web.exception.GlobalExceptionHandler;
33
 import com.ruoyi.framework.web.exception.GlobalExceptionHandler;
34
+import com.ruoyi.web.modules.category.domain.vo.InformationCategoryItemVo;
34
 import com.ruoyi.web.modules.technology.OnlineTrainingCourseTestSamples;
35
 import com.ruoyi.web.modules.technology.OnlineTrainingCourseTestSamples;
35
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseDetailVo;
36
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseDetailVo;
36
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseListVo;
37
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseListVo;
@@ -103,11 +104,14 @@ class BizOnlineTrainingCourseControllerApiTest
103
     void courseTopics() throws Exception
104
     void courseTopics() throws Exception
104
     {
105
     {
105
         when(onlineTrainingCourseService.selectCourseTopics())
106
         when(onlineTrainingCourseService.selectCourseTopics())
106
-                .thenReturn(Arrays.asList("005001", "005002"));
107
+                .thenReturn(Arrays.asList(
108
+                        new InformationCategoryItemVo("005001", "基础养殖"),
109
+                        new InformationCategoryItemVo("005002", "疫病防治")));
107
         mockMvc.perform(get("/techService/onlineTrainingCourse/courseTopics"))
110
         mockMvc.perform(get("/techService/onlineTrainingCourse/courseTopics"))
108
                 .andExpect(status().isOk())
111
                 .andExpect(status().isOk())
109
                 .andExpect(jsonPath("$.code").value(200))
112
                 .andExpect(jsonPath("$.code").value(200))
110
-                .andExpect(jsonPath("$.data[0]").value("005001"));
113
+                .andExpect(jsonPath("$.data[0].code").value("005001"))
114
+                .andExpect(jsonPath("$.data[0].name").value("基础养殖"));
111
     }
115
     }
112
 
116
 
113
     @Test
117
     @Test

+ 5 - 1
baqing-admin/src/test/java/com/ruoyi/web/modules/technology/mapper/BizOnlineTrainingCourseMapperXmlTest.java

@@ -47,7 +47,11 @@ class BizOnlineTrainingCourseMapperXmlTest
47
     {
47
     {
48
         String xml = readMapperXml();
48
         String xml = readMapperXml();
49
         assertTrue(xml.contains("selectDistinctCourseTopics"));
49
         assertTrue(xml.contains("selectDistinctCourseTopics"));
50
-        assertTrue(xml.contains("distinct course_topic"));
50
+        assertTrue(xml.contains("biz_information_category"));
51
+        assertTrue(xml.contains("parent_code = '005'"));
52
+        assertTrue(xml.contains("category_type = '8'"));
53
+        assertTrue(xml.contains("category_code"));
54
+        assertTrue(xml.contains("category_name"));
51
     }
55
     }
52
 
56
 
53
     private static String readMapperXml()
57
     private static String readMapperXml()

+ 9 - 11
baqing-admin/src/test/java/com/ruoyi/web/modules/technology/service/impl/BizOnlineTrainingCourseServiceImplTest.java

@@ -7,6 +7,7 @@ import static org.mockito.ArgumentMatchers.any;
7
 import static org.mockito.Mockito.when;
7
 import static org.mockito.Mockito.when;
8
 import java.util.Arrays;
8
 import java.util.Arrays;
9
 import java.util.Collections;
9
 import java.util.Collections;
10
+import java.util.List;
10
 import org.junit.jupiter.api.DisplayName;
11
 import org.junit.jupiter.api.DisplayName;
11
 import org.junit.jupiter.api.Test;
12
 import org.junit.jupiter.api.Test;
12
 import org.junit.jupiter.api.extension.ExtendWith;
13
 import org.junit.jupiter.api.extension.ExtendWith;
@@ -15,7 +16,6 @@ import org.mockito.Mock;
15
 import org.mockito.junit.jupiter.MockitoExtension;
16
 import org.mockito.junit.jupiter.MockitoExtension;
16
 import com.ruoyi.common.exception.ServiceException;
17
 import com.ruoyi.common.exception.ServiceException;
17
 import com.ruoyi.web.modules.category.domain.vo.InformationCategoryItemVo;
18
 import com.ruoyi.web.modules.category.domain.vo.InformationCategoryItemVo;
18
-import com.ruoyi.web.modules.category.service.IBizInformationCategoryService;
19
 import com.ruoyi.web.modules.farming.support.TechResourceRules;
19
 import com.ruoyi.web.modules.farming.support.TechResourceRules;
20
 import com.ruoyi.web.modules.technology.OnlineTrainingCourseTestSamples;
20
 import com.ruoyi.web.modules.technology.OnlineTrainingCourseTestSamples;
21
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseDetailVo;
21
 import com.ruoyi.web.modules.technology.domain.OnlineTrainingCourseDetailVo;
@@ -31,9 +31,6 @@ class BizOnlineTrainingCourseServiceImplTest
31
     @Mock
31
     @Mock
32
     private BizOnlineTrainingCourseMapper onlineTrainingCourseMapper;
32
     private BizOnlineTrainingCourseMapper onlineTrainingCourseMapper;
33
 
33
 
34
-    @Mock
35
-    private IBizInformationCategoryService bizInformationCategoryService;
36
-
37
     @InjectMocks
34
     @InjectMocks
38
     private BizOnlineTrainingCourseServiceImpl service;
35
     private BizOnlineTrainingCourseServiceImpl service;
39
 
36
 
@@ -73,18 +70,19 @@ class BizOnlineTrainingCourseServiceImplTest
73
     }
70
     }
74
 
71
 
75
     @Test
72
     @Test
76
-    @DisplayName("课程主题合并去重")
73
+    @DisplayName("课程主题列表")
77
     void courseTopics()
74
     void courseTopics()
78
     {
75
     {
79
-        when(bizInformationCategoryService.selectChildren("005", "8"))
76
+        when(onlineTrainingCourseMapper.selectDistinctCourseTopics())
80
                 .thenReturn(Arrays.asList(
77
                 .thenReturn(Arrays.asList(
81
                         new InformationCategoryItemVo(TechResourceRules.COURSE_TOPIC_BASIC, "基础养殖"),
78
                         new InformationCategoryItemVo(TechResourceRules.COURSE_TOPIC_BASIC, "基础养殖"),
82
                         new InformationCategoryItemVo(TechResourceRules.COURSE_TOPIC_DISEASE, "疫病防治")));
79
                         new InformationCategoryItemVo(TechResourceRules.COURSE_TOPIC_DISEASE, "疫病防治")));
83
-        when(onlineTrainingCourseMapper.selectDistinctCourseTopics())
84
-                .thenReturn(Arrays.asList(TechResourceRules.COURSE_TOPIC_BASIC, "004099"));
85
-        assertTrue(service.selectCourseTopics().contains(TechResourceRules.COURSE_TOPIC_BASIC));
86
-        assertTrue(service.selectCourseTopics().contains(TechResourceRules.COURSE_TOPIC_DISEASE));
87
-        assertTrue(service.selectCourseTopics().contains("004099"));
80
+        List<InformationCategoryItemVo> topics = service.selectCourseTopics();
81
+        assertEquals(2, topics.size());
82
+        assertEquals(TechResourceRules.COURSE_TOPIC_BASIC, topics.get(0).getCode());
83
+        assertEquals("基础养殖", topics.get(0).getName());
84
+        assertEquals(TechResourceRules.COURSE_TOPIC_DISEASE, topics.get(1).getCode());
85
+        assertEquals("疫病防治", topics.get(1).getName());
88
     }
86
     }
89
 
87
 
90
     @Test
88
     @Test

+ 7 - 2
doc/科技平台与服务/网上培训课程/网上培训课程技术方案.md

@@ -132,9 +132,14 @@ AND del_flag = '0'
132
 
132
 
133
 ### 3.3 课程主题下拉
133
 ### 3.3 课程主题下拉
134
 
134
 
135
-**响应 `data`**:`string[]`,升序去重
135
+**响应 `data`**:`InformationCategoryItemVo[]`,按 `category_code` 升序
136
 
136
 
137
-**SQL 逻辑**:在 **§2.1** 条件下 `SELECT DISTINCT course_topic`,过滤空串;实现类可合并预设四主题 + 库内 DISTINCT,去重排序后返回(预设在前或全量排序,产品无严格要求时按字母序即可)。
137
+| 字段 | 说明 |
138
+| --- | --- |
139
+| `code` | 资讯类别编码(如 `005001`) |
140
+| `name` | 类别名称(如 `基础养殖`) |
141
+
142
+**SQL 逻辑**:`selectDistinctCourseTopics` 从 `biz_information_category` 查询 `parent_code='005'`、`category_type='8'` 的下级,返回 `category_code`、`category_name`。
138
 
143
 
139
 ### 3.4 Mapper 约定
144
 ### 3.4 Mapper 约定
140
 
145
 

+ 2 - 2
doc/科技平台与服务/网上培训课程/网上培训课程测试用例.md

@@ -31,7 +31,7 @@
31
 | ZCZX-WSYPX-UT-004 | Service | 详情 Vo 映射 | 单元测试 | JUnit5+Mockito | §3.2 | Mock 返回含封面、视频 URL | `selectById(KC1.id)` | 含 `videoFileUrl`、`coverFileUrl`、`createTime` |
31
 | ZCZX-WSYPX-UT-004 | Service | 详情 Vo 映射 | 单元测试 | JUnit5+Mockito | §3.2 | Mock 返回含封面、视频 URL | `selectById(KC1.id)` | 含 `videoFileUrl`、`coverFileUrl`、`createTime` |
32
 | ZCZX-WSYPX-UT-005 | Service | 详情不存在 | 单元测试 | JUnit5+Mockito | §3.2、§8 | Mapper 返回 null | `selectById(99999)` | `ServiceException`,msg 含「下线或不存在」 |
32
 | ZCZX-WSYPX-UT-005 | Service | 详情不存在 | 单元测试 | JUnit5+Mockito | §3.2、§8 | Mapper 返回 null | `selectById(99999)` | `ServiceException`,msg 含「下线或不存在」 |
33
 | ZCZX-WSYPX-UT-006 | Service | 详情已下架 | 单元测试 | JUnit5+Mockito | §7 | Mock 对 KC-O 条件查询无行 | `selectById(KC-O.id)` | 同上异常 |
33
 | ZCZX-WSYPX-UT-006 | Service | 详情已下架 | 单元测试 | JUnit5+Mockito | §7 | Mock 对 KC-O 条件查询无行 | `selectById(KC-O.id)` | 同上异常 |
34
-| ZCZX-WSYPX-UT-007 | Service | 课程主题列表 | 单元测试 | JUnit5+Mockito | §3.3 | Mock 类别 children + DISTINCT | `selectCourseTopics` | 去重;含 `005001` 等编码及库内 DISTINCT |
34
+| ZCZX-WSYPX-UT-007 | Service | 课程主题列表 | 单元测试 | JUnit5+Mockito | §3.3 | Mock 类别 rows | `selectCourseTopics` | 返回 `{code,name}` 列表 |
35
 | ZCZX-WSYPX-UT-008 | Service | 列表不含未发布 | 单元测试 | JUnit5+Mockito | §2.2 | Mock 仅应带 published 条件 | 调用 `selectList` | Mapper 入参或 XML 固定 `publish_status=1` |
35
 | ZCZX-WSYPX-UT-008 | Service | 列表不含未发布 | 单元测试 | JUnit5+Mockito | §2.2 | Mock 仅应带 published 条件 | 调用 `selectList` | Mapper 入参或 XML 固定 `publish_status=1` |
36
 | ZCZX-WSYPX-UT-009 | Mapper | 列表 WHERE 固定条件 | 单元测试 | JUnit5 | §3.4 | 读 XML | 检查 `selectList` | `resource_type='004008'`;`publish_status=1`;`del_flag='0'` |
36
 | ZCZX-WSYPX-UT-009 | Mapper | 列表 WHERE 固定条件 | 单元测试 | JUnit5 | §3.4 | 读 XML | 检查 `selectList` | `resource_type='004008'`;`publish_status=1`;`del_flag='0'` |
37
 | ZCZX-WSYPX-UT-010 | Mapper | 标题模糊与主题精确 | 单元测试 | JUnit5 | §3.1 | 读 XML | 检查动态 SQL | `resource_name like`;`course_topic =` |
37
 | ZCZX-WSYPX-UT-010 | Mapper | 标题模糊与主题精确 | 单元测试 | JUnit5 | §3.1 | 读 XML | 检查动态 SQL | `resource_name like`;`course_topic =` |
@@ -64,7 +64,7 @@
64
 | ZCZX-WSYPX-API-017 | 详情 | 已删除 id | 接口测试 | Postman | §5.2.3 | 有 KC-X | `GET /{KC-X.id}` | 同上 |
64
 | ZCZX-WSYPX-API-017 | 详情 | 已删除 id | 接口测试 | Postman | §5.2.3 | 有 KC-X | `GET /{KC-X.id}` | 同上 |
65
 | ZCZX-WSYPX-API-018 | 详情 | 不存在 id | 接口测试 | Postman | 异常 | 已登录 | `GET /999999999` | 失败;可读 `msg` |
65
 | ZCZX-WSYPX-API-018 | 详情 | 不存在 id | 接口测试 | Postman | 异常 | 已登录 | `GET /999999999` | 失败;可读 `msg` |
66
 | ZCZX-WSYPX-API-019 | 详情 | 非视频已发布 id | 接口测试 | Postman | §7 | 有 Z2 | `GET /{Z2.id}` | 失败(无纳入行) |
66
 | ZCZX-WSYPX-API-019 | 详情 | 非视频已发布 id | 接口测试 | Postman | §7 | 有 Z2 | `GET /{Z2.id}` | 失败(无纳入行) |
67
-| ZCZX-WSYPX-API-020 | 主题下拉 | courseTopics | 接口测试 | Postman | §3.3 | 有 KC1/KC2 | `GET .../courseTopics` | `data` 为编码数组;含 `005001`、`005002`;无空串 |
67
+| ZCZX-WSYPX-API-020 | 主题下拉 | courseTopics | 接口测试 | Postman | §3.3 | 有类别数据 | `GET .../courseTopics` | `data` 为 `{code,name}` 数组;含 `005001`/`基础养殖` 等 |
68
 | ZCZX-WSYPX-API-021 | 交叉 | 源库发布后可见 | 接口测试 | Postman | §10 | KC-D 未发布 | `techResource` 发布 KC-D→本模块 `list` | 列表出现;`GET detail` 成功 |
68
 | ZCZX-WSYPX-API-021 | 交叉 | 源库发布后可见 | 接口测试 | Postman | §10 | KC-D 未发布 | `techResource` 发布 KC-D→本模块 `list` | 列表出现;`GET detail` 成功 |
69
 | ZCZX-WSYPX-API-022 | 交叉 | 源库下架后不可见 | 接口测试 | Postman | §10 | KC1 已发布 | `techResource` 下架 KC1→本模块 `list`+`detail` | 列表无 KC1;详情失败 |
69
 | ZCZX-WSYPX-API-022 | 交叉 | 源库下架后不可见 | 接口测试 | Postman | §10 | KC1 已发布 | `techResource` 下架 KC1→本模块 `list`+`detail` | 列表无 KC1;详情失败 |
70
 | ZCZX-WSYPX-API-023 | 写接口 | 无 POST 新增 | 接口测试 | Postman | §3、§3.2 | 已登录 | `POST .../onlineTrainingCourse` | 404 或 405;或权限拒绝 |
70
 | ZCZX-WSYPX-API-023 | 写接口 | 无 POST 新增 | 接口测试 | Postman | §3、§3.2 | 已登录 | `POST .../onlineTrainingCourse` | 404 或 405;或权限拒绝 |