Skip to content

Commit

Permalink
Merge branch 'TencentBlueKing:master' into issue_168
Browse files Browse the repository at this point in the history
  • Loading branch information
eazence authored Mar 15, 2024
2 parents 20f8751 + e15a650 commit e13d285
Show file tree
Hide file tree
Showing 15 changed files with 358 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.tencent.devops.turbo.api

import com.tencent.devops.api.pojo.Response
import com.tencent.devops.common.api.annotation.ServiceInterface
import com.tencent.devops.common.api.pojo.Page
import com.tencent.devops.turbo.vo.ProjectResourceUsageVO
import io.swagger.annotations.Api
import io.swagger.annotations.ApiOperation
import io.swagger.annotations.ApiParam
import org.springframework.cloud.openfeign.FeignClient
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam

@Api(tags = ["SERVER_RESOURCES"], description = "资源统计查询接口")
@RequestMapping("/service/resources")
@FeignClient(name = "turbo", contextId = "IServiceResourceStatController")
@ServiceInterface("turbo-new")
interface IServiceResourceStatController {

@ApiOperation("获取项目使用服务器资源统计")
@GetMapping("/getSummary", produces = [MediaType.APPLICATION_JSON_VALUE])
fun getSummary(
@ApiParam("起始统计日期")
@RequestParam("startDate")
startDate: String?,
@ApiParam("截止统计日期")
@RequestParam("endDate")
endDate: String?,
@ApiParam(value = "页数")
@RequestParam(value = "pageNum")
pageNum: Int?,
@ApiParam(value = "每页多少条")
@RequestParam("pageSize")
pageSize: Int?,
): Response<Page<ProjectResourceUsageVO>>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package com.tencent.devops.turbo.api
import com.tencent.devops.api.pojo.Response
import com.tencent.devops.common.api.annotation.ServiceInterface
import com.tencent.devops.common.api.pojo.Page
import com.tencent.devops.turbo.pojo.TurboPlanModel
import com.tencent.devops.turbo.pojo.TurboRecordModel
import com.tencent.devops.turbo.validate.TurboPlanGroup
import com.tencent.devops.turbo.validate.TurboRecordGroup
import com.tencent.devops.turbo.vo.TurboPlanDetailVO
import com.tencent.devops.turbo.vo.TurboPlanStatRowVO
Expand Down Expand Up @@ -107,5 +109,24 @@ interface IServiceTurboController {
@ApiParam(value = "用户信息", required = true)
@PathVariable("userId")
userId: String
): Response<TurboPlanDetailVO>
): Response<TurboPlanDetailVO>

@ApiOperation("新增加速方案")
@PostMapping(
"/projectId/{projectId}/{userId}/addTurboPlan",
consumes = [MediaType.APPLICATION_JSON_VALUE],
produces = [MediaType.APPLICATION_JSON_VALUE]
)
fun addNewTurboPlan(
@ApiParam(value = "蓝盾项目id", required = true)
@PathVariable("projectId")
projectId: String,
@ApiParam(value = "用户信息", required = true)
@PathVariable("userId")
userId: String,
@ApiParam(value = "新增加速方案请求数据信息", required = true)
@RequestBody
@Validated(TurboPlanGroup.Create::class)
turboPlanModel: TurboPlanModel
): Response<String?>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.tencent.devops.turbo.vo

import io.swagger.annotations.ApiModel
import io.swagger.annotations.ApiModelProperty

@ApiModel("机器资源使用统计")
data class ProjectResourceUsageVO(

@ApiModelProperty("项目id")
val projectId: String,

@ApiModelProperty("项目名称")
val projectName: String?,

@ApiModelProperty("加速模式")
var engineCode: String?,

@ApiModelProperty("加速时长*cpu核数(单位分钟*核)")
var totalTimeWithCpu: Double?,

@ApiModelProperty("项目所属运营产品id")
var productId: Int?,

@ApiModelProperty("BG名称")
var bgName: String?,

@ApiModelProperty("BG id")
var bgId: Int?,

@ApiModelProperty("部门名称")
var deptName: String?,

@ApiModelProperty("部门id")
var deptId: Int?,

@ApiModelProperty("中心名称")
var centerName: String?,

@ApiModelProperty("中心id")
var centerId: Int?,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.tencent.devops.turbo.controller

import com.tencent.devops.api.pojo.Response
import com.tencent.devops.common.api.pojo.Page
import com.tencent.devops.turbo.api.IServiceResourceStatController
import com.tencent.devops.turbo.service.ProjectResourcesService
import com.tencent.devops.turbo.vo.ProjectResourceUsageVO
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.RestController

@RestController
class ServiceResourceStatController @Autowired constructor(
private val projectResourcesService: ProjectResourcesService
) : IServiceResourceStatController {

override fun getSummary(
startDate: String?,
endDate: String?,
pageNum: Int?,
pageSize: Int?
): Response<Page<ProjectResourceUsageVO>> {
return Response.success(projectResourcesService.querySummary(startDate, endDate, pageNum, pageSize))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.tencent.devops.common.api.exception.code.IS_NOT_ADMIN_MEMBER
import com.tencent.devops.common.api.pojo.Page
import com.tencent.devops.common.util.constants.NO_ADMIN_MEMBER_MESSAGE
import com.tencent.devops.turbo.api.IServiceTurboController
import com.tencent.devops.turbo.pojo.TurboPlanModel
import com.tencent.devops.turbo.pojo.TurboRecordModel
import com.tencent.devops.turbo.service.TurboAuthService
import com.tencent.devops.turbo.service.TurboPlanService
Expand Down Expand Up @@ -68,4 +69,12 @@ class ServiceTurboController @Autowired constructor(
}
return Response.success(turboPlanService.getTurboPlanDetailByPlanId(planId))
}

override fun addNewTurboPlan(projectId: String, userId: String, turboPlanModel: TurboPlanModel): Response<String?> {
// 判断是否是管理员
if (!turboAuthService.getAuthResult(projectId, userId)) {
throw TurboException(errorCode = IS_NOT_ADMIN_MEMBER, errorMessage = NO_ADMIN_MEMBER_MESSAGE)
}
return Response.success(turboPlanService.addNewTurboPlan(turboPlanModel, userId))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.tencent.devops.turbo.dao.mongotemplate

import com.tencent.devops.turbo.model.TTbsDaySummaryEntity
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.data.domain.Sort
import org.springframework.data.mongodb.core.MongoTemplate
import org.springframework.data.mongodb.core.aggregation.Aggregation
import org.springframework.data.mongodb.core.query.Criteria
import org.springframework.stereotype.Repository

@Repository
class TbsDaySummaryDao @Autowired constructor(
private val mongoTemplate: MongoTemplate
) {
companion object {
private val logger = LoggerFactory.getLogger(this::class.java)
private const val COLLECTION_NAME = "t_tbs_day_summary_entity"
}

/**
* 根据日期查询机器资源统计
*/
fun findByDay(
startDate: String,
endDate: String,
filterPlanIdNin: Set<String>,
filterProjectIdNin: Set<String>,
pageNum: Int,
pageSize: Int
): List<TTbsDaySummaryEntity> {
logger.info("findByDay startDate: $startDate, endDate: $endDate, filterPlanIdNin: $filterPlanIdNin")

val criteria = Criteria.where("day").gte(startDate).lte(endDate)
.and("user").`is`(null)

// 过滤方案id
filterPlanIdNin.takeIf { it.isNotEmpty() }.let { criteria.and("plan_id").nin(filterPlanIdNin) }
// 过滤项目id
filterProjectIdNin.takeIf { it.isNotEmpty() }.let { criteria.and("project_id").nin(filterPlanIdNin) }

val match = Aggregation.match(criteria)
val sort = Aggregation.sort(Sort.Direction.DESC, "day")
val group = Aggregation.group("project_id", "engine_code")
.sum("total_time_with_cpu").`as`("total_time_with_cpu")
.first("project_id").`as`("project_id")
.first("project_name").`as`("project_name")
.first("engine_code").`as`("engine_code")
.first("product_id").`as`("product_id")
.first("bg_name").`as`("bg_name")
.first("dept_name").`as`("dept_name")
.first("center_name").`as`("center_name")
.first("bg_id").`as`("bg_id")
.first("dept_id").`as`("dept_id")
.first("center_id").`as`("center_id")

val skip = Aggregation.skip((pageNum * pageSize).toLong())
val limit = Aggregation.limit(pageSize.toLong())

val aggregation = Aggregation.newAggregation(match, sort, group, skip, limit)
return mongoTemplate.aggregate(aggregation, COLLECTION_NAME, TTbsDaySummaryEntity::class.java).mappedResults
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.tencent.devops.turbo.dao.repository

import com.tencent.devops.turbo.model.BaseDataEntity
import org.springframework.data.mongodb.repository.MongoRepository
import org.springframework.stereotype.Repository

@Repository
interface BaseDataRepository : MongoRepository<BaseDataEntity, String> {

/**
* 根据参数标识代码查询
*/
fun findFirstByParamCode(paramCode: String): BaseDataEntity?
}
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ class TBSDaySummaryJob @Autowired constructor(
day = summary.day,
engineCode = engineCode,
planId = planId,
// user字段没有值即为disttask的统计数据,有值的是ue的用户数据
user = if (engineCode == "disttask-ue4") summary.user else null,
totalTime = summary.totalTime,
totalTimeWithCpu = summary.totalTimeWithCpu,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,8 @@ object TBSSdkApi {
engineCode = "disttask",
resourceName = "summary",
queryParam = queryParam,
// disttask的统计数据涵盖cc和ue4,因此disttask+distcc就是全量统计
// 该groupbyuser接口的统计信息不包含cc,只是为了方便查看ue的用户维度数据,是disttask的子集
customPath = if (engineCode.contains("ue4"))"/groupbyuser/scene/ue4" else null
)
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.tencent.devops.turbo.service

import com.tencent.devops.common.api.pojo.Page
import com.tencent.devops.common.util.MathUtil
import com.tencent.devops.common.util.constants.BASE_EXCLUDED_PLAN_ID_LIST
import com.tencent.devops.common.util.constants.BASE_EXCLUDED_PROJECT_ID_LIST
import com.tencent.devops.turbo.dao.mongotemplate.TbsDaySummaryDao
import com.tencent.devops.turbo.dao.repository.BaseDataRepository
import com.tencent.devops.turbo.vo.ProjectResourceUsageVO
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service
import java.time.LocalDate

@Service
class ProjectResourcesService @Autowired constructor(
private val tbsDaySummaryDao: TbsDaySummaryDao,
private val baseDataRepository: BaseDataRepository
) {

companion object {
private val logger = LoggerFactory.getLogger(this::class.java)
}

/**
* 不传日期时默认统计上个月所有天数的数据
*/
fun querySummary(
startDate: String?,
endDate: String?,
pageNum: Int?,
pageSize: Int?
): Page<ProjectResourceUsageVO> {
val page = pageNum?.takeIf { it > 0 }?.let { it - 1 } ?: 0
val pageSizeNum = pageSize?.coerceAtMost(10000) ?: 100

// 获取需要过滤掉的方案id集合
val baseDataEntity = baseDataRepository.findFirstByParamCode(BASE_EXCLUDED_PLAN_ID_LIST)
val filterPlanIds = baseDataEntity?.paramValue?.split(",")?.toSet() ?: emptySet()

// 获取需要过滤掉的项目id集合
val projectExcludedEntity = baseDataRepository.findFirstByParamCode(BASE_EXCLUDED_PROJECT_ID_LIST)
val filterProjectIds = projectExcludedEntity?.paramValue?.split(",")?.toSet() ?: emptySet()

val today = LocalDate.now()

val summaryEntityList = tbsDaySummaryDao.findByDay(
startDate = startDate ?: today.minusMonths(1).withDayOfMonth(1).toString(),
endDate = endDate ?: today.withDayOfMonth(1).minusDays(1).toString(),
filterPlanIdNin = filterPlanIds,
filterProjectIdNin = filterProjectIds,
pageNum = page,
pageSize = pageSizeNum
)
logger.info("summaryEntityList size: ${summaryEntityList.size}")

val resultList = summaryEntityList.map {
with(it) {
ProjectResourceUsageVO(
projectId = projectId!!,
projectName = projectName,
engineCode = engineCode,
// 秒转分钟
totalTimeWithCpu = MathUtil.secondsToMinutes(totalTimeWithCpu!!).toDouble(),
productId = productId,
bgName = bgName,
bgId = bgId,
deptName = deptName,
deptId = deptId,
centerName = centerName,
centerId = centerId
)
}
}
return Page(page + 1, pageSizeNum, 0, resultList)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ const val TURBO_NO_DATA_FOUND = "2121003" // 没有查询到对应数据
const val TURBO_PARAM_INVALID = "2121004" // 参数异常
const val IS_NOT_ADMIN_MEMBER = "2300017" // 非管理员,操作失败
const val SUB_CLASS_CHECK_ERROR = "2121006" // 子类检查错误
const val PERMISSION_DENIED = "2121007" // {0}无权限
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
package com.tencent.devops.common.util

import java.text.DecimalFormat

object MathUtil {

fun roundToTwoDigits(input: Double): String {
return String.format("%.2f", input)
}

/**
* 秒转分钟
*/
fun secondsToMinutes(seconds: Double): String {
val minutes = seconds.toDouble() / 60
val df = DecimalFormat("#.##")
return df.format(minutes)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,13 @@ const val SYSTEM_ADMIN = "Turbo"
* 无权限错误提示信息
*/
const val NO_ADMIN_MEMBER_MESSAGE = "无权限,请先加入项目!"

/**
* 统计数据需排除的plan id
*/
const val BASE_EXCLUDED_PLAN_ID_LIST = "EXCLUDED_PLAN_ID_LIST"

/**
* 统计数据需排除的project id
*/
const val BASE_EXCLUDED_PROJECT_ID_LIST = "EXCLUDED_PROJECT_ID_LIST"
Loading

0 comments on commit e13d285

Please sign in to comment.