class AlertRobotsServiceImpl extends SonicServiceImpl<AlertRobotsMapper, AlertRobots> implements AlertRobotsService {

    @Value("${robot.client.host}")
    private String clientHost;

    @Autowired
    private RobotFactory robotFactory;

    @Autowired
    private RestTemplate restTemplate;

    @Override
    public CommentPage<AlertRobots> findRobots(Page<AlertRobots> page, Integer projectId, String scene) {
        return CommentPage.convertFrom(findRobots(projectId, scene).page(page));
    }

    @Override
    public List<AlertRobots> findAllRobots(Integer projectId, String scene) {
        return findRobots(projectId, scene).list();
    }

    private LambdaQueryChainWrapper<AlertRobots> 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 = findRobots(projectId, SCENE_SUMMARY).list();
        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, new BigDecimal(BigInteger.valueOf(tem), 1), udId);
        send(robots, msg);
    }

    private void send(List<AlertRobots> robots, Message message) {<FILL_FUNCTION_BODY>}

    @Override
    public String getDefaultNoticeTemplate(int type, String scene) {
        var messenger = robotFactory.getRobotMessenger(type);
        var template = switch (scene) {
            case SCENE_AGENT -> messenger.getDefaultDeviceMessageTemplate();
            case SCENE_SUMMARY -> messenger.getDefaultProjectSummaryTemplate();
            case SCENE_TESTSUITE -> messenger.getDefaultTestSuiteTemplate();
            default -> null;
        };
        if (null == template) {
            return "";
        } else if (template instanceof SpelExpression) {
            return RobotMessenger.templateParserContext.getExpressionPrefix() + template.getExpressionString() + RobotMessenger.templateParserContext.getExpressionSuffix();
        } else {
            return template.getExpressionString();
        }
    }
}

        for (var robot : robots) {
            try {
                var messenger = robotFactory.getRobotMessenger(robot.getRobotType(), robot.getMuteRule(), message);
                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);
            }
        }
    
<methods>public non-sealed void <init>() ,public boolean save(org.cloud.sonic.controller.models.domain.AlertRobots) <variables>