ソースを参照

Merge branch 'feat-quiz' into temp-pre

shc 1 年間 前
コミット
1b0b0f9848
23 ファイル変更1655 行追加0 行削除
  1. 28 0
      db/10wm_quiz_item.sql
  2. 29 0
      db/11wm_question.sql
  3. 33 0
      db/9wm_quiz.sql
  4. 31 0
      hnqz-common/hnqz-common-core/src/main/java/com/qunzhixinxi/hnqz/common/core/constant/enums/UpmsState.java
  5. 32 0
      hnqz-common/hnqz-common-core/src/main/java/com/qunzhixinxi/hnqz/common/core/constant/enums/UpmsType.java
  6. 144 0
      hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/dto/WmQuizDTO.java
  7. 86 0
      hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/entity/WmQuestion.java
  8. 105 0
      hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/entity/WmQuiz.java
  9. 62 0
      hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/entity/WmQuizItem.java
  10. 27 0
      hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/vo/WmQuizVO.java
  11. 388 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/controller/WmQuizController.java
  12. 1 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/mapper/SysUserMapper.java
  13. 16 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/mapper/WmQuestionMapper.java
  14. 16 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/mapper/WmQuizItemMapper.java
  15. 16 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/mapper/WmQuizMapper.java
  16. 8 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/SysUserService.java
  17. 66 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/WmQuestionService.java
  18. 69 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/WmQuizItemService.java
  19. 53 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/WmQuizService.java
  20. 19 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/SysUserServiceImpl.java
  21. 147 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/WmQuestionServiceImpl.java
  22. 152 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/WmQuizItemServiceImpl.java
  23. 127 0
      hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/WmQuizServiceImpl.java

+ 28 - 0
db/10wm_quiz_item.sql

@@ -0,0 +1,28 @@
+USE `hnqzx`;
+
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- ----------------------------
+-- Table structure for wm_quiz_item
+-- ----------------------------
+DROP TABLE IF EXISTS `wm_quiz_item`;
+CREATE TABLE `wm_quiz_item`
+(
+    `item_id`     INT AUTO_INCREMENT COMMENT 'Id',
+    `quiz_sn`     VARCHAR(64)  NOT NULL COMMENT '试卷序号',
+    `label`       VARCHAR(64)  NOT NULL COMMENT '标题',
+    `options`     JSON COMMENT '选项',
+    `answer`      VARCHAR(255) NULL COMMENT '答案',
+    `widget`      BIGINT       NOT NULL COMMENT '排序权重',
+    `create_by`   VARCHAR(64)  NOT NULL COMMENT '创建人',
+    `update_by`   VARCHAR(64)  NOT NULL COMMENT '修改人',
+    `create_time` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    PRIMARY KEY `pk_id` (`item_id`) USING BTREE,
+    INDEX `idx_quiz_sn` (`quiz_sn`) USING BTREE
+) ENGINE = InnoDB
+    COMMENT ='试卷项表';
+
+
+SET FOREIGN_KEY_CHECKS = 1;

+ 29 - 0
db/11wm_question.sql

@@ -0,0 +1,29 @@
+USE `hnqzx`;
+
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- ----------------------------
+-- Table structure for wm_question
+-- ----------------------------
+DROP TABLE IF EXISTS `wm_question`;
+CREATE TABLE `wm_question`
+(
+    `qst_id`      INT AUTO_INCREMENT COMMENT 'Id',
+    `serial_num`  VARCHAR(64)  NOT NULL COMMENT '序列号',
+    `label`       VARCHAR(64)  NOT NULL COMMENT '标题',
+    `options`     JSON COMMENT '选项',
+    `answer`      VARCHAR(255) NULL COMMENT '答案',
+    `ent_id`      INT UNSIGNED NOT NULL COMMENT '企业信息',
+    `create_by`   VARCHAR(64)  NOT NULL COMMENT '创建人',
+    `update_by`   VARCHAR(64)  NOT NULL COMMENT '修改人',
+    `create_time` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    PRIMARY KEY `pk_id` (`qst_id`) USING BTREE,
+    UNIQUE KEY `uk_sn` (`serial_num`) USING BTREE,
+    INDEX `idx_eid` (`ent_id`) USING BTREE
+) ENGINE = InnoDB
+    COMMENT ='试题表';
+
+
+SET FOREIGN_KEY_CHECKS = 1;

+ 33 - 0
db/9wm_quiz.sql

@@ -0,0 +1,33 @@
+USE `hnqzx`;
+
+SET NAMES utf8mb4;
+SET FOREIGN_KEY_CHECKS = 0;
+
+-- ----------------------------
+-- Table structure for wm_quiz
+-- ----------------------------
+DROP TABLE IF EXISTS `wm_quiz`;
+CREATE TABLE `wm_quiz`
+(
+    `quiz_id`      INT AUTO_INCREMENT COMMENT 'Id',
+    `serial_num`   VARCHAR(64)  NOT NULL COMMENT '序列号',
+    `quiz_title`   VARCHAR(64)  NOT NULL COMMENT '标题',
+    `introduction` VARCHAR(255) NULL COMMENT '简介',
+    `source_type`  VARCHAR(8)   NOT NULL COMMENT '问卷来源类型',
+    `quiz_state`   VARCHAR(8)   NOT NULL COMMENT '问卷状态',
+    `ent_id`       INT UNSIGNED NOT NULL COMMENT '企业信息',
+    `quiz_expand`  JSON COMMENT '试卷统计',
+    `create_by`    VARCHAR(64)  NOT NULL COMMENT '创建人',
+    `update_by`    VARCHAR(64)  NOT NULL COMMENT '修改人',
+    `create_time`  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time`  DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    PRIMARY KEY `pk_id` (`quiz_id`) USING BTREE,
+    UNIQUE KEY `uk_sn` (`serial_num`) USING BTREE,
+    INDEX `idx_st` (`source_type`) USING BTREE,
+    INDEX `idx_state` (`quiz_state`) USING BTREE,
+    INDEX `idx_eid` (`ent_id`) USING BTREE
+) ENGINE = InnoDB
+    COMMENT ='试卷表';
+
+
+SET FOREIGN_KEY_CHECKS = 1;

+ 31 - 0
hnqz-common/hnqz-common-core/src/main/java/com/qunzhixinxi/hnqz/common/core/constant/enums/UpmsState.java

@@ -0,0 +1,31 @@
+package com.qunzhixinxi.hnqz.common.core.constant.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * UPMS 状态
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 14:34
+ */
+@NoArgsConstructor(access = AccessLevel.NONE)
+public final class UpmsState {
+
+	@Getter
+	@AllArgsConstructor
+	public enum QuizState {
+		CREATE("CREATE", "未发布"),
+		RELEASE("RELEASE", "收集中"),
+		STOP("STOP", "停止发布");
+
+
+		@EnumValue
+		private String state;
+		private String description;
+	}
+}

+ 32 - 0
hnqz-common/hnqz-common-core/src/main/java/com/qunzhixinxi/hnqz/common/core/constant/enums/UpmsType.java

@@ -0,0 +1,32 @@
+package com.qunzhixinxi.hnqz.common.core.constant.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+
+/**
+ * UPMS类型枚举
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 14:31
+ */
+@NoArgsConstructor(access = AccessLevel.NONE)
+public final class UpmsType {
+
+	@Getter
+	@AllArgsConstructor
+	public enum QuizSourceType {
+
+		BLANK("BLK", "空白创建"),
+		TEMPLATE("TMP", "模板");
+
+		@EnumValue
+		private String type;
+
+		private String description;
+
+	}
+}

+ 144 - 0
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/dto/WmQuizDTO.java

@@ -0,0 +1,144 @@
+package com.qunzhixinxi.hnqz.admin.api.dto;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuestion;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuiz;
+import com.qunzhixinxi.hnqz.common.core.constant.enums.UpmsState;
+import com.qunzhixinxi.hnqz.common.core.constant.enums.UpmsType;
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.hibernate.validator.constraints.Length;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.time.LocalDateTime;
+
+/**
+ * 试卷dto
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 16:07
+ */
+@NoArgsConstructor(access = AccessLevel.NONE)
+public final class WmQuizDTO {
+
+	@Data
+	public static class OnQuizCreate {
+
+		/**
+		 * 标题
+		 */
+		@NotBlank(message = "标题必填")
+		@Length(max = 64, message = "标题最大支持字符数{max}")
+		private String title;
+
+		/**
+		 * 简介
+		 */
+		private String introduction;
+
+		/**
+		 * 问卷来源类型
+		 */
+		@NotNull(message = "问卷类型必填")
+		private UpmsType.QuizSourceType sourceType;
+
+	}
+
+	@Data
+	public static class OnQuizUpdate {
+
+	}
+
+	@Data
+	public static class OnQuizState {
+
+		@NotNull(message = "试卷ID必填")
+		private Integer quizId;
+
+		@NotNull(message = "操作状态必填")
+		private UpmsState.QuizState state;
+	}
+
+
+	@Data
+	public static class OnItemCreate {
+
+		@NotBlank(message = "标题必填")
+		private String label;
+
+		@NotBlank(message = "试卷编号")
+		private String quizSerialNum;
+
+		@NotEmpty(message = "选项不能为空")
+		private WmQuestion.Option[] options;
+
+		@NotBlank(message = "答案必填")
+		private String answer;
+
+		@NotNull(message = "排序权重必填")
+		private Long widget;
+	}
+
+	@Data
+	public static class OnItemUpdate {
+
+		@NotNull(message = "试卷项ID必填")
+		private Integer itemId;
+
+		private String label;
+
+		private WmQuestion.Option[] options;
+
+		private String answer;
+
+		private Long widget;
+	}
+
+	@Data
+	public static class OnItemDelete {
+
+		@NotNull(message = "试卷项ID必填")
+		private Integer itemId;
+	}
+
+
+	@Data
+	public static class OnQuestionCreate {
+		@NotBlank(message = "试题标题必填")
+		@Length(max = 64, message = "试卷标题最多{max}个字符")
+		private String label;
+
+		@Valid
+		@NotEmpty(message = "试题选项必填")
+		private WmQuestion.Option[] options;
+
+		@NotBlank(message = "试题答案必填")
+		@Length(max = 255, message = "试卷标题最多{max}个字符")
+		private String answer;
+
+	}
+
+	@Data
+	public static class OnQuestionUpdate {
+
+		@NotNull(message = "试题ID必填")
+		private Integer questionId;
+
+		private String label;
+
+		private WmQuestion.Option[] options;
+
+		private String answer;
+
+	}
+
+}

+ 86 - 0
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/entity/WmQuestion.java

@@ -0,0 +1,86 @@
+package com.qunzhixinxi.hnqz.admin.api.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.time.LocalDateTime;
+
+/**
+ * 试题实体
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 14:58
+ */
+@Data
+@TableName(value = "wm_question", autoResultMap = true)
+public final class WmQuestion {
+
+	@TableId(value = "qst_id", type = IdType.AUTO)
+	private Integer questionId;
+
+	@TableField(value = "serial_num")
+	private String serialNumber;
+
+	private String label;
+
+	private Option[] options;
+
+	private String answer;
+
+	@TableField(value = "ent_id")
+	private Integer enterpriseId;
+
+	/**
+	 * 创建人
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private String createBy;
+
+	/**
+	 * 修改人
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private String updateBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private LocalDateTime createTime;
+
+	/**
+	 * 修改时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private LocalDateTime updateTime;
+
+	/**
+	 * 选项实体
+	 *
+	 * @author jimmy
+	 * @version 1.0.0
+	 * @date 2023-10-16 15:13
+	 */
+	@Data
+	public static class Option {
+
+		/**
+		 * 选项编号
+		 */
+		@NotBlank(message = "选项编号必填")
+		private String no;
+
+		/**
+		 * 选项文本
+		 */
+		@NotBlank(message = "选项文本必填")
+		private String text;
+
+	}
+}

+ 105 - 0
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/entity/WmQuiz.java

@@ -0,0 +1,105 @@
+package com.qunzhixinxi.hnqz.admin.api.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import com.qunzhixinxi.hnqz.common.core.constant.enums.UpmsState;
+import com.qunzhixinxi.hnqz.common.core.constant.enums.UpmsType;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 试卷实体
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 14:20
+ */
+@Data
+@TableName(value = "wm_quiz", autoResultMap = true)
+public final class WmQuiz {
+
+	/**
+	 * 试卷ID
+	 */
+	@TableId(value = "quiz_id", type = IdType.AUTO)
+	private Integer quizId;
+
+	/**
+	 * 问卷序列号
+	 */
+	@TableField(value = "serial_num")
+	private String serialNumber;
+
+	/**
+	 * 标题
+	 */
+	@TableField(value = "quiz_title")
+	private String title;
+
+	/**
+	 * 简介
+	 */
+	private String introduction;
+
+	/**
+	 * 问卷来源类型
+	 */
+	private UpmsType.QuizSourceType sourceType;
+
+	/**
+	 * 问卷状态
+	 */
+	@TableField(value = "quiz_state")
+	private UpmsState.QuizState state;
+
+	/**
+	 * 企业id
+	 */
+	@TableField(value = "ent_id")
+	private Integer enterpriseId;
+
+	@TableField(value = "quiz_expand", typeHandler = JacksonTypeHandler.class)
+	private Expand expand;
+
+	/**
+	 * 创建人
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private String createBy;
+
+	/**
+	 * 修改人
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private String updateBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private LocalDateTime createTime;
+
+	/**
+	 * 修改时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private LocalDateTime updateTime;
+
+	@Data
+	public static class Expand {
+
+		private Integer itemQty;
+
+		private Integer totalScore;
+
+		private Integer passingScore;
+
+	}
+
+
+}

+ 62 - 0
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/entity/WmQuizItem.java

@@ -0,0 +1,62 @@
+package com.qunzhixinxi.hnqz.admin.api.entity;
+
+import com.baomidou.mybatisplus.annotation.FieldFill;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 试卷项
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 14:58
+ */
+@Data
+@TableName(value = "wm_quiz_item", autoResultMap = true)
+public final class WmQuizItem {
+
+	@TableId(value = "item_id", type = IdType.AUTO)
+	private Integer itemId;
+
+	@TableField(value = "quiz_sn")
+	private String quizSerialNumber;
+
+	private String label;
+
+	@TableField(typeHandler = JacksonTypeHandler.class)
+	private WmQuestion.Option[] options;
+
+	private String answer;
+
+	private Long widget;
+
+	/**
+	 * 创建人
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private String createBy;
+
+	/**
+	 * 修改人
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private String updateBy;
+
+	/**
+	 * 创建时间
+	 */
+	@TableField(fill = FieldFill.INSERT)
+	private LocalDateTime createTime;
+
+	/**
+	 * 修改时间
+	 */
+	@TableField(fill = FieldFill.INSERT_UPDATE)
+	private LocalDateTime updateTime;
+}

+ 27 - 0
hnqz-upms/hnqz-upms-api/src/main/java/com/qunzhixinxi/hnqz/admin/api/vo/WmQuizVO.java

@@ -0,0 +1,27 @@
+package com.qunzhixinxi.hnqz.admin.api.vo;
+
+import lombok.AccessLevel;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+/**
+ * 试卷模块vo
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 17:01
+ */
+@NoArgsConstructor(access = AccessLevel.NONE)
+public final class WmQuizVO {
+
+	@Data
+	public static class ToQuestionPageVO{
+		private Integer itemId;
+		private String serialNum;
+		private String label;
+		private LocalDateTime createTime;
+		private String createBy;
+	}
+}

+ 388 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/controller/WmQuizController.java

@@ -0,0 +1,388 @@
+package com.qunzhixinxi.hnqz.admin.controller;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.qunzhixinxi.hnqz.admin.api.dto.WmQuizDTO;
+import com.qunzhixinxi.hnqz.admin.api.entity.SysUser;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuestion;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuiz;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuizItem;
+import com.qunzhixinxi.hnqz.admin.api.vo.WmQuizVO;
+import com.qunzhixinxi.hnqz.admin.service.SysUserService;
+import com.qunzhixinxi.hnqz.admin.service.WmQuestionService;
+import com.qunzhixinxi.hnqz.admin.service.WmQuizItemService;
+import com.qunzhixinxi.hnqz.admin.service.WmQuizService;
+import com.qunzhixinxi.hnqz.common.core.constant.enums.UpmsState;
+import com.qunzhixinxi.hnqz.common.core.exception.BizException;
+import com.qunzhixinxi.hnqz.common.core.util.R;
+import com.qunzhixinxi.hnqz.common.log.annotation.SysLog;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+import com.qunzhixinxi.hnqz.common.security.util.SecurityUtils;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * 试卷前端控制器
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 16:03
+ */
+@Slf4j
+@RestController
+@AllArgsConstructor
+public class WmQuizController {
+
+	private final SysUserService userService;
+
+	private final WmQuizService quizService;
+	private final WmQuizItemService quizItemService;
+	private final WmQuestionService questionService;
+
+	// ~ QUIZ =========
+
+	/**
+	 * 获取试卷详情
+	 *
+	 * @param quizId 试卷ID
+	 * @return 详情信息
+	 */
+	@GetMapping(value = "/quiz/details")
+	public R<Map<String, Object>> getQuizDetails(@RequestParam(value = "quizId") Integer quizId) {
+
+		Map<String, Object> details = quizService.getDetails(quizId);
+
+		return R.ok(details);
+	}
+
+	/**
+	 * 获取试卷分页
+	 *
+	 * @param current    当前页
+	 * @param size       每页记录数
+	 * @param title      试卷标题
+	 * @param createTime 创建时间范围
+	 * @return 分页结果
+	 */
+	@GetMapping(value = "/quiz/page")
+	public R<Page<?>> pageQuizzes(@RequestParam(value = "current", defaultValue = "1") Integer current,
+								  @RequestParam(value = "size", defaultValue = "20") Integer size,
+								  @RequestParam(value = "title", required = false) String title,
+								  @RequestParam(value = "state", required = false) UpmsState.QuizState state,
+								  @RequestParam(value = "createTime", required = false) LocalDateTime[] createTime) {
+
+		LambdaQueryWrapper<WmQuiz> queryWrapper = Wrappers.<WmQuiz>lambdaQuery()
+				.like(StrUtil.isNotBlank(title), WmQuiz::getTitle, title)
+				.eq(state != null, WmQuiz::getState, state)
+				.eq(WmQuiz::getEnterpriseId, SecurityUtils.getUser().getDeptId())
+				.orderByDesc(WmQuiz::getCreateTime);
+
+		if (ArrayUtil.isNotEmpty(createTime)) {
+			LocalDateTime end;
+			if (createTime.length == 1) {
+				end = LocalDateTime.now();
+			} else {
+				end = createTime[1];
+			}
+			queryWrapper.between(WmQuiz::getCreateTime, createTime[0], end);
+		}
+
+
+		Page<WmQuiz> pages = quizService.page(new Page<>(current, size), queryWrapper);
+
+		return R.ok(pages);
+	}
+
+	/**
+	 * 获取可用的试卷
+	 *
+	 * @return 可用试卷列表
+	 */
+	@GetMapping(value = "/quiz/avail/list")
+	public R<List<WmQuiz>> listQuizzes() {
+
+		List<WmQuiz> quizzes = quizService.list(Wrappers.<WmQuiz>lambdaQuery().eq(WmQuiz::getEnterpriseId, SecurityUtils.getUser().getDeptId()).eq(WmQuiz::getState, UpmsState.QuizState.RELEASE));
+
+		return R.ok(quizzes);
+	}
+
+	/**
+	 * 发布试卷
+	 *
+	 * @param resource 发布试卷
+	 * @return 发布结果
+	 */
+	@SysLog("发布试卷")
+	@PostMapping(value = "/quiz/state/release")
+	public R<Boolean> releaseQuiz(@Valid @RequestBody WmQuizDTO.OnQuizState resource) {
+
+		if (!UpmsState.QuizState.RELEASE.equals(resource.getState())) {
+			log.error("发布请求参数封装错误");
+			throw new BizException("发布请求参数封装错误");
+		}
+
+		Boolean succ = quizService.releaseQuiz(resource.getQuizId(), SecurityUtils.getUser());
+		return succ ? R.ok(true, "发布试卷成功") : R.failed(false, "发布试卷失败");
+	}
+
+	/**
+	 * 创建试卷
+	 *
+	 * @param resource 试卷信息
+	 * @return 创建结果
+	 */
+	@SysLog(value = "创建试卷")
+	@PostMapping(value = "/quiz/create")
+	public R<String> createQuiz(@Valid @RequestBody WmQuizDTO.OnQuizCreate resource) {
+		String sn = quizService.createQuiz(resource, SecurityUtils.getUser());
+		return StrUtil.isNotBlank(sn) ? R.ok(sn, "创建试卷成功") : R.failed("", "创建试卷失败");
+	}
+
+	/**
+	 * 更新试卷
+	 *
+	 * @param resource 试卷信息
+	 * @return 创建结果
+	 */
+	@SysLog("更新试卷")
+	@PostMapping(value = "/quiz/update")
+	public R<Boolean> updateQuiz(@Valid @RequestBody WmQuizDTO.OnQuizUpdate resource) {
+
+		return R.ok();
+	}
+
+	/**
+	 * 中止试卷
+	 *
+	 * @param resource 试卷信息
+	 * @return 中止结果
+	 */
+	@SysLog("中止试卷")
+	@PostMapping(value = "/quiz/state/stop")
+	public R<Boolean> stopQuiz(@Valid @RequestBody WmQuizDTO.OnQuizState resource) {
+
+		if (!UpmsState.QuizState.STOP.equals(resource.getState())) {
+			log.error("中止请求参数封装错误");
+			throw new BizException("中止请求参数封装错误");
+		}
+
+		Boolean succ = quizService.stopQuiz(resource.getQuizId(), SecurityUtils.getUser());
+		return succ ? R.ok(true, "发布试卷成功") : R.failed(false, "发布试卷失败");
+
+	}
+
+
+	// ~ QUIZ ITEM =========
+
+	/**
+	 * 获取试卷项信息
+	 *
+	 * @param itemId 试卷项ID
+	 * @return 试卷项详情
+	 */
+	@GetMapping(value = "/quiz/item/details")
+	public R<WmQuizItem> getItemDetails(@RequestParam(value = "itemId") Integer itemId) {
+		WmQuizItem item = quizItemService.getItemDetails(itemId);
+		return R.ok(item);
+	}
+
+	/**
+	 * 获取试卷的试卷项列表
+	 *
+	 * @param sn 试卷编号
+	 * @return 试卷项链表
+	 */
+	@GetMapping(value = "/quiz/item/list")
+	public R<List<WmQuizItem>> listItems(@RequestParam(value = "quizSerialNum") String sn) {
+		List<WmQuizItem> items = quizItemService.listItems(sn);
+		return R.ok(items);
+	}
+
+	/**
+	 * 创建试卷项(添加试题)
+	 *
+	 * @param resource 试卷项信息
+	 * @return 创建结果
+	 */
+	@SysLog("创建试卷项(添加试题)")
+	@PostMapping(value = "/quiz/item/create")
+	public R<Boolean> createQuizItem(@Valid @RequestBody WmQuizDTO.OnItemCreate resource) {
+
+		Boolean succ = quizItemService.createItem(resource, SecurityUtils.getUser());
+
+		return succ ? R.ok(true, "添加试卷项成功") : R.failed(false, "添加试卷项失败");
+	}
+
+	/**
+	 * 批量创建试卷项(添加试题)
+	 *
+	 * @param resources 试卷项信息
+	 * @return 创建结果
+	 */
+	@SysLog("批量创建试卷项(添加试题)")
+	@PostMapping(value = "/quiz/item/batch/create")
+	public R<Boolean> createQuizItems(@Valid @RequestBody List<WmQuizDTO.OnItemCreate> resources) {
+
+		Boolean succ = quizItemService.createItems(resources, SecurityUtils.getUser());
+
+		return succ ? R.ok(true, "批量添加试卷项成功") : R.failed(false, "批量添加试卷项失败");
+	}
+
+	/**
+	 * 更新试卷项
+	 *
+	 * @param resource 试卷项信息
+	 * @return 更新结果
+	 */
+	@SysLog("更新试卷项")
+	@PostMapping(value = "/quiz/item/update")
+	public R<Boolean> updateQuizItem(@Valid @RequestBody WmQuizDTO.OnItemUpdate resource) {
+
+		Boolean succ = quizItemService.updateItem(resource, SecurityUtils.getUser());
+
+		return succ ? R.ok(true, "更新试卷项成功") : R.failed(false, "更新试卷项失败");
+	}
+
+	@SysLog("删除试卷项")
+	@PostMapping(value = "/quiz/item/del")
+	public R<Boolean> delQuizItem(@Valid @RequestBody WmQuizDTO.OnItemDelete resource) {
+
+		Boolean deleted = quizItemService.delQuizItem(resource.getItemId());
+
+		return deleted ? R.ok(true, "删除试卷项成功") : R.failed(false, "删除试卷项失败");
+	}
+
+	// ~ QUESTION =========
+
+	/**
+	 * 获取试题详情
+	 *
+	 * @param questionId 试题ID
+	 * @return 详情
+	 */
+	@GetMapping(value = "/quiz/question/details")
+	public R<WmQuestion> getQuestionDetails(@RequestParam(value = "questionId", required = false) Integer questionId,
+											@RequestParam(value = "sn", required = false) String sn) {
+
+		if (questionId == null && StrUtil.isBlank(sn)) {
+			log.error("试题编号或者ID缺失");
+			throw new BizException("试题编号或者ID缺失");
+		}
+
+		WmQuestion item = questionService.getQuestionDetails(questionId, sn);
+		return R.ok(item);
+
+	}
+
+	/**
+	 * 获取试题分页
+	 *
+	 * @param current 当前页
+	 * @param size    每页记录数
+	 * @param label   试题标题
+	 * @return 分页结果
+	 */
+	@GetMapping(value = "/quiz/question/page")
+	public R<Page<?>> pageQuestions(@RequestParam(value = "current", defaultValue = "1") Integer current,
+									@RequestParam(value = "size", defaultValue = "20") Integer size,
+									@RequestParam(value = "label", required = false) String label) {
+		// 获取试题信息
+		Page<WmQuestion> itemPage = questionService.pageQuestions(current, size, label);
+
+		List<WmQuestion> records = itemPage.getRecords();
+
+		// 不存在试题的情况
+		if (CollUtil.isEmpty(records)) {
+			return R.ok(itemPage);
+		}
+		// 封装包含创建人的实体信息
+		else {
+			Set<String> usernames = records.stream().map(WmQuestion::getCreateBy).collect(Collectors.toSet());
+
+			Map<String, List<SysUser>> users = userService.mapUsersByUsername(usernames);
+
+			List<WmQuizVO.ToQuestionPageVO> vos = records.stream().map(item -> {
+				WmQuizVO.ToQuestionPageVO vo = new WmQuizVO.ToQuestionPageVO();
+				vo.setItemId(item.getQuestionId());
+				vo.setSerialNum(item.getSerialNumber());
+				vo.setLabel(item.getLabel());
+				vo.setCreateTime(item.getCreateTime());
+				SysUser sysUser = users.get(item.getCreateBy()).get(0);
+				vo.setCreateBy(sysUser == null ? "" : sysUser.getRealname());
+				return vo;
+			}).collect(Collectors.toList());
+
+			Page<WmQuizVO.ToQuestionPageVO> pages = new Page<>();
+			pages.setCurrent(current);
+			pages.setSize(size);
+			pages.setPages(itemPage.getPages());
+			pages.setRecords(vos);
+			pages.setTotal(itemPage.getTotal());
+
+			return R.ok(pages);
+		}
+
+	}
+
+	/**
+	 * 创建试题
+	 *
+	 * @param resource 试卷项内容
+	 * @return 创建结果
+	 */
+	@SysLog("创建试卷项")
+	@PostMapping(value = "/quiz/question/create")
+	public R<Boolean> createQuestion(@Valid @RequestBody WmQuizDTO.OnQuestionCreate resource) {
+
+		HnqzUser operator = SecurityUtils.getUser();
+		Boolean passed = questionService.checkQuestion(operator, resource.getLabel(), null);
+
+		if (!passed) {
+			String errorMsg = String.format("存在标题重复的试题,[%s]", resource.getLabel());
+			log.error(errorMsg);
+			throw new BizException(errorMsg);
+		}
+
+		Boolean succ = questionService.createQuestion(resource, operator);
+		return succ ? R.ok(succ, "添加试题成功") : R.failed(succ, "添加试题失败");
+	}
+
+	/**
+	 * 更新试卷项
+	 *
+	 * @param resource 试卷项
+	 * @return 更新结果
+	 */
+	@SysLog("更新试卷项")
+	@PostMapping(value = "/quiz/question/update")
+	public R<Boolean> updateQuizItem(@Valid @RequestBody WmQuizDTO.OnQuestionUpdate resource) {
+
+		HnqzUser operator = SecurityUtils.getUser();
+		Boolean passed = questionService.checkQuestion(operator, resource.getLabel(), resource.getQuestionId());
+		if (!passed) {
+			String errorMsg = String.format("存在标题重复的试题,[%s]", resource.getLabel());
+			log.error(errorMsg);
+			throw new BizException(errorMsg);
+		}
+		Boolean succ = questionService.updateQuestion(resource, operator);
+		return succ ? R.ok(true, "更新试题成功") : R.failed(false, "更新试题失败");
+
+	}
+
+}

+ 1 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/mapper/SysUserMapper.java

@@ -31,6 +31,7 @@ import org.apache.ibatis.annotations.Mapper;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * <p>

+ 16 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/mapper/WmQuestionMapper.java

@@ -0,0 +1,16 @@
+package com.qunzhixinxi.hnqz.admin.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuestion;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 试卷项Mapper
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 15:49
+ */
+@Mapper
+public interface WmQuestionMapper extends BaseMapper<WmQuestion> {
+}

+ 16 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/mapper/WmQuizItemMapper.java

@@ -0,0 +1,16 @@
+package com.qunzhixinxi.hnqz.admin.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuizItem;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 试卷项Mapper
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 22:29
+ */
+@Mapper
+public interface WmQuizItemMapper extends BaseMapper<WmQuizItem> {
+}

+ 16 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/mapper/WmQuizMapper.java

@@ -0,0 +1,16 @@
+package com.qunzhixinxi.hnqz.admin.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuiz;
+import org.apache.ibatis.annotations.Mapper;
+
+/**
+ * 试卷Mapper
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 15:51
+ */
+@Mapper
+public interface WmQuizMapper extends BaseMapper<WmQuiz> {
+}

+ 8 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/SysUserService.java

@@ -162,4 +162,12 @@ public interface SysUserService extends IService<SysUser> {
 	 * @return 结果
 	 */
 	Map<String, String> checkSixtyYearsOld(UserVO userVO);
+
+	/**
+	 * 根据用户名获取用户信息
+	 *
+	 * @param usernames 用户名
+	 * @return 用户信息map
+	 */
+	Map<String, List<SysUser>> mapUsersByUsername(Collection<String> usernames);
 }

+ 66 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/WmQuestionService.java

@@ -0,0 +1,66 @@
+package com.qunzhixinxi.hnqz.admin.service;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qunzhixinxi.hnqz.admin.api.dto.WmQuizDTO;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuestion;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+
+/**
+ * 试卷项服务
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 15:59
+ */
+public interface WmQuestionService extends IService<WmQuestion> {
+
+	/**
+	 * 校验试卷项
+	 *
+	 * @param operator 操作人
+	 * @param label    标题
+	 * @param itemId   试卷项ID
+	 * @return 校验结果
+	 */
+	Boolean checkQuestion(HnqzUser operator, String label, Integer itemId);
+
+	/**
+	 * 创建试题
+	 *
+	 * @param resource 试题信息
+	 * @param operator 操作人
+	 * @return 创建结果
+	 */
+	Boolean createQuestion(WmQuizDTO.OnQuestionCreate resource, HnqzUser operator);
+
+	/**
+	 * 创建试题
+	 *
+	 * @param resource 试题信息
+	 * @param operator 操作人
+	 * @return 创建结果
+	 */
+	Boolean updateQuestion(WmQuizDTO.OnQuestionUpdate resource, HnqzUser operator);
+
+	/**
+	 * 获取试题分页
+	 *
+	 * @param current 当前页
+	 * @param size    每页记录数
+	 * @param label   标题
+	 * @return 分页结果
+	 */
+	Page<WmQuestion> pageQuestions(Integer current, Integer size, String label);
+
+	/**
+	 * 获取试题详情
+	 *
+	 * @param itemId 试题ID
+	 * @param sn     试题唯一序列号
+	 * @return 试题信息
+	 */
+	WmQuestion getQuestionDetails(Integer itemId, String sn);
+
+
+}

+ 69 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/WmQuizItemService.java

@@ -0,0 +1,69 @@
+package com.qunzhixinxi.hnqz.admin.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qunzhixinxi.hnqz.admin.api.dto.WmQuizDTO;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuizItem;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+
+import java.util.List;
+
+/**
+ * 试卷项服务
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 22:30
+ */
+public interface WmQuizItemService extends IService<WmQuizItem> {
+
+	/**
+	 * 删除试卷项
+	 *
+	 * @param itemId 试卷项ID
+	 * @return 删除结果
+	 */
+	Boolean delQuizItem(Integer itemId);
+
+	/**
+	 * 创建试卷项
+	 *
+	 * @param resource 试卷项信息
+	 * @param user     操作人
+	 * @return 创建结果
+	 */
+	Boolean createItem(WmQuizDTO.OnItemCreate resource, HnqzUser user);
+
+	/**
+	 * 批量创建试卷项
+	 *
+	 * @param resources 试卷项集合
+	 * @param user      操作人
+	 * @return 创建结果
+	 */
+	Boolean createItems(List<WmQuizDTO.OnItemCreate> resources, HnqzUser user);
+
+	/**
+	 * 更新试卷项
+	 *
+	 * @param resource 试卷信息
+	 * @param user     操作人
+	 * @return 更新结果
+	 */
+	Boolean updateItem(WmQuizDTO.OnItemUpdate resource, HnqzUser user);
+
+	/**
+	 * 获取试卷项详情
+	 *
+	 * @param itemId 试卷项ID
+	 * @return 试卷项详情
+	 */
+	WmQuizItem getItemDetails(Integer itemId);
+
+	/**
+	 * 获取试卷的试卷项
+	 *
+	 * @param sn 试卷编号
+	 * @return 试卷项集合
+	 */
+	List<WmQuizItem> listItems(String sn);
+}

+ 53 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/WmQuizService.java

@@ -0,0 +1,53 @@
+package com.qunzhixinxi.hnqz.admin.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qunzhixinxi.hnqz.admin.api.dto.WmQuizDTO;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuiz;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+
+import java.util.Map;
+
+/**
+ * 试卷服务
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 15:54
+ */
+public interface WmQuizService extends IService<WmQuiz> {
+
+	/**
+	 * 获取试卷详情
+	 *
+	 * @param quizId 试卷ID
+	 * @return 试卷详情
+	 */
+	Map<String, Object> getDetails(Integer quizId);
+
+	/**
+	 * 发布试题
+	 *
+	 * @param quizId   试题ID
+	 * @param operator 操作人
+	 * @return 发布结果
+	 */
+	Boolean releaseQuiz(Integer quizId, HnqzUser operator);
+
+	/**
+	 * 中止试卷
+	 *
+	 * @param quizId   试题ID
+	 * @param operator 操作人
+	 * @return 发布结果
+	 */
+	Boolean stopQuiz(Integer quizId, HnqzUser operator);
+
+	/**
+	 * 创建试卷
+	 *
+	 * @param resource 试卷信息
+	 * @param user     操作人
+	 * @return 试卷编号
+	 */
+	String createQuiz(WmQuizDTO.OnQuizCreate resource, HnqzUser user);
+}

+ 19 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/SysUserServiceImpl.java

@@ -23,6 +23,7 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.date.DatePattern;
 import cn.hutool.core.lang.Validator;
+import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.IdcardUtil;
 import cn.hutool.core.util.StrUtil;
@@ -273,6 +274,24 @@ public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> impl
 		return result;
 	}
 
+	/**
+	 * 根据用户名获取用户信息
+	 *
+	 * @param usernames 用户名
+	 * @return 用户信息map
+	 */
+	@Override
+	public Map<String, List<SysUser>> mapUsersByUsername(Collection<String> usernames) {
+
+		if (CollUtil.isEmpty(usernames)){
+			return Collections.emptyMap();
+		}
+
+		List<SysUser> sysUsers = this.list(Wrappers.<SysUser>lambdaQuery().in(SysUser::getUsername, usernames));
+
+		return sysUsers.stream().collect(Collectors.groupingBy(SysUser::getUsername));
+	}
+
 	/**
 	 * 封装错误信息
 	 *

+ 147 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/WmQuestionServiceImpl.java

@@ -0,0 +1,147 @@
+package com.qunzhixinxi.hnqz.admin.service.impl;
+
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qunzhixinxi.hnqz.admin.api.dto.WmQuizDTO;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuestion;
+import com.qunzhixinxi.hnqz.admin.mapper.WmQuestionMapper;
+import com.qunzhixinxi.hnqz.admin.service.WmQuestionService;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+
+/**
+ * 试题服务实现
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 16:01
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class WmQuestionServiceImpl extends ServiceImpl<WmQuestionMapper, WmQuestion> implements WmQuestionService {
+
+	/**
+	 * 校验试题
+	 *
+	 * @param operator   操作人
+	 * @param label      标题
+	 * @param questionId 试题ID
+	 * @return 校验结果
+	 */
+	@Override
+	public Boolean checkQuestion(HnqzUser operator, String label, Integer questionId) {
+
+		int count = this.count(Wrappers.<WmQuestion>lambdaQuery()
+				.eq(WmQuestion::getLabel, label)
+				.eq(WmQuestion::getEnterpriseId, operator.getDeptId())
+				.ne(questionId != null, WmQuestion::getQuestionId, questionId)
+		);
+
+		return count == 0;
+	}
+
+	/**
+	 * 创建试题
+	 *
+	 * @param resource 试题信息
+	 * @param operator 操作人
+	 * @return 创建结果
+	 */
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public Boolean createQuestion(WmQuizDTO.OnQuestionCreate resource, HnqzUser operator) {
+
+		WmQuestion question = new WmQuestion();
+		question.setSerialNumber(IdUtil.fastSimpleUUID());
+		question.setLabel(resource.getLabel());
+		question.setOptions(resource.getOptions());
+		question.setAnswer(resource.getAnswer());
+		question.setEnterpriseId(operator.getDeptId());
+		String username = operator.getUsername();
+		question.setCreateBy(username);
+		question.setUpdateBy(username);
+		LocalDateTime now = LocalDateTime.now();
+		question.setCreateTime(now);
+		question.setUpdateTime(now);
+
+		return this.save(question);
+	}
+
+	/**
+	 * 创建试题
+	 *
+	 * @param resource 试题信息
+	 * @param operator 操作人
+	 * @return 创建结果
+	 */
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public Boolean updateQuestion(WmQuizDTO.OnQuestionUpdate resource, HnqzUser operator) {
+
+		WmQuestion question = new WmQuestion();
+		question.setQuestionId(resource.getQuestionId());
+		question.setUpdateBy(operator.getUsername());
+		question.setUpdateTime(LocalDateTime.now());
+
+		if (StrUtil.isNotBlank(resource.getLabel())) {
+			question.setLabel(resource.getLabel());
+		}
+
+		if (ArrayUtil.isNotEmpty(resource.getOptions())) {
+			question.setOptions(resource.getOptions());
+		}
+
+		if (StrUtil.isNotBlank(resource.getAnswer())) {
+			question.setAnswer(resource.getAnswer());
+		}
+
+		return this.updateById(question);
+	}
+
+	/**
+	 * 获取试题分页
+	 *
+	 * @param current 当前页
+	 * @param size    每页记录数
+	 * @param label   标题
+	 * @return 分页结果
+	 */
+	@Override
+	public Page<WmQuestion> pageQuestions(Integer current, Integer size, String label) {
+
+		return this.page(new Page<>(current, size),
+				Wrappers.<WmQuestion>lambdaQuery()
+						.like(StrUtil.isNotBlank(label), WmQuestion::getLabel, label)
+						.orderByDesc(WmQuestion::getUpdateTime)
+		);
+	}
+
+	/**
+	 * 获取试题详情
+	 *
+	 * @param questionId 试题ID
+	 * @param sn         试题唯一序列号
+	 * @return 试题信息
+	 */
+	@Override
+	public WmQuestion getQuestionDetails(Integer questionId, String sn) {
+
+		if (questionId != null) {
+			return this.getById(questionId);
+		} else if (StrUtil.isNotBlank(sn)) {
+			return this.getOne(Wrappers.<WmQuestion>lambdaQuery().eq(WmQuestion::getSerialNumber, sn));
+		} else {
+			return null;
+		}
+	}
+}

+ 152 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/WmQuizItemServiceImpl.java

@@ -0,0 +1,152 @@
+package com.qunzhixinxi.hnqz.admin.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.IdUtil;
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qunzhixinxi.hnqz.admin.api.dto.WmQuizDTO;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuizItem;
+import com.qunzhixinxi.hnqz.admin.mapper.WmQuizItemMapper;
+import com.qunzhixinxi.hnqz.admin.service.WmQuizItemService;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 试卷项服务实现
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 22:31
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class WmQuizItemServiceImpl extends ServiceImpl<WmQuizItemMapper, WmQuizItem> implements WmQuizItemService {
+
+	/**
+	 * 删除试卷项
+	 *
+	 * @param itemId 试卷项ID
+	 * @return 删除结果
+	 */
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public Boolean delQuizItem(Integer itemId) {
+		return this.removeById(itemId);
+	}
+
+	/**
+	 * 创建试卷项
+	 *
+	 * @param resource 试卷项信息
+	 * @param user     操作人
+	 * @return 创建结果
+	 */
+	@Override
+	public Boolean createItem(WmQuizDTO.OnItemCreate resource, HnqzUser user) {
+		return this.createItems(Collections.singletonList(resource), user);
+	}
+
+	/**
+	 * 批量创建试卷项
+	 *
+	 * @param resources 试卷项集合
+	 * @param user      操作人
+	 * @return 创建结果
+	 */
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public Boolean createItems(List<WmQuizDTO.OnItemCreate> resources, HnqzUser user) {
+
+		List<WmQuizItem> quizItems = resources.stream().map(item -> {
+			WmQuizItem quizItem = new WmQuizItem();
+			quizItem.setQuizSerialNumber(item.getQuizSerialNum());
+			quizItem.setLabel(item.getLabel());
+			quizItem.setOptions(item.getOptions());
+			quizItem.setAnswer(item.getAnswer());
+			quizItem.setWidget(item.getWidget());
+			String username = user.getUsername();
+			quizItem.setCreateBy(username);
+			quizItem.setUpdateBy(username);
+			LocalDateTime now = LocalDateTime.now();
+			quizItem.setCreateTime(now);
+			quizItem.setUpdateTime(now);
+
+			return quizItem;
+		}).collect(Collectors.toList());
+
+
+		return this.saveBatch(quizItems);
+	}
+
+	/**
+	 * 更新试卷项
+	 *
+	 * @param resource 试卷信息
+	 * @param user     操作人
+	 * @return 更新结果
+	 */
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public Boolean updateItem(WmQuizDTO.OnItemUpdate resource, HnqzUser user) {
+
+		WmQuizItem quizItem = new WmQuizItem();
+		quizItem.setItemId(resource.getItemId());
+		quizItem.setUpdateBy(user.getUsername());
+		quizItem.setUpdateTime(LocalDateTime.now());
+
+		if (StrUtil.isNotBlank(resource.getLabel())) {
+			quizItem.setLabel(resource.getLabel());
+		}
+
+		if (ArrayUtil.isNotEmpty(resource.getOptions())) {
+			quizItem.setOptions(resource.getOptions());
+		}
+
+		if (StrUtil.isNotBlank(resource.getAnswer())) {
+			quizItem.setAnswer(resource.getAnswer());
+		}
+
+		if (resource.getWidget() != null) {
+			quizItem.setWidget(resource.getWidget());
+		}
+
+
+		return this.updateById(quizItem);
+	}
+
+	/**
+	 * 获取试卷项详情
+	 *
+	 * @param itemId 试卷项ID
+	 * @return 试卷项详情
+	 */
+	@Override
+	public WmQuizItem getItemDetails(Integer itemId) {
+		return this.getById(itemId);
+	}
+
+	/**
+	 * 获取试卷的试卷项
+	 *
+	 * @param sn 试卷编号
+	 * @return 试卷项集合
+	 */
+	@Override
+	public List<WmQuizItem> listItems(String sn) {
+		List<WmQuizItem> quizItems =
+				this.list(Wrappers.<WmQuizItem>lambdaQuery().eq(WmQuizItem::getQuizSerialNumber, sn).orderByAsc(WmQuizItem::getWidget, WmQuizItem::getCreateTime));
+
+		return CollUtil.isEmpty(quizItems) ? Collections.emptyList() : quizItems;
+	}
+}

+ 127 - 0
hnqz-upms/hnqz-upms-biz/src/main/java/com/qunzhixinxi/hnqz/admin/service/impl/WmQuizServiceImpl.java

@@ -0,0 +1,127 @@
+package com.qunzhixinxi.hnqz.admin.service.impl;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.IdUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qunzhixinxi.hnqz.admin.api.dto.WmQuizDTO;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuiz;
+import com.qunzhixinxi.hnqz.admin.api.entity.WmQuizItem;
+import com.qunzhixinxi.hnqz.admin.mapper.WmQuizMapper;
+import com.qunzhixinxi.hnqz.admin.service.WmQuizItemService;
+import com.qunzhixinxi.hnqz.admin.service.WmQuizService;
+import com.qunzhixinxi.hnqz.common.core.constant.enums.UpmsState;
+import com.qunzhixinxi.hnqz.common.security.service.HnqzUser;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 试卷服务实现
+ *
+ * @author jimmy
+ * @version 1.0.0
+ * @date 2023-10-16 15:55
+ */
+@Slf4j
+@Service
+@AllArgsConstructor
+public class WmQuizServiceImpl extends ServiceImpl<WmQuizMapper, WmQuiz> implements WmQuizService {
+
+	private final WmQuizItemService quizItemService;
+
+	/**
+	 * 获取试卷详情
+	 *
+	 * @param quizId 试卷ID
+	 * @return 试卷详情
+	 */
+	@Override
+	public Map<String, Object> getDetails(Integer quizId) {
+
+
+		WmQuiz quiz = this.getById(quizId);
+
+		List<WmQuizItem> quizItems = null;
+		if (quiz != null) {
+			quizItems = quizItemService.listItems(quiz.getSerialNumber());
+		}
+
+		Map<String, Object> res = new HashMap<>(2);
+		res.put("quiz", quiz);
+		res.put("items", CollUtil.isEmpty(quizItems) ? Collections.emptyList() : quizItems);
+		return res;
+	}
+
+	/**
+	 * 发布试题
+	 *
+	 * @param quizId   试题ID
+	 * @param operator 操作人
+	 * @return 发布结果
+	 */
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public Boolean releaseQuiz(Integer quizId, HnqzUser operator) {
+		return this.update(Wrappers.<WmQuiz>lambdaUpdate()
+				.set(WmQuiz::getState, UpmsState.QuizState.RELEASE)
+				.set(WmQuiz::getCreateBy, operator.getUsername())
+				.set(WmQuiz::getUpdateTime, LocalDateTime.now())
+				.eq(WmQuiz::getQuizId, quizId)
+		);
+	}
+
+	/**
+	 * 中止试卷
+	 *
+	 * @param quizId   试题ID
+	 * @param operator 操作人
+	 * @return 发布结果
+	 */
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public Boolean stopQuiz(Integer quizId, HnqzUser operator) {
+		return this.update(Wrappers.<WmQuiz>lambdaUpdate()
+				.set(WmQuiz::getState, UpmsState.QuizState.STOP)
+				.set(WmQuiz::getCreateBy, operator.getUsername())
+				.set(WmQuiz::getUpdateTime, LocalDateTime.now())
+				.eq(WmQuiz::getQuizId, quizId)
+		);
+	}
+
+	/**
+	 * 创建试卷
+	 *
+	 * @param resource 试卷信息
+	 * @param user     操作人
+	 * @return 创建结果
+	 */
+	@Override
+	@Transactional(rollbackFor = Exception.class)
+	public String createQuiz(WmQuizDTO.OnQuizCreate resource, HnqzUser user) {
+
+		WmQuiz quiz = new WmQuiz();
+		String sn = IdUtil.fastSimpleUUID();
+		quiz.setSerialNumber(sn);
+		quiz.setTitle(resource.getTitle());
+		quiz.setIntroduction(resource.getIntroduction());
+		quiz.setSourceType(resource.getSourceType());
+		quiz.setState(UpmsState.QuizState.CREATE);
+		quiz.setEnterpriseId(user.getDeptId());
+		String username = user.getUsername();
+		quiz.setCreateBy(username);
+		quiz.setUpdateBy(username);
+		LocalDateTime now = LocalDateTime.now();
+		quiz.setCreateTime(now);
+		quiz.setUpdateTime(now);
+
+		return this.save(quiz) ? sn : "";
+	}
+}