fix(缺陷管理): 缺陷通知自定义字段通知取值有误

--bug=1047046 --user=宋昌昌 【消息通知】缺陷管理的消息通知中自定义字段获取不到实际值 https://www.tapd.cn/55049933/s/1606574
This commit is contained in:
song-cc-rock 2024-11-06 17:55:01 +08:00 committed by Craftsman
parent 1a38c3a1f0
commit 5054ccea61
8 changed files with 152 additions and 177 deletions

View File

@ -26,12 +26,16 @@ public class BugCustomField implements Serializable {
@Schema(description = "字段值")
private String value;
@Schema(description = "字段文本")
private String content;
private static final long serialVersionUID = 1L;
public enum Column {
bugId("bug_id", "bugId", "VARCHAR", false),
fieldId("field_id", "fieldId", "VARCHAR", false),
value("value", "value", "VARCHAR", true);
value("value", "value", "LONGVARCHAR", true),
content("content", "content", "LONGVARCHAR", false);
private static final String BEGINNING_DELIMITER = "`";

View File

@ -243,76 +243,6 @@ public class BugCustomFieldExample {
addCriterion("field_id not between", value1, value2, "fieldId");
return (Criteria) this;
}
public Criteria andValueIsNull() {
addCriterion("`value` is null");
return (Criteria) this;
}
public Criteria andValueIsNotNull() {
addCriterion("`value` is not null");
return (Criteria) this;
}
public Criteria andValueEqualTo(String value) {
addCriterion("`value` =", value, "value");
return (Criteria) this;
}
public Criteria andValueNotEqualTo(String value) {
addCriterion("`value` <>", value, "value");
return (Criteria) this;
}
public Criteria andValueGreaterThan(String value) {
addCriterion("`value` >", value, "value");
return (Criteria) this;
}
public Criteria andValueGreaterThanOrEqualTo(String value) {
addCriterion("`value` >=", value, "value");
return (Criteria) this;
}
public Criteria andValueLessThan(String value) {
addCriterion("`value` <", value, "value");
return (Criteria) this;
}
public Criteria andValueLessThanOrEqualTo(String value) {
addCriterion("`value` <=", value, "value");
return (Criteria) this;
}
public Criteria andValueLike(String value) {
addCriterion("`value` like", value, "value");
return (Criteria) this;
}
public Criteria andValueNotLike(String value) {
addCriterion("`value` not like", value, "value");
return (Criteria) this;
}
public Criteria andValueIn(List<String> values) {
addCriterion("`value` in", values, "value");
return (Criteria) this;
}
public Criteria andValueNotIn(List<String> values) {
addCriterion("`value` not in", values, "value");
return (Criteria) this;
}
public Criteria andValueBetween(String value1, String value2) {
addCriterion("`value` between", value1, value2, "value");
return (Criteria) this;
}
public Criteria andValueNotBetween(String value1, String value2) {
addCriterion("`value` not between", value1, value2, "value");
return (Criteria) this;
}
}
public static class Criteria extends GeneratedCriteria {

View File

@ -11,23 +11,27 @@ public interface BugCustomFieldMapper {
int deleteByExample(BugCustomFieldExample example);
int deleteByPrimaryKey(String bugId);
int deleteByPrimaryKey(@Param("bugId") String bugId, @Param("fieldId") String fieldId);
int insert(BugCustomField record);
int insertSelective(BugCustomField record);
List<BugCustomField> selectByExampleWithBLOBs(BugCustomFieldExample example);
List<BugCustomField> selectByExample(BugCustomFieldExample example);
BugCustomField selectByPrimaryKey(String bugId);
BugCustomField selectByPrimaryKey(@Param("bugId") String bugId, @Param("fieldId") String fieldId);
int updateByExampleSelective(@Param("record") BugCustomField record, @Param("example") BugCustomFieldExample example);
int updateByExampleWithBLOBs(@Param("record") BugCustomField record, @Param("example") BugCustomFieldExample example);
int updateByExample(@Param("record") BugCustomField record, @Param("example") BugCustomFieldExample example);
int updateByPrimaryKeySelective(BugCustomField record);
int updateByPrimaryKey(BugCustomField record);
int updateByPrimaryKeyWithBLOBs(BugCustomField record);
int batchInsert(@Param("list") List<BugCustomField> list);

View File

@ -3,8 +3,11 @@
<mapper namespace="io.metersphere.bug.mapper.BugCustomFieldMapper">
<resultMap id="BaseResultMap" type="io.metersphere.bug.domain.BugCustomField">
<id column="bug_id" jdbcType="VARCHAR" property="bugId" />
<result column="field_id" jdbcType="VARCHAR" property="fieldId" />
<result column="value" jdbcType="VARCHAR" property="value" />
<id column="field_id" jdbcType="VARCHAR" property="fieldId" />
</resultMap>
<resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="io.metersphere.bug.domain.BugCustomField">
<result column="value" jdbcType="LONGVARCHAR" property="value" />
<result column="content" jdbcType="LONGVARCHAR" property="content" />
</resultMap>
<sql id="Example_Where_Clause">
<where>
@ -65,8 +68,27 @@
</where>
</sql>
<sql id="Base_Column_List">
bug_id, field_id, `value`
bug_id, field_id
</sql>
<sql id="Blob_Column_List">
`value`, content
</sql>
<select id="selectByExampleWithBLOBs" parameterType="io.metersphere.bug.domain.BugCustomFieldExample" resultMap="ResultMapWithBLOBs">
select
<if test="distinct">
distinct
</if>
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from bug_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.bug.domain.BugCustomFieldExample" resultMap="BaseResultMap">
select
<if test="distinct">
@ -81,15 +103,19 @@
order by ${orderByClause}
</if>
</select>
<select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
<select id="selectByPrimaryKey" parameterType="map" resultMap="ResultMapWithBLOBs">
select
<include refid="Base_Column_List" />
,
<include refid="Blob_Column_List" />
from bug_custom_field
where bug_id = #{bugId,jdbcType=VARCHAR}
and field_id = #{fieldId,jdbcType=VARCHAR}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.String">
<delete id="deleteByPrimaryKey" parameterType="map">
delete from bug_custom_field
where bug_id = #{bugId,jdbcType=VARCHAR}
and field_id = #{fieldId,jdbcType=VARCHAR}
</delete>
<delete id="deleteByExample" parameterType="io.metersphere.bug.domain.BugCustomFieldExample">
delete from bug_custom_field
@ -98,10 +124,10 @@
</if>
</delete>
<insert id="insert" parameterType="io.metersphere.bug.domain.BugCustomField">
insert into bug_custom_field (bug_id, field_id, `value`
)
values (#{bugId,jdbcType=VARCHAR}, #{fieldId,jdbcType=VARCHAR}, #{value,jdbcType=VARCHAR}
)
insert into bug_custom_field (bug_id, field_id, `value`,
content)
values (#{bugId,jdbcType=VARCHAR}, #{fieldId,jdbcType=VARCHAR}, #{value,jdbcType=LONGVARCHAR},
#{content,jdbcType=LONGVARCHAR})
</insert>
<insert id="insertSelective" parameterType="io.metersphere.bug.domain.BugCustomField">
insert into bug_custom_field
@ -115,6 +141,9 @@
<if test="value != null">
`value`,
</if>
<if test="content != null">
content,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="bugId != null">
@ -124,7 +153,10 @@
#{fieldId,jdbcType=VARCHAR},
</if>
<if test="value != null">
#{value,jdbcType=VARCHAR},
#{value,jdbcType=LONGVARCHAR},
</if>
<if test="content != null">
#{content,jdbcType=LONGVARCHAR},
</if>
</trim>
</insert>
@ -144,18 +176,30 @@
field_id = #{record.fieldId,jdbcType=VARCHAR},
</if>
<if test="record.value != null">
`value` = #{record.value,jdbcType=VARCHAR},
`value` = #{record.value,jdbcType=LONGVARCHAR},
</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 bug_custom_field
set bug_id = #{record.bugId,jdbcType=VARCHAR},
field_id = #{record.fieldId,jdbcType=VARCHAR},
`value` = #{record.value,jdbcType=LONGVARCHAR},
content = #{record.content,jdbcType=LONGVARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
</update>
<update id="updateByExample" parameterType="map">
update bug_custom_field
set bug_id = #{record.bugId,jdbcType=VARCHAR},
field_id = #{record.fieldId,jdbcType=VARCHAR},
`value` = #{record.value,jdbcType=VARCHAR}
field_id = #{record.fieldId,jdbcType=VARCHAR}
<if test="_parameter != null">
<include refid="Update_By_Example_Where_Clause" />
</if>
@ -163,28 +207,30 @@
<update id="updateByPrimaryKeySelective" parameterType="io.metersphere.bug.domain.BugCustomField">
update bug_custom_field
<set>
<if test="fieldId != null">
field_id = #{fieldId,jdbcType=VARCHAR},
</if>
<if test="value != null">
`value` = #{value,jdbcType=VARCHAR},
`value` = #{value,jdbcType=LONGVARCHAR},
</if>
<if test="content != null">
content = #{content,jdbcType=LONGVARCHAR},
</if>
</set>
where bug_id = #{bugId,jdbcType=VARCHAR}
and field_id = #{fieldId,jdbcType=VARCHAR}
</update>
<update id="updateByPrimaryKey" parameterType="io.metersphere.bug.domain.BugCustomField">
<update id="updateByPrimaryKeyWithBLOBs" parameterType="io.metersphere.bug.domain.BugCustomField">
update bug_custom_field
set field_id = #{fieldId,jdbcType=VARCHAR},
`value` = #{value,jdbcType=VARCHAR}
set `value` = #{value,jdbcType=LONGVARCHAR},
content = #{content,jdbcType=LONGVARCHAR}
where bug_id = #{bugId,jdbcType=VARCHAR}
and field_id = #{fieldId,jdbcType=VARCHAR}
</update>
<insert id="batchInsert" parameterType="map">
insert into bug_custom_field
(bug_id, field_id, `value`)
(bug_id, field_id, `value`, content)
values
<foreach collection="list" item="item" separator=",">
(#{item.bugId,jdbcType=VARCHAR}, #{item.fieldId,jdbcType=VARCHAR}, #{item.value,jdbcType=VARCHAR}
)
(#{item.bugId,jdbcType=VARCHAR}, #{item.fieldId,jdbcType=VARCHAR}, #{item.value,jdbcType=LONGVARCHAR},
#{item.content,jdbcType=LONGVARCHAR})
</foreach>
</insert>
<insert id="batchInsertSelective" parameterType="map">
@ -204,7 +250,10 @@
#{item.fieldId,jdbcType=VARCHAR}
</if>
<if test="'value'.toString() == column.value">
#{item.value,jdbcType=VARCHAR}
#{item.value,jdbcType=LONGVARCHAR}
</if>
<if test="'content'.toString() == column.value">
#{item.content,jdbcType=LONGVARCHAR}
</if>
</foreach>
)

View File

@ -1,6 +1,8 @@
-- set innodb lock wait timeout
SET SESSION innodb_lock_wait_timeout = 7200;
-- 缺陷自定义字段增加文本字段
ALTER TABLE bug_custom_field ADD COLUMN `content` VARCHAR(1000) COMMENT '字段文本';
-- set innodb lock wait timeout to default
SET SESSION innodb_lock_wait_timeout = DEFAULT;

View File

@ -11,7 +11,7 @@
</select>
<select id="getBugAllCustomFields" resultType="io.metersphere.bug.dto.response.BugCustomFieldDTO">
select cf.name, bcf.value, bcf.bug_id, bcf.field_id as id, cf.type as type
select cf.name, bcf.value, bcf.content as text, bcf.bug_id, bcf.field_id as id, cf.type as type
from bug_custom_field bcf left join custom_field cf on bcf.field_id = cf.id
and cf.scene = 'BUG' and cf.scope_type = 'PROJECT' and scope_id = #{projectId}
where bug_id in

View File

@ -3,7 +3,7 @@ package io.metersphere.bug.service;
import io.metersphere.bug.dto.request.BugBatchRequest;
import io.metersphere.bug.dto.request.BugEditRequest;
import io.metersphere.bug.dto.response.BugDTO;
import io.metersphere.plugin.platform.dto.SelectOption;
import io.metersphere.plugin.sdk.util.PluginUtils;
import io.metersphere.sdk.util.BeanUtils;
import io.metersphere.system.dto.BugNoticeDTO;
import io.metersphere.system.dto.sdk.OptionDTO;
@ -15,8 +15,10 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author song-cc-rock
*/
@Service
@Transactional(rollbackFor = Exception.class)
@ -24,16 +26,12 @@ public class BugNoticeService {
public static final String CUSTOM_TITLE = "summary";
public static final String CUSTOM_STATUS = "status";
public static final String CUSTOM_HANDLE_USER = "处理人";
public static final String CUSTOM_HANDLE_USER = "handleUser";
@Resource
private BugService bugService;
@Resource
private BugLogService bugLogService;
@Resource
private BugCommonService bugCommonService;
@Resource
private BugStatusService bugStatusService;
/**
* 获取缺陷通知
@ -42,41 +40,35 @@ public class BugNoticeService {
*/
@SuppressWarnings("unused")
public BugNoticeDTO getNoticeByRequest(BugEditRequest request) {
// 获取状态选项, 处理人选项
Map<String, String> statusMap = getStatusMap(request.getProjectId());
Map<String, String> handlerMap = getHandleMap(request.getProjectId());
if (StringUtils.isEmpty(request.getId())) {
// 构建通知对象
BugNoticeDTO notice = new BugNoticeDTO();
notice.setTitle(request.getTitle());
// 自定义字段解析{name: value}
if (CollectionUtils.isNotEmpty(request.getCustomFields())) {
List<OptionDTO> fields = new ArrayList<>();
request.getCustomFields().forEach(field -> {
if (StringUtils.equals(field.getId(), CUSTOM_TITLE)) {
// TITLE {标题为空时, 从自定义字段中获取标题}
notice.setTitle(field.getValue());
} else if (StringUtils.equals(field.getId(), CUSTOM_STATUS)) {
// 状态 {从自定义字段中获取状态}
notice.setStatus(statusMap.get(field.getValue()));
} else if (StringUtils.equals(field.getName(), CUSTOM_HANDLE_USER)) {
// 处理人 {从自定义字段中获取状态}
notice.setHandleUser(handlerMap.get(field.getValue()));
} else {
// 其他自定义字段
OptionDTO fieldDTO = new OptionDTO();
fieldDTO.setId(field.getName());
fieldDTO.setName(StringUtils.isEmpty(field.getText()) ? field.getValue() : field.getText());
fields.add(fieldDTO);
// 构建通知对象
BugNoticeDTO notice = new BugNoticeDTO();
notice.setTitle(request.getTitle());
// 自定义字段解析{name: value}
if (CollectionUtils.isNotEmpty(request.getCustomFields())) {
List<OptionDTO> fields = new ArrayList<>();
request.getCustomFields().forEach(field -> {
if (StringUtils.equals(field.getId(), CUSTOM_TITLE)) {
// TITLE {标题为空时, 从自定义字段中获取标题}
notice.setTitle(field.getValue());
} else if (StringUtils.equalsIgnoreCase(field.getId(), CUSTOM_STATUS)) {
// 状态 {从自定义字段中获取状态}
notice.setStatus(PluginUtils.parseArray(field.getText()).getFirst().toString());
} else if (StringUtils.equalsIgnoreCase(field.getId(), CUSTOM_HANDLE_USER)) {
// 处理人 {从自定义字段中获取状态}
notice.setHandleUser(PluginUtils.parseArray(field.getText()).getFirst().toString());
} else {
// 其他自定义字段
OptionDTO fieldDTO = new OptionDTO();
fieldDTO.setId(field.getName());
if (StringUtils.isNotEmpty(field.getText()) && !StringUtils.equals(field.getText(), "[]")) {
fieldDTO.setName(field.getText());
}
});
notice.setFields(fields);
}
return notice;
} else {
// 需设置业务ID(用来通知关注人), 创建人
return getNoticeById(request.getId());
fields.add(fieldDTO);
}
});
notice.setFields(fields);
}
return notice;
}
/**
@ -117,26 +109,6 @@ public class BugNoticeService {
return getNoticeByIds(batchIds);
}
/**
* 获取状态集合
* @param projectId 项目ID
* @return 状态集合
*/
private Map<String, String> getStatusMap(String projectId) {
List<SelectOption> statusOption = bugStatusService.getHeaderStatusOption(projectId);
return statusOption.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
}
/**
* 获取处理人集合
* @param projectId 项目ID
* @return 处理人集合
*/
private Map<String, String> getHandleMap(String projectId) {
List<SelectOption> handlerOption = bugCommonService.getHeaderHandlerOption(projectId);
return handlerOption.stream().collect(Collectors.toMap(SelectOption::getValue, SelectOption::getText));
}
/**
* 构建通知对象
* @param bugDTO 缺陷DTO
@ -155,7 +127,9 @@ public class BugNoticeService {
// 其他自定义字段
OptionDTO fieldDTO = new OptionDTO();
fieldDTO.setId(field.getName());
fieldDTO.setName(field.getValue());
if (StringUtils.isNotEmpty(field.getText()) && !StringUtils.equals(field.getText(), "[]")) {
fieldDTO.setName(field.getText());
}
fields.add(fieldDTO);
});
notice.setFields(fields);

View File

@ -997,7 +997,6 @@ public class BugService {
} else {
bug.setStatus(statusField.get().getValue());
}
request.getCustomFields().removeIf(field -> StringUtils.equals(field.getId(), BugTemplateCustomField.STATUS.getId()));
} else {
throw new MSException(Translator.get("bug_status_can_not_be_empty"));
}
@ -1009,7 +1008,6 @@ public class BugService {
Optional<BugCustomFieldDTO> handleUserField = request.getCustomFields().stream().filter(field -> StringUtils.equals(field.getId(), BugTemplateCustomField.HANDLE_USER.getId())).findFirst();
if (handleUserField.isPresent()) {
bug.setHandleUser(handleUserField.get().getValue());
request.getCustomFields().removeIf(field -> StringUtils.equals(field.getId(), BugTemplateCustomField.HANDLE_USER.getId()));
} else {
throw new MSException(Translator.get("handle_user_can_not_be_empty"));
}
@ -1033,10 +1031,6 @@ public class BugService {
// 平台状态为空
bug.setStatus(StringUtils.EMPTY);
}
// 第三方平台内置的处理人字段需要从自定义字段中移除 (当使用MS系统模板时)
if (!isPluginDefaultTemplate(request.getTemplateId(), request.getProjectId())) {
request.getCustomFields().removeIf(field -> StringUtils.startsWith(field.getName(), BugTemplateCustomField.HANDLE_USER.getName()));
}
}
boolean noticeHandler = false;
@ -1065,6 +1059,8 @@ public class BugService {
bug.setHandleUsers(originalBug.getHandleUsers() + "," + bug.getHandleUser());
noticeHandler = true;
}
bug.setCreateUser(originalBug.getCreateUser());
bug.setCreateTime(originalBug.getCreateTime());
bug.setUpdateUser(currentUser);
bug.setUpdateTime(System.currentTimeMillis());
bugMapper.updateByPrimaryKeySelective(bug);
@ -1116,16 +1112,21 @@ public class BugService {
*/
public void handleAndSaveCustomFields(BugEditRequest request, boolean merge, PlatformBugUpdateDTO platformBug) {
// 处理ID, 值的映射关系
Map<String, String> customFieldMap = request.getCustomFields().stream()
Map<String, BugCustomFieldDTO> customFieldMap = request.getCustomFields().stream()
.filter(f -> StringUtils.isNotBlank(f.getId()))
.collect(HashMap::new, (m, field) -> m.put(field.getId(), field.getValue()), HashMap::putAll);
.collect(Collectors.toMap(BugCustomFieldDTO::getId, f -> f));
if (MapUtils.isEmpty(customFieldMap)) {
return;
}
// 拦截, 如果平台返回结果存在自定义字段值, 替换
if (platformBug != null && MapUtils.isNotEmpty(platformBug.getPlatformCustomFieldMap())) {
Map<String, String> platformCustomFieldMap = platformBug.getPlatformCustomFieldMap();
platformCustomFieldMap.keySet().forEach(key -> customFieldMap.put(key, platformCustomFieldMap.get(key)));
platformCustomFieldMap.keySet().forEach(key -> {
BugCustomFieldDTO field = new BugCustomFieldDTO();
field.setValue(platformCustomFieldMap.get(key));
field.setText(platformCustomFieldMap.get(key));
customFieldMap.put(key, field);
});
}
List<BugCustomField> addFields = new ArrayList<>();
List<BugCustomField> updateFields = new ArrayList<>();
@ -1133,31 +1134,42 @@ public class BugService {
// 编辑缺陷需合并原有自定义字段
List<BugCustomFieldDTO> originalFields = extBugCustomFieldMapper.getBugAllCustomFields(List.of(request.getId()), request.getProjectId());
Map<String, String> originalFieldMap = originalFields.stream().collect(Collectors.toMap(BugCustomFieldDTO::getId, field -> Optional.ofNullable(field.getValue()).orElse(StringUtils.EMPTY)));
customFieldMap.keySet().forEach(fieldId -> {
for (String fieldId : customFieldMap.keySet()) {
// 处理人 / 状态 作为内置的自定义字段, 不需要处理
if (StringUtils.equalsAnyIgnoreCase(fieldId, BugTemplateCustomField.HANDLE_USER.getId(), BugTemplateCustomField.STATUS.getId())) {
continue;
}
BugCustomField bugCustomField = new BugCustomField();
if (!originalFieldMap.containsKey(fieldId)) {
// 新的缺陷字段关系
bugCustomField.setBugId(request.getId());
bugCustomField.setFieldId(fieldId);
bugCustomField.setValue(customFieldMap.get(fieldId));
bugCustomField.setValue(customFieldMap.get(fieldId).getValue());
bugCustomField.setContent(customFieldMap.get(fieldId).getText());
addFields.add(bugCustomField);
} else {
// 已存在的缺陷字段关系
bugCustomField.setBugId(request.getId());
bugCustomField.setFieldId(fieldId);
bugCustomField.setValue(customFieldMap.get(fieldId));
bugCustomField.setValue(customFieldMap.get(fieldId).getValue());
bugCustomField.setContent(customFieldMap.get(fieldId).getText());
updateFields.add(bugCustomField);
}
});
}
} else {
// 新增缺陷不需要合并自定义字段
customFieldMap.keySet().forEach(fieldId -> {
for (String fieldId : customFieldMap.keySet()) {
// 处理人 / 状态 作为内置的自定义字段, 不需要处理
if (StringUtils.equalsAnyIgnoreCase(fieldId, BugTemplateCustomField.HANDLE_USER.getId(), BugTemplateCustomField.STATUS.getId())) {
continue;
}
BugCustomField bugCustomField = new BugCustomField();
bugCustomField.setBugId(request.getId());
bugCustomField.setFieldId(fieldId);
bugCustomField.setValue(customFieldMap.get(fieldId));
bugCustomField.setValue(customFieldMap.get(fieldId).getValue());
bugCustomField.setContent(customFieldMap.get(fieldId).getText());
addFields.add(bugCustomField);
});
}
}
if (CollectionUtils.isNotEmpty(addFields)) {
bugCustomFieldMapper.batchInsert(addFields);