添加统计接口

决策,区域管理接口优化
This commit is contained in:
tiger_zhou 2023-09-08 17:05:42 +08:00
parent f78920ea33
commit 9a1fe4a96a
27 changed files with 807 additions and 59 deletions

View File

@ -133,14 +133,6 @@
</dependencies>
<build>
<!-- <resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>-->
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>

View File

@ -25,6 +25,7 @@ import org.springframework.util.CollectionUtils;
public class DeviceNameChangerManage {
final static Table<Integer, DeviceType, List<NameChangerFilter>> CHANGER_TABLE = HashBasedTable.create();
private final static Integer TMP_DEFAULT_LINEID = 3;
public static String findMatch(DeviceType deviceType, String occName) {
if (Objects.isNull(deviceType)) {
@ -35,7 +36,7 @@ public class DeviceNameChangerManage {
log.debug("错误的设备名称 occName[{}]", occName);
return occName;
}
List<NameChangerFilter> filters = CHANGER_TABLE.get(3, deviceType);
List<NameChangerFilter> filters = CHANGER_TABLE.get(TMP_DEFAULT_LINEID, deviceType);
if (CollectionUtils.isEmpty(filters)) {
log.debug("未找到对应的转换的occName:[{}] deviceType[{}]", occName, deviceType);
return occName;
@ -60,7 +61,7 @@ public class DeviceNameChangerManage {
Map<DeviceType, List<NameChangerFilter>> filteMaper = filters.stream().collect(Collectors.groupingBy(NameChangerFilter::deviceType));
filteMaper.forEach((k, v) -> {
Collections.sort(v, Comparator.comparingInt(NameChangerFilter::filterIndex));
CHANGER_TABLE.put(3, k, v);
CHANGER_TABLE.put(TMP_DEFAULT_LINEID, k, v);
});
}

View File

@ -1,7 +1,9 @@
package club.joylink.xiannccda.controller;
import club.joylink.xiannccda.alert.NccAlertInfo;
import club.joylink.xiannccda.dto.AlertRecordQueryDTO;
import club.joylink.xiannccda.dto.record.AlertRecordQueryDTO;
import club.joylink.xiannccda.dto.record.AlertRecordReportDTO;
import club.joylink.xiannccda.dto.record.AlertRecordReportResponseDTO;
import club.joylink.xiannccda.entity.AlertRecord;
import club.joylink.xiannccda.entity.AlertTip;
import club.joylink.xiannccda.repository.IAlertRecordRepository;
@ -11,6 +13,7 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@ -68,4 +71,13 @@ public class AlertRecordController {
public Page<NccAlertInfo> pageQueryAlertDetail(AlertRecordQueryDTO queryDTO) {
return alertRecordService.pageQueryAlertDetail(queryDTO);
}
@GetMapping("/report/{lineId}")
@SecurityRequirement(name = "jwt")
@Operation(summary = "报警统计")
@ApiResponse(description = "报警统计")
public List<AlertRecordReportResponseDTO> reportStatistics(@PathVariable("lineId") Integer lineId, AlertRecordReportDTO reportDTO) {
return this.alertRecordService.report(lineId, reportDTO);
}
}

View File

@ -70,9 +70,9 @@ public class DeviceAreaConfigController {
@SecurityRequirement(name = "jwt")
@Operation(summary = "获取详情数据")
@GetMapping("/page")
public Page<DeviceAreaConfig> page(DeviceAreaConfigQueryDto queryDto) {
return this.areaConfigService.page(queryDto);
@GetMapping("/page/{lineId}")
public Page<DeviceAreaConfig> page(@PathVariable("lineId") Integer lineId, DeviceAreaConfigQueryDto queryDto) {
return this.areaConfigService.page(queryDto, lineId);
}
@SecurityRequirement(name = "jwt")

View File

@ -13,7 +13,9 @@ import lombok.Setter;
@Setter
@NoArgsConstructor
public class AlertTipQueryDTO extends PageDTO<AlertTip> {
private AlertType alertType;
private AlertTipTimeType timeType;
private AlertLocation locationType;
private Long areaConfigId;
}

View File

@ -0,0 +1,36 @@
package club.joylink.xiannccda.dto.config;
import club.joylink.xiannccda.alert.core.AlertDeviceType;
import club.joylink.xiannccda.entity.DeviceAreaConfig;
import io.swagger.v3.oas.annotations.media.Schema;
import java.util.Map;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class DeviceAreaConfigPageDto {
public DeviceAreaConfigPageDto(DeviceAreaConfig pageDto, Map<Long, String> tipIdsMap) {
this.id = pageDto.getId();
this.lineId = pageDto.getLineId();
this.areaName = pageDto.getAreaName();
this.deviceType = pageDto.getDeviceType();
this.data = pageDto.getData();
this.alertTypes = pageDto.getAlertTypes();
this.alertTipIds = tipIdsMap.get(this.id);
}
private Long id;
private Integer lineId;
private String areaName;
private AlertDeviceType deviceType;
private String data;
private String alertTypes;
@Schema(description = "已经绑定决策辅助信息对应的id 以,号隔开")
private String alertTipIds;
}

View File

@ -1,6 +1,7 @@
package club.joylink.xiannccda.dto.config;
import club.joylink.xiannccda.ats.message.line3.device.DeviceType;
import club.joylink.xiannccda.dto.protos.AlertConstProto.AlertType;
import club.joylink.xiannccda.entity.DeviceAreaConfig;
import club.joylink.xiannccda.entity.DeviceGuardConfig;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO;
@ -10,9 +11,11 @@ import lombok.Data;
@Data
public class DeviceAreaConfigQueryDto extends PageDTO<DeviceAreaConfig> {
@Schema(description = "线路id")
private Integer lineId;
@Schema(description = "设备类型")
private DeviceType deviceType;
private String areaName;
private AlertType alertType;
}

View File

@ -1,4 +1,4 @@
package club.joylink.xiannccda.dto;
package club.joylink.xiannccda.dto.record;
import club.joylink.xiannccda.dto.protos.AlertConstProto.AlertType;
import club.joylink.xiannccda.entity.AlertRecord;
@ -7,6 +7,7 @@ import lombok.Data;
@Data
public class AlertRecordQueryDTO extends PageDTO<AlertRecord> {
private AlertType alertType;
private Integer lineId;

View File

@ -0,0 +1,15 @@
package club.joylink.xiannccda.dto.record;
import club.joylink.xiannccda.dto.protos.AlertConstProto.AlertType;
import java.time.LocalDateTime;
import java.util.List;
import lombok.Data;
@Data
public class AlertRecordReportDTO {
private List<AlertType> alertTypes;
private LocalDateTime beginDateTime;
private LocalDateTime endDateTime;
}

View File

@ -0,0 +1,11 @@
package club.joylink.xiannccda.dto.record;
import club.joylink.xiannccda.dto.protos.AlertConstProto.AlertType;
import lombok.Data;
@Data
public class AlertRecordReportResponseDTO {
private AlertType alertType;
private Integer counter;
}

View File

@ -1,8 +1,13 @@
package club.joylink.xiannccda.mapper;
import club.joylink.xiannccda.dto.record.AlertRecordReportDTO;
import club.joylink.xiannccda.dto.record.AlertRecordReportResponseDTO;
import club.joylink.xiannccda.entity.AlertRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
/**
* <p>
@ -13,6 +18,8 @@ import org.apache.ibatis.annotations.Mapper;
* @since 2023-07-25
*/
@Mapper
@Repository
public interface AlertRecordMapper extends BaseMapper<AlertRecord> {
List<AlertRecordReportResponseDTO> report(AlertRecordReportDTO dto, @Param("lineId") Integer lineId);
}

View File

@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="club.joylink.xiannccda.mapper.AlertRecordMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="club.joylink.xiannccda.entity.AlertRecord">
<id column="id" property="id" />
<result column="alert_type" property="alertType" />
<result column="line_id" property="lineId" />
<result column="alert_object" property="alertObject" />
<result column="alert_time" property="alertTime" />
<result column="alert_location" property="alertLocation" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
id, alert_type, line_id, alert_object, alert_time, alert_location
</sql>
</mapper>

View File

@ -1,6 +1,6 @@
package club.joylink.xiannccda.repository;
import club.joylink.xiannccda.dto.AlertRecordQueryDTO;
import club.joylink.xiannccda.dto.record.AlertRecordQueryDTO;
import club.joylink.xiannccda.entity.AlertRecord;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
@ -14,5 +14,6 @@ import com.baomidou.mybatisplus.extension.service.IService;
* @since 2023-07-25
*/
public interface IAlertRecordRepository extends IService<AlertRecord> {
Page<AlertRecord> page(AlertRecordQueryDTO queryDTO);
}

View File

@ -4,6 +4,8 @@ import club.joylink.xiannccda.dto.alertTip.AlertTipQueryDTO;
import club.joylink.xiannccda.entity.AlertTip;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import java.util.List;
import java.util.Map;
/**
* <p>
@ -16,4 +18,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
public interface IAlertTipRepository extends IService<AlertTip> {
Page<AlertTip> page(AlertTipQueryDTO queryDTO);
Map<Long, String> findBindAreaConfigId(List<Long> areaConfigList);
}

View File

@ -1,6 +1,6 @@
package club.joylink.xiannccda.repository.impl;
import club.joylink.xiannccda.dto.AlertRecordQueryDTO;
import club.joylink.xiannccda.dto.record.AlertRecordQueryDTO;
import club.joylink.xiannccda.entity.AlertRecord;
import club.joylink.xiannccda.mapper.AlertRecordMapper;
import club.joylink.xiannccda.repository.IAlertRecordRepository;

View File

@ -10,7 +10,11 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
/**
@ -33,10 +37,29 @@ public class AlertTipRepository extends ServiceImpl<AlertTipMapper, AlertTip> im
if (queryDTO.getTimeType() != null) {
queryWrapper.eq(AlertTip::getTimeType, queryDTO.getTimeType().name());
}
queryWrapper.eq(queryDTO.getAreaConfigId() != null, AlertTip::getAreaConfigId, queryDTO.getAreaConfigId());
/* if (queryDTO.getLocationType() != null) {
queryWrapper.eq(AlertTip::getLocationType, queryDTO.getLocationType().name());
}*/
queryWrapper.notIn(AlertTip::getAlertType, List.of(AlertType.SWITCH_LOST.name()));
return page(queryDTO, queryWrapper);
}
@Override
public Map<Long, String> findBindAreaConfigId(List<Long> areaConfigList) {
LambdaQueryWrapper<AlertTip> queryWrapper = Wrappers.lambdaQuery(AlertTip.class);
queryWrapper.select(List.of(AlertTip::getId, AlertTip::getAreaConfigId));
queryWrapper.in(AlertTip::getAreaConfigId, areaConfigList);
List<AlertTip> tipList = this.list(queryWrapper);
Map<Long, List<AlertTip>> tipListMap = tipList.stream().collect(Collectors.groupingBy(AlertTip::getAreaConfigId));
Map<Long, String> areaConfigIdTipIdsMap = Maps.newHashMap();
tipListMap.forEach((k, v) -> {
String tipIds = v.stream().map(d -> d.getId().toString()).collect(Collectors.joining(","));
areaConfigIdTipIdsMap.put(k, tipIds);
});
return areaConfigIdTipIdsMap;
}
}

View File

@ -1,38 +1,35 @@
package club.joylink.xiannccda.service;
import club.joylink.xiannccda.alert.NccAlertInfo;
import club.joylink.xiannccda.dto.AlertRecordQueryDTO;
import club.joylink.xiannccda.dto.record.AlertRecordQueryDTO;
import club.joylink.xiannccda.dto.protos.AlertConstProto.AlertType;
import club.joylink.xiannccda.dto.protos.LayoutGraphicsProto.Station;
import club.joylink.xiannccda.dto.record.AlertRecordReportDTO;
import club.joylink.xiannccda.dto.record.AlertRecordReportResponseDTO;
import club.joylink.xiannccda.entity.AlertRecord;
import club.joylink.xiannccda.entity.AlertTip;
import club.joylink.xiannccda.exception.BusinessExceptionAssertEnum;
import club.joylink.xiannccda.repository.IAlertRecordRepository;
import club.joylink.xiannccda.repository.impl.AlertRecordRepository;
import club.joylink.xiannccda.repository.impl.AlertTipRepository;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;
@Service
public class AlertRecordService {
private final IAlertRecordRepository alertRecordRepository;
private final AlertTipService alertTipService;
private final AlertRecordRepository alertRecordRepository;
private final AlertTipRepository alertTipRepository;
public AlertRecordService(IAlertRecordRepository alertRecordRepository, AlertTipService alertTipService, AlertTipRepository alertTipRepository) {
public AlertRecordService(AlertRecordRepository alertRecordRepository, AlertTipRepository alertTipRepository) {
this.alertRecordRepository = alertRecordRepository;
this.alertTipService = alertTipService;
this.alertTipRepository = alertTipRepository;
}
@ -58,9 +55,6 @@ public class AlertRecordService {
}
private List<NccAlertInfo> changeData(List<AlertRecord> ars) {
// List<Long> ids = ars.stream().map(AlertRecord::getAlertTipId).filter(Objects::nonNull).distinct().toList();
// List<AlertTip> ats = alertTipRepository.listByIds(ids);
// Map<Integer, AlertTip> tipMap = ats.stream().collect(Collectors.toMap(AlertTip::getId, Function.identity()));
List<NccAlertInfo> alertInfos = Lists.newArrayList();
for (AlertRecord record : ars) {
NccAlertInfo nccAlertInfo = new NccAlertInfo(record.getAlertTime(),
@ -82,4 +76,12 @@ public class AlertRecordService {
nccPage.setRecords(collect);
return nccPage;
}
public List<AlertRecordReportResponseDTO> report(Integer lineId, AlertRecordReportDTO reportDTO) {
if (Objects.nonNull(reportDTO.getBeginDateTime()) && Objects.nonNull(reportDTO.getEndDateTime())) {
BusinessExceptionAssertEnum.ARGUMENT_ILLEGAL.assertTrue(reportDTO.getEndDateTime().isAfter(reportDTO.getBeginDateTime()), "开始时间不能大于结束时间");
}
return this.alertRecordRepository.getBaseMapper().report(reportDTO, lineId);
}
}

View File

@ -2,11 +2,14 @@ package club.joylink.xiannccda.service.config;
import club.joylink.xiannccda.alert.core.AlertDeviceType;
import club.joylink.xiannccda.ats.message.line3.device.DeviceType;
import club.joylink.xiannccda.dto.alertTip.AlertTipInfoDto;
import club.joylink.xiannccda.dto.config.DeviceAreaConfigDto;
import club.joylink.xiannccda.dto.config.DeviceAreaConfigPageDto;
import club.joylink.xiannccda.dto.config.DeviceAreaConfigQueryDto;
import club.joylink.xiannccda.dto.protos.GuardConfigProto.GuardConfig;
import club.joylink.xiannccda.entity.DeviceAreaConfig;
import club.joylink.xiannccda.exception.BusinessExceptionAssertEnum;
import club.joylink.xiannccda.repository.IAlertTipRepository;
import club.joylink.xiannccda.repository.impl.DeviceAreaConfigRepository;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
@ -37,6 +40,9 @@ public class DeviceAreaConfigService {
@Autowired
private DeviceAreaConfigRepository deviceAreaConfigRepository;
@Autowired
private IAlertTipRepository iAlertTipRepository;
private final static Cache<Integer, Map<AlertDeviceType, List<DeviceAreaConfig>>> AREA_CONFIG_CACHE = CacheBuilder.newBuilder().expireAfterWrite(5 * 60, TimeUnit.SECONDS).build();
@ -109,17 +115,32 @@ public class DeviceAreaConfigService {
return this.convertDto(areaConfig);
}
public Page<DeviceAreaConfig> page(DeviceAreaConfigQueryDto queryDTO) {
public Page<DeviceAreaConfigPageDto> page(DeviceAreaConfigQueryDto queryDTO, Integer lineId) {
LambdaQueryWrapper<DeviceAreaConfig> queryWrapper = Wrappers.lambdaQuery(DeviceAreaConfig.class);
queryWrapper.select(List.of(DeviceAreaConfig::getId, DeviceAreaConfig::getAreaName, DeviceAreaConfig::getLineId, DeviceAreaConfig::getDeviceType));
if (Objects.nonNull(queryDTO.getLineId())) {
queryWrapper.eq(DeviceAreaConfig::getLineId, queryDTO.getLineId());
}
queryWrapper.eq(DeviceAreaConfig::getLineId, lineId);
queryWrapper.like(StringUtils.isNotEmpty(queryDTO.getAreaName()), DeviceAreaConfig::getAreaName, queryDTO.getAreaName());
if (Objects.nonNull(queryDTO.getDeviceType())) {
queryWrapper.eq(DeviceAreaConfig::getDeviceType, queryDTO.getDeviceType().name());
}
return deviceAreaConfigRepository.page(queryDTO, queryWrapper);
if (Objects.nonNull(queryDTO.getAlertType())) {
queryWrapper.last(String.format(" and find_in_set('%s',%s) > 0", queryDTO.getAlertType().name(), DeviceAreaConfig.ALERT_TYPES));
}
Page<DeviceAreaConfig> pageConfig = deviceAreaConfigRepository.page(queryDTO, queryWrapper);
return this.convertPage(pageConfig);
}
private Page<DeviceAreaConfigPageDto> convertPage(Page<DeviceAreaConfig> pageConfig) {
List<Long> areaConfigIds = pageConfig.getRecords().stream().map(DeviceAreaConfig::getId).toList();
List<DeviceAreaConfigPageDto> pageList = Lists.newArrayListWithCapacity(pageConfig.getRecords().size());
Map<Long, String> areaIdMapTipIds = iAlertTipRepository.findBindAreaConfigId(areaConfigIds);
for (DeviceAreaConfig ac : pageConfig.getRecords()) {
pageList.add(new DeviceAreaConfigPageDto(ac, areaIdMapTipIds));
}
Page<DeviceAreaConfigPageDto> dtoPage = Page.of(pageConfig.getCurrent(), pageConfig.getSize(), pageConfig.getTotal());
dtoPage.setRecords(pageList);
return dtoPage;
}
public List<AreaFinder> findAlertType(Integer lineId, String alertType) {

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="club.joylink.xiannccda.mapper.AlertRecordMapper">
<select id="report" parameterType="club.joylink.xiannccda.dto.record.AlertRecordReportDTO" resultType="club.joylink.xiannccda.dto.record.AlertRecordReportResponseDTO">
select alert_type, count(1) as counter
from alert_record
where line_id = #{lineId}
<if test="alertTypes != null and alertTypes.size() > 0">
<foreach collection="alertTypes" open=" and alert_type in (" separator="," close=")" item="t">
#{t}
</foreach>
</if>
<choose>
<when test="beginDateTime != null">
and alert_time <![CDATA[>=]]> #{beginDateTime}
</when>
<when test="endDateTime != null">
and alert_time <![CDATA[<=]]> #{endDateTime}
</when>
</choose>
GROUP BY alert_type
</select>
</mapper>

View File

@ -0,0 +1,65 @@
package club.joylink.xiannccda;
import club.joylink.xiannccda.service.BaiduVoiceService;
import club.joylink.xiannccda.vo.BaseVoiceSynthesisVO;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import org.junit.jupiter.api.Test;
public class GenerateVoiceUtil {
public byte[] synthesisSource(BaseVoiceSynthesisVO vo) {
BaiduVoiceService voiceService = new BaiduVoiceService();
byte[] data = voiceService.voiceSynthesis(vo);
return data;
}
@Test
public void voiceService() {
BaseVoiceSynthesisVO svo = new BaseVoiceSynthesisVO("请注意,三号线,出现红光带");
byte[] data = this.synthesisSource(svo);
writeFile("d:\\voice\\red.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意,三号线,出现橙光带");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\orange.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意,三号线,出现大面积红光带");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\red-most.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意,三号线,出现大面积橙光带");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\orange-most.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意,三号线,道岔失表了");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\switch-lost.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意,三号线,出现大面积道岔失表");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\switch-lost-most.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意,三号线,站台屏蔽门无法打开");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\cannot-open.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意,三号线,站台屏蔽门无法关闭");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\cannot-close.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意,三号线,出现蓝显");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\blue.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意,三号线,出现全线");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\all-line-blue.mp3", data);
svo = new BaseVoiceSynthesisVO("请注意三号线列车禁制ATP切除");
data = this.synthesisSource(svo);
writeFile("d:\\voice\\atp-cut.mp3", data);
}
private static void writeFile(String path, byte[] data) {
try (BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(path))) {
outputStream.write(data);
} catch (Exception e) {
e.getMessage();
}
}
}

View File

@ -0,0 +1,53 @@
package club.joylink.xiannccda.service;
import club.joylink.xiannccda.dto.protos.AlertConstProto.AlertType;
import club.joylink.xiannccda.dto.record.AlertRecordReportDTO;
import club.joylink.xiannccda.dto.record.AlertRecordReportResponseDTO;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter.Feature;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class AlertRecordServiceTest {
@Autowired
private AlertRecordService alertRecordService;
@Test
public void reportEmpty() {
AlertRecordReportDTO dto = new AlertRecordReportDTO();
List<AlertRecordReportResponseDTO> list = this.alertRecordService.report(3, dto);
System.out.println(JSON.toJSONString(list, Feature.PrettyFormat, Feature.WriteEnumsUsingName));
}
@Test
public void reportType() {
AlertRecordReportDTO dto = new AlertRecordReportDTO();
dto.setAlertTypes(List.of(AlertType.AXLE_LED_RED));
List<AlertRecordReportResponseDTO> list = this.alertRecordService.report(3, dto);
System.out.println(JSON.toJSONString(list, Feature.PrettyFormat, Feature.WriteEnumsUsingName));
}
@Test
public void reportDate() {
AlertRecordReportDTO dto = new AlertRecordReportDTO();
// dto.setAlertTypes(List.of(AlertType.AXLE_LED_RED));
dto.setBeginDateTime(LocalDateTime.parse("2023-09-05 13:21:46", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
dto.setEndDateTime(LocalDateTime.now());
List<AlertRecordReportResponseDTO> list = this.alertRecordService.report(3, dto);
System.out.println(JSON.toJSONString(list, Feature.PrettyFormat, Feature.WriteEnumsUsingName));
}
@Test
public void reportErrorDate() {
LocalDateTime begin = LocalDateTime.parse("2023-09-05 13:21:46", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
LocalDateTime now = LocalDateTime.now();
System.out.println(begin.isBefore(now));
System.out.println(now.isBefore(begin));
}
}

View File

@ -0,0 +1,123 @@
package club.joylink.xiannccda.service;
import club.joylink.xiannccda.util.ConnUtil;
import club.joylink.xiannccda.util.TokenHolder;
import club.joylink.xiannccda.vo.BaseVoiceSynthesisVO;
import com.google.common.collect.Maps;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
/**
* 百度语音识别合成服务
*/
@Slf4j
public class BaiduVoiceService {
/**
* 错误码 用户输入/服务端 含义 一般解决方法 3300 用户输入错误 输入参数不正确 请参考技术文档及demo核对输入参数 3301 用户输入错误 音频质量过差或没有清晰语音 请上传包含语音的清晰音频 3302 用户输入错误 鉴权失败 token字段校验失败请使用正确的API_KEY
* SECRET_KEY生成或并发调用量超出限额或音频采样率不正确可尝试更换为16k采样率 3303 服务端问题 百度服务器后端繁忙 有可能是原始音频质量过差可以将api返回结果和原始音频反馈至工单论坛或者QQ群 3304 用户请求超限 用户的请求并发超限 请降低识别api请求频率账号内所有应用APPID共用并发限额
* 3305 用户请求超限 用户的日调用量日请求量超限 请开通付费购买调用量资源账号内所有应用APPID共用调用量限额 3307 服务端问题 语音服务器后端识别出错问题 有可能是原始音频质量过差可以将api返回结果和原始音频反馈至论坛或者QQ群 3308 用户输入错误 音频过长 音频时长不超过60s或音频base64后超过2MB请将音频时长截取为更短的音频
* 3309 用户输入错误 音频数据问题 服务端无法将音频转为pcm格式可能是长度问题音频格式问题等 请将输入的音频时长截取为60s以下并核对下音频的编码采样率16000单声道小端序16bits 3310 用户输入错误 输入的音频文件过大 语音文件共有3种输入方式 json 里的speech 参数base64后 直接post 二进制数据callback参数里url
* 分别对应三种情况json超过10M直接post的语音文件超过10Mcallback里回调url的音频文件超过10M 3311 用户输入错误 采样率rate参数不在选项里 目前rate参数仅支持16000填写其他值即会有此错误 3312 用户输入错误 音频格式format参数不在选项里 目前格式仅仅支持pcmwavamrm4a如填写mp3即会有此错误
* 3313 服务端问题 语音服务器解析超时 请将api返回结果反馈至工单论坛或者QQ群 3314 用户输入错误 音频长度过短 音频长度的len参数不能小于等于4 3315 服务端问题 语音服务器处理超时 请将api返回结果反馈至工单论坛或者QQ群 3316 用户输入错误 音频转为pcm失败 使用pcm格式或者确认wav和amr的采样率16000单声道
* wav文件需要是pcm编码小端序16bits
*
* @param errCode
* @return
*/
private String handleErrMsg(Integer errCode) {
String errMsg = "未知错误";
switch (errCode) {
case 3300:
case 3302:
case 3311:
case 3312:
case 3313:
case 3315:
errMsg = "参数错误或语音服务异常";
break;
case 3301:
case 3303:
case 3307:
errMsg = "音频质量过差或没有清晰语音或识别异常";
break;
case 3304:
log.error("百度语音识别请求并发超限");
errMsg = "参数错误或语音服务异常";
break;
case 3308:
case 3310:
case 3314:
errMsg = "音频时长过长或过短";
break;
case 3309:
case 3316:
errMsg = "音频格式错误";
break;
}
return errMsg;
}
/**
* spd 选填 语速取值0-15默认为5中语速 pit 选填 音调取值0-15默认为5中语调 vol 选填 音量取值0-15默认为5中音量取值为0时为音量最小值并非为无声 per基础音库 选填 度小宇=1度小美=0度逍遥基础=3度丫丫=4
* per精品音库 选填 度逍遥精品=5003度小鹿=5118度博文=106度小童=110度小萌=111度米朵=103度小娇=5 aue 选填 3为mp3格式(默认) 4为pcm-16k5为pcm-8k6为wav内容同pcm-16k; 注意aue=4或者6是语音识别要求的格式但是音频内容不是语音识别要求的自然人发音所以识别效果会受影响
*/
// private String aue = "6";
private Map<String, Object> toMapParam(BaseVoiceSynthesisVO vo) {
Map<String, Object> dataMap = Maps.newHashMap();
vo.checkBaiduParam();
dataMap.put("spd", vo.getSpd());
dataMap.put("pit", vo.getPit());
dataMap.put("vol", vo.getVol());
dataMap.put("pre", vo.getPer());
dataMap.put("aue", "3");
return dataMap;
}
public byte[] voiceSynthesis(BaseVoiceSynthesisVO vo) {
try {
byte[] data = this.run(vo, TokenHolder.getInstance().getToken());
return data;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public final static String TEXT_VOICE_URL = "http://tsn.baidu.com/text2audio"; // 可以使用https
private byte[] run(BaseVoiceSynthesisVO vo, String token) throws IOException {
StringBuilder sb = new StringBuilder("tex=");
// 此处2次urlencode 确保特殊字符被正确编码
sb.append(ConnUtil.urlEncode(ConnUtil.urlEncode(vo.getMessage())));
Map<String, Object> dataMap = toMapParam(vo);
dataMap.forEach((k, v) -> {
sb.append("&").append(k).append("=").append(v);
});
sb.append("&cuid=").append(TokenHolder.APP_ID);
sb.append("&tok=").append(token);
sb.append("&lan=zh&ctp=1");
HttpURLConnection conn = (HttpURLConnection) new URL(TEXT_VOICE_URL).openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setConnectTimeout(9000);
PrintWriter printWriter = new PrintWriter(conn.getOutputStream());
printWriter.write(sb.toString());
printWriter.close();
String contentType = conn.getContentType();
if (contentType.contains("audio/")) {
return ConnUtil.getResponseBytes(conn);
} else {
String res = ConnUtil.getResponseString(conn);
throw new RuntimeException(String.format("百度语音合成失败:%s", res));
}
}
}

View File

@ -0,0 +1,23 @@
package club.joylink.xiannccda.service;
import club.joylink.xiannccda.dto.config.DeviceAreaConfigQueryDto;
import club.joylink.xiannccda.dto.protos.AlertConstProto.AlertType;
import club.joylink.xiannccda.service.config.DeviceAreaConfigService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class DeviceAreaConfigServiceTest {
@Autowired
private DeviceAreaConfigService areaConfigService;
@Test
public void page() {
DeviceAreaConfigQueryDto dto = new DeviceAreaConfigQueryDto();
dto.setAlertType(AlertType.BLUE_DISPLAY);
// dto.setLineId(3);
this.areaConfigService.page(dto, 3);
}
}

View File

@ -0,0 +1,94 @@
package club.joylink.xiannccda.util;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
/**
* 与连接相关的Util类
*/
public class ConnUtil {
/**
* UrlEncode UTF-8 编码
*
* @param str 原始字符串
* @return
*/
public static String urlEncode(String str) {
String result = null;
try {
result = URLEncoder.encode(str, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return result;
}
/**
* 从HttpURLConnection 获取返回的字符串
*
* @param conn
* @return
* @throws IOException
*/
public static String getResponseString(HttpURLConnection conn) throws IOException {
return new String(getResponseBytes(conn));
}
/**
* 从HttpURLConnection 获取返回的bytes 注意 HttpURLConnection自身问题 400类错误会直接抛出异常不能获取conn.getInputStream();
*
* @param conn
* @return
* @throws IOException http请求错误
*/
public static byte[] getResponseBytes(HttpURLConnection conn) throws IOException {
int responseCode = conn.getResponseCode();
InputStream inputStream = conn.getInputStream();
if (responseCode != 200) {
System.err.println("http 请求返回的状态码错误期望200 当前是 " + responseCode);
if (responseCode == 401) {
System.err.println("可能是appkey appSecret 填错");
}
System.err.println("response headers" + conn.getHeaderFields());
if (inputStream == null) {
inputStream = conn.getErrorStream();
}
byte[] result = getInputStreamContent(inputStream);
System.err.println(new String(result));
throw new RuntimeException("接口调用异常:" + responseCode);
}
byte[] result = getInputStreamContent(inputStream);
return result;
}
/**
* 将InputStream内的内容全部读取作为bytes返回
*
* @param is
* @return
* @throws IOException @see InputStream.read()
*/
public static byte[] getInputStreamContent(InputStream is) throws IOException {
byte[] b = new byte[1024];
// 定义一个输出流存储接收到的数据
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 开始接收数据
int len = 0;
while (true) {
len = is.read(b);
if (len == -1) {
// 数据读完
break;
}
byteArrayOutputStream.write(b, 0, len);
}
return byteArrayOutputStream.toByteArray();
}
}

View File

@ -0,0 +1,144 @@
package club.joylink.xiannccda.util;
import com.alibaba.fastjson2.JSONObject;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
/**
* token的获取类 将apiKey和secretKey换取token注意有效期保存在expiresAt
*/
@Slf4j
public class TokenHolder {
public static final String APP_ID = "17048486";
static final String API_KEY = "owDE3FR55ZXpZzAATbBlwKZZ";
static final String SECRET_KEY = "oFjGfwXFkIGCrK8OntwfuxgzNK4k0oNH";
private static final String SCOPE = "brain_enhanced_asr";
static final TokenHolder holder = new TokenHolder(API_KEY, SECRET_KEY, SCOPE);
public static final String TTS_SCOPE = "audio_tts_post";
/**
* url , Token的urlhttp可以改为https
*/
private static final String url = "http://openapi.baidu.com/oauth/2.0/token";
/**
* asr的权限 scope "audio_voice_assistant_get" tts 的权限 scope "audio_tts_post"
*/
private String scope;
/**
* 网页上申请语音识别应用获取的apiKey
*/
private String apiKey;
/**
* 网页上申请语音识别应用获取的secretKey
*/
private String secretKey;
/**
* 保存访问接口获取的token
*/
private String token;
/**
* 当前的时间戳毫秒
*/
private long expiresAt;
/**
* @param apiKey 网页上申请语音识别应用获取的apiKey
* @param secretKey 网页上申请语音识别应用获取的secretKey
*/
private TokenHolder(String apiKey, String secretKey, String scope) {
this.apiKey = apiKey;
this.secretKey = secretKey;
this.scope = scope;
}
public static TokenHolder getInstance() {
return holder;
}
/**
* 获取tokenrefresh 方法后调用有效
*
* @return
*/
public String getToken() {
if (this.isExpire()) {
try {
this.resfresh();
} catch (IOException e) {
log.error("百度语音获取token失败。", e);
}
}
return token;
}
private boolean isExpire() {
return System.currentTimeMillis() > this.expiresAt;
}
/**
* 获取过期时间refresh 方法后调用有效
*
* @return
*/
public long getExpiresAt() {
return expiresAt;
}
/**
* 获取token
*
* @return
* @throws IOException http请求错误
*/
public void resfresh() throws IOException {
String getTokenURL = url + "?grant_type=client_credentials"
+ "&client_id=" + ConnUtil.urlEncode(apiKey) + "&client_secret=" + ConnUtil.urlEncode(secretKey);
// 打印的url出来放到浏览器内可以复现
log.debug("token url:" + getTokenURL);
URL url = new URL(getTokenURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
String result = ConnUtil.getResponseString(conn);
log.debug("Token result json:" + result);
parseJson(result);
}
/**
* @param result token接口获得的result result示例 { "access_token": "1.a6b7dbd428f731035f771b8d********.86400.1292922000-2346678-124328", "expires_in": 86400, "refresh_token":
* "2.385d55f8615fdfd9edb7c4b********.604800.1293440400-2346678-124328", "scope": "public audio_voice_assistant_get 。。。", "session_key": "ANXxSNjwQDugf8615Onqeik********CdlLxn",
* "session_secret": "248APxvxjCZ0VEC********aK4oZExMB", }
*/
private void parseJson(String result) {
Map<String, Object> map = JSONObject.parseObject(result, HashMap.class);
if (!map.containsKey("access_token")) {
// 返回没有access_token字段
throw new RuntimeException("access_token not obtained, " + result);
}
if (!map.containsKey("scope")) {
// 返回没有scope字段
throw new RuntimeException("scopenot obtained, " + result);
}
// scope = null, 忽略scope检查
if (scope != null && !((String) map.get("scope")).contains(scope)) {
throw new RuntimeException("scope not exist, " + scope + "," + result);
}
token = (String) map.get("access_token");
expiresAt = System.currentTimeMillis() + (Long.parseLong(map.get("expires_in").toString())) * 1000;
}
}

View File

@ -0,0 +1,108 @@
package club.joylink.xiannccda.vo;
import lombok.Data;
@Data
public class BaseVoiceSynthesisVO {
/**
* 消息
*/
private String message;
/**
* 语速
*/
private int spd;
/**
* 音调
*/
private int pit;
/**
* 音量
*/
private int vol;
/**
* 语音样式
*/
private String per;
/**
* 是否启用服务商的默认参数
*/
private boolean defaultParam = true;
public BaseVoiceSynthesisVO(String message) {
this.message = message;
}
public BaseVoiceSynthesisVO(String message, boolean defaultParam) {
this.message = message;
this.defaultParam = defaultParam;
}
/**
* spd 选填 语速取值0-15默认为5中语速 pit 选填 音调取值0-15默认为5中语调 vol 选填 音量取值0-15默认为5中音量取值为0时为音量最小值并非为无声 per基础音库 选填 度小宇=1度小美=0度逍遥基础=3度丫丫=4
* per精品音库 选填 度逍遥精品=5003度小鹿=5118度博文=106度小童=110度小萌=111度米朵=103度小娇=5 aue 选填 3为mp3格式(默认) 4为pcm-16k5为pcm-8k6为wav内容同pcm-16k; 注意aue=4或者6是语音识别要求的格式但是音频内容不是语音识别要求的自然人发音所以识别效果会受影响
*/
public void checkBaiduParam() {
if (this.spd < 0 || this.spd > 15) {
this.spd = 5;
}
if (this.pit < 0 || this.pit > 15) {
this.pit = 5;
}
if (this.vol < 0 || this.vol > 15) {
this.vol = 5;
}
if (this.defaultParam) {
this.spd = 5;
this.pit = 5;
this.vol = 10;
}
}
/* public SynthesisXunFei toBuildXunFei(){
if(this.spd <0 || this.spd > 100){
this.spd = 50;
}
if(this.pit < 0 || this.pit > 100){
this.pit = 50;
}
if(this.vol < 0 || this.vol> 100){
this.vol = 50;
}
SynthesisXunFei fei = new SynthesisXunFei(this.message);
return fei;
}*/
/* public static class SynthesisXunFei extends BaseVoiceSynthesisVO {
*//**
* voice_name 合成发音人 合成所需发音人,对应发音人参数可在控制台"发音人授权管理"查看
* speed 语速 通过此参数设置合成返回音频的语速默认值50取值范围:[0,100]
* volume 音量 通过此参数设置合成返回音频的音量默认值50取值范围:[0,100]
* pitch 语调 通过此参数设置合成返回音频的语调默认值50取值范围:[0,100]
* sample_rate 采样率 音频的采样率是音频属性的其中一个一般来说采样率越高音频的质量越好识别的匹配率越高但上传带宽消耗也越大 默认16KHZ取值{8KHZ,16KHZ}
* tts_audio_path 合成录音保存路径 通过此参数可以在合成完成后在本地保存一个音频文件
* engine_type 引擎类型 设置使用的引擎类型在线离线混合在线合成设置参数为"cloud"
*
*//*
private String engine_type = "cloud";
public SynthesisXunFei(String message) {
super(message);
}
@Override
public Map<String, Object> toMapParam() {
Map<String,Object> dataMap = Maps.newHashMap();
dataMap.put("speed",this.getSpd());
dataMap.put("volume",this.getVol());
dataMap.put("pitch",this.getPit());
dataMap.put("voice_name",this.getPer());
dataMap.put("engine_type",this.engine_type);
return dataMap;
}
}*/
}