Browse Source

1.解除开发环境设备xyz参数
2.web端问卷调查接口发布
3.小程序端问卷调查接口发布

hxd 1 tháng trước cách đây
mục cha
commit
f815f43844
36 tập tin đã thay đổi với 1265 bổ sung51 xóa
  1. 3 4
      portal-service-application/src/main/java/com/hfln/portal/application/controller/wap/GroupController.java
  2. 42 0
      portal-service-application/src/main/java/com/hfln/portal/application/controller/wap/QuestionController.java
  3. 80 0
      portal-service-application/src/main/java/com/hfln/portal/application/controller/web/WebQuestionController.java
  4. 37 0
      portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/QuestionDTO.java
  5. 32 0
      portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/QuestionnaireDTO.java
  6. 23 0
      portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/QuestionnaireDetailsDTO.java
  7. 30 0
      portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/UserQuestionDetailsDTO.java
  8. 29 0
      portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/UserQuestionnaireDetailsDTO.java
  9. 27 0
      portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/UserQuestionnaireListDTO.java
  10. 14 14
      portal-service-common/src/main/java/com/hfln/portal/common/request/device/DeviceBandingParams.java
  11. 14 14
      portal-service-common/src/main/java/com/hfln/portal/common/request/device/UpdateDeviceParams.java
  12. 12 12
      portal-service-common/src/main/java/com/hfln/portal/common/request/device/WebUpdateDeviceParams.java
  13. 25 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/AnswerSaveParam.java
  14. 1 1
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/ParameterUpsertParam.java
  15. 34 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/QuestionSaveParam.java
  16. 26 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/QuestionnaireQueryParam.java
  17. 25 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/QuestionnaireSaveParam.java
  18. 22 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/UserQuestionnaireDetailsParam.java
  19. 14 0
      portal-service-common/src/main/java/com/hfln/portal/common/request/web/UserQuestionnaireListParam.java
  20. 2 2
      portal-service-common/src/main/java/com/hfln/portal/common/vo/PageVo.java
  21. 18 0
      portal-service-domain/src/main/java/com/hfln/portal/domain/exception/ErrorEnum.java
  22. 36 0
      portal-service-domain/src/main/java/com/hfln/portal/domain/gateway/WebQuestionGateway.java
  23. 19 4
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/WebGatewayImpl.java
  24. 460 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/WebQuestionGatewayImpl.java
  25. 13 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/AnswerMapper.java
  26. 13 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/QuestionMapper.java
  27. 13 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/QuestionnaireMapper.java
  28. 44 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/Answer.java
  29. 54 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/Question.java
  30. 39 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/Questionnaire.java
  31. 7 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/AnswerService.java
  32. 7 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/QuestionService.java
  33. 8 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/QuestionnaireService.java
  34. 14 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/AnswerServiceImpl.java
  35. 14 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/QuestionServiceImpl.java
  36. 14 0
      portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/QuestionnaireServiceImpl.java

+ 3 - 4
portal-service-application/src/main/java/com/hfln/portal/application/controller/wap/GroupController.java

@@ -13,7 +13,6 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import javax.validation.Valid;
-import javax.validation.constraints.NotNull;
 import java.util.List;
 
 @RestController
@@ -34,7 +33,7 @@ public class GroupController {
 
     @PostMapping("/deleteGroup")
     @Operation(summary = "删除群组")
-    public ApiResult<Boolean> deleteGroup(@RequestParam("group_id") Long groupId){
+    public ApiResult<Boolean> deleteGroup(@RequestParam Long groupId){
         return ApiResult.success(groupGateway.deleteGroup(groupId));
     }
 
@@ -46,13 +45,13 @@ public class GroupController {
 
     @GetMapping("/groupList")
     @Operation(summary = "获取群组列表")
-    public ApiResult<List<GroupDTO>> GroupList(@RequestParam(value = "user_id", required = true) @NotNull(message = "用户Id不能为空!") Long userId) {
+    public ApiResult<List<GroupDTO>> GroupList(@RequestParam Long userId) {
         return ApiResult.success(groupGateway.queryGroupList(userId));
     }
 
     @GetMapping("/queryGroupDeviceInfoById")
     @Operation(summary = "获取群组设备列表")
-    public ApiResult<List<DeviceDTO>> queryGroupById(@RequestParam("group_id") Long groupId){
+    public ApiResult<List<DeviceDTO>> queryGroupById(@RequestParam Long groupId){
         return ApiResult.success(groupGateway.queryGroupDeviceInfoById(groupId));
     }
 

+ 42 - 0
portal-service-application/src/main/java/com/hfln/portal/application/controller/wap/QuestionController.java

@@ -0,0 +1,42 @@
+package com.hfln.portal.application.controller.wap;
+
+
+import cn.hfln.framework.catchlog.CatchAndLog;
+import cn.hfln.framework.dto.ApiResult;
+import com.hfln.portal.common.dto.data.question.QuestionnaireDetailsDTO;
+import com.hfln.portal.common.request.web.AnswerSaveParam;
+import com.hfln.portal.domain.gateway.WebQuestionGateway;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+@RestController
+@CatchAndLog
+@Tag(name = "问卷相关")
+@Slf4j
+@RequestMapping("/wap/question")
+public class QuestionController {
+
+
+    @Autowired
+    private WebQuestionGateway webQuestionGateway;
+
+    @GetMapping("/queryQuestionnaireDetails")
+    @Operation(summary = "查询问卷详情")
+    public ApiResult<QuestionnaireDetailsDTO> queryQuestionnaireDetails(@RequestParam Long questionnaireId){
+        return ApiResult.success(webQuestionGateway.queryQuestionnaireDetails(questionnaireId));
+    }
+
+    @PostMapping("/saveAnswer")
+    @Operation(summary = "保存问卷答案")
+    public ApiResult<Void> saveAnswer(@RequestBody @Valid List<AnswerSaveParam> params){
+        webQuestionGateway.saveAnswer(params);
+        return ApiResult.success();
+    }
+
+}

+ 80 - 0
portal-service-application/src/main/java/com/hfln/portal/application/controller/web/WebQuestionController.java

@@ -0,0 +1,80 @@
+package com.hfln.portal.application.controller.web;
+
+
+import cn.hfln.framework.catchlog.CatchAndLog;
+import cn.hfln.framework.dto.ApiResult;
+import com.hfln.portal.common.dto.data.question.*;
+import com.hfln.portal.common.request.web.*;
+import com.hfln.portal.common.vo.PageRecord;
+import com.hfln.portal.domain.gateway.WebQuestionGateway;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+@RestController
+@CatchAndLog
+@Tag(name = "web端问卷相关")
+@Slf4j
+@RequestMapping("/web/question")
+public class WebQuestionController {
+
+
+    @Autowired
+    private WebQuestionGateway webQuestionGateway;
+
+    @PostMapping("/saveQuestionnaire")
+    @Operation(summary = "新增或编辑问卷")
+    public ApiResult<QuestionnaireDTO> saveQuestionnaire(@RequestBody @Valid QuestionnaireSaveParam param){
+        return ApiResult.success(webQuestionGateway.saveQuestionnaire(param));
+    }
+
+    @PostMapping("/deleteQuestionnaire")
+    @Operation(summary = "删除问卷")
+    public ApiResult<Void> deleteQuestionnaire(@RequestParam Long questionnaireId){
+        webQuestionGateway.deleteQuestionnaire(questionnaireId);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/queryQuestionnaire")
+    @Operation(summary = "查询问卷列表")
+    public ApiResult<PageRecord<QuestionnaireDTO>> queryQuestionnaire(@RequestBody @Valid QuestionnaireQueryParam  param){
+        return ApiResult.success(webQuestionGateway.queryQuestionnaire(param));
+    }
+
+    @GetMapping("/queryQuestionnaireDetails")
+    @Operation(summary = "查询问卷详情")
+    public ApiResult<QuestionnaireDetailsDTO> queryQuestionnaireDetails(@RequestParam Long questionnaireId){
+        return ApiResult.success(webQuestionGateway.queryQuestionnaireDetails(questionnaireId));
+    }
+
+    @PostMapping("/saveQuestion")
+    @Operation(summary = "新增或编辑问题")
+    public ApiResult<List<QuestionDTO>> saveQuestion(@RequestBody @Valid List<QuestionSaveParam> params){
+        return ApiResult.success(webQuestionGateway.saveQuestion(params));
+    }
+
+    @PostMapping("/saveAnswer")
+    @Operation(summary = "保存问卷答案")
+    public ApiResult<Void> saveAnswer(@RequestBody @Valid List<AnswerSaveParam> params){
+        webQuestionGateway.saveAnswer(params);
+        return ApiResult.success();
+    }
+
+    @PostMapping("/queryUserQuestionnaireList")
+    @Operation(summary = "查询用户问卷列表")
+    public ApiResult<PageRecord<UserQuestionnaireListDTO>> queryUserQuestionnaireList(@RequestBody @Valid UserQuestionnaireListParam param){
+        return ApiResult.success(webQuestionGateway.queryUserQuestionnaireList(param));
+    }
+
+    @PostMapping("/queryUserQuestionnaireDetails")
+    @Operation(summary = "查询用户问卷详情")
+    public ApiResult<UserQuestionnaireDetailsDTO> queryUserQuestionnaireDetails(@RequestBody @Valid UserQuestionnaireDetailsParam param){
+        return ApiResult.success(webQuestionGateway.queryUserQuestionnaireDetails(param));
+    }
+
+}

+ 37 - 0
portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/QuestionDTO.java

@@ -0,0 +1,37 @@
+
+package com.hfln.portal.common.dto.data.question;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class QuestionDTO extends BaseVO {
+
+    @Schema(description = "问题表主键id")
+    private Long questionId;
+
+    @Schema(description = "问卷表主键id")
+    private Long questionnaireId;
+
+    @Schema(description = "问题类型")
+    private String questionType;
+
+    @Schema(description = "问题内容")
+    private String content;
+
+    @Schema(description = "问题配置")
+    private String config;
+
+    @Schema(description = "是否必填:1-必填,0-选填")
+    private Integer required;
+
+    @Schema(description = "排序")
+    private int sort;
+
+    @Schema(description = "备注")
+    private String remark;
+}

+ 32 - 0
portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/QuestionnaireDTO.java

@@ -0,0 +1,32 @@
+package com.hfln.portal.common.dto.data.question;
+
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class QuestionnaireDTO extends BaseVO {
+
+    @Schema(description = "问卷表主键id")
+    private Long questionnaireId;
+
+    @Schema(description = "问卷标题")
+    private String title;
+
+    @Schema(description = "问卷描述")
+    private String description;
+
+    @Schema(description = "是否启用")
+    private Integer enable;
+
+    @Schema(description = "创建时间")
+    private LocalDateTime createTime;
+
+    @Schema(description = "备注")
+    private String remark;
+}

+ 23 - 0
portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/QuestionnaireDetailsDTO.java

@@ -0,0 +1,23 @@
+package com.hfln.portal.common.dto.data.question;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class QuestionnaireDetailsDTO extends BaseVO {
+
+    @Schema(description = "问卷id")
+    private Long questionnaireId;
+
+    @Schema(description = "问卷标题")
+    private String title;
+
+    @Schema(description = "问卷描述")
+    private String description;
+
+    @Schema(description = "问题集合")
+    private List<QuestionDTO> questionList;
+}

+ 30 - 0
portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/UserQuestionDetailsDTO.java

@@ -0,0 +1,30 @@
+package com.hfln.portal.common.dto.data.question;
+
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class UserQuestionDetailsDTO extends BaseVO {
+
+    @Schema(description = "问题id")
+    private Long questionId;
+
+    @Schema(description = "问题类型")
+    private String questionType;
+
+    @Schema(description = "问题内容")
+    private String Content;
+
+    @Schema(description = "问题配置")
+    private String config;
+
+    @Schema(description = "问题排序")
+    private int sort;
+
+    @Schema(description = "用户问题答案")
+    private String answer;
+
+
+}

+ 29 - 0
portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/UserQuestionnaireDetailsDTO.java

@@ -0,0 +1,29 @@
+package com.hfln.portal.common.dto.data.question;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.util.List;
+
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class UserQuestionnaireDetailsDTO extends BaseVO {
+
+    @Schema(description = "问卷id")
+    private Long questionnaireId;
+
+    @Schema(description = "问卷标题")
+    private String Title;
+
+    @Schema(description = "答题用户id")
+    private Long userId;
+
+    @Schema(description = "答题用户手机号")
+    private String userPhone;
+
+    @Schema(description = "问题集合")
+    private List<UserQuestionDetailsDTO> questionList;
+}

+ 27 - 0
portal-service-common/src/main/java/com/hfln/portal/common/dto/data/question/UserQuestionnaireListDTO.java

@@ -0,0 +1,27 @@
+package com.hfln.portal.common.dto.data.question;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDateTime;
+
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class UserQuestionnaireListDTO extends BaseVO {
+
+
+    @Schema(description = "问卷id")
+    private Long questionnaireId;
+
+    @Schema(description = "问卷标题")
+    private String questionnaireTitle;
+
+    @Schema(description = "答题用户手机号")
+    private String userPhone;
+
+    @Schema(description = "答题时间")
+    private LocalDateTime answerTime;
+}

+ 14 - 14
portal-service-common/src/main/java/com/hfln/portal/common/request/device/DeviceBandingParams.java

@@ -26,8 +26,8 @@ public class DeviceBandingParams extends BaseVO {
     private String devName;
 
     @Schema(description = "安装高度: 200 - 370 cm 之内")
-    @DecimalMax(value = "370", message = "height不能大于370")
-    @DecimalMin(value = "200", message = "height不能小于200")
+//    @DecimalMax(value = "370", message = "height不能大于370")
+//    @DecimalMin(value = "200", message = "height不能小于200")
     private BigDecimal height;
 
     @Schema(description = "WIFI名称")
@@ -47,35 +47,35 @@ public class DeviceBandingParams extends BaseVO {
     private String installPosition;
 
     @Schema(description = "雷达监测范围x开始,范围:-200-200 cm之内")
-    @DecimalMin(value = "-200", message = "xxStart不能小于-200")
-    @DecimalMax(value = "200", message = "xxStart不能大于200")
+//    @DecimalMin(value = "-200", message = "xxStart不能小于-200")
+//    @DecimalMax(value = "200", message = "xxStart不能大于200")
     private BigDecimal xxStart;
 
     @Schema(description = "雷达监测范围x结束,范围:-200-200 cm之内,且xxEnd > xxStart")
-    @DecimalMin(value = "-200", message = "xxEnd不能小于-200")
-    @DecimalMax(value = "200", message = "xxEnd不能大于200")
+//    @DecimalMin(value = "-200", message = "xxEnd不能小于-200")
+//    @DecimalMax(value = "200", message = "xxEnd不能大于200")
     private BigDecimal xxEnd;
 
 
     @Schema(description = "雷达监测范围y开始,范围:-250-250 cm之内")
-    @DecimalMin(value = "-250", message = "yyStart不能小于-250")
-    @DecimalMax(value = "250", message = "yyStart不能大于250")
+//    @DecimalMin(value = "-250", message = "yyStart不能小于-250")
+//    @DecimalMax(value = "250", message = "yyStart不能大于250")
     private BigDecimal yyStart;
 
     @Schema(description = "雷达监测范围y结束,范围:-250-250 cm之内,且yyEnd > yyStart")
-    @DecimalMin(value = "-250", message = "yyEnd不能小于-250")
-    @DecimalMax(value = "250", message = "yyEnd不能大于250")
+//    @DecimalMin(value = "-250", message = "yyEnd不能小于-250")
+//    @DecimalMax(value = "250", message = "yyEnd不能大于250")
     private BigDecimal yyEnd;
 
 
     @Schema(description = "雷达监测范围z开始,范围:0-5 cm之内")
-    @DecimalMin(value = "0", message = "zzStart不能小于0")
-    @DecimalMax(value = "5", message = "zzStart不能大于5")
+//    @DecimalMin(value = "0", message = "zzStart不能小于0")
+//    @DecimalMax(value = "5", message = "zzStart不能大于5")
     private BigDecimal zzStart;
 
 
     @Schema(description = "雷达监测范围z结束,范围:200-300 cm之内")
-    @DecimalMin(value = "200", message = "zzEnd不能小于200")
-    @DecimalMax(value = "300", message = "zzEnd不能大于300")
+//    @DecimalMin(value = "200", message = "zzEnd不能小于200")
+//    @DecimalMax(value = "300", message = "zzEnd不能大于300")
     private BigDecimal zzEnd;
 }

+ 14 - 14
portal-service-common/src/main/java/com/hfln/portal/common/request/device/UpdateDeviceParams.java

@@ -28,8 +28,8 @@ public class UpdateDeviceParams extends BaseVO {
     private String devName;
 
     @Schema(description = "安装高度: 200 - 370 cm 之内")
-    @DecimalMax(value = "370", message = "height不能大于370")
-    @DecimalMin(value = "200", message = "height不能小于200")
+//    @DecimalMax(value = "370", message = "height不能大于370")
+//    @DecimalMin(value = "200", message = "height不能小于200")
     private BigDecimal height;
 
     @Schema(description = "WIFI名称")
@@ -49,36 +49,36 @@ public class UpdateDeviceParams extends BaseVO {
     private String installPosition;
 
     @Schema(description = "雷达监测范围x开始,范围:-200-200 cm之内")
-    @DecimalMin(value = "-200", message = "xxStart不能小于-200")
-    @DecimalMax(value = "200", message = "xxStart不能大于200")
+//    @DecimalMin(value = "-200", message = "xxStart不能小于-200")
+//    @DecimalMax(value = "200", message = "xxStart不能大于200")
     private BigDecimal xxStart;
 
     @Schema(description = "雷达监测范围x结束,范围:-200-200 cm之内,且xxEnd > xxStart")
-    @DecimalMin(value = "-200", message = "xxEnd不能小于-200")
-    @DecimalMax(value = "200", message = "xxEnd不能大于200")
+//    @DecimalMin(value = "-200", message = "xxEnd不能小于-200")
+//    @DecimalMax(value = "200", message = "xxEnd不能大于200")
     private BigDecimal xxEnd;
 
 
     @Schema(description = "雷达监测范围y开始,范围:-250-250 cm之内")
-    @DecimalMin(value = "-250", message = "yyStart不能小于-250")
-    @DecimalMax(value = "250", message = "yyStart不能大于250")
+//    @DecimalMin(value = "-250", message = "yyStart不能小于-250")
+//    @DecimalMax(value = "250", message = "yyStart不能大于250")
     private BigDecimal yyStart;
 
     @Schema(description = "雷达监测范围y结束,范围:-250-250 cm之内,且yyEnd > yyStart")
-    @DecimalMin(value = "-250", message = "yyEnd不能小于-250")
-    @DecimalMax(value = "250", message = "yyEnd不能大于250")
+//    @DecimalMin(value = "-250", message = "yyEnd不能小于-250")
+//    @DecimalMax(value = "250", message = "yyEnd不能大于250")
     private BigDecimal yyEnd;
 
 
     @Schema(description = "雷达监测范围z开始,范围:0-5 cm之内")
-    @DecimalMin(value = "0", message = "zzStart不能小于0")
-    @DecimalMax(value = "5", message = "zzStart不能大于5")
+//    @DecimalMin(value = "0", message = "zzStart不能小于0")
+//    @DecimalMax(value = "5", message = "zzStart不能大于5")
     private BigDecimal zzStart;
 
 
     @Schema(description = "雷达监测范围z结束,范围:200-300 cm之内")
-    @DecimalMin(value = "200", message = "zzEnd不能小于200")
-    @DecimalMax(value = "300", message = "zzEnd不能大于300")
+//    @DecimalMin(value = "200", message = "zzEnd不能小于200")
+//    @DecimalMax(value = "300", message = "zzEnd不能大于300")
     private BigDecimal zzEnd;
 
 }

+ 12 - 12
portal-service-common/src/main/java/com/hfln/portal/common/request/device/WebUpdateDeviceParams.java

@@ -51,36 +51,36 @@ public class WebUpdateDeviceParams extends BaseVO {
     private String installPosition;
 
     @Schema(description = "雷达监测范围x开始,范围:-200-200 cm之内")
-    @DecimalMin(value = "-200", message = "xxStart不能小于-200")
-    @DecimalMax(value = "200", message = "xxStart不能大于200")
+//    @DecimalMin(value = "-200", message = "xxStart不能小于-200")
+//    @DecimalMax(value = "200", message = "xxStart不能大于200")
     private BigDecimal xxStart;
 
     @Schema(description = "雷达监测范围x结束,范围:-200-200 cm之内,且xxEnd > xxStart")
-    @DecimalMin(value = "-200", message = "xxEnd不能小于-200")
-    @DecimalMax(value = "200", message = "xxEnd不能大于200")
+//    @DecimalMin(value = "-200", message = "xxEnd不能小于-200")
+//    @DecimalMax(value = "200", message = "xxEnd不能大于200")
     private BigDecimal xxEnd;
 
 
     @Schema(description = "雷达监测范围y开始,范围:-250-250 cm之内")
-    @DecimalMin(value = "-250", message = "yyStart不能小于-250")
-    @DecimalMax(value = "250", message = "yyStart不能大于250")
+//    @DecimalMin(value = "-250", message = "yyStart不能小于-250")
+//    @DecimalMax(value = "250", message = "yyStart不能大于250")
     private BigDecimal yyStart;
 
     @Schema(description = "雷达监测范围y结束,范围:-250-250 cm之内,且yyEnd > yyStart")
-    @DecimalMin(value = "-250", message = "yyEnd不能小于-250")
-    @DecimalMax(value = "250", message = "yyEnd不能大于250")
+//    @DecimalMin(value = "-250", message = "yyEnd不能小于-250")
+//    @DecimalMax(value = "250", message = "yyEnd不能大于250")
     private BigDecimal yyEnd;
 
 
     @Schema(description = "雷达监测范围z开始,范围:0-5 cm之内")
-    @DecimalMin(value = "0", message = "zzStart不能小于0")
-    @DecimalMax(value = "5", message = "zzStart不能大于5")
+//    @DecimalMin(value = "0", message = "zzStart不能小于0")
+//    @DecimalMax(value = "5", message = "zzStart不能大于5")
     private BigDecimal zzStart;
 
 
     @Schema(description = "雷达监测范围z结束,范围:200-300 cm之内")
-    @DecimalMin(value = "200", message = "zzEnd不能小于200")
-    @DecimalMax(value = "300", message = "zzEnd不能大于300")
+//    @DecimalMin(value = "200", message = "zzEnd不能小于200")
+//    @DecimalMax(value = "300", message = "zzEnd不能大于300")
     private BigDecimal zzEnd;
 
     @Schema(description = "设备跌倒确认时间,大于0")

+ 25 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/web/AnswerSaveParam.java

@@ -0,0 +1,25 @@
+package com.hfln.portal.common.request.web;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+
+@Data
+public class AnswerSaveParam extends BaseVO {
+
+    @Schema(description = "问卷表主键id")
+    private Long questionnaireId;
+
+    @Schema(description = "问题表主键id")
+    private Long questionId;
+
+    @Schema(description = "用户id")
+    private Long userId;
+
+    @Schema(description = "用户答案内容")
+    private String answer;
+
+    @Schema(description = "备注")
+    private String remark;
+}

+ 1 - 1
portal-service-common/src/main/java/com/hfln/portal/common/request/web/ParameterUpsertParam.java

@@ -10,7 +10,7 @@ import javax.validation.constraints.NotBlank;
 public class ParameterUpsertParam extends BaseVO {
 
 	@Schema(description = "系统参数表主键id,如果param_id为空则新增,否则修改")
-	private Long paramId;
+	private Long parameterId;
 
 
 	@Schema(description ="参数编码")

+ 34 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/web/QuestionSaveParam.java

@@ -0,0 +1,34 @@
+package com.hfln.portal.common.request.web;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+
+@Data
+public class QuestionSaveParam extends BaseVO {
+
+    @Schema(description = "问题表主键id")
+    private Long questionId;
+
+    @Schema(description = "问卷表主键id")
+    private Long questionnaireId;
+
+    @Schema(description = "问题类型: 查询字典表 关键词:question_type ")
+    private String questionType;
+
+    @Schema(description = "问题内容")
+    private String content;
+
+    @Schema(description = "问题配置")
+    private String config;
+
+    @Schema(description = "是否必填:1-必填,0-选填")
+    private Integer required;
+
+    @Schema(description = "排序")
+    private int sort;
+
+    @Schema(description = "备注")
+    private String remark;
+}

+ 26 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/web/QuestionnaireQueryParam.java

@@ -0,0 +1,26 @@
+package com.hfln.portal.common.request.web;
+
+import com.hfln.portal.common.vo.PageVo;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.time.LocalDate;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class QuestionnaireQueryParam extends PageVo {
+
+    @Schema(description = "问卷标题")
+    private String title;
+
+    @Schema(description = "是否启用, 0-禁用,1-启用")
+    private Integer enable;
+
+    @Schema(description = "起始时间 格式yyyy-MM-dd")
+    private LocalDate createTimeStart;
+
+    @Schema(description = "结束时间 格式yyyy-MM-dd")
+    private LocalDate createTimeEnd;
+
+}

+ 25 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/web/QuestionnaireSaveParam.java

@@ -0,0 +1,25 @@
+package com.hfln.portal.common.request.web;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+
+@Data
+public class QuestionnaireSaveParam extends BaseVO {
+
+    @Schema(description = "问卷表主键id")
+    private Long questionnaireId;
+
+    @Schema(description = "问卷标题")
+    private String title;
+
+    @Schema(description = "问卷描述")
+    private String description;
+
+    @Schema(description = "是否启用,0-禁用,1-启用")
+    private Integer enable;
+
+    @Schema(description = "备注")
+    private String remark;
+}

+ 22 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/web/UserQuestionnaireDetailsParam.java

@@ -0,0 +1,22 @@
+package com.hfln.portal.common.request.web;
+
+import com.hfln.portal.common.vo.BaseVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+public class UserQuestionnaireDetailsParam extends BaseVO {
+
+    @Schema(description = "问卷id")
+    @NotNull
+    private Long questionnaireId;
+
+    @Schema(description = "用户手机号")
+    @NotBlank
+    private String userPhone;
+}

+ 14 - 0
portal-service-common/src/main/java/com/hfln/portal/common/request/web/UserQuestionnaireListParam.java

@@ -0,0 +1,14 @@
+package com.hfln.portal.common.request.web;
+
+import com.hfln.portal.common.vo.PageVo;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class UserQuestionnaireListParam extends PageVo {
+
+    @Schema(description = "用户手机号")
+    private String userPhone;
+    @Schema(description = "问卷id")
+    private Long questionnaireId;
+}

+ 2 - 2
portal-service-common/src/main/java/com/hfln/portal/common/vo/PageVo.java

@@ -8,9 +8,9 @@ import lombok.EqualsAndHashCode;
 @EqualsAndHashCode(callSuper = true)
 public class PageVo extends BaseVO {
 
-    @Schema(description = "当前页码", required = true)
+    @Schema(description = "当前页码")
     private Integer pageNo = 1;
 
-    @Schema(description = "页面大小", required = true)
+    @Schema(description = "页面大小")
     private Integer pageSize = 10;
 }

+ 18 - 0
portal-service-domain/src/main/java/com/hfln/portal/domain/exception/ErrorEnum.java

@@ -140,6 +140,24 @@ public enum ErrorEnum implements ErrorEnumInterface{
     ALARM_TIME_NOT_EXIST("15002", "时间计划不存在!"),
     ALARM_PLAN_TPL_NOT_EXIST("15011", "告警计划模板不存在!"),
     ALARM_TIME_TPL_NOT_EXIST("15012", "时间计划模板不存在!"),
+
+    /**
+     * 问卷调查相关
+     */
+    QUESTIONNAIRE_NOT_EXIST("16001", "问卷不存在!"),
+    DELETE_QUESTIONNAIRE_FAIL("16002", "删除问卷失败!"),
+    QUESTION_NOT_EXIST("16003", "问题不存在!"),
+    DELETE_QUESTION_FAIL("16004", "删除问题失败!"),
+    QUESTION_LIST_NOT_EXIST("16005", "问题列表为空!"),
+    QUESTIONNAIRE_ID_IS_NULL("16006", "问卷ID不能为空!"),
+    ANSWER_LIST_IS_NOT_NULL("16007", "答案列表不能为空!"),
+    QUESTIONNAIRE_QUESTION_ANSWER_NULL("16008", "问卷ID、问题ID、答案ID不能为空!"),
+
+    /**
+     * 系统参数相关
+     */
+    PARAM_CODE_EXISTS("17001", "系统参数编码已存在!"),
+    PARAM_NOT_EXIST("17002", "系统参数不存在!"),
     ;
 
     private final String errorCode;

+ 36 - 0
portal-service-domain/src/main/java/com/hfln/portal/domain/gateway/WebQuestionGateway.java

@@ -0,0 +1,36 @@
+package com.hfln.portal.domain.gateway;
+
+import com.hfln.portal.common.dto.data.question.*;
+import com.hfln.portal.common.request.web.*;
+import com.hfln.portal.common.vo.PageRecord;
+
+import java.util.List;
+
+public interface WebQuestionGateway {
+
+    /**
+     * 问卷相关接口
+     */
+    QuestionnaireDTO saveQuestionnaire(QuestionnaireSaveParam param);
+
+    void deleteQuestionnaire(Long questionnaireId);
+
+    PageRecord<QuestionnaireDTO> queryQuestionnaire(QuestionnaireQueryParam param);
+
+    QuestionnaireDetailsDTO queryQuestionnaireDetails(Long questionnaireId);
+
+    /**
+     * 问题相关接口
+     */
+    List<QuestionDTO> saveQuestion(List<QuestionSaveParam> params);
+
+
+    /**
+     * 答案相关接口
+     */
+    void saveAnswer(List<AnswerSaveParam> params);
+
+    PageRecord<UserQuestionnaireListDTO> queryUserQuestionnaireList(UserQuestionnaireListParam param);
+
+    UserQuestionnaireDetailsDTO queryUserQuestionnaireDetails(UserQuestionnaireDetailsParam param);
+}

+ 19 - 4
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/WebGatewayImpl.java

@@ -111,11 +111,26 @@ public class WebGatewayImpl implements WebGateway {
 
     @Override
     public void upsertParameter(ParameterUpsertParam param) {
-        // 如果有ID则更新,否则新增
-        if (param.getParamId() != null) {
-            TblParameter exist = tblParameterService.getById(param.getParamId());
+
+        // 1.唯一性校验
+        boolean exists = tblParameterService.count(
+                new LambdaQueryWrapper<TblParameter>()
+                        .eq(TblParameter::getParamCode, param.getParamCode())
+                        .ne(param.getParameterId() != null, TblParameter::getParameterId, param.getParameterId())
+        ) > 0;
+
+        log.info("校验参数唯一性:paramCode={}, paramId={}, exists={}",
+                param.getParamCode(), param.getParameterId(), exists);
+
+        if (exists) {
+            throw new BizException(ErrorEnum.PARAM_CODE_EXISTS.getErrorCode(), ErrorEnum.PARAM_CODE_EXISTS.getErrorMessage());
+        }
+        // 2. 更新 or 新增
+        if (param.getParameterId() != null) {
+            TblParameter exist = tblParameterService.getById(param.getParameterId());
+            log.info("更新参数:{}", exist);
             if (exist == null) {
-                throw new BizException(ErrorEnum.DATA_NOT_EXISTS.getErrorCode(), ErrorEnum.DATA_NOT_EXISTS.getErrorMessage());
+                throw new BizException(ErrorEnum.PARAM_NOT_EXIST.getErrorCode(), ErrorEnum.PARAM_NOT_EXIST.getErrorMessage());
             }
             exist.setParamCode(param.getParamCode());
             exist.setParamName(param.getParamName());

+ 460 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/gateway/impl/WebQuestionGatewayImpl.java

@@ -0,0 +1,460 @@
+package com.hfln.portal.infrastructure.gateway.impl;
+
+import cn.hfln.framework.extension.BizException;
+import com.alibaba.excel.util.StringUtils;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.hfln.portal.common.dto.data.question.*;
+import com.hfln.portal.common.request.web.*;
+import com.hfln.portal.common.vo.PageRecord;
+import com.hfln.portal.domain.customer.util.CopyUtils;
+import com.hfln.portal.domain.exception.ErrorEnum;
+import com.hfln.portal.domain.gateway.WebQuestionGateway;
+import com.hfln.portal.infrastructure.po.Answer;
+import com.hfln.portal.infrastructure.po.Question;
+import com.hfln.portal.infrastructure.po.Questionnaire;
+import com.hfln.portal.infrastructure.po.UserInfo;
+import com.hfln.portal.infrastructure.service.AnswerService;
+import com.hfln.portal.infrastructure.service.QuestionService;
+import com.hfln.portal.infrastructure.service.QuestionnaireService;
+import com.hfln.portal.infrastructure.service.UserService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+@Slf4j
+@Service
+public class WebQuestionGatewayImpl implements WebQuestionGateway {
+
+    @Autowired
+    private QuestionnaireService questionnaireService;
+
+    @Autowired
+    private QuestionService questionService;
+
+    @Autowired
+    private AnswerService answerService;
+
+    @Autowired
+    private UserService userService;
+
+    @Override
+    public QuestionnaireDTO saveQuestionnaire(QuestionnaireSaveParam param) {
+
+        Questionnaire questionnaire;
+
+        if (param.getQuestionnaireId() != null) {
+            // 编辑
+            questionnaire = questionnaireService.getById(param.getQuestionnaireId());
+            if (questionnaire == null) {
+                throw new BizException(ErrorEnum.QUESTIONNAIRE_NOT_EXIST.getErrorCode(), ErrorEnum.QUESTIONNAIRE_NOT_EXIST.getErrorMessage());
+            }
+            // 只更新允许修改的字段
+            BeanUtils.copyProperties(param, questionnaire, "questionnaireId");
+            questionnaire.setUpdateTime(LocalDateTime.now());
+        } else {
+            // 新增
+            questionnaire = new Questionnaire();
+            BeanUtils.copyProperties(param, questionnaire, "questionnaireId");
+            questionnaire.setCreateTime(LocalDateTime.now());
+            questionnaire.setUpdateTime(LocalDateTime.now());
+        }
+
+        questionnaireService.saveOrUpdate(questionnaire);
+
+        return CopyUtils.copy(questionnaire, QuestionnaireDTO.class);
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void deleteQuestionnaire(Long questionnaireId) {
+        // 1. 检查问卷是否存在
+        Questionnaire questionnaire = questionnaireService.getById(questionnaireId);
+        if (questionnaire == null) {
+            throw new BizException(ErrorEnum.QUESTIONNAIRE_NOT_EXIST.getErrorCode(),
+                    ErrorEnum.QUESTIONNAIRE_NOT_EXIST.getErrorMessage());
+        }
+
+        // 2. 查询问卷下的所有问题
+        List<Question> questions = questionService.list(
+                new LambdaQueryWrapper<Question>()
+                        .eq(Question::getQuestionnaireId, questionnaireId)
+        );
+
+        if (!questions.isEmpty()) {
+            // 3. 删除问题对应的所有答案
+            List<Long> questionIds = questions.stream()
+                    .map(Question::getQuestionId)
+                    .collect(Collectors.toList());
+            answerService.remove(
+                    new LambdaQueryWrapper<Answer>()
+                            .in(Answer::getQuestionId, questionIds)
+            );
+
+            // 4. 删除问题
+            questionService.remove(
+                    new LambdaQueryWrapper<Question>()
+                            .eq(Question::getQuestionnaireId, questionnaireId)
+            );
+        }
+
+        // 5. 删除问卷
+        boolean removed = questionnaireService.removeById(questionnaireId);
+        if (!removed) {
+            throw new BizException(ErrorEnum.DELETE_QUESTIONNAIRE_FAIL.getErrorCode(), ErrorEnum.DELETE_QUESTIONNAIRE_FAIL.getErrorMessage());
+        }
+    }
+
+    @Override
+    public PageRecord<QuestionnaireDTO> queryQuestionnaire(QuestionnaireQueryParam param) {
+
+        // 1. 构建分页对象
+        Page<Questionnaire> pageRequest = new Page<>(param.getPageNo(), param.getPageSize());
+
+        // 2. 构建查询条件
+        LambdaQueryWrapper<Questionnaire> queryWrapper = new LambdaQueryWrapper<>();
+
+        if (param.getTitle() != null && !param.getTitle().isEmpty()) {
+            queryWrapper.like(Questionnaire::getTitle, param.getTitle());
+        }
+
+        if (param.getEnable() != null) {
+            queryWrapper.eq(Questionnaire::getEnable, param.getEnable());
+        }
+
+        if (param.getCreateTimeStart() != null) {
+            queryWrapper.ge(Questionnaire::getCreateTime, param.getCreateTimeStart().atStartOfDay());
+        }
+
+        if (param.getCreateTimeEnd() != null) {
+            queryWrapper.le(Questionnaire::getCreateTime, param.getCreateTimeEnd().atTime(23, 59, 59));
+        }
+
+        // 按创建时间倒序,可根据业务调整
+        queryWrapper.orderByDesc(Questionnaire::getCreateTime);
+
+        // 3. 执行分页查询
+        Page<Questionnaire> pageResult = questionnaireService.page(pageRequest, queryWrapper);
+
+        // 4. 转 DTO
+        List<QuestionnaireDTO> dtoList = pageResult.getRecords().stream()
+                .map(q -> CopyUtils.copy(q, QuestionnaireDTO.class))
+                .collect(Collectors.toList());
+
+        // 5. 封装分页结果
+        PageRecord<QuestionnaireDTO> result = new PageRecord<>();
+        result.setRows(dtoList);
+        result.setTotal(pageResult.getTotal());
+        result.setPageNum((int) pageResult.getCurrent());
+        result.setPageSize((int) pageResult.getSize());
+        result.setTotalPageNum((int) pageResult.getPages());
+        result.setOutTotalPageNum(false); // 按需设置
+
+        return result;
+    }
+
+    @Override
+    public QuestionnaireDetailsDTO queryQuestionnaireDetails(Long questionnaireId) {
+        if (questionnaireId == null) {
+            throw new BizException(
+                    ErrorEnum.QUESTIONNAIRE_ID_IS_NULL.getErrorCode(), ErrorEnum.QUESTIONNAIRE_ID_IS_NULL.getErrorMessage());
+        }
+
+        // 1. 查询问卷信息
+        Questionnaire questionnaire = questionnaireService.getById(questionnaireId);
+        if (questionnaire == null) {
+            throw new BizException(
+                    ErrorEnum.QUESTIONNAIRE_NOT_EXIST.getErrorCode(), ErrorEnum.QUESTIONNAIRE_NOT_EXIST.getErrorMessage());
+        }
+
+        // 2. 查询问卷下的问题,按 sort 升序
+        List<Question> questions = questionService.list(
+                new LambdaQueryWrapper<Question>()
+                        .eq(Question::getQuestionnaireId, questionnaireId)
+                        .orderByAsc(Question::getSort)
+        );
+
+        // 3. 转换为 QuestionDTO
+        List<QuestionDTO> questionDTOList = questions.stream()
+                .map(q -> {
+                    QuestionDTO dto = new QuestionDTO();
+                    BeanUtils.copyProperties(q, dto);
+                    return dto;
+                })
+                .collect(Collectors.toList());
+
+        // 4. 封装 QuestionnaireDetailsDTO
+        QuestionnaireDetailsDTO detailsDTO = new QuestionnaireDetailsDTO();
+        detailsDTO.setQuestionnaireId(questionnaire.getQuestionnaireId());
+        detailsDTO.setTitle(questionnaire.getTitle());
+        detailsDTO.setDescription(questionnaire.getDescription());
+        detailsDTO.setQuestionList(questionDTOList);
+
+        return detailsDTO;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public List<QuestionDTO> saveQuestion(List<QuestionSaveParam> params) {
+        if (params == null || params.isEmpty()) {
+            throw new BizException(ErrorEnum.QUESTION_LIST_NOT_EXIST.getErrorCode(), ErrorEnum.QUESTION_LIST_NOT_EXIST.getErrorMessage());
+        }
+
+        // 假设前端保证同一次提交的都是同一份问卷里的问题
+        Long questionnaireId = params.get(0).getQuestionnaireId();
+        if (questionnaireId == null) {
+            throw new BizException(ErrorEnum.QUESTIONNAIRE_ID_IS_NULL.getErrorCode(), ErrorEnum.QUESTIONNAIRE_ID_IS_NULL.getErrorMessage());
+        }
+
+        // 1. 查出该问卷下现有的问题
+        List<Question> existingQuestions = questionService.list(
+                new LambdaQueryWrapper<Question>()
+                        .eq(Question::getQuestionnaireId, questionnaireId)
+        );
+        Set<Long> existingIds = existingQuestions.stream()
+                .map(Question::getQuestionId)
+                .collect(Collectors.toSet());
+
+        List<Question> toSaveOrUpdate = new ArrayList<>();
+        Set<Long> incomingIds = new HashSet<>();
+
+        // 2. 遍历入参,组装需要保存/更新的问题
+        for (QuestionSaveParam param : params) {
+            Question question;
+            if (param.getQuestionId() != null) {
+                // 编辑
+                question = questionService.getById(param.getQuestionId());
+                if (question == null) {
+                    throw new BizException(ErrorEnum.QUESTION_NOT_EXIST.getErrorCode(),
+                            ErrorEnum.QUESTION_NOT_EXIST.getErrorMessage());
+                }
+                BeanUtils.copyProperties(param, question, "questionId", "createTime");
+                question.setUpdateTime(LocalDateTime.now());
+                incomingIds.add(question.getQuestionId());
+            } else {
+                // 新增
+                question = new Question();
+                BeanUtils.copyProperties(param, question, "questionId");
+                question.setQuestionnaireId(questionnaireId);
+                question.setCreateTime(LocalDateTime.now());
+                question.setUpdateTime(LocalDateTime.now());
+            }
+            toSaveOrUpdate.add(question);
+        }
+
+        // 3. 删除数据库有但入参没传的
+        List<Long> toDeleteIds = existingIds.stream()
+                .filter(id -> !incomingIds.contains(id))
+                .collect(Collectors.toList());
+        if (!toDeleteIds.isEmpty()) {
+            // 先删答案
+            answerService.remove(new LambdaQueryWrapper<Answer>()
+                    .in(Answer::getQuestionId, toDeleteIds));
+            // 再删问题
+            questionService.removeByIds(toDeleteIds);
+        }
+
+        // 4. 保存或更新当前入参的问题
+        questionService.saveOrUpdateBatch(toSaveOrUpdate);
+
+        // 5. 返回最新问题集合
+        List<Question> latestQuestions = questionService.list(
+                new LambdaQueryWrapper<Question>()
+                        .eq(Question::getQuestionnaireId, questionnaireId)
+                        .orderByAsc(Question::getCreateTime)
+        );
+
+        return latestQuestions.stream()
+                .map(q -> CopyUtils.copy(q, QuestionDTO.class))
+                .collect(Collectors.toList());
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void saveAnswer(List<AnswerSaveParam> params) {
+
+        // 1. 校验入参是否为空
+        if (params == null || params.isEmpty()) {
+            throw new BizException(ErrorEnum.ANSWER_LIST_IS_NOT_NULL.getErrorCode(), ErrorEnum.ANSWER_LIST_IS_NOT_NULL.getErrorMessage());
+        }
+
+        // 2.遍历答案集合
+        List<Answer> answers = new ArrayList<>();
+        for (AnswerSaveParam param : params) {
+            if (param.getQuestionnaireId() == null || param.getQuestionId() == null || param.getUserId() == null) {
+                throw new BizException(ErrorEnum.QUESTIONNAIRE_QUESTION_ANSWER_NULL.getErrorCode(), ErrorEnum.QUESTIONNAIRE_QUESTION_ANSWER_NULL.getErrorMessage());
+            }
+
+            Answer answer = new Answer();
+            BeanUtils.copyProperties(param, answer); // 直接拷贝字段
+            answers.add(answer);
+        }
+
+        // 3.直接批量插入,自动填充字段
+        answerService.saveBatch(answers);
+    }
+
+    @Override
+    public PageRecord<UserQuestionnaireListDTO> queryUserQuestionnaireList(UserQuestionnaireListParam query) {
+
+        // 1. 查用户ID(如果传了手机号才查)
+        Long userId = null;
+        if (StringUtils.isNotBlank(query.getUserPhone())) {
+            UserInfo user = userService.getOne(
+                    new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getPhone, query.getUserPhone())
+            );
+            if (user == null) return emptyPage(query);
+            userId = user.getUserId();
+            log.info("用户ID: {}", userId);
+        }
+
+        // 2. 查询答案表,按时间倒序
+        LambdaQueryWrapper<Answer> wrapper = new LambdaQueryWrapper<Answer>()
+                .eq(userId != null, Answer::getUserId, userId)
+                .eq(query.getQuestionnaireId() != null, Answer::getQuestionnaireId, query.getQuestionnaireId())
+                .orderByDesc(Answer::getCreateTime);
+
+        List<Answer> allAnswers = answerService.list(wrapper);
+        log.info("答案表: {}", allAnswers);
+
+        // 3. 每个用户每份问卷只保留一条记录
+        Map<String, Answer> oneAnswerPerUserQuestionnaire = new LinkedHashMap<>();
+        for (Answer ans : allAnswers) {
+            String key = ans.getUserId() + "-" + ans.getQuestionnaireId();
+            oneAnswerPerUserQuestionnaire.putIfAbsent(key, ans);
+        }
+        log.info("去重后答案表: {}", oneAnswerPerUserQuestionnaire);
+
+        // 4. 分页处理
+        List<Answer> pageAnswers = new ArrayList<>(oneAnswerPerUserQuestionnaire.values());
+        int total = pageAnswers.size();
+        int fromIndex = (query.getPageNo() - 1) * query.getPageSize();
+        int toIndex = Math.min(fromIndex + query.getPageSize(), total);
+        if (fromIndex >= total) return emptyPage(query);
+        pageAnswers = pageAnswers.subList(fromIndex, toIndex);
+
+        // 5. 查询问卷信息
+        Set<Long> questionnaireIds = pageAnswers.stream()
+                .map(Answer::getQuestionnaireId).collect(Collectors.toSet());
+        Map<Long, Questionnaire> questionnaireMap = questionnaireService.listByIds(questionnaireIds)
+                .stream().collect(Collectors.toMap(Questionnaire::getQuestionnaireId, q -> q));
+        log.info("问卷信息: {}", questionnaireMap);
+
+        // 5.1 查询用户信息(所有答题用户)
+        Set<Long> userIds = pageAnswers.stream().map(Answer::getUserId).collect(Collectors.toSet());
+        Map<Long, UserInfo> userMap = userService.listByIds(userIds)
+                .stream().collect(Collectors.toMap(UserInfo::getUserId, u -> u));
+        log.info("用户信息: {}", userMap);
+
+        // 6. 组装 DTO
+        List<UserQuestionnaireListDTO> dtoList = pageAnswers.stream().map(ans -> {
+            UserQuestionnaireListDTO dto = new UserQuestionnaireListDTO();
+            dto.setQuestionnaireId(ans.getQuestionnaireId());
+            dto.setQuestionnaireTitle(
+                    questionnaireMap.get(ans.getQuestionnaireId()) != null
+                            ? questionnaireMap.get(ans.getQuestionnaireId()).getTitle()
+                            : null
+            );
+            UserInfo ansUser = userMap.get(ans.getUserId());
+            dto.setUserPhone(ansUser != null ? ansUser.getPhone() : null);
+            dto.setAnswerTime(ans.getCreateTime());
+            return dto;
+        }).collect(Collectors.toList());
+        log.info("DTO: {}", dtoList);
+
+        // 7. 封装分页结果
+        PageRecord<UserQuestionnaireListDTO> result = new PageRecord<>();
+        result.setRows(dtoList);
+        result.setTotal((long) total);
+        result.setPageNum(query.getPageNo());
+        result.setPageSize(query.getPageSize());
+        result.setOutTotalPageNum(true);
+        result.setTotalPageNum((total + query.getPageSize() - 1) / query.getPageSize());
+
+        return result;
+    }
+
+
+    @Override
+    public UserQuestionnaireDetailsDTO queryUserQuestionnaireDetails(UserQuestionnaireDetailsParam param) {
+        // 1. 根据手机号查用户
+        UserInfo user = userService.getOne(
+                new LambdaQueryWrapper<UserInfo>().eq(UserInfo::getPhone, param.getUserPhone())
+        );
+        if (user == null) {
+            throw new BizException(ErrorEnum.USER_IS_NOT_EXIST.getErrorCode(),ErrorEnum.USER_IS_NOT_EXIST.getErrorMessage());
+        }
+        Long userId = user.getUserId();
+        log.info("用户ID: {}", userId);
+
+        // 2. 查问卷信息
+        Questionnaire questionnaire = questionnaireService.getById(param.getQuestionnaireId());
+        if (questionnaire == null) {
+            throw new BizException(ErrorEnum.QUESTIONNAIRE_NOT_EXIST.getErrorCode(),ErrorEnum.QUESTIONNAIRE_NOT_EXIST.getErrorMessage());
+        }
+        log.info("问卷信息: {}", questionnaire);
+
+        // 3. 查该问卷下的问题
+        List<Question> questions = questionService.list(
+                new LambdaQueryWrapper<Question>()
+                        .eq(Question::getQuestionnaireId, param.getQuestionnaireId())
+                        .orderByAsc(Question::getSort)
+        );
+        log.info("问题信息: {}", questions);
+
+        // 4. 查该用户在该问卷下的答案
+        List<Answer> answers = answerService.list(
+                new LambdaQueryWrapper<Answer>()
+                        .eq(Answer::getQuestionnaireId, param.getQuestionnaireId())
+                        .eq(Answer::getUserId, userId)
+        );
+        Map<Long, String> answerMap = answers.stream()
+                .collect(Collectors.toMap(Answer::getQuestionId, Answer::getAnswer));
+        log.info("用户答案: {}", answerMap);
+
+        // 5. 组装问题集合 DTO
+        List<UserQuestionDetailsDTO> questionDTOList = questions.stream().map(q -> {
+            UserQuestionDetailsDTO dto = new UserQuestionDetailsDTO();
+            dto.setQuestionId(q.getQuestionId());
+            dto.setQuestionType(q.getQuestionType());
+            dto.setContent(q.getContent());
+            dto.setConfig(q.getConfig());
+            dto.setSort(q.getSort());
+            dto.setAnswer(answerMap.get(q.getQuestionId())); // 用户答案
+            return dto;
+        }).collect(Collectors.toList());
+        log.info("问题集合 DTO: {}", questionDTOList);
+
+        // 6. 封装最终返回 DTO
+        UserQuestionnaireDetailsDTO result = new UserQuestionnaireDetailsDTO();
+        result.setQuestionnaireId(questionnaire.getQuestionnaireId());
+        result.setTitle(questionnaire.getTitle());
+        result.setUserPhone(user.getPhone());
+        result.setUserId(userId);
+        result.setQuestionList(questionDTOList);
+
+        return result;
+    }
+
+    // 空分页方法
+    private PageRecord<UserQuestionnaireListDTO> emptyPage(UserQuestionnaireListParam query) {
+        PageRecord<UserQuestionnaireListDTO> empty = new PageRecord<>();
+        empty.setRows(Collections.emptyList());
+        empty.setTotal(0L);
+        empty.setPageNum(query.getPageNo());
+        empty.setPageSize(query.getPageSize());
+        empty.setOutTotalPageNum(true);
+        empty.setTotalPageNum((int) ((0 + query.getPageSize() - 1) / query.getPageSize())); // 计算总页数
+        return empty;
+    }
+}
+
+
+

+ 13 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/AnswerMapper.java

@@ -0,0 +1,13 @@
+package com.hfln.portal.infrastructure.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hfln.portal.infrastructure.po.Answer;
+import org.apache.ibatis.annotations.Mapper;
+
+
+/**
+ * 答案表 Mapper 接口
+ */
+@Mapper
+public interface AnswerMapper extends BaseMapper<Answer> {
+}

+ 13 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/QuestionMapper.java

@@ -0,0 +1,13 @@
+package com.hfln.portal.infrastructure.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hfln.portal.infrastructure.po.Question;
+import org.apache.ibatis.annotations.Mapper;
+
+
+/**
+ * 问题表 Mapper 接口
+ */
+@Mapper
+public interface QuestionMapper extends BaseMapper<Question> {
+}

+ 13 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/mapper/QuestionnaireMapper.java

@@ -0,0 +1,13 @@
+package com.hfln.portal.infrastructure.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hfln.portal.infrastructure.po.Questionnaire;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 问卷表 Mapper 接口
+ */
+@Mapper
+public interface QuestionnaireMapper extends BaseMapper<Questionnaire> {
+}

+ 44 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/Answer.java

@@ -0,0 +1,44 @@
+package com.hfln.portal.infrastructure.po;
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 答案表
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("answer")
+public class Answer extends BasePO {
+
+    /**
+     * 主键ID,数据库自增策略
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long answerId;
+
+    /**
+     * 所属问卷id
+     */
+    private Long questionnaireId;
+
+    /**
+     * 所属问题id
+     */
+    private Long questionId;
+
+    /**
+     * 用户id
+     *
+     */
+    private Long userId;
+
+    /**
+     * 用户答案
+     */
+    private String answer;
+}

+ 54 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/Question.java

@@ -0,0 +1,54 @@
+package com.hfln.portal.infrastructure.po;
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 问题表
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("question")
+public class Question extends BasePO{
+
+    /**
+     * 主键ID,数据库自增策略
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long questionId;
+
+    /**
+     * 所属问卷id
+     */
+    private Long questionnaireId;
+
+    /**
+     * 问题类型
+     */
+    private String questionType;
+
+    /**
+     * 问题内容
+     */
+    private String content;
+
+    /**
+     * 问题配置
+     */
+    private String config;
+
+    /**
+     * 是否必填 1-必填,0-选填
+     */
+    private Integer required;
+
+    /**
+     * 问题排序
+     */
+    private int sort;
+}
+

+ 39 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/po/Questionnaire.java

@@ -0,0 +1,39 @@
+package com.hfln.portal.infrastructure.po;
+
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 问卷表
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@TableName("questionnaire")
+public class Questionnaire extends BasePO{
+
+
+    /**
+     * 主键ID,数据库自增策略
+     */
+    @TableId(type = IdType.ASSIGN_ID)
+    private Long questionnaireId;
+
+    /**
+     * 问卷标题
+     */
+    private String title;
+
+    /**
+     * 问卷描述
+     */
+    private String description;
+
+    /**
+     * 问卷是否启用 0-禁用,1-启用
+     */
+    private Integer enable;
+}

+ 7 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/AnswerService.java

@@ -0,0 +1,7 @@
+package com.hfln.portal.infrastructure.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hfln.portal.infrastructure.po.Answer;
+
+public interface AnswerService extends IService<Answer> {
+}

+ 7 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/QuestionService.java

@@ -0,0 +1,7 @@
+package com.hfln.portal.infrastructure.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hfln.portal.infrastructure.po.Question;
+
+public interface QuestionService extends IService<Question> {
+}

+ 8 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/QuestionnaireService.java

@@ -0,0 +1,8 @@
+package com.hfln.portal.infrastructure.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hfln.portal.infrastructure.po.Questionnaire;
+
+public interface QuestionnaireService extends IService<Questionnaire> {
+
+}

+ 14 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/AnswerServiceImpl.java

@@ -0,0 +1,14 @@
+package com.hfln.portal.infrastructure.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hfln.portal.infrastructure.mapper.AnswerMapper;
+import com.hfln.portal.infrastructure.po.Answer;
+import com.hfln.portal.infrastructure.service.AnswerService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Service
+@Slf4j
+public class AnswerServiceImpl extends ServiceImpl<AnswerMapper, Answer> implements AnswerService {
+}

+ 14 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/QuestionServiceImpl.java

@@ -0,0 +1,14 @@
+package com.hfln.portal.infrastructure.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hfln.portal.infrastructure.mapper.QuestionMapper;
+import com.hfln.portal.infrastructure.po.Question;
+import com.hfln.portal.infrastructure.service.QuestionService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+
+@Service
+@Slf4j
+public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {
+}

+ 14 - 0
portal-service-infrastructure/src/main/java/com/hfln/portal/infrastructure/service/impl/QuestionnaireServiceImpl.java

@@ -0,0 +1,14 @@
+package com.hfln.portal.infrastructure.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hfln.portal.infrastructure.mapper.QuestionnaireMapper;
+import com.hfln.portal.infrastructure.po.Questionnaire;
+import com.hfln.portal.infrastructure.service.QuestionnaireService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+
+@Service
+@Slf4j
+public class QuestionnaireServiceImpl extends ServiceImpl<QuestionnaireMapper, Questionnaire> implements QuestionnaireService {
+}