package com.njcn.gather.steady.checksquare.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareCalculator;
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareHarmonicParityRuleComponent;
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareInfluxQueryComponent;
import com.njcn.gather.steady.checksquare.component.SteadyChecksquareValueOrderRuleComponent;
import com.njcn.gather.steady.checksquare.pojo.param.SteadyChecksquareQueryParam;
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareDetailPO;
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareItemPO;
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareStatSummaryPO;
import com.njcn.gather.steady.checksquare.pojo.po.SteadyChecksquareTaskPO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityDetailVO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareHarmonicParityRuleVO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemDetailVO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareItemVO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareQueryVO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareStatSummaryVO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareTaskVO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderDetailVO;
import com.njcn.gather.steady.checksquare.pojo.vo.SteadyChecksquareValueOrderRuleVO;
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareDetailService;
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareItemService;
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareStatSummaryService;
import com.njcn.gather.steady.checksquare.service.SteadyChecksquareTaskService;
import com.njcn.gather.steady.datavie.component.SteadyTrendIndicatorCatalog;
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendIndicatorDefinitionBO;
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
import com.njcn.gather.tool.adddata.component.AddDataTimeSlotCalculator;
import com.njcn.gather.tool.addledger.pojo.vo.AddLedgerLinePathVO;
import com.njcn.gather.tool.addledger.service.AddLedgerService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.apache.ibatis.session.Configuration;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

/**
 * 数据校验服务测试。
 */
class SteadyChecksquareServiceImplTest {

    @BeforeAll
    static void initMybatisPlusTableInfo() {
        MapperBuilderAssistant assistant = new MapperBuilderAssistant(new Configuration(), "");
        TableInfoHelper.initTableInfo(assistant, SteadyChecksquareTaskPO.class);
        TableInfoHelper.initTableInfo(assistant, SteadyChecksquareItemPO.class);
        TableInfoHelper.initTableInfo(assistant, SteadyChecksquareStatSummaryPO.class);
        TableInfoHelper.initTableInfo(assistant, SteadyChecksquareDetailPO.class);
    }

    @Test
    void shouldNotOpenTransactionAroundCreateCalculation() throws Exception {
        Method createMethod = SteadyChecksquareServiceImpl.class.getMethod("create", SteadyChecksquareQueryParam.class);

        Assertions.assertNull(createMethod.getAnnotation(Transactional.class));
    }

    @Test
    void shouldRejectCreateWhenTimeRangeExceedsSevenDays() {
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
        when(taskService.lambdaQuery()).thenReturn(taskQuery);
        when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
        when(taskQuery.orderByDesc(any(SFunction.class))).thenReturn(taskQuery);
        when(taskQuery.list()).thenReturn(Collections.emptyList());
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), addLedgerService, taskService,
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("line-001");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Collections.singletonList("V_RMS"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-08 00:01:00");

        Assertions.assertThrows(RuntimeException.class, () -> service.create(param));

        verify(taskService, never()).save(any());
    }

    @Test
    void shouldNotRejectCreateByIndicatorCountWithinSevenDays() {
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
        SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
        LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
        when(taskService.lambdaQuery()).thenReturn(taskQuery);
        when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
        when(taskQuery.orderByDesc(any(SFunction.class))).thenReturn(taskQuery);
        when(taskQuery.list()).thenReturn(Collections.emptyList());
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(),
                valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, taskService,
                itemService, statSummaryService, detailService, new ObjectMapper());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("line-001");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(new HashSet<LocalDateTime>());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyHarmonicParityRuleResult());
        ArgumentCaptor<SteadyChecksquareTaskPO> taskCaptor = ArgumentCaptor.forClass(SteadyChecksquareTaskPO.class);
        when(taskService.save(taskCaptor.capture())).thenReturn(true);
        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Arrays.asList("V_RMS", "V_LINE_RMS", "FREQ", "I_RMS", "I_THD"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-03 23:59:00");

        SteadyChecksquareTaskVO result = service.create(param);

        Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
        Assertions.assertEquals("RUNNING", result.getTaskStatus());
        Assertions.assertEquals(Integer.valueOf(0), result.getItemCount());
        verify(itemService).saveBatch(any());
    }

    @Test
    void shouldReturnExistingTaskSummaryWhenCreateMatchesLineAndTime() {
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
        SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
        task.setId("task-001");
        task.setTaskNo("CS202605010001");
        task.setLineId("line-001");
        task.setLineName("line-001");
        task.setTimeStart(LocalDateTime.of(2026, 5, 1, 0, 0));
        task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
        task.setIntervalMinutes(1);
        task.setIndicatorCodesJson("[\"V_RMS\"]");
        task.setTaskStatus("SUCCESS");
        task.setItemCount(1);
        task.setAbnormalItemCount(0);
        task.setMinDataIntegrity(BigDecimal.ONE.setScale(6));
        task.setCreateTime(LocalDateTime.of(2026, 5, 1, 1, 0));
        task.setState(1);
        when(taskService.lambdaQuery()).thenReturn(taskQuery);
        when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
        when(taskQuery.orderByDesc(any(SFunction.class))).thenReturn(taskQuery);
        when(taskQuery.list()).thenReturn(Collections.singletonList(task));
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Collections.singletonList("I_RMS"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 00:01:00");

        SteadyChecksquareTaskVO result = service.create(param);

        Assertions.assertEquals("task-001", result.getTaskId());
        Assertions.assertEquals("CS202605010001", result.getTaskNo());
        Assertions.assertEquals("SUCCESS", result.getTaskStatus());
        verify(taskService, never()).save(any());
    }

    @Test
    void shouldCreateRunningTaskAndReturnTaskSummaryBeforeCalculationCompletes() {
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
        SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
        LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
        when(taskService.lambdaQuery()).thenReturn(taskQuery);
        when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
        when(taskQuery.orderByDesc(any(SFunction.class))).thenReturn(taskQuery);
        when(taskQuery.list()).thenReturn(Collections.emptyList());
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(),
                valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, taskService,
                itemService, statSummaryService, detailService, new ObjectMapper());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("line-001");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(new HashSet<LocalDateTime>());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyHarmonicParityRuleResult());
        ArgumentCaptor<SteadyChecksquareTaskPO> taskCaptor = ArgumentCaptor.forClass(SteadyChecksquareTaskPO.class);
        when(taskService.save(taskCaptor.capture())).thenReturn(true);
        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Collections.singletonList("V_RMS"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 00:01:00");

        SteadyChecksquareTaskVO result = service.create(param);

        Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
        Assertions.assertEquals("RUNNING", result.getTaskStatus());
        Assertions.assertEquals(Integer.valueOf(0), result.getItemCount());
        verify(itemService).saveBatch(any());
        verify(statSummaryService).saveBatch(any());
    }

    @Test
    void shouldCreateSingleRunningTaskForMultipleLines() {
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
        SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
        LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
        when(taskService.lambdaQuery()).thenReturn(taskQuery);
        when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
        when(taskQuery.and(any())).thenReturn(taskQuery);
        when(taskQuery.orderByDesc(any(SFunction.class))).thenReturn(taskQuery);
        when(taskQuery.list()).thenReturn(Collections.emptyList());
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(),
                valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, taskService,
                itemService, statSummaryService, detailService, new ObjectMapper());
        AddLedgerLinePathVO linePath1 = new AddLedgerLinePathVO();
        linePath1.setLineId("line-001");
        linePath1.setLineName("line-001");
        linePath1.setLineInterval(1);
        AddLedgerLinePathVO linePath2 = new AddLedgerLinePathVO();
        linePath2.setLineId("line-002");
        linePath2.setLineName("line-002");
        linePath2.setLineInterval(1);
        LinkedHashMap<String, AddLedgerLinePathVO> linePathMap = new LinkedHashMap<String, AddLedgerLinePathVO>();
        linePathMap.put("line-001", linePath1);
        linePathMap.put("line-002", linePath2);
        when(addLedgerService.listLinePathByLineIds(eq(Arrays.asList("line-001", "line-002"))))
                .thenReturn(linePathMap);
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(new HashSet<LocalDateTime>());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyHarmonicParityRuleResult());
        ArgumentCaptor<SteadyChecksquareTaskPO> taskCaptor = ArgumentCaptor.forClass(SteadyChecksquareTaskPO.class);
        when(taskService.save(taskCaptor.capture())).thenReturn(true);
        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Arrays.asList("line-001", "line-002"));
        param.setIndicatorCodes(Collections.singletonList("V_RMS"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 00:01:00");

        SteadyChecksquareTaskVO result = service.create(param);

        Assertions.assertEquals(taskCaptor.getValue().getId(), result.getTaskId());
        Assertions.assertEquals(Arrays.asList("line-001", "line-002"), result.getLineIds());
        Assertions.assertEquals("|line-001|line-002|", taskCaptor.getValue().getLineIdsText());
        Assertions.assertEquals("line-001、line-002", taskCaptor.getValue().getLineName());
        verify(taskService, times(1)).save(any());
    }

    @Test
    void shouldUseFixedFlickerIntervalsPerIndicator() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyHarmonicParityRuleResult());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("进线一");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(10)))
                .thenReturn(new HashSet<LocalDateTime>(Arrays.asList(
                        LocalDateTime.of(2026, 5, 1, 0, 0),
                        LocalDateTime.of(2026, 5, 1, 0, 10))));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(120)))
                .thenReturn(new HashSet<LocalDateTime>(Collections.singletonList(
                        LocalDateTime.of(2026, 5, 1, 0, 0))));

        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Arrays.asList("FLUC", "PST", "PLT"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 02:00:00");

        SteadyChecksquareQueryVO result = calculate(service, param);

        Assertions.assertEquals(Integer.valueOf(1), result.getIntervalMinutes());
        Assertions.assertEquals(3, result.getItems().size());
        assertItemInterval(result.getItems().get(0), "FLUC", 10, 13);
        assertItemInterval(result.getItems().get(1), "PST", 10, 13);
        assertItemInterval(result.getItems().get(2), "PLT", 120, 2);
    }

    @Test
    void shouldUseAllIndicatorsWhenCreateIndicatorCodesIsEmpty() {
        SteadyTrendIndicatorCatalog indicatorCatalog = new SteadyTrendIndicatorCatalog();
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(indicatorCatalog,
                influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyHarmonicParityRuleResult());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("line-001");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(new HashSet<LocalDateTime>());

        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Collections.<String>emptyList());
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 00:01:00");

        SteadyChecksquareQueryVO result = calculate(service, param);

        List<String> expectedIndicatorCodes = new ArrayList<String>();
        for (SteadyTrendIndicatorDefinitionBO indicator : indicatorCatalog.listIndicators()) {
            expectedIndicatorCodes.add(indicator.getIndicatorCode());
        }
        List<String> actualIndicatorCodes = new ArrayList<String>();
        for (SteadyChecksquareItemVO item : result.getItems()) {
            actualIndicatorCodes.add(item.getIndicatorCode());
        }
        Assertions.assertEquals(expectedIndicatorCodes, actualIndicatorCodes);
    }

    @Test
    void shouldAggregateAllHarmonicOrdersIntoIndicatorItem() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyHarmonicParityRuleResult());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("进线一");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(new HashSet<LocalDateTime>(Collections.singletonList(
                        LocalDateTime.of(2026, 5, 1, 0, 0))));

        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Collections.singletonList("V_HARMONIC"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 00:01:00");

        SteadyChecksquareQueryVO result = calculate(service, param);

        Assertions.assertEquals(1, result.getItems().size());
        Assertions.assertEquals("line-001|V_HARMONIC", result.getItems().get(0).getItemKey());
        Assertions.assertNull(result.getItems().get(0).getHarmonicOrder());
        Assertions.assertEquals(Integer.valueOf(2), result.getItems().get(0).getStatDetails().get(0).getSegments().get(0).getHarmonicOrder());
    }

    @Test
    void shouldAverageHarmonicOrderResultsAndMarkAbnormalWhenAnyOrderAbnormal() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyHarmonicParityRuleResult());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("进线一");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(new HashSet<LocalDateTime>(Collections.singletonList(
                        LocalDateTime.of(2026, 5, 1, 0, 0))));
        SteadyChecksquareValueOrderRuleVO normalRuleResult = new SteadyChecksquareValueOrderRuleVO();
        SteadyChecksquareValueOrderRuleVO abnormalRuleResult = new SteadyChecksquareValueOrderRuleVO();
        SteadyChecksquareValueOrderDetailVO abnormalDetail = new SteadyChecksquareValueOrderDetailVO();
        abnormalDetail.setTime("2026-05-01 00:00:00");
        abnormalDetail.setPhase("A");
        abnormalDetail.setHarmonicOrder(2);
        abnormalRuleResult.setAbnormal(true);
        abnormalRuleResult.setAbnormalPointCount(4);
        abnormalRuleResult.setAbnormalDetails(Collections.singletonList(abnormalDetail));
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(normalRuleResult);
        when(valueOrderRuleComponent.check(any(), any(), eq(2), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(abnormalRuleResult);
        when(valueOrderRuleComponent.check(any(), any(), eq(3), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(normalRuleResult);

        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Collections.singletonList("V_HARMONIC"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 00:01:00");

        SteadyChecksquareQueryVO result = calculate(service, param);

        List<SteadyChecksquareItemVO> items = result.getItems();
        Assertions.assertEquals(1, items.size());
        Assertions.assertEquals(Boolean.TRUE, items.get(0).getAbnormal());
        Assertions.assertEquals(Integer.valueOf(1), items.get(0).getAbnormalPointCount());
        Assertions.assertEquals(1, items.get(0).getAbnormalDetails().size());
        Assertions.assertEquals(Integer.valueOf(2), items.get(0).getAbnormalDetails().get(0).getHarmonicOrder());
        Assertions.assertEquals(Integer.valueOf(4), items.get(0).getExpectedPointCount());
        Assertions.assertEquals(Integer.valueOf(4), items.get(0).getActualPointCount());
    }

    @Test
    void shouldAssembleValueOrderRuleResultIntoItem() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("进线一");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(new HashSet<LocalDateTime>(Collections.singletonList(
                        LocalDateTime.of(2026, 5, 1, 0, 0))));
        SteadyChecksquareValueOrderRuleVO ruleResult = new SteadyChecksquareValueOrderRuleVO();
        SteadyChecksquareValueOrderDetailVO detail = new SteadyChecksquareValueOrderDetailVO();
        detail.setTime("2026-05-01 00:00:00");
        detail.setPhase("A");
        ruleResult.setAbnormalPointCount(2);
        ruleResult.setAbnormal(true);
        ruleResult.setAbnormalDetails(Collections.singletonList(detail));
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(ruleResult);

        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Collections.singletonList("V_RMS"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 00:01:00");

        SteadyChecksquareQueryVO result = calculate(service, param);

        SteadyChecksquareItemVO item = result.getItems().get(0);
        Assertions.assertEquals(Boolean.TRUE, item.getAbnormal());
        Assertions.assertEquals(Integer.valueOf(2), item.getAbnormalPointCount());
        Assertions.assertEquals(1, item.getAbnormalDetails().size());
        Assertions.assertEquals("A", item.getAbnormalDetails().get(0).getPhase());
    }

    @Test
    void shouldPrefetchNormalIndicatorFieldsByMeasurementPhaseAndStat() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyHarmonicParityRuleResult());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("进线一");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(new HashSet<LocalDateTime>(Collections.singletonList(
                        LocalDateTime.of(2026, 5, 1, 0, 0))));

        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Arrays.asList("V_RMS", "V_LINE_RMS"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 00:01:00");

        calculate(service, param);

        ArgumentCaptor<List> captor = ArgumentCaptor.forClass(List.class);
        verify(influxQueryComponent, times(12)).queryValuePointMap(captor.capture(),
                any(LocalDateTime.class), any(LocalDateTime.class), eq(1));
        boolean foundBatch = false;
        for (List fields : captor.getAllValues()) {
            if (fields.size() == 2) {
                List<String> fieldNames = new ArrayList<String>();
                for (Object field : fields) {
                    fieldNames.add(((SteadyTrendResolvedFieldBO) field).getField());
                }
                foundBatch = fieldNames.contains("rms") && fieldNames.contains("rms_lvr");
            }
        }
        Assertions.assertTrue(foundBatch);
    }

    @Test
    void shouldAssembleHarmonicParityRuleResultIntoAggregateItem() {
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(), valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, mock(SteadyChecksquareTaskService.class),
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("进线一");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(new HashSet<LocalDateTime>(Collections.singletonList(
                        LocalDateTime.of(2026, 5, 1, 0, 0))));
        SteadyChecksquareHarmonicParityRuleVO ruleResult = new SteadyChecksquareHarmonicParityRuleVO();
        SteadyChecksquareHarmonicParityDetailVO detail = new SteadyChecksquareHarmonicParityDetailVO();
        detail.setTime("2026-05-01 00:00:00");
        detail.setPhase("A");
        detail.setStatType("AVG");
        detail.setEvenHarmonicOrder(4);
        ruleResult.setAbnormal(true);
        ruleResult.setAbnormalPointCount(1);
        ruleResult.setAbnormalDetails(Collections.singletonList(detail));
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), eq(1)))
                .thenReturn(ruleResult);

        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setLineIds(Collections.singletonList("line-001"));
        param.setIndicatorCodes(Collections.singletonList("V_HARMONIC"));
        param.setTimeStart("2026-05-01 00:00:00");
        param.setTimeEnd("2026-05-01 00:01:00");

        SteadyChecksquareQueryVO result = calculate(service, param);

        SteadyChecksquareItemVO item = result.getItems().get(0);
        Assertions.assertNull(item.getHarmonicOrder());
        Assertions.assertEquals(Boolean.TRUE, item.getHarmonicParityAbnormal());
        Assertions.assertEquals(Integer.valueOf(1), item.getHarmonicParityAbnormalPointCount());
        Assertions.assertEquals("AVG", item.getHarmonicParityAbnormalDetails().get(0).getStatType());
    }

    @Test
    void shouldRejectUnsupportedItemDetailType() {
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
        item.setId("item-001");
        item.setState(1);
        when(itemService.getById("item-001")).thenReturn(item);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), mock(SteadyChecksquareTaskService.class),
                itemService, mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());

        Assertions.assertThrows(RuntimeException.class, () -> service.itemDetail("item-001", "UNKNOWN", null));
    }

    @Test
    void shouldLoadDetailSummariesInSingleBatch() {
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
        LambdaQueryChainWrapper<SteadyChecksquareItemPO> itemQuery = mock(LambdaQueryChainWrapper.class);
        LambdaQueryChainWrapper<SteadyChecksquareStatSummaryPO> summaryQuery = mock(LambdaQueryChainWrapper.class);
        SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
        task.setId("task-001");
        task.setState(1);
        task.setLineId("line-001");
        task.setLineName("进线一");
        task.setTimeStart(LocalDateTime.of(2026, 5, 1, 0, 0));
        task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
        task.setIntervalMinutes(1);
        SteadyChecksquareItemPO item1 = buildItemPO("item-001", "V_RMS");
        item1.setLineId("line-001");
        item1.setLineName("line-001-name");
        SteadyChecksquareItemPO item2 = buildItemPO("item-002", "FREQ");
        item2.setLineId("line-002");
        item2.setLineName("line-002-name");
        SteadyChecksquareStatSummaryPO summary1 = buildSummaryPO("item-001", "AVG");
        SteadyChecksquareStatSummaryPO summary2 = buildSummaryPO("item-002", "AVG");
        when(taskService.getById("task-001")).thenReturn(task);
        when(itemService.lambdaQuery()).thenReturn(itemQuery);
        when(itemQuery.eq(any(), any())).thenReturn(itemQuery);
        when(itemQuery.list()).thenReturn(Arrays.asList(item1, item2));
        when(statSummaryService.lambdaQuery()).thenReturn(summaryQuery);
        when(summaryQuery.in(any(), any(List.class))).thenReturn(summaryQuery);
        when(summaryQuery.list()).thenReturn(Arrays.asList(summary1, summary2));
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
                itemService, statSummaryService, mock(SteadyChecksquareDetailService.class), new ObjectMapper());

        SteadyChecksquareQueryVO result = service.detail("task-001");

        Assertions.assertEquals(2, result.getItems().size());
        Assertions.assertEquals("line-001", result.getItems().get(0).getLineId());
        Assertions.assertEquals("line-001-name", result.getItems().get(0).getLineName());
        Assertions.assertEquals("line-002", result.getItems().get(1).getLineId());
        Assertions.assertEquals("line-002-name", result.getItems().get(1).getLineName());
        Assertions.assertEquals(1, result.getItems().get(0).getStatSummaries().size());
        Assertions.assertEquals(1, result.getItems().get(1).getStatSummaries().size());
        verify(statSummaryService, times(1)).lambdaQuery();
    }

    @Test
    void shouldPageItemDetailWhenPageArgumentsPresent() {
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
        SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
        item.setId("item-001");
        item.setLineId("line-001");
        item.setLineName("line-001-name");
        item.setState(1);
        SteadyChecksquareDetailPO detail = new SteadyChecksquareDetailPO();
        detail.setItemId("item-001");
        detail.setDetailType("VALUE_ORDER");
        detail.setPointTime(LocalDateTime.of(2026, 5, 1, 0, 0));
        detail.setPhase("A");
        Page<SteadyChecksquareDetailPO> page = new Page<SteadyChecksquareDetailPO>(2, 1);
        page.setTotal(1);
        page.setRecords(Collections.singletonList(detail));
        when(itemService.getById("item-001")).thenReturn(item);
        when(detailService.page(any(Page.class), any())).thenReturn(page);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), mock(SteadyChecksquareTaskService.class),
                itemService, mock(SteadyChecksquareStatSummaryService.class),
                detailService, new ObjectMapper());

        SteadyChecksquareItemDetailVO result = service.itemDetail("item-001", "VALUE_ORDER", null, 2, 1);

        Assertions.assertEquals(Integer.valueOf(2), result.getPageNum());
        Assertions.assertEquals(Integer.valueOf(1), result.getPageSize());
        Assertions.assertEquals(Long.valueOf(1L), result.getTotal());
        Assertions.assertEquals("line-001", result.getLineId());
        Assertions.assertEquals("line-001-name", result.getLineName());
        Assertions.assertEquals(1, result.getValueOrderDetails().size());
        verify(detailService).page(any(Page.class), any());
    }

    @Test
    void shouldDeleteTasksAndItemsLogically() {
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        LambdaQueryChainWrapper<SteadyChecksquareTaskPO> taskQuery = mock(LambdaQueryChainWrapper.class);
        SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
        task.setId("task-001");
        task.setState(1);
        when(taskService.lambdaQuery()).thenReturn(taskQuery);
        when(taskQuery.in(any(), any(List.class))).thenReturn(taskQuery);
        when(taskQuery.eq(any(), any())).thenReturn(taskQuery);
        when(taskQuery.list()).thenReturn(Collections.singletonList(task));
        when(taskService.update(any())).thenReturn(true);
        when(itemService.update(any())).thenReturn(true);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
                itemService, mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());

        boolean result = service.delete(Collections.singletonList("task-001"));

        Assertions.assertTrue(result);
        verify(taskService).update(any());
        verify(itemService).update(any());
    }

    @Test
    void shouldRejectRestartWhenTaskIsNotFail() {
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareTaskPO task = buildTask("task-001", "SUCCESS");
        when(taskService.getById("task-001")).thenReturn(task);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());

        Assertions.assertThrows(RuntimeException.class, () -> service.restart("task-001"));

        verify(taskService, never()).update(any());
    }

    @Test
    void shouldRestartFailTaskWithSameTaskIdAndOriginalParam() {
        AddLedgerService addLedgerService = mock(AddLedgerService.class);
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareInfluxQueryComponent influxQueryComponent = mock(SteadyChecksquareInfluxQueryComponent.class);
        SteadyChecksquareValueOrderRuleComponent valueOrderRuleComponent = mock(SteadyChecksquareValueOrderRuleComponent.class);
        SteadyChecksquareHarmonicParityRuleComponent harmonicParityRuleComponent = mock(SteadyChecksquareHarmonicParityRuleComponent.class);
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
        SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
        LambdaQueryChainWrapper<SteadyChecksquareItemPO> itemQuery = mock(LambdaQueryChainWrapper.class);
        SteadyChecksquareTaskPO task = buildTask("task-001", "FAIL");
        SteadyChecksquareItemPO oldItem = new SteadyChecksquareItemPO();
        oldItem.setId("item-001");
        oldItem.setTaskId("task-001");
        when(taskService.getById("task-001")).thenReturn(task);
        when(taskService.update(any())).thenReturn(true);
        when(itemService.lambdaQuery()).thenReturn(itemQuery);
        when(itemQuery.eq(any(), any())).thenReturn(itemQuery);
        when(itemQuery.list()).thenReturn(Collections.singletonList(oldItem));
        when(itemService.remove(any())).thenReturn(true);
        when(statSummaryService.remove(any())).thenReturn(true);
        when(detailService.remove(any())).thenReturn(true);
        AddLedgerLinePathVO linePath = new AddLedgerLinePathVO();
        linePath.setLineId("line-001");
        linePath.setLineName("line-001");
        linePath.setLineInterval(1);
        when(addLedgerService.listLinePathByLineIds(eq(Collections.singletonList("line-001"))))
                .thenReturn(Collections.singletonMap("line-001", linePath));
        when(influxQueryComponent.queryExistingSlots(any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(new HashSet<LocalDateTime>());
        when(valueOrderRuleComponent.check(any(), any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyRuleResult());
        when(harmonicParityRuleComponent.check(any(), any(), any(LocalDateTime.class), any(LocalDateTime.class), anyInt()))
                .thenReturn(emptyHarmonicParityRuleResult());
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                influxQueryComponent, new SteadyChecksquareCalculator(),
                valueOrderRuleComponent, harmonicParityRuleComponent,
                new AddDataTimeSlotCalculator(), addLedgerService, taskService,
                itemService, statSummaryService, detailService, new ObjectMapper());

        SteadyChecksquareTaskVO result = service.restart("task-001");

        Assertions.assertEquals("task-001", result.getTaskId());
        Assertions.assertEquals("RUNNING", result.getTaskStatus());
        verify(detailService).remove(any());
        verify(statSummaryService).remove(any());
        verify(itemService).remove(any());
        verify(taskService, times(2)).update(any());
        verify(itemService).saveBatch(any());
    }

    @Test
    void shouldSaveChecksquareResultsInBatch() {
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
        SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
                itemService, statSummaryService, detailService, new ObjectMapper());
        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setIndicatorCodes(Collections.singletonList("V_RMS"));
        SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
        result.setLineIds(Collections.singletonList("line-001"));
        result.setLineName("进线一");
        result.setTimeStart("2026-05-01 00:00:00");
        result.setTimeEnd("2026-05-01 00:01:00");
        result.setIntervalMinutes(1);
        SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
        item.setItemKey("line-001|V_RMS");
        item.setLineId("line-001");
        item.setLineName("line-001-name");
        item.setIndicatorCode("V_RMS");
        item.setIndicatorName("相电压有效值");
        item.setIntervalMinutes(1);
        item.setHasData(true);
        item.setExpectedPointCount(2);
        item.setActualPointCount(2);
        item.setMissingPointCount(0);
        item.setDataIntegrity(BigDecimal.ONE.setScale(6));
        item.setDataIntegrityText("100.00%");
        item.setAbnormal(false);
        item.setAbnormalPointCount(0);
        item.setHarmonicParityAbnormal(false);
        item.setHarmonicParityAbnormalPointCount(0);
        SteadyChecksquareStatSummaryVO summary = new SteadyChecksquareStatSummaryVO();
        summary.setStatType("AVG");
        summary.setSupported(true);
        summary.setHasData(true);
        summary.setExpectedPointCount(2);
        summary.setActualPointCount(2);
        summary.setMissingPointCount(0);
        summary.setDataIntegrity(BigDecimal.ONE.setScale(6));
        summary.setDataIntegrityText("100.00%");
        item.getStatSummaries().add(summary);
        result.getItems().add(item);

        saveResult(service, param, result);

        verify(taskService).save(any());
        ArgumentCaptor<List> itemCaptor = ArgumentCaptor.forClass(List.class);
        verify(itemService).saveBatch(itemCaptor.capture());
        SteadyChecksquareItemPO savedItem = (SteadyChecksquareItemPO) itemCaptor.getValue().get(0);
        Assertions.assertEquals("line-001", savedItem.getLineId());
        Assertions.assertEquals("line-001-name", savedItem.getLineName());
        verify(statSummaryService).saveBatch(any());
    }

    @Test
    void shouldSaveDetailResultsInChunks() {
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareItemService itemService = mock(SteadyChecksquareItemService.class);
        SteadyChecksquareStatSummaryService statSummaryService = mock(SteadyChecksquareStatSummaryService.class);
        SteadyChecksquareDetailService detailService = mock(SteadyChecksquareDetailService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
                itemService, statSummaryService, detailService, new ObjectMapper());
        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setIndicatorCodes(Collections.singletonList("V_RMS"));
        SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
        result.setLineIds(Collections.singletonList("line-001"));
        result.setLineName("line-001");
        result.setTimeStart("2026-05-01 00:00:00");
        result.setTimeEnd("2026-05-01 00:01:00");
        result.setIntervalMinutes(1);
        SteadyChecksquareItemVO item = buildOrderItem(true, BigDecimal.ONE.setScale(6));
        item.setItemKey("line-001|V_RMS");
        item.setIndicatorCode("V_RMS");
        for (int i = 0; i < 1001; i++) {
            SteadyChecksquareValueOrderDetailVO detail = new SteadyChecksquareValueOrderDetailVO();
            detail.setTime("2026-05-01 00:00:00");
            detail.setPhase("A");
            item.getAbnormalDetails().add(detail);
        }
        result.getItems().add(item);

        saveResult(service, param, result);

        verify(detailService, times(2)).saveBatch(any());
    }

    @Test
    void shouldCountNoDataItemAsAbnormalWhenSavingTask() {
        SteadyChecksquareTaskService taskService = mock(SteadyChecksquareTaskService.class);
        SteadyChecksquareServiceImpl service = new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), taskService,
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
        SteadyChecksquareQueryParam param = new SteadyChecksquareQueryParam();
        param.setIndicatorCodes(Collections.singletonList("V_RMS"));
        SteadyChecksquareQueryVO result = new SteadyChecksquareQueryVO();
        result.setLineIds(Collections.singletonList("line-001"));
        result.setLineName("进线一");
        result.setTimeStart("2026-05-01 00:00:00");
        result.setTimeEnd("2026-05-01 00:01:00");
        result.setIntervalMinutes(1);
        SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
        item.setItemKey("line-001|V_RMS");
        item.setIndicatorCode("V_RMS");
        item.setHasData(false);
        item.setExpectedPointCount(2);
        item.setActualPointCount(0);
        item.setMissingPointCount(2);
        item.setDataIntegrity(BigDecimal.ZERO.setScale(6));
        item.setDataIntegrityText("0.00%");
        item.setAbnormal(false);
        item.setHarmonicParityAbnormal(false);
        result.getItems().add(item);

        saveResult(service, param, result);

        ArgumentCaptor<SteadyChecksquareTaskPO> captor = ArgumentCaptor.forClass(SteadyChecksquareTaskPO.class);
        verify(taskService).save(captor.capture());
        Assertions.assertEquals(Integer.valueOf(1), captor.getValue().getAbnormalItemCount());
        Assertions.assertEquals(BigDecimal.ZERO.setScale(6), captor.getValue().getMinDataIntegrity());
    }

    @Test
    void shouldMarkAggregateHarmonicItemNoDataWhenDataIntegrityIsZero() {
        SteadyChecksquareServiceImpl service = newService();
        SteadyTrendIndicatorDefinitionBO indicator = buildHarmonicIndicator();
        SteadyChecksquareItemVO orderItem = buildOrderItem(true, BigDecimal.ZERO.setScale(6));
        orderItem.getStatSummaries().add(buildSummaryVO(true, BigDecimal.ZERO.setScale(6)));

        SteadyChecksquareItemVO result = aggregateHarmonicItems(service, indicator, Collections.singletonList(orderItem));

        Assertions.assertEquals(BigDecimal.ZERO.setScale(6), result.getDataIntegrity());
        Assertions.assertEquals(Boolean.FALSE, result.getHasData());
        Assertions.assertEquals(Boolean.FALSE, result.getStatSummaries().get(0).getHasData());
    }

    @Test
    void shouldMarkAggregateHarmonicItemHasDataWhenDataIntegrityIsGreaterThanZero() {
        SteadyChecksquareServiceImpl service = newService();
        SteadyTrendIndicatorDefinitionBO indicator = buildHarmonicIndicator();
        SteadyChecksquareItemVO orderItem = buildOrderItem(false, new BigDecimal("0.500000"));
        orderItem.getStatSummaries().add(buildSummaryVO(false, new BigDecimal("0.500000")));

        SteadyChecksquareItemVO result = aggregateHarmonicItems(service, indicator, Collections.singletonList(orderItem));

        Assertions.assertEquals(new BigDecimal("0.500000"), result.getDataIntegrity());
        Assertions.assertEquals(Boolean.TRUE, result.getHasData());
        Assertions.assertEquals(Boolean.TRUE, result.getStatSummaries().get(0).getHasData());
    }

    private void assertItemInterval(SteadyChecksquareItemVO item, String indicatorCode, int intervalMinutes, int expectedPointCount) {
        Assertions.assertEquals(indicatorCode, item.getIndicatorCode());
        Assertions.assertEquals(Integer.valueOf(intervalMinutes), item.getIntervalMinutes());
        Assertions.assertEquals(Integer.valueOf(expectedPointCount), item.getExpectedPointCount());
    }

    private SteadyChecksquareServiceImpl newService() {
        return new SteadyChecksquareServiceImpl(new SteadyTrendIndicatorCatalog(),
                mock(SteadyChecksquareInfluxQueryComponent.class), new SteadyChecksquareCalculator(),
                mock(SteadyChecksquareValueOrderRuleComponent.class), mock(SteadyChecksquareHarmonicParityRuleComponent.class),
                new AddDataTimeSlotCalculator(), mock(AddLedgerService.class), mock(SteadyChecksquareTaskService.class),
                mock(SteadyChecksquareItemService.class), mock(SteadyChecksquareStatSummaryService.class),
                mock(SteadyChecksquareDetailService.class), new ObjectMapper());
    }

    private SteadyChecksquareQueryVO calculate(SteadyChecksquareServiceImpl service, SteadyChecksquareQueryParam param) {
        try {
            Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("calculate", SteadyChecksquareQueryParam.class);
            method.setAccessible(true);
            return (SteadyChecksquareQueryVO) method.invoke(service, param);
        } catch (Exception exception) {
            throw new RuntimeException(exception);
        }
    }

    private void saveResult(SteadyChecksquareServiceImpl service, SteadyChecksquareQueryParam param, SteadyChecksquareQueryVO result) {
        try {
            Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("saveResult",
                    SteadyChecksquareQueryParam.class, SteadyChecksquareQueryVO.class);
            method.setAccessible(true);
            method.invoke(service, param, result);
        } catch (Exception exception) {
            throw new RuntimeException(exception);
        }
    }

    private SteadyChecksquareItemVO aggregateHarmonicItems(SteadyChecksquareServiceImpl service,
                                                          SteadyTrendIndicatorDefinitionBO indicator,
                                                          List<SteadyChecksquareItemVO> orderItems) {
        try {
            Method method = SteadyChecksquareServiceImpl.class.getDeclaredMethod("aggregateHarmonicItems",
                    String.class, String.class, SteadyTrendIndicatorDefinitionBO.class, List.class, int.class);
            method.setAccessible(true);
            return (SteadyChecksquareItemVO) method.invoke(service, "line-001", "line-001-name", indicator, orderItems, 1);
        } catch (Exception exception) {
            throw new RuntimeException(exception);
        }
    }

    private SteadyTrendIndicatorDefinitionBO buildHarmonicIndicator() {
        SteadyTrendIndicatorDefinitionBO indicator = new SteadyTrendIndicatorDefinitionBO();
        indicator.setIndicatorCode("V_HARMONIC");
        indicator.setName("V_HARMONIC");
        indicator.setHarmonic(true);
        return indicator;
    }

    private SteadyChecksquareTaskPO buildTask(String taskId, String taskStatus) {
        SteadyChecksquareTaskPO task = new SteadyChecksquareTaskPO();
        task.setId(taskId);
        task.setTaskNo("CS202605010001");
        task.setLineId("line-001");
        task.setLineName("line-001");
        task.setLineIdsJson("[\"line-001\"]");
        task.setLineIdsText("|line-001|");
        task.setTimeStart(LocalDateTime.of(2026, 5, 1, 0, 0));
        task.setTimeEnd(LocalDateTime.of(2026, 5, 1, 0, 1));
        task.setIntervalMinutes(1);
        task.setIndicatorCodesJson("[\"V_RMS\"]");
        task.setIndicatorCodesText("|V_RMS|");
        task.setTaskStatus(taskStatus);
        task.setItemCount(1);
        task.setAbnormalItemCount(1);
        task.setMinDataIntegrity(BigDecimal.ZERO.setScale(6));
        task.setResultMessage("failed");
        task.setState(1);
        task.setCreateTime(LocalDateTime.of(2026, 5, 1, 1, 0));
        return task;
    }

    private SteadyChecksquareItemVO buildOrderItem(boolean hasData, BigDecimal dataIntegrity) {
        SteadyChecksquareItemVO item = new SteadyChecksquareItemVO();
        item.setItemKey("line-001|V_HARMONIC|2");
        item.setIndicatorCode("V_HARMONIC");
        item.setIndicatorName("V_HARMONIC");
        item.setHarmonicOrder(2);
        item.setIntervalMinutes(1);
        item.setHasData(hasData);
        item.setExpectedPointCount(2);
        item.setActualPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 0);
        item.setMissingPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 2);
        item.setDataIntegrity(dataIntegrity);
        item.setAbnormal(false);
        item.setAbnormalPointCount(0);
        item.setHarmonicParityAbnormal(false);
        item.setHarmonicParityAbnormalPointCount(0);
        return item;
    }

    private SteadyChecksquareStatSummaryVO buildSummaryVO(boolean hasData, BigDecimal dataIntegrity) {
        SteadyChecksquareStatSummaryVO summary = new SteadyChecksquareStatSummaryVO();
        summary.setStatType("AVG");
        summary.setSupported(true);
        summary.setHasData(hasData);
        summary.setExpectedPointCount(2);
        summary.setActualPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 0);
        summary.setMissingPointCount(dataIntegrity.compareTo(BigDecimal.ZERO) > 0 ? 1 : 2);
        summary.setDataIntegrity(dataIntegrity);
        return summary;
    }

    private SteadyChecksquareItemPO buildItemPO(String itemId, String indicatorCode) {
        SteadyChecksquareItemPO item = new SteadyChecksquareItemPO();
        item.setId(itemId);
        item.setIndicatorCode(indicatorCode);
        item.setIndicatorName(indicatorCode);
        item.setState(1);
        item.setHasData(1);
        item.setExpectedPointCount(1);
        item.setActualPointCount(1);
        item.setMissingPointCount(0);
        item.setDataIntegrity(BigDecimal.ONE.setScale(6));
        item.setAbnormal(0);
        item.setAbnormalPointCount(0);
        item.setHarmonicParityAbnormal(0);
        item.setHarmonicParityAbnormalPointCount(0);
        return item;
    }

    private SteadyChecksquareStatSummaryPO buildSummaryPO(String itemId, String statType) {
        SteadyChecksquareStatSummaryPO summary = new SteadyChecksquareStatSummaryPO();
        summary.setItemId(itemId);
        summary.setStatType(statType);
        summary.setSupported(1);
        summary.setHasData(1);
        summary.setExpectedPointCount(1);
        summary.setActualPointCount(1);
        summary.setMissingPointCount(0);
        summary.setDataIntegrity(BigDecimal.ONE.setScale(6));
        return summary;
    }

    private SteadyChecksquareValueOrderRuleVO emptyRuleResult() {
        return new SteadyChecksquareValueOrderRuleVO();
    }

    private SteadyChecksquareHarmonicParityRuleVO emptyHarmonicParityRuleResult() {
        return new SteadyChecksquareHarmonicParityRuleVO();
    }
}


