mirror of
https://github.com/hpd840321/starRiverProperty.git
synced 2026-06-09 16:30:29 +08:00
chore: 工作区反编译与 Maven/文档/脚本同步到发布分支
- artifacts/decompiled 树与相关源码变更 - maven-cw-elevator-application 业务 docs 与 package-info - scripts 下 formatter 校验与辅助脚本 - 其他子工程/接口与发布线一并纳入版本控制 Made-with: Cursor Former-commit-id: e102e8cab64e575bcd23c9a66a598aa1892bb492
This commit is contained in:
+44
-44
@@ -1,44 +1,44 @@
|
||||
package cn.cloudwalk.client.davinci.portal.file.param.part;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** 分片追加参数(与存储层 append 调用约定一致)。 */
|
||||
public class FilePartAppendParam<T> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String filePath;
|
||||
private Integer partNumber;
|
||||
private String uploadId;
|
||||
private T content;
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public Integer getPartNumber() {
|
||||
return partNumber;
|
||||
}
|
||||
|
||||
public void setPartNumber(Integer partNumber) {
|
||||
this.partNumber = partNumber;
|
||||
}
|
||||
|
||||
public String getUploadId() {
|
||||
return uploadId;
|
||||
}
|
||||
|
||||
public void setUploadId(String uploadId) {
|
||||
this.uploadId = uploadId;
|
||||
}
|
||||
|
||||
public T getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(T content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
package cn.cloudwalk.client.davinci.portal.file.param.part;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** 分片追加参数(与存储层 append 调用约定一致)。 */
|
||||
public class FilePartAppendParam<T> implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String filePath;
|
||||
private Integer partNumber;
|
||||
private String uploadId;
|
||||
private T content;
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public Integer getPartNumber() {
|
||||
return partNumber;
|
||||
}
|
||||
|
||||
public void setPartNumber(Integer partNumber) {
|
||||
this.partNumber = partNumber;
|
||||
}
|
||||
|
||||
public String getUploadId() {
|
||||
return uploadId;
|
||||
}
|
||||
|
||||
public void setUploadId(String uploadId) {
|
||||
this.uploadId = uploadId;
|
||||
}
|
||||
|
||||
public T getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(T content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
|
||||
+44
-44
@@ -1,44 +1,44 @@
|
||||
package cn.cloudwalk.client.davinci.portal.file.param.part;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** 分片结束参数(与 PartFinishDTO 字段对齐)。 */
|
||||
public class FilePartFinishParam implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String uploadId;
|
||||
private Long fileSize;
|
||||
private String filePath;
|
||||
private Integer returnType;
|
||||
|
||||
public String getUploadId() {
|
||||
return uploadId;
|
||||
}
|
||||
|
||||
public void setUploadId(String uploadId) {
|
||||
this.uploadId = uploadId;
|
||||
}
|
||||
|
||||
public Long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(Long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public Integer getReturnType() {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
public void setReturnType(Integer returnType) {
|
||||
this.returnType = returnType;
|
||||
}
|
||||
}
|
||||
package cn.cloudwalk.client.davinci.portal.file.param.part;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** 分片结束参数(与 PartFinishDTO 字段对齐)。 */
|
||||
public class FilePartFinishParam implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String uploadId;
|
||||
private Long fileSize;
|
||||
private String filePath;
|
||||
private Integer returnType;
|
||||
|
||||
public String getUploadId() {
|
||||
return uploadId;
|
||||
}
|
||||
|
||||
public void setUploadId(String uploadId) {
|
||||
this.uploadId = uploadId;
|
||||
}
|
||||
|
||||
public Long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(Long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public Integer getReturnType() {
|
||||
return returnType;
|
||||
}
|
||||
|
||||
public void setReturnType(Integer returnType) {
|
||||
this.returnType = returnType;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -1,17 +1,17 @@
|
||||
package cn.cloudwalk.client.davinci.portal.file.param.part;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** 分片上传初始化参数(与 PartInitDTO 字段对齐,供 BeanCopy 与业务层使用)。 */
|
||||
public class FilePartInitParam implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String fileName;
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
}
|
||||
package cn.cloudwalk.client.davinci.portal.file.param.part;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** 分片上传初始化参数(与 PartInitDTO 字段对齐,供 BeanCopy 与业务层使用)。 */
|
||||
public class FilePartInitParam implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String fileName;
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
}
|
||||
|
||||
+26
-26
@@ -1,26 +1,26 @@
|
||||
package cn.cloudwalk.client.davinci.portal.file.result;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** 分片初始化/追加返回(与 PartInitResultDTO 字段对齐)。 */
|
||||
public class FilePartResult implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String filePath;
|
||||
private String uploadId;
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public String getUploadId() {
|
||||
return uploadId;
|
||||
}
|
||||
|
||||
public void setUploadId(String uploadId) {
|
||||
this.uploadId = uploadId;
|
||||
}
|
||||
}
|
||||
package cn.cloudwalk.client.davinci.portal.file.result;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/** 分片初始化/追加返回(与 PartInitResultDTO 字段对齐)。 */
|
||||
public class FilePartResult implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private String filePath;
|
||||
private String uploadId;
|
||||
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
public void setFilePath(String filePath) {
|
||||
this.filePath = filePath;
|
||||
}
|
||||
|
||||
public String getUploadId() {
|
||||
return uploadId;
|
||||
}
|
||||
|
||||
public void setUploadId(String uploadId) {
|
||||
this.uploadId = uploadId;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -16,8 +16,7 @@ public interface AcsElevatorCodeService {
|
||||
AcsElevatorCodeResultDTO getFirstByParentId(String paramString) throws ServiceException;
|
||||
|
||||
/**
|
||||
* 按区域 ID 批量查询电梯编码,供树形接口一次拉取,避免循环内逐条查询。
|
||||
* 不改变 {@link #get} 语义;入参去重由调用方控制。
|
||||
* 按区域 ID 批量查询电梯编码,供树形接口一次拉取,避免循环内逐条查询。 不改变 {@link #get} 语义;入参去重由调用方控制。
|
||||
*/
|
||||
Map<String, AcsElevatorCodeResultDTO> mapByZoneIds(List<String> zoneIds) throws ServiceException;
|
||||
}
|
||||
|
||||
+3
@@ -10,6 +10,9 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 设备相关服务的抽象基类:提供区域树扁平化、以及为远程调用组装的 {@link CloudwalkCallContext}(含 Feign 线程本地传参)。
|
||||
*/
|
||||
public class AbstractAcsDeviceService extends AbstractCloudwalkService {
|
||||
protected void getAreaMap(List<AreaTreeResult> areaTreeResultList, Map<String, String> areaMap) {
|
||||
for (AreaTreeResult areaTree : areaTreeResultList) {
|
||||
|
||||
+27
-27
@@ -1,27 +1,27 @@
|
||||
package cn.cloudwalk.elevator.common;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
@Configuration
|
||||
public class ElevatorRemoteIoExecutorConfig {
|
||||
@Autowired
|
||||
private ElevatorRemoteIoPoolProperties elevatorRemoteIoPoolProperties;
|
||||
|
||||
@Bean(name = {"elevatorRemoteBoundedExecutor"})
|
||||
public ThreadPoolTaskExecutor elevatorRemoteBoundedExecutor() {
|
||||
ThreadPoolTaskExecutor ex = new ThreadPoolTaskExecutor();
|
||||
ex.setCorePoolSize(this.elevatorRemoteIoPoolProperties.getCorePoolSize());
|
||||
ex.setMaxPoolSize(this.elevatorRemoteIoPoolProperties.getMaxPoolSize());
|
||||
ex.setQueueCapacity(this.elevatorRemoteIoPoolProperties.getQueueCapacity());
|
||||
ex.setKeepAliveSeconds(this.elevatorRemoteIoPoolProperties.getKeepAliveSeconds());
|
||||
ex.setAllowCoreThreadTimeOut(this.elevatorRemoteIoPoolProperties.isAllowCoreThreadTimeOut());
|
||||
ex.setThreadNamePrefix("elevator-remote-io-");
|
||||
ex.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
ex.initialize();
|
||||
return ex;
|
||||
}
|
||||
}
|
||||
package cn.cloudwalk.elevator.common;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
@Configuration
|
||||
public class ElevatorRemoteIoExecutorConfig {
|
||||
@Autowired
|
||||
private ElevatorRemoteIoPoolProperties elevatorRemoteIoPoolProperties;
|
||||
|
||||
@Bean(name = {"elevatorRemoteBoundedExecutor"})
|
||||
public ThreadPoolTaskExecutor elevatorRemoteBoundedExecutor() {
|
||||
ThreadPoolTaskExecutor ex = new ThreadPoolTaskExecutor();
|
||||
ex.setCorePoolSize(this.elevatorRemoteIoPoolProperties.getCorePoolSize());
|
||||
ex.setMaxPoolSize(this.elevatorRemoteIoPoolProperties.getMaxPoolSize());
|
||||
ex.setQueueCapacity(this.elevatorRemoteIoPoolProperties.getQueueCapacity());
|
||||
ex.setKeepAliveSeconds(this.elevatorRemoteIoPoolProperties.getKeepAliveSeconds());
|
||||
ex.setAllowCoreThreadTimeOut(this.elevatorRemoteIoPoolProperties.isAllowCoreThreadTimeOut());
|
||||
ex.setThreadNamePrefix("elevator-remote-io-");
|
||||
ex.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
ex.initialize();
|
||||
return ex;
|
||||
}
|
||||
}
|
||||
|
||||
+55
-55
@@ -1,55 +1,55 @@
|
||||
package cn.cloudwalk.elevator.common;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "ninca.elevator.remote-io.pool")
|
||||
public class ElevatorRemoteIoPoolProperties {
|
||||
/** 约定 §3.2 / §3.3 / §3.4:有界并行建议 4~8,默认 6 */
|
||||
private int corePoolSize = 6;
|
||||
private int maxPoolSize = 6;
|
||||
private int queueCapacity = 512;
|
||||
private int keepAliveSeconds = 60;
|
||||
private boolean allowCoreThreadTimeOut = true;
|
||||
|
||||
public int getCorePoolSize() {
|
||||
return this.corePoolSize;
|
||||
}
|
||||
|
||||
public void setCorePoolSize(int corePoolSize) {
|
||||
this.corePoolSize = corePoolSize;
|
||||
}
|
||||
|
||||
public int getMaxPoolSize() {
|
||||
return this.maxPoolSize;
|
||||
}
|
||||
|
||||
public void setMaxPoolSize(int maxPoolSize) {
|
||||
this.maxPoolSize = maxPoolSize;
|
||||
}
|
||||
|
||||
public int getQueueCapacity() {
|
||||
return this.queueCapacity;
|
||||
}
|
||||
|
||||
public void setQueueCapacity(int queueCapacity) {
|
||||
this.queueCapacity = queueCapacity;
|
||||
}
|
||||
|
||||
public int getKeepAliveSeconds() {
|
||||
return this.keepAliveSeconds;
|
||||
}
|
||||
|
||||
public void setKeepAliveSeconds(int keepAliveSeconds) {
|
||||
this.keepAliveSeconds = keepAliveSeconds;
|
||||
}
|
||||
|
||||
public boolean isAllowCoreThreadTimeOut() {
|
||||
return this.allowCoreThreadTimeOut;
|
||||
}
|
||||
|
||||
public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
|
||||
this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
|
||||
}
|
||||
}
|
||||
package cn.cloudwalk.elevator.common;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "ninca.elevator.remote-io.pool")
|
||||
public class ElevatorRemoteIoPoolProperties {
|
||||
/** 约定 §3.2 / §3.3 / §3.4:有界并行建议 4~8,默认 6 */
|
||||
private int corePoolSize = 6;
|
||||
private int maxPoolSize = 6;
|
||||
private int queueCapacity = 512;
|
||||
private int keepAliveSeconds = 60;
|
||||
private boolean allowCoreThreadTimeOut = true;
|
||||
|
||||
public int getCorePoolSize() {
|
||||
return this.corePoolSize;
|
||||
}
|
||||
|
||||
public void setCorePoolSize(int corePoolSize) {
|
||||
this.corePoolSize = corePoolSize;
|
||||
}
|
||||
|
||||
public int getMaxPoolSize() {
|
||||
return this.maxPoolSize;
|
||||
}
|
||||
|
||||
public void setMaxPoolSize(int maxPoolSize) {
|
||||
this.maxPoolSize = maxPoolSize;
|
||||
}
|
||||
|
||||
public int getQueueCapacity() {
|
||||
return this.queueCapacity;
|
||||
}
|
||||
|
||||
public void setQueueCapacity(int queueCapacity) {
|
||||
this.queueCapacity = queueCapacity;
|
||||
}
|
||||
|
||||
public int getKeepAliveSeconds() {
|
||||
return this.keepAliveSeconds;
|
||||
}
|
||||
|
||||
public void setKeepAliveSeconds(int keepAliveSeconds) {
|
||||
this.keepAliveSeconds = keepAliveSeconds;
|
||||
}
|
||||
|
||||
public boolean isAllowCoreThreadTimeOut() {
|
||||
return this.allowCoreThreadTimeOut;
|
||||
}
|
||||
|
||||
public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
|
||||
this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
|
||||
}
|
||||
}
|
||||
|
||||
+211
-83
@@ -1,6 +1,7 @@
|
||||
package cn.cloudwalk.elevator.device.impl;
|
||||
|
||||
import cn.cloudwalk.cloud.context.CloudwalkCallContext;
|
||||
import cn.cloudwalk.cloud.exception.DataAccessException;
|
||||
import cn.cloudwalk.cloud.exception.ServiceException;
|
||||
import cn.cloudwalk.cloud.result.CloudwalkResult;
|
||||
import cn.cloudwalk.elevator.common.AbstractAcsDeviceService;
|
||||
@@ -14,23 +15,37 @@ import cn.cloudwalk.elevator.passrule.dto.AcsPassRuleDeleteDto;
|
||||
import cn.cloudwalk.elevator.passrule.dto.AcsPassRuleImageResultDto;
|
||||
import cn.cloudwalk.elevator.passrule.param.AcsPassRuleDeleteParam;
|
||||
import cn.cloudwalk.elevator.passrule.param.AcsPassRuleNewParam;
|
||||
import cn.cloudwalk.elevator.config.FeignThreadLocalUtil;
|
||||
import cn.cloudwalk.elevator.passrule.service.ImageRuleRefService;
|
||||
import cn.cloudwalk.elevator.person.param.AcsPersonAddParam;
|
||||
import cn.cloudwalk.elevator.person.param.AcsPersonDeleteParam;
|
||||
import cn.cloudwalk.elevator.person.service.PersonRuleService;
|
||||
import cn.cloudwalk.elevator.util.CollectionUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* 设备异步任务:推进绑定进度、按楼层增删人员/规则;{@code updateFloors} 在楼层维度使用有界线程池并行远程调用,并按 Future 完成顺序推进
|
||||
* {@code bindDevices},与走查约定 §9 一致。
|
||||
*/
|
||||
@Service
|
||||
public class AcsDeviceTaskServiceImpl extends AbstractAcsDeviceService implements AcsDeviceTaskService {
|
||||
/** 单次并发执行的楼层数上限,与 {@code elevatorRemoteBoundedExecutor} 池容量配合,避免对下游突发压测。 */
|
||||
private static final int UPDATE_FLOORS_FLOOR_PARALLEL = 6;
|
||||
|
||||
@Autowired
|
||||
private PersonRuleService personRuleService;
|
||||
@Autowired
|
||||
@@ -39,107 +54,220 @@ public class AcsDeviceTaskServiceImpl extends AbstractAcsDeviceService implement
|
||||
private AcsDeviceTaskDao acsDeviceTaskDao;
|
||||
@Resource
|
||||
private ImageRuleRefDao imageRuleRefDao;
|
||||
@Autowired
|
||||
@Qualifier("elevatorRemoteBoundedExecutor")
|
||||
private ThreadPoolTaskExecutor elevatorRemoteBoundedExecutor;
|
||||
|
||||
@Async("updateFloorsExecutor")
|
||||
public void updateFloors(AcsRestructureBindingParam param, List<AcsPassRuleImageResultDto> addFloors,
|
||||
List<String> delFloorIds, CloudwalkCallContext context) throws ServiceException {
|
||||
try {
|
||||
if (!CollectionUtils.isEmpty(addFloors)) {
|
||||
for (AcsPassRuleImageResultDto addFloor : addFloors) {
|
||||
AcsDeviceTaskDTO task = this.acsDeviceTaskDao.getById(param.getTaskId());
|
||||
if (task == null) {
|
||||
this.logger.error("updateFloors 任务不存在 taskId={}", param.getTaskId());
|
||||
throw new ServiceException("设备任务不存在");
|
||||
}
|
||||
if (task.getIsStop().intValue() == 0) {
|
||||
if (!ObjectUtils.isEmpty(param.getPersonId())) {
|
||||
AcsPersonAddParam addParam = new AcsPersonAddParam();
|
||||
addParam.setPersonIds(Collections.singletonList(param.getPersonId()));
|
||||
addParam.setParentId(param.getParentId());
|
||||
addParam.setZoneId(addFloor.getZoneId());
|
||||
addParam.setZoneName(addFloor.getZoneName());
|
||||
CloudwalkResult<Boolean> addResult = this.personRuleService.add(addParam, context);
|
||||
requireTaskStepSuccess(addResult, "personRuleService.add");
|
||||
} else {
|
||||
AcsPassRuleNewParam ruleParam = new AcsPassRuleNewParam();
|
||||
ruleParam.setParentId(param.getParentId());
|
||||
ruleParam.setZoneId(addFloor.getZoneId());
|
||||
ruleParam.setZoneName(addFloor.getZoneName());
|
||||
if (!ObjectUtils.isEmpty(param.getLabelId())) {
|
||||
ruleParam.setIncludeLabels(Collections.singletonList(param.getLabelId()));
|
||||
ruleParam.setRuleName(addFloor.getZoneName() + param.getLabelName());
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(param.getOrgId())) {
|
||||
ruleParam.setIncludeOrganizations(Collections.singletonList(param.getOrgId()));
|
||||
ruleParam.setRuleName(addFloor.getZoneName() + param.getOrgName());
|
||||
}
|
||||
CloudwalkResult<Boolean> addRuleResult =
|
||||
this.imageRuleRefService.addOnlyRule(ruleParam, context);
|
||||
requireTaskStepSuccess(addRuleResult, "imageRuleRefService.addOnlyRule");
|
||||
}
|
||||
AcsDeviceTaskAddDto addDto = new AcsDeviceTaskAddDto();
|
||||
addDto.setId(task.getId());
|
||||
addDto.setBindDevices(Integer.valueOf(task.getBindDevices().intValue() + 1));
|
||||
this.acsDeviceTaskDao.updateBingDevices(addDto);
|
||||
}
|
||||
}
|
||||
runAddFloorsInBoundedParallel(param, addFloors, context);
|
||||
}
|
||||
if (!CollectionUtils.isEmpty(delFloorIds)) {
|
||||
List<AcsPassRuleImageResultDto> ruleList = this.imageRuleRefDao.listZoneInfoByIds(delFloorIds);
|
||||
Map<String, String> ruleMap = new HashMap<>();
|
||||
ruleList.forEach(rule -> ruleMap.put(rule.getZoneId(), rule.getZoneName()));
|
||||
for (String delFloorId : delFloorIds) {
|
||||
AcsDeviceTaskDTO task = this.acsDeviceTaskDao.getById(param.getTaskId());
|
||||
if (task == null) {
|
||||
this.logger.error("updateFloors 任务不存在 taskId={}", param.getTaskId());
|
||||
throw new ServiceException("设备任务不存在");
|
||||
}
|
||||
if (task.getIsStop().intValue() == 0) {
|
||||
if (!ObjectUtils.isEmpty(param.getPersonId())) {
|
||||
AcsPersonDeleteParam delParam = new AcsPersonDeleteParam();
|
||||
delParam.setParentId(param.getParentId());
|
||||
delParam.setZoneId(delFloorId);
|
||||
delParam.setPersonIds(Collections.singletonList(param.getPersonId()));
|
||||
CloudwalkResult<Boolean> delResult = this.personRuleService.delete(delParam, context);
|
||||
requireTaskStepSuccess(delResult, "personRuleService.delete");
|
||||
} else {
|
||||
String ruleName = "";
|
||||
if (!ObjectUtils.isEmpty(param.getLabelName())) {
|
||||
ruleName = (String)ruleMap.get(delFloorId) + param.getLabelName();
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(param.getOrgName())) {
|
||||
ruleName = (String)ruleMap.get(delFloorId) + param.getOrgName();
|
||||
}
|
||||
String ruleId = this.imageRuleRefDao.getByRuleName(ruleName, delFloorId);
|
||||
if (!ObjectUtils.isEmpty(ruleId)) {
|
||||
AcsPassRuleDeleteParam deleteParam = new AcsPassRuleDeleteParam();
|
||||
deleteParam.setIds(Collections.singletonList(ruleId));
|
||||
deleteParam.setZoneId(delFloorId);
|
||||
deleteParam.setParentId(param.getParentId());
|
||||
CloudwalkResult<Boolean> delRuleResult =
|
||||
this.imageRuleRefService.delete(deleteParam, context);
|
||||
requireTaskStepSuccess(delRuleResult, "imageRuleRefService.delete");
|
||||
} else {
|
||||
AcsPassRuleDeleteDto dto = new AcsPassRuleDeleteDto();
|
||||
dto.setZoneId(delFloorId);
|
||||
dto.setLabelId(param.getLabelId());
|
||||
dto.setOrgId(param.getOrgId());
|
||||
this.imageRuleRefDao.deleteByOrgAndLabel(dto);
|
||||
}
|
||||
}
|
||||
AcsDeviceTaskAddDto addDto = new AcsDeviceTaskAddDto();
|
||||
addDto.setId(task.getId());
|
||||
addDto.setBindDevices(Integer.valueOf(task.getBindDevices().intValue() + 1));
|
||||
this.acsDeviceTaskDao.updateBingDevices(addDto);
|
||||
}
|
||||
}
|
||||
runDelFloorsInBoundedParallel(param, delFloorIds, ruleMap, context);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
this.logger.error("处理设备任务失败,失败原因:{}", e);
|
||||
if (e instanceof ServiceException) {
|
||||
throw (ServiceException)e;
|
||||
}
|
||||
throw new ServiceException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 约定 §3.5:楼层级有界并行发起远程调用;本方法内按原列表顺序 {@code get()} Future,
|
||||
* 与串行时一致地「每成功一层 → 重读任务行并 BIND_DEVICES+1」。
|
||||
*/
|
||||
private void runAddFloorsInBoundedParallel(AcsRestructureBindingParam param, List<AcsPassRuleImageResultDto> addFloors,
|
||||
CloudwalkCallContext context) throws ServiceException {
|
||||
for (int i = 0; i < addFloors.size(); i += UPDATE_FLOORS_FLOOR_PARALLEL) {
|
||||
int end = Math.min(i + UPDATE_FLOORS_FLOOR_PARALLEL, addFloors.size());
|
||||
List<Callable<Integer>> batch = new ArrayList<>();
|
||||
for (int j = i; j < end; j++) {
|
||||
final AcsPassRuleImageResultDto addFloor = addFloors.get(j);
|
||||
batch.add(
|
||||
() -> FeignThreadLocalUtil.callWithContext(context, () -> addOneFloorStep(addFloor, param, context)));
|
||||
}
|
||||
List<Future<Integer>> futures;
|
||||
try {
|
||||
futures = this.elevatorRemoteBoundedExecutor.getThreadPoolExecutor().invokeAll(batch);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ServiceException("76260540", "updateFloors 被中断");
|
||||
}
|
||||
for (Future<Integer> f : futures) {
|
||||
int inc;
|
||||
try {
|
||||
inc = f.get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ServiceException("76260540", "updateFloors 被中断");
|
||||
} catch (ExecutionException e) {
|
||||
Throwable c = e.getCause();
|
||||
if (c instanceof ServiceException) {
|
||||
throw (ServiceException)c;
|
||||
}
|
||||
throw new ServiceException(c != null ? c.getMessage() : e.getMessage());
|
||||
}
|
||||
if (inc > 0) {
|
||||
advanceBindProgressOne(param.getTaskId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void runDelFloorsInBoundedParallel(AcsRestructureBindingParam param, List<String> delFloorIds,
|
||||
Map<String, String> ruleMap, CloudwalkCallContext context) throws ServiceException {
|
||||
for (int i = 0; i < delFloorIds.size(); i += UPDATE_FLOORS_FLOOR_PARALLEL) {
|
||||
int end = Math.min(i + UPDATE_FLOORS_FLOOR_PARALLEL, delFloorIds.size());
|
||||
List<Callable<Integer>> batch = new ArrayList<>();
|
||||
for (int j = i; j < end; j++) {
|
||||
final String delFloorId = delFloorIds.get(j);
|
||||
batch.add(
|
||||
() -> FeignThreadLocalUtil.callWithContext(context, () -> delOneFloorStep(delFloorId, param, ruleMap, context)));
|
||||
}
|
||||
List<Future<Integer>> futures;
|
||||
try {
|
||||
futures = this.elevatorRemoteBoundedExecutor.getThreadPoolExecutor().invokeAll(batch);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ServiceException("76260540", "updateFloors 被中断");
|
||||
}
|
||||
for (Future<Integer> f : futures) {
|
||||
int inc;
|
||||
try {
|
||||
inc = f.get();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ServiceException("76260540", "updateFloors 被中断");
|
||||
} catch (ExecutionException e) {
|
||||
Throwable c = e.getCause();
|
||||
if (c instanceof ServiceException) {
|
||||
throw (ServiceException)c;
|
||||
}
|
||||
throw new ServiceException(c != null ? c.getMessage() : e.getMessage());
|
||||
}
|
||||
if (inc > 0) {
|
||||
advanceBindProgressOne(param.getTaskId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceBindProgressOne(String taskId) throws ServiceException {
|
||||
AcsDeviceTaskDTO task = this.acsDeviceTaskDao.getById(taskId);
|
||||
if (task == null) {
|
||||
this.logger.error("updateFloors 任务不存在 taskId={}", taskId);
|
||||
throw new ServiceException("设备任务不存在");
|
||||
}
|
||||
AcsDeviceTaskAddDto addDto = new AcsDeviceTaskAddDto();
|
||||
addDto.setId(task.getId());
|
||||
addDto.setBindDevices(Integer.valueOf(task.getBindDevices().intValue() + 1));
|
||||
this.acsDeviceTaskDao.updateBingDevices(addDto);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 1 本层已执行远程步骤且应推进 bind 计数;0 任务已停止跳过
|
||||
*/
|
||||
private int addOneFloorStep(AcsPassRuleImageResultDto addFloor, AcsRestructureBindingParam param,
|
||||
CloudwalkCallContext context) throws ServiceException {
|
||||
AcsDeviceTaskDTO task = this.acsDeviceTaskDao.getById(param.getTaskId());
|
||||
if (task == null) {
|
||||
this.logger.error("updateFloors 任务不存在 taskId={}", param.getTaskId());
|
||||
throw new ServiceException("设备任务不存在");
|
||||
}
|
||||
if (task.getIsStop().intValue() != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(param.getPersonId())) {
|
||||
AcsPersonAddParam addParam = new AcsPersonAddParam();
|
||||
addParam.setPersonIds(Collections.singletonList(param.getPersonId()));
|
||||
addParam.setParentId(param.getParentId());
|
||||
addParam.setZoneId(addFloor.getZoneId());
|
||||
addParam.setZoneName(addFloor.getZoneName());
|
||||
CloudwalkResult<Boolean> addResult = this.personRuleService.add(addParam, context);
|
||||
requireTaskStepSuccess(addResult, "personRuleService.add");
|
||||
} else {
|
||||
AcsPassRuleNewParam ruleParam = new AcsPassRuleNewParam();
|
||||
ruleParam.setParentId(param.getParentId());
|
||||
ruleParam.setZoneId(addFloor.getZoneId());
|
||||
ruleParam.setZoneName(addFloor.getZoneName());
|
||||
if (!ObjectUtils.isEmpty(param.getLabelId())) {
|
||||
ruleParam.setIncludeLabels(Collections.singletonList(param.getLabelId()));
|
||||
ruleParam.setRuleName(addFloor.getZoneName() + param.getLabelName());
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(param.getOrgId())) {
|
||||
ruleParam.setIncludeOrganizations(Collections.singletonList(param.getOrgId()));
|
||||
ruleParam.setRuleName(addFloor.getZoneName() + param.getOrgName());
|
||||
}
|
||||
CloudwalkResult<Boolean> addRuleResult = this.imageRuleRefService.addOnlyRule(ruleParam, context);
|
||||
requireTaskStepSuccess(addRuleResult, "imageRuleRefService.addOnlyRule");
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private int delOneFloorStep(String delFloorId, AcsRestructureBindingParam param, Map<String, String> ruleMap,
|
||||
CloudwalkCallContext context) throws ServiceException {
|
||||
AcsDeviceTaskDTO task = this.acsDeviceTaskDao.getById(param.getTaskId());
|
||||
if (task == null) {
|
||||
this.logger.error("updateFloors 任务不存在 taskId={}", param.getTaskId());
|
||||
throw new ServiceException("设备任务不存在");
|
||||
}
|
||||
if (task.getIsStop().intValue() != 0) {
|
||||
return 0;
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(param.getPersonId())) {
|
||||
AcsPersonDeleteParam delParam = new AcsPersonDeleteParam();
|
||||
delParam.setParentId(param.getParentId());
|
||||
delParam.setZoneId(delFloorId);
|
||||
delParam.setPersonIds(Collections.singletonList(param.getPersonId()));
|
||||
CloudwalkResult<Boolean> delResult = this.personRuleService.delete(delParam, context);
|
||||
requireTaskStepSuccess(delResult, "personRuleService.delete");
|
||||
} else {
|
||||
String baseName = ruleMap.getOrDefault(delFloorId, "");
|
||||
String ruleName = "";
|
||||
if (!ObjectUtils.isEmpty(param.getLabelName())) {
|
||||
ruleName = baseName + param.getLabelName();
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(param.getOrgName())) {
|
||||
ruleName = baseName + param.getOrgName();
|
||||
}
|
||||
String ruleId;
|
||||
try {
|
||||
ruleId = this.imageRuleRefDao.getByRuleName(ruleName, delFloorId);
|
||||
} catch (DataAccessException e) {
|
||||
this.logger.error("updateFloors getByRuleName 失败 delFloorId={} {}", delFloorId, e.getMessage());
|
||||
throw new ServiceException("76260540", e.getMessage());
|
||||
}
|
||||
if (!ObjectUtils.isEmpty(ruleId)) {
|
||||
AcsPassRuleDeleteParam deleteParam = new AcsPassRuleDeleteParam();
|
||||
deleteParam.setIds(Collections.singletonList(ruleId));
|
||||
deleteParam.setZoneId(delFloorId);
|
||||
deleteParam.setParentId(param.getParentId());
|
||||
CloudwalkResult<Boolean> delRuleResult = this.imageRuleRefService.delete(deleteParam, context);
|
||||
requireTaskStepSuccess(delRuleResult, "imageRuleRefService.delete");
|
||||
} else {
|
||||
AcsPassRuleDeleteDto dto = new AcsPassRuleDeleteDto();
|
||||
dto.setZoneId(delFloorId);
|
||||
dto.setLabelId(param.getLabelId());
|
||||
dto.setOrgId(param.getOrgId());
|
||||
try {
|
||||
this.imageRuleRefDao.deleteByOrgAndLabel(dto);
|
||||
} catch (DataAccessException e) {
|
||||
this.logger.error("updateFloors deleteByOrgAndLabel 失败 delFloorId={} {}", delFloorId, e.getMessage());
|
||||
throw new ServiceException("76260540", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 约定 §2.2:异步任务内对业务服务返回的 {@link CloudwalkResult} 须校验成功后再推进进度(避免失败仍递增 bindDevices)。
|
||||
*/
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 设备域服务层:电梯设备查询/编辑、设备任务(含楼层变更)、设备侧设置与图库应用绑定等编排。
|
||||
* <p>
|
||||
* 同包名在 data 模块中承担 DAO/Mapper;此处仅放接口、入参出参与 {@code impl} 实现,表结构见 data 包说明。
|
||||
*/
|
||||
package cn.cloudwalk.elevator.device;
|
||||
+9
@@ -8,9 +8,18 @@ import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
/**
|
||||
* 设备第三方 MQTT 发布入口:向指定 topic 推送 JSON 载荷(由 {@code /mqtt/publish} 落到底层 broker)。
|
||||
*/
|
||||
@FeignClient(name = "${feign.mqtt.name:cloudwalk-device-thirdparty}", path = "/mqtt",
|
||||
fallback = MqttFeignClientFallback.class)
|
||||
public interface MqttFeignClient {
|
||||
/**
|
||||
* 发布一条 MQTT 消息。
|
||||
*
|
||||
* @param paramMqttSendMessageParam topic 与 body(通常为业务侧 JSON 字符串)
|
||||
* @return 是否投递成功,由下游服务定义成功语义
|
||||
*/
|
||||
@RequestMapping(value = {"/publish"}, method = {RequestMethod.POST})
|
||||
CloudwalkResult<Boolean> publish(MqttSendMessageParam paramMqttSendMessageParam) throws ServiceException;
|
||||
}
|
||||
|
||||
+8
@@ -6,8 +6,16 @@ import cn.cloudwalk.elevator.mqtt.client.MqttFeignClient;
|
||||
import cn.cloudwalk.elevator.mqtt.param.MqttSendMessageParam;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link MqttFeignClient} 熔断/降级:调用失败时抛出运行时异常,促使上层按失败处理(不伪造成功投递)。
|
||||
*/
|
||||
@Component
|
||||
public class MqttFeignClientFallback implements MqttFeignClient {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* @implSpec 不返回降级业务结果,直接抛错以暴露下游不可用
|
||||
*/
|
||||
@Override
|
||||
public CloudwalkResult<Boolean> publish(MqttSendMessageParam param) throws ServiceException {
|
||||
throw new RuntimeException("mqtt发送数据失败");
|
||||
}
|
||||
|
||||
+14
-2
@@ -23,10 +23,16 @@ import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link MqttService} 实现:先短暂休眠以便识别明细入库,再按通行流水查人名、访客标签,向 {@code businessId + ELEVATOR_RECORD_SUFFIX} topic 推送 JSON。
|
||||
*/
|
||||
@Component
|
||||
public class MqttServiceImpl extends AbstractAcsDeviceService implements MqttService {
|
||||
/** 人员标签中表示「访客」的编码,与识别记录里 {@code personLabelIds} 包含关系判断一致。 */
|
||||
private static final String VISITOR_LABEL_CODE = "1";
|
||||
/** 与 {@link #VISITOR_LABEL_CODE} 对应的展示名,当前实现未参与逻辑,仅作文档对齐。 */
|
||||
private static final String VISITOR_LABEL_NAME = "访客";
|
||||
/** MQTT topic 后缀,与 {@code businessId} 拼接为完整 topic。 */
|
||||
private static final String ELEVATOR_RECORD_SUFFIX = "_elevator_record";
|
||||
@Qualifier("cn.cloudwalk.elevator.mqtt.client.MqttFeignClient")
|
||||
@Resource
|
||||
@@ -34,6 +40,12 @@ public class MqttServiceImpl extends AbstractAcsDeviceService implements MqttSer
|
||||
@Resource
|
||||
private AcsRecogRecordDao acsRecogRecordDao;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* 异常在方法内记录日志后吞掉(除中断转 {@link ServiceException}),避免异步线程因下游偶发错误反复未捕获终止。
|
||||
*/
|
||||
@Override
|
||||
@Async
|
||||
public void sendInfoToOne(AcsElevatorRecordAddDTO addDTO) throws ServiceException {
|
||||
this.logger.info("防止人员识别记录未入库即开始推送消息,休眠10秒");
|
||||
@@ -56,11 +68,11 @@ public class MqttServiceImpl extends AbstractAcsDeviceService implements MqttSer
|
||||
acsElevatorRecordMqttParam.setOpenDoorId(addDTO.getId());
|
||||
acsElevatorRecordMqttParam.setPersonName(acsRecogRecordResultDTO.getPersonName());
|
||||
if (StringUtils.isNotBlank(acsRecogRecordResultDTO.getPersonLabelIds())
|
||||
&& acsRecogRecordResultDTO.getPersonLabelIds().contains("1")) {
|
||||
&& acsRecogRecordResultDTO.getPersonLabelIds().contains(VISITOR_LABEL_CODE)) {
|
||||
acsElevatorRecordMqttParam.setIsVisitor(Boolean.TRUE);
|
||||
}
|
||||
CloudwalkResult<Boolean> publish = this.mqttFeignClient
|
||||
.publish(MqttSendMessageParam.builder().topic(addDTO.getBusinessId() + "_elevator_record")
|
||||
.publish(MqttSendMessageParam.builder().topic(addDTO.getBusinessId() + ELEVATOR_RECORD_SUFFIX)
|
||||
.data(JSON.toJSONString(acsElevatorRecordMqttParam)).build());
|
||||
if (publish.isSuccess()) {
|
||||
this.logger.info("推送数据成功!!!,数据,{}", JSON.toJSONString(acsElevatorRecordMqttParam));
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 电梯识别记录 MQTT 推送:经 {@code cloudwalk-device-thirdparty} 等下游将消息发布到业务 topic,供大屏/第三方订阅。
|
||||
* <p>
|
||||
* 与 {@code record} 域协作:在人员识别记录落库后异步组装载荷并调用发布接口。
|
||||
*/
|
||||
package cn.cloudwalk.elevator.mqtt;
|
||||
+3
@@ -1,5 +1,8 @@
|
||||
package cn.cloudwalk.elevator.mqtt.param;
|
||||
|
||||
/**
|
||||
* 推送到 MQTT 的电梯记录载荷:在 {@link cn.cloudwalk.elevator.record.dto.AcsElevatorRecordAddDTO} 基础上补全人名、开门流水 id、是否访客等,序列化为 {@code data} 字段内容。
|
||||
*/
|
||||
public class AcsElevatorRecordMqttParam {
|
||||
private String openDoorId;
|
||||
private String openDoorType;
|
||||
|
||||
+3
@@ -2,6 +2,9 @@ package cn.cloudwalk.elevator.mqtt.param;
|
||||
|
||||
import java.beans.ConstructorProperties;
|
||||
|
||||
/**
|
||||
* Feign 发布请求体:MQTT topic 与一条 JSON 字符串形式的业务数据。
|
||||
*/
|
||||
public class MqttSendMessageParam {
|
||||
private String topic;
|
||||
private String data;
|
||||
|
||||
+11
@@ -3,6 +3,17 @@ package cn.cloudwalk.elevator.mqtt.service;
|
||||
import cn.cloudwalk.cloud.exception.ServiceException;
|
||||
import cn.cloudwalk.elevator.record.dto.AcsElevatorRecordAddDTO;
|
||||
|
||||
/**
|
||||
* 将单条电梯通行/识别结果异步推送到 MQTT(供订阅方如大屏展示),与落库存在时序上的缓冲(实现内会短暂休眠后查库补全人名等)。
|
||||
*/
|
||||
public interface MqttService {
|
||||
/**
|
||||
* 按一条待写入或已关联的识别记录,组装业务 topic 与 JSON 后发起 {@link cn.cloudwalk.elevator.mqtt.client.MqttFeignClient#publish}。
|
||||
* <p>
|
||||
* 异步方法:调用方不应依赖其完成时刻做强一致逻辑。
|
||||
*
|
||||
* @param paramAcsElevatorRecordAddDTO 电梯识别记录主数据(需含 businessId、recognition 关联键等,供拼 topic 与反查人员)
|
||||
* @throws ServiceException 睡眠被中断等不可恢复情况
|
||||
*/
|
||||
void sendInfoToOne(AcsElevatorRecordAddDTO paramAcsElevatorRecordAddDTO) throws ServiceException;
|
||||
}
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 电梯应用业务编排层({@code cw-elevator-application-service}):领域服务接口与实现、远程调用、异步与任务推进。
|
||||
* <p>
|
||||
* 与 {@code cw-elevator-application-data} 的持久化、{@code cw-elevator-application-web} 的 HTTP 入口分工协作;
|
||||
* 本模块内可依赖对外 Feign 契约(如 {@code intelligent-cwoscomponent-interface}),但不应向上依赖 Web 层。
|
||||
*/
|
||||
package cn.cloudwalk.elevator;
|
||||
+3
-4
@@ -126,7 +126,7 @@ public class AcsPassRuleServiceImpl extends AbstractAcsPassService implements Ac
|
||||
}
|
||||
int floorCount = passRuleResults.size();
|
||||
long[] personTotals = new long[floorCount];
|
||||
for (int i = 0; i < floorCount; ) {
|
||||
for (int i = 0; i < floorCount;) {
|
||||
int end = Math.min(i + REMOTE_IO_PARALLEL, floorCount);
|
||||
List<Callable<Void>> batch = new ArrayList<>();
|
||||
for (int j = i; j < end; j++) {
|
||||
@@ -237,7 +237,7 @@ public class AcsPassRuleServiceImpl extends AbstractAcsPassService implements Ac
|
||||
this.acsDeviceImageStoreAppBindService.bindAppImageStoreDevice(appBindParam, context);
|
||||
if (!CollectionUtils.isEmpty(deviceList)) {
|
||||
final String newImageStoreId = (String)imageStoreId.getData();
|
||||
for (int i = 0; i < deviceList.size(); ) {
|
||||
for (int i = 0; i < deviceList.size();) {
|
||||
int end = Math.min(i + REMOTE_IO_PARALLEL, deviceList.size());
|
||||
List<Callable<Void>> bindBatch = new ArrayList<>();
|
||||
for (int j = i; j < end; j++) {
|
||||
@@ -556,8 +556,7 @@ public class AcsPassRuleServiceImpl extends AbstractAcsPassService implements Ac
|
||||
ImageStoreDelParam delParam = new ImageStoreDelParam();
|
||||
delParam.setId(imageStoreIdValue);
|
||||
delParam.setBusinessId(context.getCompany().getCompanyId());
|
||||
this.logger.info("回滚删除图库开始,delParam={},context={}", JSONObject.toJSON(delParam),
|
||||
JSONObject.toJSON(context));
|
||||
this.logger.info("回滚删除图库开始,delParam={},context={}", JSONObject.toJSON(delParam), JSONObject.toJSON(context));
|
||||
CloudwalkResult<Boolean> deleteResult = this.imageStoreService.delete(delParam, context);
|
||||
this.logger.info("删除图库:图库id={},结果:{}", imageStoreIdValue, deleteResult.getMessage());
|
||||
}
|
||||
|
||||
+2
-2
@@ -678,8 +678,8 @@ public class ImageRuleRefServiceImpl extends AbstractAcsPassService implements I
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次 {@link LabelService#getAll} 建 id→详情索引,避免规则详情/分页组装时对 {@link LabelService#detail} 的 N 次远程调用。
|
||||
* 若某 id 不在全量列表中(数据不同步),回退单次 detail。
|
||||
* 一次 {@link LabelService#getAll} 建 id→详情索引,避免规则详情/分页组装时对 {@link LabelService#detail} 的 N 次远程调用。 若某 id
|
||||
* 不在全量列表中(数据不同步),回退单次 detail。
|
||||
*/
|
||||
private Map<String, LabelDetailResult> loadLabelDetailMap(CloudwalkCallContext context) throws ServiceException {
|
||||
CloudwalkResult<List<LabelDetailResult>> all = this.labelService.getAll(new LabelQueryParam(), context);
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 通行/人员规则与图库规则引用:规则增删、与区域/标签/组织维度的组合,及与设备任务的协作。
|
||||
* <p>
|
||||
* 与 {@code person} 包在“按人下发”和“按规则下发”两种路径上常共同出现在设备任务流中。
|
||||
*/
|
||||
package cn.cloudwalk.elevator.passrule;
|
||||
+1
-2
@@ -208,8 +208,7 @@ public class AcsPersonServiceImpl extends AbstractAcsPassService implements AcsP
|
||||
Throwable c = e.getCause();
|
||||
if (c instanceof ServiceException) {
|
||||
ServiceException se = (ServiceException)c;
|
||||
return CloudwalkResult.fail(
|
||||
se.getCode() != null ? se.getCode() : "76260407",
|
||||
return CloudwalkResult.fail(se.getCode() != null ? se.getCode() : "76260407",
|
||||
getMessage("76260407") + " " + se.getMessage());
|
||||
}
|
||||
return CloudwalkResult.fail("76260407",
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 人员与人员-规则服务:人员增删、与区域/父级人员关系、及与设备侧同步相关的编排。
|
||||
*/
|
||||
package cn.cloudwalk.elevator.person;
|
||||
+2
-2
@@ -44,8 +44,8 @@ public class AcsPersonAddVisitorParam implements Serializable {
|
||||
return false;
|
||||
}
|
||||
return Objects.equals(visitorId, other.visitorId) && Objects.equals(personId, other.personId)
|
||||
&& Objects.equals(begVisitorTime, other.begVisitorTime) && Objects.equals(endVisitorTime, other.endVisitorTime)
|
||||
&& Objects.equals(floorIds, other.floorIds);
|
||||
&& Objects.equals(begVisitorTime, other.begVisitorTime)
|
||||
&& Objects.equals(endVisitorTime, other.endVisitorTime) && Objects.equals(floorIds, other.floorIds);
|
||||
}
|
||||
|
||||
protected boolean canEqual(Object other) {
|
||||
|
||||
+38
@@ -86,8 +86,23 @@ import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.web.util.UriComponentsBuilder;
|
||||
|
||||
/**
|
||||
* 电梯开门/通行记录:查询、落库、统计与域内事件发布。
|
||||
* <p>
|
||||
* 新增记录时通过 {@link cn.cloudwalk.elevator.util.RestTemplateUtil} 请求 {@code ninca-crk-std} 的
|
||||
* {@code intelligent/three/visitor/record/query},用识别 id 反查是否访客及被访人;该路径与
|
||||
* {@link cn.cloudwalk.elevator.visitor.client.VisitorFeignClient} 所映射的
|
||||
* {@code /intelligent/visitor/record/query} 为两套接口,本处固定走「three」版 HTTP。
|
||||
* <p>
|
||||
* {@link MqttService} 为识别结果 MQTT 推送能力,本类已注入但<strong>未直接调用</strong>;若需与入库联动,可在监听
|
||||
* {@link cn.cloudwalk.elevator.record.result.VisitorRecordPushEvent} 的处理器中显式
|
||||
* {@link cn.cloudwalk.elevator.mqtt.service.MqttService#sendInfoToOne}(或其它入口接线)。
|
||||
*/
|
||||
@Service
|
||||
public class AcsElevatorRecordServiceImpl extends AbstractAcsDeviceService implements AcsElevatorRecordService {
|
||||
/**
|
||||
* 标准访客/三方服务所在主机(IP 或 host:port),与 {@link #combineAuthClientURI} 拼成完整 URL。
|
||||
*/
|
||||
@Value("${ninca-crk-std.ip}")
|
||||
private String nincaCrkStd;
|
||||
@Autowired
|
||||
@@ -104,6 +119,9 @@ public class AcsElevatorRecordServiceImpl extends AbstractAcsDeviceService imple
|
||||
private DeviceDistrictService deviceDistrictService;
|
||||
@Resource
|
||||
private AcsAreaTreeCacheableService acsAreaTreeCacheableService;
|
||||
/**
|
||||
* 识别记录 MQTT 异步推送服务;本类当前不调用,保留供与 {@link #sendRecordEvent} 或外部监听协同接入。
|
||||
*/
|
||||
@Resource
|
||||
private MqttService mqttService;
|
||||
@Resource
|
||||
@@ -118,6 +136,9 @@ public class AcsElevatorRecordServiceImpl extends AbstractAcsDeviceService imple
|
||||
protected static final int CACHE_EXPIRE_TIME = 8;
|
||||
private static final String ELEVATOR_RECORD_SUFFIX = "elevator_record";
|
||||
|
||||
/**
|
||||
* 分页查询开门记录明细:组装区域/片区/人员等展示字段,时间跨度超过一年直接拒绝。
|
||||
*/
|
||||
@CloudwalkParamsValidate(argsIndexs = {0, 1})
|
||||
public CloudwalkResult<CloudwalkPageAble<AcsElevatorRecordResult>> openRecord(AcsElevatorRecordDetailParam param,
|
||||
CloudwalkPageInfo pageInfo, CloudwalkCallContext cloudwalkContext) throws ServiceException {
|
||||
@@ -210,6 +231,7 @@ public class AcsElevatorRecordServiceImpl extends AbstractAcsDeviceService imple
|
||||
}
|
||||
}
|
||||
|
||||
/** 从记录列表中按片区或区域去重后收集 id,供批量拉取名称。 */
|
||||
private List<String> acsElevatorRecordListDupRemove(List<AcsElevatorRecordDetailQueryResultDTO> list, String flag) {
|
||||
List<String> tempList = new ArrayList<>();
|
||||
if (flag.equals("district")) {
|
||||
@@ -227,6 +249,16 @@ public class AcsElevatorRecordServiceImpl extends AbstractAcsDeviceService imple
|
||||
return tempList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增单条电梯通行/识别结果:写库并发布 {@link cn.cloudwalk.elevator.record.result.VisitorRecordPushEvent} 域事件。
|
||||
* <p>
|
||||
* 在持久化前通过 {@link cn.cloudwalk.elevator.util.RestTemplateUtil#post} 调访客「three」线查询,若命中则置访客并回填被访人
|
||||
* {@code interviewee};再拉人员详情补工号/组织。此处未调用 {@link cn.cloudwalk.elevator.mqtt.service.MqttService}。
|
||||
*
|
||||
* @param param 设备侧识别结果、图片、操作者等
|
||||
* @param context 租户与调用方上下文
|
||||
* @return 落库与事件是否成功
|
||||
*/
|
||||
public CloudwalkResult<Boolean> add(AcsElevatorRecordAddParam param, CloudwalkCallContext context)
|
||||
throws ServiceException {
|
||||
try {
|
||||
@@ -393,6 +425,9 @@ public class AcsElevatorRecordServiceImpl extends AbstractAcsDeviceService imple
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将入库后的记录复制为域事件并发布,供图库/订阅方等异步入队处理(可能间接触发其它推送,与 MQTT 无强绑定)。
|
||||
*/
|
||||
private void sendRecordEvent(AcsElevatorRecordAddDTO addDTO) {
|
||||
VisitorRecordPushEvent event =
|
||||
(VisitorRecordPushEvent)BeanCopyUtils.copyProperties(addDTO, VisitorRecordPushEvent.class);
|
||||
@@ -401,6 +436,9 @@ public class AcsElevatorRecordServiceImpl extends AbstractAcsDeviceService imple
|
||||
this.cloudwalkEventManager.publish((BaseEvent)event);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造访问 {@code ninca-crk-std} 的绝对 URI,{@code api} 为不含前导斜杠的 path 段(如 {@code intelligent/three/...})。
|
||||
*/
|
||||
private URI combineAuthClientURI(String api, @Nullable MultiValueMap<String, String> params) {
|
||||
return UriComponentsBuilder.fromUriString("http://" + this.nincaCrkStd).path(api).queryParams(params).build()
|
||||
.toUri();
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* 电梯通行/识别记录业务:查询、落库、统计,并与访客中心(HTTP「three」线或 Feign「intelligent」线)、域事件、可选 MQTT 推送协同。
|
||||
*/
|
||||
package cn.cloudwalk.elevator.record;
|
||||
+4
@@ -2,6 +2,10 @@ package cn.cloudwalk.elevator.record.result;
|
||||
|
||||
import cn.cloudwalk.cwos.client.event.event.CustomEvent;
|
||||
|
||||
/**
|
||||
* 通行记录入库后发布的域事件,{@link #getTopic()} 固定为 {@code VISITOR_RECORD_TOPIC};监听方可据此做下游同步,与
|
||||
* {@link cn.cloudwalk.elevator.mqtt.service.MqttService} 无强制耦合。
|
||||
*/
|
||||
public class VisitorRecordPushEvent extends CustomEvent {
|
||||
private String businessId;
|
||||
private String deviceId;
|
||||
|
||||
+9
@@ -9,9 +9,18 @@ import org.springframework.cloud.openfeign.FeignClient;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
|
||||
/**
|
||||
* 标准访客服务:按访客主键分页查询其档案与到访/识别记录列表(路径 {@code /intelligent/visitor/record/query})。
|
||||
*/
|
||||
@FeignClient(name = "${feign.ninca-crk-std.name:ninca-crk-std}", path = "/intelligent/visitor/record",
|
||||
fallback = VisitorFeignClientFallback.class)
|
||||
public interface VisitorFeignClient {
|
||||
/**
|
||||
* 查询某租户下指定访客的详细资料及关联识别记录。
|
||||
*
|
||||
* @param paramVisitorRecordQueryParam 访客 id、业务租户 id 与分页参数
|
||||
* @return 聚合 {@link cn.cloudwalk.elevator.visitor.result.VisitorQueryResult}
|
||||
*/
|
||||
@RequestMapping(value = {"/query"}, method = {RequestMethod.POST})
|
||||
CloudwalkResult<VisitorQueryResult> query(VisitorRecordQueryParam paramVisitorRecordQueryParam)
|
||||
throws ServiceException;
|
||||
|
||||
+5
@@ -7,8 +7,13 @@ import cn.cloudwalk.elevator.visitor.param.VisitorRecordQueryParam;
|
||||
import cn.cloudwalk.elevator.visitor.result.VisitorQueryResult;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* {@link VisitorFeignClient} 熔断/降级:查询失败时抛错,不返回空壳数据,避免业务误判「无记录」与「服务不可用」。
|
||||
*/
|
||||
@Component
|
||||
public class VisitorFeignClientFallback implements VisitorFeignClient {
|
||||
/** {@inheritDoc} */
|
||||
@Override
|
||||
public CloudwalkResult<VisitorQueryResult> query(VisitorRecordQueryParam param) throws ServiceException {
|
||||
throw new RuntimeException("查询识别记录详情失败");
|
||||
}
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 访客主数据与识别记录查询:通过 Feign 调用 {@code ninca-crk-std} 等标准访客服务,为电梯业务侧提供访客档案与到访记录。
|
||||
* <p>
|
||||
* 本包内为入参/出参模型与客户端;业务组装与调用方在 record 等域中完成。
|
||||
*/
|
||||
package cn.cloudwalk.elevator.visitor;
|
||||
+3
@@ -3,6 +3,9 @@ package cn.cloudwalk.elevator.visitor.param;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 按主键批量拉取访客(或软删过滤)的查询条件,供与标准访客服务其它接口对接时复用。
|
||||
*/
|
||||
public class VisitorGetsParam implements Serializable {
|
||||
private static final long serialVersionUID = 3454014008721633675L;
|
||||
private List<String> ids;
|
||||
|
||||
+3
@@ -1,5 +1,8 @@
|
||||
package cn.cloudwalk.elevator.visitor.param;
|
||||
|
||||
/**
|
||||
* 轻量入参模型,仅含访客 id,字段可与 {@link VisitorRecordQueryParam#visitorId} 对应后再补全租户、分页等。
|
||||
*/
|
||||
public class VisitorRecordQueryForm {
|
||||
private String visitorId;
|
||||
|
||||
|
||||
+3
@@ -3,6 +3,9 @@ package cn.cloudwalk.elevator.visitor.param;
|
||||
import cn.cloudwalk.cloud.page.CloudwalkPageInfo;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 访客记录分页查询条件:必含业务租户与访客主键,继承公共分页信息。
|
||||
*/
|
||||
public class VisitorRecordQueryParam extends CloudwalkPageInfo implements Serializable {
|
||||
private static final long serialVersionUID = -5418825126025402170L;
|
||||
private String visitorId;
|
||||
|
||||
+3
@@ -3,6 +3,9 @@ package cn.cloudwalk.elevator.visitor.result;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 访客查询接口的聚合出参:一份访客档案 + 多页识别记录集合。
|
||||
*/
|
||||
public class VisitorQueryResult implements Serializable {
|
||||
private static final long serialVersionUID = 5547248296251558353L;
|
||||
private VisitorResult visitorInfo;
|
||||
|
||||
+3
@@ -3,6 +3,9 @@ package cn.cloudwalk.elevator.visitor.result;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 单条访客识别/签到记录:设备与区域信息、比分数、以及电梯业务扩展字段(如原因、起止楼层、派梯号等)。
|
||||
*/
|
||||
public class VisitorRecordResult implements Serializable {
|
||||
private static final long serialVersionUID = -7671207718927334038L;
|
||||
private String id;
|
||||
|
||||
+3
@@ -4,6 +4,9 @@ import cn.cloudwalk.cloud.annotation.SensitiveField;
|
||||
import cn.cloudwalk.cloud.enums.SensitiveType;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 访客主数据:含证件号(脱敏标注)、访期、登记人脸/展示图、与平台人员关系等;字段与标准访客中心模型对齐。
|
||||
*/
|
||||
public class VisitorResult implements Serializable {
|
||||
private static final long serialVersionUID = -6835803928819332341L;
|
||||
private String id;
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* 区域(园区/楼栋/楼层等)树与下一级树查询,供设备、通行与前端级联选择使用。
|
||||
* <p>
|
||||
* 多通过 Feign 拉取平台区域数据并在本包内做组装与 {@code result} 封装。
|
||||
*/
|
||||
package cn.cloudwalk.elevator.zone;
|
||||
Reference in New Issue
Block a user