feat(用例管理): 创建用例接口
This commit is contained in:
parent
1d6a8b4141
commit
b6d9a1214d
|
@ -42,7 +42,7 @@ public class FunctionalCase implements Serializable {
|
||||||
@Size(min = 1, max = 255, message = "{functional_case.name.length_range}", groups = {Created.class, Updated.class})
|
@Size(min = 1, max = 255, message = "{functional_case.name.length_range}", groups = {Created.class, Updated.class})
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
@Schema(description = "评审状态:未开始/进行中/已完成/已结束", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "评审状态:未评审/评审中/通过/不通过/重新提审", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@NotBlank(message = "{functional_case.review_status.not_blank}", groups = {Created.class})
|
@NotBlank(message = "{functional_case.review_status.not_blank}", groups = {Created.class})
|
||||||
@Size(min = 1, max = 64, message = "{functional_case.review_status.length_range}", groups = {Created.class, Updated.class})
|
@Size(min = 1, max = 64, message = "{functional_case.review_status.length_range}", groups = {Created.class, Updated.class})
|
||||||
private String reviewStatus;
|
private String reviewStatus;
|
||||||
|
|
|
@ -25,16 +25,12 @@ public class FunctionalCaseCustomField implements Serializable {
|
||||||
@Schema(description = "字段值")
|
@Schema(description = "字段值")
|
||||||
private String value;
|
private String value;
|
||||||
|
|
||||||
@Schema(description = "富文本类型字段值")
|
|
||||||
private String textValue;
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public enum Column {
|
public enum Column {
|
||||||
caseId("case_id", "caseId", "VARCHAR", false),
|
caseId("case_id", "caseId", "VARCHAR", false),
|
||||||
fieldId("field_id", "fieldId", "VARCHAR", false),
|
fieldId("field_id", "fieldId", "VARCHAR", false),
|
||||||
value("value", "value", "VARCHAR", true),
|
value("value", "value", "VARCHAR", true);
|
||||||
textValue("text_value", "textValue", "LONGVARCHAR", false);
|
|
||||||
|
|
||||||
private static final String BEGINNING_DELIMITER = "`";
|
private static final String BEGINNING_DELIMITER = "`";
|
||||||
|
|
||||||
|
|
|
@ -16,22 +16,16 @@ public interface FunctionalCaseCustomFieldMapper {
|
||||||
|
|
||||||
int insertSelective(FunctionalCaseCustomField record);
|
int insertSelective(FunctionalCaseCustomField record);
|
||||||
|
|
||||||
List<FunctionalCaseCustomField> selectByExampleWithBLOBs(FunctionalCaseCustomFieldExample example);
|
|
||||||
|
|
||||||
List<FunctionalCaseCustomField> selectByExample(FunctionalCaseCustomFieldExample example);
|
List<FunctionalCaseCustomField> selectByExample(FunctionalCaseCustomFieldExample example);
|
||||||
|
|
||||||
FunctionalCaseCustomField selectByPrimaryKey(@Param("caseId") String caseId, @Param("fieldId") String fieldId);
|
FunctionalCaseCustomField selectByPrimaryKey(@Param("caseId") String caseId, @Param("fieldId") String fieldId);
|
||||||
|
|
||||||
int updateByExampleSelective(@Param("record") FunctionalCaseCustomField record, @Param("example") FunctionalCaseCustomFieldExample example);
|
int updateByExampleSelective(@Param("record") FunctionalCaseCustomField record, @Param("example") FunctionalCaseCustomFieldExample example);
|
||||||
|
|
||||||
int updateByExampleWithBLOBs(@Param("record") FunctionalCaseCustomField record, @Param("example") FunctionalCaseCustomFieldExample example);
|
|
||||||
|
|
||||||
int updateByExample(@Param("record") FunctionalCaseCustomField record, @Param("example") FunctionalCaseCustomFieldExample example);
|
int updateByExample(@Param("record") FunctionalCaseCustomField record, @Param("example") FunctionalCaseCustomFieldExample example);
|
||||||
|
|
||||||
int updateByPrimaryKeySelective(FunctionalCaseCustomField record);
|
int updateByPrimaryKeySelective(FunctionalCaseCustomField record);
|
||||||
|
|
||||||
int updateByPrimaryKeyWithBLOBs(FunctionalCaseCustomField record);
|
|
||||||
|
|
||||||
int updateByPrimaryKey(FunctionalCaseCustomField record);
|
int updateByPrimaryKey(FunctionalCaseCustomField record);
|
||||||
|
|
||||||
int batchInsert(@Param("list") List<FunctionalCaseCustomField> list);
|
int batchInsert(@Param("list") List<FunctionalCaseCustomField> list);
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
<id column="field_id" jdbcType="VARCHAR" property="fieldId" />
|
<id column="field_id" jdbcType="VARCHAR" property="fieldId" />
|
||||||
<result column="value" jdbcType="VARCHAR" property="value" />
|
<result column="value" jdbcType="VARCHAR" property="value" />
|
||||||
</resultMap>
|
</resultMap>
|
||||||
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.functional.domain.FunctionalCaseCustomField">
|
|
||||||
<result column="text_value" jdbcType="LONGVARCHAR" property="textValue" />
|
|
||||||
</resultMap>
|
|
||||||
<sql id="Example_Where_Clause">
|
<sql id="Example_Where_Clause">
|
||||||
<where>
|
<where>
|
||||||
<foreach collection="oredCriteria" item="criteria" separator="or">
|
<foreach collection="oredCriteria" item="criteria" separator="or">
|
||||||
|
@ -70,25 +67,6 @@
|
||||||
<sql id="Base_Column_List">
|
<sql id="Base_Column_List">
|
||||||
case_id, field_id, `value`
|
case_id, field_id, `value`
|
||||||
</sql>
|
</sql>
|
||||||
<sql id="Blob_Column_List">
|
|
||||||
text_value
|
|
||||||
</sql>
|
|
||||||
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomFieldExample" resultMap="ResultMapWithBLOBs">
|
|
||||||
select
|
|
||||||
<if test="distinct">
|
|
||||||
distinct
|
|
||||||
</if>
|
|
||||||
<include refid="Base_Column_List" />
|
|
||||||
,
|
|
||||||
<include refid="Blob_Column_List" />
|
|
||||||
from functional_case_custom_field
|
|
||||||
<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.functional.domain.FunctionalCaseCustomFieldExample" resultMap="BaseResultMap">
|
<select id="selectByExample" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomFieldExample" resultMap="BaseResultMap">
|
||||||
select
|
select
|
||||||
<if test="distinct">
|
<if test="distinct">
|
||||||
|
@ -103,11 +81,9 @@
|
||||||
order by ${orderByClause}
|
order by ${orderByClause}
|
||||||
</if>
|
</if>
|
||||||
</select>
|
</select>
|
||||||
<select id="selectByPrimaryKey" parameterType="map" resultMap="ResultMapWithBLOBs">
|
<select id="selectByPrimaryKey" parameterType="map" resultMap="BaseResultMap">
|
||||||
select
|
select
|
||||||
<include refid="Base_Column_List" />
|
<include refid="Base_Column_List" />
|
||||||
,
|
|
||||||
<include refid="Blob_Column_List" />
|
|
||||||
from functional_case_custom_field
|
from functional_case_custom_field
|
||||||
where case_id = #{caseId,jdbcType=VARCHAR}
|
where case_id = #{caseId,jdbcType=VARCHAR}
|
||||||
and field_id = #{fieldId,jdbcType=VARCHAR}
|
and field_id = #{fieldId,jdbcType=VARCHAR}
|
||||||
|
@ -124,10 +100,10 @@
|
||||||
</if>
|
</if>
|
||||||
</delete>
|
</delete>
|
||||||
<insert id="insert" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomField">
|
<insert id="insert" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomField">
|
||||||
insert into functional_case_custom_field (case_id, field_id, `value`,
|
insert into functional_case_custom_field (case_id, field_id, `value`
|
||||||
text_value)
|
)
|
||||||
values (#{caseId,jdbcType=VARCHAR}, #{fieldId,jdbcType=VARCHAR}, #{value,jdbcType=VARCHAR},
|
values (#{caseId,jdbcType=VARCHAR}, #{fieldId,jdbcType=VARCHAR}, #{value,jdbcType=VARCHAR}
|
||||||
#{textValue,jdbcType=LONGVARCHAR})
|
)
|
||||||
</insert>
|
</insert>
|
||||||
<insert id="insertSelective" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomField">
|
<insert id="insertSelective" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomField">
|
||||||
insert into functional_case_custom_field
|
insert into functional_case_custom_field
|
||||||
|
@ -141,9 +117,6 @@
|
||||||
<if test="value != null">
|
<if test="value != null">
|
||||||
`value`,
|
`value`,
|
||||||
</if>
|
</if>
|
||||||
<if test="textValue != null">
|
|
||||||
text_value,
|
|
||||||
</if>
|
|
||||||
</trim>
|
</trim>
|
||||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||||
<if test="caseId != null">
|
<if test="caseId != null">
|
||||||
|
@ -155,9 +128,6 @@
|
||||||
<if test="value != null">
|
<if test="value != null">
|
||||||
#{value,jdbcType=VARCHAR},
|
#{value,jdbcType=VARCHAR},
|
||||||
</if>
|
</if>
|
||||||
<if test="textValue != null">
|
|
||||||
#{textValue,jdbcType=LONGVARCHAR},
|
|
||||||
</if>
|
|
||||||
</trim>
|
</trim>
|
||||||
</insert>
|
</insert>
|
||||||
<select id="countByExample" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomFieldExample" resultType="java.lang.Long">
|
<select id="countByExample" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomFieldExample" resultType="java.lang.Long">
|
||||||
|
@ -178,24 +148,11 @@
|
||||||
<if test="record.value != null">
|
<if test="record.value != null">
|
||||||
`value` = #{record.value,jdbcType=VARCHAR},
|
`value` = #{record.value,jdbcType=VARCHAR},
|
||||||
</if>
|
</if>
|
||||||
<if test="record.textValue != null">
|
|
||||||
text_value = #{record.textValue,jdbcType=LONGVARCHAR},
|
|
||||||
</if>
|
|
||||||
</set>
|
</set>
|
||||||
<if test="_parameter != null">
|
<if test="_parameter != null">
|
||||||
<include refid="Update_By_Example_Where_Clause" />
|
<include refid="Update_By_Example_Where_Clause" />
|
||||||
</if>
|
</if>
|
||||||
</update>
|
</update>
|
||||||
<update id="updateByExampleWithBLOBs" parameterType="map">
|
|
||||||
update functional_case_custom_field
|
|
||||||
set case_id = #{record.caseId,jdbcType=VARCHAR},
|
|
||||||
field_id = #{record.fieldId,jdbcType=VARCHAR},
|
|
||||||
`value` = #{record.value,jdbcType=VARCHAR},
|
|
||||||
text_value = #{record.textValue,jdbcType=LONGVARCHAR}
|
|
||||||
<if test="_parameter != null">
|
|
||||||
<include refid="Update_By_Example_Where_Clause" />
|
|
||||||
</if>
|
|
||||||
</update>
|
|
||||||
<update id="updateByExample" parameterType="map">
|
<update id="updateByExample" parameterType="map">
|
||||||
update functional_case_custom_field
|
update functional_case_custom_field
|
||||||
set case_id = #{record.caseId,jdbcType=VARCHAR},
|
set case_id = #{record.caseId,jdbcType=VARCHAR},
|
||||||
|
@ -211,20 +168,10 @@
|
||||||
<if test="value != null">
|
<if test="value != null">
|
||||||
`value` = #{value,jdbcType=VARCHAR},
|
`value` = #{value,jdbcType=VARCHAR},
|
||||||
</if>
|
</if>
|
||||||
<if test="textValue != null">
|
|
||||||
text_value = #{textValue,jdbcType=LONGVARCHAR},
|
|
||||||
</if>
|
|
||||||
</set>
|
</set>
|
||||||
where case_id = #{caseId,jdbcType=VARCHAR}
|
where case_id = #{caseId,jdbcType=VARCHAR}
|
||||||
and field_id = #{fieldId,jdbcType=VARCHAR}
|
and field_id = #{fieldId,jdbcType=VARCHAR}
|
||||||
</update>
|
</update>
|
||||||
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomField">
|
|
||||||
update functional_case_custom_field
|
|
||||||
set `value` = #{value,jdbcType=VARCHAR},
|
|
||||||
text_value = #{textValue,jdbcType=LONGVARCHAR}
|
|
||||||
where case_id = #{caseId,jdbcType=VARCHAR}
|
|
||||||
and field_id = #{fieldId,jdbcType=VARCHAR}
|
|
||||||
</update>
|
|
||||||
<update id="updateByPrimaryKey" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomField">
|
<update id="updateByPrimaryKey" parameterType="io.metersphere.functional.domain.FunctionalCaseCustomField">
|
||||||
update functional_case_custom_field
|
update functional_case_custom_field
|
||||||
set `value` = #{value,jdbcType=VARCHAR}
|
set `value` = #{value,jdbcType=VARCHAR}
|
||||||
|
@ -233,11 +180,11 @@
|
||||||
</update>
|
</update>
|
||||||
<insert id="batchInsert" parameterType="map">
|
<insert id="batchInsert" parameterType="map">
|
||||||
insert into functional_case_custom_field
|
insert into functional_case_custom_field
|
||||||
(case_id, field_id, `value`, text_value)
|
(case_id, field_id, `value`)
|
||||||
values
|
values
|
||||||
<foreach collection="list" item="item" separator=",">
|
<foreach collection="list" item="item" separator=",">
|
||||||
(#{item.caseId,jdbcType=VARCHAR}, #{item.fieldId,jdbcType=VARCHAR}, #{item.value,jdbcType=VARCHAR},
|
(#{item.caseId,jdbcType=VARCHAR}, #{item.fieldId,jdbcType=VARCHAR}, #{item.value,jdbcType=VARCHAR}
|
||||||
#{item.textValue,jdbcType=LONGVARCHAR})
|
)
|
||||||
</foreach>
|
</foreach>
|
||||||
</insert>
|
</insert>
|
||||||
<insert id="batchInsertSelective" parameterType="map">
|
<insert id="batchInsertSelective" parameterType="map">
|
||||||
|
@ -259,9 +206,6 @@
|
||||||
<if test="'value'.toString() == column.value">
|
<if test="'value'.toString() == column.value">
|
||||||
#{item.value,jdbcType=VARCHAR}
|
#{item.value,jdbcType=VARCHAR}
|
||||||
</if>
|
</if>
|
||||||
<if test="'text_value'.toString() == column.value">
|
|
||||||
#{item.textValue,jdbcType=LONGVARCHAR}
|
|
||||||
</if>
|
|
||||||
</foreach>
|
</foreach>
|
||||||
)
|
)
|
||||||
</foreach>
|
</foreach>
|
||||||
|
|
|
@ -8,13 +8,13 @@ CREATE TABLE IF NOT EXISTS functional_case(
|
||||||
`project_id` VARCHAR(50) NOT NULL COMMENT '项目ID' ,
|
`project_id` VARCHAR(50) NOT NULL COMMENT '项目ID' ,
|
||||||
`template_id` VARCHAR(50) NOT NULL COMMENT '模板ID' ,
|
`template_id` VARCHAR(50) NOT NULL COMMENT '模板ID' ,
|
||||||
`name` VARCHAR(255) NOT NULL COMMENT '名称' ,
|
`name` VARCHAR(255) NOT NULL COMMENT '名称' ,
|
||||||
`review_status` VARCHAR(64) NOT NULL DEFAULT 'PREPARE' COMMENT '评审状态:未开始/进行中/已完成/已结束' ,
|
`review_status` VARCHAR(64) NOT NULL DEFAULT 'UN_REVIEWED' COMMENT '评审状态:未评审/评审中/通过/不通过/重新提审' ,
|
||||||
`tags` VARCHAR(1000) COMMENT '标签(JSON)' ,
|
`tags` VARCHAR(1000) COMMENT '标签(JSON)' ,
|
||||||
`case_edit_type` VARCHAR(50) NOT NULL DEFAULT 'STEP' COMMENT '编辑模式:步骤模式/文本模式' ,
|
`case_edit_type` VARCHAR(50) NOT NULL DEFAULT 'STEP' COMMENT '编辑模式:步骤模式/文本模式' ,
|
||||||
`pos` BIGINT NOT NULL DEFAULT 0 COMMENT '自定义排序,间隔5000' ,
|
`pos` BIGINT NOT NULL DEFAULT 0 COMMENT '自定义排序,间隔5000' ,
|
||||||
`version_id` VARCHAR(50) NOT NULL COMMENT '版本ID' ,
|
`version_id` VARCHAR(50) NOT NULL COMMENT '版本ID' ,
|
||||||
`ref_id` VARCHAR(50) NOT NULL COMMENT '指向初始版本ID' ,
|
`ref_id` VARCHAR(50) NOT NULL COMMENT '指向初始版本ID' ,
|
||||||
`last_execute_result` VARCHAR(64) NOT NULL DEFAULT 'PREPARE' COMMENT '最近的执行结果:未执行/通过/失败/阻塞/跳过' ,
|
`last_execute_result` VARCHAR(64) NOT NULL DEFAULT 'UN_EXECUTED' COMMENT '最近的执行结果:未执行/通过/失败/阻塞/跳过' ,
|
||||||
`deleted` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否在回收站:0-否,1-是' ,
|
`deleted` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否在回收站:0-否,1-是' ,
|
||||||
`public_case` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否是公共用例:0-否,1-是' ,
|
`public_case` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否是公共用例:0-否,1-是' ,
|
||||||
`latest` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否为最新版本:0-否,1-是' ,
|
`latest` BIT(1) NOT NULL DEFAULT 0 COMMENT '是否为最新版本:0-否,1-是' ,
|
||||||
|
|
|
@ -588,9 +588,8 @@ INSERT INTO message_task_blob(id, template) VALUES (@schedule_close_id, 'message
|
||||||
|
|
||||||
|
|
||||||
-- 初始化定时任务数据
|
-- 初始化定时任务数据
|
||||||
SET @load_report_id = UUID_SHORT();
|
|
||||||
INSERT INTO schedule(`id`, `key`, `type`, `value`, `job`, `enable`, `resource_id`, `create_user`, `create_time`, `update_time`, `project_id`, `name`, `config`)
|
INSERT INTO schedule(`id`, `key`, `type`, `value`, `job`, `enable`, `resource_id`, `create_user`, `create_time`, `update_time`, `project_id`, `name`, `config`)
|
||||||
VALUES (@load_report_id, '100001100001', 'CRON', '0 0 2 * * ?', 'io.metersphere.project.job.CleanUpReportJob', true, '100001100001', 'admin', unix_timestamp() * 1000, unix_timestamp() * 1000, '100001100001', 'Clean Report Job', NULL);
|
VALUES (UUID_SHORT(), '100001100001', 'CRON', '0 0 2 * * ?', 'io.metersphere.project.job.CleanUpReportJob', true, '100001100001', 'admin', unix_timestamp() * 1000, unix_timestamp() * 1000, '100001100001', 'Clean Report Job', NULL);
|
||||||
|
|
||||||
-- 初始化默认项目版本配置项
|
-- 初始化默认项目版本配置项
|
||||||
INSERT INTO project_application (`project_id`, `type`, `type_value`) VALUES ('100001100001', 'VERSION_ENABLE', 'FALSE');
|
INSERT INTO project_application (`project_id`, `type`, `type_value`) VALUES ('100001100001', 'VERSION_ENABLE', 'FALSE');
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
package io.metersphere.sdk.constants;
|
||||||
|
|
||||||
|
public enum FunctionalCaseExecuteResult {
|
||||||
|
UN_EXECUTED,
|
||||||
|
PASSED,
|
||||||
|
FAILED,
|
||||||
|
BLOCKED,
|
||||||
|
SKIPPED
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package io.metersphere.sdk.constants;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
|
||||||
|
public enum FunctionalCaseReviewStatus {
|
||||||
|
UN_REVIEWED,
|
||||||
|
UNDER_REVIEWED,
|
||||||
|
PASS,
|
||||||
|
UN_PASS,
|
||||||
|
RE_REVIEWED
|
||||||
|
}
|
|
@ -207,4 +207,10 @@ public class PermissionConstants {
|
||||||
public static final String PROJECT_TEMPLATE_UPDATE = "PROJECT_TEMPLATE:READ+UPDATE";
|
public static final String PROJECT_TEMPLATE_UPDATE = "PROJECT_TEMPLATE:READ+UPDATE";
|
||||||
public static final String PROJECT_TEMPLATE_DELETE = "PROJECT_TEMPLATE:READ+DELETE";
|
public static final String PROJECT_TEMPLATE_DELETE = "PROJECT_TEMPLATE:READ+DELETE";
|
||||||
/*------ end: PROJECT_TEMPLATE ------*/
|
/*------ end: PROJECT_TEMPLATE ------*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*------ start: FUNCTIONAL_CASE ------*/
|
||||||
|
public static final String FUNCTIONAL_CASE_READ_ADD = "FUNCTIONAL_CASE:READ+ADD";
|
||||||
|
/*------ end: FUNCTIONAL_CASE ------*/
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@ public class MsFileUtils {
|
||||||
public static final String PLUGIN_DIR_NAME = "plugins";
|
public static final String PLUGIN_DIR_NAME = "plugins";
|
||||||
public static final String PLUGIN_DIR = DATE_ROOT_DIR + "/" + PLUGIN_DIR_NAME;
|
public static final String PLUGIN_DIR = DATE_ROOT_DIR + "/" + PLUGIN_DIR_NAME;
|
||||||
|
|
||||||
|
public static final String FUNCTIONAL_CASE_ATTACHMENT_DIR_NAME = "functionalCaseAttachment";
|
||||||
|
public static final String FUNCTIONAL_CASE_ATTACHMENT_DIR = DATE_ROOT_DIR + "/" + FUNCTIONAL_CASE_ATTACHMENT_DIR_NAME;
|
||||||
|
|
||||||
public static void validateFileName(String... fileNames) {
|
public static void validateFileName(String... fileNames) {
|
||||||
if (fileNames != null) {
|
if (fileNames != null) {
|
||||||
for (String fileName : fileNames) {
|
for (String fileName : fileNames) {
|
||||||
|
|
|
@ -80,6 +80,9 @@ functional_case_test.test_id.length_range=The length of the test ID must be betw
|
||||||
functional_case_test.test_id.not_blank=Test ID cannot be empty
|
functional_case_test.test_id.not_blank=Test ID cannot be empty
|
||||||
functional_case_test.test_type.length_range=The length of the test type must be between 1 and 64
|
functional_case_test.test_type.length_range=The length of the test type must be between 1 and 64
|
||||||
functional_case_test.test_type.not_blank=Test type cannot be empty
|
functional_case_test.test_type.not_blank=Test type cannot be empty
|
||||||
|
#FunctionalCaseCustomField
|
||||||
|
functional_case_custom_field.case_id.not_blank=Case ID cannot be empty
|
||||||
|
functional_case_custom_field.field_id.not_blank=Field ID cannot be empty
|
||||||
#module:FunctionalCaseHistory
|
#module:FunctionalCaseHistory
|
||||||
functional_case_history.id.not_blank=ID cannot be empty
|
functional_case_history.id.not_blank=ID cannot be empty
|
||||||
functional_case_history.case_id.not_blank=Case ID cannot be empty
|
functional_case_history.case_id.not_blank=Case ID cannot be empty
|
||||||
|
@ -132,3 +135,4 @@ case_review_follow.follow_id.not_blank=follower cannot be empty
|
||||||
#module:CustomFieldTestCase
|
#module:CustomFieldTestCase
|
||||||
custom_field_test_case.resource_id.not_blank=Resource ID cannot be empty
|
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
|
custom_field_test_case.field_id.not_blank=Field ID cannot be empty
|
||||||
|
default_template_not_found=Default template not found
|
|
@ -80,6 +80,9 @@ functional_case_test.test_id.length_range=其他类型用例ID长度必须在1-5
|
||||||
functional_case_test.test_id.not_blank=其他类型用例ID不能为空
|
functional_case_test.test_id.not_blank=其他类型用例ID不能为空
|
||||||
functional_case_test.test_type.length_range=用例类型长度必须在1-64之间
|
functional_case_test.test_type.length_range=用例类型长度必须在1-64之间
|
||||||
functional_case_test.test_type.not_blank=用例类型不能为空
|
functional_case_test.test_type.not_blank=用例类型不能为空
|
||||||
|
#FunctionalCaseCustomField
|
||||||
|
functional_case_custom_field.case_id.not_blank=功能用例ID不能为空
|
||||||
|
functional_case_custom_field.field_id.not_blank=自定义字段ID不能为空
|
||||||
#module:FunctionalCaseHistory
|
#module:FunctionalCaseHistory
|
||||||
functional_case_history.id.not_blank=ID不能为空
|
functional_case_history.id.not_blank=ID不能为空
|
||||||
functional_case_history.case_id.not_blank=功能用例ID不能为空
|
functional_case_history.case_id.not_blank=功能用例ID不能为空
|
||||||
|
@ -132,3 +135,4 @@ case_review_follow.follow_id.not_blank=关注人不能为空
|
||||||
#module:CustomFieldTestCase
|
#module:CustomFieldTestCase
|
||||||
custom_field_test_case.resource_id.not_blank=资源ID不能为空
|
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不能为空
|
||||||
|
default_template_not_found=默认模板不存在
|
|
@ -80,6 +80,9 @@ functional_case_test.test_id.length_range=其他類型用例ID長度必須在1-5
|
||||||
functional_case_test.test_id.not_blank=其他類型用例ID不能為空
|
functional_case_test.test_id.not_blank=其他類型用例ID不能為空
|
||||||
functional_case_test.test_type.length_range=用例類型長度必須在1-64之間
|
functional_case_test.test_type.length_range=用例類型長度必須在1-64之間
|
||||||
functional_case_test.test_type.not_blank=用例類型不能為空
|
functional_case_test.test_type.not_blank=用例類型不能為空
|
||||||
|
#FunctionalCaseCustomField
|
||||||
|
functional_case_custom_field.case_id.not_blank=功能用例ID不能爲空
|
||||||
|
functional_case_custom_field.field_id.not_blank=自定義字段ID不能爲空
|
||||||
#module:FunctionalCaseHistory
|
#module:FunctionalCaseHistory
|
||||||
functional_case_history.id.not_blank=ID不能為空
|
functional_case_history.id.not_blank=ID不能為空
|
||||||
functional_case_history.case_id.not_blank=功能用例ID不能爲空
|
functional_case_history.case_id.not_blank=功能用例ID不能爲空
|
||||||
|
@ -132,3 +135,4 @@ case_review_follow.follow_id.not_blank=關注人不能為空
|
||||||
#module:CustomFieldTestCase
|
#module:CustomFieldTestCase
|
||||||
custom_field_test_case.resource_id.not_blank=資源ID不能為空
|
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不能為空
|
||||||
|
default_template_not_found=默認模板不存在
|
|
@ -28,6 +28,14 @@
|
||||||
<artifactId>metersphere-project-management</artifactId>
|
<artifactId>metersphere-project-management</artifactId>
|
||||||
<version>${revision}</version>
|
<version>${revision}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.metersphere</groupId>
|
||||||
|
<artifactId>metersphere-system-setting</artifactId>
|
||||||
|
<version>${revision}</version>
|
||||||
|
<classifier>tests</classifier>
|
||||||
|
<type>test-jar</type>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package io.metersphere.functional.controller;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.request.FunctionalCaseAddRequest;
|
||||||
|
import io.metersphere.functional.service.FunctionalCaseService;
|
||||||
|
import io.metersphere.sdk.constants.PermissionConstants;
|
||||||
|
import io.metersphere.system.log.annotation.Log;
|
||||||
|
import io.metersphere.system.log.constants.OperationLogType;
|
||||||
|
import io.metersphere.system.utils.SessionUtils;
|
||||||
|
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.*;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Tag(name = "功能测试-功能用例")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/functional/case")
|
||||||
|
public class FunctionalCaseController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseService functionalCaseService;
|
||||||
|
|
||||||
|
|
||||||
|
//TODO 获取模板列表 获取对应模板自定义字段
|
||||||
|
|
||||||
|
|
||||||
|
@PostMapping("/add")
|
||||||
|
@Operation(summary = "功能用例-新增用例")
|
||||||
|
@RequiresPermissions(PermissionConstants.FUNCTIONAL_CASE_READ_ADD)
|
||||||
|
@Log(type = OperationLogType.ADD, expression = "#msClass.addFunctionalCaseLog(#request, #files)", msClass = FunctionalCaseService.class)
|
||||||
|
public FunctionalCase addFunctionalCase(@Validated @RequestPart("request") FunctionalCaseAddRequest request, @RequestPart(value = "files", required = false) List<MultipartFile> files) {
|
||||||
|
String userId = SessionUtils.getUserId();
|
||||||
|
return functionalCaseService.addFunctionalCase(request, files, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package io.metersphere.functional.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class CaseCustomsFieldDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "字段id")
|
||||||
|
@NotBlank(message = "{functional_case_custom_field.field_id.not_blank}")
|
||||||
|
private String fieldId;
|
||||||
|
|
||||||
|
@Schema(description = "自定义字段值")
|
||||||
|
private String value;
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package io.metersphere.functional.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class FileUploadDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package io.metersphere.functional.mapper;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
public interface ExtFunctionalCaseMapper {
|
||||||
|
FunctionalCase getMaxNumByProjectId(@Param("projectId") String projectId);
|
||||||
|
|
||||||
|
Long getPos(@Param("projectId") String projectId);
|
||||||
|
|
||||||
|
;
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?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.functional.mapper.ExtFunctionalCaseMapper">
|
||||||
|
|
||||||
|
<select id="getMaxNumByProjectId" resultType="io.metersphere.functional.domain.FunctionalCase">
|
||||||
|
SELECT
|
||||||
|
num
|
||||||
|
FROM
|
||||||
|
functional_case
|
||||||
|
WHERE
|
||||||
|
project_id = #{projectId}
|
||||||
|
ORDER BY
|
||||||
|
num DESC
|
||||||
|
LIMIT 1;
|
||||||
|
</select>
|
||||||
|
|
||||||
|
|
||||||
|
<select id="getPos" resultType="java.lang.Long">
|
||||||
|
SELECT
|
||||||
|
pos
|
||||||
|
FROM
|
||||||
|
functional_case
|
||||||
|
WHERE
|
||||||
|
project_id = #{projectId}
|
||||||
|
ORDER BY
|
||||||
|
pos DESC
|
||||||
|
LIMIT 1;
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</mapper>
|
|
@ -0,0 +1,76 @@
|
||||||
|
package io.metersphere.functional.request;
|
||||||
|
|
||||||
|
import io.metersphere.functional.dto.CaseCustomsFieldDTO;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class FunctionalCaseAddRequest implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "项目id")
|
||||||
|
@NotBlank(message = "{functional_case.project_id.not_blank}")
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
|
@Schema(description = "模板id")
|
||||||
|
@NotBlank(message = "{functional_case.template_id.not_blank}")
|
||||||
|
private String templateId;
|
||||||
|
|
||||||
|
@Schema(description = "用例名称")
|
||||||
|
@NotBlank(message = "{functional_case.name.not_blank}")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
@Schema(description = "前置条件")
|
||||||
|
private String prerequisite;
|
||||||
|
|
||||||
|
@Schema(description = "编辑模式", allowableValues = {"STEP", "TEXT"})
|
||||||
|
@NotBlank(message = "{functional_case.case_edit_type.not_blank}")
|
||||||
|
private String caseEditType;
|
||||||
|
|
||||||
|
@Schema(description = "用例步骤")
|
||||||
|
private String steps;
|
||||||
|
|
||||||
|
@Schema(description = "步骤描述")
|
||||||
|
private String textDescription;
|
||||||
|
|
||||||
|
@Schema(description = "预期结果")
|
||||||
|
private String expectedResult;
|
||||||
|
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "是否公共用例库")
|
||||||
|
private String publicCase;
|
||||||
|
|
||||||
|
|
||||||
|
@Schema(description = "模块id")
|
||||||
|
@NotBlank(message = "{functional_case.module_id.not_blank}")
|
||||||
|
private String moduleId;
|
||||||
|
|
||||||
|
@Schema(description = "版本id")
|
||||||
|
private String versionId;
|
||||||
|
|
||||||
|
@Schema(description = "标签")
|
||||||
|
private String tags;
|
||||||
|
|
||||||
|
|
||||||
|
@Schema(description = "自定义字段集合")
|
||||||
|
private List<CaseCustomsFieldDTO> customsFields;
|
||||||
|
|
||||||
|
|
||||||
|
@Schema(description = "关联文件ID集合")
|
||||||
|
private List<String> relateFileMetaIds = new ArrayList<>();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package io.metersphere.functional.request;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class TemplateFieldsRequest implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "模板id")
|
||||||
|
private String templateId;
|
||||||
|
|
||||||
|
@Schema(description = "项目id", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
|
@NotBlank(message = "{functional_case.project_id.not_blank}")
|
||||||
|
private String projectId;
|
||||||
|
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
package io.metersphere.functional.service;
|
|
||||||
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义字段功能用例关系表服务实现类
|
|
||||||
*
|
|
||||||
* @date : 2023-5-17
|
|
||||||
*/
|
|
||||||
@Service
|
|
||||||
public class CustomFieldTestCaseService {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +1,85 @@
|
||||||
package io.metersphere.functional.service;
|
package io.metersphere.functional.service;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseAttachment;
|
||||||
|
import io.metersphere.functional.mapper.FunctionalCaseAttachmentMapper;
|
||||||
|
import io.metersphere.project.domain.FileMetadata;
|
||||||
|
import io.metersphere.project.mapper.FileMetadataMapper;
|
||||||
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.ibatis.session.ExecutorType;
|
||||||
|
import org.apache.ibatis.session.SqlSession;
|
||||||
|
import org.apache.ibatis.session.SqlSessionFactory;
|
||||||
|
import org.mybatis.spring.SqlSessionUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public class FunctionalCaseAttachmentService {
|
public class FunctionalCaseAttachmentService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
SqlSessionFactory sqlSessionFactory;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseAttachmentMapper functionalCaseAttachmentMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FileMetadataMapper fileMetadataMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存本地上传文件和用例关联关系
|
||||||
|
*
|
||||||
|
* @param fileId
|
||||||
|
* @param file
|
||||||
|
* @param caseId
|
||||||
|
* @param isLocal
|
||||||
|
* @param userId
|
||||||
|
*/
|
||||||
|
public void saveCaseAttachment(String fileId, MultipartFile file, String caseId, Boolean isLocal, String userId) {
|
||||||
|
FunctionalCaseAttachment caseAttachment = creatModule(fileId, file.getName(), file.getSize(), caseId, isLocal, userId);
|
||||||
|
functionalCaseAttachmentMapper.insertSelective(caseAttachment);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存文件库文件与用例关联关系
|
||||||
|
*
|
||||||
|
* @param relateFileMetaIds
|
||||||
|
* @param caseId
|
||||||
|
* @param userId
|
||||||
|
*/
|
||||||
|
public void relateFileMeta(List<String> relateFileMetaIds, String caseId, String userId) {
|
||||||
|
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||||
|
FunctionalCaseAttachmentMapper sessionMapper = sqlSession.getMapper(FunctionalCaseAttachmentMapper.class);
|
||||||
|
relateFileMetaIds.forEach(fileMetaId -> {
|
||||||
|
FileMetadata fileMetadata = fileMetadataMapper.selectByPrimaryKey(fileMetaId);
|
||||||
|
FunctionalCaseAttachment caseAttachment = creatModule(fileMetadata.getId(), fileMetadata.getName(), fileMetadata.getSize(), caseId, false, userId);
|
||||||
|
sessionMapper.insertSelective(caseAttachment);
|
||||||
|
});
|
||||||
|
sqlSession.flushStatements();
|
||||||
|
if (sqlSession != null && sqlSessionFactory != null) {
|
||||||
|
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FunctionalCaseAttachment creatModule(String fileId, String fileName, long fileSize, String caseId, Boolean isLocal, String userId) {
|
||||||
|
FunctionalCaseAttachment caseAttachment = new FunctionalCaseAttachment();
|
||||||
|
caseAttachment.setId(IDGenerator.nextStr());
|
||||||
|
caseAttachment.setCaseId(caseId);
|
||||||
|
caseAttachment.setFileId(fileId);
|
||||||
|
caseAttachment.setFileName(fileName);
|
||||||
|
caseAttachment.setSize(fileSize);
|
||||||
|
caseAttachment.setLocal(isLocal);
|
||||||
|
caseAttachment.setCreateUser(userId);
|
||||||
|
caseAttachment.setCreateTime(System.currentTimeMillis());
|
||||||
|
return caseAttachment;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package io.metersphere.functional.service;
|
||||||
|
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseCustomField;
|
||||||
|
import io.metersphere.functional.dto.CaseCustomsFieldDTO;
|
||||||
|
import io.metersphere.functional.mapper.FunctionalCaseCustomFieldMapper;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public class FunctionalCaseCustomFieldService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseCustomFieldMapper functionalCaseCustomFieldMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存 用例-自定义字段关系
|
||||||
|
*
|
||||||
|
* @param customsFields
|
||||||
|
*/
|
||||||
|
public void saveCustomField(String caseId, List<CaseCustomsFieldDTO> customsFields) {
|
||||||
|
customsFields.forEach(customsField -> {
|
||||||
|
FunctionalCaseCustomField customField = new FunctionalCaseCustomField();
|
||||||
|
customField.setCaseId(caseId);
|
||||||
|
customField.setFieldId(customsField.getFieldId());
|
||||||
|
customField.setValue(customsField.getValue());
|
||||||
|
functionalCaseCustomFieldMapper.insertSelective(customField);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,174 @@
|
||||||
package io.metersphere.functional.service;
|
package io.metersphere.functional.service;
|
||||||
|
|
||||||
|
import io.metersphere.functional.domain.FunctionalCase;
|
||||||
|
import io.metersphere.functional.domain.FunctionalCaseBlob;
|
||||||
|
import io.metersphere.functional.dto.CaseCustomsFieldDTO;
|
||||||
|
import io.metersphere.functional.mapper.ExtFunctionalCaseMapper;
|
||||||
|
import io.metersphere.functional.mapper.FunctionalCaseBlobMapper;
|
||||||
|
import io.metersphere.functional.mapper.FunctionalCaseMapper;
|
||||||
|
import io.metersphere.functional.request.FunctionalCaseAddRequest;
|
||||||
|
import io.metersphere.sdk.constants.FunctionalCaseExecuteResult;
|
||||||
|
import io.metersphere.sdk.constants.FunctionalCaseReviewStatus;
|
||||||
|
import io.metersphere.sdk.constants.HttpMethodConstants;
|
||||||
|
import io.metersphere.sdk.constants.StorageType;
|
||||||
|
import io.metersphere.sdk.dto.LogDTO;
|
||||||
|
import io.metersphere.sdk.exception.MSException;
|
||||||
|
import io.metersphere.sdk.file.FileRequest;
|
||||||
|
import io.metersphere.sdk.file.MinioRepository;
|
||||||
|
import io.metersphere.sdk.util.BeanUtils;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.MsFileUtils;
|
||||||
|
import io.metersphere.system.log.constants.OperationLogModule;
|
||||||
|
import io.metersphere.system.log.constants.OperationLogType;
|
||||||
|
import io.metersphere.system.uid.IDGenerator;
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import org.apache.commons.collections.CollectionUtils;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author wx
|
||||||
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public class FunctionalCaseService {
|
public class FunctionalCaseService {
|
||||||
|
|
||||||
|
public static final int ORDER_STEP = 5000;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private MinioRepository minioRepository;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private ExtFunctionalCaseMapper extFunctionalCaseMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseMapper functionalCaseMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseBlobMapper functionalCaseBlobMapper;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseCustomFieldService functionalCaseCustomFieldService;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private FunctionalCaseAttachmentService functionalCaseAttachmentService;
|
||||||
|
|
||||||
|
|
||||||
|
public FunctionalCase addFunctionalCase(FunctionalCaseAddRequest request, List<MultipartFile> files, String userId) {
|
||||||
|
String caseId = IDGenerator.nextStr();
|
||||||
|
//添加功能用例
|
||||||
|
FunctionalCase functionalCase = addTestCase(caseId, request, userId);
|
||||||
|
|
||||||
|
//上传文件
|
||||||
|
if (CollectionUtils.isNotEmpty(files)) {
|
||||||
|
uploadFile(request, caseId, files, true, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
//关联附件
|
||||||
|
if (CollectionUtils.isNotEmpty(request.getRelateFileMetaIds())) {
|
||||||
|
functionalCaseAttachmentService.relateFileMeta(request.getRelateFileMetaIds(), caseId, userId);
|
||||||
|
}
|
||||||
|
return functionalCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加功能用例
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
*/
|
||||||
|
private FunctionalCase addTestCase(String caseId, FunctionalCaseAddRequest request, String userId) {
|
||||||
|
FunctionalCase functionalCase = new FunctionalCase();
|
||||||
|
BeanUtils.copyBean(functionalCase, request);
|
||||||
|
functionalCase.setId(caseId);
|
||||||
|
functionalCase.setNum(getNextNum(request.getProjectId()));
|
||||||
|
functionalCase.setReviewStatus(FunctionalCaseReviewStatus.UN_REVIEWED.name());
|
||||||
|
functionalCase.setPos(getNextOrder(request.getProjectId()));
|
||||||
|
functionalCase.setRefId(caseId);
|
||||||
|
functionalCase.setLastExecuteResult(FunctionalCaseExecuteResult.UN_EXECUTED.name());
|
||||||
|
functionalCase.setLatest(true);
|
||||||
|
functionalCase.setCreateUser(userId);
|
||||||
|
functionalCase.setCreateTime(System.currentTimeMillis());
|
||||||
|
functionalCase.setUpdateTime(System.currentTimeMillis());
|
||||||
|
functionalCase.setVersionId(StringUtils.defaultIfBlank(request.getVersionId(), "v1.0.0"));
|
||||||
|
functionalCaseMapper.insertSelective(functionalCase);
|
||||||
|
//附属表
|
||||||
|
FunctionalCaseBlob functionalCaseBlob = new FunctionalCaseBlob();
|
||||||
|
functionalCaseBlob.setId(caseId);
|
||||||
|
BeanUtils.copyBean(functionalCaseBlob, request);
|
||||||
|
functionalCaseBlobMapper.insertSelective(functionalCaseBlob);
|
||||||
|
//保存自定义字段
|
||||||
|
List<CaseCustomsFieldDTO> customsFields = request.getCustomsFields();
|
||||||
|
if (CollectionUtils.isNotEmpty(customsFields)) {
|
||||||
|
functionalCaseCustomFieldService.saveCustomField(caseId, customsFields);
|
||||||
|
}
|
||||||
|
return functionalCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getNextOrder(String projectId) {
|
||||||
|
Long pos = extFunctionalCaseMapper.getPos(projectId);
|
||||||
|
return (pos == null ? 0 : pos) + ORDER_STEP;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNextNum(String projectId) {
|
||||||
|
//TODO 获取下一个num方法(暂时直接查询数据库)
|
||||||
|
FunctionalCase testCase = extFunctionalCaseMapper.getMaxNumByProjectId(projectId);
|
||||||
|
if (testCase == null || testCase.getNum() == null) {
|
||||||
|
return 100001;
|
||||||
|
} else {
|
||||||
|
return Optional.ofNullable(testCase.getNum() + 1).orElse(100001);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 功能用例上传附件
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @param files
|
||||||
|
*/
|
||||||
|
public void uploadFile(FunctionalCaseAddRequest request, String caseId, List<MultipartFile> files, Boolean isLocal, String userId) {
|
||||||
|
files.forEach(file -> {
|
||||||
|
String fileId = IDGenerator.nextStr();
|
||||||
|
FileRequest fileRequest = new FileRequest();
|
||||||
|
fileRequest.setFileName(file.getName());
|
||||||
|
fileRequest.setProjectId(request.getProjectId());
|
||||||
|
fileRequest.setResourceId(MsFileUtils.FUNCTIONAL_CASE_ATTACHMENT_DIR + fileId);
|
||||||
|
fileRequest.setStorage(StorageType.MINIO.name());
|
||||||
|
try {
|
||||||
|
minioRepository.saveFile(file, fileRequest);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new MSException("save file error");
|
||||||
|
}
|
||||||
|
functionalCaseAttachmentService.saveCaseAttachment(fileId, file, caseId, isLocal, userId);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增用例 日志
|
||||||
|
*
|
||||||
|
* @param requests
|
||||||
|
* @param files
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public LogDTO addFunctionalCaseLog(FunctionalCaseAddRequest requests, List<MultipartFile> files) {
|
||||||
|
LogDTO dto = new LogDTO(
|
||||||
|
requests.getProjectId(),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
OperationLogType.ADD.name(),
|
||||||
|
OperationLogModule.FUNCTIONAL_CASE,
|
||||||
|
requests.getName());
|
||||||
|
|
||||||
|
dto.setPath("/functional/case/add");
|
||||||
|
dto.setMethod(HttpMethodConstants.POST.name());
|
||||||
|
dto.setOriginalValue(JSON.toJSONBytes(requests));
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
package io.metersphere.functional.controller;
|
||||||
|
|
||||||
|
import io.metersphere.functional.dto.CaseCustomsFieldDTO;
|
||||||
|
import io.metersphere.functional.request.FunctionalCaseAddRequest;
|
||||||
|
import io.metersphere.functional.utils.FileBaseUtils;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.system.base.BaseTest;
|
||||||
|
import io.metersphere.system.controller.handler.ResultHolder;
|
||||||
|
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.mock.web.MockMultipartFile;
|
||||||
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
|
import org.springframework.test.context.jdbc.SqlConfig;
|
||||||
|
import org.springframework.test.web.servlet.MvcResult;
|
||||||
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||||
|
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
|
||||||
|
@AutoConfigureMockMvc
|
||||||
|
public class FunctionalCaseControllerTests extends BaseTest {
|
||||||
|
|
||||||
|
public static final String FUNCTIONAL_CASE_URL = "/functional/case/add";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Order(1)
|
||||||
|
@Sql(scripts = {"/dml/init_file_metadata_test.sql"}, config = @SqlConfig(encoding = "utf-8", transactionMode = SqlConfig.TransactionMode.ISOLATED))
|
||||||
|
public void testTestPlanShare() throws Exception {
|
||||||
|
//新增
|
||||||
|
FunctionalCaseAddRequest request = creatFunctionalCase();
|
||||||
|
LinkedMultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>();
|
||||||
|
List<MockMultipartFile> files = new ArrayList<>();
|
||||||
|
paramMap.add("request", JSON.toJSONString(request));
|
||||||
|
paramMap.add("files", files);
|
||||||
|
MvcResult mvcResult = this.requestMultipartWithOkAndReturn(FUNCTIONAL_CASE_URL, paramMap);
|
||||||
|
// 获取返回值
|
||||||
|
String returnData = mvcResult.getResponse().getContentAsString(StandardCharsets.UTF_8);
|
||||||
|
ResultHolder resultHolder = JSON.parseObject(returnData, ResultHolder.class);
|
||||||
|
// 返回请求正常
|
||||||
|
Assertions.assertNotNull(resultHolder);
|
||||||
|
|
||||||
|
//设置自定义字段
|
||||||
|
List<CaseCustomsFieldDTO> dtoList = creatCustomsFields();
|
||||||
|
request.setCustomsFields(dtoList);
|
||||||
|
|
||||||
|
//设置文件
|
||||||
|
String filePath = Objects.requireNonNull(this.getClass().getClassLoader().getResource("file/test.JPG")).getPath();
|
||||||
|
MockMultipartFile file = new MockMultipartFile("file", "file_re-upload.JPG", MediaType.APPLICATION_OCTET_STREAM_VALUE, FileBaseUtils.getFileBytes(filePath));
|
||||||
|
files.add(file);
|
||||||
|
|
||||||
|
//设置关联文件
|
||||||
|
request.setRelateFileMetaIds(Arrays.asList("relate_file_meta_id_1", "relate_file_meta_id_2"));
|
||||||
|
paramMap = new LinkedMultiValueMap<>();
|
||||||
|
paramMap.add("request", JSON.toJSONString(request));
|
||||||
|
paramMap.add("files", files);
|
||||||
|
|
||||||
|
this.requestMultipartWithOkAndReturn(FUNCTIONAL_CASE_URL, paramMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<CaseCustomsFieldDTO> creatCustomsFields() {
|
||||||
|
List<CaseCustomsFieldDTO> list = new ArrayList<>();
|
||||||
|
CaseCustomsFieldDTO customsFieldDTO = new CaseCustomsFieldDTO();
|
||||||
|
customsFieldDTO.setFieldId("customs_field_id_1");
|
||||||
|
customsFieldDTO.setValue("customs_field_value_1");
|
||||||
|
list.add(customsFieldDTO);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private FunctionalCaseAddRequest creatFunctionalCase() {
|
||||||
|
FunctionalCaseAddRequest functionalCaseAddRequest = new FunctionalCaseAddRequest();
|
||||||
|
functionalCaseAddRequest.setProjectId(DEFAULT_PROJECT_ID);
|
||||||
|
functionalCaseAddRequest.setTemplateId("default_template_id");
|
||||||
|
functionalCaseAddRequest.setName("测试用例新增");
|
||||||
|
functionalCaseAddRequest.setCaseEditType("STEP");
|
||||||
|
functionalCaseAddRequest.setModuleId("default_module_id");
|
||||||
|
return functionalCaseAddRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,128 @@
|
||||||
|
package io.metersphere.functional.utils;
|
||||||
|
|
||||||
|
import io.metersphere.project.dto.FileInformationDTO;
|
||||||
|
import io.metersphere.project.request.filemanagement.FileMetadataTableRequest;
|
||||||
|
import io.metersphere.sdk.dto.BaseTreeNode;
|
||||||
|
import io.metersphere.sdk.util.FilePreviewUtils;
|
||||||
|
import io.metersphere.sdk.util.JSON;
|
||||||
|
import io.metersphere.sdk.util.Pager;
|
||||||
|
import org.apache.commons.collections4.CollectionUtils;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.testcontainers.shaded.org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class FileBaseUtils {
|
||||||
|
public static BaseTreeNode getNodeByName(List<BaseTreeNode> preliminaryTreeNodes, String nodeName) {
|
||||||
|
for (BaseTreeNode firstLevelNode : preliminaryTreeNodes) {
|
||||||
|
if (StringUtils.equals(firstLevelNode.getName(), nodeName)) {
|
||||||
|
return firstLevelNode;
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isNotEmpty(firstLevelNode.getChildren())) {
|
||||||
|
for (BaseTreeNode secondLevelNode : firstLevelNode.getChildren()) {
|
||||||
|
if (StringUtils.equals(secondLevelNode.getName(), nodeName)) {
|
||||||
|
return secondLevelNode;
|
||||||
|
}
|
||||||
|
if (CollectionUtils.isNotEmpty(secondLevelNode.getChildren())) {
|
||||||
|
for (BaseTreeNode thirdLevelNode : secondLevelNode.getChildren()) {
|
||||||
|
if (StringUtils.equals(thirdLevelNode.getName(), nodeName)) {
|
||||||
|
return thirdLevelNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getFileBytes(String filePath) {
|
||||||
|
File file = new File(filePath);
|
||||||
|
byte[] buffer = new byte[0];
|
||||||
|
try (FileInputStream fi = new FileInputStream(file)) {
|
||||||
|
buffer = new byte[(int) file.length()];
|
||||||
|
int offset = 0;
|
||||||
|
int numRead;
|
||||||
|
while (offset < buffer.length
|
||||||
|
&& (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {
|
||||||
|
offset += numRead;
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileMD5(File file) {
|
||||||
|
if (!file.isFile()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
MessageDigest digest = null;
|
||||||
|
FileInputStream in = null;
|
||||||
|
byte buffer[] = new byte[8192];
|
||||||
|
int len;
|
||||||
|
try {
|
||||||
|
digest = MessageDigest.getInstance("MD5");
|
||||||
|
in = new FileInputStream(file);
|
||||||
|
while ((len = in.read(buffer)) != -1) {
|
||||||
|
digest.update(buffer, 0, len);
|
||||||
|
}
|
||||||
|
BigInteger bigInt = new BigInteger(1, digest.digest());
|
||||||
|
return bigInt.toString(16);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getFileMD5(byte[] bytes) {
|
||||||
|
try {
|
||||||
|
MessageDigest digest = MessageDigest.getInstance("MD5");
|
||||||
|
digest.update(bytes, 0, bytes.length);
|
||||||
|
BigInteger bigInt = new BigInteger(1, digest.digest());
|
||||||
|
return bigInt.toString(16);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkFilePage(Pager<List<FileInformationDTO>> tableData, Map<String, Integer> moduleCount, FileMetadataTableRequest request, boolean hasData) {
|
||||||
|
//返回值的页码和当前页码相同
|
||||||
|
Assertions.assertEquals(tableData.getCurrent(), request.getCurrent());
|
||||||
|
//返回的数据量不超过规定要返回的数据量相同
|
||||||
|
Assertions.assertTrue(JSON.parseArray(JSON.toJSONString(tableData.getList())).size() <= request.getPageSize());
|
||||||
|
List<FileInformationDTO> fileInformationDTOList = JSON.parseArray(JSON.toJSONString(tableData.getList()), FileInformationDTO.class);
|
||||||
|
for (FileInformationDTO fileInformationDTO : fileInformationDTOList) {
|
||||||
|
if (FilePreviewUtils.isImage(fileInformationDTO.getFileType())) {
|
||||||
|
//检查是否有预览文件
|
||||||
|
String previewPath = fileInformationDTO.getPreviewSrc();
|
||||||
|
File file = new File(previewPath);
|
||||||
|
Assertions.assertTrue(file.exists());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//判断返回的节点统计总量是否和表格总量匹配
|
||||||
|
long allResult = 0;
|
||||||
|
for (int countByModuleId : moduleCount.values()) {
|
||||||
|
allResult += countByModuleId;
|
||||||
|
}
|
||||||
|
Assertions.assertEquals(allResult, tableData.getTotal());
|
||||||
|
Assertions.assertEquals(request.getPageSize(), tableData.getPageSize());
|
||||||
|
if (hasData) {
|
||||||
|
Assertions.assertTrue(allResult > 0);
|
||||||
|
} else {
|
||||||
|
Assertions.assertTrue(allResult == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ quartz.properties.org.quartz.jobStore.acquireTriggersWithinLock=true
|
||||||
#
|
#
|
||||||
logging.file.path=/opt/metersphere/logs/metersphere
|
logging.file.path=/opt/metersphere/logs/metersphere
|
||||||
# Hikari
|
# Hikari
|
||||||
spring.datasource.url=jdbc:mysql://${embedded.mysql.host}:${embedded.mysql.port}/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
|
spring.datasource.url=jdbc:mysql://${embedded.mysql.host}:${embedded.mysql.port}/test?autoReconnect=false&useUnicode=true&characterEncoding=UTF-8&characterSetResults=UTF-8&zeroDateTimeBehavior=convertToNull&allowPublicKeyRetrieval=true&useSSL=false&sessionVariables=sql_mode=%27STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION%27
|
||||||
spring.datasource.username=${embedded.mysql.user}
|
spring.datasource.username=${embedded.mysql.user}
|
||||||
spring.datasource.password=${embedded.mysql.password}
|
spring.datasource.password=${embedded.mysql.password}
|
||||||
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
|
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
INSERT INTO file_metadata(id, name, type, size, create_time, update_time, project_id, storage, create_user, update_user, tags, description, module_id, path, latest, ref_id, file_version) VALUES ('relate_file_meta_id_1', 'formItem', 'ts', 2502, 1698058347559, 1698058347559, '100001100001', 'MINIO', 'admin', 'admin', NULL, NULL, 'root', '100001100001/1127016598347779', b'1', '1127016598347779', '1127016598347779');
|
||||||
|
INSERT INTO file_metadata(id, name, type, size, create_time, update_time, project_id, storage, create_user, update_user, tags, description, module_id, path, latest, ref_id, file_version) VALUES ('relate_file_meta_id_2', 'formItem', 'ts', 2502, 1698058347559, 1698058347559, '100001100001', 'MINIO', 'admin', 'admin', NULL, NULL, 'root', '100001100001/1127016598347779', b'1', '1127016598347779', '1127016598347779');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
|
@ -44,7 +44,7 @@ import java.util.stream.Collectors;
|
||||||
import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND;
|
import static io.metersphere.system.controller.handler.result.MsHttpResultCode.NOT_FOUND;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Transactional
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public class ProjectApplicationService {
|
public class ProjectApplicationService {
|
||||||
@Resource
|
@Resource
|
||||||
private ProjectApplicationMapper projectApplicationMapper;
|
private ProjectApplicationMapper projectApplicationMapper;
|
||||||
|
|
|
@ -94,4 +94,7 @@ public class OperationLogModule {
|
||||||
public static final String PROJECT_MANAGEMENT_MESSAGE_MANAGEMENT_ROBOT = "PROJECT_MANAGEMENT_MESSAGE_MANAGEMENT_ROBOT";
|
public static final String PROJECT_MANAGEMENT_MESSAGE_MANAGEMENT_ROBOT = "PROJECT_MANAGEMENT_MESSAGE_MANAGEMENT_ROBOT";
|
||||||
public static final String PROJECT_TEMPLATE = "PROJECT_TEMPLATE";// 项目模板
|
public static final String PROJECT_TEMPLATE = "PROJECT_TEMPLATE";// 项目模板
|
||||||
public static final String PROJECT_CUSTOM_FIELD = "PROJECT_CUSTOM_FIELD";// 项目字段
|
public static final String PROJECT_CUSTOM_FIELD = "PROJECT_CUSTOM_FIELD";// 项目字段
|
||||||
|
|
||||||
|
//用例
|
||||||
|
public static final String FUNCTIONAL_CASE = "FUNCTIONAL_CASE";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue