refactor(系统设置): 配额管理开源
--task=1010908 --user=李玉号 【开源计划】租户配额管理 https://www.tapd.cn/55049933/s/1327251
This commit is contained in:
parent
d4b0d51df4
commit
49e384231d
|
@ -41,6 +41,7 @@ import io.metersphere.log.vo.api.DefinitionReference;
|
|||
import io.metersphere.notice.sender.NoticeModel;
|
||||
import io.metersphere.notice.service.NoticeSendService;
|
||||
import io.metersphere.plugin.core.MsTestElement;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.RelationshipEdgeRequest;
|
||||
import io.metersphere.request.ResetOrderRequest;
|
||||
import io.metersphere.request.SyncApiDefinitionRequest;
|
||||
|
@ -52,7 +53,6 @@ import io.metersphere.service.plan.TestPlanApiCaseService;
|
|||
import io.metersphere.service.scenario.ApiScenarioService;
|
||||
import io.metersphere.xpack.api.service.ApiCaseBatchSyncService;
|
||||
import io.metersphere.xpack.api.service.ApiDefinitionSyncService;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import org.apache.commons.beanutils.BeanComparator;
|
||||
import org.apache.commons.beanutils.BeanMap;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
@ -160,6 +160,8 @@ public class ApiDefinitionService {
|
|||
|
||||
@Resource
|
||||
private ApiDefinitionImportUtilService apiDefinitionImportUtilService;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
|
||||
private static final String COPY = "Copy";
|
||||
|
@ -432,10 +434,7 @@ public class ApiDefinitionService {
|
|||
|
||||
|
||||
public void checkQuota(String projectId) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService != null) {
|
||||
quotaService.checkAPIDefinitionQuota(projectId);
|
||||
}
|
||||
baseQuotaService.checkAPIDefinitionQuota(projectId);
|
||||
}
|
||||
|
||||
public void delete(String apiId) {
|
||||
|
|
|
@ -48,6 +48,7 @@ import io.metersphere.log.vo.schedule.ScheduleReference;
|
|||
import io.metersphere.notice.sender.NoticeModel;
|
||||
import io.metersphere.notice.service.NoticeSendService;
|
||||
import io.metersphere.plugin.core.MsTestElement;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.ResetOrderRequest;
|
||||
import io.metersphere.sechedule.ApiScenarioTestJob;
|
||||
import io.metersphere.sechedule.SwaggerUrlImportJob;
|
||||
|
@ -57,7 +58,6 @@ import io.metersphere.service.ext.ExtApiScheduleService;
|
|||
import io.metersphere.service.ext.ExtFileAssociationService;
|
||||
import io.metersphere.service.plan.TestPlanScenarioCaseService;
|
||||
import io.metersphere.xpack.api.service.ApiAutomationRelationshipEdgeService;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
|
@ -157,6 +157,8 @@ public class ApiScenarioService {
|
|||
private ExtApiScenarioReferenceIdMapper extApiScenarioReferenceIdMapper;
|
||||
@Resource
|
||||
private ExtTestPlanApiScenarioMapper extTestPlanApiScenarioMapper;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
private ThreadLocal<Long> currentScenarioOrder = new ThreadLocal<>();
|
||||
|
||||
|
@ -297,10 +299,7 @@ public class ApiScenarioService {
|
|||
}
|
||||
|
||||
private void checkQuota(String projectId) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService != null) {
|
||||
quotaService.checkAPIAutomationQuota(projectId);
|
||||
}
|
||||
baseQuotaService.checkAPIAutomationQuota(projectId);
|
||||
}
|
||||
|
||||
private void uploadFiles(SaveApiScenarioRequest request, List<MultipartFile> bodyFiles, List<MultipartFile> scenarioFiles) {
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.base.mapper.ext;
|
||||
|
||||
import io.metersphere.base.domain.LoadTestReportResult;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Repository
|
||||
public interface ExtLoadTestReportResultMapper {
|
||||
|
||||
@Select("SELECT * FROM load_test_report_result WHERE report_id = #{id} AND report_key = #{key}")
|
||||
List<LoadTestReportResult> selectByIdAndKey(@Param("id") String id, @Param("key") String key);
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package io.metersphere.base.mapper.ext;
|
||||
|
||||
import io.metersphere.base.domain.Quota;
|
||||
import io.metersphere.quota.dto.CountDto;
|
||||
import io.metersphere.quota.dto.QuotaResult;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ExtQuotaMapper {
|
||||
|
||||
List<QuotaResult> listWorkspaceQuota(@Param("name") String name);
|
||||
|
||||
Quota getWorkspaceQuota(@Param("workspaceId") String workspaceId);
|
||||
|
||||
List<Quota> listUseDefaultOrgQuota();
|
||||
|
||||
List<Quota> listUseDefaultWsQuota();
|
||||
|
||||
List<Quota> listUseDefaultProjectQuota(String workspaceId);
|
||||
|
||||
long countAPITest(@Param("workspaceIds") List<String> workspaceIds);
|
||||
|
||||
long countLoadTest(@Param("projectIds") List<String> workspaceIds);
|
||||
|
||||
long countAPIDefinition(@Param("projectIds") List<String> ids);
|
||||
|
||||
long countAPIAutomation(@Param("projectIds") List<String> ids);
|
||||
|
||||
List<QuotaResult> listProjectQuota(@Param("wsId") String workspaceId, @Param("name") String name);
|
||||
|
||||
Quota getProjectQuota(String projectId);
|
||||
|
||||
Long countMember(@Param("sourceId") String sourceId);
|
||||
|
||||
Long countWorkspaceProject(@Param("workspaceId") String workspaceId);
|
||||
|
||||
List<Quota> listQuotaBySourceIds(@Param("sourceIds") List<String> sourceIds);
|
||||
|
||||
List<CountDto> listUserBySourceIds(@Param("sourceIds") List<String> sourceIds);
|
||||
|
||||
long listUserByWorkspaceAndProjectIds(@Param("sourceIds") List<String> sourceIds, @Param("memberIds") List<String> memberIds);
|
||||
|
||||
Quota getProjectQuotaSum(String workspaceId);
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
|
||||
<mapper namespace="io.metersphere.base.mapper.ext.ExtQuotaMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="io.metersphere.quota.dto.QuotaResult"
|
||||
extends="io.metersphere.base.mapper.QuotaMapper.BaseResultMap">
|
||||
|
||||
<result column="project_name" property="projectName"/>
|
||||
<result column="workspace_name" property="workspaceName"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="listWorkspaceQuota" resultMap="BaseResultMap">
|
||||
select w.name as workspace_name, w.id as workspace_id, q.id, q.api, q.performance,
|
||||
q.max_threads, q.duration, q.resource_pool, q.workspace_id, q.use_default, q.update_time,
|
||||
q.member, q.project, q.project_id, q.vum_total, temp.vum_used, temp.project_used
|
||||
from workspace as w
|
||||
left join quota as q on w.id = q.workspace_id
|
||||
inner join (select sum(vum_used) as vum_used, workspace.id, count(p.id) as project_used
|
||||
from workspace
|
||||
left join project p on p.workspace_id = workspace.id
|
||||
left join quota on quota.project_id = p.id
|
||||
group by workspace.id) as temp
|
||||
on temp.id = w.id
|
||||
<where>
|
||||
<if test="name != null">
|
||||
and w.name like CONCAT('%', #{name},'%')
|
||||
</if>
|
||||
</where>
|
||||
order by q.update_time desc, workspace_name
|
||||
</select>
|
||||
|
||||
<select id="getWorkspaceQuota" resultMap="io.metersphere.base.mapper.QuotaMapper.BaseResultMap">
|
||||
SELECT *
|
||||
FROM quota
|
||||
<where>
|
||||
workspace_id = #{workspaceId}
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="listUseDefaultOrgQuota" resultMap="io.metersphere.base.mapper.QuotaMapper.BaseResultMap">
|
||||
SELECT *
|
||||
FROM quota
|
||||
<where>
|
||||
organization_id IS NOT NULL
|
||||
AND workspace_id IS NULL
|
||||
AND use_default = '1'
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="listUseDefaultWsQuota" resultMap="io.metersphere.base.mapper.QuotaMapper.BaseResultMap">
|
||||
SELECT *
|
||||
FROM quota
|
||||
<where>
|
||||
workspace_id IS NOT NULL
|
||||
AND use_default = '1'
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="countAPITest" resultType="java.lang.Long">
|
||||
SELECT count(*)
|
||||
FROM api_test LEFT JOIN project ON api_test.project_id = project.id
|
||||
WHERE project.workspace_id in
|
||||
<foreach collection="workspaceIds" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="countLoadTest" resultType="java.lang.Long">
|
||||
SELECT count(*)
|
||||
FROM load_test LEFT JOIN project ON load_test.project_id = project.id
|
||||
WHERE project.id in
|
||||
<foreach collection="projectIds" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="countAPIDefinition" resultType="long">
|
||||
SELECT count(*)
|
||||
FROM api_definition
|
||||
WHERE api_definition.status != 'Trash' and api_definition.latest = 1 and api_definition.project_id in
|
||||
<foreach collection="projectIds" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
<select id="countAPIAutomation" resultType="long">
|
||||
SELECT count(*)
|
||||
FROM api_scenario
|
||||
WHERE api_scenario.status != 'Trash' and api_scenario.latest = 1 and api_scenario.project_id in
|
||||
<foreach collection="projectIds" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</select>
|
||||
<select id="listUseDefaultProjectQuota" resultType="io.metersphere.base.domain.Quota">
|
||||
select *
|
||||
from quota join project on quota.project_id = project.id
|
||||
<where>
|
||||
project_id is not null
|
||||
and project.workspace_id = #{workspaceId}
|
||||
and quota.workspace_id is null
|
||||
and use_default = '1'
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="listProjectQuota" resultMap="BaseResultMap">
|
||||
SELECT p.name AS project_name, p.id AS project_id, p.workspace_id AS workspace_id, q.*
|
||||
FROM project AS p LEFT JOIN quota AS q
|
||||
ON p.id = q.project_id
|
||||
<where>
|
||||
<if test="wsId != null">
|
||||
and p.workspace_id = #{wsId}
|
||||
</if>
|
||||
<if test="name != null">
|
||||
and p.name like CONCAT('%', #{name},'%')
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY q.update_time DESC, project_name
|
||||
</select>
|
||||
|
||||
<select id="getProjectQuota" resultMap="io.metersphere.base.mapper.QuotaMapper.BaseResultMap">
|
||||
SELECT *
|
||||
FROM quota
|
||||
<where>
|
||||
project_id = #{projectId}
|
||||
and workspace_id IS NULL
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="countMember" resultType="java.lang.Long">
|
||||
SELECT count(*)
|
||||
FROM user_group
|
||||
WHERE user_group.source_id = #{sourceId}
|
||||
</select>
|
||||
|
||||
<select id="countWorkspaceProject" resultType="java.lang.Long">
|
||||
SELECT count(*)
|
||||
FROM project
|
||||
WHERE project.workspace_id = #{workspaceId}
|
||||
</select>
|
||||
|
||||
<select id="listQuotaBySourceIds" resultMap="io.metersphere.base.mapper.QuotaMapper.BaseResultMap">
|
||||
SELECT *
|
||||
FROM quota
|
||||
<where>
|
||||
project_id in
|
||||
<foreach collection="sourceIds" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
or
|
||||
workspace_id in
|
||||
<foreach collection="sourceIds" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="listUserBySourceIds" resultType="io.metersphere.quota.dto.CountDto">
|
||||
SELECT source_id, count(distinct(user.id)) as count
|
||||
FROM user_group join user on user_group.user_id = user.id
|
||||
<where>
|
||||
source_id in
|
||||
<foreach collection="sourceIds" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
group by source_id
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="listUserByWorkspaceAndProjectIds" resultType="java.lang.Long">
|
||||
SELECT count(distinct(user.id)) as count
|
||||
FROM user_group join user on user_group.user_id = user.id
|
||||
<where>
|
||||
source_id in
|
||||
<foreach collection="sourceIds" item="id" separator="," open="(" close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
<if test="memberIds != null and memberIds.size() != 0">
|
||||
and user_id not in
|
||||
<foreach collection="memberIds" item="memberId" separator="," open="(" close=")">
|
||||
#{memberId}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="getProjectQuotaSum" resultMap="io.metersphere.base.mapper.QuotaMapper.BaseResultMap">
|
||||
select sum(vum_used) as vum_used
|
||||
from quota
|
||||
<where>
|
||||
quota.project_id
|
||||
in (select id from project where project.workspace_id = #{workspaceId})
|
||||
</where>
|
||||
</select>
|
||||
|
||||
</mapper>
|
|
@ -0,0 +1,81 @@
|
|||
package io.metersphere.quota.controller;
|
||||
|
||||
import com.github.pagehelper.Page;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import io.metersphere.base.domain.Quota;
|
||||
import io.metersphere.commons.constants.OperLogConstants;
|
||||
import io.metersphere.commons.constants.OperLogModule;
|
||||
import io.metersphere.commons.utils.PageUtils;
|
||||
import io.metersphere.commons.utils.Pager;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
import io.metersphere.log.annotation.MsAuditLog;
|
||||
import io.metersphere.quota.dto.QuotaConstants;
|
||||
import io.metersphere.quota.dto.QuotaResult;
|
||||
import io.metersphere.quota.service.QuotaManagementService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping(value = "/quota")
|
||||
|
||||
public class QuotaController {
|
||||
|
||||
@Resource
|
||||
private QuotaManagementService quotaManagementService;
|
||||
|
||||
@GetMapping("/default/workspace")
|
||||
public Quota getWsDefaultQuota() {
|
||||
return quotaManagementService.getDefaultQuota(QuotaConstants.DefaultType.workspace);
|
||||
}
|
||||
|
||||
@GetMapping("/default/project/{workspaceId}")
|
||||
public Quota getProjectDefaultQuota(@PathVariable String workspaceId) {
|
||||
return quotaManagementService.getProjectDefaultQuota(workspaceId);
|
||||
}
|
||||
|
||||
@PostMapping("/save/default/workspace")
|
||||
@MsAuditLog(module = OperLogModule.SYSTEM_QUOTA_MANAGEMENT, type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#quota.id)", content = "#msClass.getLogDetails(#quota.id)", msClass = QuotaManagementService.class)
|
||||
public void saveWsDefaultQuota(@RequestBody Quota quota) {
|
||||
quota.setId(QuotaConstants.DefaultType.workspace.name());
|
||||
quotaManagementService.saveQuota(quota);
|
||||
}
|
||||
|
||||
@PostMapping("/save/default/project")
|
||||
@MsAuditLog(module = OperLogModule.SYSTEM_QUOTA_MANAGEMENT, type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#quota.id)", content = "#msClass.getLogDetails(#quota.id)", msClass = QuotaManagementService.class)
|
||||
public void saveProjectDefaultQuota(@RequestBody Quota quota) {
|
||||
if (StringUtils.isBlank(quota.getId())) {
|
||||
quota.setId(SessionUtils.getCurrentWorkspaceId());
|
||||
}
|
||||
quota.setId(QuotaConstants.prefix + quota.getId());
|
||||
quotaManagementService.saveQuota(quota);
|
||||
}
|
||||
|
||||
@PostMapping("/list/workspace/{goPage}/{pageSize}")
|
||||
public Pager<List<QuotaResult>> listWsQuota(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody Map<String, String> param) {
|
||||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
return PageUtils.setPageInfo(page, quotaManagementService.listWorkspaceQuota(param.get("name")));
|
||||
}
|
||||
|
||||
@PostMapping("/list/project/{goPage}/{pageSize}")
|
||||
public Pager<List<QuotaResult>> listProjectQuota(@PathVariable int goPage, @PathVariable int pageSize, @RequestBody Map<String, String> param) {
|
||||
Page<Object> page = PageHelper.startPage(goPage, pageSize, true);
|
||||
return PageUtils.setPageInfo(page, quotaManagementService.listProjectQuota(param.get("workspaceId"), param.get("name")));
|
||||
}
|
||||
|
||||
@PostMapping("/save")
|
||||
@MsAuditLog(module = OperLogModule.SYSTEM_QUOTA_MANAGEMENT, type = OperLogConstants.UPDATE, beforeEvent = "#msClass.getLogDetails(#quota.id)", content = "#msClass.getLogDetails(#quota.id)", msClass = QuotaManagementService.class)
|
||||
public void saveQuota(@RequestBody Quota quota) {
|
||||
quotaManagementService.saveQuota(quota);
|
||||
}
|
||||
|
||||
@PostMapping("/delete")
|
||||
@MsAuditLog(module = OperLogModule.SYSTEM_QUOTA_MANAGEMENT, type = OperLogConstants.DELETE, beforeEvent = "#msClass.getLogDetails(#quota.id)", msClass = QuotaManagementService.class)
|
||||
public void delete(@RequestBody Quota quota) {
|
||||
quotaManagementService.deleteQuota(quota.getId());
|
||||
}
|
||||
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.xpack.quota.dto;
|
||||
package io.metersphere.quota.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.xpack.quota.dto;
|
||||
package io.metersphere.quota.dto;
|
||||
|
||||
public class QuotaConstants {
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package io.metersphere.xpack.quota.dto;
|
||||
package io.metersphere.quota.dto;
|
||||
|
||||
import io.metersphere.base.domain.Quota;
|
||||
import lombok.Getter;
|
|
@ -0,0 +1,10 @@
|
|||
package io.metersphere.quota.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ReportTimeInfo {
|
||||
private long duration;
|
||||
private long startTime;
|
||||
private long endTime;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package io.metersphere.quota.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TestOverview {
|
||||
|
||||
private String maxUsers;
|
||||
private String avgThroughput;
|
||||
private String errors;
|
||||
private String avgResponseTime;
|
||||
private String responseTime90;
|
||||
private String avgBandwidth;
|
||||
private String avgTransactions;
|
||||
}
|
|
@ -0,0 +1,757 @@
|
|||
package io.metersphere.quota.service;
|
||||
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.ProjectMapper;
|
||||
import io.metersphere.base.mapper.QuotaMapper;
|
||||
import io.metersphere.base.mapper.WorkspaceMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtLoadTestReportResultMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtQuotaMapper;
|
||||
import io.metersphere.commons.constants.ReportKeys;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.JSON;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.commons.utils.SessionUtils;
|
||||
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.quota.dto.*;
|
||||
import io.metersphere.request.TestPlanRequest;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author lyh
|
||||
*/
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class BaseQuotaService {
|
||||
|
||||
@Resource
|
||||
private QuotaManagementService quotaManagementService;
|
||||
@Resource
|
||||
private ExtQuotaMapper extQuotaMapper;
|
||||
@Resource
|
||||
private QuotaMapper quotaMapper;
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
private WorkspaceMapper workspaceMapper;
|
||||
@Resource
|
||||
private ExtLoadTestReportResultMapper extLoadTestReportResultMapper;
|
||||
|
||||
private static final String API = "API";
|
||||
private static final String LOAD = "LOAD";
|
||||
private static final String DURATION = "DURATION";
|
||||
private static final String MAX_THREAD = "MAX_THREAD";
|
||||
private static final String MEMBER = "MEMBER";
|
||||
private static final String PROJECT = "PROJECT";
|
||||
private static final String CHECK_PROJECT = "PROJECT";
|
||||
private static final String CHECK_WORKSPACE = "WORKSPACE";
|
||||
|
||||
private boolean isValid(Quota quota, Object obj) {
|
||||
boolean sign = quota != null;
|
||||
if (obj instanceof Integer) {
|
||||
return sign && quotaManagementService.isValid((Integer) obj);
|
||||
} else if (obj instanceof String) {
|
||||
return sign && quotaManagementService.isValid((String) obj);
|
||||
} else if (obj instanceof BigDecimal) {
|
||||
return sign && quotaManagementService.isValid((BigDecimal) obj);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<String> queryProjectIdsByWorkspaceId(String workspaceId) {
|
||||
ProjectExample example = new ProjectExample();
|
||||
example.createCriteria().andWorkspaceIdEqualTo(workspaceId);
|
||||
List<Project> projects = projectMapper.selectByExample(example);
|
||||
return projects.stream().map(Project::getId).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String queryWorkspaceId(String projectId) {
|
||||
Project project = projectMapper.selectByPrimaryKey(projectId);
|
||||
if (project == null) {
|
||||
return null;
|
||||
}
|
||||
return project.getWorkspaceId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口定义配额数量检查
|
||||
*/
|
||||
public void checkAPIDefinitionQuota(String projectId) {
|
||||
this.checkQuota(extQuotaMapper::countAPIDefinition, API, projectId,
|
||||
Translator.get("quota_api_excess_project"),
|
||||
Translator.get("quota_api_excess_workspace"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 接口自动化配额数量检查
|
||||
*/
|
||||
public void checkAPIAutomationQuota(String projectId) {
|
||||
this.checkQuota(extQuotaMapper::countAPIAutomation, API, projectId,
|
||||
Translator.get("quota_api_excess_project"),
|
||||
Translator.get("quota_api_excess_workspace"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 增量为1的配额检查方法
|
||||
*
|
||||
* @param queryFunc 查询已存在数量的方法
|
||||
* @param checkType 检查配额类型
|
||||
* @param projectId 项目ID
|
||||
* @param projectMessage 项目超出配额警告信息
|
||||
* @param workspaceMessage 工作空间超出配额警告信息
|
||||
*/
|
||||
public void checkQuota(Function<List<String>, Long> queryFunc, String checkType, String projectId, String projectMessage, String workspaceMessage) {
|
||||
if (queryFunc == null) {
|
||||
LogUtil.info("param warning. function is null");
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isBlank(projectId)) {
|
||||
return;
|
||||
}
|
||||
// 检查项目配额
|
||||
Quota qt = quotaManagementService.getProjectQuota(projectId);
|
||||
boolean isContinue = true;
|
||||
long count;
|
||||
if (qt != null) {
|
||||
count = queryFunc.apply(Collections.singletonList(projectId));
|
||||
// 数量+1后检查
|
||||
isContinue = this.doCheckQuota(qt, checkType, projectMessage, count + 1);
|
||||
}
|
||||
// 检查是否有工作空间限额
|
||||
if (isContinue) {
|
||||
String workspaceId = this.queryWorkspaceId(projectId);
|
||||
if (StringUtils.isBlank(workspaceId)) {
|
||||
return;
|
||||
}
|
||||
Quota quota = quotaManagementService.getWorkspaceQuota(workspaceId);
|
||||
if (quota == null) {
|
||||
return;
|
||||
}
|
||||
count = queryFunc.apply(this.queryProjectIdsByWorkspaceId(workspaceId));
|
||||
this.doCheckQuota(quota, checkType, workspaceMessage, count + 1);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doCheckQuota(Quota quota, String checkType, String errorMsg, long queryCount) {
|
||||
if (quota == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Object quotaCount = getQuotaCount(quota, checkType);
|
||||
if (isValid(quota, quotaCount)) {
|
||||
long count = Long.parseLong(String.valueOf(quotaCount));
|
||||
if (queryCount > count) {
|
||||
MSException.throwException(errorMsg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Object getQuotaCount(Quota quota, String type) {
|
||||
Object count = null;
|
||||
switch (type) {
|
||||
case API:
|
||||
count = quota.getApi();
|
||||
break;
|
||||
case LOAD:
|
||||
count = quota.getPerformance();
|
||||
break;
|
||||
case DURATION:
|
||||
count = quota.getDuration();
|
||||
break;
|
||||
case MAX_THREAD:
|
||||
count = quota.getMaxThreads();
|
||||
break;
|
||||
case MEMBER:
|
||||
count = quota.getMember();
|
||||
break;
|
||||
case PROJECT:
|
||||
count = quota.getProject();
|
||||
break;
|
||||
default:
|
||||
MSException.throwException("get quota count fail, don't have type: " + type);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 性能测试配额检查
|
||||
* @param request 压力配置
|
||||
* @param checkPerformance 是:检查创建数量配额 / 否:检查并发数和时间
|
||||
*/
|
||||
@Transactional(noRollbackFor = MSException.class, rollbackFor = Exception.class)
|
||||
public void checkLoadTestQuota(TestPlanRequest request, boolean checkPerformance) {
|
||||
String loadConfig = request.getLoadConfiguration();
|
||||
int threadNum = 0;
|
||||
long duration = 0;
|
||||
if (loadConfig != null) {
|
||||
threadNum = getIntegerValue(loadConfig, "TargetLevel");
|
||||
duration = getIntegerValue(loadConfig, "duration");
|
||||
}
|
||||
String projectId = request.getProjectId();
|
||||
if (checkPerformance) {
|
||||
this.checkPerformance(projectId);
|
||||
} else {
|
||||
checkMaxThread(projectId, threadNum);
|
||||
checkDuration(projectId, duration);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkPerformance(String projectId) {
|
||||
this.checkQuota(extQuotaMapper::countLoadTest, LOAD, projectId,
|
||||
Translator.get("quota_performance_excess_project"),
|
||||
Translator.get("quota_performance_excess_workspace"));
|
||||
}
|
||||
|
||||
private void checkMaxThread(String projectId, int threadNum) {
|
||||
// 增量为0的检查
|
||||
this.checkQuota(p -> (long) (threadNum - 1), MAX_THREAD, projectId,
|
||||
Translator.get("quota_max_threads_excess_project"),
|
||||
Translator.get("quota_max_threads_excess_workspace"));
|
||||
}
|
||||
|
||||
private void checkDuration(String projectId, long duration) {
|
||||
// 增量为0的检查
|
||||
this.checkQuota(p -> duration - 1, DURATION, projectId,
|
||||
Translator.get("quota_duration_excess_project"),
|
||||
Translator.get("quota_duration_excess_workspace"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取可用资源池集合
|
||||
* @return 资源池名称Set集合
|
||||
*/
|
||||
public Set<String> getQuotaResourcePools() {
|
||||
Set<String> pools = new HashSet<>();
|
||||
String projectId = SessionUtils.getCurrentProjectId();
|
||||
Quota pjQuota = quotaManagementService.getProjectQuota(projectId);
|
||||
if (pjQuota != null) {
|
||||
if (isValid(pjQuota, pjQuota.getResourcePool())) {
|
||||
pools.addAll(Arrays.asList(pjQuota.getResourcePool().split(",")));
|
||||
return pools;
|
||||
}
|
||||
}
|
||||
|
||||
String workspaceId = this.queryWorkspaceId(projectId);
|
||||
if (StringUtils.isBlank(workspaceId)) {
|
||||
return pools;
|
||||
}
|
||||
Quota wsQuota = quotaManagementService.getWorkspaceQuota(workspaceId);
|
||||
if (wsQuota != null) {
|
||||
if (isValid(wsQuota, wsQuota.getResourcePool())) {
|
||||
pools.addAll(Arrays.asList(wsQuota.getResourcePool().split(",")));
|
||||
}
|
||||
}
|
||||
return pools;
|
||||
}
|
||||
|
||||
/**
|
||||
* 工作空间下被限制使用的资源池
|
||||
* @param workspaceId 工作空间ID
|
||||
* @return 资源池名称Set
|
||||
*/
|
||||
public Set<String> getQuotaWsResourcePools(String workspaceId) {
|
||||
Set<String> pools = new HashSet<>();
|
||||
Quota wsQuota = quotaManagementService.getWorkspaceQuota(workspaceId);
|
||||
if (wsQuota != null) {
|
||||
if (isValid(wsQuota, wsQuota.getResourcePool())) {
|
||||
pools.addAll(Arrays.asList(wsQuota.getResourcePool().split(",")));
|
||||
}
|
||||
}
|
||||
return pools;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查工作空间项目数量配额
|
||||
* @param workspaceId 工作空间ID
|
||||
*/
|
||||
public void checkWorkspaceProject(String workspaceId) {
|
||||
this.doCheckQuota(quotaManagementService.getWorkspaceQuota(workspaceId),
|
||||
PROJECT, Translator.get("quota_project_excess_project"),
|
||||
extQuotaMapper.countWorkspaceProject(workspaceId) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查vumUsed配额
|
||||
* 未超过:返回本次执行预计消耗的配额
|
||||
* 超过:抛出异常
|
||||
* @param request 压力配置
|
||||
* @param projectId 性能测试所属项目ID
|
||||
* @return 本次执行预计消耗的配额
|
||||
*/
|
||||
@Transactional(noRollbackFor = MSException.class, rollbackFor = Exception.class)
|
||||
public BigDecimal checkVumUsed(TestPlanRequest request, String projectId) {
|
||||
BigDecimal toVumUsed = this.calcVum(request.getLoadConfiguration());
|
||||
if (toVumUsed.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return toVumUsed;
|
||||
}
|
||||
|
||||
Quota pjQuota = quotaManagementService.getProjectQuota(projectId);
|
||||
String pjWarningMsg = Translator.get("quota_vum_used_excess_project");
|
||||
boolean isContinue = this.doCheckVumUsed(pjQuota, toVumUsed, pjWarningMsg);
|
||||
|
||||
if (isContinue) {
|
||||
String workspaceId = this.queryWorkspaceId(projectId);
|
||||
if (StringUtils.isBlank(workspaceId)) {
|
||||
return toVumUsed;
|
||||
}
|
||||
String wsWarningMsg = Translator.get("quota_vum_used_excess_workspace");
|
||||
Quota wsQuota = quotaManagementService.getWorkspaceQuota(workspaceId);
|
||||
if (wsQuota == null) {
|
||||
return toVumUsed;
|
||||
}
|
||||
// 获取工作空间下已经消耗的vum
|
||||
Quota qt = extQuotaMapper.getProjectQuotaSum(workspaceId);
|
||||
if (qt == null || qt.getVumUsed() == null) {
|
||||
wsQuota.setVumUsed(BigDecimal.ZERO);
|
||||
} else {
|
||||
wsQuota.setVumUsed(qt.getVumUsed());
|
||||
}
|
||||
this.doCheckVumUsed(wsQuota, toVumUsed, wsWarningMsg);
|
||||
}
|
||||
|
||||
return toVumUsed;
|
||||
}
|
||||
|
||||
private BigDecimal calcVum(String loadConfig) {
|
||||
BigDecimal vum = BigDecimal.ZERO;
|
||||
try {
|
||||
List jsonArray = JSON.parseArray(loadConfig);
|
||||
this.filterDeleteAndEnabled(jsonArray);
|
||||
for (Object value : jsonArray) {
|
||||
if (value instanceof List) {
|
||||
List o = (List) value;
|
||||
int thread = 0;
|
||||
long duration = 0;
|
||||
for (Object item : o) {
|
||||
Map b = (Map) item;
|
||||
if (StringUtils.equals((String) b.get("key"), "TargetLevel")) {
|
||||
thread += (int) b.get("value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int j = 0; j < o.size(); j++) {
|
||||
Map b = (Map) o.get(j);
|
||||
if (StringUtils.equals((String) b.get("key"), "duration")) {
|
||||
duration += (int) b.get("value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// 每个ThreadGroup消耗的vum单独计算
|
||||
vum = vum.add(this.calcVum(thread, duration));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
return vum;
|
||||
}
|
||||
|
||||
private BigDecimal calcVum(int thread, long duration) {
|
||||
double used = thread * duration * 1.00 / 60;
|
||||
DecimalFormat df = new DecimalFormat("#.00000");
|
||||
return new BigDecimal(df.format(used));
|
||||
}
|
||||
|
||||
private boolean doCheckVumUsed(Quota quota, BigDecimal toVumUsed, String errorMsg) {
|
||||
if (quota == null) {
|
||||
return true;
|
||||
}
|
||||
// 如果vumTotal为NULL是不限制
|
||||
if (isValid(quota, quota.getVumTotal())) {
|
||||
BigDecimal vumTotal = quota.getVumTotal();
|
||||
BigDecimal used = quota.getVumUsed();
|
||||
if (used == null) {
|
||||
used = BigDecimal.ZERO;
|
||||
}
|
||||
if (used.add(toVumUsed).compareTo(vumTotal) > 0) {
|
||||
MSException.throwException(errorMsg);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查向某资源添加人员时是否超额
|
||||
* @param addMemberMap 资源ID:添加用户ID列表
|
||||
* @param type 检查类型 PROJECT/WORKSPACE
|
||||
*/
|
||||
public void checkMemberCount(Map<String, List<String>> addMemberMap, String type) {
|
||||
if (addMemberMap == null || addMemberMap.keySet().size() == 0) {
|
||||
return;
|
||||
}
|
||||
if (!StringUtils.equals(type, CHECK_PROJECT) && !StringUtils.equals(type, CHECK_WORKSPACE)) {
|
||||
return;
|
||||
}
|
||||
List<String> sourceIds = new ArrayList<>(addMemberMap.keySet());
|
||||
List<Quota> quotas = extQuotaMapper.listQuotaBySourceIds(sourceIds);
|
||||
|
||||
Quota defaultQuota = null;
|
||||
if (StringUtils.equals(CHECK_WORKSPACE, type)) {
|
||||
defaultQuota = quotaManagementService.getDefaultQuota(QuotaConstants.DefaultType.workspace);
|
||||
}
|
||||
|
||||
Map<String, Integer> quotaMap = new HashMap<>();
|
||||
for (Quota quota : quotas) {
|
||||
String key;
|
||||
if (StringUtils.equals(CHECK_PROJECT, type)) {
|
||||
key = quota.getProjectId();
|
||||
} else {
|
||||
key = quota.getWorkspaceId();
|
||||
}
|
||||
if (StringUtils.isBlank(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (BooleanUtils.isTrue(quota.getUseDefault())) {
|
||||
if (StringUtils.equals(CHECK_PROJECT, type)) {
|
||||
Project project = projectMapper.selectByPrimaryKey(key);
|
||||
if (project == null || StringUtils.isBlank(project.getWorkspaceId())) {
|
||||
continue;
|
||||
}
|
||||
defaultQuota = quotaManagementService.getProjectDefaultQuota(project.getWorkspaceId());
|
||||
}
|
||||
if (defaultQuota == null) {
|
||||
continue;
|
||||
}
|
||||
quota = defaultQuota;
|
||||
}
|
||||
|
||||
if (quota.getMember() == null || quota.getMember() == 0) {
|
||||
continue;
|
||||
}
|
||||
quotaMap.put(key, quota.getMember());
|
||||
}
|
||||
|
||||
Set<String> set = quotaMap.keySet();
|
||||
if (set.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<String, Integer> memberCountMap = this.getDBMemberCountMap(addMemberMap, type);
|
||||
Map<String, String> sourceNameMap = this.getNameMap(sourceIds, type);
|
||||
|
||||
this.doCheckMemberCount(quotaMap, memberCountMap, sourceNameMap, addMemberMap, type);
|
||||
}
|
||||
|
||||
private void doCheckMemberCount(Map<String, Integer> quotaMap, Map<String, Integer> memberCountMap,
|
||||
Map<String, String> sourceNameMap, Map<String, List<String>> addMemberMap, String checkType) {
|
||||
Set<String> set = quotaMap.keySet();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (String sourceId : set) {
|
||||
// 没有配额限制的跳过
|
||||
if (!addMemberMap.containsKey(sourceId)) {
|
||||
continue;
|
||||
}
|
||||
// 当前已存在人员数量
|
||||
Integer dbCount = memberCountMap.getOrDefault(sourceId, 0);
|
||||
int toAddCount = addMemberMap.get(sourceId) == null ? 0 : addMemberMap.get(sourceId).size();
|
||||
// 添加人员之后判断配额
|
||||
if (dbCount + toAddCount > quotaMap.get(sourceId)) {
|
||||
builder.append(sourceNameMap.get(sourceId));
|
||||
builder.append(" ");
|
||||
}
|
||||
}
|
||||
if (builder.length() > 0) {
|
||||
builder.append("超出成员数量配额");
|
||||
if (StringUtils.equals(CHECK_WORKSPACE, checkType)) {
|
||||
builder.append("(工作空间成员配额将其下所有项目成员也计算在内)");
|
||||
}
|
||||
MSException.throwException(builder.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Integer> getDBMemberCountMap(Map<String, List<String>> addMemberMap, String type) {
|
||||
List<String> sourceIds = new ArrayList<>(addMemberMap.keySet());
|
||||
Map<String, Integer> memberCountMap = new HashMap<>();
|
||||
if (StringUtils.equals(CHECK_WORKSPACE, type)) {
|
||||
// 检查工作空间配额时将其下所有项目成员也计算在其中
|
||||
ProjectExample projectExample = new ProjectExample();
|
||||
for (String sourceId : sourceIds) {
|
||||
projectExample.clear();
|
||||
projectExample.createCriteria().andWorkspaceIdEqualTo(sourceId);
|
||||
List<Project> projects = projectMapper.selectByExample(projectExample);
|
||||
List<String> ids = projects.stream().map(Project::getId).collect(Collectors.toList());
|
||||
ids.add(sourceId);
|
||||
List<String> memberIds = addMemberMap.getOrDefault(sourceId, new ArrayList<>());
|
||||
long count = extQuotaMapper.listUserByWorkspaceAndProjectIds(ids, memberIds);
|
||||
memberCountMap.put(sourceId, (int) count);
|
||||
}
|
||||
} else if (StringUtils.equals(CHECK_PROJECT, type)) {
|
||||
List<CountDto> list = extQuotaMapper.listUserBySourceIds(sourceIds);
|
||||
memberCountMap = list.stream().collect(Collectors.toMap(CountDto::getSourceId, CountDto::getCount));
|
||||
}
|
||||
return memberCountMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 更新VumUsed配额
|
||||
* @param projectId 项目ID
|
||||
* @param vumUsed 预计使用数量
|
||||
*/
|
||||
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
|
||||
public void updateVumUsed(String projectId, BigDecimal vumUsed) {
|
||||
if (vumUsed == null) {
|
||||
LogUtil.info("update vum count fail. vum count is null.");
|
||||
return;
|
||||
}
|
||||
Quota dbPjQuota = extQuotaMapper.getProjectQuota(projectId);
|
||||
Quota newPjQuota = this.newPjQuota(projectId, vumUsed);
|
||||
this.doUpdateVumUsed(dbPjQuota, newPjQuota);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取需要回退的vum数量
|
||||
* @param report 性能测试报告
|
||||
* @return 需要回退的vum数量
|
||||
*/
|
||||
@Transactional(propagation = Propagation.NESTED, rollbackFor = Exception.class)
|
||||
public BigDecimal getReduceVumUsed(LoadTestReportWithBLOBs report) {
|
||||
String reportId = report.getId();
|
||||
List<LoadTestReportResult> timeInfos = queryReportResult(reportId, ReportKeys.TimeInfo.toString());
|
||||
List<LoadTestReportResult> overviews = queryReportResult(reportId, ReportKeys.Overview.toString());
|
||||
|
||||
// 预计使用的数量
|
||||
String loadConfig = report.getLoadConfiguration();
|
||||
BigDecimal toUsed = calcVum(loadConfig);
|
||||
if (CollectionUtils.isEmpty(timeInfos) || CollectionUtils.isEmpty(overviews)) {
|
||||
LogUtil.error("reduce vum used error. load test report time info is null.");
|
||||
return toUsed;
|
||||
}
|
||||
|
||||
ReportTimeInfo timeInfo = parseReportTimeInfo(timeInfos.get(0));
|
||||
TestOverview overview = parseOverview(overviews.get(0));
|
||||
|
||||
long duration = timeInfo.getDuration();
|
||||
String maxUserStr = overview.getMaxUsers();
|
||||
int maxUsers = 0;
|
||||
try {
|
||||
maxUsers = Integer.parseInt(maxUserStr);
|
||||
} catch (Exception e) {
|
||||
//
|
||||
}
|
||||
|
||||
if (duration == 0 || maxUsers == 0) {
|
||||
return toUsed;
|
||||
}
|
||||
|
||||
// 已经使用的数量
|
||||
BigDecimal used = calcVum(maxUsers, duration);
|
||||
// 实际使用值比预计值大,不回退,否则回退差值
|
||||
return used.compareTo(toUsed) >= 0 ? BigDecimal.ZERO : toUsed.subtract(used);
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果有该项目配额,修改为使用工作空间下项目默认配额
|
||||
* 无该配额,新建配额并默认使用工作空间下项目默认配额
|
||||
* @return 操作后的配额
|
||||
*/
|
||||
public Quota projectUseDefaultQuota(String projectId) {
|
||||
Quota pjQuota = extQuotaMapper.getProjectQuota(projectId);
|
||||
if (pjQuota != null) {
|
||||
pjQuota.setUseDefault(true);
|
||||
quotaMapper.updateByPrimaryKeySelective(pjQuota);
|
||||
return pjQuota;
|
||||
} else {
|
||||
Quota quota = new Quota();
|
||||
quota.setId(UUID.randomUUID().toString());
|
||||
quota.setUseDefault(true);
|
||||
quota.setProjectId(projectId);
|
||||
quota.setWorkspaceId(null);
|
||||
quota.setUpdateTime(System.currentTimeMillis());
|
||||
quotaMapper.insert(quota);
|
||||
return quota;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果有该工作空间配额,修改为使用系统默认配额,
|
||||
* 无该配额,新建配额并默认使用系统配额
|
||||
* @param workspaceId 工作空间ID
|
||||
* @return 操作后的配额
|
||||
*/
|
||||
public Quota workspaceUseDefaultQuota(String workspaceId) {
|
||||
Quota wsQuota = extQuotaMapper.getWorkspaceQuota(workspaceId);
|
||||
if (wsQuota != null) {
|
||||
wsQuota.setUseDefault(true);
|
||||
quotaMapper.updateByPrimaryKeySelective(wsQuota);
|
||||
return wsQuota;
|
||||
} else {
|
||||
Quota quota = new Quota();
|
||||
quota.setId(UUID.randomUUID().toString());
|
||||
quota.setUseDefault(true);
|
||||
quota.setProjectId(null);
|
||||
quota.setWorkspaceId(workspaceId);
|
||||
quota.setUpdateTime(System.currentTimeMillis());
|
||||
quotaMapper.insert(quota);
|
||||
return quota;
|
||||
}
|
||||
}
|
||||
|
||||
// todo
|
||||
private List<LoadTestReportResult> queryReportResult(String id, String key) {
|
||||
return extLoadTestReportResultMapper.selectByIdAndKey(id, key);
|
||||
}
|
||||
|
||||
private ReportTimeInfo parseReportTimeInfo(LoadTestReportResult reportResult) {
|
||||
String content = reportResult.getReportValue();
|
||||
ReportTimeInfo timeInfo = new ReportTimeInfo();
|
||||
try {
|
||||
timeInfo = JSON.parseObject(content, ReportTimeInfo.class);
|
||||
} catch (Exception e) {
|
||||
// 兼容字符串和数字
|
||||
Map jsonObject = JSON.parseObject(content, Map.class);
|
||||
String startTime = (String) jsonObject.get("startTime");
|
||||
String endTime = (String) jsonObject.get("endTime");
|
||||
String duration = (String) jsonObject.get("duration");
|
||||
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
|
||||
try {
|
||||
timeInfo.setStartTime(df.parse(startTime).getTime());
|
||||
timeInfo.setEndTime(df.parse(endTime).getTime());
|
||||
timeInfo.setDuration(Long.parseLong(duration));
|
||||
} catch (Exception parseException) {
|
||||
LogUtil.error("reduce vum error. parse time info error. " + parseException.getMessage());
|
||||
}
|
||||
}
|
||||
return timeInfo;
|
||||
}
|
||||
|
||||
private TestOverview parseOverview(LoadTestReportResult reportResult) {
|
||||
String content = reportResult.getReportValue();
|
||||
TestOverview overview = new TestOverview();
|
||||
try {
|
||||
overview = JSON.parseObject(content, TestOverview.class);
|
||||
} catch (Exception e) {
|
||||
LogUtil.error("parse test overview error.");
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
return overview;
|
||||
}
|
||||
|
||||
private void doUpdateVumUsed(Quota dbQuota, Quota newQuota) {
|
||||
if (dbQuota == null || StringUtils.isBlank(dbQuota.getId())) {
|
||||
quotaMapper.insert(newQuota);
|
||||
} else {
|
||||
BigDecimal vumUsed = dbQuota.getVumUsed();
|
||||
if (vumUsed == null) {
|
||||
vumUsed = BigDecimal.ZERO;
|
||||
}
|
||||
if (newQuota == null || newQuota.getVumUsed() == null) {
|
||||
return;
|
||||
}
|
||||
BigDecimal toSetVum = vumUsed.add(newQuota.getVumUsed());
|
||||
if (toSetVum.compareTo(BigDecimal.ZERO) < 0) {
|
||||
LogUtil.info("update vum used warning. vum value: " + toSetVum);
|
||||
toSetVum = BigDecimal.ZERO;
|
||||
}
|
||||
LogUtil.info("update vum used add value: " + newQuota.getVumUsed());
|
||||
dbQuota.setVumUsed(toSetVum);
|
||||
quotaMapper.updateByPrimaryKeySelective(dbQuota);
|
||||
}
|
||||
}
|
||||
|
||||
private Quota newPjQuota(String projectId, BigDecimal vumUsed) {
|
||||
Quota quota = new Quota();
|
||||
quota.setId(UUID.randomUUID().toString());
|
||||
quota.setUpdateTime(System.currentTimeMillis());
|
||||
quota.setUseDefault(false);
|
||||
quota.setVumUsed(vumUsed);
|
||||
quota.setProjectId(projectId);
|
||||
quota.setWorkspaceId(null);
|
||||
return quota;
|
||||
}
|
||||
|
||||
private Map<String, String> getNameMap(List<String> sourceIds, String type) {
|
||||
Map<String, String> nameMap = new HashMap<>(16);
|
||||
if (CollectionUtils.isEmpty(sourceIds)) {
|
||||
return nameMap;
|
||||
}
|
||||
if (StringUtils.equals(CHECK_PROJECT, type)) {
|
||||
ProjectExample projectExample = new ProjectExample();
|
||||
projectExample.createCriteria().andIdIn(sourceIds);
|
||||
List<Project> projects = projectMapper.selectByExample(projectExample);
|
||||
nameMap = projects.stream().collect(Collectors.toMap(Project::getId, Project::getName));
|
||||
} else if (StringUtils.equals(CHECK_WORKSPACE, type)) {
|
||||
WorkspaceExample workspaceExample = new WorkspaceExample();
|
||||
workspaceExample.createCriteria().andIdIn(sourceIds);
|
||||
List<Workspace> workspaces = workspaceMapper.selectByExample(workspaceExample);
|
||||
nameMap = workspaces.stream().collect(Collectors.toMap(Workspace::getId, Workspace::getName));
|
||||
}
|
||||
return nameMap;
|
||||
}
|
||||
|
||||
private void filterDeleteAndEnabled(List jsonArray) {
|
||||
Iterator<Object> iterator = jsonArray.iterator();
|
||||
outer:
|
||||
while (iterator.hasNext()) {
|
||||
Object next = iterator.next();
|
||||
if (next instanceof List<?>) {
|
||||
List<?> o = (List<?>) next;
|
||||
for (Object o1 : o) {
|
||||
Map jsonObject = (Map) o1;
|
||||
if (StringUtils.equals((String) jsonObject.get("key"), "deleted")) {
|
||||
String value = (String) jsonObject.get("value");
|
||||
if (StringUtils.equals(value, "true")) {
|
||||
iterator.remove();
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Object o1 : o) {
|
||||
Map jsonObject = (Map) o1;
|
||||
if (StringUtils.equals((String) jsonObject.get("key"), "enabled")) {
|
||||
String value = (String) jsonObject.get("value");
|
||||
if (StringUtils.equals(value, "false")) {
|
||||
iterator.remove();
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int getIntegerValue(String loadConfiguration, String key) {
|
||||
int s = 0;
|
||||
try {
|
||||
List jsonArray = JSON.parseArray(loadConfiguration);
|
||||
this.filterDeleteAndEnabled(jsonArray);
|
||||
for (int i = 0; i < jsonArray.size(); i++) {
|
||||
if (jsonArray.get(i) instanceof List) {
|
||||
List o = (List) jsonArray.get(i);
|
||||
for (int j = 0; j < o.size(); j++) {
|
||||
Map b = (Map) o.get(j);
|
||||
if (StringUtils.equals((String) b.get("key"), key)) {
|
||||
s += (int) b.get("value");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LogUtil.error("get load configuration integer value error.");
|
||||
LogUtil.error(e.getMessage(), e);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,388 @@
|
|||
package io.metersphere.quota.service;
|
||||
|
||||
import io.metersphere.base.domain.Project;
|
||||
import io.metersphere.base.domain.Quota;
|
||||
import io.metersphere.base.domain.QuotaExample;
|
||||
import io.metersphere.base.mapper.ProjectMapper;
|
||||
import io.metersphere.base.mapper.QuotaMapper;
|
||||
import io.metersphere.base.mapper.ext.ExtQuotaMapper;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.utils.JSON;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.i18n.Translator;
|
||||
import io.metersphere.log.utils.ReflexObjectUtil;
|
||||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.system.SystemReference;
|
||||
import io.metersphere.quota.dto.QuotaConstants;
|
||||
import io.metersphere.quota.dto.QuotaResult;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* 工作空间默认配额(全局唯一):QuotaConstants.DefaultType.workspace
|
||||
* 工作空间下项目的默认配额(工作空间下唯一):QuotaConstants.prefix + workspace_id
|
||||
*/
|
||||
|
||||
@Service
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public class QuotaManagementService {
|
||||
|
||||
@Resource
|
||||
private QuotaMapper quotaMapper;
|
||||
@Resource
|
||||
private ExtQuotaMapper extQuotaMapper;
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
|
||||
public Quota getDefaultQuota(QuotaConstants.DefaultType type) {
|
||||
Quota quota = quotaMapper.selectByPrimaryKey(type.name());
|
||||
if (quota == null) {
|
||||
quota = new Quota();
|
||||
quota.setId(type.name());
|
||||
}
|
||||
return quota;
|
||||
}
|
||||
|
||||
public Quota getProjectDefaultQuota(String workspaceId) {
|
||||
if (StringUtils.isBlank(workspaceId)) {
|
||||
return new Quota();
|
||||
}
|
||||
Quota quota = quotaMapper.selectByPrimaryKey(QuotaConstants.prefix + workspaceId);
|
||||
if (quota == null) {
|
||||
quota = new Quota();
|
||||
quota.setId(QuotaConstants.prefix + workspaceId);
|
||||
}
|
||||
return quota;
|
||||
}
|
||||
|
||||
public void saveQuota(Quota quota) {
|
||||
if (!isDefaultQuota(quota)) {
|
||||
// 保存项目配额时,检查工作空间下所有项目配额总和是否大于工作空间配额
|
||||
if (StringUtils.isNotBlank(quota.getProjectId())) {
|
||||
// 使用默认配额
|
||||
if (BooleanUtils.isTrue(quota.getUseDefault())) {
|
||||
Project project = projectMapper.selectByPrimaryKey(quota.getProjectId());
|
||||
if (project == null || StringUtils.isBlank(project.getWorkspaceId())) {
|
||||
MSException.throwException("save project quota fail. project is null");
|
||||
}
|
||||
useDefaultQuota(quota, getProjectDefaultQuota(project.getWorkspaceId()));
|
||||
}
|
||||
vumCompare(quota.getVumTotal(), quota.getVumUsed());
|
||||
checkProjectQuota(quota);
|
||||
quota.setWorkspaceId(null);
|
||||
}
|
||||
|
||||
// 保存工作空间配额时,检查是否小于项目配额总和
|
||||
if (StringUtils.isBlank(quota.getProjectId()) && StringUtils.isNotBlank(quota.getWorkspaceId())) {
|
||||
// 使用默认配额
|
||||
if (BooleanUtils.isTrue(quota.getUseDefault())) {
|
||||
useDefaultQuota(quota, getDefaultQuota(QuotaConstants.DefaultType.workspace));
|
||||
}
|
||||
wsVumCompare(quota);
|
||||
checkWorkspaceQuota(quota);
|
||||
}
|
||||
} else {
|
||||
checkDefaultQuota(quota);
|
||||
}
|
||||
|
||||
quota.setUpdateTime(System.currentTimeMillis());
|
||||
if (StringUtils.isNotBlank(quota.getId())) {
|
||||
Quota qt = quotaMapper.selectByPrimaryKey(quota.getId());
|
||||
if (qt != null) {
|
||||
quota.setVumUsed(qt.getVumUsed());
|
||||
}
|
||||
}
|
||||
quotaMapper.deleteByPrimaryKey(quota.getId());
|
||||
if (StringUtils.isBlank(quota.getId())) {
|
||||
quota.setId(UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(quota.getWorkspaceId()) && StringUtils.isNotBlank(quota.getProjectId())) {
|
||||
LogUtil.error("save quota error, illegal parameter, workspace id and project id cannot exist at the same time");
|
||||
return;
|
||||
} else if (StringUtils.isNotBlank(quota.getWorkspaceId()) && StringUtils.isBlank(quota.getProjectId())) {
|
||||
QuotaExample quotaExample = new QuotaExample();
|
||||
quotaExample.createCriteria().andWorkspaceIdEqualTo(quota.getWorkspaceId())
|
||||
.andProjectIdIsNull();
|
||||
if (quotaMapper.countByExample(quotaExample) > 0) {
|
||||
LogUtil.error("save quota error, repeat insert workspace quota, id is: " + quota.getWorkspaceId());
|
||||
return;
|
||||
}
|
||||
} else if (StringUtils.isNotBlank(quota.getProjectId()) && StringUtils.isBlank(quota.getWorkspaceId())) {
|
||||
QuotaExample quotaExample = new QuotaExample();
|
||||
quotaExample.createCriteria().andProjectIdEqualTo(quota.getProjectId())
|
||||
.andWorkspaceIdIsNull();
|
||||
if (quotaMapper.countByExample(quotaExample) > 0) {
|
||||
LogUtil.error("save quota error, repeat insert project quota, id is: " + quota.getProjectId());
|
||||
return;
|
||||
}
|
||||
} else if (this.isDefaultQuota(quota)) {
|
||||
QuotaExample quotaExample = new QuotaExample();
|
||||
quotaExample.createCriteria().andIdEqualTo(quota.getId());
|
||||
if (quotaMapper.countByExample(quotaExample) > 0) {
|
||||
LogUtil.error("save quota error, repeat insert default quota, id is: " + quota.getId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
BigDecimal vumTotal = quota.getVumTotal();
|
||||
BigDecimal max = BigDecimal.valueOf(99999999.00);
|
||||
if (vumTotal.compareTo(max) > 0) {
|
||||
MSException.throwException("总vum数量不能超过99999999!");
|
||||
}
|
||||
quotaMapper.insert(quota);
|
||||
}
|
||||
|
||||
public void wsVumCompare(Quota quota) {
|
||||
if (quota == null) {
|
||||
return;
|
||||
}
|
||||
if (StringUtils.isNotBlank(quota.getWorkspaceId())) {
|
||||
// 工作空间消耗的vum数量
|
||||
Quota vumUsedSum = extQuotaMapper.getProjectQuotaSum(quota.getWorkspaceId());
|
||||
if (vumUsedSum != null && vumUsedSum.getVumUsed() != null) {
|
||||
vumCompare(quota.getVumTotal(), vumUsedSum.getVumUsed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void vumCompare(BigDecimal vumTotal, BigDecimal vumUsed) {
|
||||
if (isValid(vumTotal) && isValid(vumUsed)) {
|
||||
if (vumUsed.compareTo(vumTotal) > 0) {
|
||||
MSException.throwException(Translator.get("quota_vum_used_gt_vum_total"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<QuotaResult> listWorkspaceQuota(String name) {
|
||||
return extQuotaMapper.listWorkspaceQuota(name);
|
||||
}
|
||||
|
||||
public List<QuotaResult> listProjectQuota(String workspaceId, String name) {
|
||||
return extQuotaMapper.listProjectQuota(workspaceId, name);
|
||||
}
|
||||
|
||||
public void deleteQuota(String id) {
|
||||
Quota quota = quotaMapper.selectByPrimaryKey(id);
|
||||
// 保留vum使用数量
|
||||
Quota qt = new Quota();
|
||||
qt.setId(UUID.randomUUID().toString());
|
||||
qt.setVumUsed(quota.getVumUsed());
|
||||
qt.setWorkspaceId(quota.getWorkspaceId());
|
||||
qt.setProjectId(quota.getProjectId());
|
||||
qt.setUseDefault(false);
|
||||
qt.setUpdateTime(System.currentTimeMillis());
|
||||
quotaMapper.insert(qt);
|
||||
quotaMapper.deleteByPrimaryKey(id);
|
||||
}
|
||||
|
||||
public void checkDefaultQuota(Quota quota) {
|
||||
if (StringUtils.equals(quota.getId(), QuotaConstants.DefaultType.workspace.name())) {
|
||||
List<Quota> wsQuotas = extQuotaMapper.listUseDefaultWsQuota();
|
||||
for (Quota q : wsQuotas) {
|
||||
useDefaultQuota(q, quota);
|
||||
wsVumCompare(q);
|
||||
checkWorkspaceQuota(q);
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.startsWith(quota.getId(), QuotaConstants.prefix)) {
|
||||
String workspaceId = quota.getId().substring(QuotaConstants.prefix.length());
|
||||
Quota wsQuota = getWorkspaceQuota(workspaceId);
|
||||
checkQuota(quota, wsQuota);
|
||||
List<Quota> pjQuotas = extQuotaMapper.listUseDefaultProjectQuota(workspaceId);
|
||||
for (Quota q : pjQuotas) {
|
||||
useDefaultQuota(q, quota);
|
||||
vumCompare(q.getVumTotal(), q.getVumUsed());
|
||||
checkProjectQuota(q);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean isDefaultQuota(Quota quota) {
|
||||
return StringUtils.startsWith(quota.getId(), QuotaConstants.prefix)
|
||||
|| StringUtils.equals(quota.getId(), QuotaConstants.DefaultType.workspace.name());
|
||||
}
|
||||
|
||||
private void checkProjectQuota(Quota quota) {
|
||||
// 获取工作空间下所有项目配额总和(接口和性能测试数量)
|
||||
Quota sumQuota = getProjectSumQuota(quota.getWorkspaceId(), quota.getProjectId());
|
||||
if (quota.getApi() != null) {
|
||||
sumQuota.setApi(sumQuota.getApi() + quota.getApi());
|
||||
}
|
||||
if (quota.getPerformance() != null) {
|
||||
sumQuota.setPerformance(sumQuota.getPerformance() + quota.getPerformance());
|
||||
}
|
||||
if (quota.getVumTotal() != null) {
|
||||
sumQuota.setVumTotal(sumQuota.getVumTotal().add(quota.getVumTotal()));
|
||||
}
|
||||
sumQuota.setMaxThreads(quota.getMaxThreads());
|
||||
sumQuota.setDuration(quota.getDuration());
|
||||
sumQuota.setResourcePool(quota.getResourcePool());
|
||||
|
||||
// 工作空间配额
|
||||
Quota wsQuota = getWorkspaceQuota(quota.getWorkspaceId());
|
||||
|
||||
// 检查是否超过工作空间配额
|
||||
checkQuota(sumQuota, wsQuota);
|
||||
}
|
||||
|
||||
private void checkWorkspaceQuota(Quota quota) {
|
||||
// 获取工作空间下所有项目配额总和(接口和性能测试数量)
|
||||
Quota sumQuota = getProjectSumQuota(quota.getWorkspaceId(), null);
|
||||
sumQuota.setResourcePool(quota.getResourcePool());
|
||||
|
||||
// 检查是否超过工作空间配额
|
||||
checkQuota(sumQuota, quota);
|
||||
}
|
||||
|
||||
private void checkQuota(Quota sumQuota, Quota wsQuota) {
|
||||
if (wsQuota == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isValid(wsQuota.getApi()) && isValid(sumQuota.getApi())) {
|
||||
if (sumQuota.getApi() > wsQuota.getApi()) {
|
||||
MSException.throwException(Translator.get("quota_project_excess_ws_api"));
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid(wsQuota.getPerformance()) && isValid(sumQuota.getPerformance())) {
|
||||
if (sumQuota.getPerformance() > wsQuota.getPerformance()) {
|
||||
MSException.throwException(Translator.get("quota_project_excess_ws_performance"));
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid(wsQuota.getMaxThreads()) && isValid(sumQuota.getMaxThreads())) {
|
||||
if (sumQuota.getMaxThreads() > wsQuota.getMaxThreads()) {
|
||||
MSException.throwException(Translator.get("quota_project_excess_ws_max_threads"));
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid(wsQuota.getDuration()) && isValid(sumQuota.getDuration())) {
|
||||
if (sumQuota.getDuration() > wsQuota.getDuration()) {
|
||||
MSException.throwException(Translator.get("quota_project_excess_ws_max_duration"));
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid(wsQuota.getVumTotal()) && isValid(sumQuota.getVumTotal())) {
|
||||
if (sumQuota.getVumTotal().compareTo(wsQuota.getVumTotal()) > 0) {
|
||||
MSException.throwException(Translator.get("quota_project_excess_ws_vum_total"));
|
||||
}
|
||||
}
|
||||
|
||||
if (isValid(wsQuota.getResourcePool()) && isValid(sumQuota.getResourcePool())) {
|
||||
for (String id : sumQuota.getResourcePool().split(",")) {
|
||||
if (!wsQuota.getResourcePool().contains(id)) {
|
||||
MSException.throwException(Translator.get("quota_project_excess_ws_resource_pool"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Quota getProjectSumQuota(String workspaceId, String currentProjectId) {
|
||||
List<QuotaResult> quotaResults = listProjectQuota(workspaceId, null);
|
||||
Quota projectDefaultQuota = this.getProjectDefaultQuota(workspaceId);
|
||||
AtomicInteger api = new AtomicInteger();
|
||||
AtomicInteger performance = new AtomicInteger();
|
||||
AtomicReference<BigDecimal> vumTotal = new AtomicReference<>(new BigDecimal("0.00"));
|
||||
int thread = 0;
|
||||
int duration = 0;
|
||||
for (QuotaResult quotaResult : quotaResults) {
|
||||
if (BooleanUtils.isTrue(quotaResult.getUseDefault())) {
|
||||
useDefaultQuota(quotaResult, projectDefaultQuota);
|
||||
}
|
||||
// 不计算当前project
|
||||
if (StringUtils.isNotBlank(currentProjectId) && StringUtils.equals(currentProjectId, quotaResult.getProjectId())) {
|
||||
continue;
|
||||
}
|
||||
if (quotaResult.getApi() != null) {
|
||||
api.addAndGet(quotaResult.getApi());
|
||||
}
|
||||
if (quotaResult.getPerformance() != null) {
|
||||
performance.addAndGet(quotaResult.getPerformance());
|
||||
}
|
||||
if (quotaResult.getVumTotal() != null) {
|
||||
vumTotal.updateAndGet(v -> v.add(quotaResult.getVumTotal()));
|
||||
}
|
||||
if (quotaResult.getMaxThreads() != null) {
|
||||
if (quotaResult.getMaxThreads() > thread) {
|
||||
thread = quotaResult.getMaxThreads();
|
||||
}
|
||||
}
|
||||
if (quotaResult.getDuration() != null) {
|
||||
if (quotaResult.getDuration() > duration) {
|
||||
duration = quotaResult.getDuration();
|
||||
}
|
||||
}
|
||||
}
|
||||
Quota quota = new Quota();
|
||||
quota.setApi(api.get());
|
||||
quota.setPerformance(performance.get());
|
||||
quota.setVumTotal(vumTotal.get());
|
||||
quota.setMaxThreads(thread);
|
||||
quota.setDuration(duration);
|
||||
return quota;
|
||||
}
|
||||
|
||||
public boolean isValid(Integer value) {
|
||||
return value != null && value > 0;
|
||||
}
|
||||
|
||||
public boolean isValid(String value) {
|
||||
return StringUtils.isNotBlank(value);
|
||||
}
|
||||
|
||||
public boolean isValid(BigDecimal value) {
|
||||
return value != null && value.compareTo(BigDecimal.ZERO) != 0;
|
||||
}
|
||||
|
||||
private void useDefaultQuota(Quota quota, Quota defaultQuota) {
|
||||
quota.setApi(defaultQuota.getApi());
|
||||
quota.setPerformance(defaultQuota.getPerformance());
|
||||
quota.setMaxThreads(defaultQuota.getMaxThreads());
|
||||
quota.setDuration(defaultQuota.getDuration());
|
||||
quota.setResourcePool(defaultQuota.getResourcePool());
|
||||
quota.setVumTotal(defaultQuota.getVumTotal());
|
||||
}
|
||||
|
||||
public Quota getWorkspaceQuota(String workspaceId) {
|
||||
Quota quota = extQuotaMapper.getWorkspaceQuota(workspaceId);
|
||||
if (quota != null && BooleanUtils.isTrue(quota.getUseDefault())) {
|
||||
return getDefaultQuota(QuotaConstants.DefaultType.workspace);
|
||||
}
|
||||
return quota;
|
||||
}
|
||||
|
||||
public Quota getProjectQuota(String projectId) {
|
||||
Quota quota = extQuotaMapper.getProjectQuota(projectId);
|
||||
if (quota != null && BooleanUtils.isTrue(quota.getUseDefault())) {
|
||||
Project project = projectMapper.selectByPrimaryKey(projectId);
|
||||
if (project == null || StringUtils.isBlank(project.getWorkspaceId())) {
|
||||
MSException.throwException("project is null or workspace_id of project is null");
|
||||
}
|
||||
return getProjectDefaultQuota(project.getWorkspaceId());
|
||||
}
|
||||
return quota;
|
||||
}
|
||||
|
||||
public String getLogDetails(String id) {
|
||||
Quota pool = quotaMapper.selectByPrimaryKey(id);
|
||||
if (pool != null) {
|
||||
List<DetailColumn> columns = ReflexObjectUtil.getColumns(pool, SystemReference.quotaColumns);
|
||||
OperatingLogDetails details = new OperatingLogDetails(JSON.toJSONString(pool.getId()), null, "默认配额", null, columns);
|
||||
return JSON.toJSONString(details);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package io.metersphere.request;
|
||||
|
||||
import io.metersphere.base.domain.Schedule;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class TestPlanRequest {
|
||||
|
||||
private String id;
|
||||
|
||||
private String projectId;
|
||||
|
||||
private String name;
|
||||
|
||||
private String description;
|
||||
|
||||
private Long createTime;
|
||||
|
||||
private Long updateTime;
|
||||
|
||||
private String loadConfiguration;
|
||||
|
||||
private String advancedConfiguration;
|
||||
|
||||
private String runtimeConfiguration;
|
||||
|
||||
private Schedule schedule;
|
||||
|
||||
private String testResourcePoolId;
|
||||
|
||||
private String refId;
|
||||
|
||||
private String versionId;
|
||||
|
||||
private List<String> follows;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
package io.metersphere.xpack.quota.service;
|
||||
|
||||
|
||||
import io.metersphere.base.domain.LoadTestReportWithBLOBs;
|
||||
import io.metersphere.base.domain.Quota;
|
||||
import io.metersphere.request.TestPlanRequest;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author lyh
|
||||
*/
|
||||
public interface QuotaService {
|
||||
|
||||
/**
|
||||
* api配额检查
|
||||
*/
|
||||
void checkAPIDefinitionQuota(String projectId);
|
||||
|
||||
/**
|
||||
* api配额检查
|
||||
*/
|
||||
void checkAPIAutomationQuota(String projectId);
|
||||
|
||||
/**
|
||||
* 性能测试配额检查
|
||||
* @param request 压力配置
|
||||
* @param checkPerformance 是:检查创建数量配额 / 否:检查并发数和时间
|
||||
*/
|
||||
void checkLoadTestQuota(TestPlanRequest request, boolean checkPerformance);
|
||||
|
||||
/**
|
||||
* 检查资源池
|
||||
* @return 资源池名称
|
||||
*/
|
||||
Set<String> getQuotaResourcePools();
|
||||
|
||||
/**
|
||||
* 工作空间下被限制使用的资源池
|
||||
* @param workspaceId 工作空间ID
|
||||
* @return 资源池名称Set
|
||||
*/
|
||||
Set<String> getQuotaWsResourcePools(String workspaceId);
|
||||
|
||||
/**
|
||||
* 检查工作空间项目数量配额
|
||||
* @param workspaceId 工作空间ID
|
||||
*/
|
||||
void checkWorkspaceProject(String workspaceId);
|
||||
|
||||
/**
|
||||
* 检查vumUsed配额
|
||||
* 未超过:返回本次执行预计消耗的配额
|
||||
* 超过:抛出异常
|
||||
* @param request 压力配置
|
||||
* @param projectId 性能测试所属项目ID
|
||||
* @return 本次执行预计消耗的配额
|
||||
*/
|
||||
BigDecimal checkVumUsed(TestPlanRequest request, String projectId);
|
||||
|
||||
/**
|
||||
* 检查向某资源添加人员时是否超额
|
||||
* @param map 资源ID:添加用户ID列表
|
||||
* @param type 检查类型 PROJECT/WORKSPACE
|
||||
*/
|
||||
void checkMemberCount(Map<String, List<String>> map, String type);
|
||||
|
||||
/**
|
||||
* 更新VumUsed配额
|
||||
* @param projectId 项目ID
|
||||
* @param used 预计使用数量
|
||||
*/
|
||||
void updateVumUsed(String projectId, BigDecimal used);
|
||||
|
||||
/**
|
||||
* 回退因主动停止或者测试启动后中途执行异常扣除的VumUsed配额
|
||||
* @param report 性能测试报告
|
||||
* @return 预计回退数量
|
||||
*/
|
||||
BigDecimal getReduceVumUsed(LoadTestReportWithBLOBs report);
|
||||
|
||||
/**
|
||||
* 如果有该项目配额,修改为使用工作空间下项目默认配额
|
||||
* 无该配额,新建配额并默认使用工作空间下项目默认配额
|
||||
* @return 操作后的配额
|
||||
*/
|
||||
Quota projectUseDefaultQuota(String projectId);
|
||||
|
||||
/**
|
||||
* 如果有该工作空间配额,修改为使用系统默认配额,
|
||||
* 无该配额,新建配额并默认使用系统配额
|
||||
* @param workspaceId 工作空间ID
|
||||
* @return 操作后的配额
|
||||
*/
|
||||
Quota workspaceUseDefaultQuota(String workspaceId);
|
||||
|
||||
}
|
|
@ -5,10 +5,10 @@ import io.metersphere.commons.constants.ResourcePoolTypeEnum;
|
|||
import io.metersphere.commons.utils.CommonBeanFactory;
|
||||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.dto.TestResourcePoolDTO;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.resourcepool.QueryResourcePoolRequest;
|
||||
import io.metersphere.service.BaseTestResourcePoolService;
|
||||
import io.metersphere.service.NodeResourcePoolService;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import io.metersphere.xpack.resourcepool.engine.KubernetesResourcePoolService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
@ -29,17 +29,15 @@ public class ValidQuotaResourcePoolService {
|
|||
private NodeResourcePoolService nodeResourcePoolService;
|
||||
@Resource
|
||||
private TestResourcePoolMapper testResourcePoolMapper;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
public List<TestResourcePoolDTO> listValidQuotaResourcePools() {
|
||||
return filterQuota(listValidResourcePools());
|
||||
}
|
||||
|
||||
private List<TestResourcePoolDTO> filterQuota(List<TestResourcePoolDTO> list) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService == null) {
|
||||
return list;
|
||||
}
|
||||
Set<String> pools = quotaService.getQuotaResourcePools();
|
||||
Set<String> pools = baseQuotaService.getQuotaResourcePools();
|
||||
if (!pools.isEmpty()) {
|
||||
return list.stream().filter(pool -> pools.contains(pool.getId())).collect(Collectors.toList());
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ import io.metersphere.commons.utils.CommonBeanFactory;
|
|||
import io.metersphere.commons.utils.LogUtil;
|
||||
import io.metersphere.consumer.LoadTestFinishEvent;
|
||||
import io.metersphere.dto.VumProcessedStatus;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.redisson.api.RLock;
|
||||
import org.redisson.api.RedissonClient;
|
||||
|
@ -35,6 +35,8 @@ public class LoadTestVumEvent implements LoadTestFinishEvent {
|
|||
private RedissonClient redissonClient;
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
private void handleVum(LoadTestReport report) {
|
||||
if (report == null) {
|
||||
|
@ -63,19 +65,14 @@ public class LoadTestVumEvent implements LoadTestFinishEvent {
|
|||
MSException.throwException("project is null or workspace_id of project is null. project id: " + projectId);
|
||||
}
|
||||
RLock lock = redissonClient.getLock(project.getWorkspaceId());
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService != null) {
|
||||
try {
|
||||
lock.lock();
|
||||
BigDecimal toReduceVum = quotaService.getReduceVumUsed(testReport);
|
||||
if (toReduceVum.compareTo(BigDecimal.ZERO) != 0) {
|
||||
quotaService.updateVumUsed(projectId, toReduceVum.negate());
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
try {
|
||||
lock.lock();
|
||||
BigDecimal toReduceVum = baseQuotaService.getReduceVumUsed(testReport);
|
||||
if (toReduceVum.compareTo(BigDecimal.ZERO) != 0) {
|
||||
baseQuotaService.updateVumUsed(projectId, toReduceVum.negate());
|
||||
}
|
||||
} else {
|
||||
LogUtil.error("handle vum event get quota service bean is null. load test report id: " + report.getId());
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,11 +22,11 @@ import io.metersphere.log.vo.DetailColumn;
|
|||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.performance.PerformanceReference;
|
||||
import io.metersphere.metadata.service.FileMetadataService;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.DeleteReportRequest;
|
||||
import io.metersphere.request.OrderRequest;
|
||||
import io.metersphere.request.RenameReportRequest;
|
||||
import io.metersphere.request.ReportRequest;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.collections.MapUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -85,6 +85,8 @@ public class PerformanceReportService {
|
|||
private ExtTestPlanLoadCaseMapper extTestPlanLoadCaseMapper;
|
||||
@Resource
|
||||
private BaseEnvironmentService baseEnvironmentService;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
public List<ReportDTO> getRecentReportList(ReportRequest request) {
|
||||
List<OrderRequest> orders = new ArrayList<>();
|
||||
|
@ -191,21 +193,18 @@ public class PerformanceReportService {
|
|||
if (project == null || StringUtils.isBlank(project.getWorkspaceId())) {
|
||||
MSException.throwException("project is null or workspace_id of project is null. project id: " + projectId);
|
||||
}
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
RLock lock = redissonClient.getLock(project.getWorkspaceId());
|
||||
if (quotaService != null) {
|
||||
try {
|
||||
lock.lock();
|
||||
BigDecimal toReduceVum = quotaService.getReduceVumUsed(report);
|
||||
if (toReduceVum.compareTo(BigDecimal.ZERO) != 0) {
|
||||
quotaService.updateVumUsed(projectId, toReduceVum.negate());
|
||||
}
|
||||
engine.stop();
|
||||
loadTest.setStatus(PerformanceTestStatus.Saved.name());
|
||||
loadTestMapper.updateByPrimaryKeySelective(loadTest);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
try {
|
||||
lock.lock();
|
||||
BigDecimal toReduceVum = baseQuotaService.getReduceVumUsed(report);
|
||||
if (toReduceVum.compareTo(BigDecimal.ZERO) != 0) {
|
||||
baseQuotaService.updateVumUsed(projectId, toReduceVum.negate());
|
||||
}
|
||||
engine.stop();
|
||||
loadTest.setStatus(PerformanceTestStatus.Saved.name());
|
||||
loadTestMapper.updateByPrimaryKeySelective(loadTest);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@ import io.metersphere.log.vo.DetailColumn;
|
|||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.performance.PerformanceReference;
|
||||
import io.metersphere.metadata.service.FileMetadataService;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.*;
|
||||
import io.metersphere.task.dto.TaskRequestDTO;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.apache.commons.collections4.MapUtils;
|
||||
|
@ -92,6 +92,8 @@ public class PerformanceTestService {
|
|||
private TestPlanLoadCaseMapper testPlanLoadCaseMapper;
|
||||
@Resource
|
||||
private TestCaseTestMapper testCaseTestMapper;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
public List<LoadTestDTO> list(QueryTestPlanRequest request) {
|
||||
request.setOrders(ServiceUtils.getDefaultSortOrder(request.getOrders()));
|
||||
|
@ -509,30 +511,24 @@ public class PerformanceTestService {
|
|||
|
||||
private void checkLoadQuota(LoadTestReportWithBLOBs testReport, Engine engine) {
|
||||
RunTestPlanRequest checkRequest = new RunTestPlanRequest();
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
checkRequest.setLoadConfiguration(testReport.getLoadConfiguration());
|
||||
checkRequest.setProjectId(testReport.getProjectId());
|
||||
if (quotaService != null) {
|
||||
quotaService.checkLoadTestQuota(checkRequest, false);
|
||||
String projectId = testReport.getProjectId();
|
||||
Project project = projectMapper.selectByPrimaryKey(projectId);
|
||||
if (project == null || StringUtils.isBlank(project.getWorkspaceId())) {
|
||||
MSException.throwException("project is null or workspace_id of project is null. project id: " + projectId);
|
||||
}
|
||||
RLock lock = redissonClient.getLock(project.getWorkspaceId());
|
||||
try {
|
||||
lock.lock();
|
||||
BigDecimal toUsed = quotaService.checkVumUsed(checkRequest, projectId);
|
||||
engine.start();
|
||||
if (toUsed.compareTo(BigDecimal.ZERO) != 0) {
|
||||
quotaService.updateVumUsed(projectId, toUsed);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
} else {
|
||||
baseQuotaService.checkLoadTestQuota(checkRequest, false);
|
||||
String projectId = testReport.getProjectId();
|
||||
Project project = projectMapper.selectByPrimaryKey(projectId);
|
||||
if (project == null || StringUtils.isBlank(project.getWorkspaceId())) {
|
||||
MSException.throwException("project is null or workspace_id of project is null. project id: " + projectId);
|
||||
}
|
||||
RLock lock = redissonClient.getLock(project.getWorkspaceId());
|
||||
try {
|
||||
lock.lock();
|
||||
BigDecimal toUsed = baseQuotaService.checkVumUsed(checkRequest, projectId);
|
||||
engine.start();
|
||||
LogUtil.error("check load test quota fail, quotaService is null.");
|
||||
if (toUsed.compareTo(BigDecimal.ZERO) != 0) {
|
||||
baseQuotaService.updateVumUsed(projectId, toUsed);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -710,10 +706,8 @@ public class PerformanceTestService {
|
|||
}
|
||||
|
||||
private void checkQuota(TestPlanRequest request, boolean create) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService != null) {
|
||||
quotaService.checkLoadTestQuota(request, create);
|
||||
}
|
||||
baseQuotaService.checkLoadTestQuota(request, create);
|
||||
|
||||
}
|
||||
|
||||
public List<LoadTestDTO> getLoadTestListByIds(List<String> ids) {
|
||||
|
|
|
@ -20,10 +20,10 @@ import io.metersphere.log.utils.ReflexObjectUtil;
|
|||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.system.SystemReference;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.GroupRequest;
|
||||
import io.metersphere.request.group.EditGroupRequest;
|
||||
import io.metersphere.request.group.EditGroupUserRequest;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -69,6 +69,8 @@ public class GroupService {
|
|||
private MicroService microService;
|
||||
@Resource
|
||||
private BaseUserService baseUserService;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
private static final String GLOBAL = "global";
|
||||
|
||||
// 服务权限拼装顺序
|
||||
|
@ -526,13 +528,12 @@ public class GroupService {
|
|||
}
|
||||
|
||||
private void addNotSystemGroupUser(Group group, List<String> userIds, List<String> sourceIds) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
for (String userId : userIds) {
|
||||
User user = userMapper.selectByPrimaryKey(userId);
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
checkQuota(quotaService, group.getType(), sourceIds, Collections.singletonList(userId));
|
||||
checkQuota(group.getType(), sourceIds, Collections.singletonList(userId));
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
UserGroupMapper mapper = sqlSession.getMapper(UserGroupMapper.class);
|
||||
UserGroupExample userGroupExample = new UserGroupExample();
|
||||
|
@ -555,11 +556,9 @@ public class GroupService {
|
|||
}
|
||||
|
||||
|
||||
private void checkQuota(QuotaService quotaService, String type, List<String> sourceIds, List<String> userIds) {
|
||||
if (quotaService != null) {
|
||||
Map<String, List<String>> addMemberMap = sourceIds.stream().collect(Collectors.toMap( id -> id, id -> userIds));
|
||||
quotaService.checkMemberCount(addMemberMap, type);
|
||||
}
|
||||
private void checkQuota(String type, List<String> sourceIds, List<String> userIds) {
|
||||
Map<String, List<String>> addMemberMap = sourceIds.stream().collect(Collectors.toMap( id -> id, id -> userIds));
|
||||
baseQuotaService.checkMemberCount(addMemberMap, type);
|
||||
}
|
||||
|
||||
public void editGroupUser(EditGroupUserRequest request) {
|
||||
|
|
|
@ -20,10 +20,10 @@ import io.metersphere.log.utils.ReflexObjectUtil;
|
|||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.system.SystemReference;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.GroupRequest;
|
||||
import io.metersphere.request.group.EditGroupRequest;
|
||||
import io.metersphere.request.group.EditGroupUserRequest;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -66,6 +66,8 @@ public class GroupService {
|
|||
private StringRedisTemplate stringRedisTemplate;
|
||||
@Resource
|
||||
private UserMapper userMapper;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
private static final String GLOBAL = "global";
|
||||
private static final String PERSONAL_PREFIX = "PERSONAL";
|
||||
|
@ -511,13 +513,12 @@ public class GroupService {
|
|||
}
|
||||
|
||||
private void addNotSystemGroupUser(Group group, List<String> userIds, List<String> sourceIds) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
for (String userId : userIds) {
|
||||
User user = userMapper.selectByPrimaryKey(userId);
|
||||
if (user == null) {
|
||||
continue;
|
||||
}
|
||||
checkQuota(quotaService, group.getType(), sourceIds, Collections.singletonList(userId));
|
||||
checkQuota(group.getType(), sourceIds, Collections.singletonList(userId));
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
UserGroupMapper mapper = sqlSession.getMapper(UserGroupMapper.class);
|
||||
UserGroupExample userGroupExample = new UserGroupExample();
|
||||
|
@ -539,11 +540,9 @@ public class GroupService {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkQuota(QuotaService quotaService, String type, List<String> sourceIds, List<String> userIds) {
|
||||
if (quotaService != null) {
|
||||
Map<String, List<String>> addMemberMap = sourceIds.stream().collect(Collectors.toMap(id -> id, id -> userIds));
|
||||
quotaService.checkMemberCount(addMemberMap, type);
|
||||
}
|
||||
private void checkQuota(String type, List<String> sourceIds, List<String> userIds) {
|
||||
Map<String, List<String>> addMemberMap = sourceIds.stream().collect(Collectors.toMap(id -> id, id -> userIds));
|
||||
baseQuotaService.checkMemberCount(addMemberMap, type);
|
||||
}
|
||||
|
||||
public void editGroupUser(EditGroupUserRequest request) {
|
||||
|
|
|
@ -24,10 +24,10 @@ import io.metersphere.log.vo.DetailColumn;
|
|||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.system.SystemReference;
|
||||
import io.metersphere.metadata.service.FileMetadataService;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.AddProjectRequest;
|
||||
import io.metersphere.request.ProjectRequest;
|
||||
import io.metersphere.xpack.api.service.ProjectApplicationSyncService;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.kafka.core.KafkaTemplate;
|
||||
|
@ -71,6 +71,8 @@ public class SystemProjectService {
|
|||
private EnvironmentGroupProjectService environmentGroupProjectService;
|
||||
@Resource
|
||||
private BaseScheduleService baseScheduleService;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
public Project addProject(AddProjectRequest project) {
|
||||
if (StringUtils.isBlank(project.getName())) {
|
||||
|
@ -81,10 +83,7 @@ public class SystemProjectService {
|
|||
if (projectMapper.countByExample(example) > 0) {
|
||||
MSException.throwException(Translator.get("project_name_already_exists"));
|
||||
}
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService != null) {
|
||||
quotaService.checkWorkspaceProject(project.getWorkspaceId());
|
||||
}
|
||||
baseQuotaService.checkWorkspaceProject(project.getWorkspaceId());
|
||||
|
||||
if (project.getMockTcpPort() != null && project.getMockTcpPort() > 0) {
|
||||
this.checkMockTcpPort(project.getMockTcpPort());
|
||||
|
@ -123,9 +122,7 @@ public class SystemProjectService {
|
|||
// 设置默认的通知
|
||||
baseProjectMapper.setDefaultMessageTask(project.getId());
|
||||
|
||||
if (quotaService != null) {
|
||||
quotaService.projectUseDefaultQuota(pjId);
|
||||
}
|
||||
baseQuotaService.projectUseDefaultQuota(pjId);
|
||||
|
||||
// 创建默认版本
|
||||
addProjectVersion(project);
|
||||
|
|
|
@ -19,8 +19,8 @@ import io.metersphere.log.utils.ReflexObjectUtil;
|
|||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.system.SystemReference;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.resourcepool.QueryResourcePoolRequest;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import io.metersphere.xpack.resourcepool.engine.KubernetesResourcePoolService;
|
||||
import org.apache.commons.beanutils.BeanUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
|
@ -52,6 +52,8 @@ public class TestResourcePoolService {
|
|||
private BaseTaskMapper baseTaskMapper;
|
||||
@Resource
|
||||
private MicroService microService;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
public TestResourcePoolDTO addTestResourcePool(TestResourcePoolDTO testResourcePool) {
|
||||
checkTestResourcePool(testResourcePool);
|
||||
|
@ -229,12 +231,9 @@ public class TestResourcePoolService {
|
|||
}
|
||||
|
||||
private List<TestResourcePoolDTO> filterQuota(List<TestResourcePoolDTO> list) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService != null) {
|
||||
Set<String> pools = quotaService.getQuotaResourcePools();
|
||||
if (!pools.isEmpty()) {
|
||||
return list.stream().filter(pool -> pools.contains(pool.getId())).collect(Collectors.toList());
|
||||
}
|
||||
Set<String> pools = baseQuotaService.getQuotaResourcePools();
|
||||
if (!pools.isEmpty()) {
|
||||
return list.stream().filter(pool -> pools.contains(pool.getId())).collect(Collectors.toList());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
@ -285,13 +284,10 @@ public class TestResourcePoolService {
|
|||
}
|
||||
|
||||
public List<TestResourcePoolDTO> listWsValidQuotaResourcePools(String workspaceId) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
List<TestResourcePoolDTO> list = listValidResourcePools();
|
||||
if (quotaService != null) {
|
||||
Set<String> pools = quotaService.getQuotaWsResourcePools(workspaceId);
|
||||
if (!pools.isEmpty()) {
|
||||
return list.stream().filter(pool -> pools.contains(pool.getId())).collect(Collectors.toList());
|
||||
}
|
||||
Set<String> pools = baseQuotaService.getQuotaWsResourcePools(workspaceId);
|
||||
if (!pools.isEmpty()) {
|
||||
return list.stream().filter(pool -> pools.contains(pool.getId())).collect(Collectors.toList());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
|
|
@ -3,10 +3,8 @@ package io.metersphere.service;
|
|||
import com.alibaba.excel.EasyExcelFactory;
|
||||
import io.metersphere.base.domain.*;
|
||||
import io.metersphere.base.mapper.*;
|
||||
import io.metersphere.base.mapper.ext.BaseProjectMapper;
|
||||
import io.metersphere.base.mapper.ext.BaseUserGroupMapper;
|
||||
import io.metersphere.base.mapper.ext.BaseUserMapper;
|
||||
import io.metersphere.base.mapper.ext.BaseWorkspaceMapper;
|
||||
import io.metersphere.commons.constants.*;
|
||||
import io.metersphere.commons.exception.MSException;
|
||||
import io.metersphere.commons.user.SessionUser;
|
||||
|
@ -27,13 +25,13 @@ import io.metersphere.log.utils.ReflexObjectUtil;
|
|||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.system.SystemReference;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.UserRequest;
|
||||
import io.metersphere.request.WorkspaceRequest;
|
||||
import io.metersphere.request.member.AddMemberRequest;
|
||||
import io.metersphere.request.member.EditPassWordRequest;
|
||||
import io.metersphere.request.member.QueryMemberRequest;
|
||||
import io.metersphere.request.resourcepool.UserBatchProcessRequest;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.ibatis.session.ExecutorType;
|
||||
|
@ -81,6 +79,8 @@ public class UserService {
|
|||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
private BaseUserService baseUserService;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
public UserDTO insert(io.metersphere.request.member.UserRequest userRequest) {
|
||||
checkUserParam(userRequest);
|
||||
|
@ -116,8 +116,7 @@ public class UserService {
|
|||
} else {
|
||||
List<String> ids = (List<String>) map.get("ids");
|
||||
Group group = groupMapper.selectByPrimaryKey(groupId);
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
checkQuota(quotaService, group.getType(), ids, Collections.singletonList(userId));
|
||||
checkQuota(group.getType(), ids, Collections.singletonList(userId));
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
UserGroupMapper mapper = sqlSession.getMapper(UserGroupMapper.class);
|
||||
for (String id : ids) {
|
||||
|
@ -139,11 +138,9 @@ public class UserService {
|
|||
}
|
||||
}
|
||||
|
||||
private void checkQuota(QuotaService quotaService, String type, List<String> sourceIds, List<String> userIds) {
|
||||
if (quotaService != null) {
|
||||
Map<String, List<String>> addMemberMap = sourceIds.stream().collect(Collectors.toMap(id -> id, id -> userIds));
|
||||
quotaService.checkMemberCount(addMemberMap, type);
|
||||
}
|
||||
private void checkQuota(String type, List<String> sourceIds, List<String> userIds) {
|
||||
Map<String, List<String>> addMemberMap = sourceIds.stream().collect(Collectors.toMap(id -> id, id -> userIds));
|
||||
baseQuotaService.checkMemberCount(addMemberMap, type);
|
||||
}
|
||||
private void checkUserParam(User user) {
|
||||
|
||||
|
@ -351,8 +348,7 @@ public class UserService {
|
|||
return;
|
||||
}
|
||||
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
checkQuota(quotaService, "WORKSPACE", Collections.singletonList(request.getWorkspaceId()), request.getUserIds());
|
||||
checkQuota("WORKSPACE", Collections.singletonList(request.getWorkspaceId()), request.getUserIds());
|
||||
|
||||
List<String> allUserIds = baseUserService.getAllUserIds();
|
||||
|
||||
|
@ -606,8 +602,7 @@ public class UserService {
|
|||
|
||||
List<String> worksapceIds = request.getBatchProcessValue();
|
||||
if (CollectionUtils.isNotEmpty(userIds)) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
checkQuota(quotaService, "WORKSPACE", worksapceIds, userIds);
|
||||
checkQuota("WORKSPACE", worksapceIds, userIds);
|
||||
}
|
||||
for (String userId : userIds) {
|
||||
UserGroupExample userGroupExample = new UserGroupExample();
|
||||
|
@ -685,8 +680,7 @@ public class UserService {
|
|||
List<String> sourceIds = userGroups.stream().map(UserGroup::getSourceId).collect(Collectors.toList());
|
||||
List<String> list = sourceMap.get(group);
|
||||
list.removeAll(sourceIds);
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
checkQuota(quotaService, gp.getType(), list, Collections.singletonList(userId));
|
||||
checkQuota(gp.getType(), list, Collections.singletonList(userId));
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
UserGroupMapper mapper = sqlSession.getMapper(UserGroupMapper.class);
|
||||
for (String sourceId : list) {
|
||||
|
@ -730,8 +724,7 @@ public class UserService {
|
|||
|
||||
List<String> projectIds = request.getBatchProcessValue();
|
||||
if (CollectionUtils.isNotEmpty(userIds)) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
checkQuota(quotaService, "PROJECT", projectIds, userIds);
|
||||
checkQuota("PROJECT", projectIds, userIds);
|
||||
}
|
||||
for (String userId : userIds) {
|
||||
UserGroupExample userGroupExample = new UserGroupExample();
|
||||
|
@ -1006,8 +999,7 @@ public class UserService {
|
|||
} else {
|
||||
List<String> ids = (List<String>) map.get("ids");
|
||||
Group group = groupMapper.selectByPrimaryKey(groupId);
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
checkQuota(quotaService, group.getType(), ids, Collections.singletonList(userId));
|
||||
checkQuota(group.getType(), ids, Collections.singletonList(userId));
|
||||
SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
|
||||
UserGroupMapper mapper = sqlSession.getMapper(UserGroupMapper.class);
|
||||
for (String id : ids) {
|
||||
|
@ -1086,9 +1078,8 @@ public class UserService {
|
|||
* @param userIds 添加的用户id
|
||||
*/
|
||||
private void checkQuotaOfMemberSize(String type, String sourceId, List<String> userIds) {
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (CollectionUtils.isNotEmpty(userIds)) {
|
||||
checkQuota(quotaService, type, Collections.singletonList(sourceId), userIds);
|
||||
checkQuota(type, Collections.singletonList(sourceId), userIds);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ import io.metersphere.log.utils.ReflexObjectUtil;
|
|||
import io.metersphere.log.vo.DetailColumn;
|
||||
import io.metersphere.log.vo.OperatingLogDetails;
|
||||
import io.metersphere.log.vo.system.SystemReference;
|
||||
import io.metersphere.quota.service.BaseQuotaService;
|
||||
import io.metersphere.request.WorkspaceRequest;
|
||||
import io.metersphere.xpack.quota.service.QuotaService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -57,6 +57,8 @@ public class WorkspaceService {
|
|||
private EnvironmentGroupService environmentGroupService;
|
||||
@Resource
|
||||
private BaseScheduleService baseScheduleService;
|
||||
@Resource
|
||||
private BaseQuotaService baseQuotaService;
|
||||
|
||||
private static final String GLOBAL = "global";
|
||||
|
||||
|
@ -150,10 +152,7 @@ public class WorkspaceService {
|
|||
workspace.setCreateUser(SessionUtils.getUserId());
|
||||
workspaceMapper.insertSelective(workspace);
|
||||
|
||||
QuotaService quotaService = CommonBeanFactory.getBean(QuotaService.class);
|
||||
if (quotaService != null) {
|
||||
quotaService.workspaceUseDefaultQuota(wsId);
|
||||
}
|
||||
baseQuotaService.workspaceUseDefaultQuota(wsId);
|
||||
|
||||
// 创建工作空间为当前用户添加用户组
|
||||
UserGroup userGroup = new UserGroup();
|
||||
|
|
|
@ -108,14 +108,12 @@
|
|||
{
|
||||
"id": "SYSTEM_QUOTA:READ",
|
||||
"name": "permission.system_quota.read",
|
||||
"resourceId": "SYSTEM_QUOTA",
|
||||
"license": true
|
||||
"resourceId": "SYSTEM_QUOTA"
|
||||
},
|
||||
{
|
||||
"id": "SYSTEM_QUOTA:READ+EDIT",
|
||||
"name": "permission.system_quota.edit",
|
||||
"resourceId": "SYSTEM_QUOTA",
|
||||
"license": true
|
||||
"resourceId": "SYSTEM_QUOTA"
|
||||
},
|
||||
{
|
||||
"id": "SYSTEM_AUTH:READ",
|
||||
|
@ -260,14 +258,12 @@
|
|||
{
|
||||
"id": "WORKSPACE_QUOTA:READ",
|
||||
"name": "permission.workspace_quota.read",
|
||||
"resourceId": "WORKSPACE_QUOTA",
|
||||
"license": true
|
||||
"resourceId": "WORKSPACE_QUOTA"
|
||||
},
|
||||
{
|
||||
"id": "WORKSPACE_QUOTA:READ+EDIT",
|
||||
"name": "permission.workspace_quota.edit",
|
||||
"resourceId": "WORKSPACE_QUOTA",
|
||||
"license": true
|
||||
"resourceId": "WORKSPACE_QUOTA"
|
||||
},
|
||||
{
|
||||
"id": "WORKSPACE_OPERATING_LOG:READ",
|
||||
|
@ -339,8 +335,7 @@
|
|||
},
|
||||
{
|
||||
"id": "SYSTEM_QUOTA",
|
||||
"name": "permission.system_quota.name",
|
||||
"license": true
|
||||
"name": "permission.system_quota.name"
|
||||
},
|
||||
{
|
||||
"id": "SYSTEM_AUTH",
|
||||
|
@ -372,8 +367,7 @@
|
|||
},
|
||||
{
|
||||
"id": "WORKSPACE_QUOTA",
|
||||
"name": "permission.workspace_quota.name",
|
||||
"license": true
|
||||
"name": "permission.workspace_quota.name"
|
||||
},
|
||||
{
|
||||
"id": "WORKSPACE_OPERATING_LOG",
|
||||
|
|
|
@ -70,13 +70,13 @@ const Setting = {
|
|||
path: 'project/quota',
|
||||
component: () => import('../../business/workspace/quota/MxProjectQuota'),
|
||||
meta: {
|
||||
workspace: true, valid: true, xpack: true, title: 'commons.quota', permissions: ['WORKSPACE_QUOTA:READ'],
|
||||
workspace: true, valid: true, title: 'commons.quota', permissions: ['WORKSPACE_QUOTA:READ'],
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'workspace/quota',
|
||||
component: () => import('../../business/system/quota/MxWorkspaceQuota'),
|
||||
meta: {system: true, valid: true, xpack: true, title: 'commons.quota', permissions: ['SYSTEM_QUOTA:READ']}
|
||||
meta: {system: true, valid: true, title: 'commons.quota', permissions: ['SYSTEM_QUOTA:READ']}
|
||||
},
|
||||
{
|
||||
path: 'license',
|
||||
|
|
Loading…
Reference in New Issue