diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/config/mybatis/ActableConfig.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/config/mybatis/ActableConfig.java index d18e4650..cf6e499f 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/config/mybatis/ActableConfig.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/config/mybatis/ActableConfig.java @@ -19,19 +19,13 @@ import com.gitee.sunchenbin.mybatis.actable.manager.handler.StartUpHandler; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; @Configuration @Order(1) public class ActableConfig { - - @Autowired - private StartUpHandler startUpHandler; - - @Bean - public void generate() { + public ActableConfig(@Autowired StartUpHandler startUpHandler) { startUpHandler.startHandler(); } } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/config/mybatis/RobotConfigMigrate.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/config/mybatis/RobotConfigMigrate.java new file mode 100644 index 00000000..a183ce74 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/config/mybatis/RobotConfigMigrate.java @@ -0,0 +1,52 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.config.mybatis; + +import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.mapper.AlertRobotsMigrateMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +/** + * @deprecated 保留此类一段时间,在ActableConfig完成之后执行, + * 如果发现旧的robot_token配置数据,则自动迁移到新的表结构中,并清空robot_token, + * 表结构保留原字段需要暂时保留以免ActableConfig后数据丢失, + * 后续完成过渡后可直接删除此类。 + * 此过程需在ActableConfig完成后才可进行, + * Order等注解某些打包方式可能出现不生效问题,通过直接依赖config确保执行顺序 + */ +@Configuration +@Slf4j +@Deprecated +public class RobotConfigMigrate { + public RobotConfigMigrate( + @Autowired AlertRobotsMigrateMapper robotsMapper, + @Autowired ActableConfig config + ) { + if (null == config) return; + int n; + if ((n = robotsMapper.migrateProjectRobot()) > 0) { + log.warn("legacy project robot found! migrated to {} alert robots", n); + robotsMapper.clearProjectRobot(); + } + if ((n = robotsMapper.migrateAgentRobot()) > 0) { + log.warn("legacy project robot found! migrated to {} alert robots", n); + robotsMapper.clearAgentRobot(); + } + } +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AgentsController.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AgentsController.java index 2463d660..23e709b6 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AgentsController.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AgentsController.java @@ -19,9 +19,6 @@ import com.alibaba.fastjson.JSONObject; import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.Parameters; -import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; import org.cloud.sonic.common.config.WebAspect; import org.cloud.sonic.common.http.RespEnum; @@ -75,11 +72,12 @@ public RespModel> findAgents() { @WebAspect @Operation(summary = "修改agent信息", description = "修改agent信息") @PutMapping("/update") - public RespModel update(@RequestBody JSONObject jsonObject) { - agentsService.update(jsonObject.getInteger("id"), - jsonObject.getString("name"), jsonObject.getInteger("highTemp"), - jsonObject.getInteger("highTempTime"), jsonObject.getInteger("robotType"), - jsonObject.getString("robotToken"), jsonObject.getString("robotSecret")); + public RespModel update(@RequestBody AgentsDTO jsonObject) { + agentsService.update(jsonObject.getId(), + jsonObject.getName(), jsonObject.getHighTemp(), + jsonObject.getHighTempTime(), jsonObject.getRobotType(), + jsonObject.getRobotToken(), jsonObject.getRobotToken(), + jsonObject.getAlertRobotIds()); return new RespModel<>(RespEnum.HANDLE_OK); } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AlertRobotsAdminController.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AlertRobotsAdminController.java new file mode 100644 index 00000000..61966f2d --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AlertRobotsAdminController.java @@ -0,0 +1,104 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.cloud.sonic.common.config.WebAspect; +import org.cloud.sonic.common.config.WhiteUrl; +import org.cloud.sonic.common.http.RespEnum; +import org.cloud.sonic.common.http.RespModel; +import org.cloud.sonic.controller.models.base.CommentPage; +import org.cloud.sonic.controller.models.domain.AlertRobots; +import org.cloud.sonic.controller.models.dto.AlertRobotsDTO; +import org.cloud.sonic.controller.services.AlertRobotsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@Tag(name = "告警通知机器人相关") +@RestController +@RequestMapping("/alertRobotsAdmin") +public class AlertRobotsAdminController { + + @Autowired + private AlertRobotsService alertRobotsService; + + @WebAspect + @Operation(summary = "更新机器人参数", description = "新增或更新对应的机器人") + @PutMapping + public RespModel save(@Validated @RequestBody AlertRobotsDTO alertRobotsDTO) { + alertRobotsService.saveOrUpdate(alertRobotsDTO.convertTo()); + return new RespModel<>(RespEnum.UPDATE_OK); + } + + @WebAspect + @Operation(summary = "查找机器人参数", description = "查找所有机器人参数列表") + @GetMapping("/list") + @Parameters(value = { + @Parameter(name = "scene", allowEmptyValue = true, description = "使用场景"), + @Parameter(name = "page", description = "页码"), + @Parameter(name = "pageSize", description = "页尺寸") + }) + public RespModel> listAll( + @RequestParam(name = "scene", required = false) String scene, + @RequestParam(name = "page") int page, + @RequestParam(name = "pageSize", defaultValue = "20") int pageSize + ) { + return new RespModel<>(RespEnum.SEARCH_OK, alertRobotsService.findRobots(new Page<>(page, pageSize), null, scene)); + } + + @WebAspect + @Operation(summary = "查找机器人参数", description = "查找所有机器人参数列表") + @GetMapping("/listAll") + @Parameters(value = { + @Parameter(name = "scene", allowEmptyValue = true, description = "使用场景") + }) + public RespModel> listAll( + @RequestParam(name = "scene", required = false) String scene + ) { + return new RespModel<>(RespEnum.SEARCH_OK, alertRobotsService.findAllRobots(null, scene)); + } + + @WebAspect + @Operation(summary = "删除机器人参数", description = "删除对应id的机器人参数") + @Parameter(name = "id", description = "id") + @DeleteMapping + public RespModel delete(@RequestParam(name = "id") int id) { + if (alertRobotsService.removeById(id)) { + return new RespModel<>(RespEnum.DELETE_OK); + } else { + return new RespModel<>(RespEnum.DELETE_FAIL); + } + } + + @WebAspect + @Operation(summary = "获取机器人对类型机器人在相应使用场景下的默认模板", description = "获取机器人对类型机器人在相应使用场景下的默认模板") + @Parameter(name = "type", description = "type") + @Parameter(name = "scene", description = "scene") + @GetMapping("/findDefaultTemplate") + @WhiteUrl + public RespModel getDefaultNoticeTemplate(@RequestParam(name = "type") int type, @RequestParam(name = "scene") String scene) { + return new RespModel<>(RespEnum.SEARCH_OK, alertRobotsService.getDefaultNoticeTemplate(type, scene)); + } +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AlertRobotsController.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AlertRobotsController.java new file mode 100644 index 00000000..727af057 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/controller/AlertRobotsController.java @@ -0,0 +1,112 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.cloud.sonic.common.config.WebAspect; +import org.cloud.sonic.common.config.WhiteUrl; +import org.cloud.sonic.common.http.RespEnum; +import org.cloud.sonic.common.http.RespModel; +import org.cloud.sonic.controller.models.base.CommentPage; +import org.cloud.sonic.controller.models.domain.AlertRobots; +import org.cloud.sonic.controller.models.dto.AlertRobotsDTO; +import org.cloud.sonic.controller.services.AlertRobotsService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@Tag(name = "告警通知机器人相关") +@RestController +@RequestMapping("/alertRobots") +public class AlertRobotsController { + + @Autowired + private AlertRobotsService alertRobotsService; + + @WebAspect + @Operation(summary = "更新机器人参数", description = "新增或更新对应的机器人") + @PutMapping + public RespModel save(@Validated @RequestBody AlertRobotsDTO alertRobotsDTO) { + alertRobotsService.saveOrUpdate(alertRobotsDTO.convertTo()); + return new RespModel<>(RespEnum.UPDATE_OK); + } + + @WebAspect + @Operation(summary = "查找项目机器人参数", description = "查找对应项目id的机器人参数列表") + @GetMapping("/list") + @Parameters(value = { + @Parameter(name = "projectId", description = "项目id"), + @Parameter(name = "scene", allowEmptyValue = true, description = "使用场景"), + @Parameter(name = "page", description = "页码"), + @Parameter(name = "pageSize", description = "页尺寸") + }) + public RespModel> list( + @RequestParam(name = "projectId") int projectId, + @RequestParam(name = "scene", required = false) String scene, + @RequestParam(name = "page") int page, + @RequestParam(name = "pageSize", defaultValue = "20") int pageSize + ) { + return new RespModel<>(RespEnum.SEARCH_OK, alertRobotsService.findRobots(new Page<>(page, pageSize), projectId, scene)); + } + + @WebAspect + @Operation(summary = "查找项目机器人参数", description = "查找对应项目id的机器人参数列表") + @GetMapping("/listAll") + @Parameters(value = { + @Parameter(name = "projectId", allowEmptyValue = true, description = "项目id"), + @Parameter(name = "scene", allowEmptyValue = true, description = "使用场景") + }) + public RespModel> listAll( + @RequestParam(name = "projectId", required = false, defaultValue = "-1") int projectId, + @RequestParam(name = "scene", required = false) String scene + ) { + return new RespModel<>(RespEnum.SEARCH_OK, alertRobotsService.findAllRobots(projectId, scene)); + } + + @WebAspect + @Operation(summary = "删除机器人参数", description = "删除对应id的机器人参数") + @Parameter(name = "id", description = "id") + @DeleteMapping + public RespModel delete( + @Parameter(name = "projectId", description = "项目id") int projectId, + @RequestParam(name = "id") int id + ) { + if (alertRobotsService.removeByMap(Map.of("id", id, "projectId", projectId))) { + return new RespModel<>(RespEnum.DELETE_OK); + } else { + return new RespModel<>(RespEnum.DELETE_FAIL); + } + } + + @WebAspect + @Operation(summary = "获取机器人对类型机器人在相应使用场景下的默认模板", description = "获取机器人对类型机器人在相应使用场景下的默认模板") + @Parameter(name = "type", description = "type") + @Parameter(name = "scene", description = "scene") + @GetMapping("/findDefaultTemplate") + @WhiteUrl + public RespModel getDefaultNoticeTemplate(@RequestParam(name = "type") int type, @RequestParam(name = "scene") String scene) { + return new RespModel<>(RespEnum.SEARCH_OK, alertRobotsService.getDefaultNoticeTemplate(type, scene)); + } +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/mapper/AlertRobotsMapper.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/mapper/AlertRobotsMapper.java new file mode 100644 index 00000000..0d6b3b79 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/mapper/AlertRobotsMapper.java @@ -0,0 +1,84 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.cloud.sonic.controller.models.domain.AlertRobots; +import org.springframework.lang.Nullable; + +import java.util.List; + +public interface AlertRobotsMapper extends BaseMapper { + @Select(""" + select ifnull(alert_robot_ids,(select testsuite_alert_robot_ids from projects p where p.id = ts.project_id)) + as alert_robot_ids from test_suites ts where id = #{suiteId} + """) + @Nullable String getIdsForTestsuite(@Param("suiteId") int suiteId); + + @Select(""" + + """) + List listTestsuiteRobotsFromIds(@Param("ids") @Nullable String ids, @Param("suiteId") int suiteId); + + default List computeTestsuiteRobots(int suiteId) { + var ids = getIdsForTestsuite(suiteId); + if ("".equals(ids)) return List.of(); + return listTestsuiteRobotsFromIds(ids, suiteId); + } + + @Select("select alert_robot_ids from agents where id = #{agentId}") + @Nullable String getIdsForAgent(@Param("agentId") int agentId); + + @Select(""" + """) + List listAgentRobotsFromIds(@Param("ids") @Nullable String ids); + + default List computeAgentRobots(int agentId) { + String ids = getIdsForAgent(agentId); + if ("".equals(ids)) return List.of(); + return listAgentRobotsFromIds(ids); + } + + @Select(""" + select * from alert_robots r where scene = 'summary' + and ( + r.project_id = ${projectId} or + (r.project_id is null and 1 = (select global_robot from projects where id = ${projectId}) + ) + """) + List computeSummaryRobots(@Param("projectId") int projectId); +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/mapper/AlertRobotsMigrateMapper.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/mapper/AlertRobotsMigrateMapper.java new file mode 100644 index 00000000..7b42d853 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/mapper/AlertRobotsMigrateMapper.java @@ -0,0 +1,54 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.mapper; + +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Update; + +/** + * @see org.cloud.sonic.controller.config.mybatis.RobotConfigMigrate + * @deprecated + */ +@Deprecated +public interface AlertRobotsMigrateMapper { + + @Insert(""" + INSERT into alert_robots (robot_secret, robot_token, robot_type, project_id, name, scene) + SELECT robot_secret, robot_token, robot_type, id, project_name, 'summary' + FROM projects + WHERE robot_type > 0 and robot_token != '' + UNION ALL + SELECT robot_secret, robot_token, robot_type, id, project_name, 'testsuite' + FROM projects + WHERE robot_type > 0 and robot_token != '' + """) + int migrateProjectRobot(); + @Update("update projects set robot_type = -robot_type where robot_type > 0") + void clearProjectRobot(); + + @Insert(""" + INSERT into alert_robots (robot_secret, robot_token, robot_type, project_id, name, scene) + SELECT robot_secret, robot_token, robot_type, null, 'agent', 'agent' + FROM agents + WHERE robot_type > 0 and robot_token != '' + """) + int migrateAgentRobot(); + + @Update("update agents set robot_type = -robot_type where robot_type > 0") + void clearAgentRobot(); +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/Agents.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/Agents.java index ed159a01..4766a6c0 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/Agents.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/Agents.java @@ -1,12 +1,10 @@ package org.cloud.sonic.controller.models.domain; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.*; import com.gitee.sunchenbin.mybatis.actable.annotation.*; import com.gitee.sunchenbin.mybatis.actable.constants.MySqlCharsetConstant; import com.gitee.sunchenbin.mybatis.actable.constants.MySqlEngineConstant; +import com.gitee.sunchenbin.mybatis.actable.constants.MySqlTypeConstant; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; @@ -15,6 +13,7 @@ import lombok.experimental.Accessors; import org.cloud.sonic.controller.models.base.TypeConverter; import org.cloud.sonic.controller.models.dto.AgentsDTO; +import org.cloud.sonic.controller.tools.NullableIntArrayTypeHandler; import java.io.Serializable; @@ -28,7 +27,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -@TableName("agents") +@TableName(value = "agents", autoResultMap = true) @TableComment("agents表") @TableCharset(MySqlCharsetConstant.DEFAULT) @TableEngine(MySqlEngineConstant.InnoDB) @@ -78,14 +77,29 @@ public class Agents implements Serializable, TypeConverter { @Column(value = "high_temp_time", isNull = false, comment = "highTempTime", defaultValue = "15") private Integer highTempTime; + /** + * @see org.cloud.sonic.controller.config.mybatis.RobotConfigMigrate + * @deprecated + */ + @Deprecated(forRemoval = true) @TableField @Column(value = "robot_secret", isNull = false, comment = "机器人秘钥", defaultValue = "") private String robotSecret; + /** + * @see org.cloud.sonic.controller.config.mybatis.RobotConfigMigrate + * @deprecated + */ + @Deprecated(forRemoval = true) @TableField @Column(value = "robot_token", isNull = false, comment = "机器人token", defaultValue = "") private String robotToken; + /** + * @see org.cloud.sonic.controller.config.mybatis.RobotConfigMigrate + * @deprecated + */ + @Deprecated(forRemoval = true) @TableField @Column(value = "robot_type", isNull = false, comment = "机器人类型", defaultValue = "1") private Integer robotType; @@ -93,4 +107,8 @@ public class Agents implements Serializable, TypeConverter { @TableField @Column(value = "has_hub", isNull = false, comment = "是否使用了Sonic hub", defaultValue = "0") private Integer hasHub; + + @TableField(typeHandler = NullableIntArrayTypeHandler.class, updateStrategy = FieldStrategy.IGNORED) + @Column(value = "alert_robot_ids", type = MySqlTypeConstant.VARCHAR, length = 1024, comment = "逗号分隔通知机器人id串,为null时自动选取所有可用机器人") + private int[] alertRobotIds; } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/AlertRobots.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/AlertRobots.java new file mode 100644 index 00000000..018d0c69 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/AlertRobots.java @@ -0,0 +1,82 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.models.domain; + +import com.baomidou.mybatisplus.annotation.*; +import com.gitee.sunchenbin.mybatis.actable.annotation.*; +import com.gitee.sunchenbin.mybatis.actable.constants.MySqlCharsetConstant; +import com.gitee.sunchenbin.mybatis.actable.constants.MySqlEngineConstant; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.cloud.sonic.controller.models.base.TypeConverter; +import org.cloud.sonic.controller.models.dto.AlertRobotsDTO; + +import java.io.Serializable; + + +@Schema(name = "AlertRobots对象", description = "") +@Data +@Accessors(chain = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@TableName("alert_robots") +@TableComment("alert_robots表") +@TableCharset(MySqlCharsetConstant.DEFAULT) +@TableEngine(MySqlEngineConstant.InnoDB) +public class AlertRobots implements Serializable, TypeConverter { + + @TableId(value = "id", type = IdType.AUTO) + @IsAutoIncrement + private Integer id; + + @TableField(updateStrategy = FieldStrategy.IGNORED) + @Column(value = "project_id", comment = "可用项目id, null为公共机器人") + @Index(value = "IDX_PROJECT_ID", columns = {"project_id"}) + private Integer projectId; + + @TableField + @Column(isNull = false, comment = "显示名称") + private String name; + + @TableField + @Column(value = "robot_secret", comment = "机器人秘钥") + private String robotSecret; + + @TableField + @Column(value = "robot_token", isNull = false, comment = "机器人token/接口uri") + private String robotToken; + + @TableField + @Column(value = "robot_type", isNull = false, comment = "机器人类型") + private Integer robotType; + + @TableField + @Column(value = "scene", isNull = false, comment = "使用场景,可选 agent, testsuite, summary") + private String scene; + + @Column(value = "mute_rule", length = 1024, isNull = false, defaultValue = "", comment = "静默规则,SpEL表达式,表达式求值为true时不发送消息,否则正常发送") + private String muteRule; + + @Column(value = "template", length = 4096, isNull = false, defaultValue = "", comment = "通知模板,SpEL表达式,表达式为空时机器人类型自动使用默认值") + private String template; +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/Projects.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/Projects.java index 50d44f6d..da6da7dd 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/Projects.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/domain/Projects.java @@ -14,6 +14,7 @@ import lombok.experimental.Accessors; import org.cloud.sonic.controller.models.base.TypeConverter; import org.cloud.sonic.controller.models.dto.ProjectsDTO; +import org.cloud.sonic.controller.tools.NullableIntArrayTypeHandler; import java.io.Serializable; import java.util.Date; @@ -28,7 +29,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -@TableName("projects") +@TableName(value = "projects", autoResultMap = true) @TableComment("项目表") @TableCharset(MySqlCharsetConstant.DEFAULT) @TableEngine(MySqlEngineConstant.InnoDB) @@ -55,15 +56,38 @@ public class Projects implements Serializable, TypeConverter @Schema(description = "highTempTime", example = "10") Integer highTempTime; + /** + * @see org.cloud.sonic.controller.config.mybatis.RobotConfigMigrate + * @deprecated + */ + @Deprecated(forRemoval = true) @Schema(description = "机器人类型", example = "1") Integer robotType; + /** + * @see org.cloud.sonic.controller.config.mybatis.RobotConfigMigrate + * @deprecated + */ + @Deprecated(forRemoval = true) @Schema(description = "机器人token", example = "token") String robotToken; + /** + * @see org.cloud.sonic.controller.config.mybatis.RobotConfigMigrate + * @deprecated + */ + @Deprecated(forRemoval = true) @Schema(description = "机器人加签密钥", example = "key") String robotSecret; @Schema(description = "是否使用sonic hub", example = "1") Integer hasHub; + + @Schema(description = "通知机器人id串,为null时自动选取所有可用机器人", example = "[1,2]") + int[] alertRobotIds; } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/dto/AlertRobotsDTO.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/dto/AlertRobotsDTO.java new file mode 100644 index 00000000..95da5ea8 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/dto/AlertRobotsDTO.java @@ -0,0 +1,57 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.models.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.cloud.sonic.controller.models.base.TypeConverter; +import org.cloud.sonic.controller.models.domain.AlertRobots; + +import java.io.Serializable; + +@Schema(name = "告警通知机器人DTO 模型") +@Data +@Accessors(chain = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class AlertRobotsDTO implements Serializable, TypeConverter { + + @Schema(description = "通知机器人id", example = "1") + Integer id; + @Schema(description = "机器人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + Integer robotType; + @Schema(description = "机器人token", requiredMode = Schema.RequiredMode.REQUIRED, example = "http://dingTalk.com?token=*****") + String robotToken; + @Schema(description = "机器人加签密钥", example = "qwe***") + String robotSecret; + @Schema(description = "机器人配置显示名称", example = "1") + private String name; + @Schema(description = "所属项目id,不属于任何项目的系统级机器人此项为null", example = "1") + private Integer projectId; + @Schema(description = "通知类型,可选 agent, testsuite, summary", example = "testsuite") + private String scene; + @Schema(description = "静默规则,SpEL表达式,表达式求值为真时不发送消息", example = "error == 0 && fail == 0") + private String muteRule; + @Schema(description = "通知模板,SpEL表达式,表达式为null时采用相应 机器人类型 的缺省模板", example = "alert! pass count is #{pass}, error count is #{error}") + private String template; +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/dto/ProjectsDTO.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/dto/ProjectsDTO.java index b4754f88..d8d7903d 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/dto/ProjectsDTO.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/dto/ProjectsDTO.java @@ -32,12 +32,27 @@ public class ProjectsDTO implements Serializable, TypeConverter devices; + + @Schema(description = "测试套件默认通知机器人id串,为null时取项目配置的默认值", example = "[1,2]") + int[] alertRobotIds; } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/interfaces/RobotType.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/interfaces/RobotType.java index cd2be166..145f00c7 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/interfaces/RobotType.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/models/interfaces/RobotType.java @@ -8,4 +8,6 @@ public interface RobotType { int Telegram = 5; int LineNotify = 6; int SlackNotify = 7; + + int WebHook = 8; } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/AgentsService.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/AgentsService.java index 7a6603f2..a3559268 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/AgentsService.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/AgentsService.java @@ -31,9 +31,7 @@ public interface AgentsService extends IService { List findAgents(); - void update(int id, String name, int highTemp, int highTempTime, int robotType, String robotToken, String robotSecret); - - // todo 删除 + void update(int id, String name, int highTemp, int highTempTime, int robotType, String robotToken, String robotSecret, int[] alertRobotIds); boolean offLine(int id); diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/AlertRobotsService.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/AlertRobotsService.java new file mode 100644 index 00000000..5146f6f8 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/AlertRobotsService.java @@ -0,0 +1,44 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.services; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; +import org.cloud.sonic.controller.models.base.CommentPage; +import org.cloud.sonic.controller.models.domain.AlertRobots; + +import java.util.Date; +import java.util.List; + +public interface AlertRobotsService extends IService { + String SCENE_AGENT = "agent"; + String SCENE_TESTSUITE = "testsuite"; + String SCENE_SUMMARY = "summary"; + + CommentPage findRobots(Page page, Integer projectId, String scene); + + List findAllRobots(Integer projectId, String scene); + + void sendResultFinishReport(int suitId, String suiteName, int pass, int warn, int fail, int projectId, int resultId); + + void sendProjectReportMessage(int projectId, String projectName, Date startDate, Date endDate, boolean isWeekly, int pass, int warn, int fail); + + void sendErrorDevice(int agentId, int errorType, int tem, String udId); + + String getDefaultNoticeTemplate(int type, String scene); +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/AgentsServiceImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/AgentsServiceImpl.java index e35637b5..1f0d03d8 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/AgentsServiceImpl.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/AgentsServiceImpl.java @@ -28,7 +28,6 @@ import org.cloud.sonic.controller.services.AgentsService; import org.cloud.sonic.controller.services.DevicesService; import org.cloud.sonic.controller.services.impl.base.SonicServiceImpl; -import org.cloud.sonic.controller.tools.RobotMsgTool; import org.cloud.sonic.controller.transport.TransportWorker; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -44,7 +43,7 @@ public class AgentsServiceImpl extends SonicServiceImpl im @Autowired private DevicesService devicesService; @Autowired - private RobotMsgTool robotMsgTool; + private AlertRobotsServiceImpl alertRobotsService; @Autowired private AgentsMapper agentsMapper; @@ -54,7 +53,7 @@ public List findAgents() { } @Override - public void update(int id, String name, int highTemp, int highTempTime, int robotType, String robotToken, String robotSecret) { + public void update(int id, String name, int highTemp, int highTempTime, int robotType, String robotToken, String robotSecret, int[] alertRobotIds) { if (id == 0) { Agents agents = new Agents(); agents.setName(name); @@ -70,6 +69,7 @@ public void update(int id, String name, int highTemp, int highTempTime, int robo agents.setRobotSecret(robotSecret); agents.setSecretKey(UUID.randomUUID().toString()); agents.setHasHub(0); + agents.setAlertRobotIds(alertRobotIds); save(agents); } else { Agents ag = findById(id); @@ -80,6 +80,7 @@ public void update(int id, String name, int highTemp, int highTempTime, int robo ag.setRobotType(robotType); ag.setRobotToken(robotToken); ag.setRobotSecret(robotSecret); + ag.setAlertRobotIds(alertRobotIds); save(ag); JSONObject result = new JSONObject(); result.put("msg", "settings"); @@ -169,9 +170,6 @@ public Agents findBySecretKey(String secretKey) { @Override public void errCall(int id, String udId, int tem, int type) { - Agents agents = findById(id); - if (agents != null && agents.getRobotType() != 0 && agents.getRobotToken().length() > 0 && agents.getRobotSecret().length() > 0) { - robotMsgTool.sendErrorDevice(agents.getRobotToken(), agents.getRobotSecret(), agents.getRobotType(), type, tem, udId); - } + alertRobotsService.sendErrorDevice(id, type, tem, udId); } } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/AlertRobotsServiceImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/AlertRobotsServiceImpl.java new file mode 100644 index 00000000..bfd16497 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/AlertRobotsServiceImpl.java @@ -0,0 +1,128 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.services.impl; + +import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.mapper.AlertRobotsMapper; +import org.cloud.sonic.controller.models.base.CommentPage; +import org.cloud.sonic.controller.models.domain.AlertRobots; +import org.cloud.sonic.controller.services.AlertRobotsService; +import org.cloud.sonic.controller.services.impl.base.SonicServiceImpl; +import org.cloud.sonic.controller.tools.robot.Message; +import org.cloud.sonic.controller.tools.robot.RobotFactory; +import org.cloud.sonic.controller.tools.robot.message.DeviceMessage; +import org.cloud.sonic.controller.tools.robot.message.ProjectSummaryMessage; +import org.cloud.sonic.controller.tools.robot.message.TestSuiteMessage; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Date; +import java.util.List; + +@Service +@Slf4j +public class AlertRobotsServiceImpl extends SonicServiceImpl implements AlertRobotsService { + + @Value("${robot.client.host}") + private String clientHost; + + @Autowired + private RobotFactory robotFactory; + + @Autowired + private RestTemplate restTemplate; + + @Override + public CommentPage findRobots(Page page, Integer projectId, String scene) { + return CommentPage.convertFrom(findRobots(projectId, scene).page(page)); + } + + @Override + public List findAllRobots(Integer projectId, String scene) { + return findRobots(projectId, scene).list(); + } + + private LambdaQueryChainWrapper findRobots(Integer projectId, String scene) { + var query = lambdaQuery(); + query.eq(null != scene, AlertRobots::getScene, scene); + query.and(null != projectId, + it -> it.eq(AlertRobots::getProjectId, projectId).or( + p -> p.apply("1 = (select global_robot from projects where id = {0})", projectId).isNull(AlertRobots::getProjectId) + ) + ); + return query; + } + + @Override + public void sendResultFinishReport(int suitId, String suiteName, int pass, int warn, int fail, int projectId, int resultId) { + var robots = baseMapper.computeTestsuiteRobots(suitId); + if (robots.isEmpty()) return; + var msg = new TestSuiteMessage(suiteName, pass, warn, fail, projectId, resultId, clientHost + "/Home/" + projectId + "/ResultDetail/" + resultId); + send(robots, msg); + } + + @Override + public void sendProjectReportMessage(int projectId, String projectName, Date startDate, Date endDate, boolean isWeekly, int pass, int warn, int fail) { + var robots = baseMapper.computeSummaryRobots(projectId); + if (robots.isEmpty()) return; + var total = pass + warn + fail; + var rate = total > 0 ? BigDecimal.valueOf(((float) pass / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0; + var url = clientHost + "/Home/" + projectId; + var msg = new ProjectSummaryMessage(projectId, projectName, startDate, endDate, pass, warn, fail, rate, total, url, isWeekly); + send(robots, msg); + } + + @Override + public void sendErrorDevice(int agentId, int errorType, int tem, String udId) { + var robots = baseMapper.computeAgentRobots(agentId); + if (robots.isEmpty()) return; + var msg = new DeviceMessage(errorType, tem, udId); + send(robots, msg); + } + + private void send(List robots, Message message) { + for (var robot : robots) { + try { + var messenger = robotFactory.getRobotMessenger(robot.getRobotType(), robot.getMuteRule()); + if (messenger == null) continue; + var template = robot.getTemplate(); + messenger.sendMessage(restTemplate, robot.getRobotToken(), robot.getRobotSecret(), template, message); + } catch (Exception e) { + log.warn("send messaget to robot {} failed, skipping", robot, e); + } + } + } + + @Override + public String getDefaultNoticeTemplate(int type, String scene) { + var messenger = robotFactory.getRobotMessenger(type); + return switch (scene) { + case SCENE_AGENT -> messenger.getDefaultDeviceMessageTemplate().getExpressionString(); + case SCENE_SUMMARY -> messenger.getDefaultProjectSummaryTemplate().getExpressionString(); + case SCENE_TESTSUITE -> messenger.getDefaultTestSuiteTemplate().getExpressionString(); + default -> ""; + }; + } +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/ResultsServiceImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/ResultsServiceImpl.java index 46769322..8e5d10c4 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/ResultsServiceImpl.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/services/impl/ResultsServiceImpl.java @@ -31,7 +31,6 @@ import org.cloud.sonic.controller.models.interfaces.ResultStatus; import org.cloud.sonic.controller.services.*; import org.cloud.sonic.controller.services.impl.base.SonicServiceImpl; -import org.cloud.sonic.controller.tools.RobotMsgTool; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -61,7 +60,7 @@ public class ResultsServiceImpl extends SonicServiceImpl @Autowired private ProjectsService projectsService; @Autowired - private RobotMsgTool robotMsgTool; + private AlertRobotsService alertRobotsService; @Autowired private TestSuitesService testSuitesService; @Autowired @@ -239,10 +238,7 @@ public void sendDayReport() { break; } } - if (projects.getRobotType() != 0 && projects.getRobotToken().length() > 0) { - robotMsgTool.sendDayReportMessage(projects.getRobotToken(), projects.getRobotSecret(), projects.getId() - , projects.getProjectName(), sf.format(yesterday), sf.format(today), suc, warn, fail, projects.getRobotType()); - } + alertRobotsService.sendProjectReportMessage(projects.getId(), projects.getProjectName(), yesterday, today, false, suc, warn, fail); } } @@ -273,10 +269,7 @@ public void sendWeekReport() { break; } } - if (projects.getRobotType() != 0 && projects.getRobotToken().length() > 0) { - robotMsgTool.sendWeekReportMessage(projects.getRobotToken(), projects.getRobotSecret(), projects.getId() - , projects.getProjectName(), sf.format(lastWeek), sf.format(today), suc, warn, fail, count, projects.getRobotType()); - } + alertRobotsService.sendProjectReportMessage(projects.getId(), projects.getProjectName(), lastWeek, today, true, suc, warn, fail); } } @@ -336,11 +329,8 @@ public void setStatus(Results results) { if (results.getReceiveMsgCount() == results.getSendMsgCount()) { results.setEndTime(new Date()); save(results); - Projects projects = projectsService.findById(results.getProjectId()); - if (projects != null && projects.getRobotType() != 0 && projects.getRobotToken().length() > 0) { - robotMsgTool.sendResultFinishReport(projects.getRobotToken(), projects.getRobotSecret(), - results.getSuiteName(), sucCount, warnCount, failCount, projects.getId(), results.getId(), projects.getRobotType()); - } + alertRobotsService.sendResultFinishReport(results.getSuiteId(), + results.getSuiteName(), sucCount, warnCount, failCount, results.getProjectId(), results.getId()); } } } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/NullableIntArrayTypeHandler.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/NullableIntArrayTypeHandler.java new file mode 100644 index 00000000..d98ca275 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/NullableIntArrayTypeHandler.java @@ -0,0 +1,67 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.tools; + +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.MappedJdbcTypes; +import org.apache.ibatis.type.MappedTypes; +import org.apache.ibatis.type.TypeHandler; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.stream.Collectors; + +@MappedTypes(int[].class) +@MappedJdbcTypes(JdbcType.VARCHAR) +public class NullableIntArrayTypeHandler implements TypeHandler { + @Override + public void setParameter(PreparedStatement preparedStatement, int i, int[] param, JdbcType jdbcType) throws SQLException { + if (null == param) { + preparedStatement.setNull(i, jdbcType.TYPE_CODE); + } else { + preparedStatement.setString(i, Arrays.stream(param).mapToObj(Integer::toString).collect(Collectors.joining(","))); + } + } + + @Override + public int[] getResult(ResultSet resultSet, String s) throws SQLException { + return parseString(resultSet.getString(s)); + } + + @Override + public int[] getResult(ResultSet resultSet, int i) throws SQLException { + return parseString(resultSet.getString(i)); + } + + @Override + public int[] getResult(CallableStatement callableStatement, int i) throws SQLException { + return parseString(callableStatement.getString(i)); + } + + private int[] parseString(String ret) { + if (ret == null) return null; + try { + return Arrays.stream(ret.split(",", 0)).mapToInt(Integer::valueOf).toArray(); + } catch (NumberFormatException e) { + return new int[0]; + } + } +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/RobotMsgTool.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/RobotMsgTool.java deleted file mode 100644 index 55029104..00000000 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/RobotMsgTool.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * sonic-server Sonic Cloud Real Machine Platform. - * Copyright (C) 2022 SonicCloudOrg - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package org.cloud.sonic.controller.tools; - -import org.cloud.sonic.controller.tools.robot.RobotFactory; -import org.cloud.sonic.controller.tools.robot.RobotMessenger; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; - -/** - * @author ZhouYiXun - * @des 机器人推送相关工具类 - * @date 2021/8/15 18:20 - */ -@Component -public class RobotMsgTool { - - @Autowired - private RobotFactory robotFactory; - - @Autowired - private RestTemplate restTemplate; - - /** - * @param token 机器人token - * @param secret 机器人密钥 - * @param suiteName 套件名称 - * @param pass 通过数量 - * @param warn 警告数量 - * @param fail 失败数量 - * @param projectId 项目id - * @param resultId 结果id - * @return void - * @author ZhouYiXun - * @des 发送每次测试结果到钉钉 - * @date 2021/8/20 18:29 - */ - public void sendResultFinishReport(String token, String secret, String suiteName, int pass, - int warn, int fail, int projectId, int resultId, int type) { - RobotMessenger robotMessenger = robotFactory.getRobotMessenger(type); - robotMessenger.sendResultFinishReport(restTemplate, token, secret, suiteName, pass, warn, fail, projectId, resultId); - } - - public void sendDayReportMessage(String token, String secret, int projectId, String projectName, - String yesterday, String today, int passCount, int warnCount, int failCount, int type) { - RobotMessenger robotMessenger = robotFactory.getRobotMessenger(type); - robotMessenger.sendDayReportMessage(restTemplate, token, secret, projectId, projectName, yesterday, today, passCount, warnCount, failCount); - } - - public void sendErrorDevice(String token, String secret, int type, int errorType, int tem, String udId) { - RobotMessenger robotMessenger = robotFactory.getRobotMessenger(type); - robotMessenger.sendErrorDevice(restTemplate, token, secret, errorType, tem, udId); - } - - public void sendWeekReportMessage(String token, String secret, int projectId, String projectName, - String yesterday, String today, int passCount, int warnCount, int failCount, int count, int type) { - RobotMessenger robotMessenger = robotFactory.getRobotMessenger(type); - robotMessenger.sendWeekReportMessage(restTemplate, token, secret, projectId, projectName, yesterday, today, passCount, warnCount, failCount, count); - } -} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/Message.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/Message.java new file mode 100644 index 00000000..001fb2b7 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/Message.java @@ -0,0 +1,46 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.tools.robot; + +import java.text.SimpleDateFormat; +import java.util.Calendar; + +public abstract class Message { + public final Calendar now = Calendar.getInstance(); + public Object ext = null; + public SimpleDateFormat format = null; + + public SimpleDateFormat getFormat(String pattern) { + if (null == format) { + format = new SimpleDateFormat(pattern); + } else { + format.applyPattern(pattern); + } + return format; + } + + public SimpleDateFormat getFormat() { + if (null == format) { + format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + } + return format; + } + +} + + diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/RobotFactory.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/RobotFactory.java index a7699b84..687111b2 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/RobotFactory.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/RobotFactory.java @@ -53,8 +53,19 @@ public RobotMessenger getRobotMessenger(int robotType) { case RobotType.Telegram -> robotMessenger = context.getBean(TelegramImpl.class); case RobotType.LineNotify -> robotMessenger = context.getBean(LineNotifyImpl.class); case RobotType.SlackNotify -> robotMessenger = context.getBean(SlackNotifyImpl.class); + case RobotType.WebHook -> robotMessenger = context.getBean(WebhookImpl.class); default -> throw new SonicException("Unsupported robot type"); } return robotMessenger; } + + public RobotMessenger getRobotMessenger(int robotType, String muteRule) { + if (!muteRule.isEmpty()) { + var mute = RobotMessenger.parseTemplate(muteRule).getValue(RobotMessenger.ctx, String.class); + if ("true".equals(mute)) { + return null; + } + } + return getRobotMessenger(robotType); + } } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/RobotMessenger.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/RobotMessenger.java index e289d4a5..e2042b91 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/RobotMessenger.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/RobotMessenger.java @@ -17,24 +17,53 @@ */ package org.cloud.sonic.controller.tools.robot; +import org.cloud.sonic.controller.tools.robot.message.DeviceMessage; +import org.cloud.sonic.controller.tools.robot.message.ProjectSummaryMessage; +import org.cloud.sonic.controller.tools.robot.message.TestSuiteMessage; +import org.springframework.expression.EvaluationContext; +import org.springframework.expression.Expression; +import org.springframework.expression.ExpressionParser; +import org.springframework.expression.ParserContext; +import org.springframework.expression.common.TemplateParserContext; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.SimpleEvaluationContext; import org.springframework.web.client.RestTemplate; -/** - * @author ayumi760405 - * @des 推送机器人方法介面 - * @date 2022/12/19 - */ +import java.util.WeakHashMap; + public interface RobotMessenger { + ExpressionParser defaultParser = new SpelExpressionParser(); + ParserContext templateParserContext = new TemplateParserContext(); + WeakHashMap expressionCache = new WeakHashMap<>(); + EvaluationContext ctx = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build(); + + static Expression parseTemplate(String template) { + return expressionCache.computeIfAbsent(template, it -> defaultParser.parseExpression(it, templateParserContext)); + } - void sendResultFinishReport(RestTemplate restTemplate, String token, String secret, String suiteName, int pass, - int warn, int fail, int projectId, int resultId); + default void sendMessage(RestTemplate restTemplate, String token, String secret, String messageTemplate, Message message) { + Expression template; + if (messageTemplate.isEmpty()) { + if (message instanceof TestSuiteMessage) { + template = getDefaultTestSuiteTemplate(); + } else if (message instanceof ProjectSummaryMessage) { + template = getDefaultProjectSummaryTemplate(); + } else if (message instanceof DeviceMessage) { + template = getDefaultDeviceMessageTemplate(); + } else { + return; + } + } else { + template = parseTemplate(messageTemplate); + } + sendMessage(restTemplate, token, secret, template, message); + } - void sendDayReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, - String yesterday, String today, int passCount, int warnCount, int failCount); + void sendMessage(RestTemplate restTemplate, String token, String secret, Expression messageTemplate, Message message); - void sendErrorDevice(RestTemplate restTemplate, String token, String secret, int errorType, int tem, String udId); + Expression getDefaultTestSuiteTemplate(); - void sendWeekReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, - String yesterday, String today, int passCount, int warnCount, int failCount, int count); + Expression getDefaultProjectSummaryTemplate(); + Expression getDefaultDeviceMessageTemplate(); } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/message/DeviceMessage.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/message/DeviceMessage.java new file mode 100644 index 00000000..3548db9c --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/message/DeviceMessage.java @@ -0,0 +1,28 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.tools.robot.message; + +import lombok.AllArgsConstructor; +import org.cloud.sonic.controller.tools.robot.Message; + +@AllArgsConstructor +public class DeviceMessage extends Message { + public int errorType; + public int tem; + public String udId; +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/message/ProjectSummaryMessage.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/message/ProjectSummaryMessage.java new file mode 100644 index 00000000..e749661a --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/message/ProjectSummaryMessage.java @@ -0,0 +1,38 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.tools.robot.message; + +import lombok.AllArgsConstructor; +import org.cloud.sonic.controller.tools.robot.Message; + +import java.util.Date; + +@AllArgsConstructor +public class ProjectSummaryMessage extends Message { + public int projectId; + public String projectName; + public Date startDate; + public Date endDate; + public int pass; + public int warn; + public int fail; + public double rate; + public int total; + public String url; + public boolean isWeekly; +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/message/TestSuiteMessage.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/message/TestSuiteMessage.java new file mode 100644 index 00000000..4907d1cb --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/message/TestSuiteMessage.java @@ -0,0 +1,32 @@ +/* + * sonic-server Sonic Cloud Real Machine Platform. + * Copyright (C) 2022 SonicCloudOrg + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.cloud.sonic.controller.tools.robot.message; + +import lombok.AllArgsConstructor; +import org.cloud.sonic.controller.tools.robot.Message; + +@AllArgsConstructor +public class TestSuiteMessage extends Message { + public String suiteName; + public int pass; + public int warn; + public int fail; + public int projectId; + public int resultId; + public String url; +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/DingTalkImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/DingTalkImpl.java index b2cac109..08eb0a58 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/DingTalkImpl.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/DingTalkImpl.java @@ -19,19 +19,21 @@ import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.tools.robot.Message; import org.cloud.sonic.controller.tools.robot.RobotMessenger; import org.springframework.beans.factory.annotation.Value; +import org.springframework.expression.Expression; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.util.Base64Utils; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Map; /** * @author ayumi760405 @@ -42,10 +44,6 @@ @Service("DingTalkImpl") public class DingTalkImpl implements RobotMessenger { - //从配置文件获取前端部署的host - @Value("${robot.client.host}") - private String clientHost; - //成功时的图片url @Value("${robot.img.success}") private String successUrl; //警告时的图片url @@ -55,6 +53,50 @@ public class DingTalkImpl implements RobotMessenger { @Value("${robot.img.error}") private String errorUrl; + Expression templateTestSuiteMessage = RobotMessenger.parseTemplate(""" + #{ + { + msgtype: 'link', + link: { + title: '测试套件:' + suiteName + '运行完毕!', + text: '通过数:'+pass+' + 异常数:'+warn+' + 失败数:'+fail, + messageUrl: url, + picUrl: (fail > 0 ? ext.errorUrl : (warn > 0 ? ext.warningUrl : ext.successUrl)) + } + } + }"""); + Expression templateProjectSummaryMessage = RobotMessenger.parseTemplate(""" + #{ + { + msgtype: 'markdown', + markdown: { + title: 'Sonic云真机测试平台'+(isWeekly?'周':'日')+'报', + text: '### Sonic云真机测试平台'+(isWeekly ? '周': '日')+'报 + > ###### 项目:'+projectName+' + > ###### 时间:'+getFormat().format(startDate)+' ~ '+getFormat().format(endDate)+' + > ###### 共测试:'+total+'次 + > ###### 通过数:'+pass+' + > ###### 异常数:'+warn+' + > ###### 失败数:'+fail+' + > ###### 测试通过率:'+rate+'% + > ###### 详细统计:[点击查看]('+url+')' + } + } + }"""); + Expression templateDeviceMessage = RobotMessenger.parseTemplate(""" + #{ + { msgtype: 'markdown', + markdown: { + title: '设备温度异常通知', + text: '### Sonic设备高温'+(errorType == 1 ? '预警' : '超时,已关机!')+' + > ###### 设备序列号:'+udId+' + > ###### 电池温度:'+tem+' ℃' + } + } + }"""); + /** * @param restTemplate RestTemplate * @param token 机器人token @@ -64,191 +106,46 @@ public class DingTalkImpl implements RobotMessenger { * @des 钉钉官方签名方法 * @date 2021/8/20 18:20 */ - private void signAndSend(RestTemplate restTemplate, String token, String secret, JSONObject jsonObject) { - clientHost = clientHost.replace(":80/", "/"); + private void signAndSend(RestTemplate restTemplate, String token, String secret, Map jsonObject) { try { String path = ""; if (!StringUtils.isEmpty(secret)) { Long timestamp = System.currentTimeMillis(); String stringToSign = timestamp + "\n" + secret; Mac mac = Mac.getInstance("HmacSHA256"); - mac.init(new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256")); - byte[] signData = mac.doFinal(stringToSign.getBytes("UTF-8")); - String sign = URLEncoder.encode(new String(Base64Utils.encode(signData)), "UTF-8"); + mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); + byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)); + String sign = URLEncoder.encode(new String(Base64.getEncoder().encode(signData)), StandardCharsets.UTF_8); path = "×tamp=" + timestamp + "&sign=" + sign; } - ResponseEntity responseEntity = - restTemplate.postForEntity(token + path - , jsonObject, JSONObject.class); + ResponseEntity responseEntity = restTemplate.postForEntity(token + path, jsonObject, JSONObject.class); log.info("robot result: " + responseEntity.getBody()); } catch (Exception e) { log.warn("robot send failed, cause: " + e.getMessage()); } } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param suiteName 套件名称 - * @param pass 通过数量 - * @param warn 警告数量 - * @param fail 失败数量 - * @param projectId 项目id - * @param resultId 结果id - * @return void - * @author ZhouYiXun - * @des 发送每次测试结果到钉钉 - * @date 2021/8/20 18:29 - */ @Override - public void sendResultFinishReport(RestTemplate restTemplate, String token, String secret, String suiteName, int pass, int warn, int fail, int projectId, int resultId) { - JSONObject jsonObject = new JSONObject(); - JSONObject link = new JSONObject(); - link.put("text", "通过数:" + pass + - " \n异常数:" + warn + - " \n失败数:" + fail); - link.put("title", "测试套件: " + suiteName + " 运行完毕!"); - link.put("messageUrl", clientHost + "/Home/" + projectId + "/ResultDetail/" + resultId); - //判断测试结果,来决定显示什么图片 - if (fail > 0) { - link.put("picUrl", errorUrl); - } else if (warn > 0) { - link.put("picUrl", warningUrl); - } else { - link.put("picUrl", successUrl); - } - jsonObject.put("msgtype", "link"); - jsonObject.put("link", link); - this.signAndSend(restTemplate, token, secret, jsonObject); + public void sendMessage(RestTemplate restTemplate, String token, String secret, Expression messageTemplate, Message msg) { + msg.ext = this; + Map content = messageTemplate.getValue(ctx, msg, Map.class); + this.signAndSend(restTemplate, token, secret, content); } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @return void - * @author ZhouYiXun - * @des 发送日报 - * @date 2021/8/20 18:42 - */ @Override - public void sendDayReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount) { - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - JSONObject markdown = new JSONObject(); - //根据三个数量来决定markdown的字体颜色 - String failColorString; - if (failCount == 0) { - failColorString = "" + failCount + ""; - } else { - failColorString = "" + failCount + ""; - } - String warnColorString; - if (warnCount == 0) { - warnColorString = "" + warnCount + ""; - } else { - warnColorString = "" + warnCount + ""; - } - markdown.put("text", "### Sonic云真机测试平台日报 \n" + - "> ###### 项目:" + projectName + " \n" + - "> ###### 时间:" + yesterday + " ~ " + today + " \n" + - "> ###### 通过数:" + passCount + " \n" + - "> ###### 异常数:" + warnColorString + " \n" + - "> ###### 失败数:" + failColorString + " \n" + - "> ###### 测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "% \n" + - "> ###### 详细统计:[点击查看](" + clientHost + "/Home/" + projectId + ")"); - markdown.put("title", "Sonic云真机测试平台日报"); - jsonObject.put("msgtype", "markdown"); - jsonObject.put("markdown", markdown); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultTestSuiteTemplate() { + return templateTestSuiteMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param errorType errorType - * @param tem 温度 - * @param udId 设备Id - * @return void - * @author ZhouYiXun - * @des 发送设备错误讯息 - * @date 2021/8/20 18:42 - */ @Override - public void sendErrorDevice(RestTemplate restTemplate, String token, String secret, int errorType, int tem, String udId) { - JSONObject jsonObject = new JSONObject(); - JSONObject markdown = new JSONObject(); - if (errorType == 1) { - markdown.put("text", "### Sonic设备高温预警 \n" + - "> ###### 设备序列号:" + udId + " \n" + - "> ###### 电池温度:" + (tem / 10) + " ℃"); - } else { - markdown.put("text", "### Sonic设备高温超时,已关机! \n" + - "> ###### 设备序列号:" + udId + " \n" + - "> ###### 电池温度:" + (tem / 10) + " ℃"); - } - markdown.put("title", "设备温度异常通知"); - jsonObject.put("msgtype", "markdown"); - jsonObject.put("markdown", markdown); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultProjectSummaryTemplate() { + return templateProjectSummaryMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @param passCount 通过数量 - * @param warnCount 警告数量 - * @param failCount 失败数量 - * @param count 测试数量 - * @return void - * @author ZhouYiXun - * @des 发送周报 - * @date 2021/8/20 18:42 - */ @Override - public void sendWeekReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount, int count) { - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - JSONObject markdown = new JSONObject(); - //根据三个数量来决定markdown的字体颜色 - String failColorString; - if (failCount == 0) { - failColorString = "" + failCount + ""; - } else { - failColorString = "" + failCount + ""; - } - String warnColorString; - if (warnCount == 0) { - warnColorString = "" + warnCount + ""; - } else { - warnColorString = "" + warnCount + ""; - } - markdown.put("text", "### Sonic云真机测试平台周报 \n" + - "> ###### 项目:" + projectName + " \n" + - "> ###### 时间:" + yesterday + " ~ " + today + " \n" + - "> ###### 共测试:" + count + " 次\n" + - "> ###### 通过数:" + passCount + " \n" + - "> ###### 异常数:" + warnColorString + " \n" + - "> ###### 失败数:" + failColorString + " \n" + - "> ###### 测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "% \n" + - "> ###### 详细统计:[点击查看](" + clientHost + "/Home/" + projectId + ")"); - markdown.put("title", "Sonic云真机测试平台周报"); - jsonObject.put("msgtype", "markdown"); - jsonObject.put("markdown", markdown); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultDeviceMessageTemplate() { + return templateDeviceMessage; } + } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/FeiShuImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/FeiShuImpl.java index 832392c7..15fbc030 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/FeiShuImpl.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/FeiShuImpl.java @@ -19,20 +19,19 @@ import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.tools.robot.Message; import org.cloud.sonic.controller.tools.robot.RobotMessenger; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.expression.Expression; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.util.Base64Utils; import org.springframework.util.StringUtils; import org.springframework.web.client.RestTemplate; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Base64; import java.util.List; /** @@ -44,21 +43,33 @@ @Service("FeiShuImpl") public class FeiShuImpl implements RobotMessenger { - //从配置文件获取前端部署的host - @Value("${robot.client.host}") - private String clientHost; + Expression templateTestSuiteMessage = RobotMessenger.parseTemplate(""" + **测试套件: ${suiteName} 运行完毕!** + 通过数:${pass} + 异常数:${warn} + 失败数:${fail} + 测试报告:[点击查看](${url})"""); + Expression templateProjectSummaryMessage = RobotMessenger.parseTemplate(""" + **Sonic云真机测试平台${isWeekly ? '周': '日'}报** + 项目:${projectName} + 时间:${getFormat().format(startDate)} ~ ${getFormat().format(endDate)} + 共测试:${total}次 + 通过数:${pass} + 异常数:${warn} + 失败数:${fail} + 测试通过率:${rate}% + 详细统计:[点击查看](${url})"""); + Expression templateDeviceMessage = RobotMessenger.parseTemplate(""" + **Sonic设备高温${errorType == 1 ? '预警' : '超时,已关机!'}** + 设备序列号:${udId} + 电池温度:${tem}℃"""); /** * @param restTemplate RestTemplate * @param token 机器人token - * @param secret 机器人密钥 * @param jsonObject 通知内容 - * @author ZhouYiXun - * @des 飞书群机签名方法 - * @date 2021/8/20 18:20 */ private void signAndSend(RestTemplate restTemplate, String token, String secret, JSONObject jsonObject) { - clientHost = clientHost.replace(":80/", "/"); try { if (!StringUtils.isEmpty(secret)) { String timestamp = String.valueOf(System.currentTimeMillis()).substring(0, 10); @@ -66,35 +77,20 @@ private void signAndSend(RestTemplate restTemplate, String token, String secret, Mac mac = Mac.getInstance("HmacSHA256"); mac.init(new SecretKeySpec(stringToSign.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); byte[] signData = mac.doFinal(new byte[]{}); - String sign = new String(Base64Utils.encode(signData)); + String sign = new String(Base64.getEncoder().encode(signData)); jsonObject.put("timestamp", timestamp); jsonObject.put("sign", sign); } - ResponseEntity responseEntity = - restTemplate.postForEntity(token, jsonObject, JSONObject.class); + ResponseEntity responseEntity = restTemplate.postForEntity(token, jsonObject, JSONObject.class); log.info("robot result: " + responseEntity.getBody()); } catch (Exception e) { log.warn("robot send failed, cause: " + e.getMessage()); } } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param suiteName 套件名称 - * @param pass 通过数量 - * @param warn 警告数量 - * @param fail 失败数量 - * @param projectId 项目id - * @param resultId 结果id - * @return void - * @author ZhouYiXun - * @des 发送每次测试结果到飞书群 - * @date 2021/8/20 18:29 - */ @Override - public void sendResultFinishReport(RestTemplate restTemplate, String token, String secret, String suiteName, int pass, int warn, int fail, int projectId, int resultId) { + public void sendMessage(RestTemplate restTemplate, String token, String secret, Expression messageTemplate, Message msg) { + String content = messageTemplate.getValue(ctx, msg, String.class); JSONObject jsonObject = new JSONObject(); jsonObject.put("msg_type", "interactive"); JSONObject card = new JSONObject(); @@ -104,138 +100,26 @@ public void sendResultFinishReport(RestTemplate restTemplate, String token, Stri JSONObject element = new JSONObject(); element.put("tag", "markdown"); List elementList = new ArrayList<>(); - element.put("content", "**测试套件: " + suiteName + " 运行完毕!**\n" + - "通过数:" + pass + " \n" + - "异常数:" + warn + " \n" + - "失败数:" + fail + "\n" + - "测试报告:[点击查看](" + clientHost + "/Home/" + projectId + "/ResultDetail/" + resultId + ")"); + element.put("content", content); elementList.add(element); card.put("elements", elementList); jsonObject.put("card", card); this.signAndSend(restTemplate, token, secret, jsonObject); } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @return void - * @author ZhouYiXun - * @des 发送日报 - * @date 2021/8/20 18:42 - */ @Override - public void sendDayReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount) { - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - jsonObject.put("msg_type", "interactive"); - JSONObject card = new JSONObject(); - JSONObject config = new JSONObject(); - config.put("wide_screen_mode", true); - card.put("config", config); - JSONObject element = new JSONObject(); - element.put("tag", "markdown"); - List elementList = new ArrayList<>(); - element.put("content", "**Sonic云真机测试平台日报**\n" + - "项目:" + projectName + " \n" + - "时间:" + yesterday + " ~ " + today + " \n" + - "通过数:" + passCount + "\n" + - "异常数:" + warnCount + " \n" + - "失败数:" + failCount + " \n" + - "测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "% \n" + - "详细统计:[点击查看](" + clientHost + "/Home/" + projectId + ")"); - elementList.add(element); - card.put("elements", elementList); - jsonObject.put("card", card); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultTestSuiteTemplate() { + return templateTestSuiteMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param errorType errorType - * @param tem 温度 - * @param udId 设备Id - * @return void - * @author ZhouYiXun - * @des 发送设备错误讯息 - * @date 2021/8/20 18:42 - */ @Override - public void sendErrorDevice(RestTemplate restTemplate, String token, String secret, int errorType, int tem, String udId) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("msg_type", "interactive"); - JSONObject card = new JSONObject(); - JSONObject config = new JSONObject(); - config.put("wide_screen_mode", true); - card.put("config", config); - JSONObject element = new JSONObject(); - element.put("tag", "markdown"); - List elementList = new ArrayList<>(); - - if (errorType == 1) { - element.put("content", "**Sonic设备高温预警** \n" + - "设备序列号:" + udId + " \n" + - "电池温度:" + (tem / 10) + " ℃"); - } else { - element.put("content", "**Sonic设备高温超时,已关机!** \n" + - "设备序列号:" + udId + " \n" + - "电池温度:" + (tem / 10) + " ℃"); - } - elementList.add(element); - card.put("elements", elementList); - jsonObject.put("card", card); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultProjectSummaryTemplate() { + return templateProjectSummaryMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @param passCount 通过数量 - * @param warnCount 警告数量 - * @param failCount 失败数量 - * @param count 测试数量 - * @return void - * @author ZhouYiXun - * @des 发送周报 - * @date 2021/8/20 18:42 - */ @Override - public void sendWeekReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount, int count) { - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - jsonObject.put("msg_type", "interactive"); - JSONObject card = new JSONObject(); - JSONObject config = new JSONObject(); - config.put("wide_screen_mode", true); - card.put("config", config); - JSONObject element = new JSONObject(); - element.put("tag", "markdown"); - List elementList = new ArrayList<>(); - element.put("content", "**Sonic云真机测试平台周报**\n" + - "项目:" + projectName + " \n" + - "时间:" + yesterday + " ~ " + today + " \n" + - "共测试:" + count + " 次\n" + - "通过数:" + passCount + " \n" + - "异常数:" + warnCount + " \n" + - "失败数:" + failCount + " \n" + - "测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "% \n" + - "详细统计:[点击查看](" + clientHost + "/Home/" + projectId + ")"); - elementList.add(element); - card.put("elements", elementList); - jsonObject.put("card", card); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultDeviceMessageTemplate() { + return templateDeviceMessage; } + } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/LineNotifyImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/LineNotifyImpl.java index 734ad777..be35f31e 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/LineNotifyImpl.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/LineNotifyImpl.java @@ -18,8 +18,9 @@ package org.cloud.sonic.controller.tools.robot.vendor; import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.tools.robot.Message; import org.cloud.sonic.controller.tools.robot.RobotMessenger; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.expression.Expression; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; @@ -29,9 +30,6 @@ import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; -import java.math.BigDecimal; -import java.math.RoundingMode; - /** * @author ayumi760405 * @des Line Notify 推送实作类,可以参考 https://notify-bot.line.me/doc/en/ @@ -41,12 +39,34 @@ @Service("LineNotifyImpl") public class LineNotifyImpl implements RobotMessenger { - //从配置文件获取前端部署的host - @Value("${robot.client.host}") - private String clientHost; //line notify的host private static final String LINE_NOTIFY_HOST = "https://notify-api.line.me/api/notify"; + Expression templateTestSuiteMessage = RobotMessenger.parseTemplate(""" + *Sonic云真机测试报告* + 测试套件: #{suiteName} 运行完毕! + 通过数:#{pass} + 异常数:#{warn>0?' `'+warn+'`':warn} + 失败数:#{fail>0?' `'+fail+'`':fail} + 测试报告: #{url} + """); + Expression templateProjectSummaryMessage = RobotMessenger.parseTemplate(""" + *Sonic云真机测试平台#{isWeekly ? '周': '日'}报* + 项目:#{projectName} + 时间:#{getFormat().format(startDate)} ~ #{getFormat().format(endDate)} + 共测试:#{total}次 + 通过数:#{pass} + 异常数:#{warn>0?' `'+warn+'`':warn} + 失败数:#{fail>0?' `'+fail+'`':fail} + 测试通过率:#{rate}% + 详细统计:#{url} + """); + Expression templateDeviceMessage = RobotMessenger.parseTemplate(""" + *设备温度异常通知* + Sonic设备高温#{errorType == 1 ? '预警' : '超时,已关机!'} + 设备序列号:#{udId} + 电池温度:#{tem}℃"""); + /** * @param restTemplate RestTemplate * @param token 机器人token @@ -56,7 +76,6 @@ public class LineNotifyImpl implements RobotMessenger { * @date 2022/12/29 */ private void signAndSend(RestTemplate restTemplate, String token, String message) { - clientHost = clientHost.replace(":80/", "/"); try { HttpHeaders headers = new HttpHeaders(); headers.add("Authorization", "Bearer " + token); @@ -64,168 +83,32 @@ private void signAndSend(RestTemplate restTemplate, String token, String message MultiValueMap requestMap = new LinkedMultiValueMap(); requestMap.add("message", message); HttpEntity> entity = new HttpEntity>(requestMap, headers); - ResponseEntity responseEntity = - restTemplate.postForEntity(LINE_NOTIFY_HOST, entity, String.class); + ResponseEntity responseEntity = restTemplate.postForEntity(LINE_NOTIFY_HOST, entity, String.class); log.info("robot result: " + responseEntity.getBody()); } catch (Exception e) { log.warn("robot send failed, cause: " + e.getMessage()); } } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param suiteName 套件名称 - * @param pass 通过数量 - * @param warnCount 警告数量 - * @param failCount 失败数量 - * @param projectId 项目id - * @param resultId 结果id - * @return void - * @author ayumi760405 - * @des 发送每次测试结果到Line Notify - * @date 2022/12/29 - */ @Override - public void sendResultFinishReport(RestTemplate restTemplate, String token, String secret, String suiteName, int pass, int warnCount, int failCount, int projectId, int resultId) { - String failColorString; - if (failCount == 0) { - failColorString = String.valueOf(failCount); - } else { - failColorString = " `" + failCount + "`"; - } - String warnColorString; - if (warnCount == 0) { - warnColorString = String.valueOf(warnCount); - } else { - warnColorString = " `" + warnCount + "`"; - } - StringBuilder builder = new StringBuilder(); - builder.append("\n"); - builder.append("*Sonic云真机测试报告*").append("\n"); - builder.append("测试套件: ").append(suiteName).append(" 运行完毕!").append("\n"); - builder.append("通过数:").append(pass).append("\n"); - builder.append("异常数:").append(warnColorString).append("\n"); - builder.append("失败数:").append(failColorString).append("\n"); - builder.append("测试报告:").append(clientHost).append("/Home/").append(projectId).append("/ResultDetail/").append(resultId); - this.signAndSend(restTemplate, token, builder.toString()); + public void sendMessage(RestTemplate restTemplate, String token, String secret, Expression messageTemplate, Message msg) { + String content = messageTemplate.getValue(ctx, msg, String.class); + signAndSend(restTemplate, token, content); } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @return void - * @author ayumi760405 - * @des 发送日报 - * @date 2022/12/29 - */ @Override - public void sendDayReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount) { - int total = passCount + warnCount + failCount; - String failColorString; - if (failCount == 0) { - failColorString = String.valueOf(failCount); - } else { - failColorString = " `" + failCount + "`"; - } - String warnColorString; - if (warnCount == 0) { - warnColorString = String.valueOf(warnCount); - } else { - warnColorString = " `" + warnCount + "`"; - } - - StringBuilder builder = new StringBuilder(); - builder.append("\n"); - builder.append("*Sonic云真机测试平台日报*").append("\n"); - builder.append("项目:").append(projectName).append("\n"); - builder.append("时间:").append(yesterday).append(" ~ ").append(today).append("\n"); - builder.append("通过数:").append(passCount).append("\n"); - builder.append("异常数:").append(warnColorString).append("\n"); - builder.append("失败数:").append(failColorString).append("\n"); - builder.append("测试通过率: ").append((total > 0 ? - BigDecimal.valueOf(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0)).append("%").append("\n"); - builder.append("测试报告:").append(clientHost).append("/Home/").append(projectId); - this.signAndSend(restTemplate, token, builder.toString()); + public Expression getDefaultTestSuiteTemplate() { + return templateTestSuiteMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param errorType errorType - * @param tem 温度 - * @param udId 设备Id - * @return void - * @author ayumi760405 - * @des 发送设备错误讯息 - * @date 2022/12/29 - */ @Override - public void sendErrorDevice(RestTemplate restTemplate, String token, String secret, int errorType, int tem, String udId) { - StringBuilder builder = new StringBuilder(); - builder.append("\n"); - builder.append("*设备温度异常通知*").append("\n"); - if (errorType == 1) { - builder.append("Sonic设备高温预警").append("\n"); - } else { - builder.append("Sonic设备高温超时,已关机").append("\n"); - } - builder.append("设备序列号:").append(udId).append("\n"); - builder.append("电池温度:").append(tem / 10).append(" ℃").append("\n"); - this.signAndSend(restTemplate, token, builder.toString()); + public Expression getDefaultProjectSummaryTemplate() { + return templateProjectSummaryMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @param passCount 通过数量 - * @param warnCount 警告数量 - * @param failCount 失败数量 - * @param count 测试数量 - * @return void - * @author ayumi760405 - * @des 发送周报 - * @date 2022/12/29 - */ @Override - public void sendWeekReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount, int count) { - int total = passCount + warnCount + failCount; - String failColorString; - if (failCount == 0) { - failColorString = String.valueOf(failCount); - } else { - failColorString = " `" + failCount + "`"; - } - String warnColorString; - if (warnCount == 0) { - warnColorString = String.valueOf(warnCount); - } else { - warnColorString = " `" + warnCount + "`"; - } - StringBuilder builder = new StringBuilder(); - builder.append("\n"); - builder.append("*Sonic云真机测试平台周报*").append("\n"); - builder.append("项目:").append(projectName).append("\n"); - builder.append("时间:").append(yesterday).append(" ~ ").append(today).append("\n"); - builder.append("共测试:").append(count).append("次").append("\n"); - builder.append("通过数:").append(passCount).append("\n"); - builder.append("异常数:").append(warnColorString).append("\n"); - builder.append("失败数:").append(failColorString).append("\n"); - builder.append("测试通过率: ").append((total > 0 ? - BigDecimal.valueOf(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0)).append("%").append("\n"); - builder.append("测试报告:").append(clientHost).append("/Home/").append(projectId); - this.signAndSend(restTemplate, token, builder.toString()); + public Expression getDefaultDeviceMessageTemplate() { + return templateDeviceMessage; } + } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/SlackNotifyImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/SlackNotifyImpl.java index 4620a4fd..5343bfab 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/SlackNotifyImpl.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/SlackNotifyImpl.java @@ -17,18 +17,14 @@ */ package org.cloud.sonic.controller.tools.robot.vendor; -import com.alibaba.fastjson.JSONArray; -import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.tools.robot.Message; import org.cloud.sonic.controller.tools.robot.RobotMessenger; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.expression.Expression; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.LinkedHashMap; import java.util.Map; @@ -41,212 +37,99 @@ @Service("SlackNotifyImpl") public class SlackNotifyImpl implements RobotMessenger { - //从配置文件获取前端部署的host - @Value("${robot.client.host}") - private String clientHost; + Expression templateTestSuiteMessage = RobotMessenger.parseTemplate(""" + #{ + { + text: '测试套件:' + suiteName + '运行完毕!', + blocks: { + { type: 'section', + text: { + type: 'mrkdwn', + text: '*测试套件:' + suiteName + '运行完毕!* + 通过数:'+pass+' + 异常数:'+warn+' + 失败数:'+fail+' + 测试报告:'+url + } + } + } + } + }"""); + Expression templateProjectSummaryMessage = RobotMessenger.parseTemplate(""" + #{ + { + text: 'Sonic云真机测试平台'+(isWeekly?'周':'日')+'报', + blocks: { + { type: 'section', + text: { + type: 'mrkdwn', + text: '*Sonic云真机测试平台'+(isWeekly ? '周': '日')+'报* + 项目:'+projectName+' + 时间:'+getFormat().format(startDate)+' ~ '+getFormat().format(endDate)+' + 共测试:'+total+'次 + 通过数:'+pass+' + 异常数:'+warn+' + 失败数:'+fail+' + 测试通过率:'+rate+'% + 详细统计:'+url + } + } + } + } + }"""); + Expression templateDeviceMessage = RobotMessenger.parseTemplate(""" + #{ + { + text: '设备温度异常通知', + blocks: { + { type: 'section', + text: { + type: 'mrkdwn', + text: '*Sonic设备高温'+(errorType == 1 ? '预警' : '超时,已关机!')+'* + 设备序列号:'+udId+' + 电池温度:'+tem+'℃' + } + } + } + } + }"""); /** * @param restTemplate RestTemplate * @param token 机器人token * @param secret 机器人密钥 - * @param jsonObject 通知内容 + * @param content 通知内容 * @author young(stephenwang1011) * @des SLACK发送测试通知 * @date 2023/2/14 */ - private void signAndSend(RestTemplate restTemplate, String token, String secret, JSONObject jsonObject) { - clientHost = clientHost.replace(":80/", "/"); + private void signAndSend(RestTemplate restTemplate, String token, String secret, Object content) { try { - ResponseEntity responseEntity = - restTemplate.postForEntity(token, jsonObject, JSONObject.class); + ResponseEntity responseEntity = restTemplate.postForEntity(token, content, String.class); log.info("robot result: " + responseEntity.getBody()); } catch (Exception e) { log.warn("robot send failed, cause: " + e.getMessage()); } } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param suiteName 套件名称 - * @param passCount 通过数量 - * @param warnCount 警告数量 - * @param failCount 失败数量 - * @param projectId 项目id - * @param resultId 结果id - * @return void - * @author young(stephenwang1011) - * @des 发送每次测试结果到SLACK - * @date 2023/2/14 - */ @Override - public void sendResultFinishReport(RestTemplate restTemplate, String token, String secret, String suiteName, int passCount, int warnCount, int failCount, int projectId, int resultId) { - JSONObject slackObjs = new JSONObject(true); - - String reportLink = clientHost + "/Home/" + projectId + "/ResultDetail/" + resultId; - SlackMessage slackMessage = new SlackMessage(); - slackMessage.setType("mrkdwn"); - slackMessage.setText("*测试套件:" + suiteName + " 运行完毕* \n" + - "通过数:" + passCount + "\n" + - "异常数:" + warnCount + "\n" + - "失败数:" + failCount + "\n" + - "测试报告:" + reportLink); - - this.slackMessage(slackMessage, restTemplate, token, secret, slackObjs, suiteName); + public void sendMessage(RestTemplate restTemplate, String token, String secret, Expression messageTemplate, Message msg) { + Map content = messageTemplate.getValue(ctx, msg, Map.class); + signAndSend(restTemplate, token, secret, content); } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @return void - * @author young(stephenwang1011) - * @des 通过slack发送day report - * @date 2022/12/20 - */ @Override - public void sendDayReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount) { - JSONObject slackObjs = new JSONObject(true); - - int total = passCount + warnCount + failCount; - String reportLink = clientHost + "/Home/" + projectId; - - SlackMessage slackMessage = new SlackMessage(); - slackMessage.setType("mrkdwn"); - slackMessage.setText("*Sonic云真机测试平台日报* \n" + - "项目:" + projectName + "\n" + - "时间:" + yesterday + " - " + today + "\n" + - "通过数:" + passCount + "\n" + - "异常数:" + warnCount + "\n" + - "失败数:" + failCount + "\n" + - "测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "%\n" + - "详细统计:" + reportLink); - - this.slackMessage(slackMessage, restTemplate, token, secret, slackObjs, projectName); - + public Expression getDefaultTestSuiteTemplate() { + return templateTestSuiteMessage; } - - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param errorType errorType - * @param tem 温度 - * @param udId 设备Id - * @return void - * @author young(stephenwang1011) - * @des 向slack发送设备错误讯息 - * @date 2022/12/20 - */ @Override - public void sendErrorDevice(RestTemplate restTemplate, String token, String secret, int errorType, int tem, String udId) { - - JSONObject slackObjs = new JSONObject(true); - SlackMessage slackMessage = new SlackMessage(); - slackMessage.setType("mrkdwn"); - - if (errorType == 1) { - slackMessage.setText("*Sonic设备高温预警* \n" + - "设备序列号:" + udId + "\n" + - "电池温度:" + (tem / 10) + "℃"); - - //这个是slack消息的通知title - slackObjs.put("text", "Sonic设备:" + udId + "温度过高!"); - } else { - slackMessage.setText("*Sonic设备高温超时,已关机* \n" + - "设备序列号:" + udId + "\n" + - "电池温度:" + (tem / 10) + "℃"); - - //这个是slack消息的通知title - slackObjs.put("text", "Sonic设备:" + udId + "高温超时,已关机"); - } - - this.slackMessage(slackMessage, restTemplate, token, secret, slackObjs, udId); + public Expression getDefaultProjectSummaryTemplate() { + return templateProjectSummaryMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @param passCount 通过数量 - * @param warnCount 警告数量 - * @param failCount 失败数量 - * @param count 测试数量 - * @return void - * @author young(stephenwang1011) - * @des 发送周报到slack - * @date 2022/12/20 - */ @Override - public void sendWeekReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount, int count) { - JSONObject slackObjs = new JSONObject(true); - int total = passCount + warnCount + failCount; - String reportLink = clientHost + "/Home/" + projectId; - - SlackMessage slackMessage = new SlackMessage(); - slackMessage.setType("mrkdwn"); - slackMessage.setText("*Sonic云真机测试平台周报* \n" + - "项目:" + projectName + "\n" + - "时间:" + yesterday + " - " + today + "\n" + - "共测试:" + count + "次" + "\n" + - "通过数:" + passCount + "\n" + - "异常数:" + warnCount + "\n" + - "失败数:" + failCount + "\n" + - "测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "%\n" + - "详细统计:" + reportLink); - - this.slackMessage(slackMessage, restTemplate, token, secret, slackObjs, projectName); + public Expression getDefaultDeviceMessageTemplate() { + return templateDeviceMessage; } - - public void slackMessage(SlackMessage slackMessage, RestTemplate restTemplate, String token, String secret, JSONObject slackObjs, String name) { - - Map slackMap = new LinkedHashMap<>(); - slackMap.put("type", "section"); - slackMap.put("text", slackMessage); - JSONArray blocksArray = new JSONArray(); - blocksArray.add(slackMap); - - //这个是slack消息的通知title - slackObjs.put("text", name + "测试完成,请查看!"); - //这个是slack消息的具体内容 - slackObjs.put("blocks", blocksArray); - - this.signAndSend(restTemplate, token, secret, slackObjs); - } - - - static class SlackMessage { - private String type; - private String text; - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - - } - } \ No newline at end of file diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/TelegramImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/TelegramImpl.java index c2539679..d2da067c 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/TelegramImpl.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/TelegramImpl.java @@ -19,15 +19,13 @@ import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.tools.robot.Message; import org.cloud.sonic.controller.tools.robot.RobotMessenger; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.expression.Expression; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import java.math.BigDecimal; -import java.math.RoundingMode; - /** * @author ayumi760405 * @des Telegram电报机器人推送实作类,可以参考 https://core.telegram.org/bots/api#sendmessage @@ -37,155 +35,66 @@ @Service("TelegramImpl") public class TelegramImpl implements RobotMessenger { - //从配置文件获取前端部署的host - @Value("${robot.client.host}") - private String clientHost; + Expression templateTestSuiteMessage = RobotMessenger.parseTemplate(""" + 测试套件: #{suiteName} 运行完毕! + 通过数:#{pass} + 异常数:#{warn} + 失败数:#{fail} + 测试报告:点击查看"""); + Expression templateProjectSummaryMessage = RobotMessenger.parseTemplate(""" + Sonic云真机测试平台#{isWeekly ? '周': '日'}报 + 项目:#{projectName} + 时间:#{getFormat().format(startDate)} ~ #{getFormat().format(endDate)} + 共测试:#{total}次 + 通过数:#{pass} + 异常数:#{warn} + 失败数:#{fail} + 测试通过率:#{rate}% + 详细统计:点击查看"""); + Expression templateDeviceMessage = RobotMessenger.parseTemplate(""" + Sonic设备高温#{errorType == 1 ? '预警' : '超时,已关机!'} + 设备序列号:#{udId} + 电池温度:#{tem}" ℃"""); /** * @param restTemplate RestTemplate * @param token 机器人token - * @param secret 机器人密钥 - * @param jsonObject 通知内容 + * @param content 通知内容 * @author ayumi760405 * @des Telegram电报机器人传送讯息方法 * @date 2022/12/20 */ - private void signAndSend(RestTemplate restTemplate, String token, String secret, JSONObject jsonObject) { - clientHost = clientHost.replace(":80/", "/"); + private void signAndSend(RestTemplate restTemplate, String token, String content) { try { - ResponseEntity responseEntity = - restTemplate.postForEntity(token, jsonObject, JSONObject.class); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("parse_mode", "html"); + jsonObject.put("text", content); + ResponseEntity responseEntity = restTemplate.postForEntity(token, jsonObject, JSONObject.class); log.info("robot result: " + responseEntity.getBody()); } catch (Exception e) { log.warn("robot send failed, cause: " + e.getMessage()); } } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param suiteName 套件名称 - * @param pass 通过数量 - * @param warn 警告数量 - * @param fail 失败数量 - * @param projectId 项目id - * @param resultId 结果id - * @return void - * @author ayumi760405 - * @des 发送每次测试结果到Telegram - * @date 2022/12/20 - */ @Override - public void sendResultFinishReport(RestTemplate restTemplate, String token, String secret, String suiteName, int pass, int warn, int fail, int projectId, int resultId) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("parse_mode", "html"); - String reportLink = clientHost + "/Home/" + projectId + "/ResultDetail/" + resultId; - jsonObject.put("text", - "测试套件: " + suiteName + " 运行完毕!\n" + - "通过数:" + pass + "\n" + - "异常数:" + warn + "\n" + - "失败数:" + fail + "\n" + - "测试报告:" + reportLink + "\n"); - this.signAndSend(restTemplate, token, secret, jsonObject); + public void sendMessage(RestTemplate restTemplate, String token, String secret, Expression messageTemplate, Message msg) { + String content = messageTemplate.getValue(ctx, msg, String.class); + this.signAndSend(restTemplate, token, content); } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @return void - * @author ayumi760405 - * @des 发送日报 - * @date 2022/12/20 - */ @Override - public void sendDayReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount) { - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - String warnColorString; - warnColorString = "" + warnCount + ""; - String failColorString; - failColorString = "" + failCount + ""; - jsonObject.put("parse_mode", "html"); - String reportLink = clientHost + "/Home/" + projectId; - jsonObject.put("text", " Sonic云真机测试平台日报 \n" + - "项目:" + projectName + "\n" + - "时间:" + yesterday + " ~ " + today + "\n" + - "通过数:" + passCount + "\n" + - "异常数:" + warnColorString + "\n" + - "失败数:" + failColorString + "\n" + - " 测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "% \n" + - "详细统计:" + reportLink + "\n"); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultTestSuiteTemplate() { + return templateTestSuiteMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param errorType errorType - * @param tem 温度 - * @param udId 设备Id - * @return void - * @author ayumi760405 - * @des 发送设备错误讯息 - * @date 2022/12/20 - */ @Override - public void sendErrorDevice(RestTemplate restTemplate, String token, String secret, int errorType, int tem, String udId) { - JSONObject jsonObject = new JSONObject(); - if (errorType == 1) { - jsonObject.put("text", " Sonic设备高温预警 \n" + - " 设备序列号:" + udId + " \n" + - " 电池温度:" + (tem / 10) + " ℃"); - } else { - jsonObject.put("text", " Sonic设备高温超时,已关机! \n" + - " 设备序列号:" + udId + " \n" + - " ###### 电池温度:" + (tem / 10) + " ℃"); - } - jsonObject.put("parse_mode", "html"); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultProjectSummaryTemplate() { + return templateProjectSummaryMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @param passCount 通过数量 - * @param warnCount 警告数量 - * @param failCount 失败数量 - * @param count 测试数量 - * @return void - * @author ayumi760405 - * @des 发送周报 - * @date 2022/12/20 - */ @Override - public void sendWeekReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount, int count) { - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - String reportLink = clientHost + "/Home/" + projectId; - jsonObject.put("parse_mode", "html"); - jsonObject.put("text", " Sonic云真机测试平台周报 \n" + - " 项目:" + projectName + " \n" + - " 时间:" + yesterday + " ~ " + today + " \n" + - " 共测试:" + count + " 次\n" + - " 通过数:" + passCount + " \n" + - " 异常数:" + warnCount + " \n" + - " 失败数:" + failCount + " \n" + - " 测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "% \n" + - "详细统计:" + reportLink + "\n"); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultDeviceMessageTemplate() { + return templateDeviceMessage; } + } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/WeChatImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/WeChatImpl.java index 31b770fc..1cf2c83b 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/WeChatImpl.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/WeChatImpl.java @@ -19,17 +19,13 @@ import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.tools.robot.Message; import org.cloud.sonic.controller.tools.robot.RobotMessenger; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.expression.Expression; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; -import java.math.BigDecimal; -import java.math.RoundingMode; - /** * @author ayumi760405 * @des 企业微信机器人推送实作类 @@ -39,186 +35,68 @@ @Service("WeChatImpl") public class WeChatImpl implements RobotMessenger { - private final Logger logger = LoggerFactory.getLogger(DingTalkImpl.class); - - //从配置文件获取前端部署的host - @Value("${robot.client.host}") - private String clientHost; + Expression templateTestSuiteMessage = RobotMessenger.parseTemplate(""" + **测试套件: #{suiteName} 运行完毕!** + 通过数:#{pass} + 异常数:#{warn} + 失败数:#{fail} + 测试报告:[点击查看](#{url})"""); + Expression templateProjectSummaryMessage = RobotMessenger.parseTemplate(""" + ### Sonic云真机测试平台#{isWeekly ? '周': '日'}报 + > ###### 项目:#{projectName} + > ###### 时间:#{getFormat().format(startDate)} ~ #{getFormat().format(endDate)} + > ###### 共测试:#{total}次 + > ###### 通过数:#{pass} + > ###### 异常数:#{warn} + > ###### 失败数:#{fail} + > ###### 测试通过率:#{rate}% + > ###### 详细统计:[点击查看](#{url})"""); + Expression templateDeviceMessage = RobotMessenger.parseTemplate(""" + ### Sonic设备高温#{errorType == 1 ? '预警' : '超时,已关机!'} + > ###### 设备序列号:#{udId} + > ###### 电池温度:#{tem}℃"""); /** * @param restTemplate RestTemplate * @param token 机器人token - * @param secret 机器人密钥 - * @param jsonObject 通知内容 + * @param content 通知内容 * @author ZhouYiXun * @des 企业微信机器人传送讯息方法 * @date 2021/8/20 18:20 */ - private void signAndSend(RestTemplate restTemplate, String token, String secret, JSONObject jsonObject) { - clientHost = clientHost.replace(":80/", "/"); + private void signAndSend(RestTemplate restTemplate, String token, String content) { try { - ResponseEntity responseEntity = - restTemplate.postForEntity(token, jsonObject, JSONObject.class); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("msgtype", "markdown"); + JSONObject markdown = new JSONObject(); + markdown.put("content", content); + jsonObject.put("markdown", markdown); + ResponseEntity responseEntity = restTemplate.postForEntity(token, jsonObject, JSONObject.class); log.info("robot result: " + responseEntity.getBody()); } catch (Exception e) { log.warn("robot send failed, cause: " + e.getMessage()); } } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param suiteName 套件名称 - * @param pass 通过数量 - * @param warn 警告数量 - * @param fail 失败数量 - * @param projectId 项目id - * @param resultId 结果id - * @return void - * @author ZhouYiXun - * @des 发送每次测试结果到企业微信 - * @date 2021/8/20 18:29 - */ @Override - public void sendResultFinishReport(RestTemplate restTemplate, String token, String secret, String suiteName, int pass, int warn, int fail, int projectId, int resultId) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("msgtype", "markdown"); - JSONObject markdown = new JSONObject(); - markdown.put("content", "**测试套件: " + suiteName + " 运行完毕!**\n" + - "通过数:" + pass + " \n" + - "异常数:" + warn + " \n" + - "失败数:" + fail + "\n" + - "测试报告:[点击查看](" + clientHost + "/Home/" + projectId + "/ResultDetail/" + resultId + ")"); - jsonObject.put("markdown", markdown); - this.signAndSend(restTemplate, token, secret, jsonObject); + public void sendMessage(RestTemplate restTemplate, String token, String secret, Expression messageTemplate, Message msg) { + String content = messageTemplate.getValue(ctx, msg, String.class); + this.signAndSend(restTemplate, token, content); } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @return void - * @author ZhouYiXun - * @des 发送日报 - * @date 2021/8/20 18:42 - */ @Override - public void sendDayReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount) { - - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - String warnColorString; - - if (warnCount == 0) { - warnColorString = "" + warnCount + ""; - } else { - warnColorString = "" + warnCount + ""; - } - - String failColorString; - - if (failCount == 0) { - failColorString = "" + failCount + ""; - } else { - failColorString = "" + failCount + ""; - } - - jsonObject.put("msgtype", "markdown"); - JSONObject markdown = new JSONObject(); - markdown.put("content", "### Sonic云真机测试平台日报 \n" + - "> ###### 项目:" + projectName + " \n" + - "> ###### 时间:" + yesterday + " ~ " + today + " \n" + - "> ###### 通过数:" + passCount + " \n" + - "> ###### 异常数:" + warnColorString + " \n" + - "> ###### 失败数:" + failColorString + " \n" + - "> ###### 测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "% \n" + - "> ###### 详细统计:[点击查看](" + clientHost + "/Home/" + projectId + ")"); - jsonObject.put("markdown", markdown); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultTestSuiteTemplate() { + return templateTestSuiteMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param errorType errorType - * @param tem 温度 - * @param udId 设备Id - * @return void - * @author ZhouYiXun - * @des 发送设备错误讯息 - * @date 2021/8/20 18:42 - */ @Override - public void sendErrorDevice(RestTemplate restTemplate, String token, String secret, int errorType, int tem, String udId) { - JSONObject jsonObject = new JSONObject(); - JSONObject markdown = new JSONObject(); - if (errorType == 1) { - markdown.put("content", "### Sonic设备高温预警 \n" + - "> ###### 设备序列号:" + udId + " \n" + - "> ###### 电池温度:" + (tem / 10) + " ℃"); - } else { - markdown.put("content", "### Sonic设备高温超时,已关机! \n" + - "> ###### 设备序列号:" + udId + " \n" + - "> ###### 电池温度:" + (tem / 10) + " ℃"); - } - jsonObject.put("msgtype", "markdown"); - jsonObject.put("markdown", markdown); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultProjectSummaryTemplate() { + return templateProjectSummaryMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @param passCount 通过数量 - * @param warnCount 警告数量 - * @param failCount 失败数量 - * @param count 测试数量 - * @return void - * @author ZhouYiXun - * @des 发送周报 - * @date 2021/8/20 18:42 - */ @Override - public void sendWeekReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount, int count) { - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - String warnColorString; - if (warnCount == 0) { - warnColorString = "" + warnCount + ""; - } else { - warnColorString = "" + warnCount + ""; - } - String failColorString; - if (failCount == 0) { - failColorString = "" + failCount + ""; - } else { - failColorString = "" + failCount + ""; - } - jsonObject.put("msgtype", "markdown"); - JSONObject markdown = new JSONObject(); - markdown.put("content", "### Sonic云真机测试平台周报 \n" + - "> ###### 项目:" + projectName + " \n" + - "> ###### 时间:" + yesterday + " ~ " + today + " \n" + - "> ###### 共测试:" + count + " 次\n" + - "> ###### 通过数:" + passCount + " \n" + - "> ###### 异常数:" + warnColorString + " \n" + - "> ###### 失败数:" + failColorString + " \n" + - "> ###### 测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "% \n" + - "> ###### 详细统计:[点击查看](" + clientHost + "/Home/" + projectId + ")"); - jsonObject.put("markdown", markdown); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultDeviceMessageTemplate() { + return templateDeviceMessage; } + } diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/WebhookImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/WebhookImpl.java new file mode 100644 index 00000000..0b7d9c53 --- /dev/null +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/WebhookImpl.java @@ -0,0 +1,38 @@ +package org.cloud.sonic.controller.tools.robot.vendor; + +import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.tools.robot.Message; +import org.cloud.sonic.controller.tools.robot.RobotMessenger; +import org.springframework.expression.Expression; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +@Slf4j +public class WebhookImpl implements RobotMessenger { + @Override + public void sendMessage(RestTemplate restTemplate, String token, String secret, Expression messageTemplate, Message message) { + if (messageTemplate == null) { + ResponseEntity responseEntity = restTemplate.getForEntity(token, Object.class); + log.info("robot result: " + responseEntity.getBody()); + } else { + Object content = messageTemplate.getValue(ctx); + ResponseEntity responseEntity = restTemplate.postForEntity(token, content, Object.class); + log.info("robot result: " + responseEntity.getBody()); + } + } + + @Override + public Expression getDefaultTestSuiteTemplate() { + return null; + } + + @Override + public Expression getDefaultProjectSummaryTemplate() { + return null; + } + + @Override + public Expression getDefaultDeviceMessageTemplate() { + return null; + } +} diff --git a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/YouSpaceImpl.java b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/YouSpaceImpl.java index f368b6cc..a781cc7f 100644 --- a/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/YouSpaceImpl.java +++ b/sonic-server-controller/src/main/java/org/cloud/sonic/controller/tools/robot/vendor/YouSpaceImpl.java @@ -19,18 +19,16 @@ import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; +import org.cloud.sonic.controller.tools.robot.Message; import org.cloud.sonic.controller.tools.robot.RobotMessenger; -import org.springframework.beans.factory.annotation.Value; +import org.springframework.expression.Expression; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; -import org.springframework.util.Base64Utils; import org.springframework.web.client.RestTemplate; -import java.math.BigDecimal; -import java.math.RoundingMode; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; +import java.util.Base64; +import java.util.Map; /** * @author ayumi760405 @@ -41,209 +39,88 @@ @Service("YouSpaceImpl") public class YouSpaceImpl implements RobotMessenger { - //从配置文件获取前端部署的host - @Value("${robot.client.host}") - private String clientHost; + Expression templateTestSuiteMessage = RobotMessenger.parseTemplate(""" + #{ + { + businessId: '测试套件: ' + suiteName + '运行完毕!', + titleZone: {type: 0, text: '测试套件: ' + suiteName + '运行完毕!'}, + contentZone: { + {type: 'textView', data: { text: '通过数:' + pass, level: 1}}, + {type: 'textView', data: { text: '异常数:' + warn, level: 1}}, + {type: 'textView', data: { text: '失败数:' + fail, level: 1}}, + {type: 'buttonView', data: {mode: 0, text: '点击查看测试报告', url: url}} + } + } + }"""); + Expression templateProjectSummaryMessage = RobotMessenger.parseTemplate(""" + #{ + { + businessId: 'Sonic云真机测试平台'+(isWeekly?'周':'日')+'报', + titleZone: {type: 0, text: 'Sonic云真机测试平台'+(isWeekly?'周':'日')+'报'}, + contentZone: { + {type: 'textView', data: {text: '项目:' + projectName, level: 1}}, + {type: 'textView', data: {text: '时间:' + getFormat().format(startDate) + ' ~ ' + getFormat().format(endDate), level: 1}}, + {type: 'textView', data: {text: '共测试:' + total, level: 1}}, + {type: 'textView', data: {text: '通过数:' + pass, level: 1}}, + {type: 'textView', data: {text: '异常数:' + warn, level: 1}}, + {type: 'textView', data: {text: '失败数:' + fail, level: 1}}, + {type: 'textView', data: {text: '测试通过率:' + rate, level: 1}}, + {type: 'buttonView', data: {mode: 0, text: '点击查看', url: url}} + } + } + }"""); + Expression templateDeviceMessage = RobotMessenger.parseTemplate(""" + #{ + { + businessId: 'Sonic设备高温'+(errorType==1?'预警':'超时,已关机!'), + titleZone: {type: 0, text: 'Sonic设备高温'+(errorType==1?'预警':'超时,已关机!')}, + contentZone: { + {type: 'textView', data: {text: '设备序列号:' + udId, level: 1}}, + {type: 'textView', data: {text: '电池温度:' + tem + ' ℃', level: 1}} + } + } + }"""); /** * @param restTemplate RestTemplate * @param token 机器人token - * @param secret 机器人密钥 * @param jsonObject 通知内容 * @author ZhouYiXun * @des 友空间签名方法 * @date 2021/8/20 18:20 */ - private void signAndSend(RestTemplate restTemplate, String token, String secret, JSONObject jsonObject) { - clientHost = clientHost.replace(":80/", "/"); + private void signAndSend(RestTemplate restTemplate, String token, Map jsonObject) { try { JSONObject you = new JSONObject(); you.put("timestamp", System.currentTimeMillis()); - you.put("content", Base64Utils.encodeToString(jsonObject.toJSONString().getBytes(StandardCharsets.UTF_8))); - ResponseEntity responseEntity = - restTemplate.postForEntity(token - , you, JSONObject.class); + String encoded_content = Base64.getEncoder().encodeToString(JSONObject.toJSONString(jsonObject).getBytes(StandardCharsets.UTF_8)); + you.put("content", encoded_content); + ResponseEntity responseEntity = restTemplate.postForEntity(token, you, JSONObject.class); log.info("robot result: " + responseEntity.getBody()); } catch (Exception e) { log.warn("robot send failed, cause: " + e.getMessage()); } } - /** - * @param s 讯息内容 - * @return JSONObject - * @author ZhouYiXun - * @des 将字串转为友空间格式 - * @date 2021/8/20 18:29 - */ - private JSONObject generateYouTextView(String s) { - JSONObject text = new JSONObject(); - text.put("type", "textView"); - JSONObject data = new JSONObject(); - data.put("text", s); - data.put("level", 1); - text.put("data", data); - return text; - } - - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param suiteName 套件名称 - * @param pass 通过数量 - * @param warn 警告数量 - * @param fail 失败数量 - * @param projectId 项目id - * @param resultId 结果id - * @return void - * @author ZhouYiXun - * @des 发送每次测试结果到友空间 - * @date 2021/8/20 18:29 - */ @Override - public void sendResultFinishReport(RestTemplate restTemplate, String token, String secret, String suiteName, int pass, int warn, int fail, int projectId, int resultId) { - JSONObject jsonObject = new JSONObject(); - jsonObject.put("businessId", "测试套件: " + suiteName + " 运行完毕!"); - JSONObject titleZone = new JSONObject(); - titleZone.put("type", 0); - titleZone.put("text", "测试套件: " + suiteName + " 运行完毕!"); - jsonObject.put("titleZone", titleZone); - List contentZone = new ArrayList<>(); - contentZone.add(generateYouTextView("通过数:" + pass)); - contentZone.add(generateYouTextView("异常数:" + warn)); - contentZone.add(generateYouTextView("失败数:" + fail)); - JSONObject button = new JSONObject(); - button.put("type", "buttonView"); - JSONObject data = new JSONObject(); - data.put("mode", 0); - data.put("text", "点击查看测试报告"); - data.put("url", clientHost + "/Home/" + projectId + "/ResultDetail/" + resultId); - button.put("data", data); - contentZone.add(button); - jsonObject.put("contentZone", contentZone); - this.signAndSend(restTemplate, token, secret, jsonObject); + public void sendMessage(RestTemplate restTemplate, String token, String secret, Expression messageTemplate, Message msg) { + Map json = messageTemplate.getValue(ctx, msg, Map.class); + this.signAndSend(restTemplate, token, json); } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @return void - * @author ZhouYiXun - * @des 发送日报 - * @date 2021/8/20 18:42 - */ @Override - public void sendDayReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount) { - - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - jsonObject.put("businessId", "Sonic云真机测试平台日报"); - JSONObject titleZone = new JSONObject(); - titleZone.put("type", 0); - titleZone.put("text", "Sonic云真机测试平台日报"); - jsonObject.put("titleZone", titleZone); - List contentZone = new ArrayList<>(); - contentZone.add(generateYouTextView("项目:" + projectName)); - contentZone.add(generateYouTextView("时间:" + yesterday + " ~ " + today)); - contentZone.add(generateYouTextView("通过数:" + passCount)); - contentZone.add(generateYouTextView("异常数:" + warnCount)); - contentZone.add(generateYouTextView("失败数:" + failCount)); - contentZone.add(generateYouTextView("测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "%")); - JSONObject button = new JSONObject(); - button.put("type", "buttonView"); - JSONObject data = new JSONObject(); - data.put("mode", 0); - data.put("text", "点击查看"); - data.put("url", clientHost + "/Home/" + projectId); - button.put("data", data); - contentZone.add(button); - jsonObject.put("contentZone", contentZone); - this.signAndSend(restTemplate, token, secret, jsonObject); - + public Expression getDefaultTestSuiteTemplate() { + return templateTestSuiteMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param errorType errorType - * @param tem 温度 - * @param udId 设备Id - * @return void - * @author ZhouYiXun - * @des 发送设备错误讯息 - * @date 2021/8/20 18:42 - */ @Override - public void sendErrorDevice(RestTemplate restTemplate, String token, String secret, int errorType, int tem, String udId) { - JSONObject jsonObject = new JSONObject(); - String t = "Sonic设备高温预警"; - if (errorType != 1) { - t = "Sonic设备高温超时,已关机!"; - } - jsonObject.put("businessId", t); - JSONObject titleZone = new JSONObject(); - titleZone.put("type", 0); - titleZone.put("text", t); - jsonObject.put("titleZone", titleZone); - List contentZone = new ArrayList<>(); - contentZone.add(generateYouTextView("设备序列号:" + udId)); - contentZone.add(generateYouTextView("电池温度:" + (tem / 10) + " ℃")); - jsonObject.put("contentZone", contentZone); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultProjectSummaryTemplate() { + return templateProjectSummaryMessage; } - /** - * @param restTemplate RestTemplate - * @param token 机器人token - * @param secret 机器人密钥 - * @param projectId 项目id - * @param projectName 项目名称 - * @param yesterday 昨天的起始时间 - * @param today 今天的起始时间 - * @param passCount 通过数量 - * @param warnCount 警告数量 - * @param failCount 失败数量 - * @param count 测试数量 - * @return void - * @author ZhouYiXun - * @des 发送周报 - * @date 2021/8/20 18:42 - */ @Override - public void sendWeekReportMessage(RestTemplate restTemplate, String token, String secret, int projectId, String projectName, String yesterday, String today, int passCount, int warnCount, int failCount, int count) { - JSONObject jsonObject = new JSONObject(); - int total = passCount + warnCount + failCount; - jsonObject.put("businessId", "Sonic云真机测试平台周报"); - JSONObject titleZone = new JSONObject(); - titleZone.put("type", 0); - titleZone.put("text", "Sonic云真机测试平台周报"); - jsonObject.put("titleZone", titleZone); - List contentZone = new ArrayList<>(); - contentZone.add(generateYouTextView("项目:" + projectName)); - contentZone.add(generateYouTextView("时间:" + yesterday + " ~ " + today)); - contentZone.add(generateYouTextView("共测试:" + count)); - contentZone.add(generateYouTextView("通过数:" + passCount)); - contentZone.add(generateYouTextView("异常数:" + warnCount)); - contentZone.add(generateYouTextView("失败数:" + failCount)); - contentZone.add(generateYouTextView("测试通过率:" + (total > 0 ? - new BigDecimal(((float) passCount / total) * 100).setScale(2, RoundingMode.HALF_UP).doubleValue() : 0) + "%")); - JSONObject button = new JSONObject(); - button.put("type", "buttonView"); - JSONObject data = new JSONObject(); - data.put("mode", 0); - data.put("text", "点击查看"); - data.put("url", clientHost + "/Home/" + projectId); - button.put("data", data); - contentZone.add(button); - jsonObject.put("contentZone", contentZone); - this.signAndSend(restTemplate, token, secret, jsonObject); + public Expression getDefaultDeviceMessageTemplate() { + return templateDeviceMessage; } + } diff --git a/sonic-server-controller/src/test/java/org/cloud/sonic/controller/service/impl/AgentsServiceImplTest.java b/sonic-server-controller/src/test/java/org/cloud/sonic/controller/service/impl/AgentsServiceImplTest.java index 778889d4..0398eb2c 100644 --- a/sonic-server-controller/src/test/java/org/cloud/sonic/controller/service/impl/AgentsServiceImplTest.java +++ b/sonic-server-controller/src/test/java/org/cloud/sonic/controller/service/impl/AgentsServiceImplTest.java @@ -98,7 +98,7 @@ public void testUpdate() { Mockito.when(agentsMapper.insert(agents)) .thenReturn(1); - agentsService.update(id, name, highTemp, highTempTime, robotType, robotToken, robotSecret); + agentsService.update(id, name, highTemp, highTempTime, robotType, robotToken, robotSecret, null); } diff --git a/sonic-server-controller/src/test/java/org/cloud/sonic/controller/tools/RobotMsgToolTest.java b/sonic-server-controller/src/test/java/org/cloud/sonic/controller/tools/RobotMsgToolTest.java index a09c53a3..c94b911c 100644 --- a/sonic-server-controller/src/test/java/org/cloud/sonic/controller/tools/RobotMsgToolTest.java +++ b/sonic-server-controller/src/test/java/org/cloud/sonic/controller/tools/RobotMsgToolTest.java @@ -18,145 +18,58 @@ package org.cloud.sonic.controller.tools; import org.cloud.sonic.controller.ControllerApplication; -import org.cloud.sonic.controller.models.interfaces.RobotType; +import org.cloud.sonic.controller.tools.robot.Message; +import org.cloud.sonic.controller.tools.robot.RobotMessenger; +import org.cloud.sonic.controller.tools.robot.message.DeviceMessage; +import org.cloud.sonic.controller.tools.robot.message.ProjectSummaryMessage; +import org.cloud.sonic.controller.tools.robot.message.TestSuiteMessage; +import org.cloud.sonic.controller.tools.robot.vendor.*; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import java.util.Date; @SpringBootTest(classes = ControllerApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @RunWith(SpringRunner.class) public class RobotMsgToolTest { - @Autowired - private RobotMsgTool robotMsgTool; - - private String wechatToken = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx"; - - private String feishuToken = "https://open.feishu.cn/open-apis/bot/v2/hook/xxx"; - - // API path https://api.telegram.org/bot/sendMessage?chat_id= - private String telegramToken = "https://api.telegram.org/botxxx/sendMessage?chat_id=zzz"; - - private String lineToken = "XXXXX"; - - private String slackWebhook = "https://hooks.slack.com/services/TAMNTVCAH/B04PURURLEM/XXXXXXXXX"; - - @Test - public void sendDayReportMessage() { - robotMsgTool.sendDayReportMessage(wechatToken, "", 1, "2", - "1", "2", 1, 2, 3, RobotType.WeChat); - } - - @Test - public void sendSlackDayReportMessage() { - robotMsgTool.sendDayReportMessage(slackWebhook, "", 1, "Operator App", - "2023-2-14 14:52:01", "2023-2-14 14:59:21", 40, 0, 0, RobotType.SlackNotify); - } - - - @Test - public void sendDayReportMessage2() { - robotMsgTool.sendDayReportMessage(wechatToken, "", 1, "2", - "1", "2", 1, 0, 0, RobotType.WeChat); - } - - @Test - public void testSendErrorDeviceSlack() { - robotMsgTool.sendErrorDevice(slackWebhook, "", RobotType.SlackNotify, 0, 80, "111"); - } - - @Test - public void testSendErrorDeviceSlack2() { - robotMsgTool.sendErrorDevice(slackWebhook, "", RobotType.SlackNotify, 1, 80, "111"); - } - - - @Test - public void testSendErrorDevice2() { - robotMsgTool.sendErrorDevice(wechatToken, "", RobotType.WeChat, 2, 80, "111"); - } - - @Test - public void testSendErrorDeviceFeishu() { - robotMsgTool.sendErrorDevice(feishuToken, "", RobotType.FeiShu, 1, 80, "测试"); - } - - @Test - public void testSendErrorDeviceFeishu2() { - robotMsgTool.sendErrorDevice(feishuToken, "", RobotType.FeiShu, 2, 80, "测试"); - } - - - @Test - public void sendWeekReportMessage() { - robotMsgTool.sendWeekReportMessage(wechatToken, "", 1, "2", - "1", "2", 1, 0, 0, 100, RobotType.WeChat); - } - - @Test - public void sendWeekReportMessageSlack() { - robotMsgTool.sendWeekReportMessage(slackWebhook, "", 1, "2", - "1", "2", 1, 0, 0, 100, RobotType.SlackNotify); - } - - @Test - public void sendResultFinishReport() { - robotMsgTool.sendResultFinishReport(wechatToken, "", "111", 1, 1, 1, 1, - 1, RobotType.WeChat); - } - - @Test - public void sendResultFinishReportSlack() { - robotMsgTool.sendResultFinishReport(slackWebhook, "", "suite-01", 1, 1, 1, 1, - 1, RobotType.SlackNotify); - } - - @Test - public void sendDayReportMessageForTelegram() { - robotMsgTool.sendDayReportMessage(telegramToken, "", 1, "2", - "1", "2", 1, 2, 3, RobotType.Telegram); - } - - @Test - public void sendResultFinishReportForTelegram() { - robotMsgTool.sendResultFinishReport(telegramToken, "", "111", 1, 1, 1, 1, - 1, RobotType.Telegram); - } - - @Test - public void sendWeekReportMessageForTelegram() { - robotMsgTool.sendWeekReportMessage(telegramToken, "", 1, "2", - "1", "2", 1, 0, 0, 100, RobotType.Telegram); + private void testMessage(RobotMessenger bot, String token, String secret) { + for (var msg : new Message[]{new DeviceMessage(0, 0, "test"), new ProjectSummaryMessage(0, "test", new Date(System.currentTimeMillis() - 7 * 24 * 3600 * 1000), new Date(), 1, 3, 4, 1.4, 3, "https://sonic/?#=+", true), new ProjectSummaryMessage(0, "test", new Date(System.currentTimeMillis() - 24 * 3600 * 1000), new Date(), 1, 3, 4, 1.4, 3, "https://sonic/?#=+", false), new TestSuiteMessage("asf", 0, 1, 4, 5, 3, "https://sonic/?#=+"),}) { + bot.sendMessage(new RestTemplate(), token, secret, "", msg); + } } @Test - public void testSendErrorDeviceForTelegram() { - robotMsgTool.sendErrorDevice(telegramToken, "", RobotType.Telegram, 2, 80, "测试"); + public void testWechat() { + String wechatToken = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=f233d0b8-8ced-43a1-97c9-84f2d0acbd94"; + testMessage(new WeChatImpl(), wechatToken, ""); } @Test - public void sendDayReportMessageForLineNotify() { - robotMsgTool.sendDayReportMessage(lineToken, "", 1, "2", - "1", "2", 1, 2, 3, RobotType.LineNotify); + public void testSlack() { + String slackWebhook = "https://hooks.slack.com/services/TAMNTVCAH/B04PURURLEM/XXXXXXXXX"; + testMessage(new SlackNotifyImpl(), slackWebhook, ""); } @Test - public void sendResultFinishReportForLineNotify() { - robotMsgTool.sendResultFinishReport(lineToken, "", "111", 1, 1, 2, 1, - 1, RobotType.LineNotify); + public void testFeishu() { + String feishuToken = "https://open.feishu.cn/open-apis/bot/v2/hook/xxx"; + testMessage(new FeiShuImpl(), feishuToken, ""); } @Test - public void sendWeekReportMessageForLineNotify() { - robotMsgTool.sendWeekReportMessage(lineToken, "", 1, "2", - "1", "2", 1, 3, 5, 100, RobotType.LineNotify); + public void testTelegram() { + String telegramToken = "https://api.telegram.org/botxxx/sendMessage?chat_id=zzz"; + testMessage(new TelegramImpl(), telegramToken, ""); } @Test - public void testSendErrorDeviceForLineNotify() { - robotMsgTool.sendErrorDevice(lineToken, "", RobotType.LineNotify, 2, 80, "测试"); + public void testLine() { + String lineToken = ""; + testMessage(new LineNotifyImpl(), lineToken, ""); } }