feat(功能用例): 新增用例创建评论功能

This commit is contained in:
guoyuqi 2023-10-25 17:38:24 +08:00 committed by 刘瑞斌
parent 34f970fdb2
commit 683d4fb729
24 changed files with 623 additions and 117 deletions

View File

@ -1,81 +1,90 @@
package io.metersphere.project.domain;
import io.metersphere.validation.groups.*;
import io.metersphere.validation.groups.Created;
import io.metersphere.validation.groups.Updated;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import lombok.Data;
@Data
public class Notification implements Serializable {
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.id.not_blank}", groups = {Updated.class})
private Long id;
@Schema(description = "通知类型", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "通知类型", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.type.not_blank}", groups = {Created.class})
@Size(min = 1, max = 30, message = "{notification.type.length_range}", groups = {Created.class, Updated.class})
private String type;
@Schema(description = "接收人", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "接收人", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.receiver.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{notification.receiver.length_range}", groups = {Created.class, Updated.class})
private String receiver;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.title.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{notification.title.length_range}", groups = {Created.class, Updated.class})
private String title;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.subject.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{notification.subject.length_range}", groups = {Created.class, Updated.class})
private String subject;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.status.not_blank}", groups = {Created.class})
@Size(min = 1, max = 30, message = "{notification.status.length_range}", groups = {Created.class, Updated.class})
private String status;
@Schema(description = "创建时间")
@Schema(description = "创建时间")
private Long createTime;
@Schema(description = "操作人", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "操作人", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.operator.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{notification.operator.length_range}", groups = {Created.class, Updated.class})
private String operator;
@Schema(description = "操作", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "操作", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.operation.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{notification.operation.length_range}", groups = {Created.class, Updated.class})
private String operation;
@Schema(description = "资源ID", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "资源ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.resource_id.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{notification.resource_id.length_range}", groups = {Created.class, Updated.class})
private String resourceId;
@Schema(description = "资源类型", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "资源类型", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.resource_type.not_blank}", groups = {Created.class})
@Size(min = 1, max = 50, message = "{notification.resource_type.length_range}", groups = {Created.class, Updated.class})
private String resourceType;
@Schema(description = "资源名称", requiredMode = Schema.RequiredMode.REQUIRED)
@Schema(description = "资源名称", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.resource_name.not_blank}", groups = {Created.class})
@Size(min = 1, max = 255, message = "{notification.resource_name.length_range}", groups = {Created.class, Updated.class})
private String resourceName;
@Schema(description = "通知内容", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{notification.content.not_blank}", groups = {Created.class})
@Size(min = 1, max = 65535, message = "{notification.content.length_range}", groups = {Created.class, Updated.class})
private String content;
private static final long serialVersionUID = 1L;
public enum Column {
id("id", "id", "BIGINT", false),
type("type", "type", "VARCHAR", true),
receiver("receiver", "receiver", "VARCHAR", false),
title("title", "title", "VARCHAR", false),
subject("subject", "subject", "VARCHAR", false),
status("status", "status", "VARCHAR", true),
createTime("create_time", "createTime", "BIGINT", false),
operator("operator", "operator", "VARCHAR", true),
operation("operation", "operation", "VARCHAR", true),
resourceId("resource_id", "resourceId", "VARCHAR", false),
resourceType("resource_type", "resourceType", "VARCHAR", false),
resourceName("resource_name", "resourceName", "VARCHAR", false);
resourceName("resource_name", "resourceName", "VARCHAR", false),
content("content", "content", "LONGVARCHAR", false);
private static final String BEGINNING_DELIMITER = "`";

View File

@ -304,73 +304,73 @@ public class NotificationExample {
return (Criteria) this;
}
public Criteria andTitleIsNull() {
addCriterion("title is null");
public Criteria andSubjectIsNull() {
addCriterion("subject is null");
return (Criteria) this;
}
public Criteria andTitleIsNotNull() {
addCriterion("title is not null");
public Criteria andSubjectIsNotNull() {
addCriterion("subject is not null");
return (Criteria) this;
}
public Criteria andTitleEqualTo(String value) {
addCriterion("title =", value, "title");
public Criteria andSubjectEqualTo(String value) {
addCriterion("subject =", value, "subject");
return (Criteria) this;
}
public Criteria andTitleNotEqualTo(String value) {
addCriterion("title <>", value, "title");
public Criteria andSubjectNotEqualTo(String value) {
addCriterion("subject <>", value, "subject");
return (Criteria) this;
}
public Criteria andTitleGreaterThan(String value) {
addCriterion("title >", value, "title");
public Criteria andSubjectGreaterThan(String value) {
addCriterion("subject >", value, "subject");
return (Criteria) this;
}
public Criteria andTitleGreaterThanOrEqualTo(String value) {
addCriterion("title >=", value, "title");
public Criteria andSubjectGreaterThanOrEqualTo(String value) {
addCriterion("subject >=", value, "subject");
return (Criteria) this;
}
public Criteria andTitleLessThan(String value) {
addCriterion("title <", value, "title");
public Criteria andSubjectLessThan(String value) {
addCriterion("subject <", value, "subject");
return (Criteria) this;
}
public Criteria andTitleLessThanOrEqualTo(String value) {
addCriterion("title <=", value, "title");
public Criteria andSubjectLessThanOrEqualTo(String value) {
addCriterion("subject <=", value, "subject");
return (Criteria) this;
}
public Criteria andTitleLike(String value) {
addCriterion("title like", value, "title");
public Criteria andSubjectLike(String value) {
addCriterion("subject like", value, "subject");
return (Criteria) this;
}
public Criteria andTitleNotLike(String value) {
addCriterion("title not like", value, "title");
public Criteria andSubjectNotLike(String value) {
addCriterion("subject not like", value, "subject");
return (Criteria) this;
}
public Criteria andTitleIn(List<String> values) {
addCriterion("title in", values, "title");
public Criteria andSubjectIn(List<String> values) {
addCriterion("subject in", values, "subject");
return (Criteria) this;
}
public Criteria andTitleNotIn(List<String> values) {
addCriterion("title not in", values, "title");
public Criteria andSubjectNotIn(List<String> values) {
addCriterion("subject not in", values, "subject");
return (Criteria) this;
}
public Criteria andTitleBetween(String value1, String value2) {
addCriterion("title between", value1, value2, "title");
public Criteria andSubjectBetween(String value1, String value2) {
addCriterion("subject between", value1, value2, "subject");
return (Criteria) this;
}
public Criteria andTitleNotBetween(String value1, String value2) {
addCriterion("title not between", value1, value2, "title");
public Criteria andSubjectNotBetween(String value1, String value2) {
addCriterion("subject not between", value1, value2, "subject");
return (Criteria) this;
}

View File

@ -16,16 +16,22 @@ public interface NotificationMapper {
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);
int batchInsert(@Param("list") List<Notification> list);

View File

@ -5,7 +5,7 @@
<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="subject" jdbcType="VARCHAR" property="subject" />
<result column="status" jdbcType="VARCHAR" property="status" />
<result column="create_time" jdbcType="BIGINT" property="createTime" />
<result column="operator" jdbcType="VARCHAR" property="operator" />
@ -14,6 +14,9 @@
<result column="resource_type" jdbcType="VARCHAR" property="resourceType" />
<result column="resource_name" jdbcType="VARCHAR" property="resourceName" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.project.domain.Notification">
<result column="content" jdbcType="LONGVARCHAR" property="content" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
<foreach collection="oredCriteria" item="criteria" separator="or">
@ -73,9 +76,28 @@
</where>
</sql>
<sql id="Base_Column_List">
id, `type`, receiver, title, `status`, create_time, `operator`, `operation`, resource_id,
id, `type`, receiver, subject, `status`, create_time, `operator`, `operation`, resource_id,
resource_type, resource_name
</sql>
<sql id="Blob_Column_List">
content
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.project.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.project.domain.NotificationExample" resultMap="BaseResultMap">
select
<if test="distinct">
@ -90,9 +112,11 @@
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
<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>
@ -108,13 +132,15 @@
</delete>
<insert id="insert" parameterType="io.metersphere.project.domain.Notification">
insert into notification (id, `type`, receiver,
title, `status`, create_time,
subject, `status`, create_time,
`operator`, `operation`, resource_id,
resource_type, resource_name)
resource_type, resource_name, content
)
values (#{id,jdbcType=BIGINT}, #{type,jdbcType=VARCHAR}, #{receiver,jdbcType=VARCHAR},
#{title,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT},
#{subject,jdbcType=VARCHAR}, #{status,jdbcType=VARCHAR}, #{createTime,jdbcType=BIGINT},
#{operator,jdbcType=VARCHAR}, #{operation,jdbcType=VARCHAR}, #{resourceId,jdbcType=VARCHAR},
#{resourceType,jdbcType=VARCHAR}, #{resourceName,jdbcType=VARCHAR})
#{resourceType,jdbcType=VARCHAR}, #{resourceName,jdbcType=VARCHAR}, #{content,jdbcType=LONGVARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="io.metersphere.project.domain.Notification">
insert into notification
@ -128,8 +154,8 @@
<if test="receiver != null">
receiver,
</if>
<if test="title != null">
title,
<if test="subject != null">
subject,
</if>
<if test="status != null">
`status`,
@ -152,6 +178,9 @@
<if test="resourceName != null">
resource_name,
</if>
<if test="content != null">
content,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
@ -163,8 +192,8 @@
<if test="receiver != null">
#{receiver,jdbcType=VARCHAR},
</if>
<if test="title != null">
#{title,jdbcType=VARCHAR},
<if test="subject != null">
#{subject,jdbcType=VARCHAR},
</if>
<if test="status != null">
#{status,jdbcType=VARCHAR},
@ -187,6 +216,9 @@
<if test="resourceName != null">
#{resourceName,jdbcType=VARCHAR},
</if>
<if test="content != null">
#{content,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
<select id="countByExample" parameterType="io.metersphere.project.domain.NotificationExample" resultType="java.lang.Long">
@ -207,8 +239,8 @@
<if test="record.receiver != null">
receiver = #{record.receiver,jdbcType=VARCHAR},
</if>
<if test="record.title != null">
title = #{record.title,jdbcType=VARCHAR},
<if test="record.subject != null">
subject = #{record.subject,jdbcType=VARCHAR},
</if>
<if test="record.status != null">
`status` = #{record.status,jdbcType=VARCHAR},
@ -231,17 +263,38 @@
<if test="record.resourceName != null">
resource_name = #{record.resourceName,jdbcType=VARCHAR},
</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},
subject = #{record.subject,jdbcType=VARCHAR},
`status` = #{record.status,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
`operator` = #{record.operator,jdbcType=VARCHAR},
`operation` = #{record.operation,jdbcType=VARCHAR},
resource_id = #{record.resourceId,jdbcType=VARCHAR},
resource_type = #{record.resourceType,jdbcType=VARCHAR},
resource_name = #{record.resourceName,jdbcType=VARCHAR},
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},
subject = #{record.subject,jdbcType=VARCHAR},
`status` = #{record.status,jdbcType=VARCHAR},
create_time = #{record.createTime,jdbcType=BIGINT},
`operator` = #{record.operator,jdbcType=VARCHAR},
@ -262,8 +315,8 @@
<if test="receiver != null">
receiver = #{receiver,jdbcType=VARCHAR},
</if>
<if test="title != null">
title = #{title,jdbcType=VARCHAR},
<if test="subject != null">
subject = #{subject,jdbcType=VARCHAR},
</if>
<if test="status != null">
`status` = #{status,jdbcType=VARCHAR},
@ -286,14 +339,32 @@
<if test="resourceName != null">
resource_name = #{resourceName,jdbcType=VARCHAR},
</if>
<if test="content != null">
content = #{content,jdbcType=LONGVARCHAR},
</if>
</set>
where id = #{id,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.project.domain.Notification">
update notification
set `type` = #{type,jdbcType=VARCHAR},
receiver = #{receiver,jdbcType=VARCHAR},
subject = #{subject,jdbcType=VARCHAR},
`status` = #{status,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
`operator` = #{operator,jdbcType=VARCHAR},
`operation` = #{operation,jdbcType=VARCHAR},
resource_id = #{resourceId,jdbcType=VARCHAR},
resource_type = #{resourceType,jdbcType=VARCHAR},
resource_name = #{resourceName,jdbcType=VARCHAR},
content = #{content,jdbcType=LONGVARCHAR}
where id = #{id,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.project.domain.Notification">
update notification
set `type` = #{type,jdbcType=VARCHAR},
receiver = #{receiver,jdbcType=VARCHAR},
title = #{title,jdbcType=VARCHAR},
subject = #{subject,jdbcType=VARCHAR},
`status` = #{status,jdbcType=VARCHAR},
create_time = #{createTime,jdbcType=BIGINT},
`operator` = #{operator,jdbcType=VARCHAR},
@ -305,14 +376,15 @@
</update>
<insert id="batchInsert" parameterType="map">
insert into notification
(id, `type`, receiver, title, `status`, create_time, `operator`, `operation`, resource_id,
resource_type, resource_name)
(id, `type`, receiver, subject, `status`, create_time, `operator`, `operation`, resource_id,
resource_type, resource_name, content)
values
<foreach collection="list" item="item" separator=",">
(#{item.id,jdbcType=BIGINT}, #{item.type,jdbcType=VARCHAR}, #{item.receiver,jdbcType=VARCHAR},
#{item.title,jdbcType=VARCHAR}, #{item.status,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT},
#{item.subject,jdbcType=VARCHAR}, #{item.status,jdbcType=VARCHAR}, #{item.createTime,jdbcType=BIGINT},
#{item.operator,jdbcType=VARCHAR}, #{item.operation,jdbcType=VARCHAR}, #{item.resourceId,jdbcType=VARCHAR},
#{item.resourceType,jdbcType=VARCHAR}, #{item.resourceName,jdbcType=VARCHAR})
#{item.resourceType,jdbcType=VARCHAR}, #{item.resourceName,jdbcType=VARCHAR}, #{item.content,jdbcType=LONGVARCHAR}
)
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
@ -334,8 +406,8 @@
<if test="'receiver'.toString() == column.value">
#{item.receiver,jdbcType=VARCHAR}
</if>
<if test="'title'.toString() == column.value">
#{item.title,jdbcType=VARCHAR}
<if test="'subject'.toString() == column.value">
#{item.subject,jdbcType=VARCHAR}
</if>
<if test="'status'.toString() == column.value">
#{item.status,jdbcType=VARCHAR}
@ -358,6 +430,9 @@
<if test="'resource_name'.toString() == column.value">
#{item.resourceName,jdbcType=VARCHAR}
</if>
<if test="'content'.toString() == column.value">
#{item.content,jdbcType=LONGVARCHAR}
</if>
</foreach>
)
</foreach>

View File

@ -271,29 +271,32 @@ CREATE TABLE IF NOT EXISTS message_task_blob
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '消息通知任务大字段';
CREATE TABLE IF NOT EXISTS notification
(
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID',
`type` VARCHAR(30) NOT NULL COMMENT '通知类型',
`receiver` VARCHAR(50) NOT NULL COMMENT '接收人',
`title` VARCHAR(255) NOT NULL COMMENT '标题',
`status` VARCHAR(30) NOT NULL COMMENT '状态',
`create_time` BIGINT NOT NULL COMMENT '创建时间',
`operator` VARCHAR(50) NOT NULL COMMENT '操作人',
`operation` VARCHAR(50) NOT NULL COMMENT '操作',
`resource_id` VARCHAR(50) NOT NULL COMMENT '资源ID',
`resource_type` VARCHAR(50) NOT NULL COMMENT '资源类型',
`resource_name` VARCHAR(255) NOT NULL COMMENT '资源名称',
PRIMARY KEY (id)
CREATE TABLE IF NOT EXISTS notification(
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID' ,
`type` VARCHAR(30) NOT NULL COMMENT '通知类型' ,
`receiver` VARCHAR(50) NOT NULL COMMENT '接收人' ,
`subject` VARCHAR(255) NOT NULL COMMENT '标题' ,
`status` VARCHAR(30) NOT NULL COMMENT '状态' ,
`create_time` BIGINT NOT NULL COMMENT '创建时间' ,
`operator` VARCHAR(50) NOT NULL COMMENT '操作人' ,
`operation` VARCHAR(50) NOT NULL COMMENT '操作' ,
`resource_id` VARCHAR(50) NOT NULL COMMENT '资源ID' ,
`resource_type` VARCHAR(50) NOT NULL COMMENT '资源类型' ,
`resource_name` VARCHAR(255) NOT NULL COMMENT '资源名称' ,
`content` TEXT NOT NULL COMMENT '通知内容' ,
PRIMARY KEY (id)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci COMMENT = '消息通知';
DEFAULT CHARSET = utf8mb4
COLLATE = utf8mb4_general_ci
COMMENT = '消息通知';
CREATE INDEX idx_receiver ON notification (`receiver`);
CREATE INDEX idx_receiver_type ON notification (`receiver`, `type`);
CREATE INDEX idx_notification_create_time ON notification (`create_time`);
CREATE INDEX idx_receiver ON notification(receiver);
CREATE INDEX idx_create_time ON notification(create_time);
CREATE INDEX idx_subject ON notification(subject);
CREATE INDEX idx_resource_id ON notification(resource_id);
CREATE INDEX idx_resource_type ON notification(resource_type);
CREATE INDEX idx_operator ON notification(operator);
CREATE TABLE IF NOT EXISTS project_robot(

View File

@ -586,6 +586,38 @@ Insert into message_task(id, event, receiver, project_robot_id, task_type, test_
VALUES (@schedule_close_id, 'CLOSE', 'CREATE_USER', @robot_in_site_id, 'SCHEDULE_TASK', 'NONE', '100001100001', false, 'admin', unix_timestamp() * 1000, 'admin', unix_timestamp() * 1000, true, true, 'message.title.schedule_task_close');
INSERT INTO message_task_blob(id, template) VALUES (@schedule_close_id, 'message.schedule_task_close');
-- 初始化内部at 消息通知
SET @case_comment_at_id = UUID_SHORT();
Insert into message_task(id, event, receiver, project_robot_id, task_type, test_id, project_id, enable, create_user, create_time, update_user, update_time, use_default_template, use_default_subject, subject)
VALUES (@case_comment_at_id, 'AT', 'NONE', @robot_in_site_id, 'FUNCTIONAL_CASE_TASK', 'NONE', '100001100001', true, 'admin', unix_timestamp() * 1000, 'admin', unix_timestamp() * 1000, true, true, 'message.title.functional_case_task_comment');
INSERT INTO message_task_blob(id, template) VALUES (@case_comment_at_id, 'message.functional_case_task_at_comment');
SET @case_comment_reply_id = UUID_SHORT();
Insert into message_task(id, event, receiver, project_robot_id, task_type, test_id, project_id, enable, create_user, create_time, update_user, update_time, use_default_template, use_default_subject, subject)
VALUES (@case_comment_reply_id, 'REPLY', 'NONE', @robot_in_site_id, 'FUNCTIONAL_CASE_TASK', 'NONE', '100001100001', true, 'admin', unix_timestamp() * 1000, 'admin', unix_timestamp() * 1000, true, true, 'message.title.functional_case_task_comment');
INSERT INTO message_task_blob(id, template) VALUES (@case_comment_reply_id, 'message.functional_case_task_reply_comment');
SET @case_review_at_id = UUID_SHORT();
Insert into message_task(id, event, receiver, project_robot_id, task_type, test_id, project_id, enable, create_user, create_time, update_user, update_time, use_default_template, use_default_subject, subject)
VALUES (@case_review_at_id, 'REVIEW_AT', 'NONE', @robot_in_site_id, 'FUNCTIONAL_CASE_TASK', 'NONE', '100001100001', true, 'admin', unix_timestamp() * 1000, 'admin', unix_timestamp() * 1000, true, true, 'message.title.functional_case_task_review_at');
INSERT INTO message_task_blob(id, template) VALUES (@case_review_at_id, 'message.functional_case_task_review_at');
SET @case_plan_at_id = UUID_SHORT();
Insert into message_task(id, event, receiver, project_robot_id, task_type, test_id, project_id, enable, create_user, create_time, update_user, update_time, use_default_template, use_default_subject, subject)
VALUES (@case_plan_at_id, 'EXECUTE_AT', 'NONE', @robot_in_site_id, 'FUNCTIONAL_CASE_TASK', 'NONE', '100001100001', true, 'admin', unix_timestamp() * 1000, 'admin', unix_timestamp() * 1000, true, true, 'message.title.functional_case_task_execute_at');
INSERT INTO message_task_blob(id, template) VALUES (@case_plan_at_id, 'message.functional_case_task_plan_at');
SET @bug_comment_at_id = UUID_SHORT();
Insert into message_task(id, event, receiver, project_robot_id, task_type, test_id, project_id, enable, create_user, create_time, update_user, update_time, use_default_template, use_default_subject, subject)
VALUES (@bug_comment_at_id, 'AT', 'NONE', @robot_in_site_id, 'BUG_TASK', 'NONE', '100001100001', true, 'admin', unix_timestamp() * 1000, 'admin', unix_timestamp() * 1000, true, true, 'message.title.bug_task_comment');
INSERT INTO message_task_blob(id, template) VALUES (@bug_comment_at_id, 'message.bug_task_at_comment');
SET @bug_comment_reply_id = UUID_SHORT();
Insert into message_task(id, event, receiver, project_robot_id, task_type, test_id, project_id, enable, create_user, create_time, update_user, update_time, use_default_template, use_default_subject, subject)
VALUES (@bug_comment_reply_id, 'REPLY', 'NONE', @robot_in_site_id, 'BUG_TASK', 'NONE', '100001100001', true, 'admin', unix_timestamp() * 1000, 'admin', unix_timestamp() * 1000, true, true, 'message.title.bug_task_comment');
INSERT INTO message_task_blob(id, template) VALUES (@bug_comment_reply_id, 'message.bug_task_reply_comment');
-- 初始化定时任务数据
INSERT INTO schedule(`id`, `key`, `type`, `value`, `job`, `enable`, `resource_id`, `create_user`, `create_time`, `update_time`, `project_id`, `name`, `config`)

View File

@ -212,5 +212,7 @@ public class PermissionConstants {
/*------ start: FUNCTIONAL_CASE ------*/
public static final String FUNCTIONAL_CASE_READ_ADD = "FUNCTIONAL_CASE:READ+ADD";
public static final String FUNCTIONAL_CASE_COMMENT_READ_ADD = "FUNCTIONAL_CASE_COMMENT:READ+ADD";
/*------ end: FUNCTIONAL_CASE ------*/
}

View File

@ -118,4 +118,6 @@ case_review_follow.review_id.not_blank=评审ID不能为空
case_review_follow.follow_id.not_blank=关注人不能为空
#moduleCustomFieldTestCase
custom_field_test_case.resource_id.not_blank=资源ID不能为空
custom_field_test_case.field_id.not_blank=字段ID不能为空
custom_field_test_case.field_id.not_blank=字段ID不能为空
#comment
case_comment.case_is_null=功能用例不存在

View File

@ -135,4 +135,6 @@ case_review_follow.follow_id.not_blank=follower cannot be empty
#moduleCustomFieldTestCase
custom_field_test_case.resource_id.not_blank=Resource ID cannot be empty
custom_field_test_case.field_id.not_blank=Field ID cannot be empty
default_template_not_found=Default template not found
default_template_not_found=Default template not found
#comment
case_comment.case_is_null=Function use case does not exist

View File

@ -135,4 +135,6 @@ case_review_follow.follow_id.not_blank=关注人不能为空
#moduleCustomFieldTestCase
custom_field_test_case.resource_id.not_blank=资源ID不能为空
custom_field_test_case.field_id.not_blank=字段ID不能为空
default_template_not_found=默认模板不存在
default_template_not_found=默认模板不存在
#comment
case_comment.case_is_null=功能用例不存在

View File

@ -135,4 +135,6 @@ case_review_follow.follow_id.not_blank=關注人不能為空
#moduleCustomFieldTestCase
custom_field_test_case.resource_id.not_blank=資源ID不能為空
custom_field_test_case.field_id.not_blank=字段ID不能為空
default_template_not_found=默認模板不存在
default_template_not_found=默認模板不存在
#comment
case_comment.case_is_null=功能用例不存在

View File

@ -0,0 +1,37 @@
package io.metersphere.functional.controller;
import io.metersphere.functional.domain.FunctionalCaseComment;
import io.metersphere.functional.request.FunctionalCaseCommentRequest;
import io.metersphere.functional.service.FunctionalCaseCommentService;
import io.metersphere.functional.service.FunctionalCaseNoticeService;
import io.metersphere.sdk.constants.PermissionConstants;
import io.metersphere.system.notice.annotation.SendNotice;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.utils.SessionUtils;
import io.metersphere.validation.groups.Created;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Tag(name = "用例管理-功能用例-用例评论")
@RestController
@RequestMapping("/functional/case/comment")
public class FunctionalCaseCommentController {
@Resource
private FunctionalCaseCommentService functionalCaseCommentService;
@PostMapping("/save")
@Operation(summary = "用例管理-功能用例-用例评论-创建评论")
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_COMMENT_READ_ADD)
@SendNotice(taskType = NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK, event = NoticeConstants.Event.AT, target = "#targetClass.getRelatedUsers(#functionalCaseCommentRequest)", targetClass = FunctionalCaseNoticeService.class)
public FunctionalCaseComment saveComment(@Validated({Created.class}) @RequestBody FunctionalCaseCommentRequest functionalCaseCommentRequest) {
return functionalCaseCommentService.saveComment(functionalCaseCommentRequest, SessionUtils.getUserId());
}
}

View File

@ -0,0 +1,5 @@
package io.metersphere.functional.dto;
public enum CommentEnum {
CASE, TEST_PLAN, CASE_REVIEW
}

View File

@ -0,0 +1,13 @@
package io.metersphere.functional.dto;
import io.metersphere.functional.domain.FunctionalCase;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class FunctionalCaseDTO extends FunctionalCase {
@Schema(description = "评论@的人, 多个以';'隔开")
private String relatedUsers;
}

View File

@ -0,0 +1,30 @@
package io.metersphere.functional.request;
import io.metersphere.validation.groups.Created;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = false)
public class FunctionalCaseCommentRequest {
@Schema(description = "功能用例ID", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case_comment.case_id.not_blank}", groups = {Created.class})
private String caseId;
@Schema(description = "评论@的人, 多个以';'隔开")
private String notifier;
@Schema(description = "回复人")
private String replyUser;
@Schema(description = "父评论ID")
private String parentId;
@Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED)
@NotBlank(message = "{functional_case_comment.content.not_blank}", groups = {Created.class})
private String content;
}

View File

@ -1,7 +1,61 @@
package io.metersphere.functional.service;
import io.metersphere.functional.domain.FunctionalCase;
import io.metersphere.functional.domain.FunctionalCaseComment;
import io.metersphere.functional.dto.CommentEnum;
import io.metersphere.functional.mapper.FunctionalCaseCommentMapper;
import io.metersphere.functional.mapper.FunctionalCaseMapper;
import io.metersphere.functional.request.FunctionalCaseCommentRequest;
import io.metersphere.sdk.exception.MSException;
import io.metersphere.sdk.util.Translator;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
/**
* @author guoyuqi
*/
@Service
public class FunctionalCaseCommentService {
@Resource
private FunctionalCaseCommentMapper functionalCaseCommentMapper;
@Resource
private FunctionalCaseMapper functionalCaseMapper;
/**
* 新增评论
* @param functionalCaseCommentRequest functionalCaseCommentDTO
* @param userId 当前操作用户
* @return FunctionalCaseComment
*/
public FunctionalCaseComment saveComment(FunctionalCaseCommentRequest functionalCaseCommentRequest, String userId) {
checkCase(functionalCaseCommentRequest);
FunctionalCaseComment functionalCaseComment = new FunctionalCaseComment();
functionalCaseComment.setId(IDGenerator.nextStr());
functionalCaseComment.setCaseId(functionalCaseCommentRequest.getCaseId());
functionalCaseComment.setContent(functionalCaseCommentRequest.getContent());
functionalCaseComment.setCreateUser(userId);
functionalCaseComment.setCreateTime(System.currentTimeMillis());
functionalCaseComment.setUpdateTime(System.currentTimeMillis());
functionalCaseComment.setType(CommentEnum.CASE.toString());
if (StringUtils.isNotBlank(functionalCaseCommentRequest.getNotifier())) {
functionalCaseComment.setNotifier(functionalCaseCommentRequest.getNotifier());
}
if (StringUtils.isNotBlank(functionalCaseCommentRequest.getParentId())) {
functionalCaseComment.setParentId(functionalCaseCommentRequest.getParentId());
}
functionalCaseCommentMapper.insert(functionalCaseComment);
return functionalCaseComment;
}
private void checkCase(FunctionalCaseCommentRequest functionalCaseCommentRequest) {
FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseCommentRequest.getCaseId());
if (functionalCase ==null) {
throw new MSException(Translator.get("case_comment.case_is_null"));
}
}
}

View File

@ -0,0 +1,29 @@
package io.metersphere.functional.service;
import io.metersphere.functional.domain.FunctionalCase;
import io.metersphere.functional.dto.FunctionalCaseDTO;
import io.metersphere.functional.mapper.FunctionalCaseMapper;
import io.metersphere.functional.request.FunctionalCaseCommentRequest;
import io.metersphere.sdk.util.BeanUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@Service
public class FunctionalCaseNoticeService {
@Resource
private FunctionalCaseMapper functionalCaseMapper;
public FunctionalCaseDTO getRelatedUsers(FunctionalCaseCommentRequest functionalCaseCommentRequest){
FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(functionalCaseCommentRequest.getCaseId());
FunctionalCaseDTO functionalCaseDTO = new FunctionalCaseDTO();
if (functionalCase!=null) {
BeanUtils.copyBean(functionalCaseDTO,functionalCase);
}
functionalCaseDTO.setRelatedUsers(functionalCaseCommentRequest.getNotifier());
return functionalCaseDTO;
}
}

View File

@ -0,0 +1,136 @@
package io.metersphere.functional.controller;
import com.jayway.jsonpath.JsonPath;
import io.metersphere.functional.domain.FunctionalCaseComment;
import io.metersphere.functional.request.FunctionalCaseCommentRequest;
import io.metersphere.project.domain.Notification;
import io.metersphere.project.domain.NotificationExample;
import io.metersphere.project.mapper.NotificationMapper;
import io.metersphere.sdk.constants.SessionConstants;
import io.metersphere.sdk.util.JSON;
import io.metersphere.system.controller.handler.ResultHolder;
import io.metersphere.system.notice.constants.NoticeConstants;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.*;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import java.nio.charset.StandardCharsets;
import java.util.List;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@AutoConfigureMockMvc
public class FunctionalCaseCommentControllerTests {
@Resource
private MockMvc mockMvc;
@Resource
private NotificationMapper notificationMapper;
public static final String SAVE_URL = "/functional/case/comment/save";
private static String sessionId;
private static String csrfToken;
private static String projectId = "100001100001";
@Test
@Order(0)
@Sql(scripts = {"/dml/init_case_comment.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void login() throws Exception {
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/login")
.content("{\"username\":\"default-project-member-user-guo\",\"password\":\"metersphere\"}")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON))
.andReturn();
sessionId = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.sessionId");
csrfToken = JsonPath.read(mvcResult.getResponse().getContentAsString(), "$.data.csrfToken");
}
@Test
@Order(1)
public void saveCommentSuccess() throws Exception {
FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest();
functionalCaseCommentRequest.setCaseId("xiaomeinvGTest");
functionalCaseCommentRequest.setNotifier("default-project-member-user-guo-1");
functionalCaseCommentRequest.setContent("评论你好");
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(SAVE_URL).header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.header(SessionConstants.CURRENT_PROJECT, projectId)
.content(JSON.toJSONString(functionalCaseCommentRequest))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
String contentAsString = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class);
FunctionalCaseComment functionalCaseComment = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalCaseComment.class);
NotificationExample notificationExample = new NotificationExample();
notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTest").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK);
List<Notification> notifications = notificationMapper.selectByExampleWithBLOBs(notificationExample);
Assertions.assertTrue(notifications.size() > 0);
Assertions.assertTrue(StringUtils.equals(notifications.get(0).getReceiver(), "default-project-member-user-guo-1"));
System.out.println(notifications.get(0).getContent());
Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getCaseId(), "xiaomeinvGTest"));
Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getNotifier(), "default-project-member-user-guo-1"));
Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getContent(), "评论你好"));
}
@Test
@Order(2)
public void saveCommentFalse() throws Exception {
FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest();
functionalCaseCommentRequest.setCaseId("xiaomeinvGTestNo");
functionalCaseCommentRequest.setNotifier("default-project-member-user-guo-1");
functionalCaseCommentRequest.setContent("评论你好");
mockMvc.perform(MockMvcRequestBuilders.post(SAVE_URL).header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.header(SessionConstants.CURRENT_PROJECT, projectId)
.content(JSON.toJSONString(functionalCaseCommentRequest))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is5xxServerError())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
NotificationExample notificationExample = new NotificationExample();
notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTestNo").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK);
List<Notification> notifications = notificationMapper.selectByExample(notificationExample);
Assertions.assertTrue(CollectionUtils.isEmpty(notifications));
}
@Test
@Order(3)
public void saveCommentExcludeSelfSuccess() throws Exception {
FunctionalCaseCommentRequest functionalCaseCommentRequest = new FunctionalCaseCommentRequest();
functionalCaseCommentRequest.setCaseId("xiaomeinvGTest");
functionalCaseCommentRequest.setNotifier("default-project-member-user-guo");
functionalCaseCommentRequest.setContent("这个好");
MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post(SAVE_URL).header(SessionConstants.HEADER_TOKEN, sessionId)
.header(SessionConstants.CSRF_TOKEN, csrfToken)
.header(SessionConstants.CURRENT_PROJECT, projectId)
.content(JSON.toJSONString(functionalCaseCommentRequest))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andReturn();
String contentAsString = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
ResultHolder resultHolder = JSON.parseObject(contentAsString, ResultHolder.class);
FunctionalCaseComment functionalCaseComment = JSON.parseObject(JSON.toJSONString(resultHolder.getData()), FunctionalCaseComment.class);
NotificationExample notificationExample = new NotificationExample();
notificationExample.createCriteria().andResourceIdEqualTo("xiaomeinvGTest").andResourceTypeEqualTo(NoticeConstants.TaskType.FUNCTIONAL_CASE_TASK).andReceiverEqualTo("default-project-member-user-guo");
List<Notification> notifications = notificationMapper.selectByExample(notificationExample);
Assertions.assertTrue(CollectionUtils.isEmpty(notifications));
Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getCaseId(), "xiaomeinvGTest"));
Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getNotifier(), "default-project-member-user-guo"));
Assertions.assertTrue(StringUtils.equals(functionalCaseComment.getContent(), "这个好"));
}
}

View File

@ -0,0 +1,41 @@
INSERT INTO functional_case(id, num, module_id, project_id, template_id, name, review_status, tags, case_edit_type, pos,
version_id, ref_id, last_execute_result, deleted, public_case, latest, create_user,
update_user, delete_user, create_time, update_time, delete_time)
VALUES ('xiaomeinvGTest', 1000001, 'test_guo', '100001100001', 'test_guo', '郭雨琦测试', 'UN_REVIEWED', null, 'text',
10001, '111', 'xiaomeinvGTest', 'success', false, false, true, 'gyq', 'gyq', null, 1698058347559, 1698058347559,
null);
INSERT INTO user(id, name, email, password, create_time, update_time, language, last_organization_id, phone, source,
last_project_id, create_user, update_user, deleted)
VALUES ('default-project-member-user-guo', 'default-project-member-user1', 'project-member-guo1@metersphere.io',
MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin',
'admin', 0),
('default-project-member-user-guo-1', 'default-project-member-user2', 'project-member-guo2@metersphere.io',
MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000, NULL, NUll, '', 'LOCAL', NULL, 'admin',
'admin', 0),
('default-project-member-user-guo-del', 'default-project-member-userDel',
'project-member-guo-del@metersphere.io', MD5('metersphere'), UNIX_TIMESTAMP() * 1000, UNIX_TIMESTAMP() * 1000,
NULL, NUll, '', 'LOCAL', NULL, 'admin', 'admin', 1);
INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user)
VALUES (UUID(), 'default-project-member-user-guo', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000, 'admin'),
(UUID(), 'default-project-member-user-guo-1', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000,
'admin'),
(UUID(), 'default-project-member-user-guo-del', 'org_member', '100001', '100001', UNIX_TIMESTAMP() * 1000,
'admin');
INSERT INTO user_role_relation (id, user_id, role_id, source_id, organization_id, create_time, create_user)
VALUES (UUID(), 'default-project-member-user-guo', 'project_admin', '100001100001', '100001', UNIX_TIMESTAMP() * 1000,
'admin'),
(UUID(), 'default-project-member-user-guo-1', 'project_admin', '100001100001', '100001', UNIX_TIMESTAMP() * 1000,
'admin'),
(UUID(), 'default-project-member-user-guo-del', 'project_admin', '100001100001', '100001',
UNIX_TIMESTAMP() * 1000, 'admin');
INSERT INTO user_role_permission(id, role_id, permission_id)
VALUES ('user_role_guo_permission1', 'project_admin', 'FUNCTIONAL_CASE_COMMENT:READ+ADD'),
('user_role_guo_permission2', 'project_admin', 'FUNCTIONAL_CASE:READ+ADD');

View File

@ -34,7 +34,4 @@ public @interface SendNotice {
*/
String context() default "";
String successContext() default "";
String failedContext() default "";
}

View File

@ -137,19 +137,22 @@ public abstract class AbstractNoticeSender implements NoticeSender {
default -> toUsers.add(new Receiver(userId, NotificationConstants.Type.MENTIONED_ME.name()));
}
//TODO接口同步时通知的接收人特殊处理v2接口同步的通知v3这里待讨论
//处理评论人
if (messageDetail.getTaskType().contains("AT_COMMENT")) {
if (CollectionUtils.isNotEmpty(noticeModel.getRelatedUsers())) {
for (String relatedUser : noticeModel.getRelatedUsers()) {
toUsers.add(new Receiver(relatedUser, NotificationConstants.Type.MENTIONED_ME.name()));
}
}
//处理评论人
if (event.contains(NoticeConstants.Event.AT) || event.contains(NoticeConstants.Event.REPLAY)) {
if (CollectionUtils.isNotEmpty(noticeModel.getRelatedUsers())) {
for (String relatedUser : noticeModel.getRelatedUsers()) {
toUsers.add(new Receiver(relatedUser, NotificationConstants.Type.MENTIONED_ME.name()));
}
}
}
// 去重复
return toUsers.stream()
.distinct()
.collect(Collectors.toList());
List<String> userIds = toUsers.stream().map(Receiver::getUserId).distinct().toList();
List<User> users = getUsers(userIds);
List<String> realUserIds = users.stream().map(User::getId).toList();
return toUsers.stream().filter(t -> realUserIds.contains(t.getUserId())).toList();
}
private List<Receiver> handleFollows(MessageDetail messageDetail, NoticeModel noticeModel) {

View File

@ -3,8 +3,8 @@ package io.metersphere.system.notice.sender;
import io.metersphere.sdk.dto.BaseSystemConfigDTO;
import io.metersphere.sdk.dto.SessionUser;
import io.metersphere.system.notice.annotation.SendNotice;
import io.metersphere.system.notice.NoticeModel;
import io.metersphere.system.notice.annotation.SendNotice;
import io.metersphere.system.notice.constants.NoticeConstants;
import io.metersphere.system.notice.utils.MessageTemplateUtils;
import io.metersphere.system.service.NoticeSendService;
@ -14,9 +14,7 @@ import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
@Component
public class AfterReturningNoticeSendService {
@ -45,6 +43,8 @@ public class AfterReturningNoticeSendService {
String subject = getSubject(sendNotice);
List<String> relatedUsers = getRelatedUsers(resource.get("relatedUsers"));
NoticeModel noticeModel = NoticeModel.builder()
.operator(sessionUser.getId())
.context(context)
@ -53,16 +53,27 @@ public class AfterReturningNoticeSendService {
.event(sendNotice.event())
.status((String) paramMap.get("status"))
.excludeSelf(true)
.relatedUsers(relatedUsers)
.build();
noticeSendService.send(sendNotice.taskType(), noticeModel);
}
}
private List<String> getRelatedUsers(Object relatedUsers) {
String relatedUser = (String) relatedUsers;
List<String> relatedUserList = new ArrayList<>();
if (StringUtils.isNotBlank(relatedUser)) {
relatedUserList = Arrays.asList(relatedUser.split(","));
}
return relatedUserList;
}
private String getSubject(SendNotice sendNotice) {
Map<String, String> defaultTemplateTitleMap = MessageTemplateUtils.getDefaultTemplateSubjectMap();
return defaultTemplateTitleMap.get(sendNotice.taskType() + "_" + sendNotice.event());
}
/**
* 有些默认的值避免通知里出现 ${key}
*/

View File

@ -9,11 +9,13 @@ import io.metersphere.system.notice.Receiver;
import io.metersphere.system.notice.sender.AbstractNoticeSender;
import io.metersphere.system.service.NotificationService;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.uid.IDGenerator;
import jakarta.annotation.Resource;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -26,19 +28,25 @@ public class InSiteNoticeSender extends AbstractNoticeSender {
public void sendAnnouncement(MessageDetail messageDetail, NoticeModel noticeModel, String context) {
List<Receiver> receivers = noticeModel.getReceivers();
// 排除自己
if (noticeModel.isExcludeSelf()) {
receivers.removeIf(u -> StringUtils.equals(u.getUserId(), noticeModel.getOperator()));
List<Receiver> realReceivers = new ArrayList<>();
for (Receiver receiver : receivers) {
if (!StringUtils.equals(receiver.getUserId(), noticeModel.getOperator())) {
realReceivers.add(receiver);
}
}
if (CollectionUtils.isEmpty(receivers)) {
if (CollectionUtils.isEmpty(realReceivers)) {
LogUtils.info("发送人是自己不发");
return;
}
LogUtils.info("发送站内通知: {}", receivers);
receivers.forEach(receiver -> {
LogUtils.info("发送站内通知: {}", realReceivers);
realReceivers.forEach(receiver -> {
Map<String, Object> paramMap = noticeModel.getParamMap();
Notification notification = new Notification();
notification.setTitle(noticeModel.getSubject());
notification.setId(IDGenerator.nextNum());
notification.setSubject(noticeModel.getSubject());
notification.setOperator(noticeModel.getOperator());
notification.setOperation(noticeModel.getEvent());
notification.setResourceId((String) paramMap.get("id"));
@ -53,6 +61,7 @@ public class InSiteNoticeSender extends AbstractNoticeSender {
notification.setStatus(NotificationConstants.Status.UNREAD.name());
notification.setCreateTime(System.currentTimeMillis());
notification.setReceiver(receiver.getUserId());
notification.setContent(context);
notificationService.sendAnnouncement(notification);
});
}

View File

@ -8,6 +8,7 @@ import io.metersphere.system.notice.MessageDetail;
import io.metersphere.sdk.util.LogUtils;
import io.metersphere.system.notice.utils.MessageTemplateUtils;
import jakarta.annotation.Resource;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@ -54,6 +55,9 @@ public class MessageDetailService {
.andProjectIdEqualTo(projectId).andEnableEqualTo(true);
example.setOrderByClause("create_time asc");
List<MessageTask> messageTaskLists = messageTaskMapper.selectByExample(example);
if (CollectionUtils.isEmpty(messageTaskLists)) {
return new ArrayList<>();
}
getMessageDetails(messageDetails, messageTaskLists);
return messageDetails.stream()
.sorted(Comparator.comparing(MessageDetail::getCreateTime, Comparator.nullsLast(Long::compareTo)).reversed())
@ -93,7 +97,9 @@ public class MessageDetailService {
return;
}
messageDetail.setType(projectRobot.getPlatform());
messageDetail.setWebhook(projectRobot.getWebhook());
if (StringUtils.isNotBlank(projectRobot.getWebhook())) {
messageDetail.setWebhook(projectRobot.getWebhook());
}
if (StringUtils.isNotBlank(messageTask.getTestId())) {
messageDetail.setTestId(messageTask.getTestId());
}