|
@@ -0,0 +1,357 @@
|
|
|
|
|
+package com.etotem.cfc.unit;
|
|
|
|
|
+
|
|
|
|
|
+import com.etotem.cfc.controller.DailyFeedbackController;
|
|
|
|
|
+import org.junit.jupiter.api.DisplayName;
|
|
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
|
|
+
|
|
|
|
|
+import java.io.InputStream;
|
|
|
|
|
+import java.lang.reflect.Field;
|
|
|
|
|
+import java.lang.reflect.Method;
|
|
|
|
|
+import java.lang.reflect.Parameter;
|
|
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Scanner;
|
|
|
|
|
+
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.fail;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 契约级单测:孩子/家庭成员提交意见反馈(能力键 {@code child.feedback.submit})。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>契约来源(requirements + design):</p>
|
|
|
|
|
+ * <ul>
|
|
|
|
|
+ * <li>需求:{@code tests/user-scenarios/应用场景总览.md} SC-C-23「意见反馈(孩子端)」——
|
|
|
|
|
+ * <b>主流程</b>:孩子(不能登录用户,memberType=local)提交反馈(内容/类型)→ 进入客服队列;
|
|
|
|
|
+ * <b>异常/边界</b>:内容适龄过滤;敏感信息脱敏;附件大小限制。</li>
|
|
|
|
|
+ * <li>设计:{@code docs/superpowers/api/API_REFERENCE.md} §4.61.7 缺口 G-06——
|
|
|
|
|
+ * 「无通用意见反馈端点/表/管理端;现有反馈均为专用方向」,修复建议:
|
|
|
|
|
+ * <b>新增 {@code user_feedback} 表 + {@code POST /api/user/feedback/submit}
|
|
|
|
|
+ * (避开 {@code /api/feedback} 前缀冲突)+ 管理端受理</b>。
|
|
|
|
|
+ * 现有 {@link DailyFeedbackController}({@code /api/feedback/*},家长→孩子)、
|
|
|
|
|
+ * {@code ActivityFeedbackController}({@code /api/activity/feedback/*},活动评价)、
|
|
|
|
|
+ * {@code HealthReportController.submitFeedback}({@code /api/health/feedback/submit},报告维度)
|
|
|
|
|
+ * 均不是「孩子→客服队列」的通用意见反馈通道。</li>
|
|
|
|
|
+ * </ul>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>实现现状(本分支 src/main 核实)</b>:契约面全部缺失——
|
|
|
|
|
+ * <ul>
|
|
|
|
|
+ * <li>无 {@code user_feedback} 表(schema.sql 0 命中);</li>
|
|
|
|
|
+ * <li>无 {@code UserFeedback} 实体 / {@code UserFeedbackService} / {@code UserFeedbackController}
|
|
|
|
|
+ * (src/main 0 命中);</li>
|
|
|
|
|
+ * <li>无针对意见反馈的内容适龄过滤 / 敏感信息脱敏 / 附件大小限制。
|
|
|
|
|
+ * (现库内「敏感信息」文案均为文章/图片/语音等审核位点,非反馈通道。)</li>
|
|
|
|
|
+ * </ul>
|
|
|
|
|
+ * 孩子(不能登录用户)无 userId,反馈须以家庭成员 {@code memberId} 维度承载(同 SC-C-15/C-19 口径)。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>测试策略</b>:纯反射结构契约 + schema.sql 文本探测(无 Spring / 无 DB / 无外部服务),
|
|
|
|
|
+ * 能编译、能运行、断言失败(红),失败信息可读可贴。GREEN 阶段实现后本测试自动转绿。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>RED 预期</b>:下列断言在实现落地前应全部失败(红);其中「正常流-1」为既有
|
|
|
|
|
+ * {@code /api/feedback/today} 绿锚点——证明 DailyFeedbackController 方向(家长→孩子)
|
|
|
|
|
+ * 与契约要求(孩子提交→客服队列)相反,不作为本能力载体。</p>
|
|
|
|
|
+ */
|
|
|
|
|
+class ChildFeedbackSubmitContractTest {
|
|
|
|
|
+
|
|
|
|
|
+ /** 契约建议的表(§4.61.7 G-06):通用意见反馈表 */
|
|
|
|
|
+ private static final String FEEDBACK_TABLE = "user_feedback";
|
|
|
|
|
+
|
|
|
|
|
+ /** 契约建议的端点全路径(避开 /api/feedback 前缀冲突) */
|
|
|
|
|
+ private static final String SUBMIT_PATH = "/api/user/feedback/submit";
|
|
|
|
|
+
|
|
|
|
|
+ /** 孩子(不能登录用户)的家庭成员标识 */
|
|
|
|
|
+ private static final Long MEMBER_ID = 88L;
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 正常流:通用意见反馈通道(POST /api/user/feedback/submit)三件套
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-1(绿锚点):DailyFeedbackController 为家长→孩子方向,非孩子提交通道")
|
|
|
|
|
+ void dailyFeedback_isParentToChildDirection() {
|
|
|
|
|
+ Class<?> clazz = DailyFeedbackController.class;
|
|
|
|
|
+ String classPath = resolveRequestMapping(clazz);
|
|
|
|
|
+ boolean hasToday = false;
|
|
|
|
|
+ for (Method m : clazz.getDeclaredMethods()) {
|
|
|
|
|
+ Map<String, String> mapping = resolveMappingPath(m);
|
|
|
|
|
+ if ("/today".equals(mapping.get("path")) && "POST".equals(mapping.get("httpMethod"))) {
|
|
|
|
|
+ hasToday = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertNotNull(classPath, "[child.feedback.submit] DailyFeedbackController 应保留 /api/feedback 类映射。");
|
|
|
|
|
+ assertTrue(("/api/feedback".equals(classPath) && hasToday),
|
|
|
|
|
+ "[child.feedback.submit] DailyFeedbackController 应为 /api/feedback/today(家长→孩子即时反馈),"
|
|
|
|
|
+ + "与人「孩子→客服队列」的意见反馈方向相反,不能作为 child.feedback.submit 载体。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-2:UserFeedback 实体应存在(涉及 user_feedback 表)")
|
|
|
|
|
+ void entity_userFeedback_exists() {
|
|
|
|
|
+ Class<?> clazz = loadClass("com.etotem.cfc.entity.UserFeedback");
|
|
|
|
|
+ assertNotNull(clazz,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedback 实体不存在:契约要求通用意见反馈落库 user_feedback"
|
|
|
|
|
+ + "(§4.61.7 G-06;SC-C-23 主流程)。实现未落地。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-3:UserFeedback 实体应承载内容/类型/队列状态")
|
|
|
|
|
+ void entity_userFeedback_carriesSumbitFields() {
|
|
|
|
|
+ Class<?> clazz = loadClass("com.etotem.cfc.entity.UserFeedback");
|
|
|
|
|
+ if (clazz == null) {
|
|
|
|
|
+ fail("[child.feedback.submit] UserFeedback 实体不存在,无法验证提交字段(SC-C-23 主流程)。");
|
|
|
|
|
+ }
|
|
|
|
|
+ for (String required : new String[]{"content", "type", "status", "attachmentUrl"}) {
|
|
|
|
|
+ boolean has = false;
|
|
|
|
|
+ for (Field f : clazz.getDeclaredFields()) {
|
|
|
|
|
+ if (f.getName().equals(required)) {
|
|
|
|
|
+ has = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertTrue(has,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedback 应含字段 " + required + "(内容/类型/客服队列状态/"
|
|
|
|
|
+ + "附件),契约要点:提交反馈(内容/类型)→ 进入客服队列(SC-C-23)。实现未落地。");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-4:UserFeedbackService 应存在并提供 submit 入口")
|
|
|
|
|
+ void service_userFeedback_serviceAndSubmitExists() {
|
|
|
|
|
+ Class<?> clazz = loadClass("com.etotem.cfc.service.UserFeedbackService");
|
|
|
|
|
+ assertNotNull(clazz,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 不存在:契约要求通用意见反馈提交入口"
|
|
|
|
|
+ + "(§4.61.7 G-06;SC-C-23 主流程)。实现未落地。");
|
|
|
|
|
+ boolean hasSubmit = false;
|
|
|
|
|
+ for (Method m : clazz.getDeclaredMethods()) {
|
|
|
|
|
+ if (m.getName().equals("submit")) {
|
|
|
|
|
+ hasSubmit = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertTrue(hasSubmit,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 应提供 submit(...) 入口(内容/类型 → 客服队列),"
|
|
|
|
|
+ + "接受成员级 memberId 维度(孩子不能登录无 userId;SC-C-23 主流程)。实现未落地。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-5:UserFeedbackController 应存在并映射 POST /api/user/feedback/submit")
|
|
|
|
|
+ void controller_userFeedback_submitMappingExists() {
|
|
|
|
|
+ Class<?> clazz = loadClass("com.etotem.cfc.controller.UserFeedbackController");
|
|
|
|
|
+ assertNotNull(clazz,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackController 不存在:契约要求 POST /api/user/feedback/submit"
|
|
|
|
|
+ + " 意见反馈端点(§4.61.7 G-06,避开 /api/feedback 前缀冲突;SC-C-23 主流程)。实现未落地。");
|
|
|
|
|
+ boolean hasSubmit = false;
|
|
|
|
|
+ for (Method m : clazz.getDeclaredMethods()) {
|
|
|
|
|
+ Map<String, String> mapping = resolveMappingPath(m);
|
|
|
|
|
+ if (SUBMIT_PATH.equals(mapping.get("path")) && "POST".equals(mapping.get("httpMethod"))) {
|
|
|
|
|
+ hasSubmit = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertTrue(hasSubmit,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackController 应映射 " + SUBMIT_PATH
|
|
|
|
|
+ + "(POST):提交反馈后记录进入客服队列(SC-C-23 主流程;§4.61.7 G-06)。实现未落地。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 边界流:内容适龄过滤 / 敏感信息脱敏 / 附件大小限制
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("边界流-1:应提供内容适龄过滤位点(含年龄段位点)")
|
|
|
|
|
+ void boundary_ageAppropriatenessFilterExists() {
|
|
|
|
|
+ Class<?> clazz = loadClass("com.etotem.cfc.service.UserFeedbackService");
|
|
|
|
|
+ assertNotNull(clazz,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 不存在,无法验证内容适龄过滤位点(SC-C-23 边界)。");
|
|
|
|
|
+
|
|
|
|
|
+ boolean hasChildAge = false;
|
|
|
|
|
+ boolean hasAgeFilter = false;
|
|
|
|
|
+ for (Field f : clazz.getDeclaredFields()) {
|
|
|
|
|
+ if (f.getName().contains("ageGroup") || f.getName().contains("Age")) {
|
|
|
|
|
+ hasChildAge = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Method m : clazz.getDeclaredMethods()) {
|
|
|
|
|
+ String name = m.getName();
|
|
|
|
|
+ if (name.toLowerCase().contains("age") || name.contains("filter")
|
|
|
|
|
+ || name.contains("sanitize") || name.contains("review")) {
|
|
|
|
|
+ hasAgeFilter = true;
|
|
|
|
|
+ hasChildAge = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertTrue(hasChildAge,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 应提供适龄过滤位点(如方法/字段含 age/filter),"
|
|
|
|
|
+ + "异常/边界:内容适龄过滤(SC-C-23)。实现未落地。");
|
|
|
|
|
+ assertTrue(hasAgeFilter,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 应提供内容安全过滤(filter/sanitize/review),"
|
|
|
|
|
+ + "对反馈内容做适龄/安全把关(SC-C-23 边界)。实现未落地。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("边界流-2:应提供敏感信息脱敏位点")
|
|
|
|
|
+ void boundary_sensitiveInfoDesensitizationExists() {
|
|
|
|
|
+ Class<?> clazz = loadClass("com.etotem.cfc.service.UserFeedbackService");
|
|
|
|
|
+ assertNotNull(clazz,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 不存在,无法验证敏感信息脱敏位点(SC-C-23 边界)。");
|
|
|
|
|
+ boolean hasMask = false;
|
|
|
|
|
+ boolean hasDesensitize = false;
|
|
|
|
|
+ for (Field f : clazz.getDeclaredFields()) {
|
|
|
|
|
+ if (f.getName().toLowerCase().contains("mask")
|
|
|
|
|
+ || f.getName().toLowerCase().contains("sensitive")
|
|
|
|
|
+ || f.getName().toLowerCase().contains("desensit")) {
|
|
|
|
|
+ hasMask = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Method m : clazz.getDeclaredMethods()) {
|
|
|
|
|
+ String name = m.getName().toLowerCase();
|
|
|
|
|
+ if (name.contains("mask") || name.contains("desensit") || name.contains("pii")) {
|
|
|
|
|
+ hasDesensitize = true;
|
|
|
|
|
+ hasMask = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertTrue(hasMask,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 应提供敏感信息脱敏位点(mask/desensitize/"
|
|
|
|
|
+ + "sensitive/pii),异常/边界:敏感信息脱敏(SC-C-23)。实现未落地。");
|
|
|
|
|
+ assertTrue(hasDesensitize,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 应提供脱敏处理(mask/desensitize/pii),"
|
|
|
|
|
+ + "反馈内容中的手机号/身份证等敏感信息入库前脱敏(SC-C-23 边界)。实现未落地。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("边界流-3:应提供附件大小限制位点")
|
|
|
|
|
+ void boundary_attachmentSizeLimitExists() {
|
|
|
|
|
+ Class<?> clazz = loadClass("com.etotem.cfc.service.UserFeedbackService");
|
|
|
|
|
+ assertNotNull(clazz,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 不存在,无法验证附件大小限制位点(SC-C-23 边界)。");
|
|
|
|
|
+ boolean hasLimit = false;
|
|
|
|
|
+ boolean hasLimitCheck = false;
|
|
|
|
|
+ for (Field f : clazz.getDeclaredFields()) {
|
|
|
|
|
+ if (f.getName().toLowerCase().contains("size")
|
|
|
|
|
+ || f.getName().contains("MAX") || f.getName().contains("limit")) {
|
|
|
|
|
+ hasLimit = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Method m : clazz.getDeclaredMethods()) {
|
|
|
|
|
+ String name = m.getName().toLowerCase();
|
|
|
|
|
+ if (name.contains("attachment") || name.contains("size")
|
|
|
|
|
+ || name.contains("limit") || name.contains("check")) {
|
|
|
|
|
+ hasLimitCheck = true;
|
|
|
|
|
+ hasLimit = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertTrue(hasLimit,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 应提供附件大小限制位点(size/MAX/limit),"
|
|
|
|
|
+ + "异常/边界:附件大小限制(SC-C-23)。实现未落地。");
|
|
|
|
|
+ assertTrue(hasLimitCheck,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackService 应提供附件校验(attachment/size/limit/check),"
|
|
|
|
|
+ + "超出上限的附件应被拒绝(SC-C-23 边界)。实现未落地。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("边界流-4:user_feedback 表应落地 schema.sql 并承载客服队列状态")
|
|
|
|
|
+ void schemaSql_hasUserFeedbackTable() {
|
|
|
|
|
+ String schema = readSchemaSql();
|
|
|
|
|
+ assertTrue(schema.contains(FEEDBACK_TABLE),
|
|
|
|
|
+ "[child.feedback.submit] schema.sql 缺少 " + FEEDBACK_TABLE
|
|
|
|
|
+ + " 建表:通用意见反馈落库位缺失(§4.61.7 G-06)。实现未落地。");
|
|
|
|
|
+ assertTrue(schema.contains("status"),
|
|
|
|
|
+ "[child.feedback.submit] " + FEEDBACK_TABLE
|
|
|
|
|
+ + " 应含 status 列承载客服队列状态(提交反馈 → 进入客服队列,SC-C-23 主流程)。实现未落地。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 权限流:孩子(不能登录用户)由家庭成员代管提交,按 memberId 归属自身
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("权限流:提交入口应接受成员级 memberId(孩子不能登录,由家庭成员代管)")
|
|
|
|
|
+ void permissionFlow_memberScopedSubmit() {
|
|
|
|
|
+ Class<?> clazz = loadClass("com.etotem.cfc.controller.UserFeedbackController");
|
|
|
|
|
+ assertNotNull(clazz,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackController 不存在,无法验证成员级提交归属(SC-C-23 前置)。");
|
|
|
|
|
+ boolean hasMemberSubmit = false;
|
|
|
|
|
+ for (Method m : clazz.getDeclaredMethods()) {
|
|
|
|
|
+ for (Parameter p : m.getParameters()) {
|
|
|
|
|
+ if (p.getName().equals("memberId")) {
|
|
|
|
|
+ hasMemberSubmit = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ assertTrue(hasMemberSubmit,
|
|
|
|
|
+ "[child.feedback.submit] UserFeedbackController 提交入口应接受 memberId 参数:"
|
|
|
|
|
+ + "孩子(不能登录用户)无 userId,意见反馈须以家庭成员 memberId 维度提交,"
|
|
|
|
|
+ + "且应校验 familyId 归属(SC-C-23 前置条件:已登录)。实现未落地。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 反射辅助
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ private Class<?> loadClass(String name) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ return Class.forName(name, false, Thread.currentThread().getContextClassLoader());
|
|
|
|
|
+ } catch (ClassNotFoundException e) {
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 读取类级 @RequestMapping 的 path 值 */
|
|
|
|
|
+ private String resolveRequestMapping(Class<?> clazz) {
|
|
|
|
|
+ if (clazz.isAnnotationPresent(org.springframework.web.bind.annotation.RequestMapping.class)) {
|
|
|
|
|
+ org.springframework.web.bind.annotation.RequestMapping rm =
|
|
|
|
|
+ clazz.getAnnotation(org.springframework.web.bind.annotation.RequestMapping.class);
|
|
|
|
|
+ String[] value = rm.value();
|
|
|
|
|
+ if (value != null && value.length > 0) {
|
|
|
|
|
+ return value[0];
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 读取方法级映射 path(@PostMapping/@GetMapping/@RequestMapping) */
|
|
|
|
|
+ private Map<String, String> resolveMappingPath(Method m) {
|
|
|
|
|
+ Map<String, String> result = new HashMap<>();
|
|
|
|
|
+ org.springframework.web.bind.annotation.PostMapping pm =
|
|
|
|
|
+ m.getAnnotation(org.springframework.web.bind.annotation.PostMapping.class);
|
|
|
|
|
+ if (pm != null) {
|
|
|
|
|
+ String[] value = pm.value();
|
|
|
|
|
+ result.put("path", (value != null && value.length > 0) ? value[0] : "");
|
|
|
|
|
+ result.put("httpMethod", "POST");
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+ org.springframework.web.bind.annotation.GetMapping gm =
|
|
|
|
|
+ m.getAnnotation(org.springframework.web.bind.annotation.GetMapping.class);
|
|
|
|
|
+ if (gm != null) {
|
|
|
|
|
+ String[] value = gm.value();
|
|
|
|
|
+ result.put("path", (value != null && value.length > 0) ? value[0] : "");
|
|
|
|
|
+ result.put("httpMethod", "GET");
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+ org.springframework.web.bind.annotation.RequestMapping rm =
|
|
|
|
|
+ m.getAnnotation(org.springframework.web.bind.annotation.RequestMapping.class);
|
|
|
|
|
+ if (rm != null) {
|
|
|
|
|
+ String[] value = rm.value();
|
|
|
|
|
+ result.put("path", (value != null && value.length > 0) ? value[0] : "");
|
|
|
|
|
+ result.put("httpMethod", "REQUEST");
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private String readSchemaSql() {
|
|
|
|
|
+ try (InputStream is = getClass().getClassLoader().getResourceAsStream("schema.sql")) {
|
|
|
|
|
+ if (is == null) {
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
+ try (Scanner scanner = new Scanner(is, StandardCharsets.UTF_8.name())) {
|
|
|
|
|
+ scanner.useDelimiter("\\A");
|
|
|
|
|
+ return scanner.hasNext() ? scanner.next() : "";
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ return "";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|