diff --git a/backend/src/main/java/io/metersphere/base/domain/Notification.java b/backend/src/main/java/io/metersphere/base/domain/Notification.java new file mode 100644 index 0000000000000000000000000000000000000000..9fb7981ea1af97daf8f42790c78987d251e92a62 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/Notification.java @@ -0,0 +1,23 @@ +package io.metersphere.base.domain; + +import java.io.Serializable; +import lombok.Data; + +@Data +public class Notification implements Serializable { + private Long id; + + private String type; + + private String receiver; + + private String title; + + private String status; + + private Long createTime; + + private String content; + + private static final long serialVersionUID = 1L; +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/domain/NotificationExample.java b/backend/src/main/java/io/metersphere/base/domain/NotificationExample.java new file mode 100644 index 0000000000000000000000000000000000000000..1f1dbeedbded06a110a81901bda21402d25eed5b --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/domain/NotificationExample.java @@ -0,0 +1,600 @@ +package io.metersphere.base.domain; + +import java.util.ArrayList; +import java.util.List; + +public class NotificationExample { + protected String orderByClause; + + protected boolean distinct; + + protected List<Criteria> oredCriteria; + + public NotificationExample() { + oredCriteria = new ArrayList<Criteria>(); + } + + public void setOrderByClause(String orderByClause) { + this.orderByClause = orderByClause; + } + + public String getOrderByClause() { + return orderByClause; + } + + public void setDistinct(boolean distinct) { + this.distinct = distinct; + } + + public boolean isDistinct() { + return distinct; + } + + public List<Criteria> getOredCriteria() { + return oredCriteria; + } + + public void or(Criteria criteria) { + oredCriteria.add(criteria); + } + + public Criteria or() { + Criteria criteria = createCriteriaInternal(); + oredCriteria.add(criteria); + return criteria; + } + + public Criteria createCriteria() { + Criteria criteria = createCriteriaInternal(); + if (oredCriteria.size() == 0) { + oredCriteria.add(criteria); + } + return criteria; + } + + protected Criteria createCriteriaInternal() { + Criteria criteria = new Criteria(); + return criteria; + } + + public void clear() { + oredCriteria.clear(); + orderByClause = null; + distinct = false; + } + + protected abstract static class GeneratedCriteria { + protected List<Criterion> criteria; + + protected GeneratedCriteria() { + super(); + criteria = new ArrayList<Criterion>(); + } + + public boolean isValid() { + return criteria.size() > 0; + } + + public List<Criterion> getAllCriteria() { + return criteria; + } + + public List<Criterion> getCriteria() { + return criteria; + } + + protected void addCriterion(String condition) { + if (condition == null) { + throw new RuntimeException("Value for condition cannot be null"); + } + criteria.add(new Criterion(condition)); + } + + protected void addCriterion(String condition, Object value, String property) { + if (value == null) { + throw new RuntimeException("Value for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value)); + } + + protected void addCriterion(String condition, Object value1, Object value2, String property) { + if (value1 == null || value2 == null) { + throw new RuntimeException("Between values for " + property + " cannot be null"); + } + criteria.add(new Criterion(condition, value1, value2)); + } + + public Criteria andIdIsNull() { + addCriterion("id is null"); + return (Criteria) this; + } + + public Criteria andIdIsNotNull() { + addCriterion("id is not null"); + return (Criteria) this; + } + + public Criteria andIdEqualTo(Long value) { + addCriterion("id =", value, "id"); + return (Criteria) this; + } + + public Criteria andIdNotEqualTo(Long value) { + addCriterion("id <>", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThan(Long value) { + addCriterion("id >", value, "id"); + return (Criteria) this; + } + + public Criteria andIdGreaterThanOrEqualTo(Long value) { + addCriterion("id >=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThan(Long value) { + addCriterion("id <", value, "id"); + return (Criteria) this; + } + + public Criteria andIdLessThanOrEqualTo(Long value) { + addCriterion("id <=", value, "id"); + return (Criteria) this; + } + + public Criteria andIdIn(List<Long> values) { + addCriterion("id in", values, "id"); + return (Criteria) this; + } + + public Criteria andIdNotIn(List<Long> values) { + addCriterion("id not in", values, "id"); + return (Criteria) this; + } + + public Criteria andIdBetween(Long value1, Long value2) { + addCriterion("id between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andIdNotBetween(Long value1, Long value2) { + addCriterion("id not between", value1, value2, "id"); + return (Criteria) this; + } + + public Criteria andTypeIsNull() { + addCriterion("`type` is null"); + return (Criteria) this; + } + + public Criteria andTypeIsNotNull() { + addCriterion("`type` is not null"); + return (Criteria) this; + } + + public Criteria andTypeEqualTo(String value) { + addCriterion("`type` =", value, "type"); + return (Criteria) this; + } + + public Criteria andTypeNotEqualTo(String value) { + addCriterion("`type` <>", value, "type"); + return (Criteria) this; + } + + public Criteria andTypeGreaterThan(String value) { + addCriterion("`type` >", value, "type"); + return (Criteria) this; + } + + public Criteria andTypeGreaterThanOrEqualTo(String value) { + addCriterion("`type` >=", value, "type"); + return (Criteria) this; + } + + public Criteria andTypeLessThan(String value) { + addCriterion("`type` <", value, "type"); + return (Criteria) this; + } + + public Criteria andTypeLessThanOrEqualTo(String value) { + addCriterion("`type` <=", value, "type"); + return (Criteria) this; + } + + public Criteria andTypeLike(String value) { + addCriterion("`type` like", value, "type"); + return (Criteria) this; + } + + public Criteria andTypeNotLike(String value) { + addCriterion("`type` not like", value, "type"); + return (Criteria) this; + } + + public Criteria andTypeIn(List<String> values) { + addCriterion("`type` in", values, "type"); + return (Criteria) this; + } + + public Criteria andTypeNotIn(List<String> values) { + addCriterion("`type` not in", values, "type"); + return (Criteria) this; + } + + public Criteria andTypeBetween(String value1, String value2) { + addCriterion("`type` between", value1, value2, "type"); + return (Criteria) this; + } + + public Criteria andTypeNotBetween(String value1, String value2) { + addCriterion("`type` not between", value1, value2, "type"); + return (Criteria) this; + } + + public Criteria andReceiverIsNull() { + addCriterion("receiver is null"); + return (Criteria) this; + } + + public Criteria andReceiverIsNotNull() { + addCriterion("receiver is not null"); + return (Criteria) this; + } + + public Criteria andReceiverEqualTo(String value) { + addCriterion("receiver =", value, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverNotEqualTo(String value) { + addCriterion("receiver <>", value, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverGreaterThan(String value) { + addCriterion("receiver >", value, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverGreaterThanOrEqualTo(String value) { + addCriterion("receiver >=", value, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverLessThan(String value) { + addCriterion("receiver <", value, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverLessThanOrEqualTo(String value) { + addCriterion("receiver <=", value, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverLike(String value) { + addCriterion("receiver like", value, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverNotLike(String value) { + addCriterion("receiver not like", value, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverIn(List<String> values) { + addCriterion("receiver in", values, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverNotIn(List<String> values) { + addCriterion("receiver not in", values, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverBetween(String value1, String value2) { + addCriterion("receiver between", value1, value2, "receiver"); + return (Criteria) this; + } + + public Criteria andReceiverNotBetween(String value1, String value2) { + addCriterion("receiver not between", value1, value2, "receiver"); + return (Criteria) this; + } + + public Criteria andTitleIsNull() { + addCriterion("title is null"); + return (Criteria) this; + } + + public Criteria andTitleIsNotNull() { + addCriterion("title is not null"); + return (Criteria) this; + } + + public Criteria andTitleEqualTo(String value) { + addCriterion("title =", value, "title"); + return (Criteria) this; + } + + public Criteria andTitleNotEqualTo(String value) { + addCriterion("title <>", value, "title"); + return (Criteria) this; + } + + public Criteria andTitleGreaterThan(String value) { + addCriterion("title >", value, "title"); + return (Criteria) this; + } + + public Criteria andTitleGreaterThanOrEqualTo(String value) { + addCriterion("title >=", value, "title"); + return (Criteria) this; + } + + public Criteria andTitleLessThan(String value) { + addCriterion("title <", value, "title"); + return (Criteria) this; + } + + public Criteria andTitleLessThanOrEqualTo(String value) { + addCriterion("title <=", value, "title"); + return (Criteria) this; + } + + public Criteria andTitleLike(String value) { + addCriterion("title like", value, "title"); + return (Criteria) this; + } + + public Criteria andTitleNotLike(String value) { + addCriterion("title not like", value, "title"); + return (Criteria) this; + } + + public Criteria andTitleIn(List<String> values) { + addCriterion("title in", values, "title"); + return (Criteria) this; + } + + public Criteria andTitleNotIn(List<String> values) { + addCriterion("title not in", values, "title"); + return (Criteria) this; + } + + public Criteria andTitleBetween(String value1, String value2) { + addCriterion("title between", value1, value2, "title"); + return (Criteria) this; + } + + public Criteria andTitleNotBetween(String value1, String value2) { + addCriterion("title not between", value1, value2, "title"); + return (Criteria) this; + } + + public Criteria andStatusIsNull() { + addCriterion("`status` is null"); + return (Criteria) this; + } + + public Criteria andStatusIsNotNull() { + addCriterion("`status` is not null"); + return (Criteria) this; + } + + public Criteria andStatusEqualTo(String value) { + addCriterion("`status` =", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotEqualTo(String value) { + addCriterion("`status` <>", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusGreaterThan(String value) { + addCriterion("`status` >", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusGreaterThanOrEqualTo(String value) { + addCriterion("`status` >=", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLessThan(String value) { + addCriterion("`status` <", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLessThanOrEqualTo(String value) { + addCriterion("`status` <=", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusLike(String value) { + addCriterion("`status` like", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotLike(String value) { + addCriterion("`status` not like", value, "status"); + return (Criteria) this; + } + + public Criteria andStatusIn(List<String> values) { + addCriterion("`status` in", values, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotIn(List<String> values) { + addCriterion("`status` not in", values, "status"); + return (Criteria) this; + } + + public Criteria andStatusBetween(String value1, String value2) { + addCriterion("`status` between", value1, value2, "status"); + return (Criteria) this; + } + + public Criteria andStatusNotBetween(String value1, String value2) { + addCriterion("`status` not between", value1, value2, "status"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNull() { + addCriterion("create_time is null"); + return (Criteria) this; + } + + public Criteria andCreateTimeIsNotNull() { + addCriterion("create_time is not null"); + return (Criteria) this; + } + + public Criteria andCreateTimeEqualTo(Long value) { + addCriterion("create_time =", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotEqualTo(Long value) { + addCriterion("create_time <>", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThan(Long value) { + addCriterion("create_time >", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeGreaterThanOrEqualTo(Long value) { + addCriterion("create_time >=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThan(Long value) { + addCriterion("create_time <", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeLessThanOrEqualTo(Long value) { + addCriterion("create_time <=", value, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeIn(List<Long> values) { + addCriterion("create_time in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotIn(List<Long> values) { + addCriterion("create_time not in", values, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeBetween(Long value1, Long value2) { + addCriterion("create_time between", value1, value2, "createTime"); + return (Criteria) this; + } + + public Criteria andCreateTimeNotBetween(Long value1, Long value2) { + addCriterion("create_time not between", value1, value2, "createTime"); + return (Criteria) this; + } + } + + public static class Criteria extends GeneratedCriteria { + + protected Criteria() { + super(); + } + } + + public static class Criterion { + private String condition; + + private Object value; + + private Object secondValue; + + private boolean noValue; + + private boolean singleValue; + + private boolean betweenValue; + + private boolean listValue; + + private String typeHandler; + + public String getCondition() { + return condition; + } + + public Object getValue() { + return value; + } + + public Object getSecondValue() { + return secondValue; + } + + public boolean isNoValue() { + return noValue; + } + + public boolean isSingleValue() { + return singleValue; + } + + public boolean isBetweenValue() { + return betweenValue; + } + + public boolean isListValue() { + return listValue; + } + + public String getTypeHandler() { + return typeHandler; + } + + protected Criterion(String condition) { + super(); + this.condition = condition; + this.typeHandler = null; + this.noValue = true; + } + + protected Criterion(String condition, Object value, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.typeHandler = typeHandler; + if (value instanceof List<?>) { + this.listValue = true; + } else { + this.singleValue = true; + } + } + + protected Criterion(String condition, Object value) { + this(condition, value, null); + } + + protected Criterion(String condition, Object value, Object secondValue, String typeHandler) { + super(); + this.condition = condition; + this.value = value; + this.secondValue = secondValue; + this.typeHandler = typeHandler; + this.betweenValue = true; + } + + protected Criterion(String condition, Object value, Object secondValue) { + this(condition, value, secondValue, null); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/NotificationMapper.java b/backend/src/main/java/io/metersphere/base/mapper/NotificationMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..6af1de0c1b7cf615127628f6281a774d2c0e5157 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/NotificationMapper.java @@ -0,0 +1,36 @@ +package io.metersphere.base.mapper; + +import io.metersphere.base.domain.Notification; +import io.metersphere.base.domain.NotificationExample; +import java.util.List; +import org.apache.ibatis.annotations.Param; + +public interface NotificationMapper { + long countByExample(NotificationExample example); + + int deleteByExample(NotificationExample example); + + int deleteByPrimaryKey(Long id); + + int insert(Notification record); + + int insertSelective(Notification record); + + List<Notification> selectByExampleWithBLOBs(NotificationExample example); + + List<Notification> selectByExample(NotificationExample example); + + Notification selectByPrimaryKey(Long id); + + int updateByExampleSelective(@Param("record") Notification record, @Param("example") NotificationExample example); + + int updateByExampleWithBLOBs(@Param("record") Notification record, @Param("example") NotificationExample example); + + int updateByExample(@Param("record") Notification record, @Param("example") NotificationExample example); + + int updateByPrimaryKeySelective(Notification record); + + int updateByPrimaryKeyWithBLOBs(Notification record); + + int updateByPrimaryKey(Notification record); +} \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/NotificationMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/NotificationMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..cc90b7695afa9898745b95281a59761522ca9d2c --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/NotificationMapper.xml @@ -0,0 +1,287 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> +<mapper namespace="io.metersphere.base.mapper.NotificationMapper"> + <resultMap id="BaseResultMap" type="io.metersphere.base.domain.Notification"> + <id column="id" jdbcType="BIGINT" property="id" /> + <result column="type" jdbcType="VARCHAR" property="type" /> + <result column="receiver" jdbcType="VARCHAR" property="receiver" /> + <result column="title" jdbcType="VARCHAR" property="title" /> + <result column="status" jdbcType="VARCHAR" property="status" /> + <result column="create_time" jdbcType="BIGINT" property="createTime" /> + </resultMap> + <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.base.domain.Notification"> + <result column="content" jdbcType="LONGVARCHAR" property="content" /> + </resultMap> + <sql id="Example_Where_Clause"> + <where> + <foreach collection="oredCriteria" item="criteria" separator="or"> + <if test="criteria.valid"> + <trim prefix="(" prefixOverrides="and" suffix=")"> + <foreach collection="criteria.criteria" item="criterion"> + <choose> + <when test="criterion.noValue"> + and ${criterion.condition} + </when> + <when test="criterion.singleValue"> + and ${criterion.condition} #{criterion.value} + </when> + <when test="criterion.betweenValue"> + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + </when> + <when test="criterion.listValue"> + and ${criterion.condition} + <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=","> + #{listItem} + </foreach> + </when> + </choose> + </foreach> + </trim> + </if> + </foreach> + </where> + </sql> + <sql id="Update_By_Example_Where_Clause"> + <where> + <foreach collection="example.oredCriteria" item="criteria" separator="or"> + <if test="criteria.valid"> + <trim prefix="(" prefixOverrides="and" suffix=")"> + <foreach collection="criteria.criteria" item="criterion"> + <choose> + <when test="criterion.noValue"> + and ${criterion.condition} + </when> + <when test="criterion.singleValue"> + and ${criterion.condition} #{criterion.value} + </when> + <when test="criterion.betweenValue"> + and ${criterion.condition} #{criterion.value} and #{criterion.secondValue} + </when> + <when test="criterion.listValue"> + and ${criterion.condition} + <foreach close=")" collection="criterion.value" item="listItem" open="(" separator=","> + #{listItem} + </foreach> + </when> + </choose> + </foreach> + </trim> + </if> + </foreach> + </where> + </sql> + <sql id="Base_Column_List"> + id, `type`, receiver, title, `status`, create_time + </sql> + <sql id="Blob_Column_List"> + content + </sql> + <select id="selectByExampleWithBLOBs" parameterType="io.metersphere.base.domain.NotificationExample" resultMap="ResultMapWithBLOBs"> + select + <if test="distinct"> + distinct + </if> + <include refid="Base_Column_List" /> + , + <include refid="Blob_Column_List" /> + from notification + <if test="_parameter != null"> + <include refid="Example_Where_Clause" /> + </if> + <if test="orderByClause != null"> + order by ${orderByClause} + </if> + </select> + <select id="selectByExample" parameterType="io.metersphere.base.domain.NotificationExample" resultMap="BaseResultMap"> + select + <if test="distinct"> + distinct + </if> + <include refid="Base_Column_List" /> + from notification + <if test="_parameter != null"> + <include refid="Example_Where_Clause" /> + </if> + <if test="orderByClause != null"> + order by ${orderByClause} + </if> + </select> + <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="ResultMapWithBLOBs"> + select + <include refid="Base_Column_List" /> + , + <include refid="Blob_Column_List" /> + from notification + where id = #{id,jdbcType=BIGINT} + </select> + <delete id="deleteByPrimaryKey" parameterType="java.lang.Long"> + delete from notification + where id = #{id,jdbcType=BIGINT} + </delete> + <delete id="deleteByExample" parameterType="io.metersphere.base.domain.NotificationExample"> + delete from notification + <if test="_parameter != null"> + <include refid="Example_Where_Clause" /> + </if> + </delete> + <insert id="insert" parameterType="io.metersphere.base.domain.Notification"> + insert into notification (id, `type`, receiver, + title, `status`, create_time, + content) + values (#{id,jdbcType=BIGINT}, #{type,jdbcType=VARCHAR}, #{receiver,jdbcType=VARCHAR}, + #{title,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT}, + #{content,jdbcType=LONGVARCHAR}) + </insert> + <insert id="insertSelective" parameterType="io.metersphere.base.domain.Notification"> + insert into notification + <trim prefix="(" suffix=")" suffixOverrides=","> + <if test="id != null"> + id, + </if> + <if test="type != null"> + `type`, + </if> + <if test="receiver != null"> + receiver, + </if> + <if test="title != null"> + title, + </if> + <if test="status != null"> + `status`, + </if> + <if test="createTime != null"> + create_time, + </if> + <if test="content != null"> + content, + </if> + </trim> + <trim prefix="values (" suffix=")" suffixOverrides=","> + <if test="id != null"> + #{id,jdbcType=BIGINT}, + </if> + <if test="type != null"> + #{type,jdbcType=VARCHAR}, + </if> + <if test="receiver != null"> + #{receiver,jdbcType=VARCHAR}, + </if> + <if test="title != null"> + #{title,jdbcType=VARCHAR}, + </if> + <if test="status != null"> + #{status,jdbcType=VARCHAR}, + </if> + <if test="createTime != null"> + #{createTime,jdbcType=BIGINT}, + </if> + <if test="content != null"> + #{content,jdbcType=LONGVARCHAR}, + </if> + </trim> + </insert> + <select id="countByExample" parameterType="io.metersphere.base.domain.NotificationExample" resultType="java.lang.Long"> + select count(*) from notification + <if test="_parameter != null"> + <include refid="Example_Where_Clause" /> + </if> + </select> + <update id="updateByExampleSelective" parameterType="map"> + update notification + <set> + <if test="record.id != null"> + id = #{record.id,jdbcType=BIGINT}, + </if> + <if test="record.type != null"> + `type` = #{record.type,jdbcType=VARCHAR}, + </if> + <if test="record.receiver != null"> + receiver = #{record.receiver,jdbcType=VARCHAR}, + </if> + <if test="record.title != null"> + title = #{record.title,jdbcType=VARCHAR}, + </if> + <if test="record.status != null"> + `status` = #{record.status,jdbcType=VARCHAR}, + </if> + <if test="record.createTime != null"> + create_time = #{record.createTime,jdbcType=BIGINT}, + </if> + <if test="record.content != null"> + content = #{record.content,jdbcType=LONGVARCHAR}, + </if> + </set> + <if test="_parameter != null"> + <include refid="Update_By_Example_Where_Clause" /> + </if> + </update> + <update id="updateByExampleWithBLOBs" parameterType="map"> + update notification + set id = #{record.id,jdbcType=BIGINT}, + `type` = #{record.type,jdbcType=VARCHAR}, + receiver = #{record.receiver,jdbcType=VARCHAR}, + title = #{record.title,jdbcType=VARCHAR}, + `status` = #{record.status,jdbcType=VARCHAR}, + create_time = #{record.createTime,jdbcType=BIGINT}, + content = #{record.content,jdbcType=LONGVARCHAR} + <if test="_parameter != null"> + <include refid="Update_By_Example_Where_Clause" /> + </if> + </update> + <update id="updateByExample" parameterType="map"> + update notification + set id = #{record.id,jdbcType=BIGINT}, + `type` = #{record.type,jdbcType=VARCHAR}, + receiver = #{record.receiver,jdbcType=VARCHAR}, + title = #{record.title,jdbcType=VARCHAR}, + `status` = #{record.status,jdbcType=VARCHAR}, + create_time = #{record.createTime,jdbcType=BIGINT} + <if test="_parameter != null"> + <include refid="Update_By_Example_Where_Clause" /> + </if> + </update> + <update id="updateByPrimaryKeySelective" parameterType="io.metersphere.base.domain.Notification"> + update notification + <set> + <if test="type != null"> + `type` = #{type,jdbcType=VARCHAR}, + </if> + <if test="receiver != null"> + receiver = #{receiver,jdbcType=VARCHAR}, + </if> + <if test="title != null"> + title = #{title,jdbcType=VARCHAR}, + </if> + <if test="status != null"> + `status` = #{status,jdbcType=VARCHAR}, + </if> + <if test="createTime != null"> + create_time = #{createTime,jdbcType=BIGINT}, + </if> + <if test="content != null"> + content = #{content,jdbcType=LONGVARCHAR}, + </if> + </set> + where id = #{id,jdbcType=BIGINT} + </update> + <update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.base.domain.Notification"> + update notification + set `type` = #{type,jdbcType=VARCHAR}, + receiver = #{receiver,jdbcType=VARCHAR}, + title = #{title,jdbcType=VARCHAR}, + `status` = #{status,jdbcType=VARCHAR}, + create_time = #{createTime,jdbcType=BIGINT}, + content = #{content,jdbcType=LONGVARCHAR} + where id = #{id,jdbcType=BIGINT} + </update> + <update id="updateByPrimaryKey" parameterType="io.metersphere.base.domain.Notification"> + update notification + set `type` = #{type,jdbcType=VARCHAR}, + receiver = #{receiver,jdbcType=VARCHAR}, + title = #{title,jdbcType=VARCHAR}, + `status` = #{status,jdbcType=VARCHAR}, + create_time = #{createTime,jdbcType=BIGINT} + where id = #{id,jdbcType=BIGINT} + </update> +</mapper> \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtNotificationMapper.java b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtNotificationMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..108a8541c8f63d1f4ecbabbf8a8f6bb8a65c7649 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtNotificationMapper.java @@ -0,0 +1,21 @@ +package io.metersphere.base.mapper.ext; + + +import io.metersphere.base.domain.Notification; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +public interface ExtNotificationMapper { + + Notification getNotification(@Param("id") Integer id, @Param("receiver") String receiver); + + List<Notification> listNotification(@Param("search") String search, @Param("receiver") String receiver); + + List<Notification> listReadNotification(@Param("search") String search, @Param("receiver") String receiver); + + List<Notification> listUnreadNotification(@Param("search") String search, @Param("receiver") String receiver); + + int countNotification(@Param("notification") Notification notification); + +} diff --git a/backend/src/main/java/io/metersphere/base/mapper/ext/ExtNotificationMapper.xml b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtNotificationMapper.xml new file mode 100644 index 0000000000000000000000000000000000000000..027cc9e61fa2f650d93ebb573eabd33838a0cab2 --- /dev/null +++ b/backend/src/main/java/io/metersphere/base/mapper/ext/ExtNotificationMapper.xml @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > +<mapper namespace="io.metersphere.base.mapper.ext.ExtNotificationMapper"> + + <select id="getNotification" resultMap="io.metersphere.base.mapper.NotificationMapper.ResultMapWithBLOBs"> + select * from notification + where id = #{id} and receiver = #{receiver} + limit 1 + </select> + + <select id="listNotification" resultMap="io.metersphere.base.mapper.NotificationMapper.ResultMapWithBLOBs"> + select * from notification + where receiver = #{receiver} + <if test='search != null and search != ""'> + and ( title like #{search} or content like #{search} ) + </if> + order by create_time desc + </select> + + <select id="listReadNotification" resultMap="io.metersphere.base.mapper.NotificationMapper.ResultMapWithBLOBs"> + select * from notification + where receiver = #{receiver} and status = 'READ' + <if test='search != null and search != ""'> + and ( title like #{search} or content like #{search} ) + </if> + order by create_time desc + </select> + + <select id="listUnreadNotification" resultMap="io.metersphere.base.mapper.NotificationMapper.ResultMapWithBLOBs"> + select * from notification + where receiver = #{receiver} and status = 'UNREAD' + <if test='search != null and search != ""'> + and ( title like #{search} or content like #{search} ) + </if> + order by create_time desc + </select> + + <select id="countNotification" resultType="java.lang.Integer"> + select count(*) from notification + where receiver = #{notification.receiver} + <if test="notification.type != null"> + and type = #{notification.type} + </if> + <if test="notification.status != null"> + and status = #{notification.status} + </if> + <if test="notification.uuid != null"> + and uuid = #{notification.uuid} + </if> + </select> + + +</mapper> \ No newline at end of file diff --git a/backend/src/main/java/io/metersphere/commons/constants/NotificationConstants.java b/backend/src/main/java/io/metersphere/commons/constants/NotificationConstants.java new file mode 100644 index 0000000000000000000000000000000000000000..a8574a5068cf53417f00f234d7be208383849a2c --- /dev/null +++ b/backend/src/main/java/io/metersphere/commons/constants/NotificationConstants.java @@ -0,0 +1,12 @@ +package io.metersphere.commons.constants; + +public class NotificationConstants { + + public enum Type { + MESSAGE, ANNOUNCEMENT + } + + public enum Status { + READ, UNREAD + } +} diff --git a/backend/src/main/java/io/metersphere/commons/utils/SessionUtils.java b/backend/src/main/java/io/metersphere/commons/utils/SessionUtils.java index 8b81a9e3e24e7193630837e4789d77448fd93162..c8ab04c11565008fa190b72be54630067057393f 100644 --- a/backend/src/main/java/io/metersphere/commons/utils/SessionUtils.java +++ b/backend/src/main/java/io/metersphere/commons/utils/SessionUtils.java @@ -7,7 +7,10 @@ import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.DefaultSessionManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.support.DefaultSubjectContext; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; import java.util.Collection; import static io.metersphere.commons.constants.SessionConstants.ATTR_USER; @@ -65,14 +68,26 @@ public class SessionUtils { } public static String getCurrentWorkspaceId() { + HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); + if (request.getHeader("WORKSPACE_ID") != null) { + return request.getHeader("WORKSPACE_ID"); + } return getUser().getLastWorkspaceId(); } public static String getCurrentOrganizationId() { + HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); + if (request.getHeader("ORGANIZATION_ID") != null) { + return request.getHeader("ORGANIZATION_ID"); + } return getUser().getLastOrganizationId(); } public static String getCurrentProjectId() { + HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest(); + if (request.getHeader("PROJECT_ID") != null) { + return request.getHeader("PROJECT_ID"); + } return getUser().getLastProjectId(); } } diff --git a/backend/src/main/java/io/metersphere/notice/sender/SendNoticeAspect.java b/backend/src/main/java/io/metersphere/notice/sender/SendNoticeAspect.java index 0f551bb08d0ae97c35859d241104dec4722b2080..7b39c0f8db096ba1272e5751b6f4ffb8aea4a87f 100644 --- a/backend/src/main/java/io/metersphere/notice/sender/SendNoticeAspect.java +++ b/backend/src/main/java/io/metersphere/notice/sender/SendNoticeAspect.java @@ -35,10 +35,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - -/** - * 系统日志:切é¢å¤„ç†ç±» - */ @Aspect @Component public class SendNoticeAspect { diff --git a/backend/src/main/java/io/metersphere/notice/service/NoticeSendService.java b/backend/src/main/java/io/metersphere/notice/service/NoticeSendService.java index dcdb601d0edfc1ad9eccb55c2337777584dce172..0ef42fbdc0df0862d4626df691820c7d997b7516 100644 --- a/backend/src/main/java/io/metersphere/notice/service/NoticeSendService.java +++ b/backend/src/main/java/io/metersphere/notice/service/NoticeSendService.java @@ -1,19 +1,27 @@ package io.metersphere.notice.service; import com.alibaba.nacos.client.utils.StringUtils; +import io.metersphere.base.domain.User; import io.metersphere.commons.constants.NoticeConstants; import io.metersphere.commons.utils.LogUtil; +import io.metersphere.commons.utils.SessionUtils; +import io.metersphere.controller.request.organization.QueryOrgMemberRequest; import io.metersphere.notice.domain.MessageDetail; +import io.metersphere.notice.sender.AbstractNoticeSender; import io.metersphere.notice.sender.NoticeModel; -import io.metersphere.notice.sender.NoticeSender; import io.metersphere.notice.sender.impl.DingNoticeSender; import io.metersphere.notice.sender.impl.LarkNoticeSender; import io.metersphere.notice.sender.impl.MailNoticeSender; import io.metersphere.notice.sender.impl.WeComNoticeSender; +import io.metersphere.service.UserService; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.RegExUtils; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.util.List; +import java.util.Map; @Component public class NoticeSendService { @@ -27,9 +35,13 @@ public class NoticeSendService { private LarkNoticeSender larkNoticeSender; @Resource private NoticeService noticeService; + @Resource + private NotificationService notificationService; + @Resource + private UserService userService; - private NoticeSender getNoticeSender(MessageDetail messageDetail) { - NoticeSender noticeSender = null; + private AbstractNoticeSender getNoticeSender(MessageDetail messageDetail) { + AbstractNoticeSender noticeSender = null; switch (messageDetail.getType()) { case NoticeConstants.Type.EMAIL: noticeSender = mailNoticeSender; @@ -50,11 +62,11 @@ public class NoticeSendService { } public void send(String taskType, NoticeModel noticeModel) { - String loadReportId = (String) noticeModel.getParamMap().get("id"); try { List<MessageDetail> messageDetails; switch (taskType) { case NoticeConstants.Mode.API: + String loadReportId = (String) noticeModel.getParamMap().get("id"); messageDetails = noticeService.searchMessageByTypeBySend(NoticeConstants.TaskType.JENKINS_TASK, loadReportId); break; case NoticeConstants.Mode.SCHEDULE: @@ -64,13 +76,46 @@ public class NoticeSendService { messageDetails = noticeService.searchMessageByType(taskType); break; } - messageDetails.forEach(messageDetail -> { - if (StringUtils.equals(messageDetail.getEvent(), noticeModel.getEvent())) { - this.getNoticeSender(messageDetail).send(messageDetail, noticeModel); - } - }); + QueryOrgMemberRequest request = new QueryOrgMemberRequest(); + request.setOrganizationId(SessionUtils.getCurrentOrganizationId()); + List<User> orgAllMember = userService.getOrgAllMember(request); + + + // 异æ¥å‘é€å®žä½“通知 + messageDetails.stream() + .filter(messageDetail -> StringUtils.equals(messageDetail.getEvent(), noticeModel.getEvent())) + .forEach(messageDetail -> this.getNoticeSender(messageDetail).send(messageDetail, noticeModel)); + + // 异æ¥å‘é€ç«™å†…通知 + sendAnnouncement(noticeModel, orgAllMember); } catch (Exception e) { LogUtil.error(e.getMessage(), e); } } + + @Async + public void sendAnnouncement(NoticeModel noticeModel, List<User> orgAllMember) { + // 替æ¢å˜é‡ + noticeModel.setContext(getContent(noticeModel)); + orgAllMember.forEach(receiver -> { + String context = noticeModel.getContext(); + LogUtil.debug("å‘é€ç«™å†…通知: {}, 内容: {}", receiver.getName(), context); + notificationService.sendAnnouncement(noticeModel.getSubject(), context, receiver.getId()); + }); + } + + private String getContent(NoticeModel noticeModel) { + String template = noticeModel.getContext(); + Map<String, Object> paramMap = noticeModel.getParamMap(); + if (MapUtils.isNotEmpty(paramMap)) { + for (String k : paramMap.keySet()) { + if (paramMap.get(k) != null) { + template = RegExUtils.replaceAll(template, "\\$\\{" + k + "}", paramMap.get(k).toString()); + } else { + template = RegExUtils.replaceAll(template, "\\$\\{" + k + "}", ""); + } + } + } + return template; + } } diff --git a/backend/src/main/java/io/metersphere/notice/service/NotificationService.java b/backend/src/main/java/io/metersphere/notice/service/NotificationService.java new file mode 100644 index 0000000000000000000000000000000000000000..9e82da315c4343a7f2c7fe8d0532a2e1f6e4e64c --- /dev/null +++ b/backend/src/main/java/io/metersphere/notice/service/NotificationService.java @@ -0,0 +1,88 @@ +package io.metersphere.notice.service; + + +import io.metersphere.base.domain.Notification; +import io.metersphere.base.domain.NotificationExample; +import io.metersphere.base.mapper.NotificationMapper; +import io.metersphere.base.mapper.ext.ExtNotificationMapper; +import io.metersphere.commons.constants.NotificationConstants; +import io.metersphere.commons.utils.SessionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.List; + +@Service +@Transactional(rollbackFor = Exception.class) +public class NotificationService { + + @Resource + private NotificationMapper notificationMapper; + + @Resource + private ExtNotificationMapper extNotificationMapper; + + public void sendAnnouncement(String subject, String content, String receiver) { + Notification notification = new Notification(); + notification.setTitle(subject); + notification.setContent(content); + notification.setType(NotificationConstants.Type.ANNOUNCEMENT.name()); + notification.setStatus(NotificationConstants.Status.UNREAD.name()); + notification.setCreateTime(System.currentTimeMillis()); + notification.setReceiver(receiver); + notificationMapper.insert(notification); + } + + public Notification getNotification(int id) { + return extNotificationMapper.getNotification(id, SessionUtils.getUser().getId()); + } + + public int readAll() { + Notification record = new Notification(); + record.setStatus(NotificationConstants.Status.READ.name()); + NotificationExample example = new NotificationExample(); + example.createCriteria().andReceiverEqualTo(SessionUtils.getUser().getId()); + return notificationMapper.updateByExampleSelective(record, example); + } + + public int countNotification(Notification notification) { + notification.setReceiver(SessionUtils.getUser().getId()); + return extNotificationMapper.countNotification(notification); + } + + public int read(long id) { + Notification record = new Notification(); + record.setStatus(NotificationConstants.Status.READ.name()); + NotificationExample example = new NotificationExample(); + example.createCriteria().andIdEqualTo(id).andReceiverEqualTo(SessionUtils.getUser().getId()); + return notificationMapper.updateByExampleSelective(record, example); + } + + public List<Notification> listNotification(Notification notification) { + String search = null; + if (StringUtils.isNotBlank(notification.getTitle())) { + search = "%" + notification.getTitle() + "%"; + } + return extNotificationMapper.listNotification(search, SessionUtils.getUser().getId()); + } + + public List<Notification> listReadNotification(Notification notification) { + String search = null; + if (StringUtils.isNotBlank(notification.getTitle())) { + search = "%" + notification.getTitle() + "%"; + } + return extNotificationMapper.listReadNotification(search, SessionUtils.getUser().getId()); + } + + public List<Notification> listUnreadNotification(Notification notification) { + String search = null; + if (StringUtils.isNotBlank(notification.getTitle())) { + search = "%" + notification.getTitle() + "%"; + } + return extNotificationMapper.listUnreadNotification(search, SessionUtils.getUser().getId()); + } + + +} diff --git a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java index 4142b579d01761f5a42deba3cbe056c0b6bbf26a..aaf30666a1b845c4acb769a930bdb6088cb0ffea 100644 --- a/backend/src/main/java/io/metersphere/track/service/TestPlanService.java +++ b/backend/src/main/java/io/metersphere/track/service/TestPlanService.java @@ -1007,6 +1007,8 @@ public class TestPlanService { performanceRequest.setTestPlanLoadId(caseID); if (StringUtils.equals(ReportTriggerMode.API.name(), triggerMode)) { performanceRequest.setTriggerMode(ReportTriggerMode.TEST_PLAN_API.name()); + } else if (StringUtils.equals(ReportTriggerMode.MANUAL.name(), triggerMode)) { + performanceRequest.setTriggerMode(ReportTriggerMode.MANUAL.name()); } else { performanceRequest.setTriggerMode(ReportTriggerMode.TEST_PLAN_SCHEDULE.name()); } diff --git a/backend/src/main/resources/db/migration/V93__v1.12_release.sql b/backend/src/main/resources/db/migration/V93__v1.12_release.sql index 1908b2d714a674b4ec9b6919f51303f3b29d3453..591ed5a5a5ce474bbb9e0250bb650b2213d95173 100644 --- a/backend/src/main/resources/db/migration/V93__v1.12_release.sql +++ b/backend/src/main/resources/db/migration/V93__v1.12_release.sql @@ -68,3 +68,21 @@ CREATE TABLE `plugin` ( ) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_general_ci; + +ALTER TABLE test_plan + ADD report_summary TEXT NULL COMMENT '测试计划报告总结'; + +CREATE TABLE IF NOT EXISTS `notification` +( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID', + `type` VARCHAR(30) DEFAULT NULL COMMENT '通知类型', + `receiver` VARCHAR(100) DEFAULT NULL COMMENT '接收人', + `title` VARCHAR(100) DEFAULT NULL COMMENT 'æ ‡é¢˜', + `content` LONGTEXT COMMENT '内容', + `status` VARCHAR(30) DEFAULT NULL COMMENT '状æ€', + `create_time` BIGINT(13) DEFAULT NULL COMMENT 'æ›´æ–°æ—¶é—´', + PRIMARY KEY (`id`), + KEY `IDX_RECEIVER` (`receiver`) USING BTREE +) ENGINE = InnoDB + DEFAULT CHARSET = utf8mb4 + COLLATE utf8mb4_general_ci; diff --git a/frontend/src/business/App.vue b/frontend/src/business/App.vue index 1d276c522d09a743d2d45dd9a54b5a724df946a8..92d844e4037e9827b6cc92c52c422b726ebec830 100644 --- a/frontend/src/business/App.vue +++ b/frontend/src/business/App.vue @@ -17,6 +17,7 @@ <ms-language-switch :color="color"/> <ms-header-org-ws :color="color"/> <ms-task-center :color="color"/> + <ms-notice-center :color="color"/> </el-col> </el-row> @@ -36,6 +37,7 @@ import {hasLicense, saveLocalStorage, setColor, setDefaultTheme} from "@/common/ import {registerRequestHeaders} from "@/common/js/ajax"; import {ORIGIN_COLOR} from "@/common/js/constants"; import MsTaskCenter from "@/business/components/task/TaskCenter"; +import MsNoticeCenter from "@/business/components/notice/NoticeCenter"; const requireComponent = require.context('@/business/components/xpack/', true, /\.vue$/); const header = requireComponent.keys().length > 0 ? requireComponent("./license/LicenseMessage.vue") : {}; const display = requireComponent.keys().length > 0 ? requireComponent("./display/Display.vue") : {}; @@ -181,6 +183,7 @@ export default { } }, components: { + MsNoticeCenter, MsTaskCenter, MsLanguageSwitch, MsUser, diff --git a/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue b/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue index 72a4b258fa8881012fc9efcbada550c3212b64ac..30f1efcaf8a4f215989d05ffa6883e4773c5d333 100644 --- a/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue +++ b/frontend/src/business/components/api/automation/scenario/api/RelevanceApiList.vue @@ -5,7 +5,7 @@ @isApiListEnableChange="isApiListEnableChange"> <ms-environment-select :project-id="projectId" v-if="isTestPlan" :is-read-only="isReadOnly" - @setEnvironment="setEnvironment"/> + @setEnvironment="setEnvironment" ref="msEnvironmentSelect"/> <el-input :placeholder="$t('commons.search_by_name_or_id')" @blur="initTable" class="search-input" size="small" @keyup.enter.native="initTable" v-model="condition.name"/> @@ -374,6 +374,13 @@ if (this.$refs.apitable) { this.$refs.apitable.clear(); } + }, + clearEnvAndSelect() { + this.environmentId = ""; + if (this.$refs.msEnvironmentSelect) { + this.$refs.msEnvironmentSelect.environmentId = ""; + } + this.clear(); } }, } diff --git a/frontend/src/business/components/api/automation/scenario/api/RelevanceCaseList.vue b/frontend/src/business/components/api/automation/scenario/api/RelevanceCaseList.vue index 21d90fd9d4eb140d785ea717e3400b05aeaba1b5..2b46b5a39e2d0728a4ef4bc6df09cde074ccac44 100644 --- a/frontend/src/business/components/api/automation/scenario/api/RelevanceCaseList.vue +++ b/frontend/src/business/components/api/automation/scenario/api/RelevanceCaseList.vue @@ -5,7 +5,7 @@ @isApiListEnableChange="isApiListEnableChange"> <ms-environment-select :project-id="projectId" v-if="isTestPlan" :is-read-only="isReadOnly" - @setEnvironment="setEnvironment"/> + @setEnvironment="setEnvironment" ref="msEnvironmentSelect"/> <el-input :placeholder="$t('commons.search_by_name_or_id')" @blur="initTable" @keyup.enter.native="initTable" class="search-input" size="small" v-model="condition.name"/> @@ -241,6 +241,13 @@ export default { this.$refs.table.clear(); } }, + clearEnvAndSelect() { + this.environmentId = ""; + if (this.$refs.msEnvironmentSelect) { + this.$refs.msEnvironmentSelect.environmentId = ""; + } + this.clear(); + }, showExecResult(row) { this.visible = false; this.$emit('showExecResult', row); diff --git a/frontend/src/business/components/notice/NoticeCenter.vue b/frontend/src/business/components/notice/NoticeCenter.vue new file mode 100644 index 0000000000000000000000000000000000000000..afe47ad3f539979d7fd0bfda49e6cb388e032b7a --- /dev/null +++ b/frontend/src/business/components/notice/NoticeCenter.vue @@ -0,0 +1,442 @@ +<template> + <div> + <el-menu :unique-opened="true" class="header-user-menu align-right header-top-menu" + mode="horizontal" + :background-color="color" + text-color="#fff" + active-text-color="#fff"> + <el-menu-item onselectstart="return false"> + <el-tooltip effect="light"> + <template v-slot:content> + <span>{{ $t('commons.notice_center') }}</span> + </template> + <div @click="showNoticeCenter" v-if="runningTotal > 0"> + <el-badge :value="runningTotal" class="item" type="primary"> + <font-awesome-icon class="icon global focusing" :icon="['fas', 'bell']"/> + </el-badge> + </div> + <font-awesome-icon @click="showNoticeCenter" class="icon global focusing" :icon="['fas', 'bell']" v-else/> + </el-tooltip> + </el-menu-item> + </el-menu> + + <el-drawer :visible.sync="taskVisible" :destroy-on-close="true" direction="rtl" + :withHeader="true" :modal="false" :title="$t('commons.task_center')" size="600px" + custom-class="ms-drawer-task"> + <div style="color: #2B415C;margin: 0px 20px 0px"> + <el-form label-width="68px"> + <el-row> + <el-col :span="8"> + <el-form-item :label="$t('test_track.report.list.trigger_mode')" prop="runMode"> + <el-select size="small" style="margin-right: 10px" v-model="condition.triggerMode" @change="init"> + <el-option v-for="item in runMode" :key="item.id" :value="item.id" :label="item.label"/> + </el-select> + </el-form-item> + </el-col> + <el-col :span="8"> + <el-form-item :label="$t('commons.status')" prop="status"> + <el-select size="small" style="margin-right: 10px" v-model="condition.executionStatus" @change="init"> + <el-option v-for="item in runStatus" :key="item.id" :value="item.id" :label="item.label"/> + </el-select> + </el-form-item> + </el-col> + <el-col :span="8"> + <el-form-item :label="$t('commons.executor')" prop="status"> + <el-select v-model="condition.executor" :placeholder="$t('commons.executor')" filterable size="small" + style="margin-right: 10px" @change="init"> + <el-option + v-for="item in maintainerOptions" + :key="item.id" + :label="item.id + ' (' + item.name + ')'" + :value="item.id"> + </el-option> + </el-select> + </el-form-item> + </el-col> + </el-row> + </el-form> + </div> + + <div class="report-container"> + <div v-for="item in taskData" :key="item.id" style="margin-bottom: 5px"> + <el-card class="ms-card-task" @click.native="showReport(item,$event)"> + <span><el-link type="primary">{{ getModeName(item.executionModule) }} </el-link>: {{ + item.name + }} </span><br/> + <span> + 执行器:{{ item.actuator }} ç”± {{ item.executor }} + {{ item.executionTime | timestampFormatDate }} + {{ getMode(item.triggerMode) }} + </span> + <br/> + <el-row> + <el-col :span="20"> + <el-progress :percentage="getPercentage(item.executionStatus)" :format="format"/> + </el-col> + <el-col :span="4"> + <span v-if="item.executionStatus && item.executionStatus.toLowerCase() === 'error'" + class="ms-task-error"> + error + </span> + <span v-else-if="item.executionStatus && item.executionStatus.toLowerCase() === 'success'" + class="ms-task-success"> + success + </span> + <span v-else>{{ + item.executionStatus ? item.executionStatus.toLowerCase() : item.executionStatus + }}</span> + </el-col> + </el-row> + </el-card> + </div> + </div> + </el-drawer> + + </div> +</template> + +<script> +import MsDrawer from "../common/components/MsDrawer"; +import {getCurrentProjectID, getCurrentUser, hasPermissions} from "@/common/js/utils"; +import MsRequestResultTail from "../../components/api/definition/components/response/RequestResultTail"; + +export default { + name: "MsNoticeCenter", + components: { + MsDrawer, + MsRequestResultTail + }, + inject: [ + 'reload' + ], + data() { + return { + runningTotal: 0, + taskVisible: false, + result: {}, + taskData: [], + response: {}, + initEnd: false, + visible: false, + showType: "", + runMode: [ + {id: '', label: this.$t('api_test.definition.document.data_set.all')}, + {id: 'BATCH', label: this.$t('api_test.automation.batch_execute')}, + {id: 'SCHEDULE', label: this.$t('commons.trigger_mode.schedule')}, + {id: 'MANUAL', label: this.$t('commons.trigger_mode.manual')}, + {id: 'API', label: 'API'} + ], + runStatus: [ + {id: '', label: this.$t('api_test.definition.document.data_set.all')}, + {id: 'Saved', label: 'Saved'}, + {id: 'Starting', label: 'Starting'}, + {id: 'Running', label: 'Running'}, + {id: 'Reporting', label: 'Reporting'}, + {id: 'Completed', label: 'Completed'}, + {id: 'error', label: 'Error'}, + {id: 'success', label: 'Success'} + ], + condition: {triggerMode: "", executionStatus: ""}, + maintainerOptions: [], + websocket: Object, + }; + }, + props: { + color: String + }, + created() { + if (hasPermissions('PROJECT_API_SCENARIO:READ')) { + this.condition.executor = getCurrentUser().id; + } + }, + watch: { + taskVisible(v) { + if (!v) { + this.close(); + } + } + }, + methods: { + format(item) { + return ''; + }, + getMaintainerOptions() { + this.$post('/user/project/member/tester/list', {projectId: getCurrentProjectID()}, response => { + this.maintainerOptions = response.data; + this.condition.executor = getCurrentUser().id; + }); + }, + initWebSocket() { + let protocol = "ws://"; + if (window.location.protocol === 'https:') { + protocol = "wss://"; + } + const uri = protocol + window.location.host + "/task/center/count/running/" + getCurrentProjectID(); + this.websocket = new WebSocket(uri); + this.websocket.onmessage = this.onMessage; + this.websocket.onopen = this.onOpen; + this.websocket.onerror = this.onError; + this.websocket.onclose = this.onClose; + }, + onOpen() { + }, + onError(e) { + }, + onMessage(e) { + let taskTotal = e.data; + this.runningTotal = taskTotal; + this.initIndex++; + if (this.taskVisible && taskTotal > 0 && this.initEnd) { + setTimeout(() => { + this.initEnd = false; + this.init(); + }, 3000); + } + }, + onClose(e) { + }, + showNoticeCenter() { + this.getTaskRunning(); + this.getMaintainerOptions(); + this.init(); + this.taskVisible = true; + }, + close() { + this.visible = false; + this.taskVisible = false; + this.showType = ""; + if (this.websocket && this.websocket.close instanceof Function) { + this.websocket.close(); + } + }, + open() { + this.showNoticeCenter(); + this.initIndex = 0; + }, + getPercentage(status) { + if (status) { + status = status.toLowerCase(); + if (status === "waiting") { + return 0; + } + if (status === 'saved' || status === 'completed' || status === 'success' || status === 'error') { + return 100; + } + } + return 60; + }, + getModeName(executionModule) { + switch (executionModule) { + case "SCENARIO": + return this.$t('test_track.scenario_test_case'); + case "PERFORMANCE": + return this.$t('test_track.performance_test_case'); + case "API": + return this.$t('test_track.api_test_case'); + } + }, + showReport(row, env) { + let status = row.executionStatus; + if (status) { + status = row.executionStatus.toLowerCase(); + if (status === 'saved' || status === 'completed' || status === 'success' || status === 'error') { + this.taskVisible = false; + switch (row.executionModule) { + case "SCENARIO": + this.$router.push({ + path: '/api/automation/report/view/' + row.id, + }); + break; + case "PERFORMANCE": + this.$router.push({ + path: '/performance/report/view/' + row.id, + }); + break; + case "API": + this.getExecResult(row.id); + break; + } + } else { + this.$warning("æ£åœ¨è¿è¡Œä¸ï¼Œè¯·ç¨åŽæŸ¥çœ‹"); + } + } + }, + + getExecResult(reportId) { + if (reportId) { + let url = "/api/definition/report/get/" + reportId; + this.$get(url, response => { + if (response.data) { + let data = JSON.parse(response.data.content); + this.response = data; + this.visible = true; + } + }); + } + }, + getMode(mode) { + if (mode === 'MANUAL') { + return this.$t('commons.trigger_mode.manual'); + } + if (mode === 'SCHEDULE') { + return this.$t('commons.trigger_mode.schedule'); + } + if (mode === 'TEST_PLAN_SCHEDULE') { + return this.$t('commons.trigger_mode.schedule'); + } + if (mode === 'API') { + return this.$t('commons.trigger_mode.api'); + } + if (mode === 'BATCH') { + return this.$t('api_test.automation.batch_execute'); + } + return mode; + }, + getTaskRunning() { + this.initWebSocket(); + }, + calculationRunningTotal() { + if (this.taskData) { + let total = 0; + this.taskData.forEach(item => { + if (this.getPercentage(item.executionStatus) !== 100) { + total++; + } + }); + this.runningTotal = total; + } + }, + init() { + if (this.showType === "CASE" || this.showType === "SCENARIO") { + return; + } + this.result.loading = true; + this.condition.projectId = getCurrentProjectID(); + this.result = this.$post('/task/center/list', this.condition, response => { + this.taskData = response.data; + this.calculationRunningTotal(); + this.initEnd = true; + }); + }, + initCaseHistory(id) { + this.result = this.$get('/task/center/case/' + id, response => { + this.taskData = response.data; + }); + }, + openHistory(id) { + this.initCaseHistory(id); + this.taskVisible = true; + this.showType = "CASE"; + }, + openScenarioHistory(id) { + this.result = this.$get('/task/center/scenario/' + id, response => { + this.taskData = response.data; + }); + this.showType = "SCENARIO"; + this.taskVisible = true; + } + } +}; +</script> + +<style> +.ms-drawer-task { + top: 42px !important; +} +</style> + +<style scoped> +.el-icon-check { + color: #44b349; + margin-left: 10px; +} + +.report-container { + height: calc(100vh - 180px); + min-height: 600px; + overflow-y: auto; +} + +.align-right { + float: right; +} + +.icon { + width: 24px; +} + +/deep/ .el-drawer__header { + font-size: 18px; + color: #0a0a0a; + border-bottom: 1px solid #E6E6E6; + background-color: #FFF; + margin-bottom: 10px; + padding: 10px; +} + +.ms-card-task >>> .el-card__body { + padding: 10px; +} + +.global { + color: #fff; +} + +.header-top-menu { + height: 40px; + line-height: 40px; + color: inherit; +} + +.header-top-menu.el-menu--horizontal > li { + height: 40px; + line-height: 40px; + color: inherit; +} + +.header-top-menu.el-menu--horizontal > li.el-submenu > * { + height: 39px; + line-height: 40px; + color: inherit; +} + +.header-top-menu.el-menu--horizontal > li.is-active { + background: var(--color_shallow) !important; +} + +.ms-card-task:hover { + cursor: pointer; + border-color: #783887; +} + +/deep/ .el-progress-bar { + padding-right: 20px; +} + +/deep/ .el-menu-item { + padding-left: 0; + padding-right: 0; +} + +/deep/ .el-badge__content.is-fixed { + top: 25px; +} + +/deep/ .el-badge__content { + border-radius: 10px; + height: 10px; + line-height: 10px; +} + +.item { + margin-right: 10px; +} + +.ms-task-error { + color: #F56C6C; +} + +.ms-task-success { + color: #67C23A; +} +</style> diff --git a/frontend/src/business/components/performance/test/EditPerformanceTest.vue b/frontend/src/business/components/performance/test/EditPerformanceTest.vue index bdba7403dd4372b0407d207be1fc54514f7b7ffe..d4c6ac5fc9f3596065884a6769ab9923ab230c45 100644 --- a/frontend/src/business/components/performance/test/EditPerformanceTest.vue +++ b/frontend/src/business/components/performance/test/EditPerformanceTest.vue @@ -280,7 +280,7 @@ export default { let options = this.getSaveOption(); this.result = this.$request(options, (response) => { - this.test.id = response.data; + this.test.id = response.data.id; this.$success(this.$t('commons.save_success')); this.result = this.$post(this.runPath, {id: this.test.id, triggerMode: 'MANUAL'}, (response) => { let reportId = response.data; diff --git a/frontend/src/business/components/track/plan/components/TestPlanList.vue b/frontend/src/business/components/track/plan/components/TestPlanList.vue index bb4c60f391e0628342fb977e127ed42f5a154a72..a994d5015c83ed125f83617fa19a125e35cb406f 100644 --- a/frontend/src/business/components/track/plan/components/TestPlanList.vue +++ b/frontend/src/business/components/track/plan/components/TestPlanList.vue @@ -499,6 +499,7 @@ export default { param.testPlanId = this.currentPlanId; param.projectId = getCurrentProjectID(); param.userId = getCurrentUserId(); + param.triggerMode = 'MANUAL'; this.result = this.$post('test/plan/run/', param,() => { this.$success(this.$t('commons.run_success')); }, () => { diff --git a/frontend/src/business/components/track/plan/view/comonents/api/TestCaseApiRelevance.vue b/frontend/src/business/components/track/plan/view/comonents/api/TestCaseApiRelevance.vue index 3792f6b15dbbc15ea78a2635930d4552216349c2..6d6628320d43e26e66700b4d6ee69b59190b746e 100644 --- a/frontend/src/business/components/track/plan/view/comonents/api/TestCaseApiRelevance.vue +++ b/frontend/src/business/components/track/plan/view/comonents/api/TestCaseApiRelevance.vue @@ -105,6 +105,13 @@ } }, setProject(projectId) { + // 切æ¢é¡¹ç›® 清空环境和选ä¸è¡Œ + if (this.$refs.apiList) { + this.$refs.apiList.clearEnvAndSelect(); + } + if (this.$refs.apiCaseList) { + this.$refs.apiCaseList.clearEnvAndSelect(); + } this.projectId = projectId; }, isApiListEnableChange(data) { diff --git a/frontend/src/common/js/ajax.js b/frontend/src/common/js/ajax.js index 27bd3b2e4faf8097fb62939389455fccf9de3506..6ef98509df7a89853d27bf7421f066e4027d3ec0 100644 --- a/frontend/src/common/js/ajax.js +++ b/frontend/src/common/js/ajax.js @@ -2,6 +2,7 @@ import {Message, MessageBox} from 'element-ui'; import axios from "axios"; import i18n from '../../i18n/i18n'; import {TokenKey} from "@/common/js/constants"; +import {getCurrentOrganizationId, getCurrentProjectID, getCurrentWorkspaceId} from "@/common/js/utils"; export function registerRequestHeaders() { axios.interceptors.request.use(config => { @@ -9,6 +10,10 @@ export function registerRequestHeaders() { if (user && user.csrfToken) { config.headers['CSRF-TOKEN'] = user.csrfToken; } + // åŒ…å« ç»„ç»‡ 工作空间 é¡¹ç›®çš„æ ‡è¯† + config.headers['ORGANIZATION_ID'] = getCurrentOrganizationId(); + config.headers['WORKSPACE_ID'] = getCurrentWorkspaceId(); + config.headers['PROJECT_ID'] = getCurrentProjectID(); return config; }); } diff --git a/frontend/src/i18n/en-US.js b/frontend/src/i18n/en-US.js index 46aae0b8f58879d4b4c15f3f7ae56fd5fd4c511e..4c0169c5d2011d1b8385d03652bf5f3e93c531ae 100644 --- a/frontend/src/i18n/en-US.js +++ b/frontend/src/i18n/en-US.js @@ -168,6 +168,7 @@ export default { api_case: "Api Case", scenario_case: "Scenario Case", task_center: "Task center", + notice_center: "Notice center", all_module_title: "All module", create_user: 'Creator', run_message: "The task is being executed, please go to the task center to view the details", diff --git a/frontend/src/i18n/zh-CN.js b/frontend/src/i18n/zh-CN.js index efcd3dc838347f51d9db5946b288be0633bcff0b..4d262f1767a9c4bc7c2f913f3a7756f202d6e787 100644 --- a/frontend/src/i18n/zh-CN.js +++ b/frontend/src/i18n/zh-CN.js @@ -169,6 +169,7 @@ export default { api_case: "接å£ç”¨ä¾‹", scenario_case: "场景用例", task_center: "任务ä¸å¿ƒ", + notice_center: "消æ¯ä¸å¿ƒ", all_module_title: "全部模å—", create_user: '创建人', run_message: "任务执行ä¸ï¼Œè¯·åˆ°ä»»åŠ¡ä¸å¿ƒæŸ¥çœ‹è¯¦æƒ…", diff --git a/frontend/src/i18n/zh-TW.js b/frontend/src/i18n/zh-TW.js index 3962fba15e372091282560b6906b87158b2d7e93..7c539a5d74c34e9f69fbcc3dbbfac402808638ec 100644 --- a/frontend/src/i18n/zh-TW.js +++ b/frontend/src/i18n/zh-TW.js @@ -169,6 +169,7 @@ export default { api_case: "接å£ç”¨ä¾‹", scenario_case: "å ´æ™¯ç”¨ä¾‹", task_center: "任务ä¸å¿ƒ", + notice_center: "消æ¯ä¸å¿ƒ", all_module_title: "全部模塊", create_user: "創建人", run_message: "任務執行ä¸ï¼Œè«‹åˆ°ä»»å‹™ä¸å¿ƒæŸ¥çœ‹è©³æƒ…",