feat(测试计划): 批量编辑标签
This commit is contained in:
parent
10b97d1b51
commit
cd70685204
|
@ -460,6 +460,7 @@ message.domain.test_plan_plannedEndTime=计划结束时间
|
|||
message.domain.test_plan_actualStartTime=实际开始时间
|
||||
message.domain.test_plan_actualEndTime=实际结束时间
|
||||
message.domain.test_plan_num=编号
|
||||
message.domain.test_plan_name=名称
|
||||
message.domain.test_plan_type=类型
|
||||
# 用例评审
|
||||
message.domain.case_review_name=名称
|
||||
|
|
|
@ -461,6 +461,7 @@ message.domain.test_plan_plannedEndTime=計劃結束時間
|
|||
message.domain.test_plan_actualStartTime=實際開始時間
|
||||
message.domain.test_plan_actualEndTime=實際結束時間
|
||||
message.domain.test_plan_num=編號
|
||||
message.domain.test_plan_name=名稱
|
||||
message.domain.test_plan_type=類型
|
||||
# 用例評審
|
||||
message.domain.case_review_name=名稱
|
||||
|
|
|
@ -180,4 +180,15 @@ public class TestPlanController {
|
|||
testPlanManagementService.checkModuleIsOpen(request.getTestPlanId(), TestPlanResourceConfig.CHECK_TYPE_TEST_PLAN, Collections.singletonList(TestPlanResourceConfig.CONFIG_TEST_PLAN_FUNCTIONAL_CASE));
|
||||
testPlanService.association(request);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/batch-edit")
|
||||
@Operation(summary = "测试计划-批量编辑")
|
||||
@RequiresPermissions(PermissionConstants.TEST_PLAN_READ_UPDATE)
|
||||
@CheckOwner(resourceId = "#request.getProjectId()", resourceType = "project")
|
||||
@Log(type = OperationLogType.UPDATE, expression = "#msClass.batchEditLog(#request)", msClass = TestPlanLogService.class)
|
||||
public void batchEdit(@Validated @RequestBody TestPlanBatchEditRequest request) {
|
||||
testPlanManagementService.checkModuleIsOpen(request.getProjectId(), TestPlanResourceConfig.CHECK_TYPE_PROJECT, Collections.singletonList(TestPlanResourceConfig.CONFIG_TEST_PLAN));
|
||||
testPlanService.batchEdit(request, SessionUtils.getUserId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
package io.metersphere.plan.dto;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
public class TestPlanDTO {
|
||||
|
||||
@Schema(description ="message.domain.test_plan_num")
|
||||
private String num;
|
||||
|
||||
@Schema(description ="message.domain.test_plan_name")
|
||||
private String name;
|
||||
|
||||
@Schema(description ="message.domain.test_plan_status")
|
||||
private String status;
|
||||
|
||||
@Schema(description ="message.domain.test_plan_type")
|
||||
private String type;
|
||||
|
||||
@Schema(description ="message.domain.test_plan_tags")
|
||||
private List<String> tags;
|
||||
|
||||
@Schema(description = "message.domain.test_plan_createTime")
|
||||
private Long createTime;
|
||||
|
||||
@Schema(description = "message.domain.test_plan_createUser")
|
||||
private String createUser;
|
||||
|
||||
@Schema(description = "message.domain.test_plan_updateTime")
|
||||
private Long updateTime;
|
||||
|
||||
@Schema(description = "message.domain.test_plan_updateUser")
|
||||
private String updateUser;
|
||||
|
||||
@Schema(description = "message.domain.test_plan_plannedStartTime")
|
||||
private Long plannedStartTime;
|
||||
|
||||
@Schema(description = "message.domain.test_plan_plannedEndTime")
|
||||
private Long plannedEndTime;
|
||||
|
||||
@Schema(description = "message.domain.test_plan_actualStartTime")
|
||||
private Long actualStartTime;
|
||||
|
||||
@Schema(description = "message.domain.test_plan_actualEndTime")
|
||||
private Long actualEndTime;
|
||||
|
||||
@Schema(description = "message.domain.test_plan_description")
|
||||
private Long description;
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package io.metersphere.plan.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author wx
|
||||
*/
|
||||
@Data
|
||||
public class TestPlanBatchEditRequest extends TestPlanBatchProcessRequest {
|
||||
|
||||
@Schema(description = "是否追加")
|
||||
private boolean append;
|
||||
|
||||
@Schema(description = "标签")
|
||||
private List<String> tags;
|
||||
|
||||
}
|
|
@ -32,4 +32,8 @@ public interface ExtTestPlanMapper {
|
|||
void batchUpdateStatus(@Param("status") String status, @Param("userId") String userId, @Param("updateTime") Long updateTime, @Param("ids") List<String> ids);
|
||||
|
||||
void batchMove(@Param("ids") List<String> ids, @Param("moduleId") String moduleId, @Param("userId") String userId, @Param("updateTime") long updateTime);
|
||||
|
||||
List<TestPlan> getTagsByIds(@Param("ids") List<String> ids);
|
||||
|
||||
void batchUpdate(@Param("testPlan") TestPlan testPlan, @Param("ids") List<String> ids);
|
||||
}
|
||||
|
|
|
@ -378,4 +378,32 @@
|
|||
</foreach>
|
||||
</update>
|
||||
|
||||
<select id="getTagsByIds" resultType="io.metersphere.plan.domain.TestPlan">
|
||||
select id, tags from test_plan
|
||||
where id in
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
|
||||
<update id="batchUpdate">
|
||||
update test_plan
|
||||
<set>
|
||||
<if test="testPlan.tags != null and testPlan.tags != ''">
|
||||
tags = #{testPlan.tags,typeHandler=io.metersphere.handler.ListTypeHandler},
|
||||
</if>
|
||||
<if test="testPlan.updateUser != null and testPlan.updateUser != ''">
|
||||
update_user = #{testPlan.updateUser},
|
||||
</if>
|
||||
<if test="testPlan.updateTime != null">
|
||||
update_time = #{testPlan.updateTime},
|
||||
</if>
|
||||
</set>
|
||||
where id in
|
||||
<foreach collection="ids" item="id" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
and project_id = #{testPlan.projectId}
|
||||
</update>
|
||||
</mapper>
|
|
@ -1,6 +1,8 @@
|
|||
package io.metersphere.plan.service;
|
||||
|
||||
import io.metersphere.plan.domain.TestPlan;
|
||||
import io.metersphere.plan.domain.TestPlanExample;
|
||||
import io.metersphere.plan.dto.request.TestPlanBatchEditRequest;
|
||||
import io.metersphere.plan.dto.request.TestPlanCopyRequest;
|
||||
import io.metersphere.plan.mapper.TestPlanMapper;
|
||||
import io.metersphere.project.domain.Project;
|
||||
|
@ -15,12 +17,15 @@ import io.metersphere.system.log.constants.OperationLogType;
|
|||
import io.metersphere.system.log.dto.LogDTO;
|
||||
import io.metersphere.system.log.service.OperationLogService;
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
|
@ -37,6 +42,7 @@ public class TestPlanLogService {
|
|||
|
||||
/**
|
||||
* 新增计划日志
|
||||
*
|
||||
* @param module 模块
|
||||
* @param operator 操作人
|
||||
* @param requestUrl 请求路径
|
||||
|
@ -61,6 +67,7 @@ public class TestPlanLogService {
|
|||
|
||||
/**
|
||||
* 修改计划日志
|
||||
*
|
||||
* @param oldTestPlan 旧计划
|
||||
* @param newTestPlan 新计划
|
||||
* @param projectId 项目ID
|
||||
|
@ -88,6 +95,7 @@ public class TestPlanLogService {
|
|||
|
||||
/**
|
||||
* 删除计划日志
|
||||
*
|
||||
* @param deleteTestPlan 删除计划
|
||||
* @param operator 操作人
|
||||
* @param requestUrl 请求URL
|
||||
|
@ -157,6 +165,7 @@ public class TestPlanLogService {
|
|||
|
||||
/**
|
||||
* 保存批量日志
|
||||
*
|
||||
* @param plans 计划
|
||||
* @param operator 操作人
|
||||
* @param requestUrl 请求URL
|
||||
|
@ -187,6 +196,7 @@ public class TestPlanLogService {
|
|||
|
||||
/**
|
||||
* 生成计划操作日志内容
|
||||
*
|
||||
* @param testPlan 测试计划
|
||||
* @param type 类型
|
||||
* @return 日志内容
|
||||
|
@ -200,4 +210,39 @@ public class TestPlanLogService {
|
|||
}
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 批量编辑
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public List<LogDTO> batchEditLog(TestPlanBatchEditRequest request) {
|
||||
// 测试计划不支持全选所有页
|
||||
List<String> ids = request.getSelectIds();
|
||||
List<LogDTO> dtoList = new ArrayList<>();
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
TestPlanExample example = new TestPlanExample();
|
||||
example.createCriteria().andProjectIdEqualTo(request.getProjectId()).andIdIn(ids);
|
||||
List<TestPlan> testPlans = testPlanMapper.selectByExample(example);
|
||||
Map<String, TestPlan> collect = testPlans.stream().collect(Collectors.toMap(TestPlan::getId, testPlan -> testPlan));
|
||||
ids.forEach(id -> {
|
||||
TestPlan testPlan = collect.get(id);
|
||||
LogDTO dto = new LogDTO(
|
||||
testPlan.getProjectId(),
|
||||
null,
|
||||
testPlan.getId(),
|
||||
null,
|
||||
OperationLogType.UPDATE.name(),
|
||||
OperationLogModule.TEST_PLAN,
|
||||
testPlan.getName());
|
||||
dto.setPath("/test-plan/batch-edit");
|
||||
dto.setMethod(HttpMethodConstants.POST.name());
|
||||
dto.setOriginalValue(JSON.toJSONBytes(testPlan));
|
||||
dtoList.add(dto);
|
||||
});
|
||||
}
|
||||
return dtoList;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,24 +3,28 @@ package io.metersphere.plan.service;
|
|||
import io.metersphere.functional.domain.FunctionalCase;
|
||||
import io.metersphere.functional.mapper.FunctionalCaseMapper;
|
||||
import io.metersphere.plan.domain.TestPlan;
|
||||
import io.metersphere.plan.domain.TestPlanExample;
|
||||
import io.metersphere.plan.dto.TestPlanDTO;
|
||||
import io.metersphere.plan.mapper.TestPlanMapper;
|
||||
import io.metersphere.sdk.util.BeanUtils;
|
||||
import io.metersphere.sdk.util.JSON;
|
||||
import io.metersphere.system.domain.User;
|
||||
import io.metersphere.system.mapper.UserMapper;
|
||||
import io.metersphere.system.notice.NoticeModel;
|
||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||
import io.metersphere.system.notice.utils.MessageTemplateUtils;
|
||||
import io.metersphere.system.service.CommonNoticeSendService;
|
||||
import io.metersphere.system.service.NoticeSendService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.apache.commons.beanutils.BeanMap;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
|
@ -34,6 +38,8 @@ public class TestPlanSendNoticeService {
|
|||
private TestPlanMapper testPlanMapper;
|
||||
@Resource
|
||||
private NoticeSendService noticeSendService;
|
||||
@Resource
|
||||
private CommonNoticeSendService commonNoticeSendService;
|
||||
|
||||
public void sendNoticeCase(List<String> relatedUsers, String userId, String caseId, String task, String event, String testPlanId) {
|
||||
FunctionalCase functionalCase = functionalCaseMapper.selectByPrimaryKey(caseId);
|
||||
|
@ -71,4 +77,45 @@ public class TestPlanSendNoticeService {
|
|||
}
|
||||
LocaleContextHolder.setLocale(locale);
|
||||
}
|
||||
|
||||
|
||||
public void batchSendNotice(String projectId, List<String> ids, User user, String event) {
|
||||
int amount = 100;//每次读取的条数
|
||||
long roundTimes = Double.valueOf(Math.ceil((double) ids.size() / amount)).longValue();//循环的次数
|
||||
for (int i = 0; i < (int) roundTimes; i++) {
|
||||
int fromIndex = (i * amount);
|
||||
int toIndex = ((i + 1) * amount);
|
||||
if (i == roundTimes - 1 || toIndex > ids.size()) {//最后一次遍历
|
||||
toIndex = ids.size();
|
||||
}
|
||||
List<String> subList = ids.subList(fromIndex, toIndex);
|
||||
List<TestPlanDTO> testPlanDTOS = handleBatchNotice(projectId, subList);
|
||||
List<Map> resources = new ArrayList<>();
|
||||
resources.addAll(JSON.parseArray(JSON.toJSONString(testPlanDTOS), Map.class));
|
||||
commonNoticeSendService.sendNotice(NoticeConstants.TaskType.TEST_PLAN_TASK, event, resources, user, projectId);
|
||||
}
|
||||
}
|
||||
|
||||
private List<TestPlanDTO> handleBatchNotice(String projectId, List<String> ids) {
|
||||
List<TestPlanDTO> dtoList = new ArrayList<>();
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
Map<String, TestPlan> testPlanMap = getTestPlanInfo(projectId, ids);
|
||||
ids.forEach(id -> {
|
||||
if (testPlanMap.containsKey(id)) {
|
||||
TestPlan testPlan = testPlanMap.get(id);
|
||||
TestPlanDTO testPlanDTO = new TestPlanDTO();
|
||||
BeanUtils.copyBean(testPlanDTO, testPlan);
|
||||
dtoList.add(testPlanDTO);
|
||||
}
|
||||
});
|
||||
}
|
||||
return dtoList;
|
||||
}
|
||||
|
||||
private Map<String, TestPlan> getTestPlanInfo(String projectId, List<String> ids) {
|
||||
TestPlanExample example = new TestPlanExample();
|
||||
example.createCriteria().andProjectIdEqualTo(projectId).andIdIn(ids);
|
||||
List<TestPlan> testPlans = testPlanMapper.selectByExample(example);
|
||||
return testPlans.stream().collect(Collectors.toMap(TestPlan::getId, testPlan -> testPlan));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,12 @@ import io.metersphere.sdk.util.BeanUtils;
|
|||
import io.metersphere.sdk.util.CommonBeanFactory;
|
||||
import io.metersphere.sdk.util.Translator;
|
||||
import io.metersphere.system.domain.ScheduleExample;
|
||||
import io.metersphere.system.domain.User;
|
||||
import io.metersphere.system.log.constants.OperationLogType;
|
||||
import io.metersphere.system.mapper.ScheduleMapper;
|
||||
import io.metersphere.system.mapper.TestPlanModuleMapper;
|
||||
import io.metersphere.system.mapper.UserMapper;
|
||||
import io.metersphere.system.notice.constants.NoticeConstants;
|
||||
import io.metersphere.system.uid.IDGenerator;
|
||||
import io.metersphere.system.uid.NumGenerator;
|
||||
import io.metersphere.system.utils.BatchProcessUtils;
|
||||
|
@ -24,6 +27,10 @@ import org.apache.commons.collections4.CollectionUtils;
|
|||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
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.transaction.annotation.Transactional;
|
||||
|
||||
|
@ -59,7 +66,13 @@ public class TestPlanService extends TestPlanBaseUtilsService {
|
|||
private TestPlanCaseService testPlanCaseService;
|
||||
@Resource
|
||||
private ScheduleMapper scheduleMapper;
|
||||
|
||||
@Resource
|
||||
SqlSessionFactory sqlSessionFactory;
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
@Resource
|
||||
private TestPlanSendNoticeService testPlanSendNoticeService;
|
||||
private static final int MAX_TAG_SIZE = 10;
|
||||
|
||||
/**
|
||||
* 创建测试计划
|
||||
|
@ -157,6 +170,7 @@ public class TestPlanService extends TestPlanBaseUtilsService {
|
|||
|
||||
/**
|
||||
* 计划组删除的相关逻辑(待定)
|
||||
*
|
||||
* @param testPlanGroupIds 计划组ID集合
|
||||
*/
|
||||
private void deleteGroupByList(List<String> testPlanGroupIds) {
|
||||
|
@ -531,4 +545,79 @@ public class TestPlanService extends TestPlanBaseUtilsService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 批量编辑
|
||||
*
|
||||
* @param request
|
||||
* @param userId
|
||||
*/
|
||||
public void batchEdit(TestPlanBatchEditRequest request, String userId) {
|
||||
// 目前计划的批量操作不支持全选所有页
|
||||
List<String> ids = request.getSelectIds();
|
||||
if (CollectionUtils.isNotEmpty(ids)) {
|
||||
User user = userMapper.selectByPrimaryKey(userId);
|
||||
handleTags(request, userId, ids);
|
||||
testPlanSendNoticeService.batchSendNotice(request.getProjectId(), ids, user, NoticeConstants.Event.UPDATE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理标签
|
||||
*
|
||||
* @param request
|
||||
* @param userId
|
||||
* @param ids
|
||||
*/
|
||||
private void handleTags(TestPlanBatchEditRequest request, String userId, List<String> ids) {
|
||||
if (CollectionUtils.isNotEmpty(request.getTags())) {
|
||||
if (request.isAppend()) {
|
||||
//追加标签
|
||||
List<TestPlan> planList = extTestPlanMapper.getTagsByIds(ids);
|
||||
Map<String, TestPlan> collect = planList.stream().collect(Collectors.toMap(TestPlan::getId, v -> v));
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
TestPlanMapper testPlanMapper = sqlSession.getMapper(TestPlanMapper.class);
|
||||
ids.forEach(id -> {
|
||||
TestPlan testPlan = new TestPlan();
|
||||
if (CollectionUtils.isNotEmpty(collect.get(id).getTags())) {
|
||||
List<String> tags = collect.get(id).getTags();
|
||||
tags.addAll(request.getTags());
|
||||
checkTagsLength(tags);
|
||||
List<String> newTags = tags.stream().distinct().collect(Collectors.toList());
|
||||
testPlan.setTags(newTags);
|
||||
} else {
|
||||
testPlan.setTags(request.getTags());
|
||||
}
|
||||
testPlan.setId(id);
|
||||
testPlan.setUpdateTime(System.currentTimeMillis());
|
||||
testPlan.setUpdateUser(userId);
|
||||
testPlanMapper.updateByPrimaryKeySelective(testPlan);
|
||||
});
|
||||
sqlSession.flushStatements();
|
||||
SqlSessionUtils.closeSqlSession(sqlSession, sqlSessionFactory);
|
||||
} else {
|
||||
//替换标签
|
||||
TestPlan testPlan = new TestPlan();
|
||||
testPlan.setTags(request.getTags());
|
||||
testPlan.setProjectId(request.getProjectId());
|
||||
testPlan.setUpdateTime(System.currentTimeMillis());
|
||||
testPlan.setUpdateUser(userId);
|
||||
extTestPlanMapper.batchUpdate(testPlan, ids);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 校验追加标签长度
|
||||
*
|
||||
* @param tags
|
||||
*/
|
||||
private void checkTagsLength(List<String> tags) {
|
||||
if (tags.size() > MAX_TAG_SIZE) {
|
||||
throw new MSException(Translator.getWithArgs("tags_length_large_than", String.valueOf(MAX_TAG_SIZE)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -118,6 +118,7 @@ public class TestPlanTests extends BaseTest {
|
|||
private static final String URL_TEST_PLAN_BATCH_COPY = "/test-plan/batch-copy";
|
||||
private static final String URL_TEST_PLAN_BATCH_MOVE = "/test-plan/batch-move";
|
||||
private static final String URL_TEST_PLAN_BATCH_ARCHIVED = "/test-plan/batch-archived";
|
||||
private static final String URL_TEST_PLAN_BATCH_EDIT = "/test-plan/batch-edit";
|
||||
|
||||
private static String groupTestPlanId7 = null;
|
||||
private static String groupTestPlanId15 = null;
|
||||
|
@ -1849,4 +1850,21 @@ public class TestPlanTests extends BaseTest {
|
|||
this.requestPostWithOk(URL_POST_TEST_PLAN_STATISTICS, List.of("wx_test_plan_id_7"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Order(307)
|
||||
public void testBatchEditTestPlan() throws Exception {
|
||||
TestPlanBatchEditRequest request = new TestPlanBatchEditRequest();
|
||||
request.setTags(Arrays.asList("tag1", "tag2"));
|
||||
request.setAppend(true);
|
||||
request.setType("ALL");
|
||||
request.setProjectId("123");
|
||||
request.setSelectIds(Arrays.asList("wx_test_plan_id_1"));
|
||||
this.requestPostWithOk(URL_TEST_PLAN_BATCH_EDIT, request);
|
||||
request.setAppend(false);
|
||||
request.setTags(Arrays.asList("tag3", "tag4"));
|
||||
request.setSelectIds(Arrays.asList("wx_test_plan_id_1"));
|
||||
this.requestPostWithOk(URL_TEST_PLAN_BATCH_EDIT, request);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue